contract-driven-delivery 2.1.3 → 2.2.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.
@@ -0,0 +1,157 @@
1
+ # OpenAPI export
2
+
3
+ `cdd-kit openapi export` projects `contracts/api/api-contract.md` (the source of
4
+ truth) into a minimal **OpenAPI 3.1** skeleton for tooling. The markdown contract
5
+ stays authoritative; the OpenAPI document is a one-way, regenerable projection.
6
+
7
+ See `docs/adr/0001-contract-to-openapi-export.md` and
8
+ `docs/adr/0002-schema-carrying-contract-format.md` for the design rationale.
9
+
10
+ ## Usage
11
+
12
+ ```bash
13
+ cdd-kit openapi export # JSON to stdout
14
+ cdd-kit openapi export --yaml # YAML to stdout
15
+ cdd-kit openapi export --out build/openapi.json # write to a file
16
+ cdd-kit openapi export --yaml --out openapi.yaml
17
+ cdd-kit openapi export --contract path/to/api-contract.md
18
+ cdd-kit openapi export --check --out build/openapi.json # sync gate
19
+ ```
20
+
21
+ ## The sync gate: `--check`
22
+
23
+ A regenerable artifact is only safe if it is actually regenerated. `--check`
24
+ makes that mechanical instead of a habit:
25
+
26
+ ```bash
27
+ cdd-kit openapi export --check --out build/openapi.json
28
+ ```
29
+
30
+ It does **not** write. It compares the committed artifact at `--out` against what
31
+ the contract produces right now and exits:
32
+
33
+ - `0` - in sync.
34
+ - `1` - the artifact is missing, or the contract changed but the export was not
35
+ regenerated. The command prints the exact `openapi export --out ...` command
36
+ to fix it.
37
+
38
+ Wire it into CI or a pre-commit hook so a contract edit that forgets to
39
+ regenerate the export, and therefore the typed client downstream, fails the
40
+ build. `--check` honors `--yaml`, so check the same format you committed.
41
+
42
+ ## What it derives
43
+
44
+ From the endpoint table (`| method | path | auth | request schema | response schema | errors | tests |`):
45
+
46
+ | Contract column | OpenAPI output |
47
+ |---|---|
48
+ | `method` + `path` | operation under `paths`, with `:id`/`{id}` normalized to `{id}` |
49
+ | path templates | `parameters` (`in: path`, `required: true`, `type: string`) |
50
+ | `auth` | `security` (`bearerAuth`) for `required`/`admin`, optional+anonymous for `optional`, none for `none`/`public` |
51
+ | `method` | success status: `201` for `POST`, else `200` |
52
+ | `errors` | extra response entries for any explicit `4xx`/`5xx` codes listed |
53
+ | `response schema` | if it names a schema in `## Schemas`, emitted as response JSON Schema; otherwise recorded as `x-cdd-response-contract` prose |
54
+ | `request schema` | if it names a schema in `## Schemas`, emitted as request JSON Schema; otherwise `requestBody` is marked `x-cdd-unresolved: true` |
55
+
56
+ The exporter does not fabricate field-level schemas. Request/response cells that
57
+ do not resolve to a named schema remain prose. The unresolved markers are
58
+ deliberate: emitting a fake schema would be a new drift source. Add a
59
+ `## Schemas` section when a body shape should become machine-typed.
60
+
61
+ ## Schema-carrying contracts
62
+
63
+ Add optional `### Name` subsections under `## Schemas`. Existing endpoint table
64
+ cells like `CreateUser`, `User`, or `User[]` become references when a matching
65
+ schema exists.
66
+
67
+ ```markdown
68
+ ## Endpoint Requirements
69
+ | method | path | auth | request schema | response schema | errors | tests |
70
+ |---|---|---|---|---|---|---|
71
+ | POST | /api/users | admin | CreateUser | User | 400 | yes |
72
+
73
+ ## Schemas
74
+
75
+ ### CreateUser
76
+ | field | type | required | format | notes |
77
+ |---|---|---|---|---|
78
+ | email | string | yes | email | login identity |
79
+ | name | string | yes | | display name |
80
+ | role | enum(admin, member) | no | | |
81
+
82
+ ### User
83
+ | field | type | required | notes |
84
+ |---|---|---|---|
85
+ | id | string | yes | |
86
+ | email | string | yes | |
87
+ ```
88
+
89
+ Field-table types are intentionally small and closed:
90
+
91
+ | Type cell | Output |
92
+ |---|---|
93
+ | `string`, `integer`, `number`, `boolean` | primitive JSON Schema |
94
+ | `OtherSchema` | `$ref` to another named schema |
95
+ | `OtherSchema[]` or `string[]` | array wrapper |
96
+ | `enum(active, disabled)` | string enum |
97
+
98
+ `required: yes` adds the field to JSON Schema `required`. `notes` becomes
99
+ `description`. An optional `format` column is emitted as JSON Schema `format`
100
+ and may be enforced by downstream tooling.
101
+
102
+ For complex bodies, use a raw Tier B escape hatch:
103
+
104
+ ````markdown
105
+ ### Event
106
+ ```json-schema
107
+ {
108
+ "type": "object",
109
+ "oneOf": [
110
+ { "required": ["createdAt"] },
111
+ { "required": ["deletedAt"] }
112
+ ]
113
+ }
114
+ ```
115
+ ````
116
+
117
+ The exporter fails instead of weakening types when a schema is ambiguous:
118
+ duplicate schema names, a section that mixes a field table and `json-schema`
119
+ block, invalid JSON, or an unknown field type all exit non-zero.
120
+
121
+ ## Wiring a typed client in a consumer repo
122
+
123
+ The kit produces the OpenAPI seam; you generate the client with an existing,
124
+ well-maintained generator in your own CI. When a `package.json` is present,
125
+ `cdd-kit init` scaffolds this for you as two editable npm scripts:
126
+
127
+ ```jsonc
128
+ "scripts": {
129
+ // regenerate the OpenAPI artifact + the typed client
130
+ "contract:client": "cdd-kit openapi export --out contracts/api/openapi.json && npx --yes openapi-typescript contracts/api/openapi.json -o src/api/types.ts",
131
+ // the sync gate - fails if the artifact drifted from the contract
132
+ "contract:client:check": "cdd-kit openapi export --check --out contracts/api/openapi.json"
133
+ }
134
+ ```
135
+
136
+ These are a starting point, not a hard dependency: the generator
137
+ (`openapi-typescript`) and the output path are yours to change. The kit owns the
138
+ generic contract-to-OpenAPI half (`openapi export` / `--check`); the
139
+ stack-specific codegen stays in your repo, which is why init writes an editable
140
+ script rather than hard-coding a tool. Run `npm run contract:client:check` in CI
141
+ as the gate.
142
+
143
+ Doing it by hand instead, for a TypeScript frontend:
144
+
145
+ ```bash
146
+ # 1. Export the contract to OpenAPI (committed or generated in CI)
147
+ cdd-kit openapi export --yaml --out openapi.yaml
148
+
149
+ # 2. Generate types with openapi-typescript (or orval / openapi-generator)
150
+ npx openapi-typescript openapi.yaml -o src/api/schema.d.ts
151
+ ```
152
+
153
+ Now frontend calls typed against `schema.d.ts` make a divergent path, method, or
154
+ schema-resolved body shape a compile error. Run both generated clients and
155
+ `validate_api_conformance.py`: conformance stays the universal floor for code
156
+ that cannot be regenerated, generated types are the stronger path where the
157
+ stack allows it.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "contract-driven-delivery",
3
- "version": "2.1.3",
3
+ "version": "2.2.0",
4
4
  "description": "Contract-driven delivery kit for AI coding agents with deterministic context indexes, manifest-backed read-scope governance, and orchestrated contracts-first delivery.",
5
5
  "keywords": [
6
6
  "contract-driven",