contract-drift-detection 0.1.2 → 0.1.4

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/CHANGELOG.md CHANGED
@@ -2,6 +2,28 @@
2
2
 
3
3
  All notable changes to this project are documented in this file.
4
4
 
5
+ ## 0.1.4 - 2026-03-16
6
+
7
+ ### Improved
8
+
9
+ - Reworked README for npm users with clearer onboarding and prerequisites.
10
+ - Added explicit first-run flows for `quickstart`, `--spec`, `--discover`, and `--spec-url`.
11
+ - Expanded troubleshooting section with actionable recovery steps.
12
+ - Clarified CLI usage, runtime diagnostics endpoints, and CORS guidance.
13
+
14
+ ## 0.1.3 - 2026-03-16
15
+
16
+ ### Improved
17
+
18
+ - Expanded OpenAPI auto-discovery endpoints (for example `/api/openapi.json`, `/swagger/v1/swagger.json`, `/api-docs`).
19
+ - Discovery failures now return actionable recovery guidance (use `--spec-url`, `quickstart`, or `init`) instead of opaque stack-first output.
20
+ - CLI default error output is now user-friendly (`Error: <message>`), with optional stack traces via `CDD_SHOW_STACK=1`.
21
+ - Demo backend now exposes `/openapi.yaml`, enabling out-of-the-box `--discover` success in local demos.
22
+
23
+ ### Added
24
+
25
+ - Test coverage for `/api/openapi.json` discovery fallback.
26
+
5
27
  ## 0.1.2 - 2026-03-16
6
28
 
7
29
  ### Added
package/README.md CHANGED
@@ -1,159 +1,111 @@
1
1
  # Contract Drift Detection
2
2
 
3
- Contract Drift Detection is a stateful OpenAPI mock server with proxy-based response validation.
3
+ Contract Drift Detection is a developer CLI that gives you a stateful mock API from OpenAPI and detects contract drift against real backend responses.
4
4
 
5
- It solves the frontend/backend integration gap by combining:
5
+ Use it when:
6
6
 
7
- 1. **Spec-first mocking** (OpenAPI 3.x)
8
- 2. **Persistent local API state** (CRUD that actually mutates)
9
- 3. **Live contract drift detection** against real backend responses
7
+ - frontend is blocked by incomplete backend endpoints,
8
+ - your team has an OpenAPI contract but real responses drift from it,
9
+ - you want one local endpoint that supports mock mode and proxy mode.
10
10
 
11
- If your frontend is blocked by backend readiness, or your backend keeps returning payloads that quietly break UI assumptions, this package is built for that exact pain.
11
+ ## What this tool does
12
12
 
13
- ## What problem this resolves
13
+ - Generates API routes from OpenAPI 3.x (`.yaml` or `.json`).
14
+ - Persists API state in a local `.mock-db.json` file.
15
+ - Supports workflow actions using OpenAPI vendor extensions (`x-mock-state`).
16
+ - In proxy mode, forwards traffic to real backend and validates responses against your spec.
17
+ - Prints visible drift alerts when schema mismatches happen.
14
18
 
15
- Frontend and backend teams usually have to choose between two incomplete options:
19
+ ## What you need before starting
16
20
 
17
- - Stateless OpenAPI mockers that validate contracts but do not persist mutations.
18
- - Fake JSON backends that persist state but know nothing about the OpenAPI spec the frontend was actually built against.
21
+ - Node.js `>= 20`
22
+ - One of these:
23
+ - local OpenAPI file (`openapi.yaml` / `openapi.json`), or
24
+ - backend URL that exposes OpenAPI, or
25
+ - nothing (use `quickstart` to generate starter contract)
19
26
 
20
- This project combines both and adds a third layer: drift detection against a live backend.
27
+ ## Fastest way to start
21
28
 
22
- ### Typical outcomes
23
-
24
- - Frontend can develop end-to-end before backend is complete.
25
- - Backend drift is detected immediately in development, not in QA/production.
26
- - Teams converge faster on a stable API contract.
27
-
28
- ## Features
29
-
30
- - Generates Fastify routes directly from an OpenAPI 3.x file.
31
- - Persists state in a local JSON file so POST, PATCH, and DELETE affect future requests.
32
- - Seeds collections from component schemas using realistic sample data.
33
- - Supports action-style workflows with `x-mock-state` vendor extensions instead of a separate DSL.
34
- - Proxies to a live backend with `--drift-check` and validates real responses against the contract.
35
- - Ships as a CLI and as a reusable library.
36
-
37
- ## Install and run
38
-
39
- ### Use directly with `npx` (no install)
40
-
41
- ```bash
42
- npx contract-drift-detection@latest --spec ./openapi.yaml --port 4010
43
- ```
44
-
45
- (`serve` subcommand is optional; root command accepts serve options.)
46
-
47
- ### Install globally
48
-
49
- ```bash
50
- npm i -g contract-drift-detection
51
- cdd --help
52
- ```
53
-
54
- ### Install in a project
55
-
56
- ```bash
57
- npm install
58
- npm run build
59
- ```
60
-
61
- For local development:
62
-
63
- ```bash
64
- npm run dev
65
- ```
66
-
67
- ## 30-second quick start
68
-
69
- No spec file yet? Start instantly:
29
+ ### Option A: no spec yet (recommended first run)
70
30
 
71
31
  ```bash
72
32
  npx contract-drift-detection@latest quickstart
73
33
  ```
74
34
 
75
- Already have an OpenAPI file? Start with local spec:
35
+ This creates a starter OpenAPI file and starts the server immediately.
36
+
37
+ ### Option B: you already have a local spec
76
38
 
77
39
  ```bash
78
- npx contract-drift-detection@latest serve --spec ./openapi.yaml --port 4010
40
+ npx contract-drift-detection@latest --spec ./openapi.yaml --port 4010
79
41
  ```
80
42
 
81
- Create a starter config + starter OpenAPI file in current directory:
43
+ ### Option C: backend exposes OpenAPI
82
44
 
83
45
  ```bash
84
- npx contract-drift-detection@latest init
46
+ npx contract-drift-detection@latest --discover http://localhost:8080 --port 4010
85
47
  ```
86
48
 
87
- If your backend already exposes OpenAPI, avoid writing spec manually:
49
+ If discover still fails, use explicit endpoint:
88
50
 
89
51
  ```bash
90
- npx contract-drift-detection@latest serve --discover http://localhost:8080 --port 4010
52
+ npx contract-drift-detection@latest --spec-url http://localhost:8080/v3/api-docs --port 4010
91
53
  ```
92
54
 
93
- Or use direct remote spec URL:
55
+ ## Typical frontend + backend integration flow
94
56
 
95
- ```bash
96
- npx contract-drift-detection@latest serve --spec-url http://localhost:8080/openapi.json --port 4010
97
- ```
57
+ 1. Start backend (optional for mock-only mode).
58
+ 2. Start this tool on `http://localhost:4010`.
59
+ 3. Point frontend API base URL to `http://localhost:4010`.
60
+ 4. Use mock mode during backend gaps.
61
+ 5. Turn on drift-check mode when backend is available.
98
62
 
99
- Start in drift detection mode against a real backend:
63
+ Drift-check example:
100
64
 
101
65
  ```bash
102
- npx contract-drift-detection@latest serve \
66
+ npx contract-drift-detection@latest \
103
67
  --discover http://localhost:8080 \
104
- --port 4010 \
105
- --drift-check http://localhost:8080
68
+ --drift-check http://localhost:8080 \
69
+ --port 4010
106
70
  ```
107
71
 
108
- If the backend is flaky and you still want local progress, add `--fallback-to-mock`.
109
-
110
- Point your frontend API base URL to `http://localhost:4010`.
72
+ ## CLI usage
111
73
 
112
- ## CLI
74
+ `serve` subcommand is optional. Root command accepts serve options directly.
113
75
 
114
76
  ```text
115
- contract-drift-detection serve [--spec <path> | --spec-url <url> | --discover <backend-url>] [--port 4010] [--host 0.0.0.0] [--db .mock-db.json] [--cors-origin *] [--drift-check <url>] [--fallback-to-mock] [--verbose]
77
+ contract-drift-detection [--spec <path> | --spec-url <url> | --discover <backend-url>] [--port 4010] [--host 0.0.0.0] [--db .mock-db.json] [--cors-origin *] [--drift-check <url>] [--fallback-to-mock] [--verbose]
78
+ contract-drift-detection serve [same options as above]
116
79
  contract-drift-detection init [--spec openapi.yaml] [--template rest-crud|none] [--db .mock-db.json] [--port 4010] [--host 0.0.0.0]
117
80
  contract-drift-detection quickstart [--spec openapi.yaml] [--port 4010] [--host 0.0.0.0] [--db .mock-db.json] [--cors-origin *] [--verbose]
118
-
119
- # Root command (equivalent to serve)
120
- contract-drift-detection [--spec <path> | --spec-url <url> | --discover <backend-url>] [--port 4010] ...
121
81
  ```
122
82
 
123
- `--cors-origin` defaults to `*` for frictionless browser testing.
124
-
125
- Spec resolution priority in `serve` is:
83
+ Spec resolution priority:
126
84
 
127
85
  1. `--spec`
128
86
  2. `--spec-url`
129
87
  3. `--discover`
130
88
 
131
- Remote/discovered specs are cached locally under `.cdd/`.
132
-
133
- ## Recommended workflows
134
-
135
- ### A) Frontend-first (mock only)
136
-
137
- ```bash
138
- npx cdd serve --spec ./openapi.yaml --port 4010
139
- ```
140
-
141
- Use this when backend endpoints are not ready.
89
+ Remote/discovered specs are cached under `.cdd/`.
142
90
 
143
- ### B) Integration mode (proxy + drift detection)
91
+ ## Backend OpenAPI endpoints commonly discovered
144
92
 
145
- ```bash
146
- npx cdd serve \
147
- --spec ./openapi.yaml \
148
- --port 4010 \
149
- --drift-check http://localhost:8080
150
- ```
93
+ `--discover` checks common paths, including:
151
94
 
152
- Use this when backend is partially/fully available and you want immediate contract mismatch alerts.
95
+ - `/openapi.json`
96
+ - `/openapi.yaml`
97
+ - `/api/openapi.json`
98
+ - `/api/openapi.yaml`
99
+ - `/swagger.json`
100
+ - `/swagger/v1/swagger.json`
101
+ - `/v3/api-docs`
102
+ - `/api-docs-json`
103
+ - `/api-docs`
104
+ - `/docs/openapi.json`
153
105
 
154
- ## OpenAPI workflow actions
106
+ ## OpenAPI workflow actions (`x-mock-state`)
155
107
 
156
- Instead of inventing a separate configuration language, action endpoints are defined directly in the OpenAPI document with vendor extensions.
108
+ Example:
157
109
 
158
110
  ```yaml
159
111
  paths:
@@ -175,70 +127,58 @@ Supported actions:
175
127
  - `replace`
176
128
  - `append`
177
129
 
178
- Values inside `assign` and `set` can reference request input with `{{body.field}}`, `{{params.id}}`, `{{query.key}}`, and `{{headers.header_name}}`.
130
+ Template values are supported in `assign` and `set`, for example:
179
131
 
180
- ## Runtime endpoints
132
+ - `{{body.field}}`
133
+ - `{{params.id}}`
134
+ - `{{query.key}}`
135
+ - `{{headers.authorization}}`
181
136
 
182
- - `GET /__health` returns a simple health payload.
183
- - `GET /__spec` returns the dereferenced OpenAPI document currently in memory.
184
- - `GET /__routes` returns generated route diagnostics (method/path/operation).
137
+ ## Runtime diagnostic endpoints
185
138
 
186
- ## Browser/CORS notes
139
+ - `GET /__health` → health check
140
+ - `GET /__spec` → resolved OpenAPI in memory
141
+ - `GET /__routes` → generated route summary
187
142
 
188
- - Default CORS is enabled for all origins (`*`) so local frontend apps can call the mock server immediately.
189
- - For stricter setups, set a specific origin:
143
+ ## CORS notes
144
+
145
+ - Default CORS is enabled for all origins (`*`) for fast local onboarding.
146
+ - For stricter browser setup:
190
147
 
191
148
  ```bash
192
- npx cdd serve --spec ./openapi.yaml --cors-origin http://localhost:5173
149
+ npx contract-drift-detection@latest --spec ./openapi.yaml --cors-origin http://localhost:5173
193
150
  ```
194
151
 
195
- - If you see `EADDRINUSE`, choose a different port (`--port 4020`) or stop the process already using that port.
152
+ ## Troubleshooting
153
+
154
+ ### `Could not discover an OpenAPI spec ...`
196
155
 
197
- ## Example product workflow
156
+ - Try explicit endpoint with `--spec-url`.
157
+ - Or use `quickstart` if backend does not expose OpenAPI.
158
+ - Or run `init` to scaffold starter files.
198
159
 
199
- 1. Start the mock server from the OpenAPI spec before the backend is ready.
200
- 2. Build the frontend against persistent local state instead of hand-written fixtures.
201
- 3. Switch on `--drift-check` when backend endpoints start coming online.
202
- 4. Keep frontend traffic pointed at this tool until contract mismatches are resolved.
160
+ ### `EADDRINUSE: address already in use`
203
161
 
204
- ## Development
162
+ - Port already taken. Choose another port:
205
163
 
206
164
  ```bash
207
- npm run typecheck
208
- npm test
209
- npm run lint
210
- npm run build
165
+ npx contract-drift-detection@latest --spec ./openapi.yaml --port 4020
211
166
  ```
212
167
 
213
- ## Release verification
168
+ ### Need full stack traces for debugging
214
169
 
215
170
  ```bash
216
- npm run release:verify
171
+ CDD_SHOW_STACK=1 npx contract-drift-detection@latest --spec ./openapi.yaml
217
172
  ```
218
173
 
219
- This runs typecheck, tests, lint, build, and `npm pack --dry-run`.
220
-
221
- ## Real frontend + backend test
222
-
223
- Use the built-in demo harness to validate this package in a full request chain:
224
-
225
- 1. `npm run demo:backend`
226
- 2. `npm run demo:mock:drift`
227
- 3. `npm run demo:frontend`
228
-
229
- Then open `http://localhost:5173` and exercise requests through the browser.
230
-
231
- For the complete flow and drift simulation, see `docs/REAL_STACK_TEST.md`.
232
-
233
- ## Launch
234
-
235
- For release automation and go-to-market steps, follow:
174
+ ## Package and binaries
236
175
 
237
- - `docs/RELEASE.md` for versioning and npm publish
238
- - `docs/LAUNCH_CHECKLIST.md` for launch channels and success metrics
176
+ - npm package: `contract-drift-detection`
177
+ - binaries: `contract-drift-detection`, `cdd`
178
+ - license: MIT
239
179
 
240
- ## Shipping notes
180
+ ## Links
241
181
 
242
- - The package exposes the `cdd` and `contract-drift-detection` binaries.
243
- - `prepublishOnly` runs typecheck, tests, and build to block broken releases.
244
- - The included example spec under `examples/` is intended for demos, screenshots, and onboarding.
182
+ - Changelog: `CHANGELOG.md`
183
+ - Release process: `docs/RELEASE.md`
184
+ - Real stack demo test flow: `docs/REAL_STACK_TEST.md`
package/dist/cli.js CHANGED
@@ -14,9 +14,14 @@ import path from "path";
14
14
  var DISCOVERY_PATHS = [
15
15
  "/openapi.json",
16
16
  "/openapi.yaml",
17
+ "/api/openapi.json",
18
+ "/api/openapi.yaml",
17
19
  "/swagger.json",
20
+ "/swagger/v1/swagger.json",
18
21
  "/v3/api-docs",
19
- "/api-docs-json"
22
+ "/api-docs-json",
23
+ "/api-docs",
24
+ "/docs/openapi.json"
20
25
  ];
21
26
  var STARTER_SPEC = `openapi: 3.0.3
22
27
  info:
@@ -211,7 +216,14 @@ async function discoverSpecUrl(backendBaseUrl) {
211
216
  }
212
217
  }
213
218
  throw new Error(
214
- `Could not discover an OpenAPI spec under ${baseUrl}. Tried: ${DISCOVERY_PATHS.join(", ")}`
219
+ [
220
+ `Could not discover an OpenAPI spec under ${baseUrl}.`,
221
+ `Tried: ${DISCOVERY_PATHS.join(", ")}`,
222
+ "Next steps:",
223
+ "1) Use --spec-url with your exact endpoint (for example, /v3/api-docs or /swagger/v1/swagger.json).",
224
+ "2) If backend does not expose OpenAPI, run quickstart: npx contract-drift-detection@latest quickstart",
225
+ "3) Or generate a starter file: npx contract-drift-detection@latest init"
226
+ ].join("\n")
215
227
  );
216
228
  }
217
229
  async function writeStarterSpec(cwd, specPath) {
@@ -1040,8 +1052,17 @@ async function main() {
1040
1052
  await cli.parseAsync(process.argv);
1041
1053
  }
1042
1054
  await main().catch((error) => {
1043
- process.stderr.write(`${error instanceof Error ? error.stack : String(error)}
1055
+ if (error instanceof Error) {
1056
+ process.stderr.write(`Error: ${error.message}
1044
1057
  `);
1058
+ if (process.env.CDD_SHOW_STACK === "1") {
1059
+ process.stderr.write(`${error.stack}
1060
+ `);
1061
+ }
1062
+ } else {
1063
+ process.stderr.write(`${String(error)}
1064
+ `);
1065
+ }
1045
1066
  process.exitCode = 1;
1046
1067
  });
1047
1068
  //# sourceMappingURL=cli.js.map
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts","../src/config.ts","../src/onboarding.ts","../src/server.ts","../src/utils.ts","../src/dsl.ts","../src/drift-detector.ts","../src/route-context.ts","../src/schema-seeder.ts","../src/spec-loader.ts","../src/state-store.ts","../src/proxy.ts"],"sourcesContent":["import process from 'node:process';\nimport { createCli, resolveServeConfig, writeStarterConfig } from './config.js';\nimport { createServer } from './server.js';\n\nfunction renderStartupBanner(config: {\n host: string;\n port: number;\n specPath: string;\n dbPath: string;\n driftCheckTarget?: string;\n}): string {\n const lines = [\n `🚀 Contract Drift Detection running at http://${config.host}:${config.port}`,\n `- Spec: ${config.specPath}`,\n `- DB: ${config.dbPath}`,\n `- Mode: ${config.driftCheckTarget ? `proxy + drift-check (${config.driftCheckTarget})` : 'stateful mock'}`,\n '- Health: GET /__health',\n '- Routes: GET /__routes',\n ];\n\n return `${lines.join('\\n')}\\n`;\n}\n\nasync function main(): Promise<void> {\n const cli = createCli();\n const serveCommand = cli.commands.find((command) => command.name() === 'serve');\n const initCommand = cli.commands.find((command) => command.name() === 'init');\n const quickstartCommand = cli.commands.find((command) => command.name() === 'quickstart');\n\n const startServer = async (rawOptions: Record<string, string | boolean | undefined>) => {\n const config = await resolveServeConfig(process.cwd(), rawOptions);\n const server = await createServer(config);\n await server.listen({ port: config.port, host: config.host });\n process.stdout.write(renderStartupBanner(config));\n };\n\n serveCommand?.action(async function () {\n await startServer(this.opts());\n });\n\n cli.action(async () => {\n const options = cli.opts<Record<string, string | boolean | undefined>>();\n if (!options.spec && !options.specUrl && !options.discover) {\n cli.outputHelp();\n return;\n }\n\n await startServer(options);\n });\n\n initCommand?.action(async function () {\n const options = this.opts();\n const targetPath = await writeStarterConfig(process.cwd(), {\n spec: String(options.spec),\n db: String(options.db),\n host: String(options.host),\n port: Number(options.port),\n template: options.template === 'none' ? 'none' : 'rest-crud',\n });\n process.stdout.write(`Created ${targetPath}\\n`);\n });\n\n quickstartCommand?.action(async function () {\n const options = this.opts();\n const cwd = process.cwd();\n const specPath = String(options.spec ?? 'openapi.yaml');\n\n await writeStarterConfig(cwd, {\n spec: specPath,\n db: String(options.db ?? '.mock-db.json'),\n host: String(options.host ?? '0.0.0.0'),\n port: Number(options.port ?? 4010),\n template: 'rest-crud',\n });\n\n const config = await resolveServeConfig(cwd, {\n spec: specPath,\n db: String(options.db ?? '.mock-db.json'),\n host: String(options.host ?? '0.0.0.0'),\n port: String(options.port ?? 4010),\n corsOrigin: String(options.corsOrigin ?? '*'),\n verbose: Boolean(options.verbose),\n });\n\n const server = await createServer(config);\n await server.listen({ port: config.port, host: config.host });\n process.stdout.write(renderStartupBanner(config));\n });\n\n await cli.parseAsync(process.argv);\n}\n\nawait main().catch((error) => {\n process.stderr.write(`${error instanceof Error ? error.stack : String(error)}\\n`);\n process.exitCode = 1;\n});","import { mkdir, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport { Command } from 'commander';\nimport type { InitConfig, MockEngineConfig } from './types.js';\nimport { cacheSpecFromUrl, discoverSpecUrl, writeStarterSpec } from './onboarding.js';\n\nfunction resolvePathFromCwd(cwd: string, value: string): string {\n return path.isAbsolute(value) ? value : path.join(cwd, value);\n}\n\nfunction applyServeOptions(command: Command): Command {\n return command\n .option('--spec <path>', 'Path to an OpenAPI 3.x file')\n .option('--spec-url <url>', 'Remote URL to an OpenAPI 3.x file')\n .option('--discover <backend-url>', 'Discover OpenAPI from a backend URL and cache it locally')\n .option('--port <port>', 'Port to bind the server', '4010')\n .option('--host <host>', 'Host to bind the server', '0.0.0.0')\n .option('--db <path>', 'JSON database path', '.mock-db.json')\n .option('--cors-origin <origin>', 'CORS origin (default is *)', '*')\n .option('--drift-check <url>', 'Forward traffic to a real backend and validate responses')\n .option('--fallback-to-mock', 'Fallback to mock responses when proxying fails', false)\n .option('--verbose', 'Enable verbose logging', false);\n}\n\nexport function createCli(): Command {\n const program = new Command();\n\n program\n .name('contract-drift-detection')\n .description('Stateful OpenAPI mock server with contract drift detection')\n .version('0.1.0');\n\n applyServeOptions(program);\n\n applyServeOptions(program.command('serve').description('Start the mock engine'));\n\n program\n .command('init')\n .description('Create a starter config for the current workspace')\n .option('--spec <path>', 'Default OpenAPI path', 'openapi.yaml')\n .option('--template <name>', 'Template to generate (rest-crud | none)', 'rest-crud')\n .option('--db <path>', 'Default JSON database path', '.mock-db.json')\n .option('--port <port>', 'Default port', '4010')\n .option('--host <host>', 'Default host', '0.0.0.0');\n\n program\n .command('quickstart')\n .description('Generate a starter OpenAPI file and start the server immediately')\n .option('--spec <path>', 'Starter OpenAPI path', 'openapi.yaml')\n .option('--port <port>', 'Port to bind the server', '4010')\n .option('--host <host>', 'Host to bind the server', '0.0.0.0')\n .option('--db <path>', 'JSON database path', '.mock-db.json')\n .option('--cors-origin <origin>', 'CORS origin (default is *)', '*')\n .option('--verbose', 'Enable verbose logging', false);\n\n return program;\n}\n\nexport async function resolveServeConfig(\n cwd: string,\n options: Record<string, string | boolean | undefined>,\n): Promise<MockEngineConfig> {\n let resolvedSpecPath: string | undefined;\n\n if (options.spec) {\n resolvedSpecPath = resolvePathFromCwd(cwd, String(options.spec));\n } else if (options.specUrl) {\n resolvedSpecPath = await cacheSpecFromUrl(cwd, String(options.specUrl));\n } else if (options.discover) {\n const discoveredSpecUrl = await discoverSpecUrl(String(options.discover));\n resolvedSpecPath = await cacheSpecFromUrl(cwd, discoveredSpecUrl);\n }\n\n if (!resolvedSpecPath) {\n throw new Error('Provide one of --spec, --spec-url, or --discover');\n }\n\n return {\n specPath: resolvedSpecPath,\n port: Number(options.port ?? 4010),\n host: String(options.host ?? '0.0.0.0'),\n dbPath: resolvePathFromCwd(cwd, String(options.db ?? '.mock-db.json')),\n corsOrigin: String(options.corsOrigin ?? '*'),\n driftCheckTarget: options.driftCheck ? String(options.driftCheck) : undefined,\n fallbackToMockOnProxyError: Boolean(options.fallbackToMock),\n verbose: Boolean(options.verbose),\n };\n}\n\nexport async function writeStarterConfig(cwd: string, initConfig: InitConfig): Promise<string> {\n if (initConfig.template === 'rest-crud') {\n await writeStarterSpec(cwd, initConfig.spec);\n }\n\n const targetPath = path.join(cwd, 'contract-drift.config.json');\n await mkdir(cwd, { recursive: true });\n await writeFile(\n targetPath,\n `${JSON.stringify(\n {\n spec: initConfig.spec,\n db: initConfig.db,\n host: initConfig.host,\n port: initConfig.port,\n },\n null,\n 2,\n )}\\n`,\n 'utf8',\n );\n return targetPath;\n}","import { mkdir, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\n\nconst DISCOVERY_PATHS = [\n '/openapi.json',\n '/openapi.yaml',\n '/swagger.json',\n '/v3/api-docs',\n '/api-docs-json',\n] as const;\n\nconst STARTER_SPEC = `openapi: 3.0.3\ninfo:\n title: Contract Drift Detection Starter API\n version: 1.0.0\npaths:\n /users:\n get:\n responses:\n '200':\n description: ok\n content:\n application/json:\n schema:\n type: array\n items:\n $ref: '#/components/schemas/User'\n post:\n requestBody:\n required: true\n content:\n application/json:\n schema:\n $ref: '#/components/schemas/UserCreateInput'\n responses:\n '201':\n description: created\n content:\n application/json:\n schema:\n $ref: '#/components/schemas/User'\n /users/{id}:\n get:\n parameters:\n - $ref: '#/components/parameters/UserId'\n responses:\n '200':\n description: ok\n content:\n application/json:\n schema:\n $ref: '#/components/schemas/User'\n patch:\n parameters:\n - $ref: '#/components/parameters/UserId'\n requestBody:\n required: true\n content:\n application/json:\n schema:\n $ref: '#/components/schemas/UserPatchInput'\n responses:\n '200':\n description: updated\n content:\n application/json:\n schema:\n $ref: '#/components/schemas/User'\n delete:\n parameters:\n - $ref: '#/components/parameters/UserId'\n responses:\n '204':\n description: deleted\n /tickets:\n get:\n responses:\n '200':\n description: ok\n content:\n application/json:\n schema:\n type: array\n items:\n $ref: '#/components/schemas/Ticket'\n /tickets/{id}/resolve:\n post:\n parameters:\n - $ref: '#/components/parameters/TicketId'\n x-mock-state:\n action: update\n target: tickets\n find_by: id\n set:\n status: resolved\n responses:\n '200':\n description: ok\n content:\n application/json:\n schema:\n $ref: '#/components/schemas/Ticket'\ncomponents:\n parameters:\n UserId:\n name: id\n in: path\n required: true\n schema:\n type: integer\n TicketId:\n name: id\n in: path\n required: true\n schema:\n type: integer\n schemas:\n User:\n type: object\n required: [id, name, email]\n properties:\n id:\n type: integer\n name:\n type: string\n email:\n type: string\n format: email\n UserCreateInput:\n type: object\n required: [name, email]\n properties:\n name:\n type: string\n email:\n type: string\n format: email\n UserPatchInput:\n type: object\n properties:\n name:\n type: string\n email:\n type: string\n format: email\n Ticket:\n type: object\n required: [id, title, status]\n properties:\n id:\n type: integer\n title:\n type: string\n status:\n type: string\n enum: [open, resolved]\n`;\n\nfunction normalizeBaseUrl(value: string): string {\n return value.endsWith('/') ? value.slice(0, -1) : value;\n}\n\nfunction inferSpecExtension(contentType: string, content: string): 'json' | 'yaml' {\n const normalized = contentType.toLowerCase();\n if (normalized.includes('json')) {\n return 'json';\n }\n\n const trimmed = content.trim();\n return trimmed.startsWith('{') || trimmed.startsWith('[') ? 'json' : 'yaml';\n}\n\nexport async function cacheSpecContent(\n cwd: string,\n specContent: string,\n extension: 'json' | 'yaml',\n filePrefix: string,\n): Promise<string> {\n const cacheDir = path.join(cwd, '.cdd');\n await mkdir(cacheDir, { recursive: true });\n const filePath = path.join(cacheDir, `${filePrefix}.${extension}`);\n await writeFile(filePath, specContent, 'utf8');\n return filePath;\n}\n\nexport async function cacheSpecFromUrl(cwd: string, specUrl: string): Promise<string> {\n const response = await fetch(specUrl);\n if (!response.ok) {\n throw new Error(`Failed to download OpenAPI spec from ${specUrl}: ${response.status} ${response.statusText}`);\n }\n\n const content = await response.text();\n const extension = inferSpecExtension(response.headers.get('content-type') ?? '', content);\n return cacheSpecContent(cwd, content, extension, 'openapi.cached');\n}\n\nexport async function discoverSpecUrl(backendBaseUrl: string): Promise<string> {\n const baseUrl = normalizeBaseUrl(backendBaseUrl);\n\n for (const candidatePath of DISCOVERY_PATHS) {\n const candidateUrl = `${baseUrl}${candidatePath}`;\n try {\n const response = await fetch(candidateUrl, { method: 'GET' });\n if (!response.ok) {\n continue;\n }\n\n const body = await response.text();\n const maybeSpec = body.includes('openapi') || body.includes('swagger');\n if (!maybeSpec) {\n continue;\n }\n\n return candidateUrl;\n } catch {\n continue;\n }\n }\n\n throw new Error(\n `Could not discover an OpenAPI spec under ${baseUrl}. Tried: ${DISCOVERY_PATHS.join(', ')}`,\n );\n}\n\nexport async function writeStarterSpec(cwd: string, specPath: string): Promise<string> {\n const resolvedPath = path.isAbsolute(specPath) ? specPath : path.join(cwd, specPath);\n await mkdir(path.dirname(resolvedPath), { recursive: true });\n await writeFile(resolvedPath, STARTER_SPEC, 'utf8');\n return resolvedPath;\n}","import path from 'node:path';\nimport Fastify, { type FastifyInstance, type FastifyRequest } from 'fastify';\nimport cors from '@fastify/cors';\nimport type { OpenAPIV3 } from 'openapi-types';\nimport { applyDslMutation } from './dsl.js';\nimport { DriftDetector } from './drift-detector.js';\nimport { buildRouteContexts } from './route-context.js';\nimport { inferSeedCollections, seedFromSchema } from './schema-seeder.js';\nimport { loadOpenApiDocument } from './spec-loader.js';\nimport { JsonStateStore } from './state-store.js';\nimport type { MockEngineConfig, MockOperationObject, RouteContext } from './types.js';\nimport { proxyRequest } from './proxy.js';\nimport { deepMerge, resolveFile } from './utils.js';\n\ntype ProxyBody = string | Uint8Array;\n\nfunction toProxyHeaders(inputHeaders: FastifyRequest['headers']): Record<string, string> {\n const passthrough = ['host', 'content-length', 'connection'] as const;\n const headers: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(inputHeaders)) {\n if (passthrough.includes(key as (typeof passthrough)[number])) {\n continue;\n }\n\n if (value === undefined) {\n continue;\n }\n\n headers[key] = Array.isArray(value) ? value.join(', ') : String(value);\n }\n\n return headers;\n}\n\nfunction toProxyBody(request: FastifyRequest): ProxyBody | undefined {\n if (request.method === 'GET' || request.method === 'HEAD') {\n return undefined;\n }\n\n if (request.body === undefined || request.body === null) {\n return undefined;\n }\n\n if (typeof request.body === 'string' || request.body instanceof Uint8Array) {\n return request.body;\n }\n\n return JSON.stringify(request.body);\n}\n\nfunction sendResponse(\n reply: { code: (statusCode: number) => typeof reply; send: (body?: unknown) => unknown },\n statusCode: number,\n body: unknown,\n) {\n if (statusCode === 204) {\n return reply.code(204).send();\n }\n\n return reply.code(statusCode).send(body);\n}\n\nfunction defaultStatusCode(route: RouteContext): number {\n if (route.method === 'post') {\n return 201;\n }\n\n if (route.method === 'delete') {\n return 204;\n }\n\n return 200;\n}\n\nfunction getRequestBody(request: FastifyRequest): Record<string, unknown> {\n if (!request.body || typeof request.body !== 'object' || Array.isArray(request.body)) {\n return {};\n }\n\n return request.body as Record<string, unknown>;\n}\n\nfunction getCollection(database: { collections: Record<string, unknown[]>; counters: Record<string, number> }, route: RouteContext): Record<string, unknown>[] {\n if (!database.collections[route.resourceName]) {\n database.collections[route.resourceName] = [];\n database.counters[route.resourceName] = 0;\n }\n\n return database.collections[route.resourceName] as Record<string, unknown>[];\n}\n\nfunction getCollectionByName(\n database: { collections: Record<string, unknown[]>; counters: Record<string, number> },\n collectionName: string,\n): Record<string, unknown>[] {\n if (!database.collections[collectionName]) {\n database.collections[collectionName] = [];\n database.counters[collectionName] = 0;\n }\n\n return database.collections[collectionName] as Record<string, unknown>[];\n}\n\nfunction computeIdKey(route: RouteContext): string {\n return route.pathParamName ?? 'id';\n}\n\nfunction normalizeComparable(value: unknown): string | number | boolean | undefined {\n if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {\n return String(value);\n }\n\n return undefined;\n}\n\nfunction nextId(database: { counters: Record<string, number> }, route: RouteContext): number {\n const current = database.counters[route.resourceName] ?? 0;\n const next = current + 1;\n database.counters[route.resourceName] = next;\n return next;\n}\n\nfunction allocateUniqueId(\n database: { counters: Record<string, number> },\n route: RouteContext,\n collection: Record<string, unknown>[],\n idKey: string,\n): number {\n let candidate = nextId(database, route);\n const hasCollision = (value: number) =>\n collection.some((item) => normalizeComparable(item[idKey]) === String(value));\n\n while (hasCollision(candidate)) {\n candidate = nextId(database, route);\n }\n\n return candidate;\n}\n\nfunction resolveEntity(route: RouteContext, collection: Record<string, unknown>[], request: FastifyRequest): Record<string, unknown> | undefined {\n const idKey = computeIdKey(route);\n const rawId = normalizeComparable(\n (request.params as Record<string, unknown>)[idKey] ?? (request.params as Record<string, unknown>).id,\n );\n return collection.find((item) => normalizeComparable(item[idKey]) === rawId);\n}\n\nfunction materializeMockBody(route: RouteContext, request: FastifyRequest): Record<string, unknown> {\n const requestBody = getRequestBody(request);\n const seeded = route.requestBodySchema ? seedFromSchema(route.requestBodySchema) : {};\n\n return deepMerge(\n (seeded && typeof seeded === 'object' && !Array.isArray(seeded) ? seeded : {}) as Record<string, unknown>,\n requestBody,\n );\n}\n\nfunction getNotFoundResponse(route: RouteContext): { statusCode: number; body: { message: string } } {\n return {\n statusCode: 404,\n body: { message: `${route.resourceName} not found` },\n };\n}\n\nfunction handleReadRoute(\n route: RouteContext,\n collection: Record<string, unknown>[],\n request: FastifyRequest,\n): { statusCode: number; body?: unknown } {\n if (route.isCollection) {\n return { statusCode: 200, body: collection };\n }\n\n const entity = resolveEntity(route, collection, request);\n return entity ? { statusCode: 200, body: entity } : getNotFoundResponse(route);\n}\n\nfunction handleCreateRoute(\n database: { counters: Record<string, number> },\n route: RouteContext,\n collection: Record<string, unknown>[],\n request: FastifyRequest,\n): { statusCode: number; body: Record<string, unknown> } {\n const entity = materializeMockBody(route, request);\n const idKey = computeIdKey(route);\n if (entity[idKey] === undefined) {\n entity[idKey] = allocateUniqueId(database, route, collection, idKey);\n }\n collection.push(entity);\n return { statusCode: route.successResponse?.statusCode ?? 201, body: entity };\n}\n\nfunction handleUpdateRoute(\n route: RouteContext,\n collection: Record<string, unknown>[],\n request: FastifyRequest,\n): { statusCode: number; body?: unknown } {\n const current = resolveEntity(route, collection, request);\n if (!current) {\n return getNotFoundResponse(route);\n }\n\n const merged = route.method === 'put'\n ? materializeMockBody(route, request)\n : deepMerge(current, getRequestBody(request));\n\n Object.assign(current, merged);\n return { statusCode: 200, body: current };\n}\n\nfunction handleDeleteRoute(\n route: RouteContext,\n collection: Record<string, unknown>[],\n request: FastifyRequest,\n): { statusCode: number; body?: unknown } {\n const entity = resolveEntity(route, collection, request);\n if (!entity) {\n return getNotFoundResponse(route);\n }\n\n const index = collection.indexOf(entity);\n collection.splice(index, 1);\n return { statusCode: 204 };\n}\n\nasync function handleMockRoute(\n store: JsonStateStore,\n route: RouteContext,\n request: FastifyRequest,\n): Promise<{ statusCode: number; body?: unknown }> {\n return store.withDatabase(async (database) => {\n const collection = getCollection(database, route);\n const extension = (route.operation as MockOperationObject)['x-mock-state'];\n\n if (extension) {\n const targetCollection = extension.target\n ? getCollectionByName(database, extension.target)\n : collection;\n const response = applyDslMutation(extension, request, targetCollection, computeIdKey(route));\n if (extension.response === 'none') {\n return { statusCode: 204 };\n }\n\n if (extension.response === 'collection') {\n return { statusCode: route.successResponse?.statusCode ?? 200, body: targetCollection };\n }\n\n return { statusCode: route.successResponse?.statusCode ?? defaultStatusCode(route), body: response };\n }\n\n switch (route.method) {\n case 'get': {\n return handleReadRoute(route, collection, request);\n }\n case 'post': {\n return handleCreateRoute(database, route, collection, request);\n }\n case 'put':\n case 'patch': {\n return handleUpdateRoute(route, collection, request);\n }\n case 'delete': {\n return handleDeleteRoute(route, collection, request);\n }\n }\n });\n}\n\nasync function handleProxyRoute(\n config: MockEngineConfig,\n route: RouteContext,\n detector: DriftDetector,\n request: FastifyRequest,\n): Promise<{ statusCode: number; headers: Record<string, string>; body?: unknown }> {\n const targetBaseUrl = config.driftCheckTarget;\n if (!targetBaseUrl) {\n throw new Error('Proxy target is not configured');\n }\n\n const headers = toProxyHeaders(request.headers);\n if (!headers['content-type'] && request.body !== undefined) {\n headers['content-type'] = 'application/json';\n }\n\n const result = await proxyRequest(targetBaseUrl, request.url, {\n method: request.method,\n headers,\n body: toProxyBody(request),\n });\n\n if (result.statusCode >= 200 && result.statusCode < 300) {\n detector.validate(\n route.method,\n route.path,\n result.statusCode,\n route.successResponse?.schema,\n result.body,\n );\n }\n\n return {\n statusCode: result.statusCode,\n headers: Object.fromEntries(result.headers.entries()),\n body: result.body,\n };\n}\n\nasync function registerRoutes(\n app: FastifyInstance,\n document: OpenAPIV3.Document,\n config: MockEngineConfig,\n store: JsonStateStore,\n): Promise<void> {\n const routes = buildRouteContexts(document);\n const detector = new DriftDetector(app.log);\n\n for (const route of routes) {\n app.route({\n method: route.method.toUpperCase(),\n url: route.fastifyPath,\n handler: async (request, reply) => {\n if (config.driftCheckTarget) {\n try {\n const proxied = await handleProxyRoute(config, route, detector, request);\n for (const [headerName, headerValue] of Object.entries(proxied.headers)) {\n if (headerName.toLowerCase() === 'content-length') {\n continue;\n }\n reply.header(headerName, headerValue);\n }\n return reply.code(proxied.statusCode).send(proxied.body);\n } catch (error) {\n app.log.error({ error }, `Proxy execution failed for ${route.method.toUpperCase()} ${route.path}`);\n if (!config.fallbackToMockOnProxyError) {\n throw error;\n }\n }\n }\n\n const mockResponse = await handleMockRoute(store, route, request);\n return sendResponse(reply, mockResponse.statusCode, mockResponse.body);\n },\n });\n }\n\n app.get('/__routes', async () =>\n routes.map((route) => ({\n method: route.method.toUpperCase(),\n path: route.fastifyPath,\n operationId: route.operationId,\n resource: route.resourceName,\n hasDsl: Boolean((route.operation as MockOperationObject)['x-mock-state']),\n })),\n );\n}\n\nexport async function createServer(config: MockEngineConfig): Promise<FastifyInstance> {\n const app = Fastify({\n logger: {\n level: config.verbose ? 'debug' : 'error',\n },\n });\n\n await app.register(cors, {\n origin: config.corsOrigin === '*' ? true : config.corsOrigin,\n methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],\n allowedHeaders: ['content-type', 'authorization'],\n credentials: false,\n });\n\n const document = await loadOpenApiDocument(config.specPath);\n const seedCollections = inferSeedCollections(document);\n const dbPath = resolveFile(path.dirname(config.specPath), config.dbPath);\n const store = new JsonStateStore(dbPath);\n await store.initialize(seedCollections);\n\n app.get('/__health', async () => ({ status: 'ok' }));\n app.get('/__spec', async () => document);\n\n await registerRoutes(app, document, config, store);\n return app;\n}","import path from 'node:path';\nimport type { OpenAPIV3 } from 'openapi-types';\n\nexport function isSchemaObject(\n schema?: OpenAPIV3.ReferenceObject | OpenAPIV3.SchemaObject,\n): schema is OpenAPIV3.SchemaObject {\n return Boolean(schema) && !('$ref' in (schema as OpenAPIV3.ReferenceObject));\n}\n\nexport function normalizeCollectionName(raw: string): string {\n return raw\n .replace(/[{}]/g, '')\n .split('/')\n .filter(Boolean)\n .at(-1)\n ?.replace(/[^a-zA-Z0-9]+/g, '_')\n .toLowerCase() ?? 'items';\n}\n\nexport function toFastifyPath(openApiPath: string): string {\n return openApiPath.replace(/\\{([^}]+)\\}/g, ':$1');\n}\n\nexport function singularize(value: string): string {\n if (value.endsWith('ies')) {\n return `${value.slice(0, -3)}y`;\n }\n\n if (value.endsWith('s')) {\n return value.slice(0, -1);\n }\n\n return value;\n}\n\nexport function resolveFile(baseDir: string, filePath: string): string {\n return path.isAbsolute(filePath) ? filePath : path.join(baseDir, filePath);\n}\n\nexport function getOperationId(\n method: string,\n openApiPath: string,\n operation: OpenAPIV3.OperationObject,\n): string {\n if (operation.operationId) {\n return operation.operationId;\n }\n\n const sanitizedPath = openApiPath\n .replace(/[{}]/g, '')\n .split('/')\n .filter(Boolean)\n .join('_');\n\n return `${method}_${sanitizedPath || 'root'}`;\n}\n\nexport function inferPathParamName(openApiPath: string): string | undefined {\n const match = openApiPath.match(/\\{([^}]+)\\}/);\n return match?.[1];\n}\n\nexport function deepClone<T>(value: T): T {\n return JSON.parse(JSON.stringify(value)) as T;\n}\n\nexport function deepMerge<T extends Record<string, unknown>>(\n base: T,\n patch: Record<string, unknown>,\n): T {\n const output: Record<string, unknown> = { ...base };\n\n for (const [key, value] of Object.entries(patch)) {\n if (\n value &&\n typeof value === 'object' &&\n !Array.isArray(value) &&\n output[key] &&\n typeof output[key] === 'object' &&\n !Array.isArray(output[key])\n ) {\n output[key] = deepMerge(\n output[key] as Record<string, unknown>,\n value as Record<string, unknown>,\n );\n } else {\n output[key] = value;\n }\n }\n\n return output as T;\n}\n\nexport function readContentType(headers: Headers): string {\n return headers.get('content-type')?.split(';')[0]?.trim().toLowerCase() ?? '';\n}\n\nexport function isJsonLikeContentType(contentType: string): boolean {\n return contentType === 'application/json' || contentType.endsWith('+json');\n}\n\nexport function toArray<T>(value: T | T[] | undefined): T[] {\n if (value === undefined) {\n return [];\n }\n\n return Array.isArray(value) ? value : [value];\n}","import type { FastifyRequest } from 'fastify';\nimport type { MockStateExtension } from './types.js';\nimport { deepMerge } from './utils.js';\n\nfunction resolvePathExpression(source: unknown, expression: string): unknown {\n return expression\n .split('.')\n .reduce<unknown>((value, segment) => (value && typeof value === 'object' ? (value as Record<string, unknown>)[segment] : undefined), source);\n}\n\nfunction evaluateTemplate(value: unknown, request: FastifyRequest): unknown {\n if (typeof value === 'string') {\n const match = value.match(/^\\{\\{\\s*(.+?)\\s*\\}\\}$/);\n if (!match) {\n return value;\n }\n\n return resolvePathExpression(\n {\n params: request.params,\n query: request.query,\n body: request.body,\n headers: request.headers,\n },\n match[1],\n );\n }\n\n if (Array.isArray(value)) {\n return value.map((entry) => evaluateTemplate(entry, request));\n }\n\n if (value && typeof value === 'object') {\n return Object.fromEntries(\n Object.entries(value).map(([key, entry]) => [key, evaluateTemplate(entry, request)]),\n );\n }\n\n return value;\n}\n\nexport function applyDslMutation(\n extension: MockStateExtension,\n request: FastifyRequest,\n collection: Record<string, unknown>[],\n defaultIdKey: string,\n): Record<string, unknown> | Record<string, unknown>[] | null {\n const idKey = extension.find_by ?? defaultIdKey;\n const targetId = (request.params as Record<string, unknown>)[idKey] ?? (request.params as Record<string, unknown>).id;\n const evaluatedAssign = (evaluateTemplate(extension.assign ?? {}, request) ?? {}) as Record<string, unknown>;\n const evaluatedSet = (evaluateTemplate(extension.set ?? {}, request) ?? {}) as Record<string, unknown>;\n\n switch (extension.action) {\n case 'create': {\n const candidate = {\n ...((request.body as Record<string, unknown> | undefined) ?? {}),\n ...evaluatedAssign,\n ...evaluatedSet,\n };\n collection.push(candidate);\n return candidate;\n }\n case 'append': {\n const item = {\n ...evaluatedAssign,\n ...evaluatedSet,\n };\n collection.push(item);\n return collection;\n }\n case 'replace': {\n const nextValue = {\n ...((request.body as Record<string, unknown> | undefined) ?? {}),\n ...evaluatedAssign,\n ...evaluatedSet,\n };\n const index = collection.findIndex((entry) => String(entry[idKey]) === String(targetId));\n if (index >= 0) {\n collection[index] = nextValue;\n }\n return nextValue;\n }\n case 'delete': {\n const index = collection.findIndex((entry) => String(entry[idKey]) === String(targetId));\n if (index >= 0) {\n const [removed] = collection.splice(index, 1);\n return removed;\n }\n return null;\n }\n case 'update':\n default: {\n const entity = collection.find((entry) => String(entry[idKey]) === String(targetId));\n if (!entity) {\n return null;\n }\n const merged = deepMerge(entity, {\n ...evaluatedAssign,\n ...evaluatedSet,\n });\n Object.assign(entity, merged);\n return entity;\n }\n }\n}","import Ajv, { type ErrorObject } from 'ajv';\nimport addFormats from 'ajv-formats';\nimport pc from 'picocolors';\nimport type { FastifyBaseLogger } from 'fastify';\nimport type { OpenAPIV3 } from 'openapi-types';\nimport type { DriftIssue } from './types.js';\n\nfunction formatErrors(errors: ErrorObject[] | null | undefined): string[] {\n return (errors ?? []).map((error) => {\n const location = error.instancePath || '/';\n return `${location} ${error.message ?? 'failed validation'}`.trim();\n });\n}\n\nexport class DriftDetector {\n private readonly ajv = new Ajv({ allErrors: true, strict: false });\n\n constructor(private readonly logger: FastifyBaseLogger) {\n addFormats(this.ajv);\n }\n\n validate(\n method: string,\n path: string,\n statusCode: number,\n schema: OpenAPIV3.SchemaObject | undefined,\n body: unknown,\n ): DriftIssue | null {\n if (!schema || body === undefined || body === null) {\n return null;\n }\n\n const validate = this.ajv.compile(schema);\n const valid = validate(body);\n\n if (valid) {\n return null;\n }\n\n const issue: DriftIssue = {\n method: method.toUpperCase(),\n path,\n statusCode,\n message: `Drift detected for ${method.toUpperCase()} ${path} (${statusCode})`,\n errors: formatErrors(validate.errors),\n };\n\n const errorLines = issue.errors.map((entry) => ` • ${entry}`).join('\\n');\n this.logger.error([\n `${pc.bgRed(pc.black(' 🚨 DRIFT DETECTED '))} ${pc.bold(issue.method)} ${issue.path} (${statusCode})`,\n errorLines,\n ].join('\\n'));\n\n return issue;\n }\n}","import type { OpenAPIV3 } from 'openapi-types';\nimport type { HttpMethod, RouteContext } from './types.js';\nimport {\n getOperationId,\n inferPathParamName,\n isSchemaObject,\n normalizeCollectionName,\n toFastifyPath,\n} from './utils.js';\n\nconst SUPPORTED_METHODS: HttpMethod[] = ['get', 'post', 'put', 'patch', 'delete'];\n\nfunction getRequestBodySchema(\n operation: OpenAPIV3.OperationObject,\n): OpenAPIV3.SchemaObject | undefined {\n const content = operation.requestBody && !('$ref' in operation.requestBody)\n ? operation.requestBody.content?.['application/json']\n : undefined;\n\n return isSchemaObject(content?.schema) ? content.schema : undefined;\n}\n\nfunction getSuccessResponse(\n operation: OpenAPIV3.OperationObject,\n): RouteContext['successResponse'] {\n const preferredCodes = ['200', '201', '202', '204'];\n\n for (const code of preferredCodes) {\n const response = operation.responses?.[code];\n if (!response || '$ref' in response) {\n continue;\n }\n\n const schema = response.content?.['application/json']?.schema;\n return {\n statusCode: Number(code),\n schema: isSchemaObject(schema) ? schema : undefined,\n };\n }\n\n return undefined;\n}\n\nexport function buildRouteContexts(document: OpenAPIV3.Document): RouteContext[] {\n const routes: RouteContext[] = [];\n\n for (const [openApiPath, pathItem] of Object.entries(document.paths ?? {})) {\n if (!pathItem || '$ref' in pathItem) {\n continue;\n }\n\n for (const method of SUPPORTED_METHODS) {\n const operation = pathItem[method];\n if (!operation || '$ref' in operation) {\n continue;\n }\n\n const pathParamName = inferPathParamName(openApiPath);\n const resourceName = normalizeCollectionName(\n pathParamName ? openApiPath.replace(/\\/\\{[^}]+\\}$/, '') : openApiPath,\n );\n const route: RouteContext = {\n method,\n path: openApiPath,\n fastifyPath: toFastifyPath(openApiPath),\n operation,\n operationId: getOperationId(method, openApiPath, operation),\n resourceName,\n isCollection: !pathParamName,\n pathParamName,\n requestBodySchema: getRequestBodySchema(operation),\n successResponse: getSuccessResponse(operation),\n };\n routes.push(route);\n }\n }\n\n return routes;\n}","import { faker } from '@faker-js/faker';\nimport type { OpenAPIV3 } from 'openapi-types';\nimport { isSchemaObject, singularize } from './utils.js';\n\nfunction seedPrimitive(schema: OpenAPIV3.SchemaObject): unknown {\n if (schema.enum?.length) {\n return schema.enum[0];\n }\n\n switch (schema.type) {\n case 'string': {\n if (schema.format === 'email') {\n return faker.internet.email();\n }\n\n if (schema.format === 'date-time') {\n return faker.date.recent().toISOString();\n }\n\n if (schema.format === 'uuid') {\n return faker.string.uuid();\n }\n\n return faker.lorem.words(2);\n }\n case 'integer':\n return faker.number.int({ min: 1, max: 1000 });\n case 'number':\n return faker.number.float({ min: 1, max: 1000, fractionDigits: 2 });\n case 'boolean':\n return faker.datatype.boolean();\n default:\n return null;\n }\n}\n\nexport function seedFromSchema(\n schema?: OpenAPIV3.SchemaObject,\n depth = 0,\n): unknown {\n if (!schema || depth > 4) {\n return null;\n }\n\n if (schema.oneOf?.length && isSchemaObject(schema.oneOf[0])) {\n return seedFromSchema(schema.oneOf[0], depth + 1);\n }\n\n if (schema.anyOf?.length && isSchemaObject(schema.anyOf[0])) {\n return seedFromSchema(schema.anyOf[0], depth + 1);\n }\n\n if (schema.allOf?.length) {\n return schema.allOf.reduce<Record<string, unknown>>((accumulator, item) => {\n if (!isSchemaObject(item)) {\n return accumulator;\n }\n\n const seeded = seedFromSchema(item, depth + 1);\n if (seeded && typeof seeded === 'object' && !Array.isArray(seeded)) {\n Object.assign(accumulator, seeded);\n }\n\n return accumulator;\n }, {});\n }\n\n if (schema.type === 'array') {\n const item = isSchemaObject(schema.items) ? schema.items : undefined;\n return [seedFromSchema(item, depth + 1), seedFromSchema(item, depth + 1)].filter(\n (value) => value !== null,\n );\n }\n\n if (schema.type === 'object' || schema.properties) {\n const entries = Object.entries(schema.properties ?? {}).map(([key, value]) => {\n if (!isSchemaObject(value)) {\n return [key, null] as const;\n }\n\n return [key, seedFromSchema(value, depth + 1)] as const;\n });\n\n return Object.fromEntries(entries);\n }\n\n return seedPrimitive(schema);\n}\n\nexport function inferSeedCollections(document: OpenAPIV3.Document): Record<string, unknown[]> {\n const collections: Record<string, unknown[]> = {};\n\n for (const [schemaName, schemaValue] of Object.entries(document.components?.schemas ?? {})) {\n if (!isSchemaObject(schemaValue) || schemaValue.type !== 'object') {\n continue;\n }\n\n const collectionName = `${singularize(schemaName).toLowerCase()}s`;\n collections[collectionName] = Array.from({ length: 3 }, () => seedFromSchema(schemaValue)) as unknown[];\n }\n\n return collections;\n}","import SwaggerParser from '@apidevtools/swagger-parser';\nimport type { OpenAPIV3 } from 'openapi-types';\n\nexport async function loadOpenApiDocument(specPath: string): Promise<OpenAPIV3.Document> {\n const document = (await SwaggerParser.dereference(specPath)) as OpenAPIV3.Document;\n\n if (!document.openapi?.startsWith('3.')) {\n throw new Error(`Only OpenAPI 3.x specs are supported. Received: ${document.openapi}`);\n }\n\n return document;\n}","import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport type { MockDatabase } from './types.js';\nimport { deepClone } from './utils.js';\n\nfunction normalizeDatabase(input: Partial<MockDatabase> | undefined): MockDatabase {\n return {\n collections: input?.collections ?? {},\n counters: input?.counters ?? {},\n };\n}\n\nexport class JsonStateStore {\n readonly filePath: string;\n\n constructor(filePath: string) {\n this.filePath = filePath;\n }\n\n async initialize(seedCollections: Record<string, unknown[]>): Promise<MockDatabase> {\n await mkdir(path.dirname(this.filePath), { recursive: true });\n\n try {\n const existing = await this.read();\n let changed = false;\n\n for (const [collectionName, items] of Object.entries(seedCollections)) {\n if (!existing.collections[collectionName]) {\n existing.collections[collectionName] = deepClone(items);\n existing.counters[collectionName] = items.length;\n changed = true;\n }\n }\n\n if (changed) {\n await this.write(existing);\n }\n\n return existing;\n } catch {\n const initial: MockDatabase = {\n collections: deepClone(seedCollections),\n counters: Object.fromEntries(\n Object.entries(seedCollections).map(([name, items]) => [name, items.length]),\n ),\n };\n await this.write(initial);\n return initial;\n }\n }\n\n async read(): Promise<MockDatabase> {\n const raw = await readFile(this.filePath, 'utf8');\n return normalizeDatabase(JSON.parse(raw) as Partial<MockDatabase>);\n }\n\n async write(database: MockDatabase): Promise<void> {\n await writeFile(this.filePath, `${JSON.stringify(database, null, 2)}\\n`, 'utf8');\n }\n\n async withDatabase<T>(\n updater: (database: MockDatabase) => Promise<T> | T,\n ): Promise<T> {\n const database = await this.read();\n const result = await updater(database);\n await this.write(database);\n return result;\n }\n}","import { readContentType, isJsonLikeContentType } from './utils.js';\nimport type { ProxyExecutionResult } from './types.js';\n\nexport async function proxyRequest(\n targetBaseUrl: string,\n path: string,\n init: RequestInit,\n): Promise<ProxyExecutionResult> {\n const response = await fetch(new URL(path, targetBaseUrl), init);\n const contentType = readContentType(response.headers);\n\n let body: unknown;\n let rawBody: string | undefined;\n\n if (response.status !== 204) {\n rawBody = await response.text();\n if (rawBody && isJsonLikeContentType(contentType)) {\n body = JSON.parse(rawBody);\n } else if (rawBody) {\n body = rawBody;\n }\n }\n\n return {\n ok: response.ok,\n statusCode: response.status,\n headers: response.headers,\n body,\n rawBody,\n };\n}"],"mappings":"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","names":["mkdir","writeFile","path","path","mkdir","writeFile","path","path","path","mkdir","writeFile","path","mkdir","path","writeFile","path","path"]}
1
+ {"version":3,"sources":["../src/cli.ts","../src/config.ts","../src/onboarding.ts","../src/server.ts","../src/utils.ts","../src/dsl.ts","../src/drift-detector.ts","../src/route-context.ts","../src/schema-seeder.ts","../src/spec-loader.ts","../src/state-store.ts","../src/proxy.ts"],"sourcesContent":["import process from 'node:process';\nimport { createCli, resolveServeConfig, writeStarterConfig } from './config.js';\nimport { createServer } from './server.js';\n\nfunction renderStartupBanner(config: {\n host: string;\n port: number;\n specPath: string;\n dbPath: string;\n driftCheckTarget?: string;\n}): string {\n const lines = [\n `🚀 Contract Drift Detection running at http://${config.host}:${config.port}`,\n `- Spec: ${config.specPath}`,\n `- DB: ${config.dbPath}`,\n `- Mode: ${config.driftCheckTarget ? `proxy + drift-check (${config.driftCheckTarget})` : 'stateful mock'}`,\n '- Health: GET /__health',\n '- Routes: GET /__routes',\n ];\n\n return `${lines.join('\\n')}\\n`;\n}\n\nasync function main(): Promise<void> {\n const cli = createCli();\n const serveCommand = cli.commands.find((command) => command.name() === 'serve');\n const initCommand = cli.commands.find((command) => command.name() === 'init');\n const quickstartCommand = cli.commands.find((command) => command.name() === 'quickstart');\n\n const startServer = async (rawOptions: Record<string, string | boolean | undefined>) => {\n const config = await resolveServeConfig(process.cwd(), rawOptions);\n const server = await createServer(config);\n await server.listen({ port: config.port, host: config.host });\n process.stdout.write(renderStartupBanner(config));\n };\n\n serveCommand?.action(async function () {\n await startServer(this.opts());\n });\n\n cli.action(async () => {\n const options = cli.opts<Record<string, string | boolean | undefined>>();\n if (!options.spec && !options.specUrl && !options.discover) {\n cli.outputHelp();\n return;\n }\n\n await startServer(options);\n });\n\n initCommand?.action(async function () {\n const options = this.opts();\n const targetPath = await writeStarterConfig(process.cwd(), {\n spec: String(options.spec),\n db: String(options.db),\n host: String(options.host),\n port: Number(options.port),\n template: options.template === 'none' ? 'none' : 'rest-crud',\n });\n process.stdout.write(`Created ${targetPath}\\n`);\n });\n\n quickstartCommand?.action(async function () {\n const options = this.opts();\n const cwd = process.cwd();\n const specPath = String(options.spec ?? 'openapi.yaml');\n\n await writeStarterConfig(cwd, {\n spec: specPath,\n db: String(options.db ?? '.mock-db.json'),\n host: String(options.host ?? '0.0.0.0'),\n port: Number(options.port ?? 4010),\n template: 'rest-crud',\n });\n\n const config = await resolveServeConfig(cwd, {\n spec: specPath,\n db: String(options.db ?? '.mock-db.json'),\n host: String(options.host ?? '0.0.0.0'),\n port: String(options.port ?? 4010),\n corsOrigin: String(options.corsOrigin ?? '*'),\n verbose: Boolean(options.verbose),\n });\n\n const server = await createServer(config);\n await server.listen({ port: config.port, host: config.host });\n process.stdout.write(renderStartupBanner(config));\n });\n\n await cli.parseAsync(process.argv);\n}\n\nawait main().catch((error) => {\n if (error instanceof Error) {\n process.stderr.write(`Error: ${error.message}\\n`);\n if (process.env.CDD_SHOW_STACK === '1') {\n process.stderr.write(`${error.stack}\\n`);\n }\n } else {\n process.stderr.write(`${String(error)}\\n`);\n }\n process.exitCode = 1;\n});","import { mkdir, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport { Command } from 'commander';\nimport type { InitConfig, MockEngineConfig } from './types.js';\nimport { cacheSpecFromUrl, discoverSpecUrl, writeStarterSpec } from './onboarding.js';\n\nfunction resolvePathFromCwd(cwd: string, value: string): string {\n return path.isAbsolute(value) ? value : path.join(cwd, value);\n}\n\nfunction applyServeOptions(command: Command): Command {\n return command\n .option('--spec <path>', 'Path to an OpenAPI 3.x file')\n .option('--spec-url <url>', 'Remote URL to an OpenAPI 3.x file')\n .option('--discover <backend-url>', 'Discover OpenAPI from a backend URL and cache it locally')\n .option('--port <port>', 'Port to bind the server', '4010')\n .option('--host <host>', 'Host to bind the server', '0.0.0.0')\n .option('--db <path>', 'JSON database path', '.mock-db.json')\n .option('--cors-origin <origin>', 'CORS origin (default is *)', '*')\n .option('--drift-check <url>', 'Forward traffic to a real backend and validate responses')\n .option('--fallback-to-mock', 'Fallback to mock responses when proxying fails', false)\n .option('--verbose', 'Enable verbose logging', false);\n}\n\nexport function createCli(): Command {\n const program = new Command();\n\n program\n .name('contract-drift-detection')\n .description('Stateful OpenAPI mock server with contract drift detection')\n .version('0.1.0');\n\n applyServeOptions(program);\n\n applyServeOptions(program.command('serve').description('Start the mock engine'));\n\n program\n .command('init')\n .description('Create a starter config for the current workspace')\n .option('--spec <path>', 'Default OpenAPI path', 'openapi.yaml')\n .option('--template <name>', 'Template to generate (rest-crud | none)', 'rest-crud')\n .option('--db <path>', 'Default JSON database path', '.mock-db.json')\n .option('--port <port>', 'Default port', '4010')\n .option('--host <host>', 'Default host', '0.0.0.0');\n\n program\n .command('quickstart')\n .description('Generate a starter OpenAPI file and start the server immediately')\n .option('--spec <path>', 'Starter OpenAPI path', 'openapi.yaml')\n .option('--port <port>', 'Port to bind the server', '4010')\n .option('--host <host>', 'Host to bind the server', '0.0.0.0')\n .option('--db <path>', 'JSON database path', '.mock-db.json')\n .option('--cors-origin <origin>', 'CORS origin (default is *)', '*')\n .option('--verbose', 'Enable verbose logging', false);\n\n return program;\n}\n\nexport async function resolveServeConfig(\n cwd: string,\n options: Record<string, string | boolean | undefined>,\n): Promise<MockEngineConfig> {\n let resolvedSpecPath: string | undefined;\n\n if (options.spec) {\n resolvedSpecPath = resolvePathFromCwd(cwd, String(options.spec));\n } else if (options.specUrl) {\n resolvedSpecPath = await cacheSpecFromUrl(cwd, String(options.specUrl));\n } else if (options.discover) {\n const discoveredSpecUrl = await discoverSpecUrl(String(options.discover));\n resolvedSpecPath = await cacheSpecFromUrl(cwd, discoveredSpecUrl);\n }\n\n if (!resolvedSpecPath) {\n throw new Error('Provide one of --spec, --spec-url, or --discover');\n }\n\n return {\n specPath: resolvedSpecPath,\n port: Number(options.port ?? 4010),\n host: String(options.host ?? '0.0.0.0'),\n dbPath: resolvePathFromCwd(cwd, String(options.db ?? '.mock-db.json')),\n corsOrigin: String(options.corsOrigin ?? '*'),\n driftCheckTarget: options.driftCheck ? String(options.driftCheck) : undefined,\n fallbackToMockOnProxyError: Boolean(options.fallbackToMock),\n verbose: Boolean(options.verbose),\n };\n}\n\nexport async function writeStarterConfig(cwd: string, initConfig: InitConfig): Promise<string> {\n if (initConfig.template === 'rest-crud') {\n await writeStarterSpec(cwd, initConfig.spec);\n }\n\n const targetPath = path.join(cwd, 'contract-drift.config.json');\n await mkdir(cwd, { recursive: true });\n await writeFile(\n targetPath,\n `${JSON.stringify(\n {\n spec: initConfig.spec,\n db: initConfig.db,\n host: initConfig.host,\n port: initConfig.port,\n },\n null,\n 2,\n )}\\n`,\n 'utf8',\n );\n return targetPath;\n}","import { mkdir, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\n\nconst DISCOVERY_PATHS = [\n '/openapi.json',\n '/openapi.yaml',\n '/api/openapi.json',\n '/api/openapi.yaml',\n '/swagger.json',\n '/swagger/v1/swagger.json',\n '/v3/api-docs',\n '/api-docs-json',\n '/api-docs',\n '/docs/openapi.json',\n] as const;\n\nconst STARTER_SPEC = `openapi: 3.0.3\ninfo:\n title: Contract Drift Detection Starter API\n version: 1.0.0\npaths:\n /users:\n get:\n responses:\n '200':\n description: ok\n content:\n application/json:\n schema:\n type: array\n items:\n $ref: '#/components/schemas/User'\n post:\n requestBody:\n required: true\n content:\n application/json:\n schema:\n $ref: '#/components/schemas/UserCreateInput'\n responses:\n '201':\n description: created\n content:\n application/json:\n schema:\n $ref: '#/components/schemas/User'\n /users/{id}:\n get:\n parameters:\n - $ref: '#/components/parameters/UserId'\n responses:\n '200':\n description: ok\n content:\n application/json:\n schema:\n $ref: '#/components/schemas/User'\n patch:\n parameters:\n - $ref: '#/components/parameters/UserId'\n requestBody:\n required: true\n content:\n application/json:\n schema:\n $ref: '#/components/schemas/UserPatchInput'\n responses:\n '200':\n description: updated\n content:\n application/json:\n schema:\n $ref: '#/components/schemas/User'\n delete:\n parameters:\n - $ref: '#/components/parameters/UserId'\n responses:\n '204':\n description: deleted\n /tickets:\n get:\n responses:\n '200':\n description: ok\n content:\n application/json:\n schema:\n type: array\n items:\n $ref: '#/components/schemas/Ticket'\n /tickets/{id}/resolve:\n post:\n parameters:\n - $ref: '#/components/parameters/TicketId'\n x-mock-state:\n action: update\n target: tickets\n find_by: id\n set:\n status: resolved\n responses:\n '200':\n description: ok\n content:\n application/json:\n schema:\n $ref: '#/components/schemas/Ticket'\ncomponents:\n parameters:\n UserId:\n name: id\n in: path\n required: true\n schema:\n type: integer\n TicketId:\n name: id\n in: path\n required: true\n schema:\n type: integer\n schemas:\n User:\n type: object\n required: [id, name, email]\n properties:\n id:\n type: integer\n name:\n type: string\n email:\n type: string\n format: email\n UserCreateInput:\n type: object\n required: [name, email]\n properties:\n name:\n type: string\n email:\n type: string\n format: email\n UserPatchInput:\n type: object\n properties:\n name:\n type: string\n email:\n type: string\n format: email\n Ticket:\n type: object\n required: [id, title, status]\n properties:\n id:\n type: integer\n title:\n type: string\n status:\n type: string\n enum: [open, resolved]\n`;\n\nfunction normalizeBaseUrl(value: string): string {\n return value.endsWith('/') ? value.slice(0, -1) : value;\n}\n\nfunction inferSpecExtension(contentType: string, content: string): 'json' | 'yaml' {\n const normalized = contentType.toLowerCase();\n if (normalized.includes('json')) {\n return 'json';\n }\n\n const trimmed = content.trim();\n return trimmed.startsWith('{') || trimmed.startsWith('[') ? 'json' : 'yaml';\n}\n\nexport async function cacheSpecContent(\n cwd: string,\n specContent: string,\n extension: 'json' | 'yaml',\n filePrefix: string,\n): Promise<string> {\n const cacheDir = path.join(cwd, '.cdd');\n await mkdir(cacheDir, { recursive: true });\n const filePath = path.join(cacheDir, `${filePrefix}.${extension}`);\n await writeFile(filePath, specContent, 'utf8');\n return filePath;\n}\n\nexport async function cacheSpecFromUrl(cwd: string, specUrl: string): Promise<string> {\n const response = await fetch(specUrl);\n if (!response.ok) {\n throw new Error(`Failed to download OpenAPI spec from ${specUrl}: ${response.status} ${response.statusText}`);\n }\n\n const content = await response.text();\n const extension = inferSpecExtension(response.headers.get('content-type') ?? '', content);\n return cacheSpecContent(cwd, content, extension, 'openapi.cached');\n}\n\nexport async function discoverSpecUrl(backendBaseUrl: string): Promise<string> {\n const baseUrl = normalizeBaseUrl(backendBaseUrl);\n\n for (const candidatePath of DISCOVERY_PATHS) {\n const candidateUrl = `${baseUrl}${candidatePath}`;\n try {\n const response = await fetch(candidateUrl, { method: 'GET' });\n if (!response.ok) {\n continue;\n }\n\n const body = await response.text();\n const maybeSpec = body.includes('openapi') || body.includes('swagger');\n if (!maybeSpec) {\n continue;\n }\n\n return candidateUrl;\n } catch {\n continue;\n }\n }\n\n throw new Error(\n [\n `Could not discover an OpenAPI spec under ${baseUrl}.`,\n `Tried: ${DISCOVERY_PATHS.join(', ')}`,\n 'Next steps:',\n '1) Use --spec-url with your exact endpoint (for example, /v3/api-docs or /swagger/v1/swagger.json).',\n '2) If backend does not expose OpenAPI, run quickstart: npx contract-drift-detection@latest quickstart',\n '3) Or generate a starter file: npx contract-drift-detection@latest init',\n ].join('\\n'),\n );\n}\n\nexport async function writeStarterSpec(cwd: string, specPath: string): Promise<string> {\n const resolvedPath = path.isAbsolute(specPath) ? specPath : path.join(cwd, specPath);\n await mkdir(path.dirname(resolvedPath), { recursive: true });\n await writeFile(resolvedPath, STARTER_SPEC, 'utf8');\n return resolvedPath;\n}","import path from 'node:path';\nimport Fastify, { type FastifyInstance, type FastifyRequest } from 'fastify';\nimport cors from '@fastify/cors';\nimport type { OpenAPIV3 } from 'openapi-types';\nimport { applyDslMutation } from './dsl.js';\nimport { DriftDetector } from './drift-detector.js';\nimport { buildRouteContexts } from './route-context.js';\nimport { inferSeedCollections, seedFromSchema } from './schema-seeder.js';\nimport { loadOpenApiDocument } from './spec-loader.js';\nimport { JsonStateStore } from './state-store.js';\nimport type { MockEngineConfig, MockOperationObject, RouteContext } from './types.js';\nimport { proxyRequest } from './proxy.js';\nimport { deepMerge, resolveFile } from './utils.js';\n\ntype ProxyBody = string | Uint8Array;\n\nfunction toProxyHeaders(inputHeaders: FastifyRequest['headers']): Record<string, string> {\n const passthrough = ['host', 'content-length', 'connection'] as const;\n const headers: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(inputHeaders)) {\n if (passthrough.includes(key as (typeof passthrough)[number])) {\n continue;\n }\n\n if (value === undefined) {\n continue;\n }\n\n headers[key] = Array.isArray(value) ? value.join(', ') : String(value);\n }\n\n return headers;\n}\n\nfunction toProxyBody(request: FastifyRequest): ProxyBody | undefined {\n if (request.method === 'GET' || request.method === 'HEAD') {\n return undefined;\n }\n\n if (request.body === undefined || request.body === null) {\n return undefined;\n }\n\n if (typeof request.body === 'string' || request.body instanceof Uint8Array) {\n return request.body;\n }\n\n return JSON.stringify(request.body);\n}\n\nfunction sendResponse(\n reply: { code: (statusCode: number) => typeof reply; send: (body?: unknown) => unknown },\n statusCode: number,\n body: unknown,\n) {\n if (statusCode === 204) {\n return reply.code(204).send();\n }\n\n return reply.code(statusCode).send(body);\n}\n\nfunction defaultStatusCode(route: RouteContext): number {\n if (route.method === 'post') {\n return 201;\n }\n\n if (route.method === 'delete') {\n return 204;\n }\n\n return 200;\n}\n\nfunction getRequestBody(request: FastifyRequest): Record<string, unknown> {\n if (!request.body || typeof request.body !== 'object' || Array.isArray(request.body)) {\n return {};\n }\n\n return request.body as Record<string, unknown>;\n}\n\nfunction getCollection(database: { collections: Record<string, unknown[]>; counters: Record<string, number> }, route: RouteContext): Record<string, unknown>[] {\n if (!database.collections[route.resourceName]) {\n database.collections[route.resourceName] = [];\n database.counters[route.resourceName] = 0;\n }\n\n return database.collections[route.resourceName] as Record<string, unknown>[];\n}\n\nfunction getCollectionByName(\n database: { collections: Record<string, unknown[]>; counters: Record<string, number> },\n collectionName: string,\n): Record<string, unknown>[] {\n if (!database.collections[collectionName]) {\n database.collections[collectionName] = [];\n database.counters[collectionName] = 0;\n }\n\n return database.collections[collectionName] as Record<string, unknown>[];\n}\n\nfunction computeIdKey(route: RouteContext): string {\n return route.pathParamName ?? 'id';\n}\n\nfunction normalizeComparable(value: unknown): string | number | boolean | undefined {\n if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {\n return String(value);\n }\n\n return undefined;\n}\n\nfunction nextId(database: { counters: Record<string, number> }, route: RouteContext): number {\n const current = database.counters[route.resourceName] ?? 0;\n const next = current + 1;\n database.counters[route.resourceName] = next;\n return next;\n}\n\nfunction allocateUniqueId(\n database: { counters: Record<string, number> },\n route: RouteContext,\n collection: Record<string, unknown>[],\n idKey: string,\n): number {\n let candidate = nextId(database, route);\n const hasCollision = (value: number) =>\n collection.some((item) => normalizeComparable(item[idKey]) === String(value));\n\n while (hasCollision(candidate)) {\n candidate = nextId(database, route);\n }\n\n return candidate;\n}\n\nfunction resolveEntity(route: RouteContext, collection: Record<string, unknown>[], request: FastifyRequest): Record<string, unknown> | undefined {\n const idKey = computeIdKey(route);\n const rawId = normalizeComparable(\n (request.params as Record<string, unknown>)[idKey] ?? (request.params as Record<string, unknown>).id,\n );\n return collection.find((item) => normalizeComparable(item[idKey]) === rawId);\n}\n\nfunction materializeMockBody(route: RouteContext, request: FastifyRequest): Record<string, unknown> {\n const requestBody = getRequestBody(request);\n const seeded = route.requestBodySchema ? seedFromSchema(route.requestBodySchema) : {};\n\n return deepMerge(\n (seeded && typeof seeded === 'object' && !Array.isArray(seeded) ? seeded : {}) as Record<string, unknown>,\n requestBody,\n );\n}\n\nfunction getNotFoundResponse(route: RouteContext): { statusCode: number; body: { message: string } } {\n return {\n statusCode: 404,\n body: { message: `${route.resourceName} not found` },\n };\n}\n\nfunction handleReadRoute(\n route: RouteContext,\n collection: Record<string, unknown>[],\n request: FastifyRequest,\n): { statusCode: number; body?: unknown } {\n if (route.isCollection) {\n return { statusCode: 200, body: collection };\n }\n\n const entity = resolveEntity(route, collection, request);\n return entity ? { statusCode: 200, body: entity } : getNotFoundResponse(route);\n}\n\nfunction handleCreateRoute(\n database: { counters: Record<string, number> },\n route: RouteContext,\n collection: Record<string, unknown>[],\n request: FastifyRequest,\n): { statusCode: number; body: Record<string, unknown> } {\n const entity = materializeMockBody(route, request);\n const idKey = computeIdKey(route);\n if (entity[idKey] === undefined) {\n entity[idKey] = allocateUniqueId(database, route, collection, idKey);\n }\n collection.push(entity);\n return { statusCode: route.successResponse?.statusCode ?? 201, body: entity };\n}\n\nfunction handleUpdateRoute(\n route: RouteContext,\n collection: Record<string, unknown>[],\n request: FastifyRequest,\n): { statusCode: number; body?: unknown } {\n const current = resolveEntity(route, collection, request);\n if (!current) {\n return getNotFoundResponse(route);\n }\n\n const merged = route.method === 'put'\n ? materializeMockBody(route, request)\n : deepMerge(current, getRequestBody(request));\n\n Object.assign(current, merged);\n return { statusCode: 200, body: current };\n}\n\nfunction handleDeleteRoute(\n route: RouteContext,\n collection: Record<string, unknown>[],\n request: FastifyRequest,\n): { statusCode: number; body?: unknown } {\n const entity = resolveEntity(route, collection, request);\n if (!entity) {\n return getNotFoundResponse(route);\n }\n\n const index = collection.indexOf(entity);\n collection.splice(index, 1);\n return { statusCode: 204 };\n}\n\nasync function handleMockRoute(\n store: JsonStateStore,\n route: RouteContext,\n request: FastifyRequest,\n): Promise<{ statusCode: number; body?: unknown }> {\n return store.withDatabase(async (database) => {\n const collection = getCollection(database, route);\n const extension = (route.operation as MockOperationObject)['x-mock-state'];\n\n if (extension) {\n const targetCollection = extension.target\n ? getCollectionByName(database, extension.target)\n : collection;\n const response = applyDslMutation(extension, request, targetCollection, computeIdKey(route));\n if (extension.response === 'none') {\n return { statusCode: 204 };\n }\n\n if (extension.response === 'collection') {\n return { statusCode: route.successResponse?.statusCode ?? 200, body: targetCollection };\n }\n\n return { statusCode: route.successResponse?.statusCode ?? defaultStatusCode(route), body: response };\n }\n\n switch (route.method) {\n case 'get': {\n return handleReadRoute(route, collection, request);\n }\n case 'post': {\n return handleCreateRoute(database, route, collection, request);\n }\n case 'put':\n case 'patch': {\n return handleUpdateRoute(route, collection, request);\n }\n case 'delete': {\n return handleDeleteRoute(route, collection, request);\n }\n }\n });\n}\n\nasync function handleProxyRoute(\n config: MockEngineConfig,\n route: RouteContext,\n detector: DriftDetector,\n request: FastifyRequest,\n): Promise<{ statusCode: number; headers: Record<string, string>; body?: unknown }> {\n const targetBaseUrl = config.driftCheckTarget;\n if (!targetBaseUrl) {\n throw new Error('Proxy target is not configured');\n }\n\n const headers = toProxyHeaders(request.headers);\n if (!headers['content-type'] && request.body !== undefined) {\n headers['content-type'] = 'application/json';\n }\n\n const result = await proxyRequest(targetBaseUrl, request.url, {\n method: request.method,\n headers,\n body: toProxyBody(request),\n });\n\n if (result.statusCode >= 200 && result.statusCode < 300) {\n detector.validate(\n route.method,\n route.path,\n result.statusCode,\n route.successResponse?.schema,\n result.body,\n );\n }\n\n return {\n statusCode: result.statusCode,\n headers: Object.fromEntries(result.headers.entries()),\n body: result.body,\n };\n}\n\nasync function registerRoutes(\n app: FastifyInstance,\n document: OpenAPIV3.Document,\n config: MockEngineConfig,\n store: JsonStateStore,\n): Promise<void> {\n const routes = buildRouteContexts(document);\n const detector = new DriftDetector(app.log);\n\n for (const route of routes) {\n app.route({\n method: route.method.toUpperCase(),\n url: route.fastifyPath,\n handler: async (request, reply) => {\n if (config.driftCheckTarget) {\n try {\n const proxied = await handleProxyRoute(config, route, detector, request);\n for (const [headerName, headerValue] of Object.entries(proxied.headers)) {\n if (headerName.toLowerCase() === 'content-length') {\n continue;\n }\n reply.header(headerName, headerValue);\n }\n return reply.code(proxied.statusCode).send(proxied.body);\n } catch (error) {\n app.log.error({ error }, `Proxy execution failed for ${route.method.toUpperCase()} ${route.path}`);\n if (!config.fallbackToMockOnProxyError) {\n throw error;\n }\n }\n }\n\n const mockResponse = await handleMockRoute(store, route, request);\n return sendResponse(reply, mockResponse.statusCode, mockResponse.body);\n },\n });\n }\n\n app.get('/__routes', async () =>\n routes.map((route) => ({\n method: route.method.toUpperCase(),\n path: route.fastifyPath,\n operationId: route.operationId,\n resource: route.resourceName,\n hasDsl: Boolean((route.operation as MockOperationObject)['x-mock-state']),\n })),\n );\n}\n\nexport async function createServer(config: MockEngineConfig): Promise<FastifyInstance> {\n const app = Fastify({\n logger: {\n level: config.verbose ? 'debug' : 'error',\n },\n });\n\n await app.register(cors, {\n origin: config.corsOrigin === '*' ? true : config.corsOrigin,\n methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],\n allowedHeaders: ['content-type', 'authorization'],\n credentials: false,\n });\n\n const document = await loadOpenApiDocument(config.specPath);\n const seedCollections = inferSeedCollections(document);\n const dbPath = resolveFile(path.dirname(config.specPath), config.dbPath);\n const store = new JsonStateStore(dbPath);\n await store.initialize(seedCollections);\n\n app.get('/__health', async () => ({ status: 'ok' }));\n app.get('/__spec', async () => document);\n\n await registerRoutes(app, document, config, store);\n return app;\n}","import path from 'node:path';\nimport type { OpenAPIV3 } from 'openapi-types';\n\nexport function isSchemaObject(\n schema?: OpenAPIV3.ReferenceObject | OpenAPIV3.SchemaObject,\n): schema is OpenAPIV3.SchemaObject {\n return Boolean(schema) && !('$ref' in (schema as OpenAPIV3.ReferenceObject));\n}\n\nexport function normalizeCollectionName(raw: string): string {\n return raw\n .replace(/[{}]/g, '')\n .split('/')\n .filter(Boolean)\n .at(-1)\n ?.replace(/[^a-zA-Z0-9]+/g, '_')\n .toLowerCase() ?? 'items';\n}\n\nexport function toFastifyPath(openApiPath: string): string {\n return openApiPath.replace(/\\{([^}]+)\\}/g, ':$1');\n}\n\nexport function singularize(value: string): string {\n if (value.endsWith('ies')) {\n return `${value.slice(0, -3)}y`;\n }\n\n if (value.endsWith('s')) {\n return value.slice(0, -1);\n }\n\n return value;\n}\n\nexport function resolveFile(baseDir: string, filePath: string): string {\n return path.isAbsolute(filePath) ? filePath : path.join(baseDir, filePath);\n}\n\nexport function getOperationId(\n method: string,\n openApiPath: string,\n operation: OpenAPIV3.OperationObject,\n): string {\n if (operation.operationId) {\n return operation.operationId;\n }\n\n const sanitizedPath = openApiPath\n .replace(/[{}]/g, '')\n .split('/')\n .filter(Boolean)\n .join('_');\n\n return `${method}_${sanitizedPath || 'root'}`;\n}\n\nexport function inferPathParamName(openApiPath: string): string | undefined {\n const match = openApiPath.match(/\\{([^}]+)\\}/);\n return match?.[1];\n}\n\nexport function deepClone<T>(value: T): T {\n return JSON.parse(JSON.stringify(value)) as T;\n}\n\nexport function deepMerge<T extends Record<string, unknown>>(\n base: T,\n patch: Record<string, unknown>,\n): T {\n const output: Record<string, unknown> = { ...base };\n\n for (const [key, value] of Object.entries(patch)) {\n if (\n value &&\n typeof value === 'object' &&\n !Array.isArray(value) &&\n output[key] &&\n typeof output[key] === 'object' &&\n !Array.isArray(output[key])\n ) {\n output[key] = deepMerge(\n output[key] as Record<string, unknown>,\n value as Record<string, unknown>,\n );\n } else {\n output[key] = value;\n }\n }\n\n return output as T;\n}\n\nexport function readContentType(headers: Headers): string {\n return headers.get('content-type')?.split(';')[0]?.trim().toLowerCase() ?? '';\n}\n\nexport function isJsonLikeContentType(contentType: string): boolean {\n return contentType === 'application/json' || contentType.endsWith('+json');\n}\n\nexport function toArray<T>(value: T | T[] | undefined): T[] {\n if (value === undefined) {\n return [];\n }\n\n return Array.isArray(value) ? value : [value];\n}","import type { FastifyRequest } from 'fastify';\nimport type { MockStateExtension } from './types.js';\nimport { deepMerge } from './utils.js';\n\nfunction resolvePathExpression(source: unknown, expression: string): unknown {\n return expression\n .split('.')\n .reduce<unknown>((value, segment) => (value && typeof value === 'object' ? (value as Record<string, unknown>)[segment] : undefined), source);\n}\n\nfunction evaluateTemplate(value: unknown, request: FastifyRequest): unknown {\n if (typeof value === 'string') {\n const match = value.match(/^\\{\\{\\s*(.+?)\\s*\\}\\}$/);\n if (!match) {\n return value;\n }\n\n return resolvePathExpression(\n {\n params: request.params,\n query: request.query,\n body: request.body,\n headers: request.headers,\n },\n match[1],\n );\n }\n\n if (Array.isArray(value)) {\n return value.map((entry) => evaluateTemplate(entry, request));\n }\n\n if (value && typeof value === 'object') {\n return Object.fromEntries(\n Object.entries(value).map(([key, entry]) => [key, evaluateTemplate(entry, request)]),\n );\n }\n\n return value;\n}\n\nexport function applyDslMutation(\n extension: MockStateExtension,\n request: FastifyRequest,\n collection: Record<string, unknown>[],\n defaultIdKey: string,\n): Record<string, unknown> | Record<string, unknown>[] | null {\n const idKey = extension.find_by ?? defaultIdKey;\n const targetId = (request.params as Record<string, unknown>)[idKey] ?? (request.params as Record<string, unknown>).id;\n const evaluatedAssign = (evaluateTemplate(extension.assign ?? {}, request) ?? {}) as Record<string, unknown>;\n const evaluatedSet = (evaluateTemplate(extension.set ?? {}, request) ?? {}) as Record<string, unknown>;\n\n switch (extension.action) {\n case 'create': {\n const candidate = {\n ...((request.body as Record<string, unknown> | undefined) ?? {}),\n ...evaluatedAssign,\n ...evaluatedSet,\n };\n collection.push(candidate);\n return candidate;\n }\n case 'append': {\n const item = {\n ...evaluatedAssign,\n ...evaluatedSet,\n };\n collection.push(item);\n return collection;\n }\n case 'replace': {\n const nextValue = {\n ...((request.body as Record<string, unknown> | undefined) ?? {}),\n ...evaluatedAssign,\n ...evaluatedSet,\n };\n const index = collection.findIndex((entry) => String(entry[idKey]) === String(targetId));\n if (index >= 0) {\n collection[index] = nextValue;\n }\n return nextValue;\n }\n case 'delete': {\n const index = collection.findIndex((entry) => String(entry[idKey]) === String(targetId));\n if (index >= 0) {\n const [removed] = collection.splice(index, 1);\n return removed;\n }\n return null;\n }\n case 'update':\n default: {\n const entity = collection.find((entry) => String(entry[idKey]) === String(targetId));\n if (!entity) {\n return null;\n }\n const merged = deepMerge(entity, {\n ...evaluatedAssign,\n ...evaluatedSet,\n });\n Object.assign(entity, merged);\n return entity;\n }\n }\n}","import Ajv, { type ErrorObject } from 'ajv';\nimport addFormats from 'ajv-formats';\nimport pc from 'picocolors';\nimport type { FastifyBaseLogger } from 'fastify';\nimport type { OpenAPIV3 } from 'openapi-types';\nimport type { DriftIssue } from './types.js';\n\nfunction formatErrors(errors: ErrorObject[] | null | undefined): string[] {\n return (errors ?? []).map((error) => {\n const location = error.instancePath || '/';\n return `${location} ${error.message ?? 'failed validation'}`.trim();\n });\n}\n\nexport class DriftDetector {\n private readonly ajv = new Ajv({ allErrors: true, strict: false });\n\n constructor(private readonly logger: FastifyBaseLogger) {\n addFormats(this.ajv);\n }\n\n validate(\n method: string,\n path: string,\n statusCode: number,\n schema: OpenAPIV3.SchemaObject | undefined,\n body: unknown,\n ): DriftIssue | null {\n if (!schema || body === undefined || body === null) {\n return null;\n }\n\n const validate = this.ajv.compile(schema);\n const valid = validate(body);\n\n if (valid) {\n return null;\n }\n\n const issue: DriftIssue = {\n method: method.toUpperCase(),\n path,\n statusCode,\n message: `Drift detected for ${method.toUpperCase()} ${path} (${statusCode})`,\n errors: formatErrors(validate.errors),\n };\n\n const errorLines = issue.errors.map((entry) => ` • ${entry}`).join('\\n');\n this.logger.error([\n `${pc.bgRed(pc.black(' 🚨 DRIFT DETECTED '))} ${pc.bold(issue.method)} ${issue.path} (${statusCode})`,\n errorLines,\n ].join('\\n'));\n\n return issue;\n }\n}","import type { OpenAPIV3 } from 'openapi-types';\nimport type { HttpMethod, RouteContext } from './types.js';\nimport {\n getOperationId,\n inferPathParamName,\n isSchemaObject,\n normalizeCollectionName,\n toFastifyPath,\n} from './utils.js';\n\nconst SUPPORTED_METHODS: HttpMethod[] = ['get', 'post', 'put', 'patch', 'delete'];\n\nfunction getRequestBodySchema(\n operation: OpenAPIV3.OperationObject,\n): OpenAPIV3.SchemaObject | undefined {\n const content = operation.requestBody && !('$ref' in operation.requestBody)\n ? operation.requestBody.content?.['application/json']\n : undefined;\n\n return isSchemaObject(content?.schema) ? content.schema : undefined;\n}\n\nfunction getSuccessResponse(\n operation: OpenAPIV3.OperationObject,\n): RouteContext['successResponse'] {\n const preferredCodes = ['200', '201', '202', '204'];\n\n for (const code of preferredCodes) {\n const response = operation.responses?.[code];\n if (!response || '$ref' in response) {\n continue;\n }\n\n const schema = response.content?.['application/json']?.schema;\n return {\n statusCode: Number(code),\n schema: isSchemaObject(schema) ? schema : undefined,\n };\n }\n\n return undefined;\n}\n\nexport function buildRouteContexts(document: OpenAPIV3.Document): RouteContext[] {\n const routes: RouteContext[] = [];\n\n for (const [openApiPath, pathItem] of Object.entries(document.paths ?? {})) {\n if (!pathItem || '$ref' in pathItem) {\n continue;\n }\n\n for (const method of SUPPORTED_METHODS) {\n const operation = pathItem[method];\n if (!operation || '$ref' in operation) {\n continue;\n }\n\n const pathParamName = inferPathParamName(openApiPath);\n const resourceName = normalizeCollectionName(\n pathParamName ? openApiPath.replace(/\\/\\{[^}]+\\}$/, '') : openApiPath,\n );\n const route: RouteContext = {\n method,\n path: openApiPath,\n fastifyPath: toFastifyPath(openApiPath),\n operation,\n operationId: getOperationId(method, openApiPath, operation),\n resourceName,\n isCollection: !pathParamName,\n pathParamName,\n requestBodySchema: getRequestBodySchema(operation),\n successResponse: getSuccessResponse(operation),\n };\n routes.push(route);\n }\n }\n\n return routes;\n}","import { faker } from '@faker-js/faker';\nimport type { OpenAPIV3 } from 'openapi-types';\nimport { isSchemaObject, singularize } from './utils.js';\n\nfunction seedPrimitive(schema: OpenAPIV3.SchemaObject): unknown {\n if (schema.enum?.length) {\n return schema.enum[0];\n }\n\n switch (schema.type) {\n case 'string': {\n if (schema.format === 'email') {\n return faker.internet.email();\n }\n\n if (schema.format === 'date-time') {\n return faker.date.recent().toISOString();\n }\n\n if (schema.format === 'uuid') {\n return faker.string.uuid();\n }\n\n return faker.lorem.words(2);\n }\n case 'integer':\n return faker.number.int({ min: 1, max: 1000 });\n case 'number':\n return faker.number.float({ min: 1, max: 1000, fractionDigits: 2 });\n case 'boolean':\n return faker.datatype.boolean();\n default:\n return null;\n }\n}\n\nexport function seedFromSchema(\n schema?: OpenAPIV3.SchemaObject,\n depth = 0,\n): unknown {\n if (!schema || depth > 4) {\n return null;\n }\n\n if (schema.oneOf?.length && isSchemaObject(schema.oneOf[0])) {\n return seedFromSchema(schema.oneOf[0], depth + 1);\n }\n\n if (schema.anyOf?.length && isSchemaObject(schema.anyOf[0])) {\n return seedFromSchema(schema.anyOf[0], depth + 1);\n }\n\n if (schema.allOf?.length) {\n return schema.allOf.reduce<Record<string, unknown>>((accumulator, item) => {\n if (!isSchemaObject(item)) {\n return accumulator;\n }\n\n const seeded = seedFromSchema(item, depth + 1);\n if (seeded && typeof seeded === 'object' && !Array.isArray(seeded)) {\n Object.assign(accumulator, seeded);\n }\n\n return accumulator;\n }, {});\n }\n\n if (schema.type === 'array') {\n const item = isSchemaObject(schema.items) ? schema.items : undefined;\n return [seedFromSchema(item, depth + 1), seedFromSchema(item, depth + 1)].filter(\n (value) => value !== null,\n );\n }\n\n if (schema.type === 'object' || schema.properties) {\n const entries = Object.entries(schema.properties ?? {}).map(([key, value]) => {\n if (!isSchemaObject(value)) {\n return [key, null] as const;\n }\n\n return [key, seedFromSchema(value, depth + 1)] as const;\n });\n\n return Object.fromEntries(entries);\n }\n\n return seedPrimitive(schema);\n}\n\nexport function inferSeedCollections(document: OpenAPIV3.Document): Record<string, unknown[]> {\n const collections: Record<string, unknown[]> = {};\n\n for (const [schemaName, schemaValue] of Object.entries(document.components?.schemas ?? {})) {\n if (!isSchemaObject(schemaValue) || schemaValue.type !== 'object') {\n continue;\n }\n\n const collectionName = `${singularize(schemaName).toLowerCase()}s`;\n collections[collectionName] = Array.from({ length: 3 }, () => seedFromSchema(schemaValue)) as unknown[];\n }\n\n return collections;\n}","import SwaggerParser from '@apidevtools/swagger-parser';\nimport type { OpenAPIV3 } from 'openapi-types';\n\nexport async function loadOpenApiDocument(specPath: string): Promise<OpenAPIV3.Document> {\n const document = (await SwaggerParser.dereference(specPath)) as OpenAPIV3.Document;\n\n if (!document.openapi?.startsWith('3.')) {\n throw new Error(`Only OpenAPI 3.x specs are supported. Received: ${document.openapi}`);\n }\n\n return document;\n}","import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport type { MockDatabase } from './types.js';\nimport { deepClone } from './utils.js';\n\nfunction normalizeDatabase(input: Partial<MockDatabase> | undefined): MockDatabase {\n return {\n collections: input?.collections ?? {},\n counters: input?.counters ?? {},\n };\n}\n\nexport class JsonStateStore {\n readonly filePath: string;\n\n constructor(filePath: string) {\n this.filePath = filePath;\n }\n\n async initialize(seedCollections: Record<string, unknown[]>): Promise<MockDatabase> {\n await mkdir(path.dirname(this.filePath), { recursive: true });\n\n try {\n const existing = await this.read();\n let changed = false;\n\n for (const [collectionName, items] of Object.entries(seedCollections)) {\n if (!existing.collections[collectionName]) {\n existing.collections[collectionName] = deepClone(items);\n existing.counters[collectionName] = items.length;\n changed = true;\n }\n }\n\n if (changed) {\n await this.write(existing);\n }\n\n return existing;\n } catch {\n const initial: MockDatabase = {\n collections: deepClone(seedCollections),\n counters: Object.fromEntries(\n Object.entries(seedCollections).map(([name, items]) => [name, items.length]),\n ),\n };\n await this.write(initial);\n return initial;\n }\n }\n\n async read(): Promise<MockDatabase> {\n const raw = await readFile(this.filePath, 'utf8');\n return normalizeDatabase(JSON.parse(raw) as Partial<MockDatabase>);\n }\n\n async write(database: MockDatabase): Promise<void> {\n await writeFile(this.filePath, `${JSON.stringify(database, null, 2)}\\n`, 'utf8');\n }\n\n async withDatabase<T>(\n updater: (database: MockDatabase) => Promise<T> | T,\n ): Promise<T> {\n const database = await this.read();\n const result = await updater(database);\n await this.write(database);\n return result;\n }\n}","import { readContentType, isJsonLikeContentType } from './utils.js';\nimport type { ProxyExecutionResult } from './types.js';\n\nexport async function proxyRequest(\n targetBaseUrl: string,\n path: string,\n init: RequestInit,\n): Promise<ProxyExecutionResult> {\n const response = await fetch(new URL(path, targetBaseUrl), init);\n const contentType = readContentType(response.headers);\n\n let body: unknown;\n let rawBody: string | undefined;\n\n if (response.status !== 204) {\n rawBody = await response.text();\n if (rawBody && isJsonLikeContentType(contentType)) {\n body = JSON.parse(rawBody);\n } else if (rawBody) {\n body = rawBody;\n }\n }\n\n return {\n ok: response.ok,\n statusCode: response.status,\n headers: response.headers,\n body,\n rawBody,\n };\n}"],"mappings":"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","names":["mkdir","writeFile","path","path","mkdir","writeFile","path","path","path","mkdir","writeFile","path","mkdir","path","writeFile","path","path"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "contract-drift-detection",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Stateful OpenAPI mock server with contract drift detection",
5
5
  "repository": {
6
6
  "type": "git",