@feelflow/ffid-sdk 2.17.1 → 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.
- package/dist/{chunk-DERFBYBZ.cjs → chunk-EK2W67BW.cjs} +107 -6
- package/dist/{chunk-FGTRPNSW.js → chunk-IEYXT3LA.js} +107 -6
- package/dist/components/index.cjs +8 -8
- package/dist/components/index.d.cts +1 -1
- package/dist/components/index.d.ts +1 -1
- package/dist/components/index.js +1 -1
- package/dist/{index-Cv1qXIl1.d.cts → index--S6rLHjr.d.cts} +21 -2
- package/dist/{index-Cv1qXIl1.d.ts → index--S6rLHjr.d.ts} +21 -2
- package/dist/index.cjs +28 -28
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/server/index.cjs +120 -6
- package/dist/server/index.d.cts +20 -1
- package/dist/server/index.d.ts +20 -1
- package/dist/server/index.js +120 -6
- package/package.json +1 -1
|
@@ -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,7 +807,7 @@ function createProfileMethods(deps) {
|
|
|
810
807
|
}
|
|
811
808
|
|
|
812
809
|
// src/client/version-check.ts
|
|
813
|
-
var SDK_VERSION = "2.
|
|
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() {
|
|
@@ -1373,6 +1370,87 @@ var OAUTH_AUTHORIZE_ENDPOINT = "/api/v1/oauth/authorize";
|
|
|
1373
1370
|
var AUTH_LOGOUT_ENDPOINT = "/api/v1/auth/logout";
|
|
1374
1371
|
var STATE_RANDOM_BYTES = 16;
|
|
1375
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
|
+
}
|
|
1376
1454
|
function generateRandomState() {
|
|
1377
1455
|
const array = new Uint8Array(STATE_RANDOM_BYTES);
|
|
1378
1456
|
crypto.getRandomValues(array);
|
|
@@ -1392,6 +1470,23 @@ function createRedirectMethods(deps) {
|
|
|
1392
1470
|
logger.warn("SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093");
|
|
1393
1471
|
return { success: false, error: "SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093" };
|
|
1394
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
|
+
}
|
|
1395
1490
|
const verifier = generateCodeVerifier();
|
|
1396
1491
|
storeCodeVerifier(verifier, logger);
|
|
1397
1492
|
let challenge;
|
|
@@ -1422,7 +1517,13 @@ function createRedirectMethods(deps) {
|
|
|
1422
1517
|
}
|
|
1423
1518
|
const authorizeUrl = `${baseUrl}${OAUTH_AUTHORIZE_ENDPOINT}?${params.toString()}`;
|
|
1424
1519
|
logger.debug("Redirecting to authorize:", authorizeUrl);
|
|
1425
|
-
|
|
1520
|
+
recordRedirectAttempt(authorizeKey, now, logger);
|
|
1521
|
+
try {
|
|
1522
|
+
window.location.href = authorizeUrl;
|
|
1523
|
+
} catch (err) {
|
|
1524
|
+
rollbackLastRedirectAttempt(authorizeKey, logger);
|
|
1525
|
+
throw err;
|
|
1526
|
+
}
|
|
1426
1527
|
return { success: true };
|
|
1427
1528
|
}
|
|
1428
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,7 +805,7 @@ function createProfileMethods(deps) {
|
|
|
808
805
|
}
|
|
809
806
|
|
|
810
807
|
// src/client/version-check.ts
|
|
811
|
-
var SDK_VERSION = "2.
|
|
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() {
|
|
@@ -1371,6 +1368,87 @@ var OAUTH_AUTHORIZE_ENDPOINT = "/api/v1/oauth/authorize";
|
|
|
1371
1368
|
var AUTH_LOGOUT_ENDPOINT = "/api/v1/auth/logout";
|
|
1372
1369
|
var STATE_RANDOM_BYTES = 16;
|
|
1373
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
|
+
}
|
|
1374
1452
|
function generateRandomState() {
|
|
1375
1453
|
const array = new Uint8Array(STATE_RANDOM_BYTES);
|
|
1376
1454
|
crypto.getRandomValues(array);
|
|
@@ -1390,6 +1468,23 @@ function createRedirectMethods(deps) {
|
|
|
1390
1468
|
logger.warn("SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093");
|
|
1391
1469
|
return { success: false, error: "SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093" };
|
|
1392
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
|
+
}
|
|
1393
1488
|
const verifier = generateCodeVerifier();
|
|
1394
1489
|
storeCodeVerifier(verifier, logger);
|
|
1395
1490
|
let challenge;
|
|
@@ -1420,7 +1515,13 @@ function createRedirectMethods(deps) {
|
|
|
1420
1515
|
}
|
|
1421
1516
|
const authorizeUrl = `${baseUrl}${OAUTH_AUTHORIZE_ENDPOINT}?${params.toString()}`;
|
|
1422
1517
|
logger.debug("Redirecting to authorize:", authorizeUrl);
|
|
1423
|
-
|
|
1518
|
+
recordRedirectAttempt(authorizeKey, now, logger);
|
|
1519
|
+
try {
|
|
1520
|
+
window.location.href = authorizeUrl;
|
|
1521
|
+
} catch (err) {
|
|
1522
|
+
rollbackLastRedirectAttempt(authorizeKey, logger);
|
|
1523
|
+
throw err;
|
|
1524
|
+
}
|
|
1424
1525
|
return { success: true };
|
|
1425
1526
|
}
|
|
1426
1527
|
async function redirectToLogin() {
|
|
@@ -1,34 +1,34 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
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
|
|
9
|
+
get: function () { return chunkEK2W67BW_cjs.FFIDAnnouncementBadge; }
|
|
10
10
|
});
|
|
11
11
|
Object.defineProperty(exports, "FFIDAnnouncementList", {
|
|
12
12
|
enumerable: true,
|
|
13
|
-
get: function () { return
|
|
13
|
+
get: function () { return chunkEK2W67BW_cjs.FFIDAnnouncementList; }
|
|
14
14
|
});
|
|
15
15
|
Object.defineProperty(exports, "FFIDInquiryForm", {
|
|
16
16
|
enumerable: true,
|
|
17
|
-
get: function () { return
|
|
17
|
+
get: function () { return chunkEK2W67BW_cjs.FFIDInquiryForm; }
|
|
18
18
|
});
|
|
19
19
|
Object.defineProperty(exports, "FFIDLoginButton", {
|
|
20
20
|
enumerable: true,
|
|
21
|
-
get: function () { return
|
|
21
|
+
get: function () { return chunkEK2W67BW_cjs.FFIDLoginButton; }
|
|
22
22
|
});
|
|
23
23
|
Object.defineProperty(exports, "FFIDOrganizationSwitcher", {
|
|
24
24
|
enumerable: true,
|
|
25
|
-
get: function () { return
|
|
25
|
+
get: function () { return chunkEK2W67BW_cjs.FFIDOrganizationSwitcher; }
|
|
26
26
|
});
|
|
27
27
|
Object.defineProperty(exports, "FFIDSubscriptionBadge", {
|
|
28
28
|
enumerable: true,
|
|
29
|
-
get: function () { return
|
|
29
|
+
get: function () { return chunkEK2W67BW_cjs.FFIDSubscriptionBadge; }
|
|
30
30
|
});
|
|
31
31
|
Object.defineProperty(exports, "FFIDUserMenu", {
|
|
32
32
|
enumerable: true,
|
|
33
|
-
get: function () { return
|
|
33
|
+
get: function () { return chunkEK2W67BW_cjs.FFIDUserMenu; }
|
|
34
34
|
});
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { K as FFIDAnnouncementBadge,
|
|
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,
|
|
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';
|
package/dist/components/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDInquiryForm, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDSubscriptionBadge, FFIDUserMenu } from '../chunk-
|
|
1
|
+
export { FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDInquiryForm, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDSubscriptionBadge, FFIDUserMenu } from '../chunk-IEYXT3LA.js';
|
|
@@ -537,17 +537,36 @@ interface FFIDUpdateUserProfileRequest {
|
|
|
537
537
|
/** Arbitrary user preferences bag (null clears the column; reads return {}) */
|
|
538
538
|
preferences?: Record<string, unknown> | null;
|
|
539
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';
|
|
540
557
|
/**
|
|
541
558
|
* Result of a redirect operation (redirectToLogin / redirectToAuthorize / redirectToLogout)
|
|
542
559
|
*
|
|
543
560
|
* Structured return type so callers can inspect failure reasons
|
|
544
|
-
* 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.
|
|
545
563
|
*/
|
|
546
564
|
type FFIDRedirectResult = {
|
|
547
565
|
success: true;
|
|
548
566
|
} | {
|
|
549
567
|
success: false;
|
|
550
568
|
error: string;
|
|
569
|
+
code?: FFIDRedirectErrorCode;
|
|
551
570
|
};
|
|
552
571
|
/**
|
|
553
572
|
* OAuth 2.0 token response from FFID token endpoint
|
|
@@ -1282,4 +1301,4 @@ interface FFIDInquiryFormPlaceholderContext {
|
|
|
1282
1301
|
}
|
|
1283
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;
|
|
1284
1303
|
|
|
1285
|
-
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
|
|
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 };
|
|
@@ -537,17 +537,36 @@ interface FFIDUpdateUserProfileRequest {
|
|
|
537
537
|
/** Arbitrary user preferences bag (null clears the column; reads return {}) */
|
|
538
538
|
preferences?: Record<string, unknown> | null;
|
|
539
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';
|
|
540
557
|
/**
|
|
541
558
|
* Result of a redirect operation (redirectToLogin / redirectToAuthorize / redirectToLogout)
|
|
542
559
|
*
|
|
543
560
|
* Structured return type so callers can inspect failure reasons
|
|
544
|
-
* 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.
|
|
545
563
|
*/
|
|
546
564
|
type FFIDRedirectResult = {
|
|
547
565
|
success: true;
|
|
548
566
|
} | {
|
|
549
567
|
success: false;
|
|
550
568
|
error: string;
|
|
569
|
+
code?: FFIDRedirectErrorCode;
|
|
551
570
|
};
|
|
552
571
|
/**
|
|
553
572
|
* OAuth 2.0 token response from FFID token endpoint
|
|
@@ -1282,4 +1301,4 @@ interface FFIDInquiryFormPlaceholderContext {
|
|
|
1282
1301
|
}
|
|
1283
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;
|
|
1284
1303
|
|
|
1285
|
-
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
|
|
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
|
|
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 } =
|
|
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
|
|
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
|
|
81
|
+
get: function () { return chunkEK2W67BW_cjs.FFIDAnnouncementBadge; }
|
|
82
82
|
});
|
|
83
83
|
Object.defineProperty(exports, "FFIDAnnouncementList", {
|
|
84
84
|
enumerable: true,
|
|
85
|
-
get: function () { return
|
|
85
|
+
get: function () { return chunkEK2W67BW_cjs.FFIDAnnouncementList; }
|
|
86
86
|
});
|
|
87
87
|
Object.defineProperty(exports, "FFIDInquiryForm", {
|
|
88
88
|
enumerable: true,
|
|
89
|
-
get: function () { return
|
|
89
|
+
get: function () { return chunkEK2W67BW_cjs.FFIDInquiryForm; }
|
|
90
90
|
});
|
|
91
91
|
Object.defineProperty(exports, "FFIDLoginButton", {
|
|
92
92
|
enumerable: true,
|
|
93
|
-
get: function () { return
|
|
93
|
+
get: function () { return chunkEK2W67BW_cjs.FFIDLoginButton; }
|
|
94
94
|
});
|
|
95
95
|
Object.defineProperty(exports, "FFIDOrganizationSwitcher", {
|
|
96
96
|
enumerable: true,
|
|
97
|
-
get: function () { return
|
|
97
|
+
get: function () { return chunkEK2W67BW_cjs.FFIDOrganizationSwitcher; }
|
|
98
98
|
});
|
|
99
99
|
Object.defineProperty(exports, "FFIDProvider", {
|
|
100
100
|
enumerable: true,
|
|
101
|
-
get: function () { return
|
|
101
|
+
get: function () { return chunkEK2W67BW_cjs.FFIDProvider; }
|
|
102
102
|
});
|
|
103
103
|
Object.defineProperty(exports, "FFIDSDKError", {
|
|
104
104
|
enumerable: true,
|
|
105
|
-
get: function () { return
|
|
105
|
+
get: function () { return chunkEK2W67BW_cjs.FFIDSDKError; }
|
|
106
106
|
});
|
|
107
107
|
Object.defineProperty(exports, "FFIDSubscriptionBadge", {
|
|
108
108
|
enumerable: true,
|
|
109
|
-
get: function () { return
|
|
109
|
+
get: function () { return chunkEK2W67BW_cjs.FFIDSubscriptionBadge; }
|
|
110
110
|
});
|
|
111
111
|
Object.defineProperty(exports, "FFIDUserMenu", {
|
|
112
112
|
enumerable: true,
|
|
113
|
-
get: function () { return
|
|
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
|
|
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
|
|
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
|
|
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
|
|
129
|
+
get: function () { return chunkEK2W67BW_cjs.createFFIDAnnouncementsClient; }
|
|
130
130
|
});
|
|
131
131
|
Object.defineProperty(exports, "createFFIDClient", {
|
|
132
132
|
enumerable: true,
|
|
133
|
-
get: function () { return
|
|
133
|
+
get: function () { return chunkEK2W67BW_cjs.createFFIDClient; }
|
|
134
134
|
});
|
|
135
135
|
Object.defineProperty(exports, "createTokenStore", {
|
|
136
136
|
enumerable: true,
|
|
137
|
-
get: function () { return
|
|
137
|
+
get: function () { return chunkEK2W67BW_cjs.createTokenStore; }
|
|
138
138
|
});
|
|
139
139
|
Object.defineProperty(exports, "generateCodeChallenge", {
|
|
140
140
|
enumerable: true,
|
|
141
|
-
get: function () { return
|
|
141
|
+
get: function () { return chunkEK2W67BW_cjs.generateCodeChallenge; }
|
|
142
142
|
});
|
|
143
143
|
Object.defineProperty(exports, "generateCodeVerifier", {
|
|
144
144
|
enumerable: true,
|
|
145
|
-
get: function () { return
|
|
145
|
+
get: function () { return chunkEK2W67BW_cjs.generateCodeVerifier; }
|
|
146
146
|
});
|
|
147
147
|
Object.defineProperty(exports, "isFFIDInquiryCategorySite2026", {
|
|
148
148
|
enumerable: true,
|
|
149
|
-
get: function () { return
|
|
149
|
+
get: function () { return chunkEK2W67BW_cjs.isFFIDInquiryCategorySite2026; }
|
|
150
150
|
});
|
|
151
151
|
Object.defineProperty(exports, "normalizeRedirectUri", {
|
|
152
152
|
enumerable: true,
|
|
153
|
-
get: function () { return
|
|
153
|
+
get: function () { return chunkEK2W67BW_cjs.normalizeRedirectUri; }
|
|
154
154
|
});
|
|
155
155
|
Object.defineProperty(exports, "retrieveCodeVerifier", {
|
|
156
156
|
enumerable: true,
|
|
157
|
-
get: function () { return
|
|
157
|
+
get: function () { return chunkEK2W67BW_cjs.retrieveCodeVerifier; }
|
|
158
158
|
});
|
|
159
159
|
Object.defineProperty(exports, "storeCodeVerifier", {
|
|
160
160
|
enumerable: true,
|
|
161
|
-
get: function () { return
|
|
161
|
+
get: function () { return chunkEK2W67BW_cjs.storeCodeVerifier; }
|
|
162
162
|
});
|
|
163
163
|
Object.defineProperty(exports, "useFFID", {
|
|
164
164
|
enumerable: true,
|
|
165
|
-
get: function () { return
|
|
165
|
+
get: function () { return chunkEK2W67BW_cjs.useFFID; }
|
|
166
166
|
});
|
|
167
167
|
Object.defineProperty(exports, "useFFIDAnnouncements", {
|
|
168
168
|
enumerable: true,
|
|
169
|
-
get: function () { return
|
|
169
|
+
get: function () { return chunkEK2W67BW_cjs.useFFIDAnnouncements; }
|
|
170
170
|
});
|
|
171
171
|
Object.defineProperty(exports, "useSubscription", {
|
|
172
172
|
enumerable: true,
|
|
173
|
-
get: function () { return
|
|
173
|
+
get: function () { return chunkEK2W67BW_cjs.useSubscription; }
|
|
174
174
|
});
|
|
175
175
|
Object.defineProperty(exports, "withSubscription", {
|
|
176
176
|
enumerable: true,
|
|
177
|
-
get: function () { return
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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-
|
|
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-
|
|
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
|
|
package/dist/server/index.cjs
CHANGED
|
@@ -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,7 +803,7 @@ function createProfileMethods(deps) {
|
|
|
806
803
|
}
|
|
807
804
|
|
|
808
805
|
// src/client/version-check.ts
|
|
809
|
-
var SDK_VERSION = "2.
|
|
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() {
|
|
@@ -1303,6 +1300,19 @@ function storeCodeVerifier(verifier, logger) {
|
|
|
1303
1300
|
}
|
|
1304
1301
|
return sessionStored || localStored;
|
|
1305
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
|
+
}
|
|
1306
1316
|
function base64UrlEncode(buffer) {
|
|
1307
1317
|
const bytes = new Uint8Array(buffer);
|
|
1308
1318
|
let binary = "";
|
|
@@ -1317,6 +1327,87 @@ var OAUTH_AUTHORIZE_ENDPOINT = "/api/v1/oauth/authorize";
|
|
|
1317
1327
|
var AUTH_LOGOUT_ENDPOINT = "/api/v1/auth/logout";
|
|
1318
1328
|
var STATE_RANDOM_BYTES = 16;
|
|
1319
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
|
+
}
|
|
1320
1411
|
function generateRandomState() {
|
|
1321
1412
|
const array = new Uint8Array(STATE_RANDOM_BYTES);
|
|
1322
1413
|
crypto.getRandomValues(array);
|
|
@@ -1336,6 +1427,23 @@ function createRedirectMethods(deps) {
|
|
|
1336
1427
|
logger.warn("SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093");
|
|
1337
1428
|
return { success: false, error: "SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093" };
|
|
1338
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
|
+
}
|
|
1339
1447
|
const verifier = generateCodeVerifier();
|
|
1340
1448
|
storeCodeVerifier(verifier, logger);
|
|
1341
1449
|
let challenge;
|
|
@@ -1366,7 +1474,13 @@ function createRedirectMethods(deps) {
|
|
|
1366
1474
|
}
|
|
1367
1475
|
const authorizeUrl = `${baseUrl}${OAUTH_AUTHORIZE_ENDPOINT}?${params.toString()}`;
|
|
1368
1476
|
logger.debug("Redirecting to authorize:", authorizeUrl);
|
|
1369
|
-
|
|
1477
|
+
recordRedirectAttempt(authorizeKey, now, logger);
|
|
1478
|
+
try {
|
|
1479
|
+
window.location.href = authorizeUrl;
|
|
1480
|
+
} catch (err) {
|
|
1481
|
+
rollbackLastRedirectAttempt(authorizeKey, logger);
|
|
1482
|
+
throw err;
|
|
1483
|
+
}
|
|
1370
1484
|
return { success: true };
|
|
1371
1485
|
}
|
|
1372
1486
|
async function redirectToLogin() {
|
package/dist/server/index.d.cts
CHANGED
|
@@ -891,17 +891,36 @@ interface FFIDUpdateUserProfileRequest {
|
|
|
891
891
|
/** Arbitrary user preferences bag (null clears the column; reads return {}) */
|
|
892
892
|
preferences?: Record<string, unknown> | null;
|
|
893
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';
|
|
894
911
|
/**
|
|
895
912
|
* Result of a redirect operation (redirectToLogin / redirectToAuthorize / redirectToLogout)
|
|
896
913
|
*
|
|
897
914
|
* Structured return type so callers can inspect failure reasons
|
|
898
|
-
* 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.
|
|
899
917
|
*/
|
|
900
918
|
type FFIDRedirectResult = {
|
|
901
919
|
success: true;
|
|
902
920
|
} | {
|
|
903
921
|
success: false;
|
|
904
922
|
error: string;
|
|
923
|
+
code?: FFIDRedirectErrorCode;
|
|
905
924
|
};
|
|
906
925
|
|
|
907
926
|
/** OTP / magic link methods - sendOtp / verifyOtp */
|
package/dist/server/index.d.ts
CHANGED
|
@@ -891,17 +891,36 @@ interface FFIDUpdateUserProfileRequest {
|
|
|
891
891
|
/** Arbitrary user preferences bag (null clears the column; reads return {}) */
|
|
892
892
|
preferences?: Record<string, unknown> | null;
|
|
893
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';
|
|
894
911
|
/**
|
|
895
912
|
* Result of a redirect operation (redirectToLogin / redirectToAuthorize / redirectToLogout)
|
|
896
913
|
*
|
|
897
914
|
* Structured return type so callers can inspect failure reasons
|
|
898
|
-
* 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.
|
|
899
917
|
*/
|
|
900
918
|
type FFIDRedirectResult = {
|
|
901
919
|
success: true;
|
|
902
920
|
} | {
|
|
903
921
|
success: false;
|
|
904
922
|
error: string;
|
|
923
|
+
code?: FFIDRedirectErrorCode;
|
|
905
924
|
};
|
|
906
925
|
|
|
907
926
|
/** OTP / magic link methods - sendOtp / verifyOtp */
|
package/dist/server/index.js
CHANGED
|
@@ -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,7 +802,7 @@ function createProfileMethods(deps) {
|
|
|
805
802
|
}
|
|
806
803
|
|
|
807
804
|
// src/client/version-check.ts
|
|
808
|
-
var SDK_VERSION = "2.
|
|
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() {
|
|
@@ -1302,6 +1299,19 @@ function storeCodeVerifier(verifier, logger) {
|
|
|
1302
1299
|
}
|
|
1303
1300
|
return sessionStored || localStored;
|
|
1304
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
|
+
}
|
|
1305
1315
|
function base64UrlEncode(buffer) {
|
|
1306
1316
|
const bytes = new Uint8Array(buffer);
|
|
1307
1317
|
let binary = "";
|
|
@@ -1316,6 +1326,87 @@ var OAUTH_AUTHORIZE_ENDPOINT = "/api/v1/oauth/authorize";
|
|
|
1316
1326
|
var AUTH_LOGOUT_ENDPOINT = "/api/v1/auth/logout";
|
|
1317
1327
|
var STATE_RANDOM_BYTES = 16;
|
|
1318
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
|
+
}
|
|
1319
1410
|
function generateRandomState() {
|
|
1320
1411
|
const array = new Uint8Array(STATE_RANDOM_BYTES);
|
|
1321
1412
|
crypto.getRandomValues(array);
|
|
@@ -1335,6 +1426,23 @@ function createRedirectMethods(deps) {
|
|
|
1335
1426
|
logger.warn("SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093");
|
|
1336
1427
|
return { success: false, error: "SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093" };
|
|
1337
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
|
+
}
|
|
1338
1446
|
const verifier = generateCodeVerifier();
|
|
1339
1447
|
storeCodeVerifier(verifier, logger);
|
|
1340
1448
|
let challenge;
|
|
@@ -1365,7 +1473,13 @@ function createRedirectMethods(deps) {
|
|
|
1365
1473
|
}
|
|
1366
1474
|
const authorizeUrl = `${baseUrl}${OAUTH_AUTHORIZE_ENDPOINT}?${params.toString()}`;
|
|
1367
1475
|
logger.debug("Redirecting to authorize:", authorizeUrl);
|
|
1368
|
-
|
|
1476
|
+
recordRedirectAttempt(authorizeKey, now, logger);
|
|
1477
|
+
try {
|
|
1478
|
+
window.location.href = authorizeUrl;
|
|
1479
|
+
} catch (err) {
|
|
1480
|
+
rollbackLastRedirectAttempt(authorizeKey, logger);
|
|
1481
|
+
throw err;
|
|
1482
|
+
}
|
|
1369
1483
|
return { success: true };
|
|
1370
1484
|
}
|
|
1371
1485
|
async function redirectToLogin() {
|