bootpress 5.0.1 → 6.0.1
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/README.md +4 -1
- package/helpers/index.d.ts +26 -3
- package/helpers/index.js +75 -4
- package/index.d.ts +9 -19
- package/index.js +54 -24
- package/package.json +3 -4
- package/types/index.js +20 -0
- package/types/index.ts +16 -0
package/README.md
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
<h1 align="center" style="margin-bottom: 0" >
|
|
2
|
-
<img src="bootpress.svg" height=120 alt="bootpress">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/ufukbakan/bootpress/main/bootpress.svg" height=120 alt="bootpress">
|
|
3
3
|
</h1>
|
|
4
4
|
<p align=center>Express but Spring Boot like</p>
|
|
5
5
|
|
|
6
|
+
## Quick Start
|
|
7
|
+
Recommended tool: [create-bootpress-app](https://www.npmjs.com/package/create-bootpress-app)
|
|
8
|
+
|
|
6
9
|
## Methods
|
|
7
10
|
### **<u>RestService</u>**: Converts all methods to Express RequestHandlers
|
|
8
11
|
#### Basic usage:
|
package/helpers/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { HttpError } from "
|
|
1
|
+
import { HttpError } from "../types";
|
|
2
2
|
|
|
3
3
|
type TypeMap = {
|
|
4
4
|
"string": string,
|
|
@@ -10,7 +10,7 @@ type TypeMap = {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
type ValidTypeKeys = keyof TypeMap;
|
|
13
|
-
type ValOf<T extends
|
|
13
|
+
type ValOf<T extends ValidTypeKeys> = TypeMap[T]
|
|
14
14
|
|
|
15
15
|
type StringEndsWithQm = `${string}?`;
|
|
16
16
|
|
|
@@ -35,4 +35,27 @@ export function asNumber(o: any): number;
|
|
|
35
35
|
export function asInteger(o: any): number;
|
|
36
36
|
export function asString(o: any): string;
|
|
37
37
|
export function asSchema<T extends JsSchema>(o: any, jsSchema: T): TypedSchema<T>;
|
|
38
|
-
export function schema<T extends JsSchema>(schema: T): T;
|
|
38
|
+
export function schema<T extends JsSchema>(schema: T): T;
|
|
39
|
+
|
|
40
|
+
type ExtendedTypeMap = {
|
|
41
|
+
"string": string,
|
|
42
|
+
"string[]": string[],
|
|
43
|
+
"boolean": boolean,
|
|
44
|
+
"boolean[]": boolean[],
|
|
45
|
+
"number": number,
|
|
46
|
+
"number[]": number[],
|
|
47
|
+
"integer": number,
|
|
48
|
+
"integer[]": number[],
|
|
49
|
+
"string?": string | null,
|
|
50
|
+
"string[]?": string[] | null,
|
|
51
|
+
"boolean?": boolean | null,
|
|
52
|
+
"boolean[]?": boolean[] | null,
|
|
53
|
+
"number?": number | null,
|
|
54
|
+
"number[]?": number[] | null,
|
|
55
|
+
"integer?": number | null,
|
|
56
|
+
"integer[]?": number[] | null
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
type ExtendedTypeKeys = keyof ExtendedTypeMap;
|
|
60
|
+
type ExtValOf<T extends ExtendedTypeKeys> = ExtendedTypeMap[T]
|
|
61
|
+
export function as<T extends (ExtendedTypeKeys | JsSchema | ArraySchema)>(o:any, schema: T): T extends ExtendedTypeKeys ? ExtValOf<T> : TypedSchema<T>;
|
package/helpers/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const { HttpError } = require("
|
|
1
|
+
const { HttpError } = require("../types");
|
|
2
2
|
|
|
3
3
|
function getOrThrow(data, error) {
|
|
4
4
|
if (data === null || data === undefined) {
|
|
@@ -34,6 +34,9 @@ function asBoolean(o, errorMessage = undefined, errorStatus = 400) {
|
|
|
34
34
|
return validBooleanStrings.get(lowercased);
|
|
35
35
|
} else if (typeof o === "boolean") {
|
|
36
36
|
return o;
|
|
37
|
+
} else if (typeof o === "number"){
|
|
38
|
+
if(o === 1) return true;
|
|
39
|
+
if(o === 0) return false;
|
|
37
40
|
}
|
|
38
41
|
throw new HttpError(errorStatus, errorMessage);
|
|
39
42
|
}
|
|
@@ -102,8 +105,8 @@ function asSchema(o, schema) {
|
|
|
102
105
|
else if (typeof expectedType === "string") {
|
|
103
106
|
if (expectedType.endsWith("[]")) {
|
|
104
107
|
const elementType = expectedType.replace("[]", "");
|
|
105
|
-
for (let j = 0; j < o[key].length; j++){
|
|
106
|
-
if(typeof o[key][j] !== elementType){
|
|
108
|
+
for (let j = 0; j < o[key].length; j++) {
|
|
109
|
+
if (typeof o[key][j] !== elementType) {
|
|
107
110
|
throw new HttpError(400, `Each element of ${key} should have been a ${elementType} but a ${typeof o[key][j]} is present (${o[key][j]})`);
|
|
108
111
|
}
|
|
109
112
|
}
|
|
@@ -127,6 +130,73 @@ function schema(schema) {
|
|
|
127
130
|
return schema;
|
|
128
131
|
}
|
|
129
132
|
|
|
133
|
+
function asArrayOf(o, elementType) {
|
|
134
|
+
if (Array.isArray(o)) {
|
|
135
|
+
for (let i = 0; i < o.length; i++) {
|
|
136
|
+
if(elementType === "integer"){
|
|
137
|
+
asInteger(o[i]);
|
|
138
|
+
}else if(typeof o[i] != elementType){
|
|
139
|
+
throw new HttpError(400, `Each element in array should have been a ${elementType} but ${o[i]} is present with type ${typeof o[i]}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return o;
|
|
143
|
+
} else {
|
|
144
|
+
throw new HttpError(400, `Provided object is not an array: ${JSON.stringify(o)}`)
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function as(o, type) {
|
|
149
|
+
if (typeof type == "string") {
|
|
150
|
+
if (type.endsWith("[]")) {
|
|
151
|
+
// array check
|
|
152
|
+
const elementType = type.replace("[]", "");
|
|
153
|
+
return asArrayOf(o, elementType);
|
|
154
|
+
} else {
|
|
155
|
+
if(type.endsWith("?") && o == null){
|
|
156
|
+
return null;
|
|
157
|
+
}else if(type.endsWith("?") && o != null ){
|
|
158
|
+
const actualType = type.replace("?", "");
|
|
159
|
+
return as(o, actualType);
|
|
160
|
+
}
|
|
161
|
+
// primitive check
|
|
162
|
+
switch (type) {
|
|
163
|
+
case "string":
|
|
164
|
+
return asString(o);
|
|
165
|
+
case "number":
|
|
166
|
+
return asNumber(o);
|
|
167
|
+
case "boolean":
|
|
168
|
+
return asBoolean(o);
|
|
169
|
+
case "integer":
|
|
170
|
+
return asInteger(o);
|
|
171
|
+
default:
|
|
172
|
+
throw new HttpError(500, `Unsupported type ${type}`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
} else if (typeof type == "object" && type != null) {
|
|
176
|
+
if (Array.isArray(type)) {
|
|
177
|
+
if(type.length > 1){
|
|
178
|
+
throw new HttpError(500, `You can define only one schema for types ArrayOf<Schema>`);
|
|
179
|
+
}else if (type.length < 1){
|
|
180
|
+
throw new HttpError(500, `You must define a schema for types ArrayOf<Schema>`);
|
|
181
|
+
}
|
|
182
|
+
// array schema validation
|
|
183
|
+
if(!Array.isArray(o)){
|
|
184
|
+
throw new HttpError(400, `Provided value should have been an array. (${JSON.stringify(o)})`)
|
|
185
|
+
}
|
|
186
|
+
const providedSchema = type[0];
|
|
187
|
+
for(let i = 0; i < o.length; i++){
|
|
188
|
+
asSchema(o[i], providedSchema);
|
|
189
|
+
}
|
|
190
|
+
return o;
|
|
191
|
+
} else {
|
|
192
|
+
// schema validation
|
|
193
|
+
return asSchema(o, type);
|
|
194
|
+
}
|
|
195
|
+
} else {
|
|
196
|
+
throw new HttpError(500, `Unsupported type check ${type}`)
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
130
200
|
module.exports = {
|
|
131
201
|
getOrThrow,
|
|
132
202
|
getOrElse,
|
|
@@ -135,5 +205,6 @@ module.exports = {
|
|
|
135
205
|
asInteger,
|
|
136
206
|
asString,
|
|
137
207
|
asSchema,
|
|
138
|
-
schema
|
|
208
|
+
schema,
|
|
209
|
+
as
|
|
139
210
|
}
|
package/index.d.ts
CHANGED
|
@@ -1,21 +1,9 @@
|
|
|
1
|
-
import { Response } from "express"
|
|
2
|
-
import {
|
|
1
|
+
import { Response, Request } from "express"
|
|
2
|
+
import { ArraySchema, JsSchema, ValidTypeKeys } from "./helpers"
|
|
3
3
|
|
|
4
4
|
type RequestHandler = (req: Request, res: Response) => void
|
|
5
5
|
type RequsetHandlerWithArgs = (...args: any[]) => RequestHandler
|
|
6
6
|
|
|
7
|
-
declare class HttpError extends Error {
|
|
8
|
-
status: number
|
|
9
|
-
message: string
|
|
10
|
-
constructor(status: number, message: string)
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
declare class HttpResponse<T> {
|
|
14
|
-
status: number
|
|
15
|
-
data: T
|
|
16
|
-
constructor(status: number, data: T)
|
|
17
|
-
}
|
|
18
|
-
|
|
19
7
|
type RestedService<T extends Record<string, any>> = { [K in keyof T]:
|
|
20
8
|
T[K] extends Function
|
|
21
9
|
? (...args: Parameters<T[K]>) => RequestHandler
|
|
@@ -28,17 +16,17 @@ declare function RestMethod<T>(callback: () => T): RequestHandler;
|
|
|
28
16
|
declare function Restify(target: any, key: string, desc: PropertyDescriptor): PropertyDescriptor;
|
|
29
17
|
|
|
30
18
|
declare function PassBody(serviceFunction: RequestHandler | RequsetHandlerWithArgs): RequestHandler
|
|
31
|
-
declare function
|
|
19
|
+
declare function PassBodyAs(type: ValidTypeKeys | JsSchema | ArraySchema ): (serviceFunction: RequestHandler | RequsetHandlerWithArgs) => RequestHandler
|
|
32
20
|
declare function PassAllParams(serviceFunction: RequestHandler | RequsetHandlerWithArgs): RequestHandler
|
|
33
21
|
declare function PassAllQueries(serviceFunction: RequestHandler | RequsetHandlerWithArgs): RequestHandler
|
|
34
22
|
declare function PassAllCookies(serviceFunction: RequestHandler | RequsetHandlerWithArgs): RequestHandler
|
|
35
23
|
declare function PassParams(...paramNames: string[]): (serviceFunction: RequestHandler | RequsetHandlerWithArgs) => RequestHandler
|
|
36
24
|
declare function PassQueries(...queryNames: string[]): (serviceFunction: RequestHandler | RequsetHandlerWithArgs) => RequestHandler
|
|
37
|
-
declare function PassCookies(...cookieNames: string[]): (serviceFunction: RequestHandler |RequsetHandlerWithArgs) => RequestHandler
|
|
25
|
+
declare function PassCookies(...cookieNames: string[]): (serviceFunction: RequestHandler | RequsetHandlerWithArgs) => RequestHandler
|
|
26
|
+
declare function PassRequest(serviceFunction: RequestHandler | RequsetHandlerWithArgs): RequestHandler
|
|
27
|
+
declare function PassResponse(serviceFunction: RequestHandler | RequsetHandlerWithArgs): RequestHandler
|
|
38
28
|
|
|
39
29
|
export {
|
|
40
|
-
HttpError,
|
|
41
|
-
HttpResponse,
|
|
42
30
|
RestService,
|
|
43
31
|
RestMethod,
|
|
44
32
|
Restify,
|
|
@@ -49,5 +37,7 @@ export {
|
|
|
49
37
|
PassCookies,
|
|
50
38
|
PassAllCookies,
|
|
51
39
|
PassBody,
|
|
52
|
-
|
|
40
|
+
PassBodyAs,
|
|
41
|
+
PassRequest,
|
|
42
|
+
PassResponse
|
|
53
43
|
}
|
package/index.js
CHANGED
|
@@ -1,18 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
super(message);
|
|
4
|
-
this.stack += "\n" + message;
|
|
5
|
-
this.message = message;
|
|
6
|
-
this.status = status;
|
|
7
|
-
}
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
class HttpResponse {
|
|
11
|
-
constructor(status, data) {
|
|
12
|
-
this.status = status;
|
|
13
|
-
this.data = data;
|
|
14
|
-
}
|
|
15
|
-
}
|
|
1
|
+
const { as } = require("./helpers");
|
|
2
|
+
const { HttpError } = require("./types");
|
|
16
3
|
|
|
17
4
|
const protectedProperties = [
|
|
18
5
|
"toString",
|
|
@@ -21,6 +8,14 @@ const protectedProperties = [
|
|
|
21
8
|
"toLocaleString"
|
|
22
9
|
]
|
|
23
10
|
|
|
11
|
+
function reply(res, status, data) {
|
|
12
|
+
if (typeof data == "object") {
|
|
13
|
+
res.status(status).json(data);
|
|
14
|
+
} else {
|
|
15
|
+
res.status(status).send(data);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
24
19
|
function RestService(service) {
|
|
25
20
|
if (typeof service == "function") {
|
|
26
21
|
try {
|
|
@@ -49,9 +44,9 @@ function RestService(service) {
|
|
|
49
44
|
} else if (result === null) {
|
|
50
45
|
throw new HttpError(200, "Your method is executed but it returned null. At least a value is expected to be returned.");
|
|
51
46
|
}
|
|
52
|
-
res
|
|
47
|
+
reply(res, result.status || 200, result.data || result)
|
|
53
48
|
} catch (e) {
|
|
54
|
-
res
|
|
49
|
+
reply(res, e.status || 500, e.message || e);
|
|
55
50
|
}
|
|
56
51
|
}),
|
|
57
52
|
configurable: keyvalue[1].configurable,
|
|
@@ -71,10 +66,10 @@ function RestMethod(callback) {
|
|
|
71
66
|
return (req, res) => {
|
|
72
67
|
try {
|
|
73
68
|
const result = callback();
|
|
74
|
-
res
|
|
69
|
+
reply(res, result.status || 200, result.data || result);
|
|
75
70
|
return result;
|
|
76
71
|
} catch (e) {
|
|
77
|
-
res
|
|
72
|
+
reply(res, e.status || 500, e.message || e)
|
|
78
73
|
}
|
|
79
74
|
}
|
|
80
75
|
}
|
|
@@ -87,10 +82,10 @@ function Restify(target, key, desc) {
|
|
|
87
82
|
return (req, res) => {
|
|
88
83
|
try {
|
|
89
84
|
const result = oldFunc(...args);
|
|
90
|
-
res
|
|
85
|
+
reply(res, result.status || 200, result.data || result);
|
|
91
86
|
return result;
|
|
92
87
|
} catch (e) {
|
|
93
|
-
res
|
|
88
|
+
reply(res, e.status || 500, e.message || e);
|
|
94
89
|
}
|
|
95
90
|
}
|
|
96
91
|
}).bind(target)
|
|
@@ -171,6 +166,30 @@ function PassBody(actualHandler) {
|
|
|
171
166
|
}
|
|
172
167
|
}
|
|
173
168
|
|
|
169
|
+
function PassBodyAs(type) {
|
|
170
|
+
return (actualHandler) => {
|
|
171
|
+
return (...args) => {
|
|
172
|
+
if (isRequstHandlerArgs(args)) {
|
|
173
|
+
const req = args.at(-3); const res = args.at(-2);
|
|
174
|
+
try {
|
|
175
|
+
return actualHandler(as(req.body, type))(req, res);
|
|
176
|
+
} catch (e) {
|
|
177
|
+
reply(res, e.status || 500, e.message || e);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
} else {
|
|
181
|
+
return (req, res) => {
|
|
182
|
+
try{
|
|
183
|
+
return actualHandler(...args, as(req.body, type))(req, res);
|
|
184
|
+
}catch(e){
|
|
185
|
+
reply(res, e.status || 500, e.message || e)
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
174
193
|
function PassRequest(actualHandler) {
|
|
175
194
|
return (...args) => {
|
|
176
195
|
if (isRequstHandlerArgs(args)) {
|
|
@@ -182,6 +201,17 @@ function PassRequest(actualHandler) {
|
|
|
182
201
|
}
|
|
183
202
|
}
|
|
184
203
|
|
|
204
|
+
function PassResponse(actualHandler) {
|
|
205
|
+
return (...args) => {
|
|
206
|
+
if (isRequstHandlerArgs(args)) {
|
|
207
|
+
const req = args.at(-3); const res = args.at(-2);
|
|
208
|
+
return actualHandler(res)(req, res);
|
|
209
|
+
} else {
|
|
210
|
+
return (req, res) => actualHandler(...args, res)(req, res)
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
185
215
|
function PassAllCookies(actualHandler) {
|
|
186
216
|
return (...args) => {
|
|
187
217
|
if (isRequstHandlerArgs(args)) {
|
|
@@ -208,8 +238,6 @@ function PassCookies(...cookieNames) {
|
|
|
208
238
|
}
|
|
209
239
|
|
|
210
240
|
module.exports = {
|
|
211
|
-
HttpError,
|
|
212
|
-
HttpResponse,
|
|
213
241
|
RestService,
|
|
214
242
|
RestMethod,
|
|
215
243
|
Restify,
|
|
@@ -220,5 +248,7 @@ module.exports = {
|
|
|
220
248
|
PassAllCookies,
|
|
221
249
|
PassCookies,
|
|
222
250
|
PassBody,
|
|
223
|
-
|
|
251
|
+
PassBodyAs,
|
|
252
|
+
PassRequest,
|
|
253
|
+
PassResponse
|
|
224
254
|
}
|
package/package.json
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bootpress",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.1",
|
|
4
4
|
"description": "REST service methods for express",
|
|
5
5
|
"main": "index.js",
|
|
6
|
-
"scripts": {},
|
|
7
6
|
"repository": {
|
|
8
7
|
"type": "git",
|
|
9
8
|
"url": "git+https://github.com/ufukbakan/bootpress.git"
|
|
@@ -28,7 +27,7 @@
|
|
|
28
27
|
"express": "^4.18.2"
|
|
29
28
|
},
|
|
30
29
|
"devDependencies": {
|
|
31
|
-
"@types/
|
|
32
|
-
"
|
|
30
|
+
"@types/express": "^4.17.17",
|
|
31
|
+
"typescript": "^4.9.5"
|
|
33
32
|
}
|
|
34
33
|
}
|
package/types/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
class HttpError extends Error {
|
|
2
|
+
constructor(status, message) {
|
|
3
|
+
super(message);
|
|
4
|
+
this.stack += "\n" + message;
|
|
5
|
+
this.message = message;
|
|
6
|
+
this.status = status;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
class HttpResponse {
|
|
11
|
+
constructor(status, data) {
|
|
12
|
+
this.status = status;
|
|
13
|
+
this.data = data;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
module.exports = {
|
|
18
|
+
HttpError,
|
|
19
|
+
HttpResponse
|
|
20
|
+
}
|
package/types/index.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
declare class HttpError extends Error {
|
|
2
|
+
status: number
|
|
3
|
+
message: string
|
|
4
|
+
constructor(status: number, message: string)
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
declare class HttpResponse<T> {
|
|
8
|
+
status: number
|
|
9
|
+
data: T
|
|
10
|
+
constructor(status: number, data: T)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export {
|
|
14
|
+
HttpError,
|
|
15
|
+
HttpResponse
|
|
16
|
+
}
|