Skip to content

Commit b10dbb5

Browse files
Merge branch 'master' into junaed/fssdk-11403-readme-update
2 parents df289b2 + e3234c7 commit b10dbb5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+4417
-6276
lines changed

.github/workflows/javascript.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ jobs:
6363
runs-on: ubuntu-latest
6464
strategy:
6565
matrix:
66-
node: ['18', '20', '22']
66+
node: ['18', '20', '22', '24']
6767
steps:
6868
- uses: actions/checkout@v3
6969
- name: Set up Node ${{ matrix.node }}

README.md

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,21 +31,15 @@ Optimizely Rollouts is [free feature flags](https://www.optimizely.com/free-feat
3131
3232
### Prerequisites
3333

34-
Ensure the SDK supports all of the platforms you're targeting. In particular, the SDK targets modern ES5-compliant JavaScript environment. We officially support:
35-
- Node.js >= 16.0.0. By extension, environments like AWS Lambda, Google Cloud Functions, and Auth0 Webtasks are supported as well. Older Node.js releases likely work too (try `npm test` to validate for yourself), but are not formally supported.
34+
Ensure the SDK supports all of the platforms you're targeting. In particular, the SDK targets modern ES6-compliant JavaScript environments. We officially support:
35+
- Node.js >= 18.0.0. By extension, environments like AWS Lambda, Google Cloud Functions, and Auth0 Webtasks are supported as well. Older Node.js releases likely work too (try `npm test` to validate for yourself), but are not formally supported.
3636
- Modern Web Browsers, such as Microsoft Edge 84+, Firefox 91+, Safari 13+, and Chrome 102+, Opera 76+
3737

3838
In addition, other environments are likely compatible but are not formally supported including:
3939
- Progressive Web Apps, WebViews, and hybrid mobile apps like those built with React Native and Apache Cordova.
4040
- [Cloudflare Workers](https://developers.cloudflare.com/workers/) and [Fly](https://fly.io/), both of which are powered by recent releases of V8.
4141
- Anywhere else you can think of that might embed a JavaScript engine. The sky is the limit; experiment everywhere! 🚀
4242

43-
### Requirements
44-
45-
* JavaScript (Browser): Modern web browser that is ES6-compliant.
46-
47-
* JavaScript (Node): Node 16.0.0+
48-
4943

5044
### Install the SDK
5145

lib/client_factory.spec.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/**
2+
* Copyright 2025, Optimizely
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { describe, it, expect } from 'vitest';
18+
19+
import { getOptimizelyInstance } from './client_factory';
20+
import { createStaticProjectConfigManager } from './project_config/config_manager_factory';
21+
import Optimizely from './optimizely';
22+
23+
describe('getOptimizelyInstance', () => {
24+
it('should throw if the projectConfigManager is not a valid ProjectConfigManager', () => {
25+
expect(() => getOptimizelyInstance({
26+
projectConfigManager: undefined as any,
27+
requestHandler: {} as any,
28+
})).toThrow('Invalid config manager');
29+
30+
expect(() => getOptimizelyInstance({
31+
projectConfigManager: null as any,
32+
requestHandler: {} as any,
33+
})).toThrow('Invalid config manager');
34+
35+
expect(() => getOptimizelyInstance({
36+
projectConfigManager: 'abc' as any,
37+
requestHandler: {} as any,
38+
})).toThrow('Invalid config manager');
39+
40+
expect(() => getOptimizelyInstance({
41+
projectConfigManager: 123 as any,
42+
requestHandler: {} as any,
43+
})).toThrow('Invalid config manager');
44+
45+
expect(() => getOptimizelyInstance({
46+
projectConfigManager: {} as any,
47+
requestHandler: {} as any,
48+
})).toThrow('Invalid config manager');
49+
});
50+
51+
it('should return an instance of Optimizely if a valid projectConfigManager is provided', () => {
52+
const optimizelyInstance = getOptimizelyInstance({
53+
projectConfigManager: createStaticProjectConfigManager({
54+
datafile: '{}',
55+
}),
56+
requestHandler: {} as any,
57+
});
58+
59+
expect(optimizelyInstance).toBeInstanceOf(Optimizely);
60+
});
61+
});

lib/client_factory.ts

Lines changed: 42 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,7 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
17-
import { LoggerFacade } from "./logging/logger";
1816
import { Client, Config } from "./shared_types";
19-
import { Maybe } from "./utils/type";
20-
import configValidator from './utils/config_validator';
2117
import { extractLogger } from "./logging/logger_factory";
2218
import { extractErrorNotifier } from "./error/error_notifier_factory";
2319
import { extractConfigManager } from "./project_config/config_manager_factory";
@@ -35,61 +31,50 @@ export type OptimizelyFactoryConfig = Config & {
3531
requestHandler: RequestHandler;
3632
}
3733

38-
export const getOptimizelyInstance = (config: OptimizelyFactoryConfig): Client | null => {
39-
let logger: Maybe<LoggerFacade>;
40-
41-
try {
42-
logger = config.logger ? extractLogger(config.logger) : undefined;
43-
44-
configValidator.validate(config);
45-
46-
const {
47-
clientEngine,
48-
clientVersion,
49-
jsonSchemaValidator,
50-
userProfileService,
51-
userProfileServiceAsync,
52-
defaultDecideOptions,
53-
disposable,
54-
requestHandler,
55-
} = config;
56-
57-
const errorNotifier = config.errorNotifier ? extractErrorNotifier(config.errorNotifier) : undefined;
58-
59-
const projectConfigManager = extractConfigManager(config.projectConfigManager);
60-
const eventProcessor = config.eventProcessor ? extractEventProcessor(config.eventProcessor) : undefined;
61-
const odpManager = config.odpManager ? extractOdpManager(config.odpManager) : undefined;
62-
const vuidManager = config.vuidManager ? extractVuidManager(config.vuidManager) : undefined;
34+
export const getOptimizelyInstance = (config: OptimizelyFactoryConfig): Client => {
35+
const {
36+
clientEngine,
37+
clientVersion,
38+
jsonSchemaValidator,
39+
userProfileService,
40+
userProfileServiceAsync,
41+
defaultDecideOptions,
42+
disposable,
43+
requestHandler,
44+
} = config;
45+
46+
const projectConfigManager = extractConfigManager(config.projectConfigManager);
47+
const eventProcessor = extractEventProcessor(config.eventProcessor);
48+
const odpManager = extractOdpManager(config.odpManager);
49+
const vuidManager = extractVuidManager(config.vuidManager);
50+
const errorNotifier = extractErrorNotifier(config.errorNotifier);
51+
const logger = extractLogger(config.logger);
6352

64-
const cmabClient = new DefaultCmabClient({
65-
requestHandler,
66-
});
53+
const cmabClient = new DefaultCmabClient({
54+
requestHandler,
55+
});
6756

68-
const cmabService = new DefaultCmabService({
69-
cmabClient,
70-
cmabCache: new InMemoryLruCache<CmabCacheValue>(DEFAULT_CMAB_CACHE_SIZE, DEFAULT_CMAB_CACHE_TIMEOUT),
71-
});
57+
const cmabService = new DefaultCmabService({
58+
cmabClient,
59+
cmabCache: new InMemoryLruCache<CmabCacheValue>(DEFAULT_CMAB_CACHE_SIZE, DEFAULT_CMAB_CACHE_TIMEOUT),
60+
});
7261

73-
const optimizelyOptions = {
74-
cmabService,
75-
clientEngine: clientEngine || JAVASCRIPT_CLIENT_ENGINE,
76-
clientVersion: clientVersion || CLIENT_VERSION,
77-
jsonSchemaValidator,
78-
userProfileService,
79-
userProfileServiceAsync,
80-
defaultDecideOptions,
81-
disposable,
82-
logger,
83-
errorNotifier,
84-
projectConfigManager,
85-
eventProcessor,
86-
odpManager,
87-
vuidManager,
88-
};
62+
const optimizelyOptions = {
63+
cmabService,
64+
clientEngine: clientEngine || JAVASCRIPT_CLIENT_ENGINE,
65+
clientVersion: clientVersion || CLIENT_VERSION,
66+
jsonSchemaValidator,
67+
userProfileService,
68+
userProfileServiceAsync,
69+
defaultDecideOptions,
70+
disposable,
71+
logger,
72+
errorNotifier,
73+
projectConfigManager,
74+
eventProcessor,
75+
odpManager,
76+
vuidManager,
77+
};
8978

90-
return new Optimizely(optimizelyOptions);
91-
} catch (e) {
92-
logger?.error(e);
93-
return null;
94-
}
79+
return new Optimizely(optimizelyOptions);
9580
}

lib/core/decision_service/index.tests.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import {
2525
LOG_LEVEL,
2626
DECISION_SOURCES,
2727
} from '../../utils/enums';
28-
import { getForwardingEventProcessor } from '../../event_processor/forwarding_event_processor';
28+
import { getForwardingEventProcessor } from '../../event_processor/event_processor_factory';
2929
import { createNotificationCenter } from '../../notification_center';
3030
import Optimizely from '../../optimizely';
3131
import OptimizelyUserContext from '../../optimizely_user_context';

lib/entrypoint.test-d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ import { Maybe } from './utils/type';
5959

6060
export type Entrypoint = {
6161
// client factory
62-
createInstance: (config: Config) => Client | null;
62+
createInstance: (config: Config) => Client;
6363

6464
// config manager related exports
6565
createStaticProjectConfigManager: (config: StaticConfigManagerConfig) => OpaqueConfigManager;

lib/entrypoint.universal.test-d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ import { UniversalOdpManagerOptions } from './odp/odp_manager_factory.universal'
5555

5656
export type UniversalEntrypoint = {
5757
// client factory
58-
createInstance: (config: UniversalConfig) => Client | null;
58+
createInstance: (config: UniversalConfig) => Client;
5959

6060
// config manager related exports
6161
createStaticProjectConfigManager: (config: StaticConfigManagerConfig) => OpaqueConfigManager;
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/**
2+
* Copyright 2025, Optimizely
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { describe, it, expect } from 'vitest';
18+
import { createErrorNotifier } from './error_notifier_factory';
19+
20+
describe('createErrorNotifier', () => {
21+
it('should throw errors for invalid error handlers', () => {
22+
expect(() => createErrorNotifier(null as any)).toThrow('Invalid error handler');
23+
expect(() => createErrorNotifier(undefined as any)).toThrow('Invalid error handler');
24+
25+
26+
expect(() => createErrorNotifier('abc' as any)).toThrow('Invalid error handler');
27+
expect(() => createErrorNotifier(123 as any)).toThrow('Invalid error handler');
28+
29+
expect(() => createErrorNotifier({} as any)).toThrow('Invalid error handler');
30+
31+
expect(() => createErrorNotifier({ handleError: 'abc' } as any)).toThrow('Invalid error handler');
32+
});
33+
});

lib/error/error_notifier_factory.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,35 @@
1414
* limitations under the License.
1515
*/
1616
import { errorResolver } from "../message/message_resolver";
17+
import { Maybe } from "../utils/type";
1718
import { ErrorHandler } from "./error_handler";
1819
import { DefaultErrorNotifier } from "./error_notifier";
1920

21+
export const INVALID_ERROR_HANDLER = 'Invalid error handler';
22+
2023
const errorNotifierSymbol = Symbol();
2124

2225
export type OpaqueErrorNotifier = {
2326
[errorNotifierSymbol]: unknown;
2427
};
2528

29+
const validateErrorHandler = (errorHandler: ErrorHandler) => {
30+
if (!errorHandler || typeof errorHandler !== 'object' || typeof errorHandler.handleError !== 'function') {
31+
throw new Error(INVALID_ERROR_HANDLER);
32+
}
33+
}
34+
2635
export const createErrorNotifier = (errorHandler: ErrorHandler): OpaqueErrorNotifier => {
36+
validateErrorHandler(errorHandler);
2737
return {
2838
[errorNotifierSymbol]: new DefaultErrorNotifier(errorHandler, errorResolver),
2939
}
3040
}
3141

32-
export const extractErrorNotifier = (errorNotifier: OpaqueErrorNotifier): DefaultErrorNotifier => {
33-
return errorNotifier[errorNotifierSymbol] as DefaultErrorNotifier;
42+
export const extractErrorNotifier = (errorNotifier: Maybe<OpaqueErrorNotifier>): Maybe<DefaultErrorNotifier> => {
43+
if (!errorNotifier || typeof errorNotifier !== 'object') {
44+
return undefined;
45+
}
46+
47+
return errorNotifier[errorNotifierSymbol] as Maybe<DefaultErrorNotifier>;
3448
}

lib/event_processor/event_dispatcher/event_dispatcher_factory.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ import { RequestHandler } from '../../utils/http_request_handler/http';
1818
import { DefaultEventDispatcher } from './default_dispatcher';
1919
import { EventDispatcher } from './event_dispatcher';
2020

21+
import { validateRequestHandler } from '../../utils/http_request_handler/request_handler_validator';
22+
2123
export const createEventDispatcher = (requestHander: RequestHandler): EventDispatcher => {
24+
validateRequestHandler(requestHander);
2225
return new DefaultEventDispatcher(requestHander);
2326
}

0 commit comments

Comments
 (0)