@ipation/specbridge 1.2.1 → 2.0.0
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 +56 -0
- package/README.md +29 -0
- package/dist/cli.js +6805 -643
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +227 -14
- package/dist/index.js +5754 -242
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/templates/verifiers/example-custom.ts +206 -0
package/package.json
CHANGED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Example Custom Verifier Plugin
|
|
3
|
+
*
|
|
4
|
+
* This template demonstrates how to create a custom verifier for SpecBridge.
|
|
5
|
+
* Copy this file to your project's .specbridge/verifiers/ directory and customize it.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* 1. Copy to .specbridge/verifiers/my-custom.ts
|
|
9
|
+
* 2. Implement your verification logic
|
|
10
|
+
* 3. Reference in decisions: check.verifier: 'my-custom'
|
|
11
|
+
* 4. Run: specbridge verify
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
defineVerifierPlugin,
|
|
16
|
+
createViolation,
|
|
17
|
+
type Verifier,
|
|
18
|
+
type VerificationContext,
|
|
19
|
+
type Violation,
|
|
20
|
+
} from '@ipation/specbridge';
|
|
21
|
+
import { z } from 'zod';
|
|
22
|
+
import { SyntaxKind } from 'ts-morph';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Define parameter schema for this verifier
|
|
26
|
+
* This validates the params passed in constraint.check.params
|
|
27
|
+
*/
|
|
28
|
+
const ParamsSchema = z.object({
|
|
29
|
+
// Example: Maximum allowed length
|
|
30
|
+
maxLength: z.number().positive().optional().default(100),
|
|
31
|
+
|
|
32
|
+
// Example: Pattern to match/avoid
|
|
33
|
+
pattern: z.string().optional(),
|
|
34
|
+
|
|
35
|
+
// Example: Case sensitivity for pattern matching
|
|
36
|
+
caseSensitive: z.boolean().optional().default(false),
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
type Params = z.infer<typeof ParamsSchema>;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Custom Verifier Implementation
|
|
43
|
+
*/
|
|
44
|
+
class MyCustomVerifier implements Verifier {
|
|
45
|
+
readonly id = 'my-custom';
|
|
46
|
+
readonly name = 'My Custom Verifier';
|
|
47
|
+
readonly description = 'Custom verification logic for project-specific patterns';
|
|
48
|
+
|
|
49
|
+
async verify(ctx: VerificationContext): Promise<Violation[]> {
|
|
50
|
+
const violations: Violation[] = [];
|
|
51
|
+
|
|
52
|
+
// Parse and validate parameters
|
|
53
|
+
const params: Params = ParamsSchema.parse(ctx.constraint.check?.params || {});
|
|
54
|
+
|
|
55
|
+
// Get the AST source file (ts-morph)
|
|
56
|
+
const sourceFile = ctx.sourceFile;
|
|
57
|
+
|
|
58
|
+
// Example 1: Check file length
|
|
59
|
+
const lineCount = sourceFile.getEndLineNumber();
|
|
60
|
+
if (lineCount > params.maxLength) {
|
|
61
|
+
violations.push(
|
|
62
|
+
createViolation({
|
|
63
|
+
decisionId: ctx.decisionId,
|
|
64
|
+
constraintId: ctx.constraint.id,
|
|
65
|
+
type: ctx.constraint.type,
|
|
66
|
+
severity: ctx.constraint.severity,
|
|
67
|
+
message: `File exceeds maximum length of ${params.maxLength} lines (found ${lineCount})`,
|
|
68
|
+
file: ctx.filePath,
|
|
69
|
+
line: 1,
|
|
70
|
+
suggestion: `Consider splitting this file into smaller modules`,
|
|
71
|
+
})
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Example 2: Check for specific patterns in identifiers
|
|
76
|
+
if (params.pattern) {
|
|
77
|
+
const regex = new RegExp(
|
|
78
|
+
params.pattern,
|
|
79
|
+
params.caseSensitive ? '' : 'i'
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
// Find all variable declarations
|
|
83
|
+
const variables = sourceFile.getVariableDeclarations();
|
|
84
|
+
|
|
85
|
+
for (const variable of variables) {
|
|
86
|
+
const name = variable.getName();
|
|
87
|
+
|
|
88
|
+
if (regex.test(name)) {
|
|
89
|
+
const startLine = variable.getStartLineNumber();
|
|
90
|
+
|
|
91
|
+
violations.push(
|
|
92
|
+
createViolation({
|
|
93
|
+
decisionId: ctx.decisionId,
|
|
94
|
+
constraintId: ctx.constraint.id,
|
|
95
|
+
type: ctx.constraint.type,
|
|
96
|
+
severity: ctx.constraint.severity,
|
|
97
|
+
message: `Variable "${name}" matches forbidden pattern: ${params.pattern}`,
|
|
98
|
+
file: ctx.filePath,
|
|
99
|
+
line: startLine,
|
|
100
|
+
suggestion: `Rename this variable to avoid the pattern`,
|
|
101
|
+
})
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Example 3: Check for specific AST patterns
|
|
108
|
+
// Find all function declarations with more than N parameters
|
|
109
|
+
const functions = sourceFile.getFunctions();
|
|
110
|
+
|
|
111
|
+
for (const func of functions) {
|
|
112
|
+
const paramCount = func.getParameters().length;
|
|
113
|
+
|
|
114
|
+
if (paramCount > 5) {
|
|
115
|
+
// Example threshold
|
|
116
|
+
violations.push(
|
|
117
|
+
createViolation({
|
|
118
|
+
decisionId: ctx.decisionId,
|
|
119
|
+
constraintId: ctx.constraint.id,
|
|
120
|
+
type: ctx.constraint.type,
|
|
121
|
+
severity: ctx.constraint.severity,
|
|
122
|
+
message: `Function "${func.getName() || '<anonymous>'}" has too many parameters (${paramCount})`,
|
|
123
|
+
file: ctx.filePath,
|
|
124
|
+
line: func.getStartLineNumber(),
|
|
125
|
+
suggestion: `Consider using an options object or splitting the function`,
|
|
126
|
+
})
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Example 4: Check for specific import patterns
|
|
132
|
+
const imports = sourceFile.getImportDeclarations();
|
|
133
|
+
|
|
134
|
+
for (const importDecl of imports) {
|
|
135
|
+
const moduleSpecifier = importDecl.getModuleSpecifierValue();
|
|
136
|
+
|
|
137
|
+
// Example: Forbid relative imports that go up more than one level
|
|
138
|
+
if (moduleSpecifier.startsWith('../..')) {
|
|
139
|
+
violations.push(
|
|
140
|
+
createViolation({
|
|
141
|
+
decisionId: ctx.decisionId,
|
|
142
|
+
constraintId: ctx.constraint.id,
|
|
143
|
+
type: ctx.constraint.type,
|
|
144
|
+
severity: ctx.constraint.severity,
|
|
145
|
+
message: `Deep relative import detected: ${moduleSpecifier}`,
|
|
146
|
+
file: ctx.filePath,
|
|
147
|
+
line: importDecl.getStartLineNumber(),
|
|
148
|
+
suggestion: `Use path aliases or refactor module structure`,
|
|
149
|
+
})
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Example 5: Check for specific syntax usage
|
|
155
|
+
// Find all console.log statements
|
|
156
|
+
const callExpressions = sourceFile.getDescendantsOfKind(
|
|
157
|
+
SyntaxKind.CallExpression
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
for (const call of callExpressions) {
|
|
161
|
+
const expression = call.getExpression();
|
|
162
|
+
|
|
163
|
+
if (
|
|
164
|
+
expression.getKind() === SyntaxKind.PropertyAccessExpression
|
|
165
|
+
) {
|
|
166
|
+
const propAccess = expression.asKindOrThrow(
|
|
167
|
+
SyntaxKind.PropertyAccessExpression
|
|
168
|
+
);
|
|
169
|
+
const obj = propAccess.getExpression().getText();
|
|
170
|
+
const prop = propAccess.getName();
|
|
171
|
+
|
|
172
|
+
if (obj === 'console' && prop === 'log') {
|
|
173
|
+
violations.push(
|
|
174
|
+
createViolation({
|
|
175
|
+
decisionId: ctx.decisionId,
|
|
176
|
+
constraintId: ctx.constraint.id,
|
|
177
|
+
type: ctx.constraint.type,
|
|
178
|
+
severity: 'low',
|
|
179
|
+
message: `console.log() statement found`,
|
|
180
|
+
file: ctx.filePath,
|
|
181
|
+
line: call.getStartLineNumber(),
|
|
182
|
+
suggestion: `Remove debug logging or use a proper logger`,
|
|
183
|
+
})
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return violations;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Export the plugin definition
|
|
195
|
+
* This is what SpecBridge will load
|
|
196
|
+
*/
|
|
197
|
+
export default defineVerifierPlugin({
|
|
198
|
+
metadata: {
|
|
199
|
+
id: 'my-custom',
|
|
200
|
+
version: '1.0.0',
|
|
201
|
+
author: 'Your Name',
|
|
202
|
+
description: 'Custom verifier for project-specific patterns',
|
|
203
|
+
},
|
|
204
|
+
createVerifier: () => new MyCustomVerifier(),
|
|
205
|
+
paramsSchema: ParamsSchema,
|
|
206
|
+
});
|