@feelflow/ffid-sdk 5.3.1 → 5.5.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/dist/{FFIDCookieLink-Bt0f1gyo.d.cts → FFIDCookieLink-CE01gYc4.d.cts} +40 -1
- package/dist/{FFIDCookieLink-Bt0f1gyo.d.ts → FFIDCookieLink-CE01gYc4.d.ts} +40 -1
- package/dist/{chunk-AAMNUM62.cjs → chunk-JKP3MBTT.cjs} +82 -59
- package/dist/{chunk-IVKFKMLQ.js → chunk-KJ7FUNA6.js} +21 -3
- package/dist/{chunk-QZN4WZCV.cjs → chunk-Q5SZVLNB.cjs} +20 -2
- package/dist/{chunk-ARKFHB72.js → chunk-WNYMSUHN.js} +79 -60
- package/dist/components/index.cjs +8 -8
- package/dist/components/index.d.cts +1 -1
- package/dist/components/index.d.ts +1 -1
- package/dist/components/index.js +1 -1
- package/dist/consent/index.cjs +64 -64
- package/dist/consent/index.d.cts +2 -2
- package/dist/consent/index.d.ts +2 -2
- package/dist/consent/index.js +1 -1
- package/dist/{index-DfnHXRMX.d.ts → index-lAvUS_oz.d.cts} +1 -1
- package/dist/{index-DfnHXRMX.d.cts → index-lAvUS_oz.d.ts} +1 -1
- package/dist/index.cjs +69 -53
- package/dist/index.d.cts +108 -4
- package/dist/index.d.ts +108 -4
- package/dist/index.js +3 -3
- package/dist/server/index.cjs +13 -8
- package/dist/server/index.js +13 -8
- package/package.json +1 -1
|
@@ -341,9 +341,32 @@ type GetDocumentWire = z.infer<typeof GetDocumentWireSchema>;
|
|
|
341
341
|
/**
|
|
342
342
|
* Warning event surfaced to `onConsentChange(state, warning)` after a `/sync`
|
|
343
343
|
* call. Spec §5.3 requires non-null warnings to be visible to the user.
|
|
344
|
+
*
|
|
345
|
+
* @remarks
|
|
346
|
+
* Multi-locale consumers should pattern-match on `code` and resolve their own
|
|
347
|
+
* copy. The `message` field defaults to the SDK's built-in Japanese copy
|
|
348
|
+
* (`DEFAULT_MERGE_WARNING_MESSAGES`); pass `mergeWarningMessages` on
|
|
349
|
+
* `<FFIDAnalyticsProvider>` to inject locale-specific overrides without
|
|
350
|
+
* losing the warning event itself.
|
|
344
351
|
*/
|
|
345
352
|
interface FFIDConsentMergeWarningEvent {
|
|
346
353
|
code: FFIDConsentMergeWarning;
|
|
354
|
+
/**
|
|
355
|
+
* Human-readable copy. Defaults to the SDK's built-in Japanese copy unless
|
|
356
|
+
* `<FFIDAnalyticsProvider mergeWarningMessages={...}>` overrides it.
|
|
357
|
+
*
|
|
358
|
+
* @deprecated Scheduled for removal in `@feelflow/ffid-sdk` v6.0 so that
|
|
359
|
+
* `FFIDConsentMergeWarningEvent` becomes symmetric with
|
|
360
|
+
* `FFIDConsentSyncResult.warning` (code-only). Consumers should
|
|
361
|
+
* pattern-match on `code` and resolve copy in their own i18n layer.
|
|
362
|
+
*
|
|
363
|
+
* Migration: replace `event.message` reads with `messages[event.code]`
|
|
364
|
+
* where `messages` is typed as
|
|
365
|
+
* `Record<FFIDConsentMergeWarning, string>` (not `Partial<...>`) so
|
|
366
|
+
* TypeScript exhaustiveness-checks every enum value at compile time
|
|
367
|
+
* and the dictionary can never leak `undefined` the way an unchecked
|
|
368
|
+
* `event.message` could in pre-v5.5 SDKs.
|
|
369
|
+
*/
|
|
347
370
|
message: string;
|
|
348
371
|
}
|
|
349
372
|
/**
|
|
@@ -708,11 +731,27 @@ interface FFIDAnalyticsProviderProps {
|
|
|
708
731
|
* suppressed by a UX dismissal.
|
|
709
732
|
*/
|
|
710
733
|
dismissalCooldownMinutes?: number;
|
|
734
|
+
/**
|
|
735
|
+
* Per-locale override map for `FFIDConsentMergeWarningEvent.message`.
|
|
736
|
+
*
|
|
737
|
+
* The Provider derives `message` for the `onConsentChange(state, warning)`
|
|
738
|
+
* callback from this map, falling back to `DEFAULT_MERGE_WARNING_MESSAGES`
|
|
739
|
+
* (JP) for any missing key. Pass only the keys you need to translate —
|
|
740
|
+
* partial overrides are merged with the defaults.
|
|
741
|
+
*
|
|
742
|
+
* Note: the underlying `code` (`FFIDConsentMergeWarning`) is the canonical
|
|
743
|
+
* identifier — multi-locale consumers should prefer pattern-matching on
|
|
744
|
+
* `warning.code` over reading `warning.message` directly. The `message`
|
|
745
|
+
* field is `@deprecated` and scheduled for removal in v6.0; this prop is
|
|
746
|
+
* the interim escape hatch so non-JP consumers don't have to wait for the
|
|
747
|
+
* v6.0 major to surface localized copy.
|
|
748
|
+
*/
|
|
749
|
+
mergeWarningMessages?: Partial<Record<FFIDConsentMergeWarning, string>>;
|
|
711
750
|
/** Optional fetch override (mainly for tests). */
|
|
712
751
|
fetchImpl?: typeof fetch;
|
|
713
752
|
children: ReactNode;
|
|
714
753
|
}
|
|
715
|
-
declare function FFIDAnalyticsProvider({ baseUrl, serviceApiKey, getAccessToken, gaMeasurementId, sentryReplay, sentry, onConsentChange, onError, autoShowBanner, dismissalCooldownMinutes, fetchImpl, children, }: FFIDAnalyticsProviderProps): React.JSX.Element;
|
|
754
|
+
declare function FFIDAnalyticsProvider({ baseUrl, serviceApiKey, getAccessToken, gaMeasurementId, sentryReplay, sentry, onConsentChange, onError, autoShowBanner, dismissalCooldownMinutes, mergeWarningMessages, fetchImpl, children, }: FFIDAnalyticsProviderProps): React.JSX.Element;
|
|
716
755
|
|
|
717
756
|
interface FFIDConsentContextValue {
|
|
718
757
|
/** Current consent state. Always non-null; "not yet decided" is encoded by `hasDecided=false`. */
|
|
@@ -341,9 +341,32 @@ type GetDocumentWire = z.infer<typeof GetDocumentWireSchema>;
|
|
|
341
341
|
/**
|
|
342
342
|
* Warning event surfaced to `onConsentChange(state, warning)` after a `/sync`
|
|
343
343
|
* call. Spec §5.3 requires non-null warnings to be visible to the user.
|
|
344
|
+
*
|
|
345
|
+
* @remarks
|
|
346
|
+
* Multi-locale consumers should pattern-match on `code` and resolve their own
|
|
347
|
+
* copy. The `message` field defaults to the SDK's built-in Japanese copy
|
|
348
|
+
* (`DEFAULT_MERGE_WARNING_MESSAGES`); pass `mergeWarningMessages` on
|
|
349
|
+
* `<FFIDAnalyticsProvider>` to inject locale-specific overrides without
|
|
350
|
+
* losing the warning event itself.
|
|
344
351
|
*/
|
|
345
352
|
interface FFIDConsentMergeWarningEvent {
|
|
346
353
|
code: FFIDConsentMergeWarning;
|
|
354
|
+
/**
|
|
355
|
+
* Human-readable copy. Defaults to the SDK's built-in Japanese copy unless
|
|
356
|
+
* `<FFIDAnalyticsProvider mergeWarningMessages={...}>` overrides it.
|
|
357
|
+
*
|
|
358
|
+
* @deprecated Scheduled for removal in `@feelflow/ffid-sdk` v6.0 so that
|
|
359
|
+
* `FFIDConsentMergeWarningEvent` becomes symmetric with
|
|
360
|
+
* `FFIDConsentSyncResult.warning` (code-only). Consumers should
|
|
361
|
+
* pattern-match on `code` and resolve copy in their own i18n layer.
|
|
362
|
+
*
|
|
363
|
+
* Migration: replace `event.message` reads with `messages[event.code]`
|
|
364
|
+
* where `messages` is typed as
|
|
365
|
+
* `Record<FFIDConsentMergeWarning, string>` (not `Partial<...>`) so
|
|
366
|
+
* TypeScript exhaustiveness-checks every enum value at compile time
|
|
367
|
+
* and the dictionary can never leak `undefined` the way an unchecked
|
|
368
|
+
* `event.message` could in pre-v5.5 SDKs.
|
|
369
|
+
*/
|
|
347
370
|
message: string;
|
|
348
371
|
}
|
|
349
372
|
/**
|
|
@@ -708,11 +731,27 @@ interface FFIDAnalyticsProviderProps {
|
|
|
708
731
|
* suppressed by a UX dismissal.
|
|
709
732
|
*/
|
|
710
733
|
dismissalCooldownMinutes?: number;
|
|
734
|
+
/**
|
|
735
|
+
* Per-locale override map for `FFIDConsentMergeWarningEvent.message`.
|
|
736
|
+
*
|
|
737
|
+
* The Provider derives `message` for the `onConsentChange(state, warning)`
|
|
738
|
+
* callback from this map, falling back to `DEFAULT_MERGE_WARNING_MESSAGES`
|
|
739
|
+
* (JP) for any missing key. Pass only the keys you need to translate —
|
|
740
|
+
* partial overrides are merged with the defaults.
|
|
741
|
+
*
|
|
742
|
+
* Note: the underlying `code` (`FFIDConsentMergeWarning`) is the canonical
|
|
743
|
+
* identifier — multi-locale consumers should prefer pattern-matching on
|
|
744
|
+
* `warning.code` over reading `warning.message` directly. The `message`
|
|
745
|
+
* field is `@deprecated` and scheduled for removal in v6.0; this prop is
|
|
746
|
+
* the interim escape hatch so non-JP consumers don't have to wait for the
|
|
747
|
+
* v6.0 major to surface localized copy.
|
|
748
|
+
*/
|
|
749
|
+
mergeWarningMessages?: Partial<Record<FFIDConsentMergeWarning, string>>;
|
|
711
750
|
/** Optional fetch override (mainly for tests). */
|
|
712
751
|
fetchImpl?: typeof fetch;
|
|
713
752
|
children: ReactNode;
|
|
714
753
|
}
|
|
715
|
-
declare function FFIDAnalyticsProvider({ baseUrl, serviceApiKey, getAccessToken, gaMeasurementId, sentryReplay, sentry, onConsentChange, onError, autoShowBanner, dismissalCooldownMinutes, fetchImpl, children, }: FFIDAnalyticsProviderProps): React.JSX.Element;
|
|
754
|
+
declare function FFIDAnalyticsProvider({ baseUrl, serviceApiKey, getAccessToken, gaMeasurementId, sentryReplay, sentry, onConsentChange, onError, autoShowBanner, dismissalCooldownMinutes, mergeWarningMessages, fetchImpl, children, }: FFIDAnalyticsProviderProps): React.JSX.Element;
|
|
716
755
|
|
|
717
756
|
interface FFIDConsentContextValue {
|
|
718
757
|
/** Current consent state. Always non-null; "not yet decided" is encoded by `hasDecided=false`. */
|
|
@@ -842,7 +842,7 @@ function createProfileMethods(deps) {
|
|
|
842
842
|
}
|
|
843
843
|
|
|
844
844
|
// src/client/version-check.ts
|
|
845
|
-
var SDK_VERSION = "5.
|
|
845
|
+
var SDK_VERSION = "5.5.0";
|
|
846
846
|
var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
|
|
847
847
|
var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
|
|
848
848
|
function sdkHeaders() {
|
|
@@ -2231,6 +2231,83 @@ function createInquiryMethods(deps) {
|
|
|
2231
2231
|
return { create };
|
|
2232
2232
|
}
|
|
2233
2233
|
|
|
2234
|
+
// src/subscriptions/types.ts
|
|
2235
|
+
var PAST_DUE_GRACE_PERIOD_DAYS = 7;
|
|
2236
|
+
var HOURS_PER_DAY = 24;
|
|
2237
|
+
var MINUTES_PER_HOUR = 60;
|
|
2238
|
+
var SECONDS_PER_MINUTE = 60;
|
|
2239
|
+
var MS_PER_SECOND2 = 1e3;
|
|
2240
|
+
var MS_PER_DAY = HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE * MS_PER_SECOND2;
|
|
2241
|
+
|
|
2242
|
+
// src/subscriptions/compute-effective-status.ts
|
|
2243
|
+
function isExpired(isoTimestamp, nowMs) {
|
|
2244
|
+
if (!isoTimestamp) return null;
|
|
2245
|
+
const parsed = Date.parse(isoTimestamp);
|
|
2246
|
+
if (Number.isNaN(parsed)) return null;
|
|
2247
|
+
return parsed < nowMs;
|
|
2248
|
+
}
|
|
2249
|
+
function computeEffectiveStatusFromSession(input, nowMs = Date.now()) {
|
|
2250
|
+
const { status, currentPeriodEnd, trialEnd, pastDueSince } = input;
|
|
2251
|
+
switch (status) {
|
|
2252
|
+
case "trialing": {
|
|
2253
|
+
const relevantEnd = trialEnd ?? currentPeriodEnd;
|
|
2254
|
+
const expired = isExpired(relevantEnd, nowMs);
|
|
2255
|
+
return expired === true ? "trial_expired" : "active";
|
|
2256
|
+
}
|
|
2257
|
+
case "active": {
|
|
2258
|
+
const expired = isExpired(currentPeriodEnd, nowMs);
|
|
2259
|
+
return expired === true ? "expired" : "active";
|
|
2260
|
+
}
|
|
2261
|
+
case "past_due": {
|
|
2262
|
+
const baselineIso = pastDueSince ?? currentPeriodEnd;
|
|
2263
|
+
if (!baselineIso) return "blocked";
|
|
2264
|
+
const baselineMs = Date.parse(baselineIso);
|
|
2265
|
+
if (Number.isNaN(baselineMs)) return "blocked";
|
|
2266
|
+
const graceEndMs = baselineMs + PAST_DUE_GRACE_PERIOD_DAYS * MS_PER_DAY;
|
|
2267
|
+
return graceEndMs > nowMs ? "past_due_grace" : "blocked";
|
|
2268
|
+
}
|
|
2269
|
+
case "pending_invoice":
|
|
2270
|
+
return "active";
|
|
2271
|
+
case "paused":
|
|
2272
|
+
case "incomplete":
|
|
2273
|
+
return "active";
|
|
2274
|
+
case "canceled":
|
|
2275
|
+
return "canceled";
|
|
2276
|
+
case "unpaid":
|
|
2277
|
+
case "incomplete_expired":
|
|
2278
|
+
return "blocked";
|
|
2279
|
+
default: {
|
|
2280
|
+
return "blocked";
|
|
2281
|
+
}
|
|
2282
|
+
}
|
|
2283
|
+
}
|
|
2284
|
+
|
|
2285
|
+
// src/subscriptions/userinfo-helpers.ts
|
|
2286
|
+
var ACCESS_GRANTING_EFFECTIVE_STATUSES = [
|
|
2287
|
+
"active",
|
|
2288
|
+
"past_due_grace"
|
|
2289
|
+
];
|
|
2290
|
+
var BLOCKING_EFFECTIVE_STATUSES = [
|
|
2291
|
+
"blocked",
|
|
2292
|
+
"canceled",
|
|
2293
|
+
"expired",
|
|
2294
|
+
"trial_expired"
|
|
2295
|
+
];
|
|
2296
|
+
function hasAccessFromUserinfo(sub) {
|
|
2297
|
+
const effectiveStatus = sub?.effectiveStatus;
|
|
2298
|
+
if (effectiveStatus === void 0 || effectiveStatus === null) {
|
|
2299
|
+
return false;
|
|
2300
|
+
}
|
|
2301
|
+
return ACCESS_GRANTING_EFFECTIVE_STATUSES.includes(effectiveStatus);
|
|
2302
|
+
}
|
|
2303
|
+
function isBlockedFromUserinfo(sub) {
|
|
2304
|
+
const effectiveStatus = sub?.effectiveStatus;
|
|
2305
|
+
if (effectiveStatus === void 0 || effectiveStatus === null) {
|
|
2306
|
+
return false;
|
|
2307
|
+
}
|
|
2308
|
+
return BLOCKING_EFFECTIVE_STATUSES.includes(effectiveStatus);
|
|
2309
|
+
}
|
|
2310
|
+
|
|
2234
2311
|
// src/client/ffid-client.ts
|
|
2235
2312
|
var UNAUTHORIZED_STATUS2 = 401;
|
|
2236
2313
|
var SDK_LOG_PREFIX = "[FFID SDK]";
|
|
@@ -2261,13 +2338,6 @@ var EXT_CHECK_ENDPOINT = "/api/v1/subscriptions/ext/check";
|
|
|
2261
2338
|
var DEFAULT_ALLOW_GRACE = true;
|
|
2262
2339
|
var DEFAULT_SERVICE_ACCESS_FAIL_POLICY = "failClosed";
|
|
2263
2340
|
var SERVICE_ACCESS_DENIED_CODE = "SERVICE_ACCESS_DENIED";
|
|
2264
|
-
var ACCESS_GRANTING_EFFECTIVE_STATUSES = ["active", "past_due_grace"];
|
|
2265
|
-
var BLOCKING_EFFECTIVE_STATUSES = [
|
|
2266
|
-
"blocked",
|
|
2267
|
-
"canceled",
|
|
2268
|
-
"expired",
|
|
2269
|
-
"trial_expired"
|
|
2270
|
-
];
|
|
2271
2341
|
function resolveServiceAccessDenialReason(params, allowGrace) {
|
|
2272
2342
|
if (params.hasAccess) {
|
|
2273
2343
|
return null;
|
|
@@ -3450,57 +3520,6 @@ function FFIDOrganizationSwitcher({
|
|
|
3450
3520
|
] })
|
|
3451
3521
|
] });
|
|
3452
3522
|
}
|
|
3453
|
-
|
|
3454
|
-
// src/subscriptions/types.ts
|
|
3455
|
-
var PAST_DUE_GRACE_PERIOD_DAYS = 7;
|
|
3456
|
-
var HOURS_PER_DAY = 24;
|
|
3457
|
-
var MINUTES_PER_HOUR = 60;
|
|
3458
|
-
var SECONDS_PER_MINUTE = 60;
|
|
3459
|
-
var MS_PER_SECOND2 = 1e3;
|
|
3460
|
-
var MS_PER_DAY = HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE * MS_PER_SECOND2;
|
|
3461
|
-
|
|
3462
|
-
// src/subscriptions/compute-effective-status.ts
|
|
3463
|
-
function isExpired(isoTimestamp, nowMs) {
|
|
3464
|
-
if (!isoTimestamp) return null;
|
|
3465
|
-
const parsed = Date.parse(isoTimestamp);
|
|
3466
|
-
if (Number.isNaN(parsed)) return null;
|
|
3467
|
-
return parsed < nowMs;
|
|
3468
|
-
}
|
|
3469
|
-
function computeEffectiveStatusFromSession(input, nowMs = Date.now()) {
|
|
3470
|
-
const { status, currentPeriodEnd, trialEnd, pastDueSince } = input;
|
|
3471
|
-
switch (status) {
|
|
3472
|
-
case "trialing": {
|
|
3473
|
-
const relevantEnd = trialEnd ?? currentPeriodEnd;
|
|
3474
|
-
const expired = isExpired(relevantEnd, nowMs);
|
|
3475
|
-
return expired === true ? "trial_expired" : "active";
|
|
3476
|
-
}
|
|
3477
|
-
case "active": {
|
|
3478
|
-
const expired = isExpired(currentPeriodEnd, nowMs);
|
|
3479
|
-
return expired === true ? "expired" : "active";
|
|
3480
|
-
}
|
|
3481
|
-
case "past_due": {
|
|
3482
|
-
const baselineIso = pastDueSince ?? currentPeriodEnd;
|
|
3483
|
-
if (!baselineIso) return "blocked";
|
|
3484
|
-
const baselineMs = Date.parse(baselineIso);
|
|
3485
|
-
if (Number.isNaN(baselineMs)) return "blocked";
|
|
3486
|
-
const graceEndMs = baselineMs + PAST_DUE_GRACE_PERIOD_DAYS * MS_PER_DAY;
|
|
3487
|
-
return graceEndMs > nowMs ? "past_due_grace" : "blocked";
|
|
3488
|
-
}
|
|
3489
|
-
case "pending_invoice":
|
|
3490
|
-
return "active";
|
|
3491
|
-
case "paused":
|
|
3492
|
-
case "incomplete":
|
|
3493
|
-
return "active";
|
|
3494
|
-
case "canceled":
|
|
3495
|
-
return "canceled";
|
|
3496
|
-
case "unpaid":
|
|
3497
|
-
case "incomplete_expired":
|
|
3498
|
-
return "blocked";
|
|
3499
|
-
default: {
|
|
3500
|
-
return "blocked";
|
|
3501
|
-
}
|
|
3502
|
-
}
|
|
3503
|
-
}
|
|
3504
3523
|
var ACCESS_GRANTING_STATUSES = [
|
|
3505
3524
|
"active",
|
|
3506
3525
|
"past_due_grace"
|
|
@@ -4800,6 +4819,8 @@ function FFIDInquiryForm({
|
|
|
4800
4819
|
);
|
|
4801
4820
|
}
|
|
4802
4821
|
|
|
4822
|
+
exports.ACCESS_GRANTING_EFFECTIVE_STATUSES = ACCESS_GRANTING_EFFECTIVE_STATUSES;
|
|
4823
|
+
exports.BLOCKING_EFFECTIVE_STATUSES = BLOCKING_EFFECTIVE_STATUSES;
|
|
4803
4824
|
exports.DEFAULT_API_BASE_URL = DEFAULT_API_BASE_URL;
|
|
4804
4825
|
exports.DEFAULT_OAUTH_SCOPES = DEFAULT_OAUTH_SCOPES;
|
|
4805
4826
|
exports.FFIDAnnouncementBadge = FFIDAnnouncementBadge;
|
|
@@ -4820,6 +4841,8 @@ exports.createFFIDClient = createFFIDClient;
|
|
|
4820
4841
|
exports.createTokenStore = createTokenStore;
|
|
4821
4842
|
exports.generateCodeChallenge = generateCodeChallenge;
|
|
4822
4843
|
exports.generateCodeVerifier = generateCodeVerifier;
|
|
4844
|
+
exports.hasAccessFromUserinfo = hasAccessFromUserinfo;
|
|
4845
|
+
exports.isBlockedFromUserinfo = isBlockedFromUserinfo;
|
|
4823
4846
|
exports.isFFIDInquiryCategorySite2026 = isFFIDInquiryCategorySite2026;
|
|
4824
4847
|
exports.normalizeRedirectUri = normalizeRedirectUri;
|
|
4825
4848
|
exports.retrieveCodeVerifier = retrieveCodeVerifier;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { createContext, useCallback, useState, useRef, useEffect,
|
|
2
|
+
import { createContext, useMemo, useCallback, useState, useRef, useEffect, useContext } from 'react';
|
|
3
3
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
4
4
|
|
|
5
5
|
var FFIDConsentCategoryCodeSchema = z.enum([
|
|
@@ -1071,10 +1071,18 @@ function FFIDAnalyticsProvider({
|
|
|
1071
1071
|
onError,
|
|
1072
1072
|
autoShowBanner = true,
|
|
1073
1073
|
dismissalCooldownMinutes = DEFAULT_DISMISSAL_COOLDOWN_MINUTES,
|
|
1074
|
+
mergeWarningMessages,
|
|
1074
1075
|
fetchImpl,
|
|
1075
1076
|
children
|
|
1076
1077
|
}) {
|
|
1077
1078
|
const cooldownMs = Number.isFinite(dismissalCooldownMinutes) && dismissalCooldownMinutes > 0 ? dismissalCooldownMinutes * MS_PER_MINUTE : 0;
|
|
1079
|
+
const resolvedMergeWarningMessages = useMemo(
|
|
1080
|
+
() => ({
|
|
1081
|
+
...DEFAULT_MERGE_WARNING_MESSAGES,
|
|
1082
|
+
...mergeWarningMessages
|
|
1083
|
+
}),
|
|
1084
|
+
[mergeWarningMessages]
|
|
1085
|
+
);
|
|
1078
1086
|
const isWithinDismissalCooldown = useCallback(() => {
|
|
1079
1087
|
if (cooldownMs <= 0) return false;
|
|
1080
1088
|
const dismissedAt = readConsentDismissalTimestamp();
|
|
@@ -1311,7 +1319,16 @@ function FFIDAnalyticsProvider({
|
|
|
1311
1319
|
persistCategories(result.value.state.categories);
|
|
1312
1320
|
const warning = result.value.warning ? {
|
|
1313
1321
|
code: result.value.warning,
|
|
1314
|
-
|
|
1322
|
+
// Belt-and-suspenders fallback per spec §5.3: an empty-string
|
|
1323
|
+
// override (`mergeWarningMessages={{ code: '' }}`) or a future
|
|
1324
|
+
// wire-schema widening that adds a new `FFIDConsentMergeWarning`
|
|
1325
|
+
// literal without a default copy would otherwise leak `''` /
|
|
1326
|
+
// `undefined` into `warning.message`, silently breaking consumers
|
|
1327
|
+
// that render it (`<Toast>{warning.message}</Toast>`,
|
|
1328
|
+
// `Sentry.setContext`). Truthy-fallback preserves visibility of
|
|
1329
|
+
// the JP default; consumers wanting to suppress display should
|
|
1330
|
+
// pattern-match on `code` instead of mutating `message`.
|
|
1331
|
+
message: resolvedMergeWarningMessages[result.value.warning] || DEFAULT_MERGE_WARNING_MESSAGES[result.value.warning]
|
|
1315
1332
|
} : null;
|
|
1316
1333
|
onConsentChange?.(result.value.state, warning);
|
|
1317
1334
|
return result;
|
|
@@ -1320,7 +1337,8 @@ function FFIDAnalyticsProvider({
|
|
|
1320
1337
|
ensurePolicyVersion,
|
|
1321
1338
|
onConsentChange,
|
|
1322
1339
|
onError,
|
|
1323
|
-
persistCategories
|
|
1340
|
+
persistCategories,
|
|
1341
|
+
resolvedMergeWarningMessages
|
|
1324
1342
|
]);
|
|
1325
1343
|
const openBanner = useCallback(() => setIsBannerOpen(true), []);
|
|
1326
1344
|
const closeBanner = useCallback(() => {
|
|
@@ -1073,10 +1073,18 @@ function FFIDAnalyticsProvider({
|
|
|
1073
1073
|
onError,
|
|
1074
1074
|
autoShowBanner = true,
|
|
1075
1075
|
dismissalCooldownMinutes = DEFAULT_DISMISSAL_COOLDOWN_MINUTES,
|
|
1076
|
+
mergeWarningMessages,
|
|
1076
1077
|
fetchImpl,
|
|
1077
1078
|
children
|
|
1078
1079
|
}) {
|
|
1079
1080
|
const cooldownMs = Number.isFinite(dismissalCooldownMinutes) && dismissalCooldownMinutes > 0 ? dismissalCooldownMinutes * MS_PER_MINUTE : 0;
|
|
1081
|
+
const resolvedMergeWarningMessages = react.useMemo(
|
|
1082
|
+
() => ({
|
|
1083
|
+
...DEFAULT_MERGE_WARNING_MESSAGES,
|
|
1084
|
+
...mergeWarningMessages
|
|
1085
|
+
}),
|
|
1086
|
+
[mergeWarningMessages]
|
|
1087
|
+
);
|
|
1080
1088
|
const isWithinDismissalCooldown = react.useCallback(() => {
|
|
1081
1089
|
if (cooldownMs <= 0) return false;
|
|
1082
1090
|
const dismissedAt = readConsentDismissalTimestamp();
|
|
@@ -1313,7 +1321,16 @@ function FFIDAnalyticsProvider({
|
|
|
1313
1321
|
persistCategories(result.value.state.categories);
|
|
1314
1322
|
const warning = result.value.warning ? {
|
|
1315
1323
|
code: result.value.warning,
|
|
1316
|
-
|
|
1324
|
+
// Belt-and-suspenders fallback per spec §5.3: an empty-string
|
|
1325
|
+
// override (`mergeWarningMessages={{ code: '' }}`) or a future
|
|
1326
|
+
// wire-schema widening that adds a new `FFIDConsentMergeWarning`
|
|
1327
|
+
// literal without a default copy would otherwise leak `''` /
|
|
1328
|
+
// `undefined` into `warning.message`, silently breaking consumers
|
|
1329
|
+
// that render it (`<Toast>{warning.message}</Toast>`,
|
|
1330
|
+
// `Sentry.setContext`). Truthy-fallback preserves visibility of
|
|
1331
|
+
// the JP default; consumers wanting to suppress display should
|
|
1332
|
+
// pattern-match on `code` instead of mutating `message`.
|
|
1333
|
+
message: resolvedMergeWarningMessages[result.value.warning] || DEFAULT_MERGE_WARNING_MESSAGES[result.value.warning]
|
|
1317
1334
|
} : null;
|
|
1318
1335
|
onConsentChange?.(result.value.state, warning);
|
|
1319
1336
|
return result;
|
|
@@ -1322,7 +1339,8 @@ function FFIDAnalyticsProvider({
|
|
|
1322
1339
|
ensurePolicyVersion,
|
|
1323
1340
|
onConsentChange,
|
|
1324
1341
|
onError,
|
|
1325
|
-
persistCategories
|
|
1342
|
+
persistCategories,
|
|
1343
|
+
resolvedMergeWarningMessages
|
|
1326
1344
|
]);
|
|
1327
1345
|
const openBanner = react.useCallback(() => setIsBannerOpen(true), []);
|
|
1328
1346
|
const closeBanner = react.useCallback(() => {
|
|
@@ -840,7 +840,7 @@ function createProfileMethods(deps) {
|
|
|
840
840
|
}
|
|
841
841
|
|
|
842
842
|
// src/client/version-check.ts
|
|
843
|
-
var SDK_VERSION = "5.
|
|
843
|
+
var SDK_VERSION = "5.5.0";
|
|
844
844
|
var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
|
|
845
845
|
var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
|
|
846
846
|
function sdkHeaders() {
|
|
@@ -2229,6 +2229,83 @@ function createInquiryMethods(deps) {
|
|
|
2229
2229
|
return { create };
|
|
2230
2230
|
}
|
|
2231
2231
|
|
|
2232
|
+
// src/subscriptions/types.ts
|
|
2233
|
+
var PAST_DUE_GRACE_PERIOD_DAYS = 7;
|
|
2234
|
+
var HOURS_PER_DAY = 24;
|
|
2235
|
+
var MINUTES_PER_HOUR = 60;
|
|
2236
|
+
var SECONDS_PER_MINUTE = 60;
|
|
2237
|
+
var MS_PER_SECOND2 = 1e3;
|
|
2238
|
+
var MS_PER_DAY = HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE * MS_PER_SECOND2;
|
|
2239
|
+
|
|
2240
|
+
// src/subscriptions/compute-effective-status.ts
|
|
2241
|
+
function isExpired(isoTimestamp, nowMs) {
|
|
2242
|
+
if (!isoTimestamp) return null;
|
|
2243
|
+
const parsed = Date.parse(isoTimestamp);
|
|
2244
|
+
if (Number.isNaN(parsed)) return null;
|
|
2245
|
+
return parsed < nowMs;
|
|
2246
|
+
}
|
|
2247
|
+
function computeEffectiveStatusFromSession(input, nowMs = Date.now()) {
|
|
2248
|
+
const { status, currentPeriodEnd, trialEnd, pastDueSince } = input;
|
|
2249
|
+
switch (status) {
|
|
2250
|
+
case "trialing": {
|
|
2251
|
+
const relevantEnd = trialEnd ?? currentPeriodEnd;
|
|
2252
|
+
const expired = isExpired(relevantEnd, nowMs);
|
|
2253
|
+
return expired === true ? "trial_expired" : "active";
|
|
2254
|
+
}
|
|
2255
|
+
case "active": {
|
|
2256
|
+
const expired = isExpired(currentPeriodEnd, nowMs);
|
|
2257
|
+
return expired === true ? "expired" : "active";
|
|
2258
|
+
}
|
|
2259
|
+
case "past_due": {
|
|
2260
|
+
const baselineIso = pastDueSince ?? currentPeriodEnd;
|
|
2261
|
+
if (!baselineIso) return "blocked";
|
|
2262
|
+
const baselineMs = Date.parse(baselineIso);
|
|
2263
|
+
if (Number.isNaN(baselineMs)) return "blocked";
|
|
2264
|
+
const graceEndMs = baselineMs + PAST_DUE_GRACE_PERIOD_DAYS * MS_PER_DAY;
|
|
2265
|
+
return graceEndMs > nowMs ? "past_due_grace" : "blocked";
|
|
2266
|
+
}
|
|
2267
|
+
case "pending_invoice":
|
|
2268
|
+
return "active";
|
|
2269
|
+
case "paused":
|
|
2270
|
+
case "incomplete":
|
|
2271
|
+
return "active";
|
|
2272
|
+
case "canceled":
|
|
2273
|
+
return "canceled";
|
|
2274
|
+
case "unpaid":
|
|
2275
|
+
case "incomplete_expired":
|
|
2276
|
+
return "blocked";
|
|
2277
|
+
default: {
|
|
2278
|
+
return "blocked";
|
|
2279
|
+
}
|
|
2280
|
+
}
|
|
2281
|
+
}
|
|
2282
|
+
|
|
2283
|
+
// src/subscriptions/userinfo-helpers.ts
|
|
2284
|
+
var ACCESS_GRANTING_EFFECTIVE_STATUSES = [
|
|
2285
|
+
"active",
|
|
2286
|
+
"past_due_grace"
|
|
2287
|
+
];
|
|
2288
|
+
var BLOCKING_EFFECTIVE_STATUSES = [
|
|
2289
|
+
"blocked",
|
|
2290
|
+
"canceled",
|
|
2291
|
+
"expired",
|
|
2292
|
+
"trial_expired"
|
|
2293
|
+
];
|
|
2294
|
+
function hasAccessFromUserinfo(sub) {
|
|
2295
|
+
const effectiveStatus = sub?.effectiveStatus;
|
|
2296
|
+
if (effectiveStatus === void 0 || effectiveStatus === null) {
|
|
2297
|
+
return false;
|
|
2298
|
+
}
|
|
2299
|
+
return ACCESS_GRANTING_EFFECTIVE_STATUSES.includes(effectiveStatus);
|
|
2300
|
+
}
|
|
2301
|
+
function isBlockedFromUserinfo(sub) {
|
|
2302
|
+
const effectiveStatus = sub?.effectiveStatus;
|
|
2303
|
+
if (effectiveStatus === void 0 || effectiveStatus === null) {
|
|
2304
|
+
return false;
|
|
2305
|
+
}
|
|
2306
|
+
return BLOCKING_EFFECTIVE_STATUSES.includes(effectiveStatus);
|
|
2307
|
+
}
|
|
2308
|
+
|
|
2232
2309
|
// src/client/ffid-client.ts
|
|
2233
2310
|
var UNAUTHORIZED_STATUS2 = 401;
|
|
2234
2311
|
var SDK_LOG_PREFIX = "[FFID SDK]";
|
|
@@ -2259,13 +2336,6 @@ var EXT_CHECK_ENDPOINT = "/api/v1/subscriptions/ext/check";
|
|
|
2259
2336
|
var DEFAULT_ALLOW_GRACE = true;
|
|
2260
2337
|
var DEFAULT_SERVICE_ACCESS_FAIL_POLICY = "failClosed";
|
|
2261
2338
|
var SERVICE_ACCESS_DENIED_CODE = "SERVICE_ACCESS_DENIED";
|
|
2262
|
-
var ACCESS_GRANTING_EFFECTIVE_STATUSES = ["active", "past_due_grace"];
|
|
2263
|
-
var BLOCKING_EFFECTIVE_STATUSES = [
|
|
2264
|
-
"blocked",
|
|
2265
|
-
"canceled",
|
|
2266
|
-
"expired",
|
|
2267
|
-
"trial_expired"
|
|
2268
|
-
];
|
|
2269
2339
|
function resolveServiceAccessDenialReason(params, allowGrace) {
|
|
2270
2340
|
if (params.hasAccess) {
|
|
2271
2341
|
return null;
|
|
@@ -3448,57 +3518,6 @@ function FFIDOrganizationSwitcher({
|
|
|
3448
3518
|
] })
|
|
3449
3519
|
] });
|
|
3450
3520
|
}
|
|
3451
|
-
|
|
3452
|
-
// src/subscriptions/types.ts
|
|
3453
|
-
var PAST_DUE_GRACE_PERIOD_DAYS = 7;
|
|
3454
|
-
var HOURS_PER_DAY = 24;
|
|
3455
|
-
var MINUTES_PER_HOUR = 60;
|
|
3456
|
-
var SECONDS_PER_MINUTE = 60;
|
|
3457
|
-
var MS_PER_SECOND2 = 1e3;
|
|
3458
|
-
var MS_PER_DAY = HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE * MS_PER_SECOND2;
|
|
3459
|
-
|
|
3460
|
-
// src/subscriptions/compute-effective-status.ts
|
|
3461
|
-
function isExpired(isoTimestamp, nowMs) {
|
|
3462
|
-
if (!isoTimestamp) return null;
|
|
3463
|
-
const parsed = Date.parse(isoTimestamp);
|
|
3464
|
-
if (Number.isNaN(parsed)) return null;
|
|
3465
|
-
return parsed < nowMs;
|
|
3466
|
-
}
|
|
3467
|
-
function computeEffectiveStatusFromSession(input, nowMs = Date.now()) {
|
|
3468
|
-
const { status, currentPeriodEnd, trialEnd, pastDueSince } = input;
|
|
3469
|
-
switch (status) {
|
|
3470
|
-
case "trialing": {
|
|
3471
|
-
const relevantEnd = trialEnd ?? currentPeriodEnd;
|
|
3472
|
-
const expired = isExpired(relevantEnd, nowMs);
|
|
3473
|
-
return expired === true ? "trial_expired" : "active";
|
|
3474
|
-
}
|
|
3475
|
-
case "active": {
|
|
3476
|
-
const expired = isExpired(currentPeriodEnd, nowMs);
|
|
3477
|
-
return expired === true ? "expired" : "active";
|
|
3478
|
-
}
|
|
3479
|
-
case "past_due": {
|
|
3480
|
-
const baselineIso = pastDueSince ?? currentPeriodEnd;
|
|
3481
|
-
if (!baselineIso) return "blocked";
|
|
3482
|
-
const baselineMs = Date.parse(baselineIso);
|
|
3483
|
-
if (Number.isNaN(baselineMs)) return "blocked";
|
|
3484
|
-
const graceEndMs = baselineMs + PAST_DUE_GRACE_PERIOD_DAYS * MS_PER_DAY;
|
|
3485
|
-
return graceEndMs > nowMs ? "past_due_grace" : "blocked";
|
|
3486
|
-
}
|
|
3487
|
-
case "pending_invoice":
|
|
3488
|
-
return "active";
|
|
3489
|
-
case "paused":
|
|
3490
|
-
case "incomplete":
|
|
3491
|
-
return "active";
|
|
3492
|
-
case "canceled":
|
|
3493
|
-
return "canceled";
|
|
3494
|
-
case "unpaid":
|
|
3495
|
-
case "incomplete_expired":
|
|
3496
|
-
return "blocked";
|
|
3497
|
-
default: {
|
|
3498
|
-
return "blocked";
|
|
3499
|
-
}
|
|
3500
|
-
}
|
|
3501
|
-
}
|
|
3502
3521
|
var ACCESS_GRANTING_STATUSES = [
|
|
3503
3522
|
"active",
|
|
3504
3523
|
"past_due_grace"
|
|
@@ -4798,4 +4817,4 @@ function FFIDInquiryForm({
|
|
|
4798
4817
|
);
|
|
4799
4818
|
}
|
|
4800
4819
|
|
|
4801
|
-
export { DEFAULT_API_BASE_URL, DEFAULT_OAUTH_SCOPES, FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDInquiryForm, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDProvider, FFIDSDKError, FFIDSubscriptionBadge, FFIDUserMenu, FFID_ANNOUNCEMENTS_ERROR_CODES, FFID_INQUIRY_CATEGORIES, FFID_INQUIRY_CATEGORIES_SITE_2026, computeEffectiveStatusFromSession, createFFIDAnnouncementsClient, createFFIDClient, createTokenStore, generateCodeChallenge, generateCodeVerifier, isFFIDInquiryCategorySite2026, normalizeRedirectUri, retrieveCodeVerifier, storeCodeVerifier, useFFID, useFFIDAnnouncements, useFFIDContext, useSubscription, withSubscription };
|
|
4820
|
+
export { ACCESS_GRANTING_EFFECTIVE_STATUSES, BLOCKING_EFFECTIVE_STATUSES, DEFAULT_API_BASE_URL, DEFAULT_OAUTH_SCOPES, FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDInquiryForm, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDProvider, FFIDSDKError, FFIDSubscriptionBadge, FFIDUserMenu, FFID_ANNOUNCEMENTS_ERROR_CODES, FFID_INQUIRY_CATEGORIES, FFID_INQUIRY_CATEGORIES_SITE_2026, computeEffectiveStatusFromSession, createFFIDAnnouncementsClient, createFFIDClient, createTokenStore, generateCodeChallenge, generateCodeVerifier, hasAccessFromUserinfo, isBlockedFromUserinfo, isFFIDInquiryCategorySite2026, normalizeRedirectUri, retrieveCodeVerifier, storeCodeVerifier, useFFID, useFFIDAnnouncements, useFFIDContext, useSubscription, withSubscription };
|
|
@@ -1,34 +1,34 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkJKP3MBTT_cjs = require('../chunk-JKP3MBTT.cjs');
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
Object.defineProperty(exports, "FFIDAnnouncementBadge", {
|
|
8
8
|
enumerable: true,
|
|
9
|
-
get: function () { return
|
|
9
|
+
get: function () { return chunkJKP3MBTT_cjs.FFIDAnnouncementBadge; }
|
|
10
10
|
});
|
|
11
11
|
Object.defineProperty(exports, "FFIDAnnouncementList", {
|
|
12
12
|
enumerable: true,
|
|
13
|
-
get: function () { return
|
|
13
|
+
get: function () { return chunkJKP3MBTT_cjs.FFIDAnnouncementList; }
|
|
14
14
|
});
|
|
15
15
|
Object.defineProperty(exports, "FFIDInquiryForm", {
|
|
16
16
|
enumerable: true,
|
|
17
|
-
get: function () { return
|
|
17
|
+
get: function () { return chunkJKP3MBTT_cjs.FFIDInquiryForm; }
|
|
18
18
|
});
|
|
19
19
|
Object.defineProperty(exports, "FFIDLoginButton", {
|
|
20
20
|
enumerable: true,
|
|
21
|
-
get: function () { return
|
|
21
|
+
get: function () { return chunkJKP3MBTT_cjs.FFIDLoginButton; }
|
|
22
22
|
});
|
|
23
23
|
Object.defineProperty(exports, "FFIDOrganizationSwitcher", {
|
|
24
24
|
enumerable: true,
|
|
25
|
-
get: function () { return
|
|
25
|
+
get: function () { return chunkJKP3MBTT_cjs.FFIDOrganizationSwitcher; }
|
|
26
26
|
});
|
|
27
27
|
Object.defineProperty(exports, "FFIDSubscriptionBadge", {
|
|
28
28
|
enumerable: true,
|
|
29
|
-
get: function () { return
|
|
29
|
+
get: function () { return chunkJKP3MBTT_cjs.FFIDSubscriptionBadge; }
|
|
30
30
|
});
|
|
31
31
|
Object.defineProperty(exports, "FFIDUserMenu", {
|
|
32
32
|
enumerable: true,
|
|
33
|
-
get: function () { return
|
|
33
|
+
get: function () { return chunkJKP3MBTT_cjs.FFIDUserMenu; }
|
|
34
34
|
});
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { T as FFIDAnnouncementBadge, av as FFIDAnnouncementBadgeClassNames, aw as FFIDAnnouncementBadgeProps, U as FFIDAnnouncementList, ax as FFIDAnnouncementListClassNames, ay as FFIDAnnouncementListProps, a1 as FFIDInquiryForm, a2 as FFIDInquiryFormCategoryItem, a3 as FFIDInquiryFormClassNames, a4 as FFIDInquiryFormLegalLayout, a5 as FFIDInquiryFormOrganization, a6 as FFIDInquiryFormPlaceholderContext, a7 as FFIDInquiryFormPrefill, a8 as FFIDInquiryFormProps, a9 as FFIDInquiryFormSubmitData, aa as FFIDInquiryFormSubmitResult, ac as FFIDLoginButton, az as FFIDLoginButtonProps, ah as FFIDOrganizationSwitcher, aA as FFIDOrganizationSwitcherClassNames, aB as FFIDOrganizationSwitcherProps, am as FFIDSubscriptionBadge, aC as FFIDSubscriptionBadgeClassNames, aD as FFIDSubscriptionBadgeProps, ao as FFIDUserMenu, aE as FFIDUserMenuClassNames, aF as FFIDUserMenuProps } from '../index-lAvUS_oz.cjs';
|
|
2
2
|
import 'react/jsx-runtime';
|
|
3
3
|
import 'react';
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { T as FFIDAnnouncementBadge, av as FFIDAnnouncementBadgeClassNames, aw as FFIDAnnouncementBadgeProps, U as FFIDAnnouncementList, ax as FFIDAnnouncementListClassNames, ay as FFIDAnnouncementListProps, a1 as FFIDInquiryForm, a2 as FFIDInquiryFormCategoryItem, a3 as FFIDInquiryFormClassNames, a4 as FFIDInquiryFormLegalLayout, a5 as FFIDInquiryFormOrganization, a6 as FFIDInquiryFormPlaceholderContext, a7 as FFIDInquiryFormPrefill, a8 as FFIDInquiryFormProps, a9 as FFIDInquiryFormSubmitData, aa as FFIDInquiryFormSubmitResult, ac as FFIDLoginButton, az as FFIDLoginButtonProps, ah as FFIDOrganizationSwitcher, aA as FFIDOrganizationSwitcherClassNames, aB as FFIDOrganizationSwitcherProps, am as FFIDSubscriptionBadge, aC as FFIDSubscriptionBadgeClassNames, aD as FFIDSubscriptionBadgeProps, ao as FFIDUserMenu, aE as FFIDUserMenuClassNames, aF as FFIDUserMenuProps } from '../index-lAvUS_oz.js';
|
|
2
2
|
import 'react/jsx-runtime';
|
|
3
3
|
import 'react';
|
package/dist/components/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDInquiryForm, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDSubscriptionBadge, FFIDUserMenu } from '../chunk-
|
|
1
|
+
export { FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDInquiryForm, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDSubscriptionBadge, FFIDUserMenu } from '../chunk-WNYMSUHN.js';
|