equipped 5.0.7 → 5.0.9

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.
Files changed (96) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/dist/cjs/cache.cjs +11 -0
  3. package/dist/cjs/cache.cjs.map +1 -0
  4. package/dist/cjs/chunk-46TJFKXX.cjs +2507 -0
  5. package/dist/cjs/chunk-46TJFKXX.cjs.map +1 -0
  6. package/dist/cjs/dbs.cjs +27 -0
  7. package/dist/cjs/dbs.cjs.map +1 -0
  8. package/dist/cjs/errors.cjs +23 -0
  9. package/dist/cjs/errors.cjs.map +1 -0
  10. package/dist/cjs/events.cjs +17 -0
  11. package/dist/cjs/events.cjs.map +1 -0
  12. package/dist/cjs/index.cjs +2 -2
  13. package/dist/cjs/index.cjs.map +1 -0
  14. package/dist/cjs/jobs.cjs +9 -0
  15. package/dist/cjs/jobs.cjs.map +1 -0
  16. package/dist/cjs/server.cjs +41 -0
  17. package/dist/cjs/server.cjs.map +1 -0
  18. package/dist/cjs/types.cjs +1 -0
  19. package/dist/cjs/types.cjs.map +1 -0
  20. package/dist/cjs/utilities.cjs +21 -0
  21. package/dist/cjs/utilities.cjs.map +1 -0
  22. package/dist/cjs/validations.cjs +11 -0
  23. package/dist/cjs/validations.cjs.map +1 -0
  24. package/dist/esm/cache.min.mjs +2 -0
  25. package/dist/esm/cache.min.mjs.map +1 -0
  26. package/dist/esm/cache.mjs +11 -0
  27. package/dist/esm/cache.mjs.map +1 -0
  28. package/dist/esm/chunk-7LLFKFXJ.min.mjs +32 -0
  29. package/dist/esm/chunk-7LLFKFXJ.min.mjs.map +1 -0
  30. package/dist/esm/chunk-DXTIVHEC.mjs +2507 -0
  31. package/dist/esm/chunk-DXTIVHEC.mjs.map +1 -0
  32. package/dist/esm/dbs.min.mjs +2 -0
  33. package/dist/esm/dbs.min.mjs.map +1 -0
  34. package/dist/esm/dbs.mjs +27 -0
  35. package/dist/esm/dbs.mjs.map +1 -0
  36. package/dist/esm/errors.min.mjs +2 -0
  37. package/dist/esm/errors.min.mjs.map +1 -0
  38. package/dist/esm/errors.mjs +23 -0
  39. package/dist/esm/errors.mjs.map +1 -0
  40. package/dist/esm/events.min.mjs +2 -0
  41. package/dist/esm/events.min.mjs.map +1 -0
  42. package/dist/esm/events.mjs +17 -0
  43. package/dist/esm/events.mjs.map +1 -0
  44. package/dist/esm/index.min.mjs +2 -0
  45. package/dist/esm/index.min.mjs.map +1 -0
  46. package/dist/esm/index.mjs +7 -0
  47. package/dist/esm/index.mjs.map +1 -0
  48. package/dist/esm/jobs.min.mjs +2 -0
  49. package/dist/esm/jobs.min.mjs.map +1 -0
  50. package/dist/esm/jobs.mjs +9 -0
  51. package/dist/esm/jobs.mjs.map +1 -0
  52. package/dist/esm/server.min.mjs +2 -0
  53. package/dist/esm/server.min.mjs.map +1 -0
  54. package/dist/esm/server.mjs +41 -0
  55. package/dist/esm/server.mjs.map +1 -0
  56. package/dist/esm/types.min.mjs +1 -0
  57. package/dist/esm/types.min.mjs.map +1 -0
  58. package/dist/esm/types.mjs +1 -0
  59. package/dist/esm/types.mjs.map +1 -0
  60. package/dist/esm/utilities.min.mjs +2 -0
  61. package/dist/esm/utilities.min.mjs.map +1 -0
  62. package/dist/esm/utilities.mjs +21 -0
  63. package/dist/esm/utilities.mjs.map +1 -0
  64. package/dist/esm/validations.min.mjs +2 -0
  65. package/dist/esm/validations.min.mjs.map +1 -0
  66. package/dist/esm/validations.mjs +11 -0
  67. package/dist/esm/validations.mjs.map +1 -0
  68. package/dist/types/base-k4t2NepI.d.ts +8 -0
  69. package/dist/types/cache.d.ts +32 -0
  70. package/dist/types/cache.js +10 -0
  71. package/dist/types/chunk-SK64OMPG.js +2506 -0
  72. package/dist/types/dbs.d.ts +16 -0
  73. package/dist/types/dbs.js +26 -0
  74. package/dist/types/errors.d.ts +39 -0
  75. package/dist/types/errors.js +22 -0
  76. package/dist/types/events.d.ts +13 -0
  77. package/dist/types/events.js +16 -0
  78. package/dist/types/index-DNvs4IZN.d.ts +562 -0
  79. package/dist/types/index-U2VdDArL.d.ts +287 -0
  80. package/dist/types/index.d.ts +16 -0
  81. package/dist/types/index.js +6 -0
  82. package/dist/types/jobs.d.ts +54 -0
  83. package/dist/types/jobs.js +8 -0
  84. package/dist/types/kafka-CW925i4B.d.ts +57 -0
  85. package/dist/types/overrides-6Hxg764S.d.ts +25 -0
  86. package/dist/types/requests-BlJQeR64.d.ts +207 -0
  87. package/dist/types/server.d.ts +48 -0
  88. package/dist/types/server.js +40 -0
  89. package/dist/types/types.d.ts +5 -0
  90. package/dist/types/types.js +0 -0
  91. package/dist/types/utilities.d.ts +68 -0
  92. package/dist/types/utilities.js +20 -0
  93. package/dist/types/validationError-XbHmdYeK.d.ts +12 -0
  94. package/dist/types/validations.d.ts +74 -0
  95. package/dist/types/validations.js +10 -0
  96. package/package.json +2 -2
@@ -0,0 +1,2507 @@
1
+ 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
+ export * from "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
+ import { AsyncLocalStorage } from "async_hooks";
21
+ import { PipeError as PipeError2, v as v10 } from "valleyed";
22
+
23
+ // src/instance/index.ts
24
+ import { DataClass, v as v9 } from "valleyed";
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
+ import pino from "pino";
52
+ import { v as v8 } from "valleyed";
53
+
54
+ // src/cache/base.ts
55
+ var Cache = class {
56
+ };
57
+
58
+ // src/cache/pipes.ts
59
+ import { v } from "valleyed";
60
+ var redisConfigPipe = v.meta(
61
+ v.object({
62
+ host: v.string(),
63
+ port: v.optional(v.number()),
64
+ password: v.optional(v.string()),
65
+ username: v.optional(v.string()),
66
+ tls: v.optional(v.boolean()),
67
+ cluster: v.optional(v.boolean())
68
+ }),
69
+ { title: "Redis Config", $refId: "RedisConfig" }
70
+ );
71
+
72
+ // src/cache/types/redis.ts
73
+ import { Cluster, Redis } from "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
+ import { Server as SocketServer } from "socket.io";
96
+ import supertest from "supertest";
97
+ import { PipeError, v as v3 } from "valleyed";
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 = Instance.get().settings.server?.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
+ import { convert } from "@openapi-contrib/json-schema-to-openapi-schema";
146
+ import { capitalize } from "valleyed";
147
+
148
+ // src/server/routes.ts
149
+ import { v as v2 } from "valleyed";
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 v2.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: [...config.groups ?? [], ...route.groups ?? []],
182
+ middlewares: [...config.middlewares ?? [], ...route.middlewares ?? []],
183
+ schema: mergeSchemas(config.schema ?? {}, route.schema ?? {}),
184
+ security: [...config.security ?? [], ...route.security ?? []]
185
+ }));
186
+ var Router = class {
187
+ #config = { path: "" };
188
+ #routes = [];
189
+ #children = [];
190
+ constructor(config = { path: "" }) {
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
+ head = this.#wrap(Methods.head);
201
+ get = this.#wrap(Methods.get);
202
+ post = this.#wrap(Methods.post);
203
+ put = this.#wrap(Methods.put);
204
+ patch = this.#wrap(Methods.patch);
205
+ delete = this.#wrap(Methods.delete);
206
+ 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
+ };
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: config.config.openapi.docsVersion ?? ""
224
+ },
225
+ servers: config.config.openapi.docsBaseUrl?.map((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(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: route.title ?? cleanPath,
268
+ description: route.descriptions?.join("\n\n"),
269
+ tags: tag ? [tag] : void 0,
270
+ security: route.security
271
+ });
272
+ }
273
+ async #addRouteToOpenApiDoc(path, method, def, methodObj) {
274
+ if (def.response.response?.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 convert(this.#visit(resp.schema)) };
280
+ }
281
+ }
282
+ if (def.response.responseHeaders?.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 convert(this.#visit(resp.schema)) };
289
+ }
290
+ }
291
+ if (def.request.body)
292
+ methodObj.requestBody = {
293
+ required: true,
294
+ content: {
295
+ "application/json": { schema: await convert(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 convert(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: 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 (this.#baseOpenapiDoc.components?.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 = parsed.at(-1)?.name ?? "";
354
+ const description = parsed.map((g) => g.description?.trim() ?? "").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 = capitalize(`${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
+ import axios from "axios";
405
+ import jwt from "jsonwebtoken";
406
+ import jwksClient from "jwks-rsa";
407
+ var signinWithGoogle = async (idToken) => {
408
+ const authUrl = `https://oauth2.googleapis.com/tokeninfo?id_token=${idToken}`;
409
+ const { data } = await axios.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 = jwt.decode(idToken, { complete: true });
420
+ if (!json?.header) throw new Error("");
421
+ const { kid, alg } = json.header;
422
+ const publicKey = await jwksClient({ 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 = jwt.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 axios.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
+ import * as bcrypt from "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 (data?.constructor?.name !== "String") return data;
473
+ return JSON.parse(data);
474
+ } catch {
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
+ import { parseBuffer } from "music-metadata";
484
+ var getMediaDuration = async (buffer) => {
485
+ try {
486
+ const meta = await parseBuffer(buffer);
487
+ return meta.format.duration ?? 0;
488
+ } catch {
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
+ import crypto from "crypto";
510
+ function string(length = 20) {
511
+ return crypto.randomBytes(length).toString("hex").slice(0, length);
512
+ }
513
+ function number(min = 0, max = 2 ** 48 - 1) {
514
+ return crypto.randomInt(min, max);
515
+ }
516
+
517
+ // src/server/requests.ts
518
+ var Request = class {
519
+ ip;
520
+ method;
521
+ path;
522
+ body;
523
+ params;
524
+ query;
525
+ headers;
526
+ context;
527
+ cookies;
528
+ users = {
529
+ access: {},
530
+ refresh: {},
531
+ apiKey: {}
532
+ };
533
+ authUser;
534
+ constructor({
535
+ ip,
536
+ body,
537
+ cookies,
538
+ params,
539
+ query,
540
+ method,
541
+ path,
542
+ context,
543
+ headers,
544
+ files
545
+ }) {
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
+ };
566
+ var Response = class {
567
+ body;
568
+ headers;
569
+ status;
570
+ contentType;
571
+ piped;
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
+ import { match as Match } from "path-to-regexp";
586
+ var EmitterEvent = "__listener_emitter";
587
+ var defaultTo = "*";
588
+ var SocketEmitter = class {
589
+ socketInstance;
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
+ eventBus?.createSubscriber(
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 : [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) => Match(key)(channel);
641
+ const matchedChannel = 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 = Instance.get().settings.server?.requestsAuth.tokens;
656
+ if (socket.handshake.auth.authorization && tokensUtil)
657
+ user = await tokensUtil.verifyAccessToken(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 = 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, data.query ?? {});
688
+ const newChannel = `${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: v3.meta(v3.array(v3.object({ message: v3.string(), field: v3.optional(v3.string()) })), {
709
+ $refId: `Errors.${key}Response`,
710
+ description: `${key} Response`
711
+ })
712
+ }));
713
+ var Server = class {
714
+ constructor(server, config, implementations) {
715
+ this.config = config;
716
+ this.implementations = implementations;
717
+ this.server = server;
718
+ this.#openapi = new OpenApi(this.config);
719
+ const socketInstance = new SocketServer(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
+ socket;
727
+ server;
728
+ 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) => m.onSetup?.(route));
744
+ onError?.onSetup?.(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 (onError?.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 = schema?.defaultStatusCode ?? StatusCodes.Ok;
769
+ const defaultContentType = schema?.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 = schema[def.key] ?? v3.any();
785
+ if (def.skip) return;
786
+ if (def.type === "request") {
787
+ requestPipe[def.key] = pipe;
788
+ jsonSchema.request[def.key] = v3.schema(pipe);
789
+ }
790
+ if (def.type === "response") {
791
+ const pipeRecords = errorsSchemas.concat({ status: defaultStatusCode, contentType, pipe });
792
+ responsePipe[def.key] = v3.any().pipe((input) => {
793
+ const p = pipeRecords.find((r) => r.status === status)?.pipe;
794
+ if (!p) throw PipeError.root(`schema not defined for status code: ${status}`, input);
795
+ return v3.assert(p, input);
796
+ });
797
+ jsonSchema.response[def.key] = pipeRecords.map((record) => ({
798
+ status: record.status,
799
+ contentType: record.contentType,
800
+ schema: v3.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
+ () => v3.validate(v3.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
+ () => v3.validate(v3.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 supertest(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
+ };
882
+
883
+ // src/server/impls/express.ts
884
+ import http from "http";
885
+ import cookie from "cookie-parser";
886
+ import cors from "cors";
887
+ import express from "express";
888
+ import fileUpload from "express-fileupload";
889
+ import { rateLimit } from "express-rate-limit";
890
+ import helmet from "helmet";
891
+ import { pinoHttp } from "pino-http";
892
+ var ExpressServer = class extends Server {
893
+ #expressApp;
894
+ constructor(config) {
895
+ const app = express();
896
+ super(http.createServer(app), config, {
897
+ parseRequest: async (req) => {
898
+ const allHeaders = Object.fromEntries(Object.entries(req.headers).map(([key, val]) => [key, 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(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: req.body ?? {},
929
+ cookies: req.cookies ?? {},
930
+ params: req.params ?? {},
931
+ query: 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
+ this.#expressApp[method]?.(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({ logger: config.log }));
969
+ app.use(express.json());
970
+ app.use(express.text());
971
+ app.use(cookie());
972
+ app.use(
973
+ helmet({
974
+ crossOriginResourcePolicy: { policy: "cross-origin" },
975
+ contentSecurityPolicy: false
976
+ })
977
+ );
978
+ app.use(cors(this.cors));
979
+ app.use(express.urlencoded({ extended: false }));
980
+ if (config.config.publicPath) app.use(express.static(config.config.publicPath));
981
+ app.use(
982
+ fileUpload({
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
+ rateLimit({
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
+ import fastifyCookie from "@fastify/cookie";
1000
+ import fastifyCors from "@fastify/cors";
1001
+ import fastifyFormBody from "@fastify/formbody";
1002
+ import fastifyHelmet from "@fastify/helmet";
1003
+ import fastifyMultipart from "@fastify/multipart";
1004
+ import fastifyRateLimit from "@fastify/rate-limit";
1005
+ import fastifyStatic from "@fastify/static";
1006
+ import Fastify from "fastify";
1007
+ import qs from "qs";
1008
+ var FastifyServer = class extends Server {
1009
+ constructor(config) {
1010
+ const app = Fastify({
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: [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, val ?? null]));
1026
+ const headers = {
1027
+ ...allHeaders,
1028
+ Authorization: req.headers["authorization"]?.toString(),
1029
+ RefreshToken: req.headers["x-refresh-token"]?.toString(),
1030
+ ApiKey: req.headers["x-api-key"]?.toString(),
1031
+ ContentType: req.headers["content-type"]?.toString(),
1032
+ Referer: req.headers["referer"]?.toString(),
1033
+ UserAgent: req.headers["user-agent"]?.toString()
1034
+ };
1035
+ const { body, files } = excludeBufferKeys(req.body ?? {});
1036
+ return new Request({
1037
+ ip: req.ip,
1038
+ body,
1039
+ cookies: req.cookies ?? {},
1040
+ params: req.params ?? {},
1041
+ query: 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(fastifyStatic, { root: config.config.publicPath });
1074
+ app.register(fastifyCookie, {});
1075
+ app.register(fastifyCors, this.cors);
1076
+ app.register(fastifyFormBody, { parser: (str) => qs.parse(str) });
1077
+ app.register(fastifyHelmet, { crossOriginResourcePolicy: { policy: "cross-origin" }, contentSecurityPolicy: false });
1078
+ app.register(fastifyMultipart, {
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(fastifyRateLimit, {
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(body ?? {});
1109
+ const isFile = (val) => Array.isArray(val) ? isFile(val.at(0)) : Buffer.isBuffer(val?.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 Instance.get().settings.server?.requestsAuth.tokens?.verifyRefreshToken(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
+ import { v as v4 } from "valleyed";
1178
+
1179
+ // src/server/requests-auth/apiKeys.ts
1180
+ var BaseApiKeysUtility = class {
1181
+ };
1182
+
1183
+ // src/server/requests-auth/tokens.ts
1184
+ import jwt2 from "jsonwebtoken";
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
+ options;
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 = jwt2.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 = jwt2.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 = jwt2.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 jwt2.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 = jwt2.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 = v4.object({
1272
+ type: v4.in(["fastify", "express"]),
1273
+ port: v4.number(),
1274
+ publicPath: v4.optional(v4.string()),
1275
+ healthPath: v4.optional(v4.string()),
1276
+ openapi: v4.defaults(
1277
+ v4.object({
1278
+ docsVersion: v4.defaults(v4.string(), "1.0.0"),
1279
+ docsBaseUrl: v4.defaults(v4.array(v4.string()), ["/"]),
1280
+ docsPath: v4.defaults(v4.string(), "/__docs")
1281
+ }),
1282
+ {}
1283
+ ),
1284
+ requests: v4.defaults(
1285
+ v4.object({
1286
+ log: v4.defaults(v4.boolean(), true),
1287
+ paginationDefaultLimit: v4.defaults(v4.number(), 100),
1288
+ maxFileUploadSizeInMb: v4.defaults(v4.number(), 500),
1289
+ rateLimit: v4.defaults(
1290
+ v4.object({
1291
+ enabled: v4.defaults(v4.boolean(), false),
1292
+ periodInMs: v4.defaults(v4.number(), 60 * 60 * 1e3),
1293
+ limit: v4.defaults(v4.number(), 5e3)
1294
+ }),
1295
+ {}
1296
+ ),
1297
+ slowdown: v4.defaults(
1298
+ v4.object({
1299
+ enabled: v4.defaults(v4.boolean(), false),
1300
+ periodInMs: v4.defaults(v4.number(), 10 * 60 * 1e3),
1301
+ delayAfter: v4.defaults(v4.number(), 2e3),
1302
+ delayInMs: v4.defaults(v4.number(), 500)
1303
+ }),
1304
+ {}
1305
+ )
1306
+ }),
1307
+ {}
1308
+ ),
1309
+ requestsAuth: v4.defaults(
1310
+ v4.object({
1311
+ tokens: v4.optional(v4.instanceOf(BaseTokensUtility)),
1312
+ apiKey: v4.optional(v4.instanceOf(BaseApiKeysUtility))
1313
+ }),
1314
+ {}
1315
+ )
1316
+ });
1317
+
1318
+ // src/errors/types/authorizationExpired.ts
1319
+ var AuthorizationExpired = class extends RequestError {
1320
+ statusCode = StatusCodes.AuthorizationExpired;
1321
+ constructor(message = "Access token expired", cause) {
1322
+ super(message, [{ message }], cause);
1323
+ }
1324
+ };
1325
+
1326
+ // src/errors/types/badRequestError.ts
1327
+ var BadRequestError = class extends RequestError {
1328
+ statusCode = StatusCodes.BadRequest;
1329
+ constructor(message, cause) {
1330
+ super(message, [{ message }], cause);
1331
+ }
1332
+ };
1333
+
1334
+ // src/errors/types/notAuthenticatedError.ts
1335
+ var NotAuthenticatedError = class extends RequestError {
1336
+ statusCode = StatusCodes.NotAuthenticated;
1337
+ constructor(message = "Not authenticated", cause) {
1338
+ super(message, [{ message }], cause);
1339
+ }
1340
+ };
1341
+
1342
+ // src/errors/types/notAuthorizedError.ts
1343
+ var NotAuthorizedError = class extends RequestError {
1344
+ statusCode = StatusCodes.NotAuthorized;
1345
+ constructor(message = "Not authorized", cause) {
1346
+ super(message, [{ message }], cause);
1347
+ }
1348
+ };
1349
+
1350
+ // src/errors/types/notFoundError.ts
1351
+ var NotFoundError = class extends RequestError {
1352
+ statusCode = StatusCodes.NotFound;
1353
+ constructor(message = "Not found", cause) {
1354
+ super(message, [{ message }], cause);
1355
+ }
1356
+ };
1357
+
1358
+ // src/errors/types/refreshTokenMisusedError.ts
1359
+ var RefreshTokenMisusedError = class extends RequestError {
1360
+ statusCode = StatusCodes.NotAuthenticated;
1361
+ constructor(message = "Refresh token misused", cause) {
1362
+ super(message, [{ message }], cause);
1363
+ }
1364
+ };
1365
+
1366
+ // src/errors/types/validationError.ts
1367
+ var ValidationError = class extends RequestError {
1368
+ 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
+ );
1375
+ }
1376
+ };
1377
+
1378
+ // src/cache/types/redis.ts
1379
+ var RedisCache = class extends Cache {
1380
+ client;
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 Cluster([node], {
1395
+ ...extraConfig,
1396
+ redisOptions: common,
1397
+ lazyConnect: true
1398
+ }) : new 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
+ import axios2 from "axios";
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.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 topics.data[key]?.topics?.includes?.(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
+ import { ObjectId } from "mongodb";
1471
+ import { differ } from "valleyed";
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(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(data.before ?? "null");
1490
+ let after = JSON.parse(data.after ?? "null");
1491
+ if (before) before = hydrate(before);
1492
+ if (after) after = hydrate(after);
1493
+ if (before?.__id === TestId || after?.__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: differ.from(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 ObjectId(id);
1545
+ } catch {
1546
+ return id;
1547
+ }
1548
+ };
1549
+
1550
+ // src/dbs/mongo/index.ts
1551
+ import { MongoClient as MongoClient2, ObjectId as ObjectId3 } from "mongodb";
1552
+
1553
+ // src/dbs/mongo/api.ts
1554
+ import { ObjectId as ObjectId2 } from "mongodb";
1555
+
1556
+ // src/dbs/pipes.ts
1557
+ import { v as v5 } from "valleyed";
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 = v5.catch(v5.defaults(v5.in(["and" /* and */, "or" /* or */]), "and" /* and */), "and" /* and */);
1576
+ var queryWhere = v5.object({
1577
+ field: v5.string(),
1578
+ value: v5.any(),
1579
+ condition: v5.catch(v5.defaults(v5.in(Object.values(Conditions)), "eq" /* eq */), "eq" /* eq */)
1580
+ });
1581
+ var queryWhereBlock = v5.object({
1582
+ condition: queryKeys,
1583
+ value: v5.array(queryWhere)
1584
+ });
1585
+ var queryWhereClause = v5.defaults(v5.array(v5.or([queryWhere, queryWhereBlock])), []);
1586
+ function queryParamsPipe() {
1587
+ const pagLimit = Instance.get().settings.server?.requests.paginationDefaultLimit ?? 100;
1588
+ return v5.meta(
1589
+ v5.object({
1590
+ all: v5.defaults(v5.boolean(), false),
1591
+ limit: v5.catch(v5.defaults(v5.number().pipe(v5.lte(pagLimit)), pagLimit), pagLimit),
1592
+ page: v5.catch(v5.defaults(v5.number().pipe(v5.gte(1)), 1), 1),
1593
+ search: v5.defaults(
1594
+ v5.nullish(
1595
+ v5.object({
1596
+ value: v5.string(),
1597
+ fields: v5.array(v5.string())
1598
+ })
1599
+ ),
1600
+ null
1601
+ ),
1602
+ sort: v5.defaults(
1603
+ v5.array(
1604
+ v5.object({
1605
+ field: v5.string(),
1606
+ desc: v5.defaults(v5.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 v5.object({
1619
+ pages: v5.object({
1620
+ current: v5.number(),
1621
+ start: v5.number(),
1622
+ last: v5.number(),
1623
+ previous: v5.nullable(v5.number()),
1624
+ next: v5.nullable(v5.number())
1625
+ }),
1626
+ docs: v5.object({
1627
+ limit: v5.number(),
1628
+ total: v5.number(),
1629
+ count: v5.number()
1630
+ }),
1631
+ results: v5.array(model)
1632
+ });
1633
+ }
1634
+ function wrapQueryParams(params) {
1635
+ return v5.assert(queryParamsPipe(), params);
1636
+ }
1637
+ var mongoDbConfigPipe = v5.meta(
1638
+ v5.object({
1639
+ uri: v5.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 = 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" : 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
+ ...config.options?.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 && !config.options?.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 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 = options.getTime?.() ?? /* @__PURE__ */ new Date();
1767
+ const payload = values.map((value, i) => prepInsertValue(value, options.makeId?.(i) ?? new ObjectId2().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 = options.getTime?.() ?? /* @__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 = options.getTime?.() ?? /* @__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 = options.getTime?.() ?? /* @__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, options.makeId?.() ?? new ObjectId2().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: options?.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 = options.getTime?.() ?? /* @__PURE__ */ new Date();
1822
+ operations.forEach((operation, i) => {
1823
+ switch (operation.op) {
1824
+ case "insert":
1825
+ bulk.insert(prepInsertValue(operation.value, operation.makeId?.(i) ?? new ObjectId2().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
+ operation.makeId?.(i) ?? new ObjectId2().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 MongoClient2(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 (existing.options?.changeStreamPreAndPostImages?.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 ObjectId3();
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
+ import { v as v6 } from "valleyed";
1926
+ var rabbitmqConfigPipe = v6.meta(
1927
+ v6.object({
1928
+ uri: v6.string(),
1929
+ eventColumnName: v6.string()
1930
+ }),
1931
+ { title: "Rabbitmq Config", $refId: "RabbitmqConfig" }
1932
+ );
1933
+ var kafkaConfigPipe = v6.meta(
1934
+ v6.object({
1935
+ brokers: v6.array(v6.string()),
1936
+ ssl: v6.optional(v6.boolean()),
1937
+ sasl: v6.optional(
1938
+ v6.object({
1939
+ mechanism: v6.is("plain"),
1940
+ username: v6.string(),
1941
+ password: v6.string()
1942
+ })
1943
+ ),
1944
+ confluent: v6.optional(v6.boolean()),
1945
+ clientId: v6.optional(v6.string())
1946
+ }),
1947
+ { title: "Kafka Config", $refId: "KafkaConfig" }
1948
+ );
1949
+
1950
+ // src/events/types/kafka.ts
1951
+ import Confluent from "@confluentinc/kafka-javascript";
1952
+ import Kafka from "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 Confluent.KafkaJS.Kafka({
1962
+ kafkaJS: { ...kafkaSettings, logLevel: Confluent.KafkaJS.logLevel.NOTHING }
1963
+ }) : new Kafka.Kafka({ ...kafkaSettings, logLevel: Kafka.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 {
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
+ import { connect } from "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 = connect([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 {
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
+ import { v as v7 } from "valleyed";
2079
+ var redisJobsConfigPipe = v7.meta(
2080
+ v7.object({
2081
+ redisConfig: redisConfigPipe,
2082
+ queueName: v7.string()
2083
+ }),
2084
+ { title: "Redis Jobs Config", $refId: "RedisJobsConfig" }
2085
+ );
2086
+
2087
+ // src/jobs/types/redis.ts
2088
+ import Bull from "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 Bull(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 this.#callbacks.onDelayed?.(job.data)),
2106
+ this.#queue.process("CronJob" /* CronJob */, async (job) => await this.#callbacks.onCron?.(job.data.type)),
2107
+ this.#queue.process("RepeatableJob" /* RepeatableJob */, async (job) => await this.#callbacks.onRepeatable?.(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 job.opts?.repeat?.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 = () => v8.object({
2178
+ app: v8.object({
2179
+ id: v8.string(),
2180
+ name: v8.string()
2181
+ }),
2182
+ log: v8.defaults(
2183
+ v8.object({
2184
+ level: v8.defaults(v8.in(["fatal", "error", "warn", "info", "debug", "trace", "silent"]), "info")
2185
+ }),
2186
+ {}
2187
+ ),
2188
+ dbs: v8.optional(
2189
+ v8.object({
2190
+ types: v8.record(
2191
+ v8.string(),
2192
+ v8.discriminate((e) => e?.type, {
2193
+ mongo: v8.merge(mongoDbConfigPipe, v8.object({ type: v8.is("mongo") }))
2194
+ })
2195
+ ),
2196
+ changes: v8.optional(
2197
+ v8.object({
2198
+ debeziumUrl: v8.string(),
2199
+ kafkaConfig: kafkaConfigPipe
2200
+ })
2201
+ )
2202
+ })
2203
+ ),
2204
+ eventBus: v8.optional(
2205
+ v8.discriminate((e) => e?.type, {
2206
+ kafka: v8.merge(kafkaConfigPipe, v8.object({ type: v8.is("kafka") })),
2207
+ rabbitmq: v8.merge(rabbitmqConfigPipe, v8.object({ type: v8.is("rabbitmq") }))
2208
+ })
2209
+ ),
2210
+ cache: v8.discriminate((e) => e?.type, {
2211
+ redis: v8.merge(redisConfigPipe, v8.object({ type: v8.is("redis") }))
2212
+ }),
2213
+ jobs: v8.optional(v8.merge(redisJobsConfigPipe, v8.object({ type: v8.is("redis") }))),
2214
+ server: v8.optional(
2215
+ v8.object({
2216
+ type: v8.in(["fastify", "express"]),
2217
+ port: v8.number(),
2218
+ publicPath: v8.optional(v8.string()),
2219
+ healthPath: v8.optional(v8.string()),
2220
+ openapi: v8.defaults(
2221
+ v8.object({
2222
+ docsVersion: v8.defaults(v8.string(), "1.0.0"),
2223
+ docsBaseUrl: v8.defaults(v8.array(v8.string()), ["/"]),
2224
+ docsPath: v8.defaults(v8.string(), "/__docs")
2225
+ }),
2226
+ {}
2227
+ ),
2228
+ requests: v8.defaults(
2229
+ v8.object({
2230
+ log: v8.defaults(v8.boolean(), true),
2231
+ paginationDefaultLimit: v8.defaults(v8.number(), 100),
2232
+ maxFileUploadSizeInMb: v8.defaults(v8.number(), 500),
2233
+ rateLimit: v8.defaults(
2234
+ v8.object({
2235
+ enabled: v8.defaults(v8.boolean(), false),
2236
+ periodInMs: v8.defaults(v8.number(), 60 * 60 * 1e3),
2237
+ limit: v8.defaults(v8.number(), 5e3)
2238
+ }),
2239
+ {}
2240
+ ),
2241
+ slowdown: v8.defaults(
2242
+ v8.object({
2243
+ enabled: v8.defaults(v8.boolean(), false),
2244
+ periodInMs: v8.defaults(v8.number(), 10 * 60 * 1e3),
2245
+ delayAfter: v8.defaults(v8.number(), 2e3),
2246
+ delayInMs: v8.defaults(v8.number(), 500)
2247
+ }),
2248
+ {}
2249
+ )
2250
+ }),
2251
+ {}
2252
+ ),
2253
+ requestsAuth: v8.defaults(
2254
+ v8.object({
2255
+ tokens: v8.optional(v8.instanceOf(BaseTokensUtility)),
2256
+ apiKey: v8.optional(v8.instanceOf(BaseApiKeysUtility))
2257
+ }),
2258
+ {}
2259
+ )
2260
+ })
2261
+ ),
2262
+ utils: v8.defaults(
2263
+ v8.object({
2264
+ hashSaltRounds: v8.defaults(v8.number(), 10)
2265
+ }),
2266
+ {}
2267
+ )
2268
+ });
2269
+ function mapSettingsToInstance(settings) {
2270
+ const log = pino({
2271
+ level: settings.log.level,
2272
+ serializers: {
2273
+ err: pino.stdSerializers.err,
2274
+ error: pino.stdSerializers.err,
2275
+ req: pino.stdSerializers.req,
2276
+ res: pino.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 = settings.eventBus?.type === "kafka" ? new KafkaEventBus(deleteKeys(settings.eventBus, ["type"])) : settings.eventBus?.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 = settings.server?.type === "express" ? new ExpressServer({ ...serverConfig, config: settings.server }) : settings.server?.type === "fastify" ? new FastifyServer({ ...serverConfig, config: settings.server }) : void 0;
2288
+ const changesConfig = settings.dbs?.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(settings.dbs?.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 DataClass {
2316
+ static #instance;
2317
+ static #hooks = {};
2318
+ envs;
2319
+ settings;
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(_Instance.#hooks["setup"] ?? []);
2333
+ await runHooks(_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 = v9.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 = v9.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(_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) => v10.array(
2398
+ v10.file(err).pipe((input) => {
2399
+ const err2 = `is larger than allowed limit of ${Instance.get().settings.server?.requests.maxFileUploadSizeInMb}mb`;
2400
+ const valid = input ? !input.isTruncated : true;
2401
+ if (valid) return input;
2402
+ throw PipeError2.root(err2, input);
2403
+ })
2404
+ );
2405
+ var incomingFile = (err) => v10.pipe(
2406
+ (input) => v10.assert(
2407
+ filePipe(err).pipe(v10.min(1, "no file provided")).pipe((files) => files[0]),
2408
+ input
2409
+ ),
2410
+ { schema: () => ({ type: "string", format: "binary" }) }
2411
+ );
2412
+ var incomingFiles = (err) => v10.pipe((input) => v10.assert(filePipe(err), input), {
2413
+ schema: () => ({ type: "string", format: "binary" })
2414
+ });
2415
+ var requestLocalStorage = new AsyncLocalStorage();
2416
+ var responseLocalStorage = new AsyncLocalStorage();
2417
+ var withRequest = (fn) => v10.pipe((input) => {
2418
+ const req = requestLocalStorage.getStore();
2419
+ if (!req) throw PipeError2.root("Request not found in context", input);
2420
+ return v10.assert(fn(req), input);
2421
+ });
2422
+ var withResponse = (fn) => v10.pipe((input) => {
2423
+ const res = responseLocalStorage.getStore();
2424
+ if (!res) throw PipeError2.root("Response not found in context", input);
2425
+ return v10.assert(fn(res), input);
2426
+ });
2427
+
2428
+ // src/validations/index.ts
2429
+ import { v as v11 } from "valleyed";
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 = v11.validate(pipe, value);
2440
+ if (validity.valid) return validity.value;
2441
+ throw pipeErrorToValidationError(validity.error);
2442
+ }
2443
+
2444
+ export {
2445
+ Cache,
2446
+ redisConfigPipe,
2447
+ EquippedError,
2448
+ RequestError,
2449
+ valleyed_exports,
2450
+ pipeErrorToValidationError,
2451
+ validate,
2452
+ Methods,
2453
+ StatusCodes,
2454
+ makeMiddleware,
2455
+ makeErrorMiddleware,
2456
+ Router,
2457
+ authProviders_exports,
2458
+ hash_exports,
2459
+ parseJSONValue,
2460
+ parseJSONObject,
2461
+ getMediaDuration,
2462
+ sleep,
2463
+ retry,
2464
+ random_exports,
2465
+ Request,
2466
+ Response,
2467
+ Server,
2468
+ ExpressServer,
2469
+ FastifyServer,
2470
+ requireAuthUser,
2471
+ requireAuthorizationUser,
2472
+ requireApiKeyUser,
2473
+ requireRefreshTokenUser,
2474
+ BaseApiKeysUtility,
2475
+ BaseTokensUtility,
2476
+ CacheTokensUtility,
2477
+ serverPipe,
2478
+ AuthorizationExpired,
2479
+ BadRequestError,
2480
+ NotAuthenticatedError,
2481
+ NotAuthorizedError,
2482
+ NotFoundError,
2483
+ RefreshTokenMisusedError,
2484
+ ValidationError,
2485
+ RedisCache,
2486
+ TopicPrefix,
2487
+ Db,
2488
+ DbChange,
2489
+ MongoDbChange,
2490
+ QueryKeys,
2491
+ Conditions,
2492
+ queryParamsPipe,
2493
+ queryResultsPipe,
2494
+ wrapQueryParams,
2495
+ mongoDbConfigPipe,
2496
+ MongoDb,
2497
+ EventBus,
2498
+ DefaultSubscribeOptions,
2499
+ rabbitmqConfigPipe,
2500
+ kafkaConfigPipe,
2501
+ KafkaEventBus,
2502
+ RabbitMQEventBus,
2503
+ redisJobsConfigPipe,
2504
+ RedisJob,
2505
+ Instance
2506
+ };
2507
+ //# sourceMappingURL=chunk-DXTIVHEC.mjs.map