@feelflow/ffid-sdk 7.0.0 → 7.2.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
@@ -397,6 +397,53 @@ await ffid.updateMemberRole({
397
397
  - `role` は `admin` / `member` / `viewer` のみ指定可能です。`owner` は追加・昇格 API では扱いません。
398
398
  - agency client と同じ呼び味が必要な場合は `ffid.addMember(organizationId, { email, role })` も使えます。
399
399
 
400
+ ### Organization profile / public logo wall
401
+
402
+ hub 等の外部サービスが企業プロフィールを登録・照会し、FeelFlow 全サービス共通のロゴウォールを表示するためのメソッド(#5485 / #5547)。
403
+
404
+ - **`getOrganizationProfile` / `upsertOrganizationProfile` / `uploadOrganizationLogo`** は **`authMode: 'service-key'` 専用**(scope: `organization:read` / `organization:write`)。`token` / `cookie` モードで呼ぶとサーバーへ往復せず `VALIDATION_ERROR`。
405
+ - **`getPublicLogoWall`** は認証不要。service-key を送らない。エンドポイントは `GET /api/v1/organizations/logo-wall`(公開 API なので SDK を使わず直接 GET してもよいが、型付きエラーが欲しければ SDK 経由を推奨)。
406
+ - `upsert` の `listingConsent.consented: true` では `consentVersion` と `consentedByUserId` が必須。同意操作者が対象 org の active メンバーでないとサーバーが `CONSENTED_BY_NOT_MEMBER` を返す。
407
+
408
+ ```ts
409
+ import {
410
+ createFFIDClient,
411
+ FFID_ORGANIZATION_PROFILE_ERROR_CODES,
412
+ } from '@feelflow/ffid-sdk/server'
413
+
414
+ const ffid = createFFIDClient({
415
+ serviceCode: 'feel-agent',
416
+ scope: '',
417
+ authMode: 'service-key',
418
+ serviceApiKey: process.env.FFID_SERVICE_API_KEY!,
419
+ })
420
+
421
+ const current = await ffid.getOrganizationProfile(organizationId)
422
+ if (current.error) throw new Error(current.error.message)
423
+
424
+ const upsert = await ffid.upsertOrganizationProfile(organizationId, {
425
+ displayName: '株式会社サンプル',
426
+ industry: 'it-software',
427
+ companySize: '11-50',
428
+ websiteUrl: 'https://example.co.jp',
429
+ listingConsent: {
430
+ consented: true,
431
+ consentVersion: 'v1.0',
432
+ consentedByUserId: userId,
433
+ },
434
+ })
435
+ if (upsert.error?.code === FFID_ORGANIZATION_PROFILE_ERROR_CODES.CONSENTED_BY_NOT_MEMBER) {
436
+ throw new Error(upsert.error.message)
437
+ }
438
+
439
+ await ffid.uploadOrganizationLogo(organizationId, logoFile)
440
+
441
+ const wall = await ffid.getPublicLogoWall()
442
+ // wall.data.entries: { displayName, logoUrl, websiteUrl }[]
443
+ ```
444
+
445
+ 位置引数と params オブジェクトの両方を受け付ける(`addMember` と同じ)。
446
+
400
447
  ### Provisioning(users / organizations)
401
448
 
402
449
  外部サービスからの一括移行向けに、**service-key 認証**でユーザー・組織を冪等にプロビジョニングするメソッド。REST を直叩きせず `createFFIDClient` 経由で呼べる(SDK-first、#3790 / #4127)。
@@ -226,13 +226,18 @@ function mapUserinfoSubscriptionToSession(userinfo, serviceCode) {
226
226
  ];
227
227
  }
228
228
  var JWKS_ENDPOINT = "/.well-known/jwks.json";
229
- var JWT_ISSUER = "https://id.feelflow.net";
229
+ var LEGACY_JWT_ISSUER = "https://id.feelflow.net";
230
230
  var JWT_ALGORITHM = "ES256";
231
231
  var JWT_CLOCK_TOLERANCE_SECONDS = 30;
232
+ function resolveAcceptedIssuers(baseUrl) {
233
+ const deployedOrigin = new URL(baseUrl).origin;
234
+ return deployedOrigin === LEGACY_JWT_ISSUER ? [LEGACY_JWT_ISSUER] : [deployedOrigin, LEGACY_JWT_ISSUER];
235
+ }
232
236
  function createJwtVerifier(deps) {
233
237
  const { baseUrl, serviceCode, logger, createError, errorCodes } = deps;
234
238
  const jwksUrl = new URL(JWKS_ENDPOINT, baseUrl);
235
239
  const jwks = createRemoteJWKSet(jwksUrl);
240
+ const acceptedIssuers = resolveAcceptedIssuers(baseUrl);
236
241
  async function verifyJwt(accessToken) {
237
242
  if (!accessToken || !accessToken.trim()) {
238
243
  return {
@@ -245,7 +250,7 @@ function createJwtVerifier(deps) {
245
250
  try {
246
251
  const { payload } = await jwtVerify(accessToken, jwks, {
247
252
  algorithms: [JWT_ALGORITHM],
248
- issuer: JWT_ISSUER,
253
+ issuer: acceptedIssuers,
249
254
  audience: serviceCode,
250
255
  clockTolerance: JWT_CLOCK_TOLERANCE_SECONDS
251
256
  });
@@ -1301,6 +1306,310 @@ function createMembersMethods(deps) {
1301
1306
  return { listMembers, addMember, updateMemberRole, removeMember };
1302
1307
  }
1303
1308
 
1309
+ // src/types/organization-profile.ts
1310
+ var FFID_INDUSTRY_CODES = [
1311
+ "agriculture",
1312
+ "construction",
1313
+ "manufacturing",
1314
+ "it-software",
1315
+ "telecom",
1316
+ "media",
1317
+ "retail",
1318
+ "wholesale",
1319
+ "finance-insurance",
1320
+ "real-estate",
1321
+ "transport-logistics",
1322
+ "hospitality-food",
1323
+ "healthcare",
1324
+ "education",
1325
+ "professional-services",
1326
+ "marketing-advertising",
1327
+ "energy-utilities",
1328
+ "public-sector",
1329
+ "nonprofit",
1330
+ "other"
1331
+ ];
1332
+ var FFID_COMPANY_SIZE_CODES = [
1333
+ "1-10",
1334
+ "11-50",
1335
+ "51-200",
1336
+ "201-500",
1337
+ "501-1000",
1338
+ "1001+"
1339
+ ];
1340
+ var BYTES_PER_KB = 1024;
1341
+ var KB_PER_MB = 1024;
1342
+ var FFID_MAX_ORGANIZATION_LOGO_FILE_SIZE_MB = 2;
1343
+ var FFID_MAX_ORGANIZATION_LOGO_FILE_SIZE_BYTES = FFID_MAX_ORGANIZATION_LOGO_FILE_SIZE_MB * KB_PER_MB * BYTES_PER_KB;
1344
+ var FFID_ALLOWED_ORGANIZATION_LOGO_MIME_TYPES = [
1345
+ "image/png",
1346
+ "image/jpeg",
1347
+ "image/webp"
1348
+ ];
1349
+ var FFID_ORGANIZATION_PROFILE_ERROR_CODES = {
1350
+ VALIDATION_ERROR: "VALIDATION_ERROR",
1351
+ ORGANIZATION_NOT_FOUND: "ORGANIZATION_NOT_FOUND",
1352
+ CONSENTED_BY_NOT_MEMBER: "CONSENTED_BY_NOT_MEMBER",
1353
+ MISSING_FILE: "MISSING_FILE",
1354
+ INVALID_FILE_TYPE: "INVALID_FILE_TYPE",
1355
+ FILE_TOO_LARGE: "FILE_TOO_LARGE",
1356
+ INVALID_IMAGE: "INVALID_IMAGE",
1357
+ LOGO_UPLOAD_FAILED: "LOGO_UPLOAD_FAILED"
1358
+ };
1359
+
1360
+ // src/client/version-check.ts
1361
+ var SDK_VERSION = "7.2.0";
1362
+ var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
1363
+ var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
1364
+ function sdkHeaders() {
1365
+ if (typeof window !== "undefined") return {};
1366
+ return {
1367
+ "User-Agent": SDK_USER_AGENT,
1368
+ [SDK_VERSION_HEADER]: SDK_VERSION
1369
+ };
1370
+ }
1371
+ var LATEST_VERSION_HEADER = "X-FFID-SDK-Latest-Version";
1372
+ var SEMVER_PATTERN = /^\d+(\.\d+)*$/;
1373
+ var _versionWarningShown = false;
1374
+ function compareSemver(a, b) {
1375
+ const cleanA = a.replace(/^v/, "");
1376
+ const cleanB = b.replace(/^v/, "");
1377
+ if (!SEMVER_PATTERN.test(cleanA) || !SEMVER_PATTERN.test(cleanB)) return 0;
1378
+ const partsA = cleanA.split(".").map(Number);
1379
+ const partsB = cleanB.split(".").map(Number);
1380
+ const maxLen = Math.max(partsA.length, partsB.length);
1381
+ for (let i = 0; i < maxLen; i++) {
1382
+ const numA = partsA[i] ?? 0;
1383
+ const numB = partsB[i] ?? 0;
1384
+ if (numA < numB) return -1;
1385
+ if (numA > numB) return 1;
1386
+ }
1387
+ return 0;
1388
+ }
1389
+ function checkVersionHeader(response, logger) {
1390
+ if (_versionWarningShown) return;
1391
+ if (typeof process !== "undefined" && process.env?.NODE_ENV === "production") return;
1392
+ if (!response.headers?.get) return;
1393
+ const latestVersion = response.headers.get(LATEST_VERSION_HEADER);
1394
+ if (!latestVersion) return;
1395
+ if (compareSemver(SDK_VERSION, latestVersion) < 0) {
1396
+ _versionWarningShown = true;
1397
+ logger.warn(
1398
+ `\u65B0\u3057\u3044\u30D0\u30FC\u30B8\u30E7\u30F3\u304C\u5229\u7528\u53EF\u80FD\u3067\u3059: v${latestVersion} (\u73FE\u5728: v${SDK_VERSION})
1399
+ npm install @feelflow/ffid-sdk@latest \u3067\u30A2\u30C3\u30D7\u30C7\u30FC\u30C8\u3067\u304D\u307E\u3059`
1400
+ );
1401
+ }
1402
+ }
1403
+
1404
+ // src/client/organization-profile-methods.ts
1405
+ var EXT_PROFILE_ENDPOINT = "/api/v1/organizations/ext/profile";
1406
+ var EXT_PROFILE_LOGO_ENDPOINT = "/api/v1/organizations/ext/profile/logo";
1407
+ var LOGO_WALL_ENDPOINT = "/api/v1/organizations/logo-wall";
1408
+ var DEFAULT_LOGO_UPLOAD_FILENAME = "logo";
1409
+ function isBlank(value) {
1410
+ return !value || !value.trim();
1411
+ }
1412
+ function resolveOrganizationId(organizationIdOrParams) {
1413
+ return typeof organizationIdOrParams === "string" ? organizationIdOrParams : organizationIdOrParams.organizationId;
1414
+ }
1415
+ function resolveUpsertParams(organizationIdOrParams, payload) {
1416
+ if (typeof organizationIdOrParams === "string") {
1417
+ return { ...payload, organizationId: organizationIdOrParams };
1418
+ }
1419
+ return organizationIdOrParams;
1420
+ }
1421
+ function resolveUploadParams(organizationIdOrParams, file) {
1422
+ if (typeof organizationIdOrParams === "string") {
1423
+ return { organizationId: organizationIdOrParams, file };
1424
+ }
1425
+ return organizationIdOrParams;
1426
+ }
1427
+ function buildUpsertBody(params) {
1428
+ const { organizationId, ...body } = params;
1429
+ return body;
1430
+ }
1431
+ function profileQuery(organizationId) {
1432
+ return new URLSearchParams({ organizationId }).toString();
1433
+ }
1434
+ function logoUploadFilename(file) {
1435
+ if (typeof File !== "undefined" && file instanceof File && file.name) {
1436
+ return file.name;
1437
+ }
1438
+ return DEFAULT_LOGO_UPLOAD_FILENAME;
1439
+ }
1440
+ function listingConsentValidationError(listingConsent, createError) {
1441
+ if (!listingConsent || listingConsent.consented !== true) return null;
1442
+ if (isBlank(listingConsent.consentVersion) || isBlank(listingConsent.consentedByUserId)) {
1443
+ return createError(
1444
+ FFID_ORGANIZATION_PROFILE_ERROR_CODES.VALIDATION_ERROR,
1445
+ "\u63B2\u8F09\u540C\u610F\u3092\u8A18\u9332\u3059\u308B\u5834\u5408\u306F consentVersion \u3068 consentedByUserId \u304C\u5FC5\u9808\u3067\u3059"
1446
+ );
1447
+ }
1448
+ return null;
1449
+ }
1450
+ function resolveLogoFile(file, createError) {
1451
+ if (!file) {
1452
+ return {
1453
+ ok: false,
1454
+ error: createError(
1455
+ FFID_ORGANIZATION_PROFILE_ERROR_CODES.MISSING_FILE,
1456
+ "file \u30D5\u30A3\u30FC\u30EB\u30C9\u306B\u30ED\u30B4\u753B\u50CF\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044"
1457
+ )
1458
+ };
1459
+ }
1460
+ if (file.size > FFID_MAX_ORGANIZATION_LOGO_FILE_SIZE_BYTES) {
1461
+ return {
1462
+ ok: false,
1463
+ error: createError(
1464
+ FFID_ORGANIZATION_PROFILE_ERROR_CODES.FILE_TOO_LARGE,
1465
+ `\u30ED\u30B4\u753B\u50CF\u306F${FFID_MAX_ORGANIZATION_LOGO_FILE_SIZE_MB}MB\u4EE5\u4E0B\u306B\u3057\u3066\u304F\u3060\u3055\u3044`
1466
+ )
1467
+ };
1468
+ }
1469
+ if (file.type !== "" && !FFID_ALLOWED_ORGANIZATION_LOGO_MIME_TYPES.includes(file.type)) {
1470
+ return {
1471
+ ok: false,
1472
+ error: createError(
1473
+ FFID_ORGANIZATION_PROFILE_ERROR_CODES.INVALID_FILE_TYPE,
1474
+ "\u30ED\u30B4\u753B\u50CF\u306F PNG / JPEG / WebP \u5F62\u5F0F\u3067\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044"
1475
+ )
1476
+ };
1477
+ }
1478
+ return { ok: true, file };
1479
+ }
1480
+ function createOrganizationProfileMethods(deps) {
1481
+ const { fetchWithAuth, createError, authMode, baseUrl, logger, errorCodes } = deps;
1482
+ function serviceKeyModeError() {
1483
+ if (authMode === "service-key") return null;
1484
+ return createError(
1485
+ FFID_ORGANIZATION_PROFILE_ERROR_CODES.VALIDATION_ERROR,
1486
+ "getOrganizationProfile / upsertOrganizationProfile / uploadOrganizationLogo \u306F service-key \u8A8D\u8A3C\uFF08X-Service-Api-Key\uFF09\u3067\u306E\u307F\u5229\u7528\u3067\u304D\u307E\u3059\u3002Bearer / cookie \u30E2\u30FC\u30C9\u3067\u306F\u547C\u3073\u51FA\u305B\u307E\u305B\u3093"
1487
+ );
1488
+ }
1489
+ function requireOrganizationId(organizationId) {
1490
+ if (isBlank(organizationId)) {
1491
+ return createError(
1492
+ FFID_ORGANIZATION_PROFILE_ERROR_CODES.VALIDATION_ERROR,
1493
+ "organizationId \u306F\u5FC5\u9808\u3067\u3059"
1494
+ );
1495
+ }
1496
+ return null;
1497
+ }
1498
+ async function fetchPublic(endpoint, options = {}) {
1499
+ const url = `${baseUrl}${endpoint}`;
1500
+ logger.debug("Fetching (public):", url);
1501
+ let response;
1502
+ try {
1503
+ response = await fetch(url, {
1504
+ ...options,
1505
+ credentials: "omit",
1506
+ headers: {
1507
+ ...sdkHeaders(),
1508
+ ...options.headers
1509
+ }
1510
+ });
1511
+ } catch (error) {
1512
+ logger.error("Network error:", error);
1513
+ return {
1514
+ error: {
1515
+ code: errorCodes.NETWORK_ERROR,
1516
+ message: error instanceof Error ? error.message : "\u30CD\u30C3\u30C8\u30EF\u30FC\u30AF\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"
1517
+ }
1518
+ };
1519
+ }
1520
+ let parsed;
1521
+ try {
1522
+ parsed = await response.json();
1523
+ } catch (parseError) {
1524
+ logger.error("Parse error:", parseError, "Status:", response.status);
1525
+ return {
1526
+ error: {
1527
+ code: errorCodes.PARSE_ERROR,
1528
+ message: `\u30B5\u30FC\u30D0\u30FC\u304B\u3089\u4E0D\u6B63\u306A\u30EC\u30B9\u30DD\u30F3\u30B9\u3092\u53D7\u4FE1\u3057\u307E\u3057\u305F (status: ${response.status})`
1529
+ }
1530
+ };
1531
+ }
1532
+ if (!parsed || typeof parsed !== "object") {
1533
+ return {
1534
+ error: {
1535
+ code: errorCodes.PARSE_ERROR,
1536
+ message: `\u30B5\u30FC\u30D0\u30FC\u304B\u3089\u4E0D\u6B63\u306A\u30EC\u30B9\u30DD\u30F3\u30B9\u3092\u53D7\u4FE1\u3057\u307E\u3057\u305F (status: ${response.status})`
1537
+ }
1538
+ };
1539
+ }
1540
+ const raw = parsed;
1541
+ logger.debug("Response (public):", response.status, raw);
1542
+ checkVersionHeader(response, logger);
1543
+ if (!response.ok) {
1544
+ return {
1545
+ error: raw.error ?? {
1546
+ code: errorCodes.UNKNOWN_ERROR,
1547
+ message: "\u4E0D\u660E\u306A\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"
1548
+ }
1549
+ };
1550
+ }
1551
+ if (raw.data === void 0) {
1552
+ return {
1553
+ error: {
1554
+ code: errorCodes.UNKNOWN_ERROR,
1555
+ message: "\u30B5\u30FC\u30D0\u30FC\u304B\u3089\u30C7\u30FC\u30BF\u304C\u8FD4\u3055\u308C\u307E\u305B\u3093\u3067\u3057\u305F"
1556
+ }
1557
+ };
1558
+ }
1559
+ return { data: raw.data };
1560
+ }
1561
+ async function getOrganizationProfile(organizationIdOrParams) {
1562
+ const modeError = serviceKeyModeError();
1563
+ if (modeError) return { error: modeError };
1564
+ const organizationId = resolveOrganizationId(organizationIdOrParams);
1565
+ const idError = requireOrganizationId(organizationId);
1566
+ if (idError) return { error: idError };
1567
+ return fetchWithAuth(
1568
+ `${EXT_PROFILE_ENDPOINT}?${profileQuery(organizationId)}`
1569
+ );
1570
+ }
1571
+ async function upsertOrganizationProfile(organizationIdOrParams, payload) {
1572
+ const modeError = serviceKeyModeError();
1573
+ if (modeError) return { error: modeError };
1574
+ const params = resolveUpsertParams(organizationIdOrParams, payload);
1575
+ const idError = requireOrganizationId(params.organizationId);
1576
+ if (idError) return { error: idError };
1577
+ const consentError = listingConsentValidationError(params.listingConsent, createError);
1578
+ if (consentError) return { error: consentError };
1579
+ return fetchWithAuth(
1580
+ `${EXT_PROFILE_ENDPOINT}?${profileQuery(params.organizationId)}`,
1581
+ {
1582
+ method: "PUT",
1583
+ body: JSON.stringify(buildUpsertBody(params))
1584
+ }
1585
+ );
1586
+ }
1587
+ async function uploadOrganizationLogo(organizationIdOrParams, file) {
1588
+ const modeError = serviceKeyModeError();
1589
+ if (modeError) return { error: modeError };
1590
+ const params = resolveUploadParams(organizationIdOrParams, file);
1591
+ const idError = requireOrganizationId(params.organizationId);
1592
+ if (idError) return { error: idError };
1593
+ const logoFile = resolveLogoFile(params.file, createError);
1594
+ if (!logoFile.ok) return { error: logoFile.error };
1595
+ const form = new FormData();
1596
+ form.append("file", logoFile.file, logoUploadFilename(logoFile.file));
1597
+ return fetchWithAuth(
1598
+ `${EXT_PROFILE_LOGO_ENDPOINT}?${profileQuery(params.organizationId)}`,
1599
+ { method: "POST", body: form }
1600
+ );
1601
+ }
1602
+ async function getPublicLogoWall() {
1603
+ return fetchPublic(LOGO_WALL_ENDPOINT);
1604
+ }
1605
+ return {
1606
+ getOrganizationProfile,
1607
+ upsertOrganizationProfile,
1608
+ uploadOrganizationLogo,
1609
+ getPublicLogoWall
1610
+ };
1611
+ }
1612
+
1304
1613
  // src/client/provisioning-methods.ts
1305
1614
  var USER_PROVISION_ENDPOINT = "/api/v1/ext/users/provision";
1306
1615
  var USER_WELCOME_EMAIL_ENDPOINT = "/api/v1/ext/users/welcome-email";
@@ -1444,7 +1753,7 @@ function createSeatMethods(deps) {
1444
1753
  }
1445
1754
 
1446
1755
  // src/client/profile-methods.ts
1447
- var EXT_PROFILE_ENDPOINT = "/api/v1/users/ext/me";
1756
+ var EXT_PROFILE_ENDPOINT2 = "/api/v1/users/ext/me";
1448
1757
  function resolveAuthOverride(options, createError) {
1449
1758
  if (!options || options.accessToken === void 0) {
1450
1759
  return {};
@@ -1465,7 +1774,7 @@ function createProfileMethods(deps) {
1465
1774
  async function getProfile(options) {
1466
1775
  const { override, error } = resolveAuthOverride(options, createError);
1467
1776
  if (error) return { error };
1468
- return fetchWithAuth(EXT_PROFILE_ENDPOINT, void 0, override);
1777
+ return fetchWithAuth(EXT_PROFILE_ENDPOINT2, void 0, override);
1469
1778
  }
1470
1779
  async function updateProfile(data, options) {
1471
1780
  if (data === null || typeof data !== "object" || Array.isArray(data)) {
@@ -1481,7 +1790,7 @@ function createProfileMethods(deps) {
1481
1790
  const { override, error } = resolveAuthOverride(options, createError);
1482
1791
  if (error) return { error };
1483
1792
  return fetchWithAuth(
1484
- EXT_PROFILE_ENDPOINT,
1793
+ EXT_PROFILE_ENDPOINT2,
1485
1794
  {
1486
1795
  method: "PUT",
1487
1796
  body: JSON.stringify(data)
@@ -1674,50 +1983,6 @@ function validateTokenResponse(tokenResponse) {
1674
1983
  };
1675
1984
  }
1676
1985
 
1677
- // src/client/version-check.ts
1678
- var SDK_VERSION = "7.0.0";
1679
- var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
1680
- var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
1681
- function sdkHeaders() {
1682
- if (typeof window !== "undefined") return {};
1683
- return {
1684
- "User-Agent": SDK_USER_AGENT,
1685
- [SDK_VERSION_HEADER]: SDK_VERSION
1686
- };
1687
- }
1688
- var LATEST_VERSION_HEADER = "X-FFID-SDK-Latest-Version";
1689
- var SEMVER_PATTERN = /^\d+(\.\d+)*$/;
1690
- var _versionWarningShown = false;
1691
- function compareSemver(a, b) {
1692
- const cleanA = a.replace(/^v/, "");
1693
- const cleanB = b.replace(/^v/, "");
1694
- if (!SEMVER_PATTERN.test(cleanA) || !SEMVER_PATTERN.test(cleanB)) return 0;
1695
- const partsA = cleanA.split(".").map(Number);
1696
- const partsB = cleanB.split(".").map(Number);
1697
- const maxLen = Math.max(partsA.length, partsB.length);
1698
- for (let i = 0; i < maxLen; i++) {
1699
- const numA = partsA[i] ?? 0;
1700
- const numB = partsB[i] ?? 0;
1701
- if (numA < numB) return -1;
1702
- if (numA > numB) return 1;
1703
- }
1704
- return 0;
1705
- }
1706
- function checkVersionHeader(response, logger) {
1707
- if (_versionWarningShown) return;
1708
- if (typeof process !== "undefined" && process.env?.NODE_ENV === "production") return;
1709
- if (!response.headers?.get) return;
1710
- const latestVersion = response.headers.get(LATEST_VERSION_HEADER);
1711
- if (!latestVersion) return;
1712
- if (compareSemver(SDK_VERSION, latestVersion) < 0) {
1713
- _versionWarningShown = true;
1714
- logger.warn(
1715
- `\u65B0\u3057\u3044\u30D0\u30FC\u30B8\u30E7\u30F3\u304C\u5229\u7528\u53EF\u80FD\u3067\u3059: v${latestVersion} (\u73FE\u5728: v${SDK_VERSION})
1716
- npm install @feelflow/ffid-sdk@latest \u3067\u30A2\u30C3\u30D7\u30C7\u30FC\u30C8\u3067\u304D\u307E\u3059`
1717
- );
1718
- }
1719
- }
1720
-
1721
1986
  // src/client/oauth-token.ts
1722
1987
  var OAUTH_TOKEN_ENDPOINT = "/api/v1/oauth/token";
1723
1988
  var OAUTH_REVOKE_ENDPOINT = "/api/v1/oauth/revoke";
@@ -2633,7 +2898,7 @@ function normalizeRedirectUri(input) {
2633
2898
 
2634
2899
  // src/client/password-reset.ts
2635
2900
  var RESET_PASSWORD_BASE = "/api/v1/auth/reset-password";
2636
- function isBlank(value) {
2901
+ function isBlank2(value) {
2637
2902
  return !value || !value.trim();
2638
2903
  }
2639
2904
  function createPasswordResetMethods(deps) {
@@ -2694,7 +2959,7 @@ function createPasswordResetMethods(deps) {
2694
2959
  return { data: raw.data };
2695
2960
  }
2696
2961
  async function requestPasswordReset(email) {
2697
- if (isBlank(email)) {
2962
+ if (isBlank2(email)) {
2698
2963
  return {
2699
2964
  error: createError("VALIDATION_ERROR", "\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u306F\u5FC5\u9808\u3067\u3059")
2700
2965
  };
@@ -2708,7 +2973,7 @@ function createPasswordResetMethods(deps) {
2708
2973
  );
2709
2974
  }
2710
2975
  async function verifyPasswordResetToken(accessToken) {
2711
- if (isBlank(accessToken)) {
2976
+ if (isBlank2(accessToken)) {
2712
2977
  return {
2713
2978
  error: createError("VALIDATION_ERROR", "\u30A2\u30AF\u30BB\u30B9\u30C8\u30FC\u30AF\u30F3\u306F\u5FC5\u9808\u3067\u3059")
2714
2979
  };
@@ -2722,12 +2987,12 @@ function createPasswordResetMethods(deps) {
2722
2987
  );
2723
2988
  }
2724
2989
  async function establishResetSession(accessToken, refreshToken) {
2725
- if (isBlank(accessToken)) {
2990
+ if (isBlank2(accessToken)) {
2726
2991
  return {
2727
2992
  error: createError("VALIDATION_ERROR", "\u30A2\u30AF\u30BB\u30B9\u30C8\u30FC\u30AF\u30F3\u306F\u5FC5\u9808\u3067\u3059")
2728
2993
  };
2729
2994
  }
2730
- if (isBlank(refreshToken)) {
2995
+ if (isBlank2(refreshToken)) {
2731
2996
  return {
2732
2997
  error: createError("VALIDATION_ERROR", "\u30EA\u30D5\u30EC\u30C3\u30B7\u30E5\u30C8\u30FC\u30AF\u30F3\u306F\u5FC5\u9808\u3067\u3059")
2733
2998
  };
@@ -2741,7 +3006,7 @@ function createPasswordResetMethods(deps) {
2741
3006
  );
2742
3007
  }
2743
3008
  async function confirmPasswordReset(password) {
2744
- if (isBlank(password)) {
3009
+ if (isBlank2(password)) {
2745
3010
  return {
2746
3011
  error: createError("VALIDATION_ERROR", "\u30D1\u30B9\u30EF\u30FC\u30C9\u306F\u5FC5\u9808\u3067\u3059")
2747
3012
  };
@@ -2764,7 +3029,7 @@ function createPasswordResetMethods(deps) {
2764
3029
 
2765
3030
  // src/client/otp.ts
2766
3031
  var OTP_BASE = "/api/v1/auth/otp";
2767
- function isBlank2(value) {
3032
+ function isBlank3(value) {
2768
3033
  return !value || !value.trim();
2769
3034
  }
2770
3035
  function createOtpMethods(deps) {
@@ -2825,7 +3090,7 @@ function createOtpMethods(deps) {
2825
3090
  return { data: raw.data };
2826
3091
  }
2827
3092
  async function sendOtp(email, options) {
2828
- if (isBlank2(email)) {
3093
+ if (isBlank3(email)) {
2829
3094
  return {
2830
3095
  error: createError("VALIDATION_ERROR", "\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u306F\u5FC5\u9808\u3067\u3059")
2831
3096
  };
@@ -2842,12 +3107,12 @@ function createOtpMethods(deps) {
2842
3107
  );
2843
3108
  }
2844
3109
  async function verifyOtp(params) {
2845
- if (isBlank2(params.accessToken)) {
3110
+ if (isBlank3(params.accessToken)) {
2846
3111
  return {
2847
3112
  error: createError("VALIDATION_ERROR", "\u30A2\u30AF\u30BB\u30B9\u30C8\u30FC\u30AF\u30F3\u306F\u5FC5\u9808\u3067\u3059")
2848
3113
  };
2849
3114
  }
2850
- if (isBlank2(params.refreshToken)) {
3115
+ if (isBlank3(params.refreshToken)) {
2851
3116
  return {
2852
3117
  error: createError("VALIDATION_ERROR", "\u30EA\u30D5\u30EC\u30C3\u30B7\u30E5\u30C8\u30FC\u30AF\u30F3\u306F\u5FC5\u9808\u3067\u3059")
2853
3118
  };
@@ -3413,6 +3678,12 @@ function resolveRedirectUri(raw, logger) {
3413
3678
  return raw;
3414
3679
  }
3415
3680
  }
3681
+ function jsonContentTypeHeader(body) {
3682
+ if (typeof FormData !== "undefined" && body instanceof FormData) {
3683
+ return {};
3684
+ }
3685
+ return { "Content-Type": "application/json" };
3686
+ }
3416
3687
  function createFFIDClient(config) {
3417
3688
  if (!config.serviceCode || !config.serviceCode.trim()) {
3418
3689
  throw new Error("FFID Client: serviceCode \u304C\u672A\u8A2D\u5B9A\u3067\u3059");
@@ -3465,7 +3736,7 @@ function createFFIDClient(config) {
3465
3736
  ...options,
3466
3737
  credentials: "omit",
3467
3738
  headers: {
3468
- "Content-Type": "application/json",
3739
+ ...jsonContentTypeHeader(options.body),
3469
3740
  ...sdkHeaders(),
3470
3741
  ...options.headers,
3471
3742
  Authorization: `Bearer ${authOverride.accessToken}`
@@ -3477,7 +3748,7 @@ function createFFIDClient(config) {
3477
3748
  ...options,
3478
3749
  credentials: "omit",
3479
3750
  headers: {
3480
- "Content-Type": "application/json",
3751
+ ...jsonContentTypeHeader(options.body),
3481
3752
  ...sdkHeaders(),
3482
3753
  "X-Service-Api-Key": serviceApiKey,
3483
3754
  ...options.headers
@@ -3487,7 +3758,7 @@ function createFFIDClient(config) {
3487
3758
  if (authMode === "token") {
3488
3759
  const tokens = tokenStore.getTokens();
3489
3760
  const headers = {
3490
- "Content-Type": "application/json",
3761
+ ...jsonContentTypeHeader(options.body),
3491
3762
  ...sdkHeaders(),
3492
3763
  ...options.headers
3493
3764
  };
@@ -3504,7 +3775,7 @@ function createFFIDClient(config) {
3504
3775
  ...options,
3505
3776
  credentials: "include",
3506
3777
  headers: {
3507
- "Content-Type": "application/json",
3778
+ ...jsonContentTypeHeader(options.body),
3508
3779
  ...sdkHeaders(),
3509
3780
  ...options.headers
3510
3781
  }
@@ -3721,6 +3992,19 @@ function createFFIDClient(config) {
3721
3992
  createError,
3722
3993
  serviceCode: config.serviceCode
3723
3994
  });
3995
+ const {
3996
+ getOrganizationProfile,
3997
+ upsertOrganizationProfile,
3998
+ uploadOrganizationLogo,
3999
+ getPublicLogoWall
4000
+ } = createOrganizationProfileMethods({
4001
+ fetchWithAuth,
4002
+ createError,
4003
+ authMode,
4004
+ baseUrl,
4005
+ logger,
4006
+ errorCodes: FFID_ERROR_CODES
4007
+ });
3724
4008
  const { provisionUser, sendProvisionWelcomeEmail, provisionOrganization } = createProvisioningMethods({
3725
4009
  fetchWithAuth,
3726
4010
  createError,
@@ -3821,6 +4105,10 @@ function createFFIDClient(config) {
3821
4105
  addMember,
3822
4106
  updateMemberRole,
3823
4107
  removeMember,
4108
+ getOrganizationProfile,
4109
+ upsertOrganizationProfile,
4110
+ uploadOrganizationLogo,
4111
+ getPublicLogoWall,
3824
4112
  provisionUser,
3825
4113
  sendProvisionWelcomeEmail,
3826
4114
  provisionOrganization,
@@ -6009,4 +6297,4 @@ function FFIDInquiryForm({
6009
6297
  );
6010
6298
  }
6011
6299
 
6012
- export { ACCESS_GRANTING_EFFECTIVE_STATUSES, BLOCKING_EFFECTIVE_STATUSES, CATALOG_HASH_PATTERN, DEFAULT_API_BASE_URL, DEFAULT_OAUTH_SCOPES, EFFECTIVE_SUBSCRIPTION_STATUSES, FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDCatalogHashError, FFIDInquiryForm, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDProvider, FFIDSDKError, FFIDSubscriptionBadge, FFIDUserMenu, FFID_ANNOUNCEMENTS_ERROR_CODES, FFID_CATALOG_ENVIRONMENTS, FFID_CATALOG_PUBLICATION_STATUSES, FFID_ERROR_CODES, FFID_INQUIRY_ACQUISITION_SOURCES, FFID_INQUIRY_CATEGORIES, FFID_INQUIRY_CATEGORIES_SITE_2026, canonicalizeCatalogValue, cleanupStateStorage, computeEffectiveStatusFromSession, computeFFIDCheckoutCopyHash, computeLegalDisclosureHash, computePaymentConfigurationHash, computePraxisCopyHash, computeScopeHash, computeTaxConfigurationHash, createFFIDAnnouncementsClient, createFFIDClient, createTokenStore, generateCodeChallenge, generateCodeVerifier, handleRedirectCallback, hasAccessFromUserinfo, hashCanonicalJson, isBlockedFromUserinfo, isFFIDInquiryAcquisitionSource, isFFIDInquiryCategorySite2026, normalizeRedirectUri, retrieveCodeVerifier, retrieveState, sha256Hex, storeCodeVerifier, storeState, useFFID, useFFIDAnnouncements, useFFIDContext, useSubscription, validatePublishedCatalog, withSubscription };
6300
+ export { ACCESS_GRANTING_EFFECTIVE_STATUSES, BLOCKING_EFFECTIVE_STATUSES, CATALOG_HASH_PATTERN, DEFAULT_API_BASE_URL, DEFAULT_OAUTH_SCOPES, EFFECTIVE_SUBSCRIPTION_STATUSES, FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDCatalogHashError, FFIDInquiryForm, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDProvider, FFIDSDKError, FFIDSubscriptionBadge, FFIDUserMenu, FFID_ALLOWED_ORGANIZATION_LOGO_MIME_TYPES, FFID_ANNOUNCEMENTS_ERROR_CODES, FFID_CATALOG_ENVIRONMENTS, FFID_CATALOG_PUBLICATION_STATUSES, FFID_COMPANY_SIZE_CODES, FFID_ERROR_CODES, FFID_INDUSTRY_CODES, FFID_INQUIRY_ACQUISITION_SOURCES, FFID_INQUIRY_CATEGORIES, FFID_INQUIRY_CATEGORIES_SITE_2026, FFID_MAX_ORGANIZATION_LOGO_FILE_SIZE_BYTES, FFID_MAX_ORGANIZATION_LOGO_FILE_SIZE_MB, FFID_ORGANIZATION_PROFILE_ERROR_CODES, canonicalizeCatalogValue, cleanupStateStorage, computeEffectiveStatusFromSession, computeFFIDCheckoutCopyHash, computeLegalDisclosureHash, computePaymentConfigurationHash, computePraxisCopyHash, computeScopeHash, computeTaxConfigurationHash, createFFIDAnnouncementsClient, createFFIDClient, createTokenStore, generateCodeChallenge, generateCodeVerifier, handleRedirectCallback, hasAccessFromUserinfo, hashCanonicalJson, isBlockedFromUserinfo, isFFIDInquiryAcquisitionSource, isFFIDInquiryCategorySite2026, normalizeRedirectUri, retrieveCodeVerifier, retrieveState, sha256Hex, storeCodeVerifier, storeState, useFFID, useFFIDAnnouncements, useFFIDContext, useSubscription, validatePublishedCatalog, withSubscription };