rox-ssr
Version:
Rollout.io ROX JS SDK Client for SSR
244 lines • 11.3 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Rox = exports._RoxClient = exports.RoxOverridesPosition = exports.RoxFetcherStatus = exports.RoxNumber = exports.RoxString = exports.Flag = void 0;
/* eslint-disable @typescript-eslint/naming-convention */
const utils_1 = require("./utils");
var flag_1 = require("./flag");
Object.defineProperty(exports, "Flag", { enumerable: true, get: function () { return flag_1.Flag; } });
var string_1 = require("./string");
Object.defineProperty(exports, "RoxString", { enumerable: true, get: function () { return string_1.RoxString; } });
var number_1 = require("./number");
Object.defineProperty(exports, "RoxNumber", { enumerable: true, get: function () { return number_1.RoxNumber; } });
var RoxFetcherStatus;
(function (RoxFetcherStatus) {
RoxFetcherStatus["AppliedFromEmbedded"] = "APPLIED_FROM_EMBEDDED";
RoxFetcherStatus["AppliedFromCache"] = "APPLIED_FROM_CACHE";
RoxFetcherStatus["AppliedFromNetwork"] = "APPLIED_FROM_NETWORK";
RoxFetcherStatus["ErrorFetchFailed"] = "ERROR_FETCH_FAILED";
})(RoxFetcherStatus = exports.RoxFetcherStatus || (exports.RoxFetcherStatus = {}));
var RoxOverridesPosition;
(function (RoxOverridesPosition) {
RoxOverridesPosition["TopLeft"] = "top left";
RoxOverridesPosition["TopRight"] = "top right";
RoxOverridesPosition["BottomLeft"] = "bottom left";
RoxOverridesPosition["BottomRight"] = "bottom right";
})(RoxOverridesPosition = exports.RoxOverridesPosition || (exports.RoxOverridesPosition = {}));
class _RoxClient {
constructor() {
/**
* Dynamic API is an alternative to Rollout static API for defining flags on the
* different container objects and accessing them from that container object.
* https://support.rollout.io/docs/dynamic-api
*/
this.dynamicApi = utils_1.internalRox.dynamicApi;
/**
* Override: Should only be used for development purposes (QA - Feature dev - e2e)
*
* When you override an existing flag value using the Rox.overrides.setOverride method,
* the SDK will disregard existing configuration coming from the dashboard and will
* serialize the override on disk this value will be loaded and override the flag
* right after you call Rox.setup. To clear the override from the cache you need to
* call the Rox.overrides.clearOverride method.
*
* One can refer to the javascript-browser-api for this feature:
* https://support.rollout.io/docs/javascript-browser-api#section--rox-overrides-
*/
this.overrides = utils_1.internalRox.overrides;
// Helper boolean to check that setup() is not called more than once
this.alreadyInitialized = false;
this._configurationFetchedHandlers = [];
this._rolloutData = {};
this._confFetchedHandlers = [];
this._impressionHandlers = [];
}
register(namespace, roxContainer) {
try {
utils_1.internalRox.register(namespace, roxContainer);
}
catch (e) {
if (!e.message || !e.message.includes('A namespace must be unique')) {
// register failed for a reason other than namespace already existing
throw e;
}
}
// calling register multiple times is legitimate, but underlying SDK does not support it
// Each webpack entrypoint actually gets its own instance
// of Flags. However, the Rox singleton remains unique.
// In this case, we rewrite the Flags with the values stored within Rox.
if (this.flags && this.flags.length > 0) {
this.flags.forEach((f) => {
if (f === null || f === void 0 ? void 0 : f.name) {
roxContainer[f.name.replace(`${namespace}.`, '')] = f; // eslint-disable-line no-param-reassign
}
});
}
}
setup(apiKey, options) {
return __awaiter(this, void 0, void 0, function* () {
if (this._initialSetup) {
// this is a legitimate call only if parameters remain the same
if (this._initialSetup.apiKey !== apiKey ||
this._initialSetup.devModeSecret !== (options === null || options === void 0 ? void 0 : options.devModeSecret)) {
throw new Error('Rox cannot be initialized more than once');
}
// Add the configurationFetchedHandler to the list of handlers
// _configurationFetchedHandlers can only grow but this is not a memory leak; there can
// only be a finite number of entry points
if (options === null || options === void 0 ? void 0 : options.configurationFetchedHandler) {
this._confFetchedHandlers.push(options.configurationFetchedHandler);
}
if (options === null || options === void 0 ? void 0 : options.impressionHandler) {
this._impressionHandlers.push(options.impressionHandler);
}
return;
}
const _options = Object.assign({}, options);
if (options === null || options === void 0 ? void 0 : options.configurationFetchedHandler) {
this._confFetchedHandlers.push(options.configurationFetchedHandler);
}
_options.configurationFetchedHandler = this._confFetchedHandler;
_options.impressionHandler = this._impressionsHandler;
yield this._setup(apiKey, options);
this._initialSetup = {
apiKey,
devModeSecret: options === null || options === void 0 ? void 0 : options.devModeSecret,
};
});
}
_confFetchedHandler() {
const handlers = this._confFetchedHandlers;
return (fetcherResults) => {
handlers.forEach((cfh) => cfh(fetcherResults));
};
}
_impressionsHandler() {
const handlers = this._impressionHandlers;
return (reporting, context) => {
handlers.forEach((imh) => imh(reporting, context));
};
}
/**
* Initiate connection with Rox servers for the application identified by the application key.
* The registered containers will be synced and Rox entities will get the appropriate values.
*
* https://support.rollout.io/docs/nodejs-api#section-setup
*/
_setup(apiKey, options) {
return __awaiter(this, void 0, void 0, function* () {
if (this.alreadyInitialized) {
throw new Error('Rox cannot be initialized more than once');
}
const _options = Object.assign({}, options);
// @ts-ignore
if ((0, utils_1.isBrowser)() && window && window.rolloutData) {
// @ts-ignore
Object.assign(_options, options, {
embedded: window.rolloutData,
disableNetworkFetch: true,
});
if (_options.disablePushUpdateListener !== false) {
_options.disablePushUpdateListener = true;
}
}
if (!_options.platform) {
_options.platform = 'SSR';
}
if (options === null || options === void 0 ? void 0 : options.configurationFetchedHandler) {
this._configurationFetchedHandlers.push(options.configurationFetchedHandler);
}
_options.configurationFetchedHandler = this._configurationFetchedHandler();
yield utils_1.internalRox.setup(apiKey, _options);
this.alreadyInitialized = true;
});
}
/**
* Set Global Context.
* You can think of Global Context as a default context
*
* https://support.rollout.io/docs/nodejs-api#section-setcontext
*/
setContext(globalContext) {
utils_1.internalRox.setContext(globalContext);
}
setCustomNumberProperty(name, value) {
utils_1.internalRox.setCustomNumberProperty(name, value);
}
setCustomStringProperty(name, value) {
utils_1.internalRox.setCustomStringProperty(name, value);
}
setCustomBooleanProperty(name, value) {
utils_1.internalRox.setCustomBooleanProperty(name, value);
}
setCustomDateProperty(name, value) {
utils_1.internalRox.setCustomDateProperty(name, value);
}
setCustomSemverProperty(name, value) {
utils_1.internalRox.setCustomSemverProperty(name, value);
}
/**
* Pulls the latest configuration and flag values down from the Rollout servers
*
* https://support.rollout.io/docs/nodejs-api#section-fetch
*/
fetch() {
utils_1.internalRox.fetch();
}
/**
* Opens the flag override view, providing a debug UI for the application's set of
* feature flags.
* This is only available in a browser context
* https://support.rollout.io/docs/javascript-browser-api#section-showoverrides
*/
showOverrides(position) {
if ((0, utils_1.isBrowser)()) {
utils_1.internalRox.showOverrides(position);
return;
}
throw new Error('showOverrides is only available in a browser context');
}
_configurationFetchedHandler() {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const roxClient = this;
return (fetcherResults) => {
if (fetcherResults.hasChanges) {
roxClient._rolloutData = fetcherResults.clientData;
}
roxClient._configurationFetchedHandlers.forEach((cfh) => cfh(fetcherResults));
};
}
/**
* The Rollout configuration data. This is intended to be called on the server side
* and passed to the browser to hydrate Rollout's configuration data on browser side.
* @example React example
* import {Rox} from 'rox-ssr';
* // ...
* // Your component render() method
* render() {
* return (
* <html>
* <head>
* <script type='text/javascript'
* dangerouslySetInnerHTML={{ __html: `window.rolloutData = ${Rox.rolloutData};` }} />
* <head>
* </html>)
* }
*/
get rolloutData() {
return JSON.stringify(this._rolloutData);
}
get flags() {
return utils_1.internalRox.flags;
}
}
exports._RoxClient = _RoxClient;
exports.Rox = new _RoxClient();
//# sourceMappingURL=index.js.map