Repository: pmcelhaney/counterfact
Pull request #281 (2022-10-21 21:58:25)

OpenAPI 2 support
Add support for OpenAPI 2. This is hacky. More unit tests are needed. I should look at the actual spec version rather than duck type. 

Maybe create subclasses of each Coder for OpenAPI 2. That implies a factory for creating coders instead of calling their constructors directly. That may be a better design in the long run, but I don't think I want to deal with that right now. 

- add a shortcut for generating (testing) pet store v2
- support for OAS2 parameter, where there is not a separate schema object
- generate a default response for an OAS2 operation
- refactoring caused whitespace changes in the snapshot
- generate the response type for OAS2
- move the code to copy response-builder-factory.ts into generate()
- move copying response-builder-factory.ts into repository.js
- refactoring changed whitespace in the snapshot
- support OpenAPI2 when building the response object
- don't start the server when testing via yarn go:petstore
- support the request body in OpenAPI 2
- consistently set request properties to 'never' when they should not be there



Patch:
diff --git a/.changeset/shaggy-snakes-trade.md b/.changeset/shaggy-snakes-trade.md
new file mode 100644
index 0000000..8627e75
--- /dev/null
+++ b/.changeset/shaggy-snakes-trade.md
@@ -0,0 +1,5 @@
+---
+"counterfact": minor
+---
+
+Experimental support for OpenAPI 2
diff --git a/package.json b/package.json
index bb4c968..29388a1 100644
--- a/package.json
+++ b/package.json
@@ -30,7 +30,8 @@
     "prepare": "husky install",
     "lint": "eslint . --plugin file-progress --rule 'file-progress/activate: 1'",
     "lint:quickfix": "eslint --fix . eslint --fix demo-ts --rule=\"import/namespace: 0,etc/no-deprecated:0,import/no-cycle:0,no-explicit-type-exports/no-explicit-type-exports:0,import/no-deprecated:0,import/no-self-import:0,import/default:0,import/no-named-as-default:0\"",
-    "go:petstore": "yarn counterfact  https://petstore3.swagger.io/api/v3/openapi.json out --open",
+    "go:petstore": "yarn counterfact  https://petstore3.swagger.io/api/v3/openapi.json out",
+    "go:petstore2": "yarn counterfact  https://petstore.swagger.io/v2/swagger.json out",
     "go:example": "yarn counterfact ./openapi-example.yaml out --open",
     "counterfact": "./bin/counterfact.js"
   },
diff --git a/src/server/counterfact.js b/src/server/counterfact.js
index 4d7f2d9..8dbb883 100644
--- a/src/server/counterfact.js
+++ b/src/server/counterfact.js
@@ -15,9 +15,6 @@ import { ModuleLoader } from "./module-loader.js";
 import { Transpiler } from "./transpiler.js";
 import { ContextRegistry } from "./context-registry.js";
 
-// eslint-disable-next-line no-underscore-dangle
-const __dirname = nodePath.dirname(new URL(import.meta.url).pathname);
-
 async function loadOpenApiDocument(source) {
   try {
     return $RefParser.dereference(await yaml.load(await readFile(source)));
@@ -39,11 +36,6 @@ export async function counterfact(
     nodePath.join(os.tmpdir(), "counterfact-")
   )}/`;
 
-  fs.copyFile(
-    nodePath.join(__dirname, "../../templates/response-builder-factory.ts"),
-    nodePath.join(basePath, "response-builder-factory.ts")
-  );
-
   try {
     await fs.writeFile(
       nodePath.join(modulesPath, "package.json"),
diff --git a/src/typescript-generator/operation-coder.js b/src/typescript-generator/operation-coder.js
index b70a62a..38787fa 100644
--- a/src/typescript-generator/operation-coder.js
+++ b/src/typescript-generator/operation-coder.js
@@ -21,7 +21,7 @@ export class OperationCoder extends Coder {
     );
     const [firstResponse] = responses.map((response) => response.data);
 
-    if (!("content" in firstResponse)) {
+    if (!("content" in firstResponse || "schema" in firstResponse)) {
       return "() => { /* no response content specified in the OpenAPI document */ }";
     }
 
diff --git a/src/typescript-generator/operation-type-coder.js b/src/typescript-generator/operation-type-coder.js
index e1987aa..00324be 100644
--- a/src/typescript-generator/operation-type-coder.js
+++ b/src/typescript-generator/operation-type-coder.js
@@ -22,19 +22,32 @@ export class OperationTypeCoder extends Coder {
             ? "number | undefined"
             : Number.parseInt(responseCode, 10);
 
-        if (!response.has("content")) {
-          return `{  
-            status: ${status} 
-          }`;
+        if (response.has("content")) {
+          return response.get("content").map(
+            (content, contentType) => `{  
+              status: ${status}, 
+              contentType?: "${contentType}",
+              body?: ${new SchemaTypeCoder(content.get("schema")).write(script)}
+            }`
+          );
         }
 
-        return response.get("content").map(
-          (content, contentType) => `{  
-            status: ${status}, 
+        if (response.has("schema")) {
+          return this.requirement
+            .get("produces")
+            .data.map(
+              (contentType) => `{
+            status: ${status},
             contentType?: "${contentType}",
-            body?: ${new SchemaTypeCoder(content.get("schema")).write(script)}
+            body?: ${new SchemaTypeCoder(response.get("schema")).write(script)}
           }`
-        );
+            )
+            .join(" | ");
+        }
+
+        return `{  
+          status: ${status} 
+        }`;
       })
 
       .join(" | ");
@@ -55,31 +68,38 @@ export class OperationTypeCoder extends Coder {
     );
 
     const parameters = this.requirement.get("parameters");
+
     const queryType =
       parameters === undefined
-        ? "undefined"
+        ? "never"
         : new ParametersTypeCoder(parameters, "query").write(script);
 
     const pathType =
       parameters === undefined
-        ? "undefined"
+        ? "never"
         : new ParametersTypeCoder(parameters, "path").write(script);
 
     const headerType =
       parameters === undefined
-        ? "undefined"
+        ? "never"
         : new ParametersTypeCoder(parameters, "header").write(script);
 
-    const bodyRequirement = this.requirement.select(
-      "requestBody/content/application~1json/schema"
-    );
+    const bodyRequirement = this.requirement.get("consumes")
+      ? this.requirement
+          .get("parameters")
+          .find((parameter) =>
+            ["body", "formData"].includes(parameter.get("in").data)
+          )
+          .get("schema")
+      : this.requirement.select("requestBody/content/application~1json/schema");
 
     const bodyType = bodyRequirement
       ? new SchemaTypeCoder(bodyRequirement).write(script)
       : "undefined";
 
     const responseType = new ResponseTypeCoder(
-      this.requirement.get("responses")
+      this.requirement.get("responses"),
+      this.requirement.get("produces")?.data
     ).write(script);
 
     return `({ query, path, header, body, context }: { query: ${queryType}, path: ${pathType}, header: ${headerType}, body: ${bodyType}, context: typeof ${contextImportName}, response: ${responseType} }) => ${this.responseTypes(
diff --git a/src/typescript-generator/parameters-type-coder.js b/src/typescript-generator/parameters-type-coder.js
index 5bc2478..4001a11 100644
--- a/src/typescript-generator/parameters-type-coder.js
+++ b/src/typescript-generator/parameters-type-coder.js
@@ -16,7 +16,7 @@ export class ParametersTypeCoder extends Coder {
 
   write(script) {
     if (!this.requirement) {
-      return "undefined";
+      return "never";
     }
 
     const typeDefinitions = this.requirement.data
@@ -28,9 +28,13 @@ export class ParametersTypeCoder extends Coder {
 
         const optionalFlag = required ? "" : "?";
 
-        return `${name}${optionalFlag}: ${new SchemaTypeCoder(
-          requirement.get("schema")
-        ).write(script)}`;
+        const schema = requirement.has("schema")
+          ? requirement.get("schema")
+          : requirement;
+
+        return `${name}${optionalFlag}: ${new SchemaTypeCoder(schema).write(
+          script
+        )}`;
       });
 
     if (typeDefinitions.length === 0) {
diff --git a/src/typescript-generator/repository.js b/src/typescript-generator/repository.js
index 71e2bee..48fab60 100644
--- a/src/typescript-generator/repository.js
+++ b/src/typescript-generator/repository.js
@@ -6,6 +6,9 @@ import prettier from "prettier";
 
 import { Script } from "./script.js";
 
+// eslint-disable-next-line no-underscore-dangle
+const __dirname = nodePath.dirname(new URL(import.meta.url).pathname);
+
 async function ensureDirectoryExists(filePath) {
   const directory = nodePath.dirname(filePath);
 
@@ -44,6 +47,17 @@ export class Repository {
     }
   }
 
+  copyCoreFiles(destination) {
+    const path = nodePath.join(destination, "response-builder-factory.ts");
+
+    process.stdout.write(`writing ${path}\n`);
+
+    return fs.copyFile(
+      nodePath.join(__dirname, "../../templates/response-builder-factory.ts"),
+      path
+    );
+  }
+
   async writeFiles(destination) {
     await this.finished();
 
@@ -77,5 +91,7 @@ export class Repository {
     );
 
     await Promise.all(writeFiles);
+
+    await this.copyCoreFiles(destination);
   }
 }
diff --git a/src/typescript-generator/requirement.js b/src/typescript-generator/requirement.js
index 3962dfa..f21abb9 100644
--- a/src/typescript-generator/requirement.js
+++ b/src/typescript-generator/requirement.js
@@ -81,6 +81,20 @@ export class Requirement {
     return this.map(callback).flat();
   }
 
+  find(callback) {
+    // eslint-disable-next-line init-declarations
+    let result;
+
+    this.forEach((value, key) => {
+      // eslint-disable-next-line node/callback-return
+      if (result === undefined && callback(value, key)) {
+        result = value;
+      }
+    });
+
+    return result;
+  }
+
   escapeJsonPointer(string) {
     return string.replaceAll("~", "~0").replaceAll("/", "~1");
   }
diff --git a/src/typescript-generator/response-type-coder.js b/src/typescript-generator/response-type-coder.js
index 0f47292..d936757 100644
--- a/src/typescript-generator/response-type-coder.js
+++ b/src/typescript-generator/response-type-coder.js
@@ -5,6 +5,12 @@ import { SchemaTypeCoder } from "./schema-type-coder.js";
 import { printObject, printObjectWithoutQuotes } from "./printers.js";
 
 export class ResponseTypeCoder extends Coder {
+  constructor(requirement, openApi2MediaTypes = []) {
+    super(requirement);
+
+    this.openApi2MediaTypes = openApi2MediaTypes;
+  }
+
   typeForDefaultStatusCode(listedStatusCodes) {
     const definedStatusCodes = listedStatusCodes.filter(
       (key) => key !== "default"
@@ -28,24 +34,33 @@ export class ResponseTypeCoder extends Coder {
   }
 
   buildContentObjectType(script, response) {
-    if (!response.has("content")) {
-      return "{}";
+    if (response.has("content")) {
+      return response.get("content").map((content, mediaType) => [
+        mediaType,
+        `{ 
+            schema:  ${new SchemaTypeCoder(content.get("schema")).write(script)}
+         }`,
+      ]);
     }
 
-    return response.get("content").map((content, mediaType) => [
-      mediaType,
-      `{ 
-          schema:  ${new SchemaTypeCoder(content.get("schema")).write(script)}
-       }`,
-    ]);
+    if (response.has("schema")) {
+      return this.openApi2MediaTypes.map((mediaType) => [
+        mediaType,
+        `{
+            schema: ${new SchemaTypeCoder(response.get("schema")).write(script)}
+         }`,
+      ]);
+    }
+
+    return [];
   }
 
   printContentObjectType(script, response) {
-    if (!response.has("content")) {
-      return "{}";
+    if (response.has("content") || response.has("schema")) {
+      return printObject(this.buildContentObjectType(script, response));
     }
 
-    return printObject(this.buildContentObjectType(script, response));
+    return "{}";
   }
 
   buildHeaders(script, response) {
@@ -53,7 +68,9 @@ export class ResponseTypeCoder extends Coder {
       .get("headers")
       .map((value, name) => [
         name,
-        `{ schema: ${new SchemaTypeCoder(value.get("schema")).write(script)}}`,
+        `{ schema: ${new SchemaTypeCoder(value.get("schema") ?? value).write(
+          script
+        )}}`,
       ]);
   }
 


Test patch:
diff --git a/test/typescript-generator/__snapshots__/end-to-end.test.js.snap b/test/typescript-generator/__snapshots__/end-to-end.test.js.snap
index 921865b..14aec68 100644
--- a/test/typescript-generator/__snapshots__/end-to-end.test.js.snap
+++ b/test/typescript-generator/__snapshots__/end-to-end.test.js.snap
@@ -51,16 +51,16 @@ Map {
           },
           "exports": Map {
             "HTTP_POST" => Object {
-              "code": "({ query, path, header, body, context }: { query: undefined, path: undefined, header: undefined, body: Pet, context: typeof Context, response: ResponseBuilderFactory<{
+              "code": "({ query, path, header, body, context }: { query: never, path: never, header: never, body: Pet, context: typeof Context, response: ResponseBuilderFactory<{
 200: {
           headers: {};
           content: {
 \\"application/xml\\": { 
-          schema:  Pet
-       },
+            schema:  Pet
+         },
 \\"application/json\\": { 
-          schema:  Pet
-       }
+            schema:  Pet
+         }
 };
         },
 405: {
@@ -68,16 +68,16 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: 200, 
-            contentType?: \\"application/xml\\",
-            body?: Pet
-          } | {  
-            status: 200, 
-            contentType?: \\"application/json\\",
-            body?: Pet
-          } | {  
-            status: 405 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+              status: 200, 
+              contentType?: \\"application/xml\\",
+              body?: Pet
+            } | {  
+              status: 200, 
+              contentType?: \\"application/json\\",
+              body?: Pet
+            } | {  
+          status: 405 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
               "done": true,
               "id": "OperationTypeCoder@petstore.yaml#/paths/~1pet/post",
@@ -88,16 +88,16 @@ Map {
               "typeDeclaration": "",
             },
             "HTTP_PUT" => Object {
-              "code": "({ query, path, header, body, context }: { query: undefined, path: undefined, header: undefined, body: Pet, context: typeof Context2, response: ResponseBuilderFactory<{
+              "code": "({ query, path, header, body, context }: { query: never, path: never, header: never, body: Pet, context: typeof Context2, response: ResponseBuilderFactory<{
 200: {
           headers: {};
           content: {
 \\"application/xml\\": { 
-          schema:  Pet
-       },
+            schema:  Pet
+         },
 \\"application/json\\": { 
-          schema:  Pet
-       }
+            schema:  Pet
+         }
 };
         },
 400: {
@@ -113,20 +113,20 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: 200, 
-            contentType?: \\"application/xml\\",
-            body?: Pet
-          } | {  
-            status: 200, 
-            contentType?: \\"application/json\\",
-            body?: Pet
-          } | {  
-            status: 400 
-          } | {  
-            status: 404 
-          } | {  
-            status: 405 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+              status: 200, 
+              contentType?: \\"application/xml\\",
+              body?: Pet
+            } | {  
+              status: 200, 
+              contentType?: \\"application/json\\",
+              body?: Pet
+            } | {  
+          status: 400 
+        } | {  
+          status: 404 
+        } | {  
+          status: 405 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
               "done": true,
               "id": "OperationTypeCoder@petstore.yaml#/paths/~1pet/put",
@@ -324,16 +324,16 @@ Map {
           },
           "exports": Map {
             "HTTP_POST" => Object {
-              "code": "({ query, path, header, body, context }: { query: undefined, path: undefined, header: undefined, body: Pet, context: typeof Context, response: ResponseBuilderFactory<{
+              "code": "({ query, path, header, body, context }: { query: never, path: never, header: never, body: Pet, context: typeof Context, response: ResponseBuilderFactory<{
 200: {
           headers: {};
           content: {
 \\"application/xml\\": { 
-          schema:  Pet
-       },
+            schema:  Pet
+         },
 \\"application/json\\": { 
-          schema:  Pet
-       }
+            schema:  Pet
+         }
 };
         },
 405: {
@@ -341,16 +341,16 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: 200, 
-            contentType?: \\"application/xml\\",
-            body?: Pet
-          } | {  
-            status: 200, 
-            contentType?: \\"application/json\\",
-            body?: Pet
-          } | {  
-            status: 405 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+              status: 200, 
+              contentType?: \\"application/xml\\",
+              body?: Pet
+            } | {  
+              status: 200, 
+              contentType?: \\"application/json\\",
+              body?: Pet
+            } | {  
+          status: 405 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
               "done": true,
               "id": "OperationTypeCoder@petstore.yaml#/paths/~1pet/post",
@@ -361,16 +361,16 @@ Map {
               "typeDeclaration": "",
             },
             "HTTP_PUT" => Object {
-              "code": "({ query, path, header, body, context }: { query: undefined, path: undefined, header: undefined, body: Pet, context: typeof Context2, response: ResponseBuilderFactory<{
+              "code": "({ query, path, header, body, context }: { query: never, path: never, header: never, body: Pet, context: typeof Context2, response: ResponseBuilderFactory<{
 200: {
           headers: {};
           content: {
 \\"application/xml\\": { 
-          schema:  Pet
-       },
+            schema:  Pet
+         },
 \\"application/json\\": { 
-          schema:  Pet
-       }
+            schema:  Pet
+         }
 };
         },
 400: {
@@ -386,20 +386,20 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: 200, 
-            contentType?: \\"application/xml\\",
-            body?: Pet
-          } | {  
-            status: 200, 
-            contentType?: \\"application/json\\",
-            body?: Pet
-          } | {  
-            status: 400 
-          } | {  
-            status: 404 
-          } | {  
-            status: 405 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+              status: 200, 
+              contentType?: \\"application/xml\\",
+              body?: Pet
+            } | {  
+              status: 200, 
+              contentType?: \\"application/json\\",
+              body?: Pet
+            } | {  
+          status: 400 
+        } | {  
+          status: 404 
+        } | {  
+          status: 405 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
               "done": true,
               "id": "OperationTypeCoder@petstore.yaml#/paths/~1pet/put",
@@ -601,16 +601,16 @@ Map {
     },
     "exports": Map {
       "HTTP_POST" => Object {
-        "code": "({ query, path, header, body, context }: { query: undefined, path: undefined, header: undefined, body: Pet, context: typeof Context, response: ResponseBuilderFactory<{
+        "code": "({ query, path, header, body, context }: { query: never, path: never, header: never, body: Pet, context: typeof Context, response: ResponseBuilderFactory<{
 200: {
           headers: {};
           content: {
 \\"application/xml\\": { 
-          schema:  Pet
-       },
+            schema:  Pet
+         },
 \\"application/json\\": { 
-          schema:  Pet
-       }
+            schema:  Pet
+         }
 };
         },
 405: {
@@ -618,16 +618,16 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: 200, 
-            contentType?: \\"application/xml\\",
-            body?: Pet
-          } | {  
-            status: 200, 
-            contentType?: \\"application/json\\",
-            body?: Pet
-          } | {  
-            status: 405 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+              status: 200, 
+              contentType?: \\"application/xml\\",
+              body?: Pet
+            } | {  
+              status: 200, 
+              contentType?: \\"application/json\\",
+              body?: Pet
+            } | {  
+          status: 405 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
         "done": true,
         "id": "OperationTypeCoder@petstore.yaml#/paths/~1pet/post",
@@ -638,16 +638,16 @@ Map {
         "typeDeclaration": "",
       },
       "HTTP_PUT" => Object {
-        "code": "({ query, path, header, body, context }: { query: undefined, path: undefined, header: undefined, body: Pet, context: typeof Context2, response: ResponseBuilderFactory<{
+        "code": "({ query, path, header, body, context }: { query: never, path: never, header: never, body: Pet, context: typeof Context2, response: ResponseBuilderFactory<{
 200: {
           headers: {};
           content: {
 \\"application/xml\\": { 
-          schema:  Pet
-       },
+            schema:  Pet
+         },
 \\"application/json\\": { 
-          schema:  Pet
-       }
+            schema:  Pet
+         }
 };
         },
 400: {
@@ -663,20 +663,20 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: 200, 
-            contentType?: \\"application/xml\\",
-            body?: Pet
-          } | {  
-            status: 200, 
-            contentType?: \\"application/json\\",
-            body?: Pet
-          } | {  
-            status: 400 
-          } | {  
-            status: 404 
-          } | {  
-            status: 405 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+              status: 200, 
+              contentType?: \\"application/xml\\",
+              body?: Pet
+            } | {  
+              status: 200, 
+              contentType?: \\"application/json\\",
+              body?: Pet
+            } | {  
+          status: 400 
+        } | {  
+          status: 404 
+        } | {  
+          status: 405 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
         "done": true,
         "id": "OperationTypeCoder@petstore.yaml#/paths/~1pet/put",
@@ -897,11 +897,11 @@ Map {
           headers: {};
           content: {
 \\"application/xml\\": { 
-          schema:  Array<Pet>
-       },
+            schema:  Array<Pet>
+         },
 \\"application/json\\": { 
-          schema:  Array<Pet>
-       }
+            schema:  Array<Pet>
+         }
 };
         },
 400: {
@@ -909,16 +909,16 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: 200, 
-            contentType?: \\"application/xml\\",
-            body?: Array<Pet>
-          } | {  
-            status: 200, 
-            contentType?: \\"application/json\\",
-            body?: Array<Pet>
-          } | {  
-            status: 400 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+              status: 200, 
+              contentType?: \\"application/xml\\",
+              body?: Array<Pet>
+            } | {  
+              status: 200, 
+              contentType?: \\"application/json\\",
+              body?: Array<Pet>
+            } | {  
+          status: 400 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
               "done": true,
               "id": "OperationTypeCoder@petstore.yaml#/paths/~1pet~1findByStatus/get",
@@ -1095,11 +1095,11 @@ Map {
           headers: {};
           content: {
 \\"application/xml\\": { 
-          schema:  Array<Pet>
-       },
+            schema:  Array<Pet>
+         },
 \\"application/json\\": { 
-          schema:  Array<Pet>
-       }
+            schema:  Array<Pet>
+         }
 };
         },
 400: {
@@ -1107,16 +1107,16 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: 200, 
-            contentType?: \\"application/xml\\",
-            body?: Array<Pet>
-          } | {  
-            status: 200, 
-            contentType?: \\"application/json\\",
-            body?: Array<Pet>
-          } | {  
-            status: 400 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+              status: 200, 
+              contentType?: \\"application/xml\\",
+              body?: Array<Pet>
+            } | {  
+              status: 200, 
+              contentType?: \\"application/json\\",
+              body?: Array<Pet>
+            } | {  
+          status: 400 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
         "done": true,
         "id": "OperationTypeCoder@petstore.yaml#/paths/~1pet~1findByStatus/get",
@@ -1309,11 +1309,11 @@ Map {
           headers: {};
           content: {
 \\"application/xml\\": { 
-          schema:  Array<Pet>
-       },
+            schema:  Array<Pet>
+         },
 \\"application/json\\": { 
-          schema:  Array<Pet>
-       }
+            schema:  Array<Pet>
+         }
 };
         },
 400: {
@@ -1321,16 +1321,16 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: 200, 
-            contentType?: \\"application/xml\\",
-            body?: Array<Pet>
-          } | {  
-            status: 200, 
-            contentType?: \\"application/json\\",
-            body?: Array<Pet>
-          } | {  
-            status: 400 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+              status: 200, 
+              contentType?: \\"application/xml\\",
+              body?: Array<Pet>
+            } | {  
+              status: 200, 
+              contentType?: \\"application/json\\",
+              body?: Array<Pet>
+            } | {  
+          status: 400 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
               "done": true,
               "id": "OperationTypeCoder@petstore.yaml#/paths/~1pet~1findByTags/get",
@@ -1507,11 +1507,11 @@ Map {
           headers: {};
           content: {
 \\"application/xml\\": { 
-          schema:  Array<Pet>
-       },
+            schema:  Array<Pet>
+         },
 \\"application/json\\": { 
-          schema:  Array<Pet>
-       }
+            schema:  Array<Pet>
+         }
 };
         },
 400: {
@@ -1519,16 +1519,16 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: 200, 
-            contentType?: \\"application/xml\\",
-            body?: Array<Pet>
-          } | {  
-            status: 200, 
-            contentType?: \\"application/json\\",
-            body?: Array<Pet>
-          } | {  
-            status: 400 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+              status: 200, 
+              contentType?: \\"application/xml\\",
+              body?: Array<Pet>
+            } | {  
+              status: 200, 
+              contentType?: \\"application/json\\",
+              body?: Array<Pet>
+            } | {  
+          status: 400 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
         "done": true,
         "id": "OperationTypeCoder@petstore.yaml#/paths/~1pet~1findByTags/get",
@@ -1749,11 +1749,11 @@ Map {
           headers: {};
           content: {
 \\"application/xml\\": { 
-          schema:  Pet
-       },
+            schema:  Pet
+         },
 \\"application/json\\": { 
-          schema:  Pet
-       }
+            schema:  Pet
+         }
 };
         },
 400: {
@@ -1765,18 +1765,18 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: 200, 
-            contentType?: \\"application/xml\\",
-            body?: Pet
-          } | {  
-            status: 200, 
-            contentType?: \\"application/json\\",
-            body?: Pet
-          } | {  
-            status: 400 
-          } | {  
-            status: 404 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+              status: 200, 
+              contentType?: \\"application/xml\\",
+              body?: Pet
+            } | {  
+              status: 200, 
+              contentType?: \\"application/json\\",
+              body?: Pet
+            } | {  
+          status: 400 
+        } | {  
+          status: 404 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
               "done": true,
               "id": "OperationTypeCoder@petstore.yaml#/paths/~1pet~1{petId}/get",
@@ -1793,8 +1793,8 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: 405 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+          status: 405 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
               "done": true,
               "id": "OperationTypeCoder@petstore.yaml#/paths/~1pet~1{petId}/post",
@@ -1811,8 +1811,8 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: 400 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+          status: 400 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
               "done": true,
               "id": "OperationTypeCoder@petstore.yaml#/paths/~1pet~1{petId}/delete",
@@ -2053,11 +2053,11 @@ Map {
           headers: {};
           content: {
 \\"application/xml\\": { 
-          schema:  Pet
-       },
+            schema:  Pet
+         },
 \\"application/json\\": { 
-          schema:  Pet
-       }
+            schema:  Pet
+         }
 };
         },
 400: {
@@ -2069,18 +2069,18 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: 200, 
-            contentType?: \\"application/xml\\",
-            body?: Pet
-          } | {  
-            status: 200, 
-            contentType?: \\"application/json\\",
-            body?: Pet
-          } | {  
-            status: 400 
-          } | {  
-            status: 404 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+              status: 200, 
+              contentType?: \\"application/xml\\",
+              body?: Pet
+            } | {  
+              status: 200, 
+              contentType?: \\"application/json\\",
+              body?: Pet
+            } | {  
+          status: 400 
+        } | {  
+          status: 404 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
               "done": true,
               "id": "OperationTypeCoder@petstore.yaml#/paths/~1pet~1{petId}/get",
@@ -2097,8 +2097,8 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: 405 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+          status: 405 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
               "done": true,
               "id": "OperationTypeCoder@petstore.yaml#/paths/~1pet~1{petId}/post",
@@ -2115,8 +2115,8 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: 400 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+          status: 400 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
               "done": true,
               "id": "OperationTypeCoder@petstore.yaml#/paths/~1pet~1{petId}/delete",
@@ -2357,11 +2357,11 @@ Map {
           headers: {};
           content: {
 \\"application/xml\\": { 
-          schema:  Pet
-       },
+            schema:  Pet
+         },
 \\"application/json\\": { 
-          schema:  Pet
-       }
+            schema:  Pet
+         }
 };
         },
 400: {
@@ -2373,18 +2373,18 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: 200, 
-            contentType?: \\"application/xml\\",
-            body?: Pet
-          } | {  
-            status: 200, 
-            contentType?: \\"application/json\\",
-            body?: Pet
-          } | {  
-            status: 400 
-          } | {  
-            status: 404 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+              status: 200, 
+              contentType?: \\"application/xml\\",
+              body?: Pet
+            } | {  
+              status: 200, 
+              contentType?: \\"application/json\\",
+              body?: Pet
+            } | {  
+          status: 400 
+        } | {  
+          status: 404 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
               "done": true,
               "id": "OperationTypeCoder@petstore.yaml#/paths/~1pet~1{petId}/get",
@@ -2401,8 +2401,8 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: 405 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+          status: 405 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
               "done": true,
               "id": "OperationTypeCoder@petstore.yaml#/paths/~1pet~1{petId}/post",
@@ -2419,8 +2419,8 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: 400 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+          status: 400 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
               "done": true,
               "id": "OperationTypeCoder@petstore.yaml#/paths/~1pet~1{petId}/delete",
@@ -2665,11 +2665,11 @@ Map {
           headers: {};
           content: {
 \\"application/xml\\": { 
-          schema:  Pet
-       },
+            schema:  Pet
+         },
 \\"application/json\\": { 
-          schema:  Pet
-       }
+            schema:  Pet
+         }
 };
         },
 400: {
@@ -2681,18 +2681,18 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: 200, 
-            contentType?: \\"application/xml\\",
-            body?: Pet
-          } | {  
-            status: 200, 
-            contentType?: \\"application/json\\",
-            body?: Pet
-          } | {  
-            status: 400 
-          } | {  
-            status: 404 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+              status: 200, 
+              contentType?: \\"application/xml\\",
+              body?: Pet
+            } | {  
+              status: 200, 
+              contentType?: \\"application/json\\",
+              body?: Pet
+            } | {  
+          status: 400 
+        } | {  
+          status: 404 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
         "done": true,
         "id": "OperationTypeCoder@petstore.yaml#/paths/~1pet~1{petId}/get",
@@ -2709,8 +2709,8 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: 405 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+          status: 405 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
         "done": true,
         "id": "OperationTypeCoder@petstore.yaml#/paths/~1pet~1{petId}/post",
@@ -2727,8 +2727,8 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: 400 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+          status: 400 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
         "done": true,
         "id": "OperationTypeCoder@petstore.yaml#/paths/~1pet~1{petId}/delete",
@@ -2985,15 +2985,15 @@ Map {
           headers: {};
           content: {
 \\"application/json\\": { 
-          schema:  ApiResponse
-       }
+            schema:  ApiResponse
+         }
 };
         }
 }> }) => {  
-            status: 200, 
-            contentType?: \\"application/json\\",
-            body?: ApiResponse
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+              status: 200, 
+              contentType?: \\"application/json\\",
+              body?: ApiResponse
+            } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
               "done": true,
               "id": "OperationTypeCoder@petstore.yaml#/paths/~1pet~1{petId}~1uploadImage/post",
@@ -3107,15 +3107,15 @@ Map {
           headers: {};
           content: {
 \\"application/json\\": { 
-          schema:  ApiResponse
-       }
+            schema:  ApiResponse
+         }
 };
         }
 }> }) => {  
-            status: 200, 
-            contentType?: \\"application/json\\",
-            body?: ApiResponse
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+              status: 200, 
+              contentType?: \\"application/json\\",
+              body?: ApiResponse
+            } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
         "done": true,
         "id": "OperationTypeCoder@petstore.yaml#/paths/~1pet~1{petId}~1uploadImage/post",
@@ -3239,20 +3239,20 @@ Map {
           },
           "exports": Map {
             "HTTP_GET" => Object {
-              "code": "({ query, path, header, body, context }: { query: undefined, path: undefined, header: undefined, body: undefined, context: typeof Context, response: ResponseBuilderFactory<{
+              "code": "({ query, path, header, body, context }: { query: never, path: never, header: never, body: undefined, context: typeof Context, response: ResponseBuilderFactory<{
 200: {
           headers: {};
           content: {
 \\"application/json\\": { 
-          schema:  {[key: string]: number}
-       }
+            schema:  {[key: string]: number}
+         }
 };
         }
 }> }) => {  
-            status: 200, 
-            contentType?: \\"application/json\\",
-            body?: {[key: string]: number}
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+              status: 200, 
+              contentType?: \\"application/json\\",
+              body?: {[key: string]: number}
+            } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
               "done": true,
               "id": "OperationTypeCoder@petstore.yaml#/paths/~1store~1inventory/get",
@@ -3330,20 +3330,20 @@ Map {
     },
     "exports": Map {
       "HTTP_GET" => Object {
-        "code": "({ query, path, header, body, context }: { query: undefined, path: undefined, header: undefined, body: undefined, context: typeof Context, response: ResponseBuilderFactory<{
+        "code": "({ query, path, header, body, context }: { query: never, path: never, header: never, body: undefined, context: typeof Context, response: ResponseBuilderFactory<{
 200: {
           headers: {};
           content: {
 \\"application/json\\": { 
-          schema:  {[key: string]: number}
-       }
+            schema:  {[key: string]: number}
+         }
 };
         }
 }> }) => {  
-            status: 200, 
-            contentType?: \\"application/json\\",
-            body?: {[key: string]: number}
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+              status: 200, 
+              contentType?: \\"application/json\\",
+              body?: {[key: string]: number}
+            } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
         "done": true,
         "id": "OperationTypeCoder@petstore.yaml#/paths/~1store~1inventory/get",
@@ -3438,13 +3438,13 @@ Map {
           },
           "exports": Map {
             "HTTP_POST" => Object {
-              "code": "({ query, path, header, body, context }: { query: undefined, path: undefined, header: undefined, body: Order, context: typeof Context, response: ResponseBuilderFactory<{
+              "code": "({ query, path, header, body, context }: { query: never, path: never, header: never, body: Order, context: typeof Context, response: ResponseBuilderFactory<{
 200: {
           headers: {};
           content: {
 \\"application/json\\": { 
-          schema:  Order
-       }
+            schema:  Order
+         }
 };
         },
 405: {
@@ -3452,12 +3452,12 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: 200, 
-            contentType?: \\"application/json\\",
-            body?: Order
-          } | {  
-            status: 405 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+              status: 200, 
+              contentType?: \\"application/json\\",
+              body?: Order
+            } | {  
+          status: 405 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
               "done": true,
               "id": "OperationTypeCoder@petstore.yaml#/paths/~1store~1order/post",
@@ -3566,13 +3566,13 @@ Map {
     },
     "exports": Map {
       "HTTP_POST" => Object {
-        "code": "({ query, path, header, body, context }: { query: undefined, path: undefined, header: undefined, body: Order, context: typeof Context, response: ResponseBuilderFactory<{
+        "code": "({ query, path, header, body, context }: { query: never, path: never, header: never, body: Order, context: typeof Context, response: ResponseBuilderFactory<{
 200: {
           headers: {};
           content: {
 \\"application/json\\": { 
-          schema:  Order
-       }
+            schema:  Order
+         }
 };
         },
 405: {
@@ -3580,12 +3580,12 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: 200, 
-            contentType?: \\"application/json\\",
-            body?: Order
-          } | {  
-            status: 405 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+              status: 200, 
+              contentType?: \\"application/json\\",
+              body?: Order
+            } | {  
+          status: 405 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
         "done": true,
         "id": "OperationTypeCoder@petstore.yaml#/paths/~1store~1order/post",
@@ -3729,11 +3729,11 @@ Map {
           headers: {};
           content: {
 \\"application/xml\\": { 
-          schema:  Order
-       },
+            schema:  Order
+         },
 \\"application/json\\": { 
-          schema:  Order
-       }
+            schema:  Order
+         }
 };
         },
 400: {
@@ -3745,18 +3745,18 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: 200, 
-            contentType?: \\"application/xml\\",
-            body?: Order
-          } | {  
-            status: 200, 
-            contentType?: \\"application/json\\",
-            body?: Order
-          } | {  
-            status: 400 
-          } | {  
-            status: 404 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+              status: 200, 
+              contentType?: \\"application/xml\\",
+              body?: Order
+            } | {  
+              status: 200, 
+              contentType?: \\"application/json\\",
+              body?: Order
+            } | {  
+          status: 400 
+        } | {  
+          status: 404 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
               "done": true,
               "id": "OperationTypeCoder@petstore.yaml#/paths/~1store~1order~1{orderId}/get",
@@ -3777,10 +3777,10 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: 400 
-          } | {  
-            status: 404 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+          status: 400 
+        } | {  
+          status: 404 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
               "done": true,
               "id": "OperationTypeCoder@petstore.yaml#/paths/~1store~1order~1{orderId}/delete",
@@ -3924,11 +3924,11 @@ Map {
           headers: {};
           content: {
 \\"application/xml\\": { 
-          schema:  Order
-       },
+            schema:  Order
+         },
 \\"application/json\\": { 
-          schema:  Order
-       }
+            schema:  Order
+         }
 };
         },
 400: {
@@ -3940,18 +3940,18 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: 200, 
-            contentType?: \\"application/xml\\",
-            body?: Order
-          } | {  
-            status: 200, 
-            contentType?: \\"application/json\\",
-            body?: Order
-          } | {  
-            status: 400 
-          } | {  
-            status: 404 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+              status: 200, 
+              contentType?: \\"application/xml\\",
+              body?: Order
+            } | {  
+              status: 200, 
+              contentType?: \\"application/json\\",
+              body?: Order
+            } | {  
+          status: 400 
+        } | {  
+          status: 404 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
               "done": true,
               "id": "OperationTypeCoder@petstore.yaml#/paths/~1store~1order~1{orderId}/get",
@@ -3972,10 +3972,10 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: 400 
-          } | {  
-            status: 404 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+          status: 400 
+        } | {  
+          status: 404 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
               "done": true,
               "id": "OperationTypeCoder@petstore.yaml#/paths/~1store~1order~1{orderId}/delete",
@@ -4123,11 +4123,11 @@ Map {
           headers: {};
           content: {
 \\"application/xml\\": { 
-          schema:  Order
-       },
+            schema:  Order
+         },
 \\"application/json\\": { 
-          schema:  Order
-       }
+            schema:  Order
+         }
 };
         },
 400: {
@@ -4139,18 +4139,18 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: 200, 
-            contentType?: \\"application/xml\\",
-            body?: Order
-          } | {  
-            status: 200, 
-            contentType?: \\"application/json\\",
-            body?: Order
-          } | {  
-            status: 400 
-          } | {  
-            status: 404 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+              status: 200, 
+              contentType?: \\"application/xml\\",
+              body?: Order
+            } | {  
+              status: 200, 
+              contentType?: \\"application/json\\",
+              body?: Order
+            } | {  
+          status: 400 
+        } | {  
+          status: 404 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
         "done": true,
         "id": "OperationTypeCoder@petstore.yaml#/paths/~1store~1order~1{orderId}/get",
@@ -4171,10 +4171,10 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: 400 
-          } | {  
-            status: 404 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+          status: 400 
+        } | {  
+          status: 404 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
         "done": true,
         "id": "OperationTypeCoder@petstore.yaml#/paths/~1store~1order~1{orderId}/delete",
@@ -4331,27 +4331,27 @@ Map {
           },
           "exports": Map {
             "HTTP_POST" => Object {
-              "code": "({ query, path, header, body, context }: { query: undefined, path: undefined, header: undefined, body: User, context: typeof Context, response: ResponseBuilderFactory<{
+              "code": "({ query, path, header, body, context }: { query: never, path: never, header: never, body: User, context: typeof Context, response: ResponseBuilderFactory<{
 [statusCode in HttpStatusCode]: {
           headers: {};
           content: {
 \\"application/json\\": { 
-          schema:  User
-       },
+            schema:  User
+         },
 \\"application/xml\\": { 
-          schema:  User
-       }
+            schema:  User
+         }
 };
         }
 }> }) => {  
-            status: number | undefined, 
-            contentType?: \\"application/json\\",
-            body?: User
-          } | {  
-            status: number | undefined, 
-            contentType?: \\"application/xml\\",
-            body?: User
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+              status: number | undefined, 
+              contentType?: \\"application/json\\",
+              body?: User
+            } | {  
+              status: number | undefined, 
+              contentType?: \\"application/xml\\",
+              body?: User
+            } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
               "done": true,
               "id": "OperationTypeCoder@petstore.yaml#/paths/~1user/post",
@@ -4458,27 +4458,27 @@ Map {
     },
     "exports": Map {
       "HTTP_POST" => Object {
-        "code": "({ query, path, header, body, context }: { query: undefined, path: undefined, header: undefined, body: User, context: typeof Context, response: ResponseBuilderFactory<{
+        "code": "({ query, path, header, body, context }: { query: never, path: never, header: never, body: User, context: typeof Context, response: ResponseBuilderFactory<{
 [statusCode in HttpStatusCode]: {
           headers: {};
           content: {
 \\"application/json\\": { 
-          schema:  User
-       },
+            schema:  User
+         },
 \\"application/xml\\": { 
-          schema:  User
-       }
+            schema:  User
+         }
 };
         }
 }> }) => {  
-            status: number | undefined, 
-            contentType?: \\"application/json\\",
-            body?: User
-          } | {  
-            status: number | undefined, 
-            contentType?: \\"application/xml\\",
-            body?: User
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+              status: number | undefined, 
+              contentType?: \\"application/json\\",
+              body?: User
+            } | {  
+              status: number | undefined, 
+              contentType?: \\"application/xml\\",
+              body?: User
+            } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
         "done": true,
         "id": "OperationTypeCoder@petstore.yaml#/paths/~1user/post",
@@ -4601,16 +4601,16 @@ Map {
           },
           "exports": Map {
             "HTTP_POST" => Object {
-              "code": "({ query, path, header, body, context }: { query: undefined, path: undefined, header: undefined, body: Array<User>, context: typeof Context, response: ResponseBuilderFactory<{
+              "code": "({ query, path, header, body, context }: { query: never, path: never, header: never, body: Array<User>, context: typeof Context, response: ResponseBuilderFactory<{
 200: {
           headers: {};
           content: {
 \\"application/xml\\": { 
-          schema:  User
-       },
+            schema:  User
+         },
 \\"application/json\\": { 
-          schema:  User
-       }
+            schema:  User
+         }
 };
         },
 [statusCode in Exclude<HttpStatusCode, 200>]: {
@@ -4618,16 +4618,16 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: 200, 
-            contentType?: \\"application/xml\\",
-            body?: User
-          } | {  
-            status: 200, 
-            contentType?: \\"application/json\\",
-            body?: User
-          } | {  
-            status: number | undefined 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+              status: 200, 
+              contentType?: \\"application/xml\\",
+              body?: User
+            } | {  
+              status: 200, 
+              contentType?: \\"application/json\\",
+              body?: User
+            } | {  
+          status: number | undefined 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
               "done": true,
               "id": "OperationTypeCoder@petstore.yaml#/paths/~1user~1createWithList/post",
@@ -4736,16 +4736,16 @@ Map {
     },
     "exports": Map {
       "HTTP_POST" => Object {
-        "code": "({ query, path, header, body, context }: { query: undefined, path: undefined, header: undefined, body: Array<User>, context: typeof Context, response: ResponseBuilderFactory<{
+        "code": "({ query, path, header, body, context }: { query: never, path: never, header: never, body: Array<User>, context: typeof Context, response: ResponseBuilderFactory<{
 200: {
           headers: {};
           content: {
 \\"application/xml\\": { 
-          schema:  User
-       },
+            schema:  User
+         },
 \\"application/json\\": { 
-          schema:  User
-       }
+            schema:  User
+         }
 };
         },
 [statusCode in Exclude<HttpStatusCode, 200>]: {
@@ -4753,16 +4753,16 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: 200, 
-            contentType?: \\"application/xml\\",
-            body?: User
-          } | {  
-            status: 200, 
-            contentType?: \\"application/json\\",
-            body?: User
-          } | {  
-            status: number | undefined 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+              status: 200, 
+              contentType?: \\"application/xml\\",
+              body?: User
+            } | {  
+              status: 200, 
+              contentType?: \\"application/json\\",
+              body?: User
+            } | {  
+          status: number | undefined 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
         "done": true,
         "id": "OperationTypeCoder@petstore.yaml#/paths/~1user~1createWithList/post",
@@ -4894,11 +4894,11 @@ Map {
 };
           content: {
 \\"application/xml\\": { 
-          schema:  string
-       },
+            schema:  string
+         },
 \\"application/json\\": { 
-          schema:  string
-       }
+            schema:  string
+         }
 };
         },
 400: {
@@ -4906,16 +4906,16 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: 200, 
-            contentType?: \\"application/xml\\",
-            body?: string
-          } | {  
-            status: 200, 
-            contentType?: \\"application/json\\",
-            body?: string
-          } | {  
-            status: 400 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+              status: 200, 
+              contentType?: \\"application/xml\\",
+              body?: string
+            } | {  
+              status: 200, 
+              contentType?: \\"application/json\\",
+              body?: string
+            } | {  
+          status: 400 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
               "done": true,
               "id": "OperationTypeCoder@petstore.yaml#/paths/~1user~1login/get",
@@ -5001,11 +5001,11 @@ Map {
 };
           content: {
 \\"application/xml\\": { 
-          schema:  string
-       },
+            schema:  string
+         },
 \\"application/json\\": { 
-          schema:  string
-       }
+            schema:  string
+         }
 };
         },
 400: {
@@ -5013,16 +5013,16 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: 200, 
-            contentType?: \\"application/xml\\",
-            body?: string
-          } | {  
-            status: 200, 
-            contentType?: \\"application/json\\",
-            body?: string
-          } | {  
-            status: 400 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+              status: 200, 
+              contentType?: \\"application/xml\\",
+              body?: string
+            } | {  
+              status: 200, 
+              contentType?: \\"application/json\\",
+              body?: string
+            } | {  
+          status: 400 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
         "done": true,
         "id": "OperationTypeCoder@petstore.yaml#/paths/~1user~1login/get",
@@ -5120,8 +5120,8 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: number | undefined 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+          status: number | undefined 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
               "done": true,
               "id": "OperationTypeCoder@petstore.yaml#/paths/~1user~1logout/get",
@@ -5205,8 +5205,8 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: number | undefined 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+          status: number | undefined 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
         "done": true,
         "id": "OperationTypeCoder@petstore.yaml#/paths/~1user~1logout/get",
@@ -5334,11 +5334,11 @@ Map {
           headers: {};
           content: {
 \\"application/xml\\": { 
-          schema:  User
-       },
+            schema:  User
+         },
 \\"application/json\\": { 
-          schema:  User
-       }
+            schema:  User
+         }
 };
         },
 400: {
@@ -5350,18 +5350,18 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: 200, 
-            contentType?: \\"application/xml\\",
-            body?: User
-          } | {  
-            status: 200, 
-            contentType?: \\"application/json\\",
-            body?: User
-          } | {  
-            status: 400 
-          } | {  
-            status: 404 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+              status: 200, 
+              contentType?: \\"application/xml\\",
+              body?: User
+            } | {  
+              status: 200, 
+              contentType?: \\"application/json\\",
+              body?: User
+            } | {  
+          status: 400 
+        } | {  
+          status: 404 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
               "done": true,
               "id": "OperationTypeCoder@petstore.yaml#/paths/~1user~1{username}/get",
@@ -5378,8 +5378,8 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: number | undefined 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+          status: number | undefined 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
               "done": true,
               "id": "OperationTypeCoder@petstore.yaml#/paths/~1user~1{username}/put",
@@ -5400,10 +5400,10 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: 400 
-          } | {  
-            status: 404 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+          status: 400 
+        } | {  
+          status: 404 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
               "done": true,
               "id": "OperationTypeCoder@petstore.yaml#/paths/~1user~1{username}/delete",
@@ -5581,11 +5581,11 @@ Map {
           headers: {};
           content: {
 \\"application/xml\\": { 
-          schema:  User
-       },
+            schema:  User
+         },
 \\"application/json\\": { 
-          schema:  User
-       }
+            schema:  User
+         }
 };
         },
 400: {
@@ -5597,18 +5597,18 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: 200, 
-            contentType?: \\"application/xml\\",
-            body?: User
-          } | {  
-            status: 200, 
-            contentType?: \\"application/json\\",
-            body?: User
-          } | {  
-            status: 400 
-          } | {  
-            status: 404 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+              status: 200, 
+              contentType?: \\"application/xml\\",
+              body?: User
+            } | {  
+              status: 200, 
+              contentType?: \\"application/json\\",
+              body?: User
+            } | {  
+          status: 400 
+        } | {  
+          status: 404 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
               "done": true,
               "id": "OperationTypeCoder@petstore.yaml#/paths/~1user~1{username}/get",
@@ -5625,8 +5625,8 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: number | undefined 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+          status: number | undefined 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
               "done": true,
               "id": "OperationTypeCoder@petstore.yaml#/paths/~1user~1{username}/put",
@@ -5647,10 +5647,10 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: 400 
-          } | {  
-            status: 404 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+          status: 400 
+        } | {  
+          status: 404 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
               "done": true,
               "id": "OperationTypeCoder@petstore.yaml#/paths/~1user~1{username}/delete",
@@ -5828,11 +5828,11 @@ Map {
           headers: {};
           content: {
 \\"application/xml\\": { 
-          schema:  User
-       },
+            schema:  User
+         },
 \\"application/json\\": { 
-          schema:  User
-       }
+            schema:  User
+         }
 };
         },
 400: {
@@ -5844,18 +5844,18 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: 200, 
-            contentType?: \\"application/xml\\",
-            body?: User
-          } | {  
-            status: 200, 
-            contentType?: \\"application/json\\",
-            body?: User
-          } | {  
-            status: 400 
-          } | {  
-            status: 404 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+              status: 200, 
+              contentType?: \\"application/xml\\",
+              body?: User
+            } | {  
+              status: 200, 
+              contentType?: \\"application/json\\",
+              body?: User
+            } | {  
+          status: 400 
+        } | {  
+          status: 404 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
               "done": true,
               "id": "OperationTypeCoder@petstore.yaml#/paths/~1user~1{username}/get",
@@ -5872,8 +5872,8 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: number | undefined 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+          status: number | undefined 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
               "done": true,
               "id": "OperationTypeCoder@petstore.yaml#/paths/~1user~1{username}/put",
@@ -5894,10 +5894,10 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: 400 
-          } | {  
-            status: 404 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+          status: 400 
+        } | {  
+          status: 404 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
               "done": true,
               "id": "OperationTypeCoder@petstore.yaml#/paths/~1user~1{username}/delete",
@@ -6079,11 +6079,11 @@ Map {
           headers: {};
           content: {
 \\"application/xml\\": { 
-          schema:  User
-       },
+            schema:  User
+         },
 \\"application/json\\": { 
-          schema:  User
-       }
+            schema:  User
+         }
 };
         },
 400: {
@@ -6095,18 +6095,18 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: 200, 
-            contentType?: \\"application/xml\\",
-            body?: User
-          } | {  
-            status: 200, 
-            contentType?: \\"application/json\\",
-            body?: User
-          } | {  
-            status: 400 
-          } | {  
-            status: 404 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+              status: 200, 
+              contentType?: \\"application/xml\\",
+              body?: User
+            } | {  
+              status: 200, 
+              contentType?: \\"application/json\\",
+              body?: User
+            } | {  
+          status: 400 
+        } | {  
+          status: 404 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
         "done": true,
         "id": "OperationTypeCoder@petstore.yaml#/paths/~1user~1{username}/get",
@@ -6123,8 +6123,8 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: number | undefined 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+          status: number | undefined 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
         "done": true,
         "id": "OperationTypeCoder@petstore.yaml#/paths/~1user~1{username}/put",
@@ -6145,10 +6145,10 @@ Map {
           content: {};
         }
 }> }) => {  
-            status: 400 
-          } | {  
-            status: 404 
-          } | { status: 415, contentType: \\"text/plain\\", body: string }
+          status: 400 
+        } | {  
+          status: 404 
+        } | { status: 415, contentType: \\"text/plain\\", body: string }
     | void",
         "done": true,
         "id": "OperationTypeCoder@petstore.yaml#/paths/~1user~1{username}/delete",
diff --git a/test/typescript-generator/__snapshots__/operation-type-coder.test.js.snap b/test/typescript-generator/__snapshots__/operation-type-coder.test.js.snap
index 721a66e..49f4287 100644
--- a/test/typescript-generator/__snapshots__/operation-type-coder.test.js.snap
+++ b/test/typescript-generator/__snapshots__/operation-type-coder.test.js.snap
@@ -68,9 +68,9 @@ exports[`an OperationTypeCoder generates a simple get operation 1`] = `
   body,
   context,
 }: {
-  query: undefined;
-  path: undefined;
-  header: undefined;
+  query: never;
+  path: never;
+  header: never;
   body: undefined;
   context: typeof default;
   response: ResponseBuilderFactory<{
diff --git a/test/typescript-generator/__snapshots__/parameters-type-coder.test.js.snap b/test/typescript-generator/__snapshots__/parameters-type-coder.test.js.snap
index 7c69cfd..44fbf98 100644
--- a/test/typescript-generator/__snapshots__/parameters-type-coder.test.js.snap
+++ b/test/typescript-generator/__snapshots__/parameters-type-coder.test.js.snap
@@ -5,6 +5,21 @@ exports[`a ParametersTypeCoder generates type never when there is no match 1`] =
 "
 `;
 
+exports[`a ParametersTypeCoder generates types for parameters (OAS2): header 1`] = `
+"type TestType = { name?: string };
+"
+`;
+
+exports[`a ParametersTypeCoder generates types for parameters (OAS2): path 1`] = `
+"type TestType = { id: string };
+"
+`;
+
+exports[`a ParametersTypeCoder generates types for parameters (OAS2): query 1`] = `
+"type TestType = { name?: string; age?: string };
+"
+`;
+
 exports[`a ParametersTypeCoder generates types for parameters: header 1`] = `
 "type TestType = { name?: string };
 "
diff --git a/test/typescript-generator/parameters-type-coder.test.js b/test/typescript-generator/parameters-type-coder.test.js
index a93d083..a741aaa 100644
--- a/test/typescript-generator/parameters-type-coder.test.js
+++ b/test/typescript-generator/parameters-type-coder.test.js
@@ -44,6 +44,29 @@ describe("a ParametersTypeCoder", () => {
     );
   });
 
+  it("generates types for parameters (OAS2)", () => {
+    const requirement = new Requirement([
+      { name: "id", in: "path", type: "string", required: true },
+      { name: "name", in: "query", type: "string" },
+      { name: "age", in: "query", type: "number" },
+      { name: "name", in: "header", type: "string" },
+    ]);
+
+    const pathCoder = new ParametersTypeCoder(requirement, "path");
+    const queryCoder = new ParametersTypeCoder(requirement, "query");
+    const headerCoder = new ParametersTypeCoder(requirement, "header");
+
+    expect(format(`type TestType =${pathCoder.write({})}`)).toMatchSnapshot(
+      "path"
+    );
+    expect(format(`type TestType =${queryCoder.write({})}`)).toMatchSnapshot(
+      "query"
+    );
+    expect(format(`type TestType =${headerCoder.write({})}`)).toMatchSnapshot(
+      "header"
+    );
+  });
+
   it("generates type never when there is no match", () => {
     const requirement = new Requirement([
       { name: "id", in: "path", schema: { type: "string" }, required: true },
diff --git a/test/typescript-generator/requirement.test.js b/test/typescript-generator/requirement.test.js
index 0059d29..25a3021 100644
--- a/test/typescript-generator/requirement.test.js
+++ b/test/typescript-generator/requirement.test.js
@@ -115,4 +115,18 @@ describe("a Requirement", () => {
       "b: bar 2",
     ]);
   });
+
+  it("provides a find(fn) method", () => {
+    const requirement = new Requirement({
+      a: "foo",
+      b: "bar",
+    });
+
+    const result = requirement.find(
+      // eslint-disable-next-line jest/no-conditional-in-test
+      (subRequirement, key) => key === "b" && subRequirement.data === "bar"
+    );
+
+    expect(result).toStrictEqual(requirement.get("b"));
+  });
 });