diff --git a/apps/tester-expo-widget/.env.development b/apps/tester-expo-widget/.env.development
new file mode 100644
index 000000000..277004f69
--- /dev/null
+++ b/apps/tester-expo-widget/.env.development
@@ -0,0 +1 @@
+EXPO_PUBLIC_FEDERATION_OWNER=expo-widget
diff --git a/apps/tester-expo-widget/.env.production b/apps/tester-expo-widget/.env.production
new file mode 100644
index 000000000..277004f69
--- /dev/null
+++ b/apps/tester-expo-widget/.env.production
@@ -0,0 +1 @@
+EXPO_PUBLIC_FEDERATION_OWNER=expo-widget
diff --git a/apps/tester-expo-widget/.gitignore b/apps/tester-expo-widget/.gitignore
new file mode 100644
index 000000000..721fe0e61
--- /dev/null
+++ b/apps/tester-expo-widget/.gitignore
@@ -0,0 +1,21 @@
+# Native projects are generated by Expo prebuild and intentionally untracked.
+/android
+/ios
+
+.DS_Store
+build/
+node_modules/
+.expo/
+*.jsbundle
+*.map
+
+**/Pods/
+**/.gradle/
+**/DerivedData/
+**/xcuserdata/
+**/.xcode.env.local
+local.properties
+*.keystore
+!debug.keystore
+
+**/@mf-types
diff --git a/apps/tester-expo-widget/app.json b/apps/tester-expo-widget/app.json
new file mode 100644
index 000000000..6a3cef592
--- /dev/null
+++ b/apps/tester-expo-widget/app.json
@@ -0,0 +1,18 @@
+{
+ "expo": {
+ "name": "Repack Expo Widget",
+ "slug": "repack-expo-widget",
+ "scheme": "repackexpowidget",
+ "version": "1.0.0",
+ "updates": {
+ "enabled": false
+ },
+ "plugins": ["@callstack/repack-expo"],
+ "ios": {
+ "bundleIdentifier": "com.callstack.repackexpowidget"
+ },
+ "android": {
+ "package": "com.callstack.repackexpowidget"
+ }
+ }
+}
diff --git a/apps/tester-expo-widget/assets/README.md b/apps/tester-expo-widget/assets/README.md
new file mode 100644
index 000000000..c1bddaa59
--- /dev/null
+++ b/apps/tester-expo-widget/assets/README.md
@@ -0,0 +1,7 @@
+# Widget fixture assets
+
+The density images are copied from the repository's Expo host fixture. The
+Material Symbols font is copied from the same fixture and is distributed under
+the Apache License 2.0 in `fonts/LICENSE-MATERIAL-SYMBOLS`.
+
+These copies keep the independently published widget artifact self-contained.
diff --git a/apps/tester-expo-widget/assets/fonts/LICENSE-MATERIAL-SYMBOLS b/apps/tester-expo-widget/assets/fonts/LICENSE-MATERIAL-SYMBOLS
new file mode 100644
index 000000000..7a4a3ea24
--- /dev/null
+++ b/apps/tester-expo-widget/assets/fonts/LICENSE-MATERIAL-SYMBOLS
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
\ No newline at end of file
diff --git a/apps/tester-expo-widget/assets/fonts/material-symbols-regular.ttf b/apps/tester-expo-widget/assets/fonts/material-symbols-regular.ttf
new file mode 100644
index 000000000..190afc39c
Binary files /dev/null and b/apps/tester-expo-widget/assets/fonts/material-symbols-regular.ttf differ
diff --git a/apps/tester-expo-widget/assets/images/widget-density.png b/apps/tester-expo-widget/assets/images/widget-density.png
new file mode 100644
index 000000000..352e4631e
Binary files /dev/null and b/apps/tester-expo-widget/assets/images/widget-density.png differ
diff --git a/apps/tester-expo-widget/assets/images/widget-density@2x.png b/apps/tester-expo-widget/assets/images/widget-density@2x.png
new file mode 100644
index 000000000..23b99fc52
Binary files /dev/null and b/apps/tester-expo-widget/assets/images/widget-density@2x.png differ
diff --git a/apps/tester-expo-widget/assets/images/widget-density@3x.png b/apps/tester-expo-widget/assets/images/widget-density@3x.png
new file mode 100644
index 000000000..3719056cc
Binary files /dev/null and b/apps/tester-expo-widget/assets/images/widget-density@3x.png differ
diff --git a/apps/tester-expo-widget/babel.config.cjs b/apps/tester-expo-widget/babel.config.cjs
new file mode 100644
index 000000000..e135c946f
--- /dev/null
+++ b/apps/tester-expo-widget/babel.config.cjs
@@ -0,0 +1,6 @@
+module.exports = function babelConfig(api) {
+ api.cache(true);
+ return {
+ presets: ['babel-preset-expo'],
+ };
+};
diff --git a/apps/tester-expo-widget/expo-env.d.ts b/apps/tester-expo-widget/expo-env.d.ts
new file mode 100644
index 000000000..e6394b247
--- /dev/null
+++ b/apps/tester-expo-widget/expo-env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/apps/tester-expo-widget/index.ts b/apps/tester-expo-widget/index.ts
new file mode 100644
index 000000000..c1c862e92
--- /dev/null
+++ b/apps/tester-expo-widget/index.ts
@@ -0,0 +1,4 @@
+import { registerRootComponent } from 'expo';
+import App from './src/App';
+
+registerRootComponent(App);
diff --git a/apps/tester-expo-widget/package.json b/apps/tester-expo-widget/package.json
new file mode 100644
index 000000000..1953441f6
--- /dev/null
+++ b/apps/tester-expo-widget/package.json
@@ -0,0 +1,39 @@
+{
+ "name": "tester-expo-widget",
+ "version": "0.0.1",
+ "private": true,
+ "main": "index.ts",
+ "scripts": {
+ "repack:start": "react-native webpack-start",
+ "repack:start:remote": "react-native webpack-start --port 8082",
+ "repack:ios": "expo run:ios --no-bundler",
+ "repack:android": "expo run:android --no-bundler",
+ "repack:build:production": "pnpm run repack:build:production:ios && pnpm run repack:build:production:android",
+ "repack:build:production:ios": "node ./scripts/build-production.mjs ios",
+ "repack:build:production:android": "node ./scripts/build-production.mjs android",
+ "typecheck": "tsc --noEmit"
+ },
+ "dependencies": {
+ "@callstack/repack": "workspace:*",
+ "@callstack/repack-expo": "workspace:*",
+ "@module-federation/enhanced": "2.1.0",
+ "expo": "~57.0.9",
+ "expo-asset": "~57.0.8",
+ "expo-constants": "~57.0.8",
+ "expo-font": "~57.0.1",
+ "react": "19.2.3",
+ "react-native": "0.86.2"
+ },
+ "devDependencies": {
+ "@babel/core": "^7.29.0",
+ "@react-native-community/cli": "20.1.2",
+ "@react-native-community/cli-platform-android": "20.1.2",
+ "@react-native-community/cli-platform-ios": "20.1.2",
+ "@react-native/babel-preset": "0.86.2",
+ "@react-native/community-cli-plugin": "0.86.2",
+ "@rspack/core": "catalog:",
+ "@types/react": "~19.2.2",
+ "babel-preset-expo": "~57.0.5",
+ "typescript": "~6.0.3"
+ }
+}
diff --git a/apps/tester-expo-widget/react-native.config.js b/apps/tester-expo-widget/react-native.config.js
new file mode 100644
index 000000000..d3fe7ef96
--- /dev/null
+++ b/apps/tester-expo-widget/react-native.config.js
@@ -0,0 +1,3 @@
+module.exports = {
+ commands: require('@callstack/repack/commands/rspack'),
+};
diff --git a/apps/tester-expo-widget/rspack.config.mjs b/apps/tester-expo-widget/rspack.config.mjs
new file mode 100644
index 000000000..e0739c7dd
--- /dev/null
+++ b/apps/tester-expo-widget/rspack.config.mjs
@@ -0,0 +1,94 @@
+import path from 'node:path';
+import { fileURLToPath } from 'node:url';
+import * as Repack from '@callstack/repack';
+import { ExpoPlugin } from '@callstack/repack-expo/rspack';
+
+const projectRoot = path.dirname(fileURLToPath(import.meta.url));
+
+export default (env) => {
+ const {
+ mode = 'development',
+ platform = process.env.PLATFORM,
+ devServer,
+ } = env;
+
+ if (platform !== 'ios' && platform !== 'android') {
+ throw new Error(
+ 'tester-expo-widget requires PLATFORM=ios or PLATFORM=android'
+ );
+ }
+
+ const remoteOutputPath = path.join(projectRoot, 'build', 'remote', platform);
+ const remotePublicPath = `http://localhost:8082/${platform}/remote-assets`;
+
+ return {
+ context: projectRoot,
+ devServer,
+ devtool: 'source-map',
+ mode,
+ name: platform,
+ module: {
+ rules: Repack.getAssetTransformRules({
+ remote: { publicPath: remotePublicPath },
+ }),
+ },
+ output: {
+ clean: true,
+ path: path.join(projectRoot, 'build', 'rspack', platform),
+ publicPath: `http://localhost:8082/${platform}/`,
+ uniqueName: `tester-expo-widget-${platform}`,
+ },
+ plugins: [
+ new ExpoPlugin({
+ platform,
+ repack: {
+ output: {
+ auxiliaryAssetsPath: remoteOutputPath,
+ },
+ extraChunks: [
+ {
+ include: /.*/,
+ type: 'remote',
+ outputPath: remoteOutputPath,
+ },
+ ],
+ },
+ }),
+ new Repack.plugins.ModuleFederationPluginV2({
+ name: 'ExpoWidget',
+ filename: 'ExpoWidget.container.js.bundle',
+ exposes: {
+ './Widget': './src/Widget',
+ },
+ dts: false,
+ shared: {
+ react: {
+ singleton: true,
+ eager: true,
+ requiredVersion: '19.2.3',
+ },
+ 'react-native': {
+ singleton: true,
+ eager: true,
+ requiredVersion: '0.86.2',
+ },
+ 'expo-constants': {
+ singleton: true,
+ eager: true,
+ requiredVersion: '~57.0.8',
+ },
+ 'expo-asset': {
+ singleton: true,
+ eager: true,
+ requiredVersion: '~57.0.8',
+ },
+ 'expo-font': {
+ singleton: true,
+ eager: true,
+ requiredVersion: '~57.0.1',
+ },
+ },
+ }),
+ ],
+ };
+};
diff --git a/apps/tester-expo-widget/scripts/build-production.mjs b/apps/tester-expo-widget/scripts/build-production.mjs
new file mode 100644
index 000000000..a417ab136
--- /dev/null
+++ b/apps/tester-expo-widget/scripts/build-production.mjs
@@ -0,0 +1,168 @@
+import { spawnSync } from 'node:child_process';
+import fs from 'node:fs';
+import path from 'node:path';
+import { fileURLToPath, pathToFileURL } from 'node:url';
+
+const FEDERATION_METADATA_FILES = ['mf-manifest.json', 'mf-stats.json'];
+const supportedPlatforms = new Set(['android', 'ios']);
+
+function assertProductionPlatform(platform) {
+ if (!supportedPlatforms.has(platform)) {
+ throw new Error(
+ `Expected production widget platform to be ios or android, received ${JSON.stringify(platform)}`
+ );
+ }
+ return platform;
+}
+
+const scriptDirectory = path.dirname(fileURLToPath(import.meta.url));
+export const defaultProjectRoot = path.resolve(scriptDirectory, '..');
+
+export function resolveProductionArtifactPaths(projectRoot, platform) {
+ assertProductionPlatform(platform);
+ const standaloneRoot = path.join(
+ projectRoot,
+ 'build',
+ 'standalone',
+ platform
+ );
+ const bundleFilename =
+ platform === 'ios' ? 'main.jsbundle' : 'index.android.bundle';
+
+ return {
+ assetsDest:
+ platform === 'ios' ? standaloneRoot : path.join(standaloneRoot, 'res'),
+ bundleOutput: path.join(standaloneRoot, bundleFilename),
+ configPath: path.join(projectRoot, 'rspack.config.mjs'),
+ entryFile: path.join(projectRoot, 'index.ts'),
+ publishRoot: path.join(projectRoot, 'build', 'remote', platform),
+ sourceMapOutput: path.join(standaloneRoot, `${bundleFilename}.map`),
+ stagingRoot: path.join(projectRoot, 'build', 'rspack', platform),
+ standaloneRoot,
+ };
+}
+
+export function createBundleInvocation({
+ commandName = 'bundle',
+ platform,
+ projectRoot = defaultProjectRoot,
+}) {
+ if (commandName !== 'bundle' && commandName !== 'webpack-bundle') {
+ throw new Error(
+ `Expected Re.Pack production command to be bundle or webpack-bundle, received ${JSON.stringify(commandName)}`
+ );
+ }
+ const paths = resolveProductionArtifactPaths(projectRoot, platform);
+ return {
+ arguments: [
+ commandName,
+ '--config',
+ paths.configPath,
+ '--platform',
+ platform,
+ '--entry-file',
+ paths.entryFile,
+ '--dev=false',
+ '--bundle-output',
+ paths.bundleOutput,
+ '--sourcemap-output',
+ paths.sourceMapOutput,
+ '--assets-dest',
+ paths.assetsDest,
+ ],
+ command: 'react-native',
+ cwd: projectRoot,
+ paths,
+ };
+}
+
+export function createFederationMetadataCopyPlan({ publishRoot, stagingRoot }) {
+ return FEDERATION_METADATA_FILES.map((filename) => ({
+ destination: path.join(publishRoot, filename),
+ filename,
+ source: path.join(stagingRoot, filename),
+ }));
+}
+
+export function publishFederationMetadata(
+ paths,
+ { copyFileSync = fs.copyFileSync, mkdirSync = fs.mkdirSync } = {}
+) {
+ const plan = createFederationMetadataCopyPlan(paths);
+ mkdirSync(paths.publishRoot, { recursive: true });
+ for (const item of plan) {
+ copyFileSync(item.source, item.destination);
+ }
+ return plan;
+}
+
+function renderInvocation(invocation) {
+ return [invocation.command, ...invocation.arguments]
+ .map((part) => (part.includes(' ') ? JSON.stringify(part) : part))
+ .join(' ');
+}
+
+export function runProductionBuild({
+ commandName = 'bundle',
+ platform,
+ projectRoot = defaultProjectRoot,
+ spawn = spawnSync,
+}) {
+ const invocation = createBundleInvocation({
+ commandName,
+ platform,
+ projectRoot,
+ });
+
+ fs.rmSync(invocation.paths.publishRoot, { force: true, recursive: true });
+ fs.rmSync(invocation.paths.standaloneRoot, { force: true, recursive: true });
+ fs.mkdirSync(invocation.paths.standaloneRoot, { recursive: true });
+
+ console.log(`Running ${renderInvocation(invocation)}`);
+ const result = spawn(invocation.command, invocation.arguments, {
+ cwd: invocation.cwd,
+ env: process.env,
+ stdio: 'inherit',
+ });
+ if (result.error) {
+ throw new Error(
+ `Could not run the standard Re.Pack ${commandName} command: ${result.error.message}`
+ );
+ }
+ if (result.status !== 0) {
+ throw new Error(
+ `Standard Re.Pack ${commandName} command exited with status ${String(result.status)}`
+ );
+ }
+
+ const copiedMetadata = publishFederationMetadata(invocation.paths);
+ return { copiedMetadata, invocation };
+}
+
+function parseArguments(arguments_) {
+ const commandName = arguments_.includes('--webpack-bundle')
+ ? 'webpack-bundle'
+ : 'bundle';
+ const positionals = arguments_.filter(
+ (argument) => argument !== '--webpack-bundle'
+ );
+ if (positionals.length !== 1) {
+ throw new Error(
+ 'Usage: node scripts/build-production.mjs [--webpack-bundle]'
+ );
+ }
+ return { commandName, platform: assertProductionPlatform(positionals[0]) };
+}
+
+if (import.meta.url === pathToFileURL(process.argv[1]).href) {
+ try {
+ const options = parseArguments(process.argv.slice(2));
+ const result = runProductionBuild(options);
+ console.log(
+ `Published ${options.platform} Expo widget artifacts to ${result.invocation.paths.publishRoot}.`
+ );
+ } catch (error) {
+ console.error(error instanceof Error ? error.message : error);
+ process.exitCode = 1;
+ }
+}
diff --git a/apps/tester-expo-widget/src/App.tsx b/apps/tester-expo-widget/src/App.tsx
new file mode 100644
index 000000000..2107d411a
--- /dev/null
+++ b/apps/tester-expo-widget/src/App.tsx
@@ -0,0 +1,18 @@
+import { SafeAreaView, StyleSheet } from 'react-native';
+import Widget from './Widget';
+
+export default function App() {
+ return (
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ justifyContent: 'center',
+ padding: 24,
+ },
+});
diff --git a/apps/tester-expo-widget/src/Widget.tsx b/apps/tester-expo-widget/src/Widget.tsx
new file mode 100644
index 000000000..46262867e
--- /dev/null
+++ b/apps/tester-expo-widget/src/Widget.tsx
@@ -0,0 +1,160 @@
+import { Asset } from 'expo-asset';
+import Constants from 'expo-constants';
+import { useFonts } from 'expo-font';
+import {
+ type ComponentType,
+ useCallback,
+ useEffect,
+ useMemo,
+ useState,
+} from 'react';
+import { Image, PixelRatio, StyleSheet, Text, View } from 'react-native';
+
+const widgetDensityImage = require('../assets/images/widget-density.png');
+const widgetDensityAsset = Asset.fromModule(widgetDensityImage);
+const widgetFontAsset = Asset.fromModule(
+ require('../assets/fonts/material-symbols-regular.ttf')
+);
+const widgetRuntimeGeneration = Date.now();
+
+type LazyDetailsComponent = ComponentType<{ onReady: () => void }>;
+
+type LazyDetailsLoadState =
+ | { status: 'loading' }
+ | { component: LazyDetailsComponent; status: 'loaded' }
+ | { error: Error; status: 'failed' };
+
+function LazyDetailsLoader({
+ onReady,
+}: {
+ onReady: () => void;
+}) {
+ const [loadState, setLoadState] = useState({
+ status: 'loading',
+ });
+
+ useEffect(() => {
+ let active = true;
+ import(
+ /* webpackChunkName: "expo-widget-lazy" */ './WidgetLazyDetails'
+ ).then(
+ ({ default: component }) => {
+ if (active) setLoadState({ component, status: 'loaded' });
+ },
+ (error: Error) => {
+ if (active) setLoadState({ error, status: 'failed' });
+ }
+ );
+ return () => {
+ active = false;
+ };
+ }, []);
+
+ if (loadState.status === 'failed') throw loadState.error;
+ if (loadState.status === 'loading') {
+ return (
+
+ Loading nested widget chunk…
+
+ );
+ }
+
+ const LazyDetails = loadState.component;
+ return ;
+}
+
+export default function Widget({
+ recoveryAttempt = 0,
+}: {
+ recoveryAttempt?: number;
+}) {
+ const resolvedImage = useMemo(
+ () => Image.resolveAssetSource(widgetDensityImage),
+ []
+ );
+ const imageSource = useMemo(() => {
+ const separator = resolvedImage.uri.includes('?') ? '&' : '?';
+ return {
+ ...resolvedImage,
+ uri: `${resolvedImage.uri}${separator}runtimeGeneration=${widgetRuntimeGeneration}&recoveryAttempt=${recoveryAttempt}`,
+ };
+ }, [recoveryAttempt, resolvedImage]);
+ const [imageLoaded, setImageLoaded] = useState(false);
+ const [imageError, setImageError] = useState();
+ const [lazyLoaded, setLazyLoaded] = useState(false);
+ const [fontsLoaded, fontError] = useFonts({
+ ExpoWidgetFixture: widgetFontAsset,
+ });
+ const handleLazyReady = useCallback(() => setLazyLoaded(true), []);
+ const ready = imageLoaded && fontsLoaded && lazyLoaded;
+
+ if (imageError) throw imageError;
+ if (fontError) throw fontError;
+
+ return (
+
+ {ready ? (
+ Expo widget resources loaded
+ ) : null}
+ Expo MF v2 widget loaded by Re.Pack.
+
+ Native app owner: {Constants.expoConfig?.name ?? 'host'}
+
+
+ Widget public environment:{' '}
+ {process.env.EXPO_PUBLIC_FEDERATION_OWNER ?? 'missing'}
+
+
+ Widget asset type: {widgetDensityAsset.type}
+
+ {imageLoaded ? (
+
+ Widget density image loaded
+
+ ) : null}
+
+ setImageError(new Error(nativeEvent.error))
+ }
+ onLoad={() => setImageLoaded(true)}
+ source={imageSource}
+ style={styles.image}
+ testID="expo-widget-density-image"
+ />
+
+ Selected widget density scale: {resolvedImage.scale}
+
+
+ Widget device density scale: {PixelRatio.get()}
+
+ {fontsLoaded ? (
+
+ home
+
+ ) : (
+ Loading widget font…
+ )}
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ card: {
+ backgroundColor: '#fff7ed',
+ borderColor: '#c2410c',
+ borderRadius: 8,
+ borderWidth: 1,
+ gap: 8,
+ padding: 16,
+ },
+ fixtureFont: {
+ fontFamily: 'ExpoWidgetFixture',
+ fontSize: 32,
+ },
+ image: {
+ height: 48,
+ width: 48,
+ },
+});
diff --git a/apps/tester-expo-widget/src/WidgetLazyDetails.tsx b/apps/tester-expo-widget/src/WidgetLazyDetails.tsx
new file mode 100644
index 000000000..e8f28a289
--- /dev/null
+++ b/apps/tester-expo-widget/src/WidgetLazyDetails.tsx
@@ -0,0 +1,30 @@
+import { StyleSheet, Text, View } from 'react-native';
+
+const lazyChunkSentinel = 'repack-expo-widget-lazy-sentinel';
+
+export default function WidgetLazyDetails({
+ onReady,
+}: {
+ onReady: () => void;
+}) {
+ return (
+
+ This content was loaded from a nested remote widget chunk.
+
+ );
+}
+
+const styles = StyleSheet.create({
+ card: {
+ backgroundColor: '#eff6ff',
+ borderColor: '#1d4ed8',
+ borderRadius: 8,
+ borderWidth: 1,
+ padding: 12,
+ },
+});
diff --git a/apps/tester-expo-widget/tsconfig.json b/apps/tester-expo-widget/tsconfig.json
new file mode 100644
index 000000000..5bfb77848
--- /dev/null
+++ b/apps/tester-expo-widget/tsconfig.json
@@ -0,0 +1,7 @@
+{
+ "extends": "expo/tsconfig.base",
+ "compilerOptions": {
+ "strict": true
+ },
+ "include": ["*.ts", "*.tsx", "src/**/*.ts", "src/**/*.tsx", "expo-env.d.ts"]
+}
diff --git a/apps/tester-expo/.env.development b/apps/tester-expo/.env.development
new file mode 100644
index 000000000..751c8bfb9
--- /dev/null
+++ b/apps/tester-expo/.env.development
@@ -0,0 +1 @@
+EXPO_PUBLIC_FEDERATION_OWNER=expo-host
diff --git a/apps/tester-expo/.env.production b/apps/tester-expo/.env.production
new file mode 100644
index 000000000..751c8bfb9
--- /dev/null
+++ b/apps/tester-expo/.env.production
@@ -0,0 +1 @@
+EXPO_PUBLIC_FEDERATION_OWNER=expo-host
diff --git a/apps/tester-expo/.gitignore b/apps/tester-expo/.gitignore
new file mode 100644
index 000000000..a486fe0a3
--- /dev/null
+++ b/apps/tester-expo/.gitignore
@@ -0,0 +1,3 @@
+# Native projects are generated by Expo prebuild and intentionally untracked.
+/android
+/ios
diff --git a/apps/tester-expo/app.json b/apps/tester-expo/app.json
new file mode 100644
index 000000000..e9fdeb8fc
--- /dev/null
+++ b/apps/tester-expo/app.json
@@ -0,0 +1,29 @@
+{
+ "expo": {
+ "name": "Repack Expo Tester",
+ "slug": "repack-expo-tester",
+ "scheme": "repackexpo",
+ "version": "1.0.0",
+ "orientation": "portrait",
+ "updates": {
+ "enabled": false
+ },
+ "plugins": [
+ "expo-router",
+ "@callstack/repack-expo",
+ "./plugins/with-localhost-cleartext"
+ ],
+ "ios": {
+ "bundleIdentifier": "com.callstack.repackexpo"
+ },
+ "android": {
+ "package": "com.callstack.repackexpo"
+ },
+ "extra": {
+ "eas": {
+ "projectId": "49a167cf-9412-446e-ba71-0c07c816ed93"
+ }
+ },
+ "owner": "wuskas"
+ }
+}
diff --git a/apps/tester-expo/app/_layout.tsx b/apps/tester-expo/app/_layout.tsx
new file mode 100644
index 000000000..624cfe6c7
--- /dev/null
+++ b/apps/tester-expo/app/_layout.tsx
@@ -0,0 +1,5 @@
+import { Stack } from 'expo-router';
+
+export default function RootLayout() {
+ return ;
+}
diff --git a/apps/tester-expo/app/density-image.tsx b/apps/tester-expo/app/density-image.tsx
new file mode 100644
index 000000000..a3d0ee7cd
--- /dev/null
+++ b/apps/tester-expo/app/density-image.tsx
@@ -0,0 +1,55 @@
+import { Link } from 'expo-router';
+import { useState } from 'react';
+import { Image, PixelRatio, StyleSheet, Text, View } from 'react-native';
+
+const densityImage = require('../assets/images/density.png');
+
+export default function DensityImageRoute() {
+ const resolvedImage = Image.resolveAssetSource(densityImage);
+ const [imageLoaded, setImageLoaded] = useState(false);
+ const [imageError, setImageError] = useState(false);
+
+ return (
+
+ {imageLoaded && !imageError ? (
+
+ Density-aware bundled image loaded
+
+ ) : null}
+ {imageError ? (
+
+ Density-aware bundled image failed to load
+
+ ) : null}
+ setImageError(true)}
+ onLoad={() => setImageLoaded(true)}
+ source={densityImage}
+ style={styles.image}
+ testID="density-image"
+ />
+
+ Selected density scale: {resolvedImage.scale}
+
+
+ Device density scale: {PixelRatio.get()}
+
+ Back home
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ alignItems: 'center',
+ flex: 1,
+ gap: 16,
+ justifyContent: 'center',
+ padding: 24,
+ },
+ image: {
+ height: 48,
+ width: 48,
+ },
+});
diff --git a/apps/tester-expo/app/details.tsx b/apps/tester-expo/app/details.tsx
new file mode 100644
index 000000000..ddc776e6a
--- /dev/null
+++ b/apps/tester-expo/app/details.tsx
@@ -0,0 +1,24 @@
+import Constants from 'expo-constants';
+import { Link } from 'expo-router';
+import { StyleSheet, Text, View } from 'react-native';
+
+export default function DetailsRoute() {
+ return (
+
+
+ Expo runtime: {Constants.expoVersion ?? 'development build'}
+
+ Back home
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ alignItems: 'center',
+ flex: 1,
+ gap: 16,
+ justifyContent: 'center',
+ padding: 24,
+ },
+});
diff --git a/apps/tester-expo/app/font.tsx b/apps/tester-expo/app/font.tsx
new file mode 100644
index 000000000..08fe4d71a
--- /dev/null
+++ b/apps/tester-expo/app/font.tsx
@@ -0,0 +1,49 @@
+import { useFonts } from 'expo-font';
+import { Link } from 'expo-router';
+import { StyleSheet, Text, View } from 'react-native';
+
+export default function FontRoute() {
+ const [fontsLoaded, fontError] = useFonts({
+ ExpoFixture: require('../assets/fonts/material-symbols-regular.ttf'),
+ });
+
+ if (fontError) {
+ return (
+
+ Font error: {fontError.message}
+
+ );
+ }
+
+ if (!fontsLoaded) {
+ return (
+
+ Loading bundled font…
+
+ );
+ }
+
+ return (
+
+ Bundled font loaded by expo-font:
+
+ home
+
+ Back home
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ alignItems: 'center',
+ flex: 1,
+ gap: 16,
+ justifyContent: 'center',
+ padding: 24,
+ },
+ fixtureFont: {
+ fontFamily: 'ExpoFixture',
+ fontSize: 32,
+ },
+});
diff --git a/apps/tester-expo/app/image.tsx b/apps/tester-expo/app/image.tsx
new file mode 100644
index 000000000..122c16af6
--- /dev/null
+++ b/apps/tester-expo/app/image.tsx
@@ -0,0 +1,68 @@
+import { Asset } from 'expo-asset';
+import { Link } from 'expo-router';
+import { useState } from 'react';
+import { Image, StyleSheet, Text, View } from 'react-native';
+
+const packagedImage = require('../assets/images/repack.png');
+const packagedAsset = Asset.fromModule(packagedImage);
+const monorepoImage = require('../../../tests/integration/src/loaders/__fixtures__/assets/logo.png');
+const monorepoAsset = Asset.fromModule(monorepoImage);
+
+export default function ImageRoute() {
+ const [appLocalImageLoaded, setAppLocalImageLoaded] = useState(false);
+ const [monorepoImageLoaded, setMonorepoImageLoaded] = useState(false);
+ const [imageError, setImageError] = useState();
+ const imagesLoaded =
+ appLocalImageLoaded && monorepoImageLoaded && !imageError;
+
+ return (
+
+ {imagesLoaded ? (
+
+ Expo assets loaded: {packagedAsset.name}.{packagedAsset.type} and{' '}
+ monorepo {monorepoAsset.name}.{monorepoAsset.type}
+
+ ) : null}
+ {imageError ? {imageError} : null}
+
+ setImageError((error) => error ?? 'App-local image failed to load')
+ }
+ onLoad={() => setAppLocalImageLoaded(true)}
+ source={packagedImage}
+ style={styles.logo}
+ testID="app-local-image"
+ />
+
+ setImageError((error) => error ?? 'Monorepo image failed to load')
+ }
+ onLoad={() => setMonorepoImageLoaded(true)}
+ source={monorepoImage}
+ style={styles.monorepoLogo}
+ testID="monorepo-image"
+ />
+ Back home
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ alignItems: 'center',
+ flex: 1,
+ gap: 16,
+ justifyContent: 'center',
+ padding: 24,
+ },
+ logo: {
+ height: 30,
+ width: 147,
+ },
+ monorepoLogo: {
+ height: 64,
+ width: 64,
+ },
+});
diff --git a/apps/tester-expo/app/index.tsx b/apps/tester-expo/app/index.tsx
new file mode 100644
index 000000000..8e5a32427
--- /dev/null
+++ b/apps/tester-expo/app/index.tsx
@@ -0,0 +1,42 @@
+import { Link } from 'expo-router';
+import { StatusBar } from 'expo-status-bar';
+import { useState } from 'react';
+import { Button, StyleSheet, Text, View } from 'react-native';
+import FastRefreshLeaf from '../components/FastRefreshLeaf';
+import SymbolicationProbe from '../components/SymbolicationProbe';
+
+export default function HomeRoute() {
+ const [count, setCount] = useState(0);
+
+ return (
+
+ Expo Router compiled by Re.Pack
+ Parent state: {count}
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ alignItems: 'center',
+ flex: 1,
+ gap: 16,
+ justifyContent: 'center',
+ padding: 24,
+ },
+});
diff --git a/apps/tester-expo/app/lazy.tsx b/apps/tester-expo/app/lazy.tsx
new file mode 100644
index 000000000..7b4fca9c1
--- /dev/null
+++ b/apps/tester-expo/app/lazy.tsx
@@ -0,0 +1,39 @@
+import { type ErrorBoundaryProps, Link } from 'expo-router';
+import { Suspense, lazy } from 'react';
+import { StyleSheet, Text, View } from 'react-native';
+
+const LazyCard = lazy(() => import('../components/LazyCard.local'));
+
+export function ErrorBoundary({ error }: ErrorBoundaryProps) {
+ return (
+
+ Lazy component route failed
+
+ Unable to load the local async chunk: {error.message}
+
+ Back home
+
+ );
+}
+
+export default function LazyRoute() {
+ return (
+
+ Lazy component route
+ Loading chunk…}>
+
+
+ Back home
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ alignItems: 'center',
+ flex: 1,
+ gap: 16,
+ justifyContent: 'center',
+ padding: 24,
+ },
+});
diff --git a/apps/tester-expo/app/ordinary-widget.tsx b/apps/tester-expo/app/ordinary-widget.tsx
new file mode 100644
index 000000000..dae0b0551
--- /dev/null
+++ b/apps/tester-expo/app/ordinary-widget.tsx
@@ -0,0 +1,41 @@
+import { type ErrorBoundaryProps, Link } from 'expo-router';
+import { Suspense, lazy } from 'react';
+import { StyleSheet, Text, View } from 'react-native';
+
+const OrdinaryWidget = lazy(() => import('OrdinaryWidget/Widget'));
+
+export function ErrorBoundary({ error }: ErrorBoundaryProps) {
+ return (
+
+ Unable to load the ordinary Re.Pack widget.
+ {error.message}
+ Back home
+
+ );
+}
+
+export default function OrdinaryWidgetRoute() {
+ return (
+
+
+ Ordinary Re.Pack widget route
+
+ Loading widget…}
+ >
+
+
+ Back home
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ alignItems: 'center',
+ flex: 1,
+ gap: 16,
+ justifyContent: 'center',
+ padding: 24,
+ },
+});
diff --git a/apps/tester-expo/app/widget.tsx b/apps/tester-expo/app/widget.tsx
new file mode 100644
index 000000000..c1e0d1a06
--- /dev/null
+++ b/apps/tester-expo/app/widget.tsx
@@ -0,0 +1,149 @@
+import { loadRemote } from '@module-federation/enhanced/runtime';
+import { type ErrorBoundaryProps, Link } from 'expo-router';
+import {
+ Component,
+ type ComponentType,
+ type ReactNode,
+ useEffect,
+ useState,
+} from 'react';
+import { Button, StyleSheet, Text, View } from 'react-native';
+
+type ExpoWidgetComponent = ComponentType<{ recoveryAttempt?: number }>;
+
+type ExpoWidgetLoadState =
+ | { status: 'loading' }
+ | { component: ExpoWidgetComponent; status: 'loaded' }
+ | { error: Error; status: 'failed' };
+
+interface WidgetRecoveryBoundaryProps {
+ children: (attempt: number) => ReactNode;
+}
+
+interface WidgetRecoveryBoundaryState {
+ attempt: number;
+ error: Error | null;
+}
+
+class WidgetRecoveryBoundary extends Component<
+ WidgetRecoveryBoundaryProps,
+ WidgetRecoveryBoundaryState
+> {
+ state: WidgetRecoveryBoundaryState = {
+ attempt: 0,
+ error: null,
+ };
+
+ static getDerivedStateFromError(error: Error) {
+ return { error };
+ }
+
+ retry = () => {
+ this.setState(({ attempt }) => ({
+ attempt: attempt + 1,
+ error: null,
+ }));
+ };
+
+ render() {
+ if (this.state.error) {
+ return (
+
+ Unable to load the Expo widget.
+
+ {this.state.error.message}
+
+
+
+ );
+ }
+
+ return this.props.children(this.state.attempt);
+ }
+}
+
+function ExpoWidgetAttempt({ attempt }: { attempt: number }) {
+ const [loadState, setLoadState] = useState({
+ status: 'loading',
+ });
+
+ useEffect(() => {
+ let active = true;
+ loadRemote<{ default: ExpoWidgetComponent }>('ExpoWidget/Widget').then(
+ (loadedModule) => {
+ if (!loadedModule) {
+ if (active) {
+ setLoadState({
+ error: new Error('ExpoWidget/Widget returned no module'),
+ status: 'failed',
+ });
+ }
+ return;
+ }
+ const { default: component } = loadedModule;
+ if (active) setLoadState({ component, status: 'loaded' });
+ },
+ (error: Error) => {
+ if (active) setLoadState({ error, status: 'failed' });
+ }
+ );
+ return () => {
+ active = false;
+ };
+ }, []);
+
+ if (loadState.status === 'failed') throw loadState.error;
+ if (loadState.status === 'loading') {
+ return Loading widget…;
+ }
+
+ const ExpoWidget = loadState.component;
+
+ return ;
+}
+
+export function ErrorBoundary({ error }: ErrorBoundaryProps) {
+ return (
+
+ Unable to load the Expo widget.
+
+ {error.message}
+
+ Back home
+
+ );
+}
+
+export default function WidgetRoute() {
+ return (
+
+ Expo federated widget route
+
+ Host public environment:{' '}
+ {process.env.EXPO_PUBLIC_FEDERATION_OWNER ?? 'missing'}
+
+
+ {(attempt) => }
+
+ Back home
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ alignItems: 'center',
+ flex: 1,
+ gap: 16,
+ justifyContent: 'center',
+ padding: 24,
+ },
+ recovery: {
+ alignItems: 'center',
+ gap: 16,
+ },
+});
diff --git a/apps/tester-expo/assets/README.md b/apps/tester-expo/assets/README.md
new file mode 100644
index 000000000..4db275135
--- /dev/null
+++ b/apps/tester-expo/assets/README.md
@@ -0,0 +1,10 @@
+# Fixture assets
+
+These files are copied from existing Re.Pack repository fixtures so the Expo
+tester remains self-contained:
+
+- `images/repack.png` comes from `apps/tester-app/src/miniapp/callstack-dark.png`;
+- `images/density*.png` come from `apps/tester-app/src/assetsTest/localAssets`;
+- `fonts/material-symbols-regular.ttf` comes from the installed
+ `@expo-google-fonts/material-symbols` package and is distributed under the
+ Apache License 2.0 in `fonts/LICENSE-MATERIAL-SYMBOLS`.
diff --git a/apps/tester-expo/assets/fonts/LICENSE-MATERIAL-SYMBOLS b/apps/tester-expo/assets/fonts/LICENSE-MATERIAL-SYMBOLS
new file mode 100644
index 000000000..7a4a3ea24
--- /dev/null
+++ b/apps/tester-expo/assets/fonts/LICENSE-MATERIAL-SYMBOLS
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
\ No newline at end of file
diff --git a/apps/tester-expo/assets/fonts/material-symbols-regular.ttf b/apps/tester-expo/assets/fonts/material-symbols-regular.ttf
new file mode 100644
index 000000000..190afc39c
Binary files /dev/null and b/apps/tester-expo/assets/fonts/material-symbols-regular.ttf differ
diff --git a/apps/tester-expo/assets/images/density.png b/apps/tester-expo/assets/images/density.png
new file mode 100644
index 000000000..352e4631e
Binary files /dev/null and b/apps/tester-expo/assets/images/density.png differ
diff --git a/apps/tester-expo/assets/images/density@2x.png b/apps/tester-expo/assets/images/density@2x.png
new file mode 100644
index 000000000..23b99fc52
Binary files /dev/null and b/apps/tester-expo/assets/images/density@2x.png differ
diff --git a/apps/tester-expo/assets/images/density@3x.png b/apps/tester-expo/assets/images/density@3x.png
new file mode 100644
index 000000000..3719056cc
Binary files /dev/null and b/apps/tester-expo/assets/images/density@3x.png differ
diff --git a/apps/tester-expo/assets/images/repack.png b/apps/tester-expo/assets/images/repack.png
new file mode 100644
index 000000000..0a4d65372
Binary files /dev/null and b/apps/tester-expo/assets/images/repack.png differ
diff --git a/apps/tester-expo/babel.config.cjs b/apps/tester-expo/babel.config.cjs
new file mode 100644
index 000000000..e135c946f
--- /dev/null
+++ b/apps/tester-expo/babel.config.cjs
@@ -0,0 +1,6 @@
+module.exports = function babelConfig(api) {
+ api.cache(true);
+ return {
+ presets: ['babel-preset-expo'],
+ };
+};
diff --git a/apps/tester-expo/components/FastRefreshLeaf.tsx b/apps/tester-expo/components/FastRefreshLeaf.tsx
new file mode 100644
index 000000000..73f5140e8
--- /dev/null
+++ b/apps/tester-expo/components/FastRefreshLeaf.tsx
@@ -0,0 +1,5 @@
+import { Text } from 'react-native';
+
+export default function FastRefreshLeaf() {
+ return EXPO_FAST_REFRESH_LEAF_MARKER;
+}
diff --git a/apps/tester-expo/components/LazyCard.local.tsx b/apps/tester-expo/components/LazyCard.local.tsx
new file mode 100644
index 000000000..d341a306b
--- /dev/null
+++ b/apps/tester-expo/components/LazyCard.local.tsx
@@ -0,0 +1,25 @@
+import { StyleSheet, Text, View } from 'react-native';
+
+const lazyChunkSentinel = 'repack-expo-local-lazy-card-sentinel';
+
+export default function LazyCard() {
+ return (
+
+ This component was loaded from an async Re.Pack chunk.
+
+ );
+}
+
+const styles = StyleSheet.create({
+ card: {
+ backgroundColor: '#e9f7ef',
+ borderColor: '#1f7a4d',
+ borderRadius: 8,
+ borderWidth: 1,
+ padding: 16,
+ },
+});
diff --git a/apps/tester-expo/components/SymbolicationProbe.tsx b/apps/tester-expo/components/SymbolicationProbe.tsx
new file mode 100644
index 000000000..c20891007
--- /dev/null
+++ b/apps/tester-expo/components/SymbolicationProbe.tsx
@@ -0,0 +1,28 @@
+import { Button } from 'react-native';
+
+declare global {
+ var __REPACK_ANDROID_SYMBOLICATION_STACK__: string | undefined;
+}
+
+function throwSymbolicationProbeError() {
+ throw new Error('EXPO_ANDROID_SYMBOLICATION_SENTINEL');
+}
+
+function captureSymbolicationStack() {
+ try {
+ throwSymbolicationProbeError();
+ } catch (error) {
+ globalThis.__REPACK_ANDROID_SYMBOLICATION_STACK__ =
+ error instanceof Error ? error.stack : undefined;
+ }
+}
+
+export default function SymbolicationProbe() {
+ return (
+
+ );
+}
diff --git a/apps/tester-expo/eas.json b/apps/tester-expo/eas.json
new file mode 100644
index 000000000..844b1bba5
--- /dev/null
+++ b/apps/tester-expo/eas.json
@@ -0,0 +1,30 @@
+{
+ "cli": {
+ "appVersionSource": "local",
+ "version": ">= 21.4.0"
+ },
+ "build": {
+ "repack-base": {
+ "env": {
+ "EAS_BUILD_DISABLE_BUNDLE_JAVASCRIPT_STEP": "1",
+ "EAS_BUILD_DISABLE_EXPO_DOCTOR_STEP": "1"
+ }
+ },
+ "repack-android": {
+ "extends": "repack-base",
+ "distribution": "internal",
+ "android": {
+ "buildType": "apk",
+ "withoutCredentials": true
+ }
+ },
+ "repack-ios-simulator": {
+ "extends": "repack-base",
+ "distribution": "internal",
+ "ios": {
+ "simulator": true,
+ "withoutCredentials": true
+ }
+ }
+ }
+}
diff --git a/apps/tester-expo/expo-env.d.ts b/apps/tester-expo/expo-env.d.ts
new file mode 100644
index 000000000..e6394b247
--- /dev/null
+++ b/apps/tester-expo/expo-env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/apps/tester-expo/package.json b/apps/tester-expo/package.json
new file mode 100644
index 000000000..e81f2e908
--- /dev/null
+++ b/apps/tester-expo/package.json
@@ -0,0 +1,46 @@
+{
+ "name": "tester-expo",
+ "version": "0.0.1",
+ "private": true,
+ "main": "expo-router/entry",
+ "scripts": {
+ "postinstall": "node ./scripts/eas-workspace-postinstall.mjs",
+ "repack:start": "react-native webpack-start",
+ "repack:ios": "expo run:ios --no-bundler",
+ "repack:android": "expo run:android --no-bundler",
+ "typecheck": "tsc --noEmit"
+ },
+ "dependencies": {
+ "@callstack/repack": "workspace:*",
+ "@callstack/repack-expo": "workspace:*",
+ "@callstack/repack-plugin-reanimated": "workspace:*",
+ "@module-federation/enhanced": "2.1.0",
+ "expo": "~57.0.9",
+ "expo-asset": "~57.0.8",
+ "expo-constants": "~57.0.8",
+ "expo-font": "~57.0.1",
+ "expo-linking": "~57.0.4",
+ "expo-router": "~57.0.9",
+ "expo-status-bar": "~57.0.1",
+ "react": "19.2.3",
+ "react-native": "0.86.2",
+ "react-native-gesture-handler": "~2.32.0",
+ "react-native-reanimated": "4.5.1",
+ "react-native-safe-area-context": "~5.7.0",
+ "react-native-screens": "~4.26.0",
+ "react-native-worklets": "0.10.1"
+ },
+ "devDependencies": {
+ "@babel/core": "^7.29.0",
+ "@react-native-community/cli": "20.1.2",
+ "@react-native-community/cli-platform-android": "20.1.2",
+ "@react-native-community/cli-platform-ios": "20.1.2",
+ "@react-native/babel-preset": "0.86.2",
+ "@react-native/community-cli-plugin": "0.86.2",
+ "@rspack/core": "catalog:",
+ "@types/react": "~19.2.2",
+ "babel-preset-expo": "~57.0.5",
+ "typescript": "~6.0.3",
+ "ws": "^8.18.3"
+ }
+}
diff --git a/apps/tester-expo/plugins/with-localhost-cleartext.js b/apps/tester-expo/plugins/with-localhost-cleartext.js
new file mode 100644
index 000000000..081ea8423
--- /dev/null
+++ b/apps/tester-expo/plugins/with-localhost-cleartext.js
@@ -0,0 +1,53 @@
+const {
+ withAndroidManifest,
+ withDangerousMod,
+} = require('expo/config-plugins');
+const fs = require('node:fs');
+const path = require('node:path');
+
+const NETWORK_SECURITY_CONFIG = 'repack_expo_localhost_network_security_config';
+const NETWORK_SECURITY_CONFIG_XML = `
+
+
+
+ localhost
+ 10.0.2.2
+
+
+`;
+
+function withLocalhostCleartext(config) {
+ const withManifest = withAndroidManifest(config, (modConfig) => {
+ const application = modConfig.modResults.manifest.application?.[0];
+ if (!application) {
+ throw new Error('Expected an Android application element');
+ }
+
+ application.$ ??= {};
+ application.$['android:usesCleartextTraffic'] = 'false';
+ application.$['android:networkSecurityConfig'] =
+ `@xml/${NETWORK_SECURITY_CONFIG}`;
+ return modConfig;
+ });
+
+ return withDangerousMod(withManifest, [
+ 'android',
+ (modConfig) => {
+ const resourceDirectory = path.join(
+ modConfig.modRequest.platformProjectRoot,
+ 'app/src/main/res/xml'
+ );
+ fs.mkdirSync(resourceDirectory, { recursive: true });
+ fs.writeFileSync(
+ path.join(resourceDirectory, `${NETWORK_SECURITY_CONFIG}.xml`),
+ NETWORK_SECURITY_CONFIG_XML
+ );
+ return modConfig;
+ },
+ ]);
+}
+
+module.exports = withLocalhostCleartext;
diff --git a/apps/tester-expo/react-native.config.js b/apps/tester-expo/react-native.config.js
new file mode 100644
index 000000000..d3fe7ef96
--- /dev/null
+++ b/apps/tester-expo/react-native.config.js
@@ -0,0 +1,3 @@
+module.exports = {
+ commands: require('@callstack/repack/commands/rspack'),
+};
diff --git a/apps/tester-expo/rspack.config.mjs b/apps/tester-expo/rspack.config.mjs
new file mode 100644
index 000000000..afed420e6
--- /dev/null
+++ b/apps/tester-expo/rspack.config.mjs
@@ -0,0 +1,71 @@
+import path from 'node:path';
+import { fileURLToPath } from 'node:url';
+import * as Repack from '@callstack/repack';
+import { ExpoPlugin } from '@callstack/repack-expo/rspack';
+import { ReanimatedPlugin } from '@callstack/repack-plugin-reanimated';
+
+const projectRoot = path.dirname(fileURLToPath(import.meta.url));
+
+export default (env) => {
+ const {
+ mode = 'development',
+ platform = process.env.PLATFORM,
+ devServer,
+ } = env;
+
+ if (platform !== 'ios' && platform !== 'android') {
+ throw new Error('tester-expo requires PLATFORM=ios or PLATFORM=android');
+ }
+
+ return {
+ context: projectRoot,
+ devServer,
+ devtool: 'source-map',
+ mode,
+ name: platform,
+ output: {
+ clean: true,
+ path: path.join(projectRoot, 'build', 'rspack', platform),
+ uniqueName: `tester-expo-${platform}`,
+ },
+ plugins: [
+ new ReanimatedPlugin({ unstable_disableTransform: true }),
+ new ExpoPlugin({ platform }),
+ new Repack.plugins.ModuleFederationPluginV2({
+ name: 'ExpoHost',
+ dts: false,
+ remotes: {
+ ExpoWidget: `ExpoWidget@http://localhost:8082/${platform}/mf-manifest.json`,
+ OrdinaryWidget: `OrdinaryWidget@http://localhost:8083/${platform}/mf-manifest.json`,
+ },
+ shared: {
+ react: {
+ singleton: true,
+ eager: true,
+ requiredVersion: '19.2.3',
+ },
+ 'react-native': {
+ singleton: true,
+ eager: true,
+ requiredVersion: '0.86.2',
+ },
+ 'expo-constants': {
+ singleton: true,
+ eager: true,
+ requiredVersion: '~57.0.8',
+ },
+ 'expo-asset': {
+ singleton: true,
+ eager: true,
+ requiredVersion: '~57.0.8',
+ },
+ 'expo-font': {
+ singleton: true,
+ eager: true,
+ requiredVersion: '~57.0.1',
+ },
+ },
+ }),
+ ],
+ };
+};
diff --git a/apps/tester-expo/scripts/eas-workspace-postinstall.mjs b/apps/tester-expo/scripts/eas-workspace-postinstall.mjs
new file mode 100644
index 000000000..f89eff87c
--- /dev/null
+++ b/apps/tester-expo/scripts/eas-workspace-postinstall.mjs
@@ -0,0 +1,128 @@
+import { spawnSync } from 'node:child_process';
+import { existsSync } from 'node:fs';
+import path from 'node:path';
+import { fileURLToPath } from 'node:url';
+
+const scriptDirectory = path.dirname(fileURLToPath(import.meta.url));
+const repositoryRoot = path.resolve(scriptDirectory, '..', '..', '..');
+
+export function createWorkspaceBuildCommand(root = repositoryRoot) {
+ return {
+ arguments_: [
+ '--dir',
+ root,
+ '--filter',
+ 'tester-expo^...',
+ '--workspace-concurrency=1',
+ 'run',
+ 'build',
+ ],
+ command: 'pnpm',
+ };
+}
+
+export function createRepackDoctorCommand(root = repositoryRoot) {
+ return {
+ arguments_: [
+ path.join(root, 'packages', 'repack-expo', 'dist', 'cli', 'bin.js'),
+ 'doctor',
+ ],
+ command: process.execPath,
+ cwd: path.join(root, 'apps', 'tester-expo'),
+ };
+}
+
+export function createExpoPrebuildCommand(platform, root = repositoryRoot) {
+ return {
+ arguments_: [
+ '--dir',
+ path.join(root, 'apps', 'tester-expo'),
+ 'exec',
+ 'expo',
+ 'prebuild',
+ '--clean',
+ '--no-install',
+ '--platform',
+ platform,
+ ],
+ command: 'pnpm',
+ cwd: root,
+ };
+}
+
+export function shouldGenerateNativeProject(
+ platform,
+ root = repositoryRoot,
+ pathExists = existsSync
+) {
+ return !pathExists(path.join(root, 'apps', 'tester-expo', platform));
+}
+
+export function shouldBuildEasWorkspaces(environment = process.env) {
+ return (
+ environment.EAS_BUILD_PLATFORM === 'android' ||
+ environment.EAS_BUILD_PLATFORM === 'ios'
+ );
+}
+
+function runCheckedCommand(
+ { arguments_, command, cwd },
+ { environment, label, root, run }
+) {
+ const result = run(command, arguments_, {
+ cwd: cwd ?? root,
+ env: environment,
+ stdio: 'inherit',
+ });
+ if (result.error) throw result.error;
+ if (result.status !== 0) {
+ throw new Error(`${label} exited with status ${result.status}`);
+ }
+}
+
+export function runEasWorkspacePostinstall({
+ environment = process.env,
+ pathExists = existsSync,
+ root = repositoryRoot,
+ run = spawnSync,
+} = {}) {
+ if (!shouldBuildEasWorkspaces(environment)) {
+ console.log('Skipping EAS workspace build outside an EAS Build worker.');
+ return;
+ }
+
+ console.log(
+ `Building tester-expo workspace dependencies for EAS ${environment.EAS_BUILD_PLATFORM}.`
+ );
+ runCheckedCommand(createWorkspaceBuildCommand(root), {
+ environment,
+ label: 'EAS workspace dependency build',
+ root,
+ run,
+ });
+
+ const platform = environment.EAS_BUILD_PLATFORM;
+ if (shouldGenerateNativeProject(platform, root, pathExists)) {
+ const prebuild = createExpoPrebuildCommand(platform, root);
+ console.log(`Generating the EAS ${platform} project with Expo prebuild.`);
+ runCheckedCommand(prebuild, {
+ environment,
+ label: 'Expo prebuild',
+ root,
+ run,
+ });
+ }
+
+ const doctor = createRepackDoctorCommand(root);
+ console.log('Validating the EAS native project with Re.Pack Expo doctor.');
+ runCheckedCommand(doctor, {
+ environment,
+ label: 'Re.Pack Expo doctor',
+ root,
+ run,
+ });
+}
+
+if (import.meta.url === new URL(process.argv[1], 'file:').href) {
+ runEasWorkspacePostinstall();
+}
diff --git a/apps/tester-expo/tsconfig.json b/apps/tester-expo/tsconfig.json
new file mode 100644
index 000000000..51b20857b
--- /dev/null
+++ b/apps/tester-expo/tsconfig.json
@@ -0,0 +1,15 @@
+{
+ "extends": "expo/tsconfig.base",
+ "compilerOptions": {
+ "strict": true
+ },
+ "include": [
+ "app/**/*.ts",
+ "app/**/*.tsx",
+ "components/**/*.ts",
+ "components/**/*.tsx",
+ "types/**/*.d.ts",
+ ".expo/types/**/*.ts",
+ "expo-env.d.ts"
+ ]
+}
diff --git a/apps/tester-expo/types/module-federation.d.ts b/apps/tester-expo/types/module-federation.d.ts
new file mode 100644
index 000000000..120b0641e
--- /dev/null
+++ b/apps/tester-expo/types/module-federation.d.ts
@@ -0,0 +1,13 @@
+declare module 'ExpoWidget/Widget' {
+ import type { ComponentType } from 'react';
+
+ const Widget: ComponentType<{ recoveryAttempt?: number }>;
+ export default Widget;
+}
+
+declare module 'OrdinaryWidget/Widget' {
+ import type { ComponentType } from 'react';
+
+ const ExpoHostCard: ComponentType;
+ export default ExpoHostCard;
+}
diff --git a/apps/tester-federation-v2/config.expo-host-widget.mts b/apps/tester-federation-v2/config.expo-host-widget.mts
new file mode 100644
index 000000000..6c0259ebb
--- /dev/null
+++ b/apps/tester-federation-v2/config.expo-host-widget.mts
@@ -0,0 +1,11 @@
+import path from 'node:path';
+import { fileURLToPath } from 'node:url';
+import type * as Repack from '@callstack/repack';
+import rspackConfig from './configs/rspack.expo-host-widget.mts';
+
+const projectRoot = path.dirname(fileURLToPath(import.meta.url));
+
+export default (env: Repack.EnvOptions) => {
+ env.context = projectRoot;
+ return rspackConfig(env);
+};
diff --git a/apps/tester-federation-v2/configs/rspack.expo-host-widget.mts b/apps/tester-federation-v2/configs/rspack.expo-host-widget.mts
new file mode 100644
index 000000000..6cddbf6d0
--- /dev/null
+++ b/apps/tester-federation-v2/configs/rspack.expo-host-widget.mts
@@ -0,0 +1,65 @@
+import * as Repack from '@callstack/repack';
+
+export default Repack.defineRspackConfig((env) => {
+ const { mode, context, platform } = env;
+
+ return {
+ mode,
+ context,
+ entry: './src/mini/index.js',
+ resolve: {
+ ...Repack.getResolveOptions({ enablePackageExports: true }),
+ },
+ output: {
+ path: '[context]/build/expo-host-widget/[platform]',
+ uniqueName: 'MF2Tester-ExpoHostWidget',
+ },
+ module: {
+ rules: [
+ {
+ test: /\.[cm]?[jt]sx?$/,
+ use: {
+ loader: '@callstack/repack/babel-swc-loader',
+ parallel: true,
+ options: {},
+ },
+ type: 'javascript/auto',
+ },
+ ...Repack.getAssetTransformRules({ inline: true }),
+ ],
+ },
+ plugins: [
+ new Repack.RepackPlugin({
+ extraChunks: [
+ {
+ include: /.*/,
+ type: 'remote',
+ outputPath: `build/expo-host-widget/${platform}/output-remote`,
+ },
+ ],
+ }),
+ new Repack.plugins.ModuleFederationPluginV2({
+ name: 'OrdinaryWidget',
+ filename: 'OrdinaryWidget.container.js.bundle',
+ exposes: {
+ './Widget': './src/mini/components/ExpoHostWidget',
+ },
+ dts: false,
+ shared: {
+ react: {
+ singleton: true,
+ eager: false,
+ import: false,
+ requiredVersion: false,
+ },
+ 'react-native': {
+ singleton: true,
+ eager: false,
+ import: false,
+ requiredVersion: false,
+ },
+ },
+ }),
+ ],
+ };
+});
diff --git a/apps/tester-federation-v2/package.json b/apps/tester-federation-v2/package.json
index 222874f47..de7a1e8c7 100644
--- a/apps/tester-federation-v2/package.json
+++ b/apps/tester-federation-v2/package.json
@@ -8,7 +8,8 @@
"pods": "(cd ios && bundle install && (bundle exec pod install || bundle exec pod update))",
"pods:update": "(cd ios && bundle install && bundle exec pod update)",
"start:hostapp": "react-native webpack-start --config config.host-app.mts",
- "start:miniapp": "react-native webpack-start --config config.mini-app.mts --port 8082"
+ "start:miniapp": "react-native webpack-start --config config.mini-app.mts --port 8082",
+ "start:expo-host-widget": "react-native webpack-start --config config.expo-host-widget.mts --port 8083"
},
"dependencies": {
"@callstack/repack": "workspace:*",
@@ -24,10 +25,12 @@
},
"devDependencies": {
"@babel/core": "^7.29.7",
+ "@babel/plugin-syntax-typescript": "7.25.4",
"@react-native-community/cli": "catalog:testers",
"@react-native-community/cli-platform-android": "catalog:testers",
"@react-native-community/cli-platform-ios": "catalog:testers",
"@react-native/babel-preset": "catalog:testers",
+ "@react-native/community-cli-plugin": "0.86.0",
"@react-native/typescript-config": "catalog:testers",
"@rsdoctor/rspack-plugin": "1.5.2",
"@rspack/core": "catalog:",
diff --git a/apps/tester-federation-v2/src/mini/components/ExpoHostWidget.tsx b/apps/tester-federation-v2/src/mini/components/ExpoHostWidget.tsx
new file mode 100644
index 000000000..a18f0e8ff
--- /dev/null
+++ b/apps/tester-federation-v2/src/mini/components/ExpoHostWidget.tsx
@@ -0,0 +1,19 @@
+import { StyleSheet, Text, View } from 'react-native';
+
+export default function ExpoHostWidget() {
+ return (
+
+ Ordinary Re.Pack MF v2 widget loaded by an Expo host.
+
+ );
+}
+
+const styles = StyleSheet.create({
+ card: {
+ backgroundColor: '#eef2ff',
+ borderColor: '#4338ca',
+ borderRadius: 8,
+ borderWidth: 1,
+ padding: 16,
+ },
+});
diff --git a/packages/repack-expo/README.md b/packages/repack-expo/README.md
new file mode 100644
index 000000000..5dfa23a39
--- /dev/null
+++ b/packages/repack-expo/README.md
@@ -0,0 +1,621 @@
+# `@callstack/repack-expo`
+
+First-class Expo integration for Re.Pack and Rspack.
+
+> This package is private while the Expo v1 release gates are being completed.
+> The documented contract is implemented and validated in this repository, but
+> the package is not published for application use yet.
+
+## Requirements
+
+- Expo SDK 56 or newer
+- iOS or Android
+- React Native New Architecture
+- Hermes
+- Rspack
+- Node.js supported by the selected Expo SDK (20.19+ minimum)
+- Expo prebuild or CNG
+
+SDK 56 is the minimum supported contract. The integration does not hard-code an
+upper Expo version allowlist: it validates the required generated-native seams
+and fails with recovery guidance when an Expo template is incompatible.
+
+## Quick start
+
+Run the initializer from the root of an existing Expo application with a static
+`app.json` or `package.json#expo` configuration:
+
+```sh
+npx @callstack/repack-expo init
+```
+
+Run the dependency command printed by `init`, then regenerate the native
+projects and validate the result:
+
+```sh
+npx expo prebuild --clean
+npx @callstack/repack-expo doctor
+```
+
+Start Re.Pack in one terminal:
+
+```sh
+npm run repack:start
+```
+
+Build and launch the generated development application in another terminal:
+
+```sh
+npm run repack:ios
+# or
+npm run repack:android
+```
+
+The generated scripts use `expo run:* --no-bundler`. Do not start Metro or use
+`expo start` for this workflow.
+
+The initializer detects npm, yarn, pnpm and bun. Replace `npm run` in the
+examples with the equivalent command for the application's package manager.
+
+### Safe initializer modes
+
+```sh
+npx @callstack/repack-expo init --check
+npx @callstack/repack-expo init --dry-run
+npx @callstack/repack-expo init --dry-run --json
+```
+
+`--check` exits unsuccessfully when managed files are not current. `--dry-run`
+returns the planned changes without writing them. `--json` produces
+machine-readable diagnostics. An existing incompatible Rspack configuration is
+never overwritten unless `--force` is explicitly supplied.
+
+Executable `app.config.js`, `app.config.ts`, `app.config.mjs` and
+`app.config.cjs` files are intentionally not rewritten. Convert the required
+configuration to a static Expo config or apply the diagnostic's recovery steps
+before rerunning `init`.
+
+## What `init` manages
+
+`init` updates only application-owned configuration files:
+
+| File | Managed result |
+| --- | --- |
+| `package.json` | Adds compatible Re.Pack, Expo integration, Community CLI and Rspack development dependencies, plus `repack:start`, `repack:ios` and `repack:android` scripts. |
+| `app.json` or `package.json#expo` | Registers `@callstack/repack-expo` and disables Expo Updates. SDK 56+ New Architecture and Hermes defaults are not written redundantly. |
+| `react-native.config.js` | Registers the existing Re.Pack Rspack CLI commands. |
+| `rspack.config.mjs` | Adds a minimal application config containing one `ExpoPlugin`. Existing compatible configs are preserved. |
+
+The command never edits `ios/` or `android/`. Expo prebuild invokes the Config
+Plugin and owns all generated native changes. Rerun prebuild after changing the
+Config Plugin version or options; do not copy or maintain the generated Swift,
+Objective-C, Kotlin, Gradle or Xcode snippets by hand.
+
+The generated Rspack shape is intentionally small:
+
+```js
+import path from 'node:path';
+import { fileURLToPath } from 'node:url';
+import { ExpoPlugin } from '@callstack/repack-expo/rspack';
+
+const projectRoot = path.dirname(fileURLToPath(import.meta.url));
+
+export default (env) => {
+ const {
+ mode = 'development',
+ platform = process.env.PLATFORM,
+ devServer,
+ } = env;
+
+ if (platform !== 'ios' && platform !== 'android') {
+ throw new Error('ExpoPlugin requires PLATFORM=ios or android');
+ }
+
+ return {
+ context: projectRoot,
+ devServer,
+ mode,
+ name: platform,
+ output: {
+ clean: true,
+ path: path.join(projectRoot, 'build', 'rspack', platform),
+ uniqueName: 'expo-app',
+ },
+ plugins: [new ExpoPlugin({ platform })],
+ };
+};
+```
+
+Do not add a separate `RepackPlugin`, application `setup.ts`, React Native
+aliases, `extraChunks` configuration or ScriptManager resolver. `ExpoPlugin`
+owns those Re.Pack defaults and rejects a duplicate `RepackPlugin`.
+
+## Expo Router and plain entries
+
+Expo Router applications keep the idiomatic entry:
+
+```json
+{
+ "main": "expo-router/entry"
+}
+```
+
+For native Rspack builds, `ExpoPlugin` replaces Router's Metro-only bootstrap
+side effect and mounts the resolved Router application without importing the
+Metro runtime.
+
+A non-Router application can use any resolvable `package.json#main` entry. The
+entry follows React Native platform extension precedence. Missing, ambiguous or
+Metro-virtual entries fail before compilation instead of falling back to Metro.
+
+Custom native entry and Rspack config paths are Config Plugin options:
+
+```json
+{
+ "expo": {
+ "plugins": [
+ [
+ "@callstack/repack-expo",
+ {
+ "entry": "src/main.ts",
+ "configPath": "config/rspack.native.mjs"
+ }
+ ]
+ ]
+ }
+}
+```
+
+`configPath` must resolve inside the Expo application when using `init`.
+`entry` can be a project-relative or absolute resolvable path. `init`, prebuild
+and `doctor` read the same options.
+
+## Development and Fast Refresh
+
+The development server is ordinary Re.Pack:
+
+```sh
+npm run repack:start
+```
+
+The generated iOS and Android Debug projects request the `index` bundle from
+that server. `expo run:ios --no-bundler` and
+`expo run:android --no-bundler` install and launch the native clients without
+starting Metro.
+
+Fast Refresh follows normal Re.Pack semantics. Updating a React component can
+preserve parent and runtime state. Editing the application bootstrap or another
+refresh boundary can trigger a full JavaScript reload. Source maps and Hermes
+symbolication retain application source paths.
+
+Expo Go, the `expo-dev-client` launcher/QR protocol and Expo development
+manifests are not part of this integration.
+
+## Local release builds
+
+After prebuild, standard native Release builds invoke Community CLI's `bundle`
+command. The registered Re.Pack commands dispatch that request to Rspack; no
+separate JavaScript export command is required.
+
+Typical local commands are:
+
+```sh
+npx expo run:ios --configuration Release --no-bundler
+npx expo run:android --variant release --no-bundler
+```
+
+Xcode and Gradle remain responsible for Hermes compilation, final source-map
+composition, native assets and application packaging. `expo export` is not a
+Re.Pack release path.
+
+## EAS Build
+
+EAS must not run its eager Metro-compatible JavaScript step before the native
+build. Add this environment variable to every Re.Pack EAS profile:
+
+```json
+{
+ "build": {
+ "production": {
+ "env": {
+ "EAS_BUILD_DISABLE_BUNDLE_JAVASCRIPT_STEP": "1"
+ }
+ }
+ }
+}
+```
+
+Then run the normal EAS command:
+
+```sh
+npx eas build --platform android --profile production
+npx eas build --platform ios --profile production
+```
+
+With CNG, EAS generates the absent native project and runs the registered
+Config Plugin. If native projects are committed, run `expo prebuild --clean`
+after every integration/config change and commit the regenerated projects
+before EAS Build. Never reproduce the Config Plugin's native edits manually.
+
+The repository's pnpm fixture additionally builds private workspace packages
+in `postinstall` and disables the generic Expo doctor because that doctor
+reports duplicate-module false positives for this workspace layout.
+Applications consuming a published package do not need the private-workspace
+build step. Disable the generic Expo doctor only if it reports that same pnpm
+duplicate false positive, and use `npx @callstack/repack-expo doctor` to
+validate the generated Re.Pack seams.
+
+Expo Updates and EAS Update are unsupported in v1. `init` requires
+`expo.updates.enabled` to be `false`; an EAS native build is required after
+application, integration, dependency or native fingerprint changes.
+
+## Assets, fonts and local chunks
+
+`ExpoPlugin` installs Expo-compatible handling for static images, density
+variants, fonts and XML resources. Existing compatible Rspack asset rules are
+preserved. Android production fonts are emitted as raw resources so
+`expo-asset` and `expo-font` can resolve their packaged resource identifiers.
+
+Dynamic imports produce flat local chunks using `[name].chunk.bundle` by
+default. Release builds embed those chunks in the application, and the existing
+Re.Pack ScriptManager loads them from the application filesystem:
+
+```tsx
+const LazyCard = React.lazy(() => import('./LazyCard'));
+```
+
+Development chunks are loaded from the Re.Pack development server. Release
+chunks do not require that server or an internet connection. Nested, constant
+or otherwise colliding chunk filenames fail during configuration.
+
+This packaged-local behavior remains the default when Module Federation v2 is
+enabled. A host can therefore use a local `React.lazy()` component and remote
+widgets in the same application without turning the local import into a remote
+chunk.
+
+## Module Federation v2
+
+Module Federation is an explicit, application-owned Re.Pack capability. It is
+not generated by `init` and does not add an Expo-specific runtime. Install the
+`@module-federation/enhanced` version required by the application's Re.Pack
+version, then use only the public Re.Pack v2 wrapper:
+
+```sh
+npm install @module-federation/enhanced
+```
+
+Do not instantiate `@module-federation/enhanced/rspack` directly. MF v1 and
+Metro Module Federation are different runtimes and are not supported by this
+integration.
+
+### Configure the host
+
+Keep the host's `ExpoPlugin` unchanged and add the ordinary Re.Pack v2 plugin.
+The order of the two plugins does not matter:
+
+```js
+import path from 'node:path';
+import { fileURLToPath } from 'node:url';
+import * as Repack from '@callstack/repack';
+import { ExpoPlugin } from '@callstack/repack-expo/rspack';
+
+const projectRoot = path.dirname(fileURLToPath(import.meta.url));
+
+export default (env) => {
+ const {
+ mode = 'development',
+ platform = process.env.PLATFORM,
+ devServer,
+ } = env;
+
+ if (platform !== 'ios' && platform !== 'android') {
+ throw new Error('Expo host requires PLATFORM=ios or android');
+ }
+
+ const widgetManifest =
+ `ExpoWidget@https://cdn.example.com/widgets/${platform}/mf-manifest.json`;
+
+ return {
+ context: projectRoot,
+ devServer,
+ mode,
+ name: platform,
+ output: {
+ clean: true,
+ path: path.join(projectRoot, 'build', 'rspack', platform),
+ uniqueName: `expo-host-${platform}`,
+ },
+ plugins: [
+ new ExpoPlugin({ platform }),
+ new Repack.plugins.ModuleFederationPluginV2({
+ name: 'ExpoHost',
+ dts: false,
+ remotes: {
+ ExpoWidget: widgetManifest,
+ },
+ shared: {
+ react: { singleton: true, eager: true },
+ 'react-native': { singleton: true, eager: true },
+ 'expo-constants': { singleton: true, eager: true },
+ 'expo-asset': { singleton: true, eager: true },
+ 'expo-font': { singleton: true, eager: true },
+ },
+ }),
+ ],
+ };
+};
+```
+
+The host leaves `ExpoPlugin.repack.extraChunks` absent, so the default local
+chunk rule remains active. `ModuleFederationPluginV2` registers its remote
+resolvers at normal Re.Pack priority, while the Expo local resolver remains a
+fallback for unmatched application chunks. Both paths use the same
+ScriptManager instance.
+
+The host owns Expo Router and decides where the remote UI is mounted. A
+widget can expose a component, screen or navigator, but its file routes are not
+merged into the host's Router tree.
+
+### Configure an Expo widget
+
+An Expo widget is an independent Expo project with a normal synchronous entry
+for standalone development and an MF v2 exposure. Its production config sends
+the exposure, nested lazy chunks and remote assets to a deployable platform
+directory:
+
+```js
+import path from 'node:path';
+import { fileURLToPath } from 'node:url';
+import * as Repack from '@callstack/repack';
+import { ExpoPlugin } from '@callstack/repack-expo/rspack';
+
+const projectRoot = path.dirname(fileURLToPath(import.meta.url));
+
+export default (env) => {
+ const {
+ mode = 'development',
+ platform = process.env.PLATFORM,
+ devServer,
+ } = env;
+
+ if (platform !== 'ios' && platform !== 'android') {
+ throw new Error('Expo widget requires PLATFORM=ios or android');
+ }
+
+ const remoteOutputPath = path.join(
+ projectRoot,
+ 'build',
+ 'remote',
+ platform
+ );
+ const remotePublicPath =
+ `https://cdn.example.com/widgets/${platform}/remote-assets`;
+
+ return {
+ context: projectRoot,
+ devServer,
+ mode,
+ name: platform,
+ module: {
+ rules:
+ mode === 'production'
+ ? Repack.getAssetTransformRules({
+ remote: { publicPath: remotePublicPath },
+ })
+ : [],
+ },
+ output: {
+ clean: true,
+ path: path.join(projectRoot, 'build', 'rspack', platform),
+ publicPath: `https://cdn.example.com/widgets/${platform}/`,
+ uniqueName: `expo-widget-${platform}`,
+ },
+ plugins: [
+ new ExpoPlugin({
+ platform,
+ repack: {
+ output: { auxiliaryAssetsPath: remoteOutputPath },
+ extraChunks: [
+ { include: /.*/, type: 'remote', outputPath: remoteOutputPath },
+ ],
+ },
+ }),
+ new Repack.plugins.ModuleFederationPluginV2({
+ name: 'ExpoWidget',
+ filename: 'ExpoWidget.container.js.bundle',
+ exposes: { './Widget': './src/Widget' },
+ dts: false,
+ shared: {
+ react: { singleton: true, eager: true },
+ 'react-native': { singleton: true, eager: true },
+ 'expo-constants': { singleton: true, eager: true },
+ 'expo-asset': { singleton: true, eager: true },
+ 'expo-font': { singleton: true, eager: true },
+ },
+ }),
+ ],
+ };
+};
+```
+
+Passing `extraChunks` follows ordinary Re.Pack replacement semantics. The
+widget intentionally replaces the packaged-local default so every nested
+widget import is published remotely; the host does not pass this option and
+keeps its own lazy chunks packaged.
+
+A widget cannot deliver native dependencies. Every native module used by the
+widget must already be installed and linked in the host binary, and its
+JavaScript package must be shared compatibly. Adding or changing a native
+dependency requires a new host native build. The existing Config Plugin remains
+the sole owner of generated native changes, and Expo prebuild continues to
+generate the native directories.
+
+### Develop host and widget
+
+The widget and host use existing Re.Pack CLI commands on separate ports. From
+the widget project, start its remote development server:
+
+```sh
+npm run repack:start:remote
+```
+
+From the host project, start the normal server and launch the generated native
+client:
+
+```sh
+npm run repack:start
+npm run repack:ios
+# or
+npm run repack:android
+```
+
+The validated widget script maps `repack:start:remote` to
+`react-native webpack-start --port 8082`. There is no Expo-specific widget CLI,
+async bootstrap requirement or Metro server. Ordinary Re.Pack Fast Refresh and
+reload boundaries apply.
+
+### Load, fail and retry
+
+Mount the exposure through the public MF v2 runtime:
+
+```ts
+import { loadRemote } from '@module-federation/enhanced/runtime';
+
+const widgetModule = await loadRemote('ExpoWidget/Widget');
+```
+
+The application should immediately handle the returned promise, store a
+rejection and throw that error during render into its own React error boundary.
+Retry by starting a new `loadRemote()` attempt. This preserves the original
+Module Federation or ScriptManager diagnostic for missing manifests, scripts
+and nested chunks; widget image and font failures can use the same boundary.
+Do not replace this path with a Webpack remote `import()` or a global error
+handler.
+
+### Build and deploy a widget
+
+Build each platform independently with the standard Re.Pack `bundle` (or
+`webpack-bundle`) command. The repository fixture exposes convenience scripts
+that invoke those commands and prepare the publication tree:
+
+```sh
+npm run repack:build:production:ios
+npm run repack:build:production:android
+```
+
+Publish separate `ios/` and `android/` trees. Each tree contains its own
+`mf-manifest.json`, container/exposure scripts, nested lazy chunks, source maps,
+density images and fonts. Point each host build at the matching platform
+manifest; never serve an iOS widget graph to Android or vice versa. Host and
+widget `EXPO_PUBLIC_*` values are compiled from their respective projects, so
+do not use widget build-time variables as host runtime configuration.
+
+Redeploying those remote artifacts is not Expo Updates or OTA. It does not
+replace the host launch bundle or its packaged local chunks. Host JavaScript,
+integration, dependency and native changes still require a new native build;
+only MF v2 widget artifacts follow Re.Pack's remote manifest lifecycle.
+
+Production remotes must use HTTPS. The fixture's Android cleartext exception
+is limited to localhost and must not be copied for a public host. Optional
+storage, update policy, retry, runtime plugins and code verification remain
+ordinary Re.Pack configuration: use the existing ScriptManager storage and
+locator APIs plus `CodeSigningPlugin` when required. `ExpoPlugin` adds no
+parallel cache, signature, invalidation or security policy, and it does not
+require an Expo-specific setup entry.
+
+## Public environment variables
+
+`ExpoPlugin` loads Expo-compatible dotenv files and inlines only static
+`process.env.EXPO_PUBLIC_*` accesses in application modules. Shell values take
+priority, followed by `.env..local`, `.env.local`, `.env.` and
+`.env`. Missing files are optional. Private values can participate in dotenv
+expansion but are never passed to Babel or Rspack definitions.
+
+Dot and string-literal bracket access are supported, including optional chains.
+Computed variables, destructuring, assignments, private keys and accesses in
+dependencies are left unchanged. `EXPO_NO_DOTENV=1` disables dotenv files while
+retaining shell-provided public values. In production,
+`EXPO_NO_CLIENT_ENV_VARS=1` disables public client injection entirely.
+
+Changing or creating a selected dotenv file invalidates affected modules in
+development. Changes to the launching shell environment require restarting the
+Re.Pack development server.
+
+## Migrating from Expo Metro
+
+1. Create a migration branch and ensure the application has a static Expo
+ config.
+2. Run `npx @callstack/repack-expo init --dry-run` and review the managed-file
+ changes.
+3. Run `init`, execute its printed dependency command and run
+ `npx expo prebuild --clean`.
+4. Run `npx @callstack/repack-expo doctor`.
+5. Replace `expo start` with `repack:start`; launch native clients through the
+ generated `repack:ios` and `repack:android` scripts.
+6. Exercise development, Fast Refresh and both native Release builds before
+ removing an existing production path.
+
+`init` does not delete `metro.config.js`; Re.Pack simply does not read it. Keep
+the file only if another unsupported Expo/Metro workflow still needs it. This
+migration does not automatically enable federation and does not provide Expo
+CLI bundler parity, OTA parity, Metro Module Federation, MF v1 or raw enhanced
+plugin support. Add the application-owned MF v2 configuration above only after
+the non-federated Re.Pack path is working.
+
+## Troubleshooting
+
+Start with the read-only doctor:
+
+```sh
+npx @callstack/repack-expo doctor
+npx @callstack/repack-expo doctor --json
+```
+
+| Symptom or diagnostic | Recovery |
+| --- | --- |
+| `DYNAMIC_EXPO_CONFIG` | Use static `app.json`/`package.json#expo`, or apply the reported configuration manually before prebuild. The CLI will not execute application config code. |
+| `RSPACK_CONFIG_CONFLICT` | Preserve and update the existing config, or explicitly allow replacement with `init --force`. Keep only one `ExpoPlugin` and no separate `RepackPlugin`. |
+| New Architecture or Hermes conflict | Remove `newArchEnabled: false` or a non-Hermes `jsEngine` override. SDK 56+ supplies the supported defaults when those fields are absent. |
+| Expo Updates conflict | Set `expo.updates.enabled` to `false` and regenerate the native projects. OTA is not supported. |
+| Development client requests Metro or cannot find `index.bundle` | Rerun prebuild, start `repack:start`, launch with `repack:ios`/`repack:android`, and verify the generated Re.Pack commands with `doctor`. |
+| ScriptManager autolinking cannot be verified | Install dependencies, run a clean prebuild, install iOS pods when applicable, then rerun `doctor`. Do not add another native ScriptManager module. |
+| A Release lazy import fails | Confirm the chunk has a flat unique filename, rebuild the native application and inspect the underlying Rspack chunk-loader diagnostic. |
+| An MF v2 widget fails to load | Preserve the original MF/ScriptManager error in a host-owned boundary, verify the platform manifest and remote artifact URLs, restore the remote, then start a new `loadRemote()` attempt. |
+| An Android production widget works only over HTTP | Deploy the manifest, scripts and assets over HTTPS. The generated application does not allow arbitrary cleartext hosts. |
+| An Android packaged font cannot load | Clean and rebuild with the current `ExpoPlugin`; do not add an application-owned font-copy workaround. |
+| EAS attempts Metro/`export:embed` | Set `EAS_BUILD_DISABLE_BUNDLE_JAVASCRIPT_STEP=1` on the selected EAS profile and rebuild. |
+
+## Support matrix
+
+| Capability | V1 status |
+| --- | --- |
+| Expo SDK 56+ | Supported; template shapes are accepted when all required native seams validate. |
+| iOS and Android native applications | Supported. |
+| Rspack | Supported. |
+| Webpack | Unsupported. |
+| New Architecture and Hermes | Required; SDK 56+ defaults do not need explicit app-config fields. |
+| Expo Router | Supported. |
+| Resolvable plain `package.json#main` entry | Supported. |
+| Expo prebuild/CNG | Supported and required. |
+| Direct native development and Fast Refresh | Supported through Re.Pack and `expo run:* --no-bundler`. |
+| Local native Release and EAS Build | Supported. |
+| Expo Modules, images, density variants, fonts and source maps | Supported. |
+| Expo-compatible `EXPO_PUBLIC_*` environment variables | Supported. |
+| Monorepo and pnpm workspace resolution | Supported. |
+| Packaged local chunks through ScriptManager | Supported. |
+| Re.Pack Module Federation v2 | Supported as explicit application-owned configuration. |
+| Expo host -> Expo widget | Supported and validated on iOS and Android. |
+| Expo host -> ordinary Re.Pack MF v2 widget | Supported and validated. |
+| Production MF v2 widget artifacts | Supported as platform-specific remote deployments. |
+| Ordinary Re.Pack host -> Expo widget | Unsupported; the Expo native contract is not guaranteed. |
+| Module Federation v1 or raw `@module-federation/enhanced/rspack` | Unsupported. |
+| Metro Module Federation or `@module-federation/metro` | Unsupported. |
+| Generic remote chunks outside MF v2 | Unsupported by the Expo integration contract. |
+| Expo Go | Unsupported. |
+| `expo-dev-client` launcher, QR discovery and development manifests | Unsupported. |
+| `expo start` or `expo export` as Re.Pack commands | Unsupported. |
+| Expo Updates, EAS Update and OTA | Unsupported. |
+| Metro bundler integration | Unsupported. |
+| Old Architecture or JSC | Unsupported. |
+| Web, SSR, React Server Components and DOM Components | Unsupported. |
+| Executable `app.config.*` mutation | Unsupported; static Expo config is required by `init`. |
diff --git a/packages/repack-expo/package.json b/packages/repack-expo/package.json
new file mode 100644
index 000000000..58f9734ec
--- /dev/null
+++ b/packages/repack-expo/package.json
@@ -0,0 +1,60 @@
+{
+ "name": "@callstack/repack-expo",
+ "version": "0.0.0",
+ "description": "First-class Expo integration for Re.Pack and Rspack",
+ "author": "Callstack",
+ "license": "MIT",
+ "homepage": "https://github.com/callstack/repack",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/callstack/repack.git"
+ },
+ "private": true,
+ "type": "commonjs",
+ "main": "dist/index.js",
+ "types": "dist/index.d.ts",
+ "bin": {
+ "repack-expo": "dist/cli/bin.js"
+ },
+ "files": ["dist", "README.md"],
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "default": "./dist/index.js"
+ },
+ "./rspack": {
+ "types": "./dist/rspack/index.d.ts",
+ "default": "./dist/rspack/index.js"
+ },
+ "./babel-loader": {
+ "types": "./dist/rspack/loaders/expoBabelLoader.d.ts",
+ "default": "./dist/rspack/loaders/expoBabelLoader.js"
+ }
+ },
+ "keywords": ["repack", "rspack", "react-native", "expo", "config-plugin"],
+ "engineStrict": true,
+ "engines": {
+ "node": ">=20.19"
+ },
+ "dependencies": {
+ "@callstack/repack-plugin-expo-modules": "workspace:^",
+ "@expo/env": "~2.3.1"
+ },
+ "peerDependencies": {
+ "@callstack/repack": "workspace:^",
+ "@rspack/core": ">=1",
+ "expo": ">=56"
+ },
+ "devDependencies": {
+ "@callstack/repack": "workspace:*",
+ "@rspack/core": "catalog:",
+ "@types/node": "catalog:",
+ "expo": "~57.0.9",
+ "typescript": "catalog:"
+ },
+ "scripts": {
+ "build": "tsc -p tsconfig.build.json",
+ "typecheck": "tsc --noEmit",
+ "test": "pnpm build && node --test test/*.test.cjs"
+ }
+}
diff --git a/packages/repack-expo/src/cli/bin.ts b/packages/repack-expo/src/cli/bin.ts
new file mode 100644
index 000000000..7ad94453f
--- /dev/null
+++ b/packages/repack-expo/src/cli/bin.ts
@@ -0,0 +1,80 @@
+#!/usr/bin/env node
+
+import {
+ type Diagnostic,
+ parseExpoCommand,
+ runDoctor,
+ runInit,
+} from './commands.js';
+
+function writeDiagnostics(diagnostics: Diagnostic[]): void {
+ for (const diagnostic of diagnostics) {
+ const stream =
+ diagnostic.severity === 'error' ? process.stderr : process.stdout;
+ stream.write(`[${diagnostic.code}] ${diagnostic.message}\n`);
+ if (diagnostic.recovery) {
+ stream.write(` Recovery: ${diagnostic.recovery}\n`);
+ }
+ }
+}
+
+try {
+ const options = parseExpoCommand(process.argv.slice(2));
+ if (options.command === 'init') {
+ const result = runInit(options);
+ if (options.json) {
+ process.stdout.write(`${JSON.stringify(result)}\n`);
+ } else {
+ writeDiagnostics(result.diagnostics);
+ if (result.changedFiles.length === 0 && result.ok) {
+ process.stdout.write('Re.Pack Expo setup is up to date.\n');
+ } else if (result.changedFiles.length > 0) {
+ const action = result.wrote ? 'Changed' : 'Would change';
+ process.stdout.write(`${action}: ${result.changedFiles.join(', ')}\n`);
+ }
+ if (result.wrote) {
+ process.stdout.write(
+ `Next: ${result.installCommand}, then expo prebuild.\n`
+ );
+ }
+ }
+ if (!result.ok) process.exitCode = 1;
+ } else {
+ const result = runDoctor();
+ if (options.json) {
+ process.stdout.write(`${JSON.stringify(result)}\n`);
+ } else {
+ writeDiagnostics(result.diagnostics);
+ if (result.ok) {
+ process.stdout.write(
+ result.warningCount > 0
+ ? `Doctor passed with ${result.warningCount} warning(s).\n`
+ : 'Doctor passed.\n'
+ );
+ } else {
+ process.stderr.write(`Doctor found ${result.errorCount} error(s).\n`);
+ }
+ }
+ if (!result.ok) process.exitCode = 1;
+ }
+} catch (cause) {
+ const message = cause instanceof Error ? cause.message : String(cause);
+ if (process.argv.slice(2).includes('--json')) {
+ process.stdout.write(
+ `${JSON.stringify({
+ diagnostics: [
+ {
+ code: 'CLI_ERROR',
+ message,
+ severity: 'error',
+ },
+ ],
+ errorCount: 1,
+ ok: false,
+ })}\n`
+ );
+ } else {
+ process.stderr.write(`${message}\n`);
+ }
+ process.exitCode = 1;
+}
diff --git a/packages/repack-expo/src/cli/commands.ts b/packages/repack-expo/src/cli/commands.ts
new file mode 100644
index 000000000..a26a4f1e0
--- /dev/null
+++ b/packages/repack-expo/src/cli/commands.ts
@@ -0,0 +1,44 @@
+import type { CliOptions } from './types.js';
+
+export { runDoctor } from './doctor.js';
+export { runInit } from './init.js';
+export { detectPackageManager } from './project.js';
+export type {
+ CliOptions,
+ Diagnostic,
+ DiagnosticSeverity,
+ DoctorOptions,
+ DoctorResult,
+ FileChange,
+ InitOptions,
+ InitResult,
+} from './types.js';
+
+export const EXPO_COMMANDS = ['init', 'doctor'] as const;
+
+export type ExpoCommand = (typeof EXPO_COMMANDS)[number];
+
+export type ParsedExpoCommand = CliOptions & {
+ command: ExpoCommand;
+};
+
+const USAGE = `Usage: repack-expo <${EXPO_COMMANDS.join('|')}> [--check] [--dry-run] [--json] [--force]`;
+
+export function parseExpoCommand(argv: string[]): ParsedExpoCommand {
+ const [candidate, ...args] = argv;
+ if (!EXPO_COMMANDS.includes(candidate as ExpoCommand)) {
+ throw new Error(USAGE);
+ }
+ const allowed = new Set(['--check', '--dry-run', '--json', '--force']);
+ const unknown = args.find((argument) => !allowed.has(argument));
+ if (unknown || (candidate === 'doctor' && args.includes('--force'))) {
+ throw new Error(USAGE);
+ }
+ return {
+ check: args.includes('--check'),
+ command: candidate as ExpoCommand,
+ dryRun: args.includes('--dry-run'),
+ force: args.includes('--force'),
+ json: args.includes('--json'),
+ };
+}
diff --git a/packages/repack-expo/src/cli/doctor.ts b/packages/repack-expo/src/cli/doctor.ts
new file mode 100644
index 000000000..9a6aae57b
--- /dev/null
+++ b/packages/repack-expo/src/cli/doctor.ts
@@ -0,0 +1,602 @@
+import fs from 'node:fs';
+import path from 'node:path';
+import { resolveExpoEntry } from '../rspack/entry/resolveExpoEntry.js';
+import {
+ CONFIG_PLUGIN,
+ REQUIRED_DEV_DEPENDENCIES,
+ REQUIRED_SCRIPTS,
+ RSPACK_COMMANDS,
+ RSPACK_CONFIG_NAMES,
+ dynamicExpoConfig,
+ diagnosticError as error,
+ filesContain,
+ findFiles,
+ getConfigPluginRegistration,
+ hasDependency,
+ readJson,
+ diagnosticWarning as warning,
+} from './project.js';
+import {
+ getRspackFederationUsage,
+ isRspackConfigCompatible,
+} from './rspackConfig.js';
+import type {
+ ConfigPluginOptions,
+ Diagnostic,
+ DoctorOptions,
+ DoctorResult,
+ JsonObject,
+ PackageJson,
+} from './types.js';
+
+function getStaticExpoConfig(
+ projectRoot: string,
+ packageJson: PackageJson
+): { diagnostic?: Diagnostic; expo?: JsonObject } {
+ const dynamicConfig = dynamicExpoConfig(projectRoot);
+ if (dynamicConfig) {
+ return {
+ diagnostic: error(
+ 'DYNAMIC_EXPO_CONFIG',
+ `${dynamicConfig} cannot be evaluated safely by doctor.`,
+ 'Verify the Re.Pack plugin, New Architecture, Hermes, and disabled Updates settings manually, or expose static app.json config.'
+ ),
+ };
+ }
+ const appJsonPath = path.join(projectRoot, 'app.json');
+ try {
+ const root = fs.existsSync(appJsonPath)
+ ? readJson(appJsonPath)
+ : packageJson.expo
+ ? { expo: packageJson.expo }
+ : undefined;
+ if (
+ !root?.expo ||
+ typeof root.expo !== 'object' ||
+ Array.isArray(root.expo)
+ ) {
+ return {
+ diagnostic: error(
+ 'EXPO_CONFIG_MISSING',
+ 'No static Expo app config was found.',
+ 'Create app.json or a package.json#expo object.'
+ ),
+ };
+ }
+ return { expo: root.expo as JsonObject };
+ } catch (cause) {
+ return {
+ diagnostic: error(
+ 'EXPO_CONFIG_INVALID',
+ cause instanceof Error ? cause.message : String(cause),
+ 'Repair the static Expo app config.'
+ ),
+ };
+ }
+}
+
+export function runDoctor(options: DoctorOptions = {}): DoctorResult {
+ const projectRoot = path.resolve(options.projectRoot ?? process.cwd());
+ const diagnostics: Diagnostic[] = [];
+ const packagePath = path.join(projectRoot, 'package.json');
+ if (!fs.existsSync(packagePath)) {
+ diagnostics.push(
+ error(
+ 'PACKAGE_JSON_MISSING',
+ `No package.json was found at ${projectRoot}.`,
+ 'Run doctor from an Expo application root.'
+ )
+ );
+ return { diagnostics, errorCount: 1, ok: false, warningCount: 0 };
+ }
+
+ let packageJson: PackageJson;
+ try {
+ packageJson = readJson(packagePath) as PackageJson;
+ } catch (cause) {
+ diagnostics.push(
+ error(
+ 'PACKAGE_JSON_INVALID',
+ cause instanceof Error ? cause.message : String(cause),
+ 'Repair package.json and run doctor again.'
+ )
+ );
+ return { diagnostics, errorCount: 1, ok: false, warningCount: 0 };
+ }
+
+ if (!hasDependency(packageJson, 'expo')) {
+ diagnostics.push(
+ error(
+ 'EXPO_DEPENDENCY_MISSING',
+ 'package.json does not declare Expo.',
+ 'Run doctor from an Expo application root.'
+ )
+ );
+ }
+ const staticConfig = getStaticExpoConfig(projectRoot, packageJson);
+ if (staticConfig.diagnostic) diagnostics.push(staticConfig.diagnostic);
+ const expo = staticConfig.expo;
+ let configPluginOptions: ConfigPluginOptions = {};
+ if (expo) {
+ const updates = expo.updates;
+ if (
+ !updates ||
+ typeof updates !== 'object' ||
+ Array.isArray(updates) ||
+ (updates as JsonObject).enabled !== false
+ ) {
+ diagnostics.unshift(
+ error(
+ (updates as JsonObject | undefined)?.enabled === true
+ ? 'EXPO_UPDATES_ACTIVE'
+ : 'EXPO_UPDATES_NOT_DISABLED',
+ 'Expo Updates must be explicitly disabled for the supported v1 path.',
+ 'Set expo.updates.enabled to false and run expo prebuild again.'
+ )
+ );
+ }
+ if (expo.newArchEnabled === false) {
+ diagnostics.push(
+ error(
+ 'NEW_ARCH_REQUIRED',
+ 'React Native New Architecture is explicitly disabled.',
+ 'Remove the expo.newArchEnabled=false override and run expo prebuild again.'
+ )
+ );
+ }
+ const engines = [
+ expo.jsEngine,
+ (expo.ios as JsonObject | undefined)?.jsEngine,
+ (expo.android as JsonObject | undefined)?.jsEngine,
+ ].filter((value) => value !== undefined);
+ if (engines.some((engine) => engine !== 'hermes')) {
+ diagnostics.push(
+ error(
+ 'HERMES_REQUIRED',
+ 'The supported v1 path uses Hermes on every configured platform.',
+ 'Remove JSC overrides or set Expo jsEngine values to "hermes".'
+ )
+ );
+ }
+ const registration = getConfigPluginRegistration(expo);
+ configPluginOptions = registration.options;
+ if (registration.invalidReason) {
+ diagnostics.push(
+ error(
+ 'CONFIG_PLUGIN_OPTIONS_INVALID',
+ registration.invalidReason,
+ 'Keep one valid Re.Pack Expo Config Plugin registration.'
+ )
+ );
+ } else if (!registration.registered) {
+ diagnostics.push(
+ error(
+ 'CONFIG_PLUGIN_MISSING',
+ `${CONFIG_PLUGIN} is not registered in Expo plugins.`,
+ `Add ${JSON.stringify(CONFIG_PLUGIN)} to expo.plugins and run expo prebuild.`
+ )
+ );
+ }
+ }
+
+ for (const dependency of REQUIRED_DEV_DEPENDENCIES) {
+ if (!hasDependency(packageJson, dependency)) {
+ diagnostics.push(
+ error(
+ 'DEPENDENCY_MISSING',
+ `${dependency} is not declared in package.json.`,
+ `Add ${dependency} with your package manager and install dependencies.`
+ )
+ );
+ }
+ }
+
+ const rnConfigPath = path.join(projectRoot, 'react-native.config.js');
+ if (
+ !fs.existsSync(rnConfigPath) ||
+ !fs.readFileSync(rnConfigPath, 'utf8').includes(RSPACK_COMMANDS)
+ ) {
+ diagnostics.push(
+ error(
+ 'RN_COMMANDS_MISSING',
+ 'React Native CLI is not configured with the Re.Pack Rspack commands.',
+ `Register commands from require('${RSPACK_COMMANDS}') in react-native.config.js.`
+ )
+ );
+ }
+
+ const rspackNames = configPluginOptions.configPath
+ ? fs.existsSync(path.resolve(projectRoot, configPluginOptions.configPath))
+ ? [configPluginOptions.configPath]
+ : []
+ : RSPACK_CONFIG_NAMES.filter((name) =>
+ fs.existsSync(path.join(projectRoot, name))
+ );
+ if (rspackNames.length === 0) {
+ diagnostics.push(
+ error(
+ 'RSPACK_CONFIG_MISSING',
+ 'rspack.config.mjs is missing.',
+ 'Run repack-expo init or create the supported ExpoPlugin configuration.'
+ )
+ );
+ } else if (rspackNames.length > 1) {
+ diagnostics.push(
+ error(
+ 'RSPACK_CONFIG_AMBIGUOUS',
+ `Multiple Rspack configs were found: ${rspackNames.join(', ')}.`,
+ 'Keep one supported native-client Rspack config.'
+ )
+ );
+ } else {
+ const rspackPath = path.resolve(projectRoot, rspackNames[0] as string);
+ const contents = fs.readFileSync(rspackPath, 'utf8');
+ const federation = getRspackFederationUsage(contents);
+ if (federation === 'v1') {
+ diagnostics.push(
+ error(
+ 'MODULE_FEDERATION_V1_UNSUPPORTED',
+ 'Expo integration supports Module Federation v2 only.',
+ 'Replace the active federation plugin with Repack.plugins.ModuleFederationPluginV2.'
+ )
+ );
+ } else if (federation === 'raw') {
+ diagnostics.push(
+ error(
+ 'MODULE_FEDERATION_RAW_PLUGIN_UNSUPPORTED',
+ 'Direct @module-federation/enhanced/rspack plugin usage bypasses the Re.Pack native runtime integration.',
+ 'Use Repack.plugins.ModuleFederationPluginV2 from @callstack/repack.'
+ )
+ );
+ } else if (/new\s+(?:Repack\.)?RepackPlugin\s*\(/.test(contents)) {
+ diagnostics.push(
+ error(
+ 'REPACK_PLUGIN_DUPLICATE',
+ 'ExpoPlugin owns RepackPlugin and cannot be combined with another instance.',
+ 'Remove the application-owned RepackPlugin.'
+ )
+ );
+ } else if (!isRspackConfigCompatible(contents)) {
+ diagnostics.push(
+ error(
+ 'EXPO_PLUGIN_MISSING',
+ 'rspack.config.mjs does not configure ExpoPlugin.',
+ `Import ExpoPlugin from ${JSON.stringify(`${CONFIG_PLUGIN}/rspack`)} and add one instance.`
+ )
+ );
+ }
+ }
+
+ for (const [name, command] of Object.entries(REQUIRED_SCRIPTS)) {
+ if (packageJson.scripts?.[name] !== command) {
+ diagnostics.push(
+ error(
+ 'PACKAGE_SCRIPT_MISSING',
+ `package.json script ${JSON.stringify(name)} does not match the supported command.`,
+ `Set it to ${JSON.stringify(command)}.`
+ )
+ );
+ }
+ }
+
+ for (const platform of ['ios', 'android'] as const) {
+ try {
+ resolveExpoEntry({
+ entry: configPluginOptions.entry,
+ platform,
+ projectRoot,
+ });
+ } catch (cause) {
+ const resolution = cause as {
+ message?: unknown;
+ recovery?: unknown;
+ };
+ diagnostics.push(
+ error(
+ 'ENTRY_UNRESOLVED',
+ typeof resolution.message === 'string'
+ ? resolution.message
+ : `Cannot resolve the Expo entry for ${platform}.`,
+ typeof resolution.recovery === 'string'
+ ? resolution.recovery
+ : 'Correct the Config Plugin entry option or package.json#main.'
+ )
+ );
+ }
+ }
+
+ if (
+ [
+ 'webpack.config.mts',
+ 'webpack.config.cts',
+ 'webpack.config.ts',
+ 'webpack.config.mjs',
+ 'webpack.config.cjs',
+ 'webpack.config.js',
+ ].some((name) => fs.existsSync(path.join(projectRoot, name)))
+ ) {
+ diagnostics.push(
+ error(
+ 'WEBPACK_CONFIG_UNSUPPORTED',
+ 'A Webpack configuration was found; Expo v1 supports Rspack only.',
+ 'Remove the Webpack host setup from this supported native-client path.'
+ )
+ );
+ }
+
+ checkNativeProjects(projectRoot, diagnostics, configPluginOptions);
+
+ const errorCount = diagnostics.filter(
+ (diagnostic) => diagnostic.severity === 'error'
+ ).length;
+ const warningCount = diagnostics.filter(
+ (diagnostic) => diagnostic.severity === 'warning'
+ ).length;
+ return { diagnostics, errorCount, ok: errorCount === 0, warningCount };
+}
+
+function checkNativeProjects(
+ projectRoot: string,
+ diagnostics: Diagnostic[],
+ pluginOptions: ConfigPluginOptions
+): void {
+ const iosRoot = path.join(projectRoot, 'ios');
+ const androidRoot = path.join(projectRoot, 'android');
+ let nativeUpdatesActive = false;
+ let nativeHermesMismatch = false;
+ let nativeNewArchMismatch = false;
+ let autolinkUnverified = false;
+ if (fs.existsSync(iosRoot)) {
+ const iosFiles = findFiles(
+ iosRoot,
+ (filePath) =>
+ filePath.endsWith('.swift') || filePath.endsWith('project.pbxproj')
+ );
+ // Match the generated commands by their executable contract; generated
+ // headers include hashes that are intentionally not stable inputs.
+ const hasIosUrl = generatedSectionContains(
+ iosFiles,
+ 'repack-expo-ios-bundle-url',
+ [
+ 'return RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index")',
+ ]
+ );
+ const hasIosCommands = generatedSectionContains(
+ iosFiles,
+ 'repack-expo-ios-bundle-command',
+ [
+ 'export CLI_PATH="$("$NODE_BINARY" --print',
+ "require.resolve('@react-native-community/cli/build/bin.js')",
+ 'export BUNDLE_COMMAND="bundle"',
+ ...iosOptionCommands(pluginOptions),
+ ]
+ );
+ if (!hasIosUrl || !hasIosCommands) {
+ diagnostics.push(
+ error(
+ 'IOS_NATIVE_SEAMS_MISSING',
+ 'Generated iOS files do not contain both Re.Pack Expo integration seams.',
+ 'Run expo prebuild after registering the Config Plugin.'
+ )
+ );
+ }
+ const podLock = path.join(iosRoot, 'Podfile.lock');
+ if (!fs.existsSync(podLock)) {
+ autolinkUnverified = true;
+ } else if (!/callstack-repack/.test(fs.readFileSync(podLock, 'utf8'))) {
+ diagnostics.push(
+ error(
+ 'SCRIPTMANAGER_NOT_AUTOLINKED',
+ 'The iOS Pods lockfile does not contain the Re.Pack native module.',
+ 'Install pods after adding @callstack/repack, then regenerate the native project.'
+ )
+ );
+ }
+ const expoPlists = findFiles(iosRoot, (filePath) =>
+ filePath.endsWith('Expo.plist')
+ );
+ nativeUpdatesActive ||= filesContain(
+ expoPlists,
+ /\s*EXUpdatesEnabled\s*<\/key>\s*/
+ );
+ const podPropertiesPath = path.join(iosRoot, 'Podfile.properties.json');
+ if (fs.existsSync(podPropertiesPath)) {
+ try {
+ const properties = readJson(podPropertiesPath);
+ nativeHermesMismatch ||=
+ properties['expo.jsEngine'] !== undefined &&
+ properties['expo.jsEngine'] !== 'hermes';
+ } catch {
+ diagnostics.push(
+ error(
+ 'IOS_PROPERTIES_INVALID',
+ 'ios/Podfile.properties.json is not valid JSON.',
+ 'Regenerate the iOS project with expo prebuild.'
+ )
+ );
+ }
+ }
+ }
+ if (fs.existsSync(androidRoot)) {
+ const androidFiles = findFiles(
+ androidRoot,
+ (filePath) =>
+ /MainApplication\.(?:kt|java)$/.test(filePath) ||
+ /build\.gradle(?:\.kts)?$/.test(filePath)
+ );
+ const hasAndroidMain = generatedSectionContains(
+ androidFiles,
+ 'repack-expo-android-main-module',
+ [' jsMainModulePath = "index",']
+ );
+ const hasAndroidCommands = generatedSectionContains(
+ androidFiles,
+ 'repack-expo-android-bundle-command',
+ [
+ 'cliFile = new File(["node", "--print",',
+ "require.resolve('@react-native-community/cli/build/bin.js')",
+ 'bundleCommand = "bundle"',
+ ...androidOptionCommands(pluginOptions),
+ ]
+ );
+ if (!hasAndroidMain || !hasAndroidCommands) {
+ diagnostics.push(
+ error(
+ 'ANDROID_NATIVE_SEAMS_MISSING',
+ 'Generated Android files do not contain both Re.Pack Expo integration seams.',
+ 'Run expo prebuild after registering the Config Plugin.'
+ )
+ );
+ }
+ const manifests = findFiles(androidRoot, (filePath) =>
+ filePath.endsWith('AndroidManifest.xml')
+ );
+ nativeUpdatesActive ||= filesContain(
+ manifests,
+ /expo\.modules\.updates\.ENABLED[^>]+android:value=["']true["']/
+ );
+ const gradlePropertiesPath = path.join(androidRoot, 'gradle.properties');
+ if (fs.existsSync(gradlePropertiesPath)) {
+ const properties = fs.readFileSync(gradlePropertiesPath, 'utf8');
+ nativeNewArchMismatch ||= /^newArchEnabled\s*=\s*false\s*$/m.test(
+ properties
+ );
+ nativeHermesMismatch ||= /^hermesEnabled\s*=\s*false\s*$/m.test(
+ properties
+ );
+ }
+ const settingsPath = path.join(androidRoot, 'settings.gradle');
+ const buildGradlePath = path.join(androidRoot, 'app', 'build.gradle');
+ if (
+ !fs.existsSync(settingsPath) ||
+ !fs
+ .readFileSync(settingsPath, 'utf8')
+ .includes('autolinkLibrariesFromCommand') ||
+ !fs.existsSync(buildGradlePath) ||
+ !fs
+ .readFileSync(buildGradlePath, 'utf8')
+ .includes('autolinkLibrariesWithApp')
+ ) {
+ autolinkUnverified = true;
+ }
+ }
+ if (nativeUpdatesActive) {
+ diagnostics.push(
+ error(
+ 'NATIVE_UPDATES_ACTIVE',
+ 'A generated native project still enables Expo Updates.',
+ 'Set expo.updates.enabled to false and regenerate native projects with expo prebuild.'
+ )
+ );
+ }
+ if (nativeHermesMismatch) {
+ diagnostics.push(
+ error(
+ 'NATIVE_HERMES_MISMATCH',
+ 'A generated native project is configured to use JSC instead of Hermes.',
+ 'Set Expo jsEngine values to "hermes" and regenerate native projects.'
+ )
+ );
+ }
+ if (nativeNewArchMismatch) {
+ diagnostics.push(
+ error(
+ 'NATIVE_NEW_ARCH_MISMATCH',
+ 'A generated native project disables React Native New Architecture.',
+ 'Remove the New Architecture disable override and regenerate native projects.'
+ )
+ );
+ }
+ if (autolinkUnverified) {
+ diagnostics.push(
+ warning(
+ 'SCRIPTMANAGER_AUTOLINK_UNVERIFIED',
+ 'Generated artifacts do not prove ScriptManager autolinking on every generated platform.',
+ 'Install native dependencies and regenerate the native projects, then rerun doctor.'
+ )
+ );
+ }
+ if (!fs.existsSync(iosRoot) && !fs.existsSync(androidRoot)) {
+ diagnostics.push(
+ warning(
+ 'NATIVE_PROJECTS_ABSENT',
+ 'No generated iOS or Android project is available to inspect.',
+ 'Run expo prebuild, then rerun doctor to validate native build seams.'
+ )
+ );
+ }
+}
+
+function generatedSectionContains(
+ files: string[],
+ tag: string,
+ expectedFragments: string[]
+): boolean {
+ let matches = 0;
+ for (const filePath of files) {
+ const contents = fs.readFileSync(filePath, 'utf8');
+ const beginMarker = `@generated begin ${tag}`;
+ const endMarker = `@generated end ${tag}`;
+ if (
+ contents.split(beginMarker).length - 1 !== 1 ||
+ contents.split(endMarker).length - 1 !== 1
+ ) {
+ if (contents.includes(beginMarker) || contents.includes(endMarker)) {
+ return false;
+ }
+ continue;
+ }
+ const begin = contents.indexOf(beginMarker);
+ const end = contents.indexOf(endMarker, begin + 1);
+ const section = contents
+ .slice(begin, end)
+ .replaceAll('\\"', '"')
+ .replaceAll('\\n', '\n');
+ if (!expectedFragments.every((fragment) => section.includes(fragment))) {
+ return false;
+ }
+ matches += 1;
+ }
+ return matches === 1;
+}
+
+function iosOptionCommands(options: ConfigPluginOptions): string[] {
+ const commands: string[] = [];
+ if (options.entry) {
+ commands.push(`export ENTRY_FILE="${iosProjectPath(options.entry)}"`);
+ }
+ if (options.configPath) {
+ commands.push(
+ `export BUNDLE_CONFIG="${iosProjectPath(options.configPath)}"`
+ );
+ }
+ return commands;
+}
+
+function iosProjectPath(value: string): string {
+ const escaped = value
+ .replaceAll('\\', '\\\\')
+ .replaceAll('"', '\\"')
+ .replaceAll('$', '\\$')
+ .replaceAll('`', '\\`');
+ return value.startsWith('/') ? escaped : `$PROJECT_ROOT/${escaped}`;
+}
+
+function androidOptionCommands(options: ConfigPluginOptions): string[] {
+ const commands: string[] = [];
+ if (options.entry) {
+ const entry = options.entry.startsWith('/')
+ ? options.entry
+ : `../${options.entry}`;
+ commands.push(`entryFile = rootProject.file(${JSON.stringify(entry)})`);
+ }
+ if (options.configPath) {
+ const configPath = options.configPath.startsWith('/')
+ ? options.configPath
+ : `../${options.configPath}`;
+ commands.push(
+ `bundleConfig = rootProject.file(${JSON.stringify(configPath)})`
+ );
+ }
+ return commands;
+}
diff --git a/packages/repack-expo/src/cli/init.ts b/packages/repack-expo/src/cli/init.ts
new file mode 100644
index 000000000..ffca29842
--- /dev/null
+++ b/packages/repack-expo/src/cli/init.ts
@@ -0,0 +1,507 @@
+import fs from 'node:fs';
+import path from 'node:path';
+import {
+ CONFIG_PLUGIN,
+ REQUIRED_DEV_DEPENDENCIES,
+ REQUIRED_SCRIPTS,
+ RSPACK_COMMANDS,
+ RSPACK_CONFIG_NAMES,
+ addDependencyCommand,
+ dependencyRanges,
+ detectPackageManager,
+ diagnosticError,
+ dynamicExpoConfig,
+ getConfigPluginRegistration,
+ hasDependency,
+ installCommand,
+ readJson,
+ stringifyJson,
+ writeFilesAtomically,
+} from './project.js';
+import { isRspackConfigCompatible } from './rspackConfig.js';
+import type {
+ ConfigPluginOptions,
+ Diagnostic,
+ InitOptions,
+ InitResult,
+ JsonObject,
+ PackageJson,
+} from './types.js';
+
+function rspackConfig(
+ packageName: string,
+ configPath: string,
+ packageType?: string
+): string {
+ const uniqueName = packageName.replace(/[^a-zA-Z0-9_-]/g, '-') || 'expo-app';
+ const rootFromConfig = path.relative(path.dirname(configPath), '.') || '.';
+ const commonJs =
+ /\.(?:cjs|cts)$/.test(configPath) ||
+ (configPath.endsWith('.js') && packageType !== 'module');
+ if (commonJs) {
+ return `const path = require('node:path');
+const { ExpoPlugin } = require('@callstack/repack-expo/rspack');
+
+const projectRoot = path.resolve(__dirname, ${JSON.stringify(rootFromConfig)});
+
+module.exports = (env) => createConfig(env, projectRoot);
+
+function createConfig(env, projectRoot) {
+ const { mode = 'development', platform = process.env.PLATFORM, devServer } = env;
+
+ if (platform !== 'ios' && platform !== 'android') {
+ throw new Error('ExpoPlugin requires PLATFORM=ios or android');
+ }
+
+ return {
+ context: projectRoot,
+ devServer,
+ mode,
+ name: platform,
+ output: {
+ clean: true,
+ path: path.join(projectRoot, 'build', 'rspack', platform),
+ uniqueName: ${JSON.stringify(uniqueName)},
+ },
+ plugins: [new ExpoPlugin({ platform })],
+ };
+}
+`;
+ }
+ return `import path from 'node:path';
+import { fileURLToPath } from 'node:url';
+import { ExpoPlugin } from '@callstack/repack-expo/rspack';
+
+const projectRoot = path.resolve(
+ path.dirname(fileURLToPath(import.meta.url)),
+ ${JSON.stringify(rootFromConfig)}
+);
+
+export default (env) => {
+ const { mode = 'development', platform = process.env.PLATFORM, devServer } = env;
+
+ if (platform !== 'ios' && platform !== 'android') {
+ throw new Error('ExpoPlugin requires PLATFORM=ios or android');
+ }
+
+ return {
+ context: projectRoot,
+ devServer,
+ mode,
+ name: platform,
+ output: {
+ clean: true,
+ path: path.join(projectRoot, 'build', 'rspack', platform),
+ uniqueName: ${JSON.stringify(uniqueName)},
+ },
+ plugins: [new ExpoPlugin({ platform })],
+ };
+};
+`;
+}
+
+function addConfigPlugin(expo: JsonObject): {
+ diagnostic?: Diagnostic;
+ options: ConfigPluginOptions;
+} {
+ if (expo.plugins === undefined) expo.plugins = [];
+ if (!Array.isArray(expo.plugins)) {
+ return {
+ diagnostic: diagnosticError(
+ 'EXPO_CONFIG_PLUGINS_INVALID',
+ 'Expo config "plugins" must be an array before init can update it.',
+ `Add ${JSON.stringify(CONFIG_PLUGIN)} to the Expo plugins array manually.`
+ ),
+ options: {},
+ };
+ }
+ const registration = getConfigPluginRegistration(expo);
+ if (registration.invalidReason) {
+ return {
+ diagnostic: diagnosticError(
+ 'CONFIG_PLUGIN_OPTIONS_INVALID',
+ registration.invalidReason,
+ 'Keep one valid Re.Pack Expo Config Plugin registration.'
+ ),
+ options: {},
+ };
+ }
+ if (!registration.registered) expo.plugins.push(CONFIG_PLUGIN);
+ return { options: registration.options };
+}
+
+function disableExpoUpdates(expo: JsonObject): Diagnostic | undefined {
+ if (expo.updates === undefined) {
+ expo.updates = { enabled: false };
+ return undefined;
+ }
+ if (
+ !expo.updates ||
+ typeof expo.updates !== 'object' ||
+ Array.isArray(expo.updates)
+ ) {
+ return diagnosticError(
+ 'EXPO_UPDATES_CONFLICT',
+ 'Expo config has an unsupported explicit updates value.',
+ 'Preserve the Updates config and set expo.updates.enabled to false manually.'
+ );
+ }
+ const updates = expo.updates as JsonObject;
+ if (updates.enabled === undefined) {
+ updates.enabled = false;
+ return undefined;
+ }
+ if (updates.enabled !== false) {
+ return diagnosticError(
+ 'EXPO_UPDATES_CONFLICT',
+ 'Expo config explicitly enables Expo Updates.',
+ 'Set expo.updates.enabled to false before running init.'
+ );
+ }
+ return undefined;
+}
+
+function normalizeExpoRuntimeDefaults(expo: JsonObject): Diagnostic[] {
+ const diagnostics: Diagnostic[] = [];
+ if (expo.newArchEnabled === true) {
+ delete expo.newArchEnabled;
+ } else if (expo.newArchEnabled !== undefined) {
+ diagnostics.push(
+ diagnosticError(
+ 'EXPO_NEW_ARCH_CONFLICT',
+ 'Expo config explicitly disables React Native New Architecture.',
+ 'Remove expo.newArchEnabled; Expo SDK 56+ enables New Architecture by default.'
+ )
+ );
+ }
+
+ if (expo.jsEngine === 'hermes') {
+ delete expo.jsEngine;
+ } else if (expo.jsEngine !== undefined) {
+ diagnostics.push(
+ diagnosticError(
+ 'EXPO_JS_ENGINE_CONFLICT',
+ 'Expo config explicitly selects a non-Hermes JavaScript engine.',
+ 'Remove expo.jsEngine; Expo SDK 56+ uses Hermes by default.'
+ )
+ );
+ }
+ return diagnostics;
+}
+
+function planReactNativeConfig(
+ projectRoot: string,
+ diagnostics: Diagnostic[]
+): string | undefined {
+ const filePath = path.join(projectRoot, 'react-native.config.js');
+ if (!fs.existsSync(filePath)) {
+ return `module.exports = {\n commands: require('${RSPACK_COMMANDS}'),\n};\n`;
+ }
+ const contents = fs.readFileSync(filePath, 'utf8');
+ if (contents.includes(RSPACK_COMMANDS)) return contents;
+ if (/commands\s*:/.test(contents)) {
+ diagnostics.push(
+ diagnosticError(
+ 'RN_COMMANDS_CONFLICT',
+ 'react-native.config.js already registers a different commands implementation.',
+ `Merge commands from require('${RSPACK_COMMANDS}') manually.`
+ )
+ );
+ return undefined;
+ }
+ if (/module\.exports\s*=\s*\{/.test(contents)) {
+ return contents.replace(
+ /module\.exports\s*=\s*\{/,
+ `module.exports = {\n commands: require('${RSPACK_COMMANDS}'),`
+ );
+ }
+ diagnostics.push(
+ diagnosticError(
+ 'RN_CONFIG_AMBIGUOUS',
+ 'react-native.config.js cannot be updated safely.',
+ `Register commands from require('${RSPACK_COMMANDS}') manually.`
+ )
+ );
+ return undefined;
+}
+
+export function runInit(options: InitOptions = {}): InitResult {
+ const projectRoot = path.resolve(options.projectRoot ?? process.cwd());
+ const diagnostics: Diagnostic[] = [];
+ const packagePath = path.join(projectRoot, 'package.json');
+ if (!fs.existsSync(packagePath)) {
+ const packageManager = detectPackageManager(projectRoot);
+ return {
+ changes: [],
+ changedFiles: [],
+ diagnostics: [
+ diagnosticError(
+ 'PACKAGE_JSON_MISSING',
+ `No package.json was found at ${projectRoot}.`,
+ 'Run the command from an Expo application root.'
+ ),
+ ],
+ installCommand: installCommand(packageManager),
+ ok: false,
+ packageManager,
+ wrote: false,
+ };
+ }
+
+ let packageJson: PackageJson;
+ let packageOriginal: string;
+ try {
+ packageOriginal = fs.readFileSync(packagePath, 'utf8');
+ packageJson = readJson(packagePath) as PackageJson;
+ } catch (cause) {
+ const packageManager = detectPackageManager(projectRoot);
+ return {
+ changes: [],
+ changedFiles: [],
+ diagnostics: [
+ diagnosticError(
+ 'PACKAGE_JSON_INVALID',
+ cause instanceof Error ? cause.message : String(cause),
+ 'Repair package.json and run init again.'
+ ),
+ ],
+ installCommand: installCommand(packageManager),
+ ok: false,
+ packageManager,
+ wrote: false,
+ };
+ }
+ const packageManager = detectPackageManager(projectRoot, packageJson);
+
+ if (!hasDependency(packageJson, 'expo')) {
+ diagnostics.push(
+ diagnosticError(
+ 'EXPO_DEPENDENCY_MISSING',
+ 'package.json does not declare Expo.',
+ 'Run init from an Expo application root.'
+ )
+ );
+ }
+
+ const dynamicConfig = dynamicExpoConfig(projectRoot);
+ if (dynamicConfig) {
+ diagnostics.push(
+ diagnosticError(
+ 'DYNAMIC_EXPO_CONFIG',
+ `${dynamicConfig} may contain executable configuration and will not be rewritten.`,
+ `Add ${JSON.stringify(CONFIG_PLUGIN)} and the supported engine, architecture, and Updates settings manually.`
+ )
+ );
+ }
+
+ const appJsonPath = path.join(projectRoot, 'app.json');
+ let appOriginal: string | undefined;
+ let appJson: JsonObject | undefined;
+ let expo: JsonObject | undefined;
+ let configPluginOptions: ConfigPluginOptions = {};
+ if (!dynamicConfig && fs.existsSync(appJsonPath)) {
+ try {
+ appOriginal = fs.readFileSync(appJsonPath, 'utf8');
+ appJson = readJson(appJsonPath);
+ if (
+ !appJson.expo ||
+ typeof appJson.expo !== 'object' ||
+ Array.isArray(appJson.expo)
+ ) {
+ diagnostics.push(
+ diagnosticError(
+ 'EXPO_CONFIG_INVALID',
+ 'app.json does not contain an Expo config object.',
+ 'Add a top-level "expo" object and run init again.'
+ )
+ );
+ } else {
+ expo = appJson.expo as JsonObject;
+ }
+ } catch (cause) {
+ diagnostics.push(
+ diagnosticError(
+ 'EXPO_CONFIG_INVALID',
+ cause instanceof Error ? cause.message : String(cause),
+ 'Repair app.json and run init again.'
+ )
+ );
+ }
+ } else if (!dynamicConfig && packageJson.expo !== undefined) {
+ if (
+ !packageJson.expo ||
+ typeof packageJson.expo !== 'object' ||
+ Array.isArray(packageJson.expo)
+ ) {
+ diagnostics.push(
+ diagnosticError(
+ 'EXPO_CONFIG_INVALID',
+ 'package.json#expo must contain an object.',
+ 'Replace package.json#expo with a valid Expo config object.'
+ )
+ );
+ } else {
+ expo = packageJson.expo;
+ }
+ } else if (!dynamicConfig) {
+ diagnostics.push(
+ diagnosticError(
+ 'EXPO_CONFIG_MISSING',
+ 'No static Expo app config was found.',
+ 'Create app.json or a package.json#expo object and run init again.'
+ )
+ );
+ }
+
+ if (expo) {
+ const plugin = addConfigPlugin(expo);
+ configPluginOptions = plugin.options;
+ const configDiagnostics = [
+ plugin.diagnostic,
+ ...normalizeExpoRuntimeDefaults(expo),
+ disableExpoUpdates(expo),
+ ].filter((item): item is Diagnostic => item !== undefined);
+ diagnostics.push(...configDiagnostics);
+ }
+
+ packageJson.scripts ??= {};
+ for (const [name, command] of Object.entries(REQUIRED_SCRIPTS)) {
+ const current = packageJson.scripts[name];
+ if (current && current !== command) {
+ diagnostics.push(
+ diagnosticError(
+ 'PACKAGE_SCRIPT_CONFLICT',
+ `package.json script ${JSON.stringify(name)} already has an explicit command.`,
+ `Preserve it or change it to ${JSON.stringify(command)} before running init.`
+ )
+ );
+ } else {
+ packageJson.scripts[name] = command;
+ }
+ }
+ packageJson.devDependencies ??= {};
+ const ranges = dependencyRanges();
+ const dependenciesToAdd: string[] = [];
+ for (const dependency of REQUIRED_DEV_DEPENDENCIES) {
+ if (!hasDependency(packageJson, dependency)) {
+ const range = ranges[dependency];
+ if (range) packageJson.devDependencies[dependency] = range;
+ else dependenciesToAdd.push(dependency);
+ }
+ }
+ if (dependenciesToAdd.length > 0) {
+ diagnostics.push({
+ code: 'DEPENDENCIES_REQUIRE_INSTALL',
+ message: `Required dependencies need a project-compatible version: ${dependenciesToAdd.join(', ')}.`,
+ recovery: addDependencyCommand(packageManager, dependenciesToAdd),
+ severity: 'info',
+ });
+ }
+
+ const rnConfig = planReactNativeConfig(projectRoot, diagnostics);
+ const configuredRspackName = configPluginOptions.configPath;
+ const existingRspackNames = configuredRspackName
+ ? fs.existsSync(path.resolve(projectRoot, configuredRspackName))
+ ? [configuredRspackName]
+ : []
+ : RSPACK_CONFIG_NAMES.filter((name) =>
+ fs.existsSync(path.join(projectRoot, name))
+ );
+ const rspackName =
+ configuredRspackName ?? existingRspackNames[0] ?? 'rspack.config.mjs';
+ const rspackPath = path.resolve(projectRoot, rspackName);
+ const relativeRspackPath = path.relative(
+ projectRoot,
+ path.resolve(rspackPath)
+ );
+ if (
+ relativeRspackPath.startsWith('..') ||
+ path.isAbsolute(relativeRspackPath)
+ ) {
+ diagnostics.push(
+ diagnosticError(
+ 'RSPACK_CONFIG_OUTSIDE_PROJECT',
+ `Config Plugin configPath resolves outside the Expo project: ${rspackName}.`,
+ 'Use a project-relative configPath inside the Expo application.'
+ )
+ );
+ }
+ const generatedRspack = rspackConfig(
+ packageJson.name ?? 'expo-app',
+ relativeRspackPath,
+ packageJson.type
+ );
+ let rspackContents = generatedRspack;
+ if (existingRspackNames.length > 1) {
+ diagnostics.push(
+ diagnosticError(
+ 'RSPACK_CONFIG_AMBIGUOUS',
+ `Multiple Rspack configs were found: ${existingRspackNames.join(', ')}.`,
+ 'Keep one Rspack config and rerun init.'
+ )
+ );
+ } else if (fs.existsSync(rspackPath)) {
+ const existing = fs.readFileSync(rspackPath, 'utf8');
+ if (isRspackConfigCompatible(existing)) {
+ rspackContents = existing;
+ } else if (!options.force) {
+ diagnostics.push(
+ diagnosticError(
+ 'RSPACK_CONFIG_CONFLICT',
+ `Existing ${rspackName} is not a supported ExpoPlugin configuration.`,
+ `Update it manually, or rerun init with --force to replace ${rspackName}.`
+ )
+ );
+ rspackContents = existing;
+ }
+ }
+
+ if (diagnostics.some((item) => item.severity === 'error')) {
+ return {
+ changes: [],
+ changedFiles: [],
+ diagnostics,
+ installCommand: installCommand(packageManager),
+ ok: false,
+ packageManager,
+ wrote: false,
+ };
+ }
+
+ const planned = new Map();
+ planned.set('package.json', stringifyJson(packageJson, packageOriginal));
+ if (appJson && appOriginal !== undefined) {
+ planned.set('app.json', stringifyJson(appJson, appOriginal));
+ }
+ planned.set('react-native.config.js', rnConfig as string);
+ planned.set(relativeRspackPath, rspackContents);
+ const changes = [...planned].flatMap(([relativePath, contents]) => {
+ const filePath = path.join(projectRoot, relativePath);
+ return !fs.existsSync(filePath) ||
+ fs.readFileSync(filePath, 'utf8') !== contents
+ ? [
+ {
+ after: contents,
+ before: fs.existsSync(filePath)
+ ? fs.readFileSync(filePath, 'utf8')
+ : null,
+ path: relativePath,
+ },
+ ]
+ : [];
+ });
+ const changedFiles = changes.map((change) => change.path);
+ const shouldWrite = !options.check && !options.dryRun;
+ if (shouldWrite) {
+ writeFilesAtomically(projectRoot, planned, changedFiles);
+ }
+ return {
+ changes,
+ changedFiles,
+ diagnostics,
+ installCommand: addDependencyCommand(packageManager, dependenciesToAdd),
+ ok: !options.check || changedFiles.length === 0,
+ packageManager,
+ wrote: shouldWrite && changedFiles.length > 0,
+ };
+}
diff --git a/packages/repack-expo/src/cli/project.ts b/packages/repack-expo/src/cli/project.ts
new file mode 100644
index 000000000..3bcde4a85
--- /dev/null
+++ b/packages/repack-expo/src/cli/project.ts
@@ -0,0 +1,343 @@
+import fs from 'node:fs';
+import path from 'node:path';
+import type {
+ ConfigPluginRegistration,
+ Diagnostic,
+ JsonObject,
+ PackageJson,
+ PackageManager,
+} from './types.js';
+
+export const CONFIG_PLUGIN = '@callstack/repack-expo';
+export const RSPACK_COMMANDS = '@callstack/repack/commands/rspack';
+export const RSPACK_CONFIG_NAMES = [
+ 'rspack.config.mts',
+ 'rspack.config.cts',
+ 'rspack.config.ts',
+ 'rspack.config.mjs',
+ 'rspack.config.cjs',
+ 'rspack.config.js',
+] as const;
+export const REQUIRED_DEV_DEPENDENCIES = [
+ '@callstack/repack',
+ '@callstack/repack-expo',
+ '@react-native-community/cli',
+ '@rspack/core',
+] as const;
+export const REQUIRED_SCRIPTS = {
+ 'repack:start': 'react-native webpack-start',
+ 'repack:ios': 'expo run:ios --no-bundler',
+ 'repack:android': 'expo run:android --no-bundler',
+} as const;
+
+export function diagnosticError(
+ code: string,
+ message: string,
+ recovery: string
+): Diagnostic {
+ return { code, message, recovery, severity: 'error' };
+}
+
+export function diagnosticWarning(
+ code: string,
+ message: string,
+ recovery?: string
+): Diagnostic {
+ return { code, message, recovery, severity: 'warning' };
+}
+
+export function readJson(filePath: string): JsonObject {
+ const value: unknown = JSON.parse(fs.readFileSync(filePath, 'utf8'));
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
+ throw new Error(`${path.basename(filePath)} must contain a JSON object.`);
+ }
+ return value as JsonObject;
+}
+
+export function stringifyJson(value: JsonObject, original: string): string {
+ const indentMatch = original.match(/\n([ \t]+)\S/);
+ const indent = indentMatch?.[1] ?? ' ';
+ const trailingNewline = original.endsWith('\n') ? '\n' : '';
+ return `${JSON.stringify(value, null, indent)}${trailingNewline}`;
+}
+
+export function hasDependency(packageJson: PackageJson, name: string): boolean {
+ return Boolean(
+ packageJson.dependencies?.[name] ?? packageJson.devDependencies?.[name]
+ );
+}
+
+export function detectPackageManager(
+ projectRoot: string,
+ packageJson?: PackageJson
+): PackageManager {
+ const lockfiles: Array<[string, PackageManager]> = [
+ ['pnpm-lock.yaml', 'pnpm'],
+ ['yarn.lock', 'yarn'],
+ ['bun.lock', 'bun'],
+ ['bun.lockb', 'bun'],
+ ['package-lock.json', 'npm'],
+ ['npm-shrinkwrap.json', 'npm'],
+ ];
+ let current = path.resolve(projectRoot);
+ while (true) {
+ let currentPackageJson =
+ current === path.resolve(projectRoot) ? packageJson : undefined;
+ const packagePath = path.join(current, 'package.json');
+ if (!currentPackageJson && fs.existsSync(packagePath)) {
+ try {
+ currentPackageJson = readJson(packagePath);
+ } catch {
+ currentPackageJson = undefined;
+ }
+ }
+ const declared = currentPackageJson?.packageManager;
+ const name =
+ typeof declared === 'string' ? declared.split('@')[0] : undefined;
+ if (
+ name === 'npm' ||
+ name === 'yarn' ||
+ name === 'pnpm' ||
+ name === 'bun'
+ ) {
+ return name;
+ }
+ const locked = lockfiles.find(([lockfile]) =>
+ fs.existsSync(path.join(current, lockfile))
+ )?.[1];
+ if (locked) return locked;
+
+ const parent = path.dirname(current);
+ if (
+ parent === current ||
+ fs.existsSync(path.join(current, '.git')) ||
+ fs.existsSync(path.join(current, 'pnpm-workspace.yaml'))
+ ) {
+ break;
+ }
+ current = parent;
+ }
+ return 'npm';
+}
+
+export function getConfigPluginRegistration(
+ expo: JsonObject
+): ConfigPluginRegistration {
+ if (expo.plugins === undefined) {
+ return { options: {}, registered: false };
+ }
+ if (!Array.isArray(expo.plugins)) {
+ return {
+ invalidReason: 'Expo config plugins must be an array.',
+ options: {},
+ registered: false,
+ };
+ }
+ const matches = expo.plugins.filter(
+ (plugin) =>
+ plugin === CONFIG_PLUGIN ||
+ (Array.isArray(plugin) && plugin[0] === CONFIG_PLUGIN)
+ );
+ if (matches.length === 0) return { options: {}, registered: false };
+ if (matches.length > 1) {
+ return {
+ invalidReason: `${CONFIG_PLUGIN} is registered more than once.`,
+ options: {},
+ registered: true,
+ };
+ }
+ const match = matches[0];
+ if (match === CONFIG_PLUGIN) return { options: {}, registered: true };
+ const rawOptions = (match as unknown[])[1];
+ if (rawOptions === undefined) return { options: {}, registered: true };
+ if (
+ !rawOptions ||
+ typeof rawOptions !== 'object' ||
+ Array.isArray(rawOptions)
+ ) {
+ return {
+ invalidReason: `${CONFIG_PLUGIN} options must be an object.`,
+ options: {},
+ registered: true,
+ };
+ }
+ const options = rawOptions as JsonObject;
+ for (const name of ['entry', 'configPath'] as const) {
+ const value = options[name];
+ if (
+ value !== undefined &&
+ (typeof value !== 'string' ||
+ value.length === 0 ||
+ /[\r\n\0]/.test(value))
+ ) {
+ return {
+ invalidReason: `${CONFIG_PLUGIN} option ${name} must be a non-empty single-line string.`,
+ options: {},
+ registered: true,
+ };
+ }
+ }
+ return {
+ options: {
+ configPath: options.configPath as string | undefined,
+ entry: options.entry as string | undefined,
+ },
+ registered: true,
+ };
+}
+
+export function installCommand(packageManager: PackageManager): string {
+ return packageManager === 'yarn'
+ ? 'yarn install'
+ : `${packageManager} install`;
+}
+
+function packageMetadata(): {
+ peerDependencies?: Record;
+ version?: string;
+} {
+ try {
+ return readJson(path.resolve(__dirname, '../../package.json'));
+ } catch {
+ return {};
+ }
+}
+
+function normalizePublishRange(
+ name: string,
+ range: string
+): string | undefined {
+ if (!range.startsWith('workspace:')) return range;
+ try {
+ const sibling = readJson(
+ path.resolve(__dirname, `../../../${name.split('/').at(-1)}/package.json`)
+ );
+ const version = sibling.version;
+ if (typeof version !== 'string') return undefined;
+ return range === 'workspace:^' ? `^${version}` : version;
+ } catch {
+ return undefined;
+ }
+}
+
+export function dependencyRanges(): Record {
+ const metadata = packageMetadata();
+ const ranges: Record = {};
+ if (metadata.version) ranges[CONFIG_PLUGIN] = metadata.version;
+ for (const name of ['@callstack/repack', '@rspack/core']) {
+ const range = metadata.peerDependencies?.[name];
+ if (range) {
+ const normalized = normalizePublishRange(name, range);
+ if (normalized) ranges[name] = normalized;
+ }
+ }
+ return ranges;
+}
+
+export function addDependencyCommand(
+ packageManager: PackageManager,
+ dependencies: string[]
+): string {
+ if (dependencies.length === 0) return installCommand(packageManager);
+ const names = dependencies.join(' ');
+ if (packageManager === 'npm') return `npm install --save-dev ${names}`;
+ if (packageManager === 'yarn') return `yarn add --dev ${names}`;
+ if (packageManager === 'bun') return `bun add --dev ${names}`;
+ return `pnpm add --save-dev ${names}`;
+}
+
+export function dynamicExpoConfig(projectRoot: string): string | undefined {
+ return [
+ 'app.config.ts',
+ 'app.config.js',
+ 'app.config.mjs',
+ 'app.config.cjs',
+ ].find((name) => fs.existsSync(path.join(projectRoot, name)));
+}
+
+export function findFiles(
+ directory: string,
+ predicate: (filePath: string) => boolean
+): string[] {
+ if (!fs.existsSync(directory)) return [];
+ const files: string[] = [];
+ const pending = [directory];
+ while (pending.length > 0) {
+ const current = pending.pop() as string;
+ for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
+ if (entry.name === 'Pods' || entry.name === 'build') continue;
+ const entryPath = path.join(current, entry.name);
+ if (entry.isDirectory()) pending.push(entryPath);
+ else if (predicate(entryPath)) files.push(entryPath);
+ }
+ }
+ return files;
+}
+
+export function filesContain(files: string[], pattern: RegExp): boolean {
+ return files.some((filePath) =>
+ pattern.test(fs.readFileSync(filePath, 'utf8'))
+ );
+}
+
+export function writeFilesAtomically(
+ projectRoot: string,
+ files: ReadonlyMap,
+ relativePaths: string[]
+): void {
+ const transactionId = `${process.pid}-${Date.now()}`;
+ const createdDirectories: string[] = [];
+ const staged: Array<{
+ backupPath: string;
+ existed: boolean;
+ targetPath: string;
+ temporaryPath: string;
+ }> = [];
+ try {
+ for (const relativePath of relativePaths) {
+ const targetPath = path.join(projectRoot, relativePath);
+ const temporaryPath = `${targetPath}.repack-expo-${transactionId}.tmp`;
+ const backupPath = `${targetPath}.repack-expo-${transactionId}.bak`;
+ let directory = path.dirname(targetPath);
+ const missingDirectories: string[] = [];
+ while (!fs.existsSync(directory) && directory !== projectRoot) {
+ missingDirectories.push(directory);
+ directory = path.dirname(directory);
+ }
+ fs.mkdirSync(path.dirname(targetPath), { recursive: true });
+ createdDirectories.push(...missingDirectories);
+ fs.writeFileSync(temporaryPath, files.get(relativePath) as string);
+ const existed = fs.existsSync(targetPath);
+ if (existed) fs.chmodSync(temporaryPath, fs.statSync(targetPath).mode);
+ staged.push({ backupPath, existed, targetPath, temporaryPath });
+ }
+
+ for (const item of staged) {
+ if (item.existed) fs.renameSync(item.targetPath, item.backupPath);
+ fs.renameSync(item.temporaryPath, item.targetPath);
+ }
+ for (const item of staged) {
+ if (item.existed && fs.existsSync(item.backupPath)) {
+ fs.unlinkSync(item.backupPath);
+ }
+ }
+ } catch (cause) {
+ for (const item of [...staged].reverse()) {
+ if (fs.existsSync(item.temporaryPath)) fs.unlinkSync(item.temporaryPath);
+ if (fs.existsSync(item.backupPath)) {
+ if (fs.existsSync(item.targetPath)) fs.unlinkSync(item.targetPath);
+ fs.renameSync(item.backupPath, item.targetPath);
+ } else if (!item.existed && fs.existsSync(item.targetPath)) {
+ fs.unlinkSync(item.targetPath);
+ }
+ }
+ for (const directory of createdDirectories) {
+ try {
+ fs.rmdirSync(directory);
+ } catch {
+ // Keep non-empty directories: their contents are not owned by init.
+ }
+ }
+ throw cause;
+ }
+}
diff --git a/packages/repack-expo/src/cli/rspackConfig.ts b/packages/repack-expo/src/cli/rspackConfig.ts
new file mode 100644
index 000000000..175a2ec29
--- /dev/null
+++ b/packages/repack-expo/src/cli/rspackConfig.ts
@@ -0,0 +1,151 @@
+export type RspackFederationUsage = 'none' | 'raw' | 'v1' | 'v2';
+
+function maskJavaScriptNonCode(source: string, maskStrings: boolean): string {
+ let result = '';
+ let state: 'block-comment' | 'code' | 'line-comment' | 'string' = 'code';
+ let quote = '';
+
+ for (let index = 0; index < source.length; index += 1) {
+ const character = source[index] as string;
+ const next = source[index + 1];
+
+ if (state === 'line-comment') {
+ if (character === '\n' || character === '\r') {
+ state = 'code';
+ result += character;
+ } else {
+ result += ' ';
+ }
+ continue;
+ }
+
+ if (state === 'block-comment') {
+ if (character === '*' && next === '/') {
+ result += ' ';
+ index += 1;
+ state = 'code';
+ } else {
+ result += character === '\n' || character === '\r' ? character : ' ';
+ }
+ continue;
+ }
+
+ if (state === 'string') {
+ if (character === '\\' && next !== undefined) {
+ result += maskStrings ? ' ' : `${character}${next}`;
+ index += 1;
+ } else {
+ result +=
+ maskStrings && character !== '\n' && character !== '\r'
+ ? ' '
+ : character;
+ if (character === quote) state = 'code';
+ }
+ continue;
+ }
+
+ if (character === '/' && next === '/') {
+ result += ' ';
+ index += 1;
+ state = 'line-comment';
+ } else if (character === '/' && next === '*') {
+ result += ' ';
+ index += 1;
+ state = 'block-comment';
+ } else if (character === "'" || character === '"' || character === '`') {
+ quote = character;
+ state = 'string';
+ result += maskStrings ? ' ' : character;
+ } else {
+ result += character;
+ }
+ }
+
+ return result;
+}
+
+function importsPackage(source: string, packageName: string): boolean {
+ const escapedName = packageName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+ return new RegExp(
+ `(?:\\bfrom\\s*|\\brequire\\s*\\(\\s*)['"]${escapedName}['"]`
+ ).test(source);
+}
+
+function rawFederationPluginAliases(source: string): Set {
+ const aliases = new Set(['ModuleFederationPlugin']);
+ for (const match of source.matchAll(
+ /ModuleFederationPlugin\s*(?:as\s+|:\s*)([A-Za-z_$][\w$]*)/g
+ )) {
+ aliases.add(match[1] as string);
+ }
+ for (const match of source.matchAll(
+ /\bimport\s+([A-Za-z_$][\w$]*)\s+from\s*['"]@module-federation\/enhanced\/rspack['"]/g
+ )) {
+ aliases.add(match[1] as string);
+ }
+ return aliases;
+}
+
+function repackFederationPluginAliases(source: string): {
+ v1: Set;
+ v2: Set;
+} {
+ const aliases = {
+ v1: new Set(['ModuleFederationPlugin', 'ModuleFederationPluginV1']),
+ v2: new Set(['ModuleFederationPluginV2']),
+ };
+ for (const match of source.matchAll(
+ /(ModuleFederationPlugin(?:V[12])?)\s*(?:as\s+|:\s*)([A-Za-z_$][\w$]*)/g
+ )) {
+ const version = match[1] === 'ModuleFederationPluginV2' ? 'v2' : 'v1';
+ aliases[version].add(match[2] as string);
+ }
+ return aliases;
+}
+
+export function getRspackFederationUsage(
+ contents: string
+): RspackFederationUsage {
+ const source = maskJavaScriptNonCode(contents, false);
+ const executable = maskJavaScriptNonCode(contents, true);
+ const constructors = new Set(
+ [
+ ...executable.matchAll(
+ /\bnew\s+(?:[A-Za-z_$][\w$]*\s*\.\s*)*([A-Za-z_$][\w$]*)\s*\(/g
+ ),
+ ].map((match) => match[1] as string)
+ );
+ const importsRawPlugin = importsPackage(
+ source,
+ '@module-federation/enhanced/rspack'
+ );
+ if (
+ importsRawPlugin &&
+ [...rawFederationPluginAliases(source)].some((name) =>
+ constructors.has(name)
+ )
+ ) {
+ return 'raw';
+ }
+
+ if (!importsPackage(source, '@callstack/repack')) return 'none';
+ const repackAliases = repackFederationPluginAliases(source);
+ if ([...repackAliases.v1].some((name) => constructors.has(name))) {
+ return 'v1';
+ }
+ if ([...repackAliases.v2].some((name) => constructors.has(name))) {
+ return 'v2';
+ }
+ return 'none';
+}
+
+export function isRspackConfigCompatible(contents: string): boolean {
+ const federation = getRspackFederationUsage(contents);
+ return (
+ /@callstack\/repack-expo\/rspack/.test(contents) &&
+ /new\s+(?:\w+\.)?ExpoPlugin\s*\(/.test(contents) &&
+ !/new\s+(?:Repack\.)?RepackPlugin\s*\(/.test(contents) &&
+ federation !== 'raw' &&
+ federation !== 'v1'
+ );
+}
diff --git a/packages/repack-expo/src/cli/types.ts b/packages/repack-expo/src/cli/types.ts
new file mode 100644
index 000000000..4ae846892
--- /dev/null
+++ b/packages/repack-expo/src/cli/types.ts
@@ -0,0 +1,70 @@
+export type CliOptions = {
+ check: boolean;
+ dryRun: boolean;
+ force: boolean;
+ json: boolean;
+};
+
+export type DiagnosticSeverity = 'error' | 'warning' | 'info';
+
+export type Diagnostic = {
+ code: string;
+ message: string;
+ recovery?: string;
+ severity: DiagnosticSeverity;
+};
+
+export type InitOptions = Partial & {
+ projectRoot?: string;
+};
+
+export type FileChange = {
+ after: string;
+ before: string | null;
+ path: string;
+};
+
+export type PackageManager = 'npm' | 'yarn' | 'pnpm' | 'bun';
+export type JsonObject = Record;
+export type PackageJson = JsonObject & {
+ dependencies?: Record;
+ devDependencies?: Record;
+ expo?: JsonObject;
+ main?: string;
+ name?: string;
+ packageManager?: string;
+ scripts?: Record;
+ type?: string;
+};
+
+export type ConfigPluginOptions = {
+ configPath?: string;
+ entry?: string;
+};
+
+export type ConfigPluginRegistration = {
+ invalidReason?: string;
+ options: ConfigPluginOptions;
+ registered: boolean;
+};
+
+export type InitResult = {
+ changes: FileChange[];
+ changedFiles: string[];
+ diagnostics: Diagnostic[];
+ installCommand: string;
+ ok: boolean;
+ packageManager: PackageManager;
+ wrote: boolean;
+};
+
+export type DoctorOptions = {
+ projectRoot?: string;
+};
+
+export type DoctorResult = {
+ diagnostics: Diagnostic[];
+ errorCount: number;
+ ok: boolean;
+ warningCount: number;
+};
diff --git a/packages/repack-expo/src/config/ConfigPluginError.ts b/packages/repack-expo/src/config/ConfigPluginError.ts
new file mode 100644
index 000000000..7c3434c75
--- /dev/null
+++ b/packages/repack-expo/src/config/ConfigPluginError.ts
@@ -0,0 +1,22 @@
+export type ConfigPluginErrorCode =
+ | 'ACTIVE_EXPO_UPDATES'
+ | 'HERMES_REQUIRED'
+ | 'INCOMPATIBLE_NATIVE_TEMPLATE'
+ | 'INVALID_OPTIONS'
+ | 'NEW_ARCH_REQUIRED';
+
+export class ConfigPluginError extends Error {
+ readonly code: ConfigPluginErrorCode;
+ readonly recovery: string;
+
+ constructor(options: {
+ code: ConfigPluginErrorCode;
+ message: string;
+ recovery: string;
+ }) {
+ super(`${options.message}\n\nRecovery: ${options.recovery}`);
+ this.name = 'RepackExpoConfigPluginError';
+ this.code = options.code;
+ this.recovery = options.recovery;
+ }
+}
diff --git a/packages/repack-expo/src/config/android.ts b/packages/repack-expo/src/config/android.ts
new file mode 100644
index 000000000..b3527a0fe
--- /dev/null
+++ b/packages/repack-expo/src/config/android.ts
@@ -0,0 +1,117 @@
+import {
+ CodeGenerator,
+ type ConfigPlugin,
+ withAppBuildGradle,
+ withMainApplication,
+} from 'expo/config-plugins';
+import { ConfigPluginError } from './ConfigPluginError.js';
+import {
+ assertUniqueAnchor,
+ hasIntactGeneratedSection,
+ replaceLineWithGeneratedSection,
+} from './generated.js';
+import type { RepackExpoPluginOptions } from './options.js';
+
+const ANDROID_MAIN_MODULE_TAG = 'repack-expo-android-main-module';
+const ANDROID_BUNDLE_COMMAND_TAG = 'repack-expo-android-bundle-command';
+const EXPO_CLI_BLOCK = ` // Use Expo CLI to bundle the app, this ensures the Metro config
+ // works correctly with Expo projects.
+ cliFile = new File(["node", "--print", "require.resolve('@expo/cli', { paths: [require.resolve('expo/package.json')] })"].execute(null, rootDir).text.trim())
+ bundleCommand = "export:embed"`;
+const REPACK_CLI_BLOCK = ` cliFile = new File(["node", "--print", "require.resolve('@react-native-community/cli/build/bin.js')"].execute(null, rootDir).text.trim())
+ bundleCommand = "bundle"`;
+
+function androidProjectFile(value: string): string {
+ const path = value.startsWith('/') ? value : `../${value}`;
+ return `rootProject.file(${JSON.stringify(path)})`;
+}
+
+function createAndroidCliBlock(options: RepackExpoPluginOptions): string {
+ const lines = [REPACK_CLI_BLOCK];
+ if (options.entry) {
+ lines.push(` entryFile = ${androidProjectFile(options.entry)}`);
+ }
+ if (options.configPath) {
+ lines.push(` bundleConfig = ${androidProjectFile(options.configPath)}`);
+ }
+ return lines.join('\n');
+}
+
+export function configureAndroidMainApplication(
+ contents: string,
+ language: string
+): string {
+ if (language !== 'kt') {
+ throw new ConfigPluginError({
+ code: 'INCOMPATIBLE_NATIVE_TEMPLATE',
+ message: `Expected a Kotlin Expo MainApplication, received ${language}.`,
+ recovery:
+ 'Regenerate with a supported Expo SDK 56+ Kotlin template or report the native template for explicit support.',
+ });
+ }
+
+ assertUniqueAnchor(
+ contents,
+ ' ExpoReactHostFactory.getDefaultReactHost(',
+ 'ExpoReactHostFactory call'
+ );
+ if (
+ hasIntactGeneratedSection({
+ comment: ' //',
+ contents,
+ newSrc: ' jsMainModulePath = "index",',
+ tag: ANDROID_MAIN_MODULE_TAG,
+ })
+ ) {
+ return contents;
+ }
+
+ assertUniqueAnchor(
+ contents,
+ ' packageList =',
+ 'ExpoReactHostFactory packageList argument'
+ );
+ return CodeGenerator.mergeContents({
+ anchor: /^ {6}packageList =/,
+ comment: ' //',
+ newSrc: ' jsMainModulePath = "index",',
+ offset: 0,
+ src: contents,
+ tag: ANDROID_MAIN_MODULE_TAG,
+ }).contents;
+}
+
+export function configureAndroidBuildGradle(
+ contents: string,
+ options: RepackExpoPluginOptions = {}
+): string {
+ return replaceLineWithGeneratedSection({
+ anchorLine: EXPO_CLI_BLOCK,
+ comment: ' //',
+ contents,
+ replacementLine: createAndroidCliBlock(options),
+ tag: ANDROID_BUNDLE_COMMAND_TAG,
+ });
+}
+
+export const withAndroidRepack: ConfigPlugin = (
+ config,
+ options = {}
+) => {
+ const pluginOptions = options || {};
+ const withApplication = withMainApplication(config, (modConfig) => {
+ modConfig.modResults.contents = configureAndroidMainApplication(
+ modConfig.modResults.contents,
+ modConfig.modResults.language
+ );
+ return modConfig;
+ });
+
+ return withAppBuildGradle(withApplication, (modConfig) => {
+ modConfig.modResults.contents = configureAndroidBuildGradle(
+ modConfig.modResults.contents,
+ pluginOptions
+ );
+ return modConfig;
+ });
+};
diff --git a/packages/repack-expo/src/config/generated.ts b/packages/repack-expo/src/config/generated.ts
new file mode 100644
index 000000000..e3c7b5a31
--- /dev/null
+++ b/packages/repack-expo/src/config/generated.ts
@@ -0,0 +1,94 @@
+import { CodeGenerator } from 'expo/config-plugins';
+import { ConfigPluginError } from './ConfigPluginError.js';
+
+type GeneratedSectionOptions = {
+ comment: string;
+ contents: string;
+ newSrc: string;
+ tag: string;
+};
+
+export function assertUniqueAnchor(
+ contents: string,
+ anchor: string | RegExp,
+ description: string
+): void {
+ const matches =
+ typeof anchor === 'string'
+ ? contents.split(anchor).length - 1
+ : [...contents.matchAll(new RegExp(anchor, 'g'))].length;
+
+ if (matches !== 1) {
+ throw new ConfigPluginError({
+ code: 'INCOMPATIBLE_NATIVE_TEMPLATE',
+ message: `Expected exactly one ${description}, found ${matches}.`,
+ recovery:
+ 'Regenerate the native project with expo prebuild --clean. If the generated shape still differs, report it for explicit support.',
+ });
+ }
+}
+
+export function hasIntactGeneratedSection(
+ options: GeneratedSectionOptions
+): boolean {
+ const { comment, contents, newSrc, tag } = options;
+ const header = CodeGenerator.createGeneratedHeaderComment(
+ newSrc,
+ tag,
+ comment
+ );
+ const begin = `@generated begin ${tag}`;
+ const end = `${comment} @generated end ${tag}`;
+ const expectedSection = [header, newSrc, end].join('\n');
+
+ if (contents.includes(expectedSection)) {
+ assertUniqueAnchor(contents, begin, `${tag} generated section`);
+ assertUniqueAnchor(contents, end, `${tag} generated section end`);
+ assertUniqueAnchor(contents, expectedSection, `${tag} generated contents`);
+ return true;
+ }
+
+ if (contents.includes(begin) || contents.includes(end)) {
+ throw new ConfigPluginError({
+ code: 'INCOMPATIBLE_NATIVE_TEMPLATE',
+ message: `The generated ${tag} section was modified manually.`,
+ recovery:
+ 'Restore the generated section or run expo prebuild --clean before applying the plugin again.',
+ });
+ }
+
+ return false;
+}
+
+export function replaceLineWithGeneratedSection(options: {
+ anchorLine: string;
+ comment: string;
+ contents: string;
+ replacementLine: string;
+ tag: string;
+}): string {
+ const { anchorLine, comment, contents, replacementLine, tag } = options;
+ const header = CodeGenerator.createGeneratedHeaderComment(
+ replacementLine,
+ tag,
+ comment
+ );
+ const end = `${comment} @generated end ${tag}`;
+
+ if (
+ hasIntactGeneratedSection({
+ comment,
+ contents,
+ newSrc: replacementLine,
+ tag,
+ })
+ ) {
+ return contents;
+ }
+
+ assertUniqueAnchor(contents, anchorLine, `${tag} native anchor`);
+ return contents.replace(
+ anchorLine,
+ [header, replacementLine, end].join('\n')
+ );
+}
diff --git a/packages/repack-expo/src/config/ios.ts b/packages/repack-expo/src/config/ios.ts
new file mode 100644
index 000000000..3a7aff2e9
--- /dev/null
+++ b/packages/repack-expo/src/config/ios.ts
@@ -0,0 +1,251 @@
+import {
+ CodeGenerator,
+ type ConfigPlugin,
+ IOSConfig,
+ withAppDelegate,
+ withXcodeProject,
+} from 'expo/config-plugins';
+import { ConfigPluginError } from './ConfigPluginError.js';
+import {
+ assertUniqueAnchor,
+ hasIntactGeneratedSection,
+ replaceLineWithGeneratedSection,
+} from './generated.js';
+import type { RepackExpoPluginOptions } from './options.js';
+
+const IOS_BUNDLE_URL_TAG = 'repack-expo-ios-bundle-url';
+const IOS_BUNDLE_COMMAND_TAG = 'repack-expo-ios-bundle-command';
+const EXPO_DEBUG_URL =
+ ' return RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: ".expo/.virtual-metro-entry")';
+const REPACK_DEBUG_URL =
+ ' return RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index")';
+const EXPO_CLI_DEFAULTS = `if [[ -z "$CLI_PATH" ]]; then
+ # Use Expo CLI
+ export CLI_PATH="$("$NODE_BINARY" --print "require.resolve('@expo/cli', { paths: [require.resolve('expo/package.json')] })")"
+fi
+if [[ -z "$BUNDLE_COMMAND" ]]; then
+ # Default Expo CLI command for bundling
+ export BUNDLE_COMMAND="export:embed"
+fi`;
+const XCODE_BUNDLE_SCRIPT_ANCHOR = /react-native-xcode\.sh/;
+
+function escapeShellDoubleQuoted(value: string): string {
+ return value
+ .replaceAll('\\', '\\\\')
+ .replaceAll('"', '\\"')
+ .replaceAll('$', '\\$')
+ .replaceAll('`', '\\`');
+}
+
+function iosProjectPath(value: string): string {
+ const escaped = escapeShellDoubleQuoted(value);
+ return value.startsWith('/') ? escaped : `$PROJECT_ROOT/${escaped}`;
+}
+
+function createIosBundleOverrides(options: RepackExpoPluginOptions): string {
+ const lines = [
+ 'export CLI_PATH="$("$NODE_BINARY" --print "require.resolve(\'@react-native-community/cli/build/bin.js\')")"',
+ 'export BUNDLE_COMMAND="bundle"',
+ ];
+ if (options.entry) {
+ lines.push(`export ENTRY_FILE="${iosProjectPath(options.entry)}"`);
+ }
+ if (options.configPath) {
+ lines.push(`export BUNDLE_CONFIG="${iosProjectPath(options.configPath)}"`);
+ }
+ return lines.join('\n');
+}
+
+export function configureIosAppDelegate(
+ contents: string,
+ language: string
+): string {
+ if (language !== 'swift') {
+ throw new ConfigPluginError({
+ code: 'INCOMPATIBLE_NATIVE_TEMPLATE',
+ message: `Expected a Swift Expo AppDelegate, received ${language}.`,
+ recovery:
+ 'Regenerate with a supported Expo SDK 56+ Swift template or report the native template for explicit support.',
+ });
+ }
+
+ for (const anchor of [
+ 'class ReactNativeDelegate: ExpoReactNativeFactoryDelegate',
+ ' override func bundleURL() -> URL? {',
+ ' return Bundle.main.url(forResource: "main", withExtension: "jsbundle")',
+ ]) {
+ assertUniqueAnchor(contents, anchor, 'Expo AppDelegate shape');
+ }
+
+ return replaceLineWithGeneratedSection({
+ anchorLine: EXPO_DEBUG_URL,
+ comment: ' //',
+ contents,
+ replacementLine: REPACK_DEBUG_URL,
+ tag: IOS_BUNDLE_URL_TAG,
+ });
+}
+
+export function configureIosBundleScript(
+ contents: string,
+ options: RepackExpoPluginOptions = {}
+): string {
+ assertUniqueAnchor(
+ contents,
+ EXPO_CLI_DEFAULTS,
+ 'Expo iOS CLI bundle defaults'
+ );
+ assertUniqueAnchor(
+ contents,
+ XCODE_BUNDLE_SCRIPT_ANCHOR,
+ 'react-native-xcode.sh invocation'
+ );
+
+ const newSrc = createIosBundleOverrides(options);
+ if (
+ hasIntactGeneratedSection({
+ comment: '#',
+ contents,
+ newSrc,
+ tag: IOS_BUNDLE_COMMAND_TAG,
+ })
+ ) {
+ return contents;
+ }
+
+ return CodeGenerator.mergeContents({
+ anchor: XCODE_BUNDLE_SCRIPT_ANCHOR,
+ comment: '#',
+ newSrc,
+ offset: 0,
+ src: contents,
+ tag: IOS_BUNDLE_COMMAND_TAG,
+ }).contents;
+}
+
+type XcodeProject = Parameters<
+ typeof IOSConfig.XcodeUtils.getApplicationNativeTarget
+>[0]['project'];
+
+type XcodeShellScriptPhase = {
+ shellScript?: unknown;
+};
+
+export function findIosBundlePhase(
+ project: XcodeProject,
+ projectName: string
+): XcodeShellScriptPhase {
+ try {
+ const target = IOSConfig.XcodeUtils.getApplicationNativeTarget({
+ project,
+ projectName,
+ });
+ const buildPhases: unknown = target.target.buildPhases;
+ if (!Array.isArray(buildPhases)) {
+ throw new Error('Application target buildPhases is not an array');
+ }
+
+ const phaseReferences = buildPhases.filter(
+ (phase): phase is { comment?: string; value: string } =>
+ typeof phase === 'object' &&
+ phase !== null &&
+ 'value' in phase &&
+ typeof phase.value === 'string' &&
+ phase.comment === 'Bundle React Native code and images'
+ );
+ if (phaseReferences.length !== 1) {
+ throw new ConfigPluginError({
+ code: 'INCOMPATIBLE_NATIVE_TEMPLATE',
+ message: `Expected one React Native iOS bundle phase, found ${phaseReferences.length}.`,
+ recovery:
+ 'Regenerate the iOS project with expo prebuild --clean. Custom bundle phases need explicit support.',
+ });
+ }
+
+ const phaseSection: unknown =
+ project.hash.project.objects.PBXShellScriptBuildPhase;
+ if (
+ !phaseSection ||
+ typeof phaseSection !== 'object' ||
+ Array.isArray(phaseSection)
+ ) {
+ throw new Error('PBXShellScriptBuildPhase section is missing');
+ }
+ const phase: unknown = (phaseSection as Record)[
+ phaseReferences[0]!.value
+ ];
+ if (!phase || typeof phase !== 'object' || Array.isArray(phase)) {
+ throw new ConfigPluginError({
+ code: 'INCOMPATIBLE_NATIVE_TEMPLATE',
+ message: 'The referenced React Native iOS bundle phase is missing.',
+ recovery: 'Regenerate the iOS project with expo prebuild --clean.',
+ });
+ }
+ return phase as XcodeShellScriptPhase;
+ } catch (error) {
+ if (error instanceof ConfigPluginError) throw error;
+
+ throw new ConfigPluginError({
+ code: 'INCOMPATIBLE_NATIVE_TEMPLATE',
+ message: `Could not inspect the ${projectName} iOS application target or its build phases.`,
+ recovery:
+ 'Regenerate the iOS project with expo prebuild --clean. If the target still cannot be inspected, report the native template for explicit support.',
+ });
+ }
+}
+
+function decodeShellScript(shellScript: unknown): string {
+ if (typeof shellScript !== 'string') {
+ throw new ConfigPluginError({
+ code: 'INCOMPATIBLE_NATIVE_TEMPLATE',
+ message: 'The iOS React Native bundle phase has no shell script.',
+ recovery: 'Regenerate the iOS project with expo prebuild --clean.',
+ });
+ }
+
+ try {
+ const decoded: unknown = JSON.parse(shellScript);
+ if (typeof decoded === 'string') return decoded;
+ } catch {}
+
+ throw new ConfigPluginError({
+ code: 'INCOMPATIBLE_NATIVE_TEMPLATE',
+ message: 'The iOS React Native bundle phase shell script is malformed.',
+ recovery: 'Regenerate the iOS project with expo prebuild --clean.',
+ });
+}
+
+export const withIosRepack: ConfigPlugin = (
+ config,
+ options = {}
+) => {
+ const pluginOptions = options || {};
+ const withDelegate = withAppDelegate(config, (modConfig) => {
+ modConfig.modResults.contents = configureIosAppDelegate(
+ modConfig.modResults.contents,
+ modConfig.modResults.language
+ );
+ return modConfig;
+ });
+
+ return withXcodeProject(withDelegate, (modConfig) => {
+ const projectName = modConfig.modRequest.projectName;
+ if (!projectName) {
+ throw new ConfigPluginError({
+ code: 'INCOMPATIBLE_NATIVE_TEMPLATE',
+ message: 'Expo did not provide an iOS application target name.',
+ recovery: 'Regenerate the iOS project with expo prebuild --clean.',
+ });
+ }
+
+ const phase = findIosBundlePhase(modConfig.modResults, projectName);
+
+ phase.shellScript = JSON.stringify(
+ configureIosBundleScript(
+ decodeShellScript(phase.shellScript),
+ pluginOptions
+ )
+ );
+ return modConfig;
+ });
+};
diff --git a/packages/repack-expo/src/config/options.ts b/packages/repack-expo/src/config/options.ts
new file mode 100644
index 000000000..0748d3199
--- /dev/null
+++ b/packages/repack-expo/src/config/options.ts
@@ -0,0 +1,44 @@
+import { ConfigPluginError } from './ConfigPluginError.js';
+
+export type RepackExpoPluginOptions = {
+ /** Rspack configuration path passed to native release bundle commands. */
+ configPath?: string;
+ /** Application entry passed to native release bundle commands. */
+ entry?: string;
+};
+
+export function normalizeConfigPluginOptions(
+ options: RepackExpoPluginOptions | void
+): RepackExpoPluginOptions {
+ if (options === undefined) return {};
+ if (
+ options === null ||
+ typeof options !== 'object' ||
+ Array.isArray(options)
+ ) {
+ throw new ConfigPluginError({
+ code: 'INVALID_OPTIONS',
+ message: 'The Re.Pack Expo Config Plugin options must be an object.',
+ recovery:
+ 'Pass an options object such as { configPath: "rspack.config.mjs" } or remove the options value.',
+ });
+ }
+
+ for (const name of ['configPath', 'entry'] as const) {
+ const value = options[name];
+ if (
+ value !== undefined &&
+ (typeof value !== 'string' ||
+ value.length === 0 ||
+ /[\r\n\0]/.test(value))
+ ) {
+ throw new ConfigPluginError({
+ code: 'INVALID_OPTIONS',
+ message: `The Re.Pack Expo Config Plugin ${name} option must be a non-empty single-line string.`,
+ recovery: `Set ${name} to a valid project-relative or absolute value, or remove the option.`,
+ });
+ }
+ }
+
+ return { configPath: options.configPath, entry: options.entry };
+}
diff --git a/packages/repack-expo/src/config/validateExpoConfig.ts b/packages/repack-expo/src/config/validateExpoConfig.ts
new file mode 100644
index 000000000..cbc1f37a7
--- /dev/null
+++ b/packages/repack-expo/src/config/validateExpoConfig.ts
@@ -0,0 +1,45 @@
+import type { ConfigPlugin } from 'expo/config-plugins';
+import { ConfigPluginError } from './ConfigPluginError.js';
+
+type BaseExpoConfig = Parameters[0];
+type ExpoConfig = BaseExpoConfig & {
+ android?: BaseExpoConfig['android'] & { jsEngine?: string };
+ ios?: BaseExpoConfig['ios'] & { jsEngine?: string };
+ jsEngine?: string;
+ newArchEnabled?: boolean;
+};
+
+export function validateExpoConfig(config: ExpoConfig): void {
+ if (config.newArchEnabled === false) {
+ throw new ConfigPluginError({
+ code: 'NEW_ARCH_REQUIRED',
+ message: '@callstack/repack-expo requires React Native New Architecture.',
+ recovery:
+ 'Remove the expo.newArchEnabled=false override and run expo prebuild again.',
+ });
+ }
+
+ const configuredEngines = [
+ config.jsEngine,
+ config.ios?.jsEngine,
+ config.android?.jsEngine,
+ ].filter((engine): engine is string => engine !== undefined);
+ if (configuredEngines.some((engine) => engine !== 'hermes')) {
+ throw new ConfigPluginError({
+ code: 'HERMES_REQUIRED',
+ message: '@callstack/repack-expo v1 supports Hermes only.',
+ recovery:
+ 'Remove the JSC override or set expo.jsEngine and platform overrides to hermes.',
+ });
+ }
+
+ if (config.updates && config.updates.enabled !== false) {
+ throw new ConfigPluginError({
+ code: 'ACTIVE_EXPO_UPDATES',
+ message:
+ '@callstack/repack-expo v1 cannot coexist with active Expo Updates.',
+ recovery:
+ 'Set expo.updates.enabled to false or remove expo-updates before running prebuild.',
+ });
+ }
+}
diff --git a/packages/repack-expo/src/index.ts b/packages/repack-expo/src/index.ts
new file mode 100644
index 000000000..c119049ca
--- /dev/null
+++ b/packages/repack-expo/src/index.ts
@@ -0,0 +1,22 @@
+import { type ConfigPlugin, createRunOncePlugin } from 'expo/config-plugins';
+import { withAndroidRepack } from './config/android.js';
+import { withIosRepack } from './config/ios.js';
+import {
+ type RepackExpoPluginOptions,
+ normalizeConfigPluginOptions,
+} from './config/options.js';
+import { validateExpoConfig } from './config/validateExpoConfig.js';
+
+const withRepackExpo: ConfigPlugin = (
+ config,
+ options
+) => {
+ validateExpoConfig(config);
+ const normalizedOptions = normalizeConfigPluginOptions(options);
+ return withAndroidRepack(
+ withIosRepack(config, normalizedOptions),
+ normalizedOptions
+ );
+};
+
+export = createRunOncePlugin(withRepackExpo, '@callstack/repack-expo', '0.0.0');
diff --git a/packages/repack-expo/src/rspack/ExpoPlugin.ts b/packages/repack-expo/src/rspack/ExpoPlugin.ts
new file mode 100644
index 000000000..8218010fd
--- /dev/null
+++ b/packages/repack-expo/src/rspack/ExpoPlugin.ts
@@ -0,0 +1,186 @@
+import { RepackPlugin, type RepackPluginConfig } from '@callstack/repack';
+import { ExpoModulesPlugin as RepackExpoModulesPlugin } from '@callstack/repack-plugin-expo-modules';
+import type { Compiler } from '@rspack/core';
+import { ExpoPluginError } from './ExpoPluginError.js';
+import { configureAndroidFontAssets } from './assets/configureAndroidFontAssets.js';
+import { configureExpoAssets } from './assets/configureExpoAssets.js';
+import { configureExpoBabelLoaders } from './babel/configureExpoBabelLoaders.js';
+import { createExpoBabelCaller } from './babel/createExpoBabelCaller.js';
+import { configureExpoCompilerEntry } from './entry/configureExpoCompilerEntry.js';
+import {
+ type ExpoNativePlatform,
+ resolveExpoEntry,
+} from './entry/resolveExpoEntry.js';
+import { configureExpoPublicEnvironment } from './environment/configureExpoPublicEnvironment.js';
+import { configureExpoChunkFilename } from './output/configureExpoChunkFilename.js';
+import { configureExpoModuleResolution } from './resolve/configureExpoModuleResolution.js';
+import { configureExpoResolveOptions } from './resolve/configureExpoResolveOptions.js';
+import { configureReactNativeResolution } from './resolve/configureReactNativeResolution.js';
+import { configureExpoRouterEntry } from './router/configureExpoRouterEntry.js';
+import { resolveRouterRoot } from './router/resolveRouterRoot.js';
+import { applyRepackSourceMapFix } from './source-map/applyRepackSourceMapFix.js';
+
+export type ExpoPluginOptions = {
+ /** Override package.json#main for this compilation. */
+ entry?: string;
+ /** Target native platform. Defaults to the Rspack configuration name. */
+ platform?: ExpoNativePlatform;
+ /** Expo application root. Defaults to the Rspack context. */
+ projectRoot?: string;
+ /** Configure the RepackPlugin instance owned by ExpoPlugin. */
+ repack?: RepackPluginConfig;
+ /** Expo Router application directory. Defaults to src/app when present, then app. */
+ routerRoot?: string;
+};
+
+function hasPlugin(compiler: Compiler, name: string): boolean {
+ return (compiler.options.plugins ?? []).some(
+ (plugin) =>
+ typeof plugin === 'object' &&
+ plugin !== null &&
+ plugin.constructor?.name === name
+ );
+}
+
+function resolvePlatform(
+ compiler: Compiler,
+ configuredPlatform?: ExpoNativePlatform
+): ExpoNativePlatform {
+ const platform = configuredPlatform ?? compiler.options.name;
+ if (platform !== 'ios' && platform !== 'android') {
+ throw new ExpoPluginError({
+ code: 'UNSUPPORTED_PLATFORM',
+ message: `ExpoPlugin requires an ios or android Rspack compilation, received ${String(platform)}.`,
+ recovery:
+ 'Set the Rspack configuration name or ExpoPlugin.platform to ios or android.',
+ });
+ }
+ return platform;
+}
+
+function assertRspack(compiler: Compiler): void {
+ if (!('rspackVersion' in compiler.webpack)) {
+ throw new ExpoPluginError({
+ code: 'RSPACK_REQUIRED',
+ message: 'ExpoPlugin v1 supports Rspack only.',
+ recovery: 'Use Re.Pack with @rspack/core instead of Webpack.',
+ });
+ }
+}
+
+function assertNativeClient(compiler: Compiler): void {
+ const presets = compiler.options.externalsPresets;
+ if (presets.node || presets.electron || presets.electronMain) {
+ throw new ExpoPluginError({
+ code: 'UNSUPPORTED_ENVIRONMENT',
+ message: 'ExpoPlugin v1 supports native client compilations only.',
+ recovery:
+ 'Remove the Node/Electron target and configure a separate Rspack build for server or RSC code.',
+ });
+ }
+}
+
+class ExpoModulesPlugin {
+ constructor(private options: ExpoPluginOptions = {}) {}
+
+ private createRepackPlugin(platform: ExpoNativePlatform): RepackPlugin {
+ const repack = this.options.repack ?? {};
+ if (repack.platform !== undefined && repack.platform !== platform) {
+ throw new ExpoPluginError({
+ code: 'REPACK_PLATFORM_CONFLICT',
+ message: `ExpoPlugin resolved platform ${platform}, but repack.platform is ${repack.platform}.`,
+ recovery:
+ 'Remove repack.platform or set it to the same platform as ExpoPlugin.',
+ });
+ }
+
+ return new RepackPlugin({
+ ...repack,
+ extraChunks: repack.extraChunks ?? [{ type: 'local' }],
+ platform,
+ });
+ }
+
+ apply(compiler: Compiler): void {
+ assertRspack(compiler);
+ assertNativeClient(compiler);
+
+ if (hasPlugin(compiler, 'RepackPlugin')) {
+ throw new ExpoPluginError({
+ code: 'REPACK_PLUGIN_CONFLICT',
+ message:
+ 'ExpoPlugin configures RepackPlugin internally and cannot be combined with a separate RepackPlugin.',
+ recovery:
+ 'Remove RepackPlugin from the Expo Rspack configuration. Configure Expo through ExpoPlugin only.',
+ });
+ }
+
+ const platform = resolvePlatform(compiler, this.options.platform);
+ const resolvedEntry = resolveExpoEntry({
+ entry: this.options.entry,
+ from: compiler.context,
+ platform,
+ projectRoot: this.options.projectRoot,
+ });
+ const routerRoot = resolveRouterRoot(
+ resolvedEntry.projectRoot,
+ this.options.routerRoot
+ );
+ const publicEnvironment = configureExpoPublicEnvironment(
+ compiler,
+ resolvedEntry.projectRoot
+ );
+ const caller = createExpoBabelCaller({
+ isDev: compiler.options.mode === 'development',
+ platform,
+ projectRoot: resolvedEntry.projectRoot,
+ routerRoot,
+ });
+ configureExpoBabelLoaders(
+ compiler.options.module.rules,
+ caller,
+ {
+ babel: require.resolve('./loaders/expoBabelLoader.js'),
+ },
+ publicEnvironment
+ );
+
+ configureExpoAssets(
+ compiler.options.module.rules,
+ require.resolve('@callstack/repack/assets-loader'),
+ platform
+ );
+ configureAndroidFontAssets(compiler, platform);
+ configureExpoRouterEntry(
+ compiler.options.module.rules,
+ resolvedEntry,
+ require.resolve('./loaders/expoRouterEntryLoader.js')
+ );
+
+ configureExpoResolveOptions(compiler.options.resolve, platform);
+ compiler.options.resolve.modules = configureExpoModuleResolution(
+ compiler.options.resolve.modules,
+ resolvedEntry.projectRoot
+ );
+ compiler.options.resolve.alias = configureReactNativeResolution(
+ compiler.options.resolve.alias,
+ resolvedEntry.projectRoot
+ );
+
+ configureExpoChunkFilename(compiler.options.output);
+
+ compiler.options.entry = configureExpoCompilerEntry(
+ compiler.options.entry,
+ resolvedEntry.entryPath,
+ require.resolve('./runtime/configureScriptManager.js')
+ );
+
+ applyRepackSourceMapFix(compiler, this.createRepackPlugin(platform));
+ new RepackExpoModulesPlugin({ platform }).apply(compiler);
+ }
+}
+
+// Re.Pack's CLI detects required integration plugins by constructor name.
+// Exporting the enhanced ExpoModulesPlugin under the public ExpoPlugin name
+// keeps that validation accurate without changing Re.Pack's core packages.
+export { ExpoModulesPlugin as ExpoPlugin };
diff --git a/packages/repack-expo/src/rspack/ExpoPluginError.ts b/packages/repack-expo/src/rspack/ExpoPluginError.ts
new file mode 100644
index 000000000..dcc0f9459
--- /dev/null
+++ b/packages/repack-expo/src/rspack/ExpoPluginError.ts
@@ -0,0 +1,29 @@
+export type ExpoPluginErrorCode =
+ | 'AMBIGUOUS_ENTRY'
+ | 'DYNAMIC_ENTRY_UNSUPPORTED'
+ | 'INVALID_CHUNK_FILENAME'
+ | 'INVALID_ROUTER_ROOT'
+ | 'REPACK_PLATFORM_CONFLICT'
+ | 'REPACK_PLUGIN_CONFLICT'
+ | 'REACT_NATIVE_RESOLUTION_FAILED'
+ | 'RSPACK_REQUIRED'
+ | 'UNSUPPORTED_ENVIRONMENT'
+ | 'UNSUPPORTED_PLATFORM';
+
+export type ExpoPluginErrorOptions = {
+ code: ExpoPluginErrorCode;
+ message: string;
+ recovery: string;
+};
+
+export class ExpoPluginError extends Error {
+ readonly code: ExpoPluginErrorCode;
+ readonly recovery: string;
+
+ constructor(options: ExpoPluginErrorOptions) {
+ super(options.message);
+ this.name = 'ExpoPluginError';
+ this.code = options.code;
+ this.recovery = options.recovery;
+ }
+}
diff --git a/packages/repack-expo/src/rspack/assets/configureAndroidFontAssets.ts b/packages/repack-expo/src/rspack/assets/configureAndroidFontAssets.ts
new file mode 100644
index 000000000..8b199682d
--- /dev/null
+++ b/packages/repack-expo/src/rspack/assets/configureAndroidFontAssets.ts
@@ -0,0 +1,35 @@
+import type { Compiler } from '@rspack/core';
+
+const pluginName = 'RepackExpoAndroidFontAssetsPlugin';
+const androidBinaryFontAsset = /^font\/(.+\.(?:otf|ttc|ttf))$/i;
+
+export function configureAndroidFontAssets(
+ compiler: Compiler,
+ platform: 'android' | 'ios'
+): void {
+ if (platform !== 'android' || compiler.options.mode !== 'production') {
+ return;
+ }
+
+ // A real Rspack compiler always exposes this hook. Keeping configuration
+ // probes hook-safe lets tooling inspect normalized options without starting
+ // a compilation.
+ if (!compiler.hooks?.thisCompilation) return;
+
+ compiler.hooks.thisCompilation.tap(pluginName, (compilation) => {
+ compilation.hooks.processAssets.tap(
+ {
+ name: pluginName,
+ stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_SUMMARIZE,
+ },
+ () => {
+ for (const asset of compilation.getAssets()) {
+ const match = androidBinaryFontAsset.exec(asset.name);
+ if (!match) continue;
+
+ compilation.renameAsset(asset.name, `raw/${match[1]}`);
+ }
+ }
+ );
+ });
+}
diff --git a/packages/repack-expo/src/rspack/assets/configureExpoAssets.ts b/packages/repack-expo/src/rspack/assets/configureExpoAssets.ts
new file mode 100644
index 000000000..b6b845a7d
--- /dev/null
+++ b/packages/repack-expo/src/rspack/assets/configureExpoAssets.ts
@@ -0,0 +1,110 @@
+import { ASSET_EXTENSIONS, getAssetExtensionsRegExp } from '@callstack/repack';
+
+type LoaderOptions = Record;
+type LoaderUse = string | { loader?: string; options?: LoaderOptions };
+type Rule = {
+ loader?: string;
+ oneOf?: Rule[];
+ options?: LoaderOptions;
+ rules?: Rule[];
+ test?: unknown;
+ use?: LoaderUse | LoaderUse[];
+};
+
+const REPACK_ASSETS_LOADER =
+ /(?:^|[/\\])(?:assets-loader|assetsLoader)(?:[/\\]|$)/;
+
+function testMatchesFilename(test: unknown, filename: string): boolean {
+ if (test instanceof RegExp) {
+ const lastIndex = test.lastIndex;
+ test.lastIndex = 0;
+ const matches = test.test(filename);
+ test.lastIndex = lastIndex;
+ return matches;
+ }
+ if (Array.isArray(test)) {
+ return test.some((condition) => testMatchesFilename(condition, filename));
+ }
+ return typeof test === 'string' && filename.endsWith(test);
+}
+
+function hasRuleForFilename(rules: unknown[], filename: string): boolean {
+ return rules.some((value) => {
+ if (typeof value !== 'object' || value === null) return false;
+ const rule = value as Rule;
+ return (
+ testMatchesFilename(rule.test, filename) ||
+ hasRuleForFilename(
+ [...(rule.oneOf ?? []), ...(rule.rules ?? [])],
+ filename
+ )
+ );
+ });
+}
+
+function withPlatform(use: LoaderUse, platform: 'android' | 'ios'): LoaderUse {
+ const loader = typeof use === 'string' ? use : use.loader;
+ if (!loader || !REPACK_ASSETS_LOADER.test(loader)) return use;
+
+ return {
+ ...(typeof use === 'string' ? {} : use),
+ loader,
+ options: {
+ ...(typeof use === 'string' ? {} : use.options),
+ platform,
+ },
+ };
+}
+
+function configureRulePlatform(rule: Rule, platform: 'android' | 'ios'): void {
+ if (rule.loader && REPACK_ASSETS_LOADER.test(rule.loader)) {
+ rule.options = { ...rule.options, platform };
+ }
+
+ if (rule.use) {
+ const uses = Array.isArray(rule.use) ? rule.use : [rule.use];
+ const configuredUses = uses.map((use) => withPlatform(use, platform));
+ rule.use = Array.isArray(rule.use) ? configuredUses : configuredUses[0];
+ }
+
+ for (const child of [...(rule.oneOf ?? []), ...(rule.rules ?? [])]) {
+ configureRulePlatform(child, platform);
+ }
+}
+
+export function configureExpoAssets(
+ rules: unknown[],
+ assetsLoaderPath: string,
+ platform: 'android' | 'ios'
+): void {
+ for (const rule of rules) {
+ if (typeof rule === 'object' && rule !== null) {
+ configureRulePlatform(rule as Rule, platform);
+ }
+ }
+
+ const missingExtensions = ASSET_EXTENSIONS.filter(
+ (extension) => !hasRuleForFilename(rules, `asset.${extension}`)
+ );
+ if (missingExtensions.length > 0) {
+ rules.push({
+ test: getAssetExtensionsRegExp(missingExtensions),
+ type: 'javascript/auto',
+ use: {
+ loader: assetsLoaderPath,
+ options: { platform },
+ },
+ });
+ }
+
+ if (!hasRuleForFilename(rules, 'asset.xml')) {
+ rules.push({
+ test: /\.xml$/,
+ type: 'javascript/auto',
+ use: {
+ loader: assetsLoaderPath,
+ options: { platform },
+ },
+ });
+ }
+}
diff --git a/packages/repack-expo/src/rspack/babel/configureExpoBabelLoaders.ts b/packages/repack-expo/src/rspack/babel/configureExpoBabelLoaders.ts
new file mode 100644
index 000000000..408930d14
--- /dev/null
+++ b/packages/repack-expo/src/rspack/babel/configureExpoBabelLoaders.ts
@@ -0,0 +1,153 @@
+import type { ExpoPublicEnvironmentSnapshot } from '../environment/expoPublicEnvironment.js';
+import type { ExpoBabelCaller } from './createExpoBabelCaller.js';
+
+type LoaderOptions = Record;
+type LoaderUse = string | { loader?: string; options?: LoaderOptions };
+type Rule = {
+ loader?: string;
+ oneOf?: Rule[];
+ options?: LoaderOptions;
+ rules?: Rule[];
+ use?: LoaderUse | LoaderUse[];
+};
+
+const REPACK_BABEL_LOADER =
+ /(?:^|[/\\])(?:babel-loader|babelLoader)(?:[/\\]|$)/;
+
+function mergeCaller(
+ current: unknown,
+ caller: ExpoBabelCaller
+): Record {
+ return {
+ ...(typeof current === 'object' && current !== null ? current : {}),
+ ...caller,
+ };
+}
+
+function configureLoaderOptions(
+ loader: string,
+ options: LoaderOptions | undefined,
+ caller: ExpoBabelCaller,
+ publicEnvironment: ExpoPublicEnvironmentSnapshot
+): LoaderOptions | undefined {
+ if (REPACK_BABEL_LOADER.test(loader)) {
+ return {
+ ...options,
+ caller: mergeCaller(options?.caller, caller),
+ expoPublicEnvironment: publicEnvironment,
+ };
+ }
+
+ return undefined;
+}
+
+type ExpoLoaderPaths = {
+ babel: string;
+};
+
+function getExpoLoaderPath(
+ loader: string,
+ paths: ExpoLoaderPaths
+): string | undefined {
+ if (REPACK_BABEL_LOADER.test(loader)) return paths.babel;
+ return undefined;
+}
+
+function configureUse(
+ use: LoaderUse,
+ caller: ExpoBabelCaller,
+ paths: ExpoLoaderPaths,
+ publicEnvironment: ExpoPublicEnvironmentSnapshot
+): LoaderUse {
+ const loader = typeof use === 'string' ? use : use.loader;
+ if (!loader) return use;
+
+ const expoLoader = getExpoLoaderPath(loader, paths);
+ const options = configureLoaderOptions(
+ loader,
+ typeof use === 'string' ? undefined : use.options,
+ caller,
+ publicEnvironment
+ );
+ if (!expoLoader || !options) return use;
+
+ return {
+ ...(typeof use === 'string' ? {} : use),
+ loader: expoLoader,
+ options,
+ };
+}
+
+function configureRule(
+ rule: Rule,
+ caller: ExpoBabelCaller,
+ paths: ExpoLoaderPaths,
+ publicEnvironment: ExpoPublicEnvironmentSnapshot
+): number {
+ let configured = 0;
+
+ if (rule.loader) {
+ const options = configureLoaderOptions(
+ rule.loader,
+ rule.options,
+ caller,
+ publicEnvironment
+ );
+ const expoLoader = getExpoLoaderPath(rule.loader, paths);
+ if (options && expoLoader) {
+ rule.loader = expoLoader;
+ rule.options = options;
+ configured += 1;
+ }
+ }
+
+ if (rule.use) {
+ const uses = Array.isArray(rule.use) ? rule.use : [rule.use];
+ const configuredUses = uses.map((use) =>
+ configureUse(use, caller, paths, publicEnvironment)
+ );
+ configured += configuredUses.filter(
+ (use, index) => use !== uses[index]
+ ).length;
+ rule.use = Array.isArray(rule.use) ? configuredUses : configuredUses[0];
+ }
+
+ for (const child of [...(rule.oneOf ?? []), ...(rule.rules ?? [])]) {
+ configured += configureRule(child, caller, paths, publicEnvironment);
+ }
+
+ return configured;
+}
+
+export function configureExpoBabelLoaders(
+ rules: unknown[],
+ caller: ExpoBabelCaller,
+ paths: ExpoLoaderPaths,
+ publicEnvironment: ExpoPublicEnvironmentSnapshot
+): number {
+ let configured = 0;
+ for (const rule of rules) {
+ if (typeof rule === 'object' && rule !== null) {
+ configured += configureRule(
+ rule as Rule,
+ caller,
+ paths,
+ publicEnvironment
+ );
+ }
+ }
+
+ if (configured === 0) {
+ rules.push({
+ test: /\.[cm]?[jt]sx?$/,
+ type: 'javascript/auto',
+ use: {
+ loader: paths.babel,
+ options: { caller, expoPublicEnvironment: publicEnvironment },
+ },
+ });
+ configured = 1;
+ }
+
+ return configured;
+}
diff --git a/packages/repack-expo/src/rspack/babel/createExpoBabelCaller.ts b/packages/repack-expo/src/rspack/babel/createExpoBabelCaller.ts
new file mode 100644
index 000000000..93784bb12
--- /dev/null
+++ b/packages/repack-expo/src/rspack/babel/createExpoBabelCaller.ts
@@ -0,0 +1,46 @@
+import type { ExpoNativePlatform } from '../entry/resolveExpoEntry.js';
+
+export type ExpoBabelCaller = Readonly<{
+ asyncRoutes: false;
+ baseUrl: '';
+ bundler: 'repack';
+ engine: 'hermes';
+ isDev: boolean;
+ isHMREnabled: false;
+ isReactServer: false;
+ isServer: false;
+ name: '@callstack/repack-expo';
+ platform: ExpoNativePlatform;
+ preserveEnvVars: true;
+ projectRoot: string;
+ routerRoot: string;
+ supportsStaticESM: true;
+}>;
+
+type CreateExpoBabelCallerOptions = {
+ isDev: boolean;
+ platform: ExpoNativePlatform;
+ projectRoot: string;
+ routerRoot: string;
+};
+
+export function createExpoBabelCaller(
+ options: CreateExpoBabelCallerOptions
+): ExpoBabelCaller {
+ return Object.freeze({
+ asyncRoutes: false,
+ baseUrl: '',
+ bundler: 'repack',
+ engine: 'hermes',
+ isDev: options.isDev,
+ isHMREnabled: false,
+ isReactServer: false,
+ isServer: false,
+ name: '@callstack/repack-expo',
+ platform: options.platform,
+ preserveEnvVars: true,
+ projectRoot: options.projectRoot,
+ routerRoot: options.routerRoot,
+ supportsStaticESM: true,
+ });
+}
diff --git a/packages/repack-expo/src/rspack/babel/inlineExpoPublicEnvironment.ts b/packages/repack-expo/src/rspack/babel/inlineExpoPublicEnvironment.ts
new file mode 100644
index 000000000..656cac2ba
--- /dev/null
+++ b/packages/repack-expo/src/rspack/babel/inlineExpoPublicEnvironment.ts
@@ -0,0 +1,77 @@
+type BabelApi = {
+ caller(callback: (caller: BabelCaller | undefined) => boolean): boolean;
+ types: Record any>;
+};
+
+type BabelCaller = {
+ isNodeModule?: boolean;
+};
+
+type BabelPath = {
+ node: any;
+ parent: any;
+ replaceWith(node: any): void;
+};
+
+type InlineExpoPublicEnvironmentOptions = {
+ environment: Readonly>;
+};
+
+export default function inlineExpoPublicEnvironment(
+ api: BabelApi,
+ options: InlineExpoPublicEnvironmentOptions
+) {
+ const { types: t } = api;
+ const isNodeModule = api.caller((caller) => !!caller?.isNodeModule);
+
+ function isProcessEnv(path: BabelPath): boolean {
+ const { object } = path.node;
+ if (
+ (!t.isMemberExpression(object) &&
+ !t.isOptionalMemberExpression(object)) ||
+ !t.isIdentifier(object.object) ||
+ object.object.name !== 'process'
+ ) {
+ return false;
+ }
+
+ const { property } = object;
+ if (!object.computed && t.isIdentifier(property)) {
+ return property.name === 'env';
+ }
+ if (object.computed && t.isStringLiteral(property)) {
+ return property.value === 'env';
+ }
+ return false;
+ }
+
+ function getMemberProperty(path: BabelPath): string | undefined {
+ const { computed, property } = path.node;
+ if (!computed && t.isIdentifier(property)) return property.name;
+ if (computed && t.isStringLiteral(property)) return property.value;
+ return undefined;
+ }
+
+ function isAssignment(path: BabelPath): boolean {
+ return (
+ t.isAssignmentExpression(path.parent) && path.parent.left === path.node
+ );
+ }
+
+ function visitMemberExpression(path: BabelPath): void {
+ if (isNodeModule || !isProcessEnv(path) || isAssignment(path)) return;
+
+ const key = getMemberProperty(path);
+ if (key?.startsWith('EXPO_PUBLIC_')) {
+ path.replaceWith(t.valueToNode(options.environment[key]));
+ }
+ }
+
+ return {
+ name: 'repack-expo-inline-public-environment',
+ visitor: {
+ MemberExpression: visitMemberExpression,
+ OptionalMemberExpression: visitMemberExpression,
+ },
+ };
+}
diff --git a/packages/repack-expo/src/rspack/entry/ExpoEntryResolutionError.ts b/packages/repack-expo/src/rspack/entry/ExpoEntryResolutionError.ts
new file mode 100644
index 000000000..5b4a7976b
--- /dev/null
+++ b/packages/repack-expo/src/rspack/entry/ExpoEntryResolutionError.ts
@@ -0,0 +1,34 @@
+export type ExpoEntryResolutionErrorCode =
+ | 'ENTRY_NOT_DEFINED'
+ | 'ENTRY_NOT_FOUND'
+ | 'EXPO_PROJECT_NOT_FOUND'
+ | 'INVALID_PACKAGE_JSON'
+ | 'METRO_ENTRY_UNSUPPORTED'
+ | 'UNSUPPORTED_PLATFORM';
+
+export type ExpoEntryResolutionErrorOptions = {
+ code: ExpoEntryResolutionErrorCode;
+ message: string;
+ platform?: string;
+ projectRoot?: string;
+ requestedEntry?: string;
+ recovery: string;
+};
+
+export class ExpoEntryResolutionError extends Error {
+ readonly code: ExpoEntryResolutionErrorCode;
+ readonly platform?: string;
+ readonly projectRoot?: string;
+ readonly requestedEntry?: string;
+ readonly recovery: string;
+
+ constructor(options: ExpoEntryResolutionErrorOptions) {
+ super(options.message);
+ this.name = 'ExpoEntryResolutionError';
+ this.code = options.code;
+ this.platform = options.platform;
+ this.projectRoot = options.projectRoot;
+ this.requestedEntry = options.requestedEntry;
+ this.recovery = options.recovery;
+ }
+}
diff --git a/packages/repack-expo/src/rspack/entry/configureExpoCompilerEntry.ts b/packages/repack-expo/src/rspack/entry/configureExpoCompilerEntry.ts
new file mode 100644
index 000000000..9a395fb94
--- /dev/null
+++ b/packages/repack-expo/src/rspack/entry/configureExpoCompilerEntry.ts
@@ -0,0 +1,35 @@
+import type { EntryNormalized } from '@rspack/core';
+import { ExpoPluginError } from '../ExpoPluginError.js';
+
+export function configureExpoCompilerEntry(
+ entry: EntryNormalized,
+ entryPath: string,
+ runtimeEntryPath: string
+): EntryNormalized {
+ if (typeof entry === 'function') {
+ throw new ExpoPluginError({
+ code: 'DYNAMIC_ENTRY_UNSUPPORTED',
+ message: 'ExpoPlugin cannot configure a dynamic Rspack entry.',
+ recovery:
+ 'Use a single static entry; ExpoPlugin will resolve its import from package.json#main.',
+ });
+ }
+
+ const entryNames = Object.keys(entry);
+ if (entryNames.length > 1) {
+ throw new ExpoPluginError({
+ code: 'AMBIGUOUS_ENTRY',
+ message: `ExpoPlugin found multiple Rspack entries: ${entryNames.join(', ')}.`,
+ recovery:
+ 'Use one native application entry. Extra chunks should be created with dynamic import().',
+ });
+ }
+
+ const entryName = entryNames[0] ?? 'main';
+ return {
+ [entryName]: {
+ ...(entry[entryName] ?? {}),
+ import: [runtimeEntryPath, entryPath],
+ },
+ };
+}
diff --git a/packages/repack-expo/src/rspack/entry/resolveExpoEntry.ts b/packages/repack-expo/src/rspack/entry/resolveExpoEntry.ts
new file mode 100644
index 000000000..cbeff5133
--- /dev/null
+++ b/packages/repack-expo/src/rspack/entry/resolveExpoEntry.ts
@@ -0,0 +1,250 @@
+import fs from 'node:fs';
+import path from 'node:path';
+import { ExpoEntryResolutionError } from './ExpoEntryResolutionError.js';
+
+export type ExpoNativePlatform = 'android' | 'ios';
+
+type PackageJson = {
+ dependencies?: Record;
+ devDependencies?: Record;
+ main?: unknown;
+ name?: string;
+ peerDependencies?: Record;
+};
+
+export type ResolveExpoEntryOptions = {
+ entry?: string;
+ from?: string;
+ platform: ExpoNativePlatform;
+ projectRoot?: string;
+};
+
+export type ResolvedExpoEntry = Readonly<{
+ entryPath: string;
+ packageName?: string;
+ physicalProjectRoot: string;
+ platform: ExpoNativePlatform;
+ projectRoot: string;
+ request: string;
+}>;
+
+const PROJECT_MARKERS = ['app.json', 'app.config.js', 'app.config.ts'];
+const METRO_VIRTUAL_ENTRY = '.expo/.virtual-metro-entry';
+const SOURCE_EXTENSIONS = ['tsx', 'ts', 'jsx', 'js', 'json'];
+
+function readPackageJson(projectRoot: string): PackageJson {
+ const packageJsonPath = path.join(projectRoot, 'package.json');
+ let contents: string;
+
+ try {
+ contents = fs.readFileSync(packageJsonPath, 'utf8');
+ } catch {
+ throw new ExpoEntryResolutionError({
+ code: 'EXPO_PROJECT_NOT_FOUND',
+ message: `No package.json was found for the Expo project at ${projectRoot}.`,
+ projectRoot,
+ recovery:
+ 'Run the command from an Expo application or pass its project root explicitly.',
+ });
+ }
+
+ try {
+ const packageJson: unknown = JSON.parse(contents);
+ if (
+ packageJson === null ||
+ typeof packageJson !== 'object' ||
+ Array.isArray(packageJson)
+ ) {
+ throw new Error('package.json must contain an object');
+ }
+ return packageJson as PackageJson;
+ } catch {
+ throw new ExpoEntryResolutionError({
+ code: 'INVALID_PACKAGE_JSON',
+ message: `The package.json at ${packageJsonPath} is not valid JSON.`,
+ projectRoot,
+ recovery: 'Fix package.json before configuring Re.Pack for Expo.',
+ });
+ }
+}
+
+function isExpoPackage(packageJson: PackageJson): boolean {
+ return Boolean(
+ packageJson.dependencies?.expo ??
+ packageJson.devDependencies?.expo ??
+ packageJson.peerDependencies?.expo
+ );
+}
+
+function hasExpoProjectMarker(directory: string): boolean {
+ return PROJECT_MARKERS.some((marker) =>
+ fs.existsSync(path.join(directory, marker))
+ );
+}
+
+function normalizeStartDirectory(from: string): string {
+ const absoluteFrom = path.resolve(from);
+ try {
+ return fs.statSync(absoluteFrom).isDirectory()
+ ? absoluteFrom
+ : path.dirname(absoluteFrom);
+ } catch {
+ return absoluteFrom;
+ }
+}
+
+export function findExpoProjectRoot(from: string): string {
+ let current = normalizeStartDirectory(from);
+
+ while (true) {
+ const packageJsonPath = path.join(current, 'package.json');
+ if (fs.existsSync(packageJsonPath)) {
+ const packageJson = readPackageJson(current);
+ if (isExpoPackage(packageJson) || hasExpoProjectMarker(current)) {
+ return current;
+ }
+ }
+
+ const parent = path.dirname(current);
+ if (parent === current) break;
+ current = parent;
+ }
+
+ throw new ExpoEntryResolutionError({
+ code: 'EXPO_PROJECT_NOT_FOUND',
+ message: `No Expo project could be found from ${path.resolve(from)}.`,
+ recovery:
+ 'Run the command inside a package that depends on expo or pass its project root explicitly.',
+ });
+}
+
+function platformExtensions(platform: ExpoNativePlatform): string[] {
+ return SOURCE_EXTENSIONS.flatMap((extension) => [
+ `.${platform}.${extension}`,
+ `.native.${extension}`,
+ `.${extension}`,
+ ]);
+}
+
+function resolveFileRequest(
+ projectRoot: string,
+ request: string,
+ platform: ExpoNativePlatform
+): string | undefined {
+ const basePath = path.isAbsolute(request)
+ ? request
+ : path.resolve(projectRoot, request);
+ const candidates = path.extname(basePath)
+ ? [basePath]
+ : [
+ ...platformExtensions(platform).map(
+ (extension) => `${basePath}${extension}`
+ ),
+ ...platformExtensions(platform).map((extension) =>
+ path.join(basePath, `index${extension}`)
+ ),
+ ];
+
+ return candidates.find((candidate) => {
+ try {
+ return fs.statSync(candidate).isFile();
+ } catch {
+ return false;
+ }
+ });
+}
+
+function resolveEntryRequest(
+ projectRoot: string,
+ request: string,
+ platform: ExpoNativePlatform
+): string | undefined {
+ const fileRequest = resolveFileRequest(projectRoot, request, platform);
+ if (fileRequest) return fileRequest;
+
+ try {
+ return require.resolve(request, { paths: [projectRoot] });
+ } catch {
+ return undefined;
+ }
+}
+
+function rejectMetroVirtualEntry(
+ projectRoot: string,
+ request: string,
+ platform: ExpoNativePlatform
+): void {
+ if (!request.replaceAll('\\', '/').includes(METRO_VIRTUAL_ENTRY)) return;
+
+ throw new ExpoEntryResolutionError({
+ code: 'METRO_ENTRY_UNSUPPORTED',
+ message: `The entry ${request} is an Expo Metro virtual entry and cannot be compiled by Re.Pack.`,
+ platform,
+ projectRoot,
+ requestedEntry: request,
+ recovery:
+ 'Set package.json#main to the real application entry, such as expo-router/entry or index.js.',
+ });
+}
+
+export function resolveExpoEntry(
+ options: ResolveExpoEntryOptions
+): ResolvedExpoEntry {
+ if (options.platform !== 'android' && options.platform !== 'ios') {
+ throw new ExpoEntryResolutionError({
+ code: 'UNSUPPORTED_PLATFORM',
+ message: `Expo/Re.Pack v1 does not support platform ${String(options.platform)}.`,
+ platform: options.platform,
+ recovery: 'Use the ios or android native platform.',
+ });
+ }
+
+ const projectRoot = options.projectRoot
+ ? path.resolve(options.projectRoot)
+ : findExpoProjectRoot(options.from ?? process.cwd());
+ const packageJson = readPackageJson(projectRoot);
+ if (!isExpoPackage(packageJson) && !hasExpoProjectMarker(projectRoot)) {
+ throw new ExpoEntryResolutionError({
+ code: 'EXPO_PROJECT_NOT_FOUND',
+ message: `The package at ${projectRoot} is not configured as an Expo project.`,
+ projectRoot,
+ recovery:
+ 'Install expo in the application package or pass the correct Expo project root.',
+ });
+ }
+ const request = options.entry ?? packageJson.main;
+
+ if (typeof request !== 'string' || request.length === 0) {
+ throw new ExpoEntryResolutionError({
+ code: 'ENTRY_NOT_DEFINED',
+ message: `The Expo project at ${projectRoot} does not define package.json#main.`,
+ platform: options.platform,
+ projectRoot,
+ recovery:
+ 'Set package.json#main to expo-router/entry or an application entry such as index.js.',
+ });
+ }
+
+ rejectMetroVirtualEntry(projectRoot, request, options.platform);
+ const entryPath = resolveEntryRequest(projectRoot, request, options.platform);
+ if (!entryPath) {
+ throw new ExpoEntryResolutionError({
+ code: 'ENTRY_NOT_FOUND',
+ message: `Cannot resolve Expo entry ${request} for ${options.platform} from ${projectRoot}.`,
+ platform: options.platform,
+ projectRoot,
+ requestedEntry: request,
+ recovery:
+ 'Install the entry package or correct package.json#main before running Re.Pack.',
+ });
+ }
+
+ return Object.freeze({
+ entryPath: fs.realpathSync(entryPath),
+ packageName: packageJson.name,
+ physicalProjectRoot: fs.realpathSync(projectRoot),
+ platform: options.platform,
+ projectRoot,
+ request,
+ });
+}
diff --git a/packages/repack-expo/src/rspack/environment/configureExpoPublicEnvironment.ts b/packages/repack-expo/src/rspack/environment/configureExpoPublicEnvironment.ts
new file mode 100644
index 000000000..3b1805729
--- /dev/null
+++ b/packages/repack-expo/src/rspack/environment/configureExpoPublicEnvironment.ts
@@ -0,0 +1,51 @@
+import fs from 'node:fs';
+import type { Compiler } from '@rspack/core';
+import {
+ type ExpoEnvironmentMode,
+ type ExpoPublicEnvironmentSnapshot,
+ createExpoPublicEnvironmentSnapshot,
+ getExpoEnvironmentFiles,
+} from './expoPublicEnvironment.js';
+
+const PLUGIN_NAME = 'RepackExpoPublicEnvironmentPlugin';
+
+function addEnvironmentDigestToPersistentCache(
+ compiler: Compiler,
+ digest: string
+): void {
+ const cache = compiler.options.experiments?.cache;
+ if (typeof cache !== 'object' || cache.type !== 'persistent') return;
+
+ cache.version = [cache.version, `${PLUGIN_NAME}:${digest}`]
+ .filter(Boolean)
+ .join('|');
+}
+
+export function configureExpoPublicEnvironment(
+ compiler: Compiler,
+ projectRoot: string
+): ExpoPublicEnvironmentSnapshot {
+ const mode: ExpoEnvironmentMode =
+ compiler.options.mode === 'development' ? 'development' : 'production';
+ const environment = createExpoPublicEnvironmentSnapshot({
+ mode,
+ projectRoot,
+ });
+ const environmentFiles = getExpoEnvironmentFiles(projectRoot, mode);
+ addEnvironmentDigestToPersistentCache(compiler, environment.digest);
+ const refreshEnvironment = () => {
+ Object.assign(
+ environment,
+ createExpoPublicEnvironmentSnapshot({ mode, projectRoot })
+ );
+ };
+
+ compiler.hooks?.beforeCompile?.tap(PLUGIN_NAME, refreshEnvironment);
+ compiler.hooks?.thisCompilation?.tap(PLUGIN_NAME, (compilation) => {
+ for (const filename of environmentFiles) {
+ if (fs.existsSync(filename)) compilation.fileDependencies.add(filename);
+ else compilation.missingDependencies.add(filename);
+ }
+ });
+ return environment;
+}
diff --git a/packages/repack-expo/src/rspack/environment/expoPublicEnvironment.ts b/packages/repack-expo/src/rspack/environment/expoPublicEnvironment.ts
new file mode 100644
index 000000000..92312cf8e
--- /dev/null
+++ b/packages/repack-expo/src/rspack/environment/expoPublicEnvironment.ts
@@ -0,0 +1,98 @@
+import { createHash } from 'node:crypto';
+import path from 'node:path';
+import { getEnvFiles, parseProjectEnv } from '@expo/env';
+
+export type ExpoEnvironmentMode = 'development' | 'production';
+export type ExpoPublicEnvironment = Readonly>;
+export type ExpoPublicEnvironmentSnapshot = {
+ digest: string;
+ inline: boolean;
+ values: ExpoPublicEnvironment;
+};
+
+type ResolveExpoPublicEnvironmentOptions = {
+ mode: ExpoEnvironmentMode;
+ projectRoot: string;
+ systemEnv?: NodeJS.ProcessEnv;
+};
+
+const EMPTY_PUBLIC_ENVIRONMENT = Object.freeze({});
+
+function readBoolish(value: string | undefined): boolean {
+ if (value === undefined) return false;
+ if (value === '1' || value.toLowerCase() === 'true') return true;
+ if (value === '0' || value.toLowerCase() === 'false') return false;
+ throw new Error(
+ 'Expected an Expo environment flag to be true, false, 1, or 0.'
+ );
+}
+
+export function shouldInlineExpoPublicEnvironment(
+ mode: ExpoEnvironmentMode,
+ systemEnv: NodeJS.ProcessEnv = process.env
+): boolean {
+ return !(
+ mode === 'production' && readBoolish(systemEnv.EXPO_NO_CLIENT_ENV_VARS)
+ );
+}
+
+function onlyPublicValues(
+ environment: NodeJS.ProcessEnv
+): ExpoPublicEnvironment {
+ const publicEnvironment: Record = {};
+ for (const [key, value] of Object.entries(environment)) {
+ if (key.startsWith('EXPO_PUBLIC_') && value !== undefined) {
+ publicEnvironment[key] = value;
+ }
+ }
+ return Object.freeze(publicEnvironment);
+}
+
+export function getExpoEnvironmentFiles(
+ projectRoot: string,
+ mode: ExpoEnvironmentMode
+): string[] {
+ return getEnvFiles({ mode, silent: true }).map((filename) =>
+ path.join(projectRoot, filename)
+ );
+}
+
+export function resolveExpoPublicEnvironment({
+ mode,
+ projectRoot,
+ systemEnv = process.env,
+}: ResolveExpoPublicEnvironmentOptions): ExpoPublicEnvironment {
+ if (!shouldInlineExpoPublicEnvironment(mode, systemEnv)) {
+ return EMPTY_PUBLIC_ENVIRONMENT;
+ }
+
+ const fileEnvironment = readBoolish(systemEnv.EXPO_NO_DOTENV)
+ ? {}
+ : parseProjectEnv(projectRoot, {
+ mode,
+ silent: true,
+ systemEnv,
+ }).env;
+
+ return onlyPublicValues({ ...fileEnvironment, ...systemEnv });
+}
+
+export function createExpoPublicEnvironmentSnapshot(
+ options: ResolveExpoPublicEnvironmentOptions
+): ExpoPublicEnvironmentSnapshot {
+ const inline = shouldInlineExpoPublicEnvironment(
+ options.mode,
+ options.systemEnv
+ );
+ const values = resolveExpoPublicEnvironment(options);
+ const serialized = JSON.stringify([
+ inline,
+ Object.entries(values).sort(([left], [right]) => left.localeCompare(right)),
+ ]);
+
+ return {
+ digest: createHash('sha256').update(serialized).digest('hex'),
+ inline,
+ values,
+ };
+}
diff --git a/packages/repack-expo/src/rspack/index.ts b/packages/repack-expo/src/rspack/index.ts
new file mode 100644
index 000000000..60f07c2c2
--- /dev/null
+++ b/packages/repack-expo/src/rspack/index.ts
@@ -0,0 +1,5 @@
+export { ExpoPlugin, type ExpoPluginOptions } from './ExpoPlugin.js';
+export {
+ ExpoPluginError,
+ type ExpoPluginErrorCode,
+} from './ExpoPluginError.js';
diff --git a/packages/repack-expo/src/rspack/loaders/expoBabelLoader.ts b/packages/repack-expo/src/rspack/loaders/expoBabelLoader.ts
new file mode 100644
index 000000000..07da79940
--- /dev/null
+++ b/packages/repack-expo/src/rspack/loaders/expoBabelLoader.ts
@@ -0,0 +1,81 @@
+import fs from 'node:fs';
+import repackBabelLoader, { raw } from '@callstack/repack/babel-loader';
+import type { LoaderContext } from '@rspack/core';
+import {
+ type ExpoEnvironmentMode,
+ type ExpoPublicEnvironmentSnapshot,
+ getExpoEnvironmentFiles,
+} from '../environment/expoPublicEnvironment.js';
+import { withExpoBabelCaller } from './withExpoBabelCaller.js';
+
+export { raw };
+
+type ExpoLoaderCaller = {
+ isDev?: boolean;
+ isNodeModule?: boolean;
+ projectRoot?: string;
+};
+
+function withExpoPublicEnvironment(
+ loader: LoaderContext>,
+ options: Record,
+ publicEnvironment: ExpoPublicEnvironmentSnapshot | undefined
+): Record {
+ const caller = options.caller as ExpoLoaderCaller | undefined;
+ if (
+ !caller?.projectRoot ||
+ caller.isNodeModule ||
+ !publicEnvironment?.inline
+ ) {
+ return options;
+ }
+
+ const mode: ExpoEnvironmentMode = caller.isDev ? 'development' : 'production';
+ for (const filename of getExpoEnvironmentFiles(caller.projectRoot, mode)) {
+ if (fs.existsSync(filename)) loader.addDependency(filename);
+ else if (loader.addMissingDependency) loader.addMissingDependency(filename);
+ else loader.addDependency(filename);
+ }
+
+ const environmentPlugin = [
+ require.resolve('../babel/inlineExpoPublicEnvironment.js'),
+ {
+ environment: publicEnvironment.values,
+ },
+ ];
+
+ return {
+ ...options,
+ plugins: [
+ ...(Array.isArray(options.plugins) ? options.plugins : []),
+ environmentPlugin,
+ ],
+ };
+}
+
+export default function expoBabelLoader(
+ this: LoaderContext>,
+ source: string,
+ sourceMap: string | undefined
+) {
+ const originalGetOptions = this.getOptions;
+ const { expoPublicEnvironment, ...babelLoaderOptions } =
+ originalGetOptions.call(this) as Record & {
+ expoPublicEnvironment?: ExpoPublicEnvironmentSnapshot;
+ };
+ const options = withExpoPublicEnvironment(
+ this,
+ withExpoBabelCaller(
+ { sourceRoot: undefined, ...babelLoaderOptions },
+ this.resourcePath
+ ),
+ expoPublicEnvironment
+ );
+
+ this.getOptions = () => options;
+ try {
+ return repackBabelLoader.call(this, source, sourceMap);
+ } finally {
+ this.getOptions = originalGetOptions;
+ }
+}
diff --git a/packages/repack-expo/src/rspack/loaders/expoRouterEntryLoader.ts b/packages/repack-expo/src/rspack/loaders/expoRouterEntryLoader.ts
new file mode 100644
index 000000000..bb7239235
--- /dev/null
+++ b/packages/repack-expo/src/rspack/loaders/expoRouterEntryLoader.ts
@@ -0,0 +1,34 @@
+import fs from 'node:fs';
+import path from 'node:path';
+import type { LoaderContext } from '@rspack/core';
+
+export const raw = false;
+
+function resolveRouterModule(routerRoot: string, request: string): string {
+ const modulePath = path.join(routerRoot, 'build', `${request}.js`);
+ if (fs.existsSync(modulePath)) return modulePath;
+
+ throw new Error(
+ `@callstack/repack-expo cannot find Expo Router's ${request} module at ${modulePath}. ` +
+ 'Use a supported Expo Router native entry or configure a custom application entry.'
+ );
+}
+
+export default function expoRouterEntryLoader(
+ this: LoaderContext>
+): string {
+ this.cacheable?.(true);
+
+ const routerRoot = path.dirname(this.resourcePath);
+ const qualifiedEntry = resolveRouterModule(routerRoot, 'qualified-entry');
+ const renderRootComponent = resolveRouterModule(
+ routerRoot,
+ 'renderRootComponent'
+ );
+
+ return [
+ `const { App } = require(${JSON.stringify(qualifiedEntry)});`,
+ `const { renderRootComponent } = require(${JSON.stringify(renderRootComponent)});`,
+ 'renderRootComponent(App);',
+ ].join('\n');
+}
diff --git a/packages/repack-expo/src/rspack/loaders/withExpoBabelCaller.ts b/packages/repack-expo/src/rspack/loaders/withExpoBabelCaller.ts
new file mode 100644
index 000000000..f9cf48027
--- /dev/null
+++ b/packages/repack-expo/src/rspack/loaders/withExpoBabelCaller.ts
@@ -0,0 +1,25 @@
+type LoaderOptions = Record;
+
+function isNodeModule(resourcePath: string): boolean {
+ return resourcePath.replaceAll('\\', '/').includes('/node_modules/');
+}
+
+function mergeResourceCaller(
+ caller: unknown,
+ resourcePath: string
+): LoaderOptions {
+ return {
+ ...(typeof caller === 'object' && caller !== null ? caller : {}),
+ isNodeModule: isNodeModule(resourcePath),
+ };
+}
+
+export function withExpoBabelCaller(
+ options: LoaderOptions,
+ resourcePath: string
+): LoaderOptions {
+ return {
+ ...options,
+ caller: mergeResourceCaller(options.caller, resourcePath),
+ };
+}
diff --git a/packages/repack-expo/src/rspack/output/configureExpoChunkFilename.ts b/packages/repack-expo/src/rspack/output/configureExpoChunkFilename.ts
new file mode 100644
index 000000000..b1f907c6d
--- /dev/null
+++ b/packages/repack-expo/src/rspack/output/configureExpoChunkFilename.ts
@@ -0,0 +1,51 @@
+import type { Compiler } from '@rspack/core';
+import { ExpoPluginError } from '../ExpoPluginError.js';
+
+const RSPACK_DEFAULT_CHUNK_FILENAME = '[name].js';
+const EXPO_DEFAULT_CHUNK_FILENAME = '[name].chunk.bundle';
+const CHUNK_VARYING_PLACEHOLDER =
+ /\[(?:name|id|(?:content|chunk|full)hash(?::[^\]]+)?)\]/;
+
+export function configureExpoChunkFilename(
+ output: Compiler['options']['output']
+): void {
+ const chunkFilename = output.chunkFilename;
+
+ if (
+ chunkFilename === undefined ||
+ chunkFilename === RSPACK_DEFAULT_CHUNK_FILENAME
+ ) {
+ output.chunkFilename = EXPO_DEFAULT_CHUNK_FILENAME;
+ return;
+ }
+
+ if (typeof chunkFilename !== 'string') {
+ throw new ExpoPluginError({
+ code: 'INVALID_CHUNK_FILENAME',
+ message:
+ 'ExpoPlugin requires output.chunkFilename to be a statically verifiable string.',
+ recovery:
+ 'Use a flat string such as "[name].chunk.bundle" so packaged native chunks can be resolved by basename.',
+ });
+ }
+
+ if (chunkFilename.includes('/') || chunkFilename.includes('\\')) {
+ throw new ExpoPluginError({
+ code: 'INVALID_CHUNK_FILENAME',
+ message:
+ 'ExpoPlugin requires output.chunkFilename to be a flat filename without directories.',
+ recovery:
+ 'Remove path segments and use a flat string such as "[name].chunk.bundle". Packaged native chunks are resolved by basename.',
+ });
+ }
+
+ if (!CHUNK_VARYING_PLACEHOLDER.test(chunkFilename)) {
+ throw new ExpoPluginError({
+ code: 'INVALID_CHUNK_FILENAME',
+ message:
+ 'ExpoPlugin requires output.chunkFilename to produce a unique basename for each chunk.',
+ recovery:
+ 'Include [name], [id], [contenthash], [chunkhash], or [fullhash], for example "[name].chunk.bundle".',
+ });
+ }
+}
diff --git a/packages/repack-expo/src/rspack/resolve/configureExpoModuleResolution.ts b/packages/repack-expo/src/rspack/resolve/configureExpoModuleResolution.ts
new file mode 100644
index 000000000..658d68280
--- /dev/null
+++ b/packages/repack-expo/src/rspack/resolve/configureExpoModuleResolution.ts
@@ -0,0 +1,11 @@
+import path from 'node:path';
+
+export function configureExpoModuleResolution(
+ modules: string[] | undefined,
+ projectRoot: string
+): string[] {
+ const projectNodeModules = path.join(projectRoot, 'node_modules');
+ if (!modules) return [projectNodeModules, 'node_modules'];
+ if (modules.includes(projectNodeModules)) return modules;
+ return [projectNodeModules, ...modules];
+}
diff --git a/packages/repack-expo/src/rspack/resolve/configureExpoResolveOptions.ts b/packages/repack-expo/src/rspack/resolve/configureExpoResolveOptions.ts
new file mode 100644
index 000000000..5c1666e6e
--- /dev/null
+++ b/packages/repack-expo/src/rspack/resolve/configureExpoResolveOptions.ts
@@ -0,0 +1,56 @@
+import { getResolveOptions } from '@callstack/repack';
+import type { ResolveOptions } from '@rspack/core';
+
+function mergeUnique(required: string[], current?: string[]): string[] {
+ return [...new Set([...required, ...(current ?? [])])];
+}
+
+function mergeDependencyOptions(
+ current: ResolveOptions['byDependency'],
+ required: Record
+): NonNullable {
+ const merged = { ...current };
+
+ for (const [dependency, options] of Object.entries(required)) {
+ const currentOptions = current?.[dependency];
+ merged[dependency] = {
+ ...currentOptions,
+ ...options,
+ conditionNames: mergeUnique(
+ options.conditionNames,
+ currentOptions?.conditionNames
+ ),
+ };
+ }
+
+ return merged;
+}
+
+export function configureExpoResolveOptions(
+ resolve: ResolveOptions,
+ platform: 'android' | 'ios'
+): void {
+ const required = getResolveOptions(platform, {
+ enablePackageExports: true,
+ });
+
+ resolve.aliasFields = mergeUnique(required.aliasFields, resolve.aliasFields);
+ resolve.conditionNames = mergeUnique(
+ required.conditionNames,
+ resolve.conditionNames
+ );
+ resolve.exportsFields = mergeUnique(
+ required.exportsFields,
+ resolve.exportsFields
+ );
+ resolve.extensions = mergeUnique(required.extensions, resolve.extensions);
+ resolve.mainFields = mergeUnique(required.mainFields, resolve.mainFields);
+ resolve.extensionAlias = {
+ ...required.extensionAlias,
+ ...resolve.extensionAlias,
+ };
+ resolve.byDependency = mergeDependencyOptions(
+ resolve.byDependency,
+ required.byDependency
+ );
+}
diff --git a/packages/repack-expo/src/rspack/resolve/configureReactNativeResolution.ts b/packages/repack-expo/src/rspack/resolve/configureReactNativeResolution.ts
new file mode 100644
index 000000000..7deeb15fe
--- /dev/null
+++ b/packages/repack-expo/src/rspack/resolve/configureReactNativeResolution.ts
@@ -0,0 +1,41 @@
+import path from 'node:path';
+import type { ResolveAlias } from '@rspack/core';
+import { ExpoPluginError } from '../ExpoPluginError.js';
+
+type PackageResolver = (
+ request: string,
+ options: { paths: string[] }
+) => string;
+
+export function configureReactNativeResolution(
+ alias: ResolveAlias | undefined,
+ projectRoot: string,
+ resolvePackage: PackageResolver = require.resolve
+): ResolveAlias {
+ let reactNativeRoot: string;
+ let assetsRegistryRoot: string;
+
+ try {
+ reactNativeRoot = path.dirname(
+ resolvePackage('react-native/package.json', { paths: [projectRoot] })
+ );
+ assetsRegistryRoot = path.dirname(
+ resolvePackage('@react-native/assets-registry/package.json', {
+ paths: [reactNativeRoot],
+ })
+ );
+ } catch {
+ throw new ExpoPluginError({
+ code: 'REACT_NATIVE_RESOLUTION_FAILED',
+ message: `ExpoPlugin could not resolve the application's React Native runtime from ${projectRoot}.`,
+ recovery:
+ 'Install react-native and @react-native/assets-registry for the Expo SDK, then reinstall dependencies.',
+ });
+ }
+
+ return {
+ 'react-native': reactNativeRoot,
+ '@react-native/assets-registry': assetsRegistryRoot,
+ ...(alias ?? {}),
+ };
+}
diff --git a/packages/repack-expo/src/rspack/router/configureExpoRouterEntry.ts b/packages/repack-expo/src/rspack/router/configureExpoRouterEntry.ts
new file mode 100644
index 000000000..c47799371
--- /dev/null
+++ b/packages/repack-expo/src/rspack/router/configureExpoRouterEntry.ts
@@ -0,0 +1,22 @@
+import type { ResolvedExpoEntry } from '../entry/resolveExpoEntry.js';
+
+const EXPO_ROUTER_ENTRY = 'expo-router/entry';
+
+function escapeRegExp(value: string): string {
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+}
+
+export function configureExpoRouterEntry(
+ rules: unknown[],
+ entry: ResolvedExpoEntry,
+ loaderPath: string
+): boolean {
+ if (entry.request !== EXPO_ROUTER_ENTRY) return false;
+
+ rules.push({
+ enforce: 'pre',
+ test: new RegExp(`^${escapeRegExp(entry.entryPath)}$`),
+ use: { loader: loaderPath },
+ });
+ return true;
+}
diff --git a/packages/repack-expo/src/rspack/router/resolveRouterRoot.ts b/packages/repack-expo/src/rspack/router/resolveRouterRoot.ts
new file mode 100644
index 000000000..fbf2c679c
--- /dev/null
+++ b/packages/repack-expo/src/rspack/router/resolveRouterRoot.ts
@@ -0,0 +1,35 @@
+import fs from 'node:fs';
+import path from 'node:path';
+import { ExpoPluginError } from '../ExpoPluginError.js';
+
+function isPathInside(child: string, parent: string): boolean {
+ const relative = path.relative(parent, child);
+ return (
+ relative === '' ||
+ (!relative.startsWith('..') && !path.isAbsolute(relative))
+ );
+}
+
+export function resolveRouterRoot(
+ projectRoot: string,
+ configuredRoot?: string
+): string {
+ const routerRoot =
+ configuredRoot ??
+ (fs.existsSync(path.join(projectRoot, 'src', 'app')) ? 'src/app' : 'app');
+ const absoluteRouterRoot = path.resolve(projectRoot, routerRoot);
+
+ if (!isPathInside(absoluteRouterRoot, projectRoot)) {
+ throw new ExpoPluginError({
+ code: 'INVALID_ROUTER_ROOT',
+ message: `The Expo Router root ${routerRoot} resolves outside ${projectRoot}.`,
+ recovery:
+ 'Set ExpoPlugin.routerRoot to a directory inside the Expo project.',
+ });
+ }
+
+ return path
+ .relative(projectRoot, absoluteRouterRoot)
+ .split(path.sep)
+ .join('/');
+}
diff --git a/packages/repack-expo/src/rspack/runtime/configureScriptManager.ts b/packages/repack-expo/src/rspack/runtime/configureScriptManager.ts
new file mode 100644
index 000000000..82f0df7f6
--- /dev/null
+++ b/packages/repack-expo/src/rspack/runtime/configureScriptManager.ts
@@ -0,0 +1,8 @@
+import { Script, ScriptManager } from '@callstack/repack/client';
+import { registerLocalChunkResolver } from './localChunkResolver.js';
+
+registerLocalChunkResolver({
+ isDev: __DEV__,
+ script: Script,
+ scriptManager: ScriptManager.shared,
+});
diff --git a/packages/repack-expo/src/rspack/runtime/localChunkResolver.ts b/packages/repack-expo/src/rspack/runtime/localChunkResolver.ts
new file mode 100644
index 000000000..d85d0f400
--- /dev/null
+++ b/packages/repack-expo/src/rspack/runtime/localChunkResolver.ts
@@ -0,0 +1,45 @@
+import type {
+ ResolverOptions,
+ ScriptLocator,
+ ScriptLocatorResolver,
+} from '@callstack/repack/client';
+
+export const LOCAL_CHUNK_RESOLVER_KEY = '@callstack/repack-expo/local-chunks';
+const LOCAL_CHUNK_RESOLVER_PRIORITY = 1;
+
+type ScriptApi = {
+ getDevServerURL(scriptId: string): ScriptLocator['url'];
+ getFileSystemURL(scriptId: string): ScriptLocator['url'];
+};
+
+type ScriptManagerApi = {
+ addResolver(resolver: ScriptLocatorResolver, options?: ResolverOptions): void;
+};
+
+type LocalChunkResolverOptions = {
+ isDev: boolean;
+ script: ScriptApi;
+};
+
+export function createLocalChunkResolver({
+ isDev,
+ script,
+}: LocalChunkResolverOptions): ScriptLocatorResolver {
+ return async (scriptId) => ({
+ cache: false,
+ url: isDev
+ ? script.getDevServerURL(scriptId)
+ : script.getFileSystemURL(scriptId),
+ });
+}
+
+export function registerLocalChunkResolver({
+ isDev,
+ script,
+ scriptManager,
+}: LocalChunkResolverOptions & { scriptManager: ScriptManagerApi }): void {
+ scriptManager.addResolver(createLocalChunkResolver({ isDev, script }), {
+ key: LOCAL_CHUNK_RESOLVER_KEY,
+ priority: LOCAL_CHUNK_RESOLVER_PRIORITY,
+ });
+}
diff --git a/packages/repack-expo/src/rspack/source-map/applyRepackSourceMapFix.ts b/packages/repack-expo/src/rspack/source-map/applyRepackSourceMapFix.ts
new file mode 100644
index 000000000..89569fc37
--- /dev/null
+++ b/packages/repack-expo/src/rspack/source-map/applyRepackSourceMapFix.ts
@@ -0,0 +1,61 @@
+import type { Compiler, SourceMapDevToolPluginOptions } from '@rspack/core';
+
+const MALFORMED_VIRTUAL_MODULE_PREFIX = 'webpack://=="undefined"}';
+
+type ModuleFilenameTemplate =
+ SourceMapDevToolPluginOptions['moduleFilenameTemplate'];
+type ModuleFilenameTemplateFn = Exclude<
+ ModuleFilenameTemplate,
+ string | undefined
+>;
+
+function normalizeModuleFilenameTemplate(
+ template: ModuleFilenameTemplate
+): ModuleFilenameTemplate {
+ if (typeof template !== 'function') return template;
+
+ return ((info) => {
+ const source = template(info);
+ if (!source.startsWith(MALFORMED_VIRTUAL_MODULE_PREFIX)) return source;
+
+ return `webpack://module-federation/virtual-runtime-${info.hash}.js`;
+ }) satisfies ModuleFilenameTemplateFn;
+}
+
+type RepackPlugin = {
+ apply(compiler: Compiler): void;
+};
+
+export function applyRepackSourceMapFix(
+ compiler: Compiler,
+ repackPlugin: RepackPlugin
+): void {
+ if (
+ compiler.options.mode !== 'development' ||
+ !compiler.options.devServer ||
+ !compiler.options.devtool
+ ) {
+ repackPlugin.apply(compiler);
+ return;
+ }
+
+ const SourceMapDevToolPlugin = compiler.webpack.SourceMapDevToolPlugin;
+ compiler.webpack.SourceMapDevToolPlugin = class extends (
+ SourceMapDevToolPlugin
+ ) {
+ constructor(options: SourceMapDevToolPluginOptions) {
+ super({
+ ...options,
+ moduleFilenameTemplate: normalizeModuleFilenameTemplate(
+ options.moduleFilenameTemplate
+ ),
+ });
+ }
+ };
+
+ try {
+ repackPlugin.apply(compiler);
+ } finally {
+ compiler.webpack.SourceMapDevToolPlugin = SourceMapDevToolPlugin;
+ }
+}
diff --git a/packages/repack-expo/test/android-font-assets.test.cjs b/packages/repack-expo/test/android-font-assets.test.cjs
new file mode 100644
index 000000000..e83033022
--- /dev/null
+++ b/packages/repack-expo/test/android-font-assets.test.cjs
@@ -0,0 +1,145 @@
+const assert = require('node:assert/strict');
+const fs = require('node:fs');
+const os = require('node:os');
+const path = require('node:path');
+const test = require('node:test');
+const { rspack } = require('@rspack/core');
+const {
+ configureAndroidFontAssets,
+} = require('../dist/rspack/assets/configureAndroidFontAssets.js');
+
+const fontAssetName = 'font/assets_fonts_materialsymbolsregular.ttf';
+const rawAssetName = 'raw/assets_fonts_materialsymbolsregular.ttf';
+const additionalBinaryFontAssets = new Map([
+ ['font/display.OTF', 'raw/display.OTF'],
+ ['font/icons.ttc', 'raw/icons.ttc'],
+]);
+const fontContents = Buffer.from('fixture-font-contents');
+const fontInfo = {
+ immutable: true,
+ sourceFilename: 'assets/fonts/material-symbols-regular.ttf',
+};
+const fontFamilyAssetName = 'font/material_symbols.xml';
+
+class EmitFixtureFontPlugin {
+ apply(compiler) {
+ compiler.hooks.thisCompilation.tap(
+ 'EmitFixtureFontPlugin',
+ (compilation) => {
+ compilation.hooks.processAssets.tap(
+ {
+ name: 'EmitFixtureFontPlugin',
+ stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL,
+ },
+ () => {
+ compilation.emitAsset(
+ fontAssetName,
+ new compiler.webpack.sources.RawSource(fontContents),
+ fontInfo
+ );
+ for (const assetName of additionalBinaryFontAssets.keys()) {
+ compilation.emitAsset(
+ assetName,
+ new compiler.webpack.sources.RawSource(assetName)
+ );
+ }
+ compilation.emitAsset(
+ fontFamilyAssetName,
+ new compiler.webpack.sources.RawSource('')
+ );
+ }
+ );
+ }
+ );
+ }
+}
+
+async function compile({ mode, platform }) {
+ const projectRoot = fs.mkdtempSync(
+ path.join(os.tmpdir(), 'repack-expo-font-assets-')
+ );
+ fs.writeFileSync(path.join(projectRoot, 'index.js'), 'export default 42;');
+
+ const compiler = rspack({
+ context: projectRoot,
+ entry: './index.js',
+ mode,
+ name: platform,
+ output: {
+ filename: 'main.js',
+ path: path.join(projectRoot, 'dist'),
+ },
+ plugins: [
+ new EmitFixtureFontPlugin(),
+ {
+ apply(compiler) {
+ configureAndroidFontAssets(compiler, platform);
+ },
+ },
+ ],
+ });
+
+ try {
+ return await new Promise((resolve, reject) => {
+ compiler.run((error, stats) => {
+ if (error) return reject(error);
+ if (!stats) return reject(new Error('Rspack returned no stats'));
+ if (stats.hasErrors()) {
+ return reject(
+ new Error(stats.toString({ all: false, errors: true }))
+ );
+ }
+
+ resolve(stats.compilation);
+ });
+ });
+ } finally {
+ await new Promise((resolve) => compiler.close(resolve));
+ fs.rmSync(projectRoot, { force: true, recursive: true });
+ }
+}
+
+test('moves Android production font assets to raw without changing source or info', async () => {
+ const compilation = await compile({
+ mode: 'production',
+ platform: 'android',
+ });
+
+ assert.equal(compilation.getAsset(fontAssetName), undefined);
+ const renamedFont = compilation.getAsset(rawAssetName);
+ assert.ok(renamedFont);
+ assert.deepEqual(Buffer.from(renamedFont.source.buffer()), fontContents);
+ assert.equal(renamedFont.info.immutable, fontInfo.immutable);
+ assert.equal(renamedFont.info.sourceFilename, fontInfo.sourceFilename);
+ for (const [originalName, renamedName] of additionalBinaryFontAssets) {
+ assert.equal(compilation.getAsset(originalName), undefined);
+ assert.ok(compilation.getAsset(renamedName));
+ }
+ assert.ok(compilation.getAsset(fontFamilyAssetName));
+ assert.equal(compilation.getAsset('raw/material_symbols.xml'), undefined);
+});
+
+test('leaves Android development font assets unchanged', async () => {
+ const compilation = await compile({
+ mode: 'development',
+ platform: 'android',
+ });
+
+ assert.ok(compilation.getAsset(fontAssetName));
+ assert.equal(compilation.getAsset(rawAssetName), undefined);
+ for (const [originalName, renamedName] of additionalBinaryFontAssets) {
+ assert.ok(compilation.getAsset(originalName));
+ assert.equal(compilation.getAsset(renamedName), undefined);
+ }
+});
+
+test('leaves iOS production font assets unchanged', async () => {
+ const compilation = await compile({ mode: 'production', platform: 'ios' });
+
+ assert.ok(compilation.getAsset(fontAssetName));
+ assert.equal(compilation.getAsset(rawAssetName), undefined);
+ for (const [originalName, renamedName] of additionalBinaryFontAssets) {
+ assert.ok(compilation.getAsset(originalName));
+ assert.equal(compilation.getAsset(renamedName), undefined);
+ }
+});
diff --git a/packages/repack-expo/test/cli.test.cjs b/packages/repack-expo/test/cli.test.cjs
new file mode 100644
index 000000000..451ad1998
--- /dev/null
+++ b/packages/repack-expo/test/cli.test.cjs
@@ -0,0 +1,608 @@
+const assert = require('node:assert/strict');
+const fs = require('node:fs');
+const os = require('node:os');
+const path = require('node:path');
+const { spawnSync } = require('node:child_process');
+const test = require('node:test');
+
+const {
+ detectPackageManager,
+ runDoctor,
+ runInit,
+} = require('../dist/cli/commands.js');
+const { createProject } = require('./helpers/createCliProject.cjs');
+
+test('init configures a clean static Expo app without touching native directories', () => {
+ const projectRoot = createProject({ lockfile: 'pnpm-lock.yaml' });
+
+ const result = runInit({ projectRoot });
+
+ assert.equal(result.ok, true);
+ assert.deepEqual(result.changedFiles.sort(), [
+ 'app.json',
+ 'package.json',
+ 'react-native.config.js',
+ 'rspack.config.mjs',
+ ]);
+ const appConfig = JSON.parse(
+ fs.readFileSync(path.join(projectRoot, 'app.json'), 'utf8')
+ ).expo;
+ assert.equal('newArchEnabled' in appConfig, false);
+ assert.equal('jsEngine' in appConfig, false);
+ assert.deepEqual(appConfig.updates, { enabled: false });
+ assert.ok(appConfig.plugins.includes('@callstack/repack-expo'));
+ const packageJson = JSON.parse(
+ fs.readFileSync(path.join(projectRoot, 'package.json'), 'utf8')
+ );
+ assert.equal(
+ Object.values(packageJson.devDependencies).some((range) => range === '*'),
+ false
+ );
+ assert.match(
+ fs.readFileSync(path.join(projectRoot, 'rspack.config.mjs'), 'utf8'),
+ /new ExpoPlugin\(\{ platform \}\)/
+ );
+ assert.equal(fs.existsSync(path.join(projectRoot, 'ios')), false);
+ assert.equal(fs.existsSync(path.join(projectRoot, 'android')), false);
+ assert.equal(result.packageManager, 'pnpm');
+});
+
+function snapshotTree(projectRoot) {
+ const snapshot = new Map();
+ const pending = [''];
+ while (pending.length > 0) {
+ const relativeDirectory = pending.pop();
+ for (const entry of fs.readdirSync(
+ path.join(projectRoot, relativeDirectory),
+ {
+ withFileTypes: true,
+ }
+ )) {
+ const relativePath = path.join(relativeDirectory, entry.name);
+ if (entry.isDirectory()) pending.push(relativePath);
+ else
+ snapshot.set(
+ relativePath,
+ fs.readFileSync(path.join(projectRoot, relativePath))
+ );
+ }
+ }
+ return snapshot;
+}
+
+function writeRspackConfig(
+ projectRoot,
+ { federationImport, federationPlugin }
+) {
+ fs.writeFileSync(
+ path.join(projectRoot, 'rspack.config.mjs'),
+ [
+ "import { ExpoPlugin } from '@callstack/repack-expo/rspack';",
+ federationImport,
+ "throw new Error('doctor must not execute application config');",
+ 'export default {',
+ ` plugins: [new ExpoPlugin(), ${federationPlugin}],`,
+ '};',
+ ].join('\n')
+ );
+}
+
+test('doctor is read-only and reports unsupported Expo Updates with a stable code', () => {
+ const projectRoot = createProject();
+ assert.equal(runInit({ projectRoot }).ok, true);
+ const validBefore = snapshotTree(projectRoot);
+
+ const valid = runDoctor({ projectRoot });
+
+ assert.equal(valid.ok, true, JSON.stringify(valid.diagnostics));
+ assert.deepEqual(snapshotTree(projectRoot), validBefore);
+
+ const appPath = path.join(projectRoot, 'app.json');
+ const appJson = JSON.parse(fs.readFileSync(appPath, 'utf8'));
+ appJson.expo.updates.enabled = true;
+ fs.writeFileSync(appPath, `${JSON.stringify(appJson, null, 2)}\n`);
+ const brokenBefore = snapshotTree(projectRoot);
+
+ const broken = runDoctor({ projectRoot });
+
+ assert.equal(broken.ok, false);
+ assert.equal(broken.diagnostics[0].code, 'EXPO_UPDATES_ACTIVE');
+ assert.match(broken.diagnostics[0].recovery, /enabled.*false/i);
+ assert.deepEqual(snapshotTree(projectRoot), brokenBefore);
+});
+
+test('init is idempotent and check/dry-run never write', () => {
+ const projectRoot = createProject();
+ assert.equal(runInit({ projectRoot }).ok, true);
+ const initialized = snapshotTree(projectRoot);
+
+ assert.deepEqual(runInit({ projectRoot }).changedFiles, []);
+ assert.deepEqual(snapshotTree(projectRoot), initialized);
+
+ fs.rmSync(path.join(projectRoot, 'rspack.config.mjs'));
+ const beforeCheck = snapshotTree(projectRoot);
+ const check = runInit({ check: true, projectRoot });
+ assert.equal(check.ok, false);
+ assert.deepEqual(check.changedFiles, ['rspack.config.mjs']);
+ assert.deepEqual(snapshotTree(projectRoot), beforeCheck);
+
+ const dryRun = runInit({ dryRun: true, projectRoot });
+ assert.equal(dryRun.ok, true);
+ assert.deepEqual(dryRun.changedFiles, ['rspack.config.mjs']);
+ assert.equal(dryRun.changes[0].before, null);
+ assert.match(dryRun.changes[0].after, /new ExpoPlugin/);
+ assert.deepEqual(snapshotTree(projectRoot), beforeCheck);
+});
+
+test('preserves public Module Federation v2 config and doctor validates it statically', () => {
+ const projectRoot = createProject();
+ assert.equal(runInit({ projectRoot }).ok, true);
+ writeRspackConfig(projectRoot, {
+ federationImport: [
+ "import * as Repack from '@callstack/repack';",
+ "// new Repack.plugins.ModuleFederationPluginV1({ name: 'old-config' });",
+ ].join('\n'),
+ federationPlugin:
+ "new Repack.plugins.ModuleFederationPluginV2({ name: 'Host' })",
+ });
+ const before = snapshotTree(projectRoot);
+
+ const initialized = runInit({ projectRoot });
+ const doctor = runDoctor({ projectRoot });
+
+ assert.equal(initialized.ok, true, JSON.stringify(initialized.diagnostics));
+ assert.deepEqual(initialized.changedFiles, []);
+ assert.deepEqual(snapshotTree(projectRoot), before);
+ assert.equal(doctor.ok, true, JSON.stringify(doctor.diagnostics));
+});
+
+test('doctor rejects unsupported active federation shapes with stable diagnostics', () => {
+ const cases = [
+ {
+ code: 'MODULE_FEDERATION_V1_UNSUPPORTED',
+ federationImport: "import * as Repack from '@callstack/repack';",
+ federationPlugin:
+ "new Repack.plugins.ModuleFederationPluginV1({ name: 'Widget' })",
+ },
+ {
+ code: 'MODULE_FEDERATION_V1_UNSUPPORTED',
+ federationImport: "import * as Repack from '@callstack/repack';",
+ federationPlugin:
+ "new Repack.plugins.ModuleFederationPlugin({ name: 'Widget' })",
+ },
+ {
+ code: 'MODULE_FEDERATION_V1_UNSUPPORTED',
+ federationImport:
+ "import { ModuleFederationPluginV1 as LegacyFederationPlugin } from '@callstack/repack';",
+ federationPlugin: "new LegacyFederationPlugin({ name: 'Widget' })",
+ },
+ {
+ code: 'MODULE_FEDERATION_V1_UNSUPPORTED',
+ federationImport: [
+ "import * as Repack from '@callstack/repack';",
+ 'const { ModuleFederationPlugin: LegacyFederationPlugin } = Repack.plugins;',
+ ].join('\n'),
+ federationPlugin: "new LegacyFederationPlugin({ name: 'Widget' })",
+ },
+ {
+ code: 'MODULE_FEDERATION_RAW_PLUGIN_UNSUPPORTED',
+ federationImport:
+ "import { ModuleFederationPlugin as RawFederationPlugin } from '@module-federation/enhanced/rspack';",
+ federationPlugin: "new RawFederationPlugin({ name: 'Widget' })",
+ },
+ ];
+
+ for (const testCase of cases) {
+ const projectRoot = createProject();
+ assert.equal(runInit({ projectRoot }).ok, true);
+ writeRspackConfig(projectRoot, testCase);
+ const before = snapshotTree(projectRoot);
+
+ const result = runDoctor({ projectRoot });
+ const federationDiagnostics = result.diagnostics.filter(({ code }) =>
+ code.startsWith('MODULE_FEDERATION_')
+ );
+
+ assert.equal(result.ok, false);
+ assert.deepEqual(
+ federationDiagnostics.map(({ code }) => code),
+ [testCase.code]
+ );
+ assert.match(federationDiagnostics[0].recovery, /ModuleFederationPluginV2/);
+ assert.deepEqual(snapshotTree(projectRoot), before);
+ }
+});
+
+test('doctor ignores unused Metro federation configuration and dependency', () => {
+ const projectRoot = createProject({
+ dependencies: { '@module-federation/metro': '2.8.0' },
+ });
+ assert.equal(runInit({ projectRoot }).ok, true);
+ fs.writeFileSync(
+ path.join(projectRoot, 'metro.config.js'),
+ "module.exports = require('@module-federation/metro');\n"
+ );
+
+ const result = runDoctor({ projectRoot });
+
+ assert.equal(result.ok, true, JSON.stringify(result.diagnostics));
+});
+
+test('init rolls back earlier file replacements when a transaction fails', () => {
+ const projectRoot = createProject();
+ assert.equal(runInit({ projectRoot }).ok, true);
+ const packagePath = path.join(projectRoot, 'package.json');
+ const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
+ delete packageJson.scripts['repack:start'];
+ fs.writeFileSync(packagePath, `${JSON.stringify(packageJson, null, 2)}\n`);
+ const appPath = path.join(projectRoot, 'app.json');
+ const appJson = JSON.parse(fs.readFileSync(appPath, 'utf8'));
+ appJson.expo.description = 'rollback fixture';
+ fs.writeFileSync(appPath, `${JSON.stringify(appJson, null, 2)}\n`);
+ fs.rmSync(path.join(projectRoot, 'react-native.config.js'));
+ fs.rmSync(path.join(projectRoot, 'rspack.config.mjs'));
+ const before = snapshotTree(projectRoot);
+ const originalRename = fs.renameSync;
+ let installed = 0;
+ fs.renameSync = (source, destination) => {
+ if (String(source).endsWith('.tmp') && ++installed === 2) {
+ throw new Error('injected rename failure');
+ }
+ return originalRename(source, destination);
+ };
+
+ try {
+ assert.throws(() => runInit({ projectRoot }), /injected rename failure/);
+ } finally {
+ fs.renameSync = originalRename;
+ }
+
+ assert.deepEqual(snapshotTree(projectRoot), before);
+});
+
+test('detects supported package managers without executing them', () => {
+ for (const [lockfile, expected, install] of [
+ ['package-lock.json', 'npm', 'npm install'],
+ ['yarn.lock', 'yarn', 'yarn install'],
+ ['pnpm-lock.yaml', 'pnpm', 'pnpm install'],
+ ['bun.lock', 'bun', 'bun install'],
+ ]) {
+ const projectRoot = createProject({ lockfile });
+ assert.equal(detectPackageManager(projectRoot), expected);
+ assert.equal(
+ runInit({ dryRun: true, projectRoot }).installCommand,
+ install
+ );
+ }
+});
+
+test('detects package manager metadata at the workspace root', () => {
+ const workspaceRoot = fs.mkdtempSync(
+ path.join(os.tmpdir(), 'repack-expo-workspace-')
+ );
+ fs.writeFileSync(
+ path.join(workspaceRoot, 'package.json'),
+ '{"private":true,"packageManager":"yarn@4.9.2"}\n'
+ );
+ fs.writeFileSync(path.join(workspaceRoot, 'yarn.lock'), '');
+ const projectRoot = path.join(workspaceRoot, 'apps', 'fixture');
+ fs.mkdirSync(projectRoot, { recursive: true });
+
+ assert.equal(detectPackageManager(projectRoot), 'yarn');
+});
+
+test('requires force before replacing an incompatible Rspack config', () => {
+ const projectRoot = createProject();
+ const rspackPath = path.join(projectRoot, 'rspack.config.mjs');
+ fs.writeFileSync(rspackPath, 'export default { custom: true };\n');
+ const before = snapshotTree(projectRoot);
+
+ const refused = runInit({ projectRoot });
+ assert.equal(refused.ok, false);
+ assert.equal(refused.diagnostics[0].code, 'RSPACK_CONFIG_CONFLICT');
+ assert.deepEqual(snapshotTree(projectRoot), before);
+
+ const forced = runInit({ force: true, projectRoot });
+ assert.equal(forced.ok, true);
+ assert.match(fs.readFileSync(rspackPath, 'utf8'), /new ExpoPlugin/);
+});
+
+test('force replaces the single discovered Rspack config without shadowing it', () => {
+ for (const configName of ['rspack.config.ts', 'rspack.config.cjs']) {
+ const projectRoot = createProject();
+ const rspackPath = path.join(projectRoot, configName);
+ fs.writeFileSync(rspackPath, 'module.exports = { custom: true };\n');
+ const before = snapshotTree(projectRoot);
+
+ const result = runInit({ force: true, projectRoot });
+
+ assert.equal(result.ok, true, JSON.stringify(result.diagnostics));
+ assert.notDeepEqual(snapshotTree(projectRoot), before);
+ assert.match(fs.readFileSync(rspackPath, 'utf8'), /new ExpoPlugin/);
+ if (configName.endsWith('.cjs')) {
+ assert.match(fs.readFileSync(rspackPath, 'utf8'), /module\.exports/);
+ }
+ assert.equal(
+ fs.existsSync(path.join(projectRoot, 'rspack.config.mjs')),
+ false
+ );
+ }
+});
+
+test('CLI emits JSON and maps check/doctor errors to a nonzero status without writes', () => {
+ const projectRoot = createProject();
+ const cli = path.resolve(__dirname, '../dist/cli/bin.js');
+ const before = snapshotTree(projectRoot);
+ const check = spawnSync(
+ process.execPath,
+ [cli, 'init', '--check', '--json'],
+ {
+ cwd: projectRoot,
+ encoding: 'utf8',
+ }
+ );
+
+ assert.equal(check.status, 1);
+ assert.deepEqual(JSON.parse(check.stdout).changedFiles.sort(), [
+ 'app.json',
+ 'package.json',
+ 'react-native.config.js',
+ 'rspack.config.mjs',
+ ]);
+ assert.equal(check.stderr, '');
+ assert.deepEqual(snapshotTree(projectRoot), before);
+
+ const doctor = spawnSync(process.execPath, [cli, 'doctor', '--json'], {
+ cwd: projectRoot,
+ encoding: 'utf8',
+ });
+ assert.equal(doctor.status, 1);
+ assert.ok(JSON.parse(doctor.stdout).errorCount > 0);
+ assert.equal(doctor.stderr, '');
+ assert.deepEqual(snapshotTree(projectRoot), before);
+});
+
+test('init refuses ambiguous or conflicting user configuration atomically', () => {
+ const dynamicRoot = createProject();
+ fs.writeFileSync(
+ path.join(dynamicRoot, 'app.config.js'),
+ 'module.exports = ({ config }) => config;\n'
+ );
+ const dynamicBefore = snapshotTree(dynamicRoot);
+ const dynamic = runInit({ force: true, projectRoot: dynamicRoot });
+ assert.equal(dynamic.ok, false);
+ assert.equal(dynamic.diagnostics[0].code, 'DYNAMIC_EXPO_CONFIG');
+ assert.deepEqual(snapshotTree(dynamicRoot), dynamicBefore);
+
+ const conflictRoot = createProject({
+ expo: {
+ updates: { checkAutomatically: 'ON_LOAD', enabled: true },
+ },
+ });
+ const conflictBefore = snapshotTree(conflictRoot);
+ const conflict = runInit({ projectRoot: conflictRoot });
+ assert.equal(conflict.ok, false);
+ assert.ok(
+ conflict.diagnostics.some(({ code }) => code === 'EXPO_UPDATES_CONFLICT')
+ );
+ assert.deepEqual(snapshotTree(conflictRoot), conflictBefore);
+});
+
+test('init preserves additional disabled Updates configuration', () => {
+ const projectRoot = createProject({
+ expo: {
+ updates: { checkAutomatically: 'ON_ERROR_RECOVERY', enabled: false },
+ },
+ });
+ assert.equal(runInit({ projectRoot }).ok, true);
+ const updates = JSON.parse(
+ fs.readFileSync(path.join(projectRoot, 'app.json'), 'utf8')
+ ).expo.updates;
+ assert.deepEqual(updates, {
+ checkAutomatically: 'ON_ERROR_RECOVERY',
+ enabled: false,
+ });
+});
+
+test('init removes redundant supported Expo runtime defaults from app config', () => {
+ const projectRoot = createProject({
+ expo: { jsEngine: 'hermes', newArchEnabled: true },
+ });
+
+ const result = runInit({ projectRoot });
+ const expo = JSON.parse(
+ fs.readFileSync(path.join(projectRoot, 'app.json'), 'utf8')
+ ).expo;
+
+ assert.equal(result.ok, true, JSON.stringify(result.diagnostics));
+ assert.equal('newArchEnabled' in expo, false);
+ assert.equal('jsEngine' in expo, false);
+});
+
+test('init rejects explicit supported Expo runtime opt-outs without writing', () => {
+ const projectRoot = createProject({
+ expo: { jsEngine: 'jsc', newArchEnabled: false },
+ });
+ const before = snapshotTree(projectRoot);
+
+ const result = runInit({ projectRoot });
+ const codes = result.diagnostics.map(({ code }) => code);
+
+ assert.equal(result.ok, false);
+ assert.ok(codes.includes('EXPO_NEW_ARCH_CONFLICT'));
+ assert.ok(codes.includes('EXPO_JS_ENGINE_CONFLICT'));
+ assert.deepEqual(snapshotTree(projectRoot), before);
+});
+
+test('doctor detects stale generated native runtime settings without writing', () => {
+ const projectRoot = createProject();
+ assert.equal(runInit({ projectRoot }).ok, true);
+ fs.mkdirSync(path.join(projectRoot, 'ios', 'Fixture', 'Supporting'), {
+ recursive: true,
+ });
+ fs.writeFileSync(
+ path.join(projectRoot, 'ios', 'Fixture', 'AppDelegate.swift'),
+ '// repack-expo-ios-bundle-url\n'
+ );
+ fs.writeFileSync(
+ path.join(projectRoot, 'ios', 'project.pbxproj'),
+ '// repack-expo-ios-bundle-command\n'
+ );
+ fs.writeFileSync(
+ path.join(projectRoot, 'ios', 'Fixture', 'Supporting', 'Expo.plist'),
+ 'EXUpdatesEnabled\n'
+ );
+ fs.writeFileSync(
+ path.join(projectRoot, 'ios', 'Podfile.properties.json'),
+ '{"expo.jsEngine":"jsc"}\n'
+ );
+ fs.mkdirSync(path.join(projectRoot, 'android', 'app'), { recursive: true });
+ fs.writeFileSync(
+ path.join(projectRoot, 'android', 'app', 'build.gradle'),
+ '// repack-expo-android-bundle-command\n'
+ );
+ fs.writeFileSync(
+ path.join(projectRoot, 'android', 'app', 'MainApplication.kt'),
+ '// repack-expo-android-main-module\n'
+ );
+ fs.writeFileSync(
+ path.join(projectRoot, 'android', 'app', 'AndroidManifest.xml'),
+ '\n'
+ );
+ fs.writeFileSync(
+ path.join(projectRoot, 'android', 'gradle.properties'),
+ 'newArchEnabled=false\nhermesEnabled=false\n'
+ );
+ const before = snapshotTree(projectRoot);
+
+ const result = runDoctor({ projectRoot });
+ const codes = result.diagnostics.map(({ code }) => code);
+ assert.ok(codes.includes('NATIVE_UPDATES_ACTIVE'));
+ assert.ok(codes.includes('NATIVE_HERMES_MISMATCH'));
+ assert.ok(codes.includes('NATIVE_NEW_ARCH_MISMATCH'));
+ assert.ok(codes.includes('IOS_NATIVE_SEAMS_MISSING'));
+ assert.ok(codes.includes('ANDROID_NATIVE_SEAMS_MISSING'));
+ assert.ok(codes.includes('SCRIPTMANAGER_AUTOLINK_UNVERIFIED'));
+ assert.deepEqual(snapshotTree(projectRoot), before);
+});
+
+test('doctor resolves a plain local package.json main entry', () => {
+ const projectRoot = createProject();
+ const packagePath = path.join(projectRoot, 'package.json');
+ const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
+ packageJson.main = 'index.js';
+ fs.writeFileSync(packagePath, `${JSON.stringify(packageJson, null, 2)}\n`);
+ assert.equal(runInit({ projectRoot }).ok, true);
+
+ const missing = runDoctor({ projectRoot });
+ assert.ok(
+ missing.diagnostics.some(({ code }) => code === 'ENTRY_UNRESOLVED')
+ );
+
+ fs.writeFileSync(path.join(projectRoot, 'index.js'), 'export {};\n');
+ const present = runDoctor({ projectRoot });
+ assert.equal(
+ present.diagnostics.some(({ code }) => code === 'ENTRY_UNRESOLVED'),
+ false
+ );
+});
+
+test('doctor uses the shared entry resolver for both native platforms', () => {
+ const projectRoot = createProject();
+ const packagePath = path.join(projectRoot, 'package.json');
+ const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
+ packageJson.main = 'src/main';
+ fs.writeFileSync(packagePath, `${JSON.stringify(packageJson, null, 2)}\n`);
+ fs.mkdirSync(path.join(projectRoot, 'src'));
+ fs.writeFileSync(
+ path.join(projectRoot, 'src', 'main.ios.ts'),
+ 'export {};\n'
+ );
+ fs.writeFileSync(
+ path.join(projectRoot, 'src', 'main.android.ts'),
+ 'export {};\n'
+ );
+ assert.equal(runInit({ projectRoot }).ok, true);
+
+ const result = runDoctor({ projectRoot });
+
+ assert.equal(
+ result.diagnostics.some(({ code }) => code === 'ENTRY_UNRESOLVED'),
+ false,
+ JSON.stringify(result.diagnostics)
+ );
+});
+
+test('doctor does not invent a default entry when package.json main is absent', () => {
+ const projectRoot = createProject();
+ const packagePath = path.join(projectRoot, 'package.json');
+ const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
+ delete packageJson.main;
+ fs.writeFileSync(packagePath, `${JSON.stringify(packageJson, null, 2)}\n`);
+ assert.equal(runInit({ projectRoot }).ok, true);
+
+ const result = runDoctor({ projectRoot });
+
+ assert.ok(
+ result.diagnostics.some(
+ ({ code, message }) =>
+ code === 'ENTRY_UNRESOLVED' &&
+ /does not define package\.json#main/.test(message)
+ )
+ );
+});
+
+test('tuple Config Plugin options drive entry and config paths', () => {
+ const projectRoot = createProject({
+ expo: {
+ plugins: [
+ [
+ '@callstack/repack-expo',
+ { configPath: 'config/rspack.native.mjs', entry: 'src/main' },
+ ],
+ ],
+ },
+ });
+ fs.mkdirSync(path.join(projectRoot, 'src'));
+ fs.writeFileSync(path.join(projectRoot, 'src', 'main.ts'), 'export {};\n');
+
+ const initialized = runInit({ projectRoot });
+
+ assert.equal(initialized.ok, true, JSON.stringify(initialized.diagnostics));
+ assert.ok(initialized.changedFiles.includes('config/rspack.native.mjs'));
+ assert.equal(
+ fs.existsSync(path.join(projectRoot, 'rspack.config.mjs')),
+ false
+ );
+ assert.equal(
+ runDoctor({ projectRoot }).diagnostics.some(
+ ({ code }) =>
+ code === 'ENTRY_UNRESOLVED' || code === 'RSPACK_CONFIG_MISSING'
+ ),
+ false
+ );
+});
+
+test('validates package.json#expo shape and keeps caught JSON output machine-readable', () => {
+ const projectRoot = createProject();
+ fs.rmSync(path.join(projectRoot, 'app.json'));
+ const packagePath = path.join(projectRoot, 'package.json');
+ const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
+ packageJson.expo = 'invalid';
+ fs.writeFileSync(packagePath, `${JSON.stringify(packageJson, null, 2)}\n`);
+
+ const result = runInit({ projectRoot });
+ assert.equal(result.ok, false);
+ assert.ok(
+ result.diagnostics.some(({ code }) => code === 'EXPO_CONFIG_INVALID')
+ );
+
+ const cli = path.resolve(__dirname, '../dist/cli/bin.js');
+ const caught = spawnSync(process.execPath, [cli, 'unknown', '--json'], {
+ cwd: projectRoot,
+ encoding: 'utf8',
+ });
+ assert.equal(caught.status, 1);
+ assert.equal(caught.stderr, '');
+ assert.equal(JSON.parse(caught.stdout).diagnostics[0].code, 'CLI_ERROR');
+});
diff --git a/packages/repack-expo/test/config-plugin.test.cjs b/packages/repack-expo/test/config-plugin.test.cjs
new file mode 100644
index 000000000..7b4cbd846
--- /dev/null
+++ b/packages/repack-expo/test/config-plugin.test.cjs
@@ -0,0 +1,288 @@
+const assert = require('node:assert/strict');
+const test = require('node:test');
+
+const {
+ configureAndroidBuildGradle,
+ configureAndroidMainApplication,
+} = require('../dist/config/android.js');
+const { ConfigPluginError } = require('../dist/config/ConfigPluginError.js');
+const {
+ configureIosAppDelegate,
+ configureIosBundleScript,
+ findIosBundlePhase,
+} = require('../dist/config/ios.js');
+const { normalizeConfigPluginOptions } = require('../dist/config/options.js');
+const { validateExpoConfig } = require('../dist/config/validateExpoConfig.js');
+
+const IOS_APP_DELEGATE = `class ReactNativeDelegate: ExpoReactNativeFactoryDelegate {
+ override func sourceURL(for bridge: RCTBridge) -> URL? {
+ bridge.bundleURL ?? bundleURL()
+ }
+
+ override func bundleURL() -> URL? {
+#if DEBUG
+ return RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: ".expo/.virtual-metro-entry")
+#else
+ return Bundle.main.url(forResource: "main", withExtension: "jsbundle")
+#endif
+ }
+}`;
+
+const IOS_BUNDLE_SCRIPT = `if [[ -z "$CLI_PATH" ]]; then
+ # Use Expo CLI
+ export CLI_PATH="$("$NODE_BINARY" --print "require.resolve('@expo/cli', { paths: [require.resolve('expo/package.json')] })")"
+fi
+if [[ -z "$BUNDLE_COMMAND" ]]; then
+ # Default Expo CLI command for bundling
+ export BUNDLE_COMMAND="export:embed"
+fi
+
+\`"$NODE_BINARY" --print "require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/react-native-xcode.sh'"\`
+`;
+
+const ANDROID_MAIN_APPLICATION = `class MainApplication : Application(), ReactApplication {
+ override val reactHost: ReactHost by lazy {
+ ExpoReactHostFactory.getDefaultReactHost(
+ context = applicationContext,
+ packageList =
+ PackageList(this).packages
+ )
+ }
+}`;
+
+const ANDROID_BUILD_GRADLE = `react {
+ entryFile = file("entry.js")
+ // Use Expo CLI to bundle the app, this ensures the Metro config
+ // works correctly with Expo projects.
+ cliFile = new File(["node", "--print", "require.resolve('@expo/cli', { paths: [require.resolve('expo/package.json')] })"].execute(null, rootDir).text.trim())
+ bundleCommand = "export:embed"
+ autolinkLibrariesWithApp()
+}`;
+
+test('validates the supported Expo native runtime contract', () => {
+ assert.doesNotThrow(() =>
+ validateExpoConfig({
+ name: 'fixture',
+ slug: 'fixture',
+ updates: { enabled: false },
+ })
+ );
+
+ for (const [config, code] of [
+ [
+ { name: 'fixture', newArchEnabled: false, slug: 'fixture' },
+ 'NEW_ARCH_REQUIRED',
+ ],
+ [
+ {
+ jsEngine: 'jsc',
+ name: 'fixture',
+ slug: 'fixture',
+ },
+ 'HERMES_REQUIRED',
+ ],
+ [
+ {
+ name: 'fixture',
+ slug: 'fixture',
+ updates: { enabled: true },
+ },
+ 'ACTIVE_EXPO_UPDATES',
+ ],
+ ]) {
+ assert.throws(
+ () => validateExpoConfig(config),
+ (error) => {
+ assert.equal(error.code, code);
+ assert.equal(typeof error.recovery, 'string');
+ return true;
+ }
+ );
+ }
+});
+
+test('includes recovery guidance in the displayed Config Plugin error', () => {
+ const error = new ConfigPluginError({
+ code: 'INVALID_OPTIONS',
+ message: 'The Config Plugin options are invalid.',
+ recovery: 'Remove the unsupported option.',
+ });
+
+ assert.equal(error.recovery, 'Remove the unsupported option.');
+ assert.equal(
+ error.message,
+ 'The Config Plugin options are invalid.\n\nRecovery: Remove the unsupported option.'
+ );
+ assert.equal(
+ error.toString(),
+ 'RepackExpoConfigPluginError: The Config Plugin options are invalid.\n\nRecovery: Remove the unsupported option.'
+ );
+});
+
+test('configures the iOS Debug URL without disturbing Expo release loading', () => {
+ const once = configureIosAppDelegate(IOS_APP_DELEGATE, 'swift');
+ const twice = configureIosAppDelegate(once, 'swift');
+
+ assert.equal(twice, once);
+ assert.match(once, /forBundleRoot: "index"/);
+ assert.doesNotMatch(once, /virtual-metro-entry/);
+ assert.match(once, /bridge\.bundleURL \?\? bundleURL\(\)/);
+ assert.match(once, /Bundle\.main\.url\(forResource: "main"/);
+ assert.equal(once.match(/repack-expo-ios-bundle-url/g).length, 2);
+ assert.throws(
+ () => configureIosAppDelegate(IOS_APP_DELEGATE, 'objc'),
+ (error) => error.code === 'INCOMPATIBLE_NATIVE_TEMPLATE'
+ );
+
+ assert.throws(
+ () =>
+ configureIosAppDelegate(
+ once.replace('forBundleRoot: "index"', 'forBundleRoot: "custom"'),
+ 'swift'
+ ),
+ (error) => error.code === 'INCOMPATIBLE_NATIVE_TEMPLATE'
+ );
+});
+
+test('adds the final Re.Pack iOS bundle command override once', () => {
+ const once = configureIosBundleScript(IOS_BUNDLE_SCRIPT);
+ const twice = configureIosBundleScript(once);
+
+ assert.equal(twice, once);
+ assert.equal(once.match(/repack-expo-ios-bundle-command/g).length, 2);
+ assert.match(once, /@react-native-community\/cli\/build\/bin\.js/);
+ const invocationIndex = once.indexOf('react-native-xcode.sh');
+ assert.ok(
+ once.lastIndexOf('export BUNDLE_COMMAND="bundle"') < invocationIndex
+ );
+ assert.ok(once.lastIndexOf('export CLI_PATH=') < invocationIndex);
+
+ assert.throws(
+ () =>
+ configureIosBundleScript(
+ once.replace(
+ 'export BUNDLE_COMMAND="bundle"',
+ 'export BUNDLE_COMMAND="custom"'
+ )
+ ),
+ (error) => error.code === 'INCOMPATIBLE_NATIVE_TEMPLATE'
+ );
+ assert.throws(
+ () =>
+ configureIosBundleScript(
+ once.replace(
+ '# @generated end repack-expo-ios-bundle-command',
+ 'export CUSTOM_FLAG="1"\n# @generated end repack-expo-ios-bundle-command'
+ )
+ ),
+ (error) => error.code === 'INCOMPATIBLE_NATIVE_TEMPLATE'
+ );
+ assert.throws(
+ () =>
+ configureIosBundleScript(
+ IOS_BUNDLE_SCRIPT.replace(
+ 'export BUNDLE_COMMAND="export:embed"',
+ 'export BUNDLE_COMMAND="custom"'
+ )
+ ),
+ (error) => error.code === 'INCOMPATIBLE_NATIVE_TEMPLATE'
+ );
+});
+
+test('wraps malformed Xcode application targets in an actionable error', () => {
+ assert.throws(
+ () =>
+ findIosBundlePhase(
+ {
+ getTarget() {
+ return undefined;
+ },
+ },
+ 'Fixture'
+ ),
+ (error) => {
+ assert.equal(error.code, 'INCOMPATIBLE_NATIVE_TEMPLATE');
+ assert.match(error.message, /application target.*build phases/i);
+ assert.match(error.recovery, /expo prebuild --clean/);
+ return true;
+ }
+ );
+
+ assert.throws(
+ () =>
+ findIosBundlePhase(
+ {
+ getTarget() {
+ return { target: {} };
+ },
+ },
+ 'Fixture'
+ ),
+ (error) => error.code === 'INCOMPATIBLE_NATIVE_TEMPLATE'
+ );
+});
+
+test('configures the Expo Android ReactHost main module once', () => {
+ const once = configureAndroidMainApplication(ANDROID_MAIN_APPLICATION, 'kt');
+ const twice = configureAndroidMainApplication(once, 'kt');
+
+ assert.equal(twice, once);
+ assert.match(once, /jsMainModulePath = "index"/);
+ assert.equal(once.match(/repack-expo-android-main-module/g).length, 2);
+ assert.match(once, /ExpoReactHostFactory\.getDefaultReactHost/);
+ assert.throws(
+ () => configureAndroidMainApplication(ANDROID_MAIN_APPLICATION, 'java'),
+ (error) => error.code === 'INCOMPATIBLE_NATIVE_TEMPLATE'
+ );
+});
+
+test('replaces only the Expo Android bundle command block', () => {
+ const once = configureAndroidBuildGradle(ANDROID_BUILD_GRADLE);
+ const twice = configureAndroidBuildGradle(once);
+
+ assert.equal(twice, once);
+ assert.doesNotMatch(once, /@expo\/cli|export:embed/);
+ assert.match(once, /@react-native-community\/cli\/build\/bin\.js/);
+ assert.match(once, /bundleCommand = "bundle"/);
+ assert.match(once, /entryFile = file\("entry\.js"\)/);
+ assert.match(once, /autolinkLibrariesWithApp\(\)/);
+ assert.equal(once.match(/repack-expo-android-bundle-command/g).length, 2);
+});
+
+test('plumbs custom entry and Rspack config into native bundle commands', () => {
+ const options = {
+ configPath: 'config/rspack.native.mjs',
+ entry: 'src/main.ts',
+ };
+ const ios = configureIosBundleScript(IOS_BUNDLE_SCRIPT, options);
+ const android = configureAndroidBuildGradle(ANDROID_BUILD_GRADLE, options);
+
+ assert.match(ios, /ENTRY_FILE="\$PROJECT_ROOT\/src\/main\.ts"/);
+ assert.match(
+ ios,
+ /BUNDLE_CONFIG="\$PROJECT_ROOT\/config\/rspack\.native\.mjs"/
+ );
+ assert.match(
+ android,
+ /entryFile = rootProject\.file\("\.\.\/src\/main\.ts"\)/
+ );
+ assert.match(
+ android,
+ /bundleConfig = rootProject\.file\("\.\.\/config\/rspack\.native\.mjs"\)/
+ );
+ assert.equal(configureIosBundleScript(ios, options), ios);
+ assert.equal(configureAndroidBuildGradle(android, options), android);
+});
+
+test('rejects malformed Config Plugin options actionably', () => {
+ for (const options of [null, [], 'rspack.config.mjs', { entry: '' }]) {
+ assert.throws(
+ () => normalizeConfigPluginOptions(options),
+ (error) => {
+ assert.equal(error.code, 'INVALID_OPTIONS');
+ assert.equal(typeof error.recovery, 'string');
+ return true;
+ }
+ );
+ }
+});
diff --git a/packages/repack-expo/test/documentation.test.cjs b/packages/repack-expo/test/documentation.test.cjs
new file mode 100644
index 000000000..98c757412
--- /dev/null
+++ b/packages/repack-expo/test/documentation.test.cjs
@@ -0,0 +1,226 @@
+const assert = require('node:assert/strict');
+const { spawnSync } = require('node:child_process');
+const fs = require('node:fs');
+const path = require('node:path');
+const test = require('node:test');
+const { createProject } = require('./helpers/createCliProject.cjs');
+
+const packageRoot = path.resolve(__dirname, '..');
+const repositoryRoot = path.resolve(packageRoot, '..', '..');
+const cliPath = path.join(packageRoot, 'dist', 'cli', 'bin.js');
+const readme = fs.readFileSync(path.join(packageRoot, 'README.md'), 'utf8');
+const hostRoot = path.join(repositoryRoot, 'apps', 'tester-expo');
+const widgetRoot = path.join(repositoryRoot, 'apps', 'tester-expo-widget');
+
+function readJson(filePath) {
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
+}
+
+function readReadmeSection(heading, nextHeading) {
+ const start = readme.indexOf(heading);
+ assert.notEqual(start, -1, `README is missing ${heading}`);
+ const end = readme.indexOf(nextHeading, start + heading.length);
+ assert.notEqual(end, -1, `README is missing ${nextHeading}`);
+ return readme.slice(start, end);
+}
+
+function runCli(projectRoot, arguments_) {
+ return spawnSync(process.execPath, [cliPath, ...arguments_], {
+ cwd: projectRoot,
+ encoding: 'utf8',
+ });
+}
+
+test('the documented init, check, and doctor sequence works from a clean static Expo fixture', () => {
+ const projectRoot = createProject({
+ expoName: 'Documented Expo App',
+ includeCommunityCli: false,
+ name: 'documented-expo-app',
+ prefix: 'repack-expo-documentation-',
+ slug: 'documented-expo-app',
+ });
+ try {
+ const initialized = runCli(projectRoot, ['init']);
+ assert.equal(
+ initialized.status,
+ 0,
+ `${initialized.stdout}\n${initialized.stderr}`
+ );
+ assert.match(initialized.stdout, /Changed:/);
+ assert.match(initialized.stdout, /then expo prebuild/);
+
+ const dependencyInstalledPackage = JSON.parse(
+ fs.readFileSync(path.join(projectRoot, 'package.json'), 'utf8')
+ );
+ dependencyInstalledPackage.devDependencies['@react-native-community/cli'] =
+ '^20.0.0';
+ fs.writeFileSync(
+ path.join(projectRoot, 'package.json'),
+ `${JSON.stringify(dependencyInstalledPackage, null, 2)}\n`
+ );
+
+ const checked = runCli(projectRoot, ['init', '--check']);
+ assert.equal(checked.status, 0, `${checked.stdout}\n${checked.stderr}`);
+ assert.match(checked.stdout, /setup is up to date/);
+
+ const doctor = runCli(projectRoot, ['doctor']);
+ assert.equal(doctor.status, 0, `${doctor.stdout}\n${doctor.stderr}`);
+ assert.match(doctor.stdout, /Doctor passed/);
+
+ const packageJson = JSON.parse(
+ fs.readFileSync(path.join(projectRoot, 'package.json'), 'utf8')
+ );
+ assert.equal(
+ packageJson.scripts['repack:start'],
+ 'react-native webpack-start'
+ );
+ assert.equal(
+ packageJson.scripts['repack:ios'],
+ 'expo run:ios --no-bundler'
+ );
+ assert.equal(
+ packageJson.scripts['repack:android'],
+ 'expo run:android --no-bundler'
+ );
+
+ const expo = JSON.parse(
+ fs.readFileSync(path.join(projectRoot, 'app.json'), 'utf8')
+ ).expo;
+ assert.deepEqual(expo.updates, { enabled: false });
+ assert.ok(expo.plugins.includes('@callstack/repack-expo'));
+ assert.equal('newArchEnabled' in expo, false);
+ assert.equal('jsEngine' in expo, false);
+ } finally {
+ fs.rmSync(projectRoot, { force: true, recursive: true });
+ }
+});
+
+test('the guide keeps every workflow and unsupported boundary searchable', () => {
+ for (const fragment of [
+ 'npx @callstack/repack-expo init',
+ 'npx expo prebuild --clean',
+ 'npm run repack:start',
+ 'npm run repack:ios',
+ 'npm run repack:android',
+ 'EAS_BUILD_DISABLE_BUNDLE_JAVASCRIPT_STEP',
+ '## Migrating from Expo Metro',
+ '## Troubleshooting',
+ '## Support matrix',
+ '| Webpack | Unsupported. |',
+ '| Expo Go | Unsupported. |',
+ '| `expo-dev-client` launcher, QR discovery and development manifests | Unsupported. |',
+ '| `expo start` or `expo export` as Re.Pack commands | Unsupported. |',
+ '| Expo Updates, EAS Update and OTA | Unsupported. |',
+ '| Re.Pack Module Federation v2 | Supported as explicit application-owned configuration. |',
+ '| Expo host -> Expo widget | Supported and validated on iOS and Android. |',
+ '| Expo host -> ordinary Re.Pack MF v2 widget | Supported and validated. |',
+ '| Ordinary Re.Pack host -> Expo widget | Unsupported; the Expo native contract is not guaranteed. |',
+ '| Module Federation v1 or raw `@module-federation/enhanced/rspack` | Unsupported. |',
+ '| Metro Module Federation or `@module-federation/metro` | Unsupported. |',
+ '| Production MF v2 widget artifacts | Supported as platform-specific remote deployments. |',
+ '| Generic remote chunks outside MF v2 | Unsupported by the Expo integration contract. |',
+ '| Old Architecture or JSC | Unsupported. |',
+ '| Web, SSR, React Server Components and DOM Components | Unsupported. |',
+ '| Executable `app.config.*` mutation | Unsupported; static Expo config is required by `init`. |',
+ ]) {
+ assert.ok(readme.includes(fragment), `README is missing ${fragment}`);
+ }
+ assert.match(
+ readme,
+ /do not copy or maintain the generated Swift,\s+Objective-C, Kotlin, Gradle or Xcode snippets by hand/iu
+ );
+});
+
+test('the Module Federation v2 guide matches the validated host and widget fixtures', () => {
+ const hostPackage = readJson(path.join(hostRoot, 'package.json'));
+ const widgetPackage = readJson(path.join(widgetRoot, 'package.json'));
+ const hostConfig = fs.readFileSync(
+ path.join(hostRoot, 'rspack.config.mjs'),
+ 'utf8'
+ );
+ const widgetConfig = fs.readFileSync(
+ path.join(widgetRoot, 'rspack.config.mjs'),
+ 'utf8'
+ );
+ const federationGuide = readReadmeSection(
+ '## Module Federation v2',
+ '## Public environment variables'
+ );
+
+ assert.equal(
+ hostPackage.scripts['repack:start'],
+ 'react-native webpack-start'
+ );
+ assert.equal(hostPackage.scripts['repack:ios'], 'expo run:ios --no-bundler');
+ assert.equal(
+ hostPackage.scripts['repack:android'],
+ 'expo run:android --no-bundler'
+ );
+ assert.equal(
+ widgetPackage.scripts['repack:start:remote'],
+ 'react-native webpack-start --port 8082'
+ );
+ assert.equal(
+ widgetPackage.scripts['repack:build:production'],
+ 'pnpm run repack:build:production:ios && pnpm run repack:build:production:android'
+ );
+ assert.equal(
+ widgetPackage.scripts['repack:build:production:ios'],
+ 'node ./scripts/build-production.mjs ios'
+ );
+ assert.equal(
+ widgetPackage.scripts['repack:build:production:android'],
+ 'node ./scripts/build-production.mjs android'
+ );
+
+ for (const fragment of [
+ 'new ExpoPlugin({ platform })',
+ 'Repack.plugins.ModuleFederationPluginV2',
+ 'npm run repack:start:remote',
+ 'npm run repack:build:production:ios',
+ 'npm run repack:build:production:android',
+ "loadRemote('ExpoWidget/Widget')",
+ 'mf-manifest.json',
+ 'EXPO_PUBLIC_*',
+ 'CodeSigningPlugin',
+ ]) {
+ assert.ok(
+ federationGuide.includes(fragment),
+ `Module Federation guide is missing ${fragment}`
+ );
+ }
+
+ assert.match(hostConfig, /ModuleFederationPluginV2/);
+ assert.match(hostConfig, /ExpoWidget@http:\/\/localhost:8082/);
+ assert.doesNotMatch(hostConfig, /extraChunks/);
+ assert.match(widgetConfig, /ModuleFederationPluginV2/);
+ assert.match(widgetConfig, /'\.\/Widget': '\.\/src\/Widget'/);
+ assert.match(widgetConfig, /auxiliaryAssetsPath: remoteOutputPath/);
+ assert.match(widgetConfig, /include: \/\.\*\//);
+ assert.match(widgetConfig, /type: 'remote'/);
+ assert.match(widgetConfig, /outputPath: remoteOutputPath/);
+
+ assert.equal(
+ federationGuide.match(/export default \(env\) =>/gu)?.length,
+ 2,
+ 'host and widget examples must be complete Rspack configs'
+ );
+
+ assert.match(federationGuide, /widget cannot deliver native dependencies/iu);
+ assert.match(federationGuide, /host owns Expo Router/iu);
+ assert.match(federationGuide, /not Expo Updates or OTA/iu);
+ assert.match(federationGuide, /production remotes must use HTTPS/iu);
+ assert.match(federationGuide, /same\s+ScriptManager/iu);
+ assert.match(federationGuide, /Config Plugin.*native/isu);
+ assert.match(federationGuide, /host (?:launch bundle|binary)/iu);
+ assert.doesNotMatch(
+ federationGuide,
+ /\b(?:copy|edit|modify|patch)\b[^.\n]*(?:ios\/|android\/|native)/iu
+ );
+ assert.doesNotMatch(
+ federationGuide,
+ /(?:configureScriptManager|globalThis\.ErrorUtils|new ScriptManager|setup\.ts)/u
+ );
+ assert.doesNotMatch(federationGuide, /second ScriptManager/iu);
+ assert.doesNotMatch(federationGuide, /bundle-widget/iu);
+});
diff --git a/packages/repack-expo/test/entry-resolution.test.cjs b/packages/repack-expo/test/entry-resolution.test.cjs
new file mode 100644
index 000000000..eaa360732
--- /dev/null
+++ b/packages/repack-expo/test/entry-resolution.test.cjs
@@ -0,0 +1,230 @@
+const assert = require('node:assert/strict');
+const fs = require('node:fs');
+const os = require('node:os');
+const path = require('node:path');
+const test = require('node:test');
+
+const {
+ findExpoProjectRoot,
+ resolveExpoEntry,
+} = require('../dist/rspack/entry/resolveExpoEntry.js');
+
+function createProject(options = {}) {
+ const { name = 'fixture' } = options;
+ const projectRoot = fs.mkdtempSync(
+ path.join(os.tmpdir(), 'repack-expo-entry-')
+ );
+ const packageJson = { dependencies: { expo: '56.0.0' }, name };
+
+ if (!Object.hasOwn(options, 'main') || options.main !== undefined) {
+ packageJson.main = options.main ?? 'index.js';
+ }
+
+ fs.writeFileSync(
+ path.join(projectRoot, 'package.json'),
+ JSON.stringify(packageJson)
+ );
+ return projectRoot;
+}
+
+test('resolves a plain package.json main entry', () => {
+ const projectRoot = createProject();
+ fs.writeFileSync(path.join(projectRoot, 'index.js'), 'module.exports = {};');
+
+ const result = resolveExpoEntry({ platform: 'ios', projectRoot });
+
+ assert.equal(result.projectRoot, projectRoot);
+ assert.equal(result.physicalProjectRoot, fs.realpathSync(projectRoot));
+ assert.equal(
+ result.entryPath,
+ fs.realpathSync(path.join(projectRoot, 'index.js'))
+ );
+ assert.equal(result.request, 'index.js');
+ assert.equal(result.packageName, 'fixture');
+ assert.equal(result.platform, 'ios');
+ assert.equal(Object.isFrozen(result), true);
+});
+
+test('uses native platform extension precedence for project files', () => {
+ const projectRoot = createProject({ main: './index' });
+ fs.writeFileSync(path.join(projectRoot, 'index.native.tsx'), '');
+ fs.writeFileSync(path.join(projectRoot, 'index.ios.tsx'), '');
+ fs.writeFileSync(path.join(projectRoot, 'index.tsx'), '');
+
+ assert.equal(
+ resolveExpoEntry({ platform: 'ios', projectRoot }).entryPath,
+ fs.realpathSync(path.join(projectRoot, 'index.ios.tsx'))
+ );
+ assert.equal(
+ resolveExpoEntry({ platform: 'android', projectRoot }).entryPath,
+ fs.realpathSync(path.join(projectRoot, 'index.native.tsx'))
+ );
+});
+
+test('allows an explicit entry override without changing package.json', () => {
+ const projectRoot = createProject({ main: 'index.js' });
+ fs.writeFileSync(path.join(projectRoot, 'custom.android.ts'), '');
+
+ const result = resolveExpoEntry({
+ entry: './custom',
+ platform: 'android',
+ projectRoot,
+ });
+
+ assert.equal(result.request, './custom');
+ assert.equal(
+ result.entryPath,
+ fs.realpathSync(path.join(projectRoot, 'custom.android.ts'))
+ );
+});
+
+test('resolves the Expo Router package entry', () => {
+ const projectRoot = createProject({ main: 'expo-router/entry' });
+ const routerRoot = path.join(projectRoot, 'node_modules', 'expo-router');
+ fs.mkdirSync(routerRoot, { recursive: true });
+ fs.writeFileSync(
+ path.join(routerRoot, 'package.json'),
+ JSON.stringify({
+ exports: { './entry': './entry.js' },
+ name: 'expo-router',
+ })
+ );
+ fs.writeFileSync(path.join(routerRoot, 'entry.js'), 'module.exports = {};');
+
+ const result = resolveExpoEntry({ platform: 'android', projectRoot });
+
+ assert.equal(result.request, 'expo-router/entry');
+ assert.equal(
+ result.entryPath,
+ fs.realpathSync(path.join(routerRoot, 'entry.js'))
+ );
+});
+
+test('discovers the nearest Expo project from a nested source path', () => {
+ const projectRoot = createProject();
+ const nestedFile = path.join(projectRoot, 'src', 'features', 'screen.tsx');
+ fs.mkdirSync(path.dirname(nestedFile), { recursive: true });
+ fs.writeFileSync(nestedFile, '');
+
+ assert.equal(findExpoProjectRoot(nestedFile), projectRoot);
+});
+
+test('preserves the logical monorepo symlink root', (context) => {
+ if (process.platform === 'win32') {
+ context.skip('Directory symlink permissions vary on Windows.');
+ return;
+ }
+
+ const physicalRoot = createProject();
+ fs.writeFileSync(path.join(physicalRoot, 'index.js'), '');
+ const workspaceRoot = fs.mkdtempSync(
+ path.join(os.tmpdir(), 'repack-expo-workspace-')
+ );
+ const logicalRoot = path.join(workspaceRoot, 'apps', 'fixture');
+ fs.mkdirSync(path.dirname(logicalRoot), { recursive: true });
+ fs.symlinkSync(physicalRoot, logicalRoot, 'dir');
+
+ const result = resolveExpoEntry({
+ platform: 'ios',
+ projectRoot: logicalRoot,
+ });
+
+ assert.equal(result.projectRoot, logicalRoot);
+ assert.equal(result.physicalProjectRoot, fs.realpathSync(physicalRoot));
+ assert.equal(
+ result.entryPath,
+ fs.realpathSync(path.join(physicalRoot, 'index.js'))
+ );
+});
+
+test('rejects an Expo Metro virtual entry with recovery guidance', () => {
+ const projectRoot = createProject({ main: '.expo/.virtual-metro-entry' });
+
+ assert.throws(
+ () => resolveExpoEntry({ platform: 'ios', projectRoot }),
+ (error) => {
+ assert.equal(error.code, 'METRO_ENTRY_UNSUPPORTED');
+ assert.equal(error.projectRoot, projectRoot);
+ assert.equal(error.requestedEntry, '.expo/.virtual-metro-entry');
+ assert.match(error.recovery, /package\.json#main/);
+ return true;
+ }
+ );
+});
+
+test('reports an unresolvable entry with platform and recovery guidance', () => {
+ const projectRoot = createProject({ main: 'missing-entry' });
+
+ assert.throws(
+ () => resolveExpoEntry({ platform: 'android', projectRoot }),
+ (error) => {
+ assert.equal(error.code, 'ENTRY_NOT_FOUND');
+ assert.equal(error.platform, 'android');
+ assert.equal(error.projectRoot, projectRoot);
+ assert.equal(error.requestedEntry, 'missing-entry');
+ assert.match(error.message, /missing-entry/);
+ assert.match(error.recovery, /package\.json#main/);
+ return true;
+ }
+ );
+});
+
+test('requires package.json main', () => {
+ const projectRoot = createProject({ main: undefined });
+
+ assert.throws(
+ () => resolveExpoEntry({ platform: 'ios', projectRoot }),
+ (error) => {
+ assert.equal(error.code, 'ENTRY_NOT_DEFINED');
+ assert.match(error.recovery, /expo-router\/entry/);
+ return true;
+ }
+ );
+});
+
+test('rejects an explicit non-Expo package root', () => {
+ const projectRoot = fs.mkdtempSync(
+ path.join(os.tmpdir(), 'repack-non-expo-entry-')
+ );
+ fs.writeFileSync(
+ path.join(projectRoot, 'package.json'),
+ JSON.stringify({ main: 'index.js', name: 'not-expo' })
+ );
+ fs.writeFileSync(path.join(projectRoot, 'index.js'), '');
+
+ assert.throws(
+ () => resolveExpoEntry({ platform: 'ios', projectRoot }),
+ (error) => {
+ assert.equal(error.code, 'EXPO_PROJECT_NOT_FOUND');
+ assert.match(error.recovery, /Install expo/);
+ return true;
+ }
+ );
+});
+
+test('reports malformed package.json before resolving an entry', () => {
+ const projectRoot = fs.mkdtempSync(
+ path.join(os.tmpdir(), 'repack-invalid-package-')
+ );
+ fs.writeFileSync(path.join(projectRoot, 'package.json'), '[]');
+
+ assert.throws(
+ () => resolveExpoEntry({ platform: 'ios', projectRoot }),
+ (error) => {
+ assert.equal(error.code, 'INVALID_PACKAGE_JSON');
+ assert.match(error.recovery, /Fix package\.json/);
+ return true;
+ }
+ );
+});
+
+test('rejects unsupported platforms before reading project files', () => {
+ assert.throws(
+ () => resolveExpoEntry({ platform: 'web', projectRoot: '/missing' }),
+ (error) => {
+ assert.equal(error.code, 'UNSUPPORTED_PLATFORM');
+ assert.match(error.recovery, /ios or android/);
+ return true;
+ }
+ );
+});
diff --git a/packages/repack-expo/test/expo-plugin.test.cjs b/packages/repack-expo/test/expo-plugin.test.cjs
new file mode 100644
index 000000000..67170ed54
--- /dev/null
+++ b/packages/repack-expo/test/expo-plugin.test.cjs
@@ -0,0 +1,1029 @@
+const assert = require('node:assert/strict');
+const fs = require('node:fs');
+const os = require('node:os');
+const path = require('node:path');
+const test = require('node:test');
+const { rspack } = require('@rspack/core');
+const {
+ plugins: { ModuleFederationPluginV2 },
+} = require('@callstack/repack');
+
+const { ExpoPlugin, ExpoPluginError } = require('../dist/rspack/index.js');
+const {
+ withExpoBabelCaller,
+} = require('../dist/rspack/loaders/withExpoBabelCaller.js');
+const expoRouterEntryLoader =
+ require('../dist/rspack/loaders/expoRouterEntryLoader.js').default;
+
+function getExpoBabelLoaderOptions(
+ loaderOptions,
+ {
+ dependencies = [],
+ missingDependencies = [],
+ resourcePath = '/project/app/index.tsx',
+ } = {}
+) {
+ const babelLoader = require('@callstack/repack/babel-loader');
+ const expoBabelLoaderPath = require.resolve(
+ '../dist/rspack/loaders/expoBabelLoader.js'
+ );
+ const originalBabelLoader = babelLoader.default;
+ let capturedOptions;
+
+ babelLoader.default = function captureOptions() {
+ capturedOptions = this.getOptions();
+ };
+ delete require.cache[expoBabelLoaderPath];
+
+ try {
+ const expoBabelLoader = require(expoBabelLoaderPath).default;
+ expoBabelLoader.call(
+ {
+ addDependency(filename) {
+ dependencies.push(filename);
+ },
+ addMissingDependency(filename) {
+ missingDependencies.push(filename);
+ },
+ getOptions() {
+ return loaderOptions;
+ },
+ resourcePath,
+ },
+ ''
+ );
+ } finally {
+ babelLoader.default = originalBabelLoader;
+ delete require.cache[expoBabelLoaderPath];
+ }
+
+ return capturedOptions;
+}
+
+class RepackPlugin {}
+
+class DefinePlugin {
+ constructor(definitions) {
+ this.definitions = definitions;
+ }
+
+ apply(compiler) {
+ compiler.appliedDefinitions.push(this.definitions);
+ }
+}
+
+test('uses the ExpoModulesPlugin constructor name expected by Re.Pack CLI validation', () => {
+ assert.equal(new ExpoPlugin().constructor.name, 'ExpoModulesPlugin');
+});
+
+function createPackage(packageRoot, name, version) {
+ fs.mkdirSync(packageRoot, { recursive: true });
+ fs.writeFileSync(
+ path.join(packageRoot, 'package.json'),
+ JSON.stringify({ name, version })
+ );
+}
+
+function createProject({
+ main = 'index.js',
+ rootAssetsRegistryVersion,
+ routerRoot = 'app',
+} = {}) {
+ const projectRoot = fs.realpathSync(
+ fs.mkdtempSync(path.join(os.tmpdir(), 'repack-expo-plugin-'))
+ );
+ fs.writeFileSync(
+ path.join(projectRoot, 'package.json'),
+ JSON.stringify({ dependencies: { expo: '56.0.0' }, main })
+ );
+ const reactNativeRoot = path.join(
+ projectRoot,
+ 'node_modules',
+ 'react-native'
+ );
+ createPackage(reactNativeRoot, 'react-native', '0.86.2');
+ fs.writeFileSync(
+ path.join(reactNativeRoot, 'rn-get-polyfills.js'),
+ 'module.exports = () => [];'
+ );
+ createPackage(
+ path.join(
+ reactNativeRoot,
+ 'node_modules',
+ '@react-native',
+ 'assets-registry'
+ ),
+ '@react-native/assets-registry',
+ '0.86.2'
+ );
+ if (rootAssetsRegistryVersion) {
+ createPackage(
+ path.join(
+ projectRoot,
+ 'node_modules',
+ '@react-native',
+ 'assets-registry'
+ ),
+ '@react-native/assets-registry',
+ rootAssetsRegistryVersion
+ );
+ }
+ if (main === 'expo-router/entry') {
+ const expoRouterRoot = path.join(
+ projectRoot,
+ 'node_modules',
+ 'expo-router'
+ );
+ fs.mkdirSync(path.join(expoRouterRoot, 'build'), { recursive: true });
+ fs.writeFileSync(
+ path.join(expoRouterRoot, 'package.json'),
+ JSON.stringify({
+ exports: { './entry': './entry.js' },
+ name: 'expo-router',
+ })
+ );
+ fs.writeFileSync(
+ path.join(expoRouterRoot, 'entry.js'),
+ "require('@expo/metro-runtime');"
+ );
+ fs.writeFileSync(
+ path.join(expoRouterRoot, 'build', 'qualified-entry.js'),
+ 'exports.App = function App() {};'
+ );
+ fs.writeFileSync(
+ path.join(expoRouterRoot, 'build', 'renderRootComponent.js'),
+ 'exports.renderRootComponent = function renderRootComponent() {};'
+ );
+ } else {
+ const entryPath = path.join(projectRoot, main);
+ fs.mkdirSync(path.dirname(entryPath), { recursive: true });
+ fs.writeFileSync(entryPath, 'module.exports = {};');
+ }
+ fs.mkdirSync(path.join(projectRoot, routerRoot), { recursive: true });
+ return projectRoot;
+}
+
+function installModuleFederationEnhanced(projectRoot) {
+ const scopeRoot = path.join(
+ projectRoot,
+ 'node_modules',
+ '@module-federation'
+ );
+ fs.mkdirSync(scopeRoot, { recursive: true });
+ fs.symlinkSync(
+ fs.realpathSync(
+ path.join(
+ __dirname,
+ '..',
+ '..',
+ 'repack',
+ 'node_modules',
+ '@module-federation',
+ 'enhanced'
+ )
+ ),
+ path.join(scopeRoot, 'enhanced'),
+ 'junction'
+ );
+}
+
+function mockInternalRepackPlugin(plugin, observe = () => {}) {
+ const createRepackPlugin = plugin.createRepackPlugin.bind(plugin);
+ plugin.createRepackPlugin = (platform) => {
+ const internalPlugin = createRepackPlugin(platform);
+ const config = internalPlugin.config;
+ internalPlugin.apply = (compiler) => {
+ observe({ compiler, config, platform });
+ };
+ return internalPlugin;
+ };
+ return plugin;
+}
+
+function createUnitExpoPlugin(options, observe) {
+ return mockInternalRepackPlugin(new ExpoPlugin(options), observe);
+}
+
+function expectedEntryImports(projectRoot, entry = 'index.js') {
+ return [
+ require.resolve('../dist/rspack/runtime/configureScriptManager.js'),
+ fs.realpathSync(path.join(projectRoot, entry)),
+ ];
+}
+
+function testMatchesFilename(test, filename) {
+ if (test instanceof RegExp) {
+ const lastIndex = test.lastIndex;
+ test.lastIndex = 0;
+ const matches = test.test(filename);
+ test.lastIndex = lastIndex;
+ return matches;
+ }
+ if (Array.isArray(test)) {
+ return test.some((condition) => testMatchesFilename(condition, filename));
+ }
+ return typeof test === 'string' && filename.endsWith(test);
+}
+
+function flattenRules(rules) {
+ return rules.flatMap((rule) => {
+ if (typeof rule !== 'object' || rule === null) return [];
+ return [
+ rule,
+ ...flattenRules([...(rule.oneOf ?? []), ...(rule.rules ?? [])]),
+ ];
+ });
+}
+
+function rulesForFilename(rules, filename) {
+ return flattenRules(rules).filter((rule) =>
+ testMatchesFilename(rule.test, filename)
+ );
+}
+
+function ruleUse(rule) {
+ const uses = Array.isArray(rule.use) ? rule.use : [rule.use];
+ return uses.find((use) => use && typeof use === 'object' && use.loader);
+}
+
+function createCompiler({
+ mode = 'development',
+ moduleRules = [],
+ output = {},
+ platform = 'ios',
+ plugins,
+ projectRoot = createProject(),
+ resolve = {},
+ rspack = true,
+} = {}) {
+ const compiler = {
+ appliedDefinitions: [],
+ context: projectRoot,
+ options: {
+ entry: {},
+ externalsPresets: {},
+ mode,
+ module: {
+ rules: moduleRules,
+ },
+ name: platform,
+ output,
+ plugins: plugins ?? [],
+ resolve,
+ },
+ webpack: {
+ DefinePlugin,
+ ...(rspack ? { rspackVersion: '1.6.0' } : {}),
+ },
+ };
+
+ return compiler;
+}
+
+test('configures a native Expo compilation and applies its internal RepackPlugin', () => {
+ const appliedInternalPlugins = [];
+ const expoPlugin = createUnitExpoPlugin(undefined, (application) => {
+ appliedInternalPlugins.push(application);
+ });
+ const compiler = createCompiler({ plugins: [expoPlugin] });
+
+ expoPlugin.apply(compiler);
+
+ assert.deepEqual(compiler.options.entry, {
+ main: {
+ import: expectedEntryImports(compiler.context),
+ },
+ });
+ const babelRules = rulesForFilename(compiler.options.module.rules, 'App.tsx');
+ assert.equal(babelRules.length, 1);
+ const use = ruleUse(babelRules[0]);
+ assert.match(use.loader, /expoBabelLoader\.js$/);
+ assert.deepEqual(use.options.caller, {
+ asyncRoutes: false,
+ baseUrl: '',
+ bundler: 'repack',
+ engine: 'hermes',
+ isDev: true,
+ isHMREnabled: false,
+ isReactServer: false,
+ isServer: false,
+ name: '@callstack/repack-expo',
+ platform: 'ios',
+ preserveEnvVars: true,
+ projectRoot: compiler.context,
+ routerRoot: 'app',
+ supportsStaticESM: true,
+ });
+ assert.deepEqual(compiler.appliedDefinitions, [
+ { 'process.env.EXPO_OS': JSON.stringify('ios') },
+ ]);
+ assert.deepEqual(compiler.options.resolve.modules, [
+ path.join(compiler.context, 'node_modules'),
+ 'node_modules',
+ ]);
+ assert.deepEqual(compiler.options.resolve.mainFields, [
+ 'react-native',
+ 'browser',
+ 'main',
+ ]);
+ assert.equal(compiler.options.output.chunkFilename, '[name].chunk.bundle');
+ assert.equal(appliedInternalPlugins.length, 1);
+ assert.equal(appliedInternalPlugins[0].compiler, compiler);
+ assert.equal(appliedInternalPlugins[0].platform, 'ios');
+});
+
+test('prepends ScriptManager configuration before the resolved app entry', () => {
+ const plugin = createUnitExpoPlugin();
+ const compiler = createCompiler({ plugins: [plugin] });
+
+ plugin.apply(compiler);
+
+ const imports = compiler.options.entry.main.import;
+ assert.match(imports[0], /runtime\/configureScriptManager\.js$/);
+ assert.equal(
+ imports[1],
+ fs.realpathSync(path.join(compiler.context, 'index.js'))
+ );
+});
+
+test('adds a Babel rule when module.rules is empty', () => {
+ const plugin = createUnitExpoPlugin();
+ const compiler = createCompiler({ plugins: [plugin] });
+
+ plugin.apply(compiler);
+
+ const babelRules = rulesForFilename(
+ compiler.options.module.rules,
+ 'feature.native.tsx'
+ );
+ assert.equal(babelRules.length, 1);
+ assert.equal(babelRules[0].type, 'javascript/auto');
+ assert.match(ruleUse(babelRules[0]).loader, /expoBabelLoader\.js$/);
+});
+
+test('adapts an existing Babel rule without losing its options or adding a duplicate', () => {
+ const existingRule = {
+ exclude: /vendor/,
+ test: /\.[jt]sx?$/,
+ type: 'javascript/auto',
+ use: {
+ loader: '@callstack/repack/babel-loader',
+ options: {
+ caller: { custom: true },
+ sourceMaps: false,
+ },
+ },
+ };
+ const plugin = createUnitExpoPlugin();
+ const compiler = createCompiler({
+ moduleRules: [existingRule],
+ plugins: [plugin],
+ });
+
+ plugin.apply(compiler);
+
+ const babelRules = rulesForFilename(compiler.options.module.rules, 'App.tsx');
+ assert.equal(babelRules.length, 1);
+ assert.equal(babelRules[0], existingRule);
+ assert.equal(existingRule.type, 'javascript/auto');
+ assert.equal(String(existingRule.exclude), String(/vendor/));
+ const use = ruleUse(existingRule);
+ assert.match(use.loader, /expoBabelLoader\.js$/);
+ assert.equal(use.options.sourceMaps, false);
+ assert.equal(use.options.caller.custom, true);
+ assert.equal(use.options.caller.platform, 'ios');
+});
+
+test('defaults every extra chunk to local in its internal RepackPlugin', () => {
+ let internalApplication;
+ const plugin = createUnitExpoPlugin(undefined, (application) => {
+ internalApplication = application;
+ });
+ const compiler = createCompiler({
+ mode: 'production',
+ platform: 'android',
+ plugins: [plugin],
+ });
+
+ plugin.apply(compiler);
+
+ assert.equal(internalApplication.platform, 'android');
+ assert.equal(internalApplication.config.platform, 'android');
+ assert.deepEqual(internalApplication.config.extraChunks, [{ type: 'local' }]);
+ assert.equal(
+ compiler.options.module.rules[0].use.options.caller.isDev,
+ false
+ );
+});
+
+test('rejects a nested RepackPlugin platform that conflicts with Expo', () => {
+ const projectRoot = createProject();
+ assert.throws(
+ () =>
+ rspack({
+ context: projectRoot,
+ entry: './ignored.js',
+ mode: 'development',
+ name: 'ios',
+ output: { path: path.join(projectRoot, 'dist') },
+ plugins: [
+ new ExpoPlugin({
+ platform: 'ios',
+ repack: { platform: 'android' },
+ }),
+ ],
+ }),
+ (error) => {
+ assert.equal(error instanceof ExpoPluginError, true);
+ assert.equal(error.code, 'REPACK_PLATFORM_CONFLICT');
+ assert.match(error.message, /android/);
+ assert.match(error.message, /ios/);
+ assert.match(error.recovery, /Remove repack\.platform/);
+ return true;
+ }
+ );
+});
+
+test('normalizes missing and Rspack-default chunk filenames for packaged chunks', () => {
+ for (const chunkFilename of [undefined, '[name].js']) {
+ const plugin = createUnitExpoPlugin();
+ const compiler = createCompiler({
+ output: { chunkFilename },
+ plugins: [plugin],
+ });
+
+ plugin.apply(compiler);
+
+ assert.equal(compiler.options.output.chunkFilename, '[name].chunk.bundle');
+ }
+});
+
+test('preserves an explicit flat chunk filename', () => {
+ const plugin = createUnitExpoPlugin();
+ const compiler = createCompiler({
+ output: { chunkFilename: '[id].[contenthash].bundle' },
+ plugins: [plugin],
+ });
+
+ plugin.apply(compiler);
+
+ assert.equal(
+ compiler.options.output.chunkFilename,
+ '[id].[contenthash].bundle'
+ );
+});
+
+test('rejects nested and dynamic chunk filenames', () => {
+ for (const chunkFilename of [
+ 'chunks/[name].chunk.bundle',
+ 'chunks\\[name].chunk.bundle',
+ () => '[name].chunk.bundle',
+ ]) {
+ const plugin = createUnitExpoPlugin();
+ const compiler = createCompiler({
+ output: { chunkFilename },
+ plugins: [plugin],
+ });
+
+ assert.throws(
+ () => plugin.apply(compiler),
+ (error) => {
+ assert.equal(error instanceof ExpoPluginError, true);
+ assert.equal(error.code, 'INVALID_CHUNK_FILENAME');
+ assert.match(error.recovery, /flat string/);
+ return true;
+ }
+ );
+ }
+});
+
+test('resolves React Native and its matching assets registry from the project runtime', () => {
+ const projectRoot = createProject({ rootAssetsRegistryVersion: '0.84.1' });
+ const plugin = createUnitExpoPlugin();
+ const compiler = createCompiler({
+ plugins: [plugin],
+ projectRoot,
+ resolve: {
+ alias: { existing: '/existing/alias' },
+ aliasFields: ['custom-alias'],
+ byDependency: {
+ esm: {
+ conditionNames: ['custom-esm'],
+ extensions: ['.custom-esm'],
+ },
+ url: { conditionNames: ['custom-url'] },
+ },
+ conditionNames: ['custom-condition'],
+ exportsFields: ['custom-exports'],
+ extensionAlias: {
+ '.custom': ['.custom.native', '.custom'],
+ '.png': ['.custom.png'],
+ },
+ extensions: ['.custom'],
+ mainFields: ['custom-main'],
+ modules: ['custom_modules'],
+ },
+ });
+
+ plugin.apply(compiler);
+
+ const reactNativeRoot = path.join(
+ projectRoot,
+ 'node_modules',
+ 'react-native'
+ );
+ const matchingRegistryRoot = path.join(
+ reactNativeRoot,
+ 'node_modules',
+ '@react-native',
+ 'assets-registry'
+ );
+ const conflictingRegistryRoot = path.join(
+ projectRoot,
+ 'node_modules',
+ '@react-native',
+ 'assets-registry'
+ );
+
+ assert.equal(compiler.options.resolve.alias['react-native'], reactNativeRoot);
+ assert.equal(
+ compiler.options.resolve.alias['@react-native/assets-registry'],
+ matchingRegistryRoot
+ );
+ assert.notEqual(
+ compiler.options.resolve.alias['@react-native/assets-registry'],
+ conflictingRegistryRoot
+ );
+ assert.equal(compiler.options.resolve.alias.existing, '/existing/alias');
+ assert.deepEqual(compiler.options.resolve.modules, [
+ path.join(projectRoot, 'node_modules'),
+ 'custom_modules',
+ ]);
+ assert.deepEqual(compiler.options.resolve.aliasFields, [
+ 'react-native',
+ 'browser',
+ 'main',
+ 'custom-alias',
+ ]);
+ assert.deepEqual(compiler.options.resolve.conditionNames, [
+ 'react-native',
+ 'custom-condition',
+ ]);
+ assert.deepEqual(compiler.options.resolve.exportsFields, [
+ 'exports',
+ 'custom-exports',
+ ]);
+ assert.deepEqual(compiler.options.resolve.extensions.slice(-1), ['.custom']);
+ assert.deepEqual(compiler.options.resolve.mainFields, [
+ 'react-native',
+ 'browser',
+ 'main',
+ 'custom-main',
+ ]);
+ assert.deepEqual(compiler.options.resolve.extensionAlias['.png'], [
+ '.custom.png',
+ ]);
+ assert.deepEqual(compiler.options.resolve.extensionAlias['.custom'], [
+ '.custom.native',
+ '.custom',
+ ]);
+ assert.deepEqual(compiler.options.resolve.byDependency.esm.conditionNames, [
+ 'react-native',
+ 'import',
+ 'custom-esm',
+ ]);
+ assert.deepEqual(compiler.options.resolve.byDependency.esm.extensions, [
+ '.custom-esm',
+ ]);
+ assert.deepEqual(
+ compiler.options.resolve.byDependency.commonjs.conditionNames,
+ ['react-native', 'require']
+ );
+ assert.deepEqual(compiler.options.resolve.byDependency.url, {
+ conditionNames: ['custom-url'],
+ });
+ assert.equal(
+ JSON.parse(
+ fs.readFileSync(path.join(matchingRegistryRoot, 'package.json'), 'utf8')
+ ).version,
+ '0.86.2'
+ );
+ assert.equal(
+ JSON.parse(
+ fs.readFileSync(
+ path.join(conflictingRegistryRoot, 'package.json'),
+ 'utf8'
+ )
+ ).version,
+ '0.84.1'
+ );
+});
+
+test('adds standard image, font, and XML asset handling with the platform', () => {
+ const plugin = createUnitExpoPlugin();
+ const compiler = createCompiler({ plugins: [plugin] });
+
+ plugin.apply(compiler);
+
+ const pngRules = rulesForFilename(compiler.options.module.rules, 'asset.png');
+ const fontRules = rulesForFilename(
+ compiler.options.module.rules,
+ 'material.ttf'
+ );
+ const xmlRules = rulesForFilename(compiler.options.module.rules, 'asset.xml');
+ assert.equal(pngRules.length, 1);
+ assert.equal(fontRules.length, 1);
+ assert.equal(xmlRules.length, 1);
+ assert.equal(pngRules[0], fontRules[0]);
+ for (const rule of [pngRules[0], xmlRules[0]]) {
+ assert.equal(rule.type, 'javascript/auto');
+ assert.match(ruleUse(rule).loader, /assetsLoader\/index\.js$/);
+ assert.equal(ruleUse(rule).options.platform, 'ios');
+ }
+});
+
+test('preserves existing asset rules and options while filling only missing extensions', () => {
+ const pngRule = {
+ test: /\.png$/,
+ type: 'javascript/auto',
+ use: {
+ loader: '@callstack/repack/assets-loader',
+ options: { inline: true },
+ },
+ };
+ const xmlRule = { test: /\.xml$/, use: 'custom-xml-loader' };
+ const unrelatedRule = {
+ test: /\.txt$/,
+ use: { loader: 'raw-loader', options: { encoding: 'utf8' } },
+ };
+ const plugin = createUnitExpoPlugin();
+ const compiler = createCompiler({
+ moduleRules: [pngRule, xmlRule, unrelatedRule],
+ platform: 'android',
+ plugins: [plugin],
+ });
+
+ plugin.apply(compiler);
+
+ assert.deepEqual(rulesForFilename(compiler.options.module.rules, 'a.png'), [
+ pngRule,
+ ]);
+ assert.deepEqual(rulesForFilename(compiler.options.module.rules, 'a.xml'), [
+ xmlRule,
+ ]);
+ assert.equal(
+ rulesForFilename(compiler.options.module.rules, 'font.otf').length,
+ 1
+ );
+ assert.deepEqual(ruleUse(pngRule).options, {
+ inline: true,
+ platform: 'android',
+ });
+ assert.equal(xmlRule.use, 'custom-xml-loader');
+ assert.deepEqual(unrelatedRule.use, {
+ loader: 'raw-loader',
+ options: { encoding: 'utf8' },
+ });
+});
+
+test('replaces the standard Router entry with a Metro-free bootstrap loader', () => {
+ const projectRoot = createProject({ main: 'expo-router/entry' });
+ const plugin = createUnitExpoPlugin();
+ const compiler = createCompiler({
+ plugins: [plugin],
+ projectRoot,
+ });
+
+ plugin.apply(compiler);
+
+ const entryPath = compiler.options.entry.main.import[1];
+ const routerRule = compiler.options.module.rules.find(
+ (rule) =>
+ rule.enforce === 'pre' &&
+ rule.test instanceof RegExp &&
+ rule.test.test(entryPath)
+ );
+ assert.match(routerRule.use.loader, /expoRouterEntryLoader\.js$/);
+
+ let cacheable = false;
+ const source = expoRouterEntryLoader.call({
+ cacheable(value) {
+ cacheable = value;
+ },
+ resourcePath: entryPath,
+ });
+ assert.equal(cacheable, true);
+ assert.match(source, /qualified-entry\.js/);
+ assert.match(source, /renderRootComponent\.js/);
+ assert.doesNotMatch(source, /@expo\/metro-runtime/);
+});
+
+test('rejects a separately configured RepackPlugin in either plugin order', () => {
+ for (const expoFirst of [false, true]) {
+ const expoPlugin = new ExpoPlugin();
+ const repackPlugin = new RepackPlugin();
+ const compiler = createCompiler({
+ plugins: expoFirst
+ ? [expoPlugin, repackPlugin]
+ : [repackPlugin, expoPlugin],
+ });
+
+ assert.throws(
+ () => expoPlugin.apply(compiler),
+ (error) => {
+ assert.equal(error instanceof ExpoPluginError, true);
+ assert.equal(error.code, 'REPACK_PLUGIN_CONFLICT');
+ assert.match(error.recovery, /Remove RepackPlugin/);
+ return true;
+ }
+ );
+ }
+});
+
+test('configures ExpoPlugin alone in a real Rspack compiler', () => {
+ const projectRoot = createProject();
+ const compiler = rspack({
+ context: projectRoot,
+ entry: './ignored.js',
+ mode: 'development',
+ name: 'ios',
+ output: { path: path.join(projectRoot, 'dist') },
+ plugins: [new ExpoPlugin()],
+ });
+
+ assert.deepEqual(compiler.options.entry, {
+ main: {
+ import: expectedEntryImports(projectRoot),
+ },
+ });
+ const ruleUse = compiler.options.module.rules[0].use;
+ const loader = Array.isArray(ruleUse) ? ruleUse[0].loader : ruleUse.loader;
+ assert.match(loader, /expoBabelLoader\.js$/);
+});
+
+test('composes with the real Re.Pack ModuleFederationPluginV2 in either plugin order', () => {
+ for (const expoFirst of [false, true]) {
+ const projectRoot = createProject();
+ installModuleFederationEnhanced(projectRoot);
+ const expoPlugin = new ExpoPlugin({ repack: { logger: false } });
+ const federationPlugin = new ModuleFederationPluginV2({
+ dts: false,
+ name: 'ExpoHost',
+ });
+
+ const compiler = rspack({
+ context: projectRoot,
+ entry: './ignored.js',
+ mode: 'development',
+ name: 'ios',
+ output: { path: path.join(projectRoot, 'dist') },
+ plugins: expoFirst
+ ? [expoPlugin, federationPlugin]
+ : [federationPlugin, expoPlugin],
+ });
+
+ assert.deepEqual(compiler.options.entry, {
+ main: {
+ import: expectedEntryImports(projectRoot),
+ },
+ });
+ }
+});
+
+test('selects src/app when it exists and allows an in-project override', () => {
+ const srcProject = createProject({ routerRoot: 'src/app' });
+ const srcPlugin = createUnitExpoPlugin();
+ const srcCompiler = createCompiler({
+ plugins: [srcPlugin],
+ projectRoot: srcProject,
+ });
+ srcPlugin.apply(srcCompiler);
+ assert.equal(
+ srcCompiler.options.module.rules[0].use.options.caller.routerRoot,
+ 'src/app'
+ );
+
+ const customProject = createProject({ routerRoot: 'src/screens' });
+ const customPlugin = createUnitExpoPlugin({ routerRoot: 'src/screens' });
+ const customCompiler = createCompiler({
+ plugins: [customPlugin],
+ projectRoot: customProject,
+ });
+ customPlugin.apply(customCompiler);
+ assert.equal(
+ customCompiler.options.module.rules[0].use.options.caller.routerRoot,
+ 'src/screens'
+ );
+});
+
+test('discovers an Expo project above a nested Rspack context', () => {
+ const projectRoot = createProject();
+ const nestedContext = path.join(projectRoot, 'config', 'rspack');
+ fs.mkdirSync(nestedContext, { recursive: true });
+ const plugin = createUnitExpoPlugin();
+ const compiler = createCompiler({
+ plugins: [plugin],
+ projectRoot: nestedContext,
+ });
+
+ plugin.apply(compiler);
+
+ assert.equal(
+ compiler.options.module.rules[0].use.options.caller.projectRoot,
+ projectRoot
+ );
+ assert.deepEqual(compiler.options.entry, {
+ main: {
+ import: expectedEntryImports(projectRoot),
+ },
+ });
+});
+
+test('preserves metadata on the single configured Rspack entry', () => {
+ const plugin = createUnitExpoPlugin();
+ const compiler = createCompiler({ plugins: [plugin] });
+ compiler.options.entry = {
+ application: {
+ filename: 'custom.bundle',
+ import: ['./ignored.js'],
+ runtime: 'native-runtime',
+ },
+ };
+
+ plugin.apply(compiler);
+
+ assert.deepEqual(compiler.options.entry, {
+ application: {
+ filename: 'custom.bundle',
+ import: expectedEntryImports(compiler.context),
+ runtime: 'native-runtime',
+ },
+ });
+});
+
+test('rejects ambiguous or dynamic Rspack entries', () => {
+ for (const [entry, code] of [
+ [
+ { first: { import: ['./a'] }, second: { import: ['./b'] } },
+ 'AMBIGUOUS_ENTRY',
+ ],
+ [() => ({ main: { import: ['./index'] } }), 'DYNAMIC_ENTRY_UNSUPPORTED'],
+ ]) {
+ const plugin = createUnitExpoPlugin();
+ const compiler = createCompiler({ plugins: [plugin] });
+ compiler.options.entry = entry;
+
+ assert.throws(
+ () => plugin.apply(compiler),
+ (error) => {
+ assert.equal(error.code, code);
+ assert.equal(typeof error.recovery, 'string');
+ return true;
+ }
+ );
+ }
+});
+
+test('adds isNodeModule per transformed resource inside package-local loaders', () => {
+ const caller = { name: '@callstack/repack-expo' };
+
+ assert.equal(
+ withExpoBabelCaller(
+ { caller },
+ '/project/node_modules/expo-router/_ctx.ios.js'
+ ).caller.isNodeModule,
+ true
+ );
+ assert.equal(
+ withExpoBabelCaller({ caller }, '/project/app/index.tsx').caller
+ .isNodeModule,
+ false
+ );
+});
+
+test('clears the default Babel source root while preserving an explicit override', () => {
+ assert.deepEqual(getExpoBabelLoaderOptions({ caller: { name: 'test' } }), {
+ caller: { name: 'test', isNodeModule: false },
+ sourceRoot: undefined,
+ });
+ assert.deepEqual(
+ getExpoBabelLoaderOptions({
+ caller: { name: 'test' },
+ sourceRoot: '/custom/source/root',
+ }),
+ {
+ caller: { name: 'test', isNodeModule: false },
+ sourceRoot: '/custom/source/root',
+ }
+ );
+});
+
+test('adds the public environment transform and dotenv dependencies from a public-only snapshot', () => {
+ const projectRoot = createProject();
+ fs.writeFileSync(
+ path.join(projectRoot, '.env.development'),
+ [
+ 'EXPO_PUBLIC_VISIBLE=public-loader-value',
+ 'PRIVATE_TEST_SECRET=private-loader-value',
+ ].join('\n')
+ );
+ const dependencies = [];
+ const missingDependencies = [];
+
+ const options = getExpoBabelLoaderOptions(
+ {
+ caller: {
+ isDev: true,
+ name: '@callstack/repack-expo',
+ projectRoot,
+ },
+ expoPublicEnvironment: {
+ digest: 'public-environment-digest',
+ inline: true,
+ values: { EXPO_PUBLIC_VISIBLE: 'public-loader-value' },
+ },
+ },
+ { dependencies, missingDependencies }
+ );
+
+ assert.equal(options.plugins.length, 1);
+ assert.match(options.plugins[0][0], /inlineExpoPublicEnvironment\.js$/);
+ assert.deepEqual(options.plugins[0][1].environment, {
+ EXPO_PUBLIC_VISIBLE: 'public-loader-value',
+ });
+ assert.deepEqual(dependencies, [path.join(projectRoot, '.env.development')]);
+ assert.deepEqual(
+ missingDependencies,
+ ['.env.development.local', '.env.local', '.env'].map((filename) =>
+ path.join(projectRoot, filename)
+ )
+ );
+ assert.doesNotMatch(JSON.stringify(options), /private-loader-value/);
+});
+
+test('preserves production public environment accesses when client injection is disabled', () => {
+ const options = getExpoBabelLoaderOptions({
+ caller: {
+ isDev: false,
+ name: '@callstack/repack-expo',
+ projectRoot: createProject(),
+ },
+ expoPublicEnvironment: {
+ digest: 'disabled-environment-digest',
+ inline: false,
+ values: {},
+ },
+ });
+ assert.equal(options.plugins, undefined);
+});
+
+test('fails fast for unsupported compiler configurations', () => {
+ const cases = [
+ {
+ code: 'RSPACK_REQUIRED',
+ compiler: createCompiler({
+ rspack: false,
+ }),
+ },
+ {
+ code: 'UNSUPPORTED_PLATFORM',
+ compiler: createCompiler({
+ platform: 'web',
+ }),
+ },
+ {
+ code: 'UNSUPPORTED_ENVIRONMENT',
+ compiler: createCompiler(),
+ configure(compiler) {
+ compiler.options.externalsPresets.node = true;
+ },
+ },
+ ];
+
+ for (const { code, compiler, configure } of cases) {
+ configure?.(compiler);
+ assert.throws(
+ () => new ExpoPlugin().apply(compiler),
+ (error) => {
+ assert.equal(error instanceof ExpoPluginError, true);
+ assert.equal(error.code, code);
+ assert.equal(typeof error.recovery, 'string');
+ return true;
+ }
+ );
+ }
+});
+
+test('rejects a Router root outside the Expo project', () => {
+ const plugin = new ExpoPlugin({ routerRoot: '../routes' });
+ const compiler = createCompiler({ plugins: [plugin] });
+
+ assert.throws(
+ () => plugin.apply(compiler),
+ (error) => {
+ assert.equal(error.code, 'INVALID_ROUTER_ROOT');
+ assert.match(error.recovery, /inside the Expo project/);
+ return true;
+ }
+ );
+});
diff --git a/packages/repack-expo/test/helpers/createCliProject.cjs b/packages/repack-expo/test/helpers/createCliProject.cjs
new file mode 100644
index 000000000..c4b12cc44
--- /dev/null
+++ b/packages/repack-expo/test/helpers/createCliProject.cjs
@@ -0,0 +1,56 @@
+const fs = require('node:fs');
+const os = require('node:os');
+const path = require('node:path');
+
+function createProject(options = {}) {
+ const projectRoot = fs.mkdtempSync(
+ path.join(os.tmpdir(), options.prefix ?? 'repack-expo-cli-')
+ );
+ fs.writeFileSync(
+ path.join(projectRoot, 'package.json'),
+ `${JSON.stringify(
+ {
+ name: options.name ?? 'fixture',
+ main: 'expo-router/entry',
+ scripts: {},
+ dependencies: {
+ ...(options.includeCommunityCli === false
+ ? {}
+ : { '@react-native-community/cli': '^20.0.0' }),
+ expo: '~56.0.0',
+ 'expo-router': '~56.0.0',
+ ...(options.dependencies ?? {}),
+ },
+ },
+ null,
+ 2
+ )}\n`
+ );
+ fs.writeFileSync(
+ path.join(projectRoot, 'app.json'),
+ `${JSON.stringify(
+ {
+ expo: {
+ name: options.expoName ?? 'Fixture',
+ slug: options.slug ?? 'fixture',
+ ...(options.expo ?? {}),
+ },
+ },
+ null,
+ 2
+ )}\n`
+ );
+ if (options.lockfile) {
+ fs.writeFileSync(path.join(projectRoot, options.lockfile), '');
+ }
+ fs.mkdirSync(path.join(projectRoot, 'node_modules', 'expo-router'), {
+ recursive: true,
+ });
+ fs.writeFileSync(
+ path.join(projectRoot, 'node_modules', 'expo-router', 'entry.js'),
+ 'module.exports = {};\n'
+ );
+ return projectRoot;
+}
+
+module.exports = { createProject };
diff --git a/packages/repack-expo/test/local-chunk-resolver.test.cjs b/packages/repack-expo/test/local-chunk-resolver.test.cjs
new file mode 100644
index 000000000..b66371fcb
--- /dev/null
+++ b/packages/repack-expo/test/local-chunk-resolver.test.cjs
@@ -0,0 +1,105 @@
+const assert = require('node:assert/strict');
+const test = require('node:test');
+
+const {
+ LOCAL_CHUNK_RESOLVER_KEY,
+ createLocalChunkResolver,
+ registerLocalChunkResolver,
+} = require('../dist/rspack/runtime/localChunkResolver.js');
+
+function createScript() {
+ return {
+ getDevServerURL: (scriptId) => `dev:${scriptId}`,
+ getFileSystemURL: (scriptId) => `file:${scriptId}`,
+ };
+}
+
+test('resolves local chunks from the development server without caching', async () => {
+ const resolver = createLocalChunkResolver({
+ isDev: true,
+ script: createScript(),
+ });
+
+ assert.deepEqual(
+ await resolver('chunk-1', 'caller-a', 'https://example.test/reference-a'),
+ { cache: false, url: 'dev:chunk-1' }
+ );
+ assert.deepEqual(
+ await resolver('chunk-1', 'caller-b', 'https://example.test/reference-b'),
+ { cache: false, url: 'dev:chunk-1' }
+ );
+});
+
+test('resolves packaged local chunks from the filesystem in release', async () => {
+ const resolver = createLocalChunkResolver({
+ isDev: false,
+ script: createScript(),
+ });
+
+ assert.deepEqual(await resolver('chunk-2'), {
+ cache: false,
+ url: 'file:chunk-2',
+ });
+});
+
+test('registers once per stable key below a federation resolver', async () => {
+ const registrations = [];
+ const scriptManager = {
+ addResolver(resolver, options = {}) {
+ const registration = {
+ key: options.key,
+ priority: options.priority ?? 2,
+ resolver,
+ };
+ const previous = registrations.findIndex(
+ (item) => item.key === options.key
+ );
+ if (previous >= 0) registrations.splice(previous, 1);
+ registrations.push(registration);
+ registrations.sort((left, right) => right.priority - left.priority);
+ },
+ };
+
+ registerLocalChunkResolver({
+ isDev: true,
+ script: createScript(),
+ scriptManager,
+ });
+ registerLocalChunkResolver({
+ isDev: false,
+ script: createScript(),
+ scriptManager,
+ });
+ scriptManager.addResolver(
+ async (scriptId, caller, referenceUrl) =>
+ scriptId === 'Widget' || caller === 'Widget'
+ ? { url: `${referenceUrl}/${scriptId}` }
+ : undefined,
+ { key: 'Widget' }
+ );
+
+ assert.equal(registrations.length, 2);
+ const localRegistration = registrations.find(
+ (item) => item.key === LOCAL_CHUNK_RESOLVER_KEY
+ );
+ assert.equal(localRegistration.priority, 1);
+
+ async function resolve(scriptId, caller, referenceUrl) {
+ for (const registration of registrations) {
+ const locator = await registration.resolver(
+ scriptId,
+ caller,
+ referenceUrl
+ );
+ if (locator) return locator;
+ }
+ }
+
+ assert.deepEqual(await resolve('Widget', 'main', 'https://widgets.example'), {
+ url: 'https://widgets.example/Widget',
+ });
+ assert.deepEqual(await resolve('chunk-3', 'main'), {
+ cache: false,
+ url: 'file:chunk-3',
+ });
+});
diff --git a/packages/repack-expo/test/module-federation-source-map.test.cjs b/packages/repack-expo/test/module-federation-source-map.test.cjs
new file mode 100644
index 000000000..31b3fde58
--- /dev/null
+++ b/packages/repack-expo/test/module-federation-source-map.test.cjs
@@ -0,0 +1,217 @@
+const assert = require('node:assert/strict');
+const fs = require('node:fs');
+const { createRequire } = require('node:module');
+const os = require('node:os');
+const path = require('node:path');
+const test = require('node:test');
+const { rspack } = require('@rspack/core');
+const {
+ plugins: { ModuleFederationPluginV2 },
+} = require('@callstack/repack');
+
+const { ExpoPlugin } = require('../dist/rspack/index.js');
+
+const requireFromDevServer = createRequire(
+ path.resolve(__dirname, '../../dev-server/package.json')
+);
+const { SourceMapConsumer } = requireFromDevServer('source-map');
+
+function createProject() {
+ const projectRoot = fs.realpathSync(
+ fs.mkdtempSync(path.join(os.tmpdir(), 'repack-expo-mf-source-map-'))
+ );
+ fs.writeFileSync(
+ path.join(projectRoot, 'package.json'),
+ JSON.stringify({
+ dependencies: { expo: '56.0.0' },
+ main: 'index.js',
+ name: 'repack-expo-mf-source-map-test',
+ })
+ );
+ fs.writeFileSync(
+ path.join(projectRoot, 'index.js'),
+ "globalThis.__REPACK_EXPO_SOURCE_MAP__ = 'ordinary-source';"
+ );
+ fs.writeFileSync(
+ path.join(projectRoot, 'babel.config.cjs'),
+ "module.exports = { presets: ['babel-preset-expo'] };"
+ );
+
+ fs.mkdirSync(path.join(projectRoot, 'node_modules'));
+ for (const packageName of ['babel-preset-expo', 'expo']) {
+ fs.symlinkSync(
+ fs.realpathSync(
+ path.join(
+ __dirname,
+ '..',
+ '..',
+ '..',
+ 'apps',
+ 'tester-expo',
+ 'node_modules',
+ packageName
+ )
+ ),
+ path.join(projectRoot, 'node_modules', packageName),
+ 'junction'
+ );
+ }
+
+ const scopeRoot = path.join(
+ projectRoot,
+ 'node_modules',
+ '@module-federation'
+ );
+ fs.mkdirSync(scopeRoot, { recursive: true });
+ fs.symlinkSync(
+ fs.realpathSync(
+ path.join(
+ __dirname,
+ '..',
+ '..',
+ 'repack',
+ 'node_modules',
+ '@module-federation',
+ 'enhanced'
+ )
+ ),
+ path.join(scopeRoot, 'enhanced'),
+ 'junction'
+ );
+ const reactNativeScopeRoot = path.join(
+ projectRoot,
+ 'node_modules',
+ '@react-native'
+ );
+ fs.mkdirSync(reactNativeScopeRoot, { recursive: true });
+ fs.symlinkSync(
+ fs.realpathSync(
+ path.join(
+ __dirname,
+ '..',
+ '..',
+ '..',
+ 'apps',
+ 'tester-expo',
+ 'node_modules',
+ '@react-native',
+ 'babel-preset'
+ )
+ ),
+ path.join(reactNativeScopeRoot, 'babel-preset'),
+ 'junction'
+ );
+ fs.symlinkSync(
+ fs.realpathSync(
+ path.join(
+ __dirname,
+ '..',
+ '..',
+ '..',
+ 'apps',
+ 'tester-expo',
+ 'node_modules',
+ 'react-native'
+ )
+ ),
+ path.join(projectRoot, 'node_modules', 'react-native'),
+ 'junction'
+ );
+
+ return projectRoot;
+}
+
+function runCompiler(compiler) {
+ return new Promise((resolve, reject) => {
+ compiler.run((error, stats) => {
+ if (error) return reject(error);
+ if (!stats) return reject(new Error('Rspack returned no stats'));
+ const compilationError = stats.hasErrors()
+ ? new Error(stats.toString({ all: false, errors: true }))
+ : undefined;
+ compiler.close((closeError) => {
+ if (compilationError) reject(compilationError);
+ else if (closeError) reject(closeError);
+ else resolve(stats);
+ });
+ });
+ });
+}
+
+test('emits symbolication-safe development source maps with Module Federation v2 in either plugin order', async () => {
+ for (const expoFirst of [true, false]) {
+ const projectRoot = createProject();
+ const expoPlugin = new ExpoPlugin({ repack: { logger: false } });
+ const federationPlugin = new ModuleFederationPluginV2({
+ dts: false,
+ name: 'ExpoHost',
+ });
+ const compiler = rspack({
+ context: projectRoot,
+ devServer: { host: 'localhost', port: 8081 },
+ devtool: 'source-map',
+ mode: 'development',
+ module: {
+ rules: [
+ {
+ test: /\.js$/,
+ type: 'javascript/auto',
+ use: {
+ loader: require.resolve('@callstack/repack/babel-loader'),
+ options: { cwd: projectRoot },
+ },
+ },
+ ],
+ },
+ name: 'ios',
+ output: {
+ filename: '[name].bundle',
+ path: path.join(projectRoot, 'dist'),
+ publicPath: 'http://localhost:8081/',
+ uniqueName: 'repack-expo-mf-source-map-test',
+ },
+ plugins: expoFirst
+ ? [expoPlugin, federationPlugin]
+ : [federationPlugin, expoPlugin],
+ });
+
+ await runCompiler(compiler);
+
+ const sourceMap = JSON.parse(
+ fs.readFileSync(path.join(projectRoot, 'dist', 'main.bundle.map'), 'utf8')
+ );
+ const ordinarySourceIndex = sourceMap.sourcesContent.findIndex((source) =>
+ source?.includes('ordinary-source')
+ );
+
+ assert.notEqual(ordinarySourceIndex, -1);
+ assert.equal(
+ sourceMap.sources[ordinarySourceIndex],
+ '[projectRoot]/index.js'
+ );
+ assert.match(
+ sourceMap.sourcesContent[ordinarySourceIndex],
+ /ordinary-source/
+ );
+ assert.notEqual(sourceMap.mappings, '');
+ assert.equal(
+ sourceMap.sources.some((source) =>
+ source.startsWith('webpack://=="undefined"}')
+ ),
+ false
+ );
+ assert.equal(
+ sourceMap.sources.some((source) =>
+ source.startsWith('webpack://module-federation/virtual-runtime-')
+ ),
+ true
+ );
+
+ await SourceMapConsumer.with(
+ sourceMap,
+ 'http://localhost:8081/index.bundle.map?platform=ios',
+ (consumer) =>
+ assert.equal(consumer.sources.length, sourceMap.sources.length)
+ );
+ }
+});
diff --git a/packages/repack-expo/test/package-boundary.test.cjs b/packages/repack-expo/test/package-boundary.test.cjs
new file mode 100644
index 000000000..c10d83363
--- /dev/null
+++ b/packages/repack-expo/test/package-boundary.test.cjs
@@ -0,0 +1,141 @@
+const assert = require('node:assert/strict');
+const fs = require('node:fs');
+const os = require('node:os');
+const path = require('node:path');
+const { spawnSync } = require('node:child_process');
+const test = require('node:test');
+
+const packageRoot = path.resolve(__dirname, '..');
+const packageJson = require('../package.json');
+
+test('keeps the integration package private during development', () => {
+ assert.equal(packageJson.name, '@callstack/repack-expo');
+ assert.equal(packageJson.version, '0.0.0');
+ assert.equal(packageJson.private, true);
+});
+
+test('keeps federation opt-in and owned by the application', () => {
+ const packageDependencies = {
+ ...packageJson.dependencies,
+ ...packageJson.peerDependencies,
+ };
+
+ assert.equal('@module-federation/enhanced' in packageDependencies, false);
+ assert.equal('@module-federation/metro' in packageDependencies, false);
+});
+
+test('loads the default CommonJS Config Plugin and registers native mods once', () => {
+ const withRepackExpo = require(packageRoot);
+ const config = {
+ name: 'fixture',
+ slug: 'fixture',
+ updates: { enabled: false },
+ };
+
+ assert.equal(typeof withRepackExpo, 'function');
+ const configured = withRepackExpo(config);
+ assert.deepEqual(Object.keys(configured.mods).sort(), ['android', 'ios']);
+ assert.equal(
+ configured._internal.pluginHistory['@callstack/repack-expo'].version,
+ '0.0.0'
+ );
+ assert.equal(withRepackExpo(configured), configured);
+});
+
+test('keeps the Rspack subpath isolated from the Config Plugin', () => {
+ const configPluginEntry = path.join(packageRoot, 'dist', 'index.js');
+ const script = [
+ "require('@callstack/repack-expo/rspack');",
+ `const loaded = Boolean(require.cache[${JSON.stringify(configPluginEntry)}]);`,
+ 'if (loaded) process.exit(2);',
+ ].join('');
+ const result = spawnSync(process.execPath, ['-e', script], {
+ cwd: packageRoot,
+ encoding: 'utf8',
+ });
+
+ assert.equal(result.status, 0, result.stderr);
+});
+
+test('rejects unknown CLI commands without writing to the project', () => {
+ const projectRoot = fs.mkdtempSync(
+ path.join(os.tmpdir(), 'repack-expo-unknown-')
+ );
+ const result = spawnSync(
+ process.execPath,
+ [path.join(packageRoot, packageJson.bin['repack-expo']), 'start'],
+ { cwd: projectRoot, encoding: 'utf8' }
+ );
+
+ assert.equal(result.status, 1);
+ assert.match(result.stderr, /Usage: repack-expo /);
+ assert.deepEqual(fs.readdirSync(projectRoot), []);
+});
+
+test('contains no native module or Expo autolinking descriptor', () => {
+ const forbiddenExtensions = new Set([
+ '.h',
+ '.java',
+ '.kt',
+ '.m',
+ '.mm',
+ '.swift',
+ ]);
+ const pending = [packageRoot];
+
+ while (pending.length > 0) {
+ const directory = pending.pop();
+ for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
+ if (entry.name === 'node_modules' || entry.name === 'dist') continue;
+ const entryPath = path.join(directory, entry.name);
+ if (entry.isDirectory()) {
+ pending.push(entryPath);
+ continue;
+ }
+
+ assert.equal(forbiddenExtensions.has(path.extname(entry.name)), false);
+ assert.notEqual(entry.name, 'expo-module.config.json');
+ }
+ }
+});
+
+test('packs only the declared distribution artifacts', () => {
+ const npmCache = fs.mkdtempSync(
+ path.join(os.tmpdir(), 'repack-expo-npm-cache-')
+ );
+ const result = spawnSync('npm', ['pack', '--dry-run', '--json'], {
+ cwd: packageRoot,
+ encoding: 'utf8',
+ env: { ...process.env, npm_config_cache: npmCache },
+ });
+
+ assert.equal(result.status, 0, result.stderr);
+ const [{ files }] = JSON.parse(result.stdout);
+ const packedPaths = files.map((file) => file.path);
+
+ assert.ok(packedPaths.includes('package.json'));
+ assert.ok(packedPaths.includes('README.md'));
+ assert.ok(packedPaths.includes('dist/index.js'));
+ assert.ok(packedPaths.includes('dist/rspack/index.js'));
+ assert.ok(packedPaths.includes('dist/rspack/loaders/expoBabelLoader.js'));
+ assert.ok(
+ packedPaths.includes('dist/rspack/loaders/expoRouterEntryLoader.js')
+ );
+ assert.ok(packedPaths.includes('dist/cli/bin.js'));
+ assert.ok(packedPaths.includes('dist/cli/commands.js'));
+ assert.ok(packedPaths.includes('dist/cli/doctor.js'));
+ assert.ok(packedPaths.includes('dist/cli/init.js'));
+ assert.ok(packedPaths.includes('dist/cli/project.js'));
+ assert.equal(
+ packedPaths.some((file) => file.startsWith('src/')),
+ false
+ );
+ assert.equal(
+ packedPaths.some((file) => file.startsWith('test/')),
+ false
+ );
+ assert.equal(
+ packedPaths.some((file) => file.endsWith('.ts') && !file.endsWith('.d.ts')),
+ false
+ );
+});
diff --git a/packages/repack-expo/test/public-environment.test.cjs b/packages/repack-expo/test/public-environment.test.cjs
new file mode 100644
index 000000000..31b461486
--- /dev/null
+++ b/packages/repack-expo/test/public-environment.test.cjs
@@ -0,0 +1,543 @@
+const assert = require('node:assert/strict');
+const fs = require('node:fs');
+const os = require('node:os');
+const path = require('node:path');
+const test = require('node:test');
+const { transformSync } = require('@babel/core');
+const { rspack } = require('@rspack/core');
+const { ExpoPlugin } = require('../dist/rspack/index.js');
+
+const {
+ getExpoEnvironmentFiles,
+ resolveExpoPublicEnvironment,
+} = require('../dist/rspack/environment/expoPublicEnvironment.js');
+const inlineExpoPublicEnvironment =
+ require('../dist/rspack/babel/inlineExpoPublicEnvironment.js').default;
+
+function createEnvironmentProject(files) {
+ const projectRoot = fs.realpathSync(
+ fs.mkdtempSync(path.join(os.tmpdir(), 'repack-expo-env-'))
+ );
+ for (const [filename, contents] of Object.entries(files)) {
+ fs.writeFileSync(path.join(projectRoot, filename), contents);
+ }
+ return projectRoot;
+}
+
+async function withProcessEnvironment(overrides, callback) {
+ const original = new Map(
+ Object.keys(overrides).map((key) => [key, process.env[key]])
+ );
+ for (const [key, value] of Object.entries(overrides)) {
+ if (value === undefined) delete process.env[key];
+ else process.env[key] = value;
+ }
+
+ try {
+ return await callback();
+ } finally {
+ for (const [key, value] of original) {
+ if (value === undefined) delete process.env[key];
+ else process.env[key] = value;
+ }
+ }
+}
+
+function createRspackEnvironmentProject(mode) {
+ const projectRoot = createEnvironmentProject({
+ [`.env.${mode}.local`]: [
+ 'EXPO_PUBLIC_PLATFORM_VALUE=public-platform-value',
+ 'EXPO_PUBLIC_WATCH_VALUE=public-watch-initial',
+ 'PRIVATE_TEST_SECRET=private-value-that-must-not-escape',
+ ].join('\n'),
+ 'babel.config.cjs': "module.exports = { presets: ['babel-preset-expo'] };",
+ 'index.js': [
+ 'globalThis.__REPACK_EXPO_ENV_TEST__ = {',
+ ' platform: process.env.EXPO_PUBLIC_PLATFORM_VALUE,',
+ ' shell: process.env.EXPO_PUBLIC_SHELL_VALUE,',
+ ' watch: process.env.EXPO_PUBLIC_WATCH_VALUE,',
+ ' privateValue: process.env.PRIVATE_TEST_SECRET,',
+ ' privateShellValue: process.env.PRIVATE_SHELL_SECRET,',
+ '};',
+ ].join('\n'),
+ 'package.json': JSON.stringify({
+ dependencies: { expo: '56.0.0' },
+ main: 'index.js',
+ name: 'repack-expo-environment-test',
+ }),
+ });
+ const workspaceNodeModules = path.resolve(
+ __dirname,
+ '../../../apps/tester-expo/node_modules'
+ );
+ fs.symlinkSync(workspaceNodeModules, path.join(projectRoot, 'node_modules'));
+ return projectRoot;
+}
+
+function createEnvironmentCompiler(
+ projectRoot,
+ mode,
+ platform,
+ { cacheDirectory, parallelLoader = false } = {}
+) {
+ return rspack({
+ context: projectRoot,
+ devtool: 'source-map',
+ mode,
+ module: {
+ rules: [
+ {
+ test: /\.js$/,
+ type: 'javascript/auto',
+ use: {
+ loader: require.resolve('@callstack/repack/babel-loader'),
+ ...(parallelLoader ? { parallel: true } : {}),
+ options: { cwd: projectRoot },
+ },
+ },
+ ],
+ },
+ experiments: {
+ ...(cacheDirectory
+ ? {
+ cache: {
+ storage: { directory: cacheDirectory, type: 'filesystem' },
+ type: 'persistent',
+ },
+ }
+ : {}),
+ ...(parallelLoader ? { parallelLoader: true } : {}),
+ },
+ name: platform,
+ output: {
+ path: path.join(projectRoot, 'dist', platform),
+ uniqueName: `repack-expo-environment-${platform}`,
+ },
+ plugins: [new ExpoPlugin({ platform, projectRoot })],
+ watchOptions: { ignored: /node_modules/, poll: 100 },
+ });
+}
+
+function compilationArtifacts(stats) {
+ const emitted = Object.values(stats.compilation.assets)
+ .map((asset) => asset.source().toString())
+ .join('\n');
+ const serializedStats = JSON.stringify(stats.toJson({ all: true }));
+ return `${emitted}\n${serializedStats}`;
+}
+
+function runCompiler(compiler) {
+ return new Promise((resolve, reject) => {
+ compiler.run((error, stats) => {
+ if (error) return reject(error);
+ if (!stats) return reject(new Error('Rspack returned no stats'));
+ if (stats.hasErrors()) {
+ return reject(new Error(stats.toString({ all: false, errors: true })));
+ }
+ compiler.close((closeError) => {
+ if (closeError) reject(closeError);
+ else resolve(stats);
+ });
+ });
+ });
+}
+
+function closeWatcher(watcher) {
+ return new Promise((resolve, reject) => {
+ watcher.close((error) => (error ? reject(error) : resolve()));
+ });
+}
+
+function closeCompiler(compiler) {
+ return new Promise((resolve, reject) => {
+ compiler.close((error) => (error ? reject(error) : resolve()));
+ });
+}
+
+function runTwoBuildWatch({
+ afterFirstBuild,
+ compiler,
+ timeoutMessage,
+ timeoutMs,
+ verifySecondBuild,
+}) {
+ return new Promise((resolve, reject) => {
+ let build = 0;
+ let finished = false;
+
+ const finish = async (buildError) => {
+ if (finished) return;
+ finished = true;
+ clearTimeout(timeout);
+
+ let closeError;
+ try {
+ await closeWatcher(watcher);
+ } catch (error) {
+ closeError = error;
+ }
+ try {
+ await closeCompiler(compiler);
+ } catch (error) {
+ closeError ??= error;
+ }
+
+ const error = buildError ?? closeError;
+ if (error) reject(error);
+ else resolve();
+ };
+
+ const timeout = setTimeout(() => {
+ void finish(new Error(timeoutMessage));
+ }, timeoutMs);
+
+ const watcher = compiler.watch({}, (error, stats) => {
+ if (finished) return;
+ if (error || !stats || stats.hasErrors()) {
+ void finish(
+ error ??
+ new Error(
+ stats?.toString({ all: false, errors: true }) ??
+ 'Rspack returned no stats'
+ )
+ );
+ return;
+ }
+
+ build += 1;
+ const artifacts = compilationArtifacts(stats);
+ try {
+ if (build === 1) {
+ afterFirstBuild(artifacts);
+ } else if (build === 2) {
+ verifySecondBuild(artifacts);
+ void finish();
+ }
+ } catch (assertionError) {
+ void finish(assertionError);
+ }
+ });
+ });
+}
+
+test('resolves Expo development and production dotenv precedence without exposing private values', () => {
+ const projectRoot = createEnvironmentProject({
+ '.env': [
+ 'EXPO_PUBLIC_VALUE=base',
+ 'EXPO_PUBLIC_EXPANDED=${SHARED_SUFFIX}-base',
+ 'PRIVATE_TEST_SECRET=private-value-that-must-not-escape',
+ ].join('\n'),
+ '.env.development': 'EXPO_PUBLIC_VALUE=development',
+ '.env.development.local': 'EXPO_PUBLIC_VALUE=development-local',
+ '.env.local': 'EXPO_PUBLIC_VALUE=local',
+ '.env.production': 'EXPO_PUBLIC_VALUE=production',
+ '.env.production.local': 'EXPO_PUBLIC_VALUE=production-local',
+ });
+ const systemEnv = {
+ EXPO_PUBLIC_SHELL: 'shell-wins',
+ SHARED_SUFFIX: 'from-shell',
+ };
+
+ const development = resolveExpoPublicEnvironment({
+ mode: 'development',
+ projectRoot,
+ systemEnv,
+ });
+ const production = resolveExpoPublicEnvironment({
+ mode: 'production',
+ projectRoot,
+ systemEnv,
+ });
+
+ assert.deepEqual(development, {
+ EXPO_PUBLIC_EXPANDED: 'from-shell-base',
+ EXPO_PUBLIC_SHELL: 'shell-wins',
+ EXPO_PUBLIC_VALUE: 'development-local',
+ });
+ assert.deepEqual(production, {
+ EXPO_PUBLIC_EXPANDED: 'from-shell-base',
+ EXPO_PUBLIC_SHELL: 'shell-wins',
+ EXPO_PUBLIC_VALUE: 'production-local',
+ });
+ assert.equal('PRIVATE_TEST_SECRET' in development, false);
+ assert.deepEqual(
+ getExpoEnvironmentFiles(projectRoot, 'development').map((file) =>
+ path.basename(file)
+ ),
+ ['.env.development.local', '.env.local', '.env.development', '.env']
+ );
+});
+
+test('preserves shell precedence and treats missing optional dotenv files as harmless', () => {
+ const projectRoot = createEnvironmentProject({
+ '.env': 'EXPO_PUBLIC_VALUE=from-file',
+ });
+
+ assert.deepEqual(
+ resolveExpoPublicEnvironment({
+ mode: 'development',
+ projectRoot,
+ systemEnv: { EXPO_PUBLIC_VALUE: 'from-shell' },
+ }),
+ { EXPO_PUBLIC_VALUE: 'from-shell' }
+ );
+});
+
+test('does not expose private values in Expo dotenv validation diagnostics', () => {
+ const privateValue = 'private-diagnostic-value-that-must-not-escape';
+ const projectRoot = createEnvironmentProject({
+ '.env': [
+ `FASTLANE_PASSWORD=${privateValue}`,
+ `PRIVATE_TEST_SECRET=${privateValue}`,
+ ].join('\n'),
+ });
+
+ assert.throws(
+ () =>
+ resolveExpoPublicEnvironment({
+ mode: 'production',
+ projectRoot,
+ systemEnv: {},
+ }),
+ (error) => {
+ assert.match(
+ error.message,
+ /Refused to load personal environment variables/
+ );
+ assert.match(error.message, /FASTLANE_PASSWORD/);
+ assert.doesNotMatch(error.message, new RegExp(privateValue));
+ return true;
+ }
+ );
+});
+
+test('honors EXPO_NO_DOTENV and the production client-variable opt-out', async () => {
+ const projectRoot = createEnvironmentProject({
+ '.env': 'EXPO_PUBLIC_FILE=file-value',
+ });
+
+ await withProcessEnvironment(
+ { EXPO_NO_CLIENT_ENV_VARS: undefined, EXPO_NO_DOTENV: '1' },
+ () => {
+ assert.deepEqual(
+ resolveExpoPublicEnvironment({
+ mode: 'development',
+ projectRoot,
+ systemEnv: {
+ EXPO_NO_DOTENV: '1',
+ EXPO_PUBLIC_SHELL: 'shell-value',
+ },
+ }),
+ { EXPO_PUBLIC_SHELL: 'shell-value' }
+ );
+ }
+ );
+
+ await withProcessEnvironment(
+ { EXPO_NO_CLIENT_ENV_VARS: '1', EXPO_NO_DOTENV: undefined },
+ () => {
+ assert.deepEqual(
+ resolveExpoPublicEnvironment({
+ mode: 'production',
+ projectRoot,
+ systemEnv: { EXPO_NO_CLIENT_ENV_VARS: '1' },
+ }),
+ {}
+ );
+ assert.deepEqual(
+ resolveExpoPublicEnvironment({
+ mode: 'development',
+ projectRoot,
+ systemEnv: { EXPO_NO_CLIENT_ENV_VARS: '1' },
+ }),
+ { EXPO_PUBLIC_FILE: 'file-value' }
+ );
+ }
+ );
+});
+
+test('inlines Expo-supported static public accesses and leaves unsupported or private accesses intact', () => {
+ const environment = {
+ EXPO_PUBLIC_BRACKET: 'bracket-value',
+ EXPO_PUBLIC_DOT: 'dot-value',
+ EXPO_PUBLIC_INNER_OPTIONAL: 'inner-optional-value',
+ EXPO_PUBLIC_OPTIONAL: 'optional-value',
+ };
+ const source = [
+ 'const dot = process.env.EXPO_PUBLIC_DOT;',
+ "const bracket = process['env']['EXPO_PUBLIC_BRACKET'];",
+ 'const optional = process.env?.EXPO_PUBLIC_OPTIONAL;',
+ 'const innerOptional = process?.env.EXPO_PUBLIC_INNER_OPTIONAL;',
+ 'const missing = process.env.EXPO_PUBLIC_MISSING;',
+ 'const dynamic = process.env[key];',
+ 'const publicDynamic = process.env[EXPO_PUBLIC_DOT];',
+ 'const dynamicEnv = process[env].EXPO_PUBLIC_DOT;',
+ 'const privateValue = process.env.PRIVATE_TEST_SECRET;',
+ "process.env.EXPO_PUBLIC_DOT = 'assigned';",
+ 'const { EXPO_PUBLIC_DOT } = process.env;',
+ ].join('\n');
+
+ const result = transformSync(source, {
+ babelrc: false,
+ caller: { name: 'repack-expo-test', isNodeModule: false },
+ configFile: false,
+ plugins: [[inlineExpoPublicEnvironment, { environment }]],
+ }).code;
+
+ assert.match(result, /dot-value/);
+ assert.match(result, /bracket-value/);
+ assert.match(result, /optional-value/);
+ assert.match(result, /inner-optional-value/);
+ assert.match(result, /const missing = undefined/);
+ assert.match(result, /process\.env\[key\]/);
+ assert.match(result, /process\.env\[EXPO_PUBLIC_DOT\]/);
+ assert.match(result, /process\[env\]\.EXPO_PUBLIC_DOT/);
+ assert.match(result, /process\.env\.PRIVATE_TEST_SECRET/);
+ assert.match(result, /process\.env\.EXPO_PUBLIC_DOT = 'assigned'/);
+ assert.match(result, /EXPO_PUBLIC_DOT\s*\}\s*= process\.env/);
+});
+
+test('does not inline public accesses in dependencies', () => {
+ const environment = {
+ EXPO_PUBLIC_VALUE: 'must-not-inline',
+ };
+
+ const result = transformSync('process.env.EXPO_PUBLIC_VALUE;', {
+ babelrc: false,
+ caller: { name: 'repack-expo-test', isNodeModule: true },
+ configFile: false,
+ plugins: [[inlineExpoPublicEnvironment, { environment }]],
+ }).code;
+
+ assert.match(result, /process\.env\.EXPO_PUBLIC_VALUE/);
+ assert.doesNotMatch(result, /must-not-inline/);
+});
+
+test('emits public values identically for both native platforms without leaking private dotenv values', async () => {
+ const projectRoot = createRspackEnvironmentProject('production');
+
+ for (const platform of ['ios', 'android']) {
+ const stats = await runCompiler(
+ createEnvironmentCompiler(projectRoot, 'production', platform)
+ );
+ const artifacts = compilationArtifacts(stats);
+ assert.match(artifacts, /public-platform-value/);
+ assert.doesNotMatch(artifacts, /private-value-that-must-not-escape/);
+ }
+});
+
+test('inlines public environment values through Rspack parallel loader workers', async () => {
+ const projectRoot = createRspackEnvironmentProject('production');
+
+ await withProcessEnvironment(
+ {
+ EXPO_PUBLIC_SHELL_VALUE: 'parallel-public-shell-value',
+ PRIVATE_SHELL_SECRET: 'parallel-private-shell-value',
+ },
+ async () => {
+ const stats = await runCompiler(
+ createEnvironmentCompiler(projectRoot, 'production', 'ios', {
+ parallelLoader: true,
+ })
+ );
+ const artifacts = compilationArtifacts(stats);
+ assert.match(artifacts, /public-platform-value/);
+ assert.match(artifacts, /parallel-public-shell-value/);
+ assert.doesNotMatch(artifacts, /private-value-that-must-not-escape/);
+ assert.doesNotMatch(artifacts, /parallel-private-shell-value/);
+ }
+ );
+});
+
+test('invalidates Rspack persistent loader cache when a public shell value changes', async () => {
+ const projectRoot = createRspackEnvironmentProject('development');
+ const cacheDirectory = path.join(projectRoot, 'rspack-cache');
+
+ await withProcessEnvironment(
+ {
+ EXPO_PUBLIC_SHELL_VALUE: 'first-public-shell-value',
+ PRIVATE_SHELL_SECRET: 'persistent-private-shell-value',
+ },
+ async () => {
+ const firstStats = await runCompiler(
+ createEnvironmentCompiler(projectRoot, 'development', 'ios', {
+ cacheDirectory,
+ })
+ );
+ assert.match(
+ compilationArtifacts(firstStats),
+ /first-public-shell-value/
+ );
+
+ process.env.EXPO_PUBLIC_SHELL_VALUE = 'second-public-shell-value';
+ const secondStats = await runCompiler(
+ createEnvironmentCompiler(projectRoot, 'development', 'ios', {
+ cacheDirectory,
+ })
+ );
+ const secondArtifacts = compilationArtifacts(secondStats);
+ assert.match(secondArtifacts, /second-public-shell-value/);
+ assert.doesNotMatch(secondArtifacts, /first-public-shell-value/);
+ assert.doesNotMatch(secondArtifacts, /persistent-private-shell-value/);
+ }
+ );
+});
+
+test('rebuilds transformed application modules when an Expo public dotenv value changes', async () => {
+ const projectRoot = createRspackEnvironmentProject('development');
+ const envFile = path.join(projectRoot, '.env.development.local');
+ const compiler = createEnvironmentCompiler(projectRoot, 'development', 'ios');
+
+ await runTwoBuildWatch({
+ afterFirstBuild(artifacts) {
+ assert.match(artifacts, /public-watch-initial/);
+ fs.writeFileSync(
+ envFile,
+ [
+ 'EXPO_PUBLIC_PLATFORM_VALUE=public-platform-value',
+ 'EXPO_PUBLIC_WATCH_VALUE=public-watch-rebuilt',
+ 'PRIVATE_TEST_SECRET=private-value-that-must-not-escape',
+ ].join('\n')
+ );
+ },
+ compiler,
+ timeoutMessage: 'Timed out waiting for dotenv rebuild',
+ timeoutMs: 30_000,
+ verifySecondBuild(artifacts) {
+ assert.match(artifacts, /public-watch-rebuilt/);
+ assert.doesNotMatch(artifacts, /private-value-that-must-not-escape/);
+ },
+ });
+});
+
+test('rebuilds parallel loader modules when a selected public dotenv file is created', async () => {
+ const projectRoot = createRspackEnvironmentProject('development');
+ const envFile = path.join(projectRoot, '.env.development.local');
+ fs.unlinkSync(envFile);
+ const compiler = createEnvironmentCompiler(
+ projectRoot,
+ 'development',
+ 'ios',
+ { parallelLoader: true }
+ );
+
+ await runTwoBuildWatch({
+ afterFirstBuild(artifacts) {
+ assert.doesNotMatch(artifacts, /public-created-value/);
+ fs.writeFileSync(
+ envFile,
+ [
+ 'EXPO_PUBLIC_WATCH_VALUE=public-created-value',
+ 'PRIVATE_TEST_SECRET=created-private-value',
+ ].join('\n')
+ );
+ },
+ compiler,
+ timeoutMessage: 'Timed out waiting for created dotenv rebuild',
+ timeoutMs: 15_000,
+ verifySecondBuild(artifacts) {
+ assert.match(artifacts, /public-created-value/);
+ assert.doesNotMatch(artifacts, /created-private-value/);
+ },
+ });
+});
diff --git a/packages/repack-expo/test/repack-plugin-options.test.cjs b/packages/repack-expo/test/repack-plugin-options.test.cjs
new file mode 100644
index 000000000..ab05f4409
--- /dev/null
+++ b/packages/repack-expo/test/repack-plugin-options.test.cjs
@@ -0,0 +1,165 @@
+const assert = require('node:assert/strict');
+const fs = require('node:fs');
+const os = require('node:os');
+const path = require('node:path');
+const test = require('node:test');
+const { rspack } = require('@rspack/core');
+const { ExpoPlugin } = require('../dist/rspack/index.js');
+
+class EmitAuxiliaryAssetPlugin {
+ apply(compiler) {
+ compiler.hooks.thisCompilation.tap(
+ 'EmitExpoRepackSeamAsset',
+ (compilation) => {
+ compilation.hooks.processAssets.tap(
+ {
+ name: 'EmitExpoRepackSeamAsset',
+ stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL,
+ },
+ () => {
+ compilation.emitAsset(
+ 'remote-assets/repack-seam.txt',
+ new compiler.webpack.sources.RawSource('auxiliary-seam')
+ );
+ }
+ );
+ }
+ );
+ }
+}
+
+function createProject() {
+ const projectRoot = fs.realpathSync(
+ fs.mkdtempSync(path.join(os.tmpdir(), 'repack-expo-options-'))
+ );
+ const files = {
+ 'babel.config.cjs': "module.exports = { presets: ['babel-preset-expo'] };",
+ 'index.js': [
+ "globalThis.__EXPO_REPACK_MAIN__ = 'main-seam';",
+ "void import(/* webpackChunkName: 'lazy-card' */ './lazy.js');",
+ ].join('\n'),
+ 'initialize-core.js':
+ "globalThis.__EXPO_REPACK_INITIALIZE_CORE__ = 'initialize-core-seam';",
+ 'lazy.js': "export const lazyCard = 'lazy-card-seam';",
+ 'package.json': JSON.stringify({
+ dependencies: { expo: '56.0.0' },
+ main: 'index.js',
+ name: 'repack-expo-options-test',
+ }),
+ };
+
+ for (const [filename, contents] of Object.entries(files)) {
+ fs.writeFileSync(path.join(projectRoot, filename), contents);
+ }
+
+ fs.symlinkSync(
+ path.resolve(__dirname, '../../../apps/tester-expo/node_modules'),
+ path.join(projectRoot, 'node_modules'),
+ 'junction'
+ );
+ return projectRoot;
+}
+
+function runCompiler(compiler) {
+ return new Promise((resolve, reject) => {
+ compiler.run((error, stats) => {
+ if (error) return reject(error);
+ if (!stats) return reject(new Error('Rspack returned no stats'));
+ const compilationError = stats.hasErrors()
+ ? new Error(stats.toString({ all: false, errors: true }))
+ : undefined;
+ compiler.close((closeError) => {
+ if (compilationError) reject(compilationError);
+ else if (closeError) reject(closeError);
+ else resolve(stats);
+ });
+ });
+ });
+}
+
+test('forwards RepackPlugin options through public compiler behavior', async () => {
+ const projectRoot = createProject();
+ const auxiliaryAssetsPath = path.join(projectRoot, 'copied-auxiliary');
+ const remoteOutputPath = path.join(projectRoot, 'copied-remote');
+ const loggerEntries = [];
+ const compiler = rspack({
+ context: projectRoot,
+ devtool: 'source-map',
+ mode: 'development',
+ module: {
+ rules: [
+ {
+ test: /\.js$/,
+ type: 'javascript/auto',
+ use: {
+ loader: require.resolve('@callstack/repack/babel-loader'),
+ options: { cwd: projectRoot },
+ },
+ },
+ ],
+ },
+ name: 'android',
+ optimization: { minimize: false },
+ output: {
+ filename: '[name].bundle',
+ path: path.join(projectRoot, 'dist'),
+ uniqueName: 'repack-expo-options-test',
+ },
+ plugins: [
+ new ExpoPlugin({
+ platform: 'android',
+ projectRoot,
+ repack: {
+ extraChunks: [{ outputPath: remoteOutputPath, type: 'remote' }],
+ initializeCore: path.join(projectRoot, 'initialize-core.js'),
+ logger: {
+ console: false,
+ listener: (entry) => loggerEntries.push(entry),
+ },
+ output: { auxiliaryAssetsPath },
+ platform: 'android',
+ },
+ }),
+ new EmitAuxiliaryAssetPlugin(),
+ ],
+ });
+
+ compiler.hooks.infrastructureLog.call('ExpoRepackSeam', 'info', [
+ 'logger-seam',
+ ]);
+ await runCompiler(compiler);
+
+ assert.equal(
+ loggerEntries.some(
+ (entry) =>
+ entry.issuer === 'ExpoRepackSeam' &&
+ entry.type === 'info' &&
+ entry.message.includes('logger-seam')
+ ),
+ true
+ );
+
+ const mainBundle = fs.readFileSync(
+ path.join(projectRoot, 'dist', 'main.bundle'),
+ 'utf8'
+ );
+ assert.match(mainBundle, /initialize-core-seam/);
+ assert.match(mainBundle, /sourceMappingURL=.*\?platform=android/);
+
+ assert.equal(
+ fs.readFileSync(
+ path.join(auxiliaryAssetsPath, 'remote-assets', 'repack-seam.txt'),
+ 'utf8'
+ ),
+ 'auxiliary-seam'
+ );
+
+ const lazyBundle = fs
+ .readdirSync(remoteOutputPath)
+ .find((file) => file.endsWith('.chunk.bundle'));
+ assert.ok(lazyBundle);
+ assert.match(
+ fs.readFileSync(path.join(remoteOutputPath, lazyBundle), 'utf8'),
+ /lazy-card-seam/
+ );
+});
diff --git a/packages/repack-expo/tsconfig.build.json b/packages/repack-expo/tsconfig.build.json
new file mode 100644
index 000000000..c6fb252a1
--- /dev/null
+++ b/packages/repack-expo/tsconfig.build.json
@@ -0,0 +1,7 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "outDir": "dist",
+ "rootDir": "src"
+ }
+}
diff --git a/packages/repack-expo/tsconfig.json b/packages/repack-expo/tsconfig.json
new file mode 100644
index 000000000..9a5ff7303
--- /dev/null
+++ b/packages/repack-expo/tsconfig.json
@@ -0,0 +1,8 @@
+{
+ "extends": "../../tsconfig.base.json",
+ "compilerOptions": {
+ "outDir": "dist",
+ "types": ["node"]
+ },
+ "include": ["src/**/*"]
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 6af9f345b..e5524ef21 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -69,6 +69,8 @@ overrides:
uuid@7: ^11.1.1
vite: ^7.3.1
+packageExtensionsChecksum: sha256-72xbKMSeMnhiHS5lIvWREuDsPVJqjAl31eqOaH0kpW4=
+
importers:
.:
@@ -229,7 +231,7 @@ importers:
version: 8.2.1(@rspack/core@1.6.0(@swc/helpers@0.5.23))(postcss@8.5.23)(typescript@5.9.3)(webpack@5.105.4(@swc/core@1.15.46(@swc/helpers@0.5.23)))
react-native-test-app:
specifier: catalog:testers
- version: 5.4.5(@react-native-community/cli-types@20.2.0)(memfs@4.64.0)(metro@0.84.4)(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
+ version: 5.4.5(@expo/config-plugins@57.0.6(typescript@5.9.3))(@react-native-community/cli-types@20.2.0)(memfs@4.64.0)(metro@0.84.4)(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
tailwindcss:
specifier: ^3.4.17
version: 3.4.19(yaml@2.9.0)
@@ -249,6 +251,158 @@ importers:
specifier: 'catalog:'
version: 5.105.4(@swc/core@1.15.46(@swc/helpers@0.5.23))
+ apps/tester-expo:
+ dependencies:
+ '@callstack/repack':
+ specifier: workspace:*
+ version: link:../../packages/repack
+ '@callstack/repack-expo':
+ specifier: workspace:*
+ version: link:../../packages/repack-expo
+ '@callstack/repack-plugin-reanimated':
+ specifier: workspace:*
+ version: link:../../packages/plugin-reanimated
+ '@module-federation/enhanced':
+ specifier: 2.1.0
+ version: 2.1.0(@rspack/core@1.6.0(@swc/helpers@0.5.23))(react-dom@19.2.8(react@19.2.3))(react@19.2.3)(typescript@6.0.3)(webpack@5.105.4)
+ expo:
+ specifier: ~57.0.9
+ version: 57.0.9(@babel/core@7.29.7)(@expo/metro-runtime@57.0.8)(expo-router@57.0.9)(react-dom@19.2.8(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(typescript@6.0.3)
+ expo-asset:
+ specifier: ~57.0.8
+ version: 57.0.8(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(typescript@6.0.3)
+ expo-constants:
+ specifier: ~57.0.8
+ version: 57.0.8(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))
+ expo-font:
+ specifier: ~57.0.1
+ version: 57.0.1(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)
+ expo-linking:
+ specifier: ~57.0.4
+ version: 57.0.4(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)
+ expo-router:
+ specifier: ~57.0.9
+ version: 57.0.9(a32405737b9d1025ea0eea533a2e6749)
+ expo-status-bar:
+ specifier: ~57.0.1
+ version: 57.0.1(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)
+ react:
+ specifier: 19.2.3
+ version: 19.2.3
+ react-native:
+ specifier: 0.86.2
+ version: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3)
+ react-native-gesture-handler:
+ specifier: ~2.32.0
+ version: 2.32.0(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)
+ react-native-reanimated:
+ specifier: 4.5.1
+ version: 4.5.1(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)
+ react-native-safe-area-context:
+ specifier: ~5.7.0
+ version: 5.7.0(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)
+ react-native-screens:
+ specifier: ~4.26.0
+ version: 4.26.2(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)
+ react-native-worklets:
+ specifier: 0.10.1
+ version: 0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)
+ devDependencies:
+ '@babel/core':
+ specifier: ^7.29.0
+ version: 7.29.7
+ '@react-native-community/cli':
+ specifier: 20.1.2
+ version: 20.1.2(typescript@6.0.3)
+ '@react-native-community/cli-platform-android':
+ specifier: 20.1.2
+ version: 20.1.2
+ '@react-native-community/cli-platform-ios':
+ specifier: 20.1.2
+ version: 20.1.2
+ '@react-native/babel-preset':
+ specifier: 0.86.2
+ version: 0.86.2(@babel/core@7.29.7)
+ '@react-native/community-cli-plugin':
+ specifier: 0.86.2
+ version: 0.86.2(@react-native-community/cli@20.1.2(typescript@6.0.3))
+ '@rspack/core':
+ specifier: 'catalog:'
+ version: 1.6.0(@swc/helpers@0.5.23)
+ '@types/react':
+ specifier: ~19.2.2
+ version: 19.2.17
+ babel-preset-expo:
+ specifier: ~57.0.5
+ version: 57.0.5(@babel/core@7.29.7)(@babel/runtime@7.29.7)(expo@57.0.9)(react-refresh@0.18.0)
+ typescript:
+ specifier: ~6.0.3
+ version: 6.0.3
+ ws:
+ specifier: ^8.21.1
+ version: 8.21.1
+
+ apps/tester-expo-widget:
+ dependencies:
+ '@callstack/repack':
+ specifier: workspace:*
+ version: link:../../packages/repack
+ '@callstack/repack-expo':
+ specifier: workspace:*
+ version: link:../../packages/repack-expo
+ '@module-federation/enhanced':
+ specifier: 2.1.0
+ version: 2.1.0(@rspack/core@1.6.0(@swc/helpers@0.5.23))(react-dom@19.2.8(react@19.2.3))(react@19.2.3)(typescript@6.0.3)(webpack@5.105.4)
+ expo:
+ specifier: ~57.0.9
+ version: 57.0.9(@babel/core@7.29.7)(@expo/metro-runtime@57.0.8)(expo-router@57.0.9)(react-dom@19.2.8(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(typescript@6.0.3)
+ expo-asset:
+ specifier: ~57.0.8
+ version: 57.0.8(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(typescript@6.0.3)
+ expo-constants:
+ specifier: ~57.0.8
+ version: 57.0.8(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))
+ expo-font:
+ specifier: ~57.0.1
+ version: 57.0.1(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)
+ react:
+ specifier: 19.2.3
+ version: 19.2.3
+ react-native:
+ specifier: 0.86.2
+ version: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3)
+ devDependencies:
+ '@babel/core':
+ specifier: ^7.29.0
+ version: 7.29.7
+ '@react-native-community/cli':
+ specifier: 20.1.2
+ version: 20.1.2(typescript@6.0.3)
+ '@react-native-community/cli-platform-android':
+ specifier: 20.1.2
+ version: 20.1.2
+ '@react-native-community/cli-platform-ios':
+ specifier: 20.1.2
+ version: 20.1.2
+ '@react-native/babel-preset':
+ specifier: 0.86.2
+ version: 0.86.2(@babel/core@7.29.7)
+ '@react-native/community-cli-plugin':
+ specifier: 0.86.2
+ version: 0.86.2(@react-native-community/cli@20.1.2(typescript@6.0.3))
+ '@rspack/core':
+ specifier: 'catalog:'
+ version: 1.6.0(@swc/helpers@0.5.23)
+ '@types/react':
+ specifier: ~19.2.2
+ version: 19.2.17
+ babel-preset-expo:
+ specifier: ~57.0.5
+ version: 57.0.5(@babel/core@7.29.7)(@babel/runtime@7.29.7)(expo@57.0.9)(react-refresh@0.18.0)
+ typescript:
+ specifier: ~6.0.3
+ version: 6.0.3
+
apps/tester-federation:
dependencies:
'@callstack/repack':
@@ -262,7 +416,7 @@ importers:
version: 7.3.14(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
'@react-navigation/native-stack':
specifier: 7.18.6
- version: 7.18.6(@react-navigation/native@7.3.14(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native-safe-area-context@5.8.0(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native-screens@4.26.2(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
+ version: 7.18.6(@react-native-masked-view/masked-view@0.3.2(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(@react-navigation/native@7.3.14(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native-safe-area-context@5.8.0(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native-screens@4.26.2(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
react:
specifier: 'catalog:'
version: 19.2.8
@@ -311,7 +465,7 @@ importers:
version: 19.2.17
react-native-test-app:
specifier: catalog:testers
- version: 5.4.5(@react-native-community/cli-types@20.2.0)(memfs@4.64.0)(metro@0.84.4)(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
+ version: 5.4.5(@expo/config-plugins@57.0.6(typescript@5.9.3))(@react-native-community/cli-types@20.2.0)(memfs@4.64.0)(metro@0.84.4)(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
terser-webpack-plugin:
specifier: 'catalog:'
version: 5.5.0(webpack@5.105.4)
@@ -341,7 +495,7 @@ importers:
version: 7.3.14(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
'@react-navigation/native-stack':
specifier: 7.18.6
- version: 7.18.6(@react-navigation/native@7.3.14(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native-safe-area-context@5.8.0(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native-screens@4.26.2(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
+ version: 7.18.6(@react-native-masked-view/masked-view@0.3.2(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(@react-navigation/native@7.3.14(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native-safe-area-context@5.8.0(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native-screens@4.26.2(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
react:
specifier: 'catalog:'
version: 19.2.8
@@ -358,6 +512,9 @@ importers:
'@babel/core':
specifier: ^7.29.7
version: 7.29.7
+ '@babel/plugin-syntax-typescript':
+ specifier: 7.25.4
+ version: 7.25.4(@babel/core@7.29.7)
'@react-native-community/cli':
specifier: catalog:testers
version: 20.2.0(typescript@5.9.3)
@@ -370,6 +527,9 @@ importers:
'@react-native/babel-preset':
specifier: catalog:testers
version: 0.86.0(@babel/core@7.29.7)
+ '@react-native/community-cli-plugin':
+ specifier: 0.86.0
+ version: 0.86.0(@react-native-community/cli@20.2.0(typescript@5.9.3))
'@react-native/typescript-config':
specifier: catalog:testers
version: 0.86.0
@@ -390,7 +550,7 @@ importers:
version: 19.2.17
react-native-test-app:
specifier: catalog:testers
- version: 5.4.5(@react-native-community/cli-types@20.2.0)(memfs@4.64.0)(metro@0.84.4)(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
+ version: 5.4.5(@expo/config-plugins@57.0.6(typescript@5.9.3))(@react-native-community/cli-types@20.2.0)(memfs@4.64.0)(metro@0.84.4)(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
terser-webpack-plugin:
specifier: 'catalog:'
version: 5.5.0(webpack@5.105.4)
@@ -549,10 +709,10 @@ importers:
version: 20.19.43
nativewind:
specifier: ^4.1.23
- version: 4.2.6(react-native-reanimated@4.5.3(react-native-worklets@0.11.3(@babel/core@7.29.7)(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native-safe-area-context@5.8.0(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native-svg@15.15.5(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)(tailwindcss@3.4.19(yaml@2.9.0))
+ version: 4.2.6(react-native-reanimated@4.5.3(react-native-worklets@0.11.3(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)(tailwindcss@3.4.19(yaml@2.9.0))
react-native-css-interop:
specifier: ^0.1.22
- version: 0.1.22(react-native-reanimated@4.5.3(react-native-worklets@0.11.3(@babel/core@7.29.7)(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)(tailwindcss@3.4.19(yaml@2.9.0))
+ version: 0.1.22(react-native-reanimated@4.5.3(react-native-worklets@0.11.3(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)(tailwindcss@3.4.19(yaml@2.9.0))
webpack:
specifier: 'catalog:'
version: 5.105.4
@@ -742,6 +902,31 @@ importers:
specifier: 'catalog:'
version: 5.105.4
+ packages/repack-expo:
+ dependencies:
+ '@callstack/repack-plugin-expo-modules':
+ specifier: workspace:^
+ version: link:../plugin-expo-modules
+ '@expo/env':
+ specifier: ~2.3.1
+ version: 2.3.1
+ devDependencies:
+ '@callstack/repack':
+ specifier: workspace:*
+ version: link:../repack
+ '@rspack/core':
+ specifier: 'catalog:'
+ version: 1.6.0(@swc/helpers@0.5.23)
+ '@types/node':
+ specifier: 'catalog:'
+ version: 20.19.43
+ expo:
+ specifier: ~57.0.9
+ version: 57.0.9(@babel/core@7.29.7)(@expo/metro-runtime@57.0.8)(expo-router@57.0.9)(react-dom@19.2.8(react@19.2.8))(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)(typescript@5.9.3)
+ typescript:
+ specifier: 'catalog:'
+ version: 5.9.3
+
tests/integration:
devDependencies:
'@callstack/repack':
@@ -856,6 +1041,9 @@ importers:
packages:
+ '@adobe/css-tools@4.5.0':
+ resolution: {integrity: sha512-6OzddxPio9UiWTCemp4N8cYLV2ZN1ncRnV1cVGtve7dhPOtRkleRyx32GQCYSwDYgaHU3USMm84tNsvKzRCa1Q==}
+
'@alloc/quick-lru@5.2.0':
resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
engines: {node: '>=10'}
@@ -1077,6 +1265,12 @@ packages:
peerDependencies:
'@babel/core': ^7.0.0
+ '@babel/plugin-proposal-decorators@7.29.7':
+ resolution: {integrity: sha512-EtU0Hi3GvrTqD56xKmZvV/uCXK2ZbwVNPNLAquVItcAZpUhkXwWlo3Fmj0c2LxgSf2I8IDULeAepwNP1OefLXg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
'@babel/plugin-proposal-export-default-from@7.29.7':
resolution: {integrity: sha512-p+G5BNXDcy3bOXplhY4HybQ1GxH3i2Tppmdm/3epyRu2VgJJZuUlZ61MqRTg582Q7ZLBdP7fePYvsumSEkMxcQ==}
engines: {node: '>=6.9.0'}
@@ -1110,6 +1304,12 @@ packages:
peerDependencies:
'@babel/core': ^7.0.0-0
+ '@babel/plugin-syntax-decorators@7.29.7':
+ resolution: {integrity: sha512-9MTTLbF39X6sqM92JPEsoI7++26hjZvzkxKZy64aMhWLH2mPkJ/Q3AV4QLmls3R14FpSpkOwQQfUh962JGQxxg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
'@babel/plugin-syntax-dynamic-import@7.8.3':
resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==}
peerDependencies:
@@ -1197,6 +1397,12 @@ packages:
peerDependencies:
'@babel/core': ^7.0.0-0
+ '@babel/plugin-syntax-typescript@7.25.4':
+ resolution: {integrity: sha512-uMOCoHVU52BsSWxPOMVv5qKRdeSlPuImUCB2dlPuBSU+W2/ROE7/Zg8F2Kepbk+8yBa68LlRKxO+xgEVWorsDg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
'@babel/plugin-syntax-typescript@7.29.7':
resolution: {integrity: sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==}
engines: {node: '>=6.9.0'}
@@ -1757,6 +1963,10 @@ packages:
resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==}
engines: {node: '>=10.0.0'}
+ '@egjs/hammerjs@2.0.17':
+ resolution: {integrity: sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A==}
+ engines: {node: '>=0.8.0'}
+
'@emnapi/core@1.11.3':
resolution: {integrity: sha512-zLpS5asjEb7lq8jYLq37N6XKaE41DIexlY1rF/z4/tIl3wo13Sqm28fRyfIsKZD+NZ8mM5RoKkpW/rBcuoSZSg==}
@@ -2072,6 +2282,196 @@ packages:
cpu: [x64]
os: [win32]
+ '@expo-google-fonts/material-symbols@0.4.42':
+ resolution: {integrity: sha512-KZmHZRcthJ3KFZZlpzHjopA9guZgWR9fb3uVZlTR0BNlvG2pw1bnYBCpkze2PB0vRllwGhAM7lWXsfmcWCbXYg==}
+
+ '@expo/cli@57.0.11':
+ resolution: {integrity: sha512-ENXCwRyL8Q9qbabUmm0L6w9kXTmdGotW7eDEEWmUDXLPux+nEzNDqw0MzWQ5K3ZsXS51QmUJZg5yETB+6SnNRg==}
+ hasBin: true
+ peerDependencies:
+ expo: '*'
+ expo-router: '*'
+ react-native: '*'
+ peerDependenciesMeta:
+ expo-router:
+ optional: true
+ react-native:
+ optional: true
+
+ '@expo/code-signing-certificates@0.0.6':
+ resolution: {integrity: sha512-iNe0puxwBNEcuua9gmTGzq+SuMDa0iATai1FlFTMHJ/vUmKvN/V//drXoLJkVb5i5H3iE/n/qIJxyoBnXouD0w==}
+
+ '@expo/config-plugins@57.0.6':
+ resolution: {integrity: sha512-7CmKrS5Rnu8aSZyNlxH2qzA7Ls1HEa4EQvEVOAkHDKPr1e4Cg/nz7I7dUl09QDTVjMvkKYDH4Th0DuAsgqASaw==}
+
+ '@expo/config-types@57.0.2':
+ resolution: {integrity: sha512-ewW08OonrcRIsRKIlFvvcmmafE5zemb1ocu3HkNwtVPyRtj2w42pZCAkMIROYpcVBaPnc3mDT9UZDzwXWC3i6g==}
+
+ '@expo/config@57.0.6':
+ resolution: {integrity: sha512-VpMJpB/De/fb9bBFVVBiK6Ntg9lt0kAleLH9hcZz85CYRUQ3jVFVA8rNC5f8y4cp2+FiiPNFp62+kEOFI6pDiw==}
+
+ '@expo/devcert@1.2.1':
+ resolution: {integrity: sha512-qC4eaxmKMTmJC2ahwyui6ud8f3W60Ss7pMkpBq40Hu3zyiAaugPXnZ24145U7K36qO9UHdZUVxsCvIpz2RYYCA==}
+
+ '@expo/devtools@57.0.1':
+ resolution: {integrity: sha512-GyUf+wFNkbttaX0jR7MZa9bm77U0IrLg6d2AjpxdyoXw/w4abHoXG0oFufwLMgP9zLTd5+Ct4X/ffNUTnlzZgg==}
+ peerDependencies:
+ react: '*'
+ react-native: '*'
+ peerDependenciesMeta:
+ react:
+ optional: true
+ react-native:
+ optional: true
+
+ '@expo/dom-webview@57.0.1':
+ resolution: {integrity: sha512-lAKsME4SAq+8sf56oN0DX5TBYyruupoRxbWbD2xf9RnKY8y6x8eb9LCE5pxSN0qyWdqnp+0wmyWzDkKboThKAw==}
+ peerDependencies:
+ expo: '*'
+ react: '*'
+ react-native: '*'
+
+ '@expo/env@2.3.1':
+ resolution: {integrity: sha512-JvBdZa5OkTd+dGEtt3fLW9OF6RlIp9SNu9VyMSiWPV5szMDTSAYbX8Uj3cRvZcU1zzaRbqnTSsHk2AE8WXHfxQ==}
+ engines: {node: '>=20.12.0'}
+
+ '@expo/env@2.4.2':
+ resolution: {integrity: sha512-28pqaEqwnmLduZ00Pq9HkSzE5wbj1MTwp5/n8nm8rD8MCjR9eUnVOwmNksPI3Be2ReAPO/DbPn1puy0mvoocsQ==}
+ engines: {node: '>=20.12.0'}
+
+ '@expo/expo-modules-macros-plugin@0.6.1':
+ resolution: {integrity: sha512-cpsLZE4rqkc1Y3eZTkxB98jrqY1YXgetmtxFt8q89jBRmk3quRuk1BZo+VcnCSObZardjg99r1k5xijEMONFGA==}
+
+ '@expo/fingerprint@0.20.6':
+ resolution: {integrity: sha512-cmC/6BOPRbdKr77Mgjwszb8aM0hY2RKBpMRCmjSdn9zIcn2FGor/ic4fHVr46cQFa1G6RDGg1GyAjRw3US4CCQ==}
+ hasBin: true
+
+ '@expo/image-utils@0.11.4':
+ resolution: {integrity: sha512-pn/4770DIEOcYZr484uazuwg20FX/qaDkeMRF6J+oxejynDmEmO8wLsCudaNShFE0BhyKGQTYrs2rsRhqrqESw==}
+
+ '@expo/inline-modules@0.1.4':
+ resolution: {integrity: sha512-8bPSCm//dv8raYfrQ4x79rCX52vMw0QzmnYYl4eSOAvEbGvDPPRGGdbrhZZ7oihU+hI1sZNS5kYEgqODgFwfsw==}
+
+ '@expo/json-file@11.0.1':
+ resolution: {integrity: sha512-zxHWj4MKKMAL29ZQSY/Fssx4Thluk40JmuGNaeS078wy/NhlFhnVi+rHHunulE3xJAJ0CM73m8VK2+GkF9eRwQ==}
+
+ '@expo/local-build-cache-provider@57.0.5':
+ resolution: {integrity: sha512-OwiNC0Uxu67TOH7TEQ94GLa4oNzNfbbItKGdy3QMfX+hCSZv2VvjcjRo5vttMHfXCnXa9KZIu3GBS9pvtDHWhA==}
+
+ '@expo/log-box@57.0.2':
+ resolution: {integrity: sha512-ZsFyfIR7YCbQAdVLzuTUmMHofZC7ZS9ywYCJNPlLc78x59cI8GwXFEIVbRjjC0uJERpNtXx/tsNNnkhexXlzMw==}
+ peerDependencies:
+ expo: '*'
+ react: '*'
+ react-native: '*'
+
+ '@expo/metro-config@57.0.7':
+ resolution: {integrity: sha512-bVfEkg4zF1cA62OqAdYXmFOooJ6TB/I+REi7Se6Ct+PbSC+89TwSqWXnYx34L08eIs4z+1ilgbATakTZpgefmQ==}
+ peerDependencies:
+ expo: '*'
+ peerDependenciesMeta:
+ expo:
+ optional: true
+
+ '@expo/metro-file-map@57.0.1':
+ resolution: {integrity: sha512-8JXfVstZN7QnP4NianZZnlTVboOWR0sG8trUDNajOjnbGlPln29vponXM84tY+3tAHapz5/TxE53L0ixUwqPtA==}
+
+ '@expo/metro-runtime@57.0.8':
+ resolution: {integrity: sha512-RrdehKXNtWpnm8nNs1QFhS0IauyMKR/nSQiry6dcUJs2T5EH8gYDMcMegUt9yI41K4nO3W8tr3O0xd/GZFObXQ==}
+ peerDependencies:
+ expo: '*'
+ react: '*'
+ react-dom: '*'
+ react-native: '*'
+ peerDependenciesMeta:
+ react-dom:
+ optional: true
+
+ '@expo/metro@56.0.0':
+ resolution: {integrity: sha512-5gIgQHtEpjjvsjKfVtIv23a98LLRV0/y07PDShEwYSytAMlE3FSF8RHXqtHc1sUJL6dn7hnuIBpIbrLXXuVi0A==}
+
+ '@expo/osascript@2.7.1':
+ resolution: {integrity: sha512-Zn03EX6In7ts2lPUW2ESUSkEhEWQN1qqsiXjadtZMJOuZRkMiAg1ZQHuvz9DjByDWNJ2pBwAGyrts9lj9k389g==}
+ engines: {node: '>=12'}
+
+ '@expo/package-manager@1.13.1':
+ resolution: {integrity: sha512-y/K+CaYYpZpNGZhSX4HyLT/vyIunFjNfyoxNysPBCefeLKI/VCx6f9LNPzrxayr3rCYO5bl9O8H+HRQK265Nkg==}
+
+ '@expo/plist@0.8.1':
+ resolution: {integrity: sha512-3gTReGIUm0oRaMClsAJYxBnVPCl6fVpsl8HS+DTVxDhW4GyVyxg9E/Znm3BvcHtUJ51RJJI14pC1wvrNilCRHw==}
+
+ '@expo/prebuild-config@57.0.10':
+ resolution: {integrity: sha512-myrS5NolFAQWD8g7QuqkettnkyJx3GRl603N6rlmqAMxmO5EU7sZu4EX2EsIKkva8QtpX84B0E17PsM9xXMsTQ==}
+
+ '@expo/require-utils@57.0.4':
+ resolution: {integrity: sha512-e7xbg/9BTQcsZE/oErafZXtI7kh5IgfasLJ97J5sFSzX2cA74pDvdlhW1KHVSaDkQyQv6h1LSLhsY7dEeOk7hw==}
+ peerDependencies:
+ typescript: ^5.0.0 || ^5.0.0-0 || ^6.0.0 || ^7.0.0
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ '@expo/router-server@57.0.4':
+ resolution: {integrity: sha512-ucqCP0hK8nZb9+S8QJYdQxNCkfRClzkKdg2RpWYDKDYgRIHyoRyxEDRgDgvbhH+q78yfY83abU8OTx8MMOrf1g==}
+ peerDependencies:
+ '@expo/metro-runtime': ^57.0.7
+ expo: '*'
+ expo-constants: ^57.0.7
+ expo-font: ^57.0.1
+ expo-router: '*'
+ expo-server: ^57.0.1
+ react: '*'
+ react-dom: '*'
+ react-server-dom-webpack: ~19.0.1 || ~19.1.2 || ~19.2.1
+ peerDependenciesMeta:
+ '@expo/metro-runtime':
+ optional: true
+ expo-router:
+ optional: true
+ react-dom:
+ optional: true
+ react-server-dom-webpack:
+ optional: true
+
+ '@expo/schema-utils@57.0.2':
+ resolution: {integrity: sha512-fMu/jyN0l1Wzv7XkeWR4IYCx1M8ryui3FdBNGrWwbRgJ7EhxXxK8E2jxP2W3pbgUwUY0V3hG8+GyfCZwny+Lxw==}
+
+ '@expo/sdk-runtime-versions@1.0.0':
+ resolution: {integrity: sha512-Doz2bfiPndXYFPMRwPyGa1k5QaKDVpY806UJj570epIiMzWaYyCtobasyfC++qfIXVb5Ocy7r3tP9d62hAQ7IQ==}
+
+ '@expo/spawn-async@1.8.0':
+ resolution: {integrity: sha512-eb9xxd/LbuEGSdua4NumCu/McVB9EM+F/JxB9pWgnERw4HQ9XyTNH1KapG6oqLWR8TuRK2LQfzJlmNi94CVobw==}
+ engines: {node: '>=12'}
+
+ '@expo/sudo-prompt@9.3.2':
+ resolution: {integrity: sha512-HHQigo3rQWKMDzYDLkubN5WQOYXJJE2eNqIQC2axC2iO3mHdwnIR7FgZVvHWtBwAdzBgAP0ECp8KqS8TiMKvgw==}
+
+ '@expo/ui@57.0.8':
+ resolution: {integrity: sha512-aR17CRM45k4JyFahX4Xn2b6ti3aiddC4SAzvsm3iUCVacQA6tGmClu012rYNeVXhJ96csX2o7EEymfzYqgJNxw==}
+ peerDependencies:
+ '@babel/core': '*'
+ expo: '*'
+ react: '*'
+ react-dom: '*'
+ react-native: '*'
+ react-native-worklets: '*'
+ peerDependenciesMeta:
+ '@babel/core':
+ optional: true
+ react-dom:
+ optional: true
+ react-native-worklets:
+ optional: true
+
+ '@expo/ws-tunnel@2.0.0':
+ resolution: {integrity: sha512-j+JfTRdCk820J9dU0sA2SqshQIKFOMo7ED84w9MJFcebfbNQgsLztEY/SABDkGnjatrW4xGqnUhVRxSBVyCkXw==}
+ peerDependencies:
+ ws: ^8.21.1
+
+ '@expo/xcpretty@4.4.4':
+ resolution: {integrity: sha512-4aQzz9vgxcNXFfo/iyNgDDYfsU5XGKKxWxZopw0cVotHiW+U8IJbIxMaxsINs6bHhtkG3StKNPcOrn3eBuxKPw==}
+ hasBin: true
+
'@fastify/ajv-compiler@4.0.5':
resolution: {integrity: sha512-KoWKW+MhvfTRWL4qrhUwAAZoaChluo0m0vbiJlGMt2GXvL4LVPQEjt8kSpHI3IBq5Rez8fg+XeH3cneztq+C7A==}
@@ -2391,6 +2791,9 @@ packages:
'@module-federation/bridge-react-webpack-plugin@2.0.1':
resolution: {integrity: sha512-D7LMW5EMAJShOMR1aZDAJ6s+MdsYDHaQyJADLQ3LaY0sne/BkVqkPikUwcO1IwOwKbXjYsDlQVOEvk9wZVRFhA==}
+ '@module-federation/bridge-react-webpack-plugin@2.1.0':
+ resolution: {integrity: sha512-c/iiwLwxHDG5i227v2GQ84JRPWHU+d2uhxhZhbxIAQ5uRe6kbtj8O4uPUfEq+iabiqqtUwTLbcpUFFy1bLllYA==}
+
'@module-federation/bridge-react-webpack-plugin@2.8.0':
resolution: {integrity: sha512-7AaaiE4YOXFb+st6xlVDK65aNKZYR9S9ykH0q2mkHAHTgquXciAjypS8JuhmKn7Lob/2rkplMOc4YsGxG3Ng9Q==}
@@ -2409,6 +2812,11 @@ packages:
engines: {node: '>=16.0.0'}
hasBin: true
+ '@module-federation/cli@2.1.0':
+ resolution: {integrity: sha512-VbMJMEfP1vp/693HbQTMYqMu73Qbv23aJEW9/NhmVkRXkfjBtNfP+mROSFjJVQsWhYyU5vy8kBX7DQS/mvZGBg==}
+ engines: {node: '>=16.0.0'}
+ hasBin: true
+
'@module-federation/cli@2.8.0':
resolution: {integrity: sha512-yTxdWkCJPPo+IGASz+NqdW13cw3DhjSBEn9r85aYn1ahckDwB1WcZe0OjDWMqCcR6yi0BMLedk0SWrUeUj0fWw==}
engines: {node: '>=16.0.0'}
@@ -2443,6 +2851,17 @@ packages:
react-dom:
optional: true
+ '@module-federation/data-prefetch@2.1.0':
+ resolution: {integrity: sha512-/rHwtZEknzujpCoXChZcy29vD7zNY2b/XfAcOpCkMVfWyQiNhppKxeyjA6FnPEp64NAOLzj2XxaadceXa1eFeA==}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ peerDependenciesMeta:
+ react:
+ optional: true
+ react-dom:
+ optional: true
+
'@module-federation/dts-plugin@0.15.0':
resolution: {integrity: sha512-UztaFAhpCpsy+EUOP1BiqlYpRdD4h2TUITphCmThO1grOCqU7dYYwGjWNy37NtJeykRRznH3FU0+iGBG3Oiw6w==}
peerDependencies:
@@ -2479,6 +2898,15 @@ packages:
vue-tsc:
optional: true
+ '@module-federation/dts-plugin@2.1.0':
+ resolution: {integrity: sha512-2ubWFjF72i3Z5TM2G8hg6SOS6dB0v7PRLXPUMNoVMBxHGxiFG/F0xryZ2UYFwLA2hcNmf60LNP30F1tJhsH4wg==}
+ peerDependencies:
+ typescript: ^4.9.0 || ^5.0.0
+ vue-tsc: '>=1.0.24'
+ peerDependenciesMeta:
+ vue-tsc:
+ optional: true
+
'@module-federation/dts-plugin@2.8.0':
resolution: {integrity: sha512-defjq4jOWMEfeejezPWLP5sc8kw0O6FqTT7/E5rbZPEVyjB1A0U3ynhW6GDE5/6hk9/TzdbWS+fBNi4MqUOY6Q==}
peerDependencies:
@@ -2547,6 +2975,21 @@ packages:
webpack:
optional: true
+ '@module-federation/enhanced@2.1.0':
+ resolution: {integrity: sha512-nWCe31vzYLGsT3DYf2cKtxSjUDLWVgErZeDEB8cddtuA3c4npSdKeG8P/bI9GtRph5ybIUFbyAMtuKPPhMahOw==}
+ hasBin: true
+ peerDependencies:
+ typescript: ^4.9.0 || ^5.0.0
+ vue-tsc: '>=1.0.24'
+ webpack: ^5.0.0
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+ vue-tsc:
+ optional: true
+ webpack:
+ optional: true
+
'@module-federation/enhanced@2.8.0':
resolution: {integrity: sha512-h8vkLdhK7tlcSPmyYNGfGyt0pSzfDB0tYVYdyUt2tXwQRfaJAi3bsIpujMXElw3MXtOfSHESa6M/hPKrtWTHBw==}
hasBin: true
@@ -2580,6 +3023,9 @@ packages:
'@module-federation/error-codes@2.0.1':
resolution: {integrity: sha512-2bJF/ft+qL9L6Zvq2t/G9/f/0wFL73cM8/NJ04uyYz9BjIgvx28K5qu8/6+IwgEEKATG7vOhBBVj6wH3S+5ASA==}
+ '@module-federation/error-codes@2.1.0':
+ resolution: {integrity: sha512-W+uCmPsFuV+15E1S7JUB1AeUDBFqKjJ2hImXdBNYx7T1CGM6awS/veooXqNoP7iM/kwKjtpTQPIeccWLrq76Mg==}
+
'@module-federation/error-codes@2.8.0':
resolution: {integrity: sha512-Gaog9904EmxYOQV0hli3XQ7jXeFaADfh5bnBtTCtbZ37Qd/Sz9kQfd+gYQRyIj7RGmkv9DPiN/SsmrTMrTymKw==}
@@ -2603,6 +3049,11 @@ packages:
peerDependencies:
'@module-federation/runtime-tools': 2.0.1
+ '@module-federation/inject-external-runtime-core-plugin@2.1.0':
+ resolution: {integrity: sha512-okAVRH/9rROh1fBSKF7Li/Ia8bQhgz38AfVvUSzVu32/HCvdjpfddQtPFFxvmi2oayPgUDY4Qy8RXT1pUlBqpA==}
+ peerDependencies:
+ '@module-federation/runtime-tools': 2.1.0
+
'@module-federation/inject-external-runtime-core-plugin@2.8.0':
resolution: {integrity: sha512-fW3jD1ZVds6r/Ul8TtUA42RsB0LfT1yjo5KjqgirH9QrmEMH21x44e3e6BC6IN820XKawRDSmz2kFA5YHIQp7Q==}
peerDependencies:
@@ -2620,6 +3071,9 @@ packages:
'@module-federation/managers@2.0.1':
resolution: {integrity: sha512-KR01lSlcYRQ9C6hW2a8CQQtAE0LvfTLgtV/6ZNUTagw8sRfeDln+ggrZsYilKu9zl0i8RPDgpv/kS60o4lcxCQ==}
+ '@module-federation/managers@2.1.0':
+ resolution: {integrity: sha512-8HX721e3uuDSURtnOpj6Zy/+Qc4IM5no9hMPODYdGjrYe2YUmXY4/5JScSVnFeYm+zBPw6y9QoufeG9g2jrWBg==}
+
'@module-federation/managers@2.8.0':
resolution: {integrity: sha512-SnVBCwmi962WGg6hLFElxZUCnrRJdR6glE2ZKPBY/iK07AHUN2ZxuaBCBsVzyws+xLZGHZxBHmVstijTh8dSUA==}
@@ -2635,6 +3089,9 @@ packages:
'@module-federation/manifest@2.0.1':
resolution: {integrity: sha512-p8nYGjHWp17MsYdW/Vv0ogBDiTTsI1PHWPQbvVIqLQXDqwiesaRSRR1zziECXQoEL8lV5Bs+uSkcaJGhea9P+A==}
+ '@module-federation/manifest@2.1.0':
+ resolution: {integrity: sha512-icIUhMG4FwaFZyBmVjadkdqscNb98iXrITTVeMeAxJcrnZltSBBSEHepfpfeW+tHW+wMmr+lWFnAbSCepV73+A==}
+
'@module-federation/manifest@2.8.0':
resolution: {integrity: sha512-wfVeBXc4/C2F70nRFSPqJhkcwbDgo+wQyEn3jbjJTDoUqxxhYBfHFs1ACBYOk3Qm97L7hHclHGtUG0/nvDEfAA==}
@@ -2686,6 +3143,18 @@ packages:
vue-tsc:
optional: true
+ '@module-federation/rspack@2.1.0':
+ resolution: {integrity: sha512-bojG6yIoWsta7CDdKZ3nrTn1IKT98algUGG5/uyR6nhyOxsu7CJpf17kcLUqTKdBwN9S8qZOjycXGDeEX//N3w==}
+ peerDependencies:
+ '@rspack/core': ^0.7.0 || ^1.0.0 || ^2.0.0-0
+ typescript: ^4.9.0 || ^5.0.0
+ vue-tsc: '>=1.0.24'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+ vue-tsc:
+ optional: true
+
'@module-federation/rspack@2.8.0':
resolution: {integrity: sha512-TPcrkHpaZgL25Vx3c8oSNwyv7/KktC7uo6HTQdVWlFzbq5RSoMMGkWoir5pY5124isae2/p6v5xAuqICi4r0Zg==}
peerDependencies:
@@ -2716,6 +3185,9 @@ packages:
'@module-federation/runtime-core@2.0.1':
resolution: {integrity: sha512-gOuCPSHoQGUGwlxfSTMInFX+QvLxdEWegGGMiLdU5vqbXuva4E9M+kXBBO7/0MkcBPMmVs0wOJGm0XOLeV2f1Q==}
+ '@module-federation/runtime-core@2.1.0':
+ resolution: {integrity: sha512-9W+wV5s7PTMnSFCmyNvItnOf3VRYSxAPMZQ91bOT4GdwHTO23dfmC57o0SiqXw+ri/XOQVA8gd/p8TDwDDYx6A==}
+
'@module-federation/runtime-core@2.8.0':
resolution: {integrity: sha512-Tf98+epGGiPSHqmQHuXa2uXZMMvjGf1IqJDR1/FpXfmobv5ECN0mGZCjUHGNSyxvoDyXKIkKwJu7IwEoh0ouQA==}
@@ -2737,6 +3209,9 @@ packages:
'@module-federation/runtime-tools@2.0.1':
resolution: {integrity: sha512-AStdwBtsGB3jIfDg9oP+KyVPsimdaeHsP855gqCxDp1hi2+GKjlZWZx9ThkS8NytVSXSUysxqoUL1ivDoKgcCQ==}
+ '@module-federation/runtime-tools@2.1.0':
+ resolution: {integrity: sha512-2pOyGOiWIGG0+fE0jBY6pRYVH4+G/gFiP9KnyVDp6zj3leFRdePtlIZDa4O0X1dQcMOMmOORrx+TLRZeygbCnw==}
+
'@module-federation/runtime-tools@2.8.0':
resolution: {integrity: sha512-3yOqjdSHXxX4HA3GhlXg3hghGAXW2RJUsnwXCcik2/lTxOHizKI8f3RM+GGCKPxDVqtw43IShe3tA12jNL5A/A==}
@@ -2758,6 +3233,9 @@ packages:
'@module-federation/runtime@2.0.1':
resolution: {integrity: sha512-UQ72P5Oo40dS6vdhHetwTtIsbGciEr+bjoYvDgh1WLPfFlTYd8zo9cLfqaf3juuPfV3cMVARAVPmh16lQYpUGA==}
+ '@module-federation/runtime@2.1.0':
+ resolution: {integrity: sha512-Cs6H6vAQrLeD7tWW3nI7Z9EdvhcFcbqQdYWJ2SaN1X/eX2YvgHJe8sRxa7K7zlVRV5QZEPKgQCbrUfef+d5xqQ==}
+
'@module-federation/runtime@2.8.0':
resolution: {integrity: sha512-cGtUBQ1/TVy7KrXy6xPgy3FEmOGyIYkBA2T4iGH3ZH5PNPPTmqN9jF2AfneTSOj0RtBr7Pxq3CUt81E/UCvK1A==}
@@ -2782,6 +3260,9 @@ packages:
'@module-federation/sdk@2.0.1':
resolution: {integrity: sha512-32PwudojGjog51cwpTali7D6ud82oVgsyvOx9JjAzhvXBX96YI4mRsursuWcthDxmigJP9ZvUTXDuRUEDh1OQA==}
+ '@module-federation/sdk@2.1.0':
+ resolution: {integrity: sha512-HhiSo1X+t2+r5lxU+JBVsJdE2IJZOaD1e0linw+4bLlEy8uIgXhGttF9+9rAnRKWlhn6R8E23ionwBCuSLVeXQ==}
+
'@module-federation/sdk@2.8.0':
resolution: {integrity: sha512-yBP+9+0Z8nlvKEXAZS3AsQVy7bFbZf8eMivGk4q4ZdwG3TsLMlsPjb1dQb2i7gcAG6ux9y2LWLkj/0LVk74cnQ==}
@@ -2797,6 +3278,9 @@ packages:
'@module-federation/third-party-dts-extractor@2.0.1':
resolution: {integrity: sha512-neKSr6FNUeGRh+YR57l/QZUzPytJXuJx+babF7j5iGJG3FP+kfizr6QD0hgVis5KEoXMVbQ8yyvG0slERizeyw==}
+ '@module-federation/third-party-dts-extractor@2.1.0':
+ resolution: {integrity: sha512-w/hn0J+gw+lEfsXTR3DsbtcxpAndMZJ2PHnQTFn2s5BujNL18FcStaoz0tDpcJAVxi2iQZATJ3bGrlO2t2aDjQ==}
+
'@module-federation/third-party-dts-extractor@2.8.0':
resolution: {integrity: sha512-nAMlr74OKIylkfRwlunOhytQbmsgb3gCqdXWnPQhG+ZtqWXGELLfMT4a1Q1ht3cS+sRpWj2SZRqK2M7GadI6tA==}
@@ -2818,6 +3302,9 @@ packages:
'@module-federation/webpack-bundler-runtime@2.0.1':
resolution: {integrity: sha512-u1NId3SF4lHDTmD2CHFEszulmXmIq1TGw9JYvnLx5rKJL7xt3aNxcb1GvkaYbRNVBXhSMjJ75E5LsQlZzyBx9A==}
+ '@module-federation/webpack-bundler-runtime@2.1.0':
+ resolution: {integrity: sha512-yThI7cPanvH5eobaeFUsQ51sjllA3nyN/8OxfSdlbeTogLF4K8tkCr6H7QW+alE9lXxOzI2BTCxMV6NJBKWmTQ==}
+
'@module-federation/webpack-bundler-runtime@2.8.0':
resolution: {integrity: sha512-82fDy9v+7qV5fiN8TKVhOdrxhmAZnUIX/IKivYX5ulCt8aoOzVFTiwm/P1GQUDD8z6dqR48xgJdZdf0548Mc9w==}
@@ -2845,70 +3332,353 @@ packages:
'@pinojs/redact@0.4.0':
resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==}
- '@react-native-async-storage/async-storage@3.1.1':
- resolution: {integrity: sha512-z+PnLz1n6ECKhgoHZHkfc+dijXZEyZnNFSajbtE0NEbsJhmX8x9GlOeiMQIKX2E4DUqPSgfIh4FYBv1M49KgPQ==}
+ '@radix-ui/primitive@1.1.7':
+ resolution: {integrity: sha512-rqWnm76nYT8HoNNqEjpgJ7Pw/DrBj5iBTrmEPo6HTX5+VJyBNOqTdv4g89G63HuR5g0AaENoAcH7Is5fF2kZ8Q==}
+
+ '@radix-ui/react-collection@1.1.15':
+ resolution: {integrity: sha512-9W+B9NPF0NaaPh/1NJd3+KqsnlLqU9H7T2rvww+fp+T/evVXdNAyYcnfRQZFOjkR1ajQp3yORlqnI8soawLvNA==}
peerDependencies:
- react: '*'
- react-native: '*'
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
- '@react-native-community/cli-clean@20.2.0':
- resolution: {integrity: sha512-krqbhFiwHN8l5wZ2XTcrNq9N+DqDWHxW8RK0bIcrK0O+fMvjYi9e/1Pnx2W0CTotOVcZpHzUbgZ4TQVF231Skw==}
+ '@radix-ui/react-compose-refs@1.1.5':
+ resolution: {integrity: sha512-+48PbAAbq3didjJxa+OaWY2ZwgAKsNiRGyeHKszblZMQ+kcpd9pAaT11cMkGEie0vsOi3QdeTE6d5Fe3Gn61kA==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
- '@react-native-community/cli-config-android@20.2.0':
- resolution: {integrity: sha512-lASofUNBVK0Pq3VEJ1JrCfAW94Rnk38DikauycRcoHl0hWjHIN3XhsN20dEq+CaIJoq37aCg5NSSSpmkET+ShA==}
+ '@radix-ui/react-context@1.2.2':
+ resolution: {integrity: sha512-RHCUGwKHDr0hDGg4X7ma4JG4/+12qxw8rkh5QKdDldlCvtja6nUx1Ef/8HVrJze81lEsgLQlqjzjGNHantgnQA==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
- '@react-native-community/cli-config-apple@20.2.0':
+ '@radix-ui/react-dialog@1.1.23':
+ resolution: {integrity: sha512-Ksw4WeROkO4rC9k/onilX/Ao2Cr1ku1unMNH+XSCcP4jSXYu7HDsg9n4ojMjVb22XpYjAQ9qfrFlVbru1vXDUA==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-direction@1.1.4':
+ resolution: {integrity: sha512-5pzg4FGQNpExhnhT2zlrP1wZFaYCd1K0nYWoFAdcYoYK868IEigqMX3B3f8yIoRlAhAeDWciLI6ZdCKHF9P4Vg==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-dismissable-layer@1.1.19':
+ resolution: {integrity: sha512-8g4pfOL9HoKKLWGiypT+dphVqjFfmcXO5GBnhsG6zI+lxAx/8feQpr+1LSN8Re3hiZ+XkLNS4O9ztK11/LzQ6w==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-focus-guards@1.1.6':
+ resolution: {integrity: sha512-RNOJjfZMTyBM6xYmV3IVGXkPjIhcBAuv48POevAXwrGJhkWZ9p1rFoIS1JFooPuT193AZmRsCPhpoVJxx6OPoQ==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-focus-scope@1.1.16':
+ resolution: {integrity: sha512-wmRZ2WWLvmt6KHy2rNPOdPUjwq5xOHY02+m+udwJTn0aNIox/rkskAvJTyTLGhPK6KgrUjlJUJpgmx/+wFiFIQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-id@1.1.4':
+ resolution: {integrity: sha512-TMQp2llA+RYn7JcjnrMnz7wN4pcVttPZnRZo52PLQsoLVKzNlVwUeHmfePgTgRluXFvlD3GD5g5MOVVTJCO0qA==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-portal@1.1.17':
+ resolution: {integrity: sha512-vKQLcWypUnwZVvfV7UkGahH2g6ySe8M8R+zYBwPrv5byZ9QAW6cQVvNKo7GgmD+p8aYb6D9JBuvy8/WhOno2wQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-presence@1.1.10':
+ resolution: {integrity: sha512-3wyzCQ6+ubRA+D4uv9m95JYLXxmOHp05qjrkjeA7uKHHtjpPggQzc6DAb0URl7j67oR0K2foO4ip27TiX037Bw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-primitive@2.1.10':
+ resolution: {integrity: sha512-MucOnzh6hR5mid6VpkbglRAMYMjKLqRnGBbjXkzjK52fuQDd1qbkx78a5P40mkcnVXJdEVxm26E9OPAiUq7nBg==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-roving-focus@1.1.19':
+ resolution: {integrity: sha512-V9jI6hDjT7l3jsCQD9bLNvDLM3tH/gdbOTp7Tefp3hbbgCGQoK7tUvrWiRlcoBHIZ809ElXwNQwVo0B98LuTXQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-slot@1.3.3':
+ resolution: {integrity: sha512-qx7oqnYbxnK9kYI9m317qmFmEgo6ywqWvbTogdj7cL9p3/yx4M48p7Rnw5z3H890cL/ow/EeWJsuTykeZVXP5Q==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-tabs@1.1.21':
+ resolution: {integrity: sha512-UKxJlZid7FVtsk/WTxj4i4uSEgj2Au+KBbS7SQyTlzMhhn+86Cz3tISZdTa87bfEfcuvZezf2ZsxD4xuEKtkog==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-use-callback-ref@1.1.4':
+ resolution: {integrity: sha512-R6OUY2e2fA6Yn6s+VSx5KBV6Nx8LQEhu+cz7LCej18rQ1HLyg9PSC9jP/ZNx0o6FAIK9c0F1kHylzSxKsdlkrQ==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-use-controllable-state@1.2.6':
+ resolution: {integrity: sha512-uEQJGT97ZA/TgP/Hydw47lHu+/vQj6z/0jA+WeTbK1o9Rx45GImjpD0tc3W5ad3D6XTSR6e1yEO0FvGq6WQfVQ==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-use-effect-event@0.0.5':
+ resolution: {integrity: sha512-7cshFL8HGS/7HEiHH+9kL9HBwp2sa9yX18Knwek6KYWmXwM7pegMgta2AXMQKI+rq3JnfSj9x8wYqFMTdG1Jgg==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-use-is-hydrated@0.1.3':
+ resolution: {integrity: sha512-umO/aJ+82CpOnhDZUTbILCQf7kU/g0iv+oGs/Q8jw7IkhWBzaEP4sA268PhFAJTFetbwp3ICc6ktpI4TqtxcIw==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-use-layout-effect@1.1.4':
+ resolution: {integrity: sha512-K20DkRkUwDnxEYMBPcg3Y6voLkEy5p5QQmszZgLngKKiC7dzBR/aEuK3w1qlx2JWDUNH6FluahYdgR3BP+QbYw==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@react-native-async-storage/async-storage@3.1.1':
+ resolution: {integrity: sha512-z+PnLz1n6ECKhgoHZHkfc+dijXZEyZnNFSajbtE0NEbsJhmX8x9GlOeiMQIKX2E4DUqPSgfIh4FYBv1M49KgPQ==}
+ peerDependencies:
+ react: '*'
+ react-native: '*'
+
+ '@react-native-community/cli-clean@20.1.2':
+ resolution: {integrity: sha512-XcNlmFnYOVDjvHQQn0qreI4FPLEUx8p43EdOmKbtzqwqc9T/EdBdzUnUc5wWQPO1CN7BdMfxW8fyvosz8NIlrg==}
+
+ '@react-native-community/cli-clean@20.2.0':
+ resolution: {integrity: sha512-krqbhFiwHN8l5wZ2XTcrNq9N+DqDWHxW8RK0bIcrK0O+fMvjYi9e/1Pnx2W0CTotOVcZpHzUbgZ4TQVF231Skw==}
+
+ '@react-native-community/cli-config-android@20.1.2':
+ resolution: {integrity: sha512-W0Qx+lW8pbQMz8x3Rlf/H7D2j2u8O+u9HnrZnKzDl1DaXgaOQqL484lTZlMEQofjq7eLXdmzWxuZdqS6K1QfmQ==}
+
+ '@react-native-community/cli-config-android@20.2.0':
+ resolution: {integrity: sha512-lASofUNBVK0Pq3VEJ1JrCfAW94Rnk38DikauycRcoHl0hWjHIN3XhsN20dEq+CaIJoq37aCg5NSSSpmkET+ShA==}
+
+ '@react-native-community/cli-config-apple@20.1.2':
+ resolution: {integrity: sha512-Dhi1N1EoMMmJ4dnDlmNWCrJggfv7X/kl3l8uax72uaxepQI/CfohJP2rBdG2mWis+vzrCIk14z2keY0ixxsN8g==}
+
+ '@react-native-community/cli-config-apple@20.2.0':
resolution: {integrity: sha512-oohr6BV2riWJ5PYjayi1u52bG3H95MwKPkJCZo9pnOlYTjifZQVkMxZ8VTuk392efh0bijVTYfxH8iJPi97SIQ==}
+ '@react-native-community/cli-config@20.1.2':
+ resolution: {integrity: sha512-7aPE14QA8aXpfuQ1jmfiBfjC/N6gdbg6PpBTwb3cl8c/KaeVm+OQYoC2kn2b3XS0NPgw5Ix/VxVaX6AAUQRFuA==}
+
'@react-native-community/cli-config@20.2.0':
resolution: {integrity: sha512-GF5FxgDfOSLPi/bSiE/xvOGELwzV2GLQnDED+7ArbPi01W1Vu+hD/m+J+bwX34Pocpo767eXAVxc2h9WFUTUfQ==}
+ '@react-native-community/cli-doctor@20.1.2':
+ resolution: {integrity: sha512-bbT1EhomvHz5ZtzxY2czA4/JMXhP4aIAxRDsqiW6wfZA9A9/HXqA4uv6CxP0wZUUmovmPNRl3kW/LWXrRmdv3A==}
+
'@react-native-community/cli-doctor@20.2.0':
resolution: {integrity: sha512-eZjlwmjPoBXgyD6nV5oDDocL1VH5UW4LxZcMAqyN3rQw5vq3CeJikFpNedKTtSl3P6JizIkInyIHpfrW+9qfJA==}
+ '@react-native-community/cli-platform-android@20.1.2':
+ resolution: {integrity: sha512-1iHB8cTTJpMyEKhxWTRYsxhBBsiOq3tVguGX/HtBdHRzhlCeDpanE6U+aKxWfMFetMcFL+HLe5nQPcJXf9EtKg==}
+
'@react-native-community/cli-platform-android@20.2.0':
resolution: {integrity: sha512-fRglzcf/Yq5lnIkqdA8uf1GvlG18x3Dm48Yvo+l7KiWgh/SVtfVhfvalrtX5rmx+KeJkDA534bGoPvi5+ruWjw==}
+ '@react-native-community/cli-platform-apple@20.1.2':
+ resolution: {integrity: sha512-UvzjcRGotO3E2xaty8YWE2XMGkkDDaXRtQtNRjzmtcoNY40C+y4iMHxd0o3xbD0bzYM/PO79tXye9MxTWdyVkg==}
+
'@react-native-community/cli-platform-apple@20.2.0':
resolution: {integrity: sha512-jkEPLAd8C/ZRu39a3nhxwSXe0iisUiJC808GExnrnTcViLtg3sad2ciQ/HjjvbdsPCS9sT+Jr7xh3MzBsPojsQ==}
+ '@react-native-community/cli-platform-ios@20.1.2':
+ resolution: {integrity: sha512-ZzdLwJMt7ehjO0iy/rQGPgH6uZqMYXeS5uXzSi1DeLYwurV1wOqFc0SLm4TAz5FKYQmHpwBXlMiI12rUmkZxcg==}
+
'@react-native-community/cli-platform-ios@20.2.0':
resolution: {integrity: sha512-bCKlBt2HoD7WTl/HX60+4HFhR1lKY64Y9MPWu7yWQwOCEqYRMi6MkRxLimiONj9M2/lNf0z9BPHDzdDe5HM2ag==}
+ '@react-native-community/cli-server-api@20.1.2':
+ resolution: {integrity: sha512-ZlINtIYoDAwSemwTU9OavI1IixCCmAPPw1s3Mp0cOvrddFSZ0hx1N1IR+imLyo4lhFfM8OO3rUe9oVJj1SHUCA==}
+
'@react-native-community/cli-server-api@20.2.0':
resolution: {integrity: sha512-AI1fsl+6LNuOoDcdWsnF3s9ozqminGCUlbFcrgdZIJWyOuFBeQclNydI22VO3vWaO4dHQkfVa3Zo10AncWQvwA==}
+ '@react-native-community/cli-tools@20.1.2':
+ resolution: {integrity: sha512-on2VUBZb68RlMxvIrEdK6+NiOEYu/z+t/cz746yGtxn49fwW6Wafzmh1QNZj8HPAuZ8+Ds61LiXbwoDDkzNSSA==}
+
'@react-native-community/cli-tools@20.2.0':
resolution: {integrity: sha512-A5W2nqlTidPzGyXzS57jvHsMpQd8iOGSjePj4H318uMbhIdeIHQ5e59fNbdHahS62ISs+2p9s78sVHMbGVa1bg==}
+ '@react-native-community/cli-types@20.1.2':
+ resolution: {integrity: sha512-WYK98VdcJE+lRuyRzigE/GQAbaJZOKkjpaLwhmMMItXVTqMmIccfGu9b4pRoQOVfs1aLq87DuwUOi9sxz6OG1g==}
+
'@react-native-community/cli-types@20.2.0':
resolution: {integrity: sha512-K60zY/ly8G9rGJQzZ+bFRyLEckAUzyF8Fu9a2Kw6JipCrOl7SiVRBVqjvOr/XnCRKuOpH8i9y7RVg07wkSGRBQ==}
+ '@react-native-community/cli@20.1.2':
+ resolution: {integrity: sha512-48GRnGfm1+4ueV8ESNJmKAKW01QdbB8H6qrVxCqpHYvuRkeFBaPpwRY2bEjSwqruw3Pn9ppzGfpAxYQDiUWQxQ==}
+ engines: {node: '>=20.19.4'}
+ hasBin: true
+
'@react-native-community/cli@20.2.0':
resolution: {integrity: sha512-5RZKkBeUFC4hhrzySzxWmRJwXBTc2PE3L6QUgvnJk3QDxNYMY7aGuI8Gr9eZRS1DbiY10IPWqpJfqSIplzVG6A==}
engines: {node: '>=20.19.4'}
hasBin: true
+ '@react-native-masked-view/masked-view@0.3.2':
+ resolution: {integrity: sha512-XwuQoW7/GEgWRMovOQtX3A4PrXhyaZm0lVUiY8qJDvdngjLms9Cpdck6SmGAUNqQwcj2EadHC1HwL0bEyoa/SQ==}
+ peerDependencies:
+ react: '>=16'
+ react-native: '>=0.57'
+
'@react-native/assets-registry@0.86.0':
resolution: {integrity: sha512-nIaXbm2jX1OTYp0qbviJ3O6KZivoE8z3BnhUQ2LsqfZSWRoOK/n1qsiAr6oALiNKWnXY3j2KPwtYORnZzp8xew==}
engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0}
+ '@react-native/assets-registry@0.86.2':
+ resolution: {integrity: sha512-vcX/mBjWAVnWofu7KecotquI2unZ/tITwA7OGdq/mdY/zmGXIEvYhfEYyOQij/LRqi9WAL+iizInTBWnxDhK/Q==}
+ engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0}
+
'@react-native/babel-plugin-codegen@0.86.0':
resolution: {integrity: sha512-qdsABWNW7uTll90l4Vh03gjeyu3WVDi2CyiiyvYGMRDcoYbjbQi6df3BMAm9lQI2yslZ1T14LlDDAsgTwNxplA==}
engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0}
+ '@react-native/babel-plugin-codegen@0.86.2':
+ resolution: {integrity: sha512-NNDZqOlNbH5SzgPks1jFDYH3234Rpa5e/nhZymxhIiBH3NcE3uD+rGj/HWXhH7nHF2ToGK6XbUpqy7nmJPeh+g==}
+ engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0}
+
'@react-native/babel-preset@0.86.0':
resolution: {integrity: sha512-bYQcWiPySNvF4dns9Ls9gMmwgq66ohvM9Fwc/Kn8r85t66UNHxch3p1QwPiSorDelFauZwJbgo9+ReibTgvpbA==}
engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0}
peerDependencies:
'@babel/core': '*'
+ '@react-native/babel-preset@0.86.2':
+ resolution: {integrity: sha512-4XKEJ6jKW9lXMB1O5o47gBoGgolde1fbX13gLW/erlcn+1ky+MHHo5UjuM3RWGdPJHIvIzekDDumSUHhB9x5iQ==}
+ engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0}
+ peerDependencies:
+ '@babel/core': '*'
+
'@react-native/codegen@0.86.0':
resolution: {integrity: sha512-uTs9DBo3+/lUqinsGZK0FKJRBVClrwMXoZToaDxE1Q2SL2e55vs2GwyZfIKzPl5uJnbu4PfFMIp0/mLXLWUMuA==}
engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0}
peerDependencies:
'@babel/core': '*'
+ '@react-native/codegen@0.86.2':
+ resolution: {integrity: sha512-xKkudsahUJ1n//55g4fXk5BStVqqmZlz8HQveL45ZxcfDnwvhuYe2GymksQANFsSN+slvrarjrfq8kIxJzbceA==}
+ engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0}
+ peerDependencies:
+ '@babel/core': '*'
+
'@react-native/community-cli-plugin@0.86.0':
resolution: {integrity: sha512-Jv8p1ebEPfTzs8gmrjsdT2XMXFfeAg45Pman+XPLFGaSeGAZkutRFRyX9Cs9aGTSOyIA9YPJ6vDNb1ayTf1FKQ==}
engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0}
@@ -2921,6 +3691,18 @@ packages:
'@react-native/metro-config':
optional: true
+ '@react-native/community-cli-plugin@0.86.2':
+ resolution: {integrity: sha512-YHXNKoM6Y/HjREySZ5arET2xgiHgg67r1MdwJB//MPJAJ0Xc5g0u6UHxY9VzsHO3Y07dre6s0BinYwjt1SEWvQ==}
+ engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0}
+ peerDependencies:
+ '@react-native-community/cli': '*'
+ '@react-native/metro-config': 0.86.2
+ peerDependenciesMeta:
+ '@react-native-community/cli':
+ optional: true
+ '@react-native/metro-config':
+ optional: true
+
'@react-native/debugger-frontend@0.86.0':
resolution: {integrity: sha512-7Mb3nDfyJeys+ELF75Ageu7VKERlnIMoO+aNPoXqTXvz+b41L6l2CqMyLpDHxkBSlenij6gEepPNgaIyWHbJZw==}
engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0}
@@ -2929,6 +3711,10 @@ packages:
resolution: {integrity: sha512-DRl6ctuVNcY1CtiAKFstWMDlJhXQ4tM96XVib3tTOH2zIoJPgeiGQdXUUa4tsPgqR4RkXsd5adDYwcFkE5QvVg==}
engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0}
+ '@react-native/debugger-frontend@0.86.2':
+ resolution: {integrity: sha512-KGS1aV5F6cIqpnoIUhLBXyVzy1oAj8jBFGau6vX4Vy0HXRJN7p+68RU7x6NuyraHvQcR14ccMGT5TkFuNjQ4gA==}
+ engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0}
+
'@react-native/debugger-shell@0.86.0':
resolution: {integrity: sha512-Y0zEkZzLz8ou6o/VLml1A31X/rMgc6DRjwxwzPMa94qRTMY070WeBCNTITQo4kKTBAUgbxh07oXPQqp0Tpja8w==}
engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0}
@@ -2937,6 +3723,10 @@ packages:
resolution: {integrity: sha512-IkAfhWr/zh2/n8109vQc/Gf8D6oGFzTmbLezmyal5Lw7VzIOBDBoSp0usx1Zae16eZXF1nerRN+Dyua211f8Cg==}
engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0}
+ '@react-native/debugger-shell@0.86.2':
+ resolution: {integrity: sha512-/TaVJ2+gGajZPJGrFaObUQmHmlaxAlfmOPZicl6pNKDUjzSgFMpcLkdTOExvb+USYTVdGX1XwxXyvjQdUO2bvg==}
+ engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0}
+
'@react-native/dev-middleware@0.86.0':
resolution: {integrity: sha512-20pTO6yTybmvXvro520H6C7jydIQnLKOl5qFtVEcHSdFrY63r3OGei+Rx9bILgSRmH6jgnfEcijcMx7pwWuQtw==}
engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0}
@@ -2945,17 +3735,32 @@ packages:
resolution: {integrity: sha512-+L37VJ/joaGvQifzk4dkHPXj9BsDZeH/WafMhK83PaQy3SQpZYIv/R18EjVwPVSGE+2Iciuzn+lX7bi1NM/fJw==}
engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0}
+ '@react-native/dev-middleware@0.86.2':
+ resolution: {integrity: sha512-B7L0vKvg+IcEElT7Vpqh1xj5yJAqWUegjbP+bQRaorJMAYnv11GkliTnZV2AdTDfZQJWgOEx8i8LGkHkUg7bnA==}
+ engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0}
+
'@react-native/gradle-plugin@0.86.0':
resolution: {integrity: sha512-a1RcfaEDqWExCGfCwadIxt4l8FvKYgFqeMf2uzeKyAOnb+vTGNIeCvifFL2MqvgaeYxlER437HbMIajGcuJ1pQ==}
engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0}
+ '@react-native/gradle-plugin@0.86.2':
+ resolution: {integrity: sha512-2F6x14NcHMpVmfTTFKfMkpV5dZedZrLiv6PE+c3vgnesV2bjleUBydr4U+NI8VkI7OwW71L0A5qQ76I9LCrfoQ==}
+ engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0}
+
'@react-native/js-polyfills@0.86.0':
resolution: {integrity: sha512-zYy/Cjd1VTnZ2iCNaG9bDF9C3l2ntESiPRscjIlI5FKugu6aeTwsDSv1aI8Bc4Kp3vEdoVg+UQhLAhE4svREaQ==}
engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0}
+ '@react-native/js-polyfills@0.86.2':
+ resolution: {integrity: sha512-bIwNcGBaQ74shB5z1mRkxOpjikimuwsnOCEkZSzL67Z1FTyK1ObpENfyd2QvcvVW9Cjl+tHuw9ynpBnb2jPoJQ==}
+ engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0}
+
'@react-native/normalize-colors@0.86.0':
resolution: {integrity: sha512-kG0wfCGghUKlfxkJyyHCDVutWVYWK7/DG58ojA/4v9EfulgF+osuSQmlbNb3rcKX58qutm7JcldSeVLgGFha9g==}
+ '@react-native/normalize-colors@0.86.2':
+ resolution: {integrity: sha512-EzPFc9Y6lzYOWeso2almwXI7f8+qReHxWvT+algsOczb2UhWXIWXDoSvkdwoSfiwwmGt/ijJgKJoeHlzPkLwRg==}
+
'@react-native/typescript-config@0.86.0':
resolution: {integrity: sha512-oSuFIEMVAVEXdIvDnrdXsWmIF4fYdgtvAEr2ofn8OY534m7XWm9QAqHHpGUzoK0iwDPlPZ5n2Rb25gqF1SZ0rg==}
@@ -2970,6 +3775,17 @@ packages:
'@types/react':
optional: true
+ '@react-native/virtualized-lists@0.86.2':
+ resolution: {integrity: sha512-uO0J72gh3EvE+1/GHRk18QRyBDTRHRB0AraAfojsRjbT7VMuJwKrZYaKGshavoaEud6aw00ZB9/8mTMIKjjcAw==}
+ engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0}
+ peerDependencies:
+ '@types/react': ^19.2.0
+ react: '*'
+ react-native: 0.86.2
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
'@react-navigation/core@7.21.11':
resolution: {integrity: sha512-bCW1PsLA/eOXDOukcJFEzlcL3Zpy8DJuDCfkDDwAQlAgoSZ/J9+ZeDRUMmCUi6xbnFgvFEEIMertaLeErOFP0Q==}
peerDependencies:
@@ -3773,6 +4589,16 @@ packages:
'@swc/types@0.1.27':
resolution: {integrity: sha512-K6h3iUlqeM946U4sXFYeahefR1YBbXJvko+hv8WS8/0BNJ4OHiHRywMnQUJCqkR7Y9+hqQ1TvEpiKqUhz7NEFg==}
+ '@testing-library/jest-dom@6.9.1':
+ resolution: {integrity: sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==}
+ engines: {node: '>=14', npm: '>=6', yarn: '>=1'}
+
+ '@testing-library/user-event@14.6.1':
+ resolution: {integrity: sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==}
+ engines: {node: '>=12', npm: '>=6'}
+ peerDependencies:
+ '@testing-library/dom': '>=7.21.4'
+
'@turbo/darwin-64@2.10.7':
resolution: {integrity: sha512-/c9cSBRermWDv85oufLhoH6XRLOVbvzJLRd+WLyfJCP+i0HFLQj4PVNDrHcY17/ve5l8X0Oua4bJBqJUgJPnZA==}
cpu: [x64]
@@ -3860,6 +4686,9 @@ packages:
'@types/gradient-string@1.1.6':
resolution: {integrity: sha512-LkaYxluY4G5wR1M4AKQUal2q61Di1yVVCw42ImFTuaIoQVgmV0WP1xUaLB8zwb47mp82vWTpePI9JmrjEnJ7nQ==}
+ '@types/hammerjs@2.0.46':
+ resolution: {integrity: sha512-ynRvcq6wvqexJ9brDMS4BnBLzmr0e14d6ZJTEShTBWKymQiHwlAyGu0ZPEFI2Fh1U53F7tN9ufClWM5KvqkKOw==}
+
'@types/hast@3.0.5':
resolution: {integrity: sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==}
@@ -3913,6 +4742,9 @@ packages:
peerDependencies:
'@types/react': ^17.0.0
+ '@types/react-test-renderer@19.1.0':
+ resolution: {integrity: sha512-XD0WZrHqjNrxA/MaR9O22w/RNidWR9YZmBdRGI7wcnWGrv/3dA8wKCJ8m63Sn+tLJhcjmuhOi629N66W6kgWzQ==}
+
'@types/react@18.3.31':
resolution: {integrity: sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw==}
@@ -4069,6 +4901,10 @@ packages:
'@webassemblyjs/wast-printer@1.14.1':
resolution: {integrity: sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==}
+ '@xmldom/xmldom@0.8.13':
+ resolution: {integrity: sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw==}
+ engines: {node: '>=10.0.0'}
+
'@xmldom/xmldom@0.9.10':
resolution: {integrity: sha512-A9gOqLdi6cV4ibazAjcQufGj0B1y/vDqYrcuP6d/6x8P27gRS8643Dj9o1dEKtB6O7fwxb2FgBmJS2mX7gpvdw==}
engines: {node: '>=14.6'}
@@ -4131,6 +4967,11 @@ packages:
resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
engines: {node: '>= 14'}
+ agent-cli-detector@0.1.4:
+ resolution: {integrity: sha512-qPgevFvpaQoBaRJVKzr8R7h1WPvV3DtbgRIQlne4le66KBzXx5hNBwo/+NTw67LgkKBlhCzksrdautpUdlls0Q==}
+ engines: {node: '>=18.18'}
+ hasBin: true
+
ajv-formats@2.1.1:
resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==}
@@ -4201,6 +5042,14 @@ packages:
argparse@2.0.1:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
+ aria-hidden@1.2.6:
+ resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==}
+ engines: {node: '>=10'}
+
+ aria-query@5.3.2:
+ resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==}
+ engines: {node: '>= 0.4'}
+
array-timsort@1.0.3:
resolution: {integrity: sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==}
@@ -4297,6 +5146,12 @@ packages:
peerDependencies:
'@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
+ babel-plugin-react-compiler@1.0.0:
+ resolution: {integrity: sha512-Ixm8tFfoKKIPYdCCKYTsqv+Fd4IJ0DQqMyEimo+pxUOMUR9cVPlwTrFt9Avu+3cb6Zp3mAzl+t1MrG2fxxKsxw==}
+
+ babel-plugin-react-native-web@0.21.2:
+ resolution: {integrity: sha512-SPD0J6qjJn8231i0HZhlAGH6NORe+QvRSQM2mwQEzJ2Fb3E4ruWTiiicPlHjmeWShDXLcvoorOCXjeR7k/lyWA==}
+
babel-plugin-syntax-hermes-parser@0.20.1:
resolution: {integrity: sha512-fTP6P0NV+0PrJLUQZSjAe7G4D/WvD2/Q38dNEsuBr9c6FUJ82VggApUQx6l3HnkvDtr5zHbNcJUIplGqumQ+Hg==}
@@ -4311,6 +5166,21 @@ packages:
peerDependencies:
'@babel/core': ^7.0.0 || ^8.0.0-0
+ babel-preset-expo@57.0.5:
+ resolution: {integrity: sha512-sz9ZBTiUAlu5P9VORVNKoeAaY2qKFQRC6eQV5Y8aMkqcorvXKEv97UeCkFMLmMBEPKA+QeWImREKkX9/H21WQA==}
+ peerDependencies:
+ '@babel/runtime': ^7.20.0
+ expo: '*'
+ expo-widgets: ^57.0.7
+ react-refresh: '>=0.14.0 <1.0.0'
+ peerDependenciesMeta:
+ '@babel/runtime':
+ optional: true
+ expo:
+ optional: true
+ expo-widgets:
+ optional: true
+
babel-preset-jest@29.6.3:
resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
@@ -4323,6 +5193,10 @@ packages:
balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+ balanced-match@4.0.4:
+ resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==}
+ engines: {node: 18 || 20 || >=22}
+
base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
@@ -4377,6 +5251,10 @@ packages:
brace-expansion@2.1.2:
resolution: {integrity: sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==}
+ brace-expansion@5.0.9:
+ resolution: {integrity: sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==}
+ engines: {node: 20 || >=22}
+
braces@3.0.3:
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
engines: {node: '>=8'}
@@ -4460,6 +5338,10 @@ packages:
resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==}
engines: {node: '>=18'}
+ chalk@2.4.2:
+ resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
+ engines: {node: '>=4'}
+
chalk@3.0.0:
resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==}
engines: {node: '>=8'}
@@ -4524,6 +5406,10 @@ packages:
class-variance-authority@0.7.1:
resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==}
+ cli-cursor@2.1.0:
+ resolution: {integrity: sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==}
+ engines: {node: '>=4'}
+
cli-cursor@3.1.0:
resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==}
engines: {node: '>=8'}
@@ -4532,6 +5418,9 @@ packages:
resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==}
engines: {node: '>=6'}
+ client-only@0.0.1:
+ resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==}
+
cliui@6.0.0:
resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==}
@@ -4747,6 +5636,9 @@ packages:
resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==}
engines: {node: '>= 6'}
+ css.escape@1.5.1:
+ resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==}
+
cssesc@3.0.0:
resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
engines: {node: '>=4'}
@@ -4777,6 +5669,14 @@ packages:
supports-color:
optional: true
+ debug@3.2.7:
+ resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
debug@4.3.7:
resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==}
engines: {node: '>=6.0'}
@@ -4883,6 +5783,9 @@ packages:
resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==}
engines: {node: '>=8'}
+ detect-node-es@1.1.0:
+ resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==}
+
devlop@1.1.0:
resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
@@ -4900,6 +5803,12 @@ packages:
dlv@1.1.3:
resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}
+ dnssd-advertise@1.1.6:
+ resolution: {integrity: sha512-Ndrrf6BMPalkQPd/zubL+4YghH2J9NspapQ09uDXwYbvOPkP0oaqf5CkcwJ0b50kS2O3ul6yVu+jz+RY62Cejg==}
+
+ dom-accessibility-api@0.6.3:
+ resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==}
+
dom-serializer@2.0.0:
resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
@@ -5040,6 +5949,10 @@ packages:
escape-html@1.0.3:
resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
+ escape-string-regexp@1.0.5:
+ resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
+ engines: {node: '>=0.8.0'}
+
escape-string-regexp@2.0.0:
resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==}
engines: {node: '>=8'}
@@ -5144,39 +6057,172 @@ packages:
resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
- exponential-backoff@3.1.3:
- resolution: {integrity: sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==}
-
- extend-shallow@2.0.1:
- resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==}
- engines: {node: '>=0.10.0'}
-
- extend@3.0.2:
- resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
+ expo-asset@57.0.8:
+ resolution: {integrity: sha512-sGocG+Wd2WcZ1KjKyB2LfUZXzrBM0VHEATGcpJKF9T3HM56M/sMbH73vv+n85u5Zr0FkJjEbV1DWhGPimCR1QQ==}
+ peerDependencies:
+ expo: '*'
+ react: '*'
+ react-native: '*'
- extendable-error@0.1.7:
- resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==}
+ expo-constants@57.0.8:
+ resolution: {integrity: sha512-ts+B7E5076BtkblrKGy7+Sm/R2obHfTwqhmYQVYbWRtilv3+C/xNwHZhyNEnAvzM/JjKqx7UdaITUBYaeFreZw==}
+ peerDependencies:
+ expo: '*'
+ react-native: '*'
- fast-decode-uri-component@1.0.1:
- resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==}
+ expo-file-system@57.0.1:
+ resolution: {integrity: sha512-w7/ERvQFrGP2apTO9lDtZ+O6JQIhfakL7+Xqzh+rfMO9B4LB4qwrz+YvLgir8KFRVX64JHBnRuYBVLY1oQZcqw==}
+ peerDependencies:
+ expo: '*'
+ react-native: '*'
- fast-deep-equal@3.1.3:
- resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
+ expo-font@57.0.1:
+ resolution: {integrity: sha512-QyS9L1Kh9sKJg4gfU6rdbpxpmH+DyzBX8z6jVvXMUDoqLr1GqmkO/Wu379KCXjL///kWbhpNlbi7AgBuj4VdIQ==}
+ peerDependencies:
+ expo: '*'
+ react: '*'
+ react-native: '*'
- fast-glob@3.3.3:
- resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==}
- engines: {node: '>=8.6.0'}
+ expo-glass-effect@57.0.1:
+ resolution: {integrity: sha512-m/n8maxqNcHk6ZDhuqXBfD5Kt1Iz3M8xykVgdB0iSCIXvF70IqWXmQhX8Psswhrp8eZ+3r0mAD0Jh/2gFA3QaA==}
+ peerDependencies:
+ expo: '*'
+ react: '*'
+ react-native: '*'
- fast-json-stable-stringify@2.1.0:
- resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
+ expo-keep-awake@57.0.1:
+ resolution: {integrity: sha512-28lkFImeXTS+bhAjuCFV7w7tW5bXg27BJVrxv+nC/nyYa86qEa0oFeHwqol6ha5k4pdVDQgBF09GM4A1k76Ssg==}
+ peerDependencies:
+ expo: '*'
+ react: '*'
- fast-json-stringify@7.0.1:
- resolution: {integrity: sha512-eRSayARSbbwlBjpP4vnTTIRD5QPcIrmihPxDeN1DtKnHPg66UuJLx+8hlK1kaFdjvzyQ/dzALoi4vwAQ+T+iZA==}
+ expo-linking@57.0.4:
+ resolution: {integrity: sha512-e1alfHNJdywIfJkCuKMc6M3hBfAGPd2gKMeF/6V7qwFWzHCS2mTBqU+KaO4FLpltA5Nt6CYEx6zmUlGfUF+8lA==}
+ peerDependencies:
+ react: '*'
+ react-native: '*'
- fast-querystring@1.1.2:
- resolution: {integrity: sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==}
+ expo-modules-autolinking@57.0.9:
+ resolution: {integrity: sha512-lj2nsAKMMRLXSFnGgaaQrWJ2fdSpLPc/bca6Rkiw4g8zYPn7qX4MRUJgavvzm/hBrzvMnlhXVgJtidOGuwBh+w==}
+ hasBin: true
- fast-uri@3.1.4:
+ expo-modules-core@57.0.8:
+ resolution: {integrity: sha512-ZUU943Oso3B8jFggC+2+LOk8oa+im66IQlleYay6WP5iexbdF2fqpgC4BcYsN4xhCkDKh7KDINSt1FlcxyRRjQ==}
+ peerDependencies:
+ react: '*'
+ react-native: '*'
+ react-native-worklets: ^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0
+ peerDependenciesMeta:
+ react-native-worklets:
+ optional: true
+
+ expo-modules-jsi@57.0.4:
+ resolution: {integrity: sha512-vt7FyqUqqFXiRVnBqYD7y+GSPTgeua5Ocoy0+SYt+RSHkZEA2Fyop7If3g1TYDzQObYybPRo7TG2Rle1XLaWFw==}
+ peerDependencies:
+ react-native: '*'
+
+ expo-router@57.0.9:
+ resolution: {integrity: sha512-jPBwHmKmBfzCLUuKgFf+I/UhFkxvS3J2WF5MQKcuupC8XzQPBAqdzbxmcPIDY3VIVabHw7WnhwzsROWA74t1LA==}
+ peerDependencies:
+ '@testing-library/react-native': '>= 13.2.0'
+ expo: '*'
+ expo-constants: ^57.0.8
+ expo-linking: ^57.0.4
+ react: '*'
+ react-dom: '*'
+ react-native: '*'
+ react-native-gesture-handler: '*'
+ react-native-reanimated: '*'
+ react-native-safe-area-context: '>= 5.4.0'
+ react-native-screens: ^4.26.0
+ react-native-web: '*'
+ react-server-dom-webpack: ~19.0.4 || ~19.1.5 || ~19.2.4
+ peerDependenciesMeta:
+ '@testing-library/react-native':
+ optional: true
+ react-dom:
+ optional: true
+ react-native-gesture-handler:
+ optional: true
+ react-native-reanimated:
+ optional: true
+ react-native-web:
+ optional: true
+ react-server-dom-webpack:
+ optional: true
+
+ expo-server@57.0.1:
+ resolution: {integrity: sha512-sBfVDH6dmKVHZxqUxbfkzS00PZELMZt1IpnHKxcOTMZtR/t7CtRAFrbXcisG+EyzeqHSVDacZT+1tbYfZt5D8w==}
+ engines: {node: '>=20.16.0'}
+
+ expo-status-bar@57.0.1:
+ resolution: {integrity: sha512-Xwaq1gAoVRWx5dPG5VhT5RSbnI9OilhZnO5qoPBnUaBAa5VzRzfdS8q0/bsPt0jR2DKLtGuP0bQ6efMJ4RIMDg==}
+ peerDependencies:
+ expo: '*'
+ react: '*'
+ react-native: '*'
+
+ expo-symbols@57.0.1:
+ resolution: {integrity: sha512-8Zf+a83OywV0vf1NUtSKpNqKcULmO0GTI+zfFnGYl7SLDH9FjL5RcEZoy6CHvCgq2KDrQF21pl3r7Tb4ItPscw==}
+ peerDependencies:
+ expo: '*'
+ expo-font: '*'
+ react: '*'
+ react-native: '*'
+
+ expo@57.0.9:
+ resolution: {integrity: sha512-NDEvnU+vjRdMbtOyDC25OSok9/E97al97pyx+bMphQgNRGAED0D7oF8ha7oGYsLE+7jFzpPADVM9S60dGbpHIA==}
+ hasBin: true
+ peerDependencies:
+ '@expo/metro-runtime': '*'
+ react: '*'
+ react-dom: '*'
+ react-native: '*'
+ react-native-web: '*'
+ react-native-webview: '*'
+ peerDependenciesMeta:
+ '@expo/metro-runtime':
+ optional: true
+ react-dom:
+ optional: true
+ react-native-web:
+ optional: true
+ react-native-webview:
+ optional: true
+
+ exponential-backoff@3.1.3:
+ resolution: {integrity: sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==}
+
+ extend-shallow@2.0.1:
+ resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==}
+ engines: {node: '>=0.10.0'}
+
+ extend@3.0.2:
+ resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
+
+ extendable-error@0.1.7:
+ resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==}
+
+ fast-decode-uri-component@1.0.1:
+ resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==}
+
+ fast-deep-equal@3.1.3:
+ resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
+
+ fast-glob@3.3.3:
+ resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==}
+ engines: {node: '>=8.6.0'}
+
+ fast-json-stable-stringify@2.1.0:
+ resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
+
+ fast-json-stringify@7.0.1:
+ resolution: {integrity: sha512-eRSayARSbbwlBjpP4vnTTIRD5QPcIrmihPxDeN1DtKnHPg66UuJLx+8hlK1kaFdjvzyQ/dzALoi4vwAQ+T+iZA==}
+
+ fast-querystring@1.1.2:
+ resolution: {integrity: sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==}
+
+ fast-uri@3.1.4:
resolution: {integrity: sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==}
fast-uri@4.1.1:
@@ -5222,6 +6268,9 @@ packages:
picomatch:
optional: true
+ fetch-nodeshim@0.4.10:
+ resolution: {integrity: sha512-m6I8ALe4L4XpdETy7MJZWs6L1IVMbjs99bwbpIKphxX+0CTns4IKDWJY0LWfr4YsFjfg+z1TjzTMU8lKl8rG0w==}
+
figures@6.1.0:
resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==}
engines: {node: '>=18'}
@@ -5311,6 +6360,9 @@ packages:
debug:
optional: true
+ fontfaceobserver@2.3.0:
+ resolution: {integrity: sha512-6FPvD/IVyT4ZlNe7Wcn5Fb/4ChigpucKYSvD6a+0iMoLn2inpo711eyIcKjmDtE5XNcgAkSH9uN/nfAeZzHEfg==}
+
form-data@4.0.6:
resolution: {integrity: sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==}
engines: {node: '>= 6'}
@@ -5372,6 +6424,10 @@ packages:
resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
engines: {node: '>= 0.4'}
+ get-nonce@1.0.1:
+ resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==}
+ engines: {node: '>=6'}
+
get-package-type@0.1.0:
resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==}
engines: {node: '>=8.0.0'}
@@ -5396,6 +6452,10 @@ packages:
resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==}
engines: {node: '>=18'}
+ getenv@2.0.0:
+ resolution: {integrity: sha512-VilgtJj/ALgGY77fiLam5iD336eSWi96Q15JSAG1zi8NRBysm3LXKdGnHb4m5cuyxvOLQQKWpBZAT6ni4FI2iQ==}
+ engines: {node: '>=6'}
+
github-slugger@2.0.0:
resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==}
@@ -5416,6 +6476,10 @@ packages:
glob-to-regexp@0.4.1:
resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==}
+ glob@13.0.6:
+ resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==}
+ engines: {node: 18 || 20 || >=22}
+
glob@7.2.3:
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
@@ -5460,6 +6524,10 @@ packages:
resolution: {integrity: sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==}
engines: {node: '>=6.0'}
+ has-flag@3.0.0:
+ resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
+ engines: {node: '>=4'}
+
has-flag@4.0.0:
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
engines: {node: '>=8'}
@@ -5519,6 +6587,9 @@ packages:
hermes-compiler@250829098.0.14:
resolution: {integrity: sha512-5meXwsZxgiqFaJjNzwjzI9IyUkuGGBisu+z9BvQWmGVpjH6nz11hgqkyxe4dl8UAdyIV4lTbz91+Dlnjz0VxqA==}
+ hermes-compiler@250829098.0.16:
+ resolution: {integrity: sha512-xsgzk+mUyvt9t1nUbF8USBlYxajTUtPJhVZ86q85s/SEoMKCF+52YZcudb0ENSnV3T3lV9mgB3s6R7+pH90zgw==}
+
hermes-estree@0.20.1:
resolution: {integrity: sha512-SQpZK4BzR48kuOg0v4pb3EAGNclzIlqMj3Opu/mu7bbAoFw6oig6cEt/RAi0zTFW/iW6Iz9X9ggGuZTAZ/yZHg==}
@@ -5543,6 +6614,9 @@ packages:
hermes-parser@0.36.1:
resolution: {integrity: sha512-GApNk4zLHi2UWoWZZkx7LNCOSzLSc5lB55pZ/PhK7ycFeg7u5LcF88p/WbpIi1XUDtE0MpHE3uRR3u3KB7TjSQ==}
+ hoist-non-react-statics@3.3.2:
+ resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==}
+
homedir-polyfill@1.0.3:
resolution: {integrity: sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==}
engines: {node: '>=0.10.0'}
@@ -5550,6 +6624,10 @@ packages:
hookable@6.1.1:
resolution: {integrity: sha512-U9LYDy1CwhMCnprUfeAZWZGByVbhd54hwepegYTK7Pi5NvqEj63ifz5z+xukznehT7i6NIZRu89Ay1AZmRsLEQ==}
+ hosted-git-info@7.0.2:
+ resolution: {integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==}
+ engines: {node: ^16.14.0 || >=18.0.0}
+
html-encoding-sniffer@3.0.0:
resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==}
engines: {node: '>=12'}
@@ -5656,6 +6734,10 @@ packages:
resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
engines: {node: '>=0.8.19'}
+ indent-string@4.0.0:
+ resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==}
+ engines: {node: '>=8'}
+
inflight@1.0.6:
resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
@@ -5990,6 +7072,9 @@ packages:
node-notifier:
optional: true
+ jimp-compact@0.16.1:
+ resolution: {integrity: sha512-dZ6Ra7u1G8c4Letq/B5EzAxj4tLFHL+cGtdpR+PVm4yzPDj+lCk+AbivWt1eOM+ikzkowtyV7qSqX6qr3t71Ww==}
+
jiti@1.21.7:
resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==}
hasBin: true
@@ -6090,6 +7175,10 @@ packages:
resolution: {integrity: sha512-e7IpWJrnanNUroVK2taAgMxoEZvHLXdQiNjeExSu/DEIWm83jaKGBgb7tLmu2rMYpA027qFB3iLR/k3AVpFRnA==}
engines: {node: '>= 18'}
+ lan-network@0.2.1:
+ resolution: {integrity: sha512-ONPnazC96VKDntab9j9JKwIWhZ4ZUceB4A9Epu4Ssg0hYFmtHZSeQ+n15nIwTFmcBUKtExOer8WTJ4GF9MO64A==}
+ hasBin: true
+
launch-editor@2.14.1:
resolution: {integrity: sha512-QWBrQsMpH7gPr965dsKD/3cKWiNoTjpATQf++Xq63N6sKRGMwlVXz41O1IZTMfZQgBctD/K5Zt06+/I6pP6+HA==}
@@ -6319,6 +7408,10 @@ packages:
lodash@4.18.1:
resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==}
+ log-symbols@2.2.0:
+ resolution: {integrity: sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==}
+ engines: {node: '>=4'}
+
log-symbols@4.1.0:
resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==}
engines: {node: '>=10'}
@@ -6347,6 +7440,10 @@ packages:
lru-cache@10.4.3:
resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
+ lru-cache@11.5.2:
+ resolution: {integrity: sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==}
+ engines: {node: 20 || >=22}
+
lru-cache@5.1.1:
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
@@ -6656,10 +7753,22 @@ packages:
engines: {node: '>=4.0.0'}
hasBin: true
+ mimic-fn@1.2.0:
+ resolution: {integrity: sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==}
+ engines: {node: '>=4'}
+
mimic-fn@2.1.0:
resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
engines: {node: '>=6'}
+ min-indent@1.0.1:
+ resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==}
+ engines: {node: '>=4'}
+
+ minimatch@10.2.6:
+ resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==}
+ engines: {node: 18 || 20 || >=22}
+
minimatch@3.1.5:
resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==}
@@ -6693,6 +7802,9 @@ packages:
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+ multitars@1.0.0:
+ resolution: {integrity: sha512-H/J4fMLedtudftaYMOg7ajzLYgT3/rwbWVJbqr/iUgB8DQztn38ys5HOqI1CzSxx8QhXXwOOnnBvd4v3jG5+Mg==}
+
mz@2.7.0:
resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
@@ -6741,6 +7853,10 @@ packages:
encoding:
optional: true
+ node-forge@1.4.0:
+ resolution: {integrity: sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==}
+ engines: {node: '>= 6.13.0'}
+
node-int64@0.4.0:
resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==}
@@ -6764,6 +7880,10 @@ packages:
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
engines: {node: '>=0.10.0'}
+ npm-package-arg@11.0.3:
+ resolution: {integrity: sha512-sHGJy8sOC1YraBywpzQlIKBE4pBbGbiF95U6Auspzyem956E0+FtDtsx1ZxlOJkQCZ1AFXAY/yuvtFYrOxF+Bw==}
+ engines: {node: ^16.14.0 || >=18.0.0}
+
npm-run-path@4.0.1:
resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==}
engines: {node: '>=8'}
@@ -6820,6 +7940,10 @@ packages:
once@1.4.0:
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
+ onetime@2.0.1:
+ resolution: {integrity: sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==}
+ engines: {node: '>=4'}
+
onetime@5.1.2:
resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
engines: {node: '>=6'}
@@ -6849,6 +7973,10 @@ packages:
resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==}
hasBin: true
+ ora@3.4.0:
+ resolution: {integrity: sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg==}
+ engines: {node: '>=6'}
+
ora@5.4.1:
resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==}
engines: {node: '>=10'}
@@ -6918,6 +8046,10 @@ packages:
resolution: {integrity: sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==}
engines: {node: '>=0.10.0'}
+ parse-png@2.1.0:
+ resolution: {integrity: sha512-Nt/a5SfCLiTnQAjx3fHlqp8hRgTL3z7kTQZzvIMS9uCAepnCyjpdEc6M/sz69WqMBdaDBw9sF1F1UaHROYzGkQ==}
+ engines: {node: '>=10'}
+
parse5@7.3.0:
resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==}
@@ -6963,6 +8095,10 @@ packages:
resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
engines: {node: '>=16 || 14 >=14.18'}
+ path-scurry@2.0.2:
+ resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==}
+ engines: {node: 18 || 20 || >=22}
+
path-to-regexp@8.4.2:
resolution: {integrity: sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==}
@@ -7026,6 +8162,10 @@ packages:
resolution: {integrity: sha512-ZIfcLJC+7E7FBFnDxm9MPmt7D+DidyQ26lewieO75AdhA2ayMtsJSES0iWzqJQbcVRSrTufQoy0DR94xHue0oA==}
engines: {node: '>=10.4.0'}
+ pngjs@3.4.0:
+ resolution: {integrity: sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==}
+ engines: {node: '>=4.0.0'}
+
portfinder@1.0.38:
resolution: {integrity: sha512-rEwq/ZHlJIKw++XtLAO8PPuOQA/zaPJOZJ37BVuN97nLpMJeuDVLVGRwbFoBgLudgdTMP2hdRJP++H+8QOA3vg==}
engines: {node: '>= 10.12'}
@@ -7115,12 +8255,20 @@ packages:
resolution: {integrity: sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==}
engines: {node: '>=18'}
+ proc-log@4.2.0:
+ resolution: {integrity: sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==}
+ engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+
process-warning@4.0.1:
resolution: {integrity: sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q==}
process-warning@5.0.0:
resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==}
+ progress@2.0.3:
+ resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==}
+ engines: {node: '>=0.4.0'}
+
promise@8.3.0:
resolution: {integrity: sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==}
@@ -7177,12 +8325,18 @@ packages:
peerDependencies:
react: ^19.2.8
+ react-fast-compare@3.2.2:
+ resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==}
+
react-freeze@1.0.4:
resolution: {integrity: sha512-r4F0Sec0BLxWicc7HEyo2x3/2icUTrRmDjaaRyzzn+7aDyFZliszMDOgLVwSnQnYENOlL1o569Ze2HZefk8clA==}
engines: {node: '>=10'}
peerDependencies:
react: '>=17.0.0'
+ react-is@16.13.1:
+ resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
+
react-is@17.0.2:
resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
@@ -7227,12 +8381,33 @@ packages:
react-native-svg:
optional: true
+ react-native-drawer-layout@4.2.9:
+ resolution: {integrity: sha512-ETOxvlhhb4LmuuG3RN7A3qwt9jr9AZ2it+1G2kNE4g2fTyxxay7QQkPm1HfKi/JzmMSWC5+YTepGSgZNpJIDGg==}
+ peerDependencies:
+ react: '>= 18.2.0'
+ react-native: '*'
+ react-native-gesture-handler: '>= 2.0.0'
+ react-native-reanimated: '>= 2.0.0'
+
+ react-native-gesture-handler@2.32.0:
+ resolution: {integrity: sha512-uYIMOKlKENORq2SABE+jIjbPU+h5I/sQKcq2v16zRq848nwEp1fWRVwML4QWqijc8UcXJC25o54S8GQd4Mf2OA==}
+ peerDependencies:
+ react: '*'
+ react-native: '*'
+
react-native-is-edge-to-edge@1.3.1:
resolution: {integrity: sha512-NIXU/iT5+ORyCc7p0z2nnlkouYKX425vuU1OEm6bMMtWWR9yvb+Xg5AZmImTKoF9abxCPqrKC3rOZsKzUYgYZA==}
peerDependencies:
react: '*'
react-native: '*'
+ react-native-reanimated@4.5.1:
+ resolution: {integrity: sha512-RnMvtDuR+68ig864gAvZCOdZehqhC5rFmMo0kn+ARfgVSTvFeF6IFLBVgMPUu0KwihaapEyW24WRi6nEyy1kSA==}
+ peerDependencies:
+ react: '*'
+ react-native: 0.83 - 0.86
+ react-native-worklets: 0.10.x
+
react-native-reanimated@4.5.3:
resolution: {integrity: sha512-+owIckpD4sA13XKHaLrr8V1RP4paqoxKvz/jy2wl/ULjMsfcRlfIJdKJxR1kcQr4McBZ6hAoPo01NyLnmy2ycQ==}
peerDependencies:
@@ -7240,6 +8415,12 @@ packages:
react-native: 0.83 - 0.86
react-native-worklets: 0.10.x - 0.11.x
+ react-native-safe-area-context@5.7.0:
+ resolution: {integrity: sha512-/9/MtQz8ODphjsLdZ+GZAIcC/RtoqW9EeShf7Uvnfgm/pzYrJ75y3PV/J1wuAV1T5Dye5ygq4EAW20RoBq0ABQ==}
+ peerDependencies:
+ react: '*'
+ react-native: '*'
+
react-native-safe-area-context@5.8.0:
resolution: {integrity: sha512-t+ZsAVzY/wWzzx34vqGbo3/as9EEESJdbyZNL7Yg5EYX+toYMtMqFoDDCvqZUi35eeGVsXc6pAaEk4edMwbuCQ==}
peerDependencies:
@@ -7279,6 +8460,14 @@ packages:
react-native-windows:
optional: true
+ react-native-worklets@0.10.1:
+ resolution: {integrity: sha512-62mRM19bDpfpdI8HLkEErcdOsrAPDtE9lA/sw+5lLRpzBHNhxaoj9QyY2KjXqUmirelxkX4zuPGTC3VdA0feJA==}
+ peerDependencies:
+ '@babel/core': '*'
+ '@react-native/metro-config': '*'
+ react: '*'
+ react-native: 0.83 - 0.86
+
react-native-worklets@0.11.3:
resolution: {integrity: sha512-paLRlVFTrrGhkGTi37LHwtaJbrTftA54/zAx1h/ONSBw7XejZn6XUUl1KpM6EOMwsbw7TEGuxJXTAp/wkvuMqQ==}
peerDependencies:
@@ -7301,6 +8490,20 @@ packages:
'@types/react':
optional: true
+ react-native@0.86.2:
+ resolution: {integrity: sha512-zbJXGZpwfZGA79Z9ob6Atvfx4nAQL8yJBa35s58E4Oo+khPykfQP2sTeumkKbjwajFYfVayg8pj7Il9nIfTk7A==}
+ engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0}
+ hasBin: true
+ peerDependencies:
+ '@react-native/jest-preset': 0.86.2
+ '@types/react': ^19.1.1
+ react: ^19.2.3
+ peerDependenciesMeta:
+ '@react-native/jest-preset':
+ optional: true
+ '@types/react':
+ optional: true
+
react-reconciler@0.33.0:
resolution: {integrity: sha512-KetWRytFv1epdpJc3J4G75I4WrplZE5jOL7Yq0p34+OVOKF4Se7WrdIdVC45XsSSmUTlht2FM/fM1FZb1mfQeA==}
engines: {node: '>=0.10.0'}
@@ -7315,6 +8518,26 @@ packages:
resolution: {integrity: sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==}
engines: {node: '>=0.10.0'}
+ react-remove-scroll-bar@2.3.8:
+ resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ react-remove-scroll@2.7.2:
+ resolution: {integrity: sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
react-router-dom@7.18.1:
resolution: {integrity: sha512-KaZh+X/6UtEp28x51AUYZDMg9NGoz2ja3dNHa+ta/tk40vCzKhQ/RypCWBMLbmDr6//E24Vv5uPsrqXFozdkAg==}
engines: {node: '>=20.0.0'}
@@ -7332,6 +8555,20 @@ packages:
react-dom:
optional: true
+ react-style-singleton@2.2.3:
+ resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ react@19.2.3:
+ resolution: {integrity: sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==}
+ engines: {node: '>=0.10.0'}
+
react@19.2.8:
resolution: {integrity: sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==}
engines: {node: '>=0.10.0'}
@@ -7372,6 +8609,10 @@ packages:
recma-stringify@1.0.0:
resolution: {integrity: sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==}
+ redent@3.0.0:
+ resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==}
+ engines: {node: '>=8'}
+
regenerate-unicode-properties@10.2.2:
resolution: {integrity: sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==}
engines: {node: '>=4'}
@@ -7459,6 +8700,9 @@ packages:
resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==}
engines: {node: '>=8'}
+ resolve-workspace-root@2.0.1:
+ resolution: {integrity: sha512-nR23LHAvaI6aHtMg6RWoaHpdR4D881Nydkzi2CixINyg9T00KgaJdJI6Vwty+Ps8WLxZHuxsS0BseWjxSA4C+w==}
+
resolve.exports@2.0.3:
resolution: {integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==}
engines: {node: '>=10'}
@@ -7472,6 +8716,10 @@ packages:
resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==}
hasBin: true
+ restore-cursor@2.0.0:
+ resolution: {integrity: sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==}
+ engines: {node: '>=4'}
+
restore-cursor@3.1.0:
resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==}
engines: {node: '>=8'}
@@ -7605,6 +8853,9 @@ packages:
resolution: {integrity: sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==}
engines: {node: '>= 0.8.0'}
+ server-only@0.0.1:
+ resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==}
+
set-blocking@2.0.0:
resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
@@ -7691,6 +8942,10 @@ packages:
resolution: {integrity: sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==}
engines: {node: '>=6'}
+ slugify@1.6.9:
+ resolution: {integrity: sha512-vZ7rfeehZui7wQs438JXBckYLkIIdfHOXsaVEUMyS5fHo1483l1bMdo0EDSWYclY0yZKFOipDy4KHuKs6ssvdg==}
+ engines: {node: '>=8.0.0'}
+
snake-case@3.0.4:
resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==}
@@ -7764,6 +9019,9 @@ packages:
resolution: {integrity: sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg==}
engines: {node: '>=6'}
+ standard-navigation@0.0.5:
+ resolution: {integrity: sha512-YAmzwAiiQVocZxO/VGPFiQHcu5pKiz09QIGC0MK6aRMoa3E0QkoTQgcqJr7ZZ3OMiNhu4DkaGElFI5htjOIDbw==}
+
standard-navigation@0.0.8:
resolution: {integrity: sha512-TyVbo7INUDWtsUWDFn8RR7kwR87U0S4xHfLfbbnyeC581TmmyqQ+eM+nPw8rQTSD8QitRVcYfPaSHr/QJiUy1g==}
peerDependencies:
@@ -7792,6 +9050,9 @@ packages:
resolution: {integrity: sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==}
engines: {node: '>=4'}
+ strict-url-sanitise@0.0.1:
+ resolution: {integrity: sha512-nuFtF539K8jZg3FjaWH/L8eocCR6gegz5RDOsaWxfdbF5Jqr2VXWxZayjTwUzsWJDC91k2EbnJXp6FuWW+Z4hg==}
+
string-length@4.0.2:
resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==}
engines: {node: '>=10'}
@@ -7834,6 +9095,10 @@ packages:
resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==}
engines: {node: '>=18'}
+ strip-indent@3.0.0:
+ resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==}
+ engines: {node: '>=8'}
+
strip-json-comments@3.1.1:
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
engines: {node: '>=8'}
@@ -7841,6 +9106,9 @@ packages:
strnum@2.4.1:
resolution: {integrity: sha512-M9eUSMT2dCB2cTNPG7UYj6KuK7RJR2SN2+yCV/fTW3xzTCS6EaGZ5pSMgDIjB7r8zSfTGk+dvvn9rTjpVS9Mwg==}
+ structured-headers@0.4.1:
+ resolution: {integrity: sha512-0MP/Cxx5SzeeZ10p/bZI0S6MpgD+yxAhi1BOQ34jgnMXsCq3j1t6tQnZu+KdlL7dvJTLT3g9xN8tl10TqgFMcg==}
+
style-to-js@1.1.21:
resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==}
@@ -7852,6 +9120,10 @@ packages:
engines: {node: '>=16 || 14 >=14.17'}
hasBin: true
+ supports-color@5.5.0:
+ resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
+ engines: {node: '>=4'}
+
supports-color@7.2.0:
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
engines: {node: '>=8'}
@@ -7860,6 +9132,10 @@ packages:
resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==}
engines: {node: '>=10'}
+ supports-hyperlinks@2.3.0:
+ resolution: {integrity: sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==}
+ engines: {node: '>=8'}
+
supports-preserve-symlinks-flag@1.0.0:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'}
@@ -7896,6 +9172,10 @@ packages:
resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==}
engines: {node: '>=8'}
+ terminal-link@2.1.1:
+ resolution: {integrity: sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==}
+ engines: {node: '>=8'}
+
terser-webpack-plugin@5.5.0:
resolution: {integrity: sha512-UYhptBwhWvfIjKd/UuFo6D8uq9xpGLDK+z8EDsj/zWhrTaH34cKEbrkMKfV5YWqGBvAYA3tlzZbs2R+qYrbQJA==}
engines: {node: '>= 10.13.0'}
@@ -7994,6 +9274,9 @@ packages:
resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
engines: {node: '>=0.6'}
+ toqr@0.1.1:
+ resolution: {integrity: sha512-FWAPzCIHZHnrE/5/w9MPk0kK25hSQSH2IKhYh9PyjS3SG/+IEMvlwIHbhz+oF7xl54I+ueZlVnMjyzdSwLmAwA==}
+
tr46@0.0.3:
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
@@ -8060,6 +9343,11 @@ packages:
engines: {node: '>=14.17'}
hasBin: true
+ typescript@6.0.3:
+ resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==}
+ engines: {node: '>=14.17'}
+ hasBin: true
+
undici-types@6.21.0:
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
@@ -8149,11 +9437,31 @@ packages:
url-join@4.0.1:
resolution: {integrity: sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==}
+ use-callback-ref@1.3.3:
+ resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
use-latest-callback@0.2.6:
resolution: {integrity: sha512-FvRG9i1HSo0wagmX63Vrm8SnlUU3LMM3WyZkQ76RnslpBrX694AdG4A0zQBx2B3ZifFA0yv/BaEHGBnEax5rZg==}
peerDependencies:
react: '>=16.8'
+ use-sidecar@1.1.3:
+ resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
use-sync-external-store@1.6.0:
resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==}
peerDependencies:
@@ -8174,10 +9482,20 @@ packages:
resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==}
engines: {node: '>=10.12.0'}
+ validate-npm-package-name@5.0.1:
+ resolution: {integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==}
+ engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+
vary@1.1.2:
resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
engines: {node: '>= 0.8'}
+ vaul@1.1.2:
+ resolution: {integrity: sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA==}
+ peerDependencies:
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc
+
vfile-location@5.0.3:
resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==}
@@ -8321,6 +9639,9 @@ packages:
whatwg-fetch@3.6.20:
resolution: {integrity: sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==}
+ whatwg-url-minimum@0.1.2:
+ resolution: {integrity: sha512-XPEm0XFQWNVG292lII1PrRRJl3sItrs7CettZ4ncYxuDVpLyy+NwlGyut2hXI0JswcJUxeCH+CyOJK0ZzAXD6A==}
+
whatwg-url@5.0.0:
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
@@ -8414,6 +9735,14 @@ packages:
resolution: {integrity: sha512-ghig2TBE/H11aOVgmahA3MhimvkBr6JIYknH/Dhdk10nXwdbIqBJsbfMxpvFPG8bAw77gN29aQWvKpmVoPlvPQ==}
engines: {node: '>=16.0.0'}
+ xml2js@0.6.0:
+ resolution: {integrity: sha512-eLTh0kA8uHceqesPqSE+VvO1CDDJWMwlQfB6LuN6T8w6MaDJ8Txm8P7s5cHD0miF0V+GGTZrDQfxPZQVsur33w==}
+ engines: {node: '>=4.0.0'}
+
+ xmlbuilder@11.0.1:
+ resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==}
+ engines: {node: '>=4.0'}
+
xmlbuilder@15.1.1:
resolution: {integrity: sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==}
engines: {node: '>=8.0'}
@@ -8473,6 +9802,8 @@ packages:
snapshots:
+ '@adobe/css-tools@4.5.0': {}
+
'@alloc/quick-lru@5.2.0': {}
'@ast-grep/napi-darwin-arm64@0.37.0':
@@ -8735,6 +10066,15 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@babel/plugin-proposal-decorators@7.29.7(@babel/core@7.29.7)':
+ dependencies:
+ '@babel/core': 7.29.7
+ '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7)
+ '@babel/helper-plugin-utils': 7.29.7
+ '@babel/plugin-syntax-decorators': 7.29.7(@babel/core@7.29.7)
+ transitivePeerDependencies:
+ - supports-color
+
'@babel/plugin-proposal-export-default-from@7.29.7(@babel/core@7.29.7)':
dependencies:
'@babel/core': 7.29.7
@@ -8764,6 +10104,11 @@ snapshots:
'@babel/core': 7.29.7
'@babel/helper-plugin-utils': 7.29.7
+ '@babel/plugin-syntax-decorators@7.29.7(@babel/core@7.29.7)':
+ dependencies:
+ '@babel/core': 7.29.7
+ '@babel/helper-plugin-utils': 7.29.7
+
'@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.29.7)':
dependencies:
'@babel/core': 7.29.7
@@ -8844,6 +10189,11 @@ snapshots:
'@babel/core': 7.29.7
'@babel/helper-plugin-utils': 7.29.7
+ '@babel/plugin-syntax-typescript@7.25.4(@babel/core@7.29.7)':
+ dependencies:
+ '@babel/core': 7.29.7
+ '@babel/helper-plugin-utils': 7.29.7
+
'@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7)':
dependencies:
'@babel/core': 7.29.7
@@ -9626,6 +10976,10 @@ snapshots:
'@discoveryjs/json-ext@0.5.7': {}
+ '@egjs/hammerjs@2.0.17':
+ dependencies:
+ '@types/hammerjs': 2.0.46
+
'@emnapi/core@1.11.3':
dependencies:
'@emnapi/wasi-threads': 1.2.3
@@ -9795,25 +11149,678 @@ snapshots:
'@esbuild/win32-x64@0.28.1':
optional: true
- '@fastify/ajv-compiler@4.0.5':
- dependencies:
- ajv: 8.20.0
- ajv-formats: 3.0.1
- fast-uri: 3.1.4
-
- '@fastify/error@4.2.0': {}
-
- '@fastify/fast-json-stringify-compiler@5.1.0':
- dependencies:
- fast-json-stringify: 7.0.1
-
- '@fastify/forwarded@3.0.1': {}
-
- '@fastify/merge-json-schemas@0.2.1':
- dependencies:
- dequal: 2.0.3
-
- '@fastify/middie@9.3.3':
+ '@expo-google-fonts/material-symbols@0.4.42': {}
+
+ '@expo/cli@57.0.11(@expo/metro-runtime@57.0.8)(expo-constants@57.0.8)(expo-font@57.0.1)(expo-router@57.0.9)(expo@57.0.9)(react-dom@19.2.8(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(typescript@6.0.3)':
+ dependencies:
+ '@expo/code-signing-certificates': 0.0.6
+ '@expo/config': 57.0.6(typescript@6.0.3)
+ '@expo/config-plugins': 57.0.6(typescript@6.0.3)
+ '@expo/devcert': 1.2.1
+ '@expo/env': 2.4.2
+ '@expo/image-utils': 0.11.4(typescript@6.0.3)
+ '@expo/inline-modules': 0.1.4(typescript@6.0.3)
+ '@expo/json-file': 11.0.1
+ '@expo/log-box': 57.0.2(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)
+ '@expo/metro': 56.0.0
+ '@expo/metro-config': 57.0.7(expo@57.0.9)(typescript@6.0.3)
+ '@expo/metro-file-map': 57.0.1
+ '@expo/osascript': 2.7.1
+ '@expo/package-manager': 1.13.1
+ '@expo/plist': 0.8.1
+ '@expo/prebuild-config': 57.0.10(typescript@6.0.3)
+ '@expo/require-utils': 57.0.4(typescript@6.0.3)
+ '@expo/router-server': 57.0.4(@expo/metro-runtime@57.0.8)(expo-constants@57.0.8)(expo-font@57.0.1)(expo-router@57.0.9)(expo-server@57.0.1)(expo@57.0.9)(react-dom@19.2.8(react@19.2.3))(react@19.2.3)
+ '@expo/schema-utils': 57.0.2
+ '@expo/spawn-async': 1.8.0
+ '@expo/ws-tunnel': 2.0.0(ws@8.21.1)
+ '@expo/xcpretty': 4.4.4
+ '@react-native/dev-middleware': 0.86.2
+ accepts: 1.3.8
+ agent-cli-detector: 0.1.4
+ arg: 5.0.2
+ bplist-creator: 0.1.0
+ bplist-parser: 0.3.1
+ chalk: 4.1.2
+ ci-info: 3.9.0
+ compression: 1.8.1
+ connect: 3.7.0
+ debug: 4.4.3
+ dnssd-advertise: 1.1.6
+ expo: 57.0.9(@babel/core@7.29.7)(@expo/metro-runtime@57.0.8)(expo-router@57.0.9)(react-dom@19.2.8(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(typescript@6.0.3)
+ expo-server: 57.0.1
+ fetch-nodeshim: 0.4.10
+ getenv: 2.0.0
+ glob: 13.0.6
+ lan-network: 0.2.1
+ multitars: 1.0.0
+ node-forge: 1.4.0
+ npm-package-arg: 11.0.3
+ ora: 3.4.0
+ picomatch: 4.0.5
+ pretty-format: 29.7.0
+ progress: 2.0.3
+ prompts: 2.4.2
+ resolve-from: 5.0.0
+ semver: 7.8.5
+ send: 0.19.2
+ slugify: 1.6.9
+ stacktrace-parser: 0.1.11
+ structured-headers: 0.4.1
+ terminal-link: 2.1.1
+ toqr: 0.1.1
+ wrap-ansi: 7.0.0
+ ws: 8.21.1
+ zod: 3.25.76
+ optionalDependencies:
+ expo-router: 57.0.9(a32405737b9d1025ea0eea533a2e6749)
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3)
+ transitivePeerDependencies:
+ - '@expo/metro-runtime'
+ - bufferutil
+ - expo-constants
+ - expo-font
+ - react
+ - react-dom
+ - react-server-dom-webpack
+ - supports-color
+ - typescript
+ - utf-8-validate
+
+ '@expo/cli@57.0.11(@expo/metro-runtime@57.0.8)(expo-constants@57.0.8)(expo-font@57.0.1)(expo-router@57.0.9)(expo@57.0.9)(react-dom@19.2.8(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)(typescript@5.9.3)':
+ dependencies:
+ '@expo/code-signing-certificates': 0.0.6
+ '@expo/config': 57.0.6(typescript@5.9.3)
+ '@expo/config-plugins': 57.0.6(typescript@5.9.3)
+ '@expo/devcert': 1.2.1
+ '@expo/env': 2.4.2
+ '@expo/image-utils': 0.11.4(typescript@5.9.3)
+ '@expo/inline-modules': 0.1.4(typescript@5.9.3)
+ '@expo/json-file': 11.0.1
+ '@expo/log-box': 57.0.2(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
+ '@expo/metro': 56.0.0
+ '@expo/metro-config': 57.0.7(expo@57.0.9)(typescript@5.9.3)
+ '@expo/metro-file-map': 57.0.1
+ '@expo/osascript': 2.7.1
+ '@expo/package-manager': 1.13.1
+ '@expo/plist': 0.8.1
+ '@expo/prebuild-config': 57.0.10(typescript@5.9.3)
+ '@expo/require-utils': 57.0.4(typescript@5.9.3)
+ '@expo/router-server': 57.0.4(@expo/metro-runtime@57.0.8)(expo-constants@57.0.8)(expo-font@57.0.1)(expo-router@57.0.9)(expo-server@57.0.1)(expo@57.0.9)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
+ '@expo/schema-utils': 57.0.2
+ '@expo/spawn-async': 1.8.0
+ '@expo/ws-tunnel': 2.0.0(ws@8.21.1)
+ '@expo/xcpretty': 4.4.4
+ '@react-native/dev-middleware': 0.86.2
+ accepts: 1.3.8
+ agent-cli-detector: 0.1.4
+ arg: 5.0.2
+ bplist-creator: 0.1.0
+ bplist-parser: 0.3.1
+ chalk: 4.1.2
+ ci-info: 3.9.0
+ compression: 1.8.1
+ connect: 3.7.0
+ debug: 4.4.3
+ dnssd-advertise: 1.1.6
+ expo: 57.0.9(@babel/core@7.29.7)(@expo/metro-runtime@57.0.8)(expo-router@57.0.9)(react-dom@19.2.8(react@19.2.8))(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)(typescript@5.9.3)
+ expo-server: 57.0.1
+ fetch-nodeshim: 0.4.10
+ getenv: 2.0.0
+ glob: 13.0.6
+ lan-network: 0.2.1
+ multitars: 1.0.0
+ node-forge: 1.4.0
+ npm-package-arg: 11.0.3
+ ora: 3.4.0
+ picomatch: 4.0.5
+ pretty-format: 29.7.0
+ progress: 2.0.3
+ prompts: 2.4.2
+ resolve-from: 5.0.0
+ semver: 7.8.5
+ send: 0.19.2
+ slugify: 1.6.9
+ stacktrace-parser: 0.1.11
+ structured-headers: 0.4.1
+ terminal-link: 2.1.1
+ toqr: 0.1.1
+ wrap-ansi: 7.0.0
+ ws: 8.21.1
+ zod: 3.25.76
+ optionalDependencies:
+ expo-router: 57.0.9(9c3e0008a3ce3af43de1b1cd4744d2bf)
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8)
+ transitivePeerDependencies:
+ - '@expo/metro-runtime'
+ - bufferutil
+ - expo-constants
+ - expo-font
+ - react
+ - react-dom
+ - react-server-dom-webpack
+ - supports-color
+ - typescript
+ - utf-8-validate
+
+ '@expo/code-signing-certificates@0.0.6':
+ dependencies:
+ node-forge: 1.4.0
+
+ '@expo/config-plugins@57.0.6(typescript@5.9.3)':
+ dependencies:
+ '@expo/config-types': 57.0.2
+ '@expo/json-file': 11.0.1
+ '@expo/plist': 0.8.1
+ '@expo/require-utils': 57.0.4(typescript@5.9.3)
+ '@expo/sdk-runtime-versions': 1.0.0
+ chalk: 4.1.2
+ debug: 4.4.3
+ getenv: 2.0.0
+ glob: 13.0.6
+ semver: 7.8.5
+ slugify: 1.6.9
+ xcode: 3.0.1
+ xml2js: 0.6.0
+ transitivePeerDependencies:
+ - supports-color
+ - typescript
+
+ '@expo/config-plugins@57.0.6(typescript@6.0.3)':
+ dependencies:
+ '@expo/config-types': 57.0.2
+ '@expo/json-file': 11.0.1
+ '@expo/plist': 0.8.1
+ '@expo/require-utils': 57.0.4(typescript@6.0.3)
+ '@expo/sdk-runtime-versions': 1.0.0
+ chalk: 4.1.2
+ debug: 4.4.3
+ getenv: 2.0.0
+ glob: 13.0.6
+ semver: 7.8.5
+ slugify: 1.6.9
+ xcode: 3.0.1
+ xml2js: 0.6.0
+ transitivePeerDependencies:
+ - supports-color
+ - typescript
+
+ '@expo/config-types@57.0.2': {}
+
+ '@expo/config@57.0.6(typescript@5.9.3)':
+ dependencies:
+ '@expo/config-plugins': 57.0.6(typescript@5.9.3)
+ '@expo/config-types': 57.0.2
+ '@expo/json-file': 11.0.1
+ '@expo/require-utils': 57.0.4(typescript@5.9.3)
+ deepmerge: 4.3.1
+ getenv: 2.0.0
+ glob: 13.0.6
+ resolve-workspace-root: 2.0.1
+ semver: 7.8.5
+ slugify: 1.6.9
+ transitivePeerDependencies:
+ - supports-color
+ - typescript
+
+ '@expo/config@57.0.6(typescript@6.0.3)':
+ dependencies:
+ '@expo/config-plugins': 57.0.6(typescript@6.0.3)
+ '@expo/config-types': 57.0.2
+ '@expo/json-file': 11.0.1
+ '@expo/require-utils': 57.0.4(typescript@6.0.3)
+ deepmerge: 4.3.1
+ getenv: 2.0.0
+ glob: 13.0.6
+ resolve-workspace-root: 2.0.1
+ semver: 7.8.5
+ slugify: 1.6.9
+ transitivePeerDependencies:
+ - supports-color
+ - typescript
+
+ '@expo/devcert@1.2.1':
+ dependencies:
+ '@expo/sudo-prompt': 9.3.2
+ debug: 3.2.7
+ transitivePeerDependencies:
+ - supports-color
+
+ '@expo/devtools@57.0.1(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)':
+ dependencies:
+ chalk: 4.1.2
+ optionalDependencies:
+ react: 19.2.3
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3)
+
+ '@expo/devtools@57.0.1(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)':
+ dependencies:
+ chalk: 4.1.2
+ optionalDependencies:
+ react: 19.2.8
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8)
+
+ '@expo/dom-webview@57.0.1(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)':
+ dependencies:
+ expo: 57.0.9(@babel/core@7.29.7)(@expo/metro-runtime@57.0.8)(expo-router@57.0.9)(react-dom@19.2.8(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(typescript@6.0.3)
+ react: 19.2.3
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3)
+
+ '@expo/dom-webview@57.0.1(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)':
+ dependencies:
+ expo: 57.0.9(@babel/core@7.29.7)(@expo/metro-runtime@57.0.8)(expo-router@57.0.9)(react-dom@19.2.8(react@19.2.8))(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)(typescript@5.9.3)
+ react: 19.2.8
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8)
+
+ '@expo/env@2.3.1':
+ dependencies:
+ chalk: 4.1.2
+ debug: 4.4.3
+ getenv: 2.0.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@expo/env@2.4.2':
+ dependencies:
+ chalk: 4.1.2
+ debug: 4.4.3
+ getenv: 2.0.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@expo/expo-modules-macros-plugin@0.6.1': {}
+
+ '@expo/fingerprint@0.20.6':
+ dependencies:
+ '@expo/env': 2.4.2
+ '@expo/spawn-async': 1.8.0
+ arg: 5.0.2
+ chalk: 4.1.2
+ debug: 4.4.3
+ getenv: 2.0.0
+ glob: 13.0.6
+ ignore: 5.3.2
+ minimatch: 10.2.6
+ resolve-from: 5.0.0
+ semver: 7.8.5
+ transitivePeerDependencies:
+ - supports-color
+
+ '@expo/image-utils@0.11.4(typescript@5.9.3)':
+ dependencies:
+ '@expo/require-utils': 57.0.4(typescript@5.9.3)
+ '@expo/spawn-async': 1.8.0
+ chalk: 4.1.2
+ getenv: 2.0.0
+ jimp-compact: 0.16.1
+ parse-png: 2.1.0
+ semver: 7.8.5
+ transitivePeerDependencies:
+ - supports-color
+ - typescript
+
+ '@expo/image-utils@0.11.4(typescript@6.0.3)':
+ dependencies:
+ '@expo/require-utils': 57.0.4(typescript@6.0.3)
+ '@expo/spawn-async': 1.8.0
+ chalk: 4.1.2
+ getenv: 2.0.0
+ jimp-compact: 0.16.1
+ parse-png: 2.1.0
+ semver: 7.8.5
+ transitivePeerDependencies:
+ - supports-color
+ - typescript
+
+ '@expo/inline-modules@0.1.4(typescript@5.9.3)':
+ dependencies:
+ '@expo/config-plugins': 57.0.6(typescript@5.9.3)
+ transitivePeerDependencies:
+ - supports-color
+ - typescript
+
+ '@expo/inline-modules@0.1.4(typescript@6.0.3)':
+ dependencies:
+ '@expo/config-plugins': 57.0.6(typescript@6.0.3)
+ transitivePeerDependencies:
+ - supports-color
+ - typescript
+
+ '@expo/json-file@11.0.1':
+ dependencies:
+ '@babel/code-frame': 7.29.7
+ json5: 2.2.3
+
+ '@expo/local-build-cache-provider@57.0.5(typescript@5.9.3)':
+ dependencies:
+ '@expo/config': 57.0.6(typescript@5.9.3)
+ chalk: 4.1.2
+ transitivePeerDependencies:
+ - supports-color
+ - typescript
+
+ '@expo/local-build-cache-provider@57.0.5(typescript@6.0.3)':
+ dependencies:
+ '@expo/config': 57.0.6(typescript@6.0.3)
+ chalk: 4.1.2
+ transitivePeerDependencies:
+ - supports-color
+ - typescript
+
+ '@expo/log-box@57.0.2(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)':
+ dependencies:
+ '@expo/dom-webview': 57.0.1(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)
+ anser: 1.4.10
+ expo: 57.0.9(@babel/core@7.29.7)(@expo/metro-runtime@57.0.8)(expo-router@57.0.9)(react-dom@19.2.8(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(typescript@6.0.3)
+ react: 19.2.3
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3)
+ stacktrace-parser: 0.1.11
+
+ '@expo/log-box@57.0.2(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)':
+ dependencies:
+ '@expo/dom-webview': 57.0.1(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
+ anser: 1.4.10
+ expo: 57.0.9(@babel/core@7.29.7)(@expo/metro-runtime@57.0.8)(expo-router@57.0.9)(react-dom@19.2.8(react@19.2.8))(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)(typescript@5.9.3)
+ react: 19.2.8
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8)
+ stacktrace-parser: 0.1.11
+
+ '@expo/metro-config@57.0.7(expo@57.0.9)(typescript@5.9.3)':
+ dependencies:
+ '@babel/code-frame': 7.29.7
+ '@babel/core': 7.29.7
+ '@babel/generator': 7.29.7
+ '@expo/config': 57.0.6(typescript@5.9.3)
+ '@expo/env': 2.4.2
+ '@expo/json-file': 11.0.1
+ '@expo/metro': 56.0.0
+ '@expo/require-utils': 57.0.4(typescript@5.9.3)
+ '@expo/spawn-async': 1.8.0
+ '@jridgewell/gen-mapping': 0.3.13
+ '@jridgewell/remapping': 2.3.5
+ '@jridgewell/sourcemap-codec': 1.5.5
+ browserslist: 4.28.7
+ chalk: 4.1.2
+ debug: 4.4.3
+ getenv: 2.0.0
+ glob: 13.0.6
+ hermes-parser: 0.36.1
+ jsc-safe-url: 0.2.4
+ lightningcss: 1.33.0
+ picomatch: 4.0.5
+ postcss: 8.5.23
+ resolve-from: 5.0.0
+ optionalDependencies:
+ expo: 57.0.9(@babel/core@7.29.7)(@expo/metro-runtime@57.0.8)(expo-router@57.0.9)(react-dom@19.2.8(react@19.2.8))(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)(typescript@5.9.3)
+ transitivePeerDependencies:
+ - bufferutil
+ - supports-color
+ - typescript
+ - utf-8-validate
+
+ '@expo/metro-config@57.0.7(expo@57.0.9)(typescript@6.0.3)':
+ dependencies:
+ '@babel/code-frame': 7.29.7
+ '@babel/core': 7.29.7
+ '@babel/generator': 7.29.7
+ '@expo/config': 57.0.6(typescript@6.0.3)
+ '@expo/env': 2.4.2
+ '@expo/json-file': 11.0.1
+ '@expo/metro': 56.0.0
+ '@expo/require-utils': 57.0.4(typescript@6.0.3)
+ '@expo/spawn-async': 1.8.0
+ '@jridgewell/gen-mapping': 0.3.13
+ '@jridgewell/remapping': 2.3.5
+ '@jridgewell/sourcemap-codec': 1.5.5
+ browserslist: 4.28.7
+ chalk: 4.1.2
+ debug: 4.4.3
+ getenv: 2.0.0
+ glob: 13.0.6
+ hermes-parser: 0.36.1
+ jsc-safe-url: 0.2.4
+ lightningcss: 1.33.0
+ picomatch: 4.0.5
+ postcss: 8.5.23
+ resolve-from: 5.0.0
+ optionalDependencies:
+ expo: 57.0.9(@babel/core@7.29.7)(@expo/metro-runtime@57.0.8)(expo-router@57.0.9)(react-dom@19.2.8(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(typescript@6.0.3)
+ transitivePeerDependencies:
+ - bufferutil
+ - supports-color
+ - typescript
+ - utf-8-validate
+
+ '@expo/metro-file-map@57.0.1':
+ dependencies:
+ debug: 4.4.3
+ fb-watchman: 2.0.2
+ invariant: 2.2.4
+ jest-worker: 29.7.0
+ micromatch: 4.0.8
+ walker: 1.0.8
+ transitivePeerDependencies:
+ - supports-color
+
+ '@expo/metro-runtime@57.0.8(expo@57.0.9)(react-dom@19.2.8(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)':
+ dependencies:
+ '@expo/log-box': 57.0.2(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)
+ anser: 1.4.10
+ expo: 57.0.9(@babel/core@7.29.7)(@expo/metro-runtime@57.0.8)(expo-router@57.0.9)(react-dom@19.2.8(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(typescript@6.0.3)
+ pretty-format: 29.7.0
+ react: 19.2.3
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3)
+ stacktrace-parser: 0.1.11
+ whatwg-fetch: 3.6.20
+ optionalDependencies:
+ react-dom: 19.2.8(react@19.2.3)
+
+ '@expo/metro-runtime@57.0.8(expo@57.0.9)(react-dom@19.2.8(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)':
+ dependencies:
+ '@expo/log-box': 57.0.2(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
+ anser: 1.4.10
+ expo: 57.0.9(@babel/core@7.29.7)(@expo/metro-runtime@57.0.8)(expo-router@57.0.9)(react-dom@19.2.8(react@19.2.8))(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)(typescript@5.9.3)
+ pretty-format: 29.7.0
+ react: 19.2.8
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8)
+ stacktrace-parser: 0.1.11
+ whatwg-fetch: 3.6.20
+ optionalDependencies:
+ react-dom: 19.2.8(react@19.2.8)
+ optional: true
+
+ '@expo/metro@56.0.0':
+ dependencies:
+ metro: 0.84.4
+ metro-babel-transformer: 0.84.4
+ metro-cache: 0.84.4
+ metro-cache-key: 0.84.4
+ metro-config: 0.84.4
+ metro-core: 0.84.4
+ metro-file-map: 0.84.4
+ metro-minify-terser: 0.84.4
+ metro-resolver: 0.84.4
+ metro-runtime: 0.84.4
+ metro-source-map: 0.84.4
+ metro-symbolicate: 0.84.4
+ metro-transform-plugins: 0.84.4
+ metro-transform-worker: 0.84.4
+ transitivePeerDependencies:
+ - bufferutil
+ - supports-color
+ - utf-8-validate
+
+ '@expo/osascript@2.7.1':
+ dependencies:
+ '@expo/spawn-async': 1.8.0
+
+ '@expo/package-manager@1.13.1':
+ dependencies:
+ '@expo/json-file': 11.0.1
+ '@expo/spawn-async': 1.8.0
+ chalk: 4.1.2
+ npm-package-arg: 11.0.3
+ ora: 3.4.0
+ resolve-workspace-root: 2.0.1
+
+ '@expo/plist@0.8.1':
+ dependencies:
+ '@xmldom/xmldom': 0.8.13
+ base64-js: 1.5.1
+ xmlbuilder: 15.1.1
+
+ '@expo/prebuild-config@57.0.10(typescript@5.9.3)':
+ dependencies:
+ '@expo/config': 57.0.6(typescript@5.9.3)
+ '@expo/config-plugins': 57.0.6(typescript@5.9.3)
+ '@expo/config-types': 57.0.2
+ '@expo/image-utils': 0.11.4(typescript@5.9.3)
+ '@expo/json-file': 11.0.1
+ '@react-native/normalize-colors': 0.86.2
+ debug: 4.4.3
+ expo-modules-autolinking: 57.0.9(typescript@5.9.3)
+ resolve-from: 5.0.0
+ semver: 7.8.5
+ transitivePeerDependencies:
+ - supports-color
+ - typescript
+
+ '@expo/prebuild-config@57.0.10(typescript@6.0.3)':
+ dependencies:
+ '@expo/config': 57.0.6(typescript@6.0.3)
+ '@expo/config-plugins': 57.0.6(typescript@6.0.3)
+ '@expo/config-types': 57.0.2
+ '@expo/image-utils': 0.11.4(typescript@6.0.3)
+ '@expo/json-file': 11.0.1
+ '@react-native/normalize-colors': 0.86.2
+ debug: 4.4.3
+ expo-modules-autolinking: 57.0.9(typescript@6.0.3)
+ resolve-from: 5.0.0
+ semver: 7.8.5
+ transitivePeerDependencies:
+ - supports-color
+ - typescript
+
+ '@expo/require-utils@57.0.4(typescript@5.9.3)':
+ dependencies:
+ '@babel/code-frame': 7.29.7
+ '@babel/core': 7.29.7
+ '@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7)
+ optionalDependencies:
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@expo/require-utils@57.0.4(typescript@6.0.3)':
+ dependencies:
+ '@babel/code-frame': 7.29.7
+ '@babel/core': 7.29.7
+ '@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7)
+ optionalDependencies:
+ typescript: 6.0.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@expo/router-server@57.0.4(@expo/metro-runtime@57.0.8)(expo-constants@57.0.8)(expo-font@57.0.1)(expo-router@57.0.9)(expo-server@57.0.1)(expo@57.0.9)(react-dom@19.2.8(react@19.2.3))(react@19.2.3)':
+ dependencies:
+ debug: 4.4.3
+ expo: 57.0.9(@babel/core@7.29.7)(@expo/metro-runtime@57.0.8)(expo-router@57.0.9)(react-dom@19.2.8(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(typescript@6.0.3)
+ expo-constants: 57.0.8(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))
+ expo-font: 57.0.1(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)
+ expo-server: 57.0.1
+ react: 19.2.3
+ optionalDependencies:
+ '@expo/metro-runtime': 57.0.8(expo@57.0.9)(react-dom@19.2.8(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)
+ expo-router: 57.0.9(a32405737b9d1025ea0eea533a2e6749)
+ react-dom: 19.2.8(react@19.2.3)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@expo/router-server@57.0.4(@expo/metro-runtime@57.0.8)(expo-constants@57.0.8)(expo-font@57.0.1)(expo-router@57.0.9)(expo-server@57.0.1)(expo@57.0.9)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)':
+ dependencies:
+ debug: 4.4.3
+ expo: 57.0.9(@babel/core@7.29.7)(@expo/metro-runtime@57.0.8)(expo-router@57.0.9)(react-dom@19.2.8(react@19.2.8))(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)(typescript@5.9.3)
+ expo-constants: 57.0.8(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))
+ expo-font: 57.0.1(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
+ expo-server: 57.0.1
+ react: 19.2.8
+ optionalDependencies:
+ '@expo/metro-runtime': 57.0.8(expo@57.0.9)(react-dom@19.2.8(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
+ expo-router: 57.0.9(9c3e0008a3ce3af43de1b1cd4744d2bf)
+ react-dom: 19.2.8(react@19.2.8)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@expo/schema-utils@57.0.2': {}
+
+ '@expo/sdk-runtime-versions@1.0.0': {}
+
+ '@expo/spawn-async@1.8.0':
+ dependencies:
+ cross-spawn: 7.0.6
+
+ '@expo/sudo-prompt@9.3.2': {}
+
+ '@expo/ui@57.0.8(@babel/core@7.29.7)(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(expo@57.0.9)(react-dom@19.2.8(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)':
+ dependencies:
+ expo: 57.0.9(@babel/core@7.29.7)(@expo/metro-runtime@57.0.8)(expo-router@57.0.9)(react-dom@19.2.8(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(typescript@6.0.3)
+ react: 19.2.3
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3)
+ sf-symbols-typescript: 2.2.0
+ vaul: 1.1.2(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.3))(react@19.2.3)
+ optionalDependencies:
+ '@babel/core': 7.29.7
+ react-dom: 19.2.8(react@19.2.3)
+ react-native-worklets: 0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)
+ transitivePeerDependencies:
+ - '@types/react'
+ - '@types/react-dom'
+
+ '@expo/ui@57.0.8(@babel/core@7.29.7)(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(expo@57.0.9)(react-dom@19.2.8(react@19.2.8))(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)':
+ dependencies:
+ expo: 57.0.9(@babel/core@7.29.7)(@expo/metro-runtime@57.0.8)(expo-router@57.0.9)(react-dom@19.2.8(react@19.2.8))(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)(typescript@5.9.3)
+ react: 19.2.8
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8)
+ sf-symbols-typescript: 2.2.0
+ vaul: 1.1.2(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
+ optionalDependencies:
+ '@babel/core': 7.29.7
+ react-dom: 19.2.8(react@19.2.8)
+ react-native-worklets: 0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
+ transitivePeerDependencies:
+ - '@types/react'
+ - '@types/react-dom'
+ optional: true
+
+ '@expo/ws-tunnel@2.0.0(ws@8.21.1)':
+ dependencies:
+ ws: 8.21.1
+
+ '@expo/xcpretty@4.4.4':
+ dependencies:
+ '@babel/code-frame': 7.29.7
+ chalk: 4.1.2
+ js-yaml: 4.3.0
+
+ '@fastify/ajv-compiler@4.0.5':
+ dependencies:
+ ajv: 8.20.0
+ ajv-formats: 3.0.1
+ fast-uri: 3.1.4
+
+ '@fastify/error@4.2.0': {}
+
+ '@fastify/fast-json-stringify-compiler@5.1.0':
+ dependencies:
+ fast-json-stringify: 7.0.1
+
+ '@fastify/forwarded@3.0.1': {}
+
+ '@fastify/merge-json-schemas@0.2.1':
+ dependencies:
+ dequal: 2.0.3
+
+ '@fastify/middie@9.3.3':
dependencies:
'@fastify/error': 4.2.0
fastify-plugin: 6.0.0
@@ -10303,6 +12310,12 @@ snapshots:
'@types/semver': 7.5.8
semver: 7.6.3
+ '@module-federation/bridge-react-webpack-plugin@2.1.0':
+ dependencies:
+ '@module-federation/sdk': 2.1.0
+ '@types/semver': 7.5.8
+ semver: 7.6.3
+
'@module-federation/bridge-react-webpack-plugin@2.8.0':
dependencies:
'@module-federation/sdk': 2.8.0
@@ -10352,6 +12365,21 @@ snapshots:
- utf-8-validate
- vue-tsc
+ '@module-federation/cli@2.1.0(typescript@6.0.3)':
+ dependencies:
+ '@module-federation/dts-plugin': 2.1.0(typescript@6.0.3)
+ '@module-federation/sdk': 2.1.0
+ chalk: 3.0.0
+ commander: 11.1.0
+ jiti: 2.4.2
+ transitivePeerDependencies:
+ - bufferutil
+ - debug
+ - supports-color
+ - typescript
+ - utf-8-validate
+ - vue-tsc
+
'@module-federation/cli@2.8.0(typescript@5.9.3)':
dependencies:
'@module-federation/dts-plugin': 2.8.0(typescript@5.9.3)
@@ -10397,6 +12425,15 @@ snapshots:
react: 19.2.8
react-dom: 19.2.8(react@19.2.8)
+ '@module-federation/data-prefetch@2.1.0(react-dom@19.2.8(react@19.2.3))(react@19.2.3)':
+ dependencies:
+ '@module-federation/runtime': 2.1.0
+ '@module-federation/sdk': 2.1.0
+ fs-extra: 9.1.0
+ optionalDependencies:
+ react: 19.2.3
+ react-dom: 19.2.8(react@19.2.3)
+
'@module-federation/dts-plugin@0.15.0(typescript@5.9.3)':
dependencies:
'@module-federation/error-codes': 0.15.0
@@ -10472,24 +12509,48 @@ snapshots:
- supports-color
- utf-8-validate
- '@module-federation/dts-plugin@2.0.1(typescript@5.9.3)':
+ '@module-federation/dts-plugin@2.0.1(typescript@5.9.3)':
+ dependencies:
+ '@module-federation/error-codes': 2.0.1
+ '@module-federation/managers': 2.0.1
+ '@module-federation/sdk': 2.0.1
+ '@module-federation/third-party-dts-extractor': 2.0.1
+ adm-zip: 0.6.0
+ ansi-colors: 4.1.3
+ axios: 1.18.1
+ chalk: 3.0.0
+ fs-extra: 9.1.0
+ isomorphic-ws: 5.0.0(ws@8.21.1)
+ koa: 3.2.1
+ lodash.clonedeepwith: 4.5.0
+ log4js: 6.9.1
+ node-schedule: 2.1.1
+ rambda: 9.4.2
+ typescript: 5.9.3
+ ws: 8.21.1
+ transitivePeerDependencies:
+ - bufferutil
+ - debug
+ - supports-color
+ - utf-8-validate
+
+ '@module-federation/dts-plugin@2.1.0(typescript@6.0.3)':
dependencies:
- '@module-federation/error-codes': 2.0.1
- '@module-federation/managers': 2.0.1
- '@module-federation/sdk': 2.0.1
- '@module-federation/third-party-dts-extractor': 2.0.1
+ '@module-federation/error-codes': 2.1.0
+ '@module-federation/managers': 2.1.0
+ '@module-federation/sdk': 2.1.0
+ '@module-federation/third-party-dts-extractor': 2.1.0
adm-zip: 0.6.0
ansi-colors: 4.1.3
axios: 1.18.1
chalk: 3.0.0
fs-extra: 9.1.0
isomorphic-ws: 5.0.0(ws@8.21.1)
- koa: 3.2.1
lodash.clonedeepwith: 4.5.0
log4js: 6.9.1
node-schedule: 2.1.1
rambda: 9.4.2
- typescript: 5.9.3
+ typescript: 6.0.3
ws: 8.21.1
transitivePeerDependencies:
- bufferutil
@@ -10622,6 +12683,34 @@ snapshots:
- supports-color
- utf-8-validate
+ '@module-federation/enhanced@2.1.0(@rspack/core@1.6.0(@swc/helpers@0.5.23))(react-dom@19.2.8(react@19.2.3))(react@19.2.3)(typescript@6.0.3)(webpack@5.105.4)':
+ dependencies:
+ '@module-federation/bridge-react-webpack-plugin': 2.1.0
+ '@module-federation/cli': 2.1.0(typescript@6.0.3)
+ '@module-federation/data-prefetch': 2.1.0(react-dom@19.2.8(react@19.2.3))(react@19.2.3)
+ '@module-federation/dts-plugin': 2.1.0(typescript@6.0.3)
+ '@module-federation/error-codes': 2.1.0
+ '@module-federation/inject-external-runtime-core-plugin': 2.1.0(@module-federation/runtime-tools@2.1.0)
+ '@module-federation/managers': 2.1.0
+ '@module-federation/manifest': 2.1.0(typescript@6.0.3)
+ '@module-federation/rspack': 2.1.0(@rspack/core@1.6.0(@swc/helpers@0.5.23))(typescript@6.0.3)
+ '@module-federation/runtime-tools': 2.1.0
+ '@module-federation/sdk': 2.1.0
+ btoa: 1.2.1
+ schema-utils: 4.3.3
+ upath: 2.0.1
+ optionalDependencies:
+ typescript: 6.0.3
+ webpack: 5.105.4
+ transitivePeerDependencies:
+ - '@rspack/core'
+ - bufferutil
+ - debug
+ - react
+ - react-dom
+ - supports-color
+ - utf-8-validate
+
'@module-federation/enhanced@2.8.0(@rspack/core@1.6.0(@swc/helpers@0.5.23))(typescript@5.9.3)(webpack@5.105.4)':
dependencies:
'@module-federation/bridge-react-webpack-plugin': 2.8.0
@@ -10657,6 +12746,8 @@ snapshots:
'@module-federation/error-codes@2.0.1': {}
+ '@module-federation/error-codes@2.1.0': {}
+
'@module-federation/error-codes@2.8.0': {}
'@module-federation/inject-external-runtime-core-plugin@0.15.0(@module-federation/runtime-tools@0.15.0)':
@@ -10675,6 +12766,10 @@ snapshots:
dependencies:
'@module-federation/runtime-tools': 2.0.1
+ '@module-federation/inject-external-runtime-core-plugin@2.1.0(@module-federation/runtime-tools@2.1.0)':
+ dependencies:
+ '@module-federation/runtime-tools': 2.1.0
+
'@module-federation/inject-external-runtime-core-plugin@2.8.0(@module-federation/runtime-tools@2.8.0)':
dependencies:
'@module-federation/runtime-tools': 2.8.0
@@ -10703,6 +12798,12 @@ snapshots:
find-pkg: 2.0.0
fs-extra: 9.1.0
+ '@module-federation/managers@2.1.0':
+ dependencies:
+ '@module-federation/sdk': 2.1.0
+ find-pkg: 2.0.0
+ fs-extra: 9.1.0
+
'@module-federation/managers@2.8.0':
dependencies:
'@module-federation/sdk': 2.8.0
@@ -10767,6 +12868,21 @@ snapshots:
- utf-8-validate
- vue-tsc
+ '@module-federation/manifest@2.1.0(typescript@6.0.3)':
+ dependencies:
+ '@module-federation/dts-plugin': 2.1.0(typescript@6.0.3)
+ '@module-federation/managers': 2.1.0
+ '@module-federation/sdk': 2.1.0
+ chalk: 3.0.0
+ find-pkg: 2.0.0
+ transitivePeerDependencies:
+ - bufferutil
+ - debug
+ - supports-color
+ - typescript
+ - utf-8-validate
+ - vue-tsc
+
'@module-federation/manifest@2.8.0(typescript@5.9.3)':
dependencies:
'@module-federation/dts-plugin': 2.8.0(typescript@5.9.3)
@@ -10853,6 +12969,25 @@ snapshots:
- supports-color
- utf-8-validate
+ '@module-federation/rspack@2.1.0(@rspack/core@1.6.0(@swc/helpers@0.5.23))(typescript@6.0.3)':
+ dependencies:
+ '@module-federation/bridge-react-webpack-plugin': 2.1.0
+ '@module-federation/dts-plugin': 2.1.0(typescript@6.0.3)
+ '@module-federation/inject-external-runtime-core-plugin': 2.1.0(@module-federation/runtime-tools@2.1.0)
+ '@module-federation/managers': 2.1.0
+ '@module-federation/manifest': 2.1.0(typescript@6.0.3)
+ '@module-federation/runtime-tools': 2.1.0
+ '@module-federation/sdk': 2.1.0
+ '@rspack/core': 1.6.0(@swc/helpers@0.5.23)
+ btoa: 1.2.1
+ optionalDependencies:
+ typescript: 6.0.3
+ transitivePeerDependencies:
+ - bufferutil
+ - debug
+ - supports-color
+ - utf-8-validate
+
'@module-federation/rspack@2.8.0(@rspack/core@1.6.0(@swc/helpers@0.5.23))(typescript@5.9.3)':
dependencies:
'@module-federation/bridge-react-webpack-plugin': 2.8.0
@@ -10899,6 +13034,11 @@ snapshots:
'@module-federation/error-codes': 2.0.1
'@module-federation/sdk': 2.0.1
+ '@module-federation/runtime-core@2.1.0':
+ dependencies:
+ '@module-federation/error-codes': 2.1.0
+ '@module-federation/sdk': 2.1.0
+
'@module-federation/runtime-core@2.8.0':
dependencies:
'@module-federation/error-codes': 2.8.0
@@ -10934,6 +13074,11 @@ snapshots:
'@module-federation/runtime': 2.0.1
'@module-federation/webpack-bundler-runtime': 2.0.1
+ '@module-federation/runtime-tools@2.1.0':
+ dependencies:
+ '@module-federation/runtime': 2.1.0
+ '@module-federation/webpack-bundler-runtime': 2.1.0
+
'@module-federation/runtime-tools@2.8.0':
dependencies:
'@module-federation/runtime': 2.8.0
@@ -10975,6 +13120,12 @@ snapshots:
'@module-federation/runtime-core': 2.0.1
'@module-federation/sdk': 2.0.1
+ '@module-federation/runtime@2.1.0':
+ dependencies:
+ '@module-federation/error-codes': 2.1.0
+ '@module-federation/runtime-core': 2.1.0
+ '@module-federation/sdk': 2.1.0
+
'@module-federation/runtime@2.8.0':
dependencies:
'@module-federation/error-codes': 2.8.0
@@ -10997,6 +13148,8 @@ snapshots:
'@module-federation/sdk@2.0.1': {}
+ '@module-federation/sdk@2.1.0': {}
+
'@module-federation/sdk@2.8.0': {}
'@module-federation/third-party-dts-extractor@0.15.0':
@@ -11023,6 +13176,12 @@ snapshots:
fs-extra: 9.1.0
resolve: 1.22.8
+ '@module-federation/third-party-dts-extractor@2.1.0':
+ dependencies:
+ find-pkg: 2.0.0
+ fs-extra: 9.1.0
+ resolve: 1.22.8
+
'@module-federation/third-party-dts-extractor@2.8.0': {}
'@module-federation/webpack-bundler-runtime@0.13.1':
@@ -11055,6 +13214,11 @@ snapshots:
'@module-federation/runtime': 2.0.1
'@module-federation/sdk': 2.0.1
+ '@module-federation/webpack-bundler-runtime@2.1.0':
+ dependencies:
+ '@module-federation/runtime': 2.1.0
+ '@module-federation/sdk': 2.1.0
+
'@module-federation/webpack-bundler-runtime@2.8.0':
dependencies:
'@module-federation/error-codes': 2.8.0
@@ -11068,24 +13232,434 @@ snapshots:
'@tybys/wasm-util': 0.10.3
optional: true
- '@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.3':
+ '@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.3':
+ optional: true
+
+ '@nodable/entities@3.0.0': {}
+
+ '@nodelib/fs.scandir@2.1.5':
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ run-parallel: 1.2.0
+
+ '@nodelib/fs.stat@2.0.5': {}
+
+ '@nodelib/fs.walk@1.2.8':
+ dependencies:
+ '@nodelib/fs.scandir': 2.1.5
+ fastq: 1.20.1
+
+ '@pinojs/redact@0.4.0': {}
+
+ '@radix-ui/primitive@1.1.7': {}
+
+ '@radix-ui/react-collection@1.1.15(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.3))(react@19.2.3)':
+ dependencies:
+ '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.17)(react@19.2.3)
+ '@radix-ui/react-context': 1.2.2(@types/react@19.2.17)(react@19.2.3)
+ '@radix-ui/react-primitive': 2.1.10(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.3))(react@19.2.3)
+ '@radix-ui/react-slot': 1.3.3(@types/react@19.2.17)(react@19.2.3)
+ react: 19.2.3
+ react-dom: 19.2.8(react@19.2.3)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 17.0.26(@types/react@19.2.17)
+
+ '@radix-ui/react-collection@1.1.15(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)':
+ dependencies:
+ '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.17)(react@19.2.8)
+ '@radix-ui/react-context': 1.2.2(@types/react@19.2.17)(react@19.2.8)
+ '@radix-ui/react-primitive': 2.1.10(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
+ '@radix-ui/react-slot': 1.3.3(@types/react@19.2.17)(react@19.2.8)
+ react: 19.2.8
+ react-dom: 19.2.8(react@19.2.8)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 17.0.26(@types/react@19.2.17)
+ optional: true
+
+ '@radix-ui/react-compose-refs@1.1.5(@types/react@19.2.17)(react@19.2.3)':
+ dependencies:
+ react: 19.2.3
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@radix-ui/react-compose-refs@1.1.5(@types/react@19.2.17)(react@19.2.8)':
+ dependencies:
+ react: 19.2.8
+ optionalDependencies:
+ '@types/react': 19.2.17
+ optional: true
+
+ '@radix-ui/react-context@1.2.2(@types/react@19.2.17)(react@19.2.3)':
+ dependencies:
+ react: 19.2.3
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@radix-ui/react-context@1.2.2(@types/react@19.2.17)(react@19.2.8)':
+ dependencies:
+ react: 19.2.8
+ optionalDependencies:
+ '@types/react': 19.2.17
+ optional: true
+
+ '@radix-ui/react-dialog@1.1.23(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.3))(react@19.2.3)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.7
+ '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.17)(react@19.2.3)
+ '@radix-ui/react-context': 1.2.2(@types/react@19.2.17)(react@19.2.3)
+ '@radix-ui/react-dismissable-layer': 1.1.19(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.3))(react@19.2.3)
+ '@radix-ui/react-focus-guards': 1.1.6(@types/react@19.2.17)(react@19.2.3)
+ '@radix-ui/react-focus-scope': 1.1.16(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.3))(react@19.2.3)
+ '@radix-ui/react-id': 1.1.4(@types/react@19.2.17)(react@19.2.3)
+ '@radix-ui/react-portal': 1.1.17(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.3))(react@19.2.3)
+ '@radix-ui/react-presence': 1.1.10(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.3))(react@19.2.3)
+ '@radix-ui/react-primitive': 2.1.10(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.3))(react@19.2.3)
+ '@radix-ui/react-slot': 1.3.3(@types/react@19.2.17)(react@19.2.3)
+ '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.17)(react@19.2.3)
+ '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.17)(react@19.2.3)
+ aria-hidden: 1.2.6
+ react: 19.2.3
+ react-dom: 19.2.8(react@19.2.3)
+ react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.3)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 17.0.26(@types/react@19.2.17)
+
+ '@radix-ui/react-dialog@1.1.23(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.7
+ '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.17)(react@19.2.8)
+ '@radix-ui/react-context': 1.2.2(@types/react@19.2.17)(react@19.2.8)
+ '@radix-ui/react-dismissable-layer': 1.1.19(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
+ '@radix-ui/react-focus-guards': 1.1.6(@types/react@19.2.17)(react@19.2.8)
+ '@radix-ui/react-focus-scope': 1.1.16(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
+ '@radix-ui/react-id': 1.1.4(@types/react@19.2.17)(react@19.2.8)
+ '@radix-ui/react-portal': 1.1.17(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
+ '@radix-ui/react-presence': 1.1.10(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
+ '@radix-ui/react-primitive': 2.1.10(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
+ '@radix-ui/react-slot': 1.3.3(@types/react@19.2.17)(react@19.2.8)
+ '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.17)(react@19.2.8)
+ '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.17)(react@19.2.8)
+ aria-hidden: 1.2.6
+ react: 19.2.8
+ react-dom: 19.2.8(react@19.2.8)
+ react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.8)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 17.0.26(@types/react@19.2.17)
+ optional: true
+
+ '@radix-ui/react-direction@1.1.4(@types/react@19.2.17)(react@19.2.3)':
+ dependencies:
+ react: 19.2.3
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@radix-ui/react-direction@1.1.4(@types/react@19.2.17)(react@19.2.8)':
+ dependencies:
+ react: 19.2.8
+ optionalDependencies:
+ '@types/react': 19.2.17
+ optional: true
+
+ '@radix-ui/react-dismissable-layer@1.1.19(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.3))(react@19.2.3)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.7
+ '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.17)(react@19.2.3)
+ '@radix-ui/react-primitive': 2.1.10(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.3))(react@19.2.3)
+ '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.2.17)(react@19.2.3)
+ '@radix-ui/react-use-effect-event': 0.0.5(@types/react@19.2.17)(react@19.2.3)
+ react: 19.2.3
+ react-dom: 19.2.8(react@19.2.3)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 17.0.26(@types/react@19.2.17)
+
+ '@radix-ui/react-dismissable-layer@1.1.19(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.7
+ '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.17)(react@19.2.8)
+ '@radix-ui/react-primitive': 2.1.10(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
+ '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.2.17)(react@19.2.8)
+ '@radix-ui/react-use-effect-event': 0.0.5(@types/react@19.2.17)(react@19.2.8)
+ react: 19.2.8
+ react-dom: 19.2.8(react@19.2.8)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 17.0.26(@types/react@19.2.17)
+ optional: true
+
+ '@radix-ui/react-focus-guards@1.1.6(@types/react@19.2.17)(react@19.2.3)':
+ dependencies:
+ react: 19.2.3
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@radix-ui/react-focus-guards@1.1.6(@types/react@19.2.17)(react@19.2.8)':
+ dependencies:
+ react: 19.2.8
+ optionalDependencies:
+ '@types/react': 19.2.17
+ optional: true
+
+ '@radix-ui/react-focus-scope@1.1.16(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.3))(react@19.2.3)':
+ dependencies:
+ '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.17)(react@19.2.3)
+ '@radix-ui/react-primitive': 2.1.10(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.3))(react@19.2.3)
+ '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.2.17)(react@19.2.3)
+ react: 19.2.3
+ react-dom: 19.2.8(react@19.2.3)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 17.0.26(@types/react@19.2.17)
+
+ '@radix-ui/react-focus-scope@1.1.16(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)':
+ dependencies:
+ '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.17)(react@19.2.8)
+ '@radix-ui/react-primitive': 2.1.10(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
+ '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.2.17)(react@19.2.8)
+ react: 19.2.8
+ react-dom: 19.2.8(react@19.2.8)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 17.0.26(@types/react@19.2.17)
+ optional: true
+
+ '@radix-ui/react-id@1.1.4(@types/react@19.2.17)(react@19.2.3)':
+ dependencies:
+ '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.17)(react@19.2.3)
+ react: 19.2.3
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@radix-ui/react-id@1.1.4(@types/react@19.2.17)(react@19.2.8)':
+ dependencies:
+ '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.17)(react@19.2.8)
+ react: 19.2.8
+ optionalDependencies:
+ '@types/react': 19.2.17
+ optional: true
+
+ '@radix-ui/react-portal@1.1.17(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.3))(react@19.2.3)':
+ dependencies:
+ '@radix-ui/react-primitive': 2.1.10(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.3))(react@19.2.3)
+ '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.17)(react@19.2.3)
+ react: 19.2.3
+ react-dom: 19.2.8(react@19.2.3)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 17.0.26(@types/react@19.2.17)
+
+ '@radix-ui/react-portal@1.1.17(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)':
+ dependencies:
+ '@radix-ui/react-primitive': 2.1.10(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
+ '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.17)(react@19.2.8)
+ react: 19.2.8
+ react-dom: 19.2.8(react@19.2.8)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 17.0.26(@types/react@19.2.17)
+ optional: true
+
+ '@radix-ui/react-presence@1.1.10(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.3))(react@19.2.3)':
+ dependencies:
+ '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.17)(react@19.2.3)
+ react: 19.2.3
+ react-dom: 19.2.8(react@19.2.3)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 17.0.26(@types/react@19.2.17)
+
+ '@radix-ui/react-presence@1.1.10(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)':
+ dependencies:
+ '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.17)(react@19.2.8)
+ react: 19.2.8
+ react-dom: 19.2.8(react@19.2.8)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 17.0.26(@types/react@19.2.17)
+ optional: true
+
+ '@radix-ui/react-primitive@2.1.10(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.3))(react@19.2.3)':
+ dependencies:
+ '@radix-ui/react-slot': 1.3.3(@types/react@19.2.17)(react@19.2.3)
+ react: 19.2.3
+ react-dom: 19.2.8(react@19.2.3)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 17.0.26(@types/react@19.2.17)
+
+ '@radix-ui/react-primitive@2.1.10(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)':
+ dependencies:
+ '@radix-ui/react-slot': 1.3.3(@types/react@19.2.17)(react@19.2.8)
+ react: 19.2.8
+ react-dom: 19.2.8(react@19.2.8)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 17.0.26(@types/react@19.2.17)
+ optional: true
+
+ '@radix-ui/react-roving-focus@1.1.19(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.3))(react@19.2.3)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.7
+ '@radix-ui/react-collection': 1.1.15(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.3))(react@19.2.3)
+ '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.17)(react@19.2.3)
+ '@radix-ui/react-context': 1.2.2(@types/react@19.2.17)(react@19.2.3)
+ '@radix-ui/react-direction': 1.1.4(@types/react@19.2.17)(react@19.2.3)
+ '@radix-ui/react-id': 1.1.4(@types/react@19.2.17)(react@19.2.3)
+ '@radix-ui/react-primitive': 2.1.10(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.3))(react@19.2.3)
+ '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.2.17)(react@19.2.3)
+ '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.17)(react@19.2.3)
+ '@radix-ui/react-use-is-hydrated': 0.1.3(@types/react@19.2.17)(react@19.2.3)
+ '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.17)(react@19.2.3)
+ react: 19.2.3
+ react-dom: 19.2.8(react@19.2.3)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 17.0.26(@types/react@19.2.17)
+
+ '@radix-ui/react-roving-focus@1.1.19(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.7
+ '@radix-ui/react-collection': 1.1.15(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
+ '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.17)(react@19.2.8)
+ '@radix-ui/react-context': 1.2.2(@types/react@19.2.17)(react@19.2.8)
+ '@radix-ui/react-direction': 1.1.4(@types/react@19.2.17)(react@19.2.8)
+ '@radix-ui/react-id': 1.1.4(@types/react@19.2.17)(react@19.2.8)
+ '@radix-ui/react-primitive': 2.1.10(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
+ '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.2.17)(react@19.2.8)
+ '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.17)(react@19.2.8)
+ '@radix-ui/react-use-is-hydrated': 0.1.3(@types/react@19.2.17)(react@19.2.8)
+ '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.17)(react@19.2.8)
+ react: 19.2.8
+ react-dom: 19.2.8(react@19.2.8)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 17.0.26(@types/react@19.2.17)
+ optional: true
+
+ '@radix-ui/react-slot@1.3.3(@types/react@19.2.17)(react@19.2.3)':
+ dependencies:
+ '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.17)(react@19.2.3)
+ react: 19.2.3
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@radix-ui/react-slot@1.3.3(@types/react@19.2.17)(react@19.2.8)':
+ dependencies:
+ '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.17)(react@19.2.8)
+ react: 19.2.8
+ optionalDependencies:
+ '@types/react': 19.2.17
+ optional: true
+
+ '@radix-ui/react-tabs@1.1.21(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.3))(react@19.2.3)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.7
+ '@radix-ui/react-context': 1.2.2(@types/react@19.2.17)(react@19.2.3)
+ '@radix-ui/react-direction': 1.1.4(@types/react@19.2.17)(react@19.2.3)
+ '@radix-ui/react-id': 1.1.4(@types/react@19.2.17)(react@19.2.3)
+ '@radix-ui/react-presence': 1.1.10(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.3))(react@19.2.3)
+ '@radix-ui/react-primitive': 2.1.10(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.3))(react@19.2.3)
+ '@radix-ui/react-roving-focus': 1.1.19(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.3))(react@19.2.3)
+ '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.17)(react@19.2.3)
+ react: 19.2.3
+ react-dom: 19.2.8(react@19.2.3)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 17.0.26(@types/react@19.2.17)
+
+ '@radix-ui/react-tabs@1.1.21(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.7
+ '@radix-ui/react-context': 1.2.2(@types/react@19.2.17)(react@19.2.8)
+ '@radix-ui/react-direction': 1.1.4(@types/react@19.2.17)(react@19.2.8)
+ '@radix-ui/react-id': 1.1.4(@types/react@19.2.17)(react@19.2.8)
+ '@radix-ui/react-presence': 1.1.10(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
+ '@radix-ui/react-primitive': 2.1.10(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
+ '@radix-ui/react-roving-focus': 1.1.19(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
+ '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.17)(react@19.2.8)
+ react: 19.2.8
+ react-dom: 19.2.8(react@19.2.8)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 17.0.26(@types/react@19.2.17)
+ optional: true
+
+ '@radix-ui/react-use-callback-ref@1.1.4(@types/react@19.2.17)(react@19.2.3)':
+ dependencies:
+ react: 19.2.3
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@radix-ui/react-use-callback-ref@1.1.4(@types/react@19.2.17)(react@19.2.8)':
+ dependencies:
+ react: 19.2.8
+ optionalDependencies:
+ '@types/react': 19.2.17
+ optional: true
+
+ '@radix-ui/react-use-controllable-state@1.2.6(@types/react@19.2.17)(react@19.2.3)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.7
+ '@radix-ui/react-use-effect-event': 0.0.5(@types/react@19.2.17)(react@19.2.3)
+ '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.17)(react@19.2.3)
+ react: 19.2.3
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@radix-ui/react-use-controllable-state@1.2.6(@types/react@19.2.17)(react@19.2.8)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.7
+ '@radix-ui/react-use-effect-event': 0.0.5(@types/react@19.2.17)(react@19.2.8)
+ '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.17)(react@19.2.8)
+ react: 19.2.8
+ optionalDependencies:
+ '@types/react': 19.2.17
+ optional: true
+
+ '@radix-ui/react-use-effect-event@0.0.5(@types/react@19.2.17)(react@19.2.3)':
+ dependencies:
+ '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.17)(react@19.2.3)
+ react: 19.2.3
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@radix-ui/react-use-effect-event@0.0.5(@types/react@19.2.17)(react@19.2.8)':
+ dependencies:
+ '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.17)(react@19.2.8)
+ react: 19.2.8
+ optionalDependencies:
+ '@types/react': 19.2.17
optional: true
- '@nodable/entities@3.0.0': {}
-
- '@nodelib/fs.scandir@2.1.5':
+ '@radix-ui/react-use-is-hydrated@0.1.3(@types/react@19.2.17)(react@19.2.3)':
dependencies:
- '@nodelib/fs.stat': 2.0.5
- run-parallel: 1.2.0
+ react: 19.2.3
+ optionalDependencies:
+ '@types/react': 19.2.17
- '@nodelib/fs.stat@2.0.5': {}
+ '@radix-ui/react-use-is-hydrated@0.1.3(@types/react@19.2.17)(react@19.2.8)':
+ dependencies:
+ react: 19.2.8
+ optionalDependencies:
+ '@types/react': 19.2.17
+ optional: true
- '@nodelib/fs.walk@1.2.8':
+ '@radix-ui/react-use-layout-effect@1.1.4(@types/react@19.2.17)(react@19.2.3)':
dependencies:
- '@nodelib/fs.scandir': 2.1.5
- fastq: 1.20.1
+ react: 19.2.3
+ optionalDependencies:
+ '@types/react': 19.2.17
- '@pinojs/redact@0.4.0': {}
+ '@radix-ui/react-use-layout-effect@1.1.4(@types/react@19.2.17)(react@19.2.8)':
+ dependencies:
+ react: 19.2.8
+ optionalDependencies:
+ '@types/react': 19.2.17
+ optional: true
'@react-native-async-storage/async-storage@3.1.1(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)':
dependencies:
@@ -11093,6 +13667,13 @@ snapshots:
react: 19.2.8
react-native: 0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8)
+ '@react-native-community/cli-clean@20.1.2':
+ dependencies:
+ '@react-native-community/cli-tools': 20.1.2
+ execa: 5.1.1
+ fast-glob: 3.3.3
+ picocolors: 1.1.1
+
'@react-native-community/cli-clean@20.2.0':
dependencies:
'@react-native-community/cli-tools': 20.2.0
@@ -11100,6 +13681,13 @@ snapshots:
fast-glob: 3.3.3
picocolors: 1.1.1
+ '@react-native-community/cli-config-android@20.1.2':
+ dependencies:
+ '@react-native-community/cli-tools': 20.1.2
+ fast-glob: 3.3.3
+ fast-xml-parser: 5.10.1
+ picocolors: 1.1.1
+
'@react-native-community/cli-config-android@20.2.0':
dependencies:
'@react-native-community/cli-tools': 20.2.0
@@ -11107,6 +13695,13 @@ snapshots:
fast-xml-parser: 5.10.1
picocolors: 1.1.1
+ '@react-native-community/cli-config-apple@20.1.2':
+ dependencies:
+ '@react-native-community/cli-tools': 20.1.2
+ execa: 5.1.1
+ fast-glob: 3.3.3
+ picocolors: 1.1.1
+
'@react-native-community/cli-config-apple@20.2.0':
dependencies:
'@react-native-community/cli-tools': 20.2.0
@@ -11114,6 +13709,17 @@ snapshots:
fast-glob: 3.3.3
picocolors: 1.1.1
+ '@react-native-community/cli-config@20.1.2(typescript@6.0.3)':
+ dependencies:
+ '@react-native-community/cli-tools': 20.1.2
+ cosmiconfig: 9.0.2(typescript@6.0.3)
+ deepmerge: 4.3.1
+ fast-glob: 3.3.3
+ joi: 17.13.4
+ picocolors: 1.1.1
+ transitivePeerDependencies:
+ - typescript
+
'@react-native-community/cli-config@20.2.0(typescript@5.9.3)':
dependencies:
'@react-native-community/cli-tools': 20.2.0
@@ -11125,6 +13731,26 @@ snapshots:
transitivePeerDependencies:
- typescript
+ '@react-native-community/cli-doctor@20.1.2(typescript@6.0.3)':
+ dependencies:
+ '@react-native-community/cli-config': 20.1.2(typescript@6.0.3)
+ '@react-native-community/cli-platform-android': 20.1.2
+ '@react-native-community/cli-platform-apple': 20.1.2
+ '@react-native-community/cli-platform-ios': 20.1.2
+ '@react-native-community/cli-tools': 20.1.2
+ command-exists: 1.2.9
+ deepmerge: 4.3.1
+ envinfo: 7.21.0
+ execa: 5.1.1
+ node-stream-zip: 1.16.0
+ ora: 5.4.1
+ picocolors: 1.1.1
+ semver: 7.8.5
+ wcwidth: 1.0.1
+ yaml: 2.9.0
+ transitivePeerDependencies:
+ - typescript
+
'@react-native-community/cli-doctor@20.2.0(typescript@5.9.3)':
dependencies:
'@react-native-community/cli-config': 20.2.0(typescript@5.9.3)
@@ -11145,6 +13771,14 @@ snapshots:
transitivePeerDependencies:
- typescript
+ '@react-native-community/cli-platform-android@20.1.2':
+ dependencies:
+ '@react-native-community/cli-config-android': 20.1.2
+ '@react-native-community/cli-tools': 20.1.2
+ execa: 5.1.1
+ logkitty: 0.7.1
+ picocolors: 1.1.1
+
'@react-native-community/cli-platform-android@20.2.0':
dependencies:
'@react-native-community/cli-config-android': 20.2.0
@@ -11153,6 +13787,14 @@ snapshots:
logkitty: 0.7.1
picocolors: 1.1.1
+ '@react-native-community/cli-platform-apple@20.1.2':
+ dependencies:
+ '@react-native-community/cli-config-apple': 20.1.2
+ '@react-native-community/cli-tools': 20.1.2
+ execa: 5.1.1
+ fast-xml-parser: 5.10.1
+ picocolors: 1.1.1
+
'@react-native-community/cli-platform-apple@20.2.0':
dependencies:
'@react-native-community/cli-config-apple': 20.2.0
@@ -11161,10 +13803,32 @@ snapshots:
fast-xml-parser: 5.10.1
picocolors: 1.1.1
+ '@react-native-community/cli-platform-ios@20.1.2':
+ dependencies:
+ '@react-native-community/cli-platform-apple': 20.1.2
+
'@react-native-community/cli-platform-ios@20.2.0':
dependencies:
'@react-native-community/cli-platform-apple': 20.2.0
+ '@react-native-community/cli-server-api@20.1.2':
+ dependencies:
+ '@react-native-community/cli-tools': 20.1.2
+ body-parser: 2.3.0
+ compression: 1.8.1
+ connect: 3.7.0
+ errorhandler: 1.5.2
+ nocache: 3.0.4
+ open: 6.4.0
+ pretty-format: 29.7.0
+ serve-static: 1.16.3
+ strict-url-sanitise: 0.0.1
+ ws: 6.2.6
+ transitivePeerDependencies:
+ - bufferutil
+ - supports-color
+ - utf-8-validate
+
'@react-native-community/cli-server-api@20.2.0':
dependencies:
'@react-native-community/cli-tools': 20.2.0
@@ -11182,6 +13846,19 @@ snapshots:
- supports-color
- utf-8-validate
+ '@react-native-community/cli-tools@20.1.2':
+ dependencies:
+ '@vscode/sudo-prompt': 9.3.2
+ appdirsjs: 1.2.7
+ execa: 5.1.1
+ find-up: 5.0.0
+ launch-editor: 2.14.1
+ mime: 2.6.0
+ ora: 5.4.1
+ picocolors: 1.1.1
+ prompts: 2.4.2
+ semver: 7.8.5
+
'@react-native-community/cli-tools@20.2.0':
dependencies:
'@vscode/sudo-prompt': 9.3.2
@@ -11195,10 +13872,37 @@ snapshots:
prompts: 2.4.2
semver: 7.8.5
+ '@react-native-community/cli-types@20.1.2':
+ dependencies:
+ joi: 17.13.4
+
'@react-native-community/cli-types@20.2.0':
dependencies:
joi: 17.13.4
+ '@react-native-community/cli@20.1.2(typescript@6.0.3)':
+ dependencies:
+ '@react-native-community/cli-clean': 20.1.2
+ '@react-native-community/cli-config': 20.1.2(typescript@6.0.3)
+ '@react-native-community/cli-doctor': 20.1.2(typescript@6.0.3)
+ '@react-native-community/cli-server-api': 20.1.2
+ '@react-native-community/cli-tools': 20.1.2
+ '@react-native-community/cli-types': 20.1.2
+ commander: 9.5.0
+ deepmerge: 4.3.1
+ execa: 5.1.1
+ find-up: 5.0.0
+ fs-extra: 8.1.0
+ graceful-fs: 4.2.11
+ picocolors: 1.1.1
+ prompts: 2.4.2
+ semver: 7.8.5
+ transitivePeerDependencies:
+ - bufferutil
+ - supports-color
+ - typescript
+ - utf-8-validate
+
'@react-native-community/cli@20.2.0(typescript@5.9.3)':
dependencies:
'@react-native-community/cli-clean': 20.2.0
@@ -11222,8 +13926,27 @@ snapshots:
- typescript
- utf-8-validate
+ '@react-native-masked-view/masked-view@0.3.2(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)':
+ dependencies:
+ react: 19.2.8
+ react-native: 0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8)
+ optional: true
+
+ '@react-native-masked-view/masked-view@0.3.2(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)':
+ dependencies:
+ react: 19.2.3
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3)
+
+ '@react-native-masked-view/masked-view@0.3.2(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)':
+ dependencies:
+ react: 19.2.8
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8)
+ optional: true
+
'@react-native/assets-registry@0.86.0': {}
+ '@react-native/assets-registry@0.86.2': {}
+
'@react-native/babel-plugin-codegen@0.86.0(@babel/core@7.29.7)':
dependencies:
'@babel/traverse': 7.29.7
@@ -11232,6 +13955,14 @@ snapshots:
- '@babel/core'
- supports-color
+ '@react-native/babel-plugin-codegen@0.86.2(@babel/core@7.29.7)':
+ dependencies:
+ '@babel/traverse': 7.29.7
+ '@react-native/codegen': 0.86.2(@babel/core@7.29.7)
+ transitivePeerDependencies:
+ - '@babel/core'
+ - supports-color
+
'@react-native/babel-preset@0.86.0(@babel/core@7.29.7)':
dependencies:
'@babel/core': 7.29.7
@@ -11270,6 +14001,44 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@react-native/babel-preset@0.86.2(@babel/core@7.29.7)':
+ dependencies:
+ '@babel/core': 7.29.7
+ '@babel/plugin-proposal-export-default-from': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.29.7)
+ '@babel/plugin-syntax-export-default-from': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.7)
+ '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.7)
+ '@babel/plugin-transform-async-generator-functions': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-async-to-generator': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-block-scoping': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-class-properties': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-classes': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-destructuring': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-flow-strip-types': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-for-of': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-named-capturing-groups-regex': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-nullish-coalescing-operator': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-optional-catch-binding': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-optional-chaining': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-private-methods': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-private-property-in-object': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-react-display-name': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-react-jsx': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-react-jsx-self': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-react-jsx-source': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-regenerator': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-runtime': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-unicode-regex': 7.29.7(@babel/core@7.29.7)
+ '@react-native/babel-plugin-codegen': 0.86.2(@babel/core@7.29.7)
+ babel-plugin-syntax-hermes-parser: 0.36.0
+ babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.29.7)
+ react-refresh: 0.14.2
+ transitivePeerDependencies:
+ - supports-color
+
'@react-native/codegen@0.86.0(@babel/core@7.29.7)':
dependencies:
'@babel/core': 7.29.7
@@ -11280,6 +14049,16 @@ snapshots:
tinyglobby: 0.2.17
yargs: 17.7.3
+ '@react-native/codegen@0.86.2(@babel/core@7.29.7)':
+ dependencies:
+ '@babel/core': 7.29.7
+ '@babel/parser': 7.29.7
+ hermes-parser: 0.36.0
+ invariant: 2.2.4
+ nullthrows: 1.1.1
+ tinyglobby: 0.2.17
+ yargs: 17.7.3
+
'@react-native/community-cli-plugin@0.86.0(@react-native-community/cli@20.2.0(typescript@5.9.3))':
dependencies:
'@react-native/dev-middleware': 0.86.0
@@ -11296,10 +14075,44 @@ snapshots:
- supports-color
- utf-8-validate
+ '@react-native/community-cli-plugin@0.86.2(@react-native-community/cli@20.1.2(typescript@6.0.3))':
+ dependencies:
+ '@react-native/dev-middleware': 0.86.2
+ debug: 4.4.3
+ invariant: 2.2.4
+ metro: 0.84.4
+ metro-config: 0.84.4
+ metro-core: 0.84.4
+ semver: 7.8.5
+ optionalDependencies:
+ '@react-native-community/cli': 20.1.2(typescript@6.0.3)
+ transitivePeerDependencies:
+ - bufferutil
+ - supports-color
+ - utf-8-validate
+
+ '@react-native/community-cli-plugin@0.86.2(@react-native-community/cli@20.2.0(typescript@5.9.3))':
+ dependencies:
+ '@react-native/dev-middleware': 0.86.2
+ debug: 4.4.3
+ invariant: 2.2.4
+ metro: 0.84.4
+ metro-config: 0.84.4
+ metro-core: 0.84.4
+ semver: 7.8.5
+ optionalDependencies:
+ '@react-native-community/cli': 20.2.0(typescript@5.9.3)
+ transitivePeerDependencies:
+ - bufferutil
+ - supports-color
+ - utf-8-validate
+
'@react-native/debugger-frontend@0.86.0': {}
'@react-native/debugger-frontend@0.86.1': {}
+ '@react-native/debugger-frontend@0.86.2': {}
+
'@react-native/debugger-shell@0.86.0':
dependencies:
cross-spawn: 7.0.6
@@ -11316,6 +14129,14 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@react-native/debugger-shell@0.86.2':
+ dependencies:
+ cross-spawn: 7.0.6
+ debug: 4.4.3
+ fb-dotslash: 0.5.8
+ transitivePeerDependencies:
+ - supports-color
+
'@react-native/dev-middleware@0.86.0':
dependencies:
'@isaacs/ttlcache': 1.4.1
@@ -11354,12 +14175,37 @@ snapshots:
- supports-color
- utf-8-validate
+ '@react-native/dev-middleware@0.86.2':
+ dependencies:
+ '@isaacs/ttlcache': 1.4.1
+ '@react-native/debugger-frontend': 0.86.2
+ '@react-native/debugger-shell': 0.86.2
+ chrome-launcher: 0.15.2
+ chromium-edge-launcher: 0.3.0
+ connect: 3.7.0
+ debug: 4.4.3
+ invariant: 2.2.4
+ nullthrows: 1.1.1
+ open: 7.4.2
+ serve-static: 1.16.3
+ ws: 7.5.13
+ transitivePeerDependencies:
+ - bufferutil
+ - supports-color
+ - utf-8-validate
+
'@react-native/gradle-plugin@0.86.0': {}
+ '@react-native/gradle-plugin@0.86.2': {}
+
'@react-native/js-polyfills@0.86.0': {}
+ '@react-native/js-polyfills@0.86.2': {}
+
'@react-native/normalize-colors@0.86.0': {}
+ '@react-native/normalize-colors@0.86.2': {}
+
'@react-native/typescript-config@0.86.0': {}
'@react-native/virtualized-lists@0.86.0(@types/react@19.2.17)(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)':
@@ -11371,6 +14217,24 @@ snapshots:
optionalDependencies:
'@types/react': 19.2.17
+ '@react-native/virtualized-lists@0.86.2(@types/react@19.2.17)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)':
+ dependencies:
+ invariant: 2.2.4
+ nullthrows: 1.1.1
+ react: 19.2.3
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3)
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@react-native/virtualized-lists@0.86.2(@types/react@19.2.17)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)':
+ dependencies:
+ invariant: 2.2.4
+ nullthrows: 1.1.1
+ react: 19.2.8
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8)
+ optionalDependencies:
+ '@types/react': 19.2.17
+
'@react-navigation/core@7.21.11(react@19.2.8)':
dependencies:
'@react-navigation/routers': 7.6.4
@@ -11383,7 +14247,7 @@ snapshots:
use-latest-callback: 0.2.6(react@19.2.8)
use-sync-external-store: 1.6.0(react@19.2.8)
- '@react-navigation/elements@2.9.36(@react-navigation/native@7.3.14(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native-safe-area-context@5.8.0(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)':
+ '@react-navigation/elements@2.9.36(@react-native-masked-view/masked-view@0.3.2(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(@react-navigation/native@7.3.14(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native-safe-area-context@5.8.0(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)':
dependencies:
'@react-navigation/native': 7.3.14(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
color: 4.2.3
@@ -11392,10 +14256,12 @@ snapshots:
react-native-safe-area-context: 5.8.0(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
use-latest-callback: 0.2.6(react@19.2.8)
use-sync-external-store: 1.6.0(react@19.2.8)
+ optionalDependencies:
+ '@react-native-masked-view/masked-view': 0.3.2(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
- '@react-navigation/native-stack@7.18.6(@react-navigation/native@7.3.14(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native-safe-area-context@5.8.0(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native-screens@4.26.2(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)':
+ '@react-navigation/native-stack@7.18.6(@react-native-masked-view/masked-view@0.3.2(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(@react-navigation/native@7.3.14(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native-safe-area-context@5.8.0(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native-screens@4.26.2(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)':
dependencies:
- '@react-navigation/elements': 2.9.36(@react-navigation/native@7.3.14(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native-safe-area-context@5.8.0(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
+ '@react-navigation/elements': 2.9.36(@react-native-masked-view/masked-view@0.3.2(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(@react-navigation/native@7.3.14(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native-safe-area-context@5.8.0(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
'@react-navigation/native': 7.3.14(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
color: 4.2.3
react: 19.2.8
@@ -12269,6 +15135,17 @@ snapshots:
dependencies:
'@swc/counter': 0.1.3
+ '@testing-library/jest-dom@6.9.1':
+ dependencies:
+ '@adobe/css-tools': 4.5.0
+ aria-query: 5.3.2
+ css.escape: 1.5.1
+ dom-accessibility-api: 0.6.3
+ picocolors: 1.1.1
+ redent: 3.0.0
+
+ '@testing-library/user-event@14.6.1': {}
+
'@turbo/darwin-64@2.10.7':
optional: true
@@ -12362,6 +15239,8 @@ snapshots:
dependencies:
'@types/tinycolor2': 1.4.6
+ '@types/hammerjs@2.0.46': {}
+
'@types/hast@3.0.5':
dependencies:
'@types/unist': 3.0.3
@@ -12418,6 +15297,10 @@ snapshots:
dependencies:
'@types/react': 19.2.17
+ '@types/react-test-renderer@19.1.0':
+ dependencies:
+ '@types/react': 19.2.17
+
'@types/react@18.3.31':
dependencies:
'@types/prop-types': 15.7.15
@@ -12599,6 +15482,8 @@ snapshots:
'@webassemblyjs/ast': 1.14.1
'@xtuc/long': 4.2.2
+ '@xmldom/xmldom@0.8.13': {}
+
'@xmldom/xmldom@0.9.10': {}
'@xtuc/ieee754@1.2.0': {}
@@ -12649,6 +15534,8 @@ snapshots:
agent-base@7.1.4: {}
+ agent-cli-detector@0.1.4: {}
+
ajv-formats@2.1.1:
dependencies:
ajv: 8.20.0
@@ -12716,6 +15603,12 @@ snapshots:
argparse@2.0.1: {}
+ aria-hidden@1.2.6:
+ dependencies:
+ tslib: 2.8.1
+
+ aria-query@5.3.2: {}
+
array-timsort@1.0.3: {}
array-union@2.1.0: {}
@@ -12839,6 +15732,12 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ babel-plugin-react-compiler@1.0.0:
+ dependencies:
+ '@babel/types': 7.29.7
+
+ babel-plugin-react-native-web@0.21.2: {}
+
babel-plugin-syntax-hermes-parser@0.20.1:
dependencies:
hermes-parser: 0.20.1
@@ -12872,6 +15771,110 @@ snapshots:
'@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.29.7)
'@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.29.7)
+ babel-preset-expo@57.0.5(@babel/core@7.29.7)(@babel/runtime@7.29.7)(expo@57.0.9)(react-refresh@0.14.2):
+ dependencies:
+ '@babel/generator': 7.29.7
+ '@babel/helper-module-imports': 7.29.7
+ '@babel/plugin-proposal-decorators': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-proposal-export-default-from': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.29.7)
+ '@babel/plugin-syntax-export-default-from': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.7)
+ '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.7)
+ '@babel/plugin-transform-async-generator-functions': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-async-to-generator': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-block-scoping': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-class-properties': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-class-static-block': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-classes': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-destructuring': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-export-namespace-from': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-flow-strip-types': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-for-of': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-logical-assignment-operators': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-named-capturing-groups-regex': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-nullish-coalescing-operator': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-object-rest-spread': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-optional-catch-binding': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-optional-chaining': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-parameters': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-private-methods': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-private-property-in-object': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-react-display-name': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-react-jsx': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-react-jsx-development': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-react-pure-annotations': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-runtime': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-unicode-regex': 7.29.7(@babel/core@7.29.7)
+ '@babel/preset-typescript': 7.29.7(@babel/core@7.29.7)
+ '@react-native/babel-plugin-codegen': 0.86.2(@babel/core@7.29.7)
+ babel-plugin-react-compiler: 1.0.0
+ babel-plugin-react-native-web: 0.21.2
+ babel-plugin-syntax-hermes-parser: 0.36.0
+ babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.29.7)
+ debug: 4.4.3
+ react-refresh: 0.14.2
+ optionalDependencies:
+ '@babel/runtime': 7.29.7
+ expo: 57.0.9(@babel/core@7.29.7)(@expo/metro-runtime@57.0.8)(expo-router@57.0.9)(react-dom@19.2.8(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(typescript@6.0.3)
+ transitivePeerDependencies:
+ - '@babel/core'
+ - supports-color
+
+ babel-preset-expo@57.0.5(@babel/core@7.29.7)(@babel/runtime@7.29.7)(expo@57.0.9)(react-refresh@0.18.0):
+ dependencies:
+ '@babel/generator': 7.29.7
+ '@babel/helper-module-imports': 7.29.7
+ '@babel/plugin-proposal-decorators': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-proposal-export-default-from': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.29.7)
+ '@babel/plugin-syntax-export-default-from': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.7)
+ '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.7)
+ '@babel/plugin-transform-async-generator-functions': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-async-to-generator': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-block-scoping': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-class-properties': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-class-static-block': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-classes': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-destructuring': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-export-namespace-from': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-flow-strip-types': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-for-of': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-logical-assignment-operators': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-named-capturing-groups-regex': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-nullish-coalescing-operator': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-object-rest-spread': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-optional-catch-binding': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-optional-chaining': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-parameters': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-private-methods': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-private-property-in-object': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-react-display-name': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-react-jsx': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-react-jsx-development': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-react-pure-annotations': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-runtime': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-unicode-regex': 7.29.7(@babel/core@7.29.7)
+ '@babel/preset-typescript': 7.29.7(@babel/core@7.29.7)
+ '@react-native/babel-plugin-codegen': 0.86.2(@babel/core@7.29.7)
+ babel-plugin-react-compiler: 1.0.0
+ babel-plugin-react-native-web: 0.21.2
+ babel-plugin-syntax-hermes-parser: 0.36.0
+ babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.29.7)
+ debug: 4.4.3
+ react-refresh: 0.18.0
+ optionalDependencies:
+ '@babel/runtime': 7.29.7
+ expo: 57.0.9(@babel/core@7.29.7)(@expo/metro-runtime@57.0.8)(expo-router@57.0.9)(react-dom@19.2.8(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(typescript@6.0.3)
+ transitivePeerDependencies:
+ - '@babel/core'
+ - supports-color
+
babel-preset-jest@29.6.3(@babel/core@7.29.7):
dependencies:
'@babel/core': 7.29.7
@@ -12882,6 +15885,8 @@ snapshots:
balanced-match@1.0.2: {}
+ balanced-match@4.0.4: {}
+
base64-js@1.5.1: {}
base64id@2.0.0: {}
@@ -12941,6 +15946,10 @@ snapshots:
dependencies:
balanced-match: 1.0.2
+ brace-expansion@5.0.9:
+ dependencies:
+ balanced-match: 4.0.4
+
braces@3.0.3:
dependencies:
fill-range: 7.1.1
@@ -13011,6 +16020,12 @@ snapshots:
chai@6.2.2: {}
+ chalk@2.4.2:
+ dependencies:
+ ansi-styles: 3.2.1
+ escape-string-regexp: 1.0.5
+ supports-color: 5.5.0
+
chalk@3.0.0:
dependencies:
ansi-styles: 4.3.0
@@ -13084,12 +16099,18 @@ snapshots:
dependencies:
clsx: 2.1.1
+ cli-cursor@2.1.0:
+ dependencies:
+ restore-cursor: 2.0.0
+
cli-cursor@3.1.0:
dependencies:
restore-cursor: 3.1.0
cli-spinners@2.9.2: {}
+ client-only@0.0.1: {}
+
cliui@6.0.0:
dependencies:
string-width: 4.2.3
@@ -13258,6 +16279,15 @@ snapshots:
optionalDependencies:
typescript: 5.9.3
+ cosmiconfig@9.0.2(typescript@6.0.3):
+ dependencies:
+ env-paths: 2.2.1
+ import-fresh: 3.3.1
+ js-yaml: 4.3.0
+ parse-json: 5.2.0
+ optionalDependencies:
+ typescript: 6.0.3
+
create-jest@29.7.0(@types/node@20.19.43):
dependencies:
'@jest/types': 29.6.3
@@ -13308,6 +16338,8 @@ snapshots:
css-what@6.2.2: {}
+ css.escape@1.5.1: {}
+
cssesc@3.0.0: {}
csso@5.0.5:
@@ -13326,6 +16358,10 @@ snapshots:
dependencies:
ms: 2.0.0
+ debug@3.2.7:
+ dependencies:
+ ms: 2.1.3
+
debug@4.3.7:
dependencies:
ms: 2.1.3
@@ -13387,6 +16423,8 @@ snapshots:
detect-newline@3.1.0: {}
+ detect-node-es@1.1.0: {}
+
devlop@1.1.0:
dependencies:
dequal: 2.0.3
@@ -13401,6 +16439,10 @@ snapshots:
dlv@1.1.3: {}
+ dnssd-advertise@1.1.6: {}
+
+ dom-accessibility-api@0.6.3: {}
+
dom-serializer@2.0.0:
dependencies:
domelementtype: 2.3.0
@@ -13597,6 +16639,8 @@ snapshots:
escape-html@1.0.3: {}
+ escape-string-regexp@1.0.5: {}
+
escape-string-regexp@2.0.0: {}
escape-string-regexp@4.0.0: {}
@@ -13622,91 +16666,455 @@ snapshots:
dependencies:
'@types/estree': 1.0.9
- estree-util-build-jsx@3.0.1:
+ estree-util-build-jsx@3.0.1:
+ dependencies:
+ '@types/estree-jsx': 1.0.5
+ devlop: 1.1.0
+ estree-util-is-identifier-name: 3.0.0
+ estree-walker: 3.0.3
+
+ estree-util-is-identifier-name@1.1.0: {}
+
+ estree-util-is-identifier-name@3.0.0: {}
+
+ estree-util-scope@1.0.0:
+ dependencies:
+ '@types/estree': 1.0.9
+ devlop: 1.1.0
+
+ estree-util-to-js@2.0.0:
+ dependencies:
+ '@types/estree-jsx': 1.0.5
+ astring: 1.9.0
+ source-map: 0.7.6
+
+ estree-util-visit@2.0.0:
+ dependencies:
+ '@types/estree-jsx': 1.0.5
+ '@types/unist': 3.0.3
+
+ estree-walker@3.0.3:
+ dependencies:
+ '@types/estree': 1.0.9
+
+ esutils@2.0.3: {}
+
+ etag@1.8.1: {}
+
+ event-target-shim@5.0.1: {}
+
+ eventemitter3@4.0.7: {}
+
+ events@3.3.0: {}
+
+ execa@5.1.1:
+ dependencies:
+ cross-spawn: 7.0.6
+ get-stream: 6.0.1
+ human-signals: 2.1.0
+ is-stream: 2.0.1
+ merge-stream: 2.0.0
+ npm-run-path: 4.0.1
+ onetime: 5.1.2
+ signal-exit: 3.0.7
+ strip-final-newline: 2.0.0
+
+ execa@9.6.1:
+ dependencies:
+ '@sindresorhus/merge-streams': 4.0.0
+ cross-spawn: 7.0.6
+ figures: 6.1.0
+ get-stream: 9.0.1
+ human-signals: 8.0.1
+ is-plain-obj: 4.1.0
+ is-stream: 4.0.1
+ npm-run-path: 6.0.0
+ pretty-ms: 9.3.0
+ signal-exit: 4.1.0
+ strip-final-newline: 4.0.0
+ yoctocolors: 2.1.2
+
+ exit-hook@4.0.0: {}
+
+ exit@0.1.2: {}
+
+ expand-tilde@2.0.2:
+ dependencies:
+ homedir-polyfill: 1.0.3
+
+ expect-type@1.4.0: {}
+
+ expect@29.7.0:
+ dependencies:
+ '@jest/expect-utils': 29.7.0
+ jest-get-type: 29.6.3
+ jest-matcher-utils: 29.7.0
+ jest-message-util: 29.7.0
+ jest-util: 29.7.0
+
+ expo-asset@57.0.8(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(typescript@6.0.3):
+ dependencies:
+ '@expo/image-utils': 0.11.4(typescript@6.0.3)
+ expo: 57.0.9(@babel/core@7.29.7)(@expo/metro-runtime@57.0.8)(expo-router@57.0.9)(react-dom@19.2.8(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(typescript@6.0.3)
+ expo-constants: 57.0.8(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))
+ react: 19.2.3
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3)
+ transitivePeerDependencies:
+ - supports-color
+ - typescript
+
+ expo-asset@57.0.8(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)(typescript@5.9.3):
+ dependencies:
+ '@expo/image-utils': 0.11.4(typescript@5.9.3)
+ expo: 57.0.9(@babel/core@7.29.7)(@expo/metro-runtime@57.0.8)(expo-router@57.0.9)(react-dom@19.2.8(react@19.2.8))(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)(typescript@5.9.3)
+ expo-constants: 57.0.8(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))
+ react: 19.2.8
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8)
+ transitivePeerDependencies:
+ - supports-color
+ - typescript
+
+ expo-constants@57.0.8(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3)):
+ dependencies:
+ '@expo/env': 2.4.2
+ expo: 57.0.9(@babel/core@7.29.7)(@expo/metro-runtime@57.0.8)(expo-router@57.0.9)(react-dom@19.2.8(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(typescript@6.0.3)
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3)
+ transitivePeerDependencies:
+ - supports-color
+
+ expo-constants@57.0.8(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8)):
+ dependencies:
+ '@expo/env': 2.4.2
+ expo: 57.0.9(@babel/core@7.29.7)(@expo/metro-runtime@57.0.8)(expo-router@57.0.9)(react-dom@19.2.8(react@19.2.8))(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)(typescript@5.9.3)
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8)
+ transitivePeerDependencies:
+ - supports-color
+
+ expo-file-system@57.0.1(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3)):
+ dependencies:
+ expo: 57.0.9(@babel/core@7.29.7)(@expo/metro-runtime@57.0.8)(expo-router@57.0.9)(react-dom@19.2.8(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(typescript@6.0.3)
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3)
+
+ expo-file-system@57.0.1(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8)):
+ dependencies:
+ expo: 57.0.9(@babel/core@7.29.7)(@expo/metro-runtime@57.0.8)(expo-router@57.0.9)(react-dom@19.2.8(react@19.2.8))(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)(typescript@5.9.3)
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8)
+
+ expo-font@57.0.1(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3):
+ dependencies:
+ expo: 57.0.9(@babel/core@7.29.7)(@expo/metro-runtime@57.0.8)(expo-router@57.0.9)(react-dom@19.2.8(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(typescript@6.0.3)
+ fontfaceobserver: 2.3.0
+ react: 19.2.3
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3)
+
+ expo-font@57.0.1(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8):
+ dependencies:
+ expo: 57.0.9(@babel/core@7.29.7)(@expo/metro-runtime@57.0.8)(expo-router@57.0.9)(react-dom@19.2.8(react@19.2.8))(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)(typescript@5.9.3)
+ fontfaceobserver: 2.3.0
+ react: 19.2.8
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8)
+
+ expo-glass-effect@57.0.1(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3):
+ dependencies:
+ expo: 57.0.9(@babel/core@7.29.7)(@expo/metro-runtime@57.0.8)(expo-router@57.0.9)(react-dom@19.2.8(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(typescript@6.0.3)
+ react: 19.2.3
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3)
+
+ expo-glass-effect@57.0.1(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8):
+ dependencies:
+ expo: 57.0.9(@babel/core@7.29.7)(@expo/metro-runtime@57.0.8)(expo-router@57.0.9)(react-dom@19.2.8(react@19.2.8))(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)(typescript@5.9.3)
+ react: 19.2.8
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8)
+ optional: true
+
+ expo-keep-awake@57.0.1(expo@57.0.9)(react@19.2.3):
+ dependencies:
+ expo: 57.0.9(@babel/core@7.29.7)(@expo/metro-runtime@57.0.8)(expo-router@57.0.9)(react-dom@19.2.8(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(typescript@6.0.3)
+ react: 19.2.3
+
+ expo-keep-awake@57.0.1(expo@57.0.9)(react@19.2.8):
dependencies:
- '@types/estree-jsx': 1.0.5
- devlop: 1.1.0
- estree-util-is-identifier-name: 3.0.0
- estree-walker: 3.0.3
+ expo: 57.0.9(@babel/core@7.29.7)(@expo/metro-runtime@57.0.8)(expo-router@57.0.9)(react-dom@19.2.8(react@19.2.8))(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)(typescript@5.9.3)
+ react: 19.2.8
- estree-util-is-identifier-name@1.1.0: {}
+ expo-linking@57.0.4(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3):
+ dependencies:
+ expo-constants: 57.0.8(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))
+ invariant: 2.2.4
+ react: 19.2.3
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3)
+ transitivePeerDependencies:
+ - expo
+ - supports-color
- estree-util-is-identifier-name@3.0.0: {}
+ expo-linking@57.0.4(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8):
+ dependencies:
+ expo-constants: 57.0.8(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))
+ invariant: 2.2.4
+ react: 19.2.8
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8)
+ transitivePeerDependencies:
+ - expo
+ - supports-color
+ optional: true
- estree-util-scope@1.0.0:
+ expo-modules-autolinking@57.0.9(typescript@5.9.3):
dependencies:
- '@types/estree': 1.0.9
- devlop: 1.1.0
+ '@expo/require-utils': 57.0.4(typescript@5.9.3)
+ '@expo/spawn-async': 1.8.0
+ chalk: 4.1.2
+ commander: 7.2.0
+ transitivePeerDependencies:
+ - supports-color
+ - typescript
- estree-util-to-js@2.0.0:
+ expo-modules-autolinking@57.0.9(typescript@6.0.3):
dependencies:
- '@types/estree-jsx': 1.0.5
- astring: 1.9.0
- source-map: 0.7.6
+ '@expo/require-utils': 57.0.4(typescript@6.0.3)
+ '@expo/spawn-async': 1.8.0
+ chalk: 4.1.2
+ commander: 7.2.0
+ transitivePeerDependencies:
+ - supports-color
+ - typescript
- estree-util-visit@2.0.0:
+ expo-modules-core@57.0.8(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3):
dependencies:
- '@types/estree-jsx': 1.0.5
- '@types/unist': 3.0.3
+ '@expo/expo-modules-macros-plugin': 0.6.1
+ expo-modules-jsi: 57.0.4(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))
+ invariant: 2.2.4
+ react: 19.2.3
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3)
+ optionalDependencies:
+ react-native-worklets: 0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)
- estree-walker@3.0.3:
+ expo-modules-core@57.0.8(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8):
dependencies:
- '@types/estree': 1.0.9
+ '@expo/expo-modules-macros-plugin': 0.6.1
+ expo-modules-jsi: 57.0.4(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))
+ invariant: 2.2.4
+ react: 19.2.8
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8)
+ optionalDependencies:
+ react-native-worklets: 0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
- esutils@2.0.3: {}
+ expo-modules-jsi@57.0.4(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3)):
+ dependencies:
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3)
- etag@1.8.1: {}
+ expo-modules-jsi@57.0.4(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8)):
+ dependencies:
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8)
- event-target-shim@5.0.1: {}
+ expo-router@57.0.9(9c3e0008a3ce3af43de1b1cd4744d2bf):
+ dependencies:
+ '@expo/log-box': 57.0.2(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
+ '@expo/metro-runtime': 57.0.8(expo@57.0.9)(react-dom@19.2.8(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
+ '@expo/schema-utils': 57.0.2
+ '@expo/ui': 57.0.8(@babel/core@7.29.7)(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(expo@57.0.9)(react-dom@19.2.8(react@19.2.8))(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
+ '@radix-ui/react-slot': 1.3.3(@types/react@19.2.17)(react@19.2.8)
+ '@radix-ui/react-tabs': 1.1.21(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
+ '@react-native-masked-view/masked-view': 0.3.2(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
+ '@testing-library/jest-dom': 6.9.1
+ '@testing-library/user-event': 14.6.1
+ client-only: 0.0.1
+ color: 4.2.3
+ debug: 4.4.3
+ escape-string-regexp: 4.0.0
+ expo: 57.0.9(@babel/core@7.29.7)(@expo/metro-runtime@57.0.8)(expo-router@57.0.9)(react-dom@19.2.8(react@19.2.8))(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)(typescript@5.9.3)
+ expo-constants: 57.0.8(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))
+ expo-glass-effect: 57.0.1(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
+ expo-linking: 57.0.4(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
+ expo-server: 57.0.1
+ expo-symbols: 57.0.1(expo-font@57.0.1)(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
+ fast-deep-equal: 3.1.3
+ invariant: 2.2.4
+ nanoid: 3.3.16
+ query-string: 7.1.3
+ react: 19.2.8
+ react-fast-compare: 3.2.2
+ react-is: 19.2.8
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8)
+ react-native-drawer-layout: 4.2.9(react-native-gesture-handler@2.32.0(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native-reanimated@4.5.3(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
+ react-native-safe-area-context: 5.8.0(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
+ react-native-screens: 4.26.2(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
+ server-only: 0.0.1
+ sf-symbols-typescript: 2.2.0
+ shallowequal: 1.1.0
+ standard-navigation: 0.0.5
+ vaul: 1.1.2(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
+ optionalDependencies:
+ react-dom: 19.2.8(react@19.2.8)
+ react-native-gesture-handler: 2.32.0(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
+ react-native-reanimated: 4.5.3(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
+ transitivePeerDependencies:
+ - '@babel/core'
+ - '@testing-library/dom'
+ - '@types/react'
+ - '@types/react-dom'
+ - expo-font
+ - react-native-worklets
+ - supports-color
+ optional: true
- eventemitter3@4.0.7: {}
+ expo-router@57.0.9(a32405737b9d1025ea0eea533a2e6749):
+ dependencies:
+ '@expo/log-box': 57.0.2(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)
+ '@expo/metro-runtime': 57.0.8(expo@57.0.9)(react-dom@19.2.8(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)
+ '@expo/schema-utils': 57.0.2
+ '@expo/ui': 57.0.8(@babel/core@7.29.7)(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(expo@57.0.9)(react-dom@19.2.8(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)
+ '@radix-ui/react-slot': 1.3.3(@types/react@19.2.17)(react@19.2.3)
+ '@radix-ui/react-tabs': 1.1.21(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.3))(react@19.2.3)
+ '@react-native-masked-view/masked-view': 0.3.2(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)
+ '@testing-library/jest-dom': 6.9.1
+ '@testing-library/user-event': 14.6.1
+ client-only: 0.0.1
+ color: 4.2.3
+ debug: 4.4.3
+ escape-string-regexp: 4.0.0
+ expo: 57.0.9(@babel/core@7.29.7)(@expo/metro-runtime@57.0.8)(expo-router@57.0.9)(react-dom@19.2.8(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(typescript@6.0.3)
+ expo-constants: 57.0.8(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))
+ expo-glass-effect: 57.0.1(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)
+ expo-linking: 57.0.4(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)
+ expo-server: 57.0.1
+ expo-symbols: 57.0.1(expo-font@57.0.1)(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)
+ fast-deep-equal: 3.1.3
+ invariant: 2.2.4
+ nanoid: 3.3.16
+ query-string: 7.1.3
+ react: 19.2.3
+ react-fast-compare: 3.2.2
+ react-is: 19.2.8
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3)
+ react-native-drawer-layout: 4.2.9(react-native-gesture-handler@2.32.0(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(react-native-reanimated@4.5.1(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)
+ react-native-safe-area-context: 5.7.0(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)
+ react-native-screens: 4.26.2(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)
+ server-only: 0.0.1
+ sf-symbols-typescript: 2.2.0
+ shallowequal: 1.1.0
+ standard-navigation: 0.0.5
+ vaul: 1.1.2(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.3))(react@19.2.3)
+ optionalDependencies:
+ react-dom: 19.2.8(react@19.2.3)
+ react-native-gesture-handler: 2.32.0(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)
+ react-native-reanimated: 4.5.1(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)
+ transitivePeerDependencies:
+ - '@babel/core'
+ - '@testing-library/dom'
+ - '@types/react'
+ - '@types/react-dom'
+ - expo-font
+ - react-native-worklets
+ - supports-color
- events@3.3.0: {}
+ expo-server@57.0.1: {}
- execa@5.1.1:
+ expo-status-bar@57.0.1(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3):
dependencies:
- cross-spawn: 7.0.6
- get-stream: 6.0.1
- human-signals: 2.1.0
- is-stream: 2.0.1
- merge-stream: 2.0.0
- npm-run-path: 4.0.1
- onetime: 5.1.2
- signal-exit: 3.0.7
- strip-final-newline: 2.0.0
+ expo: 57.0.9(@babel/core@7.29.7)(@expo/metro-runtime@57.0.8)(expo-router@57.0.9)(react-dom@19.2.8(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(typescript@6.0.3)
+ react: 19.2.3
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3)
- execa@9.6.1:
+ expo-symbols@57.0.1(expo-font@57.0.1)(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3):
dependencies:
- '@sindresorhus/merge-streams': 4.0.0
- cross-spawn: 7.0.6
- figures: 6.1.0
- get-stream: 9.0.1
- human-signals: 8.0.1
- is-plain-obj: 4.1.0
- is-stream: 4.0.1
- npm-run-path: 6.0.0
- pretty-ms: 9.3.0
- signal-exit: 4.1.0
- strip-final-newline: 4.0.0
- yoctocolors: 2.1.2
-
- exit-hook@4.0.0: {}
-
- exit@0.1.2: {}
+ '@expo-google-fonts/material-symbols': 0.4.42
+ expo: 57.0.9(@babel/core@7.29.7)(@expo/metro-runtime@57.0.8)(expo-router@57.0.9)(react-dom@19.2.8(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(typescript@6.0.3)
+ expo-font: 57.0.1(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)
+ react: 19.2.3
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3)
+ sf-symbols-typescript: 2.2.0
- expand-tilde@2.0.2:
+ expo-symbols@57.0.1(expo-font@57.0.1)(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8):
dependencies:
- homedir-polyfill: 1.0.3
+ '@expo-google-fonts/material-symbols': 0.4.42
+ expo: 57.0.9(@babel/core@7.29.7)(@expo/metro-runtime@57.0.8)(expo-router@57.0.9)(react-dom@19.2.8(react@19.2.8))(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)(typescript@5.9.3)
+ expo-font: 57.0.1(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
+ react: 19.2.8
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8)
+ sf-symbols-typescript: 2.2.0
+ optional: true
- expect-type@1.4.0: {}
+ expo@57.0.9(@babel/core@7.29.7)(@expo/metro-runtime@57.0.8)(expo-router@57.0.9)(react-dom@19.2.8(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(typescript@6.0.3):
+ dependencies:
+ '@babel/runtime': 7.29.7
+ '@expo/cli': 57.0.11(@expo/metro-runtime@57.0.8)(expo-constants@57.0.8)(expo-font@57.0.1)(expo-router@57.0.9)(expo@57.0.9)(react-dom@19.2.8(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(typescript@6.0.3)
+ '@expo/config': 57.0.6(typescript@6.0.3)
+ '@expo/config-plugins': 57.0.6(typescript@6.0.3)
+ '@expo/devtools': 57.0.1(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)
+ '@expo/dom-webview': 57.0.1(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)
+ '@expo/fingerprint': 0.20.6
+ '@expo/local-build-cache-provider': 57.0.5(typescript@6.0.3)
+ '@expo/log-box': 57.0.2(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)
+ '@expo/metro': 56.0.0
+ '@expo/metro-config': 57.0.7(expo@57.0.9)(typescript@6.0.3)
+ '@ungap/structured-clone': 1.3.3
+ babel-preset-expo: 57.0.5(@babel/core@7.29.7)(@babel/runtime@7.29.7)(expo@57.0.9)(react-refresh@0.14.2)
+ expo-asset: 57.0.8(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(typescript@6.0.3)
+ expo-constants: 57.0.8(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))
+ expo-file-system: 57.0.1(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))
+ expo-font: 57.0.1(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)
+ expo-keep-awake: 57.0.1(expo@57.0.9)(react@19.2.3)
+ expo-modules-autolinking: 57.0.9(typescript@6.0.3)
+ expo-modules-core: 57.0.8(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)
+ pretty-format: 29.7.0
+ react: 19.2.3
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3)
+ react-refresh: 0.14.2
+ whatwg-url-minimum: 0.1.2
+ optionalDependencies:
+ '@expo/metro-runtime': 57.0.8(expo@57.0.9)(react-dom@19.2.8(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)
+ react-dom: 19.2.8(react@19.2.3)
+ transitivePeerDependencies:
+ - '@babel/core'
+ - bufferutil
+ - expo-router
+ - expo-widgets
+ - react-native-worklets
+ - react-server-dom-webpack
+ - supports-color
+ - typescript
+ - utf-8-validate
- expect@29.7.0:
+ expo@57.0.9(@babel/core@7.29.7)(@expo/metro-runtime@57.0.8)(expo-router@57.0.9)(react-dom@19.2.8(react@19.2.8))(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)(typescript@5.9.3):
dependencies:
- '@jest/expect-utils': 29.7.0
- jest-get-type: 29.6.3
- jest-matcher-utils: 29.7.0
- jest-message-util: 29.7.0
- jest-util: 29.7.0
+ '@babel/runtime': 7.29.7
+ '@expo/cli': 57.0.11(@expo/metro-runtime@57.0.8)(expo-constants@57.0.8)(expo-font@57.0.1)(expo-router@57.0.9)(expo@57.0.9)(react-dom@19.2.8(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)(typescript@5.9.3)
+ '@expo/config': 57.0.6(typescript@5.9.3)
+ '@expo/config-plugins': 57.0.6(typescript@5.9.3)
+ '@expo/devtools': 57.0.1(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
+ '@expo/dom-webview': 57.0.1(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
+ '@expo/fingerprint': 0.20.6
+ '@expo/local-build-cache-provider': 57.0.5(typescript@5.9.3)
+ '@expo/log-box': 57.0.2(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
+ '@expo/metro': 56.0.0
+ '@expo/metro-config': 57.0.7(expo@57.0.9)(typescript@5.9.3)
+ '@ungap/structured-clone': 1.3.3
+ babel-preset-expo: 57.0.5(@babel/core@7.29.7)(@babel/runtime@7.29.7)(expo@57.0.9)(react-refresh@0.14.2)
+ expo-asset: 57.0.8(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)(typescript@5.9.3)
+ expo-constants: 57.0.8(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))
+ expo-file-system: 57.0.1(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))
+ expo-font: 57.0.1(expo@57.0.9)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
+ expo-keep-awake: 57.0.1(expo@57.0.9)(react@19.2.8)
+ expo-modules-autolinking: 57.0.9(typescript@5.9.3)
+ expo-modules-core: 57.0.8(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
+ pretty-format: 29.7.0
+ react: 19.2.8
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8)
+ react-refresh: 0.14.2
+ whatwg-url-minimum: 0.1.2
+ optionalDependencies:
+ '@expo/metro-runtime': 57.0.8(expo@57.0.9)(react-dom@19.2.8(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
+ react-dom: 19.2.8(react@19.2.8)
+ transitivePeerDependencies:
+ - '@babel/core'
+ - bufferutil
+ - expo-router
+ - expo-widgets
+ - react-native-worklets
+ - react-server-dom-webpack
+ - supports-color
+ - typescript
+ - utf-8-validate
exponential-backoff@3.1.3: {}
@@ -13803,6 +17211,8 @@ snapshots:
optionalDependencies:
picomatch: 4.0.5
+ fetch-nodeshim@0.4.10: {}
+
figures@6.1.0:
dependencies:
is-unicode-supported: 2.1.0
@@ -13894,6 +17304,8 @@ snapshots:
optionalDependencies:
debug: 4.4.3
+ fontfaceobserver@2.3.0: {}
+
form-data@4.0.6:
dependencies:
asynckit: 0.4.0
@@ -13961,6 +17373,8 @@ snapshots:
hasown: 2.0.4
math-intrinsics: 1.1.0
+ get-nonce@1.0.1: {}
+
get-package-type@0.1.0: {}
get-port@5.1.1: {}
@@ -13979,6 +17393,8 @@ snapshots:
'@sec-ant/readable-stream': 0.4.1
is-stream: 4.0.1
+ getenv@2.0.0: {}
+
github-slugger@2.0.0: {}
glob-parent@5.1.2:
@@ -13995,6 +17411,12 @@ snapshots:
glob-to-regexp@0.4.1: {}
+ glob@13.0.6:
+ dependencies:
+ minimatch: 10.2.6
+ minipass: 7.1.3
+ path-scurry: 2.0.2
+
glob@7.2.3:
dependencies:
fs.realpath: 1.0.0
@@ -14063,6 +17485,8 @@ snapshots:
section-matter: 1.0.0
strip-bom-string: 1.0.0
+ has-flag@3.0.0: {}
+
has-flag@4.0.0: {}
has-symbols@1.1.0: {}
@@ -14199,6 +17623,8 @@ snapshots:
hermes-compiler@250829098.0.14: {}
+ hermes-compiler@250829098.0.16: {}
+
hermes-estree@0.20.1: {}
hermes-estree@0.35.0: {}
@@ -14223,12 +17649,20 @@ snapshots:
dependencies:
hermes-estree: 0.36.1
+ hoist-non-react-statics@3.3.2:
+ dependencies:
+ react-is: 16.13.1
+
homedir-polyfill@1.0.3:
dependencies:
parse-passwd: 1.0.0
hookable@6.1.1: {}
+ hosted-git-info@7.0.2:
+ dependencies:
+ lru-cache: 10.4.3
+
html-encoding-sniffer@3.0.0:
dependencies:
whatwg-encoding: 2.0.0
@@ -14359,6 +17793,8 @@ snapshots:
imurmurhash@0.1.4: {}
+ indent-string@4.0.0: {}
+
inflight@1.0.6:
dependencies:
once: 1.4.0
@@ -14799,7 +18235,7 @@ snapshots:
'@babel/core': 7.29.7
'@babel/generator': 7.29.7
'@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-syntax-typescript': 7.25.4(@babel/core@7.29.7)
'@babel/types': 7.29.7
'@jest/expect-utils': 29.7.0
'@jest/transform': 29.7.0
@@ -14873,6 +18309,8 @@ snapshots:
- supports-color
- ts-node
+ jimp-compact@0.16.1: {}
+
jiti@1.21.7: {}
jiti@2.4.2: {}
@@ -15016,6 +18454,8 @@ snapshots:
type-is: 2.1.0
vary: 1.1.2
+ lan-network@0.2.1: {}
+
launch-editor@2.14.1:
dependencies:
picocolors: 1.1.1
@@ -15188,6 +18628,10 @@ snapshots:
lodash@4.18.1: {}
+ log-symbols@2.2.0:
+ dependencies:
+ chalk: 2.4.2
+
log-symbols@4.1.0:
dependencies:
chalk: 4.1.2
@@ -15223,6 +18667,8 @@ snapshots:
lru-cache@10.4.3: {}
+ lru-cache@11.5.2: {}
+
lru-cache@5.1.1:
dependencies:
yallist: 3.1.1
@@ -15911,8 +19357,16 @@ snapshots:
mime@2.6.0: {}
+ mimic-fn@1.2.0: {}
+
mimic-fn@2.1.0: {}
+ min-indent@1.0.1: {}
+
+ minimatch@10.2.6:
+ dependencies:
+ brace-expansion: 5.0.9
+
minimatch@3.1.5:
dependencies:
brace-expansion: 1.1.16
@@ -15935,6 +19389,8 @@ snapshots:
ms@2.1.3: {}
+ multitars@1.0.0: {}
+
mz@2.7.0:
dependencies:
any-promise: 1.3.0
@@ -15957,6 +19413,20 @@ snapshots:
- react-native-svg
- supports-color
+ nativewind@4.2.6(react-native-reanimated@4.5.3(react-native-worklets@0.11.3(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)(tailwindcss@3.4.19(yaml@2.9.0)):
+ dependencies:
+ comment-json: 4.6.2
+ debug: 4.4.3
+ react-native-css-interop: 0.2.6(react-native-reanimated@4.5.3(react-native-worklets@0.11.3(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)(tailwindcss@3.4.19(yaml@2.9.0))
+ tailwindcss: 3.4.19(yaml@2.9.0)
+ transitivePeerDependencies:
+ - react
+ - react-native
+ - react-native-reanimated
+ - react-native-safe-area-context
+ - react-native-svg
+ - supports-color
+
natural-compare@1.4.0: {}
negotiator@0.6.3: {}
@@ -15978,6 +19448,8 @@ snapshots:
dependencies:
whatwg-url: 5.0.0
+ node-forge@1.4.0: {}
+
node-int64@0.4.0: {}
node-modules-regexp@1.0.0: {}
@@ -15994,6 +19466,13 @@ snapshots:
normalize-path@3.0.0: {}
+ npm-package-arg@11.0.3:
+ dependencies:
+ hosted-git-info: 7.0.2
+ proc-log: 4.2.0
+ semver: 7.8.5
+ validate-npm-package-name: 5.0.1
+
npm-run-path@4.0.1:
dependencies:
path-key: 3.1.1
@@ -16039,6 +19518,10 @@ snapshots:
dependencies:
wrappy: 1.0.2
+ onetime@2.0.1:
+ dependencies:
+ mimic-fn: 1.2.0
+
onetime@5.1.2:
dependencies:
mimic-fn: 2.1.0
@@ -16071,6 +19554,15 @@ snapshots:
opener@1.5.2: {}
+ ora@3.4.0:
+ dependencies:
+ chalk: 2.4.2
+ cli-cursor: 2.1.0
+ cli-spinners: 2.9.2
+ log-symbols: 2.2.0
+ strip-ansi: 5.2.0
+ wcwidth: 1.0.1
+
ora@5.4.1:
dependencies:
bl: 4.1.0
@@ -16150,6 +19642,10 @@ snapshots:
parse-passwd@1.0.0: {}
+ parse-png@2.1.0:
+ dependencies:
+ pngjs: 3.4.0
+
parse5@7.3.0:
dependencies:
entities: 6.0.1
@@ -16179,6 +19675,11 @@ snapshots:
lru-cache: 10.4.3
minipass: 7.1.3
+ path-scurry@2.0.2:
+ dependencies:
+ lru-cache: 11.5.2
+ minipass: 7.1.3
+
path-to-regexp@8.4.2: {}
path-type@4.0.0: {}
@@ -16239,6 +19740,8 @@ snapshots:
base64-js: 1.5.1
xmlbuilder: 15.1.1
+ pngjs@3.4.0: {}
+
portfinder@1.0.38:
dependencies:
async: 3.2.6
@@ -16328,10 +19831,14 @@ snapshots:
dependencies:
parse-ms: 4.0.0
+ proc-log@4.2.0: {}
+
process-warning@4.0.1: {}
process-warning@5.0.0: {}
+ progress@2.0.3: {}
+
promise@8.3.0:
dependencies:
asap: 2.0.6
@@ -16388,15 +19895,28 @@ snapshots:
- bufferutil
- utf-8-validate
+ react-dom@19.2.8(react@19.2.3):
+ dependencies:
+ react: 19.2.3
+ scheduler: 0.27.0
+
react-dom@19.2.8(react@19.2.8):
dependencies:
react: 19.2.8
scheduler: 0.27.0
+ react-fast-compare@3.2.2: {}
+
+ react-freeze@1.0.4(react@19.2.3):
+ dependencies:
+ react: 19.2.3
+
react-freeze@1.0.4(react@19.2.8):
dependencies:
react: 19.2.8
+ react-is@16.13.1: {}
+
react-is@17.0.2: {}
react-is@18.3.1: {}
@@ -16405,7 +19925,7 @@ snapshots:
react-lazy-with-preload@2.2.1: {}
- react-native-css-interop@0.1.22(react-native-reanimated@4.5.3(react-native-worklets@0.11.3(@babel/core@7.29.7)(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)(tailwindcss@3.4.19(yaml@2.9.0)):
+ react-native-css-interop@0.1.22(react-native-reanimated@4.5.3(react-native-worklets@0.11.3(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)(tailwindcss@3.4.19(yaml@2.9.0)):
dependencies:
'@babel/helper-module-imports': 7.29.7
'@babel/traverse': 7.29.7
@@ -16413,8 +19933,8 @@ snapshots:
debug: 4.4.3
lightningcss: 1.33.0
react: 19.2.8
- react-native: 0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8)
- react-native-reanimated: 4.5.3(react-native-worklets@0.11.3(@babel/core@7.29.7)(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8)
+ react-native-reanimated: 4.5.3(react-native-worklets@0.11.3(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
semver: 7.8.5
tailwindcss: 3.4.19(yaml@2.9.0)
transitivePeerDependencies:
@@ -16438,11 +19958,91 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ react-native-css-interop@0.2.6(react-native-reanimated@4.5.3(react-native-worklets@0.11.3(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)(tailwindcss@3.4.19(yaml@2.9.0)):
+ dependencies:
+ '@babel/helper-module-imports': 7.29.7
+ '@babel/traverse': 7.29.7
+ '@babel/types': 7.29.7
+ debug: 4.4.3
+ lightningcss: 1.27.0
+ react: 19.2.8
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8)
+ react-native-reanimated: 4.5.3(react-native-worklets@0.11.3(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
+ semver: 7.8.5
+ tailwindcss: 3.4.19(yaml@2.9.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ react-native-drawer-layout@4.2.9(react-native-gesture-handler@2.32.0(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(react-native-reanimated@4.5.1(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3):
+ dependencies:
+ color: 4.2.3
+ react: 19.2.3
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3)
+ react-native-gesture-handler: 2.32.0(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)
+ react-native-reanimated: 4.5.1(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)
+ use-latest-callback: 0.2.6(react@19.2.3)
+
+ react-native-drawer-layout@4.2.9(react-native-gesture-handler@2.32.0(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native-reanimated@4.5.3(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8):
+ dependencies:
+ color: 4.2.3
+ react: 19.2.8
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8)
+ react-native-gesture-handler: 2.32.0(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
+ react-native-reanimated: 4.5.3(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
+ use-latest-callback: 0.2.6(react@19.2.8)
+ optional: true
+
+ react-native-gesture-handler@2.32.0(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3):
+ dependencies:
+ '@egjs/hammerjs': 2.0.17
+ '@types/react-test-renderer': 19.1.0
+ hoist-non-react-statics: 3.3.2
+ invariant: 2.2.4
+ react: 19.2.3
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3)
+
+ react-native-gesture-handler@2.32.0(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8):
+ dependencies:
+ '@egjs/hammerjs': 2.0.17
+ '@types/react-test-renderer': 19.1.0
+ hoist-non-react-statics: 3.3.2
+ invariant: 2.2.4
+ react: 19.2.8
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8)
+ optional: true
+
react-native-is-edge-to-edge@1.3.1(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8):
dependencies:
react: 19.2.8
react-native: 0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8)
+ react-native-is-edge-to-edge@1.3.1(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3):
+ dependencies:
+ react: 19.2.3
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3)
+
+ react-native-is-edge-to-edge@1.3.1(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8):
+ dependencies:
+ react: 19.2.8
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8)
+
+ react-native-reanimated@4.5.1(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3):
+ dependencies:
+ react: 19.2.3
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3)
+ react-native-is-edge-to-edge: 1.3.1(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)
+ react-native-worklets: 0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)
+ semver: 7.8.5
+
+ react-native-reanimated@4.5.3(react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8):
+ dependencies:
+ react: 19.2.8
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8)
+ react-native-is-edge-to-edge: 1.3.1(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
+ react-native-worklets: 0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
+ semver: 7.8.5
+ optional: true
+
react-native-reanimated@4.5.3(react-native-worklets@0.11.3(@babel/core@7.29.7)(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8):
dependencies:
react: 19.2.8
@@ -16451,11 +20051,30 @@ snapshots:
react-native-worklets: 0.11.3(@babel/core@7.29.7)(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
semver: 7.8.5
+ react-native-reanimated@4.5.3(react-native-worklets@0.11.3(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8))(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8):
+ dependencies:
+ react: 19.2.8
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8)
+ react-native-is-edge-to-edge: 1.3.1(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
+ react-native-worklets: 0.11.3(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
+ semver: 7.8.5
+
+ react-native-safe-area-context@5.7.0(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3):
+ dependencies:
+ react: 19.2.3
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3)
+
react-native-safe-area-context@5.8.0(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8):
dependencies:
react: 19.2.8
react-native: 0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8)
+ react-native-safe-area-context@5.8.0(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8):
+ dependencies:
+ react: 19.2.8
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8)
+ optional: true
+
react-native-screens@4.26.2(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8):
dependencies:
react: 19.2.8
@@ -16463,6 +20082,21 @@ snapshots:
react-native: 0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8)
warn-once: 0.1.1
+ react-native-screens@4.26.2(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3):
+ dependencies:
+ react: 19.2.3
+ react-freeze: 1.0.4(react@19.2.3)
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3)
+ warn-once: 0.1.1
+
+ react-native-screens@4.26.2(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8):
+ dependencies:
+ react: 19.2.8
+ react-freeze: 1.0.4(react@19.2.8)
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8)
+ warn-once: 0.1.1
+ optional: true
+
react-native-svg@15.15.5(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8):
dependencies:
css-select: 5.2.2
@@ -16470,7 +20104,7 @@ snapshots:
react: 19.2.8
react-native: 0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8)
- react-native-test-app@5.4.5(@react-native-community/cli-types@20.2.0)(memfs@4.64.0)(metro@0.84.4)(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8):
+ react-native-test-app@5.4.5(@expo/config-plugins@57.0.6(typescript@5.9.3))(@react-native-community/cli-types@20.2.0)(memfs@4.64.0)(metro@0.84.4)(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8):
dependencies:
'@isaacs/cliui': 9.0.0
'@rnx-kit/react-native-host': 0.5.21(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))
@@ -16482,11 +20116,58 @@ snapshots:
react: 19.2.8
react-native: 0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8)
semver: 7.8.5
+ optionalDependencies:
+ '@expo/config-plugins': 57.0.6(typescript@5.9.3)
transitivePeerDependencies:
- '@react-native-community/cli-types'
- memfs
- metro
+ react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3):
+ dependencies:
+ '@babel/core': 7.29.7
+ '@babel/generator': 7.29.7
+ '@babel/plugin-transform-arrow-functions': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-class-properties': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-classes': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-nullish-coalescing-operator': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-optional-chaining': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-shorthand-properties': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-template-literals': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-unicode-regex': 7.29.7(@babel/core@7.29.7)
+ '@babel/preset-typescript': 7.29.7(@babel/core@7.29.7)
+ '@babel/traverse': 7.29.7
+ '@babel/types': 7.29.7
+ convert-source-map: 2.0.0
+ react: 19.2.3
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3)
+ semver: 7.8.5
+ transitivePeerDependencies:
+ - supports-color
+
+ react-native-worklets@0.10.1(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8):
+ dependencies:
+ '@babel/core': 7.29.7
+ '@babel/generator': 7.29.7
+ '@babel/plugin-transform-arrow-functions': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-class-properties': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-classes': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-nullish-coalescing-operator': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-optional-chaining': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-shorthand-properties': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-template-literals': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-unicode-regex': 7.29.7(@babel/core@7.29.7)
+ '@babel/preset-typescript': 7.29.7(@babel/core@7.29.7)
+ '@babel/traverse': 7.29.7
+ '@babel/types': 7.29.7
+ convert-source-map: 2.0.0
+ react: 19.2.8
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8)
+ semver: 7.8.5
+ transitivePeerDependencies:
+ - supports-color
+ optional: true
+
react-native-worklets@0.11.3(@babel/core@7.29.7)(react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8):
dependencies:
'@babel/core': 7.29.7
@@ -16509,6 +20190,28 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ react-native-worklets@0.11.3(@babel/core@7.29.7)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8):
+ dependencies:
+ '@babel/core': 7.29.7
+ '@babel/generator': 7.29.7
+ '@babel/plugin-transform-arrow-functions': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-class-properties': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-classes': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-nullish-coalescing-operator': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-optional-chaining': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-shorthand-properties': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-template-literals': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-unicode-regex': 7.29.7(@babel/core@7.29.7)
+ '@babel/preset-typescript': 7.29.7(@babel/core@7.29.7)
+ '@babel/traverse': 7.29.7
+ '@babel/types': 7.29.7
+ convert-source-map: 2.0.0
+ react: 19.2.8
+ react-native: 0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8)
+ semver: 7.8.5
+ transitivePeerDependencies:
+ - supports-color
+
react-native@0.86.0(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8):
dependencies:
'@react-native/assets-registry': 0.86.0
@@ -16554,6 +20257,96 @@ snapshots:
- supports-color
- utf-8-validate
+ react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3):
+ dependencies:
+ '@react-native/assets-registry': 0.86.2
+ '@react-native/codegen': 0.86.2(@babel/core@7.29.7)
+ '@react-native/community-cli-plugin': 0.86.2(@react-native-community/cli@20.1.2(typescript@6.0.3))
+ '@react-native/gradle-plugin': 0.86.2
+ '@react-native/js-polyfills': 0.86.2
+ '@react-native/normalize-colors': 0.86.2
+ '@react-native/virtualized-lists': 0.86.2(@types/react@19.2.17)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.1.2(typescript@6.0.3))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)
+ abort-controller: 3.0.0
+ anser: 1.4.10
+ ansi-regex: 5.0.1
+ babel-plugin-syntax-hermes-parser: 0.36.0
+ base64-js: 1.5.1
+ commander: 12.1.0
+ flow-enums-runtime: 0.0.6
+ hermes-compiler: 250829098.0.16
+ invariant: 2.2.4
+ memoize-one: 5.2.1
+ metro-runtime: 0.84.4
+ metro-source-map: 0.84.4
+ nullthrows: 1.1.1
+ pretty-format: 29.7.0
+ promise: 8.3.0
+ react: 19.2.3
+ react-devtools-core: 6.1.5
+ react-refresh: 0.14.2
+ regenerator-runtime: 0.13.11
+ scheduler: 0.27.0
+ semver: 7.8.5
+ stacktrace-parser: 0.1.11
+ tinyglobby: 0.2.17
+ whatwg-fetch: 3.6.20
+ ws: 7.5.13
+ yargs: 17.7.3
+ optionalDependencies:
+ '@types/react': 19.2.17
+ transitivePeerDependencies:
+ - '@babel/core'
+ - '@react-native-community/cli'
+ - '@react-native/metro-config'
+ - bufferutil
+ - supports-color
+ - utf-8-validate
+
+ react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8):
+ dependencies:
+ '@react-native/assets-registry': 0.86.2
+ '@react-native/codegen': 0.86.2(@babel/core@7.29.7)
+ '@react-native/community-cli-plugin': 0.86.2(@react-native-community/cli@20.2.0(typescript@5.9.3))
+ '@react-native/gradle-plugin': 0.86.2
+ '@react-native/js-polyfills': 0.86.2
+ '@react-native/normalize-colors': 0.86.2
+ '@react-native/virtualized-lists': 0.86.2(@types/react@19.2.17)(react-native@0.86.2(@babel/core@7.29.7)(@react-native-community/cli@20.2.0(typescript@5.9.3))(@types/react@19.2.17)(react@19.2.8))(react@19.2.8)
+ abort-controller: 3.0.0
+ anser: 1.4.10
+ ansi-regex: 5.0.1
+ babel-plugin-syntax-hermes-parser: 0.36.0
+ base64-js: 1.5.1
+ commander: 12.1.0
+ flow-enums-runtime: 0.0.6
+ hermes-compiler: 250829098.0.16
+ invariant: 2.2.4
+ memoize-one: 5.2.1
+ metro-runtime: 0.84.4
+ metro-source-map: 0.84.4
+ nullthrows: 1.1.1
+ pretty-format: 29.7.0
+ promise: 8.3.0
+ react: 19.2.8
+ react-devtools-core: 6.1.5
+ react-refresh: 0.14.2
+ regenerator-runtime: 0.13.11
+ scheduler: 0.27.0
+ semver: 7.8.5
+ stacktrace-parser: 0.1.11
+ tinyglobby: 0.2.17
+ whatwg-fetch: 3.6.20
+ ws: 7.5.13
+ yargs: 17.7.3
+ optionalDependencies:
+ '@types/react': 19.2.17
+ transitivePeerDependencies:
+ - '@babel/core'
+ - '@react-native-community/cli'
+ - '@react-native/metro-config'
+ - bufferutil
+ - supports-color
+ - utf-8-validate
+
react-reconciler@0.33.0(react@19.2.8):
dependencies:
react: 19.2.8
@@ -16563,6 +20356,46 @@ snapshots:
react-refresh@0.18.0: {}
+ react-remove-scroll-bar@2.3.8(@types/react@19.2.17)(react@19.2.3):
+ dependencies:
+ react: 19.2.3
+ react-style-singleton: 2.2.3(@types/react@19.2.17)(react@19.2.3)
+ tslib: 2.8.1
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ react-remove-scroll-bar@2.3.8(@types/react@19.2.17)(react@19.2.8):
+ dependencies:
+ react: 19.2.8
+ react-style-singleton: 2.2.3(@types/react@19.2.17)(react@19.2.8)
+ tslib: 2.8.1
+ optionalDependencies:
+ '@types/react': 19.2.17
+ optional: true
+
+ react-remove-scroll@2.7.2(@types/react@19.2.17)(react@19.2.3):
+ dependencies:
+ react: 19.2.3
+ react-remove-scroll-bar: 2.3.8(@types/react@19.2.17)(react@19.2.3)
+ react-style-singleton: 2.2.3(@types/react@19.2.17)(react@19.2.3)
+ tslib: 2.8.1
+ use-callback-ref: 1.3.3(@types/react@19.2.17)(react@19.2.3)
+ use-sidecar: 1.1.3(@types/react@19.2.17)(react@19.2.3)
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ react-remove-scroll@2.7.2(@types/react@19.2.17)(react@19.2.8):
+ dependencies:
+ react: 19.2.8
+ react-remove-scroll-bar: 2.3.8(@types/react@19.2.17)(react@19.2.8)
+ react-style-singleton: 2.2.3(@types/react@19.2.17)(react@19.2.8)
+ tslib: 2.8.1
+ use-callback-ref: 1.3.3(@types/react@19.2.17)(react@19.2.8)
+ use-sidecar: 1.1.3(@types/react@19.2.17)(react@19.2.8)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ optional: true
+
react-router-dom@7.18.1(react-dom@19.2.8(react@19.2.8))(react@19.2.8):
dependencies:
react: 19.2.8
@@ -16577,6 +20410,25 @@ snapshots:
optionalDependencies:
react-dom: 19.2.8(react@19.2.8)
+ react-style-singleton@2.2.3(@types/react@19.2.17)(react@19.2.3):
+ dependencies:
+ get-nonce: 1.0.1
+ react: 19.2.3
+ tslib: 2.8.1
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ react-style-singleton@2.2.3(@types/react@19.2.17)(react@19.2.8):
+ dependencies:
+ get-nonce: 1.0.1
+ react: 19.2.8
+ tslib: 2.8.1
+ optionalDependencies:
+ '@types/react': 19.2.17
+ optional: true
+
+ react@19.2.3: {}
+
react@19.2.8: {}
read-cache@1.0.0:
@@ -16633,6 +20485,11 @@ snapshots:
unified: 11.0.5
vfile: 6.0.3
+ redent@3.0.0:
+ dependencies:
+ indent-string: 4.0.0
+ strip-indent: 3.0.0
+
regenerate-unicode-properties@10.2.2:
dependencies:
regenerate: 1.4.2
@@ -16753,6 +20610,8 @@ snapshots:
resolve-from@5.0.0: {}
+ resolve-workspace-root@2.0.1: {}
+
resolve.exports@2.0.3: {}
resolve@1.22.12:
@@ -16768,6 +20627,11 @@ snapshots:
path-parse: 1.0.7
supports-preserve-symlinks-flag: 1.0.0
+ restore-cursor@2.0.0:
+ dependencies:
+ onetime: 2.0.1
+ signal-exit: 3.0.7
+
restore-cursor@3.1.0:
dependencies:
onetime: 5.1.2
@@ -16923,6 +20787,8 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ server-only@0.0.1: {}
+
set-blocking@2.0.0: {}
set-cookie-parser@2.7.2: {}
@@ -17014,6 +20880,8 @@ snapshots:
astral-regex: 1.0.0
is-fullwidth-code-point: 2.0.0
+ slugify@1.6.9: {}
+
snake-case@3.0.4:
dependencies:
dot-case: 3.0.4
@@ -17098,6 +20966,8 @@ snapshots:
dependencies:
type-fest: 0.7.1
+ standard-navigation@0.0.5: {}
+
standard-navigation@0.0.8(react@19.2.8):
dependencies:
react: 19.2.8
@@ -17120,6 +20990,8 @@ snapshots:
strict-uri-encode@2.0.0: {}
+ strict-url-sanitise@0.0.1: {}
+
string-length@4.0.2:
dependencies:
char-regex: 1.0.2
@@ -17158,12 +21030,18 @@ snapshots:
strip-final-newline@4.0.0: {}
+ strip-indent@3.0.0:
+ dependencies:
+ min-indent: 1.0.1
+
strip-json-comments@3.1.1: {}
strnum@2.4.1:
dependencies:
anynum: 1.0.1
+ structured-headers@0.4.1: {}
+
style-to-js@1.1.21:
dependencies:
style-to-object: 1.0.14
@@ -17182,6 +21060,10 @@ snapshots:
tinyglobby: 0.2.17
ts-interface-checker: 0.1.13
+ supports-color@5.5.0:
+ dependencies:
+ has-flag: 3.0.0
+
supports-color@7.2.0:
dependencies:
has-flag: 4.0.0
@@ -17190,6 +21072,11 @@ snapshots:
dependencies:
has-flag: 4.0.0
+ supports-hyperlinks@2.3.0:
+ dependencies:
+ has-flag: 4.0.0
+ supports-color: 7.2.0
+
supports-preserve-symlinks-flag@1.0.0: {}
svg-parser@2.0.4: {}
@@ -17242,6 +21129,11 @@ snapshots:
term-size@2.2.1: {}
+ terminal-link@2.1.1:
+ dependencies:
+ ansi-escapes: 4.3.2
+ supports-hyperlinks: 2.3.0
+
terser-webpack-plugin@5.5.0(@swc/core@1.15.46(@swc/helpers@0.5.23))(webpack@5.105.4(@swc/core@1.15.46(@swc/helpers@0.5.23))):
dependencies:
'@jridgewell/trace-mapping': 0.3.31
@@ -17333,6 +21225,8 @@ snapshots:
toidentifier@1.0.1: {}
+ toqr@0.1.1: {}
+
tr46@0.0.3: {}
tree-dump@1.1.0(tslib@2.8.1):
@@ -17387,6 +21281,8 @@ snapshots:
typescript@5.9.3: {}
+ typescript@6.0.3: {}
+
undici-types@6.21.0: {}
undici-types@8.3.0: {}
@@ -17477,10 +21373,46 @@ snapshots:
url-join@4.0.1: {}
+ use-callback-ref@1.3.3(@types/react@19.2.17)(react@19.2.3):
+ dependencies:
+ react: 19.2.3
+ tslib: 2.8.1
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ use-callback-ref@1.3.3(@types/react@19.2.17)(react@19.2.8):
+ dependencies:
+ react: 19.2.8
+ tslib: 2.8.1
+ optionalDependencies:
+ '@types/react': 19.2.17
+ optional: true
+
+ use-latest-callback@0.2.6(react@19.2.3):
+ dependencies:
+ react: 19.2.3
+
use-latest-callback@0.2.6(react@19.2.8):
dependencies:
react: 19.2.8
+ use-sidecar@1.1.3(@types/react@19.2.17)(react@19.2.3):
+ dependencies:
+ detect-node-es: 1.1.0
+ react: 19.2.3
+ tslib: 2.8.1
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ use-sidecar@1.1.3(@types/react@19.2.17)(react@19.2.8):
+ dependencies:
+ detect-node-es: 1.1.0
+ react: 19.2.8
+ tslib: 2.8.1
+ optionalDependencies:
+ '@types/react': 19.2.17
+ optional: true
+
use-sync-external-store@1.6.0(react@19.2.8):
dependencies:
react: 19.2.8
@@ -17497,8 +21429,29 @@ snapshots:
'@types/istanbul-lib-coverage': 2.0.6
convert-source-map: 2.0.0
+ validate-npm-package-name@5.0.1: {}
+
vary@1.1.2: {}
+ vaul@1.1.2(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.3))(react@19.2.3):
+ dependencies:
+ '@radix-ui/react-dialog': 1.1.23(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.3))(react@19.2.3)
+ react: 19.2.3
+ react-dom: 19.2.8(react@19.2.3)
+ transitivePeerDependencies:
+ - '@types/react'
+ - '@types/react-dom'
+
+ vaul@1.1.2(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8):
+ dependencies:
+ '@radix-ui/react-dialog': 1.1.23(@types/react-dom@17.0.26(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
+ react: 19.2.8
+ react-dom: 19.2.8(react@19.2.8)
+ transitivePeerDependencies:
+ - '@types/react'
+ - '@types/react-dom'
+ optional: true
+
vfile-location@5.0.3:
dependencies:
'@types/unist': 3.0.3
@@ -17722,6 +21675,8 @@ snapshots:
whatwg-fetch@3.6.20: {}
+ whatwg-url-minimum@0.1.2: {}
+
whatwg-url@5.0.0:
dependencies:
tr46: 0.0.3
@@ -17788,6 +21743,13 @@ snapshots:
xml-naming@0.3.0: {}
+ xml2js@0.6.0:
+ dependencies:
+ sax: 1.6.1
+ xmlbuilder: 11.0.1
+
+ xmlbuilder@11.0.1: {}
+
xmlbuilder@15.1.1: {}
y18n@4.0.3: {}
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index df35d1eb0..3ce92e5df 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -10,6 +10,14 @@ packageManagerStrict: true
packageManagerStrictVersion: false
updateNotifier: false
+packageExtensions:
+ # react-native-worklets 0.10.1 imports these from its Babel plugin but does
+ # not declare them, which breaks strict pnpm dependency isolation.
+ "react-native-worklets@0.10.1":
+ dependencies:
+ "@babel/generator": ^7.29.7
+ "@babel/traverse": ^7.29.7
+
overrides:
"caniuse-lite": ^1.0.30001774
# Security fixes for advisories in @module-federation/dts-plugin, which pins