@aeriajs/http 0.0.193 → 0.0.195
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/cors.js +1 -5
- package/dist/index.js +4 -20
- package/dist/log.js +7 -11
- package/dist/payload.js +1 -5
- package/dist/routing.js +44 -51
- package/package.json +8 -12
- package/dist/cors.mjs +0 -23
- package/dist/index.mjs +0 -5
- package/dist/log.mjs +0 -20
- package/dist/payload.mjs +0 -12
- package/dist/routing.mjs +0 -233
package/dist/cors.js
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.cors = void 0;
|
|
4
|
-
const cors = async (req, res, config) => {
|
|
1
|
+
export const cors = async (req, res, config) => {
|
|
5
2
|
const { allowOrigin = [], allowMethods = [], allowHeaders = [], maxAge, } = config;
|
|
6
3
|
const headers = {
|
|
7
4
|
'Access-Control-Allow-Origin': allowOrigin.join(','),
|
|
@@ -18,4 +15,3 @@ const cors = async (req, res, config) => {
|
|
|
18
15
|
res.setHeader(key, value);
|
|
19
16
|
});
|
|
20
17
|
};
|
|
21
|
-
exports.cors = cors;
|
package/dist/index.js
CHANGED
|
@@ -1,20 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
-
};
|
|
16
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("./cors.js"), exports);
|
|
18
|
-
__exportStar(require("./log.js"), exports);
|
|
19
|
-
__exportStar(require("./routing.js"), exports);
|
|
20
|
-
__exportStar(require("./payload.js"), exports);
|
|
1
|
+
export * from './cors.js';
|
|
2
|
+
export * from './log.js';
|
|
3
|
+
export * from './routing.js';
|
|
4
|
+
export * from './payload.js';
|
package/dist/log.js
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const types_1 = require("@aeriajs/types");
|
|
5
|
-
const node_util_1 = require("node:util");
|
|
6
|
-
const logResponse = (response) => {
|
|
1
|
+
import { METHOD_COLORS } from '@aeriajs/types';
|
|
2
|
+
import { styleText } from 'node:util';
|
|
3
|
+
export const logResponse = (response) => {
|
|
7
4
|
const { statusCode, req: { method, url } } = response;
|
|
8
5
|
if (!method) {
|
|
9
6
|
return;
|
|
@@ -11,17 +8,16 @@ const logResponse = (response) => {
|
|
|
11
8
|
const statusColor = statusCode >= 400 && statusCode <= 599
|
|
12
9
|
? 'red'
|
|
13
10
|
: 'white';
|
|
14
|
-
const methodColor = method in
|
|
15
|
-
?
|
|
11
|
+
const methodColor = method in METHOD_COLORS
|
|
12
|
+
? METHOD_COLORS[method]
|
|
16
13
|
: 'white';
|
|
17
14
|
const now = new Date();
|
|
18
|
-
let line = `[${
|
|
15
|
+
let line = `[${styleText(statusColor, statusCode.toString())}] `;
|
|
19
16
|
line += `[${now.toLocaleString()}] `;
|
|
20
|
-
line +=
|
|
17
|
+
line += styleText([
|
|
21
18
|
'bold',
|
|
22
19
|
methodColor,
|
|
23
20
|
], method) + ' ';
|
|
24
21
|
line += url;
|
|
25
22
|
console.log(line);
|
|
26
23
|
};
|
|
27
|
-
exports.logResponse = logResponse;
|
package/dist/payload.js
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.safeJson = void 0;
|
|
4
|
-
const safeJson = (candidate) => {
|
|
1
|
+
export const safeJson = (candidate) => {
|
|
5
2
|
if (!candidate || typeof candidate !== 'string') {
|
|
6
3
|
return candidate;
|
|
7
4
|
}
|
|
@@ -12,4 +9,3 @@ const safeJson = (candidate) => {
|
|
|
12
9
|
}
|
|
13
10
|
return json;
|
|
14
11
|
};
|
|
15
|
-
exports.safeJson = safeJson;
|
package/dist/routing.js
CHANGED
|
@@ -1,43 +1,40 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const validation_1 = require("@aeriajs/validation");
|
|
9
|
-
const entrypoint_1 = require("@aeriajs/entrypoint");
|
|
10
|
-
const payload_js_1 = require("./payload.js");
|
|
1
|
+
import { Stream } from 'node:stream';
|
|
2
|
+
import { ObjectId } from 'mongodb';
|
|
3
|
+
import { Result, ACError, HTTPStatus, REQUEST_METHODS, STREAMED_RESPONSE } from '@aeriajs/types';
|
|
4
|
+
import { pipe, isGranted, deepMerge, endpointError } from '@aeriajs/common';
|
|
5
|
+
import { validateWithRefs } from '@aeriajs/validation';
|
|
6
|
+
import { getConfig } from '@aeriajs/entrypoint';
|
|
7
|
+
import { safeJson } from './payload.js';
|
|
11
8
|
const checkUnprocessable = async (what, schema, context, validateOptions = {}) => {
|
|
12
9
|
let result;
|
|
13
10
|
if (Array.isArray(schema)) {
|
|
14
11
|
for (const property of schema) {
|
|
15
|
-
result = await
|
|
12
|
+
result = await validateWithRefs(what, property, validateOptions);
|
|
16
13
|
if (!result.error) {
|
|
17
14
|
break;
|
|
18
15
|
}
|
|
19
16
|
}
|
|
20
17
|
}
|
|
21
18
|
else {
|
|
22
|
-
result = await
|
|
19
|
+
result = await validateWithRefs(what, schema, validateOptions);
|
|
23
20
|
}
|
|
24
21
|
const { error, result: validated } = result;
|
|
25
22
|
if (error) {
|
|
26
23
|
if ('code' in error) {
|
|
27
|
-
return context.error(
|
|
24
|
+
return context.error(HTTPStatus.UnprocessableContent, {
|
|
28
25
|
code: error.code,
|
|
29
26
|
details: error.details,
|
|
30
27
|
});
|
|
31
28
|
}
|
|
32
|
-
return context.error(
|
|
33
|
-
code:
|
|
29
|
+
return context.error(HTTPStatus.UnprocessableContent, {
|
|
30
|
+
code: ACError.MalformedInput,
|
|
34
31
|
message: 'the provided payload is unprocessable',
|
|
35
32
|
details: error,
|
|
36
33
|
});
|
|
37
34
|
}
|
|
38
|
-
return
|
|
35
|
+
return Result.result(validated);
|
|
39
36
|
};
|
|
40
|
-
const matches = (req, method, exp, options = {}, config) => {
|
|
37
|
+
export const matches = (req, method, exp, options = {}, config) => {
|
|
41
38
|
const base = config?.baseUrl && config.baseUrl !== '/'
|
|
42
39
|
? options.base
|
|
43
40
|
? `${config.baseUrl}${options.base}`
|
|
@@ -62,20 +59,19 @@ const matches = (req, method, exp, options = {}, config) => {
|
|
|
62
59
|
};
|
|
63
60
|
}
|
|
64
61
|
};
|
|
65
|
-
|
|
66
|
-
const
|
|
67
|
-
const
|
|
68
|
-
const match = (0, exports.matches)(context.request, method, exp, options, config);
|
|
62
|
+
export const registerRoute = async (context, method, exp, cb, contract, options = {}) => {
|
|
63
|
+
const config = await getConfig();
|
|
64
|
+
const match = matches(context.request, method, exp, options, config);
|
|
69
65
|
if (match) {
|
|
70
66
|
if (context.request.headers['content-type'] === 'application/json') {
|
|
71
67
|
try {
|
|
72
|
-
context.request.payload =
|
|
68
|
+
context.request.payload = deepMerge(safeJson(context.request.body), context.request.payload, {
|
|
73
69
|
arrays: false,
|
|
74
70
|
});
|
|
75
71
|
}
|
|
76
72
|
catch (err) {
|
|
77
|
-
return context.error(
|
|
78
|
-
code:
|
|
73
|
+
return context.error(HTTPStatus.UnprocessableContent, {
|
|
74
|
+
code: ACError.MalformedInput,
|
|
79
75
|
message: 'Invalid JSON',
|
|
80
76
|
});
|
|
81
77
|
}
|
|
@@ -83,13 +79,13 @@ const registerRoute = async (context, method, exp, cb, contract, options = {}) =
|
|
|
83
79
|
Object.assign(context.request, match);
|
|
84
80
|
if (contract) {
|
|
85
81
|
if (contract.streamed) {
|
|
86
|
-
context.response[
|
|
82
|
+
context.response[STREAMED_RESPONSE] = true;
|
|
87
83
|
}
|
|
88
84
|
if (contract.roles) {
|
|
89
|
-
const granted =
|
|
85
|
+
const granted = isGranted(contract.roles, context.token);
|
|
90
86
|
if (!granted) {
|
|
91
|
-
return context.error(
|
|
92
|
-
code:
|
|
87
|
+
return context.error(HTTPStatus.Unauthorized, {
|
|
88
|
+
code: ACError.AuthorizationError,
|
|
93
89
|
message: 'your roles dont grant access to this route',
|
|
94
90
|
});
|
|
95
91
|
}
|
|
@@ -99,10 +95,10 @@ const registerRoute = async (context, method, exp, cb, contract, options = {}) =
|
|
|
99
95
|
checkObjectIds: true,
|
|
100
96
|
coerceObjectIds: true,
|
|
101
97
|
context,
|
|
102
|
-
objectIdConstructor:
|
|
98
|
+
objectIdConstructor: ObjectId,
|
|
103
99
|
});
|
|
104
100
|
if (error) {
|
|
105
|
-
return
|
|
101
|
+
return Result.error(error);
|
|
106
102
|
}
|
|
107
103
|
}
|
|
108
104
|
if ('query' in contract && contract.query) {
|
|
@@ -110,10 +106,10 @@ const registerRoute = async (context, method, exp, cb, contract, options = {}) =
|
|
|
110
106
|
checkObjectIds: true,
|
|
111
107
|
coerce: true,
|
|
112
108
|
context,
|
|
113
|
-
objectIdConstructor:
|
|
109
|
+
objectIdConstructor: ObjectId,
|
|
114
110
|
});
|
|
115
111
|
if (error) {
|
|
116
|
-
return
|
|
112
|
+
return Result.error(error);
|
|
117
113
|
}
|
|
118
114
|
context.request.query = validated;
|
|
119
115
|
}
|
|
@@ -124,14 +120,13 @@ const registerRoute = async (context, method, exp, cb, contract, options = {}) =
|
|
|
124
120
|
: result;
|
|
125
121
|
}
|
|
126
122
|
};
|
|
127
|
-
|
|
128
|
-
const wrapRouteExecution = async (response, cb) => {
|
|
123
|
+
export const wrapRouteExecution = async (response, cb) => {
|
|
129
124
|
try {
|
|
130
125
|
const result = await cb();
|
|
131
|
-
if (response[
|
|
126
|
+
if (response[STREAMED_RESPONSE] && !result) {
|
|
132
127
|
return;
|
|
133
128
|
}
|
|
134
|
-
if (result instanceof
|
|
129
|
+
if (result instanceof Stream) {
|
|
135
130
|
try {
|
|
136
131
|
result.pipe(response);
|
|
137
132
|
}
|
|
@@ -141,7 +136,7 @@ const wrapRouteExecution = async (response, cb) => {
|
|
|
141
136
|
}
|
|
142
137
|
if (result === null) {
|
|
143
138
|
if (!response.headersSent) {
|
|
144
|
-
response.writeHead(
|
|
139
|
+
response.writeHead(HTTPStatus.NoContent);
|
|
145
140
|
}
|
|
146
141
|
response.end(result);
|
|
147
142
|
return;
|
|
@@ -154,19 +149,18 @@ const wrapRouteExecution = async (response, cb) => {
|
|
|
154
149
|
catch (e) {
|
|
155
150
|
console.trace(e);
|
|
156
151
|
if (!response.headersSent) {
|
|
157
|
-
response.writeHead(
|
|
152
|
+
response.writeHead(HTTPStatus.InternalServerError);
|
|
158
153
|
}
|
|
159
154
|
if (!response.writableEnded) {
|
|
160
|
-
return
|
|
161
|
-
httpStatus:
|
|
162
|
-
code:
|
|
155
|
+
return endpointError({
|
|
156
|
+
httpStatus: HTTPStatus.InternalServerError,
|
|
157
|
+
code: ACError.UnknownError,
|
|
163
158
|
message: 'Internal server error',
|
|
164
159
|
});
|
|
165
160
|
}
|
|
166
161
|
}
|
|
167
162
|
};
|
|
168
|
-
|
|
169
|
-
const createRouter = (options = {}) => {
|
|
163
|
+
export const createRouter = (options = {}) => {
|
|
170
164
|
const { exhaust } = options;
|
|
171
165
|
const routes = [];
|
|
172
166
|
const routesMeta = {};
|
|
@@ -176,7 +170,7 @@ const createRouter = (options = {}) => {
|
|
|
176
170
|
? method[0]
|
|
177
171
|
: method] = contract || null;
|
|
178
172
|
routes.push((_, context, groupOptions) => {
|
|
179
|
-
return
|
|
173
|
+
return registerRoute(context, method, exp, cb, contract, groupOptions || options);
|
|
180
174
|
});
|
|
181
175
|
};
|
|
182
176
|
const group = (exp, router, middleware) => {
|
|
@@ -185,14 +179,14 @@ const createRouter = (options = {}) => {
|
|
|
185
179
|
routesMeta[`${exp}${route}`] = router.routesMeta[route];
|
|
186
180
|
}
|
|
187
181
|
routes.push(async (_, context, groupOptions) => {
|
|
188
|
-
const config = await
|
|
182
|
+
const config = await getConfig();
|
|
189
183
|
const base = groupOptions
|
|
190
184
|
? groupOptions.base
|
|
191
185
|
: options.base;
|
|
192
186
|
newOptions.base = base && base !== '/'
|
|
193
187
|
? `${base}${exp}`
|
|
194
188
|
: exp;
|
|
195
|
-
const match =
|
|
189
|
+
const match = matches(context.request, null, config.baseUrl === '/'
|
|
196
190
|
? new RegExp(`^${newOptions.base}/`)
|
|
197
191
|
: new RegExp(`^${config.baseUrl}${newOptions.base}/`), newOptions);
|
|
198
192
|
if (match) {
|
|
@@ -206,7 +200,7 @@ const createRouter = (options = {}) => {
|
|
|
206
200
|
}
|
|
207
201
|
});
|
|
208
202
|
};
|
|
209
|
-
const routerPipe =
|
|
203
|
+
const routerPipe = pipe(routes, {
|
|
210
204
|
returnFirst: (value) => {
|
|
211
205
|
if (value !== undefined) {
|
|
212
206
|
return value;
|
|
@@ -223,8 +217,8 @@ const createRouter = (options = {}) => {
|
|
|
223
217
|
context.response = response;
|
|
224
218
|
const result = await routerPipe(undefined, context, options);
|
|
225
219
|
if (exhaust && result === undefined) {
|
|
226
|
-
return context.error(
|
|
227
|
-
code:
|
|
220
|
+
return context.error(HTTPStatus.NotFound, {
|
|
221
|
+
code: ACError.ResourceNotFound,
|
|
228
222
|
message: 'Not found',
|
|
229
223
|
});
|
|
230
224
|
}
|
|
@@ -233,11 +227,10 @@ const createRouter = (options = {}) => {
|
|
|
233
227
|
};
|
|
234
228
|
return new Proxy(router, {
|
|
235
229
|
get: (target, key) => {
|
|
236
|
-
if (
|
|
230
|
+
if (REQUEST_METHODS.includes(key)) {
|
|
237
231
|
return (...args) => target.route(key, ...args);
|
|
238
232
|
}
|
|
239
233
|
return target[key];
|
|
240
234
|
},
|
|
241
235
|
});
|
|
242
236
|
};
|
|
243
|
-
exports.createRouter = createRouter;
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aeriajs/http",
|
|
3
|
-
"
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.0.195",
|
|
4
5
|
"description": "## Installation",
|
|
5
6
|
"main": "dist/index.js",
|
|
6
7
|
"aeriaMain": "tests/fixtures/aeriaMain.js",
|
|
7
|
-
"module": "dist/index.mjs",
|
|
8
8
|
"types": "dist/index.d.ts",
|
|
9
9
|
"publishConfig": {
|
|
10
10
|
"access": "public"
|
|
@@ -15,8 +15,7 @@
|
|
|
15
15
|
"exports": {
|
|
16
16
|
".": {
|
|
17
17
|
"types": "./dist/index.d.ts",
|
|
18
|
-
"
|
|
19
|
-
"require": "./dist/index.js"
|
|
18
|
+
"default": "./dist/index.js"
|
|
20
19
|
}
|
|
21
20
|
},
|
|
22
21
|
"files": [
|
|
@@ -30,19 +29,16 @@
|
|
|
30
29
|
"mongodb": "^6.17.0"
|
|
31
30
|
},
|
|
32
31
|
"peerDependencies": {
|
|
33
|
-
"@aeriajs/common": "^0.0.
|
|
34
|
-
"@aeriajs/entrypoint": "^0.0.
|
|
35
|
-
"@aeriajs/types": "^0.0.
|
|
36
|
-
"@aeriajs/validation": "^0.0.
|
|
32
|
+
"@aeriajs/common": "^0.0.158",
|
|
33
|
+
"@aeriajs/entrypoint": "^0.0.165",
|
|
34
|
+
"@aeriajs/types": "^0.0.136",
|
|
35
|
+
"@aeriajs/validation": "^0.0.180",
|
|
37
36
|
"mongodb": "^6.17.0"
|
|
38
37
|
},
|
|
39
38
|
"scripts": {
|
|
40
39
|
"test": "vitest run",
|
|
41
40
|
"lint": "eslint .",
|
|
42
41
|
"lint:fix": "eslint . --fix",
|
|
43
|
-
"build": "
|
|
44
|
-
"build:cjs": "tsc",
|
|
45
|
-
"build:esm": "esbuild './src/**/*.ts' --outdir=dist --out-extension:.js=.mjs && pnpm build:esm-transform",
|
|
46
|
-
"build:esm-transform": "pnpm -w esm-transform $PWD/dist"
|
|
42
|
+
"build": "tsc"
|
|
47
43
|
}
|
|
48
44
|
}
|
package/dist/cors.mjs
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
export const cors = async (req, res, config) => {
|
|
3
|
-
const {
|
|
4
|
-
allowOrigin = [],
|
|
5
|
-
allowMethods = [],
|
|
6
|
-
allowHeaders = [],
|
|
7
|
-
maxAge
|
|
8
|
-
} = config;
|
|
9
|
-
const headers = {
|
|
10
|
-
"Access-Control-Allow-Origin": allowOrigin.join(","),
|
|
11
|
-
"Access-Control-Allow-Methods": allowMethods.join(","),
|
|
12
|
-
"Access-Control-Allow-Headers": allowHeaders.join(","),
|
|
13
|
-
"Access-Control-Max-Age": maxAge
|
|
14
|
-
};
|
|
15
|
-
if (req.method === "OPTIONS") {
|
|
16
|
-
res.writeHead(204, headers);
|
|
17
|
-
res.end();
|
|
18
|
-
return null;
|
|
19
|
-
}
|
|
20
|
-
Object.entries(headers).forEach(([key, value]) => {
|
|
21
|
-
res.setHeader(key, value);
|
|
22
|
-
});
|
|
23
|
-
};
|
package/dist/index.mjs
DELETED
package/dist/log.mjs
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
import { METHOD_COLORS } from "@aeriajs/types";
|
|
3
|
-
import { styleText } from "node:util";
|
|
4
|
-
export const logResponse = (response) => {
|
|
5
|
-
const { statusCode, req: { method, url } } = response;
|
|
6
|
-
if (!method) {
|
|
7
|
-
return;
|
|
8
|
-
}
|
|
9
|
-
const statusColor = statusCode >= 400 && statusCode <= 599 ? "red" : "white";
|
|
10
|
-
const methodColor = method in METHOD_COLORS ? METHOD_COLORS[method] : "white";
|
|
11
|
-
const now = /* @__PURE__ */ new Date();
|
|
12
|
-
let line = `[${styleText(statusColor, statusCode.toString())}] `;
|
|
13
|
-
line += `[${now.toLocaleString()}] `;
|
|
14
|
-
line += styleText([
|
|
15
|
-
"bold",
|
|
16
|
-
methodColor
|
|
17
|
-
], method) + " ";
|
|
18
|
-
line += url;
|
|
19
|
-
console.log(line);
|
|
20
|
-
};
|
package/dist/payload.mjs
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
export const safeJson = (candidate) => {
|
|
3
|
-
if (!candidate || typeof candidate !== "string") {
|
|
4
|
-
return candidate;
|
|
5
|
-
}
|
|
6
|
-
const json = JSON.parse(candidate);
|
|
7
|
-
if (json && typeof json === "object") {
|
|
8
|
-
delete json.constructor;
|
|
9
|
-
delete json.__proto__;
|
|
10
|
-
}
|
|
11
|
-
return json;
|
|
12
|
-
};
|
package/dist/routing.mjs
DELETED
|
@@ -1,233 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
import { Stream } from "node:stream";
|
|
3
|
-
import { ObjectId } from "mongodb";
|
|
4
|
-
import { Result, ACError, HTTPStatus, REQUEST_METHODS, STREAMED_RESPONSE } from "@aeriajs/types";
|
|
5
|
-
import { pipe, isGranted, deepMerge, endpointError } from "@aeriajs/common";
|
|
6
|
-
import { validateWithRefs } from "@aeriajs/validation";
|
|
7
|
-
import { getConfig } from "@aeriajs/entrypoint";
|
|
8
|
-
import { safeJson } from "./payload.mjs";
|
|
9
|
-
const checkUnprocessable = async (what, schema, context, validateOptions = {}) => {
|
|
10
|
-
let result;
|
|
11
|
-
if (Array.isArray(schema)) {
|
|
12
|
-
for (const property of schema) {
|
|
13
|
-
result = await validateWithRefs(what, property, validateOptions);
|
|
14
|
-
if (!result.error) {
|
|
15
|
-
break;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
} else {
|
|
19
|
-
result = await validateWithRefs(what, schema, validateOptions);
|
|
20
|
-
}
|
|
21
|
-
const { error, result: validated } = result;
|
|
22
|
-
if (error) {
|
|
23
|
-
if ("code" in error) {
|
|
24
|
-
return context.error(HTTPStatus.UnprocessableContent, {
|
|
25
|
-
code: error.code,
|
|
26
|
-
details: error.details
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
return context.error(HTTPStatus.UnprocessableContent, {
|
|
30
|
-
code: ACError.MalformedInput,
|
|
31
|
-
message: "the provided payload is unprocessable",
|
|
32
|
-
details: error
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
return Result.result(validated);
|
|
36
|
-
};
|
|
37
|
-
export const matches = (req, method, exp, options = {}, config) => {
|
|
38
|
-
const base = config?.baseUrl && config.baseUrl !== "/" ? options.base ? `${config.baseUrl}${options.base}` : config.baseUrl : options.base ? options.base : "";
|
|
39
|
-
if (method && method !== req.method) {
|
|
40
|
-
if (!Array.isArray(method) || !method.includes(req.method)) {
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
const regexp = exp instanceof RegExp ? exp : new RegExp(`^${base}${exp}$`);
|
|
45
|
-
const url = new URL(`http://0.com${req.url}`).pathname;
|
|
46
|
-
const expMatches = url.match(regexp);
|
|
47
|
-
if (expMatches) {
|
|
48
|
-
const fragments = expMatches.splice(1);
|
|
49
|
-
return {
|
|
50
|
-
fragments
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
};
|
|
54
|
-
export const registerRoute = async (context, method, exp, cb, contract, options = {}) => {
|
|
55
|
-
const config = await getConfig();
|
|
56
|
-
const match = matches(
|
|
57
|
-
context.request,
|
|
58
|
-
method,
|
|
59
|
-
exp,
|
|
60
|
-
options,
|
|
61
|
-
config
|
|
62
|
-
);
|
|
63
|
-
if (match) {
|
|
64
|
-
if (context.request.headers["content-type"] === "application/json") {
|
|
65
|
-
try {
|
|
66
|
-
context.request.payload = deepMerge(safeJson(context.request.body), context.request.payload, {
|
|
67
|
-
arrays: false
|
|
68
|
-
});
|
|
69
|
-
} catch (err) {
|
|
70
|
-
return context.error(HTTPStatus.UnprocessableContent, {
|
|
71
|
-
code: ACError.MalformedInput,
|
|
72
|
-
message: "Invalid JSON"
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
Object.assign(context.request, match);
|
|
77
|
-
if (contract) {
|
|
78
|
-
if (contract.streamed) {
|
|
79
|
-
context.response[STREAMED_RESPONSE] = true;
|
|
80
|
-
}
|
|
81
|
-
if (contract.roles) {
|
|
82
|
-
const granted = isGranted(contract.roles, context.token);
|
|
83
|
-
if (!granted) {
|
|
84
|
-
return context.error(HTTPStatus.Unauthorized, {
|
|
85
|
-
code: ACError.AuthorizationError,
|
|
86
|
-
message: "your roles dont grant access to this route"
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
if ("payload" in contract && contract.payload) {
|
|
91
|
-
const { error } = await checkUnprocessable(context.request.payload, contract.payload, context, {
|
|
92
|
-
checkObjectIds: true,
|
|
93
|
-
coerceObjectIds: true,
|
|
94
|
-
context,
|
|
95
|
-
objectIdConstructor: ObjectId
|
|
96
|
-
});
|
|
97
|
-
if (error) {
|
|
98
|
-
return Result.error(error);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
if ("query" in contract && contract.query) {
|
|
102
|
-
const { error, result: validated } = await checkUnprocessable(context.request.query, contract.query, context, {
|
|
103
|
-
checkObjectIds: true,
|
|
104
|
-
coerce: true,
|
|
105
|
-
context,
|
|
106
|
-
objectIdConstructor: ObjectId
|
|
107
|
-
});
|
|
108
|
-
if (error) {
|
|
109
|
-
return Result.error(error);
|
|
110
|
-
}
|
|
111
|
-
context.request.query = validated;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
const result = await cb(context);
|
|
115
|
-
return result === void 0 ? null : result;
|
|
116
|
-
}
|
|
117
|
-
};
|
|
118
|
-
export const wrapRouteExecution = async (response, cb) => {
|
|
119
|
-
try {
|
|
120
|
-
const result = await cb();
|
|
121
|
-
if (response[STREAMED_RESPONSE] && !result) {
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
if (result instanceof Stream) {
|
|
125
|
-
try {
|
|
126
|
-
result.pipe(response);
|
|
127
|
-
} catch (err) {
|
|
128
|
-
}
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
|
-
if (result === null) {
|
|
132
|
-
if (!response.headersSent) {
|
|
133
|
-
response.writeHead(HTTPStatus.NoContent);
|
|
134
|
-
}
|
|
135
|
-
response.end(result);
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
if (!response.writableEnded) {
|
|
139
|
-
response.end(result);
|
|
140
|
-
}
|
|
141
|
-
return result;
|
|
142
|
-
} catch (e) {
|
|
143
|
-
console.trace(e);
|
|
144
|
-
if (!response.headersSent) {
|
|
145
|
-
response.writeHead(HTTPStatus.InternalServerError);
|
|
146
|
-
}
|
|
147
|
-
if (!response.writableEnded) {
|
|
148
|
-
return endpointError({
|
|
149
|
-
httpStatus: HTTPStatus.InternalServerError,
|
|
150
|
-
code: ACError.UnknownError,
|
|
151
|
-
message: "Internal server error"
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
};
|
|
156
|
-
export const createRouter = (options = {}) => {
|
|
157
|
-
const { exhaust } = options;
|
|
158
|
-
const routes = [];
|
|
159
|
-
const routesMeta = {};
|
|
160
|
-
const route = (method, exp, cb, contract) => {
|
|
161
|
-
routesMeta[exp] ??= {};
|
|
162
|
-
routesMeta[exp][Array.isArray(method) ? method[0] : method] = contract || null;
|
|
163
|
-
routes.push((_, context, groupOptions) => {
|
|
164
|
-
return registerRoute(
|
|
165
|
-
context,
|
|
166
|
-
method,
|
|
167
|
-
exp,
|
|
168
|
-
cb,
|
|
169
|
-
contract,
|
|
170
|
-
groupOptions || options
|
|
171
|
-
);
|
|
172
|
-
});
|
|
173
|
-
};
|
|
174
|
-
const group = (exp, router2, middleware) => {
|
|
175
|
-
const newOptions = Object.assign({}, options);
|
|
176
|
-
for (const route2 in router2.routesMeta) {
|
|
177
|
-
routesMeta[`${exp}${route2}`] = router2.routesMeta[route2];
|
|
178
|
-
}
|
|
179
|
-
routes.push(async (_, context, groupOptions) => {
|
|
180
|
-
const config = await getConfig();
|
|
181
|
-
const base = groupOptions ? groupOptions.base : options.base;
|
|
182
|
-
newOptions.base = base && base !== "/" ? `${base}${exp}` : exp;
|
|
183
|
-
const match = matches(
|
|
184
|
-
context.request,
|
|
185
|
-
null,
|
|
186
|
-
config.baseUrl === "/" ? new RegExp(`^${newOptions.base}/`) : new RegExp(`^${config.baseUrl}${newOptions.base}/`),
|
|
187
|
-
newOptions
|
|
188
|
-
);
|
|
189
|
-
if (match) {
|
|
190
|
-
if (middleware) {
|
|
191
|
-
const result = await middleware(context);
|
|
192
|
-
if (result) {
|
|
193
|
-
return result;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
return router2.handle(context.request, context.response, context, newOptions);
|
|
197
|
-
}
|
|
198
|
-
});
|
|
199
|
-
};
|
|
200
|
-
const routerPipe = pipe(routes, {
|
|
201
|
-
returnFirst: (value) => {
|
|
202
|
-
if (value !== void 0) {
|
|
203
|
-
return value;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
});
|
|
207
|
-
const router = {
|
|
208
|
-
route,
|
|
209
|
-
routes,
|
|
210
|
-
routesMeta,
|
|
211
|
-
group,
|
|
212
|
-
handle: async (request, response, context, options2) => {
|
|
213
|
-
context.request = request;
|
|
214
|
-
context.response = response;
|
|
215
|
-
const result = await routerPipe(void 0, context, options2);
|
|
216
|
-
if (exhaust && result === void 0) {
|
|
217
|
-
return context.error(HTTPStatus.NotFound, {
|
|
218
|
-
code: ACError.ResourceNotFound,
|
|
219
|
-
message: "Not found"
|
|
220
|
-
});
|
|
221
|
-
}
|
|
222
|
-
return result;
|
|
223
|
-
}
|
|
224
|
-
};
|
|
225
|
-
return new Proxy(router, {
|
|
226
|
-
get: (target, key) => {
|
|
227
|
-
if (REQUEST_METHODS.includes(key)) {
|
|
228
|
-
return (...args) => target.route(key, ...args);
|
|
229
|
-
}
|
|
230
|
-
return target[key];
|
|
231
|
-
}
|
|
232
|
-
});
|
|
233
|
-
};
|