From b1f72d236861bb06a4318640e181af8e54d26173 Mon Sep 17 00:00:00 2001 From: Mihaly Lengyel Date: Wed, 15 Jan 2025 17:41:39 +0100 Subject: [PATCH] feat: update based on initial feedback --- lib/build/supertokens.js | 23 ++++++++++++++++++---- lib/build/types.d.ts | 19 +++++++++++++++--- lib/build/utils.d.ts | 2 +- lib/ts/supertokens.ts | 37 ++++++++++++++++++++++++++++++----- lib/ts/types.ts | 13 ++++++++---- lib/ts/utils.ts | 2 +- test/with-typescript/index.ts | 2 +- 7 files changed, 79 insertions(+), 19 deletions(-) diff --git a/lib/build/supertokens.js b/lib/build/supertokens.js index f4dcf0af4..1fdc19ee3 100644 --- a/lib/build/supertokens.js +++ b/lib/build/supertokens.js @@ -344,14 +344,29 @@ class SuperTokens { } return userContext._default.request; }; - const pluginList = (_a = config.plugins) !== null && _a !== void 0 ? _a : []; + const inputPluginList = (_a = config.plugins) !== null && _a !== void 0 ? _a : []; this.pluginRouteHandlers = []; - for (const plugin of pluginList) { - const versionContraints = Array.isArray(plugin.sdkVersion) ? plugin.sdkVersion : [plugin.sdkVersion]; + const finalPluginList = []; + for (const plugin of inputPluginList) { + const versionContraints = Array.isArray(plugin.compatibleSDKVersions) + ? plugin.compatibleSDKVersions + : [plugin.compatibleSDKVersions]; if (!versionContraints.includes(version_1.version)) { // TODO: better checks throw new Error("Plugin version mismatch"); } + if (plugin.dependencies) { + const result = plugin.dependencies(finalPluginList, version_1.version); + if (result.status === "ERROR") { + throw new Error(result.message); + } + if (result.pluginsToAdd) { + finalPluginList.push(...result.pluginsToAdd); + } + finalPluginList.push(plugin); + } + } + for (const plugin of finalPluginList) { if (plugin.routeHandlers) { this.pluginRouteHandlers.push(...plugin.routeHandlers); } @@ -419,7 +434,7 @@ class SuperTokens { const recipeModule = func( this.appInfo, this.isInServerlessEnv, - pluginList.map((p) => p.overrideMap) + finalPluginList.filter((p) => p.overrideMap !== undefined).map((p) => p.overrideMap) ); if (recipeModule.getRecipeId() === MultitenancyRecipe.RECIPE_ID) { multitenancyFound = true; diff --git a/lib/build/types.d.ts b/lib/build/types.d.ts index c0fce5729..f13c462b9 100644 --- a/lib/build/types.d.ts +++ b/lib/build/types.d.ts @@ -95,8 +95,21 @@ export type PluginRouteHandler = { }; export type SuperTokensPlugin = { id: string; - sdkVersion: string | string[]; - overrideMap: { + version?: string; + compatibleSDKVersions?: string | string[]; + dependencies?: ( + pluginsAbove: SuperTokensPlugin[], + sdkVersion: string + ) => + | { + status: "OK"; + pluginsToAdd?: SuperTokensPlugin[]; + } + | { + status: "ERROR"; + message: string; + }; + overrideMap?: { [recipeId in keyof AllRecipeConfigs]?: RecipePluginOverride & { recipeInitRequired?: boolean | ((sdkVersion: string) => boolean); }; @@ -126,7 +139,7 @@ export interface HttpRequest { export type RecipeListFunction = ( appInfo: NormalisedAppinfo, isInServerlessEnv: boolean, - overrideMaps?: SuperTokensPlugin["overrideMap"][] + overrideMaps?: NonNullable[] ) => RecipeModule; export type APIHandled = { pathWithoutApiBasePath: NormalisedURLPath; diff --git a/lib/build/utils.d.ts b/lib/build/utils.d.ts index c16d6254c..30a07a6b0 100644 --- a/lib/build/utils.d.ts +++ b/lib/build/utils.d.ts @@ -83,5 +83,5 @@ export declare const isBuffer: (obj: any) => boolean; export declare function applyPlugins( recipeId: T, config: AllRecipeConfigs[T] | undefined, - plugins: SuperTokensPlugin["overrideMap"][] + plugins: NonNullable[] ): AllRecipeConfigs[T]; diff --git a/lib/ts/supertokens.ts b/lib/ts/supertokens.ts index f500cc9c8..6c999ed00 100644 --- a/lib/ts/supertokens.ts +++ b/lib/ts/supertokens.ts @@ -13,7 +13,15 @@ * under the License. */ -import { TypeInput, NormalisedAppinfo, HTTPMethod, SuperTokensInfo, UserContext, PluginRouteHandler } from "./types"; +import { + TypeInput, + NormalisedAppinfo, + HTTPMethod, + SuperTokensInfo, + UserContext, + PluginRouteHandler, + SuperTokensPlugin, +} from "./types"; import { normaliseInputAppInfoOrThrowError, maxVersion, @@ -53,18 +61,35 @@ export default class SuperTokens { telemetryEnabled: boolean; constructor(config: TypeInput) { - const pluginList = config.plugins ?? []; + const inputPluginList = config.plugins ?? []; this.pluginRouteHandlers = []; - for (const plugin of pluginList) { - const versionContraints = Array.isArray(plugin.sdkVersion) ? plugin.sdkVersion : [plugin.sdkVersion]; + const finalPluginList: SuperTokensPlugin[] = []; + for (const plugin of inputPluginList) { + const versionContraints = Array.isArray(plugin.compatibleSDKVersions) + ? plugin.compatibleSDKVersions + : [plugin.compatibleSDKVersions]; if (!versionContraints.includes(version)) { // TODO: better checks throw new Error("Plugin version mismatch"); } + if (plugin.dependencies) { + const result = plugin.dependencies(finalPluginList, version); + if (result.status === "ERROR") { + throw new Error(result.message); + } + if (result.pluginsToAdd) { + finalPluginList.push(...result.pluginsToAdd); + } + finalPluginList.push(plugin); + } + } + + for (const plugin of finalPluginList) { if (plugin.routeHandlers) { this.pluginRouteHandlers.push(...plugin.routeHandlers); } } + if (config.debug === true) { enableDebugLogs(); } @@ -138,7 +163,9 @@ export default class SuperTokens { const recipeModule = func( this.appInfo, this.isInServerlessEnv, - pluginList.map((p) => p.overrideMap) + finalPluginList.filter((p) => p.overrideMap !== undefined).map((p) => p.overrideMap) as NonNullable< + SuperTokensPlugin["overrideMap"] + >[] ); if (recipeModule.getRecipeId() === MultitenancyRecipe.RECIPE_ID) { multitenancyFound = true; diff --git a/lib/ts/types.ts b/lib/ts/types.ts index 281256269..e2d752381 100644 --- a/lib/ts/types.ts +++ b/lib/ts/types.ts @@ -117,9 +117,14 @@ export type PluginRouteHandler = { }; export type SuperTokensPlugin = { - id: string; - sdkVersion: string | string[]; // match the syntax of the engines field in package.json - overrideMap: { + id: string; // TODO: validate that no two plugins have the same id + version?: string; + compatibleSDKVersions?: string | string[]; // match the syntax of the engines field in package.json + dependencies?: ( + pluginsAbove: SuperTokensPlugin[], + sdkVersion: string + ) => { status: "OK"; pluginsToAdd?: SuperTokensPlugin[] } | { status: "ERROR"; message: string }; + overrideMap?: { [recipeId in keyof AllRecipeConfigs]?: RecipePluginOverride & { recipeInitRequired?: boolean | ((sdkVersion: string) => boolean); }; @@ -151,7 +156,7 @@ export interface HttpRequest { export type RecipeListFunction = ( appInfo: NormalisedAppinfo, isInServerlessEnv: boolean, - overrideMaps?: SuperTokensPlugin["overrideMap"][] + overrideMaps?: NonNullable[] ) => RecipeModule; export type APIHandled = { diff --git a/lib/ts/utils.ts b/lib/ts/utils.ts index ece527e8b..dffef3f51 100644 --- a/lib/ts/utils.ts +++ b/lib/ts/utils.ts @@ -525,7 +525,7 @@ export const isBuffer = (obj: any): boolean => { export function applyPlugins( recipeId: T, config: AllRecipeConfigs[T] | undefined, - plugins: SuperTokensPlugin["overrideMap"][] + plugins: NonNullable[] ): AllRecipeConfigs[T] { config = config ?? ({} as AllRecipeConfigs[T]); let functionLayers = [config.override?.functions]; diff --git a/test/with-typescript/index.ts b/test/with-typescript/index.ts index 2c1c2115e..5a14358e1 100644 --- a/test/with-typescript/index.ts +++ b/test/with-typescript/index.ts @@ -2317,7 +2317,7 @@ Supertokens.init({ plugins: [ { id: "asdf", - sdkVersion: "1.2.3", + compatibleSDKVersions: "1.2.3", overrideMap: { emailpassword: { id: "emailpassword",