@scalemule/nextjs 0.0.39 → 0.0.41

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.
@@ -1,3 +1,4 @@
1
+ import { createMoneyClient } from '@scalemule/money';
1
2
  import { cookies, headers } from 'next/headers';
2
3
  import { NextResponse } from 'next/server';
3
4
  import { createHmac, timingSafeEqual, createHash } from 'crypto';
@@ -2665,6 +2666,14 @@ var ScaleMuleServer = class {
2665
2666
  this.apiKey = config.apiKey;
2666
2667
  this.gatewayUrl = resolveGatewayUrl(config);
2667
2668
  this.debug = config.debug || false;
2669
+ this.money = createMoneyClient({
2670
+ apiKey: this.apiKey,
2671
+ gatewayUrl: this.gatewayUrl,
2672
+ fetch: globalThis.fetch.bind(globalThis)
2673
+ });
2674
+ }
2675
+ moneyWithSession(sessionToken) {
2676
+ return this.money.withAccessToken(sessionToken);
2668
2677
  }
2669
2678
  /**
2670
2679
  * Make a request to the ScaleMule API
@@ -2743,6 +2752,7 @@ function createServerClient(config) {
2743
2752
  }
2744
2753
  var SESSION_COOKIE_NAME = "sm_session";
2745
2754
  var USER_ID_COOKIE_NAME = "sm_user_id";
2755
+ var KNOWN_ACCOUNTS_COOKIE_NAME = "sm_known_accounts";
2746
2756
  ({
2747
2757
  secure: process.env.NODE_ENV === "production"});
2748
2758
  function createCookieHeader(name, value, options = {}) {
@@ -2843,6 +2853,163 @@ function getSessionFromRequest(request) {
2843
2853
  expiresAt: /* @__PURE__ */ new Date()
2844
2854
  };
2845
2855
  }
2856
+ var MAX_KNOWN_ACCOUNTS = 10;
2857
+ function maskEmail(email) {
2858
+ const [local, domain] = email.split("@");
2859
+ if (!domain) return "***@***.***";
2860
+ const tldDot = domain.lastIndexOf(".");
2861
+ const tld = tldDot > 0 ? domain.slice(tldDot) : "";
2862
+ const domainBase = tldDot > 0 ? domain.slice(0, tldDot) : domain;
2863
+ return `${local[0] || "*"}***@${domainBase[0] || "*"}***${tld}`;
2864
+ }
2865
+ function stableColorIndex(userId) {
2866
+ let hash = 0;
2867
+ for (let i = 0; i < userId.length; i++) {
2868
+ hash = (hash << 5) - hash + userId.charCodeAt(i) | 0;
2869
+ }
2870
+ return Math.abs(hash) % 8;
2871
+ }
2872
+ function applyPrivacyToEntry(entry, privacy) {
2873
+ switch (privacy) {
2874
+ case "full":
2875
+ return entry;
2876
+ case "masked":
2877
+ return {
2878
+ userId: entry.userId,
2879
+ email: entry.email ? maskEmail(entry.email) : void 0,
2880
+ fullName: entry.fullName ? `${entry.fullName[0].toUpperCase()}.` : void 0,
2881
+ provider: entry.provider,
2882
+ lastActiveAt: entry.lastActiveAt,
2883
+ colorIndex: stableColorIndex(entry.userId)
2884
+ };
2885
+ case "minimal":
2886
+ return {
2887
+ userId: entry.userId,
2888
+ provider: entry.provider,
2889
+ lastActiveAt: entry.lastActiveAt,
2890
+ displayLabel: "Account",
2891
+ colorIndex: stableColorIndex(entry.userId)
2892
+ };
2893
+ }
2894
+ }
2895
+ function appendKnownAccountCookie(headers2, account, existingCookie, options = {}, privacy) {
2896
+ let accounts = {};
2897
+ if (existingCookie) {
2898
+ try {
2899
+ accounts = JSON.parse(decodeURIComponent(existingCookie));
2900
+ } catch {
2901
+ }
2902
+ }
2903
+ const effectivePrivacy = privacy || "full";
2904
+ for (const [userId, entry] of Object.entries(accounts)) {
2905
+ accounts[userId] = applyPrivacyToEntry(entry, effectivePrivacy);
2906
+ }
2907
+ accounts[account.userId] = applyPrivacyToEntry(account, effectivePrivacy);
2908
+ const entries = Object.entries(accounts);
2909
+ if (entries.length > MAX_KNOWN_ACCOUNTS) {
2910
+ entries.sort((a, b) => new Date(b[1].lastActiveAt).getTime() - new Date(a[1].lastActiveAt).getTime());
2911
+ accounts = Object.fromEntries(entries.slice(0, MAX_KNOWN_ACCOUNTS));
2912
+ }
2913
+ const maxAge = 365 * 24 * 60 * 60;
2914
+ const secure = options.secure ?? process.env.NODE_ENV === "production";
2915
+ const sameSite = options.sameSite ?? "lax";
2916
+ const path = options.path ?? "/";
2917
+ let cookie = `${KNOWN_ACCOUNTS_COOKIE_NAME}=${encodeURIComponent(JSON.stringify(accounts))}; Path=${path}; Max-Age=${maxAge}; SameSite=${sameSite}`;
2918
+ if (secure) {
2919
+ cookie += "; Secure";
2920
+ }
2921
+ if (options.domain) {
2922
+ cookie += `; Domain=${options.domain}`;
2923
+ }
2924
+ headers2.append("Set-Cookie", cookie);
2925
+ }
2926
+ function removeKnownAccountFromCookie(headers2, userId, existingCookie, options = {}) {
2927
+ let accounts = {};
2928
+ if (existingCookie) {
2929
+ try {
2930
+ accounts = JSON.parse(decodeURIComponent(existingCookie));
2931
+ } catch {
2932
+ }
2933
+ }
2934
+ delete accounts[userId];
2935
+ const maxAge = 365 * 24 * 60 * 60;
2936
+ const secure = options.secure ?? process.env.NODE_ENV === "production";
2937
+ const sameSite = options.sameSite ?? "lax";
2938
+ const path = options.path ?? "/";
2939
+ let cookie = `${KNOWN_ACCOUNTS_COOKIE_NAME}=${encodeURIComponent(JSON.stringify(accounts))}; Path=${path}; Max-Age=${maxAge}; SameSite=${sameSite}`;
2940
+ if (secure) {
2941
+ cookie += "; Secure";
2942
+ }
2943
+ if (options.domain) {
2944
+ cookie += `; Domain=${options.domain}`;
2945
+ }
2946
+ headers2.append("Set-Cookie", cookie);
2947
+ }
2948
+ function clearKnownAccountsCookie(headers2, options = {}) {
2949
+ const path = options.path ?? "/";
2950
+ let cookie = `${KNOWN_ACCOUNTS_COOKIE_NAME}=; Path=${path}; Max-Age=0`;
2951
+ if (options.domain) {
2952
+ cookie += `; Domain=${options.domain}`;
2953
+ }
2954
+ headers2.append("Set-Cookie", cookie);
2955
+ }
2956
+ function getKnownAccountsFromRequest(request) {
2957
+ const cookieHeader = request.headers.get("cookie");
2958
+ if (!cookieHeader) return [];
2959
+ const cookies4 = Object.fromEntries(
2960
+ cookieHeader.split(";").map((c) => {
2961
+ const [key, ...rest] = c.trim().split("=");
2962
+ return [key, decodeURIComponent(rest.join("="))];
2963
+ })
2964
+ );
2965
+ const raw = cookies4[KNOWN_ACCOUNTS_COOKIE_NAME];
2966
+ if (!raw) return [];
2967
+ try {
2968
+ const accounts = JSON.parse(raw);
2969
+ return Object.values(accounts);
2970
+ } catch {
2971
+ return [];
2972
+ }
2973
+ }
2974
+ function getKnownAccountsCookieRaw(request) {
2975
+ const cookieHeader = request.headers.get("cookie");
2976
+ if (!cookieHeader) return null;
2977
+ const cookies4 = Object.fromEntries(
2978
+ cookieHeader.split(";").map((c) => {
2979
+ const [key, ...rest] = c.trim().split("=");
2980
+ return [key, decodeURIComponent(rest.join("="))];
2981
+ })
2982
+ );
2983
+ return cookies4[KNOWN_ACCOUNTS_COOKIE_NAME] || null;
2984
+ }
2985
+ function normalizeKnownAccountsCookie(request, privacy, options = {}) {
2986
+ if (!privacy || privacy === "full") return null;
2987
+ const raw = getKnownAccountsCookieRaw(request);
2988
+ if (!raw) return null;
2989
+ let accounts = {};
2990
+ try {
2991
+ accounts = JSON.parse(raw);
2992
+ } catch {
2993
+ return null;
2994
+ }
2995
+ let changed = false;
2996
+ for (const [userId, entry] of Object.entries(accounts)) {
2997
+ const normalized = applyPrivacyToEntry(entry, privacy);
2998
+ if (JSON.stringify(normalized) !== JSON.stringify(entry)) {
2999
+ accounts[userId] = normalized;
3000
+ changed = true;
3001
+ }
3002
+ }
3003
+ if (!changed) return null;
3004
+ const maxAge = 365 * 24 * 60 * 60;
3005
+ const secure = options.secure ?? process.env.NODE_ENV === "production";
3006
+ const sameSite = options.sameSite ?? "lax";
3007
+ const path = options.path ?? "/";
3008
+ let cookie = `${KNOWN_ACCOUNTS_COOKIE_NAME}=${encodeURIComponent(JSON.stringify(accounts))}; Path=${path}; Max-Age=${maxAge}; SameSite=${sameSite}`;
3009
+ if (secure) cookie += "; Secure";
3010
+ if (options.domain) cookie += `; Domain=${options.domain}`;
3011
+ return cookie;
3012
+ }
2846
3013
  async function requireSession() {
2847
3014
  const session = await getSession();
2848
3015
  if (!session) {
@@ -3002,7 +3169,25 @@ function createAuthRoutes(config = {}) {
3002
3169
  } catch {
3003
3170
  return successResponse({ user: registeredUser, message: "Registration successful" }, 201);
3004
3171
  }
3005
- return withSession(loginData, { user: registeredUser, sessionToken: loginData.session_token, userId: registeredUser.id }, cookieOptions);
3172
+ const registerResponse = withSession(loginData, { user: registeredUser, sessionToken: loginData.session_token, userId: registeredUser.id }, cookieOptions);
3173
+ if (config.enableAccountSwitcher) {
3174
+ const existingKnown = getKnownAccountsCookieRaw(request);
3175
+ appendKnownAccountCookie(
3176
+ registerResponse.headers,
3177
+ {
3178
+ userId: registeredUser.id,
3179
+ email: registeredUser.email,
3180
+ fullName: registeredUser.full_name ?? void 0,
3181
+ avatarUrl: registeredUser.avatar_url ?? void 0,
3182
+ provider: "email",
3183
+ lastActiveAt: (/* @__PURE__ */ new Date()).toISOString()
3184
+ },
3185
+ existingKnown,
3186
+ cookieOptions,
3187
+ config.accountSwitcherPrivacy
3188
+ );
3189
+ }
3190
+ return registerResponse;
3006
3191
  }
3007
3192
  // ==================== Login ====================
3008
3193
  case "login": {
@@ -3033,7 +3218,25 @@ function createAuthRoutes(config = {}) {
3033
3218
  email: loginData.user.email
3034
3219
  });
3035
3220
  }
3036
- return withSession(loginData, { user: loginData.user, sessionToken: loginData.session_token, userId: loginData.user.id }, cookieOptions);
3221
+ const loginResponse = withSession(loginData, { user: loginData.user, sessionToken: loginData.session_token, userId: loginData.user.id }, cookieOptions);
3222
+ if (config.enableAccountSwitcher) {
3223
+ const existingKnown = getKnownAccountsCookieRaw(request);
3224
+ appendKnownAccountCookie(
3225
+ loginResponse.headers,
3226
+ {
3227
+ userId: loginData.user.id,
3228
+ email: loginData.user.email,
3229
+ fullName: loginData.user.full_name ?? void 0,
3230
+ avatarUrl: loginData.user.avatar_url ?? void 0,
3231
+ provider: "email",
3232
+ lastActiveAt: (/* @__PURE__ */ new Date()).toISOString()
3233
+ },
3234
+ existingKnown,
3235
+ cookieOptions,
3236
+ config.accountSwitcherPrivacy
3237
+ );
3238
+ }
3239
+ return loginResponse;
3037
3240
  }
3038
3241
  // ==================== Logout ====================
3039
3242
  case "logout": {
@@ -3180,6 +3383,59 @@ function createAuthRoutes(config = {}) {
3180
3383
  }
3181
3384
  return successResponse({ message: "Password changed successfully" });
3182
3385
  }
3386
+ // ==================== Switch Account ====================
3387
+ // Clears the active session so the user can re-authenticate as a different account.
3388
+ // The known accounts cookie is preserved — only the session cookie is cleared.
3389
+ case "switch-account": {
3390
+ if (!config.enableAccountSwitcher) {
3391
+ return errorResponse("NOT_FOUND", "Account switcher not enabled", 404);
3392
+ }
3393
+ const session = await getSession();
3394
+ if (session) {
3395
+ try {
3396
+ await sm.auth.logout(session.sessionToken);
3397
+ } catch {
3398
+ }
3399
+ }
3400
+ const switchResponse = clearSession({ message: "Session cleared for account switch" }, cookieOptions);
3401
+ const knownAccounts = getKnownAccountsFromRequest(request);
3402
+ return new Response(
3403
+ JSON.stringify({ success: true, data: { message: "Session cleared for account switch", knownAccounts } }),
3404
+ { status: 200, headers: switchResponse.headers }
3405
+ );
3406
+ }
3407
+ // ==================== Forget Account ====================
3408
+ // Remove a specific account from the known accounts cookie.
3409
+ case "forget-account": {
3410
+ if (!config.enableAccountSwitcher) {
3411
+ return errorResponse("NOT_FOUND", "Account switcher not enabled", 404);
3412
+ }
3413
+ const { user_id } = body;
3414
+ if (!user_id) {
3415
+ return errorResponse("VALIDATION_ERROR", "user_id required", 400);
3416
+ }
3417
+ const headers2 = new Headers();
3418
+ headers2.set("Content-Type", "application/json");
3419
+ const existingKnown = getKnownAccountsCookieRaw(request);
3420
+ removeKnownAccountFromCookie(headers2, user_id, existingKnown, cookieOptions);
3421
+ return new Response(
3422
+ JSON.stringify({ success: true, data: { message: "Account forgotten" } }),
3423
+ { status: 200, headers: headers2 }
3424
+ );
3425
+ }
3426
+ // ==================== Forget All Accounts ====================
3427
+ case "forget-all-accounts": {
3428
+ if (!config.enableAccountSwitcher) {
3429
+ return errorResponse("NOT_FOUND", "Account switcher not enabled", 404);
3430
+ }
3431
+ const headers2 = new Headers();
3432
+ headers2.set("Content-Type", "application/json");
3433
+ clearKnownAccountsCookie(headers2, cookieOptions);
3434
+ return new Response(
3435
+ JSON.stringify({ success: true, data: { message: "All accounts forgotten" } }),
3436
+ { status: 200, headers: headers2 }
3437
+ );
3438
+ }
3183
3439
  default:
3184
3440
  return errorResponse("NOT_FOUND", `Unknown endpoint: ${path}`, 404);
3185
3441
  }
@@ -3195,20 +3451,25 @@ function createAuthRoutes(config = {}) {
3195
3451
  switch (path) {
3196
3452
  // ==================== Get Current User ====================
3197
3453
  case "me": {
3454
+ const normCookie = config.enableAccountSwitcher ? normalizeKnownAccountsCookie(request, config.accountSwitcherPrivacy, cookieOptions) : null;
3455
+ const withNorm = (resp) => {
3456
+ if (normCookie) resp.headers.append("Set-Cookie", normCookie);
3457
+ return resp;
3458
+ };
3198
3459
  const session = await getSession();
3199
3460
  if (!session) {
3200
- return errorResponse("UNAUTHORIZED", "Authentication required", 401);
3461
+ return withNorm(errorResponse("UNAUTHORIZED", "Authentication required", 401));
3201
3462
  }
3202
3463
  let userData;
3203
3464
  try {
3204
3465
  userData = await sm.auth.me(session.sessionToken);
3205
3466
  } catch {
3206
- return clearSession(
3467
+ return withNorm(clearSession(
3207
3468
  { error: { code: "SESSION_EXPIRED", message: "Session expired" } },
3208
3469
  cookieOptions
3209
- );
3470
+ ));
3210
3471
  }
3211
- return successResponse({ user: userData, sessionToken: session.sessionToken, userId: session.userId });
3472
+ return withNorm(successResponse({ user: userData, sessionToken: session.sessionToken, userId: session.userId }));
3212
3473
  }
3213
3474
  // ==================== Get Session Status ====================
3214
3475
  case "session": {
@@ -3218,6 +3479,14 @@ function createAuthRoutes(config = {}) {
3218
3479
  userId: session?.userId || null
3219
3480
  });
3220
3481
  }
3482
+ // ==================== Get Known Accounts ====================
3483
+ case "known-accounts": {
3484
+ if (!config.enableAccountSwitcher) {
3485
+ return errorResponse("NOT_FOUND", "Account switcher not enabled", 404);
3486
+ }
3487
+ const knownAccounts = getKnownAccountsFromRequest(request);
3488
+ return successResponse({ knownAccounts });
3489
+ }
3221
3490
  default:
3222
3491
  return errorResponse("NOT_FOUND", `Unknown endpoint: ${path}`, 404);
3223
3492
  }
@@ -4825,4 +5094,121 @@ async function prefetchBundles(keys) {
4825
5094
  await Promise.all(keys.map((key) => getBundle(key)));
4826
5095
  }
4827
5096
 
4828
- export { CSRF_COOKIE_NAME, CSRF_HEADER_NAME, OAUTH_STATE_COOKIE_NAME, SESSION_COOKIE_NAME, ScaleMuleError, ScaleMuleServer, USER_ID_COOKIE_NAME, apiHandler, buildClientContextHeaders, buildFlagContext, clearOAuthState, clearSession, configureBundles, configureSecrets, createAnalyticsRoutes, createAuthMiddleware, createAuthRoutes, createNotificationRoutes, createPushRoutes, createServerClient, createWebhookHandler, createWebhookRoutes, errorCodeToStatus, extractClientContext, extractClientContextFromReq, generateCSRFToken, getAppSecret, getAppSecretOrDefault, getBootstrapFlags, getBundle, getCSRFToken, getMySqlBundle, getOAuthBundle, getPostgresBundle, getRedisBundle, getS3Bundle, getSession, getSessionFromRequest, getSmtpBundle, invalidateBundleCache, invalidateSecretCache, parseWebhookEvent, prefetchBundles, prefetchSecrets, registerVideoWebhook, requireAppSecret, requireBundle, requireSession, resolveGatewayUrl, setOAuthState, unwrap, validateCSRFToken, validateCSRFTokenAsync, validateOAuthState, validateOAuthStateAsync, verifyWebhookSignature, withAuth, withCSRFProtection, withCSRFToken, withSession };
5097
+ // src/server/redirect.ts
5098
+ function normalizeOrigin(origin) {
5099
+ try {
5100
+ const u = new URL(origin);
5101
+ return `${u.protocol}//${u.host}`.toLowerCase();
5102
+ } catch {
5103
+ return null;
5104
+ }
5105
+ }
5106
+ function isSafeSchemeRelative(input) {
5107
+ return !(input.startsWith("//") || input.startsWith("\\"));
5108
+ }
5109
+ function validateSafeRedirect(input, opts = {}) {
5110
+ const defaultPath = opts.defaultPath ?? "/";
5111
+ if (!input || typeof input !== "string") return defaultPath;
5112
+ const trimmed = input.trim();
5113
+ if (!trimmed) return defaultPath;
5114
+ if (!isSafeSchemeRelative(trimmed)) return defaultPath;
5115
+ const colon = trimmed.indexOf(":");
5116
+ if (colon > 0 && colon < 15) {
5117
+ const scheme = trimmed.slice(0, colon).toLowerCase();
5118
+ if (/^[a-z][a-z0-9+.-]*$/.test(scheme) && scheme !== "http" && scheme !== "https") {
5119
+ return defaultPath;
5120
+ }
5121
+ }
5122
+ if (trimmed.startsWith("/")) {
5123
+ if (trimmed.length > 1 && trimmed[1] === "\\") return defaultPath;
5124
+ return trimmed;
5125
+ }
5126
+ const allowed = (opts.allowedOrigins ?? []).map(normalizeOrigin).filter((o) => o !== null);
5127
+ try {
5128
+ const parsed = new URL(trimmed);
5129
+ const origin = `${parsed.protocol}//${parsed.host}`.toLowerCase();
5130
+ if (!allowed.includes(origin)) return defaultPath;
5131
+ if (opts.stripSameOriginHost !== false) {
5132
+ return `${parsed.pathname}${parsed.search}${parsed.hash}` || "/";
5133
+ }
5134
+ return trimmed;
5135
+ } catch {
5136
+ return defaultPath;
5137
+ }
5138
+ }
5139
+ function isSafeRedirect(input, opts = {}) {
5140
+ if (!input || typeof input !== "string") return false;
5141
+ const defaultPath = `__unsafe_sentinel_${Math.random()}`;
5142
+ const resolved = validateSafeRedirect(input, { ...opts, defaultPath });
5143
+ return resolved !== defaultPath;
5144
+ }
5145
+
5146
+ // src/server/security-headers.ts
5147
+ var DEFAULT_PERMISSIONS_POLICY = {
5148
+ // Conference track features — allow self-origin only.
5149
+ camera: "self",
5150
+ microphone: "self",
5151
+ "display-capture": "self",
5152
+ // Deny-by-default for features the SDK never uses.
5153
+ geolocation: "()",
5154
+ payment: "()",
5155
+ "publickey-credentials-get": "self",
5156
+ usb: "()",
5157
+ serial: "()",
5158
+ bluetooth: "()"
5159
+ };
5160
+ function formatPermissionsDirective(feature, value) {
5161
+ if (value === "*") return `${feature}=*`;
5162
+ if (value === "()" || Array.isArray(value) && value.length === 0) {
5163
+ return `${feature}=()`;
5164
+ }
5165
+ if (value === "self") return `${feature}=(self)`;
5166
+ const origins = value;
5167
+ const parts = origins.map((o) => o === "self" ? "self" : `"${o}"`);
5168
+ return `${feature}=(${parts.join(" ")})`;
5169
+ }
5170
+ function buildSecurityHeaders(opts = {}) {
5171
+ const headers2 = [];
5172
+ const includeHsts = opts.includeHsts ?? true;
5173
+ const hstsMaxAge = opts.hstsMaxAgeSeconds ?? 31536e3;
5174
+ if (includeHsts && hstsMaxAge > 0) {
5175
+ const parts = [`max-age=${hstsMaxAge}`];
5176
+ if (opts.hstsIncludeSubDomains ?? true) parts.push("includeSubDomains");
5177
+ if (opts.hstsPreload ?? false) parts.push("preload");
5178
+ headers2.push({
5179
+ key: "Strict-Transport-Security",
5180
+ value: parts.join("; ")
5181
+ });
5182
+ }
5183
+ const frameOptions = opts.frameOptions === void 0 ? "DENY" : opts.frameOptions;
5184
+ if (frameOptions !== null) {
5185
+ headers2.push({ key: "X-Frame-Options", value: frameOptions });
5186
+ }
5187
+ const contentTypeOptions = opts.contentTypeOptions === void 0 ? "nosniff" : opts.contentTypeOptions;
5188
+ if (contentTypeOptions !== null) {
5189
+ headers2.push({ key: "X-Content-Type-Options", value: contentTypeOptions });
5190
+ }
5191
+ headers2.push({
5192
+ key: "Referrer-Policy",
5193
+ value: opts.referrerPolicy ?? "strict-origin-when-cross-origin"
5194
+ });
5195
+ const xss = opts.xssProtection === void 0 ? "0" : opts.xssProtection;
5196
+ if (xss !== null) {
5197
+ headers2.push({ key: "X-XSS-Protection", value: xss });
5198
+ }
5199
+ const permissions = opts.permissionsPolicy === void 0 ? DEFAULT_PERMISSIONS_POLICY : opts.permissionsPolicy;
5200
+ if (permissions !== null) {
5201
+ const directives = Object.entries(permissions).map(([feature, value]) => formatPermissionsDirective(feature, value)).join(", ");
5202
+ headers2.push({ key: "Permissions-Policy", value: directives });
5203
+ }
5204
+ if (opts.extraHeaders?.length) {
5205
+ const byKey = new Map(headers2.map((h) => [h.key.toLowerCase(), h]));
5206
+ for (const extra of opts.extraHeaders) {
5207
+ byKey.set(extra.key.toLowerCase(), extra);
5208
+ }
5209
+ return Array.from(byKey.values());
5210
+ }
5211
+ return headers2;
5212
+ }
5213
+
5214
+ export { CSRF_COOKIE_NAME, CSRF_HEADER_NAME, DEFAULT_PERMISSIONS_POLICY, KNOWN_ACCOUNTS_COOKIE_NAME, OAUTH_STATE_COOKIE_NAME, SESSION_COOKIE_NAME, ScaleMuleError, ScaleMuleServer, USER_ID_COOKIE_NAME, apiHandler, appendKnownAccountCookie, buildClientContextHeaders, buildFlagContext, buildSecurityHeaders, clearKnownAccountsCookie, clearOAuthState, clearSession, configureBundles, configureSecrets, createAnalyticsRoutes, createAuthMiddleware, createAuthRoutes, createNotificationRoutes, createPushRoutes, createServerClient, createWebhookHandler, createWebhookRoutes, errorCodeToStatus, extractClientContext, extractClientContextFromReq, generateCSRFToken, getAppSecret, getAppSecretOrDefault, getBootstrapFlags, getBundle, getCSRFToken, getKnownAccountsCookieRaw, getKnownAccountsFromRequest, getMySqlBundle, getOAuthBundle, getPostgresBundle, getRedisBundle, getS3Bundle, getSession, getSessionFromRequest, getSmtpBundle, invalidateBundleCache, invalidateSecretCache, isSafeRedirect, normalizeKnownAccountsCookie, parseWebhookEvent, prefetchBundles, prefetchSecrets, registerVideoWebhook, removeKnownAccountFromCookie, requireAppSecret, requireBundle, requireSession, resolveGatewayUrl, setOAuthState, unwrap, validateCSRFToken, validateCSRFTokenAsync, validateOAuthState, validateOAuthStateAsync, validateSafeRedirect, verifyWebhookSignature, withAuth, withCSRFProtection, withCSRFToken, withSession };
@@ -1,2 +1,3 @@
1
- export { g as createWebhookHandler } from '../webhook-handler-Cz9jtet2.mjs';
2
- import '../index-BoENfro3.mjs';
1
+ export { g as createWebhookHandler } from '../webhook-handler-BqzCYRNJ.mjs';
2
+ import '@scalemule/money';
3
+ import '../index-BIIUrnPr.mjs';
@@ -1,2 +1,3 @@
1
- export { g as createWebhookHandler } from '../webhook-handler-CjSSCdvI.js';
2
- import '../index-BoENfro3.js';
1
+ export { g as createWebhookHandler } from '../webhook-handler-BjFbqZuq.js';
2
+ import '@scalemule/money';
3
+ import '../index-BIIUrnPr.js';
@@ -1,6 +1,9 @@
1
1
  'use strict';
2
2
 
3
3
  var crypto = require('crypto');
4
+ require('@scalemule/money');
5
+
6
+ // src/server/webhooks.ts
4
7
 
5
8
  // src/server/webhooks.ts
6
9
  function verifyWebhookSignature(payload, signature, secret) {
@@ -1,4 +1,7 @@
1
1
  import { createHmac, timingSafeEqual } from 'crypto';
2
+ import '@scalemule/money';
3
+
4
+ // src/server/webhooks.ts
2
5
 
3
6
  // src/server/webhooks.ts
4
7
  function verifyWebhookSignature(payload, signature, secret) {
@@ -1,6 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ReactNode } from 'react';
3
- import { U as User, A as ApiError, a8 as StorageFile } from './index-BoENfro3.mjs';
3
+ import { U as User, A as ApiError, a9 as StorageFile } from './index-BIIUrnPr.mjs';
4
4
 
5
5
  interface MockUserOptions {
6
6
  id?: string;
package/dist/testing.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ReactNode } from 'react';
3
- import { U as User, A as ApiError, a8 as StorageFile } from './index-BoENfro3.js';
3
+ import { U as User, A as ApiError, a9 as StorageFile } from './index-BIIUrnPr.js';
4
4
 
5
5
  interface MockUserOptions {
6
6
  id?: string;
@@ -1,4 +1,5 @@
1
- import { _ as RegisterRequest, p as ClientContext, U as User, v as LoginRequest, L as LoginResponse, c as ListFilesParams, t as ListFilesResponse, a8 as StorageFile, ae as UploadResponse } from './index-BoENfro3.js';
1
+ import { MoneyClient } from '@scalemule/money';
2
+ import { $ as RegisterRequest, p as ClientContext, U as User, v as LoginRequest, L as LoginResponse, c as ListFilesParams, t as ListFilesResponse, a9 as StorageFile, af as UploadResponse } from './index-BIIUrnPr.js';
2
3
 
3
4
  /**
4
5
  * Server-Side ScaleMule Client
@@ -23,7 +24,9 @@ declare class ScaleMuleServer {
23
24
  private apiKey;
24
25
  private gatewayUrl;
25
26
  private debug;
27
+ money: MoneyClient;
26
28
  constructor(config: ServerConfig);
29
+ moneyWithSession(sessionToken: string): MoneyClient;
27
30
  /**
28
31
  * Make a request to the ScaleMule API
29
32
  *
@@ -1,4 +1,5 @@
1
- import { _ as RegisterRequest, p as ClientContext, U as User, v as LoginRequest, L as LoginResponse, c as ListFilesParams, t as ListFilesResponse, a8 as StorageFile, ae as UploadResponse } from './index-BoENfro3.mjs';
1
+ import { MoneyClient } from '@scalemule/money';
2
+ import { $ as RegisterRequest, p as ClientContext, U as User, v as LoginRequest, L as LoginResponse, c as ListFilesParams, t as ListFilesResponse, a9 as StorageFile, af as UploadResponse } from './index-BIIUrnPr.mjs';
2
3
 
3
4
  /**
4
5
  * Server-Side ScaleMule Client
@@ -23,7 +24,9 @@ declare class ScaleMuleServer {
23
24
  private apiKey;
24
25
  private gatewayUrl;
25
26
  private debug;
27
+ money: MoneyClient;
26
28
  constructor(config: ServerConfig);
29
+ moneyWithSession(sessionToken: string): MoneyClient;
27
30
  /**
28
31
  * Make a request to the ScaleMule API
29
32
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scalemule/nextjs",
3
- "version": "0.0.39",
3
+ "version": "0.0.41",
4
4
  "description": "ScaleMule SDK for Next.js applications - authentication, storage, and user management",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -52,7 +52,8 @@
52
52
  "clean": "rm -rf dist"
53
53
  },
54
54
  "dependencies": {
55
- "@scalemule/sdk": "^0.0.31"
55
+ "@scalemule/money": "^0.0.1",
56
+ "@scalemule/sdk": "^0.0.29"
56
57
  },
57
58
  "peerDependencies": {
58
59
  "next": ">=14.0.0",