@feelflow/ffid-sdk 5.7.0 → 5.8.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.
@@ -130,7 +130,15 @@ function normalizeUserinfo(raw) {
130
130
  seatModel: raw.subscription.seat_model ?? null,
131
131
  memberRole: raw.subscription.member_role ?? null,
132
132
  organizationId: raw.subscription.organization_id ?? null,
133
- hasSeatAssignment: raw.subscription.has_seat_assignment ?? false
133
+ hasSeatAssignment: raw.subscription.has_seat_assignment ?? false,
134
+ // #3464: legacy FFID (< this issue) omits the field on the wire.
135
+ // The "unknown" axis is unused — no consumer differentiates "server
136
+ // didn't tell us" from "definitely not scheduled" because the
137
+ // companion `effectiveStatus` already returns `'active'` (not
138
+ // `'active_canceling'`) for legacy backends. Defaulting to `false`
139
+ // here keeps the downstream contract a plain boolean.
140
+ cancelAtPeriodEnd: raw.subscription.cancel_at_period_end ?? false,
141
+ currentPeriodEnd: raw.subscription.current_period_end ?? null
134
142
  } : void 0
135
143
  };
136
144
  }
@@ -147,8 +155,14 @@ function mapUserinfoSubscriptionToSession(userinfo, serviceCode) {
147
155
  planCode: subscription.planCode,
148
156
  planName: subscription.planCode,
149
157
  status: subscription.status,
150
- currentPeriodEnd: null,
158
+ // #3464: userinfo now carries `current_period_end` and
159
+ // `cancel_at_period_end`. Pre-#3464 FFID backends still omit them on
160
+ // the wire (`currentPeriodEnd === null` / `cancelAtPeriodEnd === false`
161
+ // after `normalizeUserinfo` defaults), so the session helper sees
162
+ // plain `'active'` — identical to pre-#3464 behaviour.
163
+ currentPeriodEnd: subscription.currentPeriodEnd,
151
164
  trialEnd: null,
165
+ cancelAtPeriodEnd: subscription.cancelAtPeriodEnd,
152
166
  seatModel: subscription.seatModel ?? void 0,
153
167
  memberRole: subscription.memberRole ?? void 0,
154
168
  organizationId: subscription.organizationId
@@ -842,7 +856,7 @@ function createProfileMethods(deps) {
842
856
  }
843
857
 
844
858
  // src/client/version-check.ts
845
- var SDK_VERSION = "5.7.0";
859
+ var SDK_VERSION = "5.8.0";
846
860
  var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
847
861
  var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
848
862
  function sdkHeaders() {
@@ -128,7 +128,15 @@ function normalizeUserinfo(raw) {
128
128
  seatModel: raw.subscription.seat_model ?? null,
129
129
  memberRole: raw.subscription.member_role ?? null,
130
130
  organizationId: raw.subscription.organization_id ?? null,
131
- hasSeatAssignment: raw.subscription.has_seat_assignment ?? false
131
+ hasSeatAssignment: raw.subscription.has_seat_assignment ?? false,
132
+ // #3464: legacy FFID (< this issue) omits the field on the wire.
133
+ // The "unknown" axis is unused — no consumer differentiates "server
134
+ // didn't tell us" from "definitely not scheduled" because the
135
+ // companion `effectiveStatus` already returns `'active'` (not
136
+ // `'active_canceling'`) for legacy backends. Defaulting to `false`
137
+ // here keeps the downstream contract a plain boolean.
138
+ cancelAtPeriodEnd: raw.subscription.cancel_at_period_end ?? false,
139
+ currentPeriodEnd: raw.subscription.current_period_end ?? null
132
140
  } : void 0
133
141
  };
134
142
  }
@@ -145,8 +153,14 @@ function mapUserinfoSubscriptionToSession(userinfo, serviceCode) {
145
153
  planCode: subscription.planCode,
146
154
  planName: subscription.planCode,
147
155
  status: subscription.status,
148
- currentPeriodEnd: null,
156
+ // #3464: userinfo now carries `current_period_end` and
157
+ // `cancel_at_period_end`. Pre-#3464 FFID backends still omit them on
158
+ // the wire (`currentPeriodEnd === null` / `cancelAtPeriodEnd === false`
159
+ // after `normalizeUserinfo` defaults), so the session helper sees
160
+ // plain `'active'` — identical to pre-#3464 behaviour.
161
+ currentPeriodEnd: subscription.currentPeriodEnd,
149
162
  trialEnd: null,
163
+ cancelAtPeriodEnd: subscription.cancelAtPeriodEnd,
150
164
  seatModel: subscription.seatModel ?? void 0,
151
165
  memberRole: subscription.memberRole ?? void 0,
152
166
  organizationId: subscription.organizationId
@@ -840,7 +854,7 @@ function createProfileMethods(deps) {
840
854
  }
841
855
 
842
856
  // src/client/version-check.ts
843
- var SDK_VERSION = "5.7.0";
857
+ var SDK_VERSION = "5.8.0";
844
858
  var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
845
859
  var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
846
860
  function sdkHeaders() {
@@ -1,34 +1,34 @@
1
1
  'use strict';
2
2
 
3
- var chunkFXIDKFXQ_cjs = require('../chunk-FXIDKFXQ.cjs');
3
+ var chunkMRHDPB7H_cjs = require('../chunk-MRHDPB7H.cjs');
4
4
 
5
5
 
6
6
 
7
7
  Object.defineProperty(exports, "FFIDAnnouncementBadge", {
8
8
  enumerable: true,
9
- get: function () { return chunkFXIDKFXQ_cjs.FFIDAnnouncementBadge; }
9
+ get: function () { return chunkMRHDPB7H_cjs.FFIDAnnouncementBadge; }
10
10
  });
11
11
  Object.defineProperty(exports, "FFIDAnnouncementList", {
12
12
  enumerable: true,
13
- get: function () { return chunkFXIDKFXQ_cjs.FFIDAnnouncementList; }
13
+ get: function () { return chunkMRHDPB7H_cjs.FFIDAnnouncementList; }
14
14
  });
15
15
  Object.defineProperty(exports, "FFIDInquiryForm", {
16
16
  enumerable: true,
17
- get: function () { return chunkFXIDKFXQ_cjs.FFIDInquiryForm; }
17
+ get: function () { return chunkMRHDPB7H_cjs.FFIDInquiryForm; }
18
18
  });
19
19
  Object.defineProperty(exports, "FFIDLoginButton", {
20
20
  enumerable: true,
21
- get: function () { return chunkFXIDKFXQ_cjs.FFIDLoginButton; }
21
+ get: function () { return chunkMRHDPB7H_cjs.FFIDLoginButton; }
22
22
  });
23
23
  Object.defineProperty(exports, "FFIDOrganizationSwitcher", {
24
24
  enumerable: true,
25
- get: function () { return chunkFXIDKFXQ_cjs.FFIDOrganizationSwitcher; }
25
+ get: function () { return chunkMRHDPB7H_cjs.FFIDOrganizationSwitcher; }
26
26
  });
27
27
  Object.defineProperty(exports, "FFIDSubscriptionBadge", {
28
28
  enumerable: true,
29
- get: function () { return chunkFXIDKFXQ_cjs.FFIDSubscriptionBadge; }
29
+ get: function () { return chunkMRHDPB7H_cjs.FFIDSubscriptionBadge; }
30
30
  });
31
31
  Object.defineProperty(exports, "FFIDUserMenu", {
32
32
  enumerable: true,
33
- get: function () { return chunkFXIDKFXQ_cjs.FFIDUserMenu; }
33
+ get: function () { return chunkMRHDPB7H_cjs.FFIDUserMenu; }
34
34
  });
@@ -1,3 +1,3 @@
1
- export { M as FFIDAnnouncementBadge, al as FFIDAnnouncementBadgeClassNames, am as FFIDAnnouncementBadgeProps, N as FFIDAnnouncementList, an as FFIDAnnouncementListClassNames, ao as FFIDAnnouncementListProps, V as FFIDInquiryForm, W as FFIDInquiryFormCategoryItem, X as FFIDInquiryFormClassNames, Y as FFIDInquiryFormLegalLayout, Z as FFIDInquiryFormOrganization, _ as FFIDInquiryFormPlaceholderContext, $ as FFIDInquiryFormPrefill, a0 as FFIDInquiryFormProps, a1 as FFIDInquiryFormSubmitData, a2 as FFIDInquiryFormSubmitResult, a4 as FFIDLoginButton, ap as FFIDLoginButtonProps, a7 as FFIDOrganizationSwitcher, aq as FFIDOrganizationSwitcherClassNames, ar as FFIDOrganizationSwitcherProps, ac as FFIDSubscriptionBadge, as as FFIDSubscriptionBadgeClassNames, at as FFIDSubscriptionBadgeProps, ae as FFIDUserMenu, au as FFIDUserMenuClassNames, av as FFIDUserMenuProps } from '../index-EFp_ZgK9.cjs';
1
+ export { M as FFIDAnnouncementBadge, al as FFIDAnnouncementBadgeClassNames, am as FFIDAnnouncementBadgeProps, N as FFIDAnnouncementList, an as FFIDAnnouncementListClassNames, ao as FFIDAnnouncementListProps, V as FFIDInquiryForm, W as FFIDInquiryFormCategoryItem, X as FFIDInquiryFormClassNames, Y as FFIDInquiryFormLegalLayout, Z as FFIDInquiryFormOrganization, _ as FFIDInquiryFormPlaceholderContext, $ as FFIDInquiryFormPrefill, a0 as FFIDInquiryFormProps, a1 as FFIDInquiryFormSubmitData, a2 as FFIDInquiryFormSubmitResult, a4 as FFIDLoginButton, ap as FFIDLoginButtonProps, a7 as FFIDOrganizationSwitcher, aq as FFIDOrganizationSwitcherClassNames, ar as FFIDOrganizationSwitcherProps, ac as FFIDSubscriptionBadge, as as FFIDSubscriptionBadgeClassNames, at as FFIDSubscriptionBadgeProps, ae as FFIDUserMenu, au as FFIDUserMenuClassNames, av as FFIDUserMenuProps } from '../index-DeegKrLW.cjs';
2
2
  import 'react/jsx-runtime';
3
3
  import 'react';
@@ -1,3 +1,3 @@
1
- export { M as FFIDAnnouncementBadge, al as FFIDAnnouncementBadgeClassNames, am as FFIDAnnouncementBadgeProps, N as FFIDAnnouncementList, an as FFIDAnnouncementListClassNames, ao as FFIDAnnouncementListProps, V as FFIDInquiryForm, W as FFIDInquiryFormCategoryItem, X as FFIDInquiryFormClassNames, Y as FFIDInquiryFormLegalLayout, Z as FFIDInquiryFormOrganization, _ as FFIDInquiryFormPlaceholderContext, $ as FFIDInquiryFormPrefill, a0 as FFIDInquiryFormProps, a1 as FFIDInquiryFormSubmitData, a2 as FFIDInquiryFormSubmitResult, a4 as FFIDLoginButton, ap as FFIDLoginButtonProps, a7 as FFIDOrganizationSwitcher, aq as FFIDOrganizationSwitcherClassNames, ar as FFIDOrganizationSwitcherProps, ac as FFIDSubscriptionBadge, as as FFIDSubscriptionBadgeClassNames, at as FFIDSubscriptionBadgeProps, ae as FFIDUserMenu, au as FFIDUserMenuClassNames, av as FFIDUserMenuProps } from '../index-EFp_ZgK9.js';
1
+ export { M as FFIDAnnouncementBadge, al as FFIDAnnouncementBadgeClassNames, am as FFIDAnnouncementBadgeProps, N as FFIDAnnouncementList, an as FFIDAnnouncementListClassNames, ao as FFIDAnnouncementListProps, V as FFIDInquiryForm, W as FFIDInquiryFormCategoryItem, X as FFIDInquiryFormClassNames, Y as FFIDInquiryFormLegalLayout, Z as FFIDInquiryFormOrganization, _ as FFIDInquiryFormPlaceholderContext, $ as FFIDInquiryFormPrefill, a0 as FFIDInquiryFormProps, a1 as FFIDInquiryFormSubmitData, a2 as FFIDInquiryFormSubmitResult, a4 as FFIDLoginButton, ap as FFIDLoginButtonProps, a7 as FFIDOrganizationSwitcher, aq as FFIDOrganizationSwitcherClassNames, ar as FFIDOrganizationSwitcherProps, ac as FFIDSubscriptionBadge, as as FFIDSubscriptionBadgeClassNames, at as FFIDSubscriptionBadgeProps, ae as FFIDUserMenu, au as FFIDUserMenuClassNames, av as FFIDUserMenuProps } from '../index-DeegKrLW.js';
2
2
  import 'react/jsx-runtime';
3
3
  import 'react';
@@ -1 +1 @@
1
- export { FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDInquiryForm, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDSubscriptionBadge, FFIDUserMenu } from '../chunk-YAJN36SD.js';
1
+ export { FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDInquiryForm, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDSubscriptionBadge, FFIDUserMenu } from '../chunk-Z6COUAR6.js';
@@ -1,4 +1,4 @@
1
- import { E as EffectiveSubscriptionStatus } from './types-s3y11uT6.cjs';
1
+ import { E as EffectiveSubscriptionStatus } from './types-BYdtyO_2.cjs';
2
2
 
3
3
  /**
4
4
  * Inquiry types exposed by the FFID SDK.
@@ -758,6 +758,25 @@ interface FFIDOAuthUserInfoSubscription {
758
758
  memberRole: FFIDOAuthUserInfoMemberRole | null;
759
759
  organizationId: string | null;
760
760
  hasSeatAssignment: boolean | null;
761
+ /**
762
+ * Stripe `cancel_at_period_end` (#3464, SDK 5.8.0). `true` while the
763
+ * subscription is scheduled to terminate at `currentPeriodEnd`. The
764
+ * `normalizeUserinfo` boundary defaults the wire field to `false` when
765
+ * the server omits it (older FFID pre-#3464), so consumers always see
766
+ * a plain boolean and never branch on an "unknown" sentinel — the
767
+ * companion `effectiveStatus` already returns `'active'` (not
768
+ * `'active_canceling'`) for legacy backends since `cancel_at_period_end`
769
+ * is implicitly false there.
770
+ */
771
+ cancelAtPeriodEnd: boolean;
772
+ /**
773
+ * ISO timestamp of the current billing period end (#3464, SDK 5.8.0).
774
+ * `null` for free / seatless plans, or when serving an older FFID
775
+ * (pre-#3464) that does not populate the field. Exposed so callers
776
+ * can display the cancellation effective date without an `/ext/check`
777
+ * round-trip.
778
+ */
779
+ currentPeriodEnd: string | null;
761
780
  }
762
781
  /**
763
782
  * Unified user-identity shape returned by `getSession()` (→ `/oauth/userinfo`)
@@ -1,4 +1,4 @@
1
- import { E as EffectiveSubscriptionStatus } from './types-s3y11uT6.js';
1
+ import { E as EffectiveSubscriptionStatus } from './types-BYdtyO_2.js';
2
2
 
3
3
  /**
4
4
  * Inquiry types exposed by the FFID SDK.
@@ -758,6 +758,25 @@ interface FFIDOAuthUserInfoSubscription {
758
758
  memberRole: FFIDOAuthUserInfoMemberRole | null;
759
759
  organizationId: string | null;
760
760
  hasSeatAssignment: boolean | null;
761
+ /**
762
+ * Stripe `cancel_at_period_end` (#3464, SDK 5.8.0). `true` while the
763
+ * subscription is scheduled to terminate at `currentPeriodEnd`. The
764
+ * `normalizeUserinfo` boundary defaults the wire field to `false` when
765
+ * the server omits it (older FFID pre-#3464), so consumers always see
766
+ * a plain boolean and never branch on an "unknown" sentinel — the
767
+ * companion `effectiveStatus` already returns `'active'` (not
768
+ * `'active_canceling'`) for legacy backends since `cancel_at_period_end`
769
+ * is implicitly false there.
770
+ */
771
+ cancelAtPeriodEnd: boolean;
772
+ /**
773
+ * ISO timestamp of the current billing period end (#3464, SDK 5.8.0).
774
+ * `null` for free / seatless plans, or when serving an older FFID
775
+ * (pre-#3464) that does not populate the field. Exposed so callers
776
+ * can display the cancellation effective date without an `/ext/check`
777
+ * round-trip.
778
+ */
779
+ currentPeriodEnd: string | null;
761
780
  }
762
781
  /**
763
782
  * Unified user-identity shape returned by `getSession()` (→ `/oauth/userinfo`)
@@ -57,22 +57,18 @@ interface FFIDCacheConfig {
57
57
  * This is the FFID-internal runtime-expired case (see
58
58
  * `getEffectiveSubscriptionStatus` on the server). Deny access.
59
59
  *
60
- * **SDK / server asymmetry `active_canceling` is session-only (#3460).**
61
- * The backend `EffectiveSubscriptionStatus` in
62
- * `src/lib/common/subscription-helpers.ts` does NOT currently include
63
- * `active_canceling`; cancellation-scheduled subscriptions surface as plain
64
- * `'active'` on `/api/v1/subscriptions/ext/check` and `/oauth/userinfo`, and
65
- * consumers that branch on the server verdict will not see the new literal.
66
- * The SDK enriches `computeEffectiveStatusFromSession` with
67
- * `cancelAtPeriodEnd` (carried on `FFIDSubscription` for FFID backends from
68
- * #3460 onward older backends omit the field and the helper defaults to
69
- * `false`, preserving the pre-#3460 `'active'` verdict) so `useSubscription`
70
- * can surface a "cancellation scheduled" badge without waiting for an
71
- * `/ext/check` round-trip. The asymmetry is safe because the new literal is
72
- * access-granting (no security regression on the userinfo path); when the
73
- * server's `EffectiveSubscriptionStatus` is later extended to emit
74
- * `active_canceling`, this docstring MUST be updated to drop the
75
- * session-only note.
60
+ * **`active_canceling` is mirrored on the server (#3464, SDK 5.8.0).**
61
+ * Earlier (#3460, SDK 5.7.0) the literal was a session-only enrichment;
62
+ * since FFID #3464 the backend `EffectiveSubscriptionStatus` and both
63
+ * `/api/v1/subscriptions/ext/check` + `/oauth/userinfo` emit
64
+ * `active_canceling` directly when `cancel_at_period_end=true` and the
65
+ * period is still live. SDK consumers receive the same literal whether
66
+ * they read it from the session-derived `computeEffectiveStatusFromSession`
67
+ * or the server-derived `effectiveStatus` on the userinfo / ext-check
68
+ * paths. Pre-#3464 FFID backends omit `cancelAtPeriodEnd` on the wire; the
69
+ * SDK `normalizeUserinfo` boundary defaults it to `false`, so downstream
70
+ * consumers see a plain boolean and `computeEffectiveStatus` returns the
71
+ * original `'active'` verdict for legacy deployments.
76
72
  *
77
73
  * @see /api/v1/subscriptions/ext/check
78
74
  * @see docs/03-implementation/EXPIRED_CONTRACT_HANDLING.md
@@ -285,6 +281,25 @@ interface FFIDOAuthUserInfoSubscription {
285
281
  memberRole: FFIDOAuthUserInfoMemberRole | null;
286
282
  organizationId: string | null;
287
283
  hasSeatAssignment: boolean | null;
284
+ /**
285
+ * Stripe `cancel_at_period_end` (#3464, SDK 5.8.0). `true` while the
286
+ * subscription is scheduled to terminate at `currentPeriodEnd`. The
287
+ * `normalizeUserinfo` boundary defaults the wire field to `false` when
288
+ * the server omits it (older FFID pre-#3464), so consumers always see
289
+ * a plain boolean and never branch on an "unknown" sentinel — the
290
+ * companion `effectiveStatus` already returns `'active'` (not
291
+ * `'active_canceling'`) for legacy backends since `cancel_at_period_end`
292
+ * is implicitly false there.
293
+ */
294
+ cancelAtPeriodEnd: boolean;
295
+ /**
296
+ * ISO timestamp of the current billing period end (#3464, SDK 5.8.0).
297
+ * `null` for free / seatless plans, or when serving an older FFID
298
+ * (pre-#3464) that does not populate the field. Exposed so callers
299
+ * can display the cancellation effective date without an `/ext/check`
300
+ * round-trip.
301
+ */
302
+ currentPeriodEnd: string | null;
288
303
  }
289
304
  /**
290
305
  * Unified user-identity shape returned by `getSession()` (→ `/oauth/userinfo`)
@@ -57,22 +57,18 @@ interface FFIDCacheConfig {
57
57
  * This is the FFID-internal runtime-expired case (see
58
58
  * `getEffectiveSubscriptionStatus` on the server). Deny access.
59
59
  *
60
- * **SDK / server asymmetry `active_canceling` is session-only (#3460).**
61
- * The backend `EffectiveSubscriptionStatus` in
62
- * `src/lib/common/subscription-helpers.ts` does NOT currently include
63
- * `active_canceling`; cancellation-scheduled subscriptions surface as plain
64
- * `'active'` on `/api/v1/subscriptions/ext/check` and `/oauth/userinfo`, and
65
- * consumers that branch on the server verdict will not see the new literal.
66
- * The SDK enriches `computeEffectiveStatusFromSession` with
67
- * `cancelAtPeriodEnd` (carried on `FFIDSubscription` for FFID backends from
68
- * #3460 onward older backends omit the field and the helper defaults to
69
- * `false`, preserving the pre-#3460 `'active'` verdict) so `useSubscription`
70
- * can surface a "cancellation scheduled" badge without waiting for an
71
- * `/ext/check` round-trip. The asymmetry is safe because the new literal is
72
- * access-granting (no security regression on the userinfo path); when the
73
- * server's `EffectiveSubscriptionStatus` is later extended to emit
74
- * `active_canceling`, this docstring MUST be updated to drop the
75
- * session-only note.
60
+ * **`active_canceling` is mirrored on the server (#3464, SDK 5.8.0).**
61
+ * Earlier (#3460, SDK 5.7.0) the literal was a session-only enrichment;
62
+ * since FFID #3464 the backend `EffectiveSubscriptionStatus` and both
63
+ * `/api/v1/subscriptions/ext/check` + `/oauth/userinfo` emit
64
+ * `active_canceling` directly when `cancel_at_period_end=true` and the
65
+ * period is still live. SDK consumers receive the same literal whether
66
+ * they read it from the session-derived `computeEffectiveStatusFromSession`
67
+ * or the server-derived `effectiveStatus` on the userinfo / ext-check
68
+ * paths. Pre-#3464 FFID backends omit `cancelAtPeriodEnd` on the wire; the
69
+ * SDK `normalizeUserinfo` boundary defaults it to `false`, so downstream
70
+ * consumers see a plain boolean and `computeEffectiveStatus` returns the
71
+ * original `'active'` verdict for legacy deployments.
76
72
  *
77
73
  * @see /api/v1/subscriptions/ext/check
78
74
  * @see docs/03-implementation/EXPIRED_CONTRACT_HANDLING.md
@@ -285,6 +281,25 @@ interface FFIDOAuthUserInfoSubscription {
285
281
  memberRole: FFIDOAuthUserInfoMemberRole | null;
286
282
  organizationId: string | null;
287
283
  hasSeatAssignment: boolean | null;
284
+ /**
285
+ * Stripe `cancel_at_period_end` (#3464, SDK 5.8.0). `true` while the
286
+ * subscription is scheduled to terminate at `currentPeriodEnd`. The
287
+ * `normalizeUserinfo` boundary defaults the wire field to `false` when
288
+ * the server omits it (older FFID pre-#3464), so consumers always see
289
+ * a plain boolean and never branch on an "unknown" sentinel — the
290
+ * companion `effectiveStatus` already returns `'active'` (not
291
+ * `'active_canceling'`) for legacy backends since `cancel_at_period_end`
292
+ * is implicitly false there.
293
+ */
294
+ cancelAtPeriodEnd: boolean;
295
+ /**
296
+ * ISO timestamp of the current billing period end (#3464, SDK 5.8.0).
297
+ * `null` for free / seatless plans, or when serving an older FFID
298
+ * (pre-#3464) that does not populate the field. Exposed so callers
299
+ * can display the cancellation effective date without an `/ext/check`
300
+ * round-trip.
301
+ */
302
+ currentPeriodEnd: string | null;
288
303
  }
289
304
  /**
290
305
  * Unified user-identity shape returned by `getSession()` (→ `/oauth/userinfo`)
package/dist/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkFXIDKFXQ_cjs = require('./chunk-FXIDKFXQ.cjs');
3
+ var chunkMRHDPB7H_cjs = require('./chunk-MRHDPB7H.cjs');
4
4
  var chunkQ5SZVLNB_cjs = require('./chunk-Q5SZVLNB.cjs');
5
5
  var react = require('react');
6
6
  var jsxRuntime = require('react/jsx-runtime');
@@ -54,8 +54,8 @@ function defaultRedirect(url) {
54
54
  }
55
55
  function useRequireActiveSubscription(options) {
56
56
  const { redirectTo, allowGrace = true, onRedirect } = options;
57
- const { isLoading, error } = chunkFXIDKFXQ_cjs.useFFIDContext();
58
- const { effectiveStatus, isBlocked, isGrace } = chunkFXIDKFXQ_cjs.useSubscription();
57
+ const { isLoading, error } = chunkMRHDPB7H_cjs.useFFIDContext();
58
+ const { effectiveStatus, isBlocked, isGrace } = chunkMRHDPB7H_cjs.useSubscription();
59
59
  const hasFetchError = error !== null && effectiveStatus === null;
60
60
  const shouldRedirect = !isLoading && !hasFetchError && (isBlocked || !allowGrace && isGrace || effectiveStatus === null);
61
61
  react.useEffect(() => {
@@ -76,7 +76,7 @@ function useRequireActiveSubscription(options) {
76
76
  }
77
77
  function withFFIDAuth(Component, options = {}) {
78
78
  const WrappedComponent = (props) => {
79
- const { isLoading, isAuthenticated, login } = chunkFXIDKFXQ_cjs.useFFIDContext();
79
+ const { isLoading, isAuthenticated, login } = chunkMRHDPB7H_cjs.useFFIDContext();
80
80
  const hasRedirected = react.useRef(false);
81
81
  react.useEffect(() => {
82
82
  if (!isLoading && !isAuthenticated && options.redirectToLogin && !hasRedirected.current) {
@@ -105,131 +105,131 @@ var FFID_NEWSLETTER_DISPATCH_MAX_RECIPIENTS = 1e3;
105
105
 
106
106
  Object.defineProperty(exports, "ACCESS_GRANTING_EFFECTIVE_STATUSES", {
107
107
  enumerable: true,
108
- get: function () { return chunkFXIDKFXQ_cjs.ACCESS_GRANTING_EFFECTIVE_STATUSES; }
108
+ get: function () { return chunkMRHDPB7H_cjs.ACCESS_GRANTING_EFFECTIVE_STATUSES; }
109
109
  });
110
110
  Object.defineProperty(exports, "BLOCKING_EFFECTIVE_STATUSES", {
111
111
  enumerable: true,
112
- get: function () { return chunkFXIDKFXQ_cjs.BLOCKING_EFFECTIVE_STATUSES; }
112
+ get: function () { return chunkMRHDPB7H_cjs.BLOCKING_EFFECTIVE_STATUSES; }
113
113
  });
114
114
  Object.defineProperty(exports, "DEFAULT_API_BASE_URL", {
115
115
  enumerable: true,
116
- get: function () { return chunkFXIDKFXQ_cjs.DEFAULT_API_BASE_URL; }
116
+ get: function () { return chunkMRHDPB7H_cjs.DEFAULT_API_BASE_URL; }
117
117
  });
118
118
  Object.defineProperty(exports, "DEFAULT_OAUTH_SCOPES", {
119
119
  enumerable: true,
120
- get: function () { return chunkFXIDKFXQ_cjs.DEFAULT_OAUTH_SCOPES; }
120
+ get: function () { return chunkMRHDPB7H_cjs.DEFAULT_OAUTH_SCOPES; }
121
121
  });
122
122
  Object.defineProperty(exports, "FFIDAnnouncementBadge", {
123
123
  enumerable: true,
124
- get: function () { return chunkFXIDKFXQ_cjs.FFIDAnnouncementBadge; }
124
+ get: function () { return chunkMRHDPB7H_cjs.FFIDAnnouncementBadge; }
125
125
  });
126
126
  Object.defineProperty(exports, "FFIDAnnouncementList", {
127
127
  enumerable: true,
128
- get: function () { return chunkFXIDKFXQ_cjs.FFIDAnnouncementList; }
128
+ get: function () { return chunkMRHDPB7H_cjs.FFIDAnnouncementList; }
129
129
  });
130
130
  Object.defineProperty(exports, "FFIDInquiryForm", {
131
131
  enumerable: true,
132
- get: function () { return chunkFXIDKFXQ_cjs.FFIDInquiryForm; }
132
+ get: function () { return chunkMRHDPB7H_cjs.FFIDInquiryForm; }
133
133
  });
134
134
  Object.defineProperty(exports, "FFIDLoginButton", {
135
135
  enumerable: true,
136
- get: function () { return chunkFXIDKFXQ_cjs.FFIDLoginButton; }
136
+ get: function () { return chunkMRHDPB7H_cjs.FFIDLoginButton; }
137
137
  });
138
138
  Object.defineProperty(exports, "FFIDOrganizationSwitcher", {
139
139
  enumerable: true,
140
- get: function () { return chunkFXIDKFXQ_cjs.FFIDOrganizationSwitcher; }
140
+ get: function () { return chunkMRHDPB7H_cjs.FFIDOrganizationSwitcher; }
141
141
  });
142
142
  Object.defineProperty(exports, "FFIDProvider", {
143
143
  enumerable: true,
144
- get: function () { return chunkFXIDKFXQ_cjs.FFIDProvider; }
144
+ get: function () { return chunkMRHDPB7H_cjs.FFIDProvider; }
145
145
  });
146
146
  Object.defineProperty(exports, "FFIDSDKError", {
147
147
  enumerable: true,
148
- get: function () { return chunkFXIDKFXQ_cjs.FFIDSDKError; }
148
+ get: function () { return chunkMRHDPB7H_cjs.FFIDSDKError; }
149
149
  });
150
150
  Object.defineProperty(exports, "FFIDSubscriptionBadge", {
151
151
  enumerable: true,
152
- get: function () { return chunkFXIDKFXQ_cjs.FFIDSubscriptionBadge; }
152
+ get: function () { return chunkMRHDPB7H_cjs.FFIDSubscriptionBadge; }
153
153
  });
154
154
  Object.defineProperty(exports, "FFIDUserMenu", {
155
155
  enumerable: true,
156
- get: function () { return chunkFXIDKFXQ_cjs.FFIDUserMenu; }
156
+ get: function () { return chunkMRHDPB7H_cjs.FFIDUserMenu; }
157
157
  });
158
158
  Object.defineProperty(exports, "FFID_ANNOUNCEMENTS_ERROR_CODES", {
159
159
  enumerable: true,
160
- get: function () { return chunkFXIDKFXQ_cjs.FFID_ANNOUNCEMENTS_ERROR_CODES; }
160
+ get: function () { return chunkMRHDPB7H_cjs.FFID_ANNOUNCEMENTS_ERROR_CODES; }
161
161
  });
162
162
  Object.defineProperty(exports, "FFID_INQUIRY_CATEGORIES", {
163
163
  enumerable: true,
164
- get: function () { return chunkFXIDKFXQ_cjs.FFID_INQUIRY_CATEGORIES; }
164
+ get: function () { return chunkMRHDPB7H_cjs.FFID_INQUIRY_CATEGORIES; }
165
165
  });
166
166
  Object.defineProperty(exports, "FFID_INQUIRY_CATEGORIES_SITE_2026", {
167
167
  enumerable: true,
168
- get: function () { return chunkFXIDKFXQ_cjs.FFID_INQUIRY_CATEGORIES_SITE_2026; }
168
+ get: function () { return chunkMRHDPB7H_cjs.FFID_INQUIRY_CATEGORIES_SITE_2026; }
169
169
  });
170
170
  Object.defineProperty(exports, "computeEffectiveStatusFromSession", {
171
171
  enumerable: true,
172
- get: function () { return chunkFXIDKFXQ_cjs.computeEffectiveStatusFromSession; }
172
+ get: function () { return chunkMRHDPB7H_cjs.computeEffectiveStatusFromSession; }
173
173
  });
174
174
  Object.defineProperty(exports, "createFFIDAnnouncementsClient", {
175
175
  enumerable: true,
176
- get: function () { return chunkFXIDKFXQ_cjs.createFFIDAnnouncementsClient; }
176
+ get: function () { return chunkMRHDPB7H_cjs.createFFIDAnnouncementsClient; }
177
177
  });
178
178
  Object.defineProperty(exports, "createFFIDClient", {
179
179
  enumerable: true,
180
- get: function () { return chunkFXIDKFXQ_cjs.createFFIDClient; }
180
+ get: function () { return chunkMRHDPB7H_cjs.createFFIDClient; }
181
181
  });
182
182
  Object.defineProperty(exports, "createTokenStore", {
183
183
  enumerable: true,
184
- get: function () { return chunkFXIDKFXQ_cjs.createTokenStore; }
184
+ get: function () { return chunkMRHDPB7H_cjs.createTokenStore; }
185
185
  });
186
186
  Object.defineProperty(exports, "generateCodeChallenge", {
187
187
  enumerable: true,
188
- get: function () { return chunkFXIDKFXQ_cjs.generateCodeChallenge; }
188
+ get: function () { return chunkMRHDPB7H_cjs.generateCodeChallenge; }
189
189
  });
190
190
  Object.defineProperty(exports, "generateCodeVerifier", {
191
191
  enumerable: true,
192
- get: function () { return chunkFXIDKFXQ_cjs.generateCodeVerifier; }
192
+ get: function () { return chunkMRHDPB7H_cjs.generateCodeVerifier; }
193
193
  });
194
194
  Object.defineProperty(exports, "hasAccessFromUserinfo", {
195
195
  enumerable: true,
196
- get: function () { return chunkFXIDKFXQ_cjs.hasAccessFromUserinfo; }
196
+ get: function () { return chunkMRHDPB7H_cjs.hasAccessFromUserinfo; }
197
197
  });
198
198
  Object.defineProperty(exports, "isBlockedFromUserinfo", {
199
199
  enumerable: true,
200
- get: function () { return chunkFXIDKFXQ_cjs.isBlockedFromUserinfo; }
200
+ get: function () { return chunkMRHDPB7H_cjs.isBlockedFromUserinfo; }
201
201
  });
202
202
  Object.defineProperty(exports, "isFFIDInquiryCategorySite2026", {
203
203
  enumerable: true,
204
- get: function () { return chunkFXIDKFXQ_cjs.isFFIDInquiryCategorySite2026; }
204
+ get: function () { return chunkMRHDPB7H_cjs.isFFIDInquiryCategorySite2026; }
205
205
  });
206
206
  Object.defineProperty(exports, "normalizeRedirectUri", {
207
207
  enumerable: true,
208
- get: function () { return chunkFXIDKFXQ_cjs.normalizeRedirectUri; }
208
+ get: function () { return chunkMRHDPB7H_cjs.normalizeRedirectUri; }
209
209
  });
210
210
  Object.defineProperty(exports, "retrieveCodeVerifier", {
211
211
  enumerable: true,
212
- get: function () { return chunkFXIDKFXQ_cjs.retrieveCodeVerifier; }
212
+ get: function () { return chunkMRHDPB7H_cjs.retrieveCodeVerifier; }
213
213
  });
214
214
  Object.defineProperty(exports, "storeCodeVerifier", {
215
215
  enumerable: true,
216
- get: function () { return chunkFXIDKFXQ_cjs.storeCodeVerifier; }
216
+ get: function () { return chunkMRHDPB7H_cjs.storeCodeVerifier; }
217
217
  });
218
218
  Object.defineProperty(exports, "useFFID", {
219
219
  enumerable: true,
220
- get: function () { return chunkFXIDKFXQ_cjs.useFFID; }
220
+ get: function () { return chunkMRHDPB7H_cjs.useFFID; }
221
221
  });
222
222
  Object.defineProperty(exports, "useFFIDAnnouncements", {
223
223
  enumerable: true,
224
- get: function () { return chunkFXIDKFXQ_cjs.useFFIDAnnouncements; }
224
+ get: function () { return chunkMRHDPB7H_cjs.useFFIDAnnouncements; }
225
225
  });
226
226
  Object.defineProperty(exports, "useSubscription", {
227
227
  enumerable: true,
228
- get: function () { return chunkFXIDKFXQ_cjs.useSubscription; }
228
+ get: function () { return chunkMRHDPB7H_cjs.useSubscription; }
229
229
  });
230
230
  Object.defineProperty(exports, "withSubscription", {
231
231
  enumerable: true,
232
- get: function () { return chunkFXIDKFXQ_cjs.withSubscription; }
232
+ get: function () { return chunkMRHDPB7H_cjs.withSubscription; }
233
233
  });
234
234
  Object.defineProperty(exports, "ALL_DENIED_EXCEPT_NECESSARY", {
235
235
  enumerable: true,
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { F as FFIDSubscriptionStatus, a as FFIDConfig, b as FFIDApiResponse, c as FFIDSessionResponse, d as FFIDRedirectResult, e as FFIDError, f as FFIDSubscriptionCheckResponse, g as FFIDCheckServiceAccessParams, h as FFIDServiceAccessDecision, i as FFIDProfileCallOptions, j as FFIDUserProfile, k as FFIDUpdateUserProfileRequest, l as FFIDAnalyticsConfig, m as FFIDCreateCheckoutParams, n as FFIDCheckoutSessionResponse, o as FFIDCreatePortalParams, p as FFIDPortalSessionResponse, q as FFIDVerifyAccessTokenOptions, r as FFIDOAuthUserInfo, s as FFIDInquiryCreateParams, t as FFIDInquiryCreateResponse, u as FFIDAuthMode, v as FFIDLogger, w as FFIDCacheAdapter, x as FFIDUser, y as FFIDOrganization, z as FFIDSubscription, A as FFIDSubscriptionContextValue, E as EffectiveSubscriptionStatus, B as FFIDOAuthUserInfoSubscription, C as FFIDAnnouncementsClientConfig, L as ListAnnouncementsOptions, D as FFIDAnnouncementsApiResponse, G as AnnouncementListResponse, H as FFIDAnnouncementsLogger } from './index-EFp_ZgK9.cjs';
2
- export { I as Announcement, J as AnnouncementStatus, K as AnnouncementType, M as FFIDAnnouncementBadge, N as FFIDAnnouncementList, O as FFIDAnnouncementsError, P as FFIDAnnouncementsErrorCode, Q as FFIDAnnouncementsServerResponse, R as FFIDCacheConfig, S as FFIDContextValue, T as FFIDInquiryCategory, U as FFIDInquiryCategorySite2026, V as FFIDInquiryForm, W as FFIDInquiryFormCategoryItem, X as FFIDInquiryFormClassNames, Y as FFIDInquiryFormLegalLayout, Z as FFIDInquiryFormOrganization, _ as FFIDInquiryFormPlaceholderContext, $ as FFIDInquiryFormPrefill, a0 as FFIDInquiryFormProps, a1 as FFIDInquiryFormSubmitData, a2 as FFIDInquiryFormSubmitResult, a3 as FFIDJwtClaims, a4 as FFIDLoginButton, a5 as FFIDOAuthTokenResponse, a6 as FFIDOAuthUserInfoMemberRole, a7 as FFIDOrganizationSwitcher, a8 as FFIDRedirectErrorCode, a9 as FFIDSeatModel, aa as FFIDServiceAccessDenialReason, ab as FFIDServiceAccessFailPolicy, ac as FFIDSubscriptionBadge, ad as FFIDTokenIntrospectionResponse, ae as FFIDUserMenu, af as FFID_INQUIRY_CATEGORIES, ag as FFID_INQUIRY_CATEGORIES_SITE_2026, ah as UseFFIDAnnouncementsOptions, ai as UseFFIDAnnouncementsReturn, aj as isFFIDInquiryCategorySite2026, ak as useFFIDAnnouncements } from './index-EFp_ZgK9.cjs';
1
+ import { F as FFIDSubscriptionStatus, a as FFIDConfig, b as FFIDApiResponse, c as FFIDSessionResponse, d as FFIDRedirectResult, e as FFIDError, f as FFIDSubscriptionCheckResponse, g as FFIDCheckServiceAccessParams, h as FFIDServiceAccessDecision, i as FFIDProfileCallOptions, j as FFIDUserProfile, k as FFIDUpdateUserProfileRequest, l as FFIDAnalyticsConfig, m as FFIDCreateCheckoutParams, n as FFIDCheckoutSessionResponse, o as FFIDCreatePortalParams, p as FFIDPortalSessionResponse, q as FFIDVerifyAccessTokenOptions, r as FFIDOAuthUserInfo, s as FFIDInquiryCreateParams, t as FFIDInquiryCreateResponse, u as FFIDAuthMode, v as FFIDLogger, w as FFIDCacheAdapter, x as FFIDUser, y as FFIDOrganization, z as FFIDSubscription, A as FFIDSubscriptionContextValue, E as EffectiveSubscriptionStatus, B as FFIDOAuthUserInfoSubscription, C as FFIDAnnouncementsClientConfig, L as ListAnnouncementsOptions, D as FFIDAnnouncementsApiResponse, G as AnnouncementListResponse, H as FFIDAnnouncementsLogger } from './index-DeegKrLW.cjs';
2
+ export { I as Announcement, J as AnnouncementStatus, K as AnnouncementType, M as FFIDAnnouncementBadge, N as FFIDAnnouncementList, O as FFIDAnnouncementsError, P as FFIDAnnouncementsErrorCode, Q as FFIDAnnouncementsServerResponse, R as FFIDCacheConfig, S as FFIDContextValue, T as FFIDInquiryCategory, U as FFIDInquiryCategorySite2026, V as FFIDInquiryForm, W as FFIDInquiryFormCategoryItem, X as FFIDInquiryFormClassNames, Y as FFIDInquiryFormLegalLayout, Z as FFIDInquiryFormOrganization, _ as FFIDInquiryFormPlaceholderContext, $ as FFIDInquiryFormPrefill, a0 as FFIDInquiryFormProps, a1 as FFIDInquiryFormSubmitData, a2 as FFIDInquiryFormSubmitResult, a3 as FFIDJwtClaims, a4 as FFIDLoginButton, a5 as FFIDOAuthTokenResponse, a6 as FFIDOAuthUserInfoMemberRole, a7 as FFIDOrganizationSwitcher, a8 as FFIDRedirectErrorCode, a9 as FFIDSeatModel, aa as FFIDServiceAccessDenialReason, ab as FFIDServiceAccessFailPolicy, ac as FFIDSubscriptionBadge, ad as FFIDTokenIntrospectionResponse, ae as FFIDUserMenu, af as FFID_INQUIRY_CATEGORIES, ag as FFID_INQUIRY_CATEGORIES_SITE_2026, ah as UseFFIDAnnouncementsOptions, ai as UseFFIDAnnouncementsReturn, aj as isFFIDInquiryCategorySite2026, ak as useFFIDAnnouncements } from './index-DeegKrLW.cjs';
3
3
  export { A as ALL_DENIED_EXCEPT_NECESSARY, i as CONSENT_COOKIE_NAME, j as CONSENT_DISMISSAL_TIMESTAMP_KEY, l as COOKIE_VERSION, D as DEFAULT_CONSENT_ERROR_MESSAGES, o as FFIDAnalyticsProvider, p as FFIDAnalyticsProviderProps, c as FFIDConsentCategories, r as FFIDConsentCategoryCode, e as FFIDConsentCategoryMetadata, w as FFIDConsentError, x as FFIDConsentErrorCode, y as FFIDConsentMergeStrategy, B as FFIDConsentMergeWarning, E as FFIDConsentMergeWarningEvent, F as FFIDConsentResult, I as FFIDConsentSource, a as FFIDConsentState, d as FFIDConsentSyncResult, M as FFIDCookieBanner, N as FFIDCookieBannerClassNames, O as FFIDCookieBannerProps, Q as FFIDCookieLink, R as FFIDCookieLinkProps, f as FFIDCookiePolicy, T as FFIDCookieSettings, U as FFIDCookieSettingsClassNames, V as FFIDCookieSettingsProps, _ as FFID_CONSENT_ERROR_CODES, ae as UseFFIDConsentPreferencesReturn, af as UseFFIDConsentReturn, ag as clearConsentDismissalTimestamp, aj as decodeConsentCookie, al as encodeConsentCookie, an as readConsentCookie, ao as readConsentDismissalTimestamp, aq as useFFIDConsent, ar as useFFIDConsentPreferences, as as writeConsentCookie, at as writeConsentDismissalTimestamp } from './FFIDCookieLink-CE01gYc4.cjs';
4
4
  import * as react_jsx_runtime from 'react/jsx-runtime';
5
5
  import { ReactNode, ComponentType, FC } from 'react';
@@ -1094,13 +1094,14 @@ interface ComputeEffectiveStatusInput {
1094
1094
  * current billing period (`cancel_at_period_end=true` in Stripe).
1095
1095
  *
1096
1096
  * Optional / defaults to `false` for backward compatibility — pre-#3460
1097
- * callers do not pass this field and MUST keep observing `'active'` for
1098
- * the same inputs. When `true` and the period has not yet ended, the
1097
+ * callers (and pre-#3464 FFID backends that omit the field on the wire)
1098
+ * do not pass this field and MUST keep observing `'active'` for the
1099
+ * same inputs. When `true` and the period has not yet ended, the
1099
1100
  * `'active'` branch returns `'active_canceling'` so consumers can render
1100
1101
  * a "cancellation scheduled" badge + reactivation CTA without losing
1101
- * access. Has no effect on any other DB status branch — runtime
1102
- * `expired` still wins inside `'active'`, and Stripe does not set
1103
- * `cancel_at_period_end` on terminal states.
1102
+ * access. Outside `case 'active'` the field is intentionally ignored,
1103
+ * so any non-`active` DB status is unaffected regardless of what Stripe
1104
+ * sets.
1104
1105
  */
1105
1106
  cancelAtPeriodEnd?: boolean | undefined;
1106
1107
  }
@@ -1146,12 +1147,13 @@ declare function computeEffectiveStatusFromSession(input: ComputeEffectiveStatus
1146
1147
  * backend constants in `src/app/api/v1/subscriptions/ext/check/route.ts`
1147
1148
  * (route-local) — change there forces a sync here in the same PR.
1148
1149
  *
1149
- * `active_canceling` (post-#3460) is included for forward compatibility:
1150
- * the FFID backend currently collapses cancellation-scheduled subscriptions
1151
- * back to `active` on `/ext/check`, but if a future change promotes
1152
- * `cancel_at_period_end` into the server's `EffectiveSubscriptionStatus`
1153
- * mapping the userinfo path will already treat it as access-granting
1154
- * without an additional SDK release.
1150
+ * `active_canceling` (post-#3464, server-mirrored since SDK 5.8.0) is
1151
+ * access-granting: the FFID backend now emits the literal on
1152
+ * `/oauth/userinfo` and `/ext/check` when `cancel_at_period_end=true` and
1153
+ * the period is still live. `hasAccessFromUserinfo` treats it identically
1154
+ * to `active`; `isBlockedFromUserinfo` returns `false` for it. Callers
1155
+ * that want to render a "cancellation scheduled" badge should branch on
1156
+ * the literal directly.
1155
1157
  */
1156
1158
  declare const ACCESS_GRANTING_EFFECTIVE_STATUSES: readonly EffectiveSubscriptionStatus[];
1157
1159
  /**
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { F as FFIDSubscriptionStatus, a as FFIDConfig, b as FFIDApiResponse, c as FFIDSessionResponse, d as FFIDRedirectResult, e as FFIDError, f as FFIDSubscriptionCheckResponse, g as FFIDCheckServiceAccessParams, h as FFIDServiceAccessDecision, i as FFIDProfileCallOptions, j as FFIDUserProfile, k as FFIDUpdateUserProfileRequest, l as FFIDAnalyticsConfig, m as FFIDCreateCheckoutParams, n as FFIDCheckoutSessionResponse, o as FFIDCreatePortalParams, p as FFIDPortalSessionResponse, q as FFIDVerifyAccessTokenOptions, r as FFIDOAuthUserInfo, s as FFIDInquiryCreateParams, t as FFIDInquiryCreateResponse, u as FFIDAuthMode, v as FFIDLogger, w as FFIDCacheAdapter, x as FFIDUser, y as FFIDOrganization, z as FFIDSubscription, A as FFIDSubscriptionContextValue, E as EffectiveSubscriptionStatus, B as FFIDOAuthUserInfoSubscription, C as FFIDAnnouncementsClientConfig, L as ListAnnouncementsOptions, D as FFIDAnnouncementsApiResponse, G as AnnouncementListResponse, H as FFIDAnnouncementsLogger } from './index-EFp_ZgK9.js';
2
- export { I as Announcement, J as AnnouncementStatus, K as AnnouncementType, M as FFIDAnnouncementBadge, N as FFIDAnnouncementList, O as FFIDAnnouncementsError, P as FFIDAnnouncementsErrorCode, Q as FFIDAnnouncementsServerResponse, R as FFIDCacheConfig, S as FFIDContextValue, T as FFIDInquiryCategory, U as FFIDInquiryCategorySite2026, V as FFIDInquiryForm, W as FFIDInquiryFormCategoryItem, X as FFIDInquiryFormClassNames, Y as FFIDInquiryFormLegalLayout, Z as FFIDInquiryFormOrganization, _ as FFIDInquiryFormPlaceholderContext, $ as FFIDInquiryFormPrefill, a0 as FFIDInquiryFormProps, a1 as FFIDInquiryFormSubmitData, a2 as FFIDInquiryFormSubmitResult, a3 as FFIDJwtClaims, a4 as FFIDLoginButton, a5 as FFIDOAuthTokenResponse, a6 as FFIDOAuthUserInfoMemberRole, a7 as FFIDOrganizationSwitcher, a8 as FFIDRedirectErrorCode, a9 as FFIDSeatModel, aa as FFIDServiceAccessDenialReason, ab as FFIDServiceAccessFailPolicy, ac as FFIDSubscriptionBadge, ad as FFIDTokenIntrospectionResponse, ae as FFIDUserMenu, af as FFID_INQUIRY_CATEGORIES, ag as FFID_INQUIRY_CATEGORIES_SITE_2026, ah as UseFFIDAnnouncementsOptions, ai as UseFFIDAnnouncementsReturn, aj as isFFIDInquiryCategorySite2026, ak as useFFIDAnnouncements } from './index-EFp_ZgK9.js';
1
+ import { F as FFIDSubscriptionStatus, a as FFIDConfig, b as FFIDApiResponse, c as FFIDSessionResponse, d as FFIDRedirectResult, e as FFIDError, f as FFIDSubscriptionCheckResponse, g as FFIDCheckServiceAccessParams, h as FFIDServiceAccessDecision, i as FFIDProfileCallOptions, j as FFIDUserProfile, k as FFIDUpdateUserProfileRequest, l as FFIDAnalyticsConfig, m as FFIDCreateCheckoutParams, n as FFIDCheckoutSessionResponse, o as FFIDCreatePortalParams, p as FFIDPortalSessionResponse, q as FFIDVerifyAccessTokenOptions, r as FFIDOAuthUserInfo, s as FFIDInquiryCreateParams, t as FFIDInquiryCreateResponse, u as FFIDAuthMode, v as FFIDLogger, w as FFIDCacheAdapter, x as FFIDUser, y as FFIDOrganization, z as FFIDSubscription, A as FFIDSubscriptionContextValue, E as EffectiveSubscriptionStatus, B as FFIDOAuthUserInfoSubscription, C as FFIDAnnouncementsClientConfig, L as ListAnnouncementsOptions, D as FFIDAnnouncementsApiResponse, G as AnnouncementListResponse, H as FFIDAnnouncementsLogger } from './index-DeegKrLW.js';
2
+ export { I as Announcement, J as AnnouncementStatus, K as AnnouncementType, M as FFIDAnnouncementBadge, N as FFIDAnnouncementList, O as FFIDAnnouncementsError, P as FFIDAnnouncementsErrorCode, Q as FFIDAnnouncementsServerResponse, R as FFIDCacheConfig, S as FFIDContextValue, T as FFIDInquiryCategory, U as FFIDInquiryCategorySite2026, V as FFIDInquiryForm, W as FFIDInquiryFormCategoryItem, X as FFIDInquiryFormClassNames, Y as FFIDInquiryFormLegalLayout, Z as FFIDInquiryFormOrganization, _ as FFIDInquiryFormPlaceholderContext, $ as FFIDInquiryFormPrefill, a0 as FFIDInquiryFormProps, a1 as FFIDInquiryFormSubmitData, a2 as FFIDInquiryFormSubmitResult, a3 as FFIDJwtClaims, a4 as FFIDLoginButton, a5 as FFIDOAuthTokenResponse, a6 as FFIDOAuthUserInfoMemberRole, a7 as FFIDOrganizationSwitcher, a8 as FFIDRedirectErrorCode, a9 as FFIDSeatModel, aa as FFIDServiceAccessDenialReason, ab as FFIDServiceAccessFailPolicy, ac as FFIDSubscriptionBadge, ad as FFIDTokenIntrospectionResponse, ae as FFIDUserMenu, af as FFID_INQUIRY_CATEGORIES, ag as FFID_INQUIRY_CATEGORIES_SITE_2026, ah as UseFFIDAnnouncementsOptions, ai as UseFFIDAnnouncementsReturn, aj as isFFIDInquiryCategorySite2026, ak as useFFIDAnnouncements } from './index-DeegKrLW.js';
3
3
  export { A as ALL_DENIED_EXCEPT_NECESSARY, i as CONSENT_COOKIE_NAME, j as CONSENT_DISMISSAL_TIMESTAMP_KEY, l as COOKIE_VERSION, D as DEFAULT_CONSENT_ERROR_MESSAGES, o as FFIDAnalyticsProvider, p as FFIDAnalyticsProviderProps, c as FFIDConsentCategories, r as FFIDConsentCategoryCode, e as FFIDConsentCategoryMetadata, w as FFIDConsentError, x as FFIDConsentErrorCode, y as FFIDConsentMergeStrategy, B as FFIDConsentMergeWarning, E as FFIDConsentMergeWarningEvent, F as FFIDConsentResult, I as FFIDConsentSource, a as FFIDConsentState, d as FFIDConsentSyncResult, M as FFIDCookieBanner, N as FFIDCookieBannerClassNames, O as FFIDCookieBannerProps, Q as FFIDCookieLink, R as FFIDCookieLinkProps, f as FFIDCookiePolicy, T as FFIDCookieSettings, U as FFIDCookieSettingsClassNames, V as FFIDCookieSettingsProps, _ as FFID_CONSENT_ERROR_CODES, ae as UseFFIDConsentPreferencesReturn, af as UseFFIDConsentReturn, ag as clearConsentDismissalTimestamp, aj as decodeConsentCookie, al as encodeConsentCookie, an as readConsentCookie, ao as readConsentDismissalTimestamp, aq as useFFIDConsent, ar as useFFIDConsentPreferences, as as writeConsentCookie, at as writeConsentDismissalTimestamp } from './FFIDCookieLink-CE01gYc4.js';
4
4
  import * as react_jsx_runtime from 'react/jsx-runtime';
5
5
  import { ReactNode, ComponentType, FC } from 'react';
@@ -1094,13 +1094,14 @@ interface ComputeEffectiveStatusInput {
1094
1094
  * current billing period (`cancel_at_period_end=true` in Stripe).
1095
1095
  *
1096
1096
  * Optional / defaults to `false` for backward compatibility — pre-#3460
1097
- * callers do not pass this field and MUST keep observing `'active'` for
1098
- * the same inputs. When `true` and the period has not yet ended, the
1097
+ * callers (and pre-#3464 FFID backends that omit the field on the wire)
1098
+ * do not pass this field and MUST keep observing `'active'` for the
1099
+ * same inputs. When `true` and the period has not yet ended, the
1099
1100
  * `'active'` branch returns `'active_canceling'` so consumers can render
1100
1101
  * a "cancellation scheduled" badge + reactivation CTA without losing
1101
- * access. Has no effect on any other DB status branch — runtime
1102
- * `expired` still wins inside `'active'`, and Stripe does not set
1103
- * `cancel_at_period_end` on terminal states.
1102
+ * access. Outside `case 'active'` the field is intentionally ignored,
1103
+ * so any non-`active` DB status is unaffected regardless of what Stripe
1104
+ * sets.
1104
1105
  */
1105
1106
  cancelAtPeriodEnd?: boolean | undefined;
1106
1107
  }
@@ -1146,12 +1147,13 @@ declare function computeEffectiveStatusFromSession(input: ComputeEffectiveStatus
1146
1147
  * backend constants in `src/app/api/v1/subscriptions/ext/check/route.ts`
1147
1148
  * (route-local) — change there forces a sync here in the same PR.
1148
1149
  *
1149
- * `active_canceling` (post-#3460) is included for forward compatibility:
1150
- * the FFID backend currently collapses cancellation-scheduled subscriptions
1151
- * back to `active` on `/ext/check`, but if a future change promotes
1152
- * `cancel_at_period_end` into the server's `EffectiveSubscriptionStatus`
1153
- * mapping the userinfo path will already treat it as access-granting
1154
- * without an additional SDK release.
1150
+ * `active_canceling` (post-#3464, server-mirrored since SDK 5.8.0) is
1151
+ * access-granting: the FFID backend now emits the literal on
1152
+ * `/oauth/userinfo` and `/ext/check` when `cancel_at_period_end=true` and
1153
+ * the period is still live. `hasAccessFromUserinfo` treats it identically
1154
+ * to `active`; `isBlockedFromUserinfo` returns `false` for it. Callers
1155
+ * that want to render a "cancellation scheduled" badge should branch on
1156
+ * the literal directly.
1155
1157
  */
1156
1158
  declare const ACCESS_GRANTING_EFFECTIVE_STATUSES: readonly EffectiveSubscriptionStatus[];
1157
1159
  /**
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { useFFIDContext, useSubscription } from './chunk-YAJN36SD.js';
2
- export { ACCESS_GRANTING_EFFECTIVE_STATUSES, BLOCKING_EFFECTIVE_STATUSES, DEFAULT_API_BASE_URL, DEFAULT_OAUTH_SCOPES, FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDInquiryForm, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDProvider, FFIDSDKError, FFIDSubscriptionBadge, FFIDUserMenu, FFID_ANNOUNCEMENTS_ERROR_CODES, FFID_INQUIRY_CATEGORIES, FFID_INQUIRY_CATEGORIES_SITE_2026, computeEffectiveStatusFromSession, createFFIDAnnouncementsClient, createFFIDClient, createTokenStore, generateCodeChallenge, generateCodeVerifier, hasAccessFromUserinfo, isBlockedFromUserinfo, isFFIDInquiryCategorySite2026, normalizeRedirectUri, retrieveCodeVerifier, storeCodeVerifier, useFFID, useFFIDAnnouncements, useSubscription, withSubscription } from './chunk-YAJN36SD.js';
1
+ import { useFFIDContext, useSubscription } from './chunk-Z6COUAR6.js';
2
+ export { ACCESS_GRANTING_EFFECTIVE_STATUSES, BLOCKING_EFFECTIVE_STATUSES, DEFAULT_API_BASE_URL, DEFAULT_OAUTH_SCOPES, FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDInquiryForm, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDProvider, FFIDSDKError, FFIDSubscriptionBadge, FFIDUserMenu, FFID_ANNOUNCEMENTS_ERROR_CODES, FFID_INQUIRY_CATEGORIES, FFID_INQUIRY_CATEGORIES_SITE_2026, computeEffectiveStatusFromSession, createFFIDAnnouncementsClient, createFFIDClient, createTokenStore, generateCodeChallenge, generateCodeVerifier, hasAccessFromUserinfo, isBlockedFromUserinfo, isFFIDInquiryCategorySite2026, normalizeRedirectUri, retrieveCodeVerifier, storeCodeVerifier, useFFID, useFFIDAnnouncements, useSubscription, withSubscription } from './chunk-Z6COUAR6.js';
3
3
  export { ALL_DENIED_EXCEPT_NECESSARY, CONSENT_COOKIE_NAME, CONSENT_DISMISSAL_TIMESTAMP_KEY, COOKIE_VERSION, DEFAULT_CONSENT_ERROR_MESSAGES, FFIDAnalyticsProvider, FFIDConsentError, FFIDCookieBanner, FFIDCookieLink, FFIDCookieSettings, FFID_CONSENT_ERROR_CODES, clearConsentDismissalTimestamp, decodeConsentCookie, encodeConsentCookie, readConsentCookie, readConsentDismissalTimestamp, useFFIDConsent, useFFIDConsentPreferences, writeConsentCookie, writeConsentDismissalTimestamp } from './chunk-KJ7FUNA6.js';
4
4
  import { useEffect, useRef } from 'react';
5
5
  import { jsx, Fragment } from 'react/jsx-runtime';
@@ -125,7 +125,15 @@ function normalizeUserinfo(raw) {
125
125
  seatModel: raw.subscription.seat_model ?? null,
126
126
  memberRole: raw.subscription.member_role ?? null,
127
127
  organizationId: raw.subscription.organization_id ?? null,
128
- hasSeatAssignment: raw.subscription.has_seat_assignment ?? false
128
+ hasSeatAssignment: raw.subscription.has_seat_assignment ?? false,
129
+ // #3464: legacy FFID (< this issue) omits the field on the wire.
130
+ // The "unknown" axis is unused — no consumer differentiates "server
131
+ // didn't tell us" from "definitely not scheduled" because the
132
+ // companion `effectiveStatus` already returns `'active'` (not
133
+ // `'active_canceling'`) for legacy backends. Defaulting to `false`
134
+ // here keeps the downstream contract a plain boolean.
135
+ cancelAtPeriodEnd: raw.subscription.cancel_at_period_end ?? false,
136
+ currentPeriodEnd: raw.subscription.current_period_end ?? null
129
137
  } : void 0
130
138
  };
131
139
  }
@@ -142,8 +150,14 @@ function mapUserinfoSubscriptionToSession(userinfo, serviceCode) {
142
150
  planCode: subscription.planCode,
143
151
  planName: subscription.planCode,
144
152
  status: subscription.status,
145
- currentPeriodEnd: null,
153
+ // #3464: userinfo now carries `current_period_end` and
154
+ // `cancel_at_period_end`. Pre-#3464 FFID backends still omit them on
155
+ // the wire (`currentPeriodEnd === null` / `cancelAtPeriodEnd === false`
156
+ // after `normalizeUserinfo` defaults), so the session helper sees
157
+ // plain `'active'` — identical to pre-#3464 behaviour.
158
+ currentPeriodEnd: subscription.currentPeriodEnd,
146
159
  trialEnd: null,
160
+ cancelAtPeriodEnd: subscription.cancelAtPeriodEnd,
147
161
  seatModel: subscription.seatModel ?? void 0,
148
162
  memberRole: subscription.memberRole ?? void 0,
149
163
  organizationId: subscription.organizationId
@@ -837,7 +851,7 @@ function createProfileMethods(deps) {
837
851
  }
838
852
 
839
853
  // src/client/version-check.ts
840
- var SDK_VERSION = "5.7.0";
854
+ var SDK_VERSION = "5.8.0";
841
855
  var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
842
856
  var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
843
857
  function sdkHeaders() {
@@ -1,8 +1,8 @@
1
- import { F as FFIDLogger, a as FFIDError, b as FFIDCacheAdapter, c as FFIDVerifyAccessTokenOptions, d as FFIDApiResponse, e as FFIDOAuthUserInfo } from '../ffid-client-CknwTgly.cjs';
2
- export { f as FFIDAddMemberParams, g as FFIDAddMemberRequest, h as FFIDAddMemberResponse, i as FFIDAssignableMemberRole, j as FFIDCacheConfig, k as FFIDClient, l as FFIDConfig, m as FFIDListMembersResponse, n as FFIDMemberRole, o as FFIDOrganization, p as FFIDOrganizationMember, q as FFIDOtpSendResponse, r as FFIDOtpVerifyResponse, s as FFIDPasswordResetConfirmResponse, t as FFIDPasswordResetResponse, u as FFIDPasswordResetVerifyResponse, v as FFIDProfileCallOptions, w as FFIDRemoveMemberResponse, x as FFIDResetSessionResponse, y as FFIDSubscription, z as FFIDUpdateMemberRoleResponse, A as FFIDUpdateUserProfileRequest, B as FFIDUser, C as FFIDUserProfile, T as TokenData, D as TokenStore, E as createFFIDClient, G as createTokenStore } from '../ffid-client-CknwTgly.cjs';
1
+ import { F as FFIDLogger, a as FFIDError, b as FFIDCacheAdapter, c as FFIDVerifyAccessTokenOptions, d as FFIDApiResponse, e as FFIDOAuthUserInfo } from '../ffid-client-BhopNmnq.cjs';
2
+ export { f as FFIDAddMemberParams, g as FFIDAddMemberRequest, h as FFIDAddMemberResponse, i as FFIDAssignableMemberRole, j as FFIDCacheConfig, k as FFIDClient, l as FFIDConfig, m as FFIDListMembersResponse, n as FFIDMemberRole, o as FFIDOrganization, p as FFIDOrganizationMember, q as FFIDOtpSendResponse, r as FFIDOtpVerifyResponse, s as FFIDPasswordResetConfirmResponse, t as FFIDPasswordResetResponse, u as FFIDPasswordResetVerifyResponse, v as FFIDProfileCallOptions, w as FFIDRemoveMemberResponse, x as FFIDResetSessionResponse, y as FFIDSubscription, z as FFIDUpdateMemberRoleResponse, A as FFIDUpdateUserProfileRequest, B as FFIDUser, C as FFIDUserProfile, T as TokenData, D as TokenStore, E as createFFIDClient, G as createTokenStore } from '../ffid-client-BhopNmnq.cjs';
3
3
  export { D as DEFAULT_API_BASE_URL, a as DEFAULT_OAUTH_SCOPES } from '../constants-D61jqRIO.cjs';
4
4
  import { z } from 'zod';
5
- import '../types-s3y11uT6.cjs';
5
+ import '../types-BYdtyO_2.cjs';
6
6
 
7
7
  /** Token verification - verifyAccessToken() implementation */
8
8
 
@@ -1,8 +1,8 @@
1
- import { F as FFIDLogger, a as FFIDError, b as FFIDCacheAdapter, c as FFIDVerifyAccessTokenOptions, d as FFIDApiResponse, e as FFIDOAuthUserInfo } from '../ffid-client-C0k2PTbO.js';
2
- export { f as FFIDAddMemberParams, g as FFIDAddMemberRequest, h as FFIDAddMemberResponse, i as FFIDAssignableMemberRole, j as FFIDCacheConfig, k as FFIDClient, l as FFIDConfig, m as FFIDListMembersResponse, n as FFIDMemberRole, o as FFIDOrganization, p as FFIDOrganizationMember, q as FFIDOtpSendResponse, r as FFIDOtpVerifyResponse, s as FFIDPasswordResetConfirmResponse, t as FFIDPasswordResetResponse, u as FFIDPasswordResetVerifyResponse, v as FFIDProfileCallOptions, w as FFIDRemoveMemberResponse, x as FFIDResetSessionResponse, y as FFIDSubscription, z as FFIDUpdateMemberRoleResponse, A as FFIDUpdateUserProfileRequest, B as FFIDUser, C as FFIDUserProfile, T as TokenData, D as TokenStore, E as createFFIDClient, G as createTokenStore } from '../ffid-client-C0k2PTbO.js';
1
+ import { F as FFIDLogger, a as FFIDError, b as FFIDCacheAdapter, c as FFIDVerifyAccessTokenOptions, d as FFIDApiResponse, e as FFIDOAuthUserInfo } from '../ffid-client-DoGYvIll.js';
2
+ export { f as FFIDAddMemberParams, g as FFIDAddMemberRequest, h as FFIDAddMemberResponse, i as FFIDAssignableMemberRole, j as FFIDCacheConfig, k as FFIDClient, l as FFIDConfig, m as FFIDListMembersResponse, n as FFIDMemberRole, o as FFIDOrganization, p as FFIDOrganizationMember, q as FFIDOtpSendResponse, r as FFIDOtpVerifyResponse, s as FFIDPasswordResetConfirmResponse, t as FFIDPasswordResetResponse, u as FFIDPasswordResetVerifyResponse, v as FFIDProfileCallOptions, w as FFIDRemoveMemberResponse, x as FFIDResetSessionResponse, y as FFIDSubscription, z as FFIDUpdateMemberRoleResponse, A as FFIDUpdateUserProfileRequest, B as FFIDUser, C as FFIDUserProfile, T as TokenData, D as TokenStore, E as createFFIDClient, G as createTokenStore } from '../ffid-client-DoGYvIll.js';
3
3
  export { D as DEFAULT_API_BASE_URL, a as DEFAULT_OAUTH_SCOPES } from '../constants-D61jqRIO.js';
4
4
  import { z } from 'zod';
5
- import '../types-s3y11uT6.js';
5
+ import '../types-BYdtyO_2.js';
6
6
 
7
7
  /** Token verification - verifyAccessToken() implementation */
8
8
 
@@ -124,7 +124,15 @@ function normalizeUserinfo(raw) {
124
124
  seatModel: raw.subscription.seat_model ?? null,
125
125
  memberRole: raw.subscription.member_role ?? null,
126
126
  organizationId: raw.subscription.organization_id ?? null,
127
- hasSeatAssignment: raw.subscription.has_seat_assignment ?? false
127
+ hasSeatAssignment: raw.subscription.has_seat_assignment ?? false,
128
+ // #3464: legacy FFID (< this issue) omits the field on the wire.
129
+ // The "unknown" axis is unused — no consumer differentiates "server
130
+ // didn't tell us" from "definitely not scheduled" because the
131
+ // companion `effectiveStatus` already returns `'active'` (not
132
+ // `'active_canceling'`) for legacy backends. Defaulting to `false`
133
+ // here keeps the downstream contract a plain boolean.
134
+ cancelAtPeriodEnd: raw.subscription.cancel_at_period_end ?? false,
135
+ currentPeriodEnd: raw.subscription.current_period_end ?? null
128
136
  } : void 0
129
137
  };
130
138
  }
@@ -141,8 +149,14 @@ function mapUserinfoSubscriptionToSession(userinfo, serviceCode) {
141
149
  planCode: subscription.planCode,
142
150
  planName: subscription.planCode,
143
151
  status: subscription.status,
144
- currentPeriodEnd: null,
152
+ // #3464: userinfo now carries `current_period_end` and
153
+ // `cancel_at_period_end`. Pre-#3464 FFID backends still omit them on
154
+ // the wire (`currentPeriodEnd === null` / `cancelAtPeriodEnd === false`
155
+ // after `normalizeUserinfo` defaults), so the session helper sees
156
+ // plain `'active'` — identical to pre-#3464 behaviour.
157
+ currentPeriodEnd: subscription.currentPeriodEnd,
145
158
  trialEnd: null,
159
+ cancelAtPeriodEnd: subscription.cancelAtPeriodEnd,
146
160
  seatModel: subscription.seatModel ?? void 0,
147
161
  memberRole: subscription.memberRole ?? void 0,
148
162
  organizationId: subscription.organizationId
@@ -836,7 +850,7 @@ function createProfileMethods(deps) {
836
850
  }
837
851
 
838
852
  // src/client/version-check.ts
839
- var SDK_VERSION = "5.7.0";
853
+ var SDK_VERSION = "5.8.0";
840
854
  var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
841
855
  var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
842
856
  function sdkHeaders() {
@@ -1,5 +1,5 @@
1
- import { k as FFIDClient, e as FFIDOAuthUserInfo } from '../../ffid-client-CknwTgly.cjs';
2
- import '../../types-s3y11uT6.cjs';
1
+ import { k as FFIDClient, e as FFIDOAuthUserInfo } from '../../ffid-client-BhopNmnq.cjs';
2
+ import '../../types-BYdtyO_2.cjs';
3
3
 
4
4
  /**
5
5
  * FFID SDK - Test mode client (E2E / integration test bypass)
@@ -1,5 +1,5 @@
1
- import { k as FFIDClient, e as FFIDOAuthUserInfo } from '../../ffid-client-C0k2PTbO.js';
2
- import '../../types-s3y11uT6.js';
1
+ import { k as FFIDClient, e as FFIDOAuthUserInfo } from '../../ffid-client-DoGYvIll.js';
2
+ import '../../types-BYdtyO_2.js';
3
3
 
4
4
  /**
5
5
  * FFID SDK - Test mode client (E2E / integration test bypass)
@@ -33,22 +33,18 @@
33
33
  * This is the FFID-internal runtime-expired case (see
34
34
  * `getEffectiveSubscriptionStatus` on the server). Deny access.
35
35
  *
36
- * **SDK / server asymmetry `active_canceling` is session-only (#3460).**
37
- * The backend `EffectiveSubscriptionStatus` in
38
- * `src/lib/common/subscription-helpers.ts` does NOT currently include
39
- * `active_canceling`; cancellation-scheduled subscriptions surface as plain
40
- * `'active'` on `/api/v1/subscriptions/ext/check` and `/oauth/userinfo`, and
41
- * consumers that branch on the server verdict will not see the new literal.
42
- * The SDK enriches `computeEffectiveStatusFromSession` with
43
- * `cancelAtPeriodEnd` (carried on `FFIDSubscription` for FFID backends from
44
- * #3460 onward older backends omit the field and the helper defaults to
45
- * `false`, preserving the pre-#3460 `'active'` verdict) so `useSubscription`
46
- * can surface a "cancellation scheduled" badge without waiting for an
47
- * `/ext/check` round-trip. The asymmetry is safe because the new literal is
48
- * access-granting (no security regression on the userinfo path); when the
49
- * server's `EffectiveSubscriptionStatus` is later extended to emit
50
- * `active_canceling`, this docstring MUST be updated to drop the
51
- * session-only note.
36
+ * **`active_canceling` is mirrored on the server (#3464, SDK 5.8.0).**
37
+ * Earlier (#3460, SDK 5.7.0) the literal was a session-only enrichment;
38
+ * since FFID #3464 the backend `EffectiveSubscriptionStatus` and both
39
+ * `/api/v1/subscriptions/ext/check` + `/oauth/userinfo` emit
40
+ * `active_canceling` directly when `cancel_at_period_end=true` and the
41
+ * period is still live. SDK consumers receive the same literal whether
42
+ * they read it from the session-derived `computeEffectiveStatusFromSession`
43
+ * or the server-derived `effectiveStatus` on the userinfo / ext-check
44
+ * paths. Pre-#3464 FFID backends omit `cancelAtPeriodEnd` on the wire; the
45
+ * SDK `normalizeUserinfo` boundary defaults it to `false`, so downstream
46
+ * consumers see a plain boolean and `computeEffectiveStatus` returns the
47
+ * original `'active'` verdict for legacy deployments.
52
48
  *
53
49
  * @see /api/v1/subscriptions/ext/check
54
50
  * @see docs/03-implementation/EXPIRED_CONTRACT_HANDLING.md
@@ -33,22 +33,18 @@
33
33
  * This is the FFID-internal runtime-expired case (see
34
34
  * `getEffectiveSubscriptionStatus` on the server). Deny access.
35
35
  *
36
- * **SDK / server asymmetry `active_canceling` is session-only (#3460).**
37
- * The backend `EffectiveSubscriptionStatus` in
38
- * `src/lib/common/subscription-helpers.ts` does NOT currently include
39
- * `active_canceling`; cancellation-scheduled subscriptions surface as plain
40
- * `'active'` on `/api/v1/subscriptions/ext/check` and `/oauth/userinfo`, and
41
- * consumers that branch on the server verdict will not see the new literal.
42
- * The SDK enriches `computeEffectiveStatusFromSession` with
43
- * `cancelAtPeriodEnd` (carried on `FFIDSubscription` for FFID backends from
44
- * #3460 onward older backends omit the field and the helper defaults to
45
- * `false`, preserving the pre-#3460 `'active'` verdict) so `useSubscription`
46
- * can surface a "cancellation scheduled" badge without waiting for an
47
- * `/ext/check` round-trip. The asymmetry is safe because the new literal is
48
- * access-granting (no security regression on the userinfo path); when the
49
- * server's `EffectiveSubscriptionStatus` is later extended to emit
50
- * `active_canceling`, this docstring MUST be updated to drop the
51
- * session-only note.
36
+ * **`active_canceling` is mirrored on the server (#3464, SDK 5.8.0).**
37
+ * Earlier (#3460, SDK 5.7.0) the literal was a session-only enrichment;
38
+ * since FFID #3464 the backend `EffectiveSubscriptionStatus` and both
39
+ * `/api/v1/subscriptions/ext/check` + `/oauth/userinfo` emit
40
+ * `active_canceling` directly when `cancel_at_period_end=true` and the
41
+ * period is still live. SDK consumers receive the same literal whether
42
+ * they read it from the session-derived `computeEffectiveStatusFromSession`
43
+ * or the server-derived `effectiveStatus` on the userinfo / ext-check
44
+ * paths. Pre-#3464 FFID backends omit `cancelAtPeriodEnd` on the wire; the
45
+ * SDK `normalizeUserinfo` boundary defaults it to `false`, so downstream
46
+ * consumers see a plain boolean and `computeEffectiveStatus` returns the
47
+ * original `'active'` verdict for legacy deployments.
52
48
  *
53
49
  * @see /api/v1/subscriptions/ext/check
54
50
  * @see docs/03-implementation/EXPIRED_CONTRACT_HANDLING.md
@@ -1,4 +1,4 @@
1
- import { E as EffectiveSubscriptionStatus } from '../types-s3y11uT6.cjs';
1
+ import { E as EffectiveSubscriptionStatus } from '../types-BYdtyO_2.cjs';
2
2
 
3
3
  /**
4
4
  * FFID Webhook SDK Constants
@@ -1,4 +1,4 @@
1
- import { E as EffectiveSubscriptionStatus } from '../types-s3y11uT6.js';
1
+ import { E as EffectiveSubscriptionStatus } from '../types-BYdtyO_2.js';
2
2
 
3
3
  /**
4
4
  * FFID Webhook SDK Constants
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feelflow/ffid-sdk",
3
- "version": "5.7.0",
3
+ "version": "5.8.0",
4
4
  "description": "FeelFlow ID Platform SDK for React/Next.js applications",
5
5
  "keywords": [
6
6
  "feelflow",