diff --git a/.github/scripts/compare-types/packages/installations/config.ts b/.github/scripts/compare-types/packages/installations/config.ts new file mode 100644 index 0000000000..4ec85fb7a7 --- /dev/null +++ b/.github/scripts/compare-types/packages/installations/config.ts @@ -0,0 +1,93 @@ +/** + * Known differences between the firebase-js-sdk @firebase/installations public + * API and the @react-native-firebase/installations modular API. + * + * Each entry must have a `name` (the export name) and a `reason` explaining + * why the difference exists. Any difference NOT listed here will cause CI to + * fail so that new drift is caught and deliberately acknowledged. + * + * Sections: + * nameMapping — exports that exist in both packages but under different names + * missingInRN — firebase-js-sdk exports absent from RN Firebase + * extraInRN — RN Firebase exports not present in the firebase-js-sdk + * differentShape — exports present in both but with differing signatures/members + */ + +import type { PackageConfig } from '../../src/types'; + +const config: PackageConfig = { + // --------------------------------------------------------------------------- + // Name mapping + // --------------------------------------------------------------------------- + nameMapping: {}, + + // --------------------------------------------------------------------------- + // Missing in RN Firebase + // --------------------------------------------------------------------------- + missingInRN: [ + { + name: 'IdChangeCallbackFn', + reason: + 'The firebase-js-sdk exports this type alias for the callback passed to ' + + '`onIdChange`. RN Firebase does not export it; the callback is typed inline. ' + + '`onIdChange` itself is present but throws at runtime (unsupported).', + }, + { + name: 'IdChangeUnsubscribeFn', + reason: + 'The firebase-js-sdk exports this type alias for the unsubscribe return of ' + + '`onIdChange`. RN Firebase does not export it; the return type is inline `() => void`. ' + + '`onIdChange` itself is present but throws at runtime (unsupported).', + }, + ], + + // --------------------------------------------------------------------------- + // Extra in RN Firebase + // --------------------------------------------------------------------------- + extraInRN: [ + { + name: 'Statics', + reason: + 'RN Firebase exposes a `Statics` interface (e.g. `SDK_VERSION`) for package ' + + 'version and static metadata. The firebase-js-sdk does not expose an equivalent ' + + 'named type in its public installations API.', + }, + { + name: 'FirebaseInstallationsTypes', + reason: + 'RN Firebase exports the deprecated `FirebaseInstallationsTypes` namespace for ' + + 'backwards compatibility with the namespaced API. The firebase-js-sdk has no ' + + 'equivalent; it only exposes the modular API.', + }, + ], + + // --------------------------------------------------------------------------- + // Different shape + // --------------------------------------------------------------------------- + differentShape: [ + { + name: 'getInstallations', + reason: + 'The optional `app` parameter is typed as `ReactNativeFirebase.FirebaseApp` in RN Firebase ' + + 'and as `FirebaseApp` from `@firebase/app` in the firebase-js-sdk. Both represent the app ' + + 'instance; the RN type comes from the React Native Firebase app package.', + }, + { + name: 'Installations', + reason: + 'The `app` property is typed as `ReactNativeFirebase.FirebaseApp` in RN Firebase ' + + 'and as `FirebaseApp` from `@firebase/app` in the firebase-js-sdk. Both represent ' + + 'the app instance; the RN type comes from the React Native Firebase app package.', + }, + { + name: 'onIdChange', + reason: + 'RN Firebase exposes `onIdChange` with the same signature as the firebase-js-sdk ' + + 'for API compatibility, but it is not implemented: it throws at runtime with a message ' + + 'that the method is unsupported. The JS SDK supports ID change callbacks; the native ' + + 'RN implementations do not yet provide this.', + }, + ], +}; + +export default config; diff --git a/.github/scripts/compare-types/packages/installations/installations-js-sdk.d.ts b/.github/scripts/compare-types/packages/installations/installations-js-sdk.d.ts new file mode 100644 index 0000000000..9d815fd1da --- /dev/null +++ b/.github/scripts/compare-types/packages/installations/installations-js-sdk.d.ts @@ -0,0 +1,85 @@ +/** + * The Firebase Installations Web SDK. + * This SDK does not work in a Node.js environment. + * + * @packageDocumentation + */ + +import { FirebaseApp } from '@firebase/app'; + +/** + * Deletes the Firebase Installation and all associated data. + * @param installations - The `Installations` instance. + * + * @public + */ +export declare function deleteInstallations(installations: Installations): Promise; + +/* Excluded from this release type: _FirebaseInstallationsInternal */ + +/** + * Creates a Firebase Installation if there isn't one for the app and + * returns the Installation ID. + * @param installations - The `Installations` instance. + * + * @public + */ +export declare function getId(installations: Installations): Promise; + +/** + * Returns an instance of {@link Installations} associated with the given + * {@link @firebase/app#FirebaseApp} instance. + * @param app - The {@link @firebase/app#FirebaseApp} instance. + * + * @public + */ +export declare function getInstallations(app?: FirebaseApp): Installations; + +/** + * Returns a Firebase Installations auth token, identifying the current + * Firebase Installation. + * @param installations - The `Installations` instance. + * @param forceRefresh - Force refresh regardless of token expiration. + * + * @public + */ +export declare function getToken(installations: Installations, forceRefresh?: boolean): Promise; + +/** + * An user defined callback function that gets called when Installations ID changes. + * + * @public + */ +export declare type IdChangeCallbackFn = (installationId: string) => void; + +/** + * Unsubscribe a callback function previously added via {@link IdChangeCallbackFn}. + * + * @public + */ +export declare type IdChangeUnsubscribeFn = () => void; + +/** + * Public interface of the Firebase Installations SDK. + * + * @public + */ +export declare interface Installations { + /** + * The {@link @firebase/app#FirebaseApp} this `Installations` instance is associated with. + */ + app: FirebaseApp; +} + +/** + * Sets a new callback that will get called when Installation ID changes. + * Returns an unsubscribe function that will remove the callback when called. + * @param installations - The `Installations` instance. + * @param callback - The callback function that is invoked when FID changes. + * @returns A function that can be called to unsubscribe. + * + * @public + */ +export declare function onIdChange(installations: Installations, callback: IdChangeCallbackFn): IdChangeUnsubscribeFn; + +export { } \ No newline at end of file diff --git a/.github/scripts/compare-types/src/registry.ts b/.github/scripts/compare-types/src/registry.ts index 30aac48c24..3a063657e8 100644 --- a/.github/scripts/compare-types/src/registry.ts +++ b/.github/scripts/compare-types/src/registry.ts @@ -50,5 +50,23 @@ function rnDist(packageName: string): string { } export const packages: PackageEntry[] = [ - + { + name: 'installations', + firebaseSdkTypesPath: path.join( + SCRIPT_DIR, + 'packages', + 'installations', + 'installations-js-sdk.d.ts', + ), + rnFirebaseModularFiles: [ + path.join(rnDist('installations'), 'types', 'installations.d.ts'), + path.join(rnDist('installations'), 'modular.d.ts'), + ], + rnFirebaseSupportFiles: [ + path.join(rnDist('installations'), 'types', 'namespaced.d.ts'), + path.join(rnDist('installations'), 'types', 'internal.d.ts'), + ], + config: require(path.join(SCRIPT_DIR, 'packages', 'installations', 'config')) + .default as PackageConfig, + }, ]; diff --git a/.github/scripts/compare-types/tsconfig.json b/.github/scripts/compare-types/tsconfig.json index 82fc8523ad..e4fab5729c 100644 --- a/.github/scripts/compare-types/tsconfig.json +++ b/.github/scripts/compare-types/tsconfig.json @@ -7,7 +7,7 @@ "esModuleInterop": true, "resolveJsonModule": true, "outDir": "dist", - "rootDir": "src" + "rootDir": "." }, "include": ["src/**/*.ts", "packages/**/*.ts"] } diff --git a/packages/installations/__tests__/installations.test.ts b/packages/installations/__tests__/installations.test.ts index 8c76b1cd84..1a0f08203c 100644 --- a/packages/installations/__tests__/installations.test.ts +++ b/packages/installations/__tests__/installations.test.ts @@ -109,6 +109,7 @@ describe('installations()', function () { const installations = getInstallations(); installationsV9Deprecation( () => deleteInstallations(installations), + // @ts-expect-error Combines modular and namespace API () => installations.delete(), 'delete', ); @@ -118,6 +119,7 @@ describe('installations()', function () { const installations = getInstallations(); installationsV9Deprecation( () => getId(installations), + // @ts-expect-error Combines modular and namespace API () => installations.getId(), 'getId', ); @@ -127,6 +129,7 @@ describe('installations()', function () { const installations = getInstallations(); installationsV9Deprecation( () => getToken(installations), + // @ts-expect-error Combines modular and namespace API () => installations.getToken(), 'getToken', ); diff --git a/packages/installations/lib/index.d.ts b/packages/installations/lib/index.d.ts deleted file mode 100644 index 47b4434dc0..0000000000 --- a/packages/installations/lib/index.d.ts +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (c) 2016-present Invertase Limited & Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this library 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. - * - */ - -import { ReactNativeFirebase } from '@react-native-firebase/app'; - -/** - * Firebase Installations package for React Native. - * - * #### Example 1 - * - * Access the firebase export from the `installations` package: - * - * ```js - * import { firebase } from '@react-native-firebase/installations'; - * - * // firebase.installations().X - * ``` - * - * #### Example 2 - * - * Using the default export from the `installations` package: - * - * ```js - * import installations from '@react-native-firebase/installations'; - * - * // installations().X - * ``` - * - * #### Example 3 - * - * Using the default export from the `app` package: - * - * ```js - * import firebase from '@react-native-firebase/app'; - * import '@react-native-firebase/installations'; - * - * // firebase.installations().X - * ``` - * - * @firebase installations - */ -export namespace FirebaseInstallationsTypes { - import FirebaseModule = ReactNativeFirebase.FirebaseModule; - - export interface Statics { - SDK_VERSION: string; - } - - /** - * The Firebase Installations service is available for the default app or a given app. - * - * #### Example 1 - * - * Get the installations instance for the **default app**: - * - * ```js - * const installationsForDefaultApp = firebase.installations(); - * ``` - * - * #### Example 2 - * - * Get the installations instance for a **secondary app**: - *˚ - * ```js - * const otherApp = firebase.app('otherApp'); - * const installationsForOtherApp = firebase.installations(otherApp); - * ``` - * - */ - export class Module extends FirebaseModule { - /** - * The current `FirebaseApp` instance for this Firebase service. - */ - app: ReactNativeFirebase.FirebaseApp; - - /** - * Creates a Firebase Installation if there isn't one for the app and - * returns the Installation ID. The installation ID is a globally unique, - * stable, URL-safe base64 string identifier that uniquely identifies the app instance. - * NOTE: If the application already has an existing FirebaseInstanceID then the InstanceID identifier will be used. - * - * @return Firebase Installation ID, this is an url-safe base64 string of a 128-bit integer. - */ - getId(): Promise; - - /** - * Retrieves (locally or from the server depending on forceRefresh value) a valid installation auth token. - * An existing token may be invalidated or expire, so it is recommended to fetch the installation auth token - * before any request to external servers (it will be refreshed automatically for firebase API calls). - * This method should be used with forceRefresh == YES when e.g. a request with the previously fetched - * installation auth token failed with “Not Authorized” error. - * - * @param forceRefresh Options to get an auth token either by force refreshing or not. - * @return Firebase Installation Authentication Token - */ - getToken(forceRefresh?: boolean): Promise; - - /** - * Deletes the Firebase Installation and all associated data from the Firebase backend. - * This call may cause Firebase Cloud Messaging, Firebase Remote Config, Firebase Predictions, - * or Firebase In-App Messaging to not function properly. Fetching a new installations ID should - * reset all the dependent services to a stable state again. A network connection is required - * for the method to succeed. If it fails, the existing installation data remains untouched. - */ - delete(): Promise; - - /** - * TODO implement id change listener for android. - * - * Sets a new callback that will get called when Installation ID changes. - * Returns an unsubscribe function that will remove the callback when called. - * Only the Android SDK supports sending ID change events. - * - * @android - */ - // onIdChange(callback: (installationId: string) => void): () => void; - } -} - -type InstallationsNamespace = ReactNativeFirebase.FirebaseModuleWithStaticsAndApp< - FirebaseInstallationsTypes.Module, - FirebaseInstallationsTypes.Statics -> & { - firebase: ReactNativeFirebase.Module; - app(name?: string): ReactNativeFirebase.FirebaseApp; -}; - -declare const defaultExport: InstallationsNamespace; - -export const firebase: ReactNativeFirebase.Module & { - installations: typeof defaultExport; - app( - name?: string, - ): ReactNativeFirebase.FirebaseApp & { installations(): FirebaseInstallationsTypes.Module }; -}; - -export * from './modular'; - -export default defaultExport; - -/** - * Attach namespace to `firebase.` and `FirebaseApp.`. - */ -declare module '@react-native-firebase/app' { - namespace ReactNativeFirebase { - import FirebaseModuleWithStaticsAndApp = ReactNativeFirebase.FirebaseModuleWithStaticsAndApp; - - interface Module { - installations: FirebaseModuleWithStaticsAndApp< - FirebaseInstallationsTypes.Module, - FirebaseInstallationsTypes.Statics - >; - } - - interface FirebaseApp { - installations(): FirebaseInstallationsTypes.Module; - } - } -} diff --git a/packages/installations/lib/index.ts b/packages/installations/lib/index.ts new file mode 100644 index 0000000000..c7492a400a --- /dev/null +++ b/packages/installations/lib/index.ts @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2016-present Invertase Limited & Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this library 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. + * + */ + +// Export types from types/installations (public/modular API; Statics re-exported via modular) +export type { Installations } from './types/installations'; +export type { FirebaseInstallationsTypes } from './types/namespaced'; + +// Export modular API functions +export * from './modular'; + +// Export namespaced API +export * from './namespaced'; +export { default } from './namespaced'; diff --git a/packages/installations/lib/modular.ts b/packages/installations/lib/modular.ts new file mode 100644 index 0000000000..474147fd94 --- /dev/null +++ b/packages/installations/lib/modular.ts @@ -0,0 +1,62 @@ +import { getApp } from '@react-native-firebase/app'; +import { MODULAR_DEPRECATION_ARG } from '@react-native-firebase/app/dist/module/common'; +import type { ReactNativeFirebase } from '@react-native-firebase/app'; +import type { Installations } from './types/installations'; +import type { InstallationsInternal, WithModularDeprecationArg } from './types/internal'; + +export type { Statics } from './types/installations'; + +/** + * Returns an instance of Installations associated with the given FirebaseApp instance. + */ +export function getInstallations(app?: ReactNativeFirebase.FirebaseApp): Installations { + if (app) { + return getApp(app.name).installations() as Installations; + } + return getApp().installations() as Installations; +} + +/** + * Deletes the Firebase Installation and all associated data. + */ +export function deleteInstallations(installations: Installations): Promise { + return ( + (installations as InstallationsInternal).delete as WithModularDeprecationArg< + InstallationsInternal['delete'] + > + ).call(installations, MODULAR_DEPRECATION_ARG); +} + +/** + * Creates a Firebase Installation if there isn't one for the app and returns the Installation ID. + */ +export function getId(installations: Installations): Promise { + return ( + (installations as InstallationsInternal).getId as WithModularDeprecationArg< + InstallationsInternal['getId'] + > + ).call(installations, MODULAR_DEPRECATION_ARG); +} + +/** + * Returns a Firebase Installations auth token, identifying the current Firebase Installation. + */ +export function getToken(installations: Installations, forceRefresh?: boolean): Promise { + return ( + (installations as InstallationsInternal).getToken as WithModularDeprecationArg< + InstallationsInternal['getToken'] + > + ).call(installations, forceRefresh, MODULAR_DEPRECATION_ARG); +} + +/** + * Throw an error since react-native-firebase does not support this method. + * + * Sets a new callback that will get called when Installation ID changes. Returns an unsubscribe function that will remove the callback when called. + */ +export function onIdChange( + _installations: Installations, + _callback: (installationId: string) => void, +): () => void { + throw new Error('onIdChange() is unsupported by the React Native Firebase SDK.'); +} diff --git a/packages/installations/lib/modular/index.d.ts b/packages/installations/lib/modular/index.d.ts deleted file mode 100644 index 8e493590ac..0000000000 --- a/packages/installations/lib/modular/index.d.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { ReactNativeFirebase } from '@react-native-firebase/app'; -import { FirebaseInstallationsTypes } from '../index'; - -import FirebaseInstallations = FirebaseInstallationsTypes.Module; -/** - * Returns an instance of Installations associated with the given FirebaseApp instance. - */ -export declare function getInstallations( - app?: ReactNativeFirebase.FirebaseApp, -): FirebaseInstallations; - -/** - * Deletes the Firebase Installation and all associated data. - */ -export declare function deleteInstallations(installations?: FirebaseInstallations): Promise; - -/** - * Creates a Firebase Installation if there isn't one for the app and returns the Installation ID. - */ -export declare function getId(installations: FirebaseInstallations): Promise; - -/** - * Returns a Firebase Installations auth token, identifying the current Firebase Installation. - */ -export declare function getToken( - installations: FirebaseInstallations, - forceRefresh?: boolean, -): Promise; - -declare type IdChangeCallbackFn = (installationId: string) => void; -declare type IdChangeUnsubscribeFn = () => void; - -/** - * Throw an error since react-native-firebase does not support this method. - * - * Sets a new callback that will get called when Installation ID changes. Returns an unsubscribe function that will remove the callback when called. - */ -export declare function onIdChange( - installations: FirebaseInstallations, - callback: IdChangeCallbackFn, -): IdChangeUnsubscribeFn; diff --git a/packages/installations/lib/modular/index.js b/packages/installations/lib/modular/index.js deleted file mode 100644 index 85a7a279b8..0000000000 --- a/packages/installations/lib/modular/index.js +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2016-present Invertase Limited & Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this library 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. - * - */ - -import { getApp } from '@react-native-firebase/app'; -import { MODULAR_DEPRECATION_ARG } from '@react-native-firebase/app/dist/module/common'; -/** - * @typedef {import('..').FirebaseInstallationsTypes.Module} FirebaseInstallation - */ - -export function getInstallations(app) { - if (app) { - return getApp(app.name).installations(); - } - return getApp().installations(); -} - -/** - * @param {FirebaseInstallation} installations - * @returns {Promise} - */ -export function deleteInstallations(installations) { - return installations.delete.call(installations, MODULAR_DEPRECATION_ARG); -} - -/** - * @param {FirebaseInstallation} installations - * @returns {Promise} - */ -export function getId(installations) { - return installations.getId.call(installations, MODULAR_DEPRECATION_ARG); -} - -/** - * @param {FirebaseInstallation} installations - * @param {boolean | undefined} forceRefresh - * @returns {Promise} - */ -export function getToken(installations, forceRefresh) { - return installations.getToken.call(installations, forceRefresh, MODULAR_DEPRECATION_ARG); -} - -/** - * @param {FirebaseInstallation} installations - * @param {(string) => void} callback - * @returns {() => void} - */ -export function onIdChange(_installations, _callback) { - throw new Error('onIdChange() is unsupported by the React Native Firebase SDK.'); -} diff --git a/packages/installations/lib/index.js b/packages/installations/lib/namespaced.ts similarity index 50% rename from packages/installations/lib/index.js rename to packages/installations/lib/namespaced.ts index 65deb37618..43a431d819 100644 --- a/packages/installations/lib/index.js +++ b/packages/installations/lib/namespaced.ts @@ -16,26 +16,39 @@ */ import { isIOS } from '@react-native-firebase/app/dist/module/common'; +import type { ReactNativeFirebase } from '@react-native-firebase/app'; import { createModuleNamespace, FirebaseModule, getFirebaseRoot, } from '@react-native-firebase/app/dist/module/internal'; +import type { ModuleConfig } from '@react-native-firebase/app/dist/module/internal'; -import version from './version'; +import { version } from './version'; +import type { FirebaseInstallationsTypes } from './types/namespaced'; -const statics = {}; +const statics: FirebaseInstallationsTypes.Statics = { + SDK_VERSION: version, +}; const namespace = 'installations'; -const nativeModuleName = 'RNFBInstallationsModule'; +const nativeModuleName = 'RNFBInstallationsModule' as const; -class FirebaseInstallationsModule extends FirebaseModule { - getId() { +class FirebaseInstallationsModule extends FirebaseModule { + constructor( + app: ReactNativeFirebase.FirebaseAppBase, + config: ModuleConfig, + customUrlOrRegion?: string | null, + ) { + super(app, config, customUrlOrRegion); + } + + getId(): Promise { return this.native.getId(); } - getToken(forceRefresh) { + getToken(forceRefresh?: boolean): Promise { if (!forceRefresh) { return this.native.getToken(false); } else { @@ -43,12 +56,13 @@ class FirebaseInstallationsModule extends FirebaseModule { } } - delete() { + delete(): Promise { return this.native.delete(); } - onIdChange() { + onIdChange(_callback: (installationId: string) => void): () => void { if (isIOS) { + // iOS doesn't support ID change events, return empty unsubscribe function return () => {}; } @@ -58,11 +72,11 @@ class FirebaseInstallationsModule extends FirebaseModule { } // import { SDK_VERSION } from '@react-native-firebase/installations'; -export const SDK_VERSION = version; +export const SDK_VERSION: string = version; // import installations from '@react-native-firebase/installations'; // installations().X(...); -export default createModuleNamespace({ +const installationsNamespace = createModuleNamespace({ statics, version, namespace, @@ -73,9 +87,27 @@ export default createModuleNamespace({ ModuleClass: FirebaseInstallationsModule, }); +type InstallationsNamespace = ReactNativeFirebase.FirebaseModuleWithStaticsAndApp< + FirebaseInstallationsTypes.Module, + FirebaseInstallationsTypes.Statics +> & { + installations: ReactNativeFirebase.FirebaseModuleWithStaticsAndApp< + FirebaseInstallationsTypes.Module, + FirebaseInstallationsTypes.Statics + >; + firebase: ReactNativeFirebase.Module; + app(name?: string): ReactNativeFirebase.FirebaseApp; +}; + +export default installationsNamespace as unknown as InstallationsNamespace; + // import installations, { firebase } from '@react-native-firebase/installations'; // installations().X(...); // firebase.installations().X(...); -export const firebase = getFirebaseRoot(); - -export * from './modular'; +export const firebase = + getFirebaseRoot() as unknown as ReactNativeFirebase.FirebaseNamespacedExport< + 'installations', + FirebaseInstallationsTypes.Module, + FirebaseInstallationsTypes.Statics, + false + >; diff --git a/packages/installations/lib/types/installations.ts b/packages/installations/lib/types/installations.ts new file mode 100644 index 0000000000..1ff071e28a --- /dev/null +++ b/packages/installations/lib/types/installations.ts @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2016-present Invertase Limited & Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this library 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. + * + */ + +import type { ReactNativeFirebase } from '@react-native-firebase/app'; + +// ============ Statics Interface ============ + +export interface Statics { + SDK_VERSION: string; +} + +// ============ Module Interface (public/modular API) ============ + +/** + * The Firebase Installations service interface (modular/public API). + * Only exposes the app reference; use modular functions or the internal type for methods. + * + * > This module is available for the default app or a given app. + * + * #### Example + * + * Get the installations instance for the default app: + * + * ```js + * const defaultInstallations = getInstallations(); + * ``` + */ +export interface Installations { + /** The FirebaseApp this module is associated with */ + app: ReactNativeFirebase.FirebaseApp; +} diff --git a/packages/installations/lib/types/internal.ts b/packages/installations/lib/types/internal.ts new file mode 100644 index 0000000000..6f0bf74a3d --- /dev/null +++ b/packages/installations/lib/types/internal.ts @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2016-present Invertase Limited & Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this library 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. + * + */ + +import type { Installations } from './installations'; + +/** + * Wraps a method type so it accepts MODULAR_DEPRECATION_ARG as a trailing argument. + * Allows modular wrappers to use .call(instance, ...args, MODULAR_DEPRECATION_ARG) without changing internal types. + */ +export type WithModularDeprecationArg any> = T extends ( + ...args: infer A +) => infer R + ? (...args: [...A, unknown?]) => R + : never; + +/** + * Wrapped native module interface for Installations. + */ +export interface RNFBInstallationsModule { + getId(): Promise; + getToken(forceRefresh: boolean): Promise; + delete(): Promise; +} + +declare module '@react-native-firebase/app/dist/module/internal/NativeModules' { + interface ReactNativeFirebaseNativeModules { + RNFBInstallationsModule: RNFBInstallationsModule; + } +} + +/** + * Internal Installations type with access to instance methods. + * Used by namespaced implementation and modular wrappers; not part of the public modular API. + */ +export type InstallationsInternal = Installations & { + getId(): Promise; + getToken(forceRefresh?: boolean): Promise; + delete(): Promise; + onIdChange(callback: (installationId: string) => void): () => void; +}; diff --git a/packages/installations/lib/types/namespaced.ts b/packages/installations/lib/types/namespaced.ts new file mode 100644 index 0000000000..ff9ee8c94e --- /dev/null +++ b/packages/installations/lib/types/namespaced.ts @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2016-present Invertase Limited & Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this library 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. + * + */ + +import type { ReactNativeFirebase } from '@react-native-firebase/app'; + +/** + * Firebase Installations package for React Native. + * + * @firebase installations + */ + +type FirebaseModule = ReactNativeFirebase.FirebaseModule; + +/** + * @deprecated Use the exported types directly instead. + * FirebaseInstallationsTypes namespace is kept for backwards compatibility. + */ +/* eslint-disable @typescript-eslint/no-namespace */ +export namespace FirebaseInstallationsTypes { + /** + * Cloud Installations statics. + * + * @deprecated Use the exported types directly instead. FirebaseInstallationsTypes namespace is kept for backwards compatibility. + */ + export interface Statics { + SDK_VERSION: string; + } + + /** + * The Firebase Installations service is available for the default app or a given app. + * + * @deprecated Use the modular API (getInstallations, getId, getToken, delete) and types from '@react-native-firebase/installations' instead. + * FirebaseInstallationsTypes namespace is kept for backwards compatibility. + * + * #### Example + * + * Get the Installations instance for the **default app**: + * + * ```js + * const defaultInstallations = firebase.installations(); + * ``` + */ + export interface Module extends FirebaseModule { + /** + * The current `FirebaseApp` instance for this Firebase service. + * + * @deprecated Use the modular API instead. + */ + app: ReactNativeFirebase.FirebaseApp; + + /** + * Creates a Firebase Installation if there isn't one for the app and returns the Installation ID. + * + * @deprecated Use the modular `getId(installations)` instead. + */ + getId(): Promise; + + /** + * Retrieves a valid installation auth token. + * + * @deprecated Use the modular `getToken(installations, forceRefresh)` instead. + */ + getToken(forceRefresh?: boolean): Promise; + + /** + * Deletes the Firebase Installation and all associated data from the Firebase backend. + * + * @deprecated Use the modular `deleteInstallations(installations)` instead. + */ + delete(): Promise; + + /** + * Sets a new callback that will get called when Installation ID changes. Returns an unsubscribe function. + * + * @deprecated Not supported by React Native Firebase; use modular API for alternatives. + */ + onIdChange(callback: (installationId: string) => void): () => void; + } +} +/* eslint-enable @typescript-eslint/no-namespace */ + +/* eslint-disable @typescript-eslint/no-namespace */ +declare module '@react-native-firebase/app' { + namespace ReactNativeFirebase { + import FirebaseModuleWithStaticsAndApp = ReactNativeFirebase.FirebaseModuleWithStaticsAndApp; + interface Module { + installations: FirebaseModuleWithStaticsAndApp< + FirebaseInstallationsTypes.Module, + FirebaseInstallationsTypes.Statics + >; + } + interface FirebaseApp { + installations(): FirebaseInstallationsTypes.Module; + } + } +} +/* eslint-enable @typescript-eslint/no-namespace */ diff --git a/packages/installations/package.json b/packages/installations/package.json index 27da50cd1b..955d305819 100644 --- a/packages/installations/package.json +++ b/packages/installations/package.json @@ -3,12 +3,13 @@ "version": "23.8.6", "author": "Invertase (http://invertase.io)", "description": "React Native Firebase - Installations", - "main": "lib/index.js", - "types": "lib/index.d.ts", + "main": "./dist/module/index.js", + "types": "./dist/typescript/lib/index.d.ts", "scripts": { - "build": "genversion --semi lib/version.js", + "build": "genversion --esm --semi lib/version.ts", "build:clean": "rimraf android/build && rimraf ios/build", - "prepare": "yarn run build" + "compile": "bob build", + "prepare": "yarn run build && yarn compile" }, "repository": { "type": "git", @@ -24,6 +25,39 @@ "peerDependencies": { "@react-native-firebase/app": "23.8.6" }, + "devDependencies": { + "react-native-builder-bob": "^0.40.17" + }, + "exports": { + ".": { + "source": "./lib/index.ts", + "types": "./dist/typescript/lib/index.d.ts", + "default": "./dist/module/index.js" + }, + "./package.json": "./package.json" + }, + "react-native-builder-bob": { + "source": "lib", + "output": "dist", + "targets": [ + [ + "module", + { + "esm": true + } + ], + [ + "typescript", + { + "tsc": "../../node_modules/.bin/tsc" + } + ] + ] + }, + "eslintIgnore": [ + "node_modules/", + "dist/" + ], "publishConfig": { "access": "public", "provenance": true diff --git a/packages/installations/tsconfig.json b/packages/installations/tsconfig.json new file mode 100644 index 0000000000..d23f0c8b5a --- /dev/null +++ b/packages/installations/tsconfig.json @@ -0,0 +1,21 @@ +{ + "extends": "../../tsconfig.packages.base.json", + "compilerOptions": { + "baseUrl": ".", + "rootDir": ".", + "paths": { + "@react-native-firebase/app/dist/module/common/*": ["../app/dist/typescript/lib/common/*"], + "@react-native-firebase/app/dist/module/common": ["../app/dist/typescript/lib/common"], + "@react-native-firebase/app/dist/module/internal/web/*": [ + "../app/dist/typescript/lib/internal/web/*" + ], + "@react-native-firebase/app/dist/module/internal/*": [ + "../app/dist/typescript/lib/internal/*" + ], + "@react-native-firebase/app/dist/module/internal": ["../app/dist/typescript/lib/internal"], + "@react-native-firebase/app": ["../app/dist/typescript/lib"] + } + }, + "include": ["lib/**/*", "../app/lib/internal/global.d.ts"], + "exclude": ["node_modules", "dist", "__tests__", "**/*.test.ts"] +} diff --git a/packages/installations/type-test.ts b/packages/installations/type-test.ts index 3df090f8b3..6263a78e89 100644 --- a/packages/installations/type-test.ts +++ b/packages/installations/type-test.ts @@ -1,5 +1,6 @@ import installations, { firebase, + FirebaseInstallationsTypes, getInstallations, deleteInstallations, getId, @@ -33,7 +34,7 @@ console.log(installations(firebase.app()).app.name); console.log(installations(firebase.app('foo')).app.name); // checks Module instance APIs -const installationsInstance = firebase.installations(); +const installationsInstance = firebase.installations() as unknown as FirebaseInstallationsTypes.Module; console.log(installationsInstance.app.name); installationsInstance.getId().then((id: string) => { diff --git a/tsconfig.json b/tsconfig.json index 0658ab77a9..dd46a78f7a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,7 +4,7 @@ "packages/app-check/lib/index.d.ts", "packages/app-check/lib/modular/index.d.ts", "packages/app-distribution/lib/index.d.ts", - "packages/app-distribution/lib/modular/index.d.ts", + "packages/app-distribution/lib/modular.d.ts", "packages/app/lib/internal/global.d.ts", "packages/app/lib/internal/web/memidb/index.d.ts", "packages/auth/lib/index.d.ts", @@ -27,8 +27,6 @@ "packages/firestore/lib/modular/VectorValue.d.ts", "packages/in-app-messaging/lib/index.d.ts", "packages/in-app-messaging/lib/modular/index.d.ts", - "packages/installations/lib/index.d.ts", - "packages/installations/lib/modular/index.d.ts", "packages/ml/lib/index.d.ts", "packages/ml/lib/modular/index.d.ts", "packages/perf/lib/index.d.ts", @@ -76,7 +74,13 @@ "lib": ["es2015", "es2016", "esnext", "dom"], "types": ["react-native", "node"], "paths": { - "@react-native-firebase/*": ["./packages/*/lib/index.d.ts"] + "@react-native-firebase/*": ["./packages/*/lib/index.d.ts"], + "@react-native-firebase/app": ["./packages/app/dist/typescript/lib"], + "@react-native-firebase/app/dist/module/common": ["./packages/app/dist/typescript/lib/common"], + "@react-native-firebase/app/dist/module/common/*": ["./packages/app/dist/typescript/lib/common/*"], + "@react-native-firebase/app/dist/module/internal": ["./packages/app/dist/typescript/lib/internal"], + "@react-native-firebase/app/dist/module/internal/*": ["./packages/app/dist/typescript/lib/internal/*"], + "@react-native-firebase/app/dist/module/internal/web/*": ["./packages/app/dist/typescript/lib/internal/web/*"] } }, "exclude": ["node_modules", "**/*.spec.ts", "packages/**/dist"] diff --git a/yarn.lock b/yarn.lock index e4be9a3184..572a6ce733 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5903,6 +5903,8 @@ __metadata: "@react-native-firebase/installations@npm:23.8.6, @react-native-firebase/installations@workspace:packages/installations": version: 0.0.0-use.local resolution: "@react-native-firebase/installations@workspace:packages/installations" + dependencies: + react-native-builder-bob: "npm:^0.40.17" peerDependencies: "@react-native-firebase/app": 23.8.6 languageName: unknown