@ontrails/warden 1.0.0-beta.8 → 1.0.0-beta.9

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 (91) hide show
  1. package/.turbo/turbo-lint.log +1 -1
  2. package/CHANGELOG.md +26 -0
  3. package/README.md +20 -0
  4. package/dist/index.d.ts +4 -0
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +4 -0
  7. package/dist/index.js.map +1 -1
  8. package/dist/trails/context-no-surface-types.trail.d.ts +13 -0
  9. package/dist/trails/context-no-surface-types.trail.d.ts.map +1 -0
  10. package/dist/trails/context-no-surface-types.trail.js +21 -0
  11. package/dist/trails/context-no-surface-types.trail.js.map +1 -0
  12. package/dist/trails/follow-declarations.trail.d.ts +13 -0
  13. package/dist/trails/follow-declarations.trail.d.ts.map +1 -0
  14. package/dist/trails/follow-declarations.trail.js +22 -0
  15. package/dist/trails/follow-declarations.trail.js.map +1 -0
  16. package/dist/trails/implementation-returns-result.trail.d.ts +13 -0
  17. package/dist/trails/implementation-returns-result.trail.d.ts.map +1 -0
  18. package/dist/trails/implementation-returns-result.trail.js +20 -0
  19. package/dist/trails/implementation-returns-result.trail.js.map +1 -0
  20. package/dist/trails/index.d.ts +14 -0
  21. package/dist/trails/index.d.ts.map +1 -0
  22. package/dist/trails/index.js +13 -0
  23. package/dist/trails/index.js.map +1 -0
  24. package/dist/trails/no-direct-impl-in-route.trail.d.ts +13 -0
  25. package/dist/trails/no-direct-impl-in-route.trail.d.ts.map +1 -0
  26. package/dist/trails/no-direct-impl-in-route.trail.js +22 -0
  27. package/dist/trails/no-direct-impl-in-route.trail.js.map +1 -0
  28. package/dist/trails/no-direct-implementation-call.trail.d.ts +13 -0
  29. package/dist/trails/no-direct-implementation-call.trail.d.ts.map +1 -0
  30. package/dist/trails/no-direct-implementation-call.trail.js +16 -0
  31. package/dist/trails/no-direct-implementation-call.trail.js.map +1 -0
  32. package/dist/trails/no-sync-result-assumption.trail.d.ts +13 -0
  33. package/dist/trails/no-sync-result-assumption.trail.d.ts.map +1 -0
  34. package/dist/trails/no-sync-result-assumption.trail.js +19 -0
  35. package/dist/trails/no-sync-result-assumption.trail.js.map +1 -0
  36. package/dist/trails/no-throw-in-detour-target.trail.d.ts +14 -0
  37. package/dist/trails/no-throw-in-detour-target.trail.d.ts.map +1 -0
  38. package/dist/trails/no-throw-in-detour-target.trail.js +20 -0
  39. package/dist/trails/no-throw-in-detour-target.trail.js.map +1 -0
  40. package/dist/trails/no-throw-in-implementation.trail.d.ts +13 -0
  41. package/dist/trails/no-throw-in-implementation.trail.d.ts.map +1 -0
  42. package/dist/trails/no-throw-in-implementation.trail.js +20 -0
  43. package/dist/trails/no-throw-in-implementation.trail.js.map +1 -0
  44. package/dist/trails/prefer-schema-inference.trail.d.ts +13 -0
  45. package/dist/trails/prefer-schema-inference.trail.d.ts.map +1 -0
  46. package/dist/trails/prefer-schema-inference.trail.js +21 -0
  47. package/dist/trails/prefer-schema-inference.trail.js.map +1 -0
  48. package/dist/trails/run.d.ts +16 -0
  49. package/dist/trails/run.d.ts.map +1 -0
  50. package/dist/trails/run.js +30 -0
  51. package/dist/trails/run.js.map +1 -0
  52. package/dist/trails/schema.d.ts +52 -0
  53. package/dist/trails/schema.d.ts.map +1 -0
  54. package/dist/trails/schema.js +38 -0
  55. package/dist/trails/schema.js.map +1 -0
  56. package/dist/trails/topo.d.ts +3 -0
  57. package/dist/trails/topo.d.ts.map +1 -0
  58. package/dist/trails/topo.js +5 -0
  59. package/dist/trails/topo.js.map +1 -0
  60. package/dist/trails/valid-describe-refs.trail.d.ts +14 -0
  61. package/dist/trails/valid-describe-refs.trail.d.ts.map +1 -0
  62. package/dist/trails/valid-describe-refs.trail.js +18 -0
  63. package/dist/trails/valid-describe-refs.trail.js.map +1 -0
  64. package/dist/trails/valid-detour-refs.trail.d.ts +14 -0
  65. package/dist/trails/valid-detour-refs.trail.d.ts.map +1 -0
  66. package/dist/trails/valid-detour-refs.trail.js +24 -0
  67. package/dist/trails/valid-detour-refs.trail.js.map +1 -0
  68. package/dist/trails/wrap-rule.d.ts +29 -0
  69. package/dist/trails/wrap-rule.d.ts.map +1 -0
  70. package/dist/trails/wrap-rule.js +43 -0
  71. package/dist/trails/wrap-rule.js.map +1 -0
  72. package/package.json +4 -3
  73. package/src/__tests__/trails.test.ts +19 -0
  74. package/src/index.ts +21 -0
  75. package/src/trails/context-no-surface-types.trail.ts +21 -0
  76. package/src/trails/follow-declarations.trail.ts +22 -0
  77. package/src/trails/implementation-returns-result.trail.ts +20 -0
  78. package/src/trails/index.ts +14 -0
  79. package/src/trails/no-direct-impl-in-route.trail.ts +22 -0
  80. package/src/trails/no-direct-implementation-call.trail.ts +16 -0
  81. package/src/trails/no-sync-result-assumption.trail.ts +19 -0
  82. package/src/trails/no-throw-in-detour-target.trail.ts +20 -0
  83. package/src/trails/no-throw-in-implementation.trail.ts +20 -0
  84. package/src/trails/prefer-schema-inference.trail.ts +21 -0
  85. package/src/trails/run.ts +40 -0
  86. package/src/trails/schema.ts +46 -0
  87. package/src/trails/topo.ts +6 -0
  88. package/src/trails/valid-describe-refs.trail.ts +18 -0
  89. package/src/trails/valid-detour-refs.trail.ts +24 -0
  90. package/src/trails/wrap-rule.ts +84 -0
  91. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Shared Zod schemas for warden rule trails.
3
+ *
4
+ * Every rule trail shares the same input (source file) and output
5
+ * (array of diagnostics) shape.
6
+ */
7
+ import { z } from 'zod';
8
+ /** A single diagnostic emitted by a warden rule trail. */
9
+ export declare const diagnosticSchema: z.ZodObject<{
10
+ filePath: z.ZodString;
11
+ line: z.ZodNumber;
12
+ message: z.ZodString;
13
+ rule: z.ZodString;
14
+ severity: z.ZodEnum<{
15
+ error: "error";
16
+ warn: "warn";
17
+ }>;
18
+ }, z.core.$strip>;
19
+ /** Input accepted by every warden rule trail. */
20
+ export declare const ruleInput: z.ZodObject<{
21
+ filePath: z.ZodString;
22
+ sourceCode: z.ZodString;
23
+ }, z.core.$strip>;
24
+ /**
25
+ * Extended input for project-aware warden rule trails.
26
+ *
27
+ * Adds `knownTrailIds` so the caller can supply cross-file context and avoid
28
+ * false positives for detour targets or `@see` references defined in other
29
+ * files.
30
+ */
31
+ export declare const projectAwareRuleInput: z.ZodObject<{
32
+ filePath: z.ZodString;
33
+ sourceCode: z.ZodString;
34
+ knownTrailIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
35
+ }, z.core.$strip>;
36
+ /** Output returned by every warden rule trail. */
37
+ export declare const ruleOutput: z.ZodObject<{
38
+ diagnostics: z.ZodArray<z.ZodObject<{
39
+ filePath: z.ZodString;
40
+ line: z.ZodNumber;
41
+ message: z.ZodString;
42
+ rule: z.ZodString;
43
+ severity: z.ZodEnum<{
44
+ error: "error";
45
+ warn: "warn";
46
+ }>;
47
+ }, z.core.$strip>>;
48
+ }, z.core.$strip>;
49
+ export type RuleInput = z.infer<typeof ruleInput>;
50
+ export type ProjectAwareRuleInput = z.infer<typeof projectAwareRuleInput>;
51
+ export type RuleOutput = z.infer<typeof ruleOutput>;
52
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/trails/schema.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,0DAA0D;AAC1D,eAAO,MAAM,gBAAgB;;;;;;;;;iBAM3B,CAAC;AAEH,iDAAiD;AACjD,eAAO,MAAM,SAAS;;;iBAGpB,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB;;;;iBAKhC,CAAC;AAEH,kDAAkD;AAClD,eAAO,MAAM,UAAU;;;;;;;;;;;iBAErB,CAAC;AAEH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAC;AAClD,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAC1E,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Shared Zod schemas for warden rule trails.
3
+ *
4
+ * Every rule trail shares the same input (source file) and output
5
+ * (array of diagnostics) shape.
6
+ */
7
+ import { z } from 'zod';
8
+ /** A single diagnostic emitted by a warden rule trail. */
9
+ export const diagnosticSchema = z.object({
10
+ filePath: z.string().describe('File path that was analyzed'),
11
+ line: z.number().describe('1-based line number'),
12
+ message: z.string().describe('Human-readable diagnostic message'),
13
+ rule: z.string().describe('Rule name'),
14
+ severity: z.enum(['error', 'warn']).describe('Diagnostic severity'),
15
+ });
16
+ /** Input accepted by every warden rule trail. */
17
+ export const ruleInput = z.object({
18
+ filePath: z.string().describe('Path to the source file'),
19
+ sourceCode: z.string().describe('Source code content'),
20
+ });
21
+ /**
22
+ * Extended input for project-aware warden rule trails.
23
+ *
24
+ * Adds `knownTrailIds` so the caller can supply cross-file context and avoid
25
+ * false positives for detour targets or `@see` references defined in other
26
+ * files.
27
+ */
28
+ export const projectAwareRuleInput = ruleInput.extend({
29
+ knownTrailIds: z
30
+ .array(z.string())
31
+ .optional()
32
+ .describe('Trail IDs known across the project'),
33
+ });
34
+ /** Output returned by every warden rule trail. */
35
+ export const ruleOutput = z.object({
36
+ diagnostics: z.array(diagnosticSchema).describe('Diagnostics found'),
37
+ });
38
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/trails/schema.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,0DAA0D;AAC1D,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC;IACvC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,6BAA6B,CAAC;IAC5D,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,qBAAqB,CAAC;IAChD,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,mCAAmC,CAAC;IACjE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC;IACtC,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,qBAAqB,CAAC;CACpE,CAAC,CAAC;AAEH,iDAAiD;AACjD,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,CAAC,MAAM,CAAC;IAChC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,yBAAyB,CAAC;IACxD,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,qBAAqB,CAAC;CACvD,CAAC,CAAC;AAEH;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,SAAS,CAAC,MAAM,CAAC;IACpD,aAAa,EAAE,CAAC;SACb,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;SACjB,QAAQ,EAAE;SACV,QAAQ,CAAC,oCAAoC,CAAC;CAClD,CAAC,CAAC;AAEH,kDAAkD;AAClD,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,CAAC,MAAM,CAAC;IACjC,WAAW,EAAE,CAAC,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,QAAQ,CAAC,mBAAmB,CAAC;CACrE,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ /** Topo collecting all warden rule trails. */
2
+ export declare const wardenTopo: import("@ontrails/core").Topo;
3
+ //# sourceMappingURL=topo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"topo.d.ts","sourceRoot":"","sources":["../../src/trails/topo.ts"],"names":[],"mappings":"AAIA,8CAA8C;AAC9C,eAAO,MAAM,UAAU,+BAAwB,CAAC"}
@@ -0,0 +1,5 @@
1
+ import { topo } from '@ontrails/core';
2
+ import * as rules from './index.js';
3
+ /** Topo collecting all warden rule trails. */
4
+ export const wardenTopo = topo('warden', rules);
5
+ //# sourceMappingURL=topo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"topo.js","sourceRoot":"","sources":["../../src/trails/topo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAEtC,OAAO,KAAK,KAAK,MAAM,YAAY,CAAC;AAEpC,8CAA8C;AAC9C,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC"}
@@ -0,0 +1,14 @@
1
+ export declare const validDescribeRefsTrail: import("@ontrails/core").Trail<{
2
+ filePath: string;
3
+ sourceCode: string;
4
+ knownTrailIds?: string[] | undefined;
5
+ }, {
6
+ diagnostics: {
7
+ filePath: string;
8
+ line: number;
9
+ message: string;
10
+ rule: string;
11
+ severity: "error" | "warn";
12
+ }[];
13
+ }>;
14
+ //# sourceMappingURL=valid-describe-refs.trail.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"valid-describe-refs.trail.d.ts","sourceRoot":"","sources":["../../src/trails/valid-describe-refs.trail.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,sBAAsB;;;;;;;;;;;;EAcjC,CAAC"}
@@ -0,0 +1,18 @@
1
+ import { validDescribeRefs } from '../rules/valid-describe-refs.js';
2
+ import { wrapRule } from './wrap-rule.js';
3
+ export const validDescribeRefsTrail = wrapRule({
4
+ examples: [
5
+ {
6
+ expected: { diagnostics: [] },
7
+ input: {
8
+ filePath: 'clean.ts',
9
+ sourceCode: `const schema = z.object({
10
+ name: z.string().describe("User display name"),
11
+ });`,
12
+ },
13
+ name: 'Describe without @see refs',
14
+ },
15
+ ],
16
+ rule: validDescribeRefs,
17
+ });
18
+ //# sourceMappingURL=valid-describe-refs.trail.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"valid-describe-refs.trail.js","sourceRoot":"","sources":["../../src/trails/valid-describe-refs.trail.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C,MAAM,CAAC,MAAM,sBAAsB,GAAG,QAAQ,CAAC;IAC7C,QAAQ,EAAE;QACR;YACE,QAAQ,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE;YAC7B,KAAK,EAAE;gBACL,QAAQ,EAAE,UAAU;gBACpB,UAAU,EAAE;;IAEhB;aACG;YACD,IAAI,EAAE,4BAA4B;SACnC;KACF;IACD,IAAI,EAAE,iBAAiB;CACxB,CAAC,CAAC"}
@@ -0,0 +1,14 @@
1
+ export declare const validDetourRefsTrail: import("@ontrails/core").Trail<{
2
+ filePath: string;
3
+ sourceCode: string;
4
+ knownTrailIds?: string[] | undefined;
5
+ }, {
6
+ diagnostics: {
7
+ filePath: string;
8
+ line: number;
9
+ message: string;
10
+ rule: string;
11
+ severity: "error" | "warn";
12
+ }[];
13
+ }>;
14
+ //# sourceMappingURL=valid-detour-refs.trail.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"valid-detour-refs.trail.d.ts","sourceRoot":"","sources":["../../src/trails/valid-detour-refs.trail.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,oBAAoB;;;;;;;;;;;;EAoB/B,CAAC"}
@@ -0,0 +1,24 @@
1
+ import { validDetourRefs } from '../rules/valid-detour-refs.js';
2
+ import { wrapRule } from './wrap-rule.js';
3
+ export const validDetourRefsTrail = wrapRule({
4
+ examples: [
5
+ {
6
+ expected: { diagnostics: [] },
7
+ input: {
8
+ filePath: 'clean.ts',
9
+ knownTrailIds: ['entity.fallback', 'entity.show'],
10
+ sourceCode: `trail("entity.fallback", {
11
+ run: async (input, ctx) => Result.ok(data)
12
+ })
13
+
14
+ trail("entity.show", {
15
+ detours: [{ target: "entity.fallback" }],
16
+ run: async (input, ctx) => Result.ok(data)
17
+ })`,
18
+ },
19
+ name: 'Valid detour target reference',
20
+ },
21
+ ],
22
+ rule: validDetourRefs,
23
+ });
24
+ //# sourceMappingURL=valid-detour-refs.trail.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"valid-detour-refs.trail.js","sourceRoot":"","sources":["../../src/trails/valid-detour-refs.trail.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C,MAAM,CAAC,MAAM,oBAAoB,GAAG,QAAQ,CAAC;IAC3C,QAAQ,EAAE;QACR;YACE,QAAQ,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE;YAC7B,KAAK,EAAE;gBACL,QAAQ,EAAE,UAAU;gBACpB,aAAa,EAAE,CAAC,iBAAiB,EAAE,aAAa,CAAC;gBACjD,UAAU,EAAE;;;;;;;GAOjB;aACI;YACD,IAAI,EAAE,+BAA+B;SACtC;KACF;IACD,IAAI,EAAE,eAAe;CACtB,CAAC,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Factory that wraps a WardenRule as a trail.
3
+ *
4
+ * Keeps each rule trail file minimal — just the import + examples.
5
+ */
6
+ import type { Trail } from '@ontrails/core';
7
+ import type { ProjectAwareWardenRule, WardenRule } from '../rules/types.js';
8
+ import type { ProjectAwareRuleInput, RuleInput, RuleOutput } from './schema.js';
9
+ interface WrapRuleOptions {
10
+ /** The existing warden rule to wrap. */
11
+ readonly rule: WardenRule;
12
+ /** Trail examples for testing and documentation. */
13
+ readonly examples: Trail<RuleInput, RuleOutput>['examples'];
14
+ }
15
+ interface WrapProjectAwareRuleOptions {
16
+ /** The existing project-aware warden rule to wrap. */
17
+ readonly rule: ProjectAwareWardenRule;
18
+ /** Trail examples for testing and documentation. */
19
+ readonly examples: Trail<ProjectAwareRuleInput, RuleOutput>['examples'];
20
+ }
21
+ /**
22
+ * Wrap an existing `WardenRule` as a trail with typed input/output.
23
+ *
24
+ * The trail ID follows the pattern `warden.rule.<rule-name>`.
25
+ */
26
+ export declare function wrapRule(options: WrapProjectAwareRuleOptions): Trail<ProjectAwareRuleInput, RuleOutput>;
27
+ export declare function wrapRule(options: WrapRuleOptions): Trail<RuleInput, RuleOutput>;
28
+ export {};
29
+ //# sourceMappingURL=wrap-rule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wrap-rule.d.ts","sourceRoot":"","sources":["../../src/trails/wrap-rule.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAE5C,OAAO,KAAK,EAAE,sBAAsB,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE5E,OAAO,KAAK,EAAE,qBAAqB,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEhF,UAAU,eAAe;IACvB,wCAAwC;IACxC,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,oDAAoD;IACpD,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC,UAAU,CAAC,CAAC;CAC7D;AAED,UAAU,2BAA2B;IACnC,sDAAsD;IACtD,QAAQ,CAAC,IAAI,EAAE,sBAAsB,CAAC;IACtC,oDAAoD;IACpD,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC,qBAAqB,EAAE,UAAU,CAAC,CAAC,UAAU,CAAC,CAAC;CACzE;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CACtB,OAAO,EAAE,2BAA2B,GACnC,KAAK,CAAC,qBAAqB,EAAE,UAAU,CAAC,CAAC;AAC5C,wBAAgB,QAAQ,CACtB,OAAO,EAAE,eAAe,GACvB,KAAK,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Factory that wraps a WardenRule as a trail.
3
+ *
4
+ * Keeps each rule trail file minimal — just the import + examples.
5
+ */
6
+ import { trail, Result } from '@ontrails/core';
7
+ import { projectAwareRuleInput, ruleInput, ruleOutput } from './schema.js';
8
+ export function wrapRule(options) {
9
+ const { rule, examples } = options;
10
+ const isProjectAware = 'checkWithContext' in rule;
11
+ if (isProjectAware) {
12
+ const projectAwareRule = rule;
13
+ return trail(`warden.rule.${rule.name}`, {
14
+ description: rule.description,
15
+ examples: examples,
16
+ input: projectAwareRuleInput,
17
+ intent: 'read',
18
+ metadata: { category: 'governance', severity: rule.severity },
19
+ output: ruleOutput,
20
+ run: (input) => {
21
+ const diagnostics = projectAwareRule.checkWithContext(input.sourceCode, input.filePath, {
22
+ knownTrailIds: input.knownTrailIds
23
+ ? new Set(input.knownTrailIds)
24
+ : new Set(),
25
+ });
26
+ return Result.ok({ diagnostics: [...diagnostics] });
27
+ },
28
+ });
29
+ }
30
+ return trail(`warden.rule.${rule.name}`, {
31
+ description: rule.description,
32
+ examples: examples,
33
+ input: ruleInput,
34
+ intent: 'read',
35
+ metadata: { category: 'governance', severity: rule.severity },
36
+ output: ruleOutput,
37
+ run: (input) => {
38
+ const diagnostics = rule.check(input.sourceCode, input.filePath);
39
+ return Result.ok({ diagnostics: [...diagnostics] });
40
+ },
41
+ });
42
+ }
43
+ //# sourceMappingURL=wrap-rule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wrap-rule.js","sourceRoot":"","sources":["../../src/trails/wrap-rule.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAI/C,OAAO,EAAE,qBAAqB,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AA4B3E,MAAM,UAAU,QAAQ,CACtB,OAAsD;IAEtD,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC;IACnC,MAAM,cAAc,GAAG,kBAAkB,IAAI,IAAI,CAAC;IAElD,IAAI,cAAc,EAAE,CAAC;QACnB,MAAM,gBAAgB,GAAG,IAA8B,CAAC;QACxD,OAAO,KAAK,CAAC,eAAe,IAAI,CAAC,IAAI,EAAE,EAAE;YACvC,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,QAAQ,EAAE,QAGG;YACb,KAAK,EAAE,qBAAqB;YAC5B,MAAM,EAAE,MAAM;YACd,QAAQ,EAAE,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE;YAC7D,MAAM,EAAE,UAAU;YAClB,GAAG,EAAE,CAAC,KAA4B,EAAE,EAAE;gBACpC,MAAM,WAAW,GAAG,gBAAgB,CAAC,gBAAgB,CACnD,KAAK,CAAC,UAAU,EAChB,KAAK,CAAC,QAAQ,EACd;oBACE,aAAa,EAAE,KAAK,CAAC,aAAa;wBAChC,CAAC,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,aAAa,CAAC;wBAC9B,CAAC,CAAC,IAAI,GAAG,EAAU;iBACtB,CACF,CAAC;gBACF,OAAO,MAAM,CAAC,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;YACtD,CAAC;SACF,CAAC,CAAC;IACL,CAAC;IAED,OAAO,KAAK,CAAC,eAAe,IAAI,CAAC,IAAI,EAAE,EAAE;QACvC,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,QAAQ,EAAE,QAAoD;QAC9D,KAAK,EAAE,SAAS;QAChB,MAAM,EAAE,MAAM;QACd,QAAQ,EAAE,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE;QAC7D,MAAM,EAAE,UAAU;QAClB,GAAG,EAAE,CAAC,KAAgB,EAAE,EAAE;YACxB,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;YACjE,OAAO,MAAM,CAAC,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;QACtD,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ontrails/warden",
3
- "version": "1.0.0-beta.8",
3
+ "version": "1.0.0-beta.9",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./src/index.ts",
@@ -14,12 +14,13 @@
14
14
  "clean": "rm -rf dist *.tsbuildinfo"
15
15
  },
16
16
  "devDependencies": {
17
+ "@ontrails/testing": "^1.0.0-beta.8",
17
18
  "@oxc-project/types": "^0.122.0",
18
19
  "oxc-parser": "^0.121.0",
19
20
  "zod": "^4.3.5"
20
21
  },
21
22
  "peerDependencies": {
22
- "@ontrails/core": "^1.0.0-beta.6",
23
- "@ontrails/schema": "^1.0.0-beta.6"
23
+ "@ontrails/core": "^1.0.0-beta.8",
24
+ "@ontrails/schema": "^1.0.0-beta.8"
24
25
  }
25
26
  }
@@ -0,0 +1,19 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+ import { testAll } from '@ontrails/testing';
3
+
4
+ import { wardenTopo } from '../trails/topo.js';
5
+
6
+ // oxlint-disable-next-line jest/require-hook -- testAll generates describe/test blocks, not setup code
7
+ testAll(wardenTopo);
8
+
9
+ describe('wardenTopo', () => {
10
+ test('contains all 11 rule trails', () => {
11
+ expect(wardenTopo.count).toBe(11);
12
+ });
13
+
14
+ test('all trail IDs follow warden.rule.* naming', () => {
15
+ for (const id of wardenTopo.ids()) {
16
+ expect(id).toMatch(/^warden\.rule\./);
17
+ }
18
+ });
19
+ });
package/src/index.ts CHANGED
@@ -45,3 +45,24 @@ export {
45
45
  // Drift detection
46
46
  export type { DriftResult } from './drift.js';
47
47
  export { checkDrift } from './drift.js';
48
+
49
+ // Trail layer
50
+ export { wardenTopo } from './trails/topo.js';
51
+ export { runWardenTrails } from './trails/run.js';
52
+ export {
53
+ contextNoSurfaceTypesTrail,
54
+ diagnosticSchema,
55
+ followDeclarationsTrail,
56
+ implementationReturnsResultTrail,
57
+ noDirectImplInRouteTrail,
58
+ noDirectImplementationCallTrail,
59
+ noSyncResultAssumptionTrail,
60
+ noThrowInDetourTargetTrail,
61
+ noThrowInImplementationTrail,
62
+ preferSchemaInferenceTrail,
63
+ ruleInput,
64
+ ruleOutput,
65
+ validDescribeRefsTrail,
66
+ validDetourRefsTrail,
67
+ } from './trails/index.js';
68
+ export type { RuleInput, RuleOutput } from './trails/index.js';
@@ -0,0 +1,21 @@
1
+ import { contextNoSurfaceTypes } from '../rules/context-no-surface-types.js';
2
+ import { wrapRule } from './wrap-rule.js';
3
+
4
+ export const contextNoSurfaceTypesTrail = wrapRule({
5
+ examples: [
6
+ {
7
+ expected: { diagnostics: [] },
8
+ input: {
9
+ filePath: 'clean.ts',
10
+ sourceCode: `import { trail, Result } from "@ontrails/core";
11
+ trail("entity.show", {
12
+ run: async (input, ctx) => {
13
+ return Result.ok({ name: "test" });
14
+ }
15
+ })`,
16
+ },
17
+ name: 'Clean trail without surface imports',
18
+ },
19
+ ],
20
+ rule: contextNoSurfaceTypes,
21
+ });
@@ -0,0 +1,22 @@
1
+ import { followDeclarations } from '../rules/follow-declarations.js';
2
+ import { wrapRule } from './wrap-rule.js';
3
+
4
+ export const followDeclarationsTrail = wrapRule({
5
+ examples: [
6
+ {
7
+ expected: { diagnostics: [] },
8
+ input: {
9
+ filePath: 'clean.ts',
10
+ sourceCode: `trail("entity.onboard", {
11
+ follow: ["entity.create"],
12
+ run: async (input, ctx) => {
13
+ const result = await ctx.follow("entity.create", input);
14
+ return Result.ok(result);
15
+ }
16
+ })`,
17
+ },
18
+ name: 'Matched follow declarations and calls',
19
+ },
20
+ ],
21
+ rule: followDeclarations,
22
+ });
@@ -0,0 +1,20 @@
1
+ import { implementationReturnsResult } from '../rules/implementation-returns-result.js';
2
+ import { wrapRule } from './wrap-rule.js';
3
+
4
+ export const implementationReturnsResultTrail = wrapRule({
5
+ examples: [
6
+ {
7
+ expected: { diagnostics: [] },
8
+ input: {
9
+ filePath: 'clean.ts',
10
+ sourceCode: `trail("entity.show", {
11
+ run: async (input, ctx) => {
12
+ return Result.ok({ name: "test" });
13
+ }
14
+ })`,
15
+ },
16
+ name: 'Implementation returning Result.ok()',
17
+ },
18
+ ],
19
+ rule: implementationReturnsResult,
20
+ });
@@ -0,0 +1,14 @@
1
+ export { contextNoSurfaceTypesTrail } from './context-no-surface-types.trail.js';
2
+ export { followDeclarationsTrail } from './follow-declarations.trail.js';
3
+ export { implementationReturnsResultTrail } from './implementation-returns-result.trail.js';
4
+ export { noDirectImplInRouteTrail } from './no-direct-impl-in-route.trail.js';
5
+ export { noDirectImplementationCallTrail } from './no-direct-implementation-call.trail.js';
6
+ export { noSyncResultAssumptionTrail } from './no-sync-result-assumption.trail.js';
7
+ export { noThrowInDetourTargetTrail } from './no-throw-in-detour-target.trail.js';
8
+ export { noThrowInImplementationTrail } from './no-throw-in-implementation.trail.js';
9
+ export { preferSchemaInferenceTrail } from './prefer-schema-inference.trail.js';
10
+ export { validDescribeRefsTrail } from './valid-describe-refs.trail.js';
11
+ export { validDetourRefsTrail } from './valid-detour-refs.trail.js';
12
+
13
+ export { ruleInput, ruleOutput, diagnosticSchema } from './schema.js';
14
+ export type { RuleInput, RuleOutput } from './schema.js';
@@ -0,0 +1,22 @@
1
+ import { noDirectImplInRoute } from '../rules/no-direct-impl-in-route.js';
2
+ import { wrapRule } from './wrap-rule.js';
3
+
4
+ export const noDirectImplInRouteTrail = wrapRule({
5
+ examples: [
6
+ {
7
+ expected: { diagnostics: [] },
8
+ input: {
9
+ filePath: 'clean.ts',
10
+ sourceCode: `trail("entity.onboard", {
11
+ follow: ["entity.create"],
12
+ run: async (input, ctx) => {
13
+ const result = await ctx.follow("entity.create", input);
14
+ return Result.ok(result);
15
+ }
16
+ })`,
17
+ },
18
+ name: 'Trail with follow using ctx.follow()',
19
+ },
20
+ ],
21
+ rule: noDirectImplInRoute,
22
+ });
@@ -0,0 +1,16 @@
1
+ import { noDirectImplementationCall } from '../rules/no-direct-implementation-call.js';
2
+ import { wrapRule } from './wrap-rule.js';
3
+
4
+ export const noDirectImplementationCallTrail = wrapRule({
5
+ examples: [
6
+ {
7
+ expected: { diagnostics: [] },
8
+ input: {
9
+ filePath: 'clean.ts',
10
+ sourceCode: `const data = await ctx.follow("entity.show", { id: "1" });`,
11
+ },
12
+ name: 'Clean code using ctx.follow instead of .run()',
13
+ },
14
+ ],
15
+ rule: noDirectImplementationCall,
16
+ });
@@ -0,0 +1,19 @@
1
+ import { noSyncResultAssumption } from '../rules/no-sync-result-assumption.js';
2
+ import { wrapRule } from './wrap-rule.js';
3
+
4
+ export const noSyncResultAssumptionTrail = wrapRule({
5
+ examples: [
6
+ {
7
+ expected: { diagnostics: [] },
8
+ input: {
9
+ filePath: 'clean.ts',
10
+ sourceCode: `const result = await myTrail.run(input, ctx);
11
+ if (result.isOk()) {
12
+ console.log(result.value);
13
+ }`,
14
+ },
15
+ name: 'Properly awaited .run() call',
16
+ },
17
+ ],
18
+ rule: noSyncResultAssumption,
19
+ });
@@ -0,0 +1,20 @@
1
+ import { noThrowInDetourTarget } from '../rules/no-throw-in-detour-target.js';
2
+ import { wrapRule } from './wrap-rule.js';
3
+
4
+ export const noThrowInDetourTargetTrail = wrapRule({
5
+ examples: [
6
+ {
7
+ expected: { diagnostics: [] },
8
+ input: {
9
+ filePath: 'clean.ts',
10
+ sourceCode: `trail("entity.fallback", {
11
+ run: async (input, ctx) => {
12
+ return Result.ok({ recovered: true });
13
+ }
14
+ })`,
15
+ },
16
+ name: 'Detour target without throw',
17
+ },
18
+ ],
19
+ rule: noThrowInDetourTarget,
20
+ });
@@ -0,0 +1,20 @@
1
+ import { noThrowInImplementation } from '../rules/no-throw-in-implementation.js';
2
+ import { wrapRule } from './wrap-rule.js';
3
+
4
+ export const noThrowInImplementationTrail = wrapRule({
5
+ examples: [
6
+ {
7
+ expected: { diagnostics: [] },
8
+ input: {
9
+ filePath: 'clean.ts',
10
+ sourceCode: `trail("entity.show", {
11
+ run: async (input, ctx) => {
12
+ return Result.ok({ name: "test" });
13
+ }
14
+ })`,
15
+ },
16
+ name: 'Clean implementation without throw',
17
+ },
18
+ ],
19
+ rule: noThrowInImplementation,
20
+ });
@@ -0,0 +1,21 @@
1
+ import { preferSchemaInference } from '../rules/prefer-schema-inference.js';
2
+ import { wrapRule } from './wrap-rule.js';
3
+
4
+ export const preferSchemaInferenceTrail = wrapRule({
5
+ examples: [
6
+ {
7
+ expected: { diagnostics: [] },
8
+ input: {
9
+ filePath: 'clean.ts',
10
+ sourceCode: `trail("entity.show", {
11
+ input: z.object({ name: z.string() }),
12
+ run: async (input, ctx) => {
13
+ return Result.ok({ name: input.name });
14
+ }
15
+ })`,
16
+ },
17
+ name: 'Trail without redundant field overrides',
18
+ },
19
+ ],
20
+ rule: preferSchemaInference,
21
+ });
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Run all warden rule trails against a single source file.
3
+ *
4
+ * Returns a flat array of diagnostics from every rule.
5
+ */
6
+
7
+ import { dispatch } from '@ontrails/core';
8
+
9
+ import type { WardenDiagnostic } from '../rules/types.js';
10
+ import type { RuleOutput } from './schema.js';
11
+ import { wardenTopo } from './topo.js';
12
+
13
+ /**
14
+ * Dispatch all warden rule trails for a given file and collect diagnostics.
15
+ *
16
+ * Each rule trail runs independently. Errors from individual trails are
17
+ * silently skipped so that one broken rule does not block the rest.
18
+ */
19
+ export const runWardenTrails = async (
20
+ filePath: string,
21
+ sourceCode: string,
22
+ options?: { readonly knownTrailIds?: readonly string[] }
23
+ ): Promise<readonly WardenDiagnostic[]> => {
24
+ const allDiagnostics: WardenDiagnostic[] = [];
25
+
26
+ for (const id of wardenTopo.ids()) {
27
+ const input = options?.knownTrailIds
28
+ ? { filePath, knownTrailIds: options.knownTrailIds, sourceCode }
29
+ : { filePath, sourceCode };
30
+ const result = await dispatch(wardenTopo, id, input);
31
+ if (result.isOk()) {
32
+ const { diagnostics } = result.value as RuleOutput;
33
+ for (const d of diagnostics) {
34
+ allDiagnostics.push(d);
35
+ }
36
+ }
37
+ }
38
+
39
+ return allDiagnostics;
40
+ };