@scalemule/nextjs 0.0.13 → 0.0.14

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/dist/index.d.mts CHANGED
@@ -26,6 +26,8 @@ interface ScaleMuleContextValue {
26
26
  publishableKey?: string;
27
27
  /** Gateway URL for direct API calls */
28
28
  gatewayUrl?: string;
29
+ /** Configured environment ('dev' or 'prod') */
30
+ environment?: string;
29
31
  /** Server-evaluated flag values to bootstrap the client (eliminates loading flash) */
30
32
  bootstrapFlags?: Record<string, unknown>;
31
33
  }
package/dist/index.d.ts CHANGED
@@ -26,6 +26,8 @@ interface ScaleMuleContextValue {
26
26
  publishableKey?: string;
27
27
  /** Gateway URL for direct API calls */
28
28
  gatewayUrl?: string;
29
+ /** Configured environment ('dev' or 'prod') */
30
+ environment?: string;
29
31
  /** Server-evaluated flag values to bootstrap the client (eliminates loading flash) */
30
32
  bootstrapFlags?: Record<string, unknown>;
31
33
  }
package/dist/index.js CHANGED
@@ -931,6 +931,7 @@ function ScaleMuleProvider({
931
931
  authProxyUrl,
932
932
  publishableKey,
933
933
  gatewayUrl: gatewayUrl || (environment === "dev" ? "https://api-dev.scalemule.com" : "https://api.scalemule.com"),
934
+ environment: environment || void 0,
934
935
  bootstrapFlags
935
936
  }),
936
937
  [client, user, handleSetUser, initializing, error, analyticsProxyUrl, authProxyUrl, publishableKey, gatewayUrl, environment, bootstrapFlags]
@@ -2847,13 +2848,14 @@ function toApiError(error) {
2847
2848
  };
2848
2849
  }
2849
2850
  function useFeatureFlags(options = {}) {
2851
+ const smContext = useScaleMule();
2850
2852
  const {
2851
- environment = "prod",
2853
+ environment = smContext.environment ?? "prod",
2852
2854
  context = {},
2853
2855
  keys,
2854
2856
  enabled = true
2855
2857
  } = options;
2856
- const { client, publishableKey, gatewayUrl, bootstrapFlags } = useScaleMule();
2858
+ const { client, publishableKey, gatewayUrl, bootstrapFlags } = smContext;
2857
2859
  const initialFlags = react.useMemo(() => {
2858
2860
  if (!bootstrapFlags) return {};
2859
2861
  const result = {};
@@ -2910,8 +2912,12 @@ function useFeatureFlags(options = {}) {
2910
2912
  }, [client, enabled, environment, keys, keysKey, publishableKey, gatewayUrl]);
2911
2913
  const bootstrapCoversKeys = react.useMemo(() => {
2912
2914
  if (!hasBootstrap || !keys || keys.length === 0) return false;
2913
- return keys.every((k) => k in initialFlags);
2914
- }, [hasBootstrap, keys, initialFlags]);
2915
+ if (!keys.every((k) => k in initialFlags)) return false;
2916
+ if (environment !== (smContext.environment ?? "prod")) return false;
2917
+ const contextKeys = Object.keys(context).filter((k) => k !== "ip_address");
2918
+ if (contextKeys.length > 0) return false;
2919
+ return true;
2920
+ }, [hasBootstrap, keys, initialFlags, environment, smContext.environment, context]);
2915
2921
  react.useEffect(() => {
2916
2922
  if (!bootstrapCoversKeys) {
2917
2923
  void refresh();
package/dist/index.mjs CHANGED
@@ -929,6 +929,7 @@ function ScaleMuleProvider({
929
929
  authProxyUrl,
930
930
  publishableKey,
931
931
  gatewayUrl: gatewayUrl || (environment === "dev" ? "https://api-dev.scalemule.com" : "https://api.scalemule.com"),
932
+ environment: environment || void 0,
932
933
  bootstrapFlags
933
934
  }),
934
935
  [client, user, handleSetUser, initializing, error, analyticsProxyUrl, authProxyUrl, publishableKey, gatewayUrl, environment, bootstrapFlags]
@@ -2845,13 +2846,14 @@ function toApiError(error) {
2845
2846
  };
2846
2847
  }
2847
2848
  function useFeatureFlags(options = {}) {
2849
+ const smContext = useScaleMule();
2848
2850
  const {
2849
- environment = "prod",
2851
+ environment = smContext.environment ?? "prod",
2850
2852
  context = {},
2851
2853
  keys,
2852
2854
  enabled = true
2853
2855
  } = options;
2854
- const { client, publishableKey, gatewayUrl, bootstrapFlags } = useScaleMule();
2856
+ const { client, publishableKey, gatewayUrl, bootstrapFlags } = smContext;
2855
2857
  const initialFlags = useMemo(() => {
2856
2858
  if (!bootstrapFlags) return {};
2857
2859
  const result = {};
@@ -2908,8 +2910,12 @@ function useFeatureFlags(options = {}) {
2908
2910
  }, [client, enabled, environment, keys, keysKey, publishableKey, gatewayUrl]);
2909
2911
  const bootstrapCoversKeys = useMemo(() => {
2910
2912
  if (!hasBootstrap || !keys || keys.length === 0) return false;
2911
- return keys.every((k) => k in initialFlags);
2912
- }, [hasBootstrap, keys, initialFlags]);
2913
+ if (!keys.every((k) => k in initialFlags)) return false;
2914
+ if (environment !== (smContext.environment ?? "prod")) return false;
2915
+ const contextKeys = Object.keys(context).filter((k) => k !== "ip_address");
2916
+ if (contextKeys.length > 0) return false;
2917
+ return true;
2918
+ }, [hasBootstrap, keys, initialFlags, environment, smContext.environment, context]);
2913
2919
  useEffect(() => {
2914
2920
  if (!bootstrapCoversKeys) {
2915
2921
  void refresh();
@@ -1,5 +1,5 @@
1
- import { S as ServerConfig } from '../webhook-handler-CweF_nT3.mjs';
2
- export { a as ScaleMuleServer, d as VideoFailedEvent, V as VideoReadyEvent, f as VideoTranscodedEvent, e as VideoUploadedEvent, W as WebhookEvent, g as WebhookRoutesConfig, c as createServerClient, h as createWebhookHandler, b as createWebhookRoutes, p as parseWebhookEvent, r as registerVideoWebhook, v as verifyWebhookSignature } from '../webhook-handler-CweF_nT3.mjs';
1
+ import { S as ServerConfig } from '../webhook-handler-CN_N2XlA.mjs';
2
+ export { a as ScaleMuleServer, e as VideoFailedEvent, V as VideoReadyEvent, g as VideoTranscodedEvent, f as VideoUploadedEvent, W as WebhookEvent, h as WebhookRoutesConfig, c as createServerClient, i as createWebhookHandler, d as createWebhookRoutes, p as parseWebhookEvent, b as registerVideoWebhook, r as resolveGatewayUrl, v as verifyWebhookSignature } from '../webhook-handler-CN_N2XlA.mjs';
3
3
  import { $ as ClientContext, L as LoginResponse, A as ApiError } from '../index-jomBa89d.mjs';
4
4
  import { NextRequest, NextResponse } from 'next/server';
5
5
 
@@ -436,13 +436,8 @@ declare function apiHandler(handler: HandlerFn, options?: HandlerOptions): (requ
436
436
  /**
437
437
  * Server-side flag bootstrapping for Next.js layouts.
438
438
  *
439
- * Evaluates feature flags on the server during SSR so the client
440
- * has correct values on first render -- no loading flash, no race condition.
441
- *
442
- * Results are cached in-memory with a configurable TTL (default: 60s) to
443
- * prevent excessive API calls. Since feature flags change infrequently,
444
- * a short cache window is fine and avoids hammering the flags service on
445
- * every page render.
439
+ * Uses @scalemule/sdk FlagClient for local evaluation — ZERO API calls per SSR render.
440
+ * Falls back to legacy evaluateBatch API call if FlagClient init fails.
446
441
  *
447
442
  * @example
448
443
  * ```ts
@@ -462,20 +457,13 @@ declare function apiHandler(handler: HandlerFn, options?: HandlerOptions): (requ
462
457
  /**
463
458
  * Evaluate feature flags server-side for bootstrapping the client provider.
464
459
  *
465
- * Extracts the client IP from request headers (x-forwarded-for, x-real-ip)
466
- * and passes it as ip_address in the evaluation context so IP-based targeting
467
- * rules work correctly.
468
- *
469
- * Results are cached in-memory for `cacheTtlMs` milliseconds (default: 60s).
470
- * Pass `cacheTtlMs: 0` to disable caching.
471
- *
472
- * Returns a Record that can be passed directly to ScaleMuleProvider's
473
- * bootstrapFlags prop.
460
+ * Uses local evaluation via FlagClient (zero network calls per render).
461
+ * Falls back to legacy API if FlagClient is unavailable.
474
462
  *
475
463
  * @param flagKeys - Array of flag keys to evaluate
476
464
  * @param environment - Environment name (default: 'prod')
477
465
  * @param extraContext - Additional context attributes to include
478
- * @param cacheTtlMs - Cache TTL in milliseconds (default: 60000). Set to 0 to disable.
466
+ * @param cacheTtlMs - Deprecated (ignored). FlagClient handles caching internally.
479
467
  */
480
468
  declare function getBootstrapFlags(flagKeys: string[], environment?: string, extraContext?: Record<string, unknown>, cacheTtlMs?: number): Promise<Record<string, unknown>>;
481
469
 
@@ -1,5 +1,5 @@
1
- import { S as ServerConfig } from '../webhook-handler-Bi3bMNty.js';
2
- export { a as ScaleMuleServer, d as VideoFailedEvent, V as VideoReadyEvent, f as VideoTranscodedEvent, e as VideoUploadedEvent, W as WebhookEvent, g as WebhookRoutesConfig, c as createServerClient, h as createWebhookHandler, b as createWebhookRoutes, p as parseWebhookEvent, r as registerVideoWebhook, v as verifyWebhookSignature } from '../webhook-handler-Bi3bMNty.js';
1
+ import { S as ServerConfig } from '../webhook-handler-DrC9rOoh.js';
2
+ export { a as ScaleMuleServer, e as VideoFailedEvent, V as VideoReadyEvent, g as VideoTranscodedEvent, f as VideoUploadedEvent, W as WebhookEvent, h as WebhookRoutesConfig, c as createServerClient, i as createWebhookHandler, d as createWebhookRoutes, p as parseWebhookEvent, b as registerVideoWebhook, r as resolveGatewayUrl, v as verifyWebhookSignature } from '../webhook-handler-DrC9rOoh.js';
3
3
  import { $ as ClientContext, L as LoginResponse, A as ApiError } from '../index-jomBa89d.js';
4
4
  import { NextRequest, NextResponse } from 'next/server';
5
5
 
@@ -436,13 +436,8 @@ declare function apiHandler(handler: HandlerFn, options?: HandlerOptions): (requ
436
436
  /**
437
437
  * Server-side flag bootstrapping for Next.js layouts.
438
438
  *
439
- * Evaluates feature flags on the server during SSR so the client
440
- * has correct values on first render -- no loading flash, no race condition.
441
- *
442
- * Results are cached in-memory with a configurable TTL (default: 60s) to
443
- * prevent excessive API calls. Since feature flags change infrequently,
444
- * a short cache window is fine and avoids hammering the flags service on
445
- * every page render.
439
+ * Uses @scalemule/sdk FlagClient for local evaluation — ZERO API calls per SSR render.
440
+ * Falls back to legacy evaluateBatch API call if FlagClient init fails.
446
441
  *
447
442
  * @example
448
443
  * ```ts
@@ -462,20 +457,13 @@ declare function apiHandler(handler: HandlerFn, options?: HandlerOptions): (requ
462
457
  /**
463
458
  * Evaluate feature flags server-side for bootstrapping the client provider.
464
459
  *
465
- * Extracts the client IP from request headers (x-forwarded-for, x-real-ip)
466
- * and passes it as ip_address in the evaluation context so IP-based targeting
467
- * rules work correctly.
468
- *
469
- * Results are cached in-memory for `cacheTtlMs` milliseconds (default: 60s).
470
- * Pass `cacheTtlMs: 0` to disable caching.
471
- *
472
- * Returns a Record that can be passed directly to ScaleMuleProvider's
473
- * bootstrapFlags prop.
460
+ * Uses local evaluation via FlagClient (zero network calls per render).
461
+ * Falls back to legacy API if FlagClient is unavailable.
474
462
  *
475
463
  * @param flagKeys - Array of flag keys to evaluate
476
464
  * @param environment - Environment name (default: 'prod')
477
465
  * @param extraContext - Additional context attributes to include
478
- * @param cacheTtlMs - Cache TTL in milliseconds (default: 60000). Set to 0 to disable.
466
+ * @param cacheTtlMs - Deprecated (ignored). FlagClient handles caching internally.
479
467
  */
480
468
  declare function getBootstrapFlags(flagKeys: string[], environment?: string, extraContext?: Record<string, unknown>, cacheTtlMs?: number): Promise<Record<string, unknown>>;
481
469
 
@@ -3,6 +3,7 @@
3
3
  var headers = require('next/headers');
4
4
  var server = require('next/server');
5
5
  var crypto$1 = require('crypto');
6
+ var server$1 = require('@scalemule/sdk/flags/server');
6
7
 
7
8
  // src/types/index.ts
8
9
  var ScaleMuleApiError = class extends Error {
@@ -1765,50 +1766,80 @@ function createWebhookHandler(config = {}) {
1765
1766
  }
1766
1767
  };
1767
1768
  }
1769
+ var _clients = /* @__PURE__ */ new Map();
1770
+ var _initPromises = /* @__PURE__ */ new Map();
1768
1771
  var _serverClient = null;
1769
- function getClient() {
1772
+ function getServerClient() {
1770
1773
  if (!_serverClient) {
1771
1774
  _serverClient = createServerClient();
1772
1775
  }
1773
1776
  return _serverClient;
1774
1777
  }
1775
- var _flagCache = /* @__PURE__ */ new Map();
1776
- var DEFAULT_CACHE_TTL_MS = 6e4;
1777
- async function getBootstrapFlags(flagKeys, environment = "prod", extraContext = {}, cacheTtlMs = DEFAULT_CACHE_TTL_MS) {
1778
+ var GATEWAY_URLS2 = {
1779
+ dev: "https://api-dev.scalemule.com",
1780
+ prod: "https://api.scalemule.com"
1781
+ };
1782
+ function resolveGatewayUrl2() {
1783
+ if (process.env.SCALEMULE_API_URL) return process.env.SCALEMULE_API_URL;
1784
+ const env = process.env.SCALEMULE_ENV || "prod";
1785
+ return GATEWAY_URLS2[env] || GATEWAY_URLS2.prod;
1786
+ }
1787
+ async function getFlagClient(environment) {
1788
+ const apiKey = process.env.SCALEMULE_API_KEY;
1789
+ const gatewayUrl = resolveGatewayUrl2();
1790
+ const key = `${environment}:${gatewayUrl}`;
1791
+ const existing = _clients.get(key);
1792
+ if (existing) return existing;
1793
+ const pending = _initPromises.get(key);
1794
+ if (pending) return pending;
1795
+ const promise = (async () => {
1796
+ const client = new server$1.FlagClient({ apiKey, environment, gatewayUrl });
1797
+ await client.init();
1798
+ _clients.set(key, client);
1799
+ return client;
1800
+ })();
1801
+ _initPromises.set(key, promise);
1778
1802
  try {
1779
- const cacheKey = [...flagKeys].sort().join("|") + ":" + environment;
1780
- if (cacheTtlMs > 0) {
1781
- const cached = _flagCache.get(cacheKey);
1782
- if (cached && Date.now() - cached.timestamp < cacheTtlMs) {
1783
- return cached.result;
1784
- }
1785
- }
1803
+ return await promise;
1804
+ } catch (e) {
1805
+ _initPromises.delete(key);
1806
+ throw e;
1807
+ }
1808
+ }
1809
+ var _shutdownRegistered = false;
1810
+ function ensureShutdownHook() {
1811
+ if (_shutdownRegistered) return;
1812
+ _shutdownRegistered = true;
1813
+ process.once("SIGTERM", async () => {
1814
+ const shutdowns = Array.from(_clients.values()).map((c) => c.shutdown());
1815
+ await Promise.allSettled(shutdowns);
1816
+ });
1817
+ }
1818
+ function extractClientIp(hdrs) {
1819
+ const realIp = hdrs.get("x-real-ip") || hdrs.get("x-real-client-ip");
1820
+ const forwardedFor = hdrs.get("x-forwarded-for");
1821
+ return realIp || (forwardedFor ? forwardedFor.split(",")[0].trim() : void 0);
1822
+ }
1823
+ async function getBootstrapFlags(flagKeys, environment = "prod", extraContext = {}, cacheTtlMs = 0) {
1824
+ try {
1825
+ const client = await getFlagClient(environment);
1826
+ ensureShutdownHook();
1786
1827
  const hdrs = await headers.headers();
1787
- const forwardedFor = hdrs.get("x-forwarded-for");
1788
- const realIp = hdrs.get("x-real-ip") || hdrs.get("x-real-client-ip");
1789
- const clientIp = realIp || (forwardedFor ? forwardedFor.split(",")[0].trim() : void 0);
1790
- const context = {
1791
- ...extraContext
1792
- };
1793
- if (clientIp) {
1794
- context.ip_address = clientIp;
1795
- }
1796
- const result = await getClient().flags.evaluateBatch(flagKeys, context, environment);
1797
- const flagResult = result || {};
1798
- if (cacheTtlMs > 0) {
1799
- _flagCache.set(cacheKey, { result: flagResult, timestamp: Date.now() });
1800
- if (_flagCache.size > 100) {
1801
- const now = Date.now();
1802
- for (const [key, entry] of _flagCache) {
1803
- if (now - entry.timestamp > cacheTtlMs) {
1804
- _flagCache.delete(key);
1805
- }
1806
- }
1807
- }
1808
- }
1809
- return flagResult;
1828
+ const clientIp = extractClientIp(hdrs);
1829
+ const context = { ...extraContext };
1830
+ if (clientIp) context.ip_address = clientIp;
1831
+ return client.evaluateBatch(flagKeys, context);
1810
1832
  } catch {
1811
- return {};
1833
+ try {
1834
+ const hdrs = await headers.headers();
1835
+ const clientIp = extractClientIp(hdrs);
1836
+ const context = { ...extraContext };
1837
+ if (clientIp) context.ip_address = clientIp;
1838
+ const result = await getServerClient().flags.evaluateBatch(flagKeys, context, environment);
1839
+ return result || {};
1840
+ } catch {
1841
+ return {};
1842
+ }
1812
1843
  }
1813
1844
  }
1814
1845
  function globToRegex(pattern) {
@@ -1938,14 +1969,14 @@ function clearOAuthState(response) {
1938
1969
  }
1939
1970
 
1940
1971
  // src/server/secrets.ts
1941
- var DEFAULT_CACHE_TTL_MS2 = 5 * 60 * 1e3;
1972
+ var DEFAULT_CACHE_TTL_MS = 5 * 60 * 1e3;
1942
1973
  var secretsCache = {};
1943
1974
  var globalConfig = {};
1944
1975
  function configureSecrets(config) {
1945
1976
  globalConfig = { ...globalConfig, ...config };
1946
1977
  }
1947
1978
  async function getAppSecret(key) {
1948
- const cacheTtl = globalConfig.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS2;
1979
+ const cacheTtl = globalConfig.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS;
1949
1980
  const noCache = globalConfig.noCache ?? false;
1950
1981
  if (!noCache) {
1951
1982
  const cached = secretsCache[key];
@@ -1997,14 +2028,14 @@ async function prefetchSecrets(keys) {
1997
2028
  }
1998
2029
 
1999
2030
  // src/server/bundles.ts
2000
- var DEFAULT_CACHE_TTL_MS3 = 5 * 60 * 1e3;
2031
+ var DEFAULT_CACHE_TTL_MS2 = 5 * 60 * 1e3;
2001
2032
  var bundlesCache = {};
2002
2033
  var globalConfig2 = {};
2003
2034
  function configureBundles(config) {
2004
2035
  globalConfig2 = { ...globalConfig2, ...config };
2005
2036
  }
2006
2037
  async function getBundle(key, resolve = true) {
2007
- const cacheTtl = globalConfig2.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS3;
2038
+ const cacheTtl = globalConfig2.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS2;
2008
2039
  const noCache = globalConfig2.noCache ?? false;
2009
2040
  if (!noCache) {
2010
2041
  const cached = bundlesCache[key];
@@ -2143,6 +2174,7 @@ exports.registerVideoWebhook = registerVideoWebhook;
2143
2174
  exports.requireAppSecret = requireAppSecret;
2144
2175
  exports.requireBundle = requireBundle;
2145
2176
  exports.requireSession = requireSession;
2177
+ exports.resolveGatewayUrl = resolveGatewayUrl;
2146
2178
  exports.setOAuthState = setOAuthState;
2147
2179
  exports.unwrap = unwrap;
2148
2180
  exports.validateCSRFToken = validateCSRFToken;
@@ -1,6 +1,7 @@
1
1
  import { cookies, headers } from 'next/headers';
2
2
  import { NextResponse } from 'next/server';
3
3
  import { createHmac, timingSafeEqual } from 'crypto';
4
+ import { FlagClient } from '@scalemule/sdk/flags/server';
4
5
 
5
6
  // src/types/index.ts
6
7
  var ScaleMuleApiError = class extends Error {
@@ -1763,50 +1764,80 @@ function createWebhookHandler(config = {}) {
1763
1764
  }
1764
1765
  };
1765
1766
  }
1767
+ var _clients = /* @__PURE__ */ new Map();
1768
+ var _initPromises = /* @__PURE__ */ new Map();
1766
1769
  var _serverClient = null;
1767
- function getClient() {
1770
+ function getServerClient() {
1768
1771
  if (!_serverClient) {
1769
1772
  _serverClient = createServerClient();
1770
1773
  }
1771
1774
  return _serverClient;
1772
1775
  }
1773
- var _flagCache = /* @__PURE__ */ new Map();
1774
- var DEFAULT_CACHE_TTL_MS = 6e4;
1775
- async function getBootstrapFlags(flagKeys, environment = "prod", extraContext = {}, cacheTtlMs = DEFAULT_CACHE_TTL_MS) {
1776
+ var GATEWAY_URLS2 = {
1777
+ dev: "https://api-dev.scalemule.com",
1778
+ prod: "https://api.scalemule.com"
1779
+ };
1780
+ function resolveGatewayUrl2() {
1781
+ if (process.env.SCALEMULE_API_URL) return process.env.SCALEMULE_API_URL;
1782
+ const env = process.env.SCALEMULE_ENV || "prod";
1783
+ return GATEWAY_URLS2[env] || GATEWAY_URLS2.prod;
1784
+ }
1785
+ async function getFlagClient(environment) {
1786
+ const apiKey = process.env.SCALEMULE_API_KEY;
1787
+ const gatewayUrl = resolveGatewayUrl2();
1788
+ const key = `${environment}:${gatewayUrl}`;
1789
+ const existing = _clients.get(key);
1790
+ if (existing) return existing;
1791
+ const pending = _initPromises.get(key);
1792
+ if (pending) return pending;
1793
+ const promise = (async () => {
1794
+ const client = new FlagClient({ apiKey, environment, gatewayUrl });
1795
+ await client.init();
1796
+ _clients.set(key, client);
1797
+ return client;
1798
+ })();
1799
+ _initPromises.set(key, promise);
1776
1800
  try {
1777
- const cacheKey = [...flagKeys].sort().join("|") + ":" + environment;
1778
- if (cacheTtlMs > 0) {
1779
- const cached = _flagCache.get(cacheKey);
1780
- if (cached && Date.now() - cached.timestamp < cacheTtlMs) {
1781
- return cached.result;
1782
- }
1783
- }
1801
+ return await promise;
1802
+ } catch (e) {
1803
+ _initPromises.delete(key);
1804
+ throw e;
1805
+ }
1806
+ }
1807
+ var _shutdownRegistered = false;
1808
+ function ensureShutdownHook() {
1809
+ if (_shutdownRegistered) return;
1810
+ _shutdownRegistered = true;
1811
+ process.once("SIGTERM", async () => {
1812
+ const shutdowns = Array.from(_clients.values()).map((c) => c.shutdown());
1813
+ await Promise.allSettled(shutdowns);
1814
+ });
1815
+ }
1816
+ function extractClientIp(hdrs) {
1817
+ const realIp = hdrs.get("x-real-ip") || hdrs.get("x-real-client-ip");
1818
+ const forwardedFor = hdrs.get("x-forwarded-for");
1819
+ return realIp || (forwardedFor ? forwardedFor.split(",")[0].trim() : void 0);
1820
+ }
1821
+ async function getBootstrapFlags(flagKeys, environment = "prod", extraContext = {}, cacheTtlMs = 0) {
1822
+ try {
1823
+ const client = await getFlagClient(environment);
1824
+ ensureShutdownHook();
1784
1825
  const hdrs = await headers();
1785
- const forwardedFor = hdrs.get("x-forwarded-for");
1786
- const realIp = hdrs.get("x-real-ip") || hdrs.get("x-real-client-ip");
1787
- const clientIp = realIp || (forwardedFor ? forwardedFor.split(",")[0].trim() : void 0);
1788
- const context = {
1789
- ...extraContext
1790
- };
1791
- if (clientIp) {
1792
- context.ip_address = clientIp;
1793
- }
1794
- const result = await getClient().flags.evaluateBatch(flagKeys, context, environment);
1795
- const flagResult = result || {};
1796
- if (cacheTtlMs > 0) {
1797
- _flagCache.set(cacheKey, { result: flagResult, timestamp: Date.now() });
1798
- if (_flagCache.size > 100) {
1799
- const now = Date.now();
1800
- for (const [key, entry] of _flagCache) {
1801
- if (now - entry.timestamp > cacheTtlMs) {
1802
- _flagCache.delete(key);
1803
- }
1804
- }
1805
- }
1806
- }
1807
- return flagResult;
1826
+ const clientIp = extractClientIp(hdrs);
1827
+ const context = { ...extraContext };
1828
+ if (clientIp) context.ip_address = clientIp;
1829
+ return client.evaluateBatch(flagKeys, context);
1808
1830
  } catch {
1809
- return {};
1831
+ try {
1832
+ const hdrs = await headers();
1833
+ const clientIp = extractClientIp(hdrs);
1834
+ const context = { ...extraContext };
1835
+ if (clientIp) context.ip_address = clientIp;
1836
+ const result = await getServerClient().flags.evaluateBatch(flagKeys, context, environment);
1837
+ return result || {};
1838
+ } catch {
1839
+ return {};
1840
+ }
1810
1841
  }
1811
1842
  }
1812
1843
  function globToRegex(pattern) {
@@ -1936,14 +1967,14 @@ function clearOAuthState(response) {
1936
1967
  }
1937
1968
 
1938
1969
  // src/server/secrets.ts
1939
- var DEFAULT_CACHE_TTL_MS2 = 5 * 60 * 1e3;
1970
+ var DEFAULT_CACHE_TTL_MS = 5 * 60 * 1e3;
1940
1971
  var secretsCache = {};
1941
1972
  var globalConfig = {};
1942
1973
  function configureSecrets(config) {
1943
1974
  globalConfig = { ...globalConfig, ...config };
1944
1975
  }
1945
1976
  async function getAppSecret(key) {
1946
- const cacheTtl = globalConfig.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS2;
1977
+ const cacheTtl = globalConfig.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS;
1947
1978
  const noCache = globalConfig.noCache ?? false;
1948
1979
  if (!noCache) {
1949
1980
  const cached = secretsCache[key];
@@ -1995,14 +2026,14 @@ async function prefetchSecrets(keys) {
1995
2026
  }
1996
2027
 
1997
2028
  // src/server/bundles.ts
1998
- var DEFAULT_CACHE_TTL_MS3 = 5 * 60 * 1e3;
2029
+ var DEFAULT_CACHE_TTL_MS2 = 5 * 60 * 1e3;
1999
2030
  var bundlesCache = {};
2000
2031
  var globalConfig2 = {};
2001
2032
  function configureBundles(config) {
2002
2033
  globalConfig2 = { ...globalConfig2, ...config };
2003
2034
  }
2004
2035
  async function getBundle(key, resolve = true) {
2005
- const cacheTtl = globalConfig2.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS3;
2036
+ const cacheTtl = globalConfig2.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS2;
2006
2037
  const noCache = globalConfig2.noCache ?? false;
2007
2038
  if (!noCache) {
2008
2039
  const cached = bundlesCache[key];
@@ -2096,4 +2127,4 @@ async function prefetchBundles(keys) {
2096
2127
  await Promise.all(keys.map((key) => getBundle(key)));
2097
2128
  }
2098
2129
 
2099
- export { CSRF_COOKIE_NAME, CSRF_HEADER_NAME, OAUTH_STATE_COOKIE_NAME, SESSION_COOKIE_NAME, ScaleMuleError, ScaleMuleServer, USER_ID_COOKIE_NAME, apiHandler, buildClientContextHeaders, clearOAuthState, clearSession, configureBundles, configureSecrets, createAnalyticsRoutes, createAuthMiddleware, createAuthRoutes, 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, setOAuthState, unwrap, validateCSRFToken, validateCSRFTokenAsync, validateOAuthState, validateOAuthStateAsync, verifyWebhookSignature, withAuth, withCSRFProtection, withCSRFToken, withSession };
2130
+ export { CSRF_COOKIE_NAME, CSRF_HEADER_NAME, OAUTH_STATE_COOKIE_NAME, SESSION_COOKIE_NAME, ScaleMuleError, ScaleMuleServer, USER_ID_COOKIE_NAME, apiHandler, buildClientContextHeaders, clearOAuthState, clearSession, configureBundles, configureSecrets, createAnalyticsRoutes, createAuthMiddleware, createAuthRoutes, 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 };
@@ -1,2 +1,2 @@
1
- export { h as createWebhookHandler } from '../webhook-handler-CweF_nT3.mjs';
1
+ export { i as createWebhookHandler } from '../webhook-handler-CN_N2XlA.mjs';
2
2
  import '../index-jomBa89d.mjs';
@@ -1,2 +1,2 @@
1
- export { h as createWebhookHandler } from '../webhook-handler-Bi3bMNty.js';
1
+ export { i as createWebhookHandler } from '../webhook-handler-DrC9rOoh.js';
2
2
  import '../index-jomBa89d.js';
@@ -18,6 +18,7 @@ interface ServerConfig {
18
18
  /** Enable debug logging */
19
19
  debug?: boolean;
20
20
  }
21
+ declare function resolveGatewayUrl(config: ServerConfig): string;
21
22
  declare class ScaleMuleServer {
22
23
  private apiKey;
23
24
  private gatewayUrl;
@@ -771,4 +772,4 @@ type RouteHandler = (request: Request) => Promise<Response>;
771
772
  */
772
773
  declare function createWebhookHandler(config?: WebhookHandlerConfig): RouteHandler;
773
774
 
774
- export { type ServerConfig as S, type VideoReadyEvent as V, type WebhookEvent as W, ScaleMuleServer as a, createWebhookRoutes as b, createServerClient as c, type VideoFailedEvent as d, type VideoUploadedEvent as e, type VideoTranscodedEvent as f, type WebhookRoutesConfig as g, createWebhookHandler as h, parseWebhookEvent as p, registerVideoWebhook as r, verifyWebhookSignature as v };
775
+ export { type ServerConfig as S, type VideoReadyEvent as V, type WebhookEvent as W, ScaleMuleServer as a, registerVideoWebhook as b, createServerClient as c, createWebhookRoutes as d, type VideoFailedEvent as e, type VideoUploadedEvent as f, type VideoTranscodedEvent as g, type WebhookRoutesConfig as h, createWebhookHandler as i, parseWebhookEvent as p, resolveGatewayUrl as r, verifyWebhookSignature as v };
@@ -18,6 +18,7 @@ interface ServerConfig {
18
18
  /** Enable debug logging */
19
19
  debug?: boolean;
20
20
  }
21
+ declare function resolveGatewayUrl(config: ServerConfig): string;
21
22
  declare class ScaleMuleServer {
22
23
  private apiKey;
23
24
  private gatewayUrl;
@@ -771,4 +772,4 @@ type RouteHandler = (request: Request) => Promise<Response>;
771
772
  */
772
773
  declare function createWebhookHandler(config?: WebhookHandlerConfig): RouteHandler;
773
774
 
774
- export { type ServerConfig as S, type VideoReadyEvent as V, type WebhookEvent as W, ScaleMuleServer as a, createWebhookRoutes as b, createServerClient as c, type VideoFailedEvent as d, type VideoUploadedEvent as e, type VideoTranscodedEvent as f, type WebhookRoutesConfig as g, createWebhookHandler as h, parseWebhookEvent as p, registerVideoWebhook as r, verifyWebhookSignature as v };
775
+ export { type ServerConfig as S, type VideoReadyEvent as V, type WebhookEvent as W, ScaleMuleServer as a, registerVideoWebhook as b, createServerClient as c, createWebhookRoutes as d, type VideoFailedEvent as e, type VideoUploadedEvent as f, type VideoTranscodedEvent as g, type WebhookRoutesConfig as h, createWebhookHandler as i, parseWebhookEvent as p, resolveGatewayUrl as r, verifyWebhookSignature as v };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scalemule/nextjs",
3
- "version": "0.0.13",
3
+ "version": "0.0.14",
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",
@@ -51,6 +51,9 @@
51
51
  "lint": "eslint src --ext .ts,.tsx",
52
52
  "clean": "rm -rf dist"
53
53
  },
54
+ "dependencies": {
55
+ "@scalemule/sdk": "^0.0.14"
56
+ },
54
57
  "peerDependencies": {
55
58
  "next": ">=14.0.0",
56
59
  "react": ">=18.0.0",