@croct/plug
Version:
A fully-featured devkit for building natively personalized applications.
280 lines (279 loc) • 10.2 kB
JavaScript
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
});