@feelflow/ffid-sdk 2.18.0 → 2.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -206,16 +206,51 @@ const {
206
206
 
207
207
  ```tsx
208
208
  const {
209
- subscription, // FFIDSubscription | null - 現在のサブスクリプション
210
- planCode, // string | null - プランコード
211
- isActive, // boolean - アクティブ契約
212
- isTrialing, // boolean - トライアル中
213
- isCanceled, // boolean - 解約済み
214
- hasPlan, // (plans: string | string[]) => boolean - プラン確認
215
- hasAccess, // () => boolean - アクセス権確認
209
+ subscription, // FFIDSubscription | null - 現在のサブスクリプション
210
+ planCode, // string | null - プランコード
211
+ isActive, // boolean - DB ステータスが 'active'
212
+ isTrialing, // boolean - トライアル中
213
+ isCanceled, // boolean - 解約済み
214
+ isTrialExpired, // boolean - トライアル期間超過
215
+ effectiveStatus, // EffectiveSubscriptionStatus | null - 意味論的アクセス制御値
216
+ isBlocked, // boolean - blocked / expired / canceled / trial_expired
217
+ isGrace, // boolean - past_due_grace (支払い失敗の猶予期間中)
218
+ hasPlan, // (plans: string | string[]) => boolean - プラン確認
219
+ hasAccess, // () => boolean - アクセス権確認 (active || past_due_grace)
220
+ hasAccessLegacy, // () => boolean - 旧セマンティクス (active || trialing, pre-2.19)
216
221
  } = useSubscription()
217
222
  ```
218
223
 
224
+ `effectiveStatus` は `/api/v1/subscriptions/ext/check` が返す意味論的ステータスと同じ値を取る。詳細は [契約期限切れハンドリング](#契約期限切れハンドリング) を参照。
225
+
226
+ ### useRequireActiveSubscription()
227
+
228
+ 契約が期限切れ/遮断状態のときに自動でリダイレクトするフック。
229
+
230
+ ```tsx
231
+ 'use client'
232
+ import { useRouter } from 'next/navigation'
233
+ import { useRequireActiveSubscription } from '@feelflow/ffid-sdk'
234
+
235
+ function ProtectedShell({ children }: { children: React.ReactNode }) {
236
+ const router = useRouter()
237
+ const { loading } = useRequireActiveSubscription({
238
+ redirectTo: (status) =>
239
+ status === 'canceled' ? '/contract-ended' : '/contract-required',
240
+ onRedirect: (url) => router.replace(url),
241
+ })
242
+
243
+ if (loading) return <FullPageSpinner />
244
+ return <>{children}</>
245
+ }
246
+ ```
247
+
248
+ オプション:
249
+
250
+ - `redirectTo`: `string` または `(status: EffectiveSubscriptionStatus) => string`
251
+ - `allowGrace` (default: `true`): `past_due_grace` を通過させるか
252
+ - `onRedirect` (optional): 独自のリダイレクト関数(未指定時は `window.location.href`)
253
+
219
254
  ### withSubscription()
220
255
 
221
256
  サブスクリプション確認HOC。
@@ -228,6 +263,34 @@ const PremiumFeature = withSubscription(MyComponent, {
228
263
  })
229
264
  ```
230
265
 
266
+ ## 契約期限切れハンドリング
267
+
268
+ 契約が失効したり解約されたとき、外部サービス側で適切にアクセスを遮断し、ユーザーを再契約動線に案内する必要があります。SDK は 3 層構成(トークン検証 / 契約チェック / Webhook 受信)をサポートしています。
269
+
270
+ ### 最小構成(Next.js App Router)
271
+
272
+ ```tsx
273
+ 'use client'
274
+ import { useRouter } from 'next/navigation'
275
+ import { useRequireActiveSubscription } from '@feelflow/ffid-sdk'
276
+
277
+ export default function Layout({ children }: { children: React.ReactNode }) {
278
+ const router = useRouter()
279
+ const { loading, effectiveStatus } = useRequireActiveSubscription({
280
+ redirectTo: '/contract-required',
281
+ onRedirect: (url) => router.replace(url),
282
+ })
283
+
284
+ if (loading) return <Spinner />
285
+ if (effectiveStatus !== 'active' && effectiveStatus !== 'past_due_grace') {
286
+ return null // リダイレクト発火中
287
+ }
288
+ return <>{children}</>
289
+ }
290
+ ```
291
+
292
+ **詳細な実装レシピ**(middleware、Express、UI 分岐、Webhook 受信、fixture API を使ったテスト、`EffectiveSubscriptionStatus` 別の UI 推奨動作まで) → [docs/03-implementation/EXPIRED_CONTRACT_HANDLING.md](https://github.com/feel-flow/feelflow-id-platform/blob/develop/docs/03-implementation/EXPIRED_CONTRACT_HANDLING.md)
293
+
231
294
  ### getProfile() / updateProfile()
232
295
 
233
296
  ログイン中ユーザー自身のプロフィールを取得・更新するメソッド(`createFFIDClient` から呼び出し)。
@@ -424,6 +487,88 @@ SDK の `package.json` で `react` / `react-dom` は `optional: true` に設定
424
487
  }
425
488
  ```
426
489
 
490
+ ## E2E テストモード(`@feelflow/ffid-sdk/server/test`)
491
+
492
+ > **⚠️ SECURITY NOTICE** — テストモードは Bearer トークン検証を **意図的にバイパス** する仕組みです。本番環境で誤って有効化すると、登録されたバイパストークンを持つ任意のリクエストが認証通過します。
493
+
494
+ 各サービスで E2E テストを書く際、`verifyAccessToken` の introspect 呼び出しをモックする実装を独自に持つ必要がなくなります。SDK 側で **多重 production guard 付き** の bypass クライアントを提供します。
495
+
496
+ ```ts
497
+ import { createFFIDClient } from '@feelflow/ffid-sdk/server'
498
+ import { createTestFFIDClient } from '@feelflow/ffid-sdk/server/test'
499
+
500
+ const isE2E =
501
+ process.env.NODE_ENV !== 'production' &&
502
+ process.env.FFID_TEST_MODE === 'true'
503
+
504
+ const client = isE2E
505
+ ? createTestFFIDClient({
506
+ users: [
507
+ {
508
+ bypassToken: process.env.E2E_TEST_BYPASS_SECRET!,
509
+ userInfo: {
510
+ sub: 'e2e-test-sub',
511
+ email: 'e2e@example.com',
512
+ name: 'E2E Test User',
513
+ picture: null,
514
+ },
515
+ },
516
+ ],
517
+ })
518
+ : createFFIDClient({ /* normal production options */ })
519
+
520
+ const result = await client.verifyAccessToken(bearerToken)
521
+ ```
522
+
523
+ ### Built-in production guards (defense-in-depth)
524
+
525
+ - `NODE_ENV` を **trim + lowercase** 後に比較。`"production "`(改行混入)や `"Production"` も production として扱う(Vercel 環境変数の copy/paste 事故対策)
526
+ - `process.env` を露出しない runtime(Edge / Cloudflare Workers / browser)では **fail-close** で構築拒否
527
+ - `bypassToken` の重複検知 / 空チェック / `userInfo.sub` 必須チェック(構築時 throw)
528
+ - 未登録 token は **fail-close**(実 introspect への暗黙 fallthrough は行わない)
529
+ - 構築時点で `users` のスナップショットを取り、入力配列の post-construction mutation は無視
530
+ - 各 `verifyAccessToken` 呼び出しは新しいオブジェクトを返却(caller mutation が後続呼び出しを汚染しない)
531
+
532
+ ### `allowInProduction` escape hatch
533
+
534
+ staging が `NODE_ENV=production` をミラーするケース等のみ、明示的な ack 文字列で有効化できます:
535
+
536
+ ```ts
537
+ import {
538
+ createTestFFIDClient,
539
+ TEST_CLIENT_ALLOW_IN_PRODUCTION_ACK,
540
+ } from '@feelflow/ffid-sdk/server/test'
541
+
542
+ createTestFFIDClient({
543
+ users: [...],
544
+ allowInProduction: TEST_CLIENT_ALLOW_IN_PRODUCTION_ACK,
545
+ })
546
+ // → process.emitWarning(..., 'FFIDTestModeInProduction') を毎構築時に発火
547
+ ```
548
+
549
+ `boolean` ではなく **literal string ack** を要求する型なので、`allowInProduction: someBooleanFlag` のような誤代入はコンパイルエラーになります。ack 文字列は grep 可能で監査も容易です。
550
+
551
+ ### サブパス分離
552
+
553
+ `createTestFFIDClient` は **`@feelflow/ffid-sdk/server/test` からのみ** import 可能です(`@feelflow/ffid-sdk/server` にも main entry にも含まれない)。本番コードからの誤 import は ESLint の `no-restricted-imports` 等で検知することを推奨します:
554
+
555
+ ```js
556
+ // eslint.config.mjs (flat config)
557
+ import { defineConfig } from 'eslint/config'
558
+
559
+ export default defineConfig([
560
+ {
561
+ files: ['src/**/*.{ts,tsx}'], // production code only
562
+ ignores: ['**/__tests__/**', 'tests/e2e/**'],
563
+ rules: {
564
+ 'no-restricted-imports': ['error', {
565
+ patterns: ['@feelflow/ffid-sdk/server/test'],
566
+ }],
567
+ },
568
+ },
569
+ ])
570
+ ```
571
+
427
572
  ## 環境変数
428
573
 
429
574
  オプションで環境変数を使用してデフォルト設定を上書きできます:
@@ -805,7 +805,7 @@ function createProfileMethods(deps) {
805
805
  }
806
806
 
807
807
  // src/client/version-check.ts
808
- var SDK_VERSION = "2.18.0";
808
+ var SDK_VERSION = "2.20.0";
809
809
  var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
810
810
  var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
811
811
  function sdkHeaders() {
@@ -3160,6 +3160,67 @@ function FFIDOrganizationSwitcher({
3160
3160
  ] })
3161
3161
  ] });
3162
3162
  }
3163
+
3164
+ // src/subscriptions/types.ts
3165
+ var PAST_DUE_GRACE_PERIOD_DAYS = 7;
3166
+ var HOURS_PER_DAY = 24;
3167
+ var MINUTES_PER_HOUR = 60;
3168
+ var SECONDS_PER_MINUTE = 60;
3169
+ var MS_PER_SECOND2 = 1e3;
3170
+ var MS_PER_DAY = HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE * MS_PER_SECOND2;
3171
+
3172
+ // src/subscriptions/compute-effective-status.ts
3173
+ function isExpired(isoTimestamp, nowMs) {
3174
+ if (!isoTimestamp) return null;
3175
+ const parsed = Date.parse(isoTimestamp);
3176
+ if (Number.isNaN(parsed)) return null;
3177
+ return parsed < nowMs;
3178
+ }
3179
+ function computeEffectiveStatusFromSession(input, nowMs = Date.now()) {
3180
+ const { status, currentPeriodEnd, trialEnd, pastDueSince } = input;
3181
+ switch (status) {
3182
+ case "trialing": {
3183
+ const relevantEnd = trialEnd ?? currentPeriodEnd;
3184
+ const expired = isExpired(relevantEnd, nowMs);
3185
+ return expired === true ? "trial_expired" : "active";
3186
+ }
3187
+ case "active": {
3188
+ const expired = isExpired(currentPeriodEnd, nowMs);
3189
+ return expired === true ? "expired" : "active";
3190
+ }
3191
+ case "past_due": {
3192
+ const baselineIso = pastDueSince ?? currentPeriodEnd;
3193
+ if (!baselineIso) return "blocked";
3194
+ const baselineMs = Date.parse(baselineIso);
3195
+ if (Number.isNaN(baselineMs)) return "blocked";
3196
+ const graceEndMs = baselineMs + PAST_DUE_GRACE_PERIOD_DAYS * MS_PER_DAY;
3197
+ return graceEndMs > nowMs ? "past_due_grace" : "blocked";
3198
+ }
3199
+ case "pending_invoice":
3200
+ return "active";
3201
+ case "paused":
3202
+ case "incomplete":
3203
+ return "active";
3204
+ case "canceled":
3205
+ return "canceled";
3206
+ case "unpaid":
3207
+ case "incomplete_expired":
3208
+ return "blocked";
3209
+ default: {
3210
+ return "blocked";
3211
+ }
3212
+ }
3213
+ }
3214
+ var ACCESS_GRANTING_STATUSES = [
3215
+ "active",
3216
+ "past_due_grace"
3217
+ ];
3218
+ var BLOCKING_STATUSES = [
3219
+ "blocked",
3220
+ "expired",
3221
+ "canceled",
3222
+ "trial_expired"
3223
+ ];
3163
3224
  function useSubscription() {
3164
3225
  const context = useFFIDContext();
3165
3226
  const client = useFFIDClient();
@@ -3177,12 +3238,23 @@ function useSubscription() {
3177
3238
  const isActive = subscription?.status === "active";
3178
3239
  const isTrialing = subscription?.status === "trialing";
3179
3240
  const isCanceled = subscription?.status === "canceled";
3180
- const isTrialExpired = useMemo(() => {
3181
- if (!isTrialing) return false;
3182
- const relevantEnd = subscription?.trialEnd ?? subscription?.currentPeriodEnd;
3183
- if (!relevantEnd) return false;
3184
- return new Date(relevantEnd).getTime() < Date.now();
3185
- }, [isTrialing, subscription?.trialEnd, subscription?.currentPeriodEnd]);
3241
+ const effectiveStatus = useMemo(() => {
3242
+ if (!subscription) return null;
3243
+ return computeEffectiveStatusFromSession({
3244
+ status: subscription.status,
3245
+ currentPeriodEnd: subscription.currentPeriodEnd,
3246
+ trialEnd: subscription.trialEnd,
3247
+ // TODO(別Issue): FFID backend の session API (`/api/auth/session`) に
3248
+ // `pastDueSince` (ISO timestamp) を追加し、ここで渡す。現状は undefined
3249
+ // のため `computeEffectiveStatusFromSession` 内で currentPeriodEnd を
3250
+ // fallback に使う (Stripe は period end 近辺で past_due にする)。
3251
+ // Canonical な verdict が必要な consumer は `/ext/check` を直接呼ぶ。
3252
+ pastDueSince: void 0
3253
+ });
3254
+ }, [subscription]);
3255
+ const isGrace = effectiveStatus === "past_due_grace";
3256
+ const isBlocked = effectiveStatus !== null && BLOCKING_STATUSES.includes(effectiveStatus);
3257
+ const isTrialExpired = effectiveStatus === "trial_expired";
3186
3258
  const hasPlan = useCallback(
3187
3259
  (plans) => {
3188
3260
  if (!planCode) return false;
@@ -3192,6 +3264,10 @@ function useSubscription() {
3192
3264
  [planCode]
3193
3265
  );
3194
3266
  const hasAccess = useCallback(() => {
3267
+ if (effectiveStatus === null) return false;
3268
+ return ACCESS_GRANTING_STATUSES.includes(effectiveStatus);
3269
+ }, [effectiveStatus]);
3270
+ const hasAccessLegacy = useCallback(() => {
3195
3271
  if (isTrialExpired) return false;
3196
3272
  return isActive || isTrialing;
3197
3273
  }, [isActive, isTrialing, isTrialExpired]);
@@ -3202,8 +3278,12 @@ function useSubscription() {
3202
3278
  isTrialing,
3203
3279
  isTrialExpired,
3204
3280
  isCanceled,
3281
+ effectiveStatus,
3282
+ isBlocked,
3283
+ isGrace,
3205
3284
  hasPlan,
3206
- hasAccess
3285
+ hasAccess,
3286
+ hasAccessLegacy
3207
3287
  };
3208
3288
  }
3209
3289
  function withSubscription(Component, options = {}) {
@@ -4426,4 +4506,4 @@ function FFIDInquiryForm({
4426
4506
  );
4427
4507
  }
4428
4508
 
4429
- export { DEFAULT_API_BASE_URL, FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDInquiryForm, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDProvider, FFIDSDKError, FFIDSubscriptionBadge, FFIDUserMenu, FFID_ANNOUNCEMENTS_ERROR_CODES, FFID_INQUIRY_CATEGORIES, FFID_INQUIRY_CATEGORIES_SITE_2026, createFFIDAnnouncementsClient, createFFIDClient, createTokenStore, generateCodeChallenge, generateCodeVerifier, isFFIDInquiryCategorySite2026, normalizeRedirectUri, retrieveCodeVerifier, storeCodeVerifier, useFFID, useFFIDAnnouncements, useFFIDContext, useSubscription, withSubscription };
4509
+ export { DEFAULT_API_BASE_URL, FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDInquiryForm, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDProvider, FFIDSDKError, FFIDSubscriptionBadge, FFIDUserMenu, FFID_ANNOUNCEMENTS_ERROR_CODES, FFID_INQUIRY_CATEGORIES, FFID_INQUIRY_CATEGORIES_SITE_2026, computeEffectiveStatusFromSession, createFFIDAnnouncementsClient, createFFIDClient, createTokenStore, generateCodeChallenge, generateCodeVerifier, isFFIDInquiryCategorySite2026, normalizeRedirectUri, retrieveCodeVerifier, storeCodeVerifier, useFFID, useFFIDAnnouncements, useFFIDContext, useSubscription, withSubscription };
@@ -807,7 +807,7 @@ function createProfileMethods(deps) {
807
807
  }
808
808
 
809
809
  // src/client/version-check.ts
810
- var SDK_VERSION = "2.18.0";
810
+ var SDK_VERSION = "2.20.0";
811
811
  var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
812
812
  var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
813
813
  function sdkHeaders() {
@@ -3162,6 +3162,67 @@ function FFIDOrganizationSwitcher({
3162
3162
  ] })
3163
3163
  ] });
3164
3164
  }
3165
+
3166
+ // src/subscriptions/types.ts
3167
+ var PAST_DUE_GRACE_PERIOD_DAYS = 7;
3168
+ var HOURS_PER_DAY = 24;
3169
+ var MINUTES_PER_HOUR = 60;
3170
+ var SECONDS_PER_MINUTE = 60;
3171
+ var MS_PER_SECOND2 = 1e3;
3172
+ var MS_PER_DAY = HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE * MS_PER_SECOND2;
3173
+
3174
+ // src/subscriptions/compute-effective-status.ts
3175
+ function isExpired(isoTimestamp, nowMs) {
3176
+ if (!isoTimestamp) return null;
3177
+ const parsed = Date.parse(isoTimestamp);
3178
+ if (Number.isNaN(parsed)) return null;
3179
+ return parsed < nowMs;
3180
+ }
3181
+ function computeEffectiveStatusFromSession(input, nowMs = Date.now()) {
3182
+ const { status, currentPeriodEnd, trialEnd, pastDueSince } = input;
3183
+ switch (status) {
3184
+ case "trialing": {
3185
+ const relevantEnd = trialEnd ?? currentPeriodEnd;
3186
+ const expired = isExpired(relevantEnd, nowMs);
3187
+ return expired === true ? "trial_expired" : "active";
3188
+ }
3189
+ case "active": {
3190
+ const expired = isExpired(currentPeriodEnd, nowMs);
3191
+ return expired === true ? "expired" : "active";
3192
+ }
3193
+ case "past_due": {
3194
+ const baselineIso = pastDueSince ?? currentPeriodEnd;
3195
+ if (!baselineIso) return "blocked";
3196
+ const baselineMs = Date.parse(baselineIso);
3197
+ if (Number.isNaN(baselineMs)) return "blocked";
3198
+ const graceEndMs = baselineMs + PAST_DUE_GRACE_PERIOD_DAYS * MS_PER_DAY;
3199
+ return graceEndMs > nowMs ? "past_due_grace" : "blocked";
3200
+ }
3201
+ case "pending_invoice":
3202
+ return "active";
3203
+ case "paused":
3204
+ case "incomplete":
3205
+ return "active";
3206
+ case "canceled":
3207
+ return "canceled";
3208
+ case "unpaid":
3209
+ case "incomplete_expired":
3210
+ return "blocked";
3211
+ default: {
3212
+ return "blocked";
3213
+ }
3214
+ }
3215
+ }
3216
+ var ACCESS_GRANTING_STATUSES = [
3217
+ "active",
3218
+ "past_due_grace"
3219
+ ];
3220
+ var BLOCKING_STATUSES = [
3221
+ "blocked",
3222
+ "expired",
3223
+ "canceled",
3224
+ "trial_expired"
3225
+ ];
3165
3226
  function useSubscription() {
3166
3227
  const context = useFFIDContext();
3167
3228
  const client = useFFIDClient();
@@ -3179,12 +3240,23 @@ function useSubscription() {
3179
3240
  const isActive = subscription?.status === "active";
3180
3241
  const isTrialing = subscription?.status === "trialing";
3181
3242
  const isCanceled = subscription?.status === "canceled";
3182
- const isTrialExpired = react.useMemo(() => {
3183
- if (!isTrialing) return false;
3184
- const relevantEnd = subscription?.trialEnd ?? subscription?.currentPeriodEnd;
3185
- if (!relevantEnd) return false;
3186
- return new Date(relevantEnd).getTime() < Date.now();
3187
- }, [isTrialing, subscription?.trialEnd, subscription?.currentPeriodEnd]);
3243
+ const effectiveStatus = react.useMemo(() => {
3244
+ if (!subscription) return null;
3245
+ return computeEffectiveStatusFromSession({
3246
+ status: subscription.status,
3247
+ currentPeriodEnd: subscription.currentPeriodEnd,
3248
+ trialEnd: subscription.trialEnd,
3249
+ // TODO(別Issue): FFID backend の session API (`/api/auth/session`) に
3250
+ // `pastDueSince` (ISO timestamp) を追加し、ここで渡す。現状は undefined
3251
+ // のため `computeEffectiveStatusFromSession` 内で currentPeriodEnd を
3252
+ // fallback に使う (Stripe は period end 近辺で past_due にする)。
3253
+ // Canonical な verdict が必要な consumer は `/ext/check` を直接呼ぶ。
3254
+ pastDueSince: void 0
3255
+ });
3256
+ }, [subscription]);
3257
+ const isGrace = effectiveStatus === "past_due_grace";
3258
+ const isBlocked = effectiveStatus !== null && BLOCKING_STATUSES.includes(effectiveStatus);
3259
+ const isTrialExpired = effectiveStatus === "trial_expired";
3188
3260
  const hasPlan = react.useCallback(
3189
3261
  (plans) => {
3190
3262
  if (!planCode) return false;
@@ -3194,6 +3266,10 @@ function useSubscription() {
3194
3266
  [planCode]
3195
3267
  );
3196
3268
  const hasAccess = react.useCallback(() => {
3269
+ if (effectiveStatus === null) return false;
3270
+ return ACCESS_GRANTING_STATUSES.includes(effectiveStatus);
3271
+ }, [effectiveStatus]);
3272
+ const hasAccessLegacy = react.useCallback(() => {
3197
3273
  if (isTrialExpired) return false;
3198
3274
  return isActive || isTrialing;
3199
3275
  }, [isActive, isTrialing, isTrialExpired]);
@@ -3204,8 +3280,12 @@ function useSubscription() {
3204
3280
  isTrialing,
3205
3281
  isTrialExpired,
3206
3282
  isCanceled,
3283
+ effectiveStatus,
3284
+ isBlocked,
3285
+ isGrace,
3207
3286
  hasPlan,
3208
- hasAccess
3287
+ hasAccess,
3288
+ hasAccessLegacy
3209
3289
  };
3210
3290
  }
3211
3291
  function withSubscription(Component, options = {}) {
@@ -4441,6 +4521,7 @@ exports.FFIDUserMenu = FFIDUserMenu;
4441
4521
  exports.FFID_ANNOUNCEMENTS_ERROR_CODES = FFID_ANNOUNCEMENTS_ERROR_CODES;
4442
4522
  exports.FFID_INQUIRY_CATEGORIES = FFID_INQUIRY_CATEGORIES;
4443
4523
  exports.FFID_INQUIRY_CATEGORIES_SITE_2026 = FFID_INQUIRY_CATEGORIES_SITE_2026;
4524
+ exports.computeEffectiveStatusFromSession = computeEffectiveStatusFromSession;
4444
4525
  exports.createFFIDAnnouncementsClient = createFFIDAnnouncementsClient;
4445
4526
  exports.createFFIDClient = createFFIDClient;
4446
4527
  exports.createTokenStore = createTokenStore;
@@ -1,34 +1,34 @@
1
1
  'use strict';
2
2
 
3
- var chunkEK2W67BW_cjs = require('../chunk-EK2W67BW.cjs');
3
+ var chunkKNEZ5OUQ_cjs = require('../chunk-KNEZ5OUQ.cjs');
4
4
 
5
5
 
6
6
 
7
7
  Object.defineProperty(exports, "FFIDAnnouncementBadge", {
8
8
  enumerable: true,
9
- get: function () { return chunkEK2W67BW_cjs.FFIDAnnouncementBadge; }
9
+ get: function () { return chunkKNEZ5OUQ_cjs.FFIDAnnouncementBadge; }
10
10
  });
11
11
  Object.defineProperty(exports, "FFIDAnnouncementList", {
12
12
  enumerable: true,
13
- get: function () { return chunkEK2W67BW_cjs.FFIDAnnouncementList; }
13
+ get: function () { return chunkKNEZ5OUQ_cjs.FFIDAnnouncementList; }
14
14
  });
15
15
  Object.defineProperty(exports, "FFIDInquiryForm", {
16
16
  enumerable: true,
17
- get: function () { return chunkEK2W67BW_cjs.FFIDInquiryForm; }
17
+ get: function () { return chunkKNEZ5OUQ_cjs.FFIDInquiryForm; }
18
18
  });
19
19
  Object.defineProperty(exports, "FFIDLoginButton", {
20
20
  enumerable: true,
21
- get: function () { return chunkEK2W67BW_cjs.FFIDLoginButton; }
21
+ get: function () { return chunkKNEZ5OUQ_cjs.FFIDLoginButton; }
22
22
  });
23
23
  Object.defineProperty(exports, "FFIDOrganizationSwitcher", {
24
24
  enumerable: true,
25
- get: function () { return chunkEK2W67BW_cjs.FFIDOrganizationSwitcher; }
25
+ get: function () { return chunkKNEZ5OUQ_cjs.FFIDOrganizationSwitcher; }
26
26
  });
27
27
  Object.defineProperty(exports, "FFIDSubscriptionBadge", {
28
28
  enumerable: true,
29
- get: function () { return chunkEK2W67BW_cjs.FFIDSubscriptionBadge; }
29
+ get: function () { return chunkKNEZ5OUQ_cjs.FFIDSubscriptionBadge; }
30
30
  });
31
31
  Object.defineProperty(exports, "FFIDUserMenu", {
32
32
  enumerable: true,
33
- get: function () { return chunkEK2W67BW_cjs.FFIDUserMenu; }
33
+ get: function () { return chunkKNEZ5OUQ_cjs.FFIDUserMenu; }
34
34
  });
@@ -1,3 +1,3 @@
1
- export { K as FFIDAnnouncementBadge, ak as FFIDAnnouncementBadgeClassNames, al as FFIDAnnouncementBadgeProps, M as FFIDAnnouncementList, am as FFIDAnnouncementListClassNames, an as FFIDAnnouncementListProps, U as FFIDInquiryForm, V as FFIDInquiryFormCategoryItem, W as FFIDInquiryFormClassNames, X as FFIDInquiryFormOrganization, Y as FFIDInquiryFormPlaceholderContext, Z as FFIDInquiryFormPrefill, _ as FFIDInquiryFormProps, $ as FFIDInquiryFormSubmitData, a0 as FFIDInquiryFormSubmitResult, a2 as FFIDLoginButton, ao as FFIDLoginButtonProps, a8 as FFIDOrganizationSwitcher, ap as FFIDOrganizationSwitcherClassNames, aq as FFIDOrganizationSwitcherProps, ab as FFIDSubscriptionBadge, ar as FFIDSubscriptionBadgeClassNames, as as FFIDSubscriptionBadgeProps, ad as FFIDUserMenu, at as FFIDUserMenuClassNames, au as FFIDUserMenuProps } from '../index--S6rLHjr.cjs';
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, ak as FFIDAnnouncementBadgeClassNames, al as FFIDAnnouncementBadgeProps, M as FFIDAnnouncementList, am as FFIDAnnouncementListClassNames, an as FFIDAnnouncementListProps, U as FFIDInquiryForm, V as FFIDInquiryFormCategoryItem, W as FFIDInquiryFormClassNames, X as FFIDInquiryFormOrganization, Y as FFIDInquiryFormPlaceholderContext, Z as FFIDInquiryFormPrefill, _ as FFIDInquiryFormProps, $ as FFIDInquiryFormSubmitData, a0 as FFIDInquiryFormSubmitResult, a2 as FFIDLoginButton, ao as FFIDLoginButtonProps, a8 as FFIDOrganizationSwitcher, ap as FFIDOrganizationSwitcherClassNames, aq as FFIDOrganizationSwitcherProps, ab as FFIDSubscriptionBadge, ar as FFIDSubscriptionBadgeClassNames, as as FFIDSubscriptionBadgeProps, ad as FFIDUserMenu, at as FFIDUserMenuClassNames, au as FFIDUserMenuProps } from '../index--S6rLHjr.js';
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-IEYXT3LA.js';
1
+ export { FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDInquiryForm, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDSubscriptionBadge, FFIDUserMenu } from '../chunk-GCUVFSB2.js';