@sumrco/cli 0.2.0 → 0.3.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/ai/modules/kontract/resources/configuration.rf.md +71 -12
- package/ai/modules/kontract/resources/generated-output.rf.md +172 -9
- package/ai/modules/kontract/resources/language-sdk-generator-extension.rf.md +62 -0
- package/ai/modules/kontract/resources/openapi-sdk-generator-research.rf.md +59 -0
- package/ai/modules/kontract/resources/overview.md +71 -18
- package/ai/modules/kontract/resources/schema-reuse.rf.md +83 -0
- package/ai/modules/kontract/resources/scope-and-splitting.rf.md +41 -0
- package/ai/modules/kontract/resources/spec-layout.rf.md +206 -0
- package/ai/modules/kontract/resources/team-members/contract-author.tm.md +11 -3
- package/ai/modules/kontract/resources/workflows/contract-change.wf.md +3 -1
- package/ai/modules/kontract/sumr.module.yaml +2 -2
- package/index.js +414 -268
- package/package.json +1 -1
|
@@ -25,10 +25,12 @@ kontract:
|
|
|
25
25
|
type: typescript-nestjs
|
|
26
26
|
zod: true
|
|
27
27
|
dtos: true
|
|
28
|
+
sdk: true
|
|
28
29
|
- name: frontend
|
|
29
30
|
output: frontend/src/shared/api
|
|
30
31
|
generator:
|
|
31
32
|
type: typescript-fetch
|
|
33
|
+
schemaMode: normalize
|
|
32
34
|
modelPropertyNaming: original
|
|
33
35
|
paramNaming: original
|
|
34
36
|
stringEnums: true
|
|
@@ -56,21 +58,27 @@ Use `--target <name>` to scope generation to one target.
|
|
|
56
58
|
|
|
57
59
|
## Generator types
|
|
58
60
|
|
|
59
|
-
| Type |
|
|
60
|
-
|
|
61
|
-
| `typescript-nestjs` |
|
|
62
|
-
| `asyncapi-nats` | NATS subjects, message schemas, server interfaces, and clients. |
|
|
63
|
-
| `typescript-fetch` | TypeScript fetch SDK. |
|
|
64
|
-
| `typescript-axios` | TypeScript axios SDK. |
|
|
65
|
-
| `typescript-angular` | Angular SDK. |
|
|
66
|
-
| `typescript-models` | Framework-neutral model-only output. |
|
|
61
|
+
| Category | Type | Outputs |
|
|
62
|
+
|---|---|---|
|
|
63
|
+
| Server/framework | `typescript-nestjs` | Zod DTOs, class-validator DTOs, models, Swagger metadata, generated `ApiDoc` decorators. |
|
|
64
|
+
| Messaging | `asyncapi-nats` | NATS subjects, message schemas, server interfaces, and clients. |
|
|
65
|
+
| Client/SDK | `typescript-fetch` | TypeScript fetch SDK. |
|
|
66
|
+
| Client/SDK | `typescript-axios` | TypeScript axios SDK. |
|
|
67
|
+
| Client/SDK | `typescript-angular` | Angular SDK. |
|
|
68
|
+
| Model | `typescript-models` | Framework-neutral model-only output. |
|
|
69
|
+
|
|
70
|
+
Kontract is structured for more generator targets over time: runtime evidence,
|
|
71
|
+
docs-ready OpenAPI artifacts, packaged API contracts, SDKs, mocks, contract
|
|
72
|
+
tests, and breaking-change reports.
|
|
67
73
|
|
|
68
74
|
## `typescript-nestjs` options
|
|
69
75
|
|
|
70
76
|
| Option | Output |
|
|
71
77
|
|---|---|
|
|
72
78
|
| `zod: true` | Emits `http/zod/*.contract.ts` with Zod schemas and `createZodDto()` classes. |
|
|
73
|
-
| `dtos: true` | Emits `http/dto/*.dto.ts
|
|
79
|
+
| `dtos: true` | Emits `http/dto/*.dto.ts`; also keeps the compatibility behavior of emitting `http/swagger.ts`. |
|
|
80
|
+
| `swagger: true` | Emits per-service `http/swagger.ts` operation metadata and one shared `shared/http/decorators/api-doc.ts` helper for NestJS controllers. |
|
|
81
|
+
| `sdk: true` | Emits a fetch-based `http/sdk/` client that reuses the target's `http/models/` and `http/enums/` types. |
|
|
74
82
|
|
|
75
83
|
`dtos` is the canonical spelling. `dto: true` is accepted as a compatibility
|
|
76
84
|
alias and is normalized internally to `dtos: true`.
|
|
@@ -78,15 +86,59 @@ alias and is normalized internally to `dtos: true`.
|
|
|
78
86
|
Framework-neutral interfaces under `http/models/` and enums under `http/enums/`
|
|
79
87
|
are emitted for OpenAPI object schemas regardless of the Zod/DTO flavor.
|
|
80
88
|
|
|
89
|
+
Use `sdk: true` on a NestJS target when a backend service should call another
|
|
90
|
+
generated HTTP API. Use a standalone `typescript-fetch`, `typescript-axios`, or
|
|
91
|
+
`typescript-angular` target when a frontend/admin app should own its SDK output
|
|
92
|
+
separately.
|
|
93
|
+
|
|
94
|
+
## OpenAPI schema mode
|
|
95
|
+
|
|
96
|
+
Standalone SDK and model targets default to `schemaMode: normalize`. In this
|
|
97
|
+
mode, Kontract accepts real-world OpenAPI specs with inline operation request or
|
|
98
|
+
response schemas, hoists those schemas into generated component names in memory,
|
|
99
|
+
and emits named TypeScript models.
|
|
100
|
+
|
|
101
|
+
Use `schemaMode: strict` when the source spec is product-owned and should fail
|
|
102
|
+
fast unless operation request/response schemas use reusable `$ref` components.
|
|
103
|
+
|
|
104
|
+
```yaml
|
|
105
|
+
generator:
|
|
106
|
+
type: typescript-fetch
|
|
107
|
+
schemaMode: normalize # default for SDK/model targets
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
```yaml
|
|
111
|
+
generator:
|
|
112
|
+
type: typescript-fetch
|
|
113
|
+
schemaMode: strict
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Kontract still recommends reusable component schemas for owned APIs because they
|
|
117
|
+
produce stable names and cleaner docs. Normalization exists so customers can
|
|
118
|
+
generate SDKs from external or imperfect OpenAPI specs without rewriting the API
|
|
119
|
+
description first.
|
|
120
|
+
|
|
121
|
+
Use generic OpenAPI metadata for docs/runtime surfaces:
|
|
122
|
+
|
|
123
|
+
```yaml
|
|
124
|
+
x-kontract-surface: public
|
|
125
|
+
security: []
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
`x-kontract-surface` values are consumer-defined strings. SUMR uses `public`,
|
|
129
|
+
`app`, `admin`, and `internal`; another product might use `partner`, `mobile`,
|
|
130
|
+
or `marketplace`.
|
|
131
|
+
|
|
81
132
|
## Common debugging cases
|
|
82
133
|
|
|
83
|
-
### `http/dto
|
|
134
|
+
### `http/dto/`, `http/swagger.ts`, or `shared/http/decorators/api-doc.ts` is missing
|
|
84
135
|
|
|
85
136
|
Check both:
|
|
86
137
|
|
|
87
138
|
1. The target config has `generator.type: typescript-nestjs` and `dtos: true`.
|
|
88
|
-
`dto: true` is also accepted, but prefer `dtos` in new config.
|
|
89
|
-
|
|
139
|
+
`dto: true` is also accepted, but prefer `dtos` in new config. If you use
|
|
140
|
+
Zod without class-validator DTOs, set `swagger: true` explicitly.
|
|
141
|
+
2. The installed `SUMR Kontract module` version includes Swagger/ApiDoc support.
|
|
90
142
|
|
|
91
143
|
If two configs generate identical output and both miss `dto/` + `swagger.ts`,
|
|
92
144
|
the likely issue is the generator version being executed, not the consumer
|
|
@@ -97,6 +149,13 @@ repo's config.
|
|
|
97
149
|
Check that the NestJS target has `zod: true` or that Zod generation has not been
|
|
98
150
|
explicitly disabled.
|
|
99
151
|
|
|
152
|
+
### `http/sdk/` is missing
|
|
153
|
+
|
|
154
|
+
Check that the NestJS target has `sdk: true`. Standalone SDK generator targets
|
|
155
|
+
emit service folders with `models.ts`, `runtime.ts`, and `api.ts`; the NestJS
|
|
156
|
+
composed SDK emits under `http/sdk/` and imports types from existing
|
|
157
|
+
`http/models/` and `http/enums/`.
|
|
158
|
+
|
|
100
159
|
### Generated files are stale
|
|
101
160
|
|
|
102
161
|
Run a scoped clean generation:
|
|
@@ -28,7 +28,13 @@ contracts/recipes/
|
|
|
28
28
|
models/
|
|
29
29
|
zod/ # when zod generation is enabled
|
|
30
30
|
dto/ # when dtos generation is enabled
|
|
31
|
-
|
|
31
|
+
sdk/ # when sdk generation is enabled
|
|
32
|
+
swagger.ts # when swagger generation is enabled
|
|
33
|
+
|
|
34
|
+
contracts/shared/
|
|
35
|
+
http/
|
|
36
|
+
decorators/
|
|
37
|
+
api-doc.ts # one shared helper when swagger generation is enabled
|
|
32
38
|
```
|
|
33
39
|
|
|
34
40
|
Shared `$ref` components keep the source spec DRY and semantically consistent.
|
|
@@ -45,6 +51,7 @@ symbol names:
|
|
|
45
51
|
```typescript
|
|
46
52
|
export * as Dtos from './dto';
|
|
47
53
|
export * as Models from './models';
|
|
54
|
+
export * as Sdk from './sdk';
|
|
48
55
|
export * as ZodContracts from './zod';
|
|
49
56
|
export * from './enums';
|
|
50
57
|
export * from './swagger';
|
|
@@ -122,29 +129,149 @@ properties include `@Expose()` so they remain compatible with class-transformer
|
|
|
122
129
|
serializers that use `excludeExtraneousValues: true`. Do not hand-write parallel
|
|
123
130
|
DTOs for schemas Kontract owns.
|
|
124
131
|
|
|
125
|
-
## Swagger operation metadata — when `
|
|
132
|
+
## Swagger operation metadata — when `swagger: true`
|
|
126
133
|
|
|
127
134
|
```typescript
|
|
128
135
|
// contracts/recipes/http/swagger.ts
|
|
129
|
-
import
|
|
136
|
+
import { RecipeDto } from './zod/recipe.contract';
|
|
130
137
|
|
|
131
138
|
export const SWG_LIST_RECIPES = {
|
|
139
|
+
operationId: 'listRecipes',
|
|
132
140
|
method: 'GET',
|
|
133
141
|
path: '/recipes',
|
|
134
|
-
|
|
142
|
+
summary: 'List recipes',
|
|
143
|
+
description: 'Returns recipes available to the caller.',
|
|
144
|
+
surface: 'public',
|
|
145
|
+
security: [],
|
|
135
146
|
responseDto: RecipeDto,
|
|
147
|
+
responseDescription: 'Recipes returned.',
|
|
136
148
|
} as const;
|
|
137
149
|
```
|
|
138
150
|
|
|
139
|
-
|
|
140
|
-
|
|
151
|
+
`swagger: true` works with either `zod: true` or `dtos: true`. Generated
|
|
152
|
+
metadata preserves operation IDs, paths, summaries, descriptions, params, query
|
|
153
|
+
params, body DTOs, response DTOs, success response status, `x-kontract-surface`,
|
|
154
|
+
and standard OpenAPI `security`.
|
|
155
|
+
|
|
156
|
+
## Generated `ApiDoc` decorator — when `swagger: true`
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
// contracts/shared/http/decorators/api-doc.ts
|
|
160
|
+
export function ApiDoc(operation: ApiDocOperation): MethodDecorator {
|
|
161
|
+
// Applies ApiOperation, ApiParam, ApiQuery, ApiBody, ApiResponse,
|
|
162
|
+
// ApiExtension('x-kontract-surface', ...), and Swagger security metadata.
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Controllers can consume `ApiDoc(SWG_*)` metadata to keep runtime Swagger output
|
|
167
|
+
aligned with the OpenAPI design contract:
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
import { SWG_LIST_RECIPES } from 'SUMR Schemas module/recipes/http';
|
|
171
|
+
import { ApiDoc } from 'SUMR Schemas module/shared/http';
|
|
172
|
+
|
|
173
|
+
@Get()
|
|
174
|
+
@ApiDoc(SWG_LIST_RECIPES)
|
|
175
|
+
async listRecipes() {
|
|
176
|
+
// controller implementation
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Generated `ApiDoc` depends only on NestJS and `@nestjs/swagger`; it does not
|
|
181
|
+
import Kontract at runtime.
|
|
182
|
+
|
|
183
|
+
## NestJS-composed SDK — when `sdk: true`
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
// contracts/recipes/http/sdk/api.ts
|
|
187
|
+
import type * as Models from '../models';
|
|
188
|
+
import type * as Enums from '../enums';
|
|
189
|
+
import type { RequestConfig } from './runtime';
|
|
190
|
+
|
|
191
|
+
export interface GetRecipeRequest {
|
|
192
|
+
recipeId: string;
|
|
193
|
+
status?: Enums.RecipeStatus;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export class RecipesFetchClient {
|
|
197
|
+
constructor(private readonly config: RequestConfig = {}) {}
|
|
198
|
+
|
|
199
|
+
async getRecipe(request: GetRecipeRequest): Promise<Models.Recipe> {
|
|
200
|
+
// fetch call generated from OpenAPI operation metadata
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
The composed SDK is for NestJS services that call another generated HTTP API.
|
|
206
|
+
It emits `http/sdk/runtime.ts`, `http/sdk/api.ts`, and a local barrel, but it
|
|
207
|
+
does not duplicate the target's models. Component schema references import from
|
|
208
|
+
`http/models/`; component enum references import from `http/enums/`; inline
|
|
209
|
+
operation enums remain local literal unions.
|
|
210
|
+
|
|
211
|
+
Use standalone `typescript-fetch`, `typescript-axios`, or `typescript-angular`
|
|
212
|
+
targets when a frontend/admin application should own a separate SDK package.
|
|
213
|
+
|
|
214
|
+
## Frontend SDKs — React and Angular apps
|
|
215
|
+
|
|
216
|
+
Standalone SDK targets are meant to remove hand-written API calls from consumer
|
|
217
|
+
apps:
|
|
218
|
+
|
|
219
|
+
- `typescript-fetch` emits a browser-compatible fetch client that can be used
|
|
220
|
+
from React, Vue, Svelte, plain TypeScript, or any runtime with `fetch`.
|
|
221
|
+
- `typescript-axios` emits an axios client for apps that already standardize on
|
|
222
|
+
axios interceptors or shared axios instances.
|
|
223
|
+
- `typescript-angular` emits an Angular service that uses `HttpClient` and
|
|
224
|
+
returns `Observable<T>`.
|
|
225
|
+
|
|
226
|
+
Example React usage with the fetch SDK:
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
import { ProductsFetchClient } from './generated/api';
|
|
230
|
+
|
|
231
|
+
const api = new ProductsFetchClient({
|
|
232
|
+
baseUrl: '/api',
|
|
233
|
+
decodeJson: (value) => value as never,
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
export async function loadProduct(id: string) {
|
|
237
|
+
return api.getProduct({ id });
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Example Angular usage:
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
import { Component } from '@angular/core';
|
|
245
|
+
import { ProductsApiService } from './generated/api';
|
|
246
|
+
|
|
247
|
+
@Component({ /* ... */ })
|
|
248
|
+
export class ProductPage {
|
|
249
|
+
constructor(private readonly api: ProductsApiService) {}
|
|
250
|
+
|
|
251
|
+
save(name: string) {
|
|
252
|
+
return this.api.createProduct({ name });
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
The generated SDK owns path interpolation, query serialization, request body
|
|
258
|
+
encoding, response typing, and generated model imports. Consumers call methods
|
|
259
|
+
instead of rewriting API URLs and `fetch`/`HttpClient` calls by hand.
|
|
141
260
|
|
|
142
261
|
## NATS contract — one `nats/index.ts` per AsyncAPI service
|
|
143
262
|
|
|
144
263
|
```typescript
|
|
145
264
|
// contracts/recipes/nats/index.ts
|
|
265
|
+
import { RecipeStatusValues } from './enums';
|
|
266
|
+
|
|
267
|
+
export * from './enums';
|
|
268
|
+
|
|
146
269
|
export namespace Recipes {
|
|
147
|
-
export const CreateRecipeMessage = z.object({
|
|
270
|
+
export const CreateRecipeMessage = z.object({
|
|
271
|
+
name: z.string(),
|
|
272
|
+
slug: z.string(),
|
|
273
|
+
status: z.enum(RecipeStatusValues),
|
|
274
|
+
});
|
|
148
275
|
export type CreateRecipeMessage = z.infer<typeof CreateRecipeMessage>;
|
|
149
276
|
export type CreateRecipeMessageInput = z.input<typeof CreateRecipeMessage>;
|
|
150
277
|
|
|
@@ -164,13 +291,49 @@ export namespace Recipes {
|
|
|
164
291
|
- Subjects live in a single `SUBJECTS` const used directly by NestJS NATS.
|
|
165
292
|
- Clients validate input with `.parse()` before sending.
|
|
166
293
|
|
|
294
|
+
## NATS enum constants — `nats/enums/` per AsyncAPI service
|
|
295
|
+
|
|
296
|
+
Named `components/schemas` entries that are string or number enums emit one
|
|
297
|
+
constant file each, mirroring `http/enums/`:
|
|
298
|
+
|
|
299
|
+
```typescript
|
|
300
|
+
// contracts/recipes/nats/enums/recipe-status.enum.ts
|
|
301
|
+
export const RecipeStatusValues = ['draft', 'published', 'archived'] as const;
|
|
302
|
+
|
|
303
|
+
export type RecipeStatus = (typeof RecipeStatusValues)[number];
|
|
304
|
+
|
|
305
|
+
export const RecipeStatusMap = {
|
|
306
|
+
Draft: 'draft',
|
|
307
|
+
Published: 'published',
|
|
308
|
+
Archived: 'archived',
|
|
309
|
+
} as const;
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
- Message schemas reference shared string enum components as
|
|
313
|
+
`z.enum(<Name>Values)` instead of inlining literals, so wire enums have one
|
|
314
|
+
importable source of truth. This also works for multi-file specs and external
|
|
315
|
+
`$ref` fragments: bundling runs with `--xOrigin`, and the generator resolves
|
|
316
|
+
dereferenced copies back to their named component.
|
|
317
|
+
- `nats/index.ts` re-exports the folder (`export * from './enums';`); import
|
|
318
|
+
constants from the service NATS barrel instead of deriving them from Zod
|
|
319
|
+
internals such as `Schema.shape.field.enum`.
|
|
320
|
+
- Anonymous inline property enums stay inline `z.enum([...])`. Promote an enum
|
|
321
|
+
to `components/schemas` when services need its constants.
|
|
322
|
+
- Number enums get constant files but stay inline literal unions in message
|
|
323
|
+
schemas (`z.enum` accepts strings only).
|
|
324
|
+
|
|
167
325
|
## Consuming in NestJS
|
|
168
326
|
|
|
169
327
|
- Use `http/zod` `${Name}Dto` classes when you want `nestjs-zod` request/response
|
|
170
328
|
validation.
|
|
171
|
-
- Use
|
|
172
|
-
|
|
329
|
+
- Use per-service `http/swagger.ts` and the shared
|
|
330
|
+
`shared/http/decorators/api-doc.ts` helper when a controller should prove
|
|
331
|
+
runtime Swagger/OpenAPI behavior from generated metadata.
|
|
332
|
+
- Use `http/dto` classes when a controller follows the class-validator DTO
|
|
333
|
+
pattern.
|
|
173
334
|
- Use `http/models` interfaces for type-only internal code.
|
|
335
|
+
- Use `http/sdk` clients when a NestJS service needs a generated HTTP client
|
|
336
|
+
without a separate frontend SDK target.
|
|
174
337
|
- Use the generated `*Client` classes and `SUBJECTS` for NATS — do not
|
|
175
338
|
hand-write subjects or message shapes.
|
|
176
339
|
- Each generated directory has a barrel `index.ts` for local aggregation only.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
---
|
|
2
|
+
category: reference
|
|
3
|
+
name: language-sdk-generator-extension
|
|
4
|
+
title: Language SDK Generator Extension
|
|
5
|
+
description: "How Kontract should add future non-TypeScript SDK generators such as Java or Rust."
|
|
6
|
+
label: Language SDK Generator Extension
|
|
7
|
+
when: Researching or implementing a new Kontract SDK generator for another language
|
|
8
|
+
order: 47
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Language SDK Generator Extension
|
|
12
|
+
|
|
13
|
+
Kontract can add Java, Rust, or other SDK generators, but only as explicit
|
|
14
|
+
generator types after research and tests prove the output is useful.
|
|
15
|
+
|
|
16
|
+
## Current support
|
|
17
|
+
|
|
18
|
+
Implemented generators:
|
|
19
|
+
|
|
20
|
+
- `typescript-nestjs`
|
|
21
|
+
- `asyncapi-nats`
|
|
22
|
+
- `typescript-fetch`
|
|
23
|
+
- `typescript-axios`
|
|
24
|
+
- `typescript-angular`
|
|
25
|
+
- `typescript-models`
|
|
26
|
+
|
|
27
|
+
Java and Rust are not implemented yet. Config such as `type: java` or
|
|
28
|
+
`type: rust` must fail until a real generator exists.
|
|
29
|
+
|
|
30
|
+
## Add a new language generator
|
|
31
|
+
|
|
32
|
+
Use this sequence:
|
|
33
|
+
|
|
34
|
+
1. Research mature public generators for the target language and record lessons
|
|
35
|
+
in `resources/`.
|
|
36
|
+
2. Add the generator type to `src/types/contracts.types.ts`.
|
|
37
|
+
3. Add config parsing and allowed options in `src/lib/sumr-yaml.ts`.
|
|
38
|
+
4. Add dependency warnings in `src/cli/dependency-check.ts` only when generated
|
|
39
|
+
output requires runtime packages in the consumer repo.
|
|
40
|
+
5. Add a generator implementation under `src/generators/openapi/<language>/` or
|
|
41
|
+
a shared SDK emitter if the language can reuse one.
|
|
42
|
+
6. Wire generation planning in `src/cli/generate-plan.ts`.
|
|
43
|
+
7. Add tests that prove config parsing, output shape, unsafe-schema failures,
|
|
44
|
+
and generated file roots.
|
|
45
|
+
8. Update `README.md`, `docs/**`, `resources/**`, and regenerate AI resources
|
|
46
|
+
with `bun run sync`.
|
|
47
|
+
|
|
48
|
+
## Test coverage required
|
|
49
|
+
|
|
50
|
+
Every generator must have at least:
|
|
51
|
+
|
|
52
|
+
- a config parsing test;
|
|
53
|
+
- a direct generation test for expected root files;
|
|
54
|
+
- an edge-case test for reserved names or language keywords;
|
|
55
|
+
- query array serialization coverage for HTTP SDKs;
|
|
56
|
+
- enum and `additionalProperties` coverage where the language supports them;
|
|
57
|
+
- a rejection test for unsupported schema features such as unsafe polymorphism;
|
|
58
|
+
- a catalog coverage test so the documented generator list and implemented list
|
|
59
|
+
stay aligned.
|
|
60
|
+
|
|
61
|
+
Do not add a generator type first and fill behavior later. Unsupported language
|
|
62
|
+
targets should stay rejected until they can generate useful, validated output.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
category: reference
|
|
3
|
+
name: openapi-sdk-generator-research
|
|
4
|
+
title: OpenAPI SDK Generator Research
|
|
5
|
+
description: "Public generator lessons Kontract applies when evolving TypeScript SDK output."
|
|
6
|
+
label: OpenAPI SDK Generator Research
|
|
7
|
+
when: Designing or reviewing Kontract SDK output, runtime helpers, config options, or schema preview work
|
|
8
|
+
order: 46
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# OpenAPI SDK Generator Research
|
|
12
|
+
|
|
13
|
+
Kontract learns from public OpenAPI generators without inheriting their runtime
|
|
14
|
+
shape, Java dependency chain, or template system.
|
|
15
|
+
|
|
16
|
+
## References reviewed
|
|
17
|
+
|
|
18
|
+
- OpenAPI Generator `typescript-fetch`:
|
|
19
|
+
<https://openapi-generator.tech/docs/generators/typescript-fetch/>
|
|
20
|
+
- OpenAPI Generator `typescript-axios`:
|
|
21
|
+
<https://openapi-generator.tech/docs/generators/typescript-axios/>
|
|
22
|
+
- OpenAPI Generator `typescript-angular`:
|
|
23
|
+
<https://openapi-generator.tech/docs/generators/typescript-angular/>
|
|
24
|
+
- OpenAPI Generator `typescript-nestjs`:
|
|
25
|
+
<https://openapi-generator.tech/docs/generators/typescript-nestjs/>
|
|
26
|
+
- OpenAPI TypeScript `openapi-fetch`:
|
|
27
|
+
<https://openapi-ts.dev/openapi-fetch/>
|
|
28
|
+
|
|
29
|
+
## Lessons for Kontract
|
|
30
|
+
|
|
31
|
+
- Keep SDK config options familiar where they are useful:
|
|
32
|
+
`useSingleRequestParameter`, `paramNaming`, `modelPropertyNaming`,
|
|
33
|
+
`enumPropertyNaming`, `stringEnums`, `enumUnknownDefaultCase`,
|
|
34
|
+
`sortParamsByRequiredFlag`, and `sortModelPropertiesByRequiredFlag`.
|
|
35
|
+
- Prefer strict, readable output over broad template compatibility. Public
|
|
36
|
+
generators support many options, but that breadth often creates heavy runtime
|
|
37
|
+
helpers and harder-to-review generated code.
|
|
38
|
+
- Treat query serialization as contract behavior. Array query params should stay
|
|
39
|
+
repeated keys unless the spec says otherwise.
|
|
40
|
+
- Keep reserved-word handling in the SDK path. Wire names such as `from`,
|
|
41
|
+
`class`, and `default` must remain valid request keys while local variables
|
|
42
|
+
stay valid TypeScript identifiers.
|
|
43
|
+
- Preserve OpenAPI `additionalProperties` semantics in models and SDK types.
|
|
44
|
+
- Fail before partial output for unsupported polymorphism rather than generating
|
|
45
|
+
missing or unsafe symbols.
|
|
46
|
+
- Keep the fetch runtime small. `openapi-fetch` is a useful benchmark for a
|
|
47
|
+
low-runtime client; Kontract should avoid pulling in a heavy client runtime
|
|
48
|
+
unless a target explicitly asks for it.
|
|
49
|
+
|
|
50
|
+
## Product decisions
|
|
51
|
+
|
|
52
|
+
- `typescript-nestjs` remains a server-contract generator. Its `sdk: true`
|
|
53
|
+
option adds a companion fetch client for NestJS consumers; it does not turn
|
|
54
|
+
the target into a standalone frontend package.
|
|
55
|
+
- `typescript-fetch`, `typescript-axios`, and `typescript-angular` remain
|
|
56
|
+
standalone SDK targets with their own generated models.
|
|
57
|
+
- Schema preview HTML needs a separate research spike because bundle size,
|
|
58
|
+
license, and static-output feasibility affect whether the feature belongs in
|
|
59
|
+
Kontract, Website, or both.
|
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: usage
|
|
3
3
|
title: Kontract Usage
|
|
4
|
-
description: "How to use the SUMR Kontract CLI to generate
|
|
4
|
+
description: "How to use the SUMR Kontract CLI to generate configured contract artifacts from OpenAPI and AsyncAPI sources. Use when adding, changing, reviewing, validating, or generating API contracts."
|
|
5
5
|
tags: [kontract, contracts, openapi, asyncapi, nestjs, codegen]
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Kontract Usage
|
|
9
9
|
|
|
10
|
-
Kontract
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
of truth so implementations do not drift from the contract.
|
|
10
|
+
Kontract generates the artifacts that keep APIs honest: validation schemas,
|
|
11
|
+
framework decorators, SDKs, docs inputs, runtime evidence, and message contracts
|
|
12
|
+
from OpenAPI and AsyncAPI sources.
|
|
14
13
|
|
|
15
14
|
## When to Use
|
|
16
15
|
|
|
17
16
|
- Adding or editing an OpenAPI (`*.openapi.yml`) or AsyncAPI (`*.asyncapi.yml`) spec.
|
|
18
|
-
-
|
|
19
|
-
|
|
17
|
+
- Adding domain/subdomain specs with the neutral `*.schema.yml` suffix when the
|
|
18
|
+
YAML marker (`openapi:` or `asyncapi:`) should decide the protocol.
|
|
19
|
+
- Reviewing newly written or updated `*.openapi.yml`, `*.asyncapi.yml`, or
|
|
20
|
+
`*.schema.yml` files against Kontract best practices before validation or
|
|
21
|
+
generation.
|
|
20
22
|
- Refactoring duplicated schema fields into shared `$ref` components.
|
|
21
23
|
- Regenerating contracts after a spec change.
|
|
22
24
|
- Validating specs before committing or in CI.
|
|
@@ -27,7 +29,7 @@ of truth so implementations do not drift from the contract.
|
|
|
27
29
|
```bash
|
|
28
30
|
sumr kontract init # activate Kontract AI guidance for this repo
|
|
29
31
|
sumr kontract validate # validate configured OpenAPI/AsyncAPI sources
|
|
30
|
-
sumr kontract generate # generate
|
|
32
|
+
sumr kontract generate # generate configured contract artifacts
|
|
31
33
|
```
|
|
32
34
|
|
|
33
35
|
Useful flags:
|
|
@@ -46,11 +48,14 @@ Useful flags:
|
|
|
46
48
|
schema/
|
|
47
49
|
recipes.openapi.yml → contracts/recipes/http/
|
|
48
50
|
recipes.asyncapi.yml → contracts/recipes/nats/index.ts
|
|
51
|
+
recipes/
|
|
52
|
+
catalog.schema.yml → contracts/recipes/http/
|
|
53
|
+
events.schema.yml → contracts/recipes/nats/index.ts
|
|
49
54
|
```
|
|
50
55
|
|
|
51
|
-
One
|
|
52
|
-
|
|
53
|
-
|
|
56
|
+
One OpenAPI or AsyncAPI source → many generated, always-consistent artifacts.
|
|
57
|
+
Generated files are outputs: never hand-edit them — change the spec and
|
|
58
|
+
regenerate.
|
|
54
59
|
|
|
55
60
|
## Configuration
|
|
56
61
|
|
|
@@ -68,7 +73,7 @@ kontract:
|
|
|
68
73
|
generator:
|
|
69
74
|
type: typescript-nestjs
|
|
70
75
|
zod: true
|
|
71
|
-
|
|
76
|
+
swagger: true
|
|
72
77
|
- name: frontend
|
|
73
78
|
output: frontend/src/shared/api
|
|
74
79
|
generator:
|
|
@@ -79,25 +84,69 @@ For `typescript-nestjs`:
|
|
|
79
84
|
|
|
80
85
|
- `zod: true` emits NestJS-Zod contracts under `http/zod/`.
|
|
81
86
|
- `dtos: true` emits class-validator DTOs under `http/dto/` plus
|
|
82
|
-
`http/swagger.ts` operation metadata.
|
|
87
|
+
compatibility `http/swagger.ts` operation metadata.
|
|
88
|
+
- `swagger: true` emits per-service `http/swagger.ts` operation metadata and one
|
|
89
|
+
shared `shared/http/decorators/api-doc.ts` helper for NestJS controllers.
|
|
90
|
+
- `sdk: true` emits a fetch-based `http/sdk/` client that reuses the same
|
|
91
|
+
`http/models/` and `http/enums/` generated for the NestJS target.
|
|
83
92
|
- `dto: true` is accepted as a compatibility alias for `dtos: true`; prefer
|
|
84
93
|
`dtos` in new config.
|
|
85
94
|
- Framework-neutral model interfaces are emitted under `http/models/`.
|
|
86
95
|
|
|
87
|
-
If a consuming repo is missing `http/
|
|
88
|
-
|
|
89
|
-
`SUMR Kontract module` version contains
|
|
96
|
+
If a consuming repo is missing `http/swagger.ts` or
|
|
97
|
+
`shared/http/decorators/api-doc.ts`, first confirm the target has
|
|
98
|
+
`swagger: true`, and that the installed `SUMR Kontract module` version contains
|
|
99
|
+
Swagger/ApiDoc support.
|
|
100
|
+
|
|
101
|
+
## Generator Catalog
|
|
102
|
+
|
|
103
|
+
### Current
|
|
104
|
+
|
|
105
|
+
| Category | Generator | Outputs |
|
|
106
|
+
|---|---|---|
|
|
107
|
+
| Server/framework | `typescript-nestjs` | Zod DTOs, class-validator DTOs, models, Swagger metadata, generated `ApiDoc`, optional SDK. |
|
|
108
|
+
| Messaging | `asyncapi-nats` | NATS subjects, message schemas, client/server interfaces. |
|
|
109
|
+
| Client/SDK | `typescript-fetch` / `typescript-axios` / `typescript-angular` | Standalone TypeScript clients with bundled models. |
|
|
110
|
+
| Model | `typescript-models` | Framework-neutral model-only output. |
|
|
111
|
+
|
|
112
|
+
### Near-term
|
|
113
|
+
|
|
114
|
+
| Category | Generator | Outputs |
|
|
115
|
+
|---|---|---|
|
|
116
|
+
| Runtime evidence | NestJS Swagger extraction | OpenAPI JSON from real Nest routes. |
|
|
117
|
+
| API package | API contracts package | Packaged OpenAPI artifacts. |
|
|
118
|
+
| Docs | docs-ready OpenAPI | Filtered public/app/admin docs inputs. |
|
|
119
|
+
| Preview | schema preview HTML | Static API reference output after Scalar/free-option research. |
|
|
120
|
+
| SDK | TypeScript SDK | Public/app/admin clients. |
|
|
121
|
+
| Quality | contract tests | Route/spec drift and response-shape tests. |
|
|
122
|
+
|
|
123
|
+
### Future
|
|
124
|
+
|
|
125
|
+
- Java, Rust, Python, Go, and Swift SDKs.
|
|
126
|
+
- Postman or Bruno collections.
|
|
127
|
+
- mock servers.
|
|
128
|
+
- breaking-change and OpenAPI diff reports.
|
|
129
|
+
- API changelog artifacts.
|
|
130
|
+
- docs snippets and examples.
|
|
90
131
|
|
|
91
132
|
## Core Rules
|
|
92
133
|
|
|
93
134
|
- Validate before you generate: `sumr kontract validate` then `sumr kontract generate`.
|
|
94
135
|
- Treat everything under the generated `contracts/` output as read-only.
|
|
95
136
|
- Keep specs the single source of truth; do not hand-write Zod/DTOs that Kontract owns.
|
|
96
|
-
- After writing or updating any `*.openapi.yml
|
|
97
|
-
the spec for duplicated concepts before
|
|
137
|
+
- After writing or updating any `*.openapi.yml`, `*.asyncapi.yml`, or
|
|
138
|
+
`*.schema.yml` file, review the spec for duplicated concepts before
|
|
139
|
+
generation.
|
|
140
|
+
- Treat `summary`, `description`, `title`, parameter descriptions, response
|
|
141
|
+
descriptions, and message descriptions as user-facing copy when they appear in
|
|
142
|
+
docs, Swagger, Scalar, generated SDKs, or AI answers. Route changed copy
|
|
143
|
+
through the repo's copywriter/microcopy standard before generation.
|
|
98
144
|
- Design specs for reuse: shared parameters, scalar constraints, error responses,
|
|
99
145
|
response envelope fragments, pagination metadata, schedule/time-slot shapes,
|
|
100
146
|
and common config objects belong in shared components referenced with `$ref`.
|
|
147
|
+
- Use compact YAML readability rules for editor navigation: section comments are
|
|
148
|
+
visual landmarks only, naming follows domain meaning, and large files should
|
|
149
|
+
split by domain/subdomain before comments become a table of contents.
|
|
101
150
|
- See the spec layout and generated output references for the exact file shapes.
|
|
102
151
|
- See the scope and splitting reference when a spec or Kontract source file
|
|
103
152
|
crosses the review thresholds, mixes domains, or needs domain/subdomain
|
|
@@ -106,6 +155,10 @@ the target has `dtos: true` (or `dto: true`) and that the installed
|
|
|
106
155
|
generated diff with many near-identical DTO/model files.
|
|
107
156
|
- See the OpenAPI generator lessons reference before changing query params,
|
|
108
157
|
inline schemas, SDK serialization, or strict TypeScript compatibility rules.
|
|
158
|
+
- See the OpenAPI SDK generator research reference before changing SDK runtime
|
|
159
|
+
shape, target defaults, model imports, or preview-docs direction.
|
|
160
|
+
- See the language SDK generator extension reference before adding Java, Rust,
|
|
161
|
+
or any non-TypeScript SDK target.
|
|
109
162
|
- Use scoped flags (`--source`, `--target`, `--specific`) before `--clean` in a
|
|
110
163
|
large consumer repo.
|
|
111
164
|
- Warm generation is manifest-driven. Kontract stores per-service manifests in
|