equipped 5.0.6 → 5.0.8
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/CHANGELOG.md +4 -0
- package/dist/cjs/cache.cjs +11 -0
- package/dist/cjs/cache.cjs.map +1 -0
- package/dist/cjs/chunk-UBQPLHM4.cjs +2507 -0
- package/dist/cjs/chunk-UBQPLHM4.cjs.map +1 -0
- package/dist/cjs/dbs.cjs +27 -0
- package/dist/cjs/dbs.cjs.map +1 -0
- package/dist/cjs/errors.cjs +23 -0
- package/dist/cjs/errors.cjs.map +1 -0
- package/dist/cjs/events.cjs +17 -0
- package/dist/cjs/events.cjs.map +1 -0
- package/dist/cjs/index.cjs.map +1 -0
- package/dist/cjs/jobs.cjs +9 -0
- package/dist/cjs/jobs.cjs.map +1 -0
- package/dist/cjs/server.cjs +41 -0
- package/dist/cjs/server.cjs.map +1 -0
- package/dist/cjs/types.cjs +1 -0
- package/dist/cjs/types.cjs.map +1 -0
- package/dist/cjs/utilities.cjs +21 -0
- package/dist/cjs/utilities.cjs.map +1 -0
- package/dist/cjs/validations.cjs +11 -0
- package/dist/cjs/validations.cjs.map +1 -0
- package/dist/esm/cache.min.mjs +2 -0
- package/dist/esm/cache.min.mjs.map +1 -0
- package/dist/esm/cache.mjs +11 -0
- package/dist/esm/cache.mjs.map +1 -0
- package/dist/esm/chunk-SHJLDB4M.mjs +2507 -0
- package/dist/esm/chunk-SHJLDB4M.mjs.map +1 -0
- package/dist/esm/chunk-WKZT3EQO.min.mjs +32 -0
- package/dist/esm/chunk-WKZT3EQO.min.mjs.map +1 -0
- package/dist/esm/dbs.min.mjs +2 -0
- package/dist/esm/dbs.min.mjs.map +1 -0
- package/dist/esm/dbs.mjs +27 -0
- package/dist/esm/dbs.mjs.map +1 -0
- package/dist/esm/errors.min.mjs +2 -0
- package/dist/esm/errors.min.mjs.map +1 -0
- package/dist/esm/errors.mjs +23 -0
- package/dist/esm/errors.mjs.map +1 -0
- package/dist/esm/events.min.mjs +2 -0
- package/dist/esm/events.min.mjs.map +1 -0
- package/dist/esm/events.mjs +17 -0
- package/dist/esm/events.mjs.map +1 -0
- package/dist/esm/index.min.mjs +2 -0
- package/dist/esm/index.min.mjs.map +1 -0
- package/dist/esm/index.mjs +7 -0
- package/dist/esm/index.mjs.map +1 -0
- package/dist/esm/jobs.min.mjs +2 -0
- package/dist/esm/jobs.min.mjs.map +1 -0
- package/dist/esm/jobs.mjs +9 -0
- package/dist/esm/jobs.mjs.map +1 -0
- package/dist/esm/server.min.mjs +2 -0
- package/dist/esm/server.min.mjs.map +1 -0
- package/dist/esm/server.mjs +41 -0
- package/dist/esm/server.mjs.map +1 -0
- package/dist/esm/types.min.mjs +1 -0
- package/dist/esm/types.min.mjs.map +1 -0
- package/dist/esm/types.mjs +1 -0
- package/dist/esm/types.mjs.map +1 -0
- package/dist/esm/utilities.min.mjs +2 -0
- package/dist/esm/utilities.min.mjs.map +1 -0
- package/dist/esm/utilities.mjs +21 -0
- package/dist/esm/utilities.mjs.map +1 -0
- package/dist/esm/validations.min.mjs +2 -0
- package/dist/esm/validations.min.mjs.map +1 -0
- package/dist/esm/validations.mjs +11 -0
- package/dist/esm/validations.mjs.map +1 -0
- package/dist/types/base-k4t2NepI.d.ts +8 -0
- package/dist/types/cache.d.ts +32 -0
- package/dist/types/cache.js +10 -0
- package/dist/types/chunk-XNSFFTXR.js +2506 -0
- package/dist/types/dbs.d.ts +16 -0
- package/dist/types/dbs.js +26 -0
- package/dist/types/errors.d.ts +39 -0
- package/dist/types/errors.js +22 -0
- package/dist/types/events.d.ts +13 -0
- package/dist/types/events.js +16 -0
- package/dist/types/index-D-NoC-on.d.ts +562 -0
- package/dist/types/index-U2VdDArL.d.ts +287 -0
- package/dist/types/index.d.ts +16 -0
- package/dist/types/index.js +6 -0
- package/dist/types/jobs.d.ts +54 -0
- package/dist/types/jobs.js +8 -0
- package/dist/types/kafka-CW925i4B.d.ts +57 -0
- package/dist/types/overrides-6Hxg764S.d.ts +25 -0
- package/dist/types/requests-BlJQeR64.d.ts +207 -0
- package/dist/types/server.d.ts +48 -0
- package/dist/types/server.js +40 -0
- package/dist/types/types.d.ts +5 -0
- package/dist/types/types.js +0 -0
- package/dist/types/utilities.d.ts +68 -0
- package/dist/types/utilities.js +20 -0
- package/dist/types/validationError-XbHmdYeK.d.ts +12 -0
- package/dist/types/validations.d.ts +74 -0
- package/dist/types/validations.js +10 -0
- package/package.json +2 -2
|
@@ -0,0 +1,2507 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _createStarExport(obj) { Object.keys(obj) .filter((key) => key !== "default" && key !== "__esModule") .forEach((key) => { if (exports.hasOwnProperty(key)) { return; } Object.defineProperty(exports, key, {enumerable: true, configurable: true, get: () => obj[key]}); }); } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3; var _class4; var _class5; var _class6; var _class7; var _class8; var _class9; var _class10;var __defProp = Object.defineProperty;
|
|
2
|
+
var __export = (target, all) => {
|
|
3
|
+
for (var name in all)
|
|
4
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
// src/validations/index.ts
|
|
8
|
+
var _valleyed = require('valleyed'); _createStarExport(_valleyed);
|
|
9
|
+
|
|
10
|
+
// src/validations/valleyed.ts
|
|
11
|
+
var valleyed_exports = {};
|
|
12
|
+
__export(valleyed_exports, {
|
|
13
|
+
incomingFile: () => incomingFile,
|
|
14
|
+
incomingFiles: () => incomingFiles,
|
|
15
|
+
requestLocalStorage: () => requestLocalStorage,
|
|
16
|
+
responseLocalStorage: () => responseLocalStorage,
|
|
17
|
+
withRequest: () => withRequest,
|
|
18
|
+
withResponse: () => withResponse
|
|
19
|
+
});
|
|
20
|
+
var _async_hooks = require('async_hooks');
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
// src/instance/index.ts
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
// src/instance/hooks.ts
|
|
27
|
+
async function runHooks(hooks, onError = (error) => {
|
|
28
|
+
throw error;
|
|
29
|
+
}) {
|
|
30
|
+
const grouped = hooks.reduce((acc, cur) => {
|
|
31
|
+
const key = cur.order.toString();
|
|
32
|
+
if (!acc[key]) acc[key] = [];
|
|
33
|
+
acc[key].push(cur);
|
|
34
|
+
return acc;
|
|
35
|
+
}, {});
|
|
36
|
+
const groups = Object.keys(grouped).sort((a, b) => parseInt(a) - parseInt(b)).map((key) => grouped[key]);
|
|
37
|
+
for (const group of groups)
|
|
38
|
+
await Promise.all(
|
|
39
|
+
group.map(async (h) => {
|
|
40
|
+
try {
|
|
41
|
+
if (typeof h.cb === "function") return await h.cb();
|
|
42
|
+
return await h.cb;
|
|
43
|
+
} catch (error) {
|
|
44
|
+
return onError(error instanceof Error ? error : new Error(`${error}`));
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// src/instance/settings.ts
|
|
51
|
+
var _pino = require('pino'); var _pino2 = _interopRequireDefault(_pino);
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
// src/cache/base.ts
|
|
55
|
+
var Cache = class {
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// src/cache/pipes.ts
|
|
59
|
+
|
|
60
|
+
var redisConfigPipe = _valleyed.v.meta(
|
|
61
|
+
_valleyed.v.object({
|
|
62
|
+
host: _valleyed.v.string(),
|
|
63
|
+
port: _valleyed.v.optional(_valleyed.v.number()),
|
|
64
|
+
password: _valleyed.v.optional(_valleyed.v.string()),
|
|
65
|
+
username: _valleyed.v.optional(_valleyed.v.string()),
|
|
66
|
+
tls: _valleyed.v.optional(_valleyed.v.boolean()),
|
|
67
|
+
cluster: _valleyed.v.optional(_valleyed.v.boolean())
|
|
68
|
+
}),
|
|
69
|
+
{ title: "Redis Config", $refId: "RedisConfig" }
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
// src/cache/types/redis.ts
|
|
73
|
+
var _ioredis = require('ioredis');
|
|
74
|
+
|
|
75
|
+
// src/errors/equippedError.ts
|
|
76
|
+
var EquippedError = class extends Error {
|
|
77
|
+
constructor(message, context, cause) {
|
|
78
|
+
super(message, { cause });
|
|
79
|
+
this.message = message;
|
|
80
|
+
this.context = context;
|
|
81
|
+
this.cause = cause;
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// src/errors/requestError.ts
|
|
86
|
+
var RequestError = class extends EquippedError {
|
|
87
|
+
constructor(message, serializedErrors, cause) {
|
|
88
|
+
super(message, { serializedErrors }, cause);
|
|
89
|
+
this.message = message;
|
|
90
|
+
this.serializedErrors = serializedErrors;
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// src/server/impls/base.ts
|
|
95
|
+
var _socketio = require('socket.io'); var _socketio2 = _interopRequireDefault(_socketio);
|
|
96
|
+
var _supertest = require('supertest'); var _supertest2 = _interopRequireDefault(_supertest);
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
// src/server/types.ts
|
|
100
|
+
var Methods = {
|
|
101
|
+
head: "head",
|
|
102
|
+
get: "get",
|
|
103
|
+
post: "post",
|
|
104
|
+
put: "put",
|
|
105
|
+
patch: "patch",
|
|
106
|
+
delete: "delete",
|
|
107
|
+
options: "options"
|
|
108
|
+
};
|
|
109
|
+
var StatusCodes = {
|
|
110
|
+
Ok: 200,
|
|
111
|
+
Found: 302,
|
|
112
|
+
BadRequest: 400,
|
|
113
|
+
NotAuthenticated: 401,
|
|
114
|
+
NotAuthorized: 403,
|
|
115
|
+
NotFound: 404,
|
|
116
|
+
ValidationError: 422,
|
|
117
|
+
TooManyRequests: 429,
|
|
118
|
+
AuthorizationExpired: 461
|
|
119
|
+
};
|
|
120
|
+
function makeMiddlewareHandler(cb, onSetup) {
|
|
121
|
+
return { cb, onSetup };
|
|
122
|
+
}
|
|
123
|
+
var makeMiddleware = (...args) => makeMiddlewareHandler(...args);
|
|
124
|
+
var makeErrorMiddleware = (...args) => makeMiddlewareHandler(...args);
|
|
125
|
+
|
|
126
|
+
// src/server/middlewares/parseAuthUser.ts
|
|
127
|
+
var parseAuthUser = makeMiddleware(async (request) => {
|
|
128
|
+
const requestsAuth = _optionalChain([Instance, 'access', _2 => _2.get, 'call', _3 => _3(), 'access', _4 => _4.settings, 'access', _5 => _5.server, 'optionalAccess', _6 => _6.requestsAuth]);
|
|
129
|
+
if (!requestsAuth) return;
|
|
130
|
+
const { Authorization, ApiKey } = request.headers;
|
|
131
|
+
function makeErrorHandler(key) {
|
|
132
|
+
return function(err) {
|
|
133
|
+
request.users[key].error = err instanceof RequestError ? err : new BadRequestError("failed to parse auth user", err);
|
|
134
|
+
request.users[key].error.context[key] = key;
|
|
135
|
+
return void 0;
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
if (requestsAuth.tokens && Authorization)
|
|
139
|
+
request.users.access.value = await requestsAuth.tokens.verifyAccessToken(Authorization).catch(makeErrorHandler("access"));
|
|
140
|
+
else if (requestsAuth.apiKey && ApiKey)
|
|
141
|
+
request.users.apiKey.value = await requestsAuth.apiKey.verifyApiKey(ApiKey).catch(makeErrorHandler("apiKey"));
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// src/server/openapi.ts
|
|
145
|
+
var _jsonschematoopenapischema = require('@openapi-contrib/json-schema-to-openapi-schema');
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
// src/server/routes.ts
|
|
149
|
+
|
|
150
|
+
function mergeSchemas(...schemas) {
|
|
151
|
+
const k = {
|
|
152
|
+
params: "",
|
|
153
|
+
headers: "",
|
|
154
|
+
query: "",
|
|
155
|
+
body: "",
|
|
156
|
+
response: "",
|
|
157
|
+
responseHeaders: "",
|
|
158
|
+
defaultStatusCode: "",
|
|
159
|
+
defaultContentType: "",
|
|
160
|
+
context: ""
|
|
161
|
+
};
|
|
162
|
+
function merge(acc, cur) {
|
|
163
|
+
if (!acc) return cur;
|
|
164
|
+
if (!cur) return acc;
|
|
165
|
+
if (typeof acc === "number") return cur;
|
|
166
|
+
if (typeof acc === "string") return cur;
|
|
167
|
+
if (typeof acc === "function") return cur;
|
|
168
|
+
return _valleyed.v.merge(acc, cur);
|
|
169
|
+
}
|
|
170
|
+
return Object.fromEntries(
|
|
171
|
+
Object.keys(k).map((key) => [
|
|
172
|
+
key,
|
|
173
|
+
schemas.map((s) => s[key]).reduce(merge, null)
|
|
174
|
+
])
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
var groupRoutes = (config, routes) => routes.map((route) => ({
|
|
178
|
+
...config,
|
|
179
|
+
...route,
|
|
180
|
+
path: `${config.path}/${route.path}`,
|
|
181
|
+
groups: [..._nullishCoalesce(config.groups, () => ( [])), ..._nullishCoalesce(route.groups, () => ( []))],
|
|
182
|
+
middlewares: [..._nullishCoalesce(config.middlewares, () => ( [])), ..._nullishCoalesce(route.middlewares, () => ( []))],
|
|
183
|
+
schema: mergeSchemas(_nullishCoalesce(config.schema, () => ( {})), _nullishCoalesce(route.schema, () => ( {}))),
|
|
184
|
+
security: [..._nullishCoalesce(config.security, () => ( [])), ..._nullishCoalesce(route.security, () => ( []))]
|
|
185
|
+
}));
|
|
186
|
+
var Router = (_class = class {
|
|
187
|
+
#config = { path: "" };
|
|
188
|
+
#routes = [];
|
|
189
|
+
#children = [];
|
|
190
|
+
constructor(config = { path: "" }) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);_class.prototype.__init4.call(this);_class.prototype.__init5.call(this);_class.prototype.__init6.call(this);_class.prototype.__init7.call(this);
|
|
191
|
+
this.#config = config;
|
|
192
|
+
}
|
|
193
|
+
#wrap(method) {
|
|
194
|
+
return (path, config = {}) => (handler) => {
|
|
195
|
+
const route = groupRoutes(this.#config, [{ ...config, path, method, handler }])[0];
|
|
196
|
+
this.#routes.push(route);
|
|
197
|
+
return route;
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
__init() {this.head = this.#wrap(Methods.head)}
|
|
201
|
+
__init2() {this.get = this.#wrap(Methods.get)}
|
|
202
|
+
__init3() {this.post = this.#wrap(Methods.post)}
|
|
203
|
+
__init4() {this.put = this.#wrap(Methods.put)}
|
|
204
|
+
__init5() {this.patch = this.#wrap(Methods.patch)}
|
|
205
|
+
__init6() {this.delete = this.#wrap(Methods.delete)}
|
|
206
|
+
__init7() {this.options = this.#wrap(Methods.options)}
|
|
207
|
+
nest(...routers) {
|
|
208
|
+
routers.forEach((router) => this.#children.push(router));
|
|
209
|
+
}
|
|
210
|
+
get routes() {
|
|
211
|
+
return [...this.#routes].concat(this.#children.flatMap((child) => groupRoutes(this.#config, child.routes)));
|
|
212
|
+
}
|
|
213
|
+
}, _class);
|
|
214
|
+
|
|
215
|
+
// src/server/openapi.ts
|
|
216
|
+
var OpenApi = class {
|
|
217
|
+
constructor(config) {
|
|
218
|
+
this.config = config;
|
|
219
|
+
this.#baseOpenapiDoc = {
|
|
220
|
+
openapi: "3.0.0",
|
|
221
|
+
info: {
|
|
222
|
+
title: `${config.app.name} ${config.app.id}`,
|
|
223
|
+
version: _nullishCoalesce(config.config.openapi.docsVersion, () => ( ""))
|
|
224
|
+
},
|
|
225
|
+
servers: _optionalChain([config, 'access', _7 => _7.config, 'access', _8 => _8.openapi, 'access', _9 => _9.docsBaseUrl, 'optionalAccess', _10 => _10.map, 'call', _11 => _11((url) => ({ url }))]),
|
|
226
|
+
paths: {},
|
|
227
|
+
components: {
|
|
228
|
+
schemas: {},
|
|
229
|
+
securitySchemes: {
|
|
230
|
+
Authorization: {
|
|
231
|
+
type: "apiKey",
|
|
232
|
+
name: "authorization",
|
|
233
|
+
in: "header"
|
|
234
|
+
},
|
|
235
|
+
RefreshToken: {
|
|
236
|
+
type: "apiKey",
|
|
237
|
+
name: "x-refresh-token",
|
|
238
|
+
in: "header"
|
|
239
|
+
},
|
|
240
|
+
ApiKey: {
|
|
241
|
+
type: "apiKey",
|
|
242
|
+
name: "x-api-key",
|
|
243
|
+
in: "header"
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
tags: [],
|
|
248
|
+
"x-tagGroups": []
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
#registeredTags = {};
|
|
252
|
+
#registeredTagGroups = {};
|
|
253
|
+
#baseOpenapiDoc;
|
|
254
|
+
cleanPath(path) {
|
|
255
|
+
let cleaned = path.replace(/(\/\s*)+/g, "/");
|
|
256
|
+
if (!cleaned.startsWith("/")) cleaned = `/${cleaned}`;
|
|
257
|
+
if (cleaned !== "/" && cleaned.endsWith("/")) cleaned = cleaned.slice(0, -1);
|
|
258
|
+
return cleaned;
|
|
259
|
+
}
|
|
260
|
+
async register(route, def) {
|
|
261
|
+
if (route.hide) return;
|
|
262
|
+
const tag = this.#buildTag(_nullishCoalesce(route.groups, () => ( [])));
|
|
263
|
+
const cleanPath = this.cleanPath(route.path);
|
|
264
|
+
const operationId = `(${route.method.toUpperCase()}) ${cleanPath}`;
|
|
265
|
+
await this.#addRouteToOpenApiDoc(cleanPath, route.method.toLowerCase(), def, {
|
|
266
|
+
operationId,
|
|
267
|
+
summary: _nullishCoalesce(route.title, () => ( cleanPath)),
|
|
268
|
+
description: _optionalChain([route, 'access', _12 => _12.descriptions, 'optionalAccess', _13 => _13.join, 'call', _14 => _14("\n\n")]),
|
|
269
|
+
tags: tag ? [tag] : void 0,
|
|
270
|
+
security: route.security
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
async #addRouteToOpenApiDoc(path, method, def, methodObj) {
|
|
274
|
+
if (_optionalChain([def, 'access', _15 => _15.response, 'access', _16 => _16.response, 'optionalAccess', _17 => _17.length])) {
|
|
275
|
+
methodObj.responses ??= {};
|
|
276
|
+
for (const resp of def.response.response) {
|
|
277
|
+
methodObj.responses[resp.status] ??= { description: "", content: {} };
|
|
278
|
+
const res = methodObj.responses[resp.status];
|
|
279
|
+
res.content[resp.contentType] = { schema: await _jsonschematoopenapischema.convert.call(void 0, this.#visit(resp.schema)) };
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
if (_optionalChain([def, 'access', _18 => _18.response, 'access', _19 => _19.responseHeaders, 'optionalAccess', _20 => _20.length])) {
|
|
283
|
+
methodObj.responses ??= {};
|
|
284
|
+
for (const resp of def.response.responseHeaders) {
|
|
285
|
+
methodObj.responses[resp.status] ??= { description: "", content: {} };
|
|
286
|
+
methodObj.responses[resp.status];
|
|
287
|
+
const res = methodObj.responses[resp.status];
|
|
288
|
+
res.headers = { schema: await _jsonschematoopenapischema.convert.call(void 0, this.#visit(resp.schema)) };
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
if (def.request.body)
|
|
292
|
+
methodObj.requestBody = {
|
|
293
|
+
required: true,
|
|
294
|
+
content: {
|
|
295
|
+
"application/json": { schema: await _jsonschematoopenapischema.convert.call(void 0, this.#visit(def.request.body)) }
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
const parameters = [];
|
|
299
|
+
const addParams = async (location, schema) => {
|
|
300
|
+
if (!schema) return;
|
|
301
|
+
const flat = this.#flattenForParameters(schema);
|
|
302
|
+
for (const schema2 of flat) {
|
|
303
|
+
if (!schema2.properties) continue;
|
|
304
|
+
for (const [name, value] of Object.entries(schema2.properties))
|
|
305
|
+
parameters.push({
|
|
306
|
+
name,
|
|
307
|
+
in: location,
|
|
308
|
+
schema: await _jsonschematoopenapischema.convert.call(void 0, this.#visit(value)),
|
|
309
|
+
required: (schema2.required || []).includes(name)
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
await Promise.all([
|
|
314
|
+
addParams("query", def.request.query),
|
|
315
|
+
addParams("path", def.request.params),
|
|
316
|
+
addParams("header", def.request.headers)
|
|
317
|
+
]);
|
|
318
|
+
if (parameters.length) methodObj.parameters = parameters;
|
|
319
|
+
const base = this.#baseOpenapiDoc;
|
|
320
|
+
if (!base.paths) base.paths = {};
|
|
321
|
+
if (!base.paths[path]) base.paths[path] = {};
|
|
322
|
+
base.paths[path][method] = methodObj;
|
|
323
|
+
}
|
|
324
|
+
router() {
|
|
325
|
+
const jsonPath = "/openapi.json";
|
|
326
|
+
const router = new Router({ path: _nullishCoalesce(this.config.config.openapi.docsPath, () => ( "/")), hide: true });
|
|
327
|
+
router.get("/")((req) => req.res({ body: this.#html(`.${jsonPath}`), contentType: "text/html" }));
|
|
328
|
+
router.get(jsonPath)((req) => req.res({ body: this.#baseOpenapiDoc }));
|
|
329
|
+
return router;
|
|
330
|
+
}
|
|
331
|
+
#flattenForParameters(node) {
|
|
332
|
+
const { allOf, oneOf, anyOf, ...schema } = node;
|
|
333
|
+
if (allOf) return allOf.flatMap((n) => this.#flattenForParameters(n));
|
|
334
|
+
return [schema];
|
|
335
|
+
}
|
|
336
|
+
#visit(node) {
|
|
337
|
+
if (!node || typeof node !== "object") return node;
|
|
338
|
+
if (typeof node.$refId === "string") {
|
|
339
|
+
const { $refId: id, ...rest } = node;
|
|
340
|
+
const res = this.#visit(rest);
|
|
341
|
+
if (_optionalChain([this, 'access', _21 => _21.#baseOpenapiDoc, 'access', _22 => _22.components, 'optionalAccess', _23 => _23.schemas])) {
|
|
342
|
+
this.#baseOpenapiDoc.components.schemas[id] = res;
|
|
343
|
+
return { $ref: `#/components/schemas/${id}` };
|
|
344
|
+
} else return res;
|
|
345
|
+
}
|
|
346
|
+
if (Array.isArray(node)) return node.map((n) => this.#visit(n));
|
|
347
|
+
return Object.fromEntries(Object.entries(node).map(([key, value]) => [key, this.#visit(value)]));
|
|
348
|
+
}
|
|
349
|
+
#buildTag(groups) {
|
|
350
|
+
if (!groups.length) return void 0;
|
|
351
|
+
const parsed = groups.map((g) => typeof g === "string" ? { name: g } : g);
|
|
352
|
+
const name = parsed.map((g) => g.name).join(" > ");
|
|
353
|
+
const displayName = _nullishCoalesce(_optionalChain([parsed, 'access', _24 => _24.at, 'call', _25 => _25(-1), 'optionalAccess', _26 => _26.name]), () => ( ""));
|
|
354
|
+
const description = parsed.map((g) => _nullishCoalesce(_optionalChain([g, 'access', _27 => _27.description, 'optionalAccess', _28 => _28.trim, 'call', _29 => _29()]), () => ( ""))).filter(Boolean).join("\n\n\n\n");
|
|
355
|
+
if (!this.#registeredTags[name]) {
|
|
356
|
+
this.#registeredTags[name] = true;
|
|
357
|
+
this.#baseOpenapiDoc.tags.push({ name, "x-displayName": displayName, description });
|
|
358
|
+
const tagGroups = parsed.slice(0, -1);
|
|
359
|
+
const groupName = tagGroups.map((g) => g.name).join(" > ") || "default";
|
|
360
|
+
if (!this.#registeredTagGroups[groupName]) {
|
|
361
|
+
const group = { name: groupName, tags: [] };
|
|
362
|
+
this.#baseOpenapiDoc["x-tagGroups"].push(group);
|
|
363
|
+
this.#registeredTagGroups[groupName] = group;
|
|
364
|
+
}
|
|
365
|
+
this.#registeredTagGroups[groupName].tags = [.../* @__PURE__ */ new Set([...this.#registeredTagGroups[groupName].tags, name])];
|
|
366
|
+
}
|
|
367
|
+
return name;
|
|
368
|
+
}
|
|
369
|
+
#html(jsonPath) {
|
|
370
|
+
const title = _valleyed.capitalize.call(void 0, `${this.config.app.name} ${this.config.app.id}`);
|
|
371
|
+
return `
|
|
372
|
+
<!doctype html>
|
|
373
|
+
<html>
|
|
374
|
+
<head>
|
|
375
|
+
<title>${title}</title>
|
|
376
|
+
<meta charset="utf-8" />
|
|
377
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
378
|
+
<style>
|
|
379
|
+
.darklight-reference {
|
|
380
|
+
display: none;
|
|
381
|
+
}
|
|
382
|
+
</style>
|
|
383
|
+
</head>
|
|
384
|
+
<body>
|
|
385
|
+
<script id="api-reference" data-url="${jsonPath}"></script>
|
|
386
|
+
<script>
|
|
387
|
+
const configuration = { theme: 'purple' };
|
|
388
|
+
document.getElementById('api-reference').dataset.configuration = JSON.stringify(configuration);
|
|
389
|
+
</script>
|
|
390
|
+
<script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference@1.28.33"></script>
|
|
391
|
+
</body>
|
|
392
|
+
</html>
|
|
393
|
+
`;
|
|
394
|
+
}
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
// src/utilities/authProviders.ts
|
|
398
|
+
var authProviders_exports = {};
|
|
399
|
+
__export(authProviders_exports, {
|
|
400
|
+
signinWithApple: () => signinWithApple,
|
|
401
|
+
signinWithFacebook: () => signinWithFacebook,
|
|
402
|
+
signinWithGoogle: () => signinWithGoogle
|
|
403
|
+
});
|
|
404
|
+
var _axios = require('axios'); var _axios2 = _interopRequireDefault(_axios);
|
|
405
|
+
var _jsonwebtoken = require('jsonwebtoken'); var _jsonwebtoken2 = _interopRequireDefault(_jsonwebtoken);
|
|
406
|
+
var _jwksrsa = require('jwks-rsa'); var _jwksrsa2 = _interopRequireDefault(_jwksrsa);
|
|
407
|
+
var signinWithGoogle = async (idToken) => {
|
|
408
|
+
const authUrl = `https://oauth2.googleapis.com/tokeninfo?id_token=${idToken}`;
|
|
409
|
+
const { data } = await _axios2.default.get(authUrl).catch((err) => {
|
|
410
|
+
throw new EquippedError("Failed to sign in with google", { idToken }, err);
|
|
411
|
+
});
|
|
412
|
+
data.first_name = data.given_name;
|
|
413
|
+
data.last_name = data.family_name;
|
|
414
|
+
return data;
|
|
415
|
+
};
|
|
416
|
+
var signinWithApple = async (idToken) => {
|
|
417
|
+
try {
|
|
418
|
+
const APPLE_BASE = "https://appleid.apple.com";
|
|
419
|
+
const json = _jsonwebtoken2.default.decode(idToken, { complete: true });
|
|
420
|
+
if (!_optionalChain([json, 'optionalAccess', _30 => _30.header])) throw new Error("");
|
|
421
|
+
const { kid, alg } = json.header;
|
|
422
|
+
const publicKey = await _jwksrsa2.default.call(void 0, { jwksUri: `${APPLE_BASE}/auth/keys`, cache: true }).getSigningKey(kid).then((key) => key.getPublicKey()).catch(() => null);
|
|
423
|
+
if (!publicKey) throw new EquippedError("no publicKey", { idToken, publicKey, json });
|
|
424
|
+
const data = _jsonwebtoken2.default.verify(idToken, publicKey, { algorithms: [alg] });
|
|
425
|
+
if (!data) throw new EquippedError("no data", { idToken, data });
|
|
426
|
+
if (data.iss !== APPLE_BASE) throw new EquippedError("iss doesnt match", { idToken, data });
|
|
427
|
+
if (data.exp * 1e3 < Date.now()) throw new EquippedError("expired idToken", { idToken, data });
|
|
428
|
+
return data;
|
|
429
|
+
} catch (err) {
|
|
430
|
+
throw new EquippedError("Failed to sign in with apple", { idToken }, err);
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
var signinWithFacebook = async (accessToken, fields = []) => {
|
|
434
|
+
fields = [.../* @__PURE__ */ new Set([...fields, "name", "picture", "email"])];
|
|
435
|
+
const { data } = await _axios2.default.request({
|
|
436
|
+
method: "get",
|
|
437
|
+
url: "https://graph.facebook.com/v15.0/me",
|
|
438
|
+
params: {
|
|
439
|
+
fields: fields.join(","),
|
|
440
|
+
access_token: accessToken
|
|
441
|
+
}
|
|
442
|
+
}).catch((err) => {
|
|
443
|
+
throw new EquippedError("Failed to sign in with facebook", { accessToken, fields }, err);
|
|
444
|
+
});
|
|
445
|
+
const isValidData = fields.every((key) => key in data);
|
|
446
|
+
if (!isValidData) throw new Error("Incomplete scope for access token");
|
|
447
|
+
data.email_verified = "true";
|
|
448
|
+
return data;
|
|
449
|
+
};
|
|
450
|
+
|
|
451
|
+
// src/utilities/hash.ts
|
|
452
|
+
var hash_exports = {};
|
|
453
|
+
__export(hash_exports, {
|
|
454
|
+
compare: () => compare2,
|
|
455
|
+
hash: () => hash2
|
|
456
|
+
});
|
|
457
|
+
var _bcryptjs = require('bcryptjs'); var bcrypt = _interopRequireWildcard(_bcryptjs);
|
|
458
|
+
var hash2 = async (password) => {
|
|
459
|
+
password = password.trim();
|
|
460
|
+
if (!password) return "";
|
|
461
|
+
return await bcrypt.hash(password, Instance.get().settings.utils.hashSaltRounds);
|
|
462
|
+
};
|
|
463
|
+
var compare2 = async (plainPassword, hashed) => {
|
|
464
|
+
plainPassword = plainPassword.trim();
|
|
465
|
+
if (!plainPassword && plainPassword === hashed) return true;
|
|
466
|
+
return await bcrypt.compare(plainPassword, hashed);
|
|
467
|
+
};
|
|
468
|
+
|
|
469
|
+
// src/utilities/json.ts
|
|
470
|
+
var parseJSONValue = (data) => {
|
|
471
|
+
try {
|
|
472
|
+
if (_optionalChain([data, 'optionalAccess', _31 => _31.constructor, 'optionalAccess', _32 => _32.name]) !== "String") return data;
|
|
473
|
+
return JSON.parse(data);
|
|
474
|
+
} catch (e2) {
|
|
475
|
+
return data;
|
|
476
|
+
}
|
|
477
|
+
};
|
|
478
|
+
function parseJSONObject(data) {
|
|
479
|
+
return Object.fromEntries(Object.entries(data).map(([key, value]) => [key, parseJSONValue(value)]));
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// src/utilities/media.ts
|
|
483
|
+
var _musicmetadata = require('music-metadata');
|
|
484
|
+
var getMediaDuration = async (buffer) => {
|
|
485
|
+
try {
|
|
486
|
+
const meta = await _musicmetadata.parseBuffer.call(void 0, buffer);
|
|
487
|
+
return _nullishCoalesce(meta.format.duration, () => ( 0));
|
|
488
|
+
} catch (e3) {
|
|
489
|
+
return 0;
|
|
490
|
+
}
|
|
491
|
+
};
|
|
492
|
+
|
|
493
|
+
// src/utilities/retry.ts
|
|
494
|
+
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
495
|
+
var retry = async (cb, tries, waitTimeInMs) => {
|
|
496
|
+
if (tries <= 0) throw new EquippedError("out of tries", { tries, waitTimeInMs });
|
|
497
|
+
const result = await cb();
|
|
498
|
+
if (result.done === true) return result.value;
|
|
499
|
+
await sleep(waitTimeInMs);
|
|
500
|
+
return await retry(cb, tries - 1, waitTimeInMs);
|
|
501
|
+
};
|
|
502
|
+
|
|
503
|
+
// src/utilities/random.ts
|
|
504
|
+
var random_exports = {};
|
|
505
|
+
__export(random_exports, {
|
|
506
|
+
number: () => number,
|
|
507
|
+
string: () => string
|
|
508
|
+
});
|
|
509
|
+
var _crypto = require('crypto'); var _crypto2 = _interopRequireDefault(_crypto);
|
|
510
|
+
function string(length = 20) {
|
|
511
|
+
return _crypto2.default.randomBytes(length).toString("hex").slice(0, length);
|
|
512
|
+
}
|
|
513
|
+
function number(min = 0, max = 2 ** 48 - 1) {
|
|
514
|
+
return _crypto2.default.randomInt(min, max);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// src/server/requests.ts
|
|
518
|
+
var Request = (_class2 = class {
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
|
|
527
|
+
|
|
528
|
+
__init8() {this.users = {
|
|
529
|
+
access: {},
|
|
530
|
+
refresh: {},
|
|
531
|
+
apiKey: {}
|
|
532
|
+
}}
|
|
533
|
+
|
|
534
|
+
constructor({
|
|
535
|
+
ip,
|
|
536
|
+
body,
|
|
537
|
+
cookies,
|
|
538
|
+
params,
|
|
539
|
+
query,
|
|
540
|
+
method,
|
|
541
|
+
path,
|
|
542
|
+
context,
|
|
543
|
+
headers,
|
|
544
|
+
files
|
|
545
|
+
}) {;_class2.prototype.__init8.call(this);
|
|
546
|
+
this.ip = ip;
|
|
547
|
+
this.method = method;
|
|
548
|
+
this.path = path;
|
|
549
|
+
this.params = params;
|
|
550
|
+
this.cookies = cookies;
|
|
551
|
+
this.headers = headers;
|
|
552
|
+
this.query = parseJSONObject(query);
|
|
553
|
+
this.body = Object.assign(parseJSONObject(body), files);
|
|
554
|
+
this.context = context;
|
|
555
|
+
}
|
|
556
|
+
pipe(stream, opts = {}) {
|
|
557
|
+
return new Response({ ...opts, piped: true, body: stream });
|
|
558
|
+
}
|
|
559
|
+
res(params) {
|
|
560
|
+
return new Response({ ...params, piped: false });
|
|
561
|
+
}
|
|
562
|
+
error(params) {
|
|
563
|
+
return new Response({ ...params, piped: false });
|
|
564
|
+
}
|
|
565
|
+
}, _class2);
|
|
566
|
+
var Response = class {
|
|
567
|
+
|
|
568
|
+
|
|
569
|
+
|
|
570
|
+
|
|
571
|
+
|
|
572
|
+
constructor({ body, status = 200, headers = {}, piped = false, contentType = "application/json" }) {
|
|
573
|
+
this.body = body;
|
|
574
|
+
this.status = status;
|
|
575
|
+
this.contentType = contentType;
|
|
576
|
+
this.headers = headers;
|
|
577
|
+
this.piped = piped;
|
|
578
|
+
if (!this.piped) {
|
|
579
|
+
this.headers["Content-Type"] = contentType;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
};
|
|
583
|
+
|
|
584
|
+
// src/server/sockets.ts
|
|
585
|
+
var _pathtoregexp = require('path-to-regexp');
|
|
586
|
+
var EmitterEvent = "__listener_emitter";
|
|
587
|
+
var defaultTo = "*";
|
|
588
|
+
var SocketEmitter = class {
|
|
589
|
+
|
|
590
|
+
#connectionCallbacks = { onConnect: async () => {
|
|
591
|
+
}, onDisconnect: async () => {
|
|
592
|
+
} };
|
|
593
|
+
#routes = {};
|
|
594
|
+
#publish = async () => {
|
|
595
|
+
};
|
|
596
|
+
constructor(socket, eventBus) {
|
|
597
|
+
this.socketInstance = socket;
|
|
598
|
+
this.#setupSocketConnection();
|
|
599
|
+
Instance.on(
|
|
600
|
+
"setup",
|
|
601
|
+
() => {
|
|
602
|
+
this.#publish = eventBus ? eventBus.createPublisher(EmitterEvent) : async (data) => {
|
|
603
|
+
socket.to(data.channel).emit(data.channel, data);
|
|
604
|
+
};
|
|
605
|
+
_optionalChain([eventBus, 'optionalAccess', _33 => _33.createSubscriber, 'call', _34 => _34(
|
|
606
|
+
EmitterEvent,
|
|
607
|
+
async (data) => {
|
|
608
|
+
socket.to(data.channel).emit(data.channel, data);
|
|
609
|
+
},
|
|
610
|
+
{ fanout: true }
|
|
611
|
+
)]);
|
|
612
|
+
},
|
|
613
|
+
1
|
|
614
|
+
);
|
|
615
|
+
}
|
|
616
|
+
async created(channels, data, to) {
|
|
617
|
+
await this.#emit(channels, "created" /* created */, { after: data.toJSON(), before: null }, to);
|
|
618
|
+
}
|
|
619
|
+
async updated(channels, { after, before }, to) {
|
|
620
|
+
await this.#emit(channels, "updated" /* updated */, { after: after.toJSON(), before: before.toJSON() }, to);
|
|
621
|
+
}
|
|
622
|
+
async deleted(channels, data, to) {
|
|
623
|
+
await this.#emit(channels, "deleted" /* deleted */, { before: data.toJSON(), after: null }, to);
|
|
624
|
+
}
|
|
625
|
+
async #emit(channels, type, { before, after }, to) {
|
|
626
|
+
const toArray = Array.isArray(to) ? to : [_nullishCoalesce(to, () => ( defaultTo))];
|
|
627
|
+
const channelMap = channels.flatMap((c) => toArray.map((to2) => `${to2}:${c}`));
|
|
628
|
+
await Promise.all(channelMap.map(async (channel) => this.#publish({ channel, type, before, after })));
|
|
629
|
+
}
|
|
630
|
+
set connectionCallbacks(callbacks) {
|
|
631
|
+
this.#connectionCallbacks = callbacks;
|
|
632
|
+
this.#setupSocketConnection();
|
|
633
|
+
}
|
|
634
|
+
register(channel, onJoin) {
|
|
635
|
+
this.#routes[channel] = onJoin;
|
|
636
|
+
this.#routes[channel + "/:id"] = onJoin;
|
|
637
|
+
return this;
|
|
638
|
+
}
|
|
639
|
+
#getConfig(channel) {
|
|
640
|
+
const matcher = (key) => _pathtoregexp.match.call(void 0, key)(channel);
|
|
641
|
+
const matchedChannel = _nullishCoalesce(Object.keys(this.#routes).find(matcher), () => ( null));
|
|
642
|
+
if (!matchedChannel) return null;
|
|
643
|
+
const match = matcher(matchedChannel);
|
|
644
|
+
if (!match) return null;
|
|
645
|
+
return {
|
|
646
|
+
config: this.#routes[matchedChannel],
|
|
647
|
+
params: match.params
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
#setupSocketConnection = () => {
|
|
651
|
+
this.socketInstance.removeAllListeners("connection");
|
|
652
|
+
this.socketInstance.on("connection", async (socket) => {
|
|
653
|
+
const socketId = socket.id;
|
|
654
|
+
let user = null;
|
|
655
|
+
const tokensUtil = _optionalChain([Instance, 'access', _35 => _35.get, 'call', _36 => _36(), 'access', _37 => _37.settings, 'access', _38 => _38.server, 'optionalAccess', _39 => _39.requestsAuth, 'access', _40 => _40.tokens]);
|
|
656
|
+
if (socket.handshake.auth.authorization && tokensUtil)
|
|
657
|
+
user = await tokensUtil.verifyAccessToken(_nullishCoalesce(socket.handshake.auth.authorization, () => ( ""))).catch(() => null);
|
|
658
|
+
socket.on("leave", async (data, callback) => {
|
|
659
|
+
if (!data.channel)
|
|
660
|
+
return typeof callback === "function" && callback({
|
|
661
|
+
code: StatusCodes.ValidationError,
|
|
662
|
+
message: "channel is required",
|
|
663
|
+
channel: ""
|
|
664
|
+
});
|
|
665
|
+
socket.leave(data.channel);
|
|
666
|
+
return typeof callback === "function" && callback({
|
|
667
|
+
code: StatusCodes.Ok,
|
|
668
|
+
message: "",
|
|
669
|
+
channel: data.channel
|
|
670
|
+
});
|
|
671
|
+
});
|
|
672
|
+
socket.on("join", async (data, callback) => {
|
|
673
|
+
if (!data.channel)
|
|
674
|
+
return typeof callback === "function" && callback({
|
|
675
|
+
code: StatusCodes.ValidationError,
|
|
676
|
+
message: "channel is required",
|
|
677
|
+
channel: ""
|
|
678
|
+
});
|
|
679
|
+
const channel = data.channel;
|
|
680
|
+
const route = _nullishCoalesce(this.#getConfig(channel), () => ( null));
|
|
681
|
+
if (!route)
|
|
682
|
+
return typeof callback === "function" && callback({
|
|
683
|
+
code: StatusCodes.BadRequest,
|
|
684
|
+
message: "unknown channel",
|
|
685
|
+
channel
|
|
686
|
+
});
|
|
687
|
+
const to = await route.config({ channel, user }, route.params, _nullishCoalesce(data.query, () => ( {})));
|
|
688
|
+
const newChannel = `${_nullishCoalesce(to, () => ( defaultTo))}:${channel}`;
|
|
689
|
+
socket.join(newChannel);
|
|
690
|
+
return typeof callback === "function" && callback({
|
|
691
|
+
code: StatusCodes.Ok,
|
|
692
|
+
message: "",
|
|
693
|
+
channel: newChannel
|
|
694
|
+
});
|
|
695
|
+
});
|
|
696
|
+
if (user) await this.#connectionCallbacks.onConnect(user.id, socketId);
|
|
697
|
+
socket.on("disconnect", async () => {
|
|
698
|
+
if (user) await this.#connectionCallbacks.onDisconnect(user.id, socketId);
|
|
699
|
+
});
|
|
700
|
+
});
|
|
701
|
+
};
|
|
702
|
+
};
|
|
703
|
+
|
|
704
|
+
// src/server/impls/base.ts
|
|
705
|
+
var errorsSchemas = Object.entries(StatusCodes).filter(([, value]) => value > 399).map(([key, value]) => ({
|
|
706
|
+
status: value,
|
|
707
|
+
contentType: "application/json",
|
|
708
|
+
pipe: _valleyed.v.meta(_valleyed.v.array(_valleyed.v.object({ message: _valleyed.v.string(), field: _valleyed.v.optional(_valleyed.v.string()) })), {
|
|
709
|
+
$refId: `Errors.${key}Response`,
|
|
710
|
+
description: `${key} Response`
|
|
711
|
+
})
|
|
712
|
+
}));
|
|
713
|
+
var Server = (_class3 = class {
|
|
714
|
+
constructor(server, config, implementations) {;_class3.prototype.__init9.call(this);
|
|
715
|
+
this.config = config;
|
|
716
|
+
this.implementations = implementations;
|
|
717
|
+
this.server = server;
|
|
718
|
+
this.#openapi = new OpenApi(this.config);
|
|
719
|
+
const socketInstance = new _socketio2.default.Server(server, { cors: this.cors });
|
|
720
|
+
this.socket = new SocketEmitter(socketInstance, config.eventBus);
|
|
721
|
+
this.addRouter(this.#openapi.router());
|
|
722
|
+
}
|
|
723
|
+
#queue = [];
|
|
724
|
+
#routesByKey = /* @__PURE__ */ new Map();
|
|
725
|
+
#openapi;
|
|
726
|
+
|
|
727
|
+
|
|
728
|
+
__init9() {this.cors = {
|
|
729
|
+
origin: "*",
|
|
730
|
+
methods: Object.values(Methods).filter((m) => m !== Methods.options).map((m) => m.toUpperCase())
|
|
731
|
+
}}
|
|
732
|
+
addRouter(...routers) {
|
|
733
|
+
routers.map((router) => router.routes).forEach((routes) => this.addRoute(...routes));
|
|
734
|
+
}
|
|
735
|
+
addRoute(...routes) {
|
|
736
|
+
routes.forEach((route) => {
|
|
737
|
+
this.#queue.push(async () => {
|
|
738
|
+
const { method, path, schema = {}, onError, middlewares = [] } = route;
|
|
739
|
+
const key = `(${method.toUpperCase()}) ${this.#openapi.cleanPath(path)}`;
|
|
740
|
+
if (this.#routesByKey.get(key))
|
|
741
|
+
throw new EquippedError(`Route key ${key} already registered. All route keys must be unique`, { route, key });
|
|
742
|
+
middlewares.unshift(parseAuthUser);
|
|
743
|
+
middlewares.forEach((m) => _optionalChain([m, 'access', _41 => _41.onSetup, 'optionalCall', _42 => _42(route)]));
|
|
744
|
+
_optionalChain([onError, 'optionalAccess', _43 => _43.onSetup, 'optionalCall', _44 => _44(route)]);
|
|
745
|
+
const { validateRequest, validateResponse, jsonSchema } = this.#resolveSchema(method, schema);
|
|
746
|
+
this.#routesByKey.set(key, true);
|
|
747
|
+
await this.#openapi.register(route, jsonSchema);
|
|
748
|
+
this.implementations.registerRoute(method, this.#openapi.cleanPath(path), async (req, res) => {
|
|
749
|
+
const request = await validateRequest(await this.implementations.parseRequest(req));
|
|
750
|
+
try {
|
|
751
|
+
for (const middleware of middlewares) await middleware.cb(request);
|
|
752
|
+
const rawRes = await route.handler(request);
|
|
753
|
+
const response = rawRes instanceof Response ? rawRes : new Response({ body: rawRes, status: StatusCodes.Ok, headers: {}, piped: false });
|
|
754
|
+
return await this.implementations.handleResponse(res, await validateResponse(response));
|
|
755
|
+
} catch (error) {
|
|
756
|
+
if (_optionalChain([onError, 'optionalAccess', _45 => _45.cb])) {
|
|
757
|
+
const rawResponse = await onError.cb(request, error);
|
|
758
|
+
const response = rawResponse instanceof Response ? rawResponse : new Response({ body: rawResponse, status: StatusCodes.BadRequest, headers: {} });
|
|
759
|
+
return await this.implementations.handleResponse(res, await validateResponse(response));
|
|
760
|
+
}
|
|
761
|
+
throw error;
|
|
762
|
+
}
|
|
763
|
+
});
|
|
764
|
+
});
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
#resolveSchema(method, schema) {
|
|
768
|
+
const defaultStatusCode = _nullishCoalesce(_optionalChain([schema, 'optionalAccess', _46 => _46.defaultStatusCode]), () => ( StatusCodes.Ok));
|
|
769
|
+
const defaultContentType = _nullishCoalesce(_optionalChain([schema, 'optionalAccess', _47 => _47.defaultContentType]), () => ( "application/json"));
|
|
770
|
+
let status = defaultStatusCode;
|
|
771
|
+
let contentType = defaultContentType;
|
|
772
|
+
const jsonSchema = { response: {}, request: {} };
|
|
773
|
+
const requestPipe = {};
|
|
774
|
+
const responsePipe = {};
|
|
775
|
+
const defs = [
|
|
776
|
+
{ key: "params", type: "request" },
|
|
777
|
+
{ key: "headers", type: "request" },
|
|
778
|
+
{ key: "query", type: "request" },
|
|
779
|
+
{ key: "body", type: "request", skip: ![Methods.post, Methods.put, Methods.patch].includes(method) },
|
|
780
|
+
{ key: "response", type: "response" },
|
|
781
|
+
{ key: "responseHeaders", type: "response" }
|
|
782
|
+
];
|
|
783
|
+
defs.forEach((def) => {
|
|
784
|
+
const pipe = _nullishCoalesce(schema[def.key], () => ( _valleyed.v.any()));
|
|
785
|
+
if (def.skip) return;
|
|
786
|
+
if (def.type === "request") {
|
|
787
|
+
requestPipe[def.key] = pipe;
|
|
788
|
+
jsonSchema.request[def.key] = _valleyed.v.schema(pipe);
|
|
789
|
+
}
|
|
790
|
+
if (def.type === "response") {
|
|
791
|
+
const pipeRecords = errorsSchemas.concat({ status: defaultStatusCode, contentType, pipe });
|
|
792
|
+
responsePipe[def.key] = _valleyed.v.any().pipe((input) => {
|
|
793
|
+
const p = _optionalChain([pipeRecords, 'access', _48 => _48.find, 'call', _49 => _49((r) => r.status === status), 'optionalAccess', _50 => _50.pipe]);
|
|
794
|
+
if (!p) throw _valleyed.PipeError.root(`schema not defined for status code: ${status}`, input);
|
|
795
|
+
return _valleyed.v.assert(p, input);
|
|
796
|
+
});
|
|
797
|
+
jsonSchema.response[def.key] = pipeRecords.map((record) => ({
|
|
798
|
+
status: record.status,
|
|
799
|
+
contentType: record.contentType,
|
|
800
|
+
schema: _valleyed.v.schema(record.pipe)
|
|
801
|
+
}));
|
|
802
|
+
}
|
|
803
|
+
});
|
|
804
|
+
const validateRequest = async (request) => {
|
|
805
|
+
if (!Object.keys(requestPipe)) return request;
|
|
806
|
+
const context = schema.context ? await schema.context(request) : {};
|
|
807
|
+
request.context = context;
|
|
808
|
+
const validity = requestLocalStorage.run(
|
|
809
|
+
request,
|
|
810
|
+
() => _valleyed.v.validate(_valleyed.v.object(requestPipe), {
|
|
811
|
+
params: request.params,
|
|
812
|
+
headers: request.headers,
|
|
813
|
+
query: request.query,
|
|
814
|
+
body: request.body
|
|
815
|
+
})
|
|
816
|
+
);
|
|
817
|
+
if (!validity.valid) throw pipeErrorToValidationError(validity.error);
|
|
818
|
+
request.params = validity.value.params;
|
|
819
|
+
request.headers = validity.value.headers;
|
|
820
|
+
request.query = validity.value.query;
|
|
821
|
+
request.body = validity.value.body;
|
|
822
|
+
return request;
|
|
823
|
+
};
|
|
824
|
+
const validateResponse = async (response) => {
|
|
825
|
+
if (!Object.keys(responsePipe)) return response;
|
|
826
|
+
status = response.status;
|
|
827
|
+
contentType = response.contentType;
|
|
828
|
+
contentType;
|
|
829
|
+
const validity = responseLocalStorage.run(
|
|
830
|
+
response,
|
|
831
|
+
() => _valleyed.v.validate(_valleyed.v.object(responsePipe), {
|
|
832
|
+
responseHeaders: response.headers,
|
|
833
|
+
response: response.body
|
|
834
|
+
})
|
|
835
|
+
);
|
|
836
|
+
if (!validity.valid) throw pipeErrorToValidationError(validity.error);
|
|
837
|
+
response.body = validity.value.response;
|
|
838
|
+
response.headers = validity.value.responseHeaders;
|
|
839
|
+
return response;
|
|
840
|
+
};
|
|
841
|
+
return {
|
|
842
|
+
jsonSchema,
|
|
843
|
+
validateRequest,
|
|
844
|
+
validateResponse
|
|
845
|
+
};
|
|
846
|
+
}
|
|
847
|
+
test() {
|
|
848
|
+
return _supertest2.default.call(void 0, this.server);
|
|
849
|
+
}
|
|
850
|
+
async start() {
|
|
851
|
+
const port = this.config.config.port;
|
|
852
|
+
if (this.config.config.healthPath)
|
|
853
|
+
this.addRoute({
|
|
854
|
+
method: Methods.get,
|
|
855
|
+
path: this.config.config.healthPath,
|
|
856
|
+
handler: async (req) => req.res({
|
|
857
|
+
body: `${this.config.app.id}(${this.config.app.name}) service running`,
|
|
858
|
+
contentType: "text/plain"
|
|
859
|
+
})
|
|
860
|
+
});
|
|
861
|
+
this.implementations.registerNotFoundHandler(async (req) => {
|
|
862
|
+
const request = await this.implementations.parseRequest(req);
|
|
863
|
+
throw new NotFoundError(`Route ${request.path} not found`);
|
|
864
|
+
});
|
|
865
|
+
this.implementations.registerErrorHandler(async (error, _, res) => {
|
|
866
|
+
Instance.get().log.error(error);
|
|
867
|
+
const response = error instanceof RequestError ? new Response({
|
|
868
|
+
body: error.serializedErrors,
|
|
869
|
+
status: error.statusCode
|
|
870
|
+
}) : new Response({
|
|
871
|
+
body: [{ message: "Something went wrong", data: error.message }],
|
|
872
|
+
status: StatusCodes.BadRequest
|
|
873
|
+
});
|
|
874
|
+
return await this.implementations.handleResponse(res, response);
|
|
875
|
+
});
|
|
876
|
+
await Promise.all(this.#queue.map((cb) => cb()));
|
|
877
|
+
const started = await this.implementations.start(port);
|
|
878
|
+
if (started) Instance.get().log.info(`${this.config.app.id}(${this.config.app.name}) service listening on port ${port}`);
|
|
879
|
+
return started;
|
|
880
|
+
}
|
|
881
|
+
}, _class3);
|
|
882
|
+
|
|
883
|
+
// src/server/impls/express.ts
|
|
884
|
+
var _http = require('http'); var _http2 = _interopRequireDefault(_http);
|
|
885
|
+
var _cookieparser = require('cookie-parser'); var _cookieparser2 = _interopRequireDefault(_cookieparser);
|
|
886
|
+
var _cors = require('cors'); var _cors2 = _interopRequireDefault(_cors);
|
|
887
|
+
var _express = require('express'); var _express2 = _interopRequireDefault(_express);
|
|
888
|
+
var _expressfileupload = require('express-fileupload'); var _expressfileupload2 = _interopRequireDefault(_expressfileupload);
|
|
889
|
+
var _expressratelimit = require('express-rate-limit');
|
|
890
|
+
var _helmet = require('helmet'); var _helmet2 = _interopRequireDefault(_helmet);
|
|
891
|
+
var _pinohttp = require('pino-http');
|
|
892
|
+
var ExpressServer = class extends Server {
|
|
893
|
+
#expressApp;
|
|
894
|
+
constructor(config) {
|
|
895
|
+
const app = _express2.default.call(void 0, );
|
|
896
|
+
super(_http2.default.createServer(app), config, {
|
|
897
|
+
parseRequest: async (req) => {
|
|
898
|
+
const allHeaders = Object.fromEntries(Object.entries(req.headers).map(([key, val]) => [key, _nullishCoalesce(val, () => ( null))]));
|
|
899
|
+
const headers = {
|
|
900
|
+
...allHeaders,
|
|
901
|
+
Authorization: req.get("authorization"),
|
|
902
|
+
RefreshToken: req.get("x-refresh-token"),
|
|
903
|
+
ApiKey: req.get("x-api-key"),
|
|
904
|
+
ContentType: req.get("content-type"),
|
|
905
|
+
Referer: req.get("referer"),
|
|
906
|
+
UserAgent: req.get("user-agent")
|
|
907
|
+
};
|
|
908
|
+
const files = Object.fromEntries(
|
|
909
|
+
await Promise.all(
|
|
910
|
+
Object.entries(_nullishCoalesce(req.files, () => ( {}))).map(async ([key, file]) => {
|
|
911
|
+
const uploads = Array.isArray(file) ? file : [file];
|
|
912
|
+
const fileArray = await Promise.all(
|
|
913
|
+
uploads.map(async (f) => ({
|
|
914
|
+
name: f.name,
|
|
915
|
+
type: f.mimetype,
|
|
916
|
+
size: f.size,
|
|
917
|
+
isTruncated: f.truncated,
|
|
918
|
+
data: f.data,
|
|
919
|
+
duration: await getMediaDuration(f.data)
|
|
920
|
+
}))
|
|
921
|
+
);
|
|
922
|
+
return [key, fileArray];
|
|
923
|
+
})
|
|
924
|
+
)
|
|
925
|
+
);
|
|
926
|
+
return new Request({
|
|
927
|
+
ip: req.ip,
|
|
928
|
+
body: _nullishCoalesce(req.body, () => ( {})),
|
|
929
|
+
cookies: _nullishCoalesce(req.cookies, () => ( {})),
|
|
930
|
+
params: _nullishCoalesce(req.params, () => ( {})),
|
|
931
|
+
query: _nullishCoalesce(req.query, () => ( {})),
|
|
932
|
+
method: req.method,
|
|
933
|
+
path: req.path,
|
|
934
|
+
headers,
|
|
935
|
+
files,
|
|
936
|
+
context: {}
|
|
937
|
+
});
|
|
938
|
+
},
|
|
939
|
+
handleResponse: async (res, response) => {
|
|
940
|
+
if (!response.piped) {
|
|
941
|
+
Object.entries(response.headers).forEach(([key, value]) => res.header(key, value));
|
|
942
|
+
const type = response.body === null || response.body === void 0 ? "json" : "send";
|
|
943
|
+
res.status(response.status)[type](response.body).end();
|
|
944
|
+
} else {
|
|
945
|
+
response.body.pipe(res);
|
|
946
|
+
}
|
|
947
|
+
},
|
|
948
|
+
registerRoute: (method, path, cb) => {
|
|
949
|
+
_optionalChain([this, 'access', _51 => _51.#expressApp, 'access', _52 => _52[method], 'optionalCall', _53 => _53(path, cb)]);
|
|
950
|
+
},
|
|
951
|
+
registerErrorHandler: (cb) => {
|
|
952
|
+
this.#expressApp.use(async (err, req, res, _next) => cb(err, req, res));
|
|
953
|
+
},
|
|
954
|
+
registerNotFoundHandler: (cb) => {
|
|
955
|
+
this.#expressApp.use(cb);
|
|
956
|
+
},
|
|
957
|
+
start: async (port) => new Promise((resolve, reject) => {
|
|
958
|
+
try {
|
|
959
|
+
const app2 = this.server.listen({ host: "0.0.0.0", port }, async () => resolve(true));
|
|
960
|
+
Instance.on("close", app2.close, 1);
|
|
961
|
+
} catch (err) {
|
|
962
|
+
reject(err);
|
|
963
|
+
}
|
|
964
|
+
})
|
|
965
|
+
});
|
|
966
|
+
this.#expressApp = app;
|
|
967
|
+
app.disable("x-powered-by");
|
|
968
|
+
if (config.config.requests.log) app.use(_pinohttp.pinoHttp.call(void 0, { logger: config.log }));
|
|
969
|
+
app.use(_express2.default.json());
|
|
970
|
+
app.use(_express2.default.text());
|
|
971
|
+
app.use(_cookieparser2.default.call(void 0, ));
|
|
972
|
+
app.use(
|
|
973
|
+
_helmet2.default.call(void 0, {
|
|
974
|
+
crossOriginResourcePolicy: { policy: "cross-origin" },
|
|
975
|
+
contentSecurityPolicy: false
|
|
976
|
+
})
|
|
977
|
+
);
|
|
978
|
+
app.use(_cors2.default.call(void 0, this.cors));
|
|
979
|
+
app.use(_express2.default.urlencoded({ extended: false }));
|
|
980
|
+
if (config.config.publicPath) app.use(_express2.default.static(config.config.publicPath));
|
|
981
|
+
app.use(
|
|
982
|
+
_expressfileupload2.default.call(void 0, {
|
|
983
|
+
limits: { fileSize: config.config.requests.maxFileUploadSizeInMb * 1024 * 1024 },
|
|
984
|
+
useTempFiles: false
|
|
985
|
+
})
|
|
986
|
+
);
|
|
987
|
+
if (config.config.requests.rateLimit.enabled)
|
|
988
|
+
app.use(
|
|
989
|
+
_expressratelimit.rateLimit.call(void 0, {
|
|
990
|
+
windowMs: config.config.requests.rateLimit.periodInMs,
|
|
991
|
+
limit: config.config.requests.rateLimit.limit,
|
|
992
|
+
handler: (_, res) => res.status(StatusCodes.TooManyRequests).json([{ message: "Too Many Requests" }])
|
|
993
|
+
})
|
|
994
|
+
);
|
|
995
|
+
}
|
|
996
|
+
};
|
|
997
|
+
|
|
998
|
+
// src/server/impls/fastify.ts
|
|
999
|
+
var _cookie = require('@fastify/cookie'); var _cookie2 = _interopRequireDefault(_cookie);
|
|
1000
|
+
var _cors3 = require('@fastify/cors'); var _cors4 = _interopRequireDefault(_cors3);
|
|
1001
|
+
var _formbody = require('@fastify/formbody'); var _formbody2 = _interopRequireDefault(_formbody);
|
|
1002
|
+
var _helmet3 = require('@fastify/helmet'); var _helmet4 = _interopRequireDefault(_helmet3);
|
|
1003
|
+
var _multipart = require('@fastify/multipart'); var _multipart2 = _interopRequireDefault(_multipart);
|
|
1004
|
+
var _ratelimit = require('@fastify/rate-limit'); var _ratelimit2 = _interopRequireDefault(_ratelimit);
|
|
1005
|
+
var _static = require('@fastify/static'); var _static2 = _interopRequireDefault(_static);
|
|
1006
|
+
var _fastify = require('fastify'); var _fastify2 = _interopRequireDefault(_fastify);
|
|
1007
|
+
var _qs = require('qs'); var _qs2 = _interopRequireDefault(_qs);
|
|
1008
|
+
var FastifyServer = class extends Server {
|
|
1009
|
+
constructor(config) {
|
|
1010
|
+
const app = _fastify2.default.call(void 0, {
|
|
1011
|
+
ignoreTrailingSlash: true,
|
|
1012
|
+
caseSensitive: false,
|
|
1013
|
+
disableRequestLogging: !config.config.requests.log,
|
|
1014
|
+
loggerInstance: config.config.requests.log ? config.log : void 0,
|
|
1015
|
+
ajv: { customOptions: { coerceTypes: false } },
|
|
1016
|
+
schemaErrorFormatter: (errors, data) => new ValidationError(
|
|
1017
|
+
errors.map((error) => ({
|
|
1018
|
+
messages: [_nullishCoalesce(error.message, () => ( ""))],
|
|
1019
|
+
field: `${data}${error.instancePath}`.replaceAll("/", ".")
|
|
1020
|
+
}))
|
|
1021
|
+
)
|
|
1022
|
+
});
|
|
1023
|
+
super(app.server, config, {
|
|
1024
|
+
parseRequest: async (req) => {
|
|
1025
|
+
const allHeaders = Object.fromEntries(Object.entries(req.headers).map(([key, val]) => [key, _nullishCoalesce(val, () => ( null))]));
|
|
1026
|
+
const headers = {
|
|
1027
|
+
...allHeaders,
|
|
1028
|
+
Authorization: _optionalChain([req, 'access', _54 => _54.headers, 'access', _55 => _55["authorization"], 'optionalAccess', _56 => _56.toString, 'call', _57 => _57()]),
|
|
1029
|
+
RefreshToken: _optionalChain([req, 'access', _58 => _58.headers, 'access', _59 => _59["x-refresh-token"], 'optionalAccess', _60 => _60.toString, 'call', _61 => _61()]),
|
|
1030
|
+
ApiKey: _optionalChain([req, 'access', _62 => _62.headers, 'access', _63 => _63["x-api-key"], 'optionalAccess', _64 => _64.toString, 'call', _65 => _65()]),
|
|
1031
|
+
ContentType: _optionalChain([req, 'access', _66 => _66.headers, 'access', _67 => _67["content-type"], 'optionalAccess', _68 => _68.toString, 'call', _69 => _69()]),
|
|
1032
|
+
Referer: _optionalChain([req, 'access', _70 => _70.headers, 'access', _71 => _71["referer"], 'optionalAccess', _72 => _72.toString, 'call', _73 => _73()]),
|
|
1033
|
+
UserAgent: _optionalChain([req, 'access', _74 => _74.headers, 'access', _75 => _75["user-agent"], 'optionalAccess', _76 => _76.toString, 'call', _77 => _77()])
|
|
1034
|
+
};
|
|
1035
|
+
const { body, files } = excludeBufferKeys(_nullishCoalesce(req.body, () => ( {})));
|
|
1036
|
+
return new Request({
|
|
1037
|
+
ip: req.ip,
|
|
1038
|
+
body,
|
|
1039
|
+
cookies: _nullishCoalesce(req.cookies, () => ( {})),
|
|
1040
|
+
params: _nullishCoalesce(req.params, () => ( {})),
|
|
1041
|
+
query: _nullishCoalesce(req.query, () => ( {})),
|
|
1042
|
+
method: req.method,
|
|
1043
|
+
path: req.url,
|
|
1044
|
+
headers,
|
|
1045
|
+
files,
|
|
1046
|
+
context: {}
|
|
1047
|
+
});
|
|
1048
|
+
},
|
|
1049
|
+
handleResponse: async (res, response) => {
|
|
1050
|
+
await res.status(response.status).headers(response.headers).send(response.body);
|
|
1051
|
+
},
|
|
1052
|
+
registerRoute: (method, path, cb) => {
|
|
1053
|
+
app.register(async (inst) => {
|
|
1054
|
+
inst.route({ url: path, method, handler: cb });
|
|
1055
|
+
});
|
|
1056
|
+
},
|
|
1057
|
+
registerErrorHandler: (cb) => {
|
|
1058
|
+
app.setErrorHandler(cb);
|
|
1059
|
+
},
|
|
1060
|
+
registerNotFoundHandler: (cb) => {
|
|
1061
|
+
app.setNotFoundHandler(cb);
|
|
1062
|
+
},
|
|
1063
|
+
start: async (port) => {
|
|
1064
|
+
await app.ready();
|
|
1065
|
+
await app.listen({ port, host: "0.0.0.0" });
|
|
1066
|
+
Instance.on("close", app.close, 1);
|
|
1067
|
+
return true;
|
|
1068
|
+
}
|
|
1069
|
+
});
|
|
1070
|
+
app.decorateRequest("savedReq", null);
|
|
1071
|
+
app.setValidatorCompiler(() => () => true);
|
|
1072
|
+
app.setSerializerCompiler(() => (data) => JSON.stringify(data));
|
|
1073
|
+
if (config.config.publicPath) app.register(_static2.default, { root: config.config.publicPath });
|
|
1074
|
+
app.register(_cookie2.default, {});
|
|
1075
|
+
app.register(_cors4.default, this.cors);
|
|
1076
|
+
app.register(_formbody2.default, { parser: (str) => _qs2.default.parse(str) });
|
|
1077
|
+
app.register(_helmet4.default, { crossOriginResourcePolicy: { policy: "cross-origin" }, contentSecurityPolicy: false });
|
|
1078
|
+
app.register(_multipart2.default, {
|
|
1079
|
+
attachFieldsToBody: "keyValues",
|
|
1080
|
+
throwFileSizeLimit: false,
|
|
1081
|
+
limits: { fileSize: config.config.requests.maxFileUploadSizeInMb * 1024 * 1024 },
|
|
1082
|
+
onFile: async (f) => {
|
|
1083
|
+
const buffer = await f.toBuffer();
|
|
1084
|
+
const parsed = {
|
|
1085
|
+
name: f.filename,
|
|
1086
|
+
type: f.mimetype,
|
|
1087
|
+
size: buffer.byteLength,
|
|
1088
|
+
isTruncated: f.file.truncated,
|
|
1089
|
+
data: buffer,
|
|
1090
|
+
duration: await getMediaDuration(buffer)
|
|
1091
|
+
};
|
|
1092
|
+
f.value = parsed;
|
|
1093
|
+
}
|
|
1094
|
+
});
|
|
1095
|
+
if (config.config.requests.rateLimit.enabled)
|
|
1096
|
+
app.register(_ratelimit2.default, {
|
|
1097
|
+
max: config.config.requests.rateLimit.limit,
|
|
1098
|
+
timeWindow: config.config.requests.rateLimit.periodInMs,
|
|
1099
|
+
errorResponseBuilder: (_, context) => ({
|
|
1100
|
+
statusCode: StatusCodes.TooManyRequests,
|
|
1101
|
+
message: JSON.stringify([{ message: `Too Many Requests. Retry in ${context.after}` }])
|
|
1102
|
+
})
|
|
1103
|
+
});
|
|
1104
|
+
}
|
|
1105
|
+
};
|
|
1106
|
+
function excludeBufferKeys(body) {
|
|
1107
|
+
if (typeof body !== "object") return { body, files: {} };
|
|
1108
|
+
const entries = Object.entries(_nullishCoalesce(body, () => ( {})));
|
|
1109
|
+
const isFile = (val) => Array.isArray(val) ? isFile(val.at(0)) : Buffer.isBuffer(_optionalChain([val, 'optionalAccess', _78 => _78.data]));
|
|
1110
|
+
const fileEntries = entries.filter(([_, value]) => isFile(value)).map(([key, value]) => [key, Array.isArray(value) ? value : [value]]);
|
|
1111
|
+
const nonFileEntries = entries.filter(([_, value]) => !isFile(value));
|
|
1112
|
+
return {
|
|
1113
|
+
body: Object.fromEntries(nonFileEntries),
|
|
1114
|
+
files: Object.fromEntries(fileEntries)
|
|
1115
|
+
};
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
// src/server/middlewares/requireAuthUser.ts
|
|
1119
|
+
var requireAuthUser = makeMiddleware(
|
|
1120
|
+
async (request) => {
|
|
1121
|
+
const user = request.users.access.value || request.users.apiKey.value;
|
|
1122
|
+
const error = request.users.access.error || request.users.apiKey.error;
|
|
1123
|
+
if (!user && error) throw error;
|
|
1124
|
+
request.authUser = user;
|
|
1125
|
+
if (!request.authUser) throw new NotAuthenticatedError();
|
|
1126
|
+
},
|
|
1127
|
+
(route) => {
|
|
1128
|
+
route.security ??= [];
|
|
1129
|
+
route.security.push({ Authorization: [] }, { ApiKey: [] });
|
|
1130
|
+
route.descriptions ??= [];
|
|
1131
|
+
route.descriptions.push("Requires a valid means of authentication.");
|
|
1132
|
+
}
|
|
1133
|
+
);
|
|
1134
|
+
var requireAuthorizationUser = makeMiddleware(
|
|
1135
|
+
async (request) => {
|
|
1136
|
+
if (request.users.access.error) throw request.users.access.error;
|
|
1137
|
+
request.authUser = request.users.access.value;
|
|
1138
|
+
if (!request.authUser) throw new NotAuthenticatedError();
|
|
1139
|
+
},
|
|
1140
|
+
(route) => {
|
|
1141
|
+
route.security ??= [];
|
|
1142
|
+
route.security.push({ Authorization: [] });
|
|
1143
|
+
route.descriptions ??= [];
|
|
1144
|
+
route.descriptions.push("Requires a valid authorization header.");
|
|
1145
|
+
}
|
|
1146
|
+
);
|
|
1147
|
+
var requireApiKeyUser = makeMiddleware(
|
|
1148
|
+
async (request) => {
|
|
1149
|
+
if (request.users.apiKey.error) throw request.users.apiKey.error;
|
|
1150
|
+
request.authUser = request.users.apiKey.value;
|
|
1151
|
+
if (!request.authUser) throw new NotAuthenticatedError();
|
|
1152
|
+
},
|
|
1153
|
+
(route) => {
|
|
1154
|
+
route.security ??= [];
|
|
1155
|
+
route.security.push({ ApiKey: [] });
|
|
1156
|
+
route.descriptions ??= [];
|
|
1157
|
+
route.descriptions.push("Requires a valid x-api-key header.");
|
|
1158
|
+
}
|
|
1159
|
+
);
|
|
1160
|
+
var requireRefreshTokenUser = makeMiddleware(
|
|
1161
|
+
async (request) => {
|
|
1162
|
+
if (request.users.refresh.error) throw request.users.refresh.error;
|
|
1163
|
+
const refreshToken = request.headers.RefreshToken;
|
|
1164
|
+
if (!refreshToken) throw new NotAuthorizedError("x-refresh-token header missing");
|
|
1165
|
+
request.users.refresh.value = await _optionalChain([Instance, 'access', _79 => _79.get, 'call', _80 => _80(), 'access', _81 => _81.settings, 'access', _82 => _82.server, 'optionalAccess', _83 => _83.requestsAuth, 'access', _84 => _84.tokens, 'optionalAccess', _85 => _85.verifyRefreshToken, 'call', _86 => _86(refreshToken)]);
|
|
1166
|
+
if (!request.users.refresh.value) throw new NotAuthorizedError();
|
|
1167
|
+
},
|
|
1168
|
+
(route) => {
|
|
1169
|
+
route.security ??= [];
|
|
1170
|
+
route.security.push({ RefreshToken: [] });
|
|
1171
|
+
route.descriptions ??= [];
|
|
1172
|
+
route.descriptions.push("Requires a valid x-refresh-token header.");
|
|
1173
|
+
}
|
|
1174
|
+
);
|
|
1175
|
+
|
|
1176
|
+
// src/server/pipes.ts
|
|
1177
|
+
|
|
1178
|
+
|
|
1179
|
+
// src/server/requests-auth/apiKeys.ts
|
|
1180
|
+
var BaseApiKeysUtility = class {
|
|
1181
|
+
};
|
|
1182
|
+
|
|
1183
|
+
// src/server/requests-auth/tokens.ts
|
|
1184
|
+
|
|
1185
|
+
var BaseTokensUtility = class {
|
|
1186
|
+
extractAccessTokenValue(headerValue) {
|
|
1187
|
+
if (!headerValue.startsWith("Bearer ")) throw new EquippedError(`authorization header must begin with 'Bearer '`, { headerValue });
|
|
1188
|
+
return headerValue.slice(7);
|
|
1189
|
+
}
|
|
1190
|
+
async exchangeTokens(tokens, getPayload) {
|
|
1191
|
+
const authUser = await this.verifyAccessToken(tokens.accessToken).catch((err) => {
|
|
1192
|
+
const error = err;
|
|
1193
|
+
if (error.statusCode === StatusCodes.AuthorizationExpired) return null;
|
|
1194
|
+
else throw err;
|
|
1195
|
+
});
|
|
1196
|
+
if (authUser) return tokens;
|
|
1197
|
+
const refreshUser = await this.verifyRefreshToken(tokens.refreshToken);
|
|
1198
|
+
const payloads = await getPayload(refreshUser.id);
|
|
1199
|
+
return {
|
|
1200
|
+
accessToken: await this.createAccessToken(payloads.access),
|
|
1201
|
+
refreshToken: await this.createRefreshToken(payloads.refresh)
|
|
1202
|
+
};
|
|
1203
|
+
}
|
|
1204
|
+
};
|
|
1205
|
+
var CacheTokensUtility = class extends BaseTokensUtility {
|
|
1206
|
+
#getAccessTokenKey;
|
|
1207
|
+
#getRefreshTokenKey;
|
|
1208
|
+
|
|
1209
|
+
constructor(options) {
|
|
1210
|
+
super();
|
|
1211
|
+
this.options = {
|
|
1212
|
+
accessTokenKey: "accessTokenKey",
|
|
1213
|
+
refreshTokenKey: "refreshTokenKey",
|
|
1214
|
+
accessTokenTTL: 60 * 60,
|
|
1215
|
+
refreshTokenTTL: 14 * 24 * 60 * 60,
|
|
1216
|
+
accessTokenPrefix: "tokens:access:",
|
|
1217
|
+
refreshTokenPrefix: "tokens:refresh:",
|
|
1218
|
+
...options
|
|
1219
|
+
};
|
|
1220
|
+
this.#getAccessTokenKey = (userId) => `${this.options.accessTokenPrefix}${userId}`;
|
|
1221
|
+
this.#getRefreshTokenKey = (userId) => `${this.options.refreshTokenPrefix}${userId}`;
|
|
1222
|
+
}
|
|
1223
|
+
async createAccessToken(payload) {
|
|
1224
|
+
const token = _jsonwebtoken2.default.sign(payload, this.options.accessTokenKey, { expiresIn: this.options.accessTokenTTL });
|
|
1225
|
+
await Instance.get().cache.set(this.#getAccessTokenKey(payload.id), token, this.options.accessTokenTTL);
|
|
1226
|
+
return token;
|
|
1227
|
+
}
|
|
1228
|
+
async createRefreshToken(payload) {
|
|
1229
|
+
const token = _jsonwebtoken2.default.sign(payload, this.options.refreshTokenKey, { expiresIn: this.options.refreshTokenTTL });
|
|
1230
|
+
await Instance.get().cache.set(this.#getRefreshTokenKey(payload.id), token, this.options.refreshTokenTTL);
|
|
1231
|
+
return token;
|
|
1232
|
+
}
|
|
1233
|
+
async verifyAccessToken(authHeader) {
|
|
1234
|
+
try {
|
|
1235
|
+
const accessToken = this.extractAccessTokenValue(authHeader);
|
|
1236
|
+
const user = _jsonwebtoken2.default.verify(accessToken, this.options.accessTokenKey);
|
|
1237
|
+
if (!user) throw new NotAuthenticatedError();
|
|
1238
|
+
const cachedToken = await this.retrieveAccessTokenFor(user.id);
|
|
1239
|
+
if (accessToken && accessToken !== cachedToken) throw new AuthorizationExpired();
|
|
1240
|
+
return user;
|
|
1241
|
+
} catch (err) {
|
|
1242
|
+
if (err instanceof AuthorizationExpired) throw err;
|
|
1243
|
+
if (err instanceof _jsonwebtoken2.default.TokenExpiredError) throw new AuthorizationExpired(void 0, err);
|
|
1244
|
+
else throw new NotAuthenticatedError(void 0, err);
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
async verifyRefreshToken(token) {
|
|
1248
|
+
try {
|
|
1249
|
+
const user = _jsonwebtoken2.default.verify(token, this.options.refreshTokenKey);
|
|
1250
|
+
if (!user) throw new NotAuthenticatedError();
|
|
1251
|
+
return user;
|
|
1252
|
+
} catch (err) {
|
|
1253
|
+
throw new NotAuthenticatedError(void 0, err);
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
async retrieveAccessTokenFor(userId) {
|
|
1257
|
+
return Instance.get().cache.get(this.#getAccessTokenKey(userId));
|
|
1258
|
+
}
|
|
1259
|
+
async retrieveRefreshTokenFor(userId) {
|
|
1260
|
+
return Instance.get().cache.get(this.#getRefreshTokenKey(userId));
|
|
1261
|
+
}
|
|
1262
|
+
async deleteAccessTokenFor(userId) {
|
|
1263
|
+
await Instance.get().cache.delete(this.#getAccessTokenKey(userId));
|
|
1264
|
+
}
|
|
1265
|
+
async deleteRefreshTokenFor(userId) {
|
|
1266
|
+
await Instance.get().cache.delete(this.#getRefreshTokenKey(userId));
|
|
1267
|
+
}
|
|
1268
|
+
};
|
|
1269
|
+
|
|
1270
|
+
// src/server/pipes.ts
|
|
1271
|
+
var serverPipe = _valleyed.v.object({
|
|
1272
|
+
type: _valleyed.v.in(["fastify", "express"]),
|
|
1273
|
+
port: _valleyed.v.number(),
|
|
1274
|
+
publicPath: _valleyed.v.optional(_valleyed.v.string()),
|
|
1275
|
+
healthPath: _valleyed.v.optional(_valleyed.v.string()),
|
|
1276
|
+
openapi: _valleyed.v.defaults(
|
|
1277
|
+
_valleyed.v.object({
|
|
1278
|
+
docsVersion: _valleyed.v.defaults(_valleyed.v.string(), "1.0.0"),
|
|
1279
|
+
docsBaseUrl: _valleyed.v.defaults(_valleyed.v.array(_valleyed.v.string()), ["/"]),
|
|
1280
|
+
docsPath: _valleyed.v.defaults(_valleyed.v.string(), "/__docs")
|
|
1281
|
+
}),
|
|
1282
|
+
{}
|
|
1283
|
+
),
|
|
1284
|
+
requests: _valleyed.v.defaults(
|
|
1285
|
+
_valleyed.v.object({
|
|
1286
|
+
log: _valleyed.v.defaults(_valleyed.v.boolean(), true),
|
|
1287
|
+
paginationDefaultLimit: _valleyed.v.defaults(_valleyed.v.number(), 100),
|
|
1288
|
+
maxFileUploadSizeInMb: _valleyed.v.defaults(_valleyed.v.number(), 500),
|
|
1289
|
+
rateLimit: _valleyed.v.defaults(
|
|
1290
|
+
_valleyed.v.object({
|
|
1291
|
+
enabled: _valleyed.v.defaults(_valleyed.v.boolean(), false),
|
|
1292
|
+
periodInMs: _valleyed.v.defaults(_valleyed.v.number(), 60 * 60 * 1e3),
|
|
1293
|
+
limit: _valleyed.v.defaults(_valleyed.v.number(), 5e3)
|
|
1294
|
+
}),
|
|
1295
|
+
{}
|
|
1296
|
+
),
|
|
1297
|
+
slowdown: _valleyed.v.defaults(
|
|
1298
|
+
_valleyed.v.object({
|
|
1299
|
+
enabled: _valleyed.v.defaults(_valleyed.v.boolean(), false),
|
|
1300
|
+
periodInMs: _valleyed.v.defaults(_valleyed.v.number(), 10 * 60 * 1e3),
|
|
1301
|
+
delayAfter: _valleyed.v.defaults(_valleyed.v.number(), 2e3),
|
|
1302
|
+
delayInMs: _valleyed.v.defaults(_valleyed.v.number(), 500)
|
|
1303
|
+
}),
|
|
1304
|
+
{}
|
|
1305
|
+
)
|
|
1306
|
+
}),
|
|
1307
|
+
{}
|
|
1308
|
+
),
|
|
1309
|
+
requestsAuth: _valleyed.v.defaults(
|
|
1310
|
+
_valleyed.v.object({
|
|
1311
|
+
tokens: _valleyed.v.optional(_valleyed.v.instanceOf(BaseTokensUtility)),
|
|
1312
|
+
apiKey: _valleyed.v.optional(_valleyed.v.instanceOf(BaseApiKeysUtility))
|
|
1313
|
+
}),
|
|
1314
|
+
{}
|
|
1315
|
+
)
|
|
1316
|
+
});
|
|
1317
|
+
|
|
1318
|
+
// src/errors/types/authorizationExpired.ts
|
|
1319
|
+
var AuthorizationExpired = (_class4 = class extends RequestError {
|
|
1320
|
+
__init10() {this.statusCode = StatusCodes.AuthorizationExpired}
|
|
1321
|
+
constructor(message = "Access token expired", cause) {
|
|
1322
|
+
super(message, [{ message }], cause);_class4.prototype.__init10.call(this);;
|
|
1323
|
+
}
|
|
1324
|
+
}, _class4);
|
|
1325
|
+
|
|
1326
|
+
// src/errors/types/badRequestError.ts
|
|
1327
|
+
var BadRequestError = (_class5 = class extends RequestError {
|
|
1328
|
+
__init11() {this.statusCode = StatusCodes.BadRequest}
|
|
1329
|
+
constructor(message, cause) {
|
|
1330
|
+
super(message, [{ message }], cause);_class5.prototype.__init11.call(this);;
|
|
1331
|
+
}
|
|
1332
|
+
}, _class5);
|
|
1333
|
+
|
|
1334
|
+
// src/errors/types/notAuthenticatedError.ts
|
|
1335
|
+
var NotAuthenticatedError = (_class6 = class extends RequestError {
|
|
1336
|
+
__init12() {this.statusCode = StatusCodes.NotAuthenticated}
|
|
1337
|
+
constructor(message = "Not authenticated", cause) {
|
|
1338
|
+
super(message, [{ message }], cause);_class6.prototype.__init12.call(this);;
|
|
1339
|
+
}
|
|
1340
|
+
}, _class6);
|
|
1341
|
+
|
|
1342
|
+
// src/errors/types/notAuthorizedError.ts
|
|
1343
|
+
var NotAuthorizedError = (_class7 = class extends RequestError {
|
|
1344
|
+
__init13() {this.statusCode = StatusCodes.NotAuthorized}
|
|
1345
|
+
constructor(message = "Not authorized", cause) {
|
|
1346
|
+
super(message, [{ message }], cause);_class7.prototype.__init13.call(this);;
|
|
1347
|
+
}
|
|
1348
|
+
}, _class7);
|
|
1349
|
+
|
|
1350
|
+
// src/errors/types/notFoundError.ts
|
|
1351
|
+
var NotFoundError = (_class8 = class extends RequestError {
|
|
1352
|
+
__init14() {this.statusCode = StatusCodes.NotFound}
|
|
1353
|
+
constructor(message = "Not found", cause) {
|
|
1354
|
+
super(message, [{ message }], cause);_class8.prototype.__init14.call(this);;
|
|
1355
|
+
}
|
|
1356
|
+
}, _class8);
|
|
1357
|
+
|
|
1358
|
+
// src/errors/types/refreshTokenMisusedError.ts
|
|
1359
|
+
var RefreshTokenMisusedError = (_class9 = class extends RequestError {
|
|
1360
|
+
__init15() {this.statusCode = StatusCodes.NotAuthenticated}
|
|
1361
|
+
constructor(message = "Refresh token misused", cause) {
|
|
1362
|
+
super(message, [{ message }], cause);_class9.prototype.__init15.call(this);;
|
|
1363
|
+
}
|
|
1364
|
+
}, _class9);
|
|
1365
|
+
|
|
1366
|
+
// src/errors/types/validationError.ts
|
|
1367
|
+
var ValidationError = (_class10 = class extends RequestError {
|
|
1368
|
+
__init16() {this.statusCode = StatusCodes.ValidationError}
|
|
1369
|
+
constructor(errors, cause) {
|
|
1370
|
+
super(
|
|
1371
|
+
"Unprocessable Entity",
|
|
1372
|
+
errors.flatMap(({ field, messages }) => messages.map((message) => ({ message, field }))),
|
|
1373
|
+
cause
|
|
1374
|
+
);_class10.prototype.__init16.call(this);;
|
|
1375
|
+
}
|
|
1376
|
+
}, _class10);
|
|
1377
|
+
|
|
1378
|
+
// src/cache/types/redis.ts
|
|
1379
|
+
var RedisCache = class extends Cache {
|
|
1380
|
+
|
|
1381
|
+
constructor(settings, extraConfig) {
|
|
1382
|
+
super();
|
|
1383
|
+
const node = {
|
|
1384
|
+
...settings.host ? { host: settings.host } : {},
|
|
1385
|
+
...settings.port ? { port: settings.port } : {}
|
|
1386
|
+
};
|
|
1387
|
+
const common = {
|
|
1388
|
+
...extraConfig,
|
|
1389
|
+
...settings.password ? { password: settings.password } : {},
|
|
1390
|
+
...settings.username ? { username: settings.username } : {},
|
|
1391
|
+
...settings.tls ? { tls: {} } : {},
|
|
1392
|
+
lazyConnect: true
|
|
1393
|
+
};
|
|
1394
|
+
this.client = settings.cluster ? new (0, _ioredis.Cluster)([node], {
|
|
1395
|
+
...extraConfig,
|
|
1396
|
+
redisOptions: common,
|
|
1397
|
+
lazyConnect: true
|
|
1398
|
+
}) : new (0, _ioredis.Redis)({ ...common, ...node });
|
|
1399
|
+
this.client.on("error", async (error) => {
|
|
1400
|
+
Instance.crash(new EquippedError(`Redis failed with error`, {}, error));
|
|
1401
|
+
});
|
|
1402
|
+
if (!extraConfig) Instance.on("start", async () => this.client.connect(), 1);
|
|
1403
|
+
Instance.on("close", async () => this.client.quit(), 1);
|
|
1404
|
+
}
|
|
1405
|
+
async delete(key) {
|
|
1406
|
+
await this.client.del(Instance.get().getScopedName(key, ":"));
|
|
1407
|
+
}
|
|
1408
|
+
async get(key) {
|
|
1409
|
+
return await this.client.get(Instance.get().getScopedName(key, ":"));
|
|
1410
|
+
}
|
|
1411
|
+
async set(key, data, ttlInSecs) {
|
|
1412
|
+
if (ttlInSecs > 0) await this.client.setex(Instance.get().getScopedName(key, ":"), ttlInSecs, data);
|
|
1413
|
+
else this.client.set(Instance.get().getScopedName(key), data);
|
|
1414
|
+
}
|
|
1415
|
+
async getOrSet(key, fn, ttlInSecs) {
|
|
1416
|
+
const cached = await this.get(Instance.get().getScopedName(key, ":"));
|
|
1417
|
+
if (cached) return JSON.parse(cached);
|
|
1418
|
+
const result = await fn();
|
|
1419
|
+
await this.set(Instance.get().getScopedName(key, ":"), JSON.stringify(result), ttlInSecs);
|
|
1420
|
+
}
|
|
1421
|
+
};
|
|
1422
|
+
|
|
1423
|
+
// src/dbs/base/_instance.ts
|
|
1424
|
+
|
|
1425
|
+
var TopicPrefix = "db-changes";
|
|
1426
|
+
var Db = class {
|
|
1427
|
+
constructor(config) {
|
|
1428
|
+
this.config = config;
|
|
1429
|
+
}
|
|
1430
|
+
getScopedDb(db) {
|
|
1431
|
+
return Instance.get().getScopedName(db).replaceAll(".", "-");
|
|
1432
|
+
}
|
|
1433
|
+
};
|
|
1434
|
+
var DbChange = class {
|
|
1435
|
+
constructor(config, callbacks, mapper) {
|
|
1436
|
+
this.config = config;
|
|
1437
|
+
this.#callbacks = callbacks;
|
|
1438
|
+
this.#mapper = mapper;
|
|
1439
|
+
}
|
|
1440
|
+
#callbacks = {};
|
|
1441
|
+
#mapper;
|
|
1442
|
+
get callbacks() {
|
|
1443
|
+
return Object.freeze(this.#callbacks);
|
|
1444
|
+
}
|
|
1445
|
+
get mapper() {
|
|
1446
|
+
return this.#mapper;
|
|
1447
|
+
}
|
|
1448
|
+
async configureConnector(key, data) {
|
|
1449
|
+
const instance = _axios2.default.create({ baseURL: this.config.debeziumUrl });
|
|
1450
|
+
return await instance.put(`/connectors/${key}/config`, {
|
|
1451
|
+
"topic.prefix": TopicPrefix,
|
|
1452
|
+
"topic.creation.enable": "false",
|
|
1453
|
+
"topic.creation.default.replication.factor": `-1`,
|
|
1454
|
+
"topic.creation.default.partitions": "-1",
|
|
1455
|
+
"key.converter": "org.apache.kafka.connect.json.JsonConverter",
|
|
1456
|
+
"key.converter.schemas.enable": "false",
|
|
1457
|
+
"value.converter": "org.apache.kafka.connect.json.JsonConverter",
|
|
1458
|
+
"value.converter.schemas.enable": "false",
|
|
1459
|
+
...data
|
|
1460
|
+
}).then(async () => {
|
|
1461
|
+
const topics = await instance.get(`/connectors/${key}/topics`);
|
|
1462
|
+
return _nullishCoalesce(_optionalChain([topics, 'access', _87 => _87.data, 'access', _88 => _88[key], 'optionalAccess', _89 => _89.topics, 'optionalAccess', _90 => _90.includes, 'optionalCall', _91 => _91(key)]), () => ( false));
|
|
1463
|
+
}).catch((err) => {
|
|
1464
|
+
throw new EquippedError(`Failed to configure watcher`, { key }, err);
|
|
1465
|
+
});
|
|
1466
|
+
}
|
|
1467
|
+
};
|
|
1468
|
+
|
|
1469
|
+
// src/dbs/mongo/changes.ts
|
|
1470
|
+
var _mongodb = require('mongodb');
|
|
1471
|
+
|
|
1472
|
+
var MongoDbChange = class extends DbChange {
|
|
1473
|
+
#started = false;
|
|
1474
|
+
constructor(config, change, client, dbName, colName, callbacks, mapper) {
|
|
1475
|
+
super(change, callbacks, mapper);
|
|
1476
|
+
const hydrate = (data) => data._id ? {
|
|
1477
|
+
...data,
|
|
1478
|
+
_id: makeId(_nullishCoalesce(data._id["$oid"], () => ( data._id)))
|
|
1479
|
+
} : void 0;
|
|
1480
|
+
const dbColName = `${dbName}.${colName}`;
|
|
1481
|
+
const topic = `${TopicPrefix}.${dbColName}`;
|
|
1482
|
+
const hexId = "5f5f65717569707065645f5f";
|
|
1483
|
+
const TestId = makeId(hexId);
|
|
1484
|
+
const condition = { _id: TestId };
|
|
1485
|
+
change.eventBus.createSubscriber(
|
|
1486
|
+
topic,
|
|
1487
|
+
async (data) => {
|
|
1488
|
+
const op = data.op;
|
|
1489
|
+
let before = JSON.parse(_nullishCoalesce(data.before, () => ( "null")));
|
|
1490
|
+
let after = JSON.parse(_nullishCoalesce(data.after, () => ( "null")));
|
|
1491
|
+
if (before) before = hydrate(before);
|
|
1492
|
+
if (after) after = hydrate(after);
|
|
1493
|
+
if (_optionalChain([before, 'optionalAccess', _92 => _92.__id]) === TestId || _optionalChain([after, 'optionalAccess', _93 => _93.__id]) === TestId) return;
|
|
1494
|
+
if (op === "c" && this.callbacks.created && after)
|
|
1495
|
+
await this.callbacks.created({
|
|
1496
|
+
before: null,
|
|
1497
|
+
after: this.mapper(after)
|
|
1498
|
+
});
|
|
1499
|
+
else if (op === "u" && this.callbacks.updated && before && after)
|
|
1500
|
+
await this.callbacks.updated({
|
|
1501
|
+
before: this.mapper(before),
|
|
1502
|
+
after: this.mapper(after),
|
|
1503
|
+
changes: _valleyed.differ.from(_valleyed.differ.diff(before, after))
|
|
1504
|
+
});
|
|
1505
|
+
else if (op === "d" && this.callbacks.deleted && before)
|
|
1506
|
+
await this.callbacks.deleted({
|
|
1507
|
+
before: this.mapper(before),
|
|
1508
|
+
after: null
|
|
1509
|
+
});
|
|
1510
|
+
},
|
|
1511
|
+
{ skipScope: true }
|
|
1512
|
+
);
|
|
1513
|
+
Instance.on(
|
|
1514
|
+
"start",
|
|
1515
|
+
async () => {
|
|
1516
|
+
if (this.#started) return;
|
|
1517
|
+
this.#started = true;
|
|
1518
|
+
const collection = client.db(dbName).collection(colName);
|
|
1519
|
+
await retry(
|
|
1520
|
+
async () => {
|
|
1521
|
+
const started = await this.configureConnector(topic, {
|
|
1522
|
+
"connector.class": "io.debezium.connector.mongodb.MongoDbConnector",
|
|
1523
|
+
"capture.mode": "change_streams_update_full_with_pre_image",
|
|
1524
|
+
"mongodb.connection.string": config.uri,
|
|
1525
|
+
"collection.include.list": dbColName,
|
|
1526
|
+
"snapshot.mode": "when_needed"
|
|
1527
|
+
});
|
|
1528
|
+
if (started) return { done: true, value: true };
|
|
1529
|
+
await collection.findOneAndUpdate(condition, { $set: { colName } }, { upsert: true });
|
|
1530
|
+
await collection.findOneAndDelete(condition);
|
|
1531
|
+
Instance.get().log.warn(`Waiting for db changes for ${dbColName} to start...`);
|
|
1532
|
+
return { done: false };
|
|
1533
|
+
},
|
|
1534
|
+
6,
|
|
1535
|
+
1e4
|
|
1536
|
+
).catch((err) => Instance.crash(new EquippedError(`Failed to start db changes`, { dbColName }, err)));
|
|
1537
|
+
},
|
|
1538
|
+
10
|
|
1539
|
+
);
|
|
1540
|
+
}
|
|
1541
|
+
};
|
|
1542
|
+
var makeId = (id) => {
|
|
1543
|
+
try {
|
|
1544
|
+
return new (0, _mongodb.ObjectId)(id);
|
|
1545
|
+
} catch (e4) {
|
|
1546
|
+
return id;
|
|
1547
|
+
}
|
|
1548
|
+
};
|
|
1549
|
+
|
|
1550
|
+
// src/dbs/mongo/index.ts
|
|
1551
|
+
|
|
1552
|
+
|
|
1553
|
+
// src/dbs/mongo/api.ts
|
|
1554
|
+
|
|
1555
|
+
|
|
1556
|
+
// src/dbs/pipes.ts
|
|
1557
|
+
|
|
1558
|
+
var QueryKeys = /* @__PURE__ */ ((QueryKeys2) => {
|
|
1559
|
+
QueryKeys2["and"] = "and";
|
|
1560
|
+
QueryKeys2["or"] = "or";
|
|
1561
|
+
return QueryKeys2;
|
|
1562
|
+
})(QueryKeys || {});
|
|
1563
|
+
var Conditions = /* @__PURE__ */ ((Conditions2) => {
|
|
1564
|
+
Conditions2["lt"] = "lt";
|
|
1565
|
+
Conditions2["lte"] = "lte";
|
|
1566
|
+
Conditions2["gt"] = "gt";
|
|
1567
|
+
Conditions2["gte"] = "gte";
|
|
1568
|
+
Conditions2["eq"] = "eq";
|
|
1569
|
+
Conditions2["ne"] = "ne";
|
|
1570
|
+
Conditions2["in"] = "in";
|
|
1571
|
+
Conditions2["nin"] = "nin";
|
|
1572
|
+
Conditions2["exists"] = "exists";
|
|
1573
|
+
return Conditions2;
|
|
1574
|
+
})(Conditions || {});
|
|
1575
|
+
var queryKeys = _valleyed.v.catch(_valleyed.v.defaults(_valleyed.v.in(["and" /* and */, "or" /* or */]), "and" /* and */), "and" /* and */);
|
|
1576
|
+
var queryWhere = _valleyed.v.object({
|
|
1577
|
+
field: _valleyed.v.string(),
|
|
1578
|
+
value: _valleyed.v.any(),
|
|
1579
|
+
condition: _valleyed.v.catch(_valleyed.v.defaults(_valleyed.v.in(Object.values(Conditions)), "eq" /* eq */), "eq" /* eq */)
|
|
1580
|
+
});
|
|
1581
|
+
var queryWhereBlock = _valleyed.v.object({
|
|
1582
|
+
condition: queryKeys,
|
|
1583
|
+
value: _valleyed.v.array(queryWhere)
|
|
1584
|
+
});
|
|
1585
|
+
var queryWhereClause = _valleyed.v.defaults(_valleyed.v.array(_valleyed.v.or([queryWhere, queryWhereBlock])), []);
|
|
1586
|
+
function queryParamsPipe() {
|
|
1587
|
+
const pagLimit = _nullishCoalesce(_optionalChain([Instance, 'access', _94 => _94.get, 'call', _95 => _95(), 'access', _96 => _96.settings, 'access', _97 => _97.server, 'optionalAccess', _98 => _98.requests, 'access', _99 => _99.paginationDefaultLimit]), () => ( 100));
|
|
1588
|
+
return _valleyed.v.meta(
|
|
1589
|
+
_valleyed.v.object({
|
|
1590
|
+
all: _valleyed.v.defaults(_valleyed.v.boolean(), false),
|
|
1591
|
+
limit: _valleyed.v.catch(_valleyed.v.defaults(_valleyed.v.number().pipe(_valleyed.v.lte(pagLimit)), pagLimit), pagLimit),
|
|
1592
|
+
page: _valleyed.v.catch(_valleyed.v.defaults(_valleyed.v.number().pipe(_valleyed.v.gte(1)), 1), 1),
|
|
1593
|
+
search: _valleyed.v.defaults(
|
|
1594
|
+
_valleyed.v.nullish(
|
|
1595
|
+
_valleyed.v.object({
|
|
1596
|
+
value: _valleyed.v.string(),
|
|
1597
|
+
fields: _valleyed.v.array(_valleyed.v.string())
|
|
1598
|
+
})
|
|
1599
|
+
),
|
|
1600
|
+
null
|
|
1601
|
+
),
|
|
1602
|
+
sort: _valleyed.v.defaults(
|
|
1603
|
+
_valleyed.v.array(
|
|
1604
|
+
_valleyed.v.object({
|
|
1605
|
+
field: _valleyed.v.string(),
|
|
1606
|
+
desc: _valleyed.v.defaults(_valleyed.v.boolean(), false)
|
|
1607
|
+
})
|
|
1608
|
+
),
|
|
1609
|
+
[]
|
|
1610
|
+
),
|
|
1611
|
+
whereType: queryKeys,
|
|
1612
|
+
where: queryWhereClause
|
|
1613
|
+
}).pipe((p) => ({ ...p, auth: [], authType: "and" /* and */ })),
|
|
1614
|
+
{ title: "Query Params", $refId: "QueryParams" }
|
|
1615
|
+
);
|
|
1616
|
+
}
|
|
1617
|
+
function queryResultsPipe(model) {
|
|
1618
|
+
return _valleyed.v.object({
|
|
1619
|
+
pages: _valleyed.v.object({
|
|
1620
|
+
current: _valleyed.v.number(),
|
|
1621
|
+
start: _valleyed.v.number(),
|
|
1622
|
+
last: _valleyed.v.number(),
|
|
1623
|
+
previous: _valleyed.v.nullable(_valleyed.v.number()),
|
|
1624
|
+
next: _valleyed.v.nullable(_valleyed.v.number())
|
|
1625
|
+
}),
|
|
1626
|
+
docs: _valleyed.v.object({
|
|
1627
|
+
limit: _valleyed.v.number(),
|
|
1628
|
+
total: _valleyed.v.number(),
|
|
1629
|
+
count: _valleyed.v.number()
|
|
1630
|
+
}),
|
|
1631
|
+
results: _valleyed.v.array(model)
|
|
1632
|
+
});
|
|
1633
|
+
}
|
|
1634
|
+
function wrapQueryParams(params) {
|
|
1635
|
+
return _valleyed.v.assert(queryParamsPipe(), params);
|
|
1636
|
+
}
|
|
1637
|
+
var mongoDbConfigPipe = _valleyed.v.meta(
|
|
1638
|
+
_valleyed.v.object({
|
|
1639
|
+
uri: _valleyed.v.string()
|
|
1640
|
+
}),
|
|
1641
|
+
{ title: "Mongodb Config", $refId: "MongodbConfig" }
|
|
1642
|
+
);
|
|
1643
|
+
|
|
1644
|
+
// src/dbs/mongo/query.ts
|
|
1645
|
+
var parseMongodbQueryParams = async (collection, params) => {
|
|
1646
|
+
const query = [];
|
|
1647
|
+
const where = buildWhereQuery(params.where, params.whereType);
|
|
1648
|
+
if (where) query.push(where);
|
|
1649
|
+
const auth = buildWhereQuery(params.auth, params.authType);
|
|
1650
|
+
if (auth) query.push(auth);
|
|
1651
|
+
if (params.search && params.search.fields.length > 0) {
|
|
1652
|
+
const search = params.search.fields.map((field) => ({
|
|
1653
|
+
[field]: {
|
|
1654
|
+
$regex: new RegExp(params.search.value, "i")
|
|
1655
|
+
}
|
|
1656
|
+
}));
|
|
1657
|
+
query.push({ $or: search });
|
|
1658
|
+
}
|
|
1659
|
+
const totalClause = {};
|
|
1660
|
+
if (query.length > 0) totalClause["$and"] = query;
|
|
1661
|
+
const sort = params.sort.map((p) => [p.field, p.desc ? "desc" : "asc"]);
|
|
1662
|
+
const all = _nullishCoalesce(params.all, () => ( false));
|
|
1663
|
+
const limit = params.limit;
|
|
1664
|
+
const page = params.page;
|
|
1665
|
+
const total = await collection.countDocuments(totalClause);
|
|
1666
|
+
let builtQuery = collection.find(totalClause);
|
|
1667
|
+
if (sort.length) builtQuery = builtQuery.sort(Object.fromEntries(sort));
|
|
1668
|
+
if (!all && limit) {
|
|
1669
|
+
builtQuery = builtQuery.limit(limit);
|
|
1670
|
+
if (page) builtQuery = builtQuery.skip((page - 1) * limit);
|
|
1671
|
+
}
|
|
1672
|
+
const results = await builtQuery.toArray();
|
|
1673
|
+
const start = 1;
|
|
1674
|
+
const last = Math.ceil(total / limit) || 1;
|
|
1675
|
+
const next = page >= last ? null : page + 1;
|
|
1676
|
+
const previous = page <= start ? null : page - 1;
|
|
1677
|
+
return {
|
|
1678
|
+
pages: { start, last, next, previous, current: page },
|
|
1679
|
+
docs: { limit, total, count: results.length },
|
|
1680
|
+
results
|
|
1681
|
+
};
|
|
1682
|
+
};
|
|
1683
|
+
function isWhereBlock(param) {
|
|
1684
|
+
return Object.values(QueryKeys).includes(param.condition);
|
|
1685
|
+
}
|
|
1686
|
+
var buildWhereQuery = (params, key = "and" /* and */) => {
|
|
1687
|
+
const where = (Array.isArray(params) ? params : []).map((param) => {
|
|
1688
|
+
if (isWhereBlock(param)) return buildWhereQuery(param.value, param.condition);
|
|
1689
|
+
const { field } = param;
|
|
1690
|
+
const checkedField = field === "id" ? "_id" : _nullishCoalesce(field, () => ( ""));
|
|
1691
|
+
const checkedValue = param.value === void 0 ? "" : param.value;
|
|
1692
|
+
return {
|
|
1693
|
+
field: checkedField,
|
|
1694
|
+
value: checkedValue,
|
|
1695
|
+
condition: param.condition,
|
|
1696
|
+
isWhere: true
|
|
1697
|
+
};
|
|
1698
|
+
}).filter((c) => !!c).map((c) => {
|
|
1699
|
+
if (c.isWhere) return { [`${c.field}`]: { [`$${c.condition}`]: c.value } };
|
|
1700
|
+
else return c;
|
|
1701
|
+
});
|
|
1702
|
+
return where.length > 0 ? { [`$${key}`]: where } : null;
|
|
1703
|
+
};
|
|
1704
|
+
|
|
1705
|
+
// src/dbs/mongo/api.ts
|
|
1706
|
+
var idKey = "_id";
|
|
1707
|
+
function getTable(config, collection) {
|
|
1708
|
+
async function transform(doc) {
|
|
1709
|
+
const docs = Array.isArray(doc) ? doc : [doc];
|
|
1710
|
+
const mapped = docs.map((d) => config.mapper(d));
|
|
1711
|
+
return Array.isArray(doc) ? mapped : mapped[0];
|
|
1712
|
+
}
|
|
1713
|
+
function prepInsertValue(value, id, now, skipUpdate) {
|
|
1714
|
+
const base = {
|
|
1715
|
+
[idKey]: id,
|
|
1716
|
+
..._optionalChain([config, 'access', _100 => _100.options, 'optionalAccess', _101 => _101.skipAudit]) ? {} : {
|
|
1717
|
+
createdAt: now.getTime(),
|
|
1718
|
+
...skipUpdate ? {} : { updatedAt: now.getTime() }
|
|
1719
|
+
}
|
|
1720
|
+
};
|
|
1721
|
+
return {
|
|
1722
|
+
...value,
|
|
1723
|
+
...base
|
|
1724
|
+
};
|
|
1725
|
+
}
|
|
1726
|
+
function prepUpdateValue(value, now) {
|
|
1727
|
+
return {
|
|
1728
|
+
...value,
|
|
1729
|
+
$set: {
|
|
1730
|
+
...value.$set,
|
|
1731
|
+
...Object.keys(value).length > 0 && !_optionalChain([config, 'access', _102 => _102.options, 'optionalAccess', _103 => _103.skipAudit]) ? {
|
|
1732
|
+
updatedAt: now.getTime()
|
|
1733
|
+
} : {}
|
|
1734
|
+
}
|
|
1735
|
+
};
|
|
1736
|
+
}
|
|
1737
|
+
const table = {
|
|
1738
|
+
config,
|
|
1739
|
+
extras: { collection },
|
|
1740
|
+
query: async (params) => {
|
|
1741
|
+
const results = await parseMongodbQueryParams(collection, params);
|
|
1742
|
+
return {
|
|
1743
|
+
...results,
|
|
1744
|
+
results: await transform(results.results)
|
|
1745
|
+
};
|
|
1746
|
+
},
|
|
1747
|
+
findMany: async (filter, options = {}) => {
|
|
1748
|
+
const sortArray = Array.isArray(options.sort) ? options.sort : options.sort ? [options.sort] : [];
|
|
1749
|
+
const sort = sortArray.map((p) => [p.field, p.desc ? "desc" : "asc"]);
|
|
1750
|
+
const docs = await collection.find(filter, {
|
|
1751
|
+
session: options.session,
|
|
1752
|
+
limit: options.limit,
|
|
1753
|
+
sort
|
|
1754
|
+
}).toArray();
|
|
1755
|
+
return transform(docs);
|
|
1756
|
+
},
|
|
1757
|
+
findOne: async (filter, options = {}) => {
|
|
1758
|
+
const result = await table.findMany(filter, { ...options, limit: 1 });
|
|
1759
|
+
return _nullishCoalesce(result.at(0), () => ( null));
|
|
1760
|
+
},
|
|
1761
|
+
findById: async (id, options = {}) => {
|
|
1762
|
+
const result = await table.findOne({ [idKey]: id }, options);
|
|
1763
|
+
return result;
|
|
1764
|
+
},
|
|
1765
|
+
insertMany: async (values, options = {}) => {
|
|
1766
|
+
const now = _nullishCoalesce(_optionalChain([options, 'access', _104 => _104.getTime, 'optionalCall', _105 => _105()]), () => ( /* @__PURE__ */ new Date()));
|
|
1767
|
+
const payload = values.map((value, i) => prepInsertValue(value, _nullishCoalesce(_optionalChain([options, 'access', _106 => _106.makeId, 'optionalCall', _107 => _107(i)]), () => ( new (0, _mongodb.ObjectId)().toString())), now));
|
|
1768
|
+
await collection.insertMany(payload, { session: options.session });
|
|
1769
|
+
const insertedData = await Promise.all(payload.map(async (data) => await table.findById(data[idKey], options)));
|
|
1770
|
+
return insertedData.filter((value) => !!value);
|
|
1771
|
+
},
|
|
1772
|
+
insertOne: async (values, options = {}) => {
|
|
1773
|
+
const result = await table.insertMany([values], options);
|
|
1774
|
+
return result[0];
|
|
1775
|
+
},
|
|
1776
|
+
updateMany: async (filter, values, options = {}) => {
|
|
1777
|
+
const now = _nullishCoalesce(_optionalChain([options, 'access', _108 => _108.getTime, 'optionalCall', _109 => _109()]), () => ( /* @__PURE__ */ new Date()));
|
|
1778
|
+
await collection.updateMany(filter, prepUpdateValue(values, now), { session: options.session });
|
|
1779
|
+
return table.findMany(filter, options);
|
|
1780
|
+
},
|
|
1781
|
+
updateOne: async (filter, values, options = {}) => {
|
|
1782
|
+
const now = _nullishCoalesce(_optionalChain([options, 'access', _110 => _110.getTime, 'optionalCall', _111 => _111()]), () => ( /* @__PURE__ */ new Date()));
|
|
1783
|
+
const doc = await collection.findOneAndUpdate(filter, prepUpdateValue(values, now), {
|
|
1784
|
+
returnDocument: "after",
|
|
1785
|
+
session: options.session
|
|
1786
|
+
});
|
|
1787
|
+
return doc ? transform(doc) : null;
|
|
1788
|
+
},
|
|
1789
|
+
updateById: async (id, values, options = {}) => {
|
|
1790
|
+
const result = await table.updateOne({ [idKey]: id }, values, options);
|
|
1791
|
+
return result;
|
|
1792
|
+
},
|
|
1793
|
+
upsertOne: async (filter, values, options = {}) => {
|
|
1794
|
+
const now = _nullishCoalesce(_optionalChain([options, 'access', _112 => _112.getTime, 'optionalCall', _113 => _113()]), () => ( /* @__PURE__ */ new Date()));
|
|
1795
|
+
const doc = await collection.findOneAndUpdate(
|
|
1796
|
+
filter,
|
|
1797
|
+
{
|
|
1798
|
+
...prepUpdateValue("update" in values ? values.update : {}, now),
|
|
1799
|
+
// @ts-expect-error fighting ts
|
|
1800
|
+
$setOnInsert: prepInsertValue(values.insert, _nullishCoalesce(_optionalChain([options, 'access', _114 => _114.makeId, 'optionalCall', _115 => _115()]), () => ( new (0, _mongodb.ObjectId)().toString())), now, true)
|
|
1801
|
+
},
|
|
1802
|
+
{ returnDocument: "after", session: options.session, upsert: true }
|
|
1803
|
+
);
|
|
1804
|
+
return transform(doc);
|
|
1805
|
+
},
|
|
1806
|
+
deleteMany: async (filter, options = {}) => {
|
|
1807
|
+
const docs = await table.findMany(filter, options);
|
|
1808
|
+
await collection.deleteMany(filter, { session: options.session });
|
|
1809
|
+
return docs;
|
|
1810
|
+
},
|
|
1811
|
+
deleteOne: async (filter, options) => {
|
|
1812
|
+
const doc = await collection.findOneAndDelete(filter, { session: _optionalChain([options, 'optionalAccess', _116 => _116.session]) });
|
|
1813
|
+
return doc ? transform(doc) : null;
|
|
1814
|
+
},
|
|
1815
|
+
deleteById: async (id, options) => {
|
|
1816
|
+
const result = await table.deleteOne({ [idKey]: id }, options);
|
|
1817
|
+
return result;
|
|
1818
|
+
},
|
|
1819
|
+
bulkWrite: async (operations, options = {}) => {
|
|
1820
|
+
const bulk = collection.initializeUnorderedBulkOp({ session: options.session });
|
|
1821
|
+
const now = _nullishCoalesce(_optionalChain([options, 'access', _117 => _117.getTime, 'optionalCall', _118 => _118()]), () => ( /* @__PURE__ */ new Date()));
|
|
1822
|
+
operations.forEach((operation, i) => {
|
|
1823
|
+
switch (operation.op) {
|
|
1824
|
+
case "insert":
|
|
1825
|
+
bulk.insert(prepInsertValue(operation.value, _nullishCoalesce(_optionalChain([operation, 'access', _119 => _119.makeId, 'optionalCall', _120 => _120(i)]), () => ( new (0, _mongodb.ObjectId)().toString())), now));
|
|
1826
|
+
break;
|
|
1827
|
+
case "delete":
|
|
1828
|
+
bulk.find(operation.filter).delete();
|
|
1829
|
+
break;
|
|
1830
|
+
case "update":
|
|
1831
|
+
bulk.find(operation.filter).update(prepUpdateValue(operation.value, now));
|
|
1832
|
+
break;
|
|
1833
|
+
case "upsert":
|
|
1834
|
+
bulk.find(operation.filter).upsert().update({
|
|
1835
|
+
...prepUpdateValue("update" in operation ? operation.update : {}, now),
|
|
1836
|
+
$setOnInsert: prepInsertValue(
|
|
1837
|
+
operation.insert,
|
|
1838
|
+
_nullishCoalesce(_optionalChain([operation, 'access', _121 => _121.makeId, 'optionalCall', _122 => _122(i)]), () => ( new (0, _mongodb.ObjectId)().toString())),
|
|
1839
|
+
now,
|
|
1840
|
+
true
|
|
1841
|
+
)
|
|
1842
|
+
});
|
|
1843
|
+
break;
|
|
1844
|
+
default:
|
|
1845
|
+
throw new EquippedError(`Unknown bulkWrite operation`, { operation });
|
|
1846
|
+
}
|
|
1847
|
+
});
|
|
1848
|
+
await bulk.execute({ session: options.session });
|
|
1849
|
+
}
|
|
1850
|
+
};
|
|
1851
|
+
return table;
|
|
1852
|
+
}
|
|
1853
|
+
|
|
1854
|
+
// src/dbs/mongo/index.ts
|
|
1855
|
+
var MongoDb = class extends Db {
|
|
1856
|
+
constructor(mongoConfig, dbConfig) {
|
|
1857
|
+
super(dbConfig);
|
|
1858
|
+
this.mongoConfig = mongoConfig;
|
|
1859
|
+
this.#client = new (0, _mongodb.MongoClient)(mongoConfig.uri);
|
|
1860
|
+
Instance.on(
|
|
1861
|
+
"start",
|
|
1862
|
+
async () => {
|
|
1863
|
+
await this.#client.connect();
|
|
1864
|
+
const grouped = this.#cols.reduce((acc, cur) => {
|
|
1865
|
+
if (!acc[cur.db]) acc[cur.db] = [];
|
|
1866
|
+
acc[cur.db].push(cur.col);
|
|
1867
|
+
return acc;
|
|
1868
|
+
}, {});
|
|
1869
|
+
const options = {
|
|
1870
|
+
changeStreamPreAndPostImages: { enabled: true }
|
|
1871
|
+
};
|
|
1872
|
+
await Promise.all(
|
|
1873
|
+
Object.entries(grouped).map(async ([dbName, colNames]) => {
|
|
1874
|
+
const db = this.#client.db(dbName);
|
|
1875
|
+
const collections = await db.listCollections().toArray();
|
|
1876
|
+
return colNames.map(async (colName) => {
|
|
1877
|
+
const existing = collections.find((collection) => collection.name === colName);
|
|
1878
|
+
if (existing) {
|
|
1879
|
+
if (_optionalChain([existing, 'access', _123 => _123.options, 'optionalAccess', _124 => _124.changeStreamPreAndPostImages, 'optionalAccess', _125 => _125.enabled]) !== options.changeStreamPreAndPostImages.enabled)
|
|
1880
|
+
await db.command({ collMod: colName, ...options });
|
|
1881
|
+
} else await db.createCollection(colName, options);
|
|
1882
|
+
});
|
|
1883
|
+
})
|
|
1884
|
+
);
|
|
1885
|
+
},
|
|
1886
|
+
3
|
|
1887
|
+
);
|
|
1888
|
+
Instance.on("close", async () => this.#client.close(), 1);
|
|
1889
|
+
}
|
|
1890
|
+
#client;
|
|
1891
|
+
#cols = [];
|
|
1892
|
+
async session(callback) {
|
|
1893
|
+
return this.#client.withSession(callback);
|
|
1894
|
+
}
|
|
1895
|
+
id() {
|
|
1896
|
+
return new (0, _mongodb.ObjectId)();
|
|
1897
|
+
}
|
|
1898
|
+
use(config) {
|
|
1899
|
+
if (config.change) {
|
|
1900
|
+
if (!this.config.changes) Instance.crash(new EquippedError("Db changes are not enabled in the configuration.", { config }));
|
|
1901
|
+
new MongoDbChange(
|
|
1902
|
+
this.mongoConfig,
|
|
1903
|
+
this.config.changes,
|
|
1904
|
+
this.#client,
|
|
1905
|
+
this.getScopedDb(config.db),
|
|
1906
|
+
config.col,
|
|
1907
|
+
config.change,
|
|
1908
|
+
config.mapper
|
|
1909
|
+
);
|
|
1910
|
+
}
|
|
1911
|
+
this.#cols.push({ db: this.getScopedDb(config.db), col: config.col });
|
|
1912
|
+
const collection = this.#client.db(this.getScopedDb(config.db)).collection(config.col);
|
|
1913
|
+
return getTable(config, collection);
|
|
1914
|
+
}
|
|
1915
|
+
};
|
|
1916
|
+
|
|
1917
|
+
// src/events/base.ts
|
|
1918
|
+
var EventBus = class {
|
|
1919
|
+
};
|
|
1920
|
+
var DefaultSubscribeOptions = {
|
|
1921
|
+
fanout: false
|
|
1922
|
+
};
|
|
1923
|
+
|
|
1924
|
+
// src/events/pipes.ts
|
|
1925
|
+
|
|
1926
|
+
var rabbitmqConfigPipe = _valleyed.v.meta(
|
|
1927
|
+
_valleyed.v.object({
|
|
1928
|
+
uri: _valleyed.v.string(),
|
|
1929
|
+
eventColumnName: _valleyed.v.string()
|
|
1930
|
+
}),
|
|
1931
|
+
{ title: "Rabbitmq Config", $refId: "RabbitmqConfig" }
|
|
1932
|
+
);
|
|
1933
|
+
var kafkaConfigPipe = _valleyed.v.meta(
|
|
1934
|
+
_valleyed.v.object({
|
|
1935
|
+
brokers: _valleyed.v.array(_valleyed.v.string()),
|
|
1936
|
+
ssl: _valleyed.v.optional(_valleyed.v.boolean()),
|
|
1937
|
+
sasl: _valleyed.v.optional(
|
|
1938
|
+
_valleyed.v.object({
|
|
1939
|
+
mechanism: _valleyed.v.is("plain"),
|
|
1940
|
+
username: _valleyed.v.string(),
|
|
1941
|
+
password: _valleyed.v.string()
|
|
1942
|
+
})
|
|
1943
|
+
),
|
|
1944
|
+
confluent: _valleyed.v.optional(_valleyed.v.boolean()),
|
|
1945
|
+
clientId: _valleyed.v.optional(_valleyed.v.string())
|
|
1946
|
+
}),
|
|
1947
|
+
{ title: "Kafka Config", $refId: "KafkaConfig" }
|
|
1948
|
+
);
|
|
1949
|
+
|
|
1950
|
+
// src/events/types/kafka.ts
|
|
1951
|
+
var _kafkajavascript = require('@confluentinc/kafka-javascript'); var _kafkajavascript2 = _interopRequireDefault(_kafkajavascript);
|
|
1952
|
+
var _kafkajs = require('kafkajs'); var _kafkajs2 = _interopRequireDefault(_kafkajs);
|
|
1953
|
+
var KafkaEventBus = class extends EventBus {
|
|
1954
|
+
#client;
|
|
1955
|
+
#confluent;
|
|
1956
|
+
#admin;
|
|
1957
|
+
constructor(config) {
|
|
1958
|
+
super();
|
|
1959
|
+
const { confluent = false, ...kafkaSettings } = config;
|
|
1960
|
+
this.#confluent = confluent;
|
|
1961
|
+
this.#client = confluent ? new _kafkajavascript2.default.KafkaJS.Kafka({
|
|
1962
|
+
kafkaJS: { ...kafkaSettings, logLevel: _kafkajavascript2.default.KafkaJS.logLevel.NOTHING }
|
|
1963
|
+
}) : new _kafkajs2.default.Kafka({ ...kafkaSettings, logLevel: _kafkajs2.default.logLevel.NOTHING });
|
|
1964
|
+
}
|
|
1965
|
+
createPublisher(topicName, options = {}) {
|
|
1966
|
+
const topic = options.skipScope ? topicName : Instance.get().getScopedName(topicName);
|
|
1967
|
+
return async (data) => {
|
|
1968
|
+
try {
|
|
1969
|
+
const producer = this.#client.producer();
|
|
1970
|
+
await producer.connect();
|
|
1971
|
+
await producer.send({
|
|
1972
|
+
topic,
|
|
1973
|
+
messages: [{ value: JSON.stringify(data) }]
|
|
1974
|
+
});
|
|
1975
|
+
return true;
|
|
1976
|
+
} catch (e5) {
|
|
1977
|
+
return false;
|
|
1978
|
+
}
|
|
1979
|
+
};
|
|
1980
|
+
}
|
|
1981
|
+
createSubscriber(topicName, onMessage, options = {}) {
|
|
1982
|
+
options = { ...DefaultSubscribeOptions, ...options };
|
|
1983
|
+
const topic = options.skipScope ? topicName : Instance.get().getScopedName(topicName);
|
|
1984
|
+
const subscribe = async () => {
|
|
1985
|
+
await this.#createTopic(topic);
|
|
1986
|
+
const groupId = options.fanout ? Instance.get().getScopedName(`${Instance.get().settings.app.id}-fanout-${random_exports.string(10)}`) : topic;
|
|
1987
|
+
const consumer = this.#client.consumer(this.#confluent ? { kafkaJS: { groupId } } : { groupId });
|
|
1988
|
+
await consumer.connect();
|
|
1989
|
+
await consumer.subscribe({ topic });
|
|
1990
|
+
await consumer.run({
|
|
1991
|
+
eachMessage: async ({ message }) => {
|
|
1992
|
+
Instance.resolveBeforeCrash(async () => {
|
|
1993
|
+
if (!message.value) return;
|
|
1994
|
+
await onMessage(parseJSONValue(message.value.toString()));
|
|
1995
|
+
});
|
|
1996
|
+
}
|
|
1997
|
+
});
|
|
1998
|
+
if (options.fanout)
|
|
1999
|
+
Instance.on(
|
|
2000
|
+
"close",
|
|
2001
|
+
async () => {
|
|
2002
|
+
await consumer.disconnect();
|
|
2003
|
+
await this.#deleteGroup(groupId);
|
|
2004
|
+
},
|
|
2005
|
+
10
|
|
2006
|
+
);
|
|
2007
|
+
};
|
|
2008
|
+
Instance.on("start", subscribe, 2);
|
|
2009
|
+
}
|
|
2010
|
+
async #getAdmin() {
|
|
2011
|
+
if (!this.#admin) {
|
|
2012
|
+
this.#admin = this.#client.admin();
|
|
2013
|
+
await this.#admin.connect();
|
|
2014
|
+
}
|
|
2015
|
+
return this.#admin;
|
|
2016
|
+
}
|
|
2017
|
+
async #createTopic(topic) {
|
|
2018
|
+
const admin = await this.#getAdmin();
|
|
2019
|
+
await admin.createTopics({ topics: [{ topic }], timeout: 5e3 });
|
|
2020
|
+
}
|
|
2021
|
+
async #deleteGroup(groupId) {
|
|
2022
|
+
const admin = await this.#getAdmin();
|
|
2023
|
+
await admin.deleteGroups([groupId]).catch(() => {
|
|
2024
|
+
});
|
|
2025
|
+
}
|
|
2026
|
+
};
|
|
2027
|
+
|
|
2028
|
+
// src/events/types/rabbitmq.ts
|
|
2029
|
+
var _amqpconnectionmanager = require('amqp-connection-manager');
|
|
2030
|
+
var RabbitMQEventBus = class extends EventBus {
|
|
2031
|
+
#client;
|
|
2032
|
+
#columnName;
|
|
2033
|
+
constructor(config) {
|
|
2034
|
+
super();
|
|
2035
|
+
this.#columnName = config.eventColumnName;
|
|
2036
|
+
this.#client = _amqpconnectionmanager.connect.call(void 0, [config.uri]).createChannel({
|
|
2037
|
+
json: false,
|
|
2038
|
+
setup: async (channel) => {
|
|
2039
|
+
await channel.assertExchange(this.#columnName, "direct", { durable: true });
|
|
2040
|
+
await channel.prefetch(1);
|
|
2041
|
+
}
|
|
2042
|
+
});
|
|
2043
|
+
}
|
|
2044
|
+
createPublisher(topicName, options = {}) {
|
|
2045
|
+
const topic = options.skipScope ? topicName : Instance.get().getScopedName(topicName);
|
|
2046
|
+
return async (data) => await this.#client.publish(this.#columnName, topic, JSON.stringify(data), { persistent: true });
|
|
2047
|
+
}
|
|
2048
|
+
createSubscriber(topicName, onMessage, options = {}) {
|
|
2049
|
+
options = { ...DefaultSubscribeOptions, ...options };
|
|
2050
|
+
const topic = options.skipScope ? topicName : Instance.get().getScopedName(topicName);
|
|
2051
|
+
const subscribe = async () => {
|
|
2052
|
+
await this.#client.addSetup(async (channel) => {
|
|
2053
|
+
const queueName = options.fanout ? Instance.get().getScopedName(`${Instance.get().settings.app.id}-fanout-${random_exports.string(10)}`) : topic;
|
|
2054
|
+
const { queue } = await channel.assertQueue(queueName, { durable: !options.fanout, exclusive: options.fanout });
|
|
2055
|
+
await channel.bindQueue(queue, this.#columnName, topic);
|
|
2056
|
+
channel.consume(
|
|
2057
|
+
queue,
|
|
2058
|
+
async (msg) => {
|
|
2059
|
+
Instance.resolveBeforeCrash(async () => {
|
|
2060
|
+
if (!msg) return;
|
|
2061
|
+
try {
|
|
2062
|
+
await onMessage(parseJSONValue(msg.content.toString()));
|
|
2063
|
+
channel.ack(msg);
|
|
2064
|
+
} catch (e6) {
|
|
2065
|
+
channel.nack(msg);
|
|
2066
|
+
}
|
|
2067
|
+
});
|
|
2068
|
+
},
|
|
2069
|
+
{ noAck: false }
|
|
2070
|
+
);
|
|
2071
|
+
});
|
|
2072
|
+
};
|
|
2073
|
+
Instance.on("start", subscribe, 2);
|
|
2074
|
+
}
|
|
2075
|
+
};
|
|
2076
|
+
|
|
2077
|
+
// src/jobs/pipes.ts
|
|
2078
|
+
|
|
2079
|
+
var redisJobsConfigPipe = _valleyed.v.meta(
|
|
2080
|
+
_valleyed.v.object({
|
|
2081
|
+
redisConfig: redisConfigPipe,
|
|
2082
|
+
queueName: _valleyed.v.string()
|
|
2083
|
+
}),
|
|
2084
|
+
{ title: "Redis Jobs Config", $refId: "RedisJobsConfig" }
|
|
2085
|
+
);
|
|
2086
|
+
|
|
2087
|
+
// src/jobs/types/redis.ts
|
|
2088
|
+
var _bull = require('bull'); var _bull2 = _interopRequireDefault(_bull);
|
|
2089
|
+
var RedisJob = class _RedisJob {
|
|
2090
|
+
#queue;
|
|
2091
|
+
#callbacks = {};
|
|
2092
|
+
#crons = [];
|
|
2093
|
+
constructor(config) {
|
|
2094
|
+
const redisCache = new RedisCache(config.redisConfig, {
|
|
2095
|
+
maxRetriesPerRequest: null,
|
|
2096
|
+
enableReadyCheck: false
|
|
2097
|
+
});
|
|
2098
|
+
this.#queue = new (0, _bull2.default)(config.queueName, { createClient: () => redisCache.client });
|
|
2099
|
+
Instance.on(
|
|
2100
|
+
"start",
|
|
2101
|
+
async () => {
|
|
2102
|
+
await this.#cleanup();
|
|
2103
|
+
await Promise.all(this.#crons.map(({ cron, name }) => this.#addCron(name, cron)));
|
|
2104
|
+
Promise.all([
|
|
2105
|
+
this.#queue.process("DelayedJob" /* DelayedJob */, async (job) => await _optionalChain([this, 'access', _126 => _126.#callbacks, 'access', _127 => _127.onDelayed, 'optionalCall', _128 => _128(job.data)])),
|
|
2106
|
+
this.#queue.process("CronJob" /* CronJob */, async (job) => await _optionalChain([this, 'access', _129 => _129.#callbacks, 'access', _130 => _130.onCron, 'optionalCall', _131 => _131(job.data.type)])),
|
|
2107
|
+
this.#queue.process("RepeatableJob" /* RepeatableJob */, async (job) => await _optionalChain([this, 'access', _132 => _132.#callbacks, 'access', _133 => _133.onRepeatable, 'optionalCall', _134 => _134(job.data)]))
|
|
2108
|
+
]);
|
|
2109
|
+
},
|
|
2110
|
+
10
|
|
2111
|
+
);
|
|
2112
|
+
}
|
|
2113
|
+
set callbacks(callbacks) {
|
|
2114
|
+
this.#callbacks = callbacks;
|
|
2115
|
+
}
|
|
2116
|
+
set crons(crons) {
|
|
2117
|
+
this.#crons = crons;
|
|
2118
|
+
}
|
|
2119
|
+
static #getNewId() {
|
|
2120
|
+
return [Date.now(), random_exports.string()].join(":");
|
|
2121
|
+
}
|
|
2122
|
+
async addDelayed(data, delayInMs) {
|
|
2123
|
+
const job = await this.#queue.add("DelayedJob" /* DelayedJob */, data, {
|
|
2124
|
+
jobId: _RedisJob.#getNewId(),
|
|
2125
|
+
delay: delayInMs,
|
|
2126
|
+
removeOnComplete: true,
|
|
2127
|
+
backoff: 1e3,
|
|
2128
|
+
attempts: 3
|
|
2129
|
+
});
|
|
2130
|
+
return job.id.toString();
|
|
2131
|
+
}
|
|
2132
|
+
async addRepeatable(data, cron, tz) {
|
|
2133
|
+
const job = await this.#queue.add("RepeatableJob" /* RepeatableJob */, data, {
|
|
2134
|
+
jobId: _RedisJob.#getNewId(),
|
|
2135
|
+
repeat: { cron, ...tz ? { tz } : {} },
|
|
2136
|
+
removeOnComplete: true,
|
|
2137
|
+
backoff: 1e3,
|
|
2138
|
+
attempts: 3
|
|
2139
|
+
});
|
|
2140
|
+
return _nullishCoalesce(_optionalChain([job, 'access', _135 => _135.opts, 'optionalAccess', _136 => _136.repeat, 'optionalAccess', _137 => _137.key]), () => ( ""));
|
|
2141
|
+
}
|
|
2142
|
+
async removeDelayed(jobId) {
|
|
2143
|
+
const job = await this.#queue.getJob(jobId);
|
|
2144
|
+
if (job) await job.discard();
|
|
2145
|
+
}
|
|
2146
|
+
async removeRepeatable(jobKey) {
|
|
2147
|
+
await this.#queue.removeRepeatableByKey(jobKey);
|
|
2148
|
+
}
|
|
2149
|
+
async retryAllFailedJobs() {
|
|
2150
|
+
const failedJobs = await this.#queue.getFailed();
|
|
2151
|
+
await Promise.all(failedJobs.map((job) => job.retry()));
|
|
2152
|
+
}
|
|
2153
|
+
async #addCron(type, cron) {
|
|
2154
|
+
const job = await this.#queue.add(
|
|
2155
|
+
"CronJob" /* CronJob */,
|
|
2156
|
+
{ type },
|
|
2157
|
+
{
|
|
2158
|
+
jobId: _RedisJob.#getNewId(),
|
|
2159
|
+
repeat: { cron },
|
|
2160
|
+
removeOnComplete: true,
|
|
2161
|
+
backoff: 1e3,
|
|
2162
|
+
attempts: 3
|
|
2163
|
+
}
|
|
2164
|
+
);
|
|
2165
|
+
return job.id.toString();
|
|
2166
|
+
}
|
|
2167
|
+
async #cleanup() {
|
|
2168
|
+
await this.retryAllFailedJobs();
|
|
2169
|
+
const repeatableJobs = await this.#queue.getRepeatableJobs();
|
|
2170
|
+
await Promise.all(
|
|
2171
|
+
repeatableJobs.filter((job) => job.name === "CronJob" /* CronJob */).map((job) => this.#queue.removeRepeatableByKey(job.key))
|
|
2172
|
+
);
|
|
2173
|
+
}
|
|
2174
|
+
};
|
|
2175
|
+
|
|
2176
|
+
// src/instance/settings.ts
|
|
2177
|
+
var instanceSettingsPipe = () => _valleyed.v.object({
|
|
2178
|
+
app: _valleyed.v.object({
|
|
2179
|
+
id: _valleyed.v.string(),
|
|
2180
|
+
name: _valleyed.v.string()
|
|
2181
|
+
}),
|
|
2182
|
+
log: _valleyed.v.defaults(
|
|
2183
|
+
_valleyed.v.object({
|
|
2184
|
+
level: _valleyed.v.defaults(_valleyed.v.in(["fatal", "error", "warn", "info", "debug", "trace", "silent"]), "info")
|
|
2185
|
+
}),
|
|
2186
|
+
{}
|
|
2187
|
+
),
|
|
2188
|
+
dbs: _valleyed.v.optional(
|
|
2189
|
+
_valleyed.v.object({
|
|
2190
|
+
types: _valleyed.v.record(
|
|
2191
|
+
_valleyed.v.string(),
|
|
2192
|
+
_valleyed.v.discriminate((e) => _optionalChain([e, 'optionalAccess', _138 => _138.type]), {
|
|
2193
|
+
mongo: _valleyed.v.merge(mongoDbConfigPipe, _valleyed.v.object({ type: _valleyed.v.is("mongo") }))
|
|
2194
|
+
})
|
|
2195
|
+
),
|
|
2196
|
+
changes: _valleyed.v.optional(
|
|
2197
|
+
_valleyed.v.object({
|
|
2198
|
+
debeziumUrl: _valleyed.v.string(),
|
|
2199
|
+
kafkaConfig: kafkaConfigPipe
|
|
2200
|
+
})
|
|
2201
|
+
)
|
|
2202
|
+
})
|
|
2203
|
+
),
|
|
2204
|
+
eventBus: _valleyed.v.optional(
|
|
2205
|
+
_valleyed.v.discriminate((e) => _optionalChain([e, 'optionalAccess', _139 => _139.type]), {
|
|
2206
|
+
kafka: _valleyed.v.merge(kafkaConfigPipe, _valleyed.v.object({ type: _valleyed.v.is("kafka") })),
|
|
2207
|
+
rabbitmq: _valleyed.v.merge(rabbitmqConfigPipe, _valleyed.v.object({ type: _valleyed.v.is("rabbitmq") }))
|
|
2208
|
+
})
|
|
2209
|
+
),
|
|
2210
|
+
cache: _valleyed.v.discriminate((e) => _optionalChain([e, 'optionalAccess', _140 => _140.type]), {
|
|
2211
|
+
redis: _valleyed.v.merge(redisConfigPipe, _valleyed.v.object({ type: _valleyed.v.is("redis") }))
|
|
2212
|
+
}),
|
|
2213
|
+
jobs: _valleyed.v.optional(_valleyed.v.merge(redisJobsConfigPipe, _valleyed.v.object({ type: _valleyed.v.is("redis") }))),
|
|
2214
|
+
server: _valleyed.v.optional(
|
|
2215
|
+
_valleyed.v.object({
|
|
2216
|
+
type: _valleyed.v.in(["fastify", "express"]),
|
|
2217
|
+
port: _valleyed.v.number(),
|
|
2218
|
+
publicPath: _valleyed.v.optional(_valleyed.v.string()),
|
|
2219
|
+
healthPath: _valleyed.v.optional(_valleyed.v.string()),
|
|
2220
|
+
openapi: _valleyed.v.defaults(
|
|
2221
|
+
_valleyed.v.object({
|
|
2222
|
+
docsVersion: _valleyed.v.defaults(_valleyed.v.string(), "1.0.0"),
|
|
2223
|
+
docsBaseUrl: _valleyed.v.defaults(_valleyed.v.array(_valleyed.v.string()), ["/"]),
|
|
2224
|
+
docsPath: _valleyed.v.defaults(_valleyed.v.string(), "/__docs")
|
|
2225
|
+
}),
|
|
2226
|
+
{}
|
|
2227
|
+
),
|
|
2228
|
+
requests: _valleyed.v.defaults(
|
|
2229
|
+
_valleyed.v.object({
|
|
2230
|
+
log: _valleyed.v.defaults(_valleyed.v.boolean(), true),
|
|
2231
|
+
paginationDefaultLimit: _valleyed.v.defaults(_valleyed.v.number(), 100),
|
|
2232
|
+
maxFileUploadSizeInMb: _valleyed.v.defaults(_valleyed.v.number(), 500),
|
|
2233
|
+
rateLimit: _valleyed.v.defaults(
|
|
2234
|
+
_valleyed.v.object({
|
|
2235
|
+
enabled: _valleyed.v.defaults(_valleyed.v.boolean(), false),
|
|
2236
|
+
periodInMs: _valleyed.v.defaults(_valleyed.v.number(), 60 * 60 * 1e3),
|
|
2237
|
+
limit: _valleyed.v.defaults(_valleyed.v.number(), 5e3)
|
|
2238
|
+
}),
|
|
2239
|
+
{}
|
|
2240
|
+
),
|
|
2241
|
+
slowdown: _valleyed.v.defaults(
|
|
2242
|
+
_valleyed.v.object({
|
|
2243
|
+
enabled: _valleyed.v.defaults(_valleyed.v.boolean(), false),
|
|
2244
|
+
periodInMs: _valleyed.v.defaults(_valleyed.v.number(), 10 * 60 * 1e3),
|
|
2245
|
+
delayAfter: _valleyed.v.defaults(_valleyed.v.number(), 2e3),
|
|
2246
|
+
delayInMs: _valleyed.v.defaults(_valleyed.v.number(), 500)
|
|
2247
|
+
}),
|
|
2248
|
+
{}
|
|
2249
|
+
)
|
|
2250
|
+
}),
|
|
2251
|
+
{}
|
|
2252
|
+
),
|
|
2253
|
+
requestsAuth: _valleyed.v.defaults(
|
|
2254
|
+
_valleyed.v.object({
|
|
2255
|
+
tokens: _valleyed.v.optional(_valleyed.v.instanceOf(BaseTokensUtility)),
|
|
2256
|
+
apiKey: _valleyed.v.optional(_valleyed.v.instanceOf(BaseApiKeysUtility))
|
|
2257
|
+
}),
|
|
2258
|
+
{}
|
|
2259
|
+
)
|
|
2260
|
+
})
|
|
2261
|
+
),
|
|
2262
|
+
utils: _valleyed.v.defaults(
|
|
2263
|
+
_valleyed.v.object({
|
|
2264
|
+
hashSaltRounds: _valleyed.v.defaults(_valleyed.v.number(), 10)
|
|
2265
|
+
}),
|
|
2266
|
+
{}
|
|
2267
|
+
)
|
|
2268
|
+
});
|
|
2269
|
+
function mapSettingsToInstance(settings) {
|
|
2270
|
+
const log = _pino2.default.call(void 0, {
|
|
2271
|
+
level: settings.log.level,
|
|
2272
|
+
serializers: {
|
|
2273
|
+
err: _pino2.default.stdSerializers.err,
|
|
2274
|
+
error: _pino2.default.stdSerializers.err,
|
|
2275
|
+
req: _pino2.default.stdSerializers.req,
|
|
2276
|
+
res: _pino2.default.stdSerializers.res
|
|
2277
|
+
}
|
|
2278
|
+
});
|
|
2279
|
+
const cache = new RedisCache(settings.cache);
|
|
2280
|
+
const jobs = settings.jobs ? new RedisJob(settings.jobs) : void 0;
|
|
2281
|
+
const eventBus = _optionalChain([settings, 'access', _141 => _141.eventBus, 'optionalAccess', _142 => _142.type]) === "kafka" ? new KafkaEventBus(deleteKeys(settings.eventBus, ["type"])) : _optionalChain([settings, 'access', _143 => _143.eventBus, 'optionalAccess', _144 => _144.type]) === "rabbitmq" ? new RabbitMQEventBus(deleteKeys(settings.eventBus, ["type"])) : void 0;
|
|
2282
|
+
const serverConfig = {
|
|
2283
|
+
app: settings.app,
|
|
2284
|
+
log,
|
|
2285
|
+
eventBus
|
|
2286
|
+
};
|
|
2287
|
+
const server = _optionalChain([settings, 'access', _145 => _145.server, 'optionalAccess', _146 => _146.type]) === "express" ? new ExpressServer({ ...serverConfig, config: settings.server }) : _optionalChain([settings, 'access', _147 => _147.server, 'optionalAccess', _148 => _148.type]) === "fastify" ? new FastifyServer({ ...serverConfig, config: settings.server }) : void 0;
|
|
2288
|
+
const changesConfig = _optionalChain([settings, 'access', _149 => _149.dbs, 'optionalAccess', _150 => _150.changes]) ? {
|
|
2289
|
+
debeziumUrl: settings.dbs.changes.debeziumUrl,
|
|
2290
|
+
eventBus: new KafkaEventBus(settings.dbs.changes.kafkaConfig)
|
|
2291
|
+
} : void 0;
|
|
2292
|
+
const dbs = Object.fromEntries(
|
|
2293
|
+
Object.entries(_nullishCoalesce(_optionalChain([settings, 'access', _151 => _151.dbs, 'optionalAccess', _152 => _152.types]), () => ( {}))).map(([key, config]) => [
|
|
2294
|
+
key,
|
|
2295
|
+
config.type === "mongo" ? new MongoDb(config, { changes: changesConfig }) : void 0
|
|
2296
|
+
])
|
|
2297
|
+
);
|
|
2298
|
+
return {
|
|
2299
|
+
app: settings.app,
|
|
2300
|
+
utils: settings.utils,
|
|
2301
|
+
log,
|
|
2302
|
+
eventBus,
|
|
2303
|
+
cache,
|
|
2304
|
+
jobs,
|
|
2305
|
+
server,
|
|
2306
|
+
dbs
|
|
2307
|
+
};
|
|
2308
|
+
}
|
|
2309
|
+
function deleteKeys(data, keys) {
|
|
2310
|
+
for (const key of keys) delete data[key];
|
|
2311
|
+
return data;
|
|
2312
|
+
}
|
|
2313
|
+
|
|
2314
|
+
// src/instance/index.ts
|
|
2315
|
+
var Instance = class _Instance extends _valleyed.DataClass {
|
|
2316
|
+
static #instance;
|
|
2317
|
+
static #hooks = {};
|
|
2318
|
+
|
|
2319
|
+
|
|
2320
|
+
constructor(envs, settings) {
|
|
2321
|
+
super(mapSettingsToInstance(settings));
|
|
2322
|
+
_Instance.#instance = this;
|
|
2323
|
+
this.envs = Object.freeze(envs);
|
|
2324
|
+
this.settings = Object.freeze(settings);
|
|
2325
|
+
_Instance.#registerOnExitHandler();
|
|
2326
|
+
}
|
|
2327
|
+
getScopedName(name, key = ".") {
|
|
2328
|
+
return [this.settings.app.name, name].join(key);
|
|
2329
|
+
}
|
|
2330
|
+
async start() {
|
|
2331
|
+
try {
|
|
2332
|
+
await runHooks(_nullishCoalesce(_Instance.#hooks["setup"], () => ( [])));
|
|
2333
|
+
await runHooks(_nullishCoalesce(_Instance.#hooks["start"], () => ( [])));
|
|
2334
|
+
} catch (error) {
|
|
2335
|
+
_Instance.crash(new EquippedError(`Error starting instance`, {}, error));
|
|
2336
|
+
}
|
|
2337
|
+
}
|
|
2338
|
+
static create(envsPipe, settings) {
|
|
2339
|
+
if (_Instance.#instance) throw _Instance.crash(new EquippedError("An instance has already been created. Use that instead", {}));
|
|
2340
|
+
const envValidity = _valleyed.v.validate(envsPipe, process.env);
|
|
2341
|
+
if (!envValidity.valid) {
|
|
2342
|
+
_Instance.crash(
|
|
2343
|
+
new EquippedError(`Environment variables are not valid
|
|
2344
|
+
${envValidity.error.toString()}`, {
|
|
2345
|
+
messages: envValidity.error.messages
|
|
2346
|
+
})
|
|
2347
|
+
);
|
|
2348
|
+
}
|
|
2349
|
+
const settingsValidity = _valleyed.v.validate(instanceSettingsPipe(), settings(envValidity.value));
|
|
2350
|
+
if (!settingsValidity.valid) {
|
|
2351
|
+
_Instance.crash(
|
|
2352
|
+
new EquippedError(`Settings are not valid
|
|
2353
|
+
${settingsValidity.error.toString()}`, {
|
|
2354
|
+
messages: settingsValidity.error.messages
|
|
2355
|
+
})
|
|
2356
|
+
);
|
|
2357
|
+
}
|
|
2358
|
+
return new _Instance(envValidity.value, settingsValidity.value);
|
|
2359
|
+
}
|
|
2360
|
+
static get() {
|
|
2361
|
+
if (!_Instance.#instance)
|
|
2362
|
+
return _Instance.crash(
|
|
2363
|
+
new EquippedError("Has not been initialized. Make sure an instance has been created before you get an instance", {})
|
|
2364
|
+
);
|
|
2365
|
+
return _Instance.#instance;
|
|
2366
|
+
}
|
|
2367
|
+
static on(event, cb, order) {
|
|
2368
|
+
_Instance.#hooks[event] ??= [];
|
|
2369
|
+
_Instance.#hooks[event].push({ cb, order });
|
|
2370
|
+
}
|
|
2371
|
+
static #registerOnExitHandler() {
|
|
2372
|
+
const signals = {
|
|
2373
|
+
SIGHUP: 1,
|
|
2374
|
+
SIGINT: 2,
|
|
2375
|
+
SIGTERM: 15
|
|
2376
|
+
};
|
|
2377
|
+
Object.entries(signals).forEach(([signal, code]) => {
|
|
2378
|
+
process.on(signal, async () => {
|
|
2379
|
+
await runHooks(_nullishCoalesce(_Instance.#hooks["close"], () => ( [])), () => {
|
|
2380
|
+
});
|
|
2381
|
+
process.exit(128 + code);
|
|
2382
|
+
});
|
|
2383
|
+
});
|
|
2384
|
+
}
|
|
2385
|
+
static resolveBeforeCrash(cb) {
|
|
2386
|
+
const value = cb();
|
|
2387
|
+
_Instance.on("close", async () => await value, 10);
|
|
2388
|
+
return value;
|
|
2389
|
+
}
|
|
2390
|
+
static crash(error) {
|
|
2391
|
+
console.error(error);
|
|
2392
|
+
process.exit(1);
|
|
2393
|
+
}
|
|
2394
|
+
};
|
|
2395
|
+
|
|
2396
|
+
// src/validations/valleyed.ts
|
|
2397
|
+
var filePipe = (err) => _valleyed.v.array(
|
|
2398
|
+
_valleyed.v.file(err).pipe((input) => {
|
|
2399
|
+
const err2 = `is larger than allowed limit of ${_optionalChain([Instance, 'access', _153 => _153.get, 'call', _154 => _154(), 'access', _155 => _155.settings, 'access', _156 => _156.server, 'optionalAccess', _157 => _157.requests, 'access', _158 => _158.maxFileUploadSizeInMb])}mb`;
|
|
2400
|
+
const valid = input ? !input.isTruncated : true;
|
|
2401
|
+
if (valid) return input;
|
|
2402
|
+
throw _valleyed.PipeError.root(err2, input);
|
|
2403
|
+
})
|
|
2404
|
+
);
|
|
2405
|
+
var incomingFile = (err) => _valleyed.v.pipe(
|
|
2406
|
+
(input) => _valleyed.v.assert(
|
|
2407
|
+
filePipe(err).pipe(_valleyed.v.min(1, "no file provided")).pipe((files) => files[0]),
|
|
2408
|
+
input
|
|
2409
|
+
),
|
|
2410
|
+
{ schema: () => ({ type: "string", format: "binary" }) }
|
|
2411
|
+
);
|
|
2412
|
+
var incomingFiles = (err) => _valleyed.v.pipe((input) => _valleyed.v.assert(filePipe(err), input), {
|
|
2413
|
+
schema: () => ({ type: "string", format: "binary" })
|
|
2414
|
+
});
|
|
2415
|
+
var requestLocalStorage = new (0, _async_hooks.AsyncLocalStorage)();
|
|
2416
|
+
var responseLocalStorage = new (0, _async_hooks.AsyncLocalStorage)();
|
|
2417
|
+
var withRequest = (fn) => _valleyed.v.pipe((input) => {
|
|
2418
|
+
const req = requestLocalStorage.getStore();
|
|
2419
|
+
if (!req) throw _valleyed.PipeError.root("Request not found in context", input);
|
|
2420
|
+
return _valleyed.v.assert(fn(req), input);
|
|
2421
|
+
});
|
|
2422
|
+
var withResponse = (fn) => _valleyed.v.pipe((input) => {
|
|
2423
|
+
const res = responseLocalStorage.getStore();
|
|
2424
|
+
if (!res) throw _valleyed.PipeError.root("Response not found in context", input);
|
|
2425
|
+
return _valleyed.v.assert(fn(res), input);
|
|
2426
|
+
});
|
|
2427
|
+
|
|
2428
|
+
// src/validations/index.ts
|
|
2429
|
+
|
|
2430
|
+
function pipeErrorToValidationError(error) {
|
|
2431
|
+
const errorsObject = error.messages.reduce((acc, { path = "", message }) => {
|
|
2432
|
+
if (acc[path]) acc[path].messages.push(message);
|
|
2433
|
+
else acc[path] = { field: path, messages: [message] };
|
|
2434
|
+
return acc;
|
|
2435
|
+
}, {});
|
|
2436
|
+
return new ValidationError(Object.values(errorsObject));
|
|
2437
|
+
}
|
|
2438
|
+
function validate(pipe, value) {
|
|
2439
|
+
const validity = _valleyed.v.validate(pipe, value);
|
|
2440
|
+
if (validity.valid) return validity.value;
|
|
2441
|
+
throw pipeErrorToValidationError(validity.error);
|
|
2442
|
+
}
|
|
2443
|
+
|
|
2444
|
+
|
|
2445
|
+
|
|
2446
|
+
|
|
2447
|
+
|
|
2448
|
+
|
|
2449
|
+
|
|
2450
|
+
|
|
2451
|
+
|
|
2452
|
+
|
|
2453
|
+
|
|
2454
|
+
|
|
2455
|
+
|
|
2456
|
+
|
|
2457
|
+
|
|
2458
|
+
|
|
2459
|
+
|
|
2460
|
+
|
|
2461
|
+
|
|
2462
|
+
|
|
2463
|
+
|
|
2464
|
+
|
|
2465
|
+
|
|
2466
|
+
|
|
2467
|
+
|
|
2468
|
+
|
|
2469
|
+
|
|
2470
|
+
|
|
2471
|
+
|
|
2472
|
+
|
|
2473
|
+
|
|
2474
|
+
|
|
2475
|
+
|
|
2476
|
+
|
|
2477
|
+
|
|
2478
|
+
|
|
2479
|
+
|
|
2480
|
+
|
|
2481
|
+
|
|
2482
|
+
|
|
2483
|
+
|
|
2484
|
+
|
|
2485
|
+
|
|
2486
|
+
|
|
2487
|
+
|
|
2488
|
+
|
|
2489
|
+
|
|
2490
|
+
|
|
2491
|
+
|
|
2492
|
+
|
|
2493
|
+
|
|
2494
|
+
|
|
2495
|
+
|
|
2496
|
+
|
|
2497
|
+
|
|
2498
|
+
|
|
2499
|
+
|
|
2500
|
+
|
|
2501
|
+
|
|
2502
|
+
|
|
2503
|
+
|
|
2504
|
+
|
|
2505
|
+
|
|
2506
|
+
exports.Cache = Cache; exports.redisConfigPipe = redisConfigPipe; exports.EquippedError = EquippedError; exports.RequestError = RequestError; exports.valleyed_exports = valleyed_exports; exports.pipeErrorToValidationError = pipeErrorToValidationError; exports.validate = validate; exports.Methods = Methods; exports.StatusCodes = StatusCodes; exports.makeMiddleware = makeMiddleware; exports.makeErrorMiddleware = makeErrorMiddleware; exports.Router = Router; exports.authProviders_exports = authProviders_exports; exports.hash_exports = hash_exports; exports.parseJSONValue = parseJSONValue; exports.parseJSONObject = parseJSONObject; exports.getMediaDuration = getMediaDuration; exports.sleep = sleep; exports.retry = retry; exports.random_exports = random_exports; exports.Request = Request; exports.Response = Response; exports.Server = Server; exports.ExpressServer = ExpressServer; exports.FastifyServer = FastifyServer; exports.requireAuthUser = requireAuthUser; exports.requireAuthorizationUser = requireAuthorizationUser; exports.requireApiKeyUser = requireApiKeyUser; exports.requireRefreshTokenUser = requireRefreshTokenUser; exports.BaseApiKeysUtility = BaseApiKeysUtility; exports.BaseTokensUtility = BaseTokensUtility; exports.CacheTokensUtility = CacheTokensUtility; exports.serverPipe = serverPipe; exports.AuthorizationExpired = AuthorizationExpired; exports.BadRequestError = BadRequestError; exports.NotAuthenticatedError = NotAuthenticatedError; exports.NotAuthorizedError = NotAuthorizedError; exports.NotFoundError = NotFoundError; exports.RefreshTokenMisusedError = RefreshTokenMisusedError; exports.ValidationError = ValidationError; exports.RedisCache = RedisCache; exports.TopicPrefix = TopicPrefix; exports.Db = Db; exports.DbChange = DbChange; exports.MongoDbChange = MongoDbChange; exports.QueryKeys = QueryKeys; exports.Conditions = Conditions; exports.queryParamsPipe = queryParamsPipe; exports.queryResultsPipe = queryResultsPipe; exports.wrapQueryParams = wrapQueryParams; exports.mongoDbConfigPipe = mongoDbConfigPipe; exports.MongoDb = MongoDb; exports.EventBus = EventBus; exports.DefaultSubscribeOptions = DefaultSubscribeOptions; exports.rabbitmqConfigPipe = rabbitmqConfigPipe; exports.kafkaConfigPipe = kafkaConfigPipe; exports.KafkaEventBus = KafkaEventBus; exports.RabbitMQEventBus = RabbitMQEventBus; exports.redisJobsConfigPipe = redisJobsConfigPipe; exports.RedisJob = RedisJob; exports.Instance = Instance;
|
|
2507
|
+
//# sourceMappingURL=chunk-UBQPLHM4.cjs.map
|