@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.
Files changed (201) hide show
  1. package/CHANGELOG.md +215 -2
  2. package/README.md +27 -0
  3. package/package.json +21 -8
  4. package/src/app.ts +15 -5
  5. package/src/cli.ts +303 -10
  6. package/src/completions.ts +240 -0
  7. package/src/load-app-mirror.ts +160 -0
  8. package/src/local-state-io.ts +153 -0
  9. package/src/project-writes.ts +320 -0
  10. package/src/run-collision.ts +125 -0
  11. package/src/run-completions-install.ts +179 -0
  12. package/src/run-example.ts +149 -0
  13. package/src/run-examples.ts +148 -0
  14. package/src/run-quiet.ts +75 -0
  15. package/src/run-trace.ts +273 -0
  16. package/src/run-warden.ts +39 -0
  17. package/src/run-watch.ts +432 -0
  18. package/src/scaffold-versions.generated.ts +12 -0
  19. package/src/trails/add-surface.ts +45 -23
  20. package/src/trails/add-trail.ts +27 -17
  21. package/src/trails/add-verify.ts +57 -17
  22. package/src/trails/completions-complete.ts +165 -0
  23. package/src/trails/completions.ts +47 -0
  24. package/src/trails/create-scaffold.ts +86 -33
  25. package/src/trails/create.ts +11 -3
  26. package/src/trails/dev-clean.ts +6 -1
  27. package/src/trails/dev-reset.ts +6 -1
  28. package/src/trails/dev-stats.ts +6 -1
  29. package/src/trails/dev-support.ts +29 -17
  30. package/src/trails/draft-promote.ts +289 -80
  31. package/src/trails/guide.ts +54 -34
  32. package/src/trails/load-app.ts +251 -56
  33. package/src/trails/root-dir.ts +21 -0
  34. package/src/trails/run-example.ts +482 -0
  35. package/src/trails/run-examples.ts +141 -0
  36. package/src/trails/run.ts +403 -0
  37. package/src/trails/survey.ts +534 -200
  38. package/src/trails/topo-activation.ts +385 -0
  39. package/src/trails/topo-compile.ts +55 -0
  40. package/src/trails/topo-history.ts +6 -1
  41. package/src/trails/topo-output-schemas.ts +219 -0
  42. package/src/trails/topo-pin.ts +19 -6
  43. package/src/trails/topo-read-support.ts +171 -228
  44. package/src/trails/topo-reports.ts +587 -25
  45. package/src/trails/topo-store-support.ts +43 -19
  46. package/src/trails/topo-support.ts +18 -28
  47. package/src/trails/topo-unpin.ts +6 -1
  48. package/src/trails/topo-verify.ts +18 -5
  49. package/src/trails/topo.ts +60 -23
  50. package/src/trails/warden-guide.ts +121 -0
  51. package/src/trails/warden.ts +137 -56
  52. package/src/versions.ts +3 -18
  53. package/.turbo/turbo-build.log +0 -1
  54. package/.turbo/turbo-lint.log +0 -3
  55. package/.turbo/turbo-typecheck.log +0 -1
  56. package/__tests__/examples.test.ts +0 -45
  57. package/dist/bin/trails.d.ts +0 -3
  58. package/dist/bin/trails.d.ts.map +0 -1
  59. package/dist/bin/trails.js +0 -4
  60. package/dist/bin/trails.js.map +0 -1
  61. package/dist/src/app.d.ts +0 -2
  62. package/dist/src/app.d.ts.map +0 -1
  63. package/dist/src/app.js +0 -22
  64. package/dist/src/app.js.map +0 -1
  65. package/dist/src/clack.d.ts +0 -9
  66. package/dist/src/clack.d.ts.map +0 -1
  67. package/dist/src/clack.js +0 -84
  68. package/dist/src/clack.js.map +0 -1
  69. package/dist/src/cli.d.ts +0 -2
  70. package/dist/src/cli.d.ts.map +0 -1
  71. package/dist/src/cli.js +0 -14
  72. package/dist/src/cli.js.map +0 -1
  73. package/dist/src/trails/add-surface.d.ts +0 -13
  74. package/dist/src/trails/add-surface.d.ts.map +0 -1
  75. package/dist/src/trails/add-surface.js +0 -110
  76. package/dist/src/trails/add-surface.js.map +0 -1
  77. package/dist/src/trails/add-trail.d.ts +0 -12
  78. package/dist/src/trails/add-trail.d.ts.map +0 -1
  79. package/dist/src/trails/add-trail.js +0 -104
  80. package/dist/src/trails/add-trail.js.map +0 -1
  81. package/dist/src/trails/add-trailhead.d.ts +0 -13
  82. package/dist/src/trails/add-trailhead.d.ts.map +0 -1
  83. package/dist/src/trails/add-trailhead.js +0 -88
  84. package/dist/src/trails/add-trailhead.js.map +0 -1
  85. package/dist/src/trails/add-verify.d.ts +0 -10
  86. package/dist/src/trails/add-verify.d.ts.map +0 -1
  87. package/dist/src/trails/add-verify.js +0 -68
  88. package/dist/src/trails/add-verify.js.map +0 -1
  89. package/dist/src/trails/create-scaffold.d.ts +0 -15
  90. package/dist/src/trails/create-scaffold.d.ts.map +0 -1
  91. package/dist/src/trails/create-scaffold.js +0 -295
  92. package/dist/src/trails/create-scaffold.js.map +0 -1
  93. package/dist/src/trails/create.d.ts +0 -18
  94. package/dist/src/trails/create.d.ts.map +0 -1
  95. package/dist/src/trails/create.js +0 -126
  96. package/dist/src/trails/create.js.map +0 -1
  97. package/dist/src/trails/dev-clean.d.ts +0 -9
  98. package/dist/src/trails/dev-clean.d.ts.map +0 -1
  99. package/dist/src/trails/dev-clean.js +0 -66
  100. package/dist/src/trails/dev-clean.js.map +0 -1
  101. package/dist/src/trails/dev-reset.d.ts +0 -6
  102. package/dist/src/trails/dev-reset.d.ts.map +0 -1
  103. package/dist/src/trails/dev-reset.js +0 -39
  104. package/dist/src/trails/dev-reset.js.map +0 -1
  105. package/dist/src/trails/dev-stats.d.ts +0 -7
  106. package/dist/src/trails/dev-stats.d.ts.map +0 -1
  107. package/dist/src/trails/dev-stats.js +0 -61
  108. package/dist/src/trails/dev-stats.js.map +0 -1
  109. package/dist/src/trails/dev-support.d.ts +0 -64
  110. package/dist/src/trails/dev-support.d.ts.map +0 -1
  111. package/dist/src/trails/dev-support.js +0 -181
  112. package/dist/src/trails/dev-support.js.map +0 -1
  113. package/dist/src/trails/draft-promote.d.ts +0 -18
  114. package/dist/src/trails/draft-promote.d.ts.map +0 -1
  115. package/dist/src/trails/draft-promote.js +0 -400
  116. package/dist/src/trails/draft-promote.js.map +0 -1
  117. package/dist/src/trails/guide.d.ts +0 -21
  118. package/dist/src/trails/guide.d.ts.map +0 -1
  119. package/dist/src/trails/guide.js +0 -61
  120. package/dist/src/trails/guide.js.map +0 -1
  121. package/dist/src/trails/load-app.d.ts +0 -12
  122. package/dist/src/trails/load-app.d.ts.map +0 -1
  123. package/dist/src/trails/load-app.js +0 -415
  124. package/dist/src/trails/load-app.js.map +0 -1
  125. package/dist/src/trails/project.d.ts +0 -8
  126. package/dist/src/trails/project.d.ts.map +0 -1
  127. package/dist/src/trails/project.js +0 -54
  128. package/dist/src/trails/project.js.map +0 -1
  129. package/dist/src/trails/survey.d.ts +0 -18
  130. package/dist/src/trails/survey.d.ts.map +0 -1
  131. package/dist/src/trails/survey.js +0 -234
  132. package/dist/src/trails/survey.js.map +0 -1
  133. package/dist/src/trails/topo-constants.d.ts +0 -3
  134. package/dist/src/trails/topo-constants.d.ts.map +0 -1
  135. package/dist/src/trails/topo-constants.js +0 -3
  136. package/dist/src/trails/topo-constants.js.map +0 -1
  137. package/dist/src/trails/topo-export.d.ts +0 -19
  138. package/dist/src/trails/topo-export.d.ts.map +0 -1
  139. package/dist/src/trails/topo-export.js +0 -31
  140. package/dist/src/trails/topo-export.js.map +0 -1
  141. package/dist/src/trails/topo-history.d.ts +0 -20
  142. package/dist/src/trails/topo-history.d.ts.map +0 -1
  143. package/dist/src/trails/topo-history.js +0 -32
  144. package/dist/src/trails/topo-history.js.map +0 -1
  145. package/dist/src/trails/topo-pin.d.ts +0 -17
  146. package/dist/src/trails/topo-pin.d.ts.map +0 -1
  147. package/dist/src/trails/topo-pin.js +0 -31
  148. package/dist/src/trails/topo-pin.js.map +0 -1
  149. package/dist/src/trails/topo-read-support.d.ts +0 -58
  150. package/dist/src/trails/topo-read-support.d.ts.map +0 -1
  151. package/dist/src/trails/topo-read-support.js +0 -167
  152. package/dist/src/trails/topo-read-support.js.map +0 -1
  153. package/dist/src/trails/topo-reports.d.ts +0 -54
  154. package/dist/src/trails/topo-reports.d.ts.map +0 -1
  155. package/dist/src/trails/topo-reports.js +0 -128
  156. package/dist/src/trails/topo-reports.js.map +0 -1
  157. package/dist/src/trails/topo-show.d.ts +0 -23
  158. package/dist/src/trails/topo-show.d.ts.map +0 -1
  159. package/dist/src/trails/topo-show.js +0 -49
  160. package/dist/src/trails/topo-show.js.map +0 -1
  161. package/dist/src/trails/topo-store-support.d.ts +0 -13
  162. package/dist/src/trails/topo-store-support.d.ts.map +0 -1
  163. package/dist/src/trails/topo-store-support.js +0 -55
  164. package/dist/src/trails/topo-store-support.js.map +0 -1
  165. package/dist/src/trails/topo-support.d.ts +0 -76
  166. package/dist/src/trails/topo-support.d.ts.map +0 -1
  167. package/dist/src/trails/topo-support.js +0 -132
  168. package/dist/src/trails/topo-support.js.map +0 -1
  169. package/dist/src/trails/topo-unpin.d.ts +0 -20
  170. package/dist/src/trails/topo-unpin.d.ts.map +0 -1
  171. package/dist/src/trails/topo-unpin.js +0 -44
  172. package/dist/src/trails/topo-unpin.js.map +0 -1
  173. package/dist/src/trails/topo-verify.d.ts +0 -5
  174. package/dist/src/trails/topo-verify.d.ts.map +0 -1
  175. package/dist/src/trails/topo-verify.js +0 -24
  176. package/dist/src/trails/topo-verify.js.map +0 -1
  177. package/dist/src/trails/topo.d.ts +0 -5
  178. package/dist/src/trails/topo.d.ts.map +0 -1
  179. package/dist/src/trails/topo.js +0 -63
  180. package/dist/src/trails/topo.js.map +0 -1
  181. package/dist/src/trails/warden.d.ts +0 -20
  182. package/dist/src/trails/warden.d.ts.map +0 -1
  183. package/dist/src/trails/warden.js +0 -98
  184. package/dist/src/trails/warden.js.map +0 -1
  185. package/dist/src/versions.d.ts +0 -12
  186. package/dist/src/versions.d.ts.map +0 -1
  187. package/dist/src/versions.js +0 -23
  188. package/dist/src/versions.js.map +0 -1
  189. package/dist/tsconfig.tsbuildinfo +0 -1
  190. package/src/__tests__/add-trail.test.ts +0 -97
  191. package/src/__tests__/create.test.ts +0 -415
  192. package/src/__tests__/draft-promote.test.ts +0 -144
  193. package/src/__tests__/guide.test.ts +0 -96
  194. package/src/__tests__/load-app.test.ts +0 -419
  195. package/src/__tests__/survey.test.ts +0 -377
  196. package/src/__tests__/topo-dev.test.ts +0 -426
  197. package/src/__tests__/warden.test.ts +0 -74
  198. package/src/trails/topo-export.ts +0 -35
  199. package/src/trails/topo-show.ts +0 -54
  200. package/tsconfig.json +0 -9
  201. 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
- });
@@ -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
@@ -1,9 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.json",
3
- "compilerOptions": {
4
- "outDir": "dist",
5
- "rootDir": "."
6
- },
7
- "include": ["src", "bin"],
8
- "exclude": ["**/__tests__/**", "**/*.test.ts", "dist"]
9
- }
@@ -1,10 +0,0 @@
1
- {
2
- "extends": "./tsconfig.json",
3
- "compilerOptions": {
4
- "noEmit": true,
5
- "rootDir": ".",
6
- "types": ["bun"]
7
- },
8
- "include": ["src/**/*.test.ts", "src/__tests__/**/*.ts", "__tests__/**/*.ts"],
9
- "exclude": []
10
- }