@feelflow/ffid-sdk 5.17.1 → 5.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -396,6 +396,83 @@ await ffid.updateMemberRole({
396
396
  - `role` は `admin` / `member` / `viewer` のみ指定可能です。`owner` は追加・昇格 API では扱いません。
397
397
  - agency client と同じ呼び味が必要な場合は `ffid.addMember(organizationId, { email, role })` も使えます。
398
398
 
399
+ ### Provisioning(users / organizations)
400
+
401
+ 外部サービスからの一括移行向けに、**service-key 認証**でユーザー・組織を冪等にプロビジョニングするメソッド。REST を直叩きせず `createFFIDClient` 経由で呼べる(SDK-first、#3790 / #4127)。
402
+
403
+ - **`authMode: 'service-key'`(`X-Service-Api-Key`)専用**。`token`(Bearer)/ `cookie` モードで呼ぶとサーバーへ往復せず即座に `VALIDATION_ERROR` を返す(サーバー側エンドポイントが service-key 認証のみを受け付けるため)。
404
+ - 必要 scope: `provisionUser` → `user:provision`、`provisionOrganization` → `organization:write`。
405
+
406
+ ```ts
407
+ import { createFFIDClient } from '@feelflow/ffid-sdk/server'
408
+
409
+ const ffid = createFFIDClient({
410
+ serviceCode: 'praxis',
411
+ scope: '',
412
+ authMode: 'service-key',
413
+ serviceApiKey: process.env.FFID_SERVICE_API_KEY!,
414
+ })
415
+
416
+ // 1. ユーザーを冪等に作成(新規なら created:true / 既存なら created:false)
417
+ const userRes = await ffid.provisionUser({
418
+ email: 'owner@example.com',
419
+ profile: { displayName: '山田 太郎', companyName: 'Example Inc.' },
420
+ })
421
+ if (userRes.error) throw new Error(userRes.error.message)
422
+ if (!userRes.data.dryRun) {
423
+ console.log(userRes.data.created ? '新規作成' : '既存ユーザー', userRes.data.user.id)
424
+ }
425
+
426
+ // 2. その owner の組織を作成し、メンバーを冪等に追加
427
+ const orgRes = await ffid.provisionOrganization({
428
+ name: 'Example 組織',
429
+ ownerEmail: 'owner@example.com',
430
+ members: [
431
+ { email: 'member1@example.com', role: 'member' },
432
+ { email: 'member2@example.com', role: 'viewer' },
433
+ ],
434
+ })
435
+ if (orgRes.error) {
436
+ // owner が未登録なら error.code === 'OWNER_NOT_FOUND'(先に provisionUser が必要)
437
+ throw new Error(orgRes.error.message)
438
+ }
439
+ if (!orgRes.data.dryRun) {
440
+ for (const m of orgRes.data.members) {
441
+ console.log(m.email, m.status) // 'added' | 'already_member' | 'user_not_found'
442
+ }
443
+ }
444
+ ```
445
+
446
+ #### 冪等セマンティクス
447
+
448
+ - `provisionUser`: **既存メールは `created: false`**(HTTP 200、no-op)、新規メールは `created: true`(HTTP 201、パスワードレス・メール確認済み)。メールはサーバー側で normalize(trim + lowercase)されるため、大文字小文字違いの再送でも同一ユーザーに解決する。新規作成で `profile` を渡した場合のみ `profileWritten` が付き、`false` は「ユーザーは作成されたがプロフィール書き込みに失敗」→ 再送を推奨。
449
+ - `provisionOrganization`: owner が同名の組織を既に持つ場合は `created: false`(HTTP 200)。`members` は冪等に追加され、各要素の `status` が `added` / `already_member` / `user_not_found` を返す。
450
+
451
+ #### dryRun
452
+
453
+ `dryRun: true` を渡すと **一切書き込まず**、実行時に何が起きるかだけを返す。レスポンスは `dryRun` 判別のタグ付きユニオン。
454
+
455
+ ```ts
456
+ const preview = await ffid.provisionUser({ email: 'owner@example.com', dryRun: true })
457
+ if (!preview.error && preview.data.dryRun) {
458
+ console.log(preview.data.wouldCreate) // true = 実行すれば新規作成される
459
+ }
460
+
461
+ const orgPreview = await ffid.provisionOrganization({
462
+ name: 'Example 組織',
463
+ ownerEmail: 'owner@example.com',
464
+ members: [{ email: 'member1@example.com', role: 'member' }],
465
+ dryRun: true,
466
+ })
467
+ if (!orgPreview.error && orgPreview.data.dryRun) {
468
+ console.log(orgPreview.data.wouldCreate) // 組織を新規作成するか
469
+ // per-member plan status: 'would_add' | 'already_member' | 'user_not_found'
470
+ orgPreview.data.members.forEach((m) => console.log(m.email, m.status))
471
+ }
472
+ ```
473
+
474
+ `if (res.data.dryRun)` で TypeScript が型を絞り込むため、dry-run 分岐では `wouldCreate`、実行分岐では `created` がそれぞれ型安全に参照できる。
475
+
399
476
  ### getProfile() / updateProfile()
400
477
 
401
478
  ログイン中ユーザー自身のプロフィールを取得・更新するメソッド(`createFFIDClient` から呼び出し)。
@@ -1,5 +1,5 @@
1
1
  import { createContext, useState, useRef, useEffect, useMemo, useCallback, useContext } from 'react';
2
- import { createRemoteJWKSet, jwtVerify } from 'jose';
2
+ import { createRemoteJWKSet, jwtVerify, errors } from 'jose';
3
3
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
4
 
5
5
  // src/constants.ts
@@ -228,10 +228,29 @@ function createJwtVerifier(deps) {
228
228
  return { data: userInfo };
229
229
  } catch (error) {
230
230
  const message = error instanceof Error ? error.message : "JWT\u691C\u8A3C\u306B\u5931\u6557\u3057\u307E\u3057\u305F";
231
- logger.error("JWT verification failed:", message);
231
+ if (error instanceof errors.JWTExpired) {
232
+ logger.debug("JWT verification failed (token expired):", message);
233
+ return {
234
+ error: createError(
235
+ errorCodes.TOKEN_EXPIRED,
236
+ "\u30A2\u30AF\u30BB\u30B9\u30C8\u30FC\u30AF\u30F3\u306E\u6709\u52B9\u671F\u9650\u304C\u5207\u308C\u3066\u3044\u307E\u3059"
237
+ )
238
+ };
239
+ }
240
+ const isTokenLevelError = error instanceof errors.JWTClaimValidationFailed || error instanceof errors.JWTInvalid || error instanceof errors.JWSInvalid || error instanceof errors.JWSSignatureVerificationFailed || error instanceof errors.JOSEAlgNotAllowed || error instanceof errors.JOSENotSupported || error instanceof errors.JWKSNoMatchingKey || error instanceof errors.JWKSMultipleMatchingKeys;
241
+ if (isTokenLevelError) {
242
+ logger.warn("JWT verification failed:", message);
243
+ return {
244
+ error: createError(
245
+ errorCodes.TOKEN_VERIFICATION_ERROR,
246
+ `JWT\u691C\u8A3C\u306B\u5931\u6557\u3057\u307E\u3057\u305F: ${message}`
247
+ )
248
+ };
249
+ }
250
+ logger.error("JWT verification failed (JWKS fetch):", message);
232
251
  return {
233
252
  error: createError(
234
- errorCodes.TOKEN_VERIFICATION_ERROR,
253
+ errorCodes.NETWORK_ERROR,
235
254
  `JWT\u691C\u8A3C\u306B\u5931\u6557\u3057\u307E\u3057\u305F: ${message}`
236
255
  )
237
256
  };
@@ -256,7 +275,9 @@ function createVerifyAccessToken(deps) {
256
275
  serviceCode,
257
276
  logger,
258
277
  createError,
259
- errorCodes: { TOKEN_VERIFICATION_ERROR: errorCodes.TOKEN_VERIFICATION_ERROR }
278
+ // structural superset of JwtVerifierDeps.errorCodes — passing it
279
+ // wholesale removes the hand-transcription drift point
280
+ errorCodes
260
281
  });
261
282
  }
262
283
  return jwtVerify2;
@@ -852,6 +873,93 @@ function createMembersMethods(deps) {
852
873
  return { listMembers, addMember, updateMemberRole, removeMember };
853
874
  }
854
875
 
876
+ // src/client/provisioning-methods.ts
877
+ var USER_PROVISION_ENDPOINT = "/api/v1/ext/users/provision";
878
+ var ORGANIZATION_PROVISION_ENDPOINT = "/api/v1/ext/organizations/provision";
879
+ var MAX_PROVISION_MEMBERS = 100;
880
+ var ASSIGNABLE_MEMBER_ROLES = {
881
+ admin: true,
882
+ member: true,
883
+ viewer: true
884
+ };
885
+ function isAssignableMemberRole(role) {
886
+ return Object.hasOwn(ASSIGNABLE_MEMBER_ROLES, role);
887
+ }
888
+ function createProvisioningMethods(deps) {
889
+ const { fetchWithAuth, createError, authMode } = deps;
890
+ function serviceKeyModeError() {
891
+ if (authMode === "service-key") return null;
892
+ return createError(
893
+ "VALIDATION_ERROR",
894
+ "provisionUser / provisionOrganization \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"
895
+ );
896
+ }
897
+ async function provisionUser(params) {
898
+ const modeError = serviceKeyModeError();
899
+ if (modeError) return { error: modeError };
900
+ if (!params.email || !params.email.trim()) {
901
+ return { error: createError("VALIDATION_ERROR", "email \u306F\u5FC5\u9808\u3067\u3059") };
902
+ }
903
+ const body = { email: params.email.trim() };
904
+ if (params.profile !== void 0) body.profile = params.profile;
905
+ if (params.dryRun !== void 0) body.dryRun = params.dryRun;
906
+ return fetchWithAuth(USER_PROVISION_ENDPOINT, {
907
+ method: "POST",
908
+ body: JSON.stringify(body)
909
+ });
910
+ }
911
+ async function provisionOrganization(params) {
912
+ const modeError = serviceKeyModeError();
913
+ if (modeError) return { error: modeError };
914
+ if (!params.name || !params.name.trim()) {
915
+ return { error: createError("VALIDATION_ERROR", "name \u306F\u5FC5\u9808\u3067\u3059") };
916
+ }
917
+ if (!params.ownerEmail || !params.ownerEmail.trim()) {
918
+ return { error: createError("VALIDATION_ERROR", "ownerEmail \u306F\u5FC5\u9808\u3067\u3059") };
919
+ }
920
+ if (params.members) {
921
+ if (params.members.length > MAX_PROVISION_MEMBERS) {
922
+ return {
923
+ error: createError(
924
+ "VALIDATION_ERROR",
925
+ `members \u306F1\u30EA\u30AF\u30A8\u30B9\u30C8\u3042\u305F\u308A\u6700\u5927 ${MAX_PROVISION_MEMBERS} \u4EF6\u307E\u3067\u3067\u3059`
926
+ )
927
+ };
928
+ }
929
+ for (const member of params.members) {
930
+ if (!member.email || !member.email.trim()) {
931
+ return { error: createError("VALIDATION_ERROR", "members[].email \u306F\u5FC5\u9808\u3067\u3059") };
932
+ }
933
+ if (!isAssignableMemberRole(member.role)) {
934
+ return {
935
+ error: createError(
936
+ "VALIDATION_ERROR",
937
+ "members[].role \u306F admin\u3001member\u3001viewer \u306E\u3044\u305A\u308C\u304B\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044"
938
+ )
939
+ };
940
+ }
941
+ }
942
+ }
943
+ const body = {
944
+ name: params.name.trim(),
945
+ ownerEmail: params.ownerEmail.trim()
946
+ };
947
+ if (params.billingEmail !== void 0) body.billingEmail = params.billingEmail.trim();
948
+ if (params.members !== void 0) {
949
+ body.members = params.members.map((member) => ({
950
+ email: member.email.trim(),
951
+ role: member.role
952
+ }));
953
+ }
954
+ if (params.dryRun !== void 0) body.dryRun = params.dryRun;
955
+ return fetchWithAuth(ORGANIZATION_PROVISION_ENDPOINT, {
956
+ method: "POST",
957
+ body: JSON.stringify(body)
958
+ });
959
+ }
960
+ return { provisionUser, provisionOrganization };
961
+ }
962
+
855
963
  // src/client/seat-methods.ts
856
964
  function seatsEndpoint(subscriptionId) {
857
965
  return `/api/v1/subscriptions/ext/${encodeURIComponent(subscriptionId)}/seats/assignments`;
@@ -1096,7 +1204,7 @@ function createNonContractMethods(deps) {
1096
1204
  }
1097
1205
 
1098
1206
  // src/client/version-check.ts
1099
- var SDK_VERSION = "5.17.1";
1207
+ var SDK_VERSION = "5.19.0";
1100
1208
  var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
1101
1209
  var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
1102
1210
  function sdkHeaders() {
@@ -2693,6 +2801,19 @@ function isBlockedFromUserinfo(sub) {
2693
2801
  return BLOCKING_EFFECTIVE_STATUSES.includes(effectiveStatus);
2694
2802
  }
2695
2803
 
2804
+ // src/client/error-codes.ts
2805
+ var FFID_ERROR_CODES = {
2806
+ NETWORK_ERROR: "NETWORK_ERROR",
2807
+ PARSE_ERROR: "PARSE_ERROR",
2808
+ UNKNOWN_ERROR: "UNKNOWN_ERROR",
2809
+ TOKEN_EXCHANGE_ERROR: "TOKEN_EXCHANGE_ERROR",
2810
+ TOKEN_REFRESH_ERROR: "TOKEN_REFRESH_ERROR",
2811
+ NO_TOKENS: "NO_TOKENS",
2812
+ TOKEN_VERIFICATION_ERROR: "TOKEN_VERIFICATION_ERROR",
2813
+ TOKEN_EXPIRED: "TOKEN_EXPIRED",
2814
+ STATE_MISMATCH_ERROR: "STATE_MISMATCH_ERROR"
2815
+ };
2816
+
2696
2817
  // src/client/ffid-client.ts
2697
2818
  var UNAUTHORIZED_STATUS2 = 401;
2698
2819
  var SDK_LOG_PREFIX = "[FFID SDK]";
@@ -2701,8 +2822,10 @@ var noopLogger = {
2701
2822
  },
2702
2823
  info: () => {
2703
2824
  },
2704
- warn: (...args) => console.warn(SDK_LOG_PREFIX, ...args),
2705
- error: (...args) => console.error(SDK_LOG_PREFIX, ...args)
2825
+ warn: () => {
2826
+ },
2827
+ error: () => {
2828
+ }
2706
2829
  };
2707
2830
  var consoleLogger = {
2708
2831
  debug: (...args) => console.debug(SDK_LOG_PREFIX, ...args),
@@ -2710,16 +2833,6 @@ var consoleLogger = {
2710
2833
  warn: (...args) => console.warn(SDK_LOG_PREFIX, ...args),
2711
2834
  error: (...args) => console.error(SDK_LOG_PREFIX, ...args)
2712
2835
  };
2713
- var FFID_ERROR_CODES = {
2714
- NETWORK_ERROR: "NETWORK_ERROR",
2715
- PARSE_ERROR: "PARSE_ERROR",
2716
- UNKNOWN_ERROR: "UNKNOWN_ERROR",
2717
- TOKEN_EXCHANGE_ERROR: "TOKEN_EXCHANGE_ERROR",
2718
- TOKEN_REFRESH_ERROR: "TOKEN_REFRESH_ERROR",
2719
- NO_TOKENS: "NO_TOKENS",
2720
- TOKEN_VERIFICATION_ERROR: "TOKEN_VERIFICATION_ERROR",
2721
- STATE_MISMATCH_ERROR: "STATE_MISMATCH_ERROR"
2722
- };
2723
2836
  var EXT_CHECK_ENDPOINT = "/api/v1/subscriptions/ext/check";
2724
2837
  var DEFAULT_ALLOW_GRACE = true;
2725
2838
  var DEFAULT_SERVICE_ACCESS_FAIL_POLICY = "failClosed";
@@ -3102,6 +3215,11 @@ function createFFIDClient(config) {
3102
3215
  createError,
3103
3216
  serviceCode: config.serviceCode
3104
3217
  });
3218
+ const { provisionUser, provisionOrganization } = createProvisioningMethods({
3219
+ fetchWithAuth,
3220
+ createError,
3221
+ authMode
3222
+ });
3105
3223
  const { getProfile, updateProfile } = createProfileMethods({
3106
3224
  fetchWithAuth,
3107
3225
  createError
@@ -3197,6 +3315,8 @@ function createFFIDClient(config) {
3197
3315
  addMember,
3198
3316
  updateMemberRole,
3199
3317
  removeMember,
3318
+ provisionUser,
3319
+ provisionOrganization,
3200
3320
  getProfile,
3201
3321
  updateProfile,
3202
3322
  // Non-contract ext API coverage (#3783)
@@ -5334,4 +5454,4 @@ function FFIDInquiryForm({
5334
5454
  );
5335
5455
  }
5336
5456
 
5337
- export { ACCESS_GRANTING_EFFECTIVE_STATUSES, BLOCKING_EFFECTIVE_STATUSES, DEFAULT_API_BASE_URL, DEFAULT_OAUTH_SCOPES, EFFECTIVE_SUBSCRIPTION_STATUSES, FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDInquiryForm, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDProvider, FFIDSDKError, FFIDSubscriptionBadge, FFIDUserMenu, FFID_ANNOUNCEMENTS_ERROR_CODES, FFID_INQUIRY_CATEGORIES, FFID_INQUIRY_CATEGORIES_SITE_2026, cleanupStateStorage, computeEffectiveStatusFromSession, createFFIDAnnouncementsClient, createFFIDClient, createTokenStore, generateCodeChallenge, generateCodeVerifier, handleRedirectCallback, hasAccessFromUserinfo, isBlockedFromUserinfo, isFFIDInquiryCategorySite2026, normalizeRedirectUri, retrieveCodeVerifier, retrieveState, storeCodeVerifier, storeState, useFFID, useFFIDAnnouncements, useFFIDContext, useSubscription, withSubscription };
5457
+ export { ACCESS_GRANTING_EFFECTIVE_STATUSES, BLOCKING_EFFECTIVE_STATUSES, DEFAULT_API_BASE_URL, DEFAULT_OAUTH_SCOPES, EFFECTIVE_SUBSCRIPTION_STATUSES, FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDInquiryForm, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDProvider, FFIDSDKError, FFIDSubscriptionBadge, FFIDUserMenu, FFID_ANNOUNCEMENTS_ERROR_CODES, FFID_ERROR_CODES, FFID_INQUIRY_CATEGORIES, FFID_INQUIRY_CATEGORIES_SITE_2026, cleanupStateStorage, computeEffectiveStatusFromSession, createFFIDAnnouncementsClient, createFFIDClient, createTokenStore, generateCodeChallenge, generateCodeVerifier, handleRedirectCallback, hasAccessFromUserinfo, isBlockedFromUserinfo, isFFIDInquiryCategorySite2026, normalizeRedirectUri, retrieveCodeVerifier, retrieveState, storeCodeVerifier, storeState, useFFID, useFFIDAnnouncements, useFFIDContext, useSubscription, withSubscription };
@@ -230,10 +230,29 @@ function createJwtVerifier(deps) {
230
230
  return { data: userInfo };
231
231
  } catch (error) {
232
232
  const message = error instanceof Error ? error.message : "JWT\u691C\u8A3C\u306B\u5931\u6557\u3057\u307E\u3057\u305F";
233
- logger.error("JWT verification failed:", message);
233
+ if (error instanceof jose.errors.JWTExpired) {
234
+ logger.debug("JWT verification failed (token expired):", message);
235
+ return {
236
+ error: createError(
237
+ errorCodes.TOKEN_EXPIRED,
238
+ "\u30A2\u30AF\u30BB\u30B9\u30C8\u30FC\u30AF\u30F3\u306E\u6709\u52B9\u671F\u9650\u304C\u5207\u308C\u3066\u3044\u307E\u3059"
239
+ )
240
+ };
241
+ }
242
+ const isTokenLevelError = error instanceof jose.errors.JWTClaimValidationFailed || error instanceof jose.errors.JWTInvalid || error instanceof jose.errors.JWSInvalid || error instanceof jose.errors.JWSSignatureVerificationFailed || error instanceof jose.errors.JOSEAlgNotAllowed || error instanceof jose.errors.JOSENotSupported || error instanceof jose.errors.JWKSNoMatchingKey || error instanceof jose.errors.JWKSMultipleMatchingKeys;
243
+ if (isTokenLevelError) {
244
+ logger.warn("JWT verification failed:", message);
245
+ return {
246
+ error: createError(
247
+ errorCodes.TOKEN_VERIFICATION_ERROR,
248
+ `JWT\u691C\u8A3C\u306B\u5931\u6557\u3057\u307E\u3057\u305F: ${message}`
249
+ )
250
+ };
251
+ }
252
+ logger.error("JWT verification failed (JWKS fetch):", message);
234
253
  return {
235
254
  error: createError(
236
- errorCodes.TOKEN_VERIFICATION_ERROR,
255
+ errorCodes.NETWORK_ERROR,
237
256
  `JWT\u691C\u8A3C\u306B\u5931\u6557\u3057\u307E\u3057\u305F: ${message}`
238
257
  )
239
258
  };
@@ -258,7 +277,9 @@ function createVerifyAccessToken(deps) {
258
277
  serviceCode,
259
278
  logger,
260
279
  createError,
261
- errorCodes: { TOKEN_VERIFICATION_ERROR: errorCodes.TOKEN_VERIFICATION_ERROR }
280
+ // structural superset of JwtVerifierDeps.errorCodes — passing it
281
+ // wholesale removes the hand-transcription drift point
282
+ errorCodes
262
283
  });
263
284
  }
264
285
  return jwtVerify2;
@@ -854,6 +875,93 @@ function createMembersMethods(deps) {
854
875
  return { listMembers, addMember, updateMemberRole, removeMember };
855
876
  }
856
877
 
878
+ // src/client/provisioning-methods.ts
879
+ var USER_PROVISION_ENDPOINT = "/api/v1/ext/users/provision";
880
+ var ORGANIZATION_PROVISION_ENDPOINT = "/api/v1/ext/organizations/provision";
881
+ var MAX_PROVISION_MEMBERS = 100;
882
+ var ASSIGNABLE_MEMBER_ROLES = {
883
+ admin: true,
884
+ member: true,
885
+ viewer: true
886
+ };
887
+ function isAssignableMemberRole(role) {
888
+ return Object.hasOwn(ASSIGNABLE_MEMBER_ROLES, role);
889
+ }
890
+ function createProvisioningMethods(deps) {
891
+ const { fetchWithAuth, createError, authMode } = deps;
892
+ function serviceKeyModeError() {
893
+ if (authMode === "service-key") return null;
894
+ return createError(
895
+ "VALIDATION_ERROR",
896
+ "provisionUser / provisionOrganization \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"
897
+ );
898
+ }
899
+ async function provisionUser(params) {
900
+ const modeError = serviceKeyModeError();
901
+ if (modeError) return { error: modeError };
902
+ if (!params.email || !params.email.trim()) {
903
+ return { error: createError("VALIDATION_ERROR", "email \u306F\u5FC5\u9808\u3067\u3059") };
904
+ }
905
+ const body = { email: params.email.trim() };
906
+ if (params.profile !== void 0) body.profile = params.profile;
907
+ if (params.dryRun !== void 0) body.dryRun = params.dryRun;
908
+ return fetchWithAuth(USER_PROVISION_ENDPOINT, {
909
+ method: "POST",
910
+ body: JSON.stringify(body)
911
+ });
912
+ }
913
+ async function provisionOrganization(params) {
914
+ const modeError = serviceKeyModeError();
915
+ if (modeError) return { error: modeError };
916
+ if (!params.name || !params.name.trim()) {
917
+ return { error: createError("VALIDATION_ERROR", "name \u306F\u5FC5\u9808\u3067\u3059") };
918
+ }
919
+ if (!params.ownerEmail || !params.ownerEmail.trim()) {
920
+ return { error: createError("VALIDATION_ERROR", "ownerEmail \u306F\u5FC5\u9808\u3067\u3059") };
921
+ }
922
+ if (params.members) {
923
+ if (params.members.length > MAX_PROVISION_MEMBERS) {
924
+ return {
925
+ error: createError(
926
+ "VALIDATION_ERROR",
927
+ `members \u306F1\u30EA\u30AF\u30A8\u30B9\u30C8\u3042\u305F\u308A\u6700\u5927 ${MAX_PROVISION_MEMBERS} \u4EF6\u307E\u3067\u3067\u3059`
928
+ )
929
+ };
930
+ }
931
+ for (const member of params.members) {
932
+ if (!member.email || !member.email.trim()) {
933
+ return { error: createError("VALIDATION_ERROR", "members[].email \u306F\u5FC5\u9808\u3067\u3059") };
934
+ }
935
+ if (!isAssignableMemberRole(member.role)) {
936
+ return {
937
+ error: createError(
938
+ "VALIDATION_ERROR",
939
+ "members[].role \u306F admin\u3001member\u3001viewer \u306E\u3044\u305A\u308C\u304B\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044"
940
+ )
941
+ };
942
+ }
943
+ }
944
+ }
945
+ const body = {
946
+ name: params.name.trim(),
947
+ ownerEmail: params.ownerEmail.trim()
948
+ };
949
+ if (params.billingEmail !== void 0) body.billingEmail = params.billingEmail.trim();
950
+ if (params.members !== void 0) {
951
+ body.members = params.members.map((member) => ({
952
+ email: member.email.trim(),
953
+ role: member.role
954
+ }));
955
+ }
956
+ if (params.dryRun !== void 0) body.dryRun = params.dryRun;
957
+ return fetchWithAuth(ORGANIZATION_PROVISION_ENDPOINT, {
958
+ method: "POST",
959
+ body: JSON.stringify(body)
960
+ });
961
+ }
962
+ return { provisionUser, provisionOrganization };
963
+ }
964
+
857
965
  // src/client/seat-methods.ts
858
966
  function seatsEndpoint(subscriptionId) {
859
967
  return `/api/v1/subscriptions/ext/${encodeURIComponent(subscriptionId)}/seats/assignments`;
@@ -1098,7 +1206,7 @@ function createNonContractMethods(deps) {
1098
1206
  }
1099
1207
 
1100
1208
  // src/client/version-check.ts
1101
- var SDK_VERSION = "5.17.1";
1209
+ var SDK_VERSION = "5.19.0";
1102
1210
  var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
1103
1211
  var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
1104
1212
  function sdkHeaders() {
@@ -2695,6 +2803,19 @@ function isBlockedFromUserinfo(sub) {
2695
2803
  return BLOCKING_EFFECTIVE_STATUSES.includes(effectiveStatus);
2696
2804
  }
2697
2805
 
2806
+ // src/client/error-codes.ts
2807
+ var FFID_ERROR_CODES = {
2808
+ NETWORK_ERROR: "NETWORK_ERROR",
2809
+ PARSE_ERROR: "PARSE_ERROR",
2810
+ UNKNOWN_ERROR: "UNKNOWN_ERROR",
2811
+ TOKEN_EXCHANGE_ERROR: "TOKEN_EXCHANGE_ERROR",
2812
+ TOKEN_REFRESH_ERROR: "TOKEN_REFRESH_ERROR",
2813
+ NO_TOKENS: "NO_TOKENS",
2814
+ TOKEN_VERIFICATION_ERROR: "TOKEN_VERIFICATION_ERROR",
2815
+ TOKEN_EXPIRED: "TOKEN_EXPIRED",
2816
+ STATE_MISMATCH_ERROR: "STATE_MISMATCH_ERROR"
2817
+ };
2818
+
2698
2819
  // src/client/ffid-client.ts
2699
2820
  var UNAUTHORIZED_STATUS2 = 401;
2700
2821
  var SDK_LOG_PREFIX = "[FFID SDK]";
@@ -2703,8 +2824,10 @@ var noopLogger = {
2703
2824
  },
2704
2825
  info: () => {
2705
2826
  },
2706
- warn: (...args) => console.warn(SDK_LOG_PREFIX, ...args),
2707
- error: (...args) => console.error(SDK_LOG_PREFIX, ...args)
2827
+ warn: () => {
2828
+ },
2829
+ error: () => {
2830
+ }
2708
2831
  };
2709
2832
  var consoleLogger = {
2710
2833
  debug: (...args) => console.debug(SDK_LOG_PREFIX, ...args),
@@ -2712,16 +2835,6 @@ var consoleLogger = {
2712
2835
  warn: (...args) => console.warn(SDK_LOG_PREFIX, ...args),
2713
2836
  error: (...args) => console.error(SDK_LOG_PREFIX, ...args)
2714
2837
  };
2715
- var FFID_ERROR_CODES = {
2716
- NETWORK_ERROR: "NETWORK_ERROR",
2717
- PARSE_ERROR: "PARSE_ERROR",
2718
- UNKNOWN_ERROR: "UNKNOWN_ERROR",
2719
- TOKEN_EXCHANGE_ERROR: "TOKEN_EXCHANGE_ERROR",
2720
- TOKEN_REFRESH_ERROR: "TOKEN_REFRESH_ERROR",
2721
- NO_TOKENS: "NO_TOKENS",
2722
- TOKEN_VERIFICATION_ERROR: "TOKEN_VERIFICATION_ERROR",
2723
- STATE_MISMATCH_ERROR: "STATE_MISMATCH_ERROR"
2724
- };
2725
2838
  var EXT_CHECK_ENDPOINT = "/api/v1/subscriptions/ext/check";
2726
2839
  var DEFAULT_ALLOW_GRACE = true;
2727
2840
  var DEFAULT_SERVICE_ACCESS_FAIL_POLICY = "failClosed";
@@ -3104,6 +3217,11 @@ function createFFIDClient(config) {
3104
3217
  createError,
3105
3218
  serviceCode: config.serviceCode
3106
3219
  });
3220
+ const { provisionUser, provisionOrganization } = createProvisioningMethods({
3221
+ fetchWithAuth,
3222
+ createError,
3223
+ authMode
3224
+ });
3107
3225
  const { getProfile, updateProfile } = createProfileMethods({
3108
3226
  fetchWithAuth,
3109
3227
  createError
@@ -3199,6 +3317,8 @@ function createFFIDClient(config) {
3199
3317
  addMember,
3200
3318
  updateMemberRole,
3201
3319
  removeMember,
3320
+ provisionUser,
3321
+ provisionOrganization,
3202
3322
  getProfile,
3203
3323
  updateProfile,
3204
3324
  // Non-contract ext API coverage (#3783)
@@ -5351,6 +5471,7 @@ exports.FFIDSDKError = FFIDSDKError;
5351
5471
  exports.FFIDSubscriptionBadge = FFIDSubscriptionBadge;
5352
5472
  exports.FFIDUserMenu = FFIDUserMenu;
5353
5473
  exports.FFID_ANNOUNCEMENTS_ERROR_CODES = FFID_ANNOUNCEMENTS_ERROR_CODES;
5474
+ exports.FFID_ERROR_CODES = FFID_ERROR_CODES;
5354
5475
  exports.FFID_INQUIRY_CATEGORIES = FFID_INQUIRY_CATEGORIES;
5355
5476
  exports.FFID_INQUIRY_CATEGORIES_SITE_2026 = FFID_INQUIRY_CATEGORIES_SITE_2026;
5356
5477
  exports.cleanupStateStorage = cleanupStateStorage;
@@ -1,34 +1,34 @@
1
1
  'use strict';
2
2
 
3
- var chunkA2KK6L63_cjs = require('../chunk-A2KK6L63.cjs');
3
+ var chunkTW2FXASO_cjs = require('../chunk-TW2FXASO.cjs');
4
4
 
5
5
 
6
6
 
7
7
  Object.defineProperty(exports, "FFIDAnnouncementBadge", {
8
8
  enumerable: true,
9
- get: function () { return chunkA2KK6L63_cjs.FFIDAnnouncementBadge; }
9
+ get: function () { return chunkTW2FXASO_cjs.FFIDAnnouncementBadge; }
10
10
  });
11
11
  Object.defineProperty(exports, "FFIDAnnouncementList", {
12
12
  enumerable: true,
13
- get: function () { return chunkA2KK6L63_cjs.FFIDAnnouncementList; }
13
+ get: function () { return chunkTW2FXASO_cjs.FFIDAnnouncementList; }
14
14
  });
15
15
  Object.defineProperty(exports, "FFIDInquiryForm", {
16
16
  enumerable: true,
17
- get: function () { return chunkA2KK6L63_cjs.FFIDInquiryForm; }
17
+ get: function () { return chunkTW2FXASO_cjs.FFIDInquiryForm; }
18
18
  });
19
19
  Object.defineProperty(exports, "FFIDLoginButton", {
20
20
  enumerable: true,
21
- get: function () { return chunkA2KK6L63_cjs.FFIDLoginButton; }
21
+ get: function () { return chunkTW2FXASO_cjs.FFIDLoginButton; }
22
22
  });
23
23
  Object.defineProperty(exports, "FFIDOrganizationSwitcher", {
24
24
  enumerable: true,
25
- get: function () { return chunkA2KK6L63_cjs.FFIDOrganizationSwitcher; }
25
+ get: function () { return chunkTW2FXASO_cjs.FFIDOrganizationSwitcher; }
26
26
  });
27
27
  Object.defineProperty(exports, "FFIDSubscriptionBadge", {
28
28
  enumerable: true,
29
- get: function () { return chunkA2KK6L63_cjs.FFIDSubscriptionBadge; }
29
+ get: function () { return chunkTW2FXASO_cjs.FFIDSubscriptionBadge; }
30
30
  });
31
31
  Object.defineProperty(exports, "FFIDUserMenu", {
32
32
  enumerable: true,
33
- get: function () { return chunkA2KK6L63_cjs.FFIDUserMenu; }
33
+ get: function () { return chunkTW2FXASO_cjs.FFIDUserMenu; }
34
34
  });
@@ -1 +1 @@
1
- export { FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDInquiryForm, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDSubscriptionBadge, FFIDUserMenu } from '../chunk-26X62P76.js';
1
+ export { FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDInquiryForm, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDSubscriptionBadge, FFIDUserMenu } from '../chunk-D3PZ6SZB.js';