@congruent-stack/congruent-api 0.13.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,5 +1,8 @@
1
1
  'use strict';
2
2
 
3
+ var zod = require('zod');
4
+ var mini = require('zod/v4/mini');
5
+
3
6
  function endpoint(definition) {
4
7
  return new HttpMethodEndpoint(definition);
5
8
  }
@@ -321,9 +324,19 @@ function parseRequestDefinitionField(definition, key, requestObject) {
321
324
  }
322
325
  break;
323
326
  }
327
+ const errors = [`'${key}' is required for this endpoint`];
328
+ if (key === "body") {
329
+ errors.push("{ 'Content-Type': 'application/json' } header might be missing");
330
+ }
324
331
  return {
325
332
  code: HttpStatusCode.BadRequest_400,
326
- body: `'${key}' is required for this endpoint` + (key === "body" ? ", { 'Content-Type': 'application/json' } header might be missing" : "")
333
+ headers: {
334
+ "x-failed-validation-sections": key
335
+ },
336
+ body: {
337
+ errors
338
+ // treeifyError return type like structure, but here we just return simple error messages
339
+ }
327
340
  };
328
341
  }
329
342
  return result2.data;
@@ -332,7 +345,10 @@ function parseRequestDefinitionField(definition, key, requestObject) {
332
345
  if (!result.success) {
333
346
  return {
334
347
  code: HttpStatusCode.BadRequest_400,
335
- body: result.error.issues
348
+ headers: {
349
+ "x-failed-validation-sections": key
350
+ },
351
+ body: zod.treeifyError(result.error)
336
352
  };
337
353
  }
338
354
  return result.data;
@@ -563,9 +579,19 @@ function middlewareParseRequestDefinitionField(middlewareSchemas, key, requestOb
563
579
  }
564
580
  break;
565
581
  }
582
+ const errors = [`'${key}' is required for this endpoint`];
583
+ if (key === "body") {
584
+ errors.push("{ 'Content-Type': 'application/json' } header might be missing");
585
+ }
566
586
  return {
567
587
  code: HttpStatusCode.BadRequest_400,
568
- body: `'${key}' is required for this endpoint` + (key === "body" ? ", { 'Content-Type': 'application/json' } header might be missing" : "")
588
+ headers: {
589
+ "x-failed-validation-sections": key
590
+ },
591
+ body: {
592
+ errors
593
+ // treeifyError return type like structure, but here we just return simple error messages
594
+ }
569
595
  };
570
596
  }
571
597
  return result2.data;
@@ -574,7 +600,10 @@ function middlewareParseRequestDefinitionField(middlewareSchemas, key, requestOb
574
600
  if (!result.success) {
575
601
  return {
576
602
  code: HttpStatusCode.BadRequest_400,
577
- body: result.error.issues
603
+ headers: {
604
+ "x-failed-validation-sections": key
605
+ },
606
+ body: zod.treeifyError(result.error)
578
607
  };
579
608
  }
580
609
  return result.data;
@@ -740,6 +769,15 @@ function partialPathString(_apiReg, path) {
740
769
  return path;
741
770
  }
742
771
 
772
+ var RequestFailureCode = /* @__PURE__ */ ((RequestFailureCode2) => {
773
+ RequestFailureCode2[RequestFailureCode2["ErrorThrown"] = -1] = "ErrorThrown";
774
+ RequestFailureCode2[RequestFailureCode2["SchemaValidationFailed"] = -2] = "SchemaValidationFailed";
775
+ return RequestFailureCode2;
776
+ })(RequestFailureCode || {});
777
+ function isRequestFailureSchemaValidationFailedOutput(output) {
778
+ return typeof output === "object" && output !== null && "code" in output && output.code === -2 /* SchemaValidationFailed */;
779
+ }
780
+
743
781
  function createClient(contract, clientGenericHandler) {
744
782
  const apiClient = new ApiClient(contract, clientGenericHandler);
745
783
  return apiClient;
@@ -783,9 +821,22 @@ class InnerApiClient {
783
821
  currObj[key] = (requestObject) => {
784
822
  const pathParams = { ...client.__CONTEXT__.pathParameters };
785
823
  client.__CONTEXT__ = InnerApiClient._initNewContext();
824
+ let schemaValidationRequestFailureOutput = null;
786
825
  const headers = clientParseRequestDefinitionField(val.definition, "headers", requestObject);
826
+ if (isRequestFailureSchemaValidationFailedOutput(headers)) {
827
+ schemaValidationRequestFailureOutput = headers;
828
+ return schemaValidationRequestFailureOutput;
829
+ }
787
830
  const query = clientParseRequestDefinitionField(val.definition, "query", requestObject);
831
+ if (isRequestFailureSchemaValidationFailedOutput(query)) {
832
+ schemaValidationRequestFailureOutput = query;
833
+ return schemaValidationRequestFailureOutput;
834
+ }
788
835
  const body = clientParseRequestDefinitionField(val.definition, "body", requestObject);
836
+ if (isRequestFailureSchemaValidationFailedOutput(body)) {
837
+ schemaValidationRequestFailureOutput = body;
838
+ return schemaValidationRequestFailureOutput;
839
+ }
789
840
  const path = `/${val.pathSegments.map(
790
841
  (segment) => segment.startsWith(":") ? pathParams[segment.slice(1)] ?? "?" : segment
791
842
  ).join("/")}`;
@@ -824,13 +875,32 @@ function clientParseRequestDefinitionField(definition, key, requestObject) {
824
875
  }
825
876
  break;
826
877
  }
827
- throw new Error(`'${key}' is required for this endpoint`);
878
+ const errors = [`'${key}' is required for this endpoint`];
879
+ if (key === "body") {
880
+ errors.push("{ 'Content-Type': 'application/json' } header might be missing");
881
+ }
882
+ return {
883
+ code: RequestFailureCode.SchemaValidationFailed,
884
+ headers: {
885
+ "x-failed-validation-sections": key
886
+ },
887
+ body: {
888
+ errors
889
+ // treeifyError return type like structure, but here we just return simple error messages
890
+ }
891
+ };
828
892
  }
829
893
  return result2.data;
830
894
  }
831
895
  const result = definition[key].safeParse(requestObject[key]);
832
896
  if (!result.success) {
833
- throw new Error(`Validation for '${key}' failed`, { cause: result.error });
897
+ return {
898
+ code: RequestFailureCode.SchemaValidationFailed,
899
+ headers: {
900
+ "x-failed-validation-sections": key
901
+ },
902
+ body: mini.treeifyError(result.error)
903
+ };
834
904
  }
835
905
  return result.data;
836
906
  }
@@ -905,6 +975,15 @@ class DIContainer extends DIContainerBase {
905
975
  this._map.set(serviceNameCapitalizedLiteral, entry);
906
976
  return this;
907
977
  }
978
+ registerTransient(serviceNameCapitalizedLiteral, factory) {
979
+ return this.register(serviceNameCapitalizedLiteral, factory, "transient");
980
+ }
981
+ registerScoped(serviceNameCapitalizedLiteral, factory) {
982
+ return this.register(serviceNameCapitalizedLiteral, factory, "scoped");
983
+ }
984
+ registerSingleton(serviceNameCapitalizedLiteral, factory) {
985
+ return this.register(serviceNameCapitalizedLiteral, factory, "singleton");
986
+ }
908
987
  createTestClone() {
909
988
  return new DIContainerTestClone(this);
910
989
  }
@@ -1005,9 +1084,19 @@ function decoratorParseRequestDefinitionField(decoratorSchemas, key, requestObje
1005
1084
  }
1006
1085
  break;
1007
1086
  }
1087
+ const errors = [`'${key}' is required for this endpoint`];
1088
+ if (key === "body") {
1089
+ errors.push("{ 'Content-Type': 'application/json' } header might be missing");
1090
+ }
1008
1091
  return {
1009
1092
  code: HttpStatusCode.BadRequest_400,
1010
- body: `'${key}' is required for this endpoint` + (key === "body" ? ", { 'Content-Type': 'application/json' } header might be missing" : "")
1093
+ headers: {
1094
+ "x-failed-validation-sections": key
1095
+ },
1096
+ body: {
1097
+ errors
1098
+ // treeifyError return type like structure, but here we just return simple error messages
1099
+ }
1011
1100
  };
1012
1101
  }
1013
1102
  return result2.data;
@@ -1016,7 +1105,10 @@ function decoratorParseRequestDefinitionField(decoratorSchemas, key, requestObje
1016
1105
  if (!result.success) {
1017
1106
  return {
1018
1107
  code: HttpStatusCode.BadRequest_400,
1019
- body: result.error.issues
1108
+ headers: {
1109
+ "x-failed-validation-sections": key
1110
+ },
1111
+ body: zod.treeifyError(result.error)
1020
1112
  };
1021
1113
  }
1022
1114
  return result.data;
@@ -1118,6 +1210,7 @@ exports.MethodEndpointHandlerRegistryEntry = MethodEndpointHandlerRegistryEntry;
1118
1210
  exports.MiddlewareHandlersRegistry = MiddlewareHandlersRegistry;
1119
1211
  exports.MiddlewareHandlersRegistryEntry = MiddlewareHandlersRegistryEntry;
1120
1212
  exports.MiddlewareHandlersRegistryEntryInternal = MiddlewareHandlersRegistryEntryInternal;
1213
+ exports.RequestFailureCode = RequestFailureCode;
1121
1214
  exports.apiContract = apiContract;
1122
1215
  exports.createClient = createClient;
1123
1216
  exports.createInProcApiClient = createInProcApiClient;
@@ -1127,6 +1220,7 @@ exports.execHandlerChain = execHandlerChain;
1127
1220
  exports.flatListAllRegistryEntries = flatListAllRegistryEntries;
1128
1221
  exports.isHttpResponseObject = isHttpResponseObject;
1129
1222
  exports.isHttpStatusCode = isHttpStatusCode;
1223
+ exports.isRequestFailureSchemaValidationFailedOutput = isRequestFailureSchemaValidationFailedOutput;
1130
1224
  exports.middleware = middleware;
1131
1225
  exports.partial = partial;
1132
1226
  exports.partialPathString = partialPathString;
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import z, { z as z$1 } from 'zod';
1
+ import z, { z as z$1, treeifyError } from 'zod';
2
2
 
3
3
  type StringLiteral<T extends string> = string extends T ? never : T;
4
4
  type CapitalizedStringLiteral<T extends string> = string extends T ? never : T extends `${Uppercase<infer F>}${infer _}` ? F extends Lowercase<F> ? never : T : `❌ ERROR: Must start with uppercase letter`;
@@ -16,6 +16,9 @@ declare class DIContainerBase<R extends DIRegistry> {
16
16
  }
17
17
  declare class DIContainer<R extends DIRegistry = {}> extends DIContainerBase<R> {
18
18
  register<K extends string, T>(serviceNameCapitalizedLiteral: CapitalizedStringLiteral<K>, factory: (scope: DIScope<R>) => T, lifetime: DILifetime): DIContainer<R & Record<K, DIRegistryEntry<T>>>;
19
+ registerTransient<K extends string, T>(serviceNameCapitalizedLiteral: CapitalizedStringLiteral<K>, factory: (scope: DIScope<R>) => T): DIContainer<R & Record<K, DIRegistryEntry<T>>>;
20
+ registerScoped<K extends string, T>(serviceNameCapitalizedLiteral: CapitalizedStringLiteral<K>, factory: (scope: DIScope<R>) => T): DIContainer<R & Record<K, DIRegistryEntry<T>>>;
21
+ registerSingleton<K extends string, T>(serviceNameCapitalizedLiteral: CapitalizedStringLiteral<K>, factory: (scope: DIScope<R>) => T): DIContainer<R & Record<K, DIRegistryEntry<T>>>;
19
22
  createTestClone(): DIContainerTestClone<R, this>;
20
23
  }
21
24
  declare class DIContainerTestClone<R extends DIRegistry, TDIContainer extends DIContainer<R>> extends DIContainerBase<R> {
@@ -145,9 +148,30 @@ interface ICanTriggerAsync {
145
148
  get genericPath(): string;
146
149
  }
147
150
 
151
+ type _JoinSections<A extends string, B extends string> = [
152
+ A
153
+ ] extends [never] ? B : [B] extends [never] ? A : `${A},${B}`;
154
+ type _DefinedSection<T, K extends string> = Exclude<T, undefined> extends never ? never : K;
155
+ type _FailedValidationSectionsImpl<P extends string, H extends string, Q extends string, B extends string> = P | H | Q | B | _JoinSections<P, H> | _JoinSections<P, Q> | _JoinSections<P, B> | _JoinSections<H, Q> | _JoinSections<H, B> | _JoinSections<Q, B> | _JoinSections<_JoinSections<P, H>, Q> | _JoinSections<_JoinSections<P, H>, B> | _JoinSections<_JoinSections<P, Q>, B> | _JoinSections<_JoinSections<H, Q>, B> | _JoinSections<_JoinSections<_JoinSections<P, H>, Q>, B>;
156
+ type FailedValidationSections<T extends {
157
+ headers?: any;
158
+ query?: any;
159
+ body?: any;
160
+ }> = _FailedValidationSectionsImpl<'path-segments', _DefinedSection<T['headers'], 'headers'>, _DefinedSection<T['query'], 'query'>, _DefinedSection<T['body'], 'body'>>;
161
+
148
162
  type HttpMethodEndpointHandlerOutput<TEndpointDefinition extends IHttpMethodEndpointDefinition> = {
149
163
  [THttpStatusCode in keyof TEndpointDefinition['responses'] & HttpStatusCode]: TEndpointDefinition['responses'][THttpStatusCode] extends HttpMethodEndpointResponse<THttpStatusCode, infer TRespDef> ? CreateHandlerOutput<THttpStatusCode, TRespDef> : never;
150
- }[keyof TEndpointDefinition['responses'] & HttpStatusCode];
164
+ }[keyof TEndpointDefinition['responses'] & HttpStatusCode] | {
165
+ code: HttpStatusCode.BadRequest_400;
166
+ headers: {
167
+ "x-failed-validation-sections": FailedValidationSections<TEndpointDefinition>;
168
+ };
169
+ body: ReturnType<typeof treeifyError<Exclude<TEndpointDefinition['headers'] | TEndpointDefinition['query'] | TEndpointDefinition['body'], undefined>>>;
170
+ } | {
171
+ code: HttpStatusCode.InternalServerError_500;
172
+ headers?: unknown;
173
+ body?: {};
174
+ };
151
175
  type CreateHandlerOutput<THttpStatusCode extends HttpStatusCode, TRespDef> = TRespDef extends {
152
176
  headers: z$1.ZodType;
153
177
  body: z$1.ZodType;
@@ -182,10 +206,6 @@ type HttpResponseObject = {
182
206
  headers?: any;
183
207
  body?: any;
184
208
  };
185
- type BadRequestValidationErrorResponse = {
186
- code: HttpStatusCode.BadRequest_400;
187
- body: z$1.core.$ZodIssue[] | string;
188
- };
189
209
  declare function isHttpResponseObject(obj: any): obj is HttpResponseObject;
190
210
 
191
211
  declare const __overlap__error__: unique symbol;
@@ -211,8 +231,29 @@ type DecoratorHandlerContext = {
211
231
  originalRequest: any;
212
232
  };
213
233
 
234
+ type InferInputProp<T extends IHttpMethodEndpointDefinition, P extends keyof T> = T[P] extends z$1.ZodType<any, any> ? Record<P, z$1.input<T[P]>> : {};
235
+ type Merge3<A, B, C> = A & B & C;
236
+ type HttpMethodCallInput<T extends IHttpMethodEndpointDefinition> = Merge3<InferInputProp<T, "headers">, InferInputProp<T, "query">, InferInputProp<T, "body">> extends infer M ? keyof M extends never ? never : M : never;
237
+ declare enum RequestFailureCode {
238
+ ErrorThrown = -1,
239
+ SchemaValidationFailed = -2
240
+ }
241
+ type RequestFailureErrorThrownOutput = {
242
+ code: RequestFailureCode.ErrorThrown;
243
+ body: Error;
244
+ };
245
+ type RequestFailureSchemaValidationFailedOutput<TEndpointDefinition extends IHttpMethodEndpointDefinition> = {
246
+ code: RequestFailureCode.SchemaValidationFailed;
247
+ headers: {
248
+ "x-failed-validation-sections": FailedValidationSections<TEndpointDefinition>;
249
+ };
250
+ body: ReturnType<typeof treeifyError<Exclude<TEndpointDefinition['headers'] | TEndpointDefinition['query'] | TEndpointDefinition['body'], undefined>>>;
251
+ };
252
+ declare function isRequestFailureSchemaValidationFailedOutput<TEndpointDefinition extends IHttpMethodEndpointDefinition>(output: any): output is RequestFailureSchemaValidationFailedOutput<TEndpointDefinition>;
253
+ type HttpMethodCallFunc<TEndpointDefinition extends IHttpMethodEndpointDefinition> = HttpMethodCallInput<TEndpointDefinition> extends never ? () => Promise<HttpMethodEndpointHandlerOutput<TEndpointDefinition>> : (input: HttpMethodCallInput<TEndpointDefinition>) => Promise<HttpMethodEndpointHandlerOutput<TEndpointDefinition> | RequestFailureErrorThrownOutput | RequestFailureSchemaValidationFailedOutput<TEndpointDefinition>>;
254
+
214
255
  type HttpMethodEndpointHandler<TDef extends IHttpMethodEndpointDefinition, TPathParams extends string, TInjected> = (input: HttpMethodEndpointHandlerInput<TDef, TPathParams>, context: EndpointHandlerContext<TInjected>) => Promise<HttpMethodEndpointHandlerOutput<TDef>>;
215
- type ClientHttpMethodEndpointHandler = (input: ClientHttpMethodEndpointHandlerInput) => Promise<ClientHttpMethodEndpointHandlerOutput>;
256
+ type ClientHttpMethodEndpointHandler = (input: ClientHttpMethodEndpointHandlerInput) => Promise<ClientHttpMethodEndpointHandlerOutput | RequestFailureErrorThrownOutput>;
216
257
 
217
258
  type MiddlewareHandlerSchemas = {
218
259
  headers?: z$1.ZodType;
@@ -232,7 +273,17 @@ type MiddlewareHandlerInput<TPathParams extends string, TMiddlewareSchemas exten
232
273
  };
233
274
  type MiddlewareHandlerOutput<TMiddlewareSchemas extends MiddlewareHandlerSchemas> = void | {
234
275
  [THttpStatusCode in keyof TMiddlewareSchemas['responses'] & HttpStatusCode]: TMiddlewareSchemas['responses'][THttpStatusCode] extends HttpMethodEndpointResponse<THttpStatusCode, infer TRespDef> ? CreateHandlerOutput<THttpStatusCode, TRespDef> : never;
235
- }[keyof TMiddlewareSchemas['responses'] & HttpStatusCode];
276
+ }[keyof TMiddlewareSchemas['responses'] & HttpStatusCode] | {
277
+ code: HttpStatusCode.BadRequest_400;
278
+ headers: {
279
+ "x-failed-validation-sections": FailedValidationSections<TMiddlewareSchemas>;
280
+ };
281
+ body: ReturnType<typeof treeifyError<Exclude<TMiddlewareSchemas['headers'] | TMiddlewareSchemas['query'] | TMiddlewareSchemas['body'], undefined>>>;
282
+ } | {
283
+ code: HttpStatusCode.InternalServerError_500;
284
+ headers?: unknown;
285
+ body?: unknown;
286
+ };
236
287
  type MiddlewareHandlerInputInternal = {
237
288
  method: HttpMethod;
238
289
  pathSegments: readonly string[];
@@ -342,7 +393,17 @@ type DecoratorHandlerInput<TDecoratorSchemas extends IDecoratorHandlerSchemas> =
342
393
  };
343
394
  type DecoratorHandlerOutput<TDecoratorSchemas extends IDecoratorHandlerSchemas> = void | {
344
395
  [THttpStatusCode in keyof TDecoratorSchemas['responses'] & HttpStatusCode]: TDecoratorSchemas['responses'][THttpStatusCode] extends HttpMethodEndpointResponse<THttpStatusCode, infer TRespDef> ? CreateHandlerOutput<THttpStatusCode, TRespDef> : never;
345
- }[keyof TDecoratorSchemas['responses'] & HttpStatusCode];
396
+ }[keyof TDecoratorSchemas['responses'] & HttpStatusCode] | {
397
+ code: HttpStatusCode.BadRequest_400;
398
+ headers: {
399
+ "x-failed-validation-sections": FailedValidationSections<TDecoratorSchemas>;
400
+ };
401
+ body: ReturnType<typeof treeifyError<Exclude<TDecoratorSchemas['headers'] | TDecoratorSchemas['query'] | TDecoratorSchemas['body'], undefined>>>;
402
+ } | {
403
+ code: HttpStatusCode.InternalServerError_500;
404
+ headers?: unknown;
405
+ body?: unknown;
406
+ };
346
407
  interface IEndpointHandlerDecorator<TDecoratorSchemas extends IDecoratorHandlerSchemas> {
347
408
  handle(input: DecoratorHandlerInput<TDecoratorSchemas>, context: DecoratorHandlerContext): Promise<DecoratorHandlerOutput<TDecoratorSchemas>>;
348
409
  }
@@ -417,7 +478,7 @@ declare class MethodEndpointHandlerRegistryEntry<TDef extends IHttpMethodEndpoin
417
478
  pathParams: TypedPathParams<TPathParams>;
418
479
  query: TDef['query'] extends z.ZodType ? z.output<TDef['query']> : null;
419
480
  body: TDef['body'] extends z.ZodType ? z.output<TDef['body']> : null;
420
- }): Promise<HttpMethodEndpointHandlerOutput<TDef> | BadRequestValidationErrorResponse>;
481
+ }): Promise<HttpMethodEndpointHandlerOutput<TDef>>;
421
482
  triggerNoStaticTypeCheck(diScope: DIScope<any>, requestObject: HttpRequestObject, context: EndpointHandlerContext<any>): Promise<any>;
422
483
  }
423
484
 
@@ -484,11 +545,6 @@ declare function register<const TDef extends IHttpMethodEndpointDefinition & Val
484
545
 
485
546
  declare function execHandlerChain(diScope: DIScope<any>, allHandlerEntries: ICanTriggerAsync[], input: ClientHttpMethodEndpointHandlerInput): Promise<any>;
486
547
 
487
- type InferInputProp<T extends IHttpMethodEndpointDefinition, P extends keyof T> = T[P] extends z$1.ZodType<any, any> ? Record<P, z$1.input<T[P]>> : {};
488
- type Merge3<A, B, C> = A & B & C;
489
- type HttpMethodCallInput<T extends IHttpMethodEndpointDefinition> = Merge3<InferInputProp<T, "headers">, InferInputProp<T, "query">, InferInputProp<T, "body">> extends infer M ? keyof M extends never ? never : M : never;
490
- type HttpMethodCallFunc<T extends IHttpMethodEndpointDefinition> = HttpMethodCallInput<T> extends never ? () => Promise<HttpMethodEndpointHandlerOutput<T>> : (input: HttpMethodCallInput<T>) => Promise<HttpMethodEndpointHandlerOutput<T>>;
491
-
492
548
  declare function createClient<TDef extends IApiContractDefinition & ValidateApiContractDefinition<TDef>>(contract: ApiContract<TDef>, clientGenericHandler: ClientHttpMethodEndpointHandler): ApiClient<TDef>;
493
549
  type PathParamFunc<TDef> = (value: string | number) => TDef;
494
550
  interface IClientContext {
@@ -512,4 +568,4 @@ interface InProcApiClientOptions<TDef extends IApiContractDefinition & ValidateA
512
568
  }
513
569
  declare function createInProcApiClient<TDef extends IApiContractDefinition & ValidateApiContractDefinition<TDef>, TDIContainer extends DIContainer, TDIContainerTestClone extends DIContainerTestClone<any, TDIContainer>>(contract: ApiContract<TDef>, testContainer: TDIContainerTestClone, registry: ApiHandlersRegistry<TDef, TDIContainer>, options?: InProcApiClientOptions<TDef, TDIContainer>): ApiClient<TDef>;
514
570
 
515
- export { ApiClient, type ApiClientDef, ApiContract, ApiHandlersRegistry, type ApiHandlersRegistryDef, type BadRequestValidationErrorResponse, type CapitalizedStringLiteral, type ClientHttpMethodEndpointHandler, type ClientHttpMethodEndpointHandlerInput, type ClientHttpMethodEndpointHandlerOutput, type CreateHandlerOutput, DIContainer, DIContainerBase, DIContainerTestClone, type DILifetime, type DIRegistry, type DIRegistryEntry, type DIScope, type DecoratorHandlerContext, type DecoratorHandlerInput, type DecoratorHandlerOutput, type EndpointHandlerContext, type EndpointHandlerContextOverlapGuard, type EndpointHandlerDecoratorFactory, type ExtractConcatenatedParamNamesFromMethodFirstPath, type ExtractConcatenatedParamNamesFromPath, type ExtractConcatenatedParamNamesFromPathSegments, type ExtractEndpointFromPath, type GenericOnHandlerRegisteredCallback, type HttpMethod, type HttpMethodCallFunc, type HttpMethodCallInput, HttpMethodEndpoint, type HttpMethodEndpointHandler, type HttpMethodEndpointHandlerInput, type HttpMethodEndpointHandlerOutput, HttpMethodEndpointResponse, type HttpMethodEndpointResponses, type HttpRequestObject, type HttpResponseObject, HttpStatusCode, type IApiContractDefinition, type IClientContext, type IDecoratorHandlerSchemas, type IEndpointHandlerDecorator, type IHttpMethodEndpointDefinition, type IHttpMethodEndpointResponseDefinition, type IRegistrySettings, type InProcApiClientOptions, type LowerCasedHttpMethod, MethodEndpointHandlerRegistryEntry, type MethodFirstPath, type MiddlewareHandler, type MiddlewareHandlerContext, type MiddlewareHandlerContextOverlapGuard, type MiddlewareHandlerInput, type MiddlewareHandlerInputInternal, type MiddlewareHandlerInternal, type MiddlewareHandlerOutput, type MiddlewareHandlerSchemas, MiddlewareHandlersRegistry, MiddlewareHandlersRegistryEntry, MiddlewareHandlersRegistryEntryInternal, type MiddlewarePath, type OnHandlerRegisteredCallback, type OnMiddlewareHandlerRegisteredCallback, type PartialPath, type PartialPathResult, type PathParamFunc, type PrepareRegistryEntryCallback, type StringLiteral, type TypedPathParams, type ValidateApiContractDefinition, type ValidateHttpMethodEndpointDefinition, apiContract, createClient, createInProcApiClient, createRegistry, endpoint, execHandlerChain, flatListAllRegistryEntries, isHttpResponseObject, isHttpStatusCode, middleware, partial, partialPathString, register, response, route, triggerEndpointDecoratorNoStaticTypeCheck, triggerMiddlewareDecoratorNoStaticTypeCheck };
571
+ export { ApiClient, type ApiClientDef, ApiContract, ApiHandlersRegistry, type ApiHandlersRegistryDef, type CapitalizedStringLiteral, type ClientHttpMethodEndpointHandler, type ClientHttpMethodEndpointHandlerInput, type ClientHttpMethodEndpointHandlerOutput, type CreateHandlerOutput, DIContainer, DIContainerBase, DIContainerTestClone, type DILifetime, type DIRegistry, type DIRegistryEntry, type DIScope, type DecoratorHandlerContext, type DecoratorHandlerInput, type DecoratorHandlerOutput, type EndpointHandlerContext, type EndpointHandlerContextOverlapGuard, type EndpointHandlerDecoratorFactory, type ExtractConcatenatedParamNamesFromMethodFirstPath, type ExtractConcatenatedParamNamesFromPath, type ExtractConcatenatedParamNamesFromPathSegments, type ExtractEndpointFromPath, type GenericOnHandlerRegisteredCallback, type HttpMethod, type HttpMethodCallFunc, type HttpMethodCallInput, HttpMethodEndpoint, type HttpMethodEndpointHandler, type HttpMethodEndpointHandlerInput, type HttpMethodEndpointHandlerOutput, HttpMethodEndpointResponse, type HttpMethodEndpointResponses, type HttpRequestObject, type HttpResponseObject, HttpStatusCode, type IApiContractDefinition, type IClientContext, type IDecoratorHandlerSchemas, type IEndpointHandlerDecorator, type IHttpMethodEndpointDefinition, type IHttpMethodEndpointResponseDefinition, type IRegistrySettings, type InProcApiClientOptions, type LowerCasedHttpMethod, MethodEndpointHandlerRegistryEntry, type MethodFirstPath, type MiddlewareHandler, type MiddlewareHandlerContext, type MiddlewareHandlerContextOverlapGuard, type MiddlewareHandlerInput, type MiddlewareHandlerInputInternal, type MiddlewareHandlerInternal, type MiddlewareHandlerOutput, type MiddlewareHandlerSchemas, MiddlewareHandlersRegistry, MiddlewareHandlersRegistryEntry, MiddlewareHandlersRegistryEntryInternal, type MiddlewarePath, type OnHandlerRegisteredCallback, type OnMiddlewareHandlerRegisteredCallback, type PartialPath, type PartialPathResult, type PathParamFunc, type PrepareRegistryEntryCallback, RequestFailureCode, type RequestFailureErrorThrownOutput, type RequestFailureSchemaValidationFailedOutput, type StringLiteral, type TypedPathParams, type ValidateApiContractDefinition, type ValidateHttpMethodEndpointDefinition, apiContract, createClient, createInProcApiClient, createRegistry, endpoint, execHandlerChain, flatListAllRegistryEntries, isHttpResponseObject, isHttpStatusCode, isRequestFailureSchemaValidationFailedOutput, middleware, partial, partialPathString, register, response, route, triggerEndpointDecoratorNoStaticTypeCheck, triggerMiddlewareDecoratorNoStaticTypeCheck };
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import z, { z as z$1 } from 'zod';
1
+ import z, { z as z$1, treeifyError } from 'zod';
2
2
 
3
3
  type StringLiteral<T extends string> = string extends T ? never : T;
4
4
  type CapitalizedStringLiteral<T extends string> = string extends T ? never : T extends `${Uppercase<infer F>}${infer _}` ? F extends Lowercase<F> ? never : T : `❌ ERROR: Must start with uppercase letter`;
@@ -16,6 +16,9 @@ declare class DIContainerBase<R extends DIRegistry> {
16
16
  }
17
17
  declare class DIContainer<R extends DIRegistry = {}> extends DIContainerBase<R> {
18
18
  register<K extends string, T>(serviceNameCapitalizedLiteral: CapitalizedStringLiteral<K>, factory: (scope: DIScope<R>) => T, lifetime: DILifetime): DIContainer<R & Record<K, DIRegistryEntry<T>>>;
19
+ registerTransient<K extends string, T>(serviceNameCapitalizedLiteral: CapitalizedStringLiteral<K>, factory: (scope: DIScope<R>) => T): DIContainer<R & Record<K, DIRegistryEntry<T>>>;
20
+ registerScoped<K extends string, T>(serviceNameCapitalizedLiteral: CapitalizedStringLiteral<K>, factory: (scope: DIScope<R>) => T): DIContainer<R & Record<K, DIRegistryEntry<T>>>;
21
+ registerSingleton<K extends string, T>(serviceNameCapitalizedLiteral: CapitalizedStringLiteral<K>, factory: (scope: DIScope<R>) => T): DIContainer<R & Record<K, DIRegistryEntry<T>>>;
19
22
  createTestClone(): DIContainerTestClone<R, this>;
20
23
  }
21
24
  declare class DIContainerTestClone<R extends DIRegistry, TDIContainer extends DIContainer<R>> extends DIContainerBase<R> {
@@ -145,9 +148,30 @@ interface ICanTriggerAsync {
145
148
  get genericPath(): string;
146
149
  }
147
150
 
151
+ type _JoinSections<A extends string, B extends string> = [
152
+ A
153
+ ] extends [never] ? B : [B] extends [never] ? A : `${A},${B}`;
154
+ type _DefinedSection<T, K extends string> = Exclude<T, undefined> extends never ? never : K;
155
+ type _FailedValidationSectionsImpl<P extends string, H extends string, Q extends string, B extends string> = P | H | Q | B | _JoinSections<P, H> | _JoinSections<P, Q> | _JoinSections<P, B> | _JoinSections<H, Q> | _JoinSections<H, B> | _JoinSections<Q, B> | _JoinSections<_JoinSections<P, H>, Q> | _JoinSections<_JoinSections<P, H>, B> | _JoinSections<_JoinSections<P, Q>, B> | _JoinSections<_JoinSections<H, Q>, B> | _JoinSections<_JoinSections<_JoinSections<P, H>, Q>, B>;
156
+ type FailedValidationSections<T extends {
157
+ headers?: any;
158
+ query?: any;
159
+ body?: any;
160
+ }> = _FailedValidationSectionsImpl<'path-segments', _DefinedSection<T['headers'], 'headers'>, _DefinedSection<T['query'], 'query'>, _DefinedSection<T['body'], 'body'>>;
161
+
148
162
  type HttpMethodEndpointHandlerOutput<TEndpointDefinition extends IHttpMethodEndpointDefinition> = {
149
163
  [THttpStatusCode in keyof TEndpointDefinition['responses'] & HttpStatusCode]: TEndpointDefinition['responses'][THttpStatusCode] extends HttpMethodEndpointResponse<THttpStatusCode, infer TRespDef> ? CreateHandlerOutput<THttpStatusCode, TRespDef> : never;
150
- }[keyof TEndpointDefinition['responses'] & HttpStatusCode];
164
+ }[keyof TEndpointDefinition['responses'] & HttpStatusCode] | {
165
+ code: HttpStatusCode.BadRequest_400;
166
+ headers: {
167
+ "x-failed-validation-sections": FailedValidationSections<TEndpointDefinition>;
168
+ };
169
+ body: ReturnType<typeof treeifyError<Exclude<TEndpointDefinition['headers'] | TEndpointDefinition['query'] | TEndpointDefinition['body'], undefined>>>;
170
+ } | {
171
+ code: HttpStatusCode.InternalServerError_500;
172
+ headers?: unknown;
173
+ body?: {};
174
+ };
151
175
  type CreateHandlerOutput<THttpStatusCode extends HttpStatusCode, TRespDef> = TRespDef extends {
152
176
  headers: z$1.ZodType;
153
177
  body: z$1.ZodType;
@@ -182,10 +206,6 @@ type HttpResponseObject = {
182
206
  headers?: any;
183
207
  body?: any;
184
208
  };
185
- type BadRequestValidationErrorResponse = {
186
- code: HttpStatusCode.BadRequest_400;
187
- body: z$1.core.$ZodIssue[] | string;
188
- };
189
209
  declare function isHttpResponseObject(obj: any): obj is HttpResponseObject;
190
210
 
191
211
  declare const __overlap__error__: unique symbol;
@@ -211,8 +231,29 @@ type DecoratorHandlerContext = {
211
231
  originalRequest: any;
212
232
  };
213
233
 
234
+ type InferInputProp<T extends IHttpMethodEndpointDefinition, P extends keyof T> = T[P] extends z$1.ZodType<any, any> ? Record<P, z$1.input<T[P]>> : {};
235
+ type Merge3<A, B, C> = A & B & C;
236
+ type HttpMethodCallInput<T extends IHttpMethodEndpointDefinition> = Merge3<InferInputProp<T, "headers">, InferInputProp<T, "query">, InferInputProp<T, "body">> extends infer M ? keyof M extends never ? never : M : never;
237
+ declare enum RequestFailureCode {
238
+ ErrorThrown = -1,
239
+ SchemaValidationFailed = -2
240
+ }
241
+ type RequestFailureErrorThrownOutput = {
242
+ code: RequestFailureCode.ErrorThrown;
243
+ body: Error;
244
+ };
245
+ type RequestFailureSchemaValidationFailedOutput<TEndpointDefinition extends IHttpMethodEndpointDefinition> = {
246
+ code: RequestFailureCode.SchemaValidationFailed;
247
+ headers: {
248
+ "x-failed-validation-sections": FailedValidationSections<TEndpointDefinition>;
249
+ };
250
+ body: ReturnType<typeof treeifyError<Exclude<TEndpointDefinition['headers'] | TEndpointDefinition['query'] | TEndpointDefinition['body'], undefined>>>;
251
+ };
252
+ declare function isRequestFailureSchemaValidationFailedOutput<TEndpointDefinition extends IHttpMethodEndpointDefinition>(output: any): output is RequestFailureSchemaValidationFailedOutput<TEndpointDefinition>;
253
+ type HttpMethodCallFunc<TEndpointDefinition extends IHttpMethodEndpointDefinition> = HttpMethodCallInput<TEndpointDefinition> extends never ? () => Promise<HttpMethodEndpointHandlerOutput<TEndpointDefinition>> : (input: HttpMethodCallInput<TEndpointDefinition>) => Promise<HttpMethodEndpointHandlerOutput<TEndpointDefinition> | RequestFailureErrorThrownOutput | RequestFailureSchemaValidationFailedOutput<TEndpointDefinition>>;
254
+
214
255
  type HttpMethodEndpointHandler<TDef extends IHttpMethodEndpointDefinition, TPathParams extends string, TInjected> = (input: HttpMethodEndpointHandlerInput<TDef, TPathParams>, context: EndpointHandlerContext<TInjected>) => Promise<HttpMethodEndpointHandlerOutput<TDef>>;
215
- type ClientHttpMethodEndpointHandler = (input: ClientHttpMethodEndpointHandlerInput) => Promise<ClientHttpMethodEndpointHandlerOutput>;
256
+ type ClientHttpMethodEndpointHandler = (input: ClientHttpMethodEndpointHandlerInput) => Promise<ClientHttpMethodEndpointHandlerOutput | RequestFailureErrorThrownOutput>;
216
257
 
217
258
  type MiddlewareHandlerSchemas = {
218
259
  headers?: z$1.ZodType;
@@ -232,7 +273,17 @@ type MiddlewareHandlerInput<TPathParams extends string, TMiddlewareSchemas exten
232
273
  };
233
274
  type MiddlewareHandlerOutput<TMiddlewareSchemas extends MiddlewareHandlerSchemas> = void | {
234
275
  [THttpStatusCode in keyof TMiddlewareSchemas['responses'] & HttpStatusCode]: TMiddlewareSchemas['responses'][THttpStatusCode] extends HttpMethodEndpointResponse<THttpStatusCode, infer TRespDef> ? CreateHandlerOutput<THttpStatusCode, TRespDef> : never;
235
- }[keyof TMiddlewareSchemas['responses'] & HttpStatusCode];
276
+ }[keyof TMiddlewareSchemas['responses'] & HttpStatusCode] | {
277
+ code: HttpStatusCode.BadRequest_400;
278
+ headers: {
279
+ "x-failed-validation-sections": FailedValidationSections<TMiddlewareSchemas>;
280
+ };
281
+ body: ReturnType<typeof treeifyError<Exclude<TMiddlewareSchemas['headers'] | TMiddlewareSchemas['query'] | TMiddlewareSchemas['body'], undefined>>>;
282
+ } | {
283
+ code: HttpStatusCode.InternalServerError_500;
284
+ headers?: unknown;
285
+ body?: unknown;
286
+ };
236
287
  type MiddlewareHandlerInputInternal = {
237
288
  method: HttpMethod;
238
289
  pathSegments: readonly string[];
@@ -342,7 +393,17 @@ type DecoratorHandlerInput<TDecoratorSchemas extends IDecoratorHandlerSchemas> =
342
393
  };
343
394
  type DecoratorHandlerOutput<TDecoratorSchemas extends IDecoratorHandlerSchemas> = void | {
344
395
  [THttpStatusCode in keyof TDecoratorSchemas['responses'] & HttpStatusCode]: TDecoratorSchemas['responses'][THttpStatusCode] extends HttpMethodEndpointResponse<THttpStatusCode, infer TRespDef> ? CreateHandlerOutput<THttpStatusCode, TRespDef> : never;
345
- }[keyof TDecoratorSchemas['responses'] & HttpStatusCode];
396
+ }[keyof TDecoratorSchemas['responses'] & HttpStatusCode] | {
397
+ code: HttpStatusCode.BadRequest_400;
398
+ headers: {
399
+ "x-failed-validation-sections": FailedValidationSections<TDecoratorSchemas>;
400
+ };
401
+ body: ReturnType<typeof treeifyError<Exclude<TDecoratorSchemas['headers'] | TDecoratorSchemas['query'] | TDecoratorSchemas['body'], undefined>>>;
402
+ } | {
403
+ code: HttpStatusCode.InternalServerError_500;
404
+ headers?: unknown;
405
+ body?: unknown;
406
+ };
346
407
  interface IEndpointHandlerDecorator<TDecoratorSchemas extends IDecoratorHandlerSchemas> {
347
408
  handle(input: DecoratorHandlerInput<TDecoratorSchemas>, context: DecoratorHandlerContext): Promise<DecoratorHandlerOutput<TDecoratorSchemas>>;
348
409
  }
@@ -417,7 +478,7 @@ declare class MethodEndpointHandlerRegistryEntry<TDef extends IHttpMethodEndpoin
417
478
  pathParams: TypedPathParams<TPathParams>;
418
479
  query: TDef['query'] extends z.ZodType ? z.output<TDef['query']> : null;
419
480
  body: TDef['body'] extends z.ZodType ? z.output<TDef['body']> : null;
420
- }): Promise<HttpMethodEndpointHandlerOutput<TDef> | BadRequestValidationErrorResponse>;
481
+ }): Promise<HttpMethodEndpointHandlerOutput<TDef>>;
421
482
  triggerNoStaticTypeCheck(diScope: DIScope<any>, requestObject: HttpRequestObject, context: EndpointHandlerContext<any>): Promise<any>;
422
483
  }
423
484
 
@@ -484,11 +545,6 @@ declare function register<const TDef extends IHttpMethodEndpointDefinition & Val
484
545
 
485
546
  declare function execHandlerChain(diScope: DIScope<any>, allHandlerEntries: ICanTriggerAsync[], input: ClientHttpMethodEndpointHandlerInput): Promise<any>;
486
547
 
487
- type InferInputProp<T extends IHttpMethodEndpointDefinition, P extends keyof T> = T[P] extends z$1.ZodType<any, any> ? Record<P, z$1.input<T[P]>> : {};
488
- type Merge3<A, B, C> = A & B & C;
489
- type HttpMethodCallInput<T extends IHttpMethodEndpointDefinition> = Merge3<InferInputProp<T, "headers">, InferInputProp<T, "query">, InferInputProp<T, "body">> extends infer M ? keyof M extends never ? never : M : never;
490
- type HttpMethodCallFunc<T extends IHttpMethodEndpointDefinition> = HttpMethodCallInput<T> extends never ? () => Promise<HttpMethodEndpointHandlerOutput<T>> : (input: HttpMethodCallInput<T>) => Promise<HttpMethodEndpointHandlerOutput<T>>;
491
-
492
548
  declare function createClient<TDef extends IApiContractDefinition & ValidateApiContractDefinition<TDef>>(contract: ApiContract<TDef>, clientGenericHandler: ClientHttpMethodEndpointHandler): ApiClient<TDef>;
493
549
  type PathParamFunc<TDef> = (value: string | number) => TDef;
494
550
  interface IClientContext {
@@ -512,4 +568,4 @@ interface InProcApiClientOptions<TDef extends IApiContractDefinition & ValidateA
512
568
  }
513
569
  declare function createInProcApiClient<TDef extends IApiContractDefinition & ValidateApiContractDefinition<TDef>, TDIContainer extends DIContainer, TDIContainerTestClone extends DIContainerTestClone<any, TDIContainer>>(contract: ApiContract<TDef>, testContainer: TDIContainerTestClone, registry: ApiHandlersRegistry<TDef, TDIContainer>, options?: InProcApiClientOptions<TDef, TDIContainer>): ApiClient<TDef>;
514
570
 
515
- export { ApiClient, type ApiClientDef, ApiContract, ApiHandlersRegistry, type ApiHandlersRegistryDef, type BadRequestValidationErrorResponse, type CapitalizedStringLiteral, type ClientHttpMethodEndpointHandler, type ClientHttpMethodEndpointHandlerInput, type ClientHttpMethodEndpointHandlerOutput, type CreateHandlerOutput, DIContainer, DIContainerBase, DIContainerTestClone, type DILifetime, type DIRegistry, type DIRegistryEntry, type DIScope, type DecoratorHandlerContext, type DecoratorHandlerInput, type DecoratorHandlerOutput, type EndpointHandlerContext, type EndpointHandlerContextOverlapGuard, type EndpointHandlerDecoratorFactory, type ExtractConcatenatedParamNamesFromMethodFirstPath, type ExtractConcatenatedParamNamesFromPath, type ExtractConcatenatedParamNamesFromPathSegments, type ExtractEndpointFromPath, type GenericOnHandlerRegisteredCallback, type HttpMethod, type HttpMethodCallFunc, type HttpMethodCallInput, HttpMethodEndpoint, type HttpMethodEndpointHandler, type HttpMethodEndpointHandlerInput, type HttpMethodEndpointHandlerOutput, HttpMethodEndpointResponse, type HttpMethodEndpointResponses, type HttpRequestObject, type HttpResponseObject, HttpStatusCode, type IApiContractDefinition, type IClientContext, type IDecoratorHandlerSchemas, type IEndpointHandlerDecorator, type IHttpMethodEndpointDefinition, type IHttpMethodEndpointResponseDefinition, type IRegistrySettings, type InProcApiClientOptions, type LowerCasedHttpMethod, MethodEndpointHandlerRegistryEntry, type MethodFirstPath, type MiddlewareHandler, type MiddlewareHandlerContext, type MiddlewareHandlerContextOverlapGuard, type MiddlewareHandlerInput, type MiddlewareHandlerInputInternal, type MiddlewareHandlerInternal, type MiddlewareHandlerOutput, type MiddlewareHandlerSchemas, MiddlewareHandlersRegistry, MiddlewareHandlersRegistryEntry, MiddlewareHandlersRegistryEntryInternal, type MiddlewarePath, type OnHandlerRegisteredCallback, type OnMiddlewareHandlerRegisteredCallback, type PartialPath, type PartialPathResult, type PathParamFunc, type PrepareRegistryEntryCallback, type StringLiteral, type TypedPathParams, type ValidateApiContractDefinition, type ValidateHttpMethodEndpointDefinition, apiContract, createClient, createInProcApiClient, createRegistry, endpoint, execHandlerChain, flatListAllRegistryEntries, isHttpResponseObject, isHttpStatusCode, middleware, partial, partialPathString, register, response, route, triggerEndpointDecoratorNoStaticTypeCheck, triggerMiddlewareDecoratorNoStaticTypeCheck };
571
+ export { ApiClient, type ApiClientDef, ApiContract, ApiHandlersRegistry, type ApiHandlersRegistryDef, type CapitalizedStringLiteral, type ClientHttpMethodEndpointHandler, type ClientHttpMethodEndpointHandlerInput, type ClientHttpMethodEndpointHandlerOutput, type CreateHandlerOutput, DIContainer, DIContainerBase, DIContainerTestClone, type DILifetime, type DIRegistry, type DIRegistryEntry, type DIScope, type DecoratorHandlerContext, type DecoratorHandlerInput, type DecoratorHandlerOutput, type EndpointHandlerContext, type EndpointHandlerContextOverlapGuard, type EndpointHandlerDecoratorFactory, type ExtractConcatenatedParamNamesFromMethodFirstPath, type ExtractConcatenatedParamNamesFromPath, type ExtractConcatenatedParamNamesFromPathSegments, type ExtractEndpointFromPath, type GenericOnHandlerRegisteredCallback, type HttpMethod, type HttpMethodCallFunc, type HttpMethodCallInput, HttpMethodEndpoint, type HttpMethodEndpointHandler, type HttpMethodEndpointHandlerInput, type HttpMethodEndpointHandlerOutput, HttpMethodEndpointResponse, type HttpMethodEndpointResponses, type HttpRequestObject, type HttpResponseObject, HttpStatusCode, type IApiContractDefinition, type IClientContext, type IDecoratorHandlerSchemas, type IEndpointHandlerDecorator, type IHttpMethodEndpointDefinition, type IHttpMethodEndpointResponseDefinition, type IRegistrySettings, type InProcApiClientOptions, type LowerCasedHttpMethod, MethodEndpointHandlerRegistryEntry, type MethodFirstPath, type MiddlewareHandler, type MiddlewareHandlerContext, type MiddlewareHandlerContextOverlapGuard, type MiddlewareHandlerInput, type MiddlewareHandlerInputInternal, type MiddlewareHandlerInternal, type MiddlewareHandlerOutput, type MiddlewareHandlerSchemas, MiddlewareHandlersRegistry, MiddlewareHandlersRegistryEntry, MiddlewareHandlersRegistryEntryInternal, type MiddlewarePath, type OnHandlerRegisteredCallback, type OnMiddlewareHandlerRegisteredCallback, type PartialPath, type PartialPathResult, type PathParamFunc, type PrepareRegistryEntryCallback, RequestFailureCode, type RequestFailureErrorThrownOutput, type RequestFailureSchemaValidationFailedOutput, type StringLiteral, type TypedPathParams, type ValidateApiContractDefinition, type ValidateHttpMethodEndpointDefinition, apiContract, createClient, createInProcApiClient, createRegistry, endpoint, execHandlerChain, flatListAllRegistryEntries, isHttpResponseObject, isHttpStatusCode, isRequestFailureSchemaValidationFailedOutput, middleware, partial, partialPathString, register, response, route, triggerEndpointDecoratorNoStaticTypeCheck, triggerMiddlewareDecoratorNoStaticTypeCheck };
package/dist/index.mjs CHANGED
@@ -1,3 +1,6 @@
1
+ import { treeifyError } from 'zod';
2
+ import { treeifyError as treeifyError$1 } from 'zod/v4/mini';
3
+
1
4
  function endpoint(definition) {
2
5
  return new HttpMethodEndpoint(definition);
3
6
  }
@@ -319,9 +322,19 @@ function parseRequestDefinitionField(definition, key, requestObject) {
319
322
  }
320
323
  break;
321
324
  }
325
+ const errors = [`'${key}' is required for this endpoint`];
326
+ if (key === "body") {
327
+ errors.push("{ 'Content-Type': 'application/json' } header might be missing");
328
+ }
322
329
  return {
323
330
  code: HttpStatusCode.BadRequest_400,
324
- body: `'${key}' is required for this endpoint` + (key === "body" ? ", { 'Content-Type': 'application/json' } header might be missing" : "")
331
+ headers: {
332
+ "x-failed-validation-sections": key
333
+ },
334
+ body: {
335
+ errors
336
+ // treeifyError return type like structure, but here we just return simple error messages
337
+ }
325
338
  };
326
339
  }
327
340
  return result2.data;
@@ -330,7 +343,10 @@ function parseRequestDefinitionField(definition, key, requestObject) {
330
343
  if (!result.success) {
331
344
  return {
332
345
  code: HttpStatusCode.BadRequest_400,
333
- body: result.error.issues
346
+ headers: {
347
+ "x-failed-validation-sections": key
348
+ },
349
+ body: treeifyError(result.error)
334
350
  };
335
351
  }
336
352
  return result.data;
@@ -561,9 +577,19 @@ function middlewareParseRequestDefinitionField(middlewareSchemas, key, requestOb
561
577
  }
562
578
  break;
563
579
  }
580
+ const errors = [`'${key}' is required for this endpoint`];
581
+ if (key === "body") {
582
+ errors.push("{ 'Content-Type': 'application/json' } header might be missing");
583
+ }
564
584
  return {
565
585
  code: HttpStatusCode.BadRequest_400,
566
- body: `'${key}' is required for this endpoint` + (key === "body" ? ", { 'Content-Type': 'application/json' } header might be missing" : "")
586
+ headers: {
587
+ "x-failed-validation-sections": key
588
+ },
589
+ body: {
590
+ errors
591
+ // treeifyError return type like structure, but here we just return simple error messages
592
+ }
567
593
  };
568
594
  }
569
595
  return result2.data;
@@ -572,7 +598,10 @@ function middlewareParseRequestDefinitionField(middlewareSchemas, key, requestOb
572
598
  if (!result.success) {
573
599
  return {
574
600
  code: HttpStatusCode.BadRequest_400,
575
- body: result.error.issues
601
+ headers: {
602
+ "x-failed-validation-sections": key
603
+ },
604
+ body: treeifyError(result.error)
576
605
  };
577
606
  }
578
607
  return result.data;
@@ -738,6 +767,15 @@ function partialPathString(_apiReg, path) {
738
767
  return path;
739
768
  }
740
769
 
770
+ var RequestFailureCode = /* @__PURE__ */ ((RequestFailureCode2) => {
771
+ RequestFailureCode2[RequestFailureCode2["ErrorThrown"] = -1] = "ErrorThrown";
772
+ RequestFailureCode2[RequestFailureCode2["SchemaValidationFailed"] = -2] = "SchemaValidationFailed";
773
+ return RequestFailureCode2;
774
+ })(RequestFailureCode || {});
775
+ function isRequestFailureSchemaValidationFailedOutput(output) {
776
+ return typeof output === "object" && output !== null && "code" in output && output.code === -2 /* SchemaValidationFailed */;
777
+ }
778
+
741
779
  function createClient(contract, clientGenericHandler) {
742
780
  const apiClient = new ApiClient(contract, clientGenericHandler);
743
781
  return apiClient;
@@ -781,9 +819,22 @@ class InnerApiClient {
781
819
  currObj[key] = (requestObject) => {
782
820
  const pathParams = { ...client.__CONTEXT__.pathParameters };
783
821
  client.__CONTEXT__ = InnerApiClient._initNewContext();
822
+ let schemaValidationRequestFailureOutput = null;
784
823
  const headers = clientParseRequestDefinitionField(val.definition, "headers", requestObject);
824
+ if (isRequestFailureSchemaValidationFailedOutput(headers)) {
825
+ schemaValidationRequestFailureOutput = headers;
826
+ return schemaValidationRequestFailureOutput;
827
+ }
785
828
  const query = clientParseRequestDefinitionField(val.definition, "query", requestObject);
829
+ if (isRequestFailureSchemaValidationFailedOutput(query)) {
830
+ schemaValidationRequestFailureOutput = query;
831
+ return schemaValidationRequestFailureOutput;
832
+ }
786
833
  const body = clientParseRequestDefinitionField(val.definition, "body", requestObject);
834
+ if (isRequestFailureSchemaValidationFailedOutput(body)) {
835
+ schemaValidationRequestFailureOutput = body;
836
+ return schemaValidationRequestFailureOutput;
837
+ }
787
838
  const path = `/${val.pathSegments.map(
788
839
  (segment) => segment.startsWith(":") ? pathParams[segment.slice(1)] ?? "?" : segment
789
840
  ).join("/")}`;
@@ -822,13 +873,32 @@ function clientParseRequestDefinitionField(definition, key, requestObject) {
822
873
  }
823
874
  break;
824
875
  }
825
- throw new Error(`'${key}' is required for this endpoint`);
876
+ const errors = [`'${key}' is required for this endpoint`];
877
+ if (key === "body") {
878
+ errors.push("{ 'Content-Type': 'application/json' } header might be missing");
879
+ }
880
+ return {
881
+ code: RequestFailureCode.SchemaValidationFailed,
882
+ headers: {
883
+ "x-failed-validation-sections": key
884
+ },
885
+ body: {
886
+ errors
887
+ // treeifyError return type like structure, but here we just return simple error messages
888
+ }
889
+ };
826
890
  }
827
891
  return result2.data;
828
892
  }
829
893
  const result = definition[key].safeParse(requestObject[key]);
830
894
  if (!result.success) {
831
- throw new Error(`Validation for '${key}' failed`, { cause: result.error });
895
+ return {
896
+ code: RequestFailureCode.SchemaValidationFailed,
897
+ headers: {
898
+ "x-failed-validation-sections": key
899
+ },
900
+ body: treeifyError$1(result.error)
901
+ };
832
902
  }
833
903
  return result.data;
834
904
  }
@@ -903,6 +973,15 @@ class DIContainer extends DIContainerBase {
903
973
  this._map.set(serviceNameCapitalizedLiteral, entry);
904
974
  return this;
905
975
  }
976
+ registerTransient(serviceNameCapitalizedLiteral, factory) {
977
+ return this.register(serviceNameCapitalizedLiteral, factory, "transient");
978
+ }
979
+ registerScoped(serviceNameCapitalizedLiteral, factory) {
980
+ return this.register(serviceNameCapitalizedLiteral, factory, "scoped");
981
+ }
982
+ registerSingleton(serviceNameCapitalizedLiteral, factory) {
983
+ return this.register(serviceNameCapitalizedLiteral, factory, "singleton");
984
+ }
906
985
  createTestClone() {
907
986
  return new DIContainerTestClone(this);
908
987
  }
@@ -1003,9 +1082,19 @@ function decoratorParseRequestDefinitionField(decoratorSchemas, key, requestObje
1003
1082
  }
1004
1083
  break;
1005
1084
  }
1085
+ const errors = [`'${key}' is required for this endpoint`];
1086
+ if (key === "body") {
1087
+ errors.push("{ 'Content-Type': 'application/json' } header might be missing");
1088
+ }
1006
1089
  return {
1007
1090
  code: HttpStatusCode.BadRequest_400,
1008
- body: `'${key}' is required for this endpoint` + (key === "body" ? ", { 'Content-Type': 'application/json' } header might be missing" : "")
1091
+ headers: {
1092
+ "x-failed-validation-sections": key
1093
+ },
1094
+ body: {
1095
+ errors
1096
+ // treeifyError return type like structure, but here we just return simple error messages
1097
+ }
1009
1098
  };
1010
1099
  }
1011
1100
  return result2.data;
@@ -1014,7 +1103,10 @@ function decoratorParseRequestDefinitionField(decoratorSchemas, key, requestObje
1014
1103
  if (!result.success) {
1015
1104
  return {
1016
1105
  code: HttpStatusCode.BadRequest_400,
1017
- body: result.error.issues
1106
+ headers: {
1107
+ "x-failed-validation-sections": key
1108
+ },
1109
+ body: treeifyError(result.error)
1018
1110
  };
1019
1111
  }
1020
1112
  return result.data;
@@ -1103,4 +1195,4 @@ function addMiddlewareDecorators(mwHandlers, mwEntry) {
1103
1195
  });
1104
1196
  }
1105
1197
 
1106
- export { ApiClient, ApiContract, ApiHandlersRegistry, DIContainer, DIContainerBase, DIContainerTestClone, HttpMethodEndpoint, HttpMethodEndpointResponse, HttpStatusCode, MethodEndpointHandlerRegistryEntry, MiddlewareHandlersRegistry, MiddlewareHandlersRegistryEntry, MiddlewareHandlersRegistryEntryInternal, apiContract, createClient, createInProcApiClient, createRegistry, endpoint, execHandlerChain, flatListAllRegistryEntries, isHttpResponseObject, isHttpStatusCode, middleware, partial, partialPathString, register, response, route, triggerEndpointDecoratorNoStaticTypeCheck, triggerMiddlewareDecoratorNoStaticTypeCheck };
1198
+ export { ApiClient, ApiContract, ApiHandlersRegistry, DIContainer, DIContainerBase, DIContainerTestClone, HttpMethodEndpoint, HttpMethodEndpointResponse, HttpStatusCode, MethodEndpointHandlerRegistryEntry, MiddlewareHandlersRegistry, MiddlewareHandlersRegistryEntry, MiddlewareHandlersRegistryEntryInternal, RequestFailureCode, apiContract, createClient, createInProcApiClient, createRegistry, endpoint, execHandlerChain, flatListAllRegistryEntries, isHttpResponseObject, isHttpStatusCode, isRequestFailureSchemaValidationFailedOutput, middleware, partial, partialPathString, register, response, route, triggerEndpointDecoratorNoStaticTypeCheck, triggerMiddlewareDecoratorNoStaticTypeCheck };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@congruent-stack/congruent-api",
3
3
  "type": "module",
4
- "version": "0.13.0",
4
+ "version": "0.14.0",
5
5
  "description": "Typescript schema-first tooling for agnostic REST APIs.",
6
6
  "keywords": [],
7
7
  "author": "congruent-stack",