@zerogpu/expo-android-sdk 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,97 @@
1
+ # @zerogpu/expo-android-sdk
2
+
3
+ Expo / React Native module that turns an **Android** device into an on-device
4
+ inference node on the [ZeroGPU](https://zerogpu.ai) network. It wraps the native
5
+ `ai.zerogpu:android-sdk` AAR and exposes a tiny JS API.
6
+
7
+ > **Android-only.** iOS builds link a no-op stub so cross-platform apps still
8
+ > compile, but the device only acts as a node on Android. The native library ships
9
+ > `arm64-v8a` (phones) and `x86_64` (emulators) only.
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ npx expo install @zerogpu/expo-android-sdk
15
+ ```
16
+
17
+ Add the config plugin to `app.json` / `app.config.js`:
18
+
19
+ ```json
20
+ {
21
+ "expo": {
22
+ "plugins": ["@zerogpu/expo-android-sdk"]
23
+ }
24
+ }
25
+ ```
26
+
27
+ Then regenerate the native project:
28
+
29
+ ```bash
30
+ npx expo prebuild
31
+ ```
32
+
33
+ The plugin wires up the two app-level Android needs automatically:
34
+
35
+ - a `pickFirst '**/libc++_shared.so'` packaging rule (the AAR bundles native libs
36
+ from ONNX Runtime **and** llama.cpp, both of which ship `libc++_shared.so`), and
37
+ - raises `minSdkVersion` to **26** if your app is lower (never lowers it).
38
+
39
+ Permissions (`INTERNET`, `ACCESS_NETWORK_STATE`) and `largeHeap` come from the AAR
40
+ manifest and merge in automatically — nothing to add.
41
+
42
+ > Requires an Expo app using `expo-modules-core` (managed or bare). This is not a
43
+ > plain-React-Native library.
44
+
45
+ ## Usage
46
+
47
+ ```ts
48
+ import { start, stop } from '@zerogpu/expo-android-sdk';
49
+
50
+ // Start the node (typically once, e.g. in your root component / on app launch).
51
+ await start('zgpu-sdk-xxxxxxxx'); // your operator key from https://zerogpu.ai
52
+
53
+ // ...later, on teardown:
54
+ await stop();
55
+ ```
56
+
57
+ Keep the operator key out of source control (e.g. via `expo-constants` /
58
+ environment config), just like any secret.
59
+
60
+ `start()` is idempotent, and the native SDK manages the foreground/background socket
61
+ itself — you do **not** need to wire it to `AppState`. On iOS both calls resolve as
62
+ no-ops.
63
+
64
+ ### Options
65
+
66
+ ```ts
67
+ await start('zgpu-sdk-…', {
68
+ baseUrl: 'https://devices.zerogpu.ai', // default
69
+ env: 'production', // default
70
+ enableTelemetry: true, // default
71
+ });
72
+ ```
73
+
74
+ ## Verifying it works
75
+
76
+ Run the app on an `arm64-v8a` phone or an `x86_64` emulator (Android 8.0+), then
77
+ watch logcat:
78
+
79
+ ```bash
80
+ adb logcat -s ZeroGpuSdk:* ZeroGpuWebSocket:*
81
+ ```
82
+
83
+ You want to see `status":"idle"` and `WebSocket ready for tasks`. Send an inference
84
+ request to `https://api.zerogpu.ai/v1/chat/completions` with your **API key** and
85
+ match the response `id` to the `requestId` in logcat — see the SDK's
86
+ [INTEGRATION.md](../app/INTEGRATION.md) for the full end-to-end check.
87
+
88
+ ## Versioning
89
+
90
+ This package is published from the same release as the AAR and always carries the
91
+ **same version** as `ai.zerogpu:android-sdk`. The AAR coordinate is derived from this
92
+ package's own `package.json`, so an installed npm version always pulls the matching
93
+ AAR.
94
+
95
+ ## License
96
+
97
+ Apache-2.0
@@ -0,0 +1,51 @@
1
+ import groovy.json.JsonSlurper
2
+
3
+ apply plugin: 'com.android.library'
4
+ apply plugin: 'kotlin-android'
5
+
6
+ group = 'ai.zerogpu.expo'
7
+ version = '0.0.2'
8
+
9
+ def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
10
+ apply from: expoModulesCorePlugin
11
+ applyKotlinExpoModulesCorePlugin()
12
+ useCoreDependencies()
13
+ useExpoPublishing()
14
+
15
+ buildscript {
16
+ // Lets the consuming app override versions we declare here.
17
+ ext.safeExtGet = { prop, fallback ->
18
+ rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
19
+ }
20
+ }
21
+
22
+ // Single source of truth for the wrapped AAR version: the package.json this module
23
+ // ships in. In a consumer app this resolves inside node_modules/@zerogpu/expo-android-sdk,
24
+ // so the npm package and the AAR it pulls always match — no drift possible.
25
+ def zeroGpuSdkVersion = new JsonSlurper().parse(file("../package.json")).version
26
+
27
+ android {
28
+ namespace "ai.zerogpu.expo"
29
+ defaultConfig {
30
+ // The native SDK requires API 26+. Never lower than the SDK's own minSdk.
31
+ minSdkVersion Math.max(safeExtGet("minSdkVersion", 26) as int, 26)
32
+ targetSdkVersion safeExtGet("targetSdkVersion", 34)
33
+ versionCode 1
34
+ versionName version.toString()
35
+ }
36
+
37
+ compileSdk safeExtGet("compileSdkVersion", 34)
38
+
39
+ // The consuming app must add the app-level pickFirst for libc++_shared.so — the
40
+ // config plugin does that automatically at prebuild time.
41
+
42
+ publishing {
43
+ singleVariant("release") {
44
+ withSourcesJar()
45
+ }
46
+ }
47
+ }
48
+
49
+ dependencies {
50
+ implementation "ai.zerogpu:android-sdk:${zeroGpuSdkVersion}"
51
+ }
@@ -0,0 +1,4 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+ <!-- Intentionally empty. INTERNET / ACCESS_NETWORK_STATE and largeHeap are
3
+ declared by the ai.zerogpu:android-sdk AAR and merge into the host app. -->
4
+ </manifest>
@@ -0,0 +1,55 @@
1
+ package ai.zerogpu.expo
2
+
3
+ import ai.zerogpu.android.sdk.ZeroGpuSdk
4
+ import expo.modules.kotlin.exception.Exceptions
5
+ import expo.modules.kotlin.modules.Module
6
+ import expo.modules.kotlin.modules.ModuleDefinition
7
+
8
+ /**
9
+ * Expo module that bridges the native ZeroGPU Android SDK to JavaScript.
10
+ *
11
+ * The SDK drives its own foreground/background lifecycle via ProcessLifecycleOwner,
12
+ * so the bridge only constructs the instance and calls start()/stop(). Because
13
+ * ZeroGpuSdk is single-use (not restartable after stop()), we hold a nullable
14
+ * reference and always build a fresh instance on start().
15
+ */
16
+ class ZeroGpuAndroidSdkModule : Module() {
17
+ // null == not started.
18
+ private var sdk: ZeroGpuSdk? = null
19
+
20
+ override fun definition() = ModuleDefinition {
21
+ // Must match requireNativeModule('ZeroGpuAndroidSdk') on the JS side.
22
+ Name("ZeroGpuAndroidSdk")
23
+
24
+ AsyncFunction("start") { operatorKey: String, options: Map<String, Any?>? ->
25
+ if (sdk != null) return@AsyncFunction // idempotent: already running
26
+
27
+ val appCtx = appContext.reactContext?.applicationContext
28
+ ?: throw Exceptions.ReactContextLost()
29
+
30
+ sdk = ZeroGpuSdk(
31
+ context = appCtx,
32
+ operatorKey = operatorKey,
33
+ baseUrl = (options?.get("baseUrl") as? String) ?: DEFAULT_BASE_URL,
34
+ env = (options?.get("env") as? String) ?: DEFAULT_ENV,
35
+ enableTelemetry = (options?.get("enableTelemetry") as? Boolean) ?: true,
36
+ ).also { it.start() }
37
+ }
38
+
39
+ AsyncFunction("stop") {
40
+ sdk?.stop()
41
+ sdk = null // drop the dead instance; a later start() builds a fresh one
42
+ }
43
+
44
+ // Safety net if the JS context is torn down without an explicit stop().
45
+ OnDestroy {
46
+ sdk?.stop()
47
+ sdk = null
48
+ }
49
+ }
50
+
51
+ private companion object {
52
+ const val DEFAULT_BASE_URL = "https://devices.zerogpu.ai"
53
+ const val DEFAULT_ENV = "production"
54
+ }
55
+ }
package/app.plugin.js ADDED
@@ -0,0 +1,4 @@
1
+ // Config-plugin entrypoint. Expo resolves `app.plugin.js` at the package root when
2
+ // this package name appears in an app's `plugins` array. The real implementation is
3
+ // compiled from plugin/src -> plugin/build by `npm run build:plugin`.
4
+ module.exports = require('./plugin/build');
@@ -0,0 +1,13 @@
1
+ import type { ZeroGpuOptions } from './types';
2
+ /**
3
+ * Native-module contract. Backed by the Android implementation
4
+ * (`ai.zerogpu.expo.ZeroGpuAndroidSdkModule`) and a no-op iOS stub, so both
5
+ * methods resolve on every platform.
6
+ */
7
+ export interface ZeroGpuAndroidSdkNativeModule {
8
+ start(operatorKey: string, options?: ZeroGpuOptions): Promise<void>;
9
+ stop(): Promise<void>;
10
+ }
11
+ declare const _default: ZeroGpuAndroidSdkNativeModule;
12
+ export default _default;
13
+ //# sourceMappingURL=ZeroGpuAndroidSdkModule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ZeroGpuAndroidSdkModule.d.ts","sourceRoot":"","sources":["../src/ZeroGpuAndroidSdkModule.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C;;;;GAIG;AACH,MAAM,WAAW,6BAA6B;IAC5C,KAAK,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;;AAGD,wBAAuF"}
@@ -0,0 +1,4 @@
1
+ import { requireNativeModule } from 'expo-modules-core';
2
+ // The name must match `Name(...)` in the native module definitions.
3
+ export default requireNativeModule('ZeroGpuAndroidSdk');
4
+ //# sourceMappingURL=ZeroGpuAndroidSdkModule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ZeroGpuAndroidSdkModule.js","sourceRoot":"","sources":["../src/ZeroGpuAndroidSdkModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAcxD,oEAAoE;AACpE,eAAe,mBAAmB,CAAgC,mBAAmB,CAAC,CAAC","sourcesContent":["import { requireNativeModule } from 'expo-modules-core';\n\nimport type { ZeroGpuOptions } from './types';\n\n/**\n * Native-module contract. Backed by the Android implementation\n * (`ai.zerogpu.expo.ZeroGpuAndroidSdkModule`) and a no-op iOS stub, so both\n * methods resolve on every platform.\n */\nexport interface ZeroGpuAndroidSdkNativeModule {\n start(operatorKey: string, options?: ZeroGpuOptions): Promise<void>;\n stop(): Promise<void>;\n}\n\n// The name must match `Name(...)` in the native module definitions.\nexport default requireNativeModule<ZeroGpuAndroidSdkNativeModule>('ZeroGpuAndroidSdk');\n"]}
@@ -0,0 +1,20 @@
1
+ import type { ZeroGpuOptions } from './types';
2
+ export type { ZeroGpuOptions } from './types';
3
+ /**
4
+ * Start the ZeroGPU node. The device registers with the ZeroGPU backend using the
5
+ * operator key, downloads its assigned model(s), and serves on-device inference
6
+ * while the app is foregrounded (the native SDK manages the socket lifecycle).
7
+ *
8
+ * Idempotent: calling `start` again while already running is a no-op. On iOS this
9
+ * resolves without doing anything — the SDK is Android-only.
10
+ *
11
+ * @param operatorKey Your operator key from the ZeroGPU dashboard (`zgpu-sdk-…`).
12
+ * @param options Optional base URL / environment / telemetry overrides.
13
+ */
14
+ export declare function start(operatorKey: string, options?: ZeroGpuOptions): Promise<void>;
15
+ /**
16
+ * Stop the ZeroGPU node: close the socket, release model adapters, and shut the
17
+ * SDK down. A subsequent {@link start} spins up a fresh instance. No-op on iOS.
18
+ */
19
+ export declare function stop(): Promise<void>;
20
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C;;;;;;;;;;GAUG;AACH,wBAAgB,KAAK,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAElF;AAED;;;GAGG;AACH,wBAAgB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAEpC"}
package/build/index.js ADDED
@@ -0,0 +1,23 @@
1
+ import ZeroGpuAndroidSdkModule from './ZeroGpuAndroidSdkModule';
2
+ /**
3
+ * Start the ZeroGPU node. The device registers with the ZeroGPU backend using the
4
+ * operator key, downloads its assigned model(s), and serves on-device inference
5
+ * while the app is foregrounded (the native SDK manages the socket lifecycle).
6
+ *
7
+ * Idempotent: calling `start` again while already running is a no-op. On iOS this
8
+ * resolves without doing anything — the SDK is Android-only.
9
+ *
10
+ * @param operatorKey Your operator key from the ZeroGPU dashboard (`zgpu-sdk-…`).
11
+ * @param options Optional base URL / environment / telemetry overrides.
12
+ */
13
+ export function start(operatorKey, options) {
14
+ return ZeroGpuAndroidSdkModule.start(operatorKey, options);
15
+ }
16
+ /**
17
+ * Stop the ZeroGPU node: close the socket, release model adapters, and shut the
18
+ * SDK down. A subsequent {@link start} spins up a fresh instance. No-op on iOS.
19
+ */
20
+ export function stop() {
21
+ return ZeroGpuAndroidSdkModule.stop();
22
+ }
23
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,uBAAuB,MAAM,2BAA2B,CAAC;AAKhE;;;;;;;;;;GAUG;AACH,MAAM,UAAU,KAAK,CAAC,WAAmB,EAAE,OAAwB;IACjE,OAAO,uBAAuB,CAAC,KAAK,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;AAC7D,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,IAAI;IAClB,OAAO,uBAAuB,CAAC,IAAI,EAAE,CAAC;AACxC,CAAC","sourcesContent":["import ZeroGpuAndroidSdkModule from './ZeroGpuAndroidSdkModule';\nimport type { ZeroGpuOptions } from './types';\n\nexport type { ZeroGpuOptions } from './types';\n\n/**\n * Start the ZeroGPU node. The device registers with the ZeroGPU backend using the\n * operator key, downloads its assigned model(s), and serves on-device inference\n * while the app is foregrounded (the native SDK manages the socket lifecycle).\n *\n * Idempotent: calling `start` again while already running is a no-op. On iOS this\n * resolves without doing anything — the SDK is Android-only.\n *\n * @param operatorKey Your operator key from the ZeroGPU dashboard (`zgpu-sdk-…`).\n * @param options Optional base URL / environment / telemetry overrides.\n */\nexport function start(operatorKey: string, options?: ZeroGpuOptions): Promise<void> {\n return ZeroGpuAndroidSdkModule.start(operatorKey, options);\n}\n\n/**\n * Stop the ZeroGPU node: close the socket, release model adapters, and shut the\n * SDK down. A subsequent {@link start} spins up a fresh instance. No-op on iOS.\n */\nexport function stop(): Promise<void> {\n return ZeroGpuAndroidSdkModule.stop();\n}\n"]}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Optional overrides for {@link start}. All fields default to production values
3
+ * inside the native SDK, so a typical caller only passes the operator key.
4
+ */
5
+ export type ZeroGpuOptions = {
6
+ /** Device-registration base URL. Defaults to `https://devices.zerogpu.ai`. */
7
+ baseUrl?: string;
8
+ /** Environment name. Defaults to `production`. */
9
+ env?: string;
10
+ /** Whether the SDK reports anonymous telemetry. Defaults to `true`. */
11
+ enableTelemetry?: boolean;
12
+ };
13
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,8EAA8E;IAC9E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kDAAkD;IAClD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,uEAAuE;IACvE,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,CAAC"}
package/build/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Optional overrides for {@link start}. All fields default to production values\n * inside the native SDK, so a typical caller only passes the operator key.\n */\nexport type ZeroGpuOptions = {\n /** Device-registration base URL. Defaults to `https://devices.zerogpu.ai`. */\n baseUrl?: string;\n /** Environment name. Defaults to `production`. */\n env?: string;\n /** Whether the SDK reports anonymous telemetry. Defaults to `true`. */\n enableTelemetry?: boolean;\n};\n"]}
@@ -0,0 +1,9 @@
1
+ {
2
+ "platforms": ["apple", "android"],
3
+ "apple": {
4
+ "modules": ["ZeroGpuAndroidSdkModule"]
5
+ },
6
+ "android": {
7
+ "modules": ["ai.zerogpu.expo.ZeroGpuAndroidSdkModule"]
8
+ }
9
+ }
@@ -0,0 +1,21 @@
1
+ Pod::Spec.new do |s|
2
+ s.name = 'ZeroGpuAndroidSdk'
3
+ s.version = '0.0.2'
4
+ s.summary = 'iOS no-op stub for @zerogpu/expo-android-sdk (the SDK is Android-only).'
5
+ s.description = 'Keeps cross-platform Expo apps building on iOS. start()/stop() resolve without doing anything; the device is only a ZeroGPU node on Android.'
6
+ s.author = 'ZeroGPU'
7
+ s.homepage = 'https://github.com/zerogpu/zerogpu-android-sdk'
8
+ s.platforms = { :ios => '13.4', :tvos => '13.4' }
9
+ s.source = { git: 'https://github.com/zerogpu/zerogpu-android-sdk.git' }
10
+ s.static_framework = true
11
+ s.license = { :type => 'Apache-2.0' }
12
+
13
+ s.dependency 'ExpoModulesCore'
14
+
15
+ s.pod_target_xcconfig = {
16
+ 'DEFINES_MODULE' => 'YES',
17
+ 'SWIFT_COMPILATION_MODE' => 'wholemodule'
18
+ }
19
+
20
+ s.source_files = '**/*.{h,m,mm,swift,hpp,cpp}'
21
+ end
@@ -0,0 +1,17 @@
1
+ import ExpoModulesCore
2
+
3
+ // The ZeroGPU SDK is Android-only. This iOS module exists solely so cross-platform
4
+ // Expo apps compile and link; both functions resolve immediately as no-ops.
5
+ public class ZeroGpuAndroidSdkModule: Module {
6
+ public func definition() -> ModuleDefinition {
7
+ Name("ZeroGpuAndroidSdk")
8
+
9
+ AsyncFunction("start") { (_ operatorKey: String, _ options: [String: Any]?) in
10
+ // No-op on iOS.
11
+ }
12
+
13
+ AsyncFunction("stop") {
14
+ // No-op on iOS.
15
+ }
16
+ }
17
+ }
package/package.json ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "@zerogpu/expo-android-sdk",
3
+ "version": "0.0.2",
4
+ "description": "Expo/React Native module that runs a ZeroGPU on-device inference node on Android by wrapping the native ai.zerogpu:android-sdk AAR.",
5
+ "main": "build/index.js",
6
+ "types": "build/index.d.ts",
7
+ "scripts": {
8
+ "build": "tsc -p tsconfig.json",
9
+ "build:plugin": "tsc -p plugin/tsconfig.json",
10
+ "clean": "expo-module clean",
11
+ "lint": "expo-module lint",
12
+ "test": "expo-module test",
13
+ "prepare": "npm run build && npm run build:plugin",
14
+ "prepublishOnly": "npm run build && npm run build:plugin",
15
+ "expo-module": "expo-module"
16
+ },
17
+ "keywords": [
18
+ "zerogpu",
19
+ "expo",
20
+ "react-native",
21
+ "android",
22
+ "on-device",
23
+ "inference",
24
+ "slm"
25
+ ],
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git+https://github.com/zerogpu/zerogpu-android-sdk.git",
29
+ "directory": "expo-android-sdk"
30
+ },
31
+ "bugs": {
32
+ "url": "https://github.com/zerogpu/zerogpu-android-sdk/issues"
33
+ },
34
+ "author": "ZeroGPU <hello@zerogpu.ai>",
35
+ "license": "Apache-2.0",
36
+ "homepage": "https://github.com/zerogpu/zerogpu-android-sdk/tree/develop/expo-android-sdk",
37
+ "publishConfig": {
38
+ "access": "public",
39
+ "registry": "https://registry.npmjs.org/"
40
+ },
41
+ "files": [
42
+ "build",
43
+ "plugin/build",
44
+ "android",
45
+ "ios",
46
+ "app.plugin.js",
47
+ "expo-module.config.json",
48
+ "!android/build",
49
+ "!android/.gradle"
50
+ ],
51
+ "peerDependencies": {
52
+ "expo": "*",
53
+ "react": "*",
54
+ "react-native": "*"
55
+ },
56
+ "devDependencies": {
57
+ "@types/react": "~18.2.79",
58
+ "expo": "^51.0.0",
59
+ "expo-module-scripts": "^3.5.2",
60
+ "expo-modules-core": "^1.12.0",
61
+ "react": "18.2.0",
62
+ "react-native": "0.74.5",
63
+ "typescript": "^5.3.3"
64
+ },
65
+ "dependencies": {
66
+ "@expo/config-plugins": "^8.0.0"
67
+ }
68
+ }
@@ -0,0 +1,3 @@
1
+ import { ConfigPlugin } from '@expo/config-plugins';
2
+ declare const _default: ConfigPlugin<void>;
3
+ export default _default;
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const config_plugins_1 = require("@expo/config-plugins");
4
+ const pkg = require('../../package.json');
5
+ /** The minimum Android API level the native SDK requires. */
6
+ const MIN_SDK_VERSION = 26;
7
+ /** Marker so the packaging block is injected exactly once (survives `prebuild --clean`). */
8
+ const PICK_FIRST_MARKER = '// @zerogpu/expo-android-sdk: pickFirst libc++_shared';
9
+ /**
10
+ * The AAR bundles native `.so` files from both ONNX Runtime and llama.cpp, each
11
+ * shipping their own `libc++_shared.so`. Without `pickFirst`, the app-module merge
12
+ * fails on the duplicate. This block is app-module-level and does not propagate from
13
+ * the AAR, so the plugin must inject it into the consuming app's build.gradle.
14
+ */
15
+ const withPickFirstLibcxx = (config) => (0, config_plugins_1.withAppBuildGradle)(config, (cfg) => {
16
+ if (cfg.modResults.language !== 'groovy') {
17
+ throw new Error('@zerogpu/expo-android-sdk config plugin only supports Groovy android/app/build.gradle.');
18
+ }
19
+ let contents = cfg.modResults.contents;
20
+ if (contents.includes(PICK_FIRST_MARKER)) {
21
+ return cfg; // already applied — idempotent
22
+ }
23
+ const block = [
24
+ '',
25
+ ` ${PICK_FIRST_MARKER}`,
26
+ ' packagingOptions {',
27
+ ' jniLibs {',
28
+ " pickFirst '**/libc++_shared.so'",
29
+ ' }',
30
+ ' }',
31
+ ].join('\n');
32
+ // Insert immediately after the opening of the top-level `android {` block.
33
+ let inserted = false;
34
+ contents = contents.replace(/android\s*\{/, (match) => {
35
+ inserted = true;
36
+ return `${match}\n${block}`;
37
+ });
38
+ if (!inserted) {
39
+ throw new Error('@zerogpu/expo-android-sdk: could not find an `android { }` block in android/app/build.gradle.');
40
+ }
41
+ cfg.modResults.contents = contents;
42
+ return cfg;
43
+ });
44
+ /**
45
+ * Raise `android.minSdkVersion` to at least {@link MIN_SDK_VERSION}. Only ever raises,
46
+ * never lowers, so an app already on a higher minSdk (or `expo-build-properties`) is
47
+ * left alone. The Expo Android template reads this gradle property for its minSdk.
48
+ */
49
+ const withMinSdk = (config) => (0, config_plugins_1.withGradleProperties)(config, (cfg) => {
50
+ const key = 'android.minSdkVersion';
51
+ const existing = cfg.modResults.find((item) => item.type === 'property' && item.key === key);
52
+ const current = existing ? parseInt(existing.value, 10) || 0 : 0;
53
+ const next = Math.max(current, MIN_SDK_VERSION);
54
+ if (existing) {
55
+ existing.value = String(next);
56
+ }
57
+ else {
58
+ cfg.modResults.push({ type: 'property', key, value: String(next) });
59
+ }
60
+ return cfg;
61
+ });
62
+ const withZeroGpu = (config) => {
63
+ config = withPickFirstLibcxx(config);
64
+ config = withMinSdk(config);
65
+ return config;
66
+ };
67
+ // Guarantees the plugin runs once even if listed multiple times.
68
+ exports.default = (0, config_plugins_1.createRunOncePlugin)(withZeroGpu, pkg.name, pkg.version);