@congruent-stack/congruent-api 0.12.2 → 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
  }
@@ -950,7 +1029,11 @@ async function triggerEndpointDecoratorNoStaticTypeCheck(endpoint, decorator, re
950
1029
  }
951
1030
  const path = endpoint.createPath(requestObject.pathParams);
952
1031
  return decorator.handle({
953
- ...requestObject,
1032
+ //...requestObject,
1033
+ headers,
1034
+ query,
1035
+ body,
1036
+ pathParams: requestObject.pathParams,
954
1037
  method: endpoint.method,
955
1038
  genericPath: endpoint.genericPath,
956
1039
  path,
@@ -1001,9 +1084,19 @@ function decoratorParseRequestDefinitionField(decoratorSchemas, key, requestObje
1001
1084
  }
1002
1085
  break;
1003
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
+ }
1004
1091
  return {
1005
1092
  code: HttpStatusCode.BadRequest_400,
1006
- 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
+ }
1007
1100
  };
1008
1101
  }
1009
1102
  return result2.data;
@@ -1012,7 +1105,10 @@ function decoratorParseRequestDefinitionField(decoratorSchemas, key, requestObje
1012
1105
  if (!result.success) {
1013
1106
  return {
1014
1107
  code: HttpStatusCode.BadRequest_400,
1015
- body: result.error.issues
1108
+ headers: {
1109
+ "x-failed-validation-sections": key
1110
+ },
1111
+ body: zod.treeifyError(result.error)
1016
1112
  };
1017
1113
  }
1018
1114
  return result.data;
@@ -1114,6 +1210,7 @@ exports.MethodEndpointHandlerRegistryEntry = MethodEndpointHandlerRegistryEntry;
1114
1210
  exports.MiddlewareHandlersRegistry = MiddlewareHandlersRegistry;
1115
1211
  exports.MiddlewareHandlersRegistryEntry = MiddlewareHandlersRegistryEntry;
1116
1212
  exports.MiddlewareHandlersRegistryEntryInternal = MiddlewareHandlersRegistryEntryInternal;
1213
+ exports.RequestFailureCode = RequestFailureCode;
1117
1214
  exports.apiContract = apiContract;
1118
1215
  exports.createClient = createClient;
1119
1216
  exports.createInProcApiClient = createInProcApiClient;
@@ -1123,6 +1220,7 @@ exports.execHandlerChain = execHandlerChain;
1123
1220
  exports.flatListAllRegistryEntries = flatListAllRegistryEntries;
1124
1221
  exports.isHttpResponseObject = isHttpResponseObject;
1125
1222
  exports.isHttpStatusCode = isHttpStatusCode;
1223
+ exports.isRequestFailureSchemaValidationFailedOutput = isRequestFailureSchemaValidationFailedOutput;
1126
1224
  exports.middleware = middleware;
1127
1225
  exports.partial = partial;
1128
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
  }
@@ -948,7 +1027,11 @@ async function triggerEndpointDecoratorNoStaticTypeCheck(endpoint, decorator, re
948
1027
  }
949
1028
  const path = endpoint.createPath(requestObject.pathParams);
950
1029
  return decorator.handle({
951
- ...requestObject,
1030
+ //...requestObject,
1031
+ headers,
1032
+ query,
1033
+ body,
1034
+ pathParams: requestObject.pathParams,
952
1035
  method: endpoint.method,
953
1036
  genericPath: endpoint.genericPath,
954
1037
  path,
@@ -999,9 +1082,19 @@ function decoratorParseRequestDefinitionField(decoratorSchemas, key, requestObje
999
1082
  }
1000
1083
  break;
1001
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
+ }
1002
1089
  return {
1003
1090
  code: HttpStatusCode.BadRequest_400,
1004
- 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
+ }
1005
1098
  };
1006
1099
  }
1007
1100
  return result2.data;
@@ -1010,7 +1103,10 @@ function decoratorParseRequestDefinitionField(decoratorSchemas, key, requestObje
1010
1103
  if (!result.success) {
1011
1104
  return {
1012
1105
  code: HttpStatusCode.BadRequest_400,
1013
- body: result.error.issues
1106
+ headers: {
1107
+ "x-failed-validation-sections": key
1108
+ },
1109
+ body: treeifyError(result.error)
1014
1110
  };
1015
1111
  }
1016
1112
  return result.data;
@@ -1099,4 +1195,4 @@ function addMiddlewareDecorators(mwHandlers, mwEntry) {
1099
1195
  });
1100
1196
  }
1101
1197
 
1102
- 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.12.2",
4
+ "version": "0.14.0",
5
5
  "description": "Typescript schema-first tooling for agnostic REST APIs.",
6
6
  "keywords": [],
7
7
  "author": "congruent-stack",