@uniformdev/context 20.84.0 → 20.86.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.
@@ -1,4 +1,4 @@
1
- import { p as paths$7, M as ManifestV2 } from '../types-DovaM4gV.mjs';
1
+ import { p as paths$7, M as ManifestV2 } from '../types-p9vnD820.mjs';
2
2
  import 'mitt';
3
3
 
4
4
  type LimitPolicy = <ReturnValue>(func: () => Promise<ReturnValue>) => Promise<ReturnValue>;
package/dist/api/api.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { p as paths$7, M as ManifestV2 } from '../types-DovaM4gV.js';
1
+ import { p as paths$7, M as ManifestV2 } from '../types-p9vnD820.js';
2
2
  import 'mitt';
3
3
 
4
4
  type LimitPolicy = <ReturnValue>(func: () => Promise<ReturnValue>) => Promise<ReturnValue>;
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { O as OutputSeverity, L as LogDrain, C as ContextPlugin, S as ScoreVector, A as AggregateDimension, T as TransitionDataStore, a as StorageCommands, V as VisitorData, Q as Quirks, b as TransitionDataStoreOptions, D as DecayFunction, c as CriteriaEvaluator, d as StringMatch, e as VariantMatchCriteria, f as LogMessage, P as PersonalizedVariant, g as PersonalizeOptions, h as Context, i as PersonalizedResult, j as VariationMatchDimensionCriteria, k as PersonalizationSelectionAlgorithmOptions, l as DevToolsEvents } from './types-DovaM4gV.mjs';
2
- export { m as AggregateDimensionInput, B as BehaviorTag, n as CONTEXTUAL_EDITING_TEST_NAME, o as CONTEXTUAL_EDITING_TEST_SELECTED_VARIANT_ID, q as CompositionMetadata, r as ContextEvents, s as ContextInstance, t as ContextOptions, u as ContextState, v as ContextStateUpdate, w as CriteriaEvaluatorParameters, x as CriteriaEvaluatorResult, y as DecayOptions, z as DevToolsActions, E as DevToolsDataEvent, F as DevToolsEvent, G as DevToolsForgetEvent, H as DevToolsHelloEvent, I as DevToolsLogEvent, J as DevToolsRawCommandsEvent, K as DevToolsState, N as DevToolsUiVersion, R as DevToolsUpdateEvent, U as DimensionMatch, W as EnrichmentCategory, X as EnrichmentData, Y as EventData, Z as GoalStateUpdate, _ as Goals, $ as GroupCriteriaEvaluator, a0 as IdentifyCommand, a1 as LogMessageGroup, a2 as LogMessageSingle, a3 as LogMessages, a4 as ManifestInstance, M as ManifestV2, a5 as MessageCategory, a6 as MessageFunc, a7 as ModifyScoreCommand, a8 as ModifySessionScoreCommand, a9 as NumberMatch, aa as PersonalizationEvent, ab as PersonalizationEventVariantId, ac as PersonalizationManifest, ad as PersonalizationSelectionAlgorithm, ae as PersonalizationSelectionAlgorithms, af as PersonalizeControlVariant, ag as PersonalizeVariants, ah as QuirkMatch, ai as SERVER_STATE_ID, aj as ServerToClientTransitionState, ak as SetConsentCommand, al as SetControlGroupCommand, am as SetGoalCommand, an as SetPersonalizeVariantControlCommand, ao as SetQuirkCommand, ap as SetTestCommand, aq as Severity, ar as Signal, as as SignalCriteria, at as SignalCriteriaGroup, au as SignalData, av as StorageCommand, aw as TestDefinition, ax as TestEvent, ay as TestOptions, az as TestResult, aA as TestVariant, aB as Tests, aC as TransitionDataStoreEvents, aD as VariationMatchMetadata, aE as VisitorDataStore, aF as VisitorDataStoreEvents, aG as VisitorDataStoreOptions, aH as areCommandsEqual, aI as emptyVisitorData, aJ as testVariations } from './types-DovaM4gV.mjs';
1
+ import { O as OutputSeverity, L as LogDrain, C as ContextPlugin, S as ScoreVector, A as AggregateDimension, T as TransitionDataStore, a as LogMessage, b as StorageCommands, V as VisitorData, Q as Quirks, c as TransitionDataStoreOptions, D as DecayFunction, d as CriteriaEvaluator, e as StringMatch, f as VariantMatchCriteria, P as PersonalizedVariant, g as PersonalizeOptions, h as Context, i as PersonalizedResult, j as VariationMatchDimensionCriteria, k as PersonalizationSelectionAlgorithmOptions, l as DevToolsEvents } from './types-p9vnD820.mjs';
2
+ export { m as AggregateDimensionInput, B as BehaviorTag, n as CONTEXTUAL_EDITING_TEST_NAME, o as CONTEXTUAL_EDITING_TEST_SELECTED_VARIANT_ID, q as CompositionMetadata, r as ContextEvents, s as ContextInstance, t as ContextOptions, u as ContextState, v as ContextStateUpdate, w as CriteriaEvaluatorParameters, x as CriteriaEvaluatorResult, y as DecayOptions, z as DevToolsActions, E as DevToolsDataEvent, F as DevToolsEvent, G as DevToolsForgetEvent, H as DevToolsHelloEvent, I as DevToolsLogEvent, J as DevToolsRawCommandsEvent, K as DevToolsState, N as DevToolsUiVersion, R as DevToolsUpdateEvent, U as DimensionMatch, W as EnrichmentCategory, X as EnrichmentData, Y as EventData, Z as GoalStateUpdate, _ as Goals, $ as GroupCriteriaEvaluator, a0 as IdentifyCommand, a1 as LogMessageGroup, a2 as LogMessageSingle, a3 as LogMessages, a4 as ManifestInstance, M as ManifestV2, a5 as MessageCategory, a6 as MessageFunc, a7 as ModifyScoreCommand, a8 as ModifySessionScoreCommand, a9 as NumberMatch, aa as PersonalizationEvent, ab as PersonalizationEventVariantId, ac as PersonalizationManifest, ad as PersonalizationSelectionAlgorithm, ae as PersonalizationSelectionAlgorithms, af as PersonalizeControlVariant, ag as PersonalizeVariants, ah as QuirkMatch, ai as SERVER_STATE_ID, aj as ServerToClientTransitionState, ak as SetConsentCommand, al as SetControlGroupCommand, am as SetGoalCommand, an as SetPersonalizeVariantControlCommand, ao as SetQuirkCommand, ap as SetTestCommand, aq as Severity, ar as Signal, as as SignalCriteria, at as SignalCriteriaGroup, au as SignalData, av as StorageCommand, aw as TestDefinition, ax as TestEvent, ay as TestOptions, az as TestResult, aA as TestVariant, aB as Tests, aC as TransitionDataStoreEvents, aD as VariationMatchMetadata, aE as VisitorDataStore, aF as VisitorDataStoreEvents, aG as VisitorDataStoreOptions, aH as areCommandsEqual, aI as emptyVisitorData, aJ as testVariations } from './types-p9vnD820.mjs';
3
3
  import 'mitt';
4
4
 
5
5
  /**
@@ -55,6 +55,27 @@ type CookieTransitionDataStoreOptions = {
55
55
  * local storage is the master copy. Defaults to SameSite=Lax.
56
56
  */
57
57
  cookieAttributes?: Cookies.CookieAttributes;
58
+ /**
59
+ * Ceiling on the size in bytes of the transition cookie, measured on the percent-encoded
60
+ * `name=value` pair as the request header carries it.
61
+ *
62
+ * When the visitor's data does not fit, the least valuable parts are dropped until it does,
63
+ * deterministically: dimensions the cookie would write as zero, then remaining dimensions by
64
+ * ascending score, then personalize variants, then test assignments. Control group membership
65
+ * is never dropped, so a budget below the cookie's empty cost is refused at construction.
66
+ *
67
+ * Covers this cookie only, not the quirk cookie, its attributes or other cookies on the domain.
68
+ * Local storage remains the complete copy, so only what the server is told is lost.
69
+ *
70
+ * @defaultValue undefined - the cookie is unbounded
71
+ */
72
+ maxBytes?: number;
73
+ /**
74
+ * Called with log messages the store emits, such as a report of what `maxBytes` dropped.
75
+ * Also settable after construction, which is how a host that builds the store as an argument
76
+ * to its `Context` points it at {@link Context.log}.
77
+ */
78
+ onLogMessage?: (message: LogMessage) => void;
58
79
  };
59
80
  declare const UNIFORM_DEFAULT_COOKIE_NAME = "ufvd";
60
81
  declare const UNIFORM_DEFAULT_QUIRK_COOKIE_NAME = "ufvdqk";
@@ -64,7 +85,9 @@ declare const UNIFORM_DEFAULT_QUIRK_COOKIE_NAME = "ufvdqk";
64
85
  */
65
86
  declare class CookieTransitionDataStore extends TransitionDataStore {
66
87
  #private;
67
- constructor({ serverCookieValue, experimental_quirksEnabled, quirkCookieValue, quirkCookieName, cookieName, cookieAttributes, }: CookieTransitionDataStoreOptions);
88
+ /** Where the store's log messages go. See {@link CookieTransitionDataStoreOptions.onLogMessage}. */
89
+ onLogMessage: ((message: LogMessage) => void) | undefined;
90
+ constructor({ serverCookieValue, experimental_quirksEnabled, quirkCookieValue, quirkCookieName, cookieName, cookieAttributes, maxBytes, onLogMessage, }: CookieTransitionDataStoreOptions);
68
91
  handleDelete(): Promise<void>;
69
92
  handleUpdateData(_: StorageCommands[], computedValue: VisitorData): Promise<void>;
70
93
  }
@@ -77,7 +100,19 @@ declare function parseQuirkCookie(quirkCookieValue: string): Quirks;
77
100
  declare function parseCookieScores(type: string | undefined): Record<string, string>;
78
101
  declare function parseCookieType(type: string | undefined): Record<string, string>;
79
102
  declare function decodeCookieType(type: Record<string, string>): Record<string, number>;
80
- declare function serializeCookie(data: VisitorData): string;
103
+ type SerializeCookieOptions = {
104
+ /**
105
+ * Ceiling on the size in bytes of the percent-encoded `name=value` pair.
106
+ * Parts of the visitor data are dropped until the cookie fits; see the drop order on
107
+ * {@link CookieTransitionDataStoreOptions.maxBytes}.
108
+ */
109
+ maxBytes?: number;
110
+ /** Cookie name, counted toward `maxBytes` along with the `=` that follows it. */
111
+ cookieName?: string;
112
+ /** Called once, when `maxBytes` forced at least one drop, with what was dropped. */
113
+ onBudgetExceeded?: (dropped: string[], bytes: number, maxBytes: number) => void;
114
+ };
115
+ declare function serializeCookie(data: VisitorData, options?: SerializeCookieOptions): string;
81
116
  declare function serializePersonalizeVariants({ personalizeVariants, }: Pick<VisitorData, 'personalizeVariants'>): string;
82
117
  declare function decodePersonalizeVariants(data: string | undefined): VisitorData['personalizeVariants'];
83
118
  declare function encodeCookieType(type: Record<string, number>): Record<string, string>;
@@ -225,4 +260,4 @@ type QuickConnectConfig = {
225
260
  declare function serializeQuickConnect(config: QuickConnectConfig): string;
226
261
  declare function parseQuickConnect(serialized: string): Required<QuickConnectConfig>;
227
262
 
228
- export { AggregateDimension, type ConsoleDebugLogDrainOptions, Context, type ContextDevToolOptions, ContextPlugin, CookieTransitionDataStore, type CookieTransitionDataStoreOptions, CriteriaEvaluator, DecayFunction, DevToolsEvents, ENR_SEPARATOR, EdgeNodeTagName, type EdgePersonalizeComponentOptions, type EdgeTestComponentOptions, EdgeTransitionDataStore, type EdgeTransitionDataStoreOptions, KV_SEP, type LinearDecayOptions, LogDrain, LogMessage, OutputSeverity, PAIR_SEP, PersonalizationSelectionAlgorithmOptions, PersonalizeOptions, PersonalizedResult, PersonalizedVariant, QUIRK_SEP, type QuickConnectConfig, Quirks, STRONGEST_SCORE_PERSONALIZATION_ALGORITHM, ScoreVector, ScriptType, StorageCommands, StringMatch, TOP_DOWN_CRITERIA_PERSONALIZATION_ALGORITHM, TYPE_SEP, TransitionDataStore, TransitionDataStoreOptions, UNIFORM_DEFAULT_COOKIE_NAME, UNIFORM_DEFAULT_QUIRK_COOKIE_NAME, VariantMatchCriteria, VariationMatchDimensionCriteria, VisitorData, computeAggregateDimensions, cookieEvaluator, createConsoleLogDrain, createDebugConsoleLogDrain, createLinearDecay, currentPageEvaluator, decodeCookieType, decodePersonalizeVariants, enableConsoleLogDrain, enableContextDevTools, enableDebugConsoleLogDrain, encodeCookieType, evaluateVariantMatch, eventEvaluator, explainStringMatch, explainStringMatchCriteria, getEnrichmentVectorKey, isStringMatch, pageViewCountDimension, pageViewCountEvaluator, parseCookieScores, parseCookieType, parseQuickConnect, parseQuirkCookie, parseScoreCookie, personalizeVariations, queryStringEvaluator, quirkEvaluator, serializeCookie, serializeCookieType, serializePersonalizeVariants, serializeQuickConnect, serializeQuirks, strongestScorePersonalizationSelectionAlgorithm, topDownCriteriaPersonalizationSelectionAlgorithm };
263
+ export { AggregateDimension, type ConsoleDebugLogDrainOptions, Context, type ContextDevToolOptions, ContextPlugin, CookieTransitionDataStore, type CookieTransitionDataStoreOptions, CriteriaEvaluator, DecayFunction, DevToolsEvents, ENR_SEPARATOR, EdgeNodeTagName, type EdgePersonalizeComponentOptions, type EdgeTestComponentOptions, EdgeTransitionDataStore, type EdgeTransitionDataStoreOptions, KV_SEP, type LinearDecayOptions, LogDrain, LogMessage, OutputSeverity, PAIR_SEP, PersonalizationSelectionAlgorithmOptions, PersonalizeOptions, PersonalizedResult, PersonalizedVariant, QUIRK_SEP, type QuickConnectConfig, Quirks, STRONGEST_SCORE_PERSONALIZATION_ALGORITHM, ScoreVector, ScriptType, type SerializeCookieOptions, StorageCommands, StringMatch, TOP_DOWN_CRITERIA_PERSONALIZATION_ALGORITHM, TYPE_SEP, TransitionDataStore, TransitionDataStoreOptions, UNIFORM_DEFAULT_COOKIE_NAME, UNIFORM_DEFAULT_QUIRK_COOKIE_NAME, VariantMatchCriteria, VariationMatchDimensionCriteria, VisitorData, computeAggregateDimensions, cookieEvaluator, createConsoleLogDrain, createDebugConsoleLogDrain, createLinearDecay, currentPageEvaluator, decodeCookieType, decodePersonalizeVariants, enableConsoleLogDrain, enableContextDevTools, enableDebugConsoleLogDrain, encodeCookieType, evaluateVariantMatch, eventEvaluator, explainStringMatch, explainStringMatchCriteria, getEnrichmentVectorKey, isStringMatch, pageViewCountDimension, pageViewCountEvaluator, parseCookieScores, parseCookieType, parseQuickConnect, parseQuirkCookie, parseScoreCookie, personalizeVariations, queryStringEvaluator, quirkEvaluator, serializeCookie, serializeCookieType, serializePersonalizeVariants, serializeQuickConnect, serializeQuirks, strongestScorePersonalizationSelectionAlgorithm, topDownCriteriaPersonalizationSelectionAlgorithm };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { O as OutputSeverity, L as LogDrain, C as ContextPlugin, S as ScoreVector, A as AggregateDimension, T as TransitionDataStore, a as StorageCommands, V as VisitorData, Q as Quirks, b as TransitionDataStoreOptions, D as DecayFunction, c as CriteriaEvaluator, d as StringMatch, e as VariantMatchCriteria, f as LogMessage, P as PersonalizedVariant, g as PersonalizeOptions, h as Context, i as PersonalizedResult, j as VariationMatchDimensionCriteria, k as PersonalizationSelectionAlgorithmOptions, l as DevToolsEvents } from './types-DovaM4gV.js';
2
- export { m as AggregateDimensionInput, B as BehaviorTag, n as CONTEXTUAL_EDITING_TEST_NAME, o as CONTEXTUAL_EDITING_TEST_SELECTED_VARIANT_ID, q as CompositionMetadata, r as ContextEvents, s as ContextInstance, t as ContextOptions, u as ContextState, v as ContextStateUpdate, w as CriteriaEvaluatorParameters, x as CriteriaEvaluatorResult, y as DecayOptions, z as DevToolsActions, E as DevToolsDataEvent, F as DevToolsEvent, G as DevToolsForgetEvent, H as DevToolsHelloEvent, I as DevToolsLogEvent, J as DevToolsRawCommandsEvent, K as DevToolsState, N as DevToolsUiVersion, R as DevToolsUpdateEvent, U as DimensionMatch, W as EnrichmentCategory, X as EnrichmentData, Y as EventData, Z as GoalStateUpdate, _ as Goals, $ as GroupCriteriaEvaluator, a0 as IdentifyCommand, a1 as LogMessageGroup, a2 as LogMessageSingle, a3 as LogMessages, a4 as ManifestInstance, M as ManifestV2, a5 as MessageCategory, a6 as MessageFunc, a7 as ModifyScoreCommand, a8 as ModifySessionScoreCommand, a9 as NumberMatch, aa as PersonalizationEvent, ab as PersonalizationEventVariantId, ac as PersonalizationManifest, ad as PersonalizationSelectionAlgorithm, ae as PersonalizationSelectionAlgorithms, af as PersonalizeControlVariant, ag as PersonalizeVariants, ah as QuirkMatch, ai as SERVER_STATE_ID, aj as ServerToClientTransitionState, ak as SetConsentCommand, al as SetControlGroupCommand, am as SetGoalCommand, an as SetPersonalizeVariantControlCommand, ao as SetQuirkCommand, ap as SetTestCommand, aq as Severity, ar as Signal, as as SignalCriteria, at as SignalCriteriaGroup, au as SignalData, av as StorageCommand, aw as TestDefinition, ax as TestEvent, ay as TestOptions, az as TestResult, aA as TestVariant, aB as Tests, aC as TransitionDataStoreEvents, aD as VariationMatchMetadata, aE as VisitorDataStore, aF as VisitorDataStoreEvents, aG as VisitorDataStoreOptions, aH as areCommandsEqual, aI as emptyVisitorData, aJ as testVariations } from './types-DovaM4gV.js';
1
+ import { O as OutputSeverity, L as LogDrain, C as ContextPlugin, S as ScoreVector, A as AggregateDimension, T as TransitionDataStore, a as LogMessage, b as StorageCommands, V as VisitorData, Q as Quirks, c as TransitionDataStoreOptions, D as DecayFunction, d as CriteriaEvaluator, e as StringMatch, f as VariantMatchCriteria, P as PersonalizedVariant, g as PersonalizeOptions, h as Context, i as PersonalizedResult, j as VariationMatchDimensionCriteria, k as PersonalizationSelectionAlgorithmOptions, l as DevToolsEvents } from './types-p9vnD820.js';
2
+ export { m as AggregateDimensionInput, B as BehaviorTag, n as CONTEXTUAL_EDITING_TEST_NAME, o as CONTEXTUAL_EDITING_TEST_SELECTED_VARIANT_ID, q as CompositionMetadata, r as ContextEvents, s as ContextInstance, t as ContextOptions, u as ContextState, v as ContextStateUpdate, w as CriteriaEvaluatorParameters, x as CriteriaEvaluatorResult, y as DecayOptions, z as DevToolsActions, E as DevToolsDataEvent, F as DevToolsEvent, G as DevToolsForgetEvent, H as DevToolsHelloEvent, I as DevToolsLogEvent, J as DevToolsRawCommandsEvent, K as DevToolsState, N as DevToolsUiVersion, R as DevToolsUpdateEvent, U as DimensionMatch, W as EnrichmentCategory, X as EnrichmentData, Y as EventData, Z as GoalStateUpdate, _ as Goals, $ as GroupCriteriaEvaluator, a0 as IdentifyCommand, a1 as LogMessageGroup, a2 as LogMessageSingle, a3 as LogMessages, a4 as ManifestInstance, M as ManifestV2, a5 as MessageCategory, a6 as MessageFunc, a7 as ModifyScoreCommand, a8 as ModifySessionScoreCommand, a9 as NumberMatch, aa as PersonalizationEvent, ab as PersonalizationEventVariantId, ac as PersonalizationManifest, ad as PersonalizationSelectionAlgorithm, ae as PersonalizationSelectionAlgorithms, af as PersonalizeControlVariant, ag as PersonalizeVariants, ah as QuirkMatch, ai as SERVER_STATE_ID, aj as ServerToClientTransitionState, ak as SetConsentCommand, al as SetControlGroupCommand, am as SetGoalCommand, an as SetPersonalizeVariantControlCommand, ao as SetQuirkCommand, ap as SetTestCommand, aq as Severity, ar as Signal, as as SignalCriteria, at as SignalCriteriaGroup, au as SignalData, av as StorageCommand, aw as TestDefinition, ax as TestEvent, ay as TestOptions, az as TestResult, aA as TestVariant, aB as Tests, aC as TransitionDataStoreEvents, aD as VariationMatchMetadata, aE as VisitorDataStore, aF as VisitorDataStoreEvents, aG as VisitorDataStoreOptions, aH as areCommandsEqual, aI as emptyVisitorData, aJ as testVariations } from './types-p9vnD820.js';
3
3
  import 'mitt';
4
4
 
5
5
  /**
@@ -55,6 +55,27 @@ type CookieTransitionDataStoreOptions = {
55
55
  * local storage is the master copy. Defaults to SameSite=Lax.
56
56
  */
57
57
  cookieAttributes?: Cookies.CookieAttributes;
58
+ /**
59
+ * Ceiling on the size in bytes of the transition cookie, measured on the percent-encoded
60
+ * `name=value` pair as the request header carries it.
61
+ *
62
+ * When the visitor's data does not fit, the least valuable parts are dropped until it does,
63
+ * deterministically: dimensions the cookie would write as zero, then remaining dimensions by
64
+ * ascending score, then personalize variants, then test assignments. Control group membership
65
+ * is never dropped, so a budget below the cookie's empty cost is refused at construction.
66
+ *
67
+ * Covers this cookie only, not the quirk cookie, its attributes or other cookies on the domain.
68
+ * Local storage remains the complete copy, so only what the server is told is lost.
69
+ *
70
+ * @defaultValue undefined - the cookie is unbounded
71
+ */
72
+ maxBytes?: number;
73
+ /**
74
+ * Called with log messages the store emits, such as a report of what `maxBytes` dropped.
75
+ * Also settable after construction, which is how a host that builds the store as an argument
76
+ * to its `Context` points it at {@link Context.log}.
77
+ */
78
+ onLogMessage?: (message: LogMessage) => void;
58
79
  };
59
80
  declare const UNIFORM_DEFAULT_COOKIE_NAME = "ufvd";
60
81
  declare const UNIFORM_DEFAULT_QUIRK_COOKIE_NAME = "ufvdqk";
@@ -64,7 +85,9 @@ declare const UNIFORM_DEFAULT_QUIRK_COOKIE_NAME = "ufvdqk";
64
85
  */
65
86
  declare class CookieTransitionDataStore extends TransitionDataStore {
66
87
  #private;
67
- constructor({ serverCookieValue, experimental_quirksEnabled, quirkCookieValue, quirkCookieName, cookieName, cookieAttributes, }: CookieTransitionDataStoreOptions);
88
+ /** Where the store's log messages go. See {@link CookieTransitionDataStoreOptions.onLogMessage}. */
89
+ onLogMessage: ((message: LogMessage) => void) | undefined;
90
+ constructor({ serverCookieValue, experimental_quirksEnabled, quirkCookieValue, quirkCookieName, cookieName, cookieAttributes, maxBytes, onLogMessage, }: CookieTransitionDataStoreOptions);
68
91
  handleDelete(): Promise<void>;
69
92
  handleUpdateData(_: StorageCommands[], computedValue: VisitorData): Promise<void>;
70
93
  }
@@ -77,7 +100,19 @@ declare function parseQuirkCookie(quirkCookieValue: string): Quirks;
77
100
  declare function parseCookieScores(type: string | undefined): Record<string, string>;
78
101
  declare function parseCookieType(type: string | undefined): Record<string, string>;
79
102
  declare function decodeCookieType(type: Record<string, string>): Record<string, number>;
80
- declare function serializeCookie(data: VisitorData): string;
103
+ type SerializeCookieOptions = {
104
+ /**
105
+ * Ceiling on the size in bytes of the percent-encoded `name=value` pair.
106
+ * Parts of the visitor data are dropped until the cookie fits; see the drop order on
107
+ * {@link CookieTransitionDataStoreOptions.maxBytes}.
108
+ */
109
+ maxBytes?: number;
110
+ /** Cookie name, counted toward `maxBytes` along with the `=` that follows it. */
111
+ cookieName?: string;
112
+ /** Called once, when `maxBytes` forced at least one drop, with what was dropped. */
113
+ onBudgetExceeded?: (dropped: string[], bytes: number, maxBytes: number) => void;
114
+ };
115
+ declare function serializeCookie(data: VisitorData, options?: SerializeCookieOptions): string;
81
116
  declare function serializePersonalizeVariants({ personalizeVariants, }: Pick<VisitorData, 'personalizeVariants'>): string;
82
117
  declare function decodePersonalizeVariants(data: string | undefined): VisitorData['personalizeVariants'];
83
118
  declare function encodeCookieType(type: Record<string, number>): Record<string, string>;
@@ -225,4 +260,4 @@ type QuickConnectConfig = {
225
260
  declare function serializeQuickConnect(config: QuickConnectConfig): string;
226
261
  declare function parseQuickConnect(serialized: string): Required<QuickConnectConfig>;
227
262
 
228
- export { AggregateDimension, type ConsoleDebugLogDrainOptions, Context, type ContextDevToolOptions, ContextPlugin, CookieTransitionDataStore, type CookieTransitionDataStoreOptions, CriteriaEvaluator, DecayFunction, DevToolsEvents, ENR_SEPARATOR, EdgeNodeTagName, type EdgePersonalizeComponentOptions, type EdgeTestComponentOptions, EdgeTransitionDataStore, type EdgeTransitionDataStoreOptions, KV_SEP, type LinearDecayOptions, LogDrain, LogMessage, OutputSeverity, PAIR_SEP, PersonalizationSelectionAlgorithmOptions, PersonalizeOptions, PersonalizedResult, PersonalizedVariant, QUIRK_SEP, type QuickConnectConfig, Quirks, STRONGEST_SCORE_PERSONALIZATION_ALGORITHM, ScoreVector, ScriptType, StorageCommands, StringMatch, TOP_DOWN_CRITERIA_PERSONALIZATION_ALGORITHM, TYPE_SEP, TransitionDataStore, TransitionDataStoreOptions, UNIFORM_DEFAULT_COOKIE_NAME, UNIFORM_DEFAULT_QUIRK_COOKIE_NAME, VariantMatchCriteria, VariationMatchDimensionCriteria, VisitorData, computeAggregateDimensions, cookieEvaluator, createConsoleLogDrain, createDebugConsoleLogDrain, createLinearDecay, currentPageEvaluator, decodeCookieType, decodePersonalizeVariants, enableConsoleLogDrain, enableContextDevTools, enableDebugConsoleLogDrain, encodeCookieType, evaluateVariantMatch, eventEvaluator, explainStringMatch, explainStringMatchCriteria, getEnrichmentVectorKey, isStringMatch, pageViewCountDimension, pageViewCountEvaluator, parseCookieScores, parseCookieType, parseQuickConnect, parseQuirkCookie, parseScoreCookie, personalizeVariations, queryStringEvaluator, quirkEvaluator, serializeCookie, serializeCookieType, serializePersonalizeVariants, serializeQuickConnect, serializeQuirks, strongestScorePersonalizationSelectionAlgorithm, topDownCriteriaPersonalizationSelectionAlgorithm };
263
+ export { AggregateDimension, type ConsoleDebugLogDrainOptions, Context, type ContextDevToolOptions, ContextPlugin, CookieTransitionDataStore, type CookieTransitionDataStoreOptions, CriteriaEvaluator, DecayFunction, DevToolsEvents, ENR_SEPARATOR, EdgeNodeTagName, type EdgePersonalizeComponentOptions, type EdgeTestComponentOptions, EdgeTransitionDataStore, type EdgeTransitionDataStoreOptions, KV_SEP, type LinearDecayOptions, LogDrain, LogMessage, OutputSeverity, PAIR_SEP, PersonalizationSelectionAlgorithmOptions, PersonalizeOptions, PersonalizedResult, PersonalizedVariant, QUIRK_SEP, type QuickConnectConfig, Quirks, STRONGEST_SCORE_PERSONALIZATION_ALGORITHM, ScoreVector, ScriptType, type SerializeCookieOptions, StorageCommands, StringMatch, TOP_DOWN_CRITERIA_PERSONALIZATION_ALGORITHM, TYPE_SEP, TransitionDataStore, TransitionDataStoreOptions, UNIFORM_DEFAULT_COOKIE_NAME, UNIFORM_DEFAULT_QUIRK_COOKIE_NAME, VariantMatchCriteria, VariationMatchDimensionCriteria, VisitorData, computeAggregateDimensions, cookieEvaluator, createConsoleLogDrain, createDebugConsoleLogDrain, createLinearDecay, currentPageEvaluator, decodeCookieType, decodePersonalizeVariants, enableConsoleLogDrain, enableContextDevTools, enableDebugConsoleLogDrain, encodeCookieType, evaluateVariantMatch, eventEvaluator, explainStringMatch, explainStringMatchCriteria, getEnrichmentVectorKey, isStringMatch, pageViewCountDimension, pageViewCountEvaluator, parseCookieScores, parseCookieType, parseQuickConnect, parseQuirkCookie, parseScoreCookie, personalizeVariations, queryStringEvaluator, quirkEvaluator, serializeCookie, serializeCookieType, serializePersonalizeVariants, serializeQuickConnect, serializeQuirks, strongestScorePersonalizationSelectionAlgorithm, topDownCriteriaPersonalizationSelectionAlgorithm };
package/dist/index.esm.js CHANGED
@@ -1074,11 +1074,14 @@ var bton = (base64) => {
1074
1074
  return sign ? -number : number;
1075
1075
  };
1076
1076
 
1077
+ // src/storage/util/zeroScore.ts
1078
+ var isZeroCookieScore = (score) => Math.abs(score) < 1;
1079
+
1077
1080
  // src/storage/CookieTransitionDataStore.ts
1078
1081
  var ssr = typeof document === "undefined";
1079
1082
  var UNIFORM_DEFAULT_COOKIE_NAME = "ufvd";
1080
1083
  var UNIFORM_DEFAULT_QUIRK_COOKIE_NAME = `ufvdqk`;
1081
- var _cookieName, _quirkCookieName, _cookieAttributes, _quirksEnabled;
1084
+ var _cookieName, _quirkCookieName, _cookieAttributes, _quirksEnabled, _maxBytes;
1082
1085
  var CookieTransitionDataStore = class extends TransitionDataStore {
1083
1086
  constructor({
1084
1087
  serverCookieValue,
@@ -1086,7 +1089,9 @@ var CookieTransitionDataStore = class extends TransitionDataStore {
1086
1089
  quirkCookieValue,
1087
1090
  quirkCookieName = UNIFORM_DEFAULT_QUIRK_COOKIE_NAME,
1088
1091
  cookieName = UNIFORM_DEFAULT_COOKIE_NAME,
1089
- cookieAttributes = { sameSite: "lax" }
1092
+ cookieAttributes = { sameSite: "lax" },
1093
+ maxBytes,
1094
+ onLogMessage
1090
1095
  }) {
1091
1096
  super({
1092
1097
  initialData: ssr ? parseScoreCookie(serverCookieValue, experimental_quirksEnabled ? quirkCookieValue : void 0) : void 0
@@ -1095,10 +1100,21 @@ var CookieTransitionDataStore = class extends TransitionDataStore {
1095
1100
  __privateAdd(this, _quirkCookieName);
1096
1101
  __privateAdd(this, _cookieAttributes);
1097
1102
  __privateAdd(this, _quirksEnabled);
1103
+ __privateAdd(this, _maxBytes);
1104
+ /** Where the store's log messages go. See {@link CookieTransitionDataStoreOptions.onLogMessage}. */
1105
+ __publicField(this, "onLogMessage");
1098
1106
  __privateSet(this, _cookieName, cookieName);
1099
1107
  __privateSet(this, _cookieAttributes, cookieAttributes);
1100
1108
  __privateSet(this, _quirkCookieName, quirkCookieName);
1101
1109
  __privateSet(this, _quirksEnabled, experimental_quirksEnabled || false);
1110
+ __privateSet(this, _maxBytes, maxBytes);
1111
+ this.onLogMessage = onLogMessage;
1112
+ const minimumBytes = minimumCookieBytes(cookieName);
1113
+ if (maxBytes !== void 0 && maxBytes < minimumBytes) {
1114
+ throw new Error(
1115
+ `maxBytes of ${maxBytes} is below the ${minimumBytes} bytes the "${cookieName}" cookie costs when it carries nothing but control group membership, so no visitor could ever meet it.`
1116
+ );
1117
+ }
1102
1118
  }
1103
1119
  handleDelete() {
1104
1120
  if (ssr) return Promise.resolve();
@@ -1108,7 +1124,15 @@ var CookieTransitionDataStore = class extends TransitionDataStore {
1108
1124
  async handleUpdateData(_, computedValue) {
1109
1125
  if (ssr) return;
1110
1126
  if (computedValue.consent) {
1111
- Cookies.set(__privateGet(this, _cookieName), serializeCookie(computedValue), __privateGet(this, _cookieAttributes));
1127
+ const cookieValue = serializeCookie(computedValue, {
1128
+ maxBytes: __privateGet(this, _maxBytes),
1129
+ cookieName: __privateGet(this, _cookieName),
1130
+ onBudgetExceeded: (dropped, bytes, maxBytes) => {
1131
+ var _a;
1132
+ return (_a = this.onLogMessage) == null ? void 0 : _a.call(this, ["info", 151, { dropped, bytes, maxBytes }]);
1133
+ }
1134
+ });
1135
+ Cookies.set(__privateGet(this, _cookieName), cookieValue, __privateGet(this, _cookieAttributes));
1112
1136
  if (__privateGet(this, _quirksEnabled) && Object.keys(computedValue.quirks || {}).length) {
1113
1137
  const quirkCookieValue = serializeQuirks(computedValue.quirks || {});
1114
1138
  Cookies.set(__privateGet(this, _quirkCookieName), quirkCookieValue, __privateGet(this, _cookieAttributes));
@@ -1125,6 +1149,7 @@ _cookieName = new WeakMap();
1125
1149
  _quirkCookieName = new WeakMap();
1126
1150
  _cookieAttributes = new WeakMap();
1127
1151
  _quirksEnabled = new WeakMap();
1152
+ _maxBytes = new WeakMap();
1128
1153
  var TYPE_SEP = "~";
1129
1154
  var PAIR_SEP = "!";
1130
1155
  var KV_SEP = "-";
@@ -1196,18 +1221,71 @@ function decodeCookieType(type) {
1196
1221
  return acc;
1197
1222
  }, {});
1198
1223
  }
1199
- function serializeCookie(data) {
1224
+ function serializeCookie(data, options = {}) {
1225
+ const { maxBytes, cookieName = UNIFORM_DEFAULT_COOKIE_NAME, onBudgetExceeded } = options;
1226
+ const parts = {
1227
+ tests: { ...data.tests },
1228
+ sessionScores: encodeCookieType(data.sessionScores),
1229
+ scores: encodeCookieType(data.scores),
1230
+ controlGroup: data.controlGroup ? "1" : "0",
1231
+ personalizeVariants: encodePersonalizeVariants(data.personalizeVariants)
1232
+ };
1233
+ let value = serializeCookieParts(parts);
1234
+ if (maxBytes === void 0) {
1235
+ return value;
1236
+ }
1237
+ const overhead = encodeCookieName(cookieName).length + 1;
1238
+ const measure = () => overhead + Cookies.converter.write(value, cookieName).length;
1239
+ if (measure() <= maxBytes) {
1240
+ return value;
1241
+ }
1242
+ const candidates = dropCandidates(parts, data);
1243
+ const dropped = [];
1244
+ for (let i = 0; i < candidates.length && measure() > maxBytes; i++) {
1245
+ const { bucket, key } = candidates[i];
1246
+ delete parts[bucket][key];
1247
+ dropped.push(`${bucket}.${key}`);
1248
+ value = serializeCookieParts(parts);
1249
+ }
1250
+ if (dropped.length) {
1251
+ onBudgetExceeded == null ? void 0 : onBudgetExceeded(dropped, measure(), maxBytes);
1252
+ }
1253
+ return value;
1254
+ }
1255
+ function serializeCookieParts(parts) {
1200
1256
  return [
1201
- serializeCookieType(data.tests),
1202
- serializeCookieType(encodeCookieType(data.sessionScores)),
1203
- serializeCookieType(encodeCookieType(data.scores)),
1204
- data.controlGroup ? "1" : "0",
1205
- serializePersonalizeVariants(data)
1257
+ serializeCookieType(parts.tests),
1258
+ serializeCookieType(parts.sessionScores),
1259
+ serializeCookieType(parts.scores),
1260
+ parts.controlGroup,
1261
+ serializeCookieType(parts.personalizeVariants)
1206
1262
  ].join(TYPE_SEP);
1207
1263
  }
1264
+ function dropCandidates(parts, data) {
1265
+ const dimensions = ["sessionScores", "scores"].flatMap(
1266
+ (bucket, order) => Object.keys(parts[bucket]).map((key) => {
1267
+ var _a;
1268
+ return { bucket, key, order, score: (_a = data[bucket][key]) != null ? _a : 0 };
1269
+ })
1270
+ );
1271
+ dimensions.sort(
1272
+ (a, b) => Number(isZeroCookieScore(b.score)) - Number(isZeroCookieScore(a.score)) || a.score - b.score || a.order - b.order || (a.key < b.key ? -1 : 1)
1273
+ );
1274
+ const keysOf = (bucket) => Object.keys(parts[bucket]).sort().map((key) => ({ bucket, key }));
1275
+ return [...dimensions, ...keysOf("personalizeVariants"), ...keysOf("tests")];
1276
+ }
1277
+ function minimumCookieBytes(cookieName) {
1278
+ return encodeCookieName(cookieName).length + 6;
1279
+ }
1280
+ function encodeCookieName(name) {
1281
+ return encodeURIComponent(name).replace(/%(2[346B]|5E|60|7C)/g, decodeURIComponent).replace(/\(/g, "%28").replace(/\)/g, "%29");
1282
+ }
1208
1283
  function serializePersonalizeVariants({
1209
1284
  personalizeVariants
1210
1285
  }) {
1286
+ return serializeCookieType(encodePersonalizeVariants(personalizeVariants));
1287
+ }
1288
+ function encodePersonalizeVariants(personalizeVariants) {
1211
1289
  const data = {};
1212
1290
  if (typeof personalizeVariants === "object") {
1213
1291
  Object.keys(personalizeVariants).forEach((personalizationName) => {
@@ -1219,8 +1297,7 @@ function serializePersonalizeVariants({
1219
1297
  data[personalizationName] = results.join(",");
1220
1298
  });
1221
1299
  }
1222
- const serialized = serializeCookieType(data);
1223
- return serialized;
1300
+ return data;
1224
1301
  }
1225
1302
  function decodePersonalizeVariants(data) {
1226
1303
  const parsed = parseCookieType(data);
@@ -1293,6 +1370,9 @@ fetchData_fn = async function() {
1293
1370
  }
1294
1371
  };
1295
1372
 
1373
+ // src/storage/util/systemDimension.ts
1374
+ var isSystemDimension = (dimension) => dimension.startsWith("$");
1375
+
1296
1376
  // src/storage/linearDecay.ts
1297
1377
  function createLinearDecay(options) {
1298
1378
  const { gracePeriod = 864e5, decayRate = 1 / 30, decayCap = 0.95 } = options != null ? options : {};
@@ -1318,7 +1398,7 @@ function createLinearDecay(options) {
1318
1398
  }
1319
1399
  function decayVector(vector, decay) {
1320
1400
  for (const key in vector) {
1321
- if (key.startsWith("$")) {
1401
+ if (isSystemDimension(key)) {
1322
1402
  continue;
1323
1403
  }
1324
1404
  vector[key] *= decay;
@@ -1462,7 +1542,7 @@ key_fn = function(key) {
1462
1542
 
1463
1543
  // src/storage/VisitorDataStore.ts
1464
1544
  var STORAGE_KEY = "ufvisitor";
1465
- var _mitt2, _persist, _visitTimeout, _options, _VisitorDataStore_instances, currentData_get, replaceData_fn, setVisitTimeout_fn, isExpired_fn, handleCaps_fn, defaultData_fn;
1545
+ var _mitt2, _persist, _visitTimeout, _options, _VisitorDataStore_instances, currentData_get, replaceData_fn, setVisitTimeout_fn, isExpired_fn, handleCaps_fn, pruningManifest_get, handlePruning_fn, pruneScores_fn, defaultData_fn;
1466
1546
  var VisitorDataStore = class {
1467
1547
  constructor(options) {
1468
1548
  __privateAdd(this, _VisitorDataStore_instances);
@@ -1580,6 +1660,7 @@ replaceData_fn = function(data, quiet = false) {
1580
1660
  sessionScores: data.sessionScores,
1581
1661
  onLogMessage: __privateGet(this, _options).onLogMessage
1582
1662
  });
1663
+ __privateMethod(this, _VisitorDataStore_instances, handlePruning_fn).call(this, data);
1583
1664
  const haveScoresChanged = !dequal4(oldData == null ? void 0 : oldData.visitorData.scores, data.scores);
1584
1665
  const haveSessionScoresChanged = !dequal4(oldData == null ? void 0 : oldData.visitorData.sessionScores, data.sessionScores);
1585
1666
  const haveQuirksChanged = !dequal4(oldData == null ? void 0 : oldData.visitorData.quirks, data.quirks);
@@ -1657,6 +1738,58 @@ handleCaps_fn = function(scores) {
1657
1738
  }
1658
1739
  }
1659
1740
  };
1741
+ pruningManifest_get = function() {
1742
+ var _a, _b, _c, _d, _e;
1743
+ const project = (_b = (_a = __privateGet(this, _options).manifest) == null ? void 0 : _a.data) == null ? void 0 : _b.project;
1744
+ const populated = [(_c = project == null ? void 0 : project.pz) == null ? void 0 : _c.sig, (_d = project == null ? void 0 : project.pz) == null ? void 0 : _d.enr, (_e = project == null ? void 0 : project.pz) == null ? void 0 : _e.agg, project == null ? void 0 : project.test].some(
1745
+ (definitions) => definitions && Object.keys(definitions).length > 0
1746
+ );
1747
+ return populated ? __privateGet(this, _options).manifest : void 0;
1748
+ };
1749
+ /**
1750
+ * IMPORTANT: This function mutates the input data, as #replaceData does.
1751
+ *
1752
+ * Removes stored keys that the host has opted to stop carrying. The two manifest-driven
1753
+ * prunes are skipped entirely when no populated manifest is loaded, so a partial load destroys
1754
+ * nothing; zero-score pruning consults no manifest and always runs when enabled.
1755
+ */
1756
+ handlePruning_fn = function(data) {
1757
+ var _a, _b;
1758
+ const { pruneUnknownTests, pruneUnknownDimensions } = __privateGet(this, _options);
1759
+ const manifest = pruneUnknownTests || pruneUnknownDimensions ? __privateGet(this, _VisitorDataStore_instances, pruningManifest_get) : void 0;
1760
+ const pruned = [];
1761
+ if (pruneUnknownTests && manifest) {
1762
+ for (const test in data.tests) {
1763
+ if (!manifest.getTest(test)) {
1764
+ delete data.tests[test];
1765
+ pruned.push(`tests.${test}`);
1766
+ }
1767
+ }
1768
+ }
1769
+ __privateMethod(this, _VisitorDataStore_instances, pruneScores_fn).call(this, data.scores, "scores", pruned, manifest);
1770
+ __privateMethod(this, _VisitorDataStore_instances, pruneScores_fn).call(this, data.sessionScores, "sessionScores", pruned, manifest);
1771
+ if (pruned.length) {
1772
+ (_b = (_a = __privateGet(this, _options)).onLogMessage) == null ? void 0 : _b.call(_a, ["info", 150, pruned]);
1773
+ }
1774
+ };
1775
+ /**
1776
+ * IMPORTANT: This function mutates the input scores.
1777
+ */
1778
+ pruneScores_fn = function(scores, bucket, pruned, manifest) {
1779
+ const { pruneUnknownDimensions, pruneZeroScores } = __privateGet(this, _options);
1780
+ if (!pruneUnknownDimensions && !pruneZeroScores) {
1781
+ return;
1782
+ }
1783
+ const pruneUnknown = pruneUnknownDimensions && manifest;
1784
+ for (const dim in scores) {
1785
+ const isUnknown = pruneUnknown && !isSystemDimension(dim) && !manifest.getDimensionByKey(dim);
1786
+ const isZero = pruneZeroScores && (pruneZeroScores === "round" ? isZeroCookieScore(scores[dim]) : scores[dim] === 0);
1787
+ if (isUnknown || isZero) {
1788
+ delete scores[dim];
1789
+ pruned.push(`${bucket}.${dim}`);
1790
+ }
1791
+ }
1792
+ };
1660
1793
  defaultData_fn = function() {
1661
1794
  var _a, _b, _c;
1662
1795
  return {
@@ -2388,6 +2521,13 @@ var messageContent = {
2388
2521
  ],
2389
2522
  131: () => ["storage", "server to client transition data was discarded"],
2390
2523
  140: (decayMessage) => ["storage", `score decay was applied: ${decayMessage}`],
2524
+ 150: (keys) => ["storage", `pruned from stored visitor data: ${keys.join(", ")}`],
2525
+ 151: ({ dropped, bytes, maxBytes }) => [
2526
+ "storage",
2527
+ `transition cookie exceeded its ${maxBytes} byte budget; dropped ${dropped.join(
2528
+ ", "
2529
+ )} to reach ${bytes} bytes`
2530
+ ],
2391
2531
  // SIGNAL EVAL
2392
2532
  200: () => ["signals", "evaluating signals"],
2393
2533
  201: (signal) => ["signals", `evaluating signal ${signal.id} (${signal.dur})`],
package/dist/index.js CHANGED
@@ -1166,11 +1166,14 @@ var bton = (base64) => {
1166
1166
  return sign ? -number : number;
1167
1167
  };
1168
1168
 
1169
+ // src/storage/util/zeroScore.ts
1170
+ var isZeroCookieScore = (score) => Math.abs(score) < 1;
1171
+
1169
1172
  // src/storage/CookieTransitionDataStore.ts
1170
1173
  var ssr = typeof document === "undefined";
1171
1174
  var UNIFORM_DEFAULT_COOKIE_NAME = "ufvd";
1172
1175
  var UNIFORM_DEFAULT_QUIRK_COOKIE_NAME = `ufvdqk`;
1173
- var _cookieName, _quirkCookieName, _cookieAttributes, _quirksEnabled;
1176
+ var _cookieName, _quirkCookieName, _cookieAttributes, _quirksEnabled, _maxBytes;
1174
1177
  var CookieTransitionDataStore = class extends TransitionDataStore {
1175
1178
  constructor({
1176
1179
  serverCookieValue,
@@ -1178,7 +1181,9 @@ var CookieTransitionDataStore = class extends TransitionDataStore {
1178
1181
  quirkCookieValue,
1179
1182
  quirkCookieName = UNIFORM_DEFAULT_QUIRK_COOKIE_NAME,
1180
1183
  cookieName = UNIFORM_DEFAULT_COOKIE_NAME,
1181
- cookieAttributes = { sameSite: "lax" }
1184
+ cookieAttributes = { sameSite: "lax" },
1185
+ maxBytes,
1186
+ onLogMessage
1182
1187
  }) {
1183
1188
  super({
1184
1189
  initialData: ssr ? parseScoreCookie(serverCookieValue, experimental_quirksEnabled ? quirkCookieValue : void 0) : void 0
@@ -1187,10 +1192,21 @@ var CookieTransitionDataStore = class extends TransitionDataStore {
1187
1192
  __privateAdd(this, _quirkCookieName);
1188
1193
  __privateAdd(this, _cookieAttributes);
1189
1194
  __privateAdd(this, _quirksEnabled);
1195
+ __privateAdd(this, _maxBytes);
1196
+ /** Where the store's log messages go. See {@link CookieTransitionDataStoreOptions.onLogMessage}. */
1197
+ __publicField(this, "onLogMessage");
1190
1198
  __privateSet(this, _cookieName, cookieName);
1191
1199
  __privateSet(this, _cookieAttributes, cookieAttributes);
1192
1200
  __privateSet(this, _quirkCookieName, quirkCookieName);
1193
1201
  __privateSet(this, _quirksEnabled, experimental_quirksEnabled || false);
1202
+ __privateSet(this, _maxBytes, maxBytes);
1203
+ this.onLogMessage = onLogMessage;
1204
+ const minimumBytes = minimumCookieBytes(cookieName);
1205
+ if (maxBytes !== void 0 && maxBytes < minimumBytes) {
1206
+ throw new Error(
1207
+ `maxBytes of ${maxBytes} is below the ${minimumBytes} bytes the "${cookieName}" cookie costs when it carries nothing but control group membership, so no visitor could ever meet it.`
1208
+ );
1209
+ }
1194
1210
  }
1195
1211
  handleDelete() {
1196
1212
  if (ssr) return Promise.resolve();
@@ -1200,7 +1216,15 @@ var CookieTransitionDataStore = class extends TransitionDataStore {
1200
1216
  async handleUpdateData(_, computedValue) {
1201
1217
  if (ssr) return;
1202
1218
  if (computedValue.consent) {
1203
- import_js_cookie.default.set(__privateGet(this, _cookieName), serializeCookie(computedValue), __privateGet(this, _cookieAttributes));
1219
+ const cookieValue = serializeCookie(computedValue, {
1220
+ maxBytes: __privateGet(this, _maxBytes),
1221
+ cookieName: __privateGet(this, _cookieName),
1222
+ onBudgetExceeded: (dropped, bytes, maxBytes) => {
1223
+ var _a;
1224
+ return (_a = this.onLogMessage) == null ? void 0 : _a.call(this, ["info", 151, { dropped, bytes, maxBytes }]);
1225
+ }
1226
+ });
1227
+ import_js_cookie.default.set(__privateGet(this, _cookieName), cookieValue, __privateGet(this, _cookieAttributes));
1204
1228
  if (__privateGet(this, _quirksEnabled) && Object.keys(computedValue.quirks || {}).length) {
1205
1229
  const quirkCookieValue = serializeQuirks(computedValue.quirks || {});
1206
1230
  import_js_cookie.default.set(__privateGet(this, _quirkCookieName), quirkCookieValue, __privateGet(this, _cookieAttributes));
@@ -1217,6 +1241,7 @@ _cookieName = new WeakMap();
1217
1241
  _quirkCookieName = new WeakMap();
1218
1242
  _cookieAttributes = new WeakMap();
1219
1243
  _quirksEnabled = new WeakMap();
1244
+ _maxBytes = new WeakMap();
1220
1245
  var TYPE_SEP = "~";
1221
1246
  var PAIR_SEP = "!";
1222
1247
  var KV_SEP = "-";
@@ -1288,18 +1313,71 @@ function decodeCookieType(type) {
1288
1313
  return acc;
1289
1314
  }, {});
1290
1315
  }
1291
- function serializeCookie(data) {
1316
+ function serializeCookie(data, options = {}) {
1317
+ const { maxBytes, cookieName = UNIFORM_DEFAULT_COOKIE_NAME, onBudgetExceeded } = options;
1318
+ const parts = {
1319
+ tests: { ...data.tests },
1320
+ sessionScores: encodeCookieType(data.sessionScores),
1321
+ scores: encodeCookieType(data.scores),
1322
+ controlGroup: data.controlGroup ? "1" : "0",
1323
+ personalizeVariants: encodePersonalizeVariants(data.personalizeVariants)
1324
+ };
1325
+ let value = serializeCookieParts(parts);
1326
+ if (maxBytes === void 0) {
1327
+ return value;
1328
+ }
1329
+ const overhead = encodeCookieName(cookieName).length + 1;
1330
+ const measure = () => overhead + import_js_cookie.default.converter.write(value, cookieName).length;
1331
+ if (measure() <= maxBytes) {
1332
+ return value;
1333
+ }
1334
+ const candidates = dropCandidates(parts, data);
1335
+ const dropped = [];
1336
+ for (let i = 0; i < candidates.length && measure() > maxBytes; i++) {
1337
+ const { bucket, key } = candidates[i];
1338
+ delete parts[bucket][key];
1339
+ dropped.push(`${bucket}.${key}`);
1340
+ value = serializeCookieParts(parts);
1341
+ }
1342
+ if (dropped.length) {
1343
+ onBudgetExceeded == null ? void 0 : onBudgetExceeded(dropped, measure(), maxBytes);
1344
+ }
1345
+ return value;
1346
+ }
1347
+ function serializeCookieParts(parts) {
1292
1348
  return [
1293
- serializeCookieType(data.tests),
1294
- serializeCookieType(encodeCookieType(data.sessionScores)),
1295
- serializeCookieType(encodeCookieType(data.scores)),
1296
- data.controlGroup ? "1" : "0",
1297
- serializePersonalizeVariants(data)
1349
+ serializeCookieType(parts.tests),
1350
+ serializeCookieType(parts.sessionScores),
1351
+ serializeCookieType(parts.scores),
1352
+ parts.controlGroup,
1353
+ serializeCookieType(parts.personalizeVariants)
1298
1354
  ].join(TYPE_SEP);
1299
1355
  }
1356
+ function dropCandidates(parts, data) {
1357
+ const dimensions = ["sessionScores", "scores"].flatMap(
1358
+ (bucket, order) => Object.keys(parts[bucket]).map((key) => {
1359
+ var _a;
1360
+ return { bucket, key, order, score: (_a = data[bucket][key]) != null ? _a : 0 };
1361
+ })
1362
+ );
1363
+ dimensions.sort(
1364
+ (a, b) => Number(isZeroCookieScore(b.score)) - Number(isZeroCookieScore(a.score)) || a.score - b.score || a.order - b.order || (a.key < b.key ? -1 : 1)
1365
+ );
1366
+ const keysOf = (bucket) => Object.keys(parts[bucket]).sort().map((key) => ({ bucket, key }));
1367
+ return [...dimensions, ...keysOf("personalizeVariants"), ...keysOf("tests")];
1368
+ }
1369
+ function minimumCookieBytes(cookieName) {
1370
+ return encodeCookieName(cookieName).length + 6;
1371
+ }
1372
+ function encodeCookieName(name) {
1373
+ return encodeURIComponent(name).replace(/%(2[346B]|5E|60|7C)/g, decodeURIComponent).replace(/\(/g, "%28").replace(/\)/g, "%29");
1374
+ }
1300
1375
  function serializePersonalizeVariants({
1301
1376
  personalizeVariants
1302
1377
  }) {
1378
+ return serializeCookieType(encodePersonalizeVariants(personalizeVariants));
1379
+ }
1380
+ function encodePersonalizeVariants(personalizeVariants) {
1303
1381
  const data = {};
1304
1382
  if (typeof personalizeVariants === "object") {
1305
1383
  Object.keys(personalizeVariants).forEach((personalizationName) => {
@@ -1311,8 +1389,7 @@ function serializePersonalizeVariants({
1311
1389
  data[personalizationName] = results.join(",");
1312
1390
  });
1313
1391
  }
1314
- const serialized = serializeCookieType(data);
1315
- return serialized;
1392
+ return data;
1316
1393
  }
1317
1394
  function decodePersonalizeVariants(data) {
1318
1395
  const parsed = parseCookieType(data);
@@ -1385,6 +1462,9 @@ fetchData_fn = async function() {
1385
1462
  }
1386
1463
  };
1387
1464
 
1465
+ // src/storage/util/systemDimension.ts
1466
+ var isSystemDimension = (dimension) => dimension.startsWith("$");
1467
+
1388
1468
  // src/storage/linearDecay.ts
1389
1469
  function createLinearDecay(options) {
1390
1470
  const { gracePeriod = 864e5, decayRate = 1 / 30, decayCap = 0.95 } = options != null ? options : {};
@@ -1410,7 +1490,7 @@ function createLinearDecay(options) {
1410
1490
  }
1411
1491
  function decayVector(vector, decay) {
1412
1492
  for (const key in vector) {
1413
- if (key.startsWith("$")) {
1493
+ if (isSystemDimension(key)) {
1414
1494
  continue;
1415
1495
  }
1416
1496
  vector[key] *= decay;
@@ -1554,7 +1634,7 @@ key_fn = function(key) {
1554
1634
 
1555
1635
  // src/storage/VisitorDataStore.ts
1556
1636
  var STORAGE_KEY = "ufvisitor";
1557
- var _mitt2, _persist, _visitTimeout, _options, _VisitorDataStore_instances, currentData_get, replaceData_fn, setVisitTimeout_fn, isExpired_fn, handleCaps_fn, defaultData_fn;
1637
+ var _mitt2, _persist, _visitTimeout, _options, _VisitorDataStore_instances, currentData_get, replaceData_fn, setVisitTimeout_fn, isExpired_fn, handleCaps_fn, pruningManifest_get, handlePruning_fn, pruneScores_fn, defaultData_fn;
1558
1638
  var VisitorDataStore = class {
1559
1639
  constructor(options) {
1560
1640
  __privateAdd(this, _VisitorDataStore_instances);
@@ -1672,6 +1752,7 @@ replaceData_fn = function(data, quiet = false) {
1672
1752
  sessionScores: data.sessionScores,
1673
1753
  onLogMessage: __privateGet(this, _options).onLogMessage
1674
1754
  });
1755
+ __privateMethod(this, _VisitorDataStore_instances, handlePruning_fn).call(this, data);
1675
1756
  const haveScoresChanged = !(0, import_lite4.dequal)(oldData == null ? void 0 : oldData.visitorData.scores, data.scores);
1676
1757
  const haveSessionScoresChanged = !(0, import_lite4.dequal)(oldData == null ? void 0 : oldData.visitorData.sessionScores, data.sessionScores);
1677
1758
  const haveQuirksChanged = !(0, import_lite4.dequal)(oldData == null ? void 0 : oldData.visitorData.quirks, data.quirks);
@@ -1749,6 +1830,58 @@ handleCaps_fn = function(scores) {
1749
1830
  }
1750
1831
  }
1751
1832
  };
1833
+ pruningManifest_get = function() {
1834
+ var _a, _b, _c, _d, _e;
1835
+ const project = (_b = (_a = __privateGet(this, _options).manifest) == null ? void 0 : _a.data) == null ? void 0 : _b.project;
1836
+ const populated = [(_c = project == null ? void 0 : project.pz) == null ? void 0 : _c.sig, (_d = project == null ? void 0 : project.pz) == null ? void 0 : _d.enr, (_e = project == null ? void 0 : project.pz) == null ? void 0 : _e.agg, project == null ? void 0 : project.test].some(
1837
+ (definitions) => definitions && Object.keys(definitions).length > 0
1838
+ );
1839
+ return populated ? __privateGet(this, _options).manifest : void 0;
1840
+ };
1841
+ /**
1842
+ * IMPORTANT: This function mutates the input data, as #replaceData does.
1843
+ *
1844
+ * Removes stored keys that the host has opted to stop carrying. The two manifest-driven
1845
+ * prunes are skipped entirely when no populated manifest is loaded, so a partial load destroys
1846
+ * nothing; zero-score pruning consults no manifest and always runs when enabled.
1847
+ */
1848
+ handlePruning_fn = function(data) {
1849
+ var _a, _b;
1850
+ const { pruneUnknownTests, pruneUnknownDimensions } = __privateGet(this, _options);
1851
+ const manifest = pruneUnknownTests || pruneUnknownDimensions ? __privateGet(this, _VisitorDataStore_instances, pruningManifest_get) : void 0;
1852
+ const pruned = [];
1853
+ if (pruneUnknownTests && manifest) {
1854
+ for (const test in data.tests) {
1855
+ if (!manifest.getTest(test)) {
1856
+ delete data.tests[test];
1857
+ pruned.push(`tests.${test}`);
1858
+ }
1859
+ }
1860
+ }
1861
+ __privateMethod(this, _VisitorDataStore_instances, pruneScores_fn).call(this, data.scores, "scores", pruned, manifest);
1862
+ __privateMethod(this, _VisitorDataStore_instances, pruneScores_fn).call(this, data.sessionScores, "sessionScores", pruned, manifest);
1863
+ if (pruned.length) {
1864
+ (_b = (_a = __privateGet(this, _options)).onLogMessage) == null ? void 0 : _b.call(_a, ["info", 150, pruned]);
1865
+ }
1866
+ };
1867
+ /**
1868
+ * IMPORTANT: This function mutates the input scores.
1869
+ */
1870
+ pruneScores_fn = function(scores, bucket, pruned, manifest) {
1871
+ const { pruneUnknownDimensions, pruneZeroScores } = __privateGet(this, _options);
1872
+ if (!pruneUnknownDimensions && !pruneZeroScores) {
1873
+ return;
1874
+ }
1875
+ const pruneUnknown = pruneUnknownDimensions && manifest;
1876
+ for (const dim in scores) {
1877
+ const isUnknown = pruneUnknown && !isSystemDimension(dim) && !manifest.getDimensionByKey(dim);
1878
+ const isZero = pruneZeroScores && (pruneZeroScores === "round" ? isZeroCookieScore(scores[dim]) : scores[dim] === 0);
1879
+ if (isUnknown || isZero) {
1880
+ delete scores[dim];
1881
+ pruned.push(`${bucket}.${dim}`);
1882
+ }
1883
+ }
1884
+ };
1752
1885
  defaultData_fn = function() {
1753
1886
  var _a, _b, _c;
1754
1887
  return {
@@ -2480,6 +2613,13 @@ var messageContent = {
2480
2613
  ],
2481
2614
  131: () => ["storage", "server to client transition data was discarded"],
2482
2615
  140: (decayMessage) => ["storage", `score decay was applied: ${decayMessage}`],
2616
+ 150: (keys) => ["storage", `pruned from stored visitor data: ${keys.join(", ")}`],
2617
+ 151: ({ dropped, bytes, maxBytes }) => [
2618
+ "storage",
2619
+ `transition cookie exceeded its ${maxBytes} byte budget; dropped ${dropped.join(
2620
+ ", "
2621
+ )} to reach ${bytes} bytes`
2622
+ ],
2483
2623
  // SIGNAL EVAL
2484
2624
  200: () => ["signals", "evaluating signals"],
2485
2625
  201: (signal) => ["signals", `evaluating signal ${signal.id} (${signal.dur})`],
package/dist/index.mjs CHANGED
@@ -1074,11 +1074,14 @@ var bton = (base64) => {
1074
1074
  return sign ? -number : number;
1075
1075
  };
1076
1076
 
1077
+ // src/storage/util/zeroScore.ts
1078
+ var isZeroCookieScore = (score) => Math.abs(score) < 1;
1079
+
1077
1080
  // src/storage/CookieTransitionDataStore.ts
1078
1081
  var ssr = typeof document === "undefined";
1079
1082
  var UNIFORM_DEFAULT_COOKIE_NAME = "ufvd";
1080
1083
  var UNIFORM_DEFAULT_QUIRK_COOKIE_NAME = `ufvdqk`;
1081
- var _cookieName, _quirkCookieName, _cookieAttributes, _quirksEnabled;
1084
+ var _cookieName, _quirkCookieName, _cookieAttributes, _quirksEnabled, _maxBytes;
1082
1085
  var CookieTransitionDataStore = class extends TransitionDataStore {
1083
1086
  constructor({
1084
1087
  serverCookieValue,
@@ -1086,7 +1089,9 @@ var CookieTransitionDataStore = class extends TransitionDataStore {
1086
1089
  quirkCookieValue,
1087
1090
  quirkCookieName = UNIFORM_DEFAULT_QUIRK_COOKIE_NAME,
1088
1091
  cookieName = UNIFORM_DEFAULT_COOKIE_NAME,
1089
- cookieAttributes = { sameSite: "lax" }
1092
+ cookieAttributes = { sameSite: "lax" },
1093
+ maxBytes,
1094
+ onLogMessage
1090
1095
  }) {
1091
1096
  super({
1092
1097
  initialData: ssr ? parseScoreCookie(serverCookieValue, experimental_quirksEnabled ? quirkCookieValue : void 0) : void 0
@@ -1095,10 +1100,21 @@ var CookieTransitionDataStore = class extends TransitionDataStore {
1095
1100
  __privateAdd(this, _quirkCookieName);
1096
1101
  __privateAdd(this, _cookieAttributes);
1097
1102
  __privateAdd(this, _quirksEnabled);
1103
+ __privateAdd(this, _maxBytes);
1104
+ /** Where the store's log messages go. See {@link CookieTransitionDataStoreOptions.onLogMessage}. */
1105
+ __publicField(this, "onLogMessage");
1098
1106
  __privateSet(this, _cookieName, cookieName);
1099
1107
  __privateSet(this, _cookieAttributes, cookieAttributes);
1100
1108
  __privateSet(this, _quirkCookieName, quirkCookieName);
1101
1109
  __privateSet(this, _quirksEnabled, experimental_quirksEnabled || false);
1110
+ __privateSet(this, _maxBytes, maxBytes);
1111
+ this.onLogMessage = onLogMessage;
1112
+ const minimumBytes = minimumCookieBytes(cookieName);
1113
+ if (maxBytes !== void 0 && maxBytes < minimumBytes) {
1114
+ throw new Error(
1115
+ `maxBytes of ${maxBytes} is below the ${minimumBytes} bytes the "${cookieName}" cookie costs when it carries nothing but control group membership, so no visitor could ever meet it.`
1116
+ );
1117
+ }
1102
1118
  }
1103
1119
  handleDelete() {
1104
1120
  if (ssr) return Promise.resolve();
@@ -1108,7 +1124,15 @@ var CookieTransitionDataStore = class extends TransitionDataStore {
1108
1124
  async handleUpdateData(_, computedValue) {
1109
1125
  if (ssr) return;
1110
1126
  if (computedValue.consent) {
1111
- Cookies.set(__privateGet(this, _cookieName), serializeCookie(computedValue), __privateGet(this, _cookieAttributes));
1127
+ const cookieValue = serializeCookie(computedValue, {
1128
+ maxBytes: __privateGet(this, _maxBytes),
1129
+ cookieName: __privateGet(this, _cookieName),
1130
+ onBudgetExceeded: (dropped, bytes, maxBytes) => {
1131
+ var _a;
1132
+ return (_a = this.onLogMessage) == null ? void 0 : _a.call(this, ["info", 151, { dropped, bytes, maxBytes }]);
1133
+ }
1134
+ });
1135
+ Cookies.set(__privateGet(this, _cookieName), cookieValue, __privateGet(this, _cookieAttributes));
1112
1136
  if (__privateGet(this, _quirksEnabled) && Object.keys(computedValue.quirks || {}).length) {
1113
1137
  const quirkCookieValue = serializeQuirks(computedValue.quirks || {});
1114
1138
  Cookies.set(__privateGet(this, _quirkCookieName), quirkCookieValue, __privateGet(this, _cookieAttributes));
@@ -1125,6 +1149,7 @@ _cookieName = new WeakMap();
1125
1149
  _quirkCookieName = new WeakMap();
1126
1150
  _cookieAttributes = new WeakMap();
1127
1151
  _quirksEnabled = new WeakMap();
1152
+ _maxBytes = new WeakMap();
1128
1153
  var TYPE_SEP = "~";
1129
1154
  var PAIR_SEP = "!";
1130
1155
  var KV_SEP = "-";
@@ -1196,18 +1221,71 @@ function decodeCookieType(type) {
1196
1221
  return acc;
1197
1222
  }, {});
1198
1223
  }
1199
- function serializeCookie(data) {
1224
+ function serializeCookie(data, options = {}) {
1225
+ const { maxBytes, cookieName = UNIFORM_DEFAULT_COOKIE_NAME, onBudgetExceeded } = options;
1226
+ const parts = {
1227
+ tests: { ...data.tests },
1228
+ sessionScores: encodeCookieType(data.sessionScores),
1229
+ scores: encodeCookieType(data.scores),
1230
+ controlGroup: data.controlGroup ? "1" : "0",
1231
+ personalizeVariants: encodePersonalizeVariants(data.personalizeVariants)
1232
+ };
1233
+ let value = serializeCookieParts(parts);
1234
+ if (maxBytes === void 0) {
1235
+ return value;
1236
+ }
1237
+ const overhead = encodeCookieName(cookieName).length + 1;
1238
+ const measure = () => overhead + Cookies.converter.write(value, cookieName).length;
1239
+ if (measure() <= maxBytes) {
1240
+ return value;
1241
+ }
1242
+ const candidates = dropCandidates(parts, data);
1243
+ const dropped = [];
1244
+ for (let i = 0; i < candidates.length && measure() > maxBytes; i++) {
1245
+ const { bucket, key } = candidates[i];
1246
+ delete parts[bucket][key];
1247
+ dropped.push(`${bucket}.${key}`);
1248
+ value = serializeCookieParts(parts);
1249
+ }
1250
+ if (dropped.length) {
1251
+ onBudgetExceeded == null ? void 0 : onBudgetExceeded(dropped, measure(), maxBytes);
1252
+ }
1253
+ return value;
1254
+ }
1255
+ function serializeCookieParts(parts) {
1200
1256
  return [
1201
- serializeCookieType(data.tests),
1202
- serializeCookieType(encodeCookieType(data.sessionScores)),
1203
- serializeCookieType(encodeCookieType(data.scores)),
1204
- data.controlGroup ? "1" : "0",
1205
- serializePersonalizeVariants(data)
1257
+ serializeCookieType(parts.tests),
1258
+ serializeCookieType(parts.sessionScores),
1259
+ serializeCookieType(parts.scores),
1260
+ parts.controlGroup,
1261
+ serializeCookieType(parts.personalizeVariants)
1206
1262
  ].join(TYPE_SEP);
1207
1263
  }
1264
+ function dropCandidates(parts, data) {
1265
+ const dimensions = ["sessionScores", "scores"].flatMap(
1266
+ (bucket, order) => Object.keys(parts[bucket]).map((key) => {
1267
+ var _a;
1268
+ return { bucket, key, order, score: (_a = data[bucket][key]) != null ? _a : 0 };
1269
+ })
1270
+ );
1271
+ dimensions.sort(
1272
+ (a, b) => Number(isZeroCookieScore(b.score)) - Number(isZeroCookieScore(a.score)) || a.score - b.score || a.order - b.order || (a.key < b.key ? -1 : 1)
1273
+ );
1274
+ const keysOf = (bucket) => Object.keys(parts[bucket]).sort().map((key) => ({ bucket, key }));
1275
+ return [...dimensions, ...keysOf("personalizeVariants"), ...keysOf("tests")];
1276
+ }
1277
+ function minimumCookieBytes(cookieName) {
1278
+ return encodeCookieName(cookieName).length + 6;
1279
+ }
1280
+ function encodeCookieName(name) {
1281
+ return encodeURIComponent(name).replace(/%(2[346B]|5E|60|7C)/g, decodeURIComponent).replace(/\(/g, "%28").replace(/\)/g, "%29");
1282
+ }
1208
1283
  function serializePersonalizeVariants({
1209
1284
  personalizeVariants
1210
1285
  }) {
1286
+ return serializeCookieType(encodePersonalizeVariants(personalizeVariants));
1287
+ }
1288
+ function encodePersonalizeVariants(personalizeVariants) {
1211
1289
  const data = {};
1212
1290
  if (typeof personalizeVariants === "object") {
1213
1291
  Object.keys(personalizeVariants).forEach((personalizationName) => {
@@ -1219,8 +1297,7 @@ function serializePersonalizeVariants({
1219
1297
  data[personalizationName] = results.join(",");
1220
1298
  });
1221
1299
  }
1222
- const serialized = serializeCookieType(data);
1223
- return serialized;
1300
+ return data;
1224
1301
  }
1225
1302
  function decodePersonalizeVariants(data) {
1226
1303
  const parsed = parseCookieType(data);
@@ -1293,6 +1370,9 @@ fetchData_fn = async function() {
1293
1370
  }
1294
1371
  };
1295
1372
 
1373
+ // src/storage/util/systemDimension.ts
1374
+ var isSystemDimension = (dimension) => dimension.startsWith("$");
1375
+
1296
1376
  // src/storage/linearDecay.ts
1297
1377
  function createLinearDecay(options) {
1298
1378
  const { gracePeriod = 864e5, decayRate = 1 / 30, decayCap = 0.95 } = options != null ? options : {};
@@ -1318,7 +1398,7 @@ function createLinearDecay(options) {
1318
1398
  }
1319
1399
  function decayVector(vector, decay) {
1320
1400
  for (const key in vector) {
1321
- if (key.startsWith("$")) {
1401
+ if (isSystemDimension(key)) {
1322
1402
  continue;
1323
1403
  }
1324
1404
  vector[key] *= decay;
@@ -1462,7 +1542,7 @@ key_fn = function(key) {
1462
1542
 
1463
1543
  // src/storage/VisitorDataStore.ts
1464
1544
  var STORAGE_KEY = "ufvisitor";
1465
- var _mitt2, _persist, _visitTimeout, _options, _VisitorDataStore_instances, currentData_get, replaceData_fn, setVisitTimeout_fn, isExpired_fn, handleCaps_fn, defaultData_fn;
1545
+ var _mitt2, _persist, _visitTimeout, _options, _VisitorDataStore_instances, currentData_get, replaceData_fn, setVisitTimeout_fn, isExpired_fn, handleCaps_fn, pruningManifest_get, handlePruning_fn, pruneScores_fn, defaultData_fn;
1466
1546
  var VisitorDataStore = class {
1467
1547
  constructor(options) {
1468
1548
  __privateAdd(this, _VisitorDataStore_instances);
@@ -1580,6 +1660,7 @@ replaceData_fn = function(data, quiet = false) {
1580
1660
  sessionScores: data.sessionScores,
1581
1661
  onLogMessage: __privateGet(this, _options).onLogMessage
1582
1662
  });
1663
+ __privateMethod(this, _VisitorDataStore_instances, handlePruning_fn).call(this, data);
1583
1664
  const haveScoresChanged = !dequal4(oldData == null ? void 0 : oldData.visitorData.scores, data.scores);
1584
1665
  const haveSessionScoresChanged = !dequal4(oldData == null ? void 0 : oldData.visitorData.sessionScores, data.sessionScores);
1585
1666
  const haveQuirksChanged = !dequal4(oldData == null ? void 0 : oldData.visitorData.quirks, data.quirks);
@@ -1657,6 +1738,58 @@ handleCaps_fn = function(scores) {
1657
1738
  }
1658
1739
  }
1659
1740
  };
1741
+ pruningManifest_get = function() {
1742
+ var _a, _b, _c, _d, _e;
1743
+ const project = (_b = (_a = __privateGet(this, _options).manifest) == null ? void 0 : _a.data) == null ? void 0 : _b.project;
1744
+ const populated = [(_c = project == null ? void 0 : project.pz) == null ? void 0 : _c.sig, (_d = project == null ? void 0 : project.pz) == null ? void 0 : _d.enr, (_e = project == null ? void 0 : project.pz) == null ? void 0 : _e.agg, project == null ? void 0 : project.test].some(
1745
+ (definitions) => definitions && Object.keys(definitions).length > 0
1746
+ );
1747
+ return populated ? __privateGet(this, _options).manifest : void 0;
1748
+ };
1749
+ /**
1750
+ * IMPORTANT: This function mutates the input data, as #replaceData does.
1751
+ *
1752
+ * Removes stored keys that the host has opted to stop carrying. The two manifest-driven
1753
+ * prunes are skipped entirely when no populated manifest is loaded, so a partial load destroys
1754
+ * nothing; zero-score pruning consults no manifest and always runs when enabled.
1755
+ */
1756
+ handlePruning_fn = function(data) {
1757
+ var _a, _b;
1758
+ const { pruneUnknownTests, pruneUnknownDimensions } = __privateGet(this, _options);
1759
+ const manifest = pruneUnknownTests || pruneUnknownDimensions ? __privateGet(this, _VisitorDataStore_instances, pruningManifest_get) : void 0;
1760
+ const pruned = [];
1761
+ if (pruneUnknownTests && manifest) {
1762
+ for (const test in data.tests) {
1763
+ if (!manifest.getTest(test)) {
1764
+ delete data.tests[test];
1765
+ pruned.push(`tests.${test}`);
1766
+ }
1767
+ }
1768
+ }
1769
+ __privateMethod(this, _VisitorDataStore_instances, pruneScores_fn).call(this, data.scores, "scores", pruned, manifest);
1770
+ __privateMethod(this, _VisitorDataStore_instances, pruneScores_fn).call(this, data.sessionScores, "sessionScores", pruned, manifest);
1771
+ if (pruned.length) {
1772
+ (_b = (_a = __privateGet(this, _options)).onLogMessage) == null ? void 0 : _b.call(_a, ["info", 150, pruned]);
1773
+ }
1774
+ };
1775
+ /**
1776
+ * IMPORTANT: This function mutates the input scores.
1777
+ */
1778
+ pruneScores_fn = function(scores, bucket, pruned, manifest) {
1779
+ const { pruneUnknownDimensions, pruneZeroScores } = __privateGet(this, _options);
1780
+ if (!pruneUnknownDimensions && !pruneZeroScores) {
1781
+ return;
1782
+ }
1783
+ const pruneUnknown = pruneUnknownDimensions && manifest;
1784
+ for (const dim in scores) {
1785
+ const isUnknown = pruneUnknown && !isSystemDimension(dim) && !manifest.getDimensionByKey(dim);
1786
+ const isZero = pruneZeroScores && (pruneZeroScores === "round" ? isZeroCookieScore(scores[dim]) : scores[dim] === 0);
1787
+ if (isUnknown || isZero) {
1788
+ delete scores[dim];
1789
+ pruned.push(`${bucket}.${dim}`);
1790
+ }
1791
+ }
1792
+ };
1660
1793
  defaultData_fn = function() {
1661
1794
  var _a, _b, _c;
1662
1795
  return {
@@ -2388,6 +2521,13 @@ var messageContent = {
2388
2521
  ],
2389
2522
  131: () => ["storage", "server to client transition data was discarded"],
2390
2523
  140: (decayMessage) => ["storage", `score decay was applied: ${decayMessage}`],
2524
+ 150: (keys) => ["storage", `pruned from stored visitor data: ${keys.join(", ")}`],
2525
+ 151: ({ dropped, bytes, maxBytes }) => [
2526
+ "storage",
2527
+ `transition cookie exceeded its ${maxBytes} byte budget; dropped ${dropped.join(
2528
+ ", "
2529
+ )} to reach ${bytes} bytes`
2530
+ ],
2391
2531
  // SIGNAL EVAL
2392
2532
  200: () => ["signals", "evaluating signals"],
2393
2533
  201: (signal) => ["signals", `evaluating signal ${signal.id} (${signal.dur})`],
@@ -666,6 +666,41 @@ type VisitorDataStoreOptions = {
666
666
  * Only required if using multiple Context instances on the same domain and need to separate them.
667
667
  */
668
668
  partitionKey?: string;
669
+ /**
670
+ * Removes stored A/B test assignments for tests that are no longer in the manifest.
671
+ * Does nothing until a manifest with contents is loaded - an empty manifest is read as a
672
+ * manifest that did not load, not as a project that deleted everything - so a partial or
673
+ * failed load cannot destroy anything. Pruning is irreversible.
674
+ *
675
+ * @defaultValue false
676
+ */
677
+ pruneUnknownTests?: boolean;
678
+ /**
679
+ * Removes stored dimensions that are no longer in the manifest - a signal whose key is gone,
680
+ * or an enrichment whose category is gone. Dimensions Uniform writes itself, whose keys begin
681
+ * with `$`, are kept: they are never in the manifest.
682
+ * Does nothing until a manifest with contents is loaded - an empty manifest is read as a
683
+ * manifest that did not load, not as a project that deleted everything - so a partial or
684
+ * failed load cannot destroy anything. Pruning is irreversible.
685
+ *
686
+ * @defaultValue false
687
+ */
688
+ pruneUnknownDimensions?: boolean;
689
+ /**
690
+ * Removes stored dimensions whose score is zero. Pruning is irreversible.
691
+ *
692
+ * `true` removes a score of exactly zero. A decayed 0.6 is real data on the client, so it stays.
693
+ *
694
+ * `'round'` also removes any score whose magnitude is below 1, negatives included - every score
695
+ * the transition cookie would write as zero - so the cookie and local storage hold the same keys.
696
+ *
697
+ * Note that the weakest-dimension personalization operator is affected by pruning zeros, since a
698
+ * pruned dimension is no longer a candidate for it. Every other operator reads an absent
699
+ * dimension as zero, which is what it was.
700
+ *
701
+ * @defaultValue false
702
+ */
703
+ pruneZeroScores?: boolean | 'round';
669
704
  };
670
705
  type VisitorDataStoreEvents = {
671
706
  /**
@@ -846,6 +881,14 @@ type LogMessages = {
846
881
  131: MessageFunc;
847
882
  /** Decay function executed */
848
883
  140: MessageFunc<string>;
884
+ /** Keys were pruned from the stored visitor data */
885
+ 150: MessageFunc<string[]>;
886
+ /** Transition cookie did not fit its byte budget, so data was dropped from it */
887
+ 151: MessageFunc<{
888
+ dropped: string[];
889
+ bytes: number;
890
+ maxBytes: number;
891
+ }>;
849
892
  /** Signals evaluation beginning */
850
893
  200: MessageFunc;
851
894
  /** Evaluation of a specific signal beginning */
@@ -1396,4 +1439,4 @@ declare global {
1396
1439
  }
1397
1440
  }
1398
1441
 
1399
- export { GroupCriteriaEvaluator as $, type AggregateDimension as A, type BehaviorTag as B, type ContextPlugin as C, type DecayFunction as D, type DevToolsDataEvent as E, type DevToolsEvent as F, type DevToolsForgetEvent as G, type DevToolsHelloEvent as H, type DevToolsLogEvent as I, type DevToolsRawCommandsEvent as J, type DevToolsState as K, type LogDrain as L, type ManifestV2 as M, type DevToolsUiVersion as N, type OutputSeverity as O, type PersonalizedVariant as P, type Quirks as Q, type DevToolsUpdateEvent as R, type ScoreVector as S, TransitionDataStore as T, type DimensionMatch as U, type VisitorData as V, type EnrichmentCategory as W, type EnrichmentData as X, type EventData as Y, type GoalStateUpdate as Z, type Goals as _, type StorageCommands as a, type IdentifyCommand as a0, type LogMessageGroup as a1, type LogMessageSingle as a2, type LogMessages as a3, ManifestInstance as a4, type MessageCategory as a5, type MessageFunc as a6, type ModifyScoreCommand as a7, type ModifySessionScoreCommand as a8, type NumberMatch as a9, type TestVariant as aA, type Tests as aB, type TransitionDataStoreEvents as aC, type VariationMatchMetadata as aD, VisitorDataStore as aE, type VisitorDataStoreEvents as aF, type VisitorDataStoreOptions as aG, areCommandsEqual as aH, emptyVisitorData as aI, testVariations as aJ, type PersonalizationEvent as aa, type PersonalizationEventVariantId as ab, type PersonalizationManifest as ac, type PersonalizationSelectionAlgorithm as ad, type PersonalizationSelectionAlgorithms as ae, type PersonalizeControlVariant as af, type PersonalizeVariants as ag, type QuirkMatch as ah, SERVER_STATE_ID as ai, type ServerToClientTransitionState as aj, type SetConsentCommand as ak, type SetControlGroupCommand as al, type SetGoalCommand as am, type SetPersonalizeVariantControlCommand as an, type SetQuirkCommand as ao, type SetTestCommand as ap, type Severity as aq, type Signal as ar, type SignalCriteria as as, type SignalCriteriaGroup as at, type SignalData as au, type StorageCommand as av, type TestDefinition as aw, type TestEvent as ax, type TestOptions as ay, type TestResult as az, type TransitionDataStoreOptions as b, type CriteriaEvaluator as c, type StringMatch as d, type VariantMatchCriteria as e, type LogMessage as f, type PersonalizeOptions as g, Context as h, type PersonalizedResult as i, type VariationMatchDimensionCriteria as j, type PersonalizationSelectionAlgorithmOptions as k, type DevToolsEvents as l, type AggregateDimensionInput as m, CONTEXTUAL_EDITING_TEST_NAME as n, CONTEXTUAL_EDITING_TEST_SELECTED_VARIANT_ID as o, type paths as p, type CompositionMetadata as q, type ContextEvents as r, type ContextInstance as s, type ContextOptions as t, type ContextState as u, type ContextStateUpdate as v, type CriteriaEvaluatorParameters as w, type CriteriaEvaluatorResult as x, type DecayOptions as y, type DevToolsActions as z };
1442
+ export { GroupCriteriaEvaluator as $, type AggregateDimension as A, type BehaviorTag as B, type ContextPlugin as C, type DecayFunction as D, type DevToolsDataEvent as E, type DevToolsEvent as F, type DevToolsForgetEvent as G, type DevToolsHelloEvent as H, type DevToolsLogEvent as I, type DevToolsRawCommandsEvent as J, type DevToolsState as K, type LogDrain as L, type ManifestV2 as M, type DevToolsUiVersion as N, type OutputSeverity as O, type PersonalizedVariant as P, type Quirks as Q, type DevToolsUpdateEvent as R, type ScoreVector as S, TransitionDataStore as T, type DimensionMatch as U, type VisitorData as V, type EnrichmentCategory as W, type EnrichmentData as X, type EventData as Y, type GoalStateUpdate as Z, type Goals as _, type LogMessage as a, type IdentifyCommand as a0, type LogMessageGroup as a1, type LogMessageSingle as a2, type LogMessages as a3, ManifestInstance as a4, type MessageCategory as a5, type MessageFunc as a6, type ModifyScoreCommand as a7, type ModifySessionScoreCommand as a8, type NumberMatch as a9, type TestVariant as aA, type Tests as aB, type TransitionDataStoreEvents as aC, type VariationMatchMetadata as aD, VisitorDataStore as aE, type VisitorDataStoreEvents as aF, type VisitorDataStoreOptions as aG, areCommandsEqual as aH, emptyVisitorData as aI, testVariations as aJ, type PersonalizationEvent as aa, type PersonalizationEventVariantId as ab, type PersonalizationManifest as ac, type PersonalizationSelectionAlgorithm as ad, type PersonalizationSelectionAlgorithms as ae, type PersonalizeControlVariant as af, type PersonalizeVariants as ag, type QuirkMatch as ah, SERVER_STATE_ID as ai, type ServerToClientTransitionState as aj, type SetConsentCommand as ak, type SetControlGroupCommand as al, type SetGoalCommand as am, type SetPersonalizeVariantControlCommand as an, type SetQuirkCommand as ao, type SetTestCommand as ap, type Severity as aq, type Signal as ar, type SignalCriteria as as, type SignalCriteriaGroup as at, type SignalData as au, type StorageCommand as av, type TestDefinition as aw, type TestEvent as ax, type TestOptions as ay, type TestResult as az, type StorageCommands as b, type TransitionDataStoreOptions as c, type CriteriaEvaluator as d, type StringMatch as e, type VariantMatchCriteria as f, type PersonalizeOptions as g, Context as h, type PersonalizedResult as i, type VariationMatchDimensionCriteria as j, type PersonalizationSelectionAlgorithmOptions as k, type DevToolsEvents as l, type AggregateDimensionInput as m, CONTEXTUAL_EDITING_TEST_NAME as n, CONTEXTUAL_EDITING_TEST_SELECTED_VARIANT_ID as o, type paths as p, type CompositionMetadata as q, type ContextEvents as r, type ContextInstance as s, type ContextOptions as t, type ContextState as u, type ContextStateUpdate as v, type CriteriaEvaluatorParameters as w, type CriteriaEvaluatorResult as x, type DecayOptions as y, type DevToolsActions as z };
@@ -666,6 +666,41 @@ type VisitorDataStoreOptions = {
666
666
  * Only required if using multiple Context instances on the same domain and need to separate them.
667
667
  */
668
668
  partitionKey?: string;
669
+ /**
670
+ * Removes stored A/B test assignments for tests that are no longer in the manifest.
671
+ * Does nothing until a manifest with contents is loaded - an empty manifest is read as a
672
+ * manifest that did not load, not as a project that deleted everything - so a partial or
673
+ * failed load cannot destroy anything. Pruning is irreversible.
674
+ *
675
+ * @defaultValue false
676
+ */
677
+ pruneUnknownTests?: boolean;
678
+ /**
679
+ * Removes stored dimensions that are no longer in the manifest - a signal whose key is gone,
680
+ * or an enrichment whose category is gone. Dimensions Uniform writes itself, whose keys begin
681
+ * with `$`, are kept: they are never in the manifest.
682
+ * Does nothing until a manifest with contents is loaded - an empty manifest is read as a
683
+ * manifest that did not load, not as a project that deleted everything - so a partial or
684
+ * failed load cannot destroy anything. Pruning is irreversible.
685
+ *
686
+ * @defaultValue false
687
+ */
688
+ pruneUnknownDimensions?: boolean;
689
+ /**
690
+ * Removes stored dimensions whose score is zero. Pruning is irreversible.
691
+ *
692
+ * `true` removes a score of exactly zero. A decayed 0.6 is real data on the client, so it stays.
693
+ *
694
+ * `'round'` also removes any score whose magnitude is below 1, negatives included - every score
695
+ * the transition cookie would write as zero - so the cookie and local storage hold the same keys.
696
+ *
697
+ * Note that the weakest-dimension personalization operator is affected by pruning zeros, since a
698
+ * pruned dimension is no longer a candidate for it. Every other operator reads an absent
699
+ * dimension as zero, which is what it was.
700
+ *
701
+ * @defaultValue false
702
+ */
703
+ pruneZeroScores?: boolean | 'round';
669
704
  };
670
705
  type VisitorDataStoreEvents = {
671
706
  /**
@@ -846,6 +881,14 @@ type LogMessages = {
846
881
  131: MessageFunc;
847
882
  /** Decay function executed */
848
883
  140: MessageFunc<string>;
884
+ /** Keys were pruned from the stored visitor data */
885
+ 150: MessageFunc<string[]>;
886
+ /** Transition cookie did not fit its byte budget, so data was dropped from it */
887
+ 151: MessageFunc<{
888
+ dropped: string[];
889
+ bytes: number;
890
+ maxBytes: number;
891
+ }>;
849
892
  /** Signals evaluation beginning */
850
893
  200: MessageFunc;
851
894
  /** Evaluation of a specific signal beginning */
@@ -1396,4 +1439,4 @@ declare global {
1396
1439
  }
1397
1440
  }
1398
1441
 
1399
- export { GroupCriteriaEvaluator as $, type AggregateDimension as A, type BehaviorTag as B, type ContextPlugin as C, type DecayFunction as D, type DevToolsDataEvent as E, type DevToolsEvent as F, type DevToolsForgetEvent as G, type DevToolsHelloEvent as H, type DevToolsLogEvent as I, type DevToolsRawCommandsEvent as J, type DevToolsState as K, type LogDrain as L, type ManifestV2 as M, type DevToolsUiVersion as N, type OutputSeverity as O, type PersonalizedVariant as P, type Quirks as Q, type DevToolsUpdateEvent as R, type ScoreVector as S, TransitionDataStore as T, type DimensionMatch as U, type VisitorData as V, type EnrichmentCategory as W, type EnrichmentData as X, type EventData as Y, type GoalStateUpdate as Z, type Goals as _, type StorageCommands as a, type IdentifyCommand as a0, type LogMessageGroup as a1, type LogMessageSingle as a2, type LogMessages as a3, ManifestInstance as a4, type MessageCategory as a5, type MessageFunc as a6, type ModifyScoreCommand as a7, type ModifySessionScoreCommand as a8, type NumberMatch as a9, type TestVariant as aA, type Tests as aB, type TransitionDataStoreEvents as aC, type VariationMatchMetadata as aD, VisitorDataStore as aE, type VisitorDataStoreEvents as aF, type VisitorDataStoreOptions as aG, areCommandsEqual as aH, emptyVisitorData as aI, testVariations as aJ, type PersonalizationEvent as aa, type PersonalizationEventVariantId as ab, type PersonalizationManifest as ac, type PersonalizationSelectionAlgorithm as ad, type PersonalizationSelectionAlgorithms as ae, type PersonalizeControlVariant as af, type PersonalizeVariants as ag, type QuirkMatch as ah, SERVER_STATE_ID as ai, type ServerToClientTransitionState as aj, type SetConsentCommand as ak, type SetControlGroupCommand as al, type SetGoalCommand as am, type SetPersonalizeVariantControlCommand as an, type SetQuirkCommand as ao, type SetTestCommand as ap, type Severity as aq, type Signal as ar, type SignalCriteria as as, type SignalCriteriaGroup as at, type SignalData as au, type StorageCommand as av, type TestDefinition as aw, type TestEvent as ax, type TestOptions as ay, type TestResult as az, type TransitionDataStoreOptions as b, type CriteriaEvaluator as c, type StringMatch as d, type VariantMatchCriteria as e, type LogMessage as f, type PersonalizeOptions as g, Context as h, type PersonalizedResult as i, type VariationMatchDimensionCriteria as j, type PersonalizationSelectionAlgorithmOptions as k, type DevToolsEvents as l, type AggregateDimensionInput as m, CONTEXTUAL_EDITING_TEST_NAME as n, CONTEXTUAL_EDITING_TEST_SELECTED_VARIANT_ID as o, type paths as p, type CompositionMetadata as q, type ContextEvents as r, type ContextInstance as s, type ContextOptions as t, type ContextState as u, type ContextStateUpdate as v, type CriteriaEvaluatorParameters as w, type CriteriaEvaluatorResult as x, type DecayOptions as y, type DevToolsActions as z };
1442
+ export { GroupCriteriaEvaluator as $, type AggregateDimension as A, type BehaviorTag as B, type ContextPlugin as C, type DecayFunction as D, type DevToolsDataEvent as E, type DevToolsEvent as F, type DevToolsForgetEvent as G, type DevToolsHelloEvent as H, type DevToolsLogEvent as I, type DevToolsRawCommandsEvent as J, type DevToolsState as K, type LogDrain as L, type ManifestV2 as M, type DevToolsUiVersion as N, type OutputSeverity as O, type PersonalizedVariant as P, type Quirks as Q, type DevToolsUpdateEvent as R, type ScoreVector as S, TransitionDataStore as T, type DimensionMatch as U, type VisitorData as V, type EnrichmentCategory as W, type EnrichmentData as X, type EventData as Y, type GoalStateUpdate as Z, type Goals as _, type LogMessage as a, type IdentifyCommand as a0, type LogMessageGroup as a1, type LogMessageSingle as a2, type LogMessages as a3, ManifestInstance as a4, type MessageCategory as a5, type MessageFunc as a6, type ModifyScoreCommand as a7, type ModifySessionScoreCommand as a8, type NumberMatch as a9, type TestVariant as aA, type Tests as aB, type TransitionDataStoreEvents as aC, type VariationMatchMetadata as aD, VisitorDataStore as aE, type VisitorDataStoreEvents as aF, type VisitorDataStoreOptions as aG, areCommandsEqual as aH, emptyVisitorData as aI, testVariations as aJ, type PersonalizationEvent as aa, type PersonalizationEventVariantId as ab, type PersonalizationManifest as ac, type PersonalizationSelectionAlgorithm as ad, type PersonalizationSelectionAlgorithms as ae, type PersonalizeControlVariant as af, type PersonalizeVariants as ag, type QuirkMatch as ah, SERVER_STATE_ID as ai, type ServerToClientTransitionState as aj, type SetConsentCommand as ak, type SetControlGroupCommand as al, type SetGoalCommand as am, type SetPersonalizeVariantControlCommand as an, type SetQuirkCommand as ao, type SetTestCommand as ap, type Severity as aq, type Signal as ar, type SignalCriteria as as, type SignalCriteriaGroup as at, type SignalData as au, type StorageCommand as av, type TestDefinition as aw, type TestEvent as ax, type TestOptions as ay, type TestResult as az, type StorageCommands as b, type TransitionDataStoreOptions as c, type CriteriaEvaluator as d, type StringMatch as e, type VariantMatchCriteria as f, type PersonalizeOptions as g, Context as h, type PersonalizedResult as i, type VariationMatchDimensionCriteria as j, type PersonalizationSelectionAlgorithmOptions as k, type DevToolsEvents as l, type AggregateDimensionInput as m, CONTEXTUAL_EDITING_TEST_NAME as n, CONTEXTUAL_EDITING_TEST_SELECTED_VARIANT_ID as o, type paths as p, type CompositionMetadata as q, type ContextEvents as r, type ContextInstance as s, type ContextOptions as t, type ContextState as u, type ContextStateUpdate as v, type CriteriaEvaluatorParameters as w, type CriteriaEvaluatorResult as x, type DecayOptions as y, type DevToolsActions as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniformdev/context",
3
- "version": "20.84.0",
3
+ "version": "20.86.0",
4
4
  "description": "Uniform Context core package",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "main": "./dist/index.js",
@@ -70,5 +70,5 @@
70
70
  "publishConfig": {
71
71
  "access": "public"
72
72
  },
73
- "gitHead": "aed567f635f70f4ba169010c0a1bcaebfe23d0f9"
73
+ "gitHead": "f0e4c0eb0eac24eb1e38a5bd536832f0261b003b"
74
74
  }