@ontrails/warden 1.0.0-beta.12 → 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 (182) hide show
  1. package/CHANGELOG.md +51 -31
  2. package/README.md +17 -17
  3. package/dist/cli.d.ts +1 -1
  4. package/dist/cli.d.ts.map +1 -1
  5. package/dist/cli.js +14 -10
  6. package/dist/cli.js.map +1 -1
  7. package/dist/drift.d.ts +6 -6
  8. package/dist/drift.d.ts.map +1 -1
  9. package/dist/drift.js +8 -8
  10. package/dist/drift.js.map +1 -1
  11. package/dist/formatters.js +2 -2
  12. package/dist/formatters.js.map +1 -1
  13. package/dist/index.d.ts +4 -4
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +5 -5
  16. package/dist/index.js.map +1 -1
  17. package/dist/rules/ast.d.ts +15 -11
  18. package/dist/rules/ast.d.ts.map +1 -1
  19. package/dist/rules/ast.js +34 -30
  20. package/dist/rules/ast.js.map +1 -1
  21. package/dist/rules/context-no-trailhead-types.d.ts +12 -0
  22. package/dist/rules/context-no-trailhead-types.d.ts.map +1 -0
  23. package/dist/rules/context-no-trailhead-types.js +96 -0
  24. package/dist/rules/context-no-trailhead-types.js.map +1 -0
  25. package/dist/rules/cross-declarations.d.ts +13 -0
  26. package/dist/rules/cross-declarations.d.ts.map +1 -0
  27. package/dist/rules/cross-declarations.js +264 -0
  28. package/dist/rules/cross-declarations.js.map +1 -0
  29. package/dist/rules/follow-declarations.d.ts +1 -1
  30. package/dist/rules/follow-declarations.js +5 -5
  31. package/dist/rules/follow-declarations.js.map +1 -1
  32. package/dist/rules/implementation-returns-result.d.ts +2 -2
  33. package/dist/rules/implementation-returns-result.js +6 -6
  34. package/dist/rules/implementation-returns-result.js.map +1 -1
  35. package/dist/rules/index.d.ts +4 -4
  36. package/dist/rules/index.d.ts.map +1 -1
  37. package/dist/rules/index.js +12 -12
  38. package/dist/rules/index.js.map +1 -1
  39. package/dist/rules/no-direct-impl-in-route.d.ts +4 -4
  40. package/dist/rules/no-direct-impl-in-route.js +14 -14
  41. package/dist/rules/no-direct-impl-in-route.js.map +1 -1
  42. package/dist/rules/no-direct-implementation-call.d.ts +3 -3
  43. package/dist/rules/no-direct-implementation-call.js +7 -7
  44. package/dist/rules/no-direct-implementation-call.js.map +1 -1
  45. package/dist/rules/no-sync-result-assumption.d.ts +1 -1
  46. package/dist/rules/no-sync-result-assumption.js +5 -5
  47. package/dist/rules/no-sync-result-assumption.js.map +1 -1
  48. package/dist/rules/no-throw-in-detour-target.js +2 -2
  49. package/dist/rules/no-throw-in-detour-target.js.map +1 -1
  50. package/dist/rules/no-throw-in-implementation.d.ts +1 -1
  51. package/dist/rules/no-throw-in-implementation.js +3 -3
  52. package/dist/rules/no-throw-in-implementation.js.map +1 -1
  53. package/dist/rules/provision-declarations.d.ts +14 -0
  54. package/dist/rules/provision-declarations.d.ts.map +1 -0
  55. package/dist/rules/provision-declarations.js +344 -0
  56. package/dist/rules/provision-declarations.js.map +1 -0
  57. package/dist/rules/provision-exists.d.ts +6 -0
  58. package/dist/rules/provision-exists.d.ts.map +1 -0
  59. package/dist/rules/provision-exists.js +89 -0
  60. package/dist/rules/provision-exists.js.map +1 -0
  61. package/dist/rules/service-declarations.d.ts +7 -5
  62. package/dist/rules/service-declarations.d.ts.map +1 -1
  63. package/dist/rules/service-declarations.js +106 -103
  64. package/dist/rules/service-declarations.js.map +1 -1
  65. package/dist/rules/service-exists.d.ts +3 -1
  66. package/dist/rules/service-exists.d.ts.map +1 -1
  67. package/dist/rules/service-exists.js +35 -33
  68. package/dist/rules/service-exists.js.map +1 -1
  69. package/dist/rules/specs.d.ts +1 -1
  70. package/dist/rules/specs.d.ts.map +1 -1
  71. package/dist/rules/specs.js +1 -1
  72. package/dist/rules/specs.js.map +1 -1
  73. package/dist/rules/types.d.ts +2 -2
  74. package/dist/rules/types.d.ts.map +1 -1
  75. package/dist/trails/context-no-surface-types.trail.js +1 -1
  76. package/dist/trails/context-no-trailhead-types.trail.d.ts +13 -0
  77. package/dist/trails/context-no-trailhead-types.trail.d.ts.map +1 -0
  78. package/dist/trails/context-no-trailhead-types.trail.js +21 -0
  79. package/dist/trails/context-no-trailhead-types.trail.js.map +1 -0
  80. package/dist/trails/cross-declarations.trail.d.ts +13 -0
  81. package/dist/trails/cross-declarations.trail.d.ts.map +1 -0
  82. package/dist/trails/cross-declarations.trail.js +22 -0
  83. package/dist/trails/cross-declarations.trail.js.map +1 -0
  84. package/dist/trails/follow-declarations.trail.js +1 -1
  85. package/dist/trails/implementation-returns-result.trail.js +1 -1
  86. package/dist/trails/index.d.ts +4 -4
  87. package/dist/trails/index.d.ts.map +1 -1
  88. package/dist/trails/index.js +4 -4
  89. package/dist/trails/index.js.map +1 -1
  90. package/dist/trails/no-direct-impl-in-route.trail.js +4 -4
  91. package/dist/trails/no-direct-impl-in-route.trail.js.map +1 -1
  92. package/dist/trails/no-direct-implementation-call.trail.js +2 -2
  93. package/dist/trails/no-direct-implementation-call.trail.js.map +1 -1
  94. package/dist/trails/no-sync-result-assumption.trail.js +2 -2
  95. package/dist/trails/no-sync-result-assumption.trail.js.map +1 -1
  96. package/dist/trails/no-throw-in-detour-target.trail.d.ts +1 -1
  97. package/dist/trails/no-throw-in-detour-target.trail.js +1 -1
  98. package/dist/trails/no-throw-in-implementation.trail.js +1 -1
  99. package/dist/trails/prefer-schema-inference.trail.js +1 -1
  100. package/dist/trails/provision-declarations.trail.d.ts +13 -0
  101. package/dist/trails/provision-declarations.trail.d.ts.map +1 -0
  102. package/dist/trails/provision-declarations.trail.js +25 -0
  103. package/dist/trails/provision-declarations.trail.js.map +1 -0
  104. package/dist/trails/provision-exists.trail.d.ts +15 -0
  105. package/dist/trails/provision-exists.trail.d.ts.map +1 -0
  106. package/dist/trails/provision-exists.trail.js +27 -0
  107. package/dist/trails/provision-exists.trail.js.map +1 -0
  108. package/dist/trails/run.d.ts +2 -2
  109. package/dist/trails/run.d.ts.map +1 -1
  110. package/dist/trails/run.js +6 -6
  111. package/dist/trails/run.js.map +1 -1
  112. package/dist/trails/schema.d.ts +1 -1
  113. package/dist/trails/schema.js +2 -2
  114. package/dist/trails/schema.js.map +1 -1
  115. package/dist/trails/service-declarations.trail.d.ts +13 -0
  116. package/dist/trails/service-declarations.trail.d.ts.map +1 -1
  117. package/dist/trails/service-declarations.trail.js +9 -7
  118. package/dist/trails/service-declarations.trail.js.map +1 -1
  119. package/dist/trails/service-exists.trail.d.ts +17 -0
  120. package/dist/trails/service-exists.trail.d.ts.map +1 -1
  121. package/dist/trails/service-exists.trail.js +10 -8
  122. package/dist/trails/service-exists.trail.js.map +1 -1
  123. package/dist/trails/valid-describe-refs.trail.d.ts +1 -1
  124. package/dist/trails/valid-detour-refs.trail.d.ts +1 -1
  125. package/dist/trails/valid-detour-refs.trail.js +2 -2
  126. package/dist/trails/wrap-rule.js +14 -14
  127. package/dist/trails/wrap-rule.js.map +1 -1
  128. package/package.json +1 -1
  129. package/src/__tests__/cli.test.ts +8 -8
  130. package/src/__tests__/{follow-declarations.test.ts → cross-declarations.test.ts} +78 -78
  131. package/src/__tests__/drift.test.ts +5 -5
  132. package/src/__tests__/formatters.test.ts +2 -2
  133. package/src/__tests__/implementation-returns-result.test.ts +11 -11
  134. package/src/__tests__/no-direct-implementation-call.test.ts +10 -10
  135. package/src/__tests__/no-sync-result-assumption.test.ts +6 -6
  136. package/src/__tests__/no-throw-in-detour-target.test.ts +6 -6
  137. package/src/__tests__/prefer-schema-inference.test.ts +4 -4
  138. package/src/__tests__/provision-declarations.test.ts +318 -0
  139. package/src/__tests__/provision-exists.test.ts +122 -0
  140. package/src/__tests__/rules.test.ts +38 -38
  141. package/src/__tests__/valid-describe-refs.test.ts +4 -4
  142. package/src/__tests__/wrap-rule.test.ts +4 -4
  143. package/src/cli.ts +17 -13
  144. package/src/drift.ts +12 -12
  145. package/src/formatters.ts +2 -2
  146. package/src/index.ts +8 -8
  147. package/src/rules/ast.ts +36 -31
  148. package/src/rules/{context-no-surface-types.ts → context-no-trailhead-types.ts} +8 -8
  149. package/src/rules/{follow-declarations.ts → cross-declarations.ts} +63 -56
  150. package/src/rules/implementation-returns-result.ts +6 -6
  151. package/src/rules/index.ts +12 -12
  152. package/src/rules/no-direct-impl-in-route.ts +17 -17
  153. package/src/rules/no-direct-implementation-call.ts +7 -7
  154. package/src/rules/no-sync-result-assumption.ts +5 -5
  155. package/src/rules/no-throw-in-detour-target.ts +2 -2
  156. package/src/rules/no-throw-in-implementation.ts +3 -3
  157. package/src/rules/{service-declarations.ts → provision-declarations.ts} +145 -129
  158. package/src/rules/{service-exists.ts → provision-exists.ts} +51 -46
  159. package/src/rules/specs.ts +4 -4
  160. package/src/rules/types.ts +2 -2
  161. package/src/trails/{context-no-surface-types.trail.ts → context-no-trailhead-types.trail.ts} +5 -5
  162. package/src/trails/cross-declarations.trail.ts +22 -0
  163. package/src/trails/implementation-returns-result.trail.ts +1 -1
  164. package/src/trails/index.ts +4 -4
  165. package/src/trails/no-direct-impl-in-route.trail.ts +4 -4
  166. package/src/trails/no-direct-implementation-call.trail.ts +2 -2
  167. package/src/trails/no-sync-result-assumption.trail.ts +2 -2
  168. package/src/trails/no-throw-in-detour-target.trail.ts +1 -1
  169. package/src/trails/no-throw-in-implementation.trail.ts +1 -1
  170. package/src/trails/prefer-schema-inference.trail.ts +1 -1
  171. package/src/trails/provision-declarations.trail.ts +25 -0
  172. package/src/trails/provision-exists.trail.ts +27 -0
  173. package/src/trails/run.ts +7 -7
  174. package/src/trails/schema.ts +2 -2
  175. package/src/trails/valid-detour-refs.trail.ts +2 -2
  176. package/src/trails/wrap-rule.ts +17 -17
  177. package/tsconfig.tsbuildinfo +1 -1
  178. package/src/__tests__/service-declarations.test.ts +0 -318
  179. package/src/__tests__/service-exists.test.ts +0 -122
  180. package/src/trails/follow-declarations.trail.ts +0 -22
  181. package/src/trails/service-declarations.trail.ts +0 -25
  182. package/src/trails/service-exists.trail.ts +0 -27
@@ -1,6 +1,6 @@
1
1
  import {
2
- collectNamedServiceIds,
3
- collectServiceDefinitionIds,
2
+ collectNamedProvisionIds,
3
+ collectProvisionDefinitionIds,
4
4
  extractFirstStringArg,
5
5
  findConfigProperty,
6
6
  findTrailDefinitions,
@@ -18,18 +18,18 @@ import type {
18
18
  WardenDiagnostic,
19
19
  } from './types.js';
20
20
 
21
- const isServiceCall = (node: AstNode): boolean =>
21
+ const isProvisionCall = (node: AstNode): boolean =>
22
22
  node.type === 'CallExpression' &&
23
23
  identifierName((node as unknown as { callee?: AstNode }).callee) ===
24
- 'service';
24
+ 'provision';
25
25
 
26
- const getServiceElements = (config: AstNode): readonly AstNode[] => {
27
- const servicesProp = findConfigProperty(config, 'services');
28
- if (!servicesProp) {
26
+ const getProvisionElements = (config: AstNode): readonly AstNode[] => {
27
+ const provisionsProp = findConfigProperty(config, 'provisions');
28
+ if (!provisionsProp) {
29
29
  return [];
30
30
  }
31
31
 
32
- const arrayNode = servicesProp.value;
32
+ const arrayNode = provisionsProp.value;
33
33
  if (!arrayNode || (arrayNode as AstNode).type !== 'ArrayExpression') {
34
34
  return [];
35
35
  }
@@ -40,93 +40,93 @@ const getServiceElements = (config: AstNode): readonly AstNode[] => {
40
40
  return elements ?? [];
41
41
  };
42
42
 
43
- const extractDeclaredServiceId = (
43
+ const extractDeclaredProvisionId = (
44
44
  element: AstNode,
45
- serviceIdsByName: ReadonlyMap<string, string>
45
+ provisionIdsByName: ReadonlyMap<string, string>
46
46
  ): string | null => {
47
47
  if (element.type === 'Identifier') {
48
48
  const name = identifierName(element);
49
- return name ? (serviceIdsByName.get(name) ?? null) : null;
49
+ return name ? (provisionIdsByName.get(name) ?? null) : null;
50
50
  }
51
51
 
52
52
  if (isStringLiteral(element)) {
53
53
  return getStringValue(element);
54
54
  }
55
55
 
56
- return isServiceCall(element) ? extractFirstStringArg(element) : null;
56
+ return isProvisionCall(element) ? extractFirstStringArg(element) : null;
57
57
  };
58
58
 
59
- const extractDeclaredServiceIds = (
59
+ const extractDeclaredProvisionIds = (
60
60
  config: AstNode,
61
- serviceIdsByName: ReadonlyMap<string, string>
61
+ provisionIdsByName: ReadonlyMap<string, string>
62
62
  ): readonly string[] => [
63
63
  ...new Set(
64
- getServiceElements(config).flatMap((element) => {
65
- const id = extractDeclaredServiceId(element, serviceIdsByName);
64
+ getProvisionElements(config).flatMap((element) => {
65
+ const id = extractDeclaredProvisionId(element, provisionIdsByName);
66
66
  return id ? [id] : [];
67
67
  })
68
68
  ),
69
69
  ];
70
70
 
71
- const buildMissingServiceDiagnostic = (
71
+ const buildMissingProvisionDiagnostic = (
72
72
  trailId: string,
73
- serviceId: string,
73
+ provisionId: string,
74
74
  filePath: string,
75
75
  line: number
76
76
  ): WardenDiagnostic => ({
77
77
  filePath,
78
78
  line,
79
- message: `Trail "${trailId}" declares service "${serviceId}" which is not defined in the project.`,
80
- rule: 'service-exists',
79
+ message: `Trail "${trailId}" declares provision "${provisionId}" which is not defined in the project.`,
80
+ rule: 'provision-exists',
81
81
  severity: 'error',
82
82
  });
83
83
 
84
- const reportMissingServices = (
84
+ const reportMissingProvisions = (
85
85
  def: { id: string; config: AstNode; start: number },
86
86
  sourceCode: string,
87
- serviceIdsByName: ReadonlyMap<string, string>,
87
+ provisionIdsByName: ReadonlyMap<string, string>,
88
88
  filePath: string,
89
- knownServiceIds: ReadonlySet<string>,
89
+ knownProvisionIds: ReadonlySet<string>,
90
90
  diagnostics: WardenDiagnostic[]
91
91
  ): void => {
92
92
  const line = offsetToLine(sourceCode, def.start);
93
- for (const serviceId of extractDeclaredServiceIds(
93
+ for (const provisionId of extractDeclaredProvisionIds(
94
94
  def.config,
95
- serviceIdsByName
95
+ provisionIdsByName
96
96
  )) {
97
- if (!knownServiceIds.has(serviceId)) {
97
+ if (!knownProvisionIds.has(provisionId)) {
98
98
  diagnostics.push(
99
- buildMissingServiceDiagnostic(def.id, serviceId, filePath, line)
99
+ buildMissingProvisionDiagnostic(def.id, provisionId, filePath, line)
100
100
  );
101
101
  }
102
102
  }
103
103
  };
104
104
 
105
- const buildServiceDiagnostics = (
105
+ const buildProvisionDiagnostics = (
106
106
  ast: AstNode,
107
107
  sourceCode: string,
108
108
  filePath: string,
109
- knownServiceIds: ReadonlySet<string>
109
+ knownProvisionIds: ReadonlySet<string>
110
110
  ): readonly WardenDiagnostic[] => {
111
111
  const diagnostics: WardenDiagnostic[] = [];
112
- const serviceIdsByName = collectNamedServiceIds(ast);
112
+ const provisionIdsByName = collectNamedProvisionIds(ast);
113
113
  for (const def of findTrailDefinitions(ast)) {
114
- reportMissingServices(
114
+ reportMissingProvisions(
115
115
  def,
116
116
  sourceCode,
117
- serviceIdsByName,
117
+ provisionIdsByName,
118
118
  filePath,
119
- knownServiceIds,
119
+ knownProvisionIds,
120
120
  diagnostics
121
121
  );
122
122
  }
123
123
  return diagnostics;
124
124
  };
125
125
 
126
- const checkServicesExist = (
126
+ const checkProvisionsExist = (
127
127
  sourceCode: string,
128
128
  filePath: string,
129
- knownServiceIds: ReadonlySet<string>
129
+ knownProvisionIds: ReadonlySet<string>
130
130
  ): readonly WardenDiagnostic[] => {
131
131
  if (isTestFile(filePath)) {
132
132
  return [];
@@ -137,22 +137,27 @@ const checkServicesExist = (
137
137
  return [];
138
138
  }
139
139
 
140
- return buildServiceDiagnostics(ast, sourceCode, filePath, knownServiceIds);
140
+ return buildProvisionDiagnostics(
141
+ ast,
142
+ sourceCode,
143
+ filePath,
144
+ knownProvisionIds
145
+ );
141
146
  };
142
147
 
143
148
  /**
144
- * Checks that all declared services resolve to known service definitions.
149
+ * Checks that all declared provisions resolve to known provision definitions.
145
150
  */
146
- export const serviceExists: ProjectAwareWardenRule = {
151
+ export const provisionExists: ProjectAwareWardenRule = {
147
152
  check(sourceCode: string, filePath: string): readonly WardenDiagnostic[] {
148
153
  const ast = parse(filePath, sourceCode);
149
154
  if (!ast) {
150
155
  return [];
151
156
  }
152
- return checkServicesExist(
157
+ return checkProvisionsExist(
153
158
  sourceCode,
154
159
  filePath,
155
- collectServiceDefinitionIds(ast)
160
+ collectProvisionDefinitionIds(ast)
156
161
  );
157
162
  },
158
163
  checkWithContext(
@@ -161,17 +166,17 @@ export const serviceExists: ProjectAwareWardenRule = {
161
166
  context: ProjectContext
162
167
  ): readonly WardenDiagnostic[] {
163
168
  const ast = parse(filePath, sourceCode);
164
- const localServiceIds = ast
165
- ? collectServiceDefinitionIds(ast)
169
+ const localProvisionIds = ast
170
+ ? collectProvisionDefinitionIds(ast)
166
171
  : new Set<string>();
167
- return checkServicesExist(
172
+ return checkProvisionsExist(
168
173
  sourceCode,
169
174
  filePath,
170
- context.knownServiceIds ?? localServiceIds
175
+ context.knownProvisionIds ?? localProvisionIds
171
176
  );
172
177
  },
173
178
  description:
174
- 'Ensure every service declared on a trail resolves to a known service definition.',
175
- name: 'service-exists',
179
+ 'Ensure every provision declared on a trail resolves to a known provision definition.',
180
+ name: 'provision-exists',
176
181
  severity: 'error',
177
182
  };
@@ -18,7 +18,7 @@ export interface ObjectProperty extends ParsedEntry {
18
18
 
19
19
  export interface TrailLikeSpec {
20
20
  readonly id: string;
21
- readonly kind: 'event' | 'trail';
21
+ readonly kind: 'signal' | 'trail';
22
22
  readonly line: number;
23
23
  readonly properties: ReadonlyMap<string, ObjectProperty>;
24
24
  readonly specText: string;
@@ -214,7 +214,7 @@ const resolveSpecId = (
214
214
 
215
215
  const buildTrailLikeSpec = (
216
216
  sourceCode: string,
217
- kind: 'event' | 'trail',
217
+ kind: 'signal' | 'trail',
218
218
  specArg: SplitEntry,
219
219
  specStart: number,
220
220
  id: string,
@@ -276,7 +276,7 @@ const resolveTrailLikeSpec = (
276
276
 
277
277
  const parseTrailLikeMatch = (
278
278
  sourceCode: string,
279
- kind: 'event' | 'trail',
279
+ kind: 'signal' | 'trail',
280
280
  callStart: number
281
281
  ): TrailLikeSpec | null => {
282
282
  const resolved = resolveTrailLikeSpec(sourceCode, callStart);
@@ -352,7 +352,7 @@ export const findTrailLikeSpecs = (
352
352
  continue;
353
353
  }
354
354
 
355
- const kind = match[1] === 'event' ? 'event' : 'trail';
355
+ const kind = match[1] === 'signal' ? 'signal' : 'trail';
356
356
  const spec = parseTrailLikeMatch(sourceCode, kind, callStart);
357
357
  if (spec !== null) {
358
358
  specs.push(spec);
@@ -45,8 +45,8 @@ export interface WardenRule {
45
45
  export interface ProjectContext {
46
46
  /** All known trail IDs in the project */
47
47
  readonly knownTrailIds: ReadonlySet<string>;
48
- /** All known service IDs in the project */
49
- readonly knownServiceIds?: ReadonlySet<string>;
48
+ /** All known provision IDs in the project */
49
+ readonly knownProvisionIds?: ReadonlySet<string>;
50
50
  /** All trail IDs referenced as detour targets across the project */
51
51
  readonly detourTargetTrailIds?: ReadonlySet<string>;
52
52
  }
@@ -1,7 +1,7 @@
1
- import { contextNoSurfaceTypes } from '../rules/context-no-surface-types.js';
1
+ import { contextNoTrailheadTypes } from '../rules/context-no-trailhead-types.js';
2
2
  import { wrapRule } from './wrap-rule.js';
3
3
 
4
- export const contextNoSurfaceTypesTrail = wrapRule({
4
+ export const contextNoTrailheadTypesTrail = wrapRule({
5
5
  examples: [
6
6
  {
7
7
  expected: { diagnostics: [] },
@@ -9,13 +9,13 @@ export const contextNoSurfaceTypesTrail = wrapRule({
9
9
  filePath: 'clean.ts',
10
10
  sourceCode: `import { trail, Result } from "@ontrails/core";
11
11
  trail("entity.show", {
12
- run: async (input, ctx) => {
12
+ blaze: async (input, ctx) => {
13
13
  return Result.ok({ name: "test" });
14
14
  }
15
15
  })`,
16
16
  },
17
- name: 'Clean trail without surface imports',
17
+ name: 'Clean trail without trailhead imports',
18
18
  },
19
19
  ],
20
- rule: contextNoSurfaceTypes,
20
+ rule: contextNoTrailheadTypes,
21
21
  });
@@ -0,0 +1,22 @@
1
+ import { crossDeclarations } from '../rules/cross-declarations.js';
2
+ import { wrapRule } from './wrap-rule.js';
3
+
4
+ export const crossDeclarationsTrail = wrapRule({
5
+ examples: [
6
+ {
7
+ expected: { diagnostics: [] },
8
+ input: {
9
+ filePath: 'clean.ts',
10
+ sourceCode: `trail("entity.onboard", {
11
+ crosses: ["entity.create"],
12
+ blaze: async (input, ctx) => {
13
+ const result = await ctx.cross("entity.create", input);
14
+ return Result.ok(result);
15
+ }
16
+ })`,
17
+ },
18
+ name: 'Matched crossing declarations and calls',
19
+ },
20
+ ],
21
+ rule: crossDeclarations,
22
+ });
@@ -8,7 +8,7 @@ export const implementationReturnsResultTrail = wrapRule({
8
8
  input: {
9
9
  filePath: 'clean.ts',
10
10
  sourceCode: `trail("entity.show", {
11
- run: async (input, ctx) => {
11
+ blaze: async (input, ctx) => {
12
12
  return Result.ok({ name: "test" });
13
13
  }
14
14
  })`,
@@ -1,5 +1,5 @@
1
- export { contextNoSurfaceTypesTrail } from './context-no-surface-types.trail.js';
2
- export { followDeclarationsTrail } from './follow-declarations.trail.js';
1
+ export { contextNoTrailheadTypesTrail } from './context-no-trailhead-types.trail.js';
2
+ export { crossDeclarationsTrail } from './cross-declarations.trail.js';
3
3
  export { implementationReturnsResultTrail } from './implementation-returns-result.trail.js';
4
4
  export { noDirectImplInRouteTrail } from './no-direct-impl-in-route.trail.js';
5
5
  export { noDirectImplementationCallTrail } from './no-direct-implementation-call.trail.js';
@@ -7,8 +7,8 @@ export { noSyncResultAssumptionTrail } from './no-sync-result-assumption.trail.j
7
7
  export { noThrowInDetourTargetTrail } from './no-throw-in-detour-target.trail.js';
8
8
  export { noThrowInImplementationTrail } from './no-throw-in-implementation.trail.js';
9
9
  export { preferSchemaInferenceTrail } from './prefer-schema-inference.trail.js';
10
- export { serviceDeclarationsTrail } from './service-declarations.trail.js';
11
- export { serviceExistsTrail } from './service-exists.trail.js';
10
+ export { provisionDeclarationsTrail } from './provision-declarations.trail.js';
11
+ export { provisionExistsTrail } from './provision-exists.trail.js';
12
12
  export { validDescribeRefsTrail } from './valid-describe-refs.trail.js';
13
13
  export { validDetourRefsTrail } from './valid-detour-refs.trail.js';
14
14
 
@@ -8,14 +8,14 @@ export const noDirectImplInRouteTrail = wrapRule({
8
8
  input: {
9
9
  filePath: 'clean.ts',
10
10
  sourceCode: `trail("entity.onboard", {
11
- follow: ["entity.create"],
12
- run: async (input, ctx) => {
13
- const result = await ctx.follow("entity.create", input);
11
+ crosses: ["entity.create"],
12
+ blaze: async (input, ctx) => {
13
+ const result = await ctx.cross("entity.create", input);
14
14
  return Result.ok(result);
15
15
  }
16
16
  })`,
17
17
  },
18
- name: 'Trail with follow using ctx.follow()',
18
+ name: 'Trail with crossings using ctx.cross()',
19
19
  },
20
20
  ],
21
21
  rule: noDirectImplInRoute,
@@ -7,9 +7,9 @@ export const noDirectImplementationCallTrail = wrapRule({
7
7
  expected: { diagnostics: [] },
8
8
  input: {
9
9
  filePath: 'clean.ts',
10
- sourceCode: `const data = await ctx.follow("entity.show", { id: "1" });`,
10
+ sourceCode: `const data = await ctx.cross("entity.show", { id: "1" });`,
11
11
  },
12
- name: 'Clean code using ctx.follow instead of .run()',
12
+ name: 'Clean code using ctx.cross instead of .blaze()',
13
13
  },
14
14
  ],
15
15
  rule: noDirectImplementationCall,
@@ -7,12 +7,12 @@ export const noSyncResultAssumptionTrail = wrapRule({
7
7
  expected: { diagnostics: [] },
8
8
  input: {
9
9
  filePath: 'clean.ts',
10
- sourceCode: `const result = await myTrail.run(input, ctx);
10
+ sourceCode: `const result = await myTrail.blaze(input, ctx);
11
11
  if (result.isOk()) {
12
12
  console.log(result.value);
13
13
  }`,
14
14
  },
15
- name: 'Properly awaited .run() call',
15
+ name: 'Properly awaited .blaze() call',
16
16
  },
17
17
  ],
18
18
  rule: noSyncResultAssumption,
@@ -8,7 +8,7 @@ export const noThrowInDetourTargetTrail = wrapRule({
8
8
  input: {
9
9
  filePath: 'clean.ts',
10
10
  sourceCode: `trail("entity.fallback", {
11
- run: async (input, ctx) => {
11
+ blaze: async (input, ctx) => {
12
12
  return Result.ok({ recovered: true });
13
13
  }
14
14
  })`,
@@ -8,7 +8,7 @@ export const noThrowInImplementationTrail = wrapRule({
8
8
  input: {
9
9
  filePath: 'clean.ts',
10
10
  sourceCode: `trail("entity.show", {
11
- run: async (input, ctx) => {
11
+ blaze: async (input, ctx) => {
12
12
  return Result.ok({ name: "test" });
13
13
  }
14
14
  })`,
@@ -9,7 +9,7 @@ export const preferSchemaInferenceTrail = wrapRule({
9
9
  filePath: 'clean.ts',
10
10
  sourceCode: `trail("entity.show", {
11
11
  input: z.object({ name: z.string() }),
12
- run: async (input, ctx) => {
12
+ blaze: async (input, ctx) => {
13
13
  return Result.ok({ name: input.name });
14
14
  }
15
15
  })`,
@@ -0,0 +1,25 @@
1
+ import { provisionDeclarations } from '../rules/provision-declarations.js';
2
+ import { wrapRule } from './wrap-rule.js';
3
+
4
+ export const provisionDeclarationsTrail = wrapRule({
5
+ examples: [
6
+ {
7
+ expected: { diagnostics: [] },
8
+ input: {
9
+ filePath: 'clean.ts',
10
+ sourceCode: `const db = provision("db.main", {
11
+ create: () => Result.ok({ source: "factory" }),
12
+ });
13
+
14
+ trail("entity.show", {
15
+ provisions: [db],
16
+ blaze: async (_input, ctx) => {
17
+ return Result.ok(db.from(ctx));
18
+ }
19
+ })`,
20
+ },
21
+ name: 'Matched provision declarations and usage',
22
+ },
23
+ ],
24
+ rule: provisionDeclarations,
25
+ });
@@ -0,0 +1,27 @@
1
+ import { provisionExists } from '../rules/provision-exists.js';
2
+ import { wrapRule } from './wrap-rule.js';
3
+
4
+ export const provisionExistsTrail = wrapRule({
5
+ examples: [
6
+ {
7
+ expected: { diagnostics: [] },
8
+ input: {
9
+ filePath: 'clean.ts',
10
+ knownProvisionIds: ['db.main'],
11
+ knownTrailIds: ['entity.show'],
12
+ sourceCode: `const db = provision("db.main", {
13
+ create: () => Result.ok({ source: "factory" }),
14
+ });
15
+
16
+ trail("entity.show", {
17
+ provisions: [db],
18
+ blaze: async (_input, ctx) => {
19
+ return Result.ok(db.from(ctx));
20
+ }
21
+ })`,
22
+ },
23
+ name: 'Declared provisions resolve to known project provisions',
24
+ },
25
+ ],
26
+ rule: provisionExists,
27
+ });
package/src/trails/run.ts CHANGED
@@ -4,14 +4,14 @@
4
4
  * Returns a flat array of diagnostics from every rule.
5
5
  */
6
6
 
7
- import { dispatch } from '@ontrails/core';
7
+ import { run } from '@ontrails/core';
8
8
 
9
9
  import type { WardenDiagnostic } from '../rules/types.js';
10
10
  import type { RuleOutput } from './schema.js';
11
11
  import { wardenTopo } from './topo.js';
12
12
 
13
13
  /**
14
- * Dispatch all warden rule trails for a given file and collect diagnostics.
14
+ * Run all warden rule trails for a given file and collect diagnostics.
15
15
  *
16
16
  * Each rule trail runs independently. Errors from individual trails are
17
17
  * silently skipped so that one broken rule does not block the rest.
@@ -20,7 +20,7 @@ export const runWardenTrails = async (
20
20
  filePath: string,
21
21
  sourceCode: string,
22
22
  options?: {
23
- readonly knownServiceIds?: readonly string[];
23
+ readonly knownProvisionIds?: readonly string[];
24
24
  readonly knownTrailIds?: readonly string[];
25
25
  }
26
26
  ): Promise<readonly WardenDiagnostic[]> => {
@@ -28,11 +28,11 @@ export const runWardenTrails = async (
28
28
 
29
29
  for (const id of wardenTopo.ids()) {
30
30
  const input =
31
- options?.knownTrailIds || options?.knownServiceIds
31
+ options?.knownTrailIds || options?.knownProvisionIds
32
32
  ? {
33
33
  filePath,
34
- ...(options?.knownServiceIds
35
- ? { knownServiceIds: options.knownServiceIds }
34
+ ...(options?.knownProvisionIds
35
+ ? { knownProvisionIds: options.knownProvisionIds }
36
36
  : {}),
37
37
  ...(options?.knownTrailIds
38
38
  ? { knownTrailIds: options.knownTrailIds }
@@ -40,7 +40,7 @@ export const runWardenTrails = async (
40
40
  sourceCode,
41
41
  }
42
42
  : { filePath, sourceCode };
43
- const result = await dispatch(wardenTopo, id, input);
43
+ const result = await run(wardenTopo, id, input);
44
44
  if (result.isOk()) {
45
45
  const { diagnostics } = result.value as RuleOutput;
46
46
  for (const d of diagnostics) {
@@ -30,10 +30,10 @@ export const ruleInput = z.object({
30
30
  * files.
31
31
  */
32
32
  export const projectAwareRuleInput = ruleInput.extend({
33
- knownServiceIds: z
33
+ knownProvisionIds: z
34
34
  .array(z.string())
35
35
  .optional()
36
- .describe('Service IDs known across the project'),
36
+ .describe('Provision IDs known across the project'),
37
37
  knownTrailIds: z
38
38
  .array(z.string())
39
39
  .optional()
@@ -9,12 +9,12 @@ export const validDetourRefsTrail = wrapRule({
9
9
  filePath: 'clean.ts',
10
10
  knownTrailIds: ['entity.fallback', 'entity.show'],
11
11
  sourceCode: `trail("entity.fallback", {
12
- run: async (input, ctx) => Result.ok(data)
12
+ blaze: async (input, ctx) => Result.ok(data)
13
13
  })
14
14
 
15
15
  trail("entity.show", {
16
16
  detours: [{ target: "entity.fallback" }],
17
- run: async (input, ctx) => Result.ok(data)
17
+ blaze: async (input, ctx) => Result.ok(data)
18
18
  })`,
19
19
  },
20
20
  name: 'Valid detour target reference',
@@ -49,19 +49,10 @@ export function wrapRule(
49
49
  if (isProjectAware) {
50
50
  const projectAwareRule = rule as ProjectAwareWardenRule;
51
51
  return trail(`warden.rule.${rule.name}`, {
52
- description: rule.description,
53
- examples: examples as Trail<
54
- ProjectAwareRuleInput,
55
- RuleOutput
56
- >['examples'],
57
- input: projectAwareRuleInput,
58
- intent: 'read',
59
- metadata: { category: 'governance', severity: rule.severity },
60
- output: ruleOutput,
61
- run: (input: ProjectAwareRuleInput) => {
52
+ blaze: (input: ProjectAwareRuleInput) => {
62
53
  const context = {
63
- knownServiceIds: input.knownServiceIds
64
- ? new Set(input.knownServiceIds)
54
+ knownProvisionIds: input.knownProvisionIds
55
+ ? new Set(input.knownProvisionIds)
65
56
  : undefined,
66
57
  knownTrailIds: input.knownTrailIds
67
58
  ? new Set(input.knownTrailIds)
@@ -74,19 +65,28 @@ export function wrapRule(
74
65
  );
75
66
  return Result.ok({ diagnostics: [...diagnostics] });
76
67
  },
68
+ description: rule.description,
69
+ examples: examples as Trail<
70
+ ProjectAwareRuleInput,
71
+ RuleOutput
72
+ >['examples'],
73
+ input: projectAwareRuleInput,
74
+ intent: 'read',
75
+ meta: { category: 'governance', severity: rule.severity },
76
+ output: ruleOutput,
77
77
  });
78
78
  }
79
79
 
80
80
  return trail(`warden.rule.${rule.name}`, {
81
+ blaze: (input: RuleInput) => {
82
+ const diagnostics = rule.check(input.sourceCode, input.filePath);
83
+ return Result.ok({ diagnostics: [...diagnostics] });
84
+ },
81
85
  description: rule.description,
82
86
  examples: examples as Trail<RuleInput, RuleOutput>['examples'],
83
87
  input: ruleInput,
84
88
  intent: 'read',
85
- metadata: { category: 'governance', severity: rule.severity },
89
+ meta: { category: 'governance', severity: rule.severity },
86
90
  output: ruleOutput,
87
- run: (input: RuleInput) => {
88
- const diagnostics = rule.check(input.sourceCode, input.filePath);
89
- return Result.ok({ diagnostics: [...diagnostics] });
90
- },
91
91
  });
92
92
  }
@@ -1 +1 @@
1
- {"root":["./src/cli.ts","./src/drift.ts","./src/formatters.ts","./src/index.ts","./src/rules/ast.ts","./src/rules/context-no-surface-types.ts","./src/rules/follow-declarations.ts","./src/rules/implementation-returns-result.ts","./src/rules/index.ts","./src/rules/no-direct-impl-in-route.ts","./src/rules/no-direct-implementation-call.ts","./src/rules/no-sync-result-assumption.ts","./src/rules/no-throw-in-detour-target.ts","./src/rules/no-throw-in-implementation.ts","./src/rules/prefer-schema-inference.ts","./src/rules/scan.ts","./src/rules/service-declarations.ts","./src/rules/service-exists.ts","./src/rules/specs.ts","./src/rules/structure.ts","./src/rules/types.ts","./src/rules/valid-describe-refs.ts","./src/rules/valid-detour-refs.ts","./src/trails/context-no-surface-types.trail.ts","./src/trails/follow-declarations.trail.ts","./src/trails/implementation-returns-result.trail.ts","./src/trails/index.ts","./src/trails/no-direct-impl-in-route.trail.ts","./src/trails/no-direct-implementation-call.trail.ts","./src/trails/no-sync-result-assumption.trail.ts","./src/trails/no-throw-in-detour-target.trail.ts","./src/trails/no-throw-in-implementation.trail.ts","./src/trails/prefer-schema-inference.trail.ts","./src/trails/run.ts","./src/trails/schema.ts","./src/trails/service-declarations.trail.ts","./src/trails/service-exists.trail.ts","./src/trails/topo.ts","./src/trails/valid-describe-refs.trail.ts","./src/trails/valid-detour-refs.trail.ts","./src/trails/wrap-rule.ts"],"version":"5.9.3"}
1
+ {"root":["./src/cli.ts","./src/drift.ts","./src/formatters.ts","./src/index.ts","./src/rules/ast.ts","./src/rules/context-no-trailhead-types.ts","./src/rules/cross-declarations.ts","./src/rules/implementation-returns-result.ts","./src/rules/index.ts","./src/rules/no-direct-impl-in-route.ts","./src/rules/no-direct-implementation-call.ts","./src/rules/no-sync-result-assumption.ts","./src/rules/no-throw-in-detour-target.ts","./src/rules/no-throw-in-implementation.ts","./src/rules/prefer-schema-inference.ts","./src/rules/provision-declarations.ts","./src/rules/provision-exists.ts","./src/rules/scan.ts","./src/rules/specs.ts","./src/rules/structure.ts","./src/rules/types.ts","./src/rules/valid-describe-refs.ts","./src/rules/valid-detour-refs.ts","./src/trails/context-no-trailhead-types.trail.ts","./src/trails/cross-declarations.trail.ts","./src/trails/implementation-returns-result.trail.ts","./src/trails/index.ts","./src/trails/no-direct-impl-in-route.trail.ts","./src/trails/no-direct-implementation-call.trail.ts","./src/trails/no-sync-result-assumption.trail.ts","./src/trails/no-throw-in-detour-target.trail.ts","./src/trails/no-throw-in-implementation.trail.ts","./src/trails/prefer-schema-inference.trail.ts","./src/trails/provision-declarations.trail.ts","./src/trails/provision-exists.trail.ts","./src/trails/run.ts","./src/trails/schema.ts","./src/trails/topo.ts","./src/trails/valid-describe-refs.trail.ts","./src/trails/valid-detour-refs.trail.ts","./src/trails/wrap-rule.ts"],"version":"5.9.3"}