@flowerforce/flowerbase 1.8.4-beta.1 → 1.8.4-beta.3

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.
@@ -10,6 +10,7 @@ export interface FunctionConfig {
10
10
  }
11
11
  export type Function = Omit<FunctionConfig, 'name'> & {
12
12
  code: string;
13
+ sourcePath?: string;
13
14
  };
14
15
  export type Functions = Record<string, Function>;
15
16
  export type RegisterFunctionsParams = {
@@ -1 +1 @@
1
- {"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../../src/features/functions/interface.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAClC,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAA;AACzE,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAE1C,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAC3B;AAED,MAAM,MAAM,QAAQ,GAAG,IAAI,CAAC,cAAc,EAAE,MAAM,CAAC,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAEtE,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;AAEhD,MAAM,MAAM,uBAAuB,GAAG;IACpC,GAAG,EAAE,eAAe,CAAA;IACpB,aAAa,EAAE,SAAS,CAAA;IACxB,SAAS,EAAE,KAAK,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,aAAa,EAAE,UAAU,CAAC,oBAAoB,CAAC,CAAC,MAAM,UAAU,CAAC,oBAAoB,CAAC,CAAC,CAAA;IACvF,KAAK,EAAE,UAAU,CAAC,oBAAoB,CAAC,CAAA;IACvC,MAAM,EAAE,QAAQ,CAAA;IAChB,MAAM,CAAC,EAAE,QAAQ,CAAA;IACjB,UAAU,CAAC,EAAE,QAAQ,CAAA;IACrB,OAAO,CAAC,EAAE,QAAQ,CAAA;IAClB,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,QAAQ,EAAE,QAAQ,CAAA;IAClB,SAAS,EAAE,QAAQ,EAAE,CAAA;IACrB,QAAQ,EAAE,QAAQ,EAAE,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,CAAA;AAED,KAAK,0BAA0B,GAAG;IAChC,aAAa,EAAE,SAAS,CAAA;IACxB,KAAK,EAAE,KAAK,CAAA;CACb,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG,CAC/B,GAAG,EAAE,eAAe,EACpB,EAAE,aAAa,EAAE,KAAK,EAAE,EAAE,0BAA0B,KACjD,OAAO,CAAC,IAAI,CAAC,CAAA"}
1
+ {"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../../src/features/functions/interface.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAClC,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAA;AACzE,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAE1C,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAC3B;AAED,MAAM,MAAM,QAAQ,GAAG,IAAI,CAAC,cAAc,EAAE,MAAM,CAAC,GAAG;IACpD,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;AAEhD,MAAM,MAAM,uBAAuB,GAAG;IACpC,GAAG,EAAE,eAAe,CAAA;IACpB,aAAa,EAAE,SAAS,CAAA;IACxB,SAAS,EAAE,KAAK,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,aAAa,EAAE,UAAU,CAAC,oBAAoB,CAAC,CAAC,MAAM,UAAU,CAAC,oBAAoB,CAAC,CAAC,CAAA;IACvF,KAAK,EAAE,UAAU,CAAC,oBAAoB,CAAC,CAAA;IACvC,MAAM,EAAE,QAAQ,CAAA;IAChB,MAAM,CAAC,EAAE,QAAQ,CAAA;IACjB,UAAU,CAAC,EAAE,QAAQ,CAAA;IACrB,OAAO,CAAC,EAAE,QAAQ,CAAA;IAClB,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,QAAQ,EAAE,QAAQ,CAAA;IAClB,SAAS,EAAE,QAAQ,EAAE,CAAA;IACrB,QAAQ,EAAE,QAAQ,EAAE,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,CAAA;AAED,KAAK,0BAA0B,GAAG;IAChC,aAAa,EAAE,SAAS,CAAA;IACxB,KAAK,EAAE,KAAK,CAAA;CACb,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG,CAC/B,GAAG,EAAE,eAAe,EACpB,EAAE,aAAa,EAAE,KAAK,EAAE,EAAE,0BAA0B,KACjD,OAAO,CAAC,IAAI,CAAC,CAAA"}
@@ -46,7 +46,7 @@ const loadFunctions = (...args_1) => __awaiter(void 0, [...args_1], void 0, func
46
46
  throw new Error(`File ${name}.js or ${name}.ts not found`);
47
47
  }
48
48
  code = fs_1.default.readFileSync(fnPath, 'utf-8');
49
- acc[name] = Object.assign({ code }, opts);
49
+ acc[name] = Object.assign({ code, sourcePath: fnPath }, opts);
50
50
  return acc;
51
51
  }, {});
52
52
  return functions;
@@ -77,7 +77,7 @@ export declare const getErrorDetails: (error: unknown) => {
77
77
  };
78
78
  export declare const pickHeaders: (headers: FastifyRequest["headers"]) => unknown;
79
79
  export declare const createEventStore: (maxAgeMs: number, maxEvents: number) => EventStore;
80
- export declare const classifyRequest: (url: string) => "function" | "auth" | "http_endpoint" | "trigger" | "api" | "http";
80
+ export declare const classifyRequest: (url: string) => "function" | "api" | "auth" | "http_endpoint" | "trigger" | "http";
81
81
  export declare const createEventId: () => string;
82
82
  export declare const buildRulesMeta: (meta?: MonitMeta) => {
83
83
  collection: string;
@@ -490,7 +490,7 @@ const getOperators = (mongo, { rules, dbName, collName, user, run_as_system, mon
490
490
  emitMongoEvent('findOne');
491
491
  return null;
492
492
  }
493
- const winningRole = (0, utils_2.getWinningRole)(result, user, roles);
493
+ const winningRole = yield (0, utils_2.getWinningRoleAsync)(result, user, roles);
494
494
  logDebug('findOne winningRole', {
495
495
  collection: collName,
496
496
  winningRoleName: (_a = winningRole === null || winningRole === void 0 ? void 0 : winningRole.name) !== null && _a !== void 0 ? _a : null,
@@ -546,7 +546,7 @@ const getOperators = (mongo, { rules, dbName, collName, user, run_as_system, mon
546
546
  const formattedQuery = (0, utils_3.getFormattedQuery)(filters, query, user);
547
547
  // Retrieve the document to check permissions before deleting
548
548
  const result = yield collection.findOne(buildAndQuery(formattedQuery));
549
- const winningRole = (0, utils_2.getWinningRole)(result, user, roles);
549
+ const winningRole = yield (0, utils_2.getWinningRoleAsync)(result, user, roles);
550
550
  logDebug('delete winningRole', {
551
551
  collection: collName,
552
552
  userId: getUserId(user),
@@ -600,7 +600,7 @@ const getOperators = (mongo, { rules, dbName, collName, user, run_as_system, mon
600
600
  try {
601
601
  if (!run_as_system) {
602
602
  (0, utils_3.checkDenyOperation)(normalizedRules, collection.collectionName, model_1.CRUD_OPERATIONS.CREATE);
603
- const winningRole = (0, utils_2.getWinningRole)(data, user, roles);
603
+ const winningRole = yield (0, utils_2.getWinningRoleAsync)(data, user, roles);
604
604
  const { status, document } = winningRole
605
605
  ? yield (0, machines_1.checkValidation)(winningRole, {
606
606
  type: 'insert',
@@ -673,7 +673,7 @@ const getOperators = (mongo, { rules, dbName, collName, user, run_as_system, mon
673
673
  }
674
674
  throw new Error('Update not permitted');
675
675
  }
676
- const winningRole = (0, utils_2.getWinningRole)(result, user, roles);
676
+ const winningRole = yield (0, utils_2.getWinningRoleAsync)(result, user, roles);
677
677
  // Check if the update data contains MongoDB update operators (e.g., $set, $inc)
678
678
  const updatedPaths = getUpdatedPaths(normalizedData);
679
679
  const docToCheck = applyDocumentUpdateOperators(result, normalizedData);
@@ -756,7 +756,7 @@ const getOperators = (mongo, { rules, dbName, collName, user, run_as_system, mon
756
756
  : [applyDocumentUpdateOperators(currentDoc, normalizedData)];
757
757
  docToCheck = computedDoc;
758
758
  }
759
- const winningRole = (0, utils_2.getWinningRole)(docToCheck, user, roles);
759
+ const winningRole = yield (0, utils_2.getWinningRoleAsync)(docToCheck, user, roles);
760
760
  const { status, document } = winningRole
761
761
  ? yield (0, machines_1.checkValidation)(winningRole, {
762
762
  type: validationType,
@@ -776,7 +776,7 @@ const getOperators = (mongo, { rules, dbName, collName, user, run_as_system, mon
776
776
  emitMongoEvent('findOneAndUpdate');
777
777
  return updateResult;
778
778
  }
779
- const readRole = (0, utils_2.getWinningRole)(updateResult, user, roles);
779
+ const readRole = yield (0, utils_2.getWinningRoleAsync)(updateResult, user, roles);
780
780
  const readResult = readRole
781
781
  ? yield (0, machines_1.checkValidation)(readRole, {
782
782
  type: 'read',
@@ -844,7 +844,7 @@ const getOperators = (mongo, { rules, dbName, collName, user, run_as_system, mon
844
844
  const response = yield originalToArray();
845
845
  const filteredResponse = yield Promise.all(response.map((currentDoc) => __awaiter(void 0, void 0, void 0, function* () {
846
846
  var _a;
847
- const winningRole = (0, utils_2.getWinningRole)(currentDoc, user, roles);
847
+ const winningRole = yield (0, utils_2.getWinningRoleAsync)(currentDoc, user, roles);
848
848
  logDebug('find winningRole', {
849
849
  collection: collName,
850
850
  userId: getUserId(user),
@@ -976,7 +976,7 @@ const getOperators = (mongo, { rules, dbName, collName, user, run_as_system, mon
976
976
  const isValidChange = (change) => __awaiter(void 0, void 0, void 0, function* () {
977
977
  const { fullDocument, updateDescription } = change;
978
978
  const hasFullDocument = !!fullDocument;
979
- const winningRole = (0, utils_2.getWinningRole)(fullDocument, user, roles);
979
+ const winningRole = yield (0, utils_2.getWinningRoleAsync)(fullDocument, user, roles);
980
980
  const fullDocumentValidation = winningRole
981
981
  ? yield (0, machines_1.checkValidation)(winningRole, {
982
982
  type: 'read',
@@ -1088,7 +1088,7 @@ const getOperators = (mongo, { rules, dbName, collName, user, run_as_system, mon
1088
1088
  (0, utils_3.checkDenyOperation)(normalizedRules, collection.collectionName, model_1.CRUD_OPERATIONS.CREATE);
1089
1089
  // Validate each document against user's roles
1090
1090
  const filteredItems = yield Promise.all(documents.map((currentDoc) => __awaiter(void 0, void 0, void 0, function* () {
1091
- const winningRole = (0, utils_2.getWinningRole)(currentDoc, user, roles);
1091
+ const winningRole = yield (0, utils_2.getWinningRoleAsync)(currentDoc, user, roles);
1092
1092
  const { status, document } = winningRole
1093
1093
  ? yield (0, machines_1.checkValidation)(winningRole, {
1094
1094
  type: 'insert',
@@ -1133,7 +1133,7 @@ const getOperators = (mongo, { rules, dbName, collName, user, run_as_system, mon
1133
1133
  const updatedPaths = getUpdatedPaths(normalizedData);
1134
1134
  const docsToCheck = result.map((currentDoc) => applyDocumentUpdateOperators(currentDoc, normalizedData));
1135
1135
  const filteredItems = yield Promise.all(docsToCheck.map((currentDoc, index) => __awaiter(void 0, void 0, void 0, function* () {
1136
- const winningRole = (0, utils_2.getWinningRole)(currentDoc, user, roles);
1136
+ const winningRole = yield (0, utils_2.getWinningRoleAsync)(currentDoc, user, roles);
1137
1137
  const { status, document } = winningRole
1138
1138
  ? yield (0, machines_1.checkValidation)(winningRole, {
1139
1139
  type: 'write',
@@ -1187,7 +1187,7 @@ const getOperators = (mongo, { rules, dbName, collName, user, run_as_system, mon
1187
1187
  const data = yield collection.find({ $and: formattedQuery }).toArray();
1188
1188
  // Filter and validate each document based on user's roles
1189
1189
  const filteredItems = yield Promise.all(data.map((currentDoc) => __awaiter(void 0, void 0, void 0, function* () {
1190
- const winningRole = (0, utils_2.getWinningRole)(currentDoc, user, roles);
1190
+ const winningRole = yield (0, utils_2.getWinningRoleAsync)(currentDoc, user, roles);
1191
1191
  const { status, document } = winningRole
1192
1192
  ? yield (0, machines_1.checkValidation)(winningRole, {
1193
1193
  type: 'delete',
@@ -35,11 +35,11 @@ export declare const generateContextData: ({ user, services, app, rules, current
35
35
  request: {
36
36
  remoteIPAddress: string | undefined;
37
37
  id?: string | undefined;
38
- host?: string | undefined;
39
- method?: string | undefined;
40
- url?: string | undefined;
41
38
  ips?: string[];
39
+ host?: string | undefined;
42
40
  hostname?: string | undefined;
41
+ url?: string | undefined;
42
+ method?: string | undefined;
43
43
  ip?: string | undefined;
44
44
  };
45
45
  user: unknown;
@@ -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;AA4JnD;;;;;;;;;;GAUG;AACH,wBAAsB,eAAe,CAAC,EACpC,IAAI,EACJ,GAAG,EACH,KAAK,EACL,IAAI,EACJ,eAAe,EACf,aAAa,EACb,QAAQ,EACR,YAAY,EACZ,WAAW,EACX,eAAsB,EACtB,OAAO,EACP,OAAO,EACR,EAAE,qBAAqB,GAAG,OAAO,CAAC,OAAO,CAAC,CA2G1C;AAED,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EACJ,GAAG,EACH,KAAK,EACL,IAAI,EACJ,eAAe,EACf,aAAa,EACb,QAAQ,EACR,YAAY,EACZ,WAAW,EACX,eAAsB,EACtB,OAAO,EACR,EAAE,qBAAqB,GAAG,OAAO,CA6BjC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/context/index.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AA8RnD;;;;;;;;;;GAUG;AACH,wBAAsB,eAAe,CAAC,EACpC,IAAI,EACJ,GAAG,EACH,KAAK,EACL,IAAI,EACJ,eAAe,EACf,aAAa,EACb,QAAQ,EACR,YAAY,EACZ,WAAW,EACX,eAAsB,EACtB,OAAO,EACP,OAAO,EACR,EAAE,qBAAqB,GAAG,OAAO,CAAC,OAAO,CAAC,CA2G1C;AAED,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EACJ,GAAG,EACH,KAAK,EACL,IAAI,EACJ,eAAe,EACf,aAAa,EACb,QAAQ,EACR,YAAY,EACZ,WAAW,EACX,eAAsB,EACtB,OAAO,EACR,EAAE,qBAAqB,GAAG,OAAO,CA0BjC"}
@@ -14,6 +14,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.GenerateContext = GenerateContext;
16
16
  exports.GenerateContextSync = GenerateContextSync;
17
+ const node_fs_1 = __importDefault(require("node:fs"));
17
18
  const node_module_1 = require("node:module");
18
19
  const node_path_1 = __importDefault(require("node:path"));
19
20
  const node_url_1 = require("node:url");
@@ -89,6 +90,27 @@ const wrapEsmModule = (code) => {
89
90
  ].join('\n');
90
91
  return `${prelude}\n${code}\n${trailer}`;
91
92
  };
93
+ const transpileSandboxModule = (code) => {
94
+ const exportedNames = [];
95
+ let transformed = code.includes('import ')
96
+ ? transformImportsToRequire(code)
97
+ : code;
98
+ transformed = transformed.replace(/^\s*export\s+function\s+([A-Za-z_$][\w$]*)\s*\(/gm, (_match, name) => {
99
+ exportedNames.push(name);
100
+ return `function ${name}(`;
101
+ });
102
+ transformed = transformed.replace(/^\s*export\s+(const|let|var|class)\s+([A-Za-z_$][\w$]*)/gm, (_match, kind, name) => {
103
+ exportedNames.push(name);
104
+ return `${kind} ${name}`;
105
+ });
106
+ transformed = transformed.replace(/^\s*export\s+default\s+/gm, 'module.exports = ');
107
+ if (exportedNames.length === 0) {
108
+ return transformed;
109
+ }
110
+ return `${transformed}\n${[...new Set(exportedNames)]
111
+ .map((name) => `exports.${name} = ${name}`)
112
+ .join('\n')}`;
113
+ };
92
114
  const resolveImportTarget = (specifier, customRequire) => {
93
115
  try {
94
116
  const resolved = customRequire.resolve(specifier);
@@ -109,6 +131,53 @@ const shouldFallbackFromVmModules = (error) => {
109
131
  const code = error.code;
110
132
  return code === 'ERR_VM_MODULES_DISABLED' || code === 'ERR_VM_MODULES_NOT_SUPPORTED';
111
133
  };
134
+ const resolveModulePath = (specifier, parentFile) => {
135
+ const parentDir = node_path_1.default.dirname(parentFile);
136
+ const basePath = node_path_1.default.resolve(parentDir, specifier);
137
+ const candidates = [
138
+ basePath,
139
+ `${basePath}.js`,
140
+ `${basePath}.ts`,
141
+ node_path_1.default.join(basePath, 'index.js'),
142
+ node_path_1.default.join(basePath, 'index.ts')
143
+ ];
144
+ return candidates.find((candidate) => {
145
+ try {
146
+ return node_fs_1.default.statSync(candidate).isFile();
147
+ }
148
+ catch (_a) {
149
+ return false;
150
+ }
151
+ });
152
+ };
153
+ const executeSandboxModule = ({ code, contextData, filePath, moduleCache }) => {
154
+ var _a;
155
+ if (moduleCache.has(filePath)) {
156
+ return moduleCache.get(filePath);
157
+ }
158
+ const sandboxModule = { exports: {} };
159
+ moduleCache.set(filePath, sandboxModule.exports);
160
+ const baseRequire = (0, node_module_1.createRequire)(filePath);
161
+ const localRequire = ((specifier) => {
162
+ if (specifier.startsWith('.') || specifier.startsWith('/')) {
163
+ const resolvedPath = resolveModulePath(specifier, filePath);
164
+ if (resolvedPath) {
165
+ return executeSandboxModule({
166
+ code: node_fs_1.default.readFileSync(resolvedPath, 'utf-8'),
167
+ contextData,
168
+ filePath: resolvedPath,
169
+ moduleCache
170
+ });
171
+ }
172
+ }
173
+ return baseRequire(specifier);
174
+ });
175
+ const vmContext = vm_1.default.createContext(Object.assign(Object.assign({}, contextData), { require: localRequire, exports: sandboxModule.exports, module: sandboxModule, __filename: filePath, __dirname: node_path_1.default.dirname(filePath), __fb_require: localRequire, __fb_filename: filePath, __fb_dirname: node_path_1.default.dirname(filePath) }));
176
+ vm_1.default.runInContext(transpileSandboxModule(code), vmContext, { filename: filePath });
177
+ sandboxModule.exports = (_a = resolveExport(vmContext)) !== null && _a !== void 0 ? _a : sandboxModule.exports;
178
+ moduleCache.set(filePath, sandboxModule.exports);
179
+ return sandboxModule.exports;
180
+ };
112
181
  const isExportedFunction = (value) => typeof value === 'function';
113
182
  const getDefaultExport = (value) => {
114
183
  if (!value || typeof value !== 'object')
@@ -128,11 +197,25 @@ const resolveExport = (ctx) => {
128
197
  return contextExports;
129
198
  return (_e = getDefaultExport(moduleExports)) !== null && _e !== void 0 ? _e : getDefaultExport(contextExports);
130
199
  };
131
- const buildVmContext = (contextData) => {
132
- var _a, _b;
200
+ const buildVmContext = (contextData, currentFunction) => {
201
+ var _a, _b, _c;
133
202
  const sandboxModule = { exports: {} };
134
- const entryFile = (_b = (_a = require.main) === null || _a === void 0 ? void 0 : _a.filename) !== null && _b !== void 0 ? _b : process.cwd();
135
- const customRequire = (0, node_module_1.createRequire)(entryFile);
203
+ const entryFile = (_c = (_a = currentFunction === null || currentFunction === void 0 ? void 0 : currentFunction.sourcePath) !== null && _a !== void 0 ? _a : (_b = require.main) === null || _b === void 0 ? void 0 : _b.filename) !== null && _c !== void 0 ? _c : process.cwd();
204
+ const moduleCache = new Map();
205
+ const customRequire = ((specifier) => {
206
+ if ((specifier.startsWith('.') || specifier.startsWith('/')) && (currentFunction === null || currentFunction === void 0 ? void 0 : currentFunction.sourcePath)) {
207
+ const resolvedPath = resolveModulePath(specifier, currentFunction.sourcePath);
208
+ if (resolvedPath) {
209
+ return executeSandboxModule({
210
+ code: node_fs_1.default.readFileSync(resolvedPath, 'utf-8'),
211
+ contextData,
212
+ filePath: resolvedPath,
213
+ moduleCache
214
+ });
215
+ }
216
+ }
217
+ return (0, node_module_1.createRequire)(entryFile)(specifier);
218
+ });
136
219
  const vmContext = vm_1.default.createContext(Object.assign(Object.assign({}, contextData), { require: customRequire, exports: sandboxModule.exports, module: sandboxModule, __filename,
137
220
  __dirname, __fb_require: customRequire, __fb_filename: __filename, __fb_dirname: __dirname }));
138
221
  return { sandboxModule, entryFile, customRequire, vmContext };
@@ -169,7 +252,7 @@ function GenerateContext(_a) {
169
252
  GenerateContextSync,
170
253
  request
171
254
  });
172
- const { sandboxModule, entryFile, customRequire, vmContext } = buildVmContext(contextData);
255
+ const { sandboxModule, entryFile, customRequire, vmContext } = buildVmContext(contextData, functionToRun);
173
256
  const vmModules = vm_1.default;
174
257
  const hasStaticImport = /\bimport\s+/.test(functionToRun.code);
175
258
  let usedVmModules = false;
@@ -214,10 +297,7 @@ function GenerateContext(_a) {
214
297
  }
215
298
  }
216
299
  if (!usedVmModules) {
217
- const codeToRun = functionToRun.code.includes('import ')
218
- ? transformImportsToRequire(functionToRun.code)
219
- : functionToRun.code;
220
- vm_1.default.runInContext(codeToRun, vmContext);
300
+ vm_1.default.runInContext(transpileSandboxModule(functionToRun.code), vmContext, { filename: entryFile });
221
301
  }
222
302
  sandboxModule.exports = (_a = resolveExport(vmContext)) !== null && _a !== void 0 ? _a : sandboxModule.exports;
223
303
  if (deserializeArgs) {
@@ -247,11 +327,8 @@ function GenerateContextSync({ args, app, rules, user, currentFunction, function
247
327
  GenerateContextSync,
248
328
  request
249
329
  });
250
- const { sandboxModule, vmContext } = buildVmContext(contextData);
251
- const codeToRun = functionToRun.code.includes('import ')
252
- ? transformImportsToRequire(functionToRun.code)
253
- : functionToRun.code;
254
- vm_1.default.runInContext(codeToRun, vmContext);
330
+ const { sandboxModule, entryFile, vmContext } = buildVmContext(contextData, functionToRun);
331
+ vm_1.default.runInContext(transpileSandboxModule(functionToRun.code), vmContext, { filename: entryFile });
255
332
  sandboxModule.exports = (_a = resolveExport(vmContext)) !== null && _a !== void 0 ? _a : sandboxModule.exports;
256
333
  const fn = sandboxModule.exports;
257
334
  if (deserializeArgs) {
@@ -1,4 +1,5 @@
1
1
  import { PermissionExpression } from './interface';
2
2
  import { MachineContext } from './machines/interface';
3
+ export declare const evaluateExpandedExpression: (expression: unknown, params: MachineContext["params"], user?: MachineContext["user"]) => Promise<boolean>;
3
4
  export declare const evaluateExpression: (params: MachineContext["params"], expression?: PermissionExpression, user?: MachineContext["user"]) => Promise<boolean>;
4
5
  //# sourceMappingURL=helpers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/utils/roles/helpers.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAkBrD,eAAO,MAAM,kBAAkB,GAC7B,QAAQ,cAAc,CAAC,QAAQ,CAAC,EAChC,aAAa,oBAAoB,EACjC,OAAO,cAAc,CAAC,MAAM,CAAC,KAC5B,OAAO,CAAC,OAAO,CAoBjB,CAAA"}
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/utils/roles/helpers.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AA+DrD,eAAO,MAAM,0BAA0B,GACrC,YAAY,OAAO,EACnB,QAAQ,cAAc,CAAC,QAAQ,CAAC,EAChC,OAAO,cAAc,CAAC,MAAM,CAAC,KAC5B,OAAO,CAAC,OAAO,CAwDjB,CAAA;AAED,eAAO,MAAM,kBAAkB,GAC7B,QAAQ,cAAc,CAAC,QAAQ,CAAC,EAChC,aAAa,oBAAoB,EACjC,OAAO,cAAc,CAAC,MAAM,CAAC,KAC5B,OAAO,CAAC,OAAO,CAMjB,CAAA"}
@@ -12,13 +12,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
12
12
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.evaluateExpression = void 0;
15
+ exports.evaluateExpression = exports.evaluateExpandedExpression = void 0;
16
16
  const services_1 = require("../../services");
17
17
  const state_1 = require("../../state");
18
18
  const context_1 = require("../context");
19
19
  const rules_1 = require("../rules");
20
20
  const utils_1 = __importDefault(require("../rules-matcher/utils"));
21
21
  const functionsConditions = ['%%true', '%%false'];
22
+ const andConditions = ['$and', '%and'];
23
+ const orConditions = ['$or', '%or'];
22
24
  const normalizeUserRole = (user) => {
23
25
  if (!user)
24
26
  return user;
@@ -34,17 +36,84 @@ const normalizeUserRole = (user) => {
34
36
  ? Object.assign(Object.assign({}, candidate), { role: customRole })
35
37
  : user;
36
38
  };
39
+ const buildEvaluationContext = (params, user) => {
40
+ var _a, _b, _c;
41
+ const normalizedUser = normalizeUserRole(user);
42
+ return Object.assign(Object.assign(Object.assign({}, ((_a = params.expansions) !== null && _a !== void 0 ? _a : {})), ((_b = params.cursor) !== null && _b !== void 0 ? _b : {})), { '%%root': params.cursor, '%%prevRoot': (_c = params.expansions) === null || _c === void 0 ? void 0 : _c['%%prevRoot'], '%%user': normalizedUser, '%%true': true, '%%false': false });
43
+ };
44
+ const getFunctionCondition = (expression) => {
45
+ if (!expression || typeof expression !== 'object' || Array.isArray(expression)) {
46
+ return null;
47
+ }
48
+ const entries = Object.entries(expression);
49
+ if (entries.length !== 1) {
50
+ return null;
51
+ }
52
+ const [key, value] = entries[0];
53
+ if (!functionsConditions.includes(key)) {
54
+ return null;
55
+ }
56
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
57
+ return null;
58
+ }
59
+ return Object.prototype.hasOwnProperty.call(value, '%function')
60
+ ? [key, value]
61
+ : null;
62
+ };
63
+ const evaluateExpandedExpression = (expression, params, user) => __awaiter(void 0, void 0, void 0, function* () {
64
+ if (typeof expression === 'boolean') {
65
+ return expression;
66
+ }
67
+ if (!expression || typeof expression !== 'object') {
68
+ return Boolean(expression);
69
+ }
70
+ const block = expression;
71
+ const functionCondition = getFunctionCondition(block);
72
+ if (functionCondition) {
73
+ return evaluateComplexExpression(functionCondition, params, user);
74
+ }
75
+ const andKey = andConditions.find((key) => Object.prototype.hasOwnProperty.call(block, key));
76
+ if (andKey) {
77
+ const conditions = Array.isArray(block[andKey]) ? block[andKey] : [];
78
+ if (!conditions.length)
79
+ return true;
80
+ for (const condition of conditions) {
81
+ if (!(yield (0, exports.evaluateExpandedExpression)(condition, params, user))) {
82
+ return false;
83
+ }
84
+ }
85
+ return true;
86
+ }
87
+ const orKey = orConditions.find((key) => Object.prototype.hasOwnProperty.call(block, key));
88
+ if (orKey) {
89
+ const conditions = Array.isArray(block[orKey]) ? block[orKey] : [];
90
+ if (!conditions.length)
91
+ return true;
92
+ for (const condition of conditions) {
93
+ if (yield (0, exports.evaluateExpandedExpression)(condition, params, user)) {
94
+ return true;
95
+ }
96
+ }
97
+ return false;
98
+ }
99
+ const keys = Object.keys(block);
100
+ if (keys.length > 1) {
101
+ for (const key of keys) {
102
+ if (!(yield (0, exports.evaluateExpandedExpression)({ [key]: block[key] }, params, user))) {
103
+ return false;
104
+ }
105
+ }
106
+ return true;
107
+ }
108
+ return utils_1.default.checkRule(block, buildEvaluationContext(params, user), {});
109
+ });
110
+ exports.evaluateExpandedExpression = evaluateExpandedExpression;
37
111
  const evaluateExpression = (params, expression, user) => __awaiter(void 0, void 0, void 0, function* () {
38
- var _a;
39
112
  if (!expression || typeof expression === 'boolean')
40
113
  return !!expression;
41
- const normalizedUser = normalizeUserRole(user);
42
- const value = Object.assign(Object.assign(Object.assign({}, params.expansions), params.cursor), { '%%root': params.cursor, '%%prevRoot': (_a = params.expansions) === null || _a === void 0 ? void 0 : _a['%%prevRoot'], '%%user': normalizedUser, '%%true': true, '%%false': false });
114
+ const value = buildEvaluationContext(params, user);
43
115
  const conditions = (0, rules_1.expandQuery)(expression, value);
44
- const complexCondition = Object.entries(conditions).find(([key]) => functionsConditions.includes(key));
45
- return complexCondition
46
- ? yield evaluateComplexExpression(complexCondition, params, normalizedUser)
47
- : utils_1.default.checkRule(conditions, value, {});
116
+ return (0, exports.evaluateExpandedExpression)(conditions, params, user);
48
117
  });
49
118
  exports.evaluateExpression = evaluateExpression;
50
119
  const evaluateComplexExpression = (condition, params, user) => __awaiter(void 0, void 0, void 0, function* () {
@@ -12,6 +12,7 @@ import { LogMachineInfoParams } from './interface';
12
12
  * @returns {Role | null} - Returns the first role that matches the `apply_when` condition, or `null` if none match.
13
13
  */
14
14
  export declare const getWinningRole: (document: OptionalId<Document> | null, user: User, roles?: Role[]) => Role | null;
15
+ export declare const getWinningRoleAsync: (document: OptionalId<Document> | null, user: User, roles?: Role[]) => Promise<Role | null>;
15
16
  /**
16
17
  * Checks if the `apply_when` condition is valid for the given user and document.
17
18
  *
@@ -22,6 +23,7 @@ export declare const getWinningRole: (document: OptionalId<Document> | null, use
22
23
  * @returns {boolean} - Returns `true` if at least one valid rule is found, otherwise `false`.
23
24
  */
24
25
  export declare const checkApplyWhen: (apply_when: Role["apply_when"], user: User, document: OptionalId<Document> | null) => boolean;
26
+ export declare const checkApplyWhenAsync: (apply_when: Role["apply_when"], user: User, document: OptionalId<Document> | null) => Promise<boolean>;
25
27
  /**
26
28
  * Logs machine step information if logging is enabled.
27
29
  *
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../../src/utils/roles/machines/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAC9C,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAA;AAGzC,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAA;AACnC,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAElD;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,GACzB,UAAU,UAAU,CAAC,QAAQ,CAAC,GAAG,IAAI,EACrC,MAAM,IAAI,EACV,QAAO,IAAI,EAAO,KACjB,IAAI,GAAG,IAQT,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,GACzB,YAAY,IAAI,CAAC,YAAY,CAAC,EAC9B,MAAM,IAAI,EACV,UAAU,UAAU,CAAC,QAAQ,CAAC,GAAG,IAAI,YAQtC,CAAA;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,cAAc,GAAI,sCAK5B,oBAAoB,SAEtB,CAAA"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../../src/utils/roles/machines/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAC9C,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAA;AAIzC,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAA;AACnC,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAElD;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,GACzB,UAAU,UAAU,CAAC,QAAQ,CAAC,GAAG,IAAI,EACrC,MAAM,IAAI,EACV,QAAO,IAAI,EAAO,KACjB,IAAI,GAAG,IAQT,CAAA;AAED,eAAO,MAAM,mBAAmB,GAC9B,UAAU,UAAU,CAAC,QAAQ,CAAC,GAAG,IAAI,EACrC,MAAM,IAAI,EACV,QAAO,IAAI,EAAO,KACjB,OAAO,CAAC,IAAI,GAAG,IAAI,CAQrB,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,GACzB,YAAY,IAAI,CAAC,YAAY,CAAC,EAC9B,MAAM,IAAI,EACV,UAAU,UAAU,CAAC,QAAQ,CAAC,GAAG,IAAI,YAQtC,CAAA;AAED,eAAO,MAAM,mBAAmB,GAC9B,YAAY,IAAI,CAAC,YAAY,CAAC,EAC9B,MAAM,IAAI,EACV,UAAU,UAAU,CAAC,QAAQ,CAAC,GAAG,IAAI,qBActC,CAAA;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,cAAc,GAAI,sCAK5B,oBAAoB,SAEtB,CAAA"}
@@ -1,7 +1,17 @@
1
1
  "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
2
11
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.logMachineInfo = exports.checkApplyWhen = exports.getWinningRole = void 0;
12
+ exports.logMachineInfo = exports.checkApplyWhenAsync = exports.checkApplyWhen = exports.getWinningRoleAsync = exports.getWinningRole = void 0;
4
13
  const utils_1 = require("../../../services/mongodb-atlas/utils");
14
+ const helpers_1 = require("../helpers");
5
15
  /**
6
16
  * Determines the first applicable role for a given user and document.
7
17
  *
@@ -22,6 +32,17 @@ const getWinningRole = (document, user, roles = []) => {
22
32
  return null;
23
33
  };
24
34
  exports.getWinningRole = getWinningRole;
35
+ const getWinningRoleAsync = (document_1, user_1, ...args_1) => __awaiter(void 0, [document_1, user_1, ...args_1], void 0, function* (document, user, roles = []) {
36
+ if (!roles.length)
37
+ return null;
38
+ for (const role of roles) {
39
+ if (yield (0, exports.checkApplyWhenAsync)(role.apply_when, user, document)) {
40
+ return role;
41
+ }
42
+ }
43
+ return null;
44
+ });
45
+ exports.getWinningRoleAsync = getWinningRoleAsync;
25
46
  /**
26
47
  * Checks if the `apply_when` condition is valid for the given user and document.
27
48
  *
@@ -40,6 +61,17 @@ const checkApplyWhen = (apply_when, user, document) => {
40
61
  return !!validRule.length;
41
62
  };
42
63
  exports.checkApplyWhen = checkApplyWhen;
64
+ const checkApplyWhenAsync = (apply_when, user, document) => __awaiter(void 0, void 0, void 0, function* () {
65
+ return (0, helpers_1.evaluateExpression)({
66
+ type: 'read',
67
+ roles: [],
68
+ cursor: document,
69
+ expansions: {
70
+ '%%prevRoot': undefined
71
+ }
72
+ }, apply_when, user);
73
+ });
74
+ exports.checkApplyWhenAsync = checkApplyWhenAsync;
43
75
  /**
44
76
  * Logs machine step information if logging is enabled.
45
77
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowerforce/flowerbase",
3
- "version": "1.8.4-beta.1",
3
+ "version": "1.8.4-beta.3",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -10,7 +10,10 @@ export interface FunctionConfig {
10
10
  disable_arg_logs?: boolean
11
11
  }
12
12
 
13
- export type Function = Omit<FunctionConfig, 'name'> & { code: string }
13
+ export type Function = Omit<FunctionConfig, 'name'> & {
14
+ code: string
15
+ sourcePath?: string
16
+ }
14
17
 
15
18
  export type Functions = Record<string, Function>
16
19
 
@@ -27,7 +27,7 @@ export const loadFunctions = async (rootDir = process.cwd()): Promise<Functions>
27
27
  throw new Error(`File ${name}.js or ${name}.ts not found`)
28
28
  }
29
29
  code = fs.readFileSync(fnPath, 'utf-8')
30
- acc[name] = { code, ...opts }
30
+ acc[name] = { code, sourcePath: fnPath, ...opts }
31
31
 
32
32
  return acc
33
33
  }, {} as Functions)
@@ -74,8 +74,8 @@ export const executeQuery = async ({
74
74
  typeof projection !== 'undefined'
75
75
  ? parsedProjection
76
76
  : parsedOptions &&
77
- typeof parsedOptions === 'object' &&
78
- 'projection' in parsedOptions
77
+ typeof parsedOptions === 'object' &&
78
+ 'projection' in parsedOptions
79
79
  ? (parsedOptions as Document).projection
80
80
  : undefined
81
81
  return {