@feelflow/ffid-sdk 2.17.0 → 2.18.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.
@@ -105,10 +105,7 @@ function createTokenStore(storageType) {
105
105
  // src/client/oauth-userinfo.ts
106
106
  var SESSION_ELIGIBLE_SUBSCRIPTION_STATUSES = [
107
107
  "trialing",
108
- "active",
109
- "past_due",
110
- "canceled",
111
- "paused"
108
+ "active"
112
109
  ];
113
110
  function isSessionEligibleSubscriptionStatus(value) {
114
111
  return typeof value === "string" && SESSION_ELIGIBLE_SUBSCRIPTION_STATUSES.includes(value);
@@ -810,10 +807,11 @@ function createProfileMethods(deps) {
810
807
  }
811
808
 
812
809
  // src/client/version-check.ts
813
- var SDK_VERSION = "2.17.0";
810
+ var SDK_VERSION = "2.18.0";
814
811
  var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
815
812
  var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
816
813
  function sdkHeaders() {
814
+ if (typeof window !== "undefined") return {};
817
815
  return {
818
816
  "User-Agent": SDK_USER_AGENT,
819
817
  [SDK_VERSION_HEADER]: SDK_VERSION
@@ -1372,6 +1370,87 @@ var OAUTH_AUTHORIZE_ENDPOINT = "/api/v1/oauth/authorize";
1372
1370
  var AUTH_LOGOUT_ENDPOINT = "/api/v1/auth/logout";
1373
1371
  var STATE_RANDOM_BYTES = 16;
1374
1372
  var HEX_BASE2 = 16;
1373
+ var REDIRECT_LOOP_KEY = "ffid_sdk_redirect_loop_history";
1374
+ var REDIRECT_LOOP_WINDOW_MS = 6e4;
1375
+ var REDIRECT_LOOP_THRESHOLD = 3;
1376
+ var storageReadFailureLogged = false;
1377
+ var storageWriteFailureLogged = false;
1378
+ function logStorageReadFailure(logger, err) {
1379
+ if (storageReadFailureLogged) return;
1380
+ storageReadFailureLogged = true;
1381
+ logger.warn(
1382
+ "[FFID SDK] sessionStorage read failed \u2014 redirect loop detection disabled on this browser (fail-open). iOS WebKit private mode / eviction \u304C\u5178\u578B\u8981\u56E0",
1383
+ err
1384
+ );
1385
+ }
1386
+ function logStorageWriteFailure(logger, err) {
1387
+ if (storageWriteFailureLogged) return;
1388
+ storageWriteFailureLogged = true;
1389
+ logger.warn(
1390
+ "[FFID SDK] sessionStorage write failed \u2014 redirect loop detection disabled on this browser (fail-open).",
1391
+ err
1392
+ );
1393
+ }
1394
+ function isRedirectLoopHistory(value) {
1395
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
1396
+ return Object.values(value).every(
1397
+ (arr) => Array.isArray(arr) && arr.every((t) => typeof t === "number" && Number.isFinite(t))
1398
+ );
1399
+ }
1400
+ function readRedirectLoopHistory(logger) {
1401
+ if (typeof window === "undefined") return {};
1402
+ try {
1403
+ const raw = window.sessionStorage.getItem(REDIRECT_LOOP_KEY);
1404
+ if (raw === null) return {};
1405
+ const parsed = JSON.parse(raw);
1406
+ return isRedirectLoopHistory(parsed) ? parsed : {};
1407
+ } catch (err) {
1408
+ logStorageReadFailure(logger, err);
1409
+ return {};
1410
+ }
1411
+ }
1412
+ function writeRedirectLoopHistory(history, logger) {
1413
+ if (typeof window === "undefined") return;
1414
+ try {
1415
+ window.sessionStorage.setItem(REDIRECT_LOOP_KEY, JSON.stringify(history));
1416
+ } catch (err) {
1417
+ logStorageWriteFailure(logger, err);
1418
+ }
1419
+ }
1420
+ function pruneRedirectLoopHistory(history, now) {
1421
+ const cutoff = now - REDIRECT_LOOP_WINDOW_MS;
1422
+ const pruned = {};
1423
+ for (const [key, timestamps] of Object.entries(history)) {
1424
+ const fresh = timestamps.filter((t) => t > cutoff);
1425
+ if (fresh.length > 0) pruned[key] = fresh;
1426
+ }
1427
+ return pruned;
1428
+ }
1429
+ function getRecentRedirectCount(authorizeKey, now, logger) {
1430
+ const pruned = pruneRedirectLoopHistory(readRedirectLoopHistory(logger), now);
1431
+ writeRedirectLoopHistory(pruned, logger);
1432
+ return pruned[authorizeKey]?.length ?? 0;
1433
+ }
1434
+ function recordRedirectAttempt(authorizeKey, now, logger) {
1435
+ const history = readRedirectLoopHistory(logger);
1436
+ const existing = history[authorizeKey] ?? [];
1437
+ history[authorizeKey] = [...existing, now];
1438
+ writeRedirectLoopHistory(history, logger);
1439
+ }
1440
+ function rollbackLastRedirectAttempt(authorizeKey, logger) {
1441
+ const history = readRedirectLoopHistory(logger);
1442
+ const existing = history[authorizeKey];
1443
+ if (!Array.isArray(existing) || existing.length === 0) return;
1444
+ history[authorizeKey] = existing.slice(0, -1);
1445
+ if (history[authorizeKey].length === 0) delete history[authorizeKey];
1446
+ writeRedirectLoopHistory(history, logger);
1447
+ }
1448
+ function buildAuthorizeKey(baseUrl, clientId, organizationId) {
1449
+ const params = new URLSearchParams({ client_id: clientId });
1450
+ const trimmedOrg = organizationId?.trim();
1451
+ if (trimmedOrg) params.set("organization_id", trimmedOrg);
1452
+ return `${baseUrl}${OAUTH_AUTHORIZE_ENDPOINT}?${params.toString()}`;
1453
+ }
1375
1454
  function generateRandomState() {
1376
1455
  const array = new Uint8Array(STATE_RANDOM_BYTES);
1377
1456
  crypto.getRandomValues(array);
@@ -1391,6 +1470,23 @@ function createRedirectMethods(deps) {
1391
1470
  logger.warn("SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093");
1392
1471
  return { success: false, error: "SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093" };
1393
1472
  }
1473
+ const authorizeKey = buildAuthorizeKey(baseUrl, clientId, options?.organizationId);
1474
+ const now = Date.now();
1475
+ const recentCount = getRecentRedirectCount(authorizeKey, now, logger);
1476
+ if (recentCount >= REDIRECT_LOOP_THRESHOLD) {
1477
+ cleanupVerifierStorage(logger);
1478
+ logger.warn("[FFID SDK] redirect loop detected \u2014 \u81EA\u52D5\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3092\u505C\u6B62\u3057\u307E\u3057\u305F", {
1479
+ authorizeKey,
1480
+ recentCount,
1481
+ windowMs: REDIRECT_LOOP_WINDOW_MS,
1482
+ threshold: REDIRECT_LOOP_THRESHOLD
1483
+ });
1484
+ return {
1485
+ success: false,
1486
+ code: "redirect_loop_detected",
1487
+ error: "\u77ED\u6642\u9593\u306B\u540C\u3058\u8A8D\u53EF\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u3078\u306E\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u304C\u7E70\u308A\u8FD4\u3055\u308C\u305F\u305F\u3081\u3001\u30EB\u30FC\u30D7\u691C\u51FA\u306B\u3088\u308A\u81EA\u52D5\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3092\u505C\u6B62\u3057\u307E\u3057\u305F"
1488
+ };
1489
+ }
1394
1490
  const verifier = generateCodeVerifier();
1395
1491
  storeCodeVerifier(verifier, logger);
1396
1492
  let challenge;
@@ -1421,7 +1517,13 @@ function createRedirectMethods(deps) {
1421
1517
  }
1422
1518
  const authorizeUrl = `${baseUrl}${OAUTH_AUTHORIZE_ENDPOINT}?${params.toString()}`;
1423
1519
  logger.debug("Redirecting to authorize:", authorizeUrl);
1424
- window.location.href = authorizeUrl;
1520
+ recordRedirectAttempt(authorizeKey, now, logger);
1521
+ try {
1522
+ window.location.href = authorizeUrl;
1523
+ } catch (err) {
1524
+ rollbackLastRedirectAttempt(authorizeKey, logger);
1525
+ throw err;
1526
+ }
1425
1527
  return { success: true };
1426
1528
  }
1427
1529
  async function redirectToLogin() {
@@ -103,10 +103,7 @@ function createTokenStore(storageType) {
103
103
  // src/client/oauth-userinfo.ts
104
104
  var SESSION_ELIGIBLE_SUBSCRIPTION_STATUSES = [
105
105
  "trialing",
106
- "active",
107
- "past_due",
108
- "canceled",
109
- "paused"
106
+ "active"
110
107
  ];
111
108
  function isSessionEligibleSubscriptionStatus(value) {
112
109
  return typeof value === "string" && SESSION_ELIGIBLE_SUBSCRIPTION_STATUSES.includes(value);
@@ -808,10 +805,11 @@ function createProfileMethods(deps) {
808
805
  }
809
806
 
810
807
  // src/client/version-check.ts
811
- var SDK_VERSION = "2.17.0";
808
+ var SDK_VERSION = "2.18.0";
812
809
  var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
813
810
  var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
814
811
  function sdkHeaders() {
812
+ if (typeof window !== "undefined") return {};
815
813
  return {
816
814
  "User-Agent": SDK_USER_AGENT,
817
815
  [SDK_VERSION_HEADER]: SDK_VERSION
@@ -1370,6 +1368,87 @@ var OAUTH_AUTHORIZE_ENDPOINT = "/api/v1/oauth/authorize";
1370
1368
  var AUTH_LOGOUT_ENDPOINT = "/api/v1/auth/logout";
1371
1369
  var STATE_RANDOM_BYTES = 16;
1372
1370
  var HEX_BASE2 = 16;
1371
+ var REDIRECT_LOOP_KEY = "ffid_sdk_redirect_loop_history";
1372
+ var REDIRECT_LOOP_WINDOW_MS = 6e4;
1373
+ var REDIRECT_LOOP_THRESHOLD = 3;
1374
+ var storageReadFailureLogged = false;
1375
+ var storageWriteFailureLogged = false;
1376
+ function logStorageReadFailure(logger, err) {
1377
+ if (storageReadFailureLogged) return;
1378
+ storageReadFailureLogged = true;
1379
+ logger.warn(
1380
+ "[FFID SDK] sessionStorage read failed \u2014 redirect loop detection disabled on this browser (fail-open). iOS WebKit private mode / eviction \u304C\u5178\u578B\u8981\u56E0",
1381
+ err
1382
+ );
1383
+ }
1384
+ function logStorageWriteFailure(logger, err) {
1385
+ if (storageWriteFailureLogged) return;
1386
+ storageWriteFailureLogged = true;
1387
+ logger.warn(
1388
+ "[FFID SDK] sessionStorage write failed \u2014 redirect loop detection disabled on this browser (fail-open).",
1389
+ err
1390
+ );
1391
+ }
1392
+ function isRedirectLoopHistory(value) {
1393
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
1394
+ return Object.values(value).every(
1395
+ (arr) => Array.isArray(arr) && arr.every((t) => typeof t === "number" && Number.isFinite(t))
1396
+ );
1397
+ }
1398
+ function readRedirectLoopHistory(logger) {
1399
+ if (typeof window === "undefined") return {};
1400
+ try {
1401
+ const raw = window.sessionStorage.getItem(REDIRECT_LOOP_KEY);
1402
+ if (raw === null) return {};
1403
+ const parsed = JSON.parse(raw);
1404
+ return isRedirectLoopHistory(parsed) ? parsed : {};
1405
+ } catch (err) {
1406
+ logStorageReadFailure(logger, err);
1407
+ return {};
1408
+ }
1409
+ }
1410
+ function writeRedirectLoopHistory(history, logger) {
1411
+ if (typeof window === "undefined") return;
1412
+ try {
1413
+ window.sessionStorage.setItem(REDIRECT_LOOP_KEY, JSON.stringify(history));
1414
+ } catch (err) {
1415
+ logStorageWriteFailure(logger, err);
1416
+ }
1417
+ }
1418
+ function pruneRedirectLoopHistory(history, now) {
1419
+ const cutoff = now - REDIRECT_LOOP_WINDOW_MS;
1420
+ const pruned = {};
1421
+ for (const [key, timestamps] of Object.entries(history)) {
1422
+ const fresh = timestamps.filter((t) => t > cutoff);
1423
+ if (fresh.length > 0) pruned[key] = fresh;
1424
+ }
1425
+ return pruned;
1426
+ }
1427
+ function getRecentRedirectCount(authorizeKey, now, logger) {
1428
+ const pruned = pruneRedirectLoopHistory(readRedirectLoopHistory(logger), now);
1429
+ writeRedirectLoopHistory(pruned, logger);
1430
+ return pruned[authorizeKey]?.length ?? 0;
1431
+ }
1432
+ function recordRedirectAttempt(authorizeKey, now, logger) {
1433
+ const history = readRedirectLoopHistory(logger);
1434
+ const existing = history[authorizeKey] ?? [];
1435
+ history[authorizeKey] = [...existing, now];
1436
+ writeRedirectLoopHistory(history, logger);
1437
+ }
1438
+ function rollbackLastRedirectAttempt(authorizeKey, logger) {
1439
+ const history = readRedirectLoopHistory(logger);
1440
+ const existing = history[authorizeKey];
1441
+ if (!Array.isArray(existing) || existing.length === 0) return;
1442
+ history[authorizeKey] = existing.slice(0, -1);
1443
+ if (history[authorizeKey].length === 0) delete history[authorizeKey];
1444
+ writeRedirectLoopHistory(history, logger);
1445
+ }
1446
+ function buildAuthorizeKey(baseUrl, clientId, organizationId) {
1447
+ const params = new URLSearchParams({ client_id: clientId });
1448
+ const trimmedOrg = organizationId?.trim();
1449
+ if (trimmedOrg) params.set("organization_id", trimmedOrg);
1450
+ return `${baseUrl}${OAUTH_AUTHORIZE_ENDPOINT}?${params.toString()}`;
1451
+ }
1373
1452
  function generateRandomState() {
1374
1453
  const array = new Uint8Array(STATE_RANDOM_BYTES);
1375
1454
  crypto.getRandomValues(array);
@@ -1389,6 +1468,23 @@ function createRedirectMethods(deps) {
1389
1468
  logger.warn("SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093");
1390
1469
  return { success: false, error: "SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093" };
1391
1470
  }
1471
+ const authorizeKey = buildAuthorizeKey(baseUrl, clientId, options?.organizationId);
1472
+ const now = Date.now();
1473
+ const recentCount = getRecentRedirectCount(authorizeKey, now, logger);
1474
+ if (recentCount >= REDIRECT_LOOP_THRESHOLD) {
1475
+ cleanupVerifierStorage(logger);
1476
+ logger.warn("[FFID SDK] redirect loop detected \u2014 \u81EA\u52D5\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3092\u505C\u6B62\u3057\u307E\u3057\u305F", {
1477
+ authorizeKey,
1478
+ recentCount,
1479
+ windowMs: REDIRECT_LOOP_WINDOW_MS,
1480
+ threshold: REDIRECT_LOOP_THRESHOLD
1481
+ });
1482
+ return {
1483
+ success: false,
1484
+ code: "redirect_loop_detected",
1485
+ error: "\u77ED\u6642\u9593\u306B\u540C\u3058\u8A8D\u53EF\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u3078\u306E\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u304C\u7E70\u308A\u8FD4\u3055\u308C\u305F\u305F\u3081\u3001\u30EB\u30FC\u30D7\u691C\u51FA\u306B\u3088\u308A\u81EA\u52D5\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3092\u505C\u6B62\u3057\u307E\u3057\u305F"
1486
+ };
1487
+ }
1392
1488
  const verifier = generateCodeVerifier();
1393
1489
  storeCodeVerifier(verifier, logger);
1394
1490
  let challenge;
@@ -1419,7 +1515,13 @@ function createRedirectMethods(deps) {
1419
1515
  }
1420
1516
  const authorizeUrl = `${baseUrl}${OAUTH_AUTHORIZE_ENDPOINT}?${params.toString()}`;
1421
1517
  logger.debug("Redirecting to authorize:", authorizeUrl);
1422
- window.location.href = authorizeUrl;
1518
+ recordRedirectAttempt(authorizeKey, now, logger);
1519
+ try {
1520
+ window.location.href = authorizeUrl;
1521
+ } catch (err) {
1522
+ rollbackLastRedirectAttempt(authorizeKey, logger);
1523
+ throw err;
1524
+ }
1423
1525
  return { success: true };
1424
1526
  }
1425
1527
  async function redirectToLogin() {
@@ -1,34 +1,34 @@
1
1
  'use strict';
2
2
 
3
- var chunkOWE6EPWE_cjs = require('../chunk-OWE6EPWE.cjs');
3
+ var chunkEK2W67BW_cjs = require('../chunk-EK2W67BW.cjs');
4
4
 
5
5
 
6
6
 
7
7
  Object.defineProperty(exports, "FFIDAnnouncementBadge", {
8
8
  enumerable: true,
9
- get: function () { return chunkOWE6EPWE_cjs.FFIDAnnouncementBadge; }
9
+ get: function () { return chunkEK2W67BW_cjs.FFIDAnnouncementBadge; }
10
10
  });
11
11
  Object.defineProperty(exports, "FFIDAnnouncementList", {
12
12
  enumerable: true,
13
- get: function () { return chunkOWE6EPWE_cjs.FFIDAnnouncementList; }
13
+ get: function () { return chunkEK2W67BW_cjs.FFIDAnnouncementList; }
14
14
  });
15
15
  Object.defineProperty(exports, "FFIDInquiryForm", {
16
16
  enumerable: true,
17
- get: function () { return chunkOWE6EPWE_cjs.FFIDInquiryForm; }
17
+ get: function () { return chunkEK2W67BW_cjs.FFIDInquiryForm; }
18
18
  });
19
19
  Object.defineProperty(exports, "FFIDLoginButton", {
20
20
  enumerable: true,
21
- get: function () { return chunkOWE6EPWE_cjs.FFIDLoginButton; }
21
+ get: function () { return chunkEK2W67BW_cjs.FFIDLoginButton; }
22
22
  });
23
23
  Object.defineProperty(exports, "FFIDOrganizationSwitcher", {
24
24
  enumerable: true,
25
- get: function () { return chunkOWE6EPWE_cjs.FFIDOrganizationSwitcher; }
25
+ get: function () { return chunkEK2W67BW_cjs.FFIDOrganizationSwitcher; }
26
26
  });
27
27
  Object.defineProperty(exports, "FFIDSubscriptionBadge", {
28
28
  enumerable: true,
29
- get: function () { return chunkOWE6EPWE_cjs.FFIDSubscriptionBadge; }
29
+ get: function () { return chunkEK2W67BW_cjs.FFIDSubscriptionBadge; }
30
30
  });
31
31
  Object.defineProperty(exports, "FFIDUserMenu", {
32
32
  enumerable: true,
33
- get: function () { return chunkOWE6EPWE_cjs.FFIDUserMenu; }
33
+ get: function () { return chunkEK2W67BW_cjs.FFIDUserMenu; }
34
34
  });
@@ -1,3 +1,3 @@
1
- export { K as FFIDAnnouncementBadge, aj as FFIDAnnouncementBadgeClassNames, ak as FFIDAnnouncementBadgeProps, M as FFIDAnnouncementList, al as FFIDAnnouncementListClassNames, am as FFIDAnnouncementListProps, U as FFIDInquiryForm, V as FFIDInquiryFormCategoryItem, W as FFIDInquiryFormClassNames, X as FFIDInquiryFormOrganization, Y as FFIDInquiryFormPlaceholderContext, Z as FFIDInquiryFormPrefill, _ as FFIDInquiryFormProps, $ as FFIDInquiryFormSubmitData, a0 as FFIDInquiryFormSubmitResult, a2 as FFIDLoginButton, an as FFIDLoginButtonProps, a8 as FFIDOrganizationSwitcher, ao as FFIDOrganizationSwitcherClassNames, ap as FFIDOrganizationSwitcherProps, aa as FFIDSubscriptionBadge, aq as FFIDSubscriptionBadgeClassNames, ar as FFIDSubscriptionBadgeProps, ac as FFIDUserMenu, as as FFIDUserMenuClassNames, at as FFIDUserMenuProps } from '../index-DbEyptzr.cjs';
1
+ export { K as FFIDAnnouncementBadge, ak as FFIDAnnouncementBadgeClassNames, al as FFIDAnnouncementBadgeProps, M as FFIDAnnouncementList, am as FFIDAnnouncementListClassNames, an as FFIDAnnouncementListProps, U as FFIDInquiryForm, V as FFIDInquiryFormCategoryItem, W as FFIDInquiryFormClassNames, X as FFIDInquiryFormOrganization, Y as FFIDInquiryFormPlaceholderContext, Z as FFIDInquiryFormPrefill, _ as FFIDInquiryFormProps, $ as FFIDInquiryFormSubmitData, a0 as FFIDInquiryFormSubmitResult, a2 as FFIDLoginButton, ao as FFIDLoginButtonProps, a8 as FFIDOrganizationSwitcher, ap as FFIDOrganizationSwitcherClassNames, aq as FFIDOrganizationSwitcherProps, ab as FFIDSubscriptionBadge, ar as FFIDSubscriptionBadgeClassNames, as as FFIDSubscriptionBadgeProps, ad as FFIDUserMenu, at as FFIDUserMenuClassNames, au as FFIDUserMenuProps } from '../index--S6rLHjr.cjs';
2
2
  import 'react/jsx-runtime';
3
3
  import 'react';
@@ -1,3 +1,3 @@
1
- export { K as FFIDAnnouncementBadge, aj as FFIDAnnouncementBadgeClassNames, ak as FFIDAnnouncementBadgeProps, M as FFIDAnnouncementList, al as FFIDAnnouncementListClassNames, am as FFIDAnnouncementListProps, U as FFIDInquiryForm, V as FFIDInquiryFormCategoryItem, W as FFIDInquiryFormClassNames, X as FFIDInquiryFormOrganization, Y as FFIDInquiryFormPlaceholderContext, Z as FFIDInquiryFormPrefill, _ as FFIDInquiryFormProps, $ as FFIDInquiryFormSubmitData, a0 as FFIDInquiryFormSubmitResult, a2 as FFIDLoginButton, an as FFIDLoginButtonProps, a8 as FFIDOrganizationSwitcher, ao as FFIDOrganizationSwitcherClassNames, ap as FFIDOrganizationSwitcherProps, aa as FFIDSubscriptionBadge, aq as FFIDSubscriptionBadgeClassNames, ar as FFIDSubscriptionBadgeProps, ac as FFIDUserMenu, as as FFIDUserMenuClassNames, at as FFIDUserMenuProps } from '../index-DbEyptzr.js';
1
+ export { K as FFIDAnnouncementBadge, ak as FFIDAnnouncementBadgeClassNames, al as FFIDAnnouncementBadgeProps, M as FFIDAnnouncementList, am as FFIDAnnouncementListClassNames, an as FFIDAnnouncementListProps, U as FFIDInquiryForm, V as FFIDInquiryFormCategoryItem, W as FFIDInquiryFormClassNames, X as FFIDInquiryFormOrganization, Y as FFIDInquiryFormPlaceholderContext, Z as FFIDInquiryFormPrefill, _ as FFIDInquiryFormProps, $ as FFIDInquiryFormSubmitData, a0 as FFIDInquiryFormSubmitResult, a2 as FFIDLoginButton, ao as FFIDLoginButtonProps, a8 as FFIDOrganizationSwitcher, ap as FFIDOrganizationSwitcherClassNames, aq as FFIDOrganizationSwitcherProps, ab as FFIDSubscriptionBadge, ar as FFIDSubscriptionBadgeClassNames, as as FFIDSubscriptionBadgeProps, ad as FFIDUserMenu, at as FFIDUserMenuClassNames, au as FFIDUserMenuProps } from '../index--S6rLHjr.js';
2
2
  import 'react/jsx-runtime';
3
3
  import 'react';
@@ -1 +1 @@
1
- export { FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDInquiryForm, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDSubscriptionBadge, FFIDUserMenu } from '../chunk-O2YI5N6S.js';
1
+ export { FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDInquiryForm, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDSubscriptionBadge, FFIDUserMenu } from '../chunk-IEYXT3LA.js';
@@ -471,8 +471,11 @@ interface FFIDUserProfile {
471
471
  * When `accessToken` is supplied with a non-empty value, it overrides the client's
472
472
  * configured auth mode: authentication for that request uses only
473
473
  * `Authorization: Bearer <accessToken>` (no service key, no cookie, no token-store
474
- * lookup, no auto-refresh on 401). Non-auth headers such as `Content-Type` and
475
- * SDK metadata headers (User-Agent / X-FFID-SDK-Version) are still attached.
474
+ * lookup, no auto-refresh on 401). Non-auth headers such as `Content-Type` are
475
+ * still attached. SDK metadata headers (`User-Agent` / `X-FFID-SDK-Version`) are
476
+ * attached on non-browser runtimes only (Node.js / Cloudflare Workers / Edge /
477
+ * Deno); browsers receive an empty object to avoid iOS WebKit `fetch()` breakage
478
+ * (see #2417).
476
479
  *
477
480
  * Runtime semantics:
478
481
  * - `accessToken` omitted (or `undefined`) → no override, configured `authMode` is used
@@ -534,17 +537,36 @@ interface FFIDUpdateUserProfileRequest {
534
537
  /** Arbitrary user preferences bag (null clears the column; reads return {}) */
535
538
  preferences?: Record<string, unknown> | null;
536
539
  }
540
+ /**
541
+ * Discriminant for redirect failures that callers need to handle
542
+ * programmatically (vs. logging the human-readable `error` string).
543
+ *
544
+ * - `'redirect_loop_detected'`: `redirectToAuthorize()` detected that the
545
+ * same authorize URL (keyed by `baseUrl + client_id + organization_id`)
546
+ * has been fired **3 times within 60 seconds**. Caller should surface a
547
+ * manual "再度ログインする" UI rather than retry automatically (#2406 / #2411).
548
+ *
549
+ * SDK 2.18.0 only ships `'redirect_loop_detected'`. Other failure paths
550
+ * (SSR environment, PKCE generation failure, empty organizationId) currently
551
+ * return `error` without a `code`. New codes will be added in future minor
552
+ * versions — treat this union as forward-extensible and do NOT exhaustively
553
+ * `switch` over it without a `default` branch for consumer code that must
554
+ * stay compatible across SDK upgrades.
555
+ */
556
+ type FFIDRedirectErrorCode = 'redirect_loop_detected';
537
557
  /**
538
558
  * Result of a redirect operation (redirectToLogin / redirectToAuthorize / redirectToLogout)
539
559
  *
540
560
  * Structured return type so callers can inspect failure reasons
541
- * instead of receiving a bare `false`.
561
+ * instead of receiving a bare `false`. When `code` is set, branch on it
562
+ * for programmatic handling; otherwise log `error` for humans.
542
563
  */
543
564
  type FFIDRedirectResult = {
544
565
  success: true;
545
566
  } | {
546
567
  success: false;
547
568
  error: string;
569
+ code?: FFIDRedirectErrorCode;
548
570
  };
549
571
  /**
550
572
  * OAuth 2.0 token response from FFID token endpoint
@@ -1279,4 +1301,4 @@ interface FFIDInquiryFormPlaceholderContext {
1279
1301
  }
1280
1302
  declare function FFIDInquiryForm({ mode, prefill, organizations, preselectedOrganizationId, categories, termsVersion, privacyVersion, termsHref, privacyHref, turnstileToken, turnstileSlot, onSubmit, onChange, separateLegalCheckboxes, messagePlaceholder, requireCategorySelection, unstyled, classNames, locale, className, }: FFIDInquiryFormProps): react_jsx_runtime.JSX.Element;
1281
1303
 
1282
- export { type FFIDInquiryFormSubmitData as $, type FFIDSubscription as A, type FFIDSubscriptionContextValue as B, type FFIDAnnouncementsClientConfig as C, type FFIDAnnouncementsApiResponse as D, type AnnouncementListResponse as E, type FFIDSubscriptionStatus as F, type FFIDAnnouncementsLogger as G, type Announcement as H, type AnnouncementStatus as I, type AnnouncementType as J, FFIDAnnouncementBadge as K, type ListAnnouncementsOptions as L, FFIDAnnouncementList as M, type FFIDAnnouncementsError as N, type FFIDAnnouncementsErrorCode as O, type FFIDAnnouncementsServerResponse as P, type FFIDCacheConfig as Q, type FFIDContextValue as R, type FFIDInquiryCategory as S, type FFIDInquiryCategorySite2026 as T, FFIDInquiryForm as U, type FFIDInquiryFormCategoryItem as V, type FFIDInquiryFormClassNames as W, type FFIDInquiryFormOrganization as X, type FFIDInquiryFormPlaceholderContext as Y, type FFIDInquiryFormPrefill as Z, type FFIDInquiryFormProps as _, type FFIDConfig as a, type FFIDInquiryFormSubmitResult as a0, type FFIDJwtClaims as a1, FFIDLoginButton as a2, type FFIDMemberStatus as a3, type FFIDOAuthTokenResponse as a4, type FFIDOAuthUserInfoMemberRole as a5, type FFIDOAuthUserInfoSubscription as a6, type FFIDOrganizationMember as a7, FFIDOrganizationSwitcher as a8, type FFIDSeatModel as a9, FFIDSubscriptionBadge as aa, type FFIDTokenIntrospectionResponse as ab, FFIDUserMenu as ac, FFID_INQUIRY_CATEGORIES as ad, FFID_INQUIRY_CATEGORIES_SITE_2026 as ae, type UseFFIDAnnouncementsOptions as af, type UseFFIDAnnouncementsReturn as ag, isFFIDInquiryCategorySite2026 as ah, useFFIDAnnouncements as ai, type FFIDAnnouncementBadgeClassNames as aj, type FFIDAnnouncementBadgeProps as ak, type FFIDAnnouncementListClassNames as al, type FFIDAnnouncementListProps as am, type FFIDLoginButtonProps as an, type FFIDOrganizationSwitcherClassNames as ao, type FFIDOrganizationSwitcherProps as ap, type FFIDSubscriptionBadgeClassNames as aq, type FFIDSubscriptionBadgeProps as ar, type FFIDUserMenuClassNames as as, type FFIDUserMenuProps as at, type FFIDApiResponse as b, type FFIDSessionResponse as c, type FFIDRedirectResult as d, type FFIDError as e, type FFIDSubscriptionCheckResponse as f, type FFIDListMembersResponse as g, type FFIDMemberRole as h, type FFIDUpdateMemberRoleResponse as i, type FFIDRemoveMemberResponse as j, type FFIDProfileCallOptions as k, type FFIDUserProfile as l, type FFIDUpdateUserProfileRequest as m, type FFIDCreateCheckoutParams as n, type FFIDCheckoutSessionResponse as o, type FFIDCreatePortalParams as p, type FFIDPortalSessionResponse as q, type FFIDVerifyAccessTokenOptions as r, type FFIDOAuthUserInfo as s, type FFIDInquiryCreateParams as t, type FFIDInquiryCreateResponse as u, type FFIDAuthMode as v, type FFIDLogger as w, type FFIDCacheAdapter as x, type FFIDUser as y, type FFIDOrganization as z };
1304
+ export { type FFIDInquiryFormSubmitData as $, type FFIDSubscription as A, type FFIDSubscriptionContextValue as B, type FFIDAnnouncementsClientConfig as C, type FFIDAnnouncementsApiResponse as D, type AnnouncementListResponse as E, type FFIDSubscriptionStatus as F, type FFIDAnnouncementsLogger as G, type Announcement as H, type AnnouncementStatus as I, type AnnouncementType as J, FFIDAnnouncementBadge as K, type ListAnnouncementsOptions as L, FFIDAnnouncementList as M, type FFIDAnnouncementsError as N, type FFIDAnnouncementsErrorCode as O, type FFIDAnnouncementsServerResponse as P, type FFIDCacheConfig as Q, type FFIDContextValue as R, type FFIDInquiryCategory as S, type FFIDInquiryCategorySite2026 as T, FFIDInquiryForm as U, type FFIDInquiryFormCategoryItem as V, type FFIDInquiryFormClassNames as W, type FFIDInquiryFormOrganization as X, type FFIDInquiryFormPlaceholderContext as Y, type FFIDInquiryFormPrefill as Z, type FFIDInquiryFormProps as _, type FFIDConfig as a, type FFIDInquiryFormSubmitResult as a0, type FFIDJwtClaims as a1, FFIDLoginButton as a2, type FFIDMemberStatus as a3, type FFIDOAuthTokenResponse as a4, type FFIDOAuthUserInfoMemberRole as a5, type FFIDOAuthUserInfoSubscription as a6, type FFIDOrganizationMember as a7, FFIDOrganizationSwitcher as a8, type FFIDRedirectErrorCode as a9, type FFIDSeatModel as aa, FFIDSubscriptionBadge as ab, type FFIDTokenIntrospectionResponse as ac, FFIDUserMenu as ad, FFID_INQUIRY_CATEGORIES as ae, FFID_INQUIRY_CATEGORIES_SITE_2026 as af, type UseFFIDAnnouncementsOptions as ag, type UseFFIDAnnouncementsReturn as ah, isFFIDInquiryCategorySite2026 as ai, useFFIDAnnouncements as aj, type FFIDAnnouncementBadgeClassNames as ak, type FFIDAnnouncementBadgeProps as al, type FFIDAnnouncementListClassNames as am, type FFIDAnnouncementListProps as an, type FFIDLoginButtonProps as ao, type FFIDOrganizationSwitcherClassNames as ap, type FFIDOrganizationSwitcherProps as aq, type FFIDSubscriptionBadgeClassNames as ar, type FFIDSubscriptionBadgeProps as as, type FFIDUserMenuClassNames as at, type FFIDUserMenuProps as au, type FFIDApiResponse as b, type FFIDSessionResponse as c, type FFIDRedirectResult as d, type FFIDError as e, type FFIDSubscriptionCheckResponse as f, type FFIDListMembersResponse as g, type FFIDMemberRole as h, type FFIDUpdateMemberRoleResponse as i, type FFIDRemoveMemberResponse as j, type FFIDProfileCallOptions as k, type FFIDUserProfile as l, type FFIDUpdateUserProfileRequest as m, type FFIDCreateCheckoutParams as n, type FFIDCheckoutSessionResponse as o, type FFIDCreatePortalParams as p, type FFIDPortalSessionResponse as q, type FFIDVerifyAccessTokenOptions as r, type FFIDOAuthUserInfo as s, type FFIDInquiryCreateParams as t, type FFIDInquiryCreateResponse as u, type FFIDAuthMode as v, type FFIDLogger as w, type FFIDCacheAdapter as x, type FFIDUser as y, type FFIDOrganization as z };
@@ -471,8 +471,11 @@ interface FFIDUserProfile {
471
471
  * When `accessToken` is supplied with a non-empty value, it overrides the client's
472
472
  * configured auth mode: authentication for that request uses only
473
473
  * `Authorization: Bearer <accessToken>` (no service key, no cookie, no token-store
474
- * lookup, no auto-refresh on 401). Non-auth headers such as `Content-Type` and
475
- * SDK metadata headers (User-Agent / X-FFID-SDK-Version) are still attached.
474
+ * lookup, no auto-refresh on 401). Non-auth headers such as `Content-Type` are
475
+ * still attached. SDK metadata headers (`User-Agent` / `X-FFID-SDK-Version`) are
476
+ * attached on non-browser runtimes only (Node.js / Cloudflare Workers / Edge /
477
+ * Deno); browsers receive an empty object to avoid iOS WebKit `fetch()` breakage
478
+ * (see #2417).
476
479
  *
477
480
  * Runtime semantics:
478
481
  * - `accessToken` omitted (or `undefined`) → no override, configured `authMode` is used
@@ -534,17 +537,36 @@ interface FFIDUpdateUserProfileRequest {
534
537
  /** Arbitrary user preferences bag (null clears the column; reads return {}) */
535
538
  preferences?: Record<string, unknown> | null;
536
539
  }
540
+ /**
541
+ * Discriminant for redirect failures that callers need to handle
542
+ * programmatically (vs. logging the human-readable `error` string).
543
+ *
544
+ * - `'redirect_loop_detected'`: `redirectToAuthorize()` detected that the
545
+ * same authorize URL (keyed by `baseUrl + client_id + organization_id`)
546
+ * has been fired **3 times within 60 seconds**. Caller should surface a
547
+ * manual "再度ログインする" UI rather than retry automatically (#2406 / #2411).
548
+ *
549
+ * SDK 2.18.0 only ships `'redirect_loop_detected'`. Other failure paths
550
+ * (SSR environment, PKCE generation failure, empty organizationId) currently
551
+ * return `error` without a `code`. New codes will be added in future minor
552
+ * versions — treat this union as forward-extensible and do NOT exhaustively
553
+ * `switch` over it without a `default` branch for consumer code that must
554
+ * stay compatible across SDK upgrades.
555
+ */
556
+ type FFIDRedirectErrorCode = 'redirect_loop_detected';
537
557
  /**
538
558
  * Result of a redirect operation (redirectToLogin / redirectToAuthorize / redirectToLogout)
539
559
  *
540
560
  * Structured return type so callers can inspect failure reasons
541
- * instead of receiving a bare `false`.
561
+ * instead of receiving a bare `false`. When `code` is set, branch on it
562
+ * for programmatic handling; otherwise log `error` for humans.
542
563
  */
543
564
  type FFIDRedirectResult = {
544
565
  success: true;
545
566
  } | {
546
567
  success: false;
547
568
  error: string;
569
+ code?: FFIDRedirectErrorCode;
548
570
  };
549
571
  /**
550
572
  * OAuth 2.0 token response from FFID token endpoint
@@ -1279,4 +1301,4 @@ interface FFIDInquiryFormPlaceholderContext {
1279
1301
  }
1280
1302
  declare function FFIDInquiryForm({ mode, prefill, organizations, preselectedOrganizationId, categories, termsVersion, privacyVersion, termsHref, privacyHref, turnstileToken, turnstileSlot, onSubmit, onChange, separateLegalCheckboxes, messagePlaceholder, requireCategorySelection, unstyled, classNames, locale, className, }: FFIDInquiryFormProps): react_jsx_runtime.JSX.Element;
1281
1303
 
1282
- export { type FFIDInquiryFormSubmitData as $, type FFIDSubscription as A, type FFIDSubscriptionContextValue as B, type FFIDAnnouncementsClientConfig as C, type FFIDAnnouncementsApiResponse as D, type AnnouncementListResponse as E, type FFIDSubscriptionStatus as F, type FFIDAnnouncementsLogger as G, type Announcement as H, type AnnouncementStatus as I, type AnnouncementType as J, FFIDAnnouncementBadge as K, type ListAnnouncementsOptions as L, FFIDAnnouncementList as M, type FFIDAnnouncementsError as N, type FFIDAnnouncementsErrorCode as O, type FFIDAnnouncementsServerResponse as P, type FFIDCacheConfig as Q, type FFIDContextValue as R, type FFIDInquiryCategory as S, type FFIDInquiryCategorySite2026 as T, FFIDInquiryForm as U, type FFIDInquiryFormCategoryItem as V, type FFIDInquiryFormClassNames as W, type FFIDInquiryFormOrganization as X, type FFIDInquiryFormPlaceholderContext as Y, type FFIDInquiryFormPrefill as Z, type FFIDInquiryFormProps as _, type FFIDConfig as a, type FFIDInquiryFormSubmitResult as a0, type FFIDJwtClaims as a1, FFIDLoginButton as a2, type FFIDMemberStatus as a3, type FFIDOAuthTokenResponse as a4, type FFIDOAuthUserInfoMemberRole as a5, type FFIDOAuthUserInfoSubscription as a6, type FFIDOrganizationMember as a7, FFIDOrganizationSwitcher as a8, type FFIDSeatModel as a9, FFIDSubscriptionBadge as aa, type FFIDTokenIntrospectionResponse as ab, FFIDUserMenu as ac, FFID_INQUIRY_CATEGORIES as ad, FFID_INQUIRY_CATEGORIES_SITE_2026 as ae, type UseFFIDAnnouncementsOptions as af, type UseFFIDAnnouncementsReturn as ag, isFFIDInquiryCategorySite2026 as ah, useFFIDAnnouncements as ai, type FFIDAnnouncementBadgeClassNames as aj, type FFIDAnnouncementBadgeProps as ak, type FFIDAnnouncementListClassNames as al, type FFIDAnnouncementListProps as am, type FFIDLoginButtonProps as an, type FFIDOrganizationSwitcherClassNames as ao, type FFIDOrganizationSwitcherProps as ap, type FFIDSubscriptionBadgeClassNames as aq, type FFIDSubscriptionBadgeProps as ar, type FFIDUserMenuClassNames as as, type FFIDUserMenuProps as at, type FFIDApiResponse as b, type FFIDSessionResponse as c, type FFIDRedirectResult as d, type FFIDError as e, type FFIDSubscriptionCheckResponse as f, type FFIDListMembersResponse as g, type FFIDMemberRole as h, type FFIDUpdateMemberRoleResponse as i, type FFIDRemoveMemberResponse as j, type FFIDProfileCallOptions as k, type FFIDUserProfile as l, type FFIDUpdateUserProfileRequest as m, type FFIDCreateCheckoutParams as n, type FFIDCheckoutSessionResponse as o, type FFIDCreatePortalParams as p, type FFIDPortalSessionResponse as q, type FFIDVerifyAccessTokenOptions as r, type FFIDOAuthUserInfo as s, type FFIDInquiryCreateParams as t, type FFIDInquiryCreateResponse as u, type FFIDAuthMode as v, type FFIDLogger as w, type FFIDCacheAdapter as x, type FFIDUser as y, type FFIDOrganization as z };
1304
+ export { type FFIDInquiryFormSubmitData as $, type FFIDSubscription as A, type FFIDSubscriptionContextValue as B, type FFIDAnnouncementsClientConfig as C, type FFIDAnnouncementsApiResponse as D, type AnnouncementListResponse as E, type FFIDSubscriptionStatus as F, type FFIDAnnouncementsLogger as G, type Announcement as H, type AnnouncementStatus as I, type AnnouncementType as J, FFIDAnnouncementBadge as K, type ListAnnouncementsOptions as L, FFIDAnnouncementList as M, type FFIDAnnouncementsError as N, type FFIDAnnouncementsErrorCode as O, type FFIDAnnouncementsServerResponse as P, type FFIDCacheConfig as Q, type FFIDContextValue as R, type FFIDInquiryCategory as S, type FFIDInquiryCategorySite2026 as T, FFIDInquiryForm as U, type FFIDInquiryFormCategoryItem as V, type FFIDInquiryFormClassNames as W, type FFIDInquiryFormOrganization as X, type FFIDInquiryFormPlaceholderContext as Y, type FFIDInquiryFormPrefill as Z, type FFIDInquiryFormProps as _, type FFIDConfig as a, type FFIDInquiryFormSubmitResult as a0, type FFIDJwtClaims as a1, FFIDLoginButton as a2, type FFIDMemberStatus as a3, type FFIDOAuthTokenResponse as a4, type FFIDOAuthUserInfoMemberRole as a5, type FFIDOAuthUserInfoSubscription as a6, type FFIDOrganizationMember as a7, FFIDOrganizationSwitcher as a8, type FFIDRedirectErrorCode as a9, type FFIDSeatModel as aa, FFIDSubscriptionBadge as ab, type FFIDTokenIntrospectionResponse as ac, FFIDUserMenu as ad, FFID_INQUIRY_CATEGORIES as ae, FFID_INQUIRY_CATEGORIES_SITE_2026 as af, type UseFFIDAnnouncementsOptions as ag, type UseFFIDAnnouncementsReturn as ah, isFFIDInquiryCategorySite2026 as ai, useFFIDAnnouncements as aj, type FFIDAnnouncementBadgeClassNames as ak, type FFIDAnnouncementBadgeProps as al, type FFIDAnnouncementListClassNames as am, type FFIDAnnouncementListProps as an, type FFIDLoginButtonProps as ao, type FFIDOrganizationSwitcherClassNames as ap, type FFIDOrganizationSwitcherProps as aq, type FFIDSubscriptionBadgeClassNames as ar, type FFIDSubscriptionBadgeProps as as, type FFIDUserMenuClassNames as at, type FFIDUserMenuProps as au, type FFIDApiResponse as b, type FFIDSessionResponse as c, type FFIDRedirectResult as d, type FFIDError as e, type FFIDSubscriptionCheckResponse as f, type FFIDListMembersResponse as g, type FFIDMemberRole as h, type FFIDUpdateMemberRoleResponse as i, type FFIDRemoveMemberResponse as j, type FFIDProfileCallOptions as k, type FFIDUserProfile as l, type FFIDUpdateUserProfileRequest as m, type FFIDCreateCheckoutParams as n, type FFIDCheckoutSessionResponse as o, type FFIDCreatePortalParams as p, type FFIDPortalSessionResponse as q, type FFIDVerifyAccessTokenOptions as r, type FFIDOAuthUserInfo as s, type FFIDInquiryCreateParams as t, type FFIDInquiryCreateResponse as u, type FFIDAuthMode as v, type FFIDLogger as w, type FFIDCacheAdapter as x, type FFIDUser as y, type FFIDOrganization as z };
package/dist/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkOWE6EPWE_cjs = require('./chunk-OWE6EPWE.cjs');
3
+ var chunkEK2W67BW_cjs = require('./chunk-EK2W67BW.cjs');
4
4
  var react = require('react');
5
5
  var jsxRuntime = require('react/jsx-runtime');
6
6
 
@@ -46,7 +46,7 @@ function createKVCacheAdapter(kv) {
46
46
  }
47
47
  function withFFIDAuth(Component, options = {}) {
48
48
  const WrappedComponent = (props) => {
49
- const { isLoading, isAuthenticated, login } = chunkOWE6EPWE_cjs.useFFIDContext();
49
+ const { isLoading, isAuthenticated, login } = chunkEK2W67BW_cjs.useFFIDContext();
50
50
  const hasRedirected = react.useRef(false);
51
51
  react.useEffect(() => {
52
52
  if (!isLoading && !isAuthenticated && options.redirectToLogin && !hasRedirected.current) {
@@ -74,107 +74,107 @@ var FFID_NEWSLETTER_TYPES = ["inquiry_followup", "general"];
74
74
 
75
75
  Object.defineProperty(exports, "DEFAULT_API_BASE_URL", {
76
76
  enumerable: true,
77
- get: function () { return chunkOWE6EPWE_cjs.DEFAULT_API_BASE_URL; }
77
+ get: function () { return chunkEK2W67BW_cjs.DEFAULT_API_BASE_URL; }
78
78
  });
79
79
  Object.defineProperty(exports, "FFIDAnnouncementBadge", {
80
80
  enumerable: true,
81
- get: function () { return chunkOWE6EPWE_cjs.FFIDAnnouncementBadge; }
81
+ get: function () { return chunkEK2W67BW_cjs.FFIDAnnouncementBadge; }
82
82
  });
83
83
  Object.defineProperty(exports, "FFIDAnnouncementList", {
84
84
  enumerable: true,
85
- get: function () { return chunkOWE6EPWE_cjs.FFIDAnnouncementList; }
85
+ get: function () { return chunkEK2W67BW_cjs.FFIDAnnouncementList; }
86
86
  });
87
87
  Object.defineProperty(exports, "FFIDInquiryForm", {
88
88
  enumerable: true,
89
- get: function () { return chunkOWE6EPWE_cjs.FFIDInquiryForm; }
89
+ get: function () { return chunkEK2W67BW_cjs.FFIDInquiryForm; }
90
90
  });
91
91
  Object.defineProperty(exports, "FFIDLoginButton", {
92
92
  enumerable: true,
93
- get: function () { return chunkOWE6EPWE_cjs.FFIDLoginButton; }
93
+ get: function () { return chunkEK2W67BW_cjs.FFIDLoginButton; }
94
94
  });
95
95
  Object.defineProperty(exports, "FFIDOrganizationSwitcher", {
96
96
  enumerable: true,
97
- get: function () { return chunkOWE6EPWE_cjs.FFIDOrganizationSwitcher; }
97
+ get: function () { return chunkEK2W67BW_cjs.FFIDOrganizationSwitcher; }
98
98
  });
99
99
  Object.defineProperty(exports, "FFIDProvider", {
100
100
  enumerable: true,
101
- get: function () { return chunkOWE6EPWE_cjs.FFIDProvider; }
101
+ get: function () { return chunkEK2W67BW_cjs.FFIDProvider; }
102
102
  });
103
103
  Object.defineProperty(exports, "FFIDSDKError", {
104
104
  enumerable: true,
105
- get: function () { return chunkOWE6EPWE_cjs.FFIDSDKError; }
105
+ get: function () { return chunkEK2W67BW_cjs.FFIDSDKError; }
106
106
  });
107
107
  Object.defineProperty(exports, "FFIDSubscriptionBadge", {
108
108
  enumerable: true,
109
- get: function () { return chunkOWE6EPWE_cjs.FFIDSubscriptionBadge; }
109
+ get: function () { return chunkEK2W67BW_cjs.FFIDSubscriptionBadge; }
110
110
  });
111
111
  Object.defineProperty(exports, "FFIDUserMenu", {
112
112
  enumerable: true,
113
- get: function () { return chunkOWE6EPWE_cjs.FFIDUserMenu; }
113
+ get: function () { return chunkEK2W67BW_cjs.FFIDUserMenu; }
114
114
  });
115
115
  Object.defineProperty(exports, "FFID_ANNOUNCEMENTS_ERROR_CODES", {
116
116
  enumerable: true,
117
- get: function () { return chunkOWE6EPWE_cjs.FFID_ANNOUNCEMENTS_ERROR_CODES; }
117
+ get: function () { return chunkEK2W67BW_cjs.FFID_ANNOUNCEMENTS_ERROR_CODES; }
118
118
  });
119
119
  Object.defineProperty(exports, "FFID_INQUIRY_CATEGORIES", {
120
120
  enumerable: true,
121
- get: function () { return chunkOWE6EPWE_cjs.FFID_INQUIRY_CATEGORIES; }
121
+ get: function () { return chunkEK2W67BW_cjs.FFID_INQUIRY_CATEGORIES; }
122
122
  });
123
123
  Object.defineProperty(exports, "FFID_INQUIRY_CATEGORIES_SITE_2026", {
124
124
  enumerable: true,
125
- get: function () { return chunkOWE6EPWE_cjs.FFID_INQUIRY_CATEGORIES_SITE_2026; }
125
+ get: function () { return chunkEK2W67BW_cjs.FFID_INQUIRY_CATEGORIES_SITE_2026; }
126
126
  });
127
127
  Object.defineProperty(exports, "createFFIDAnnouncementsClient", {
128
128
  enumerable: true,
129
- get: function () { return chunkOWE6EPWE_cjs.createFFIDAnnouncementsClient; }
129
+ get: function () { return chunkEK2W67BW_cjs.createFFIDAnnouncementsClient; }
130
130
  });
131
131
  Object.defineProperty(exports, "createFFIDClient", {
132
132
  enumerable: true,
133
- get: function () { return chunkOWE6EPWE_cjs.createFFIDClient; }
133
+ get: function () { return chunkEK2W67BW_cjs.createFFIDClient; }
134
134
  });
135
135
  Object.defineProperty(exports, "createTokenStore", {
136
136
  enumerable: true,
137
- get: function () { return chunkOWE6EPWE_cjs.createTokenStore; }
137
+ get: function () { return chunkEK2W67BW_cjs.createTokenStore; }
138
138
  });
139
139
  Object.defineProperty(exports, "generateCodeChallenge", {
140
140
  enumerable: true,
141
- get: function () { return chunkOWE6EPWE_cjs.generateCodeChallenge; }
141
+ get: function () { return chunkEK2W67BW_cjs.generateCodeChallenge; }
142
142
  });
143
143
  Object.defineProperty(exports, "generateCodeVerifier", {
144
144
  enumerable: true,
145
- get: function () { return chunkOWE6EPWE_cjs.generateCodeVerifier; }
145
+ get: function () { return chunkEK2W67BW_cjs.generateCodeVerifier; }
146
146
  });
147
147
  Object.defineProperty(exports, "isFFIDInquiryCategorySite2026", {
148
148
  enumerable: true,
149
- get: function () { return chunkOWE6EPWE_cjs.isFFIDInquiryCategorySite2026; }
149
+ get: function () { return chunkEK2W67BW_cjs.isFFIDInquiryCategorySite2026; }
150
150
  });
151
151
  Object.defineProperty(exports, "normalizeRedirectUri", {
152
152
  enumerable: true,
153
- get: function () { return chunkOWE6EPWE_cjs.normalizeRedirectUri; }
153
+ get: function () { return chunkEK2W67BW_cjs.normalizeRedirectUri; }
154
154
  });
155
155
  Object.defineProperty(exports, "retrieveCodeVerifier", {
156
156
  enumerable: true,
157
- get: function () { return chunkOWE6EPWE_cjs.retrieveCodeVerifier; }
157
+ get: function () { return chunkEK2W67BW_cjs.retrieveCodeVerifier; }
158
158
  });
159
159
  Object.defineProperty(exports, "storeCodeVerifier", {
160
160
  enumerable: true,
161
- get: function () { return chunkOWE6EPWE_cjs.storeCodeVerifier; }
161
+ get: function () { return chunkEK2W67BW_cjs.storeCodeVerifier; }
162
162
  });
163
163
  Object.defineProperty(exports, "useFFID", {
164
164
  enumerable: true,
165
- get: function () { return chunkOWE6EPWE_cjs.useFFID; }
165
+ get: function () { return chunkEK2W67BW_cjs.useFFID; }
166
166
  });
167
167
  Object.defineProperty(exports, "useFFIDAnnouncements", {
168
168
  enumerable: true,
169
- get: function () { return chunkOWE6EPWE_cjs.useFFIDAnnouncements; }
169
+ get: function () { return chunkEK2W67BW_cjs.useFFIDAnnouncements; }
170
170
  });
171
171
  Object.defineProperty(exports, "useSubscription", {
172
172
  enumerable: true,
173
- get: function () { return chunkOWE6EPWE_cjs.useSubscription; }
173
+ get: function () { return chunkEK2W67BW_cjs.useSubscription; }
174
174
  });
175
175
  Object.defineProperty(exports, "withSubscription", {
176
176
  enumerable: true,
177
- get: function () { return chunkOWE6EPWE_cjs.withSubscription; }
177
+ get: function () { return chunkEK2W67BW_cjs.withSubscription; }
178
178
  });
179
179
  exports.FFID_NEWSLETTER_TYPES = FFID_NEWSLETTER_TYPES;
180
180
  exports.createKVCacheAdapter = createKVCacheAdapter;
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { F as FFIDSubscriptionStatus, a as FFIDConfig, b as FFIDApiResponse, c as FFIDSessionResponse, d as FFIDRedirectResult, e as FFIDError, f as FFIDSubscriptionCheckResponse, g as FFIDListMembersResponse, h as FFIDMemberRole, i as FFIDUpdateMemberRoleResponse, j as FFIDRemoveMemberResponse, k as FFIDProfileCallOptions, l as FFIDUserProfile, m as FFIDUpdateUserProfileRequest, n as FFIDCreateCheckoutParams, o as FFIDCheckoutSessionResponse, p as FFIDCreatePortalParams, q as FFIDPortalSessionResponse, r as FFIDVerifyAccessTokenOptions, s as FFIDOAuthUserInfo, t as FFIDInquiryCreateParams, u as FFIDInquiryCreateResponse, v as FFIDAuthMode, w as FFIDLogger, x as FFIDCacheAdapter, y as FFIDUser, z as FFIDOrganization, A as FFIDSubscription, B as FFIDSubscriptionContextValue, C as FFIDAnnouncementsClientConfig, L as ListAnnouncementsOptions, D as FFIDAnnouncementsApiResponse, E as AnnouncementListResponse, G as FFIDAnnouncementsLogger } from './index-DbEyptzr.cjs';
2
- export { H as Announcement, I as AnnouncementStatus, J as AnnouncementType, K as FFIDAnnouncementBadge, M as FFIDAnnouncementList, N as FFIDAnnouncementsError, O as FFIDAnnouncementsErrorCode, P as FFIDAnnouncementsServerResponse, Q as FFIDCacheConfig, R as FFIDContextValue, S as FFIDInquiryCategory, T as FFIDInquiryCategorySite2026, U as FFIDInquiryForm, V as FFIDInquiryFormCategoryItem, W as FFIDInquiryFormClassNames, X as FFIDInquiryFormOrganization, Y as FFIDInquiryFormPlaceholderContext, Z as FFIDInquiryFormPrefill, _ as FFIDInquiryFormProps, $ as FFIDInquiryFormSubmitData, a0 as FFIDInquiryFormSubmitResult, a1 as FFIDJwtClaims, a2 as FFIDLoginButton, a3 as FFIDMemberStatus, a4 as FFIDOAuthTokenResponse, a5 as FFIDOAuthUserInfoMemberRole, a6 as FFIDOAuthUserInfoSubscription, a7 as FFIDOrganizationMember, a8 as FFIDOrganizationSwitcher, a9 as FFIDSeatModel, aa as FFIDSubscriptionBadge, ab as FFIDTokenIntrospectionResponse, ac as FFIDUserMenu, ad as FFID_INQUIRY_CATEGORIES, ae as FFID_INQUIRY_CATEGORIES_SITE_2026, af as UseFFIDAnnouncementsOptions, ag as UseFFIDAnnouncementsReturn, ah as isFFIDInquiryCategorySite2026, ai as useFFIDAnnouncements } from './index-DbEyptzr.cjs';
1
+ import { F as FFIDSubscriptionStatus, a as FFIDConfig, b as FFIDApiResponse, c as FFIDSessionResponse, d as FFIDRedirectResult, e as FFIDError, f as FFIDSubscriptionCheckResponse, g as FFIDListMembersResponse, h as FFIDMemberRole, i as FFIDUpdateMemberRoleResponse, j as FFIDRemoveMemberResponse, k as FFIDProfileCallOptions, l as FFIDUserProfile, m as FFIDUpdateUserProfileRequest, n as FFIDCreateCheckoutParams, o as FFIDCheckoutSessionResponse, p as FFIDCreatePortalParams, q as FFIDPortalSessionResponse, r as FFIDVerifyAccessTokenOptions, s as FFIDOAuthUserInfo, t as FFIDInquiryCreateParams, u as FFIDInquiryCreateResponse, v as FFIDAuthMode, w as FFIDLogger, x as FFIDCacheAdapter, y as FFIDUser, z as FFIDOrganization, A as FFIDSubscription, B as FFIDSubscriptionContextValue, C as FFIDAnnouncementsClientConfig, L as ListAnnouncementsOptions, D as FFIDAnnouncementsApiResponse, E as AnnouncementListResponse, G as FFIDAnnouncementsLogger } from './index--S6rLHjr.cjs';
2
+ export { H as Announcement, I as AnnouncementStatus, J as AnnouncementType, K as FFIDAnnouncementBadge, M as FFIDAnnouncementList, N as FFIDAnnouncementsError, O as FFIDAnnouncementsErrorCode, P as FFIDAnnouncementsServerResponse, Q as FFIDCacheConfig, R as FFIDContextValue, S as FFIDInquiryCategory, T as FFIDInquiryCategorySite2026, U as FFIDInquiryForm, V as FFIDInquiryFormCategoryItem, W as FFIDInquiryFormClassNames, X as FFIDInquiryFormOrganization, Y as FFIDInquiryFormPlaceholderContext, Z as FFIDInquiryFormPrefill, _ as FFIDInquiryFormProps, $ as FFIDInquiryFormSubmitData, a0 as FFIDInquiryFormSubmitResult, a1 as FFIDJwtClaims, a2 as FFIDLoginButton, a3 as FFIDMemberStatus, a4 as FFIDOAuthTokenResponse, a5 as FFIDOAuthUserInfoMemberRole, a6 as FFIDOAuthUserInfoSubscription, a7 as FFIDOrganizationMember, a8 as FFIDOrganizationSwitcher, a9 as FFIDRedirectErrorCode, aa as FFIDSeatModel, ab as FFIDSubscriptionBadge, ac as FFIDTokenIntrospectionResponse, ad as FFIDUserMenu, ae as FFID_INQUIRY_CATEGORIES, af as FFID_INQUIRY_CATEGORIES_SITE_2026, ag as UseFFIDAnnouncementsOptions, ah as UseFFIDAnnouncementsReturn, ai as isFFIDInquiryCategorySite2026, aj as useFFIDAnnouncements } from './index--S6rLHjr.cjs';
3
3
  import * as react_jsx_runtime from 'react/jsx-runtime';
4
4
  import { ReactNode, ComponentType, FC } from 'react';
5
5
 
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { F as FFIDSubscriptionStatus, a as FFIDConfig, b as FFIDApiResponse, c as FFIDSessionResponse, d as FFIDRedirectResult, e as FFIDError, f as FFIDSubscriptionCheckResponse, g as FFIDListMembersResponse, h as FFIDMemberRole, i as FFIDUpdateMemberRoleResponse, j as FFIDRemoveMemberResponse, k as FFIDProfileCallOptions, l as FFIDUserProfile, m as FFIDUpdateUserProfileRequest, n as FFIDCreateCheckoutParams, o as FFIDCheckoutSessionResponse, p as FFIDCreatePortalParams, q as FFIDPortalSessionResponse, r as FFIDVerifyAccessTokenOptions, s as FFIDOAuthUserInfo, t as FFIDInquiryCreateParams, u as FFIDInquiryCreateResponse, v as FFIDAuthMode, w as FFIDLogger, x as FFIDCacheAdapter, y as FFIDUser, z as FFIDOrganization, A as FFIDSubscription, B as FFIDSubscriptionContextValue, C as FFIDAnnouncementsClientConfig, L as ListAnnouncementsOptions, D as FFIDAnnouncementsApiResponse, E as AnnouncementListResponse, G as FFIDAnnouncementsLogger } from './index-DbEyptzr.js';
2
- export { H as Announcement, I as AnnouncementStatus, J as AnnouncementType, K as FFIDAnnouncementBadge, M as FFIDAnnouncementList, N as FFIDAnnouncementsError, O as FFIDAnnouncementsErrorCode, P as FFIDAnnouncementsServerResponse, Q as FFIDCacheConfig, R as FFIDContextValue, S as FFIDInquiryCategory, T as FFIDInquiryCategorySite2026, U as FFIDInquiryForm, V as FFIDInquiryFormCategoryItem, W as FFIDInquiryFormClassNames, X as FFIDInquiryFormOrganization, Y as FFIDInquiryFormPlaceholderContext, Z as FFIDInquiryFormPrefill, _ as FFIDInquiryFormProps, $ as FFIDInquiryFormSubmitData, a0 as FFIDInquiryFormSubmitResult, a1 as FFIDJwtClaims, a2 as FFIDLoginButton, a3 as FFIDMemberStatus, a4 as FFIDOAuthTokenResponse, a5 as FFIDOAuthUserInfoMemberRole, a6 as FFIDOAuthUserInfoSubscription, a7 as FFIDOrganizationMember, a8 as FFIDOrganizationSwitcher, a9 as FFIDSeatModel, aa as FFIDSubscriptionBadge, ab as FFIDTokenIntrospectionResponse, ac as FFIDUserMenu, ad as FFID_INQUIRY_CATEGORIES, ae as FFID_INQUIRY_CATEGORIES_SITE_2026, af as UseFFIDAnnouncementsOptions, ag as UseFFIDAnnouncementsReturn, ah as isFFIDInquiryCategorySite2026, ai as useFFIDAnnouncements } from './index-DbEyptzr.js';
1
+ import { F as FFIDSubscriptionStatus, a as FFIDConfig, b as FFIDApiResponse, c as FFIDSessionResponse, d as FFIDRedirectResult, e as FFIDError, f as FFIDSubscriptionCheckResponse, g as FFIDListMembersResponse, h as FFIDMemberRole, i as FFIDUpdateMemberRoleResponse, j as FFIDRemoveMemberResponse, k as FFIDProfileCallOptions, l as FFIDUserProfile, m as FFIDUpdateUserProfileRequest, n as FFIDCreateCheckoutParams, o as FFIDCheckoutSessionResponse, p as FFIDCreatePortalParams, q as FFIDPortalSessionResponse, r as FFIDVerifyAccessTokenOptions, s as FFIDOAuthUserInfo, t as FFIDInquiryCreateParams, u as FFIDInquiryCreateResponse, v as FFIDAuthMode, w as FFIDLogger, x as FFIDCacheAdapter, y as FFIDUser, z as FFIDOrganization, A as FFIDSubscription, B as FFIDSubscriptionContextValue, C as FFIDAnnouncementsClientConfig, L as ListAnnouncementsOptions, D as FFIDAnnouncementsApiResponse, E as AnnouncementListResponse, G as FFIDAnnouncementsLogger } from './index--S6rLHjr.js';
2
+ export { H as Announcement, I as AnnouncementStatus, J as AnnouncementType, K as FFIDAnnouncementBadge, M as FFIDAnnouncementList, N as FFIDAnnouncementsError, O as FFIDAnnouncementsErrorCode, P as FFIDAnnouncementsServerResponse, Q as FFIDCacheConfig, R as FFIDContextValue, S as FFIDInquiryCategory, T as FFIDInquiryCategorySite2026, U as FFIDInquiryForm, V as FFIDInquiryFormCategoryItem, W as FFIDInquiryFormClassNames, X as FFIDInquiryFormOrganization, Y as FFIDInquiryFormPlaceholderContext, Z as FFIDInquiryFormPrefill, _ as FFIDInquiryFormProps, $ as FFIDInquiryFormSubmitData, a0 as FFIDInquiryFormSubmitResult, a1 as FFIDJwtClaims, a2 as FFIDLoginButton, a3 as FFIDMemberStatus, a4 as FFIDOAuthTokenResponse, a5 as FFIDOAuthUserInfoMemberRole, a6 as FFIDOAuthUserInfoSubscription, a7 as FFIDOrganizationMember, a8 as FFIDOrganizationSwitcher, a9 as FFIDRedirectErrorCode, aa as FFIDSeatModel, ab as FFIDSubscriptionBadge, ac as FFIDTokenIntrospectionResponse, ad as FFIDUserMenu, ae as FFID_INQUIRY_CATEGORIES, af as FFID_INQUIRY_CATEGORIES_SITE_2026, ag as UseFFIDAnnouncementsOptions, ah as UseFFIDAnnouncementsReturn, ai as isFFIDInquiryCategorySite2026, aj as useFFIDAnnouncements } from './index--S6rLHjr.js';
3
3
  import * as react_jsx_runtime from 'react/jsx-runtime';
4
4
  import { ReactNode, ComponentType, FC } from 'react';
5
5
 
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { useFFIDContext } from './chunk-O2YI5N6S.js';
2
- export { DEFAULT_API_BASE_URL, FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDInquiryForm, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDProvider, FFIDSDKError, FFIDSubscriptionBadge, FFIDUserMenu, FFID_ANNOUNCEMENTS_ERROR_CODES, FFID_INQUIRY_CATEGORIES, FFID_INQUIRY_CATEGORIES_SITE_2026, createFFIDAnnouncementsClient, createFFIDClient, createTokenStore, generateCodeChallenge, generateCodeVerifier, isFFIDInquiryCategorySite2026, normalizeRedirectUri, retrieveCodeVerifier, storeCodeVerifier, useFFID, useFFIDAnnouncements, useSubscription, withSubscription } from './chunk-O2YI5N6S.js';
1
+ import { useFFIDContext } from './chunk-IEYXT3LA.js';
2
+ export { DEFAULT_API_BASE_URL, FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDInquiryForm, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDProvider, FFIDSDKError, FFIDSubscriptionBadge, FFIDUserMenu, FFID_ANNOUNCEMENTS_ERROR_CODES, FFID_INQUIRY_CATEGORIES, FFID_INQUIRY_CATEGORIES_SITE_2026, createFFIDAnnouncementsClient, createFFIDClient, createTokenStore, generateCodeChallenge, generateCodeVerifier, isFFIDInquiryCategorySite2026, normalizeRedirectUri, retrieveCodeVerifier, storeCodeVerifier, useFFID, useFFIDAnnouncements, useSubscription, withSubscription } from './chunk-IEYXT3LA.js';
3
3
  import { useRef, useEffect } from 'react';
4
4
  import { jsx, Fragment } from 'react/jsx-runtime';
5
5
 
@@ -101,10 +101,7 @@ function createTokenStore(storageType) {
101
101
  // src/client/oauth-userinfo.ts
102
102
  var SESSION_ELIGIBLE_SUBSCRIPTION_STATUSES = [
103
103
  "trialing",
104
- "active",
105
- "past_due",
106
- "canceled",
107
- "paused"
104
+ "active"
108
105
  ];
109
106
  function isSessionEligibleSubscriptionStatus(value) {
110
107
  return typeof value === "string" && SESSION_ELIGIBLE_SUBSCRIPTION_STATUSES.includes(value);
@@ -806,10 +803,11 @@ function createProfileMethods(deps) {
806
803
  }
807
804
 
808
805
  // src/client/version-check.ts
809
- var SDK_VERSION = "2.17.0";
806
+ var SDK_VERSION = "2.18.0";
810
807
  var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
811
808
  var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
812
809
  function sdkHeaders() {
810
+ if (typeof window !== "undefined") return {};
813
811
  return {
814
812
  "User-Agent": SDK_USER_AGENT,
815
813
  [SDK_VERSION_HEADER]: SDK_VERSION
@@ -1302,6 +1300,19 @@ function storeCodeVerifier(verifier, logger) {
1302
1300
  }
1303
1301
  return sessionStored || localStored;
1304
1302
  }
1303
+ function cleanupVerifierStorage(logger) {
1304
+ try {
1305
+ window.sessionStorage.removeItem(VERIFIER_STORAGE_KEY);
1306
+ } catch (error) {
1307
+ logger?.warn("retrieveCodeVerifier: sessionStorage \u306E\u30AF\u30EA\u30FC\u30F3\u30A2\u30C3\u30D7\u306B\u5931\u6557\u3057\u307E\u3057\u305F:", error);
1308
+ }
1309
+ try {
1310
+ window.localStorage.removeItem(VERIFIER_FALLBACK_STORAGE_KEY);
1311
+ window.localStorage.removeItem(VERIFIER_FALLBACK_TIMESTAMP_KEY);
1312
+ } catch (error) {
1313
+ logger?.warn("retrieveCodeVerifier: localStorage \u306E\u30AF\u30EA\u30FC\u30F3\u30A2\u30C3\u30D7\u306B\u5931\u6557\u3057\u307E\u3057\u305F:", error);
1314
+ }
1315
+ }
1305
1316
  function base64UrlEncode(buffer) {
1306
1317
  const bytes = new Uint8Array(buffer);
1307
1318
  let binary = "";
@@ -1316,6 +1327,87 @@ var OAUTH_AUTHORIZE_ENDPOINT = "/api/v1/oauth/authorize";
1316
1327
  var AUTH_LOGOUT_ENDPOINT = "/api/v1/auth/logout";
1317
1328
  var STATE_RANDOM_BYTES = 16;
1318
1329
  var HEX_BASE2 = 16;
1330
+ var REDIRECT_LOOP_KEY = "ffid_sdk_redirect_loop_history";
1331
+ var REDIRECT_LOOP_WINDOW_MS = 6e4;
1332
+ var REDIRECT_LOOP_THRESHOLD = 3;
1333
+ var storageReadFailureLogged = false;
1334
+ var storageWriteFailureLogged = false;
1335
+ function logStorageReadFailure(logger, err) {
1336
+ if (storageReadFailureLogged) return;
1337
+ storageReadFailureLogged = true;
1338
+ logger.warn(
1339
+ "[FFID SDK] sessionStorage read failed \u2014 redirect loop detection disabled on this browser (fail-open). iOS WebKit private mode / eviction \u304C\u5178\u578B\u8981\u56E0",
1340
+ err
1341
+ );
1342
+ }
1343
+ function logStorageWriteFailure(logger, err) {
1344
+ if (storageWriteFailureLogged) return;
1345
+ storageWriteFailureLogged = true;
1346
+ logger.warn(
1347
+ "[FFID SDK] sessionStorage write failed \u2014 redirect loop detection disabled on this browser (fail-open).",
1348
+ err
1349
+ );
1350
+ }
1351
+ function isRedirectLoopHistory(value) {
1352
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
1353
+ return Object.values(value).every(
1354
+ (arr) => Array.isArray(arr) && arr.every((t) => typeof t === "number" && Number.isFinite(t))
1355
+ );
1356
+ }
1357
+ function readRedirectLoopHistory(logger) {
1358
+ if (typeof window === "undefined") return {};
1359
+ try {
1360
+ const raw = window.sessionStorage.getItem(REDIRECT_LOOP_KEY);
1361
+ if (raw === null) return {};
1362
+ const parsed = JSON.parse(raw);
1363
+ return isRedirectLoopHistory(parsed) ? parsed : {};
1364
+ } catch (err) {
1365
+ logStorageReadFailure(logger, err);
1366
+ return {};
1367
+ }
1368
+ }
1369
+ function writeRedirectLoopHistory(history, logger) {
1370
+ if (typeof window === "undefined") return;
1371
+ try {
1372
+ window.sessionStorage.setItem(REDIRECT_LOOP_KEY, JSON.stringify(history));
1373
+ } catch (err) {
1374
+ logStorageWriteFailure(logger, err);
1375
+ }
1376
+ }
1377
+ function pruneRedirectLoopHistory(history, now) {
1378
+ const cutoff = now - REDIRECT_LOOP_WINDOW_MS;
1379
+ const pruned = {};
1380
+ for (const [key, timestamps] of Object.entries(history)) {
1381
+ const fresh = timestamps.filter((t) => t > cutoff);
1382
+ if (fresh.length > 0) pruned[key] = fresh;
1383
+ }
1384
+ return pruned;
1385
+ }
1386
+ function getRecentRedirectCount(authorizeKey, now, logger) {
1387
+ const pruned = pruneRedirectLoopHistory(readRedirectLoopHistory(logger), now);
1388
+ writeRedirectLoopHistory(pruned, logger);
1389
+ return pruned[authorizeKey]?.length ?? 0;
1390
+ }
1391
+ function recordRedirectAttempt(authorizeKey, now, logger) {
1392
+ const history = readRedirectLoopHistory(logger);
1393
+ const existing = history[authorizeKey] ?? [];
1394
+ history[authorizeKey] = [...existing, now];
1395
+ writeRedirectLoopHistory(history, logger);
1396
+ }
1397
+ function rollbackLastRedirectAttempt(authorizeKey, logger) {
1398
+ const history = readRedirectLoopHistory(logger);
1399
+ const existing = history[authorizeKey];
1400
+ if (!Array.isArray(existing) || existing.length === 0) return;
1401
+ history[authorizeKey] = existing.slice(0, -1);
1402
+ if (history[authorizeKey].length === 0) delete history[authorizeKey];
1403
+ writeRedirectLoopHistory(history, logger);
1404
+ }
1405
+ function buildAuthorizeKey(baseUrl, clientId, organizationId) {
1406
+ const params = new URLSearchParams({ client_id: clientId });
1407
+ const trimmedOrg = organizationId?.trim();
1408
+ if (trimmedOrg) params.set("organization_id", trimmedOrg);
1409
+ return `${baseUrl}${OAUTH_AUTHORIZE_ENDPOINT}?${params.toString()}`;
1410
+ }
1319
1411
  function generateRandomState() {
1320
1412
  const array = new Uint8Array(STATE_RANDOM_BYTES);
1321
1413
  crypto.getRandomValues(array);
@@ -1335,6 +1427,23 @@ function createRedirectMethods(deps) {
1335
1427
  logger.warn("SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093");
1336
1428
  return { success: false, error: "SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093" };
1337
1429
  }
1430
+ const authorizeKey = buildAuthorizeKey(baseUrl, clientId, options?.organizationId);
1431
+ const now = Date.now();
1432
+ const recentCount = getRecentRedirectCount(authorizeKey, now, logger);
1433
+ if (recentCount >= REDIRECT_LOOP_THRESHOLD) {
1434
+ cleanupVerifierStorage(logger);
1435
+ logger.warn("[FFID SDK] redirect loop detected \u2014 \u81EA\u52D5\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3092\u505C\u6B62\u3057\u307E\u3057\u305F", {
1436
+ authorizeKey,
1437
+ recentCount,
1438
+ windowMs: REDIRECT_LOOP_WINDOW_MS,
1439
+ threshold: REDIRECT_LOOP_THRESHOLD
1440
+ });
1441
+ return {
1442
+ success: false,
1443
+ code: "redirect_loop_detected",
1444
+ error: "\u77ED\u6642\u9593\u306B\u540C\u3058\u8A8D\u53EF\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u3078\u306E\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u304C\u7E70\u308A\u8FD4\u3055\u308C\u305F\u305F\u3081\u3001\u30EB\u30FC\u30D7\u691C\u51FA\u306B\u3088\u308A\u81EA\u52D5\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3092\u505C\u6B62\u3057\u307E\u3057\u305F"
1445
+ };
1446
+ }
1338
1447
  const verifier = generateCodeVerifier();
1339
1448
  storeCodeVerifier(verifier, logger);
1340
1449
  let challenge;
@@ -1365,7 +1474,13 @@ function createRedirectMethods(deps) {
1365
1474
  }
1366
1475
  const authorizeUrl = `${baseUrl}${OAUTH_AUTHORIZE_ENDPOINT}?${params.toString()}`;
1367
1476
  logger.debug("Redirecting to authorize:", authorizeUrl);
1368
- window.location.href = authorizeUrl;
1477
+ recordRedirectAttempt(authorizeKey, now, logger);
1478
+ try {
1479
+ window.location.href = authorizeUrl;
1480
+ } catch (err) {
1481
+ rollbackLastRedirectAttempt(authorizeKey, logger);
1482
+ throw err;
1483
+ }
1369
1484
  return { success: true };
1370
1485
  }
1371
1486
  async function redirectToLogin() {
@@ -825,8 +825,11 @@ interface FFIDUserProfile {
825
825
  * When `accessToken` is supplied with a non-empty value, it overrides the client's
826
826
  * configured auth mode: authentication for that request uses only
827
827
  * `Authorization: Bearer <accessToken>` (no service key, no cookie, no token-store
828
- * lookup, no auto-refresh on 401). Non-auth headers such as `Content-Type` and
829
- * SDK metadata headers (User-Agent / X-FFID-SDK-Version) are still attached.
828
+ * lookup, no auto-refresh on 401). Non-auth headers such as `Content-Type` are
829
+ * still attached. SDK metadata headers (`User-Agent` / `X-FFID-SDK-Version`) are
830
+ * attached on non-browser runtimes only (Node.js / Cloudflare Workers / Edge /
831
+ * Deno); browsers receive an empty object to avoid iOS WebKit `fetch()` breakage
832
+ * (see #2417).
830
833
  *
831
834
  * Runtime semantics:
832
835
  * - `accessToken` omitted (or `undefined`) → no override, configured `authMode` is used
@@ -888,17 +891,36 @@ interface FFIDUpdateUserProfileRequest {
888
891
  /** Arbitrary user preferences bag (null clears the column; reads return {}) */
889
892
  preferences?: Record<string, unknown> | null;
890
893
  }
894
+ /**
895
+ * Discriminant for redirect failures that callers need to handle
896
+ * programmatically (vs. logging the human-readable `error` string).
897
+ *
898
+ * - `'redirect_loop_detected'`: `redirectToAuthorize()` detected that the
899
+ * same authorize URL (keyed by `baseUrl + client_id + organization_id`)
900
+ * has been fired **3 times within 60 seconds**. Caller should surface a
901
+ * manual "再度ログインする" UI rather than retry automatically (#2406 / #2411).
902
+ *
903
+ * SDK 2.18.0 only ships `'redirect_loop_detected'`. Other failure paths
904
+ * (SSR environment, PKCE generation failure, empty organizationId) currently
905
+ * return `error` without a `code`. New codes will be added in future minor
906
+ * versions — treat this union as forward-extensible and do NOT exhaustively
907
+ * `switch` over it without a `default` branch for consumer code that must
908
+ * stay compatible across SDK upgrades.
909
+ */
910
+ type FFIDRedirectErrorCode = 'redirect_loop_detected';
891
911
  /**
892
912
  * Result of a redirect operation (redirectToLogin / redirectToAuthorize / redirectToLogout)
893
913
  *
894
914
  * Structured return type so callers can inspect failure reasons
895
- * instead of receiving a bare `false`.
915
+ * instead of receiving a bare `false`. When `code` is set, branch on it
916
+ * for programmatic handling; otherwise log `error` for humans.
896
917
  */
897
918
  type FFIDRedirectResult = {
898
919
  success: true;
899
920
  } | {
900
921
  success: false;
901
922
  error: string;
923
+ code?: FFIDRedirectErrorCode;
902
924
  };
903
925
 
904
926
  /** OTP / magic link methods - sendOtp / verifyOtp */
@@ -825,8 +825,11 @@ interface FFIDUserProfile {
825
825
  * When `accessToken` is supplied with a non-empty value, it overrides the client's
826
826
  * configured auth mode: authentication for that request uses only
827
827
  * `Authorization: Bearer <accessToken>` (no service key, no cookie, no token-store
828
- * lookup, no auto-refresh on 401). Non-auth headers such as `Content-Type` and
829
- * SDK metadata headers (User-Agent / X-FFID-SDK-Version) are still attached.
828
+ * lookup, no auto-refresh on 401). Non-auth headers such as `Content-Type` are
829
+ * still attached. SDK metadata headers (`User-Agent` / `X-FFID-SDK-Version`) are
830
+ * attached on non-browser runtimes only (Node.js / Cloudflare Workers / Edge /
831
+ * Deno); browsers receive an empty object to avoid iOS WebKit `fetch()` breakage
832
+ * (see #2417).
830
833
  *
831
834
  * Runtime semantics:
832
835
  * - `accessToken` omitted (or `undefined`) → no override, configured `authMode` is used
@@ -888,17 +891,36 @@ interface FFIDUpdateUserProfileRequest {
888
891
  /** Arbitrary user preferences bag (null clears the column; reads return {}) */
889
892
  preferences?: Record<string, unknown> | null;
890
893
  }
894
+ /**
895
+ * Discriminant for redirect failures that callers need to handle
896
+ * programmatically (vs. logging the human-readable `error` string).
897
+ *
898
+ * - `'redirect_loop_detected'`: `redirectToAuthorize()` detected that the
899
+ * same authorize URL (keyed by `baseUrl + client_id + organization_id`)
900
+ * has been fired **3 times within 60 seconds**. Caller should surface a
901
+ * manual "再度ログインする" UI rather than retry automatically (#2406 / #2411).
902
+ *
903
+ * SDK 2.18.0 only ships `'redirect_loop_detected'`. Other failure paths
904
+ * (SSR environment, PKCE generation failure, empty organizationId) currently
905
+ * return `error` without a `code`. New codes will be added in future minor
906
+ * versions — treat this union as forward-extensible and do NOT exhaustively
907
+ * `switch` over it without a `default` branch for consumer code that must
908
+ * stay compatible across SDK upgrades.
909
+ */
910
+ type FFIDRedirectErrorCode = 'redirect_loop_detected';
891
911
  /**
892
912
  * Result of a redirect operation (redirectToLogin / redirectToAuthorize / redirectToLogout)
893
913
  *
894
914
  * Structured return type so callers can inspect failure reasons
895
- * instead of receiving a bare `false`.
915
+ * instead of receiving a bare `false`. When `code` is set, branch on it
916
+ * for programmatic handling; otherwise log `error` for humans.
896
917
  */
897
918
  type FFIDRedirectResult = {
898
919
  success: true;
899
920
  } | {
900
921
  success: false;
901
922
  error: string;
923
+ code?: FFIDRedirectErrorCode;
902
924
  };
903
925
 
904
926
  /** OTP / magic link methods - sendOtp / verifyOtp */
@@ -100,10 +100,7 @@ function createTokenStore(storageType) {
100
100
  // src/client/oauth-userinfo.ts
101
101
  var SESSION_ELIGIBLE_SUBSCRIPTION_STATUSES = [
102
102
  "trialing",
103
- "active",
104
- "past_due",
105
- "canceled",
106
- "paused"
103
+ "active"
107
104
  ];
108
105
  function isSessionEligibleSubscriptionStatus(value) {
109
106
  return typeof value === "string" && SESSION_ELIGIBLE_SUBSCRIPTION_STATUSES.includes(value);
@@ -805,10 +802,11 @@ function createProfileMethods(deps) {
805
802
  }
806
803
 
807
804
  // src/client/version-check.ts
808
- var SDK_VERSION = "2.17.0";
805
+ var SDK_VERSION = "2.18.0";
809
806
  var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
810
807
  var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
811
808
  function sdkHeaders() {
809
+ if (typeof window !== "undefined") return {};
812
810
  return {
813
811
  "User-Agent": SDK_USER_AGENT,
814
812
  [SDK_VERSION_HEADER]: SDK_VERSION
@@ -1301,6 +1299,19 @@ function storeCodeVerifier(verifier, logger) {
1301
1299
  }
1302
1300
  return sessionStored || localStored;
1303
1301
  }
1302
+ function cleanupVerifierStorage(logger) {
1303
+ try {
1304
+ window.sessionStorage.removeItem(VERIFIER_STORAGE_KEY);
1305
+ } catch (error) {
1306
+ logger?.warn("retrieveCodeVerifier: sessionStorage \u306E\u30AF\u30EA\u30FC\u30F3\u30A2\u30C3\u30D7\u306B\u5931\u6557\u3057\u307E\u3057\u305F:", error);
1307
+ }
1308
+ try {
1309
+ window.localStorage.removeItem(VERIFIER_FALLBACK_STORAGE_KEY);
1310
+ window.localStorage.removeItem(VERIFIER_FALLBACK_TIMESTAMP_KEY);
1311
+ } catch (error) {
1312
+ logger?.warn("retrieveCodeVerifier: localStorage \u306E\u30AF\u30EA\u30FC\u30F3\u30A2\u30C3\u30D7\u306B\u5931\u6557\u3057\u307E\u3057\u305F:", error);
1313
+ }
1314
+ }
1304
1315
  function base64UrlEncode(buffer) {
1305
1316
  const bytes = new Uint8Array(buffer);
1306
1317
  let binary = "";
@@ -1315,6 +1326,87 @@ var OAUTH_AUTHORIZE_ENDPOINT = "/api/v1/oauth/authorize";
1315
1326
  var AUTH_LOGOUT_ENDPOINT = "/api/v1/auth/logout";
1316
1327
  var STATE_RANDOM_BYTES = 16;
1317
1328
  var HEX_BASE2 = 16;
1329
+ var REDIRECT_LOOP_KEY = "ffid_sdk_redirect_loop_history";
1330
+ var REDIRECT_LOOP_WINDOW_MS = 6e4;
1331
+ var REDIRECT_LOOP_THRESHOLD = 3;
1332
+ var storageReadFailureLogged = false;
1333
+ var storageWriteFailureLogged = false;
1334
+ function logStorageReadFailure(logger, err) {
1335
+ if (storageReadFailureLogged) return;
1336
+ storageReadFailureLogged = true;
1337
+ logger.warn(
1338
+ "[FFID SDK] sessionStorage read failed \u2014 redirect loop detection disabled on this browser (fail-open). iOS WebKit private mode / eviction \u304C\u5178\u578B\u8981\u56E0",
1339
+ err
1340
+ );
1341
+ }
1342
+ function logStorageWriteFailure(logger, err) {
1343
+ if (storageWriteFailureLogged) return;
1344
+ storageWriteFailureLogged = true;
1345
+ logger.warn(
1346
+ "[FFID SDK] sessionStorage write failed \u2014 redirect loop detection disabled on this browser (fail-open).",
1347
+ err
1348
+ );
1349
+ }
1350
+ function isRedirectLoopHistory(value) {
1351
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
1352
+ return Object.values(value).every(
1353
+ (arr) => Array.isArray(arr) && arr.every((t) => typeof t === "number" && Number.isFinite(t))
1354
+ );
1355
+ }
1356
+ function readRedirectLoopHistory(logger) {
1357
+ if (typeof window === "undefined") return {};
1358
+ try {
1359
+ const raw = window.sessionStorage.getItem(REDIRECT_LOOP_KEY);
1360
+ if (raw === null) return {};
1361
+ const parsed = JSON.parse(raw);
1362
+ return isRedirectLoopHistory(parsed) ? parsed : {};
1363
+ } catch (err) {
1364
+ logStorageReadFailure(logger, err);
1365
+ return {};
1366
+ }
1367
+ }
1368
+ function writeRedirectLoopHistory(history, logger) {
1369
+ if (typeof window === "undefined") return;
1370
+ try {
1371
+ window.sessionStorage.setItem(REDIRECT_LOOP_KEY, JSON.stringify(history));
1372
+ } catch (err) {
1373
+ logStorageWriteFailure(logger, err);
1374
+ }
1375
+ }
1376
+ function pruneRedirectLoopHistory(history, now) {
1377
+ const cutoff = now - REDIRECT_LOOP_WINDOW_MS;
1378
+ const pruned = {};
1379
+ for (const [key, timestamps] of Object.entries(history)) {
1380
+ const fresh = timestamps.filter((t) => t > cutoff);
1381
+ if (fresh.length > 0) pruned[key] = fresh;
1382
+ }
1383
+ return pruned;
1384
+ }
1385
+ function getRecentRedirectCount(authorizeKey, now, logger) {
1386
+ const pruned = pruneRedirectLoopHistory(readRedirectLoopHistory(logger), now);
1387
+ writeRedirectLoopHistory(pruned, logger);
1388
+ return pruned[authorizeKey]?.length ?? 0;
1389
+ }
1390
+ function recordRedirectAttempt(authorizeKey, now, logger) {
1391
+ const history = readRedirectLoopHistory(logger);
1392
+ const existing = history[authorizeKey] ?? [];
1393
+ history[authorizeKey] = [...existing, now];
1394
+ writeRedirectLoopHistory(history, logger);
1395
+ }
1396
+ function rollbackLastRedirectAttempt(authorizeKey, logger) {
1397
+ const history = readRedirectLoopHistory(logger);
1398
+ const existing = history[authorizeKey];
1399
+ if (!Array.isArray(existing) || existing.length === 0) return;
1400
+ history[authorizeKey] = existing.slice(0, -1);
1401
+ if (history[authorizeKey].length === 0) delete history[authorizeKey];
1402
+ writeRedirectLoopHistory(history, logger);
1403
+ }
1404
+ function buildAuthorizeKey(baseUrl, clientId, organizationId) {
1405
+ const params = new URLSearchParams({ client_id: clientId });
1406
+ const trimmedOrg = organizationId?.trim();
1407
+ if (trimmedOrg) params.set("organization_id", trimmedOrg);
1408
+ return `${baseUrl}${OAUTH_AUTHORIZE_ENDPOINT}?${params.toString()}`;
1409
+ }
1318
1410
  function generateRandomState() {
1319
1411
  const array = new Uint8Array(STATE_RANDOM_BYTES);
1320
1412
  crypto.getRandomValues(array);
@@ -1334,6 +1426,23 @@ function createRedirectMethods(deps) {
1334
1426
  logger.warn("SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093");
1335
1427
  return { success: false, error: "SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093" };
1336
1428
  }
1429
+ const authorizeKey = buildAuthorizeKey(baseUrl, clientId, options?.organizationId);
1430
+ const now = Date.now();
1431
+ const recentCount = getRecentRedirectCount(authorizeKey, now, logger);
1432
+ if (recentCount >= REDIRECT_LOOP_THRESHOLD) {
1433
+ cleanupVerifierStorage(logger);
1434
+ logger.warn("[FFID SDK] redirect loop detected \u2014 \u81EA\u52D5\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3092\u505C\u6B62\u3057\u307E\u3057\u305F", {
1435
+ authorizeKey,
1436
+ recentCount,
1437
+ windowMs: REDIRECT_LOOP_WINDOW_MS,
1438
+ threshold: REDIRECT_LOOP_THRESHOLD
1439
+ });
1440
+ return {
1441
+ success: false,
1442
+ code: "redirect_loop_detected",
1443
+ error: "\u77ED\u6642\u9593\u306B\u540C\u3058\u8A8D\u53EF\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u3078\u306E\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u304C\u7E70\u308A\u8FD4\u3055\u308C\u305F\u305F\u3081\u3001\u30EB\u30FC\u30D7\u691C\u51FA\u306B\u3088\u308A\u81EA\u52D5\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3092\u505C\u6B62\u3057\u307E\u3057\u305F"
1444
+ };
1445
+ }
1337
1446
  const verifier = generateCodeVerifier();
1338
1447
  storeCodeVerifier(verifier, logger);
1339
1448
  let challenge;
@@ -1364,7 +1473,13 @@ function createRedirectMethods(deps) {
1364
1473
  }
1365
1474
  const authorizeUrl = `${baseUrl}${OAUTH_AUTHORIZE_ENDPOINT}?${params.toString()}`;
1366
1475
  logger.debug("Redirecting to authorize:", authorizeUrl);
1367
- window.location.href = authorizeUrl;
1476
+ recordRedirectAttempt(authorizeKey, now, logger);
1477
+ try {
1478
+ window.location.href = authorizeUrl;
1479
+ } catch (err) {
1480
+ rollbackLastRedirectAttempt(authorizeKey, logger);
1481
+ throw err;
1482
+ }
1368
1483
  return { success: true };
1369
1484
  }
1370
1485
  async function redirectToLogin() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feelflow/ffid-sdk",
3
- "version": "2.17.0",
3
+ "version": "2.18.0",
4
4
  "description": "FeelFlow ID Platform SDK for React/Next.js applications",
5
5
  "keywords": [
6
6
  "feelflow",