@ontrails/trails 1.0.0-beta.12 → 1.0.0-beta.14
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 +40 -12
- package/__tests__/examples.test.ts +14 -0
- package/dist/src/app.d.ts.map +1 -1
- package/dist/src/app.js +13 -2
- package/dist/src/app.js.map +1 -1
- package/dist/src/clack.d.ts +1 -1
- package/dist/src/clack.js +1 -1
- package/dist/src/cli.js +2 -2
- package/dist/src/cli.js.map +1 -1
- package/dist/src/trails/add-trail.js +13 -13
- 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.js +10 -10
- package/dist/src/trails/add-verify.js.map +1 -1
- package/dist/src/trails/create-scaffold.js +26 -26
- package/dist/src/trails/create-scaffold.js.map +1 -1
- package/dist/src/trails/create.d.ts +6 -6
- package/dist/src/trails/create.d.ts.map +1 -1
- package/dist/src/trails/create.js +29 -29
- 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 +65 -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 +38 -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 +178 -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 +386 -0
- package/dist/src/trails/draft-promote.js.map +1 -0
- package/dist/src/trails/guide.d.ts +13 -3
- package/dist/src/trails/guide.d.ts.map +1 -1
- package/dist/src/trails/guide.js +21 -37
- package/dist/src/trails/guide.js.map +1 -1
- package/dist/src/trails/load-app.d.ts +3 -1
- package/dist/src/trails/load-app.d.ts.map +1 -1
- package/dist/src/trails/load-app.js +53 -10
- 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 +4 -58
- package/dist/src/trails/survey.d.ts.map +1 -1
- package/dist/src/trails/survey.js +52 -173
- 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 +18 -0
- package/dist/src/trails/topo-export.d.ts.map +1 -0
- package/dist/src/trails/topo-export.js +34 -0
- package/dist/src/trails/topo-export.js.map +1 -0
- package/dist/src/trails/topo-history.d.ts +24 -0
- package/dist/src/trails/topo-history.d.ts.map +1 -0
- package/dist/src/trails/topo-history.js +33 -0
- package/dist/src/trails/topo-history.js.map +1 -0
- package/dist/src/trails/topo-pin.d.ts +21 -0
- package/dist/src/trails/topo-pin.d.ts.map +1 -0
- package/dist/src/trails/topo-pin.js +35 -0
- package/dist/src/trails/topo-pin.js.map +1 -0
- package/dist/src/trails/topo-read-support.d.ts +54 -0
- package/dist/src/trails/topo-read-support.d.ts.map +1 -0
- package/dist/src/trails/topo-read-support.js +178 -0
- package/dist/src/trails/topo-read-support.js.map +1 -0
- package/dist/src/trails/topo-reports.d.ts +50 -0
- package/dist/src/trails/topo-reports.d.ts.map +1 -0
- package/dist/src/trails/topo-reports.js +122 -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 +53 -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 +87 -0
- package/dist/src/trails/topo-support.d.ts.map +1 -0
- package/dist/src/trails/topo-support.js +165 -0
- package/dist/src/trails/topo-support.js.map +1 -0
- package/dist/src/trails/topo-unpin.d.ts +15 -0
- package/dist/src/trails/topo-unpin.d.ts.map +1 -0
- package/dist/src/trails/topo-unpin.js +39 -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 +28 -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 +67 -0
- package/dist/src/trails/topo.js.map +1 -0
- package/dist/src/trails/warden.d.ts +1 -1
- package/dist/src/trails/warden.d.ts.map +1 -1
- package/dist/src/trails/warden.js +28 -27
- package/dist/src/trails/warden.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +8 -7
- package/src/__tests__/create.test.ts +35 -33
- package/src/__tests__/draft-promote.test.ts +144 -0
- package/src/__tests__/guide.test.ts +4 -4
- package/src/__tests__/load-app.test.ts +43 -0
- package/src/__tests__/survey.test.ts +140 -55
- package/src/__tests__/topo-dev.test.ts +424 -0
- package/src/__tests__/warden.test.ts +2 -2
- package/src/app.ts +24 -2
- package/src/clack.ts +1 -1
- package/src/cli.ts +2 -2
- package/src/trails/add-trail.ts +13 -13
- package/src/trails/{add-surface.ts → add-trailhead.ts} +39 -37
- package/src/trails/add-verify.ts +10 -10
- package/src/trails/create-scaffold.ts +28 -28
- package/src/trails/create.ts +42 -42
- package/src/trails/dev-clean.ts +73 -0
- package/src/trails/dev-reset.ts +44 -0
- package/src/trails/dev-stats.ts +64 -0
- package/src/trails/dev-support.ts +326 -0
- package/src/trails/draft-promote.ts +704 -0
- package/src/trails/guide.ts +29 -44
- package/src/trails/load-app.ts +76 -13
- package/src/trails/project.ts +17 -3
- package/src/trails/survey.ts +80 -279
- package/src/trails/topo-constants.ts +2 -0
- package/src/trails/topo-export.ts +39 -0
- package/src/trails/topo-history.ts +40 -0
- package/src/trails/topo-pin.ts +42 -0
- package/src/trails/topo-read-support.ts +332 -0
- package/src/trails/topo-reports.ts +221 -0
- package/src/trails/topo-show.ts +58 -0
- package/src/trails/topo-store-support.ts +96 -0
- package/src/trails/topo-support.ts +274 -0
- package/src/trails/topo-unpin.ts +51 -0
- package/src/trails/topo-verify.ts +29 -0
- package/src/trails/topo.ts +73 -0
- package/src/trails/warden.ts +33 -32
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ontrails/trails",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.14",
|
|
4
4
|
"bin": {
|
|
5
5
|
"trails": "./bin/trails.ts"
|
|
6
6
|
},
|
|
@@ -14,15 +14,16 @@
|
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"@clack/prompts": "^1.1.0",
|
|
17
|
-
"@ontrails/cli": "^1.0.0-beta.
|
|
18
|
-
"@ontrails/core": "^1.0.0-beta.
|
|
19
|
-
"@ontrails/logging": "^1.0.0-beta.
|
|
20
|
-
"@ontrails/schema": "^1.0.0-beta.
|
|
21
|
-
"@ontrails/
|
|
17
|
+
"@ontrails/cli": "^1.0.0-beta.13",
|
|
18
|
+
"@ontrails/core": "^1.0.0-beta.13",
|
|
19
|
+
"@ontrails/logging": "^1.0.0-beta.13",
|
|
20
|
+
"@ontrails/schema": "^1.0.0-beta.13",
|
|
21
|
+
"@ontrails/tracker": "^1.0.0-beta.13",
|
|
22
|
+
"@ontrails/warden": "^1.0.0-beta.13",
|
|
22
23
|
"commander": "^14.0.3",
|
|
23
24
|
"zod": "^4.3.5"
|
|
24
25
|
},
|
|
25
26
|
"devDependencies": {
|
|
26
|
-
"@ontrails/testing": "^1.0.0-beta.
|
|
27
|
+
"@ontrails/testing": "^1.0.0-beta.13"
|
|
27
28
|
}
|
|
28
29
|
}
|
|
@@ -11,19 +11,19 @@ import { basename, dirname, join } from 'node:path';
|
|
|
11
11
|
|
|
12
12
|
import { Result } from '@ontrails/core';
|
|
13
13
|
|
|
14
|
-
import {
|
|
14
|
+
import { addTrailhead } from '../trails/add-trailhead.js';
|
|
15
15
|
import { addVerify } from '../trails/add-verify.js';
|
|
16
16
|
import { createRoute } from '../trails/create.js';
|
|
17
17
|
import { createScaffold } from '../trails/create-scaffold.js';
|
|
18
18
|
import { isInsideProject } from '../trails/project.js';
|
|
19
19
|
|
|
20
20
|
type Starter = 'empty' | 'entity' | 'hello';
|
|
21
|
-
type
|
|
21
|
+
type Trailhead = 'cli' | 'mcp';
|
|
22
22
|
|
|
23
23
|
const makeTempProject = (): string =>
|
|
24
24
|
join(
|
|
25
25
|
tmpdir(),
|
|
26
|
-
`trails-
|
|
26
|
+
`trails-create-test-${Date.now()}-${Math.random().toString(36).slice(2)}`
|
|
27
27
|
);
|
|
28
28
|
|
|
29
29
|
const readJson = (dir: string, relativePath: string): Record<string, unknown> =>
|
|
@@ -68,43 +68,43 @@ const expectErr = <T>(result: Result<T, Error>): Error => {
|
|
|
68
68
|
return result.error;
|
|
69
69
|
};
|
|
70
70
|
|
|
71
|
-
const
|
|
71
|
+
const runCross = async (
|
|
72
72
|
id: string,
|
|
73
73
|
input: unknown
|
|
74
74
|
): Promise<Result<unknown, Error>> => {
|
|
75
75
|
switch (id) {
|
|
76
76
|
case 'create.scaffold': {
|
|
77
|
-
return await createScaffold.
|
|
77
|
+
return await createScaffold.blaze(input as never, {} as never);
|
|
78
78
|
}
|
|
79
|
-
case 'add.
|
|
80
|
-
return await
|
|
79
|
+
case 'add.trailhead': {
|
|
80
|
+
return await addTrailhead.blaze(input as never, {} as never);
|
|
81
81
|
}
|
|
82
82
|
case 'add.verify': {
|
|
83
|
-
return await addVerify.
|
|
83
|
+
return await addVerify.blaze(input as never, {} as never);
|
|
84
84
|
}
|
|
85
85
|
default: {
|
|
86
|
-
return Result.err(new Error(`Unknown
|
|
86
|
+
return Result.err(new Error(`Unknown cross target: ${id}`));
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
89
|
};
|
|
90
90
|
|
|
91
|
-
const
|
|
91
|
+
const runCreate = (
|
|
92
92
|
projectDir: string,
|
|
93
93
|
overrides?: Partial<{
|
|
94
94
|
starter: Starter;
|
|
95
|
-
|
|
95
|
+
trailheads: readonly Trailhead[];
|
|
96
96
|
verify: boolean;
|
|
97
97
|
}>
|
|
98
98
|
) =>
|
|
99
|
-
createRoute.
|
|
99
|
+
createRoute.blaze(
|
|
100
100
|
{
|
|
101
101
|
dir: dirname(projectDir),
|
|
102
102
|
name: basename(projectDir),
|
|
103
103
|
starter: overrides?.starter ?? 'hello',
|
|
104
|
-
|
|
104
|
+
trailheads: [...(overrides?.trailheads ?? ['cli'])],
|
|
105
105
|
verify: overrides?.verify ?? true,
|
|
106
106
|
},
|
|
107
|
-
{
|
|
107
|
+
{ cross: runCross } as never
|
|
108
108
|
);
|
|
109
109
|
|
|
110
110
|
const setupMinimalProject = (dir: string): void => {
|
|
@@ -177,7 +177,7 @@ const assertEntityStarter = (dir: string): void => {
|
|
|
177
177
|
'src/trails/entity.ts',
|
|
178
178
|
'src/trails/search.ts',
|
|
179
179
|
'src/trails/onboard.ts',
|
|
180
|
-
'src/
|
|
180
|
+
'src/signals/entity-signals.ts',
|
|
181
181
|
'src/store.ts',
|
|
182
182
|
],
|
|
183
183
|
true
|
|
@@ -187,7 +187,7 @@ const assertEntityStarter = (dir: string): void => {
|
|
|
187
187
|
"import * as entity from './trails/entity.js'",
|
|
188
188
|
"import * as search from './trails/search.js'",
|
|
189
189
|
"import * as onboard from './trails/onboard.js'",
|
|
190
|
-
"import * as
|
|
190
|
+
"import * as entitySignals from './signals/entity-signals.js'",
|
|
191
191
|
]);
|
|
192
192
|
expectContainsAll(readText(dir, 'src/trails/entity.ts'), [
|
|
193
193
|
"import { Result, trail } from '@ontrails/core'",
|
|
@@ -204,12 +204,12 @@ const assertEntityStarter = (dir: string): void => {
|
|
|
204
204
|
]);
|
|
205
205
|
};
|
|
206
206
|
|
|
207
|
-
const
|
|
207
|
+
const assertMcpTrailhead = (dir: string): void => {
|
|
208
208
|
expectPaths(dir, ['src/mcp.ts'], true);
|
|
209
209
|
expectPaths(dir, ['src/cli.ts'], false);
|
|
210
210
|
expectContainsAll(readText(dir, 'src/mcp.ts'), [
|
|
211
|
-
"import {
|
|
212
|
-
'await
|
|
211
|
+
"import { trailhead } from '@ontrails/mcp'",
|
|
212
|
+
'await trailhead(app)',
|
|
213
213
|
]);
|
|
214
214
|
|
|
215
215
|
const deps = readJson(dir, 'package.json')['dependencies'] as Record<
|
|
@@ -244,11 +244,11 @@ const withTempProject = async (
|
|
|
244
244
|
}
|
|
245
245
|
};
|
|
246
246
|
|
|
247
|
-
describe('trails
|
|
247
|
+
describe('trails create', () => {
|
|
248
248
|
describe('create mode', () => {
|
|
249
249
|
test('generates project structure with defaults', async () => {
|
|
250
250
|
await withTempProject(async (dir) => {
|
|
251
|
-
expectOk(await
|
|
251
|
+
expectOk(await runCreate(dir));
|
|
252
252
|
assertDefaultProjectFiles(dir);
|
|
253
253
|
assertCliPackage(dir);
|
|
254
254
|
assertHelloApp(dir);
|
|
@@ -257,46 +257,46 @@ describe('trails blaze', () => {
|
|
|
257
257
|
|
|
258
258
|
test('generates with entity starter', async () => {
|
|
259
259
|
await withTempProject(async (dir) => {
|
|
260
|
-
expectOk(await
|
|
260
|
+
expectOk(await runCreate(dir, { starter: 'entity' }));
|
|
261
261
|
assertEntityStarter(dir);
|
|
262
262
|
});
|
|
263
263
|
});
|
|
264
264
|
|
|
265
|
-
test('generates with MCP
|
|
265
|
+
test('generates with MCP trailhead', async () => {
|
|
266
266
|
await withTempProject(async (dir) => {
|
|
267
|
-
expectOk(await
|
|
268
|
-
|
|
267
|
+
expectOk(await runCreate(dir, { trailheads: ['mcp'] }));
|
|
268
|
+
assertMcpTrailhead(dir);
|
|
269
269
|
});
|
|
270
270
|
});
|
|
271
271
|
|
|
272
272
|
test('skips verification when verify is false', async () => {
|
|
273
273
|
await withTempProject(async (dir) => {
|
|
274
|
-
expectOk(await
|
|
274
|
+
expectOk(await runCreate(dir, { verify: false }));
|
|
275
275
|
assertVerifySkipped(dir);
|
|
276
276
|
});
|
|
277
277
|
});
|
|
278
278
|
|
|
279
279
|
test('generates with empty starter', async () => {
|
|
280
280
|
await withTempProject(async (dir) => {
|
|
281
|
-
expectOk(await
|
|
281
|
+
expectOk(await runCreate(dir, { starter: 'empty' }));
|
|
282
282
|
assertEmptyStarter(dir);
|
|
283
283
|
});
|
|
284
284
|
});
|
|
285
285
|
});
|
|
286
286
|
|
|
287
|
-
describe('add-
|
|
287
|
+
describe('add-trailhead mode', () => {
|
|
288
288
|
test('adds MCP to existing project', async () => {
|
|
289
289
|
await withTempProject(async (dir) => {
|
|
290
290
|
setupMinimalProject(dir);
|
|
291
291
|
const result = expectOk(
|
|
292
|
-
await
|
|
292
|
+
await addTrailhead.blaze({ dir, trailhead: 'mcp' }, {} as never)
|
|
293
293
|
);
|
|
294
294
|
|
|
295
295
|
expect(result.created).toBe('src/mcp.ts');
|
|
296
296
|
expect(result.dependency).toBe('@ontrails/mcp');
|
|
297
297
|
expectPaths(dir, ['src/mcp.ts'], true);
|
|
298
298
|
expectContainsAll(readText(dir, 'src/mcp.ts'), [
|
|
299
|
-
"import {
|
|
299
|
+
"import { trailhead } from '@ontrails/mcp'",
|
|
300
300
|
]);
|
|
301
301
|
const deps = readJson(dir, 'package.json')['dependencies'] as Record<
|
|
302
302
|
string,
|
|
@@ -306,16 +306,18 @@ describe('trails blaze', () => {
|
|
|
306
306
|
});
|
|
307
307
|
});
|
|
308
308
|
|
|
309
|
-
test('detects
|
|
309
|
+
test('detects existing trailhead entrypoint', async () => {
|
|
310
310
|
await withTempProject(async (dir) => {
|
|
311
311
|
mkdirSync(join(dir, 'src'), { recursive: true });
|
|
312
312
|
mkdirSync(join(dir, '.trails'), { recursive: true });
|
|
313
313
|
writeFileSync(join(dir, 'src', 'mcp.ts'), 'existing content');
|
|
314
314
|
|
|
315
315
|
const error = expectErr(
|
|
316
|
-
await
|
|
316
|
+
await addTrailhead.blaze({ dir, trailhead: 'mcp' }, {} as never)
|
|
317
|
+
);
|
|
318
|
+
expect(error.message).toBe(
|
|
319
|
+
'MCP trailhead already exists. Nothing to do.'
|
|
317
320
|
);
|
|
318
|
-
expect(error.message).toBe('MCP is already blazed. Nothing to do.');
|
|
319
321
|
});
|
|
320
322
|
});
|
|
321
323
|
});
|
|
@@ -0,0 +1,144 @@
|
|
|
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 { join, resolve } from 'node:path';
|
|
10
|
+
|
|
11
|
+
import type { Result } from '@ontrails/core';
|
|
12
|
+
import { ValidationError } from '@ontrails/core';
|
|
13
|
+
|
|
14
|
+
import { draftPromoteTrail } from '../trails/draft-promote.js';
|
|
15
|
+
|
|
16
|
+
const repoTempDir = (): string =>
|
|
17
|
+
join(
|
|
18
|
+
resolve(import.meta.dir, '../..'),
|
|
19
|
+
'.tmp-tests',
|
|
20
|
+
`draft-promote-${Date.now()}-${Math.random().toString(36).slice(2)}`
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
const expectOk = <T>(result: Result<T, Error>): T => {
|
|
24
|
+
if (result.isErr()) {
|
|
25
|
+
throw result.error;
|
|
26
|
+
}
|
|
27
|
+
return result.value;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const expectErr = <E extends Error>(result: Result<unknown, E>): E => {
|
|
31
|
+
if (result.isOk()) {
|
|
32
|
+
throw new Error('expected result to be an error');
|
|
33
|
+
}
|
|
34
|
+
return result.error;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const writeDraftPromoteFixture = (dir: string): void => {
|
|
38
|
+
mkdirSync(join(dir, 'src'), { recursive: true });
|
|
39
|
+
|
|
40
|
+
writeFileSync(
|
|
41
|
+
join(dir, 'src', 'app.ts'),
|
|
42
|
+
`import { topo } from '@ontrails/core';
|
|
43
|
+
import { draftPrepare } from './_draft.prepare.js';
|
|
44
|
+
import { exportTrail } from './export.js';
|
|
45
|
+
|
|
46
|
+
export const app = topo('draft-test', { draftPrepare, exportTrail });
|
|
47
|
+
`
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
writeFileSync(
|
|
51
|
+
join(dir, 'src', '_draft.prepare.ts'),
|
|
52
|
+
`import { Result, trail } from '@ontrails/core';
|
|
53
|
+
import { z } from 'zod';
|
|
54
|
+
|
|
55
|
+
export const draftPrepare = trail('_draft.entity.prepare', {
|
|
56
|
+
blaze: async () => Result.ok({ ready: true }),
|
|
57
|
+
input: z.object({}),
|
|
58
|
+
output: z.object({ ready: z.boolean() }),
|
|
59
|
+
});
|
|
60
|
+
`
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
writeFileSync(
|
|
64
|
+
join(dir, 'src', 'export.ts'),
|
|
65
|
+
`import { Result, trail } from '@ontrails/core';
|
|
66
|
+
import { z } from 'zod';
|
|
67
|
+
|
|
68
|
+
export const exportTrail = trail('entity.export', {
|
|
69
|
+
blaze: async () => Result.ok({ exported: true }),
|
|
70
|
+
crosses: ['_draft.entity.prepare'],
|
|
71
|
+
input: z.object({}),
|
|
72
|
+
output: z.object({ exported: z.boolean() }),
|
|
73
|
+
});
|
|
74
|
+
`
|
|
75
|
+
);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const expectDraftPromoteResults = (dir: string): void => {
|
|
79
|
+
expect(existsSync(join(dir, 'src', '_draft.prepare.ts'))).toBe(false);
|
|
80
|
+
expect(existsSync(join(dir, 'src', 'prepare.ts'))).toBe(true);
|
|
81
|
+
expect(readFileSync(join(dir, 'src', 'prepare.ts'), 'utf8')).toContain(
|
|
82
|
+
"trail('entity.prepare'"
|
|
83
|
+
);
|
|
84
|
+
expect(readFileSync(join(dir, 'src', 'export.ts'), 'utf8')).toContain(
|
|
85
|
+
"crosses: ['entity.prepare']"
|
|
86
|
+
);
|
|
87
|
+
expect(readFileSync(join(dir, 'src', 'app.ts'), 'utf8')).toContain(
|
|
88
|
+
"from './prepare.js'"
|
|
89
|
+
);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
describe('draft.promote', () => {
|
|
93
|
+
test('promotes draft ids, renames files, and updates imports', async () => {
|
|
94
|
+
const dir = repoTempDir();
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
writeDraftPromoteFixture(dir);
|
|
98
|
+
|
|
99
|
+
const result = expectOk(
|
|
100
|
+
await draftPromoteTrail.blaze(
|
|
101
|
+
{
|
|
102
|
+
fromId: '_draft.entity.prepare',
|
|
103
|
+
renameFiles: true,
|
|
104
|
+
rootDir: dir,
|
|
105
|
+
toId: 'entity.prepare',
|
|
106
|
+
},
|
|
107
|
+
{ cwd: dir } as never
|
|
108
|
+
)
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
expect(result.promotedEstablished).toBe(true);
|
|
112
|
+
expect(result.remainingDraftIds).toEqual([]);
|
|
113
|
+
expect(result.updatedFiles).toEqual(
|
|
114
|
+
expect.arrayContaining(['src/app.ts', 'src/export.ts'])
|
|
115
|
+
);
|
|
116
|
+
expect(result.renamedFiles).toEqual([
|
|
117
|
+
{
|
|
118
|
+
from: 'src/_draft.prepare.ts',
|
|
119
|
+
to: 'src/prepare.ts',
|
|
120
|
+
},
|
|
121
|
+
]);
|
|
122
|
+
expectDraftPromoteResults(dir);
|
|
123
|
+
} finally {
|
|
124
|
+
rmSync(dir, { force: true, recursive: true });
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test('returns ValidationError when rootDir does not exist', async () => {
|
|
129
|
+
const error = expectErr(
|
|
130
|
+
await draftPromoteTrail.blaze(
|
|
131
|
+
{
|
|
132
|
+
fromId: '_draft.entity.prepare',
|
|
133
|
+
renameFiles: true,
|
|
134
|
+
rootDir: join(repoTempDir(), 'missing'),
|
|
135
|
+
toId: 'entity.prepare',
|
|
136
|
+
},
|
|
137
|
+
{ cwd: process.cwd() } as never
|
|
138
|
+
)
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
expect(error).toBeInstanceOf(ValidationError);
|
|
142
|
+
expect(error.message).toContain('rootDir does not exist');
|
|
143
|
+
});
|
|
144
|
+
});
|
|
@@ -9,6 +9,10 @@ import { z } from 'zod';
|
|
|
9
9
|
// ---------------------------------------------------------------------------
|
|
10
10
|
|
|
11
11
|
const helloTrail = trail('hello', {
|
|
12
|
+
blaze: (input) => {
|
|
13
|
+
const name = input.name ?? 'world';
|
|
14
|
+
return Result.ok({ message: `Hello, ${name}!` });
|
|
15
|
+
},
|
|
12
16
|
description: 'Say hello',
|
|
13
17
|
detours: {
|
|
14
18
|
NotFoundError: ['search'],
|
|
@@ -28,10 +32,6 @@ const helloTrail = trail('hello', {
|
|
|
28
32
|
input: z.object({ name: z.string().optional() }),
|
|
29
33
|
intent: 'read',
|
|
30
34
|
output: z.object({ message: z.string() }),
|
|
31
|
-
run: (input) => {
|
|
32
|
-
const name = input.name ?? 'world';
|
|
33
|
-
return Result.ok({ message: `Hello, ${name}!` });
|
|
34
|
-
},
|
|
35
35
|
});
|
|
36
36
|
|
|
37
37
|
const app = topo('test-app', { hello: helloTrail });
|
|
@@ -1,8 +1,37 @@
|
|
|
1
1
|
import { describe, expect, test } from 'bun:test';
|
|
2
|
+
import { mkdirSync, rmSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
2
4
|
import { resolve } from 'node:path';
|
|
3
5
|
|
|
4
6
|
import { loadApp } from '../trails/load-app.js';
|
|
5
7
|
|
|
8
|
+
const writeLoadAppFixture = (cwd: string, name: string): void => {
|
|
9
|
+
writeFileSync(
|
|
10
|
+
resolve(cwd, 'src/app.ts'),
|
|
11
|
+
`export const app = {
|
|
12
|
+
name: '${name}',
|
|
13
|
+
trails: new Map(),
|
|
14
|
+
signals: new Map(),
|
|
15
|
+
provisions: new Map()
|
|
16
|
+
};`
|
|
17
|
+
);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const assertLoadAppCaching = async (cwd: string): Promise<void> => {
|
|
21
|
+
writeLoadAppFixture(cwd, 'first');
|
|
22
|
+
|
|
23
|
+
const first = await loadApp('./src/app.ts', cwd);
|
|
24
|
+
|
|
25
|
+
writeLoadAppFixture(cwd, 'second');
|
|
26
|
+
|
|
27
|
+
const cached = await loadApp('./src/app.ts', cwd);
|
|
28
|
+
const fresh = await loadApp('./src/app.ts', cwd, { fresh: true });
|
|
29
|
+
|
|
30
|
+
expect(first.name).toBe('first');
|
|
31
|
+
expect(cached.name).toBe('first');
|
|
32
|
+
expect(fresh.name).toBe('second');
|
|
33
|
+
};
|
|
34
|
+
|
|
6
35
|
describe('loadApp', () => {
|
|
7
36
|
test('resolves relative module paths from cwd', async () => {
|
|
8
37
|
// import.meta.dir is src/__tests__/, go up two to get apps/trails/
|
|
@@ -12,4 +41,18 @@ describe('loadApp', () => {
|
|
|
12
41
|
expect(app.name).toBe('trails');
|
|
13
42
|
expect(app.get('survey')).toBeDefined();
|
|
14
43
|
});
|
|
44
|
+
|
|
45
|
+
test('can bypass module caching with fresh loading', async () => {
|
|
46
|
+
const cwd = resolve(
|
|
47
|
+
tmpdir(),
|
|
48
|
+
`trails-load-app-${Date.now()}-${Math.random().toString(36).slice(2)}`
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
mkdirSync(resolve(cwd, 'src'), { recursive: true });
|
|
53
|
+
await assertLoadAppCaching(cwd);
|
|
54
|
+
} finally {
|
|
55
|
+
rmSync(cwd, { force: true, recursive: true });
|
|
56
|
+
}
|
|
57
|
+
});
|
|
15
58
|
});
|