bootpress 9.0.2 → 10.0.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/.eslintrc ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "env": {
3
+ "browser": false,
4
+ "node": true,
5
+ "es6": true
6
+ },
7
+ "parserOptions": {
8
+ "ecmaVersion": 2021
9
+ },
10
+ "rules": {
11
+ "no-unused-vars": ["warn", { "varsIgnorePattern": "^_" }],
12
+ "no-undef": "error"
13
+ }
14
+ }
15
+
package/.gitattributes ADDED
@@ -0,0 +1,2 @@
1
+ *.d.ts linguist-detectable=true
2
+ *.ts linguist-detectable=true
package/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  <h1 align="center" style="margin-bottom: 0" >
2
- <img src="https://raw.githubusercontent.com/ufukbakan/bootpress/main/bootpress.svg" height=120 alt="bootpress">
2
+ <img src="https://raw.githubusercontent.com/ufukbakan/bootpress/main/bootpress.svg" width=500 alt="bootpress">
3
3
  </h1>
4
4
  <p align=center>Express but Spring Boot like</p>
5
5
 
@@ -61,13 +61,17 @@ class PostServiceImpl {
61
61
  return new HttpResponse(201, casted.id);
62
62
  }
63
63
  delete(deleteInQuery: string | undefined, idInQuery: string | undefined) {
64
- const idx = deleteInQuery === "yes" ? this.posts.indexOf(as(idInQuery, "integer")) : -1;
65
- if (idx > -1) {
66
- this.#logDeleted(idInQuery);
67
- return this.posts.splice(idx, 1);
68
- }else {
69
- throw new HttpError(404, "Post is not found")
64
+ if (deleteInQuery === "yes") {
65
+ const id = as(idInQuery, "integer");
66
+ const index = this.posts.indexOf(id);
67
+ if (index > -1) {
68
+ this.#logDeleted(idInQuery!);
69
+ return this.posts.splice(index, 1);
70
+ } else {
71
+ throw new HttpError(404, "Post is not found")
72
+ }
70
73
  }
74
+ throw new HttpError(400, "Bad Request");
71
75
  }
72
76
  // use private methods to
73
77
  #logDeleted(id: number | string) {
@@ -139,9 +143,9 @@ app.get("/logs", LogService.findAll() as RequestHandler)
139
143
 
140
144
  ```PassBody(serviceFunction)``` -> Passes body to service function without any validation
141
145
 
142
- ```ParseBodyAs(type)(serviceFunction)``` -> Parses body to specified type then passes it to service function
146
+ ```ParseBodyAs(type, config?)(serviceFunction)``` -> Parses body to specified type then passes it to service function. Config object is optional and has messageTemplate field which represents a string with details placeholder: ```{0}```
143
147
 
144
- ```PassBodyAs(type)(serviceFunction)``` -> Validates body with provided type and passes it to service function
148
+ ```PassBodyAs(type, config?)(serviceFunction)``` -> Validates body with provided type and passes it to service function. Config object is optional and has messageTemplate field which represents a string with details placeholder: ```{0}```
145
149
 
146
150
  ```PassAllParams(serviceFunction)``` -> Passes all path parameters to service function as a Record<string, string> (pure js object that contains key-value pairs)
147
151
 
@@ -179,11 +183,11 @@ Returns the value back if it's not null, undefined or empty array.
179
183
  Returns the value if it's not null or undefined otherwise returns the default value.
180
184
  ### **schema(object)**
181
185
  Helps you to define a JS Schema.
182
- ### **as(any, string | object | array)**
183
- Tries to parse any value to provided type.
184
-
186
+ ### **as(target: any, [type: string | object | array](#type-paramter-for-as--asstrict-methods), [config? object](#config-object-optional-for-as--asstrict-methods))**
187
+ Tries to parse target value to provided type.
188
+ #### Type paramter (for as & asStrict methods)
185
189
  If type of provided type is string then it's a primitive key and valid values are:
186
- ```
190
+ ```ts
187
191
  "string"
188
192
  "string[]"
189
193
  "boolean"
@@ -202,25 +206,43 @@ If type of provided type is string then it's a primitive key and valid values ar
202
206
  "integer[]?"
203
207
  ```
204
208
  If typeof provided type is object then it's a JS Schema and structure must follow:
205
- ```
209
+ ```ts
206
210
  {
207
- "property": string | object | array // Nullable primitives not allowed here instead use question mark end of the property key
208
- "nullableProperty?": string | object | array
211
+ "property": string | object | Array // Nullable primitives not allowed here instead use question mark end of the property key
212
+ "nullableProperty?": string | object | Array
209
213
  }
210
214
  ```
211
215
 
212
216
  If typeof provided type is an array the structure must follow:
213
- ```
217
+ ```ts
214
218
  [
215
219
  yourJsSchemaObject
216
220
  ]
217
221
  ```
218
222
  There must be only one element in an array schema which defines ````ArrayOf<Schema>````
219
- ### **asStrict(any, string | object | array)**
223
+ #### Config object (optional for as & asStrict methods)
224
+ Config object is optional and structure follows:
225
+ ```ts
226
+ {
227
+ errorVariableName: string | undefined, // variable name in the error message
228
+ messageTemplate: string | undefined // default values is "{0}" where it directly writes error details.
229
+ // an messageTemplate example is: "Parse error:\n{0}"
230
+ }
231
+ ```
232
+ ### **asStrict(target: any, [type: string | object | array](#type-parameter), [config? object](#config-object-optional-for-as--asstrict-methods))**
220
233
  Same as 'as' method but doesn't try to parse different types instead throws error.
221
234
 
222
235
  # Release Notes
223
236
 
237
+ ## v10.0.0:
238
+ - Configuration support for as, asStrict, passBodyAs and parseBodyAs methods.
239
+ - Integrated logger. (Changeable via setLogger method)
240
+ - 500 server errors are logged with error level.
241
+
242
+ ## v9.1.0:
243
+ - Fixed chained argument type error bugs
244
+ - Improvements in argument passer type declarations
245
+
224
246
  ## v9.0.2:
225
247
  - Added support for null/undefined returning async functions
226
248
 
@@ -21,6 +21,7 @@ type JsSchema = {
21
21
  }
22
22
 
23
23
  type ArraySchema = [JsSchema]
24
+ type TypedArraySchema<A extends ArraySchema> = TypedSchema<A[0]>
24
25
 
25
26
  type RemoveQuestionMark<T extends string> = T extends `${infer Prefix}?` ? Prefix : T;
26
27
 
@@ -34,15 +35,7 @@ export function getOrThrow<T, K extends NonNullable<T>, E extends HttpError>(dat
34
35
  export function getOrElse<T, E>(data: T, defaultValue: E): E extends NonNullable<infer T> ? E : T | E;
35
36
  export function schema<T extends JsSchema>(schema: T): TypedSchema<T>;
36
37
 
37
- type ExtendedTypeMap = {
38
- "string": string,
39
- "string[]": string[],
40
- "boolean": boolean,
41
- "boolean[]": boolean[],
42
- "number": number,
43
- "number[]": number[],
44
- "integer": number,
45
- "integer[]": number[],
38
+ interface ExtendedTypeMap extends TypeMap {
46
39
  "string?": string | null,
47
40
  "string[]?": string[] | null,
48
41
  "boolean?": boolean | null,
@@ -54,6 +47,26 @@ type ExtendedTypeMap = {
54
47
  }
55
48
 
56
49
  type ExtendedTypeKeys = keyof ExtendedTypeMap;
57
- type ExtValOf<T extends ExtendedTypeKeys> = ExtendedTypeMap[T]
58
- export function as<T extends (ExtendedTypeKeys | JsSchema | ArraySchema)>(o:any, type: T, errorVariableName?: string): T extends ExtendedTypeKeys ? ExtValOf<T> : TypedSchema<T>;
59
- export function asStrict<T extends (ExtendedTypeKeys | JsSchema | ArraySchema)>(o:any, type: T, errorVariableName?: string): T extends ExtendedTypeKeys ? ExtValOf<T> : TypedSchema<T>;
50
+ type ExtValOf<T extends ExtendedTypeKeys> = ExtendedTypeMap[T];
51
+ type ErrorVariableConfiguration = {
52
+ errorVariableName?: string
53
+ };
54
+ type ErrorTemplateConfiguration = {
55
+ messageTemplate?: string
56
+ };
57
+ type ErrorConfiguration = ErrorVariableConfiguration & ErrorTemplateConfiguration;
58
+
59
+
60
+ export function as<T extends (ExtendedTypeKeys | JsSchema | ArraySchema)>(o: any, type: T, config?: ErrorConfiguration): T extends ExtendedTypeKeys ? ExtValOf<T> : TypedSchema<T>;
61
+ export function asStrict<T extends (ExtendedTypeKeys | JsSchema | ArraySchema)>(o: any, type: T, config?: ErrorConfiguration): T extends ExtendedTypeKeys ? ExtValOf<T> : TypedSchema<T>;
62
+
63
+ type Log = {
64
+ fatal: (message: any) => void,
65
+ error: (message: any) => void,
66
+ warn: (message: any) => void,
67
+ info: (message: any) => void,
68
+ debug: (message: any) => void,
69
+ trace: (message: any) => void,
70
+ }
71
+ export const log: Log;
72
+ export function setLogger(logger: Log): void;
package/helpers/index.js CHANGED
@@ -1,6 +1,41 @@
1
1
  const { HttpError } = require("../types");
2
2
 
3
3
  const allowedPrimitives = ["string", "number", "boolean", "integer"];
4
+ /**
5
+ * For both getter & setter
6
+ *
7
+ * Any logger with Log4J interface can be usable. (e.g. Pino)
8
+ *
9
+ */
10
+ let _logger = {
11
+ fatal: (message) => console.error(message),
12
+ error: (message) => console.error(message),
13
+ warn: (message) => console.warn(message),
14
+ info: (message) => console.log(message),
15
+ debug: (message) => console.log(message),
16
+ trace: (message) => console.log(message),
17
+ };
18
+
19
+ const log = {
20
+ fatal: (message) => _logger.fatal(message),
21
+ error: (message) => _logger.error(message),
22
+ warn: (message) => _logger.warn(message),
23
+ info: (message) => _logger.info(message),
24
+ debug: (message) => _logger.debug(message),
25
+ trace: (message) => _logger.trace(message),
26
+ }
27
+
28
+ function setLogger(logger) {
29
+ _logger = logger;
30
+ }
31
+
32
+ function passStringArgs(str, ...args) {
33
+ let result = str;
34
+ for (let i = 0; i < args.length; i++) {
35
+ result = str.replace(`{${i}}`, args[i]);
36
+ }
37
+ return result;
38
+ }
4
39
 
5
40
  function getOrThrow(data, error) {
6
41
  if (data === null || data === undefined || isEmptyArray(data)) {
@@ -10,7 +45,7 @@ function getOrThrow(data, error) {
10
45
  }
11
46
  }
12
47
 
13
- function isEmptyArray(x){
48
+ function isEmptyArray(x) {
14
49
  return Array.isArray(x) && x.length < 1;
15
50
  }
16
51
 
@@ -83,9 +118,12 @@ function asString(o, errorMessage = undefined, errorStatus = 400) {
83
118
  }
84
119
  }
85
120
 
86
- function asSchema(o, schema, strict = false) {
87
- if(!(schema instanceof Object) || Array.isArray(schema)){
88
- throw new HttpError(500, `Schema is not valid ${JSON.stringify(schema)}`);
121
+ function asSchema(o, schema, config = { strict: false, messageTemplate: "{0}" }) {
122
+ const strict = config.strict ?? false;
123
+ if (!(schema instanceof Object) || Array.isArray(schema)) {
124
+ const error = new HttpError(500, `Schema is not valid ${JSON.stringify(schema)}`);
125
+ _logger.error(error.stack);
126
+ throw error;
89
127
  }
90
128
  const schemaKeyValues = Object.entries(schema);
91
129
  let result = {};
@@ -104,17 +142,19 @@ function asSchema(o, schema, strict = false) {
104
142
  if (Array.isArray(expectedType)) {
105
143
  result[key] = [];
106
144
  for (let j = 0; j < o[key].length; j++) {
107
- result[key][j] = asSchema(o[key][j], expectedType[0], strict)
145
+ result[key][j] = asSchema(o[key][j], expectedType[0], config)
108
146
  }
109
147
  } else {
110
- result[key] = asSchema(o[key], expectedType, strict);
148
+ result[key] = asSchema(o[key], expectedType, config);
111
149
  }
112
150
  }
113
151
  else if (typeof expectedType === "string") {
114
- result[key] = strict ? asStrict(o[key], expectedType, key) : as(o[key], expectedType, key);
152
+ result[key] = strict ? asStrict(o[key], expectedType, { ...config, errorVariableName: key }) : as(o[key], expectedType, { ...config, errorVariableName: key });
115
153
  }
116
154
  else {
117
- throw new HttpError(500, `Type of a schema key should be a primitive type or another schema`);
155
+ const error = new HttpError(500, `Type of a schema key should be a primitive type or another schema`);
156
+ _logger.error(error.stack);
157
+ throw error;
118
158
  }
119
159
  }
120
160
  return result;
@@ -125,117 +165,139 @@ function schema(schema) {
125
165
  return schema;
126
166
  }
127
167
 
128
- function asPrimitiveArrayOf(o, elementType) {
168
+ function asPrimitiveArrayOf(o, elementType, config = { errorVariableName: o, messageTemplate: "{0}" }) {
169
+ const messageTemplate = config.messageTemplate ?? "{0}";
129
170
  if (Array.isArray(o)) {
130
171
  for (let i = 0; i < o.length; i++) {
131
- o[i] = checkPrimitive(o[i], elementType, "Array elemet");
172
+ o[i] = checkPrimitive(o[i], elementType, config);
132
173
  }
133
174
  return o;
134
175
  } else {
135
- throw new HttpError(400, `Provided object is not an array: ${JSON.stringify(o)}`)
176
+ throw new HttpError(400, passStringArgs(messageTemplate, `Provided object is not an array: ${JSON.stringify(o)}`))
136
177
  }
137
178
  }
138
179
 
139
- function as(o, type, namedErrorVariable = o) {
180
+ function as(o, type, config = { errorVariableName: o, messageTemplate: "{0}" }) {
181
+ const errorVariableName = config.errorVariableName ?? o;
182
+ const messageTemplate = config.messageTemplate ?? "{0}";
140
183
  if (typeof type === "string") {
141
184
  if (type.endsWith("[]")) {
142
185
  // array check
143
186
  const elementType = type.replace("[]", "");
144
- return asPrimitiveArrayOf(o, elementType);
187
+ return asPrimitiveArrayOf(o, elementType, config);
145
188
  } else { // non array types:
146
189
  if (type.endsWith("?") && o == null) {
147
190
  return null;
148
191
  } else if (type.endsWith("?") && o != null) {
149
192
  const actualType = type.replace("?", "");
150
- return as(o, actualType, namedErrorVariable);
193
+ return as(o, actualType, config);
151
194
  }
152
195
  // primitive check
153
196
  switch (type) {
154
197
  case "string":
155
- return asString(o, `Type of ${namedErrorVariable} should have been a string but it's ${o == null ? "null" : typeof o}`);
198
+ return asString(o, passStringArgs(messageTemplate, `Type of ${errorVariableName} should have been a string but it's ${o == null ? "null" : typeof o}`));
156
199
  case "number":
157
- return asNumber(o, `Type of ${namedErrorVariable} should have been a number but it's ${o == null ? "null" : typeof o}`);
200
+ return asNumber(o, passStringArgs(messageTemplate, `Type of ${errorVariableName} should have been a number but it's ${o == null ? "null" : typeof o}`));
158
201
  case "boolean":
159
- return asBoolean(o, `Type of ${namedErrorVariable} should have been a boolean but it's ${o == null ? "null" : typeof o}`);
202
+ return asBoolean(o, passStringArgs(messageTemplate, `Type of ${errorVariableName} should have been a boolean but it's ${o == null ? "null" : typeof o}`));
160
203
  case "integer":
161
- return asInteger(o, `Type of ${namedErrorVariable} should have been an integer but it's ${o == null ? "null" : typeof o}`);
204
+ return asInteger(o, passStringArgs(messageTemplate, `Type of ${errorVariableName} should have been an integer but it's ${o == null ? "null" : typeof o}`));
162
205
  default:
163
- throw new HttpError(500, `Unsupported type ${type}`);
206
+ const error = new HttpError(500, `Unsupported type ${type}`);
207
+ _logger.error(error.stack);
208
+ throw error;
164
209
  }
165
210
  }
166
211
  } else if (typeof type === "object" && type != null) {
167
212
  if (Array.isArray(type)) {
168
213
  if (type.length > 1) {
169
- throw new HttpError(500, `You can define only one schema for types ArrayOf<Schema>`);
214
+ const error = new HttpError(500, `You can define only one schema for types ArrayOf<Schema>`);
215
+ _logger.error(error.stack);
216
+ throw error;
170
217
  } else if (type.length < 1) {
171
- throw new HttpError(500, `You must define a schema for types ArrayOf<Schema>`);
218
+ const error = new HttpError(500, `You must define a schema for types ArrayOf<Schema>`);
219
+ _logger.error(error.stack)
220
+ throw error;
172
221
  }
173
222
  // array schema validation
174
223
  if (!Array.isArray(o)) {
175
- throw new HttpError(400, `Provided value should have been an array. (${JSON.stringify(o)})`)
224
+ throw new HttpError(400, passStringArgs(messageTemplate, `Provided value should have been an array. (${JSON.stringify(o)})`))
176
225
  }
177
226
  const providedSchema = type[0];
178
227
  let result = [];
179
228
  for (let i = 0; i < o.length; i++) {
180
- result.push(asSchema(o[i], providedSchema));
229
+ result.push(asSchema(o[i], providedSchema, config));
181
230
  }
182
231
  return result;
183
232
  } else {
184
233
  // schema validation
185
- return asSchema(o, type);
234
+ return asSchema(o, type, config);
186
235
  }
187
236
  } else {
188
- throw new HttpError(500, `Unsupported type check ${type}`)
237
+ const error = new HttpError(500, `Unsupported type check ${type}`);
238
+ _logger.error(error);
239
+ throw error;
189
240
  }
190
241
  }
191
242
 
192
- function asStrict(o, type, namedErrorVariable = o) {
243
+ function asStrict(o, type, config = { errorVariableName: o, messageTemplate: "{0}" }) {
244
+ const messageTemplate = config.messageTemplate ?? "{0}";
193
245
  if (typeof type === "string") {
194
246
  if (type.endsWith("[]")) {
195
247
  // array check
196
248
  const elementType = type.replace("[]", "");
197
- return asPrimitiveArrayOf(o, elementType);
249
+ return asPrimitiveArrayOf(o, elementType, config);
198
250
  } else { // non array types:
199
251
  if (type.endsWith("?") && o == null) {
200
252
  return null;
201
253
  } else if (type.endsWith("?") && o != null) {
202
254
  const actualType = type.replace("?", "");
203
- return asStrict(o, actualType, namedErrorVariable);
255
+ return asStrict(o, actualType, config);
204
256
  }
205
257
  // primitive check
206
258
  if (!allowedPrimitives.includes(type)) {
207
- throw new HttpError(500, `Unsupported type ${type}`);
259
+ const error = new HttpError(500, `Unsupported type ${type}`);
260
+ _logger.error(error);
261
+ throw error;
208
262
  }
209
- return checkPrimitive(o, type, namedErrorVariable);
263
+ return checkPrimitive(o, type, config);
210
264
  }
211
265
  } else if (typeof type === "object" && type != null) {
212
266
  if (Array.isArray(type)) {
213
267
  if (type.length > 1) {
214
- throw new HttpError(500, `You can define only one schema for types ArrayOf<Schema>`);
268
+ const error = new HttpError(500, `You can define only one schema for types ArrayOf<Schema>`);
269
+ _logger.error(error);
270
+ throw error;
215
271
  } else if (type.length < 1) {
216
- throw new HttpError(500, `You must define a schema for types ArrayOf<Schema>`);
272
+ const error = new HttpError(500, `You must define a schema for types ArrayOf<Schema>`);
273
+ _logger.error(error);
274
+ throw error;
217
275
  }
218
276
  // array schema validation
219
277
  if (!Array.isArray(o)) {
220
- throw new HttpError(400, `Provided value should have been an array but it's ${typeof o}`)
278
+ throw new HttpError(400, passStringArgs(messageTemplate, `Provided value should have been an array but it's ${typeof o}`))
221
279
  }
222
280
  const providedSchema = type[0];
223
281
  let result = [];
224
282
  for (let i = 0; i < o.length; i++) {
225
- result.push(asSchema(o[i], providedSchema, true));
283
+ result.push(asSchema(o[i], providedSchema, { ...config, strict: true }));
226
284
  }
227
285
  return result;
228
286
  } else {
229
287
  // schema validation
230
- return asSchema(o, type, true);
288
+ return asSchema(o, type, { ...config, strict: true });
231
289
  }
232
290
  } else {
233
- throw new HttpError(500, `Unsupported type check ${type}`)
291
+ const error = new HttpError(500, `Unsupported type check ${type}`);
292
+ _logger.error(error);
293
+ throw error;
234
294
  }
235
295
  }
236
296
 
237
- function checkPrimitive(o, type, varName = o) {
238
- const error = new HttpError(400, `${varName} should have been a ${type}. But it's ${typeof o}`);
297
+ function checkPrimitive(o, type, config = { errorVariableName: o, messageTemplate: "{0}" }) {
298
+ const errorVariableName = config.errorVariableName ?? o;
299
+ const messageTemplate = config.messageTemplate ?? "{0}";
300
+ const error = new HttpError(400, passStringArgs(messageTemplate, `${errorVariableName} should have been a ${type}. But it's ${o == null ? "null" : typeof o}`));
239
301
  if (type === "integer") {
240
302
  if (!Number.isInteger(o)) {
241
303
  throw error;
@@ -253,5 +315,7 @@ module.exports = {
253
315
  getOrElse,
254
316
  schema,
255
317
  as,
256
- asStrict
318
+ asStrict,
319
+ log,
320
+ setLogger
257
321
  }
package/index.d.ts CHANGED
@@ -1,13 +1,17 @@
1
- import { Response, Request } from "express"
2
- import { ArraySchema, JsSchema, ValidTypeKeys, TypedSchema } from "./helpers"
1
+ import { Request, Response } from "express";
2
+ import { ArraySchema, ErrorTemplateConfiguration, ExtValOf, ExtendedTypeKeys, JsSchema, TypedArraySchema, TypedSchema } from "./helpers";
3
3
 
4
4
  type RequestHandler = (req: Request, res: Response) => void
5
- type RequsetHandlerWithArgs = <A extends any[]>(...args: A) => RequestHandler
6
- type RequsetHandlerWithFirstArg<T> = (arg1: T, ...rest: any[]) => RequestHandler
5
+ type ArrayWithLastElement<L> = [...rest: any[], lastArg: L];
6
+ type RequsetHandlerWithLastArg<T, F extends (...args: any) => RequestHandler = (...args: any[]) => RequestHandler, P extends ArrayWithLastElement<T> = Parameters<F>> = (...args: P) => RequestHandler;
7
7
  type NonEmptyArray = readonly [any, ...any[]];
8
- type ShiftRequestHandler<F extends (arg1: any, ...rest: any[]) => RequestHandler> = F extends (arg1: infer T, ...rest: infer U) => RequestHandler ? U extends NonEmptyArray ? (...rest: U) => RequestHandler : RequestHandler : RequestHandler
8
+ type ShiftRequestHandler<F extends RequsetHandlerWithLastArg<any> | RequestHandler> =
9
+ F extends (arg1: infer T, ...rest: infer U) => RequestHandler ?
10
+ U extends NonEmptyArray ? (...rest: U) => RequestHandler : RequestHandler
11
+ : RequestHandler
9
12
 
10
- type RestedService<T extends Record<PropertyKey, any>> = { [K in keyof T]:
13
+ type RestedService<T extends Record<PropertyKey, any>> = {
14
+ [K in keyof T]:
11
15
  T[K] extends Function
12
16
  ? (...args: Parameters<T[K]>) => RequestHandler
13
17
  : T[K]
@@ -15,35 +19,31 @@ type RestedService<T extends Record<PropertyKey, any>> = { [K in keyof T]:
15
19
 
16
20
  type InstanceOrClass<T extends Record<PropertyKey, any>> = T | (new () => T);
17
21
 
22
+ type TypeValueOf<S extends ExtendedTypeKeys | JsSchema | TypedSchema<JsSchema> | ArraySchema> =
23
+ S extends TypedSchema<JsSchema> ? S
24
+ : S extends ExtendedTypeKeys ? ExtValOf<S>
25
+ : S extends JsSchema ? TypedSchema<S>
26
+ : S extends ArraySchema ? TypedArraySchema<S>
27
+ : never;
28
+
18
29
  declare function RestService<T extends Record<PropertyKey, any>>(service: InstanceOrClass<T>): RestedService<T>;
19
30
  declare function RestMethod<T>(callback: () => T): RequestHandler;
20
31
  declare function Restify(target: any, key: PropertyKey, desc: PropertyDescriptor): PropertyDescriptor;
21
32
 
22
- declare function PassBody<F extends RequsetHandlerWithFirstArg<any>>(serviceFunction: F): ShiftRequestHandler<F>
23
- declare function ParseBodyAs<T extends ValidTypeKeys | JsSchema | TypedSchema<any> | ArraySchema, F extends RequsetHandlerWithFirstArg<T>>(type: T): (serviceFunction: F) => ShiftRequestHandler<F>
24
- declare function PassBodyAs<T extends ValidTypeKeys | JsSchema | TypedSchema<any> | ArraySchema, F extends RequsetHandlerWithFirstArg<T>>(type: T): (serviceFunction: F) => ShiftRequestHandler<F>
25
- declare function PassAllParams<F extends RequsetHandlerWithFirstArg<string[]>>(serviceFunction: F): ShiftRequestHandler<F>
26
- declare function PassAllQueries<F extends RequsetHandlerWithFirstArg<string[]>>(serviceFunction: F): ShiftRequestHandler<F>
27
- declare function PassAllCookies<F extends RequsetHandlerWithFirstArg<string[]>>(serviceFunction: F): ShiftRequestHandler<F>
28
- declare function PassParam<F extends RequsetHandlerWithFirstArg<string | undefined>>(paramName: string): (serviceFunction: F) => ShiftRequestHandler<F>
29
- declare function PassQuery<F extends RequsetHandlerWithFirstArg<string | undefined>>(queryName: string): (serviceFunction: F) => ShiftRequestHandler<F>
30
- declare function PassCookie<F extends RequsetHandlerWithFirstArg<string | undefined>>(cookieName: string): (serviceFunction: F) => ShiftRequestHandler<F>
31
- declare function PassRequest<F extends RequsetHandlerWithFirstArg<Request>>(serviceFunction: F): ShiftRequestHandler<F>
32
- declare function PassResponse<F extends RequsetHandlerWithFirstArg<Response>>(serviceFunction: F): ShiftRequestHandler<F>
33
+ declare function PassBody<F extends RequsetHandlerWithLastArg<any>>(serviceFunction: F): ShiftRequestHandler<F>
34
+ declare function ParseBodyAs<S extends ExtendedTypeKeys | JsSchema | TypedSchema<JsSchema> | ArraySchema, T = TypeValueOf<S>>(type: S, config?: ErrorTemplateConfiguration): <F extends RequsetHandlerWithLastArg<T>>(serviceFunction: F) => ShiftRequestHandler<F>
35
+ declare function PassBodyAs<S extends ExtendedTypeKeys | JsSchema | TypedSchema<JsSchema> | ArraySchema, T = TypeValueOf<S>>(type: S, config?: ErrorTemplateConfiguration): <F extends RequsetHandlerWithLastArg<T>>(serviceFunction: F) => ShiftRequestHandler<F>
36
+ declare function PassAllParams<F extends RequsetHandlerWithLastArg<string[]>>(serviceFunction: F): ShiftRequestHandler<F>
37
+ declare function PassAllQueries<F extends RequsetHandlerWithLastArg<string[]>>(serviceFunction: F): ShiftRequestHandler<F>
38
+ declare function PassAllCookies<F extends RequsetHandlerWithLastArg<string[]>>(serviceFunction: F): ShiftRequestHandler<F>
39
+ declare function PassParam(paramName: string): <F extends RequsetHandlerWithLastArg<string | undefined>>(serviceFunction: F) => ShiftRequestHandler<F>
40
+ declare function PassQuery(queryName: string): <F extends RequsetHandlerWithLastArg<string | undefined>>(serviceFunction: F) => ShiftRequestHandler<F>
41
+ declare function PassCookie(cookieName: string): <F extends RequsetHandlerWithLastArg<string | undefined>>(serviceFunction: F) => ShiftRequestHandler<F>
42
+ declare function PassRequest<F extends RequsetHandlerWithLastArg<Request>>(serviceFunction: F): ShiftRequestHandler<F>
43
+ declare function PassResponse<F extends RequsetHandlerWithLastArg<Response>>(serviceFunction: F): ShiftRequestHandler<F>
33
44
 
34
45
  export {
35
- RestService,
36
- RestMethod,
37
- Restify,
38
- PassParam,
39
- PassAllParams,
40
- PassQuery,
41
- PassAllQueries,
42
- PassCookie,
43
- PassAllCookies,
44
- PassBody,
45
- PassBodyAs,
46
- ParseBodyAs,
47
- PassRequest,
48
- PassResponse
49
- }
46
+ ParseBodyAs, PassAllCookies, PassAllParams, PassAllQueries, PassBody,
47
+ PassBodyAs, PassCookie, PassParam, PassQuery, PassRequest,
48
+ PassResponse, RestMethod, RestService, Restify
49
+ };
package/index.js CHANGED
@@ -1,5 +1,4 @@
1
- const { as, asStrict } = require("./helpers");
2
- const { HttpError } = require("./types");
1
+ const { as, asStrict, log } = require("./helpers");
3
2
 
4
3
  const protectedProperties = [
5
4
  "toString",
@@ -48,14 +47,18 @@ function RestService(service) {
48
47
  reply(res, r.status ?? 200, r.data ?? r);
49
48
  }
50
49
  }).catch(e => {
51
- reply(res, e.status ?? 500, e.message ?? e);
50
+ const status = e.status ?? 500;
51
+ status === 500 && log.error(e.stack);
52
+ reply(res, status, e.message ?? e);
52
53
  })
53
54
  }
54
55
  else {
55
56
  reply(res, result.status ?? 200, result.data ?? result)
56
57
  }
57
58
  } catch (e) {
58
- reply(res, e.status ?? 500, e.message ?? e);
59
+ const status = e.status ?? 500;
60
+ status === 500 && log.error(e.stack);
61
+ reply(res, status, e.message ?? e);
59
62
  }
60
63
  });
61
64
  } else {
@@ -80,13 +83,17 @@ function RestMethod(callback) {
80
83
  reply(res, r.status ?? 200, r.data ?? r);
81
84
  }
82
85
  }).catch(e => {
83
- reply(res, e.status ?? 500, e.message ?? e);
86
+ const status = e.status ?? 500;
87
+ status === 500 && log.error(e.stack);
88
+ reply(res, status, e.message ?? e);
84
89
  })
85
90
  } else {
86
91
  reply(res, result.status ?? 200, result.data ?? result);
87
92
  }
88
93
  } catch (e) {
89
- reply(res, e.status ?? 500, e.message ?? e)
94
+ const status = e.status ?? 500;
95
+ status === 500 && log.error(e.stack);
96
+ reply(res, status, e.message ?? e)
90
97
  }
91
98
  }
92
99
  }
@@ -110,13 +117,17 @@ function Restify(target, key, desc) {
110
117
  reply(res, r.status ?? 200, r.data ?? r);
111
118
  }
112
119
  }).catch(e => {
113
- reply(res, e.status ?? 500, e.message ?? e);
120
+ const status = e.status ?? 500;
121
+ status === 500 && log.error(e.stack);
122
+ reply(res, status, e.message ?? e);
114
123
  })
115
124
  } else {
116
125
  reply(res, result.status ?? 200, result.data ?? result);
117
126
  }
118
127
  } catch (e) {
119
- reply(res, e.status ?? 500, e.message ?? e);
128
+ const status = e.status ?? 500;
129
+ status === 500 && log.error(e.stack);
130
+ reply(res, status, e.message ?? e);
120
131
  }
121
132
  }
122
133
  }).bind(target)
@@ -132,7 +143,7 @@ function isRequest(o) {
132
143
  }
133
144
 
134
145
  function isRequstHandlerArgs(args) {
135
- const [last1, last2, last3, ...others] = [...args].reverse();
146
+ const [_last1, last2, last3, ..._others] = [...args].reverse();
136
147
  return isResponse(last2) && isRequest(last3);
137
148
  }
138
149
 
@@ -186,23 +197,27 @@ function PassAllQueries(actualHandler) {
186
197
  }
187
198
  }
188
199
 
189
- function ParseBodyAs(type) {
200
+ function ParseBodyAs(type, config = { messageTemplate: "Malformed Request Body\n{0}" }) {
190
201
  return (actualHandler) => {
191
202
  return (...args) => {
192
203
  if (isRequstHandlerArgs(args)) {
193
204
  const req = args.at(-3); const res = args.at(-2);
194
205
  try {
195
- return actualHandler(as(req.body, type))(req, res);
206
+ return actualHandler(as(req.body, type, config))(req, res);
196
207
  } catch (e) {
197
- reply(res, e.status || 500, e.message || e);
208
+ const status = e.status ?? 500;
209
+ status === 500 && log.error(e.stack);
210
+ reply(res, status, e.message || e);
198
211
  }
199
212
 
200
213
  } else {
201
214
  return (req, res) => {
202
215
  try {
203
- return actualHandler(...args, as(req.body, type))(req, res);
216
+ return actualHandler(...args, as(req.body, type, config))(req, res);
204
217
  } catch (e) {
205
- reply(res, e.status || 500, e.message || e)
218
+ const status = e.status ?? 500;
219
+ status === 500 && log.error(e.stack);
220
+ reply(res, status, e.message || e)
206
221
  }
207
222
  }
208
223
  }
@@ -210,23 +225,27 @@ function ParseBodyAs(type) {
210
225
  }
211
226
  }
212
227
 
213
- function PassBodyAs(type) {
228
+ function PassBodyAs(type, config = { messageTemplate: "Malformed Request Body\n{0}" }) {
214
229
  return (actualHandler) => {
215
230
  return (...args) => {
216
231
  if (isRequstHandlerArgs(args)) {
217
232
  const req = args.at(-3); const res = args.at(-2);
218
233
  try {
219
- return actualHandler(asStrict(req.body, type))(req, res);
234
+ return actualHandler(asStrict(req.body, type, config))(req, res);
220
235
  } catch (e) {
221
- reply(res, e.status || 500, e.message || e);
236
+ const status = e.status ?? 500;
237
+ status === 500 && log.error(e.stack);
238
+ reply(res, status, e.message || e);
222
239
  }
223
240
 
224
241
  } else {
225
242
  return (req, res) => {
226
243
  try {
227
- return actualHandler(...args, asStrict(req.body, type))(req, res);
244
+ return actualHandler(...args, asStrict(req.body, type, config))(req, res);
228
245
  } catch (e) {
229
- reply(res, e.status || 500, e.message || e)
246
+ const status = e.status ?? 500;
247
+ status === 500 && log.error(e.stack);
248
+ reply(res, status, e.message || e)
230
249
  }
231
250
  }
232
251
  }
@@ -241,7 +260,9 @@ function PassBody(actualHandler) {
241
260
  try {
242
261
  return actualHandler(req.body)(req, res);
243
262
  } catch (e) {
244
- reply(res, e.status || 500, e.message || e);
263
+ const status = e.status ?? 500;
264
+ status === 500 && log.error(e.stack);
265
+ reply(res, status, e.message || e);
245
266
  }
246
267
 
247
268
  } else {
@@ -249,7 +270,9 @@ function PassBody(actualHandler) {
249
270
  try {
250
271
  return actualHandler(...args, req.body)(req, res);
251
272
  } catch (e) {
252
- reply(res, e.status || 500, e.message || e)
273
+ const status = e.status ?? 500;
274
+ status === 500 && log.error(e.stack);
275
+ reply(res, status, e.message || e)
253
276
  }
254
277
  }
255
278
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bootpress",
3
- "version": "9.0.2",
3
+ "version": "10.0.0",
4
4
  "description": "REST service methods for express",
5
5
  "main": "index.js",
6
6
  "repository": {
@@ -28,6 +28,7 @@
28
28
  },
29
29
  "devDependencies": {
30
30
  "@types/express": "^4.17.17",
31
+ "eslint": "^8.55.0",
31
32
  "typescript": "^4.9.5"
32
33
  }
33
34
  }