From 41dc2629b1415a5b82f8d8c7c811c16a547d5827 Mon Sep 17 00:00:00 2001 From: Andrew Paseltiner Date: Tue, 13 May 2025 11:26:38 -0400 Subject: [PATCH 1/4] Remove incomplete Full Flexible Event algorithms from specification --- index.bs | 149 +++++-------------------------------------------------- 1 file changed, 12 insertions(+), 137 deletions(-) diff --git a/index.bs b/index.bs index e894e7c60..ba3c60425 100644 --- a/index.bs +++ b/index.bs @@ -691,38 +691,6 @@ a [=report window=] [=struct=] with the following fields: [=report windows=], because there are no gaps in time between any of the [=report windows|windows=]. -

Summary operator

- -A summary operator summarizes the triggers attributed to an -[=attribution source=]. Its value is one of the following: - -
-: "count" -:: Number of triggers attributed. -: "value_sum" -:: Sum of the value of triggers. - -
- -

Summary bucket

- -A summary bucket is a [=struct=] with the following items: - -
-: start -:: An unsigned 32-bit integer. -: end -:: An unsigned 32-bit integer. - -
- -A summary bucket list is a [=list=] of [=summary buckets=]. -It has the following constraints: - -* Elements are strictly in ascending order based on their [=summary bucket/start=]. -* Every element's [=summary bucket/end=] is equal to the next element's [=summary bucket/start=] - 1, if it exists. -* There is at least one element in the list. -

Trigger-data matching mode

A trigger-data matching mode is one of the following: @@ -969,8 +937,6 @@ An event-level trigger configuration is a [=struct=] with the following items: :: A [=list=] of [=filter configs=]. : negated filters :: A [=list=] of [=filter configs=]. -: value -:: A positive unsigned 32-bit integer. @@ -2437,11 +2403,8 @@ A source-registration JSON key is one of the following:
  • "priority"
  • "source_event_id"
  • "start_time" -
  • "summary_buckets" -
  • "summary_operator"
  • "trigger_data"
  • "trigger_data_matching" -
  • "trigger_specs"
  • "values" @@ -2594,48 +2557,6 @@ To parse report windows given a |value|, a 1. Set |startDuration| to |endDuration|. 1. Return |windows|. -The user-agent has an associated boolean -experimental Flexible Event support (default false) that exposes -non-normative behavior described in the -Flexible event-level configurations -proposal. - -To parse summary operator given a [=map=] |map|: - -1. Let |value| be "[=summary operator/count=]". -1. If |map|["[=source-registration JSON key/summary_operator=]"] [=map/exists=]: - 1. If |map|["[=source-registration JSON key/summary_operator=]"] is not a [=string=], return an - error. - 1. If |map|["[=source-registration JSON key/summary_operator=]"] is not a - [=summary operator=], return an error. - 1. Set |value| to |map|["[=source-registration JSON key/summary_operator=]"]. -1. Return |value|. - -To parse summary buckets given a [=map=] |map| and an integer |maxEventLevelReports|: - -1. Let |values| be [=the inclusive range|the range=] 1 to - |maxEventLevelReports|, inclusive. -1. If |map|["[=source-registration JSON key/summary_buckets=]"] [=map/exists=]: - 1. If |map|["[=source-registration JSON key/summary_buckets=]"] is not a [=list=], [=list/is empty=], or - its [=list/size=] is greater than |maxEventLevelReports|, return an - error. - 1. Set |values| to |map|["[=source-registration JSON key/summary_buckets=]"]. -1. Let |prev| be 0. -1. Let |summaryBuckets| be a new [=list=]. -1. [=list/iterate|For each=] |item| of |values|: - 1. If |item| is not an integer or cannot be represented by an unsigned - 32-bit integer, or is less than or equal to |prev|, return an error. - 1. Let |summaryBucket| be a new [=summary bucket=] whose items are - - : [=summary bucket/start=] - :: |prev| - : [=summary bucket/end=] - :: |item| - 1 - - 1. [=list/Append=] |summaryBucket| to |summaryBuckets|. - 1. Set |prev| to |item|. -1. Return |summaryBuckets|. - To parse trigger data into a trigger spec map given a |triggerDataList|, a [=trigger spec=] |spec|, a [=trigger spec map=] |specs|, and a [=boolean=] |allowEmpty|: @@ -2662,45 +2583,20 @@ To parse trigger specs given a [=map=] |map|, a [=moment=] and |expiry|. 1. If |defaultReportWindows| is an error, return an error. 1. Let |specs| be a new [=trigger spec map=]. -1. If [=experimental Flexible Event support=] is true and - |map|["[=source-registration JSON key/trigger_specs=]"] [=map/exists=]: - 1. If |map|["[=source-registration JSON key/trigger_data=]"] [=map/exists=], return an error. - 1. If |map|["[=source-registration JSON key/trigger_specs=]"] is not a [=list=] or its - [=list/size=] is greater than [=max distinct trigger data per source=], +1. Let |spec| be a new [=trigger spec=] with the following items: + : [=trigger spec/event-level report windows=] + :: |defaultReportWindows| +1. If |map|["[=source-registration JSON key/trigger_data=]"] [=map/exists=]: + 1. Let |allowEmpty| be true. + 1. If the result of running + [=parse trigger data into a trigger spec map=] with + |map|["[=source-registration JSON key/trigger_data=]"], |spec|, |specs|, and |allowEmpty| is false, return an error. - 1. [=list/iterate|For each=] |item| of |map|["[=source-registration JSON key/trigger_specs=]"]: - 1. If |item| is not a [=map=], return an error. - 1. Let |spec| be a new [=trigger spec=] with the following items: - : [=trigger spec/event-level report windows=] - :: |defaultReportWindows| - 1. If |item|["[=source-registration JSON key/event_report_windows=]"] [=map/exists=]: - 1. Let |reportWindows| be the result of - [=parsing report windows=] with |item|["[=source-registration JSON key/event_report_windows=]"], - |sourceTime|, and |expiry|. - 1. If |reportWindows| is an error, return it. - 1. Set |spec|'s [=trigger spec/event-level report windows=] to - |reportWindows|. - 1. If |item|["[=source-registration JSON key/trigger_data=]"] does not [=map/exist=], return an error. - 1. Let |allowEmpty| be false. - 1. If the result of running - [=parse trigger data into a trigger spec map=] with - |item|["[=source-registration JSON key/trigger_data=]"], |spec|, |specs|, and |allowEmpty| is - false, return an error. 1. Otherwise: - 1. Let |spec| be a new [=trigger spec=] with the following items: - : [=trigger spec/event-level report windows=] - :: |defaultReportWindows| - 1. If |map|["[=source-registration JSON key/trigger_data=]"] [=map/exists=]: - 1. Let |allowEmpty| be true. - 1. If the result of running - [=parse trigger data into a trigger spec map=] with - |map|["[=source-registration JSON key/trigger_data=]"], |spec|, |specs|, and |allowEmpty| is false, - return an error. - 1. Otherwise: - 1. [=set/iterate|For each=] integer |triggerData| of - [=the exclusive range|the range=] 0 to - [=default trigger data cardinality=][|sourceType|], exclusive: - 1. [=map/Set=] |specs|[|triggerData|] to |spec|. + 1. [=set/iterate|For each=] integer |triggerData| of + [=the exclusive range|the range=] 0 to + [=default trigger data cardinality=][|sourceType|], exclusive: + 1. [=map/Set=] |specs|[|triggerData|] to |spec|. 1. If |matchingMode| is "[=trigger-data matching mode/modulus=]": 1. Let |i| be 0. 1. [=map/iterate|For each=] |triggerData| of |specs|'s [=map/get the keys|keys=]: @@ -2708,9 +2604,6 @@ To parse trigger specs given a [=map=] |map|, a [=moment=] 1. Set |i| to |i| + 1. 1. Return |specs|. -Issue: Invoke [=parse summary buckets=] and [=parse summary operator=] -from this algorithm. - To parse a source aggregatable debug reporting config given |value|, a non-negative integer |defaultBudget|, and an [=aggregatable debug reporting config=] |defaultConfig|: @@ -3471,15 +3364,6 @@ A trigger-registration JSON key is one of the following:

    Creating an attribution trigger

    -To parse an event-trigger value given a [=map=] |map|: - -1. If [=experimental Flexible Event support=] is false or |map|["[=trigger-registration JSON key/value=]"] does - not [=map/exists|exist=], return 1. -1. Let |value| be |map|["[=trigger-registration JSON key/value=]"]. -1. If |value| is not an integer, cannot be represented by an unsigned 32-bit - integer, or is less than or equal to zero, return an error. -1. Return |value|. - To parse event triggers given a [=map=] |map|: 1. Let |eventTriggers| be a new [=set=]. @@ -3504,9 +3388,6 @@ To parse event triggers given a [=map=] |map|: 1. Let |filterPair| be the result of running [=parse a filter pair=] with |value|. 1. If |filterPair| is an error, return it. - 1. Let |triggerValue| be the result of running - [=parse an event-trigger value=] with |value|. - 1. If |triggerValue| is an error, return it. 1. Let |eventTrigger| be a new [=event-level trigger configuration=] with the items: : [=event-level trigger configuration/trigger data=] @@ -3519,8 +3400,6 @@ To parse event triggers given a [=map=] |map|: :: |filterPair|[0] : [=event-level trigger configuration/negated filters=] :: |filterPair|[1] - : [=event-level trigger configuration/value=] - :: |triggerValue| 1. [=set/Append=] |eventTrigger| to |eventTriggers|. 1. Return |eventTriggers|. @@ -4107,10 +3986,6 @@ To maybe replace event-level report given an [=attribution source=] 1. [=set/Remove=] |rateLimitRecord| from the [=attribution rate-limit cache=]. 1. Return "[=event-level-report-replacement result/add-new-report=]". -Issue: This algorithm is not compatible with the behavior proposed for -[=experimental Flexible Event support=] with differing -[=trigger spec/event-level report windows=] for a given source. - To trigger event-level attribution given an [=attribution trigger=] |trigger| and an [=attribution source=] |sourceToAttribute|, run the following steps: From b433ca652ae6960152ae862e556cd46c0f376834 Mon Sep 17 00:00:00 2001 From: Andrew Paseltiner Date: Tue, 13 May 2025 12:00:24 -0400 Subject: [PATCH 2/4] Remove Full Flex parsing from header validator --- ts/src/flexible-event/main.ts | 1 - ts/src/header-validator/index.html | 1 - ts/src/header-validator/index.ts | 11 - ts/src/header-validator/main.ts | 8 - ts/src/header-validator/source.test.ts | 394 ------------------ ts/src/header-validator/to-json.ts | 60 +-- ts/src/header-validator/trigger.test.ts | 65 --- ts/src/header-validator/trigger.ts | 1 - ts/src/header-validator/validate-json.test.ts | 1 - ts/src/header-validator/validate-json.ts | 1 - ts/src/header-validator/validate-source.ts | 115 ----- ts/src/header-validator/validate-trigger.ts | 17 - 12 files changed, 9 insertions(+), 666 deletions(-) diff --git a/ts/src/flexible-event/main.ts b/ts/src/flexible-event/main.ts index 9cbc3df33..79077f341 100644 --- a/ts/src/flexible-event/main.ts +++ b/ts/src/flexible-event/main.ts @@ -101,7 +101,6 @@ if (options.json_file !== undefined) { const [{ errors, warnings }, source] = validateSource(json, { vsv: vsv.Chromium, sourceType: options.source_type, - fullFlex: true, }) warnings.forEach((i) => logIssue('W', i)) if (errors.length > 0) { diff --git a/ts/src/header-validator/index.html b/ts/src/header-validator/index.html index 849479011..a17b5bbd1 100644 --- a/ts/src/header-validator/index.html +++ b/ts/src/header-validator/index.html @@ -84,7 +84,6 @@

    Attribution Reporting Header Validation

    (details) -

    (details)

    Validation Result diff --git a/ts/src/header-validator/index.ts b/ts/src/header-validator/index.ts index aad868ce6..8358e550d 100644 --- a/ts/src/header-validator/index.ts +++ b/ts/src/header-validator/index.ts @@ -22,34 +22,27 @@ const sourceTypeFieldset = document.querySelector('#source-type')! const effective = document.querySelector('#effective')! -const flexCheckbox = form.elements.namedItem('flex') as HTMLInputElement - function sourceType(): SourceType { return parseSourceType(sourceTypeRadios.value) } function validate(): void { sourceTypeFieldset.disabled = true - flexCheckbox.disabled = true let v: validator.Validator switch (headerRadios.value) { case 'source': sourceTypeFieldset.disabled = false - flexCheckbox.disabled = false v = source.validator({ vsv: vsv.Chromium, sourceType: sourceType(), - fullFlex: flexCheckbox.checked, noteInfoGain: true, }) break case 'trigger': - flexCheckbox.disabled = false v = trigger.validator({ vsv: vsv.Chromium, - fullFlex: flexCheckbox.checked, }) break case 'os-source': @@ -99,8 +92,6 @@ document.querySelector('#linkify')!.addEventListener('click', () => { url.searchParams.set('source-type', sourceType()) } - url.searchParams.set('flex', flexCheckbox.checked.toString()) - void navigator.clipboard.writeText(url.toString()) }) @@ -136,6 +127,4 @@ if (st !== null && st in SourceType) { } sourceTypeRadios.value = st -flexCheckbox.checked = params.get('flex') === 'true' - validate() diff --git a/ts/src/header-validator/main.ts b/ts/src/header-validator/main.ts index 7ce094432..d9d3e45f1 100644 --- a/ts/src/header-validator/main.ts +++ b/ts/src/header-validator/main.ts @@ -11,7 +11,6 @@ interface Arguments { input?: string file?: string - fullFlex: boolean sourceType?: SourceType silent: boolean @@ -43,11 +42,6 @@ const options = parse( 'If present, parse input as a source. Otherwise, parse as a trigger.', }, - fullFlex: { - type: Boolean, - description: 'If true, parse experimental Full Flexible Event fields.', - }, - silent: { type: Boolean, description: 'If true, suppress output.', @@ -94,11 +88,9 @@ const out = validate( options.sourceType === undefined ? trigger.validator({ vsv: vsv.Chromium, - fullFlex: options.fullFlex, }) : source.validator({ vsv: vsv.Chromium, - fullFlex: options.fullFlex, sourceType: options.sourceType, }) ) diff --git a/ts/src/header-validator/source.test.ts b/ts/src/header-validator/source.test.ts index 9d75a45e1..a20368793 100644 --- a/ts/src/header-validator/source.test.ts +++ b/ts/src/header-validator/source.test.ts @@ -2109,97 +2109,6 @@ const testCases: TestCase[] = [ ], }, - // Flex - // TODO: compare returned trigger specs against expected values - - { - name: 'trigger-specs-wrong-type', - input: `{ - "destination": "https://a.test", - "trigger_specs": {} - }`, - parseFullFlex: true, - expectedErrors: [ - { - path: ['trigger_specs'], - msg: 'must be a list', - }, - ], - }, - { - name: 'trigger-specs-too-long', - input: JSON.stringify({ - destination: 'https://a.test', - trigger_specs: Array(33) - .fill(null) - .map((_, i) => ({ trigger_data: [i] })), - }), - parseFullFlex: true, - expectedErrors: [ - { - path: ['trigger_specs'], - msg: 'length must be in the range [0, 32]', - }, - ], - }, - { - name: 'trigger-specs-value-wrong-type', - input: `{ - "destination": "https://a.test", - "trigger_specs": [false] - }`, - parseFullFlex: true, - expectedErrors: [ - { - path: ['trigger_specs', 0], - msg: 'must be an object', - }, - ], - }, - { - name: 'top-level-trigger-data-and-trigger-specs', - input: `{ - "destination": "https://a.test", - "trigger_data": [], - "trigger_specs": [] - }`, - parseFullFlex: true, - expectedErrors: [ - { - path: [], - msg: 'mutually exclusive fields: trigger_data, trigger_specs', - }, - ], - }, - { - name: 'top-level-trigger-data-and-trigger-specs-ignored', - input: `{ - "destination": "https://a.test", - "max_event_level_reports": 0, - "trigger_data": [], - "trigger_specs": [] - }`, - expectedWarnings: [ - { - path: ['trigger_specs'], - msg: 'unknown field', - }, - ], - }, - { - name: 'trigger-data-missing', - input: `{ - "destination": "https://a.test", - "trigger_specs": [{}] - }`, - parseFullFlex: true, - expectedErrors: [ - { - path: ['trigger_specs', 0, 'trigger_data'], - msg: 'required', - }, - ], - }, { name: 'trigger-data-wrong-type', input: `{ @@ -2291,270 +2200,6 @@ const testCases: TestCase[] = [ }, ], }, - { - name: 'trigger-data-duplicated-across', - input: `{ - "destination": "https://a.test", - "trigger_specs": [ - { "trigger_data": [1, 2] }, - { "trigger_data": [3, 2] } - ] - }`, - parseFullFlex: true, - expectedErrors: [ - { - path: ['trigger_specs'], - msg: 'duplicate trigger_data: 2', - }, - ], - }, - { - name: 'trigger-spec-trigger-data-empty', - input: `{ - "destination": "https://a.test", - "trigger_specs": [{"trigger_data": []}] - }`, - parseFullFlex: true, - expectedErrors: [ - { - path: ['trigger_specs', 0, 'trigger_data'], - msg: 'length must be in the range [1, 32]', - }, - ], - }, - { - name: 'trigger-data-too-many-across', - input: JSON.stringify({ - destination: 'https://a.test', - trigger_specs: [ - { trigger_data: [0] }, - { - trigger_data: Array(32) - .fill(0) - .map((_, i) => i + 1), - }, - ], - }), - parseFullFlex: true, - expectedErrors: [ - { - path: ['trigger_specs'], - msg: 'exceeds maximum number of distinct trigger_data (33 > 32)', - }, - ], - }, - { - name: 'summary-buckets-wrong-type', - input: `{ - "destination": "https://a.test", - "trigger_specs": [{ - "trigger_data": [3], - "summary_buckets": 1 - }] - }`, - parseFullFlex: true, - expectedErrors: [ - { - path: ['trigger_specs', 0, 'summary_buckets'], - msg: 'must be a list', - }, - ], - }, - { - name: 'summary-buckets-empty', - input: `{ - "destination": "https://a.test", - "trigger_specs": [{ - "trigger_data": [3], - "summary_buckets": [] - }] - }`, - parseFullFlex: true, - expectedErrors: [ - { - path: ['trigger_specs', 0, 'summary_buckets'], - msg: 'length must be in the range [1, 3 (max_event_level_reports)]', - }, - ], - }, - { - name: 'summary-buckets-too-long', - input: `{ - "destination": "https://a.test", - "max_event_level_reports": 4, - "trigger_specs": [{ - "trigger_data": [3], - "summary_buckets": [1, 2, 3, 4, 5] - }] - }`, - parseFullFlex: true, - expectedErrors: [ - { - path: ['trigger_specs', 0, 'summary_buckets'], - msg: 'length must be in the range [1, 4 (max_event_level_reports)]', - }, - ], - }, - { - name: 'summary-buckets-cannot-validate-length', - input: `{ - "destination": "https://a.test", - "max_event_level_reports": null, - "trigger_specs": [{ - "trigger_data": [3], - "summary_buckets": [1, 2, 3, 4, 5] - }] - }`, - parseFullFlex: true, - expectedErrors: [ - { - path: ['max_event_level_reports'], - msg: 'must be a number', - }, - { - path: ['trigger_specs', 0, 'summary_buckets'], - msg: 'cannot be fully validated without a valid max_event_level_reports', - }, - ], - }, - { - name: 'summary-buckets-value-wrong-type', - input: `{ - "destination": "https://a.test", - "trigger_specs": [{ - "trigger_data": [3], - "summary_buckets": ["1"] - }] - }`, - parseFullFlex: true, - expectedErrors: [ - { - path: ['trigger_specs', 0, 'summary_buckets', 0], - msg: 'must be a number', - }, - ], - }, - { - name: 'summary-buckets-value-not-integer', - input: `{ - "destination": "https://a.test", - "trigger_specs": [{ - "trigger_data": [3], - "summary_buckets": [1.5] - }] - }`, - parseFullFlex: true, - expectedErrors: [ - { - path: ['trigger_specs', 0, 'summary_buckets', 0], - msg: 'must be an integer', - }, - ], - }, - { - name: 'summary-buckets-value-non-positive', - input: `{ - "destination": "https://a.test", - "trigger_specs": [{ - "trigger_data": [3], - "summary_buckets": [0] - }] - }`, - parseFullFlex: true, - expectedErrors: [ - { - path: ['trigger_specs', 0, 'summary_buckets', 0], - msg: 'must be > implicit minimum (0) and <= uint32 max (4294967295)', - }, - ], - }, - { - name: 'summary-buckets-non-increasing', - input: `{ - "destination": "https://a.test", - "trigger_specs": [{ - "trigger_data": [3], - "summary_buckets": [5, 6, 4] - }] - }`, - parseFullFlex: true, - expectedErrors: [ - { - path: ['trigger_specs', 0, 'summary_buckets', 2], - msg: 'must be > previous value (6) and <= uint32 max (4294967295)', - }, - ], - }, - { - name: 'summary-buckets-value-exceeds-max', - input: `{ - "destination": "https://a.test", - "trigger_specs": [{ - "trigger_data": [3], - "summary_buckets": [4294967296] - }] - }`, - parseFullFlex: true, - expectedErrors: [ - { - path: ['trigger_specs', 0, 'summary_buckets', 0], - msg: 'must be > implicit minimum (0) and <= uint32 max (4294967295)', - }, - ], - }, - { - name: 'summary-operator-wrong-type', - input: `{ - "destination": "https://a.test", - "trigger_specs": [{ - "trigger_data": [3], - "summary_operator": 4 - }] - }`, - parseFullFlex: true, - expectedErrors: [ - { - path: ['trigger_specs', 0, 'summary_operator'], - msg: 'must be a string', - }, - ], - }, - { - name: 'summary-operator-wrong-value', - input: `{ - "destination": "https://a.test", - "trigger_specs": [{ - "trigger_data": [3], - "summary_operator": "VALUE_SUM" - }] - }`, - parseFullFlex: true, - expectedErrors: [ - { - path: ['trigger_specs', 0, 'summary_operator'], - msg: 'must be one of the following (case-sensitive): count, value_sum', - }, - ], - }, - { - // The parser is shared with top-level event_report_windows, so just test - // basic support here. - name: 'spec-event-windows-basic', - input: `{ - "destination": "https://a.test", - "trigger_specs": [{ - "trigger_data": [3], - "event_report_windows": {} - }] - }`, - parseFullFlex: true, - expectedErrors: [ - { - path: ['trigger_specs', 0, 'event_report_windows', 'end_times'], - msg: 'required', - }, - ], - }, { name: 'trigger-data-matching-wrong-type', input: `{ @@ -2595,24 +2240,6 @@ const testCases: TestCase[] = [ }, ], }, - { - name: 'trigger-data-matching-modulus-trigger-data-not-contiguous-across', - input: `{ - "destination": "https://a.test", - "trigger_data_matching": "modulus", - "trigger_specs": [ - {"trigger_data": [0, 1]}, - {"trigger_data": [3]} - ] - }`, - parseFullFlex: true, - expectedErrors: [ - { - path: ['trigger_data_matching'], - msg: 'trigger_data must form a contiguous sequence of integers starting at 0 for modulus', - }, - ], - }, { name: 'trigger-data-matching-modulus-trigger-data-not-contiguous-within', input: `{ @@ -2640,26 +2267,6 @@ const testCases: TestCase[] = [ }, ], }, - { - name: 'trigger-data-matching-modulus-valid-across', - input: `{ - "destination": "https://a.test", - "trigger_data_matching": "modulus", - "trigger_specs": [ - {"trigger_data": [1, 0]}, - {"trigger_data": [3]}, - {"trigger_data": [2]}, - {"trigger_data": [4, 5, 6, 7, 8, 9, 10]} - ] - }`, - vsv: { - maxEventLevelChannelCapacityPerSource: { - [SourceType.event]: Infinity, - [SourceType.navigation]: Infinity, - }, - }, - parseFullFlex: true, - }, { name: 'trigger-data-matching-modulus-valid-within', input: `{ @@ -3236,7 +2843,6 @@ testCases.forEach((tc) => source.validator({ vsv: { ...vsv.Chromium, ...tc.vsv }, sourceType: tc.sourceType ?? SourceType.navigation, - fullFlex: tc.parseFullFlex, noteInfoGain: tc.noteInfoGain, }) ) diff --git a/ts/src/header-validator/to-json.ts b/ts/src/header-validator/to-json.ts index 38402e35b..ab675dba9 100644 --- a/ts/src/header-validator/to-json.ts +++ b/ts/src/header-validator/to-json.ts @@ -104,22 +104,6 @@ function serializeTriggerData(d: Set): TriggerData { return { trigger_data: Array.from(d) } } -type TriggerSpec = EventReportWindows & - TriggerData & { - summary_buckets: number[] - summary_operator: string - } - -function serializeTriggerSpec(ts: source.TriggerSpec): TriggerSpec { - return { - ...serializeEventReportWindows(ts.eventReportWindows), - ...serializeTriggerData(ts.triggerData), - - summary_buckets: Array.from(ts.summaryBuckets), - summary_operator: ts.summaryOperator, - } -} - type SourceAggregatableDebugReportingConfig = AggregatableDebugReportingConfig & { budget: number @@ -153,25 +137,9 @@ function serializeAttributionScopes( type NotFullFlexSource = Partial & { trigger_data: number[] - trigger_specs?: never } -type FullFlexSource = { - event_report_windows?: never - trigger_data?: never - trigger_specs: TriggerSpec[] -} - -function serializeFlexSource( - s: source.Source, - fullFlex: boolean -): NotFullFlexSource | FullFlexSource { - if (fullFlex) { - return { - trigger_specs: Array.from(s.triggerSpecs, serializeTriggerSpec), - } - } - +function serializeFlexSource(s: source.Source): NotFullFlexSource { if (s.triggerSpecs.length === 0) { return { trigger_data: [] } } @@ -188,7 +156,7 @@ function serializeFlexSource( type Source = CommonDebug & Priority & - (NotFullFlexSource | FullFlexSource) & { + NotFullFlexSource & { aggregation_keys: { [key: string]: string } named_budgets?: { [key: string]: number } aggregatable_report_window: number @@ -204,18 +172,16 @@ type Source = CommonDebug & attribution_scopes?: AttributionScopes } -export interface Options { - fullFlex?: boolean | undefined -} +export interface Options {} export function serializeSource( s: source.Source, - opts: Readonly + _: Readonly ): string { const source: Source = { ...serializeCommonDebug(s), ...serializePriority(s), - ...serializeFlexSource(s, opts.fullFlex ?? false), + ...serializeFlexSource(s), aggregation_keys: Object.fromEntries( Array.from(s.aggregationKeys.entries(), ([key, val]) => [ @@ -293,26 +259,18 @@ type EventTriggerDatum = FilterPair & Priority & DedupKey & { trigger_data: string - value?: number } function serializeEventTriggerDatum( - d: trigger.EventTriggerDatum, - fullFlex: boolean + d: trigger.EventTriggerDatum ): EventTriggerDatum { - const obj: EventTriggerDatum = { + return { ...serializeFilterPair(d), ...serializePriority(d), ...serializeDedupKey(d), trigger_data: d.triggerData.toString(), } - - if (fullFlex) { - obj.value = d.value - } - - return obj } type AggregatableDedupKey = FilterPair & DedupKey @@ -396,7 +354,7 @@ type Trigger = CommonDebug & export function serializeTrigger( t: trigger.Trigger, - opts: Readonly + _: Readonly ): string { const trigger: Trigger = { ...serializeCommonDebug(t), @@ -426,7 +384,7 @@ export function serializeTrigger( aggregation_coordinator_origin: t.aggregationCoordinatorOrigin, event_trigger_data: Array.from(t.eventTriggerData, (d) => - serializeEventTriggerDatum(d, opts.fullFlex ?? false) + serializeEventTriggerDatum(d) ), ...ifNotNull('trigger_context_id', t.triggerContextID, (v) => v), diff --git a/ts/src/header-validator/trigger.test.ts b/ts/src/header-validator/trigger.test.ts index 5ceb80957..ed0a2b71d 100644 --- a/ts/src/header-validator/trigger.test.ts +++ b/ts/src/header-validator/trigger.test.ts @@ -155,7 +155,6 @@ const testCases: jsontest.TestCase[] = [ map: new Map([['y', new Set()]]), }, ], - value: 1, }, ], positive: [ @@ -1656,69 +1655,6 @@ const testCases: jsontest.TestCase[] = [ ], }, - // Full Flex - - { - name: 'value-wrong-type', - input: `{"event_trigger_data": [{"value":"1"}]}`, - parseFullFlex: true, - expectedErrors: [ - { - path: ['event_trigger_data', 0, 'value'], - msg: 'must be a number', - }, - ], - }, - { - name: 'value-zero', - input: `{"event_trigger_data": [{"value":0}]}`, - parseFullFlex: true, - expectedErrors: [ - { - path: ['event_trigger_data', 0, 'value'], - msg: 'must be >= 1 and <= uint32 max (4294967295)', - }, - ], - }, - { - name: 'value-negative', - input: `{"event_trigger_data": [{"value":-1}]}`, - parseFullFlex: true, - expectedErrors: [ - { - path: ['event_trigger_data', 0, 'value'], - msg: 'must be >= 1 and <= uint32 max (4294967295)', - }, - ], - }, - { - name: 'value-not-integer', - input: `{"event_trigger_data": [{"value":1.5}]}`, - parseFullFlex: true, - expectedErrors: [ - { - path: ['event_trigger_data', 0, 'value'], - msg: 'must be an integer', - }, - ], - }, - { - name: 'value-max', - input: `{"event_trigger_data": [{"value":4294967295}]}`, - parseFullFlex: true, - }, - { - name: 'value-gt-max', - input: `{"event_trigger_data": [{"value":4294967296}]}`, - parseFullFlex: true, - expectedErrors: [ - { - path: ['event_trigger_data', 0, 'value'], - msg: 'must be >= 1 and <= uint32 max (4294967295)', - }, - ], - }, - // Named budgets. { name: 'named-budgets-wrong-type', @@ -1770,7 +1706,6 @@ testCases.forEach((tc) => tc, trigger.validator({ vsv: { ...vsv.Chromium, ...tc.vsv }, - fullFlex: tc.parseFullFlex, }) ) ) diff --git a/ts/src/header-validator/trigger.ts b/ts/src/header-validator/trigger.ts index 7ccdbe4e6..43ab19216 100644 --- a/ts/src/header-validator/trigger.ts +++ b/ts/src/header-validator/trigger.ts @@ -34,7 +34,6 @@ export type EventTriggerDatum = FilterPair & reg.Priority & DedupKey & { triggerData: bigint - value: number } export type AggregatableDedupKey = FilterPair & DedupKey diff --git a/ts/src/header-validator/validate-json.test.ts b/ts/src/header-validator/validate-json.test.ts index a17d96108..0facc5f08 100644 --- a/ts/src/header-validator/validate-json.test.ts +++ b/ts/src/header-validator/validate-json.test.ts @@ -3,6 +3,5 @@ import * as vsv from '../vendor-specific-values' export type TestCase = testutil.TestCase & { vsv?: Readonly> - parseFullFlex?: boolean parseNamedBudgets?: boolean } diff --git a/ts/src/header-validator/validate-json.ts b/ts/src/header-validator/validate-json.ts index d9aae56ac..cc057c6cf 100644 --- a/ts/src/header-validator/validate-json.ts +++ b/ts/src/header-validator/validate-json.ts @@ -38,7 +38,6 @@ export const UINT32_MAX: number = 2 ** 32 - 1 export interface RegistrationOptions { vsv: VendorSpecificValues - fullFlex?: boolean | undefined } export class RegistrationContext< diff --git a/ts/src/header-validator/validate-source.ts b/ts/src/header-validator/validate-source.ts index 6ec95350e..77cdf0205 100644 --- a/ts/src/header-validator/validate-source.ts +++ b/ts/src/header-validator/validate-source.ts @@ -432,51 +432,6 @@ function sourceAggregatableDebugReportingConfig( }) } -function summaryBuckets( - j: Json | undefined, - ctx: Context, - maxEventLevelReports: Maybe -): Maybe { - let maxLength - if (maxEventLevelReports.value === undefined) { - ctx.error( - 'cannot be fully validated without a valid max_event_level_reports' - ) - maxLength = constants.maxSettableEventLevelAttributionsPerSource - } else { - maxLength = maxEventLevelReports.value - } - - if (j === undefined) { - return maxEventLevelReports.map(makeDefaultSummaryBuckets) - } - - let prev = 0 - let prevDesc = 'implicit minimum' - - const bucket = (j: Json): Maybe => - number(j, ctx) - .filter(isInteger, ctx) - .filter( - isInRange, - ctx, - prev + 1, - UINT32_MAX, - `must be > ${prevDesc} (${prev}) and <= uint32 max (${UINT32_MAX})` - ) - .peek((n) => { - prev = n - prevDesc = 'previous value' - }) - - return array(j, ctx, bucket, { - minLength: 1, - maxLength, - maxLengthErrSuffix: ' (max_event_level_reports)', - itemErrorAction: ItemErrorAction.earlyExit, // suppress unhelpful cascaded errors - }) -} - function fullFlexTriggerDatum(j: Json, ctx: Context): Maybe { return number(j, ctx) .filter(isInteger, ctx) @@ -505,71 +460,6 @@ function makeDefaultSummaryBuckets(maxEventLevelReports: number): number[] { return Array.from({ length: maxEventLevelReports }, (_, i) => i + 1) } -function triggerSpec( - j: Json, - ctx: Context, - deps: TriggerSpecDeps -): Maybe { - return struct(j, ctx, { - eventReportWindows: field('event_report_windows', (j) => - j === undefined - ? deps.eventReportWindows - : eventReportWindows(j, ctx, deps.expiry) - ), - - summaryBuckets: field( - 'summary_buckets', - summaryBuckets, - deps.maxEventLevelReports - ), - - summaryOperator: field( - 'summary_operator', - withDefault(enumerated, SummaryOperator.count), - SummaryOperator - ), - - triggerData: field('trigger_data', required(triggerDataSet)), - }) -} - -function triggerSpecs( - j: Json, - ctx: Context, - deps: TriggerSpecDeps -): Maybe { - return array(j, ctx, (j) => triggerSpec(j, ctx, deps), { - maxLength: constants.maxTriggerDataPerSource, - }).filter((specs) => { - const triggerData = new Set() - const dups = new Set() - for (const spec of specs) { - for (const triggerDatum of spec.triggerData) { - if (triggerData.has(triggerDatum)) { - dups.add(triggerDatum) - } else { - triggerData.add(triggerDatum) - } - } - } - - let ok = true - if (triggerData.size > constants.maxTriggerDataPerSource) { - ctx.error( - `exceeds maximum number of distinct trigger_data (${triggerData.size} > ${constants.maxTriggerDataPerSource})` - ) - ok = false - } - - if (dups.size > 0) { - ctx.error(`duplicate trigger_data: ${Array.from(dups).join(', ')}`) - ok = false - } - - return ok - }) -} - function triggerSpecsFromTriggerData( j: Json, ctx: Context, @@ -706,11 +596,6 @@ function source(j: Json, ctx: Context): Maybe { { trigger_data: (j) => triggerSpecsFromTriggerData(j, ctx, triggerSpecsDeps), - ...(ctx.opts.fullFlex - ? { - trigger_specs: (j) => triggerSpecs(j, ctx, triggerSpecsDeps), - } - : {}), }, defaultTriggerSpecsVal )(j, ctx) diff --git a/ts/src/header-validator/validate-trigger.ts b/ts/src/header-validator/validate-trigger.ts index 49a6e5590..7d3a713ac 100644 --- a/ts/src/header-validator/validate-trigger.ts +++ b/ts/src/header-validator/validate-trigger.ts @@ -31,7 +31,6 @@ import { RegistrationContext as Context, RegistrationOptions, StructFields, - UINT32_MAX, aggregatableDebugReportingConfig, aggregatableKeyValueValue, aggregationCoordinatorOriginField, @@ -245,27 +244,11 @@ function aggregatableFilteringIdMaxBytes( }) } -function eventTriggerValue(j: Json, ctx: Context): Maybe { - return number(j, ctx) - .filter(isInteger, ctx) - .filter( - isInRange, - ctx, - 1, - UINT32_MAX, - `must be >= 1 and <= uint32 max (${UINT32_MAX})` - ) -} - function eventTriggerData(j: Json, ctx: Context): Maybe { return array(j, ctx, (j) => struct(j, ctx, { triggerData: field('trigger_data', withDefault(uint64, 0n)), - value: ctx.opts.fullFlex - ? field('value', withDefault(eventTriggerValue, 1)) - : () => Maybe.some(1), - ...filterFields, ...dedupKeyField, ...priorityField, From 70315ce0662541772adb942f7eb88275f876776b Mon Sep 17 00:00:00 2001 From: Andrew Paseltiner Date: Tue, 13 May 2025 13:02:50 -0400 Subject: [PATCH 3/4] Flatten trigger specs --- ts/src/flexible-event/main.ts | 10 +- ts/src/header-validator/source.test.ts | 18 +-- ts/src/header-validator/source.ts | 15 +- ts/src/header-validator/to-json.ts | 65 ++------ ts/src/header-validator/validate-source.ts | 159 +++++--------------- ts/src/header-validator/validate-trigger.ts | 2 +- 6 files changed, 62 insertions(+), 207 deletions(-) diff --git a/ts/src/flexible-event/main.ts b/ts/src/flexible-event/main.ts index 79077f341..53ca3ab03 100644 --- a/ts/src/flexible-event/main.ts +++ b/ts/src/flexible-event/main.ts @@ -113,12 +113,10 @@ if (options.json_file !== undefined) { new Config( source.maxEventLevelReports, source.attributionScopes, - source.triggerSpecs.flatMap((spec) => - new Array(spec.triggerData.size).fill( - new PerTriggerDataConfig( - spec.eventReportWindows.endTimes.length, - spec.summaryBuckets.length - ) + new Array(source.triggerData.size).fill( + new PerTriggerDataConfig( + source.eventReportWindows.endTimes.length, + source.maxEventLevelReports ) ) ) diff --git a/ts/src/header-validator/source.test.ts b/ts/src/header-validator/source.test.ts index a20368793..bc0144d0c 100644 --- a/ts/src/header-validator/source.test.ts +++ b/ts/src/header-validator/source.test.ts @@ -1,7 +1,7 @@ import { SourceType } from '../source-type' import * as vsv from '../vendor-specific-values' import { Maybe } from './maybe' -import { Source, SummaryOperator, TriggerDataMatching } from './source' +import { Source, TriggerDataMatching } from './source' import * as testutil from './util.test' import * as jsontest from './validate-json.test' import * as source from './validate-source' @@ -69,17 +69,11 @@ const testCases: TestCase[] = [ priority: 2n, sourceEventId: 3n, maxEventLevelReports: 2, - triggerSpecs: [ - { - eventReportWindows: { - startTime: 0, - endTimes: [3601], - }, - summaryBuckets: [1, 2], - summaryOperator: SummaryOperator.count, - triggerData: new Set([0, 1, 2, 3, 4, 5, 6, 7]), - }, - ], + eventReportWindows: { + startTime: 0, + endTimes: [3601], + }, + triggerData: new Set([0, 1, 2, 3, 4, 5, 6, 7]), triggerDataMatching: TriggerDataMatching.modulus, aggregatableDebugReporting: { budget: 1234, diff --git a/ts/src/header-validator/source.ts b/ts/src/header-validator/source.ts index 7eeb69fcf..f4e5a2446 100644 --- a/ts/src/header-validator/source.ts +++ b/ts/src/header-validator/source.ts @@ -10,18 +10,6 @@ export type FilterData = Map> export type AggregationKeys = Map export type NamedBudgets = Map -export enum SummaryOperator { - count = 'count', - value_sum = 'value_sum', -} - -export type TriggerSpec = { - eventReportWindows: EventReportWindows - summaryBuckets: number[] - summaryOperator: SummaryOperator - triggerData: Set -} - export type SourceAggregatableDebugReportingConfig = reg.AggregatableDebugReportingConfig & { budget: number @@ -49,7 +37,8 @@ export type Source = reg.CommonDebug & maxEventLevelReports: number sourceEventId: bigint - triggerSpecs: TriggerSpec[] + eventReportWindows: EventReportWindows + triggerData: Set triggerDataMatching: TriggerDataMatching eventLevelEpsilon: number diff --git a/ts/src/header-validator/to-json.ts b/ts/src/header-validator/to-json.ts index ab675dba9..2c2f97b79 100644 --- a/ts/src/header-validator/to-json.ts +++ b/ts/src/header-validator/to-json.ts @@ -81,29 +81,6 @@ function serializeAggregatableDebugReportingConfig( } } -type EventReportWindows = { - event_report_windows: { start_time: number; end_times: number[] } -} - -function serializeEventReportWindows( - e: source.EventReportWindows -): EventReportWindows { - return { - event_report_windows: { - start_time: e.startTime, - end_times: [...e.endTimes], - }, - } -} - -type TriggerData = { - trigger_data: number[] -} - -function serializeTriggerData(d: Set): TriggerData { - return { trigger_data: Array.from(d) } -} - type SourceAggregatableDebugReportingConfig = AggregatableDebugReportingConfig & { budget: number @@ -135,53 +112,29 @@ function serializeAttributionScopes( } } -type NotFullFlexSource = Partial & { - trigger_data: number[] -} - -function serializeFlexSource(s: source.Source): NotFullFlexSource { - if (s.triggerSpecs.length === 0) { - return { trigger_data: [] } - } - - if (s.triggerSpecs.length === 1) { - return { - ...serializeEventReportWindows(s.triggerSpecs[0]!.eventReportWindows), - ...serializeTriggerData(s.triggerSpecs[0]!.triggerData), - } - } - - throw new TypeError() -} - type Source = CommonDebug & - Priority & - NotFullFlexSource & { + Priority & { aggregation_keys: { [key: string]: string } named_budgets?: { [key: string]: number } aggregatable_report_window: number destination: string[] destination_limit_priority: string event_level_epsilon: number + event_report_windows: { start_time: number; end_times: number[] } expiry: number filter_data: { [key: string]: string[] } max_event_level_reports: number source_event_id: string + trigger_data: number[] trigger_data_matching: string aggregatable_debug_reporting?: SourceAggregatableDebugReportingConfig attribution_scopes?: AttributionScopes } -export interface Options {} - -export function serializeSource( - s: source.Source, - _: Readonly -): string { +export function serializeSource(s: source.Source): string { const source: Source = { ...serializeCommonDebug(s), ...serializePriority(s), - ...serializeFlexSource(s), aggregation_keys: Object.fromEntries( Array.from(s.aggregationKeys.entries(), ([key, val]) => [ @@ -201,9 +154,14 @@ export function serializeSource( destination: Array.from(s.destination), destination_limit_priority: s.destinationLimitPriority.toString(), event_level_epsilon: s.eventLevelEpsilon, + event_report_windows: { + start_time: s.eventReportWindows.startTime, + end_times: [...s.eventReportWindows.endTimes], + }, expiry: s.expiry, max_event_level_reports: s.maxEventLevelReports, source_event_id: s.sourceEventId.toString(), + trigger_data: Array.from(s.triggerData), trigger_data_matching: s.triggerDataMatching, ...ifNotNull( 'aggregatable_debug_reporting', @@ -352,10 +310,7 @@ type Trigger = CommonDebug & attribution_scopes?: string[] } -export function serializeTrigger( - t: trigger.Trigger, - _: Readonly -): string { +export function serializeTrigger(t: trigger.Trigger): string { const trigger: Trigger = { ...serializeCommonDebug(t), ...serializeFilterPair(t), diff --git a/ts/src/header-validator/validate-source.ts b/ts/src/header-validator/validate-source.ts index 77cdf0205..f536826e5 100644 --- a/ts/src/header-validator/validate-source.ts +++ b/ts/src/header-validator/validate-source.ts @@ -10,9 +10,7 @@ import { FilterData, Source, SourceAggregatableDebugReportingConfig, - SummaryOperator, TriggerDataMatching, - TriggerSpec, } from './source' import { ItemErrorAction, @@ -336,12 +334,12 @@ function eventLevelEpsilon(j: Json, ctx: Context): Maybe { function channelCapacity(s: Source, ctx: Context): void { const numStatesWords = 'number of possible output states' - const perTriggerDataConfigs = s.triggerSpecs.flatMap((spec) => - Array(spec.triggerData.size).fill( - new privacy.PerTriggerDataConfig( - spec.eventReportWindows.endTimes.length, - spec.summaryBuckets.length - ) + const perTriggerDataConfigs = Array( + s.triggerData.size + ).fill( + new privacy.PerTriggerDataConfig( + s.eventReportWindows.endTimes.length, + s.maxEventLevelReports ) ) @@ -438,84 +436,14 @@ function fullFlexTriggerDatum(j: Json, ctx: Context): Maybe { .filter(isInRange, ctx, 0, UINT32_MAX) } -function triggerDataSet( - j: Json, - ctx: Context, - allowEmpty: boolean = false -): Maybe> { +function triggerData(j: Json, ctx: Context): Maybe> { return set(j, ctx, fullFlexTriggerDatum, { - minLength: allowEmpty ? 0 : 1, + minLength: 0, maxLength: constants.maxTriggerDataPerSource, requireDistinct: true, }) } -type TriggerSpecDeps = { - expiry: Maybe - eventReportWindows: Maybe - maxEventLevelReports: Maybe -} - -function makeDefaultSummaryBuckets(maxEventLevelReports: number): number[] { - return Array.from({ length: maxEventLevelReports }, (_, i) => i + 1) -} - -function triggerSpecsFromTriggerData( - j: Json, - ctx: Context, - deps: TriggerSpecDeps -): Maybe { - return triggerDataSet(j, ctx, /*allowEmpty=*/ true).map((triggerData) => { - if ( - triggerData.size === 0 || - deps.eventReportWindows.value === undefined || - deps.maxEventLevelReports.value === undefined - ) { - return [] - } - - return [ - { - eventReportWindows: deps.eventReportWindows.value, - summaryBuckets: makeDefaultSummaryBuckets( - deps.maxEventLevelReports.value - ), - summaryOperator: SummaryOperator.count, - triggerData: triggerData, - }, - ] - }) -} - -function defaultTriggerSpecs( - ctx: Context, - eventReportWindows: Maybe, - maxEventLevelReports: Maybe -): Maybe { - return eventReportWindows.flatMap((eventReportWindows) => - maxEventLevelReports.map((maxEventLevelReports) => [ - { - eventReportWindows, - summaryBuckets: Array.from( - { length: maxEventLevelReports }, - (_, i) => i + 1 - ), - summaryOperator: SummaryOperator.count, - triggerData: new Set( - Array.from( - { - length: Number( - constants.defaultTriggerDataCardinality[ctx.opts.sourceType] - ), - }, - (_, i) => i - ) - ), - }, - ]) - ) -} - function compareNumbers(a: number, b: number): number { return a - b } @@ -526,9 +454,7 @@ function isTriggerDataMatchingValidForSpecs(s: Source, ctx: Context): boolean { return true } - const triggerData: number[] = s.triggerSpecs - .flatMap((spec) => Array.from(spec.triggerData)) - .sort(compareNumbers) + const triggerData: number[] = Array.from(s.triggerData).sort(compareNumbers) if (triggerData.some((triggerDatum, i) => triggerDatum !== i)) { ctx.error( @@ -546,7 +472,7 @@ function warnInconsistentMaxEventLevelReportsAndTriggerSpecs( ctx: Context ): void { const allowsReports = s.maxEventLevelReports > 0 - const hasSpecs = s.triggerSpecs.length > 0 + const hasSpecs = s.triggerData.size > 0 if (allowsReports && !hasSpecs) { ctx.warning( @@ -567,39 +493,6 @@ function source(j: Json, ctx: Context): Maybe { withDefault(expiry, constants.validSourceExpiryRange[1]) )(j, ctx) - const eventReportWindowsVal = exclusive( - { - event_report_window: (j) => eventReportWindow(j, ctx, expiryVal), - event_report_windows: (j) => eventReportWindows(j, ctx, expiryVal), - }, - expiryVal.map(defaultEventReportWindows, ctx) - )(j, ctx) - - const maxEventLevelReportsVal = field( - 'max_event_level_reports', - maxEventLevelReports - )(j, ctx) - - const defaultTriggerSpecsVal = defaultTriggerSpecs( - ctx, - eventReportWindowsVal, - maxEventLevelReportsVal - ) - - const triggerSpecsDeps = { - expiry: expiryVal, - eventReportWindows: eventReportWindowsVal, - maxEventLevelReports: maxEventLevelReportsVal, - } - - const triggerSpecsVal = exclusive( - { - trigger_data: (j) => - triggerSpecsFromTriggerData(j, ctx, triggerSpecsDeps), - }, - defaultTriggerSpecsVal - )(j, ctx) - return struct(j, ctx, { aggregatableReportWindow: field('aggregatable_report_window', (j) => j === undefined ? expiryVal : singleReportWindow(j, ctx, expiryVal) @@ -618,9 +511,35 @@ function source(j: Json, ctx: Context): Maybe { ), expiry: () => expiryVal, filterData: field('filter_data', withDefault(filterData, new Map())), - maxEventLevelReports: () => maxEventLevelReportsVal, + maxEventLevelReports: field( + 'max_event_level_reports', + maxEventLevelReports + ), sourceEventId: field('source_event_id', withDefault(uint64, 0n)), - triggerSpecs: () => triggerSpecsVal, + eventReportWindows: exclusive( + { + event_report_window: (j) => eventReportWindow(j, ctx, expiryVal), + event_report_windows: (j) => eventReportWindows(j, ctx, expiryVal), + }, + expiryVal.map(defaultEventReportWindows, ctx) + ), + + triggerData: field( + 'trigger_data', + withDefault( + triggerData, + new Set( + Array.from( + { + length: Number( + constants.defaultTriggerDataCardinality[ctx.opts.sourceType] + ), + }, + (_, i) => i + ) + ) + ) + ), aggregatableDebugReporting: field( 'aggregatable_debug_reporting', withDefault( @@ -731,6 +650,6 @@ export function validateSource( export function validator(opts: Readonly): Validator { return { validate: (input) => validateSource(input, opts), - serialize: (value) => serializeSource(value, opts), + serialize: serializeSource, } } diff --git a/ts/src/header-validator/validate-trigger.ts b/ts/src/header-validator/validate-trigger.ts index 7d3a713ac..9d81ae420 100644 --- a/ts/src/header-validator/validate-trigger.ts +++ b/ts/src/header-validator/validate-trigger.ts @@ -426,6 +426,6 @@ export function validator( ): Validator { return { validate: (input) => validateTrigger(input, opts), - serialize: (value) => serializeTrigger(value, opts), + serialize: serializeTrigger, } } From fe5d01d37b9ff90fe83cb4ddee92a9c6d7902c1d Mon Sep 17 00:00:00 2001 From: Andrew Paseltiner Date: Tue, 13 May 2025 13:44:28 -0400 Subject: [PATCH 4/4] Remove vacuous allowEmpty parameter --- index.bs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/index.bs b/index.bs index ba3c60425..df0cc4976 100644 --- a/index.bs +++ b/index.bs @@ -2558,13 +2558,11 @@ To parse report windows given a |value|, a 1. Return |windows|. To parse trigger data into a trigger spec map given a -|triggerDataList|, a [=trigger spec=] |spec|, a [=trigger spec map=] -|specs|, and a [=boolean=] |allowEmpty|: +|triggerDataList|, a [=trigger spec=] |spec|, and a [=trigger spec map=] +|specs|: 1. If |triggerDataList| is not a [=list=] or its [=list/size=] is greater than [=max distinct trigger data per source=], return false. -1. If |allowEmpty| is false and |triggerDataList| [=list/is empty=], return - false. 1. [=list/iterate|For each=] |triggerData| of |triggerDataList|: 1. If |triggerData| is not an integer or cannot be represented by an unsigned 32-bit integer, or |specs|[|triggerData|] [=map/exists=], @@ -2587,10 +2585,9 @@ To parse trigger specs given a [=map=] |map|, a [=moment=] : [=trigger spec/event-level report windows=] :: |defaultReportWindows| 1. If |map|["[=source-registration JSON key/trigger_data=]"] [=map/exists=]: - 1. Let |allowEmpty| be true. 1. If the result of running [=parse trigger data into a trigger spec map=] with - |map|["[=source-registration JSON key/trigger_data=]"], |spec|, |specs|, and |allowEmpty| is false, + |map|["[=source-registration JSON key/trigger_data=]"], |spec|, and |specs| is false, return an error. 1. Otherwise: 1. [=set/iterate|For each=] integer |triggerData| of