speckeeper 0.8.1 → 0.9.1
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/README.md +137 -66
- package/dist/cli.js +639 -102
- package/dist/cli.js.map +1 -1
- package/dist/{config-api-U2pt1aHJ.d.ts → config-api-CfxXt9Zt.d.ts} +110 -2
- package/dist/dsl/index.d.ts +50 -82
- package/dist/dsl/index.js +434 -453
- package/dist/dsl/index.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.js +26 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,20 +11,28 @@
|
|
|
11
11
|
Requirements and design documents often drift from implementation. **speckeeper** treats specifications as **code** — type-safe, version-controlled, and continuously validated against your actual artifacts (tests, OpenAPI, DDL, IaC).
|
|
12
12
|
|
|
13
13
|
```
|
|
14
|
-
|
|
15
|
-
│
|
|
16
|
-
design/*.ts ─────────────────────────────► Validation & Consistency Checks
|
|
14
|
+
speckeeper.config.ts (sources)
|
|
17
15
|
│
|
|
18
|
-
├─►
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
├─► Global Source Scan → Find spec IDs in OpenAPI / DDL / annotations
|
|
17
|
+
│ │
|
|
18
|
+
│ ▼
|
|
19
|
+
│ MatchMap (specId → matches)
|
|
20
|
+
│ │
|
|
21
|
+
│ ▼
|
|
22
|
+
├─► Deep Validation → Model-level structural checks (optional)
|
|
23
|
+
│
|
|
24
|
+
design/*.ts
|
|
25
|
+
│
|
|
26
|
+
├─► speckeeper lint → Design integrity (IDs, references, phase gates)
|
|
27
|
+
├─► speckeeper check → External SSOT validation (global scan + deep validation)
|
|
28
|
+
└─► speckeeper impact → Change impact analysis with traceability
|
|
21
29
|
```
|
|
22
30
|
|
|
23
31
|
## Features
|
|
24
32
|
|
|
25
33
|
- **TypeScript as SSOT** — Define requirements, architecture, and design in type-safe TypeScript
|
|
26
34
|
- **Design validation** — Lint rules for ID uniqueness, reference integrity, circular dependencies, and phase gates
|
|
27
|
-
- **External SSOT validation** —
|
|
35
|
+
- **External SSOT validation** — Global scan across OpenAPI, DDL, annotations; optional deep validation per model
|
|
28
36
|
- **Traceability** — Track relationships across model levels (L0-L3) with impact analysis
|
|
29
37
|
- **Scaffold from Mermaid** — Generate `_models/` skeletons from a mermaid flowchart with class-based artifact resolution
|
|
30
38
|
- **Custom models** — Extend with domain-specific models (Runbooks, Policies, etc.)
|
|
@@ -78,10 +86,9 @@ flowchart TB
|
|
|
78
86
|
Key concepts:
|
|
79
87
|
- `class ... speckeeper` marks nodes as managed by speckeeper
|
|
80
88
|
- Additional `class` lines assign **artifact classes** (determines model name/file and node grouping)
|
|
81
|
-
- External node classes (`openapi`, `sqlschema`, `test`)
|
|
89
|
+
- External node classes (`openapi`, `sqlschema`, `test`) describe the artifact type
|
|
82
90
|
- `subgraph` determines model level (L0–L3)
|
|
83
|
-
- `implements` edges
|
|
84
|
-
- `verifiedBy` edges trigger test verification
|
|
91
|
+
- `implements`/`verifiedBy` edges define the relationship semantics
|
|
85
92
|
|
|
86
93
|
### 2. Scaffold models
|
|
87
94
|
|
|
@@ -90,7 +97,7 @@ npx speckeeper scaffold --source requirements.md
|
|
|
90
97
|
```
|
|
91
98
|
|
|
92
99
|
This generates:
|
|
93
|
-
- `design/_models/` — Model classes with base schema
|
|
100
|
+
- `design/_models/` — Model classes with base schema and lint rules
|
|
94
101
|
- `design/*.ts` — Spec data files using `defineSpecs()`
|
|
95
102
|
- `design/index.ts` — Entry point via `mergeSpecs()`
|
|
96
103
|
|
|
@@ -183,11 +190,64 @@ Checks include:
|
|
|
183
190
|
|
|
184
191
|
### External SSOT Validation (check)
|
|
185
192
|
|
|
186
|
-
Validate your specifications against actual implementation artifacts. speckeeper
|
|
193
|
+
Validate your specifications against actual implementation artifacts. speckeeper performs a **global source scan** across all configured sources, then optionally runs **deep validation** using model-specific rules.
|
|
194
|
+
|
|
195
|
+
The check flow has three levels:
|
|
196
|
+
|
|
197
|
+
1. **Existence check** (automatic) — Is the spec ID found in any configured source?
|
|
198
|
+
2. **Structural check** (via `deepValidation`) — Does the matched object's structure match? (e.g. HTTP method, table columns)
|
|
199
|
+
3. **Type check** (via `deepValidation`) — Do types match? (e.g. parameter types, column types)
|
|
200
|
+
|
|
201
|
+
#### Source configuration
|
|
202
|
+
|
|
203
|
+
Define global scan sources in `speckeeper.config.ts`:
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
// speckeeper.config.ts
|
|
207
|
+
import { defineConfig } from 'speckeeper';
|
|
208
|
+
|
|
209
|
+
export default defineConfig({
|
|
210
|
+
// ...
|
|
211
|
+
sources: [
|
|
212
|
+
{
|
|
213
|
+
type: 'openapi',
|
|
214
|
+
paths: ['api/openapi.yaml'],
|
|
215
|
+
relation: 'implements',
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
type: 'ddl',
|
|
219
|
+
paths: ['db/schema.sql'],
|
|
220
|
+
relation: 'implements',
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
type: 'annotation',
|
|
224
|
+
paths: ['test/**/*.test.ts', 'tests/**/*.test.ts'],
|
|
225
|
+
relation: 'verifiedBy',
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
type: 'annotation',
|
|
229
|
+
paths: ['src/**/*.ts'],
|
|
230
|
+
exclude: ['src/**/*.test.ts'],
|
|
231
|
+
relation: 'implements',
|
|
232
|
+
},
|
|
233
|
+
],
|
|
234
|
+
});
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Each source defines:
|
|
238
|
+
- **`type`** — Built-in (`'openapi'`, `'ddl'`, `'annotation'`) or custom with a `scanner` plugin
|
|
239
|
+
- **`paths`** — Glob patterns for files to scan
|
|
240
|
+
- **`relation`** — Whether matches represent `'implements'` or `'verifiedBy'`
|
|
241
|
+
|
|
242
|
+
#### Built-in scanners
|
|
187
243
|
|
|
188
|
-
|
|
244
|
+
| Scanner | Finds spec IDs via | Deep validation |
|
|
245
|
+
|---------|-------------------|-----------------|
|
|
246
|
+
| `openapi` | operationId, path segment, schema name, `x-spec-id` | HTTP method, parameter names/types, response property names/types |
|
|
247
|
+
| `ddl` | Table name (case-insensitive, schema-prefix stripped) | Column names, column types (containment-based) |
|
|
248
|
+
| `annotation` | `@verifies`, `@implements`, `@traces` annotations | — |
|
|
189
249
|
|
|
190
|
-
|
|
250
|
+
Annotations work in any comment style (`//`, `#`, `--`, `/* */`, `<!-- -->`). Multiple IDs can be comma- or space-separated.
|
|
191
251
|
|
|
192
252
|
```typescript
|
|
193
253
|
// tests/unit/auth.test.ts
|
|
@@ -201,82 +261,93 @@ describe('User Authentication', () => { ... });
|
|
|
201
261
|
export class AuthHandler { ... }
|
|
202
262
|
```
|
|
203
263
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
#### Artifact configuration
|
|
264
|
+
#### Deep validation (optional)
|
|
207
265
|
|
|
208
|
-
|
|
266
|
+
Models can define `deepValidation` to enable Level 2/3 structural checks on matched source objects:
|
|
209
267
|
|
|
210
268
|
```typescript
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
exclude: ['src/**/*.test.ts', 'src/**/*.d.ts'],
|
|
222
|
-
contentPatterns: [/@implements\s+([\w-]+(?:[,\s]+[\w-]+)*)/],
|
|
269
|
+
class EntityModel extends Model<typeof EntitySchema> {
|
|
270
|
+
// ... schema, lintRules, etc.
|
|
271
|
+
|
|
272
|
+
protected deepValidation: DeepValidationConfig<Entity> = {
|
|
273
|
+
ddl: {
|
|
274
|
+
mapper: (spec) => ({
|
|
275
|
+
tableName: spec.tableName,
|
|
276
|
+
columns: spec.columns.map(c => ({ name: c.name, type: c.type })),
|
|
277
|
+
checkTypes: true,
|
|
278
|
+
}),
|
|
223
279
|
},
|
|
224
280
|
openapi: {
|
|
225
|
-
|
|
281
|
+
mapper: (spec) => ({
|
|
282
|
+
path: spec.apiPath,
|
|
283
|
+
method: spec.httpMethod,
|
|
284
|
+
responseProperties: spec.fields.map(f => ({ name: f.name, type: f.type })),
|
|
285
|
+
}),
|
|
226
286
|
},
|
|
227
|
-
}
|
|
228
|
-
}
|
|
287
|
+
};
|
|
288
|
+
}
|
|
229
289
|
```
|
|
230
290
|
|
|
231
|
-
|
|
291
|
+
Without `deepValidation`, speckeeper still performs existence checks for all spec IDs across all configured sources.
|
|
232
292
|
|
|
233
|
-
####
|
|
293
|
+
#### Lookup keys (when spec ID differs from external identifier)
|
|
234
294
|
|
|
235
|
-
|
|
236
|
-
|---------|------|-----------|
|
|
237
|
-
| `annotationChecker()` | Generic | Scans files for `@verifies`/`@implements`/`@traces` annotations matching spec IDs |
|
|
238
|
-
| `annotationCoverage()` | Coverage | Computes coverage from annotation scan results (no manual relations needed) |
|
|
239
|
-
| `externalOpenAPIChecker()` | Specialized | OpenAPI spec: operationId, path, schema, x-spec-id, HTTP method, parameter/response types |
|
|
240
|
-
| `externalSqlSchemaChecker()` | Specialized | SQL schema: table existence, column existence, type containment |
|
|
241
|
-
|
|
242
|
-
`annotationChecker()` is the generic checker. Specialized checkers (`externalOpenAPIChecker`, `externalSqlSchemaChecker`) extend it with source-level parsing for deeper validation. Scaffold generates the appropriate checker binding based on your artifact classes.
|
|
295
|
+
By default, the global scanner searches for each spec's `id` in external sources. When the external identifier differs — for example, entity ID `"user"` vs DDL table name `"users"` — define `lookupKeys` on the model to map per source type:
|
|
243
296
|
|
|
244
297
|
```typescript
|
|
245
|
-
|
|
246
|
-
|
|
298
|
+
class EntityModel extends Model<typeof EntitySchema> {
|
|
299
|
+
readonly id = 'entity';
|
|
300
|
+
readonly name = 'Entity';
|
|
301
|
+
readonly idPrefix = 'ENT';
|
|
302
|
+
readonly schema = EntitySchema;
|
|
303
|
+
|
|
304
|
+
protected lookupKeys: LookupKeyConfig<Entity> = {
|
|
305
|
+
ddl: (spec) => spec.tableName,
|
|
306
|
+
openapi: (spec) => spec.schemaName ?? spec.id,
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
```
|
|
247
310
|
|
|
248
|
-
|
|
249
|
-
// ... schema, lintRules, etc.
|
|
311
|
+
With this configuration, when scanning DDL sources the scanner searches for `spec.tableName` instead of `spec.id`. If a match is found, the result is mapped back to the original spec ID for reporting and deep validation.
|
|
250
312
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
313
|
+
`lookupKeys` is optional per source type — any source type not listed falls back to `spec.id`.
|
|
314
|
+
|
|
315
|
+
#### Custom scanners
|
|
316
|
+
|
|
317
|
+
For file formats not covered by the built-in scanners, provide a custom `SourceScanner` plugin:
|
|
318
|
+
|
|
319
|
+
```typescript
|
|
320
|
+
// speckeeper.config.ts
|
|
321
|
+
import { defineConfig } from 'speckeeper';
|
|
322
|
+
import type { SourceScanner } from 'speckeeper';
|
|
323
|
+
|
|
324
|
+
const protoScanner: SourceScanner = {
|
|
325
|
+
findSpecIds(content, specIds, filePath) {
|
|
326
|
+
// Parse protobuf content, find spec IDs in service/message names
|
|
327
|
+
// Return SourceMatch[] with specId, location, and optional context
|
|
328
|
+
return [];
|
|
329
|
+
},
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
export default defineConfig({
|
|
333
|
+
sources: [
|
|
334
|
+
{ type: 'proto', paths: ['proto/**/*.proto'], relation: 'implements', scanner: protoScanner },
|
|
335
|
+
// ... other sources
|
|
336
|
+
],
|
|
337
|
+
});
|
|
258
338
|
```
|
|
259
339
|
|
|
260
340
|
```bash
|
|
261
|
-
$ npx speckeeper check
|
|
341
|
+
$ npx speckeeper check --verbose
|
|
262
342
|
|
|
263
343
|
speckeeper check
|
|
264
344
|
|
|
265
345
|
Design: design/
|
|
266
|
-
Type:
|
|
267
|
-
|
|
268
|
-
Scanning artifacts...
|
|
269
|
-
test: 12 @verifies annotations in 8 files
|
|
270
|
-
typescript: 5 @implements annotations in 4 files
|
|
346
|
+
Type: all
|
|
271
347
|
|
|
272
348
|
✓ All checks passed
|
|
273
|
-
|
|
274
|
-
Coverage: Requirement (verifiedBy → test)
|
|
275
|
-
Coverage: 100% (7/7 requirements verified)
|
|
276
349
|
```
|
|
277
350
|
|
|
278
|
-
You can also implement custom checkers for any external source by defining an `ExternalChecker<T>` directly. See [Model Definition Guide](./docs/model-guide.md) for details.
|
|
279
|
-
|
|
280
351
|
## Model Levels & Traceability
|
|
281
352
|
|
|
282
353
|
speckeeper organizes models by abstraction level:
|
|
@@ -354,7 +425,7 @@ class RunbookModel extends Model<typeof RunbookSchema> {
|
|
|
354
425
|
}
|
|
355
426
|
```
|
|
356
427
|
|
|
357
|
-
Core DSL factories (`speckeeper/dsl`) include `requireField`, `arrayMinLength`, `idFormat`, `childIdFormat`, `markdownExporter`, `
|
|
428
|
+
Core DSL factories (`speckeeper/dsl`) include `requireField`, `arrayMinLength`, `idFormat`, `childIdFormat`, `markdownExporter`, `annotationCoverage`, `relationCoverage`, and `baseSpecSchema`. Global scanner utilities (`openapiScanner`, `ddlScanner`, `annotationScanner`, `createAnnotationScanner`) are also re-exported for advanced use.
|
|
358
429
|
|
|
359
430
|
## Documentation
|
|
360
431
|
|