@flowerforce/flowerbase 1.8.2 → 1.8.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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## 1.8.3 (2026-04-02)
2
+
3
+
4
+ ### 🩹 Fixes
5
+
6
+ - updateOne options ([5b16467](https://github.com/flowerforce/flowerbase/commit/5b16467))
7
+
1
8
  ## 1.8.2 (2026-03-31)
2
9
 
3
10
 
package/README.md CHANGED
@@ -616,35 +616,80 @@ Once deployed, you'll receive a public URL (e.g. https://your-app-name.up.exampl
616
616
 
617
617
  >This URL should be used as the base URL in your frontend application, as explained in the next section.
618
618
 
619
- ## 🌐 Frontend Setup – Realm SDK in React (Example)
619
+ ## 🌐 Frontend Setup – `@flowerforce/flowerbase-client` (Recommended)
620
620
 
621
- You can use the official `realm-web` SDK to integrate MongoDB Realm into a React application.
622
- This serves as a sample setup — similar logic can be applied using other official Realm SDKs **(e.g. React Native, Node, or Flutter)**.
623
-
624
- ### 📦 Install Realm SDK
621
+ For frontend and mobile projects, you can use the dedicated Flowerbase client:
625
622
 
626
623
  ```bash
627
- npm install realm-web
624
+ npm install @flowerforce/flowerbase-client
628
625
  ```
629
626
 
630
- ### ⚙️ Configure Realm in React
631
-
632
- Create a file to initialize and export the Realm App instance:
627
+ ### ⚙️ Configure client app
633
628
 
634
629
  ```ts
635
- // src/realm/realmApp.ts
630
+ import { App, Credentials } from '@flowerforce/flowerbase-client'
636
631
 
637
- import * as Realm from "realm-web";
632
+ const app = new App({
633
+ id: 'your-app-id',
634
+ baseUrl: 'https://your-deployed-backend-url.com',
635
+ timeout: 10000
636
+ })
638
637
 
639
- // Replace with your actual Realm App ID and your deployed backend URL
640
- const app = new Realm.App({
641
- id: "your-realm-app-id", // e.g., my-app-abcde
642
- baseUrl: "https://your-deployed-backend-url.com" // e.g., https://your-app-name.up.example.app
643
- });
638
+ await app.logIn(Credentials.emailPassword('user@example.com', 'secret'))
639
+ ```
640
+
641
+ ### 📦 Common client operations
642
+
643
+ ```ts
644
+ const user = app.currentUser
645
+ if (!user) throw new Error('User not logged in')
646
+
647
+ const profile = await user.functions.getProfile()
644
648
 
645
- export default app;
649
+ const todos = user.mongoClient('mongodb-atlas')
650
+ .db('my-db')
651
+ .collection('todos')
646
652
 
653
+ await todos.insertOne({ title: 'Ship docs update', done: false })
654
+ const openTodos = await todos.find({ done: false })
647
655
  ```
648
656
 
649
- >🔗 The baseUrl should point to the backend URL you deployed earlier using Flowerbase.
650
- This tells the frontend SDK where to send authentication and data requests.
657
+ `@flowerforce/flowerbase-client` supports:
658
+ - local-userpass / anon-user / custom-function authentication
659
+ - function calls (`user.functions.<name>(...)`)
660
+ - MongoDB operations via `user.mongoClient('mongodb-atlas')`
661
+ - change streams with `watch()` async iterator
662
+ - BSON/EJSON interoperability (`ObjectId`, `Date`, etc.)
663
+
664
+ ## 💡 Use Cases by Feature
665
+
666
+ ### 🔐 Authentication
667
+ - Registration and login flows for SaaS dashboards using `local-userpass`.
668
+ - Guest sessions for trial users with `anon-user`, then account upgrade with full registration.
669
+ - Delegated enterprise login with `custom-function` auth when credentials must be validated by external identity logic.
670
+
671
+ ### 🔒 Rules
672
+ - Multi-tenant isolation where each user can only read/write documents of their own workspace.
673
+ - Field-level protection to hide private fields (for example billing or internal notes) from non-admin users.
674
+
675
+ ### ⚙️ Functions
676
+ - Centralized business logic (pricing, counters, workflows) called from web and mobile clients.
677
+ - Privileged server-side tasks invoked with `run_as_system` to perform safe internal operations.
678
+
679
+ ### 🔔 Triggers
680
+ - Audit logging on insert/update/delete events into an activity collection.
681
+ - Scheduled jobs (for example nightly cleanup, reminder generation, data aggregation).
682
+ - Auth lifecycle reactions (welcome email on user creation, cleanup on user deletion).
683
+
684
+ ### 🌐 HTTP Endpoints
685
+ - Public webhook ingestion from third-party systems.
686
+ - Protected custom APIs for backoffice actions not exposed as direct database operations.
687
+
688
+ ### 📡 `flowerbase-client`
689
+ - Real-time UI updates in task boards using `collection.watch()` change streams.
690
+ - Frontend data access with Realm-style API surface to minimize integration complexity.
691
+ - Shared client usage across web and React Native projects with consistent auth/session behavior.
692
+
693
+ ### 🖥 Monitoring UI
694
+ - Live inspection of function invocations, endpoint calls, and trigger executions in staging/production.
695
+ - Fast troubleshooting with event stream filters and user/session search tools.
@@ -18,7 +18,7 @@ export declare const executeQuery: ({ currentMethod, query, update, filter, proj
18
18
  countDocuments: () => Promise<number>;
19
19
  deleteOne: () => Promise<import("mongodb").DeleteResult>;
20
20
  insertOne: () => Promise<import("mongodb").InsertOneResult<Document>>;
21
- updateOne: () => Promise<unknown> | import("mongodb").FindCursor<any> | import("mongodb").ChangeStream<Document, Document> | import("mongodb").AggregationCursor<Document>;
21
+ updateOne: () => Promise<import("mongodb").UpdateResult<Document>>;
22
22
  findOneAndUpdate: () => Promise<Document | null>;
23
23
  aggregate: () => Promise<Document[]>;
24
24
  insertMany: () => Promise<import("mongodb").InsertManyResult<Document>>;
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/features/functions/utils.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAGlC,OAAO,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAE3D;;;GAGG;AACH,eAAO,MAAM,aAAa,GAAU,gBAAuB,KAAG,OAAO,CAAC,SAAS,CAwB9E,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,YAAY,GAAU,2HAYhC,kBAAkB;;;;;;;;;;;;;EAiGpB,CAAA"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/features/functions/utils.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAGlC,OAAO,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAE3D;;;GAGG;AACH,eAAO,MAAM,aAAa,GAAU,gBAAuB,KAAG,OAAO,CAAC,SAAS,CAwB9E,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,YAAY,GAAU,2HAYhC,kBAAkB;;;;;;;;;;;;;EAsGpB,CAAA"}
@@ -100,7 +100,7 @@ const executeQuery = (_a) => __awaiter(void 0, [_a], void 0, function* ({ curren
100
100
  countDocuments: () => currentMethod(bson_1.EJSON.deserialize(resolvedQuery), parsedOptions),
101
101
  deleteOne: () => currentMethod(bson_1.EJSON.deserialize(resolvedQuery), parsedOptions),
102
102
  insertOne: () => currentMethod(bson_1.EJSON.deserialize(document)),
103
- updateOne: () => currentMethod(bson_1.EJSON.deserialize(resolvedQuery), bson_1.EJSON.deserialize(resolvedUpdate)),
103
+ updateOne: () => currentMethod(bson_1.EJSON.deserialize(resolvedQuery), bson_1.EJSON.deserialize(resolvedUpdate), parsedOptions),
104
104
  findOneAndUpdate: () => currentMethod(bson_1.EJSON.deserialize(resolvedQuery), bson_1.EJSON.deserialize(resolvedUpdate), parsedOptions),
105
105
  aggregate: () => __awaiter(void 0, void 0, void 0, function* () {
106
106
  return (yield currentMethod(bson_1.EJSON.deserialize(pipeline), {}, // TODO -> ADD OPTIONS
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowerforce/flowerbase",
3
- "version": "1.8.2",
3
+ "version": "1.8.3",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -0,0 +1,33 @@
1
+ import { executeQuery } from '../utils'
2
+
3
+ describe('executeQuery', () => {
4
+ it('passes parsed options to updateOne', async () => {
5
+ const currentMethod = jest.fn().mockResolvedValue({
6
+ acknowledged: true,
7
+ matchedCount: 0,
8
+ modifiedCount: 0,
9
+ upsertedCount: 1
10
+ })
11
+
12
+ const operators = await executeQuery({
13
+ currentMethod,
14
+ query: { ownerUserId: 'user-1' },
15
+ update: {
16
+ $set: { locale: 'it-IT' },
17
+ $setOnInsert: { scope: 'workspace' }
18
+ },
19
+ options: { upsert: true }
20
+ } as any)
21
+
22
+ await operators.updateOne()
23
+
24
+ expect(currentMethod).toHaveBeenCalledWith(
25
+ { ownerUserId: 'user-1' },
26
+ {
27
+ $set: { locale: 'it-IT' },
28
+ $setOnInsert: { scope: 'workspace' }
29
+ },
30
+ { upsert: true }
31
+ )
32
+ })
33
+ })
@@ -122,7 +122,12 @@ export const executeQuery = async ({
122
122
  (currentMethod as ReturnType<GetOperatorsFunction>['insertOne'])(
123
123
  EJSON.deserialize(document)
124
124
  ),
125
- updateOne: () => currentMethod(EJSON.deserialize(resolvedQuery), EJSON.deserialize(resolvedUpdate)),
125
+ updateOne: () =>
126
+ (currentMethod as ReturnType<GetOperatorsFunction>['updateOne'])(
127
+ EJSON.deserialize(resolvedQuery),
128
+ EJSON.deserialize(resolvedUpdate),
129
+ parsedOptions
130
+ ),
126
131
  findOneAndUpdate: () =>
127
132
  (currentMethod as ReturnType<GetOperatorsFunction>['findOneAndUpdate'])(
128
133
  EJSON.deserialize(resolvedQuery),