@feelflow/ffid-sdk 2.19.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
@@ -487,6 +487,88 @@ SDK の `package.json` で `react` / `react-dom` は `optional: true` に設定
487
487
  }
488
488
  ```
489
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
+
490
572
  ## 環境変数
491
573
 
492
574
  オプションで環境変数を使用してデフォルト設定を上書きできます:
@@ -805,7 +805,7 @@ function createProfileMethods(deps) {
805
805
  }
806
806
 
807
807
  // src/client/version-check.ts
808
- var SDK_VERSION = "2.19.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() {
@@ -807,7 +807,7 @@ function createProfileMethods(deps) {
807
807
  }
808
808
 
809
809
  // src/client/version-check.ts
810
- var SDK_VERSION = "2.19.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() {
@@ -1,34 +1,34 @@
1
1
  'use strict';
2
2
 
3
- var chunkBBXUZS4U_cjs = require('../chunk-BBXUZS4U.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 chunkBBXUZS4U_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 chunkBBXUZS4U_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 chunkBBXUZS4U_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 chunkBBXUZS4U_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 chunkBBXUZS4U_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 chunkBBXUZS4U_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 chunkBBXUZS4U_cjs.FFIDUserMenu; }
33
+ get: function () { return chunkKNEZ5OUQ_cjs.FFIDUserMenu; }
34
34
  });
@@ -1 +1 @@
1
- export { FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDInquiryForm, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDSubscriptionBadge, FFIDUserMenu } from '../chunk-SXYB5QM3.js';
1
+ export { FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDInquiryForm, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDSubscriptionBadge, FFIDUserMenu } from '../chunk-GCUVFSB2.js';