@flowerforce/flowerbase 1.6.3-beta.4 → 1.6.3-beta.6

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 (93) hide show
  1. package/README.md +14 -3
  2. package/dist/auth/providers/custom-function/controller.d.ts.map +1 -1
  3. package/dist/auth/providers/custom-function/controller.js +1 -0
  4. package/dist/auth/providers/local-userpass/controller.d.ts.map +1 -1
  5. package/dist/auth/providers/local-userpass/controller.js +1 -0
  6. package/dist/cli/call-function.js +1 -0
  7. package/dist/constants.js +1 -1
  8. package/dist/features/endpoints/index.d.ts.map +1 -1
  9. package/dist/features/endpoints/index.js +8 -1
  10. package/dist/features/endpoints/interface.d.ts +1 -0
  11. package/dist/features/endpoints/interface.d.ts.map +1 -1
  12. package/dist/features/endpoints/utils.d.ts +1 -1
  13. package/dist/features/endpoints/utils.d.ts.map +1 -1
  14. package/dist/features/endpoints/utils.js +2 -1
  15. package/dist/features/functions/controller.d.ts.map +1 -1
  16. package/dist/features/functions/controller.js +1 -0
  17. package/dist/features/triggers/index.d.ts.map +1 -1
  18. package/dist/features/triggers/index.js +5 -1
  19. package/dist/features/triggers/interface.d.ts +3 -0
  20. package/dist/features/triggers/interface.d.ts.map +1 -1
  21. package/dist/features/triggers/utils.d.ts +3 -3
  22. package/dist/features/triggers/utils.d.ts.map +1 -1
  23. package/dist/features/triggers/utils.js +172 -21
  24. package/dist/monitoring/plugin.d.ts.map +1 -1
  25. package/dist/monitoring/plugin.js +3 -155
  26. package/dist/monitoring/routes/functions.d.ts.map +1 -1
  27. package/dist/monitoring/routes/functions.js +3 -1
  28. package/dist/monitoring/utils.d.ts +1 -1
  29. package/dist/services/api/index.d.ts +5 -1
  30. package/dist/services/api/index.d.ts.map +1 -1
  31. package/dist/services/api/index.js +120 -31
  32. package/dist/services/aws/index.d.ts +5 -1
  33. package/dist/services/aws/index.d.ts.map +1 -1
  34. package/dist/services/aws/index.js +165 -11
  35. package/dist/services/index.d.ts +10 -2
  36. package/dist/services/index.d.ts.map +1 -1
  37. package/dist/services/mongodb-atlas/index.d.ts.map +1 -1
  38. package/dist/services/mongodb-atlas/index.js +533 -429
  39. package/dist/services/mongodb-atlas/model.d.ts +6 -2
  40. package/dist/services/mongodb-atlas/model.d.ts.map +1 -1
  41. package/dist/services/monitoring.d.ts +11 -0
  42. package/dist/services/monitoring.d.ts.map +1 -0
  43. package/dist/services/monitoring.js +22 -0
  44. package/dist/shared/handleUserDeletion.d.ts.map +1 -1
  45. package/dist/shared/handleUserDeletion.js +45 -21
  46. package/dist/shared/handleUserRegistration.d.ts.map +1 -1
  47. package/dist/shared/handleUserRegistration.js +112 -88
  48. package/dist/shared/models/handleUserRegistration.model.d.ts +3 -0
  49. package/dist/shared/models/handleUserRegistration.model.d.ts.map +1 -1
  50. package/dist/state.d.ts.map +1 -1
  51. package/dist/state.js +2 -1
  52. package/dist/utils/context/helpers.d.ts +2 -2
  53. package/dist/utils/context/helpers.d.ts.map +1 -1
  54. package/dist/utils/context/helpers.js +4 -2
  55. package/dist/utils/context/index.d.ts +1 -1
  56. package/dist/utils/context/index.d.ts.map +1 -1
  57. package/dist/utils/context/index.js +2 -1
  58. package/dist/utils/context/interface.d.ts +1 -0
  59. package/dist/utils/context/interface.d.ts.map +1 -1
  60. package/dist/utils/roles/helpers.js +1 -0
  61. package/package.json +1 -1
  62. package/src/auth/providers/custom-function/controller.ts +1 -0
  63. package/src/auth/providers/local-userpass/controller.ts +1 -0
  64. package/src/cli/call-function.ts +1 -0
  65. package/src/constants.ts +1 -1
  66. package/src/features/endpoints/index.ts +8 -1
  67. package/src/features/endpoints/interface.ts +1 -0
  68. package/src/features/endpoints/utils.ts +2 -0
  69. package/src/features/functions/controller.ts +1 -0
  70. package/src/features/triggers/index.ts +5 -1
  71. package/src/features/triggers/interface.ts +3 -0
  72. package/src/features/triggers/utils.ts +195 -23
  73. package/src/monitoring/plugin.ts +4 -177
  74. package/src/monitoring/routes/functions.ts +3 -1
  75. package/src/monitoring/ui.collections.js +61 -4
  76. package/src/monitoring/ui.css +76 -3
  77. package/src/monitoring/ui.endpoints.js +192 -13
  78. package/src/monitoring/ui.events.js +56 -0
  79. package/src/monitoring/ui.functions.js +61 -1
  80. package/src/monitoring/ui.html +40 -19
  81. package/src/services/api/index.ts +139 -55
  82. package/src/services/aws/index.ts +164 -18
  83. package/src/services/mongodb-atlas/index.ts +579 -477
  84. package/src/services/mongodb-atlas/model.ts +6 -2
  85. package/src/services/monitoring.ts +35 -0
  86. package/src/shared/handleUserDeletion.ts +44 -22
  87. package/src/shared/handleUserRegistration.ts +127 -104
  88. package/src/shared/models/handleUserRegistration.model.ts +1 -0
  89. package/src/state.ts +6 -1
  90. package/src/utils/context/helpers.ts +4 -1
  91. package/src/utils/context/index.ts +2 -0
  92. package/src/utils/context/interface.ts +1 -0
  93. package/src/utils/roles/helpers.ts +1 -0
package/README.md CHANGED
@@ -2,10 +2,20 @@
2
2
 
3
3
  > **A serverless-native MongoDB package designed for modern cloud applications**
4
4
 
5
- Unlike MongoDB Realm or other cloud platforms, we do not offer a graphical interface where you can configure services through a dashboard.
6
- Instead, everything is code-based and developer-driven, offering full flexibility through configuration files and source code.
5
+ Flowerbase is a Fastify‑based application that closely mirrors the Realm platform, while keeping consumer project structure and configuration identical to Realm for near‑zero‑impact migrations.
7
6
 
8
- This documentation is structured to guide both experienced Realm users and newcomers alike whether you’re migrating or starting clean.
7
+ We rewrote the core in Node.js and TypeScript, but preserved the same folder structure and configuration format as Realm services.
8
+
9
+ In addition to rewriting the core, we designed and built a dedicated Monitoring interface to give operators a real-time view of what’s happening inside Flowerbase, while keeping security and parity with Realm-like workflows.
10
+
11
+
12
+ Monitor Key capabilities:
13
+ - Live event stream, auth, functions, triggers, endpoints, service calls, console logs, and errors, with quick filtering/search.
14
+ - System stats snapshot (CPU, RAM, uptime, peaks) for quick health checks.
15
+ - Functions panel: list functions, view code (if allowed), invoke functions with custom args, and review recent invocation history.
16
+ - Endpoints panel: list HTTP endpoints and invoke them with custom method, headers, query, and payload.
17
+ - Users panel: search auth/custom users, create users, reset passwords, and enable/disable accounts.
18
+ - Collections panel: list collections, inspect rules snapshot for a given user/system context, and run query/aggregate with pagination + history.
9
19
 
10
20
  #### 🧠 Features Summary
11
21
  | Feature | Status |
@@ -18,6 +28,7 @@ This documentation is structured to guide both experienced Realm users and newco
18
28
  | Functions | ✅ Supported (unchanged) |
19
29
  | Triggers | ✅ Supported (unchanged) |
20
30
  | HTTP Endpoints | ✅ Supported (unchanged) |
31
+ | Monitoring UI | ✅ New |
21
32
 
22
33
 
23
34
  > ⚠️ **Already have an existing Realm project?**
@@ -1 +1 @@
1
- {"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../../../src/auth/providers/custom-function/controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AASzC;;;;GAIG;AACH,wBAAsB,wBAAwB,CAAC,GAAG,EAAE,eAAe,iBAoGlE"}
1
+ {"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../../../src/auth/providers/custom-function/controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AASzC;;;;GAIG;AACH,wBAAsB,wBAAwB,CAAC,GAAG,EAAE,eAAe,iBAqGlE"}
@@ -58,6 +58,7 @@ function customFunctionController(app) {
58
58
  rules: {},
59
59
  user: {},
60
60
  currentFunction: functionsList[authFunctionName],
61
+ functionName: authFunctionName,
61
62
  functionsList,
62
63
  services,
63
64
  request: {
@@ -1 +1 @@
1
- {"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../../../src/auth/providers/local-userpass/controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAqCzC;;;;GAIG;AACH,wBAAsB,uBAAuB,CAAC,GAAG,EAAE,eAAe,iBA8WjE"}
1
+ {"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../../../src/auth/providers/local-userpass/controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAqCzC;;;;GAIG;AACH,wBAAsB,uBAAuB,CAAC,GAAG,EAAE,eAAe,iBA+WjE"}
@@ -86,6 +86,7 @@ function localUserPassController(app) {
86
86
  rules: {},
87
87
  user: {},
88
88
  currentFunction,
89
+ functionName: resetPasswordConfig.resetFunctionName,
89
90
  functionsList,
90
91
  services
91
92
  });
@@ -219,6 +219,7 @@ const executeLocal = (_a) => __awaiter(void 0, [_a], void 0, function* ({ basePa
219
219
  rules: rulesList,
220
220
  user,
221
221
  currentFunction: currentFunction,
222
+ functionName: name,
222
223
  functionsList,
223
224
  services,
224
225
  runAsSystem: true,
package/dist/constants.js CHANGED
@@ -57,7 +57,7 @@ exports.DEFAULT_CONFIG = {
57
57
  .map((item) => item.trim())
58
58
  .filter(Boolean),
59
59
  MONIT_RATE_LIMIT_WINDOW_MS: Number(process.env.MONIT_RATE_LIMIT_WINDOW_MS) || 60000,
60
- MONIT_RATE_LIMIT_MAX: Number(process.env.MONIT_RATE_LIMIT_MAX) || 120,
60
+ MONIT_RATE_LIMIT_MAX: Number(process.env.MONIT_RATE_LIMIT_MAX) || 220,
61
61
  MONIT_ALLOW_INVOKE: parseBoolean((_d = process.env.MONIT_ALLOW_INVOKE) !== null && _d !== void 0 ? _d : 'true'),
62
62
  MONIT_ALLOW_EDIT: parseBoolean((_e = process.env.MONIT_ALLOW_EDIT) !== null && _e !== void 0 ? _e : 'true'),
63
63
  CORS_OPTIONS: {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/features/endpoints/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAA;AAGrD;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,GAAU,kDAKrC,uBAAuB,kBAYzB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/features/endpoints/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAA;AAGrD;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,GAAU,kDAKrC,uBAAuB,kBAmBzB,CAAA"}
@@ -23,7 +23,14 @@ const generateEndpoints = (_a) => __awaiter(void 0, [_a], void 0, function* ({ a
23
23
  const currentFunction = functionsList[function_name];
24
24
  if (disabled || !currentFunction)
25
25
  return;
26
- const handler = (0, utils_1.generateHandler)({ app, rulesList, currentFunction, functionsList, http_method });
26
+ const handler = (0, utils_1.generateHandler)({
27
+ app,
28
+ rulesList,
29
+ currentFunction,
30
+ functionName: function_name,
31
+ functionsList,
32
+ http_method
33
+ });
27
34
  const currentMethod = (0, utils_1.getMethodsConfig)(app, handler, `/app/:appId/endpoint/${route.replace(/^\//, "")}`)[http_method];
28
35
  currentMethod();
29
36
  });
@@ -10,6 +10,7 @@ export type GenerateEndpointsParams = {
10
10
  export type GenerateHandlerParams = {
11
11
  app: FastifyInstance;
12
12
  currentFunction: Function;
13
+ functionName?: string;
13
14
  functionsList: Functions;
14
15
  http_method: string;
15
16
  rulesList: Rules;
@@ -1 +1 @@
1
- {"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../../src/features/endpoints/interface.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AACzC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AAC5D,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAE1C,MAAM,MAAM,uBAAuB,GAAG;IACpC,GAAG,EAAE,eAAe,CAAA;IACpB,aAAa,EAAE,SAAS,CAAA;IACxB,aAAa,EAAE,SAAS,CAAA;IACxB,SAAS,EAAE,KAAK,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG;IAClC,GAAG,EAAE,eAAe,CAAA;IACpB,eAAe,EAAE,QAAQ,CAAA;IACzB,aAAa,EAAE,SAAS,CAAA;IACxB,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,KAAK,CAAA;CACjB,CAAA;AAED,KAAK,WAAW,CAAC,CAAC,IAAI,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,OAAO,GAAG,CAAC,CAAA;AAErE,MAAM,MAAM,QAAQ,CAAC,CAAC,GAAG,KAAK,IAAI;IAChC,WAAW,EAAE,WAAW,CAAC,CAAC,CAAC,CAAA;IAC3B,KAAK,EAAE,MAAM,CAAA;IACb,aAAa,EAAE,MAAM,CAAA;IACrB,WAAW,EAAE,MAAM,CAAA;IACnB,iBAAiB,EAAE,MAAM,CAAA;IACzB,cAAc,EAAE,OAAO,CAAA;IACvB,sBAAsB,EAAE,OAAO,CAAA;IAC/B,mBAAmB,EAAE,OAAO,CAAA;IAC5B,QAAQ,EAAE,OAAO,CAAA;CAClB,CAAA;AACD,MAAM,MAAM,SAAS,CAAC,CAAC,GAAG,KAAK,IAAI,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAA"}
1
+ {"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../../src/features/endpoints/interface.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AACzC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AAC5D,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAE1C,MAAM,MAAM,uBAAuB,GAAG;IACpC,GAAG,EAAE,eAAe,CAAA;IACpB,aAAa,EAAE,SAAS,CAAA;IACxB,aAAa,EAAE,SAAS,CAAA;IACxB,SAAS,EAAE,KAAK,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG;IAClC,GAAG,EAAE,eAAe,CAAA;IACpB,eAAe,EAAE,QAAQ,CAAA;IACzB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,aAAa,EAAE,SAAS,CAAA;IACxB,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,KAAK,CAAA;CACjB,CAAA;AAED,KAAK,WAAW,CAAC,CAAC,IAAI,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,OAAO,GAAG,CAAC,CAAA;AAErE,MAAM,MAAM,QAAQ,CAAC,CAAC,GAAG,KAAK,IAAI;IAChC,WAAW,EAAE,WAAW,CAAC,CAAC,CAAC,CAAA;IAC3B,KAAK,EAAE,MAAM,CAAA;IACb,aAAa,EAAE,MAAM,CAAA;IACrB,WAAW,EAAE,MAAM,CAAA;IACnB,iBAAiB,EAAE,MAAM,CAAA;IACzB,cAAc,EAAE,OAAO,CAAA;IACvB,sBAAsB,EAAE,OAAO,CAAA;IAC/B,mBAAmB,EAAE,OAAO,CAAA;IAC5B,QAAQ,EAAE,OAAO,CAAA;CAClB,CAAA;AACD,MAAM,MAAM,SAAS,CAAC,CAAC,GAAG,KAAK,IAAI,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAA"}
@@ -27,5 +27,5 @@ export declare const getMethodsConfig: (app: FastifyInstance, handler: ReturnTyp
27
27
  * @param currentFunction -> the name of the function that should be called for that endpoint
28
28
  * @param functionsList -> the list of all functions
29
29
  */
30
- export declare const generateHandler: ({ app, currentFunction, functionsList, rulesList }: GenerateHandlerParams) => (req: FastifyRequest, res: FastifyReply) => Promise<{}>;
30
+ export declare const generateHandler: ({ app, currentFunction, functionName, functionsList, rulesList }: GenerateHandlerParams) => (req: FastifyRequest, res: FastifyReply) => Promise<{}>;
31
31
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/features/endpoints/utils.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAKvE,OAAO,EAAE,SAAS,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AAE9D;;;GAGG;AACH,eAAO,MAAM,aAAa,GAAU,gBAAuB,KAAG,OAAO,CAAC,SAAS,CA+B9E,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,GAC3B,KAAK,eAAe,EACpB,SAAS,UAAU,CAAC,OAAO,eAAe,CAAC,EAC3C,UAAU,MAAM;;;;;;;CAkDhB,CAAA;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,GAAI,oDAK7B,qBAAqB,MACR,KAAK,cAAc,EAAE,KAAK,YAAY,gBA4CrD,CAAA"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/features/endpoints/utils.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAKvE,OAAO,EAAE,SAAS,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AAE9D;;;GAGG;AACH,eAAO,MAAM,aAAa,GAAU,gBAAuB,KAAG,OAAO,CAAC,SAAS,CA+B9E,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,GAC3B,KAAK,eAAe,EACpB,SAAS,UAAU,CAAC,OAAO,eAAe,CAAC,EAC3C,UAAU,MAAM;;;;;;;CAkDhB,CAAA;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,GAAI,kEAM7B,qBAAqB,MACR,KAAK,cAAc,EAAE,KAAK,YAAY,gBA6CrD,CAAA"}
@@ -119,7 +119,7 @@ exports.getMethodsConfig = getMethodsConfig;
119
119
  * @param currentFunction -> the name of the function that should be called for that endpoint
120
120
  * @param functionsList -> the list of all functions
121
121
  */
122
- const generateHandler = ({ app, currentFunction, functionsList, rulesList }) => {
122
+ const generateHandler = ({ app, currentFunction, functionName, functionsList, rulesList }) => {
123
123
  return (req, res) => __awaiter(void 0, void 0, void 0, function* () {
124
124
  var _a;
125
125
  const { body: originalBody, headers, query, rawBody } = req;
@@ -148,6 +148,7 @@ const generateHandler = ({ app, currentFunction, functionsList, rulesList }) =>
148
148
  rules: rulesList,
149
149
  user: req.user,
150
150
  currentFunction,
151
+ functionName,
151
152
  functionsList,
152
153
  services: services_1.services,
153
154
  deserializeArgs: false
@@ -1 +1 @@
1
- {"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../../src/features/functions/controller.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AA2ChD;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,EAAE,kBAkKjC,CAAA"}
1
+ {"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../../src/features/functions/controller.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AA2ChD;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,EAAE,kBAmKjC,CAAA"}
@@ -106,6 +106,7 @@ const functionsController = (app_1, _a) => __awaiter(void 0, [app_1, _a], void 0
106
106
  rules,
107
107
  user: Object.assign(Object.assign({}, user), { _id: new bson_1.ObjectId(user.id) }),
108
108
  currentFunction,
109
+ functionName: String(method),
109
110
  functionsList,
110
111
  services: services_1.services
111
112
  });
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/features/triggers/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,sBAAsB,EAAE,MAAM,QAAQ,CAAA;AAG/C;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,GAAU,0CAIpC,sBAAsB,kBAgExB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/features/triggers/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,sBAAsB,EAAE,MAAM,QAAQ,CAAA;AAG/C;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,GAAU,0CAIpC,sBAAsB,kBAoExB,CAAA"}
@@ -82,12 +82,16 @@ const activateTriggers = (_a) => __awaiter(void 0, [_a], void 0, function* ({ fa
82
82
  const { type, config, event_processors } = content;
83
83
  const functionName = event_processors.FUNCTION.config.function_name;
84
84
  const triggerHandler = functionsList[functionName];
85
+ const triggerName = content.name || trigger.fileName || 'trigger';
85
86
  yield utils_1.TRIGGER_HANDLERS[type]({
86
87
  config,
87
88
  triggerHandler,
88
89
  app: fastify,
89
90
  services: services_1.services,
90
- functionsList
91
+ functionsList,
92
+ triggerName,
93
+ triggerType: type,
94
+ functionName: String(functionName)
91
95
  });
92
96
  }
93
97
  }
@@ -42,6 +42,9 @@ export type HandlerParams = {
42
42
  app: FastifyInstance;
43
43
  services: Services;
44
44
  functionsList: Functions;
45
+ triggerName: string;
46
+ triggerType: TriggerType;
47
+ functionName: string;
45
48
  };
46
49
  export {};
47
50
  //# sourceMappingURL=interface.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../../src/features/triggers/interface.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAA;AACnD,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AAE5D,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,WAAW,CAAA;IACjB,QAAQ,EAAE,OAAO,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,gBAAgB,EAAE;QAChB,QAAQ,EAAE;YACR,MAAM,EAAE;gBACN,aAAa,EAAE,MAAM,CAAA;aACtB,CAAA;SACF,CAAA;KACF,CAAA;CACF;AAED,KAAK,MAAM,GAAG;IACZ,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,aAAa,EAAE,OAAO,CAAA;IACtB,2BAA2B,EAAE,OAAO,CAAA;IACpC,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B,eAAe,EAAE,MAAM,EAAE,CAAA;IACzB,cAAc,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAA;IAC/C,SAAS,CAAC,EAAE,MAAM,EAAE,CAAA;IACpB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAChC,YAAY,EAAE,MAAM,CAAA;IACpB,mBAAmB,EAAE,OAAO,CAAA;IAC5B,sBAAsB,EAAE,OAAO,CAAA;IAC/B,SAAS,EAAE,OAAO,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,UAAU,GAAG,gBAAgB,CAAA;AACrE,MAAM,MAAM,QAAQ,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,EAAE,CAAA;AAE/D,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,cAAc,EAAE,QAAQ,CAAA;IACxB,GAAG,EAAE,eAAe,CAAA;IACpB,QAAQ,EAAE,QAAQ,CAAA;IAClB,aAAa,EAAE,SAAS,CAAA;CACzB,CAAA"}
1
+ {"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../../src/features/triggers/interface.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAA;AACnD,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AAE5D,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,WAAW,CAAA;IACjB,QAAQ,EAAE,OAAO,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,gBAAgB,EAAE;QAChB,QAAQ,EAAE;YACR,MAAM,EAAE;gBACN,aAAa,EAAE,MAAM,CAAA;aACtB,CAAA;SACF,CAAA;KACF,CAAA;CACF;AAED,KAAK,MAAM,GAAG;IACZ,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,aAAa,EAAE,OAAO,CAAA;IACtB,2BAA2B,EAAE,OAAO,CAAA;IACpC,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B,eAAe,EAAE,MAAM,EAAE,CAAA;IACzB,cAAc,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAA;IAC/C,SAAS,CAAC,EAAE,MAAM,EAAE,CAAA;IACpB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAChC,YAAY,EAAE,MAAM,CAAA;IACpB,mBAAmB,EAAE,OAAO,CAAA;IAC5B,sBAAsB,EAAE,OAAO,CAAA;IAC/B,SAAS,EAAE,OAAO,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,UAAU,GAAG,gBAAgB,CAAA;AACrE,MAAM,MAAM,QAAQ,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,EAAE,CAAA;AAE/D,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,cAAc,EAAE,QAAQ,CAAA;IACxB,GAAG,EAAE,eAAe,CAAA;IACpB,QAAQ,EAAE,QAAQ,CAAA;IAClB,aAAa,EAAE,SAAS,CAAA;IACxB,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,WAAW,CAAA;IACxB,YAAY,EAAE,MAAM,CAAA;CACrB,CAAA"}
@@ -9,8 +9,8 @@ import { HandlerParams, Triggers } from './interface';
9
9
  */
10
10
  export declare const loadTriggers: (rootDir?: string) => Promise<Triggers>;
11
11
  export declare const TRIGGER_HANDLERS: {
12
- SCHEDULED: ({ config, triggerHandler, functionsList, services, app }: HandlerParams) => Promise<void>;
13
- DATABASE: ({ config, triggerHandler, functionsList, services, app }: HandlerParams) => Promise<void>;
14
- AUTHENTICATION: ({ config, triggerHandler, functionsList, services, app }: HandlerParams) => Promise<void>;
12
+ SCHEDULED: ({ config, triggerHandler, functionsList, services, app, triggerName, triggerType, functionName }: HandlerParams) => Promise<void>;
13
+ DATABASE: ({ config, triggerHandler, functionsList, services, app, triggerName, triggerType, functionName }: HandlerParams) => Promise<void>;
14
+ AUTHENTICATION: ({ config, triggerHandler, functionsList, services, app, triggerName, triggerType, functionName }: HandlerParams) => Promise<void>;
15
15
  };
16
16
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/features/triggers/utils.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,aAAa,EAAW,QAAQ,EAAE,MAAM,aAAa,CAAA;AAqC9D;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,GAAU,gBAAuB,KAAG,OAAO,CAAC,QAAQ,CAkB5E,CAAA;AAyeD,eAAO,MAAM,gBAAgB;0EArd1B,aAAa;yEA0Zb,aAAa;+EArUb,aAAa;CAoYf,CAAA"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/features/triggers/utils.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,aAAa,EAAW,QAAQ,EAAE,MAAM,aAAa,CAAA;AA0E9D;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,GAAU,gBAAuB,KAAG,OAAO,CAAC,QAAQ,CAkB5E,CAAA;AA+mBD,eAAO,MAAM,gBAAgB;kHAxlB1B,aAAa;iHAggBb,aAAa;uHA/Yb,aAAa;CA2ef,CAAA"}
@@ -28,8 +28,9 @@ const fs_1 = __importDefault(require("fs"));
28
28
  const node_path_1 = __importDefault(require("node:path"));
29
29
  const node_cron_1 = __importDefault(require("node-cron"));
30
30
  const constants_1 = require("../../constants");
31
+ const utils_1 = require("../../monitoring/utils");
31
32
  const state_1 = require("../../state");
32
- const utils_1 = require("../../utils");
33
+ const utils_2 = require("../../utils");
33
34
  const context_1 = require("../../utils/context");
34
35
  const registerOnClose = (app, handler, label) => {
35
36
  if (app.server) {
@@ -65,6 +66,29 @@ const shouldIgnoreStreamError = (error) => {
65
66
  return true;
66
67
  return false;
67
68
  };
69
+ const emitTriggerEvent = ({ status, triggerName, triggerType, functionName, meta, error }) => {
70
+ const monitoring = state_1.StateManager.select('monitoring');
71
+ const addEvent = monitoring === null || monitoring === void 0 ? void 0 : monitoring.addEvent;
72
+ if (typeof addEvent !== 'function')
73
+ return;
74
+ addEvent({
75
+ id: (0, utils_1.createEventId)(),
76
+ ts: Date.now(),
77
+ type: status === 'error' ? 'error' : 'trigger',
78
+ source: 'trigger',
79
+ message: status === 'error'
80
+ ? `trigger ${triggerName} failed`
81
+ : `trigger ${triggerName} fired`,
82
+ data: (0, utils_1.sanitize)({
83
+ trigger: triggerName,
84
+ triggerType,
85
+ functionName,
86
+ status,
87
+ meta,
88
+ error: status === 'error' ? error : undefined
89
+ })
90
+ });
91
+ };
68
92
  /**
69
93
  * Loads trigger files from the specified directory and returns them as an array of objects.
70
94
  * Each object contains the file name and the parsed JSON content.
@@ -81,7 +105,7 @@ const loadTriggers = (...args_1) => __awaiter(void 0, [...args_1], void 0, funct
81
105
  .filter((fileName) => fileName.endsWith('.json'))
82
106
  .map((fileName) => ({
83
107
  fileName,
84
- content: (0, utils_1.readJsonContent)(node_path_1.default.join(triggersPath, fileName))
108
+ content: (0, utils_2.readJsonContent)(node_path_1.default.join(triggersPath, fileName))
85
109
  }));
86
110
  return triggers;
87
111
  }
@@ -103,17 +127,43 @@ exports.loadTriggers = loadTriggers;
103
127
  * @param {Object} params.services - Services available to the handler.
104
128
  * @param {Object} params.app - The app instance for context.
105
129
  */
106
- const handleCronTrigger = (_a) => __awaiter(void 0, [_a], void 0, function* ({ config, triggerHandler, functionsList, services, app }) {
130
+ const handleCronTrigger = (_a) => __awaiter(void 0, [_a], void 0, function* ({ config, triggerHandler, functionsList, services, app, triggerName, triggerType, functionName }) {
107
131
  const task = node_cron_1.default.schedule(config.schedule, () => __awaiter(void 0, void 0, void 0, function* () {
108
- yield (0, context_1.GenerateContext)({
109
- args: [],
110
- app,
111
- rules: {},
112
- user: {},
113
- currentFunction: triggerHandler,
114
- functionsList,
115
- services
132
+ emitTriggerEvent({
133
+ status: 'fired',
134
+ triggerName,
135
+ triggerType,
136
+ functionName,
137
+ meta: {
138
+ schedule: config.schedule,
139
+ isAutoTrigger: !!config.isAutoTrigger
140
+ }
116
141
  });
142
+ try {
143
+ yield (0, context_1.GenerateContext)({
144
+ args: [],
145
+ app,
146
+ rules: {},
147
+ user: {},
148
+ currentFunction: triggerHandler,
149
+ functionName,
150
+ functionsList,
151
+ services
152
+ });
153
+ }
154
+ catch (error) {
155
+ emitTriggerEvent({
156
+ status: 'error',
157
+ triggerName,
158
+ triggerType,
159
+ functionName,
160
+ meta: {
161
+ schedule: config.schedule,
162
+ isAutoTrigger: !!config.isAutoTrigger
163
+ },
164
+ error
165
+ });
166
+ }
117
167
  }));
118
168
  registerOnClose(app, () => task.stop(), 'Scheduled trigger');
119
169
  });
@@ -165,7 +215,7 @@ const resolveDocumentOptions = ({ requestFullDocument, requestFullDocumentBefore
165
215
  const fullDocumentBeforeChange = requestFullDocumentBeforeChange ? 'whenAvailable' : undefined;
166
216
  return { fullDocument, fullDocumentBeforeChange };
167
217
  };
168
- const handleAuthenticationTrigger = (_a) => __awaiter(void 0, [_a], void 0, function* ({ config, triggerHandler, functionsList, services, app }) {
218
+ const handleAuthenticationTrigger = (_a) => __awaiter(void 0, [_a], void 0, function* ({ config, triggerHandler, functionsList, services, app, triggerName, triggerType, functionName }) {
169
219
  var _b, _c;
170
220
  const { database, isAutoTrigger, operation_types = [], operation_type } = config;
171
221
  const providerFilter = normalizeProviders((_b = config.providers) !== null && _b !== void 0 ? _b : []);
@@ -173,6 +223,13 @@ const handleAuthenticationTrigger = (_a) => __awaiter(void 0, [_a], void 0, func
173
223
  const collection = app.mongo.client.db(database || constants_1.DB_NAME).collection(authCollection);
174
224
  const operationCandidates = operation_type ? mapOpInverse[operation_type] : operation_types;
175
225
  const normalizedOps = normalizeOperationTypes(operationCandidates);
226
+ const baseMeta = {
227
+ database: database || constants_1.DB_NAME,
228
+ collection: authCollection,
229
+ operationTypes: normalizedOps,
230
+ providers: providerFilter,
231
+ isAutoTrigger: !!isAutoTrigger
232
+ };
176
233
  const { fullDocument, fullDocumentBeforeChange } = resolveDocumentOptions({
177
234
  requestFullDocument: config.full_document,
178
235
  requestFullDocumentBeforeChange: config.full_document_before_change,
@@ -264,18 +321,34 @@ const handleAuthenticationTrigger = (_a) => __awaiter(void 0, [_a], void 0, func
264
321
  updateDescription
265
322
  };
266
323
  try {
324
+ emitTriggerEvent({
325
+ status: 'fired',
326
+ triggerName,
327
+ triggerType,
328
+ functionName,
329
+ meta: Object.assign(Object.assign({}, baseMeta), { event: 'LOGOUT' })
330
+ });
267
331
  yield (0, context_1.GenerateContext)({
268
332
  args: [Object.assign({ user: userData }, op)],
269
333
  app,
270
334
  rules: state_1.StateManager.select("rules"),
271
335
  user: {}, // TODO from currentUser ??
272
336
  currentFunction: triggerHandler,
337
+ functionName,
273
338
  functionsList,
274
339
  services,
275
340
  runAsSystem: true
276
341
  });
277
342
  }
278
343
  catch (error) {
344
+ emitTriggerEvent({
345
+ status: 'error',
346
+ triggerName,
347
+ triggerType,
348
+ functionName,
349
+ meta: Object.assign(Object.assign({}, baseMeta), { event: 'LOGOUT' }),
350
+ error
351
+ });
279
352
  console.log("🚀 ~ handleAuthenticationTrigger ~ error:", error);
280
353
  }
281
354
  return;
@@ -300,18 +373,34 @@ const handleAuthenticationTrigger = (_a) => __awaiter(void 0, [_a], void 0, func
300
373
  updateDescription
301
374
  };
302
375
  try {
376
+ emitTriggerEvent({
377
+ status: 'fired',
378
+ triggerName,
379
+ triggerType,
380
+ functionName,
381
+ meta: Object.assign(Object.assign({}, baseMeta), { event: 'DELETE' })
382
+ });
303
383
  yield (0, context_1.GenerateContext)({
304
384
  args: isAutoTrigger ? [userData] : [Object.assign({ user: userData }, op)],
305
385
  app,
306
386
  rules: state_1.StateManager.select("rules"),
307
387
  user: {}, // TODO from currentUser ??
308
388
  currentFunction: triggerHandler,
389
+ functionName,
309
390
  functionsList,
310
391
  services,
311
392
  runAsSystem: true
312
393
  });
313
394
  }
314
395
  catch (error) {
396
+ emitTriggerEvent({
397
+ status: 'error',
398
+ triggerName,
399
+ triggerType,
400
+ functionName,
401
+ meta: Object.assign(Object.assign({}, baseMeta), { event: 'DELETE' }),
402
+ error
403
+ });
315
404
  console.log("🚀 ~ handleAuthenticationTrigger ~ error:", error);
316
405
  }
317
406
  return;
@@ -338,18 +427,34 @@ const handleAuthenticationTrigger = (_a) => __awaiter(void 0, [_a], void 0, func
338
427
  updateDescription
339
428
  };
340
429
  try {
430
+ emitTriggerEvent({
431
+ status: 'fired',
432
+ triggerName,
433
+ triggerType,
434
+ functionName,
435
+ meta: Object.assign(Object.assign({}, baseMeta), { event: 'UPDATE' })
436
+ });
341
437
  yield (0, context_1.GenerateContext)({
342
438
  args: isAutoTrigger ? [userData] : [Object.assign({ user: userData }, op)],
343
439
  app,
344
440
  rules: state_1.StateManager.select("rules"),
345
441
  user: {}, // TODO from currentUser ??
346
442
  currentFunction: triggerHandler,
443
+ functionName,
347
444
  functionsList,
348
445
  services,
349
446
  runAsSystem: true
350
447
  });
351
448
  }
352
449
  catch (error) {
450
+ emitTriggerEvent({
451
+ status: 'error',
452
+ triggerName,
453
+ triggerType,
454
+ functionName,
455
+ meta: Object.assign(Object.assign({}, baseMeta), { event: 'UPDATE' }),
456
+ error
457
+ });
353
458
  console.log("🚀 ~ handleAuthenticationTrigger ~ error:", error);
354
459
  }
355
460
  return;
@@ -413,18 +518,34 @@ const handleAuthenticationTrigger = (_a) => __awaiter(void 0, [_a], void 0, func
413
518
  updateDescription
414
519
  };
415
520
  try {
521
+ emitTriggerEvent({
522
+ status: 'fired',
523
+ triggerName,
524
+ triggerType,
525
+ functionName,
526
+ meta: Object.assign(Object.assign({}, baseMeta), { event: 'CREATE' })
527
+ });
416
528
  yield (0, context_1.GenerateContext)({
417
529
  args: isAutoTrigger ? [userData] : [Object.assign({ user: userData }, op)],
418
530
  app,
419
531
  rules: state_1.StateManager.select("rules"),
420
532
  user: {}, // TODO from currentUser ??
421
533
  currentFunction: triggerHandler,
534
+ functionName,
422
535
  functionsList,
423
536
  services,
424
537
  runAsSystem: true
425
538
  });
426
539
  }
427
540
  catch (error) {
541
+ emitTriggerEvent({
542
+ status: 'error',
543
+ triggerName,
544
+ triggerType,
545
+ functionName,
546
+ meta: Object.assign(Object.assign({}, baseMeta), { event: 'CREATE' }),
547
+ error
548
+ });
428
549
  console.log("🚀 ~ handleAuthenticationTrigger ~ error:", error);
429
550
  }
430
551
  });
@@ -451,7 +572,7 @@ const handleAuthenticationTrigger = (_a) => __awaiter(void 0, [_a], void 0, func
451
572
  * @param {Object} params.services - Services available to the handler.
452
573
  * @param {Object} params.app - The app instance for context.
453
574
  */
454
- const handleDataBaseTrigger = (_a) => __awaiter(void 0, [_a], void 0, function* ({ config, triggerHandler, functionsList, services, app }) {
575
+ const handleDataBaseTrigger = (_a) => __awaiter(void 0, [_a], void 0, function* ({ config, triggerHandler, functionsList, services, app, triggerName, triggerType, functionName }) {
455
576
  const { database, collection: collectionName, operation_types = [], match = {}, project = {} } = config;
456
577
  const normalizedOperations = normalizeOperationTypes(operation_types);
457
578
  const collection = app.mongo.client.db(database).collection(collectionName);
@@ -482,15 +603,45 @@ const handleDataBaseTrigger = (_a) => __awaiter(void 0, [_a], void 0, function*
482
603
  changeStream.on('change', function (_a) {
483
604
  return __awaiter(this, void 0, void 0, function* () {
484
605
  var { clusterTime } = _a, change = __rest(_a, ["clusterTime"]);
485
- yield (0, context_1.GenerateContext)({
486
- args: [change],
487
- app,
488
- rules: state_1.StateManager.select("rules"),
489
- user: {}, // TODO add from?
490
- currentFunction: triggerHandler,
491
- functionsList,
492
- services
606
+ emitTriggerEvent({
607
+ status: 'fired',
608
+ triggerName,
609
+ triggerType,
610
+ functionName,
611
+ meta: {
612
+ database: config.database || constants_1.DB_NAME,
613
+ collection: config.collection,
614
+ operationTypes: normalizedOperations,
615
+ isAutoTrigger: !!config.isAutoTrigger
616
+ }
493
617
  });
618
+ try {
619
+ yield (0, context_1.GenerateContext)({
620
+ args: [change],
621
+ app,
622
+ rules: state_1.StateManager.select("rules"),
623
+ user: {}, // TODO add from?
624
+ currentFunction: triggerHandler,
625
+ functionName,
626
+ functionsList,
627
+ services
628
+ });
629
+ }
630
+ catch (error) {
631
+ emitTriggerEvent({
632
+ status: 'error',
633
+ triggerName,
634
+ triggerType,
635
+ functionName,
636
+ meta: {
637
+ database: config.database || constants_1.DB_NAME,
638
+ collection: config.collection,
639
+ operationTypes: normalizedOperations,
640
+ isAutoTrigger: !!config.isAutoTrigger
641
+ },
642
+ error
643
+ });
644
+ }
494
645
  });
495
646
  });
496
647
  registerOnClose(app, () => __awaiter(void 0, void 0, void 0, function* () {
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/monitoring/plugin.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAgC,MAAM,SAAS,CAAA;AAE5E,OAAO,oBAAoB,CAAA;AA2J3B,QAAA,MAAM,sBAAsB,QACrB,eAAe,SACd;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,kBAwXH,CAAA;AAE1B,eAAe,sBAAsB,CAAA"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/monitoring/plugin.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAgC,MAAM,SAAS,CAAA;AAE5E,OAAO,oBAAoB,CAAA;AA0B3B,QAAA,MAAM,sBAAsB,QACrB,eAAe,SACd;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,kBA4UH,CAAA;AAE1B,eAAe,sBAAsB,CAAA"}