@react-native-firebase/app 25.0.1 → 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 +49 -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
@@ -1,74 +1,18 @@
1
- import { expect, jest } from '@jest/globals';
2
- import { createMessage } from './index';
1
+ /*
2
+ * Copyright (c) 2016-present Invertase Limited & Contributors
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this library except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ *
16
+ */
3
17
 
4
- export const checkV9Deprecation = (modularFunction: () => void, nonModularFunction: () => void) => {
5
- const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
6
- consoleWarnSpy.mockRestore();
7
- modularFunction();
8
- expect(consoleWarnSpy).not.toHaveBeenCalled();
9
- consoleWarnSpy.mockClear();
10
- const consoleWarnSpy2 = jest.spyOn(console, 'warn').mockImplementation(() => {});
11
- nonModularFunction();
12
-
13
- expect(consoleWarnSpy2).toHaveBeenCalledTimes(1);
14
- consoleWarnSpy2.mockClear();
15
- };
16
-
17
- export type CheckV9DeprecationFunction = (
18
- modularFunction: () => void,
19
- nonModularFunction: () => void,
20
- methodNameKey: string,
21
- uniqueMessage?: string | null,
22
- ignoreFirebaseAppDeprecationWarning?: boolean,
23
- ) => void;
24
-
25
- export const withDeprecationWarningsSilenced = <T>(fn: () => T): T => {
26
- const previous = globalThis.RNFB_SILENCE_MODULAR_DEPRECATION_WARNINGS;
27
- globalThis.RNFB_SILENCE_MODULAR_DEPRECATION_WARNINGS = true;
28
- try {
29
- return fn();
30
- } finally {
31
- globalThis.RNFB_SILENCE_MODULAR_DEPRECATION_WARNINGS = previous;
32
- }
33
- };
34
-
35
- export const createCheckV9Deprecation = (moduleNames: string[]): CheckV9DeprecationFunction => {
36
- return (
37
- modularFunction: () => void,
38
- nonModularFunction: () => void,
39
- methodNameKey: string,
40
- uniqueMessage?: string | null,
41
- checkFirebaseAppDeprecationWarning: boolean = false,
42
- ) => {
43
- const moduleName = moduleNames[0] as string; // firestore, database, etc
44
- const instanceName = moduleNames[1] || 'default'; // default, FirestoreCollectionReference, etc
45
- const consoleWarnSpy = jest.spyOn(console, 'warn');
46
- consoleWarnSpy.mockReset();
47
-
48
- consoleWarnSpy.mockImplementation(warnMessage => {
49
- const firebaseAppDeprecationMessage = warnMessage.includes('Please use `getApp()` instead.');
50
- if (checkFirebaseAppDeprecationWarning) {
51
- throw new Error(`Console warn was called unexpectedly with: ${warnMessage}`);
52
- } else {
53
- if (!firebaseAppDeprecationMessage) {
54
- // we want to ignore all firebase app deprecation warnings (e.g. "Please use `getApp()` instead.") unless actually testing for it which we do above
55
- throw new Error(`Console warn was called unexpectedly with: ${warnMessage}`);
56
- }
57
- }
58
- });
59
- // Do not call `mockRestore()` unless removing the spy
60
- modularFunction();
61
- consoleWarnSpy.mockReset();
62
- consoleWarnSpy.mockRestore();
63
- const consoleWarnSpy2 = jest.spyOn(console, 'warn').mockImplementation(warnMessage => {
64
- const message = createMessage(moduleName, methodNameKey, instanceName, uniqueMessage);
65
- if (message) {
66
- expect(warnMessage).toMatch(message);
67
- }
68
- });
69
- nonModularFunction();
70
-
71
- expect(consoleWarnSpy2).toHaveBeenCalledTimes(1);
72
- consoleWarnSpy2.mockReset();
73
- };
74
- };
18
+ export {};
package/lib/index.ts CHANGED
@@ -15,7 +15,6 @@
15
15
  *
16
16
  */
17
17
 
18
- export { firebase, utils, default } from './namespaced';
19
18
  export * from './modular';
20
19
  export type {
21
20
  ReactNativeFirebase,
@@ -22,7 +22,7 @@ export default class NativeFirebaseError extends Error {
22
22
  readonly code!: string;
23
23
  readonly jsStack!: string;
24
24
  readonly userInfo!: NativeErrorUserInfo;
25
- readonly customData: any;
25
+ readonly customData!: Record<string, unknown> | null;
26
26
  readonly operationType!: string | null;
27
27
  readonly nativeErrorCode!: string | number | null;
28
28
  readonly nativeErrorMessage!: string | null;
@@ -16,6 +16,7 @@
16
16
  */
17
17
 
18
18
  import { type EmitterSubscription, NativeEventEmitter } from 'react-native';
19
+ import { APP_NATIVE_MODULE } from './constants';
19
20
  import { getReactNativeModule } from './nativeModule';
20
21
  import type { RNFBAppModuleInterface } from './NativeModules';
21
22
 
@@ -38,10 +39,11 @@ class RNFBNativeEventEmitter extends NativeEventEmitter {
38
39
  ready: boolean;
39
40
 
40
41
  constructor() {
41
- const RNFBAppModule = getReactNativeModule('RNFBAppModule');
42
+ // NewArch-AD-18 E1: NativeEventEmitter must receive the same raw host that implements addListener/removeListeners.
43
+ const RNFBAppModule = getReactNativeModule(APP_NATIVE_MODULE);
42
44
  if (!RNFBAppModule) {
43
45
  throw new Error(
44
- 'Native module RNFBAppModule not found. Re-check module install, linking, configuration, build and install steps.',
46
+ `Native module ${APP_NATIVE_MODULE} not found. Re-check module install, linking, configuration, build and install steps.`,
45
47
  );
46
48
  }
47
49
  // Cast to any for NativeEventEmitter constructor which expects React Native's NativeModule type
@@ -54,8 +56,9 @@ class RNFBNativeEventEmitter extends NativeEventEmitter {
54
56
  listener: (...args: unknown[]) => unknown,
55
57
  context?: object,
56
58
  ): EmitterSubscription {
59
+ // NewArch-AD-18 E1: event bridge identity requires raw app-module host for listener registration.
57
60
  const RNFBAppModule = getReactNativeModule(
58
- 'RNFBAppModule',
61
+ APP_NATIVE_MODULE,
59
62
  ) as unknown as RNFBAppModuleInterface;
60
63
  if (!this.ready) {
61
64
  (RNFBAppModule.eventsNotifyReady as EventsNotifyReadyMethod)(true);
@@ -99,7 +102,8 @@ class RNFBNativeEventEmitter extends NativeEventEmitter {
99
102
  // we will modify it to do our native unsubscription then call the original
100
103
  const originalRemove = subscription.remove;
101
104
  const newRemove = () => {
102
- const module = getReactNativeModule('RNFBAppModule') as unknown as RNFBAppModuleInterface;
105
+ // NewArch-AD-18 E1: raw host for eventsRemoveListener on unsubscribe.
106
+ const module = getReactNativeModule(APP_NATIVE_MODULE) as unknown as RNFBAppModuleInterface;
103
107
  (module.eventsRemoveListener as EventsRemoveListenerMethod)(eventType, false);
104
108
  const superClass = Object.getPrototypeOf(Object.getPrototypeOf(this));
105
109
  if (superClass.removeSubscription != null) {
@@ -115,8 +119,9 @@ class RNFBNativeEventEmitter extends NativeEventEmitter {
115
119
  }
116
120
 
117
121
  removeAllListeners(eventType: string): void {
122
+ // NewArch-AD-18 E1: raw host for eventsRemoveListener.
118
123
  const RNFBAppModule = getReactNativeModule(
119
- 'RNFBAppModule',
124
+ APP_NATIVE_MODULE,
120
125
  ) as unknown as RNFBAppModuleInterface;
121
126
  (RNFBAppModule.eventsRemoveListener as EventsRemoveListenerMethod)(eventType, true);
122
127
  super.removeAllListeners(`rnfb_${eventType}`);
@@ -124,8 +129,9 @@ class RNFBNativeEventEmitter extends NativeEventEmitter {
124
129
 
125
130
  // This is likely no longer ever called, but it is here for backwards compatibility with RN <= 0.64
126
131
  removeSubscription(subscription: EmitterSubscription & { eventType?: string }): void {
132
+ // NewArch-AD-18 E1: raw host for legacy removeSubscription path.
127
133
  const RNFBAppModule = getReactNativeModule(
128
- 'RNFBAppModule',
134
+ APP_NATIVE_MODULE,
129
135
  ) as unknown as RNFBAppModuleInterface;
130
136
  const eventType = subscription.eventType?.replace('rnfb_', '') || '';
131
137
  (RNFBAppModule.eventsRemoveListener as EventsRemoveListenerMethod)(eventType, false);
@@ -15,30 +15,8 @@
15
15
  *
16
16
  */
17
17
 
18
- export const APP_NATIVE_MODULE = 'RNFBAppModule';
18
+ export const APP_NATIVE_MODULE = 'NativeRNFBTurboApp';
19
19
 
20
- export const DEFAULT_APP_NAME = '[DEFAULT]';
21
-
22
- export const KNOWN_NAMESPACES = [
23
- 'appCheck',
24
- 'appDistribution',
25
- 'auth',
26
- 'analytics',
27
- 'remoteConfig',
28
- 'crashlytics',
29
- 'database',
30
- 'inAppMessaging',
31
- 'installations',
32
- 'firestore',
33
- 'functions',
34
- 'indexing',
35
- 'storage',
36
- 'messaging',
37
- 'naturalLanguage',
38
- 'ml',
39
- 'notifications',
40
- 'perf',
41
- 'utils',
42
- ] as const;
20
+ export const UTILS_NATIVE_MODULE = 'NativeRNFBTurboUtils';
43
21
 
44
- export type KnownNamespace = (typeof KNOWN_NAMESPACES)[number];
22
+ export const DEFAULT_APP_NAME = '[DEFAULT]';
@@ -24,10 +24,6 @@ declare global {
24
24
  var RNFBTest: boolean | undefined;
25
25
  var RNFBDebugInTestLeakDetection: boolean | undefined;
26
26
  var RNFBDebugLastTest: string | undefined;
27
-
28
- // Modular API deprecation flags
29
- var RNFB_SILENCE_MODULAR_DEPRECATION_WARNINGS: boolean | undefined;
30
- var RNFB_MODULAR_DEPRECATION_STRICT_MODE: boolean | undefined;
31
27
  }
32
28
 
33
29
  export {};
@@ -21,7 +21,7 @@ export { default as FirebaseModule } from './FirebaseModule';
21
21
  export { default as NativeFirebaseError } from './NativeFirebaseError';
22
22
  export * from './NativeModules';
23
23
  export * from './registry/app';
24
- export * from './registry/namespace';
24
+ export * from './registry/modular';
25
25
  export * from './registry/nativeModule';
26
26
  export { default as SharedEventEmitter } from './SharedEventEmitter';
27
27
  export { Logger } from './logger';
@@ -1,30 +1,85 @@
1
1
  /* eslint-disable no-console */
2
- import { NativeModules } from 'react-native';
2
+ import { TurboModuleRegistry } from 'react-native';
3
+
4
+ const DYNAMIC_CONSTANT_KEYS = new Set(['androidPlayServices']);
5
+
6
+ const memoizedModuleConstants = new Map<string, Record<string, unknown>>();
7
+ const memoizedDebugProxies = new Map<string, Record<string, unknown>>();
8
+
9
+ function withTurboConstants(
10
+ moduleName: string,
11
+ module: Record<string, unknown> | undefined,
12
+ ): Record<string, unknown> | undefined {
13
+ if (!module) {
14
+ return module;
15
+ }
16
+
17
+ const getConstants = module.getConstants;
18
+ if (typeof getConstants !== 'function') {
19
+ return module;
20
+ }
21
+
22
+ let constants = memoizedModuleConstants.get(moduleName);
23
+ if (!constants) {
24
+ constants = (getConstants as () => Record<string, unknown>).call(module) || {};
25
+ memoizedModuleConstants.set(moduleName, constants);
26
+ }
27
+
28
+ const descriptors: PropertyDescriptorMap = {};
29
+ for (const key of Object.keys(constants)) {
30
+ if (DYNAMIC_CONSTANT_KEYS.has(key)) {
31
+ continue;
32
+ }
33
+ descriptors[key] = {
34
+ value: constants[key],
35
+ enumerable: true,
36
+ writable: true,
37
+ configurable: true,
38
+ };
39
+ }
40
+
41
+ return Object.create(module, descriptors);
42
+ }
3
43
 
4
44
  /**
5
- * This is used by Android and iOS to get a native module.
6
- * We additionally add a Proxy to the module to intercept calls
7
- * and log them to the console for debugging purposes, if enabled.
8
- * @param moduleName
9
- * @returns Raw native module from React Native (object with methods/properties or undefined)
45
+ * Unified native module resolver TurboModule only (NewArch-AD-6 Phase R).
10
46
  */
11
- export function getReactNativeModule(moduleName: string): Record<string, unknown> | undefined {
12
- const nativeModule = NativeModules[moduleName];
47
+ export function getReactNativeModule(moduleName: string): Record<string, unknown> {
48
+ const turboModule = TurboModuleRegistry.get(moduleName);
49
+ if (!turboModule) {
50
+ throw new Error(`Native module ${moduleName} is not registered.`);
51
+ }
52
+
53
+ const nativeModule = withTurboConstants(
54
+ moduleName,
55
+ turboModule as Record<string, unknown>,
56
+ ) as Record<string, unknown>;
57
+
13
58
  if (!globalThis.RNFBDebug) {
14
59
  return nativeModule;
15
60
  }
16
- return new Proxy(nativeModule, {
61
+
62
+ let debugProxy = memoizedDebugProxies.get(moduleName);
63
+ if (debugProxy) {
64
+ return debugProxy;
65
+ }
66
+
67
+ debugProxy = new Proxy(nativeModule as Record<string, unknown>, {
17
68
  ownKeys(target) {
18
- return Object.keys(target);
69
+ const keys: string[] = [];
70
+ for (const key in target) {
71
+ keys.push(key);
72
+ }
73
+ return keys;
19
74
  },
20
75
  get: (_, name) => {
21
- const prop = nativeModule[name as string];
76
+ const prop = (nativeModule as Record<string, unknown>)[name as string];
22
77
  if (typeof prop !== 'function') return prop;
23
78
  return (...args: unknown[]) => {
24
79
  console.debug(
25
80
  `[RNFB->Native][🔵] ${moduleName}.${String(name)} -> ${JSON.stringify(args)}`,
26
81
  );
27
- const result: unknown = (prop as (...args: unknown[]) => unknown)(...args);
82
+ const result: unknown = (prop as (...args: unknown[]) => unknown).apply(nativeModule, args);
28
83
  if (result && typeof result === 'object' && 'then' in result) {
29
84
  return (result as Promise<unknown>).then(
30
85
  (res: unknown) => {
@@ -48,6 +103,8 @@ export function getReactNativeModule(moduleName: string): Record<string, unknown
48
103
  };
49
104
  },
50
105
  });
106
+ memoizedDebugProxies.set(moduleName, debugProxy);
107
+ return debugProxy;
51
108
  }
52
109
 
53
110
  export function setReactNativeModule(): void {
@@ -1,7 +1,12 @@
1
1
  /* eslint-disable no-console */
2
2
  import RNFBAppModule from './web/RNFBAppModule';
3
+ import { APP_NATIVE_MODULE } from './constants';
3
4
 
4
- const nativeModuleRegistry: Record<string, Record<string, unknown>> = {};
5
+ // Register before exporting getters RNFBNativeEventEmitter instantiates during circular imports.
6
+ const nativeModuleRegistry: Record<string, Record<string, unknown>> = {
7
+ RNFBAppModule: RNFBAppModule as unknown as Record<string, unknown>,
8
+ [APP_NATIVE_MODULE]: RNFBAppModule as unknown as Record<string, unknown>,
9
+ };
5
10
 
6
11
  export function getReactNativeModule(moduleName: string): Record<string, unknown> | undefined {
7
12
  const nativeModule = nativeModuleRegistry[moduleName];
@@ -14,8 +19,11 @@ export function getReactNativeModule(moduleName: string): Record<string, unknown
14
19
  }
15
20
  return new Proxy(nativeModule, {
16
21
  ownKeys(target) {
17
- // FIXME - test in new arch context - I don't think Object.keys works
18
- return Object.keys(target);
22
+ const keys: string[] = [];
23
+ for (const key in target) {
24
+ keys.push(key);
25
+ }
26
+ return keys;
19
27
  },
20
28
  get: (_, name) => {
21
29
  const prop = nativeModule[name as string];
@@ -56,5 +64,3 @@ export function setReactNativeModule(
56
64
  ): void {
57
65
  nativeModuleRegistry[moduleName] = nativeModule;
58
66
  }
59
-
60
- setReactNativeModule('RNFBAppModule', RNFBAppModule);
@@ -15,16 +15,7 @@
15
15
  *
16
16
  */
17
17
 
18
- import {
19
- isIOS,
20
- isOther,
21
- isNull,
22
- warnIfNotModularCall,
23
- isObject,
24
- isFunction,
25
- isString,
26
- isUndefined,
27
- } from '../../common';
18
+ import { isIOS, isOther, isNull, isObject, isFunction, isString, isUndefined } from '../../common';
28
19
  import FirebaseApp from '../../FirebaseApp';
29
20
  import { DEFAULT_APP_NAME } from '../constants';
30
21
  import { setReactNativeAsyncStorageInternal } from '../asyncStorage';
@@ -38,7 +29,7 @@ type ReactNativeAsyncStorage = ReactNativeFirebase.ReactNativeAsyncStorage;
38
29
 
39
30
  const APP_REGISTRY: Record<string, FirebaseApp> = {};
40
31
  let onAppCreateFn: ((app: FirebaseApp) => void) | null = null;
41
- let onAppDestroyFn: ((app: FirebaseApp) => void) | null = null;
32
+ const onAppDestroyCallbacks: Array<(app: FirebaseApp) => void> = [];
42
33
  let initializedNativeApps = false;
43
34
 
44
35
  /**
@@ -50,11 +41,11 @@ export function setOnAppCreate(fn: (app: FirebaseApp) => void): void {
50
41
  }
51
42
 
52
43
  /**
53
- * This was needed to avoid metro require cycles...
44
+ * Registers an app-destroy listener.
54
45
  * @param fn
55
46
  */
56
- export function setOnAppDestroy(fn: (app: FirebaseApp) => void): void {
57
- onAppDestroyFn = fn;
47
+ export function addOnAppDestroy(fn: (app: FirebaseApp) => void): void {
48
+ onAppDestroyCallbacks.push(fn);
58
49
  }
59
50
 
60
51
  /**
@@ -93,7 +84,6 @@ export function initializeNativeApps(): void {
93
84
  * @param name
94
85
  */
95
86
  export function getApp(name: string = DEFAULT_APP_NAME): ReactNativeFirebase.FirebaseApp {
96
- warnIfNotModularCall(arguments, 'getApp()');
97
87
  if (!initializedNativeApps) {
98
88
  initializeNativeApps();
99
89
  }
@@ -110,7 +100,6 @@ export function getApp(name: string = DEFAULT_APP_NAME): ReactNativeFirebase.Fir
110
100
  * Gets all app instances, used for `firebase.apps`
111
101
  */
112
102
  export function getApps(): ReactNativeFirebase.FirebaseApp[] {
113
- warnIfNotModularCall(arguments, 'getApps()');
114
103
  if (!initializedNativeApps) {
115
104
  initializeNativeApps();
116
105
  }
@@ -126,7 +115,6 @@ export function initializeApp(
126
115
  options: Partial<FirebaseAppOptions> = {},
127
116
  configOrName?: string | FirebaseAppConfig,
128
117
  ): Promise<ReactNativeFirebase.FirebaseApp> {
129
- warnIfNotModularCall(arguments, 'initializeApp()');
130
118
  let appConfig: FirebaseAppConfig = configOrName as FirebaseAppConfig;
131
119
 
132
120
  if (!isObject(configOrName) || isNull(configOrName)) {
@@ -220,7 +208,6 @@ export function initializeApp(
220
208
  }
221
209
 
222
210
  export function setLogLevel(logLevel: ReactNativeFirebase.LogLevelString): void {
223
- warnIfNotModularCall(arguments, 'setLogLevel()');
224
211
  if (!['error', 'warn', 'info', 'debug', 'verbose'].includes(logLevel)) {
225
212
  throw new Error('LogLevel must be one of "error", "warn", "info", "debug", "verbose"');
226
213
  }
@@ -233,8 +220,6 @@ export function setLogLevel(logLevel: ReactNativeFirebase.LogLevelString): void
233
220
  }
234
221
 
235
222
  export function setReactNativeAsyncStorage(asyncStorage: ReactNativeAsyncStorage): void {
236
- warnIfNotModularCall(arguments, 'setReactNativeAsyncStorage()');
237
-
238
223
  if (!isObject(asyncStorage)) {
239
224
  throw new Error("setReactNativeAsyncStorage(*) 'asyncStorage' must be an object.");
240
225
  }
@@ -272,7 +257,24 @@ export function deleteApp(name: string, nativeInitialized: boolean): Promise<voi
272
257
 
273
258
  return nativeModule.deleteApp(name).then(() => {
274
259
  (app as any)._deleted = true;
275
- onAppDestroyFn?.(app);
276
- delete APP_REGISTRY[name];
260
+ try {
261
+ for (let i = 0; i < onAppDestroyCallbacks.length; i++) {
262
+ try {
263
+ onAppDestroyCallbacks[i]?.(app);
264
+ } catch (e) {
265
+ // A failing destroy listener must not prevent the remaining listeners (or the
266
+ // registry cleanup below) from running. Surface it for diagnosis — partial cleanup
267
+ // here can leave a module's native/JS state stale for the deleted app.
268
+ // eslint-disable-next-line no-console
269
+ console.warn(
270
+ `Firebase: an onAppDestroy callback (index ${i}) for app '${name}' threw and was ` +
271
+ 'skipped; remaining callbacks will still run. Inspect the offending listener:',
272
+ e,
273
+ );
274
+ }
275
+ }
276
+ } finally {
277
+ delete APP_REGISTRY[name];
278
+ }
277
279
  });
278
280
  }
@@ -0,0 +1,97 @@
1
+ /*
2
+ * Copyright (c) 2016-present Invertase Limited & Contributors
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this library except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ *
16
+ */
17
+
18
+ import { DEFAULT_APP_NAME } from '../constants';
19
+ import type FirebaseModule from '../FirebaseModule';
20
+ import type { ReactNativeFirebase } from '../../types/app';
21
+ import type { ModuleConfig } from '../../types/internal';
22
+ import { addOnAppDestroy, getApp } from './app';
23
+
24
+ /**
25
+ * Constructor signature shared by every {@link FirebaseModule} subclass.
26
+ */
27
+ type ModularModuleClass<T extends FirebaseModule> = new (
28
+ app: ReactNativeFirebase.FirebaseAppBase,
29
+ config: ModuleConfig,
30
+ customUrlOrRegion?: string | null,
31
+ ) => T;
32
+
33
+ // app.name -> instanceKey -> module instance
34
+ const MODULAR_INSTANCES: Record<string, Record<string, FirebaseModule>> = {};
35
+
36
+ let destroyHookRegistered = false;
37
+ function ensureDestroyHook(): void {
38
+ if (destroyHookRegistered) {
39
+ return;
40
+ }
41
+ destroyHookRegistered = true;
42
+ addOnAppDestroy(app => {
43
+ delete MODULAR_INSTANCES[app.name];
44
+ });
45
+ }
46
+
47
+ /**
48
+ * Builds (and memoises per app) a Firebase service instance for the modular API.
49
+ *
50
+ * Each module's `index.ts` defines its own `ModuleClass` and `ModuleConfig` and calls this from
51
+ * `getX(app?)`.
52
+ *
53
+ * @param ModuleClass - The module's `FirebaseModule` subclass.
54
+ * @param config - Static `ModuleConfig` for the module (namespace, native module name, support flags).
55
+ * @param app - The `FirebaseApp` to use; defaults to the default app.
56
+ * @param customUrlOrRegion - Optional URL/region/databaseId for modules that support it.
57
+ * @returns The memoised module instance for that app + key.
58
+ */
59
+ export function getOrCreateModularInstance<T extends FirebaseModule>(
60
+ ModuleClass: ModularModuleClass<T>,
61
+ config: ModuleConfig,
62
+ app?: ReactNativeFirebase.FirebaseApp,
63
+ customUrlOrRegion?: string | null,
64
+ ): T {
65
+ ensureDestroyHook();
66
+
67
+ const _app = getApp(app?.name);
68
+
69
+ const { namespace, hasMultiAppSupport } = config;
70
+
71
+ // Modules such as analytics only run on the default app.
72
+ if (!hasMultiAppSupport && _app.name !== DEFAULT_APP_NAME) {
73
+ throw new Error(
74
+ [
75
+ `You attempted to call "${namespace}" with a secondary Firebase App but; ${namespace} does not support multiple Firebase Apps.`,
76
+ '',
77
+ `Ensure you access ${namespace} from the default application only.`,
78
+ ].join('\r\n'),
79
+ );
80
+ }
81
+
82
+ const key = customUrlOrRegion ? `${customUrlOrRegion}:${namespace}` : namespace;
83
+
84
+ if (!MODULAR_INSTANCES[_app.name]) {
85
+ MODULAR_INSTANCES[_app.name] = {};
86
+ }
87
+
88
+ if (!MODULAR_INSTANCES[_app.name]![key]) {
89
+ MODULAR_INSTANCES[_app.name]![key] = new ModuleClass(
90
+ _app as unknown as ReactNativeFirebase.FirebaseAppBase,
91
+ Object.assign({}, config),
92
+ customUrlOrRegion,
93
+ );
94
+ }
95
+
96
+ return MODULAR_INSTANCES[_app.name]![key] as T;
97
+ }