From 3c99b206c0fa2e27553208891effe859b7c282f8 Mon Sep 17 00:00:00 2001 From: Steven Zhang Date: Fri, 9 Oct 2026 11:19:11 -0400 Subject: [PATCH 1/2] test: enable bootstrap contract tests --- .../sdk/browser/contract-tests/entity/src/ClientEntity.ts | 6 +++++- .../contract-tests/entity/src/TestHarnessWebSocket.ts | 1 + packages/sdk/node-client/contract-tests/src/index.ts | 1 + .../sdk/node-client/contract-tests/src/sdkClientEntity.ts | 6 +++++- .../react-native/contract-tests/entity/src/ClientEntity.ts | 7 ++++++- .../contract-tests/entity/src/TestHarnessWebSocket.ts | 1 + packages/sdk/react/contract-tests/app/ClientRoot.tsx | 6 +++++- .../sdk/react/contract-tests/app/TestHarnessWebSocket.ts | 1 + packages/sdk/vue/contract-tests/src/ClientEntity.ts | 2 ++ .../sdk/vue/contract-tests/src/TestHarnessWebSocket.ts | 1 + .../tooling/contract-test-utils/src/types/ConfigParams.ts | 1 + 11 files changed, 29 insertions(+), 4 deletions(-) diff --git a/packages/sdk/browser/contract-tests/entity/src/ClientEntity.ts b/packages/sdk/browser/contract-tests/entity/src/ClientEntity.ts index 739bfd7c61..8b65e2ae6f 100644 --- a/packages/sdk/browser/contract-tests/entity/src/ClientEntity.ts +++ b/packages/sdk/browser/contract-tests/entity/src/ClientEntity.ts @@ -354,7 +354,11 @@ export async function newSdkClientEntity(options: CreateInstanceParams) { initialContext, sdkConfig, ); - const { status } = await client.start({ timeout: timeout / 1000 }); + const bootstrap = options.configuration.clientSide?.bootstrap; + const { status } = await client.start({ + timeout: timeout / 1000, + ...(bootstrap !== undefined && { bootstrap }), + }); const failed = status !== 'complete'; if (failed && !options.configuration.initCanFail) { client.close(); diff --git a/packages/sdk/browser/contract-tests/entity/src/TestHarnessWebSocket.ts b/packages/sdk/browser/contract-tests/entity/src/TestHarnessWebSocket.ts index de8f5f5c6d..9bde09419b 100644 --- a/packages/sdk/browser/contract-tests/entity/src/TestHarnessWebSocket.ts +++ b/packages/sdk/browser/contract-tests/entity/src/TestHarnessWebSocket.ts @@ -46,6 +46,7 @@ export default class TestHarnessWebSocket { 'client-per-context-summaries', 'track-hooks', 'fdv1-fallback', + 'bootstrap', ]; break; diff --git a/packages/sdk/node-client/contract-tests/src/index.ts b/packages/sdk/node-client/contract-tests/src/index.ts index 64e19c7fc9..9dd67b80ad 100644 --- a/packages/sdk/node-client/contract-tests/src/index.ts +++ b/packages/sdk/node-client/contract-tests/src/index.ts @@ -43,6 +43,7 @@ app.get('/', (req: Request, res: Response) => { 'wrapper', 'fdv1-fallback', 'client-event-source-http-errors', + 'bootstrap', ], }); }); diff --git a/packages/sdk/node-client/contract-tests/src/sdkClientEntity.ts b/packages/sdk/node-client/contract-tests/src/sdkClientEntity.ts index a1cb6c06e1..f35737b0b7 100644 --- a/packages/sdk/node-client/contract-tests/src/sdkClientEntity.ts +++ b/packages/sdk/node-client/contract-tests/src/sdkClientEntity.ts @@ -277,7 +277,11 @@ export async function newSdkClientEntity(options: CreateInstanceParams): Promise initialContext, sdkConfig, ); - const startResult = await client.start({ timeout: timeout / 1000 }); + const bootstrap = options.configuration.clientSide?.bootstrap; + const startResult = await client.start({ + timeout: timeout / 1000, + ...(bootstrap !== undefined && { bootstrap }), + }); const failed = startResult.status !== 'complete'; if (failed && !options.configuration.initCanFail) { await client.close(); diff --git a/packages/sdk/react-native/contract-tests/entity/src/ClientEntity.ts b/packages/sdk/react-native/contract-tests/entity/src/ClientEntity.ts index 21b8b5f257..824c13c359 100644 --- a/packages/sdk/react-native/contract-tests/entity/src/ClientEntity.ts +++ b/packages/sdk/react-native/contract-tests/entity/src/ClientEntity.ts @@ -364,8 +364,13 @@ export async function newSdkClientEntity(options: CreateInstanceParams) { let failed = false; try { + const bootstrap = options.configuration.clientSide?.bootstrap; await Promise.race([ - client.identify(initialContext, { timeout: timeout / 1000, waitForNetworkResults: true }), + client.identify(initialContext, { + timeout: timeout / 1000, + waitForNetworkResults: true, + ...(bootstrap !== undefined && { bootstrap }), + }), new Promise((_resolve, reject) => { setTimeout(reject, timeout); }), diff --git a/packages/sdk/react-native/contract-tests/entity/src/TestHarnessWebSocket.ts b/packages/sdk/react-native/contract-tests/entity/src/TestHarnessWebSocket.ts index 979eb816ed..dad55c9f48 100644 --- a/packages/sdk/react-native/contract-tests/entity/src/TestHarnessWebSocket.ts +++ b/packages/sdk/react-native/contract-tests/entity/src/TestHarnessWebSocket.ts @@ -60,6 +60,7 @@ export default class TestHarnessWebSocket { 'track-hooks', 'fdv1-fallback', 'client-event-source-http-errors', + 'bootstrap', ]; break; diff --git a/packages/sdk/react/contract-tests/app/ClientRoot.tsx b/packages/sdk/react/contract-tests/app/ClientRoot.tsx index fb282868c0..72c73759df 100644 --- a/packages/sdk/react/contract-tests/app/ClientRoot.tsx +++ b/packages/sdk/react/contract-tests/app/ClientRoot.tsx @@ -60,7 +60,11 @@ export default function ClientRoot({ children }: { children: React.ReactNode }) sdkConfig, ); - const { status } = await client.start({ timeout: timeout / 1000 }); + const bootstrap = params.configuration.clientSide?.bootstrap; + const { status } = await client.start({ + timeout: timeout / 1000, + ...(bootstrap !== undefined && { bootstrap }), + }); if (status === 'failed' && !params.configuration.initCanFail) { client.close(); diff --git a/packages/sdk/react/contract-tests/app/TestHarnessWebSocket.ts b/packages/sdk/react/contract-tests/app/TestHarnessWebSocket.ts index 8d09d8c2de..e8c72d5c18 100644 --- a/packages/sdk/react/contract-tests/app/TestHarnessWebSocket.ts +++ b/packages/sdk/react/contract-tests/app/TestHarnessWebSocket.ts @@ -50,6 +50,7 @@ export default class TestHarnessWebSocket { 'client-per-context-summaries', 'track-hooks', 'fdv1-fallback', + 'bootstrap', ]; break; diff --git a/packages/sdk/vue/contract-tests/src/ClientEntity.ts b/packages/sdk/vue/contract-tests/src/ClientEntity.ts index 3b5da3d559..660ca5e965 100644 --- a/packages/sdk/vue/contract-tests/src/ClientEntity.ts +++ b/packages/sdk/vue/contract-tests/src/ClientEntity.ts @@ -403,8 +403,10 @@ export async function newSdkClientEntity(options: CreateInstanceParams) { // Forward a harness-supplied secure mode hash as the `h` query parameter, only when // the harness actually configured one. const secureModeHash = options.configuration.clientSide?.hash; + const bootstrap = options.configuration.clientSide?.bootstrap; const result = await client.start({ timeout: timeout / 1000, + ...(bootstrap !== undefined && { bootstrap }), ...(secureModeHash !== undefined && { identifyOptions: { hash: secureModeHash } }), }); if (result.status !== 'complete') { diff --git a/packages/sdk/vue/contract-tests/src/TestHarnessWebSocket.ts b/packages/sdk/vue/contract-tests/src/TestHarnessWebSocket.ts index 3d9ff65138..95de66e09e 100644 --- a/packages/sdk/vue/contract-tests/src/TestHarnessWebSocket.ts +++ b/packages/sdk/vue/contract-tests/src/TestHarnessWebSocket.ts @@ -50,6 +50,7 @@ export default class TestHarnessWebSocket { 'secure-mode-hash', 'flag-change-listeners', 'fdv1-fallback', + 'bootstrap', ]; break; diff --git a/packages/tooling/contract-test-utils/src/types/ConfigParams.ts b/packages/tooling/contract-test-utils/src/types/ConfigParams.ts index 848fdde91f..25156ce07c 100644 --- a/packages/tooling/contract-test-utils/src/types/ConfigParams.ts +++ b/packages/tooling/contract-test-utils/src/types/ConfigParams.ts @@ -102,6 +102,7 @@ export interface SDKConfigClientSideParams { useReport?: boolean; includeEnvironmentAttributes?: boolean; hash?: string; + bootstrap?: unknown; } export interface SDKConfigEvaluationHookData { From feb1852127affffee1c5da020d339020cd7ac523 Mon Sep 17 00:00:00 2001 From: Steven Zhang Date: Fri, 9 Oct 2026 13:02:45 -0400 Subject: [PATCH 2/2] ci: treat null bootstrap configuration as abset in contract test services --- packages/sdk/browser/contract-tests/entity/src/ClientEntity.ts | 2 +- packages/sdk/node-client/contract-tests/src/sdkClientEntity.ts | 2 +- .../sdk/react-native/contract-tests/entity/src/ClientEntity.ts | 2 +- packages/sdk/react/contract-tests/app/ClientRoot.tsx | 2 +- packages/sdk/vue/contract-tests/src/ClientEntity.ts | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/sdk/browser/contract-tests/entity/src/ClientEntity.ts b/packages/sdk/browser/contract-tests/entity/src/ClientEntity.ts index 8b65e2ae6f..124d31ae41 100644 --- a/packages/sdk/browser/contract-tests/entity/src/ClientEntity.ts +++ b/packages/sdk/browser/contract-tests/entity/src/ClientEntity.ts @@ -357,7 +357,7 @@ export async function newSdkClientEntity(options: CreateInstanceParams) { const bootstrap = options.configuration.clientSide?.bootstrap; const { status } = await client.start({ timeout: timeout / 1000, - ...(bootstrap !== undefined && { bootstrap }), + ...(bootstrap != null && { bootstrap }), }); const failed = status !== 'complete'; if (failed && !options.configuration.initCanFail) { diff --git a/packages/sdk/node-client/contract-tests/src/sdkClientEntity.ts b/packages/sdk/node-client/contract-tests/src/sdkClientEntity.ts index f35737b0b7..d281d3ae96 100644 --- a/packages/sdk/node-client/contract-tests/src/sdkClientEntity.ts +++ b/packages/sdk/node-client/contract-tests/src/sdkClientEntity.ts @@ -280,7 +280,7 @@ export async function newSdkClientEntity(options: CreateInstanceParams): Promise const bootstrap = options.configuration.clientSide?.bootstrap; const startResult = await client.start({ timeout: timeout / 1000, - ...(bootstrap !== undefined && { bootstrap }), + ...(bootstrap != null && { bootstrap }), }); const failed = startResult.status !== 'complete'; if (failed && !options.configuration.initCanFail) { diff --git a/packages/sdk/react-native/contract-tests/entity/src/ClientEntity.ts b/packages/sdk/react-native/contract-tests/entity/src/ClientEntity.ts index 824c13c359..979763e049 100644 --- a/packages/sdk/react-native/contract-tests/entity/src/ClientEntity.ts +++ b/packages/sdk/react-native/contract-tests/entity/src/ClientEntity.ts @@ -369,7 +369,7 @@ export async function newSdkClientEntity(options: CreateInstanceParams) { client.identify(initialContext, { timeout: timeout / 1000, waitForNetworkResults: true, - ...(bootstrap !== undefined && { bootstrap }), + ...(bootstrap != null && { bootstrap }), }), new Promise((_resolve, reject) => { setTimeout(reject, timeout); diff --git a/packages/sdk/react/contract-tests/app/ClientRoot.tsx b/packages/sdk/react/contract-tests/app/ClientRoot.tsx index 72c73759df..5a47e65631 100644 --- a/packages/sdk/react/contract-tests/app/ClientRoot.tsx +++ b/packages/sdk/react/contract-tests/app/ClientRoot.tsx @@ -63,7 +63,7 @@ export default function ClientRoot({ children }: { children: React.ReactNode }) const bootstrap = params.configuration.clientSide?.bootstrap; const { status } = await client.start({ timeout: timeout / 1000, - ...(bootstrap !== undefined && { bootstrap }), + ...(bootstrap != null && { bootstrap }), }); if (status === 'failed' && !params.configuration.initCanFail) { diff --git a/packages/sdk/vue/contract-tests/src/ClientEntity.ts b/packages/sdk/vue/contract-tests/src/ClientEntity.ts index 660ca5e965..618b789f5c 100644 --- a/packages/sdk/vue/contract-tests/src/ClientEntity.ts +++ b/packages/sdk/vue/contract-tests/src/ClientEntity.ts @@ -406,7 +406,7 @@ export async function newSdkClientEntity(options: CreateInstanceParams) { const bootstrap = options.configuration.clientSide?.bootstrap; const result = await client.start({ timeout: timeout / 1000, - ...(bootstrap !== undefined && { bootstrap }), + ...(bootstrap != null && { bootstrap }), ...(secureModeHash !== undefined && { identifyOptions: { hash: secureModeHash } }), }); if (result.status !== 'complete') {