@adcp/sdk 16.0.0-beta.2 → 16.0.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -0
- package/dist/lib/core/ConversationTypes.d.mts +8 -3
- package/dist/lib/core/ConversationTypes.d.ts +8 -3
- package/dist/lib/core/SingleAgentClient.d.mts +5 -3
- package/dist/lib/core/SingleAgentClient.d.ts +5 -3
- package/dist/lib/core/TaskExecutor.js +5 -1
- package/dist/lib/core/TaskExecutor.mjs +5 -1
- package/dist/lib/errors/index.d.mts +9 -4
- package/dist/lib/errors/index.d.ts +9 -4
- package/dist/lib/index.d.mts +2 -2
- package/dist/lib/index.d.ts +2 -2
- package/dist/lib/index.js +6 -0
- package/dist/lib/index.mjs +7 -1
- package/dist/lib/negotiation/types.d.mts +20 -39
- package/dist/lib/negotiation/types.d.ts +20 -39
- package/dist/lib/protocols/index.d.mts +60 -24
- package/dist/lib/protocols/index.d.ts +60 -24
- package/dist/lib/protocols/index.js +9 -4
- package/dist/lib/protocols/index.mjs +3 -0
- package/dist/lib/protocols/mcp-modern.js +1 -0
- package/dist/lib/protocols/mcp-modern.mjs +2 -1
- package/dist/lib/protocols/mcp.js +1 -0
- package/dist/lib/protocols/mcp.mjs +2 -1
- package/dist/lib/protocols/responseSizeLimit.d.mts +91 -5
- package/dist/lib/protocols/responseSizeLimit.d.ts +91 -5
- package/dist/lib/protocols/responseSizeLimit.js +195 -31
- package/dist/lib/protocols/responseSizeLimit.mjs +192 -31
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/signing/crypto.d.mts +15 -0
- package/dist/lib/signing/crypto.d.ts +15 -0
- package/dist/lib/signing/crypto.js +75 -3
- package/dist/lib/signing/crypto.mjs +78 -3
- package/dist/lib/signing/server.d.mts +1 -1
- package/dist/lib/signing/server.d.ts +1 -1
- package/dist/lib/signing/server.js +2 -0
- package/dist/lib/signing/server.mjs +2 -1
- package/dist/lib/signing/signer.js +2 -8
- package/dist/lib/signing/signer.mjs +3 -9
- package/dist/lib/signing/testing.js +2 -6
- package/dist/lib/signing/testing.mjs +3 -7
- package/dist/lib/signing/verifier.js +2 -2
- package/dist/lib/signing/verifier.mjs +3 -3
- package/dist/lib/signing/web-bot-auth.js +20 -7
- package/dist/lib/signing/web-bot-auth.mjs +21 -8
- package/dist/lib/signing/webhook-verifier.js +2 -2
- package/dist/lib/signing/webhook-verifier.mjs +3 -3
- package/dist/lib/substitution/compile.js +2 -1
- package/dist/lib/substitution/compile.mjs +2 -1
- package/dist/lib/substitution/consent-macros.d.mts +4 -0
- package/dist/lib/substitution/consent-macros.d.ts +4 -0
- package/dist/lib/substitution/consent-macros.js +45 -0
- package/dist/lib/substitution/consent-macros.mjs +20 -0
- package/dist/lib/substitution/index.d.mts +1 -1
- package/dist/lib/substitution/index.d.ts +1 -1
- package/dist/lib/substitution/index.js +2 -0
- package/dist/lib/substitution/index.mjs +2 -1
- package/dist/lib/substitution/translate.d.mts +15 -2
- package/dist/lib/substitution/translate.d.ts +15 -2
- package/dist/lib/substitution/translate.js +148 -26
- package/dist/lib/substitution/translate.mjs +147 -26
- package/dist/lib/testing/client.js +2 -1
- package/dist/lib/testing/client.mjs +2 -1
- package/dist/lib/testing/scenarios/capabilities.js +1 -1
- package/dist/lib/testing/scenarios/capabilities.mjs +1 -1
- package/dist/lib/testing/storyboard/request-signing/builder.js +2 -5
- package/dist/lib/testing/storyboard/request-signing/builder.mjs +3 -6
- package/dist/lib/testing/storyboard/signer-grader/grader.js +2 -7
- package/dist/lib/testing/storyboard/signer-grader/grader.mjs +2 -7
- package/dist/lib/testing/storyboard/validations.d.mts +1 -1
- package/dist/lib/testing/storyboard/validations.d.ts +1 -1
- package/dist/lib/types/enums.generated.d.mts +3 -0
- package/dist/lib/types/enums.generated.d.ts +3 -0
- package/dist/lib/types/enums.generated.js +9 -0
- package/dist/lib/types/enums.generated.mjs +6 -0
- package/dist/lib/validation/schema-loader.js +25 -0
- package/dist/lib/validation/schema-loader.mjs +24 -0
- package/dist/lib/version.d.mts +3 -3
- package/dist/lib/version.d.ts +3 -3
- package/dist/lib/version.js +3 -3
- package/dist/lib/version.mjs +3 -3
- package/docs/TYPE-SUMMARY.md +6 -1
- package/docs/guides/DISCOVERY-CACHE-AND-SESSION-REUSE.md +3 -2
- package/docs/guides/UNIVERSAL-MACRO-COMPILATION.md +38 -0
- package/docs/guides/VALIDATE-YOUR-AGENT.md +12 -0
- package/docs/llms.txt +1 -1
- package/docs/migration-14.x-rc-worksheet.md +4 -4
- package/package.json +2 -1
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const VENDOR_SCOPED_CONSENT_MACRO = /^\{GDPR_CONSENT_[1-9][0-9]*\}$/;
|
|
2
|
+
const CONSENT_MACROS = /* @__PURE__ */ new Set([
|
|
3
|
+
"{GDPR}",
|
|
4
|
+
"{GDPR_CONSENT}",
|
|
5
|
+
"{ADDTL_CONSENT}",
|
|
6
|
+
"{US_PRIVACY}",
|
|
7
|
+
"{GPP_STRING}",
|
|
8
|
+
"{GPP_SID}",
|
|
9
|
+
"{LIMIT_AD_TRACKING}"
|
|
10
|
+
]);
|
|
11
|
+
function isVendorScopedConsentMacro(token) {
|
|
12
|
+
return VENDOR_SCOPED_CONSENT_MACRO.test(token);
|
|
13
|
+
}
|
|
14
|
+
function isConsentMacro(token) {
|
|
15
|
+
return CONSENT_MACROS.has(token) || isVendorScopedConsentMacro(token);
|
|
16
|
+
}
|
|
17
|
+
export {
|
|
18
|
+
isConsentMacro,
|
|
19
|
+
isVendorScopedConsentMacro
|
|
20
|
+
};
|
|
@@ -33,7 +33,7 @@ export type { CatalogMacroVectorName } from './vectors.mjs';
|
|
|
33
33
|
export { extractTrackerUrls, matchBindings, assertNoNestedExpansion, assertRfc3986Safe, assertSchemePreserved, assertUnreservedOnly, DEFAULT_MACRO_PROHIBITED_PATTERN, enforceSsrfPolicy, enforceSsrfPolicyResolved, DEFAULT_SSRF_POLICY, } from './observer/index.mjs';
|
|
34
34
|
export { encodeUnreserved, equalUnderHexCasePolicy, isUnreservedOnly, divergenceOffset } from './rfc3986.mjs';
|
|
35
35
|
export type { AssertionOptions, AssertionResult, BindingMatch, CatalogBinding, CatalogMacroVector, PolicyResult, SsrfPolicy, TrackerUrlRecord, } from './types.mjs';
|
|
36
|
-
export { translateUniversalMacros, UnsafeNativeMappingError } from './translate.mjs';
|
|
36
|
+
export { translateUniversalMacros, UnsafeNativeMappingError, MultipleVendorConsentMacrosError } from './translate.mjs';
|
|
37
37
|
export type { MacroMapping, TranslateResult } from './translate.mjs';
|
|
38
38
|
export { compileUniversalMacroTemplate } from './compile.mjs';
|
|
39
39
|
export type { BuiltInSourceMacroSyntax, CompileUniversalMacroTemplateInput, CompileUniversalMacroTemplateResult, CustomSourceMacroSyntax, MacroDocumentationReference, SourceMacroMapping, SourceMacroRequirement, SourceMacroRequirementSatisfaction, SourceMacroSyntax, SourceMacroSyntaxDeclaration, UniversalMacroCompileDiagnostic, UniversalMacroOccurrence, } from './compile.mjs';
|
|
@@ -33,7 +33,7 @@ export type { CatalogMacroVectorName } from './vectors';
|
|
|
33
33
|
export { extractTrackerUrls, matchBindings, assertNoNestedExpansion, assertRfc3986Safe, assertSchemePreserved, assertUnreservedOnly, DEFAULT_MACRO_PROHIBITED_PATTERN, enforceSsrfPolicy, enforceSsrfPolicyResolved, DEFAULT_SSRF_POLICY, } from './observer';
|
|
34
34
|
export { encodeUnreserved, equalUnderHexCasePolicy, isUnreservedOnly, divergenceOffset } from './rfc3986';
|
|
35
35
|
export type { AssertionOptions, AssertionResult, BindingMatch, CatalogBinding, CatalogMacroVector, PolicyResult, SsrfPolicy, TrackerUrlRecord, } from './types';
|
|
36
|
-
export { translateUniversalMacros, UnsafeNativeMappingError } from './translate';
|
|
36
|
+
export { translateUniversalMacros, UnsafeNativeMappingError, MultipleVendorConsentMacrosError } from './translate';
|
|
37
37
|
export type { MacroMapping, TranslateResult } from './translate';
|
|
38
38
|
export { compileUniversalMacroTemplate } from './compile';
|
|
39
39
|
export type { BuiltInSourceMacroSyntax, CompileUniversalMacroTemplateInput, CompileUniversalMacroTemplateResult, CustomSourceMacroSyntax, MacroDocumentationReference, SourceMacroMapping, SourceMacroRequirement, SourceMacroRequirementSatisfaction, SourceMacroSyntax, SourceMacroSyntaxDeclaration, UniversalMacroCompileDiagnostic, UniversalMacroOccurrence, } from './compile';
|
|
@@ -22,6 +22,7 @@ __export(substitution_exports, {
|
|
|
22
22
|
DEFAULT_MACRO_PROHIBITED_PATTERN: () => import_observer.DEFAULT_MACRO_PROHIBITED_PATTERN,
|
|
23
23
|
DEFAULT_SSRF_POLICY: () => import_observer.DEFAULT_SSRF_POLICY,
|
|
24
24
|
MacroInRawValueError: () => import_SubstitutionEncoder.MacroInRawValueError,
|
|
25
|
+
MultipleVendorConsentMacrosError: () => import_translate.MultipleVendorConsentMacrosError,
|
|
25
26
|
PreviewFetchError: () => import_SubstitutionObserver.PreviewFetchError,
|
|
26
27
|
SubstitutionEncoder: () => import_SubstitutionEncoder.SubstitutionEncoder,
|
|
27
28
|
SubstitutionObserver: () => import_SubstitutionObserver.SubstitutionObserver,
|
|
@@ -56,6 +57,7 @@ var import_compile = require('./compile.js');
|
|
|
56
57
|
DEFAULT_MACRO_PROHIBITED_PATTERN,
|
|
57
58
|
DEFAULT_SSRF_POLICY,
|
|
58
59
|
MacroInRawValueError,
|
|
60
|
+
MultipleVendorConsentMacrosError,
|
|
59
61
|
PreviewFetchError,
|
|
60
62
|
SubstitutionEncoder,
|
|
61
63
|
SubstitutionObserver,
|
|
@@ -14,13 +14,14 @@ import {
|
|
|
14
14
|
DEFAULT_SSRF_POLICY
|
|
15
15
|
} from "./observer/index.mjs";
|
|
16
16
|
import { encodeUnreserved, equalUnderHexCasePolicy, isUnreservedOnly, divergenceOffset } from "./rfc3986.mjs";
|
|
17
|
-
import { translateUniversalMacros, UnsafeNativeMappingError } from "./translate.mjs";
|
|
17
|
+
import { translateUniversalMacros, UnsafeNativeMappingError, MultipleVendorConsentMacrosError } from "./translate.mjs";
|
|
18
18
|
import { compileUniversalMacroTemplate } from "./compile.mjs";
|
|
19
19
|
export {
|
|
20
20
|
CATALOG_MACRO_VECTORS,
|
|
21
21
|
DEFAULT_MACRO_PROHIBITED_PATTERN,
|
|
22
22
|
DEFAULT_SSRF_POLICY,
|
|
23
23
|
MacroInRawValueError,
|
|
24
|
+
MultipleVendorConsentMacrosError,
|
|
24
25
|
PreviewFetchError,
|
|
25
26
|
SubstitutionEncoder,
|
|
26
27
|
SubstitutionObserver,
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* position pass through untouched — only query-parameter values are translated.
|
|
5
5
|
*
|
|
6
6
|
* Universal macros match `\{[A-Z][A-Z0-9_]*\}` (upper-snake, single braces).
|
|
7
|
-
* Native ad-server tokens (`%%X%%`, `{{x}}`)
|
|
7
|
+
* Native ad-server tokens (`%%X%%`, `{{x}}`, `${x}`, `[X]`) pass through
|
|
8
|
+
* without translating their interiors.
|
|
8
9
|
*
|
|
9
10
|
* Per query parameter:
|
|
10
11
|
* - No universal macro → left untouched (literal param passes through).
|
|
@@ -64,6 +65,15 @@ export declare class UnsafeNativeMappingError extends Error {
|
|
|
64
65
|
readonly macro: string;
|
|
65
66
|
constructor(macro: string);
|
|
66
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* Thrown before emitting a URL containing a second vendor-scoped TCF macro.
|
|
70
|
+
* Only the canonical second token is retained, never the URL or consent data.
|
|
71
|
+
*/
|
|
72
|
+
export declare class MultipleVendorConsentMacrosError extends Error {
|
|
73
|
+
readonly code: "multiple_vendor_consent_macros";
|
|
74
|
+
readonly macro: string;
|
|
75
|
+
constructor(macro: string);
|
|
76
|
+
}
|
|
67
77
|
/** Result of {@link translateUniversalMacros}. */
|
|
68
78
|
export interface TranslateResult {
|
|
69
79
|
/** The translated URL. Base, path, and fragment are unchanged. */
|
|
@@ -82,7 +92,8 @@ export interface TranslateResult {
|
|
|
82
92
|
unmapped_macros: string[];
|
|
83
93
|
/**
|
|
84
94
|
* Subset of `unmapped_macros` that are consent/privacy signals (`{GDPR}`,
|
|
85
|
-
* `{GDPR_CONSENT}`, `{
|
|
95
|
+
* `{GDPR_CONSENT}`, `{GDPR_CONSENT_<gvl_id>}`, `{ADDTL_CONSENT}`,
|
|
96
|
+
* `{US_PRIVACY}`, `{GPP_STRING}`, `{GPP_SID}`,
|
|
86
97
|
* `{LIMIT_AD_TRACKING}`). Surfaced separately so a forgotten-mapping drop of
|
|
87
98
|
* a compliance-relevant macro isn't lost among benign drops. Deduplicated.
|
|
88
99
|
*/
|
|
@@ -115,5 +126,7 @@ export interface TranslateResult {
|
|
|
115
126
|
* @throws {UnsafeNativeMappingError} If any native mapping entry contains
|
|
116
127
|
* U+0000-U+001F or U+007F. The whole mapping is checked before translation,
|
|
117
128
|
* including entries not referenced by `input_pixel_url`.
|
|
129
|
+
* @throws {MultipleVendorConsentMacrosError} If the final URL contains more
|
|
130
|
+
* than one scoped TCF consent macro, after unsupported parameters are dropped.
|
|
118
131
|
*/
|
|
119
132
|
export declare function translateUniversalMacros(input_pixel_url: string, mapping: MacroMapping): TranslateResult;
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* position pass through untouched — only query-parameter values are translated.
|
|
5
5
|
*
|
|
6
6
|
* Universal macros match `\{[A-Z][A-Z0-9_]*\}` (upper-snake, single braces).
|
|
7
|
-
* Native ad-server tokens (`%%X%%`, `{{x}}`)
|
|
7
|
+
* Native ad-server tokens (`%%X%%`, `{{x}}`, `${x}`, `[X]`) pass through
|
|
8
|
+
* without translating their interiors.
|
|
8
9
|
*
|
|
9
10
|
* Per query parameter:
|
|
10
11
|
* - No universal macro → left untouched (literal param passes through).
|
|
@@ -64,6 +65,15 @@ export declare class UnsafeNativeMappingError extends Error {
|
|
|
64
65
|
readonly macro: string;
|
|
65
66
|
constructor(macro: string);
|
|
66
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* Thrown before emitting a URL containing a second vendor-scoped TCF macro.
|
|
70
|
+
* Only the canonical second token is retained, never the URL or consent data.
|
|
71
|
+
*/
|
|
72
|
+
export declare class MultipleVendorConsentMacrosError extends Error {
|
|
73
|
+
readonly code: "multiple_vendor_consent_macros";
|
|
74
|
+
readonly macro: string;
|
|
75
|
+
constructor(macro: string);
|
|
76
|
+
}
|
|
67
77
|
/** Result of {@link translateUniversalMacros}. */
|
|
68
78
|
export interface TranslateResult {
|
|
69
79
|
/** The translated URL. Base, path, and fragment are unchanged. */
|
|
@@ -82,7 +92,8 @@ export interface TranslateResult {
|
|
|
82
92
|
unmapped_macros: string[];
|
|
83
93
|
/**
|
|
84
94
|
* Subset of `unmapped_macros` that are consent/privacy signals (`{GDPR}`,
|
|
85
|
-
* `{GDPR_CONSENT}`, `{
|
|
95
|
+
* `{GDPR_CONSENT}`, `{GDPR_CONSENT_<gvl_id>}`, `{ADDTL_CONSENT}`,
|
|
96
|
+
* `{US_PRIVACY}`, `{GPP_STRING}`, `{GPP_SID}`,
|
|
86
97
|
* `{LIMIT_AD_TRACKING}`). Surfaced separately so a forgotten-mapping drop of
|
|
87
98
|
* a compliance-relevant macro isn't lost among benign drops. Deduplicated.
|
|
88
99
|
*/
|
|
@@ -115,5 +126,7 @@ export interface TranslateResult {
|
|
|
115
126
|
* @throws {UnsafeNativeMappingError} If any native mapping entry contains
|
|
116
127
|
* U+0000-U+001F or U+007F. The whole mapping is checked before translation,
|
|
117
128
|
* including entries not referenced by `input_pixel_url`.
|
|
129
|
+
* @throws {MultipleVendorConsentMacrosError} If the final URL contains more
|
|
130
|
+
* than one scoped TCF consent macro, after unsupported parameters are dropped.
|
|
118
131
|
*/
|
|
119
132
|
export declare function translateUniversalMacros(input_pixel_url: string, mapping: MacroMapping): TranslateResult;
|
|
@@ -18,21 +18,51 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
19
|
var translate_exports = {};
|
|
20
20
|
__export(translate_exports, {
|
|
21
|
+
MultipleVendorConsentMacrosError: () => MultipleVendorConsentMacrosError,
|
|
21
22
|
UnsafeNativeMappingError: () => UnsafeNativeMappingError,
|
|
22
23
|
translateUniversalMacros: () => translateUniversalMacros
|
|
23
24
|
});
|
|
24
25
|
module.exports = __toCommonJS(translate_exports);
|
|
25
26
|
var import_rfc3986 = require('./rfc3986.js');
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
27
|
+
var import_consent_macros = require('./consent-macros.js');
|
|
28
|
+
function* macroTokens(text) {
|
|
29
|
+
const missingClosers = /* @__PURE__ */ new Set();
|
|
30
|
+
let cursor = 0;
|
|
31
|
+
while (cursor < text.length) {
|
|
32
|
+
let end = -1;
|
|
33
|
+
let native = true;
|
|
34
|
+
const close = text.startsWith("%%", cursor) ? "%%" : text.startsWith("{{", cursor) ? "}}" : text.startsWith("${", cursor) ? "}" : null;
|
|
35
|
+
if (close && !missingClosers.has(close)) {
|
|
36
|
+
const closer = text.indexOf(close, cursor + 2);
|
|
37
|
+
if (closer === -1) missingClosers.add(close);
|
|
38
|
+
else end = closer + close.length;
|
|
39
|
+
}
|
|
40
|
+
if (end === -1 && (text[cursor] === "{" || text[cursor] === "[")) {
|
|
41
|
+
let nameEnd = cursor + 1;
|
|
42
|
+
if (/[A-Z]/.test(text[nameEnd] ?? "")) {
|
|
43
|
+
nameEnd += 1;
|
|
44
|
+
while (/[A-Z0-9_]/.test(text[nameEnd] ?? "")) nameEnd += 1;
|
|
45
|
+
if (text[nameEnd] === (text[cursor] === "{" ? "}" : "]")) {
|
|
46
|
+
end = nameEnd + 1;
|
|
47
|
+
native = text[cursor] === "[";
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (end === -1) {
|
|
52
|
+
cursor += 1;
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
yield { text: text.slice(cursor, end), start: cursor, end, native };
|
|
56
|
+
cursor = end;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function hasNativeTokenSyntax(value) {
|
|
60
|
+
for (const token of macroTokens(value)) {
|
|
61
|
+
const minimumLength = token.text.startsWith("${") ? 3 : token.text.startsWith("[") ? 2 : 4;
|
|
62
|
+
if (token.native && token.text.length > minimumLength) return true;
|
|
63
|
+
}
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
36
66
|
const UNSAFE_NATIVE_CHARACTER = /[\u0000-\u001F\u007F]/;
|
|
37
67
|
class UnsafeNativeMappingError extends Error {
|
|
38
68
|
code = "unsafe_native_mapping";
|
|
@@ -46,6 +76,83 @@ class UnsafeNativeMappingError extends Error {
|
|
|
46
76
|
this.macro = macro;
|
|
47
77
|
}
|
|
48
78
|
}
|
|
79
|
+
class MultipleVendorConsentMacrosError extends Error {
|
|
80
|
+
code = "multiple_vendor_consent_macros";
|
|
81
|
+
macro;
|
|
82
|
+
constructor(macro) {
|
|
83
|
+
super(`Multiple vendor-scoped consent macros; second token is ${JSON.stringify(macro)}`);
|
|
84
|
+
this.name = "MultipleVendorConsentMacrosError";
|
|
85
|
+
this.macro = macro;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function overlappingRange(ranges, candidate) {
|
|
89
|
+
let low = 0;
|
|
90
|
+
let high = ranges.length;
|
|
91
|
+
while (low < high) {
|
|
92
|
+
const middle = Math.floor((low + high) / 2);
|
|
93
|
+
if (ranges[middle].end <= candidate.start) low = middle + 1;
|
|
94
|
+
else high = middle;
|
|
95
|
+
}
|
|
96
|
+
const range = ranges[low];
|
|
97
|
+
return range && range.start < candidate.end ? range : void 0;
|
|
98
|
+
}
|
|
99
|
+
function assertSingleScopedConsent(spans, mapping) {
|
|
100
|
+
const url = spans.map((span) => span.text).join("");
|
|
101
|
+
const concrete = [];
|
|
102
|
+
const sources = [];
|
|
103
|
+
let offset = 0;
|
|
104
|
+
for (const span of spans) {
|
|
105
|
+
const range = { start: offset, end: offset + span.text.length };
|
|
106
|
+
if (span.concrete && range.start < range.end) {
|
|
107
|
+
const previous = concrete[concrete.length - 1];
|
|
108
|
+
if (previous?.end === range.start) previous.end = range.end;
|
|
109
|
+
else concrete.push(range);
|
|
110
|
+
}
|
|
111
|
+
if (span.scopedSource && span.text.length > 0) sources.push({ ...range, macro: span.scopedSource });
|
|
112
|
+
offset = range.end;
|
|
113
|
+
}
|
|
114
|
+
const occurrences = [];
|
|
115
|
+
const shells = [];
|
|
116
|
+
for (const token of macroTokens(url)) {
|
|
117
|
+
const range = { start: token.start, end: token.end };
|
|
118
|
+
shells.push(range);
|
|
119
|
+
const canonical = token.text.startsWith("${") ? token.text.slice(1) : token.text;
|
|
120
|
+
if ((0, import_consent_macros.isVendorScopedConsentMacro)(canonical)) {
|
|
121
|
+
occurrences.push({ ...range, macro: canonical });
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const aliases = [];
|
|
125
|
+
for (const [macro, entry] of Object.entries(mapping)) {
|
|
126
|
+
if (!(0, import_consent_macros.isVendorScopedConsentMacro)(macro) || !("native" in entry) || entry.native.length === 0) continue;
|
|
127
|
+
let start = url.indexOf(entry.native);
|
|
128
|
+
while (start !== -1) {
|
|
129
|
+
const range = { start, end: start + entry.native.length };
|
|
130
|
+
const shell = overlappingRange(shells, range);
|
|
131
|
+
const completeShell = shell?.start === range.start && shell.end === range.end;
|
|
132
|
+
const insideShell = shell && shell.start <= range.start && range.end <= shell.end && !completeShell;
|
|
133
|
+
const data = overlappingRange(concrete, range);
|
|
134
|
+
const entirelyConcrete = data && data.start <= range.start && range.end <= data.end;
|
|
135
|
+
if (!insideShell && (completeShell || !entirelyConcrete) && !overlappingRange(occurrences, range)) {
|
|
136
|
+
aliases.push({ ...range, macro });
|
|
137
|
+
}
|
|
138
|
+
start = url.indexOf(entry.native, range.end);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
aliases.sort((a, b) => a.start - b.start || b.end - a.end);
|
|
142
|
+
const selectedAliases = [];
|
|
143
|
+
for (const alias of aliases) {
|
|
144
|
+
const previous = selectedAliases[selectedAliases.length - 1];
|
|
145
|
+
if (!previous || previous.end <= alias.start) selectedAliases.push(alias);
|
|
146
|
+
}
|
|
147
|
+
for (const alias of selectedAliases) occurrences.push(alias);
|
|
148
|
+
occurrences.sort((a, b) => a.start - b.start);
|
|
149
|
+
const unmatchedSources = sources.filter((source) => !overlappingRange(occurrences, source));
|
|
150
|
+
for (const source of unmatchedSources) occurrences.push(source);
|
|
151
|
+
occurrences.sort((a, b) => a.start - b.start);
|
|
152
|
+
const second = occurrences[1];
|
|
153
|
+
if (second) throw new MultipleVendorConsentMacrosError(second.macro);
|
|
154
|
+
return url;
|
|
155
|
+
}
|
|
49
156
|
function translateUniversalMacros(input_pixel_url, mapping) {
|
|
50
157
|
const suspect_native_values = [];
|
|
51
158
|
const suspectSeen = /* @__PURE__ */ new Set();
|
|
@@ -58,11 +165,11 @@ function translateUniversalMacros(input_pixel_url, mapping) {
|
|
|
58
165
|
}
|
|
59
166
|
continue;
|
|
60
167
|
}
|
|
61
|
-
if (
|
|
168
|
+
if ((0, import_consent_macros.isConsentMacro)(macro) && !frozenConsentSeen.has(macro)) {
|
|
62
169
|
frozenConsentSeen.add(macro);
|
|
63
170
|
frozen_consent_macros.push(macro);
|
|
64
171
|
}
|
|
65
|
-
if (
|
|
172
|
+
if (hasNativeTokenSyntax(entry.value) && !suspectSeen.has(macro)) {
|
|
66
173
|
suspectSeen.add(macro);
|
|
67
174
|
suspect_native_values.push(macro);
|
|
68
175
|
}
|
|
@@ -73,7 +180,7 @@ function translateUniversalMacros(input_pixel_url, mapping) {
|
|
|
73
180
|
const queryIdx = withoutFragment.indexOf("?");
|
|
74
181
|
if (queryIdx === -1) {
|
|
75
182
|
return {
|
|
76
|
-
url: input_pixel_url,
|
|
183
|
+
url: assertSingleScopedConsent([{ text: input_pixel_url }], mapping),
|
|
77
184
|
dropped_params: [],
|
|
78
185
|
unmapped_macros: [],
|
|
79
186
|
dropped_consent_macros: [],
|
|
@@ -93,12 +200,12 @@ function translateUniversalMacros(input_pixel_url, mapping) {
|
|
|
93
200
|
const eqIdx = rawParam.indexOf("=");
|
|
94
201
|
const key = eqIdx === -1 ? rawParam : rawParam.slice(0, eqIdx);
|
|
95
202
|
const value = eqIdx === -1 ? "" : rawParam.slice(eqIdx + 1);
|
|
96
|
-
const tokens = value.
|
|
97
|
-
if (
|
|
98
|
-
outputParts.push(rawParam);
|
|
203
|
+
const tokens = [...macroTokens(value)].filter((token) => !token.native);
|
|
204
|
+
if (tokens.length === 0) {
|
|
205
|
+
outputParts.push([{ text: rawParam }]);
|
|
99
206
|
continue;
|
|
100
207
|
}
|
|
101
|
-
const missing = tokens.filter((
|
|
208
|
+
const missing = tokens.map((token) => token.text).filter((token) => !(token in mapping));
|
|
102
209
|
if (missing.length > 0) {
|
|
103
210
|
dropped_params.push(key);
|
|
104
211
|
for (const m of missing) {
|
|
@@ -106,24 +213,38 @@ function translateUniversalMacros(input_pixel_url, mapping) {
|
|
|
106
213
|
unmappedSeen.add(m);
|
|
107
214
|
unmapped_macros.push(m);
|
|
108
215
|
}
|
|
109
|
-
if (
|
|
216
|
+
if ((0, import_consent_macros.isConsentMacro)(m) && !consentSeen.has(m)) {
|
|
110
217
|
consentSeen.add(m);
|
|
111
218
|
dropped_consent_macros.push(m);
|
|
112
219
|
}
|
|
113
220
|
}
|
|
114
221
|
continue;
|
|
115
222
|
}
|
|
116
|
-
const translated =
|
|
223
|
+
const translated = [{ text: `${key}=` }];
|
|
224
|
+
let cursor = 0;
|
|
225
|
+
for (const match of tokens) {
|
|
226
|
+
const token = match.text;
|
|
227
|
+
translated.push({ text: value.slice(cursor, match.start) });
|
|
117
228
|
const entry = mapping[token];
|
|
118
|
-
if (
|
|
119
|
-
|
|
229
|
+
if ("native" in entry) {
|
|
230
|
+
translated.push({
|
|
231
|
+
text: entry.native,
|
|
232
|
+
...(0, import_consent_macros.isVendorScopedConsentMacro)(token) ? { scopedSource: token } : {}
|
|
233
|
+
});
|
|
234
|
+
} else {
|
|
235
|
+
translated.push({ text: (0, import_rfc3986.encodeUnreserved)(entry.value), concrete: true });
|
|
120
236
|
}
|
|
121
|
-
|
|
122
|
-
}
|
|
123
|
-
|
|
237
|
+
cursor = match.end;
|
|
238
|
+
}
|
|
239
|
+
translated.push({ text: value.slice(cursor) });
|
|
240
|
+
outputParts.push(translated);
|
|
124
241
|
}
|
|
125
|
-
const
|
|
126
|
-
const
|
|
242
|
+
const querySpans = outputParts.flatMap((part, index) => index === 0 ? part : [{ text: "&" }, ...part]);
|
|
243
|
+
const hasQuery = querySpans.some((span) => span.text.length > 0);
|
|
244
|
+
const url = assertSingleScopedConsent(
|
|
245
|
+
[{ text: base }, ...hasQuery ? [{ text: "?" }, ...querySpans] : [], { text: fragment }],
|
|
246
|
+
mapping
|
|
247
|
+
);
|
|
127
248
|
return {
|
|
128
249
|
url,
|
|
129
250
|
dropped_params,
|
|
@@ -135,6 +256,7 @@ function translateUniversalMacros(input_pixel_url, mapping) {
|
|
|
135
256
|
}
|
|
136
257
|
// Annotate the CommonJS export names for ESM import in node:
|
|
137
258
|
0 && (module.exports = {
|
|
259
|
+
MultipleVendorConsentMacrosError,
|
|
138
260
|
UnsafeNativeMappingError,
|
|
139
261
|
translateUniversalMacros
|
|
140
262
|
});
|
|
@@ -1,14 +1,43 @@
|
|
|
1
1
|
import { encodeUnreserved } from "./rfc3986.mjs";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
2
|
+
import { isConsentMacro, isVendorScopedConsentMacro } from "./consent-macros.mjs";
|
|
3
|
+
function* macroTokens(text) {
|
|
4
|
+
const missingClosers = /* @__PURE__ */ new Set();
|
|
5
|
+
let cursor = 0;
|
|
6
|
+
while (cursor < text.length) {
|
|
7
|
+
let end = -1;
|
|
8
|
+
let native = true;
|
|
9
|
+
const close = text.startsWith("%%", cursor) ? "%%" : text.startsWith("{{", cursor) ? "}}" : text.startsWith("${", cursor) ? "}" : null;
|
|
10
|
+
if (close && !missingClosers.has(close)) {
|
|
11
|
+
const closer = text.indexOf(close, cursor + 2);
|
|
12
|
+
if (closer === -1) missingClosers.add(close);
|
|
13
|
+
else end = closer + close.length;
|
|
14
|
+
}
|
|
15
|
+
if (end === -1 && (text[cursor] === "{" || text[cursor] === "[")) {
|
|
16
|
+
let nameEnd = cursor + 1;
|
|
17
|
+
if (/[A-Z]/.test(text[nameEnd] ?? "")) {
|
|
18
|
+
nameEnd += 1;
|
|
19
|
+
while (/[A-Z0-9_]/.test(text[nameEnd] ?? "")) nameEnd += 1;
|
|
20
|
+
if (text[nameEnd] === (text[cursor] === "{" ? "}" : "]")) {
|
|
21
|
+
end = nameEnd + 1;
|
|
22
|
+
native = text[cursor] === "[";
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if (end === -1) {
|
|
27
|
+
cursor += 1;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
yield { text: text.slice(cursor, end), start: cursor, end, native };
|
|
31
|
+
cursor = end;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function hasNativeTokenSyntax(value) {
|
|
35
|
+
for (const token of macroTokens(value)) {
|
|
36
|
+
const minimumLength = token.text.startsWith("${") ? 3 : token.text.startsWith("[") ? 2 : 4;
|
|
37
|
+
if (token.native && token.text.length > minimumLength) return true;
|
|
38
|
+
}
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
12
41
|
const UNSAFE_NATIVE_CHARACTER = /[\u0000-\u001F\u007F]/;
|
|
13
42
|
class UnsafeNativeMappingError extends Error {
|
|
14
43
|
code = "unsafe_native_mapping";
|
|
@@ -22,6 +51,83 @@ class UnsafeNativeMappingError extends Error {
|
|
|
22
51
|
this.macro = macro;
|
|
23
52
|
}
|
|
24
53
|
}
|
|
54
|
+
class MultipleVendorConsentMacrosError extends Error {
|
|
55
|
+
code = "multiple_vendor_consent_macros";
|
|
56
|
+
macro;
|
|
57
|
+
constructor(macro) {
|
|
58
|
+
super(`Multiple vendor-scoped consent macros; second token is ${JSON.stringify(macro)}`);
|
|
59
|
+
this.name = "MultipleVendorConsentMacrosError";
|
|
60
|
+
this.macro = macro;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function overlappingRange(ranges, candidate) {
|
|
64
|
+
let low = 0;
|
|
65
|
+
let high = ranges.length;
|
|
66
|
+
while (low < high) {
|
|
67
|
+
const middle = Math.floor((low + high) / 2);
|
|
68
|
+
if (ranges[middle].end <= candidate.start) low = middle + 1;
|
|
69
|
+
else high = middle;
|
|
70
|
+
}
|
|
71
|
+
const range = ranges[low];
|
|
72
|
+
return range && range.start < candidate.end ? range : void 0;
|
|
73
|
+
}
|
|
74
|
+
function assertSingleScopedConsent(spans, mapping) {
|
|
75
|
+
const url = spans.map((span) => span.text).join("");
|
|
76
|
+
const concrete = [];
|
|
77
|
+
const sources = [];
|
|
78
|
+
let offset = 0;
|
|
79
|
+
for (const span of spans) {
|
|
80
|
+
const range = { start: offset, end: offset + span.text.length };
|
|
81
|
+
if (span.concrete && range.start < range.end) {
|
|
82
|
+
const previous = concrete[concrete.length - 1];
|
|
83
|
+
if (previous?.end === range.start) previous.end = range.end;
|
|
84
|
+
else concrete.push(range);
|
|
85
|
+
}
|
|
86
|
+
if (span.scopedSource && span.text.length > 0) sources.push({ ...range, macro: span.scopedSource });
|
|
87
|
+
offset = range.end;
|
|
88
|
+
}
|
|
89
|
+
const occurrences = [];
|
|
90
|
+
const shells = [];
|
|
91
|
+
for (const token of macroTokens(url)) {
|
|
92
|
+
const range = { start: token.start, end: token.end };
|
|
93
|
+
shells.push(range);
|
|
94
|
+
const canonical = token.text.startsWith("${") ? token.text.slice(1) : token.text;
|
|
95
|
+
if (isVendorScopedConsentMacro(canonical)) {
|
|
96
|
+
occurrences.push({ ...range, macro: canonical });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const aliases = [];
|
|
100
|
+
for (const [macro, entry] of Object.entries(mapping)) {
|
|
101
|
+
if (!isVendorScopedConsentMacro(macro) || !("native" in entry) || entry.native.length === 0) continue;
|
|
102
|
+
let start = url.indexOf(entry.native);
|
|
103
|
+
while (start !== -1) {
|
|
104
|
+
const range = { start, end: start + entry.native.length };
|
|
105
|
+
const shell = overlappingRange(shells, range);
|
|
106
|
+
const completeShell = shell?.start === range.start && shell.end === range.end;
|
|
107
|
+
const insideShell = shell && shell.start <= range.start && range.end <= shell.end && !completeShell;
|
|
108
|
+
const data = overlappingRange(concrete, range);
|
|
109
|
+
const entirelyConcrete = data && data.start <= range.start && range.end <= data.end;
|
|
110
|
+
if (!insideShell && (completeShell || !entirelyConcrete) && !overlappingRange(occurrences, range)) {
|
|
111
|
+
aliases.push({ ...range, macro });
|
|
112
|
+
}
|
|
113
|
+
start = url.indexOf(entry.native, range.end);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
aliases.sort((a, b) => a.start - b.start || b.end - a.end);
|
|
117
|
+
const selectedAliases = [];
|
|
118
|
+
for (const alias of aliases) {
|
|
119
|
+
const previous = selectedAliases[selectedAliases.length - 1];
|
|
120
|
+
if (!previous || previous.end <= alias.start) selectedAliases.push(alias);
|
|
121
|
+
}
|
|
122
|
+
for (const alias of selectedAliases) occurrences.push(alias);
|
|
123
|
+
occurrences.sort((a, b) => a.start - b.start);
|
|
124
|
+
const unmatchedSources = sources.filter((source) => !overlappingRange(occurrences, source));
|
|
125
|
+
for (const source of unmatchedSources) occurrences.push(source);
|
|
126
|
+
occurrences.sort((a, b) => a.start - b.start);
|
|
127
|
+
const second = occurrences[1];
|
|
128
|
+
if (second) throw new MultipleVendorConsentMacrosError(second.macro);
|
|
129
|
+
return url;
|
|
130
|
+
}
|
|
25
131
|
function translateUniversalMacros(input_pixel_url, mapping) {
|
|
26
132
|
const suspect_native_values = [];
|
|
27
133
|
const suspectSeen = /* @__PURE__ */ new Set();
|
|
@@ -34,11 +140,11 @@ function translateUniversalMacros(input_pixel_url, mapping) {
|
|
|
34
140
|
}
|
|
35
141
|
continue;
|
|
36
142
|
}
|
|
37
|
-
if (
|
|
143
|
+
if (isConsentMacro(macro) && !frozenConsentSeen.has(macro)) {
|
|
38
144
|
frozenConsentSeen.add(macro);
|
|
39
145
|
frozen_consent_macros.push(macro);
|
|
40
146
|
}
|
|
41
|
-
if (
|
|
147
|
+
if (hasNativeTokenSyntax(entry.value) && !suspectSeen.has(macro)) {
|
|
42
148
|
suspectSeen.add(macro);
|
|
43
149
|
suspect_native_values.push(macro);
|
|
44
150
|
}
|
|
@@ -49,7 +155,7 @@ function translateUniversalMacros(input_pixel_url, mapping) {
|
|
|
49
155
|
const queryIdx = withoutFragment.indexOf("?");
|
|
50
156
|
if (queryIdx === -1) {
|
|
51
157
|
return {
|
|
52
|
-
url: input_pixel_url,
|
|
158
|
+
url: assertSingleScopedConsent([{ text: input_pixel_url }], mapping),
|
|
53
159
|
dropped_params: [],
|
|
54
160
|
unmapped_macros: [],
|
|
55
161
|
dropped_consent_macros: [],
|
|
@@ -69,12 +175,12 @@ function translateUniversalMacros(input_pixel_url, mapping) {
|
|
|
69
175
|
const eqIdx = rawParam.indexOf("=");
|
|
70
176
|
const key = eqIdx === -1 ? rawParam : rawParam.slice(0, eqIdx);
|
|
71
177
|
const value = eqIdx === -1 ? "" : rawParam.slice(eqIdx + 1);
|
|
72
|
-
const tokens = value.
|
|
73
|
-
if (
|
|
74
|
-
outputParts.push(rawParam);
|
|
178
|
+
const tokens = [...macroTokens(value)].filter((token) => !token.native);
|
|
179
|
+
if (tokens.length === 0) {
|
|
180
|
+
outputParts.push([{ text: rawParam }]);
|
|
75
181
|
continue;
|
|
76
182
|
}
|
|
77
|
-
const missing = tokens.filter((
|
|
183
|
+
const missing = tokens.map((token) => token.text).filter((token) => !(token in mapping));
|
|
78
184
|
if (missing.length > 0) {
|
|
79
185
|
dropped_params.push(key);
|
|
80
186
|
for (const m of missing) {
|
|
@@ -82,24 +188,38 @@ function translateUniversalMacros(input_pixel_url, mapping) {
|
|
|
82
188
|
unmappedSeen.add(m);
|
|
83
189
|
unmapped_macros.push(m);
|
|
84
190
|
}
|
|
85
|
-
if (
|
|
191
|
+
if (isConsentMacro(m) && !consentSeen.has(m)) {
|
|
86
192
|
consentSeen.add(m);
|
|
87
193
|
dropped_consent_macros.push(m);
|
|
88
194
|
}
|
|
89
195
|
}
|
|
90
196
|
continue;
|
|
91
197
|
}
|
|
92
|
-
const translated =
|
|
198
|
+
const translated = [{ text: `${key}=` }];
|
|
199
|
+
let cursor = 0;
|
|
200
|
+
for (const match of tokens) {
|
|
201
|
+
const token = match.text;
|
|
202
|
+
translated.push({ text: value.slice(cursor, match.start) });
|
|
93
203
|
const entry = mapping[token];
|
|
94
|
-
if (
|
|
95
|
-
|
|
204
|
+
if ("native" in entry) {
|
|
205
|
+
translated.push({
|
|
206
|
+
text: entry.native,
|
|
207
|
+
...isVendorScopedConsentMacro(token) ? { scopedSource: token } : {}
|
|
208
|
+
});
|
|
209
|
+
} else {
|
|
210
|
+
translated.push({ text: encodeUnreserved(entry.value), concrete: true });
|
|
96
211
|
}
|
|
97
|
-
|
|
98
|
-
}
|
|
99
|
-
|
|
212
|
+
cursor = match.end;
|
|
213
|
+
}
|
|
214
|
+
translated.push({ text: value.slice(cursor) });
|
|
215
|
+
outputParts.push(translated);
|
|
100
216
|
}
|
|
101
|
-
const
|
|
102
|
-
const
|
|
217
|
+
const querySpans = outputParts.flatMap((part, index) => index === 0 ? part : [{ text: "&" }, ...part]);
|
|
218
|
+
const hasQuery = querySpans.some((span) => span.text.length > 0);
|
|
219
|
+
const url = assertSingleScopedConsent(
|
|
220
|
+
[{ text: base }, ...hasQuery ? [{ text: "?" }, ...querySpans] : [], { text: fragment }],
|
|
221
|
+
mapping
|
|
222
|
+
);
|
|
103
223
|
return {
|
|
104
224
|
url,
|
|
105
225
|
dropped_params,
|
|
@@ -110,6 +230,7 @@ function translateUniversalMacros(input_pixel_url, mapping) {
|
|
|
110
230
|
};
|
|
111
231
|
}
|
|
112
232
|
export {
|
|
233
|
+
MultipleVendorConsentMacrosError,
|
|
113
234
|
UnsafeNativeMappingError,
|
|
114
235
|
translateUniversalMacros
|
|
115
236
|
};
|
|
@@ -55,6 +55,7 @@ var import_protocols = require('../protocols/index.js');
|
|
|
55
55
|
var import_node_crypto = require("node:crypto");
|
|
56
56
|
var import_node_async_hooks = require("node:async_hooks");
|
|
57
57
|
var import_client_hooks = require('../validation/client-hooks.js');
|
|
58
|
+
var import_schema_loader = require('../validation/schema-loader.js');
|
|
58
59
|
var import_error_extraction = require('../utils/error-extraction.js');
|
|
59
60
|
var import_client = require('../signing/client.js');
|
|
60
61
|
var import_storage_registry = require('../auth/oauth/storage-registry.js');
|
|
@@ -534,7 +535,7 @@ async function discoverAgentProfile(client, signal, schemaAdcpVersion) {
|
|
|
534
535
|
caps.data,
|
|
535
536
|
"strict",
|
|
536
537
|
void 0,
|
|
537
|
-
schemaAdcpVersion ?? client.getAdcpVersion()
|
|
538
|
+
(0, import_schema_loader.resolveServedValidationVersion)(caps.data, schemaAdcpVersion ?? client.getAdcpVersion())
|
|
538
539
|
);
|
|
539
540
|
if (!validation.valid) {
|
|
540
541
|
profile.capabilities_schema_issues = validation.issues.map((issue) => ({
|