@supertokens-plugins/rownd-nodejs 0.3.0-beta.0 → 0.3.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -64,6 +64,19 @@ RowndMigrationPlugin.init({
64
64
  });
65
65
  ```
66
66
 
67
+ ### Mobile Deep Links
68
+
69
+ Set `mobileDeepLinkBaseUrl` to rewrite mobile-context account links to a native deep link target. The value must be an absolute URL base, such as a custom scheme or HTTPS universal/app link domain.
70
+
71
+ ```typescript
72
+ RowndMigrationPlugin.init({
73
+ rowndAppKey: process.env.ROWND_APP_KEY,
74
+ rowndAppSecret: process.env.ROWND_APP_SECRET,
75
+ mobileDeepLinkBaseUrl: "customDomain://",
76
+ // or: mobileDeepLinkBaseUrl: "https://links.example.com",
77
+ });
78
+ ```
79
+
67
80
  ## API Endpoint
68
81
 
69
82
  The plugin exposes a single endpoint:
package/dist/index.d.mts CHANGED
@@ -1,5 +1,7 @@
1
1
  import * as SuperTokens from 'supertokens-node';
2
2
  import { JSONObject, SuperTokensPlugin } from 'supertokens-node/types';
3
+ import { TypePasswordlessEmailDeliveryInput } from 'supertokens-node/recipe/passwordless/types';
4
+ import { TypeEmailVerificationEmailDeliveryInput } from 'supertokens-node/recipe/emailverification/types';
3
5
 
4
6
  type RowndSignInMethod = {
5
7
  /**
@@ -660,6 +662,7 @@ interface RowndPluginConfig {
660
662
  rowndAppKey: string;
661
663
  rowndAppSecret: string;
662
664
  enableDebugLogs?: boolean;
665
+ mobileDeepLinkBaseUrl?: string;
663
666
  telemetry?: RowndTelemetryConfig;
664
667
  schema?: RowndSchema;
665
668
  appConfig?: RowndAppConfigInput;
@@ -736,6 +739,7 @@ interface RowndPluginNormalisedConfig {
736
739
  rowndAppKey: string;
737
740
  rowndAppSecret: string;
738
741
  enableDebugLogs?: boolean;
742
+ mobileDeepLinkBaseUrl?: string;
739
743
  telemetry?: RowndTelemetryConfig;
740
744
  schema?: RowndSchema;
741
745
  appConfig?: RowndAppConfigInput;
@@ -816,6 +820,19 @@ declare class RowndPluginError extends Error {
816
820
  constructor(type: RowndPluginErrorType);
817
821
  }
818
822
 
823
+ type RowndEmailContentResult = {
824
+ body: string;
825
+ isHtml: boolean;
826
+ subject: string;
827
+ toEmail: string;
828
+ };
829
+ declare function getRowndPasswordlessEmailContent(input: TypePasswordlessEmailDeliveryInput & {
830
+ userContext?: Record<string, unknown>;
831
+ }, originalContent: RowndEmailContentResult): Promise<RowndEmailContentResult>;
832
+ declare function getRowndEmailVerificationContent(input: TypeEmailVerificationEmailDeliveryInput & {
833
+ userContext?: Record<string, unknown>;
834
+ }, originalContent: RowndEmailContentResult): Promise<RowndEmailContentResult>;
835
+
819
836
  declare function setRowndClient(client: IRowndClient): void;
820
837
  declare function getRowndClient(): IRowndClient;
821
838
 
@@ -823,4 +840,4 @@ declare const _default: {
823
840
  init: (config: RowndPluginConfig) => SuperTokens.SuperTokensPlugin;
824
841
  };
825
842
 
826
- export { ANONYMOUS_AUTH_METHOD_ID, DEFAULT_ROWND_SCHEMA, GUEST_AUTH_METHOD_ID, HANDLE_BASE_PATH, type IRowndClient, type MigrationResponse, PLUGIN_ID, PLUGIN_SDK_VERSION, PUBLIC_TENANT_ID, ROWND_JWT_CLAIMS, ROWND_PLUGIN_ERROR_MESSAGES, type RowndAppConfigInput, type RowndAuthConfig, type RowndBranding, type RowndCustomContent, type RowndLegal, type RowndPluginConfig, RowndPluginError, type RowndPluginErrorType, type RowndPluginNormalisedConfig, type RowndProfileConfig, type RowndSchema, type RowndSchemaField, type RowndSignInMethod, type RowndSubBrandConfigInput, type RowndTelemetryClient, type RowndTelemetryConfig, type RowndTelemetryEvent, type RowndUser, type RowndUserMetadata, type SuperTokensUserImport, _default as default, getRowndClient, init, setRowndClient };
843
+ export { ANONYMOUS_AUTH_METHOD_ID, DEFAULT_ROWND_SCHEMA, GUEST_AUTH_METHOD_ID, HANDLE_BASE_PATH, type IRowndClient, type MigrationResponse, PLUGIN_ID, PLUGIN_SDK_VERSION, PUBLIC_TENANT_ID, ROWND_JWT_CLAIMS, ROWND_PLUGIN_ERROR_MESSAGES, type RowndAppConfigInput, type RowndAuthConfig, type RowndBranding, type RowndCustomContent, type RowndEmailContentResult, type RowndLegal, type RowndPluginConfig, RowndPluginError, type RowndPluginErrorType, type RowndPluginNormalisedConfig, type RowndProfileConfig, type RowndSchema, type RowndSchemaField, type RowndSignInMethod, type RowndSubBrandConfigInput, type RowndTelemetryClient, type RowndTelemetryConfig, type RowndTelemetryEvent, type RowndUser, type RowndUserMetadata, type SuperTokensUserImport, _default as default, getRowndClient, getRowndEmailVerificationContent, getRowndPasswordlessEmailContent, init, setRowndClient };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import * as SuperTokens from 'supertokens-node';
2
2
  import { JSONObject, SuperTokensPlugin } from 'supertokens-node/types';
3
+ import { TypePasswordlessEmailDeliveryInput } from 'supertokens-node/recipe/passwordless/types';
4
+ import { TypeEmailVerificationEmailDeliveryInput } from 'supertokens-node/recipe/emailverification/types';
3
5
 
4
6
  type RowndSignInMethod = {
5
7
  /**
@@ -660,6 +662,7 @@ interface RowndPluginConfig {
660
662
  rowndAppKey: string;
661
663
  rowndAppSecret: string;
662
664
  enableDebugLogs?: boolean;
665
+ mobileDeepLinkBaseUrl?: string;
663
666
  telemetry?: RowndTelemetryConfig;
664
667
  schema?: RowndSchema;
665
668
  appConfig?: RowndAppConfigInput;
@@ -736,6 +739,7 @@ interface RowndPluginNormalisedConfig {
736
739
  rowndAppKey: string;
737
740
  rowndAppSecret: string;
738
741
  enableDebugLogs?: boolean;
742
+ mobileDeepLinkBaseUrl?: string;
739
743
  telemetry?: RowndTelemetryConfig;
740
744
  schema?: RowndSchema;
741
745
  appConfig?: RowndAppConfigInput;
@@ -816,6 +820,19 @@ declare class RowndPluginError extends Error {
816
820
  constructor(type: RowndPluginErrorType);
817
821
  }
818
822
 
823
+ type RowndEmailContentResult = {
824
+ body: string;
825
+ isHtml: boolean;
826
+ subject: string;
827
+ toEmail: string;
828
+ };
829
+ declare function getRowndPasswordlessEmailContent(input: TypePasswordlessEmailDeliveryInput & {
830
+ userContext?: Record<string, unknown>;
831
+ }, originalContent: RowndEmailContentResult): Promise<RowndEmailContentResult>;
832
+ declare function getRowndEmailVerificationContent(input: TypeEmailVerificationEmailDeliveryInput & {
833
+ userContext?: Record<string, unknown>;
834
+ }, originalContent: RowndEmailContentResult): Promise<RowndEmailContentResult>;
835
+
819
836
  declare function setRowndClient(client: IRowndClient): void;
820
837
  declare function getRowndClient(): IRowndClient;
821
838
 
@@ -823,4 +840,4 @@ declare const _default: {
823
840
  init: (config: RowndPluginConfig) => SuperTokens.SuperTokensPlugin;
824
841
  };
825
842
 
826
- export { ANONYMOUS_AUTH_METHOD_ID, DEFAULT_ROWND_SCHEMA, GUEST_AUTH_METHOD_ID, HANDLE_BASE_PATH, type IRowndClient, type MigrationResponse, PLUGIN_ID, PLUGIN_SDK_VERSION, PUBLIC_TENANT_ID, ROWND_JWT_CLAIMS, ROWND_PLUGIN_ERROR_MESSAGES, type RowndAppConfigInput, type RowndAuthConfig, type RowndBranding, type RowndCustomContent, type RowndLegal, type RowndPluginConfig, RowndPluginError, type RowndPluginErrorType, type RowndPluginNormalisedConfig, type RowndProfileConfig, type RowndSchema, type RowndSchemaField, type RowndSignInMethod, type RowndSubBrandConfigInput, type RowndTelemetryClient, type RowndTelemetryConfig, type RowndTelemetryEvent, type RowndUser, type RowndUserMetadata, type SuperTokensUserImport, _default as default, getRowndClient, init, setRowndClient };
843
+ export { ANONYMOUS_AUTH_METHOD_ID, DEFAULT_ROWND_SCHEMA, GUEST_AUTH_METHOD_ID, HANDLE_BASE_PATH, type IRowndClient, type MigrationResponse, PLUGIN_ID, PLUGIN_SDK_VERSION, PUBLIC_TENANT_ID, ROWND_JWT_CLAIMS, ROWND_PLUGIN_ERROR_MESSAGES, type RowndAppConfigInput, type RowndAuthConfig, type RowndBranding, type RowndCustomContent, type RowndEmailContentResult, type RowndLegal, type RowndPluginConfig, RowndPluginError, type RowndPluginErrorType, type RowndPluginNormalisedConfig, type RowndProfileConfig, type RowndSchema, type RowndSchemaField, type RowndSignInMethod, type RowndSubBrandConfigInput, type RowndTelemetryClient, type RowndTelemetryConfig, type RowndTelemetryEvent, type RowndUser, type RowndUserMetadata, type SuperTokensUserImport, _default as default, getRowndClient, getRowndEmailVerificationContent, getRowndPasswordlessEmailContent, init, setRowndClient };
package/dist/index.js CHANGED
@@ -279,6 +279,8 @@ __export(index_exports, {
279
279
  RowndPluginError: () => RowndPluginError,
280
280
  default: () => index_default,
281
281
  getRowndClient: () => getRowndClient,
282
+ getRowndEmailVerificationContent: () => getRowndEmailVerificationContent,
283
+ getRowndPasswordlessEmailContent: () => getRowndPasswordlessEmailContent,
282
284
  init: () => init,
283
285
  setRowndClient: () => setRowndClient
284
286
  });
@@ -881,6 +883,26 @@ function rewriteLinkPath(inputUrl, targetPath, searchParams) {
881
883
  return queryString ? `${path}?${queryString}` : path;
882
884
  }
883
885
  }
886
+ function rewriteLinkToMobileDeepLink(inputUrl, targetPath, baseUrl, searchParams) {
887
+ const normalizedBaseUrl = baseUrl.endsWith("://") ? baseUrl : baseUrl.replace(/\/+$/, "") + "/";
888
+ const targetUrl = new URL(
889
+ `${normalizedBaseUrl}${targetPath.replace(/^\//, "")}`
890
+ );
891
+ try {
892
+ const sourceUrl = new URL(inputUrl);
893
+ targetUrl.search = sourceUrl.search;
894
+ targetUrl.hash = sourceUrl.hash;
895
+ } catch {
896
+ const [, query = ""] = inputUrl.split("?");
897
+ targetUrl.search = query ? `?${query.split("#")[0]}` : "";
898
+ const hash = inputUrl.split("#")[1];
899
+ targetUrl.hash = hash ? `#${hash}` : "";
900
+ }
901
+ for (const [key, value] of Object.entries(searchParams ?? {})) {
902
+ targetUrl.searchParams.set(key, value);
903
+ }
904
+ return targetUrl.toString();
905
+ }
884
906
  var rowndClient;
885
907
  var pluginConfig;
886
908
  function setRowndClient(client) {
@@ -895,6 +917,9 @@ function getRowndClient() {
895
917
  function setPluginConfig(config2) {
896
918
  pluginConfig = config2;
897
919
  }
920
+ function getPluginConfig() {
921
+ return pluginConfig;
922
+ }
898
923
  async function parseRequest(req) {
899
924
  const authHeader = req.getHeaderValue("authorization");
900
925
  if (!authHeader) {
@@ -2355,9 +2380,15 @@ var init = createPluginInitFunction(
2355
2380
  ...typeof displayContext === "string" ? { displayContext } : {},
2356
2381
  ...typeof redirectToPath === "string" ? { redirectToPath } : {}
2357
2382
  };
2383
+ const rewrittenLink = input[linkKey] ? displayContext === "mobile_app" && pluginConfig2.mobileDeepLinkBaseUrl ? rewriteLinkToMobileDeepLink(
2384
+ input[linkKey],
2385
+ targetPath,
2386
+ pluginConfig2.mobileDeepLinkBaseUrl,
2387
+ bootstrapParams
2388
+ ) : rewriteLinkPath(input[linkKey], targetPath, bootstrapParams) : input[linkKey];
2358
2389
  return {
2359
2390
  ...input,
2360
- [linkKey]: input[linkKey] ? rewriteLinkPath(input[linkKey], targetPath, bootstrapParams) : input[linkKey]
2391
+ [linkKey]: rewrittenLink
2361
2392
  };
2362
2393
  };
2363
2394
  setRowndClient(rowndClient2);
@@ -2720,10 +2751,21 @@ var init = createPluginInitFunction(
2720
2751
  );
2721
2752
  }
2722
2753
  }
2754
+ if (config2.mobileDeepLinkBaseUrl !== void 0) {
2755
+ try {
2756
+ if (!/^[A-Za-z][A-Za-z0-9+.-]*:\/\//.test(config2.mobileDeepLinkBaseUrl)) {
2757
+ throw new Error();
2758
+ }
2759
+ new URL(config2.mobileDeepLinkBaseUrl);
2760
+ } catch {
2761
+ throw new Error("Invalid mobileDeepLinkBaseUrl in plugin config");
2762
+ }
2763
+ }
2723
2764
  return {
2724
2765
  rowndAppKey: config2.rowndAppKey,
2725
2766
  rowndAppSecret: config2.rowndAppSecret,
2726
2767
  enableDebugLogs: config2.enableDebugLogs,
2768
+ mobileDeepLinkBaseUrl: config2.mobileDeepLinkBaseUrl,
2727
2769
  telemetry: config2.telemetry,
2728
2770
  schema: config2.schema,
2729
2771
  appConfig: config2.appConfig,
@@ -2732,6 +2774,111 @@ var init = createPluginInitFunction(
2732
2774
  }
2733
2775
  );
2734
2776
 
2777
+ // src/emailContent.ts
2778
+ function getActiveAppConfig(userContext) {
2779
+ const config2 = getPluginConfig();
2780
+ const baseApp = config2?.appConfig;
2781
+ const appVariantId = userContext?.rowndAppVariantId;
2782
+ if (typeof appVariantId === "string" && config2?.subBrands?.[appVariantId]) {
2783
+ return deepMerge(baseApp ?? {}, config2.subBrands[appVariantId]);
2784
+ }
2785
+ return baseApp;
2786
+ }
2787
+ function deepMerge(base, overlay) {
2788
+ const result = { ...base };
2789
+ for (const [key, value] of Object.entries(overlay)) {
2790
+ if (value === void 0) {
2791
+ continue;
2792
+ }
2793
+ const existingValue = result[key];
2794
+ if (isRecord2(existingValue) && isRecord2(value)) {
2795
+ result[key] = deepMerge(existingValue, value);
2796
+ } else {
2797
+ result[key] = value;
2798
+ }
2799
+ }
2800
+ return result;
2801
+ }
2802
+ function isRecord2(value) {
2803
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2804
+ }
2805
+ function getEmailConfig(userContext) {
2806
+ return getActiveAppConfig(userContext)?.auth?.email;
2807
+ }
2808
+ function getEmailLink(input) {
2809
+ if (hasOwn2(input, "urlWithLinkCode")) {
2810
+ return input.urlWithLinkCode;
2811
+ }
2812
+ return hasOwn2(input, "emailVerifyLink") ? input.emailVerifyLink : void 0;
2813
+ }
2814
+ function getEmailAddress(input) {
2815
+ if (hasOwn2(input, "email")) {
2816
+ return input.email;
2817
+ }
2818
+ return input.user.email;
2819
+ }
2820
+ function hasOwn2(value, key) {
2821
+ return Object.prototype.hasOwnProperty.call(value, key);
2822
+ }
2823
+ function buildRowndEmailContent(input, originalContent, emailConfig) {
2824
+ if (!emailConfig) {
2825
+ return originalContent;
2826
+ }
2827
+ const appConfig = getActiveAppConfig(input.userContext) ?? {};
2828
+ const link = getEmailLink(input);
2829
+ const callToActionText = emailConfig.callToActionText ?? "Verify your email";
2830
+ if (!link) {
2831
+ return {
2832
+ ...originalContent,
2833
+ subject: emailConfig.subject ?? originalContent.subject
2834
+ };
2835
+ }
2836
+ return {
2837
+ ...originalContent,
2838
+ toEmail: getEmailAddress(input),
2839
+ subject: emailConfig.subject ?? originalContent.subject,
2840
+ isHtml: true,
2841
+ body: renderEmailHtml({
2842
+ appConfig,
2843
+ emailConfig,
2844
+ link,
2845
+ callToActionText,
2846
+ originalBody: originalContent.body
2847
+ })
2848
+ };
2849
+ }
2850
+ function renderEmailHtml(input) {
2851
+ const primaryColor = input.appConfig.branding?.primaryColor ?? DEFAULT_PRIMARY_COLOR;
2852
+ const image = input.emailConfig.image || input.appConfig.branding?.logo || input.appConfig.icon;
2853
+ const appName = input.appConfig.name ?? "Application";
2854
+ const content = input.emailConfig.customContent ?? input.originalBody;
2855
+ return `<!doctype html>
2856
+ <html>
2857
+ <body style="font-family: Arial, sans-serif; color: #1f2937; margin: 0; padding: 32px; background: #f9fafb;">
2858
+ <table role="presentation" width="100%" cellspacing="0" cellpadding="0" style="max-width: 560px; margin: 0 auto; background: #ffffff; border-radius: 16px; padding: 32px;">
2859
+ <tr>
2860
+ <td style="text-align: center;">
2861
+ ${image ? `<img src="${escapeHtml(image)}" alt="${escapeHtml(appName)}" style="max-width: 96px; max-height: 96px; margin-bottom: 24px;" />` : ""}
2862
+ <div style="font-size: 18px; font-weight: 600; margin-bottom: 16px;">${escapeHtml(appName)}</div>
2863
+ <div style="font-size: 15px; line-height: 1.6; margin-bottom: 24px;">${content}</div>
2864
+ <a href="${escapeHtml(input.link)}" style="display: inline-block; background: ${escapeHtml(primaryColor)}; color: #ffffff; text-decoration: none; padding: 12px 20px; border-radius: 10px; font-weight: 600;">${escapeHtml(input.callToActionText)}</a>
2865
+ ${input.emailConfig.customClosingContent ? `<div style="font-size: 14px; line-height: 1.6; margin-top: 24px;">${input.emailConfig.customClosingContent}</div>` : ""}
2866
+ </td>
2867
+ </tr>
2868
+ </table>
2869
+ </body>
2870
+ </html>`;
2871
+ }
2872
+ function escapeHtml(value) {
2873
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
2874
+ }
2875
+ async function getRowndPasswordlessEmailContent(input, originalContent) {
2876
+ return buildRowndEmailContent(input, originalContent, getEmailConfig(input.userContext));
2877
+ }
2878
+ async function getRowndEmailVerificationContent(input, originalContent) {
2879
+ return buildRowndEmailContent(input, originalContent, getEmailConfig(input.userContext));
2880
+ }
2881
+
2735
2882
  // src/index.ts
2736
2883
  var index_default = { init };
2737
2884
  // Annotate the CommonJS export names for ESM import in node:
@@ -2747,6 +2894,8 @@ var index_default = { init };
2747
2894
  ROWND_PLUGIN_ERROR_MESSAGES,
2748
2895
  RowndPluginError,
2749
2896
  getRowndClient,
2897
+ getRowndEmailVerificationContent,
2898
+ getRowndPasswordlessEmailContent,
2750
2899
  init,
2751
2900
  setRowndClient
2752
2901
  });
package/dist/index.mjs CHANGED
@@ -861,6 +861,26 @@ function rewriteLinkPath(inputUrl, targetPath, searchParams) {
861
861
  return queryString ? `${path}?${queryString}` : path;
862
862
  }
863
863
  }
864
+ function rewriteLinkToMobileDeepLink(inputUrl, targetPath, baseUrl, searchParams) {
865
+ const normalizedBaseUrl = baseUrl.endsWith("://") ? baseUrl : baseUrl.replace(/\/+$/, "") + "/";
866
+ const targetUrl = new URL(
867
+ `${normalizedBaseUrl}${targetPath.replace(/^\//, "")}`
868
+ );
869
+ try {
870
+ const sourceUrl = new URL(inputUrl);
871
+ targetUrl.search = sourceUrl.search;
872
+ targetUrl.hash = sourceUrl.hash;
873
+ } catch {
874
+ const [, query = ""] = inputUrl.split("?");
875
+ targetUrl.search = query ? `?${query.split("#")[0]}` : "";
876
+ const hash = inputUrl.split("#")[1];
877
+ targetUrl.hash = hash ? `#${hash}` : "";
878
+ }
879
+ for (const [key, value] of Object.entries(searchParams ?? {})) {
880
+ targetUrl.searchParams.set(key, value);
881
+ }
882
+ return targetUrl.toString();
883
+ }
864
884
  var rowndClient;
865
885
  var pluginConfig;
866
886
  function setRowndClient(client) {
@@ -875,6 +895,9 @@ function getRowndClient() {
875
895
  function setPluginConfig(config2) {
876
896
  pluginConfig = config2;
877
897
  }
898
+ function getPluginConfig() {
899
+ return pluginConfig;
900
+ }
878
901
  async function parseRequest(req) {
879
902
  const authHeader = req.getHeaderValue("authorization");
880
903
  if (!authHeader) {
@@ -2335,9 +2358,15 @@ var init = createPluginInitFunction(
2335
2358
  ...typeof displayContext === "string" ? { displayContext } : {},
2336
2359
  ...typeof redirectToPath === "string" ? { redirectToPath } : {}
2337
2360
  };
2361
+ const rewrittenLink = input[linkKey] ? displayContext === "mobile_app" && pluginConfig2.mobileDeepLinkBaseUrl ? rewriteLinkToMobileDeepLink(
2362
+ input[linkKey],
2363
+ targetPath,
2364
+ pluginConfig2.mobileDeepLinkBaseUrl,
2365
+ bootstrapParams
2366
+ ) : rewriteLinkPath(input[linkKey], targetPath, bootstrapParams) : input[linkKey];
2338
2367
  return {
2339
2368
  ...input,
2340
- [linkKey]: input[linkKey] ? rewriteLinkPath(input[linkKey], targetPath, bootstrapParams) : input[linkKey]
2369
+ [linkKey]: rewrittenLink
2341
2370
  };
2342
2371
  };
2343
2372
  setRowndClient(rowndClient2);
@@ -2700,10 +2729,21 @@ var init = createPluginInitFunction(
2700
2729
  );
2701
2730
  }
2702
2731
  }
2732
+ if (config2.mobileDeepLinkBaseUrl !== void 0) {
2733
+ try {
2734
+ if (!/^[A-Za-z][A-Za-z0-9+.-]*:\/\//.test(config2.mobileDeepLinkBaseUrl)) {
2735
+ throw new Error();
2736
+ }
2737
+ new URL(config2.mobileDeepLinkBaseUrl);
2738
+ } catch {
2739
+ throw new Error("Invalid mobileDeepLinkBaseUrl in plugin config");
2740
+ }
2741
+ }
2703
2742
  return {
2704
2743
  rowndAppKey: config2.rowndAppKey,
2705
2744
  rowndAppSecret: config2.rowndAppSecret,
2706
2745
  enableDebugLogs: config2.enableDebugLogs,
2746
+ mobileDeepLinkBaseUrl: config2.mobileDeepLinkBaseUrl,
2707
2747
  telemetry: config2.telemetry,
2708
2748
  schema: config2.schema,
2709
2749
  appConfig: config2.appConfig,
@@ -2712,6 +2752,111 @@ var init = createPluginInitFunction(
2712
2752
  }
2713
2753
  );
2714
2754
 
2755
+ // src/emailContent.ts
2756
+ function getActiveAppConfig(userContext) {
2757
+ const config2 = getPluginConfig();
2758
+ const baseApp = config2?.appConfig;
2759
+ const appVariantId = userContext?.rowndAppVariantId;
2760
+ if (typeof appVariantId === "string" && config2?.subBrands?.[appVariantId]) {
2761
+ return deepMerge(baseApp ?? {}, config2.subBrands[appVariantId]);
2762
+ }
2763
+ return baseApp;
2764
+ }
2765
+ function deepMerge(base, overlay) {
2766
+ const result = { ...base };
2767
+ for (const [key, value] of Object.entries(overlay)) {
2768
+ if (value === void 0) {
2769
+ continue;
2770
+ }
2771
+ const existingValue = result[key];
2772
+ if (isRecord2(existingValue) && isRecord2(value)) {
2773
+ result[key] = deepMerge(existingValue, value);
2774
+ } else {
2775
+ result[key] = value;
2776
+ }
2777
+ }
2778
+ return result;
2779
+ }
2780
+ function isRecord2(value) {
2781
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2782
+ }
2783
+ function getEmailConfig(userContext) {
2784
+ return getActiveAppConfig(userContext)?.auth?.email;
2785
+ }
2786
+ function getEmailLink(input) {
2787
+ if (hasOwn2(input, "urlWithLinkCode")) {
2788
+ return input.urlWithLinkCode;
2789
+ }
2790
+ return hasOwn2(input, "emailVerifyLink") ? input.emailVerifyLink : void 0;
2791
+ }
2792
+ function getEmailAddress(input) {
2793
+ if (hasOwn2(input, "email")) {
2794
+ return input.email;
2795
+ }
2796
+ return input.user.email;
2797
+ }
2798
+ function hasOwn2(value, key) {
2799
+ return Object.prototype.hasOwnProperty.call(value, key);
2800
+ }
2801
+ function buildRowndEmailContent(input, originalContent, emailConfig) {
2802
+ if (!emailConfig) {
2803
+ return originalContent;
2804
+ }
2805
+ const appConfig = getActiveAppConfig(input.userContext) ?? {};
2806
+ const link = getEmailLink(input);
2807
+ const callToActionText = emailConfig.callToActionText ?? "Verify your email";
2808
+ if (!link) {
2809
+ return {
2810
+ ...originalContent,
2811
+ subject: emailConfig.subject ?? originalContent.subject
2812
+ };
2813
+ }
2814
+ return {
2815
+ ...originalContent,
2816
+ toEmail: getEmailAddress(input),
2817
+ subject: emailConfig.subject ?? originalContent.subject,
2818
+ isHtml: true,
2819
+ body: renderEmailHtml({
2820
+ appConfig,
2821
+ emailConfig,
2822
+ link,
2823
+ callToActionText,
2824
+ originalBody: originalContent.body
2825
+ })
2826
+ };
2827
+ }
2828
+ function renderEmailHtml(input) {
2829
+ const primaryColor = input.appConfig.branding?.primaryColor ?? DEFAULT_PRIMARY_COLOR;
2830
+ const image = input.emailConfig.image || input.appConfig.branding?.logo || input.appConfig.icon;
2831
+ const appName = input.appConfig.name ?? "Application";
2832
+ const content = input.emailConfig.customContent ?? input.originalBody;
2833
+ return `<!doctype html>
2834
+ <html>
2835
+ <body style="font-family: Arial, sans-serif; color: #1f2937; margin: 0; padding: 32px; background: #f9fafb;">
2836
+ <table role="presentation" width="100%" cellspacing="0" cellpadding="0" style="max-width: 560px; margin: 0 auto; background: #ffffff; border-radius: 16px; padding: 32px;">
2837
+ <tr>
2838
+ <td style="text-align: center;">
2839
+ ${image ? `<img src="${escapeHtml(image)}" alt="${escapeHtml(appName)}" style="max-width: 96px; max-height: 96px; margin-bottom: 24px;" />` : ""}
2840
+ <div style="font-size: 18px; font-weight: 600; margin-bottom: 16px;">${escapeHtml(appName)}</div>
2841
+ <div style="font-size: 15px; line-height: 1.6; margin-bottom: 24px;">${content}</div>
2842
+ <a href="${escapeHtml(input.link)}" style="display: inline-block; background: ${escapeHtml(primaryColor)}; color: #ffffff; text-decoration: none; padding: 12px 20px; border-radius: 10px; font-weight: 600;">${escapeHtml(input.callToActionText)}</a>
2843
+ ${input.emailConfig.customClosingContent ? `<div style="font-size: 14px; line-height: 1.6; margin-top: 24px;">${input.emailConfig.customClosingContent}</div>` : ""}
2844
+ </td>
2845
+ </tr>
2846
+ </table>
2847
+ </body>
2848
+ </html>`;
2849
+ }
2850
+ function escapeHtml(value) {
2851
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
2852
+ }
2853
+ async function getRowndPasswordlessEmailContent(input, originalContent) {
2854
+ return buildRowndEmailContent(input, originalContent, getEmailConfig(input.userContext));
2855
+ }
2856
+ async function getRowndEmailVerificationContent(input, originalContent) {
2857
+ return buildRowndEmailContent(input, originalContent, getEmailConfig(input.userContext));
2858
+ }
2859
+
2715
2860
  // src/index.ts
2716
2861
  var index_default = { init };
2717
2862
  export {
@@ -2727,6 +2872,8 @@ export {
2727
2872
  RowndPluginError,
2728
2873
  index_default as default,
2729
2874
  getRowndClient,
2875
+ getRowndEmailVerificationContent,
2876
+ getRowndPasswordlessEmailContent,
2730
2877
  init,
2731
2878
  setRowndClient
2732
2879
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supertokens-plugins/rownd-nodejs",
3
- "version": "0.3.0-beta.0",
3
+ "version": "0.3.0-beta.1",
4
4
  "description": "Rownd User Migration Plugin for SuperTokens",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",