refactor(remote-config): move to typescript#8896
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
russellwheatley
left a comment
There was a problem hiding this comment.
Good job, @akashdeep931 - just needs some further work doing 😄 .
As another aside, I do prefer smaller commits rather than one big commit 🙏
| } | ||
|
|
||
| /** | ||
| * Internal: Module instance methods accept optional deprecation arg (string). |
There was a problem hiding this comment.
internal types need to be moved to their own types/internal.ts file. See storage typescript: https://github.com/invertase/react-native-firebase/blob/storage-typescript/packages/storage/lib/types/internal.ts
packages/remote-config/lib/index.ts
Outdated
| export type { FirebaseRemoteConfigTypes } from './types/namespaced'; | ||
| export * from './modular'; | ||
| export { default, firebase, SDK_VERSION } from './namespaced'; |
There was a problem hiding this comment.
This needs to match storage: https://github.com/invertase/react-native-firebase/blob/storage-typescript/packages/storage/lib/index.ts#L19-L27
| const listener = parseListenerOrObserver(listenerOrObserver) as ( | ||
| event?: { updatedKeys: string[] }, | ||
| error?: { code: string; message: string; nativeErrorMessage: string }, | ||
| ) => void; |
| if (typeof c.lastFetchTime === 'number') { | ||
| this._lastFetchTime = c.lastFetchTime; | ||
| } | ||
|
|
||
| // Wrapped this as we update using sync getters initially for `defaultConfig` & `settings` | ||
| if (constants.lastFetchStatus) { | ||
| this._lastFetchStatus = constants.lastFetchStatus; | ||
| if (typeof c.lastFetchStatus === 'string') { | ||
| this._lastFetchStatus = c.lastFetchStatus as FirebaseRemoteConfigTypes.LastFetchStatusType; |
There was a problem hiding this comment.
constants from native are loosely typed, so values can be strings, numbers, objects, or missing. The typeof checks act as a type-safety layer: we only assign to the instance fields when the value is actually the expected type (e.g. number for lastFetchTime, string for lastFetchStatus), so we don’t set invalid or mismatched data.
| if (c.values && typeof c.values === 'object' && !Array.isArray(c.values)) { | ||
| this._values = Object.freeze(c.values) as Record<string, { value: string; source: string }>; | ||
| } |
packages/remote-config/type-test.ts
Outdated
| type FirebaseRoot = { | ||
| app(name?: string): { name: string; remoteConfig(): ReturnType<typeof getRemoteConfig> }; | ||
| remoteConfig: ((app?: { name: string }) => ReturnType<typeof getRemoteConfig>) & { | ||
| SDK_VERSION: string; | ||
| ValueSource: { REMOTE: string; DEFAULT: string; STATIC: string }; | ||
| LastFetchStatus: { SUCCESS: string; FAILURE: string; THROTTLED: string; NO_FETCH_YET: string }; | ||
| }; | ||
| SDK_VERSION?: string; | ||
| }; | ||
| const _firebase = firebase as unknown as FirebaseRoot; |
There was a problem hiding this comment.
this shouldn't be needed
There was a problem hiding this comment.
You are right, linter was complaining when I didn't define the type of _firebase, but tsconfig is set up and isn't complaining anymore. Fixed in commit.
| "@react-native-firebase/remote-config": ["./packages/remote-config/lib/index.ts"], | ||
| "@react-native-firebase/app/dist/module/internal/web/utils": [ | ||
| "./packages/app/lib/internal/web/utils" | ||
| ], | ||
| "@react-native-firebase/app/dist/module/internal/web/firebaseRemoteConfig": [ | ||
| "./packages/app/lib/internal/web/firebaseRemoteConfig" | ||
| ], | ||
| "@react-native-firebase/app/dist/module/internal/nativeModule": [ | ||
| "./packages/app/lib/internal/nativeModule" | ||
| ], | ||
| "@react-native-firebase/app/dist/module/internal/*": ["./packages/app/lib/internal/*"], | ||
| "@react-native-firebase/app/dist/module/internal": ["./packages/app/lib/internal"], | ||
| "@react-native-firebase/app/dist/module/common/*": ["./packages/app/lib/common/*"], | ||
| "@react-native-firebase/app/dist/module/common": ["./packages/app/lib/common"], | ||
| "@react-native-firebase/app": ["./packages/app/lib/index.ts"], |
There was a problem hiding this comment.
fairly sure some if not all of these are not needed
There was a problem hiding this comment.
Removed some as they were reduntant. Fixed in commit.
|
Appears to have caused some havoc with unexpected results on the Other platform - make sure to run |
russellwheatley
left a comment
There was a problem hiding this comment.
Provided some more feedback 🙏
| export interface FirebaseExperimentDescription { | ||
| // A string of max length 22 characters and of format: _exp_<experiment_id> | ||
| experimentId: string; | ||
|
|
||
| // The variant of the experiment assigned to the app instance. | ||
| variantId: string; | ||
|
|
||
| // When the experiment was started. | ||
| experimentStartTime: string; | ||
|
|
||
| // How long the experiment can remain in STANDBY state. Valid range from 1 ms | ||
| // to 6 months. | ||
| triggerTimeoutMillis: string; | ||
|
|
||
| // How long the experiment can remain in ON state. Valid range from 1 ms to 6 | ||
| // months. | ||
| timeToLiveMillis: string; | ||
|
|
||
| // Which all parameters are affected by this experiment. | ||
| affectedParameterKeys?: string[]; | ||
| } | ||
|
|
||
| /** | ||
| * Defines a successful response (200 or 304). | ||
| * | ||
| * <p>Modeled after the native `Response` interface, but simplified for Remote Config's | ||
| * use case. | ||
| * | ||
| * @public | ||
| */ | ||
| export interface FetchResponse { | ||
| /** | ||
| * The HTTP status, which is useful for differentiating success responses with data from | ||
| * those without. | ||
| * | ||
| * <p>The Remote Config client is modeled after the native `Fetch` interface, so | ||
| * HTTP status is first-class. | ||
| * | ||
| * <p>Disambiguation: the fetch response returns a legacy "state" value that is redundant with the | ||
| * HTTP status code. The former is normalized into the latter. | ||
| */ | ||
| status: number; | ||
|
|
||
| /** | ||
| * Defines the ETag response header value. | ||
| * | ||
| * <p>Only defined for 200 and 304 responses. | ||
| */ | ||
| eTag?: string; | ||
|
|
||
| /** | ||
| * Defines the map of parameters returned as "entries" in the fetch response body. | ||
| * | ||
| * <p>Only defined for 200 responses. | ||
| */ | ||
| config?: FirebaseRemoteConfigObject; | ||
|
|
||
| /** | ||
| * The version number of the config template fetched from the server. | ||
| */ | ||
| templateVersion?: number; | ||
|
|
||
| /** | ||
| * Metadata for A/B testing and Remote Config Rollout experiments. | ||
| * | ||
| * @remarks Only defined for 200 responses. | ||
| */ | ||
| experiments?: FirebaseExperimentDescription[]; | ||
| } | ||
|
|
||
| /** | ||
| * Options for Remote Config initialization. | ||
| * | ||
| * @public | ||
| */ | ||
| export interface RemoteConfigOptions { | ||
| /** | ||
| * The ID of the template to use. If not provided, defaults to "firebase". | ||
| */ | ||
| templateId?: string; | ||
|
|
||
| /** | ||
| * Hydrates the state with an initial fetch response. | ||
| */ | ||
| initialFetchResponse?: FetchResponse; | ||
| } |
There was a problem hiding this comment.
not sure if we should provide types that don't currently do anything. See what @mikehardy thinks
There was a problem hiding this comment.
I think we'll keep this type, but it should be annotated as web only, pretty sure it is only supported on firebase-js-sdk. Also - it does nothing at the moment, as it isn't wired up. I would also add a note that this isn't implemented currently and it is to be implemented on web in the future.
| @@ -26,9 +28,9 @@ export type { CustomSignals }; | |||
| */ | |||
| export function getRemoteConfig(app?: ReactNativeFirebase.FirebaseApp): RemoteConfig { | |||
There was a problem hiding this comment.
I guess this should map to firebase-js-sdk getRemoteConfig(): https://firebase.google.com/docs/reference/js/remote-config.md#getremoteconfig_61d368f
Essentially needs RemoteConfigOptions as second arg. confirm that works for you, @mikehardy 🙏
There was a problem hiding this comment.
Fixed in commit. Let me know if that's what you expected.
| * @param key - key for the given value | ||
| * @returns ConfigValue | ||
| */ | ||
| export function getValue(remoteConfig: RemoteConfig, key: string): ConfigValue { |
There was a problem hiding this comment.
This should be returning Value. See: https://github.com/firebase/firebase-js-sdk/blob/main/packages/remote-config/src/api.ts#L271
| ) => Promise<null>; | ||
|
|
||
| /** Alias for Value. */ | ||
| export type ConfigValue = Value; |
There was a problem hiding this comment.
remove this alias and use Value
| export type ConfigValues = { [key: string]: Value }; | ||
|
|
||
| /** Alias for FetchStatus. */ | ||
| export type LastFetchStatusType = FetchStatus; |
There was a problem hiding this comment.
LastFetchStatusType doesn't exist on firebase-js-sdk, remove and use FetchStatus
| export type LastFetchStatusType = FetchStatus; | ||
|
|
||
| /** Alias for LogLevel. */ | ||
| export type RemoteConfigLogLevel = LogLevel; |
There was a problem hiding this comment.
this should also be public, it's on consumer facing api: https://firebase.google.com/docs/reference/js/remote-config.md#setloglevel_039a45b
| */ | ||
| export function onConfigUpdated( | ||
| remoteConfig: RemoteConfig, | ||
| callback: CallbackOrObserver<OnConfigUpdatedListenerCallback>, |
There was a problem hiding this comment.
This type comes from internal, use the type from namespaced/types.ts instead.
There was a problem hiding this comment.
Should the Modular API import namespaced types?
There was a problem hiding this comment.
modular should not import namespaced types.
if anything, it should be:
modular -> namespaced.
We're modular first now, and namespaced will be removed soon. Same for web, any types for RNFBConfigModule.js, should be from modular or internal types.
| */ | ||
| export function setConfigSettings( | ||
| remoteConfig: RemoteConfig, | ||
| settings: ConfigSettings, |
There was a problem hiding this comment.
use RemoteConfigSettings here instead
russellwheatley
left a comment
There was a problem hiding this comment.
@akashdeep931 - getting really close with this! Just need to revert a couple of changes made to release docs. I'll be going over this tomorrow to double check the config options setup in the comparison script and whether they need to change as well 🙏
|
|
||
|  | ||
|
|
||
| - [BREAKING] Module namespace has been renamed to `.remoteConfig()`, replace all usages of `firebase.config` with the new name. |
There was a problem hiding this comment.
Revert all changes to this file, these are the release notes for a previous release and should remain the same.
| `@react-native-firebase/remote-config` | ||
|
|
||
| - Module namespace has been renamed to `.remoteConfig()` from `.config()`. | ||
| - All Remote Config values can now be accessed synchronously in JS, see `getValue(key: string): ConfigValue` & `getAll(): ConfigValues` below. |
There was a problem hiding this comment.
Revert change for the same reason above
There was a problem hiding this comment.
This still needs reverting. It should be exactly the same as before.
russellwheatley
left a comment
There was a problem hiding this comment.
Sorry, now that I've spent a long time looking, there's a bunch of other changes I've missed. but these should really bring us closer to what we want 😄
| `@react-native-firebase/remote-config` | ||
|
|
||
| - Module namespace has been renamed to `.remoteConfig()` from `.config()`. | ||
| - All Remote Config values can now be accessed synchronously in JS, see `getValue(key: string): ConfigValue` & `getAll(): ConfigValues` below. |
There was a problem hiding this comment.
This still needs reverting. It should be exactly the same as before.
|
|
||
| type RemoteConfigInstance = FirebaseRemoteConfigTypes.Module & { | ||
| settings: Record<string, number>; | ||
| defaultConfig: FirebaseRemoteConfigTypes.ConfigDefaults; |
There was a problem hiding this comment.
Remove all references to FirebaseRemoteConfigTypes in web. At some point we're going to remove those types. So types for modular and in here ought to come from types/internal or types/modular.
| { minimumFetchIntervalMillis: number; fetchTimeoutMillis: number } | ||
| > = {}; | ||
|
|
||
| const defaultConfigForInstance: Record<string, FirebaseRemoteConfigTypes.ConfigDefaults> = {}; |
| }); | ||
| }, | ||
|
|
||
| setDefaults(appName: string, defaults: FirebaseRemoteConfigTypes.ConfigDefaults) { |
packages/remote-config/lib/index.ts
Outdated
| export type { | ||
| ConfigUpdate, | ||
| ConfigUpdateObserver, | ||
| ConfigValues, | ||
| CustomSignals, | ||
| FetchResponse, | ||
| FetchStatus, | ||
| FetchType, | ||
| FirebaseExperimentDescription, | ||
| FirebaseRemoteConfigObject, | ||
| LogLevel, | ||
| RemoteConfig, | ||
| RemoteConfigLogLevel, | ||
| RemoteConfigOptions, | ||
| RemoteConfigSettings, | ||
| Unsubscribe, | ||
| Value, | ||
| ValueSource as ModularValueSource, | ||
| } from './types/modular'; | ||
|
|
||
| export * from './modular'; | ||
|
|
||
| export type { FirebaseRemoteConfigTypes } from './types/namespaced'; | ||
| export * from './namespaced'; | ||
| export { default } from './namespaced'; |
There was a problem hiding this comment.
update this. It needs to look similar to this:
// Export modular types from types/storage
export type * from './types/storage';
// Export modular API functions
export * from './modular';
// Export namespaced API
export type { FirebaseStorageTypes } from './types/namespaced';
export * from './namespaced';
export { default } from './namespaced';
A clear separation of modular and namespaced. It's also important to do this:
export type * from './types/storage';
It helps find out if we have duplicate type and value. If there is a type that is also a value, we should use the value as the type. I hope that makes sense.
| { | ||
| name: 'settings', | ||
| reason: | ||
| 'Getter function returning the current `ConfigSettings`. In the ' + | ||
| 'firebase-js-sdk settings are accessed via the `settings` property ' + | ||
| 'on the `RemoteConfig` object rather than a standalone function.', | ||
| }, |
There was a problem hiding this comment.
this can also be removed from modular.ts, should be getting from remote config instance now.
| reason: | ||
| 'The RN Firebase implementation accepts `RemoteConfigLogLevel` (a type alias ' + | ||
| 'for `LogLevel`) and returns it, whereas the firebase-js-sdk accepts `LogLevel` ' + | ||
| 'and returns `void`. The parameter types are semantically identical; the return ' + | ||
| 'type difference is a legacy artefact of the RN implementation.', |
There was a problem hiding this comment.
let's update this to match firebase-js-sdk
| name: 'getAll', | ||
| reason: | ||
| 'Returns `ConfigValues` (RN Firebase type alias for `{ [key: string]: Value }`) ' + | ||
| 'instead of `Record<string, Value>`. The two types are structurally equivalent; ' + | ||
| 'the alias is retained for backwards compatibility.', | ||
| }, |
There was a problem hiding this comment.
this can be removed when we remove ConfigValues and use raw { [key: string]: Value } type for getAll().
…on with module type
2627152 to
1d0a0ba
Compare
Description
First TypeScript migration for the remote-config package. Source is now written in TypeScript and namespaced types are fully separated from modular types:
lib/types/namespaced.tsand are aligned with the previousindex.d.tssurface. This keeps the migration low-risk and makes it straightforward to remove namespaced support later.lib/types/modular.tsand are consumed only by the modular API.The package continues to build with react-native-builder-bob (ESM + TypeScript declaration targets). No intentional changes to the public API or runtime behavior.
Breaking Changes
No consumer-facing breaking changes. Same public API and type surface; only the implementation and type file layout have changed (JS → TS, types split into
types/namespaced.tsandtypes/modular.ts).Related issues
Release Summary
types/namespaced.tsandtypes/modular.ts. No API changes.Checklist
packages/**/e2e(existing e2e unchanged; type-test and build validate TS)packages/**/__tests__(existing tests updated for TS where applicable)Test Plan
yarn compileat repo root (builds all packages including remote-config).packages/remote-config/type-test.tsis included in roottscand passes.packages/remote-config/e2e/config.e2e.js.packages/remote-config/__tests__/remote-config.test.ts.tests/local-tests/remote-config) runs and can use remote-config.Think react-native-firebase is great? Please consider supporting the project with any of the below:
👉 Star this repo on GitHub ⭐️
👉 Follow React Native Firebase and Invertase on Twitter