@ontrails/warden 1.0.0-beta.4 → 1.0.0-beta.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +835 -13
- package/README.md +133 -26
- package/bin/warden.ts +51 -0
- package/package.json +27 -6
- package/src/adapter-check.ts +136 -0
- package/src/cli.ts +1570 -105
- package/src/command.ts +986 -0
- package/src/config.ts +193 -0
- package/src/draft.ts +22 -0
- package/src/drift.ts +233 -23
- package/src/fix.ts +126 -0
- package/src/formatters.ts +78 -13
- package/src/guide.ts +245 -0
- package/src/index.ts +247 -15
- package/src/project-context.ts +446 -0
- package/src/project-rules.ts +290 -0
- package/src/resolve.ts +531 -0
- package/src/rules/activation-orphan.ts +97 -0
- package/src/rules/captured-kernel.ts +375 -0
- package/src/rules/circular-refs.ts +150 -0
- package/src/rules/cli-command-route-coherence.ts +177 -0
- package/src/rules/composes-declarations.ts +839 -0
- package/src/rules/context-no-surface-types.ts +79 -15
- package/src/rules/dead-internal-trail.ts +161 -0
- package/src/rules/dead-public-trail.ts +258 -0
- package/src/rules/draft-file-marking.ts +155 -0
- package/src/rules/draft-visible-debt.ts +82 -0
- package/src/rules/duplicate-exported-symbol.ts +211 -0
- package/src/rules/duplicate-public-contract.ts +137 -0
- package/src/rules/entity-exists.ts +254 -0
- package/src/rules/entity-ids.ts +15 -0
- package/src/rules/error-mapping-completeness.ts +290 -0
- package/src/rules/example-valid.ts +395 -0
- package/src/rules/fires-declarations.ts +740 -0
- package/src/rules/governed-symbol-residue.ts +438 -0
- package/src/rules/implementation-returns-result.ts +1409 -166
- package/src/rules/incomplete-accessor-for-standard-op.ts +272 -0
- package/src/rules/incomplete-crud.ts +583 -0
- package/src/rules/index.ts +277 -10
- package/src/rules/intent-propagation.ts +125 -0
- package/src/rules/layer-field-name-drift.ts +102 -0
- package/src/rules/library-projection-coherence.ts +100 -0
- package/src/rules/metadata.ts +871 -0
- package/src/rules/missing-reconcile.ts +97 -0
- package/src/rules/missing-visibility.ts +111 -0
- package/src/rules/no-destructured-compose.ts +196 -0
- package/src/rules/no-dev-permit-in-source.ts +99 -0
- package/src/rules/no-direct-implementation-call.ts +12 -7
- package/src/rules/no-legacy-cli-alias-export.ts +247 -0
- package/src/rules/no-legacy-layer-imports.ts +211 -0
- package/src/rules/no-native-error-result.ts +118 -0
- package/src/rules/no-redundant-result-error-wrap.ts +384 -0
- package/src/rules/no-retired-cross-vocabulary.ts +204 -0
- package/src/rules/no-sync-result-assumption.ts +1141 -98
- package/src/rules/no-throw-in-detour-recover.ts +225 -0
- package/src/rules/no-throw-in-implementation.ts +15 -8
- package/src/rules/no-top-level-surface.ts +371 -0
- package/src/rules/on-references-exist.ts +194 -0
- package/src/rules/orphaned-signal.ts +149 -0
- package/src/rules/owner-projection-parity.ts +143 -0
- package/src/rules/permit-governance.ts +25 -0
- package/src/rules/public-export-example-coverage.ts +573 -0
- package/src/rules/public-internal-deep-imports.ts +456 -0
- package/src/rules/public-output-schema.ts +29 -0
- package/src/rules/public-union-output-discriminants.ts +150 -0
- package/src/rules/read-intent-fires.ts +188 -0
- package/src/rules/reference-exists.ts +97 -0
- package/src/rules/registry-names.ts +167 -0
- package/src/rules/resolved-import-boundary.ts +146 -0
- package/src/rules/resource-declarations.ts +697 -0
- package/src/rules/resource-exists.ts +181 -0
- package/src/rules/resource-id-grammar.ts +65 -0
- package/src/rules/resource-mock-coverage.ts +115 -0
- package/src/rules/retired-vocabulary.ts +633 -0
- package/src/rules/scan.ts +38 -25
- package/src/rules/scheduled-destroy-intent.ts +44 -0
- package/src/rules/signal-graph-coaching.ts +220 -0
- package/src/rules/source/composition.ts +165 -0
- package/src/rules/source/drafts.ts +164 -0
- package/src/rules/source/entities.ts +618 -0
- package/src/rules/source/pragmas.ts +45 -0
- package/src/rules/source/resources.ts +64 -0
- package/src/rules/source/signals.ts +397 -0
- package/src/rules/source/stores.ts +310 -0
- package/src/rules/specs.ts +9 -5
- package/src/rules/static-resource-accessor-preference.ts +653 -0
- package/src/rules/surface-overlay-coherence.ts +262 -0
- package/src/rules/surface-trailhead-coherence.ts +366 -0
- package/src/rules/trail-fork-coaching.ts +625 -0
- package/src/rules/trail-versioning-source.ts +1076 -0
- package/src/rules/trail-versioning-topo.ts +172 -0
- package/src/rules/trailhead-override-divergence.ts +356 -0
- package/src/rules/types.ts +354 -8
- package/src/rules/unmaterialized-activation-source.ts +85 -0
- package/src/rules/unreachable-detour-shadowing.ts +339 -0
- package/src/rules/valid-describe-refs.ts +162 -32
- package/src/rules/valid-detour-contract.ts +78 -0
- package/src/rules/warden-export-symmetry.ts +540 -0
- package/src/rules/warden-rules-use-ast.ts +1109 -0
- package/src/rules/webhook-route-collision.ts +306 -0
- package/src/trails/activation-orphan.trail.ts +84 -0
- package/src/trails/captured-kernel.trail.ts +108 -0
- package/src/trails/circular-refs.trail.ts +29 -0
- package/src/trails/cli-command-route-coherence.trail.ts +47 -0
- package/src/trails/composes-declarations.trail.ts +22 -0
- package/src/trails/context-no-surface-types.trail.ts +21 -0
- package/src/trails/dead-internal-trail.trail.ts +26 -0
- package/src/trails/dead-public-trail.trail.ts +31 -0
- package/src/trails/deprecation-without-guidance.trail.ts +21 -0
- package/src/trails/draft-file-marking.trail.ts +16 -0
- package/src/trails/draft-visible-debt.trail.ts +16 -0
- package/src/trails/duplicate-exported-symbol.trail.ts +48 -0
- package/src/trails/duplicate-public-contract.trail.ts +47 -0
- package/src/trails/entity-exists.trail.ts +21 -0
- package/src/trails/error-mapping-completeness.trail.ts +30 -0
- package/src/trails/example-valid.trail.ts +25 -0
- package/src/trails/fires-declarations.trail.ts +23 -0
- package/src/trails/fork-without-preserved-implementation.trail.ts +31 -0
- package/src/trails/governed-symbol-residue.trail.ts +24 -0
- package/src/trails/implementation-returns-result.trail.ts +20 -0
- package/src/trails/incomplete-accessor-for-standard-op.trail.ts +76 -0
- package/src/trails/incomplete-crud.trail.ts +39 -0
- package/src/trails/index.ts +89 -0
- package/src/trails/intent-propagation.trail.ts +30 -0
- package/src/trails/layer-field-name-drift.trail.ts +39 -0
- package/src/trails/library-projection-coherence.trail.ts +43 -0
- package/src/trails/marker-schema-unsupported.trail.ts +23 -0
- package/src/trails/missing-reconcile.trail.ts +33 -0
- package/src/trails/missing-visibility.trail.ts +22 -0
- package/src/trails/no-destructured-compose.trail.ts +44 -0
- package/src/trails/no-dev-permit-in-source.trail.ts +16 -0
- package/src/trails/no-direct-implementation-call.trail.ts +16 -0
- package/src/trails/no-legacy-cli-alias-export.trail.ts +41 -0
- package/src/trails/no-legacy-layer-imports.trail.ts +41 -0
- package/src/trails/no-native-error-result.trail.ts +18 -0
- package/src/trails/no-redundant-result-error-wrap.trail.ts +55 -0
- package/src/trails/no-retired-cross-vocabulary.trail.ts +42 -0
- package/src/trails/no-sync-result-assumption.trail.ts +19 -0
- package/src/trails/no-throw-in-detour-recover.trail.ts +24 -0
- package/src/trails/no-throw-in-implementation.trail.ts +20 -0
- package/src/trails/no-top-level-surface.trail.ts +43 -0
- package/src/trails/on-references-exist.trail.ts +21 -0
- package/src/trails/orphaned-signal.trail.ts +36 -0
- package/src/trails/owner-projection-parity.trail.ts +26 -0
- package/src/trails/pending-force.trail.ts +21 -0
- package/src/trails/permit-governance.trail.ts +51 -0
- package/src/trails/prefer-schema-inference.trail.ts +21 -0
- package/src/trails/public-export-example-coverage.trail.ts +16 -0
- package/src/trails/public-internal-deep-imports.trail.ts +94 -0
- package/src/trails/public-output-schema.trail.ts +55 -0
- package/src/trails/public-union-output-discriminants.trail.ts +33 -0
- package/src/trails/read-intent-fires.trail.ts +20 -0
- package/src/trails/reference-exists.trail.ts +25 -0
- package/src/trails/resolved-import-boundary.trail.ts +109 -0
- package/src/trails/resource-declarations.trail.ts +25 -0
- package/src/trails/resource-exists.trail.ts +27 -0
- package/src/trails/resource-id-grammar.trail.ts +39 -0
- package/src/trails/resource-mock-coverage.trail.ts +40 -0
- package/src/trails/run.ts +160 -0
- package/src/trails/scheduled-destroy-intent.trail.ts +56 -0
- package/src/trails/schema.ts +237 -0
- package/src/trails/signal-graph-coaching.trail.ts +77 -0
- package/src/trails/static-resource-accessor-preference.trail.ts +25 -0
- package/src/trails/surface-overlay-coherence.trail.ts +24 -0
- package/src/trails/surface-trailhead-coherence.trail.ts +25 -0
- package/src/trails/topo.ts +6 -0
- package/src/trails/trail-fork-coaching.trail.ts +42 -0
- package/src/trails/trailhead-override-divergence.trail.ts +47 -0
- package/src/trails/unmaterialized-activation-source.trail.ts +72 -0
- package/src/trails/unreachable-detour-shadowing.trail.ts +45 -0
- package/src/trails/valid-describe-refs.trail.ts +18 -0
- package/src/trails/valid-detour-contract.trail.ts +71 -0
- package/src/trails/version-gap.trail.ts +35 -0
- package/src/trails/version-pinned-compose.trail.ts +23 -0
- package/src/trails/version-without-examples.trail.ts +38 -0
- package/src/trails/warden-export-symmetry.trail.ts +16 -0
- package/src/trails/warden-rules-use-ast.trail.ts +64 -0
- package/src/trails/webhook-route-collision.trail.ts +50 -0
- package/src/trails/wrap-rule.ts +224 -0
- package/src/workspaces.ts +199 -0
- package/.turbo/turbo-build.log +0 -1
- package/.turbo/turbo-lint.log +0 -3
- package/.turbo/turbo-typecheck.log +0 -1
- package/dist/cli.d.ts +0 -46
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js +0 -218
- package/dist/cli.js.map +0 -1
- package/dist/drift.d.ts +0 -26
- package/dist/drift.d.ts.map +0 -1
- package/dist/drift.js +0 -27
- package/dist/drift.js.map +0 -1
- package/dist/formatters.d.ts +0 -29
- package/dist/formatters.d.ts.map +0 -1
- package/dist/formatters.js +0 -87
- package/dist/formatters.js.map +0 -1
- package/dist/index.d.ts +0 -26
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -26
- package/dist/index.js.map +0 -1
- package/dist/rules/ast.d.ts +0 -41
- package/dist/rules/ast.d.ts.map +0 -1
- package/dist/rules/ast.js +0 -161
- package/dist/rules/ast.js.map +0 -1
- package/dist/rules/context-no-surface-types.d.ts +0 -12
- package/dist/rules/context-no-surface-types.d.ts.map +0 -1
- package/dist/rules/context-no-surface-types.js +0 -96
- package/dist/rules/context-no-surface-types.js.map +0 -1
- package/dist/rules/implementation-returns-result.d.ts +0 -13
- package/dist/rules/implementation-returns-result.d.ts.map +0 -1
- package/dist/rules/implementation-returns-result.js +0 -277
- package/dist/rules/implementation-returns-result.js.map +0 -1
- package/dist/rules/index.d.ts +0 -15
- package/dist/rules/index.d.ts.map +0 -1
- package/dist/rules/index.js +0 -34
- package/dist/rules/index.js.map +0 -1
- package/dist/rules/no-direct-impl-in-route.d.ts +0 -12
- package/dist/rules/no-direct-impl-in-route.d.ts.map +0 -1
- package/dist/rules/no-direct-impl-in-route.js +0 -47
- package/dist/rules/no-direct-impl-in-route.js.map +0 -1
- package/dist/rules/no-direct-implementation-call.d.ts +0 -12
- package/dist/rules/no-direct-implementation-call.d.ts.map +0 -1
- package/dist/rules/no-direct-implementation-call.js +0 -39
- package/dist/rules/no-direct-implementation-call.js.map +0 -1
- package/dist/rules/no-sync-result-assumption.d.ts +0 -6
- package/dist/rules/no-sync-result-assumption.d.ts.map +0 -1
- package/dist/rules/no-sync-result-assumption.js +0 -98
- package/dist/rules/no-sync-result-assumption.js.map +0 -1
- package/dist/rules/no-throw-in-detour-target.d.ts +0 -12
- package/dist/rules/no-throw-in-detour-target.d.ts.map +0 -1
- package/dist/rules/no-throw-in-detour-target.js +0 -87
- package/dist/rules/no-throw-in-detour-target.js.map +0 -1
- package/dist/rules/no-throw-in-implementation.d.ts +0 -9
- package/dist/rules/no-throw-in-implementation.d.ts.map +0 -1
- package/dist/rules/no-throw-in-implementation.js +0 -34
- package/dist/rules/no-throw-in-implementation.js.map +0 -1
- package/dist/rules/prefer-schema-inference.d.ts +0 -7
- package/dist/rules/prefer-schema-inference.d.ts.map +0 -1
- package/dist/rules/prefer-schema-inference.js +0 -86
- package/dist/rules/prefer-schema-inference.js.map +0 -1
- package/dist/rules/scan.d.ts +0 -8
- package/dist/rules/scan.d.ts.map +0 -1
- package/dist/rules/scan.js +0 -32
- package/dist/rules/scan.js.map +0 -1
- package/dist/rules/specs.d.ts +0 -29
- package/dist/rules/specs.d.ts.map +0 -1
- package/dist/rules/specs.js +0 -192
- package/dist/rules/specs.js.map +0 -1
- package/dist/rules/structure.d.ts +0 -13
- package/dist/rules/structure.d.ts.map +0 -1
- package/dist/rules/structure.js +0 -142
- package/dist/rules/structure.js.map +0 -1
- package/dist/rules/types.d.ts +0 -52
- package/dist/rules/types.d.ts.map +0 -1
- package/dist/rules/types.js +0 -2
- package/dist/rules/types.js.map +0 -1
- package/dist/rules/valid-describe-refs.d.ts +0 -7
- package/dist/rules/valid-describe-refs.d.ts.map +0 -1
- package/dist/rules/valid-describe-refs.js +0 -51
- package/dist/rules/valid-describe-refs.js.map +0 -1
- package/dist/rules/valid-detour-refs.d.ts +0 -6
- package/dist/rules/valid-detour-refs.d.ts.map +0 -1
- package/dist/rules/valid-detour-refs.js +0 -116
- package/dist/rules/valid-detour-refs.js.map +0 -1
- package/src/__tests__/cli.test.ts +0 -198
- package/src/__tests__/drift.test.ts +0 -74
- package/src/__tests__/formatters.test.ts +0 -157
- package/src/__tests__/implementation-returns-result.test.ts +0 -129
- package/src/__tests__/no-direct-implementation-call.test.ts +0 -83
- package/src/__tests__/no-sync-result-assumption.test.ts +0 -85
- package/src/__tests__/no-throw-in-detour-target.test.ts +0 -78
- package/src/__tests__/prefer-schema-inference.test.ts +0 -84
- package/src/__tests__/rules.test.ts +0 -227
- package/src/__tests__/valid-describe-refs.test.ts +0 -60
- package/src/rules/ast.ts +0 -213
- package/src/rules/no-direct-impl-in-route.ts +0 -81
- package/src/rules/no-throw-in-detour-target.ts +0 -150
- package/src/rules/valid-detour-refs.ts +0 -187
- package/tsconfig.json +0 -9
- package/tsconfig.tsbuildinfo +0 -1
|
@@ -0,0 +1,237 @@
|
|
|
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
|
+
|
|
8
|
+
import { intentValues } from '@ontrails/core';
|
|
9
|
+
import type { Topo } from '@ontrails/core';
|
|
10
|
+
import type { TopoGraph } from '@ontrails/topography';
|
|
11
|
+
import { z } from 'zod';
|
|
12
|
+
import { wardenImportResolutionErrorKinds } from '../resolve.js';
|
|
13
|
+
import { wardenFixClasses, wardenFixSafeties } from '../rules/metadata.js';
|
|
14
|
+
|
|
15
|
+
export const guidanceLinkSchema = z.object({
|
|
16
|
+
label: z.string(),
|
|
17
|
+
path: z.string().optional(),
|
|
18
|
+
url: z.string().optional(),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export const guidanceSchema = z.object({
|
|
22
|
+
commands: z.array(z.string()).readonly().optional(),
|
|
23
|
+
docs: z.array(guidanceLinkSchema).readonly().optional(),
|
|
24
|
+
relatedRules: z.array(z.string()).readonly().optional(),
|
|
25
|
+
steps: z.array(z.string()).readonly().optional(),
|
|
26
|
+
summary: z.string(),
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export const fixEditSchema = z.object({
|
|
30
|
+
end: z.number(),
|
|
31
|
+
replacement: z.string(),
|
|
32
|
+
start: z.number(),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
export const fixSchema = z.object({
|
|
36
|
+
class: z.enum(wardenFixClasses),
|
|
37
|
+
edits: z.array(fixEditSchema).readonly().optional(),
|
|
38
|
+
fixture: z.string().optional(),
|
|
39
|
+
reason: z.string(),
|
|
40
|
+
safety: z.enum(wardenFixSafeties),
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
/** A single diagnostic emitted by a warden rule trail. */
|
|
44
|
+
export const diagnosticSchema = z.object({
|
|
45
|
+
code: z.string().optional().describe('Optional rule-local diagnostic code'),
|
|
46
|
+
filePath: z.string().describe('File path that was analyzed'),
|
|
47
|
+
fix: fixSchema.optional().describe('Structured fix metadata'),
|
|
48
|
+
guidance: guidanceSchema
|
|
49
|
+
.optional()
|
|
50
|
+
.describe('Structured remediation guidance'),
|
|
51
|
+
line: z.number().describe('1-based line number'),
|
|
52
|
+
message: z.string().describe('Human-readable diagnostic message'),
|
|
53
|
+
rule: z.string().describe('Rule name'),
|
|
54
|
+
severity: z.enum(['error', 'warn']).describe('Diagnostic severity'),
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
/** Input accepted by every warden rule trail. */
|
|
58
|
+
export const ruleInput = z.object({
|
|
59
|
+
filePath: z.string().describe('Path to the source file'),
|
|
60
|
+
sourceCode: z.string().describe('Source code content'),
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
export const importResolutionSchema = z.object({
|
|
64
|
+
builtinModule: z.string().optional(),
|
|
65
|
+
crossesPackageBoundary: z.boolean(),
|
|
66
|
+
errorKind: z.enum(wardenImportResolutionErrorKinds).optional(),
|
|
67
|
+
errorMessage: z.string().optional(),
|
|
68
|
+
importSource: z.string(),
|
|
69
|
+
importerPath: z.string(),
|
|
70
|
+
isInternalTarget: z.boolean(),
|
|
71
|
+
line: z.number(),
|
|
72
|
+
packageName: z.string().optional(),
|
|
73
|
+
packageRoot: z.string().optional(),
|
|
74
|
+
resolvedPath: z.string().optional(),
|
|
75
|
+
usesPublicExport: z.boolean(),
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
export const publicWorkspaceSchema = z.object({
|
|
79
|
+
bin: z.record(z.string(), z.string()).optional(),
|
|
80
|
+
exportTargets: z.record(z.string(), z.string()).optional(),
|
|
81
|
+
files: z.array(z.string()).optional(),
|
|
82
|
+
hasExports: z.boolean(),
|
|
83
|
+
name: z.string(),
|
|
84
|
+
packageJsonPath: z.string(),
|
|
85
|
+
rootDir: z.string(),
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
export const exportedSymbolDefinitionSchema = z.object({
|
|
89
|
+
filePath: z.string(),
|
|
90
|
+
kind: z.enum([
|
|
91
|
+
'class',
|
|
92
|
+
'const',
|
|
93
|
+
'enum',
|
|
94
|
+
'export',
|
|
95
|
+
'function',
|
|
96
|
+
'interface',
|
|
97
|
+
'type',
|
|
98
|
+
]),
|
|
99
|
+
line: z.number(),
|
|
100
|
+
name: z.string(),
|
|
101
|
+
workspaceName: z.string(),
|
|
102
|
+
workspaceRoot: z.string(),
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Extended input for project-aware warden rule trails.
|
|
107
|
+
*
|
|
108
|
+
* Adds `knownTrailIds` so the caller can supply compose-file context and avoid
|
|
109
|
+
* false positives for `@see` references or compose-file entity relationships.
|
|
110
|
+
*/
|
|
111
|
+
export const authoredMcpSurfaceBindingSetSchema = z.object({
|
|
112
|
+
appName: z.string().describe('App/topo label the bindings were authored for'),
|
|
113
|
+
bindings: z
|
|
114
|
+
.record(z.string(), z.union([z.string(), z.array(z.string()).readonly()]))
|
|
115
|
+
.describe('Surfaces overlay mcp bindings: binding name to selector(s)'),
|
|
116
|
+
trailIds: z
|
|
117
|
+
.array(z.string())
|
|
118
|
+
.readonly()
|
|
119
|
+
.describe('Trail ids registered in the owning app topo'),
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
export const projectAwareRuleInput = ruleInput.extend({
|
|
123
|
+
authoredMcpSurfaceBindingSets: z
|
|
124
|
+
.array(authoredMcpSurfaceBindingSetSchema)
|
|
125
|
+
.readonly()
|
|
126
|
+
.optional()
|
|
127
|
+
.describe(
|
|
128
|
+
'Per-app authored surfaces overlay mcp bindings with owning-app trail ids'
|
|
129
|
+
),
|
|
130
|
+
composeTargetTrailIds: z
|
|
131
|
+
.array(z.string())
|
|
132
|
+
.optional()
|
|
133
|
+
.describe('Trail IDs referenced by composes arrays across the project'),
|
|
134
|
+
crudCoverageByEntity: z
|
|
135
|
+
.record(z.string(), z.array(z.string()))
|
|
136
|
+
.optional()
|
|
137
|
+
.describe(
|
|
138
|
+
'CRUD operation coverage per entity aggregated across the project'
|
|
139
|
+
),
|
|
140
|
+
crudTableIds: z
|
|
141
|
+
.array(z.string())
|
|
142
|
+
.optional()
|
|
143
|
+
.describe('Store table IDs used with CRUD factories across the project'),
|
|
144
|
+
documentedImportResolutionsByFile: z
|
|
145
|
+
.record(z.string(), z.array(importResolutionSchema))
|
|
146
|
+
.optional()
|
|
147
|
+
.describe('Resolved docs/specifier facts keyed by documentation file path'),
|
|
148
|
+
entityReferencesByName: z
|
|
149
|
+
.record(z.string(), z.array(z.string()))
|
|
150
|
+
.optional()
|
|
151
|
+
.describe('Declared entity references keyed by source entity name'),
|
|
152
|
+
exportedSymbolDefinitionsByName: z
|
|
153
|
+
.record(z.string(), z.array(exportedSymbolDefinitionSchema))
|
|
154
|
+
.optional()
|
|
155
|
+
.describe('Public exported symbol definitions grouped by exported name'),
|
|
156
|
+
importResolutionsByFile: z
|
|
157
|
+
.record(z.string(), z.array(importResolutionSchema))
|
|
158
|
+
.optional()
|
|
159
|
+
.describe('Resolved import facts keyed by importer file path'),
|
|
160
|
+
knownEntityIds: z
|
|
161
|
+
.array(z.string())
|
|
162
|
+
.optional()
|
|
163
|
+
.describe('Entity names known across the project'),
|
|
164
|
+
knownResourceIds: z
|
|
165
|
+
.array(z.string())
|
|
166
|
+
.optional()
|
|
167
|
+
.describe('Resource IDs known across the project'),
|
|
168
|
+
knownSignalIds: z
|
|
169
|
+
.array(z.string())
|
|
170
|
+
.optional()
|
|
171
|
+
.describe('Signal IDs known across the project'),
|
|
172
|
+
knownTrailIds: z
|
|
173
|
+
.array(z.string())
|
|
174
|
+
.optional()
|
|
175
|
+
.describe('Trail IDs known across the project'),
|
|
176
|
+
onTargetSignalIds: z
|
|
177
|
+
.array(z.string())
|
|
178
|
+
.optional()
|
|
179
|
+
.describe('Signal IDs referenced by trail on arrays across the project'),
|
|
180
|
+
publicWorkspaces: z
|
|
181
|
+
.record(z.string(), publicWorkspaceSchema)
|
|
182
|
+
.optional()
|
|
183
|
+
.describe('Non-private published @ontrails workspaces by package name'),
|
|
184
|
+
reconcileTableIds: z
|
|
185
|
+
.array(z.string())
|
|
186
|
+
.optional()
|
|
187
|
+
.describe('Store table IDs used with reconcile trails across the project'),
|
|
188
|
+
topoTrailIds: z
|
|
189
|
+
.array(z.string())
|
|
190
|
+
.optional()
|
|
191
|
+
.describe('Trail IDs registered in configured app topo targets'),
|
|
192
|
+
trailIntentsById: z
|
|
193
|
+
.record(z.string(), z.enum(intentValues))
|
|
194
|
+
.optional()
|
|
195
|
+
.describe('Normalized trail intents keyed by trail ID'),
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Input for topo-aware warden rule trails.
|
|
200
|
+
*
|
|
201
|
+
* The `Topo` graph is not a serializable value, so the schema accepts it
|
|
202
|
+
* as an opaque `z.custom`. Topo-aware rules are invoked from the warden
|
|
203
|
+
* runtime with a live, resolved topo reference — they are not expected
|
|
204
|
+
* to be called across a network boundary.
|
|
205
|
+
*/
|
|
206
|
+
export const topoAwareRuleInput = z.object({
|
|
207
|
+
graph: z
|
|
208
|
+
.custom<TopoGraph>(
|
|
209
|
+
(value) =>
|
|
210
|
+
typeof value === 'object' && value !== null && 'entries' in value,
|
|
211
|
+
{ message: 'Expected a serialized TopoGraph object' }
|
|
212
|
+
)
|
|
213
|
+
.optional()
|
|
214
|
+
.describe('Optional derived TopoGraph with graph-only audit annotations'),
|
|
215
|
+
topo: z
|
|
216
|
+
.custom<Topo>(
|
|
217
|
+
(value) =>
|
|
218
|
+
typeof value === 'object' &&
|
|
219
|
+
value !== null &&
|
|
220
|
+
'trails' in value &&
|
|
221
|
+
'resources' in value &&
|
|
222
|
+
'entities' in value &&
|
|
223
|
+
'signals' in value,
|
|
224
|
+
{ message: 'Expected a resolved Topo instance' }
|
|
225
|
+
)
|
|
226
|
+
.describe('Resolved topo graph under inspection'),
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
/** Output returned by every warden rule trail. */
|
|
230
|
+
export const ruleOutput = z.object({
|
|
231
|
+
diagnostics: z.array(diagnosticSchema).describe('Diagnostics found'),
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
export type RuleInput = z.infer<typeof ruleInput>;
|
|
235
|
+
export type ProjectAwareRuleInput = z.infer<typeof projectAwareRuleInput>;
|
|
236
|
+
export type TopoAwareRuleInput = z.infer<typeof topoAwareRuleInput>;
|
|
237
|
+
export type RuleOutput = z.infer<typeof ruleOutput>;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Result, resource, signal, topo, trail } from '@ontrails/core';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
import { signalGraphCoaching } from '../rules/signal-graph-coaching.js';
|
|
5
|
+
import { wrapTopoRule } from './wrap-rule.js';
|
|
6
|
+
|
|
7
|
+
const unusedSignal = signal('invoice.unused', {
|
|
8
|
+
payload: z.object({ invoiceId: z.string() }),
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const producedSignal = signal('invoice.created', {
|
|
12
|
+
payload: z.object({ invoiceId: z.string() }),
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
const producerTrail = trail('invoice.create', {
|
|
16
|
+
fires: [producedSignal],
|
|
17
|
+
implementation: async (_input, ctx) => {
|
|
18
|
+
await ctx.fire?.(producedSignal, { invoiceId: 'inv_1' });
|
|
19
|
+
return Result.ok({ invoiceId: 'inv_1' });
|
|
20
|
+
},
|
|
21
|
+
input: z.object({}),
|
|
22
|
+
output: z.object({ invoiceId: z.string() }),
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const resourceSignal = signal('store:invoice.created', {
|
|
26
|
+
payload: z.object({ invoiceId: z.string() }),
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const invoiceStore = resource('store', {
|
|
30
|
+
create: () => Result.ok({ ok: true }),
|
|
31
|
+
signals: [resourceSignal],
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
export const signalGraphCoachingTrail = wrapTopoRule({
|
|
35
|
+
examples: [
|
|
36
|
+
{
|
|
37
|
+
expected: {
|
|
38
|
+
diagnostics: [
|
|
39
|
+
{
|
|
40
|
+
filePath: '<topo>',
|
|
41
|
+
line: 1,
|
|
42
|
+
message:
|
|
43
|
+
'Signal "invoice.created" is produced by producer trail "invoice.create" but has no consumer trails. Add an on: consumer if the signal is meant to drive reactive work, or remove the unused fires:/producer declaration.',
|
|
44
|
+
rule: 'signal-graph-coaching',
|
|
45
|
+
severity: 'warn',
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
filePath: '<topo>',
|
|
49
|
+
line: 1,
|
|
50
|
+
message:
|
|
51
|
+
'Signal "invoice.unused" is declared in the topo but has no producer trails, producer resources, or consumer trails. Add fires:/on: edges, attach producer metadata, or remove the unused signal contract.',
|
|
52
|
+
rule: 'signal-graph-coaching',
|
|
53
|
+
severity: 'warn',
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
filePath: '<topo>',
|
|
57
|
+
line: 1,
|
|
58
|
+
message:
|
|
59
|
+
'Signal "store:invoice.created" is produced by producer resource "store" but has no consumer trails. Add an on: consumer if the signal is meant to drive reactive work, or remove the unused fires:/producer declaration.',
|
|
60
|
+
rule: 'signal-graph-coaching',
|
|
61
|
+
severity: 'warn',
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
},
|
|
65
|
+
input: {
|
|
66
|
+
topo: topo('trl-447-signal-graph-coaching', {
|
|
67
|
+
invoiceStore,
|
|
68
|
+
producedSignal,
|
|
69
|
+
producerTrail,
|
|
70
|
+
unusedSignal,
|
|
71
|
+
}),
|
|
72
|
+
},
|
|
73
|
+
name: 'Declared and produced signals without consumers get coaching',
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
rule: signalGraphCoaching,
|
|
77
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { staticResourceAccessorPreference } from '../rules/static-resource-accessor-preference.js';
|
|
2
|
+
import { wrapRule } from './wrap-rule.js';
|
|
3
|
+
|
|
4
|
+
export const staticResourceAccessorPreferenceTrail = wrapRule({
|
|
5
|
+
examples: [
|
|
6
|
+
{
|
|
7
|
+
expected: { diagnostics: [] },
|
|
8
|
+
input: {
|
|
9
|
+
filePath: 'clean.ts',
|
|
10
|
+
sourceCode: `const db = resource("db.main", {
|
|
11
|
+
create: () => Result.ok({ source: "factory" }),
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
trail("entity.show", {
|
|
15
|
+
resources: [db],
|
|
16
|
+
implementation: async (_input, ctx) => {
|
|
17
|
+
return Result.ok(db.from(ctx));
|
|
18
|
+
}
|
|
19
|
+
})`,
|
|
20
|
+
},
|
|
21
|
+
name: 'Static resource helper access',
|
|
22
|
+
},
|
|
23
|
+
],
|
|
24
|
+
rule: staticResourceAccessorPreference,
|
|
25
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Result, topo, trail } from '@ontrails/core';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
import { surfaceOverlayCoherence } from '../rules/surface-overlay-coherence.js';
|
|
5
|
+
import { wrapTopoRule } from './wrap-rule.js';
|
|
6
|
+
|
|
7
|
+
const listTrail = trail('gear.list', {
|
|
8
|
+
implementation: () => Result.ok([]),
|
|
9
|
+
input: z.object({}),
|
|
10
|
+
output: z.array(z.string()),
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
export const surfaceOverlayCoherenceTrail = wrapTopoRule({
|
|
14
|
+
examples: [
|
|
15
|
+
{
|
|
16
|
+
expected: { diagnostics: [] },
|
|
17
|
+
input: {
|
|
18
|
+
topo: topo('surface-overlay-coherence', { listTrail }),
|
|
19
|
+
},
|
|
20
|
+
name: 'Topo without a serialized surfaces overlay stays quiet',
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
rule: surfaceOverlayCoherence,
|
|
24
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { surfaceTrailheadCoherence } from '../rules/surface-trailhead-coherence.js';
|
|
2
|
+
import { wrapRule } from './wrap-rule.js';
|
|
3
|
+
|
|
4
|
+
export const surfaceTrailheadCoherenceTrail = wrapRule({
|
|
5
|
+
examples: [
|
|
6
|
+
{
|
|
7
|
+
expected: { diagnostics: [] },
|
|
8
|
+
input: {
|
|
9
|
+
filePath: 'mcp-options.ts',
|
|
10
|
+
sourceCode: `export const trailheads = {
|
|
11
|
+
inspect: {
|
|
12
|
+
description: "Inspect topo state.",
|
|
13
|
+
trails: ["survey", "survey.brief"],
|
|
14
|
+
},
|
|
15
|
+
governance: {
|
|
16
|
+
description: "Run diagnostics.",
|
|
17
|
+
trails: ["warden", "doctor"],
|
|
18
|
+
},
|
|
19
|
+
};`,
|
|
20
|
+
},
|
|
21
|
+
name: 'Reviewable trailhead map',
|
|
22
|
+
},
|
|
23
|
+
],
|
|
24
|
+
rule: surfaceTrailheadCoherence,
|
|
25
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { trailForkCoaching } from '../rules/trail-fork-coaching.js';
|
|
2
|
+
import { wrapRule } from './wrap-rule.js';
|
|
3
|
+
|
|
4
|
+
export const trailForkCoachingTrail = wrapRule({
|
|
5
|
+
examples: [
|
|
6
|
+
{
|
|
7
|
+
expected: {
|
|
8
|
+
diagnostics: [
|
|
9
|
+
{
|
|
10
|
+
filePath: 'users.ts',
|
|
11
|
+
line: 9,
|
|
12
|
+
message:
|
|
13
|
+
'Trail "users.manage" branches on input.action ("create", "delete"). This may be a trail fork hidden as a surface accommodation. If branches change semantics (intent, permits, errors, outputs, lifecycle, or side effects) or structure (selected trail identity), split them into distinct trails, a composing trail, or a trailhead that preserves member identity.',
|
|
14
|
+
rule: 'trail-fork-coaching',
|
|
15
|
+
severity: 'warn',
|
|
16
|
+
},
|
|
17
|
+
],
|
|
18
|
+
},
|
|
19
|
+
input: {
|
|
20
|
+
filePath: 'users.ts',
|
|
21
|
+
sourceCode: `import { Result, trail } from "@ontrails/core";
|
|
22
|
+
import { z } from "zod";
|
|
23
|
+
|
|
24
|
+
export const usersManage = trail("users.manage", {
|
|
25
|
+
input: z.object({
|
|
26
|
+
action: z.enum(["create", "delete"]),
|
|
27
|
+
}),
|
|
28
|
+
implementation: async (input) => {
|
|
29
|
+
switch (input.action) {
|
|
30
|
+
case "create":
|
|
31
|
+
return Result.ok({ created: true });
|
|
32
|
+
case "delete":
|
|
33
|
+
return Result.ok({ deleted: true });
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
});`,
|
|
37
|
+
},
|
|
38
|
+
name: 'Possible trail fork hidden behind action',
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
rule: trailForkCoaching,
|
|
42
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { trailheadOverrideDivergence } from '../rules/trailhead-override-divergence.js';
|
|
2
|
+
import { wrapRule } from './wrap-rule.js';
|
|
3
|
+
|
|
4
|
+
export const trailheadOverrideDivergenceTrail = wrapRule({
|
|
5
|
+
examples: [
|
|
6
|
+
{
|
|
7
|
+
expected: { diagnostics: [] },
|
|
8
|
+
input: {
|
|
9
|
+
authoredMcpSurfaceBindingSets: [
|
|
10
|
+
{
|
|
11
|
+
appName: 'demo',
|
|
12
|
+
bindings: { inspect: ['survey', 'survey.brief'] },
|
|
13
|
+
trailIds: ['survey', 'survey.brief'],
|
|
14
|
+
},
|
|
15
|
+
],
|
|
16
|
+
filePath: 'mcp-options.ts',
|
|
17
|
+
sourceCode: `export const trailheads = {
|
|
18
|
+
inspect: {
|
|
19
|
+
description: "Inspect topo state.",
|
|
20
|
+
trails: ["survey", "survey.brief"],
|
|
21
|
+
},
|
|
22
|
+
};`,
|
|
23
|
+
},
|
|
24
|
+
name: 'Aligned call-site override',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
input: {
|
|
28
|
+
authoredMcpSurfaceBindingSets: [
|
|
29
|
+
{
|
|
30
|
+
appName: 'demo',
|
|
31
|
+
bindings: { inspect: ['survey', 'survey.brief'] },
|
|
32
|
+
trailIds: ['survey', 'survey.brief'],
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
filePath: 'mcp-options.ts',
|
|
36
|
+
sourceCode: `export const trailheads = {
|
|
37
|
+
inspect: {
|
|
38
|
+
description: "Inspect topo state.",
|
|
39
|
+
trails: ["survey"],
|
|
40
|
+
},
|
|
41
|
+
};`,
|
|
42
|
+
},
|
|
43
|
+
name: 'Diverging member selectors are flagged',
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
rule: trailheadOverrideDivergence,
|
|
47
|
+
});
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Result, schedule, signal, topo, trail, webhook } from '@ontrails/core';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
import { unmaterializedActivationSource } from '../rules/unmaterialized-activation-source.js';
|
|
5
|
+
import { wrapTopoRule } from './wrap-rule.js';
|
|
6
|
+
|
|
7
|
+
const invoicePaidWebhook = webhook('webhook.invoice.paid', {
|
|
8
|
+
parse: z.object({ invoiceId: z.string() }),
|
|
9
|
+
path: '/webhooks/invoice/paid',
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
const webhookConsumer = trail('invoice.audit-webhook', {
|
|
13
|
+
implementation: () => Result.ok({ ok: true }),
|
|
14
|
+
input: z.object({ invoiceId: z.string() }),
|
|
15
|
+
on: [invoicePaidWebhook],
|
|
16
|
+
output: z.object({ ok: z.boolean() }),
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const invoiceCreated = signal('invoice.created', {
|
|
20
|
+
from: ['invoice.create'],
|
|
21
|
+
payload: z.object({ invoiceId: z.string() }),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const signalProducer = trail('invoice.create', {
|
|
25
|
+
fires: [invoiceCreated],
|
|
26
|
+
implementation: async (_input, ctx) => {
|
|
27
|
+
await ctx.fire?.(invoiceCreated, { invoiceId: 'inv_1' });
|
|
28
|
+
return Result.ok({ invoiceId: 'inv_1' });
|
|
29
|
+
},
|
|
30
|
+
input: z.object({}),
|
|
31
|
+
output: z.object({ invoiceId: z.string() }),
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const signalConsumer = trail('invoice.index', {
|
|
35
|
+
implementation: () => Result.ok({ ok: true }),
|
|
36
|
+
input: z.object({ invoiceId: z.string() }),
|
|
37
|
+
on: [invoiceCreated],
|
|
38
|
+
output: z.object({ ok: z.boolean() }),
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const scheduleConsumer = trail('invoice.reconcile', {
|
|
42
|
+
implementation: () => Result.ok({ ok: true }),
|
|
43
|
+
input: z.object({}),
|
|
44
|
+
on: [schedule('schedule.invoice.reconcile', { cron: '0 * * * *' })],
|
|
45
|
+
output: z.object({ ok: z.boolean() }),
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
export const unmaterializedActivationSourceTrail = wrapTopoRule({
|
|
49
|
+
examples: [
|
|
50
|
+
{
|
|
51
|
+
expected: { diagnostics: [] },
|
|
52
|
+
input: {
|
|
53
|
+
topo: topo('trl-461-webhook-materialized', { webhookConsumer }),
|
|
54
|
+
},
|
|
55
|
+
name: 'Webhook activation sources are materialized by HTTP',
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
expected: { diagnostics: [] },
|
|
59
|
+
input: {
|
|
60
|
+
topo: topo('trl-496-materialized-sources', {
|
|
61
|
+
invoiceCreated,
|
|
62
|
+
scheduleConsumer,
|
|
63
|
+
signalConsumer,
|
|
64
|
+
signalProducer,
|
|
65
|
+
webhookConsumer,
|
|
66
|
+
}),
|
|
67
|
+
},
|
|
68
|
+
name: 'Signal, schedule, and webhook activation sources are materialized',
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
rule: unmaterializedActivationSource,
|
|
72
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { unreachableDetourShadowing } from '../rules/unreachable-detour-shadowing.js';
|
|
2
|
+
import { wrapRule } from './wrap-rule.js';
|
|
3
|
+
|
|
4
|
+
export const unreachableDetourShadowingTrail = wrapRule({
|
|
5
|
+
examples: [
|
|
6
|
+
{
|
|
7
|
+
expected: { diagnostics: [] },
|
|
8
|
+
input: {
|
|
9
|
+
filePath: 'clean.ts',
|
|
10
|
+
sourceCode: `trail("entity.save", {
|
|
11
|
+
detours: [
|
|
12
|
+
{ on: ConflictError, recover: async () => Result.ok({ winner: "specific" }) },
|
|
13
|
+
{ on: TrailsError, recover: async () => Result.ok({ winner: "broad" }) },
|
|
14
|
+
],
|
|
15
|
+
});`,
|
|
16
|
+
},
|
|
17
|
+
name: 'Specific detours can precede broader ones',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
expected: {
|
|
21
|
+
diagnostics: [
|
|
22
|
+
{
|
|
23
|
+
filePath: 'shadowed.ts',
|
|
24
|
+
line: 4,
|
|
25
|
+
message:
|
|
26
|
+
'Trail "entity.save" declares detour on "ConflictError" after earlier detour on "TrailsError". Because "TrailsError" matches "ConflictError" first, the later detour is unreachable.',
|
|
27
|
+
rule: 'unreachable-detour-shadowing',
|
|
28
|
+
severity: 'error',
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
},
|
|
32
|
+
input: {
|
|
33
|
+
filePath: 'shadowed.ts',
|
|
34
|
+
sourceCode: `trail("entity.save", {
|
|
35
|
+
detours: [
|
|
36
|
+
{ on: TrailsError, recover: async () => Result.ok({ winner: "broad" }) },
|
|
37
|
+
{ on: ConflictError, recover: async () => Result.ok({ winner: "specific" }) },
|
|
38
|
+
],
|
|
39
|
+
});`,
|
|
40
|
+
},
|
|
41
|
+
name: 'Broader detours declared first shadow later specific ones',
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
rule: unreachableDetourShadowing,
|
|
45
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { validDescribeRefs } from '../rules/valid-describe-refs.js';
|
|
2
|
+
import { wrapRule } from './wrap-rule.js';
|
|
3
|
+
|
|
4
|
+
export const validDescribeRefsTrail = wrapRule({
|
|
5
|
+
examples: [
|
|
6
|
+
{
|
|
7
|
+
expected: { diagnostics: [] },
|
|
8
|
+
input: {
|
|
9
|
+
filePath: 'clean.ts',
|
|
10
|
+
sourceCode: `const schema = z.object({
|
|
11
|
+
name: z.string().describe("User display name"),
|
|
12
|
+
});`,
|
|
13
|
+
},
|
|
14
|
+
name: 'Describe without @see refs',
|
|
15
|
+
},
|
|
16
|
+
],
|
|
17
|
+
rule: validDescribeRefs,
|
|
18
|
+
});
|