feat: add server-side TestHook and migrate server-node/shopify-oxygen to shared utils [SDK-2009]#1168
feat: add server-side TestHook and migrate server-node/shopify-oxygen to shared utils [SDK-2009]#1168
Conversation
… to shared utils [SDK-2009] - Add ServerSideTestHook using got for HTTP callbacks in contract-test-utils - Create src/server.ts barrel re-exporting index + ServerSideTestHook - Add tsconfig.server.json excluding client-side sources - Add ./server subpath export pointing to compiled dist/server.js - Add got and @launchdarkly/node-server-sdk as runtime dependencies - Migrate server-node contract tests to import from shared package - Migrate Shopify Oxygen to use shared ClientPool<LDClient> - Update Shopify Oxygen tsup.config.ts with noExternal for shared package Co-Authored-By: Steven Zhang <szhang@launchdarkly.com>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
|
@launchdarkly/js-sdk-common size report |
|
@launchdarkly/js-client-sdk size report |
|
@launchdarkly/browser size report |
|
@launchdarkly/js-client-sdk-common size report |
- Change ./server export to point to source .ts files (like ./client) so contract-test-utils doesn't need a separate build step in CI - Restore .js extensions in relative imports for node16 moduleResolution - server.ts only exports ServerSideTestHook and ClientPool (not index) to avoid pulling in client-sdk-common deps in server context Co-Authored-By: Steven Zhang <szhang@launchdarkly.com>
…to CI - Revert ./server subpath export to point to compiled dist/server.js (source exports don't work at runtime with node16 module resolution) - Add 'Build shared contract test utils (server)' step to server-node CI workflow so dist/ files exist before contract tests build - server.ts only re-exports ServerSideTestHook and ClientPool to avoid pulling in client-sdk-common deps in server context Co-Authored-By: Steven Zhang <szhang@launchdarkly.com>
- Only include src/server.ts and src/server-side/* to avoid compiling files that depend on @launchdarkly/js-client-sdk-common which isn't available in the server-node CI environment Co-Authored-By: Steven Zhang <szhang@launchdarkly.com>
These are only needed by the ./server subpath. Making them optional peer dependencies prevents them from being hoisted into unrelated workspaces (e.g. React SDK) where @types/cacheable-request conflicts with TypeScript compilation. Co-Authored-By: Steven Zhang <szhang@launchdarkly.com>
…elated CI The default 'build' script was running 'tsc' which compiled server-side files (TestHook.ts) that depend on @launchdarkly/node-server-sdk and got. When other CI jobs (e.g. React SDK) run 'yarn workspaces foreach ... build', this compilation fails because those optional peer deps aren't installed. Since ./ and ./client exports point to source .ts files (no compilation needed), the build script is now a no-op. Server-side dist files are produced by 'build:server' which is explicitly called in server-node CI. Co-Authored-By: Steven Zhang <szhang@launchdarkly.com>
…ution index.ts is consumed as source .ts (not compiled), so .js extensions break Turbopack module resolution in the React SDK contract tests. Only server.ts (compiled to dist/) needs .js extensions. Co-Authored-By: Steven Zhang <szhang@launchdarkly.com>
…d ClientPool - Replace inline clientCounter + Record with shared ClientPool<SdkClientEntity> - Replace local SdkConfigOptions with shared SDKConfigParams (extended for server) - Type newSdkClientEntity options with ServerCreateInstanceParams - Re-export shared config types through ./server barrel for node16 consumers - Import TestHook, SDKConfigParams, CreateInstanceParams from shared ./server Co-Authored-By: Steven Zhang <szhang@launchdarkly.com>
Co-Authored-By: Steven Zhang <szhang@launchdarkly.com>
…sdk-common dep in CI The build:server compilation in CI doesn't have @launchdarkly/js-client-sdk-common available (focused install for server-node only). Reverting type re-exports from server.ts and keeping server-node config types local. ClientPool deduplication in index.ts is preserved. Co-Authored-By: Steven Zhang <szhang@launchdarkly.com>
Co-Authored-By: Steven Zhang <szhang@launchdarkly.com>
…K dependency isolation - src/types/ now contains only SDK-independent types (no LDContext/LDEvaluationReason) - src/client-side/types/ has client-specific types importing from js-client-sdk-common - src/server-side/types/ has server-specific types importing from js-server-sdk-common - server.ts barrel re-exports shared + server types (bigSegments, dataSystem, migrations) - client.ts barrel re-exports shared + client types (SDKConfigClientSideParams) - index.ts exports only universal types with no SDK dependency - server-node contract tests now import CommandParams, CreateInstanceParams, SDKConfigParams from shared package - Added @launchdarkly/js-server-sdk-common as optional peer dependency Co-Authored-By: Steven Zhang <szhang@launchdarkly.com>
…1151 - Add src/types/compat.ts with minimal LDContext and LDEvaluationReason types compatible with both client and server SDKs (no SDK imports needed) - Merge all types back into unified CommandParams.ts and ConfigParams.ts (includes both client-specific and server-specific fields) - Delete src/client-side/types/ and src/server-side/types/ directories (no longer needed with compat types approach) - Simplify barrel exports: client.ts re-exports from index, server.ts re-exports from types/ directly - Remove @launchdarkly/js-server-sdk-common peer dependency - Add necessary casts in server-node consumer for SDK-specific types Co-Authored-By: Steven Zhang <szhang@launchdarkly.com>
Requirements
Related issues
Describe the solution you've provided
Moves the server-node
TestHook(which usesgotfor HTTP callbacks) into the shared@launchdarkly/js-contract-test-utilspackage and migrates both server-node and Shopify Oxygen contract tests to use shared infrastructure. Also deduplicates shared types (command params, config params) so both client and server contract tests consume them from the shared package.Changes to
packages/tooling/contract-test-utils/:src/server-side/TestHook.ts— moved from server-node contract tests (exact rename, no content changes)src/server.ts— barrel re-exportingServerSideTestHook,ClientPool, and all shared typestsconfig.server.json— server-only TypeScript configsrc/types/compat.ts— minimalLDContextandLDEvaluationReasontype definitions compatible with both client and server SDKs, allowing shared types to avoid importing from either SDK package directlysrc/types/CommandParams.ts— now imports fromcompat.tsinstead of@launchdarkly/js-client-sdk-common; includes both client and server command fields (e.g.migrationVariation,registerFlagChangeListener)src/types/ConfigParams.ts— same pattern; includes bothclientSideand server-specific fields (bigSegments,dataSystem)src/index.ts/src/client.ts— simplified barrel exports;client.tsre-exports fromindexpackage.json:"./server"subpath export pointing to compileddist/server.jsgotand@launchdarkly/node-server-sdkadded as optional peer dependenciesbuildscript changed to no-op;build:serverscript added for explicit server compilationtsconfig.json— addedgotpath mapping for TypeScript resolutionMigrations:
TestHook.ts; importsServerSideTestHookfrom shared packageclientCounter+Record<string, SdkClientEntity>inindex.tswith sharedClientPool<SdkClientEntity>CommandParams,CreateInstanceParams,SDKConfigParamsfrom shared package (~130 lines of duplicated local type definitions removed)unknownorstringbut SDK expects specific types (e.g.,pe.defaultValue as boolean,migrationVariation.defaultStage as LDMigrationStage)Record<string, LDClient>+ counter with sharedClientPool<LDClient>noExternaltotsup.config.tsto bundle shared package inline"client-1"to"1"(auto-generated byClientPool.add())CI:
.github/workflows/server-node.yml— addedbuild:serverstep before contract test build sodist/server.jsexists at import timeCompat types pattern: Types use minimal compatible definitions (
LDContext = Record<string, any>,LDEvaluationReason = { kind: string; ... }) to avoid importing from SDK packages directly. This allows one unified set of type files to work for both client and server, but requires consumers to add explicit casts when passing values to SDK methods (e.g.,defaultValue as boolean).Type safety tradeoff: The casts in
sdkClientEntity.ts(e.g.,pe.defaultValue as boolean,migrationVariation.defaultStage as LDMigrationStage) could mask real type errors if the contract test harness sends incorrect types. However, this follows the pattern from PR feat: create shared contract test utilities package (SDK-1866) #1151 which was already merged.ClientPool API change:
ClientPool.add(client)now auto-generates and returns the client ID instead of requiring an explicit ID. Shopify Oxygen client IDs changed from"client-1"to"1". CI contract tests pass, confirming the test harness handles this correctly.gotpath mapping:tsconfig.jsonuses a hardcoded relative path ("../../../node_modules/got/dist/source") which is fragile and depends on Yarn's hoisting layout. This is a workaround because@launchdarkly/js-client-sdk-commondoesn't work withnode16module resolution whilegotv14 requires it.buildscript is now a no-op: The./and./clientexports serve source.tsfiles and don't need compilation. Only the./serversubpath needs compiled output viabuild:server.Optional peer deps: Consumers of the
./serversubpath must havegotand@launchdarkly/node-server-sdkinstalled. The server-node and shopify-oxygen contract test packages already do.Describe alternatives you've considered
./serveras source files (like./client) — rejected because server-side code has dependencies that may not resolve correctly in all consumer contextsgotandnode-server-sdkas regulardependencies— rejected because transitive types break React SDK compilationAdditional context
Link to Devin Session: https://app.devin.ai/sessions/f0a2bd3c755e49c3b639bd3ee770effe
Requested by: @joker23
All CI checks pass (40/40 checks passing).
No test coverage added in this PR — the existing contract test suites serve as integration tests for the shared utilities.