@feelflow/ffid-sdk 2.17.1 → 2.19.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.
@@ -1,34 +1,34 @@
1
1
  'use strict';
2
2
 
3
- var chunkDERFBYBZ_cjs = require('../chunk-DERFBYBZ.cjs');
3
+ var chunkBBXUZS4U_cjs = require('../chunk-BBXUZS4U.cjs');
4
4
 
5
5
 
6
6
 
7
7
  Object.defineProperty(exports, "FFIDAnnouncementBadge", {
8
8
  enumerable: true,
9
- get: function () { return chunkDERFBYBZ_cjs.FFIDAnnouncementBadge; }
9
+ get: function () { return chunkBBXUZS4U_cjs.FFIDAnnouncementBadge; }
10
10
  });
11
11
  Object.defineProperty(exports, "FFIDAnnouncementList", {
12
12
  enumerable: true,
13
- get: function () { return chunkDERFBYBZ_cjs.FFIDAnnouncementList; }
13
+ get: function () { return chunkBBXUZS4U_cjs.FFIDAnnouncementList; }
14
14
  });
15
15
  Object.defineProperty(exports, "FFIDInquiryForm", {
16
16
  enumerable: true,
17
- get: function () { return chunkDERFBYBZ_cjs.FFIDInquiryForm; }
17
+ get: function () { return chunkBBXUZS4U_cjs.FFIDInquiryForm; }
18
18
  });
19
19
  Object.defineProperty(exports, "FFIDLoginButton", {
20
20
  enumerable: true,
21
- get: function () { return chunkDERFBYBZ_cjs.FFIDLoginButton; }
21
+ get: function () { return chunkBBXUZS4U_cjs.FFIDLoginButton; }
22
22
  });
23
23
  Object.defineProperty(exports, "FFIDOrganizationSwitcher", {
24
24
  enumerable: true,
25
- get: function () { return chunkDERFBYBZ_cjs.FFIDOrganizationSwitcher; }
25
+ get: function () { return chunkBBXUZS4U_cjs.FFIDOrganizationSwitcher; }
26
26
  });
27
27
  Object.defineProperty(exports, "FFIDSubscriptionBadge", {
28
28
  enumerable: true,
29
- get: function () { return chunkDERFBYBZ_cjs.FFIDSubscriptionBadge; }
29
+ get: function () { return chunkBBXUZS4U_cjs.FFIDSubscriptionBadge; }
30
30
  });
31
31
  Object.defineProperty(exports, "FFIDUserMenu", {
32
32
  enumerable: true,
33
- get: function () { return chunkDERFBYBZ_cjs.FFIDUserMenu; }
33
+ get: function () { return chunkBBXUZS4U_cjs.FFIDUserMenu; }
34
34
  });
@@ -1,3 +1,3 @@
1
- export { K as FFIDAnnouncementBadge, aj as FFIDAnnouncementBadgeClassNames, ak as FFIDAnnouncementBadgeProps, M as FFIDAnnouncementList, al as FFIDAnnouncementListClassNames, am as FFIDAnnouncementListProps, U as FFIDInquiryForm, V as FFIDInquiryFormCategoryItem, W as FFIDInquiryFormClassNames, X as FFIDInquiryFormOrganization, Y as FFIDInquiryFormPlaceholderContext, Z as FFIDInquiryFormPrefill, _ as FFIDInquiryFormProps, $ as FFIDInquiryFormSubmitData, a0 as FFIDInquiryFormSubmitResult, a2 as FFIDLoginButton, an as FFIDLoginButtonProps, a8 as FFIDOrganizationSwitcher, ao as FFIDOrganizationSwitcherClassNames, ap as FFIDOrganizationSwitcherProps, aa as FFIDSubscriptionBadge, aq as FFIDSubscriptionBadgeClassNames, ar as FFIDSubscriptionBadgeProps, ac as FFIDUserMenu, as as FFIDUserMenuClassNames, at as FFIDUserMenuProps } from '../index-Cv1qXIl1.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 FFIDInquiryFormOrganization, Z as FFIDInquiryFormPlaceholderContext, _ as FFIDInquiryFormPrefill, $ as FFIDInquiryFormProps, a0 as FFIDInquiryFormSubmitData, a1 as FFIDInquiryFormSubmitResult, a3 as FFIDLoginButton, ap as FFIDLoginButtonProps, a9 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-0D2vYSLq.cjs';
2
2
  import 'react/jsx-runtime';
3
3
  import 'react';
@@ -1,3 +1,3 @@
1
- export { K as FFIDAnnouncementBadge, aj as FFIDAnnouncementBadgeClassNames, ak as FFIDAnnouncementBadgeProps, M as FFIDAnnouncementList, al as FFIDAnnouncementListClassNames, am as FFIDAnnouncementListProps, U as FFIDInquiryForm, V as FFIDInquiryFormCategoryItem, W as FFIDInquiryFormClassNames, X as FFIDInquiryFormOrganization, Y as FFIDInquiryFormPlaceholderContext, Z as FFIDInquiryFormPrefill, _ as FFIDInquiryFormProps, $ as FFIDInquiryFormSubmitData, a0 as FFIDInquiryFormSubmitResult, a2 as FFIDLoginButton, an as FFIDLoginButtonProps, a8 as FFIDOrganizationSwitcher, ao as FFIDOrganizationSwitcherClassNames, ap as FFIDOrganizationSwitcherProps, aa as FFIDSubscriptionBadge, aq as FFIDSubscriptionBadgeClassNames, ar as FFIDSubscriptionBadgeProps, ac as FFIDUserMenu, as as FFIDUserMenuClassNames, at as FFIDUserMenuProps } from '../index-Cv1qXIl1.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 FFIDInquiryFormOrganization, Z as FFIDInquiryFormPlaceholderContext, _ as FFIDInquiryFormPrefill, $ as FFIDInquiryFormProps, a0 as FFIDInquiryFormSubmitData, a1 as FFIDInquiryFormSubmitResult, a3 as FFIDLoginButton, ap as FFIDLoginButtonProps, a9 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-0D2vYSLq.js';
2
2
  import 'react/jsx-runtime';
3
3
  import 'react';
@@ -1 +1 @@
1
- export { FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDInquiryForm, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDSubscriptionBadge, FFIDUserMenu } from '../chunk-FGTRPNSW.js';
1
+ export { FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDInquiryForm, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDSubscriptionBadge, FFIDUserMenu } from '../chunk-SXYB5QM3.js';
@@ -22,6 +22,42 @@ interface FFIDCacheConfig {
22
22
  ttl: number;
23
23
  }
24
24
 
25
+ /**
26
+ * Subscription access-control types (Chain of Pacts / Issue #2443).
27
+ *
28
+ * Defines `EffectiveSubscriptionStatus` — the semantic status surfaced by
29
+ * `/api/v1/subscriptions/ext/check` for external services that need a single
30
+ * value to drive access-control decisions. Mirrors the server-side type in
31
+ * `src/lib/common/subscription-helpers.ts` so the FFID backend, SDK, and
32
+ * consuming services agree on the same vocabulary.
33
+ */
34
+ /**
35
+ * Semantic subscription status used for access-control decisions by external
36
+ * services.
37
+ *
38
+ * Unlike the raw DB `FFIDSubscriptionStatus` (which keeps the Stripe-aligned
39
+ * status machine, e.g. `past_due` / `incomplete` / `incomplete_expired`), this
40
+ * narrowed union flattens dunning-window vs. hard-block cases so callers can
41
+ * branch on a single value:
42
+ *
43
+ * - `active` — normal paying subscription; grant full access.
44
+ * - `past_due_grace` — payment failed but FFID is still retrying. Service
45
+ * SHOULD keep access and surface a recovery banner.
46
+ * - `blocked` — payment dunning exhausted (`past_due` over grace,
47
+ * `unpaid`, `incomplete_expired`, `paused`, `incomplete`). Deny access.
48
+ * - `canceled` — contract ended (voluntary or auto-cancel). Deny access;
49
+ * check the accompanying `reactivatable` flag before showing a restart CTA.
50
+ * - `trial_expired` — `status = 'trialing'` but the trial end is in the past.
51
+ * DB row is still `trialing`; deny access and prompt for a paid plan.
52
+ * - `expired` — `status = 'active'` but `current_period_end` is in the past.
53
+ * This is the FFID-internal runtime-expired case (see
54
+ * `getEffectiveSubscriptionStatus` on the server). Deny access.
55
+ *
56
+ * @see /api/v1/subscriptions/ext/check
57
+ * @see docs/03-implementation/EXPIRED_CONTRACT_HANDLING.md
58
+ */
59
+ type EffectiveSubscriptionStatus = 'active' | 'past_due_grace' | 'blocked' | 'canceled' | 'trial_expired' | 'expired';
60
+
25
61
  /**
26
62
  * FFID SDK Type Definitions
27
63
  *
@@ -284,10 +320,59 @@ interface FFIDSubscriptionContextValue {
284
320
  isTrialExpired: boolean;
285
321
  /** Whether subscription is canceled */
286
322
  isCanceled: boolean;
323
+ /**
324
+ * Semantic access-control status for the current subscription.
325
+ *
326
+ * Always computed locally from the session response via
327
+ * `computeEffectiveStatusFromSession` (status + currentPeriodEnd + trialEnd).
328
+ * The session endpoint does not currently surface an authoritative
329
+ * effectiveStatus, so this is a best-effort approximation suitable for UI
330
+ * decisions that tolerate ~1 request worth of staleness.
331
+ *
332
+ * For authoritative decisions (e.g., paywall gates on billing-critical UI,
333
+ * grace-window banners that depend on `gracePeriodEndsAt`, or reactivation
334
+ * CTAs that depend on `reactivatable`), call the server-side
335
+ * `/api/v1/subscriptions/ext/check` endpoint directly — it returns a richer
336
+ * payload (`effectiveStatus`, `gracePeriodEndsAt`, `reactivatable`) computed
337
+ * against the live DB.
338
+ *
339
+ * `null` when there is no subscription for the current service.
340
+ *
341
+ * @see {@link EffectiveSubscriptionStatus}
342
+ */
343
+ effectiveStatus: EffectiveSubscriptionStatus | null;
344
+ /**
345
+ * Whether the subscription is in an access-blocking terminal state
346
+ * (`blocked` | `expired` | `canceled` | `trial_expired`).
347
+ */
348
+ isBlocked: boolean;
349
+ /**
350
+ * Whether the subscription is inside the payment-failure grace window
351
+ * (`past_due_grace`). Access is still granted by `hasAccess`, but UIs
352
+ * should surface a recovery banner.
353
+ */
354
+ isGrace: boolean;
287
355
  /** Check if user has specific plan */
288
356
  hasPlan: (plans: string | string[]) => boolean;
289
- /** Check if user has access (active or trialing subscription) */
357
+ /**
358
+ * Whether the user currently has access.
359
+ *
360
+ * Post Chain-of-Pacts (Issue #2444): `true` when
361
+ * `effectiveStatus === 'active' || effectiveStatus === 'past_due_grace'`.
362
+ * Legacy consumers that need the pre-2444 semantics
363
+ * (`status === 'active' || status === 'trialing'`) can use
364
+ * `hasAccessLegacy()`.
365
+ */
290
366
  hasAccess: () => boolean;
367
+ /**
368
+ * Pre-2444 `hasAccess` semantics: `true` when the raw DB status is
369
+ * `active` or `trialing` (and, for trialing, the trial is not runtime-
370
+ * expired). Kept as a backwards-compatibility escape hatch so services
371
+ * that have not yet migrated to the effective-status model keep working.
372
+ *
373
+ * @deprecated Prefer `hasAccess()` or branch on `effectiveStatus` directly.
374
+ */
375
+ hasAccessLegacy: () => boolean;
291
376
  }
292
377
  /**
293
378
  * Logger interface for SDK debug output
@@ -537,17 +622,36 @@ interface FFIDUpdateUserProfileRequest {
537
622
  /** Arbitrary user preferences bag (null clears the column; reads return {}) */
538
623
  preferences?: Record<string, unknown> | null;
539
624
  }
625
+ /**
626
+ * Discriminant for redirect failures that callers need to handle
627
+ * programmatically (vs. logging the human-readable `error` string).
628
+ *
629
+ * - `'redirect_loop_detected'`: `redirectToAuthorize()` detected that the
630
+ * same authorize URL (keyed by `baseUrl + client_id + organization_id`)
631
+ * has been fired **3 times within 60 seconds**. Caller should surface a
632
+ * manual "再度ログインする" UI rather than retry automatically (#2406 / #2411).
633
+ *
634
+ * SDK 2.18.0 only ships `'redirect_loop_detected'`. Other failure paths
635
+ * (SSR environment, PKCE generation failure, empty organizationId) currently
636
+ * return `error` without a `code`. New codes will be added in future minor
637
+ * versions — treat this union as forward-extensible and do NOT exhaustively
638
+ * `switch` over it without a `default` branch for consumer code that must
639
+ * stay compatible across SDK upgrades.
640
+ */
641
+ type FFIDRedirectErrorCode = 'redirect_loop_detected';
540
642
  /**
541
643
  * Result of a redirect operation (redirectToLogin / redirectToAuthorize / redirectToLogout)
542
644
  *
543
645
  * Structured return type so callers can inspect failure reasons
544
- * instead of receiving a bare `false`.
646
+ * instead of receiving a bare `false`. When `code` is set, branch on it
647
+ * for programmatic handling; otherwise log `error` for humans.
545
648
  */
546
649
  type FFIDRedirectResult = {
547
650
  success: true;
548
651
  } | {
549
652
  success: false;
550
653
  error: string;
654
+ code?: FFIDRedirectErrorCode;
551
655
  };
552
656
  /**
553
657
  * OAuth 2.0 token response from FFID token endpoint
@@ -1282,4 +1386,4 @@ interface FFIDInquiryFormPlaceholderContext {
1282
1386
  }
1283
1387
  declare function FFIDInquiryForm({ mode, prefill, organizations, preselectedOrganizationId, categories, termsVersion, privacyVersion, termsHref, privacyHref, turnstileToken, turnstileSlot, onSubmit, onChange, separateLegalCheckboxes, messagePlaceholder, requireCategorySelection, unstyled, classNames, locale, className, }: FFIDInquiryFormProps): react_jsx_runtime.JSX.Element;
1284
1388
 
1285
- export { type FFIDInquiryFormSubmitData as $, type FFIDSubscription as A, type FFIDSubscriptionContextValue as B, type FFIDAnnouncementsClientConfig as C, type FFIDAnnouncementsApiResponse as D, type AnnouncementListResponse as E, type FFIDSubscriptionStatus as F, type FFIDAnnouncementsLogger as G, type Announcement as H, type AnnouncementStatus as I, type AnnouncementType as J, FFIDAnnouncementBadge as K, type ListAnnouncementsOptions as L, FFIDAnnouncementList as M, type FFIDAnnouncementsError as N, type FFIDAnnouncementsErrorCode as O, type FFIDAnnouncementsServerResponse as P, type FFIDCacheConfig as Q, type FFIDContextValue as R, type FFIDInquiryCategory as S, type FFIDInquiryCategorySite2026 as T, FFIDInquiryForm as U, type FFIDInquiryFormCategoryItem as V, type FFIDInquiryFormClassNames as W, type FFIDInquiryFormOrganization as X, type FFIDInquiryFormPlaceholderContext as Y, type FFIDInquiryFormPrefill as Z, type FFIDInquiryFormProps as _, type FFIDConfig as a, type FFIDInquiryFormSubmitResult as a0, type FFIDJwtClaims as a1, FFIDLoginButton as a2, type FFIDMemberStatus as a3, type FFIDOAuthTokenResponse as a4, type FFIDOAuthUserInfoMemberRole as a5, type FFIDOAuthUserInfoSubscription as a6, type FFIDOrganizationMember as a7, FFIDOrganizationSwitcher as a8, type FFIDSeatModel as a9, FFIDSubscriptionBadge as aa, type FFIDTokenIntrospectionResponse as ab, FFIDUserMenu as ac, FFID_INQUIRY_CATEGORIES as ad, FFID_INQUIRY_CATEGORIES_SITE_2026 as ae, type UseFFIDAnnouncementsOptions as af, type UseFFIDAnnouncementsReturn as ag, isFFIDInquiryCategorySite2026 as ah, useFFIDAnnouncements as ai, type FFIDAnnouncementBadgeClassNames as aj, type FFIDAnnouncementBadgeProps as ak, type FFIDAnnouncementListClassNames as al, type FFIDAnnouncementListProps as am, type FFIDLoginButtonProps as an, type FFIDOrganizationSwitcherClassNames as ao, type FFIDOrganizationSwitcherProps as ap, type FFIDSubscriptionBadgeClassNames as aq, type FFIDSubscriptionBadgeProps as ar, type FFIDUserMenuClassNames as as, type FFIDUserMenuProps as at, type FFIDApiResponse as b, type FFIDSessionResponse as c, type FFIDRedirectResult as d, type FFIDError as e, type FFIDSubscriptionCheckResponse as f, type FFIDListMembersResponse as g, type FFIDMemberRole as h, type FFIDUpdateMemberRoleResponse as i, type FFIDRemoveMemberResponse as j, type FFIDProfileCallOptions as k, type FFIDUserProfile as l, type FFIDUpdateUserProfileRequest as m, type FFIDCreateCheckoutParams as n, type FFIDCheckoutSessionResponse as o, type FFIDCreatePortalParams as p, type FFIDPortalSessionResponse as q, type FFIDVerifyAccessTokenOptions as r, type FFIDOAuthUserInfo as s, type FFIDInquiryCreateParams as t, type FFIDInquiryCreateResponse as u, type FFIDAuthMode as v, type FFIDLogger as w, type FFIDCacheAdapter as x, type FFIDUser as y, type FFIDOrganization as z };
1389
+ export { type FFIDInquiryFormProps as $, type FFIDSubscription as A, type FFIDSubscriptionContextValue as B, type FFIDAnnouncementsClientConfig as C, type FFIDAnnouncementsApiResponse as D, type EffectiveSubscriptionStatus as E, type FFIDSubscriptionStatus as F, type AnnouncementListResponse as G, type FFIDAnnouncementsLogger as H, type Announcement as I, type AnnouncementStatus as J, type AnnouncementType as K, type ListAnnouncementsOptions as L, FFIDAnnouncementBadge as M, FFIDAnnouncementList as N, type FFIDAnnouncementsError as O, type FFIDAnnouncementsErrorCode as P, type FFIDAnnouncementsServerResponse as Q, type FFIDCacheConfig as R, type FFIDContextValue as S, type FFIDInquiryCategory as T, type FFIDInquiryCategorySite2026 as U, FFIDInquiryForm as V, type FFIDInquiryFormCategoryItem as W, type FFIDInquiryFormClassNames as X, type FFIDInquiryFormOrganization as Y, type FFIDInquiryFormPlaceholderContext as Z, type FFIDInquiryFormPrefill as _, type FFIDConfig as a, type FFIDInquiryFormSubmitData as a0, type FFIDInquiryFormSubmitResult as a1, type FFIDJwtClaims as a2, FFIDLoginButton as a3, type FFIDMemberStatus as a4, type FFIDOAuthTokenResponse as a5, type FFIDOAuthUserInfoMemberRole as a6, type FFIDOAuthUserInfoSubscription as a7, type FFIDOrganizationMember as a8, FFIDOrganizationSwitcher as a9, type FFIDRedirectErrorCode as aa, type FFIDSeatModel as ab, FFIDSubscriptionBadge as ac, type FFIDTokenIntrospectionResponse as ad, FFIDUserMenu as ae, FFID_INQUIRY_CATEGORIES as af, FFID_INQUIRY_CATEGORIES_SITE_2026 as ag, type UseFFIDAnnouncementsOptions as ah, type UseFFIDAnnouncementsReturn as ai, isFFIDInquiryCategorySite2026 as aj, useFFIDAnnouncements as ak, type FFIDAnnouncementBadgeClassNames as al, type FFIDAnnouncementBadgeProps as am, type FFIDAnnouncementListClassNames as an, type FFIDAnnouncementListProps as ao, type FFIDLoginButtonProps as ap, type FFIDOrganizationSwitcherClassNames as aq, type FFIDOrganizationSwitcherProps as ar, type FFIDSubscriptionBadgeClassNames as as, type FFIDSubscriptionBadgeProps as at, type FFIDUserMenuClassNames as au, type FFIDUserMenuProps as av, type FFIDApiResponse as b, type FFIDSessionResponse as c, type FFIDRedirectResult as d, type FFIDError as e, type FFIDSubscriptionCheckResponse as f, type FFIDListMembersResponse as g, type FFIDMemberRole as h, type FFIDUpdateMemberRoleResponse as i, type FFIDRemoveMemberResponse as j, type FFIDProfileCallOptions as k, type FFIDUserProfile as l, type FFIDUpdateUserProfileRequest as m, type FFIDCreateCheckoutParams as n, type FFIDCheckoutSessionResponse as o, type FFIDCreatePortalParams as p, type FFIDPortalSessionResponse as q, type FFIDVerifyAccessTokenOptions as r, type FFIDOAuthUserInfo as s, type FFIDInquiryCreateParams as t, type FFIDInquiryCreateResponse as u, type FFIDAuthMode as v, type FFIDLogger as w, type FFIDCacheAdapter as x, type FFIDUser as y, type FFIDOrganization as z };
@@ -22,6 +22,42 @@ interface FFIDCacheConfig {
22
22
  ttl: number;
23
23
  }
24
24
 
25
+ /**
26
+ * Subscription access-control types (Chain of Pacts / Issue #2443).
27
+ *
28
+ * Defines `EffectiveSubscriptionStatus` — the semantic status surfaced by
29
+ * `/api/v1/subscriptions/ext/check` for external services that need a single
30
+ * value to drive access-control decisions. Mirrors the server-side type in
31
+ * `src/lib/common/subscription-helpers.ts` so the FFID backend, SDK, and
32
+ * consuming services agree on the same vocabulary.
33
+ */
34
+ /**
35
+ * Semantic subscription status used for access-control decisions by external
36
+ * services.
37
+ *
38
+ * Unlike the raw DB `FFIDSubscriptionStatus` (which keeps the Stripe-aligned
39
+ * status machine, e.g. `past_due` / `incomplete` / `incomplete_expired`), this
40
+ * narrowed union flattens dunning-window vs. hard-block cases so callers can
41
+ * branch on a single value:
42
+ *
43
+ * - `active` — normal paying subscription; grant full access.
44
+ * - `past_due_grace` — payment failed but FFID is still retrying. Service
45
+ * SHOULD keep access and surface a recovery banner.
46
+ * - `blocked` — payment dunning exhausted (`past_due` over grace,
47
+ * `unpaid`, `incomplete_expired`, `paused`, `incomplete`). Deny access.
48
+ * - `canceled` — contract ended (voluntary or auto-cancel). Deny access;
49
+ * check the accompanying `reactivatable` flag before showing a restart CTA.
50
+ * - `trial_expired` — `status = 'trialing'` but the trial end is in the past.
51
+ * DB row is still `trialing`; deny access and prompt for a paid plan.
52
+ * - `expired` — `status = 'active'` but `current_period_end` is in the past.
53
+ * This is the FFID-internal runtime-expired case (see
54
+ * `getEffectiveSubscriptionStatus` on the server). Deny access.
55
+ *
56
+ * @see /api/v1/subscriptions/ext/check
57
+ * @see docs/03-implementation/EXPIRED_CONTRACT_HANDLING.md
58
+ */
59
+ type EffectiveSubscriptionStatus = 'active' | 'past_due_grace' | 'blocked' | 'canceled' | 'trial_expired' | 'expired';
60
+
25
61
  /**
26
62
  * FFID SDK Type Definitions
27
63
  *
@@ -284,10 +320,59 @@ interface FFIDSubscriptionContextValue {
284
320
  isTrialExpired: boolean;
285
321
  /** Whether subscription is canceled */
286
322
  isCanceled: boolean;
323
+ /**
324
+ * Semantic access-control status for the current subscription.
325
+ *
326
+ * Always computed locally from the session response via
327
+ * `computeEffectiveStatusFromSession` (status + currentPeriodEnd + trialEnd).
328
+ * The session endpoint does not currently surface an authoritative
329
+ * effectiveStatus, so this is a best-effort approximation suitable for UI
330
+ * decisions that tolerate ~1 request worth of staleness.
331
+ *
332
+ * For authoritative decisions (e.g., paywall gates on billing-critical UI,
333
+ * grace-window banners that depend on `gracePeriodEndsAt`, or reactivation
334
+ * CTAs that depend on `reactivatable`), call the server-side
335
+ * `/api/v1/subscriptions/ext/check` endpoint directly — it returns a richer
336
+ * payload (`effectiveStatus`, `gracePeriodEndsAt`, `reactivatable`) computed
337
+ * against the live DB.
338
+ *
339
+ * `null` when there is no subscription for the current service.
340
+ *
341
+ * @see {@link EffectiveSubscriptionStatus}
342
+ */
343
+ effectiveStatus: EffectiveSubscriptionStatus | null;
344
+ /**
345
+ * Whether the subscription is in an access-blocking terminal state
346
+ * (`blocked` | `expired` | `canceled` | `trial_expired`).
347
+ */
348
+ isBlocked: boolean;
349
+ /**
350
+ * Whether the subscription is inside the payment-failure grace window
351
+ * (`past_due_grace`). Access is still granted by `hasAccess`, but UIs
352
+ * should surface a recovery banner.
353
+ */
354
+ isGrace: boolean;
287
355
  /** Check if user has specific plan */
288
356
  hasPlan: (plans: string | string[]) => boolean;
289
- /** Check if user has access (active or trialing subscription) */
357
+ /**
358
+ * Whether the user currently has access.
359
+ *
360
+ * Post Chain-of-Pacts (Issue #2444): `true` when
361
+ * `effectiveStatus === 'active' || effectiveStatus === 'past_due_grace'`.
362
+ * Legacy consumers that need the pre-2444 semantics
363
+ * (`status === 'active' || status === 'trialing'`) can use
364
+ * `hasAccessLegacy()`.
365
+ */
290
366
  hasAccess: () => boolean;
367
+ /**
368
+ * Pre-2444 `hasAccess` semantics: `true` when the raw DB status is
369
+ * `active` or `trialing` (and, for trialing, the trial is not runtime-
370
+ * expired). Kept as a backwards-compatibility escape hatch so services
371
+ * that have not yet migrated to the effective-status model keep working.
372
+ *
373
+ * @deprecated Prefer `hasAccess()` or branch on `effectiveStatus` directly.
374
+ */
375
+ hasAccessLegacy: () => boolean;
291
376
  }
292
377
  /**
293
378
  * Logger interface for SDK debug output
@@ -537,17 +622,36 @@ interface FFIDUpdateUserProfileRequest {
537
622
  /** Arbitrary user preferences bag (null clears the column; reads return {}) */
538
623
  preferences?: Record<string, unknown> | null;
539
624
  }
625
+ /**
626
+ * Discriminant for redirect failures that callers need to handle
627
+ * programmatically (vs. logging the human-readable `error` string).
628
+ *
629
+ * - `'redirect_loop_detected'`: `redirectToAuthorize()` detected that the
630
+ * same authorize URL (keyed by `baseUrl + client_id + organization_id`)
631
+ * has been fired **3 times within 60 seconds**. Caller should surface a
632
+ * manual "再度ログインする" UI rather than retry automatically (#2406 / #2411).
633
+ *
634
+ * SDK 2.18.0 only ships `'redirect_loop_detected'`. Other failure paths
635
+ * (SSR environment, PKCE generation failure, empty organizationId) currently
636
+ * return `error` without a `code`. New codes will be added in future minor
637
+ * versions — treat this union as forward-extensible and do NOT exhaustively
638
+ * `switch` over it without a `default` branch for consumer code that must
639
+ * stay compatible across SDK upgrades.
640
+ */
641
+ type FFIDRedirectErrorCode = 'redirect_loop_detected';
540
642
  /**
541
643
  * Result of a redirect operation (redirectToLogin / redirectToAuthorize / redirectToLogout)
542
644
  *
543
645
  * Structured return type so callers can inspect failure reasons
544
- * instead of receiving a bare `false`.
646
+ * instead of receiving a bare `false`. When `code` is set, branch on it
647
+ * for programmatic handling; otherwise log `error` for humans.
545
648
  */
546
649
  type FFIDRedirectResult = {
547
650
  success: true;
548
651
  } | {
549
652
  success: false;
550
653
  error: string;
654
+ code?: FFIDRedirectErrorCode;
551
655
  };
552
656
  /**
553
657
  * OAuth 2.0 token response from FFID token endpoint
@@ -1282,4 +1386,4 @@ interface FFIDInquiryFormPlaceholderContext {
1282
1386
  }
1283
1387
  declare function FFIDInquiryForm({ mode, prefill, organizations, preselectedOrganizationId, categories, termsVersion, privacyVersion, termsHref, privacyHref, turnstileToken, turnstileSlot, onSubmit, onChange, separateLegalCheckboxes, messagePlaceholder, requireCategorySelection, unstyled, classNames, locale, className, }: FFIDInquiryFormProps): react_jsx_runtime.JSX.Element;
1284
1388
 
1285
- export { type FFIDInquiryFormSubmitData as $, type FFIDSubscription as A, type FFIDSubscriptionContextValue as B, type FFIDAnnouncementsClientConfig as C, type FFIDAnnouncementsApiResponse as D, type AnnouncementListResponse as E, type FFIDSubscriptionStatus as F, type FFIDAnnouncementsLogger as G, type Announcement as H, type AnnouncementStatus as I, type AnnouncementType as J, FFIDAnnouncementBadge as K, type ListAnnouncementsOptions as L, FFIDAnnouncementList as M, type FFIDAnnouncementsError as N, type FFIDAnnouncementsErrorCode as O, type FFIDAnnouncementsServerResponse as P, type FFIDCacheConfig as Q, type FFIDContextValue as R, type FFIDInquiryCategory as S, type FFIDInquiryCategorySite2026 as T, FFIDInquiryForm as U, type FFIDInquiryFormCategoryItem as V, type FFIDInquiryFormClassNames as W, type FFIDInquiryFormOrganization as X, type FFIDInquiryFormPlaceholderContext as Y, type FFIDInquiryFormPrefill as Z, type FFIDInquiryFormProps as _, type FFIDConfig as a, type FFIDInquiryFormSubmitResult as a0, type FFIDJwtClaims as a1, FFIDLoginButton as a2, type FFIDMemberStatus as a3, type FFIDOAuthTokenResponse as a4, type FFIDOAuthUserInfoMemberRole as a5, type FFIDOAuthUserInfoSubscription as a6, type FFIDOrganizationMember as a7, FFIDOrganizationSwitcher as a8, type FFIDSeatModel as a9, FFIDSubscriptionBadge as aa, type FFIDTokenIntrospectionResponse as ab, FFIDUserMenu as ac, FFID_INQUIRY_CATEGORIES as ad, FFID_INQUIRY_CATEGORIES_SITE_2026 as ae, type UseFFIDAnnouncementsOptions as af, type UseFFIDAnnouncementsReturn as ag, isFFIDInquiryCategorySite2026 as ah, useFFIDAnnouncements as ai, type FFIDAnnouncementBadgeClassNames as aj, type FFIDAnnouncementBadgeProps as ak, type FFIDAnnouncementListClassNames as al, type FFIDAnnouncementListProps as am, type FFIDLoginButtonProps as an, type FFIDOrganizationSwitcherClassNames as ao, type FFIDOrganizationSwitcherProps as ap, type FFIDSubscriptionBadgeClassNames as aq, type FFIDSubscriptionBadgeProps as ar, type FFIDUserMenuClassNames as as, type FFIDUserMenuProps as at, type FFIDApiResponse as b, type FFIDSessionResponse as c, type FFIDRedirectResult as d, type FFIDError as e, type FFIDSubscriptionCheckResponse as f, type FFIDListMembersResponse as g, type FFIDMemberRole as h, type FFIDUpdateMemberRoleResponse as i, type FFIDRemoveMemberResponse as j, type FFIDProfileCallOptions as k, type FFIDUserProfile as l, type FFIDUpdateUserProfileRequest as m, type FFIDCreateCheckoutParams as n, type FFIDCheckoutSessionResponse as o, type FFIDCreatePortalParams as p, type FFIDPortalSessionResponse as q, type FFIDVerifyAccessTokenOptions as r, type FFIDOAuthUserInfo as s, type FFIDInquiryCreateParams as t, type FFIDInquiryCreateResponse as u, type FFIDAuthMode as v, type FFIDLogger as w, type FFIDCacheAdapter as x, type FFIDUser as y, type FFIDOrganization as z };
1389
+ export { type FFIDInquiryFormProps as $, type FFIDSubscription as A, type FFIDSubscriptionContextValue as B, type FFIDAnnouncementsClientConfig as C, type FFIDAnnouncementsApiResponse as D, type EffectiveSubscriptionStatus as E, type FFIDSubscriptionStatus as F, type AnnouncementListResponse as G, type FFIDAnnouncementsLogger as H, type Announcement as I, type AnnouncementStatus as J, type AnnouncementType as K, type ListAnnouncementsOptions as L, FFIDAnnouncementBadge as M, FFIDAnnouncementList as N, type FFIDAnnouncementsError as O, type FFIDAnnouncementsErrorCode as P, type FFIDAnnouncementsServerResponse as Q, type FFIDCacheConfig as R, type FFIDContextValue as S, type FFIDInquiryCategory as T, type FFIDInquiryCategorySite2026 as U, FFIDInquiryForm as V, type FFIDInquiryFormCategoryItem as W, type FFIDInquiryFormClassNames as X, type FFIDInquiryFormOrganization as Y, type FFIDInquiryFormPlaceholderContext as Z, type FFIDInquiryFormPrefill as _, type FFIDConfig as a, type FFIDInquiryFormSubmitData as a0, type FFIDInquiryFormSubmitResult as a1, type FFIDJwtClaims as a2, FFIDLoginButton as a3, type FFIDMemberStatus as a4, type FFIDOAuthTokenResponse as a5, type FFIDOAuthUserInfoMemberRole as a6, type FFIDOAuthUserInfoSubscription as a7, type FFIDOrganizationMember as a8, FFIDOrganizationSwitcher as a9, type FFIDRedirectErrorCode as aa, type FFIDSeatModel as ab, FFIDSubscriptionBadge as ac, type FFIDTokenIntrospectionResponse as ad, FFIDUserMenu as ae, FFID_INQUIRY_CATEGORIES as af, FFID_INQUIRY_CATEGORIES_SITE_2026 as ag, type UseFFIDAnnouncementsOptions as ah, type UseFFIDAnnouncementsReturn as ai, isFFIDInquiryCategorySite2026 as aj, useFFIDAnnouncements as ak, type FFIDAnnouncementBadgeClassNames as al, type FFIDAnnouncementBadgeProps as am, type FFIDAnnouncementListClassNames as an, type FFIDAnnouncementListProps as ao, type FFIDLoginButtonProps as ap, type FFIDOrganizationSwitcherClassNames as aq, type FFIDOrganizationSwitcherProps as ar, type FFIDSubscriptionBadgeClassNames as as, type FFIDSubscriptionBadgeProps as at, type FFIDUserMenuClassNames as au, type FFIDUserMenuProps as av, type FFIDApiResponse as b, type FFIDSessionResponse as c, type FFIDRedirectResult as d, type FFIDError as e, type FFIDSubscriptionCheckResponse as f, type FFIDListMembersResponse as g, type FFIDMemberRole as h, type FFIDUpdateMemberRoleResponse as i, type FFIDRemoveMemberResponse as j, type FFIDProfileCallOptions as k, type FFIDUserProfile as l, type FFIDUpdateUserProfileRequest as m, type FFIDCreateCheckoutParams as n, type FFIDCheckoutSessionResponse as o, type FFIDCreatePortalParams as p, type FFIDPortalSessionResponse as q, type FFIDVerifyAccessTokenOptions as r, type FFIDOAuthUserInfo as s, type FFIDInquiryCreateParams as t, type FFIDInquiryCreateResponse as u, type FFIDAuthMode as v, type FFIDLogger as w, type FFIDCacheAdapter as x, type FFIDUser as y, type FFIDOrganization as z };
package/dist/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkDERFBYBZ_cjs = require('./chunk-DERFBYBZ.cjs');
3
+ var chunkBBXUZS4U_cjs = require('./chunk-BBXUZS4U.cjs');
4
4
  var react = require('react');
5
5
  var jsxRuntime = require('react/jsx-runtime');
6
6
 
@@ -44,9 +44,38 @@ function createKVCacheAdapter(kv) {
44
44
  }
45
45
  };
46
46
  }
47
+ function resolveRedirectUrl(redirectTo, status) {
48
+ return typeof redirectTo === "function" ? redirectTo(status) : redirectTo;
49
+ }
50
+ function defaultRedirect(url) {
51
+ if (typeof window === "undefined") return;
52
+ window.location.href = url;
53
+ }
54
+ function useRequireActiveSubscription(options) {
55
+ const { redirectTo, allowGrace = true, onRedirect } = options;
56
+ const { isLoading, error } = chunkBBXUZS4U_cjs.useFFIDContext();
57
+ const { effectiveStatus, isBlocked, isGrace } = chunkBBXUZS4U_cjs.useSubscription();
58
+ const hasFetchError = error !== null && effectiveStatus === null;
59
+ const shouldRedirect = !isLoading && !hasFetchError && (isBlocked || !allowGrace && isGrace || effectiveStatus === null);
60
+ react.useEffect(() => {
61
+ if (!shouldRedirect) return;
62
+ const triggeringStatus = effectiveStatus ?? "blocked";
63
+ const url = resolveRedirectUrl(redirectTo, triggeringStatus);
64
+ if (onRedirect) {
65
+ onRedirect(url);
66
+ } else {
67
+ defaultRedirect(url);
68
+ }
69
+ }, [shouldRedirect, effectiveStatus]);
70
+ return {
71
+ loading: isLoading,
72
+ effectiveStatus,
73
+ error
74
+ };
75
+ }
47
76
  function withFFIDAuth(Component, options = {}) {
48
77
  const WrappedComponent = (props) => {
49
- const { isLoading, isAuthenticated, login } = chunkDERFBYBZ_cjs.useFFIDContext();
78
+ const { isLoading, isAuthenticated, login } = chunkBBXUZS4U_cjs.useFFIDContext();
50
79
  const hasRedirected = react.useRef(false);
51
80
  react.useEffect(() => {
52
81
  if (!isLoading && !isAuthenticated && options.redirectToLogin && !hasRedirected.current) {
@@ -74,109 +103,114 @@ var FFID_NEWSLETTER_TYPES = ["inquiry_followup", "general"];
74
103
 
75
104
  Object.defineProperty(exports, "DEFAULT_API_BASE_URL", {
76
105
  enumerable: true,
77
- get: function () { return chunkDERFBYBZ_cjs.DEFAULT_API_BASE_URL; }
106
+ get: function () { return chunkBBXUZS4U_cjs.DEFAULT_API_BASE_URL; }
78
107
  });
79
108
  Object.defineProperty(exports, "FFIDAnnouncementBadge", {
80
109
  enumerable: true,
81
- get: function () { return chunkDERFBYBZ_cjs.FFIDAnnouncementBadge; }
110
+ get: function () { return chunkBBXUZS4U_cjs.FFIDAnnouncementBadge; }
82
111
  });
83
112
  Object.defineProperty(exports, "FFIDAnnouncementList", {
84
113
  enumerable: true,
85
- get: function () { return chunkDERFBYBZ_cjs.FFIDAnnouncementList; }
114
+ get: function () { return chunkBBXUZS4U_cjs.FFIDAnnouncementList; }
86
115
  });
87
116
  Object.defineProperty(exports, "FFIDInquiryForm", {
88
117
  enumerable: true,
89
- get: function () { return chunkDERFBYBZ_cjs.FFIDInquiryForm; }
118
+ get: function () { return chunkBBXUZS4U_cjs.FFIDInquiryForm; }
90
119
  });
91
120
  Object.defineProperty(exports, "FFIDLoginButton", {
92
121
  enumerable: true,
93
- get: function () { return chunkDERFBYBZ_cjs.FFIDLoginButton; }
122
+ get: function () { return chunkBBXUZS4U_cjs.FFIDLoginButton; }
94
123
  });
95
124
  Object.defineProperty(exports, "FFIDOrganizationSwitcher", {
96
125
  enumerable: true,
97
- get: function () { return chunkDERFBYBZ_cjs.FFIDOrganizationSwitcher; }
126
+ get: function () { return chunkBBXUZS4U_cjs.FFIDOrganizationSwitcher; }
98
127
  });
99
128
  Object.defineProperty(exports, "FFIDProvider", {
100
129
  enumerable: true,
101
- get: function () { return chunkDERFBYBZ_cjs.FFIDProvider; }
130
+ get: function () { return chunkBBXUZS4U_cjs.FFIDProvider; }
102
131
  });
103
132
  Object.defineProperty(exports, "FFIDSDKError", {
104
133
  enumerable: true,
105
- get: function () { return chunkDERFBYBZ_cjs.FFIDSDKError; }
134
+ get: function () { return chunkBBXUZS4U_cjs.FFIDSDKError; }
106
135
  });
107
136
  Object.defineProperty(exports, "FFIDSubscriptionBadge", {
108
137
  enumerable: true,
109
- get: function () { return chunkDERFBYBZ_cjs.FFIDSubscriptionBadge; }
138
+ get: function () { return chunkBBXUZS4U_cjs.FFIDSubscriptionBadge; }
110
139
  });
111
140
  Object.defineProperty(exports, "FFIDUserMenu", {
112
141
  enumerable: true,
113
- get: function () { return chunkDERFBYBZ_cjs.FFIDUserMenu; }
142
+ get: function () { return chunkBBXUZS4U_cjs.FFIDUserMenu; }
114
143
  });
115
144
  Object.defineProperty(exports, "FFID_ANNOUNCEMENTS_ERROR_CODES", {
116
145
  enumerable: true,
117
- get: function () { return chunkDERFBYBZ_cjs.FFID_ANNOUNCEMENTS_ERROR_CODES; }
146
+ get: function () { return chunkBBXUZS4U_cjs.FFID_ANNOUNCEMENTS_ERROR_CODES; }
118
147
  });
119
148
  Object.defineProperty(exports, "FFID_INQUIRY_CATEGORIES", {
120
149
  enumerable: true,
121
- get: function () { return chunkDERFBYBZ_cjs.FFID_INQUIRY_CATEGORIES; }
150
+ get: function () { return chunkBBXUZS4U_cjs.FFID_INQUIRY_CATEGORIES; }
122
151
  });
123
152
  Object.defineProperty(exports, "FFID_INQUIRY_CATEGORIES_SITE_2026", {
124
153
  enumerable: true,
125
- get: function () { return chunkDERFBYBZ_cjs.FFID_INQUIRY_CATEGORIES_SITE_2026; }
154
+ get: function () { return chunkBBXUZS4U_cjs.FFID_INQUIRY_CATEGORIES_SITE_2026; }
155
+ });
156
+ Object.defineProperty(exports, "computeEffectiveStatusFromSession", {
157
+ enumerable: true,
158
+ get: function () { return chunkBBXUZS4U_cjs.computeEffectiveStatusFromSession; }
126
159
  });
127
160
  Object.defineProperty(exports, "createFFIDAnnouncementsClient", {
128
161
  enumerable: true,
129
- get: function () { return chunkDERFBYBZ_cjs.createFFIDAnnouncementsClient; }
162
+ get: function () { return chunkBBXUZS4U_cjs.createFFIDAnnouncementsClient; }
130
163
  });
131
164
  Object.defineProperty(exports, "createFFIDClient", {
132
165
  enumerable: true,
133
- get: function () { return chunkDERFBYBZ_cjs.createFFIDClient; }
166
+ get: function () { return chunkBBXUZS4U_cjs.createFFIDClient; }
134
167
  });
135
168
  Object.defineProperty(exports, "createTokenStore", {
136
169
  enumerable: true,
137
- get: function () { return chunkDERFBYBZ_cjs.createTokenStore; }
170
+ get: function () { return chunkBBXUZS4U_cjs.createTokenStore; }
138
171
  });
139
172
  Object.defineProperty(exports, "generateCodeChallenge", {
140
173
  enumerable: true,
141
- get: function () { return chunkDERFBYBZ_cjs.generateCodeChallenge; }
174
+ get: function () { return chunkBBXUZS4U_cjs.generateCodeChallenge; }
142
175
  });
143
176
  Object.defineProperty(exports, "generateCodeVerifier", {
144
177
  enumerable: true,
145
- get: function () { return chunkDERFBYBZ_cjs.generateCodeVerifier; }
178
+ get: function () { return chunkBBXUZS4U_cjs.generateCodeVerifier; }
146
179
  });
147
180
  Object.defineProperty(exports, "isFFIDInquiryCategorySite2026", {
148
181
  enumerable: true,
149
- get: function () { return chunkDERFBYBZ_cjs.isFFIDInquiryCategorySite2026; }
182
+ get: function () { return chunkBBXUZS4U_cjs.isFFIDInquiryCategorySite2026; }
150
183
  });
151
184
  Object.defineProperty(exports, "normalizeRedirectUri", {
152
185
  enumerable: true,
153
- get: function () { return chunkDERFBYBZ_cjs.normalizeRedirectUri; }
186
+ get: function () { return chunkBBXUZS4U_cjs.normalizeRedirectUri; }
154
187
  });
155
188
  Object.defineProperty(exports, "retrieveCodeVerifier", {
156
189
  enumerable: true,
157
- get: function () { return chunkDERFBYBZ_cjs.retrieveCodeVerifier; }
190
+ get: function () { return chunkBBXUZS4U_cjs.retrieveCodeVerifier; }
158
191
  });
159
192
  Object.defineProperty(exports, "storeCodeVerifier", {
160
193
  enumerable: true,
161
- get: function () { return chunkDERFBYBZ_cjs.storeCodeVerifier; }
194
+ get: function () { return chunkBBXUZS4U_cjs.storeCodeVerifier; }
162
195
  });
163
196
  Object.defineProperty(exports, "useFFID", {
164
197
  enumerable: true,
165
- get: function () { return chunkDERFBYBZ_cjs.useFFID; }
198
+ get: function () { return chunkBBXUZS4U_cjs.useFFID; }
166
199
  });
167
200
  Object.defineProperty(exports, "useFFIDAnnouncements", {
168
201
  enumerable: true,
169
- get: function () { return chunkDERFBYBZ_cjs.useFFIDAnnouncements; }
202
+ get: function () { return chunkBBXUZS4U_cjs.useFFIDAnnouncements; }
170
203
  });
171
204
  Object.defineProperty(exports, "useSubscription", {
172
205
  enumerable: true,
173
- get: function () { return chunkDERFBYBZ_cjs.useSubscription; }
206
+ get: function () { return chunkBBXUZS4U_cjs.useSubscription; }
174
207
  });
175
208
  Object.defineProperty(exports, "withSubscription", {
176
209
  enumerable: true,
177
- get: function () { return chunkDERFBYBZ_cjs.withSubscription; }
210
+ get: function () { return chunkBBXUZS4U_cjs.withSubscription; }
178
211
  });
179
212
  exports.FFID_NEWSLETTER_TYPES = FFID_NEWSLETTER_TYPES;
180
213
  exports.createKVCacheAdapter = createKVCacheAdapter;
181
214
  exports.createMemoryCacheAdapter = createMemoryCacheAdapter;
215
+ exports.useRequireActiveSubscription = useRequireActiveSubscription;
182
216
  exports.withFFIDAuth = withFFIDAuth;