@feelflow/ffid-sdk 2.11.0 → 2.12.1

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/dist/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkBEHDXUB2_cjs = require('./chunk-BEHDXUB2.cjs');
3
+ var chunkDJPOGNAO_cjs = require('./chunk-DJPOGNAO.cjs');
4
4
  var react = require('react');
5
5
  var jsxRuntime = require('react/jsx-runtime');
6
6
 
@@ -46,7 +46,7 @@ function createKVCacheAdapter(kv) {
46
46
  }
47
47
  function withFFIDAuth(Component, options = {}) {
48
48
  const WrappedComponent = (props) => {
49
- const { isLoading, isAuthenticated, login } = chunkBEHDXUB2_cjs.useFFIDContext();
49
+ const { isLoading, isAuthenticated, login } = chunkDJPOGNAO_cjs.useFFIDContext();
50
50
  const hasRedirected = react.useRef(false);
51
51
  react.useEffect(() => {
52
52
  if (!isLoading && !isAuthenticated && options.redirectToLogin && !hasRedirected.current) {
@@ -74,91 +74,99 @@ var FFID_NEWSLETTER_TYPES = ["inquiry_followup", "general"];
74
74
 
75
75
  Object.defineProperty(exports, "DEFAULT_API_BASE_URL", {
76
76
  enumerable: true,
77
- get: function () { return chunkBEHDXUB2_cjs.DEFAULT_API_BASE_URL; }
77
+ get: function () { return chunkDJPOGNAO_cjs.DEFAULT_API_BASE_URL; }
78
78
  });
79
79
  Object.defineProperty(exports, "FFIDAnnouncementBadge", {
80
80
  enumerable: true,
81
- get: function () { return chunkBEHDXUB2_cjs.FFIDAnnouncementBadge; }
81
+ get: function () { return chunkDJPOGNAO_cjs.FFIDAnnouncementBadge; }
82
82
  });
83
83
  Object.defineProperty(exports, "FFIDAnnouncementList", {
84
84
  enumerable: true,
85
- get: function () { return chunkBEHDXUB2_cjs.FFIDAnnouncementList; }
85
+ get: function () { return chunkDJPOGNAO_cjs.FFIDAnnouncementList; }
86
86
  });
87
87
  Object.defineProperty(exports, "FFIDInquiryForm", {
88
88
  enumerable: true,
89
- get: function () { return chunkBEHDXUB2_cjs.FFIDInquiryForm; }
89
+ get: function () { return chunkDJPOGNAO_cjs.FFIDInquiryForm; }
90
90
  });
91
91
  Object.defineProperty(exports, "FFIDLoginButton", {
92
92
  enumerable: true,
93
- get: function () { return chunkBEHDXUB2_cjs.FFIDLoginButton; }
93
+ get: function () { return chunkDJPOGNAO_cjs.FFIDLoginButton; }
94
94
  });
95
95
  Object.defineProperty(exports, "FFIDOrganizationSwitcher", {
96
96
  enumerable: true,
97
- get: function () { return chunkBEHDXUB2_cjs.FFIDOrganizationSwitcher; }
97
+ get: function () { return chunkDJPOGNAO_cjs.FFIDOrganizationSwitcher; }
98
98
  });
99
99
  Object.defineProperty(exports, "FFIDProvider", {
100
100
  enumerable: true,
101
- get: function () { return chunkBEHDXUB2_cjs.FFIDProvider; }
101
+ get: function () { return chunkDJPOGNAO_cjs.FFIDProvider; }
102
+ });
103
+ Object.defineProperty(exports, "FFIDSDKError", {
104
+ enumerable: true,
105
+ get: function () { return chunkDJPOGNAO_cjs.FFIDSDKError; }
102
106
  });
103
107
  Object.defineProperty(exports, "FFIDSubscriptionBadge", {
104
108
  enumerable: true,
105
- get: function () { return chunkBEHDXUB2_cjs.FFIDSubscriptionBadge; }
109
+ get: function () { return chunkDJPOGNAO_cjs.FFIDSubscriptionBadge; }
106
110
  });
107
111
  Object.defineProperty(exports, "FFIDUserMenu", {
108
112
  enumerable: true,
109
- get: function () { return chunkBEHDXUB2_cjs.FFIDUserMenu; }
113
+ get: function () { return chunkDJPOGNAO_cjs.FFIDUserMenu; }
110
114
  });
111
115
  Object.defineProperty(exports, "FFID_ANNOUNCEMENTS_ERROR_CODES", {
112
116
  enumerable: true,
113
- get: function () { return chunkBEHDXUB2_cjs.FFID_ANNOUNCEMENTS_ERROR_CODES; }
117
+ get: function () { return chunkDJPOGNAO_cjs.FFID_ANNOUNCEMENTS_ERROR_CODES; }
114
118
  });
115
119
  Object.defineProperty(exports, "FFID_INQUIRY_CATEGORIES", {
116
120
  enumerable: true,
117
- get: function () { return chunkBEHDXUB2_cjs.FFID_INQUIRY_CATEGORIES; }
121
+ get: function () { return chunkDJPOGNAO_cjs.FFID_INQUIRY_CATEGORIES; }
118
122
  });
119
123
  Object.defineProperty(exports, "createFFIDAnnouncementsClient", {
120
124
  enumerable: true,
121
- get: function () { return chunkBEHDXUB2_cjs.createFFIDAnnouncementsClient; }
125
+ get: function () { return chunkDJPOGNAO_cjs.createFFIDAnnouncementsClient; }
122
126
  });
123
127
  Object.defineProperty(exports, "createFFIDClient", {
124
128
  enumerable: true,
125
- get: function () { return chunkBEHDXUB2_cjs.createFFIDClient; }
129
+ get: function () { return chunkDJPOGNAO_cjs.createFFIDClient; }
126
130
  });
127
131
  Object.defineProperty(exports, "createTokenStore", {
128
132
  enumerable: true,
129
- get: function () { return chunkBEHDXUB2_cjs.createTokenStore; }
133
+ get: function () { return chunkDJPOGNAO_cjs.createTokenStore; }
130
134
  });
131
135
  Object.defineProperty(exports, "generateCodeChallenge", {
132
136
  enumerable: true,
133
- get: function () { return chunkBEHDXUB2_cjs.generateCodeChallenge; }
137
+ get: function () { return chunkDJPOGNAO_cjs.generateCodeChallenge; }
134
138
  });
135
139
  Object.defineProperty(exports, "generateCodeVerifier", {
136
140
  enumerable: true,
137
- get: function () { return chunkBEHDXUB2_cjs.generateCodeVerifier; }
141
+ get: function () { return chunkDJPOGNAO_cjs.generateCodeVerifier; }
142
+ });
143
+ Object.defineProperty(exports, "normalizeRedirectUri", {
144
+ enumerable: true,
145
+ get: function () { return chunkDJPOGNAO_cjs.normalizeRedirectUri; }
138
146
  });
139
147
  Object.defineProperty(exports, "retrieveCodeVerifier", {
140
148
  enumerable: true,
141
- get: function () { return chunkBEHDXUB2_cjs.retrieveCodeVerifier; }
149
+ get: function () { return chunkDJPOGNAO_cjs.retrieveCodeVerifier; }
142
150
  });
143
151
  Object.defineProperty(exports, "storeCodeVerifier", {
144
152
  enumerable: true,
145
- get: function () { return chunkBEHDXUB2_cjs.storeCodeVerifier; }
153
+ get: function () { return chunkDJPOGNAO_cjs.storeCodeVerifier; }
146
154
  });
147
155
  Object.defineProperty(exports, "useFFID", {
148
156
  enumerable: true,
149
- get: function () { return chunkBEHDXUB2_cjs.useFFID; }
157
+ get: function () { return chunkDJPOGNAO_cjs.useFFID; }
150
158
  });
151
159
  Object.defineProperty(exports, "useFFIDAnnouncements", {
152
160
  enumerable: true,
153
- get: function () { return chunkBEHDXUB2_cjs.useFFIDAnnouncements; }
161
+ get: function () { return chunkDJPOGNAO_cjs.useFFIDAnnouncements; }
154
162
  });
155
163
  Object.defineProperty(exports, "useSubscription", {
156
164
  enumerable: true,
157
- get: function () { return chunkBEHDXUB2_cjs.useSubscription; }
165
+ get: function () { return chunkDJPOGNAO_cjs.useSubscription; }
158
166
  });
159
167
  Object.defineProperty(exports, "withSubscription", {
160
168
  enumerable: true,
161
- get: function () { return chunkBEHDXUB2_cjs.withSubscription; }
169
+ get: function () { return chunkDJPOGNAO_cjs.withSubscription; }
162
170
  });
163
171
  exports.FFID_NEWSLETTER_TYPES = FFID_NEWSLETTER_TYPES;
164
172
  exports.createKVCacheAdapter = createKVCacheAdapter;
package/dist/index.d.cts CHANGED
@@ -341,6 +341,62 @@ type FFIDPlanChangePreview = FFIDPlanChangePreviewBase & ({
341
341
  interface FFIDPlanChangePreviewResponse {
342
342
  preview: FFIDPlanChangePreview;
343
343
  }
344
+ /** Parameters for previewing a seat count change */
345
+ interface FFIDPreviewSeatChangeParams {
346
+ /** Subscription ID (UUID) */
347
+ subscriptionId: string;
348
+ /** New seat quantity. Must be an integer within the plan's allowed range. */
349
+ quantity: number;
350
+ }
351
+ /** Seat change preview line item */
352
+ interface FFIDSeatChangeLineItem {
353
+ description: string;
354
+ amount: number;
355
+ }
356
+ /**
357
+ * Seat change proration preview.
358
+ *
359
+ * Sister type to `FFIDPlanChangePreview`. The shared `type` discriminant lets
360
+ * consumers narrow a preview payload without inspecting unrelated fields.
361
+ *
362
+ * - `isEstimate=true` (default when Stripe is not configured or data is unavailable):
363
+ * `proratedAmount` is computed locally as `(newQuantity - currentQuantity) * unitPrice`.
364
+ * - `isEstimate=false`: `proratedAmount` reflects Stripe's live proration calculation.
365
+ *
366
+ * `nextInvoiceAmount` is always a local estimate (`unitPrice * newQuantity`).
367
+ *
368
+ * `pricingUnavailable=true` indicates the plan uses custom pricing with a zero unit
369
+ * price (Enterprise). Consumers should hide monetary amounts in this case.
370
+ */
371
+ interface FFIDSeatChangePreview {
372
+ /** Discriminant for preview response variants (pairs with `FFIDPlanChangePreview.type`) */
373
+ type: 'seat-change';
374
+ currentQuantity: number;
375
+ newQuantity: number;
376
+ /** Per-seat price for the current billing interval */
377
+ unitPrice: number;
378
+ billingInterval: FFIDBillingInterval;
379
+ /**
380
+ * Prorated cost for the current billing period. Negative when seats are decreased
381
+ * (credit). `0` when `pricingUnavailable === true` (Enterprise custom pricing).
382
+ */
383
+ proratedAmount: number;
384
+ /** Next invoice full amount — always a local estimate (`unitPrice * newQuantity`) */
385
+ nextInvoiceAmount: number;
386
+ nextInvoiceDate: string | null;
387
+ currency: FFIDSupportedCurrency;
388
+ /** true when proratedAmount is estimated from local prices rather than Stripe proration data */
389
+ isEstimate: boolean;
390
+ /** true when unit price is 0 due to custom pricing (Enterprise) — hide monetary amounts */
391
+ pricingUnavailable?: boolean;
392
+ /** Reason why `isEstimate` is true. Only meaningful when `isEstimate === true`. */
393
+ estimateReason?: 'no_stripe_data' | 'custom_pricing' | 'stripe_error';
394
+ lineItems: FFIDSeatChangeLineItem[];
395
+ }
396
+ /** Response from seat change preview endpoint */
397
+ interface FFIDSeatChangePreviewResponse {
398
+ preview: FFIDSeatChangePreview;
399
+ }
344
400
 
345
401
  /** OTP / magic link methods - sendOtp / verifyOtp */
346
402
 
@@ -453,6 +509,7 @@ declare function createFFIDClient(config: FFIDConfig): {
453
509
  cancelSubscription: (params: FFIDCancelSubscriptionParams) => Promise<FFIDApiResponse<FFIDCancelSubscriptionResponse>>;
454
510
  cancelPendingDowngrade: (subscriptionId: string) => Promise<FFIDApiResponse<FFIDCancelPendingDowngradeResponse>>;
455
511
  previewPlanChange: (params: FFIDPreviewPlanChangeParams) => Promise<FFIDApiResponse<FFIDPlanChangePreviewResponse>>;
512
+ previewSeatChange: (params: FFIDPreviewSeatChangeParams) => Promise<FFIDApiResponse<FFIDSeatChangePreviewResponse>>;
456
513
  verifyAccessToken: (accessToken: string, options?: FFIDVerifyAccessTokenOptions) => Promise<FFIDApiResponse<FFIDOAuthUserInfo>>;
457
514
  getSubscribeUrl: (options?: ContractWizardSubscribeOptions) => string;
458
515
  redirectToSubscribe: (options?: ContractWizardSubscribeOptions) => FFIDRedirectResult;
@@ -499,6 +556,37 @@ declare function createFFIDClient(config: FFIDConfig): {
499
556
  /** Type of the FFID client */
500
557
  type FFIDClient = ReturnType<typeof createFFIDClient>;
501
558
 
559
+ /** Subscription management methods - plan listing, subscribe, change, cancel, preview */
560
+
561
+ /**
562
+ * Thrown by the SDK when the server returns a payload that violates a type-level
563
+ * invariant the SDK relies on for consumer narrowing. Always indicates a server
564
+ * regression (or a client/server version mismatch); never caused by user input.
565
+ */
566
+ declare class FFIDSDKError extends Error {
567
+ readonly code: string;
568
+ constructor(code: string, message: string);
569
+ }
570
+
571
+ /**
572
+ * Redirect URI normalization (SDK copy)
573
+ *
574
+ * Kept in sync with `src/lib/common/redirect-uri.ts` in the FFID server
575
+ * repository. OAuth 2.1 exact-match comparison requires that both the
576
+ * registered redirect_uri (managed via the FFID admin UI) and the value
577
+ * sent by OAuth clients converge on the same canonical form — otherwise
578
+ * root-path URLs like `https://example.com` vs `https://example.com/`
579
+ * silently fail to match.
580
+ */
581
+ /** Result of a normalization attempt. */
582
+ interface NormalizeRedirectUriResult {
583
+ /** The normalized URL. `input` plus a trailing `/` if a root-path slash was appended; otherwise exactly `input`. */
584
+ readonly normalized: string;
585
+ /** True only when a trailing slash was appended to a root-path URL. */
586
+ readonly changed: boolean;
587
+ }
588
+ declare function normalizeRedirectUri(input: string): NormalizeRedirectUriResult;
589
+
502
590
  /**
503
591
  * Create an in-memory cache adapter using a Map.
504
592
  * Suitable for single-process environments (e.g., development, testing).
@@ -844,4 +932,4 @@ declare function createInquiryMethods(deps: InquiryMethodsDeps): {
844
932
  };
845
933
  type FFIDInquiryClient = ReturnType<typeof createInquiryMethods>;
846
934
 
847
- export { AnnouncementListResponse, type ContractWizardFlowType, type ContractWizardResubscribeOptions, type ContractWizardSubscribeOptions, type ContractWizardSubscriptionOptions, DEFAULT_API_BASE_URL, FFIDAnnouncementsApiResponse, type FFIDAnnouncementsClient, FFIDAnnouncementsClientConfig, FFIDAnnouncementsLogger, FFIDApiResponse, type FFIDBillingInterval, FFIDCacheAdapter, type FFIDCancelPendingDowngradeResponse, type FFIDCancelSubscriptionParams, type FFIDCancelSubscriptionResponse, type FFIDChangePlanParams, type FFIDChangePlanResponse, FFIDCheckoutSessionResponse, type FFIDClient, FFIDConfig, FFIDCreateCheckoutParams, FFIDCreatePortalParams, FFIDError, type FFIDInquiryClient, FFIDInquiryCreateParams, FFIDInquiryCreateResponse, FFIDListMembersResponse, type FFIDListPlansResponse, FFIDLogger, FFIDMemberRole, type FFIDNewsletterClient, type FFIDNewsletterConfirmParams, type FFIDNewsletterConfirmResponse, type FFIDNewsletterSubscribeParams, type FFIDNewsletterSubscribeResponse, type FFIDNewsletterType, type FFIDNewsletterUnsubscribeParams, type FFIDNewsletterUnsubscribeResponse, FFIDOAuthUserInfo, FFIDOrganization, type FFIDOtpSendResponse, type FFIDOtpVerifyResponse, type FFIDPasswordResetConfirmResponse, type FFIDPasswordResetResponse, type FFIDPasswordResetVerifyResponse, type FFIDPlanChangeLineItem, type FFIDPlanChangePreview, type FFIDPlanChangePreviewResponse, type FFIDPlanInfo, FFIDPortalSessionResponse, type FFIDPreviewPlanChangeParams, FFIDProvider, type FFIDProviderProps, FFIDRedirectResult, FFIDRemoveMemberResponse, type FFIDResetSessionResponse, type FFIDServiceInfo, FFIDSessionResponse, type FFIDSubscribeParams, type FFIDSubscribeResponse, FFIDSubscription, FFIDSubscriptionCheckResponse, FFIDSubscriptionContextValue, type FFIDSubscriptionDetail, FFIDSubscriptionStatus, type FFIDSubscriptionSummary, type FFIDSupportedCurrency, FFIDUpdateMemberRoleResponse, FFIDUser, FFIDVerifyAccessTokenOptions, FFID_ANNOUNCEMENTS_ERROR_CODES, FFID_NEWSLETTER_TYPES, type KVNamespaceLike, ListAnnouncementsOptions, type RedirectToAuthorizeOptions, type TokenData, type TokenStore, type UseFFIDReturn, type WithFFIDAuthOptions, type WithSubscriptionOptions, createFFIDAnnouncementsClient, createFFIDClient, createKVCacheAdapter, createMemoryCacheAdapter, createTokenStore, generateCodeChallenge, generateCodeVerifier, retrieveCodeVerifier, storeCodeVerifier, useFFID, useSubscription, withFFIDAuth, withSubscription };
935
+ export { AnnouncementListResponse, type ContractWizardFlowType, type ContractWizardResubscribeOptions, type ContractWizardSubscribeOptions, type ContractWizardSubscriptionOptions, DEFAULT_API_BASE_URL, FFIDAnnouncementsApiResponse, type FFIDAnnouncementsClient, FFIDAnnouncementsClientConfig, FFIDAnnouncementsLogger, FFIDApiResponse, type FFIDBillingInterval, FFIDCacheAdapter, type FFIDCancelPendingDowngradeResponse, type FFIDCancelSubscriptionParams, type FFIDCancelSubscriptionResponse, type FFIDChangePlanParams, type FFIDChangePlanResponse, FFIDCheckoutSessionResponse, type FFIDClient, FFIDConfig, FFIDCreateCheckoutParams, FFIDCreatePortalParams, FFIDError, type FFIDInquiryClient, FFIDInquiryCreateParams, FFIDInquiryCreateResponse, FFIDListMembersResponse, type FFIDListPlansResponse, FFIDLogger, FFIDMemberRole, type FFIDNewsletterClient, type FFIDNewsletterConfirmParams, type FFIDNewsletterConfirmResponse, type FFIDNewsletterSubscribeParams, type FFIDNewsletterSubscribeResponse, type FFIDNewsletterType, type FFIDNewsletterUnsubscribeParams, type FFIDNewsletterUnsubscribeResponse, FFIDOAuthUserInfo, FFIDOrganization, type FFIDOtpSendResponse, type FFIDOtpVerifyResponse, type FFIDPasswordResetConfirmResponse, type FFIDPasswordResetResponse, type FFIDPasswordResetVerifyResponse, type FFIDPlanChangeLineItem, type FFIDPlanChangePreview, type FFIDPlanChangePreviewResponse, type FFIDPlanInfo, FFIDPortalSessionResponse, type FFIDPreviewPlanChangeParams, type FFIDPreviewSeatChangeParams, FFIDProvider, type FFIDProviderProps, FFIDRedirectResult, FFIDRemoveMemberResponse, type FFIDResetSessionResponse, FFIDSDKError, type FFIDSeatChangeLineItem, type FFIDSeatChangePreview, type FFIDSeatChangePreviewResponse, type FFIDServiceInfo, FFIDSessionResponse, type FFIDSubscribeParams, type FFIDSubscribeResponse, FFIDSubscription, FFIDSubscriptionCheckResponse, FFIDSubscriptionContextValue, type FFIDSubscriptionDetail, FFIDSubscriptionStatus, type FFIDSubscriptionSummary, type FFIDSupportedCurrency, FFIDUpdateMemberRoleResponse, FFIDUser, FFIDVerifyAccessTokenOptions, FFID_ANNOUNCEMENTS_ERROR_CODES, FFID_NEWSLETTER_TYPES, type KVNamespaceLike, ListAnnouncementsOptions, type NormalizeRedirectUriResult, type RedirectToAuthorizeOptions, type TokenData, type TokenStore, type UseFFIDReturn, type WithFFIDAuthOptions, type WithSubscriptionOptions, createFFIDAnnouncementsClient, createFFIDClient, createKVCacheAdapter, createMemoryCacheAdapter, createTokenStore, generateCodeChallenge, generateCodeVerifier, normalizeRedirectUri, retrieveCodeVerifier, storeCodeVerifier, useFFID, useSubscription, withFFIDAuth, withSubscription };
package/dist/index.d.ts CHANGED
@@ -341,6 +341,62 @@ type FFIDPlanChangePreview = FFIDPlanChangePreviewBase & ({
341
341
  interface FFIDPlanChangePreviewResponse {
342
342
  preview: FFIDPlanChangePreview;
343
343
  }
344
+ /** Parameters for previewing a seat count change */
345
+ interface FFIDPreviewSeatChangeParams {
346
+ /** Subscription ID (UUID) */
347
+ subscriptionId: string;
348
+ /** New seat quantity. Must be an integer within the plan's allowed range. */
349
+ quantity: number;
350
+ }
351
+ /** Seat change preview line item */
352
+ interface FFIDSeatChangeLineItem {
353
+ description: string;
354
+ amount: number;
355
+ }
356
+ /**
357
+ * Seat change proration preview.
358
+ *
359
+ * Sister type to `FFIDPlanChangePreview`. The shared `type` discriminant lets
360
+ * consumers narrow a preview payload without inspecting unrelated fields.
361
+ *
362
+ * - `isEstimate=true` (default when Stripe is not configured or data is unavailable):
363
+ * `proratedAmount` is computed locally as `(newQuantity - currentQuantity) * unitPrice`.
364
+ * - `isEstimate=false`: `proratedAmount` reflects Stripe's live proration calculation.
365
+ *
366
+ * `nextInvoiceAmount` is always a local estimate (`unitPrice * newQuantity`).
367
+ *
368
+ * `pricingUnavailable=true` indicates the plan uses custom pricing with a zero unit
369
+ * price (Enterprise). Consumers should hide monetary amounts in this case.
370
+ */
371
+ interface FFIDSeatChangePreview {
372
+ /** Discriminant for preview response variants (pairs with `FFIDPlanChangePreview.type`) */
373
+ type: 'seat-change';
374
+ currentQuantity: number;
375
+ newQuantity: number;
376
+ /** Per-seat price for the current billing interval */
377
+ unitPrice: number;
378
+ billingInterval: FFIDBillingInterval;
379
+ /**
380
+ * Prorated cost for the current billing period. Negative when seats are decreased
381
+ * (credit). `0` when `pricingUnavailable === true` (Enterprise custom pricing).
382
+ */
383
+ proratedAmount: number;
384
+ /** Next invoice full amount — always a local estimate (`unitPrice * newQuantity`) */
385
+ nextInvoiceAmount: number;
386
+ nextInvoiceDate: string | null;
387
+ currency: FFIDSupportedCurrency;
388
+ /** true when proratedAmount is estimated from local prices rather than Stripe proration data */
389
+ isEstimate: boolean;
390
+ /** true when unit price is 0 due to custom pricing (Enterprise) — hide monetary amounts */
391
+ pricingUnavailable?: boolean;
392
+ /** Reason why `isEstimate` is true. Only meaningful when `isEstimate === true`. */
393
+ estimateReason?: 'no_stripe_data' | 'custom_pricing' | 'stripe_error';
394
+ lineItems: FFIDSeatChangeLineItem[];
395
+ }
396
+ /** Response from seat change preview endpoint */
397
+ interface FFIDSeatChangePreviewResponse {
398
+ preview: FFIDSeatChangePreview;
399
+ }
344
400
 
345
401
  /** OTP / magic link methods - sendOtp / verifyOtp */
346
402
 
@@ -453,6 +509,7 @@ declare function createFFIDClient(config: FFIDConfig): {
453
509
  cancelSubscription: (params: FFIDCancelSubscriptionParams) => Promise<FFIDApiResponse<FFIDCancelSubscriptionResponse>>;
454
510
  cancelPendingDowngrade: (subscriptionId: string) => Promise<FFIDApiResponse<FFIDCancelPendingDowngradeResponse>>;
455
511
  previewPlanChange: (params: FFIDPreviewPlanChangeParams) => Promise<FFIDApiResponse<FFIDPlanChangePreviewResponse>>;
512
+ previewSeatChange: (params: FFIDPreviewSeatChangeParams) => Promise<FFIDApiResponse<FFIDSeatChangePreviewResponse>>;
456
513
  verifyAccessToken: (accessToken: string, options?: FFIDVerifyAccessTokenOptions) => Promise<FFIDApiResponse<FFIDOAuthUserInfo>>;
457
514
  getSubscribeUrl: (options?: ContractWizardSubscribeOptions) => string;
458
515
  redirectToSubscribe: (options?: ContractWizardSubscribeOptions) => FFIDRedirectResult;
@@ -499,6 +556,37 @@ declare function createFFIDClient(config: FFIDConfig): {
499
556
  /** Type of the FFID client */
500
557
  type FFIDClient = ReturnType<typeof createFFIDClient>;
501
558
 
559
+ /** Subscription management methods - plan listing, subscribe, change, cancel, preview */
560
+
561
+ /**
562
+ * Thrown by the SDK when the server returns a payload that violates a type-level
563
+ * invariant the SDK relies on for consumer narrowing. Always indicates a server
564
+ * regression (or a client/server version mismatch); never caused by user input.
565
+ */
566
+ declare class FFIDSDKError extends Error {
567
+ readonly code: string;
568
+ constructor(code: string, message: string);
569
+ }
570
+
571
+ /**
572
+ * Redirect URI normalization (SDK copy)
573
+ *
574
+ * Kept in sync with `src/lib/common/redirect-uri.ts` in the FFID server
575
+ * repository. OAuth 2.1 exact-match comparison requires that both the
576
+ * registered redirect_uri (managed via the FFID admin UI) and the value
577
+ * sent by OAuth clients converge on the same canonical form — otherwise
578
+ * root-path URLs like `https://example.com` vs `https://example.com/`
579
+ * silently fail to match.
580
+ */
581
+ /** Result of a normalization attempt. */
582
+ interface NormalizeRedirectUriResult {
583
+ /** The normalized URL. `input` plus a trailing `/` if a root-path slash was appended; otherwise exactly `input`. */
584
+ readonly normalized: string;
585
+ /** True only when a trailing slash was appended to a root-path URL. */
586
+ readonly changed: boolean;
587
+ }
588
+ declare function normalizeRedirectUri(input: string): NormalizeRedirectUriResult;
589
+
502
590
  /**
503
591
  * Create an in-memory cache adapter using a Map.
504
592
  * Suitable for single-process environments (e.g., development, testing).
@@ -844,4 +932,4 @@ declare function createInquiryMethods(deps: InquiryMethodsDeps): {
844
932
  };
845
933
  type FFIDInquiryClient = ReturnType<typeof createInquiryMethods>;
846
934
 
847
- export { AnnouncementListResponse, type ContractWizardFlowType, type ContractWizardResubscribeOptions, type ContractWizardSubscribeOptions, type ContractWizardSubscriptionOptions, DEFAULT_API_BASE_URL, FFIDAnnouncementsApiResponse, type FFIDAnnouncementsClient, FFIDAnnouncementsClientConfig, FFIDAnnouncementsLogger, FFIDApiResponse, type FFIDBillingInterval, FFIDCacheAdapter, type FFIDCancelPendingDowngradeResponse, type FFIDCancelSubscriptionParams, type FFIDCancelSubscriptionResponse, type FFIDChangePlanParams, type FFIDChangePlanResponse, FFIDCheckoutSessionResponse, type FFIDClient, FFIDConfig, FFIDCreateCheckoutParams, FFIDCreatePortalParams, FFIDError, type FFIDInquiryClient, FFIDInquiryCreateParams, FFIDInquiryCreateResponse, FFIDListMembersResponse, type FFIDListPlansResponse, FFIDLogger, FFIDMemberRole, type FFIDNewsletterClient, type FFIDNewsletterConfirmParams, type FFIDNewsletterConfirmResponse, type FFIDNewsletterSubscribeParams, type FFIDNewsletterSubscribeResponse, type FFIDNewsletterType, type FFIDNewsletterUnsubscribeParams, type FFIDNewsletterUnsubscribeResponse, FFIDOAuthUserInfo, FFIDOrganization, type FFIDOtpSendResponse, type FFIDOtpVerifyResponse, type FFIDPasswordResetConfirmResponse, type FFIDPasswordResetResponse, type FFIDPasswordResetVerifyResponse, type FFIDPlanChangeLineItem, type FFIDPlanChangePreview, type FFIDPlanChangePreviewResponse, type FFIDPlanInfo, FFIDPortalSessionResponse, type FFIDPreviewPlanChangeParams, FFIDProvider, type FFIDProviderProps, FFIDRedirectResult, FFIDRemoveMemberResponse, type FFIDResetSessionResponse, type FFIDServiceInfo, FFIDSessionResponse, type FFIDSubscribeParams, type FFIDSubscribeResponse, FFIDSubscription, FFIDSubscriptionCheckResponse, FFIDSubscriptionContextValue, type FFIDSubscriptionDetail, FFIDSubscriptionStatus, type FFIDSubscriptionSummary, type FFIDSupportedCurrency, FFIDUpdateMemberRoleResponse, FFIDUser, FFIDVerifyAccessTokenOptions, FFID_ANNOUNCEMENTS_ERROR_CODES, FFID_NEWSLETTER_TYPES, type KVNamespaceLike, ListAnnouncementsOptions, type RedirectToAuthorizeOptions, type TokenData, type TokenStore, type UseFFIDReturn, type WithFFIDAuthOptions, type WithSubscriptionOptions, createFFIDAnnouncementsClient, createFFIDClient, createKVCacheAdapter, createMemoryCacheAdapter, createTokenStore, generateCodeChallenge, generateCodeVerifier, retrieveCodeVerifier, storeCodeVerifier, useFFID, useSubscription, withFFIDAuth, withSubscription };
935
+ export { AnnouncementListResponse, type ContractWizardFlowType, type ContractWizardResubscribeOptions, type ContractWizardSubscribeOptions, type ContractWizardSubscriptionOptions, DEFAULT_API_BASE_URL, FFIDAnnouncementsApiResponse, type FFIDAnnouncementsClient, FFIDAnnouncementsClientConfig, FFIDAnnouncementsLogger, FFIDApiResponse, type FFIDBillingInterval, FFIDCacheAdapter, type FFIDCancelPendingDowngradeResponse, type FFIDCancelSubscriptionParams, type FFIDCancelSubscriptionResponse, type FFIDChangePlanParams, type FFIDChangePlanResponse, FFIDCheckoutSessionResponse, type FFIDClient, FFIDConfig, FFIDCreateCheckoutParams, FFIDCreatePortalParams, FFIDError, type FFIDInquiryClient, FFIDInquiryCreateParams, FFIDInquiryCreateResponse, FFIDListMembersResponse, type FFIDListPlansResponse, FFIDLogger, FFIDMemberRole, type FFIDNewsletterClient, type FFIDNewsletterConfirmParams, type FFIDNewsletterConfirmResponse, type FFIDNewsletterSubscribeParams, type FFIDNewsletterSubscribeResponse, type FFIDNewsletterType, type FFIDNewsletterUnsubscribeParams, type FFIDNewsletterUnsubscribeResponse, FFIDOAuthUserInfo, FFIDOrganization, type FFIDOtpSendResponse, type FFIDOtpVerifyResponse, type FFIDPasswordResetConfirmResponse, type FFIDPasswordResetResponse, type FFIDPasswordResetVerifyResponse, type FFIDPlanChangeLineItem, type FFIDPlanChangePreview, type FFIDPlanChangePreviewResponse, type FFIDPlanInfo, FFIDPortalSessionResponse, type FFIDPreviewPlanChangeParams, type FFIDPreviewSeatChangeParams, FFIDProvider, type FFIDProviderProps, FFIDRedirectResult, FFIDRemoveMemberResponse, type FFIDResetSessionResponse, FFIDSDKError, type FFIDSeatChangeLineItem, type FFIDSeatChangePreview, type FFIDSeatChangePreviewResponse, type FFIDServiceInfo, FFIDSessionResponse, type FFIDSubscribeParams, type FFIDSubscribeResponse, FFIDSubscription, FFIDSubscriptionCheckResponse, FFIDSubscriptionContextValue, type FFIDSubscriptionDetail, FFIDSubscriptionStatus, type FFIDSubscriptionSummary, type FFIDSupportedCurrency, FFIDUpdateMemberRoleResponse, FFIDUser, FFIDVerifyAccessTokenOptions, FFID_ANNOUNCEMENTS_ERROR_CODES, FFID_NEWSLETTER_TYPES, type KVNamespaceLike, ListAnnouncementsOptions, type NormalizeRedirectUriResult, type RedirectToAuthorizeOptions, type TokenData, type TokenStore, type UseFFIDReturn, type WithFFIDAuthOptions, type WithSubscriptionOptions, createFFIDAnnouncementsClient, createFFIDClient, createKVCacheAdapter, createMemoryCacheAdapter, createTokenStore, generateCodeChallenge, generateCodeVerifier, normalizeRedirectUri, retrieveCodeVerifier, storeCodeVerifier, useFFID, useSubscription, withFFIDAuth, withSubscription };
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { useFFIDContext } from './chunk-2OAFWUEL.js';
2
- export { DEFAULT_API_BASE_URL, FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDInquiryForm, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDProvider, FFIDSubscriptionBadge, FFIDUserMenu, FFID_ANNOUNCEMENTS_ERROR_CODES, FFID_INQUIRY_CATEGORIES, createFFIDAnnouncementsClient, createFFIDClient, createTokenStore, generateCodeChallenge, generateCodeVerifier, retrieveCodeVerifier, storeCodeVerifier, useFFID, useFFIDAnnouncements, useSubscription, withSubscription } from './chunk-2OAFWUEL.js';
1
+ import { useFFIDContext } from './chunk-MDBV4WY3.js';
2
+ export { DEFAULT_API_BASE_URL, FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDInquiryForm, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDProvider, FFIDSDKError, FFIDSubscriptionBadge, FFIDUserMenu, FFID_ANNOUNCEMENTS_ERROR_CODES, FFID_INQUIRY_CATEGORIES, createFFIDAnnouncementsClient, createFFIDClient, createTokenStore, generateCodeChallenge, generateCodeVerifier, normalizeRedirectUri, retrieveCodeVerifier, storeCodeVerifier, useFFID, useFFIDAnnouncements, useSubscription, withSubscription } from './chunk-MDBV4WY3.js';
3
3
  import { useRef, useEffect } from 'react';
4
4
  import { jsx, Fragment } from 'react/jsx-runtime';
5
5
 
@@ -453,9 +453,118 @@ function createBillingMethods(deps) {
453
453
  var EXT_PLANS_ENDPOINT = "/api/v1/subscriptions/ext/plans";
454
454
  var EXT_SUBSCRIPTION_ENDPOINT = "/api/v1/subscriptions/ext";
455
455
  var EXT_SUBSCRIBE_ENDPOINT = "/api/v1/subscriptions/ext/subscribe";
456
+ var FFIDSDKError = class extends Error {
457
+ code;
458
+ constructor(code, message) {
459
+ super(message);
460
+ this.name = "FFIDSDKError";
461
+ this.code = code;
462
+ }
463
+ };
456
464
  function isBlankString(value) {
457
465
  return typeof value !== "string" || value.trim() === "";
458
466
  }
467
+ var MALFORMED_PREVIEW_CODE = "MALFORMED_PLAN_CHANGE_PREVIEW";
468
+ var MALFORMED_SEAT_PREVIEW_CODE = "MALFORMED_SEAT_CHANGE_PREVIEW";
469
+ var SEAT_ESTIMATE_REASONS = /* @__PURE__ */ new Set(["no_stripe_data", "custom_pricing", "stripe_error"]);
470
+ function validatePlanChangePreview(preview) {
471
+ if (preview === null || typeof preview !== "object") {
472
+ throw new FFIDSDKError(
473
+ MALFORMED_PREVIEW_CODE,
474
+ "SDK: server returned malformed PlanChangePreview \u2014 expected object, got " + (preview === null ? "null" : typeof preview)
475
+ );
476
+ }
477
+ const p = preview;
478
+ const flag = p.willApplyAtPeriodEnd;
479
+ if (typeof flag !== "boolean") {
480
+ throw new FFIDSDKError(
481
+ MALFORMED_PREVIEW_CODE,
482
+ `SDK: server returned malformed PlanChangePreview \u2014 willApplyAtPeriodEnd must be boolean (got ${typeof flag})`
483
+ );
484
+ }
485
+ const { proratedAmount, effectiveDate } = p;
486
+ if (flag === true) {
487
+ if (proratedAmount !== 0) {
488
+ throw new FFIDSDKError(
489
+ MALFORMED_PREVIEW_CODE,
490
+ `SDK: server returned malformed PlanChangePreview \u2014 willApplyAtPeriodEnd=true requires proratedAmount=0 (got ${JSON.stringify(proratedAmount)})`
491
+ );
492
+ }
493
+ if (effectiveDate !== null && typeof effectiveDate !== "string") {
494
+ throw new FFIDSDKError(
495
+ MALFORMED_PREVIEW_CODE,
496
+ `SDK: server returned malformed PlanChangePreview \u2014 willApplyAtPeriodEnd=true requires effectiveDate to be string or null (got ${typeof effectiveDate})`
497
+ );
498
+ }
499
+ return;
500
+ }
501
+ if (effectiveDate !== null) {
502
+ throw new FFIDSDKError(
503
+ MALFORMED_PREVIEW_CODE,
504
+ `SDK: server returned malformed PlanChangePreview \u2014 willApplyAtPeriodEnd=false requires effectiveDate=null (got ${JSON.stringify(effectiveDate)})`
505
+ );
506
+ }
507
+ if (typeof proratedAmount !== "number") {
508
+ throw new FFIDSDKError(
509
+ MALFORMED_PREVIEW_CODE,
510
+ `SDK: server returned malformed PlanChangePreview \u2014 willApplyAtPeriodEnd=false requires proratedAmount to be a number (got ${typeof proratedAmount})`
511
+ );
512
+ }
513
+ }
514
+ function validateSeatChangePreview(preview) {
515
+ if (preview === null || typeof preview !== "object") {
516
+ throw new FFIDSDKError(
517
+ MALFORMED_SEAT_PREVIEW_CODE,
518
+ "SDK: server returned malformed SeatChangePreview \u2014 expected object, got " + (preview === null ? "null" : typeof preview)
519
+ );
520
+ }
521
+ const p = preview;
522
+ if (p.type !== "seat-change") {
523
+ throw new FFIDSDKError(
524
+ MALFORMED_SEAT_PREVIEW_CODE,
525
+ `SDK: server returned malformed SeatChangePreview \u2014 type must be 'seat-change' (got ${JSON.stringify(p.type)})`
526
+ );
527
+ }
528
+ const numericFields = [
529
+ "currentQuantity",
530
+ "newQuantity",
531
+ "unitPrice",
532
+ "proratedAmount",
533
+ "nextInvoiceAmount"
534
+ ];
535
+ for (const key of numericFields) {
536
+ if (typeof p[key] !== "number") {
537
+ throw new FFIDSDKError(
538
+ MALFORMED_SEAT_PREVIEW_CODE,
539
+ `SDK: server returned malformed SeatChangePreview \u2014 ${key} must be a number (got ${typeof p[key]})`
540
+ );
541
+ }
542
+ }
543
+ if (p.nextInvoiceDate !== null && typeof p.nextInvoiceDate !== "string") {
544
+ throw new FFIDSDKError(
545
+ MALFORMED_SEAT_PREVIEW_CODE,
546
+ `SDK: server returned malformed SeatChangePreview \u2014 nextInvoiceDate must be string or null (got ${typeof p.nextInvoiceDate})`
547
+ );
548
+ }
549
+ if (typeof p.isEstimate !== "boolean") {
550
+ throw new FFIDSDKError(
551
+ MALFORMED_SEAT_PREVIEW_CODE,
552
+ `SDK: server returned malformed SeatChangePreview \u2014 isEstimate must be boolean (got ${typeof p.isEstimate})`
553
+ );
554
+ }
555
+ if (p.estimateReason !== void 0 && !SEAT_ESTIMATE_REASONS.has(p.estimateReason)) {
556
+ throw new FFIDSDKError(
557
+ MALFORMED_SEAT_PREVIEW_CODE,
558
+ `SDK: server returned malformed SeatChangePreview \u2014 estimateReason must be one of 'no_stripe_data' | 'custom_pricing' | 'stripe_error' (got ${JSON.stringify(p.estimateReason)})`
559
+ );
560
+ }
561
+ if (!Array.isArray(p.lineItems)) {
562
+ throw new FFIDSDKError(
563
+ MALFORMED_SEAT_PREVIEW_CODE,
564
+ `SDK: server returned malformed SeatChangePreview \u2014 lineItems must be an array (got ${typeof p.lineItems})`
565
+ );
566
+ }
567
+ }
459
568
  function createSubscriptionMethods(deps) {
460
569
  const { fetchWithAuth, createError } = deps;
461
570
  async function listPlans() {
@@ -542,7 +651,7 @@ function createSubscriptionMethods(deps) {
542
651
  error: createError("VALIDATION_ERROR", "subscriptionId \u3068 planCode \u306F\u5FC5\u9808\u3067\u3059")
543
652
  };
544
653
  }
545
- return fetchWithAuth(
654
+ const response = await fetchWithAuth(
546
655
  `${EXT_SUBSCRIPTION_ENDPOINT}/${encodeURIComponent(params.subscriptionId)}/plan/preview`,
547
656
  {
548
657
  method: "POST",
@@ -552,6 +661,36 @@ function createSubscriptionMethods(deps) {
552
661
  })
553
662
  }
554
663
  );
664
+ if (response.data !== void 0) {
665
+ validatePlanChangePreview(response.data.preview);
666
+ }
667
+ return response;
668
+ }
669
+ async function previewSeatChange(params) {
670
+ if (isBlankString(params.subscriptionId)) {
671
+ return {
672
+ error: createError("VALIDATION_ERROR", "subscriptionId \u306F\u5FC5\u9808\u3067\u3059")
673
+ };
674
+ }
675
+ if (typeof params.quantity !== "number" || !Number.isInteger(params.quantity) || params.quantity < 1) {
676
+ return {
677
+ error: createError(
678
+ "VALIDATION_ERROR",
679
+ `quantity \u306F 1 \u4EE5\u4E0A\u306E\u6574\u6570\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059 (received: ${String(params.quantity)})`
680
+ )
681
+ };
682
+ }
683
+ const response = await fetchWithAuth(
684
+ `${EXT_SUBSCRIPTION_ENDPOINT}/${encodeURIComponent(params.subscriptionId)}/seats/preview`,
685
+ {
686
+ method: "POST",
687
+ body: JSON.stringify({ quantity: params.quantity })
688
+ }
689
+ );
690
+ if (response.data !== void 0) {
691
+ validateSeatChangePreview(response.data.preview);
692
+ }
693
+ return response;
555
694
  }
556
695
  return {
557
696
  listPlans,
@@ -560,7 +699,8 @@ function createSubscriptionMethods(deps) {
560
699
  changePlan,
561
700
  cancelSubscription,
562
701
  cancelPendingDowngrade,
563
- previewPlanChange
702
+ previewPlanChange,
703
+ previewSeatChange
564
704
  };
565
705
  }
566
706
 
@@ -617,7 +757,7 @@ function createMembersMethods(deps) {
617
757
  }
618
758
 
619
759
  // src/client/version-check.ts
620
- var SDK_VERSION = "2.11.0";
760
+ var SDK_VERSION = "2.12.1";
621
761
  var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
622
762
  var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
623
763
  function sdkHeaders() {
@@ -1227,6 +1367,23 @@ function createRedirectMethods(deps) {
1227
1367
  return { redirectToLogin, redirectToAuthorize, getLoginUrl, getSignupUrl, getLogoutUrl, redirectToLogout };
1228
1368
  }
1229
1369
 
1370
+ // src/client/redirect-uri.ts
1371
+ var AUTHORITY_BOUNDARY = /^([a-zA-Z][a-zA-Z0-9+.-]*:\/\/[^/?#]+)([/?#]?)/;
1372
+ function normalizeRedirectUri(input) {
1373
+ const url = new URL(input);
1374
+ const isRootPath = url.pathname === "" || url.pathname === "/";
1375
+ if (!isRootPath) {
1376
+ return { normalized: input, changed: false };
1377
+ }
1378
+ const match = input.match(AUTHORITY_BOUNDARY);
1379
+ if (match === null || match[2] === "/") {
1380
+ return { normalized: input, changed: false };
1381
+ }
1382
+ const authority = match[1];
1383
+ const rest = input.slice(authority.length);
1384
+ return { normalized: `${authority}/${rest}`, changed: true };
1385
+ }
1386
+
1230
1387
  // src/client/password-reset.ts
1231
1388
  var RESET_PASSWORD_BASE = "/api/v1/auth/reset-password";
1232
1389
  function isBlank(value) {
@@ -1737,6 +1894,24 @@ var FFID_ERROR_CODES = {
1737
1894
  TOKEN_VERIFICATION_ERROR: "TOKEN_VERIFICATION_ERROR"
1738
1895
  };
1739
1896
  var EXT_CHECK_ENDPOINT = "/api/v1/subscriptions/ext/check";
1897
+ function resolveRedirectUri(raw, logger) {
1898
+ if (raw === null) return null;
1899
+ try {
1900
+ const { normalized, changed } = normalizeRedirectUri(raw);
1901
+ if (changed) {
1902
+ logger.warn(
1903
+ `FFID Client: redirect_uri \u3092\u6B63\u898F\u5316\u3057\u307E\u3057\u305F (${raw} \u2192 ${normalized})\u3002FFID \u7BA1\u7406\u753B\u9762\u3067\u306E\u767B\u9332\u5024\u3068\u4E00\u81F4\u3055\u305B\u3066\u304F\u3060\u3055\u3044\u3002`
1904
+ );
1905
+ }
1906
+ return normalized;
1907
+ } catch (error) {
1908
+ logger.warn(
1909
+ `FFID Client: redirectUri \u306E\u30D1\u30FC\u30B9\u306B\u5931\u6557\u3057\u305F\u305F\u3081\u6B63\u898F\u5316\u3092\u30B9\u30AD\u30C3\u30D7\u3057\u307E\u3057\u305F (${raw})`,
1910
+ error
1911
+ );
1912
+ return raw;
1913
+ }
1914
+ }
1740
1915
  function createFFIDClient(config) {
1741
1916
  if (!config.serviceCode || !config.serviceCode.trim()) {
1742
1917
  throw new Error("FFID Client: serviceCode \u304C\u672A\u8A2D\u5B9A\u3067\u3059");
@@ -1744,7 +1919,7 @@ function createFFIDClient(config) {
1744
1919
  const baseUrl = config.apiBaseUrl ?? chunkYUIITYBE_cjs.DEFAULT_API_BASE_URL;
1745
1920
  const authMode = config.authMode ?? "cookie";
1746
1921
  const clientId = config.clientId ?? config.serviceCode;
1747
- const resolvedRedirectUri = config.redirectUri ?? null;
1922
+ const rawRedirectUri = config.redirectUri ?? null;
1748
1923
  const serviceApiKey = config.serviceApiKey?.trim();
1749
1924
  const verifyStrategy = config.verifyStrategy ?? "jwt";
1750
1925
  const cache = config.cache;
@@ -1759,6 +1934,7 @@ function createFFIDClient(config) {
1759
1934
  throw new Error("FFID Client: timeout \u306F\u6B63\u306E\u6570\u5024\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044");
1760
1935
  }
1761
1936
  const logger = config.logger ?? (config.debug ? consoleLogger : noopLogger);
1937
+ const resolvedRedirectUri = resolveRedirectUri(rawRedirectUri, logger);
1762
1938
  const tokenStore = authMode === "token" ? createTokenStore() : createTokenStore("memory");
1763
1939
  function createError(code, message) {
1764
1940
  return { code, message };
@@ -1941,7 +2117,8 @@ function createFFIDClient(config) {
1941
2117
  changePlan,
1942
2118
  cancelSubscription,
1943
2119
  cancelPendingDowngrade,
1944
- previewPlanChange
2120
+ previewPlanChange,
2121
+ previewSeatChange
1945
2122
  } = createSubscriptionMethods({
1946
2123
  fetchWithAuth,
1947
2124
  createError
@@ -2031,6 +2208,7 @@ function createFFIDClient(config) {
2031
2208
  cancelSubscription,
2032
2209
  cancelPendingDowngrade,
2033
2210
  previewPlanChange,
2211
+ previewSeatChange,
2034
2212
  verifyAccessToken,
2035
2213
  getSubscribeUrl,
2036
2214
  redirectToSubscribe,