@flowerforce/flowerbase 1.5.1-beta.3 → 1.6.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,21 @@
1
+ ## 1.6.0 (2026-01-28)
2
+
3
+
4
+ ### 🚀 Features
5
+
6
+ - added cli run function ([fb2e418](https://github.com/flowerforce/flowerbase/commit/fb2e418))
7
+
8
+
9
+ ### 🩹 Fixes
10
+
11
+ - trigger db update ([8eed422](https://github.com/flowerforce/flowerbase/commit/8eed422))
12
+
13
+ - trigger test e2e full_document_before_change ([117e89c](https://github.com/flowerforce/flowerbase/commit/117e89c))
14
+
15
+ - trigger fullDocumentBeforeChange ([a659a8d](https://github.com/flowerforce/flowerbase/commit/a659a8d))
16
+
17
+ - revert trigger fullDocumentBeforeChange ([f1736b5](https://github.com/flowerforce/flowerbase/commit/f1736b5))
18
+
1
19
  ## 1.5.0 (2026-01-26)
2
20
 
3
21
 
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/auth/utils.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;CAcxB,CAAA;AAED,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;CAa7B,CAAA;AAED,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;CAe7B,CAAA;AAED,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;CAUhC,CAAA;AAED,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;CAS/B,CAAA;AAED,eAAO,MAAM,YAAY;;;;;;;;;;;;;CAAoB,CAAA;AAE7C,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;CAc/B,CAAA;AAED,oBAAY,cAAc;IACxB,KAAK,WAAW;IAChB,YAAY,cAAc;IAC1B,OAAO,aAAa;IACpB,OAAO,aAAa;IACpB,OAAO,aAAa;IACpB,KAAK,gBAAgB;IACrB,UAAU,gBAAgB;IAC1B,aAAa,WAAW;IACxB,UAAU,sBAAsB;CACjC;AAED,oBAAY,WAAW;IACrB,mBAAmB,wBAAwB;IAC3C,aAAa,mCAAmC;IAChD,oBAAoB,sCAAsC;IAC1D,sBAAsB,2BAA2B;IACjD,kBAAkB,uBAAuB;CAC1C;AAED,MAAM,WAAW,UAAU;IACzB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,SAAS,EAAE,MAAM,CAAA;IACjB,gBAAgB,EAAE,aAAa,CAAA;IAC/B,iBAAiB,EAAE,cAAc,CAAA;IACjC,WAAW,CAAC,EAAE,QAAQ,CAAA;CACvB;AAED,UAAU,MAAM;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,OAAO,CAAA;CAClB;AACD,UAAU,aAAa;IACrB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,OAAO,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;CACf;AAED,UAAU,cAAc;IACtB,IAAI,EAAE,iBAAiB,CAAC;IACxB,IAAI,EAAE,iBAAiB,CAAC;IACxB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE;QACN,kBAAkB,EAAE,MAAM,CAAA;KAC3B,CAAA;CACF;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,WAAW,CAAA;IACjB,IAAI,EAAE,WAAW,CAAA;IACjB,QAAQ,EAAE,OAAO,CAAA;CAClB;AAED,MAAM,WAAW,MAAM;IACrB,WAAW,EAAE,OAAO,CAAA;IACpB,wBAAwB,CAAC,EAAE,MAAM,CAAA;IACjC,iBAAiB,EAAE,MAAM,CAAA;IACzB,gBAAgB,EAAE,MAAM,CAAA;IACxB,uBAAuB,EAAE,OAAO,CAAA;IAChC,gBAAgB,EAAE,OAAO,CAAA;CAC1B;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAA;IAChB,kBAAkB,EAAE,MAAM,CAAA;IAC1B,aAAa,EAAE,MAAM,CAAA;IACrB,eAAe,EAAE,MAAM,CAAA;IACvB,aAAa,EAAE,MAAM,CAAA;IACrB,8BAA8B,EAAE,MAAM,CAAA;CACvC;AAMD;;;GAGG;AACH,eAAO,MAAM,cAAc,QAAO,UAGjC,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,kBAAkB,QAAO,oBAGrC,CAAA;AAED,eAAO,MAAM,gBAAgB,GAAI,eAAW,WAG3C,CAAA"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/auth/utils.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;CAcxB,CAAA;AAED,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;CAa7B,CAAA;AAED,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;CAe7B,CAAA;AAED,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;CAUhC,CAAA;AAED,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;CAS/B,CAAA;AAED,eAAO,MAAM,YAAY;;;;;;;;;;;;;CAAoB,CAAA;AAE7C,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;CAc/B,CAAA;AAED,oBAAY,cAAc;IACxB,KAAK,WAAW;IAChB,YAAY,cAAc;IAC1B,OAAO,aAAa;IACpB,OAAO,aAAa;IACpB,OAAO,aAAa;IACpB,KAAK,gBAAgB;IACrB,UAAU,gBAAgB;IAC1B,aAAa,WAAW;IACxB,UAAU,sBAAsB;CACjC;AAED,oBAAY,WAAW;IACrB,mBAAmB,wBAAwB;IAC3C,aAAa,mCAAmC;IAChD,oBAAoB,sCAAsC;IAC1D,sBAAsB,2BAA2B;IACjD,kBAAkB,uBAAuB;CAC1C;AAED,MAAM,WAAW,UAAU;IACzB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,SAAS,EAAE,MAAM,CAAA;IACjB,gBAAgB,EAAE,aAAa,CAAA;IAC/B,iBAAiB,EAAE,cAAc,CAAA;IACjC,WAAW,CAAC,EAAE,QAAQ,CAAA;CACvB;AAED,UAAU,MAAM;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,OAAO,CAAA;CAClB;AACD,UAAU,aAAa;IACrB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,OAAO,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;CACf;AAED,UAAU,cAAc;IACtB,IAAI,EAAE,iBAAiB,CAAC;IACxB,IAAI,EAAE,iBAAiB,CAAC;IACxB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE;QACN,kBAAkB,EAAE,MAAM,CAAA;KAC3B,CAAA;CACF;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,WAAW,CAAA;IACjB,IAAI,EAAE,WAAW,CAAA;IACjB,QAAQ,EAAE,OAAO,CAAA;CAClB;AAED,MAAM,WAAW,MAAM;IACrB,WAAW,EAAE,OAAO,CAAA;IACpB,wBAAwB,CAAC,EAAE,MAAM,CAAA;IACjC,iBAAiB,EAAE,MAAM,CAAA;IACzB,gBAAgB,EAAE,MAAM,CAAA;IACxB,uBAAuB,EAAE,OAAO,CAAA;IAChC,gBAAgB,EAAE,OAAO,CAAA;CAC1B;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAA;IAChB,kBAAkB,EAAE,MAAM,CAAA;IAC1B,aAAa,EAAE,MAAM,CAAA;IACrB,eAAe,EAAE,MAAM,CAAA;IACvB,aAAa,EAAE,MAAM,CAAA;IACrB,8BAA8B,EAAE,MAAM,CAAA;CACvC;AAMD;;;GAGG;AACH,eAAO,MAAM,cAAc,QAAO,UAuCjC,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,kBAAkB,QAAO,oBAarC,CAAA;AAED,eAAO,MAAM,gBAAgB,GAAI,eAAW,WAG3C,CAAA"}
@@ -117,6 +117,42 @@ const resolveAppPath = () => { var _a, _b, _c; return (_c = (_a = process.env.FL
117
117
  */
118
118
  const loadAuthConfig = () => {
119
119
  const authPath = path_1.default.join(resolveAppPath(), 'auth/providers.json');
120
+ if (!fs_1.default.existsSync(authPath)) {
121
+ return {
122
+ auth_collection: 'auth_users',
123
+ 'api-key': {
124
+ name: 'api-key',
125
+ type: 'api-key',
126
+ disabled: true
127
+ },
128
+ 'local-userpass': {
129
+ name: 'local-userpass',
130
+ type: 'local-userpass',
131
+ disabled: true,
132
+ config: {
133
+ autoConfirm: true,
134
+ confirmationFunctionName: '',
135
+ resetFunctionName: '',
136
+ resetPasswordUrl: '',
137
+ runConfirmationFunction: false,
138
+ runResetFunction: false
139
+ }
140
+ },
141
+ 'custom-function': {
142
+ name: 'custom-function',
143
+ type: 'custom-function',
144
+ disabled: true,
145
+ config: {
146
+ authFunctionName: ''
147
+ }
148
+ },
149
+ 'anon-user': {
150
+ name: 'anon-user',
151
+ type: 'anon-user',
152
+ disabled: false
153
+ }
154
+ };
155
+ }
120
156
  return JSON.parse(fs_1.default.readFileSync(authPath, 'utf-8'));
121
157
  };
122
158
  exports.loadAuthConfig = loadAuthConfig;
@@ -125,7 +161,18 @@ exports.loadAuthConfig = loadAuthConfig;
125
161
  * @testable
126
162
  */
127
163
  const loadCustomUserData = () => {
164
+ var _a;
128
165
  const userDataPath = path_1.default.join(resolveAppPath(), 'auth/custom_user_data.json');
166
+ if (!fs_1.default.existsSync(userDataPath)) {
167
+ return {
168
+ enabled: false,
169
+ mongo_service_name: 'mongodb-atlas',
170
+ database_name: (_a = process.env.DB_NAME) !== null && _a !== void 0 ? _a : '',
171
+ collection_name: 'users',
172
+ user_id_field: 'id',
173
+ on_user_creation_function_name: ''
174
+ };
175
+ }
129
176
  return JSON.parse(fs_1.default.readFileSync(userDataPath, 'utf-8'));
130
177
  };
131
178
  exports.loadCustomUserData = loadCustomUserData;
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ import 'dotenv/config';
3
+ //# sourceMappingURL=call-function.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"call-function.d.ts","sourceRoot":"","sources":["../../src/cli/call-function.ts"],"names":[],"mappings":";AACA,OAAO,eAAe,CAAA"}
@@ -0,0 +1,282 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
37
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
38
+ return new (P || (P = Promise))(function (resolve, reject) {
39
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
40
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
41
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
42
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
43
+ });
44
+ };
45
+ var __importDefault = (this && this.__importDefault) || function (mod) {
46
+ return (mod && mod.__esModule) ? mod : { "default": mod };
47
+ };
48
+ Object.defineProperty(exports, "__esModule", { value: true });
49
+ require("dotenv/config");
50
+ const node_fs_1 = __importDefault(require("node:fs"));
51
+ const node_path_1 = __importDefault(require("node:path"));
52
+ const node_process_1 = __importDefault(require("node:process"));
53
+ const bson_1 = require("bson");
54
+ const state_1 = require("../state");
55
+ const context_1 = require("../utils/context");
56
+ const HELP_TEXT = `
57
+ Usage:
58
+ flowerbase-function --app-id <id> --name <function> [options]
59
+
60
+ Options:
61
+ --base-path <path> Project base path (default: cwd)
62
+ --mongodb-url <url> MongoDB connection string
63
+ --jwt-secret <secret> JWT secret
64
+ --app-id <id> App ID (optional)
65
+ --name <name> Function name (required)
66
+ --args <json> JSON arguments (array or single value)
67
+ --args-file <path> JSON arguments file
68
+ --stdin Read JSON arguments from stdin
69
+ --user <json> User payload JSON
70
+ --user-file <path> User payload JSON file
71
+ --user-id <id> User id shortcut
72
+ --user-email <email> User email shortcut
73
+ --user-role <role> User role shortcut
74
+ --port <number> Server port
75
+ --host <string> Server host
76
+ --help Show help
77
+
78
+ Env:
79
+ MONGODB_URL, JWT_SECRET
80
+ `;
81
+ const readStdin = () => __awaiter(void 0, void 0, void 0, function* () {
82
+ return yield new Promise((resolve, reject) => {
83
+ let data = '';
84
+ node_process_1.default.stdin.setEncoding('utf8');
85
+ node_process_1.default.stdin.on('data', (chunk) => {
86
+ data += chunk;
87
+ });
88
+ node_process_1.default.stdin.on('end', () => resolve(data));
89
+ node_process_1.default.stdin.on('error', reject);
90
+ });
91
+ });
92
+ const getArgValue = (argv, name) => {
93
+ const index = argv.findIndex((arg) => arg === name || arg.startsWith(`${name}=`));
94
+ if (index === -1)
95
+ return undefined;
96
+ const direct = argv[index];
97
+ if (direct.includes('=')) {
98
+ return direct.split('=').slice(1).join('=');
99
+ }
100
+ return argv[index + 1];
101
+ };
102
+ const hasFlag = (argv, name) => argv.includes(name);
103
+ const parseArgs = (argv) => {
104
+ if (hasFlag(argv, '--help') || hasFlag(argv, '-h')) {
105
+ console.log(HELP_TEXT.trim());
106
+ return null;
107
+ }
108
+ const appId = getArgValue(argv, '--app-id');
109
+ const name = getArgValue(argv, '--name') || '';
110
+ const basePath = getArgValue(argv, '--base-path');
111
+ const mongodbUrl = getArgValue(argv, '--mongodb-url') || node_process_1.default.env.MONGODB_URL;
112
+ const jwtSecret = getArgValue(argv, '--jwt-secret') || node_process_1.default.env.JWT_SECRET;
113
+ const portValue = getArgValue(argv, '--port');
114
+ const host = getArgValue(argv, '--host');
115
+ const port = portValue ? Number(portValue) : undefined;
116
+ return {
117
+ appId,
118
+ name,
119
+ basePath,
120
+ mongodbUrl,
121
+ jwtSecret,
122
+ port,
123
+ host,
124
+ };
125
+ };
126
+ const parseJsonValue = (value, sourceLabel) => {
127
+ try {
128
+ return JSON.parse(value);
129
+ }
130
+ catch (error) {
131
+ const message = error instanceof Error ? error.message : String(error);
132
+ throw new Error(`Invalid JSON from ${sourceLabel}: ${message}`);
133
+ }
134
+ };
135
+ const loadArgs = (argv) => __awaiter(void 0, void 0, void 0, function* () {
136
+ const argsValue = getArgValue(argv, '--args');
137
+ const argsFile = getArgValue(argv, '--args-file');
138
+ const useStdin = hasFlag(argv, '--stdin');
139
+ const shouldReadStdin = useStdin || (!node_process_1.default.stdin.isTTY && !argsValue && !argsFile);
140
+ if (argsValue) {
141
+ return parseJsonValue(argsValue, '--args');
142
+ }
143
+ if (argsFile) {
144
+ const resolved = node_path_1.default.resolve(node_process_1.default.cwd(), argsFile);
145
+ const contents = node_fs_1.default.readFileSync(resolved, 'utf8');
146
+ return parseJsonValue(contents, '--args-file');
147
+ }
148
+ if (shouldReadStdin) {
149
+ const stdinValue = yield readStdin();
150
+ if (!stdinValue.trim())
151
+ return undefined;
152
+ return parseJsonValue(stdinValue, 'stdin');
153
+ }
154
+ return undefined;
155
+ });
156
+ const normalizeArguments = (value) => {
157
+ if (value === undefined)
158
+ return [];
159
+ if (Array.isArray(value))
160
+ return value;
161
+ return [value];
162
+ };
163
+ const loadUser = (argv) => __awaiter(void 0, void 0, void 0, function* () {
164
+ const userValue = getArgValue(argv, '--user');
165
+ const userFile = getArgValue(argv, '--user-file');
166
+ const userId = getArgValue(argv, '--user-id');
167
+ const userEmail = getArgValue(argv, '--user-email');
168
+ const userRole = getArgValue(argv, '--user-role');
169
+ let user;
170
+ if (userValue) {
171
+ user = parseJsonValue(userValue, '--user');
172
+ }
173
+ else if (userFile) {
174
+ const resolved = node_path_1.default.resolve(node_process_1.default.cwd(), userFile);
175
+ const contents = node_fs_1.default.readFileSync(resolved, 'utf8');
176
+ user = parseJsonValue(contents, '--user-file');
177
+ }
178
+ else if (userId || userEmail || userRole) {
179
+ user = Object.assign(Object.assign(Object.assign({}, (userId ? { id: userId } : {})), (userEmail ? { email: userEmail } : {})), (userRole ? { role: userRole } : {}));
180
+ }
181
+ if (!user)
182
+ return undefined;
183
+ if (!('typ' in user)) {
184
+ user.typ = 'access';
185
+ }
186
+ if (!('_id' in user) && typeof user.id === 'string') {
187
+ try {
188
+ user._id = new bson_1.ObjectId(user.id);
189
+ }
190
+ catch (_a) {
191
+ // Keep the user id as-is when it is not a valid ObjectId.
192
+ }
193
+ }
194
+ return user;
195
+ });
196
+ const executeLocal = (_a) => __awaiter(void 0, [_a], void 0, function* ({ basePath, projectId, port, host, mongodbUrl, jwtSecret, name, args, user }) {
197
+ node_process_1.default.env.FLOWERBASE_APP_PATH = basePath;
198
+ const { initialize } = yield Promise.resolve().then(() => __importStar(require('../index')));
199
+ yield initialize({
200
+ projectId,
201
+ port,
202
+ mongodbUrl,
203
+ jwtSecret,
204
+ host,
205
+ basePath
206
+ });
207
+ const app = state_1.StateManager.select('app');
208
+ const functionsList = state_1.StateManager.select('functions');
209
+ const rulesList = state_1.StateManager.select('rules');
210
+ const services = state_1.StateManager.select('services');
211
+ try {
212
+ const currentFunction = functionsList[name];
213
+ if (!currentFunction) {
214
+ throw new Error(`Function "${name}" does not exist`);
215
+ }
216
+ return yield (0, context_1.GenerateContext)({
217
+ args,
218
+ app,
219
+ rules: rulesList,
220
+ user,
221
+ currentFunction: currentFunction,
222
+ functionsList,
223
+ services,
224
+ runAsSystem: true,
225
+ request: {
226
+ ip: 'cli',
227
+ method: 'CLI',
228
+ url: 'cli://local',
229
+ host: 'cli',
230
+ id: 'cli',
231
+ hostname: 'cli'
232
+ }
233
+ });
234
+ }
235
+ finally {
236
+ yield app.close();
237
+ }
238
+ });
239
+ const main = () => __awaiter(void 0, void 0, void 0, function* () {
240
+ const argv = node_process_1.default.argv.slice(2);
241
+ const options = parseArgs(argv);
242
+ if (!options)
243
+ return;
244
+ if (!options.name) {
245
+ console.error('Missing required --name');
246
+ console.log(HELP_TEXT.trim());
247
+ node_process_1.default.exit(1);
248
+ }
249
+ const basePath = options.basePath ? node_path_1.default.resolve(options.basePath) : node_process_1.default.cwd();
250
+ if (!options.mongodbUrl) {
251
+ throw new Error('Missing --mongodb-url (or MONGODB_URL)');
252
+ }
253
+ if (!options.jwtSecret) {
254
+ throw new Error('Missing --jwt-secret (or JWT_SECRET)');
255
+ }
256
+ const argsInput = yield loadArgs(argv);
257
+ const args = normalizeArguments(argsInput);
258
+ const user = yield loadUser(argv);
259
+ const projectId = options.appId || node_path_1.default.basename(basePath);
260
+ const result = yield executeLocal({
261
+ basePath,
262
+ projectId,
263
+ port: options.port,
264
+ host: options.host,
265
+ mongodbUrl: options.mongodbUrl,
266
+ jwtSecret: options.jwtSecret,
267
+ name: options.name,
268
+ args,
269
+ user
270
+ });
271
+ if (typeof result === 'string') {
272
+ console.log(result);
273
+ }
274
+ else {
275
+ console.log(JSON.stringify(result));
276
+ }
277
+ });
278
+ main().catch((error) => {
279
+ const message = error instanceof Error ? error.message : String(error);
280
+ console.error(message);
281
+ node_process_1.default.exit(1);
282
+ });
@@ -21,10 +21,10 @@ export declare const generateContextData: ({ user, services, app, rules, current
21
21
  context: {
22
22
  request: {
23
23
  remoteIPAddress: string | undefined;
24
+ id?: string | undefined;
24
25
  method?: string | undefined;
25
26
  url?: string | undefined;
26
27
  host?: string | undefined;
27
- id?: string | undefined;
28
28
  ips?: string[];
29
29
  hostname?: string | undefined;
30
30
  ip?: string | undefined;
@@ -40,7 +40,7 @@ const registerPlugins = (_a) => __awaiter(void 0, [_a], void 0, function* ({ reg
40
40
  registersConfig.forEach(({ plugin, options, pluginName }) => {
41
41
  try {
42
42
  register(plugin, options);
43
- console.log('registration COMPLETED --->', pluginName);
43
+ // console.log('registration COMPLETED --->', pluginName)
44
44
  }
45
45
  catch (e) {
46
46
  console.log('Registration FAILED --->', pluginName);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowerforce/flowerbase",
3
- "version": "1.5.1-beta.3",
3
+ "version": "1.6.0",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -67,5 +67,8 @@
67
67
  },
68
68
  "exports": {
69
69
  ".": "./dist/index.js"
70
+ },
71
+ "bin": {
72
+ "flowerbase-function": "dist/cli/call-function.js"
70
73
  }
71
74
  }
package/src/auth/utils.ts CHANGED
@@ -176,6 +176,42 @@ const resolveAppPath = () =>
176
176
  */
177
177
  export const loadAuthConfig = (): AuthConfig => {
178
178
  const authPath = path.join(resolveAppPath(), 'auth/providers.json')
179
+ if (!fs.existsSync(authPath)) {
180
+ return {
181
+ auth_collection: 'auth_users',
182
+ 'api-key': {
183
+ name: 'api-key',
184
+ type: 'api-key',
185
+ disabled: true
186
+ },
187
+ 'local-userpass': {
188
+ name: 'local-userpass',
189
+ type: 'local-userpass',
190
+ disabled: true,
191
+ config: {
192
+ autoConfirm: true,
193
+ confirmationFunctionName: '',
194
+ resetFunctionName: '',
195
+ resetPasswordUrl: '',
196
+ runConfirmationFunction: false,
197
+ runResetFunction: false
198
+ }
199
+ },
200
+ 'custom-function': {
201
+ name: 'custom-function',
202
+ type: 'custom-function',
203
+ disabled: true,
204
+ config: {
205
+ authFunctionName: ''
206
+ }
207
+ },
208
+ 'anon-user': {
209
+ name: 'anon-user',
210
+ type: 'anon-user',
211
+ disabled: false
212
+ }
213
+ }
214
+ }
179
215
  return JSON.parse(fs.readFileSync(authPath, 'utf-8'))
180
216
  }
181
217
 
@@ -185,6 +221,16 @@ export const loadAuthConfig = (): AuthConfig => {
185
221
  */
186
222
  export const loadCustomUserData = (): CustomUserDataConfig => {
187
223
  const userDataPath = path.join(resolveAppPath(), 'auth/custom_user_data.json')
224
+ if (!fs.existsSync(userDataPath)) {
225
+ return {
226
+ enabled: false,
227
+ mongo_service_name: 'mongodb-atlas',
228
+ database_name: process.env.DB_NAME ?? '',
229
+ collection_name: 'users',
230
+ user_id_field: 'id',
231
+ on_user_creation_function_name: ''
232
+ }
233
+ }
188
234
  return JSON.parse(fs.readFileSync(userDataPath, 'utf-8'))
189
235
  }
190
236
 
@@ -0,0 +1,287 @@
1
+ #!/usr/bin/env node
2
+ import 'dotenv/config'
3
+ import fs from 'node:fs'
4
+ import path from 'node:path'
5
+ import process from 'node:process'
6
+ import { ObjectId } from 'bson'
7
+ import { Function } from '../features/functions/interface'
8
+ import { StateManager } from '../state'
9
+ import { GenerateContext } from '../utils/context'
10
+
11
+ type CliOptions = {
12
+ appId?: string
13
+ name: string
14
+ basePath?: string
15
+ mongodbUrl?: string
16
+ jwtSecret?: string
17
+ port?: number
18
+ host?: string
19
+ }
20
+
21
+ const HELP_TEXT = `
22
+ Usage:
23
+ flowerbase-function --app-id <id> --name <function> [options]
24
+
25
+ Options:
26
+ --base-path <path> Project base path (default: cwd)
27
+ --mongodb-url <url> MongoDB connection string
28
+ --jwt-secret <secret> JWT secret
29
+ --app-id <id> App ID (optional)
30
+ --name <name> Function name (required)
31
+ --args <json> JSON arguments (array or single value)
32
+ --args-file <path> JSON arguments file
33
+ --stdin Read JSON arguments from stdin
34
+ --user <json> User payload JSON
35
+ --user-file <path> User payload JSON file
36
+ --user-id <id> User id shortcut
37
+ --user-email <email> User email shortcut
38
+ --user-role <role> User role shortcut
39
+ --port <number> Server port
40
+ --host <string> Server host
41
+ --help Show help
42
+
43
+ Env:
44
+ MONGODB_URL, JWT_SECRET
45
+ `
46
+
47
+ const readStdin = async () =>
48
+ await new Promise<string>((resolve, reject) => {
49
+ let data = ''
50
+ process.stdin.setEncoding('utf8')
51
+ process.stdin.on('data', (chunk) => {
52
+ data += chunk
53
+ })
54
+ process.stdin.on('end', () => resolve(data))
55
+ process.stdin.on('error', reject)
56
+ })
57
+
58
+ const getArgValue = (argv: string[], name: string) => {
59
+ const index = argv.findIndex((arg) => arg === name || arg.startsWith(`${name}=`))
60
+ if (index === -1) return undefined
61
+ const direct = argv[index]
62
+ if (direct.includes('=')) {
63
+ return direct.split('=').slice(1).join('=')
64
+ }
65
+ return argv[index + 1]
66
+ }
67
+
68
+ const hasFlag = (argv: string[], name: string) => argv.includes(name)
69
+
70
+ const parseArgs = (argv: string[]): CliOptions | null => {
71
+ if (hasFlag(argv, '--help') || hasFlag(argv, '-h')) {
72
+ console.log(HELP_TEXT.trim())
73
+ return null
74
+ }
75
+
76
+ const appId = getArgValue(argv, '--app-id')
77
+ const name = getArgValue(argv, '--name') || ''
78
+ const basePath = getArgValue(argv, '--base-path')
79
+ const mongodbUrl =
80
+ getArgValue(argv, '--mongodb-url') || process.env.MONGODB_URL
81
+ const jwtSecret = getArgValue(argv, '--jwt-secret') || process.env.JWT_SECRET
82
+ const portValue = getArgValue(argv, '--port')
83
+ const host = getArgValue(argv, '--host')
84
+ const port = portValue ? Number(portValue) : undefined
85
+
86
+ return {
87
+ appId,
88
+ name,
89
+ basePath,
90
+ mongodbUrl,
91
+ jwtSecret,
92
+ port,
93
+ host,
94
+ }
95
+ }
96
+
97
+ const parseJsonValue = (value: string, sourceLabel: string) => {
98
+ try {
99
+ return JSON.parse(value)
100
+ } catch (error) {
101
+ const message = error instanceof Error ? error.message : String(error)
102
+ throw new Error(`Invalid JSON from ${sourceLabel}: ${message}`)
103
+ }
104
+ }
105
+
106
+ const loadArgs = async (argv: string[]) => {
107
+ const argsValue = getArgValue(argv, '--args')
108
+ const argsFile = getArgValue(argv, '--args-file')
109
+ const useStdin = hasFlag(argv, '--stdin')
110
+ const shouldReadStdin = useStdin || (!process.stdin.isTTY && !argsValue && !argsFile)
111
+
112
+ if (argsValue) {
113
+ return parseJsonValue(argsValue, '--args')
114
+ }
115
+
116
+ if (argsFile) {
117
+ const resolved = path.resolve(process.cwd(), argsFile)
118
+ const contents = fs.readFileSync(resolved, 'utf8')
119
+ return parseJsonValue(contents, '--args-file')
120
+ }
121
+
122
+ if (shouldReadStdin) {
123
+ const stdinValue = await readStdin()
124
+ if (!stdinValue.trim()) return undefined
125
+ return parseJsonValue(stdinValue, 'stdin')
126
+ }
127
+
128
+ return undefined
129
+ }
130
+
131
+ const normalizeArguments = (value: unknown) => {
132
+ if (value === undefined) return []
133
+ if (Array.isArray(value)) return value
134
+ return [value]
135
+ }
136
+
137
+ const loadUser = async (argv: string[]) => {
138
+ const userValue = getArgValue(argv, '--user')
139
+ const userFile = getArgValue(argv, '--user-file')
140
+ const userId = getArgValue(argv, '--user-id')
141
+ const userEmail = getArgValue(argv, '--user-email')
142
+ const userRole = getArgValue(argv, '--user-role')
143
+
144
+ let user: Record<string, unknown> | undefined
145
+
146
+ if (userValue) {
147
+ user = parseJsonValue(userValue, '--user') as Record<string, unknown>
148
+ } else if (userFile) {
149
+ const resolved = path.resolve(process.cwd(), userFile)
150
+ const contents = fs.readFileSync(resolved, 'utf8')
151
+ user = parseJsonValue(contents, '--user-file') as Record<string, unknown>
152
+ } else if (userId || userEmail || userRole) {
153
+ user = {
154
+ ...(userId ? { id: userId } : {}),
155
+ ...(userEmail ? { email: userEmail } : {}),
156
+ ...(userRole ? { role: userRole } : {})
157
+ }
158
+ }
159
+
160
+ if (!user) return undefined
161
+
162
+ if (!('typ' in user)) {
163
+ user.typ = 'access'
164
+ }
165
+
166
+ if (!('_id' in user) && typeof user.id === 'string') {
167
+ try {
168
+ user._id = new ObjectId(user.id)
169
+ } catch {
170
+ // Keep the user id as-is when it is not a valid ObjectId.
171
+ }
172
+ }
173
+
174
+ return user
175
+ }
176
+
177
+ const executeLocal = async ({
178
+ basePath,
179
+ projectId,
180
+ port,
181
+ host,
182
+ mongodbUrl,
183
+ jwtSecret,
184
+ name,
185
+ args,
186
+ user
187
+ }: {
188
+ basePath: string
189
+ projectId: string
190
+ port?: number
191
+ host?: string
192
+ mongodbUrl: string
193
+ jwtSecret: string
194
+ name: string
195
+ args: unknown[]
196
+ user?: Record<string, unknown>
197
+ }) => {
198
+ process.env.FLOWERBASE_APP_PATH = basePath
199
+ const { initialize } = await import('../index')
200
+ await initialize({
201
+ projectId,
202
+ port,
203
+ mongodbUrl,
204
+ jwtSecret,
205
+ host,
206
+ basePath
207
+ })
208
+
209
+ const app = StateManager.select('app')
210
+ const functionsList = StateManager.select('functions')
211
+ const rulesList = StateManager.select('rules')
212
+ const services = StateManager.select('services')
213
+
214
+ try {
215
+ const currentFunction = functionsList[name]
216
+ if (!currentFunction) {
217
+ throw new Error(`Function "${name}" does not exist`)
218
+ }
219
+
220
+ return await GenerateContext({
221
+ args,
222
+ app,
223
+ rules: rulesList,
224
+ user,
225
+ currentFunction: currentFunction as Function,
226
+ functionsList,
227
+ services,
228
+ runAsSystem: true,
229
+ request: {
230
+ ip: 'cli',
231
+ method: 'CLI',
232
+ url: 'cli://local',
233
+ host: 'cli',
234
+ id: 'cli',
235
+ hostname: 'cli'
236
+ }
237
+ })
238
+ } finally {
239
+ await app.close()
240
+ }
241
+ }
242
+
243
+ const main = async () => {
244
+ const argv = process.argv.slice(2)
245
+ const options = parseArgs(argv)
246
+ if (!options) return
247
+
248
+ if (!options.name) {
249
+ console.error('Missing required --name')
250
+ console.log(HELP_TEXT.trim())
251
+ process.exit(1)
252
+ }
253
+
254
+ const basePath = options.basePath ? path.resolve(options.basePath) : process.cwd()
255
+ if (!options.mongodbUrl) {
256
+ throw new Error('Missing --mongodb-url (or MONGODB_URL)')
257
+ }
258
+ if (!options.jwtSecret) {
259
+ throw new Error('Missing --jwt-secret (or JWT_SECRET)')
260
+ }
261
+ const argsInput = await loadArgs(argv)
262
+ const args = normalizeArguments(argsInput)
263
+ const user = await loadUser(argv)
264
+ const projectId = options.appId || path.basename(basePath)
265
+ const result = await executeLocal({
266
+ basePath,
267
+ projectId,
268
+ port: options.port,
269
+ host: options.host,
270
+ mongodbUrl: options.mongodbUrl,
271
+ jwtSecret: options.jwtSecret,
272
+ name: options.name,
273
+ args,
274
+ user
275
+ })
276
+ if (typeof result === 'string') {
277
+ console.log(result)
278
+ } else {
279
+ console.log(JSON.stringify(result))
280
+ }
281
+ }
282
+
283
+ main().catch((error) => {
284
+ const message = error instanceof Error ? error.message : String(error)
285
+ console.error(message)
286
+ process.exit(1)
287
+ })
@@ -53,7 +53,7 @@ export const registerPlugins = async ({
53
53
  registersConfig.forEach(({ plugin, options, pluginName }) => {
54
54
  try {
55
55
  register(plugin, options)
56
- console.log('registration COMPLETED --->', pluginName)
56
+ // console.log('registration COMPLETED --->', pluginName)
57
57
  } catch (e) {
58
58
  console.log('Registration FAILED --->', pluginName)
59
59
  console.log('Error --->', e)