@feelflow/ffid-sdk 1.7.3 → 1.9.0

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
@@ -17,6 +17,10 @@ yarn add @feelflow/ffid-sdk
17
17
  pnpm add @feelflow/ffid-sdk
18
18
  ```
19
19
 
20
+ ## 統合ガイド
21
+
22
+ サービスを FFID Platform に統合する詳細なガイドは **[Integration Guide](./docs/INTEGRATION_GUIDE.md)** を参照してください。OAuth フロー、フロントエンド/バックエンド実装パターン、セキュリティチェックリスト、アンチパターン集を網羅しています。
23
+
20
24
  ## クイックスタート
21
25
 
22
26
  ### 1. プロバイダーを設定(5行で完了!)
@@ -263,9 +267,10 @@ token mode では SDK は `/api/v1/oauth/userinfo` を呼び出し、基本プ
263
267
 
264
268
  ```ts
265
269
  interface FFIDOAuthUserInfoSubscription {
270
+ subscriptionId: string | null
266
271
  status: 'trialing' | 'active' | 'past_due' | 'canceled' | 'paused' | null
267
272
  planCode: string | null
268
- seatModel: 'organization' | 'individual' | null
273
+ seatModel: 'organization' | null
269
274
  memberRole: 'owner' | 'admin' | 'member' | 'viewer' | null
270
275
  organizationId: string | null
271
276
  }
@@ -238,8 +238,11 @@ function createJwtVerifier(deps) {
238
238
 
239
239
  // src/client/verify-access-token.ts
240
240
  var OAUTH_INTROSPECT_ENDPOINT = "/api/v1/oauth/introspect";
241
+ var CACHE_KEY_PREFIX = "ffid:introspect:";
242
+ var HEX_BASE = 16;
243
+ var HEX_BYTE_WIDTH = 2;
241
244
  function createVerifyAccessToken(deps) {
242
- const { authMode, baseUrl, serviceCode, serviceApiKey, verifyStrategy, logger, createError, errorCodes } = deps;
245
+ const { authMode, baseUrl, serviceCode, serviceApiKey, verifyStrategy, logger, createError, errorCodes, cache, timeout } = deps;
243
246
  let jwtVerify2 = null;
244
247
  function getJwtVerifier() {
245
248
  if (!jwtVerify2) {
@@ -286,17 +289,33 @@ function createVerifyAccessToken(deps) {
286
289
  }
287
290
  const url = `${baseUrl}${OAUTH_INTROSPECT_ENDPOINT}`;
288
291
  logger.debug("Verifying access token:", url);
292
+ const cacheKey = await buildCacheKey(accessToken);
293
+ if (cache) {
294
+ try {
295
+ const cached = await cache.adapter.get(cacheKey);
296
+ if (cached && typeof cached === "object" && "sub" in cached) {
297
+ logger.debug("Cache hit for introspect result");
298
+ return { data: cached };
299
+ }
300
+ } catch (cacheError) {
301
+ logger.warn("Cache read failed, falling back to API call:", cacheError);
302
+ }
303
+ }
304
+ const fetchOptions = {
305
+ method: "POST",
306
+ credentials: "omit",
307
+ headers: {
308
+ "Content-Type": "application/x-www-form-urlencoded",
309
+ "X-Service-Api-Key": serviceApiKey
310
+ },
311
+ body: new URLSearchParams({ token: accessToken }).toString()
312
+ };
313
+ if (timeout !== void 0) {
314
+ fetchOptions.signal = AbortSignal.timeout(timeout);
315
+ }
289
316
  let response;
290
317
  try {
291
- response = await fetch(url, {
292
- method: "POST",
293
- credentials: "omit",
294
- headers: {
295
- "Content-Type": "application/x-www-form-urlencoded",
296
- "X-Service-Api-Key": serviceApiKey
297
- },
298
- body: new URLSearchParams({ token: accessToken }).toString()
299
- });
318
+ response = await fetch(url, fetchOptions);
300
319
  } catch (error) {
301
320
  logger.error("Network error during token verification:", error);
302
321
  return {
@@ -362,7 +381,26 @@ function createVerifyAccessToken(deps) {
362
381
  organization_id: introspectResponse.subscription.organization_id
363
382
  }
364
383
  } : base;
365
- return { data: normalizeUserinfo(raw) };
384
+ const userinfo = normalizeUserinfo(raw);
385
+ if (cache) {
386
+ try {
387
+ await cache.adapter.set(cacheKey, userinfo, cache.ttl);
388
+ } catch (cacheError) {
389
+ logger.warn("Cache write failed (result still returned):", cacheError);
390
+ }
391
+ }
392
+ return { data: userinfo };
393
+ }
394
+ async function buildCacheKey(token) {
395
+ if (typeof globalThis.crypto?.subtle?.digest === "function") {
396
+ const encoder = new TextEncoder();
397
+ const data = encoder.encode(token);
398
+ const hashBuffer = await crypto.subtle.digest("SHA-256", data);
399
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
400
+ const hashHex = hashArray.map((b) => b.toString(HEX_BASE).padStart(HEX_BYTE_WIDTH, "0")).join("");
401
+ return CACHE_KEY_PREFIX + hashHex;
402
+ }
403
+ return CACHE_KEY_PREFIX + token;
366
404
  }
367
405
  return verifyAccessToken;
368
406
  }
@@ -414,6 +452,43 @@ function createBillingMethods(deps) {
414
452
  return { createCheckoutSession, createPortalSession };
415
453
  }
416
454
 
455
+ // src/client/version-check.ts
456
+ var SDK_VERSION = "1.9.0";
457
+ var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
458
+ var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
459
+ var LATEST_VERSION_HEADER = "X-FFID-SDK-Latest-Version";
460
+ var SEMVER_PATTERN = /^\d+(\.\d+)*$/;
461
+ var _versionWarningShown = false;
462
+ function compareSemver(a, b) {
463
+ const cleanA = a.replace(/^v/, "");
464
+ const cleanB = b.replace(/^v/, "");
465
+ if (!SEMVER_PATTERN.test(cleanA) || !SEMVER_PATTERN.test(cleanB)) return 0;
466
+ const partsA = cleanA.split(".").map(Number);
467
+ const partsB = cleanB.split(".").map(Number);
468
+ const maxLen = Math.max(partsA.length, partsB.length);
469
+ for (let i = 0; i < maxLen; i++) {
470
+ const numA = partsA[i] ?? 0;
471
+ const numB = partsB[i] ?? 0;
472
+ if (numA < numB) return -1;
473
+ if (numA > numB) return 1;
474
+ }
475
+ return 0;
476
+ }
477
+ function checkVersionHeader(response, logger) {
478
+ if (_versionWarningShown) return;
479
+ if (typeof process !== "undefined" && process.env?.NODE_ENV === "production") return;
480
+ if (!response.headers?.get) return;
481
+ const latestVersion = response.headers.get(LATEST_VERSION_HEADER);
482
+ if (!latestVersion) return;
483
+ if (compareSemver(SDK_VERSION, latestVersion) < 0) {
484
+ _versionWarningShown = true;
485
+ logger.warn(
486
+ `\u65B0\u3057\u3044\u30D0\u30FC\u30B8\u30E7\u30F3\u304C\u5229\u7528\u53EF\u80FD\u3067\u3059: v${latestVersion} (\u73FE\u5728: v${SDK_VERSION})
487
+ npm install @feelflow/ffid-sdk@latest \u3067\u30A2\u30C3\u30D7\u30C7\u30FC\u30C8\u3067\u304D\u307E\u3059`
488
+ );
489
+ }
490
+ }
491
+
417
492
  // src/client/ffid-client.ts
418
493
  var NO_CONTENT_STATUS = 204;
419
494
  var SESSION_ENDPOINT = "/api/v1/auth/session";
@@ -427,7 +502,7 @@ var SDK_LOG_PREFIX = "[FFID SDK]";
427
502
  var MS_PER_SECOND = 1e3;
428
503
  var UNAUTHORIZED_STATUS = 401;
429
504
  var STATE_RANDOM_BYTES = 16;
430
- var HEX_BASE = 16;
505
+ var HEX_BASE2 = 16;
431
506
  var noopLogger = {
432
507
  debug: () => {
433
508
  },
@@ -462,9 +537,17 @@ function createFFIDClient(config) {
462
537
  const resolvedRedirectUri = config.redirectUri ?? null;
463
538
  const serviceApiKey = config.serviceApiKey?.trim();
464
539
  const verifyStrategy = config.verifyStrategy ?? "jwt";
540
+ const cache = config.cache;
541
+ const timeout = config.timeout;
465
542
  if (authMode === "service-key" && !serviceApiKey) {
466
543
  throw new Error("FFID Client: service-key \u30E2\u30FC\u30C9\u3067\u306F serviceApiKey \u304C\u5FC5\u9808\u3067\u3059");
467
544
  }
545
+ if (cache && cache.ttl <= 0) {
546
+ throw new Error("FFID Client: cache.ttl \u306F\u6B63\u306E\u6570\u5024\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044");
547
+ }
548
+ if (timeout !== void 0 && timeout <= 0) {
549
+ throw new Error("FFID Client: timeout \u306F\u6B63\u306E\u6570\u5024\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044");
550
+ }
468
551
  const logger = config.logger ?? (config.debug ? consoleLogger : noopLogger);
469
552
  const tokenStore = authMode === "token" ? createTokenStore() : createTokenStore("memory");
470
553
  async function fetchWithAuth(endpoint, options = {}) {
@@ -514,6 +597,7 @@ function createFFIDClient(config) {
514
597
  };
515
598
  }
516
599
  logger.debug("Response:", response.status, raw);
600
+ checkVersionHeader(response, logger);
517
601
  if (!response.ok) {
518
602
  return {
519
603
  error: raw.error ?? {
@@ -532,6 +616,12 @@ function createFFIDClient(config) {
532
616
  }
533
617
  return { data: raw.data };
534
618
  }
619
+ function sdkHeaders() {
620
+ return {
621
+ "User-Agent": SDK_USER_AGENT,
622
+ [SDK_VERSION_HEADER]: SDK_VERSION
623
+ };
624
+ }
535
625
  function buildFetchOptions(options) {
536
626
  if (authMode === "service-key") {
537
627
  return {
@@ -539,6 +629,7 @@ function createFFIDClient(config) {
539
629
  credentials: "omit",
540
630
  headers: {
541
631
  "Content-Type": "application/json",
632
+ ...sdkHeaders(),
542
633
  "X-Service-Api-Key": serviceApiKey,
543
634
  ...options.headers
544
635
  }
@@ -548,6 +639,7 @@ function createFFIDClient(config) {
548
639
  const tokens = tokenStore.getTokens();
549
640
  const headers = {
550
641
  "Content-Type": "application/json",
642
+ ...sdkHeaders(),
551
643
  ...options.headers
552
644
  };
553
645
  if (tokens) {
@@ -564,6 +656,7 @@ function createFFIDClient(config) {
564
656
  credentials: "include",
565
657
  headers: {
566
658
  "Content-Type": "application/json",
659
+ ...sdkHeaders(),
567
660
  ...options.headers
568
661
  }
569
662
  };
@@ -609,7 +702,8 @@ function createFFIDClient(config) {
609
702
  credentials: "omit",
610
703
  headers: {
611
704
  "Authorization": `Bearer ${tokens.accessToken}`,
612
- "Content-Type": "application/json"
705
+ "Content-Type": "application/json",
706
+ ...sdkHeaders()
613
707
  }
614
708
  });
615
709
  } catch (error) {
@@ -631,7 +725,8 @@ function createFFIDClient(config) {
631
725
  credentials: "omit",
632
726
  headers: {
633
727
  "Authorization": `Bearer ${retryTokens.accessToken}`,
634
- "Content-Type": "application/json"
728
+ "Content-Type": "application/json",
729
+ ...sdkHeaders()
635
730
  }
636
731
  });
637
732
  } catch (retryError) {
@@ -701,7 +796,7 @@ function createFFIDClient(config) {
701
796
  response = await fetch(url, {
702
797
  method: "POST",
703
798
  credentials: "include",
704
- headers: { "Content-Type": "application/json" }
799
+ headers: { "Content-Type": "application/json", ...sdkHeaders() }
705
800
  });
706
801
  } catch (error) {
707
802
  logger.error("Network error:", error);
@@ -750,7 +845,7 @@ function createFFIDClient(config) {
750
845
  await fetch(url, {
751
846
  method: "POST",
752
847
  credentials: "omit",
753
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
848
+ headers: { "Content-Type": "application/x-www-form-urlencoded", ...sdkHeaders() },
754
849
  body: new URLSearchParams({
755
850
  token: tokens.accessToken,
756
851
  client_id: clientId
@@ -789,7 +884,7 @@ function createFFIDClient(config) {
789
884
  response = await fetch(url, {
790
885
  method: "POST",
791
886
  credentials: "omit",
792
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
887
+ headers: { "Content-Type": "application/x-www-form-urlencoded", ...sdkHeaders() },
793
888
  body: new URLSearchParams(body).toString()
794
889
  });
795
890
  } catch (error) {
@@ -847,7 +942,7 @@ function createFFIDClient(config) {
847
942
  response = await fetch(url, {
848
943
  method: "POST",
849
944
  credentials: "omit",
850
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
945
+ headers: { "Content-Type": "application/x-www-form-urlencoded", ...sdkHeaders() },
851
946
  body: new URLSearchParams({
852
947
  grant_type: "refresh_token",
853
948
  refresh_token: tokens.refreshToken,
@@ -972,7 +1067,9 @@ function createFFIDClient(config) {
972
1067
  verifyStrategy,
973
1068
  logger,
974
1069
  createError,
975
- errorCodes: FFID_ERROR_CODES
1070
+ errorCodes: FFID_ERROR_CODES,
1071
+ cache,
1072
+ timeout
976
1073
  });
977
1074
  return {
978
1075
  getSession,
@@ -1002,7 +1099,7 @@ function createFFIDClient(config) {
1002
1099
  function generateRandomState() {
1003
1100
  const array = new Uint8Array(STATE_RANDOM_BYTES);
1004
1101
  crypto.getRandomValues(array);
1005
- return Array.from(array, (byte) => byte.toString(HEX_BASE).padStart(2, "0")).join("");
1102
+ return Array.from(array, (byte) => byte.toString(HEX_BASE2).padStart(2, "0")).join("");
1006
1103
  }
1007
1104
  var DEFAULT_REFRESH_INTERVAL_MS = 5 * 60 * 1e3;
1008
1105
  var TOKEN_REFRESH_RATIO = 0.8;
@@ -236,8 +236,11 @@ function createJwtVerifier(deps) {
236
236
 
237
237
  // src/client/verify-access-token.ts
238
238
  var OAUTH_INTROSPECT_ENDPOINT = "/api/v1/oauth/introspect";
239
+ var CACHE_KEY_PREFIX = "ffid:introspect:";
240
+ var HEX_BASE = 16;
241
+ var HEX_BYTE_WIDTH = 2;
239
242
  function createVerifyAccessToken(deps) {
240
- const { authMode, baseUrl, serviceCode, serviceApiKey, verifyStrategy, logger, createError, errorCodes } = deps;
243
+ const { authMode, baseUrl, serviceCode, serviceApiKey, verifyStrategy, logger, createError, errorCodes, cache, timeout } = deps;
241
244
  let jwtVerify2 = null;
242
245
  function getJwtVerifier() {
243
246
  if (!jwtVerify2) {
@@ -284,17 +287,33 @@ function createVerifyAccessToken(deps) {
284
287
  }
285
288
  const url = `${baseUrl}${OAUTH_INTROSPECT_ENDPOINT}`;
286
289
  logger.debug("Verifying access token:", url);
290
+ const cacheKey = await buildCacheKey(accessToken);
291
+ if (cache) {
292
+ try {
293
+ const cached = await cache.adapter.get(cacheKey);
294
+ if (cached && typeof cached === "object" && "sub" in cached) {
295
+ logger.debug("Cache hit for introspect result");
296
+ return { data: cached };
297
+ }
298
+ } catch (cacheError) {
299
+ logger.warn("Cache read failed, falling back to API call:", cacheError);
300
+ }
301
+ }
302
+ const fetchOptions = {
303
+ method: "POST",
304
+ credentials: "omit",
305
+ headers: {
306
+ "Content-Type": "application/x-www-form-urlencoded",
307
+ "X-Service-Api-Key": serviceApiKey
308
+ },
309
+ body: new URLSearchParams({ token: accessToken }).toString()
310
+ };
311
+ if (timeout !== void 0) {
312
+ fetchOptions.signal = AbortSignal.timeout(timeout);
313
+ }
287
314
  let response;
288
315
  try {
289
- response = await fetch(url, {
290
- method: "POST",
291
- credentials: "omit",
292
- headers: {
293
- "Content-Type": "application/x-www-form-urlencoded",
294
- "X-Service-Api-Key": serviceApiKey
295
- },
296
- body: new URLSearchParams({ token: accessToken }).toString()
297
- });
316
+ response = await fetch(url, fetchOptions);
298
317
  } catch (error) {
299
318
  logger.error("Network error during token verification:", error);
300
319
  return {
@@ -360,7 +379,26 @@ function createVerifyAccessToken(deps) {
360
379
  organization_id: introspectResponse.subscription.organization_id
361
380
  }
362
381
  } : base;
363
- return { data: normalizeUserinfo(raw) };
382
+ const userinfo = normalizeUserinfo(raw);
383
+ if (cache) {
384
+ try {
385
+ await cache.adapter.set(cacheKey, userinfo, cache.ttl);
386
+ } catch (cacheError) {
387
+ logger.warn("Cache write failed (result still returned):", cacheError);
388
+ }
389
+ }
390
+ return { data: userinfo };
391
+ }
392
+ async function buildCacheKey(token) {
393
+ if (typeof globalThis.crypto?.subtle?.digest === "function") {
394
+ const encoder = new TextEncoder();
395
+ const data = encoder.encode(token);
396
+ const hashBuffer = await crypto.subtle.digest("SHA-256", data);
397
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
398
+ const hashHex = hashArray.map((b) => b.toString(HEX_BASE).padStart(HEX_BYTE_WIDTH, "0")).join("");
399
+ return CACHE_KEY_PREFIX + hashHex;
400
+ }
401
+ return CACHE_KEY_PREFIX + token;
364
402
  }
365
403
  return verifyAccessToken;
366
404
  }
@@ -412,6 +450,43 @@ function createBillingMethods(deps) {
412
450
  return { createCheckoutSession, createPortalSession };
413
451
  }
414
452
 
453
+ // src/client/version-check.ts
454
+ var SDK_VERSION = "1.9.0";
455
+ var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
456
+ var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
457
+ var LATEST_VERSION_HEADER = "X-FFID-SDK-Latest-Version";
458
+ var SEMVER_PATTERN = /^\d+(\.\d+)*$/;
459
+ var _versionWarningShown = false;
460
+ function compareSemver(a, b) {
461
+ const cleanA = a.replace(/^v/, "");
462
+ const cleanB = b.replace(/^v/, "");
463
+ if (!SEMVER_PATTERN.test(cleanA) || !SEMVER_PATTERN.test(cleanB)) return 0;
464
+ const partsA = cleanA.split(".").map(Number);
465
+ const partsB = cleanB.split(".").map(Number);
466
+ const maxLen = Math.max(partsA.length, partsB.length);
467
+ for (let i = 0; i < maxLen; i++) {
468
+ const numA = partsA[i] ?? 0;
469
+ const numB = partsB[i] ?? 0;
470
+ if (numA < numB) return -1;
471
+ if (numA > numB) return 1;
472
+ }
473
+ return 0;
474
+ }
475
+ function checkVersionHeader(response, logger) {
476
+ if (_versionWarningShown) return;
477
+ if (typeof process !== "undefined" && process.env?.NODE_ENV === "production") return;
478
+ if (!response.headers?.get) return;
479
+ const latestVersion = response.headers.get(LATEST_VERSION_HEADER);
480
+ if (!latestVersion) return;
481
+ if (compareSemver(SDK_VERSION, latestVersion) < 0) {
482
+ _versionWarningShown = true;
483
+ logger.warn(
484
+ `\u65B0\u3057\u3044\u30D0\u30FC\u30B8\u30E7\u30F3\u304C\u5229\u7528\u53EF\u80FD\u3067\u3059: v${latestVersion} (\u73FE\u5728: v${SDK_VERSION})
485
+ npm install @feelflow/ffid-sdk@latest \u3067\u30A2\u30C3\u30D7\u30C7\u30FC\u30C8\u3067\u304D\u307E\u3059`
486
+ );
487
+ }
488
+ }
489
+
415
490
  // src/client/ffid-client.ts
416
491
  var NO_CONTENT_STATUS = 204;
417
492
  var SESSION_ENDPOINT = "/api/v1/auth/session";
@@ -425,7 +500,7 @@ var SDK_LOG_PREFIX = "[FFID SDK]";
425
500
  var MS_PER_SECOND = 1e3;
426
501
  var UNAUTHORIZED_STATUS = 401;
427
502
  var STATE_RANDOM_BYTES = 16;
428
- var HEX_BASE = 16;
503
+ var HEX_BASE2 = 16;
429
504
  var noopLogger = {
430
505
  debug: () => {
431
506
  },
@@ -460,9 +535,17 @@ function createFFIDClient(config) {
460
535
  const resolvedRedirectUri = config.redirectUri ?? null;
461
536
  const serviceApiKey = config.serviceApiKey?.trim();
462
537
  const verifyStrategy = config.verifyStrategy ?? "jwt";
538
+ const cache = config.cache;
539
+ const timeout = config.timeout;
463
540
  if (authMode === "service-key" && !serviceApiKey) {
464
541
  throw new Error("FFID Client: service-key \u30E2\u30FC\u30C9\u3067\u306F serviceApiKey \u304C\u5FC5\u9808\u3067\u3059");
465
542
  }
543
+ if (cache && cache.ttl <= 0) {
544
+ throw new Error("FFID Client: cache.ttl \u306F\u6B63\u306E\u6570\u5024\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044");
545
+ }
546
+ if (timeout !== void 0 && timeout <= 0) {
547
+ throw new Error("FFID Client: timeout \u306F\u6B63\u306E\u6570\u5024\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044");
548
+ }
466
549
  const logger = config.logger ?? (config.debug ? consoleLogger : noopLogger);
467
550
  const tokenStore = authMode === "token" ? createTokenStore() : createTokenStore("memory");
468
551
  async function fetchWithAuth(endpoint, options = {}) {
@@ -512,6 +595,7 @@ function createFFIDClient(config) {
512
595
  };
513
596
  }
514
597
  logger.debug("Response:", response.status, raw);
598
+ checkVersionHeader(response, logger);
515
599
  if (!response.ok) {
516
600
  return {
517
601
  error: raw.error ?? {
@@ -530,6 +614,12 @@ function createFFIDClient(config) {
530
614
  }
531
615
  return { data: raw.data };
532
616
  }
617
+ function sdkHeaders() {
618
+ return {
619
+ "User-Agent": SDK_USER_AGENT,
620
+ [SDK_VERSION_HEADER]: SDK_VERSION
621
+ };
622
+ }
533
623
  function buildFetchOptions(options) {
534
624
  if (authMode === "service-key") {
535
625
  return {
@@ -537,6 +627,7 @@ function createFFIDClient(config) {
537
627
  credentials: "omit",
538
628
  headers: {
539
629
  "Content-Type": "application/json",
630
+ ...sdkHeaders(),
540
631
  "X-Service-Api-Key": serviceApiKey,
541
632
  ...options.headers
542
633
  }
@@ -546,6 +637,7 @@ function createFFIDClient(config) {
546
637
  const tokens = tokenStore.getTokens();
547
638
  const headers = {
548
639
  "Content-Type": "application/json",
640
+ ...sdkHeaders(),
549
641
  ...options.headers
550
642
  };
551
643
  if (tokens) {
@@ -562,6 +654,7 @@ function createFFIDClient(config) {
562
654
  credentials: "include",
563
655
  headers: {
564
656
  "Content-Type": "application/json",
657
+ ...sdkHeaders(),
565
658
  ...options.headers
566
659
  }
567
660
  };
@@ -607,7 +700,8 @@ function createFFIDClient(config) {
607
700
  credentials: "omit",
608
701
  headers: {
609
702
  "Authorization": `Bearer ${tokens.accessToken}`,
610
- "Content-Type": "application/json"
703
+ "Content-Type": "application/json",
704
+ ...sdkHeaders()
611
705
  }
612
706
  });
613
707
  } catch (error) {
@@ -629,7 +723,8 @@ function createFFIDClient(config) {
629
723
  credentials: "omit",
630
724
  headers: {
631
725
  "Authorization": `Bearer ${retryTokens.accessToken}`,
632
- "Content-Type": "application/json"
726
+ "Content-Type": "application/json",
727
+ ...sdkHeaders()
633
728
  }
634
729
  });
635
730
  } catch (retryError) {
@@ -699,7 +794,7 @@ function createFFIDClient(config) {
699
794
  response = await fetch(url, {
700
795
  method: "POST",
701
796
  credentials: "include",
702
- headers: { "Content-Type": "application/json" }
797
+ headers: { "Content-Type": "application/json", ...sdkHeaders() }
703
798
  });
704
799
  } catch (error) {
705
800
  logger.error("Network error:", error);
@@ -748,7 +843,7 @@ function createFFIDClient(config) {
748
843
  await fetch(url, {
749
844
  method: "POST",
750
845
  credentials: "omit",
751
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
846
+ headers: { "Content-Type": "application/x-www-form-urlencoded", ...sdkHeaders() },
752
847
  body: new URLSearchParams({
753
848
  token: tokens.accessToken,
754
849
  client_id: clientId
@@ -787,7 +882,7 @@ function createFFIDClient(config) {
787
882
  response = await fetch(url, {
788
883
  method: "POST",
789
884
  credentials: "omit",
790
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
885
+ headers: { "Content-Type": "application/x-www-form-urlencoded", ...sdkHeaders() },
791
886
  body: new URLSearchParams(body).toString()
792
887
  });
793
888
  } catch (error) {
@@ -845,7 +940,7 @@ function createFFIDClient(config) {
845
940
  response = await fetch(url, {
846
941
  method: "POST",
847
942
  credentials: "omit",
848
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
943
+ headers: { "Content-Type": "application/x-www-form-urlencoded", ...sdkHeaders() },
849
944
  body: new URLSearchParams({
850
945
  grant_type: "refresh_token",
851
946
  refresh_token: tokens.refreshToken,
@@ -970,7 +1065,9 @@ function createFFIDClient(config) {
970
1065
  verifyStrategy,
971
1066
  logger,
972
1067
  createError,
973
- errorCodes: FFID_ERROR_CODES
1068
+ errorCodes: FFID_ERROR_CODES,
1069
+ cache,
1070
+ timeout
974
1071
  });
975
1072
  return {
976
1073
  getSession,
@@ -1000,7 +1097,7 @@ function createFFIDClient(config) {
1000
1097
  function generateRandomState() {
1001
1098
  const array = new Uint8Array(STATE_RANDOM_BYTES);
1002
1099
  crypto.getRandomValues(array);
1003
- return Array.from(array, (byte) => byte.toString(HEX_BASE).padStart(2, "0")).join("");
1100
+ return Array.from(array, (byte) => byte.toString(HEX_BASE2).padStart(2, "0")).join("");
1004
1101
  }
1005
1102
  var DEFAULT_REFRESH_INTERVAL_MS = 5 * 60 * 1e3;
1006
1103
  var TOKEN_REFRESH_RATIO = 0.8;
@@ -1,30 +1,30 @@
1
1
  'use strict';
2
2
 
3
- var chunkLLOXOE37_cjs = require('../chunk-LLOXOE37.cjs');
3
+ var chunkCBYBTBKA_cjs = require('../chunk-CBYBTBKA.cjs');
4
4
 
5
5
 
6
6
 
7
7
  Object.defineProperty(exports, "FFIDAnnouncementBadge", {
8
8
  enumerable: true,
9
- get: function () { return chunkLLOXOE37_cjs.FFIDAnnouncementBadge; }
9
+ get: function () { return chunkCBYBTBKA_cjs.FFIDAnnouncementBadge; }
10
10
  });
11
11
  Object.defineProperty(exports, "FFIDAnnouncementList", {
12
12
  enumerable: true,
13
- get: function () { return chunkLLOXOE37_cjs.FFIDAnnouncementList; }
13
+ get: function () { return chunkCBYBTBKA_cjs.FFIDAnnouncementList; }
14
14
  });
15
15
  Object.defineProperty(exports, "FFIDLoginButton", {
16
16
  enumerable: true,
17
- get: function () { return chunkLLOXOE37_cjs.FFIDLoginButton; }
17
+ get: function () { return chunkCBYBTBKA_cjs.FFIDLoginButton; }
18
18
  });
19
19
  Object.defineProperty(exports, "FFIDOrganizationSwitcher", {
20
20
  enumerable: true,
21
- get: function () { return chunkLLOXOE37_cjs.FFIDOrganizationSwitcher; }
21
+ get: function () { return chunkCBYBTBKA_cjs.FFIDOrganizationSwitcher; }
22
22
  });
23
23
  Object.defineProperty(exports, "FFIDSubscriptionBadge", {
24
24
  enumerable: true,
25
- get: function () { return chunkLLOXOE37_cjs.FFIDSubscriptionBadge; }
25
+ get: function () { return chunkCBYBTBKA_cjs.FFIDSubscriptionBadge; }
26
26
  });
27
27
  Object.defineProperty(exports, "FFIDUserMenu", {
28
28
  enumerable: true,
29
- get: function () { return chunkLLOXOE37_cjs.FFIDUserMenu; }
29
+ get: function () { return chunkCBYBTBKA_cjs.FFIDUserMenu; }
30
30
  });
@@ -1,3 +1,3 @@
1
- export { u as FFIDAnnouncementBadge, Q as FFIDAnnouncementBadgeClassNames, R as FFIDAnnouncementBadgeProps, v as FFIDAnnouncementList, S as FFIDAnnouncementListClassNames, T as FFIDAnnouncementListProps, C as FFIDLoginButton, V as FFIDLoginButtonProps, H as FFIDOrganizationSwitcher, W as FFIDOrganizationSwitcherClassNames, X as FFIDOrganizationSwitcherProps, J as FFIDSubscriptionBadge, Y as FFIDSubscriptionBadgeClassNames, Z as FFIDSubscriptionBadgeProps, N as FFIDUserMenu, _ as FFIDUserMenuClassNames, $ as FFIDUserMenuProps } from '../index-ea8WAh39.cjs';
1
+ export { v as FFIDAnnouncementBadge, S as FFIDAnnouncementBadgeClassNames, T as FFIDAnnouncementBadgeProps, w as FFIDAnnouncementList, V as FFIDAnnouncementListClassNames, W as FFIDAnnouncementListProps, E as FFIDLoginButton, X as FFIDLoginButtonProps, J as FFIDOrganizationSwitcher, Y as FFIDOrganizationSwitcherClassNames, Z as FFIDOrganizationSwitcherProps, M as FFIDSubscriptionBadge, _ as FFIDSubscriptionBadgeClassNames, $ as FFIDSubscriptionBadgeProps, P as FFIDUserMenu, a0 as FFIDUserMenuClassNames, a1 as FFIDUserMenuProps } from '../index-CTvhqdrH.cjs';
2
2
  import 'react/jsx-runtime';
3
3
  import 'react';
@@ -1,3 +1,3 @@
1
- export { u as FFIDAnnouncementBadge, Q as FFIDAnnouncementBadgeClassNames, R as FFIDAnnouncementBadgeProps, v as FFIDAnnouncementList, S as FFIDAnnouncementListClassNames, T as FFIDAnnouncementListProps, C as FFIDLoginButton, V as FFIDLoginButtonProps, H as FFIDOrganizationSwitcher, W as FFIDOrganizationSwitcherClassNames, X as FFIDOrganizationSwitcherProps, J as FFIDSubscriptionBadge, Y as FFIDSubscriptionBadgeClassNames, Z as FFIDSubscriptionBadgeProps, N as FFIDUserMenu, _ as FFIDUserMenuClassNames, $ as FFIDUserMenuProps } from '../index-ea8WAh39.js';
1
+ export { v as FFIDAnnouncementBadge, S as FFIDAnnouncementBadgeClassNames, T as FFIDAnnouncementBadgeProps, w as FFIDAnnouncementList, V as FFIDAnnouncementListClassNames, W as FFIDAnnouncementListProps, E as FFIDLoginButton, X as FFIDLoginButtonProps, J as FFIDOrganizationSwitcher, Y as FFIDOrganizationSwitcherClassNames, Z as FFIDOrganizationSwitcherProps, M as FFIDSubscriptionBadge, _ as FFIDSubscriptionBadgeClassNames, $ as FFIDSubscriptionBadgeProps, P as FFIDUserMenu, a0 as FFIDUserMenuClassNames, a1 as FFIDUserMenuProps } from '../index-CTvhqdrH.js';
2
2
  import 'react/jsx-runtime';
3
3
  import 'react';
@@ -1 +1 @@
1
- export { FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDSubscriptionBadge, FFIDUserMenu } from '../chunk-SNARMHI7.js';
1
+ export { FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDSubscriptionBadge, FFIDUserMenu } from '../chunk-H5JRKOD7.js';
@@ -1,11 +1,33 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ButtonHTMLAttributes, ReactNode, CSSProperties } from 'react';
3
3
 
4
+ /** Cache adapter interface for FFID SDK token verification */
5
+ /**
6
+ * Pluggable cache adapter interface.
7
+ * Implement this to use custom cache backends (Redis, Memcached, etc.)
8
+ */
9
+ interface FFIDCacheAdapter {
10
+ /** Get a cached value by key. Returns null if not found or expired. */
11
+ get(key: string): Promise<unknown | null>;
12
+ /** Set a value with TTL in seconds. */
13
+ set(key: string, value: unknown, ttlSeconds: number): Promise<void>;
14
+ /** Delete a cached value. */
15
+ delete(key: string): Promise<void>;
16
+ }
17
+ /** Cache configuration for createFFIDClient */
18
+ interface FFIDCacheConfig {
19
+ /** Cache adapter instance */
20
+ adapter: FFIDCacheAdapter;
21
+ /** Cache TTL in seconds (how long to cache userinfo/introspect results) */
22
+ ttl: number;
23
+ }
24
+
4
25
  /**
5
26
  * FFID SDK Type Definitions
6
27
  *
7
28
  * Core types for the FeelFlow ID SDK
8
29
  */
30
+
9
31
  /**
10
32
  * User information from FFID
11
33
  */
@@ -151,6 +173,18 @@ interface FFIDConfig {
151
173
  * Use 'introspect' if you need full user profile data.
152
174
  */
153
175
  verifyStrategy?: 'jwt' | 'introspect' | undefined;
176
+ /**
177
+ * Cache configuration for token verification results.
178
+ * When set, introspect/userinfo responses are cached to reduce API calls.
179
+ * Only effective in service-key mode with verifyStrategy: 'introspect'.
180
+ */
181
+ cache?: FFIDCacheConfig | undefined;
182
+ /**
183
+ * Request timeout in milliseconds for FFID API calls.
184
+ * Applies to token verification and introspection requests.
185
+ * @default undefined (no timeout, uses fetch default)
186
+ */
187
+ timeout?: number | undefined;
154
188
  }
155
189
  /**
156
190
  * FFID JWT claims structure (minimal payload).
@@ -727,4 +761,4 @@ interface FFIDAnnouncementListProps {
727
761
  */
728
762
  declare function FFIDAnnouncementList({ announcements, isLoading, className, classNames, style, formatDate, emptyMessage, loadingRender, renderItem, maxContentLines, }: FFIDAnnouncementListProps): react_jsx_runtime.JSX.Element;
729
763
 
730
- export { type FFIDUserMenuProps as $, type AnnouncementListResponse as A, type FFIDJwtClaims as B, FFIDLoginButton as C, type FFIDOAuthTokenResponse as D, type FFIDOAuthUserInfoMemberRole as E, type FFIDConfig as F, type FFIDOAuthUserInfoSubscription as G, FFIDOrganizationSwitcher as H, type FFIDSeatModel as I, FFIDSubscriptionBadge as J, type FFIDSubscriptionStatus as K, type ListAnnouncementsOptions as L, type FFIDTokenIntrospectionResponse as M, FFIDUserMenu as N, type UseFFIDAnnouncementsReturn as O, useFFIDAnnouncements as P, type FFIDAnnouncementBadgeClassNames as Q, type FFIDAnnouncementBadgeProps as R, type FFIDAnnouncementListClassNames as S, type FFIDAnnouncementListProps as T, type UseFFIDAnnouncementsOptions as U, type FFIDLoginButtonProps as V, type FFIDOrganizationSwitcherClassNames as W, type FFIDOrganizationSwitcherProps as X, type FFIDSubscriptionBadgeClassNames as Y, type FFIDSubscriptionBadgeProps as Z, type FFIDUserMenuClassNames as _, type FFIDApiResponse as a, type FFIDSessionResponse as b, type FFIDError as c, type FFIDSubscriptionCheckResponse as d, type FFIDCreateCheckoutParams as e, type FFIDCheckoutSessionResponse as f, type FFIDCreatePortalParams as g, type FFIDPortalSessionResponse as h, type FFIDOAuthUserInfo as i, type FFIDLogger as j, type FFIDUser as k, type FFIDOrganization as l, type FFIDSubscription as m, type FFIDSubscriptionContextValue as n, type FFIDAnnouncementsClientConfig as o, type FFIDAnnouncementsApiResponse as p, type FFIDAnnouncementsLogger as q, type Announcement as r, type AnnouncementStatus as s, type AnnouncementType as t, FFIDAnnouncementBadge as u, FFIDAnnouncementList as v, type FFIDAnnouncementsError as w, type FFIDAnnouncementsErrorCode as x, type FFIDAnnouncementsServerResponse as y, type FFIDContextValue as z };
764
+ export { type FFIDSubscriptionBadgeProps as $, type AnnouncementListResponse as A, type FFIDCacheConfig as B, type FFIDContextValue as C, type FFIDJwtClaims as D, FFIDLoginButton as E, type FFIDConfig as F, type FFIDOAuthTokenResponse as G, type FFIDOAuthUserInfoMemberRole as H, type FFIDOAuthUserInfoSubscription as I, FFIDOrganizationSwitcher as J, type FFIDSeatModel as K, type ListAnnouncementsOptions as L, FFIDSubscriptionBadge as M, type FFIDSubscriptionStatus as N, type FFIDTokenIntrospectionResponse as O, FFIDUserMenu as P, type UseFFIDAnnouncementsReturn as Q, useFFIDAnnouncements as R, type FFIDAnnouncementBadgeClassNames as S, type FFIDAnnouncementBadgeProps as T, type UseFFIDAnnouncementsOptions as U, type FFIDAnnouncementListClassNames as V, type FFIDAnnouncementListProps as W, type FFIDLoginButtonProps as X, type FFIDOrganizationSwitcherClassNames as Y, type FFIDOrganizationSwitcherProps as Z, type FFIDSubscriptionBadgeClassNames as _, type FFIDApiResponse as a, type FFIDUserMenuClassNames as a0, type FFIDUserMenuProps as a1, type FFIDSessionResponse as b, type FFIDError as c, type FFIDSubscriptionCheckResponse as d, type FFIDCreateCheckoutParams as e, type FFIDCheckoutSessionResponse as f, type FFIDCreatePortalParams as g, type FFIDPortalSessionResponse as h, type FFIDOAuthUserInfo as i, type FFIDLogger as j, type FFIDCacheAdapter as k, type FFIDUser as l, type FFIDOrganization as m, type FFIDSubscription as n, type FFIDSubscriptionContextValue as o, type FFIDAnnouncementsClientConfig as p, type FFIDAnnouncementsApiResponse as q, type FFIDAnnouncementsLogger as r, type Announcement as s, type AnnouncementStatus as t, type AnnouncementType as u, FFIDAnnouncementBadge as v, FFIDAnnouncementList as w, type FFIDAnnouncementsError as x, type FFIDAnnouncementsErrorCode as y, type FFIDAnnouncementsServerResponse as z };
@@ -1,11 +1,33 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ButtonHTMLAttributes, ReactNode, CSSProperties } from 'react';
3
3
 
4
+ /** Cache adapter interface for FFID SDK token verification */
5
+ /**
6
+ * Pluggable cache adapter interface.
7
+ * Implement this to use custom cache backends (Redis, Memcached, etc.)
8
+ */
9
+ interface FFIDCacheAdapter {
10
+ /** Get a cached value by key. Returns null if not found or expired. */
11
+ get(key: string): Promise<unknown | null>;
12
+ /** Set a value with TTL in seconds. */
13
+ set(key: string, value: unknown, ttlSeconds: number): Promise<void>;
14
+ /** Delete a cached value. */
15
+ delete(key: string): Promise<void>;
16
+ }
17
+ /** Cache configuration for createFFIDClient */
18
+ interface FFIDCacheConfig {
19
+ /** Cache adapter instance */
20
+ adapter: FFIDCacheAdapter;
21
+ /** Cache TTL in seconds (how long to cache userinfo/introspect results) */
22
+ ttl: number;
23
+ }
24
+
4
25
  /**
5
26
  * FFID SDK Type Definitions
6
27
  *
7
28
  * Core types for the FeelFlow ID SDK
8
29
  */
30
+
9
31
  /**
10
32
  * User information from FFID
11
33
  */
@@ -151,6 +173,18 @@ interface FFIDConfig {
151
173
  * Use 'introspect' if you need full user profile data.
152
174
  */
153
175
  verifyStrategy?: 'jwt' | 'introspect' | undefined;
176
+ /**
177
+ * Cache configuration for token verification results.
178
+ * When set, introspect/userinfo responses are cached to reduce API calls.
179
+ * Only effective in service-key mode with verifyStrategy: 'introspect'.
180
+ */
181
+ cache?: FFIDCacheConfig | undefined;
182
+ /**
183
+ * Request timeout in milliseconds for FFID API calls.
184
+ * Applies to token verification and introspection requests.
185
+ * @default undefined (no timeout, uses fetch default)
186
+ */
187
+ timeout?: number | undefined;
154
188
  }
155
189
  /**
156
190
  * FFID JWT claims structure (minimal payload).
@@ -727,4 +761,4 @@ interface FFIDAnnouncementListProps {
727
761
  */
728
762
  declare function FFIDAnnouncementList({ announcements, isLoading, className, classNames, style, formatDate, emptyMessage, loadingRender, renderItem, maxContentLines, }: FFIDAnnouncementListProps): react_jsx_runtime.JSX.Element;
729
763
 
730
- export { type FFIDUserMenuProps as $, type AnnouncementListResponse as A, type FFIDJwtClaims as B, FFIDLoginButton as C, type FFIDOAuthTokenResponse as D, type FFIDOAuthUserInfoMemberRole as E, type FFIDConfig as F, type FFIDOAuthUserInfoSubscription as G, FFIDOrganizationSwitcher as H, type FFIDSeatModel as I, FFIDSubscriptionBadge as J, type FFIDSubscriptionStatus as K, type ListAnnouncementsOptions as L, type FFIDTokenIntrospectionResponse as M, FFIDUserMenu as N, type UseFFIDAnnouncementsReturn as O, useFFIDAnnouncements as P, type FFIDAnnouncementBadgeClassNames as Q, type FFIDAnnouncementBadgeProps as R, type FFIDAnnouncementListClassNames as S, type FFIDAnnouncementListProps as T, type UseFFIDAnnouncementsOptions as U, type FFIDLoginButtonProps as V, type FFIDOrganizationSwitcherClassNames as W, type FFIDOrganizationSwitcherProps as X, type FFIDSubscriptionBadgeClassNames as Y, type FFIDSubscriptionBadgeProps as Z, type FFIDUserMenuClassNames as _, type FFIDApiResponse as a, type FFIDSessionResponse as b, type FFIDError as c, type FFIDSubscriptionCheckResponse as d, type FFIDCreateCheckoutParams as e, type FFIDCheckoutSessionResponse as f, type FFIDCreatePortalParams as g, type FFIDPortalSessionResponse as h, type FFIDOAuthUserInfo as i, type FFIDLogger as j, type FFIDUser as k, type FFIDOrganization as l, type FFIDSubscription as m, type FFIDSubscriptionContextValue as n, type FFIDAnnouncementsClientConfig as o, type FFIDAnnouncementsApiResponse as p, type FFIDAnnouncementsLogger as q, type Announcement as r, type AnnouncementStatus as s, type AnnouncementType as t, FFIDAnnouncementBadge as u, FFIDAnnouncementList as v, type FFIDAnnouncementsError as w, type FFIDAnnouncementsErrorCode as x, type FFIDAnnouncementsServerResponse as y, type FFIDContextValue as z };
764
+ export { type FFIDSubscriptionBadgeProps as $, type AnnouncementListResponse as A, type FFIDCacheConfig as B, type FFIDContextValue as C, type FFIDJwtClaims as D, FFIDLoginButton as E, type FFIDConfig as F, type FFIDOAuthTokenResponse as G, type FFIDOAuthUserInfoMemberRole as H, type FFIDOAuthUserInfoSubscription as I, FFIDOrganizationSwitcher as J, type FFIDSeatModel as K, type ListAnnouncementsOptions as L, FFIDSubscriptionBadge as M, type FFIDSubscriptionStatus as N, type FFIDTokenIntrospectionResponse as O, FFIDUserMenu as P, type UseFFIDAnnouncementsReturn as Q, useFFIDAnnouncements as R, type FFIDAnnouncementBadgeClassNames as S, type FFIDAnnouncementBadgeProps as T, type UseFFIDAnnouncementsOptions as U, type FFIDAnnouncementListClassNames as V, type FFIDAnnouncementListProps as W, type FFIDLoginButtonProps as X, type FFIDOrganizationSwitcherClassNames as Y, type FFIDOrganizationSwitcherProps as Z, type FFIDSubscriptionBadgeClassNames as _, type FFIDApiResponse as a, type FFIDUserMenuClassNames as a0, type FFIDUserMenuProps as a1, type FFIDSessionResponse as b, type FFIDError as c, type FFIDSubscriptionCheckResponse as d, type FFIDCreateCheckoutParams as e, type FFIDCheckoutSessionResponse as f, type FFIDCreatePortalParams as g, type FFIDPortalSessionResponse as h, type FFIDOAuthUserInfo as i, type FFIDLogger as j, type FFIDCacheAdapter as k, type FFIDUser as l, type FFIDOrganization as m, type FFIDSubscription as n, type FFIDSubscriptionContextValue as o, type FFIDAnnouncementsClientConfig as p, type FFIDAnnouncementsApiResponse as q, type FFIDAnnouncementsLogger as r, type Announcement as s, type AnnouncementStatus as t, type AnnouncementType as u, FFIDAnnouncementBadge as v, FFIDAnnouncementList as w, type FFIDAnnouncementsError as x, type FFIDAnnouncementsErrorCode as y, type FFIDAnnouncementsServerResponse as z };
package/dist/index.cjs CHANGED
@@ -1,12 +1,52 @@
1
1
  'use strict';
2
2
 
3
- var chunkLLOXOE37_cjs = require('./chunk-LLOXOE37.cjs');
3
+ var chunkCBYBTBKA_cjs = require('./chunk-CBYBTBKA.cjs');
4
4
  var react = require('react');
5
5
  var jsxRuntime = require('react/jsx-runtime');
6
6
 
7
+ // src/client/cache/memory-cache-adapter.ts
8
+ var MS_PER_SECOND = 1e3;
9
+ function createMemoryCacheAdapter() {
10
+ const store = /* @__PURE__ */ new Map();
11
+ return {
12
+ async get(key) {
13
+ const entry = store.get(key);
14
+ if (!entry) return null;
15
+ if (Date.now() >= entry.expiresAt) {
16
+ store.delete(key);
17
+ return null;
18
+ }
19
+ return entry.value;
20
+ },
21
+ async set(key, value, ttlSeconds) {
22
+ store.set(key, {
23
+ value,
24
+ expiresAt: Date.now() + ttlSeconds * MS_PER_SECOND
25
+ });
26
+ },
27
+ async delete(key) {
28
+ store.delete(key);
29
+ }
30
+ };
31
+ }
32
+
33
+ // src/client/cache/kv-cache-adapter.ts
34
+ function createKVCacheAdapter(kv) {
35
+ return {
36
+ async get(key) {
37
+ return kv.get(key, "json");
38
+ },
39
+ async set(key, value, ttlSeconds) {
40
+ await kv.put(key, JSON.stringify(value), { expirationTtl: ttlSeconds });
41
+ },
42
+ async delete(key) {
43
+ await kv.delete(key);
44
+ }
45
+ };
46
+ }
7
47
  function withFFIDAuth(Component, options = {}) {
8
48
  const WrappedComponent = (props) => {
9
- const { isLoading, isAuthenticated, login } = chunkLLOXOE37_cjs.useFFIDContext();
49
+ const { isLoading, isAuthenticated, login } = chunkCBYBTBKA_cjs.useFFIDContext();
10
50
  const hasRedirected = react.useRef(false);
11
51
  react.useEffect(() => {
12
52
  if (!isLoading && !isAuthenticated && options.redirectToLogin && !hasRedirected.current) {
@@ -31,82 +71,84 @@ function withFFIDAuth(Component, options = {}) {
31
71
 
32
72
  Object.defineProperty(exports, "DEFAULT_API_BASE_URL", {
33
73
  enumerable: true,
34
- get: function () { return chunkLLOXOE37_cjs.DEFAULT_API_BASE_URL; }
74
+ get: function () { return chunkCBYBTBKA_cjs.DEFAULT_API_BASE_URL; }
35
75
  });
36
76
  Object.defineProperty(exports, "FFIDAnnouncementBadge", {
37
77
  enumerable: true,
38
- get: function () { return chunkLLOXOE37_cjs.FFIDAnnouncementBadge; }
78
+ get: function () { return chunkCBYBTBKA_cjs.FFIDAnnouncementBadge; }
39
79
  });
40
80
  Object.defineProperty(exports, "FFIDAnnouncementList", {
41
81
  enumerable: true,
42
- get: function () { return chunkLLOXOE37_cjs.FFIDAnnouncementList; }
82
+ get: function () { return chunkCBYBTBKA_cjs.FFIDAnnouncementList; }
43
83
  });
44
84
  Object.defineProperty(exports, "FFIDLoginButton", {
45
85
  enumerable: true,
46
- get: function () { return chunkLLOXOE37_cjs.FFIDLoginButton; }
86
+ get: function () { return chunkCBYBTBKA_cjs.FFIDLoginButton; }
47
87
  });
48
88
  Object.defineProperty(exports, "FFIDOrganizationSwitcher", {
49
89
  enumerable: true,
50
- get: function () { return chunkLLOXOE37_cjs.FFIDOrganizationSwitcher; }
90
+ get: function () { return chunkCBYBTBKA_cjs.FFIDOrganizationSwitcher; }
51
91
  });
52
92
  Object.defineProperty(exports, "FFIDProvider", {
53
93
  enumerable: true,
54
- get: function () { return chunkLLOXOE37_cjs.FFIDProvider; }
94
+ get: function () { return chunkCBYBTBKA_cjs.FFIDProvider; }
55
95
  });
56
96
  Object.defineProperty(exports, "FFIDSubscriptionBadge", {
57
97
  enumerable: true,
58
- get: function () { return chunkLLOXOE37_cjs.FFIDSubscriptionBadge; }
98
+ get: function () { return chunkCBYBTBKA_cjs.FFIDSubscriptionBadge; }
59
99
  });
60
100
  Object.defineProperty(exports, "FFIDUserMenu", {
61
101
  enumerable: true,
62
- get: function () { return chunkLLOXOE37_cjs.FFIDUserMenu; }
102
+ get: function () { return chunkCBYBTBKA_cjs.FFIDUserMenu; }
63
103
  });
64
104
  Object.defineProperty(exports, "FFID_ANNOUNCEMENTS_ERROR_CODES", {
65
105
  enumerable: true,
66
- get: function () { return chunkLLOXOE37_cjs.FFID_ANNOUNCEMENTS_ERROR_CODES; }
106
+ get: function () { return chunkCBYBTBKA_cjs.FFID_ANNOUNCEMENTS_ERROR_CODES; }
67
107
  });
68
108
  Object.defineProperty(exports, "createFFIDAnnouncementsClient", {
69
109
  enumerable: true,
70
- get: function () { return chunkLLOXOE37_cjs.createFFIDAnnouncementsClient; }
110
+ get: function () { return chunkCBYBTBKA_cjs.createFFIDAnnouncementsClient; }
71
111
  });
72
112
  Object.defineProperty(exports, "createFFIDClient", {
73
113
  enumerable: true,
74
- get: function () { return chunkLLOXOE37_cjs.createFFIDClient; }
114
+ get: function () { return chunkCBYBTBKA_cjs.createFFIDClient; }
75
115
  });
76
116
  Object.defineProperty(exports, "createTokenStore", {
77
117
  enumerable: true,
78
- get: function () { return chunkLLOXOE37_cjs.createTokenStore; }
118
+ get: function () { return chunkCBYBTBKA_cjs.createTokenStore; }
79
119
  });
80
120
  Object.defineProperty(exports, "generateCodeChallenge", {
81
121
  enumerable: true,
82
- get: function () { return chunkLLOXOE37_cjs.generateCodeChallenge; }
122
+ get: function () { return chunkCBYBTBKA_cjs.generateCodeChallenge; }
83
123
  });
84
124
  Object.defineProperty(exports, "generateCodeVerifier", {
85
125
  enumerable: true,
86
- get: function () { return chunkLLOXOE37_cjs.generateCodeVerifier; }
126
+ get: function () { return chunkCBYBTBKA_cjs.generateCodeVerifier; }
87
127
  });
88
128
  Object.defineProperty(exports, "retrieveCodeVerifier", {
89
129
  enumerable: true,
90
- get: function () { return chunkLLOXOE37_cjs.retrieveCodeVerifier; }
130
+ get: function () { return chunkCBYBTBKA_cjs.retrieveCodeVerifier; }
91
131
  });
92
132
  Object.defineProperty(exports, "storeCodeVerifier", {
93
133
  enumerable: true,
94
- get: function () { return chunkLLOXOE37_cjs.storeCodeVerifier; }
134
+ get: function () { return chunkCBYBTBKA_cjs.storeCodeVerifier; }
95
135
  });
96
136
  Object.defineProperty(exports, "useFFID", {
97
137
  enumerable: true,
98
- get: function () { return chunkLLOXOE37_cjs.useFFID; }
138
+ get: function () { return chunkCBYBTBKA_cjs.useFFID; }
99
139
  });
100
140
  Object.defineProperty(exports, "useFFIDAnnouncements", {
101
141
  enumerable: true,
102
- get: function () { return chunkLLOXOE37_cjs.useFFIDAnnouncements; }
142
+ get: function () { return chunkCBYBTBKA_cjs.useFFIDAnnouncements; }
103
143
  });
104
144
  Object.defineProperty(exports, "useSubscription", {
105
145
  enumerable: true,
106
- get: function () { return chunkLLOXOE37_cjs.useSubscription; }
146
+ get: function () { return chunkCBYBTBKA_cjs.useSubscription; }
107
147
  });
108
148
  Object.defineProperty(exports, "withSubscription", {
109
149
  enumerable: true,
110
- get: function () { return chunkLLOXOE37_cjs.withSubscription; }
150
+ get: function () { return chunkCBYBTBKA_cjs.withSubscription; }
111
151
  });
152
+ exports.createKVCacheAdapter = createKVCacheAdapter;
153
+ exports.createMemoryCacheAdapter = createMemoryCacheAdapter;
112
154
  exports.withFFIDAuth = withFFIDAuth;
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { F as FFIDConfig, a as FFIDApiResponse, b as FFIDSessionResponse, c as FFIDError, d as FFIDSubscriptionCheckResponse, e as FFIDCreateCheckoutParams, f as FFIDCheckoutSessionResponse, g as FFIDCreatePortalParams, h as FFIDPortalSessionResponse, i as FFIDOAuthUserInfo, j as FFIDLogger, k as FFIDUser, l as FFIDOrganization, m as FFIDSubscription, n as FFIDSubscriptionContextValue, o as FFIDAnnouncementsClientConfig, L as ListAnnouncementsOptions, p as FFIDAnnouncementsApiResponse, A as AnnouncementListResponse, q as FFIDAnnouncementsLogger } from './index-ea8WAh39.cjs';
2
- export { r as Announcement, s as AnnouncementStatus, t as AnnouncementType, u as FFIDAnnouncementBadge, v as FFIDAnnouncementList, w as FFIDAnnouncementsError, x as FFIDAnnouncementsErrorCode, y as FFIDAnnouncementsServerResponse, z as FFIDContextValue, B as FFIDJwtClaims, C as FFIDLoginButton, D as FFIDOAuthTokenResponse, E as FFIDOAuthUserInfoMemberRole, G as FFIDOAuthUserInfoSubscription, H as FFIDOrganizationSwitcher, I as FFIDSeatModel, J as FFIDSubscriptionBadge, K as FFIDSubscriptionStatus, M as FFIDTokenIntrospectionResponse, N as FFIDUserMenu, U as UseFFIDAnnouncementsOptions, O as UseFFIDAnnouncementsReturn, P as useFFIDAnnouncements } from './index-ea8WAh39.cjs';
1
+ import { F as FFIDConfig, a as FFIDApiResponse, b as FFIDSessionResponse, c as FFIDError, d as FFIDSubscriptionCheckResponse, e as FFIDCreateCheckoutParams, f as FFIDCheckoutSessionResponse, g as FFIDCreatePortalParams, h as FFIDPortalSessionResponse, i as FFIDOAuthUserInfo, j as FFIDLogger, k as FFIDCacheAdapter, l as FFIDUser, m as FFIDOrganization, n as FFIDSubscription, o as FFIDSubscriptionContextValue, p as FFIDAnnouncementsClientConfig, L as ListAnnouncementsOptions, q as FFIDAnnouncementsApiResponse, A as AnnouncementListResponse, r as FFIDAnnouncementsLogger } from './index-CTvhqdrH.cjs';
2
+ export { s as Announcement, t as AnnouncementStatus, u as AnnouncementType, v as FFIDAnnouncementBadge, w as FFIDAnnouncementList, x as FFIDAnnouncementsError, y as FFIDAnnouncementsErrorCode, z as FFIDAnnouncementsServerResponse, B as FFIDCacheConfig, C as FFIDContextValue, D as FFIDJwtClaims, E as FFIDLoginButton, G as FFIDOAuthTokenResponse, H as FFIDOAuthUserInfoMemberRole, I as FFIDOAuthUserInfoSubscription, J as FFIDOrganizationSwitcher, K as FFIDSeatModel, M as FFIDSubscriptionBadge, N as FFIDSubscriptionStatus, O as FFIDTokenIntrospectionResponse, P as FFIDUserMenu, U as UseFFIDAnnouncementsOptions, Q as UseFFIDAnnouncementsReturn, R as useFFIDAnnouncements } from './index-CTvhqdrH.cjs';
3
3
  import * as react_jsx_runtime from 'react/jsx-runtime';
4
4
  import { ReactNode, ComponentType, FC } from 'react';
5
5
 
@@ -115,6 +115,29 @@ declare function createFFIDClient(config: FFIDConfig): {
115
115
  /** Type of the FFID client */
116
116
  type FFIDClient = ReturnType<typeof createFFIDClient>;
117
117
 
118
+ /**
119
+ * Create an in-memory cache adapter using a Map.
120
+ * Suitable for single-process environments (e.g., development, testing).
121
+ */
122
+ declare function createMemoryCacheAdapter(): FFIDCacheAdapter;
123
+
124
+ /**
125
+ * Minimal KVNamespace-compatible interface.
126
+ * Matches Cloudflare Workers KV without requiring the CF Workers dependency.
127
+ */
128
+ interface KVNamespaceLike {
129
+ get(key: string, type: 'json'): Promise<unknown | null>;
130
+ put(key: string, value: string, options?: {
131
+ expirationTtl?: number;
132
+ }): Promise<void>;
133
+ delete(key: string): Promise<void>;
134
+ }
135
+ /**
136
+ * Create a cache adapter backed by a KVNamespace-compatible store.
137
+ * Suitable for Cloudflare Workers and other KV-based environments.
138
+ */
139
+ declare function createKVCacheAdapter(kv: KVNamespaceLike): FFIDCacheAdapter;
140
+
118
141
  /**
119
142
  * Props for FFIDProvider component
120
143
  */
@@ -343,4 +366,4 @@ declare function createFFIDAnnouncementsClient(config?: FFIDAnnouncementsClientC
343
366
  /** Type of the FFID Announcements client */
344
367
  type FFIDAnnouncementsClient = ReturnType<typeof createFFIDAnnouncementsClient>;
345
368
 
346
- export { AnnouncementListResponse, DEFAULT_API_BASE_URL, FFIDAnnouncementsApiResponse, type FFIDAnnouncementsClient, FFIDAnnouncementsClientConfig, FFIDAnnouncementsLogger, FFIDApiResponse, FFIDCheckoutSessionResponse, type FFIDClient, FFIDConfig, FFIDCreateCheckoutParams, FFIDCreatePortalParams, FFIDError, FFIDLogger, FFIDOAuthUserInfo, FFIDOrganization, FFIDPortalSessionResponse, FFIDProvider, type FFIDProviderProps, FFIDSessionResponse, FFIDSubscription, FFIDSubscriptionCheckResponse, FFIDSubscriptionContextValue, FFIDUser, FFID_ANNOUNCEMENTS_ERROR_CODES, ListAnnouncementsOptions, type TokenData, type TokenStore, type UseFFIDReturn, type WithFFIDAuthOptions, type WithSubscriptionOptions, createFFIDAnnouncementsClient, createFFIDClient, createTokenStore, generateCodeChallenge, generateCodeVerifier, retrieveCodeVerifier, storeCodeVerifier, useFFID, useSubscription, withFFIDAuth, withSubscription };
369
+ export { AnnouncementListResponse, DEFAULT_API_BASE_URL, FFIDAnnouncementsApiResponse, type FFIDAnnouncementsClient, FFIDAnnouncementsClientConfig, FFIDAnnouncementsLogger, FFIDApiResponse, FFIDCacheAdapter, FFIDCheckoutSessionResponse, type FFIDClient, FFIDConfig, FFIDCreateCheckoutParams, FFIDCreatePortalParams, FFIDError, FFIDLogger, FFIDOAuthUserInfo, FFIDOrganization, FFIDPortalSessionResponse, FFIDProvider, type FFIDProviderProps, FFIDSessionResponse, FFIDSubscription, FFIDSubscriptionCheckResponse, FFIDSubscriptionContextValue, FFIDUser, FFID_ANNOUNCEMENTS_ERROR_CODES, type KVNamespaceLike, ListAnnouncementsOptions, type TokenData, type TokenStore, type UseFFIDReturn, type WithFFIDAuthOptions, type WithSubscriptionOptions, createFFIDAnnouncementsClient, createFFIDClient, createKVCacheAdapter, createMemoryCacheAdapter, createTokenStore, generateCodeChallenge, generateCodeVerifier, retrieveCodeVerifier, storeCodeVerifier, useFFID, useSubscription, withFFIDAuth, withSubscription };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { F as FFIDConfig, a as FFIDApiResponse, b as FFIDSessionResponse, c as FFIDError, d as FFIDSubscriptionCheckResponse, e as FFIDCreateCheckoutParams, f as FFIDCheckoutSessionResponse, g as FFIDCreatePortalParams, h as FFIDPortalSessionResponse, i as FFIDOAuthUserInfo, j as FFIDLogger, k as FFIDUser, l as FFIDOrganization, m as FFIDSubscription, n as FFIDSubscriptionContextValue, o as FFIDAnnouncementsClientConfig, L as ListAnnouncementsOptions, p as FFIDAnnouncementsApiResponse, A as AnnouncementListResponse, q as FFIDAnnouncementsLogger } from './index-ea8WAh39.js';
2
- export { r as Announcement, s as AnnouncementStatus, t as AnnouncementType, u as FFIDAnnouncementBadge, v as FFIDAnnouncementList, w as FFIDAnnouncementsError, x as FFIDAnnouncementsErrorCode, y as FFIDAnnouncementsServerResponse, z as FFIDContextValue, B as FFIDJwtClaims, C as FFIDLoginButton, D as FFIDOAuthTokenResponse, E as FFIDOAuthUserInfoMemberRole, G as FFIDOAuthUserInfoSubscription, H as FFIDOrganizationSwitcher, I as FFIDSeatModel, J as FFIDSubscriptionBadge, K as FFIDSubscriptionStatus, M as FFIDTokenIntrospectionResponse, N as FFIDUserMenu, U as UseFFIDAnnouncementsOptions, O as UseFFIDAnnouncementsReturn, P as useFFIDAnnouncements } from './index-ea8WAh39.js';
1
+ import { F as FFIDConfig, a as FFIDApiResponse, b as FFIDSessionResponse, c as FFIDError, d as FFIDSubscriptionCheckResponse, e as FFIDCreateCheckoutParams, f as FFIDCheckoutSessionResponse, g as FFIDCreatePortalParams, h as FFIDPortalSessionResponse, i as FFIDOAuthUserInfo, j as FFIDLogger, k as FFIDCacheAdapter, l as FFIDUser, m as FFIDOrganization, n as FFIDSubscription, o as FFIDSubscriptionContextValue, p as FFIDAnnouncementsClientConfig, L as ListAnnouncementsOptions, q as FFIDAnnouncementsApiResponse, A as AnnouncementListResponse, r as FFIDAnnouncementsLogger } from './index-CTvhqdrH.js';
2
+ export { s as Announcement, t as AnnouncementStatus, u as AnnouncementType, v as FFIDAnnouncementBadge, w as FFIDAnnouncementList, x as FFIDAnnouncementsError, y as FFIDAnnouncementsErrorCode, z as FFIDAnnouncementsServerResponse, B as FFIDCacheConfig, C as FFIDContextValue, D as FFIDJwtClaims, E as FFIDLoginButton, G as FFIDOAuthTokenResponse, H as FFIDOAuthUserInfoMemberRole, I as FFIDOAuthUserInfoSubscription, J as FFIDOrganizationSwitcher, K as FFIDSeatModel, M as FFIDSubscriptionBadge, N as FFIDSubscriptionStatus, O as FFIDTokenIntrospectionResponse, P as FFIDUserMenu, U as UseFFIDAnnouncementsOptions, Q as UseFFIDAnnouncementsReturn, R as useFFIDAnnouncements } from './index-CTvhqdrH.js';
3
3
  import * as react_jsx_runtime from 'react/jsx-runtime';
4
4
  import { ReactNode, ComponentType, FC } from 'react';
5
5
 
@@ -115,6 +115,29 @@ declare function createFFIDClient(config: FFIDConfig): {
115
115
  /** Type of the FFID client */
116
116
  type FFIDClient = ReturnType<typeof createFFIDClient>;
117
117
 
118
+ /**
119
+ * Create an in-memory cache adapter using a Map.
120
+ * Suitable for single-process environments (e.g., development, testing).
121
+ */
122
+ declare function createMemoryCacheAdapter(): FFIDCacheAdapter;
123
+
124
+ /**
125
+ * Minimal KVNamespace-compatible interface.
126
+ * Matches Cloudflare Workers KV without requiring the CF Workers dependency.
127
+ */
128
+ interface KVNamespaceLike {
129
+ get(key: string, type: 'json'): Promise<unknown | null>;
130
+ put(key: string, value: string, options?: {
131
+ expirationTtl?: number;
132
+ }): Promise<void>;
133
+ delete(key: string): Promise<void>;
134
+ }
135
+ /**
136
+ * Create a cache adapter backed by a KVNamespace-compatible store.
137
+ * Suitable for Cloudflare Workers and other KV-based environments.
138
+ */
139
+ declare function createKVCacheAdapter(kv: KVNamespaceLike): FFIDCacheAdapter;
140
+
118
141
  /**
119
142
  * Props for FFIDProvider component
120
143
  */
@@ -343,4 +366,4 @@ declare function createFFIDAnnouncementsClient(config?: FFIDAnnouncementsClientC
343
366
  /** Type of the FFID Announcements client */
344
367
  type FFIDAnnouncementsClient = ReturnType<typeof createFFIDAnnouncementsClient>;
345
368
 
346
- export { AnnouncementListResponse, DEFAULT_API_BASE_URL, FFIDAnnouncementsApiResponse, type FFIDAnnouncementsClient, FFIDAnnouncementsClientConfig, FFIDAnnouncementsLogger, FFIDApiResponse, FFIDCheckoutSessionResponse, type FFIDClient, FFIDConfig, FFIDCreateCheckoutParams, FFIDCreatePortalParams, FFIDError, FFIDLogger, FFIDOAuthUserInfo, FFIDOrganization, FFIDPortalSessionResponse, FFIDProvider, type FFIDProviderProps, FFIDSessionResponse, FFIDSubscription, FFIDSubscriptionCheckResponse, FFIDSubscriptionContextValue, FFIDUser, FFID_ANNOUNCEMENTS_ERROR_CODES, ListAnnouncementsOptions, type TokenData, type TokenStore, type UseFFIDReturn, type WithFFIDAuthOptions, type WithSubscriptionOptions, createFFIDAnnouncementsClient, createFFIDClient, createTokenStore, generateCodeChallenge, generateCodeVerifier, retrieveCodeVerifier, storeCodeVerifier, useFFID, useSubscription, withFFIDAuth, withSubscription };
369
+ export { AnnouncementListResponse, DEFAULT_API_BASE_URL, FFIDAnnouncementsApiResponse, type FFIDAnnouncementsClient, FFIDAnnouncementsClientConfig, FFIDAnnouncementsLogger, FFIDApiResponse, FFIDCacheAdapter, FFIDCheckoutSessionResponse, type FFIDClient, FFIDConfig, FFIDCreateCheckoutParams, FFIDCreatePortalParams, FFIDError, FFIDLogger, FFIDOAuthUserInfo, FFIDOrganization, FFIDPortalSessionResponse, FFIDProvider, type FFIDProviderProps, FFIDSessionResponse, FFIDSubscription, FFIDSubscriptionCheckResponse, FFIDSubscriptionContextValue, FFIDUser, FFID_ANNOUNCEMENTS_ERROR_CODES, type KVNamespaceLike, ListAnnouncementsOptions, type TokenData, type TokenStore, type UseFFIDReturn, type WithFFIDAuthOptions, type WithSubscriptionOptions, createFFIDAnnouncementsClient, createFFIDClient, createKVCacheAdapter, createMemoryCacheAdapter, createTokenStore, generateCodeChallenge, generateCodeVerifier, retrieveCodeVerifier, storeCodeVerifier, useFFID, useSubscription, withFFIDAuth, withSubscription };
package/dist/index.js CHANGED
@@ -1,8 +1,48 @@
1
- import { useFFIDContext } from './chunk-SNARMHI7.js';
2
- export { DEFAULT_API_BASE_URL, FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDProvider, FFIDSubscriptionBadge, FFIDUserMenu, FFID_ANNOUNCEMENTS_ERROR_CODES, createFFIDAnnouncementsClient, createFFIDClient, createTokenStore, generateCodeChallenge, generateCodeVerifier, retrieveCodeVerifier, storeCodeVerifier, useFFID, useFFIDAnnouncements, useSubscription, withSubscription } from './chunk-SNARMHI7.js';
1
+ import { useFFIDContext } from './chunk-H5JRKOD7.js';
2
+ export { DEFAULT_API_BASE_URL, FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDProvider, FFIDSubscriptionBadge, FFIDUserMenu, FFID_ANNOUNCEMENTS_ERROR_CODES, createFFIDAnnouncementsClient, createFFIDClient, createTokenStore, generateCodeChallenge, generateCodeVerifier, retrieveCodeVerifier, storeCodeVerifier, useFFID, useFFIDAnnouncements, useSubscription, withSubscription } from './chunk-H5JRKOD7.js';
3
3
  import { useRef, useEffect } from 'react';
4
4
  import { jsx, Fragment } from 'react/jsx-runtime';
5
5
 
6
+ // src/client/cache/memory-cache-adapter.ts
7
+ var MS_PER_SECOND = 1e3;
8
+ function createMemoryCacheAdapter() {
9
+ const store = /* @__PURE__ */ new Map();
10
+ return {
11
+ async get(key) {
12
+ const entry = store.get(key);
13
+ if (!entry) return null;
14
+ if (Date.now() >= entry.expiresAt) {
15
+ store.delete(key);
16
+ return null;
17
+ }
18
+ return entry.value;
19
+ },
20
+ async set(key, value, ttlSeconds) {
21
+ store.set(key, {
22
+ value,
23
+ expiresAt: Date.now() + ttlSeconds * MS_PER_SECOND
24
+ });
25
+ },
26
+ async delete(key) {
27
+ store.delete(key);
28
+ }
29
+ };
30
+ }
31
+
32
+ // src/client/cache/kv-cache-adapter.ts
33
+ function createKVCacheAdapter(kv) {
34
+ return {
35
+ async get(key) {
36
+ return kv.get(key, "json");
37
+ },
38
+ async set(key, value, ttlSeconds) {
39
+ await kv.put(key, JSON.stringify(value), { expirationTtl: ttlSeconds });
40
+ },
41
+ async delete(key) {
42
+ await kv.delete(key);
43
+ }
44
+ };
45
+ }
6
46
  function withFFIDAuth(Component, options = {}) {
7
47
  const WrappedComponent = (props) => {
8
48
  const { isLoading, isAuthenticated, login } = useFFIDContext();
@@ -28,4 +68,4 @@ function withFFIDAuth(Component, options = {}) {
28
68
  return WrappedComponent;
29
69
  }
30
70
 
31
- export { withFFIDAuth };
71
+ export { createKVCacheAdapter, createMemoryCacheAdapter, withFFIDAuth };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feelflow/ffid-sdk",
3
- "version": "1.7.3",
3
+ "version": "1.9.0",
4
4
  "description": "FeelFlow ID Platform SDK for React/Next.js applications",
5
5
  "keywords": [
6
6
  "feelflow",