@feelflow/ffid-sdk 7.2.0 → 8.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.
@@ -155,7 +155,7 @@ function normalizeUserinfo(raw) {
155
155
  emailVerified: raw.email_verified,
156
156
  name: raw.name,
157
157
  picture: raw.picture,
158
- companyName: raw.company_name ?? null,
158
+ companyName: null,
159
159
  department: raw.department ?? null,
160
160
  position: raw.position ?? null,
161
161
  phoneNumber: raw.phone_number ?? null,
@@ -464,7 +464,7 @@ function createVerifyAccessToken(deps) {
464
464
  email_verified: introspectResponse.email_verified,
465
465
  name: introspectResponse.name ?? null,
466
466
  picture: introspectResponse.picture ?? null,
467
- company_name: introspectResponse.company_name ?? null,
467
+ company_name: null,
468
468
  department: introspectResponse.department ?? null,
469
469
  position: introspectResponse.position ?? null,
470
470
  phone_number: introspectResponse.phone_number ?? null,
@@ -715,6 +715,24 @@ function validateSeatChangePreview(preview) {
715
715
  `SDK: server returned malformed SeatChangePreview \u2014 estimateReason must be one of 'no_stripe_data' | 'custom_pricing' | 'stripe_error' (got ${JSON.stringify(p.estimateReason)})`
716
716
  );
717
717
  }
718
+ if (p.willApplyAtPeriodEnd !== void 0 && typeof p.willApplyAtPeriodEnd !== "boolean") {
719
+ throw new FFIDSDKError(
720
+ MALFORMED_SEAT_PREVIEW_CODE,
721
+ `SDK: server returned malformed SeatChangePreview \u2014 willApplyAtPeriodEnd must be boolean when present (got ${typeof p.willApplyAtPeriodEnd})`
722
+ );
723
+ }
724
+ if (p.effectiveDate !== void 0 && p.effectiveDate !== null && typeof p.effectiveDate !== "string") {
725
+ throw new FFIDSDKError(
726
+ MALFORMED_SEAT_PREVIEW_CODE,
727
+ `SDK: server returned malformed SeatChangePreview \u2014 effectiveDate must be string or null when present (got ${typeof p.effectiveDate})`
728
+ );
729
+ }
730
+ if (p.willApplyAtPeriodEnd === true && p.proratedAmount !== 0) {
731
+ throw new FFIDSDKError(
732
+ MALFORMED_SEAT_PREVIEW_CODE,
733
+ `SDK: server returned malformed SeatChangePreview \u2014 willApplyAtPeriodEnd=true requires proratedAmount=0 (got ${JSON.stringify(p.proratedAmount)})`
734
+ );
735
+ }
718
736
  if (!Array.isArray(p.lineItems)) {
719
737
  throw new FFIDSDKError(
720
738
  MALFORMED_SEAT_PREVIEW_CODE,
@@ -1355,7 +1373,7 @@ var FFID_ORGANIZATION_PROFILE_ERROR_CODES = {
1355
1373
  };
1356
1374
 
1357
1375
  // src/client/version-check.ts
1358
- var SDK_VERSION = "7.2.0";
1376
+ var SDK_VERSION = "8.0.0";
1359
1377
  var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
1360
1378
  var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
1361
1379
  function sdkHeaders() {
@@ -2514,6 +2532,86 @@ function cleanupStateStorage(logger) {
2514
2532
  }
2515
2533
  }
2516
2534
 
2535
+ // src/client/agency-invite-token.ts
2536
+ var AGENCY_INVITE_TOKEN_PARAM = "invite";
2537
+ var AGENCY_INVITE_COOKIE_NAME = "ffid_invite";
2538
+ var AGENCY_INVITE_TOKEN_REDACTION = "[REDACTED]";
2539
+ function hasDocument() {
2540
+ return typeof document !== "undefined";
2541
+ }
2542
+ function toQueryString(source) {
2543
+ const queryStart = source.indexOf("?");
2544
+ if (queryStart < 0) return "";
2545
+ const afterQuery = source.slice(queryStart);
2546
+ const fragmentStart = afterQuery.indexOf("#");
2547
+ return fragmentStart < 0 ? afterQuery : afterQuery.slice(0, fragmentStart);
2548
+ }
2549
+ function readTokenFromUrl(options) {
2550
+ const source = options.url ?? (typeof window !== "undefined" ? window.location.search : void 0);
2551
+ if (source === void 0) return void 0;
2552
+ const params = new URLSearchParams(toQueryString(source));
2553
+ return params.get(AGENCY_INVITE_TOKEN_PARAM)?.trim() || void 0;
2554
+ }
2555
+ function readAgencyInviteCookie() {
2556
+ if (!hasDocument()) return void 0;
2557
+ const prefix = `${AGENCY_INVITE_COOKIE_NAME}=`;
2558
+ const raw = document.cookie.split(";").map((entry) => entry.trim()).find((entry) => entry.startsWith(prefix));
2559
+ if (!raw) return void 0;
2560
+ try {
2561
+ return decodeURIComponent(raw.slice(prefix.length)).trim() || void 0;
2562
+ } catch {
2563
+ return void 0;
2564
+ }
2565
+ }
2566
+ function readAgencyInviteToken(options = {}) {
2567
+ return readTokenFromUrl(options) ?? readAgencyInviteCookie();
2568
+ }
2569
+ var REDACT_BOUNDARIES = [
2570
+ "%2526",
2571
+ "%2523",
2572
+ "%253B",
2573
+ "%26",
2574
+ "%23",
2575
+ "%3B",
2576
+ "&",
2577
+ "#",
2578
+ ";"
2579
+ ];
2580
+ var REDACT_LEADS = ["%253F", "%3F", "?", ...REDACT_BOUNDARIES];
2581
+ var REDACT_SEPARATORS = ["%253D", "%3D", "="];
2582
+ function escapeForRegExp(literal) {
2583
+ return literal.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2584
+ }
2585
+ var REDACT_PATTERN = new RegExp(
2586
+ [
2587
+ // 先頭区切り。`\b` が使えないのは、入れ子形 (`%3Finvite%3D`) では `F` と `i` が
2588
+ // どちらも単語文字なので単語境界が存在しないため。
2589
+ `(^|${REDACT_LEADS.map(escapeForRegExp).join("|")})`,
2590
+ // パラメータ名と区切りはキャプチャして再出力する。`i` フラグがあるので、
2591
+ // 入力の `INVITE=` / `%3d` などの元の表記を保つ必要がある。
2592
+ `(${AGENCY_INVITE_TOKEN_PARAM})`,
2593
+ `(${REDACT_SEPARATORS.map(escapeForRegExp).join("|")})`,
2594
+ // 値。境界の直前まで貪欲に食う。`\s` は終端にしない — 生の空白や改行を含む
2595
+ // トークンでマスクが途中で切れ、後半が漏れる。
2596
+ `(?:(?!${REDACT_BOUNDARIES.map(escapeForRegExp).join("|")})[^&#;])*`
2597
+ ].join(""),
2598
+ "gi"
2599
+ );
2600
+ function redactAgencyInviteToken(url) {
2601
+ return url.replace(
2602
+ REDACT_PATTERN,
2603
+ // 文字列 replacement(`'$1$2$3[REDACTED]'`)にしないのは、
2604
+ // `AGENCY_INVITE_TOKEN_REDACTION` に `$` が入った瞬間に置換パターンとして
2605
+ // 解釈される罠を作らないため。
2606
+ (_match, lead, name, separator) => `${lead}${name}${separator}${AGENCY_INVITE_TOKEN_REDACTION}`
2607
+ );
2608
+ }
2609
+ function appendAgencyInviteToken(signupUrl, token) {
2610
+ if (token === void 0) return signupUrl;
2611
+ const separator = signupUrl.includes("?") ? "&" : "?";
2612
+ return `${signupUrl}${separator}${AGENCY_INVITE_TOKEN_PARAM}=${encodeURIComponent(token)}`;
2613
+ }
2614
+
2517
2615
  // src/client/redirect.ts
2518
2616
  var OAUTH_AUTHORIZE_ENDPOINT = "/api/v1/oauth/authorize";
2519
2617
  var AUTH_LOGOUT_ENDPOINT = "/api/v1/auth/logout";
@@ -2526,6 +2624,7 @@ var REDIRECT_LOOP_WINDOW_MS = 6e4;
2526
2624
  var REDIRECT_LOOP_THRESHOLD = 3;
2527
2625
  var storageReadFailureLogged = false;
2528
2626
  var storageWriteFailureLogged = false;
2627
+ var ssrInviteTokenUnavailableLogged = false;
2529
2628
  function isLoopDetectionDegraded() {
2530
2629
  return storageReadFailureLogged || storageWriteFailureLogged;
2531
2630
  }
@@ -2704,12 +2803,16 @@ function createRedirectMethods(deps) {
2704
2803
  }
2705
2804
  if (options?.screenHint === SCREEN_HINT_SIGNUP) {
2706
2805
  params.set(SCREEN_HINT_PARAM, SCREEN_HINT_SIGNUP);
2806
+ const inviteToken = readAgencyInviteToken();
2807
+ if (inviteToken !== void 0) {
2808
+ params.set(AGENCY_INVITE_TOKEN_PARAM, inviteToken);
2809
+ }
2707
2810
  }
2708
2811
  if (options?.prompt) {
2709
2812
  params.set("prompt", options.prompt);
2710
2813
  }
2711
2814
  const authorizeUrl = `${baseUrl}${OAUTH_AUTHORIZE_ENDPOINT}?${params.toString()}`;
2712
- logger.debug("Redirecting to authorize:", authorizeUrl);
2815
+ logger.debug("Redirecting to authorize:", redactAgencyInviteToken(authorizeUrl));
2713
2816
  recordRedirectAttempt(authorizeKey, now, logger);
2714
2817
  try {
2715
2818
  window.location.href = authorizeUrl;
@@ -2728,7 +2831,7 @@ function createRedirectMethods(deps) {
2728
2831
  return redirectToAuthorize(options);
2729
2832
  }
2730
2833
  const targetUrl = options?.screenHint === SCREEN_HINT_SIGNUP ? getSignupUrl() : getLoginUrl(void 0, options?.prompt ? { prompt: options.prompt } : void 0);
2731
- logger.debug("Redirecting to auth page:", targetUrl);
2834
+ logger.debug("Redirecting to auth page:", redactAgencyInviteToken(targetUrl));
2732
2835
  window.location.href = targetUrl;
2733
2836
  return { success: true };
2734
2837
  }
@@ -2745,7 +2848,7 @@ function createRedirectMethods(deps) {
2745
2848
  const promptSuffix = options?.prompt ? `&prompt=${encodeURIComponent(options.prompt)}` : "";
2746
2849
  return `${baseUrl}/login?redirect=${encodeURIComponent(redirect)}&service=${encodeURIComponent(serviceCode)}${promptSuffix}`;
2747
2850
  }
2748
- function getSignupUrl(redirectUrl) {
2851
+ function getSignupUrl(redirectUrl, options) {
2749
2852
  let redirect;
2750
2853
  if (redirectUrl != null) {
2751
2854
  redirect = redirectUrl;
@@ -2755,7 +2858,22 @@ function createRedirectMethods(deps) {
2755
2858
  logger.warn("getSignupUrl: SSR \u74B0\u5883\u3067 redirectUrl \u304C\u672A\u6307\u5B9A\u306E\u305F\u3081\u7A7A\u6587\u5B57\u306B\u30D5\u30A9\u30FC\u30EB\u30D0\u30C3\u30AF\u3057\u307E\u3059");
2756
2859
  redirect = "";
2757
2860
  }
2758
- return `${baseUrl}/signup?redirect=${encodeURIComponent(redirect)}&service=${encodeURIComponent(serviceCode)}`;
2861
+ let inviteToken = options?.inviteToken;
2862
+ if (inviteToken === void 0) {
2863
+ inviteToken = readAgencyInviteToken();
2864
+ if (inviteToken === void 0 && typeof window === "undefined") {
2865
+ if (!ssrInviteTokenUnavailableLogged) {
2866
+ ssrInviteTokenUnavailableLogged = true;
2867
+ logger.warn(
2868
+ "[FFID SDK] getSignupUrl: SSR \u3067\u306F\u62DB\u5F85\u30C8\u30FC\u30AF\u30F3\uFF08invite\uFF09\u3092\u8AAD\u3081\u307E\u305B\u3093\u3002\u4EE3\u7406\u5E97\u306E\u62DB\u5F85\u30EA\u30F3\u30AF\u3092\u4F7F\u3046\u5834\u5408\u306F options.inviteToken \u306B\u6E21\u3057\u3066\u304F\u3060\u3055\u3044\uFF08\u4F7F\u3063\u3066\u3044\u306A\u3044\u5834\u5408\u306F\u3053\u306E\u8B66\u544A\u3092\u7121\u8996\u3057\u3066\u3088\u3044\uFF09"
2869
+ );
2870
+ }
2871
+ }
2872
+ }
2873
+ return appendAgencyInviteToken(
2874
+ `${baseUrl}/signup?redirect=${encodeURIComponent(redirect)}&service=${encodeURIComponent(serviceCode)}`,
2875
+ inviteToken
2876
+ );
2759
2877
  }
2760
2878
  function getLogoutUrl(postLogoutRedirectUri) {
2761
2879
  const url = new URL(`${baseUrl}${AUTH_LOGOUT_ENDPOINT}`);
@@ -2771,7 +2889,7 @@ function createRedirectMethods(deps) {
2771
2889
  return { success: false, error: "SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093" };
2772
2890
  }
2773
2891
  const logoutUrl = getLogoutUrl(postLogoutRedirectUri);
2774
- logger.debug("Redirecting to logout:", logoutUrl);
2892
+ logger.debug("Redirecting to logout:", redactAgencyInviteToken(logoutUrl));
2775
2893
  window.location.href = logoutUrl;
2776
2894
  return { success: true };
2777
2895
  }
@@ -4325,9 +4443,9 @@ function createServerAuthClient(config) {
4325
4443
  var FFID_SESSION_COOKIE_NAME = "ffid_session";
4326
4444
  var DEFAULT_PATH = "/";
4327
4445
  var DEFAULT_SAME_SITE = "lax";
4328
- var HOURS_PER_DAY2 = 24;
4446
+ var HOURS_PER_DAY3 = 24;
4329
4447
  var DEFAULT_MAX_AGE_DAYS = 30;
4330
- var DEFAULT_MAX_AGE_SECONDS = 60 * 60 * HOURS_PER_DAY2 * DEFAULT_MAX_AGE_DAYS;
4448
+ var DEFAULT_MAX_AGE_SECONDS = 60 * 60 * HOURS_PER_DAY3 * DEFAULT_MAX_AGE_DAYS;
4331
4449
  var BASE64_GROUP = 4;
4332
4450
  function base64UrlEncode2(input) {
4333
4451
  const bytes = new TextEncoder().encode(input);
@@ -1,5 +1,5 @@
1
- import { F as FFIDLogger, a as FFIDError, b as FFIDCacheAdapter, c as FFIDVerifyAccessTokenOptions, d as FFIDApiResponse, e as FFIDOAuthUserInfo, f as FFIDCatalogEnvironment, g as FFIDBillingInterval, h as FFIDTaxBehavior, i as FFIDPublishedCatalog } from '../ffid-client-BFgoVPOZ.cjs';
2
- export { j as FFIDAddMemberParams, k as FFIDAddMemberRequest, l as FFIDAddMemberResponse, m as FFIDAllowedLogoMimeType, n as FFIDAssignableMemberRole, o as FFIDCacheConfig, p as FFIDCatalogPublication, q as FFIDCatalogPublicationStatus, r as FFIDClient, s as FFIDCompanySizeCode, t as FFIDConfig, u as FFIDIndustryCode, v as FFIDListMembersResponse, w as FFIDListingConsentInput, x as FFIDLogoWallEntry, y as FFIDLogoWallResponse, z as FFIDMemberRole, A as FFIDOrganization, B as FFIDOrganizationLogoUploadResponse, C as FFIDOrganizationMember, D as FFIDOrganizationProfile, E as FFIDOrganizationProfileErrorCode, G as FFIDOrganizationProfileResponse, H as FFIDOtpSendResponse, I as FFIDOtpVerifyResponse, J as FFIDPasswordResetConfirmResponse, K as FFIDPasswordResetResponse, L as FFIDPasswordResetVerifyResponse, M as FFIDProfileCallOptions, N as FFIDProvisionMemberPlan, O as FFIDProvisionMemberPlanStatus, P as FFIDProvisionMemberResult, Q as FFIDProvisionMemberStatus, R as FFIDProvisionOrganizationDryRun, S as FFIDProvisionOrganizationMemberInput, T as FFIDProvisionOrganizationOutcome, U as FFIDProvisionOrganizationParams, V as FFIDProvisionOrganizationResponse, W as FFIDProvisionUserDryRun, X as FFIDProvisionUserOutcome, Y as FFIDProvisionUserParams, Z as FFIDProvisionUserProfileInput, _ as FFIDProvisionUserResponse, $ as FFIDProvisionedOrganization, a0 as FFIDProvisionedUser, a1 as FFIDPublishedPlan, a2 as FFIDRemoveMemberResponse, a3 as FFIDResetSessionResponse, a4 as FFIDSubscription, a5 as FFIDUpdateMemberRoleResponse, a6 as FFIDUpdateUserProfileRequest, a7 as FFIDUploadOrganizationLogoParams, a8 as FFIDUpsertOrganizationProfileParams, a9 as FFIDUpsertOrganizationProfileRequest, aa as FFIDUser, ab as FFIDUserProfile, ac as FFID_ALLOWED_ORGANIZATION_LOGO_MIME_TYPES, ad as FFID_CATALOG_ENVIRONMENTS, ae as FFID_CATALOG_PUBLICATION_STATUSES, af as FFID_COMPANY_SIZE_CODES, ag as FFID_INDUSTRY_CODES, ah as FFID_MAX_ORGANIZATION_LOGO_FILE_SIZE_BYTES, ai as FFID_MAX_ORGANIZATION_LOGO_FILE_SIZE_MB, aj as FFID_ORGANIZATION_PROFILE_ERROR_CODES, ak as TokenData, al as TokenStore, am as createFFIDClient, an as createTokenStore } from '../ffid-client-BFgoVPOZ.cjs';
1
+ import { F as FFIDLogger, a as FFIDError, b as FFIDCacheAdapter, c as FFIDVerifyAccessTokenOptions, d as FFIDApiResponse, e as FFIDOAuthUserInfo, f as FFIDCatalogEnvironment, g as FFIDBillingInterval, h as FFIDTaxBehavior, i as FFIDPublishedCatalog } from '../ffid-client-BaEmw7jP.cjs';
2
+ export { j as FFIDAddMemberParams, k as FFIDAddMemberRequest, l as FFIDAddMemberResponse, m as FFIDAllowedLogoMimeType, n as FFIDAssignableMemberRole, o as FFIDCacheConfig, p as FFIDCatalogPublication, q as FFIDCatalogPublicationStatus, r as FFIDClient, s as FFIDCompanySizeCode, t as FFIDConfig, u as FFIDIndustryCode, v as FFIDListMembersResponse, w as FFIDListingConsentInput, x as FFIDLogoWallEntry, y as FFIDLogoWallResponse, z as FFIDMemberRole, A as FFIDOrganization, B as FFIDOrganizationLogoUploadResponse, C as FFIDOrganizationMember, D as FFIDOrganizationProfile, E as FFIDOrganizationProfileErrorCode, G as FFIDOrganizationProfileResponse, H as FFIDOtpSendResponse, I as FFIDOtpVerifyResponse, J as FFIDPasswordResetConfirmResponse, K as FFIDPasswordResetResponse, L as FFIDPasswordResetVerifyResponse, M as FFIDProfileCallOptions, N as FFIDProvisionMemberPlan, O as FFIDProvisionMemberPlanStatus, P as FFIDProvisionMemberResult, Q as FFIDProvisionMemberStatus, R as FFIDProvisionOrganizationDryRun, S as FFIDProvisionOrganizationMemberInput, T as FFIDProvisionOrganizationOutcome, U as FFIDProvisionOrganizationParams, V as FFIDProvisionOrganizationResponse, W as FFIDProvisionUserDryRun, X as FFIDProvisionUserOutcome, Y as FFIDProvisionUserParams, Z as FFIDProvisionUserProfileInput, _ as FFIDProvisionUserResponse, $ as FFIDProvisionedOrganization, a0 as FFIDProvisionedUser, a1 as FFIDPublishedPlan, a2 as FFIDRemoveMemberResponse, a3 as FFIDResetSessionResponse, a4 as FFIDSubscription, a5 as FFIDUpdateMemberRoleResponse, a6 as FFIDUpdateUserProfileRequest, a7 as FFIDUploadOrganizationLogoParams, a8 as FFIDUpsertOrganizationProfileParams, a9 as FFIDUpsertOrganizationProfileRequest, aa as FFIDUser, ab as FFIDUserProfile, ac as FFID_ALLOWED_ORGANIZATION_LOGO_MIME_TYPES, ad as FFID_CATALOG_ENVIRONMENTS, ae as FFID_CATALOG_PUBLICATION_STATUSES, af as FFID_COMPANY_SIZE_CODES, ag as FFID_INDUSTRY_CODES, ah as FFID_MAX_ORGANIZATION_LOGO_FILE_SIZE_BYTES, ai as FFID_MAX_ORGANIZATION_LOGO_FILE_SIZE_MB, aj as FFID_ORGANIZATION_PROFILE_ERROR_CODES, ak as TokenData, al as TokenStore, am as createFFIDClient, an as createTokenStore } from '../ffid-client-BaEmw7jP.cjs';
3
3
  export { D as DEFAULT_API_BASE_URL, a as DEFAULT_OAUTH_SCOPES } from '../constants-Ce2eXQtB.cjs';
4
4
  import { z } from 'zod';
5
5
  import '../types-Cjb9J0rL.cjs';
@@ -1,5 +1,5 @@
1
- import { F as FFIDLogger, a as FFIDError, b as FFIDCacheAdapter, c as FFIDVerifyAccessTokenOptions, d as FFIDApiResponse, e as FFIDOAuthUserInfo, f as FFIDCatalogEnvironment, g as FFIDBillingInterval, h as FFIDTaxBehavior, i as FFIDPublishedCatalog } from '../ffid-client-QNEFA3mv.js';
2
- export { j as FFIDAddMemberParams, k as FFIDAddMemberRequest, l as FFIDAddMemberResponse, m as FFIDAllowedLogoMimeType, n as FFIDAssignableMemberRole, o as FFIDCacheConfig, p as FFIDCatalogPublication, q as FFIDCatalogPublicationStatus, r as FFIDClient, s as FFIDCompanySizeCode, t as FFIDConfig, u as FFIDIndustryCode, v as FFIDListMembersResponse, w as FFIDListingConsentInput, x as FFIDLogoWallEntry, y as FFIDLogoWallResponse, z as FFIDMemberRole, A as FFIDOrganization, B as FFIDOrganizationLogoUploadResponse, C as FFIDOrganizationMember, D as FFIDOrganizationProfile, E as FFIDOrganizationProfileErrorCode, G as FFIDOrganizationProfileResponse, H as FFIDOtpSendResponse, I as FFIDOtpVerifyResponse, J as FFIDPasswordResetConfirmResponse, K as FFIDPasswordResetResponse, L as FFIDPasswordResetVerifyResponse, M as FFIDProfileCallOptions, N as FFIDProvisionMemberPlan, O as FFIDProvisionMemberPlanStatus, P as FFIDProvisionMemberResult, Q as FFIDProvisionMemberStatus, R as FFIDProvisionOrganizationDryRun, S as FFIDProvisionOrganizationMemberInput, T as FFIDProvisionOrganizationOutcome, U as FFIDProvisionOrganizationParams, V as FFIDProvisionOrganizationResponse, W as FFIDProvisionUserDryRun, X as FFIDProvisionUserOutcome, Y as FFIDProvisionUserParams, Z as FFIDProvisionUserProfileInput, _ as FFIDProvisionUserResponse, $ as FFIDProvisionedOrganization, a0 as FFIDProvisionedUser, a1 as FFIDPublishedPlan, a2 as FFIDRemoveMemberResponse, a3 as FFIDResetSessionResponse, a4 as FFIDSubscription, a5 as FFIDUpdateMemberRoleResponse, a6 as FFIDUpdateUserProfileRequest, a7 as FFIDUploadOrganizationLogoParams, a8 as FFIDUpsertOrganizationProfileParams, a9 as FFIDUpsertOrganizationProfileRequest, aa as FFIDUser, ab as FFIDUserProfile, ac as FFID_ALLOWED_ORGANIZATION_LOGO_MIME_TYPES, ad as FFID_CATALOG_ENVIRONMENTS, ae as FFID_CATALOG_PUBLICATION_STATUSES, af as FFID_COMPANY_SIZE_CODES, ag as FFID_INDUSTRY_CODES, ah as FFID_MAX_ORGANIZATION_LOGO_FILE_SIZE_BYTES, ai as FFID_MAX_ORGANIZATION_LOGO_FILE_SIZE_MB, aj as FFID_ORGANIZATION_PROFILE_ERROR_CODES, ak as TokenData, al as TokenStore, am as createFFIDClient, an as createTokenStore } from '../ffid-client-QNEFA3mv.js';
1
+ import { F as FFIDLogger, a as FFIDError, b as FFIDCacheAdapter, c as FFIDVerifyAccessTokenOptions, d as FFIDApiResponse, e as FFIDOAuthUserInfo, f as FFIDCatalogEnvironment, g as FFIDBillingInterval, h as FFIDTaxBehavior, i as FFIDPublishedCatalog } from '../ffid-client-5GAPcRIG.js';
2
+ export { j as FFIDAddMemberParams, k as FFIDAddMemberRequest, l as FFIDAddMemberResponse, m as FFIDAllowedLogoMimeType, n as FFIDAssignableMemberRole, o as FFIDCacheConfig, p as FFIDCatalogPublication, q as FFIDCatalogPublicationStatus, r as FFIDClient, s as FFIDCompanySizeCode, t as FFIDConfig, u as FFIDIndustryCode, v as FFIDListMembersResponse, w as FFIDListingConsentInput, x as FFIDLogoWallEntry, y as FFIDLogoWallResponse, z as FFIDMemberRole, A as FFIDOrganization, B as FFIDOrganizationLogoUploadResponse, C as FFIDOrganizationMember, D as FFIDOrganizationProfile, E as FFIDOrganizationProfileErrorCode, G as FFIDOrganizationProfileResponse, H as FFIDOtpSendResponse, I as FFIDOtpVerifyResponse, J as FFIDPasswordResetConfirmResponse, K as FFIDPasswordResetResponse, L as FFIDPasswordResetVerifyResponse, M as FFIDProfileCallOptions, N as FFIDProvisionMemberPlan, O as FFIDProvisionMemberPlanStatus, P as FFIDProvisionMemberResult, Q as FFIDProvisionMemberStatus, R as FFIDProvisionOrganizationDryRun, S as FFIDProvisionOrganizationMemberInput, T as FFIDProvisionOrganizationOutcome, U as FFIDProvisionOrganizationParams, V as FFIDProvisionOrganizationResponse, W as FFIDProvisionUserDryRun, X as FFIDProvisionUserOutcome, Y as FFIDProvisionUserParams, Z as FFIDProvisionUserProfileInput, _ as FFIDProvisionUserResponse, $ as FFIDProvisionedOrganization, a0 as FFIDProvisionedUser, a1 as FFIDPublishedPlan, a2 as FFIDRemoveMemberResponse, a3 as FFIDResetSessionResponse, a4 as FFIDSubscription, a5 as FFIDUpdateMemberRoleResponse, a6 as FFIDUpdateUserProfileRequest, a7 as FFIDUploadOrganizationLogoParams, a8 as FFIDUpsertOrganizationProfileParams, a9 as FFIDUpsertOrganizationProfileRequest, aa as FFIDUser, ab as FFIDUserProfile, ac as FFID_ALLOWED_ORGANIZATION_LOGO_MIME_TYPES, ad as FFID_CATALOG_ENVIRONMENTS, ae as FFID_CATALOG_PUBLICATION_STATUSES, af as FFID_COMPANY_SIZE_CODES, ag as FFID_INDUSTRY_CODES, ah as FFID_MAX_ORGANIZATION_LOGO_FILE_SIZE_BYTES, ai as FFID_MAX_ORGANIZATION_LOGO_FILE_SIZE_MB, aj as FFID_ORGANIZATION_PROFILE_ERROR_CODES, ak as TokenData, al as TokenStore, am as createFFIDClient, an as createTokenStore } from '../ffid-client-5GAPcRIG.js';
3
3
  export { D as DEFAULT_API_BASE_URL, a as DEFAULT_OAUTH_SCOPES } from '../constants-Ce2eXQtB.js';
4
4
  import { z } from 'zod';
5
5
  import '../types-Cjb9J0rL.js';
@@ -154,7 +154,7 @@ function normalizeUserinfo(raw) {
154
154
  emailVerified: raw.email_verified,
155
155
  name: raw.name,
156
156
  picture: raw.picture,
157
- companyName: raw.company_name ?? null,
157
+ companyName: null,
158
158
  department: raw.department ?? null,
159
159
  position: raw.position ?? null,
160
160
  phoneNumber: raw.phone_number ?? null,
@@ -463,7 +463,7 @@ function createVerifyAccessToken(deps) {
463
463
  email_verified: introspectResponse.email_verified,
464
464
  name: introspectResponse.name ?? null,
465
465
  picture: introspectResponse.picture ?? null,
466
- company_name: introspectResponse.company_name ?? null,
466
+ company_name: null,
467
467
  department: introspectResponse.department ?? null,
468
468
  position: introspectResponse.position ?? null,
469
469
  phone_number: introspectResponse.phone_number ?? null,
@@ -714,6 +714,24 @@ function validateSeatChangePreview(preview) {
714
714
  `SDK: server returned malformed SeatChangePreview \u2014 estimateReason must be one of 'no_stripe_data' | 'custom_pricing' | 'stripe_error' (got ${JSON.stringify(p.estimateReason)})`
715
715
  );
716
716
  }
717
+ if (p.willApplyAtPeriodEnd !== void 0 && typeof p.willApplyAtPeriodEnd !== "boolean") {
718
+ throw new FFIDSDKError(
719
+ MALFORMED_SEAT_PREVIEW_CODE,
720
+ `SDK: server returned malformed SeatChangePreview \u2014 willApplyAtPeriodEnd must be boolean when present (got ${typeof p.willApplyAtPeriodEnd})`
721
+ );
722
+ }
723
+ if (p.effectiveDate !== void 0 && p.effectiveDate !== null && typeof p.effectiveDate !== "string") {
724
+ throw new FFIDSDKError(
725
+ MALFORMED_SEAT_PREVIEW_CODE,
726
+ `SDK: server returned malformed SeatChangePreview \u2014 effectiveDate must be string or null when present (got ${typeof p.effectiveDate})`
727
+ );
728
+ }
729
+ if (p.willApplyAtPeriodEnd === true && p.proratedAmount !== 0) {
730
+ throw new FFIDSDKError(
731
+ MALFORMED_SEAT_PREVIEW_CODE,
732
+ `SDK: server returned malformed SeatChangePreview \u2014 willApplyAtPeriodEnd=true requires proratedAmount=0 (got ${JSON.stringify(p.proratedAmount)})`
733
+ );
734
+ }
717
735
  if (!Array.isArray(p.lineItems)) {
718
736
  throw new FFIDSDKError(
719
737
  MALFORMED_SEAT_PREVIEW_CODE,
@@ -1354,7 +1372,7 @@ var FFID_ORGANIZATION_PROFILE_ERROR_CODES = {
1354
1372
  };
1355
1373
 
1356
1374
  // src/client/version-check.ts
1357
- var SDK_VERSION = "7.2.0";
1375
+ var SDK_VERSION = "8.0.0";
1358
1376
  var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
1359
1377
  var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
1360
1378
  function sdkHeaders() {
@@ -2513,6 +2531,86 @@ function cleanupStateStorage(logger) {
2513
2531
  }
2514
2532
  }
2515
2533
 
2534
+ // src/client/agency-invite-token.ts
2535
+ var AGENCY_INVITE_TOKEN_PARAM = "invite";
2536
+ var AGENCY_INVITE_COOKIE_NAME = "ffid_invite";
2537
+ var AGENCY_INVITE_TOKEN_REDACTION = "[REDACTED]";
2538
+ function hasDocument() {
2539
+ return typeof document !== "undefined";
2540
+ }
2541
+ function toQueryString(source) {
2542
+ const queryStart = source.indexOf("?");
2543
+ if (queryStart < 0) return "";
2544
+ const afterQuery = source.slice(queryStart);
2545
+ const fragmentStart = afterQuery.indexOf("#");
2546
+ return fragmentStart < 0 ? afterQuery : afterQuery.slice(0, fragmentStart);
2547
+ }
2548
+ function readTokenFromUrl(options) {
2549
+ const source = options.url ?? (typeof window !== "undefined" ? window.location.search : void 0);
2550
+ if (source === void 0) return void 0;
2551
+ const params = new URLSearchParams(toQueryString(source));
2552
+ return params.get(AGENCY_INVITE_TOKEN_PARAM)?.trim() || void 0;
2553
+ }
2554
+ function readAgencyInviteCookie() {
2555
+ if (!hasDocument()) return void 0;
2556
+ const prefix = `${AGENCY_INVITE_COOKIE_NAME}=`;
2557
+ const raw = document.cookie.split(";").map((entry) => entry.trim()).find((entry) => entry.startsWith(prefix));
2558
+ if (!raw) return void 0;
2559
+ try {
2560
+ return decodeURIComponent(raw.slice(prefix.length)).trim() || void 0;
2561
+ } catch {
2562
+ return void 0;
2563
+ }
2564
+ }
2565
+ function readAgencyInviteToken(options = {}) {
2566
+ return readTokenFromUrl(options) ?? readAgencyInviteCookie();
2567
+ }
2568
+ var REDACT_BOUNDARIES = [
2569
+ "%2526",
2570
+ "%2523",
2571
+ "%253B",
2572
+ "%26",
2573
+ "%23",
2574
+ "%3B",
2575
+ "&",
2576
+ "#",
2577
+ ";"
2578
+ ];
2579
+ var REDACT_LEADS = ["%253F", "%3F", "?", ...REDACT_BOUNDARIES];
2580
+ var REDACT_SEPARATORS = ["%253D", "%3D", "="];
2581
+ function escapeForRegExp(literal) {
2582
+ return literal.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2583
+ }
2584
+ var REDACT_PATTERN = new RegExp(
2585
+ [
2586
+ // 先頭区切り。`\b` が使えないのは、入れ子形 (`%3Finvite%3D`) では `F` と `i` が
2587
+ // どちらも単語文字なので単語境界が存在しないため。
2588
+ `(^|${REDACT_LEADS.map(escapeForRegExp).join("|")})`,
2589
+ // パラメータ名と区切りはキャプチャして再出力する。`i` フラグがあるので、
2590
+ // 入力の `INVITE=` / `%3d` などの元の表記を保つ必要がある。
2591
+ `(${AGENCY_INVITE_TOKEN_PARAM})`,
2592
+ `(${REDACT_SEPARATORS.map(escapeForRegExp).join("|")})`,
2593
+ // 値。境界の直前まで貪欲に食う。`\s` は終端にしない — 生の空白や改行を含む
2594
+ // トークンでマスクが途中で切れ、後半が漏れる。
2595
+ `(?:(?!${REDACT_BOUNDARIES.map(escapeForRegExp).join("|")})[^&#;])*`
2596
+ ].join(""),
2597
+ "gi"
2598
+ );
2599
+ function redactAgencyInviteToken(url) {
2600
+ return url.replace(
2601
+ REDACT_PATTERN,
2602
+ // 文字列 replacement(`'$1$2$3[REDACTED]'`)にしないのは、
2603
+ // `AGENCY_INVITE_TOKEN_REDACTION` に `$` が入った瞬間に置換パターンとして
2604
+ // 解釈される罠を作らないため。
2605
+ (_match, lead, name, separator) => `${lead}${name}${separator}${AGENCY_INVITE_TOKEN_REDACTION}`
2606
+ );
2607
+ }
2608
+ function appendAgencyInviteToken(signupUrl, token) {
2609
+ if (token === void 0) return signupUrl;
2610
+ const separator = signupUrl.includes("?") ? "&" : "?";
2611
+ return `${signupUrl}${separator}${AGENCY_INVITE_TOKEN_PARAM}=${encodeURIComponent(token)}`;
2612
+ }
2613
+
2516
2614
  // src/client/redirect.ts
2517
2615
  var OAUTH_AUTHORIZE_ENDPOINT = "/api/v1/oauth/authorize";
2518
2616
  var AUTH_LOGOUT_ENDPOINT = "/api/v1/auth/logout";
@@ -2525,6 +2623,7 @@ var REDIRECT_LOOP_WINDOW_MS = 6e4;
2525
2623
  var REDIRECT_LOOP_THRESHOLD = 3;
2526
2624
  var storageReadFailureLogged = false;
2527
2625
  var storageWriteFailureLogged = false;
2626
+ var ssrInviteTokenUnavailableLogged = false;
2528
2627
  function isLoopDetectionDegraded() {
2529
2628
  return storageReadFailureLogged || storageWriteFailureLogged;
2530
2629
  }
@@ -2703,12 +2802,16 @@ function createRedirectMethods(deps) {
2703
2802
  }
2704
2803
  if (options?.screenHint === SCREEN_HINT_SIGNUP) {
2705
2804
  params.set(SCREEN_HINT_PARAM, SCREEN_HINT_SIGNUP);
2805
+ const inviteToken = readAgencyInviteToken();
2806
+ if (inviteToken !== void 0) {
2807
+ params.set(AGENCY_INVITE_TOKEN_PARAM, inviteToken);
2808
+ }
2706
2809
  }
2707
2810
  if (options?.prompt) {
2708
2811
  params.set("prompt", options.prompt);
2709
2812
  }
2710
2813
  const authorizeUrl = `${baseUrl}${OAUTH_AUTHORIZE_ENDPOINT}?${params.toString()}`;
2711
- logger.debug("Redirecting to authorize:", authorizeUrl);
2814
+ logger.debug("Redirecting to authorize:", redactAgencyInviteToken(authorizeUrl));
2712
2815
  recordRedirectAttempt(authorizeKey, now, logger);
2713
2816
  try {
2714
2817
  window.location.href = authorizeUrl;
@@ -2727,7 +2830,7 @@ function createRedirectMethods(deps) {
2727
2830
  return redirectToAuthorize(options);
2728
2831
  }
2729
2832
  const targetUrl = options?.screenHint === SCREEN_HINT_SIGNUP ? getSignupUrl() : getLoginUrl(void 0, options?.prompt ? { prompt: options.prompt } : void 0);
2730
- logger.debug("Redirecting to auth page:", targetUrl);
2833
+ logger.debug("Redirecting to auth page:", redactAgencyInviteToken(targetUrl));
2731
2834
  window.location.href = targetUrl;
2732
2835
  return { success: true };
2733
2836
  }
@@ -2744,7 +2847,7 @@ function createRedirectMethods(deps) {
2744
2847
  const promptSuffix = options?.prompt ? `&prompt=${encodeURIComponent(options.prompt)}` : "";
2745
2848
  return `${baseUrl}/login?redirect=${encodeURIComponent(redirect)}&service=${encodeURIComponent(serviceCode)}${promptSuffix}`;
2746
2849
  }
2747
- function getSignupUrl(redirectUrl) {
2850
+ function getSignupUrl(redirectUrl, options) {
2748
2851
  let redirect;
2749
2852
  if (redirectUrl != null) {
2750
2853
  redirect = redirectUrl;
@@ -2754,7 +2857,22 @@ function createRedirectMethods(deps) {
2754
2857
  logger.warn("getSignupUrl: SSR \u74B0\u5883\u3067 redirectUrl \u304C\u672A\u6307\u5B9A\u306E\u305F\u3081\u7A7A\u6587\u5B57\u306B\u30D5\u30A9\u30FC\u30EB\u30D0\u30C3\u30AF\u3057\u307E\u3059");
2755
2858
  redirect = "";
2756
2859
  }
2757
- return `${baseUrl}/signup?redirect=${encodeURIComponent(redirect)}&service=${encodeURIComponent(serviceCode)}`;
2860
+ let inviteToken = options?.inviteToken;
2861
+ if (inviteToken === void 0) {
2862
+ inviteToken = readAgencyInviteToken();
2863
+ if (inviteToken === void 0 && typeof window === "undefined") {
2864
+ if (!ssrInviteTokenUnavailableLogged) {
2865
+ ssrInviteTokenUnavailableLogged = true;
2866
+ logger.warn(
2867
+ "[FFID SDK] getSignupUrl: SSR \u3067\u306F\u62DB\u5F85\u30C8\u30FC\u30AF\u30F3\uFF08invite\uFF09\u3092\u8AAD\u3081\u307E\u305B\u3093\u3002\u4EE3\u7406\u5E97\u306E\u62DB\u5F85\u30EA\u30F3\u30AF\u3092\u4F7F\u3046\u5834\u5408\u306F options.inviteToken \u306B\u6E21\u3057\u3066\u304F\u3060\u3055\u3044\uFF08\u4F7F\u3063\u3066\u3044\u306A\u3044\u5834\u5408\u306F\u3053\u306E\u8B66\u544A\u3092\u7121\u8996\u3057\u3066\u3088\u3044\uFF09"
2868
+ );
2869
+ }
2870
+ }
2871
+ }
2872
+ return appendAgencyInviteToken(
2873
+ `${baseUrl}/signup?redirect=${encodeURIComponent(redirect)}&service=${encodeURIComponent(serviceCode)}`,
2874
+ inviteToken
2875
+ );
2758
2876
  }
2759
2877
  function getLogoutUrl(postLogoutRedirectUri) {
2760
2878
  const url = new URL(`${baseUrl}${AUTH_LOGOUT_ENDPOINT}`);
@@ -2770,7 +2888,7 @@ function createRedirectMethods(deps) {
2770
2888
  return { success: false, error: "SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093" };
2771
2889
  }
2772
2890
  const logoutUrl = getLogoutUrl(postLogoutRedirectUri);
2773
- logger.debug("Redirecting to logout:", logoutUrl);
2891
+ logger.debug("Redirecting to logout:", redactAgencyInviteToken(logoutUrl));
2774
2892
  window.location.href = logoutUrl;
2775
2893
  return { success: true };
2776
2894
  }
@@ -4324,9 +4442,9 @@ function createServerAuthClient(config) {
4324
4442
  var FFID_SESSION_COOKIE_NAME = "ffid_session";
4325
4443
  var DEFAULT_PATH = "/";
4326
4444
  var DEFAULT_SAME_SITE = "lax";
4327
- var HOURS_PER_DAY2 = 24;
4445
+ var HOURS_PER_DAY3 = 24;
4328
4446
  var DEFAULT_MAX_AGE_DAYS = 30;
4329
- var DEFAULT_MAX_AGE_SECONDS = 60 * 60 * HOURS_PER_DAY2 * DEFAULT_MAX_AGE_DAYS;
4447
+ var DEFAULT_MAX_AGE_SECONDS = 60 * 60 * HOURS_PER_DAY3 * DEFAULT_MAX_AGE_DAYS;
4330
4448
  var BASE64_GROUP = 4;
4331
4449
  function base64UrlEncode2(input) {
4332
4450
  const bytes = new TextEncoder().encode(input);
@@ -1,4 +1,4 @@
1
- import { r as FFIDClient, e as FFIDOAuthUserInfo } from '../../ffid-client-BFgoVPOZ.cjs';
1
+ import { r as FFIDClient, e as FFIDOAuthUserInfo } from '../../ffid-client-BaEmw7jP.cjs';
2
2
  import '../../types-Cjb9J0rL.cjs';
3
3
 
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { r as FFIDClient, e as FFIDOAuthUserInfo } from '../../ffid-client-QNEFA3mv.js';
1
+ import { r as FFIDClient, e as FFIDOAuthUserInfo } from '../../ffid-client-5GAPcRIG.js';
2
2
  import '../../types-Cjb9J0rL.js';
3
3
 
4
4
  /**
@@ -47,7 +47,7 @@ declare class FFIDWebhookPayloadError extends FFIDWebhookError {
47
47
  */
48
48
 
49
49
  /** All supported webhook event types */
50
- type FFIDWebhookEventType = 'subscription.created' | 'subscription.updated' | 'subscription.canceled' | 'subscription.downgrade_scheduled' | 'subscription.downgrade_applied' | 'subscription.downgrade_canceled' | 'subscription.trial_ending' | 'subscription.payment_failed' | 'user.created' | 'user.updated' | 'user.deleted' | 'user.deletion_requested' | 'organization.created' | 'organization.updated' | 'organization.member.added' | 'organization.member.removed' | 'organization.member.role_changed' | 'legal.document.updated' | 'legal.agreement.required' | 'system.maintenance.scheduled' | 'system.maintenance.started' | 'system.maintenance.completed' | 'announcement.published' | 'test.ping';
50
+ type FFIDWebhookEventType = 'subscription.created' | 'subscription.reactivated' | 'subscription.updated' | 'subscription.canceled' | 'subscription.downgrade_scheduled' | 'subscription.downgrade_applied' | 'subscription.downgrade_canceled' | 'subscription.trial_ending' | 'subscription.payment_failed' | 'user.created' | 'user.updated' | 'user.deleted' | 'user.deletion_requested' | 'organization.created' | 'organization.updated' | 'organization.member.added' | 'organization.member.removed' | 'organization.member.role_changed' | 'legal.document.updated' | 'legal.agreement.required' | 'system.maintenance.scheduled' | 'system.maintenance.started' | 'system.maintenance.completed' | 'announcement.published' | 'test.ping';
51
51
  interface FFIDSubscriptionCreatedPayload {
52
52
  subscriptionId: string;
53
53
  organizationId?: string;
@@ -65,6 +65,46 @@ interface FFIDSubscriptionUpdatedPayload {
65
65
  effectiveStatus?: EffectiveSubscriptionStatus;
66
66
  gracePeriodEndsAt?: string | null;
67
67
  }
68
+ /**
69
+ * `subscription.reactivated` (8.0.0): a cancellation reserved until the period end was
70
+ * withdrawn in the FFID portal, so the subscription continues. It pairs with the
71
+ * reservation-time `subscription.canceled` (`source: 'user_initiated'`,
72
+ * `reactivatable: true`). Sent only for contracts billed through Stripe.
73
+ */
74
+ interface FFIDSubscriptionReactivatedPayload {
75
+ subscriptionId: string;
76
+ organizationId?: string;
77
+ /** Plan code */
78
+ plan?: string;
79
+ status?: string;
80
+ effectiveStatus?: EffectiveSubscriptionStatus;
81
+ gracePeriodEndsAt?: string | null;
82
+ stripeSubscriptionId: string;
83
+ stripeCustomerId: string;
84
+ /** Code of the service the subscription belongs to (resolved by FFID when the event is sent) */
85
+ serviceCode?: string;
86
+ }
87
+ /**
88
+ * `subscription.canceled`.
89
+ *
90
+ * A contract billed through Stripe is canceled in two steps (FFID #6327), so **two events
91
+ * arrive per cancellation**:
92
+ *
93
+ * 1. At the reservation — `source: 'user_initiated'`, `reactivatable: true`, `cancelAt` /
94
+ * `expiredAt` = the period end, no `effectiveStatus`. **Access continues until `cancelAt`**;
95
+ * do not revoke it here. The reservation may still be withdrawn
96
+ * (`subscription.reactivated`).
97
+ * 2. At the period end — `source: 'stripe_confirmed'`, `effectiveStatus: 'canceled'`. Revoke
98
+ * access on this one.
99
+ *
100
+ * **Rule for consumers:** only an event with `source === 'user_initiated'` **and** a
101
+ * `cancelAt` is a reservation (keep access until `cancelAt`). Every other
102
+ * `subscription.canceled` ends access: the final `stripe_confirmed`, an immediate
103
+ * cancellation (`user_initiated` without `cancelAt` — a contract that cannot be reserved,
104
+ * e.g. no Stripe subscription or no period end yet; `expiredAt` is the cancellation time),
105
+ * and `payment_failure_auto_cancel` (automatic cancellation after unpaid invoices).
106
+ * Do not key the decision on `effectiveStatus`: it is set only on Stripe's deletion notice.
107
+ */
68
108
  interface FFIDSubscriptionCanceledPayload {
69
109
  /** FFID subscription UUID. */
70
110
  subscriptionId: string;
@@ -80,20 +120,28 @@ interface FFIDSubscriptionCanceledPayload {
80
120
  */
81
121
  stripeSubscriptionId: string | null;
82
122
  reason?: string;
123
+ /** When a reserved cancellation takes effect (the period end) */
83
124
  cancelAt?: string;
84
125
  /**
85
126
  * Origin of the cancellation. Always emitted by the FFID server — required
86
- * by the wire shape in `SubscriptionCanceledPayload`.
127
+ * by the wire shape in `SubscriptionCanceledPayload`. Distinguishes the reservation
128
+ * (`user_initiated`) from the final event (`stripe_confirmed`); see the interface doc.
87
129
  */
88
130
  source: 'user_initiated' | 'stripe_confirmed' | 'payment_failure_auto_cancel';
89
131
  /** Whether this cancellation can be resumed via resubscribe flow. */
90
132
  reactivatable?: boolean;
91
133
  /**
92
- * ISO timestamp at which access was actually lost. May differ from `cancelAt`
93
- * when Stripe runs out the paid period before emitting the deletion event.
134
+ * ISO timestamp at which access is (or was) lost. On the reservation event this is the
135
+ * future period end. May differ from `cancelAt` when Stripe runs out the paid period
136
+ * before emitting the deletion event.
94
137
  */
95
138
  expiredAt?: string;
96
- /** Cancellation always leaves the canonical access-control status as `canceled`. */
139
+ /**
140
+ * `canceled` only on Stripe's deletion notice (e.g. the final `stripe_confirmed`). Absent on
141
+ * the reservation event, on immediate cancellations and on the cron's
142
+ * `payment_failure_auto_cancel` — so its absence does not mean "still usable"; decide by
143
+ * `source` + `cancelAt` (see the interface doc).
144
+ */
97
145
  effectiveStatus?: EffectiveSubscriptionStatus;
98
146
  /** Always null for cancellation events because no grace window remains. */
99
147
  gracePeriodEndsAt?: string | null;
@@ -279,6 +327,7 @@ interface FFIDTestPingPayload {
279
327
  /** Maps event type strings to their typed payloads */
280
328
  interface FFIDWebhookEventMap {
281
329
  'subscription.created': FFIDSubscriptionCreatedPayload;
330
+ 'subscription.reactivated': FFIDSubscriptionReactivatedPayload;
282
331
  'subscription.updated': FFIDSubscriptionUpdatedPayload;
283
332
  'subscription.canceled': FFIDSubscriptionCanceledPayload;
284
333
  'subscription.downgrade_scheduled': FFIDSubscriptionDowngradeScheduledPayload;
@@ -426,4 +475,4 @@ declare function createFFIDWebhookHandler(config: FFIDWebhookHandlerConfig): {
426
475
  /** Type of the FFID Webhook handler */
427
476
  type FFIDWebhookHandler = ReturnType<typeof createFFIDWebhookHandler>;
428
477
 
429
- export { DEFAULT_TOLERANCE_SECONDS, type FFIDLegalAgreementRequiredPayload, type FFIDLegalDocumentUpdatedPayload, type FFIDOrganizationCreatedPayload, type FFIDOrganizationMemberAddedPayload, type FFIDOrganizationMemberRemovedPayload, type FFIDOrganizationMemberRoleChangedPayload, type FFIDOrganizationUpdatedPayload, type FFIDSubscriptionCanceledPayload, type FFIDSubscriptionCreatedPayload, type FFIDSubscriptionDowngradeAppliedPayload, type FFIDSubscriptionDowngradeCanceledPayload, type FFIDSubscriptionDowngradeScheduledPayload, type FFIDSubscriptionPaymentFailedPayload, type FFIDSubscriptionTrialEndingPayload, type FFIDSubscriptionUpdatedPayload, type FFIDSystemMaintenancePayload, type FFIDTestPingPayload, type FFIDUserCreatedPayload, type FFIDUserDeletedPayload, type FFIDUserDeletionRequestedPayload, type FFIDUserUpdatedPayload, FFIDWebhookError, type FFIDWebhookEvent, type FFIDWebhookEventHandler, type FFIDWebhookEventMap, type FFIDWebhookEventType, type FFIDWebhookHandler, type FFIDWebhookHandlerConfig, type FFIDWebhookLogger, FFIDWebhookPayloadError, FFIDWebhookSignatureError, FFIDWebhookTimestampError, FFID_WEBHOOK_EVENT_ID_HEADER, FFID_WEBHOOK_SIGNATURE_HEADER, FFID_WEBHOOK_SIGNATURE_VERSION, FFID_WEBHOOK_TIMESTAMP_HEADER, computeSignature, createFFIDWebhookHandler, parseSignatureHeader, verifyWebhookSignature };
478
+ export { DEFAULT_TOLERANCE_SECONDS, type FFIDLegalAgreementRequiredPayload, type FFIDLegalDocumentUpdatedPayload, type FFIDOrganizationCreatedPayload, type FFIDOrganizationMemberAddedPayload, type FFIDOrganizationMemberRemovedPayload, type FFIDOrganizationMemberRoleChangedPayload, type FFIDOrganizationUpdatedPayload, type FFIDSubscriptionCanceledPayload, type FFIDSubscriptionCreatedPayload, type FFIDSubscriptionDowngradeAppliedPayload, type FFIDSubscriptionDowngradeCanceledPayload, type FFIDSubscriptionDowngradeScheduledPayload, type FFIDSubscriptionPaymentFailedPayload, type FFIDSubscriptionReactivatedPayload, type FFIDSubscriptionTrialEndingPayload, type FFIDSubscriptionUpdatedPayload, type FFIDSystemMaintenancePayload, type FFIDTestPingPayload, type FFIDUserCreatedPayload, type FFIDUserDeletedPayload, type FFIDUserDeletionRequestedPayload, type FFIDUserUpdatedPayload, FFIDWebhookError, type FFIDWebhookEvent, type FFIDWebhookEventHandler, type FFIDWebhookEventMap, type FFIDWebhookEventType, type FFIDWebhookHandler, type FFIDWebhookHandlerConfig, type FFIDWebhookLogger, FFIDWebhookPayloadError, FFIDWebhookSignatureError, FFIDWebhookTimestampError, FFID_WEBHOOK_EVENT_ID_HEADER, FFID_WEBHOOK_SIGNATURE_HEADER, FFID_WEBHOOK_SIGNATURE_VERSION, FFID_WEBHOOK_TIMESTAMP_HEADER, computeSignature, createFFIDWebhookHandler, parseSignatureHeader, verifyWebhookSignature };