@grupodiariodaregiao/bunstone 0.2.7 → 0.2.9

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.js CHANGED
@@ -67726,6 +67726,85 @@ var ZodRealError = $constructor("ZodError", initializer2, {
67726
67726
  Parent: Error
67727
67727
  });
67728
67728
 
67729
+ // lib/http-exceptions.ts
67730
+ class HttpException extends Error {
67731
+ status;
67732
+ response;
67733
+ constructor(response, status) {
67734
+ const responseObj = typeof response === "string" ? { message: response } : response;
67735
+ super(JSON.stringify(responseObj));
67736
+ this.status = status;
67737
+ this.response = responseObj;
67738
+ Object.setPrototypeOf(this, HttpException.prototype);
67739
+ }
67740
+ getResponse() {
67741
+ return this.response;
67742
+ }
67743
+ getStatus() {
67744
+ return this.status;
67745
+ }
67746
+ }
67747
+
67748
+ class BadRequestException extends HttpException {
67749
+ constructor(response = "Bad Request") {
67750
+ super(response, 400);
67751
+ }
67752
+ }
67753
+
67754
+ class UnauthorizedException extends HttpException {
67755
+ constructor(response = "Unauthorized") {
67756
+ super(response, 401);
67757
+ }
67758
+ }
67759
+
67760
+ class ForbiddenException extends HttpException {
67761
+ constructor(response = "Forbidden") {
67762
+ super(response, 403);
67763
+ }
67764
+ }
67765
+
67766
+ class NotFoundException extends HttpException {
67767
+ constructor(response = "Not Found") {
67768
+ super(response, 404);
67769
+ }
67770
+ }
67771
+
67772
+ class ConflictException extends HttpException {
67773
+ constructor(response = "Conflict") {
67774
+ super(response, 409);
67775
+ }
67776
+ }
67777
+
67778
+ class UnprocessableEntityException extends HttpException {
67779
+ constructor(response = "Unprocessable Entity") {
67780
+ super(response, 422);
67781
+ }
67782
+ }
67783
+
67784
+ class InternalServerErrorException extends HttpException {
67785
+ constructor(response = "Internal Server Error") {
67786
+ super(response, 500);
67787
+ }
67788
+ }
67789
+
67790
+ class OkResponse extends HttpException {
67791
+ constructor(response = "OK") {
67792
+ super(response, 200);
67793
+ }
67794
+ }
67795
+
67796
+ class CreatedResponse extends HttpException {
67797
+ constructor(response = "Created") {
67798
+ super(response, 201);
67799
+ }
67800
+ }
67801
+
67802
+ class NoContentResponse extends HttpException {
67803
+ constructor() {
67804
+ super("", 204);
67805
+ }
67806
+ }
67807
+
67729
67808
  // lib/utils/is-zod-schema.ts
67730
67809
  var isZodSchema = (obj) => {
67731
67810
  return obj && typeof obj === "object" && typeof obj.parse === "function";
@@ -67852,8 +67931,15 @@ async function processParameters(request, target, propertyKey) {
67852
67931
  }
67853
67932
  } catch (e2) {
67854
67933
  if (e2 instanceof ZodError) {
67855
- badRequest(e2.issues.map((issue) => issue.message).join(", "));
67934
+ throw new BadRequestException({
67935
+ status: 400,
67936
+ errors: e2.issues.map((issue) => ({
67937
+ field: issue.path.join("."),
67938
+ message: issue.message
67939
+ }))
67940
+ });
67856
67941
  }
67942
+ throw e2;
67857
67943
  }
67858
67944
  }
67859
67945
  return args;
@@ -67965,10 +68051,7 @@ function appendValue(formData, key, value) {
67965
68051
  }
67966
68052
  }
67967
68053
  function badRequest(message) {
67968
- throw new Response(JSON.stringify({ error: message }), {
67969
- status: 400,
67970
- headers: { "content-type": "application/json" }
67971
- });
68054
+ throw new BadRequestException(message);
67972
68055
  }
67973
68056
 
67974
68057
  // lib/adapters/form-data.ts
@@ -68283,84 +68366,6 @@ QueryBus = __legacyDecorateClassTS([
68283
68366
  Injectable()
68284
68367
  ], QueryBus);
68285
68368
 
68286
- // lib/http-exceptions.ts
68287
- class HttpException extends Error {
68288
- response;
68289
- status;
68290
- constructor(response, status) {
68291
- super(typeof response === "string" ? response : JSON.stringify(response));
68292
- this.response = response;
68293
- this.status = status;
68294
- Object.setPrototypeOf(this, HttpException.prototype);
68295
- }
68296
- getResponse() {
68297
- return this.response;
68298
- }
68299
- getStatus() {
68300
- return this.status;
68301
- }
68302
- }
68303
-
68304
- class BadRequestException extends HttpException {
68305
- constructor(response = "Bad Request") {
68306
- super(response, 400);
68307
- }
68308
- }
68309
-
68310
- class UnauthorizedException extends HttpException {
68311
- constructor(response = "Unauthorized") {
68312
- super(response, 401);
68313
- }
68314
- }
68315
-
68316
- class ForbiddenException extends HttpException {
68317
- constructor(response = "Forbidden") {
68318
- super(response, 403);
68319
- }
68320
- }
68321
-
68322
- class NotFoundException extends HttpException {
68323
- constructor(response = "Not Found") {
68324
- super(response, 404);
68325
- }
68326
- }
68327
-
68328
- class ConflictException extends HttpException {
68329
- constructor(response = "Conflict") {
68330
- super(response, 409);
68331
- }
68332
- }
68333
-
68334
- class UnprocessableEntityException extends HttpException {
68335
- constructor(response = "Unprocessable Entity") {
68336
- super(response, 422);
68337
- }
68338
- }
68339
-
68340
- class InternalServerErrorException extends HttpException {
68341
- constructor(response = "Internal Server Error") {
68342
- super(response, 500);
68343
- }
68344
- }
68345
-
68346
- class OkResponse extends HttpException {
68347
- constructor(response = "OK") {
68348
- super(response, 200);
68349
- }
68350
- }
68351
-
68352
- class CreatedResponse extends HttpException {
68353
- constructor(response = "Created") {
68354
- super(response, 201);
68355
- }
68356
- }
68357
-
68358
- class NoContentResponse extends HttpException {
68359
- constructor() {
68360
- super("", 204);
68361
- }
68362
- }
68363
-
68364
68369
  // lib/openapi.ts
68365
68370
  var import_reflect_metadata12 = __toESM(require_Reflect(), 1);
68366
68371
  var API_TAGS_METADATA = "dip:openapi:tags";
@@ -68612,11 +68617,37 @@ class AppStartup {
68612
68617
  AppStartup.elysia.error({
68613
68618
  HttpException
68614
68619
  });
68615
- AppStartup.elysia.onError(({ error, set }) => {
68620
+ AppStartup.elysia.onError(({ code: code2, error, set }) => {
68616
68621
  if (error instanceof HttpException) {
68617
68622
  set.status = error.getStatus();
68618
68623
  return error.getResponse();
68619
68624
  }
68625
+ if (code2 === "VALIDATION") {
68626
+ set.status = 400;
68627
+ const extractField = (path) => {
68628
+ if (Array.isArray(path)) {
68629
+ return path.join(".").replace(/^body\./, "").replace(/^query\./, "").replace(/^params\./, "");
68630
+ }
68631
+ if (typeof path === "string") {
68632
+ return path.replace(/^body\./, "").replace(/^query\./, "").replace(/^params\./, "");
68633
+ }
68634
+ return "";
68635
+ };
68636
+ const allErrors = error.all;
68637
+ const errors = Array.isArray(allErrors) && allErrors.length > 0 ? allErrors.map((err) => ({
68638
+ field: extractField(err.path),
68639
+ message: err.message
68640
+ })) : [
68641
+ {
68642
+ field: extractField(error.path),
68643
+ message: error.message
68644
+ }
68645
+ ];
68646
+ return {
68647
+ status: 400,
68648
+ errors
68649
+ };
68650
+ }
68620
68651
  return error;
68621
68652
  });
68622
68653
  if (options?.cors) {
@@ -2,10 +2,10 @@
2
2
  * Base class for all HTTP exceptions.
3
3
  */
4
4
  export declare class HttpException extends Error {
5
- readonly response: string | object;
6
5
  readonly status: number;
6
+ readonly response: object;
7
7
  constructor(response: string | object, status: number);
8
- getResponse(): string | object;
8
+ getResponse(): object;
9
9
  getStatus(): number;
10
10
  }
11
11
  /**
@@ -79,11 +79,52 @@ export class AppStartup {
79
79
  HttpException,
80
80
  });
81
81
 
82
- AppStartup.elysia.onError(({ error, set }) => {
82
+ AppStartup.elysia.onError(({ code, error, set }) => {
83
83
  if (error instanceof HttpException) {
84
84
  set.status = error.getStatus();
85
85
  return error.getResponse();
86
86
  }
87
+
88
+ if (code === "VALIDATION") {
89
+ set.status = 400;
90
+
91
+ const extractField = (path: any): string => {
92
+ if (Array.isArray(path)) {
93
+ return path
94
+ .join(".")
95
+ .replace(/^body\./, "")
96
+ .replace(/^query\./, "")
97
+ .replace(/^params\./, "");
98
+ }
99
+ if (typeof path === "string") {
100
+ return path
101
+ .replace(/^body\./, "")
102
+ .replace(/^query\./, "")
103
+ .replace(/^params\./, "");
104
+ }
105
+ return "";
106
+ };
107
+
108
+ const allErrors = (error as any).all;
109
+ const errors =
110
+ Array.isArray(allErrors) && allErrors.length > 0
111
+ ? allErrors.map((err: any) => ({
112
+ field: extractField(err.path),
113
+ message: err.message,
114
+ }))
115
+ : [
116
+ {
117
+ field: extractField((error as any).path),
118
+ message: error.message,
119
+ },
120
+ ];
121
+
122
+ return {
123
+ status: 400,
124
+ errors,
125
+ };
126
+ }
127
+
87
128
  return error;
88
129
  });
89
130
 
@@ -2,11 +2,16 @@
2
2
  * Base class for all HTTP exceptions.
3
3
  */
4
4
  export class HttpException extends Error {
5
+ public readonly response: object;
6
+
5
7
  constructor(
6
- public readonly response: string | object,
8
+ response: string | object,
7
9
  public readonly status: number,
8
10
  ) {
9
- super(typeof response === "string" ? response : JSON.stringify(response));
11
+ const responseObj =
12
+ typeof response === "string" ? { message: response } : response;
13
+ super(JSON.stringify(responseObj));
14
+ this.response = responseObj;
10
15
  Object.setPrototypeOf(this, HttpException.prototype);
11
16
  }
12
17
 
@@ -1,395 +1,400 @@
1
1
  import "reflect-metadata";
2
2
  import { ZodError, type ZodType } from "zod/v4";
3
3
  import { PARAM_METADATA_KEY } from "./constants";
4
+ import { BadRequestException } from "./http-exceptions";
4
5
  import { isZodSchema } from "./utils/is-zod-schema";
5
6
 
6
7
  export enum ParamType {
7
- BODY = "body",
8
- QUERY = "query",
9
- PARAM = "param",
10
- HEADER = "header",
11
- REQUEST = "request",
12
- FORM_DATA = "form-data",
8
+ BODY = "body",
9
+ QUERY = "query",
10
+ PARAM = "param",
11
+ HEADER = "header",
12
+ REQUEST = "request",
13
+ FORM_DATA = "form-data",
13
14
  }
14
15
 
15
16
  function setParamMetadata(
16
- target: any,
17
- propertyKey: string | symbol,
18
- parameterIndex: number,
19
- type: ParamType,
20
- key?: string,
21
- options?: unknown
17
+ target: any,
18
+ propertyKey: string | symbol,
19
+ parameterIndex: number,
20
+ type: ParamType,
21
+ key?: string,
22
+ options?: unknown,
22
23
  ) {
23
- const existingParams =
24
- Reflect.getOwnMetadata(PARAM_METADATA_KEY, target, propertyKey) || [];
24
+ const existingParams =
25
+ Reflect.getOwnMetadata(PARAM_METADATA_KEY, target, propertyKey) || [];
25
26
 
26
- existingParams.push({ index: parameterIndex, type, key, options });
27
+ existingParams.push({ index: parameterIndex, type, key, options });
27
28
 
28
- Reflect.defineMetadata(
29
- PARAM_METADATA_KEY,
30
- existingParams,
31
- target,
32
- propertyKey
33
- );
29
+ Reflect.defineMetadata(
30
+ PARAM_METADATA_KEY,
31
+ existingParams,
32
+ target,
33
+ propertyKey,
34
+ );
34
35
  }
35
36
 
36
37
  export function Body(schema?: ZodType): any;
37
38
  export function Body(): any {
38
- if (arguments.length === 1) {
39
- const arg = arguments[0] as ZodType;
40
- if (isZodSchema(arg)) {
41
- return (target: any, propertyKey: any, parameterIndex: any) => {
42
- setParamMetadata(
43
- target,
44
- propertyKey as string,
45
- parameterIndex,
46
- ParamType.BODY,
47
- undefined,
48
- { zodSchema: arg }
49
- );
50
- };
51
- }
52
- }
53
-
54
- return (target: any, propertyKey: any, parameterIndex: any) => {
55
- setParamMetadata(
56
- target,
57
- propertyKey as string,
58
- parameterIndex,
59
- ParamType.BODY
60
- );
61
- };
39
+ if (arguments.length === 1) {
40
+ const arg = arguments[0] as ZodType;
41
+ if (isZodSchema(arg)) {
42
+ return (target: any, propertyKey: any, parameterIndex: any) => {
43
+ setParamMetadata(
44
+ target,
45
+ propertyKey as string,
46
+ parameterIndex,
47
+ ParamType.BODY,
48
+ undefined,
49
+ { zodSchema: arg },
50
+ );
51
+ };
52
+ }
53
+ }
54
+
55
+ return (target: any, propertyKey: any, parameterIndex: any) => {
56
+ setParamMetadata(
57
+ target,
58
+ propertyKey as string,
59
+ parameterIndex,
60
+ ParamType.BODY,
61
+ );
62
+ };
62
63
  }
63
64
 
64
65
  export function Param(schema?: ZodType): any;
65
66
  export function Param(key?: string): any;
66
67
  export function Param(): any {
67
- let key: string | undefined;
68
- if (arguments.length === 1) {
69
- if (isZodSchema(arguments[0])) {
70
- return function (target: any, propertyKey: any, parameterIndex: any) {
71
- setParamMetadata(
72
- target,
73
- propertyKey as string,
74
- parameterIndex,
75
- ParamType.PARAM,
76
- undefined,
77
- {
78
- zodSchema: arguments[0] as ZodType,
79
- }
80
- );
81
- };
82
- }
83
- key = arguments[0] as string;
84
- }
85
-
86
- return (target: any, propertyKey: any, parameterIndex: any) => {
87
- setParamMetadata(
88
- target,
89
- propertyKey as string,
90
- parameterIndex,
91
- ParamType.PARAM,
92
- key
93
- );
94
- };
68
+ let key: string | undefined;
69
+ if (arguments.length === 1) {
70
+ if (isZodSchema(arguments[0])) {
71
+ return function (target: any, propertyKey: any, parameterIndex: any) {
72
+ setParamMetadata(
73
+ target,
74
+ propertyKey as string,
75
+ parameterIndex,
76
+ ParamType.PARAM,
77
+ undefined,
78
+ {
79
+ zodSchema: arguments[0] as ZodType,
80
+ },
81
+ );
82
+ };
83
+ }
84
+ key = arguments[0] as string;
85
+ }
86
+
87
+ return (target: any, propertyKey: any, parameterIndex: any) => {
88
+ setParamMetadata(
89
+ target,
90
+ propertyKey as string,
91
+ parameterIndex,
92
+ ParamType.PARAM,
93
+ key,
94
+ );
95
+ };
95
96
  }
96
97
 
97
98
  export function Query(schema?: ZodType): any;
98
99
  export function Query(key?: string): any;
99
100
  export function Query(): any {
100
- let key: string | undefined;
101
- if (arguments.length === 1) {
102
- if (isZodSchema(arguments[0])) {
103
- return function (target: any, propertyKey: any, parameterIndex: any) {
104
- setParamMetadata(
105
- target,
106
- propertyKey as string,
107
- parameterIndex,
108
- ParamType.QUERY,
109
- undefined,
110
- {
111
- zodSchema: arguments[0] as ZodType,
112
- }
113
- );
114
- };
115
- }
116
- key = arguments[0] as string;
117
- }
118
-
119
- return (target: any, propertyKey: any, parameterIndex: any) => {
120
- setParamMetadata(
121
- target,
122
- propertyKey as string,
123
- parameterIndex,
124
- ParamType.QUERY,
125
- key
126
- );
127
- };
101
+ let key: string | undefined;
102
+ if (arguments.length === 1) {
103
+ if (isZodSchema(arguments[0])) {
104
+ return function (target: any, propertyKey: any, parameterIndex: any) {
105
+ setParamMetadata(
106
+ target,
107
+ propertyKey as string,
108
+ parameterIndex,
109
+ ParamType.QUERY,
110
+ undefined,
111
+ {
112
+ zodSchema: arguments[0] as ZodType,
113
+ },
114
+ );
115
+ };
116
+ }
117
+ key = arguments[0] as string;
118
+ }
119
+
120
+ return (target: any, propertyKey: any, parameterIndex: any) => {
121
+ setParamMetadata(
122
+ target,
123
+ propertyKey as string,
124
+ parameterIndex,
125
+ ParamType.QUERY,
126
+ key,
127
+ );
128
+ };
128
129
  }
129
130
 
130
131
  export function Header(key: string): any {
131
- return (target: any, propertyKey: any, parameterIndex: any) => {
132
- setParamMetadata(
133
- target,
134
- propertyKey,
135
- parameterIndex,
136
- ParamType.HEADER,
137
- key
138
- );
139
- };
132
+ return (target: any, propertyKey: any, parameterIndex: any) => {
133
+ setParamMetadata(
134
+ target,
135
+ propertyKey,
136
+ parameterIndex,
137
+ ParamType.HEADER,
138
+ key,
139
+ );
140
+ };
140
141
  }
141
142
 
142
143
  export function Request(): any {
143
- return (target: any, propertyKey: any, parameterIndex: any) => {
144
- setParamMetadata(target, propertyKey, parameterIndex, ParamType.REQUEST);
145
- };
144
+ return (target: any, propertyKey: any, parameterIndex: any) => {
145
+ setParamMetadata(target, propertyKey, parameterIndex, ParamType.REQUEST);
146
+ };
146
147
  }
147
148
 
148
149
  export async function processParameters(
149
- request: any,
150
- target: any,
151
- propertyKey: string
150
+ request: any,
151
+ target: any,
152
+ propertyKey: string,
152
153
  ): Promise<any[]> {
153
- const paramMetadata =
154
- Reflect.getOwnMetadata(
155
- PARAM_METADATA_KEY,
156
- Object.getPrototypeOf(target),
157
- propertyKey
158
- ) || [];
159
-
160
- const paramTypes =
161
- Reflect.getMetadata("design:paramtypes", target, propertyKey) || [];
162
-
163
- const args: any[] = new Array(paramTypes.length);
164
- let cachedFormData: FormData | null = null;
165
-
166
- for (const metadata of paramMetadata) {
167
- const { index, type, key } = metadata;
168
-
169
- switch (type) {
170
- case ParamType.BODY:
171
- try {
172
- args[index] = request.body;
173
- } catch (_e) {
174
- args[index] = null;
175
- }
176
- break;
177
-
178
- case ParamType.QUERY:
179
- if (key) {
180
- args[index] = request.query?.[key];
181
- } else {
182
- args[index] = request.query;
183
- }
184
- break;
185
-
186
- case ParamType.PARAM:
187
- if (key === undefined) {
188
- args[index] = request.params;
189
- } else {
190
- args[index] = request.params?.[key];
191
- }
192
- break;
193
-
194
- case ParamType.HEADER:
195
- if (key) {
196
- args[index] = request.headers?.[key];
197
- }
198
- break;
199
-
200
- case ParamType.REQUEST:
201
- args[index] = request;
202
- break;
203
-
204
- case ParamType.FORM_DATA:
205
- cachedFormData = cachedFormData || (await readFormData(request));
206
- args[index] = extractFormDataPayload(
207
- cachedFormData,
208
- metadata.options as FormDataOptions | undefined
209
- );
210
- break;
211
- }
212
-
213
- try {
214
- if (metadata.options?.zodSchema) {
215
- const zodSchema = metadata.options.zodSchema as ZodType;
216
- if (isZodSchema(zodSchema)) {
217
- args[index] = zodSchema.parse(args[index]);
218
- }
219
- }
220
- } catch (e) {
221
- if (e instanceof ZodError) {
222
- badRequest(e.issues.map((issue) => issue.message).join(", "));
223
- }
224
- }
225
- }
226
-
227
- return args;
154
+ const paramMetadata =
155
+ Reflect.getOwnMetadata(
156
+ PARAM_METADATA_KEY,
157
+ Object.getPrototypeOf(target),
158
+ propertyKey,
159
+ ) || [];
160
+
161
+ const paramTypes =
162
+ Reflect.getMetadata("design:paramtypes", target, propertyKey) || [];
163
+
164
+ const args: any[] = new Array(paramTypes.length);
165
+ let cachedFormData: FormData | null = null;
166
+
167
+ for (const metadata of paramMetadata) {
168
+ const { index, type, key } = metadata;
169
+
170
+ switch (type) {
171
+ case ParamType.BODY:
172
+ try {
173
+ args[index] = request.body;
174
+ } catch (_e) {
175
+ args[index] = null;
176
+ }
177
+ break;
178
+
179
+ case ParamType.QUERY:
180
+ if (key) {
181
+ args[index] = request.query?.[key];
182
+ } else {
183
+ args[index] = request.query;
184
+ }
185
+ break;
186
+
187
+ case ParamType.PARAM:
188
+ if (key === undefined) {
189
+ args[index] = request.params;
190
+ } else {
191
+ args[index] = request.params?.[key];
192
+ }
193
+ break;
194
+
195
+ case ParamType.HEADER:
196
+ if (key) {
197
+ args[index] = request.headers?.[key];
198
+ }
199
+ break;
200
+
201
+ case ParamType.REQUEST:
202
+ args[index] = request;
203
+ break;
204
+
205
+ case ParamType.FORM_DATA:
206
+ cachedFormData = cachedFormData || (await readFormData(request));
207
+ args[index] = extractFormDataPayload(
208
+ cachedFormData,
209
+ metadata.options as FormDataOptions | undefined,
210
+ );
211
+ break;
212
+ }
213
+
214
+ try {
215
+ if (metadata.options?.zodSchema) {
216
+ const zodSchema = metadata.options.zodSchema as ZodType;
217
+ if (isZodSchema(zodSchema)) {
218
+ args[index] = zodSchema.parse(args[index]);
219
+ }
220
+ }
221
+ } catch (e) {
222
+ if (e instanceof ZodError) {
223
+ throw new BadRequestException({
224
+ status: 400,
225
+ errors: e.issues.map((issue) => ({
226
+ field: issue.path.join("."),
227
+ message: issue.message,
228
+ })),
229
+ });
230
+ }
231
+ throw e;
232
+ }
233
+ }
234
+
235
+ return args;
228
236
  }
229
237
 
230
238
  export type FormDataOptions = {
231
- fileField?: string;
232
- allowedTypes?: string[];
233
- jsonField?: string;
239
+ fileField?: string;
240
+ allowedTypes?: string[];
241
+ jsonField?: string;
234
242
  };
235
243
 
236
244
  export type FormDataFields = Record<string, string | string[]>;
237
245
 
238
246
  export type FormDataPayload = {
239
- files: File[];
240
- json?: unknown;
247
+ files: File[];
248
+ json?: unknown;
241
249
  };
242
250
 
243
251
  const FORM_DATA_CACHE = Symbol.for("dip:form-data-cache");
244
252
 
245
253
  async function readFormData(request: any): Promise<FormData> {
246
- if (request?.[FORM_DATA_CACHE]) {
247
- return request[FORM_DATA_CACHE];
248
- }
249
-
250
- const existingBody = request?.body;
251
- const bodyAsFormData = tryResolveFromBody(existingBody);
252
- if (bodyAsFormData) {
253
- request[FORM_DATA_CACHE] = bodyAsFormData;
254
- return bodyAsFormData;
255
- }
256
-
257
- const requestLike = request?.request || request?.raw || request;
258
-
259
- if (!requestLike || typeof requestLike.formData !== "function") {
260
- throw new Error("FormData is not available on this request.");
261
- }
262
-
263
- let formData: FormData;
264
- try {
265
- formData =
266
- typeof requestLike.clone === "function"
267
- ? await requestLike.clone().formData()
268
- : await requestLike.formData();
269
- } catch (err: any) {
270
- const fallback = tryResolveFromBody(existingBody);
271
- if (fallback) {
272
- request[FORM_DATA_CACHE] = fallback;
273
- return fallback;
274
- }
275
-
276
- const reason =
277
- err instanceof Error
278
- ? err.message
279
- : "Body already consumed or unreadable";
280
- throw new Error(
281
- `Could not read multipart form data from the request. ${reason}`
282
- );
283
- }
284
-
285
- if (!(formData instanceof FormData)) {
286
- throw new Error("Could not read multipart form data from the request.");
287
- }
288
-
289
- request[FORM_DATA_CACHE] = formData;
290
- return formData;
254
+ if (request?.[FORM_DATA_CACHE]) {
255
+ return request[FORM_DATA_CACHE];
256
+ }
257
+
258
+ const existingBody = request?.body;
259
+ const bodyAsFormData = tryResolveFromBody(existingBody);
260
+ if (bodyAsFormData) {
261
+ request[FORM_DATA_CACHE] = bodyAsFormData;
262
+ return bodyAsFormData;
263
+ }
264
+
265
+ const requestLike = request?.request || request?.raw || request;
266
+
267
+ if (!requestLike || typeof requestLike.formData !== "function") {
268
+ throw new Error("FormData is not available on this request.");
269
+ }
270
+
271
+ let formData: FormData;
272
+ try {
273
+ formData =
274
+ typeof requestLike.clone === "function"
275
+ ? await requestLike.clone().formData()
276
+ : await requestLike.formData();
277
+ } catch (err: any) {
278
+ const fallback = tryResolveFromBody(existingBody);
279
+ if (fallback) {
280
+ request[FORM_DATA_CACHE] = fallback;
281
+ return fallback;
282
+ }
283
+
284
+ const reason =
285
+ err instanceof Error
286
+ ? err.message
287
+ : "Body already consumed or unreadable";
288
+ throw new Error(
289
+ `Could not read multipart form data from the request. ${reason}`,
290
+ );
291
+ }
292
+
293
+ if (!(formData instanceof FormData)) {
294
+ throw new Error("Could not read multipart form data from the request.");
295
+ }
296
+
297
+ request[FORM_DATA_CACHE] = formData;
298
+ return formData;
291
299
  }
292
300
 
293
301
  function extractFormDataPayload(
294
- formData: FormData,
295
- options: FormDataOptions = {}
302
+ formData: FormData,
303
+ options: FormDataOptions = {},
296
304
  ): FormDataPayload {
297
- const { fileField, allowedTypes, jsonField } = options;
298
- const files: File[] = [];
299
-
300
- const allowed = (allowedTypes || []).map((item) => item.toLowerCase());
301
- const getFiles = fileField
302
- ? formData.getAll(fileField)
303
- : Array.from(formData.values());
304
-
305
- for (const value of getFiles) {
306
- if (value instanceof File) {
307
- if (allowed.length > 0 && !isAllowedFileType(value, allowed)) {
308
- badRequest(
309
- `File type for "${
310
- value.name
311
- }" is not allowed. Allowed: ${allowed.join(", ")}`
312
- );
313
- }
314
-
315
- files.push(value);
316
- }
317
- }
318
-
319
- let parsedJson: unknown;
320
-
321
- if (jsonField) {
322
- const rawJson = formData.get(jsonField);
323
-
324
- if (typeof rawJson === "string") {
325
- try {
326
- parsedJson = JSON.parse(rawJson);
327
- } catch {
328
- badRequest(`Failed to parse JSON field "${jsonField}".`);
329
- }
330
- } else if (rawJson !== null) {
331
- badRequest(`JSON field "${jsonField}" must be a string value.`);
332
- }
333
- }
334
-
335
- return {
336
- files,
337
- json: parsedJson,
338
- };
305
+ const { fileField, allowedTypes, jsonField } = options;
306
+ const files: File[] = [];
307
+
308
+ const allowed = (allowedTypes || []).map((item) => item.toLowerCase());
309
+ const getFiles = fileField
310
+ ? formData.getAll(fileField)
311
+ : Array.from(formData.values());
312
+
313
+ for (const value of getFiles) {
314
+ if (value instanceof File) {
315
+ if (allowed.length > 0 && !isAllowedFileType(value, allowed)) {
316
+ badRequest(
317
+ `File type for "${
318
+ value.name
319
+ }" is not allowed. Allowed: ${allowed.join(", ")}`,
320
+ );
321
+ }
322
+
323
+ files.push(value);
324
+ }
325
+ }
326
+
327
+ let parsedJson: unknown;
328
+
329
+ if (jsonField) {
330
+ const rawJson = formData.get(jsonField);
331
+
332
+ if (typeof rawJson === "string") {
333
+ try {
334
+ parsedJson = JSON.parse(rawJson);
335
+ } catch {
336
+ badRequest(`Failed to parse JSON field "${jsonField}".`);
337
+ }
338
+ } else if (rawJson !== null) {
339
+ badRequest(`JSON field "${jsonField}" must be a string value.`);
340
+ }
341
+ }
342
+
343
+ return {
344
+ files,
345
+ json: parsedJson,
346
+ };
339
347
  }
340
348
 
341
349
  function isAllowedFileType(file: File, allowedTypes: string[]): boolean {
342
- const mime = file.type?.toLowerCase?.() || "";
343
- const extension = file.name.split(".").pop()?.toLowerCase();
350
+ const mime = file.type?.toLowerCase?.() || "";
351
+ const extension = file.name.split(".").pop()?.toLowerCase();
344
352
 
345
- if (mime && allowedTypes.includes(mime)) return true;
346
- if (extension && allowedTypes.includes(extension)) return true;
353
+ if (mime && allowedTypes.includes(mime)) return true;
354
+ if (extension && allowedTypes.includes(extension)) return true;
347
355
 
348
- return allowedTypes.length === 0;
356
+ return allowedTypes.length === 0;
349
357
  }
350
358
 
351
359
  function isFormDataLike(value: unknown): value is FormData {
352
- return (
353
- typeof value === "object" &&
354
- value !== null &&
355
- typeof (value as FormData).get === "function" &&
356
- typeof (value as FormData).entries === "function"
357
- );
360
+ return (
361
+ typeof value === "object" &&
362
+ value !== null &&
363
+ typeof (value as FormData).get === "function" &&
364
+ typeof (value as FormData).entries === "function"
365
+ );
358
366
  }
359
367
 
360
368
  function tryResolveFromBody(body: unknown): FormData | null {
361
- if (!body) return null;
362
- if (isFormDataLike(body)) return body;
363
- if (typeof body !== "object") return null;
364
-
365
- const formData = new (globalThis as any).FormData();
366
- for (const [key, value] of Object.entries(body)) {
367
- if (value === undefined || value === null) continue;
368
- if (Array.isArray(value)) {
369
- value.forEach((item) => {
370
- appendValue(formData, key, item);
371
- });
372
- continue;
373
- }
374
- appendValue(formData, key, value);
375
- }
376
-
377
- return formData;
369
+ if (!body) return null;
370
+ if (isFormDataLike(body)) return body;
371
+ if (typeof body !== "object") return null;
372
+
373
+ const formData = new (globalThis as any).FormData();
374
+ for (const [key, value] of Object.entries(body)) {
375
+ if (value === undefined || value === null) continue;
376
+ if (Array.isArray(value)) {
377
+ value.forEach((item) => {
378
+ appendValue(formData, key, item);
379
+ });
380
+ continue;
381
+ }
382
+ appendValue(formData, key, value);
383
+ }
384
+
385
+ return formData;
378
386
  }
379
387
 
380
388
  function appendValue(formData: FormData, key: string, value: unknown) {
381
- if (value instanceof File || value instanceof Blob) {
382
- formData.append(key, value);
383
- } else if (typeof value === "object") {
384
- formData.append(key, JSON.stringify(value));
385
- } else {
386
- formData.append(key, String(value));
387
- }
389
+ if (value instanceof File || value instanceof Blob) {
390
+ formData.append(key, value);
391
+ } else if (typeof value === "object") {
392
+ formData.append(key, JSON.stringify(value));
393
+ } else {
394
+ formData.append(key, String(value));
395
+ }
388
396
  }
389
397
 
390
398
  function badRequest(message: string): never {
391
- throw new Response(JSON.stringify({ error: message }), {
392
- status: 400,
393
- headers: { "content-type": "application/json" },
394
- });
399
+ throw new BadRequestException(message);
395
400
  }
package/package.json CHANGED
@@ -13,7 +13,7 @@
13
13
  "types": "./dist/*.d.ts"
14
14
  }
15
15
  },
16
- "version": "0.2.7",
16
+ "version": "0.2.9",
17
17
  "homepage": "https://bunstone.diario.one/",
18
18
  "repository": {
19
19
  "url": "https://github.com/diariodaregiao/bunstone.git",