@flowerforce/flowerbase 1.9.1-beta.0 → 1.10.1-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## 1.10.0 (2026-05-28)
2
+
3
+
4
+ ### 🚀 Features
5
+
6
+ - enhance rule evaluation and testing for scalar equality with array ([#71](https://github.com/flowerforce/flowerbase/pull/71))
7
+
1
8
  ## 1.9.0 (2026-05-06)
2
9
 
3
10
 
@@ -1,6 +1,14 @@
1
1
  import { mongodb } from '@fastify/mongodb';
2
- import { Arguments } from '../../auth/dtos';
2
+ import { Arguments, User } from '../../auth/dtos';
3
3
  import { GenerateContextDataParams } from './interface';
4
+ /** Realm-compatible system user for run_as_system execution with no authenticated caller. */
5
+ export declare const REALM_SYSTEM_USER: {
6
+ readonly type: "system";
7
+ readonly data: {};
8
+ readonly custom_data: {};
9
+ readonly identities: readonly [];
10
+ };
11
+ export declare const contextUserForRun: (user: User, runAsSystem: boolean) => User;
4
12
  type JwtUtils = {
5
13
  encode: (signingMethod: string, payload: unknown, secret: string | Buffer, customHeaderFields?: Record<string, unknown>) => string;
6
14
  decode: (jwtString: string, key: string | Buffer, returnHeader?: boolean, acceptedSigningMethods?: string[]) => unknown;
@@ -24,7 +32,6 @@ export declare const generateContextData: ({ user, services, app, rules, current
24
32
  deserialize: (ejson: mongodb.BSON.Document, options?: mongodb.BSON.EJSONOptions) => any;
25
33
  };
26
34
  Buffer: BufferConstructor;
27
- fetch: typeof fetch;
28
35
  utils: {
29
36
  jwt: JwtUtils;
30
37
  };
@@ -47,6 +54,7 @@ export declare const generateContextData: ({ user, services, app, rules, current
47
54
  action: string;
48
55
  };
49
56
  user: unknown;
57
+ runningAsSystem: () => boolean;
50
58
  environment: {
51
59
  tag: string | undefined;
52
60
  };
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/utils/context/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAG1C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAE3C,OAAO,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAA;AAEvD,KAAK,QAAQ,GAAG;IACd,MAAM,EAAE,CACN,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,GAAG,MAAM,EACvB,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KACzC,MAAM,CAAA;IACX,MAAM,EAAE,CACN,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,GAAG,MAAM,EACpB,YAAY,CAAC,EAAE,OAAO,EACtB,sBAAsB,CAAC,EAAE,MAAM,EAAE,KAC9B,OAAO,CAAA;CACb,CAAA;AAgFD;;;;;;;;;GASG;AACH,eAAO,MAAM,mBAAmB,GAAI,4GAUjC,yBAAyB;;;;;;;;;;;;;;uBA6DP,SAAS;yBAGP,SAAS;;;;;;;;;;;0BAoBmB,MAAM,GAAG,SAAS;2BACV,MAAM,GAAG,SAAS;;;;;;;;;uBAS1D,MAAM;;;;;;+BA7DU,MAAM,OAAO,QAAQ;;;;sCA1HrC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8BAsGT,CAAC;iCAAa,CAAC;;;;;;;;;;;;;;;;;;;kCAtGP,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BAsGT,CAAC;6BAAa,CAAC;;;;;;;;;;;;;;;;;;kCAtGP,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BAsGT,CAAC;6BAAa,CAAC;;;;;;;;;;;;;;;4BA0FF,MAAM,OAAO,aAAa,WAAW,SAAS;;;CAkBrE,CAAA"}
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/utils/context/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAG1C,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AAEjD,OAAO,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAA;AAEvD,6FAA6F;AAC7F,eAAO,MAAM,iBAAiB;;;;;CAKpB,CAAA;AAKV,eAAO,MAAM,iBAAiB,GAAI,MAAM,IAAI,EAAE,aAAa,OAAO,KAAG,IACR,CAAA;AAE7D,KAAK,QAAQ,GAAG;IACd,MAAM,EAAE,CACN,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,GAAG,MAAM,EACvB,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KACzC,MAAM,CAAA;IACX,MAAM,EAAE,CACN,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,GAAG,MAAM,EACpB,YAAY,CAAC,EAAE,OAAO,EACtB,sBAAsB,CAAC,EAAE,MAAM,EAAE,KAC9B,OAAO,CAAA;CACb,CAAA;AAgFD;;;;;;;;;GASG;AACH,eAAO,MAAM,mBAAmB,GAAI,4GAUjC,yBAAyB;;;;;;;;;;;;;uBA4DP,SAAS;yBAGP,SAAS;;;;;;;;;;;0BAoBmB,MAAM,GAAG,SAAS;2BACV,MAAM,GAAG,SAAS;;;;;;;;;;uBAU1D,MAAM;;;;;;+BA7DU,MAAM,OAAO,QAAQ;;;;sCAjJtD,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8BAkHM,CAAC;iCAAa,CAAC;;;;;;;;;;;;;;;;;;;kCAlHtB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BAkHM,CAAC;6BAAa,CAAC;;;;;;;;;;;;;;;;;;kCAlHtB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BAkHM,CAAC;6BAAa,CAAC;;;;;;;;;;;;;;;4BAqGA,MAAM,OAAO,aAAa,WAAW,SAAS;;;CAkBrE,CAAA"}
@@ -33,10 +33,20 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.generateContextData = void 0;
36
+ exports.generateContextData = exports.contextUserForRun = exports.REALM_SYSTEM_USER = void 0;
37
37
  const mongodb_1 = require("@fastify/mongodb");
38
38
  const bson_1 = require("bson");
39
39
  const jwt = __importStar(require("jsonwebtoken"));
40
+ /** Realm-compatible system user for run_as_system execution with no authenticated caller. */
41
+ exports.REALM_SYSTEM_USER = {
42
+ type: 'system',
43
+ data: {},
44
+ custom_data: {},
45
+ identities: []
46
+ };
47
+ const isEmptyUser = (user) => user != null && typeof user === 'object' && !Array.isArray(user) && Object.keys(user).length === 0;
48
+ const contextUserForRun = (user, runAsSystem) => runAsSystem && isEmptyUser(user) ? exports.REALM_SYSTEM_USER : user;
49
+ exports.contextUserForRun = contextUserForRun;
40
50
  const normalizePayload = (payload) => {
41
51
  if (typeof payload !== 'string')
42
52
  return payload;
@@ -150,7 +160,6 @@ const generateContextData = ({ user, services, app, rules, currentFunction, func
150
160
  BSON,
151
161
  EJSON: bson_1.EJSON,
152
162
  Buffer,
153
- fetch,
154
163
  utils,
155
164
  console: {
156
165
  log: (...args) => {
@@ -180,6 +189,7 @@ const generateContextData = ({ user, services, app, rules, currentFunction, func
180
189
  action: ''
181
190
  },
182
191
  user,
192
+ runningAsSystem: () => Boolean(currentFunction.run_as_system),
183
193
  environment: {
184
194
  tag: process.env.NODE_ENV
185
195
  },
@@ -1 +1 @@
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"}
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,CA4G1C;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,CA2BjC"}
@@ -238,10 +238,11 @@ function GenerateContext(_a) {
238
238
  const functionsQueue = state_1.StateManager.select("functionsQueue");
239
239
  const effectiveRunAsSystem = Boolean(runAsSystem || currentFunction.run_as_system);
240
240
  const functionToRun = Object.assign(Object.assign({}, currentFunction), { run_as_system: effectiveRunAsSystem });
241
+ const contextUser = (0, helpers_1.contextUserForRun)(user, effectiveRunAsSystem);
241
242
  const run = () => __awaiter(this, void 0, void 0, function* () {
242
243
  var _a;
243
244
  const contextData = (0, helpers_1.generateContextData)({
244
- user,
245
+ user: contextUser,
245
246
  services,
246
247
  app,
247
248
  rules,
@@ -315,8 +316,9 @@ function GenerateContextSync({ args, app, rules, user, currentFunction, function
315
316
  return;
316
317
  const effectiveRunAsSystem = Boolean(runAsSystem || currentFunction.run_as_system);
317
318
  const functionToRun = Object.assign(Object.assign({}, currentFunction), { run_as_system: effectiveRunAsSystem });
319
+ const contextUser = (0, helpers_1.contextUserForRun)(user, effectiveRunAsSystem);
318
320
  const contextData = (0, helpers_1.generateContextData)({
319
- user,
321
+ user: contextUser,
320
322
  services,
321
323
  app,
322
324
  rules,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowerforce/flowerbase",
3
- "version": "1.9.1-beta.0",
3
+ "version": "1.10.1-beta.0",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -157,25 +157,4 @@ describe('context.functions.execute compatibility', () => {
157
157
  expect(result).toEqual({ address: 'rome', total: 10 })
158
158
  fs.rmSync(tempDir, { recursive: true, force: true })
159
159
  })
160
-
161
- it('exposes native fetch in the sandbox global scope', () => {
162
- const functionsList = {
163
- caller: {
164
- code: 'module.exports = function() { return fetch === globalThis.fetch && typeof fetch === "function" }'
165
- }
166
- } as Functions
167
-
168
- const result = GenerateContextSync({
169
- args: [],
170
- app: {} as any,
171
- rules: {} as any,
172
- user: {} as any,
173
- currentFunction: functionsList.caller,
174
- functionsList,
175
- services: mockServices,
176
- functionName: 'caller'
177
- })
178
-
179
- expect(result).toBe(true)
180
- })
181
160
  })
@@ -4,7 +4,7 @@ import { User } from '../../auth/dtos'
4
4
  import { Functions } from '../../features/functions/interface'
5
5
  import { Rules } from '../../features/rules/interface'
6
6
  import { services } from '../../services'
7
- import { generateContextData } from '../context/helpers'
7
+ import { contextUserForRun, generateContextData, REALM_SYSTEM_USER } from '../context/helpers'
8
8
 
9
9
  const originalEnv = process.env
10
10
 
@@ -47,7 +47,7 @@ describe('generateContextData', () => {
47
47
 
48
48
  it('should return an object with context configuration', async () => {
49
49
  const mockApp = Fastify()
50
- const { context, console: contextConsole, BSON, fetch } = generateContextData({
50
+ const { context, console: contextConsole, BSON } = generateContextData({
51
51
  services,
52
52
  app: mockApp,
53
53
  functionsList: mockFunctions,
@@ -66,8 +66,6 @@ describe('generateContextData', () => {
66
66
 
67
67
  expect(context.user).toEqual(mockUser)
68
68
 
69
- expect(fetch).toBe(global.fetch)
70
-
71
69
  const mockedLog = jest.spyOn(console, 'log').mockImplementation(() => { })
72
70
  contextConsole.log('Test', 'generateContextData')
73
71
  expect(mockedLog).toHaveBeenCalledWith('Test', 'generateContextData')
@@ -115,4 +113,49 @@ describe('generateContextData', () => {
115
113
  const binaryObject = Binary.fromBase64Binary(base64, 0)
116
114
  expect(binaryObject).toBeInstanceOf(BSON.Binary)
117
115
  })
116
+
117
+ it('exposes Realm system user and runningAsSystem when run_as_system is enabled', () => {
118
+ const mockApp = Fastify()
119
+ const systemFunction = { ...currentFunction, run_as_system: true }
120
+ const { context } = generateContextData({
121
+ services,
122
+ app: mockApp,
123
+ functionsList: mockFunctions,
124
+ currentFunction: systemFunction,
125
+ GenerateContext: GenerateContextMock,
126
+ GenerateContextSync: GenerateContextSyncMock,
127
+ user: REALM_SYSTEM_USER,
128
+ rules: mockRules
129
+ })
130
+
131
+ expect(context.user).toEqual(REALM_SYSTEM_USER)
132
+ expect(context.runningAsSystem()).toBe(true)
133
+ })
134
+
135
+ it('runningAsSystem is false for regular function execution', () => {
136
+ const mockApp = Fastify()
137
+ const { context } = generateContextData({
138
+ services,
139
+ app: mockApp,
140
+ functionsList: mockFunctions,
141
+ currentFunction,
142
+ GenerateContext: GenerateContextMock,
143
+ GenerateContextSync: GenerateContextSyncMock,
144
+ user: { id: 'user-1', type: 'normal', data: {}, custom_data: {}, identities: [] },
145
+ rules: mockRules
146
+ })
147
+
148
+ expect(context.runningAsSystem()).toBe(false)
149
+ })
150
+ })
151
+
152
+ describe('contextUserForRun', () => {
153
+ it('returns Realm system user for empty user with run_as_system', () => {
154
+ expect(contextUserForRun({}, true)).toEqual(REALM_SYSTEM_USER)
155
+ })
156
+
157
+ it('returns the user unchanged when not running as system', () => {
158
+ const user = { id: 'user-1' }
159
+ expect(contextUserForRun(user, false)).toBe(user)
160
+ })
118
161
  })
@@ -1,10 +1,24 @@
1
1
  import { mongodb } from '@fastify/mongodb'
2
2
  import { EJSON } from 'bson'
3
3
  import * as jwt from 'jsonwebtoken'
4
- import { Arguments } from '../../auth/dtos'
4
+ import { Arguments, User } from '../../auth/dtos'
5
5
  import { Function } from '../../features/functions/interface'
6
6
  import { GenerateContextDataParams } from './interface'
7
7
 
8
+ /** Realm-compatible system user for run_as_system execution with no authenticated caller. */
9
+ export const REALM_SYSTEM_USER = {
10
+ type: 'system',
11
+ data: {},
12
+ custom_data: {},
13
+ identities: []
14
+ } as const
15
+
16
+ const isEmptyUser = (user: User) =>
17
+ user != null && typeof user === 'object' && !Array.isArray(user) && Object.keys(user).length === 0
18
+
19
+ export const contextUserForRun = (user: User, runAsSystem: boolean): User =>
20
+ runAsSystem && isEmptyUser(user) ? REALM_SYSTEM_USER : user
21
+
8
22
  type JwtUtils = {
9
23
  encode: (
10
24
  signingMethod: string,
@@ -176,7 +190,6 @@ export const generateContextData = ({
176
190
  BSON,
177
191
  EJSON,
178
192
  Buffer,
179
- fetch,
180
193
  utils,
181
194
  console: {
182
195
  log: (...args: Arguments) => {
@@ -208,6 +221,7 @@ export const generateContextData = ({
208
221
  action: ''
209
222
  },
210
223
  user,
224
+ runningAsSystem: () => Boolean(currentFunction.run_as_system),
211
225
  environment: {
212
226
  tag: process.env.NODE_ENV
213
227
  },
@@ -6,7 +6,7 @@ import vm from 'vm'
6
6
  import { EJSON } from 'bson'
7
7
  import { StateManager } from '../../state'
8
8
  import { Function as AppFunction } from '../../features/functions/interface'
9
- import { generateContextData } from './helpers'
9
+ import { contextUserForRun, generateContextData } from './helpers'
10
10
  import { GenerateContextParams } from './interface'
11
11
 
12
12
  const dynamicImport = new Function('specifier', 'return import(specifier)') as (
@@ -323,11 +323,12 @@ export async function GenerateContext({
323
323
  const functionsQueue = StateManager.select("functionsQueue")
324
324
  const effectiveRunAsSystem = Boolean(runAsSystem || currentFunction.run_as_system)
325
325
  const functionToRun = { ...currentFunction, run_as_system: effectiveRunAsSystem }
326
+ const contextUser = contextUserForRun(user, effectiveRunAsSystem)
326
327
 
327
328
  const run = async () => {
328
329
 
329
330
  const contextData = generateContextData({
330
- user,
331
+ user: contextUser,
331
332
  services,
332
333
  app,
333
334
  rules,
@@ -443,8 +444,9 @@ export function GenerateContextSync({
443
444
 
444
445
  const effectiveRunAsSystem = Boolean(runAsSystem || currentFunction.run_as_system)
445
446
  const functionToRun = { ...currentFunction, run_as_system: effectiveRunAsSystem }
447
+ const contextUser = contextUserForRun(user, effectiveRunAsSystem)
446
448
  const contextData = generateContextData({
447
- user,
449
+ user: contextUser,
448
450
  services,
449
451
  app,
450
452
  rules,