@feelflow/ffid-sdk 2.20.0 → 3.0.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.
Files changed (39) hide show
  1. package/README.md +37 -0
  2. package/dist/agency/index.cjs +3 -3
  3. package/dist/agency/index.d.cts +1 -1
  4. package/dist/agency/index.d.ts +1 -1
  5. package/dist/agency/index.js +2 -2
  6. package/dist/announcements/index.cjs +2 -2
  7. package/dist/announcements/index.js +1 -1
  8. package/dist/chunk-JEVK2XUM.js +5 -0
  9. package/dist/{chunk-KNEZ5OUQ.cjs → chunk-JNR4AKL5.cjs} +133 -3
  10. package/dist/{chunk-YUIITYBE.cjs → chunk-MDHKSVLP.cjs} +2 -0
  11. package/dist/{chunk-GCUVFSB2.js → chunk-XWI4BFKW.js} +133 -4
  12. package/dist/components/index.cjs +8 -8
  13. package/dist/components/index.d.cts +1 -1
  14. package/dist/components/index.d.ts +1 -1
  15. package/dist/components/index.js +1 -1
  16. package/dist/constants-D61jqRIO.d.cts +35 -0
  17. package/dist/constants-D61jqRIO.d.ts +35 -0
  18. package/dist/{ffid-client-Cjm_TKUc.d.cts → ffid-client-B26jbUp5.d.cts} +72 -0
  19. package/dist/{ffid-client-Cjm_TKUc.d.ts → ffid-client-B26jbUp5.d.ts} +72 -0
  20. package/dist/{index-0D2vYSLq.d.cts → index-C3zyNa4j.d.cts} +37 -1
  21. package/dist/{index-0D2vYSLq.d.ts → index-C3zyNa4j.d.ts} +37 -1
  22. package/dist/index.cjs +37 -31
  23. package/dist/index.d.cts +68 -4
  24. package/dist/index.d.ts +68 -4
  25. package/dist/index.js +4 -3
  26. package/dist/legal/index.cjs +3 -3
  27. package/dist/legal/index.d.cts +1 -1
  28. package/dist/legal/index.d.ts +1 -1
  29. package/dist/legal/index.js +2 -2
  30. package/dist/server/index.cjs +135 -5
  31. package/dist/server/index.d.cts +3 -3
  32. package/dist/server/index.d.ts +3 -3
  33. package/dist/server/index.js +130 -4
  34. package/dist/server/test/index.d.cts +1 -1
  35. package/dist/server/test/index.d.ts +1 -1
  36. package/package.json +1 -1
  37. package/dist/chunk-QBRM2RRC.js +0 -4
  38. package/dist/constants-DvTGHPZn.d.cts +0 -10
  39. package/dist/constants-DvTGHPZn.d.ts +0 -10
package/README.md CHANGED
@@ -351,6 +351,43 @@ await client.updateProfile({
351
351
 
352
352
  対応フィールド: `displayName` / `phone` / `companyName` / `department` / `jobTitle` / `preferences`。`timezone` / `locale` は application-level invariant(サーバー側 normalization が string 前提)のため null 非許容。クリアは不可 — キー未指定で現状維持、もしくは新しい有効な値を渡す。
353
353
 
354
+ ### getAnalyticsConfig() (#2347)
355
+
356
+ 外部サービスが自身に割り当てられた GA4 Measurement ID を取得するメソッド。
357
+
358
+ - エンドポイント: `GET /api/v1/ext/analytics/config?service=<code>`
359
+ - 必要 scope: `analytics:read`(Bearer / API Key 両方で enforce)
360
+ - レスポンス: `FFIDAnalyticsConfig` (`{ code, measurementId, displayName, isActive }`)
361
+ - **archived service** (`isActive: false`) でも 200 を返す — caller が次回 deploy で tracking 停止判断する間、in-flight events が 5xx を起こさないように measurementId は引き続き返す
362
+
363
+ ```ts
364
+ import { createFFIDClient } from '@feelflow/ffid-sdk/server'
365
+
366
+ const client = createFFIDClient({
367
+ serviceCode: 'flow-board-ai',
368
+ authMode: 'service-key',
369
+ serviceApiKey: process.env.FFID_SERVICE_API_KEY!,
370
+ })
371
+
372
+ const result = await client.getAnalyticsConfig('feel-agent-ai')
373
+ if (result.error) {
374
+ if (result.error.code === 'SERVICE_NOT_FOUND') {
375
+ // 該当 GA4 stream がまだ sync されていない / typo
376
+ } else {
377
+ console.error(`[FFID] analytics config fetch failed: ${result.error.code}`)
378
+ }
379
+ } else if (result.data.isActive) {
380
+ // GA4 タグを描画して events を送信
381
+ loadGA4Script(result.data.measurementId)
382
+ }
383
+ ```
384
+
385
+ エラーコード:
386
+ - `VALIDATION_ERROR` — `serviceCode` が空 / kebab-case 形式違反(SDK 側 pre-validate)
387
+ - `INSUFFICIENT_SCOPE` (403) — `analytics:read` scope なし
388
+ - `SERVICE_NOT_FOUND` (404) — DB に未登録の service code
389
+ - `INVALID_PARAM` (400) — server 側で形式違反を検出(pre-validate 通過後の boundary)
390
+
354
391
  ## 型定義
355
392
 
356
393
  ```typescript
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkYUIITYBE_cjs = require('../chunk-YUIITYBE.cjs');
3
+ var chunkMDHKSVLP_cjs = require('../chunk-MDHKSVLP.cjs');
4
4
 
5
5
  // src/agency/ffid-agency-client.ts
6
6
  var API_PREFIX = "/api/v1/agencies";
@@ -26,7 +26,7 @@ var consoleLogger = {
26
26
  error: (...args) => console.error(SDK_LOG_PREFIX, ...args)
27
27
  };
28
28
  function createFFIDAgencyClient(config = {}) {
29
- const baseUrl = config.apiBaseUrl ?? chunkYUIITYBE_cjs.DEFAULT_API_BASE_URL;
29
+ const baseUrl = config.apiBaseUrl ?? chunkMDHKSVLP_cjs.DEFAULT_API_BASE_URL;
30
30
  const logger = config.logger ?? (config.debug ? consoleLogger : noopLogger);
31
31
  function validateIds(...pairs) {
32
32
  for (const [value, name] of pairs) {
@@ -316,7 +316,7 @@ function createFFIDAgencyClient(config = {}) {
316
316
 
317
317
  Object.defineProperty(exports, "DEFAULT_API_BASE_URL", {
318
318
  enumerable: true,
319
- get: function () { return chunkYUIITYBE_cjs.DEFAULT_API_BASE_URL; }
319
+ get: function () { return chunkMDHKSVLP_cjs.DEFAULT_API_BASE_URL; }
320
320
  });
321
321
  exports.FFID_AGENCY_ERROR_CODES = FFID_AGENCY_ERROR_CODES;
322
322
  exports.createFFIDAgencyClient = createFFIDAgencyClient;
@@ -1,4 +1,4 @@
1
- export { D as DEFAULT_API_BASE_URL } from '../constants-DvTGHPZn.cjs';
1
+ export { D as DEFAULT_API_BASE_URL } from '../constants-D61jqRIO.cjs';
2
2
 
3
3
  /**
4
4
  * FFID Agency SDK Type Definitions
@@ -1,4 +1,4 @@
1
- export { D as DEFAULT_API_BASE_URL } from '../constants-DvTGHPZn.js';
1
+ export { D as DEFAULT_API_BASE_URL } from '../constants-D61jqRIO.js';
2
2
 
3
3
  /**
4
4
  * FFID Agency SDK Type Definitions
@@ -1,5 +1,5 @@
1
- import { DEFAULT_API_BASE_URL } from '../chunk-QBRM2RRC.js';
2
- export { DEFAULT_API_BASE_URL } from '../chunk-QBRM2RRC.js';
1
+ import { DEFAULT_API_BASE_URL } from '../chunk-JEVK2XUM.js';
2
+ export { DEFAULT_API_BASE_URL } from '../chunk-JEVK2XUM.js';
3
3
 
4
4
  // src/agency/ffid-agency-client.ts
5
5
  var API_PREFIX = "/api/v1/agencies";
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkYUIITYBE_cjs = require('../chunk-YUIITYBE.cjs');
3
+ var chunkMDHKSVLP_cjs = require('../chunk-MDHKSVLP.cjs');
4
4
 
5
5
  // src/announcements/ffid-announcements-client.ts
6
6
  var API_PATH = "/api/v1/announcements";
@@ -29,7 +29,7 @@ var consoleLogger = {
29
29
  error: (...args) => console.error(SDK_LOG_PREFIX, ...args)
30
30
  };
31
31
  function createFFIDAnnouncementsClient(config = {}) {
32
- const baseUrl = config.apiBaseUrl ?? chunkYUIITYBE_cjs.DEFAULT_API_BASE_URL;
32
+ const baseUrl = config.apiBaseUrl ?? chunkMDHKSVLP_cjs.DEFAULT_API_BASE_URL;
33
33
  const logger = config.logger ?? (config.debug ? consoleLogger : quietLogger);
34
34
  async function fetchApi(endpoint) {
35
35
  const url = `${baseUrl}${endpoint}`;
@@ -1,4 +1,4 @@
1
- import { DEFAULT_API_BASE_URL } from '../chunk-QBRM2RRC.js';
1
+ import { DEFAULT_API_BASE_URL } from '../chunk-JEVK2XUM.js';
2
2
 
3
3
  // src/announcements/ffid-announcements-client.ts
4
4
  var API_PATH = "/api/v1/announcements";
@@ -0,0 +1,5 @@
1
+ // src/constants.ts
2
+ var DEFAULT_API_BASE_URL = "https://id.feelflow.net";
3
+ var DEFAULT_OAUTH_SCOPES = "openid email profile subscription:read legal:read";
4
+
5
+ export { DEFAULT_API_BASE_URL, DEFAULT_OAUTH_SCOPES };
@@ -6,6 +6,7 @@ var jsxRuntime = require('react/jsx-runtime');
6
6
 
7
7
  // src/constants.ts
8
8
  var DEFAULT_API_BASE_URL = "https://id.feelflow.net";
9
+ var DEFAULT_OAUTH_SCOPES = "openid email profile subscription:read legal:read";
9
10
 
10
11
  // src/auth/token-store.ts
11
12
  var STORAGE_KEY = "ffid_tokens";
@@ -807,7 +808,7 @@ function createProfileMethods(deps) {
807
808
  }
808
809
 
809
810
  // src/client/version-check.ts
810
- var SDK_VERSION = "2.20.0";
811
+ var SDK_VERSION = "3.0.0";
811
812
  var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
812
813
  var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
813
814
  function sdkHeaders() {
@@ -1462,6 +1463,7 @@ function createRedirectMethods(deps) {
1462
1463
  baseUrl,
1463
1464
  clientId,
1464
1465
  serviceCode,
1466
+ scope,
1465
1467
  resolvedRedirectUri,
1466
1468
  logger
1467
1469
  } = deps;
@@ -1470,6 +1472,24 @@ function createRedirectMethods(deps) {
1470
1472
  logger.warn("SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093");
1471
1473
  return { success: false, error: "SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093" };
1472
1474
  }
1475
+ if (authMode === "service-key") {
1476
+ logger.error(
1477
+ "[FFID SDK] service-key \u30E2\u30FC\u30C9\u3067\u306F redirectToAuthorize \u306F\u547C\u3073\u51FA\u305B\u307E\u305B\u3093 \u2014 server-to-server \u7528\u9014\u306E\u305F\u3081 OAuth \u30D6\u30E9\u30A6\u30B6 flow \u3092\u6301\u3061\u307E\u305B\u3093"
1478
+ );
1479
+ return {
1480
+ success: false,
1481
+ error: "service-key \u30E2\u30FC\u30C9\u3067\u306F OAuth \u8A8D\u53EF\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u306F\u5229\u7528\u3067\u304D\u307E\u305B\u3093"
1482
+ };
1483
+ }
1484
+ if (!scope || !scope.trim()) {
1485
+ logger.error(
1486
+ "[FFID SDK] scope \u304C\u672A\u8A2D\u5B9A\u306E\u305F\u3081 /oauth/authorize \u3092\u547C\u3073\u51FA\u305B\u307E\u305B\u3093 (#2674) \u2014 FFIDConfig.scope \u306B DEFAULT_OAUTH_SCOPES \u307E\u305F\u306F\u660E\u793A\u7684\u306A scope \u3092\u6E21\u3057\u3066\u304F\u3060\u3055\u3044"
1487
+ );
1488
+ return {
1489
+ success: false,
1490
+ error: "OAuth scope \u304C\u672A\u8A2D\u5B9A\u306E\u305F\u3081\u8A8D\u53EF\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3092\u5B9F\u884C\u3067\u304D\u307E\u305B\u3093"
1491
+ };
1492
+ }
1473
1493
  const authorizeKey = buildAuthorizeKey(baseUrl, clientId, options?.organizationId);
1474
1494
  const now = Date.now();
1475
1495
  const recentCount = getRecentRedirectCount(authorizeKey, now, logger);
@@ -1502,6 +1522,7 @@ function createRedirectMethods(deps) {
1502
1522
  const params = new URLSearchParams({
1503
1523
  response_type: "code",
1504
1524
  client_id: clientId,
1525
+ scope,
1505
1526
  redirect_uri: redirectUri,
1506
1527
  state,
1507
1528
  code_challenge: challenge,
@@ -1840,6 +1861,63 @@ function createOtpMethods(deps) {
1840
1861
  };
1841
1862
  }
1842
1863
 
1864
+ // src/client/analytics-methods.ts
1865
+ var EXT_ANALYTICS_CONFIG_ENDPOINT = "/api/v1/ext/analytics/config";
1866
+ function resolveAuthOverride2(options, createError) {
1867
+ if (!options || options.accessToken === void 0) {
1868
+ return {};
1869
+ }
1870
+ const token = options.accessToken;
1871
+ if (typeof token !== "string" || token.trim() === "") {
1872
+ return {
1873
+ error: createError(
1874
+ "VALIDATION_ERROR",
1875
+ "accessToken \u3092\u6307\u5B9A\u3059\u308B\u5834\u5408\u3001\u7A7A\u6587\u5B57\u5217\u3084\u7A7A\u767D\u306E\u307F\u306E\u5024\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093"
1876
+ )
1877
+ };
1878
+ }
1879
+ return { override: { accessToken: token } };
1880
+ }
1881
+ var ANALYTICS_SERVICE_CODE_PATTERN = /^[a-z0-9]+(-[a-z0-9]+)*$/;
1882
+ function validateServiceCode(serviceCode, createError) {
1883
+ if (typeof serviceCode !== "string" || serviceCode.trim() === "") {
1884
+ return createError(
1885
+ "VALIDATION_ERROR",
1886
+ "serviceCode \u306F\u5FC5\u9808\u306E kebab-case \u6587\u5B57\u5217\u3067\u3059"
1887
+ );
1888
+ }
1889
+ if (!ANALYTICS_SERVICE_CODE_PATTERN.test(serviceCode)) {
1890
+ return createError(
1891
+ "VALIDATION_ERROR",
1892
+ "serviceCode \u306F kebab-case \u5F62\u5F0F (\u82F1\u5C0F\u6587\u5B57\u30FB\u6570\u5B57\u30FB\u30CF\u30A4\u30D5\u30F3) \u3067\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044"
1893
+ );
1894
+ }
1895
+ return null;
1896
+ }
1897
+ function createAnalyticsMethods(deps) {
1898
+ const { fetchWithAuth, createError } = deps;
1899
+ async function getAnalyticsConfig(serviceCode, options) {
1900
+ const validationError = validateServiceCode(serviceCode, createError);
1901
+ if (validationError) {
1902
+ return { error: validationError };
1903
+ }
1904
+ const { override, error: overrideError } = resolveAuthOverride2(
1905
+ options,
1906
+ createError
1907
+ );
1908
+ if (overrideError) {
1909
+ return { error: overrideError };
1910
+ }
1911
+ const endpoint = `${EXT_ANALYTICS_CONFIG_ENDPOINT}?service=${encodeURIComponent(serviceCode)}`;
1912
+ return fetchWithAuth(
1913
+ endpoint,
1914
+ { method: "GET" },
1915
+ override
1916
+ );
1917
+ }
1918
+ return { getAnalyticsConfig };
1919
+ }
1920
+
1843
1921
  // src/client/contract-wizard-methods.ts
1844
1922
  var CONTRACT_WIZARD_PATH = "/contract-wizard";
1845
1923
  function buildWizardUrl(baseUrl, flow, params) {
@@ -1927,6 +2005,7 @@ function createContractWizardMethods(deps) {
1927
2005
  var EXT_SUBSCRIBE_ENDPOINT2 = "/api/v1/ext/newsletter/subscribe";
1928
2006
  var CONFIRM_ENDPOINT = "/api/newsletter/confirm";
1929
2007
  var UNSUBSCRIBE_ENDPOINT = "/api/newsletter/unsubscribe";
2008
+ var DISPATCH_ENDPOINT = "/api/v1/newsletter/dispatch";
1930
2009
  function trimOrEmpty(s) {
1931
2010
  return typeof s === "string" ? s.trim() : "";
1932
2011
  }
@@ -2032,7 +2111,36 @@ function createNewsletterMethods(deps) {
2032
2111
  }
2033
2112
  );
2034
2113
  }
2035
- return { subscribe, confirm, unsubscribe };
2114
+ async function dispatch(params) {
2115
+ const subject = trimOrEmpty(params.subject);
2116
+ if (!subject) {
2117
+ return { error: createError("VALIDATION_ERROR", "subject \u306F\u5FC5\u9808\u3067\u3059") };
2118
+ }
2119
+ if (!params.body || typeof params.body !== "object") {
2120
+ return { error: createError("VALIDATION_ERROR", "body \u306F\u5FC5\u9808\u3067\u3059") };
2121
+ }
2122
+ const bodySource = params.body;
2123
+ const hasHtmlBody = typeof bodySource.htmlBody === "string" && bodySource.htmlBody.length > 0;
2124
+ const hasTemplateId = typeof bodySource.templateId === "string" && bodySource.templateId.length > 0;
2125
+ if (hasHtmlBody === hasTemplateId) {
2126
+ return {
2127
+ error: createError(
2128
+ "BODY_SOURCE_REQUIRED",
2129
+ "body \u306F htmlBody \u307E\u305F\u306F templateId \u306E\u3044\u305A\u308C\u304B\u4E00\u65B9\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044"
2130
+ )
2131
+ };
2132
+ }
2133
+ return fetchWithAuth(DISPATCH_ENDPOINT, {
2134
+ method: "POST",
2135
+ body: JSON.stringify({
2136
+ subject,
2137
+ articleUrl: params.articleUrl,
2138
+ body: params.body,
2139
+ segment: params.segment
2140
+ })
2141
+ });
2142
+ }
2143
+ return { subscribe, confirm, unsubscribe, dispatch };
2036
2144
  }
2037
2145
 
2038
2146
  // src/inquiry/ffid-inquiry-client.ts
@@ -2134,8 +2242,21 @@ function createFFIDClient(config) {
2134
2242
  if (!config.serviceCode || !config.serviceCode.trim()) {
2135
2243
  throw new Error("FFID Client: serviceCode \u304C\u672A\u8A2D\u5B9A\u3067\u3059");
2136
2244
  }
2245
+ const scope = config.scope?.trim() ?? "";
2137
2246
  const baseUrl = config.apiBaseUrl ?? DEFAULT_API_BASE_URL;
2138
2247
  const authMode = config.authMode ?? "cookie";
2248
+ const VALID_AUTH_MODES = ["cookie", "token", "service-key"];
2249
+ if (!VALID_AUTH_MODES.includes(authMode)) {
2250
+ throw new Error(
2251
+ `FFID Client: authMode \u304C\u4E0D\u6B63\u3067\u3059 (\u53D7\u4FE1: ${JSON.stringify(authMode)}, \u6709\u52B9\u5024: ${VALID_AUTH_MODES.join(" | ")})`
2252
+ );
2253
+ }
2254
+ if (authMode !== "service-key" && !scope) {
2255
+ const received = config.scope === void 0 ? "undefined" : JSON.stringify(config.scope);
2256
+ throw new Error(
2257
+ `FFID Client: scope \u304C\u672A\u8A2D\u5B9A\u3067\u3059 (\u53D7\u4FE1: ${received})\u3002\`DEFAULT_OAUTH_SCOPES\` \u3092 import \u3059\u308B\u304B\u3001\u660E\u793A\u7684\u306B scope \u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044 (#2674)`
2258
+ );
2259
+ }
2139
2260
  const clientId = config.clientId ?? config.serviceCode;
2140
2261
  const rawRedirectUri = config.redirectUri ?? null;
2141
2262
  const serviceApiKey = config.serviceApiKey?.trim();
@@ -2306,6 +2427,7 @@ function createFFIDClient(config) {
2306
2427
  baseUrl,
2307
2428
  clientId,
2308
2429
  serviceCode: config.serviceCode,
2430
+ scope,
2309
2431
  resolvedRedirectUri,
2310
2432
  logger
2311
2433
  });
@@ -2362,6 +2484,10 @@ function createFFIDClient(config) {
2362
2484
  fetchWithAuth,
2363
2485
  createError
2364
2486
  });
2487
+ const { getAnalyticsConfig } = createAnalyticsMethods({
2488
+ fetchWithAuth,
2489
+ createError
2490
+ });
2365
2491
  const {
2366
2492
  requestPasswordReset,
2367
2493
  verifyPasswordResetToken,
@@ -2435,6 +2561,7 @@ function createFFIDClient(config) {
2435
2561
  removeMember,
2436
2562
  getProfile,
2437
2563
  updateProfile,
2564
+ getAnalyticsConfig,
2438
2565
  createCheckoutSession,
2439
2566
  createPortalSession,
2440
2567
  listPlans,
@@ -2485,6 +2612,7 @@ var FFIDClientContext = react.createContext(null);
2485
2612
  function FFIDProvider({
2486
2613
  children,
2487
2614
  serviceCode,
2615
+ scope,
2488
2616
  apiBaseUrl,
2489
2617
  debug = false,
2490
2618
  logger,
@@ -2511,13 +2639,14 @@ function FFIDProvider({
2511
2639
  const client = react.useMemo(
2512
2640
  () => createFFIDClient({
2513
2641
  serviceCode,
2642
+ scope,
2514
2643
  apiBaseUrl,
2515
2644
  debug,
2516
2645
  logger,
2517
2646
  authMode,
2518
2647
  clientId
2519
2648
  }),
2520
- [serviceCode, apiBaseUrl, debug, logger, authMode, clientId]
2649
+ [serviceCode, scope, apiBaseUrl, debug, logger, authMode, clientId]
2521
2650
  );
2522
2651
  const refresh = react.useCallback(async () => {
2523
2652
  client.logger.debug("Refreshing session...");
@@ -4509,6 +4638,7 @@ function FFIDInquiryForm({
4509
4638
  }
4510
4639
 
4511
4640
  exports.DEFAULT_API_BASE_URL = DEFAULT_API_BASE_URL;
4641
+ exports.DEFAULT_OAUTH_SCOPES = DEFAULT_OAUTH_SCOPES;
4512
4642
  exports.FFIDAnnouncementBadge = FFIDAnnouncementBadge;
4513
4643
  exports.FFIDAnnouncementList = FFIDAnnouncementList;
4514
4644
  exports.FFIDInquiryForm = FFIDInquiryForm;
@@ -2,5 +2,7 @@
2
2
 
3
3
  // src/constants.ts
4
4
  var DEFAULT_API_BASE_URL = "https://id.feelflow.net";
5
+ var DEFAULT_OAUTH_SCOPES = "openid email profile subscription:read legal:read";
5
6
 
6
7
  exports.DEFAULT_API_BASE_URL = DEFAULT_API_BASE_URL;
8
+ exports.DEFAULT_OAUTH_SCOPES = DEFAULT_OAUTH_SCOPES;
@@ -4,6 +4,7 @@ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
4
 
5
5
  // src/constants.ts
6
6
  var DEFAULT_API_BASE_URL = "https://id.feelflow.net";
7
+ var DEFAULT_OAUTH_SCOPES = "openid email profile subscription:read legal:read";
7
8
 
8
9
  // src/auth/token-store.ts
9
10
  var STORAGE_KEY = "ffid_tokens";
@@ -805,7 +806,7 @@ function createProfileMethods(deps) {
805
806
  }
806
807
 
807
808
  // src/client/version-check.ts
808
- var SDK_VERSION = "2.20.0";
809
+ var SDK_VERSION = "3.0.0";
809
810
  var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
810
811
  var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
811
812
  function sdkHeaders() {
@@ -1460,6 +1461,7 @@ function createRedirectMethods(deps) {
1460
1461
  baseUrl,
1461
1462
  clientId,
1462
1463
  serviceCode,
1464
+ scope,
1463
1465
  resolvedRedirectUri,
1464
1466
  logger
1465
1467
  } = deps;
@@ -1468,6 +1470,24 @@ function createRedirectMethods(deps) {
1468
1470
  logger.warn("SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093");
1469
1471
  return { success: false, error: "SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093" };
1470
1472
  }
1473
+ if (authMode === "service-key") {
1474
+ logger.error(
1475
+ "[FFID SDK] service-key \u30E2\u30FC\u30C9\u3067\u306F redirectToAuthorize \u306F\u547C\u3073\u51FA\u305B\u307E\u305B\u3093 \u2014 server-to-server \u7528\u9014\u306E\u305F\u3081 OAuth \u30D6\u30E9\u30A6\u30B6 flow \u3092\u6301\u3061\u307E\u305B\u3093"
1476
+ );
1477
+ return {
1478
+ success: false,
1479
+ error: "service-key \u30E2\u30FC\u30C9\u3067\u306F OAuth \u8A8D\u53EF\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u306F\u5229\u7528\u3067\u304D\u307E\u305B\u3093"
1480
+ };
1481
+ }
1482
+ if (!scope || !scope.trim()) {
1483
+ logger.error(
1484
+ "[FFID SDK] scope \u304C\u672A\u8A2D\u5B9A\u306E\u305F\u3081 /oauth/authorize \u3092\u547C\u3073\u51FA\u305B\u307E\u305B\u3093 (#2674) \u2014 FFIDConfig.scope \u306B DEFAULT_OAUTH_SCOPES \u307E\u305F\u306F\u660E\u793A\u7684\u306A scope \u3092\u6E21\u3057\u3066\u304F\u3060\u3055\u3044"
1485
+ );
1486
+ return {
1487
+ success: false,
1488
+ error: "OAuth scope \u304C\u672A\u8A2D\u5B9A\u306E\u305F\u3081\u8A8D\u53EF\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3092\u5B9F\u884C\u3067\u304D\u307E\u305B\u3093"
1489
+ };
1490
+ }
1471
1491
  const authorizeKey = buildAuthorizeKey(baseUrl, clientId, options?.organizationId);
1472
1492
  const now = Date.now();
1473
1493
  const recentCount = getRecentRedirectCount(authorizeKey, now, logger);
@@ -1500,6 +1520,7 @@ function createRedirectMethods(deps) {
1500
1520
  const params = new URLSearchParams({
1501
1521
  response_type: "code",
1502
1522
  client_id: clientId,
1523
+ scope,
1503
1524
  redirect_uri: redirectUri,
1504
1525
  state,
1505
1526
  code_challenge: challenge,
@@ -1838,6 +1859,63 @@ function createOtpMethods(deps) {
1838
1859
  };
1839
1860
  }
1840
1861
 
1862
+ // src/client/analytics-methods.ts
1863
+ var EXT_ANALYTICS_CONFIG_ENDPOINT = "/api/v1/ext/analytics/config";
1864
+ function resolveAuthOverride2(options, createError) {
1865
+ if (!options || options.accessToken === void 0) {
1866
+ return {};
1867
+ }
1868
+ const token = options.accessToken;
1869
+ if (typeof token !== "string" || token.trim() === "") {
1870
+ return {
1871
+ error: createError(
1872
+ "VALIDATION_ERROR",
1873
+ "accessToken \u3092\u6307\u5B9A\u3059\u308B\u5834\u5408\u3001\u7A7A\u6587\u5B57\u5217\u3084\u7A7A\u767D\u306E\u307F\u306E\u5024\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093"
1874
+ )
1875
+ };
1876
+ }
1877
+ return { override: { accessToken: token } };
1878
+ }
1879
+ var ANALYTICS_SERVICE_CODE_PATTERN = /^[a-z0-9]+(-[a-z0-9]+)*$/;
1880
+ function validateServiceCode(serviceCode, createError) {
1881
+ if (typeof serviceCode !== "string" || serviceCode.trim() === "") {
1882
+ return createError(
1883
+ "VALIDATION_ERROR",
1884
+ "serviceCode \u306F\u5FC5\u9808\u306E kebab-case \u6587\u5B57\u5217\u3067\u3059"
1885
+ );
1886
+ }
1887
+ if (!ANALYTICS_SERVICE_CODE_PATTERN.test(serviceCode)) {
1888
+ return createError(
1889
+ "VALIDATION_ERROR",
1890
+ "serviceCode \u306F kebab-case \u5F62\u5F0F (\u82F1\u5C0F\u6587\u5B57\u30FB\u6570\u5B57\u30FB\u30CF\u30A4\u30D5\u30F3) \u3067\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044"
1891
+ );
1892
+ }
1893
+ return null;
1894
+ }
1895
+ function createAnalyticsMethods(deps) {
1896
+ const { fetchWithAuth, createError } = deps;
1897
+ async function getAnalyticsConfig(serviceCode, options) {
1898
+ const validationError = validateServiceCode(serviceCode, createError);
1899
+ if (validationError) {
1900
+ return { error: validationError };
1901
+ }
1902
+ const { override, error: overrideError } = resolveAuthOverride2(
1903
+ options,
1904
+ createError
1905
+ );
1906
+ if (overrideError) {
1907
+ return { error: overrideError };
1908
+ }
1909
+ const endpoint = `${EXT_ANALYTICS_CONFIG_ENDPOINT}?service=${encodeURIComponent(serviceCode)}`;
1910
+ return fetchWithAuth(
1911
+ endpoint,
1912
+ { method: "GET" },
1913
+ override
1914
+ );
1915
+ }
1916
+ return { getAnalyticsConfig };
1917
+ }
1918
+
1841
1919
  // src/client/contract-wizard-methods.ts
1842
1920
  var CONTRACT_WIZARD_PATH = "/contract-wizard";
1843
1921
  function buildWizardUrl(baseUrl, flow, params) {
@@ -1925,6 +2003,7 @@ function createContractWizardMethods(deps) {
1925
2003
  var EXT_SUBSCRIBE_ENDPOINT2 = "/api/v1/ext/newsletter/subscribe";
1926
2004
  var CONFIRM_ENDPOINT = "/api/newsletter/confirm";
1927
2005
  var UNSUBSCRIBE_ENDPOINT = "/api/newsletter/unsubscribe";
2006
+ var DISPATCH_ENDPOINT = "/api/v1/newsletter/dispatch";
1928
2007
  function trimOrEmpty(s) {
1929
2008
  return typeof s === "string" ? s.trim() : "";
1930
2009
  }
@@ -2030,7 +2109,36 @@ function createNewsletterMethods(deps) {
2030
2109
  }
2031
2110
  );
2032
2111
  }
2033
- return { subscribe, confirm, unsubscribe };
2112
+ async function dispatch(params) {
2113
+ const subject = trimOrEmpty(params.subject);
2114
+ if (!subject) {
2115
+ return { error: createError("VALIDATION_ERROR", "subject \u306F\u5FC5\u9808\u3067\u3059") };
2116
+ }
2117
+ if (!params.body || typeof params.body !== "object") {
2118
+ return { error: createError("VALIDATION_ERROR", "body \u306F\u5FC5\u9808\u3067\u3059") };
2119
+ }
2120
+ const bodySource = params.body;
2121
+ const hasHtmlBody = typeof bodySource.htmlBody === "string" && bodySource.htmlBody.length > 0;
2122
+ const hasTemplateId = typeof bodySource.templateId === "string" && bodySource.templateId.length > 0;
2123
+ if (hasHtmlBody === hasTemplateId) {
2124
+ return {
2125
+ error: createError(
2126
+ "BODY_SOURCE_REQUIRED",
2127
+ "body \u306F htmlBody \u307E\u305F\u306F templateId \u306E\u3044\u305A\u308C\u304B\u4E00\u65B9\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044"
2128
+ )
2129
+ };
2130
+ }
2131
+ return fetchWithAuth(DISPATCH_ENDPOINT, {
2132
+ method: "POST",
2133
+ body: JSON.stringify({
2134
+ subject,
2135
+ articleUrl: params.articleUrl,
2136
+ body: params.body,
2137
+ segment: params.segment
2138
+ })
2139
+ });
2140
+ }
2141
+ return { subscribe, confirm, unsubscribe, dispatch };
2034
2142
  }
2035
2143
 
2036
2144
  // src/inquiry/ffid-inquiry-client.ts
@@ -2132,8 +2240,21 @@ function createFFIDClient(config) {
2132
2240
  if (!config.serviceCode || !config.serviceCode.trim()) {
2133
2241
  throw new Error("FFID Client: serviceCode \u304C\u672A\u8A2D\u5B9A\u3067\u3059");
2134
2242
  }
2243
+ const scope = config.scope?.trim() ?? "";
2135
2244
  const baseUrl = config.apiBaseUrl ?? DEFAULT_API_BASE_URL;
2136
2245
  const authMode = config.authMode ?? "cookie";
2246
+ const VALID_AUTH_MODES = ["cookie", "token", "service-key"];
2247
+ if (!VALID_AUTH_MODES.includes(authMode)) {
2248
+ throw new Error(
2249
+ `FFID Client: authMode \u304C\u4E0D\u6B63\u3067\u3059 (\u53D7\u4FE1: ${JSON.stringify(authMode)}, \u6709\u52B9\u5024: ${VALID_AUTH_MODES.join(" | ")})`
2250
+ );
2251
+ }
2252
+ if (authMode !== "service-key" && !scope) {
2253
+ const received = config.scope === void 0 ? "undefined" : JSON.stringify(config.scope);
2254
+ throw new Error(
2255
+ `FFID Client: scope \u304C\u672A\u8A2D\u5B9A\u3067\u3059 (\u53D7\u4FE1: ${received})\u3002\`DEFAULT_OAUTH_SCOPES\` \u3092 import \u3059\u308B\u304B\u3001\u660E\u793A\u7684\u306B scope \u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044 (#2674)`
2256
+ );
2257
+ }
2137
2258
  const clientId = config.clientId ?? config.serviceCode;
2138
2259
  const rawRedirectUri = config.redirectUri ?? null;
2139
2260
  const serviceApiKey = config.serviceApiKey?.trim();
@@ -2304,6 +2425,7 @@ function createFFIDClient(config) {
2304
2425
  baseUrl,
2305
2426
  clientId,
2306
2427
  serviceCode: config.serviceCode,
2428
+ scope,
2307
2429
  resolvedRedirectUri,
2308
2430
  logger
2309
2431
  });
@@ -2360,6 +2482,10 @@ function createFFIDClient(config) {
2360
2482
  fetchWithAuth,
2361
2483
  createError
2362
2484
  });
2485
+ const { getAnalyticsConfig } = createAnalyticsMethods({
2486
+ fetchWithAuth,
2487
+ createError
2488
+ });
2363
2489
  const {
2364
2490
  requestPasswordReset,
2365
2491
  verifyPasswordResetToken,
@@ -2433,6 +2559,7 @@ function createFFIDClient(config) {
2433
2559
  removeMember,
2434
2560
  getProfile,
2435
2561
  updateProfile,
2562
+ getAnalyticsConfig,
2436
2563
  createCheckoutSession,
2437
2564
  createPortalSession,
2438
2565
  listPlans,
@@ -2483,6 +2610,7 @@ var FFIDClientContext = createContext(null);
2483
2610
  function FFIDProvider({
2484
2611
  children,
2485
2612
  serviceCode,
2613
+ scope,
2486
2614
  apiBaseUrl,
2487
2615
  debug = false,
2488
2616
  logger,
@@ -2509,13 +2637,14 @@ function FFIDProvider({
2509
2637
  const client = useMemo(
2510
2638
  () => createFFIDClient({
2511
2639
  serviceCode,
2640
+ scope,
2512
2641
  apiBaseUrl,
2513
2642
  debug,
2514
2643
  logger,
2515
2644
  authMode,
2516
2645
  clientId
2517
2646
  }),
2518
- [serviceCode, apiBaseUrl, debug, logger, authMode, clientId]
2647
+ [serviceCode, scope, apiBaseUrl, debug, logger, authMode, clientId]
2519
2648
  );
2520
2649
  const refresh = useCallback(async () => {
2521
2650
  client.logger.debug("Refreshing session...");
@@ -4506,4 +4635,4 @@ function FFIDInquiryForm({
4506
4635
  );
4507
4636
  }
4508
4637
 
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 };
4638
+ export { 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, isFFIDInquiryCategorySite2026, normalizeRedirectUri, retrieveCodeVerifier, storeCodeVerifier, useFFID, useFFIDAnnouncements, useFFIDContext, useSubscription, withSubscription };
@@ -1,34 +1,34 @@
1
1
  'use strict';
2
2
 
3
- var chunkKNEZ5OUQ_cjs = require('../chunk-KNEZ5OUQ.cjs');
3
+ var chunkJNR4AKL5_cjs = require('../chunk-JNR4AKL5.cjs');
4
4
 
5
5
 
6
6
 
7
7
  Object.defineProperty(exports, "FFIDAnnouncementBadge", {
8
8
  enumerable: true,
9
- get: function () { return chunkKNEZ5OUQ_cjs.FFIDAnnouncementBadge; }
9
+ get: function () { return chunkJNR4AKL5_cjs.FFIDAnnouncementBadge; }
10
10
  });
11
11
  Object.defineProperty(exports, "FFIDAnnouncementList", {
12
12
  enumerable: true,
13
- get: function () { return chunkKNEZ5OUQ_cjs.FFIDAnnouncementList; }
13
+ get: function () { return chunkJNR4AKL5_cjs.FFIDAnnouncementList; }
14
14
  });
15
15
  Object.defineProperty(exports, "FFIDInquiryForm", {
16
16
  enumerable: true,
17
- get: function () { return chunkKNEZ5OUQ_cjs.FFIDInquiryForm; }
17
+ get: function () { return chunkJNR4AKL5_cjs.FFIDInquiryForm; }
18
18
  });
19
19
  Object.defineProperty(exports, "FFIDLoginButton", {
20
20
  enumerable: true,
21
- get: function () { return chunkKNEZ5OUQ_cjs.FFIDLoginButton; }
21
+ get: function () { return chunkJNR4AKL5_cjs.FFIDLoginButton; }
22
22
  });
23
23
  Object.defineProperty(exports, "FFIDOrganizationSwitcher", {
24
24
  enumerable: true,
25
- get: function () { return chunkKNEZ5OUQ_cjs.FFIDOrganizationSwitcher; }
25
+ get: function () { return chunkJNR4AKL5_cjs.FFIDOrganizationSwitcher; }
26
26
  });
27
27
  Object.defineProperty(exports, "FFIDSubscriptionBadge", {
28
28
  enumerable: true,
29
- get: function () { return chunkKNEZ5OUQ_cjs.FFIDSubscriptionBadge; }
29
+ get: function () { return chunkJNR4AKL5_cjs.FFIDSubscriptionBadge; }
30
30
  });
31
31
  Object.defineProperty(exports, "FFIDUserMenu", {
32
32
  enumerable: true,
33
- get: function () { return chunkKNEZ5OUQ_cjs.FFIDUserMenu; }
33
+ get: function () { return chunkJNR4AKL5_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 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';
1
+ export { N as FFIDAnnouncementBadge, am as FFIDAnnouncementBadgeClassNames, an as FFIDAnnouncementBadgeProps, O as FFIDAnnouncementList, ao as FFIDAnnouncementListClassNames, ap as FFIDAnnouncementListProps, W as FFIDInquiryForm, X as FFIDInquiryFormCategoryItem, Y as FFIDInquiryFormClassNames, Z as FFIDInquiryFormOrganization, _ as FFIDInquiryFormPlaceholderContext, $ as FFIDInquiryFormPrefill, a0 as FFIDInquiryFormProps, a1 as FFIDInquiryFormSubmitData, a2 as FFIDInquiryFormSubmitResult, a4 as FFIDLoginButton, aq as FFIDLoginButtonProps, aa as FFIDOrganizationSwitcher, ar as FFIDOrganizationSwitcherClassNames, as as FFIDOrganizationSwitcherProps, ad as FFIDSubscriptionBadge, at as FFIDSubscriptionBadgeClassNames, au as FFIDSubscriptionBadgeProps, af as FFIDUserMenu, av as FFIDUserMenuClassNames, aw as FFIDUserMenuProps } from '../index-C3zyNa4j.cjs';
2
2
  import 'react/jsx-runtime';
3
3
  import 'react';