diff --git a/.changeset/smart-vans-compete.md b/.changeset/smart-vans-compete.md new file mode 100644 index 0000000000..a845151cc8 --- /dev/null +++ b/.changeset/smart-vans-compete.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.gitignore b/.gitignore index 35881a0e0d..40fb94d9ad 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ /incubator/*/bin/ /incubator/*/dist/ /incubator/*/lib/ +/incubator/@react-native-webapis/*/lib/ /packages/*/*.LICENSE.txt /packages/*/*/rnx-build/ /packages/*/bin/ diff --git a/incubator/@react-native-webapis/battery-status/README.md b/incubator/@react-native-webapis/battery-status/README.md new file mode 100644 index 0000000000..7f880218ee --- /dev/null +++ b/incubator/@react-native-webapis/battery-status/README.md @@ -0,0 +1,92 @@ +# @react-native-webapis/battery-status + +[![Build](https://github.com/microsoft/rnx-kit/actions/workflows/build.yml/badge.svg)](https://github.com/microsoft/rnx-kit/actions/workflows/build.yml) +[![npm version](https://img.shields.io/npm/v/@react-native-webapis/battery-status)](https://www.npmjs.com/package/@react-native-webapis/battery-status) + +šŸš§šŸš§šŸš§šŸš§šŸš§šŸš§šŸš§šŸš§šŸš§šŸš§šŸš§ + +### THIS TOOL IS EXPERIMENTAL ā€” USE WITH CAUTION + +šŸš§šŸš§šŸš§šŸš§šŸš§šŸš§šŸš§šŸš§šŸš§šŸš§šŸš§ + +[Battery Status API](https://developer.mozilla.org/en-US/docs/Web/API/Battery_Status_API) +for React Native. + +## Motivation + +This is a prototype for the [React Native WebAPIs RFC](https://github.com/microsoft/rnx-kit/pull/2504) + +## Installation + +```sh +yarn add @rnx-kit/polyfills --dev +yarn add @react-native-webapis/battery-status +``` + +or if you're using npm + +```sh +npm add --save-dev @rnx-kit/polyfills +npm add @react-native-webapis/battery-status +``` + +## Usage + +```diff +diff --git a/packages/test-app/metro.config.js b/packages/test-app/metro.config.js +index 7c0dcfc2..df0f8b0d 100644 +--- a/packages/test-app/metro.config.js ++++ b/packages/test-app/metro.config.js +@@ -33,4 +33,7 @@ module.exports = makeMetroConfig({ + blacklistRE: blockList, + blockList, + }, ++ serializer: { ++ getModulesRunBeforeMainModule: require("@rnx-kit/polyfills").default, ++ }, + }); +diff --git a/packages/test-app/src/App.native.tsx b/packages/test-app/src/App.native.tsx +index 599634a9..b465f0fe 100644 +--- a/packages/test-app/src/App.native.tsx ++++ b/packages/test-app/src/App.native.tsx +@@ -1,3 +1,5 @@ ++// Temporary until we figure out how to magically inject WebAPIs ++import "@react-native-webapis/battery-status"; + import { acquireTokenWithScopes } from "@rnx-kit/react-native-auth"; + // Both `internal` imports are used to verify that `metro-resolver-symlinks` + // resolves them correctly when `experimental_retryResolvingFromDisk` is +@@ -7,7 +9,7 @@ import { + getRemoteDebuggingAvailability, + } from "internal"; + import { getHermesVersion } from "internal/hermes"; +-import React, { useCallback, useMemo, useState } from "react"; ++import React, { useCallback, useEffect, useMemo, useState } from "react"; + import type { LayoutChangeEvent } from "react-native"; + import { + NativeModules, +@@ -186,6 +188,14 @@ function App({ concurrentRoot }: { concurrentRoot?: boolean }) { + [setFabric] + ); + ++ const [batteryLevel, setBatteryLevel] = useState(-1); ++ useEffect(() => { ++ // @ts-expect-error FIXME ++ global.navigator.getBattery().then((status) => { ++ setBatteryLevel(status.level); ++ }); ++ }, []); ++ + return ( + + +@@ -195,6 +205,9 @@ function App({ concurrentRoot }: { concurrentRoot?: boolean }) { + style={styles.body} + > +
++ ++ Battery Level ++ + + + +``` diff --git a/incubator/@react-native-webapis/battery-status/RNWBatteryStatus.podspec b/incubator/@react-native-webapis/battery-status/RNWBatteryStatus.podspec new file mode 100644 index 0000000000..7a5ef8db47 --- /dev/null +++ b/incubator/@react-native-webapis/battery-status/RNWBatteryStatus.podspec @@ -0,0 +1,30 @@ +require 'json' + +package = JSON.parse(File.read(File.join(__dir__, 'package.json'))) +version = package['version'] +repository = package['repository'] + +Pod::Spec.new do |s| + s.name = 'RNWBatteryStatus' + s.version = version + s.author = { package['author']['name'] => package['author']['email'] } + s.license = package['license'] + s.homepage = package['homepage'] + s.source = { :git => repository['url'], :tag => "#{package['name']}@#{version}" } + s.summary = package['description'] + + s.ios.deployment_target = '13.0' + s.osx.deployment_target = '10.15' + + s.dependency 'React-Core' + + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } + + # Include both package and repository relative paths to allow the podspec to + # be consumed from both a local path, and as a podspec outside a spec + # repository. + s.source_files = 'ios/*.{h,m}', # :path + "#{repository['directory']}/ios/*.{h,m}" # :podspec + s.public_header_files = 'ios/*.h', # :path + "#{repository['directory']}/ios/*.h" # :podspec +end diff --git a/incubator/@react-native-webapis/battery-status/android/build.gradle b/incubator/@react-native-webapis/battery-status/android/build.gradle new file mode 100644 index 0000000000..076ea9ba5e --- /dev/null +++ b/incubator/@react-native-webapis/battery-status/android/build.gradle @@ -0,0 +1,73 @@ +import java.nio.file.Paths + +buildscript { + ext.findFile = { fileName -> + def currentDirPath = rootDir == null ? null : rootDir.toString() + + while (currentDirPath != null) { + def currentDir = file(currentDirPath); + def requestedFile = Paths.get(currentDirPath, fileName).toFile() + + if (requestedFile.exists()) { + return requestedFile + } + + currentDirPath = currentDir.getParent() + } + + return null + } + + ext.findNodeModulesPath = { packageName -> + return findFile(Paths.get("node_modules", packageName).toString()) + } + + ext.getExtProp = { prop, defaultValue -> + return rootProject.ext.has(prop) ? rootProject.ext.get(prop) : defaultValue + } + + ext.kotlinVersion = getExtProp("kotlinVersion", "1.7.21") + + repositories { + google() + mavenCentral() + } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" + } +} + +plugins { + id("com.android.library") + id("org.jetbrains.kotlin.android") +} + +repositories { + maven { + url("${findNodeModulesPath('react-native')}/android") + } + + google() + mavenCentral() +} + +android { + compileSdkVersion getExtProp("compileSdkVersion", 33) + defaultConfig { + minSdkVersion getExtProp("minSdkVersion", 23) + targetSdkVersion getExtProp("targetSdkVersion", 29) + } + lintOptions { + abortOnError false + } +} + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion" + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion" + implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion" + + //noinspection GradleDynamicVersion + implementation "com.facebook.react:react-native:+" +} diff --git a/incubator/@react-native-webapis/battery-status/android/gradle.properties b/incubator/@react-native-webapis/battery-status/android/gradle.properties new file mode 100644 index 0000000000..08bd26ebc5 --- /dev/null +++ b/incubator/@react-native-webapis/battery-status/android/gradle.properties @@ -0,0 +1,3 @@ +# These properties are required to enable AndroidX for the test app. +android.useAndroidX=true +android.enableJetifier=true diff --git a/incubator/@react-native-webapis/battery-status/android/src/main/AndroidManifest.xml b/incubator/@react-native-webapis/battery-status/android/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..d03f8b9134 --- /dev/null +++ b/incubator/@react-native-webapis/battery-status/android/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + diff --git a/incubator/@react-native-webapis/battery-status/android/src/main/java/org/reactnativewebapis/batterystatus/BatteryStatusModule.kt b/incubator/@react-native-webapis/battery-status/android/src/main/java/org/reactnativewebapis/batterystatus/BatteryStatusModule.kt new file mode 100644 index 0000000000..0e123b50ca --- /dev/null +++ b/incubator/@react-native-webapis/battery-status/android/src/main/java/org/reactnativewebapis/batterystatus/BatteryStatusModule.kt @@ -0,0 +1,47 @@ +package org.reactnativewebapis.batterystatus + +import android.content.Context +import android.os.BatteryManager +import android.os.Build +import com.facebook.react.bridge.Arguments +import com.facebook.react.bridge.Promise +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.bridge.ReactContextBaseJavaModule +import com.facebook.react.bridge.ReactMethod +import com.facebook.react.bridge.ReactModuleWithSpec + +class BatteryStatusModule(context: ReactApplicationContext?) : + ReactContextBaseJavaModule(context), ReactModuleWithSpec { + + companion object { + const val NAME = "RNWBatteryStatus" + } + + override fun getName(): String = NAME + + @ReactMethod + fun getStatus(promise: Promise) { + val batteryManager = + reactApplicationContext.getSystemService(Context.BATTERY_SERVICE) as BatteryManager + promise.resolve( + Arguments.createMap().also { map -> + map.putBoolean("charging", batteryManager.isCharging) + map.putInt("chargingTime", batteryManager.getChargingTime()) + map.putInt("dischargingTime", -1) + map.putDouble("level", batteryManager.getBatteryLevel()) + } + ) + } +} + +fun BatteryManager.getBatteryLevel(): Double { + return getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY) / 100.0 +} + +fun BatteryManager.getChargingTime(): Int { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + (computeChargeTimeRemaining() / 1000).toInt() + } else { + -1 + } +} diff --git a/incubator/@react-native-webapis/battery-status/android/src/main/java/org/reactnativewebapis/batterystatus/BatteryStatusPackage.kt b/incubator/@react-native-webapis/battery-status/android/src/main/java/org/reactnativewebapis/batterystatus/BatteryStatusPackage.kt new file mode 100644 index 0000000000..a17dbec5ad --- /dev/null +++ b/incubator/@react-native-webapis/battery-status/android/src/main/java/org/reactnativewebapis/batterystatus/BatteryStatusPackage.kt @@ -0,0 +1,30 @@ +package org.reactnativewebapis.batterystatus + +import com.facebook.react.TurboReactPackage +import com.facebook.react.bridge.NativeModule +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.module.model.ReactModuleInfo +import com.facebook.react.module.model.ReactModuleInfoProvider + +class BatteryStatusPackage : TurboReactPackage() { + override fun getModule(name: String?, reactContext: ReactApplicationContext?): NativeModule { + return when (name) { + BatteryStatusModule.NAME -> BatteryStatusModule(reactContext) + else -> throw IllegalArgumentException("No module named '$name'") + } + } + + override fun getReactModuleInfoProvider(): ReactModuleInfoProvider = + ReactModuleInfoProvider { + val info = ReactModuleInfo( + BatteryStatusModule.NAME, + BatteryStatusModule::class.java.name, + false, + false, + false, + false, + false + ) + mapOf(info.name() to info).toMutableMap() + } +} diff --git a/incubator/@react-native-webapis/battery-status/ios/RNWBatteryStatus.h b/incubator/@react-native-webapis/battery-status/ios/RNWBatteryStatus.h new file mode 100644 index 0000000000..30b1d08e1d --- /dev/null +++ b/incubator/@react-native-webapis/battery-status/ios/RNWBatteryStatus.h @@ -0,0 +1,10 @@ +#import + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface RNWBatteryStatus : NSObject +@end + +NS_ASSUME_NONNULL_END diff --git a/incubator/@react-native-webapis/battery-status/ios/RNWBatteryStatus.m b/incubator/@react-native-webapis/battery-status/ios/RNWBatteryStatus.m new file mode 100644 index 0000000000..9ea36db0cd --- /dev/null +++ b/incubator/@react-native-webapis/battery-status/ios/RNWBatteryStatus.m @@ -0,0 +1,34 @@ +#import "RNWBatteryStatus.h" + +@implementation RNWBatteryStatus + +RCT_EXPORT_MODULE(RNWBatteryStatus) + ++ (BOOL)requiresMainQueueSetup +{ + return NO; +} + +// clang-format off +RCT_EXPORT_METHOD(getStatus:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) +// clang-format on +{ + UIDevice *device = [UIDevice currentDevice]; + [device setBatteryMonitoringEnabled:YES]; + + UIDeviceBatteryState batteryState = [device batteryState]; + BOOL isCharging = batteryState == UIDeviceBatteryStateCharging; + NSDictionary *status = @{ + @"charging": [NSNumber numberWithBool:isCharging], + @"chargingTime": + [NSNumber numberWithFloat:batteryState == UIDeviceBatteryStateFull ? 0 : -1], + @"dischargingTime": [NSNumber numberWithFloat:-1], + @"level": [NSNumber numberWithFloat:[device batteryLevel]], + }; + [device setBatteryMonitoringEnabled:NO]; + + resolve(status); +} + +@end diff --git a/incubator/@react-native-webapis/battery-status/ios/RNWBatteryStatus.xcodeproj/project.pbxproj b/incubator/@react-native-webapis/battery-status/ios/RNWBatteryStatus.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..f3c5a56e22 --- /dev/null +++ b/incubator/@react-native-webapis/battery-status/ios/RNWBatteryStatus.xcodeproj/project.pbxproj @@ -0,0 +1 @@ +/* Dummy file so @react-native-community/cli recognizes this as an iOS package */ diff --git a/incubator/@react-native-webapis/battery-status/package.json b/incubator/@react-native-webapis/battery-status/package.json new file mode 100644 index 0000000000..c66899fc4f --- /dev/null +++ b/incubator/@react-native-webapis/battery-status/package.json @@ -0,0 +1,55 @@ +{ + "private": true, + "name": "@react-native-webapis/battery-status", + "version": "0.0.1", + "description": "EXPERIMENTAL - USE WITH CAUTION - Battery Status API for React Native", + "homepage": "https://github.com/microsoft/rnx-kit/tree/main/incubator/@react-native-webapis/battery-status#readme", + "license": "MIT", + "author": { + "name": "Microsoft Open Source", + "email": "microsoftopensource@users.noreply.github.com" + }, + "files": [ + "RNWBatteryStatus.podspec", + "android/*", + "ios/*", + "lib/*", + "macos/*", + "windows/*", + "react-native.config.js" + ], + "main": "lib/index.js", + "types": "lib/index.d.ts", + "repository": { + "type": "git", + "url": "https://github.com/microsoft/rnx-kit", + "directory": "incubator/@react-native-webapis/battery-status" + }, + "engines": { + "node": ">=14.15" + }, + "scripts": { + "build": "rnx-kit-scripts build", + "format": "rnx-kit-scripts format", + "format:c": "clang-format -i $(git ls-files '*.c' '*.cpp' '*.h' '*.m' '*.mm')", + "lint": "rnx-kit-scripts lint", + "lint:kt": "ktlint --code-style=android_studio --relative 'android/src/**/*.kt'" + }, + "peerDependencies": { + "react-native": ">=0.71.0" + }, + "devDependencies": { + "@rnx-kit/scripts": "*", + "eslint": "^8.0.0", + "jest": "^29.2.1", + "prettier": "^3.0.0", + "typescript": "^5.0.0" + }, + "eslintConfig": { + "extends": "@rnx-kit/eslint-config" + }, + "jest": { + "preset": "@rnx-kit/scripts" + }, + "experimental": true +} diff --git a/incubator/@react-native-webapis/battery-status/react-native.config.js b/incubator/@react-native-webapis/battery-status/react-native.config.js new file mode 100644 index 0000000000..5fa183fc30 --- /dev/null +++ b/incubator/@react-native-webapis/battery-status/react-native.config.js @@ -0,0 +1,7 @@ +module.exports = { + dependency: { + api: { + polyfill: "./lib/index.js", + }, + }, +}; diff --git a/incubator/@react-native-webapis/battery-status/src/NativeBatteryStatus.ts b/incubator/@react-native-webapis/battery-status/src/NativeBatteryStatus.ts new file mode 100644 index 0000000000..6c9e8e1b21 --- /dev/null +++ b/incubator/@react-native-webapis/battery-status/src/NativeBatteryStatus.ts @@ -0,0 +1,16 @@ +import type { TurboModule } from "react-native"; +import { TurboModuleRegistry } from "react-native"; + +// https://developer.mozilla.org/en-US/docs/Web/API/Battery_Status_API +export type BatteryStatus = { + charging: boolean; + chargingTime: number; + dischargingTime: number; + level: number; +}; + +export interface Spec extends TurboModule { + getStatus(): Promise; +} + +export default TurboModuleRegistry.getEnforcing("RNWBatteryStatus"); diff --git a/incubator/@react-native-webapis/battery-status/src/index.ts b/incubator/@react-native-webapis/battery-status/src/index.ts new file mode 100644 index 0000000000..5f0cb29add --- /dev/null +++ b/incubator/@react-native-webapis/battery-status/src/index.ts @@ -0,0 +1,24 @@ +import type { BatteryStatus } from "./NativeBatteryStatus"; +import NativeBatteryStatus from "./NativeBatteryStatus"; + +const navigator = global.navigator || {}; + +// @ts-expect-error TS2339: Property 'getBattery' does not exist on type 'Navigator'. +navigator.getBattery = (): Promise => { + return new Promise((resolve) => { + NativeBatteryStatus.getStatus().then( + ({ charging, chargingTime, dischargingTime, level }) => { + resolve({ + charging: Boolean(charging), + chargingTime: chargingTime < 0 ? Infinity : chargingTime, + dischargingTime: dischargingTime < 0 ? Infinity : dischargingTime, + level, + }); + } + ); + }); +}; + +if (navigator !== global.navigator) { + global.navigator = navigator; +} diff --git a/incubator/@react-native-webapis/battery-status/tsconfig.json b/incubator/@react-native-webapis/battery-status/tsconfig.json new file mode 100644 index 0000000000..5d6d07c16e --- /dev/null +++ b/incubator/@react-native-webapis/battery-status/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "@rnx-kit/scripts/tsconfig-shared.json", + "include": ["src"] +} diff --git a/packages/react-native-test-app-msal/ios/AuthError.swift b/packages/react-native-test-app-msal/ios/AuthError.swift index d123b7282d..c71c9ce7e6 100644 --- a/packages/react-native-test-app-msal/ios/AuthError.swift +++ b/packages/react-native-test-app-msal/ios/AuthError.swift @@ -16,7 +16,7 @@ public final class AuthError: NSObject { extension AuthError { convenience init(error: NSError?) { - if let error = error { + if let error { let correlationID = error.userInfo[MSALCorrelationIDKey] as? String let message = error.userInfo[MSALErrorDescriptionKey] as? String self.init( diff --git a/packages/react-native-test-app-msal/ios/SecretStore.swift b/packages/react-native-test-app-msal/ios/SecretStore.swift index 127e173a6d..fea1104b9d 100644 --- a/packages/react-native-test-app-msal/ios/SecretStore.swift +++ b/packages/react-native-test-app-msal/ios/SecretStore.swift @@ -39,7 +39,7 @@ extension CFDictionary { let service = "com.microsoft.ReactTestApp-MSAL" let account = "account" - guard let secret = secret else { + guard let secret else { return [ kSecAttrService: service, kSecAttrAccount: account, diff --git a/packages/react-native-test-app-msal/ios/TokenBroker.swift b/packages/react-native-test-app-msal/ios/TokenBroker.swift index 7e5d91421c..98108e72b2 100644 --- a/packages/react-native-test-app-msal/ios/TokenBroker.swift +++ b/packages/react-native-test-app-msal/ios/TokenBroker.swift @@ -47,7 +47,7 @@ public final class TokenBroker: NSObject { sender: RTAViewController, onTokenAcquired: @escaping TokenAcquiredHandler ) { - guard let selectedAccount = selectedAccount else { + guard let selectedAccount else { let error = AuthError( type: .preconditionViolated, correlationID: Constants.EmptyGUID, @@ -186,7 +186,7 @@ public final class TokenBroker: NSObject { application.acquireToken(with: parameters) { result, error in self.condition.signal() - guard let result = result else { + guard let result else { onTokenAcquired(nil, AuthError(error: error as NSError?)) return } @@ -207,7 +207,7 @@ public final class TokenBroker: NSObject { return } - guard let userPrincipalName = userPrincipalName, + guard let userPrincipalName, let cachedAccount = try? application.account(forUsername: userPrincipalName) else { acquireTokenInteractive( @@ -225,7 +225,7 @@ public final class TokenBroker: NSObject { DispatchQueue.main.async { application.acquireTokenSilent(with: parameters) { result, error in - guard let result = result else { + guard let result else { if let error = error as NSError? { if error.isInteractionRequiredError() { self.acquireTokenInteractive( diff --git a/packages/react-native-test-app-msal/package.json b/packages/react-native-test-app-msal/package.json index a58a00dc7d..18acb5456f 100644 --- a/packages/react-native-test-app-msal/package.json +++ b/packages/react-native-test-app-msal/package.json @@ -15,7 +15,7 @@ }, "scripts": { "format:c": "clang-format -i $(git ls-files '*.h' '*.m')", - "format:swift": "swiftformat --swiftversion 5.5 ios", + "format:swift": "swiftformat --swiftversion 5.7 ios", "lint:kt": "ktlint --relative --verbose 'android/src/**/*.kt'", "lint:swift": "swiftlint" }, diff --git a/packages/test-app/app.json b/packages/test-app/app.json index 06f06a47c5..452770f3f8 100644 --- a/packages/test-app/app.json +++ b/packages/test-app/app.json @@ -30,7 +30,7 @@ "userPrincipalName": "arnold@contoso.com" }, "resources": { - "android": ["dist/res", "dist/main.android.jsbundle"], + "android": ["dist/res", "dist/main.android.bundle"], "ios": ["app.json", "dist/assets", "dist/main.ios.jsbundle"], "macos": ["app.json", "dist/assets", "dist/main.macos.jsbundle"], "windows": ["dist/assets", "dist/main.windows.bundle"] diff --git a/scripts/new-package.mjs b/scripts/new-package.mjs index 265c80a74e..57ad1e9f49 100644 --- a/scripts/new-package.mjs +++ b/scripts/new-package.mjs @@ -7,7 +7,7 @@ import { fileURLToPath } from "node:url"; import yargs from "yargs"; const EXPERIMENTAL_BANNER = - "šŸš§šŸš§šŸš§šŸš§šŸš§šŸš§šŸš§šŸš§šŸš§šŸš§šŸš§\n### THIS TOOL IS EXPERIMENTAL ā€” USE WITH CAUTION\nšŸš§šŸš§šŸš§šŸš§šŸš§šŸš§šŸš§šŸš§šŸš§šŸš§šŸš§"; + "šŸš§šŸš§šŸš§šŸš§šŸš§šŸš§šŸš§šŸš§šŸš§šŸš§šŸš§\n\n### THIS TOOL IS EXPERIMENTAL ā€” USE WITH CAUTION\n\nšŸš§šŸš§šŸš§šŸš§šŸš§šŸš§šŸš§šŸš§šŸš§šŸš§šŸš§"; const USAGE_TOKEN_START = ""; const USAGE_TOKEN_END = ""; const WARNING_BANNER_TOKEN = ""; diff --git a/yarn.lock b/yarn.lock index 805e1faada..93fe212b7e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3180,6 +3180,20 @@ __metadata: languageName: node linkType: hard +"@react-native-webapis/battery-status@workspace:incubator/@react-native-webapis/battery-status": + version: 0.0.0-use.local + resolution: "@react-native-webapis/battery-status@workspace:incubator/@react-native-webapis/battery-status" + dependencies: + "@rnx-kit/scripts": "*" + eslint: ^8.0.0 + jest: ^29.2.1 + prettier: ^3.0.0 + typescript: ^5.0.0 + peerDependencies: + react-native: ">=0.71.0" + languageName: unknown + linkType: soft + "@react-native-webapis/types@workspace:incubator/@react-native-webapis/types": version: 0.0.0-use.local resolution: "@react-native-webapis/types@workspace:incubator/@react-native-webapis/types"