@rexeus/typeweaver-types 0.6.4 → 0.6.5

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.
@@ -0,0 +1,17 @@
1
+ import type {
2
+ IHttpRequest,
3
+ IRequestValidator,
4
+ SafeRequestValidationResult,
5
+ } from "@rexeus/typeweaver-core";
6
+ import { Validator } from "./Validator";
7
+
8
+ export declare abstract class RequestValidator
9
+ extends Validator
10
+ implements IRequestValidator
11
+ {
12
+ public constructor();
13
+ public abstract safeValidate(
14
+ request: IHttpRequest
15
+ ): SafeRequestValidationResult<IHttpRequest>;
16
+ public abstract validate(request: IHttpRequest): IHttpRequest;
17
+ }
@@ -0,0 +1,19 @@
1
+ import { Validator } from "./Validator";
2
+ /**
3
+ * Abstract base class for HTTP request validation.
4
+ *
5
+ * This class provides the foundation for request validators that:
6
+ * - Validate headers, body, query parameters, and path parameters
7
+ * - Support both safe (non-throwing) and unsafe (throwing) validation
8
+ * - Integrate with Zod schemas for runtime validation
9
+ * - Provide detailed error information for debugging
10
+ *
11
+ * Implementations should validate all request components and either:
12
+ * - Return validated data (for `validate`)
13
+ * - Return success/error result (for `safeValidate`)
14
+ */
15
+ export class RequestValidator extends Validator {
16
+ constructor() {
17
+ super();
18
+ }
19
+ }
@@ -0,0 +1,25 @@
1
+ import type {
2
+ IHttpResponse,
3
+ IResponseValidator,
4
+ ResponseValidationError,
5
+ SafeResponseValidationResult,
6
+ } from "@rexeus/typeweaver-core";
7
+ import { Validator } from "./Validator";
8
+
9
+ export declare abstract class ResponseValidator
10
+ extends Validator
11
+ implements IResponseValidator
12
+ {
13
+ public abstract safeValidate(
14
+ response: IHttpResponse
15
+ ): SafeResponseValidationResult<IHttpResponse>;
16
+ public abstract validate(response: IHttpResponse): IHttpResponse;
17
+ protected validateResponseType<Response extends IHttpResponse>(
18
+ responseName: string,
19
+ headerSchema: unknown,
20
+ bodySchema: unknown
21
+ ): (
22
+ response: IHttpResponse,
23
+ error: ResponseValidationError
24
+ ) => SafeResponseValidationResult<Response>;
25
+ }
@@ -0,0 +1,63 @@
1
+ import { Validator } from "./Validator";
2
+ /**
3
+ * Abstract base class for HTTP response validation.
4
+ *
5
+ * This class provides the foundation for response validators that:
6
+ * - Validate response status codes match expected values
7
+ * - Validate response headers and body against schemas
8
+ * - Support both safe (non-throwing) and unsafe (throwing) validation
9
+ * - Integrate with Zod schemas for runtime validation
10
+ *
11
+ * Response validators are typically used in API clients to ensure
12
+ * responses match the expected format before processing.
13
+ */
14
+ export class ResponseValidator extends Validator {
15
+ /**
16
+ * Generic response validation method that validates header and body schemas.
17
+ * This method reduces code duplication across individual response validators.
18
+ *
19
+ * @param responseName - Name of the response type for error reporting
20
+ * @param headerSchema - Zod schema for header validation (optional)
21
+ * @param bodySchema - Zod schema for body validation (optional)
22
+ * @returns Function that validates response and returns result
23
+ */
24
+ validateResponseType(responseName, headerSchema, bodySchema) {
25
+ return (response, error) => {
26
+ let isValid = true;
27
+ const validatedResponse = {
28
+ statusCode: response.statusCode,
29
+ header: undefined,
30
+ body: undefined
31
+ };
32
+ if (bodySchema) {
33
+ const validateBodyResult = bodySchema.safeParse(response.body);
34
+ if (!validateBodyResult.success) {
35
+ error.addBodyIssues(responseName, validateBodyResult.error.issues);
36
+ isValid = false;
37
+ } else {
38
+ validatedResponse.body = validateBodyResult.data;
39
+ }
40
+ }
41
+ if (headerSchema) {
42
+ const coercedHeader = this.coerceHeaderToSchema(response.header, this.getSchema(headerSchema));
43
+ const validateHeaderResult = headerSchema.safeParse(coercedHeader);
44
+ if (!validateHeaderResult.success) {
45
+ error.addHeaderIssues(responseName, validateHeaderResult.error.issues);
46
+ isValid = false;
47
+ } else {
48
+ validatedResponse.header = validateHeaderResult.data;
49
+ }
50
+ }
51
+ if (!isValid) {
52
+ return {
53
+ isValid: false,
54
+ error
55
+ };
56
+ }
57
+ return {
58
+ isValid: true,
59
+ data: validatedResponse
60
+ };
61
+ };
62
+ }
63
+ }
@@ -0,0 +1,25 @@
1
+ type SchemaInfo = {
2
+ readonly originalKey: string;
3
+ readonly isArray: boolean;
4
+ };
5
+
6
+ export declare abstract class Validator {
7
+ protected analyzeSchema(
8
+ shape: Record<string, unknown>,
9
+ caseSensitive: boolean
10
+ ): Map<string, SchemaInfo>;
11
+ protected getSchema(headerSchema: unknown): Record<string, unknown>;
12
+ protected coerceToSchema(
13
+ data: unknown,
14
+ shape: Record<string, unknown>,
15
+ caseSensitive: boolean
16
+ ): unknown;
17
+ protected coerceHeaderToSchema(
18
+ header: unknown,
19
+ shape: Record<string, unknown>
20
+ ): unknown;
21
+ protected coerceQueryToSchema(
22
+ query: unknown,
23
+ shape: Record<string, unknown>
24
+ ): unknown;
25
+ }
@@ -0,0 +1,185 @@
1
+ import z from "zod";
2
+ import { $ZodArray, $ZodOptional } from "zod/v4/core";
3
+ /**
4
+ * Abstract base class for HTTP validation.
5
+ *
6
+ * This class provides the foundation for request and response validators that:
7
+ * - Analyze Zod schemas for correct single/multi value handling of headers and query parameters
8
+ * - Coerce objects to match schema expectations
9
+ */
10
+ export class Validator {
11
+ static schemaCacheCaseSensitive = new WeakMap();
12
+ static schemaCacheCaseInsensitive = new WeakMap();
13
+ /**
14
+ * Analyzes a Zod schema shape to create an efficient lookup map.
15
+ * Results are cached using WeakMap for optimal performance.
16
+ *
17
+ * @param shape - The Zod schema shape to analyze
18
+ * @param caseSensitive - Whether to preserve key casing (true) or normalize to lowercase (false)
19
+ * @returns Map with lookup keys pointing to original key and array type information
20
+ */
21
+ analyzeSchema(shape, caseSensitive) {
22
+ const cache = caseSensitive ? Validator.schemaCacheCaseSensitive : Validator.schemaCacheCaseInsensitive;
23
+ const cached = cache.get(shape);
24
+ if (cached) {
25
+ return cached;
26
+ }
27
+ const schemaMap = this.buildSchemaMap(shape, caseSensitive);
28
+ cache.set(shape, schemaMap);
29
+ return schemaMap;
30
+ }
31
+ /**
32
+ *
33
+ * Extracts a Zod schema shape from header or query schemas.
34
+ * This is used to support schema coercion and analysis.
35
+ *
36
+ * @param headerSchema
37
+ * @returns
38
+ */
39
+ getSchema(headerSchema) {
40
+ if (headerSchema instanceof z.ZodObject) {
41
+ return headerSchema.shape;
42
+ }
43
+ if (headerSchema instanceof z.ZodOptional) {
44
+ const unwrapped = headerSchema.unwrap();
45
+ if (unwrapped instanceof z.ZodObject) {
46
+ return unwrapped.shape;
47
+ }
48
+ }
49
+ return {};
50
+ }
51
+ /**
52
+ * Builds a schema map by analyzing the Zod shape structure.
53
+ * Extracts type information for each field to support proper coercion.
54
+ */
55
+ buildSchemaMap(shape, caseSensitive) {
56
+ const schemaMap = new Map();
57
+ for (const [key, zodType] of Object.entries(shape)) {
58
+ if (!zodType) continue;
59
+ const isArray = zodType instanceof $ZodArray || zodType instanceof $ZodOptional && zodType._zod.def.innerType instanceof $ZodArray;
60
+ const lookupKey = caseSensitive ? key : key.toLowerCase();
61
+ schemaMap.set(lookupKey, {
62
+ originalKey: key,
63
+ isArray
64
+ });
65
+ }
66
+ return schemaMap;
67
+ }
68
+ /**
69
+ * Coerces objects to match schema expectations with configurable case sensitivity.
70
+ * Values not in the schema are ignored.
71
+ *
72
+ * @param data - The data object to coerce
73
+ * @param shape - The Zod schema shape to match against
74
+ * @param caseSensitive - Whether keys should match exactly (true) or case-insensitively (false)
75
+ * @returns Coerced data object with proper key casing and array coercion
76
+ */
77
+ coerceToSchema(data, shape, caseSensitive) {
78
+ if (typeof data !== "object" || data === null) {
79
+ return data;
80
+ }
81
+ const schemaMap = this.analyzeSchema(shape, caseSensitive);
82
+ const coerced = {};
83
+ for (const [key, value] of Object.entries(data)) {
84
+ const normalizedKey = caseSensitive ? key : key.toLowerCase();
85
+ const schemaInfo = schemaMap.get(normalizedKey);
86
+ if (schemaInfo) {
87
+ this.addValueToCoerced(coerced, normalizedKey, value, schemaInfo.isArray);
88
+ }
89
+ }
90
+ // If case-sensitive, return coerced object as is
91
+ if (caseSensitive) {
92
+ return coerced;
93
+ }
94
+ // If case-insensitive, map back to original keys from schema
95
+ return this.mapToOriginalKeys(coerced, schemaMap);
96
+ }
97
+ /**
98
+ * Adds a value to the coerced object, handling collisions when multiple
99
+ * values exist for the same key (e.g., duplicate headers with different casing).
100
+ * Preserves all values as arrays when collisions occur to prevent data loss.
101
+ */
102
+ addValueToCoerced(coerced, key, value, expectsArray) {
103
+ const existing = coerced[key];
104
+ const newValue = this.coerceValueStructure(value, expectsArray);
105
+ if (existing === undefined) {
106
+ coerced[key] = newValue;
107
+ return;
108
+ }
109
+ // Merge existing and new values
110
+ const existingArray = Array.isArray(existing) ? existing : [existing];
111
+ const newArray = Array.isArray(newValue) ? newValue : [newValue];
112
+ const merged = [...existingArray, ...newArray];
113
+ // If schema expects a single value but we have multiple, preserve as array
114
+ // to avoid data loss (validation will catch this later)
115
+ coerced[key] = expectsArray || merged.length > 1 ? merged : merged[0];
116
+ }
117
+ /**
118
+ * Coerces a value's structure to match schema expectations.
119
+ * Wraps single values in arrays when schema expects array type,
120
+ * unwraps single-element arrays when schema expects single value.
121
+ */
122
+ coerceValueStructure(value, expectsArray) {
123
+ if (expectsArray && !Array.isArray(value)) {
124
+ return [value];
125
+ }
126
+ if (!expectsArray && Array.isArray(value) && value.length === 1) {
127
+ return value[0];
128
+ }
129
+ return value;
130
+ }
131
+ /**
132
+ * Maps normalized (lowercase) keys back to their original casing as defined in the schema.
133
+ * Used for case-insensitive matching where the output should preserve schema-defined casing.
134
+ */
135
+ mapToOriginalKeys(coerced, schemaMap) {
136
+ const withOriginalKeys = {};
137
+ for (const [key, value] of Object.entries(coerced)) {
138
+ const originalKey = schemaMap.get(key)?.originalKey ?? key;
139
+ withOriginalKeys[originalKey] = value;
140
+ }
141
+ return withOriginalKeys;
142
+ }
143
+ /**
144
+ * Coerces header data to match schema expectations with case-insensitive matching.
145
+ *
146
+ * @param header - The header object to coerce
147
+ * @param shape - The Zod schema shape for headers
148
+ * @returns Coerced header object
149
+ */
150
+ coerceHeaderToSchema(header, shape) {
151
+ if (typeof header !== "object" || header === null) {
152
+ return this.coerceToSchema(header ?? {}, shape, false);
153
+ }
154
+ const preprocessed = this.splitCommaDelimitedValues(header, shape);
155
+ return this.coerceToSchema(preprocessed, shape, false);
156
+ }
157
+ /**
158
+ * Splits comma-separated header strings into arrays per RFC 7230.
159
+ * Only applies to fields where the schema expects an array type.
160
+ * Values that are already arrays pass through unchanged.
161
+ */
162
+ splitCommaDelimitedValues(header, shape) {
163
+ const schemaMap = this.analyzeSchema(shape, false);
164
+ const result = {};
165
+ for (const [key, value] of Object.entries(header)) {
166
+ const schemaInfo = schemaMap.get(key.toLowerCase());
167
+ if (schemaInfo?.isArray && typeof value === "string") {
168
+ result[key] = value.split(",").map((v) => v.trim()).filter((v) => v !== "");
169
+ } else {
170
+ result[key] = value;
171
+ }
172
+ }
173
+ return result;
174
+ }
175
+ /**
176
+ * Coerces query data to match schema expectations with case-sensitive matching.
177
+ *
178
+ * @param query - The query object to coerce
179
+ * @param shape - The Zod schema shape for query parameters
180
+ * @returns Coerced query object
181
+ */
182
+ coerceQueryToSchema(query, shape) {
183
+ return this.coerceToSchema(query ?? {}, shape, true);
184
+ }
185
+ }
@@ -0,0 +1 @@
1
+ export declare function assert(value: unknown, message?: string): asserts value;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * This file was automatically generated by typeweaver.
3
+ * DO NOT EDIT. Instead, modify the source definition file and generate again.
4
+ *
5
+ * @generated by @rexeus/typeweaver
6
+ */
7
+ export function assert(value, message) {
8
+ if (!value) {
9
+ throw new Error(message || "Assertion failed");
10
+ }
11
+ }
@@ -2,7 +2,7 @@
2
2
  /**
3
3
  * This file was automatically generated by typeweaver.
4
4
  * DO NOT EDIT. Instead, modify the source definition file and generate again.
5
- *
5
+ *
6
6
  * @generated by @rexeus/typeweaver
7
7
  */
8
8
 
@@ -13,8 +13,11 @@ import {
13
13
  <%= ownResponse.name %>Response,
14
14
  <% } %>
15
15
  } from "<%= responseFile %>";
16
- <% for (const sharedResponse of sharedErrorResponses) { %>
17
- import { <%= sharedResponse.name %>Response } from "<%= sharedResponse.path %>";
16
+ <% for (const sharedError of sharedErrorResponses) { %>
17
+ import { <%= sharedError.name %>Response } from "<%= sharedError.path %>";
18
+ <% } %>
19
+ <% for (const entityError of entityErrorResponses) { %>
20
+ import { <%= entityError.name %>Response } from "<%= entityError.path %>";
18
21
  <% } %>
19
22
 
20
23
  <% if (headerTsType) { %>
@@ -47,6 +50,10 @@ export type I<%= pascalCaseOperationId %>Request = {
47
50
  <% } else if (!hasErrorResponses) { %>
48
51
  export type Successful<%= pascalCaseOperationId %>Response = <%= pascalCaseOperationId %>Response;
49
52
  <% } else { %>
50
- export type Successful<%= pascalCaseOperationId %>Response = Exclude<<%= pascalCaseOperationId %>Response, <%= [...ownErrorResponses, ...sharedErrorResponses].map(({ name }) => `${name}Response`).join(" | ") %>
53
+ export type Successful<%= pascalCaseOperationId %>Response = Exclude<<%= pascalCaseOperationId %>Response, <%= [
54
+ ...sharedErrorResponses,
55
+ ...ownErrorResponses,
56
+ ...entityErrorResponses,
57
+ ].map(({ name }) => `${name}Response`).join(" | ") %>
51
58
  >;
52
59
  <% } %>
@@ -2,13 +2,16 @@
2
2
  /**
3
3
  * This file was automatically generated by typeweaver.
4
4
  * DO NOT EDIT. Instead, modify the source definition file and generate again.
5
- *
5
+ *
6
6
  * @generated by @rexeus/typeweaver
7
7
  */
8
8
 
9
9
  import { HttpResponse, HttpStatusCode } from "<%= coreDir %>";
10
- <% for(const sharedResponse of sharedResponses) { %>
11
- import type { I<%= sharedResponse.name %>Response, <%= sharedResponse.name %>Response } from "<%= sharedResponse.path %>";
10
+ <% for (const shared of sharedResponses) { %>
11
+ import type { I<%= shared.name %>Response, <%= shared.name %>Response } from "<%= shared.path %>";
12
+ <% } %>
13
+ <% for(const entityResponse of entityResponses) { %>
14
+ import type { I<%= entityResponse.name %>Response, <%= entityResponse.name %>Response } from "<%= entityResponse.path %>";
12
15
  <% } %>
13
16
 
14
17
  <% for (const ownResponse of ownResponses) { %>
@@ -73,14 +76,20 @@ export type I<%= pascalCaseOperationId %>Response =
73
76
  <% for (const ownResponse of ownResponses) { %>
74
77
  | I<%= ownResponse.name %>Response
75
78
  <% } %>
76
- <% for (const sharedResponse of sharedResponses) { %>
77
- | I<%= sharedResponse.name %>Response
79
+ <% for (const shared of sharedResponses) { %>
80
+ | I<%= shared.name %>Response
81
+ <% } %>
82
+ <% for (const entityResponse of entityResponses) { %>
83
+ | I<%= entityResponse.name %>Response
78
84
  <% } %>;
79
85
 
80
86
  export type <%= pascalCaseOperationId %>Response =
81
87
  <% for (const ownResponse of ownResponses) { %>
82
88
  | <%= ownResponse.name %>Response
83
89
  <% } %>
84
- <% for (const sharedResponse of sharedResponses) { %>
85
- | <%= sharedResponse.name %>Response
90
+ <% for (const shared of sharedResponses) { %>
91
+ | <%= shared.name %>Response
92
+ <% } %>
93
+ <% for (const entityResponse of entityResponses) { %>
94
+ | <%= entityResponse.name %>Response
86
95
  <% } %>;
@@ -7,8 +7,7 @@
7
7
  */
8
8
 
9
9
  import definition from "<%= sourcePath %>";
10
- import type { ZodSafeParseResult } from "zod";
11
- import {
10
+ import {
12
11
  type IHttpResponse,
13
12
  type SafeResponseValidationResult,
14
13
  ResponseValidationError
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rexeus/typeweaver-types",
3
- "version": "0.6.4",
3
+ "version": "0.6.5",
4
4
  "description": "Generates request and response types plus validators aligned with your API contract. Powered by Typeweaver 🧵✨",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -47,22 +47,23 @@
47
47
  },
48
48
  "homepage": "https://github.com/rexeus/typeweaver#readme",
49
49
  "peerDependencies": {
50
- "@rexeus/typeweaver-core": "^0.6.4",
51
- "@rexeus/typeweaver-gen": "^0.6.4"
50
+ "@rexeus/typeweaver-core": "^0.6.5",
51
+ "@rexeus/typeweaver-gen": "^0.6.5"
52
52
  },
53
53
  "devDependencies": {
54
+ "oxc-transform": "^0.115.0",
54
55
  "test-utils": "file:../test-utils",
55
- "@rexeus/typeweaver-gen": "^0.6.4",
56
- "@rexeus/typeweaver-core": "^0.6.4"
56
+ "@rexeus/typeweaver-core": "^0.6.5",
57
+ "@rexeus/typeweaver-gen": "^0.6.5"
57
58
  },
58
59
  "dependencies": {
59
60
  "case": "^1.6.3",
60
- "@rexeus/typeweaver-zod-to-ts": "^0.6.4"
61
+ "@rexeus/typeweaver-zod-to-ts": "^0.6.5"
61
62
  },
62
63
  "scripts": {
63
64
  "typecheck": "tsc --noEmit",
64
65
  "format": "oxfmt",
65
- "build": "tsdown && mkdir -p ./dist/templates ./dist/lib && cp -r ./src/templates/* ./dist/templates/ && cp -r ./src/lib/* ./dist/lib/ && cp ../../LICENSE ../../NOTICE ./dist/",
66
+ "build": "tsdown && mkdir -p ./dist/templates ./dist/lib && cp -r ./src/templates/* ./dist/templates/ && node scripts/compile-lib.mjs && cp src/lib-declarations/*.d.ts dist/lib/ && cp src/lib/index.ts dist/lib/ && cp ../../LICENSE ../../NOTICE ./dist/",
66
67
  "test": "vitest --run",
67
68
  "preversion": "npm run build"
68
69
  }
@@ -1,54 +0,0 @@
1
- /**
2
- * This file was automatically generated by typeweaver.
3
- * DO NOT EDIT. Instead, modify the source definition file and generate again.
4
- *
5
- * @generated by @rexeus/typeweaver
6
- */
7
-
8
- import type {
9
- IHttpRequest,
10
- IRequestValidator,
11
- SafeRequestValidationResult,
12
- } from "@rexeus/typeweaver-core";
13
- import { Validator } from "./Validator";
14
-
15
- /**
16
- * Abstract base class for HTTP request validation.
17
- *
18
- * This class provides the foundation for request validators that:
19
- * - Validate headers, body, query parameters, and path parameters
20
- * - Support both safe (non-throwing) and unsafe (throwing) validation
21
- * - Integrate with Zod schemas for runtime validation
22
- * - Provide detailed error information for debugging
23
- *
24
- * Implementations should validate all request components and either:
25
- * - Return validated data (for `validate`)
26
- * - Return success/error result (for `safeValidate`)
27
- */
28
- export abstract class RequestValidator
29
- extends Validator
30
- implements IRequestValidator
31
- {
32
- public constructor() {
33
- super();
34
- }
35
-
36
- /**
37
- * Validates a request without throwing errors.
38
- *
39
- * @param request - The HTTP request to validate
40
- * @returns A result object containing either the validated request or error details
41
- */
42
- public abstract safeValidate(
43
- request: IHttpRequest
44
- ): SafeRequestValidationResult<IHttpRequest>;
45
-
46
- /**
47
- * Validates a request and throws if validation fails.
48
- *
49
- * @param request - The HTTP request to validate
50
- * @returns The validated request with proper typing
51
- * @throws {RequestValidationError} If any part of the request fails validation
52
- */
53
- public abstract validate(request: IHttpRequest): IHttpRequest;
54
- }
@@ -1,120 +0,0 @@
1
- /**
2
- * This file was automatically generated by typeweaver.
3
- * DO NOT EDIT. Instead, modify the source definition file and generate again.
4
- *
5
- * @generated by @rexeus/typeweaver
6
- */
7
-
8
- import type {
9
- HttpBodySchema,
10
- HttpHeaderSchema,
11
- IHttpResponse,
12
- IResponseValidator,
13
- ResponseValidationError,
14
- SafeResponseValidationResult,
15
- } from "@rexeus/typeweaver-core";
16
- import { Validator } from "./Validator";
17
- import type { ZodSafeParseResult } from "zod";
18
-
19
- /**
20
- * Abstract base class for HTTP response validation.
21
- *
22
- * This class provides the foundation for response validators that:
23
- * - Validate response status codes match expected values
24
- * - Validate response headers and body against schemas
25
- * - Support both safe (non-throwing) and unsafe (throwing) validation
26
- * - Integrate with Zod schemas for runtime validation
27
- *
28
- * Response validators are typically used in API clients to ensure
29
- * responses match the expected format before processing.
30
- */
31
- export abstract class ResponseValidator
32
- extends Validator
33
- implements IResponseValidator
34
- {
35
- /**
36
- * Validates a response without throwing errors.
37
- *
38
- * @param response - The HTTP response to validate
39
- * @returns A result object containing either the validated response or error details
40
- */
41
- public abstract safeValidate(
42
- response: IHttpResponse
43
- ): SafeResponseValidationResult<IHttpResponse>;
44
-
45
- /**
46
- * Validates a response and throws if validation fails.
47
- *
48
- * @param response - The HTTP response to validate
49
- * @returns The validated response with proper typing
50
- * @throws {InvalidResponseStatusCodeError} If status code doesn't match expected
51
- * @throws {ResponseValidationError} If response structure fails validation
52
- */
53
- public abstract validate(response: IHttpResponse): IHttpResponse;
54
-
55
- /**
56
- * Generic response validation method that validates header and body schemas.
57
- * This method reduces code duplication across individual response validators.
58
- *
59
- * @param responseName - Name of the response type for error reporting
60
- * @param headerSchema - Zod schema for header validation (optional)
61
- * @param bodySchema - Zod schema for body validation (optional)
62
- * @returns Function that validates response and returns result
63
- */
64
- protected validateResponseType<Response extends IHttpResponse>(
65
- responseName: string,
66
- headerSchema: HttpHeaderSchema | undefined,
67
- bodySchema: HttpBodySchema | undefined
68
- ): (
69
- response: IHttpResponse,
70
- error: ResponseValidationError
71
- ) => SafeResponseValidationResult<Response> {
72
- return (response, error) => {
73
- let isValid = true;
74
- const validatedResponse: IHttpResponse = {
75
- statusCode: response.statusCode,
76
- header: undefined,
77
- body: undefined,
78
- };
79
-
80
- if (bodySchema) {
81
- const validateBodyResult = bodySchema.safeParse(
82
- response.body
83
- ) as unknown as ZodSafeParseResult<Response["body"]>;
84
-
85
- if (!validateBodyResult.success) {
86
- error.addBodyIssues(responseName, validateBodyResult.error.issues);
87
- isValid = false;
88
- } else {
89
- validatedResponse.body = validateBodyResult.data;
90
- }
91
- }
92
-
93
- if (headerSchema) {
94
- const coercedHeader = this.coerceHeaderToSchema(
95
- response.header,
96
- this.getSchema(headerSchema)
97
- );
98
- const validateHeaderResult = headerSchema.safeParse(
99
- coercedHeader
100
- ) as unknown as ZodSafeParseResult<Response["header"]>;
101
-
102
- if (!validateHeaderResult.success) {
103
- error.addHeaderIssues(
104
- responseName,
105
- validateHeaderResult.error.issues
106
- );
107
- isValid = false;
108
- } else {
109
- validatedResponse.header = validateHeaderResult.data;
110
- }
111
- }
112
-
113
- if (!isValid) {
114
- return { isValid: false, error };
115
- }
116
-
117
- return { isValid: true, data: validatedResponse as Response };
118
- };
119
- }
120
- }