counterfact 2.7.0 → 2.9.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.
Files changed (57) hide show
  1. package/README.md +5 -160
  2. package/bin/README.md +39 -14
  3. package/bin/counterfact.js +18 -539
  4. package/bin/ts-loader.mjs +1 -0
  5. package/dist/api-runner.js +202 -0
  6. package/dist/app.js +102 -114
  7. package/dist/cli/banner.js +81 -0
  8. package/dist/cli/check-for-updates.js +45 -0
  9. package/dist/cli/run.js +304 -0
  10. package/dist/cli/telemetry.js +50 -0
  11. package/dist/migrate/paths-to-routes.js +1 -0
  12. package/dist/migrate/update-route-types.js +3 -3
  13. package/dist/msw.js +78 -0
  14. package/dist/repl/raw-http-client.js +22 -1
  15. package/dist/repl/repl.js +250 -63
  16. package/dist/repl/route-builder.js +68 -0
  17. package/dist/server/constants.js +8 -0
  18. package/dist/server/context-registry.js +54 -1
  19. package/dist/server/determine-module-kind.js +14 -0
  20. package/dist/server/dispatcher.js +46 -0
  21. package/dist/server/file-discovery.js +21 -9
  22. package/dist/server/is-proxy-enabled-for-path.js +12 -0
  23. package/dist/server/json-to-xml.js +10 -0
  24. package/dist/server/load-openapi-document.js +4 -11
  25. package/dist/server/module-dependency-graph.js +25 -0
  26. package/dist/server/module-loader.js +52 -21
  27. package/dist/server/module-tree.js +36 -0
  28. package/dist/server/openapi-document.js +69 -0
  29. package/dist/server/registry.js +89 -0
  30. package/dist/server/response-builder.js +15 -0
  31. package/dist/server/scenario-registry.js +26 -0
  32. package/dist/server/tools.js +27 -0
  33. package/dist/server/transpiler.js +24 -9
  34. package/dist/server/{admin-api-middleware.js → web-server/admin-api-middleware.js} +19 -9
  35. package/dist/server/web-server/create-koa-app.js +68 -0
  36. package/dist/server/web-server/openapi-middleware.js +34 -0
  37. package/dist/server/{koa-middleware.js → web-server/routes-middleware.js} +26 -6
  38. package/dist/typescript-generator/code-generator.js +118 -4
  39. package/dist/typescript-generator/coder.js +76 -0
  40. package/dist/typescript-generator/operation-coder.js +12 -4
  41. package/dist/typescript-generator/operation-type-coder.js +39 -4
  42. package/dist/typescript-generator/parameters-type-coder.js +2 -4
  43. package/dist/typescript-generator/prune.js +3 -1
  44. package/dist/typescript-generator/repository.js +77 -20
  45. package/dist/typescript-generator/requirement.js +69 -0
  46. package/dist/typescript-generator/{generate.js → scenario-file-generator.js} +99 -81
  47. package/dist/typescript-generator/script.js +70 -7
  48. package/dist/typescript-generator/specification.js +27 -0
  49. package/dist/util/ensure-directory-exists.js +8 -0
  50. package/dist/util/forward-slash-path.js +63 -0
  51. package/dist/util/load-config-file.js +2 -2
  52. package/dist/util/read-file.js +27 -2
  53. package/dist/util/runtime-can-execute-erasable-ts.js +12 -0
  54. package/dist/util/windows-escape.js +18 -0
  55. package/package.json +5 -4
  56. package/dist/server/create-koa-app.js +0 -42
  57. package/dist/server/openapi-middleware.js +0 -19
package/README.md CHANGED
@@ -4,174 +4,19 @@
4
4
 
5
5
  <br>
6
6
 
7
- **Your backend isn't ready. Your frontend can't wait.**
8
-
9
- **Counterfact turns your OpenAPI spec into a live, stateful API you can program in TypeScript.**
10
-
11
- <br>
12
-
13
- ![MIT License](https://img.shields.io/badge/license-MIT-blue) [![TypeScript](./typescript-badge.png)](https://github.com/ellerbrock/typescript-badges/) [![Coverage Status](https://coveralls.io/repos/github/pmcelhaney/counterfact/badge.svg)](https://coveralls.io/github/pmcelhaney/counterfact)
7
+ ![MIT License](https://img.shields.io/badge/license-MIT-blue) [![TypeScript](./typescript-badge.png)](https://github.com/ellerbrock/typescript-badges/) [![Coverage Status](https://coveralls.io/repos/github/counterfact/api-simulator/badge.svg)](https://coveralls.io/github/pmcelhaney/counterfact)
14
8
 
15
9
  </div>
16
10
 
17
- This is a five-minute walkthrough. By the end, you’ll have a **stateful, type-safe, hot-reloading API simulator** running locally—and you’ll understand why it’s different from traditional mock servers.
18
-
19
- Built for frontend developers, test engineers, and AI agents that need a predictable API to work against.
11
+ You've used mock servers. You know where they stop being useful: static responses, no shared state, no way to inject a failure mid-run, no control without restarting. Counterfact picks up where they leave off.
20
12
 
21
-
22
-
23
- ## Minute 1 — Start the server
13
+ Point it at an OpenAPI spec and it generates TypeScript handlers for every endpoint—type-safe, hot-reloading, sharing state across routes. A built-in REPL gives you a live control surface: seed data, trigger error conditions, proxy individual routes to a real backend, all on a running server. Whether you're a frontend developer waiting on a backend, a test engineer who needs clean reproducible state, or an AI agent that needs a stable API to work against, Counterfact is the simulator that doesn't plateau.
24
14
 
25
15
  ```sh
26
16
  npx counterfact@latest https://petstore3.swagger.io/api/v3/openapi.json api
27
17
  ```
28
18
 
29
- > **Requires Node ≥ 22.0.0**
30
-
31
- That’s it.
32
-
33
- Counterfact reads your spec, generates a TypeScript handler for every endpoint, and starts a server at `http://localhost:3100`.
34
-
35
- Open `http://localhost:3100/counterfact/swagger/`.
36
-
37
- Every endpoint is already live, returning random, schema-valid responses. No code written yet.
38
-
39
-
40
-
41
- ## Minute 2 — Make a route return real data
42
-
43
- Open the generated file for `GET /pet/{petId}`:
44
-
45
- ```ts
46
- import type { HTTP_GET } from "../../types/paths/pet/{petId}.types.js";
47
-
48
- export const GET: HTTP_GET = ($) => $.response[200].random();
49
- ```
50
-
51
- Replace `.random()` with your own logic:
52
-
53
- ```ts
54
- export const GET: HTTP_GET = ($) => {
55
- if ($.path.petId === 99) {
56
- return $.response[404].text("Pet not found");
57
- }
58
- return $.response[200].json({
59
- id: $.path.petId,
60
- name: "Fluffy",
61
- status: "available",
62
- photoUrls: []
63
- });
64
- };
65
- ```
66
-
67
- Save the file. The server reloads instantly—no restart, no lost state.
68
-
69
- TypeScript enforces the contract. If your response doesn’t match the spec, you’ll know before you make the request.
70
-
71
- ## Minute 3 — Add state that survives across requests
72
-
73
- Real APIs have memory. Yours should too.
74
-
75
- Create `api/routes/_.context.ts`:
76
-
77
- ```ts
78
- import type { Pet } from "../types/components/pet.types.js";
79
-
80
- export class Context {
81
- private pets = new Map<number, Pet>();
82
- private nextId = 1;
83
-
84
- add(data: Omit<Pet, "id">): Pet {
85
- const pet = { ...data, id: this.nextId++ };
86
- this.pets.set(pet.id, pet);
87
- return pet;
88
- }
89
-
90
- get(id: number): Pet | undefined { return this.pets.get(id); }
91
- list(): Pet[] { return [...this.pets.values()]; }
92
- remove(id: number): void { this.pets.delete(id); }
93
- }
94
- ```
95
-
96
- Use it in your routes:
97
-
98
- ```ts
99
- export const GET: HTTP_GET = ($) => $.response[200].json($.context.list());
100
- export const POST: HTTP_POST = ($) => $.response[200].json($.context.add($.body));
101
- ```
102
-
103
- Now your API behaves like a real system:
104
- - POST creates data
105
- - GET returns it
106
- - DELETE removes it
107
-
108
- State survives hot reloads. Restarting resets everything—perfect for clean test runs.
109
-
110
-
111
-
112
- ## Minute 4 — Control the system at runtime (REPL)
113
-
114
- This is where Counterfact becomes more than a mock.
115
-
116
- The built-in REPL lets you inspect and control the system while it’s running.
117
-
118
- Seed data:
119
-
120
- ```
121
- ⬣> context.add({ name: "Fluffy", status: "available", photoUrls: [] })
122
- ⬣> context.add({ name: "Rex", status: "pending", photoUrls: [] })
123
- ```
124
-
125
- Make requests:
126
-
127
- ```
128
- ⬣> client.get("/pet/1")
129
- ```
130
-
131
- Simulate failures instantly:
132
-
133
- ```
134
- ⬣> context.rateLimitExceeded = true
135
- ⬣> client.get("/pet/1")
136
- { status: 429, body: "Too Many Requests" }
137
- ```
138
-
139
- No HTTP scripts. No restarts. Just direct control.
140
-
141
-
142
-
143
- ## Minute 5 — Proxy to the real backend
144
-
145
- When parts of your backend are ready, forward them through.
146
-
147
- Everything else stays simulated.
148
-
149
- ```sh
150
- npx counterfact@latest openapi.yaml api --proxy-url https://api.example.com
151
- ```
152
-
153
- Toggle paths live:
154
-
155
- ```
156
- ⬣> .proxy on /payments
157
- ⬣> .proxy on /auth
158
- ⬣> .proxy off
159
- ```
160
-
161
-
162
-
163
- ## What you just built
164
-
165
- In five minutes, you turned a static spec into a working system:
166
-
167
- - **Schema-valid responses** from the moment it starts
168
- - **Type-safe handlers** generated from your spec
169
- - **Shared state** across all routes
170
- - **Hot reloading** without losing that state
171
- - A **live control surface (REPL)** for runtime behavior
172
- - **Selective proxying** to real services
173
-
174
-
19
+ > Requires Node ≥ 22.0.0
175
20
 
176
21
  ## Go deeper
177
22
 
@@ -189,4 +34,4 @@ In five minutes, you turned a static spec into a working system:
189
34
 
190
35
  [Changelog](./CHANGELOG.md) · [Contributing](./CONTRIBUTING.md)
191
36
 
192
- </div>
37
+ </div>
package/bin/README.md CHANGED
@@ -6,7 +6,19 @@ This directory contains the executable script that is run when a developer invok
6
6
 
7
7
  | File | Description |
8
8
  |---|---|
9
- | `counterfact.js` | Parses command-line arguments with [Commander](https://github.com/tj/commander.js), validates inputs, and calls `counterfact()` from `src/app.ts` to start the server, code generator, file watcher, and/or REPL |
9
+ | `counterfact.js` | Thin bootstrap: enforces minimum Node version, probes for native TypeScript execution, then delegates to `src/cli/run.ts` (or `dist/cli/run.js`) |
10
+ | `taglines.txt` | One-per-line list of random taglines shown in the startup banner |
11
+
12
+ ## Architecture
13
+
14
+ Most of the CLI logic lives in **`src/cli/`** as TypeScript:
15
+
16
+ | Module | Description |
17
+ |---|---|
18
+ | `src/cli/run.ts` | Commander program setup, `main()` action handler, and the `runCli()` entry point |
19
+ | `src/cli/banner.ts` | Startup banner utilities: `padTagLine`, `createWatchMessage`, `createIntroduction` |
20
+ | `src/cli/check-for-updates.ts` | npm update check: `isOutdated`, `checkForUpdates` |
21
+ | `src/cli/telemetry.ts` | PostHog telemetry: `isTelemetryEnabled`, `sendTelemetry` |
10
22
 
11
23
  ## How It Works
12
24
 
@@ -14,19 +26,32 @@ This directory contains the executable script that is run when a developer invok
14
26
  npx counterfact@latest openapi.yaml ./api [options]
15
27
 
16
28
 
17
- ┌────────────────────────────┐
18
- counterfact.js
19
-
20
- │ 1. Parse args (Commander)
21
- │ 2. Load counterfact.yaml
22
- │ 3. Merge config + args
23
- 4. Resolve paths
24
- 5. Build Config object
25
- │ 6. Run migrations if │
26
- old layout detected │
27
- │ 7. Call start(config) │
28
- │ from src/app.ts │
29
- └────────────────────────────┘
29
+ ┌────────────────────────────────────────────────┐
30
+ bin/counterfact.js (thin bootstrap)
31
+
32
+ │ 1. Enforce minimum Node.js version
33
+ │ 2. Probe native TypeScript execution
34
+ │ 3. Import runCli() from src/cli/run.ts
35
+ (or dist/cli/run.js when compiled)
36
+ 4. Call runCli(process.argv)
37
+ └────────────────────────────────────────────────┘
38
+
39
+
40
+ ┌────────────────────────────────────────────────┐
41
+ │ src/cli/run.ts (all CLI logic) │
42
+ │ │
43
+ │ 1. Read version from package.json │
44
+ │ 2. Read taglines from bin/taglines.txt │
45
+ │ 3. Fire telemetry (if enabled) │
46
+ │ 4. Parse args (Commander) │
47
+ │ 5. Load counterfact.yaml │
48
+ │ 6. Merge config + args │
49
+ │ 7. Resolve paths │
50
+ │ 8. Build Config object │
51
+ │ 9. Run migrations if old layout detected │
52
+ │ 10. Print startup banner │
53
+ │ 11. Call start(config) from src/app.ts │
54
+ └────────────────────────────────────────────────┘
30
55
  ```
31
56
 
32
57
  ### Key CLI Options