swaggerizer 0.0.0-stage → 1.0.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.
Files changed (64) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +359 -2
  3. package/dist/admin.d.ts +35 -0
  4. package/dist/admin.d.ts.map +1 -0
  5. package/dist/catalogue.d.ts +22 -0
  6. package/dist/catalogue.d.ts.map +1 -0
  7. package/dist/chunks/scenario-DXI-TbMN.mjs +371 -0
  8. package/dist/chunks/scenario-DXI-TbMN.mjs.map +1 -0
  9. package/dist/chunks/scenario-_ZBu5fJX.js +391 -0
  10. package/dist/chunks/scenario-_ZBu5fJX.js.map +1 -0
  11. package/dist/cli.d.ts +2 -0
  12. package/dist/cli.d.ts.map +1 -0
  13. package/dist/cli.js +2977 -0
  14. package/dist/cli.js.map +1 -0
  15. package/dist/config.d.ts +100 -0
  16. package/dist/config.d.ts.map +1 -0
  17. package/dist/config.js +129 -0
  18. package/dist/config.js.map +1 -0
  19. package/dist/config.mjs +120 -0
  20. package/dist/config.mjs.map +1 -0
  21. package/dist/contract.d.ts +60 -0
  22. package/dist/contract.d.ts.map +1 -0
  23. package/dist/debug-menu.d.ts +20 -0
  24. package/dist/debug-menu.d.ts.map +1 -0
  25. package/dist/debug-menu.js +238 -0
  26. package/dist/debug-menu.js.map +1 -0
  27. package/dist/debug-menu.mjs +236 -0
  28. package/dist/debug-menu.mjs.map +1 -0
  29. package/dist/generate.d.ts +74 -0
  30. package/dist/generate.d.ts.map +1 -0
  31. package/dist/index.d.ts +14 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +2397 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/index.mjs +2343 -0
  36. package/dist/index.mjs.map +1 -0
  37. package/dist/load-config.d.ts +15 -0
  38. package/dist/load-config.d.ts.map +1 -0
  39. package/dist/msw.d.ts +48 -0
  40. package/dist/msw.d.ts.map +1 -0
  41. package/dist/msw.js +324 -0
  42. package/dist/msw.js.map +1 -0
  43. package/dist/msw.mjs +317 -0
  44. package/dist/msw.mjs.map +1 -0
  45. package/dist/paths.d.ts +14 -0
  46. package/dist/paths.d.ts.map +1 -0
  47. package/dist/pull.d.ts +31 -0
  48. package/dist/pull.d.ts.map +1 -0
  49. package/dist/scenario.d.ts +86 -0
  50. package/dist/scenario.d.ts.map +1 -0
  51. package/dist/schema.d.ts +13 -0
  52. package/dist/schema.d.ts.map +1 -0
  53. package/dist/server.d.ts +83 -0
  54. package/dist/server.d.ts.map +1 -0
  55. package/dist/spec.d.ts +36 -0
  56. package/dist/spec.d.ts.map +1 -0
  57. package/dist/types.d.ts +175 -0
  58. package/dist/types.d.ts.map +1 -0
  59. package/dist/ui/app.css +1119 -0
  60. package/dist/ui/app.js +806 -0
  61. package/dist/ui/index.html +77 -0
  62. package/dist/ui-assets.d.ts +9 -0
  63. package/dist/ui-assets.d.ts.map +1 -0
  64. package/package.json +99 -4
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 James Bolton
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,360 @@
1
- # Temporary Holding Version
1
+ # swaggerizer
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ It's a strategizer for OpenAPI and Swagger, the sibling of [ramlizer](https://github.com/jayjaybeeuk/ramlizer).
4
+
5
+ ## What?
6
+
7
+ Swaggerizer takes your OpenAPI 3.x or Swagger 2.0 documents and gives you:
8
+
9
+ 1. **A live mock API with an admin page.** Every response and named example in the spec becomes a scenario you can switch to while the mock is running. You can switch from the admin page, with a GET link, with an HTML form post or with JSON.
10
+ 2. **Generated code from [orval](https://orval.dev).** You get a typed client, models and [MSW](https://mswjs.io) handlers. Swaggerizer wraps those handlers so they follow the same scenario choices: they serve a named example or a custom response, fall back to orval's faker data, or pass through to the real API.
11
+ 3. **A debug menu for your app.** It's a small floating panel you mount in the browser to switch the MSW mocks while you click around. It works on its own, or synced with a running admin page.
12
+ 4. **Contract-change warnings before you regenerate.** Swaggerizer saves a snapshot of the contract next to the generated code. It then tells you about new APIs, deleted APIs and breaking changes before `generate` overwrites anything.
13
+
14
+ ## Why?
15
+
16
+ Your UI behaves differently depending on what the API returns: an empty list, a validation error, a 500 or a slow response. You need to see every one of those states without changing backend data or restarting a mock server.
17
+
18
+ You also regenerate client code whenever the backend team updates the spec. Swaggerizer warns you that an endpoint was removed, a field became required or an `operationId` was renamed (which renames your generated function) *before* the change lands in your code.
19
+
20
+ ## Quick start
21
+
22
+ ```bash
23
+ npm install --save-dev swaggerizer msw @faker-js/faker
24
+
25
+ # 1. Serve live mocks with an admin page
26
+ npx swaggerizer serve --folder ./specs --port 8080
27
+ # → mock API on http://localhost:8080, admin page on http://localhost:8080/swaggerizer/
28
+
29
+ # 2. Generate a typed client, models and MSW mocks (warns about contract changes first)
30
+ npx swaggerizer generate --folder ./specs --output ./src/api
31
+
32
+ # 3. In CI: fail when the specs drift from the generated code in a breaking way
33
+ npx swaggerizer diff --folder ./specs --output ./src/api --fail-on breaking
34
+ ```
35
+
36
+ Specs don't have to be files: anywhere a spec is expected you can give a URL, such as a running backend's api-docs endpoint.
37
+
38
+ ```bash
39
+ npx swaggerizer serve --folder http://localhost:8081/v3/api-docs
40
+ npx swaggerizer generate --folder https://api.example.com/swagger.json -H "Authorization: Bearer $TOKEN" --output ./src/api
41
+ ```
42
+
43
+ You can try it on the bundled examples:
44
+
45
+ ```bash
46
+ npm install && npm run build
47
+ npm run example # serve ./example_specs, compared against ./example_generated
48
+ npm run example:diff # what would change if ./example_specs_next were adopted
49
+ npm run example:browser # orval client + MSW + debug menu demo on http://localhost:5173
50
+ ```
51
+
52
+ ## Scenarios
53
+
54
+ Each operation (method + route) answers in one of three **modes**:
55
+
56
+ | Mode | What is served |
57
+ | --- | --- |
58
+ | `mock` (default) | A response declared in the spec. Pick a status code and a named example. A response without examples gets a body generated from its schema. |
59
+ | `custom` | A status, body and content type you type in yourself. Bodies that aren't valid JSON are sent as `text/plain`. |
60
+ | `proxy` | The real API. The server forwards to `--proxy` or the spec's `servers` URL; MSW calls `passthrough()`. |
61
+
62
+ Any mode can add an artificial **delay** in milliseconds.
63
+
64
+ #### Named examples
65
+
66
+ OpenAPI 3 `examples` show up as switchable scenarios:
67
+
68
+ ```yaml
69
+ responses:
70
+ "400":
71
+ description: Validation failed
72
+ content:
73
+ application/json:
74
+ schema: { $ref: "#/components/schemas/ValidationError" }
75
+ examples:
76
+ missingName:
77
+ value: { errorList: [{ fieldName: name, message: Name is required }] }
78
+ badSpecies:
79
+ value: { errorList: [{ fieldName: species, message: We do not stock dragons }] }
80
+ ```
81
+
82
+ A single `example`, a schema-level `example`/`examples`, or a Swagger 2 `examples` entry appears as one scenario called `example`. A response with a body but no example is called `generated`, and one without a body is called `empty`.
83
+
84
+ #### Switching from anywhere
85
+
86
+ ```bash
87
+ # A GET link: paste it in a browser or call it from a test
88
+ curl 'http://localhost:8080/swaggerizer/api/select?method=post&route=/pets&code=400&example=badSpecies'
89
+
90
+ # A plain HTML form post (add redirect=/somewhere to be sent back afterwards)
91
+ curl -X POST http://localhost:8080/swaggerizer/api/select -d 'method=get&route=/pets&example=emptyList'
92
+
93
+ # JSON: one selection or an array, applied all-or-nothing
94
+ curl -X POST http://localhost:8080/swaggerizer/api/select -H 'Content-Type: application/json' -d '[
95
+ { "method": "get", "route": "/pets", "code": "500", "delay": 1500 },
96
+ { "method": "post", "route": "/login", "custom": { "status": 423, "body": { "locked": true } } },
97
+ { "method": "get", "route": "/pets/{petId}", "mode": "proxy" }
98
+ ]'
99
+ ```
100
+
101
+ `example` can be given without `code`: Swaggerizer finds the response that declares it. With curl, pass `-g` when a GET link contains a templated route such as `route=/pets/{petId}`, or curl treats the braces as a glob. The ramlizer payload (`{ route, method, nextResponseCode, nextExampleName }` posted to `/swaggerizer`) works too.
102
+
103
+ #### One request at a time
104
+
105
+ Send `X-Swaggerizer-Scenario` with a request to override the choice for that request only. This is handy for parallel tests:
106
+
107
+ ```
108
+ X-Swaggerizer-Scenario: 404 → the 404 response
109
+ X-Swaggerizer-Scenario: 400:badSpecies → that example
110
+ X-Swaggerizer-Scenario: emptyList → whichever response declares it
111
+ X-Swaggerizer-Scenario: proxy → the real API
112
+ ```
113
+
114
+ ## The admin page
115
+
116
+ Open **http://localhost:8080/swaggerizer/** while `serve` is running:
117
+
118
+ - **Switch responses.** Every response code and named example appears as a radio button. Use the **Spec response / Custom / Real API** buttons to change mode, and the delay box to add latency.
119
+ - **Custom responses.** Edit the status, content type and body. The editor starts from whatever is currently served.
120
+ - **Preview & try.** See the payload that will be served and fire a real request at the mock.
121
+ - **Copy link / Copy curl.** Copy the GET link or curl command that reproduces the current choice.
122
+ - **Contract changes.** A banner lists breaking changes, warnings, new and removed operations whenever the specs differ from the generated code (`--generated`) or changed since they were last loaded.
123
+ - **Specs.** Turn whole files on or off. When two enabled specs declare the same route, the first one wins and the other is marked *shadowed*.
124
+ - **Reload specs.** Re-read the files from disk, or start with `--watch` to reload automatically.
125
+ - **Live requests.** A log of calls to the mock, including requests answered by MSW in a browser or test (tagged `msw`).
126
+
127
+ The admin page and API have no authentication, so only run Swaggerizer on networks you trust.
128
+
129
+ ## Generated code
130
+
131
+ ```bash
132
+ npx swaggerizer generate --folder ./specs --output ./src/api --client fetch
133
+ ```
134
+
135
+ For each spec (`petstore.yaml` → `src/api/petstore/`), orval writes:
136
+
137
+ | File | What it is |
138
+ | --- | --- |
139
+ | `petstore.ts` | The typed client (`listPets()`, `createPet()`…). `--client` accepts any orval client: `fetch`, `axios`, `react-query`, `swr`, `vue-query`, `angular`… |
140
+ | `model/` | TypeScript types for every schema |
141
+ | `petstore.msw.ts`, `petstore.faker.ts` | orval's MSW handlers and faker data |
142
+
143
+ Swaggerizer adds:
144
+
145
+ | File | What it is |
146
+ | --- | --- |
147
+ | `swaggerizer.ts` | The manifest (operations, responses, examples) and `createPetstoreMocks()` |
148
+ | `swaggerizer.contract.json` | The contract snapshot used for change detection. Commit it. |
149
+ | `../index.ts` | `createMocks()` for every spec, and each client as a namespace (`petstore.listPets()`) |
150
+
151
+ #### Using the mocks
152
+
153
+ ```ts
154
+ // browser
155
+ import { setupWorker } from "msw/browser";
156
+ import { mountDebugMenu } from "swaggerizer/debug-menu";
157
+ import { createMocks } from "./api";
158
+
159
+ const mocks = createMocks(); // choices kept in localStorage
160
+ await setupWorker(...mocks.handlers).start();
161
+ mountDebugMenu(mocks); // floating "API mocks" button, or Alt+Shift+S
162
+ ```
163
+
164
+ ```ts
165
+ // tests (vitest / jest / node:test)
166
+ import { setupServer } from "msw/node";
167
+ import { createMocks } from "./api";
168
+
169
+ const mocks = createMocks({ storageKey: false });
170
+ const server = setupServer(...mocks.handlers);
171
+
172
+ test("shows an error banner when the pet list fails", async () => {
173
+ await mocks.select({ method: "get", route: "/pets", code: "500" });
174
+ // …render and assert
175
+ });
176
+ ```
177
+
178
+ Pass `server: "http://localhost:8080/swaggerizer"` to `createMocks()` to follow a running admin page instead of local state. Choices made in the admin page apply to your browser mocks, choices made in the debug menu are sent back, and every request MSW answers shows up in the admin page's live log.
179
+
180
+ When the selected response is `generated`, the request goes to orval's faker handler for that operation, so you get realistic random data. Named examples, error codes and custom responses come from Swaggerizer. Proxy mode lets the request through to the real API.
181
+
182
+ `swaggerizer serve --state-file .swaggerizer/state.json` saves the choices you make in the admin page. `swaggerizer generate --state-file .swaggerizer/state.json` builds them into the generated mocks as their defaults.
183
+
184
+ ## Config file
185
+
186
+ For more than a couple of specs, put the settings in `swaggerizer.config.ts` (or `.mts`, `.js`, `.mjs`, `.json`) next to your `package.json`. `serve`, `generate`, `diff` and `pull` pick it up automatically, or pass `--config <file>`. Command-line flags win over the file.
187
+
188
+ ```ts
189
+ import { defineConfig } from "swaggerizer/config";
190
+ import { rewardsPopulated, rewardsEmpty } from "@acme/data-access/rewards/mock-data";
191
+
192
+ export default defineConfig({
193
+ port: 3000,
194
+ delay: 2000, // default latency for every operation
195
+ fallback: "http://localhost:3001", // requests no spec declares go here (e.g. a legacy mock server)
196
+ stateFile: ".swaggerizer/state.json",
197
+ client: "axios-functions", // orval client for every spec
198
+ specs: {
199
+ "online-banking-bff": {
200
+ source: "https://apidocs.example.com/specs/online-banking-bff", // `swaggerizer pull` saves it to `file`
201
+ file: "specs/online-banking-bff.json",
202
+ aliases: { "/bff/v4/": "/v4/" }, // the same API reached under another prefix
203
+ output: "libs/data-access/bff/src/lib/generated", // client, models, contract snapshot
204
+ mocksOutput: "libs/data-access/bff/src/lib/mock-service/generated", // MSW, faker and the Swaggerizer wrapper
205
+ orval: { override: { mutator: { path: "tools/api/request.ts", name: "request" } } },
206
+ scenarios: {
207
+ getBusinessRewards: {
208
+ populated: { status: 200, body: rewardsPopulated },
209
+ empty: { status: 200, body: rewardsEmpty, summary: "Nothing earned yet" },
210
+ limitExceeded: { status: 422, body: { code: "ACCOUNT_LIMIT_EXCEEDED" } }
211
+ }
212
+ },
213
+ handlers: {
214
+ searchTransactions: ({ body, json }) => (body?.cursor ? json(page2) : json(page1)),
215
+ createExpense: ({ params, body, store, json }) => {
216
+ store.set(params.businessId, [...((store.get(params.businessId) as unknown[]) ?? []), body]);
217
+ return json(body, 201);
218
+ }
219
+ }
220
+ }
221
+ }
222
+ });
223
+ ```
224
+
225
+ Imports in the config file are resolved with the `paths` from `tsconfig.base.json` or `tsconfig.json`, so monorepo aliases work. Set `tsconfig` in the config to use a different file. Relative paths in the config are relative to the config file.
226
+
227
+ #### Scenario overlays
228
+
229
+ Many specs, such as those generated by springdoc, carry no examples at all. `scenarios` adds named scenarios to an operation from code, so fixtures you already keep for tests become switchable responses in the admin page, the debug menu and the generated MSW mocks. Keys are `operationId`s or `"GET /route"`.
230
+
231
+ - The first overlay for a success code becomes the operation's default response.
232
+ - A status the spec doesn't declare (the `422` above) is added as a new response.
233
+ - The body generated from the schema stays available as `generated`.
234
+ - Keys that match no operation are reported as warnings on the spec.
235
+
236
+ #### Custom handlers
237
+
238
+ `handlers` cover behaviour a fixed response can't: paging on a cursor, data that depends on a path parameter, or a journey where a POST is visible in the next GET. A handler receives `{ operation, method, path, params, query, headers, body, store }`. It returns `json(body, status?)` or `empty(status?)`, or `undefined` to fall back to the normal response.
239
+
240
+ - `store` is a `Map` shared by every handler. **Reset all** clears it.
241
+ - Handlers only run while the operation serves its default scenario. Once you switch it in the admin page, the debug menu or with the `X-Swaggerizer-Scenario` header, the switch wins.
242
+ - To use the same handlers in the browser or in tests, pass them to the MSW runtime: `createMocks({ handlers: config.specs["online-banking-bff"].handlers })`.
243
+
244
+ #### Aliases
245
+
246
+ When a gateway exposes an API under more than one prefix, list each extra prefix with its canonical spelling. Requests to either prefix reach the same operation, in the mock server and in the generated MSW handlers.
247
+
248
+ #### Hosted specs
249
+
250
+ A spec can live at a URL instead of in a file: give `source` and leave out `file` (or pass the URL to `--folder`). Swaggerizer fetches it on every start, reload and `generate`. JSON or YAML both work, including from extension-less paths such as springdoc's `/v3/api-docs`, and so do `localhost` and internal hosts. Relative `$ref`s on the same host are fetched with the same headers.
251
+
252
+ ```ts
253
+ specs: {
254
+ orders: {
255
+ source: "http://localhost:8081/v3/api-docs",
256
+ headers: { Authorization: `Bearer ${process.env.API_DOCS_TOKEN}` } // or top-level `headers` for every spec
257
+ }
258
+ }
259
+ ```
260
+
261
+ - On the command line, `-H 'Name: value'` adds a header to every spec URL (`serve`, `generate`, `diff` and `pull`).
262
+ - `generate` hands orval the copy Swaggerizer fetched, so orval never downloads the spec itself.
263
+ - For safety, a `$ref` that points to a *different* host on your network (`localhost`, private addresses) is still refused. Bundle such specs first, or pull them into a file.
264
+ - Keep tokens in environment variables, not in the config file.
265
+
266
+ To review spec changes in pull requests, commit a copy instead: set both `source` and `file`, and run `swaggerizer pull`.
267
+
268
+ #### Pulling specs
269
+
270
+ `swaggerizer pull` downloads every spec that has a `source`, saves it to `file` (JSON is re-indented so diffs stay readable), and prints what changed in the contract. If the downloaded document doesn't parse, the existing file is kept. Pass spec ids to pull only some, and `--header 'Authorization: Bearer …'` (or the config's `headers`) for a private catalogue. A spec with a `source` but no local `file` is loaded straight from the URL.
271
+
272
+ ## Contract-change warnings
273
+
274
+ `generate` compares every spec with the `swaggerizer.contract.json` saved last time and prints a report first:
275
+
276
+ ```
277
+ Petstore (1.0.0 → 1.1.0)
278
+ 9 breaking, 4 warning(s), 10 info · 2 new, 1 removed, 3 changed operation(s)
279
+ ✖ BREAKING DELETE /pets/{petId} — Operation removed
280
+ ✖ BREAKING GET /pets — Required parameter added [query parameter 'ownerId']
281
+ ✖ BREAKING GET /pets/{petId} — operationId changed from 'showPetById' to 'getPet' — generated function names will change
282
+ ✖ BREAKING POST /pets — Required property 'ownerId' added [request body.ownerId]
283
+ ✖ BREAKING GET /pets — Type changed from integer to string [response 500 body.code]
284
+ ▲ WARNING GET /pets — Enum value(s) added: "rabbit" [response 200 body[].species]
285
+ ● info GET /owners — New operation
286
+ ```
287
+
288
+ If any change reaches `--fail-on` (default `breaking`), `generate` asks before writing anything. Pass `--yes` to accept the changes. In a non-interactive shell it stops with exit code 1. `swaggerizer diff` prints the same report without writing anything, and `--json` gives machine-readable output.
289
+
290
+ What is checked, judged from the point of view of code using the generated client:
291
+
292
+ | Change | Severity |
293
+ | --- | --- |
294
+ | Operation removed, spec file removed | breaking |
295
+ | `operationId` renamed (generated function renamed) | breaking |
296
+ | Required parameter or request property added; optional one became required | breaking |
297
+ | Response property removed, became optional or became nullable | breaking |
298
+ | Type changed; enum value removed from a request; success response or content type removed | breaking |
299
+ | Response enum value added (exhaustive `switch` statements); success response added; format changed; operation deprecated; error response removed | warning |
300
+ | New operation; optional parameter or property added; error response added | info |
301
+
302
+ Renaming a path parameter (`/pets/{id}` → `/pets/{petId}`) isn't reported, because the generated call doesn't change.
303
+
304
+ ## CLI
305
+
306
+ ```
307
+ swaggerizer serve [--config <file>] [--folder <files|dirs|urls…>] [-H 'Name: value'] [--port 8080] [--host] [--endpoint swaggerizer]
308
+ [--no-ui] [--proxy <url>] [--delay <ms>] [--fallback <url>] [--state-file <file>]
309
+ [--generated <dir>] [--watch] [--silent]
310
+ swaggerizer generate [--config <file>] [--folder …] [-H 'Name: value'] [--output src/api] [--client fetch] [--base-url <url>]
311
+ [--no-mock] [--state-file <file>] [--fail-on breaking|warning|any|none] [--yes]
312
+ [--runtime-import swaggerizer/msw] [--verbose]
313
+ swaggerizer diff [--config <file>] [--folder …] [-H 'Name: value'] [--output src/api] [--fail-on …] [--json] [--verbose]
314
+ swaggerizer pull [ids…] [--config <file>] [-H 'Name: value'] [--verbose]
315
+ ```
316
+
317
+ `serve` is the default command, so `swaggerizer --folder ./specs` works like `ramlizer --folder`. A folder is scanned for `.yaml`, `.yml` and `.json` files that contain an `openapi` or `swagger` key. `--folder` also accepts URLs (see [Hosted specs](#hosted-specs)), and it's optional when a config file declares the specs.
318
+
319
+ ## Admin API
320
+
321
+ All routes live under `/swaggerizer` (change the path with `--endpoint`).
322
+
323
+ | Method | Path | Body / query | Purpose |
324
+ | --- | --- | --- | --- |
325
+ | `GET` | `/api/state` | – | Specs, operations with every response and example, current choices and contract changes |
326
+ | `GET` `POST` | `/api/select` | `method`, `route`, `code?`, `example?`, `mode?`, `delay?`, `custom?` (or flat `status`/`body`/`contentType`). The body can be JSON, an array or a form | Switch one or more operations. Unknown codes or examples return `400` |
327
+ | `GET` `POST` | `/api/reset` | `{}` or `{ method, route }` | Restore defaults |
328
+ | `POST` | `/api/spec` | `{ id, enabled }` | Turn a spec on or off |
329
+ | `POST` | `/api/reload` | – | Re-read specs from disk |
330
+ | `GET` | `/api/changes` | – | Contract-change reports |
331
+ | `GET` | `/api/log?since=<id>` | – | Requests received after `id` |
332
+ | `POST` | `/api/log` | entry or array | Lets MSW report requests it answered |
333
+ | `GET` | `/api/openapi/<id>.json` | – | The spec as OpenAPI 3 JSON (Swagger 2 is converted), e.g. as an orval input |
334
+ | `POST` | `/swaggerizer` | ramlizer body | ramlizer-compatible switch |
335
+
336
+ ## Programmatic use
337
+
338
+ ```ts
339
+ import { createSwaggerizer, generate, checkChanges, formatReport } from "swaggerizer";
340
+
341
+ const sw = createSwaggerizer({ stateFile: ".swaggerizer/state.json" });
342
+ await sw.load("./specs");
343
+ await sw.listen(8080);
344
+ sw.select({ method: "get", route: "/pets", code: "500" });
345
+
346
+ // or mount sw.handler in Express / Connect alongside your dev server
347
+ app.use(sw.handler);
348
+
349
+ const { reports } = await checkChanges("./specs", "./src/api");
350
+ reports.forEach(r => console.log(formatReport(r)));
351
+ ```
352
+
353
+ ## Spec support
354
+
355
+ - OpenAPI 3.0 and 3.1, and Swagger 2.0 (converted with swagger2openapi).
356
+ - Spec files or spec URLs (JSON or YAML), with `localhost`/internal hosts and auth headers.
357
+ - Internal and external `$ref`s, including recursive schemas.
358
+ - `allOf`, `oneOf` and `anyOf`, nullable types (3.0 `nullable` and 3.1 type arrays), enums, `const`, formats, and `default` / `2XX` responses.
359
+ - Requests are matched with or without the base path from `servers` and through any configured aliases. Static routes beat templated ones (`/users/me` before `/users/{id}`).
360
+ - Wildcard media types such as springdoc's `*/*` are served as `application/json`.
@@ -0,0 +1,35 @@
1
+ import type http from "node:http";
2
+ import type { OperationState, RequestLogEntry, ScenarioSelection, SpecSummary, SwaggerizerState } from "./types";
3
+ export interface AdminContext {
4
+ basePath: string;
5
+ ui: boolean;
6
+ getState: () => SwaggerizerState;
7
+ select: (selection: ScenarioSelection | ScenarioSelection[]) => OperationState[];
8
+ reset: (filter?: {
9
+ method?: string;
10
+ route?: string;
11
+ }) => number;
12
+ setSpecEnabled: (id: string, enabled: boolean) => SpecSummary;
13
+ reload: () => Promise<SwaggerizerState>;
14
+ getRequestLog: (sinceId?: number) => RequestLogEntry[];
15
+ recordRequest: (entry: Omit<RequestLogEntry, "id" | "time">) => RequestLogEntry;
16
+ getSpecDocument: (id: string) => any;
17
+ }
18
+ export interface AdminRequest {
19
+ method: string;
20
+ /** Path below the admin base, always starting with "/". */
21
+ path: string;
22
+ query: URLSearchParams;
23
+ body: any;
24
+ contentType?: string;
25
+ }
26
+ /**
27
+ * Turns a flat record (query string, form post or JSON) into a selection. Accepts the ramlizer
28
+ * names (`nextResponseCode`, `nextExampleName`) and flat custom fields (`status`, `body`, `contentType`).
29
+ */
30
+ export declare function toSelection(raw: Record<string, any>): ScenarioSelection;
31
+ export declare function registerAdmin(ctx: AdminContext): {
32
+ handle: (req: AdminRequest, res: http.ServerResponse) => Promise<boolean>;
33
+ uiAvailable: boolean;
34
+ };
35
+ //# sourceMappingURL=admin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin.d.ts","sourceRoot":"","sources":["../src/admin.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,OAAO,KAAK,EACV,cAAc,EACd,eAAe,EACf,iBAAiB,EACjB,WAAW,EACX,gBAAgB,EACjB,MAAM,SAAS,CAAC;AAEjB,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,EAAE,EAAE,OAAO,CAAC;IACZ,QAAQ,EAAE,MAAM,gBAAgB,CAAC;IACjC,MAAM,EAAE,CAAC,SAAS,EAAE,iBAAiB,GAAG,iBAAiB,EAAE,KAAK,cAAc,EAAE,CAAC;IACjF,KAAK,EAAE,CAAC,MAAM,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,MAAM,CAAC;IAChE,cAAc,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,WAAW,CAAC;IAC9D,MAAM,EAAE,MAAM,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACxC,aAAa,EAAE,CAAC,OAAO,CAAC,EAAE,MAAM,KAAK,eAAe,EAAE,CAAC;IACvD,aAAa,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,eAAe,EAAE,IAAI,GAAG,MAAM,CAAC,KAAK,eAAe,CAAC;IAChF,eAAe,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,GAAG,CAAC;CACtC;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,eAAe,CAAC;IACvB,IAAI,EAAE,GAAG,CAAC;IACV,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAaD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,iBAAiB,CAsBvE;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,YAAY;kBASlB,YAAY,OAAO,IAAI,CAAC,cAAc,KAAG,OAAO,CAAC,OAAO,CAAC;;EAsHrF"}
@@ -0,0 +1,22 @@
1
+ import { type Json } from "./schema";
2
+ import { type CatalogueExample, type CatalogueOperation, type CatalogueResponse } from "./types";
3
+ /** HTTP status to send for a response key: "404" → 404, "2XX" → 200, "default" → 500. */
4
+ export declare function statusForCode(code: string): number;
5
+ export declare function extractExamples(media: Json, doc: Json): CatalogueExample[];
6
+ /**
7
+ * Media types as they are served. springdoc writes `*\/*` for JSON responses; serving that as a
8
+ * Content-Type confuses clients, so wildcards become `application/json`.
9
+ */
10
+ export declare function normaliseMediaType(type: string): string;
11
+ export declare function buildResponse(code: string, raw: Json, doc: Json): CatalogueResponse;
12
+ export declare function defaultVariant(responses: CatalogueResponse[]): {
13
+ code: string;
14
+ example: string;
15
+ };
16
+ export declare function firstVariantName(response: CatalogueResponse): string;
17
+ export declare function buildCatalogue(doc: Json, specId: string): CatalogueOperation[];
18
+ /** Path prefixes from `servers`, e.g. `https://api.example.com/v1` → `/v1`. */
19
+ export declare function basePathsOf(doc: Json): string[];
20
+ /** The first absolute http(s) server URL, used as the default proxy target. */
21
+ export declare function serverUrlOf(doc: Json): string | undefined;
22
+ //# sourceMappingURL=catalogue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalogue.d.ts","sourceRoot":"","sources":["../src/catalogue.ts"],"names":[],"mappings":"AAIA,OAAO,EAA2B,KAAK,IAAI,EAAE,MAAM,UAAU,CAAC;AAE9D,OAAO,EAIL,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACvB,MAAM,SAAS,CAAC;AAMjB,yFAAyF;AACzF,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKlD;AAWD,wBAAgB,eAAe,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,GAAG,gBAAgB,EAAE,CA+B1E;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGvD;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,GAAG,iBAAiB,CAqBnF;AAMD,wBAAgB,cAAc,CAAC,SAAS,EAAE,iBAAiB,EAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAKhG;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,iBAAiB,GAAG,MAAM,CAGpE;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,GAAG,kBAAkB,EAAE,CA6B9E;AAED,+EAA+E;AAC/E,wBAAgB,WAAW,CAAC,GAAG,EAAE,IAAI,GAAG,MAAM,EAAE,CAmB/C;AAED,+EAA+E;AAC/E,wBAAgB,WAAW,CAAC,GAAG,EAAE,IAAI,GAAG,MAAM,GAAG,SAAS,CASzD"}