@feelflow/ffid-sdk 5.0.2 → 5.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{FFIDCookieLink-Buh84sTc.d.cts → FFIDCookieLink-D37pN-v_.d.cts} +101 -11
- package/dist/{FFIDCookieLink-Buh84sTc.d.ts → FFIDCookieLink-D37pN-v_.d.ts} +101 -11
- package/dist/{chunk-WZ6LF34H.cjs → chunk-4IIVIB2Q.cjs} +1 -1
- package/dist/{chunk-QYKYTZA6.js → chunk-FACWGQHL.js} +1 -1
- package/dist/{chunk-XPSWABVY.cjs → chunk-I3QZJXQQ.cjs} +69 -3
- package/dist/{chunk-ZM53LRXW.js → chunk-KSCAUQLP.js} +67 -5
- package/dist/components/index.cjs +8 -8
- package/dist/components/index.js +1 -1
- package/dist/consent/index.cjs +76 -60
- 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.cjs +65 -49
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3 -3
- package/dist/server/index.cjs +1 -1
- package/dist/server/index.js +1 -1
- package/package.json +1 -1
|
@@ -165,10 +165,9 @@ declare const FFIDConsentCategoryMetadataSchema: z.ZodObject<{
|
|
|
165
165
|
*
|
|
166
166
|
* `publishedAt` accepts both `Z` suffix and numeric offsets (`+00:00`, `+09:00`)
|
|
167
167
|
* so that consumers tolerate Postgres `timestamptz` raw passthrough as well as
|
|
168
|
-
* `Z`-normalized output. The FFID server normalizes to `Z` since Issue #3244
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
* layer specified in Issue #3245.
|
|
168
|
+
* `Z`-normalized output. The FFID server normalizes to `Z` since Issue #3244;
|
|
169
|
+
* third-party / future server paths may emit offsets — tolerant parsing is
|
|
170
|
+
* the defense-in-depth layer specified in Issue #3245.
|
|
172
171
|
*/
|
|
173
172
|
declare const FFIDCookiePolicySchema: z.ZodObject<{
|
|
174
173
|
id: z.ZodString;
|
|
@@ -478,6 +477,16 @@ declare const CONSENT_COOKIE_MAX_AGE_SEC: number;
|
|
|
478
477
|
* first, then fall back to cookie. See `FFIDAnalyticsProvider.bootstrap`.
|
|
479
478
|
*/
|
|
480
479
|
declare const CONSENT_SESSION_STORAGE_KEY = "ffid_consent_state";
|
|
480
|
+
/**
|
|
481
|
+
* sessionStorage key for the dismissal timestamp ("あとで" cooldown).
|
|
482
|
+
*
|
|
483
|
+
* EDPB Guidelines 05/2020 §86 forbids treating dismissal as a refusal, so we
|
|
484
|
+
* never write this to a cookie or DB. Holding the timestamp in sessionStorage
|
|
485
|
+
* keeps it scoped to the current tab session — once the tab closes, the user
|
|
486
|
+
* is reprompted on next visit. See `FFIDAnalyticsProvider`'s
|
|
487
|
+
* `dismissalCooldownMinutes` prop for the auto-show suppression policy.
|
|
488
|
+
*/
|
|
489
|
+
declare const CONSENT_DISMISSAL_TIMESTAMP_KEY = "ffid_consent_dismissed_at";
|
|
481
490
|
interface WriteConsentCookieOptions {
|
|
482
491
|
/** Override Secure flag for non-https custom domains. Defaults to production. */
|
|
483
492
|
secure?: boolean;
|
|
@@ -521,6 +530,34 @@ declare function deleteConsentCookie(opts?: WriteConsentCookieOptions): boolean;
|
|
|
521
530
|
declare function readConsentSessionMirror(): FFIDConsentCategories | null;
|
|
522
531
|
declare function writeConsentSessionMirror(cats: FFIDConsentCategories): boolean;
|
|
523
532
|
declare function clearConsentSessionMirror(): void;
|
|
533
|
+
/**
|
|
534
|
+
* Read the millisecond unix timestamp at which the banner was last dismissed,
|
|
535
|
+
* or `null` if absent / invalid / sessionStorage unavailable.
|
|
536
|
+
*
|
|
537
|
+
* Defensive parsing rejects non-finite values, negatives, and future-dated
|
|
538
|
+
* timestamps (clock-skew / tampering). When a corrupted value is detected,
|
|
539
|
+
* the helper **auto-heals** by clearing the slot — otherwise the same bad
|
|
540
|
+
* value would be re-parsed on every Provider mount and a buggy / extension-
|
|
541
|
+
* polluted consumer site could leave the cooldown effectively permanently
|
|
542
|
+
* disabled with no surface signal.
|
|
543
|
+
*/
|
|
544
|
+
declare function readConsentDismissalTimestamp(): number | null;
|
|
545
|
+
/**
|
|
546
|
+
* Write the dismissal timestamp. Returns `true` on success, `false` when
|
|
547
|
+
* sessionStorage is unavailable / throws (e.g., quota, old Safari Private
|
|
548
|
+
* Browsing) **or** when `nowMs` is non-finite / negative.
|
|
549
|
+
*
|
|
550
|
+
* Callers may safely ignore the return — failure degrades to the legacy
|
|
551
|
+
* "reprompt every Provider mount" behavior, which is the published v5.1.0
|
|
552
|
+
* baseline. Dismissal carries no GDPR weight (EDPB §86) so there is no
|
|
553
|
+
* obligation to surface storage failure via `onError`.
|
|
554
|
+
*/
|
|
555
|
+
declare function writeConsentDismissalTimestamp(nowMs?: number): boolean;
|
|
556
|
+
/**
|
|
557
|
+
* Idempotent clear of the dismissal timestamp. Safe to call when no value is
|
|
558
|
+
* present or sessionStorage is unavailable; never throws.
|
|
559
|
+
*/
|
|
560
|
+
declare function clearConsentDismissalTimestamp(): void;
|
|
524
561
|
|
|
525
562
|
/**
|
|
526
563
|
* Cookie Consent — Google Consent Mode v2 wiring.
|
|
@@ -653,11 +690,29 @@ interface FFIDAnalyticsProviderProps {
|
|
|
653
690
|
onError?: (error: FFIDConsentError) => void;
|
|
654
691
|
/** Whether to open banner automatically when no decision is on file. Default: true. */
|
|
655
692
|
autoShowBanner?: boolean;
|
|
693
|
+
/**
|
|
694
|
+
* Minutes to suppress auto-reopen after the user presses "あとで" (closeBanner).
|
|
695
|
+
*
|
|
696
|
+
* The dismissal timestamp is held in `sessionStorage` only — never written
|
|
697
|
+
* to cookie or DB — so it expires when the tab closes. This is EDPB
|
|
698
|
+
* Guidelines 05/2020 §86 compliant: dismissal is not a refusal, and the
|
|
699
|
+
* user is reprompted on the next fresh visit regardless of cooldown.
|
|
700
|
+
*
|
|
701
|
+
* Accepts any `number`; non-finite (`NaN` / `±Infinity`) and negative
|
|
702
|
+
* values are treated as `0` (suppression disabled). `0` reverts to the
|
|
703
|
+
* legacy v5.1.0 "reprompt on every Provider mount" behavior. Default: 30.
|
|
704
|
+
*
|
|
705
|
+
* Manual `openBanner()` calls ignore the cooldown so consumer "Cookie 設定"
|
|
706
|
+
* links keep working, and `needsRenewal=true` overrides the cooldown so the
|
|
707
|
+
* renewal reprompt (CNIL/ICO renewal guidance — see PR #3261) is never
|
|
708
|
+
* suppressed by a UX dismissal.
|
|
709
|
+
*/
|
|
710
|
+
dismissalCooldownMinutes?: number;
|
|
656
711
|
/** Optional fetch override (mainly for tests). */
|
|
657
712
|
fetchImpl?: typeof fetch;
|
|
658
713
|
children: ReactNode;
|
|
659
714
|
}
|
|
660
|
-
declare function FFIDAnalyticsProvider({ baseUrl, serviceApiKey, getAccessToken, gaMeasurementId, sentryReplay, sentry, onConsentChange, onError, autoShowBanner, fetchImpl, children, }: FFIDAnalyticsProviderProps): React.JSX.Element;
|
|
715
|
+
declare function FFIDAnalyticsProvider({ baseUrl, serviceApiKey, getAccessToken, gaMeasurementId, sentryReplay, sentry, onConsentChange, onError, autoShowBanner, dismissalCooldownMinutes, fetchImpl, children, }: FFIDAnalyticsProviderProps): React.JSX.Element;
|
|
661
716
|
|
|
662
717
|
interface FFIDConsentContextValue {
|
|
663
718
|
/** Current consent state. Always non-null; "not yet decided" is encoded by `hasDecided=false`. */
|
|
@@ -797,15 +852,23 @@ declare function useFFIDConsentPreferences(): UseFFIDConsentPreferencesReturn;
|
|
|
797
852
|
* `<FFIDCookieBanner>` — bottom-sheet consent banner.
|
|
798
853
|
*
|
|
799
854
|
* Renders only when `state.hasDecided === false` or `state.needsRenewal === true`.
|
|
800
|
-
*
|
|
855
|
+
* Four buttons (spec §6.2):
|
|
801
856
|
* 1. "すべて同意" (Accept all)
|
|
802
|
-
* 2. "
|
|
857
|
+
* 2. "同意しない" (Necessary only — `necessary=true` is enforced by ePrivacy
|
|
858
|
+
* 5(3) and the DB invariant; the 3 user-toggleable categories are
|
|
859
|
+
* recorded as false. The action itself is preserved in `source='banner'`)
|
|
803
860
|
* 3. "詳細設定" (Open preference center)
|
|
861
|
+
* 4. "あとで" (Dismiss without recording a decision — banner re-appears on
|
|
862
|
+
* the next visit because no row is written)
|
|
804
863
|
*
|
|
805
864
|
* GDPR Article 7(1)/(2) UX requirements:
|
|
806
865
|
* - "同意" and "拒否" share the same size + visual prominence.
|
|
807
866
|
* - Buttons share the same styles by default; consumers can override via
|
|
808
|
-
* `classNames` props (`primaryButton`, `secondaryButton`, `linkButton
|
|
867
|
+
* `classNames` props (`primaryButton`, `secondaryButton`, `linkButton`,
|
|
868
|
+
* `closeButton`).
|
|
869
|
+
* - "あとで" is intentionally not a refusal: EDPB Guidelines 05/2020 §86
|
|
870
|
+
* forbid treating dismissal as a refusal. We honor that by writing
|
|
871
|
+
* nothing on dismissal and re-prompting next visit.
|
|
809
872
|
*
|
|
810
873
|
* Refs: docs/02-design/COOKIE_CONSENT.md §6.2
|
|
811
874
|
*/
|
|
@@ -817,6 +880,7 @@ interface FFIDCookieBannerClassNames {
|
|
|
817
880
|
primaryButton?: string;
|
|
818
881
|
secondaryButton?: string;
|
|
819
882
|
linkButton?: string;
|
|
883
|
+
closeButton?: string;
|
|
820
884
|
}
|
|
821
885
|
interface FFIDCookieBannerProps {
|
|
822
886
|
/** Root container class. */
|
|
@@ -827,14 +891,40 @@ interface FFIDCookieBannerProps {
|
|
|
827
891
|
message?: ReactNode;
|
|
828
892
|
/** Accept-all button label. Default: "すべて同意". */
|
|
829
893
|
acceptAllLabel?: ReactNode;
|
|
830
|
-
/**
|
|
894
|
+
/**
|
|
895
|
+
* Necessary-only button label. Default: "同意しない".
|
|
896
|
+
*
|
|
897
|
+
* Selecting this button writes `necessary=true, functional=false,
|
|
898
|
+
* analytics=false, marketing=false` to the audit trail with
|
|
899
|
+
* `source='banner'`. The `necessary=true` value is enforced by the DB
|
|
900
|
+
* invariant; ePrivacy 5(3) exempts strictly necessary cookies from consent.
|
|
901
|
+
* Consumers that want to surface the legacy "必要なものだけ" wording can
|
|
902
|
+
* override via this prop.
|
|
903
|
+
*/
|
|
831
904
|
necessaryOnlyLabel?: ReactNode;
|
|
832
905
|
/** Preference center link label. Default: "詳細設定". */
|
|
833
906
|
preferencesLabel?: ReactNode;
|
|
907
|
+
/**
|
|
908
|
+
* Dismiss-without-recording button label. Default: "あとで".
|
|
909
|
+
*
|
|
910
|
+
* Selecting this button calls `closeBanner()` — session-only state, no row
|
|
911
|
+
* is written to the database. The banner re-appears on the next visit
|
|
912
|
+
* because bootstrap still sees `hasDecided=false`. Aligns with EDPB
|
|
913
|
+
* Guidelines 05/2020 §86 prohibiting treating dismissal as a refusal.
|
|
914
|
+
*
|
|
915
|
+
* NOTE for consumers: do NOT pass `closeLabel={null}` or `closeLabel=""`
|
|
916
|
+
* to visually hide the button while keeping the others. Doing so leaves
|
|
917
|
+
* a click-able empty area and breaks the GDPR Article 7(2) requirement
|
|
918
|
+
* that "consent" and "refusal" / "dismissal" controls share equal visual
|
|
919
|
+
* prominence. If you need to suppress dismissal entirely (e.g., for a
|
|
920
|
+
* strict-consent jurisdiction), build a custom banner using
|
|
921
|
+
* `useFFIDConsent()` instead of overriding the label.
|
|
922
|
+
*/
|
|
923
|
+
closeLabel?: ReactNode;
|
|
834
924
|
/** Custom root style. */
|
|
835
925
|
style?: CSSProperties;
|
|
836
926
|
}
|
|
837
|
-
declare function FFIDCookieBanner({ className, classNames, message, acceptAllLabel, necessaryOnlyLabel, preferencesLabel, style, }: FFIDCookieBannerProps): React.JSX.Element | null;
|
|
927
|
+
declare function FFIDCookieBanner({ className, classNames, message, acceptAllLabel, necessaryOnlyLabel, preferencesLabel, closeLabel, style, }: FFIDCookieBannerProps): React.JSX.Element | null;
|
|
838
928
|
|
|
839
929
|
/**
|
|
840
930
|
* `<FFIDCookieSettings>` — preference center modal.
|
|
@@ -882,4 +972,4 @@ interface FFIDCookieLinkProps {
|
|
|
882
972
|
}
|
|
883
973
|
declare function FFIDCookieLink({ children, className, style, id, }: FFIDCookieLinkProps): React.JSX.Element;
|
|
884
974
|
|
|
885
|
-
export {
|
|
975
|
+
export { type GetCategoriesWire as $, ALL_DENIED_EXCEPT_NECESSARY as A, type FFIDConsentMergeWarning as B, CONSENT_COOKIE_MAX_AGE_SEC as C, DEFAULT_CONSENT_ERROR_MESSAGES as D, FFIDConsentMergeWarningSchema as E, type FFIDConsentResult as F, type GetConsentMeWire as G, type FFIDConsentSource as H, FFIDConsentSourceSchema as I, FFIDConsentStateSchema as J, FFIDConsentUpdateSchema as K, FFIDCookieBanner as L, type FFIDCookieBannerClassNames as M, type FFIDCookieBannerProps as N, FFIDCookieLink as O, type PostConsentWire as P, type FFIDCookieLinkProps as Q, FFIDCookiePolicySchema as R, FFIDCookieSettings as S, type FFIDCookieSettingsClassNames as T, type FFIDCookieSettingsProps as U, type FFIDInternalConsentSource as V, FFIDInternalConsentSourceSchema as W, type FFIDPublicConsentSource as X, FFIDPublicConsentSourceSchema as Y, FFID_CONSENT_ERROR_CODES as Z, FFID_CONSENT_NOT_DECIDED_STATE as _, type FFIDConsentState as a, GetCategoriesWireSchema as a0, GetConsentMeWireSchema as a1, type GetDocumentWire as a2, GetDocumentWireSchema as a3, type GtagBridge as a4, type GtagBridgeOptions as a5, type PostConsentRequest as a6, PostConsentRequestSchema as a7, PostConsentWireSchema as a8, type PostSyncRequest as a9, PostSyncRequestSchema as aa, PostSyncWireSchema as ab, PostWithdrawWireSchema as ac, type UseFFIDConsentPreferencesReturn as ad, type UseFFIDConsentReturn as ae, clearConsentDismissalTimestamp as af, clearConsentSessionMirror as ag, createGtagBridge as ah, decodeConsentCookie as ai, deleteConsentCookie as aj, encodeConsentCookie as ak, mapCategoriesToGtagParams as al, readConsentCookie as am, readConsentDismissalTimestamp as an, readConsentSessionMirror as ao, useFFIDConsent as ap, useFFIDConsentPreferences as aq, writeConsentCookie as ar, writeConsentDismissalTimestamp as as, writeConsentSessionMirror as at, type FFIDConsentUpdate as b, type FFIDConsentCategories as c, type FFIDConsentSyncResult as d, type FFIDConsentCategoryMetadata as e, type FFIDCookiePolicy as f, type PostSyncWire as g, type PostWithdrawWire as h, CONSENT_COOKIE_NAME as i, CONSENT_DISMISSAL_TIMESTAMP_KEY as j, CONSENT_SESSION_STORAGE_KEY as k, COOKIE_VERSION as l, DEFAULT_MERGE_WARNING_MESSAGES as m, DeviceIdSchema as n, FFIDAnalyticsProvider as o, type FFIDAnalyticsProviderProps as p, FFIDConsentCategoriesSchema as q, type FFIDConsentCategoryCode as r, FFIDConsentCategoryCodeSchema as s, FFIDConsentCategoryMetadataSchema as t, FFIDConsentContext as u, type FFIDConsentContextValue as v, FFIDConsentError as w, type FFIDConsentErrorCode as x, type FFIDConsentMergeStrategy as y, FFIDConsentMergeStrategySchema as z };
|
|
@@ -165,10 +165,9 @@ declare const FFIDConsentCategoryMetadataSchema: z.ZodObject<{
|
|
|
165
165
|
*
|
|
166
166
|
* `publishedAt` accepts both `Z` suffix and numeric offsets (`+00:00`, `+09:00`)
|
|
167
167
|
* so that consumers tolerate Postgres `timestamptz` raw passthrough as well as
|
|
168
|
-
* `Z`-normalized output. The FFID server normalizes to `Z` since Issue #3244
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
* layer specified in Issue #3245.
|
|
168
|
+
* `Z`-normalized output. The FFID server normalizes to `Z` since Issue #3244;
|
|
169
|
+
* third-party / future server paths may emit offsets — tolerant parsing is
|
|
170
|
+
* the defense-in-depth layer specified in Issue #3245.
|
|
172
171
|
*/
|
|
173
172
|
declare const FFIDCookiePolicySchema: z.ZodObject<{
|
|
174
173
|
id: z.ZodString;
|
|
@@ -478,6 +477,16 @@ declare const CONSENT_COOKIE_MAX_AGE_SEC: number;
|
|
|
478
477
|
* first, then fall back to cookie. See `FFIDAnalyticsProvider.bootstrap`.
|
|
479
478
|
*/
|
|
480
479
|
declare const CONSENT_SESSION_STORAGE_KEY = "ffid_consent_state";
|
|
480
|
+
/**
|
|
481
|
+
* sessionStorage key for the dismissal timestamp ("あとで" cooldown).
|
|
482
|
+
*
|
|
483
|
+
* EDPB Guidelines 05/2020 §86 forbids treating dismissal as a refusal, so we
|
|
484
|
+
* never write this to a cookie or DB. Holding the timestamp in sessionStorage
|
|
485
|
+
* keeps it scoped to the current tab session — once the tab closes, the user
|
|
486
|
+
* is reprompted on next visit. See `FFIDAnalyticsProvider`'s
|
|
487
|
+
* `dismissalCooldownMinutes` prop for the auto-show suppression policy.
|
|
488
|
+
*/
|
|
489
|
+
declare const CONSENT_DISMISSAL_TIMESTAMP_KEY = "ffid_consent_dismissed_at";
|
|
481
490
|
interface WriteConsentCookieOptions {
|
|
482
491
|
/** Override Secure flag for non-https custom domains. Defaults to production. */
|
|
483
492
|
secure?: boolean;
|
|
@@ -521,6 +530,34 @@ declare function deleteConsentCookie(opts?: WriteConsentCookieOptions): boolean;
|
|
|
521
530
|
declare function readConsentSessionMirror(): FFIDConsentCategories | null;
|
|
522
531
|
declare function writeConsentSessionMirror(cats: FFIDConsentCategories): boolean;
|
|
523
532
|
declare function clearConsentSessionMirror(): void;
|
|
533
|
+
/**
|
|
534
|
+
* Read the millisecond unix timestamp at which the banner was last dismissed,
|
|
535
|
+
* or `null` if absent / invalid / sessionStorage unavailable.
|
|
536
|
+
*
|
|
537
|
+
* Defensive parsing rejects non-finite values, negatives, and future-dated
|
|
538
|
+
* timestamps (clock-skew / tampering). When a corrupted value is detected,
|
|
539
|
+
* the helper **auto-heals** by clearing the slot — otherwise the same bad
|
|
540
|
+
* value would be re-parsed on every Provider mount and a buggy / extension-
|
|
541
|
+
* polluted consumer site could leave the cooldown effectively permanently
|
|
542
|
+
* disabled with no surface signal.
|
|
543
|
+
*/
|
|
544
|
+
declare function readConsentDismissalTimestamp(): number | null;
|
|
545
|
+
/**
|
|
546
|
+
* Write the dismissal timestamp. Returns `true` on success, `false` when
|
|
547
|
+
* sessionStorage is unavailable / throws (e.g., quota, old Safari Private
|
|
548
|
+
* Browsing) **or** when `nowMs` is non-finite / negative.
|
|
549
|
+
*
|
|
550
|
+
* Callers may safely ignore the return — failure degrades to the legacy
|
|
551
|
+
* "reprompt every Provider mount" behavior, which is the published v5.1.0
|
|
552
|
+
* baseline. Dismissal carries no GDPR weight (EDPB §86) so there is no
|
|
553
|
+
* obligation to surface storage failure via `onError`.
|
|
554
|
+
*/
|
|
555
|
+
declare function writeConsentDismissalTimestamp(nowMs?: number): boolean;
|
|
556
|
+
/**
|
|
557
|
+
* Idempotent clear of the dismissal timestamp. Safe to call when no value is
|
|
558
|
+
* present or sessionStorage is unavailable; never throws.
|
|
559
|
+
*/
|
|
560
|
+
declare function clearConsentDismissalTimestamp(): void;
|
|
524
561
|
|
|
525
562
|
/**
|
|
526
563
|
* Cookie Consent — Google Consent Mode v2 wiring.
|
|
@@ -653,11 +690,29 @@ interface FFIDAnalyticsProviderProps {
|
|
|
653
690
|
onError?: (error: FFIDConsentError) => void;
|
|
654
691
|
/** Whether to open banner automatically when no decision is on file. Default: true. */
|
|
655
692
|
autoShowBanner?: boolean;
|
|
693
|
+
/**
|
|
694
|
+
* Minutes to suppress auto-reopen after the user presses "あとで" (closeBanner).
|
|
695
|
+
*
|
|
696
|
+
* The dismissal timestamp is held in `sessionStorage` only — never written
|
|
697
|
+
* to cookie or DB — so it expires when the tab closes. This is EDPB
|
|
698
|
+
* Guidelines 05/2020 §86 compliant: dismissal is not a refusal, and the
|
|
699
|
+
* user is reprompted on the next fresh visit regardless of cooldown.
|
|
700
|
+
*
|
|
701
|
+
* Accepts any `number`; non-finite (`NaN` / `±Infinity`) and negative
|
|
702
|
+
* values are treated as `0` (suppression disabled). `0` reverts to the
|
|
703
|
+
* legacy v5.1.0 "reprompt on every Provider mount" behavior. Default: 30.
|
|
704
|
+
*
|
|
705
|
+
* Manual `openBanner()` calls ignore the cooldown so consumer "Cookie 設定"
|
|
706
|
+
* links keep working, and `needsRenewal=true` overrides the cooldown so the
|
|
707
|
+
* renewal reprompt (CNIL/ICO renewal guidance — see PR #3261) is never
|
|
708
|
+
* suppressed by a UX dismissal.
|
|
709
|
+
*/
|
|
710
|
+
dismissalCooldownMinutes?: number;
|
|
656
711
|
/** Optional fetch override (mainly for tests). */
|
|
657
712
|
fetchImpl?: typeof fetch;
|
|
658
713
|
children: ReactNode;
|
|
659
714
|
}
|
|
660
|
-
declare function FFIDAnalyticsProvider({ baseUrl, serviceApiKey, getAccessToken, gaMeasurementId, sentryReplay, sentry, onConsentChange, onError, autoShowBanner, fetchImpl, children, }: FFIDAnalyticsProviderProps): React.JSX.Element;
|
|
715
|
+
declare function FFIDAnalyticsProvider({ baseUrl, serviceApiKey, getAccessToken, gaMeasurementId, sentryReplay, sentry, onConsentChange, onError, autoShowBanner, dismissalCooldownMinutes, fetchImpl, children, }: FFIDAnalyticsProviderProps): React.JSX.Element;
|
|
661
716
|
|
|
662
717
|
interface FFIDConsentContextValue {
|
|
663
718
|
/** Current consent state. Always non-null; "not yet decided" is encoded by `hasDecided=false`. */
|
|
@@ -797,15 +852,23 @@ declare function useFFIDConsentPreferences(): UseFFIDConsentPreferencesReturn;
|
|
|
797
852
|
* `<FFIDCookieBanner>` — bottom-sheet consent banner.
|
|
798
853
|
*
|
|
799
854
|
* Renders only when `state.hasDecided === false` or `state.needsRenewal === true`.
|
|
800
|
-
*
|
|
855
|
+
* Four buttons (spec §6.2):
|
|
801
856
|
* 1. "すべて同意" (Accept all)
|
|
802
|
-
* 2. "
|
|
857
|
+
* 2. "同意しない" (Necessary only — `necessary=true` is enforced by ePrivacy
|
|
858
|
+
* 5(3) and the DB invariant; the 3 user-toggleable categories are
|
|
859
|
+
* recorded as false. The action itself is preserved in `source='banner'`)
|
|
803
860
|
* 3. "詳細設定" (Open preference center)
|
|
861
|
+
* 4. "あとで" (Dismiss without recording a decision — banner re-appears on
|
|
862
|
+
* the next visit because no row is written)
|
|
804
863
|
*
|
|
805
864
|
* GDPR Article 7(1)/(2) UX requirements:
|
|
806
865
|
* - "同意" and "拒否" share the same size + visual prominence.
|
|
807
866
|
* - Buttons share the same styles by default; consumers can override via
|
|
808
|
-
* `classNames` props (`primaryButton`, `secondaryButton`, `linkButton
|
|
867
|
+
* `classNames` props (`primaryButton`, `secondaryButton`, `linkButton`,
|
|
868
|
+
* `closeButton`).
|
|
869
|
+
* - "あとで" is intentionally not a refusal: EDPB Guidelines 05/2020 §86
|
|
870
|
+
* forbid treating dismissal as a refusal. We honor that by writing
|
|
871
|
+
* nothing on dismissal and re-prompting next visit.
|
|
809
872
|
*
|
|
810
873
|
* Refs: docs/02-design/COOKIE_CONSENT.md §6.2
|
|
811
874
|
*/
|
|
@@ -817,6 +880,7 @@ interface FFIDCookieBannerClassNames {
|
|
|
817
880
|
primaryButton?: string;
|
|
818
881
|
secondaryButton?: string;
|
|
819
882
|
linkButton?: string;
|
|
883
|
+
closeButton?: string;
|
|
820
884
|
}
|
|
821
885
|
interface FFIDCookieBannerProps {
|
|
822
886
|
/** Root container class. */
|
|
@@ -827,14 +891,40 @@ interface FFIDCookieBannerProps {
|
|
|
827
891
|
message?: ReactNode;
|
|
828
892
|
/** Accept-all button label. Default: "すべて同意". */
|
|
829
893
|
acceptAllLabel?: ReactNode;
|
|
830
|
-
/**
|
|
894
|
+
/**
|
|
895
|
+
* Necessary-only button label. Default: "同意しない".
|
|
896
|
+
*
|
|
897
|
+
* Selecting this button writes `necessary=true, functional=false,
|
|
898
|
+
* analytics=false, marketing=false` to the audit trail with
|
|
899
|
+
* `source='banner'`. The `necessary=true` value is enforced by the DB
|
|
900
|
+
* invariant; ePrivacy 5(3) exempts strictly necessary cookies from consent.
|
|
901
|
+
* Consumers that want to surface the legacy "必要なものだけ" wording can
|
|
902
|
+
* override via this prop.
|
|
903
|
+
*/
|
|
831
904
|
necessaryOnlyLabel?: ReactNode;
|
|
832
905
|
/** Preference center link label. Default: "詳細設定". */
|
|
833
906
|
preferencesLabel?: ReactNode;
|
|
907
|
+
/**
|
|
908
|
+
* Dismiss-without-recording button label. Default: "あとで".
|
|
909
|
+
*
|
|
910
|
+
* Selecting this button calls `closeBanner()` — session-only state, no row
|
|
911
|
+
* is written to the database. The banner re-appears on the next visit
|
|
912
|
+
* because bootstrap still sees `hasDecided=false`. Aligns with EDPB
|
|
913
|
+
* Guidelines 05/2020 §86 prohibiting treating dismissal as a refusal.
|
|
914
|
+
*
|
|
915
|
+
* NOTE for consumers: do NOT pass `closeLabel={null}` or `closeLabel=""`
|
|
916
|
+
* to visually hide the button while keeping the others. Doing so leaves
|
|
917
|
+
* a click-able empty area and breaks the GDPR Article 7(2) requirement
|
|
918
|
+
* that "consent" and "refusal" / "dismissal" controls share equal visual
|
|
919
|
+
* prominence. If you need to suppress dismissal entirely (e.g., for a
|
|
920
|
+
* strict-consent jurisdiction), build a custom banner using
|
|
921
|
+
* `useFFIDConsent()` instead of overriding the label.
|
|
922
|
+
*/
|
|
923
|
+
closeLabel?: ReactNode;
|
|
834
924
|
/** Custom root style. */
|
|
835
925
|
style?: CSSProperties;
|
|
836
926
|
}
|
|
837
|
-
declare function FFIDCookieBanner({ className, classNames, message, acceptAllLabel, necessaryOnlyLabel, preferencesLabel, style, }: FFIDCookieBannerProps): React.JSX.Element | null;
|
|
927
|
+
declare function FFIDCookieBanner({ className, classNames, message, acceptAllLabel, necessaryOnlyLabel, preferencesLabel, closeLabel, style, }: FFIDCookieBannerProps): React.JSX.Element | null;
|
|
838
928
|
|
|
839
929
|
/**
|
|
840
930
|
* `<FFIDCookieSettings>` — preference center modal.
|
|
@@ -882,4 +972,4 @@ interface FFIDCookieLinkProps {
|
|
|
882
972
|
}
|
|
883
973
|
declare function FFIDCookieLink({ children, className, style, id, }: FFIDCookieLinkProps): React.JSX.Element;
|
|
884
974
|
|
|
885
|
-
export {
|
|
975
|
+
export { type GetCategoriesWire as $, ALL_DENIED_EXCEPT_NECESSARY as A, type FFIDConsentMergeWarning as B, CONSENT_COOKIE_MAX_AGE_SEC as C, DEFAULT_CONSENT_ERROR_MESSAGES as D, FFIDConsentMergeWarningSchema as E, type FFIDConsentResult as F, type GetConsentMeWire as G, type FFIDConsentSource as H, FFIDConsentSourceSchema as I, FFIDConsentStateSchema as J, FFIDConsentUpdateSchema as K, FFIDCookieBanner as L, type FFIDCookieBannerClassNames as M, type FFIDCookieBannerProps as N, FFIDCookieLink as O, type PostConsentWire as P, type FFIDCookieLinkProps as Q, FFIDCookiePolicySchema as R, FFIDCookieSettings as S, type FFIDCookieSettingsClassNames as T, type FFIDCookieSettingsProps as U, type FFIDInternalConsentSource as V, FFIDInternalConsentSourceSchema as W, type FFIDPublicConsentSource as X, FFIDPublicConsentSourceSchema as Y, FFID_CONSENT_ERROR_CODES as Z, FFID_CONSENT_NOT_DECIDED_STATE as _, type FFIDConsentState as a, GetCategoriesWireSchema as a0, GetConsentMeWireSchema as a1, type GetDocumentWire as a2, GetDocumentWireSchema as a3, type GtagBridge as a4, type GtagBridgeOptions as a5, type PostConsentRequest as a6, PostConsentRequestSchema as a7, PostConsentWireSchema as a8, type PostSyncRequest as a9, PostSyncRequestSchema as aa, PostSyncWireSchema as ab, PostWithdrawWireSchema as ac, type UseFFIDConsentPreferencesReturn as ad, type UseFFIDConsentReturn as ae, clearConsentDismissalTimestamp as af, clearConsentSessionMirror as ag, createGtagBridge as ah, decodeConsentCookie as ai, deleteConsentCookie as aj, encodeConsentCookie as ak, mapCategoriesToGtagParams as al, readConsentCookie as am, readConsentDismissalTimestamp as an, readConsentSessionMirror as ao, useFFIDConsent as ap, useFFIDConsentPreferences as aq, writeConsentCookie as ar, writeConsentDismissalTimestamp as as, writeConsentSessionMirror as at, type FFIDConsentUpdate as b, type FFIDConsentCategories as c, type FFIDConsentSyncResult as d, type FFIDConsentCategoryMetadata as e, type FFIDCookiePolicy as f, type PostSyncWire as g, type PostWithdrawWire as h, CONSENT_COOKIE_NAME as i, CONSENT_DISMISSAL_TIMESTAMP_KEY as j, CONSENT_SESSION_STORAGE_KEY as k, COOKIE_VERSION as l, DEFAULT_MERGE_WARNING_MESSAGES as m, DeviceIdSchema as n, FFIDAnalyticsProvider as o, type FFIDAnalyticsProviderProps as p, FFIDConsentCategoriesSchema as q, type FFIDConsentCategoryCode as r, FFIDConsentCategoryCodeSchema as s, FFIDConsentCategoryMetadataSchema as t, FFIDConsentContext as u, type FFIDConsentContextValue as v, FFIDConsentError as w, type FFIDConsentErrorCode as x, type FFIDConsentMergeStrategy as y, FFIDConsentMergeStrategySchema as z };
|
|
@@ -840,7 +840,7 @@ function createProfileMethods(deps) {
|
|
|
840
840
|
}
|
|
841
841
|
|
|
842
842
|
// src/client/version-check.ts
|
|
843
|
-
var SDK_VERSION = "5.0
|
|
843
|
+
var SDK_VERSION = "5.2.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() {
|
|
@@ -838,7 +838,7 @@ function createProfileMethods(deps) {
|
|
|
838
838
|
}
|
|
839
839
|
|
|
840
840
|
// src/client/version-check.ts
|
|
841
|
-
var SDK_VERSION = "5.0
|
|
841
|
+
var SDK_VERSION = "5.2.0";
|
|
842
842
|
var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
|
|
843
843
|
var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
|
|
844
844
|
function sdkHeaders() {
|
|
@@ -284,6 +284,7 @@ var HOURS_PER_DAY = 24;
|
|
|
284
284
|
var DAYS_PER_YEAR = 365;
|
|
285
285
|
var CONSENT_COOKIE_MAX_AGE_SEC = SECONDS_PER_MINUTE * MINUTES_PER_HOUR * HOURS_PER_DAY * DAYS_PER_YEAR;
|
|
286
286
|
var CONSENT_SESSION_STORAGE_KEY = "ffid_consent_state";
|
|
287
|
+
var CONSENT_DISMISSAL_TIMESTAMP_KEY = "ffid_consent_dismissed_at";
|
|
287
288
|
function hasDocument() {
|
|
288
289
|
return typeof document !== "undefined";
|
|
289
290
|
}
|
|
@@ -374,6 +375,39 @@ function clearConsentSessionMirror() {
|
|
|
374
375
|
} catch {
|
|
375
376
|
}
|
|
376
377
|
}
|
|
378
|
+
function readConsentDismissalTimestamp() {
|
|
379
|
+
if (!hasSessionStorage()) return null;
|
|
380
|
+
try {
|
|
381
|
+
const raw = sessionStorage.getItem(CONSENT_DISMISSAL_TIMESTAMP_KEY);
|
|
382
|
+
if (!raw) return null;
|
|
383
|
+
const parsed = Number(raw);
|
|
384
|
+
const CLOCK_SKEW_GRACE_MS = 6e4;
|
|
385
|
+
if (!Number.isFinite(parsed) || parsed < 0 || parsed > Date.now() + CLOCK_SKEW_GRACE_MS) {
|
|
386
|
+
clearConsentDismissalTimestamp();
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
389
|
+
return parsed;
|
|
390
|
+
} catch {
|
|
391
|
+
return null;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
function writeConsentDismissalTimestamp(nowMs = Date.now()) {
|
|
395
|
+
if (!Number.isFinite(nowMs) || nowMs < 0) return false;
|
|
396
|
+
if (!hasSessionStorage()) return false;
|
|
397
|
+
try {
|
|
398
|
+
sessionStorage.setItem(CONSENT_DISMISSAL_TIMESTAMP_KEY, String(nowMs));
|
|
399
|
+
return true;
|
|
400
|
+
} catch {
|
|
401
|
+
return false;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
function clearConsentDismissalTimestamp() {
|
|
405
|
+
if (!hasSessionStorage()) return;
|
|
406
|
+
try {
|
|
407
|
+
sessionStorage.removeItem(CONSENT_DISMISSAL_TIMESTAMP_KEY);
|
|
408
|
+
} catch {
|
|
409
|
+
}
|
|
410
|
+
}
|
|
377
411
|
|
|
378
412
|
// src/consent/storage/device-id.ts
|
|
379
413
|
var DEVICE_ID_LOCAL_STORAGE_KEY = "ffid_device_id";
|
|
@@ -1014,6 +1048,8 @@ var DEFAULT_MERGE_WARNING_MESSAGES = {
|
|
|
1014
1048
|
shared_device_detected: "\u3053\u306E\u7AEF\u672B\u306F\u5225\u306E\u30A2\u30AB\u30A6\u30F3\u30C8\u3067\u3082\u4F7F\u7528\u3055\u308C\u3066\u3044\u307E\u3059\u3002Cookie \u8A2D\u5B9A\u306F\u30A2\u30AB\u30A6\u30F3\u30C8\u3054\u3068\u306B\u4FDD\u5B58\u3055\u308C\u3066\u3044\u307E\u3059",
|
|
1015
1049
|
drift_detected: "\u4FDD\u5B58\u3055\u308C\u3066\u3044\u305F\u8A2D\u5B9A\u3068\u7570\u306A\u308B\u9805\u76EE\u304C\u3042\u3063\u305F\u305F\u3081\u3001\u65B0\u3057\u304F\u8A18\u9332\u3055\u308C\u307E\u3057\u305F"
|
|
1016
1050
|
};
|
|
1051
|
+
var DEFAULT_DISMISSAL_COOLDOWN_MINUTES = 30;
|
|
1052
|
+
var MS_PER_MINUTE = 6e4;
|
|
1017
1053
|
function applyChanges(base, changes) {
|
|
1018
1054
|
return {
|
|
1019
1055
|
necessary: true,
|
|
@@ -1032,9 +1068,17 @@ function FFIDAnalyticsProvider({
|
|
|
1032
1068
|
onConsentChange,
|
|
1033
1069
|
onError,
|
|
1034
1070
|
autoShowBanner = true,
|
|
1071
|
+
dismissalCooldownMinutes = DEFAULT_DISMISSAL_COOLDOWN_MINUTES,
|
|
1035
1072
|
fetchImpl,
|
|
1036
1073
|
children
|
|
1037
1074
|
}) {
|
|
1075
|
+
const cooldownMs = Number.isFinite(dismissalCooldownMinutes) && dismissalCooldownMinutes > 0 ? dismissalCooldownMinutes * MS_PER_MINUTE : 0;
|
|
1076
|
+
const isWithinDismissalCooldown = react.useCallback(() => {
|
|
1077
|
+
if (cooldownMs <= 0) return false;
|
|
1078
|
+
const dismissedAt = readConsentDismissalTimestamp();
|
|
1079
|
+
if (dismissedAt === null) return false;
|
|
1080
|
+
return Date.now() - dismissedAt < cooldownMs;
|
|
1081
|
+
}, [cooldownMs]);
|
|
1038
1082
|
const [state, setState] = react.useState(
|
|
1039
1083
|
FFID_CONSENT_NOT_DECIDED_STATE
|
|
1040
1084
|
);
|
|
@@ -1145,7 +1189,7 @@ function FFIDAnalyticsProvider({
|
|
|
1145
1189
|
applyCategoriesSideEffects(result.value.categories);
|
|
1146
1190
|
writeConsentSessionMirror(result.value.categories);
|
|
1147
1191
|
setIsLoading(false);
|
|
1148
|
-
if (!result.value.hasDecided && autoShowBanner) {
|
|
1192
|
+
if (!result.value.hasDecided && autoShowBanner && !isWithinDismissalCooldown()) {
|
|
1149
1193
|
setIsBannerOpen(true);
|
|
1150
1194
|
}
|
|
1151
1195
|
if (result.value.needsRenewal && autoShowBanner) {
|
|
@@ -1273,7 +1317,10 @@ function FFIDAnalyticsProvider({
|
|
|
1273
1317
|
persistCategories
|
|
1274
1318
|
]);
|
|
1275
1319
|
const openBanner = react.useCallback(() => setIsBannerOpen(true), []);
|
|
1276
|
-
const closeBanner = react.useCallback(() =>
|
|
1320
|
+
const closeBanner = react.useCallback(() => {
|
|
1321
|
+
if (cooldownMs > 0) writeConsentDismissalTimestamp();
|
|
1322
|
+
setIsBannerOpen(false);
|
|
1323
|
+
}, [cooldownMs]);
|
|
1277
1324
|
const openPreferences = react.useCallback(async () => {
|
|
1278
1325
|
if (!categoryMetadata) {
|
|
1279
1326
|
const client = clientRef.current;
|
|
@@ -1403,6 +1450,7 @@ function FFIDCookieBanner({
|
|
|
1403
1450
|
acceptAllLabel,
|
|
1404
1451
|
necessaryOnlyLabel,
|
|
1405
1452
|
preferencesLabel,
|
|
1453
|
+
closeLabel,
|
|
1406
1454
|
style
|
|
1407
1455
|
}) {
|
|
1408
1456
|
const {
|
|
@@ -1411,6 +1459,7 @@ function FFIDCookieBanner({
|
|
|
1411
1459
|
acceptAll,
|
|
1412
1460
|
acceptNecessaryOnly,
|
|
1413
1461
|
openPreferences,
|
|
1462
|
+
closeBanner,
|
|
1414
1463
|
error
|
|
1415
1464
|
} = useFFIDConsent();
|
|
1416
1465
|
if (!isBannerOpen && state.hasDecided && !state.needsRenewal) return null;
|
|
@@ -1462,7 +1511,7 @@ function FFIDCookieBanner({
|
|
|
1462
1511
|
void acceptNecessaryOnly();
|
|
1463
1512
|
},
|
|
1464
1513
|
"data-testid": "ffid-cookie-banner-necessary-only",
|
|
1465
|
-
children: necessaryOnlyLabel ?? "\
|
|
1514
|
+
children: necessaryOnlyLabel ?? "\u540C\u610F\u3057\u306A\u3044"
|
|
1466
1515
|
}
|
|
1467
1516
|
),
|
|
1468
1517
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -1477,6 +1526,19 @@ function FFIDCookieBanner({
|
|
|
1477
1526
|
"data-testid": "ffid-cookie-banner-preferences",
|
|
1478
1527
|
children: preferencesLabel ?? "\u8A73\u7D30\u8A2D\u5B9A"
|
|
1479
1528
|
}
|
|
1529
|
+
),
|
|
1530
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1531
|
+
"button",
|
|
1532
|
+
{
|
|
1533
|
+
type: "button",
|
|
1534
|
+
className: classNames?.closeButton,
|
|
1535
|
+
style: DEFAULT_BUTTON_STYLE,
|
|
1536
|
+
onClick: () => {
|
|
1537
|
+
closeBanner();
|
|
1538
|
+
},
|
|
1539
|
+
"data-testid": "ffid-cookie-banner-close",
|
|
1540
|
+
children: closeLabel ?? "\u3042\u3068\u3067"
|
|
1541
|
+
}
|
|
1480
1542
|
)
|
|
1481
1543
|
]
|
|
1482
1544
|
}
|
|
@@ -1780,6 +1842,7 @@ function FFIDCookieLink({
|
|
|
1780
1842
|
exports.ALL_DENIED_EXCEPT_NECESSARY = ALL_DENIED_EXCEPT_NECESSARY;
|
|
1781
1843
|
exports.CONSENT_COOKIE_MAX_AGE_SEC = CONSENT_COOKIE_MAX_AGE_SEC;
|
|
1782
1844
|
exports.CONSENT_COOKIE_NAME = CONSENT_COOKIE_NAME;
|
|
1845
|
+
exports.CONSENT_DISMISSAL_TIMESTAMP_KEY = CONSENT_DISMISSAL_TIMESTAMP_KEY;
|
|
1783
1846
|
exports.CONSENT_SESSION_STORAGE_KEY = CONSENT_SESSION_STORAGE_KEY;
|
|
1784
1847
|
exports.COOKIE_VERSION = COOKIE_VERSION;
|
|
1785
1848
|
exports.DEFAULT_CONSENT_ERROR_MESSAGES = DEFAULT_CONSENT_ERROR_MESSAGES;
|
|
@@ -1814,6 +1877,7 @@ exports.PostConsentWireSchema = PostConsentWireSchema;
|
|
|
1814
1877
|
exports.PostSyncRequestSchema = PostSyncRequestSchema;
|
|
1815
1878
|
exports.PostSyncWireSchema = PostSyncWireSchema;
|
|
1816
1879
|
exports.PostWithdrawWireSchema = PostWithdrawWireSchema;
|
|
1880
|
+
exports.clearConsentDismissalTimestamp = clearConsentDismissalTimestamp;
|
|
1817
1881
|
exports.clearConsentSessionMirror = clearConsentSessionMirror;
|
|
1818
1882
|
exports.clearDeviceId = clearDeviceId;
|
|
1819
1883
|
exports.createConsentClient = createConsentClient;
|
|
@@ -1831,8 +1895,10 @@ exports.mapMeWireToState = mapMeWireToState;
|
|
|
1831
1895
|
exports.mapSyncWireToResult = mapSyncWireToResult;
|
|
1832
1896
|
exports.mapWithdrawWireToState = mapWithdrawWireToState;
|
|
1833
1897
|
exports.readConsentCookie = readConsentCookie;
|
|
1898
|
+
exports.readConsentDismissalTimestamp = readConsentDismissalTimestamp;
|
|
1834
1899
|
exports.readConsentSessionMirror = readConsentSessionMirror;
|
|
1835
1900
|
exports.useFFIDConsent = useFFIDConsent;
|
|
1836
1901
|
exports.useFFIDConsentPreferences = useFFIDConsentPreferences;
|
|
1837
1902
|
exports.writeConsentCookie = writeConsentCookie;
|
|
1903
|
+
exports.writeConsentDismissalTimestamp = writeConsentDismissalTimestamp;
|
|
1838
1904
|
exports.writeConsentSessionMirror = writeConsentSessionMirror;
|