Skip to content

[FSSDK-11492] make public promise rejection messages non tree-shakable #1041

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
May 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions lib/event_processor/batch_event_processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ import { isSuccessStatusCode } from "../utils/http_request_handler/http_util";
import { EventEmitter } from "../utils/event_emitter/event_emitter";
import { IdGenerator } from "../utils/id_generator";
import { areEventContextsEqual } from "./event_builder/user_event";
import { EVENT_PROCESSOR_STOPPED, FAILED_TO_DISPATCH_EVENTS, FAILED_TO_DISPATCH_EVENTS_WITH_ARG } from "error_message";
import { FAILED_TO_DISPATCH_EVENTS, SERVICE_NOT_RUNNING } from "error_message";
import { OptimizelyError } from "../error/optimizly_error";
import { sprintf } from "../utils/fns";
import { SERVICE_STOPPED_BEFORE_RUNNING } from "../service";

export const DEFAULT_MIN_BACKOFF = 1000;
export const DEFAULT_MAX_BACKOFF = 32000;
Expand Down Expand Up @@ -174,7 +176,7 @@ export class BatchEventProcessor extends BaseService implements EventProcessor {
const dispatcher = closing && this.closingEventDispatcher ? this.closingEventDispatcher : this.eventDispatcher;
return dispatcher.dispatchEvent(request).then((res) => {
if (res.statusCode && !isSuccessStatusCode(res.statusCode)) {
return Promise.reject(new OptimizelyError(FAILED_TO_DISPATCH_EVENTS_WITH_ARG, res.statusCode));
return Promise.reject(new OptimizelyError(FAILED_TO_DISPATCH_EVENTS, res.statusCode));
}
return Promise.resolve(res);
});
Expand Down Expand Up @@ -209,7 +211,7 @@ export class BatchEventProcessor extends BaseService implements EventProcessor {
}).catch((err) => {
// if the dispatch fails, the events will still be
// in the store for future processing
this.logger?.error(FAILED_TO_DISPATCH_EVENTS, err);
this.logger?.error(err);
}).finally(() => {
this.runningTask.delete(taskId);
ids.forEach((id) => this.dispatchingEventIds.delete(id));
Expand All @@ -228,7 +230,7 @@ export class BatchEventProcessor extends BaseService implements EventProcessor {

async process(event: ProcessableEvent): Promise<void> {
if (!this.isRunning()) {
return Promise.reject('Event processor is not running');
return Promise.reject(new OptimizelyError(SERVICE_NOT_RUNNING, 'BatchEventProcessor'));
}

const eventWithId = {
Expand Down Expand Up @@ -285,7 +287,9 @@ export class BatchEventProcessor extends BaseService implements EventProcessor {
}

if (this.isNew()) {
this.startPromise.reject(new OptimizelyError(EVENT_PROCESSOR_STOPPED));
this.startPromise.reject(new Error(
sprintf(SERVICE_STOPPED_BEFORE_RUNNING, 'BatchEventProcessor')
));
}

this.state = ServiceState.Stopping;
Expand Down
8 changes: 5 additions & 3 deletions lib/event_processor/forwarding_event_processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ import { buildLogEvent } from './event_builder/log_event';
import { BaseService, ServiceState } from '../service';
import { EventEmitter } from '../utils/event_emitter/event_emitter';
import { Consumer, Fn } from '../utils/type';
import { SERVICE_STOPPED_BEFORE_RUNNING } from 'error_message';
import { OptimizelyError } from '../error/optimizly_error';
import { SERVICE_STOPPED_BEFORE_RUNNING } from '../service';
import { sprintf } from '../utils/fns';

class ForwardingEventProcessor extends BaseService implements EventProcessor {
private dispatcher: EventDispatcher;
Expand Down Expand Up @@ -57,7 +57,9 @@ class ForwardingEventProcessor extends BaseService implements EventProcessor {
}

if (this.isNew()) {
this.startPromise.reject(new OptimizelyError(SERVICE_STOPPED_BEFORE_RUNNING));
this.startPromise.reject(new Error(
sprintf(SERVICE_STOPPED_BEFORE_RUNNING, 'ForwardingEventProcessor'))
);
}

this.state = ServiceState.Terminated;
Expand Down
14 changes: 2 additions & 12 deletions lib/message/error_message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ export const NO_EVENT_PROCESSOR = 'No event processor is provided';
export const NO_VARIATION_FOR_EXPERIMENT_KEY = 'No variation key %s defined in datafile for experiment %s.';
export const ODP_CONFIG_NOT_AVAILABLE = 'ODP config is not available.';
export const ODP_EVENT_FAILED = 'ODP event send failed.';
export const ODP_EVENT_MANAGER_IS_NOT_RUNNING = 'ODP event manager is not running.';
export const ODP_EVENTS_SHOULD_HAVE_ATLEAST_ONE_KEY_VALUE = 'ODP events should have at least one key-value pair in identifiers.';
export const ODP_EVENT_FAILED_ODP_MANAGER_MISSING = 'ODP Event failed to send. (ODP Manager not available).';
export const ODP_NOT_INTEGRATED = 'ODP is not integrated';
Expand Down Expand Up @@ -89,25 +88,16 @@ export const REQUEST_TIMEOUT = 'Request timeout';
export const REQUEST_ERROR = 'Request error';
export const NO_STATUS_CODE_IN_RESPONSE = 'No status code in response';
export const UNSUPPORTED_PROTOCOL = 'Unsupported protocol: %s';
export const ONREADY_TIMEOUT = 'onReady timeout expired after %s ms';
export const INSTANCE_CLOSED = 'Instance closed';
export const DATAFILE_MANAGER_STOPPED = 'Datafile manager stopped before it could be started';
export const FAILED_TO_FETCH_DATAFILE = 'Failed to fetch datafile';
export const NO_SDKKEY_OR_DATAFILE = 'At least one of sdkKey or datafile must be provided';
export const RETRY_CANCELLED = 'Retry cancelled';
export const SERVICE_STOPPED_BEFORE_RUNNING = 'Service stopped before running';
export const ONLY_POST_REQUESTS_ARE_SUPPORTED = 'Only POST requests are supported';
export const SEND_BEACON_FAILED = 'sendBeacon failed';
export const FAILED_TO_DISPATCH_EVENTS = 'Failed to dispatch events'
export const FAILED_TO_DISPATCH_EVENTS_WITH_ARG = 'Failed to dispatch events: %s';
export const EVENT_PROCESSOR_STOPPED = 'Event processor stopped before it could be started';
export const ODP_MANAGER_STOPPED_BEFORE_RUNNING = 'odp manager stopped before running';
export const FAILED_TO_DISPATCH_EVENTS = 'Failed to dispatch events, status: %s';
export const ODP_EVENT_MANAGER_STOPPED = "ODP event manager stopped before it could start";
export const DATAFILE_MANAGER_FAILED_TO_START = 'Datafile manager failed to start';
export const UNABLE_TO_ATTACH_UNLOAD = 'unable to bind optimizely.close() to page unload event: "%s"';
export const UNABLE_TO_PARSE_AND_SKIPPED_HEADER = 'Unable to parse & skipped header item';
export const CMAB_FETCH_FAILED = 'CMAB decision fetch failed with status: %s';
export const INVALID_CMAB_FETCH_RESPONSE = 'Invalid CMAB fetch response';
export const PROMISE_NOT_ALLOWED = "Promise value is not allowed in sync operation";
export const SERVICE_NOT_RUNNING = "%s not running";

export const messages: string[] = [];
18 changes: 11 additions & 7 deletions lib/odp/event_manager/odp_event_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,16 @@ import {
EVENT_ACTION_INVALID,
EVENT_DATA_INVALID,
FAILED_TO_SEND_ODP_EVENTS,
ODP_EVENT_MANAGER_IS_NOT_RUNNING,
ODP_EVENTS_SHOULD_HAVE_ATLEAST_ONE_KEY_VALUE,
ODP_NOT_INTEGRATED,
FAILED_TO_DISPATCH_EVENTS_WITH_ARG,
ODP_EVENT_MANAGER_STOPPED
FAILED_TO_DISPATCH_EVENTS,
ODP_EVENT_MANAGER_STOPPED,
SERVICE_NOT_RUNNING
} from 'error_message';
import { OptimizelyError } from '../../error/optimizly_error';
import { LoggerFacade } from '../../logging/logger';
import { SERVICE_STOPPED_BEFORE_RUNNING } from '../../service';
import { sprintf } from '../../utils/fns';

export interface OdpEventManager extends Service {
updateConfig(odpIntegrationConfig: OdpIntegrationConfig): void;
Expand Down Expand Up @@ -86,7 +88,7 @@ export class DefaultOdpEventManager extends BaseService implements OdpEventManag
private async executeDispatch(odpConfig: OdpConfig, batch: OdpEvent[]): Promise<unknown> {
const res = await this.apiManager.sendEvents(odpConfig, batch);
if (res.statusCode && !isSuccessStatusCode(res.statusCode)) {
return Promise.reject(new OptimizelyError(FAILED_TO_DISPATCH_EVENTS_WITH_ARG, res.statusCode));
return Promise.reject(new OptimizelyError(FAILED_TO_DISPATCH_EVENTS, res.statusCode));
}
return await Promise.resolve(res);
}
Expand All @@ -113,7 +115,7 @@ export class DefaultOdpEventManager extends BaseService implements OdpEventManag
}

start(): void {
if (!this.isNew) {
if (!this.isNew()) {
return;
}

Expand Down Expand Up @@ -164,7 +166,9 @@ export class DefaultOdpEventManager extends BaseService implements OdpEventManag
}

if (this.isNew()) {
this.startPromise.reject(new OptimizelyError(ODP_EVENT_MANAGER_STOPPED));
this.startPromise.reject(new Error(
sprintf(SERVICE_STOPPED_BEFORE_RUNNING, 'OdpEventManager')
));
}

this.flush();
Expand All @@ -174,7 +178,7 @@ export class DefaultOdpEventManager extends BaseService implements OdpEventManag

sendEvent(event: OdpEvent): void {
if (!this.isRunning()) {
this.logger?.error(ODP_EVENT_MANAGER_IS_NOT_RUNNING);
this.logger?.error(SERVICE_NOT_RUNNING, 'OdpEventManager');
return;
}

Expand Down
8 changes: 5 additions & 3 deletions lib/odp/odp_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ import { CLIENT_VERSION, JAVASCRIPT_CLIENT_ENGINE } from '../utils/enums';
import { ODP_DEFAULT_EVENT_TYPE, ODP_EVENT_ACTION, ODP_USER_KEY } from './constant';
import { isVuid } from '../vuid/vuid';
import { Maybe } from '../utils/type';
import { ODP_MANAGER_STOPPED_BEFORE_RUNNING } from 'error_message';
import { OptimizelyError } from '../error/optimizly_error';
import { sprintf } from '../utils/fns';
import { SERVICE_STOPPED_BEFORE_RUNNING } from '../service';

export interface OdpManager extends Service {
updateConfig(odpIntegrationConfig: OdpIntegrationConfig): boolean;
Expand Down Expand Up @@ -151,7 +151,9 @@ export class DefaultOdpManager extends BaseService implements OdpManager {
}

if (!this.isRunning()) {
this.startPromise.reject(new OptimizelyError(ODP_MANAGER_STOPPED_BEFORE_RUNNING));
this.startPromise.reject(new Error(
sprintf(SERVICE_STOPPED_BEFORE_RUNNING, 'OdpManager')
));
}

this.state = ServiceState.Stopping;
Expand Down
8 changes: 3 additions & 5 deletions lib/optimizely/index.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ import {
NOT_TRACKING_USER,
EVENT_KEY_NOT_FOUND,
INVALID_EXPERIMENT_KEY,
ONREADY_TIMEOUT,
SERVICE_STOPPED_BEFORE_RUNNING
} from 'error_message';

import { ONREADY_TIMEOUT, INSTANCE_CLOSED } from './';
import {
AUDIENCE_EVALUATION_RESULT_COMBINED,
USER_NOT_IN_EXPERIMENT,
Expand Down Expand Up @@ -9455,8 +9455,7 @@ describe('lib/optimizely', function() {
return readyPromise.then(() => {
return Promise.reject(new Error('PROMISE_SHOULD_NOT_HAVE_RESOLVED'));
}, (err) => {
assert.equal(err.baseMessage, ONREADY_TIMEOUT);
assert.deepEqual(err.params, [ 500 ]);
assert.equal(err.message, sprintf(ONREADY_TIMEOUT, 500));
});
});

Expand All @@ -9479,8 +9478,7 @@ describe('lib/optimizely', function() {
return readyPromise.then(() => {
return Promise.reject(new Error('PROMISE_SHOULD_NOT_HAVE_RESOLVED'));
}, (err) => {
assert.equal(err.baseMessage, ONREADY_TIMEOUT);
assert.deepEqual(err.params, [ 30000 ]);
assert.equal(err.message, sprintf(ONREADY_TIMEOUT, 30000));
});
});

Expand Down
20 changes: 13 additions & 7 deletions lib/optimizely/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ import {
NODE_CLIENT_ENGINE,
CLIENT_VERSION,
} from '../utils/enums';
import { Fn, Maybe, OpType, OpValue } from '../utils/type';
import { Fn, Maybe, OpType } from '../utils/type';
import { resolvablePromise } from '../utils/promise/resolvablePromise';

import { NOTIFICATION_TYPES, DecisionNotificationType, DECISION_NOTIFICATION_TYPES } from '../notification_center/type';
Expand All @@ -75,9 +75,6 @@ import {
EVENT_KEY_NOT_FOUND,
NOT_TRACKING_USER,
VARIABLE_REQUESTED_WITH_WRONG_TYPE,
ONREADY_TIMEOUT,
INSTANCE_CLOSED,
SERVICE_STOPPED_BEFORE_RUNNING
} from 'error_message';

import {
Expand All @@ -98,6 +95,8 @@ import {
VARIABLE_NOT_USED_RETURN_DEFAULT_VARIABLE_VALUE,
} from 'log_message';

import { SERVICE_STOPPED_BEFORE_RUNNING } from '../service';

import { ErrorNotifier } from '../error/error_notifier';
import { ErrorReporter } from '../error/error_reporter';
import { OptimizelyError } from '../error/optimizly_error';
Expand All @@ -113,6 +112,9 @@ type StringInputs = Partial<Record<InputKey, unknown>>;

type DecisionReasons = (string | number)[];

export const INSTANCE_CLOSED = 'Instance closed';
export const ONREADY_TIMEOUT = 'onReady timeout expired after %s ms';

/**
* options required to create optimizely object
*/
Expand Down Expand Up @@ -1257,7 +1259,9 @@ export default class Optimizely extends BaseService implements Client {
}

if (!this.isRunning()) {
this.startPromise.reject(new OptimizelyError(SERVICE_STOPPED_BEFORE_RUNNING));
this.startPromise.reject(new Error(
sprintf(SERVICE_STOPPED_BEFORE_RUNNING, 'Client')
));
}

this.state = ServiceState.Stopping;
Expand Down Expand Up @@ -1322,14 +1326,16 @@ export default class Optimizely extends BaseService implements Client {

const onReadyTimeout = () => {
this.cleanupTasks.delete(cleanupTaskId);
timeoutPromise.reject(new OptimizelyError(ONREADY_TIMEOUT, timeoutValue));
timeoutPromise.reject(new Error(
sprintf(ONREADY_TIMEOUT, timeoutValue)
));
};

const readyTimeout = setTimeout(onReadyTimeout, timeoutValue);

this.cleanupTasks.set(cleanupTaskId, () => {
clearTimeout(readyTimeout);
timeoutPromise.reject(new OptimizelyError(INSTANCE_CLOSED));
timeoutPromise.reject(new Error(INSTANCE_CLOSED));
});

return Promise.race([this.onRunning().then(() => {
Expand Down
12 changes: 8 additions & 4 deletions lib/project_config/polling_datafile_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,8 @@ import { Repeater } from '../utils/repeater/repeater';
import { Consumer, Fn } from '../utils/type';
import { isSuccessStatusCode } from '../utils/http_request_handler/http_util';
import {
DATAFILE_MANAGER_STOPPED,
DATAFILE_FETCH_REQUEST_FAILED,
ERROR_FETCHING_DATAFILE,
FAILED_TO_FETCH_DATAFILE,
} from 'error_message';
import {
ADDING_AUTHORIZATION_HEADER_WITH_BEARER_TOKEN,
Expand All @@ -40,6 +38,10 @@ import { LoggerFacade } from '../logging/logger';

export const LOGGER_NAME = 'PollingDatafileManager';

import { SERVICE_STOPPED_BEFORE_RUNNING } from '../service';

export const FAILED_TO_FETCH_DATAFILE = 'Failed to fetch datafile';

export class PollingDatafileManager extends BaseService implements DatafileManager {
private requestHandler: RequestHandler;
private currentDatafile?: string;
Expand Down Expand Up @@ -123,7 +125,9 @@ export class PollingDatafileManager extends BaseService implements DatafileManag
}

if (this.isNew() || this.isStarting()) {
this.startPromise.reject(new OptimizelyError(DATAFILE_MANAGER_STOPPED));
this.startPromise.reject(new Error(
sprintf(SERVICE_STOPPED_BEFORE_RUNNING, 'PollingDatafileManager')
));
}

this.state = ServiceState.Terminated;
Expand All @@ -136,7 +140,7 @@ export class PollingDatafileManager extends BaseService implements DatafileManag
private handleInitFailure(): void {
this.state = ServiceState.Failed;
this.repeater.stop();
const error = new OptimizelyError(FAILED_TO_FETCH_DATAFILE);
const error = new Error(FAILED_TO_FETCH_DATAFILE);
this.startPromise.reject(error);
this.stopPromise.reject(error);
}
Expand Down
8 changes: 4 additions & 4 deletions lib/project_config/project_config_manager.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,10 +209,10 @@ describe('ProjectConfigManagerImpl', () => {

describe('when datafile is invalid', () => {
it('should reject onRunning() with the same error if datafileManager.onRunning() rejects', async () => {
const datafileManager = getMockDatafileManager({ onRunning: Promise.reject('test error') });
const datafileManager = getMockDatafileManager({ onRunning: Promise.reject(new Error('test error')) });
const manager = new ProjectConfigManagerImpl({ datafile: {}, datafileManager });
manager.start();
await expect(manager.onRunning()).rejects.toBe('test error');
await expect(manager.onRunning()).rejects.toThrow('DatafileManager failed to start, reason: test error');
});

it('should resolve onRunning() if datafileManager.onUpdate() is fired and should update config', async () => {
Expand Down Expand Up @@ -258,10 +258,10 @@ describe('ProjectConfigManagerImpl', () => {

describe('when datafile is not provided', () => {
it('should reject onRunning() if datafileManager.onRunning() rejects', async () => {
const datafileManager = getMockDatafileManager({ onRunning: Promise.reject('test error') });
const datafileManager = getMockDatafileManager({ onRunning: Promise.reject(new Error('test error')) });
const manager = new ProjectConfigManagerImpl({ datafileManager });
manager.start();
await expect(manager.onRunning()).rejects.toBe('test error');
await expect(manager.onRunning()).rejects.toThrow('DatafileManager failed to start, reason: test error');
});

it('should reject onRunning() and onTerminated if datafileManager emits an invalid datafile in the first onUpdate', async () => {
Expand Down
Loading
Loading