From 23224c4ece71c29dae994648e75f3952f09cc8a6 Mon Sep 17 00:00:00 2001 From: Ron Cohen Date: Fri, 20 Mar 2026 11:58:07 +0100 Subject: [PATCH 1/4] Relax bootstrapped flag payload typing --- packages/react-native-sdk/src/index.tsx | 2 + packages/react-sdk/package.json | 5 +- packages/react-sdk/src/index.tsx | 12 ++- .../test/bootstrapped-flags.type-test.ts | 78 +++++++++++++++++++ packages/react-sdk/tsconfig.type-tests.json | 7 ++ 5 files changed, 99 insertions(+), 5 deletions(-) create mode 100644 packages/react-sdk/test/bootstrapped-flags.type-test.ts create mode 100644 packages/react-sdk/tsconfig.type-tests.json diff --git a/packages/react-native-sdk/src/index.tsx b/packages/react-native-sdk/src/index.tsx index b7b1482d..da4e815d 100644 --- a/packages/react-native-sdk/src/index.tsx +++ b/packages/react-native-sdk/src/index.tsx @@ -18,6 +18,7 @@ import type { ReflagProps, ReflagPropsBase, RequestFeedbackOptions, + SerializedBootstrappedFlags, StorageAdapter, TrackEvent, TypedFlags, @@ -75,6 +76,7 @@ export type { ReflagProps, ReflagPropsBase, RequestFeedbackOptions, + SerializedBootstrappedFlags, StorageAdapter, TrackEvent, TypedFlags, diff --git a/packages/react-sdk/package.json b/packages/react-sdk/package.json index 3587dedc..8786f047 100644 --- a/packages/react-sdk/package.json +++ b/packages/react-sdk/package.json @@ -13,14 +13,15 @@ "dev": "vite", "build": "tsc --project tsconfig.build.json && vite build", "test": "vitest run", + "test:types": "tsc --project tsconfig.type-tests.json", "test:watch": "vitest", - "test:ci": "yarn test --reporter=default --reporter=junit --outputFile=junit.xml", + "test:ci": "yarn test --reporter=default --reporter=junit --outputFile=junit.xml && yarn test:types", "coverage": "yarn test --coverage", "lint": "eslint .", "lint:ci": "eslint --output-file eslint-report.json --format json .", "prettier": "prettier --check .", "format": "yarn lint --fix && yarn prettier --write", - "preversion": "yarn lint && yarn prettier && yarn test && yarn build" + "preversion": "yarn lint && yarn prettier && yarn test && yarn test:types && yarn build" }, "files": [ "dist" diff --git a/packages/react-sdk/src/index.tsx b/packages/react-sdk/src/index.tsx index a287facd..15331cbd 100644 --- a/packages/react-sdk/src/index.tsx +++ b/packages/react-sdk/src/index.tsx @@ -130,11 +130,17 @@ export type FlagKey = keyof TypedFlags; */ export type RawFlags = Record; -export type BootstrappedFlags = { +/** + * Serialized bootstrapped flags that can cross the server/client boundary + * without depending on the local generated flag-key union. + */ +export type SerializedBootstrappedFlags = { context: ReflagContext; - flags: RawFlags; + flags: Record; }; +export type BootstrappedFlags = SerializedBootstrappedFlags; + const SDK_VERSION = `react-sdk/${version}`; /** @@ -352,7 +358,7 @@ export type ReflagBootstrappedProps = ReflagPropsBase & /** * Pre-fetched flags to be used instead of fetching them from the server. */ - flags: BootstrappedFlags; + flags: SerializedBootstrappedFlags; }; /** diff --git a/packages/react-sdk/test/bootstrapped-flags.type-test.ts b/packages/react-sdk/test/bootstrapped-flags.type-test.ts new file mode 100644 index 00000000..82ee94b3 --- /dev/null +++ b/packages/react-sdk/test/bootstrapped-flags.type-test.ts @@ -0,0 +1,78 @@ +import type { RawFlag, ReflagContext } from "@reflag/browser-sdk"; + +import type { + BootstrappedFlags, + FlagKey, + ReflagBootstrappedProps, + SerializedBootstrappedFlags, +} from "../src"; +import { useFlag } from "../src"; + +declare module "../src" { + interface Flags { + "frontend-flag": { + config: { + payload: { + message: string; + }; + }; + }; + } +} + +type Equal = (() => T extends A ? 1 : 2) extends < + T, +>() => T extends B ? 1 : 2 + ? true + : false; + +type Expect = T; +type Extends = A extends B ? true : false; + +type SharedBootstrappedPayload = { + context: ReflagContext; + flags: Record; +}; + +type ServerGeneratedBootstrappedPayload = { + context: ReflagContext; + flags: Record<"server-only-flag", RawFlag>; +}; + +type Assertions = [ + Expect>, + Expect>, + Expect>, + Expect>, + Expect< + Extends + >, + Expect>, + Expect[0], "frontend-flag">>, +]; + +declare const assertions: Assertions; +declare const sharedPayload: SharedBootstrappedPayload; +declare const serverGeneratedPayload: ServerGeneratedBootstrappedPayload; + +const bootstrappedFlags: BootstrappedFlags = sharedPayload; +const serializedFlags: SerializedBootstrappedFlags = serverGeneratedPayload; +const sharedProviderProps: ReflagBootstrappedProps = { + flags: sharedPayload, + publishableKey: "pk_test", +}; +const mismatchedUnionProviderProps: ReflagBootstrappedProps = { + flags: serverGeneratedPayload, + publishableKey: "pk_test", +}; +const validLocalFlagKey: Parameters[0] = "frontend-flag"; + +void assertions; +void bootstrappedFlags; +void serializedFlags; +void sharedProviderProps; +void mismatchedUnionProviderProps; +void validLocalFlagKey; + +// @ts-expect-error Bootstrapped transport widening must not weaken local consumer typing. +const _invalidLocalFlagKey: Parameters[0] = "server-only-flag"; diff --git a/packages/react-sdk/tsconfig.type-tests.json b/packages/react-sdk/tsconfig.type-tests.json new file mode 100644 index 00000000..6669fed1 --- /dev/null +++ b/packages/react-sdk/tsconfig.type-tests.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": true + }, + "include": ["test/bootstrapped-flags.type-test.ts"] +} From e704387397f5cfd8e6fa75ff6a646e47189e3f3c Mon Sep 17 00:00:00 2001 From: Ron Cohen Date: Fri, 20 Mar 2026 12:02:04 +0100 Subject: [PATCH 2/4] Simplify bootstrapped flag typing change --- packages/react-native-sdk/src/index.tsx | 2 - packages/react-sdk/package.json | 5 +- packages/react-sdk/src/index.tsx | 10 +-- .../test/bootstrapped-flags.type-test.ts | 78 ------------------- packages/react-sdk/tsconfig.type-tests.json | 7 -- 5 files changed, 4 insertions(+), 98 deletions(-) delete mode 100644 packages/react-sdk/test/bootstrapped-flags.type-test.ts delete mode 100644 packages/react-sdk/tsconfig.type-tests.json diff --git a/packages/react-native-sdk/src/index.tsx b/packages/react-native-sdk/src/index.tsx index da4e815d..b7b1482d 100644 --- a/packages/react-native-sdk/src/index.tsx +++ b/packages/react-native-sdk/src/index.tsx @@ -18,7 +18,6 @@ import type { ReflagProps, ReflagPropsBase, RequestFeedbackOptions, - SerializedBootstrappedFlags, StorageAdapter, TrackEvent, TypedFlags, @@ -76,7 +75,6 @@ export type { ReflagProps, ReflagPropsBase, RequestFeedbackOptions, - SerializedBootstrappedFlags, StorageAdapter, TrackEvent, TypedFlags, diff --git a/packages/react-sdk/package.json b/packages/react-sdk/package.json index 8786f047..3587dedc 100644 --- a/packages/react-sdk/package.json +++ b/packages/react-sdk/package.json @@ -13,15 +13,14 @@ "dev": "vite", "build": "tsc --project tsconfig.build.json && vite build", "test": "vitest run", - "test:types": "tsc --project tsconfig.type-tests.json", "test:watch": "vitest", - "test:ci": "yarn test --reporter=default --reporter=junit --outputFile=junit.xml && yarn test:types", + "test:ci": "yarn test --reporter=default --reporter=junit --outputFile=junit.xml", "coverage": "yarn test --coverage", "lint": "eslint .", "lint:ci": "eslint --output-file eslint-report.json --format json .", "prettier": "prettier --check .", "format": "yarn lint --fix && yarn prettier --write", - "preversion": "yarn lint && yarn prettier && yarn test && yarn test:types && yarn build" + "preversion": "yarn lint && yarn prettier && yarn test && yarn build" }, "files": [ "dist" diff --git a/packages/react-sdk/src/index.tsx b/packages/react-sdk/src/index.tsx index 15331cbd..8a16f1bd 100644 --- a/packages/react-sdk/src/index.tsx +++ b/packages/react-sdk/src/index.tsx @@ -130,17 +130,11 @@ export type FlagKey = keyof TypedFlags; */ export type RawFlags = Record; -/** - * Serialized bootstrapped flags that can cross the server/client boundary - * without depending on the local generated flag-key union. - */ -export type SerializedBootstrappedFlags = { +export type BootstrappedFlags = { context: ReflagContext; flags: Record; }; -export type BootstrappedFlags = SerializedBootstrappedFlags; - const SDK_VERSION = `react-sdk/${version}`; /** @@ -358,7 +352,7 @@ export type ReflagBootstrappedProps = ReflagPropsBase & /** * Pre-fetched flags to be used instead of fetching them from the server. */ - flags: SerializedBootstrappedFlags; + flags: BootstrappedFlags; }; /** diff --git a/packages/react-sdk/test/bootstrapped-flags.type-test.ts b/packages/react-sdk/test/bootstrapped-flags.type-test.ts deleted file mode 100644 index 82ee94b3..00000000 --- a/packages/react-sdk/test/bootstrapped-flags.type-test.ts +++ /dev/null @@ -1,78 +0,0 @@ -import type { RawFlag, ReflagContext } from "@reflag/browser-sdk"; - -import type { - BootstrappedFlags, - FlagKey, - ReflagBootstrappedProps, - SerializedBootstrappedFlags, -} from "../src"; -import { useFlag } from "../src"; - -declare module "../src" { - interface Flags { - "frontend-flag": { - config: { - payload: { - message: string; - }; - }; - }; - } -} - -type Equal = (() => T extends A ? 1 : 2) extends < - T, ->() => T extends B ? 1 : 2 - ? true - : false; - -type Expect = T; -type Extends = A extends B ? true : false; - -type SharedBootstrappedPayload = { - context: ReflagContext; - flags: Record; -}; - -type ServerGeneratedBootstrappedPayload = { - context: ReflagContext; - flags: Record<"server-only-flag", RawFlag>; -}; - -type Assertions = [ - Expect>, - Expect>, - Expect>, - Expect>, - Expect< - Extends - >, - Expect>, - Expect[0], "frontend-flag">>, -]; - -declare const assertions: Assertions; -declare const sharedPayload: SharedBootstrappedPayload; -declare const serverGeneratedPayload: ServerGeneratedBootstrappedPayload; - -const bootstrappedFlags: BootstrappedFlags = sharedPayload; -const serializedFlags: SerializedBootstrappedFlags = serverGeneratedPayload; -const sharedProviderProps: ReflagBootstrappedProps = { - flags: sharedPayload, - publishableKey: "pk_test", -}; -const mismatchedUnionProviderProps: ReflagBootstrappedProps = { - flags: serverGeneratedPayload, - publishableKey: "pk_test", -}; -const validLocalFlagKey: Parameters[0] = "frontend-flag"; - -void assertions; -void bootstrappedFlags; -void serializedFlags; -void sharedProviderProps; -void mismatchedUnionProviderProps; -void validLocalFlagKey; - -// @ts-expect-error Bootstrapped transport widening must not weaken local consumer typing. -const _invalidLocalFlagKey: Parameters[0] = "server-only-flag"; diff --git a/packages/react-sdk/tsconfig.type-tests.json b/packages/react-sdk/tsconfig.type-tests.json deleted file mode 100644 index 6669fed1..00000000 --- a/packages/react-sdk/tsconfig.type-tests.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "noEmit": true - }, - "include": ["test/bootstrapped-flags.type-test.ts"] -} From 382bf6bf5d71e1fbe6bb9b638975df71990d4674 Mon Sep 17 00:00:00 2001 From: Ron Cohen Date: Fri, 20 Mar 2026 12:24:10 +0100 Subject: [PATCH 3/4] Use browser RawFlags for bootstrapped payload --- packages/react-sdk/src/index.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/react-sdk/src/index.tsx b/packages/react-sdk/src/index.tsx index 8a16f1bd..f31fe57e 100644 --- a/packages/react-sdk/src/index.tsx +++ b/packages/react-sdk/src/index.tsx @@ -17,6 +17,7 @@ import { InitOptions, Logger, RawFlag, + RawFlags as BrowserRawFlags, ReflagClient, ReflagContext, RequestFeedbackData, @@ -132,7 +133,7 @@ export type RawFlags = Record; export type BootstrappedFlags = { context: ReflagContext; - flags: Record; + flags: BrowserRawFlags; }; const SDK_VERSION = `react-sdk/${version}`; From 18ab411e30d4319bc7a3b75071f4cc83404762bb Mon Sep 17 00:00:00 2001 From: Ron Cohen Date: Fri, 20 Mar 2026 12:39:41 +0100 Subject: [PATCH 4/4] changeset --- .changeset/small-monkeys-warn.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/small-monkeys-warn.md diff --git a/.changeset/small-monkeys-warn.md b/.changeset/small-monkeys-warn.md new file mode 100644 index 00000000..a84c4bc6 --- /dev/null +++ b/.changeset/small-monkeys-warn.md @@ -0,0 +1,5 @@ +--- +"@reflag/react-sdk": patch +--- + +Relaxed types for bootstrapping