@feelflow/ffid-sdk 1.0.1 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -256,6 +256,23 @@ interface FFIDSubscription {
256
256
  }
257
257
  ```
258
258
 
259
+ ### OAuth userinfo の契約要約
260
+
261
+ token mode では SDK は `/api/v1/oauth/userinfo` を呼び出し、基本プロフィールに加えてサービス契約の要約を受け取ります。
262
+ この要約により、追加 API を呼ばずにプラン判定や UI 分岐を行えます。
263
+
264
+ ```ts
265
+ interface FFIDOAuthUserInfoSubscription {
266
+ status: 'trialing' | 'active' | 'past_due' | 'canceled' | 'paused' | null
267
+ planCode: string | null
268
+ seatModel: 'organization' | 'individual' | null
269
+ memberRole: 'owner' | 'admin' | 'member' | 'viewer' | null
270
+ organizationId: string | null
271
+ }
272
+ ```
273
+
274
+ `seatModel` はシートモデル識別用であり、organization と role は userinfo の解決済み組織文脈として扱います。
275
+
259
276
  ## 環境変数
260
277
 
261
278
  オプションで環境変数を使用してデフォルト設定を上書きできます:
@@ -137,6 +137,48 @@ function base64UrlEncode(buffer) {
137
137
  return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
138
138
  }
139
139
 
140
+ // src/client/oauth-userinfo.ts
141
+ var VALID_SUBSCRIPTION_STATUSES = ["trialing", "active", "past_due", "canceled", "paused"];
142
+ function isValidSubscriptionStatus(value) {
143
+ return VALID_SUBSCRIPTION_STATUSES.includes(value);
144
+ }
145
+ function normalizeUserinfo(raw) {
146
+ return {
147
+ sub: raw.sub,
148
+ email: raw.email,
149
+ name: raw.name,
150
+ picture: raw.picture,
151
+ organizationId: raw.organization_id ?? null,
152
+ subscription: raw.subscription ? {
153
+ status: raw.subscription.status ?? null,
154
+ planCode: raw.subscription.plan_code ?? null,
155
+ seatModel: raw.subscription.seat_model ?? null,
156
+ memberRole: raw.subscription.member_role ?? null,
157
+ organizationId: raw.subscription.organization_id ?? null
158
+ } : void 0
159
+ };
160
+ }
161
+ function mapUserinfoSubscriptionToSession(userinfo, serviceCode) {
162
+ const subscription = userinfo.subscription;
163
+ if (!subscription || !subscription.planCode || !isValidSubscriptionStatus(subscription.status)) {
164
+ return [];
165
+ }
166
+ return [
167
+ {
168
+ id: `userinfo:${serviceCode}`,
169
+ serviceCode,
170
+ serviceName: serviceCode,
171
+ planCode: subscription.planCode,
172
+ planName: subscription.planCode,
173
+ status: subscription.status,
174
+ currentPeriodEnd: null,
175
+ seatModel: subscription.seatModel ?? void 0,
176
+ memberRole: subscription.memberRole ?? void 0,
177
+ organizationId: subscription.organizationId
178
+ }
179
+ ];
180
+ }
181
+
140
182
  // src/client/ffid-client.ts
141
183
  var NO_CONTENT_STATUS = 204;
142
184
  var SESSION_ENDPOINT = "/api/v1/auth/session";
@@ -282,7 +324,24 @@ function createFFIDClient(config) {
282
324
  if (authMode === "token") {
283
325
  return getSessionFromUserinfo();
284
326
  }
285
- return fetchWithAuth(SESSION_ENDPOINT);
327
+ const result = await fetchWithAuth(SESSION_ENDPOINT);
328
+ if (result.data?.user) {
329
+ return {
330
+ ...result,
331
+ data: {
332
+ ...result.data,
333
+ user: normalizeFFIDUser(result.data.user)
334
+ }
335
+ };
336
+ }
337
+ return result;
338
+ }
339
+ function normalizeFFIDUser(user) {
340
+ return {
341
+ ...user,
342
+ locale: user.locale ?? null,
343
+ timezone: user.timezone ?? null
344
+ };
286
345
  }
287
346
  async function getSessionFromUserinfo() {
288
347
  const tokens = tokenStore.getTokens();
@@ -341,9 +400,9 @@ function createFFIDClient(config) {
341
400
  return refreshResult;
342
401
  }
343
402
  }
344
- let userinfo;
403
+ let rawUserinfo;
345
404
  try {
346
- userinfo = await response.json();
405
+ rawUserinfo = await response.json();
347
406
  } catch (parseError) {
348
407
  logger.error("Parse error:", parseError, "Status:", response.status);
349
408
  return {
@@ -354,7 +413,7 @@ function createFFIDClient(config) {
354
413
  };
355
414
  }
356
415
  if (!response.ok) {
357
- const errorBody = userinfo;
416
+ const errorBody = rawUserinfo;
358
417
  return {
359
418
  error: {
360
419
  code: errorBody.code ?? FFID_ERROR_CODES.UNKNOWN_ERROR,
@@ -362,6 +421,7 @@ function createFFIDClient(config) {
362
421
  }
363
422
  };
364
423
  }
424
+ const userinfo = normalizeUserinfo(rawUserinfo);
365
425
  const user = {
366
426
  id: userinfo.sub,
367
427
  email: userinfo.email ?? "",
@@ -375,7 +435,7 @@ function createFFIDClient(config) {
375
435
  data: {
376
436
  user,
377
437
  organizations: [],
378
- subscriptions: []
438
+ subscriptions: mapUserinfoSubscriptionToSession(userinfo, config.serviceCode)
379
439
  }
380
440
  };
381
441
  }
@@ -615,6 +675,10 @@ function createFFIDClient(config) {
615
675
  const redirect = redirectUrl ?? (typeof window !== "undefined" ? window.location.href : "");
616
676
  return `${baseUrl}/login?redirect=${encodeURIComponent(redirect)}&service=${encodeURIComponent(config.serviceCode)}`;
617
677
  }
678
+ function getSignupUrl(redirectUrl) {
679
+ const redirect = redirectUrl ?? (typeof window !== "undefined" ? window.location.href : "");
680
+ return `${baseUrl}/signup?redirect=${encodeURIComponent(redirect)}&service=${encodeURIComponent(config.serviceCode)}`;
681
+ }
618
682
  function createError(code, message) {
619
683
  return { code, message };
620
684
  }
@@ -623,6 +687,7 @@ function createFFIDClient(config) {
623
687
  signOut,
624
688
  redirectToLogin,
625
689
  getLoginUrl,
690
+ getSignupUrl,
626
691
  createError,
627
692
  exchangeCodeForTokens,
628
693
  refreshAccessToken,
@@ -900,6 +965,7 @@ function useFFIDClient() {
900
965
  // src/hooks/useFFID.ts
901
966
  function useFFID() {
902
967
  const context = useFFIDContext();
968
+ const client = useFFIDClient();
903
969
  return {
904
970
  user: context.user,
905
971
  organizations: context.organizations,
@@ -910,7 +976,9 @@ function useFFID() {
910
976
  login: context.login,
911
977
  logout: context.logout,
912
978
  switchOrganization: context.switchOrganization,
913
- refresh: context.refresh
979
+ refresh: context.refresh,
980
+ getLoginUrl: client.getLoginUrl,
981
+ getSignupUrl: client.getSignupUrl
914
982
  };
915
983
  }
916
984
  function FFIDLoginButton({
@@ -135,6 +135,48 @@ function base64UrlEncode(buffer) {
135
135
  return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
136
136
  }
137
137
 
138
+ // src/client/oauth-userinfo.ts
139
+ var VALID_SUBSCRIPTION_STATUSES = ["trialing", "active", "past_due", "canceled", "paused"];
140
+ function isValidSubscriptionStatus(value) {
141
+ return VALID_SUBSCRIPTION_STATUSES.includes(value);
142
+ }
143
+ function normalizeUserinfo(raw) {
144
+ return {
145
+ sub: raw.sub,
146
+ email: raw.email,
147
+ name: raw.name,
148
+ picture: raw.picture,
149
+ organizationId: raw.organization_id ?? null,
150
+ subscription: raw.subscription ? {
151
+ status: raw.subscription.status ?? null,
152
+ planCode: raw.subscription.plan_code ?? null,
153
+ seatModel: raw.subscription.seat_model ?? null,
154
+ memberRole: raw.subscription.member_role ?? null,
155
+ organizationId: raw.subscription.organization_id ?? null
156
+ } : void 0
157
+ };
158
+ }
159
+ function mapUserinfoSubscriptionToSession(userinfo, serviceCode) {
160
+ const subscription = userinfo.subscription;
161
+ if (!subscription || !subscription.planCode || !isValidSubscriptionStatus(subscription.status)) {
162
+ return [];
163
+ }
164
+ return [
165
+ {
166
+ id: `userinfo:${serviceCode}`,
167
+ serviceCode,
168
+ serviceName: serviceCode,
169
+ planCode: subscription.planCode,
170
+ planName: subscription.planCode,
171
+ status: subscription.status,
172
+ currentPeriodEnd: null,
173
+ seatModel: subscription.seatModel ?? void 0,
174
+ memberRole: subscription.memberRole ?? void 0,
175
+ organizationId: subscription.organizationId
176
+ }
177
+ ];
178
+ }
179
+
138
180
  // src/client/ffid-client.ts
139
181
  var NO_CONTENT_STATUS = 204;
140
182
  var SESSION_ENDPOINT = "/api/v1/auth/session";
@@ -280,7 +322,24 @@ function createFFIDClient(config) {
280
322
  if (authMode === "token") {
281
323
  return getSessionFromUserinfo();
282
324
  }
283
- return fetchWithAuth(SESSION_ENDPOINT);
325
+ const result = await fetchWithAuth(SESSION_ENDPOINT);
326
+ if (result.data?.user) {
327
+ return {
328
+ ...result,
329
+ data: {
330
+ ...result.data,
331
+ user: normalizeFFIDUser(result.data.user)
332
+ }
333
+ };
334
+ }
335
+ return result;
336
+ }
337
+ function normalizeFFIDUser(user) {
338
+ return {
339
+ ...user,
340
+ locale: user.locale ?? null,
341
+ timezone: user.timezone ?? null
342
+ };
284
343
  }
285
344
  async function getSessionFromUserinfo() {
286
345
  const tokens = tokenStore.getTokens();
@@ -339,9 +398,9 @@ function createFFIDClient(config) {
339
398
  return refreshResult;
340
399
  }
341
400
  }
342
- let userinfo;
401
+ let rawUserinfo;
343
402
  try {
344
- userinfo = await response.json();
403
+ rawUserinfo = await response.json();
345
404
  } catch (parseError) {
346
405
  logger.error("Parse error:", parseError, "Status:", response.status);
347
406
  return {
@@ -352,7 +411,7 @@ function createFFIDClient(config) {
352
411
  };
353
412
  }
354
413
  if (!response.ok) {
355
- const errorBody = userinfo;
414
+ const errorBody = rawUserinfo;
356
415
  return {
357
416
  error: {
358
417
  code: errorBody.code ?? FFID_ERROR_CODES.UNKNOWN_ERROR,
@@ -360,6 +419,7 @@ function createFFIDClient(config) {
360
419
  }
361
420
  };
362
421
  }
422
+ const userinfo = normalizeUserinfo(rawUserinfo);
363
423
  const user = {
364
424
  id: userinfo.sub,
365
425
  email: userinfo.email ?? "",
@@ -373,7 +433,7 @@ function createFFIDClient(config) {
373
433
  data: {
374
434
  user,
375
435
  organizations: [],
376
- subscriptions: []
436
+ subscriptions: mapUserinfoSubscriptionToSession(userinfo, config.serviceCode)
377
437
  }
378
438
  };
379
439
  }
@@ -613,6 +673,10 @@ function createFFIDClient(config) {
613
673
  const redirect = redirectUrl ?? (typeof window !== "undefined" ? window.location.href : "");
614
674
  return `${baseUrl}/login?redirect=${encodeURIComponent(redirect)}&service=${encodeURIComponent(config.serviceCode)}`;
615
675
  }
676
+ function getSignupUrl(redirectUrl) {
677
+ const redirect = redirectUrl ?? (typeof window !== "undefined" ? window.location.href : "");
678
+ return `${baseUrl}/signup?redirect=${encodeURIComponent(redirect)}&service=${encodeURIComponent(config.serviceCode)}`;
679
+ }
616
680
  function createError(code, message) {
617
681
  return { code, message };
618
682
  }
@@ -621,6 +685,7 @@ function createFFIDClient(config) {
621
685
  signOut,
622
686
  redirectToLogin,
623
687
  getLoginUrl,
688
+ getSignupUrl,
624
689
  createError,
625
690
  exchangeCodeForTokens,
626
691
  refreshAccessToken,
@@ -898,6 +963,7 @@ function useFFIDClient() {
898
963
  // src/hooks/useFFID.ts
899
964
  function useFFID() {
900
965
  const context = useFFIDContext();
966
+ const client = useFFIDClient();
901
967
  return {
902
968
  user: context.user,
903
969
  organizations: context.organizations,
@@ -908,7 +974,9 @@ function useFFID() {
908
974
  login: context.login,
909
975
  logout: context.logout,
910
976
  switchOrganization: context.switchOrganization,
911
- refresh: context.refresh
977
+ refresh: context.refresh,
978
+ getLoginUrl: client.getLoginUrl,
979
+ getSignupUrl: client.getSignupUrl
912
980
  };
913
981
  }
914
982
  function FFIDLoginButton({
@@ -1,30 +1,30 @@
1
1
  'use strict';
2
2
 
3
- var chunk53CYDXU3_cjs = require('../chunk-53CYDXU3.cjs');
3
+ var chunk4QMSVP3G_cjs = require('../chunk-4QMSVP3G.cjs');
4
4
 
5
5
 
6
6
 
7
7
  Object.defineProperty(exports, "FFIDAnnouncementBadge", {
8
8
  enumerable: true,
9
- get: function () { return chunk53CYDXU3_cjs.FFIDAnnouncementBadge; }
9
+ get: function () { return chunk4QMSVP3G_cjs.FFIDAnnouncementBadge; }
10
10
  });
11
11
  Object.defineProperty(exports, "FFIDAnnouncementList", {
12
12
  enumerable: true,
13
- get: function () { return chunk53CYDXU3_cjs.FFIDAnnouncementList; }
13
+ get: function () { return chunk4QMSVP3G_cjs.FFIDAnnouncementList; }
14
14
  });
15
15
  Object.defineProperty(exports, "FFIDLoginButton", {
16
16
  enumerable: true,
17
- get: function () { return chunk53CYDXU3_cjs.FFIDLoginButton; }
17
+ get: function () { return chunk4QMSVP3G_cjs.FFIDLoginButton; }
18
18
  });
19
19
  Object.defineProperty(exports, "FFIDOrganizationSwitcher", {
20
20
  enumerable: true,
21
- get: function () { return chunk53CYDXU3_cjs.FFIDOrganizationSwitcher; }
21
+ get: function () { return chunk4QMSVP3G_cjs.FFIDOrganizationSwitcher; }
22
22
  });
23
23
  Object.defineProperty(exports, "FFIDSubscriptionBadge", {
24
24
  enumerable: true,
25
- get: function () { return chunk53CYDXU3_cjs.FFIDSubscriptionBadge; }
25
+ get: function () { return chunk4QMSVP3G_cjs.FFIDSubscriptionBadge; }
26
26
  });
27
27
  Object.defineProperty(exports, "FFIDUserMenu", {
28
28
  enumerable: true,
29
- get: function () { return chunk53CYDXU3_cjs.FFIDUserMenu; }
29
+ get: function () { return chunk4QMSVP3G_cjs.FFIDUserMenu; }
30
30
  });
@@ -1,3 +1,3 @@
1
- export { k as FFIDAnnouncementBadge, C as FFIDAnnouncementBadgeClassNames, D as FFIDAnnouncementBadgeProps, l as FFIDAnnouncementList, E as FFIDAnnouncementListClassNames, G as FFIDAnnouncementListProps, s as FFIDLoginButton, H as FFIDLoginButtonProps, u as FFIDOrganizationSwitcher, I as FFIDOrganizationSwitcherClassNames, J as FFIDOrganizationSwitcherProps, x as FFIDSubscriptionBadge, K as FFIDSubscriptionBadgeClassNames, M as FFIDSubscriptionBadgeProps, y as FFIDUserMenu, N as FFIDUserMenuClassNames, O as FFIDUserMenuProps } from '../index-DKBSoAHg.cjs';
1
+ export { k as FFIDAnnouncementBadge, H as FFIDAnnouncementBadgeClassNames, I as FFIDAnnouncementBadgeProps, l as FFIDAnnouncementList, J as FFIDAnnouncementListClassNames, K as FFIDAnnouncementListProps, s as FFIDLoginButton, M as FFIDLoginButtonProps, x as FFIDOrganizationSwitcher, N as FFIDOrganizationSwitcherClassNames, O as FFIDOrganizationSwitcherProps, C as FFIDSubscriptionBadge, P as FFIDSubscriptionBadgeClassNames, Q as FFIDSubscriptionBadgeProps, D as FFIDUserMenu, R as FFIDUserMenuClassNames, S as FFIDUserMenuProps } from '../index-CyYHo3-R.cjs';
2
2
  import 'react/jsx-runtime';
3
3
  import 'react';
@@ -1,3 +1,3 @@
1
- export { k as FFIDAnnouncementBadge, C as FFIDAnnouncementBadgeClassNames, D as FFIDAnnouncementBadgeProps, l as FFIDAnnouncementList, E as FFIDAnnouncementListClassNames, G as FFIDAnnouncementListProps, s as FFIDLoginButton, H as FFIDLoginButtonProps, u as FFIDOrganizationSwitcher, I as FFIDOrganizationSwitcherClassNames, J as FFIDOrganizationSwitcherProps, x as FFIDSubscriptionBadge, K as FFIDSubscriptionBadgeClassNames, M as FFIDSubscriptionBadgeProps, y as FFIDUserMenu, N as FFIDUserMenuClassNames, O as FFIDUserMenuProps } from '../index-DKBSoAHg.js';
1
+ export { k as FFIDAnnouncementBadge, H as FFIDAnnouncementBadgeClassNames, I as FFIDAnnouncementBadgeProps, l as FFIDAnnouncementList, J as FFIDAnnouncementListClassNames, K as FFIDAnnouncementListProps, s as FFIDLoginButton, M as FFIDLoginButtonProps, x as FFIDOrganizationSwitcher, N as FFIDOrganizationSwitcherClassNames, O as FFIDOrganizationSwitcherProps, C as FFIDSubscriptionBadge, P as FFIDSubscriptionBadgeClassNames, Q as FFIDSubscriptionBadgeProps, D as FFIDUserMenu, R as FFIDUserMenuClassNames, S as FFIDUserMenuProps } from '../index-CyYHo3-R.js';
2
2
  import 'react/jsx-runtime';
3
3
  import 'react';
@@ -1 +1 @@
1
- export { FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDSubscriptionBadge, FFIDUserMenu } from '../chunk-GAAFEPIY.js';
1
+ export { FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDSubscriptionBadge, FFIDUserMenu } from '../chunk-USE5Q2OF.js';
@@ -9,6 +9,9 @@ import { ButtonHTMLAttributes, ReactNode, CSSProperties } from 'react';
9
9
  /**
10
10
  * User information from FFID
11
11
  */
12
+ type FFIDSeatModel = 'organization' | 'individual';
13
+ /** Userinfo member role for OAuth responses */
14
+ type FFIDOAuthUserInfoMemberRole = 'owner' | 'admin' | 'member' | 'viewer';
12
15
  interface FFIDUser {
13
16
  /** User ID (UUID) */
14
17
  id: string;
@@ -80,6 +83,29 @@ interface FFIDSubscription {
80
83
  status: 'trialing' | 'active' | 'past_due' | 'canceled' | 'paused';
81
84
  /** Current billing period end date */
82
85
  currentPeriodEnd: string | null;
86
+ /** Service seat model (available when sourced from OAuth userinfo) */
87
+ seatModel?: FFIDSeatModel | undefined;
88
+ /** Member role in the resolved organization context */
89
+ memberRole?: FFIDOAuthUserInfoMemberRole | undefined;
90
+ /** Organization ID used to resolve subscription access */
91
+ organizationId?: string | null | undefined;
92
+ }
93
+ /** OAuth userinfo subscription summary */
94
+ interface FFIDOAuthUserInfoSubscription {
95
+ status: 'trialing' | 'active' | 'past_due' | 'canceled' | 'paused' | null;
96
+ planCode: string | null;
97
+ seatModel: FFIDSeatModel | null;
98
+ memberRole: FFIDOAuthUserInfoMemberRole | null;
99
+ organizationId: string | null;
100
+ }
101
+ /** OAuth userinfo response exposed by FFID */
102
+ interface FFIDOAuthUserInfo {
103
+ sub: string;
104
+ email: string | null;
105
+ name: string | null;
106
+ picture: string | null;
107
+ organizationId?: string | null;
108
+ subscription?: FFIDOAuthUserInfoSubscription;
83
109
  }
84
110
  /**
85
111
  * SDK configuration options
@@ -577,4 +603,4 @@ interface FFIDAnnouncementListProps {
577
603
  */
578
604
  declare function FFIDAnnouncementList({ announcements, isLoading, className, classNames, style, formatDate, emptyMessage, loadingRender, renderItem, maxContentLines, }: FFIDAnnouncementListProps): react_jsx_runtime.JSX.Element;
579
605
 
580
- export { type AnnouncementListResponse as A, useFFIDAnnouncements as B, type FFIDAnnouncementBadgeClassNames as C, type FFIDAnnouncementBadgeProps as D, type FFIDAnnouncementListClassNames as E, type FFIDConfig as F, type FFIDAnnouncementListProps as G, type FFIDLoginButtonProps as H, type FFIDOrganizationSwitcherClassNames as I, type FFIDOrganizationSwitcherProps as J, type FFIDSubscriptionBadgeClassNames as K, type ListAnnouncementsOptions as L, type FFIDSubscriptionBadgeProps as M, type FFIDUserMenuClassNames as N, type FFIDUserMenuProps as O, type UseFFIDAnnouncementsOptions as U, type FFIDUser as a, type FFIDOrganization as b, type FFIDError as c, type FFIDSubscriptionContextValue as d, type FFIDAnnouncementsClientConfig as e, type FFIDAnnouncementsApiResponse as f, type FFIDAnnouncementsLogger as g, type Announcement as h, type AnnouncementStatus as i, type AnnouncementType as j, FFIDAnnouncementBadge as k, FFIDAnnouncementList as l, type FFIDAnnouncementsError as m, type FFIDAnnouncementsErrorCode as n, type FFIDAnnouncementsServerResponse as o, type FFIDApiResponse as p, type FFIDContextValue as q, type FFIDLogger as r, FFIDLoginButton as s, type FFIDOAuthTokenResponse as t, FFIDOrganizationSwitcher as u, type FFIDSessionResponse as v, type FFIDSubscription as w, FFIDSubscriptionBadge as x, FFIDUserMenu as y, type UseFFIDAnnouncementsReturn as z };
606
+ export { type AnnouncementListResponse as A, type FFIDSubscription as B, FFIDSubscriptionBadge as C, FFIDUserMenu as D, type UseFFIDAnnouncementsReturn as E, type FFIDConfig as F, useFFIDAnnouncements as G, type FFIDAnnouncementBadgeClassNames as H, type FFIDAnnouncementBadgeProps as I, type FFIDAnnouncementListClassNames as J, type FFIDAnnouncementListProps as K, type ListAnnouncementsOptions as L, type FFIDLoginButtonProps as M, type FFIDOrganizationSwitcherClassNames as N, type FFIDOrganizationSwitcherProps as O, type FFIDSubscriptionBadgeClassNames as P, type FFIDSubscriptionBadgeProps as Q, type FFIDUserMenuClassNames as R, type FFIDUserMenuProps as S, type UseFFIDAnnouncementsOptions as U, type FFIDUser as a, type FFIDOrganization as b, type FFIDError as c, type FFIDSubscriptionContextValue as d, type FFIDAnnouncementsClientConfig as e, type FFIDAnnouncementsApiResponse as f, type FFIDAnnouncementsLogger as g, type Announcement as h, type AnnouncementStatus as i, type AnnouncementType as j, FFIDAnnouncementBadge as k, FFIDAnnouncementList as l, type FFIDAnnouncementsError as m, type FFIDAnnouncementsErrorCode as n, type FFIDAnnouncementsServerResponse as o, type FFIDApiResponse as p, type FFIDContextValue as q, type FFIDLogger as r, FFIDLoginButton as s, type FFIDOAuthTokenResponse as t, type FFIDOAuthUserInfo as u, type FFIDOAuthUserInfoMemberRole as v, type FFIDOAuthUserInfoSubscription as w, FFIDOrganizationSwitcher as x, type FFIDSeatModel as y, type FFIDSessionResponse as z };
@@ -9,6 +9,9 @@ import { ButtonHTMLAttributes, ReactNode, CSSProperties } from 'react';
9
9
  /**
10
10
  * User information from FFID
11
11
  */
12
+ type FFIDSeatModel = 'organization' | 'individual';
13
+ /** Userinfo member role for OAuth responses */
14
+ type FFIDOAuthUserInfoMemberRole = 'owner' | 'admin' | 'member' | 'viewer';
12
15
  interface FFIDUser {
13
16
  /** User ID (UUID) */
14
17
  id: string;
@@ -80,6 +83,29 @@ interface FFIDSubscription {
80
83
  status: 'trialing' | 'active' | 'past_due' | 'canceled' | 'paused';
81
84
  /** Current billing period end date */
82
85
  currentPeriodEnd: string | null;
86
+ /** Service seat model (available when sourced from OAuth userinfo) */
87
+ seatModel?: FFIDSeatModel | undefined;
88
+ /** Member role in the resolved organization context */
89
+ memberRole?: FFIDOAuthUserInfoMemberRole | undefined;
90
+ /** Organization ID used to resolve subscription access */
91
+ organizationId?: string | null | undefined;
92
+ }
93
+ /** OAuth userinfo subscription summary */
94
+ interface FFIDOAuthUserInfoSubscription {
95
+ status: 'trialing' | 'active' | 'past_due' | 'canceled' | 'paused' | null;
96
+ planCode: string | null;
97
+ seatModel: FFIDSeatModel | null;
98
+ memberRole: FFIDOAuthUserInfoMemberRole | null;
99
+ organizationId: string | null;
100
+ }
101
+ /** OAuth userinfo response exposed by FFID */
102
+ interface FFIDOAuthUserInfo {
103
+ sub: string;
104
+ email: string | null;
105
+ name: string | null;
106
+ picture: string | null;
107
+ organizationId?: string | null;
108
+ subscription?: FFIDOAuthUserInfoSubscription;
83
109
  }
84
110
  /**
85
111
  * SDK configuration options
@@ -577,4 +603,4 @@ interface FFIDAnnouncementListProps {
577
603
  */
578
604
  declare function FFIDAnnouncementList({ announcements, isLoading, className, classNames, style, formatDate, emptyMessage, loadingRender, renderItem, maxContentLines, }: FFIDAnnouncementListProps): react_jsx_runtime.JSX.Element;
579
605
 
580
- export { type AnnouncementListResponse as A, useFFIDAnnouncements as B, type FFIDAnnouncementBadgeClassNames as C, type FFIDAnnouncementBadgeProps as D, type FFIDAnnouncementListClassNames as E, type FFIDConfig as F, type FFIDAnnouncementListProps as G, type FFIDLoginButtonProps as H, type FFIDOrganizationSwitcherClassNames as I, type FFIDOrganizationSwitcherProps as J, type FFIDSubscriptionBadgeClassNames as K, type ListAnnouncementsOptions as L, type FFIDSubscriptionBadgeProps as M, type FFIDUserMenuClassNames as N, type FFIDUserMenuProps as O, type UseFFIDAnnouncementsOptions as U, type FFIDUser as a, type FFIDOrganization as b, type FFIDError as c, type FFIDSubscriptionContextValue as d, type FFIDAnnouncementsClientConfig as e, type FFIDAnnouncementsApiResponse as f, type FFIDAnnouncementsLogger as g, type Announcement as h, type AnnouncementStatus as i, type AnnouncementType as j, FFIDAnnouncementBadge as k, FFIDAnnouncementList as l, type FFIDAnnouncementsError as m, type FFIDAnnouncementsErrorCode as n, type FFIDAnnouncementsServerResponse as o, type FFIDApiResponse as p, type FFIDContextValue as q, type FFIDLogger as r, FFIDLoginButton as s, type FFIDOAuthTokenResponse as t, FFIDOrganizationSwitcher as u, type FFIDSessionResponse as v, type FFIDSubscription as w, FFIDSubscriptionBadge as x, FFIDUserMenu as y, type UseFFIDAnnouncementsReturn as z };
606
+ export { type AnnouncementListResponse as A, type FFIDSubscription as B, FFIDSubscriptionBadge as C, FFIDUserMenu as D, type UseFFIDAnnouncementsReturn as E, type FFIDConfig as F, useFFIDAnnouncements as G, type FFIDAnnouncementBadgeClassNames as H, type FFIDAnnouncementBadgeProps as I, type FFIDAnnouncementListClassNames as J, type FFIDAnnouncementListProps as K, type ListAnnouncementsOptions as L, type FFIDLoginButtonProps as M, type FFIDOrganizationSwitcherClassNames as N, type FFIDOrganizationSwitcherProps as O, type FFIDSubscriptionBadgeClassNames as P, type FFIDSubscriptionBadgeProps as Q, type FFIDUserMenuClassNames as R, type FFIDUserMenuProps as S, type UseFFIDAnnouncementsOptions as U, type FFIDUser as a, type FFIDOrganization as b, type FFIDError as c, type FFIDSubscriptionContextValue as d, type FFIDAnnouncementsClientConfig as e, type FFIDAnnouncementsApiResponse as f, type FFIDAnnouncementsLogger as g, type Announcement as h, type AnnouncementStatus as i, type AnnouncementType as j, FFIDAnnouncementBadge as k, FFIDAnnouncementList as l, type FFIDAnnouncementsError as m, type FFIDAnnouncementsErrorCode as n, type FFIDAnnouncementsServerResponse as o, type FFIDApiResponse as p, type FFIDContextValue as q, type FFIDLogger as r, FFIDLoginButton as s, type FFIDOAuthTokenResponse as t, type FFIDOAuthUserInfo as u, type FFIDOAuthUserInfoMemberRole as v, type FFIDOAuthUserInfoSubscription as w, FFIDOrganizationSwitcher as x, type FFIDSeatModel as y, type FFIDSessionResponse as z };
package/dist/index.cjs CHANGED
@@ -1,12 +1,12 @@
1
1
  'use strict';
2
2
 
3
- var chunk53CYDXU3_cjs = require('./chunk-53CYDXU3.cjs');
3
+ var chunk4QMSVP3G_cjs = require('./chunk-4QMSVP3G.cjs');
4
4
  var react = require('react');
5
5
  var jsxRuntime = require('react/jsx-runtime');
6
6
 
7
7
  function withFFIDAuth(Component, options = {}) {
8
8
  const WrappedComponent = (props) => {
9
- const { isLoading, isAuthenticated, login } = chunk53CYDXU3_cjs.useFFIDContext();
9
+ const { isLoading, isAuthenticated, login } = chunk4QMSVP3G_cjs.useFFIDContext();
10
10
  const hasRedirected = react.useRef(false);
11
11
  react.useEffect(() => {
12
12
  if (!isLoading && !isAuthenticated && options.redirectToLogin && !hasRedirected.current) {
@@ -31,78 +31,78 @@ function withFFIDAuth(Component, options = {}) {
31
31
 
32
32
  Object.defineProperty(exports, "DEFAULT_API_BASE_URL", {
33
33
  enumerable: true,
34
- get: function () { return chunk53CYDXU3_cjs.DEFAULT_API_BASE_URL; }
34
+ get: function () { return chunk4QMSVP3G_cjs.DEFAULT_API_BASE_URL; }
35
35
  });
36
36
  Object.defineProperty(exports, "FFIDAnnouncementBadge", {
37
37
  enumerable: true,
38
- get: function () { return chunk53CYDXU3_cjs.FFIDAnnouncementBadge; }
38
+ get: function () { return chunk4QMSVP3G_cjs.FFIDAnnouncementBadge; }
39
39
  });
40
40
  Object.defineProperty(exports, "FFIDAnnouncementList", {
41
41
  enumerable: true,
42
- get: function () { return chunk53CYDXU3_cjs.FFIDAnnouncementList; }
42
+ get: function () { return chunk4QMSVP3G_cjs.FFIDAnnouncementList; }
43
43
  });
44
44
  Object.defineProperty(exports, "FFIDLoginButton", {
45
45
  enumerable: true,
46
- get: function () { return chunk53CYDXU3_cjs.FFIDLoginButton; }
46
+ get: function () { return chunk4QMSVP3G_cjs.FFIDLoginButton; }
47
47
  });
48
48
  Object.defineProperty(exports, "FFIDOrganizationSwitcher", {
49
49
  enumerable: true,
50
- get: function () { return chunk53CYDXU3_cjs.FFIDOrganizationSwitcher; }
50
+ get: function () { return chunk4QMSVP3G_cjs.FFIDOrganizationSwitcher; }
51
51
  });
52
52
  Object.defineProperty(exports, "FFIDProvider", {
53
53
  enumerable: true,
54
- get: function () { return chunk53CYDXU3_cjs.FFIDProvider; }
54
+ get: function () { return chunk4QMSVP3G_cjs.FFIDProvider; }
55
55
  });
56
56
  Object.defineProperty(exports, "FFIDSubscriptionBadge", {
57
57
  enumerable: true,
58
- get: function () { return chunk53CYDXU3_cjs.FFIDSubscriptionBadge; }
58
+ get: function () { return chunk4QMSVP3G_cjs.FFIDSubscriptionBadge; }
59
59
  });
60
60
  Object.defineProperty(exports, "FFIDUserMenu", {
61
61
  enumerable: true,
62
- get: function () { return chunk53CYDXU3_cjs.FFIDUserMenu; }
62
+ get: function () { return chunk4QMSVP3G_cjs.FFIDUserMenu; }
63
63
  });
64
64
  Object.defineProperty(exports, "FFID_ANNOUNCEMENTS_ERROR_CODES", {
65
65
  enumerable: true,
66
- get: function () { return chunk53CYDXU3_cjs.FFID_ANNOUNCEMENTS_ERROR_CODES; }
66
+ get: function () { return chunk4QMSVP3G_cjs.FFID_ANNOUNCEMENTS_ERROR_CODES; }
67
67
  });
68
68
  Object.defineProperty(exports, "createFFIDAnnouncementsClient", {
69
69
  enumerable: true,
70
- get: function () { return chunk53CYDXU3_cjs.createFFIDAnnouncementsClient; }
70
+ get: function () { return chunk4QMSVP3G_cjs.createFFIDAnnouncementsClient; }
71
71
  });
72
72
  Object.defineProperty(exports, "createTokenStore", {
73
73
  enumerable: true,
74
- get: function () { return chunk53CYDXU3_cjs.createTokenStore; }
74
+ get: function () { return chunk4QMSVP3G_cjs.createTokenStore; }
75
75
  });
76
76
  Object.defineProperty(exports, "generateCodeChallenge", {
77
77
  enumerable: true,
78
- get: function () { return chunk53CYDXU3_cjs.generateCodeChallenge; }
78
+ get: function () { return chunk4QMSVP3G_cjs.generateCodeChallenge; }
79
79
  });
80
80
  Object.defineProperty(exports, "generateCodeVerifier", {
81
81
  enumerable: true,
82
- get: function () { return chunk53CYDXU3_cjs.generateCodeVerifier; }
82
+ get: function () { return chunk4QMSVP3G_cjs.generateCodeVerifier; }
83
83
  });
84
84
  Object.defineProperty(exports, "retrieveCodeVerifier", {
85
85
  enumerable: true,
86
- get: function () { return chunk53CYDXU3_cjs.retrieveCodeVerifier; }
86
+ get: function () { return chunk4QMSVP3G_cjs.retrieveCodeVerifier; }
87
87
  });
88
88
  Object.defineProperty(exports, "storeCodeVerifier", {
89
89
  enumerable: true,
90
- get: function () { return chunk53CYDXU3_cjs.storeCodeVerifier; }
90
+ get: function () { return chunk4QMSVP3G_cjs.storeCodeVerifier; }
91
91
  });
92
92
  Object.defineProperty(exports, "useFFID", {
93
93
  enumerable: true,
94
- get: function () { return chunk53CYDXU3_cjs.useFFID; }
94
+ get: function () { return chunk4QMSVP3G_cjs.useFFID; }
95
95
  });
96
96
  Object.defineProperty(exports, "useFFIDAnnouncements", {
97
97
  enumerable: true,
98
- get: function () { return chunk53CYDXU3_cjs.useFFIDAnnouncements; }
98
+ get: function () { return chunk4QMSVP3G_cjs.useFFIDAnnouncements; }
99
99
  });
100
100
  Object.defineProperty(exports, "useSubscription", {
101
101
  enumerable: true,
102
- get: function () { return chunk53CYDXU3_cjs.useSubscription; }
102
+ get: function () { return chunk4QMSVP3G_cjs.useSubscription; }
103
103
  });
104
104
  Object.defineProperty(exports, "withSubscription", {
105
105
  enumerable: true,
106
- get: function () { return chunk53CYDXU3_cjs.withSubscription; }
106
+ get: function () { return chunk4QMSVP3G_cjs.withSubscription; }
107
107
  });
108
108
  exports.withFFIDAuth = withFFIDAuth;
package/dist/index.d.cts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ReactNode, ComponentType, FC } from 'react';
3
- import { F as FFIDConfig, a as FFIDUser, b as FFIDOrganization, c as FFIDError, d as FFIDSubscriptionContextValue, e as FFIDAnnouncementsClientConfig, L as ListAnnouncementsOptions, f as FFIDAnnouncementsApiResponse, A as AnnouncementListResponse, g as FFIDAnnouncementsLogger } from './index-DKBSoAHg.cjs';
4
- export { h as Announcement, i as AnnouncementStatus, j as AnnouncementType, k as FFIDAnnouncementBadge, l as FFIDAnnouncementList, m as FFIDAnnouncementsError, n as FFIDAnnouncementsErrorCode, o as FFIDAnnouncementsServerResponse, p as FFIDApiResponse, q as FFIDContextValue, r as FFIDLogger, s as FFIDLoginButton, t as FFIDOAuthTokenResponse, u as FFIDOrganizationSwitcher, v as FFIDSessionResponse, w as FFIDSubscription, x as FFIDSubscriptionBadge, y as FFIDUserMenu, U as UseFFIDAnnouncementsOptions, z as UseFFIDAnnouncementsReturn, B as useFFIDAnnouncements } from './index-DKBSoAHg.cjs';
3
+ import { F as FFIDConfig, a as FFIDUser, b as FFIDOrganization, c as FFIDError, d as FFIDSubscriptionContextValue, e as FFIDAnnouncementsClientConfig, L as ListAnnouncementsOptions, f as FFIDAnnouncementsApiResponse, A as AnnouncementListResponse, g as FFIDAnnouncementsLogger } from './index-CyYHo3-R.cjs';
4
+ export { h as Announcement, i as AnnouncementStatus, j as AnnouncementType, k as FFIDAnnouncementBadge, l as FFIDAnnouncementList, m as FFIDAnnouncementsError, n as FFIDAnnouncementsErrorCode, o as FFIDAnnouncementsServerResponse, p as FFIDApiResponse, q as FFIDContextValue, r as FFIDLogger, s as FFIDLoginButton, t as FFIDOAuthTokenResponse, u as FFIDOAuthUserInfo, v as FFIDOAuthUserInfoMemberRole, w as FFIDOAuthUserInfoSubscription, x as FFIDOrganizationSwitcher, y as FFIDSeatModel, z as FFIDSessionResponse, B as FFIDSubscription, C as FFIDSubscriptionBadge, D as FFIDUserMenu, U as UseFFIDAnnouncementsOptions, E as UseFFIDAnnouncementsReturn, G as useFFIDAnnouncements } from './index-CyYHo3-R.cjs';
5
5
 
6
6
  /**
7
7
  * FFID SDK Shared Constants
@@ -127,6 +127,10 @@ interface UseFFIDReturn {
127
127
  switchOrganization: (organizationId: string) => void;
128
128
  /** Refresh session data */
129
129
  refresh: () => Promise<void>;
130
+ /** Get login URL with redirect parameter */
131
+ getLoginUrl: (redirectUrl?: string) => string;
132
+ /** Get signup URL with redirect parameter */
133
+ getSignupUrl: (redirectUrl?: string) => string;
130
134
  }
131
135
  /**
132
136
  * Hook to access FFID authentication and user data
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ReactNode, ComponentType, FC } from 'react';
3
- import { F as FFIDConfig, a as FFIDUser, b as FFIDOrganization, c as FFIDError, d as FFIDSubscriptionContextValue, e as FFIDAnnouncementsClientConfig, L as ListAnnouncementsOptions, f as FFIDAnnouncementsApiResponse, A as AnnouncementListResponse, g as FFIDAnnouncementsLogger } from './index-DKBSoAHg.js';
4
- export { h as Announcement, i as AnnouncementStatus, j as AnnouncementType, k as FFIDAnnouncementBadge, l as FFIDAnnouncementList, m as FFIDAnnouncementsError, n as FFIDAnnouncementsErrorCode, o as FFIDAnnouncementsServerResponse, p as FFIDApiResponse, q as FFIDContextValue, r as FFIDLogger, s as FFIDLoginButton, t as FFIDOAuthTokenResponse, u as FFIDOrganizationSwitcher, v as FFIDSessionResponse, w as FFIDSubscription, x as FFIDSubscriptionBadge, y as FFIDUserMenu, U as UseFFIDAnnouncementsOptions, z as UseFFIDAnnouncementsReturn, B as useFFIDAnnouncements } from './index-DKBSoAHg.js';
3
+ import { F as FFIDConfig, a as FFIDUser, b as FFIDOrganization, c as FFIDError, d as FFIDSubscriptionContextValue, e as FFIDAnnouncementsClientConfig, L as ListAnnouncementsOptions, f as FFIDAnnouncementsApiResponse, A as AnnouncementListResponse, g as FFIDAnnouncementsLogger } from './index-CyYHo3-R.js';
4
+ export { h as Announcement, i as AnnouncementStatus, j as AnnouncementType, k as FFIDAnnouncementBadge, l as FFIDAnnouncementList, m as FFIDAnnouncementsError, n as FFIDAnnouncementsErrorCode, o as FFIDAnnouncementsServerResponse, p as FFIDApiResponse, q as FFIDContextValue, r as FFIDLogger, s as FFIDLoginButton, t as FFIDOAuthTokenResponse, u as FFIDOAuthUserInfo, v as FFIDOAuthUserInfoMemberRole, w as FFIDOAuthUserInfoSubscription, x as FFIDOrganizationSwitcher, y as FFIDSeatModel, z as FFIDSessionResponse, B as FFIDSubscription, C as FFIDSubscriptionBadge, D as FFIDUserMenu, U as UseFFIDAnnouncementsOptions, E as UseFFIDAnnouncementsReturn, G as useFFIDAnnouncements } from './index-CyYHo3-R.js';
5
5
 
6
6
  /**
7
7
  * FFID SDK Shared Constants
@@ -127,6 +127,10 @@ interface UseFFIDReturn {
127
127
  switchOrganization: (organizationId: string) => void;
128
128
  /** Refresh session data */
129
129
  refresh: () => Promise<void>;
130
+ /** Get login URL with redirect parameter */
131
+ getLoginUrl: (redirectUrl?: string) => string;
132
+ /** Get signup URL with redirect parameter */
133
+ getSignupUrl: (redirectUrl?: string) => string;
130
134
  }
131
135
  /**
132
136
  * Hook to access FFID authentication and user data
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { useFFIDContext } from './chunk-GAAFEPIY.js';
2
- export { DEFAULT_API_BASE_URL, FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDProvider, FFIDSubscriptionBadge, FFIDUserMenu, FFID_ANNOUNCEMENTS_ERROR_CODES, createFFIDAnnouncementsClient, createTokenStore, generateCodeChallenge, generateCodeVerifier, retrieveCodeVerifier, storeCodeVerifier, useFFID, useFFIDAnnouncements, useSubscription, withSubscription } from './chunk-GAAFEPIY.js';
1
+ import { useFFIDContext } from './chunk-USE5Q2OF.js';
2
+ export { DEFAULT_API_BASE_URL, FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDProvider, FFIDSubscriptionBadge, FFIDUserMenu, FFID_ANNOUNCEMENTS_ERROR_CODES, createFFIDAnnouncementsClient, createTokenStore, generateCodeChallenge, generateCodeVerifier, retrieveCodeVerifier, storeCodeVerifier, useFFID, useFFIDAnnouncements, useSubscription, withSubscription } from './chunk-USE5Q2OF.js';
3
3
  import { useRef, useEffect } from 'react';
4
4
  import { jsx, Fragment } from 'react/jsx-runtime';
5
5
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feelflow/ffid-sdk",
3
- "version": "1.0.1",
3
+ "version": "1.1.1",
4
4
  "description": "FeelFlow ID Platform SDK for React/Next.js applications",
5
5
  "keywords": [
6
6
  "feelflow",