From 95466d78de95f8c762f3629e62a237ac9d20db86 Mon Sep 17 00:00:00 2001 From: Yogendra Shelke Date: Fri, 4 Sep 2026 11:02:35 +0530 Subject: [PATCH 1/3] chore: add mendix-native dependency --- configs/jsactions/rollup.config.mjs | 5 ++- eslint.config.js | 34 +++++++++++++++- .../mobile-resources-native/package.json | 1 + .../src/camera/TakePicture.ts | 7 ++-- .../src/camera/TakePictureAdvanced.ts | 7 ++-- .../nanoflow-actions-native/package.json | 3 +- .../src/native/mendix-native.ts | 10 +++++ .../src/other/Base64DecodeToImage.ts | 5 ++- pnpm-lock.yaml | 39 +++++++++++++++++++ 9 files changed, 100 insertions(+), 11 deletions(-) create mode 100644 packages/jsActions/nanoflow-actions-native/src/native/mendix-native.ts diff --git a/configs/jsactions/rollup.config.mjs b/configs/jsactions/rollup.config.mjs index 29e99997c..52a2731af 100644 --- a/configs/jsactions/rollup.config.mjs +++ b/configs/jsactions/rollup.config.mjs @@ -22,7 +22,9 @@ export default async args => { const jsActionTargetFolder = `javascriptsource/${args.configProject ?? "nativemobileresources"}/actions`; const result = []; const posixPath = join(cwd, "src", "**/*.ts").split(sep).join(posix.sep); // Always use forward slashes - const files = await fg([posixPath]); // fast-glob only works with forward slashes + // `src/native` holds shared helpers, not actions, so it gets no bundle of its own; it lives under + // `src` because these packages have no tsconfig and TypeScript infers the source root from there. + const files = await fg([posixPath], { ignore: ["**/src/native/**"] }); // fast-glob only works with forward slashes const outDir = join(cwd, "dist"); const nodeResolvePlugin = nodeResolve({ preferBuiltins: false, mainFields: ["module", "browser", "main"] }); @@ -138,6 +140,7 @@ export default async args => { const nativeExternal = [ /^mendix\//, + /^mendix-native(\/|$)/, /^react-native(\/|$)/, /^react-native-windows(\/|$)/, /^react-native-web(\/|$)/, diff --git a/eslint.config.js b/eslint.config.js index 2dd2307f6..371e2a7f2 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -19,5 +19,37 @@ module.exports = [ ] }, // Then extend from the legacy .eslintrc.js file - ...compat.extends(path.resolve(__dirname, ".eslintrc.js")) + ...compat.extends(path.resolve(__dirname, ".eslintrc.js")), + // Nanoflow Commons actions also run on web, where `mendix-native` does not resolve at all, so a + // static import would break the web bundle even on paths that never touch native. These actions + // must go through src/native/mendix-native.ts, which owns the lazy import; src/native itself is + // exempt. Native-only packages such as mobile-resources-native import `mendix-native` directly. + { + files: ["packages/jsActions/nanoflow-actions-native/src/**/*.{ts,tsx,js,jsx}"], + ignores: ["packages/jsActions/nanoflow-actions-native/src/native/**"], + rules: { + "no-restricted-imports": [ + "error", + { + patterns: [ + { + regex: "^mendix-native(/|$)", + message: + "Import from src/native/mendix-native instead, which owns the import of 'mendix-native'. In Nanoflow Commons a direct import also breaks the web bundle." + } + ] + } + ], + // no-restricted-imports does not flag `import()` expressions, so the dynamic form is + // matched separately. + "no-restricted-syntax": [ + "error", + { + selector: "ImportExpression > Literal[value=/^mendix-native(\\u002F|$)/]", + message: + "Import from src/native/mendix-native instead; that module owns the dynamic import of 'mendix-native'." + } + ] + } + } ]; diff --git a/packages/jsActions/mobile-resources-native/package.json b/packages/jsActions/mobile-resources-native/package.json index a7cc45e75..2281fea5f 100644 --- a/packages/jsActions/mobile-resources-native/package.json +++ b/packages/jsActions/mobile-resources-native/package.json @@ -49,6 +49,7 @@ "@types/querystringify": "^2.0.0", "@types/url-parse": "^1.4.3", "mendix": "11.8.0", + "mendix-native": "https://github.com/mendix/mendix-native/releases/download/v0.6.0/mendix-native-v0.6.0.tgz", "rimraf": "^6.1.2" } } diff --git a/packages/jsActions/mobile-resources-native/src/camera/TakePicture.ts b/packages/jsActions/mobile-resources-native/src/camera/TakePicture.ts index f80254be5..a41d3d835 100644 --- a/packages/jsActions/mobile-resources-native/src/camera/TakePicture.ts +++ b/packages/jsActions/mobile-resources-native/src/camera/TakePicture.ts @@ -7,6 +7,7 @@ // Other code you write will be lost the next time you deploy the project. import { Big } from "big.js"; import { Alert, Linking, NativeModules, Platform } from "react-native"; +import { NativeFileSystem } from "mendix-native"; import { CameraOptions, ErrorCode, @@ -118,7 +119,7 @@ export async function TakePicture( async function safeRemove(filePath: string): Promise { try { - await NativeModules.MxFileSystem.remove(filePath); + await NativeFileSystem.remove(filePath); } catch (error) { console.warn(`Failed to remove file at ${filePath}. Error: ${error}`); // ignore error @@ -127,8 +128,8 @@ export async function TakePicture( function storeFile(imageObject: mendix.lib.MxObject, uri: string): Promise { return new Promise((resolve, reject) => { - NativeModules.MxFileSystem.read(uri.replace("file://", "")) - .then((nativeBlob: unknown) => { + NativeFileSystem.read(uri.replace("file://", "")) + .then(nativeBlob => { const blob = new Blob(); Object.assign(blob, { data: nativeBlob }); // eslint-disable-next-line no-useless-escape diff --git a/packages/jsActions/mobile-resources-native/src/camera/TakePictureAdvanced.ts b/packages/jsActions/mobile-resources-native/src/camera/TakePictureAdvanced.ts index d283dab5f..66d4d3228 100644 --- a/packages/jsActions/mobile-resources-native/src/camera/TakePictureAdvanced.ts +++ b/packages/jsActions/mobile-resources-native/src/camera/TakePictureAdvanced.ts @@ -7,6 +7,7 @@ // Other code you write will be lost the next time you deploy the project. import { Big } from "big.js"; import { Alert, Linking, NativeModules, Platform } from "react-native"; +import { NativeFileSystem } from "mendix-native"; import { CameraOptions, ErrorCode, @@ -171,7 +172,7 @@ export async function TakePictureAdvanced( async function safeRemove(filePath: string): Promise { try { - await NativeModules.MxFileSystem.remove(filePath); + await NativeFileSystem.remove(filePath); } catch (error) { console.warn(`Failed to remove file at ${filePath}. Error: ${error}`); // ignore error @@ -180,8 +181,8 @@ export async function TakePictureAdvanced( function storeFile(imageObject: mendix.lib.MxObject, uri: string): Promise { return new Promise((resolve, reject) => { - NativeModules.MxFileSystem.read(uri.replace("file://", "")) - .then((nativeBlob: unknown) => { + NativeFileSystem.read(uri.replace("file://", "")) + .then(nativeBlob => { const blob = new Blob(); Object.assign(blob, { data: nativeBlob }); // eslint-disable-next-line no-useless-escape diff --git a/packages/jsActions/nanoflow-actions-native/package.json b/packages/jsActions/nanoflow-actions-native/package.json index bf1b576a0..09042533c 100644 --- a/packages/jsActions/nanoflow-actions-native/package.json +++ b/packages/jsActions/nanoflow-actions-native/package.json @@ -35,6 +35,7 @@ }, "devDependencies": { "@mendix/pluggable-widgets-tools": "*", - "mendix": "11.8.0" + "mendix": "11.8.0", + "mendix-native": "https://github.com/mendix/mendix-native/releases/download/v0.6.0/mendix-native-v0.6.0.tgz" } } diff --git a/packages/jsActions/nanoflow-actions-native/src/native/mendix-native.ts b/packages/jsActions/nanoflow-actions-native/src/native/mendix-native.ts new file mode 100644 index 000000000..e019ff9e0 --- /dev/null +++ b/packages/jsActions/nanoflow-actions-native/src/native/mendix-native.ts @@ -0,0 +1,10 @@ +// Single entry point for `mendix-native`. +// +// The package is provided at runtime by native-template (native code) and the AppDev client (JS), +// and is listed in `nativeExternal` in configs/jsactions/rollup.config.mjs so it is never bundled +// into the MPK. Nanoflow Commons actions also run on web, where the module does not resolve at all, +// so actions must reach it through the dynamic import below instead of a static one. Rollup inlines +// this module into each action bundle and leaves the `import()` untouched. +export type MendixNative = typeof import("mendix-native"); + +export const loadMendixNative = async (): Promise => import("mendix-native"); diff --git a/packages/jsActions/nanoflow-actions-native/src/other/Base64DecodeToImage.ts b/packages/jsActions/nanoflow-actions-native/src/other/Base64DecodeToImage.ts index 8a8f5c4cf..7d0f8e2ec 100644 --- a/packages/jsActions/nanoflow-actions-native/src/other/Base64DecodeToImage.ts +++ b/packages/jsActions/nanoflow-actions-native/src/other/Base64DecodeToImage.ts @@ -7,7 +7,7 @@ // Other code you write will be lost the next time you deploy the project. import { Base64 } from "js-base64"; import RNBlobUtil from "react-native-blob-util"; -import { NativeModules } from "react-native"; +import { loadMendixNative } from "../native/mendix-native"; // BEGIN EXTRA CODE // END EXTRA CODE @@ -56,7 +56,8 @@ export async function Base64DecodeToImage(base64: string, image: mendix.lib.MxOb // NativeFileBackend.storeFile calls NativeFileSystem.save(blob.data, path) // and blob.close() — a plain object has no .data getter or .close(), which // crashes iOS via [NSInvocation invokeWithTarget:]. - const nativeBlob = await NativeModules.MxFileSystem.read(tempPath.replace("file://", "")); + const { NativeFileSystem } = await loadMendixNative(); + const nativeBlob = await NativeFileSystem.read(tempPath.replace("file://", "")); // Normalize: MxFileSystem.read may return 'length' instead of 'size'. const blobData = { ...(nativeBlob as any) }; if (blobData.size === undefined && blobData.length !== undefined) { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f45195e7f..fd4f4d892 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -186,6 +186,9 @@ importers: mendix: specifier: 11.8.0 version: 11.8.0 + mendix-native: + specifier: https://github.com/mendix/mendix-native/releases/download/v0.6.0/mendix-native-v0.6.0.tgz + version: https://github.com/mendix/mendix-native/releases/download/v0.6.0/mendix-native-v0.6.0.tgz(@op-engineering/op-sqlite@18.1.4(react-native@0.84.1(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(react@19.2.3))(react@19.2.3))(@react-native-async-storage/async-storage@2.2.0(react-native@0.84.1(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(react@19.2.3)))(react-native-gesture-handler@2.31.2(patch_hash=5be6c2cce8be6bd8afdbbf6c289d8b210686eafce0b41bf8cae2c29388aaa3b8)(react-native@0.84.1(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(react@19.2.3))(react@19.2.3))(react-native@0.84.1(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(react@19.2.3))(react@19.2.3) rimraf: specifier: ^6.1.2 version: 6.1.3 @@ -217,6 +220,9 @@ importers: mendix: specifier: 11.8.0 version: 11.8.0 + mendix-native: + specifier: https://github.com/mendix/mendix-native/releases/download/v0.6.0/mendix-native-v0.6.0.tgz + version: https://github.com/mendix/mendix-native/releases/download/v0.6.0/mendix-native-v0.6.0.tgz(@op-engineering/op-sqlite@18.1.4(react-native@0.84.1(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(react@19.2.3))(react@19.2.3))(@react-native-async-storage/async-storage@2.2.0(react-native@0.84.1(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(react@19.2.3)))(react-native-gesture-handler@2.31.2(patch_hash=5be6c2cce8be6bd8afdbbf6c289d8b210686eafce0b41bf8cae2c29388aaa3b8)(react-native@0.84.1(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(react@19.2.3))(react@19.2.3))(react-native@0.84.1(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(react@19.2.3))(react@19.2.3) packages/modules/atlas-content-native: devDependencies: @@ -2184,6 +2190,16 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@op-engineering/op-sqlite@18.1.4': + resolution: {integrity: sha512-8gjHXMyigCSDy/xdn46SQdnOW9pgJuTyrxqZj9ro75Lkp6dg+xs8wzyO+QH+khTWkY6TSofP3NcttJ1uiLdUFA==} + peerDependencies: + '@sqlite.org/sqlite-wasm': '*' + react: 19.2.3 + react-native: 0.84.1 + peerDependenciesMeta: + '@sqlite.org/sqlite-wasm': + optional: true + '@parcel/watcher-android-arm64@2.5.6': resolution: {integrity: sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==} engines: {node: '>= 10.0.0'} @@ -5561,6 +5577,16 @@ packages: mendix-client@7.15.8: resolution: {integrity: sha512-RazCdCHoLVNKUUeKDkSkIL6Lxx6fUaa4iiy+Ltp9ra8mLQhwyNqD33TIN7YZJ3HDjHc3eWh9cjiZWwh6Jg/cQg==} + mendix-native@https://github.com/mendix/mendix-native/releases/download/v0.6.0/mendix-native-v0.6.0.tgz: + resolution: {integrity: sha512-7q2dCjO0dNaS7Ad+p6rQTeJaYj5nxi2lXCgMBRDuDCHqSjdDFIMuD6Q6rSyVaiDdHt1WjOMLX5jsj38AcrNIoQ==, tarball: https://github.com/mendix/mendix-native/releases/download/v0.6.0/mendix-native-v0.6.0.tgz} + version: 0.6.0 + peerDependencies: + '@op-engineering/op-sqlite': '*' + '@react-native-async-storage/async-storage': '*' + react: 19.2.3 + react-native: 0.84.1 + react-native-gesture-handler: '*' + mendix@11.8.0: resolution: {integrity: sha512-txq9wAuVcXpGCCbBzwtmb7afFpQzv4j6qccDoS1eMYe1JH9Pu/jpe4EsyfSSimoSgtSi55iG9wa+Iuy2M2RDgw==} @@ -9654,6 +9680,11 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.20.1 + '@op-engineering/op-sqlite@18.1.4(react-native@0.84.1(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(react@19.2.3))(react@19.2.3)': + dependencies: + react: 19.2.3 + react-native: 0.84.1(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(react@19.2.3) + '@parcel/watcher-android-arm64@2.5.6': optional: true @@ -13611,6 +13642,14 @@ snapshots: '@types/big.js': 0.0.31 '@types/dojo': 1.9.49 + mendix-native@https://github.com/mendix/mendix-native/releases/download/v0.6.0/mendix-native-v0.6.0.tgz(@op-engineering/op-sqlite@18.1.4(react-native@0.84.1(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(react@19.2.3))(react@19.2.3))(@react-native-async-storage/async-storage@2.2.0(react-native@0.84.1(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(react@19.2.3)))(react-native-gesture-handler@2.31.2(patch_hash=5be6c2cce8be6bd8afdbbf6c289d8b210686eafce0b41bf8cae2c29388aaa3b8)(react-native@0.84.1(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(react@19.2.3))(react@19.2.3))(react-native@0.84.1(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(react@19.2.3))(react@19.2.3): + dependencies: + '@op-engineering/op-sqlite': 18.1.4(react-native@0.84.1(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(react@19.2.3))(react@19.2.3) + '@react-native-async-storage/async-storage': 2.2.0(react-native@0.84.1(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(react@19.2.3)) + react: 19.2.3 + react-native: 0.84.1(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(react@19.2.3) + react-native-gesture-handler: 2.31.2(patch_hash=5be6c2cce8be6bd8afdbbf6c289d8b210686eafce0b41bf8cae2c29388aaa3b8)(react-native@0.84.1(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(react@19.2.3))(react@19.2.3) + mendix@11.8.0: dependencies: '@types/big.js': 6.2.2 From 0e0c20693fb184d67a6282d2012430e85b889a8a Mon Sep 17 00:00:00 2001 From: Yogendra Shelke Date: Fri, 4 Sep 2026 11:26:30 +0530 Subject: [PATCH 2/3] chore: bump sp version for ci runs --- configs/e2e/mendix-versions.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/e2e/mendix-versions.json b/configs/e2e/mendix-versions.json index f29ceecd6..d7a50cf9b 100644 --- a/configs/e2e/mendix-versions.json +++ b/configs/e2e/mendix-versions.json @@ -1,3 +1,3 @@ { - "latest": "11.12.3" + "latest": "11.14.0" } From dfe6dd80ace805e472972f64309606e435925a34 Mon Sep 17 00:00:00 2001 From: Yogendra Shelke Date: Fri, 4 Sep 2026 11:28:28 +0530 Subject: [PATCH 3/3] chore: bump min mx version --- packages/jsActions/mobile-resources-native/package.json | 2 +- packages/jsActions/nanoflow-actions-native/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/jsActions/mobile-resources-native/package.json b/packages/jsActions/mobile-resources-native/package.json index 2281fea5f..52d8515ec 100644 --- a/packages/jsActions/mobile-resources-native/package.json +++ b/packages/jsActions/mobile-resources-native/package.json @@ -11,7 +11,7 @@ "marketplace": { "name": "Native Mobile Resources", "description": "Download this module to access a rich set of widgets and nanoflow actions created for native mobile, including authentication, network, platform, and more. A must-have for native mobile development!", - "minimumMXVersion": "11.12.3", + "minimumMXVersion": "11.14.0", "marketplaceId": 109513 }, "testProject": { diff --git a/packages/jsActions/nanoflow-actions-native/package.json b/packages/jsActions/nanoflow-actions-native/package.json index 09042533c..4af0e3e0e 100644 --- a/packages/jsActions/nanoflow-actions-native/package.json +++ b/packages/jsActions/nanoflow-actions-native/package.json @@ -11,7 +11,7 @@ "marketplace": { "name": "Nanoflow Commons", "description": "The Nanoflow Commons module contains commonly used nanoflow actions that are usable across web, hybrid mobile, and native mobile apps, such as: client activities, geo location, local storage, & more.", - "minimumMXVersion": "11.12.3", + "minimumMXVersion": "11.14.0", "marketplaceId": 109515 }, "testProject": {