@feelflow/ffid-sdk 1.13.0 → 1.17.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.
@@ -113,6 +113,10 @@ function normalizeUserinfo(raw) {
113
113
  email: raw.email,
114
114
  name: raw.name,
115
115
  picture: raw.picture,
116
+ companyName: raw.company_name ?? null,
117
+ department: raw.department ?? null,
118
+ position: raw.position ?? null,
119
+ phoneNumber: raw.phone_number ?? null,
116
120
  organizationId: raw.organization_id ?? null,
117
121
  subscription: raw.subscription ? {
118
122
  subscriptionId: raw.subscription.subscription_id ?? null,
@@ -120,7 +124,8 @@ function normalizeUserinfo(raw) {
120
124
  planCode: raw.subscription.plan_code ?? null,
121
125
  seatModel: raw.subscription.seat_model ?? null,
122
126
  memberRole: raw.subscription.member_role ?? null,
123
- organizationId: raw.subscription.organization_id ?? null
127
+ organizationId: raw.subscription.organization_id ?? null,
128
+ hasSeatAssignment: raw.subscription.has_seat_assignment ?? null
124
129
  } : void 0
125
130
  };
126
131
  }
@@ -345,6 +350,10 @@ function createVerifyAccessToken(deps) {
345
350
  email: introspectResponse.email ?? null,
346
351
  name: introspectResponse.name ?? null,
347
352
  picture: introspectResponse.picture ?? null,
353
+ company_name: introspectResponse.company_name ?? null,
354
+ department: introspectResponse.department ?? null,
355
+ position: introspectResponse.position ?? null,
356
+ phone_number: introspectResponse.phone_number ?? null,
348
357
  organization_id: introspectResponse.organization_id ?? null
349
358
  };
350
359
  const raw = introspectResponse.subscription ? {
@@ -355,7 +364,8 @@ function createVerifyAccessToken(deps) {
355
364
  plan_code: introspectResponse.subscription.plan_code,
356
365
  seat_model: introspectResponse.subscription.seat_model,
357
366
  member_role: introspectResponse.subscription.member_role,
358
- organization_id: introspectResponse.subscription.organization_id
367
+ organization_id: introspectResponse.subscription.organization_id,
368
+ has_seat_assignment: introspectResponse.subscription.has_seat_assignment ?? false
359
369
  }
360
370
  } : base;
361
371
  const userinfo = normalizeUserinfo(raw);
@@ -437,7 +447,7 @@ function createBillingMethods(deps) {
437
447
  }
438
448
 
439
449
  // src/client/version-check.ts
440
- var SDK_VERSION = "1.13.0";
450
+ var SDK_VERSION = "1.17.0";
441
451
  var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
442
452
  var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
443
453
  function sdkHeaders() {
@@ -946,6 +956,7 @@ function base64UrlEncode(buffer) {
946
956
 
947
957
  // src/client/redirect.ts
948
958
  var OAUTH_AUTHORIZE_ENDPOINT = "/api/v1/oauth/authorize";
959
+ var AUTH_LOGOUT_ENDPOINT = "/api/v1/auth/logout";
949
960
  var STATE_RANDOM_BYTES = 16;
950
961
  var HEX_BASE2 = 16;
951
962
  function generateRandomState() {
@@ -962,7 +973,11 @@ function createRedirectMethods(deps) {
962
973
  resolvedRedirectUri,
963
974
  logger
964
975
  } = deps;
965
- async function redirectToAuthorize() {
976
+ async function redirectToAuthorize(options) {
977
+ if (typeof window === "undefined") {
978
+ logger.warn("SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093");
979
+ return { success: false, error: "SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093" };
980
+ }
966
981
  const verifier = generateCodeVerifier();
967
982
  storeCodeVerifier(verifier, logger);
968
983
  let challenge;
@@ -983,6 +998,14 @@ function createRedirectMethods(deps) {
983
998
  code_challenge: challenge,
984
999
  code_challenge_method: "S256"
985
1000
  });
1001
+ if (options?.organizationId !== void 0) {
1002
+ const trimmedOrgId = options.organizationId.trim();
1003
+ if (!trimmedOrgId) {
1004
+ logger.error("organizationId \u304C\u7A7A\u6587\u5B57\u3067\u3059");
1005
+ return { success: false, error: "organizationId \u306F\u7A7A\u306B\u3067\u304D\u307E\u305B\u3093" };
1006
+ }
1007
+ params.set("organization_id", trimmedOrgId);
1008
+ }
986
1009
  const authorizeUrl = `${baseUrl}${OAUTH_AUTHORIZE_ENDPOINT}?${params.toString()}`;
987
1010
  logger.debug("Redirecting to authorize:", authorizeUrl);
988
1011
  window.location.href = authorizeUrl;
@@ -1026,7 +1049,25 @@ function createRedirectMethods(deps) {
1026
1049
  }
1027
1050
  return `${baseUrl}/signup?redirect=${encodeURIComponent(redirect)}&service=${encodeURIComponent(serviceCode)}`;
1028
1051
  }
1029
- return { redirectToLogin, redirectToAuthorize, getLoginUrl, getSignupUrl };
1052
+ function getLogoutUrl(postLogoutRedirectUri) {
1053
+ const url = new URL(`${baseUrl}${AUTH_LOGOUT_ENDPOINT}`);
1054
+ url.searchParams.set("client_id", clientId);
1055
+ if (postLogoutRedirectUri != null) {
1056
+ url.searchParams.set("post_logout_redirect_uri", postLogoutRedirectUri);
1057
+ }
1058
+ return url.toString();
1059
+ }
1060
+ function redirectToLogout(postLogoutRedirectUri) {
1061
+ if (typeof window === "undefined") {
1062
+ logger.warn("SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093");
1063
+ return { success: false, error: "SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093" };
1064
+ }
1065
+ const logoutUrl = getLogoutUrl(postLogoutRedirectUri);
1066
+ logger.debug("Redirecting to logout:", logoutUrl);
1067
+ window.location.href = logoutUrl;
1068
+ return { success: true };
1069
+ }
1070
+ return { redirectToLogin, redirectToAuthorize, getLoginUrl, getSignupUrl, getLogoutUrl, redirectToLogout };
1030
1071
  }
1031
1072
 
1032
1073
  // src/client/password-reset.ts
@@ -1452,7 +1493,7 @@ function createFFIDClient(config) {
1452
1493
  }
1453
1494
  return signOutCookie();
1454
1495
  }
1455
- const { redirectToLogin, getLoginUrl, getSignupUrl } = createRedirectMethods({
1496
+ const { redirectToLogin, redirectToAuthorize, getLoginUrl, getSignupUrl, getLogoutUrl, redirectToLogout } = createRedirectMethods({
1456
1497
  authMode,
1457
1498
  baseUrl,
1458
1499
  clientId,
@@ -1513,7 +1554,10 @@ function createFFIDClient(config) {
1513
1554
  getSession,
1514
1555
  signOut,
1515
1556
  redirectToLogin,
1557
+ redirectToAuthorize,
1558
+ redirectToLogout,
1516
1559
  getLoginUrl,
1560
+ getLogoutUrl,
1517
1561
  getSignupUrl,
1518
1562
  createError,
1519
1563
  exchangeCodeForTokens,
@@ -1666,11 +1710,7 @@ function FFIDProvider({
1666
1710
  const switchOrganization = react.useCallback(
1667
1711
  (organizationId) => {
1668
1712
  const org = organizations.find((o) => o.id === organizationId);
1669
- if (org) {
1670
- setCurrentOrganizationId(organizationId);
1671
- setError(null);
1672
- client.logger.debug("Switched organization:", org.name);
1673
- } else {
1713
+ if (!org) {
1674
1714
  client.logger.debug("Organization not found:", organizationId);
1675
1715
  const ffidError = client.createError(
1676
1716
  "ORGANIZATION_NOT_FOUND",
@@ -1678,6 +1718,33 @@ function FFIDProvider({
1678
1718
  );
1679
1719
  setError(ffidError);
1680
1720
  onErrorRef.current?.(ffidError);
1721
+ return;
1722
+ }
1723
+ if (client.authMode === "token") {
1724
+ client.logger.debug("Switching organization via re-authorization:", org.name);
1725
+ client.redirectToAuthorize({ organizationId }).then((result) => {
1726
+ if (!result.success) {
1727
+ client.logger.error("\u7D44\u7E54\u5207\u308A\u66FF\u3048\u306E\u518D\u8A8D\u8A3C\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u306B\u5931\u6557\u3057\u307E\u3057\u305F:", result.error);
1728
+ const ffidError = client.createError(
1729
+ "ORGANIZATION_SWITCH_ERROR",
1730
+ result.error
1731
+ );
1732
+ setError(ffidError);
1733
+ onErrorRef.current?.(ffidError);
1734
+ }
1735
+ }).catch((err) => {
1736
+ client.logger.error("\u7D44\u7E54\u5207\u308A\u66FF\u3048\u306E\u518D\u8A8D\u8A3C\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u306B\u5931\u6557\u3057\u307E\u3057\u305F:", err);
1737
+ const ffidError = client.createError(
1738
+ "ORGANIZATION_SWITCH_ERROR",
1739
+ err instanceof Error ? err.message : "\u7D44\u7E54\u5207\u308A\u66FF\u3048\u306B\u5931\u6557\u3057\u307E\u3057\u305F"
1740
+ );
1741
+ setError(ffidError);
1742
+ onErrorRef.current?.(ffidError);
1743
+ });
1744
+ } else {
1745
+ setCurrentOrganizationId(organizationId);
1746
+ setError(null);
1747
+ client.logger.debug("Switched organization:", org.name);
1681
1748
  }
1682
1749
  },
1683
1750
  [organizations, client]
@@ -2208,6 +2275,12 @@ function useSubscription() {
2208
2275
  const isActive = subscription?.status === "active";
2209
2276
  const isTrialing = subscription?.status === "trialing";
2210
2277
  const isCanceled = subscription?.status === "canceled";
2278
+ const isTrialExpired = react.useMemo(() => {
2279
+ if (!isTrialing) return false;
2280
+ const relevantEnd = subscription?.trialEnd ?? subscription?.currentPeriodEnd;
2281
+ if (!relevantEnd) return false;
2282
+ return new Date(relevantEnd).getTime() < Date.now();
2283
+ }, [isTrialing, subscription?.trialEnd, subscription?.currentPeriodEnd]);
2211
2284
  const hasPlan = react.useCallback(
2212
2285
  (plans) => {
2213
2286
  if (!planCode) return false;
@@ -2217,13 +2290,15 @@ function useSubscription() {
2217
2290
  [planCode]
2218
2291
  );
2219
2292
  const hasAccess = react.useCallback(() => {
2293
+ if (isTrialExpired) return false;
2220
2294
  return isActive || isTrialing;
2221
- }, [isActive, isTrialing]);
2295
+ }, [isActive, isTrialing, isTrialExpired]);
2222
2296
  return {
2223
2297
  subscription,
2224
2298
  planCode,
2225
2299
  isActive,
2226
2300
  isTrialing,
2301
+ isTrialExpired,
2227
2302
  isCanceled,
2228
2303
  hasPlan,
2229
2304
  hasAccess
@@ -111,6 +111,10 @@ function normalizeUserinfo(raw) {
111
111
  email: raw.email,
112
112
  name: raw.name,
113
113
  picture: raw.picture,
114
+ companyName: raw.company_name ?? null,
115
+ department: raw.department ?? null,
116
+ position: raw.position ?? null,
117
+ phoneNumber: raw.phone_number ?? null,
114
118
  organizationId: raw.organization_id ?? null,
115
119
  subscription: raw.subscription ? {
116
120
  subscriptionId: raw.subscription.subscription_id ?? null,
@@ -118,7 +122,8 @@ function normalizeUserinfo(raw) {
118
122
  planCode: raw.subscription.plan_code ?? null,
119
123
  seatModel: raw.subscription.seat_model ?? null,
120
124
  memberRole: raw.subscription.member_role ?? null,
121
- organizationId: raw.subscription.organization_id ?? null
125
+ organizationId: raw.subscription.organization_id ?? null,
126
+ hasSeatAssignment: raw.subscription.has_seat_assignment ?? null
122
127
  } : void 0
123
128
  };
124
129
  }
@@ -343,6 +348,10 @@ function createVerifyAccessToken(deps) {
343
348
  email: introspectResponse.email ?? null,
344
349
  name: introspectResponse.name ?? null,
345
350
  picture: introspectResponse.picture ?? null,
351
+ company_name: introspectResponse.company_name ?? null,
352
+ department: introspectResponse.department ?? null,
353
+ position: introspectResponse.position ?? null,
354
+ phone_number: introspectResponse.phone_number ?? null,
346
355
  organization_id: introspectResponse.organization_id ?? null
347
356
  };
348
357
  const raw = introspectResponse.subscription ? {
@@ -353,7 +362,8 @@ function createVerifyAccessToken(deps) {
353
362
  plan_code: introspectResponse.subscription.plan_code,
354
363
  seat_model: introspectResponse.subscription.seat_model,
355
364
  member_role: introspectResponse.subscription.member_role,
356
- organization_id: introspectResponse.subscription.organization_id
365
+ organization_id: introspectResponse.subscription.organization_id,
366
+ has_seat_assignment: introspectResponse.subscription.has_seat_assignment ?? false
357
367
  }
358
368
  } : base;
359
369
  const userinfo = normalizeUserinfo(raw);
@@ -435,7 +445,7 @@ function createBillingMethods(deps) {
435
445
  }
436
446
 
437
447
  // src/client/version-check.ts
438
- var SDK_VERSION = "1.13.0";
448
+ var SDK_VERSION = "1.17.0";
439
449
  var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
440
450
  var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
441
451
  function sdkHeaders() {
@@ -944,6 +954,7 @@ function base64UrlEncode(buffer) {
944
954
 
945
955
  // src/client/redirect.ts
946
956
  var OAUTH_AUTHORIZE_ENDPOINT = "/api/v1/oauth/authorize";
957
+ var AUTH_LOGOUT_ENDPOINT = "/api/v1/auth/logout";
947
958
  var STATE_RANDOM_BYTES = 16;
948
959
  var HEX_BASE2 = 16;
949
960
  function generateRandomState() {
@@ -960,7 +971,11 @@ function createRedirectMethods(deps) {
960
971
  resolvedRedirectUri,
961
972
  logger
962
973
  } = deps;
963
- async function redirectToAuthorize() {
974
+ async function redirectToAuthorize(options) {
975
+ if (typeof window === "undefined") {
976
+ logger.warn("SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093");
977
+ return { success: false, error: "SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093" };
978
+ }
964
979
  const verifier = generateCodeVerifier();
965
980
  storeCodeVerifier(verifier, logger);
966
981
  let challenge;
@@ -981,6 +996,14 @@ function createRedirectMethods(deps) {
981
996
  code_challenge: challenge,
982
997
  code_challenge_method: "S256"
983
998
  });
999
+ if (options?.organizationId !== void 0) {
1000
+ const trimmedOrgId = options.organizationId.trim();
1001
+ if (!trimmedOrgId) {
1002
+ logger.error("organizationId \u304C\u7A7A\u6587\u5B57\u3067\u3059");
1003
+ return { success: false, error: "organizationId \u306F\u7A7A\u306B\u3067\u304D\u307E\u305B\u3093" };
1004
+ }
1005
+ params.set("organization_id", trimmedOrgId);
1006
+ }
984
1007
  const authorizeUrl = `${baseUrl}${OAUTH_AUTHORIZE_ENDPOINT}?${params.toString()}`;
985
1008
  logger.debug("Redirecting to authorize:", authorizeUrl);
986
1009
  window.location.href = authorizeUrl;
@@ -1024,7 +1047,25 @@ function createRedirectMethods(deps) {
1024
1047
  }
1025
1048
  return `${baseUrl}/signup?redirect=${encodeURIComponent(redirect)}&service=${encodeURIComponent(serviceCode)}`;
1026
1049
  }
1027
- return { redirectToLogin, redirectToAuthorize, getLoginUrl, getSignupUrl };
1050
+ function getLogoutUrl(postLogoutRedirectUri) {
1051
+ const url = new URL(`${baseUrl}${AUTH_LOGOUT_ENDPOINT}`);
1052
+ url.searchParams.set("client_id", clientId);
1053
+ if (postLogoutRedirectUri != null) {
1054
+ url.searchParams.set("post_logout_redirect_uri", postLogoutRedirectUri);
1055
+ }
1056
+ return url.toString();
1057
+ }
1058
+ function redirectToLogout(postLogoutRedirectUri) {
1059
+ if (typeof window === "undefined") {
1060
+ logger.warn("SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093");
1061
+ return { success: false, error: "SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093" };
1062
+ }
1063
+ const logoutUrl = getLogoutUrl(postLogoutRedirectUri);
1064
+ logger.debug("Redirecting to logout:", logoutUrl);
1065
+ window.location.href = logoutUrl;
1066
+ return { success: true };
1067
+ }
1068
+ return { redirectToLogin, redirectToAuthorize, getLoginUrl, getSignupUrl, getLogoutUrl, redirectToLogout };
1028
1069
  }
1029
1070
 
1030
1071
  // src/client/password-reset.ts
@@ -1450,7 +1491,7 @@ function createFFIDClient(config) {
1450
1491
  }
1451
1492
  return signOutCookie();
1452
1493
  }
1453
- const { redirectToLogin, getLoginUrl, getSignupUrl } = createRedirectMethods({
1494
+ const { redirectToLogin, redirectToAuthorize, getLoginUrl, getSignupUrl, getLogoutUrl, redirectToLogout } = createRedirectMethods({
1454
1495
  authMode,
1455
1496
  baseUrl,
1456
1497
  clientId,
@@ -1511,7 +1552,10 @@ function createFFIDClient(config) {
1511
1552
  getSession,
1512
1553
  signOut,
1513
1554
  redirectToLogin,
1555
+ redirectToAuthorize,
1556
+ redirectToLogout,
1514
1557
  getLoginUrl,
1558
+ getLogoutUrl,
1515
1559
  getSignupUrl,
1516
1560
  createError,
1517
1561
  exchangeCodeForTokens,
@@ -1664,11 +1708,7 @@ function FFIDProvider({
1664
1708
  const switchOrganization = useCallback(
1665
1709
  (organizationId) => {
1666
1710
  const org = organizations.find((o) => o.id === organizationId);
1667
- if (org) {
1668
- setCurrentOrganizationId(organizationId);
1669
- setError(null);
1670
- client.logger.debug("Switched organization:", org.name);
1671
- } else {
1711
+ if (!org) {
1672
1712
  client.logger.debug("Organization not found:", organizationId);
1673
1713
  const ffidError = client.createError(
1674
1714
  "ORGANIZATION_NOT_FOUND",
@@ -1676,6 +1716,33 @@ function FFIDProvider({
1676
1716
  );
1677
1717
  setError(ffidError);
1678
1718
  onErrorRef.current?.(ffidError);
1719
+ return;
1720
+ }
1721
+ if (client.authMode === "token") {
1722
+ client.logger.debug("Switching organization via re-authorization:", org.name);
1723
+ client.redirectToAuthorize({ organizationId }).then((result) => {
1724
+ if (!result.success) {
1725
+ client.logger.error("\u7D44\u7E54\u5207\u308A\u66FF\u3048\u306E\u518D\u8A8D\u8A3C\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u306B\u5931\u6557\u3057\u307E\u3057\u305F:", result.error);
1726
+ const ffidError = client.createError(
1727
+ "ORGANIZATION_SWITCH_ERROR",
1728
+ result.error
1729
+ );
1730
+ setError(ffidError);
1731
+ onErrorRef.current?.(ffidError);
1732
+ }
1733
+ }).catch((err) => {
1734
+ client.logger.error("\u7D44\u7E54\u5207\u308A\u66FF\u3048\u306E\u518D\u8A8D\u8A3C\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u306B\u5931\u6557\u3057\u307E\u3057\u305F:", err);
1735
+ const ffidError = client.createError(
1736
+ "ORGANIZATION_SWITCH_ERROR",
1737
+ err instanceof Error ? err.message : "\u7D44\u7E54\u5207\u308A\u66FF\u3048\u306B\u5931\u6557\u3057\u307E\u3057\u305F"
1738
+ );
1739
+ setError(ffidError);
1740
+ onErrorRef.current?.(ffidError);
1741
+ });
1742
+ } else {
1743
+ setCurrentOrganizationId(organizationId);
1744
+ setError(null);
1745
+ client.logger.debug("Switched organization:", org.name);
1679
1746
  }
1680
1747
  },
1681
1748
  [organizations, client]
@@ -2206,6 +2273,12 @@ function useSubscription() {
2206
2273
  const isActive = subscription?.status === "active";
2207
2274
  const isTrialing = subscription?.status === "trialing";
2208
2275
  const isCanceled = subscription?.status === "canceled";
2276
+ const isTrialExpired = useMemo(() => {
2277
+ if (!isTrialing) return false;
2278
+ const relevantEnd = subscription?.trialEnd ?? subscription?.currentPeriodEnd;
2279
+ if (!relevantEnd) return false;
2280
+ return new Date(relevantEnd).getTime() < Date.now();
2281
+ }, [isTrialing, subscription?.trialEnd, subscription?.currentPeriodEnd]);
2209
2282
  const hasPlan = useCallback(
2210
2283
  (plans) => {
2211
2284
  if (!planCode) return false;
@@ -2215,13 +2288,15 @@ function useSubscription() {
2215
2288
  [planCode]
2216
2289
  );
2217
2290
  const hasAccess = useCallback(() => {
2291
+ if (isTrialExpired) return false;
2218
2292
  return isActive || isTrialing;
2219
- }, [isActive, isTrialing]);
2293
+ }, [isActive, isTrialing, isTrialExpired]);
2220
2294
  return {
2221
2295
  subscription,
2222
2296
  planCode,
2223
2297
  isActive,
2224
2298
  isTrialing,
2299
+ isTrialExpired,
2225
2300
  isCanceled,
2226
2301
  hasPlan,
2227
2302
  hasAccess
@@ -1,30 +1,30 @@
1
1
  'use strict';
2
2
 
3
- var chunk7YV3SUEY_cjs = require('../chunk-7YV3SUEY.cjs');
3
+ var chunk4L5JIETV_cjs = require('../chunk-4L5JIETV.cjs');
4
4
 
5
5
 
6
6
 
7
7
  Object.defineProperty(exports, "FFIDAnnouncementBadge", {
8
8
  enumerable: true,
9
- get: function () { return chunk7YV3SUEY_cjs.FFIDAnnouncementBadge; }
9
+ get: function () { return chunk4L5JIETV_cjs.FFIDAnnouncementBadge; }
10
10
  });
11
11
  Object.defineProperty(exports, "FFIDAnnouncementList", {
12
12
  enumerable: true,
13
- get: function () { return chunk7YV3SUEY_cjs.FFIDAnnouncementList; }
13
+ get: function () { return chunk4L5JIETV_cjs.FFIDAnnouncementList; }
14
14
  });
15
15
  Object.defineProperty(exports, "FFIDLoginButton", {
16
16
  enumerable: true,
17
- get: function () { return chunk7YV3SUEY_cjs.FFIDLoginButton; }
17
+ get: function () { return chunk4L5JIETV_cjs.FFIDLoginButton; }
18
18
  });
19
19
  Object.defineProperty(exports, "FFIDOrganizationSwitcher", {
20
20
  enumerable: true,
21
- get: function () { return chunk7YV3SUEY_cjs.FFIDOrganizationSwitcher; }
21
+ get: function () { return chunk4L5JIETV_cjs.FFIDOrganizationSwitcher; }
22
22
  });
23
23
  Object.defineProperty(exports, "FFIDSubscriptionBadge", {
24
24
  enumerable: true,
25
- get: function () { return chunk7YV3SUEY_cjs.FFIDSubscriptionBadge; }
25
+ get: function () { return chunk4L5JIETV_cjs.FFIDSubscriptionBadge; }
26
26
  });
27
27
  Object.defineProperty(exports, "FFIDUserMenu", {
28
28
  enumerable: true,
29
- get: function () { return chunk7YV3SUEY_cjs.FFIDUserMenu; }
29
+ get: function () { return chunk4L5JIETV_cjs.FFIDUserMenu; }
30
30
  });
@@ -1,3 +1,3 @@
1
- export { y as FFIDAnnouncementBadge, W as FFIDAnnouncementBadgeClassNames, X as FFIDAnnouncementBadgeProps, z as FFIDAnnouncementList, Y as FFIDAnnouncementListClassNames, Z as FFIDAnnouncementListProps, I as FFIDLoginButton, _ as FFIDLoginButtonProps, N as FFIDOrganizationSwitcher, $ as FFIDOrganizationSwitcherClassNames, a0 as FFIDOrganizationSwitcherProps, P as FFIDSubscriptionBadge, a1 as FFIDSubscriptionBadgeClassNames, a2 as FFIDSubscriptionBadgeProps, S as FFIDUserMenu, a3 as FFIDUserMenuClassNames, a4 as FFIDUserMenuProps } from '../index-GrAWBpmf.cjs';
1
+ export { y as FFIDAnnouncementBadge, W as FFIDAnnouncementBadgeClassNames, X as FFIDAnnouncementBadgeProps, z as FFIDAnnouncementList, Y as FFIDAnnouncementListClassNames, Z as FFIDAnnouncementListProps, I as FFIDLoginButton, _ as FFIDLoginButtonProps, N as FFIDOrganizationSwitcher, $ as FFIDOrganizationSwitcherClassNames, a0 as FFIDOrganizationSwitcherProps, P as FFIDSubscriptionBadge, a1 as FFIDSubscriptionBadgeClassNames, a2 as FFIDSubscriptionBadgeProps, S as FFIDUserMenu, a3 as FFIDUserMenuClassNames, a4 as FFIDUserMenuProps } from '../index-B9sSp8kZ.cjs';
2
2
  import 'react/jsx-runtime';
3
3
  import 'react';
@@ -1,3 +1,3 @@
1
- export { y as FFIDAnnouncementBadge, W as FFIDAnnouncementBadgeClassNames, X as FFIDAnnouncementBadgeProps, z as FFIDAnnouncementList, Y as FFIDAnnouncementListClassNames, Z as FFIDAnnouncementListProps, I as FFIDLoginButton, _ as FFIDLoginButtonProps, N as FFIDOrganizationSwitcher, $ as FFIDOrganizationSwitcherClassNames, a0 as FFIDOrganizationSwitcherProps, P as FFIDSubscriptionBadge, a1 as FFIDSubscriptionBadgeClassNames, a2 as FFIDSubscriptionBadgeProps, S as FFIDUserMenu, a3 as FFIDUserMenuClassNames, a4 as FFIDUserMenuProps } from '../index-GrAWBpmf.js';
1
+ export { y as FFIDAnnouncementBadge, W as FFIDAnnouncementBadgeClassNames, X as FFIDAnnouncementBadgeProps, z as FFIDAnnouncementList, Y as FFIDAnnouncementListClassNames, Z as FFIDAnnouncementListProps, I as FFIDLoginButton, _ as FFIDLoginButtonProps, N as FFIDOrganizationSwitcher, $ as FFIDOrganizationSwitcherClassNames, a0 as FFIDOrganizationSwitcherProps, P as FFIDSubscriptionBadge, a1 as FFIDSubscriptionBadgeClassNames, a2 as FFIDSubscriptionBadgeProps, S as FFIDUserMenu, a3 as FFIDUserMenuClassNames, a4 as FFIDUserMenuProps } from '../index-B9sSp8kZ.js';
2
2
  import 'react/jsx-runtime';
3
3
  import 'react';
@@ -1 +1 @@
1
- export { FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDSubscriptionBadge, FFIDUserMenu } from '../chunk-KFOIVZEY.js';
1
+ export { FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDSubscriptionBadge, FFIDUserMenu } from '../chunk-4LRZTABD.js';
@@ -107,6 +107,8 @@ interface FFIDSubscription {
107
107
  status: 'trialing' | 'active' | 'past_due' | 'canceled' | 'paused';
108
108
  /** Current billing period end date */
109
109
  currentPeriodEnd: string | null;
110
+ /** Trial end date (null if not a trial or no trial_end set) */
111
+ trialEnd: string | null;
110
112
  /** Service seat model (available when sourced from OAuth userinfo) */
111
113
  seatModel?: FFIDSeatModel | undefined;
112
114
  /** Member role in the resolved organization context */
@@ -122,6 +124,7 @@ interface FFIDOAuthUserInfoSubscription {
122
124
  seatModel: FFIDSeatModel | null;
123
125
  memberRole: FFIDOAuthUserInfoMemberRole | null;
124
126
  organizationId: string | null;
127
+ hasSeatAssignment: boolean | null;
125
128
  }
126
129
  /** OAuth userinfo response exposed by FFID */
127
130
  interface FFIDOAuthUserInfo {
@@ -129,6 +132,10 @@ interface FFIDOAuthUserInfo {
129
132
  email: string | null;
130
133
  name: string | null;
131
134
  picture: string | null;
135
+ companyName?: string | null | undefined;
136
+ department?: string | null | undefined;
137
+ position?: string | null | undefined;
138
+ phoneNumber?: string | null | undefined;
132
139
  organizationId?: string | null | undefined;
133
140
  subscription?: FFIDOAuthUserInfoSubscription | undefined;
134
141
  }
@@ -250,7 +257,13 @@ interface FFIDContextValue {
250
257
  login: () => void;
251
258
  /** Sign out and clear session */
252
259
  logout: () => Promise<void>;
253
- /** Switch to a different organization */
260
+ /**
261
+ * Switch to a different organization.
262
+ *
263
+ * Cookie mode: updates local state synchronously.
264
+ * Token mode: triggers a full-page redirect for OAuth re-authorization.
265
+ * Errors surface via the `error` state and `onError` callback.
266
+ */
254
267
  switchOrganization: (organizationId: string) => void;
255
268
  /** Refresh session data */
256
269
  refresh: () => Promise<void>;
@@ -267,6 +280,8 @@ interface FFIDSubscriptionContextValue {
267
280
  isActive: boolean;
268
281
  /** Whether in trial period */
269
282
  isTrialing: boolean;
283
+ /** Whether trial has expired (status is 'trialing' but period end is in the past) */
284
+ isTrialExpired: boolean;
270
285
  /** Whether subscription is canceled */
271
286
  isCanceled: boolean;
272
287
  /** Check if user has specific plan */
@@ -376,7 +391,7 @@ interface FFIDCreatePortalParams {
376
391
  returnUrl: string;
377
392
  }
378
393
  /**
379
- * Result of a redirect operation (redirectToLogin / redirectToAuthorize)
394
+ * Result of a redirect operation (redirectToLogin / redirectToAuthorize / redirectToLogout)
380
395
  *
381
396
  * Structured return type so callers can inspect failure reasons
382
397
  * instead of receiving a bare `false`.
@@ -410,6 +425,10 @@ interface FFIDTokenIntrospectionResponse {
410
425
  email?: string;
411
426
  name?: string;
412
427
  picture?: string | null;
428
+ company_name?: string | null;
429
+ department?: string | null;
430
+ position?: string | null;
431
+ phone_number?: string | null;
413
432
  scope?: string | null;
414
433
  exp?: number;
415
434
  iat?: number;
@@ -424,6 +443,7 @@ interface FFIDTokenIntrospectionResponse {
424
443
  seat_model: FFIDOAuthUserInfoSubscription['seatModel'];
425
444
  member_role: FFIDOAuthUserInfoSubscription['memberRole'];
426
445
  organization_id: string | null;
446
+ has_seat_assignment?: boolean;
427
447
  };
428
448
  }
429
449
 
@@ -107,6 +107,8 @@ interface FFIDSubscription {
107
107
  status: 'trialing' | 'active' | 'past_due' | 'canceled' | 'paused';
108
108
  /** Current billing period end date */
109
109
  currentPeriodEnd: string | null;
110
+ /** Trial end date (null if not a trial or no trial_end set) */
111
+ trialEnd: string | null;
110
112
  /** Service seat model (available when sourced from OAuth userinfo) */
111
113
  seatModel?: FFIDSeatModel | undefined;
112
114
  /** Member role in the resolved organization context */
@@ -122,6 +124,7 @@ interface FFIDOAuthUserInfoSubscription {
122
124
  seatModel: FFIDSeatModel | null;
123
125
  memberRole: FFIDOAuthUserInfoMemberRole | null;
124
126
  organizationId: string | null;
127
+ hasSeatAssignment: boolean | null;
125
128
  }
126
129
  /** OAuth userinfo response exposed by FFID */
127
130
  interface FFIDOAuthUserInfo {
@@ -129,6 +132,10 @@ interface FFIDOAuthUserInfo {
129
132
  email: string | null;
130
133
  name: string | null;
131
134
  picture: string | null;
135
+ companyName?: string | null | undefined;
136
+ department?: string | null | undefined;
137
+ position?: string | null | undefined;
138
+ phoneNumber?: string | null | undefined;
132
139
  organizationId?: string | null | undefined;
133
140
  subscription?: FFIDOAuthUserInfoSubscription | undefined;
134
141
  }
@@ -250,7 +257,13 @@ interface FFIDContextValue {
250
257
  login: () => void;
251
258
  /** Sign out and clear session */
252
259
  logout: () => Promise<void>;
253
- /** Switch to a different organization */
260
+ /**
261
+ * Switch to a different organization.
262
+ *
263
+ * Cookie mode: updates local state synchronously.
264
+ * Token mode: triggers a full-page redirect for OAuth re-authorization.
265
+ * Errors surface via the `error` state and `onError` callback.
266
+ */
254
267
  switchOrganization: (organizationId: string) => void;
255
268
  /** Refresh session data */
256
269
  refresh: () => Promise<void>;
@@ -267,6 +280,8 @@ interface FFIDSubscriptionContextValue {
267
280
  isActive: boolean;
268
281
  /** Whether in trial period */
269
282
  isTrialing: boolean;
283
+ /** Whether trial has expired (status is 'trialing' but period end is in the past) */
284
+ isTrialExpired: boolean;
270
285
  /** Whether subscription is canceled */
271
286
  isCanceled: boolean;
272
287
  /** Check if user has specific plan */
@@ -376,7 +391,7 @@ interface FFIDCreatePortalParams {
376
391
  returnUrl: string;
377
392
  }
378
393
  /**
379
- * Result of a redirect operation (redirectToLogin / redirectToAuthorize)
394
+ * Result of a redirect operation (redirectToLogin / redirectToAuthorize / redirectToLogout)
380
395
  *
381
396
  * Structured return type so callers can inspect failure reasons
382
397
  * instead of receiving a bare `false`.
@@ -410,6 +425,10 @@ interface FFIDTokenIntrospectionResponse {
410
425
  email?: string;
411
426
  name?: string;
412
427
  picture?: string | null;
428
+ company_name?: string | null;
429
+ department?: string | null;
430
+ position?: string | null;
431
+ phone_number?: string | null;
413
432
  scope?: string | null;
414
433
  exp?: number;
415
434
  iat?: number;
@@ -424,6 +443,7 @@ interface FFIDTokenIntrospectionResponse {
424
443
  seat_model: FFIDOAuthUserInfoSubscription['seatModel'];
425
444
  member_role: FFIDOAuthUserInfoSubscription['memberRole'];
426
445
  organization_id: string | null;
446
+ has_seat_assignment?: boolean;
427
447
  };
428
448
  }
429
449
 
package/dist/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunk7YV3SUEY_cjs = require('./chunk-7YV3SUEY.cjs');
3
+ var chunk4L5JIETV_cjs = require('./chunk-4L5JIETV.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 } = chunk7YV3SUEY_cjs.useFFIDContext();
49
+ const { isLoading, isAuthenticated, login } = chunk4L5JIETV_cjs.useFFIDContext();
50
50
  const hasRedirected = react.useRef(false);
51
51
  react.useEffect(() => {
52
52
  if (!isLoading && !isAuthenticated && options.redirectToLogin && !hasRedirected.current) {
@@ -71,83 +71,83 @@ function withFFIDAuth(Component, options = {}) {
71
71
 
72
72
  Object.defineProperty(exports, "DEFAULT_API_BASE_URL", {
73
73
  enumerable: true,
74
- get: function () { return chunk7YV3SUEY_cjs.DEFAULT_API_BASE_URL; }
74
+ get: function () { return chunk4L5JIETV_cjs.DEFAULT_API_BASE_URL; }
75
75
  });
76
76
  Object.defineProperty(exports, "FFIDAnnouncementBadge", {
77
77
  enumerable: true,
78
- get: function () { return chunk7YV3SUEY_cjs.FFIDAnnouncementBadge; }
78
+ get: function () { return chunk4L5JIETV_cjs.FFIDAnnouncementBadge; }
79
79
  });
80
80
  Object.defineProperty(exports, "FFIDAnnouncementList", {
81
81
  enumerable: true,
82
- get: function () { return chunk7YV3SUEY_cjs.FFIDAnnouncementList; }
82
+ get: function () { return chunk4L5JIETV_cjs.FFIDAnnouncementList; }
83
83
  });
84
84
  Object.defineProperty(exports, "FFIDLoginButton", {
85
85
  enumerable: true,
86
- get: function () { return chunk7YV3SUEY_cjs.FFIDLoginButton; }
86
+ get: function () { return chunk4L5JIETV_cjs.FFIDLoginButton; }
87
87
  });
88
88
  Object.defineProperty(exports, "FFIDOrganizationSwitcher", {
89
89
  enumerable: true,
90
- get: function () { return chunk7YV3SUEY_cjs.FFIDOrganizationSwitcher; }
90
+ get: function () { return chunk4L5JIETV_cjs.FFIDOrganizationSwitcher; }
91
91
  });
92
92
  Object.defineProperty(exports, "FFIDProvider", {
93
93
  enumerable: true,
94
- get: function () { return chunk7YV3SUEY_cjs.FFIDProvider; }
94
+ get: function () { return chunk4L5JIETV_cjs.FFIDProvider; }
95
95
  });
96
96
  Object.defineProperty(exports, "FFIDSubscriptionBadge", {
97
97
  enumerable: true,
98
- get: function () { return chunk7YV3SUEY_cjs.FFIDSubscriptionBadge; }
98
+ get: function () { return chunk4L5JIETV_cjs.FFIDSubscriptionBadge; }
99
99
  });
100
100
  Object.defineProperty(exports, "FFIDUserMenu", {
101
101
  enumerable: true,
102
- get: function () { return chunk7YV3SUEY_cjs.FFIDUserMenu; }
102
+ get: function () { return chunk4L5JIETV_cjs.FFIDUserMenu; }
103
103
  });
104
104
  Object.defineProperty(exports, "FFID_ANNOUNCEMENTS_ERROR_CODES", {
105
105
  enumerable: true,
106
- get: function () { return chunk7YV3SUEY_cjs.FFID_ANNOUNCEMENTS_ERROR_CODES; }
106
+ get: function () { return chunk4L5JIETV_cjs.FFID_ANNOUNCEMENTS_ERROR_CODES; }
107
107
  });
108
108
  Object.defineProperty(exports, "createFFIDAnnouncementsClient", {
109
109
  enumerable: true,
110
- get: function () { return chunk7YV3SUEY_cjs.createFFIDAnnouncementsClient; }
110
+ get: function () { return chunk4L5JIETV_cjs.createFFIDAnnouncementsClient; }
111
111
  });
112
112
  Object.defineProperty(exports, "createFFIDClient", {
113
113
  enumerable: true,
114
- get: function () { return chunk7YV3SUEY_cjs.createFFIDClient; }
114
+ get: function () { return chunk4L5JIETV_cjs.createFFIDClient; }
115
115
  });
116
116
  Object.defineProperty(exports, "createTokenStore", {
117
117
  enumerable: true,
118
- get: function () { return chunk7YV3SUEY_cjs.createTokenStore; }
118
+ get: function () { return chunk4L5JIETV_cjs.createTokenStore; }
119
119
  });
120
120
  Object.defineProperty(exports, "generateCodeChallenge", {
121
121
  enumerable: true,
122
- get: function () { return chunk7YV3SUEY_cjs.generateCodeChallenge; }
122
+ get: function () { return chunk4L5JIETV_cjs.generateCodeChallenge; }
123
123
  });
124
124
  Object.defineProperty(exports, "generateCodeVerifier", {
125
125
  enumerable: true,
126
- get: function () { return chunk7YV3SUEY_cjs.generateCodeVerifier; }
126
+ get: function () { return chunk4L5JIETV_cjs.generateCodeVerifier; }
127
127
  });
128
128
  Object.defineProperty(exports, "retrieveCodeVerifier", {
129
129
  enumerable: true,
130
- get: function () { return chunk7YV3SUEY_cjs.retrieveCodeVerifier; }
130
+ get: function () { return chunk4L5JIETV_cjs.retrieveCodeVerifier; }
131
131
  });
132
132
  Object.defineProperty(exports, "storeCodeVerifier", {
133
133
  enumerable: true,
134
- get: function () { return chunk7YV3SUEY_cjs.storeCodeVerifier; }
134
+ get: function () { return chunk4L5JIETV_cjs.storeCodeVerifier; }
135
135
  });
136
136
  Object.defineProperty(exports, "useFFID", {
137
137
  enumerable: true,
138
- get: function () { return chunk7YV3SUEY_cjs.useFFID; }
138
+ get: function () { return chunk4L5JIETV_cjs.useFFID; }
139
139
  });
140
140
  Object.defineProperty(exports, "useFFIDAnnouncements", {
141
141
  enumerable: true,
142
- get: function () { return chunk7YV3SUEY_cjs.useFFIDAnnouncements; }
142
+ get: function () { return chunk4L5JIETV_cjs.useFFIDAnnouncements; }
143
143
  });
144
144
  Object.defineProperty(exports, "useSubscription", {
145
145
  enumerable: true,
146
- get: function () { return chunk7YV3SUEY_cjs.useSubscription; }
146
+ get: function () { return chunk4L5JIETV_cjs.useSubscription; }
147
147
  });
148
148
  Object.defineProperty(exports, "withSubscription", {
149
149
  enumerable: true,
150
- get: function () { return chunk7YV3SUEY_cjs.withSubscription; }
150
+ get: function () { return chunk4L5JIETV_cjs.withSubscription; }
151
151
  });
152
152
  exports.createKVCacheAdapter = createKVCacheAdapter;
153
153
  exports.createMemoryCacheAdapter = createMemoryCacheAdapter;
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { F as FFIDConfig, a as FFIDApiResponse, b as FFIDSessionResponse, c as FFIDRedirectResult, d as FFIDError, e as FFIDSubscriptionCheckResponse, f as FFIDCreateCheckoutParams, g as FFIDCheckoutSessionResponse, h as FFIDCreatePortalParams, i as FFIDPortalSessionResponse, j as FFIDVerifyAccessTokenOptions, k as FFIDOAuthUserInfo, l as FFIDAuthMode, m as FFIDLogger, n as FFIDCacheAdapter, o as FFIDUser, p as FFIDOrganization, q as FFIDSubscription, r as FFIDSubscriptionContextValue, s as FFIDAnnouncementsClientConfig, L as ListAnnouncementsOptions, t as FFIDAnnouncementsApiResponse, A as AnnouncementListResponse, u as FFIDAnnouncementsLogger } from './index-GrAWBpmf.cjs';
2
- export { v as Announcement, w as AnnouncementStatus, x as AnnouncementType, y as FFIDAnnouncementBadge, z as FFIDAnnouncementList, B as FFIDAnnouncementsError, C as FFIDAnnouncementsErrorCode, D as FFIDAnnouncementsServerResponse, E as FFIDCacheConfig, G as FFIDContextValue, H as FFIDJwtClaims, I as FFIDLoginButton, J as FFIDOAuthTokenResponse, K as FFIDOAuthUserInfoMemberRole, M as FFIDOAuthUserInfoSubscription, N as FFIDOrganizationSwitcher, O as FFIDSeatModel, P as FFIDSubscriptionBadge, Q as FFIDSubscriptionStatus, R as FFIDTokenIntrospectionResponse, S as FFIDUserMenu, U as UseFFIDAnnouncementsOptions, T as UseFFIDAnnouncementsReturn, V as useFFIDAnnouncements } from './index-GrAWBpmf.cjs';
1
+ import { F as FFIDConfig, a as FFIDApiResponse, b as FFIDSessionResponse, c as FFIDRedirectResult, d as FFIDError, e as FFIDSubscriptionCheckResponse, f as FFIDCreateCheckoutParams, g as FFIDCheckoutSessionResponse, h as FFIDCreatePortalParams, i as FFIDPortalSessionResponse, j as FFIDVerifyAccessTokenOptions, k as FFIDOAuthUserInfo, l as FFIDAuthMode, m as FFIDLogger, n as FFIDCacheAdapter, o as FFIDUser, p as FFIDOrganization, q as FFIDSubscription, r as FFIDSubscriptionContextValue, s as FFIDAnnouncementsClientConfig, L as ListAnnouncementsOptions, t as FFIDAnnouncementsApiResponse, A as AnnouncementListResponse, u as FFIDAnnouncementsLogger } from './index-B9sSp8kZ.cjs';
2
+ export { v as Announcement, w as AnnouncementStatus, x as AnnouncementType, y as FFIDAnnouncementBadge, z as FFIDAnnouncementList, B as FFIDAnnouncementsError, C as FFIDAnnouncementsErrorCode, D as FFIDAnnouncementsServerResponse, E as FFIDCacheConfig, G as FFIDContextValue, H as FFIDJwtClaims, I as FFIDLoginButton, J as FFIDOAuthTokenResponse, K as FFIDOAuthUserInfoMemberRole, M as FFIDOAuthUserInfoSubscription, N as FFIDOrganizationSwitcher, O as FFIDSeatModel, P as FFIDSubscriptionBadge, Q as FFIDSubscriptionStatus, R as FFIDTokenIntrospectionResponse, S as FFIDUserMenu, U as UseFFIDAnnouncementsOptions, T as UseFFIDAnnouncementsReturn, V as useFFIDAnnouncements } from './index-B9sSp8kZ.cjs';
3
3
  import * as react_jsx_runtime from 'react/jsx-runtime';
4
4
  import { ReactNode, ComponentType, FC } from 'react';
5
5
 
@@ -129,12 +129,23 @@ type FFIDResetSessionResponse = FFIDPasswordResetResponse;
129
129
  /** Response from confirmPasswordReset */
130
130
  type FFIDPasswordResetConfirmResponse = FFIDPasswordResetResponse;
131
131
 
132
+ /** Redirect and URL generation - redirectToLogin / redirectToAuthorize / redirectToLogout / getLoginUrl / getSignupUrl / getLogoutUrl */
133
+
134
+ /** Options for redirectToAuthorize */
135
+ interface RedirectToAuthorizeOptions {
136
+ /** Target organization ID — triggers org-scoped OAuth re-authorization */
137
+ organizationId?: string;
138
+ }
139
+
132
140
  /** Creates an FFID API client instance */
133
141
  declare function createFFIDClient(config: FFIDConfig): {
134
142
  getSession: () => Promise<FFIDApiResponse<FFIDSessionResponse>>;
135
143
  signOut: () => Promise<FFIDApiResponse<void>>;
136
144
  redirectToLogin: () => Promise<FFIDRedirectResult>;
145
+ redirectToAuthorize: (options?: RedirectToAuthorizeOptions) => Promise<FFIDRedirectResult>;
146
+ redirectToLogout: (postLogoutRedirectUri?: string) => FFIDRedirectResult;
137
147
  getLoginUrl: (redirectUrl?: string) => string;
148
+ getLogoutUrl: (postLogoutRedirectUri?: string) => string;
138
149
  getSignupUrl: (redirectUrl?: string) => string;
139
150
  createError: (code: string, message: string) => FFIDError;
140
151
  exchangeCodeForTokens: (code: string, codeVerifier?: string) => Promise<FFIDApiResponse<void>>;
@@ -235,7 +246,10 @@ interface UseFFIDReturn {
235
246
  login: () => void;
236
247
  /** Sign out */
237
248
  logout: () => Promise<void>;
238
- /** Switch current organization */
249
+ /**
250
+ * Switch current organization.
251
+ * Cookie mode: local state change. Token mode: OAuth re-authorization redirect.
252
+ */
239
253
  switchOrganization: (organizationId: string) => void;
240
254
  /** Refresh session data */
241
255
  refresh: () => Promise<void>;
@@ -422,4 +436,4 @@ declare function createFFIDAnnouncementsClient(config?: FFIDAnnouncementsClientC
422
436
  /** Type of the FFID Announcements client */
423
437
  type FFIDAnnouncementsClient = ReturnType<typeof createFFIDAnnouncementsClient>;
424
438
 
425
- export { AnnouncementListResponse, DEFAULT_API_BASE_URL, FFIDAnnouncementsApiResponse, type FFIDAnnouncementsClient, FFIDAnnouncementsClientConfig, FFIDAnnouncementsLogger, FFIDApiResponse, FFIDCacheAdapter, FFIDCheckoutSessionResponse, type FFIDClient, FFIDConfig, FFIDCreateCheckoutParams, FFIDCreatePortalParams, FFIDError, FFIDLogger, FFIDOAuthUserInfo, FFIDOrganization, type FFIDOtpSendResponse, type FFIDOtpVerifyResponse, type FFIDPasswordResetConfirmResponse, type FFIDPasswordResetResponse, type FFIDPasswordResetVerifyResponse, FFIDPortalSessionResponse, FFIDProvider, type FFIDProviderProps, FFIDRedirectResult, type FFIDResetSessionResponse, FFIDSessionResponse, FFIDSubscription, FFIDSubscriptionCheckResponse, FFIDSubscriptionContextValue, FFIDUser, FFIDVerifyAccessTokenOptions, FFID_ANNOUNCEMENTS_ERROR_CODES, type KVNamespaceLike, ListAnnouncementsOptions, type TokenData, type TokenStore, type UseFFIDReturn, type WithFFIDAuthOptions, type WithSubscriptionOptions, createFFIDAnnouncementsClient, createFFIDClient, createKVCacheAdapter, createMemoryCacheAdapter, createTokenStore, generateCodeChallenge, generateCodeVerifier, retrieveCodeVerifier, storeCodeVerifier, useFFID, useSubscription, withFFIDAuth, withSubscription };
439
+ export { AnnouncementListResponse, DEFAULT_API_BASE_URL, FFIDAnnouncementsApiResponse, type FFIDAnnouncementsClient, FFIDAnnouncementsClientConfig, FFIDAnnouncementsLogger, FFIDApiResponse, FFIDCacheAdapter, FFIDCheckoutSessionResponse, type FFIDClient, FFIDConfig, FFIDCreateCheckoutParams, FFIDCreatePortalParams, FFIDError, FFIDLogger, FFIDOAuthUserInfo, FFIDOrganization, type FFIDOtpSendResponse, type FFIDOtpVerifyResponse, type FFIDPasswordResetConfirmResponse, type FFIDPasswordResetResponse, type FFIDPasswordResetVerifyResponse, FFIDPortalSessionResponse, FFIDProvider, type FFIDProviderProps, FFIDRedirectResult, type FFIDResetSessionResponse, FFIDSessionResponse, FFIDSubscription, FFIDSubscriptionCheckResponse, FFIDSubscriptionContextValue, FFIDUser, FFIDVerifyAccessTokenOptions, FFID_ANNOUNCEMENTS_ERROR_CODES, type KVNamespaceLike, ListAnnouncementsOptions, type RedirectToAuthorizeOptions, type TokenData, type TokenStore, type UseFFIDReturn, type WithFFIDAuthOptions, type WithSubscriptionOptions, createFFIDAnnouncementsClient, createFFIDClient, createKVCacheAdapter, createMemoryCacheAdapter, createTokenStore, generateCodeChallenge, generateCodeVerifier, retrieveCodeVerifier, storeCodeVerifier, useFFID, useSubscription, withFFIDAuth, withSubscription };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { F as FFIDConfig, a as FFIDApiResponse, b as FFIDSessionResponse, c as FFIDRedirectResult, d as FFIDError, e as FFIDSubscriptionCheckResponse, f as FFIDCreateCheckoutParams, g as FFIDCheckoutSessionResponse, h as FFIDCreatePortalParams, i as FFIDPortalSessionResponse, j as FFIDVerifyAccessTokenOptions, k as FFIDOAuthUserInfo, l as FFIDAuthMode, m as FFIDLogger, n as FFIDCacheAdapter, o as FFIDUser, p as FFIDOrganization, q as FFIDSubscription, r as FFIDSubscriptionContextValue, s as FFIDAnnouncementsClientConfig, L as ListAnnouncementsOptions, t as FFIDAnnouncementsApiResponse, A as AnnouncementListResponse, u as FFIDAnnouncementsLogger } from './index-GrAWBpmf.js';
2
- export { v as Announcement, w as AnnouncementStatus, x as AnnouncementType, y as FFIDAnnouncementBadge, z as FFIDAnnouncementList, B as FFIDAnnouncementsError, C as FFIDAnnouncementsErrorCode, D as FFIDAnnouncementsServerResponse, E as FFIDCacheConfig, G as FFIDContextValue, H as FFIDJwtClaims, I as FFIDLoginButton, J as FFIDOAuthTokenResponse, K as FFIDOAuthUserInfoMemberRole, M as FFIDOAuthUserInfoSubscription, N as FFIDOrganizationSwitcher, O as FFIDSeatModel, P as FFIDSubscriptionBadge, Q as FFIDSubscriptionStatus, R as FFIDTokenIntrospectionResponse, S as FFIDUserMenu, U as UseFFIDAnnouncementsOptions, T as UseFFIDAnnouncementsReturn, V as useFFIDAnnouncements } from './index-GrAWBpmf.js';
1
+ import { F as FFIDConfig, a as FFIDApiResponse, b as FFIDSessionResponse, c as FFIDRedirectResult, d as FFIDError, e as FFIDSubscriptionCheckResponse, f as FFIDCreateCheckoutParams, g as FFIDCheckoutSessionResponse, h as FFIDCreatePortalParams, i as FFIDPortalSessionResponse, j as FFIDVerifyAccessTokenOptions, k as FFIDOAuthUserInfo, l as FFIDAuthMode, m as FFIDLogger, n as FFIDCacheAdapter, o as FFIDUser, p as FFIDOrganization, q as FFIDSubscription, r as FFIDSubscriptionContextValue, s as FFIDAnnouncementsClientConfig, L as ListAnnouncementsOptions, t as FFIDAnnouncementsApiResponse, A as AnnouncementListResponse, u as FFIDAnnouncementsLogger } from './index-B9sSp8kZ.js';
2
+ export { v as Announcement, w as AnnouncementStatus, x as AnnouncementType, y as FFIDAnnouncementBadge, z as FFIDAnnouncementList, B as FFIDAnnouncementsError, C as FFIDAnnouncementsErrorCode, D as FFIDAnnouncementsServerResponse, E as FFIDCacheConfig, G as FFIDContextValue, H as FFIDJwtClaims, I as FFIDLoginButton, J as FFIDOAuthTokenResponse, K as FFIDOAuthUserInfoMemberRole, M as FFIDOAuthUserInfoSubscription, N as FFIDOrganizationSwitcher, O as FFIDSeatModel, P as FFIDSubscriptionBadge, Q as FFIDSubscriptionStatus, R as FFIDTokenIntrospectionResponse, S as FFIDUserMenu, U as UseFFIDAnnouncementsOptions, T as UseFFIDAnnouncementsReturn, V as useFFIDAnnouncements } from './index-B9sSp8kZ.js';
3
3
  import * as react_jsx_runtime from 'react/jsx-runtime';
4
4
  import { ReactNode, ComponentType, FC } from 'react';
5
5
 
@@ -129,12 +129,23 @@ type FFIDResetSessionResponse = FFIDPasswordResetResponse;
129
129
  /** Response from confirmPasswordReset */
130
130
  type FFIDPasswordResetConfirmResponse = FFIDPasswordResetResponse;
131
131
 
132
+ /** Redirect and URL generation - redirectToLogin / redirectToAuthorize / redirectToLogout / getLoginUrl / getSignupUrl / getLogoutUrl */
133
+
134
+ /** Options for redirectToAuthorize */
135
+ interface RedirectToAuthorizeOptions {
136
+ /** Target organization ID — triggers org-scoped OAuth re-authorization */
137
+ organizationId?: string;
138
+ }
139
+
132
140
  /** Creates an FFID API client instance */
133
141
  declare function createFFIDClient(config: FFIDConfig): {
134
142
  getSession: () => Promise<FFIDApiResponse<FFIDSessionResponse>>;
135
143
  signOut: () => Promise<FFIDApiResponse<void>>;
136
144
  redirectToLogin: () => Promise<FFIDRedirectResult>;
145
+ redirectToAuthorize: (options?: RedirectToAuthorizeOptions) => Promise<FFIDRedirectResult>;
146
+ redirectToLogout: (postLogoutRedirectUri?: string) => FFIDRedirectResult;
137
147
  getLoginUrl: (redirectUrl?: string) => string;
148
+ getLogoutUrl: (postLogoutRedirectUri?: string) => string;
138
149
  getSignupUrl: (redirectUrl?: string) => string;
139
150
  createError: (code: string, message: string) => FFIDError;
140
151
  exchangeCodeForTokens: (code: string, codeVerifier?: string) => Promise<FFIDApiResponse<void>>;
@@ -235,7 +246,10 @@ interface UseFFIDReturn {
235
246
  login: () => void;
236
247
  /** Sign out */
237
248
  logout: () => Promise<void>;
238
- /** Switch current organization */
249
+ /**
250
+ * Switch current organization.
251
+ * Cookie mode: local state change. Token mode: OAuth re-authorization redirect.
252
+ */
239
253
  switchOrganization: (organizationId: string) => void;
240
254
  /** Refresh session data */
241
255
  refresh: () => Promise<void>;
@@ -422,4 +436,4 @@ declare function createFFIDAnnouncementsClient(config?: FFIDAnnouncementsClientC
422
436
  /** Type of the FFID Announcements client */
423
437
  type FFIDAnnouncementsClient = ReturnType<typeof createFFIDAnnouncementsClient>;
424
438
 
425
- export { AnnouncementListResponse, DEFAULT_API_BASE_URL, FFIDAnnouncementsApiResponse, type FFIDAnnouncementsClient, FFIDAnnouncementsClientConfig, FFIDAnnouncementsLogger, FFIDApiResponse, FFIDCacheAdapter, FFIDCheckoutSessionResponse, type FFIDClient, FFIDConfig, FFIDCreateCheckoutParams, FFIDCreatePortalParams, FFIDError, FFIDLogger, FFIDOAuthUserInfo, FFIDOrganization, type FFIDOtpSendResponse, type FFIDOtpVerifyResponse, type FFIDPasswordResetConfirmResponse, type FFIDPasswordResetResponse, type FFIDPasswordResetVerifyResponse, FFIDPortalSessionResponse, FFIDProvider, type FFIDProviderProps, FFIDRedirectResult, type FFIDResetSessionResponse, FFIDSessionResponse, FFIDSubscription, FFIDSubscriptionCheckResponse, FFIDSubscriptionContextValue, FFIDUser, FFIDVerifyAccessTokenOptions, FFID_ANNOUNCEMENTS_ERROR_CODES, type KVNamespaceLike, ListAnnouncementsOptions, type TokenData, type TokenStore, type UseFFIDReturn, type WithFFIDAuthOptions, type WithSubscriptionOptions, createFFIDAnnouncementsClient, createFFIDClient, createKVCacheAdapter, createMemoryCacheAdapter, createTokenStore, generateCodeChallenge, generateCodeVerifier, retrieveCodeVerifier, storeCodeVerifier, useFFID, useSubscription, withFFIDAuth, withSubscription };
439
+ export { AnnouncementListResponse, DEFAULT_API_BASE_URL, FFIDAnnouncementsApiResponse, type FFIDAnnouncementsClient, FFIDAnnouncementsClientConfig, FFIDAnnouncementsLogger, FFIDApiResponse, FFIDCacheAdapter, FFIDCheckoutSessionResponse, type FFIDClient, FFIDConfig, FFIDCreateCheckoutParams, FFIDCreatePortalParams, FFIDError, FFIDLogger, FFIDOAuthUserInfo, FFIDOrganization, type FFIDOtpSendResponse, type FFIDOtpVerifyResponse, type FFIDPasswordResetConfirmResponse, type FFIDPasswordResetResponse, type FFIDPasswordResetVerifyResponse, FFIDPortalSessionResponse, FFIDProvider, type FFIDProviderProps, FFIDRedirectResult, type FFIDResetSessionResponse, FFIDSessionResponse, FFIDSubscription, FFIDSubscriptionCheckResponse, FFIDSubscriptionContextValue, FFIDUser, FFIDVerifyAccessTokenOptions, FFID_ANNOUNCEMENTS_ERROR_CODES, type KVNamespaceLike, ListAnnouncementsOptions, type RedirectToAuthorizeOptions, type TokenData, type TokenStore, type UseFFIDReturn, type WithFFIDAuthOptions, type WithSubscriptionOptions, createFFIDAnnouncementsClient, createFFIDClient, createKVCacheAdapter, createMemoryCacheAdapter, createTokenStore, generateCodeChallenge, generateCodeVerifier, retrieveCodeVerifier, storeCodeVerifier, useFFID, useSubscription, withFFIDAuth, withSubscription };
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { useFFIDContext } from './chunk-KFOIVZEY.js';
2
- export { DEFAULT_API_BASE_URL, FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDProvider, FFIDSubscriptionBadge, FFIDUserMenu, FFID_ANNOUNCEMENTS_ERROR_CODES, createFFIDAnnouncementsClient, createFFIDClient, createTokenStore, generateCodeChallenge, generateCodeVerifier, retrieveCodeVerifier, storeCodeVerifier, useFFID, useFFIDAnnouncements, useSubscription, withSubscription } from './chunk-KFOIVZEY.js';
1
+ import { useFFIDContext } from './chunk-4LRZTABD.js';
2
+ export { DEFAULT_API_BASE_URL, FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDProvider, FFIDSubscriptionBadge, FFIDUserMenu, FFID_ANNOUNCEMENTS_ERROR_CODES, createFFIDAnnouncementsClient, createFFIDClient, createTokenStore, generateCodeChallenge, generateCodeVerifier, retrieveCodeVerifier, storeCodeVerifier, useFFID, useFFIDAnnouncements, useSubscription, withSubscription } from './chunk-4LRZTABD.js';
3
3
  import { useRef, useEffect } from 'react';
4
4
  import { jsx, Fragment } from 'react/jsx-runtime';
5
5
 
@@ -109,6 +109,10 @@ function normalizeUserinfo(raw) {
109
109
  email: raw.email,
110
110
  name: raw.name,
111
111
  picture: raw.picture,
112
+ companyName: raw.company_name ?? null,
113
+ department: raw.department ?? null,
114
+ position: raw.position ?? null,
115
+ phoneNumber: raw.phone_number ?? null,
112
116
  organizationId: raw.organization_id ?? null,
113
117
  subscription: raw.subscription ? {
114
118
  subscriptionId: raw.subscription.subscription_id ?? null,
@@ -116,7 +120,8 @@ function normalizeUserinfo(raw) {
116
120
  planCode: raw.subscription.plan_code ?? null,
117
121
  seatModel: raw.subscription.seat_model ?? null,
118
122
  memberRole: raw.subscription.member_role ?? null,
119
- organizationId: raw.subscription.organization_id ?? null
123
+ organizationId: raw.subscription.organization_id ?? null,
124
+ hasSeatAssignment: raw.subscription.has_seat_assignment ?? null
120
125
  } : void 0
121
126
  };
122
127
  }
@@ -341,6 +346,10 @@ function createVerifyAccessToken(deps) {
341
346
  email: introspectResponse.email ?? null,
342
347
  name: introspectResponse.name ?? null,
343
348
  picture: introspectResponse.picture ?? null,
349
+ company_name: introspectResponse.company_name ?? null,
350
+ department: introspectResponse.department ?? null,
351
+ position: introspectResponse.position ?? null,
352
+ phone_number: introspectResponse.phone_number ?? null,
344
353
  organization_id: introspectResponse.organization_id ?? null
345
354
  };
346
355
  const raw = introspectResponse.subscription ? {
@@ -351,7 +360,8 @@ function createVerifyAccessToken(deps) {
351
360
  plan_code: introspectResponse.subscription.plan_code,
352
361
  seat_model: introspectResponse.subscription.seat_model,
353
362
  member_role: introspectResponse.subscription.member_role,
354
- organization_id: introspectResponse.subscription.organization_id
363
+ organization_id: introspectResponse.subscription.organization_id,
364
+ has_seat_assignment: introspectResponse.subscription.has_seat_assignment ?? false
355
365
  }
356
366
  } : base;
357
367
  const userinfo = normalizeUserinfo(raw);
@@ -433,7 +443,7 @@ function createBillingMethods(deps) {
433
443
  }
434
444
 
435
445
  // src/client/version-check.ts
436
- var SDK_VERSION = "1.13.0";
446
+ var SDK_VERSION = "1.17.0";
437
447
  var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
438
448
  var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
439
449
  function sdkHeaders() {
@@ -929,6 +939,7 @@ function base64UrlEncode(buffer) {
929
939
 
930
940
  // src/client/redirect.ts
931
941
  var OAUTH_AUTHORIZE_ENDPOINT = "/api/v1/oauth/authorize";
942
+ var AUTH_LOGOUT_ENDPOINT = "/api/v1/auth/logout";
932
943
  var STATE_RANDOM_BYTES = 16;
933
944
  var HEX_BASE2 = 16;
934
945
  function generateRandomState() {
@@ -945,7 +956,11 @@ function createRedirectMethods(deps) {
945
956
  resolvedRedirectUri,
946
957
  logger
947
958
  } = deps;
948
- async function redirectToAuthorize() {
959
+ async function redirectToAuthorize(options) {
960
+ if (typeof window === "undefined") {
961
+ logger.warn("SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093");
962
+ return { success: false, error: "SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093" };
963
+ }
949
964
  const verifier = generateCodeVerifier();
950
965
  storeCodeVerifier(verifier, logger);
951
966
  let challenge;
@@ -966,6 +981,14 @@ function createRedirectMethods(deps) {
966
981
  code_challenge: challenge,
967
982
  code_challenge_method: "S256"
968
983
  });
984
+ if (options?.organizationId !== void 0) {
985
+ const trimmedOrgId = options.organizationId.trim();
986
+ if (!trimmedOrgId) {
987
+ logger.error("organizationId \u304C\u7A7A\u6587\u5B57\u3067\u3059");
988
+ return { success: false, error: "organizationId \u306F\u7A7A\u306B\u3067\u304D\u307E\u305B\u3093" };
989
+ }
990
+ params.set("organization_id", trimmedOrgId);
991
+ }
969
992
  const authorizeUrl = `${baseUrl}${OAUTH_AUTHORIZE_ENDPOINT}?${params.toString()}`;
970
993
  logger.debug("Redirecting to authorize:", authorizeUrl);
971
994
  window.location.href = authorizeUrl;
@@ -1009,7 +1032,25 @@ function createRedirectMethods(deps) {
1009
1032
  }
1010
1033
  return `${baseUrl}/signup?redirect=${encodeURIComponent(redirect)}&service=${encodeURIComponent(serviceCode)}`;
1011
1034
  }
1012
- return { redirectToLogin, redirectToAuthorize, getLoginUrl, getSignupUrl };
1035
+ function getLogoutUrl(postLogoutRedirectUri) {
1036
+ const url = new URL(`${baseUrl}${AUTH_LOGOUT_ENDPOINT}`);
1037
+ url.searchParams.set("client_id", clientId);
1038
+ if (postLogoutRedirectUri != null) {
1039
+ url.searchParams.set("post_logout_redirect_uri", postLogoutRedirectUri);
1040
+ }
1041
+ return url.toString();
1042
+ }
1043
+ function redirectToLogout(postLogoutRedirectUri) {
1044
+ if (typeof window === "undefined") {
1045
+ logger.warn("SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093");
1046
+ return { success: false, error: "SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093" };
1047
+ }
1048
+ const logoutUrl = getLogoutUrl(postLogoutRedirectUri);
1049
+ logger.debug("Redirecting to logout:", logoutUrl);
1050
+ window.location.href = logoutUrl;
1051
+ return { success: true };
1052
+ }
1053
+ return { redirectToLogin, redirectToAuthorize, getLoginUrl, getSignupUrl, getLogoutUrl, redirectToLogout };
1013
1054
  }
1014
1055
 
1015
1056
  // src/client/password-reset.ts
@@ -1435,7 +1476,7 @@ function createFFIDClient(config) {
1435
1476
  }
1436
1477
  return signOutCookie();
1437
1478
  }
1438
- const { redirectToLogin, getLoginUrl, getSignupUrl } = createRedirectMethods({
1479
+ const { redirectToLogin, redirectToAuthorize, getLoginUrl, getSignupUrl, getLogoutUrl, redirectToLogout } = createRedirectMethods({
1439
1480
  authMode,
1440
1481
  baseUrl,
1441
1482
  clientId,
@@ -1496,7 +1537,10 @@ function createFFIDClient(config) {
1496
1537
  getSession,
1497
1538
  signOut,
1498
1539
  redirectToLogin,
1540
+ redirectToAuthorize,
1541
+ redirectToLogout,
1499
1542
  getLoginUrl,
1543
+ getLogoutUrl,
1500
1544
  getSignupUrl,
1501
1545
  createError,
1502
1546
  exchangeCodeForTokens,
@@ -106,6 +106,8 @@ interface FFIDSubscription {
106
106
  status: 'trialing' | 'active' | 'past_due' | 'canceled' | 'paused';
107
107
  /** Current billing period end date */
108
108
  currentPeriodEnd: string | null;
109
+ /** Trial end date (null if not a trial or no trial_end set) */
110
+ trialEnd: string | null;
109
111
  /** Service seat model (available when sourced from OAuth userinfo) */
110
112
  seatModel?: FFIDSeatModel | undefined;
111
113
  /** Member role in the resolved organization context */
@@ -121,6 +123,7 @@ interface FFIDOAuthUserInfoSubscription {
121
123
  seatModel: FFIDSeatModel | null;
122
124
  memberRole: FFIDOAuthUserInfoMemberRole | null;
123
125
  organizationId: string | null;
126
+ hasSeatAssignment: boolean | null;
124
127
  }
125
128
  /** OAuth userinfo response exposed by FFID */
126
129
  interface FFIDOAuthUserInfo {
@@ -128,6 +131,10 @@ interface FFIDOAuthUserInfo {
128
131
  email: string | null;
129
132
  name: string | null;
130
133
  picture: string | null;
134
+ companyName?: string | null | undefined;
135
+ department?: string | null | undefined;
136
+ position?: string | null | undefined;
137
+ phoneNumber?: string | null | undefined;
131
138
  organizationId?: string | null | undefined;
132
139
  subscription?: FFIDOAuthUserInfoSubscription | undefined;
133
140
  }
@@ -302,7 +309,7 @@ interface FFIDCreatePortalParams {
302
309
  returnUrl: string;
303
310
  }
304
311
  /**
305
- * Result of a redirect operation (redirectToLogin / redirectToAuthorize)
312
+ * Result of a redirect operation (redirectToLogin / redirectToAuthorize / redirectToLogout)
306
313
  *
307
314
  * Structured return type so callers can inspect failure reasons
308
315
  * instead of receiving a bare `false`.
@@ -351,6 +358,14 @@ type FFIDResetSessionResponse = FFIDPasswordResetResponse;
351
358
  /** Response from confirmPasswordReset */
352
359
  type FFIDPasswordResetConfirmResponse = FFIDPasswordResetResponse;
353
360
 
361
+ /** Redirect and URL generation - redirectToLogin / redirectToAuthorize / redirectToLogout / getLoginUrl / getSignupUrl / getLogoutUrl */
362
+
363
+ /** Options for redirectToAuthorize */
364
+ interface RedirectToAuthorizeOptions {
365
+ /** Target organization ID — triggers org-scoped OAuth re-authorization */
366
+ organizationId?: string;
367
+ }
368
+
354
369
  /**
355
370
  * Token Store
356
371
  *
@@ -397,7 +412,10 @@ declare function createFFIDClient(config: FFIDConfig): {
397
412
  getSession: () => Promise<FFIDApiResponse<FFIDSessionResponse>>;
398
413
  signOut: () => Promise<FFIDApiResponse<void>>;
399
414
  redirectToLogin: () => Promise<FFIDRedirectResult>;
415
+ redirectToAuthorize: (options?: RedirectToAuthorizeOptions) => Promise<FFIDRedirectResult>;
416
+ redirectToLogout: (postLogoutRedirectUri?: string) => FFIDRedirectResult;
400
417
  getLoginUrl: (redirectUrl?: string) => string;
418
+ getLogoutUrl: (postLogoutRedirectUri?: string) => string;
401
419
  getSignupUrl: (redirectUrl?: string) => string;
402
420
  createError: (code: string, message: string) => FFIDError;
403
421
  exchangeCodeForTokens: (code: string, codeVerifier?: string) => Promise<FFIDApiResponse<void>>;
@@ -106,6 +106,8 @@ interface FFIDSubscription {
106
106
  status: 'trialing' | 'active' | 'past_due' | 'canceled' | 'paused';
107
107
  /** Current billing period end date */
108
108
  currentPeriodEnd: string | null;
109
+ /** Trial end date (null if not a trial or no trial_end set) */
110
+ trialEnd: string | null;
109
111
  /** Service seat model (available when sourced from OAuth userinfo) */
110
112
  seatModel?: FFIDSeatModel | undefined;
111
113
  /** Member role in the resolved organization context */
@@ -121,6 +123,7 @@ interface FFIDOAuthUserInfoSubscription {
121
123
  seatModel: FFIDSeatModel | null;
122
124
  memberRole: FFIDOAuthUserInfoMemberRole | null;
123
125
  organizationId: string | null;
126
+ hasSeatAssignment: boolean | null;
124
127
  }
125
128
  /** OAuth userinfo response exposed by FFID */
126
129
  interface FFIDOAuthUserInfo {
@@ -128,6 +131,10 @@ interface FFIDOAuthUserInfo {
128
131
  email: string | null;
129
132
  name: string | null;
130
133
  picture: string | null;
134
+ companyName?: string | null | undefined;
135
+ department?: string | null | undefined;
136
+ position?: string | null | undefined;
137
+ phoneNumber?: string | null | undefined;
131
138
  organizationId?: string | null | undefined;
132
139
  subscription?: FFIDOAuthUserInfoSubscription | undefined;
133
140
  }
@@ -302,7 +309,7 @@ interface FFIDCreatePortalParams {
302
309
  returnUrl: string;
303
310
  }
304
311
  /**
305
- * Result of a redirect operation (redirectToLogin / redirectToAuthorize)
312
+ * Result of a redirect operation (redirectToLogin / redirectToAuthorize / redirectToLogout)
306
313
  *
307
314
  * Structured return type so callers can inspect failure reasons
308
315
  * instead of receiving a bare `false`.
@@ -351,6 +358,14 @@ type FFIDResetSessionResponse = FFIDPasswordResetResponse;
351
358
  /** Response from confirmPasswordReset */
352
359
  type FFIDPasswordResetConfirmResponse = FFIDPasswordResetResponse;
353
360
 
361
+ /** Redirect and URL generation - redirectToLogin / redirectToAuthorize / redirectToLogout / getLoginUrl / getSignupUrl / getLogoutUrl */
362
+
363
+ /** Options for redirectToAuthorize */
364
+ interface RedirectToAuthorizeOptions {
365
+ /** Target organization ID — triggers org-scoped OAuth re-authorization */
366
+ organizationId?: string;
367
+ }
368
+
354
369
  /**
355
370
  * Token Store
356
371
  *
@@ -397,7 +412,10 @@ declare function createFFIDClient(config: FFIDConfig): {
397
412
  getSession: () => Promise<FFIDApiResponse<FFIDSessionResponse>>;
398
413
  signOut: () => Promise<FFIDApiResponse<void>>;
399
414
  redirectToLogin: () => Promise<FFIDRedirectResult>;
415
+ redirectToAuthorize: (options?: RedirectToAuthorizeOptions) => Promise<FFIDRedirectResult>;
416
+ redirectToLogout: (postLogoutRedirectUri?: string) => FFIDRedirectResult;
400
417
  getLoginUrl: (redirectUrl?: string) => string;
418
+ getLogoutUrl: (postLogoutRedirectUri?: string) => string;
401
419
  getSignupUrl: (redirectUrl?: string) => string;
402
420
  createError: (code: string, message: string) => FFIDError;
403
421
  exchangeCodeForTokens: (code: string, codeVerifier?: string) => Promise<FFIDApiResponse<void>>;
@@ -108,6 +108,10 @@ function normalizeUserinfo(raw) {
108
108
  email: raw.email,
109
109
  name: raw.name,
110
110
  picture: raw.picture,
111
+ companyName: raw.company_name ?? null,
112
+ department: raw.department ?? null,
113
+ position: raw.position ?? null,
114
+ phoneNumber: raw.phone_number ?? null,
111
115
  organizationId: raw.organization_id ?? null,
112
116
  subscription: raw.subscription ? {
113
117
  subscriptionId: raw.subscription.subscription_id ?? null,
@@ -115,7 +119,8 @@ function normalizeUserinfo(raw) {
115
119
  planCode: raw.subscription.plan_code ?? null,
116
120
  seatModel: raw.subscription.seat_model ?? null,
117
121
  memberRole: raw.subscription.member_role ?? null,
118
- organizationId: raw.subscription.organization_id ?? null
122
+ organizationId: raw.subscription.organization_id ?? null,
123
+ hasSeatAssignment: raw.subscription.has_seat_assignment ?? null
119
124
  } : void 0
120
125
  };
121
126
  }
@@ -340,6 +345,10 @@ function createVerifyAccessToken(deps) {
340
345
  email: introspectResponse.email ?? null,
341
346
  name: introspectResponse.name ?? null,
342
347
  picture: introspectResponse.picture ?? null,
348
+ company_name: introspectResponse.company_name ?? null,
349
+ department: introspectResponse.department ?? null,
350
+ position: introspectResponse.position ?? null,
351
+ phone_number: introspectResponse.phone_number ?? null,
343
352
  organization_id: introspectResponse.organization_id ?? null
344
353
  };
345
354
  const raw = introspectResponse.subscription ? {
@@ -350,7 +359,8 @@ function createVerifyAccessToken(deps) {
350
359
  plan_code: introspectResponse.subscription.plan_code,
351
360
  seat_model: introspectResponse.subscription.seat_model,
352
361
  member_role: introspectResponse.subscription.member_role,
353
- organization_id: introspectResponse.subscription.organization_id
362
+ organization_id: introspectResponse.subscription.organization_id,
363
+ has_seat_assignment: introspectResponse.subscription.has_seat_assignment ?? false
354
364
  }
355
365
  } : base;
356
366
  const userinfo = normalizeUserinfo(raw);
@@ -432,7 +442,7 @@ function createBillingMethods(deps) {
432
442
  }
433
443
 
434
444
  // src/client/version-check.ts
435
- var SDK_VERSION = "1.13.0";
445
+ var SDK_VERSION = "1.17.0";
436
446
  var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
437
447
  var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
438
448
  function sdkHeaders() {
@@ -928,6 +938,7 @@ function base64UrlEncode(buffer) {
928
938
 
929
939
  // src/client/redirect.ts
930
940
  var OAUTH_AUTHORIZE_ENDPOINT = "/api/v1/oauth/authorize";
941
+ var AUTH_LOGOUT_ENDPOINT = "/api/v1/auth/logout";
931
942
  var STATE_RANDOM_BYTES = 16;
932
943
  var HEX_BASE2 = 16;
933
944
  function generateRandomState() {
@@ -944,7 +955,11 @@ function createRedirectMethods(deps) {
944
955
  resolvedRedirectUri,
945
956
  logger
946
957
  } = deps;
947
- async function redirectToAuthorize() {
958
+ async function redirectToAuthorize(options) {
959
+ if (typeof window === "undefined") {
960
+ logger.warn("SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093");
961
+ return { success: false, error: "SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093" };
962
+ }
948
963
  const verifier = generateCodeVerifier();
949
964
  storeCodeVerifier(verifier, logger);
950
965
  let challenge;
@@ -965,6 +980,14 @@ function createRedirectMethods(deps) {
965
980
  code_challenge: challenge,
966
981
  code_challenge_method: "S256"
967
982
  });
983
+ if (options?.organizationId !== void 0) {
984
+ const trimmedOrgId = options.organizationId.trim();
985
+ if (!trimmedOrgId) {
986
+ logger.error("organizationId \u304C\u7A7A\u6587\u5B57\u3067\u3059");
987
+ return { success: false, error: "organizationId \u306F\u7A7A\u306B\u3067\u304D\u307E\u305B\u3093" };
988
+ }
989
+ params.set("organization_id", trimmedOrgId);
990
+ }
968
991
  const authorizeUrl = `${baseUrl}${OAUTH_AUTHORIZE_ENDPOINT}?${params.toString()}`;
969
992
  logger.debug("Redirecting to authorize:", authorizeUrl);
970
993
  window.location.href = authorizeUrl;
@@ -1008,7 +1031,25 @@ function createRedirectMethods(deps) {
1008
1031
  }
1009
1032
  return `${baseUrl}/signup?redirect=${encodeURIComponent(redirect)}&service=${encodeURIComponent(serviceCode)}`;
1010
1033
  }
1011
- return { redirectToLogin, redirectToAuthorize, getLoginUrl, getSignupUrl };
1034
+ function getLogoutUrl(postLogoutRedirectUri) {
1035
+ const url = new URL(`${baseUrl}${AUTH_LOGOUT_ENDPOINT}`);
1036
+ url.searchParams.set("client_id", clientId);
1037
+ if (postLogoutRedirectUri != null) {
1038
+ url.searchParams.set("post_logout_redirect_uri", postLogoutRedirectUri);
1039
+ }
1040
+ return url.toString();
1041
+ }
1042
+ function redirectToLogout(postLogoutRedirectUri) {
1043
+ if (typeof window === "undefined") {
1044
+ logger.warn("SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093");
1045
+ return { success: false, error: "SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093" };
1046
+ }
1047
+ const logoutUrl = getLogoutUrl(postLogoutRedirectUri);
1048
+ logger.debug("Redirecting to logout:", logoutUrl);
1049
+ window.location.href = logoutUrl;
1050
+ return { success: true };
1051
+ }
1052
+ return { redirectToLogin, redirectToAuthorize, getLoginUrl, getSignupUrl, getLogoutUrl, redirectToLogout };
1012
1053
  }
1013
1054
 
1014
1055
  // src/client/password-reset.ts
@@ -1434,7 +1475,7 @@ function createFFIDClient(config) {
1434
1475
  }
1435
1476
  return signOutCookie();
1436
1477
  }
1437
- const { redirectToLogin, getLoginUrl, getSignupUrl } = createRedirectMethods({
1478
+ const { redirectToLogin, redirectToAuthorize, getLoginUrl, getSignupUrl, getLogoutUrl, redirectToLogout } = createRedirectMethods({
1438
1479
  authMode,
1439
1480
  baseUrl,
1440
1481
  clientId,
@@ -1495,7 +1536,10 @@ function createFFIDClient(config) {
1495
1536
  getSession,
1496
1537
  signOut,
1497
1538
  redirectToLogin,
1539
+ redirectToAuthorize,
1540
+ redirectToLogout,
1498
1541
  getLoginUrl,
1542
+ getLogoutUrl,
1499
1543
  getSignupUrl,
1500
1544
  createError,
1501
1545
  exchangeCodeForTokens,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feelflow/ffid-sdk",
3
- "version": "1.13.0",
3
+ "version": "1.17.0",
4
4
  "description": "FeelFlow ID Platform SDK for React/Next.js applications",
5
5
  "keywords": [
6
6
  "feelflow",