supertokens-node
Version:
NodeJS driver for SuperTokens core
206 lines (205 loc) • 8.47 kB
JavaScript
;
var __rest =
(this && this.__rest) ||
function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]];
}
return t;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getPublicPlugin = getPublicPlugin;
exports.getPluginDependencies = getPluginDependencies;
exports.applyPlugins = applyPlugins;
exports.loadPlugins = loadPlugins;
const version_1 = require("./version");
const postSuperTokensInitCallbacks_1 = require("./postSuperTokensInitCallbacks");
const utils_1 = require("./utils");
const versionChecker_1 = require("./versionChecker");
function getPublicPlugin(plugin) {
return {
id: plugin.id,
initialized: plugin.init ? false : true, // since the init method is optional, we default to true
version: plugin.version,
exports: plugin.exports,
compatibleSDKVersions: plugin.compatibleSDKVersions,
};
}
/**
* Resolve plugin dependencies recursively, ensuring that each plugin is only added once.
* @param plugin Plugin to process
* @param publicConfig Supertokens Public config
* @param pluginsAbove Plugins processed before this plugin
* @param sdkVersion Current SDK version
* @returns Resolved list of dependencies for the plugin
*/
function getPluginDependencies({ plugin, config, pluginsAbove, sdkVersion, normalisedAppInfo }) {
const publicConfig = (0, utils_1.getPublicConfig)(
Object.assign(Object.assign({}, config), { appInfo: normalisedAppInfo })
);
function recurseDependencies(plugin, dependencies, visited) {
if (dependencies === undefined) {
dependencies = [];
}
if (visited === undefined) {
visited = new Set();
}
if (visited.has(plugin.id)) {
return dependencies;
}
visited.add(plugin.id);
if (plugin.dependencies) {
// Get the current plugin's dependencies
const result = plugin.dependencies(publicConfig, pluginsAbove.map(getPublicPlugin), sdkVersion);
if (result.status === "ERROR") {
throw new Error(result.message);
}
if (result.pluginsToAdd) {
// Recurse through each dependency to resolve their dependencies as well
for (const dep of result.pluginsToAdd) {
recurseDependencies(dep, dependencies, visited);
}
}
}
dependencies.push(plugin);
return dependencies;
}
return recurseDependencies(plugin);
}
function applyPlugins(recipeId, config, plugins) {
var _a, _b;
config = config !== null && config !== void 0 ? config : {};
let functionLayers = [];
let apiLayers = [];
for (const plugin of plugins) {
const overrides = plugin[recipeId];
if (overrides) {
config = overrides.config ? overrides.config(config) : config;
if (overrides.functions !== undefined) {
functionLayers.push(overrides.functions);
}
if (overrides.apis !== undefined) {
apiLayers.push(overrides.apis);
}
}
}
functionLayers.push((_a = config.override) === null || _a === void 0 ? void 0 : _a.functions);
apiLayers.push((_b = config.override) === null || _b === void 0 ? void 0 : _b.apis);
functionLayers = functionLayers.filter((layer) => layer !== undefined);
apiLayers = apiLayers.filter((layer) => layer !== undefined);
if (recipeId !== "accountlinking" && apiLayers.length > 0) {
config.override = Object.assign(Object.assign({}, config.override), {
apis: (oI, builder) => {
for (const layer of apiLayers) {
builder.override(layer);
}
return oI;
},
});
}
if (functionLayers.length > 0) {
config.override = Object.assign(Object.assign({}, config.override), {
functions: (oI, builder) => {
for (const layer of functionLayers) {
builder.override(layer);
}
return oI;
},
});
}
return config;
}
/**
* Processes the list of plugins, resolving dependencies, applying overrides, and collecting route handlers.
*/
function loadPlugins({ plugins, config, normalisedAppInfo }) {
const inputPluginList = plugins !== null && plugins !== void 0 ? plugins : [];
const finalPluginList = [];
const seenPlugins = new Set();
for (const plugin of inputPluginList) {
if (seenPlugins.has(plugin.id)) {
continue;
}
if (plugin.compatibleSDKVersions) {
const versionCheck = (0, versionChecker_1.isVersionCompatible)(
version_1.version,
plugin.compatibleSDKVersions
);
if (!versionCheck) {
throw new Error(
`Incompatible SDK version for plugin ${plugin.id}. Version "${
version_1.version
}" not found in compatible versions: ${JSON.stringify(plugin.compatibleSDKVersions)}`
);
}
}
const dependencies = getPluginDependencies({
plugin,
config,
pluginsAbove: finalPluginList,
sdkVersion: version_1.version,
normalisedAppInfo,
});
finalPluginList.push(...dependencies);
for (const dep of dependencies) {
seenPlugins.add(dep.id);
}
}
const duplicatePluginIds = finalPluginList.filter((plugin, index) =>
finalPluginList.some((elem, idx) => elem.id === plugin.id && idx !== index)
);
if (duplicatePluginIds.length > 0) {
throw new Error(`Duplicate plugin IDs: ${duplicatePluginIds.map((plugin) => plugin.id).join(", ")}`);
}
const processedPlugins = finalPluginList.map(getPublicPlugin);
let _config = Object.assign({}, config);
const pluginRouteHandlers = [];
for (const [pluginIndex, plugin] of finalPluginList.entries()) {
if (plugin.config) {
// @ts-ignore
const _a = plugin.config(
(0, utils_1.getPublicConfig)(
Object.assign(Object.assign({}, _config), { appInfo: normalisedAppInfo })
)
),
{ appInfo, recipeList, experimental } = _a,
pluginConfigOverride = __rest(_a, ["appInfo", "recipeList", "experimental"]);
_config = Object.assign(Object.assign({}, _config), pluginConfigOverride);
}
const publicConfig = (0, utils_1.getPublicConfig)(
Object.assign(Object.assign({}, _config), { appInfo: normalisedAppInfo })
);
if (plugin.routeHandlers) {
let handlers = [];
if (typeof plugin.routeHandlers === "function") {
const result = plugin.routeHandlers(publicConfig, processedPlugins, version_1.version);
if (result.status === "ERROR") {
throw new Error(result.message);
}
handlers = result.routeHandlers.map((handler) =>
Object.assign(Object.assign({}, handler), { pluginId: plugin.id })
);
} else {
handlers = plugin.routeHandlers.map((handler) =>
Object.assign(Object.assign({}, handler), { pluginId: plugin.id })
);
}
pluginRouteHandlers.push(...handlers);
}
if (plugin.init) {
postSuperTokensInitCallbacks_1.PostSuperTokensInitCallbacks.addPostInitCallback(() => {
plugin.init(publicConfig, processedPlugins, version_1.version);
processedPlugins[pluginIndex].initialized = true;
});
}
}
const overrideMaps = finalPluginList.filter((p) => p.overrideMap !== undefined).map((p) => p.overrideMap);
return {
config: _config,
pluginRouteHandlers,
overrideMaps,
};
}