xpref 1.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/api-docs/body-schema.d.ts +1 -0
- package/api-docs/body-schema.js +31 -0
- package/api-docs/index.d.ts +3 -0
- package/api-docs/index.js +49 -0
- package/api-docs/parameter-schema.d.ts +1 -0
- package/api-docs/parameter-schema.js +57 -0
- package/api-docs/path-schema.d.ts +1 -0
- package/api-docs/path-schema.js +31 -0
- package/api-docs/path.d.ts +2 -0
- package/api-docs/path.js +79 -0
- package/api-docs/schema.d.ts +1 -0
- package/api-docs/schema.js +42 -0
- package/api-docs/security.d.ts +1 -0
- package/api-docs/security.js +48 -0
- package/api-docs/types.d.ts +27 -0
- package/api-docs/types.js +2 -0
- package/debug.d.ts +6 -0
- package/debug.js +26 -0
- package/i18n/index.d.ts +9 -0
- package/i18n/index.js +51 -0
- package/idempotency/index.d.ts +7 -0
- package/idempotency/index.js +130 -0
- package/idempotency/storage.d.ts +36 -0
- package/idempotency/storage.js +54 -0
- package/index.d.ts +7 -0
- package/index.js +126 -0
- package/package.json +36 -0
- package/request-forwarder/common.d.ts +11 -0
- package/request-forwarder/common.js +36 -0
- package/request-forwarder/forwarder.d.ts +2 -0
- package/request-forwarder/forwarder.js +81 -0
- package/request-forwarder/index.d.ts +2 -0
- package/request-forwarder/index.js +10 -0
- package/request-forwarder/pass-to-next.d.ts +4 -0
- package/request-forwarder/pass-to-next.js +48 -0
- package/request-forwarder/proxy.d.ts +8 -0
- package/request-forwarder/proxy.js +34 -0
- package/request-id.d.ts +7 -0
- package/request-id.js +19 -0
- package/request-log.d.ts +3 -0
- package/request-log.js +88 -0
- package/router.d.ts +13 -0
- package/router.js +65 -0
- package/setting/index.d.ts +1 -0
- package/setting/index.js +6 -0
- package/static.d.ts +2 -0
- package/static.js +12 -0
- package/types.d.ts +52 -0
- package/types.js +2 -0
- package/utils.d.ts +5 -0
- package/utils.js +22 -0
- package/validator/method-validator.d.ts +6 -0
- package/validator/method-validator.js +45 -0
- package/validator/route-schema.d.ts +2 -0
- package/validator/route-schema.js +71 -0
- package/validator/schema.d.ts +15 -0
- package/validator/schema.js +63 -0
- package/validator/validate.d.ts +8 -0
- package/validator/validate.js +94 -0
- package/validator/validator.test.d.ts +0 -0
- package/validator/validator.test.js +6 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function getBodySchemas(props: any): any;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = getBodySchemas;
|
|
4
|
+
const utils_1 = require("../utils");
|
|
5
|
+
const schemaFromString = (name) => {
|
|
6
|
+
const [schemaName, required] = (0, utils_1.decodeSchemaName)(name);
|
|
7
|
+
return {
|
|
8
|
+
[schemaName]: {
|
|
9
|
+
schema: { $ref: `#components/schemas/${schemaName}` },
|
|
10
|
+
required,
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
const getSchemasOfArrayProperties = (properties) => properties
|
|
15
|
+
.reduce((accu, item) => {
|
|
16
|
+
const isString = typeof item === 'string';
|
|
17
|
+
if (isString)
|
|
18
|
+
return { ...accu, ...schemaFromString(item) };
|
|
19
|
+
return accu;
|
|
20
|
+
}, {});
|
|
21
|
+
const getSchemasOfProperties = (properties) => {
|
|
22
|
+
const isArray = Array.isArray(properties);
|
|
23
|
+
if (!isArray)
|
|
24
|
+
return properties;
|
|
25
|
+
return getSchemasOfArrayProperties(properties);
|
|
26
|
+
};
|
|
27
|
+
function getBodySchemas(props) {
|
|
28
|
+
const { properties } = props;
|
|
29
|
+
const nextProps = getSchemasOfProperties(properties);
|
|
30
|
+
return { ...props, properties: nextProps };
|
|
31
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
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
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
17
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
18
|
+
};
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.default = setupApiDocs;
|
|
21
|
+
const swagger_ui_express_1 = require("swagger-ui-express");
|
|
22
|
+
const security_1 = __importDefault(require("./security"));
|
|
23
|
+
const path_1 = __importDefault(require("./path"));
|
|
24
|
+
const schema_1 = __importDefault(require("./schema"));
|
|
25
|
+
const getTags = (tags) => {
|
|
26
|
+
const tagNames = Object.keys(tags);
|
|
27
|
+
return tagNames.reduce((acc, name) => {
|
|
28
|
+
const tag = tags[name] || {};
|
|
29
|
+
return [...acc, { name, ...tag }];
|
|
30
|
+
}, []);
|
|
31
|
+
};
|
|
32
|
+
function setupApiDocs(apiDocs, options) {
|
|
33
|
+
const { routes, schemas } = options;
|
|
34
|
+
const { bearerAuth = false, tags = {}, apiKeys = [], } = apiDocs;
|
|
35
|
+
const [securitySchemas, security] = (0, security_1.default)({ bearerAuth, apiKeys });
|
|
36
|
+
const touchedSchemas = (0, schema_1.default)(schemas);
|
|
37
|
+
const components = { schemas: touchedSchemas, ...securitySchemas };
|
|
38
|
+
const paths = (0, path_1.default)(routes);
|
|
39
|
+
const docs = {
|
|
40
|
+
openapi: '3.0.0',
|
|
41
|
+
...apiDocs,
|
|
42
|
+
paths,
|
|
43
|
+
tags: getTags(tags),
|
|
44
|
+
components,
|
|
45
|
+
security,
|
|
46
|
+
};
|
|
47
|
+
return [swagger_ui_express_1.serve, (0, swagger_ui_express_1.setup)(docs)];
|
|
48
|
+
}
|
|
49
|
+
__exportStar(require("./types"), exports);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function getParameterSchemas(routeParams: any): any[];
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = getParameterSchemas;
|
|
4
|
+
const utils_1 = require("../utils");
|
|
5
|
+
const schemaFromString = (name, whereToBe = '') => {
|
|
6
|
+
const [schemaName, required] = (0, utils_1.decodeSchemaName)(name);
|
|
7
|
+
return {
|
|
8
|
+
name: schemaName,
|
|
9
|
+
schema: { $ref: `#components/schemas/${schemaName}` },
|
|
10
|
+
in: whereToBe,
|
|
11
|
+
required,
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
const inlineSchema = (schema, whereToBe = '') => {
|
|
15
|
+
const [name, props] = schema;
|
|
16
|
+
const [schemaName, required] = (0, utils_1.decodeSchemaName)(name);
|
|
17
|
+
const type = props.type || 'string';
|
|
18
|
+
if (type !== 'object')
|
|
19
|
+
return {
|
|
20
|
+
name: schemaName,
|
|
21
|
+
required,
|
|
22
|
+
in: whereToBe,
|
|
23
|
+
...props,
|
|
24
|
+
};
|
|
25
|
+
return {
|
|
26
|
+
name: schemaName,
|
|
27
|
+
parameters: getObjectSchema({ name: schemaName, ...props }, whereToBe),
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
const getSchemasOfObjectProperties = (properties, _whereToBe = '') => properties;
|
|
31
|
+
const getSchemasOfArrayProperties = (properties, whereToBe = '') => properties.map((name) => {
|
|
32
|
+
const isString = typeof name === 'string';
|
|
33
|
+
if (isString)
|
|
34
|
+
return schemaFromString(name, whereToBe);
|
|
35
|
+
return inlineSchema(name, whereToBe);
|
|
36
|
+
});
|
|
37
|
+
const getObjectSchema = (schema, whereToBe = '') => {
|
|
38
|
+
const type = schema.type || 'string';
|
|
39
|
+
if (type !== 'object')
|
|
40
|
+
return [{ type, ...schema, in: whereToBe }];
|
|
41
|
+
const { properties } = schema;
|
|
42
|
+
const isArray = Array.isArray(properties);
|
|
43
|
+
if (isArray)
|
|
44
|
+
return getSchemasOfArrayProperties(properties, whereToBe);
|
|
45
|
+
return getSchemasOfObjectProperties(properties, whereToBe);
|
|
46
|
+
};
|
|
47
|
+
function getParameterSchemas(routeParams) {
|
|
48
|
+
const { headers = [], params = [], query = [] } = routeParams;
|
|
49
|
+
const headersSchema = { type: 'object', properties: headers, name: 'headers' };
|
|
50
|
+
const paramsSchema = { type: 'object', properties: params, name: 'params' };
|
|
51
|
+
const querySchema = { type: 'object', properties: query, name: 'query' };
|
|
52
|
+
return [
|
|
53
|
+
...getObjectSchema(headersSchema, 'headers'),
|
|
54
|
+
...getObjectSchema(paramsSchema, 'path'),
|
|
55
|
+
...getObjectSchema(querySchema, 'query'),
|
|
56
|
+
];
|
|
57
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const getMethodParameters: (methodHandler: any) => {};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getMethodParameters = void 0;
|
|
7
|
+
const parameter_schema_1 = __importDefault(require("./parameter-schema"));
|
|
8
|
+
const body_schema_1 = __importDefault(require("./body-schema"));
|
|
9
|
+
const getMethodParameters = (methodHandler) => {
|
|
10
|
+
if (Array.isArray(methodHandler))
|
|
11
|
+
return {};
|
|
12
|
+
if (typeof methodHandler === 'function')
|
|
13
|
+
return {};
|
|
14
|
+
const { params } = methodHandler;
|
|
15
|
+
const parameters = {
|
|
16
|
+
parameters: (0, parameter_schema_1.default)(params),
|
|
17
|
+
};
|
|
18
|
+
const { body = [] } = params;
|
|
19
|
+
if (body.length === 0)
|
|
20
|
+
return parameters;
|
|
21
|
+
const content = {
|
|
22
|
+
'application/json': {
|
|
23
|
+
schema: (0, body_schema_1.default)({
|
|
24
|
+
type: 'object',
|
|
25
|
+
properties: body,
|
|
26
|
+
}),
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
return { ...parameters, requestBody: { content } };
|
|
30
|
+
};
|
|
31
|
+
exports.getMethodParameters = getMethodParameters;
|
package/api-docs/path.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = getPaths;
|
|
4
|
+
const path_schema_1 = require("./path-schema");
|
|
5
|
+
const defaultResponse = {
|
|
6
|
+
200: {
|
|
7
|
+
description: 'Operation completed successfully.',
|
|
8
|
+
},
|
|
9
|
+
};
|
|
10
|
+
const decodePath = (path) => path.replace(/:(\w*)/g, '{$1}');
|
|
11
|
+
const getMethodDescription = (methodHandler = {}) => {
|
|
12
|
+
if (Array.isArray(methodHandler) || typeof methodHandler === 'function') {
|
|
13
|
+
return {};
|
|
14
|
+
}
|
|
15
|
+
const { description = [] } = methodHandler;
|
|
16
|
+
const [summary, desc = ''] = Array.isArray(description) ? description : [description, ''];
|
|
17
|
+
return { summary, description: desc };
|
|
18
|
+
};
|
|
19
|
+
const getResponses = (methodProps = {}) => {
|
|
20
|
+
const { responses = {} } = methodProps;
|
|
21
|
+
const allResponse = { ...defaultResponse, ...responses };
|
|
22
|
+
return Object.entries(allResponse).reduce((accu, response) => {
|
|
23
|
+
const [code, res] = response;
|
|
24
|
+
const touchedResponse = { contentType: 'application/json', ...res };
|
|
25
|
+
return { ...accu, [code]: touchedResponse };
|
|
26
|
+
}, {});
|
|
27
|
+
};
|
|
28
|
+
const getMethodPath = (tag, methodProps) => {
|
|
29
|
+
const desc = getMethodDescription(methodProps);
|
|
30
|
+
const params = (0, path_schema_1.getMethodParameters)(methodProps);
|
|
31
|
+
const responses = getResponses(methodProps);
|
|
32
|
+
return {
|
|
33
|
+
responses,
|
|
34
|
+
tags: [tag],
|
|
35
|
+
...desc,
|
|
36
|
+
...params,
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
const generateMethodsPath = (props, { tag }) => {
|
|
40
|
+
const methods = Object.keys(props);
|
|
41
|
+
return methods.reduce((carry, method) => {
|
|
42
|
+
const methodDetail = props[method];
|
|
43
|
+
const result = {
|
|
44
|
+
...carry,
|
|
45
|
+
[method]: getMethodPath(tag, methodDetail),
|
|
46
|
+
};
|
|
47
|
+
return result;
|
|
48
|
+
}, {});
|
|
49
|
+
};
|
|
50
|
+
const generatePath = (pPath, pathProps, { parentTag = false } = {}) => {
|
|
51
|
+
const path = decodePath(pPath);
|
|
52
|
+
const [tag, middlewares, methodHandler, children = {}] = pathProps;
|
|
53
|
+
const curTag = parentTag || tag;
|
|
54
|
+
const result = {
|
|
55
|
+
[path]: generateMethodsPath(methodHandler, { tag: curTag, middlewares }),
|
|
56
|
+
};
|
|
57
|
+
const childPaths = Object.keys(children);
|
|
58
|
+
if (childPaths.length === 0)
|
|
59
|
+
return result;
|
|
60
|
+
const paths = childPaths.reduce((carry, childPath) => {
|
|
61
|
+
const childProps = children[childPath];
|
|
62
|
+
return {
|
|
63
|
+
...carry,
|
|
64
|
+
...generatePath(`${path}${childPath}`, childProps, { parentTag: curTag }),
|
|
65
|
+
};
|
|
66
|
+
}, result);
|
|
67
|
+
return paths;
|
|
68
|
+
};
|
|
69
|
+
function getPaths(routes) {
|
|
70
|
+
const paths = Object.keys(routes);
|
|
71
|
+
const pathResult = paths.reduce((carry, path) => {
|
|
72
|
+
const pathProps = routes[path];
|
|
73
|
+
return {
|
|
74
|
+
...carry,
|
|
75
|
+
...generatePath(path, pathProps),
|
|
76
|
+
};
|
|
77
|
+
}, {});
|
|
78
|
+
return pathResult;
|
|
79
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function initSchemas(schemaObj: any): any;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = initSchemas;
|
|
4
|
+
const defaultSchema = {
|
|
5
|
+
type: 'string',
|
|
6
|
+
example: 'String value here',
|
|
7
|
+
description: 'Pre-defined string schema',
|
|
8
|
+
};
|
|
9
|
+
const touchObjectProperties = (names) => names.reduce((accu, name) => {
|
|
10
|
+
if (typeof name === 'string')
|
|
11
|
+
return {
|
|
12
|
+
...accu,
|
|
13
|
+
[name]: { '$ref': `#/components/schemas/${name}` },
|
|
14
|
+
};
|
|
15
|
+
const [schemaName, schemaProps] = name;
|
|
16
|
+
return {
|
|
17
|
+
...accu,
|
|
18
|
+
[schemaName]: senitizeSchema(schemaProps),
|
|
19
|
+
};
|
|
20
|
+
}, {});
|
|
21
|
+
const senitizeObjectSchema = (schemaProps) => {
|
|
22
|
+
const { properties } = schemaProps;
|
|
23
|
+
const touchedProperties = touchObjectProperties(properties);
|
|
24
|
+
return { ...schemaProps, properties: touchedProperties };
|
|
25
|
+
};
|
|
26
|
+
const senitizeSchema = (schemaProps) => {
|
|
27
|
+
const type = schemaProps.type || 'string';
|
|
28
|
+
if (type !== 'object')
|
|
29
|
+
return { ...defaultSchema, ...schemaProps };
|
|
30
|
+
return senitizeObjectSchema(schemaProps);
|
|
31
|
+
};
|
|
32
|
+
function initSchemas(schemaObj) {
|
|
33
|
+
const schemas = Object.keys(schemaObj);
|
|
34
|
+
return schemas.reduce((acc, schemaName) => {
|
|
35
|
+
const schemaProps = schemaObj[schemaName];
|
|
36
|
+
const schema = senitizeSchema(schemaProps);
|
|
37
|
+
return ({
|
|
38
|
+
...acc,
|
|
39
|
+
[schemaName]: schema,
|
|
40
|
+
});
|
|
41
|
+
}, {});
|
|
42
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function setSecurity(props: any): any;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = setSecurity;
|
|
4
|
+
const bearer = (bearerAuth = false) => {
|
|
5
|
+
if (bearerAuth)
|
|
6
|
+
return {};
|
|
7
|
+
return {
|
|
8
|
+
bearerAuth: {
|
|
9
|
+
type: 'http',
|
|
10
|
+
scheme: 'bearer',
|
|
11
|
+
bearerFormat: 'JWT',
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
const getApiKeys = (apiKeys) => {
|
|
16
|
+
if (apiKeys.length === 0)
|
|
17
|
+
return {};
|
|
18
|
+
return apiKeys.reduce((carry, apiKey) => {
|
|
19
|
+
const [name, where] = Array.isArray(apiKey)
|
|
20
|
+
? apiKey
|
|
21
|
+
: [apiKey, 'header'];
|
|
22
|
+
return {
|
|
23
|
+
...carry,
|
|
24
|
+
[name]: {
|
|
25
|
+
name,
|
|
26
|
+
type: 'apiKey',
|
|
27
|
+
in: where,
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}, {});
|
|
31
|
+
};
|
|
32
|
+
function setSecurity(props) {
|
|
33
|
+
const { bearerAuth = false, apiKeys = [] } = props;
|
|
34
|
+
const res = {
|
|
35
|
+
...bearer(bearerAuth),
|
|
36
|
+
...getApiKeys(apiKeys),
|
|
37
|
+
};
|
|
38
|
+
const keys = Object.keys(res);
|
|
39
|
+
if (keys.length === 0)
|
|
40
|
+
return [{}, {}];
|
|
41
|
+
const globalSecurities = keys.map((key) => ({
|
|
42
|
+
[key]: [],
|
|
43
|
+
}));
|
|
44
|
+
return [
|
|
45
|
+
{ securitySchemes: res },
|
|
46
|
+
globalSecurities,
|
|
47
|
+
];
|
|
48
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
type Info = {
|
|
2
|
+
version: string;
|
|
3
|
+
title: string;
|
|
4
|
+
description: string;
|
|
5
|
+
};
|
|
6
|
+
type ExternalDocs = {
|
|
7
|
+
description: string;
|
|
8
|
+
url: string;
|
|
9
|
+
};
|
|
10
|
+
export type Tag = {
|
|
11
|
+
description: string;
|
|
12
|
+
externalDocs?: ExternalDocs;
|
|
13
|
+
};
|
|
14
|
+
export type APIKey = Array<string | string[]>;
|
|
15
|
+
export type APIDocs = [any, CallableFunction];
|
|
16
|
+
export type APIDocsOptions = {
|
|
17
|
+
routes: Record<string, any>;
|
|
18
|
+
schemas: Record<string, any>;
|
|
19
|
+
};
|
|
20
|
+
export type APIDocsProps = {
|
|
21
|
+
info: Info;
|
|
22
|
+
tags?: Record<string, Tag>;
|
|
23
|
+
bearerAuth?: boolean;
|
|
24
|
+
apiKeys?: APIKey;
|
|
25
|
+
paths?: any;
|
|
26
|
+
};
|
|
27
|
+
export {};
|
package/debug.d.ts
ADDED
package/debug.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = logDebug;
|
|
4
|
+
/* eslint-disable no-console */
|
|
5
|
+
function getRaiser(error) {
|
|
6
|
+
const regex = /\((.*):(\d+):(\d+)\)$/;
|
|
7
|
+
const found = regex.exec(error.stack.split('\n')[2]) || false;
|
|
8
|
+
if (!found)
|
|
9
|
+
return {};
|
|
10
|
+
const [path] = found;
|
|
11
|
+
const { message } = error;
|
|
12
|
+
return {
|
|
13
|
+
path,
|
|
14
|
+
error: message,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Gets the error information for debugin
|
|
19
|
+
* @param {Error} error - The Error object
|
|
20
|
+
* @return Object
|
|
21
|
+
*/
|
|
22
|
+
function logDebug(error) {
|
|
23
|
+
const res = getRaiser(error);
|
|
24
|
+
const msg = { ...res, source: 'xpressify' };
|
|
25
|
+
console.log(msg);
|
|
26
|
+
}
|
package/i18n/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
type Locale = Record<string, string>;
|
|
2
|
+
type I18n = {
|
|
3
|
+
locale: Locale;
|
|
4
|
+
fallbackLang?: string;
|
|
5
|
+
fallbackLangOnly?: boolean;
|
|
6
|
+
};
|
|
7
|
+
declare const translate: (key: string, replace?: Record<string, any>, pLang?: string) => string;
|
|
8
|
+
export default function initI18n(props: I18n): typeof translate;
|
|
9
|
+
export {};
|
package/i18n/index.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = initI18n;
|
|
4
|
+
const fs_1 = require("fs");
|
|
5
|
+
const singletonLocale = {};
|
|
6
|
+
let privProps = {
|
|
7
|
+
fallbackLang: 'en',
|
|
8
|
+
fallbackLangOnly: false,
|
|
9
|
+
};
|
|
10
|
+
const toLowerCase = (data) => {
|
|
11
|
+
const entries = Object.entries(data);
|
|
12
|
+
return entries.reduce((accu, item) => {
|
|
13
|
+
const [key, value] = item;
|
|
14
|
+
return { ...accu, [key.toLowerCase()]: value };
|
|
15
|
+
}, {});
|
|
16
|
+
};
|
|
17
|
+
const loadLocalContent = (locale) => {
|
|
18
|
+
Object.entries(locale).forEach((localeItem) => {
|
|
19
|
+
const [localeName, localePath] = localeItem;
|
|
20
|
+
const data = (0, fs_1.readFileSync)(localePath, 'utf8');
|
|
21
|
+
const jsonData = JSON.parse(data === '' ? '{}' : data);
|
|
22
|
+
singletonLocale[localeName] = toLowerCase(jsonData);
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
const localeEscape = (str, obj = {}) => {
|
|
26
|
+
const keys = Object.keys(obj);
|
|
27
|
+
return keys.reduce((carry, placeholder) => {
|
|
28
|
+
const nextResult = carry.replace(`{${placeholder}}`, obj[placeholder]);
|
|
29
|
+
return nextResult;
|
|
30
|
+
}, str);
|
|
31
|
+
};
|
|
32
|
+
const getLang = (lang) => {
|
|
33
|
+
const { fallbackLangOnly, fallbackLang } = privProps;
|
|
34
|
+
if (fallbackLangOnly)
|
|
35
|
+
return fallbackLang;
|
|
36
|
+
return lang;
|
|
37
|
+
};
|
|
38
|
+
const translate = (key, replace = {}, pLang = 'en') => {
|
|
39
|
+
const lang = getLang(pLang);
|
|
40
|
+
const { fallbackLang } = privProps;
|
|
41
|
+
const i18n = singletonLocale[lang] || singletonLocale[fallbackLang] || {};
|
|
42
|
+
const translation = i18n[key.toLowerCase()] || key;
|
|
43
|
+
const translated = localeEscape(translation, replace);
|
|
44
|
+
return translated;
|
|
45
|
+
};
|
|
46
|
+
function initI18n(props) {
|
|
47
|
+
const { locale, fallbackLang = 'en', fallbackLangOnly = false } = props;
|
|
48
|
+
privProps = { fallbackLang, fallbackLangOnly };
|
|
49
|
+
loadLocalContent(locale);
|
|
50
|
+
return translate;
|
|
51
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = idempotency;
|
|
4
|
+
/* eslint-disable no-console */
|
|
5
|
+
const stream_1 = require("stream");
|
|
6
|
+
const storage_1 = require("./storage");
|
|
7
|
+
const getContentToStore = (response, chunks) => {
|
|
8
|
+
const { statusCode } = response;
|
|
9
|
+
const headers = {
|
|
10
|
+
'content-type': 'application/json',
|
|
11
|
+
...response.getHeaders(),
|
|
12
|
+
};
|
|
13
|
+
const content = {
|
|
14
|
+
statusCode,
|
|
15
|
+
headers,
|
|
16
|
+
chunks,
|
|
17
|
+
};
|
|
18
|
+
return content;
|
|
19
|
+
};
|
|
20
|
+
const parseBody = (chunks) => {
|
|
21
|
+
const decoded = Buffer.from(chunks.join('')).toString('utf8');
|
|
22
|
+
try {
|
|
23
|
+
return JSON.parse(decoded);
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
const { message } = error;
|
|
27
|
+
console.error(`[ERROR] API - ${message}`);
|
|
28
|
+
return {};
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Validate the rsponse content, by default it validate the HTTP Status Code (200, 201),
|
|
33
|
+
* but in some case, the response is not only validated by status code, so we can
|
|
34
|
+
* inject the callback function with maniplucated content as parameter. The callback function
|
|
35
|
+
* returns boolean value indicate if the response is success or not.
|
|
36
|
+
*/
|
|
37
|
+
const isSuccessResponse = (content, validateResponse = false) => {
|
|
38
|
+
const { statusCode, chunks } = content;
|
|
39
|
+
const isSuccess = (statusCode === 200 || statusCode === 201);
|
|
40
|
+
if (!validateResponse)
|
|
41
|
+
return isSuccess;
|
|
42
|
+
const body = parseBody(chunks);
|
|
43
|
+
return isSuccess && validateResponse({ ...content, body });
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Handles the streaming between middleware
|
|
47
|
+
* @param {Response} res - ExpressJS HTTP Response object
|
|
48
|
+
* @param {NextFuction} next - ExpressJS next function
|
|
49
|
+
* @param {Object} props - Injected properties
|
|
50
|
+
*/
|
|
51
|
+
const handleResponse = (res, next, props = {}) => {
|
|
52
|
+
const { idempotencyKey, validateResponse } = props;
|
|
53
|
+
(0, storage_1.create)(idempotencyKey);
|
|
54
|
+
const [oldWrite, oldEnd] = [res.write, res.end];
|
|
55
|
+
const chunks = [];
|
|
56
|
+
res.write = function (chunk) {
|
|
57
|
+
chunks.push(Buffer.from(chunk));
|
|
58
|
+
oldWrite.apply(res, arguments);
|
|
59
|
+
};
|
|
60
|
+
res.end = function (chunk) {
|
|
61
|
+
if (chunk)
|
|
62
|
+
chunks.push(Buffer.from(chunk));
|
|
63
|
+
const content = getContentToStore(res, chunks);
|
|
64
|
+
const success = isSuccessResponse(content, validateResponse);
|
|
65
|
+
if (success)
|
|
66
|
+
(0, storage_1.store)(idempotencyKey, content);
|
|
67
|
+
else
|
|
68
|
+
(0, storage_1.deleteItem)(idempotencyKey);
|
|
69
|
+
oldEnd.apply(res, arguments);
|
|
70
|
+
};
|
|
71
|
+
return next();
|
|
72
|
+
};
|
|
73
|
+
/*
|
|
74
|
+
* Writes the saved response back to the client, this write only
|
|
75
|
+
* just the content, but also the previous response headers. This
|
|
76
|
+
* to make sure the content is written back to the client is exactly
|
|
77
|
+
* the same as the result.
|
|
78
|
+
*/
|
|
79
|
+
function writeResponse(res, item) {
|
|
80
|
+
const { chunks, headers, statusCode } = item.response;
|
|
81
|
+
res.writeHeader(statusCode, {
|
|
82
|
+
'content-type': 'application/json',
|
|
83
|
+
'cached-source': 'cache/idempotency',
|
|
84
|
+
...headers,
|
|
85
|
+
});
|
|
86
|
+
return stream_1.Readable.from(chunks).pipe(res);
|
|
87
|
+
}
|
|
88
|
+
;
|
|
89
|
+
function idempotency(props = {}) {
|
|
90
|
+
const { headerKey = 'idempotency-key', enforced = false, ttl = (60 * 5), validateResponse = false, } = props;
|
|
91
|
+
return (req, res, next) => {
|
|
92
|
+
const { headers, method = 'GET' } = req;
|
|
93
|
+
if (method.toUpperCase() !== 'POST')
|
|
94
|
+
return next();
|
|
95
|
+
const idempotencyKey = headers[headerKey] || false;
|
|
96
|
+
if (!enforced && !idempotencyKey)
|
|
97
|
+
return next();
|
|
98
|
+
if (enforced && !idempotencyKey) {
|
|
99
|
+
return res.status(422).json({
|
|
100
|
+
message: 'Idempotency key required for this request',
|
|
101
|
+
error: true,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
if (!idempotencyKey)
|
|
105
|
+
return next();
|
|
106
|
+
if (!(0, storage_1.exists)(idempotencyKey))
|
|
107
|
+
return handleResponse(res, next, {
|
|
108
|
+
idempotencyKey,
|
|
109
|
+
validateResponse,
|
|
110
|
+
});
|
|
111
|
+
const item = (0, storage_1.getItem)(idempotencyKey);
|
|
112
|
+
const { createdAt } = item;
|
|
113
|
+
const now = new Date();
|
|
114
|
+
const timelapse = Math.ceil((now.valueOf() - createdAt.valueOf()) / 1000);
|
|
115
|
+
if (timelapse > ttl) {
|
|
116
|
+
return res.status(410).json({
|
|
117
|
+
message: 'Request using this idempotency key has expired.',
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
if (item.status === storage_1.status.IN_PROGERSS) {
|
|
121
|
+
return res.status(202).json({
|
|
122
|
+
at: new Date(),
|
|
123
|
+
message: 'Processing',
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
if (item.status === storage_1.status.DONE)
|
|
127
|
+
return writeResponse(res, item);
|
|
128
|
+
return next();
|
|
129
|
+
};
|
|
130
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
type Status = 'in-progress' | 'done';
|
|
2
|
+
type Response = {
|
|
3
|
+
statusCode: number;
|
|
4
|
+
headers: Record<string, any>;
|
|
5
|
+
chunks: any;
|
|
6
|
+
};
|
|
7
|
+
type Item = {
|
|
8
|
+
createdAt: Date;
|
|
9
|
+
status: Status;
|
|
10
|
+
referenceNumber?: string | number;
|
|
11
|
+
response?: Response;
|
|
12
|
+
};
|
|
13
|
+
export declare const status: Record<string, string>;
|
|
14
|
+
export declare const getAll: () => Record<string, Item>;
|
|
15
|
+
export declare const exists: (key: string) => boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Creates the storage with minimal information (the predetermined default value)
|
|
18
|
+
* @param {string} key - The key to be added into storage.
|
|
19
|
+
*/
|
|
20
|
+
export declare const create: (key: string) => any;
|
|
21
|
+
/**
|
|
22
|
+
* Gets the stored item by key
|
|
23
|
+
* @param {string} key - Storage key
|
|
24
|
+
* @return object;
|
|
25
|
+
*/
|
|
26
|
+
export declare const getItem: (key: string) => Item | any;
|
|
27
|
+
/**
|
|
28
|
+
* Stores the response object to be used when passing the stored
|
|
29
|
+
* result back to the client. This make sure the previouse response
|
|
30
|
+
* (stored response) is sent back.
|
|
31
|
+
* @param {string} key - The key of the storage
|
|
32
|
+
* @param {Response} response - The response information
|
|
33
|
+
*/
|
|
34
|
+
export declare const store: (key: string, response: Response) => void;
|
|
35
|
+
export declare const deleteItem: (key: string) => void;
|
|
36
|
+
export {};
|