@flowerforce/flowerbase 1.4.1 → 1.4.2-beta.1

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.
@@ -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;AAoChD;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,EAAE,kBAoJjC,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,kBAwJjC,CAAA"}
@@ -38,6 +38,14 @@ const formatFunctionExecutionError = (error) => {
38
38
  const name = typeof (err === null || err === void 0 ? void 0 : err.name) === 'string' ? err.name : 'Error';
39
39
  return JSON.stringify({ message, name });
40
40
  };
41
+ const isReturnedError = (value) => {
42
+ if (value instanceof Error)
43
+ return true;
44
+ if (!value || typeof value !== 'object')
45
+ return false;
46
+ const candidate = value;
47
+ return typeof candidate.message === 'string' && typeof candidate.name === 'string';
48
+ };
41
49
  /**
42
50
  * > Creates a pre handler for every query
43
51
  * @param app -> the fastify instance
@@ -96,6 +104,10 @@ const functionsController = (app_1, _a) => __awaiter(void 0, [app_1, _a], void 0
96
104
  functionsList,
97
105
  services: services_1.services
98
106
  });
107
+ if (isReturnedError(result)) {
108
+ res.type('application/json');
109
+ return JSON.stringify({ message: result.message, name: result.name });
110
+ }
99
111
  res.type('application/json');
100
112
  return JSON.stringify(result);
101
113
  }
@@ -22,7 +22,7 @@ type Config = {
22
22
  isAutoTrigger?: boolean;
23
23
  match: Record<string, unknown>;
24
24
  operation_types: string[];
25
- operation_type?: 'CREATE' | 'DELETE';
25
+ operation_type?: 'CREATE' | 'DELETE' | 'LOGOUT';
26
26
  project: Record<string, unknown>;
27
27
  service_name: string;
28
28
  skip_catchup_events: boolean;
@@ -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,CAAA;IACpC,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,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 +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;AA8VD,eAAO,MAAM,gBAAgB;0EA1U1B,aAAa;yEAqRb,aAAa;+EA3Pb,aAAa;CAoTf,CAAA"}
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;AAuYD,eAAO,MAAM,gBAAgB;0EAnX1B,aAAa;yEA8Tb,aAAa;+EAnSb,aAAa;CA4Vf,CAAA"}
@@ -120,6 +120,7 @@ const handleCronTrigger = (_a) => __awaiter(void 0, [_a], void 0, function* ({ c
120
120
  const mapOpInverse = {
121
121
  CREATE: ['insert', 'update', 'replace'],
122
122
  DELETE: ['delete'],
123
+ LOGOUT: ['update'],
123
124
  };
124
125
  const handleAuthenticationTrigger = (_a) => __awaiter(void 0, [_a], void 0, function* ({ config, triggerHandler, functionsList, services, app }) {
125
126
  var _b;
@@ -150,7 +151,7 @@ const handleAuthenticationTrigger = (_a) => __awaiter(void 0, [_a], void 0, func
150
151
  });
151
152
  changeStream.on('change', function (change) {
152
153
  return __awaiter(this, void 0, void 0, function* () {
153
- var _a, _b;
154
+ var _a;
154
155
  const operationType = change['operationType'];
155
156
  const documentKey = change['documentKey'];
156
157
  const fullDocument = change['fullDocument'];
@@ -159,11 +160,13 @@ const handleAuthenticationTrigger = (_a) => __awaiter(void 0, [_a], void 0, func
159
160
  return;
160
161
  }
161
162
  const updateDescription = change['updateDescription'];
162
- const updatedStatus = (_a = updateDescription === null || updateDescription === void 0 ? void 0 : updateDescription.updatedFields) === null || _a === void 0 ? void 0 : _a.status;
163
+ const updatedFields = updateDescription === null || updateDescription === void 0 ? void 0 : updateDescription.updatedFields;
164
+ const updatedStatus = updatedFields === null || updatedFields === void 0 ? void 0 : updatedFields.status;
163
165
  const isInsert = operationType === 'insert';
164
166
  const isUpdate = operationType === 'update';
165
167
  const isReplace = operationType === 'replace';
166
168
  const isDelete = operationType === 'delete';
169
+ const isLogoutUpdate = isUpdate && !!updatedFields && 'lastLogoutAt' in updatedFields;
167
170
  let confirmedCandidate = false;
168
171
  let confirmedDocument = fullDocument;
169
172
  const buildUserData = (document) => {
@@ -189,6 +192,44 @@ const handleAuthenticationTrigger = (_a) => __awaiter(void 0, [_a], void 0, func
189
192
  email: currentUser.email
190
193
  } });
191
194
  };
195
+ if (operation_type === 'LOGOUT') {
196
+ if (!isLogoutUpdate) {
197
+ return;
198
+ }
199
+ let logoutDocument = fullDocument !== null && fullDocument !== void 0 ? fullDocument : confirmedDocument;
200
+ if (!logoutDocument && (documentKey === null || documentKey === void 0 ? void 0 : documentKey._id)) {
201
+ logoutDocument = (yield collection.findOne({
202
+ _id: documentKey._id
203
+ }));
204
+ }
205
+ const userData = buildUserData(logoutDocument);
206
+ if (!userData) {
207
+ return;
208
+ }
209
+ const op = {
210
+ operationType: 'LOGOUT',
211
+ fullDocument,
212
+ fullDocumentBeforeChange,
213
+ documentKey,
214
+ updateDescription
215
+ };
216
+ try {
217
+ yield (0, context_1.GenerateContext)({
218
+ args: [Object.assign({ user: userData }, op)],
219
+ app,
220
+ rules: state_1.StateManager.select("rules"),
221
+ user: {}, // TODO from currentUser ??
222
+ currentFunction: triggerHandler,
223
+ functionsList,
224
+ services,
225
+ runAsSystem: true
226
+ });
227
+ }
228
+ catch (error) {
229
+ console.log("🚀 ~ handleAuthenticationTrigger ~ error:", error);
230
+ }
231
+ return;
232
+ }
192
233
  if (isDelete) {
193
234
  if (isAutoTrigger || operation_type !== 'DELETE') {
194
235
  return;
@@ -279,7 +320,7 @@ const handleAuthenticationTrigger = (_a) => __awaiter(void 0, [_a], void 0, func
279
320
  }, {
280
321
  returnDocument: 'after'
281
322
  });
282
- const document = (_b = updateResult === null || updateResult === void 0 ? void 0 : updateResult.value) !== null && _b !== void 0 ? _b : confirmedDocument;
323
+ const document = (_a = updateResult === null || updateResult === void 0 ? void 0 : updateResult.value) !== null && _a !== void 0 ? _a : confirmedDocument;
283
324
  if (!document) {
284
325
  return;
285
326
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/context/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AA0GnD;;;;;;;;;;GAUG;AACH,wBAAsB,eAAe,CAAC,EACpC,IAAI,EACJ,GAAG,EACH,KAAK,EACL,IAAI,EACJ,eAAe,EACf,aAAa,EACb,QAAQ,EACR,WAAW,EACX,eAAsB,EACtB,OAAO,EACP,OAAO,EACR,EAAE,qBAAqB,GAAG,OAAO,CAAC,OAAO,CAAC,CA8J1C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/context/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AA0GnD;;;;;;;;;;GAUG;AACH,wBAAsB,eAAe,CAAC,EACpC,IAAI,EACJ,GAAG,EACH,KAAK,EACL,IAAI,EACJ,eAAe,EACf,aAAa,EACb,QAAQ,EACR,WAAW,EACX,eAAsB,EACtB,OAAO,EACP,OAAO,EACR,EAAE,qBAAqB,GAAG,OAAO,CAAC,OAAO,CAAC,CAuJ1C"}
@@ -157,78 +157,66 @@ function GenerateContext(_a) {
157
157
  return (_e = getDefaultExport(moduleExports)) !== null && _e !== void 0 ? _e : getDefaultExport(contextExports);
158
158
  };
159
159
  const sandboxModule = { exports: {} };
160
- try {
161
- const entryFile = (_b = (_a = require.main) === null || _a === void 0 ? void 0 : _a.filename) !== null && _b !== void 0 ? _b : process.cwd();
162
- const customRequire = (0, node_module_1.createRequire)(entryFile);
163
- const vmContext = vm_1.default.createContext(Object.assign(Object.assign({}, contextData), { require: customRequire, exports: sandboxModule.exports, module: sandboxModule, __filename,
164
- __dirname, __fb_require: customRequire, __fb_filename: __filename, __fb_dirname: __dirname }));
165
- const vmModules = vm_1.default;
166
- const hasStaticImport = /\bimport\s+/.test(functionToRun.code);
167
- let usedVmModules = false;
168
- if (hasStaticImport && vmModules.SourceTextModule && vmModules.SyntheticModule) {
169
- try {
170
- const moduleCache = new Map();
171
- const loadModule = (specifier) => __awaiter(this, void 0, void 0, function* () {
172
- const importTarget = resolveImportTarget(specifier, customRequire);
173
- const cached = moduleCache.get(importTarget);
174
- if (cached)
175
- return cached;
176
- const namespace = yield dynamicImport(importTarget);
177
- const exportNames = Object.keys(namespace);
178
- if ('default' in namespace && !exportNames.includes('default')) {
179
- exportNames.push('default');
180
- }
181
- const syntheticModule = new vmModules.SyntheticModule(exportNames, function () {
182
- for (const name of exportNames) {
183
- this.setExport(name, namespace[name]);
184
- }
185
- }, { context: vmContext, identifier: importTarget });
186
- moduleCache.set(importTarget, syntheticModule);
187
- return syntheticModule;
188
- });
189
- const importModuleDynamically = ((specifier) => loadModule(specifier));
190
- const sourceModule = new vmModules.SourceTextModule(wrapEsmModule(functionToRun.code), {
191
- context: vmContext,
192
- identifier: entryFile,
193
- initializeImportMeta: (meta) => {
194
- meta.url = (0, node_url_1.pathToFileURL)(entryFile).href;
195
- },
196
- importModuleDynamically
197
- });
198
- yield sourceModule.link(loadModule);
199
- yield sourceModule.evaluate();
200
- usedVmModules = true;
201
- }
202
- catch (error) {
203
- if (!shouldFallbackFromVmModules(error)) {
204
- throw error;
160
+ const entryFile = (_b = (_a = require.main) === null || _a === void 0 ? void 0 : _a.filename) !== null && _b !== void 0 ? _b : process.cwd();
161
+ const customRequire = (0, node_module_1.createRequire)(entryFile);
162
+ const vmContext = vm_1.default.createContext(Object.assign(Object.assign({}, contextData), { require: customRequire, exports: sandboxModule.exports, module: sandboxModule, __filename,
163
+ __dirname, __fb_require: customRequire, __fb_filename: __filename, __fb_dirname: __dirname }));
164
+ const vmModules = vm_1.default;
165
+ const hasStaticImport = /\bimport\s+/.test(functionToRun.code);
166
+ let usedVmModules = false;
167
+ if (hasStaticImport && vmModules.SourceTextModule && vmModules.SyntheticModule) {
168
+ try {
169
+ const moduleCache = new Map();
170
+ const loadModule = (specifier) => __awaiter(this, void 0, void 0, function* () {
171
+ const importTarget = resolveImportTarget(specifier, customRequire);
172
+ const cached = moduleCache.get(importTarget);
173
+ if (cached)
174
+ return cached;
175
+ const namespace = yield dynamicImport(importTarget);
176
+ const exportNames = Object.keys(namespace);
177
+ if ('default' in namespace && !exportNames.includes('default')) {
178
+ exportNames.push('default');
205
179
  }
206
- }
180
+ const syntheticModule = new vmModules.SyntheticModule(exportNames, function () {
181
+ for (const name of exportNames) {
182
+ this.setExport(name, namespace[name]);
183
+ }
184
+ }, { context: vmContext, identifier: importTarget });
185
+ moduleCache.set(importTarget, syntheticModule);
186
+ return syntheticModule;
187
+ });
188
+ const importModuleDynamically = ((specifier) => loadModule(specifier));
189
+ const sourceModule = new vmModules.SourceTextModule(wrapEsmModule(functionToRun.code), {
190
+ context: vmContext,
191
+ identifier: entryFile,
192
+ initializeImportMeta: (meta) => {
193
+ meta.url = (0, node_url_1.pathToFileURL)(entryFile).href;
194
+ },
195
+ importModuleDynamically
196
+ });
197
+ yield sourceModule.link(loadModule);
198
+ yield sourceModule.evaluate();
199
+ usedVmModules = true;
207
200
  }
208
- if (!usedVmModules) {
209
- const codeToRun = functionToRun.code.includes('import ')
210
- ? transformImportsToRequire(functionToRun.code)
211
- : functionToRun.code;
212
- vm_1.default.runInContext(codeToRun, vmContext);
201
+ catch (error) {
202
+ if (!shouldFallbackFromVmModules(error)) {
203
+ throw error;
204
+ }
213
205
  }
214
- sandboxModule.exports = (_c = resolveExport(vmContext)) !== null && _c !== void 0 ? _c : sandboxModule.exports;
215
206
  }
216
- catch (error) {
217
- console.error(error);
218
- throw error;
207
+ if (!usedVmModules) {
208
+ const codeToRun = functionToRun.code.includes('import ')
209
+ ? transformImportsToRequire(functionToRun.code)
210
+ : functionToRun.code;
211
+ vm_1.default.runInContext(codeToRun, vmContext);
219
212
  }
213
+ sandboxModule.exports = (_c = resolveExport(vmContext)) !== null && _c !== void 0 ? _c : sandboxModule.exports;
220
214
  if (deserializeArgs) {
221
215
  return yield sandboxModule.exports(...bson_1.EJSON.deserialize(args));
222
216
  }
223
217
  return yield sandboxModule.exports(...args);
224
218
  });
225
- try {
226
- const res = yield functionsQueue.add(run, enqueue);
227
- return res;
228
- }
229
- catch (error) {
230
- console.error(error);
231
- throw error;
232
- }
219
+ const res = yield functionsQueue.add(run, enqueue);
220
+ return res;
233
221
  });
234
222
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowerforce/flowerbase",
3
- "version": "1.4.1",
3
+ "version": "1.4.2-beta.1",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -41,6 +41,13 @@ const formatFunctionExecutionError = (error: unknown) => {
41
41
  return JSON.stringify({ message, name })
42
42
  }
43
43
 
44
+ const isReturnedError = (value: unknown): value is { message: string; name: string } => {
45
+ if (value instanceof Error) return true
46
+ if (!value || typeof value !== 'object') return false
47
+ const candidate = value as { message?: unknown; name?: unknown }
48
+ return typeof candidate.message === 'string' && typeof candidate.name === 'string'
49
+ }
50
+
44
51
  /**
45
52
  * > Creates a pre handler for every query
46
53
  * @param app -> the fastify instance
@@ -122,6 +129,10 @@ export const functionsController: FunctionController = async (
122
129
  functionsList,
123
130
  services
124
131
  })
132
+ if (isReturnedError(result)) {
133
+ res.type('application/json')
134
+ return JSON.stringify({ message: result.message, name: result.name })
135
+ }
125
136
  res.type('application/json')
126
137
  return JSON.stringify(result)
127
138
  } catch (error) {
@@ -24,7 +24,7 @@ type Config = {
24
24
  isAutoTrigger?: boolean
25
25
  match: Record<string, unknown>
26
26
  operation_types: string[]
27
- operation_type?: 'CREATE' | 'DELETE'
27
+ operation_type?: 'CREATE' | 'DELETE' | 'LOGOUT'
28
28
  project: Record<string, unknown>
29
29
  service_name: string
30
30
  skip_catchup_events: boolean
@@ -106,6 +106,7 @@ const handleCronTrigger = async ({
106
106
  const mapOpInverse = {
107
107
  CREATE: ['insert', 'update', 'replace'],
108
108
  DELETE: ['delete'],
109
+ LOGOUT: ['update'],
109
110
  }
110
111
 
111
112
  const handleAuthenticationTrigger = async ({
@@ -161,11 +162,13 @@ const handleAuthenticationTrigger = async ({
161
162
  const updateDescription = change[
162
163
  'updateDescription' as keyof typeof change
163
164
  ] as { updatedFields?: Record<string, unknown> } | undefined
164
- const updatedStatus = updateDescription?.updatedFields?.status
165
+ const updatedFields = updateDescription?.updatedFields
166
+ const updatedStatus = updatedFields?.status
165
167
  const isInsert = operationType === 'insert'
166
168
  const isUpdate = operationType === 'update'
167
169
  const isReplace = operationType === 'replace'
168
170
  const isDelete = operationType === 'delete'
171
+ const isLogoutUpdate = isUpdate && !!updatedFields && 'lastLogoutAt' in updatedFields
169
172
 
170
173
  let confirmedCandidate = false
171
174
  let confirmedDocument =
@@ -199,6 +202,44 @@ const handleAuthenticationTrigger = async ({
199
202
  }
200
203
  }
201
204
 
205
+ if (operation_type === 'LOGOUT') {
206
+ if (!isLogoutUpdate) {
207
+ return
208
+ }
209
+ let logoutDocument = fullDocument ?? confirmedDocument
210
+ if (!logoutDocument && documentKey?._id) {
211
+ logoutDocument = await collection.findOne({
212
+ _id: documentKey._id
213
+ }) as Record<string, unknown> | null
214
+ }
215
+ const userData = buildUserData(logoutDocument)
216
+ if (!userData) {
217
+ return
218
+ }
219
+ const op = {
220
+ operationType: 'LOGOUT',
221
+ fullDocument,
222
+ fullDocumentBeforeChange,
223
+ documentKey,
224
+ updateDescription
225
+ }
226
+ try {
227
+ await GenerateContext({
228
+ args: [{ user: userData, ...op }],
229
+ app,
230
+ rules: StateManager.select("rules"),
231
+ user: {}, // TODO from currentUser ??
232
+ currentFunction: triggerHandler,
233
+ functionsList,
234
+ services,
235
+ runAsSystem: true
236
+ })
237
+ } catch (error) {
238
+ console.log("🚀 ~ handleAuthenticationTrigger ~ error:", error)
239
+ }
240
+ return
241
+ }
242
+
202
243
  if (isDelete) {
203
244
  if (isAutoTrigger || operation_type !== 'DELETE') {
204
245
  return
@@ -186,97 +186,93 @@ export async function GenerateContext({
186
186
 
187
187
  const sandboxModule: SandboxModule = { exports: {} }
188
188
 
189
- try {
190
- const entryFile = require.main?.filename ?? process.cwd()
191
- const customRequire = createRequire(entryFile)
192
-
193
- const vmContext: SandboxContext = vm.createContext({
194
- ...contextData,
195
- require: customRequire,
196
- exports: sandboxModule.exports,
197
- module: sandboxModule,
198
- __filename,
199
- __dirname,
200
- __fb_require: customRequire,
201
- __fb_filename: __filename,
202
- __fb_dirname: __dirname
203
- }) as SandboxContext
204
-
205
- const vmModules = vm as typeof vm & {
206
- SourceTextModule?: typeof vm.SourceTextModule
207
- SyntheticModule?: typeof vm.SyntheticModule
208
- }
209
- const hasStaticImport = /\bimport\s+/.test(functionToRun.code)
210
- let usedVmModules = false
211
-
212
- if (hasStaticImport && vmModules.SourceTextModule && vmModules.SyntheticModule) {
213
- try {
214
- const moduleCache = new Map<string, vm.Module>()
215
-
216
- const loadModule = async (specifier: string): Promise<vm.Module> => {
217
- const importTarget = resolveImportTarget(specifier, customRequire)
218
- const cached = moduleCache.get(importTarget)
219
- if (cached) return cached
220
-
221
- const namespace = await dynamicImport(importTarget)
222
- const exportNames = Object.keys(namespace)
223
- if ('default' in namespace && !exportNames.includes('default')) {
224
- exportNames.push('default')
225
- }
226
-
227
- const syntheticModule = new vmModules.SyntheticModule(
228
- exportNames,
229
- function () {
230
- for (const name of exportNames) {
231
- this.setExport(name, namespace[name])
232
- }
233
- },
234
- { context: vmContext, identifier: importTarget }
235
- )
236
-
237
- moduleCache.set(importTarget, syntheticModule)
238
- return syntheticModule
189
+ const entryFile = require.main?.filename ?? process.cwd()
190
+ const customRequire = createRequire(entryFile)
191
+
192
+ const vmContext: SandboxContext = vm.createContext({
193
+ ...contextData,
194
+ require: customRequire,
195
+ exports: sandboxModule.exports,
196
+ module: sandboxModule,
197
+ __filename,
198
+ __dirname,
199
+ __fb_require: customRequire,
200
+ __fb_filename: __filename,
201
+ __fb_dirname: __dirname
202
+ }) as SandboxContext
203
+
204
+ const vmModules = vm as typeof vm & {
205
+ SourceTextModule?: typeof vm.SourceTextModule
206
+ SyntheticModule?: typeof vm.SyntheticModule
207
+ }
208
+ const hasStaticImport = /\bimport\s+/.test(functionToRun.code)
209
+ let usedVmModules = false
210
+
211
+ if (hasStaticImport && vmModules.SourceTextModule && vmModules.SyntheticModule) {
212
+ try {
213
+ const moduleCache = new Map<string, vm.Module>()
214
+
215
+ const loadModule = async (specifier: string): Promise<vm.Module> => {
216
+ const importTarget = resolveImportTarget(specifier, customRequire)
217
+ const cached = moduleCache.get(importTarget)
218
+ if (cached) return cached
219
+
220
+ const namespace = await dynamicImport(importTarget)
221
+ const exportNames = Object.keys(namespace)
222
+ if ('default' in namespace && !exportNames.includes('default')) {
223
+ exportNames.push('default')
239
224
  }
240
225
 
241
- const importModuleDynamically =
242
- ((specifier: string) => loadModule(specifier) as unknown as vm.Module) as unknown as
243
- vm.SourceTextModuleOptions['importModuleDynamically']
244
-
245
- const sourceModule = new vmModules.SourceTextModule(
246
- wrapEsmModule(functionToRun.code),
247
- {
248
- context: vmContext,
249
- identifier: entryFile,
250
- initializeImportMeta: (meta) => {
251
- meta.url = pathToFileURL(entryFile).href
252
- },
253
- importModuleDynamically
254
- }
226
+ const syntheticModule = new vmModules.SyntheticModule(
227
+ exportNames,
228
+ function () {
229
+ for (const name of exportNames) {
230
+ this.setExport(name, namespace[name])
231
+ }
232
+ },
233
+ { context: vmContext, identifier: importTarget }
255
234
  )
256
235
 
257
- await sourceModule.link(loadModule)
258
- await sourceModule.evaluate()
259
- usedVmModules = true
260
- } catch (error) {
261
- if (!shouldFallbackFromVmModules(error)) {
262
- throw error
263
- }
236
+ moduleCache.set(importTarget, syntheticModule)
237
+ return syntheticModule
264
238
  }
265
- }
266
239
 
267
- if (!usedVmModules) {
268
- const codeToRun = functionToRun.code.includes('import ')
269
- ? transformImportsToRequire(functionToRun.code)
270
- : functionToRun.code
271
- vm.runInContext(codeToRun, vmContext)
240
+ const importModuleDynamically =
241
+ ((specifier: string) => loadModule(specifier) as unknown as vm.Module) as unknown as
242
+ vm.SourceTextModuleOptions['importModuleDynamically']
243
+
244
+ const sourceModule = new vmModules.SourceTextModule(
245
+ wrapEsmModule(functionToRun.code),
246
+ {
247
+ context: vmContext,
248
+ identifier: entryFile,
249
+ initializeImportMeta: (meta) => {
250
+ meta.url = pathToFileURL(entryFile).href
251
+ },
252
+ importModuleDynamically
253
+ }
254
+ )
255
+
256
+ await sourceModule.link(loadModule)
257
+ await sourceModule.evaluate()
258
+ usedVmModules = true
259
+ } catch (error) {
260
+ if (!shouldFallbackFromVmModules(error)) {
261
+ throw error
262
+ }
272
263
  }
264
+ }
273
265
 
274
- sandboxModule.exports = resolveExport(vmContext) ?? sandboxModule.exports
275
- } catch (error) {
276
- console.error(error)
277
- throw error
266
+ if (!usedVmModules) {
267
+ const codeToRun = functionToRun.code.includes('import ')
268
+ ? transformImportsToRequire(functionToRun.code)
269
+ : functionToRun.code
270
+ vm.runInContext(codeToRun, vmContext)
278
271
  }
279
272
 
273
+ sandboxModule.exports = resolveExport(vmContext) ?? sandboxModule.exports
274
+
275
+
280
276
  if (deserializeArgs) {
281
277
  return await (sandboxModule.exports as ExportedFunction)(
282
278
  ...EJSON.deserialize(args)
@@ -285,11 +281,8 @@ export async function GenerateContext({
285
281
 
286
282
  return await (sandboxModule.exports as ExportedFunction)(...args)
287
283
  }
288
- try {
289
- const res = await functionsQueue.add(run, enqueue)
290
- return res
291
- } catch (error) {
292
- console.error(error)
293
- throw error
294
- }
284
+
285
+ const res = await functionsQueue.add(run, enqueue)
286
+ return res
287
+
295
288
  }