@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 +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +10 -4
- package/dist/index.mjs +10 -4
- package/dist/server/index.d.mts +7 -19
- package/dist/server/index.d.ts +7 -19
- package/dist/server/index.js +71 -39
- package/dist/server/index.mjs +71 -40
- package/dist/server/webhook-handler.d.mts +1 -1
- package/dist/server/webhook-handler.d.ts +1 -1
- package/dist/{webhook-handler-CweF_nT3.d.mts → webhook-handler-CN_N2XlA.d.mts} +2 -1
- package/dist/{webhook-handler-Bi3bMNty.d.ts → webhook-handler-DrC9rOoh.d.ts} +2 -1
- package/package.json +4 -1
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 } =
|
|
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
|
-
|
|
2914
|
-
|
|
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 } =
|
|
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
|
-
|
|
2912
|
-
|
|
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();
|
package/dist/server/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { S as ServerConfig } from '../webhook-handler-
|
|
2
|
-
export { a as ScaleMuleServer,
|
|
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
|
-
*
|
|
440
|
-
*
|
|
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
|
-
*
|
|
466
|
-
*
|
|
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 -
|
|
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
|
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { S as ServerConfig } from '../webhook-handler-
|
|
2
|
-
export { a as ScaleMuleServer,
|
|
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
|
-
*
|
|
440
|
-
*
|
|
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
|
-
*
|
|
466
|
-
*
|
|
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 -
|
|
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
|
|
package/dist/server/index.js
CHANGED
|
@@ -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
|
|
1772
|
+
function getServerClient() {
|
|
1770
1773
|
if (!_serverClient) {
|
|
1771
1774
|
_serverClient = createServerClient();
|
|
1772
1775
|
}
|
|
1773
1776
|
return _serverClient;
|
|
1774
1777
|
}
|
|
1775
|
-
var
|
|
1776
|
-
|
|
1777
|
-
|
|
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
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
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
|
|
1788
|
-
const
|
|
1789
|
-
|
|
1790
|
-
|
|
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
|
-
|
|
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
|
|
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 ??
|
|
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
|
|
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 ??
|
|
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;
|
package/dist/server/index.mjs
CHANGED
|
@@ -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
|
|
1770
|
+
function getServerClient() {
|
|
1768
1771
|
if (!_serverClient) {
|
|
1769
1772
|
_serverClient = createServerClient();
|
|
1770
1773
|
}
|
|
1771
1774
|
return _serverClient;
|
|
1772
1775
|
}
|
|
1773
|
-
var
|
|
1774
|
-
|
|
1775
|
-
|
|
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
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
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
|
|
1786
|
-
const
|
|
1787
|
-
|
|
1788
|
-
|
|
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
|
-
|
|
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
|
|
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 ??
|
|
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
|
|
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 ??
|
|
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 {
|
|
1
|
+
export { i as createWebhookHandler } from '../webhook-handler-CN_N2XlA.mjs';
|
|
2
2
|
import '../index-jomBa89d.mjs';
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export {
|
|
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,
|
|
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,
|
|
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.
|
|
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",
|