@segment/analytics-next
Version:
Analytics Next (aka Analytics 2.0) is the latest version of Segment’s JavaScript SDK - enabling you to send your data to any tool without having to learn, test, or use a new API every time.
323 lines • 16 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.remoteLoader = exports.ActionDestination = void 0;
var tslib_1 = require("tslib");
var load_script_1 = require("../../lib/load-script");
var parse_cdn_1 = require("../../lib/parse-cdn");
var middleware_1 = require("../middleware");
var context_1 = require("../../core/context");
var metric_helpers_1 = require("../../core/stats/metric-helpers");
var analytics_generic_utils_1 = require("@segment/analytics-generic-utils");
var ActionDestination = /** @class */ (function () {
function ActionDestination(name, action) {
this.version = '1.0.0';
this.alternativeNames = [];
this.loadPromise = (0, analytics_generic_utils_1.createDeferred)();
this.middleware = [];
this.alias = this._createMethod('alias');
this.group = this._createMethod('group');
this.identify = this._createMethod('identify');
this.page = this._createMethod('page');
this.screen = this._createMethod('screen');
this.track = this._createMethod('track');
this.action = action;
this.name = name;
this.type = action.type;
this.alternativeNames.push(action.name);
}
ActionDestination.prototype.addMiddleware = function () {
var _a;
var fn = [];
for (var _i = 0; _i < arguments.length; _i++) {
fn[_i] = arguments[_i];
}
/** Make sure we only apply destination filters to actions of the "destination" type to avoid causing issues for hybrid destinations */
if (this.type === 'destination') {
(_a = this.middleware).push.apply(_a, fn);
}
};
ActionDestination.prototype.transform = function (ctx) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var modifiedEvent;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, (0, middleware_1.applyDestinationMiddleware)(this.name, ctx.event, this.middleware)];
case 1:
modifiedEvent = _a.sent();
if (modifiedEvent === null) {
ctx.cancel(new context_1.ContextCancelation({
retry: false,
reason: 'dropped by destination middleware',
}));
}
return [2 /*return*/, new context_1.Context(modifiedEvent)];
}
});
});
};
ActionDestination.prototype._createMethod = function (methodName) {
var _this = this;
return function (ctx) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
var transformedContext, error_1;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!this.action[methodName])
return [2 /*return*/, ctx];
transformedContext = ctx;
if (!(this.type === 'destination')) return [3 /*break*/, 2];
return [4 /*yield*/, this.transform(ctx)];
case 1:
transformedContext = _a.sent();
_a.label = 2;
case 2:
_a.trys.push([2, 5, , 6]);
return [4 /*yield*/, this.ready()];
case 3:
if (!(_a.sent())) {
throw new Error('Something prevented the destination from getting ready');
}
(0, metric_helpers_1.recordIntegrationMetric)(ctx, {
integrationName: this.action.name,
methodName: methodName,
type: 'action',
});
return [4 /*yield*/, this.action[methodName](transformedContext)];
case 4:
_a.sent();
return [3 /*break*/, 6];
case 5:
error_1 = _a.sent();
(0, metric_helpers_1.recordIntegrationMetric)(ctx, {
integrationName: this.action.name,
methodName: methodName,
type: 'action',
didError: true,
});
throw error_1;
case 6: return [2 /*return*/, ctx];
}
});
}); };
};
/* --- PASSTHROUGH METHODS --- */
ActionDestination.prototype.isLoaded = function () {
return this.action.isLoaded();
};
ActionDestination.prototype.ready = function () {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var _a;
return tslib_1.__generator(this, function (_b) {
switch (_b.label) {
case 0:
_b.trys.push([0, 2, , 3]);
return [4 /*yield*/, this.loadPromise.promise];
case 1:
_b.sent();
return [2 /*return*/, true];
case 2:
_a = _b.sent();
return [2 /*return*/, false];
case 3: return [2 /*return*/];
}
});
});
};
ActionDestination.prototype.load = function (ctx, analytics) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var loadP, _a, _b, error_2;
return tslib_1.__generator(this, function (_c) {
switch (_c.label) {
case 0:
if (this.loadPromise.isSettled()) {
return [2 /*return*/, this.loadPromise.promise];
}
_c.label = 1;
case 1:
_c.trys.push([1, 3, , 4]);
(0, metric_helpers_1.recordIntegrationMetric)(ctx, {
integrationName: this.action.name,
methodName: 'load',
type: 'action',
});
loadP = this.action.load(ctx, analytics);
_b = (_a = this.loadPromise).resolve;
return [4 /*yield*/, loadP];
case 2:
_b.apply(_a, [_c.sent()]);
return [2 /*return*/, loadP];
case 3:
error_2 = _c.sent();
(0, metric_helpers_1.recordIntegrationMetric)(ctx, {
integrationName: this.action.name,
methodName: 'load',
type: 'action',
didError: true,
});
this.loadPromise.reject(error_2);
throw error_2;
case 4: return [2 /*return*/];
}
});
});
};
ActionDestination.prototype.unload = function (ctx, analytics) {
var _a, _b;
return (_b = (_a = this.action).unload) === null || _b === void 0 ? void 0 : _b.call(_a, ctx, analytics);
};
return ActionDestination;
}());
exports.ActionDestination = ActionDestination;
function validate(pluginLike) {
if (!Array.isArray(pluginLike)) {
throw new Error('Not a valid list of plugins');
}
var required = ['load', 'isLoaded', 'name', 'version', 'type'];
pluginLike.forEach(function (plugin) {
required.forEach(function (method) {
var _a;
if (plugin[method] === undefined) {
throw new Error("Plugin: ".concat((_a = plugin.name) !== null && _a !== void 0 ? _a : 'unknown', " missing required function ").concat(method));
}
});
});
return true;
}
function isPluginDisabled(userIntegrations, remotePlugin) {
var creationNameEnabled = userIntegrations[remotePlugin.creationName];
var currentNameEnabled = userIntegrations[remotePlugin.name];
// Check that the plugin isn't explicitly enabled when All: false
if (userIntegrations.All === false &&
!creationNameEnabled &&
!currentNameEnabled) {
return true;
}
// Check that the plugin isn't explicitly disabled
if (creationNameEnabled === false || currentNameEnabled === false) {
return true;
}
return false;
}
function loadPluginFactory(remotePlugin, obfuscate) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var defaultCdn, cdn, urlSplit, name_1, obfuscatedURL, error_3, err_1;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
_a.trys.push([0, 9, , 10]);
defaultCdn = new RegExp('https://cdn.segment.(com|build)');
cdn = (0, parse_cdn_1.getCDN)();
if (!obfuscate) return [3 /*break*/, 6];
urlSplit = remotePlugin.url.split('/');
name_1 = urlSplit[urlSplit.length - 2];
obfuscatedURL = remotePlugin.url.replace(name_1, btoa(name_1).replace(/=/g, ''));
_a.label = 1;
case 1:
_a.trys.push([1, 3, , 5]);
return [4 /*yield*/, (0, load_script_1.loadScript)(obfuscatedURL.replace(defaultCdn, cdn))];
case 2:
_a.sent();
return [3 /*break*/, 5];
case 3:
error_3 = _a.sent();
// Due to syncing concerns it is possible that the obfuscated action destination (or requested version) might not exist.
// We should use the unobfuscated version as a fallback.
return [4 /*yield*/, (0, load_script_1.loadScript)(remotePlugin.url.replace(defaultCdn, cdn))];
case 4:
// Due to syncing concerns it is possible that the obfuscated action destination (or requested version) might not exist.
// We should use the unobfuscated version as a fallback.
_a.sent();
return [3 /*break*/, 5];
case 5: return [3 /*break*/, 8];
case 6: return [4 /*yield*/, (0, load_script_1.loadScript)(remotePlugin.url.replace(defaultCdn, cdn))];
case 7:
_a.sent();
_a.label = 8;
case 8:
// @ts-expect-error
if (typeof window[remotePlugin.libraryName] === 'function') {
// @ts-expect-error
return [2 /*return*/, window[remotePlugin.libraryName]];
}
return [3 /*break*/, 10];
case 9:
err_1 = _a.sent();
console.error('Failed to create PluginFactory', remotePlugin);
throw err_1;
case 10: return [2 /*return*/];
}
});
});
}
function remoteLoader(settings, integrations, mergedIntegrations, options, routingMiddleware, pluginSources) {
var _a, _b, _c;
return tslib_1.__awaiter(this, void 0, void 0, function () {
var allPlugins, routingRules, pluginPromises;
var _this = this;
return tslib_1.__generator(this, function (_d) {
switch (_d.label) {
case 0:
allPlugins = [];
routingRules = (_b = (_a = settings.middlewareSettings) === null || _a === void 0 ? void 0 : _a.routingRules) !== null && _b !== void 0 ? _b : [];
pluginPromises = ((_c = settings.remotePlugins) !== null && _c !== void 0 ? _c : []).map(function (remotePlugin) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
var pluginFactory, _a, intg, plugin, plugins, routing_1, error_4;
return tslib_1.__generator(this, function (_b) {
switch (_b.label) {
case 0:
if (isPluginDisabled(integrations, remotePlugin))
return [2 /*return*/];
_b.label = 1;
case 1:
_b.trys.push([1, 6, , 7]);
_a = (pluginSources === null || pluginSources === void 0 ? void 0 : pluginSources.find(function (_a) {
var pluginName = _a.pluginName;
return pluginName === remotePlugin.name;
}));
if (_a) return [3 /*break*/, 3];
return [4 /*yield*/, loadPluginFactory(remotePlugin, options === null || options === void 0 ? void 0 : options.obfuscate)];
case 2:
_a = (_b.sent());
_b.label = 3;
case 3:
pluginFactory = _a;
if (!pluginFactory) return [3 /*break*/, 5];
intg = mergedIntegrations[remotePlugin.name];
return [4 /*yield*/, pluginFactory(tslib_1.__assign(tslib_1.__assign({}, remotePlugin.settings), intg))];
case 4:
plugin = _b.sent();
plugins = Array.isArray(plugin) ? plugin : [plugin];
validate(plugins);
routing_1 = routingRules.filter(function (rule) { return rule.destinationName === remotePlugin.creationName; });
plugins.forEach(function (plugin) {
var wrapper = new ActionDestination(remotePlugin.creationName, plugin);
if (routing_1.length && routingMiddleware) {
wrapper.addMiddleware(routingMiddleware);
}
allPlugins.push(wrapper);
});
_b.label = 5;
case 5: return [3 /*break*/, 7];
case 6:
error_4 = _b.sent();
console.warn('Failed to load Remote Plugin', error_4);
(0, metric_helpers_1.recordIntegrationMetric)(context_1.Context.system(), {
integrationName: remotePlugin.name,
methodName: 'load',
type: 'action',
didError: true,
});
return [3 /*break*/, 7];
case 7: return [2 /*return*/];
}
});
}); });
return [4 /*yield*/, Promise.all(pluginPromises)];
case 1:
_d.sent();
return [2 /*return*/, allPlugins.filter(Boolean)];
}
});
});
}
exports.remoteLoader = remoteLoader;
//# sourceMappingURL=index.js.map