From f26b3edf164bb1d063e46d04130c5236d66b8345 Mon Sep 17 00:00:00 2001 From: M-Taha-Dev Date: Wed, 23 Jul 2025 07:21:27 +0500 Subject: [PATCH 01/15] added docs for appcheck and firebase functions --- pages/firebase/_meta.json | 6 + pages/firebase/firebase-appcheck.mdx | 269 +++++++++++++++++ pages/firebase/firebase-functions.mdx | 402 ++++++++++++++++++++++++++ 3 files changed, 677 insertions(+) create mode 100644 pages/firebase/firebase-appcheck.mdx create mode 100644 pages/firebase/firebase-functions.mdx diff --git a/pages/firebase/_meta.json b/pages/firebase/_meta.json index be8d6b3..0b9d8ca 100644 --- a/pages/firebase/_meta.json +++ b/pages/firebase/_meta.json @@ -4,5 +4,11 @@ }, "firestore-operations": { "title": "Firestore" + }, + "firebase-functions": { + "title": "Firebase Functions" + }, + "firebase-appcheck": { + "title": "Firebase Appcheck" } } diff --git a/pages/firebase/firebase-appcheck.mdx b/pages/firebase/firebase-appcheck.mdx new file mode 100644 index 0000000..7b93e83 --- /dev/null +++ b/pages/firebase/firebase-appcheck.mdx @@ -0,0 +1,269 @@ +import { Callout } from 'nextra/components' + +# Firebase App Check Configuration + +[Firebase App Check](https://firebase.google.com/docs/app-check) helps protect your API resources from abuse by preventing unauthorized clients from accessing your backend resources. Ensemble platform provides seamless integration with Firebase App Check, ensuring that only legitimate requests from your verified app can access your Firebase services. + +Unlike traditional API security measures, App Check provides automatic app verification that works transparently with your existing Firebase services. App Check is an excellent security addition for Ensemble applications because it provides automatic app verification, protection against abuse, seamless integration with Firebase services, and minimal performance impact. + +Now, let's dive into configuring Firebase App Check for our Ensemble application: + + + App Check integration requires proper Firebase configuration. Ensure your Firebase project is set up before proceeding. Learn how to configure it [here](pages/firebase/configuration.mdx). + + +- To get hands-on experience with App Check configuration, check the live example on [Ensemble Studio](https://studio.ensembleui.com/app/e24402cb-75e2-404c-866c-29e6c3dd7992/screen/W7b1n4a1LVoQgLyAie4F) + +## 1. Environment Configuration + +To enable App Check security in your Ensemble application, you'll need to configure environment variables and Firebase service files. Follow these steps to set up the necessary configuration: + +### Enabling App Check +Add an environment variable named `firebase_app_check` and set its value to `true` to enforce App Check security across your application. + +**Example**: +```bash +firebase_app_check=true +``` +**Explanation**: +- `firebase_app_check=true`: Enables App Check verification for all Firebase API calls in your Ensemble application. + +### Firebase Service Files Configuration +Configure the `firebase_config` environment variable with platform-specific configuration extracted from your Firebase service files. + +**Example**: +```json +{ + "web": { + "apiKey": "your-web-api-key", + "authDomain": "your-project.firebaseapp.com", + "projectId": "your-project-id", + "storageBucket": "your-project.appspot.com", + "messagingSenderId": "123456789", + "appId": "1:123456789:web:abcdef123456" + }, + "android": { + "apiKey": "your-android-api-key", + "appId": "1:123456789:android:abcdef123456", + "messagingSenderId": "123456789", + "projectId": "your-project-id", + "storageBucket": "your-project.appspot.com" + }, + "ios": { + "apiKey": "your-ios-api-key", + "appId": "1:123456789:ios:abcdef123456", + "messagingSenderId": "123456789", + "projectId": "your-project-id", + "storageBucket": "your-project.appspot.com", + "iosBundleId": "com.your.app.bundle" + } +} +``` + +**Explanation**: +- Extract configuration values from your downloaded Firebase service files (`google-services.json` for Android, `GoogleService-Info.plist` for iOS). +- Each platform section contains the necessary keys for App Check verification. + + + Download the required service files from your Firebase console: `google-services.json` for Android and `GoogleService-Info.plist` for iOS before configuring the environment variable. + + +## 2. Types of App Check Operations + +App Check provides different verification methods for different platforms and environments. Here's a breakdown of the main operations and configurations: + +### Debug Token Setup: +Debug tokens are essential for development and testing environments where app verification might not work as expected. + +1. **Example (Android Debug Token Extraction)**: +```bash +# Run the application +flutter run + +# Extract debug token from logs +adb logcat | grep "App Check" +``` +**Explanation**: +- `flutter run`: Starts your application in debug mode. +- `adb logcat | grep "App Check"`: Filters log output to show App Check debug token. +- Look for output like: `App Check debug token: 12345678-ABCD-EFGH-IJKL-123456789012` + +2. **Example (iOS Debug Token Configuration)**: +```yaml +# In Xcode scheme configuration +Environment Variables: + FIRDebugEnabled: YES + FIRAppCheckDebugEnabled: YES +``` +**Explanation**: +- `FIRDebugEnabled`: Enables Firebase debug logging. +- `FIRAppCheckDebugEnabled`: Specifically enables App Check debug token generation. + +### Production Providers: +Configure App Check providers for production environments with enhanced security. + +1. **Example (Android Production Setup)**: +```yaml +# Firebase Console Configuration +Provider: Play Integrity +Status: Enabled +Apps: com.yourapp.package +``` + +2. **Example (iOS Production Setup)**: +```yaml +# Firebase Console Configuration +Provider: App Attest +Fallback: DeviceCheck +Status: Enabled +Apps: com.yourapp.bundle +``` + +3. **Example (Web Production Setup)**: +```yaml +# Firebase Console Configuration +Provider: reCAPTCHA v3 +Site Key: your-recaptcha-site-key +Domains: yourdomain.com +``` + +### Conditional App Check: +Control App Check usage on a per-API basis depending on your security requirements. + +1. **Example (Secure Firebase Function)**: +```yaml +secureFunction: + type: firebaseFunction + name: sensitiveOperation + # App Check enabled by default when firebase_app_check=true + data: + userId: ${currentUser.id} + operation: transfer +``` + +2. **Example (Public Firebase Function)**: +```yaml +publicFunction: + type: firebaseFunction + name: getPublicData + useAppcheck: false # Explicitly disable App Check + data: + category: news + limit: 10 +``` + +3. **Example (Environment-Conditional App Check)**: +```yaml +conditionalFunction: + inputs: + - userId + - isProduction + type: firebaseFunction + name: getUserData + useAppcheck: ${isProduction} # Use App Check only in production + data: + userId: ${userId} +``` + +**Explanation**: +- `useAppcheck: false`: Explicitly disables App Check for public endpoints. +- `useAppcheck: ${isProduction}`: Conditionally enables App Check based on environment variables. +- Default behavior when `firebase_app_check=true` is to enable App Check for all Firebase operations. + +## 3. Response and Monitoring of App Check Operations + +When performing Firebase operations with App Check enabled, you can monitor verification status and handle failures appropriately. Below are examples demonstrating how to handle App Check responses and implement monitoring. + +### 1. Making API calls with App Check monitoring: +```yaml +invokeAPI: + name: secureFirestoreOperation + inputs: + userId: ${userID} + onResponse: + executeCode: + body: |- + console.log('App Check verification successful'); + analytics.logEvent('app_check_success', { + operation: 'firestore_query' + }); + onError: + executeCode: + body: |- + console.log('App Check verification failed:', response.error); + analytics.logEvent('app_check_failure', { + operation: 'firestore_query', + error_code: response.error.code + }); +``` + +### 2. Using App Check status in UI components: +```yaml +Column: + children: + - Column: + styles: + visible: '${secureFirestoreOperation.isLoading ? true : false}' + children: + - Progress: + display: circular + - Text: + text: "Verifying app authenticity..." + - Column: + styles: + visible: '${secureFirestoreOperation.isSuccess ? true : false}' + item-template: + data: ${secureFirestoreOperation.body.documents} + name: item + template: + Text: + text: ${item.name} + - Column: + styles: + visible: '${secureFirestoreOperation.isError ? true : false}' + children: + - Text: + text: "App verification failed. Please update your app." + styles: + color: red +``` + +### 3. App Check status monitoring: +```yaml +Button: + label: Check App Security Status + onTap: + invokeAPI: + name: checkAppCheckStatus + onResponse: + executeCode: + body: |- + var isVerified = response.body.appCheckVerified || false; + statusIndicator.styles.backgroundColor = isVerified ? 'green' : 'red'; + statusText.text = isVerified ? 'App Verified' : 'Verification Failed'; +``` + +**Explanation**: +- The first child `Column` shows a loading state with an App Check verification message. +- The second child `Column` displays data only when App Check verification succeeds. +- The third child `Column` shows a user-friendly error message when App Check fails. +- The monitoring example demonstrates how to check and display App Check verification status. + +## 4. Troubleshooting Common App Check Issues + +### Debug Token Issues + +**Debug Token Not Working**: Ensure the token is correctly copied without extra spaces, verify it's added to the correct app in Firebase Console, and check that debug environment variables are properly set. + + +### Production Verification Failures + +**App Check Failing in Production**: Verify Play Integrity/App Attest is properly configured, check that your app is signed with correct certificates, and ensure your app is published or in internal testing. + + +### Web Configuration Issues + +**Web App Check Issues**: Verify reCAPTCHA configuration and site keys, check that your domain is whitelisted, and ensure the reCAPTCHA script loads correctly. + + +By implementing Firebase App Check with these operations, you can significantly enhance the security of your Ensemble application. App Check's real-time verification capabilities and seamless integration make it a powerful tool for protecting your Firebase resources from unauthorized access and abuse. \ No newline at end of file diff --git a/pages/firebase/firebase-functions.mdx b/pages/firebase/firebase-functions.mdx new file mode 100644 index 0000000..61547c0 --- /dev/null +++ b/pages/firebase/firebase-functions.mdx @@ -0,0 +1,402 @@ +--- +title: "Calling Firebase Functions APIs" +description: "Learn how to integrate and call Firebase Functions in your Ensemble applications" +--- + +import { Callout } from 'nextra/components' + +# Calling Firebase Functions APIs + +Firebase Functions allows you to run backend code in response to events triggered by Firebase features and HTTPS requests. Ensemble platform provides seamless integration with Firebase Functions, enabling you to call serverless functions from your app effortlessly. + +Unlike traditional server setups, Firebase Functions offers a serverless architecture that automatically scales based on demand. Firebase Functions is an excellent choice for Ensemble applications because it provides automatic scaling, secure execution environment, easy deployment, and simplified backend logic without server management. + +Now, let's dive into implementing Firebase Functions in our Ensemble application: + + +Operations on Firebase Functions won't work unless we have configured our Ensemble application with Firebase. Learn how to configure it here. + + + +To get hands-on experience with Firebase Functions operations, check the live example on Ensemble Studio. + + +## 1. Environment Configuration + +### Setting Up API Providers +To use Firebase Functions, create an environment variable named `api_providers` and add "firebase" to it. + + +**Note:** You can use multiple api_providers by using comma-separated values (e.g., firestore,firebase) + + +**Example:** +```bash +api_providers=firestore,firebase +``` + +## 2. Types of Firebase Functions Operations + +Firebase Functions offers various ways to interact with your serverless backend. Here's a breakdown of core operations along with demo API calls for our Ensemble app: + +### Basic Function Call: +This operation calls a Firebase Function without any parameters. + +**Example (Simple function call):** +```yaml +testFunction: + type: firebaseFunction + name: helloWorld +``` + +**Explanation:** +- `type: firebaseFunction`: Specifies that the operation is for Firebase Functions +- `name`: The name of the Firebase Function to call + +### Function Call with Data: +This operation calls a Firebase Function with input parameters. + +**Example (Function with parameters):** +```yaml +createUser: + inputs: + - email + - displayName + type: firebaseFunction + name: createCustomUser + data: + email: ${email} + displayName: ${displayName} + role: user + createdAt: ${new Date().toISOString()} +``` + +**Explanation:** +- `inputs`: Dynamic variables that can be passed to the function +- `data`: The payload sent to the Firebase Function +- Values can be static or use dynamic variables with `${variableName}` syntax + +### Function Call with Custom Headers: +You can add custom headers to your Firebase Function calls. + +**Example (With custom headers):** +```yaml +authenticatedCall: + inputs: + - authToken + - userId + type: firebaseFunction + name: getUserProfile + headers: + Authorization: Bearer ${authToken} + Content-Type: application/json + X-Custom-Header: ensemble-app + data: + userId: ${userId} + includePrivateData: true +``` + +**Explanation:** +- `headers`: Custom HTTP headers to include with the request +- Useful for authentication tokens, content types, or custom application headers + +### Function Call with HTTP Methods: +By default, Firebase Functions use POST method, but you can specify other methods if your function supports them. + +**Example (GET request):** +```yaml +getPublicData: + type: firebaseFunction + name: getNews + method: GET + query: + category: technology + limit: 10 +``` + +**Explanation:** +- `method`: HTTP method for the request (GET, POST, PUT, DELETE) +- `query`: Query parameters for GET requests + +## 3. Response Handling of Firebase Functions + +When performing Firebase Functions operations, you may need to handle responses and errors appropriately. Below are common patterns for handling API responses in your Ensemble app. + +### 1. Making an API call: +```yaml +invokeAPI: + name: myFunction + inputs: + userId: ${userID} +``` + +You can also use `onResponse` & `onError` on Firebase Function API calls and perform operations on the response. + +### 2. Complete Function Definition with Response Handling: +```yaml +API: + getUserData: + inputs: + - userId + type: firebaseFunction + name: fetchUserProfile + data: + userId: ${userId} + includeStats: true + onResponse: + executeCode: + body: |- + console.log('User data fetched successfully'); + console.log(response.body); + userNameText.text = response.body.user.name; + userEmailText.text = response.body.user.email; + onError: + executeCode: + body: |- + console.log('Failed to fetch user data'); + console.log(response.error); + errorText.text = "Error: " + response.error.message; + + sendNotification: + inputs: + - message + - recipientId + type: firebaseFunction + name: sendPushNotification + data: + message: ${message} + recipientId: ${recipientId} + priority: high + onResponse: + executeCode: + body: |- + console.log('Notification sent successfully'); + statusText.text = "Notification sent!"; + statusText.styles = { color: "green" }; + onError: + executeCode: + body: |- + console.log('Failed to send notification'); + statusText.text = "Failed to send notification"; + statusText.styles = { color: "red" }; +``` + +### 3. Using response in UI Components: +To display data based on the API call's state (loading, success, error), you can use the following structure: + +```yaml +Column: + children: + - Column: + styles: + visible: '${getUserData.isLoading ? true : false}' + children: + - Progress: + display: circular + - Text: + text: "Loading user data..." + - Column: + styles: + visible: '${getUserData.isSuccess ? true : false}' + children: + - Text: + id: userNameText + text: "Name: ${getUserData.body.user.name}" + - Text: + id: userEmailText + text: "Email: ${getUserData.body.user.email}" + - Text: + text: "Last Login: ${getUserData.body.user.lastLogin}" + - Column: + styles: + visible: '${getUserData.isError ? true : false}' + children: + - Text: + id: errorText + text: "Failed to load user data" + styles: + color: red +``` + +**Explanation:** +- The first child Column is visible only when the API call is loading (`visible: '${getUserData.isLoading ? true : false}'`). It shows a circular progress indicator. +- The second child Column is visible only when the API call is successful (`visible: '${getUserData.isSuccess ? true : false}'`). It displays the function result. +- The third child Column is visible only when there is an error (`visible: '${getUserData.isError ? true : false}'`). It shows an error message. + +### 4. Using Function Response in Forms: +```yaml +Form: + children: + - TextInput: + id: emailInput + label: Email Address + required: true + - TextInput: + id: messageInput + label: Message + multiline: true + - Button: + label: Send Email + onTap: + invokeAPI: + name: sendEmail + inputs: + email: ${emailInput.value} + message: ${messageInput.value} + - Text: + id: emailResult + styles: + visible: '${sendEmail.isSuccess ? true : false}' + color: green + text: "Email sent successfully!" + - Text: + id: emailError + styles: + visible: '${sendEmail.isError ? true : false}' + color: red + text: "Failed to send email: ${sendEmail.error.message}" +``` + +### 5. Using response in ListView: +```yaml +ListView: + item-template: + data: ${getNotifications.body.notifications} + name: notification + template: + Card: + padding: 16 + margin: 8 + children: + - Text: + text: ${notification.title} + styles: + fontSize: 18 + fontWeight: bold + - Text: + text: ${notification.message} + styles: + fontSize: 14 + color: gray + - Text: + text: ${notification.timestamp} + styles: + fontSize: 12 + color: lightgray +``` + +## 4. Advanced Features + +### Batch Function Calls: +You can call multiple Firebase Functions sequentially or handle complex workflows. + +**Example (Sequential calls):** +```yaml +API: + processOrder: + inputs: + - orderId + type: firebaseFunction + name: validateOrder + data: + orderId: ${orderId} + onResponse: + invokeAPI: + name: chargePayment + inputs: + orderId: ${orderId} + amount: ${response.body.totalAmount} + + chargePayment: + inputs: + - orderId + - amount + type: firebaseFunction + name: processPayment + data: + orderId: ${orderId} + amount: ${amount} + onResponse: + invokeAPI: + name: sendConfirmation + inputs: + orderId: ${orderId} +``` + +### Error Handling with Retry Logic: +```yaml +API: + reliableFunction: + type: firebaseFunction + name: criticalOperation + data: + operation: important + onError: + executeCode: + body: |- + console.log('Function failed, implementing retry logic'); + if (response.error.code === 'timeout') { + // Retry after a delay + setTimeout(() => { + invokeAPI({ name: 'reliableFunction' }); + }, 2000); + } +``` + +## 5. Best Practices + +### Input Validation: +Always validate inputs before sending to Firebase Functions: + +```yaml +validateAndSubmit: + inputs: + - email + - password + type: firebaseFunction + name: createAccount + data: + email: ${email} + password: ${password} + condition: '${email.includes("@") && password.length >= 8}' + onResponse: + executeCode: + body: |- + console.log('Account created successfully'); + navigateToScreen('welcome'); + onError: + executeCode: + body: |- + console.log('Account creation failed'); + showErrorDialog(response.error.message); +``` + +### Loading States: +Provide clear feedback during function execution: + +```yaml +Button: + label: '${submitForm.isLoading ? "Processing..." : "Submit"}' + enabled: '${!submitForm.isLoading}' + onTap: + invokeAPI: + name: submitForm +``` + +## 6. Troubleshooting + +### Common Issues +- Ensure Firebase Functions are deployed and accessible +- Verify function names match exactly (case-sensitive) +- Check that the Firebase project is correctly configured +- Confirm internet connectivity for function calls + +### Debug Tips +- Use console.log in onResponse and onError handlers to inspect responses +- Check Firebase Console for function logs and error details +- Test functions independently using Firebase Console or Postman +- Verify function permissions and authentication requirements + + +By using these operations, you can efficiently call Firebase Functions from your Ensemble application. Firebase Functions' serverless architecture makes it a powerful solution for any backend logic your application needs. + \ No newline at end of file From cbbeaf6090f0bbf3abb974c0e2556c42e45b8336 Mon Sep 17 00:00:00 2001 From: M-Taha-Dev Date: Wed, 23 Jul 2025 07:29:20 +0500 Subject: [PATCH 02/15] fix 1 --- pages/firebase/firebase-appcheck.mdx | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/pages/firebase/firebase-appcheck.mdx b/pages/firebase/firebase-appcheck.mdx index 7b93e83..479781b 100644 --- a/pages/firebase/firebase-appcheck.mdx +++ b/pages/firebase/firebase-appcheck.mdx @@ -12,7 +12,6 @@ Now, let's dive into configuring Firebase App Check for our Ensemble application App Check integration requires proper Firebase configuration. Ensure your Firebase project is set up before proceeding. Learn how to configure it [here](pages/firebase/configuration.mdx). -- To get hands-on experience with App Check configuration, check the live example on [Ensemble Studio](https://studio.ensembleui.com/app/e24402cb-75e2-404c-866c-29e6c3dd7992/screen/W7b1n4a1LVoQgLyAie4F) ## 1. Environment Configuration @@ -99,33 +98,6 @@ Environment Variables: - `FIRDebugEnabled`: Enables Firebase debug logging. - `FIRAppCheckDebugEnabled`: Specifically enables App Check debug token generation. -### Production Providers: -Configure App Check providers for production environments with enhanced security. - -1. **Example (Android Production Setup)**: -```yaml -# Firebase Console Configuration -Provider: Play Integrity -Status: Enabled -Apps: com.yourapp.package -``` - -2. **Example (iOS Production Setup)**: -```yaml -# Firebase Console Configuration -Provider: App Attest -Fallback: DeviceCheck -Status: Enabled -Apps: com.yourapp.bundle -``` - -3. **Example (Web Production Setup)**: -```yaml -# Firebase Console Configuration -Provider: reCAPTCHA v3 -Site Key: your-recaptcha-site-key -Domains: yourdomain.com -``` ### Conditional App Check: Control App Check usage on a per-API basis depending on your security requirements. From 11361fdc21af899e8f80ed0987212158bfec358a Mon Sep 17 00:00:00 2001 From: M-Taha-Dev Date: Wed, 23 Jul 2025 07:40:30 +0500 Subject: [PATCH 03/15] fix -2 --- pages/firebase/firebase-appcheck.mdx | 2 +- pages/firebase/firebase-functions.mdx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pages/firebase/firebase-appcheck.mdx b/pages/firebase/firebase-appcheck.mdx index 479781b..b2c9e5c 100644 --- a/pages/firebase/firebase-appcheck.mdx +++ b/pages/firebase/firebase-appcheck.mdx @@ -9,7 +9,7 @@ Unlike traditional API security measures, App Check provides automatic app verif Now, let's dive into configuring Firebase App Check for our Ensemble application: - App Check integration requires proper Firebase configuration. Ensure your Firebase project is set up before proceeding. Learn how to configure it [here](pages/firebase/configuration.mdx). + App Check integration requires proper Firebase configuration. Ensure your Firebase project is set up before proceeding. Learn how to configure it [here](pages/firebase/firestore.mdx#2-types-of-firestore-operations). diff --git a/pages/firebase/firebase-functions.mdx b/pages/firebase/firebase-functions.mdx index 61547c0..1075c45 100644 --- a/pages/firebase/firebase-functions.mdx +++ b/pages/firebase/firebase-functions.mdx @@ -14,7 +14,7 @@ Unlike traditional server setups, Firebase Functions offers a serverless archite Now, let's dive into implementing Firebase Functions in our Ensemble application: -Operations on Firebase Functions won't work unless we have configured our Ensemble application with Firebase. Learn how to configure it here. + Firebase function integration requires proper Firebase configuration. Ensure your Firebase project is set up before proceeding. Learn how to configure it [here](pages/firebase/firestore.mdx#2-types-of-firestore-operations). From 4e699eb1016791c0b76288c0246d2f5b9b30f586 Mon Sep 17 00:00:00 2001 From: M-Taha-Dev Date: Wed, 23 Jul 2025 07:49:50 +0500 Subject: [PATCH 04/15] fix 3 --- pages/firebase/firebase-appcheck.mdx | 2 +- pages/firebase/firebase-functions.mdx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pages/firebase/firebase-appcheck.mdx b/pages/firebase/firebase-appcheck.mdx index b2c9e5c..b67c721 100644 --- a/pages/firebase/firebase-appcheck.mdx +++ b/pages/firebase/firebase-appcheck.mdx @@ -9,7 +9,7 @@ Unlike traditional API security measures, App Check provides automatic app verif Now, let's dive into configuring Firebase App Check for our Ensemble application: - App Check integration requires proper Firebase configuration. Ensure your Firebase project is set up before proceeding. Learn how to configure it [here](pages/firebase/firestore.mdx#2-types-of-firestore-operations). + App Check integration requires proper Firebase configuration. Ensure your Firebase project is set up before proceeding. Learn how to configure it [here](firestore-operations.mdx#2-types-of-firestore-operations). diff --git a/pages/firebase/firebase-functions.mdx b/pages/firebase/firebase-functions.mdx index 1075c45..ec53287 100644 --- a/pages/firebase/firebase-functions.mdx +++ b/pages/firebase/firebase-functions.mdx @@ -14,7 +14,7 @@ Unlike traditional server setups, Firebase Functions offers a serverless archite Now, let's dive into implementing Firebase Functions in our Ensemble application: - Firebase function integration requires proper Firebase configuration. Ensure your Firebase project is set up before proceeding. Learn how to configure it [here](pages/firebase/firestore.mdx#2-types-of-firestore-operations). + Firebase function integration requires proper Firebase configuration. Ensure your Firebase project is set up before proceeding. Learn how to configure it [here](firestore-operations.mdx#2-types-of-firestore-operations). From bd7e82276333f16835872ee4581bb9d225f865e5 Mon Sep 17 00:00:00 2001 From: M-Taha-Dev Date: Wed, 23 Jul 2025 07:53:55 +0500 Subject: [PATCH 05/15] fix 4 --- pages/firebase/firebase-appcheck.mdx | 2 +- pages/firebase/firebase-functions.mdx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pages/firebase/firebase-appcheck.mdx b/pages/firebase/firebase-appcheck.mdx index b67c721..0845bef 100644 --- a/pages/firebase/firebase-appcheck.mdx +++ b/pages/firebase/firebase-appcheck.mdx @@ -9,7 +9,7 @@ Unlike traditional API security measures, App Check provides automatic app verif Now, let's dive into configuring Firebase App Check for our Ensemble application: - App Check integration requires proper Firebase configuration. Ensure your Firebase project is set up before proceeding. Learn how to configure it [here](firestore-operations.mdx#2-types-of-firestore-operations). + App Check integration requires proper Firebase configuration. Ensure your Firebase project is set up before proceeding. Learn how to configure it [here](configuration.mdx#2-types-of-firestore-operations). diff --git a/pages/firebase/firebase-functions.mdx b/pages/firebase/firebase-functions.mdx index ec53287..504166f 100644 --- a/pages/firebase/firebase-functions.mdx +++ b/pages/firebase/firebase-functions.mdx @@ -14,7 +14,7 @@ Unlike traditional server setups, Firebase Functions offers a serverless archite Now, let's dive into implementing Firebase Functions in our Ensemble application: - Firebase function integration requires proper Firebase configuration. Ensure your Firebase project is set up before proceeding. Learn how to configure it [here](firestore-operations.mdx#2-types-of-firestore-operations). + Firebase function integration requires proper Firebase configuration. Ensure your Firebase project is set up before proceeding. Learn how to configure it [here](configuration.mdx#2-types-of-firestore-operations). From 113a94578e9af47599e6a7b4c1b2615b4eaa0b6e Mon Sep 17 00:00:00 2001 From: M-Taha-Dev Date: Wed, 23 Jul 2025 07:59:24 +0500 Subject: [PATCH 06/15] fix 5 --- pages/firebase/firebase-appcheck.mdx | 2 +- pages/firebase/firebase-functions.mdx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pages/firebase/firebase-appcheck.mdx b/pages/firebase/firebase-appcheck.mdx index 0845bef..7ab88e1 100644 --- a/pages/firebase/firebase-appcheck.mdx +++ b/pages/firebase/firebase-appcheck.mdx @@ -9,7 +9,7 @@ Unlike traditional API security measures, App Check provides automatic app verif Now, let's dive into configuring Firebase App Check for our Ensemble application: - App Check integration requires proper Firebase configuration. Ensure your Firebase project is set up before proceeding. Learn how to configure it [here](configuration.mdx#2-types-of-firestore-operations). + App Check integration requires proper Firebase configuration. Ensure your Firebase project is set up before proceeding. Learn how to configure it [here](configuration.mdx#2-configure-ensemble-app). diff --git a/pages/firebase/firebase-functions.mdx b/pages/firebase/firebase-functions.mdx index 504166f..39f59c1 100644 --- a/pages/firebase/firebase-functions.mdx +++ b/pages/firebase/firebase-functions.mdx @@ -14,7 +14,7 @@ Unlike traditional server setups, Firebase Functions offers a serverless archite Now, let's dive into implementing Firebase Functions in our Ensemble application: - Firebase function integration requires proper Firebase configuration. Ensure your Firebase project is set up before proceeding. Learn how to configure it [here](configuration.mdx#2-types-of-firestore-operations). + Firebase function integration requires proper Firebase configuration. Ensure your Firebase project is set up before proceeding. Learn how to configure it [here](configuration.mdx#2-configure-ensemble-app). From 4b5be1eba33a20e81345fad0094fd9c1497cc961 Mon Sep 17 00:00:00 2001 From: M-Taha-Dev Date: Wed, 23 Jul 2025 08:43:40 +0500 Subject: [PATCH 07/15] fix 6 --- pages/firebase/firebase-appcheck.mdx | 140 +------------ pages/firebase/firebase-functions.mdx | 278 ++------------------------ 2 files changed, 24 insertions(+), 394 deletions(-) diff --git a/pages/firebase/firebase-appcheck.mdx b/pages/firebase/firebase-appcheck.mdx index 7ab88e1..7f95c58 100644 --- a/pages/firebase/firebase-appcheck.mdx +++ b/pages/firebase/firebase-appcheck.mdx @@ -9,16 +9,14 @@ Unlike traditional API security measures, App Check provides automatic app verif Now, let's dive into configuring Firebase App Check for our Ensemble application: - App Check integration requires proper Firebase configuration. Ensure your Firebase project is set up before proceeding. Learn how to configure it [here](configuration.mdx#2-configure-ensemble-app). + App Check integration requires proper Firebase configuration. Ensure your Firebase project is set up before proceeding. Learn how to configure it [here](pages/firebase/configuration.mdx#2-configure-ensemble-app). +- To get hands-on experience with App Check configuration, check the live example on [Ensemble Studio](https://studio.ensembleui.com/app/e24402cb-75e2-404c-866c-29e6c3dd7992/screen/W7b1n4a1LVoQgLyAie4F) ## 1. Environment Configuration -To enable App Check security in your Ensemble application, you'll need to configure environment variables and Firebase service files. Follow these steps to set up the necessary configuration: - -### Enabling App Check -Add an environment variable named `firebase_app_check` and set its value to `true` to enforce App Check security across your application. +To enable App Check security in your Ensemble application, add an environment variable named `firebase_app_check` and set its value to `true` to enforce App Check security across your application. **Example**: ```bash @@ -27,52 +25,13 @@ firebase_app_check=true **Explanation**: - `firebase_app_check=true`: Enables App Check verification for all Firebase API calls in your Ensemble application. -### Firebase Service Files Configuration -Configure the `firebase_config` environment variable with platform-specific configuration extracted from your Firebase service files. - -**Example**: -```json -{ - "web": { - "apiKey": "your-web-api-key", - "authDomain": "your-project.firebaseapp.com", - "projectId": "your-project-id", - "storageBucket": "your-project.appspot.com", - "messagingSenderId": "123456789", - "appId": "1:123456789:web:abcdef123456" - }, - "android": { - "apiKey": "your-android-api-key", - "appId": "1:123456789:android:abcdef123456", - "messagingSenderId": "123456789", - "projectId": "your-project-id", - "storageBucket": "your-project.appspot.com" - }, - "ios": { - "apiKey": "your-ios-api-key", - "appId": "1:123456789:ios:abcdef123456", - "messagingSenderId": "123456789", - "projectId": "your-project-id", - "storageBucket": "your-project.appspot.com", - "iosBundleId": "com.your.app.bundle" - } -} -``` - -**Explanation**: -- Extract configuration values from your downloaded Firebase service files (`google-services.json` for Android, `GoogleService-Info.plist` for iOS). -- Each platform section contains the necessary keys for App Check verification. - - - Download the required service files from your Firebase console: `google-services.json` for Android and `GoogleService-Info.plist` for iOS before configuring the environment variable. - - ## 2. Types of App Check Operations App Check provides different verification methods for different platforms and environments. Here's a breakdown of the main operations and configurations: ### Debug Token Setup: -Debug tokens are essential for development and testing environments where app verification might not work as expected. +Debug tokens are essential for development and testing environments where app verification might not work as expected. To run application with appcheck (Dev environment), you would need to register debug token in firebase console's appcheck section for your project. +For release, you will need to register SHA 256 token of your app with your respective platform (Appstore or Google Play). 1. **Example (Android Debug Token Extraction)**: ```bash @@ -102,7 +61,7 @@ Environment Variables: ### Conditional App Check: Control App Check usage on a per-API basis depending on your security requirements. -1. **Example (Secure Firebase Function)**: +**Example (Secure Firebase Function)**: ```yaml secureFunction: type: firebaseFunction @@ -113,34 +72,6 @@ secureFunction: operation: transfer ``` -2. **Example (Public Firebase Function)**: -```yaml -publicFunction: - type: firebaseFunction - name: getPublicData - useAppcheck: false # Explicitly disable App Check - data: - category: news - limit: 10 -``` - -3. **Example (Environment-Conditional App Check)**: -```yaml -conditionalFunction: - inputs: - - userId - - isProduction - type: firebaseFunction - name: getUserData - useAppcheck: ${isProduction} # Use App Check only in production - data: - userId: ${userId} -``` - -**Explanation**: -- `useAppcheck: false`: Explicitly disables App Check for public endpoints. -- `useAppcheck: ${isProduction}`: Conditionally enables App Check based on environment variables. -- Default behavior when `firebase_app_check=true` is to enable App Check for all Firebase operations. ## 3. Response and Monitoring of App Check Operations @@ -156,70 +87,13 @@ invokeAPI: executeCode: body: |- console.log('App Check verification successful'); - analytics.logEvent('app_check_success', { - operation: 'firestore_query' - }); + onError: executeCode: body: |- console.log('App Check verification failed:', response.error); - analytics.logEvent('app_check_failure', { - operation: 'firestore_query', - error_code: response.error.code - }); ``` -### 2. Using App Check status in UI components: -```yaml -Column: - children: - - Column: - styles: - visible: '${secureFirestoreOperation.isLoading ? true : false}' - children: - - Progress: - display: circular - - Text: - text: "Verifying app authenticity..." - - Column: - styles: - visible: '${secureFirestoreOperation.isSuccess ? true : false}' - item-template: - data: ${secureFirestoreOperation.body.documents} - name: item - template: - Text: - text: ${item.name} - - Column: - styles: - visible: '${secureFirestoreOperation.isError ? true : false}' - children: - - Text: - text: "App verification failed. Please update your app." - styles: - color: red -``` - -### 3. App Check status monitoring: -```yaml -Button: - label: Check App Security Status - onTap: - invokeAPI: - name: checkAppCheckStatus - onResponse: - executeCode: - body: |- - var isVerified = response.body.appCheckVerified || false; - statusIndicator.styles.backgroundColor = isVerified ? 'green' : 'red'; - statusText.text = isVerified ? 'App Verified' : 'Verification Failed'; -``` - -**Explanation**: -- The first child `Column` shows a loading state with an App Check verification message. -- The second child `Column` displays data only when App Check verification succeeds. -- The third child `Column` shows a user-friendly error message when App Check fails. -- The monitoring example demonstrates how to check and display App Check verification status. ## 4. Troubleshooting Common App Check Issues diff --git a/pages/firebase/firebase-functions.mdx b/pages/firebase/firebase-functions.mdx index 39f59c1..82d282c 100644 --- a/pages/firebase/firebase-functions.mdx +++ b/pages/firebase/firebase-functions.mdx @@ -76,48 +76,6 @@ createUser: - `data`: The payload sent to the Firebase Function - Values can be static or use dynamic variables with `${variableName}` syntax -### Function Call with Custom Headers: -You can add custom headers to your Firebase Function calls. - -**Example (With custom headers):** -```yaml -authenticatedCall: - inputs: - - authToken - - userId - type: firebaseFunction - name: getUserProfile - headers: - Authorization: Bearer ${authToken} - Content-Type: application/json - X-Custom-Header: ensemble-app - data: - userId: ${userId} - includePrivateData: true -``` - -**Explanation:** -- `headers`: Custom HTTP headers to include with the request -- Useful for authentication tokens, content types, or custom application headers - -### Function Call with HTTP Methods: -By default, Firebase Functions use POST method, but you can specify other methods if your function supports them. - -**Example (GET request):** -```yaml -getPublicData: - type: firebaseFunction - name: getNews - method: GET - query: - category: technology - limit: 10 -``` - -**Explanation:** -- `method`: HTTP method for the request (GET, POST, PUT, DELETE) -- `query`: Query parameters for GET requests - ## 3. Response Handling of Firebase Functions When performing Firebase Functions operations, you may need to handle responses and errors appropriately. Below are common patterns for handling API responses in your Ensemble app. @@ -154,236 +112,34 @@ API: executeCode: body: |- console.log('Failed to fetch user data'); - console.log(response.error); - errorText.text = "Error: " + response.error.message; + console.log(response.body); + errorText.text = "Error: " + response.body; - sendNotification: - inputs: - - message - - recipientId - type: firebaseFunction - name: sendPushNotification - data: - message: ${message} - recipientId: ${recipientId} - priority: high - onResponse: - executeCode: - body: |- - console.log('Notification sent successfully'); - statusText.text = "Notification sent!"; - statusText.styles = { color: "green" }; - onError: - executeCode: - body: |- - console.log('Failed to send notification'); - statusText.text = "Failed to send notification"; - statusText.styles = { color: "red" }; ``` ### 3. Using response in UI Components: -To display data based on the API call's state (loading, success, error), you can use the following structure: - -```yaml -Column: - children: - - Column: - styles: - visible: '${getUserData.isLoading ? true : false}' - children: - - Progress: - display: circular - - Text: - text: "Loading user data..." - - Column: - styles: - visible: '${getUserData.isSuccess ? true : false}' - children: - - Text: - id: userNameText - text: "Name: ${getUserData.body.user.name}" - - Text: - id: userEmailText - text: "Email: ${getUserData.body.user.email}" - - Text: - text: "Last Login: ${getUserData.body.user.lastLogin}" - - Column: - styles: - visible: '${getUserData.isError ? true : false}' - children: - - Text: - id: errorText - text: "Failed to load user data" - styles: - color: red -``` - -**Explanation:** -- The first child Column is visible only when the API call is loading (`visible: '${getUserData.isLoading ? true : false}'`). It shows a circular progress indicator. -- The second child Column is visible only when the API call is successful (`visible: '${getUserData.isSuccess ? true : false}'`). It displays the function result. -- The third child Column is visible only when there is an error (`visible: '${getUserData.isError ? true : false}'`). It shows an error message. +Firebase functions will work similar to simple http APIs and their responses can be used in UI Components: -### 4. Using Function Response in Forms: ```yaml -Form: - children: - - TextInput: - id: emailInput - label: Email Address - required: true - - TextInput: - id: messageInput - label: Message - multiline: true - - Button: - label: Send Email - onTap: - invokeAPI: - name: sendEmail - inputs: - email: ${emailInput.value} - message: ${messageInput.value} - - Text: - id: emailResult - styles: - visible: '${sendEmail.isSuccess ? true : false}' - color: green - text: "Email sent successfully!" - - Text: - id: emailError - styles: - visible: '${sendEmail.isError ? true : false}' - color: red - text: "Failed to send email: ${sendEmail.error.message}" -``` - -### 5. Using response in ListView: -```yaml -ListView: - item-template: - data: ${getNotifications.body.notifications} - name: notification - template: - Card: - padding: 16 - margin: 8 - children: - - Text: - text: ${notification.title} - styles: - fontSize: 18 - fontWeight: bold - - Text: - text: ${notification.message} - styles: - fontSize: 14 - color: gray - - Text: - text: ${notification.timestamp} - styles: - fontSize: 12 - color: lightgray -``` -## 4. Advanced Features - -### Batch Function Calls: -You can call multiple Firebase Functions sequentially or handle complex workflows. - -**Example (Sequential calls):** -```yaml -API: - processOrder: - inputs: - - orderId - type: firebaseFunction - name: validateOrder - data: - orderId: ${orderId} - onResponse: - invokeAPI: - name: chargePayment - inputs: - orderId: ${orderId} - amount: ${response.body.totalAmount} - - chargePayment: - inputs: - - orderId - - amount - type: firebaseFunction - name: processPayment - data: - orderId: ${orderId} - amount: ${amount} - onResponse: - invokeAPI: - name: sendConfirmation - inputs: - orderId: ${orderId} -``` - -### Error Handling with Retry Logic: -```yaml -API: - reliableFunction: - type: firebaseFunction - name: criticalOperation - data: - operation: important - onError: - executeCode: - body: |- - console.log('Function failed, implementing retry logic'); - if (response.error.code === 'timeout') { - // Retry after a delay - setTimeout(() => { - invokeAPI({ name: 'reliableFunction' }); - }, 2000); - } -``` - -## 5. Best Practices - -### Input Validation: -Always validate inputs before sending to Firebase Functions: - -```yaml -validateAndSubmit: - inputs: - - email - - password - type: firebaseFunction - name: createAccount - data: - email: ${email} - password: ${password} - condition: '${email.includes("@") && password.length >= 8}' - onResponse: - executeCode: - body: |- - console.log('Account created successfully'); - navigateToScreen('welcome'); - onError: - executeCode: - body: |- - console.log('Account creation failed'); - showErrorDialog(response.error.message); +Column: + styles: + visible: '${getUserData.isSuccess ? true : false}' + children: + - Text: + id: userNameText + text: "Name: ${getUserData.body.user.name}" + - Text: + id: userEmailText + text: "Email: ${getUserData.body.user.email}" + - Text: + text: "Last Login: ${getUserData.body.user.lastLogin}" + ``` -### Loading States: -Provide clear feedback during function execution: -```yaml -Button: - label: '${submitForm.isLoading ? "Processing..." : "Submit"}' - enabled: '${!submitForm.isLoading}' - onTap: - invokeAPI: - name: submitForm -``` -## 6. Troubleshooting +## 4. Troubleshooting ### Common Issues - Ensure Firebase Functions are deployed and accessible From eb6dd92d66af2b698db891177867896a8ce5012d Mon Sep 17 00:00:00 2001 From: M-Taha-Dev Date: Wed, 23 Jul 2025 08:52:34 +0500 Subject: [PATCH 08/15] fix 7 --- pages/firebase/firebase-appcheck.mdx | 3 +-- pages/firebase/firebase-functions.mdx | 4 +--- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/pages/firebase/firebase-appcheck.mdx b/pages/firebase/firebase-appcheck.mdx index 7f95c58..be1010e 100644 --- a/pages/firebase/firebase-appcheck.mdx +++ b/pages/firebase/firebase-appcheck.mdx @@ -9,10 +9,9 @@ Unlike traditional API security measures, App Check provides automatic app verif Now, let's dive into configuring Firebase App Check for our Ensemble application: - App Check integration requires proper Firebase configuration. Ensure your Firebase project is set up before proceeding. Learn how to configure it [here](pages/firebase/configuration.mdx#2-configure-ensemble-app). + App Check integration requires proper Firebase configuration. Ensure your Firebase project is set up before proceeding. Learn how to configure it [here](configuration.mdx#2-configure-ensemble-app). -- To get hands-on experience with App Check configuration, check the live example on [Ensemble Studio](https://studio.ensembleui.com/app/e24402cb-75e2-404c-866c-29e6c3dd7992/screen/W7b1n4a1LVoQgLyAie4F) ## 1. Environment Configuration diff --git a/pages/firebase/firebase-functions.mdx b/pages/firebase/firebase-functions.mdx index 82d282c..69a90ad 100644 --- a/pages/firebase/firebase-functions.mdx +++ b/pages/firebase/firebase-functions.mdx @@ -17,9 +17,7 @@ Now, let's dive into implementing Firebase Functions in our Ensemble application Firebase function integration requires proper Firebase configuration. Ensure your Firebase project is set up before proceeding. Learn how to configure it [here](configuration.mdx#2-configure-ensemble-app). - -To get hands-on experience with Firebase Functions operations, check the live example on Ensemble Studio. - + ## 1. Environment Configuration From 7eeed84f33feb03d611c7c162d77d86e39f3b754 Mon Sep 17 00:00:00 2001 From: M-Taha-Dev Date: Wed, 23 Jul 2025 09:20:14 +0500 Subject: [PATCH 09/15] secure storage --- pages/actions/read-keychain.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/pages/actions/read-keychain.md b/pages/actions/read-keychain.md index d9c8981..74283f4 100644 --- a/pages/actions/read-keychain.md +++ b/pages/actions/read-keychain.md @@ -1,3 +1,5 @@ +import { Callout } from 'nextra/components' + # readKeychain The `readKeychain` action retrieves previously stored data from the device's secure keychain (iOS) or equivalent secure storage (Android), allowing access to sensitive information that was stored with the highest level of OS security protection. @@ -33,10 +35,13 @@ Button: ## JavaScript Usage + It is only available in YAML, as this calls a async function whose return type is Future and we use callbacks to handle the result. Our JS is sync and we cannot use async/await in JS. + ## Notes + - This action reads from the device's secure keychain or equivalent OS security storage. - Unlike the `getSecureStorage` action, this operation is asynchronous and must use callbacks even in JavaScript. - The retrieved data is converted back to its original data type (string, number, boolean, or object). @@ -44,3 +49,17 @@ It is only available in YAML, as this calls a async function whose return type i - Data stored using [saveKeychain](/actions/save-keychain) can be retrieved with this action. - The value is available in the `onComplete` action under `event.data`. - This action provides access to data with OS-level security protection. + + +For API calls, you can use `apiSecureStorage.key` to directly access secure storage values within the API context without depending on callbacks or async issues. + +yamlAPI: + createToDo: + url: http://192.168.18.163:3000/api/test + method: 'POST' + headers: + Context: apiKey ${apiSecureStorage.newdata} + body: + records: + - fields: + desc: "${apiSecureStorage.newdata} ${ensemble.storage.counter}" \ No newline at end of file From d110f2dd11d763e1539d6b0ebacfecdc8705e27d Mon Sep 17 00:00:00 2001 From: M-Taha-Dev Date: Wed, 23 Jul 2025 09:30:58 +0500 Subject: [PATCH 10/15] format fix --- pages/actions/read-keychain.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/pages/actions/read-keychain.md b/pages/actions/read-keychain.md index 74283f4..2e7bb67 100644 --- a/pages/actions/read-keychain.md +++ b/pages/actions/read-keychain.md @@ -50,9 +50,13 @@ It is only available in YAML, as this calls a async function whose return type i - The value is available in the `onComplete` action under `event.data`. - This action provides access to data with OS-level security protection. - -For API calls, you can use `apiSecureStorage.key` to directly access secure storage values within the API context without depending on callbacks or async issues. - +## apiSecureStorage + +For API calls, you can use `apiSecureStorage.key` to directly access secure storage values within the API context. This approach doesn't require any callbacks or async handling. + +### Example + +```yaml yamlAPI: createToDo: url: http://192.168.18.163:3000/api/test @@ -62,4 +66,5 @@ yamlAPI: body: records: - fields: - desc: "${apiSecureStorage.newdata} ${ensemble.storage.counter}" \ No newline at end of file + desc: "${apiSecureStorage.newdata} ${ensemble.storage.counter}" +``` \ No newline at end of file From 3edb9b27cb7fbcb5acbc3ef2289f1e7c2580bc7f Mon Sep 17 00:00:00 2001 From: M-Taha-Dev Date: Thu, 24 Jul 2025 05:22:15 +0500 Subject: [PATCH 11/15] added apiSecureStorage in API definition --- pages/actions/read-keychain.md | 19 ------------------- pages/apis/define-api.md | 19 +++++++++++++++++++ pages/widgets/Image.mdx | 1 + 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/pages/actions/read-keychain.md b/pages/actions/read-keychain.md index 2e7bb67..e2269b1 100644 --- a/pages/actions/read-keychain.md +++ b/pages/actions/read-keychain.md @@ -49,22 +49,3 @@ It is only available in YAML, as this calls a async function whose return type i - Data stored using [saveKeychain](/actions/save-keychain) can be retrieved with this action. - The value is available in the `onComplete` action under `event.data`. - This action provides access to data with OS-level security protection. - -## apiSecureStorage - -For API calls, you can use `apiSecureStorage.key` to directly access secure storage values within the API context. This approach doesn't require any callbacks or async handling. - -### Example - -```yaml -yamlAPI: - createToDo: - url: http://192.168.18.163:3000/api/test - method: 'POST' - headers: - Context: apiKey ${apiSecureStorage.newdata} - body: - records: - - fields: - desc: "${apiSecureStorage.newdata} ${ensemble.storage.counter}" -``` \ No newline at end of file diff --git a/pages/apis/define-api.md b/pages/apis/define-api.md index 5634139..0210c66 100644 --- a/pages/apis/define-api.md +++ b/pages/apis/define-api.md @@ -71,3 +71,22 @@ API: title: "${productTitle}" # example of a dynamic data that is set based on the inputs source: MyApp # example of a static data that is always passed ``` + +## apiSecureStorage + +For API calls, you can use `apiSecureStorage.key` to directly access secure storage values within the API context. This approach doesn't require any callbacks or async handling. + +### Example + +```yaml +API: + createToDo: + url: http://192.168.18.163:3000/api/test + method: 'POST' + headers: + Context: apiKey ${apiSecureStorage.newdata} + body: + records: + - fields: + desc: "${apiSecureStorage.newdata} ${ensemble.storage.counter}" +``` \ No newline at end of file diff --git a/pages/widgets/Image.mdx b/pages/widgets/Image.mdx index 864050f..c0170ef 100644 --- a/pages/widgets/Image.mdx +++ b/pages/widgets/Image.mdx @@ -19,6 +19,7 @@ You can manage your assets using Ensemble Studio. [See how](/assets) | source | string | URL to or asset name of the image or an inline SVG string. If the URL is used, it is highly recommended that the dimensions is set (either with width/height or other means) to prevent the UI jerkiness while loading. | | onTap | action | Call Ensemble's built-in functions or execute code | | onTapHaptic | enum | The type of haptic to perform when image is pressed. It should be one of heavyImpact, mediumImpact, lightImpact, selectionClick, and vibrate | +| colorFilter | object | Applies color filter to the image. | | styles | object | [See properties](#styles) | ### styles From e56851f8019cc5df47c527e94ca858e63a210440 Mon Sep 17 00:00:00 2001 From: M-Taha-Dev Date: Thu, 24 Jul 2025 05:52:31 +0500 Subject: [PATCH 12/15] added colorFilter docs --- pages/widgets/Image.mdx | 2 +- pages/widgets/avatar.mdx | 1 + pages/widgets/carousel.mdx | 3 ++- pages/widgets/markdown.md | 4 +++- pages/widgets/shape.md | 2 ++ pages/widgets/slider.mdx | 3 +++ pages/widgets/text.mdx | 2 ++ 7 files changed, 14 insertions(+), 3 deletions(-) diff --git a/pages/widgets/Image.mdx b/pages/widgets/Image.mdx index c0170ef..4af7d74 100644 --- a/pages/widgets/Image.mdx +++ b/pages/widgets/Image.mdx @@ -19,7 +19,6 @@ You can manage your assets using Ensemble Studio. [See how](/assets) | source | string | URL to or asset name of the image or an inline SVG string. If the URL is used, it is highly recommended that the dimensions is set (either with width/height or other means) to prevent the UI jerkiness while loading. | | onTap | action | Call Ensemble's built-in functions or execute code | | onTapHaptic | enum | The type of haptic to perform when image is pressed. It should be one of heavyImpact, mediumImpact, lightImpact, selectionClick, and vibrate | -| colorFilter | object | Applies color filter to the image. | | styles | object | [See properties](#styles) | ### styles @@ -57,6 +56,7 @@ You can manage your assets using Ensemble Studio. [See how](/assets) | resizedHeight | integer | Images will be automatically resized (default to 800 width with no height set) before rendering. If you know the rough image height, set this number to be the same or a slightly larger height to optimize the loading time. To maintain the original aspect ratio, set either resizedWidth or resizedHeight, but not both. This setting is not supported on Web. | | visibilityTransitionDuration | integer | Specify the duration in seconds when a widget animates between visible and not visible state. Note that setting this value will cause the widget to still occupy the UI space even when it is not visible. | | placeholderColor | integer or string | The placeholder color while the image is loading `transparent` `black` `blue` `white` `red` `grey` `teal` `amber` `pink` `purple` `yellow` `green` `brown` `cyan` `indigo` `lime` `orange` | +| colorFilter | object | Applies color filter to the image. | ### styles.backgroundGradient diff --git a/pages/widgets/avatar.mdx b/pages/widgets/avatar.mdx index 799fed8..0001789 100644 --- a/pages/widgets/avatar.mdx +++ b/pages/widgets/avatar.mdx @@ -30,6 +30,7 @@ The Avatar Widget provides a visual representation of a user or entity, typicall |:-----------------|:---------------------------------------|:--------------------------------------------------| | placeholderColor | [Color](/widgets/types#Color) | The placeholder color while the image is loading. | | fit | [Fit](/widgets/types#Fit) | How to fit the image within the dimensions | +| colorFilter | object | Applies color filter to Avatar | ### Box Styles (Inherited) This widget also inherits these styles diff --git a/pages/widgets/carousel.mdx b/pages/widgets/carousel.mdx index 2f4e3f1..ea6e2e5 100644 --- a/pages/widgets/carousel.mdx +++ b/pages/widgets/carousel.mdx @@ -49,7 +49,8 @@ The Carousel Widget allows you to create and render carousels, enabling the pres | direction | string | The axis along which the carousel view scrolls. | | cacheKey | string | Pass a cacheKey if you want to keep the carousel's item position when it was recreated | | buildOnDemand | boolean | Build the carousel items only when its visible in the screen. Default - false | -| buildOnDemandLength | integer | Build the carousel items when its visible in the screen only when the item is greater or equal to this length. Default - 6 | +| buildOnDemandLength | integer | Build the carousel items when its visible in the screen only when the item is greater or equal to this length. Default - 6 +| colorFilter | object | Applies color filter to whole carousel | | ### Box Styles (Inherited) diff --git a/pages/widgets/markdown.md b/pages/widgets/markdown.md index 9a6f94f..c72584a 100644 --- a/pages/widgets/markdown.md +++ b/pages/widgets/markdown.md @@ -13,7 +13,7 @@ The Markdown Render Widget empowers you to effortlessly render Markdown text, tr ### styles -| Property | Type | Description | +| Property | Type | Description Image: | | :--------------------------- | :---------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | textStyle | string | An opaque object that determines the size, color, and decoration of text. Styling for regular text. Default to theme's bodyMedium styling. [See properties](#styleslinkstyle) | | linkStyle | object | Styling for the url. [See properties](#styleslinkstyle) | @@ -30,6 +30,8 @@ The Markdown Render Widget empowers you to effortlessly render Markdown text, tr | visibilityTransitionDuration | number | Specify the duration in seconds when a widget animates between visible and not visible state. Note that setting this value will cause the widget to still occupy the UI space even when it is not visible. | | visible | boolean | Toggle a widget visibility on/off. Note that an invisible widget will not occupy UI space, unless the visibilityTransitionDuration is specified. | | opacity | double | Adjusts the opacity of the widget. Values range from 0 (fully transparent) to 1 (opaque). Default is `1`. | +| colorFilter | object | Applies color filter to Markdown | + #### styles.linkStyle diff --git a/pages/widgets/shape.md b/pages/widgets/shape.md index 34e9655..ab78c31 100644 --- a/pages/widgets/shape.md +++ b/pages/widgets/shape.md @@ -16,3 +16,5 @@ ECreate and customize a variety of shapes to add flair to your UI. Use this widg | width | integer | The width property determines the horizontal size of an element, allowing control over its width dimension within the layout. | | height | integer | The height property determines the vertical size of an element, allowing control over its height dimension within the layout. | | backgroundColor | integer or string | Background color of the box. which can be represented in different formats. It can be specified as a number, a predefined color name, or a hexadecimal value starting with '0x'. `transparent` `black` `blue` `white` `red` `grey` `teal` `amber` `pink` `purple` `yellow` `green` `brown` `cyan` `indigo` `lime` `orange` | +| colorFilter | object | Applies color filter to Avatar | + diff --git a/pages/widgets/slider.mdx b/pages/widgets/slider.mdx index 0eddf84..ef35823 100644 --- a/pages/widgets/slider.mdx +++ b/pages/widgets/slider.mdx @@ -38,6 +38,9 @@ The base properties define the fundamental behavior and appearance of the slider | divisions | integer | Number of discrete steps. If not set, the slider will be continuous | | styles | object | [See properties](#styles) | | onChange | Action | Action triggered when value changes | +| enableRange | boolean | Turning it true will give you two thumbs | +| startValue | number | Starting position of thumb (incase of range slider) | +| endValue | number | Ending position of thumb (incase of range slider) | ## Style Properties diff --git a/pages/widgets/text.mdx b/pages/widgets/text.mdx index b297f57..4c91644 100644 --- a/pages/widgets/text.mdx +++ b/pages/widgets/text.mdx @@ -56,6 +56,8 @@ Display and style text within your application. | expandLabel | string | Default: '...show more'. Default is in English. When expandable is set to true, expandLabel will be displayed as a clickable link at the end of the text to show the full text. If you want text for each language, use Ensemble's translation feature and specify a token here instead of hardcoding the text. | | collapseLabel | string | Default: ' show less'. Default is in English. When text is in the expanded state, collapseLabel will be displayed as a clickable link at the end of the text to show the truncated text. If you want text for each language, use Ensemble's translation feature and specify a token here instead of hardcoding the text. | | expandTextStyle | TextStyle | Default: textStyle of the Text widget with color changed to blue. The [TextStyle] of the expand/collapse label. Applicable only when [expandable] is set to true. | +| colorFilter | object | Applies color filter to Avatar | + ### styles.textStyles From f042f00697275c8c421ab57845c64b92f2e8d0f3 Mon Sep 17 00:00:00 2001 From: M-Taha-Dev Date: Thu, 24 Jul 2025 07:52:26 +0500 Subject: [PATCH 13/15] added image resources --- pages/firebase/configuration.mdx | 7 ++++++- pages/firebase/firebase-appcheck.mdx | 7 +++---- pages/firebase/firebase-functions.mdx | 11 ++++++----- pages/widgets/carousel.mdx | 2 +- public/images/firebase/appcheck.png | Bin 0 -> 4298 bytes public/images/firebase/firebasefunction.png | Bin 0 -> 24734 bytes 6 files changed, 16 insertions(+), 11 deletions(-) create mode 100644 public/images/firebase/appcheck.png create mode 100644 public/images/firebase/firebasefunction.png diff --git a/pages/firebase/configuration.mdx b/pages/firebase/configuration.mdx index 0a736c2..7dd7b6c 100644 --- a/pages/firebase/configuration.mdx +++ b/pages/firebase/configuration.mdx @@ -87,9 +87,14 @@ After we have replaced the placeholder hashtags (#) in our configuration file, i #### Update Environmental Variables -Within our Ensemble application, navigate to the settings section and look for the option to manage environmental variables. Here, we'll create two new variables: +Within our Ensemble application, navigate to the settings section and look for the option to manage environmental variables. Here, we'll create the following variables: - `api_providers`: Set the value of this variable to `firestore`. This tells our app to use Firebase Firestore as a data provider. - `firestore_config`: This is where you'll paste the complete configuration file we created earlier, including the replaced values from our downloaded configuration files. +- `firebase_config`: Similar to `firestore_config`, this variable is required when using Firebase Functions or Firebase App Check. Use the same configuration file format and values as above. + + + The `firebase_config` environment variable follows the same structure and values as `firestore_config`. It's specifically needed when implementing Firebase Functions or Firebase App Check in your Ensemble application. + ![Environment Variables](/images/firebase/envVari.png) diff --git a/pages/firebase/firebase-appcheck.mdx b/pages/firebase/firebase-appcheck.mdx index be1010e..58d0635 100644 --- a/pages/firebase/firebase-appcheck.mdx +++ b/pages/firebase/firebase-appcheck.mdx @@ -14,15 +14,14 @@ Now, let's dive into configuring Firebase App Check for our Ensemble application ## 1. Environment Configuration - -To enable App Check security in your Ensemble application, add an environment variable named `firebase_app_check` and set its value to `true` to enforce App Check security across your application. +Within our Ensemble application, navigate to the settings section and look for the option to manage environmental variables. Here, we'll create the environment variable for appcheck as follows: **Example**: ```bash -firebase_app_check=true +![All Apps](/images/firebase/appcheck.png) ``` **Explanation**: -- `firebase_app_check=true`: Enables App Check verification for all Firebase API calls in your Ensemble application. +Enables App Check verification for all Firebase API calls in your Ensemble application. ## 2. Types of App Check Operations diff --git a/pages/firebase/firebase-functions.mdx b/pages/firebase/firebase-functions.mdx index 69a90ad..3005402 100644 --- a/pages/firebase/firebase-functions.mdx +++ b/pages/firebase/firebase-functions.mdx @@ -22,16 +22,17 @@ Now, let's dive into implementing Firebase Functions in our Ensemble application ## 1. Environment Configuration ### Setting Up API Providers -To use Firebase Functions, create an environment variable named `api_providers` and add "firebase" to it. +Within our Ensemble application, navigate to the settings section and look for the option to manage environmental variables. Here, we'll create two new variables: +- `api_providers`: Set the value of this variable to `firebase`. This tells our app to use Firebase function as a api provider. + +**Example**: +```bash +![All Apps](/images/firebase/firebasefunction.png) **Note:** You can use multiple api_providers by using comma-separated values (e.g., firestore,firebase) -**Example:** -```bash -api_providers=firestore,firebase -``` ## 2. Types of Firebase Functions Operations diff --git a/pages/widgets/carousel.mdx b/pages/widgets/carousel.mdx index ea6e2e5..bce466b 100644 --- a/pages/widgets/carousel.mdx +++ b/pages/widgets/carousel.mdx @@ -49,7 +49,7 @@ The Carousel Widget allows you to create and render carousels, enabling the pres | direction | string | The axis along which the carousel view scrolls. | | cacheKey | string | Pass a cacheKey if you want to keep the carousel's item position when it was recreated | | buildOnDemand | boolean | Build the carousel items only when its visible in the screen. Default - false | -| buildOnDemandLength | integer | Build the carousel items when its visible in the screen only when the item is greater or equal to this length. Default - 6 +| buildOnDemandLength | integer | Build the carousel items when its visible in the screen only when the item is greater or equal to this length. Default - 6 | | colorFilter | object | Applies color filter to whole carousel | | diff --git a/public/images/firebase/appcheck.png b/public/images/firebase/appcheck.png new file mode 100644 index 0000000000000000000000000000000000000000..f813d3fb5f87d99f32632cddd2e0833997ea9113 GIT binary patch literal 4298 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 literal 0 HcmV?d00001 diff --git a/public/images/firebase/firebasefunction.png b/public/images/firebase/firebasefunction.png new file mode 100644 index 0000000000000000000000000000000000000000..99eabbfbe6e23779163e98902a9b9ae9138c537c GIT binary patch literal 24734 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

-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 literal 0 HcmV?d00001 From b51c4b37ce2373522ffcb115baec098d2c6517fd Mon Sep 17 00:00:00 2001 From: M-Taha-Dev Date: Thu, 24 Jul 2025 08:02:38 +0500 Subject: [PATCH 14/15] syntax fix --- pages/firebase/firebase-appcheck.mdx | 4 +--- pages/firebase/firebase-functions.mdx | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/pages/firebase/firebase-appcheck.mdx b/pages/firebase/firebase-appcheck.mdx index 58d0635..27f77c5 100644 --- a/pages/firebase/firebase-appcheck.mdx +++ b/pages/firebase/firebase-appcheck.mdx @@ -17,9 +17,7 @@ Now, let's dive into configuring Firebase App Check for our Ensemble application Within our Ensemble application, navigate to the settings section and look for the option to manage environmental variables. Here, we'll create the environment variable for appcheck as follows: **Example**: -```bash -![All Apps](/images/firebase/appcheck.png) -``` +![App Check](/images/firebase/appcheck.png) **Explanation**: Enables App Check verification for all Firebase API calls in your Ensemble application. diff --git a/pages/firebase/firebase-functions.mdx b/pages/firebase/firebase-functions.mdx index 3005402..5167dc1 100644 --- a/pages/firebase/firebase-functions.mdx +++ b/pages/firebase/firebase-functions.mdx @@ -26,7 +26,6 @@ Within our Ensemble application, navigate to the settings section and look for t - `api_providers`: Set the value of this variable to `firebase`. This tells our app to use Firebase function as a api provider. **Example**: -```bash ![All Apps](/images/firebase/firebasefunction.png) From 08fd124ba97dfbecc0cafdb85ef3449f4c09e457 Mon Sep 17 00:00:00 2001 From: M-Taha-Dev Date: Thu, 24 Jul 2025 08:10:02 +0500 Subject: [PATCH 15/15] updated image --- public/images/firebase/firebasefunction.png | Bin 24734 -> 4598 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/public/images/firebase/firebasefunction.png b/public/images/firebase/firebasefunction.png index 99eabbfbe6e23779163e98902a9b9ae9138c537c..73f3331727bd612723ef33a0ffa983de6cd5061e 100644 GIT binary patch literal 4598 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

-of@B8V-m88(kDCfN*XT(*i6PlV8}AjpM)FG(&Sa4PXq zF6uRfu2@j;rGSgnOFs-voM5evh?qrYJu%_gAfgH*v*nzt-P~STZf@N=^FV|hhR0hN z_mo_u7CcR%I~+i>N34^=AE^;^Xp>x#n|&3sY|;sQdEOx7Ta@q$64B@;ig6M{?EG;7 zQ#^5GoFl1&*;y%vXdD$!tT+O{=OjeKLqMr*YGAHzxo5 z*hNUO)zCL1f?zPf$la`Twf9OPS~iG3{&ubF$i8%QBQB)PYt?y~vrSuatH%BUK4D|C zv9Iq3xh?w<(jqVC!;$@A-pYsjZ;!z>z(PsV0ddfGv8hIC$!4~5hrfcw-PfQXaC6Q6 zFcLvFe1FehHpXDLin@ZRn>?s;DS?D(Y{~^-kui^&HI4_v$T2^#(1{T%cZ+LuVEtM2 zOi42R5EhgAcm4DcB9tj`moDZ~r?S|HaES>Z$TeR@d;|r(-mjZnG5>pJygHI2q8Sdg zB6(ZrM%S^dDpx&S)$ww#4fSou1E$FuYGLi~%o?tBB3BWgs@apGo~DG$aG1_t2ZdE4 zTyh~do}qr=-+Sj@kt#$%QgZIJmibvHY|sy5ycY>^fVTQ@gW+r* z#4+uzXaVh$AGrAZb8NO{$#vG_U7s9|Am#8p=98q=3wr(GeNcs&;P+E%@!G&AvjV_l-r#{Iqut2 zS#GMY|2A2JXT1|@1j!t!(q+cxx*s5o$(JC}S)2X`xfe2C={RmdIZS1=`^hUgvsI-F z5>q0B#FWtzWF)AyG!Zs-MZ2TN+Vuxl7A*JEYa^MQ0QZE4PmFyO+7+z$C9ko4=s z2FSj3!z4vw&qIr(b){{>`yC)r0ga;x;kBY*FPaB~o)}D+Scw2)*7;L*EQeq4S-!A@ zL2OinEJ6MBuDE!VX-S$sdrtQXgvN60`4-y`#z!gS8IdrL#mM8jH{jKI)nYPmZI+xZ zYa%u80xFv`(R^raTV@iXqW$h0UnVl&OUzYl-p5*aukbvTY_i~8otM@5-*H;i#^btn z1fYei-*KLsQ=Co6MkFw$Y6gwtp=&~DeSOuWsJ%I*W{GTjQ*kS+PtH#IO?ils_Svfb zmTuMGH>MJQka6qGxWqzAHKNqj%+B`gXn#j??*lI}#iIFRj(^gcZzu(oJc1qgo(@zt zyGXZYp6lB%IR4vgTcs%Q&A*w&=VLd=cl7@Yo(3lUAEz76e?@LD|4WcQ`wxvO_2)nO zxBnlSa!$VahvSv{GI8Mk5)I85-dJA`SXx@D2nzm3!SHXBUGUkNb6$S_qo@&*$?btN z{G0N<<0Oq0HSU=Eg48ttCI3b1Zjmtv3i?jqS7tf2lYFhHsGtT1!CM1iqkP=Z)bf$QcLRbc;lHXE-35%W zp~tuFR8mpNWLrP9htR6V)GGS={=*-s^pG=!anh8Hjg2)X<9zCTMu~_+>Z%t626g1V zsjPU}F?OE$Mf$HfP33tt+Fw*)#EFUrW4HyZzGSd{E-SF2deXQZ$Ei!+VKok#{D6EX zsKhWdQD$&q%`TkFR>`V*;dnvJArCrEHkeV^?y6`HDEu8hz*kVco61$9NO+W1JEM(X z%f$C8wq{S3muXyEt%H`Vd}?nVPW1CnW{t1BHaPM82ry)uRkgsvGfSE0EeY@)O_k&g zldxU?NayDxx;$hHsa%jPLH#6mw8e|x{#p7_SIAQV@3%#-y}wjE5F&Li%L(ttkc%cw zcpsWMvsYTYni#8fK)c$hr=GJSb&%*gwsfRJTEZCMLU32rxv2WVqSmFK+Ezusb-g3@ zLFTg9uWJb8Uore;7Nbyec251ln?=4bYz7<+u{}#`+hKvrL@nOPcBNjUQ}_M!H4-fh zVxkk0Q-Fp&&QMnFOdJZ(NkIq2)KzR8S>%bEPo57A;JX?z?oLG3E;tZou6@jRHYBYU zQQG5x``vXfTMHWYmC&(5*ynwpo{U6&PyJ?VxdzsafaQZz_lwJ*(f20MRN=Ra49qh< zq{5ZF0k-|F%^*o3zC>=B?mw1`r>o-CZ!NgE3ED&NPz)9-E@>RE`(M=QQA2)TJlWS4G1)PZ1DbS0e$bfaF&#ZF_Ej z#N?x6jtXi7a?`ZI7m;&MYwQSir9p zX<~uHYKS7wpkh4I_o0-@*M@?<-K=0I7`NO6Psn&?`a$oRm|szxWqRg!s+|PHp(*<8 zkFo`AiJk4Nfp+$&AqVXuZN_|cT|w+PhvJ^2;Nn1=sf0Uw&SgSo zeh|l+JWxYqY?EHxqoY6*0Z@<4`=TP}Xd_Rt;oVlVizY$B?L>8_M&*p}6e{IW{z-EY zba1^r_N~mDaS^pYQQSmr$8!wwrxOXc(KO_Pc41}fIh5l&SF|nu%8%-6iAtLlvDT#+ zI*9GrnZ-|8De_~rSxlfJ^Fa;z3_}j{tc<1~Q<&A7PDV1j8nHU5E(HaZ)IoksOJFWt zM4X5D&ex>kP&-|``f%2c!GfClSEc%r&@fZ3eE{ zeLHf8b-nfmmATe1U#@w;)=eCrVI_Wk08MhMS6SetjO}Gp2fgvUa;Jt*3K?0o(M$pd z`>WAcXuqVgkkEvaxr2j)q@$d03NlXbj%IlU;36%W2H~H2)nLp%ya7g4DQBkXsH<^2 zyL}~)3YW`3*d7|jfFEQ<$ZAm@p7l!SL%;f+IToZa_Q-)s{5gK|rXtQEU+WSXzV14c zX`7_dQ5?Bz((oY6lVzx~JVmS{Gx)_?95DM+-!`Q2s+{38b?D>4(k;2qdSt%44;JJe z#>iv`<_}HQ{%+`6=n)9Y<$~ZoRL_s%ZSi?zIsys<| z<*NDja#(k6(q%UuI38=e4d<#jhaEGjSL*`i<80uK#y#wiWhoI7F}YtZFxQKx1-yru zYA-joxxqsFK)TetZIN$duNUI0Emqx9>b$jURuX|@|=mD{tR)y}fi);oodLlnJ# zS>g(7swUl7u;SY4K&*uUQ}8KUHegyc6JKmzCXHUj92H4wF<5yMmMpDF>C^*ta3jM$ zrpS4~KpI=;7F1rV+sf*K#+P;=`zQNCJZr%&t~uMyOAujIzAq+)(N`rRJfcm60cDwO z$F$aut5~F^%r>nP2#fyyos{@vyay9EWFm%E1q8osf3(LoaXTkxh@QTHzA&Dr~iPyn9Z>A zK?K#Ly){5E|F_p2ccNJ51P?eJh( zXOVyS2oX;I_Qb|Iv<-?zbUK)_+zI7P*mlVp+G}N? ziVr54ao@F>bp*fQdta23R@th3Y_<74qh_=!`b+7zy4M+T9HuXBQQE}CZ{M4z0f)j0 zccR!zCrdQM_zaN)_Rpm3 zq{a{YnHV@s#h{TKi!D$DdB7>X>SUonYL6@z-MFRQEpr2W>_-CtCk@HX?P=abI_@b_R6wmNJHVESfz z8W6?_WyYC+%B)U~p{10iF*CI@R#}-*&24eO@TJqI0SlV~S7RJ~&|ojep69Lu51yT@ z^s+$vEfG?vvc7=I?D%n);Un}fGQj#~dhFC`YKdfpIDSDpLLE(t#TU(v0uZZkBs4`j zsPwB}FKJ=Lm@g5b5Si!!vW#@3C*_v?H-sAmwC))LQ(dy5N z&E)irWdox~wZ|i(m%p9I>ler9Np4~+?)y!NeT?N@9%eM46jHQ;(9I>NY{E)q6)u!r zUeD#_fF-x z72Y!wr;uXWhJuxtv@JDmx|lCXowB&B2twH{B^L>!l;65(4E*yfekMNDp(;g}^^w)r z<#}i2rdkIxYxRDZWI4*T0B`r54MB~uG%93aF&Z;giSe-6aKA5le9q=kq#jmV(rC&OE;AY`r6Fr;wWmlwO_2D(8w}57-4alI zp<_3?)~P-DZTHYhj+ws$?<*y*nk#mtg=4}@tO}E%Z?h~xyU^*Jvd^!|bedAfF?&I* z><(6tp=s6I5m&8_#Sg?TKOijSf444m$kDvi1XhGy8bX@2(`rrKFqqiqO{*8HTRURy zr3Fgok-=yuRnpLE$mgV5n7KQ!GcdU~`RN*6&MjR-eNt4hsnL0%tnow#Xd<2L^>NG> z{q(o`l8S0x~OMz+WC{!RJNdcV)#jv$L{m9CY9rFBiU^ZrpJfF zs$>fM&Qs6O>6Zi+A1=Wpbcz%^jUmh8t9DIg-lK}pUA?g-;aipfuDGpzWu#e9wAO&~O@Qc+g|wa_Fop&T&%gk<*#IiaqDCImD0skngClcSq(4 ziG~>Pt)pXNKF)gwLAU@}TVi_?Hwu!cj@;S-Dw?FX>=iV4#T|zETLhc6B zS3g4YUwb0!_8YlKKy8B9h``AB+@M|~S*$XcR#32c{i>(4vs3u&&kK~+$E#&`4-ZbK zHQ}!A?%SK~_}l&b_^(gPc*8HKLf_4IW5S3(Y5NHrQne2OTRPutCle#b3?({mb?r`l`ZoW3WZ)fA(v%rKE;28?=&b)<(f}|$wQIK;l$jAhK8D-Kg0cw^{7yp zzP~dwk$6s^t?=eYtwP^ZNDJv=4Pzv$8Dj8%y;d%CwErlsdiV_7-FYS_C-IP|&i8#Q zd#`^{Gd-AQER-9J*x&xiWbkSZ)Bh>XpG!JBbMUSE!y?w%k)`*R@Bg&}VGjLQ6J(z@ w{^O#5{a*q7^ZyF9zW&Ex|Np8f4Dk%}r&k&FpD(4ze+Qt*Nh?cLNPG$YACj|(1^@s6