contract-drift-detection 0.1.9 → 0.1.10

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
@@ -8,11 +8,14 @@ All notable changes to this project are documented in this file.
8
8
 
9
9
  - Added default-on mock response schema validation in stateful mock mode.
10
10
  - Added CLI opt-out flag: `--no-validate-mock`.
11
+ - Added `.driftignore` parser for line format: `[METHOD] [PATH_PATTERN] [JSON_PATH]`.
12
+ - Added wildcard-based drift filtering utilities for method/path/json-path matching.
11
13
 
12
14
  ### Improved
13
15
 
14
16
  - Unified issue tracking/reporting for both proxy drift and mock-mode validation findings.
15
17
  - Updated README with mock validation behavior and CLI option documentation.
18
+ - `.driftignore` now supports scoped ignores like `GET /users body.metadata.*`.
16
19
 
17
20
  ### Fixed
18
21
 
package/README.md CHANGED
@@ -185,6 +185,28 @@ When the server is running, you can access:
185
185
  - To disable this check temporarily, pass `--no-validate-mock`.
186
186
  - Proxy drift detection (`--drift-check`) remains available for validating real backend responses.
187
187
 
188
+ ### `.driftignore` syntax
189
+ Create a `.driftignore` file in your project root to suppress specific drift findings.
190
+
191
+ Each line format:
192
+
193
+ ```text
194
+ [METHOD] [PATH_PATTERN] [JSON_PATH]
195
+ ```
196
+
197
+ Examples:
198
+
199
+ ```text
200
+ * * body.trace_id
201
+ GET /users body.metadata.*
202
+ POST /orders/* body.items.*.debug
203
+ ```
204
+
205
+ Notes:
206
+ - `*` wildcard is supported in all three fields.
207
+ - `JSON_PATH` is matched as dot-path form (`body.*`).
208
+ - Lines starting with `#` are comments.
209
+
188
210
  ## 🐛 Troubleshooting
189
211
 
190
212
  - **Could not discover an OpenAPI spec:** Try an explicit endpoint with `--spec-url`, or use `quickstart` if the backend doesn't expose OpenAPI yet.
package/dist/cli.js CHANGED
@@ -8,6 +8,80 @@ import { mkdir as mkdir2, readFile, writeFile as writeFile2 } from "fs/promises"
8
8
  import path2 from "path";
9
9
  import { Command } from "commander";
10
10
 
11
+ // src/drift-ignore.ts
12
+ function wildcardToRegex(pattern, caseInsensitive = false) {
13
+ const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
14
+ return new RegExp(`^${escaped}$`, caseInsensitive ? "i" : void 0);
15
+ }
16
+ function normalizePathPattern(pathPattern) {
17
+ if (pathPattern === "*") {
18
+ return "*";
19
+ }
20
+ return pathPattern.startsWith("/") ? pathPattern : `/${pathPattern}`;
21
+ }
22
+ function normalizeJsonPathPattern(jsonPathPattern) {
23
+ if (jsonPathPattern === "*") {
24
+ return "body.*";
25
+ }
26
+ if (jsonPathPattern.startsWith("/")) {
27
+ const segments = jsonPathPattern.split("/").filter(Boolean).map((segment) => segment.replace(/~1/g, "/").replace(/~0/g, "~"));
28
+ return segments.length ? `body.${segments.join(".")}` : "body";
29
+ }
30
+ return jsonPathPattern.startsWith("body") ? jsonPathPattern : `body.${jsonPathPattern}`;
31
+ }
32
+ function normalizeMethodPattern(methodPattern) {
33
+ return methodPattern === "*" ? "*" : methodPattern.toUpperCase();
34
+ }
35
+ function normalizeRequestPath(requestPath) {
36
+ const [pathOnly] = requestPath.split("?");
37
+ return pathOnly || "/";
38
+ }
39
+ function instancePathToJsonPath(instancePath) {
40
+ const segments = instancePath.split("/").filter(Boolean).map((segment) => segment.replace(/~1/g, "/").replace(/~0/g, "~"));
41
+ return segments.length ? `body.${segments.join(".")}` : "body";
42
+ }
43
+ function matchesRule(rule, method, requestPath, jsonPath) {
44
+ const methodRegex = wildcardToRegex(normalizeMethodPattern(rule.methodPattern), true);
45
+ const pathRegex = wildcardToRegex(normalizePathPattern(rule.pathPattern));
46
+ const jsonPathRegex = wildcardToRegex(normalizeJsonPathPattern(rule.jsonPathPattern));
47
+ return methodRegex.test(method.toUpperCase()) && pathRegex.test(normalizeRequestPath(requestPath)) && jsonPathRegex.test(jsonPath);
48
+ }
49
+ function parseDriftIgnoreFile(content) {
50
+ const rules = [];
51
+ for (const [index, rawLine] of content.split(/\r?\n/u).entries()) {
52
+ const line = rawLine.trim();
53
+ if (!line || line.startsWith("#")) {
54
+ continue;
55
+ }
56
+ const parts = line.split(/\s+/u);
57
+ if (parts.length === 1) {
58
+ rules.push({
59
+ methodPattern: "*",
60
+ pathPattern: "*",
61
+ jsonPathPattern: parts[0]
62
+ });
63
+ continue;
64
+ }
65
+ if (parts.length < 3) {
66
+ throw new Error(`Invalid .driftignore rule at line ${index + 1}: "${rawLine}"`);
67
+ }
68
+ const [methodPattern, pathPattern, ...jsonPathParts] = parts;
69
+ rules.push({
70
+ methodPattern,
71
+ pathPattern,
72
+ jsonPathPattern: jsonPathParts.join(" ")
73
+ });
74
+ }
75
+ return rules;
76
+ }
77
+ function shouldIgnoreInstancePath(instancePath, requestMethod, requestPath, ignoreRules) {
78
+ if (!ignoreRules.length) {
79
+ return false;
80
+ }
81
+ const jsonPath = instancePathToJsonPath(instancePath);
82
+ return ignoreRules.some((rule) => matchesRule(rule, requestMethod, requestPath, jsonPath));
83
+ }
84
+
11
85
  // src/onboarding.ts
12
86
  import { mkdir, writeFile } from "fs/promises";
13
87
  import path from "path";
@@ -250,14 +324,14 @@ async function loadDriftIgnoreFromFile(cwd) {
250
324
  const ignoreFilePath = path2.join(cwd, ".driftignore");
251
325
  try {
252
326
  const raw = await readFile(ignoreFilePath, "utf8");
253
- return raw.split(/\r?\n/u).map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
327
+ return parseDriftIgnoreFile(raw);
254
328
  } catch {
255
329
  return [];
256
330
  }
257
331
  }
258
332
  function createCli() {
259
333
  const program = new Command();
260
- program.name("contract-drift-detection").description("Stateful OpenAPI mock server with contract drift detection").version("0.1.9");
334
+ program.name("contract-drift-detection").description("Stateful OpenAPI mock server with contract drift detection").version("0.1.10");
261
335
  applyServeOptions(program);
262
336
  applyServeOptions(program.command("serve").description("Start the mock engine"));
263
337
  program.command("init").description("Create a starter config for the current workspace").option("--spec <path>", "Default OpenAPI path", "openapi.yaml").option("--template <name>", "Template to generate (rest-crud | none)", "rest-crud").option("--db <path>", "Default JSON database path", ".mock-db.json").option("--port <port>", "Default port", "4010").option("--host <host>", "Default host", "0.0.0.0");
@@ -279,7 +353,7 @@ async function resolveServeConfig(cwd, options) {
279
353
  }
280
354
  const ignoreFromCli = parseCsvOption(options.driftIgnore);
281
355
  const ignoreFromFile = await loadDriftIgnoreFromFile(cwd);
282
- const mergedIgnore = Array.from(/* @__PURE__ */ new Set([...ignoreFromFile, ...ignoreFromCli]));
356
+ const mergedIgnore = Array.from(new Set(ignoreFromCli));
283
357
  const reporter = String(options.reporter ?? "pretty");
284
358
  if (!["pretty", "json", "junit"].includes(reporter)) {
285
359
  throw new Error(`Invalid reporter '${reporter}'. Use one of: pretty, json, junit`);
@@ -293,6 +367,7 @@ async function resolveServeConfig(cwd, options) {
293
367
  driftCheckTarget: options.driftCheck ? String(options.driftCheck) : void 0,
294
368
  strictDrift: Boolean(options.strict),
295
369
  driftIgnorePaths: mergedIgnore,
370
+ driftIgnoreRules: ignoreFromFile,
296
371
  reporter,
297
372
  reportFile: options.reportFile ? resolvePathFromCwd(cwd, String(options.reportFile)) : void 0,
298
373
  failOnDrift: Boolean(options.failOnDrift) || process.env.CI === "true",
@@ -547,7 +622,7 @@ function analyzeDrift(ajv, schema, body, options) {
547
622
  return [];
548
623
  }
549
624
  const filteredErrors = (validate.errors ?? []).filter(
550
- (entry) => !options.ignorePaths.some((rule) => pathMatchesIgnoreRule(entry.instancePath || "/", rule))
625
+ (entry) => !options.ignorePaths.some((rule) => pathMatchesIgnoreRule(entry.instancePath || "/", rule)) && !shouldIgnoreInstancePath(entry.instancePath || "/", options.method, options.path, options.ignoreRules)
551
626
  );
552
627
  return formatErrors(filteredErrors);
553
628
  }
@@ -557,17 +632,22 @@ var DriftDetector = class {
557
632
  addFormats(this.ajv);
558
633
  this.strictMode = options?.strictMode ?? false;
559
634
  this.ignorePaths = options?.ignorePaths ?? [];
635
+ this.ignoreRules = options?.ignoreRules ?? [];
560
636
  }
561
637
  ajv = new Ajv({ allErrors: true, strict: false });
562
638
  strictMode;
563
639
  ignorePaths;
640
+ ignoreRules;
564
641
  validate(method, path7, statusCode, schema, body) {
565
642
  if (!schema || body === void 0 || body === null) {
566
643
  return null;
567
644
  }
568
645
  const errors = analyzeDrift(this.ajv, schema, body, {
646
+ method,
647
+ path: path7,
569
648
  strictMode: this.strictMode,
570
- ignorePaths: this.ignorePaths
649
+ ignorePaths: this.ignorePaths,
650
+ ignoreRules: this.ignoreRules
571
651
  });
572
652
  if (!errors.length) {
573
653
  return null;
@@ -1061,7 +1141,8 @@ async function registerRoutes(app, document, config, store) {
1061
1141
  const routes = buildRouteContexts(document);
1062
1142
  const detector = new DriftDetector(app.log, {
1063
1143
  strictMode: config.strictDrift,
1064
- ignorePaths: config.driftIgnorePaths
1144
+ ignorePaths: config.driftIgnorePaths,
1145
+ ignoreRules: config.driftIgnoreRules
1065
1146
  });
1066
1147
  const driftIssues = [];
1067
1148
  const trackIssue = async (issue) => {
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","../src/drift-reporter.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 strictDrift: boolean;\n reporter: 'pretty' | 'json' | 'junit';\n reportFile?: string;\n failOnDrift: boolean;\n driftIgnorePaths: string[];\n validateMockResponses: boolean;\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 `- Drift policy: strict=${config.strictDrift ? 'on' : 'off'}, fail-on-drift=${config.failOnDrift ? 'on' : 'off'}`,\n `- Mock response validation: ${config.validateMockResponses ? 'on (default)' : 'off'}`,\n `- Reporter: ${config.reporter}${config.reportFile ? ` (${config.reportFile})` : ''}`,\n `- Ignore rules: ${config.driftIgnorePaths.length ? config.driftIgnorePaths.join(', ') : 'none'}`,\n '- Health: GET /__health',\n '- Routes: GET /__routes',\n '- Drift: GET /__drift',\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 let hasFailedOnDrift = false;\n const config = await resolveServeConfig(process.cwd(), rawOptions);\n config.onDriftDetected = async () => {\n if (!config.failOnDrift || hasFailedOnDrift) {\n return;\n }\n\n hasFailedOnDrift = true;\n process.stderr.write('Failing process because drift was detected (--fail-on-drift enabled).\\n');\n setTimeout(() => {\n process.exit(1);\n }, 25);\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 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, readFile, 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('--strict', 'Treat additional fields as drift', false)\n .option('--drift-ignore <paths>', 'Comma-separated JSON pointer paths to ignore during drift checks')\n .option('--reporter <type>', 'Drift reporter: pretty|json|junit', 'pretty')\n .option('--report-file <path>', 'Output file path for json/junit reporters')\n .option('--fail-on-drift', 'Exit with code 1 when drift is detected (enabled by default in CI)', false)\n .option('--no-validate-mock', 'Disable schema validation for mock-mode responses')\n .option('--fallback-to-mock', 'Fallback to mock responses when proxying fails', false)\n .option('--verbose', 'Enable verbose logging', false);\n}\n\nfunction parseCsvOption(value: string | boolean | undefined): string[] {\n if (!value || typeof value !== 'string') {\n return [];\n }\n\n return value\n .split(',')\n .map((entry) => entry.trim())\n .filter(Boolean);\n}\n\nasync function loadDriftIgnoreFromFile(cwd: string): Promise<string[]> {\n const ignoreFilePath = path.join(cwd, '.driftignore');\n try {\n const raw = await readFile(ignoreFilePath, 'utf8');\n return raw\n .split(/\\r?\\n/u)\n .map((line) => line.trim())\n .filter((line) => line && !line.startsWith('#'));\n } catch {\n return [];\n }\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.9');\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 const ignoreFromCli = parseCsvOption(options.driftIgnore);\n const ignoreFromFile = await loadDriftIgnoreFromFile(cwd);\n const mergedIgnore = Array.from(new Set([...ignoreFromFile, ...ignoreFromCli]));\n const reporter = String(options.reporter ?? 'pretty') as MockEngineConfig['reporter'];\n\n if (!['pretty', 'json', 'junit'].includes(reporter)) {\n throw new Error(`Invalid reporter '${reporter}'. Use one of: pretty, json, junit`);\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 strictDrift: Boolean(options.strict),\n driftIgnorePaths: mergedIgnore,\n reporter,\n reportFile: options.reportFile ? resolvePathFromCwd(cwd, String(options.reportFile)) : undefined,\n failOnDrift: Boolean(options.failOnDrift) || process.env.CI === 'true',\n validateMockResponses: options.validateMock !== false,\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 { DriftIssue, MockEngineConfig, MockOperationObject, RouteContext } from './types.js';\nimport { proxyRequest } from './proxy.js';\nimport { writeDriftReport } from './drift-reporter.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 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 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 strictMode: config.strictDrift,\n ignorePaths: config.driftIgnorePaths,\n });\n const driftIssues: DriftIssue[] = [];\n\n const trackIssue = async (issue: DriftIssue | null): Promise<void> => {\n if (!issue) {\n return;\n }\n\n driftIssues.push(issue);\n await writeDriftReport(config, driftIssues);\n await config.onDriftDetected?.(issue);\n };\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, request);\n if (proxied.statusCode >= 200 && proxied.statusCode < 300 && proxied.body !== undefined) {\n const issue = detector.validate(\n route.method,\n route.path,\n proxied.statusCode,\n route.successResponse?.schema,\n proxied.body,\n );\n await trackIssue(issue);\n }\n\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 if (\n config.validateMockResponses\n && mockResponse.statusCode >= 200\n && mockResponse.statusCode < 300\n && mockResponse.body !== undefined\n ) {\n const issue = detector.validate(\n route.method,\n route.path,\n mockResponse.statusCode,\n route.successResponse?.schema,\n mockResponse.body,\n );\n await trackIssue(issue);\n }\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 app.get('/__drift', async () => ({\n total: driftIssues.length,\n issues: driftIssues,\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 deepClone<T>(value: T): T {\n return JSON.parse(JSON.stringify(value)) as T;\n}\n\nfunction pathMatchesIgnoreRule(instancePath: string, rule: string): boolean {\n if (!rule) {\n return false;\n }\n\n const pathSegments = instancePath.split('/').filter(Boolean);\n const ruleSegments = rule.split('/').filter(Boolean);\n\n if (ruleSegments.length > pathSegments.length) {\n return false;\n }\n\n return ruleSegments.every((segment, index) => segment === '*' || segment === pathSegments[index]);\n}\n\nfunction applyStrictAdditionalProperties(schema: OpenAPIV3.SchemaObject): OpenAPIV3.SchemaObject {\n const output = deepClone(schema);\n\n const walk = (candidate: OpenAPIV3.SchemaObject): void => {\n if (candidate.type === 'object' || candidate.properties) {\n if (candidate.additionalProperties === undefined) {\n candidate.additionalProperties = false;\n }\n\n for (const value of Object.values(candidate.properties ?? {})) {\n if (value && !('$ref' in value)) {\n walk(value);\n }\n }\n }\n\n if (candidate.type === 'array' && candidate.items && !('$ref' in candidate.items)) {\n walk(candidate.items);\n }\n\n for (const entry of candidate.allOf ?? []) {\n if (!('$ref' in entry)) {\n walk(entry);\n }\n }\n\n for (const entry of candidate.oneOf ?? []) {\n if (!('$ref' in entry)) {\n walk(entry);\n }\n }\n\n for (const entry of candidate.anyOf ?? []) {\n if (!('$ref' in entry)) {\n walk(entry);\n }\n }\n };\n\n walk(output);\n return output;\n}\n\nfunction buildAjvSchema(\n schema: OpenAPIV3.SchemaObject,\n strictMode: boolean,\n): OpenAPIV3.SchemaObject {\n return strictMode ? applyStrictAdditionalProperties(schema) : schema;\n}\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 function analyzeDrift(\n ajv: Ajv,\n schema: OpenAPIV3.SchemaObject,\n body: unknown,\n options: {\n strictMode: boolean;\n ignorePaths: string[];\n },\n): string[] {\n const normalizedSchema = buildAjvSchema(schema, options.strictMode);\n const validate = ajv.compile(normalizedSchema);\n const valid = validate(body);\n\n if (valid) {\n return [];\n }\n\n const filteredErrors = (validate.errors ?? []).filter((entry) =>\n !options.ignorePaths.some((rule) => pathMatchesIgnoreRule(entry.instancePath || '/', rule)),\n );\n\n return formatErrors(filteredErrors);\n}\n\nexport class DriftDetector {\n private readonly ajv = new Ajv({ allErrors: true, strict: false });\n private readonly strictMode: boolean;\n private readonly ignorePaths: string[];\n\n constructor(\n private readonly logger: FastifyBaseLogger,\n options?: {\n strictMode?: boolean;\n ignorePaths?: string[];\n },\n ) {\n addFormats(this.ajv);\n this.strictMode = options?.strictMode ?? false;\n this.ignorePaths = options?.ignorePaths ?? [];\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 errors = analyzeDrift(this.ajv, schema, body, {\n strictMode: this.strictMode,\n ignorePaths: this.ignorePaths,\n });\n\n if (!errors.length) {\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,\n };\n\n const errorLines = issue.errors.map((entry) => ` • ${entry}`).join('\\n');\n const prettyBlock = [\n `${pc.bgRed(pc.black(' 🚨 DRIFT DETECTED '))} ${pc.bold(issue.method)} ${issue.path} (${statusCode})`,\n errorLines,\n ].join('\\n');\n\n process.stderr.write(`${prettyBlock}\\n`);\n this.logger.debug({ drift: issue }, issue.message);\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}","import { mkdir, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport type { DriftIssue, DriftReport, MockEngineConfig } from './types.js';\n\nfunction escapeXml(value: string): string {\n return value\n .replaceAll('&', '&amp;')\n .replaceAll('<', '&lt;')\n .replaceAll('>', '&gt;')\n .replaceAll('\"', '&quot;')\n .replaceAll(\"'\", '&apos;');\n}\n\nfunction toJunit(issues: DriftIssue[]): string {\n const testCases = issues\n .map((issue, index) => {\n const name = `${issue.method} ${issue.path} (${issue.statusCode}) #${index + 1}`;\n const failure = escapeXml(issue.errors.join('; '));\n return ` <testcase classname=\"contract-drift-detection\" name=\"${escapeXml(name)}\">\\n <failure message=\"drift detected\">${failure}</failure>\\n </testcase>`;\n })\n .join('\\n');\n\n return [\n '<?xml version=\"1.0\" encoding=\"UTF-8\"?>',\n `<testsuite name=\"contract-drift-detection\" tests=\"${issues.length}\" failures=\"${issues.length}\">`,\n testCases,\n '</testsuite>',\n '',\n ].join('\\n');\n}\n\nfunction toJson(issues: DriftIssue[]): string {\n const report: DriftReport = {\n generatedAt: new Date().toISOString(),\n summary: {\n total: issues.length,\n },\n issues,\n };\n\n return `${JSON.stringify(report, null, 2)}\\n`;\n}\n\nfunction defaultReportPath(config: MockEngineConfig): string {\n if (config.reportFile) {\n return config.reportFile;\n }\n\n return config.reporter === 'junit' ? 'cdd-drift-report.xml' : 'cdd-drift-report.json';\n}\n\nexport async function writeDriftReport(\n config: MockEngineConfig,\n issues: DriftIssue[],\n): Promise<string | null> {\n if (config.reporter === 'pretty') {\n return null;\n }\n\n const filePath = defaultReportPath(config);\n const content = config.reporter === 'junit' ? toJunit(issues) : toJson(issues);\n await mkdir(path.dirname(filePath), { recursive: true });\n await writeFile(filePath, content, 'utf8');\n return filePath;\n}"],"mappings":"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","names":["process","mkdir","writeFile","path","path","mkdir","writeFile","path","path","deepClone","path","mkdir","readFile","writeFile","path","mkdir","path","readFile","writeFile","path","mkdir","writeFile","path","path","process"]}
1
+ {"version":3,"sources":["../src/cli.ts","../src/config.ts","../src/drift-ignore.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","../src/drift-reporter.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 strictDrift: boolean;\n reporter: 'pretty' | 'json' | 'junit';\n reportFile?: string;\n failOnDrift: boolean;\n driftIgnorePaths: string[];\n validateMockResponses: boolean;\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 `- Drift policy: strict=${config.strictDrift ? 'on' : 'off'}, fail-on-drift=${config.failOnDrift ? 'on' : 'off'}`,\n `- Mock response validation: ${config.validateMockResponses ? 'on (default)' : 'off'}`,\n `- Reporter: ${config.reporter}${config.reportFile ? ` (${config.reportFile})` : ''}`,\n `- Ignore rules: ${config.driftIgnorePaths.length ? config.driftIgnorePaths.join(', ') : 'none'}`,\n '- Health: GET /__health',\n '- Routes: GET /__routes',\n '- Drift: GET /__drift',\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 let hasFailedOnDrift = false;\n const config = await resolveServeConfig(process.cwd(), rawOptions);\n config.onDriftDetected = async () => {\n if (!config.failOnDrift || hasFailedOnDrift) {\n return;\n }\n\n hasFailedOnDrift = true;\n process.stderr.write('Failing process because drift was detected (--fail-on-drift enabled).\\n');\n setTimeout(() => {\n process.exit(1);\n }, 25);\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 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, readFile, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport { Command } from 'commander';\nimport type { InitConfig, MockEngineConfig } from './types.js';\nimport { parseDriftIgnoreFile } from './drift-ignore.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('--strict', 'Treat additional fields as drift', false)\n .option('--drift-ignore <paths>', 'Comma-separated JSON pointer paths to ignore during drift checks')\n .option('--reporter <type>', 'Drift reporter: pretty|json|junit', 'pretty')\n .option('--report-file <path>', 'Output file path for json/junit reporters')\n .option('--fail-on-drift', 'Exit with code 1 when drift is detected (enabled by default in CI)', false)\n .option('--no-validate-mock', 'Disable schema validation for mock-mode responses')\n .option('--fallback-to-mock', 'Fallback to mock responses when proxying fails', false)\n .option('--verbose', 'Enable verbose logging', false);\n}\n\nfunction parseCsvOption(value: string | boolean | undefined): string[] {\n if (!value || typeof value !== 'string') {\n return [];\n }\n\n return value\n .split(',')\n .map((entry) => entry.trim())\n .filter(Boolean);\n}\n\nasync function loadDriftIgnoreFromFile(cwd: string): Promise<ReturnType<typeof parseDriftIgnoreFile>> {\n const ignoreFilePath = path.join(cwd, '.driftignore');\n try {\n const raw = await readFile(ignoreFilePath, 'utf8');\n return parseDriftIgnoreFile(raw);\n } catch {\n return [];\n }\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.10');\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 const ignoreFromCli = parseCsvOption(options.driftIgnore);\n const ignoreFromFile = await loadDriftIgnoreFromFile(cwd);\n const mergedIgnore = Array.from(new Set(ignoreFromCli));\n const reporter = String(options.reporter ?? 'pretty') as MockEngineConfig['reporter'];\n\n if (!['pretty', 'json', 'junit'].includes(reporter)) {\n throw new Error(`Invalid reporter '${reporter}'. Use one of: pretty, json, junit`);\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 strictDrift: Boolean(options.strict),\n driftIgnorePaths: mergedIgnore,\n driftIgnoreRules: ignoreFromFile,\n reporter,\n reportFile: options.reportFile ? resolvePathFromCwd(cwd, String(options.reportFile)) : undefined,\n failOnDrift: Boolean(options.failOnDrift) || process.env.CI === 'true',\n validateMockResponses: options.validateMock !== false,\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}","export interface DriftIgnoreRule {\n methodPattern: string;\n pathPattern: string;\n jsonPathPattern: string;\n}\n\nfunction wildcardToRegex(pattern: string, caseInsensitive = false): RegExp {\n const escaped = pattern\n .replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&')\n .replace(/\\*/g, '.*');\n return new RegExp(`^${escaped}$`, caseInsensitive ? 'i' : undefined);\n}\n\nfunction normalizePathPattern(pathPattern: string): string {\n if (pathPattern === '*') {\n return '*';\n }\n\n return pathPattern.startsWith('/') ? pathPattern : `/${pathPattern}`;\n}\n\nfunction normalizeJsonPathPattern(jsonPathPattern: string): string {\n if (jsonPathPattern === '*') {\n return 'body.*';\n }\n\n if (jsonPathPattern.startsWith('/')) {\n const segments = jsonPathPattern\n .split('/')\n .filter(Boolean)\n .map((segment) => segment.replace(/~1/g, '/').replace(/~0/g, '~'));\n return segments.length ? `body.${segments.join('.')}` : 'body';\n }\n\n return jsonPathPattern.startsWith('body') ? jsonPathPattern : `body.${jsonPathPattern}`;\n}\n\nfunction normalizeMethodPattern(methodPattern: string): string {\n return methodPattern === '*' ? '*' : methodPattern.toUpperCase();\n}\n\nfunction normalizeRequestPath(requestPath: string): string {\n const [pathOnly] = requestPath.split('?');\n return pathOnly || '/';\n}\n\nfunction extractInstancePathFromError(error: string): string {\n const token = error.trim().split(/\\s+/u)[0] ?? '/';\n return token.startsWith('/') ? token : '/';\n}\n\nexport function instancePathToJsonPath(instancePath: string): string {\n const segments = instancePath\n .split('/')\n .filter(Boolean)\n .map((segment) => segment.replace(/~1/g, '/').replace(/~0/g, '~'));\n\n return segments.length ? `body.${segments.join('.')}` : 'body';\n}\n\nfunction matchesRule(\n rule: DriftIgnoreRule,\n method: string,\n requestPath: string,\n jsonPath: string,\n): boolean {\n const methodRegex = wildcardToRegex(normalizeMethodPattern(rule.methodPattern), true);\n const pathRegex = wildcardToRegex(normalizePathPattern(rule.pathPattern));\n const jsonPathRegex = wildcardToRegex(normalizeJsonPathPattern(rule.jsonPathPattern));\n\n return methodRegex.test(method.toUpperCase())\n && pathRegex.test(normalizeRequestPath(requestPath))\n && jsonPathRegex.test(jsonPath);\n}\n\nexport function parseDriftIgnoreFile(content: string): DriftIgnoreRule[] {\n const rules: DriftIgnoreRule[] = [];\n\n for (const [index, rawLine] of content.split(/\\r?\\n/u).entries()) {\n const line = rawLine.trim();\n if (!line || line.startsWith('#')) {\n continue;\n }\n\n const parts = line.split(/\\s+/u);\n if (parts.length === 1) {\n rules.push({\n methodPattern: '*',\n pathPattern: '*',\n jsonPathPattern: parts[0],\n });\n continue;\n }\n\n if (parts.length < 3) {\n throw new Error(`Invalid .driftignore rule at line ${index + 1}: \"${rawLine}\"`);\n }\n\n const [methodPattern, pathPattern, ...jsonPathParts] = parts;\n rules.push({\n methodPattern,\n pathPattern,\n jsonPathPattern: jsonPathParts.join(' '),\n });\n }\n\n return rules;\n}\n\nexport function filterDriftErrors(\n errors: string[],\n requestMethod: string,\n requestPath: string,\n ignoreRules: DriftIgnoreRule[],\n): string[] {\n if (!ignoreRules.length) {\n return errors;\n }\n\n return errors.filter((error) => {\n const instancePath = extractInstancePathFromError(error);\n const jsonPath = instancePathToJsonPath(instancePath);\n\n return !ignoreRules.some((rule) => matchesRule(rule, requestMethod, requestPath, jsonPath));\n });\n}\n\nexport function shouldIgnoreInstancePath(\n instancePath: string,\n requestMethod: string,\n requestPath: string,\n ignoreRules: DriftIgnoreRule[],\n): boolean {\n if (!ignoreRules.length) {\n return false;\n }\n\n const jsonPath = instancePathToJsonPath(instancePath);\n return ignoreRules.some((rule) => matchesRule(rule, requestMethod, requestPath, jsonPath));\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 { DriftIssue, MockEngineConfig, MockOperationObject, RouteContext } from './types.js';\nimport { proxyRequest } from './proxy.js';\nimport { writeDriftReport } from './drift-reporter.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 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 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 strictMode: config.strictDrift,\n ignorePaths: config.driftIgnorePaths,\n ignoreRules: config.driftIgnoreRules,\n });\n const driftIssues: DriftIssue[] = [];\n\n const trackIssue = async (issue: DriftIssue | null): Promise<void> => {\n if (!issue) {\n return;\n }\n\n driftIssues.push(issue);\n await writeDriftReport(config, driftIssues);\n await config.onDriftDetected?.(issue);\n };\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, request);\n if (proxied.statusCode >= 200 && proxied.statusCode < 300 && proxied.body !== undefined) {\n const issue = detector.validate(\n route.method,\n route.path,\n proxied.statusCode,\n route.successResponse?.schema,\n proxied.body,\n );\n await trackIssue(issue);\n }\n\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 if (\n config.validateMockResponses\n && mockResponse.statusCode >= 200\n && mockResponse.statusCode < 300\n && mockResponse.body !== undefined\n ) {\n const issue = detector.validate(\n route.method,\n route.path,\n mockResponse.statusCode,\n route.successResponse?.schema,\n mockResponse.body,\n );\n await trackIssue(issue);\n }\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 app.get('/__drift', async () => ({\n total: driftIssues.length,\n issues: driftIssues,\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 { shouldIgnoreInstancePath, type DriftIgnoreRule } from './drift-ignore.js';\nimport type { DriftIssue } from './types.js';\n\nfunction deepClone<T>(value: T): T {\n return JSON.parse(JSON.stringify(value)) as T;\n}\n\nfunction pathMatchesIgnoreRule(instancePath: string, rule: string): boolean {\n if (!rule) {\n return false;\n }\n\n const pathSegments = instancePath.split('/').filter(Boolean);\n const ruleSegments = rule.split('/').filter(Boolean);\n\n if (ruleSegments.length > pathSegments.length) {\n return false;\n }\n\n return ruleSegments.every((segment, index) => segment === '*' || segment === pathSegments[index]);\n}\n\nfunction applyStrictAdditionalProperties(schema: OpenAPIV3.SchemaObject): OpenAPIV3.SchemaObject {\n const output = deepClone(schema);\n\n const walk = (candidate: OpenAPIV3.SchemaObject): void => {\n if (candidate.type === 'object' || candidate.properties) {\n if (candidate.additionalProperties === undefined) {\n candidate.additionalProperties = false;\n }\n\n for (const value of Object.values(candidate.properties ?? {})) {\n if (value && !('$ref' in value)) {\n walk(value);\n }\n }\n }\n\n if (candidate.type === 'array' && candidate.items && !('$ref' in candidate.items)) {\n walk(candidate.items);\n }\n\n for (const entry of candidate.allOf ?? []) {\n if (!('$ref' in entry)) {\n walk(entry);\n }\n }\n\n for (const entry of candidate.oneOf ?? []) {\n if (!('$ref' in entry)) {\n walk(entry);\n }\n }\n\n for (const entry of candidate.anyOf ?? []) {\n if (!('$ref' in entry)) {\n walk(entry);\n }\n }\n };\n\n walk(output);\n return output;\n}\n\nfunction buildAjvSchema(\n schema: OpenAPIV3.SchemaObject,\n strictMode: boolean,\n): OpenAPIV3.SchemaObject {\n return strictMode ? applyStrictAdditionalProperties(schema) : schema;\n}\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 function analyzeDrift(\n ajv: Ajv,\n schema: OpenAPIV3.SchemaObject,\n body: unknown,\n options: {\n method: string;\n path: string;\n strictMode: boolean;\n ignorePaths: string[];\n ignoreRules: DriftIgnoreRule[];\n },\n): string[] {\n const normalizedSchema = buildAjvSchema(schema, options.strictMode);\n const validate = ajv.compile(normalizedSchema);\n const valid = validate(body);\n\n if (valid) {\n return [];\n }\n\n const filteredErrors = (validate.errors ?? []).filter((entry) =>\n !options.ignorePaths.some((rule) => pathMatchesIgnoreRule(entry.instancePath || '/', rule))\n && !shouldIgnoreInstancePath(entry.instancePath || '/', options.method, options.path, options.ignoreRules),\n );\n\n return formatErrors(filteredErrors);\n}\n\nexport class DriftDetector {\n private readonly ajv = new Ajv({ allErrors: true, strict: false });\n private readonly strictMode: boolean;\n private readonly ignorePaths: string[];\n private readonly ignoreRules: DriftIgnoreRule[];\n\n constructor(\n private readonly logger: FastifyBaseLogger,\n options?: {\n strictMode?: boolean;\n ignorePaths?: string[];\n ignoreRules?: DriftIgnoreRule[];\n },\n ) {\n addFormats(this.ajv);\n this.strictMode = options?.strictMode ?? false;\n this.ignorePaths = options?.ignorePaths ?? [];\n this.ignoreRules = options?.ignoreRules ?? [];\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 errors = analyzeDrift(this.ajv, schema, body, {\n method,\n path,\n strictMode: this.strictMode,\n ignorePaths: this.ignorePaths,\n ignoreRules: this.ignoreRules,\n });\n\n if (!errors.length) {\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,\n };\n\n const errorLines = issue.errors.map((entry) => ` • ${entry}`).join('\\n');\n const prettyBlock = [\n `${pc.bgRed(pc.black(' 🚨 DRIFT DETECTED '))} ${pc.bold(issue.method)} ${issue.path} (${statusCode})`,\n errorLines,\n ].join('\\n');\n\n process.stderr.write(`${prettyBlock}\\n`);\n this.logger.debug({ drift: issue }, issue.message);\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}","import { mkdir, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport type { DriftIssue, DriftReport, MockEngineConfig } from './types.js';\n\nfunction escapeXml(value: string): string {\n return value\n .replaceAll('&', '&amp;')\n .replaceAll('<', '&lt;')\n .replaceAll('>', '&gt;')\n .replaceAll('\"', '&quot;')\n .replaceAll(\"'\", '&apos;');\n}\n\nfunction toJunit(issues: DriftIssue[]): string {\n const testCases = issues\n .map((issue, index) => {\n const name = `${issue.method} ${issue.path} (${issue.statusCode}) #${index + 1}`;\n const failure = escapeXml(issue.errors.join('; '));\n return ` <testcase classname=\"contract-drift-detection\" name=\"${escapeXml(name)}\">\\n <failure message=\"drift detected\">${failure}</failure>\\n </testcase>`;\n })\n .join('\\n');\n\n return [\n '<?xml version=\"1.0\" encoding=\"UTF-8\"?>',\n `<testsuite name=\"contract-drift-detection\" tests=\"${issues.length}\" failures=\"${issues.length}\">`,\n testCases,\n '</testsuite>',\n '',\n ].join('\\n');\n}\n\nfunction toJson(issues: DriftIssue[]): string {\n const report: DriftReport = {\n generatedAt: new Date().toISOString(),\n summary: {\n total: issues.length,\n },\n issues,\n };\n\n return `${JSON.stringify(report, null, 2)}\\n`;\n}\n\nfunction defaultReportPath(config: MockEngineConfig): string {\n if (config.reportFile) {\n return config.reportFile;\n }\n\n return config.reporter === 'junit' ? 'cdd-drift-report.xml' : 'cdd-drift-report.json';\n}\n\nexport async function writeDriftReport(\n config: MockEngineConfig,\n issues: DriftIssue[],\n): Promise<string | null> {\n if (config.reporter === 'pretty') {\n return null;\n }\n\n const filePath = defaultReportPath(config);\n const content = config.reporter === 'junit' ? toJunit(issues) : toJson(issues);\n await mkdir(path.dirname(filePath), { recursive: true });\n await writeFile(filePath, content, 'utf8');\n return filePath;\n}"],"mappings":";;;AAAA,OAAOA,cAAa;;;ACApB,SAAS,SAAAC,QAAO,UAAU,aAAAC,kBAAiB;AAC3C,OAAOC,WAAU;AACjB,SAAS,eAAe;;;ACIxB,SAAS,gBAAgB,SAAiB,kBAAkB,OAAe;AACzE,QAAM,UAAU,QACb,QAAQ,sBAAsB,MAAM,EACpC,QAAQ,OAAO,IAAI;AACtB,SAAO,IAAI,OAAO,IAAI,OAAO,KAAK,kBAAkB,MAAM,MAAS;AACrE;AAEA,SAAS,qBAAqB,aAA6B;AACzD,MAAI,gBAAgB,KAAK;AACvB,WAAO;AAAA,EACT;AAEA,SAAO,YAAY,WAAW,GAAG,IAAI,cAAc,IAAI,WAAW;AACpE;AAEA,SAAS,yBAAyB,iBAAiC;AACjE,MAAI,oBAAoB,KAAK;AAC3B,WAAO;AAAA,EACT;AAEA,MAAI,gBAAgB,WAAW,GAAG,GAAG;AACnC,UAAM,WAAW,gBACd,MAAM,GAAG,EACT,OAAO,OAAO,EACd,IAAI,CAAC,YAAY,QAAQ,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,GAAG,CAAC;AACnE,WAAO,SAAS,SAAS,QAAQ,SAAS,KAAK,GAAG,CAAC,KAAK;AAAA,EAC1D;AAEA,SAAO,gBAAgB,WAAW,MAAM,IAAI,kBAAkB,QAAQ,eAAe;AACvF;AAEA,SAAS,uBAAuB,eAA+B;AAC7D,SAAO,kBAAkB,MAAM,MAAM,cAAc,YAAY;AACjE;AAEA,SAAS,qBAAqB,aAA6B;AACzD,QAAM,CAAC,QAAQ,IAAI,YAAY,MAAM,GAAG;AACxC,SAAO,YAAY;AACrB;AAOO,SAAS,uBAAuB,cAA8B;AACnE,QAAM,WAAW,aACd,MAAM,GAAG,EACT,OAAO,OAAO,EACd,IAAI,CAAC,YAAY,QAAQ,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,GAAG,CAAC;AAEnE,SAAO,SAAS,SAAS,QAAQ,SAAS,KAAK,GAAG,CAAC,KAAK;AAC1D;AAEA,SAAS,YACP,MACA,QACA,aACA,UACS;AACT,QAAM,cAAc,gBAAgB,uBAAuB,KAAK,aAAa,GAAG,IAAI;AACpF,QAAM,YAAY,gBAAgB,qBAAqB,KAAK,WAAW,CAAC;AACxE,QAAM,gBAAgB,gBAAgB,yBAAyB,KAAK,eAAe,CAAC;AAEpF,SAAO,YAAY,KAAK,OAAO,YAAY,CAAC,KACvC,UAAU,KAAK,qBAAqB,WAAW,CAAC,KAChD,cAAc,KAAK,QAAQ;AAClC;AAEO,SAAS,qBAAqB,SAAoC;AACvE,QAAM,QAA2B,CAAC;AAElC,aAAW,CAAC,OAAO,OAAO,KAAK,QAAQ,MAAM,QAAQ,EAAE,QAAQ,GAAG;AAChE,UAAM,OAAO,QAAQ,KAAK;AAC1B,QAAI,CAAC,QAAQ,KAAK,WAAW,GAAG,GAAG;AACjC;AAAA,IACF;AAEA,UAAM,QAAQ,KAAK,MAAM,MAAM;AAC/B,QAAI,MAAM,WAAW,GAAG;AACtB,YAAM,KAAK;AAAA,QACT,eAAe;AAAA,QACf,aAAa;AAAA,QACb,iBAAiB,MAAM,CAAC;AAAA,MAC1B,CAAC;AACD;AAAA,IACF;AAEA,QAAI,MAAM,SAAS,GAAG;AACpB,YAAM,IAAI,MAAM,qCAAqC,QAAQ,CAAC,MAAM,OAAO,GAAG;AAAA,IAChF;AAEA,UAAM,CAAC,eAAe,aAAa,GAAG,aAAa,IAAI;AACvD,UAAM,KAAK;AAAA,MACT;AAAA,MACA;AAAA,MACA,iBAAiB,cAAc,KAAK,GAAG;AAAA,IACzC,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAoBO,SAAS,yBACd,cACA,eACA,aACA,aACS;AACT,MAAI,CAAC,YAAY,QAAQ;AACvB,WAAO;AAAA,EACT;AAEA,QAAM,WAAW,uBAAuB,YAAY;AACpD,SAAO,YAAY,KAAK,CAAC,SAAS,YAAY,MAAM,eAAe,aAAa,QAAQ,CAAC;AAC3F;;;AC3IA,SAAS,OAAO,iBAAiB;AACjC,OAAO,UAAU;AAEjB,IAAM,kBAAkB;AAAA,EACtB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmJrB,SAAS,iBAAiB,OAAuB;AAC/C,SAAO,MAAM,SAAS,GAAG,IAAI,MAAM,MAAM,GAAG,EAAE,IAAI;AACpD;AAEA,SAAS,mBAAmB,aAAqB,SAAkC;AACjF,QAAM,aAAa,YAAY,YAAY;AAC3C,MAAI,WAAW,SAAS,MAAM,GAAG;AAC/B,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,QAAQ,KAAK;AAC7B,SAAO,QAAQ,WAAW,GAAG,KAAK,QAAQ,WAAW,GAAG,IAAI,SAAS;AACvE;AAEA,eAAsB,iBACpB,KACA,aACA,WACA,YACiB;AACjB,QAAM,WAAW,KAAK,KAAK,KAAK,MAAM;AACtC,QAAM,MAAM,UAAU,EAAE,WAAW,KAAK,CAAC;AACzC,QAAM,WAAW,KAAK,KAAK,UAAU,GAAG,UAAU,IAAI,SAAS,EAAE;AACjE,QAAM,UAAU,UAAU,aAAa,MAAM;AAC7C,SAAO;AACT;AAEA,eAAsB,iBAAiB,KAAa,SAAkC;AACpF,QAAM,WAAW,MAAM,MAAM,OAAO;AACpC,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,IAAI,MAAM,wCAAwC,OAAO,KAAK,SAAS,MAAM,IAAI,SAAS,UAAU,EAAE;AAAA,EAC9G;AAEA,QAAM,UAAU,MAAM,SAAS,KAAK;AACpC,QAAM,YAAY,mBAAmB,SAAS,QAAQ,IAAI,cAAc,KAAK,IAAI,OAAO;AACxF,SAAO,iBAAiB,KAAK,SAAS,WAAW,gBAAgB;AACnE;AAEA,eAAsB,gBAAgB,gBAAyC;AAC7E,QAAM,UAAU,iBAAiB,cAAc;AAE/C,aAAW,iBAAiB,iBAAiB;AAC3C,UAAM,eAAe,GAAG,OAAO,GAAG,aAAa;AAC/C,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,cAAc,EAAE,QAAQ,MAAM,CAAC;AAC5D,UAAI,CAAC,SAAS,IAAI;AAChB;AAAA,MACF;AAEA,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,YAAY,KAAK,SAAS,SAAS,KAAK,KAAK,SAAS,SAAS;AACrE,UAAI,CAAC,WAAW;AACd;AAAA,MACF;AAEA,aAAO;AAAA,IACT,QAAQ;AACN;AAAA,IACF;AAAA,EACF;AAEA,QAAM,IAAI;AAAA,IACR;AAAA,MACE,4CAA4C,OAAO;AAAA,MACnD,UAAU,gBAAgB,KAAK,IAAI,CAAC;AAAA,MACpC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,EAAE,KAAK,IAAI;AAAA,EACb;AACF;AAEA,eAAsB,iBAAiB,KAAa,UAAmC;AACrF,QAAM,eAAe,KAAK,WAAW,QAAQ,IAAI,WAAW,KAAK,KAAK,KAAK,QAAQ;AACnF,QAAM,MAAM,KAAK,QAAQ,YAAY,GAAG,EAAE,WAAW,KAAK,CAAC;AAC3D,QAAM,UAAU,cAAc,cAAc,MAAM;AAClD,SAAO;AACT;;;AF1OA,SAAS,mBAAmB,KAAa,OAAuB;AAC9D,SAAOC,MAAK,WAAW,KAAK,IAAI,QAAQA,MAAK,KAAK,KAAK,KAAK;AAC9D;AAEA,SAAS,kBAAkB,SAA2B;AACpD,SAAO,QACJ,OAAO,iBAAiB,6BAA6B,EACrD,OAAO,oBAAoB,mCAAmC,EAC9D,OAAO,4BAA4B,0DAA0D,EAC7F,OAAO,iBAAiB,2BAA2B,MAAM,EACzD,OAAO,iBAAiB,2BAA2B,SAAS,EAC5D,OAAO,eAAe,sBAAsB,eAAe,EAC3D,OAAO,0BAA0B,8BAA8B,GAAG,EAClE,OAAO,uBAAuB,0DAA0D,EACxF,OAAO,YAAY,oCAAoC,KAAK,EAC5D,OAAO,0BAA0B,kEAAkE,EACnG,OAAO,qBAAqB,qCAAqC,QAAQ,EACzE,OAAO,wBAAwB,2CAA2C,EAC1E,OAAO,mBAAmB,sEAAsE,KAAK,EACrG,OAAO,sBAAsB,mDAAmD,EAChF,OAAO,sBAAsB,kDAAkD,KAAK,EACpF,OAAO,aAAa,0BAA0B,KAAK;AACxD;AAEA,SAAS,eAAe,OAA+C;AACrE,MAAI,CAAC,SAAS,OAAO,UAAU,UAAU;AACvC,WAAO,CAAC;AAAA,EACV;AAEA,SAAO,MACJ,MAAM,GAAG,EACT,IAAI,CAAC,UAAU,MAAM,KAAK,CAAC,EAC3B,OAAO,OAAO;AACnB;AAEA,eAAe,wBAAwB,KAA+D;AACpG,QAAM,iBAAiBA,MAAK,KAAK,KAAK,cAAc;AACpD,MAAI;AACF,UAAM,MAAM,MAAM,SAAS,gBAAgB,MAAM;AACjD,WAAO,qBAAqB,GAAG;AAAA,EACjC,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAEO,SAAS,YAAqB;AACnC,QAAM,UAAU,IAAI,QAAQ;AAE5B,UACG,KAAK,0BAA0B,EAC/B,YAAY,4DAA4D,EACxE,QAAQ,QAAQ;AAEnB,oBAAkB,OAAO;AAEzB,oBAAkB,QAAQ,QAAQ,OAAO,EAAE,YAAY,uBAAuB,CAAC;AAE/E,UACG,QAAQ,MAAM,EACd,YAAY,mDAAmD,EAC/D,OAAO,iBAAiB,wBAAwB,cAAc,EAC9D,OAAO,qBAAqB,2CAA2C,WAAW,EAClF,OAAO,eAAe,8BAA8B,eAAe,EACnE,OAAO,iBAAiB,gBAAgB,MAAM,EAC9C,OAAO,iBAAiB,gBAAgB,SAAS;AAEpD,UACG,QAAQ,YAAY,EACpB,YAAY,kEAAkE,EAC9E,OAAO,iBAAiB,wBAAwB,cAAc,EAC9D,OAAO,iBAAiB,2BAA2B,MAAM,EACzD,OAAO,iBAAiB,2BAA2B,SAAS,EAC5D,OAAO,eAAe,sBAAsB,eAAe,EAC3D,OAAO,0BAA0B,8BAA8B,GAAG,EAClE,OAAO,aAAa,0BAA0B,KAAK;AAEtD,SAAO;AACT;AAEA,eAAsB,mBACpB,KACA,SAC2B;AAC3B,MAAI;AAEJ,MAAI,QAAQ,MAAM;AAChB,uBAAmB,mBAAmB,KAAK,OAAO,QAAQ,IAAI,CAAC;AAAA,EACjE,WAAW,QAAQ,SAAS;AAC1B,uBAAmB,MAAM,iBAAiB,KAAK,OAAO,QAAQ,OAAO,CAAC;AAAA,EACxE,WAAW,QAAQ,UAAU;AAC3B,UAAM,oBAAoB,MAAM,gBAAgB,OAAO,QAAQ,QAAQ,CAAC;AACxE,uBAAmB,MAAM,iBAAiB,KAAK,iBAAiB;AAAA,EAClE;AAEA,MAAI,CAAC,kBAAkB;AACrB,UAAM,IAAI,MAAM,kDAAkD;AAAA,EACpE;AAEA,QAAM,gBAAgB,eAAe,QAAQ,WAAW;AACxD,QAAM,iBAAiB,MAAM,wBAAwB,GAAG;AACxD,QAAM,eAAe,MAAM,KAAK,IAAI,IAAI,aAAa,CAAC;AACtD,QAAM,WAAW,OAAO,QAAQ,YAAY,QAAQ;AAEpD,MAAI,CAAC,CAAC,UAAU,QAAQ,OAAO,EAAE,SAAS,QAAQ,GAAG;AACnD,UAAM,IAAI,MAAM,qBAAqB,QAAQ,oCAAoC;AAAA,EACnF;AAEA,SAAO;AAAA,IACL,UAAU;AAAA,IACV,MAAM,OAAO,QAAQ,QAAQ,IAAI;AAAA,IACjC,MAAM,OAAO,QAAQ,QAAQ,SAAS;AAAA,IACtC,QAAQ,mBAAmB,KAAK,OAAO,QAAQ,MAAM,eAAe,CAAC;AAAA,IACrE,YAAY,OAAO,QAAQ,cAAc,GAAG;AAAA,IAC5C,kBAAkB,QAAQ,aAAa,OAAO,QAAQ,UAAU,IAAI;AAAA,IACpE,aAAa,QAAQ,QAAQ,MAAM;AAAA,IACnC,kBAAkB;AAAA,IAClB,kBAAkB;AAAA,IAClB;AAAA,IACA,YAAY,QAAQ,aAAa,mBAAmB,KAAK,OAAO,QAAQ,UAAU,CAAC,IAAI;AAAA,IACvF,aAAa,QAAQ,QAAQ,WAAW,KAAK,QAAQ,IAAI,OAAO;AAAA,IAChE,uBAAuB,QAAQ,iBAAiB;AAAA,IAChD,4BAA4B,QAAQ,QAAQ,cAAc;AAAA,IAC1D,SAAS,QAAQ,QAAQ,OAAO;AAAA,EAClC;AACF;AAEA,eAAsB,mBAAmB,KAAa,YAAyC;AAC7F,MAAI,WAAW,aAAa,aAAa;AACvC,UAAM,iBAAiB,KAAK,WAAW,IAAI;AAAA,EAC7C;AAEA,QAAM,aAAaA,MAAK,KAAK,KAAK,4BAA4B;AAC9D,QAAMC,OAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AACpC,QAAMC;AAAA,IACJ;AAAA,IACA,GAAG,KAAK;AAAA,MACN;AAAA,QACE,MAAM,WAAW;AAAA,QACjB,IAAI,WAAW;AAAA,QACf,MAAM,WAAW;AAAA,QACjB,MAAM,WAAW;AAAA,MACnB;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAAA;AAAA,IACD;AAAA,EACF;AACA,SAAO;AACT;;;AG3JA,OAAOC,WAAU;AACjB,OAAO,aAA4D;AACnE,OAAO,UAAU;;;ACFjB,OAAOC,WAAU;AAGV,SAAS,eACd,QACkC;AAClC,SAAO,QAAQ,MAAM,KAAK,EAAE,UAAW;AACzC;AAEO,SAAS,wBAAwB,KAAqB;AAC3D,SAAO,IACJ,QAAQ,SAAS,EAAE,EACnB,MAAM,GAAG,EACT,OAAO,OAAO,EACd,GAAG,EAAE,GACJ,QAAQ,kBAAkB,GAAG,EAC9B,YAAY,KAAK;AACtB;AAEO,SAAS,cAAc,aAA6B;AACzD,SAAO,YAAY,QAAQ,gBAAgB,KAAK;AAClD;AAEO,SAAS,YAAY,OAAuB;AACjD,MAAI,MAAM,SAAS,KAAK,GAAG;AACzB,WAAO,GAAG,MAAM,MAAM,GAAG,EAAE,CAAC;AAAA,EAC9B;AAEA,MAAI,MAAM,SAAS,GAAG,GAAG;AACvB,WAAO,MAAM,MAAM,GAAG,EAAE;AAAA,EAC1B;AAEA,SAAO;AACT;AAEO,SAAS,YAAY,SAAiB,UAA0B;AACrE,SAAOA,MAAK,WAAW,QAAQ,IAAI,WAAWA,MAAK,KAAK,SAAS,QAAQ;AAC3E;AAEO,SAAS,eACd,QACA,aACA,WACQ;AACR,MAAI,UAAU,aAAa;AACzB,WAAO,UAAU;AAAA,EACnB;AAEA,QAAM,gBAAgB,YACnB,QAAQ,SAAS,EAAE,EACnB,MAAM,GAAG,EACT,OAAO,OAAO,EACd,KAAK,GAAG;AAEX,SAAO,GAAG,MAAM,IAAI,iBAAiB,MAAM;AAC7C;AAEO,SAAS,mBAAmB,aAAyC;AAC1E,QAAM,QAAQ,YAAY,MAAM,aAAa;AAC7C,SAAO,QAAQ,CAAC;AAClB;AAEO,SAAS,UAAa,OAAa;AACxC,SAAO,KAAK,MAAM,KAAK,UAAU,KAAK,CAAC;AACzC;AAEO,SAAS,UACd,MACA,OACG;AACH,QAAM,SAAkC,EAAE,GAAG,KAAK;AAElD,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,QACE,SACA,OAAO,UAAU,YACjB,CAAC,MAAM,QAAQ,KAAK,KACpB,OAAO,GAAG,KACV,OAAO,OAAO,GAAG,MAAM,YACvB,CAAC,MAAM,QAAQ,OAAO,GAAG,CAAC,GAC1B;AACA,aAAO,GAAG,IAAI;AAAA,QACZ,OAAO,GAAG;AAAA,QACV;AAAA,MACF;AAAA,IACF,OAAO;AACL,aAAO,GAAG,IAAI;AAAA,IAChB;AAAA,EACF;AAEA,SAAO;AACT;AAEO,SAAS,gBAAgB,SAA0B;AACxD,SAAO,QAAQ,IAAI,cAAc,GAAG,MAAM,GAAG,EAAE,CAAC,GAAG,KAAK,EAAE,YAAY,KAAK;AAC7E;AAEO,SAAS,sBAAsB,aAA8B;AAClE,SAAO,gBAAgB,sBAAsB,YAAY,SAAS,OAAO;AAC3E;;;AC/FA,SAAS,sBAAsB,QAAiB,YAA6B;AAC3E,SAAO,WACJ,MAAM,GAAG,EACT,OAAgB,CAAC,OAAO,YAAa,SAAS,OAAO,UAAU,WAAY,MAAkC,OAAO,IAAI,QAAY,MAAM;AAC/I;AAEA,SAAS,iBAAiB,OAAgB,SAAkC;AAC1E,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,QAAQ,MAAM,MAAM,uBAAuB;AACjD,QAAI,CAAC,OAAO;AACV,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,MACL;AAAA,QACE,QAAQ,QAAQ;AAAA,QAChB,OAAO,QAAQ;AAAA,QACf,MAAM,QAAQ;AAAA,QACd,SAAS,QAAQ;AAAA,MACnB;AAAA,MACA,MAAM,CAAC;AAAA,IACT;AAAA,EACF;AAEA,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,WAAO,MAAM,IAAI,CAAC,UAAU,iBAAiB,OAAO,OAAO,CAAC;AAAA,EAC9D;AAEA,MAAI,SAAS,OAAO,UAAU,UAAU;AACtC,WAAO,OAAO;AAAA,MACZ,OAAO,QAAQ,KAAK,EAAE,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM,CAAC,KAAK,iBAAiB,OAAO,OAAO,CAAC,CAAC;AAAA,IACrF;AAAA,EACF;AAEA,SAAO;AACT;AAEO,SAAS,iBACd,WACA,SACA,YACA,cAC4D;AAC5D,QAAM,QAAQ,UAAU,WAAW;AACnC,QAAM,WAAY,QAAQ,OAAmC,KAAK,KAAM,QAAQ,OAAmC;AACnH,QAAM,kBAAmB,iBAAiB,UAAU,UAAU,CAAC,GAAG,OAAO,KAAK,CAAC;AAC/E,QAAM,eAAgB,iBAAiB,UAAU,OAAO,CAAC,GAAG,OAAO,KAAK,CAAC;AAEzE,UAAQ,UAAU,QAAQ;AAAA,IACxB,KAAK,UAAU;AACb,YAAM,YAAY;AAAA,QAChB,GAAK,QAAQ,QAAgD,CAAC;AAAA,QAC9D,GAAG;AAAA,QACH,GAAG;AAAA,MACL;AACA,iBAAW,KAAK,SAAS;AACzB,aAAO;AAAA,IACT;AAAA,IACA,KAAK,UAAU;AACb,YAAM,OAAO;AAAA,QACX,GAAG;AAAA,QACH,GAAG;AAAA,MACL;AACA,iBAAW,KAAK,IAAI;AACpB,aAAO;AAAA,IACT;AAAA,IACA,KAAK,WAAW;AACd,YAAM,YAAY;AAAA,QAChB,GAAK,QAAQ,QAAgD,CAAC;AAAA,QAC9D,GAAG;AAAA,QACH,GAAG;AAAA,MACL;AACA,YAAM,QAAQ,WAAW,UAAU,CAAC,UAAU,OAAO,MAAM,KAAK,CAAC,MAAM,OAAO,QAAQ,CAAC;AACvF,UAAI,SAAS,GAAG;AACd,mBAAW,KAAK,IAAI;AAAA,MACtB;AACA,aAAO;AAAA,IACT;AAAA,IACA,KAAK,UAAU;AACb,YAAM,QAAQ,WAAW,UAAU,CAAC,UAAU,OAAO,MAAM,KAAK,CAAC,MAAM,OAAO,QAAQ,CAAC;AACvF,UAAI,SAAS,GAAG;AACd,cAAM,CAAC,OAAO,IAAI,WAAW,OAAO,OAAO,CAAC;AAC5C,eAAO;AAAA,MACT;AACA,aAAO;AAAA,IACT;AAAA,IACA,KAAK;AAAA,IACL,SAAS;AACP,YAAM,SAAS,WAAW,KAAK,CAAC,UAAU,OAAO,MAAM,KAAK,CAAC,MAAM,OAAO,QAAQ,CAAC;AACnF,UAAI,CAAC,QAAQ;AACX,eAAO;AAAA,MACT;AACA,YAAM,SAAS,UAAU,QAAQ;AAAA,QAC/B,GAAG;AAAA,QACH,GAAG;AAAA,MACL,CAAC;AACD,aAAO,OAAO,QAAQ,MAAM;AAC5B,aAAO;AAAA,IACT;AAAA,EACF;AACF;;;ACxGA,OAAO,SAA+B;AACtC,OAAO,gBAAgB;AACvB,OAAO,QAAQ;AAMf,SAASC,WAAa,OAAa;AACjC,SAAO,KAAK,MAAM,KAAK,UAAU,KAAK,CAAC;AACzC;AAEA,SAAS,sBAAsB,cAAsB,MAAuB;AAC1E,MAAI,CAAC,MAAM;AACT,WAAO;AAAA,EACT;AAEA,QAAM,eAAe,aAAa,MAAM,GAAG,EAAE,OAAO,OAAO;AAC3D,QAAM,eAAe,KAAK,MAAM,GAAG,EAAE,OAAO,OAAO;AAEnD,MAAI,aAAa,SAAS,aAAa,QAAQ;AAC7C,WAAO;AAAA,EACT;AAEA,SAAO,aAAa,MAAM,CAAC,SAAS,UAAU,YAAY,OAAO,YAAY,aAAa,KAAK,CAAC;AAClG;AAEA,SAAS,gCAAgC,QAAwD;AAC/F,QAAM,SAASA,WAAU,MAAM;AAE/B,QAAM,OAAO,CAAC,cAA4C;AACxD,QAAI,UAAU,SAAS,YAAY,UAAU,YAAY;AACvD,UAAI,UAAU,yBAAyB,QAAW;AAChD,kBAAU,uBAAuB;AAAA,MACnC;AAEA,iBAAW,SAAS,OAAO,OAAO,UAAU,cAAc,CAAC,CAAC,GAAG;AAC7D,YAAI,SAAS,EAAE,UAAU,QAAQ;AAC/B,eAAK,KAAK;AAAA,QACZ;AAAA,MACF;AAAA,IACF;AAEA,QAAI,UAAU,SAAS,WAAW,UAAU,SAAS,EAAE,UAAU,UAAU,QAAQ;AACjF,WAAK,UAAU,KAAK;AAAA,IACtB;AAEA,eAAW,SAAS,UAAU,SAAS,CAAC,GAAG;AACzC,UAAI,EAAE,UAAU,QAAQ;AACtB,aAAK,KAAK;AAAA,MACZ;AAAA,IACF;AAEA,eAAW,SAAS,UAAU,SAAS,CAAC,GAAG;AACzC,UAAI,EAAE,UAAU,QAAQ;AACtB,aAAK,KAAK;AAAA,MACZ;AAAA,IACF;AAEA,eAAW,SAAS,UAAU,SAAS,CAAC,GAAG;AACzC,UAAI,EAAE,UAAU,QAAQ;AACtB,aAAK,KAAK;AAAA,MACZ;AAAA,IACF;AAAA,EACF;AAEA,OAAK,MAAM;AACX,SAAO;AACT;AAEA,SAAS,eACP,QACA,YACwB;AACxB,SAAO,aAAa,gCAAgC,MAAM,IAAI;AAChE;AAEA,SAAS,aAAa,QAAoD;AACxE,UAAQ,UAAU,CAAC,GAAG,IAAI,CAAC,UAAU;AACnC,UAAM,WAAW,MAAM,gBAAgB;AACvC,WAAO,GAAG,QAAQ,IAAI,MAAM,WAAW,mBAAmB,GAAG,KAAK;AAAA,EACpE,CAAC;AACH;AAEO,SAAS,aACd,KACA,QACA,MACA,SAOU;AACV,QAAM,mBAAmB,eAAe,QAAQ,QAAQ,UAAU;AAClE,QAAM,WAAW,IAAI,QAAQ,gBAAgB;AAC7C,QAAM,QAAQ,SAAS,IAAI;AAE3B,MAAI,OAAO;AACT,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,kBAAkB,SAAS,UAAU,CAAC,GAAG;AAAA,IAAO,CAAC,UACrD,CAAC,QAAQ,YAAY,KAAK,CAAC,SAAS,sBAAsB,MAAM,gBAAgB,KAAK,IAAI,CAAC,KACvF,CAAC,yBAAyB,MAAM,gBAAgB,KAAK,QAAQ,QAAQ,QAAQ,MAAM,QAAQ,WAAW;AAAA,EAC3G;AAEA,SAAO,aAAa,cAAc;AACpC;AAEO,IAAM,gBAAN,MAAoB;AAAA,EAMzB,YACmB,QACjB,SAKA;AANiB;AAOjB,eAAW,KAAK,GAAG;AACnB,SAAK,aAAa,SAAS,cAAc;AACzC,SAAK,cAAc,SAAS,eAAe,CAAC;AAC5C,SAAK,cAAc,SAAS,eAAe,CAAC;AAAA,EAC9C;AAAA,EAjBiB,MAAM,IAAI,IAAI,EAAE,WAAW,MAAM,QAAQ,MAAM,CAAC;AAAA,EAChD;AAAA,EACA;AAAA,EACA;AAAA,EAgBjB,SACE,QACAC,OACA,YACA,QACA,MACmB;AACnB,QAAI,CAAC,UAAU,SAAS,UAAa,SAAS,MAAM;AAClD,aAAO;AAAA,IACT;AAEA,UAAM,SAAS,aAAa,KAAK,KAAK,QAAQ,MAAM;AAAA,MAClD;AAAA,MACA,MAAAA;AAAA,MACA,YAAY,KAAK;AAAA,MACjB,aAAa,KAAK;AAAA,MAClB,aAAa,KAAK;AAAA,IACpB,CAAC;AAED,QAAI,CAAC,OAAO,QAAQ;AAClB,aAAO;AAAA,IACT;AAEA,UAAM,QAAoB;AAAA,MACxB,QAAQ,OAAO,YAAY;AAAA,MAC3B,MAAAA;AAAA,MACA;AAAA,MACA,SAAS,sBAAsB,OAAO,YAAY,CAAC,IAAIA,KAAI,KAAK,UAAU;AAAA,MAC1E;AAAA,IACF;AAEA,UAAM,aAAa,MAAM,OAAO,IAAI,CAAC,UAAU,YAAO,KAAK,EAAE,EAAE,KAAK,IAAI;AACxE,UAAM,cAAc;AAAA,MAClB,GAAG,GAAG,MAAM,GAAG,MAAM,4BAAqB,CAAC,CAAC,IAAI,GAAG,KAAK,MAAM,MAAM,CAAC,IAAI,MAAM,IAAI,KAAK,UAAU;AAAA,MAClG;AAAA,IACF,EAAE,KAAK,IAAI;AAEX,YAAQ,OAAO,MAAM,GAAG,WAAW;AAAA,CAAI;AACvC,SAAK,OAAO,MAAM,EAAE,OAAO,MAAM,GAAG,MAAM,OAAO;AAEjD,WAAO;AAAA,EACT;AACF;;;ACpKA,IAAM,oBAAkC,CAAC,OAAO,QAAQ,OAAO,SAAS,QAAQ;AAEhF,SAAS,qBACP,WACoC;AACpC,QAAM,UAAU,UAAU,eAAe,EAAE,UAAU,UAAU,eAC3D,UAAU,YAAY,UAAU,kBAAkB,IAClD;AAEJ,SAAO,eAAe,SAAS,MAAM,IAAI,QAAQ,SAAS;AAC5D;AAEA,SAAS,mBACP,WACiC;AACjC,QAAM,iBAAiB,CAAC,OAAO,OAAO,OAAO,KAAK;AAElD,aAAW,QAAQ,gBAAgB;AACjC,UAAM,WAAW,UAAU,YAAY,IAAI;AAC3C,QAAI,CAAC,YAAY,UAAU,UAAU;AACnC;AAAA,IACF;AAEA,UAAM,SAAS,SAAS,UAAU,kBAAkB,GAAG;AACvD,WAAO;AAAA,MACL,YAAY,OAAO,IAAI;AAAA,MACvB,QAAQ,eAAe,MAAM,IAAI,SAAS;AAAA,IAC5C;AAAA,EACF;AAEA,SAAO;AACT;AAEO,SAAS,mBAAmB,UAA8C;AAC/E,QAAM,SAAyB,CAAC;AAEhC,aAAW,CAAC,aAAa,QAAQ,KAAK,OAAO,QAAQ,SAAS,SAAS,CAAC,CAAC,GAAG;AAC1E,QAAI,CAAC,YAAY,UAAU,UAAU;AACnC;AAAA,IACF;AAEA,eAAW,UAAU,mBAAmB;AACtC,YAAM,YAAY,SAAS,MAAM;AACjC,UAAI,CAAC,aAAa,UAAU,WAAW;AACrC;AAAA,MACF;AAEA,YAAM,gBAAgB,mBAAmB,WAAW;AACpD,YAAM,eAAe;AAAA,QACnB,gBAAgB,YAAY,QAAQ,gBAAgB,EAAE,IAAI;AAAA,MAC5D;AACA,YAAM,QAAsB;AAAA,QAC1B;AAAA,QACA,MAAM;AAAA,QACN,aAAa,cAAc,WAAW;AAAA,QACtC;AAAA,QACA,aAAa,eAAe,QAAQ,aAAa,SAAS;AAAA,QAC1D;AAAA,QACA,cAAc,CAAC;AAAA,QACf;AAAA,QACA,mBAAmB,qBAAqB,SAAS;AAAA,QACjD,iBAAiB,mBAAmB,SAAS;AAAA,MAC/C;AACA,aAAO,KAAK,KAAK;AAAA,IACnB;AAAA,EACF;AAEA,SAAO;AACT;;;AC9EA,SAAS,aAAa;AAItB,SAAS,cAAc,QAAyC;AAC9D,MAAI,OAAO,MAAM,QAAQ;AACvB,WAAO,OAAO,KAAK,CAAC;AAAA,EACtB;AAEA,UAAQ,OAAO,MAAM;AAAA,IACnB,KAAK,UAAU;AACb,UAAI,OAAO,WAAW,SAAS;AAC7B,eAAO,MAAM,SAAS,MAAM;AAAA,MAC9B;AAEA,UAAI,OAAO,WAAW,aAAa;AACjC,eAAO,MAAM,KAAK,OAAO,EAAE,YAAY;AAAA,MACzC;AAEA,UAAI,OAAO,WAAW,QAAQ;AAC5B,eAAO,MAAM,OAAO,KAAK;AAAA,MAC3B;AAEA,aAAO,MAAM,MAAM,MAAM,CAAC;AAAA,IAC5B;AAAA,IACA,KAAK;AACH,aAAO,MAAM,OAAO,IAAI,EAAE,KAAK,GAAG,KAAK,IAAK,CAAC;AAAA,IAC/C,KAAK;AACH,aAAO,MAAM,OAAO,MAAM,EAAE,KAAK,GAAG,KAAK,KAAM,gBAAgB,EAAE,CAAC;AAAA,IACpE,KAAK;AACH,aAAO,MAAM,SAAS,QAAQ;AAAA,IAChC;AACE,aAAO;AAAA,EACX;AACF;AAEO,SAAS,eACd,QACA,QAAQ,GACC;AACT,MAAI,CAAC,UAAU,QAAQ,GAAG;AACxB,WAAO;AAAA,EACT;AAEA,MAAI,OAAO,OAAO,UAAU,eAAe,OAAO,MAAM,CAAC,CAAC,GAAG;AAC3D,WAAO,eAAe,OAAO,MAAM,CAAC,GAAG,QAAQ,CAAC;AAAA,EAClD;AAEA,MAAI,OAAO,OAAO,UAAU,eAAe,OAAO,MAAM,CAAC,CAAC,GAAG;AAC3D,WAAO,eAAe,OAAO,MAAM,CAAC,GAAG,QAAQ,CAAC;AAAA,EAClD;AAEA,MAAI,OAAO,OAAO,QAAQ;AACxB,WAAO,OAAO,MAAM,OAAgC,CAAC,aAAa,SAAS;AACzE,UAAI,CAAC,eAAe,IAAI,GAAG;AACzB,eAAO;AAAA,MACT;AAEA,YAAM,SAAS,eAAe,MAAM,QAAQ,CAAC;AAC7C,UAAI,UAAU,OAAO,WAAW,YAAY,CAAC,MAAM,QAAQ,MAAM,GAAG;AAClE,eAAO,OAAO,aAAa,MAAM;AAAA,MACnC;AAEA,aAAO;AAAA,IACT,GAAG,CAAC,CAAC;AAAA,EACP;AAEA,MAAI,OAAO,SAAS,SAAS;AAC3B,UAAM,OAAO,eAAe,OAAO,KAAK,IAAI,OAAO,QAAQ;AAC3D,WAAO,CAAC,eAAe,MAAM,QAAQ,CAAC,GAAG,eAAe,MAAM,QAAQ,CAAC,CAAC,EAAE;AAAA,MACxE,CAAC,UAAU,UAAU;AAAA,IACvB;AAAA,EACF;AAEA,MAAI,OAAO,SAAS,YAAY,OAAO,YAAY;AACjD,UAAM,UAAU,OAAO,QAAQ,OAAO,cAAc,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM;AAC5E,UAAI,CAAC,eAAe,KAAK,GAAG;AAC1B,eAAO,CAAC,KAAK,IAAI;AAAA,MACnB;AAEA,aAAO,CAAC,KAAK,eAAe,OAAO,QAAQ,CAAC,CAAC;AAAA,IAC/C,CAAC;AAED,WAAO,OAAO,YAAY,OAAO;AAAA,EACnC;AAEA,SAAO,cAAc,MAAM;AAC7B;AAEO,SAAS,qBAAqB,UAAyD;AAC5F,QAAM,cAAyC,CAAC;AAEhD,aAAW,CAAC,YAAY,WAAW,KAAK,OAAO,QAAQ,SAAS,YAAY,WAAW,CAAC,CAAC,GAAG;AAC1F,QAAI,CAAC,eAAe,WAAW,KAAK,YAAY,SAAS,UAAU;AACjE;AAAA,IACF;AAEA,UAAM,iBAAiB,GAAG,YAAY,UAAU,EAAE,YAAY,CAAC;AAC/D,gBAAY,cAAc,IAAI,MAAM,KAAK,EAAE,QAAQ,EAAE,GAAG,MAAM,eAAe,WAAW,CAAC;AAAA,EAC3F;AAEA,SAAO;AACT;;;ACtGA,OAAO,mBAAmB;AAG1B,eAAsB,oBAAoB,UAA+C;AACvF,QAAM,WAAY,MAAM,cAAc,YAAY,QAAQ;AAE1D,MAAI,CAAC,SAAS,SAAS,WAAW,IAAI,GAAG;AACvC,UAAM,IAAI,MAAM,mDAAmD,SAAS,OAAO,EAAE;AAAA,EACvF;AAEA,SAAO;AACT;;;ACXA,SAAS,SAAAC,QAAO,YAAAC,WAAU,aAAAC,kBAAiB;AAC3C,OAAOC,WAAU;AAIjB,SAAS,kBAAkB,OAAwD;AACjF,SAAO;AAAA,IACL,aAAa,OAAO,eAAe,CAAC;AAAA,IACpC,UAAU,OAAO,YAAY,CAAC;AAAA,EAChC;AACF;AAEO,IAAM,iBAAN,MAAqB;AAAA,EACjB;AAAA,EAET,YAAY,UAAkB;AAC5B,SAAK,WAAW;AAAA,EAClB;AAAA,EAEA,MAAM,WAAW,iBAAmE;AAClF,UAAMC,OAAMC,MAAK,QAAQ,KAAK,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAE5D,QAAI;AACF,YAAM,WAAW,MAAM,KAAK,KAAK;AACjC,UAAI,UAAU;AAEd,iBAAW,CAAC,gBAAgB,KAAK,KAAK,OAAO,QAAQ,eAAe,GAAG;AACrE,YAAI,CAAC,SAAS,YAAY,cAAc,GAAG;AACzC,mBAAS,YAAY,cAAc,IAAI,UAAU,KAAK;AACtD,mBAAS,SAAS,cAAc,IAAI,MAAM;AAC1C,oBAAU;AAAA,QACZ;AAAA,MACF;AAEA,UAAI,SAAS;AACX,cAAM,KAAK,MAAM,QAAQ;AAAA,MAC3B;AAEA,aAAO;AAAA,IACT,QAAQ;AACN,YAAM,UAAwB;AAAA,QAC5B,aAAa,UAAU,eAAe;AAAA,QACtC,UAAU,OAAO;AAAA,UACf,OAAO,QAAQ,eAAe,EAAE,IAAI,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM,MAAM,MAAM,CAAC;AAAA,QAC7E;AAAA,MACF;AACA,YAAM,KAAK,MAAM,OAAO;AACxB,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAM,OAA8B;AAClC,UAAM,MAAM,MAAMC,UAAS,KAAK,UAAU,MAAM;AAChD,WAAO,kBAAkB,KAAK,MAAM,GAAG,CAA0B;AAAA,EACnE;AAAA,EAEA,MAAM,MAAM,UAAuC;AACjD,UAAMC,WAAU,KAAK,UAAU,GAAG,KAAK,UAAU,UAAU,MAAM,CAAC,CAAC;AAAA,GAAM,MAAM;AAAA,EACjF;AAAA,EAEA,MAAM,aACJ,SACY;AACZ,UAAM,WAAW,MAAM,KAAK,KAAK;AACjC,UAAM,SAAS,MAAM,QAAQ,QAAQ;AACrC,UAAM,KAAK,MAAM,QAAQ;AACzB,WAAO;AAAA,EACT;AACF;;;ACjEA,eAAsB,aACpB,eACAC,OACA,MAC+B;AAC/B,QAAM,WAAW,MAAM,MAAM,IAAI,IAAIA,OAAM,aAAa,GAAG,IAAI;AAC/D,QAAM,cAAc,gBAAgB,SAAS,OAAO;AAEpD,MAAI;AACJ,MAAI;AAEJ,MAAI,SAAS,WAAW,KAAK;AAC3B,cAAU,MAAM,SAAS,KAAK;AAC9B,QAAI,WAAW,sBAAsB,WAAW,GAAG;AACjD,aAAO,KAAK,MAAM,OAAO;AAAA,IAC3B,WAAW,SAAS;AAClB,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AAAA,IACL,IAAI,SAAS;AAAA,IACb,YAAY,SAAS;AAAA,IACrB,SAAS,SAAS;AAAA,IAClB;AAAA,IACA;AAAA,EACF;AACF;;;AC9BA,SAAS,SAAAC,QAAO,aAAAC,kBAAiB;AACjC,OAAOC,WAAU;AAGjB,SAAS,UAAU,OAAuB;AACxC,SAAO,MACJ,WAAW,KAAK,OAAO,EACvB,WAAW,KAAK,MAAM,EACtB,WAAW,KAAK,MAAM,EACtB,WAAW,KAAK,QAAQ,EACxB,WAAW,KAAK,QAAQ;AAC7B;AAEA,SAAS,QAAQ,QAA8B;AAC7C,QAAM,YAAY,OACf,IAAI,CAAC,OAAO,UAAU;AACrB,UAAM,OAAO,GAAG,MAAM,MAAM,IAAI,MAAM,IAAI,KAAK,MAAM,UAAU,MAAM,QAAQ,CAAC;AAC9E,UAAM,UAAU,UAAU,MAAM,OAAO,KAAK,IAAI,CAAC;AACjD,WAAO,4DAA4D,UAAU,IAAI,CAAC;AAAA,0CAA+C,OAAO;AAAA;AAAA,EAC1I,CAAC,EACA,KAAK,IAAI;AAEZ,SAAO;AAAA,IACL;AAAA,IACA,qDAAqD,OAAO,MAAM,eAAe,OAAO,MAAM;AAAA,IAC9F;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AACb;AAEA,SAAS,OAAO,QAA8B;AAC5C,QAAM,SAAsB;AAAA,IAC1B,cAAa,oBAAI,KAAK,GAAE,YAAY;AAAA,IACpC,SAAS;AAAA,MACP,OAAO,OAAO;AAAA,IAChB;AAAA,IACA;AAAA,EACF;AAEA,SAAO,GAAG,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAAA;AAC3C;AAEA,SAAS,kBAAkB,QAAkC;AAC3D,MAAI,OAAO,YAAY;AACrB,WAAO,OAAO;AAAA,EAChB;AAEA,SAAO,OAAO,aAAa,UAAU,yBAAyB;AAChE;AAEA,eAAsB,iBACpB,QACA,QACwB;AACxB,MAAI,OAAO,aAAa,UAAU;AAChC,WAAO;AAAA,EACT;AAEA,QAAM,WAAW,kBAAkB,MAAM;AACzC,QAAM,UAAU,OAAO,aAAa,UAAU,QAAQ,MAAM,IAAI,OAAO,MAAM;AAC7E,QAAMF,OAAME,MAAK,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AACvD,QAAMD,WAAU,UAAU,SAAS,MAAM;AACzC,SAAO;AACT;;;AT/CA,SAAS,eAAe,cAAiE;AACvF,QAAM,cAAc,CAAC,QAAQ,kBAAkB,YAAY;AAC3D,QAAM,UAAkC,CAAC;AAEzC,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,YAAY,GAAG;AACvD,QAAI,YAAY,SAAS,GAAmC,GAAG;AAC7D;AAAA,IACF;AAEA,QAAI,UAAU,QAAW;AACvB;AAAA,IACF;AAEA,YAAQ,GAAG,IAAI,MAAM,QAAQ,KAAK,IAAI,MAAM,KAAK,IAAI,IAAI,OAAO,KAAK;AAAA,EACvE;AAEA,SAAO;AACT;AAEA,SAAS,YAAY,SAAgD;AACnE,MAAI,QAAQ,WAAW,SAAS,QAAQ,WAAW,QAAQ;AACzD,WAAO;AAAA,EACT;AAEA,MAAI,QAAQ,SAAS,UAAa,QAAQ,SAAS,MAAM;AACvD,WAAO;AAAA,EACT;AAEA,MAAI,OAAO,QAAQ,SAAS,YAAY,QAAQ,gBAAgB,YAAY;AAC1E,WAAO,QAAQ;AAAA,EACjB;AAEA,SAAO,KAAK,UAAU,QAAQ,IAAI;AACpC;AAEA,SAAS,aACP,OACA,YACA,MACA;AACA,MAAI,eAAe,KAAK;AACtB,WAAO,MAAM,KAAK,GAAG,EAAE,KAAK;AAAA,EAC9B;AAEA,SAAO,MAAM,KAAK,UAAU,EAAE,KAAK,IAAI;AACzC;AAEA,SAAS,kBAAkB,OAA6B;AACtD,MAAI,MAAM,WAAW,QAAQ;AAC3B,WAAO;AAAA,EACT;AAEA,MAAI,MAAM,WAAW,UAAU;AAC7B,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEA,SAAS,eAAe,SAAkD;AACxE,MAAI,CAAC,QAAQ,QAAQ,OAAO,QAAQ,SAAS,YAAY,MAAM,QAAQ,QAAQ,IAAI,GAAG;AACpF,WAAO,CAAC;AAAA,EACV;AAEA,SAAO,QAAQ;AACjB;AAEA,SAAS,cAAc,UAAwF,OAAgD;AAC7J,MAAI,CAAC,SAAS,YAAY,MAAM,YAAY,GAAG;AAC7C,aAAS,YAAY,MAAM,YAAY,IAAI,CAAC;AAC5C,aAAS,SAAS,MAAM,YAAY,IAAI;AAAA,EAC1C;AAEA,SAAO,SAAS,YAAY,MAAM,YAAY;AAChD;AAEA,SAAS,oBACP,UACA,gBAC2B;AAC3B,MAAI,CAAC,SAAS,YAAY,cAAc,GAAG;AACzC,aAAS,YAAY,cAAc,IAAI,CAAC;AACxC,aAAS,SAAS,cAAc,IAAI;AAAA,EACtC;AAEA,SAAO,SAAS,YAAY,cAAc;AAC5C;AAEA,SAAS,aAAa,OAA6B;AACjD,SAAO,MAAM,iBAAiB;AAChC;AAEA,SAAS,oBAAoB,OAAuD;AAClF,MAAI,OAAO,UAAU,YAAY,OAAO,UAAU,YAAY,OAAO,UAAU,WAAW;AACxF,WAAO,OAAO,KAAK;AAAA,EACrB;AAEA,SAAO;AACT;AAEA,SAAS,OAAO,UAAgD,OAA6B;AAC3F,QAAM,UAAU,SAAS,SAAS,MAAM,YAAY,KAAK;AACzD,QAAM,OAAO,UAAU;AACvB,WAAS,SAAS,MAAM,YAAY,IAAI;AACxC,SAAO;AACT;AAEA,SAAS,iBACP,UACA,OACA,YACA,OACQ;AACR,MAAI,YAAY,OAAO,UAAU,KAAK;AACtC,QAAM,eAAe,CAAC,UACpB,WAAW,KAAK,CAAC,SAAS,oBAAoB,KAAK,KAAK,CAAC,MAAM,OAAO,KAAK,CAAC;AAE9E,SAAO,aAAa,SAAS,GAAG;AAC9B,gBAAY,OAAO,UAAU,KAAK;AAAA,EACpC;AAEA,SAAO;AACT;AAEA,SAAS,cAAc,OAAqB,YAAuC,SAA8D;AAC/I,QAAM,QAAQ,aAAa,KAAK;AAChC,QAAM,QAAQ;AAAA,IACX,QAAQ,OAAmC,KAAK,KAAM,QAAQ,OAAmC;AAAA,EACpG;AACA,SAAO,WAAW,KAAK,CAAC,SAAS,oBAAoB,KAAK,KAAK,CAAC,MAAM,KAAK;AAC7E;AAEA,SAAS,oBAAoB,OAAqB,SAAkD;AAClG,QAAM,cAAc,eAAe,OAAO;AAC1C,QAAM,SAAS,MAAM,oBAAoB,eAAe,MAAM,iBAAiB,IAAI,CAAC;AAEpF,SAAO;AAAA,IACJ,UAAU,OAAO,WAAW,YAAY,CAAC,MAAM,QAAQ,MAAM,IAAI,SAAS,CAAC;AAAA,IAC5E;AAAA,EACF;AACF;AAEA,SAAS,oBAAoB,OAAwE;AACnG,SAAO;AAAA,IACL,YAAY;AAAA,IACZ,MAAM,EAAE,SAAS,GAAG,MAAM,YAAY,aAAa;AAAA,EACrD;AACF;AAEA,SAAS,gBACP,OACA,YACA,SACwC;AACxC,MAAI,MAAM,cAAc;AACtB,WAAO,EAAE,YAAY,KAAK,MAAM,WAAW;AAAA,EAC7C;AAEA,QAAM,SAAS,cAAc,OAAO,YAAY,OAAO;AACvD,SAAO,SAAS,EAAE,YAAY,KAAK,MAAM,OAAO,IAAI,oBAAoB,KAAK;AAC/E;AAEA,SAAS,kBACP,UACA,OACA,YACA,SACuD;AACvD,QAAM,SAAS,oBAAoB,OAAO,OAAO;AACjD,QAAM,QAAQ,aAAa,KAAK;AAChC,MAAI,OAAO,KAAK,MAAM,QAAW;AAC/B,WAAO,KAAK,IAAI,iBAAiB,UAAU,OAAO,YAAY,KAAK;AAAA,EACrE;AACA,aAAW,KAAK,MAAM;AACtB,SAAO,EAAE,YAAY,MAAM,iBAAiB,cAAc,KAAK,MAAM,OAAO;AAC9E;AAEA,SAAS,kBACP,OACA,YACA,SACwC;AACxC,QAAM,UAAU,cAAc,OAAO,YAAY,OAAO;AACxD,MAAI,CAAC,SAAS;AACZ,WAAO,oBAAoB,KAAK;AAAA,EAClC;AAEA,QAAM,SAAS,MAAM,WAAW,QAC5B,oBAAoB,OAAO,OAAO,IAClC,UAAU,SAAS,eAAe,OAAO,CAAC;AAE9C,SAAO,OAAO,SAAS,MAAM;AAC7B,SAAO,EAAE,YAAY,KAAK,MAAM,QAAQ;AAC1C;AAEA,SAAS,kBACP,OACA,YACA,SACwC;AACxC,QAAM,SAAS,cAAc,OAAO,YAAY,OAAO;AACvD,MAAI,CAAC,QAAQ;AACX,WAAO,oBAAoB,KAAK;AAAA,EAClC;AAEA,QAAM,QAAQ,WAAW,QAAQ,MAAM;AACvC,aAAW,OAAO,OAAO,CAAC;AAC1B,SAAO,EAAE,YAAY,IAAI;AAC3B;AAEA,eAAe,gBACb,OACA,OACA,SACiD;AACjD,SAAO,MAAM,aAAa,OAAO,aAAa;AAC5C,UAAM,aAAa,cAAc,UAAU,KAAK;AAChD,UAAM,YAAa,MAAM,UAAkC,cAAc;AAEzE,QAAI,WAAW;AACb,YAAM,mBAAmB,UAAU,SAC/B,oBAAoB,UAAU,UAAU,MAAM,IAC9C;AACJ,YAAM,WAAW,iBAAiB,WAAW,SAAS,kBAAkB,aAAa,KAAK,CAAC;AAC3F,UAAI,UAAU,aAAa,QAAQ;AACjC,eAAO,EAAE,YAAY,IAAI;AAAA,MAC3B;AAEA,UAAI,UAAU,aAAa,cAAc;AACvC,eAAO,EAAE,YAAY,MAAM,iBAAiB,cAAc,KAAK,MAAM,iBAAiB;AAAA,MACxF;AAEA,aAAO,EAAE,YAAY,MAAM,iBAAiB,cAAc,kBAAkB,KAAK,GAAG,MAAM,SAAS;AAAA,IACrG;AAEA,YAAQ,MAAM,QAAQ;AAAA,MACpB,KAAK,OAAO;AACV,eAAO,gBAAgB,OAAO,YAAY,OAAO;AAAA,MACnD;AAAA,MACA,KAAK,QAAQ;AACX,eAAO,kBAAkB,UAAU,OAAO,YAAY,OAAO;AAAA,MAC/D;AAAA,MACA,KAAK;AAAA,MACL,KAAK,SAAS;AACZ,eAAO,kBAAkB,OAAO,YAAY,OAAO;AAAA,MACrD;AAAA,MACA,KAAK,UAAU;AACb,eAAO,kBAAkB,OAAO,YAAY,OAAO;AAAA,MACrD;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAEA,eAAe,iBACb,QACA,SACkF;AAClF,QAAM,gBAAgB,OAAO;AAC7B,MAAI,CAAC,eAAe;AAClB,UAAM,IAAI,MAAM,gCAAgC;AAAA,EAClD;AAEA,QAAM,UAAU,eAAe,QAAQ,OAAO;AAC9C,MAAI,CAAC,QAAQ,cAAc,KAAK,QAAQ,SAAS,QAAW;AAC1D,YAAQ,cAAc,IAAI;AAAA,EAC5B;AAEA,QAAM,SAAS,MAAM,aAAa,eAAe,QAAQ,KAAK;AAAA,IAC5D,QAAQ,QAAQ;AAAA,IAChB;AAAA,IACA,MAAM,YAAY,OAAO;AAAA,EAC3B,CAAC;AAED,SAAO;AAAA,IACL,YAAY,OAAO;AAAA,IACnB,SAAS,OAAO,YAAY,OAAO,QAAQ,QAAQ,CAAC;AAAA,IACpD,MAAM,OAAO;AAAA,EACf;AACF;AAEA,eAAe,eACb,KACA,UACA,QACA,OACe;AACf,QAAM,SAAS,mBAAmB,QAAQ;AAC1C,QAAM,WAAW,IAAI,cAAc,IAAI,KAAK;AAAA,IAC1C,YAAY,OAAO;AAAA,IACnB,aAAa,OAAO;AAAA,IACpB,aAAa,OAAO;AAAA,EACtB,CAAC;AACD,QAAM,cAA4B,CAAC;AAEnC,QAAM,aAAa,OAAO,UAA4C;AACpE,QAAI,CAAC,OAAO;AACV;AAAA,IACF;AAEA,gBAAY,KAAK,KAAK;AACtB,UAAM,iBAAiB,QAAQ,WAAW;AAC1C,UAAM,OAAO,kBAAkB,KAAK;AAAA,EACtC;AAEA,aAAW,SAAS,QAAQ;AAC1B,QAAI,MAAM;AAAA,MACR,QAAQ,MAAM,OAAO,YAAY;AAAA,MACjC,KAAK,MAAM;AAAA,MACX,SAAS,OAAO,SAAS,UAAU;AACjC,YAAI,OAAO,kBAAkB;AAC3B,cAAI;AACF,kBAAM,UAAU,MAAM,iBAAiB,QAAQ,OAAO;AACtD,gBAAI,QAAQ,cAAc,OAAO,QAAQ,aAAa,OAAO,QAAQ,SAAS,QAAW;AACvF,oBAAM,QAAQ,SAAS;AAAA,gBACrB,MAAM;AAAA,gBACN,MAAM;AAAA,gBACN,QAAQ;AAAA,gBACR,MAAM,iBAAiB;AAAA,gBACvB,QAAQ;AAAA,cACV;AACA,oBAAM,WAAW,KAAK;AAAA,YACxB;AAEA,uBAAW,CAAC,YAAY,WAAW,KAAK,OAAO,QAAQ,QAAQ,OAAO,GAAG;AACvE,kBAAI,WAAW,YAAY,MAAM,kBAAkB;AACjD;AAAA,cACF;AACA,oBAAM,OAAO,YAAY,WAAW;AAAA,YACtC;AACA,mBAAO,MAAM,KAAK,QAAQ,UAAU,EAAE,KAAK,QAAQ,IAAI;AAAA,UACzD,SAAS,OAAO;AACd,gBAAI,IAAI,MAAM,EAAE,MAAM,GAAG,8BAA8B,MAAM,OAAO,YAAY,CAAC,IAAI,MAAM,IAAI,EAAE;AACjG,gBAAI,CAAC,OAAO,4BAA4B;AACtC,oBAAM;AAAA,YACR;AAAA,UACF;AAAA,QACF;AAEA,cAAM,eAAe,MAAM,gBAAgB,OAAO,OAAO,OAAO;AAChE,YACE,OAAO,yBACJ,aAAa,cAAc,OAC3B,aAAa,aAAa,OAC1B,aAAa,SAAS,QACzB;AACA,gBAAM,QAAQ,SAAS;AAAA,YACrB,MAAM;AAAA,YACN,MAAM;AAAA,YACN,aAAa;AAAA,YACb,MAAM,iBAAiB;AAAA,YACvB,aAAa;AAAA,UACf;AACA,gBAAM,WAAW,KAAK;AAAA,QACxB;AACA,eAAO,aAAa,OAAO,aAAa,YAAY,aAAa,IAAI;AAAA,MACvE;AAAA,IACF,CAAC;AAAA,EACH;AAEA,MAAI;AAAA,IAAI;AAAA,IAAa,YACnB,OAAO,IAAI,CAAC,WAAW;AAAA,MACrB,QAAQ,MAAM,OAAO,YAAY;AAAA,MACjC,MAAM,MAAM;AAAA,MACZ,aAAa,MAAM;AAAA,MACnB,UAAU,MAAM;AAAA,MAChB,QAAQ,QAAS,MAAM,UAAkC,cAAc,CAAC;AAAA,IAC1E,EAAE;AAAA,EACJ;AAEA,MAAI,IAAI,YAAY,aAAa;AAAA,IAC/B,OAAO,YAAY;AAAA,IACnB,QAAQ;AAAA,EACV,EAAE;AACJ;AAEA,eAAsB,aAAa,QAAoD;AACrF,QAAM,MAAM,QAAQ;AAAA,IAClB,QAAQ;AAAA,MACN,OAAO,OAAO,UAAU,UAAU;AAAA,IACpC;AAAA,EACF,CAAC;AAED,QAAM,IAAI,SAAS,MAAM;AAAA,IACvB,QAAQ,OAAO,eAAe,MAAM,OAAO,OAAO;AAAA,IAClD,SAAS,CAAC,OAAO,QAAQ,OAAO,SAAS,UAAU,SAAS;AAAA,IAC5D,gBAAgB,CAAC,gBAAgB,eAAe;AAAA,IAChD,aAAa;AAAA,EACf,CAAC;AAED,QAAM,WAAW,MAAM,oBAAoB,OAAO,QAAQ;AAC1D,QAAM,kBAAkB,qBAAqB,QAAQ;AACrD,QAAM,SAAS,YAAYE,MAAK,QAAQ,OAAO,QAAQ,GAAG,OAAO,MAAM;AACvE,QAAM,QAAQ,IAAI,eAAe,MAAM;AACvC,QAAM,MAAM,WAAW,eAAe;AAEtC,MAAI,IAAI,aAAa,aAAa,EAAE,QAAQ,KAAK,EAAE;AACnD,MAAI,IAAI,WAAW,YAAY,QAAQ;AAEvC,QAAM,eAAe,KAAK,UAAU,QAAQ,KAAK;AACjD,SAAO;AACT;;;AJ7ZA,SAAS,oBAAoB,QAYlB;AACT,QAAM,QAAQ;AAAA,IACZ,wDAAiD,OAAO,IAAI,IAAI,OAAO,IAAI;AAAA,IAC3E,WAAW,OAAO,QAAQ;AAAA,IAC1B,SAAS,OAAO,MAAM;AAAA,IACtB,WAAW,OAAO,mBAAmB,wBAAwB,OAAO,gBAAgB,MAAM,eAAe;AAAA,IACzG,0BAA0B,OAAO,cAAc,OAAO,KAAK,mBAAmB,OAAO,cAAc,OAAO,KAAK;AAAA,IAC/G,+BAA+B,OAAO,wBAAwB,iBAAiB,KAAK;AAAA,IACpF,eAAe,OAAO,QAAQ,GAAG,OAAO,aAAa,KAAK,OAAO,UAAU,MAAM,EAAE;AAAA,IACnF,mBAAmB,OAAO,iBAAiB,SAAS,OAAO,iBAAiB,KAAK,IAAI,IAAI,MAAM;AAAA,IAC/F;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,SAAO,GAAG,MAAM,KAAK,IAAI,CAAC;AAAA;AAC5B;AAEA,eAAe,OAAsB;AACnC,QAAM,MAAM,UAAU;AACtB,QAAM,eAAe,IAAI,SAAS,KAAK,CAAC,YAAY,QAAQ,KAAK,MAAM,OAAO;AAC9E,QAAM,cAAc,IAAI,SAAS,KAAK,CAAC,YAAY,QAAQ,KAAK,MAAM,MAAM;AAC5E,QAAM,oBAAoB,IAAI,SAAS,KAAK,CAAC,YAAY,QAAQ,KAAK,MAAM,YAAY;AAExF,QAAM,cAAc,OAAO,eAA6D;AACtF,QAAI,mBAAmB;AACvB,UAAM,SAAS,MAAM,mBAAmBC,SAAQ,IAAI,GAAG,UAAU;AACjE,WAAO,kBAAkB,YAAY;AACnC,UAAI,CAAC,OAAO,eAAe,kBAAkB;AAC3C;AAAA,MACF;AAEA,yBAAmB;AACnB,MAAAA,SAAQ,OAAO,MAAM,yEAAyE;AAC9F,iBAAW,MAAM;AACf,QAAAA,SAAQ,KAAK,CAAC;AAAA,MAChB,GAAG,EAAE;AAAA,IACP;AAEA,UAAM,SAAS,MAAM,aAAa,MAAM;AACxC,UAAM,OAAO,OAAO,EAAE,MAAM,OAAO,MAAM,MAAM,OAAO,KAAK,CAAC;AAC5D,IAAAA,SAAQ,OAAO,MAAM,oBAAoB,MAAM,CAAC;AAAA,EAClD;AAEA,gBAAc,OAAO,iBAAkB;AACrC,UAAM,YAAY,KAAK,KAAK,CAAC;AAAA,EAC/B,CAAC;AAED,MAAI,OAAO,YAAY;AACrB,UAAM,UAAU,IAAI,KAAmD;AACvE,QAAI,CAAC,QAAQ,QAAQ,CAAC,QAAQ,WAAW,CAAC,QAAQ,UAAU;AAC1D,UAAI,WAAW;AACf;AAAA,IACF;AAEA,UAAM,YAAY,OAAO;AAAA,EAC3B,CAAC;AAED,eAAa,OAAO,iBAAkB;AACpC,UAAM,UAAU,KAAK,KAAK;AAC1B,UAAM,aAAa,MAAM,mBAAmBA,SAAQ,IAAI,GAAG;AAAA,MACzD,MAAM,OAAO,QAAQ,IAAI;AAAA,MACzB,IAAI,OAAO,QAAQ,EAAE;AAAA,MACrB,MAAM,OAAO,QAAQ,IAAI;AAAA,MACzB,MAAM,OAAO,QAAQ,IAAI;AAAA,MACzB,UAAU,QAAQ,aAAa,SAAS,SAAS;AAAA,IACnD,CAAC;AACD,IAAAA,SAAQ,OAAO,MAAM,WAAW,UAAU;AAAA,CAAI;AAAA,EAChD,CAAC;AAED,qBAAmB,OAAO,iBAAkB;AAC1C,UAAM,UAAU,KAAK,KAAK;AAC1B,UAAM,MAAMA,SAAQ,IAAI;AACxB,UAAM,WAAW,OAAO,QAAQ,QAAQ,cAAc;AAEtD,UAAM,mBAAmB,KAAK;AAAA,MAC5B,MAAM;AAAA,MACN,IAAI,OAAO,QAAQ,MAAM,eAAe;AAAA,MACxC,MAAM,OAAO,QAAQ,QAAQ,SAAS;AAAA,MACtC,MAAM,OAAO,QAAQ,QAAQ,IAAI;AAAA,MACjC,UAAU;AAAA,IACZ,CAAC;AAED,UAAM,SAAS,MAAM,mBAAmB,KAAK;AAAA,MAC3C,MAAM;AAAA,MACN,IAAI,OAAO,QAAQ,MAAM,eAAe;AAAA,MACxC,MAAM,OAAO,QAAQ,QAAQ,SAAS;AAAA,MACtC,MAAM,OAAO,QAAQ,QAAQ,IAAI;AAAA,MACjC,YAAY,OAAO,QAAQ,cAAc,GAAG;AAAA,MAC5C,SAAS,QAAQ,QAAQ,OAAO;AAAA,IAClC,CAAC;AAED,UAAM,SAAS,MAAM,aAAa,MAAM;AACxC,UAAM,OAAO,OAAO,EAAE,MAAM,OAAO,MAAM,MAAM,OAAO,KAAK,CAAC;AAC5D,IAAAA,SAAQ,OAAO,MAAM,oBAAoB,MAAM,CAAC;AAAA,EAClD,CAAC;AAED,QAAM,IAAI,WAAWA,SAAQ,IAAI;AACnC;AAEA,MAAM,KAAK,EAAE,MAAM,CAAC,UAAU;AAC5B,MAAI,iBAAiB,OAAO;AAC1B,IAAAA,SAAQ,OAAO,MAAM,UAAU,MAAM,OAAO;AAAA,CAAI;AAChD,QAAIA,SAAQ,IAAI,mBAAmB,KAAK;AACtC,MAAAA,SAAQ,OAAO,MAAM,GAAG,MAAM,KAAK;AAAA,CAAI;AAAA,IACzC;AAAA,EACF,OAAO;AACL,IAAAA,SAAQ,OAAO,MAAM,GAAG,OAAO,KAAK,CAAC;AAAA,CAAI;AAAA,EAC3C;AACA,EAAAA,SAAQ,WAAW;AACrB,CAAC;","names":["process","mkdir","writeFile","path","path","mkdir","writeFile","path","path","deepClone","path","mkdir","readFile","writeFile","path","mkdir","path","readFile","writeFile","path","mkdir","writeFile","path","path","process"]}
package/dist/index.d.ts CHANGED
@@ -1,6 +1,12 @@
1
1
  import { FastifyInstance } from 'fastify';
2
2
  import { OpenAPIV3 } from 'openapi-types';
3
3
 
4
+ interface DriftIgnoreRule {
5
+ methodPattern: string;
6
+ pathPattern: string;
7
+ jsonPathPattern: string;
8
+ }
9
+
4
10
  type HttpMethod = 'get' | 'post' | 'put' | 'patch' | 'delete';
5
11
  interface MockEngineConfig {
6
12
  specPath: string;
@@ -12,6 +18,7 @@ interface MockEngineConfig {
12
18
  fallbackToMockOnProxyError: boolean;
13
19
  strictDrift: boolean;
14
20
  driftIgnorePaths: string[];
21
+ driftIgnoreRules: DriftIgnoreRule[];
15
22
  reporter: 'pretty' | 'json' | 'junit';
16
23
  reportFile?: string;
17
24
  failOnDrift: boolean;
package/dist/index.js CHANGED
@@ -156,6 +156,54 @@ function applyDslMutation(extension, request, collection, defaultIdKey) {
156
156
  import Ajv from "ajv";
157
157
  import addFormats from "ajv-formats";
158
158
  import pc from "picocolors";
159
+
160
+ // src/drift-ignore.ts
161
+ function wildcardToRegex(pattern, caseInsensitive = false) {
162
+ const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
163
+ return new RegExp(`^${escaped}$`, caseInsensitive ? "i" : void 0);
164
+ }
165
+ function normalizePathPattern(pathPattern) {
166
+ if (pathPattern === "*") {
167
+ return "*";
168
+ }
169
+ return pathPattern.startsWith("/") ? pathPattern : `/${pathPattern}`;
170
+ }
171
+ function normalizeJsonPathPattern(jsonPathPattern) {
172
+ if (jsonPathPattern === "*") {
173
+ return "body.*";
174
+ }
175
+ if (jsonPathPattern.startsWith("/")) {
176
+ const segments = jsonPathPattern.split("/").filter(Boolean).map((segment) => segment.replace(/~1/g, "/").replace(/~0/g, "~"));
177
+ return segments.length ? `body.${segments.join(".")}` : "body";
178
+ }
179
+ return jsonPathPattern.startsWith("body") ? jsonPathPattern : `body.${jsonPathPattern}`;
180
+ }
181
+ function normalizeMethodPattern(methodPattern) {
182
+ return methodPattern === "*" ? "*" : methodPattern.toUpperCase();
183
+ }
184
+ function normalizeRequestPath(requestPath) {
185
+ const [pathOnly] = requestPath.split("?");
186
+ return pathOnly || "/";
187
+ }
188
+ function instancePathToJsonPath(instancePath) {
189
+ const segments = instancePath.split("/").filter(Boolean).map((segment) => segment.replace(/~1/g, "/").replace(/~0/g, "~"));
190
+ return segments.length ? `body.${segments.join(".")}` : "body";
191
+ }
192
+ function matchesRule(rule, method, requestPath, jsonPath) {
193
+ const methodRegex = wildcardToRegex(normalizeMethodPattern(rule.methodPattern), true);
194
+ const pathRegex = wildcardToRegex(normalizePathPattern(rule.pathPattern));
195
+ const jsonPathRegex = wildcardToRegex(normalizeJsonPathPattern(rule.jsonPathPattern));
196
+ return methodRegex.test(method.toUpperCase()) && pathRegex.test(normalizeRequestPath(requestPath)) && jsonPathRegex.test(jsonPath);
197
+ }
198
+ function shouldIgnoreInstancePath(instancePath, requestMethod, requestPath, ignoreRules) {
199
+ if (!ignoreRules.length) {
200
+ return false;
201
+ }
202
+ const jsonPath = instancePathToJsonPath(instancePath);
203
+ return ignoreRules.some((rule) => matchesRule(rule, requestMethod, requestPath, jsonPath));
204
+ }
205
+
206
+ // src/drift-detector.ts
159
207
  function deepClone2(value) {
160
208
  return JSON.parse(JSON.stringify(value));
161
209
  }
@@ -222,7 +270,7 @@ function analyzeDrift(ajv, schema, body, options) {
222
270
  return [];
223
271
  }
224
272
  const filteredErrors = (validate.errors ?? []).filter(
225
- (entry) => !options.ignorePaths.some((rule) => pathMatchesIgnoreRule(entry.instancePath || "/", rule))
273
+ (entry) => !options.ignorePaths.some((rule) => pathMatchesIgnoreRule(entry.instancePath || "/", rule)) && !shouldIgnoreInstancePath(entry.instancePath || "/", options.method, options.path, options.ignoreRules)
226
274
  );
227
275
  return formatErrors(filteredErrors);
228
276
  }
@@ -232,17 +280,22 @@ var DriftDetector = class {
232
280
  addFormats(this.ajv);
233
281
  this.strictMode = options?.strictMode ?? false;
234
282
  this.ignorePaths = options?.ignorePaths ?? [];
283
+ this.ignoreRules = options?.ignoreRules ?? [];
235
284
  }
236
285
  ajv = new Ajv({ allErrors: true, strict: false });
237
286
  strictMode;
238
287
  ignorePaths;
288
+ ignoreRules;
239
289
  validate(method, path5, statusCode, schema, body) {
240
290
  if (!schema || body === void 0 || body === null) {
241
291
  return null;
242
292
  }
243
293
  const errors = analyzeDrift(this.ajv, schema, body, {
294
+ method,
295
+ path: path5,
244
296
  strictMode: this.strictMode,
245
- ignorePaths: this.ignorePaths
297
+ ignorePaths: this.ignorePaths,
298
+ ignoreRules: this.ignoreRules
246
299
  });
247
300
  if (!errors.length) {
248
301
  return null;
@@ -736,7 +789,8 @@ async function registerRoutes(app, document, config, store) {
736
789
  const routes = buildRouteContexts(document);
737
790
  const detector = new DriftDetector(app.log, {
738
791
  strictMode: config.strictDrift,
739
- ignorePaths: config.driftIgnorePaths
792
+ ignorePaths: config.driftIgnorePaths,
793
+ ignoreRules: config.driftIgnoreRules
740
794
  });
741
795
  const driftIssues = [];
742
796
  const trackIssue = async (issue) => {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../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","../src/drift-reporter.ts"],"sourcesContent":["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 { DriftIssue, MockEngineConfig, MockOperationObject, RouteContext } from './types.js';\nimport { proxyRequest } from './proxy.js';\nimport { writeDriftReport } from './drift-reporter.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 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 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 strictMode: config.strictDrift,\n ignorePaths: config.driftIgnorePaths,\n });\n const driftIssues: DriftIssue[] = [];\n\n const trackIssue = async (issue: DriftIssue | null): Promise<void> => {\n if (!issue) {\n return;\n }\n\n driftIssues.push(issue);\n await writeDriftReport(config, driftIssues);\n await config.onDriftDetected?.(issue);\n };\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, request);\n if (proxied.statusCode >= 200 && proxied.statusCode < 300 && proxied.body !== undefined) {\n const issue = detector.validate(\n route.method,\n route.path,\n proxied.statusCode,\n route.successResponse?.schema,\n proxied.body,\n );\n await trackIssue(issue);\n }\n\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 if (\n config.validateMockResponses\n && mockResponse.statusCode >= 200\n && mockResponse.statusCode < 300\n && mockResponse.body !== undefined\n ) {\n const issue = detector.validate(\n route.method,\n route.path,\n mockResponse.statusCode,\n route.successResponse?.schema,\n mockResponse.body,\n );\n await trackIssue(issue);\n }\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 app.get('/__drift', async () => ({\n total: driftIssues.length,\n issues: driftIssues,\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 deepClone<T>(value: T): T {\n return JSON.parse(JSON.stringify(value)) as T;\n}\n\nfunction pathMatchesIgnoreRule(instancePath: string, rule: string): boolean {\n if (!rule) {\n return false;\n }\n\n const pathSegments = instancePath.split('/').filter(Boolean);\n const ruleSegments = rule.split('/').filter(Boolean);\n\n if (ruleSegments.length > pathSegments.length) {\n return false;\n }\n\n return ruleSegments.every((segment, index) => segment === '*' || segment === pathSegments[index]);\n}\n\nfunction applyStrictAdditionalProperties(schema: OpenAPIV3.SchemaObject): OpenAPIV3.SchemaObject {\n const output = deepClone(schema);\n\n const walk = (candidate: OpenAPIV3.SchemaObject): void => {\n if (candidate.type === 'object' || candidate.properties) {\n if (candidate.additionalProperties === undefined) {\n candidate.additionalProperties = false;\n }\n\n for (const value of Object.values(candidate.properties ?? {})) {\n if (value && !('$ref' in value)) {\n walk(value);\n }\n }\n }\n\n if (candidate.type === 'array' && candidate.items && !('$ref' in candidate.items)) {\n walk(candidate.items);\n }\n\n for (const entry of candidate.allOf ?? []) {\n if (!('$ref' in entry)) {\n walk(entry);\n }\n }\n\n for (const entry of candidate.oneOf ?? []) {\n if (!('$ref' in entry)) {\n walk(entry);\n }\n }\n\n for (const entry of candidate.anyOf ?? []) {\n if (!('$ref' in entry)) {\n walk(entry);\n }\n }\n };\n\n walk(output);\n return output;\n}\n\nfunction buildAjvSchema(\n schema: OpenAPIV3.SchemaObject,\n strictMode: boolean,\n): OpenAPIV3.SchemaObject {\n return strictMode ? applyStrictAdditionalProperties(schema) : schema;\n}\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 function analyzeDrift(\n ajv: Ajv,\n schema: OpenAPIV3.SchemaObject,\n body: unknown,\n options: {\n strictMode: boolean;\n ignorePaths: string[];\n },\n): string[] {\n const normalizedSchema = buildAjvSchema(schema, options.strictMode);\n const validate = ajv.compile(normalizedSchema);\n const valid = validate(body);\n\n if (valid) {\n return [];\n }\n\n const filteredErrors = (validate.errors ?? []).filter((entry) =>\n !options.ignorePaths.some((rule) => pathMatchesIgnoreRule(entry.instancePath || '/', rule)),\n );\n\n return formatErrors(filteredErrors);\n}\n\nexport class DriftDetector {\n private readonly ajv = new Ajv({ allErrors: true, strict: false });\n private readonly strictMode: boolean;\n private readonly ignorePaths: string[];\n\n constructor(\n private readonly logger: FastifyBaseLogger,\n options?: {\n strictMode?: boolean;\n ignorePaths?: string[];\n },\n ) {\n addFormats(this.ajv);\n this.strictMode = options?.strictMode ?? false;\n this.ignorePaths = options?.ignorePaths ?? [];\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 errors = analyzeDrift(this.ajv, schema, body, {\n strictMode: this.strictMode,\n ignorePaths: this.ignorePaths,\n });\n\n if (!errors.length) {\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,\n };\n\n const errorLines = issue.errors.map((entry) => ` • ${entry}`).join('\\n');\n const prettyBlock = [\n `${pc.bgRed(pc.black(' 🚨 DRIFT DETECTED '))} ${pc.bold(issue.method)} ${issue.path} (${statusCode})`,\n errorLines,\n ].join('\\n');\n\n process.stderr.write(`${prettyBlock}\\n`);\n this.logger.debug({ drift: issue }, issue.message);\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}","import { mkdir, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport type { DriftIssue, DriftReport, MockEngineConfig } from './types.js';\n\nfunction escapeXml(value: string): string {\n return value\n .replaceAll('&', '&amp;')\n .replaceAll('<', '&lt;')\n .replaceAll('>', '&gt;')\n .replaceAll('\"', '&quot;')\n .replaceAll(\"'\", '&apos;');\n}\n\nfunction toJunit(issues: DriftIssue[]): string {\n const testCases = issues\n .map((issue, index) => {\n const name = `${issue.method} ${issue.path} (${issue.statusCode}) #${index + 1}`;\n const failure = escapeXml(issue.errors.join('; '));\n return ` <testcase classname=\"contract-drift-detection\" name=\"${escapeXml(name)}\">\\n <failure message=\"drift detected\">${failure}</failure>\\n </testcase>`;\n })\n .join('\\n');\n\n return [\n '<?xml version=\"1.0\" encoding=\"UTF-8\"?>',\n `<testsuite name=\"contract-drift-detection\" tests=\"${issues.length}\" failures=\"${issues.length}\">`,\n testCases,\n '</testsuite>',\n '',\n ].join('\\n');\n}\n\nfunction toJson(issues: DriftIssue[]): string {\n const report: DriftReport = {\n generatedAt: new Date().toISOString(),\n summary: {\n total: issues.length,\n },\n issues,\n };\n\n return `${JSON.stringify(report, null, 2)}\\n`;\n}\n\nfunction defaultReportPath(config: MockEngineConfig): string {\n if (config.reportFile) {\n return config.reportFile;\n }\n\n return config.reporter === 'junit' ? 'cdd-drift-report.xml' : 'cdd-drift-report.json';\n}\n\nexport async function writeDriftReport(\n config: MockEngineConfig,\n issues: DriftIssue[],\n): Promise<string | null> {\n if (config.reporter === 'pretty') {\n return null;\n }\n\n const filePath = defaultReportPath(config);\n const content = config.reporter === 'junit' ? toJunit(issues) : toJson(issues);\n await mkdir(path.dirname(filePath), { recursive: true });\n await writeFile(filePath, content, 'utf8');\n return filePath;\n}"],"mappings":"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","names":["path","deepClone","path","path","path","path","mkdir","writeFile","path","path"]}
1
+ {"version":3,"sources":["../src/server.ts","../src/utils.ts","../src/dsl.ts","../src/drift-detector.ts","../src/drift-ignore.ts","../src/route-context.ts","../src/schema-seeder.ts","../src/spec-loader.ts","../src/state-store.ts","../src/proxy.ts","../src/drift-reporter.ts"],"sourcesContent":["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 { DriftIssue, MockEngineConfig, MockOperationObject, RouteContext } from './types.js';\nimport { proxyRequest } from './proxy.js';\nimport { writeDriftReport } from './drift-reporter.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 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 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 strictMode: config.strictDrift,\n ignorePaths: config.driftIgnorePaths,\n ignoreRules: config.driftIgnoreRules,\n });\n const driftIssues: DriftIssue[] = [];\n\n const trackIssue = async (issue: DriftIssue | null): Promise<void> => {\n if (!issue) {\n return;\n }\n\n driftIssues.push(issue);\n await writeDriftReport(config, driftIssues);\n await config.onDriftDetected?.(issue);\n };\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, request);\n if (proxied.statusCode >= 200 && proxied.statusCode < 300 && proxied.body !== undefined) {\n const issue = detector.validate(\n route.method,\n route.path,\n proxied.statusCode,\n route.successResponse?.schema,\n proxied.body,\n );\n await trackIssue(issue);\n }\n\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 if (\n config.validateMockResponses\n && mockResponse.statusCode >= 200\n && mockResponse.statusCode < 300\n && mockResponse.body !== undefined\n ) {\n const issue = detector.validate(\n route.method,\n route.path,\n mockResponse.statusCode,\n route.successResponse?.schema,\n mockResponse.body,\n );\n await trackIssue(issue);\n }\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 app.get('/__drift', async () => ({\n total: driftIssues.length,\n issues: driftIssues,\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 { shouldIgnoreInstancePath, type DriftIgnoreRule } from './drift-ignore.js';\nimport type { DriftIssue } from './types.js';\n\nfunction deepClone<T>(value: T): T {\n return JSON.parse(JSON.stringify(value)) as T;\n}\n\nfunction pathMatchesIgnoreRule(instancePath: string, rule: string): boolean {\n if (!rule) {\n return false;\n }\n\n const pathSegments = instancePath.split('/').filter(Boolean);\n const ruleSegments = rule.split('/').filter(Boolean);\n\n if (ruleSegments.length > pathSegments.length) {\n return false;\n }\n\n return ruleSegments.every((segment, index) => segment === '*' || segment === pathSegments[index]);\n}\n\nfunction applyStrictAdditionalProperties(schema: OpenAPIV3.SchemaObject): OpenAPIV3.SchemaObject {\n const output = deepClone(schema);\n\n const walk = (candidate: OpenAPIV3.SchemaObject): void => {\n if (candidate.type === 'object' || candidate.properties) {\n if (candidate.additionalProperties === undefined) {\n candidate.additionalProperties = false;\n }\n\n for (const value of Object.values(candidate.properties ?? {})) {\n if (value && !('$ref' in value)) {\n walk(value);\n }\n }\n }\n\n if (candidate.type === 'array' && candidate.items && !('$ref' in candidate.items)) {\n walk(candidate.items);\n }\n\n for (const entry of candidate.allOf ?? []) {\n if (!('$ref' in entry)) {\n walk(entry);\n }\n }\n\n for (const entry of candidate.oneOf ?? []) {\n if (!('$ref' in entry)) {\n walk(entry);\n }\n }\n\n for (const entry of candidate.anyOf ?? []) {\n if (!('$ref' in entry)) {\n walk(entry);\n }\n }\n };\n\n walk(output);\n return output;\n}\n\nfunction buildAjvSchema(\n schema: OpenAPIV3.SchemaObject,\n strictMode: boolean,\n): OpenAPIV3.SchemaObject {\n return strictMode ? applyStrictAdditionalProperties(schema) : schema;\n}\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 function analyzeDrift(\n ajv: Ajv,\n schema: OpenAPIV3.SchemaObject,\n body: unknown,\n options: {\n method: string;\n path: string;\n strictMode: boolean;\n ignorePaths: string[];\n ignoreRules: DriftIgnoreRule[];\n },\n): string[] {\n const normalizedSchema = buildAjvSchema(schema, options.strictMode);\n const validate = ajv.compile(normalizedSchema);\n const valid = validate(body);\n\n if (valid) {\n return [];\n }\n\n const filteredErrors = (validate.errors ?? []).filter((entry) =>\n !options.ignorePaths.some((rule) => pathMatchesIgnoreRule(entry.instancePath || '/', rule))\n && !shouldIgnoreInstancePath(entry.instancePath || '/', options.method, options.path, options.ignoreRules),\n );\n\n return formatErrors(filteredErrors);\n}\n\nexport class DriftDetector {\n private readonly ajv = new Ajv({ allErrors: true, strict: false });\n private readonly strictMode: boolean;\n private readonly ignorePaths: string[];\n private readonly ignoreRules: DriftIgnoreRule[];\n\n constructor(\n private readonly logger: FastifyBaseLogger,\n options?: {\n strictMode?: boolean;\n ignorePaths?: string[];\n ignoreRules?: DriftIgnoreRule[];\n },\n ) {\n addFormats(this.ajv);\n this.strictMode = options?.strictMode ?? false;\n this.ignorePaths = options?.ignorePaths ?? [];\n this.ignoreRules = options?.ignoreRules ?? [];\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 errors = analyzeDrift(this.ajv, schema, body, {\n method,\n path,\n strictMode: this.strictMode,\n ignorePaths: this.ignorePaths,\n ignoreRules: this.ignoreRules,\n });\n\n if (!errors.length) {\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,\n };\n\n const errorLines = issue.errors.map((entry) => ` • ${entry}`).join('\\n');\n const prettyBlock = [\n `${pc.bgRed(pc.black(' 🚨 DRIFT DETECTED '))} ${pc.bold(issue.method)} ${issue.path} (${statusCode})`,\n errorLines,\n ].join('\\n');\n\n process.stderr.write(`${prettyBlock}\\n`);\n this.logger.debug({ drift: issue }, issue.message);\n\n return issue;\n }\n}","export interface DriftIgnoreRule {\n methodPattern: string;\n pathPattern: string;\n jsonPathPattern: string;\n}\n\nfunction wildcardToRegex(pattern: string, caseInsensitive = false): RegExp {\n const escaped = pattern\n .replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&')\n .replace(/\\*/g, '.*');\n return new RegExp(`^${escaped}$`, caseInsensitive ? 'i' : undefined);\n}\n\nfunction normalizePathPattern(pathPattern: string): string {\n if (pathPattern === '*') {\n return '*';\n }\n\n return pathPattern.startsWith('/') ? pathPattern : `/${pathPattern}`;\n}\n\nfunction normalizeJsonPathPattern(jsonPathPattern: string): string {\n if (jsonPathPattern === '*') {\n return 'body.*';\n }\n\n if (jsonPathPattern.startsWith('/')) {\n const segments = jsonPathPattern\n .split('/')\n .filter(Boolean)\n .map((segment) => segment.replace(/~1/g, '/').replace(/~0/g, '~'));\n return segments.length ? `body.${segments.join('.')}` : 'body';\n }\n\n return jsonPathPattern.startsWith('body') ? jsonPathPattern : `body.${jsonPathPattern}`;\n}\n\nfunction normalizeMethodPattern(methodPattern: string): string {\n return methodPattern === '*' ? '*' : methodPattern.toUpperCase();\n}\n\nfunction normalizeRequestPath(requestPath: string): string {\n const [pathOnly] = requestPath.split('?');\n return pathOnly || '/';\n}\n\nfunction extractInstancePathFromError(error: string): string {\n const token = error.trim().split(/\\s+/u)[0] ?? '/';\n return token.startsWith('/') ? token : '/';\n}\n\nexport function instancePathToJsonPath(instancePath: string): string {\n const segments = instancePath\n .split('/')\n .filter(Boolean)\n .map((segment) => segment.replace(/~1/g, '/').replace(/~0/g, '~'));\n\n return segments.length ? `body.${segments.join('.')}` : 'body';\n}\n\nfunction matchesRule(\n rule: DriftIgnoreRule,\n method: string,\n requestPath: string,\n jsonPath: string,\n): boolean {\n const methodRegex = wildcardToRegex(normalizeMethodPattern(rule.methodPattern), true);\n const pathRegex = wildcardToRegex(normalizePathPattern(rule.pathPattern));\n const jsonPathRegex = wildcardToRegex(normalizeJsonPathPattern(rule.jsonPathPattern));\n\n return methodRegex.test(method.toUpperCase())\n && pathRegex.test(normalizeRequestPath(requestPath))\n && jsonPathRegex.test(jsonPath);\n}\n\nexport function parseDriftIgnoreFile(content: string): DriftIgnoreRule[] {\n const rules: DriftIgnoreRule[] = [];\n\n for (const [index, rawLine] of content.split(/\\r?\\n/u).entries()) {\n const line = rawLine.trim();\n if (!line || line.startsWith('#')) {\n continue;\n }\n\n const parts = line.split(/\\s+/u);\n if (parts.length === 1) {\n rules.push({\n methodPattern: '*',\n pathPattern: '*',\n jsonPathPattern: parts[0],\n });\n continue;\n }\n\n if (parts.length < 3) {\n throw new Error(`Invalid .driftignore rule at line ${index + 1}: \"${rawLine}\"`);\n }\n\n const [methodPattern, pathPattern, ...jsonPathParts] = parts;\n rules.push({\n methodPattern,\n pathPattern,\n jsonPathPattern: jsonPathParts.join(' '),\n });\n }\n\n return rules;\n}\n\nexport function filterDriftErrors(\n errors: string[],\n requestMethod: string,\n requestPath: string,\n ignoreRules: DriftIgnoreRule[],\n): string[] {\n if (!ignoreRules.length) {\n return errors;\n }\n\n return errors.filter((error) => {\n const instancePath = extractInstancePathFromError(error);\n const jsonPath = instancePathToJsonPath(instancePath);\n\n return !ignoreRules.some((rule) => matchesRule(rule, requestMethod, requestPath, jsonPath));\n });\n}\n\nexport function shouldIgnoreInstancePath(\n instancePath: string,\n requestMethod: string,\n requestPath: string,\n ignoreRules: DriftIgnoreRule[],\n): boolean {\n if (!ignoreRules.length) {\n return false;\n }\n\n const jsonPath = instancePathToJsonPath(instancePath);\n return ignoreRules.some((rule) => matchesRule(rule, requestMethod, requestPath, jsonPath));\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}","import { mkdir, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport type { DriftIssue, DriftReport, MockEngineConfig } from './types.js';\n\nfunction escapeXml(value: string): string {\n return value\n .replaceAll('&', '&amp;')\n .replaceAll('<', '&lt;')\n .replaceAll('>', '&gt;')\n .replaceAll('\"', '&quot;')\n .replaceAll(\"'\", '&apos;');\n}\n\nfunction toJunit(issues: DriftIssue[]): string {\n const testCases = issues\n .map((issue, index) => {\n const name = `${issue.method} ${issue.path} (${issue.statusCode}) #${index + 1}`;\n const failure = escapeXml(issue.errors.join('; '));\n return ` <testcase classname=\"contract-drift-detection\" name=\"${escapeXml(name)}\">\\n <failure message=\"drift detected\">${failure}</failure>\\n </testcase>`;\n })\n .join('\\n');\n\n return [\n '<?xml version=\"1.0\" encoding=\"UTF-8\"?>',\n `<testsuite name=\"contract-drift-detection\" tests=\"${issues.length}\" failures=\"${issues.length}\">`,\n testCases,\n '</testsuite>',\n '',\n ].join('\\n');\n}\n\nfunction toJson(issues: DriftIssue[]): string {\n const report: DriftReport = {\n generatedAt: new Date().toISOString(),\n summary: {\n total: issues.length,\n },\n issues,\n };\n\n return `${JSON.stringify(report, null, 2)}\\n`;\n}\n\nfunction defaultReportPath(config: MockEngineConfig): string {\n if (config.reportFile) {\n return config.reportFile;\n }\n\n return config.reporter === 'junit' ? 'cdd-drift-report.xml' : 'cdd-drift-report.json';\n}\n\nexport async function writeDriftReport(\n config: MockEngineConfig,\n issues: DriftIssue[],\n): Promise<string | null> {\n if (config.reporter === 'pretty') {\n return null;\n }\n\n const filePath = defaultReportPath(config);\n const content = config.reporter === 'junit' ? toJunit(issues) : toJson(issues);\n await mkdir(path.dirname(filePath), { recursive: true });\n await writeFile(filePath, content, 'utf8');\n return filePath;\n}"],"mappings":"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","names":["path","deepClone","path","path","path","path","mkdir","writeFile","path","path"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "contract-drift-detection",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "Stateful OpenAPI mock server with contract drift detection",
5
5
  "repository": {
6
6
  "type": "git",