From 5339505b6317e5a90a3a7fe15631b84c8ec96743 Mon Sep 17 00:00:00 2001 From: doswalt Date: Fri, 4 Sep 2026 11:40:31 -0400 Subject: [PATCH 1/2] remove class-validator and rejigger types loading to shrink bundles, update quicktest to include easy lite lib testing --- clientlibs/js/package.json | 3 +- clientlibs/js/quickTest.ts | 97 +++++++++++-------- clientlibs/js/quickTestLiteHttpClient.ts | 59 +++++++++++ clientlibs/js/src/ApiService/ApiService.ts | 8 +- clientlibs/js/src/Assignment/Assignment.ts | 9 +- clientlibs/js/src/DataService/DataService.ts | 2 +- .../js/src/UpGradeClient/UpgradeClient.ts | 14 +-- clientlibs/js/src/index.ts | 4 +- clientlibs/js/src/types/Interfaces.ts | 2 +- clientlibs/js/src/types/enums.ts | 5 + clientlibs/js/src/types/requests.ts | 2 +- clientlibs/js/tsconfig.json | 8 +- clientlibs/js/webpack.config.ts | 5 + 13 files changed, 151 insertions(+), 67 deletions(-) create mode 100644 clientlibs/js/quickTestLiteHttpClient.ts diff --git a/clientlibs/js/package.json b/clientlibs/js/package.json index d2c781be38..5c55116e3c 100644 --- a/clientlibs/js/package.json +++ b/clientlibs/js/package.json @@ -50,7 +50,8 @@ "docs:markdown": "typedoc --options typedoc.json", "test": "jest", "test:coverage": "jest --coverage", - "quicktest": "([ -d dist ] || yarn build) && ts-node quickTest.ts" + "quicktest": "([ -d dist ] || yarn build) && ts-node quickTest.ts", + "quicktest:lite": "([ -d dist ] || yarn build) && ts-node quickTest.ts lite" }, "keywords": [], "author": "", diff --git a/clientlibs/js/quickTest.ts b/clientlibs/js/quickTest.ts index ad0e38cbea..905e142c3a 100644 --- a/clientlibs/js/quickTest.ts +++ b/clientlibs/js/quickTest.ts @@ -1,7 +1,21 @@ -// to run: npx ts-node clientlibs/js/quickTest.ts +// to run against the full (axios-bundled) build: npx ts-node clientlibs/js/quickTest.ts +// to run against the "lite" build (BYO http client): npx ts-node clientlibs/js/quickTest.ts lite -import { AxiosError } from 'axios'; -import UpgradeClient, { MARKED_DECISION_POINT_STATUS, UpGradeClientInterfaces } from './dist/node'; +import type { UpGradeClientInterfaces } from './dist/node'; +import { FetchHttpClient } from './quickTestLiteHttpClient'; + +const variant = process.argv[2] === 'lite' ? 'lite' : 'node'; +console.log(`\n[quickTest] running against the "${variant}" build\n`); + +// dynamic require so the unused variant's bundle (and, for "node", its bundled axios) is never +// loaded -- that would defeat the point of smoke-testing "lite" in isolation. +// webpack's `libraryExport: 'default'` UMD setting makes the required module *be* the default +// export (the UpgradeClient class) directly, with MARKED_DECISION_POINT_STATUS/etc. reachable +// only as static properties on it -- there is no `.default` to unwrap. +// eslint-disable-next-line @typescript-eslint/no-var-requires +const UpgradeClient = require(`./dist/${variant}`) as typeof import('./dist/node').default; +const { MARKED_DECISION_POINT_STATUS } = UpgradeClient; +type UpgradeClientInstance = InstanceType; const URL = { LOCAL: 'http://localhost:3030', @@ -39,6 +53,9 @@ const options: UpGradeClientInterfaces.IConfigOptions = { includeStoredUserGroups, } : null, + // the "lite" build ships with no bundled http client and throws unless one is provided; + // the "node"/"browser" builds throw if one *is* provided, since they own the default (axios) client + httpClient: variant === 'lite' ? new FetchHttpClient() : undefined, }; const logRequest = [ @@ -100,65 +117,65 @@ async function quickTest() { /** test functions *******************************************************************************/ -async function doInit(client: UpgradeClient) { +async function doInit(client: UpgradeClientInstance) { try { const response = await client.init(); console.log('\n[Init response]:', JSON.stringify(response)); } catch (error) { - logAxiosError('Init', error); + logRequestError('Init', error); } } -async function doGroupMembership(client: UpgradeClient) { +async function doGroupMembership(client: UpgradeClientInstance) { const groupRequest: UpGradeClientInterfaces.IExperimentUserGroup = group; try { const response = await client.setGroupMembership(groupRequest); console.log('\n[Group response]:', JSON.stringify(response)); } catch (error) { - logAxiosError('Group', error); + logRequestError('Group', error); } } -async function doWorkingGroupMembership(client: UpgradeClient) { +async function doWorkingGroupMembership(client: UpgradeClientInstance) { const workingGroupRequest: UpGradeClientInterfaces.IExperimentUserWorkingGroup = { workingGroup }; try { const response = await client.setWorkingGroup(workingGroupRequest); console.log('\n[Working Group response]:', JSON.stringify(response)); } catch (error) { - logAxiosError('Working Group', error); + logRequestError('Working Group', error); } } -async function doAliases(client: UpgradeClient) { +async function doAliases(client: UpgradeClientInstance) { const aliasRequest = [alias]; try { const response = await client.setAltUserIds(aliasRequest); console.log('\n[Aliases response]:', JSON.stringify(response)); } catch (error) { - logAxiosError('Aliases', error); + logRequestError('Aliases', error); } } -async function doAssign(client: UpgradeClient) { +async function doAssign(client: UpgradeClientInstance) { try { const response = await client.getAllExperimentConditions(); console.log('\n[Assign response]:', JSON.stringify(response)); } catch (error) { - logAxiosError('Assign', error); + logRequestError('Assign', error); } } -async function doAssignIgnoreCache(client: UpgradeClient) { +async function doAssignIgnoreCache(client: UpgradeClientInstance) { try { const response = await client.getAllExperimentConditions({ ignoreCache: true }); console.log('\n[Assign response]:', JSON.stringify(response)); } catch (error) { - logAxiosError('Assign', error); + logRequestError('Assign', error); } } -async function doGetDecisionPointAssignment(client: UpgradeClient): Promise { +async function doGetDecisionPointAssignment(client: UpgradeClientInstance): Promise { try { const response = await client.getDecisionPointAssignment(site, target); console.log('\n[Decision Point Assignment response]:', JSON.stringify(response)); @@ -176,65 +193,65 @@ async function doGetDecisionPointAssignment(client: UpgradeClient): Promise( + url: string, + options: UpGradeClientInterfaces.IHttpClientWrapperRequestConfig + ): Promise { + return this.request('GET', url, undefined, options); + } + + public async doPost( + url: string, + body: RequestBodyType, + options: UpGradeClientInterfaces.IHttpClientWrapperRequestConfig + ): Promise { + return this.request('POST', url, body, options); + } + + public async doPatch( + url: string, + body: RequestBodyType, + options: UpGradeClientInterfaces.IHttpClientWrapperRequestConfig + ): Promise { + return this.request('PATCH', url, body, options); + } + + private async request( + method: 'GET' | 'POST' | 'PATCH', + url: string, + body: unknown, + options: UpGradeClientInterfaces.IHttpClientWrapperRequestConfig + ): Promise { + const response = await fetch(url, { + method, + headers: options.headers as Record, + credentials: options.withCredentials ? 'include' : 'same-origin', + body: body === undefined ? undefined : JSON.stringify(body), + }); + + // matches DefaultHttpClient's shape so the existing quickTest error logging works unchanged + let responseBody: unknown; + try { + responseBody = await response.json(); + } catch { + responseBody = undefined; + } + if (!response.ok) { + throw new Error(JSON.stringify({ statusCode: response.status, response: responseBody })); + } + + return responseBody as ResponseType; + } +} diff --git a/clientlibs/js/src/ApiService/ApiService.ts b/clientlibs/js/src/ApiService/ApiService.ts index cc65ab6f07..21fcd9b532 100644 --- a/clientlibs/js/src/ApiService/ApiService.ts +++ b/clientlibs/js/src/ApiService/ApiService.ts @@ -1,5 +1,11 @@ import { UpGradeClientEnums, UpGradeClientInterfaces, UpGradeClientRequests } from '../types'; -import { CaliperEnvelope, IExperimentAssignment, ILogInput, IUserAliases, ILogRequestBody } from 'upgrade_types'; +import { + CaliperEnvelope, + IExperimentAssignment, + ILogInput, + IUserAliases, + ILogRequestBody, +} from 'upgrade_types/Experiment/interfaces'; import { DataService } from 'DataService/DataService'; import { IApiServiceRequestParams, IEndpoints } from './ApiService.types'; diff --git a/clientlibs/js/src/Assignment/Assignment.ts b/clientlibs/js/src/Assignment/Assignment.ts index 18ce9b9eee..720a996beb 100644 --- a/clientlibs/js/src/Assignment/Assignment.ts +++ b/clientlibs/js/src/Assignment/Assignment.ts @@ -1,10 +1,5 @@ -import { - IExperimentAssignment, - PAYLOAD_TYPE, - EXPERIMENT_TYPE, - IPayload, - MARKED_DECISION_POINT_STATUS, -} from 'upgrade_types'; +import { IExperimentAssignment, IPayload } from 'upgrade_types/Experiment/interfaces'; +import { PAYLOAD_TYPE, EXPERIMENT_TYPE, MARKED_DECISION_POINT_STATUS } from 'upgrade_types/Experiment/enums'; import { UpGradeClientInterfaces } from '../types'; import ApiService from '../ApiService/ApiService'; diff --git a/clientlibs/js/src/DataService/DataService.ts b/clientlibs/js/src/DataService/DataService.ts index b8bc0edeca..90f01a99b2 100644 --- a/clientlibs/js/src/DataService/DataService.ts +++ b/clientlibs/js/src/DataService/DataService.ts @@ -1,5 +1,5 @@ import { UpGradeClientInterfaces } from '../types'; -import { IExperimentAssignment } from 'upgrade_types'; +import { IExperimentAssignment } from 'upgrade_types/Experiment/interfaces'; /** * Synchronous data store diff --git a/clientlibs/js/src/UpGradeClient/UpgradeClient.ts b/clientlibs/js/src/UpGradeClient/UpgradeClient.ts index 2bffb52fea..54f924c187 100644 --- a/clientlibs/js/src/UpGradeClient/UpgradeClient.ts +++ b/clientlibs/js/src/UpGradeClient/UpgradeClient.ts @@ -1,12 +1,6 @@ -import { UpGradeClientInterfaces } from '../types'; -import { - ILogInput, - CaliperEnvelope, - IExperimentAssignment, - MARKED_DECISION_POINT_STATUS, - IUserAliases, - BinaryRewardAllowedValue, -} from 'upgrade_types'; +import { UpGradeClientEnums, UpGradeClientInterfaces } from '../types'; +import { ILogInput, CaliperEnvelope, IExperimentAssignment, IUserAliases } from 'upgrade_types/Experiment/interfaces'; +import { MARKED_DECISION_POINT_STATUS } from 'upgrade_types/Experiment/enums'; import Assignment from '../Assignment/Assignment'; import ApiService from '../ApiService/ApiService'; import { DataService } from '../DataService/DataService'; @@ -75,7 +69,7 @@ export default class UpgradeClient { // allow BINARY_REWARD_VALUE to be exposed on the client a la UpgradeClient.BINARY_REWARD_VALUE // this will allow js users who are not using the upgrade types package to use this enum for sendReward() - public static BINARY_REWARD_VALUE = BinaryRewardAllowedValue; + public static BINARY_REWARD_VALUE = UpGradeClientEnums.BINARY_REWARD_VALUE; /** * When constructing UpgradeClient, the user id, api host url, and "context" identifier are required. diff --git a/clientlibs/js/src/index.ts b/clientlibs/js/src/index.ts index e5e68c25eb..9242c28b1f 100644 --- a/clientlibs/js/src/index.ts +++ b/clientlibs/js/src/index.ts @@ -1,8 +1,8 @@ import UpgradeClient from './UpGradeClient/UpgradeClient'; import Assignment from './Assignment/Assignment'; import { UpGradeClientEnums, UpGradeClientInterfaces, UpGradeClientRequests } from './types'; -import { MARKED_DECISION_POINT_STATUS } from 'upgrade_types'; -import type { IExperimentAssignment, IExperimentAssignmentv5 } from 'upgrade_types'; +import { MARKED_DECISION_POINT_STATUS } from 'upgrade_types/Experiment/enums'; +import type { IExperimentAssignment, IExperimentAssignmentv5 } from 'upgrade_types/Experiment/interfaces'; export default UpgradeClient; diff --git a/clientlibs/js/src/types/Interfaces.ts b/clientlibs/js/src/types/Interfaces.ts index 28a2df222b..3e4e107c40 100644 --- a/clientlibs/js/src/types/Interfaces.ts +++ b/clientlibs/js/src/types/Interfaces.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-namespace */ -import { IMetricMetaData, MARKED_DECISION_POINT_STATUS } from 'upgrade_types'; +import { IMetricMetaData, MARKED_DECISION_POINT_STATUS } from 'upgrade_types/Experiment/enums'; export namespace UpGradeClientInterfaces { // this namespace should be for consumer facing interface diff --git a/clientlibs/js/src/types/enums.ts b/clientlibs/js/src/types/enums.ts index 69acecc3ed..988833302c 100644 --- a/clientlibs/js/src/types/enums.ts +++ b/clientlibs/js/src/types/enums.ts @@ -5,4 +5,9 @@ export namespace UpGradeClientEnums { POST = 'POST', PATCH = 'PATCH', } + + export enum BINARY_REWARD_VALUE { + SUCCESS = 'SUCCESS', + FAILURE = 'FAILURE', + } } diff --git a/clientlibs/js/src/types/requests.ts b/clientlibs/js/src/types/requests.ts index 87efddfcb5..74695e1d25 100644 --- a/clientlibs/js/src/types/requests.ts +++ b/clientlibs/js/src/types/requests.ts @@ -1,4 +1,4 @@ -import { MARKED_DECISION_POINT_STATUS } from 'upgrade_types'; +import { MARKED_DECISION_POINT_STATUS } from 'upgrade_types/Experiment/enums'; import { UpGradeClientInterfaces } from './Interfaces'; /* eslint-disable @typescript-eslint/no-namespace */ diff --git a/clientlibs/js/tsconfig.json b/clientlibs/js/tsconfig.json index 070288b7bc..300b405bc0 100644 --- a/clientlibs/js/tsconfig.json +++ b/clientlibs/js/tsconfig.json @@ -2,6 +2,7 @@ "compileOnSave": false, "compilerOptions": { "baseUrl": "./src", + "rootDir": "../..", "outDir": "./dist", "sourceMap": false, "declaration": true, @@ -18,15 +19,14 @@ "target": "es6", "ignoreDeprecations": "6.0", "typeRoots": ["./node_modules/@types"], - "types": ["node"], - "ignoreDeprecations": "6.0", "lib": ["es2018", "dom"], "types": ["jest", "node"], "paths": { - "upgrade_types": ["../../../packages/types"] + "upgrade_types": ["../../../packages/types"], + "upgrade_types/*": ["../../../packages/types/src/*"] }, "esModuleInterop": true }, - "include": ["src", ".eslintrc.js", "webpack.config.ts"], + "include": ["src", ".eslintrc.js", "webpack.config.ts", "quickTest.ts", "quickTestLiteHttpClient.ts"], "exclude": ["src/**/*.spec.ts"] } diff --git a/clientlibs/js/webpack.config.ts b/clientlibs/js/webpack.config.ts index 4218a5d433..0be7c84584 100644 --- a/clientlibs/js/webpack.config.ts +++ b/clientlibs/js/webpack.config.ts @@ -19,6 +19,11 @@ const generalConfiguration = { resolve: { alias: { upgrade_types: path.resolve(__dirname, '../../packages/types/src'), + // packages/types is outside this package's node_modules, so its own + // tslib resolves from the repo-root install while files under src/ + // resolve tslib from this package's local install. Force both to the + // same physical module so webpack doesn't bundle it twice. + tslib: path.resolve(__dirname, 'node_modules/tslib'), }, extensions: ['.tsx', '.ts', '.js'], }, From 836b6fa605891fc69c497090c3c610701cf45196 Mon Sep 17 00:00:00 2001 From: danoswaltCL <97542869+danoswaltCL@users.noreply.github.com> Date: Fri, 4 Sep 2026 12:13:44 -0400 Subject: [PATCH 2/2] Update quicktest scripts to check for dist build files Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- clientlibs/js/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clientlibs/js/package.json b/clientlibs/js/package.json index 5c55116e3c..c86c49af38 100644 --- a/clientlibs/js/package.json +++ b/clientlibs/js/package.json @@ -50,8 +50,8 @@ "docs:markdown": "typedoc --options typedoc.json", "test": "jest", "test:coverage": "jest --coverage", - "quicktest": "([ -d dist ] || yarn build) && ts-node quickTest.ts", - "quicktest:lite": "([ -d dist ] || yarn build) && ts-node quickTest.ts lite" + "quicktest": "([ -f dist/node/index.js ] || yarn build) && ts-node quickTest.ts", + "quicktest:lite": "([ -f dist/lite/index.js ] || yarn build) && ts-node quickTest.ts lite" }, "keywords": [], "author": "",