@react-native-firebase/app 25.1.0 → 26.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/RNFBApp.podspec +7 -13
  3. package/android/build.gradle +25 -6
  4. package/android/src/reactnative/java/io/invertase/firebase/app/{ReactNativeFirebaseAppModule.java → NativeRNFBTurboApp.java} +81 -45
  5. package/android/src/reactnative/java/io/invertase/firebase/app/ReactNativeFirebaseAppPackage.java +3 -3
  6. package/android/src/reactnative/java/io/invertase/firebase/app/ReactNativeFirebaseVersion.java +2 -2
  7. package/android/src/reactnative/java/io/invertase/firebase/app/generated/java/com/facebook/fbreact/specs/NativeRNFBTurboAppSpec.java +136 -0
  8. package/android/src/reactnative/java/io/invertase/firebase/app/generated/java/com/facebook/fbreact/specs/NativeRNFBTurboUtilsSpec.java +94 -0
  9. package/android/src/reactnative/java/io/invertase/firebase/app/generated/jni/CMakeLists.txt +36 -0
  10. package/android/src/reactnative/java/io/invertase/firebase/app/generated/jni/RNFBAppTurboModules-generated.cpp +170 -0
  11. package/android/src/reactnative/java/io/invertase/firebase/app/generated/jni/RNFBAppTurboModules.h +39 -0
  12. package/android/src/reactnative/java/io/invertase/firebase/app/generated/jni/react/renderer/components/RNFBAppTurboModules/RNFBAppTurboModulesJSI-generated.cpp +187 -0
  13. package/android/src/reactnative/java/io/invertase/firebase/app/generated/jni/react/renderer/components/RNFBAppTurboModules/RNFBAppTurboModulesJSI.h +606 -0
  14. package/android/src/reactnative/java/io/invertase/firebase/common/RCTConvertFirebase.java +3 -3
  15. package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseEventEmitter.java +475 -20
  16. package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseMeta.java +8 -0
  17. package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebasePreferences.java +8 -0
  18. package/android/src/reactnative/java/io/invertase/firebase/utils/{ReactNativeFirebaseUtilsModule.java → NativeRNFBTurboUtils.java} +57 -40
  19. package/android/src/test/java/io/invertase/firebase/app/NativeRNFBTurboAppTest.java +400 -0
  20. package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseEventEmitterTest.java +1823 -0
  21. package/app.plugin.js +1 -1
  22. package/dist/module/FirebaseApp.js +0 -9
  23. package/dist/module/FirebaseApp.js.map +1 -1
  24. package/dist/module/common/index.js +0 -645
  25. package/dist/module/common/index.js.map +1 -1
  26. package/dist/module/common/unitTestUtils.js +18 -54
  27. package/dist/module/common/unitTestUtils.js.map +1 -1
  28. package/dist/module/index.js +0 -1
  29. package/dist/module/index.js.map +1 -1
  30. package/dist/module/internal/RNFBNativeEventEmitter.js +12 -6
  31. package/dist/module/internal/RNFBNativeEventEmitter.js.map +1 -1
  32. package/dist/module/internal/constants.js +2 -2
  33. package/dist/module/internal/constants.js.map +1 -1
  34. package/dist/module/internal/index.js +1 -1
  35. package/dist/module/internal/index.js.map +1 -1
  36. package/dist/module/internal/nativeModuleAndroidIos.js +50 -10
  37. package/dist/module/internal/nativeModuleAndroidIos.js.map +1 -1
  38. package/dist/module/internal/nativeModuleWeb.js +12 -4
  39. package/dist/module/internal/nativeModuleWeb.js.map +1 -1
  40. package/dist/module/internal/registry/app.js +20 -12
  41. package/dist/module/internal/registry/app.js.map +1 -1
  42. package/dist/module/internal/registry/modular.js +73 -0
  43. package/dist/module/internal/registry/modular.js.map +1 -0
  44. package/dist/module/internal/registry/nativeModule.js +116 -95
  45. package/dist/module/internal/registry/nativeModule.js.map +1 -1
  46. package/dist/module/modular.js +44 -37
  47. package/dist/module/modular.js.map +1 -1
  48. package/dist/module/types/app.js +0 -3
  49. package/dist/module/types/app.js.map +1 -1
  50. package/dist/module/types/internal.js +0 -2
  51. package/dist/module/utils/UtilsStatics.js +2 -2
  52. package/dist/module/utils/UtilsStatics.js.map +1 -1
  53. package/dist/module/utils/index.js +21 -17
  54. package/dist/module/utils/index.js.map +1 -1
  55. package/dist/module/version.js +1 -1
  56. package/dist/typescript/lib/FirebaseApp.d.ts +1 -2
  57. package/dist/typescript/lib/FirebaseApp.d.ts.map +1 -1
  58. package/dist/typescript/lib/common/index.d.ts +0 -8
  59. package/dist/typescript/lib/common/index.d.ts.map +1 -1
  60. package/dist/typescript/lib/common/unitTestUtils.d.ts +1 -4
  61. package/dist/typescript/lib/common/unitTestUtils.d.ts.map +1 -1
  62. package/dist/typescript/lib/index.d.ts +0 -1
  63. package/dist/typescript/lib/index.d.ts.map +1 -1
  64. package/dist/typescript/lib/internal/NativeFirebaseError.d.ts +1 -1
  65. package/dist/typescript/lib/internal/NativeFirebaseError.d.ts.map +1 -1
  66. package/dist/typescript/lib/internal/RNFBNativeEventEmitter.d.ts.map +1 -1
  67. package/dist/typescript/lib/internal/constants.d.ts +2 -3
  68. package/dist/typescript/lib/internal/constants.d.ts.map +1 -1
  69. package/dist/typescript/lib/internal/index.d.ts +1 -1
  70. package/dist/typescript/lib/internal/index.d.ts.map +1 -1
  71. package/dist/typescript/lib/internal/nativeModuleAndroidIos.d.ts +2 -6
  72. package/dist/typescript/lib/internal/nativeModuleAndroidIos.d.ts.map +1 -1
  73. package/dist/typescript/lib/internal/nativeModuleWeb.d.ts.map +1 -1
  74. package/dist/typescript/lib/internal/registry/app.d.ts +2 -2
  75. package/dist/typescript/lib/internal/registry/app.d.ts.map +1 -1
  76. package/dist/typescript/lib/internal/registry/modular.d.ts +22 -0
  77. package/dist/typescript/lib/internal/registry/modular.d.ts.map +1 -0
  78. package/dist/typescript/lib/internal/registry/nativeModule.d.ts +3 -11
  79. package/dist/typescript/lib/internal/registry/nativeModule.d.ts.map +1 -1
  80. package/dist/typescript/lib/modular.d.ts +21 -7
  81. package/dist/typescript/lib/modular.d.ts.map +1 -1
  82. package/dist/typescript/lib/types/app.d.ts +4 -4
  83. package/dist/typescript/lib/types/app.d.ts.map +1 -1
  84. package/dist/typescript/lib/types/internal.d.ts +1 -33
  85. package/dist/typescript/lib/types/internal.d.ts.map +1 -1
  86. package/dist/typescript/lib/utils/index.d.ts +7 -3
  87. package/dist/typescript/lib/utils/index.d.ts.map +1 -1
  88. package/dist/typescript/lib/version.d.ts +1 -1
  89. package/firebase-schema.json +5 -1
  90. package/ios/RNFBApp/RNFBAppModule.h +1 -3
  91. package/ios/RNFBApp/{RNFBAppModule.m → RNFBAppModule.mm} +74 -77
  92. package/ios/RNFBApp/RNFBUtilsModule.h +1 -3
  93. package/ios/RNFBApp/{RNFBUtilsModule.m → RNFBUtilsModule.mm} +68 -25
  94. package/ios/RNFBApp/RNFBVersion.m +1 -1
  95. package/ios/RNFBApp.xcodeproj/project.pbxproj +12 -12
  96. package/ios/generated/RCTAppDependencyProvider.h +25 -0
  97. package/ios/generated/RCTAppDependencyProvider.mm +55 -0
  98. package/ios/generated/RCTModuleProviders.h +16 -0
  99. package/ios/generated/RCTModuleProviders.mm +52 -0
  100. package/ios/generated/RCTModulesConformingToProtocolsProvider.h +18 -0
  101. package/ios/generated/RCTModulesConformingToProtocolsProvider.mm +33 -0
  102. package/ios/generated/RCTThirdPartyComponentsProvider.h +16 -0
  103. package/ios/generated/RCTThirdPartyComponentsProvider.mm +23 -0
  104. package/ios/generated/RCTUnstableModulesRequiringMainQueueSetupProvider.h +14 -0
  105. package/ios/generated/RCTUnstableModulesRequiringMainQueueSetupProvider.mm +19 -0
  106. package/ios/generated/RNFBAppTurboModules/RNFBAppTurboModules-generated.mm +210 -0
  107. package/ios/generated/RNFBAppTurboModules/RNFBAppTurboModules.h +399 -0
  108. package/ios/generated/RNFBAppTurboModulesJSI-generated.cpp +187 -0
  109. package/ios/generated/RNFBAppTurboModulesJSI.h +606 -0
  110. package/ios/generated/ReactAppDependencyProvider.podspec +34 -0
  111. package/ios/generated/ReactCodegen.podspec +111 -0
  112. package/lib/FirebaseApp.ts +1 -10
  113. package/lib/common/index.ts +0 -742
  114. package/lib/common/unitTestUtils.ts +17 -73
  115. package/lib/index.ts +0 -1
  116. package/lib/internal/NativeFirebaseError.ts +1 -1
  117. package/lib/internal/RNFBNativeEventEmitter.ts +12 -6
  118. package/lib/internal/constants.ts +3 -25
  119. package/lib/internal/global.d.ts +0 -4
  120. package/lib/internal/index.ts +1 -1
  121. package/lib/internal/nativeModuleAndroidIos.ts +69 -12
  122. package/lib/internal/nativeModuleWeb.ts +11 -5
  123. package/lib/internal/registry/app.ts +24 -22
  124. package/lib/internal/registry/modular.ts +97 -0
  125. package/lib/internal/registry/nativeModule.ts +136 -107
  126. package/lib/modular.ts +47 -72
  127. package/lib/types/app.ts +6 -11
  128. package/lib/types/internal.ts +1 -46
  129. package/lib/utils/UtilsStatics.ts +2 -2
  130. package/lib/utils/index.ts +24 -18
  131. package/lib/version.ts +1 -1
  132. package/package.json +41 -3
  133. package/plugin/build/app.plugin.d.ts +2 -0
  134. package/plugin/build/app.plugin.js +6 -0
  135. package/plugin/src/app.plugin.ts +3 -0
  136. package/plugin/tsconfig.tsbuildinfo +1 -1
  137. package/react-native.config.js +2 -0
  138. package/specs/NativeRNFBTurboApp.ts +57 -0
  139. package/specs/NativeRNFBTurboUtils.ts +34 -0
  140. package/typedoc.json +1 -2
  141. package/dist/module/internal/registry/namespace.js +0 -262
  142. package/dist/module/internal/registry/namespace.js.map +0 -1
  143. package/dist/module/namespaced.js +0 -25
  144. package/dist/module/namespaced.js.map +0 -1
  145. package/dist/typescript/lib/internal/registry/namespace.d.ts +0 -26
  146. package/dist/typescript/lib/internal/registry/namespace.d.ts.map +0 -1
  147. package/dist/typescript/lib/namespaced.d.ts +0 -5
  148. package/dist/typescript/lib/namespaced.d.ts.map +0 -1
  149. package/lib/internal/registry/namespace.ts +0 -338
  150. package/lib/namespaced.ts +0 -24
@@ -14,7 +14,7 @@
14
14
  * limitations under the License.
15
15
  *
16
16
  */
17
- import { APP_NATIVE_MODULE } from '../constants';
17
+ import { APP_NATIVE_MODULE, UTILS_NATIVE_MODULE } from '../constants';
18
18
  import NativeFirebaseError from '../NativeFirebaseError';
19
19
  import type { NativeError } from '../../types/internal';
20
20
  import RNFBNativeEventEmitter from '../RNFBNativeEventEmitter';
@@ -31,8 +31,15 @@ interface NativeEvent {
31
31
  [key: string]: unknown;
32
32
  }
33
33
 
34
+ interface RouteEntry {
35
+ host: Record<string, unknown>;
36
+ key: string;
37
+ argToPrepend: unknown[];
38
+ }
39
+
34
40
  const NATIVE_MODULE_REGISTRY: Record<string, WrappedNativeModule> = {};
35
41
  const NATIVE_MODULE_EVENT_SUBSCRIPTIONS: Record<string, boolean> = {};
42
+ let staticUtilsModule: WrappedNativeModule | null = null;
36
43
 
37
44
  function nativeModuleKey(module: FirebaseModule): string {
38
45
  return `${module._customUrlOrRegion || ''}:${module.app.name}:${module._config.namespace}`;
@@ -41,11 +48,6 @@ function nativeModuleKey(module: FirebaseModule): string {
41
48
  /**
42
49
  * Wraps a native module method to provide
43
50
  * auto prepended args and custom Error classes.
44
- *
45
- * @param namespace
46
- * @param method
47
- * @param argToPrepend
48
- * @returns {Function}
49
51
  */
50
52
  function nativeModuleMethodWrapped(
51
53
  namespace: string,
@@ -54,9 +56,7 @@ function nativeModuleMethodWrapped(
54
56
  isTurboModule: boolean,
55
57
  ): (...args: unknown[]) => unknown {
56
58
  return (...args: unknown[]) => {
57
- // For iOS TurboModules, encode null values in arguments to work around
58
- // the limitation where null values in object properties get stripped during serialization
59
- // See: https://github.com/facebook/react-native/issues/52802
59
+ // Per-call encodeNullValues walk is intentional: args may differ on every invocation.
60
60
  const processedArgs = isIOS && isTurboModule ? args.map(arg => encodeNullValues(arg)) : args;
61
61
  const allArgs = [...argToPrepend, ...processedArgs];
62
62
  const possiblePromise = method(...allArgs);
@@ -73,50 +73,107 @@ function nativeModuleMethodWrapped(
73
73
  }
74
74
 
75
75
  /**
76
- * Prepends all arguments in prependArgs to all native method calls
77
- *
78
- * @param namespace
79
- * @param NativeModule - Raw native module from React Native
80
- * @param argToPrepend
76
+ * NewArch-AD-14 / NewArch-AD-14a: routing composite Proxy lazy wrap+bind on first access per method.
81
77
  */
82
- function nativeModuleWrapped(
78
+ function createRoutingCompositeProxy(
83
79
  namespace: string,
84
- NativeModule: Record<string, unknown> | undefined,
85
- argToPrepend: unknown[],
80
+ hosts: Array<{ module: Record<string, unknown> | undefined; argToPrepend: unknown[] }>,
86
81
  isTurboModule: boolean,
87
82
  ): WrappedNativeModule {
88
- const native: Record<string, unknown> = {};
89
- if (!NativeModule) {
90
- return NativeModule as unknown as WrappedNativeModule;
91
- }
83
+ const routingMap: Record<string, RouteEntry> = {};
84
+ const memoizedMethods: Record<string, (...args: unknown[]) => unknown> = {};
85
+ const memoizedConstants: Record<string, unknown> = {};
92
86
 
93
- const nativeModuleObj = NativeModule;
94
- let properties = Object.keys(Object.getPrototypeOf(nativeModuleObj));
95
- if (!properties.length) properties = Object.keys(nativeModuleObj);
96
-
97
- for (let i = 0, len = properties.length; i < len; i++) {
98
- const property = properties[i];
99
- if (!property) continue;
100
- if (typeof nativeModuleObj[property] === 'function') {
101
- native[property] = nativeModuleMethodWrapped(
102
- namespace,
103
- nativeModuleObj[property] as (...args: unknown[]) => unknown,
104
- argToPrepend,
105
- isTurboModule,
106
- );
107
- } else {
108
- native[property] = nativeModuleObj[property];
87
+ for (const { module, argToPrepend } of hosts) {
88
+ if (!module) {
89
+ continue;
90
+ }
91
+ for (const key in module) {
92
+ if (key === 'constructor' || key === 'getConstants') {
93
+ continue;
94
+ }
95
+ routingMap[key] = { host: module, key, argToPrepend };
109
96
  }
110
97
  }
111
98
 
112
- return native;
99
+ const target = Object.create(null) as WrappedNativeModule;
100
+
101
+ return new Proxy(target, {
102
+ get(_target, name: string | symbol) {
103
+ if (typeof name !== 'string' || !(name in routingMap)) {
104
+ return undefined;
105
+ }
106
+
107
+ const route = routingMap[name];
108
+ if (!route) {
109
+ return undefined;
110
+ }
111
+
112
+ const value = route.host[route.key];
113
+
114
+ if (typeof value !== 'function') {
115
+ if (!(name in memoizedConstants)) {
116
+ memoizedConstants[name] = value;
117
+ }
118
+ return memoizedConstants[name];
119
+ }
120
+
121
+ if (!memoizedMethods[name]) {
122
+ memoizedMethods[name] = nativeModuleMethodWrapped(
123
+ namespace,
124
+ (value as (...args: unknown[]) => unknown).bind(route.host),
125
+ route.argToPrepend,
126
+ isTurboModule,
127
+ ) as (...args: unknown[]) => unknown;
128
+ }
129
+
130
+ return memoizedMethods[name];
131
+ },
132
+ has(_target, name) {
133
+ return typeof name === 'string' && name in routingMap;
134
+ },
135
+ ownKeys() {
136
+ return Object.keys(routingMap);
137
+ },
138
+ getOwnPropertyDescriptor(_target, name) {
139
+ if (typeof name !== 'string' || !(name in routingMap)) {
140
+ return undefined;
141
+ }
142
+ return {
143
+ enumerable: true,
144
+ configurable: true,
145
+ };
146
+ },
147
+ set() {
148
+ return false;
149
+ },
150
+ });
151
+ }
152
+
153
+ /**
154
+ * NewArch-AD-14a N=1: single-host routing composite (TurboModule by default).
155
+ */
156
+ function createSingleHostComposite(
157
+ namespace: string,
158
+ moduleName: string,
159
+ argToPrepend: unknown[] = [],
160
+ isTurboModule = true,
161
+ ): WrappedNativeModule {
162
+ const nativeModule = getReactNativeModule(moduleName);
163
+
164
+ if (!nativeModule) {
165
+ throw new Error(getMissingModuleHelpText(namespace));
166
+ }
167
+
168
+ return createRoutingCompositeProxy(
169
+ namespace,
170
+ [{ module: nativeModule, argToPrepend }],
171
+ isTurboModule,
172
+ );
113
173
  }
114
174
 
115
175
  /**
116
176
  * Initialises and wraps all the native module methods.
117
- *
118
- * @param module
119
- * @returns {*}
120
177
  */
121
178
  function initialiseNativeModule(module: FirebaseModule): WrappedNativeModule {
122
179
  const config = module._config;
@@ -130,41 +187,45 @@ function initialiseNativeModule(module: FirebaseModule): WrappedNativeModule {
130
187
  disablePrependCustomUrlOrRegion,
131
188
  turboModule,
132
189
  } = config;
133
- const multiModuleRoot: WrappedNativeModule = {};
134
190
  const isTurboModule = !!turboModule;
135
191
  const multiModule = Array.isArray(nativeModuleName);
136
192
  const nativeModuleNames = multiModule ? nativeModuleName : [nativeModuleName];
137
193
 
138
- for (let i = 0; i < nativeModuleNames.length; i++) {
139
- const moduleName = nativeModuleNames[i];
140
- if (!moduleName) continue;
194
+ const argToPrepend: Array<string> = [];
195
+
196
+ if (hasMultiAppSupport) {
197
+ argToPrepend.push(module.app.name);
198
+ }
199
+
200
+ if (hasCustomUrlOrRegionSupport && !disablePrependCustomUrlOrRegion) {
201
+ argToPrepend.push(module._customUrlOrRegion as string);
202
+ }
141
203
 
142
- const nativeModule = getReactNativeModule(moduleName);
204
+ let composite: WrappedNativeModule;
143
205
 
144
- // only error if there's a single native module
145
- // as multi modules can mean some are optional
146
- if (!multiModule && !nativeModule) {
206
+ if (!multiModule) {
207
+ const moduleName = nativeModuleNames[0];
208
+ if (!moduleName) {
147
209
  throw new Error(getMissingModuleHelpText(namespace));
148
210
  }
149
211
 
150
- if (multiModule) {
151
- multiModuleRoot[moduleName] = !!nativeModule;
152
- }
212
+ composite = createSingleHostComposite(namespace, moduleName, argToPrepend, isTurboModule);
213
+ } else {
214
+ const hosts: Array<{ module: Record<string, unknown> | undefined; argToPrepend: unknown[] }> =
215
+ [];
153
216
 
154
- const argToPrepend: Array<string> = [];
217
+ for (let i = 0; i < nativeModuleNames.length; i++) {
218
+ const moduleName = nativeModuleNames[i];
219
+ if (!moduleName) continue;
155
220
 
156
- if (hasMultiAppSupport) {
157
- argToPrepend.push(module.app.name);
158
- }
221
+ const nativeModule = getReactNativeModule(moduleName);
159
222
 
160
- if (hasCustomUrlOrRegionSupport && !disablePrependCustomUrlOrRegion) {
161
- argToPrepend.push(module._customUrlOrRegion as string);
223
+ if (nativeModule) {
224
+ hosts.push({ module: nativeModule, argToPrepend });
225
+ }
162
226
  }
163
227
 
164
- Object.assign(
165
- multiModuleRoot,
166
- nativeModuleWrapped(namespace, nativeModule, argToPrepend, isTurboModule),
167
- );
228
+ composite = createRoutingCompositeProxy(namespace, hosts, isTurboModule);
168
229
  }
169
230
 
170
231
  if (nativeEvents && Array.isArray(nativeEvents) && nativeEvents.length) {
@@ -176,34 +237,20 @@ function initialiseNativeModule(module: FirebaseModule): WrappedNativeModule {
176
237
  }
177
238
  }
178
239
 
179
- Object.freeze(multiModuleRoot);
180
-
181
- NATIVE_MODULE_REGISTRY[key] = multiModuleRoot;
240
+ NATIVE_MODULE_REGISTRY[key] = composite;
182
241
 
183
242
  return NATIVE_MODULE_REGISTRY[key];
184
243
  }
185
244
 
186
- /**
187
- * Subscribe to a native event for js side distribution by appName
188
- * React Native events are hard set at compile - cant do dynamic event names
189
- * so we use a single event send it to js and js then internally can prefix it
190
- * and distribute dynamically.
191
- *
192
- * @param eventName
193
- * @private
194
- */
195
245
  function subscribeToNativeModuleEvent(eventName: string): void {
196
246
  if (!NATIVE_MODULE_EVENT_SUBSCRIPTIONS[eventName]) {
197
247
  RNFBNativeEventEmitter.addListener(eventName, (...args: unknown[]) => {
198
248
  const event = args[0] as NativeEvent;
199
249
  if (event.appName && event.databaseId) {
200
- // Firestore requires both appName and databaseId to prefix
201
250
  SharedEventEmitter.emit(`${event.appName}-${event.databaseId}-${eventName}`, event);
202
251
  } else if (event.appName) {
203
- // native event has an appName property - auto prefix and internally emit
204
252
  SharedEventEmitter.emit(`${event.appName}-${eventName}`, event);
205
253
  } else {
206
- // standard event - no need to prefix
207
254
  SharedEventEmitter.emit(eventName, event);
208
255
  }
209
256
  });
@@ -212,12 +259,6 @@ function subscribeToNativeModuleEvent(eventName: string): void {
212
259
  }
213
260
  }
214
261
 
215
- /**
216
- * Help text for integrating the native counter parts for each firebase module.
217
- *
218
- * @param namespace
219
- * @returns {string}
220
- */
221
262
  function getMissingModuleHelpText(namespace: string): string {
222
263
  const snippet = `firebase.${namespace}()`;
223
264
 
@@ -236,13 +277,6 @@ function getMissingModuleHelpText(namespace: string): string {
236
277
  );
237
278
  }
238
279
 
239
- /**
240
- * Gets a wrapped native module instance for the provided firebase module.
241
- * Will attempt to create a new instance if non previously created.
242
- *
243
- * @param module
244
- * @returns {*}
245
- */
246
280
  export function getNativeModule(module: FirebaseModule): WrappedNativeModule {
247
281
  const key = nativeModuleKey(module);
248
282
 
@@ -253,30 +287,25 @@ export function getNativeModule(module: FirebaseModule): WrappedNativeModule {
253
287
  return initialiseNativeModule(module);
254
288
  }
255
289
 
256
- /**
257
- * Custom wrapped app module as it does not have it's own FirebaseModule based class.
258
- *
259
- * @returns {*}
260
- */
261
290
  export function getAppModule(): RNFBAppModuleInterface {
262
291
  if (NATIVE_MODULE_REGISTRY[APP_NATIVE_MODULE]) {
263
292
  return NATIVE_MODULE_REGISTRY[APP_NATIVE_MODULE] as unknown as RNFBAppModuleInterface;
264
293
  }
265
294
 
266
- const namespace = 'app';
267
- const nativeModule = getReactNativeModule(APP_NATIVE_MODULE);
295
+ NATIVE_MODULE_REGISTRY[APP_NATIVE_MODULE] = createSingleHostComposite('app', APP_NATIVE_MODULE);
268
296
 
269
- if (!nativeModule) {
270
- throw new Error(getMissingModuleHelpText(namespace));
297
+ return NATIVE_MODULE_REGISTRY[APP_NATIVE_MODULE] as unknown as RNFBAppModuleInterface;
298
+ }
299
+
300
+ /**
301
+ * Memoized wrapped utils surface for static path-constant reads (NewArch-AD-18 E5).
302
+ */
303
+ export function getStaticUtilsModule(): WrappedNativeModule {
304
+ if (staticUtilsModule) {
305
+ return staticUtilsModule;
271
306
  }
272
307
 
273
- NATIVE_MODULE_REGISTRY[APP_NATIVE_MODULE] = nativeModuleWrapped(
274
- namespace,
275
- nativeModule,
276
- [],
277
- // TODO: change to true when we use TurboModules for app package
278
- false,
279
- );
308
+ staticUtilsModule = createSingleHostComposite('utils', UTILS_NATIVE_MODULE);
280
309
 
281
- return NATIVE_MODULE_REGISTRY[APP_NATIVE_MODULE] as unknown as RNFBAppModuleInterface;
310
+ return staticUtilsModule;
282
311
  }
package/lib/modular.ts CHANGED
@@ -15,8 +15,9 @@
15
15
  *
16
16
  */
17
17
 
18
- import { MODULAR_DEPRECATION_ARG } from './common';
19
- import type { ReactNativeFirebase, LogCallback, LogOptions } from './types/app';
18
+ import type { ReactNativeFirebase, LogCallback, LogOptions, Utils } from './types/app';
19
+ import { getUtils as getUtilsImpl } from './utils';
20
+ import UtilsStatics from './utils/UtilsStatics';
20
21
  import {
21
22
  deleteApp as deleteAppCompat,
22
23
  getApp as getAppCompat,
@@ -27,36 +28,27 @@ import {
27
28
  } from './internal/registry/app';
28
29
  import { setUserLogHandler } from './internal/logger';
29
30
  import { version as sdkVersion } from './version';
30
- import { getReactNativeModule } from './internal/nativeModule';
31
- import { APP_NATIVE_MODULE } from './internal/constants';
32
- import type { RNFBAppModuleInterface } from './internal/NativeModules';
31
+ import { getAppModule } from './internal/registry/nativeModule';
33
32
  /**
34
33
  * Renders this app unusable and frees the resources of all associated services.
35
34
  * @param app - The app to delete.
36
35
  * @returns Promise<void>
37
36
  */
38
37
  export function deleteApp(app: ReactNativeFirebase.FirebaseApp): Promise<void> {
39
- return deleteAppCompat.call(
40
- null,
41
- app.name,
42
- (app as any)._nativeInitialized,
43
- // @ts-expect-error - Extra arg used by deprecation proxy to detect modular calls
44
- MODULAR_DEPRECATION_ARG,
45
- );
38
+ return deleteAppCompat(app.name, (app as any)._nativeInitialized);
46
39
  }
47
40
 
48
41
  /**
49
42
  * Registers a library's name and version for platform logging purposes.
50
- * @param _libraryKeyOrName - library name or key.
51
- * @param _version - library version.
52
- * @param _variant - library variant. Optional.
53
- * @returns Promise<void>
43
+ *
44
+ * @remarks **Web only.** Always throws on React Native Firebase — native SDK version registration
45
+ * is handled by the Firebase iOS/Android SDKs.
54
46
  */
55
47
  export function registerVersion(
56
48
  _libraryKeyOrName: string,
57
49
  _version: string,
58
50
  _variant?: string,
59
- ): Promise<void> {
51
+ ): void {
60
52
  throw new Error('registerVersion is only supported on Web');
61
53
  }
62
54
 
@@ -75,15 +67,16 @@ export function onLog(logCallback: LogCallback | null, options?: LogOptions): vo
75
67
  * @returns An array of all initialized Firebase apps.
76
68
  */
77
69
  export function getApps(): ReactNativeFirebase.FirebaseApp[] {
78
- return getAppsCompat.call(
79
- null,
80
- // @ts-expect-error - Extra arg used by deprecation proxy to detect modular calls
81
- MODULAR_DEPRECATION_ARG,
82
- );
70
+ return getAppsCompat();
83
71
  }
84
72
 
85
73
  /**
86
74
  * Initializes a Firebase app with the provided options and name.
75
+ *
76
+ * @remarks On React Native Firebase this is **async** (`Promise<FirebaseApp>`) because secondary
77
+ * app creation crosses the native bridge. The default app is still configured from native
78
+ * `GoogleService-Info.plist` / `google-services.json` at launch.
79
+ *
87
80
  * @param options - Options to configure the services used in the app.
88
81
  * @param configOrName - The optional name of the app, or config for the app to initialize (a name of '[DEFAULT]' will be used if omitted).
89
82
  * @returns The initialized Firebase app.
@@ -92,13 +85,7 @@ export function initializeApp(
92
85
  options: ReactNativeFirebase.FirebaseAppOptions,
93
86
  configOrName?: string | ReactNativeFirebase.FirebaseAppConfig,
94
87
  ): Promise<ReactNativeFirebase.FirebaseApp> {
95
- return initializeAppCompat.call(
96
- null,
97
- options,
98
- configOrName,
99
- // @ts-expect-error - Extra arg used by deprecation proxy to detect modular calls
100
- MODULAR_DEPRECATION_ARG,
101
- );
88
+ return initializeAppCompat(options, configOrName);
102
89
  }
103
90
 
104
91
  /**
@@ -107,12 +94,7 @@ export function initializeApp(
107
94
  * @returns The requested Firebase app instance.
108
95
  */
109
96
  export function getApp(name?: string): ReactNativeFirebase.FirebaseApp {
110
- return getAppCompat.call(
111
- null,
112
- name,
113
- // @ts-expect-error - Extra arg used by deprecation proxy to detect modular calls
114
- MODULAR_DEPRECATION_ARG,
115
- );
97
+ return getAppCompat(name);
116
98
  }
117
99
 
118
100
  /**
@@ -121,12 +103,7 @@ export function getApp(name?: string): ReactNativeFirebase.FirebaseApp {
121
103
  * @returns void
122
104
  */
123
105
  export function setLogLevel(logLevel: ReactNativeFirebase.LogLevelString): void {
124
- return setLogLevelCompat.call(
125
- null,
126
- logLevel,
127
- // @ts-expect-error - Extra arg used by deprecation proxy to detect modular calls
128
- MODULAR_DEPRECATION_ARG,
129
- );
106
+ return setLogLevelCompat(logLevel);
130
107
  }
131
108
 
132
109
  /**
@@ -138,12 +115,7 @@ export function setLogLevel(logLevel: ReactNativeFirebase.LogLevelString): void
138
115
  export function setReactNativeAsyncStorage(
139
116
  asyncStorage: ReactNativeFirebase.ReactNativeAsyncStorage,
140
117
  ): void {
141
- return setReactNativeAsyncStorageCompat.call(
142
- null,
143
- asyncStorage,
144
- // @ts-expect-error - Extra arg used by deprecation proxy to detect modular calls
145
- MODULAR_DEPRECATION_ARG,
146
- );
118
+ return setReactNativeAsyncStorageCompat(asyncStorage);
147
119
  }
148
120
 
149
121
  /**
@@ -151,10 +123,7 @@ export function setReactNativeAsyncStorage(
151
123
  * @returns map of key / value pairs containing native meta data
152
124
  */
153
125
  export function metaGetAll(): Promise<{ [key: string]: string | boolean }> {
154
- const RNFBAppModule = getReactNativeModule(
155
- APP_NATIVE_MODULE,
156
- ) as unknown as RNFBAppModuleInterface;
157
- return RNFBAppModule.metaGetAll();
126
+ return getAppModule().metaGetAll();
158
127
  }
159
128
 
160
129
  /**
@@ -162,10 +131,7 @@ export function metaGetAll(): Promise<{ [key: string]: string | boolean }> {
162
131
  * @returns map of key / value pairs containing native firebase.json constants
163
132
  */
164
133
  export function jsonGetAll(): Promise<{ [key: string]: string | boolean }> {
165
- const RNFBAppModule = getReactNativeModule(
166
- APP_NATIVE_MODULE,
167
- ) as unknown as RNFBAppModuleInterface;
168
- return RNFBAppModule.jsonGetAll();
134
+ return getAppModule().jsonGetAll();
169
135
  }
170
136
 
171
137
  /**
@@ -173,10 +139,7 @@ export function jsonGetAll(): Promise<{ [key: string]: string | boolean }> {
173
139
  * @returns Promise<void>
174
140
  */
175
141
  export function preferencesClearAll(): Promise<void> {
176
- const RNFBAppModule = getReactNativeModule(
177
- APP_NATIVE_MODULE,
178
- ) as unknown as RNFBAppModuleInterface;
179
- return RNFBAppModule.preferencesClearAll();
142
+ return getAppModule().preferencesClearAll();
180
143
  }
181
144
 
182
145
  /**
@@ -184,10 +147,7 @@ export function preferencesClearAll(): Promise<void> {
184
147
  * @returns map of key / value pairs containing native preferences data
185
148
  */
186
149
  export function preferencesGetAll(): Promise<{ [key: string]: string | boolean }> {
187
- const RNFBAppModule = getReactNativeModule(
188
- APP_NATIVE_MODULE,
189
- ) as unknown as RNFBAppModuleInterface;
190
- return RNFBAppModule.preferencesGetAll();
150
+ return getAppModule().preferencesGetAll();
191
151
  }
192
152
 
193
153
  /**
@@ -197,10 +157,7 @@ export function preferencesGetAll(): Promise<{ [key: string]: string | boolean }
197
157
  * @returns Promise<void>
198
158
  */
199
159
  export function preferencesSetBool(key: string, value: boolean): Promise<void> {
200
- const RNFBAppModule = getReactNativeModule(
201
- APP_NATIVE_MODULE,
202
- ) as unknown as RNFBAppModuleInterface;
203
- return RNFBAppModule.preferencesSetBool(key, value);
160
+ return getAppModule().preferencesSetBool(key, value);
204
161
  }
205
162
 
206
163
  /**
@@ -210,10 +167,28 @@ export function preferencesSetBool(key: string, value: boolean): Promise<void> {
210
167
  * @returns Promise<void>
211
168
  */
212
169
  export function preferencesSetString(key: string, value: string): Promise<void> {
213
- const RNFBAppModule = getReactNativeModule(
214
- APP_NATIVE_MODULE,
215
- ) as unknown as RNFBAppModuleInterface;
216
- return RNFBAppModule.preferencesSetString(key, value);
170
+ return getAppModule().preferencesSetString(key, value);
217
171
  }
218
172
 
219
- export const SDK_VERSION = sdkVersion;
173
+ export const SDK_VERSION: string = sdkVersion;
174
+
175
+ /**
176
+ * Returns the {@link Utils.Module} instance for the default or given {@link ReactNativeFirebase.FirebaseApp}.
177
+ *
178
+ * @param app - The Firebase app to use. When omitted, the default app is used.
179
+ */
180
+ export function getUtils(app?: ReactNativeFirebase.FirebaseApp): Utils.Module {
181
+ return getUtilsImpl(app);
182
+ }
183
+
184
+ /**
185
+ * Native device file paths for use with file-based APIs such as Storage `putFile` or `writeToFile`.
186
+ */
187
+ export const FilePath: Utils.FilePath = new Proxy({} as Utils.FilePath, {
188
+ get(_target, prop: string | symbol) {
189
+ if (typeof prop === 'string') {
190
+ return UtilsStatics.FilePath[prop as keyof Utils.FilePath];
191
+ }
192
+ return undefined;
193
+ },
194
+ });
package/lib/types/app.ts CHANGED
@@ -26,6 +26,11 @@ export namespace ReactNativeFirebase {
26
26
  */
27
27
  readonly code: string;
28
28
 
29
+ /**
30
+ * Optional extra data included with the error, matching FirebaseError from `@firebase/app`.
31
+ */
32
+ customData?: Record<string, unknown>;
33
+
29
34
  /**
30
35
  * Firebase error message
31
36
  */
@@ -130,7 +135,6 @@ export namespace ReactNativeFirebase {
130
135
  /**
131
136
  * Base interface for FirebaseApp containing core properties and methods.
132
137
  * The concrete FirebaseApp class implements this interface.
133
- * Module-specific methods (auth(), analytics(), etc.) are added to FirebaseApp via declaration merging.
134
138
  */
135
139
  export interface FirebaseAppBase {
136
140
  /**
@@ -152,21 +156,12 @@ export namespace ReactNativeFirebase {
152
156
  * Make this app unusable and free up resources.
153
157
  */
154
158
  delete(): Promise<void>;
155
-
156
- utils(): Utils.Module;
157
159
  }
158
160
 
159
161
  /**
160
162
  * Full FirebaseApp interface that extends the base interface.
161
- * Module-specific methods (auth(), analytics(), etc.) are added here via declaration merging
162
- * from individual package .d.ts files.
163
163
  */
164
- export interface FirebaseApp extends FirebaseAppBase {
165
- // Module methods are added here via declaration merging, e.g.:
166
- // auth(): FirebaseAuthTypes.Module;
167
- // analytics(): FirebaseAnalyticsTypes.Module;
168
- // etc.
169
- }
164
+ export interface FirebaseApp extends FirebaseAppBase {}
170
165
 
171
166
  /**
172
167
  * Interface for a supplied `AsyncStorage`.
@@ -15,8 +15,6 @@
15
15
  *
16
16
  */
17
17
 
18
- import type { ReactNativeFirebase, Utils } from './app';
19
-
20
18
  /**
21
19
  * Internal types for React Native Firebase
22
20
  * These types are used internally across multiple files and should not be exported to consumers
@@ -29,7 +27,7 @@ import type { ReactNativeFirebase, Utils } from './app';
29
27
  export type FirebaseJsonConfig = Record<string, unknown>;
30
28
 
31
29
  /**
32
- * Configuration for module namespace registration
30
+ * Configuration for a Firebase module instance.
33
31
  */
34
32
  export interface ModuleConfig {
35
33
  namespace: string;
@@ -41,49 +39,6 @@ export interface ModuleConfig {
41
39
  turboModule?: boolean;
42
40
  }
43
41
 
44
- /**
45
- * Extended configuration for namespace registration including native module details
46
- */
47
- export interface NamespaceConfig extends ModuleConfig {
48
- nativeModuleName: string | string[];
49
- nativeEvents: boolean | string[];
50
- // ModuleClass can be FirebaseModule or any subclass of it
51
- // Uses FirebaseAppBase (the concrete class type) rather than FirebaseApp (the augmented interface)
52
- ModuleClass: new (
53
- app: ReactNativeFirebase.FirebaseAppBase,
54
- config: ModuleConfig,
55
- customUrlOrRegion?: string | null,
56
- ) => ReactNativeFirebase.FirebaseModule;
57
- statics?: object;
58
- version?: string;
59
- }
60
-
61
- /**
62
- * Type for a Firebase module getter function that can optionally accept
63
- * a custom URL/region/databaseId parameter
64
- */
65
- export type ModuleGetter = {
66
- (customUrlOrRegionOrDatabaseId?: string): ReactNativeFirebase.FirebaseModule;
67
- [key: string]: unknown;
68
- };
69
-
70
- /**
71
- * Type for Firebase root object with module getters
72
- */
73
- export interface FirebaseRoot {
74
- initializeApp: (
75
- options: ReactNativeFirebase.FirebaseAppOptions,
76
- configOrName?: string | ReactNativeFirebase.FirebaseAppConfig,
77
- ) => Promise<ReactNativeFirebase.FirebaseApp>;
78
- setReactNativeAsyncStorage: (asyncStorage: ReactNativeFirebase.ReactNativeAsyncStorage) => void;
79
- app: (name?: string) => ReactNativeFirebase.FirebaseApp;
80
- apps: ReactNativeFirebase.FirebaseApp[];
81
- SDK_VERSION: string;
82
- setLogLevel: (logLevel: ReactNativeFirebase.LogLevelString) => void;
83
- utils: Utils.Statics & (() => Utils.Module);
84
- [key: string]: unknown;
85
- }
86
-
87
42
  /**
88
43
  * Native error types
89
44
  */