UNPKG

@croct/plug

Version:

A fully-featured devkit for building natively personalized applications.

280 lines (279 loc) 10.2 kB
var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var plug_exports = {}; __export(plug_exports, { GlobalPlug: () => GlobalPlug }); module.exports = __toCommonJS(plug_exports); var import_sdkFacade = require("@croct/sdk/facade/sdkFacade"); var import_error = require("@croct/sdk/error"); var import_validation = require("@croct/sdk/validation"); var import_token = require("@croct/sdk/token"); var import_sdk = require("@croct/sdk"); var import_content = require("@croct/content"); var import_constants = require('./constants.cjs'); var import_playground = require('./playground.cjs'); var import_preview = require('./preview.cjs'); const PLUGIN_NAMESPACE = "Plugin"; function detectAppId() { const script = window.document.querySelector(`script[src^='${import_constants.CDN_URL}']`); if (!(script instanceof HTMLScriptElement)) { return null; } return new URL(script.src).searchParams.get("appId"); } class GlobalPlug { constructor() { this.pluginFactories = { playground: import_playground.factory, preview: import_preview.factory }; this.plugins = {}; this.ready = new Promise((resolve) => { this.initialize = resolve; }); } extend(name, plugin) { if (this.pluginFactories[name] !== void 0) { throw new Error(`Another plugin is already registered with name "${name}".`); } this.pluginFactories[name] = plugin; } plug(configuration = {}) { if (this.instance !== void 0) { const logger2 = this.instance.getLogger(); logger2.info("Croct is already plugged in."); return; } const detectedAppId = detectAppId(); const configuredAppId = configuration.appId ?? null; if (detectedAppId !== null && configuredAppId !== null && detectedAppId !== configuredAppId) { throw new Error( 'The specified app ID and the auto-detected app ID are conflicting. There is no need to specify an app ID when using an application-specific tag. Please try again omitting the "appId" option. For help, see https://croct.help/sdk/javascript/conflicting-app-id' ); } const appId = detectedAppId ?? configuredAppId; if (appId === null) { throw new Error( 'The app ID must be specified when it cannot be auto-detected. Please try again specifying the "appId" option.For help, see https://croct.help/sdk/javascript/missing-app-id' ); } const { plugins, test, ...sdkConfiguration } = configuration; if (sdkConfiguration.defaultPreferredLocale === "") { delete sdkConfiguration.defaultPreferredLocale; } const sdk = import_sdkFacade.SdkFacade.init({ ...sdkConfiguration, appId, test: test ?? (typeof process === "object" && (process.env?.CROCT_TEST_MODE !== void 0 ? process.env.CROCT_TEST_MODE === "true" : process.env?.NODE_ENV === "test")) }); this.instance = sdk; const logger = this.instance.getLogger(); if (detectedAppId === configuredAppId) { logger.warn( 'It is strongly recommended omitting the "appId" option when using the application-specific tag as it is detected automatically.' ); } const pending = []; const defaultEnabledPlugins = Object.fromEntries( Object.keys(this.pluginFactories).map((name) => [name, true]) ); for (const [name, options] of Object.entries({ ...defaultEnabledPlugins, ...plugins })) { logger.debug(`Initializing plugin "${name}"...`); const factory = this.pluginFactories[name]; if (factory === void 0) { logger.error(`Plugin "${name}" is not registered.`); continue; } if (typeof options !== "boolean" && (options === null || typeof options !== "object")) { logger.error( `Invalid options for plugin "${name}", expected either boolean or object but got ${(0, import_validation.describe)(options)}` ); continue; } if (options === false) { logger.warn(`Plugin "${name}" is declared but not enabled`); continue; } const args = { options: options === true ? {} : options, sdk: { version: import_sdk.VERSION, appId, tracker: sdk.tracker, evaluator: sdk.evaluator, user: sdk.user, session: sdk.session, tab: sdk.context.getTab(), userTokenStore: { getToken: sdk.getToken.bind(sdk), setToken: sdk.setToken.bind(sdk) }, previewTokenStore: sdk.previewTokenStore, cidAssigner: sdk.cidAssigner, eventManager: sdk.eventManager, getLogger: (...namespace) => sdk.getLogger(PLUGIN_NAMESPACE, name, ...namespace), getTabStorage: (...namespace) => sdk.getTabStorage(PLUGIN_NAMESPACE, name, ...namespace), getBrowserStorage: (...namespace) => sdk.getBrowserStorage(PLUGIN_NAMESPACE, name, ...namespace) } }; let plugin; try { plugin = factory(args); } catch (error) { logger.error(`Failed to initialize plugin "${name}": ${(0, import_error.formatCause)(error)}`); continue; } logger.debug(`Plugin "${name}" initialized`); if (typeof plugin !== "object") { continue; } this.plugins[name] = plugin; const promise = plugin.enable(); if (!(promise instanceof Promise)) { logger.debug(`Plugin "${name}" enabled`); continue; } pending.push( promise.then(() => logger.debug(`Plugin "${name}" enabled`)).catch((error) => logger.error(`Failed to enable plugin "${name}": ${(0, import_error.formatCause)(error)}`)) ); } Promise.all(pending).then(() => { this.initialize(); logger.debug("Initialization complete"); }); } get initialized() { return this.instance !== void 0; } get plugged() { return this.ready.then(() => this); } get flushed() { return this.tracker.flushed.then(() => this); } get sdk() { if (this.instance === void 0) { throw new Error("Croct is not plugged in. For help, see https://croct.help/sdk/javascript/not-plugged-in"); } return this.instance; } get tracker() { return this.sdk.tracker; } get evaluator() { return this.sdk.evaluator; } get user() { return this.sdk.user; } get session() { return this.sdk.session; } isAnonymous() { return this.sdk.context.isAnonymous(); } getUserId() { return this.sdk.context.getUser(); } identify(userId) { if (typeof userId !== "string") { throw new Error( "The user ID must be a string. For help, see https://croct.help/sdk/javascript/invalid-user-id" ); } this.sdk.identify(userId); } anonymize() { this.sdk.anonymize(); } setToken(token) { this.sdk.setToken(import_token.Token.parse(token)); } unsetToken() { this.sdk.unsetToken(); } track(type, payload) { return this.sdk.tracker.track(type, payload); } evaluate(query, options = {}) { return this.sdk.evaluator.evaluate(query, options).catch((error) => { const logger = this.sdk.getLogger(); const reference = query.length > 20 ? `${query.slice(0, 20)}...` : query; logger.error(`Failed to evaluate query "${reference}": ${(0, import_error.formatCause)(error)}`); throw error; }); } test(expression, options = {}) { return this.evaluate(expression, options).then((result) => result === true); } fetch(slotId, { fallback, preferredLocale = "", ...options } = {}) { const [id, version = "latest"] = slotId.split("@"); const logger = this.sdk.getLogger(); const normalizedLocale = preferredLocale === "" ? void 0 : preferredLocale; return this.sdk.contentFetcher.fetch(id, { ...options, ...normalizedLocale !== void 0 ? { preferredLocale: normalizedLocale } : {}, ...version !== "latest" ? { version } : {} }).catch(async (error) => { logger.error(`Failed to fetch content for slot "${id}@${version}": ${(0, import_error.formatCause)(error)}`); const resolvedFallback = fallback === void 0 ? await (0, import_content.loadSlotContent)(slotId, normalizedLocale) ?? void 0 : fallback; if (resolvedFallback === void 0) { throw error; } return { content: resolvedFallback }; }); } async unplug() { if (this.instance === void 0) { return; } const { instance, plugins } = this; const logger = this.sdk.getLogger(); const pending = []; for (const [pluginName, controller] of Object.entries(plugins)) { if (typeof controller.disable !== "function") { continue; } logger.debug(`Disabling plugin "${pluginName}"...`); const promise = controller.disable(); if (!(promise instanceof Promise)) { logger.debug(`Plugin "${pluginName}" disabled`); continue; } pending.push( promise.then(() => logger.debug(`Plugin "${pluginName}" disabled`)).catch((error) => logger.error(`Failed to disable "${pluginName}": ${(0, import_error.formatCause)(error)}`)) ); } delete this.instance; this.plugins = {}; this.ready = new Promise((resolve) => { this.initialize = resolve; }); await Promise.all(pending); try { await instance.close(); } finally { logger.info("\u{1F50C} Croct has been unplugged."); } } } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { GlobalPlug });