@ontrails/warden 1.0.0-beta.11 → 1.0.0-beta.13

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 (183) hide show
  1. package/.turbo/turbo-lint.log +1 -1
  2. package/CHANGELOG.md +59 -31
  3. package/README.md +17 -17
  4. package/dist/cli.d.ts +1 -1
  5. package/dist/cli.d.ts.map +1 -1
  6. package/dist/cli.js +14 -10
  7. package/dist/cli.js.map +1 -1
  8. package/dist/drift.d.ts +6 -6
  9. package/dist/drift.d.ts.map +1 -1
  10. package/dist/drift.js +8 -8
  11. package/dist/drift.js.map +1 -1
  12. package/dist/formatters.js +2 -2
  13. package/dist/formatters.js.map +1 -1
  14. package/dist/index.d.ts +4 -4
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +5 -5
  17. package/dist/index.js.map +1 -1
  18. package/dist/rules/ast.d.ts +15 -11
  19. package/dist/rules/ast.d.ts.map +1 -1
  20. package/dist/rules/ast.js +34 -30
  21. package/dist/rules/ast.js.map +1 -1
  22. package/dist/rules/context-no-trailhead-types.d.ts +12 -0
  23. package/dist/rules/context-no-trailhead-types.d.ts.map +1 -0
  24. package/dist/rules/context-no-trailhead-types.js +96 -0
  25. package/dist/rules/context-no-trailhead-types.js.map +1 -0
  26. package/dist/rules/cross-declarations.d.ts +13 -0
  27. package/dist/rules/cross-declarations.d.ts.map +1 -0
  28. package/dist/rules/cross-declarations.js +264 -0
  29. package/dist/rules/cross-declarations.js.map +1 -0
  30. package/dist/rules/follow-declarations.d.ts +1 -1
  31. package/dist/rules/follow-declarations.js +5 -5
  32. package/dist/rules/follow-declarations.js.map +1 -1
  33. package/dist/rules/implementation-returns-result.d.ts +2 -2
  34. package/dist/rules/implementation-returns-result.js +6 -6
  35. package/dist/rules/implementation-returns-result.js.map +1 -1
  36. package/dist/rules/index.d.ts +4 -4
  37. package/dist/rules/index.d.ts.map +1 -1
  38. package/dist/rules/index.js +12 -12
  39. package/dist/rules/index.js.map +1 -1
  40. package/dist/rules/no-direct-impl-in-route.d.ts +4 -4
  41. package/dist/rules/no-direct-impl-in-route.js +14 -14
  42. package/dist/rules/no-direct-impl-in-route.js.map +1 -1
  43. package/dist/rules/no-direct-implementation-call.d.ts +3 -3
  44. package/dist/rules/no-direct-implementation-call.js +7 -7
  45. package/dist/rules/no-direct-implementation-call.js.map +1 -1
  46. package/dist/rules/no-sync-result-assumption.d.ts +1 -1
  47. package/dist/rules/no-sync-result-assumption.js +5 -5
  48. package/dist/rules/no-sync-result-assumption.js.map +1 -1
  49. package/dist/rules/no-throw-in-detour-target.js +2 -2
  50. package/dist/rules/no-throw-in-detour-target.js.map +1 -1
  51. package/dist/rules/no-throw-in-implementation.d.ts +1 -1
  52. package/dist/rules/no-throw-in-implementation.js +3 -3
  53. package/dist/rules/no-throw-in-implementation.js.map +1 -1
  54. package/dist/rules/provision-declarations.d.ts +14 -0
  55. package/dist/rules/provision-declarations.d.ts.map +1 -0
  56. package/dist/rules/provision-declarations.js +344 -0
  57. package/dist/rules/provision-declarations.js.map +1 -0
  58. package/dist/rules/provision-exists.d.ts +6 -0
  59. package/dist/rules/provision-exists.d.ts.map +1 -0
  60. package/dist/rules/provision-exists.js +89 -0
  61. package/dist/rules/provision-exists.js.map +1 -0
  62. package/dist/rules/service-declarations.d.ts +7 -5
  63. package/dist/rules/service-declarations.d.ts.map +1 -1
  64. package/dist/rules/service-declarations.js +106 -103
  65. package/dist/rules/service-declarations.js.map +1 -1
  66. package/dist/rules/service-exists.d.ts +3 -1
  67. package/dist/rules/service-exists.d.ts.map +1 -1
  68. package/dist/rules/service-exists.js +35 -33
  69. package/dist/rules/service-exists.js.map +1 -1
  70. package/dist/rules/specs.d.ts +1 -1
  71. package/dist/rules/specs.d.ts.map +1 -1
  72. package/dist/rules/specs.js +1 -1
  73. package/dist/rules/specs.js.map +1 -1
  74. package/dist/rules/types.d.ts +2 -2
  75. package/dist/rules/types.d.ts.map +1 -1
  76. package/dist/trails/context-no-surface-types.trail.js +1 -1
  77. package/dist/trails/context-no-trailhead-types.trail.d.ts +13 -0
  78. package/dist/trails/context-no-trailhead-types.trail.d.ts.map +1 -0
  79. package/dist/trails/context-no-trailhead-types.trail.js +21 -0
  80. package/dist/trails/context-no-trailhead-types.trail.js.map +1 -0
  81. package/dist/trails/cross-declarations.trail.d.ts +13 -0
  82. package/dist/trails/cross-declarations.trail.d.ts.map +1 -0
  83. package/dist/trails/cross-declarations.trail.js +22 -0
  84. package/dist/trails/cross-declarations.trail.js.map +1 -0
  85. package/dist/trails/follow-declarations.trail.js +1 -1
  86. package/dist/trails/implementation-returns-result.trail.js +1 -1
  87. package/dist/trails/index.d.ts +4 -4
  88. package/dist/trails/index.d.ts.map +1 -1
  89. package/dist/trails/index.js +4 -4
  90. package/dist/trails/index.js.map +1 -1
  91. package/dist/trails/no-direct-impl-in-route.trail.js +4 -4
  92. package/dist/trails/no-direct-impl-in-route.trail.js.map +1 -1
  93. package/dist/trails/no-direct-implementation-call.trail.js +2 -2
  94. package/dist/trails/no-direct-implementation-call.trail.js.map +1 -1
  95. package/dist/trails/no-sync-result-assumption.trail.js +2 -2
  96. package/dist/trails/no-sync-result-assumption.trail.js.map +1 -1
  97. package/dist/trails/no-throw-in-detour-target.trail.d.ts +1 -1
  98. package/dist/trails/no-throw-in-detour-target.trail.js +1 -1
  99. package/dist/trails/no-throw-in-implementation.trail.js +1 -1
  100. package/dist/trails/prefer-schema-inference.trail.js +1 -1
  101. package/dist/trails/provision-declarations.trail.d.ts +13 -0
  102. package/dist/trails/provision-declarations.trail.d.ts.map +1 -0
  103. package/dist/trails/provision-declarations.trail.js +25 -0
  104. package/dist/trails/provision-declarations.trail.js.map +1 -0
  105. package/dist/trails/provision-exists.trail.d.ts +15 -0
  106. package/dist/trails/provision-exists.trail.d.ts.map +1 -0
  107. package/dist/trails/provision-exists.trail.js +27 -0
  108. package/dist/trails/provision-exists.trail.js.map +1 -0
  109. package/dist/trails/run.d.ts +2 -2
  110. package/dist/trails/run.d.ts.map +1 -1
  111. package/dist/trails/run.js +6 -6
  112. package/dist/trails/run.js.map +1 -1
  113. package/dist/trails/schema.d.ts +1 -1
  114. package/dist/trails/schema.js +2 -2
  115. package/dist/trails/schema.js.map +1 -1
  116. package/dist/trails/service-declarations.trail.d.ts +13 -0
  117. package/dist/trails/service-declarations.trail.d.ts.map +1 -1
  118. package/dist/trails/service-declarations.trail.js +9 -7
  119. package/dist/trails/service-declarations.trail.js.map +1 -1
  120. package/dist/trails/service-exists.trail.d.ts +17 -0
  121. package/dist/trails/service-exists.trail.d.ts.map +1 -1
  122. package/dist/trails/service-exists.trail.js +10 -8
  123. package/dist/trails/service-exists.trail.js.map +1 -1
  124. package/dist/trails/valid-describe-refs.trail.d.ts +1 -1
  125. package/dist/trails/valid-detour-refs.trail.d.ts +1 -1
  126. package/dist/trails/valid-detour-refs.trail.js +2 -2
  127. package/dist/trails/wrap-rule.js +14 -14
  128. package/dist/trails/wrap-rule.js.map +1 -1
  129. package/package.json +4 -4
  130. package/src/__tests__/cli.test.ts +8 -8
  131. package/src/__tests__/{follow-declarations.test.ts → cross-declarations.test.ts} +78 -78
  132. package/src/__tests__/drift.test.ts +5 -5
  133. package/src/__tests__/formatters.test.ts +2 -2
  134. package/src/__tests__/implementation-returns-result.test.ts +11 -11
  135. package/src/__tests__/no-direct-implementation-call.test.ts +10 -10
  136. package/src/__tests__/no-sync-result-assumption.test.ts +6 -6
  137. package/src/__tests__/no-throw-in-detour-target.test.ts +6 -6
  138. package/src/__tests__/prefer-schema-inference.test.ts +4 -4
  139. package/src/__tests__/provision-declarations.test.ts +318 -0
  140. package/src/__tests__/provision-exists.test.ts +122 -0
  141. package/src/__tests__/rules.test.ts +38 -38
  142. package/src/__tests__/valid-describe-refs.test.ts +4 -4
  143. package/src/__tests__/wrap-rule.test.ts +4 -4
  144. package/src/cli.ts +17 -13
  145. package/src/drift.ts +12 -12
  146. package/src/formatters.ts +2 -2
  147. package/src/index.ts +8 -8
  148. package/src/rules/ast.ts +36 -31
  149. package/src/rules/{context-no-surface-types.ts → context-no-trailhead-types.ts} +8 -8
  150. package/src/rules/{follow-declarations.ts → cross-declarations.ts} +63 -56
  151. package/src/rules/implementation-returns-result.ts +6 -6
  152. package/src/rules/index.ts +12 -12
  153. package/src/rules/no-direct-impl-in-route.ts +17 -17
  154. package/src/rules/no-direct-implementation-call.ts +7 -7
  155. package/src/rules/no-sync-result-assumption.ts +5 -5
  156. package/src/rules/no-throw-in-detour-target.ts +2 -2
  157. package/src/rules/no-throw-in-implementation.ts +3 -3
  158. package/src/rules/{service-declarations.ts → provision-declarations.ts} +145 -129
  159. package/src/rules/{service-exists.ts → provision-exists.ts} +51 -46
  160. package/src/rules/specs.ts +4 -4
  161. package/src/rules/types.ts +2 -2
  162. package/src/trails/{context-no-surface-types.trail.ts → context-no-trailhead-types.trail.ts} +5 -5
  163. package/src/trails/cross-declarations.trail.ts +22 -0
  164. package/src/trails/implementation-returns-result.trail.ts +1 -1
  165. package/src/trails/index.ts +4 -4
  166. package/src/trails/no-direct-impl-in-route.trail.ts +4 -4
  167. package/src/trails/no-direct-implementation-call.trail.ts +2 -2
  168. package/src/trails/no-sync-result-assumption.trail.ts +2 -2
  169. package/src/trails/no-throw-in-detour-target.trail.ts +1 -1
  170. package/src/trails/no-throw-in-implementation.trail.ts +1 -1
  171. package/src/trails/prefer-schema-inference.trail.ts +1 -1
  172. package/src/trails/provision-declarations.trail.ts +25 -0
  173. package/src/trails/provision-exists.trail.ts +27 -0
  174. package/src/trails/run.ts +7 -7
  175. package/src/trails/schema.ts +2 -2
  176. package/src/trails/valid-detour-refs.trail.ts +2 -2
  177. package/src/trails/wrap-rule.ts +17 -17
  178. package/tsconfig.tsbuildinfo +1 -1
  179. package/src/__tests__/service-declarations.test.ts +0 -318
  180. package/src/__tests__/service-exists.test.ts +0 -122
  181. package/src/trails/follow-declarations.trail.ts +0 -22
  182. package/src/trails/service-declarations.trail.ts +0 -25
  183. package/src/trails/service-exists.trail.ts +0 -27
@@ -1,41 +1,41 @@
1
1
  import { describe, expect, test } from 'bun:test';
2
2
 
3
- import { followDeclarations } from '../rules/follow-declarations.js';
3
+ import { crossDeclarations } from '../rules/cross-declarations.js';
4
4
 
5
5
  const TEST_FILE = 'test.ts';
6
6
 
7
- describe('follow-declarations', () => {
7
+ describe('cross-declarations', () => {
8
8
  describe('clean cases', () => {
9
9
  test('declared and called match exactly', () => {
10
10
  const code = `
11
11
  import { trail, Result } from '@ontrails/core';
12
12
  const t = trail('onboard', {
13
- follow: ['entity.add', 'search'],
13
+ crosses: ['entity.add', 'search'],
14
14
  input: z.object({ name: z.string() }),
15
- run: async (input, ctx) => {
16
- await ctx.follow('entity.add', { name: input.name });
17
- await ctx.follow('search', { query: input.name });
15
+ blaze: async (input, ctx) => {
16
+ await ctx.cross('entity.add', { name: input.name });
17
+ await ctx.cross('search', { query: input.name });
18
18
  return Result.ok({});
19
19
  },
20
20
  });
21
21
  `;
22
22
 
23
- const diagnostics = followDeclarations.check(code, TEST_FILE);
23
+ const diagnostics = crossDeclarations.check(code, TEST_FILE);
24
24
 
25
25
  expect(diagnostics.length).toBe(0);
26
26
  });
27
27
 
28
- test('no follow declaration and no ctx.follow() calls', () => {
28
+ test('no crosses declaration and no ctx.cross() calls', () => {
29
29
  const code = `
30
30
  trail('simple', {
31
31
  input: z.object({ name: z.string() }),
32
- run: async (input, ctx) => {
32
+ blaze: async (input, ctx) => {
33
33
  return Result.ok({ greeting: 'hello ' + input.name });
34
34
  },
35
35
  });
36
36
  `;
37
37
 
38
- const diagnostics = followDeclarations.check(code, TEST_FILE);
38
+ const diagnostics = crossDeclarations.check(code, TEST_FILE);
39
39
 
40
40
  expect(diagnostics.length).toBe(0);
41
41
  });
@@ -46,20 +46,20 @@ trail('simple', {
46
46
  const code = `
47
47
  trail('onboard', {
48
48
  input: z.object({ name: z.string() }),
49
- run: async (input, ctx) => {
50
- await ctx.follow('entity.add', { name: input.name });
49
+ blaze: async (input, ctx) => {
50
+ await ctx.cross('entity.add', { name: input.name });
51
51
  return Result.ok({});
52
52
  },
53
53
  });
54
54
  `;
55
55
 
56
- const diagnostics = followDeclarations.check(code, TEST_FILE);
56
+ const diagnostics = crossDeclarations.check(code, TEST_FILE);
57
57
 
58
58
  expect(diagnostics.length).toBe(1);
59
59
  expect(diagnostics[0]?.severity).toBe('error');
60
- expect(diagnostics[0]?.rule).toBe('follow-declarations');
61
- expect(diagnostics[0]?.message).toContain("ctx.follow('entity.add')");
62
- expect(diagnostics[0]?.message).toContain('not declared in follow');
60
+ expect(diagnostics[0]?.rule).toBe('cross-declarations');
61
+ expect(diagnostics[0]?.message).toContain("ctx.cross('entity.add')");
62
+ expect(diagnostics[0]?.message).toContain('not declared in crosses');
63
63
  });
64
64
  });
65
65
 
@@ -67,56 +67,56 @@ trail('onboard', {
67
67
  test('declared but not called produces warning', () => {
68
68
  const code = `
69
69
  trail('onboard', {
70
- follow: ['entity.add', 'search'],
71
- run: async (input, ctx) => {
72
- await ctx.follow('entity.add', { name: input.name });
70
+ crosses: ['entity.add', 'search'],
71
+ blaze: async (input, ctx) => {
72
+ await ctx.cross('entity.add', { name: input.name });
73
73
  return Result.ok({});
74
74
  },
75
75
  });
76
76
  `;
77
77
 
78
- const diagnostics = followDeclarations.check(code, TEST_FILE);
78
+ const diagnostics = crossDeclarations.check(code, TEST_FILE);
79
79
 
80
80
  expect(diagnostics.length).toBe(1);
81
81
  expect(diagnostics[0]?.severity).toBe('warn');
82
- expect(diagnostics[0]?.rule).toBe('follow-declarations');
83
- expect(diagnostics[0]?.message).toContain("'search' declared in follow");
82
+ expect(diagnostics[0]?.rule).toBe('cross-declarations');
83
+ expect(diagnostics[0]?.message).toContain("'search' declared in crosses");
84
84
  expect(diagnostics[0]?.message).toContain('never called');
85
85
  });
86
86
  });
87
87
 
88
88
  describe('single-object overload', () => {
89
- test('recognizes trail({ id, follow, run }) form', () => {
89
+ test('recognizes trail({ id, crosses, blaze }) form', () => {
90
90
  const code = `
91
91
  trail({
92
92
  id: 'onboard',
93
- follow: ['entity.add'],
93
+ crosses: ['entity.add'],
94
94
  input: z.object({ name: z.string() }),
95
- run: async (input, ctx) => {
96
- await ctx.follow('entity.add', { name: input.name });
95
+ blaze: async (input, ctx) => {
96
+ await ctx.cross('entity.add', { name: input.name });
97
97
  return Result.ok({});
98
98
  },
99
99
  });
100
100
  `;
101
101
 
102
- const diagnostics = followDeclarations.check(code, TEST_FILE);
102
+ const diagnostics = crossDeclarations.check(code, TEST_FILE);
103
103
 
104
104
  expect(diagnostics.length).toBe(0);
105
105
  });
106
106
 
107
- test('detects undeclared follows in single-object form', () => {
107
+ test('detects undeclared crossings in single-object form', () => {
108
108
  const code = `
109
109
  trail({
110
110
  id: 'onboard',
111
111
  input: z.object({ name: z.string() }),
112
- run: async (input, ctx) => {
113
- await ctx.follow('entity.add', { name: input.name });
112
+ blaze: async (input, ctx) => {
113
+ await ctx.cross('entity.add', { name: input.name });
114
114
  return Result.ok({});
115
115
  },
116
116
  });
117
117
  `;
118
118
 
119
- const diagnostics = followDeclarations.check(code, TEST_FILE);
119
+ const diagnostics = crossDeclarations.check(code, TEST_FILE);
120
120
 
121
121
  expect(diagnostics.length).toBe(1);
122
122
  expect(diagnostics[0]?.severity).toBe('error');
@@ -125,112 +125,112 @@ trail({
125
125
  });
126
126
 
127
127
  describe('context parameter naming', () => {
128
- test('recognizes context.follow() when second param is named context', () => {
128
+ test('recognizes context.cross() when second param is named context', () => {
129
129
  const code = `
130
130
  trail('onboard', {
131
- follow: ['entity.add'],
131
+ crosses: ['entity.add'],
132
132
  input: z.object({ name: z.string() }),
133
- run: async (input, context) => {
134
- await context.follow('entity.add', { name: input.name });
133
+ blaze: async (input, context) => {
134
+ await context.cross('entity.add', { name: input.name });
135
135
  return Result.ok({});
136
136
  },
137
137
  });
138
138
  `;
139
139
 
140
- const diagnostics = followDeclarations.check(code, TEST_FILE);
140
+ const diagnostics = crossDeclarations.check(code, TEST_FILE);
141
141
 
142
142
  expect(diagnostics.length).toBe(0);
143
143
  });
144
144
 
145
- test('detects undeclared context.follow() calls', () => {
145
+ test('detects undeclared context.cross() calls', () => {
146
146
  const code = `
147
147
  trail('onboard', {
148
148
  input: z.object({ name: z.string() }),
149
- run: async (input, context) => {
150
- await context.follow('entity.add', { name: input.name });
149
+ blaze: async (input, context) => {
150
+ await context.cross('entity.add', { name: input.name });
151
151
  return Result.ok({});
152
152
  },
153
153
  });
154
154
  `;
155
155
 
156
- const diagnostics = followDeclarations.check(code, TEST_FILE);
156
+ const diagnostics = crossDeclarations.check(code, TEST_FILE);
157
157
 
158
158
  expect(diagnostics.length).toBe(1);
159
159
  expect(diagnostics[0]?.severity).toBe('error');
160
160
  });
161
161
 
162
- test('recognizes destructured follow() calls', () => {
162
+ test('recognizes destructured cross() calls', () => {
163
163
  const code = `
164
164
  trail('onboard', {
165
- follow: ['entity.add'],
165
+ crosses: ['entity.add'],
166
166
  input: z.object({ name: z.string() }),
167
- run: async (input, ctx) => {
168
- const { follow } = ctx;
169
- await follow('entity.add', { name: input.name });
167
+ blaze: async (input, ctx) => {
168
+ const { cross } = ctx;
169
+ await cross('entity.add', { name: input.name });
170
170
  return Result.ok({});
171
171
  },
172
172
  });
173
173
  `;
174
174
 
175
- const diagnostics = followDeclarations.check(code, TEST_FILE);
175
+ const diagnostics = crossDeclarations.check(code, TEST_FILE);
176
176
 
177
177
  expect(diagnostics.length).toBe(0);
178
178
  });
179
179
  });
180
180
 
181
181
  describe('nested run false positives', () => {
182
- test('metadata.run does not trigger false positives', () => {
182
+ test('meta.run does not trigger false positives', () => {
183
183
  const code = `
184
184
  trail('onboard', {
185
- follow: ['entity.add'],
185
+ crosses: ['entity.add'],
186
186
  input: z.object({ name: z.string() }),
187
- metadata: { run: async () => ctx.follow('phantom') },
188
- run: async (input, ctx) => {
189
- await ctx.follow('entity.add', { name: input.name });
187
+ meta: { blaze: async () => ctx.cross('phantom') },
188
+ blaze: async (input, ctx) => {
189
+ await ctx.cross('entity.add', { name: input.name });
190
190
  return Result.ok({});
191
191
  },
192
192
  });
193
193
  `;
194
194
 
195
- const diagnostics = followDeclarations.check(code, TEST_FILE);
195
+ const diagnostics = crossDeclarations.check(code, TEST_FILE);
196
196
 
197
197
  expect(diagnostics.length).toBe(0);
198
198
  });
199
199
  });
200
200
 
201
- describe('identifier resolution in follow arrays', () => {
202
- test('resolves const identifiers in follow array', () => {
201
+ describe('identifier resolution in crosses arrays', () => {
202
+ test('resolves const identifiers in crosses array', () => {
203
203
  const code = `
204
204
  const ENTITY_ADD = 'entity.add';
205
205
  trail('onboard', {
206
- follow: [ENTITY_ADD],
206
+ crosses: [ENTITY_ADD],
207
207
  input: z.object({ name: z.string() }),
208
- run: async (input, ctx) => {
209
- await ctx.follow('entity.add', { name: input.name });
208
+ blaze: async (input, ctx) => {
209
+ await ctx.cross('entity.add', { name: input.name });
210
210
  return Result.ok({});
211
211
  },
212
212
  });
213
213
  `;
214
214
 
215
- const diagnostics = followDeclarations.check(code, TEST_FILE);
215
+ const diagnostics = crossDeclarations.check(code, TEST_FILE);
216
216
 
217
217
  expect(diagnostics.length).toBe(0);
218
218
  });
219
219
 
220
- test('reports error when resolved identifier does not match called follow', () => {
220
+ test('reports error when resolved identifier does not match called cross', () => {
221
221
  const code = `
222
222
  const ENTITY_ADD = 'entity.add';
223
223
  trail('onboard', {
224
- follow: [ENTITY_ADD],
224
+ crosses: [ENTITY_ADD],
225
225
  input: z.object({ name: z.string() }),
226
- run: async (input, ctx) => {
227
- await ctx.follow('search', { name: input.name });
226
+ blaze: async (input, ctx) => {
227
+ await ctx.cross('search', { name: input.name });
228
228
  return Result.ok({});
229
229
  },
230
230
  });
231
231
  `;
232
232
 
233
- const diagnostics = followDeclarations.check(code, TEST_FILE);
233
+ const diagnostics = crossDeclarations.check(code, TEST_FILE);
234
234
 
235
235
  // 'search' called but not declared, 'entity.add' declared but not called
236
236
  expect(diagnostics.length).toBe(2);
@@ -238,20 +238,20 @@ trail('onboard', {
238
238
  });
239
239
 
240
240
  describe('edge cases', () => {
241
- test('dynamic follow IDs are skipped', () => {
241
+ test('dynamic cross IDs are skipped', () => {
242
242
  const code = `
243
243
  trail('dispatch', {
244
- follow: ['entity.add'],
245
- run: async (input, ctx) => {
244
+ crosses: ['entity.add'],
245
+ blaze: async (input, ctx) => {
246
246
  const trailId = input.target;
247
- await ctx.follow(trailId, input);
248
- await ctx.follow('entity.add', input);
247
+ await ctx.cross(trailId, input);
248
+ await ctx.cross('entity.add', input);
249
249
  return Result.ok({});
250
250
  },
251
251
  });
252
252
  `;
253
253
 
254
- const diagnostics = followDeclarations.check(code, TEST_FILE);
254
+ const diagnostics = crossDeclarations.check(code, TEST_FILE);
255
255
 
256
256
  expect(diagnostics.length).toBe(0);
257
257
  });
@@ -259,22 +259,22 @@ trail('dispatch', {
259
259
  test('multiple trails in one file are validated independently', () => {
260
260
  const code = `
261
261
  trail('alpha', {
262
- follow: ['shared'],
263
- run: async (input, ctx) => {
264
- await ctx.follow('shared', input);
262
+ crosses: ['shared'],
263
+ blaze: async (input, ctx) => {
264
+ await ctx.cross('shared', input);
265
265
  return Result.ok({});
266
266
  },
267
267
  });
268
268
 
269
269
  trail('beta', {
270
- run: async (input, ctx) => {
271
- await ctx.follow('undeclared', input);
270
+ blaze: async (input, ctx) => {
271
+ await ctx.cross('undeclared', input);
272
272
  return Result.ok({});
273
273
  },
274
274
  });
275
275
  `;
276
276
 
277
- const diagnostics = followDeclarations.check(code, TEST_FILE);
277
+ const diagnostics = crossDeclarations.check(code, TEST_FILE);
278
278
 
279
279
  expect(diagnostics.length).toBe(1);
280
280
  expect(diagnostics[0]?.message).toContain('Trail "beta"');
@@ -285,14 +285,14 @@ trail('beta', {
285
285
  test('skips test files', () => {
286
286
  const code = `
287
287
  trail('onboard', {
288
- run: async (input, ctx) => {
289
- await ctx.follow('entity.add', input);
288
+ blaze: async (input, ctx) => {
289
+ await ctx.cross('entity.add', input);
290
290
  return Result.ok({});
291
291
  },
292
292
  });
293
293
  `;
294
294
 
295
- const diagnostics = followDeclarations.check(
295
+ const diagnostics = crossDeclarations.check(
296
296
  code,
297
297
  'src/__tests__/trails.test.ts'
298
298
  );
@@ -4,16 +4,16 @@ import { join } from 'node:path';
4
4
  import { tmpdir } from 'node:os';
5
5
 
6
6
  import { trail, topo, Result } from '@ontrails/core';
7
- import { hashSurfaceMap, generateSurfaceMap } from '@ontrails/schema';
7
+ import { hashTrailheadMap, generateTrailheadMap } from '@ontrails/schema';
8
8
  import { z } from 'zod';
9
9
 
10
10
  import { checkDrift } from '../drift.js';
11
11
 
12
12
  const makeTopo = () => {
13
13
  const t = trail('test.hello', {
14
+ blaze: () => Result.ok({ greeting: 'hi' }),
14
15
  input: z.object({ name: z.string() }),
15
16
  output: z.object({ greeting: z.string() }),
16
- run: () => Result.ok({ greeting: 'hi' }),
17
17
  });
18
18
  return topo('test-app', { t });
19
19
  };
@@ -47,8 +47,8 @@ describe('checkDrift', () => {
47
47
  const dir = createTempDir();
48
48
  try {
49
49
  const tp = makeTopo();
50
- const hash = hashSurfaceMap(generateSurfaceMap(tp));
51
- writeFileSync(join(dir, 'surface.lock'), `${hash}\n`);
50
+ const hash = hashTrailheadMap(generateTrailheadMap(tp));
51
+ writeFileSync(join(dir, 'trailhead.lock'), `${hash}\n`);
52
52
 
53
53
  const result = await checkDrift(dir, tp);
54
54
  expect(result.stale).toBe(false);
@@ -62,7 +62,7 @@ describe('checkDrift', () => {
62
62
  test('returns stale: true when lock does not match', async () => {
63
63
  const dir = createTempDir();
64
64
  try {
65
- writeFileSync(join(dir, 'surface.lock'), 'outdated-hash\n');
65
+ writeFileSync(join(dir, 'trailhead.lock'), 'outdated-hash\n');
66
66
 
67
67
  const result = await checkDrift(dir, makeTopo());
68
68
  expect(result.stale).toBe(true);
@@ -67,7 +67,7 @@ describe('formatGitHubAnnotations', () => {
67
67
 
68
68
  test('emits drift as a single ::error annotation', () => {
69
69
  const output = formatGitHubAnnotations(reportWithDrift);
70
- expect(output).toContain('::error::drift: surface.lock is stale');
70
+ expect(output).toContain('::error::drift: trailhead.lock is stale');
71
71
  });
72
72
 
73
73
  test('produces one line per diagnostic', () => {
@@ -148,7 +148,7 @@ describe('formatSummary', () => {
148
148
  test('includes drift section when stale', () => {
149
149
  const output = formatSummary(reportWithDrift);
150
150
  expect(output).toContain('### Drift');
151
- expect(output).toContain('surface.lock is stale');
151
+ expect(output).toContain('trailhead.lock is stale');
152
152
  });
153
153
 
154
154
  test('omits drift section when clean', () => {
@@ -8,7 +8,7 @@ describe('implementation-returns-result', () => {
8
8
  test('flags raw object return in trail implementation', () => {
9
9
  const code = `
10
10
  trail("entity.show", {
11
- run: async (input, ctx) => {
11
+ blaze: async (input, ctx) => {
12
12
  return { name: "foo" };
13
13
  }
14
14
  })`;
@@ -20,18 +20,18 @@ trail("entity.show", {
20
20
  expect(diagnostics[0]?.severity).toBe('error');
21
21
  });
22
22
 
23
- test('allows Result.ok() and returning ctx.follow() results', () => {
23
+ test('allows Result.ok() and returning ctx.cross() results', () => {
24
24
  const code = `
25
25
  trail("entity.onboard", {
26
- follow: ["entity.create"],
27
- run: async (input, ctx) => {
28
- const result = await ctx.follow("entity.create", input);
26
+ crosses: ["entity.create"],
27
+ blaze: async (input, ctx) => {
28
+ const result = await ctx.cross("entity.create", input);
29
29
  return result;
30
30
  }
31
31
  })
32
32
 
33
33
  trail("entity.create", {
34
- run: async (input, ctx) => Result.ok({ id: "123" })
34
+ blaze: async (input, ctx) => Result.ok({ id: "123" })
35
35
  })`;
36
36
 
37
37
  const diagnostics = implementationReturnsResult.check(code, TEST_FILE);
@@ -42,7 +42,7 @@ trail("entity.create", {
42
42
  test('flags concise raw implementation bodies', () => {
43
43
  const code = `
44
44
  trail("entity.create", {
45
- run: async (input, ctx) => ({ id: "123" })
45
+ blaze: async (input, ctx) => ({ id: "123" })
46
46
  })`;
47
47
 
48
48
  const diagnostics = implementationReturnsResult.check(code, TEST_FILE);
@@ -54,7 +54,7 @@ trail("entity.create", {
54
54
  test('ignores return statements inside nested callbacks like .map()', () => {
55
55
  const code = `
56
56
  trail("entity.list", {
57
- run: async (input, ctx) => {
57
+ blaze: async (input, ctx) => {
58
58
  const items = ["a", "b", "c"];
59
59
  const mapped = items.map((item) => {
60
60
  return { name: item };
@@ -74,7 +74,7 @@ trail("entity.list", {
74
74
  test('ignores return statements inside .then() callbacks', () => {
75
75
  const code = `
76
76
  trail("entity.fetch", {
77
- run: async (input, ctx) => {
77
+ blaze: async (input, ctx) => {
78
78
  const data = await somePromise.then((res) => {
79
79
  return res.json();
80
80
  });
@@ -90,7 +90,7 @@ trail("entity.fetch", {
90
90
  test('still flags raw returns at the implementation level', () => {
91
91
  const code = `
92
92
  trail("entity.list", {
93
- run: async (input, ctx) => {
93
+ blaze: async (input, ctx) => {
94
94
  const items = ["a", "b"].map((item) => {
95
95
  return { name: item };
96
96
  });
@@ -113,7 +113,7 @@ const buildDiff = async (): Promise<Result<object, Error>> =>
113
113
  Result.ok({ breaking: [] });
114
114
 
115
115
  trail("survey", {
116
- run: async (input, ctx) => {
116
+ blaze: async (input, ctx) => {
117
117
  if (input.diff) {
118
118
  return await buildDiff();
119
119
  }
@@ -8,11 +8,11 @@ describe('no-direct-implementation-call', () => {
8
8
  import { trail, Result } from "@ontrails/core";
9
9
 
10
10
  const entityShow = trail("entity.show", {
11
- run: async (input, ctx) => Result.ok({ id: input.id }),
11
+ blaze: async (input, ctx) => Result.ok({ id: input.id }),
12
12
  });
13
13
 
14
14
  async function run() {
15
- const result = await entityShow.run({ id: "1" }, ctx);
15
+ const result = await entityShow.blaze({ id: "1" }, ctx);
16
16
  return result;
17
17
  }`;
18
18
 
@@ -21,15 +21,15 @@ async function run() {
21
21
  expect(diagnostics).toHaveLength(1);
22
22
  expect(diagnostics[0]?.rule).toBe('no-direct-implementation-call');
23
23
  expect(diagnostics[0]?.severity).toBe('warn');
24
- expect(diagnostics[0]?.message).toContain('ctx.follow');
24
+ expect(diagnostics[0]?.message).toContain('ctx.cross');
25
25
  });
26
26
 
27
- test('allows ctx.follow() calls', () => {
27
+ test('allows ctx.cross() calls', () => {
28
28
  const code = `
29
29
  trail("entity.onboard", {
30
- follow: ["entity.create"],
31
- run: async (input, ctx) => {
32
- const result = await ctx.follow("entity.create", input);
30
+ crosses: ["entity.create"],
31
+ blaze: async (input, ctx) => {
32
+ const result = await ctx.cross("entity.create", input);
33
33
  return Result.ok(result);
34
34
  },
35
35
  });
@@ -43,7 +43,7 @@ trail("entity.onboard", {
43
43
  test('ignores test files', () => {
44
44
  const code = `
45
45
  async function run() {
46
- return await entityShow.run({ id: "1" }, ctx);
46
+ return await entityShow.blaze({ id: "1" }, ctx);
47
47
  }`;
48
48
 
49
49
  const diagnostics = noDirectImplementationCall.check(
@@ -57,7 +57,7 @@ async function run() {
57
57
  test('ignores framework internals that intentionally call implementations', () => {
58
58
  const code = `
59
59
  export async function run() {
60
- return await entityShow.run({ id: "1" }, ctx);
60
+ return await entityShow.blaze({ id: "1" }, ctx);
61
61
  }`;
62
62
 
63
63
  const diagnostics = noDirectImplementationCall.check(
@@ -70,7 +70,7 @@ export async function run() {
70
70
 
71
71
  test('ignores implementation references inside template strings', () => {
72
72
  const code = `
73
- const generated = \`const result = await entityShow.run({ id: "1" }, ctx);\`;
73
+ const generated = \`const result = await entityShow.blaze({ id: "1" }, ctx);\`;
74
74
  `;
75
75
 
76
76
  const diagnostics = noDirectImplementationCall.check(
@@ -6,7 +6,7 @@ describe('no-sync-result-assumption', () => {
6
6
  test('flags direct result access on implementation calls', () => {
7
7
  const code = `
8
8
  async function run() {
9
- const isOk = entityShow.run({ id: "1" }, ctx).isOk();
9
+ const isOk = entityShow.blaze({ id: "1" }, ctx).isOk();
10
10
  return isOk;
11
11
  }`;
12
12
 
@@ -20,7 +20,7 @@ async function run() {
20
20
 
21
21
  test('flags a stored implementation result that is used synchronously', () => {
22
22
  const code = `
23
- const result = entityShow.run({ id: "1" }, ctx);
23
+ const result = entityShow.blaze({ id: "1" }, ctx);
24
24
 
25
25
  if (result.isOk()) {
26
26
  console.log("ok");
@@ -35,7 +35,7 @@ if (result.isOk()) {
35
35
  test('allows awaited implementation calls before result access', () => {
36
36
  const code = `
37
37
  async function run() {
38
- const result = await entityShow.run({ id: "1" }, ctx);
38
+ const result = await entityShow.blaze({ id: "1" }, ctx);
39
39
  return result.isOk();
40
40
  }`;
41
41
 
@@ -47,7 +47,7 @@ async function run() {
47
47
  test('allows awaited implementation calls when the property access is chained', () => {
48
48
  const code = `
49
49
  async function run() {
50
- return (await entityShow.run({ id: "1" }, ctx)).isOk();
50
+ return (await entityShow.blaze({ id: "1" }, ctx)).isOk();
51
51
  }`;
52
52
 
53
53
  const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
@@ -57,7 +57,7 @@ async function run() {
57
57
 
58
58
  test('ignores test files', () => {
59
59
  const code = `
60
- const result = entityShow.run({ id: "1" }, ctx);
60
+ const result = entityShow.blaze({ id: "1" }, ctx);
61
61
  result.isOk();
62
62
  `;
63
63
 
@@ -71,7 +71,7 @@ result.isOk();
71
71
 
72
72
  test('ignores framework internals that intentionally call implementations', () => {
73
73
  const code = `
74
- const result = entityShow.run({ id: "1" }, ctx);
74
+ const result = entityShow.blaze({ id: "1" }, ctx);
75
75
  result.isOk();
76
76
  `;
77
77
 
@@ -9,11 +9,11 @@ describe('no-throw-in-detour-target', () => {
9
9
  const code = `
10
10
  trail("entity.show", {
11
11
  detours: { NotFoundError: ["entity.fallback"] },
12
- run: async (input, ctx) => Result.ok({ id: "123" })
12
+ blaze: async (input, ctx) => Result.ok({ id: "123" })
13
13
  })
14
14
 
15
15
  trail("entity.fallback", {
16
- run: async (input, ctx) => {
16
+ blaze: async (input, ctx) => {
17
17
  throw new Error("boom");
18
18
  }
19
19
  })`;
@@ -28,7 +28,7 @@ trail("entity.fallback", {
28
28
  test('allows throw in implementations that are not detour targets', () => {
29
29
  const code = `
30
30
  trail("entity.show", {
31
- run: async (input, ctx) => {
31
+ blaze: async (input, ctx) => {
32
32
  throw new Error("boom");
33
33
  }
34
34
  })`;
@@ -42,11 +42,11 @@ trail("entity.show", {
42
42
  const code = `
43
43
  trail("entity.show", {
44
44
  detours: { NotFoundError: ["entity.fallback"] },
45
- run: async (input, ctx) => Result.ok({ id: "123" })
45
+ blaze: async (input, ctx) => Result.ok({ id: "123" })
46
46
  })
47
47
 
48
48
  trail("entity.fallback", {
49
- run: async (input, ctx) => { throw new Error("boom"); }
49
+ blaze: async (input, ctx) => { throw new Error("boom"); }
50
50
  })`;
51
51
 
52
52
  const diagnostics = noThrowInDetourTarget.check(code, TEST_FILE);
@@ -58,7 +58,7 @@ trail("entity.fallback", {
58
58
  test('uses project context when the detour target is defined in another file', () => {
59
59
  const code = `
60
60
  trail("entity.fallback", {
61
- run: async (input, ctx) => {
61
+ blaze: async (input, ctx) => {
62
62
  throw new Error("boom");
63
63
  }
64
64
  })`;