@adcp/sdk 16.0.0-beta.6 → 16.0.0-beta.8
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 +17 -0
- package/dist/lib/conformance/index.d.mts +2 -0
- package/dist/lib/conformance/index.d.ts +2 -0
- package/dist/lib/conformance/index.js +3 -0
- package/dist/lib/conformance/index.mjs +2 -0
- package/dist/lib/conformance/property-lists.d.mts +14 -0
- package/dist/lib/conformance/property-lists.d.ts +14 -0
- package/dist/lib/conformance/property-lists.js +45 -0
- package/dist/lib/conformance/property-lists.mjs +21 -0
- package/dist/lib/conformance/runConformance.js +20 -1
- package/dist/lib/conformance/runConformance.mjs +20 -1
- package/dist/lib/conformance/types.d.mts +3 -0
- package/dist/lib/conformance/types.d.ts +3 -0
- package/dist/lib/core/SingleAgentClient.d.mts +13 -3
- package/dist/lib/core/SingleAgentClient.d.ts +13 -3
- package/dist/lib/core/SingleAgentClient.js +109 -21
- package/dist/lib/core/SingleAgentClient.mjs +112 -22
- package/dist/lib/core/TaskExecutor.d.mts +1 -0
- package/dist/lib/core/TaskExecutor.d.ts +1 -0
- package/dist/lib/core/TaskExecutor.js +48 -34
- package/dist/lib/core/TaskExecutor.mjs +49 -35
- package/dist/lib/identifiers.d.mts +53 -0
- package/dist/lib/identifiers.d.ts +53 -0
- package/dist/lib/identifiers.js +212 -0
- package/dist/lib/identifiers.mjs +163 -0
- package/dist/lib/identity-3.3/revocation.js +5 -4
- package/dist/lib/identity-3.3/revocation.mjs +5 -4
- package/dist/lib/index.d.mts +3 -1
- package/dist/lib/index.d.ts +3 -1
- package/dist/lib/index.js +2 -0
- package/dist/lib/index.mjs +1 -0
- package/dist/lib/media-buy/index.d.mts +1 -1
- package/dist/lib/media-buy/index.d.ts +1 -1
- package/dist/lib/media-buy/property-policy.d.mts +15 -1
- package/dist/lib/media-buy/property-policy.d.ts +15 -1
- package/dist/lib/media-buy/property-policy.js +135 -3
- package/dist/lib/media-buy/property-policy.mjs +134 -3
- package/dist/lib/registry/index.d.mts +12 -0
- package/dist/lib/registry/index.d.ts +12 -0
- package/dist/lib/registry/index.js +159 -5
- package/dist/lib/registry/index.mjs +156 -4
- package/dist/lib/registry/types.d.mts +23 -0
- package/dist/lib/registry/types.d.ts +23 -0
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/server/auth-signature.js +23 -2
- package/dist/lib/server/auth-signature.mjs +26 -2
- package/dist/lib/server/auth.d.mts +6 -0
- package/dist/lib/server/auth.d.ts +6 -0
- package/dist/lib/server/auth.js +6 -0
- package/dist/lib/server/auth.mjs +6 -0
- package/dist/lib/server/create-adcp-server.js +19 -3
- package/dist/lib/server/create-adcp-server.mjs +23 -4
- package/dist/lib/server/decisioning/runtime/from-platform.js +6 -2
- package/dist/lib/server/decisioning/runtime/from-platform.mjs +6 -2
- package/dist/lib/server/decisioning/specialisms/lists.d.mts +4 -4
- package/dist/lib/server/decisioning/specialisms/lists.d.ts +4 -4
- package/dist/lib/server/index.d.mts +3 -0
- package/dist/lib/server/index.d.ts +3 -0
- package/dist/lib/server/index.js +8 -1
- package/dist/lib/server/index.mjs +4 -0
- package/dist/lib/server/property-lists.d.mts +36 -0
- package/dist/lib/server/property-lists.d.ts +36 -0
- package/dist/lib/server/property-lists.js +154 -0
- package/dist/lib/server/property-lists.mjs +129 -0
- package/dist/lib/server/serve.js +2 -1
- package/dist/lib/server/serve.mjs +2 -1
- package/dist/lib/server/targeting-helpers.d.mts +2 -0
- package/dist/lib/server/targeting-helpers.d.ts +2 -0
- package/dist/lib/server/targeting-helpers.js +28 -1
- package/dist/lib/server/targeting-helpers.mjs +18 -1
- package/dist/lib/server/webhook-emitter.js +1 -0
- package/dist/lib/server/webhook-emitter.mjs +2 -1
- package/dist/lib/signing/crypto.d.mts +1 -1
- package/dist/lib/signing/crypto.d.ts +1 -1
- package/dist/lib/signing/crypto.js +37 -2
- package/dist/lib/signing/crypto.mjs +37 -2
- package/dist/lib/signing/parser.d.mts +6 -0
- package/dist/lib/signing/parser.d.ts +6 -0
- package/dist/lib/signing/parser.js +7 -3
- package/dist/lib/signing/parser.mjs +6 -3
- package/dist/lib/signing/server.d.mts +1 -1
- package/dist/lib/signing/server.d.ts +1 -1
- package/dist/lib/signing/server.mjs +5 -1
- package/dist/lib/signing/web-bot-auth.d.mts +61 -22
- package/dist/lib/signing/web-bot-auth.d.ts +61 -22
- package/dist/lib/signing/web-bot-auth.js +286 -266
- package/dist/lib/signing/web-bot-auth.mjs +288 -277
- package/dist/lib/signing/webhook-verifier.d.mts +11 -0
- package/dist/lib/signing/webhook-verifier.d.ts +11 -0
- package/dist/lib/signing/webhook-verifier.js +11 -3
- package/dist/lib/signing/webhook-verifier.mjs +11 -3
- package/dist/lib/supply-path/verify.js +12 -2
- package/dist/lib/supply-path/verify.mjs +2 -2
- package/dist/lib/testing/client.js +3 -5
- package/dist/lib/testing/client.mjs +4 -6
- package/dist/lib/testing/compliance/comply.js +7 -2
- package/dist/lib/testing/compliance/comply.mjs +8 -3
- package/dist/lib/testing/index.d.mts +1 -1
- package/dist/lib/testing/index.d.ts +1 -1
- package/dist/lib/testing/index.js +4 -2
- package/dist/lib/testing/index.mjs +8 -2
- package/dist/lib/testing/storyboard/runner.js +12 -12
- package/dist/lib/testing/storyboard/runner.mjs +14 -13
- package/dist/lib/testing/storyboard/validations.d.mts +1 -1
- package/dist/lib/testing/storyboard/validations.d.ts +1 -1
- package/dist/lib/testing/storyboard/validations.js +23 -1
- package/dist/lib/testing/storyboard/validations.mjs +28 -2
- package/dist/lib/testing/types.d.mts +39 -0
- package/dist/lib/testing/types.d.ts +39 -0
- package/dist/lib/v2/projection/augment-response.js +6 -3
- package/dist/lib/v2/projection/augment-response.mjs +8 -5
- package/dist/lib/v2/projection/creative-delivery.js +29 -17
- package/dist/lib/v2/projection/creative-delivery.mjs +31 -18
- package/dist/lib/v2/projection/v1-to-v2.d.mts +7 -0
- package/dist/lib/v2/projection/v1-to-v2.d.ts +7 -0
- package/dist/lib/v2/projection/v1-to-v2.js +70 -2
- package/dist/lib/v2/projection/v1-to-v2.mjs +72 -1
- package/dist/lib/validation/index.d.mts +1 -1
- package/dist/lib/validation/index.d.ts +1 -1
- package/dist/lib/validation/index.js +4 -2
- package/dist/lib/validation/index.mjs +4 -2
- package/dist/lib/validation/schema-loader.d.mts +26 -0
- package/dist/lib/validation/schema-loader.d.ts +26 -0
- package/dist/lib/validation/schema-loader.js +98 -14
- package/dist/lib/validation/schema-loader.mjs +95 -13
- 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 +2 -2
- package/docs/guides/PROPERTY-LISTS.md +264 -0
- package/docs/guides/SIGNING-GUIDE.md +25 -0
- package/docs/guides/VALIDATE-YOUR-AGENT.md +34 -0
- package/docs/llms.txt +2 -2
- package/docs/migration-14.x-rc-worksheet.md +4 -4
- package/docs/migration-agent-identity-3.3.md +60 -23
- package/package.json +1 -1
|
@@ -54,7 +54,9 @@ __export(schema_loader_exports, {
|
|
|
54
54
|
unregisterExternalSchemaRoot: () => unregisterExternalSchemaRoot,
|
|
55
55
|
validateAdcpVersionWire: () => validateAdcpVersionWire,
|
|
56
56
|
withExternalSchemaRoot: () => withExternalSchemaRoot,
|
|
57
|
-
withIdentity33SchemaScope: () => withIdentity33SchemaScope
|
|
57
|
+
withIdentity33SchemaScope: () => withIdentity33SchemaScope,
|
|
58
|
+
withServedReleaseSchemaRoots: () => withServedReleaseSchemaRoots,
|
|
59
|
+
withServedValidationVersion: () => withServedValidationVersion
|
|
58
60
|
});
|
|
59
61
|
module.exports = __toCommonJS(schema_loader_exports);
|
|
60
62
|
var import_ajv = __toESM(require("ajv"));
|
|
@@ -755,28 +757,108 @@ function hasSchemaBundle(version) {
|
|
|
755
757
|
return false;
|
|
756
758
|
}
|
|
757
759
|
}
|
|
758
|
-
const
|
|
759
|
-
const
|
|
760
|
-
function
|
|
761
|
-
|
|
762
|
-
if (
|
|
760
|
+
const servedReleaseSchemaRootScope = new import_async_hooks.AsyncLocalStorage();
|
|
761
|
+
const servedReleaseRootAliases = /* @__PURE__ */ new Map();
|
|
762
|
+
function withServedReleaseSchemaRoots(roots, fn) {
|
|
763
|
+
if (roots === void 0) return fn();
|
|
764
|
+
if (roots === null || typeof roots !== "object" || Array.isArray(roots)) {
|
|
765
|
+
throw new TypeError("servedReleaseSchemaRoots must be an object mapping AdCP versions to schema roots");
|
|
766
|
+
}
|
|
767
|
+
const entries = [];
|
|
768
|
+
for (const [version, root] of Object.entries(roots)) {
|
|
769
|
+
resolveBundleKey(version);
|
|
770
|
+
if (typeof root !== "string" || root.length === 0) {
|
|
771
|
+
throw new TypeError(`servedReleaseSchemaRoots["${version}"] must be a non-empty schema root path`);
|
|
772
|
+
}
|
|
773
|
+
const memoKey = `${version}\0${root}`;
|
|
774
|
+
let aliases = servedReleaseRootAliases.get(memoKey);
|
|
775
|
+
if (!aliases) {
|
|
776
|
+
if (!hasSchemaRootShape(root)) {
|
|
777
|
+
throw new Error(`Served-release AdCP schema root for version "${version}" not found or empty at ${root}`);
|
|
778
|
+
}
|
|
779
|
+
aliases = assertSchemaRootMatchesVersion(version, root);
|
|
780
|
+
servedReleaseRootAliases.set(memoKey, aliases);
|
|
781
|
+
}
|
|
782
|
+
const conflict = entries.find((entry) => entry.root !== root && [...aliases].some((alias) => entry.aliases.has(alias)));
|
|
783
|
+
if (conflict) {
|
|
784
|
+
throw new Error(
|
|
785
|
+
`servedReleaseSchemaRoots["${version}"] and servedReleaseSchemaRoots["${conflict.version}"] name the same release with different schema roots (${root}, ${conflict.root})`
|
|
786
|
+
);
|
|
787
|
+
}
|
|
788
|
+
entries.push({ version, root, aliases });
|
|
789
|
+
}
|
|
790
|
+
if (entries.length === 0) return fn();
|
|
791
|
+
const parent = servedReleaseSchemaRootScope.getStore() ?? [];
|
|
792
|
+
return servedReleaseSchemaRootScope.run([...entries, ...parent], fn);
|
|
793
|
+
}
|
|
794
|
+
function findServedReleaseSchemaRoot(release) {
|
|
795
|
+
const entries = servedReleaseSchemaRootScope.getStore();
|
|
796
|
+
if (!entries?.length) return void 0;
|
|
797
|
+
let key;
|
|
763
798
|
try {
|
|
764
|
-
|
|
799
|
+
key = resolveBundleKey(release);
|
|
765
800
|
} catch {
|
|
766
|
-
return
|
|
801
|
+
return void 0;
|
|
767
802
|
}
|
|
768
|
-
|
|
769
|
-
|
|
803
|
+
return entries.find((entry) => entry.aliases.has(key) || entry.aliases.has(release));
|
|
804
|
+
}
|
|
805
|
+
const SERVED_RELEASE_MAJOR = /^(\d+)\.\d+(?:-[a-zA-Z0-9.-]+)?$/;
|
|
806
|
+
const NUMERIC_PIN_MAJOR = /^(\d+)\.\d+/;
|
|
807
|
+
function listsSupportedRelease(response, release) {
|
|
808
|
+
const adcp = response.adcp;
|
|
809
|
+
if (adcp === null || typeof adcp !== "object") return false;
|
|
810
|
+
const supported = adcp.supported_versions;
|
|
811
|
+
if (!Array.isArray(supported)) return false;
|
|
812
|
+
return supported.some((entry) => {
|
|
813
|
+
if (typeof entry !== "string") return false;
|
|
814
|
+
if (entry === release) return true;
|
|
815
|
+
try {
|
|
816
|
+
return toReleasePrecisionWire(entry) === release;
|
|
817
|
+
} catch {
|
|
818
|
+
return false;
|
|
819
|
+
}
|
|
820
|
+
});
|
|
821
|
+
}
|
|
822
|
+
function selectServedValidationVersion(response, pinnedVersion) {
|
|
823
|
+
const pin = { version: pinnedVersion };
|
|
824
|
+
const pinMajor = NUMERIC_PIN_MAJOR.exec(pinnedVersion)?.[1];
|
|
825
|
+
if (pinMajor === void 0) return pin;
|
|
826
|
+
if (response === null || typeof response !== "object") return pin;
|
|
827
|
+
const echoed = response.adcp_version;
|
|
828
|
+
if (typeof echoed !== "string") return pin;
|
|
770
829
|
const echoMajor = SERVED_RELEASE_MAJOR.exec(echoed)?.[1];
|
|
771
|
-
if (echoMajor === void 0 || echoMajor !== pinMajor) return
|
|
830
|
+
if (echoMajor === void 0 || echoMajor !== pinMajor) return pin;
|
|
772
831
|
let pinnedRelease;
|
|
773
832
|
try {
|
|
774
833
|
pinnedRelease = toReleasePrecisionWire(pinnedVersion);
|
|
834
|
+
} catch {
|
|
835
|
+
return pin;
|
|
836
|
+
}
|
|
837
|
+
if (echoed === pinnedRelease) return pin;
|
|
838
|
+
if (!listsSupportedRelease(response, echoed)) return pin;
|
|
839
|
+
const served = findServedReleaseSchemaRoot(echoed);
|
|
840
|
+
if (served) return { version: echoed, served };
|
|
841
|
+
return hasSchemaBundle(echoed) ? { version: echoed } : pin;
|
|
842
|
+
}
|
|
843
|
+
function resolveServedValidationVersion(response, pinnedVersion) {
|
|
844
|
+
try {
|
|
845
|
+
return selectServedValidationVersion(response, pinnedVersion).version;
|
|
775
846
|
} catch {
|
|
776
847
|
return pinnedVersion;
|
|
777
848
|
}
|
|
778
|
-
|
|
779
|
-
|
|
849
|
+
}
|
|
850
|
+
function withServedValidationVersion(response, pinnedVersion, fn) {
|
|
851
|
+
let selection;
|
|
852
|
+
try {
|
|
853
|
+
selection = selectServedValidationVersion(response, pinnedVersion);
|
|
854
|
+
} catch {
|
|
855
|
+
selection = { version: pinnedVersion };
|
|
856
|
+
}
|
|
857
|
+
const { version, served } = selection;
|
|
858
|
+
if (!served) return fn(version);
|
|
859
|
+
const next = new Map(scopedExternalSchemaRoots.getStore());
|
|
860
|
+
for (const alias of served.aliases) next.set(alias, served.root);
|
|
861
|
+
return scopedExternalSchemaRoots.run(next, () => fn(version));
|
|
780
862
|
}
|
|
781
863
|
const mcpToolSchemaCache = /* @__PURE__ */ new Map();
|
|
782
864
|
const mcpToolSummaryCache = /* @__PURE__ */ new Map();
|
|
@@ -1049,5 +1131,7 @@ function cloneJsonValue(value) {
|
|
|
1049
1131
|
unregisterExternalSchemaRoot,
|
|
1050
1132
|
validateAdcpVersionWire,
|
|
1051
1133
|
withExternalSchemaRoot,
|
|
1052
|
-
withIdentity33SchemaScope
|
|
1134
|
+
withIdentity33SchemaScope,
|
|
1135
|
+
withServedReleaseSchemaRoots,
|
|
1136
|
+
withServedValidationVersion
|
|
1053
1137
|
});
|
|
@@ -701,28 +701,108 @@ function hasSchemaBundle(version) {
|
|
|
701
701
|
return false;
|
|
702
702
|
}
|
|
703
703
|
}
|
|
704
|
-
const
|
|
705
|
-
const
|
|
706
|
-
function
|
|
707
|
-
|
|
708
|
-
if (
|
|
704
|
+
const servedReleaseSchemaRootScope = new AsyncLocalStorage();
|
|
705
|
+
const servedReleaseRootAliases = /* @__PURE__ */ new Map();
|
|
706
|
+
function withServedReleaseSchemaRoots(roots, fn) {
|
|
707
|
+
if (roots === void 0) return fn();
|
|
708
|
+
if (roots === null || typeof roots !== "object" || Array.isArray(roots)) {
|
|
709
|
+
throw new TypeError("servedReleaseSchemaRoots must be an object mapping AdCP versions to schema roots");
|
|
710
|
+
}
|
|
711
|
+
const entries = [];
|
|
712
|
+
for (const [version, root] of Object.entries(roots)) {
|
|
713
|
+
resolveBundleKey(version);
|
|
714
|
+
if (typeof root !== "string" || root.length === 0) {
|
|
715
|
+
throw new TypeError(`servedReleaseSchemaRoots["${version}"] must be a non-empty schema root path`);
|
|
716
|
+
}
|
|
717
|
+
const memoKey = `${version}\0${root}`;
|
|
718
|
+
let aliases = servedReleaseRootAliases.get(memoKey);
|
|
719
|
+
if (!aliases) {
|
|
720
|
+
if (!hasSchemaRootShape(root)) {
|
|
721
|
+
throw new Error(`Served-release AdCP schema root for version "${version}" not found or empty at ${root}`);
|
|
722
|
+
}
|
|
723
|
+
aliases = assertSchemaRootMatchesVersion(version, root);
|
|
724
|
+
servedReleaseRootAliases.set(memoKey, aliases);
|
|
725
|
+
}
|
|
726
|
+
const conflict = entries.find((entry) => entry.root !== root && [...aliases].some((alias) => entry.aliases.has(alias)));
|
|
727
|
+
if (conflict) {
|
|
728
|
+
throw new Error(
|
|
729
|
+
`servedReleaseSchemaRoots["${version}"] and servedReleaseSchemaRoots["${conflict.version}"] name the same release with different schema roots (${root}, ${conflict.root})`
|
|
730
|
+
);
|
|
731
|
+
}
|
|
732
|
+
entries.push({ version, root, aliases });
|
|
733
|
+
}
|
|
734
|
+
if (entries.length === 0) return fn();
|
|
735
|
+
const parent = servedReleaseSchemaRootScope.getStore() ?? [];
|
|
736
|
+
return servedReleaseSchemaRootScope.run([...entries, ...parent], fn);
|
|
737
|
+
}
|
|
738
|
+
function findServedReleaseSchemaRoot(release) {
|
|
739
|
+
const entries = servedReleaseSchemaRootScope.getStore();
|
|
740
|
+
if (!entries?.length) return void 0;
|
|
741
|
+
let key;
|
|
709
742
|
try {
|
|
710
|
-
|
|
743
|
+
key = resolveBundleKey(release);
|
|
711
744
|
} catch {
|
|
712
|
-
return
|
|
745
|
+
return void 0;
|
|
713
746
|
}
|
|
714
|
-
|
|
715
|
-
|
|
747
|
+
return entries.find((entry) => entry.aliases.has(key) || entry.aliases.has(release));
|
|
748
|
+
}
|
|
749
|
+
const SERVED_RELEASE_MAJOR = /^(\d+)\.\d+(?:-[a-zA-Z0-9.-]+)?$/;
|
|
750
|
+
const NUMERIC_PIN_MAJOR = /^(\d+)\.\d+/;
|
|
751
|
+
function listsSupportedRelease(response, release) {
|
|
752
|
+
const adcp = response.adcp;
|
|
753
|
+
if (adcp === null || typeof adcp !== "object") return false;
|
|
754
|
+
const supported = adcp.supported_versions;
|
|
755
|
+
if (!Array.isArray(supported)) return false;
|
|
756
|
+
return supported.some((entry) => {
|
|
757
|
+
if (typeof entry !== "string") return false;
|
|
758
|
+
if (entry === release) return true;
|
|
759
|
+
try {
|
|
760
|
+
return toReleasePrecisionWire(entry) === release;
|
|
761
|
+
} catch {
|
|
762
|
+
return false;
|
|
763
|
+
}
|
|
764
|
+
});
|
|
765
|
+
}
|
|
766
|
+
function selectServedValidationVersion(response, pinnedVersion) {
|
|
767
|
+
const pin = { version: pinnedVersion };
|
|
768
|
+
const pinMajor = NUMERIC_PIN_MAJOR.exec(pinnedVersion)?.[1];
|
|
769
|
+
if (pinMajor === void 0) return pin;
|
|
770
|
+
if (response === null || typeof response !== "object") return pin;
|
|
771
|
+
const echoed = response.adcp_version;
|
|
772
|
+
if (typeof echoed !== "string") return pin;
|
|
716
773
|
const echoMajor = SERVED_RELEASE_MAJOR.exec(echoed)?.[1];
|
|
717
|
-
if (echoMajor === void 0 || echoMajor !== pinMajor) return
|
|
774
|
+
if (echoMajor === void 0 || echoMajor !== pinMajor) return pin;
|
|
718
775
|
let pinnedRelease;
|
|
719
776
|
try {
|
|
720
777
|
pinnedRelease = toReleasePrecisionWire(pinnedVersion);
|
|
778
|
+
} catch {
|
|
779
|
+
return pin;
|
|
780
|
+
}
|
|
781
|
+
if (echoed === pinnedRelease) return pin;
|
|
782
|
+
if (!listsSupportedRelease(response, echoed)) return pin;
|
|
783
|
+
const served = findServedReleaseSchemaRoot(echoed);
|
|
784
|
+
if (served) return { version: echoed, served };
|
|
785
|
+
return hasSchemaBundle(echoed) ? { version: echoed } : pin;
|
|
786
|
+
}
|
|
787
|
+
function resolveServedValidationVersion(response, pinnedVersion) {
|
|
788
|
+
try {
|
|
789
|
+
return selectServedValidationVersion(response, pinnedVersion).version;
|
|
721
790
|
} catch {
|
|
722
791
|
return pinnedVersion;
|
|
723
792
|
}
|
|
724
|
-
|
|
725
|
-
|
|
793
|
+
}
|
|
794
|
+
function withServedValidationVersion(response, pinnedVersion, fn) {
|
|
795
|
+
let selection;
|
|
796
|
+
try {
|
|
797
|
+
selection = selectServedValidationVersion(response, pinnedVersion);
|
|
798
|
+
} catch {
|
|
799
|
+
selection = { version: pinnedVersion };
|
|
800
|
+
}
|
|
801
|
+
const { version, served } = selection;
|
|
802
|
+
if (!served) return fn(version);
|
|
803
|
+
const next = new Map(scopedExternalSchemaRoots.getStore());
|
|
804
|
+
for (const alias of served.aliases) next.set(alias, served.root);
|
|
805
|
+
return scopedExternalSchemaRoots.run(next, () => fn(version));
|
|
726
806
|
}
|
|
727
807
|
const mcpToolSchemaCache = /* @__PURE__ */ new Map();
|
|
728
808
|
const mcpToolSummaryCache = /* @__PURE__ */ new Map();
|
|
@@ -994,5 +1074,7 @@ export {
|
|
|
994
1074
|
unregisterExternalSchemaRoot,
|
|
995
1075
|
validateAdcpVersionWire,
|
|
996
1076
|
withExternalSchemaRoot,
|
|
997
|
-
withIdentity33SchemaScope
|
|
1077
|
+
withIdentity33SchemaScope,
|
|
1078
|
+
withServedReleaseSchemaRoots,
|
|
1079
|
+
withServedValidationVersion
|
|
998
1080
|
};
|
package/dist/lib/version.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* AdCP SDK library version
|
|
3
3
|
*/
|
|
4
|
-
export declare const LIBRARY_VERSION = "16.0.0-beta.
|
|
4
|
+
export declare const LIBRARY_VERSION = "16.0.0-beta.8";
|
|
5
5
|
/**
|
|
6
6
|
* AdCP specification version this library is built for
|
|
7
7
|
*/
|
|
@@ -33,10 +33,10 @@ export type AdcpVersion = (typeof COMPATIBLE_ADCP_VERSIONS)[number];
|
|
|
33
33
|
* Full version information
|
|
34
34
|
*/
|
|
35
35
|
export declare const VERSION_INFO: {
|
|
36
|
-
readonly library: "16.0.0-beta.
|
|
36
|
+
readonly library: "16.0.0-beta.8";
|
|
37
37
|
readonly adcp: "3.2.5";
|
|
38
38
|
readonly compatibleVersions: readonly ["v2.5", "v2.6", "v3", "3.0.0", "3.0", "3.0.1", "3.0.2", "3.0.3", "3.0.4", "3.0.5", "3.0.6", "3.0.7", "3.0.8", "3.0.9", "3.0.10", "3.0.11", "3.0.12", "3.0.13", "3.0.14", "3.0.15", "3.0.16", "3.0.17", "3.0.18", "3.0.19", "3.0.20", "3.0.21", "3.0.22", "3.0.23", "3.0.24", "3.0.25", "3.1.0", "3.1", "3.1.1", "3.1.2", "3.1.3", "3.1.4", "3.1.5", "3.1.6", "3.1.7", "3.1.8", "3.1.9", "3.1.10", "3.1.11", "3.1.12", "3.1.13", "3.1.14", "3.1.15", "3.1.16", "3.1.17", "3.1.18", "3.1.19", "3.1.20", "3.1.21", "3.1.22", "3.1.23", "3.1.24", "3.1.25", "3.1.26", "3.1.27", "3.1.28", "3.1.29", "3.2.1", "3.2", "3.2.2", "3.2.3", "3.2.4", "3.2.5"];
|
|
39
|
-
readonly generatedAt: "2026-10-
|
|
39
|
+
readonly generatedAt: "2026-10-18T01:21:23.594Z";
|
|
40
40
|
};
|
|
41
41
|
/**
|
|
42
42
|
* Get the AdCP specification version this library is built for
|
package/dist/lib/version.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* AdCP SDK library version
|
|
3
3
|
*/
|
|
4
|
-
export declare const LIBRARY_VERSION = "16.0.0-beta.
|
|
4
|
+
export declare const LIBRARY_VERSION = "16.0.0-beta.8";
|
|
5
5
|
/**
|
|
6
6
|
* AdCP specification version this library is built for
|
|
7
7
|
*/
|
|
@@ -33,10 +33,10 @@ export type AdcpVersion = (typeof COMPATIBLE_ADCP_VERSIONS)[number];
|
|
|
33
33
|
* Full version information
|
|
34
34
|
*/
|
|
35
35
|
export declare const VERSION_INFO: {
|
|
36
|
-
readonly library: "16.0.0-beta.
|
|
36
|
+
readonly library: "16.0.0-beta.8";
|
|
37
37
|
readonly adcp: "3.2.5";
|
|
38
38
|
readonly compatibleVersions: readonly ["v2.5", "v2.6", "v3", "3.0.0", "3.0", "3.0.1", "3.0.2", "3.0.3", "3.0.4", "3.0.5", "3.0.6", "3.0.7", "3.0.8", "3.0.9", "3.0.10", "3.0.11", "3.0.12", "3.0.13", "3.0.14", "3.0.15", "3.0.16", "3.0.17", "3.0.18", "3.0.19", "3.0.20", "3.0.21", "3.0.22", "3.0.23", "3.0.24", "3.0.25", "3.1.0", "3.1", "3.1.1", "3.1.2", "3.1.3", "3.1.4", "3.1.5", "3.1.6", "3.1.7", "3.1.8", "3.1.9", "3.1.10", "3.1.11", "3.1.12", "3.1.13", "3.1.14", "3.1.15", "3.1.16", "3.1.17", "3.1.18", "3.1.19", "3.1.20", "3.1.21", "3.1.22", "3.1.23", "3.1.24", "3.1.25", "3.1.26", "3.1.27", "3.1.28", "3.1.29", "3.2.1", "3.2", "3.2.2", "3.2.3", "3.2.4", "3.2.5"];
|
|
39
|
-
readonly generatedAt: "2026-10-
|
|
39
|
+
readonly generatedAt: "2026-10-18T01:21:23.594Z";
|
|
40
40
|
};
|
|
41
41
|
/**
|
|
42
42
|
* Get the AdCP specification version this library is built for
|
package/dist/lib/version.js
CHANGED
|
@@ -31,7 +31,7 @@ __export(version_exports, {
|
|
|
31
31
|
toReleasePrecisionVersion: () => toReleasePrecisionVersion
|
|
32
32
|
});
|
|
33
33
|
module.exports = __toCommonJS(version_exports);
|
|
34
|
-
const LIBRARY_VERSION = "16.0.0-beta.
|
|
34
|
+
const LIBRARY_VERSION = "16.0.0-beta.8";
|
|
35
35
|
const ADCP_VERSION = "3.2.5";
|
|
36
36
|
const ADCP_MAJOR_VERSION = 3;
|
|
37
37
|
const COMPATIBLE_ADCP_VERSIONS = [
|
|
@@ -104,10 +104,10 @@ const COMPATIBLE_ADCP_VERSIONS = [
|
|
|
104
104
|
"3.2.5"
|
|
105
105
|
];
|
|
106
106
|
const VERSION_INFO = {
|
|
107
|
-
library: "16.0.0-beta.
|
|
107
|
+
library: "16.0.0-beta.8",
|
|
108
108
|
adcp: "3.2.5",
|
|
109
109
|
compatibleVersions: COMPATIBLE_ADCP_VERSIONS,
|
|
110
|
-
generatedAt: "2026-10-
|
|
110
|
+
generatedAt: "2026-10-18T01:21:23.594Z"
|
|
111
111
|
};
|
|
112
112
|
function getAdcpVersion() {
|
|
113
113
|
return ADCP_VERSION;
|
package/dist/lib/version.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const LIBRARY_VERSION = "16.0.0-beta.
|
|
1
|
+
const LIBRARY_VERSION = "16.0.0-beta.8";
|
|
2
2
|
const ADCP_VERSION = "3.2.5";
|
|
3
3
|
const ADCP_MAJOR_VERSION = 3;
|
|
4
4
|
const COMPATIBLE_ADCP_VERSIONS = [
|
|
@@ -71,10 +71,10 @@ const COMPATIBLE_ADCP_VERSIONS = [
|
|
|
71
71
|
"3.2.5"
|
|
72
72
|
];
|
|
73
73
|
const VERSION_INFO = {
|
|
74
|
-
library: "16.0.0-beta.
|
|
74
|
+
library: "16.0.0-beta.8",
|
|
75
75
|
adcp: "3.2.5",
|
|
76
76
|
compatibleVersions: COMPATIBLE_ADCP_VERSIONS,
|
|
77
|
-
generatedAt: "2026-10-
|
|
77
|
+
generatedAt: "2026-10-18T01:21:23.594Z"
|
|
78
78
|
};
|
|
79
79
|
function getAdcpVersion() {
|
|
80
80
|
return ADCP_VERSION;
|
package/docs/TYPE-SUMMARY.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# AdCP Type Summary
|
|
2
2
|
|
|
3
|
-
> Generated at: 2026-10-
|
|
4
|
-
> @adcp/sdk v16.0.0-beta.
|
|
3
|
+
> Generated at: 2026-10-18
|
|
4
|
+
> @adcp/sdk v16.0.0-beta.8
|
|
5
5
|
|
|
6
6
|
Curated reference of the types that matter for using the AdCP client. For full generated types see `src/lib/types/tools.generated.ts` and `src/lib/types/core.generated.ts`.
|
|
7
7
|
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
# Property lists: matching, registry contribution, and serving
|
|
2
|
+
|
|
3
|
+
## Buyer enforcement
|
|
4
|
+
|
|
5
|
+
`validation.productPropertyPolicy` reads `get_products.filters.property_list`
|
|
6
|
+
and the deprecated top-level `property_list`. When both are present they must
|
|
7
|
+
have the same `agent_url`, `list_id`, and `auth_token`; conflicting references
|
|
8
|
+
fail closed with `property_list_conflicting_references`. A malformed nested
|
|
9
|
+
reference never falls back to the top-level field. The same reference is kept
|
|
10
|
+
for polling, tracking, and webhook completions.
|
|
11
|
+
|
|
12
|
+
These location and mixed-identifier fixes ship together. As before, request
|
|
13
|
+
list enforcement defaults to enabled. Set `enforceRequestPropertyList: false`
|
|
14
|
+
to disable local resolution, or `productPropertyPolicy: false` to disable the
|
|
15
|
+
policy entirely. Resolution errors (including a non-MCP list URL) fail closed.
|
|
16
|
+
|
|
17
|
+
Lists may mix domains, app bundles, and CTV store IDs. Non-domain types no
|
|
18
|
+
longer cause an unconditional response failure. A product's
|
|
19
|
+
`publisher_properties` contains publisher selectors, not app IDs: `property_ids`
|
|
20
|
+
are opaque names scoped to a publisher. Supply complete catalogs to match
|
|
21
|
+
their real identifiers:
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import { AdCPClient, type ProductPolicyResolvedProperty } from '@adcp/sdk';
|
|
25
|
+
|
|
26
|
+
declare const loadPublisherCatalog: (
|
|
27
|
+
domain: string
|
|
28
|
+
) => Promise<readonly ProductPolicyResolvedProperty[] | undefined>;
|
|
29
|
+
|
|
30
|
+
const client = new AdCPClient(agents, {
|
|
31
|
+
validation: {
|
|
32
|
+
productPropertyPolicy: {
|
|
33
|
+
enforceRequestPropertyList: true,
|
|
34
|
+
unknownSelectorBehavior: 'flag',
|
|
35
|
+
resolvePublisherProperties: loadPublisherCatalog,
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The resolver receives normalized, untrusted publisher hostnames and an optional
|
|
42
|
+
`{ signal }` argument. It returns the complete
|
|
43
|
+
catalog of `{ property_id?, identifiers: [{ type, value }], tags? }` records
|
|
44
|
+
from your publisher metadata store or discovery layer. Alternatively provide
|
|
45
|
+
`publisherProperties: { [publisherDomain]: catalog }` directly. Keys use the exact
|
|
46
|
+
normalized publisher hostname, including `www.` when present; catalogs are not
|
|
47
|
+
shared across host aliases. When catalogs are configured, missing or failed
|
|
48
|
+
lookups mean unknown metadata; `[]` means a known empty catalog. This hook does
|
|
49
|
+
not initiate any additional registry or publisher lookups unless you implement
|
|
50
|
+
them. Identifier type and value must match exactly for non-domain identifiers;
|
|
51
|
+
bundle casing is preserved. `all`, `by_id`, and `by_tag` selectors are resolved
|
|
52
|
+
against these records. Products with `property_targeting_allowed: true` may
|
|
53
|
+
retain a proven eligible subset; other products must satisfy the policy for
|
|
54
|
+
every selected property.
|
|
55
|
+
|
|
56
|
+
Catalog loading is limited to 100 distinct lookups per response, 10 seconds
|
|
57
|
+
per lookup, and a 10-second total budget by default (`maxPublisherLookups`,
|
|
58
|
+
`publisherLookupTimeoutMs`, `publisherLookupBudgetMs`).
|
|
59
|
+
Overflow and failed/timed-out lookups stay unknown even when a domain matches.
|
|
60
|
+
Literal IP and localhost inputs do not reach the hook. For network discovery,
|
|
61
|
+
use the SDK's SSRF-safe networking utilities to protect DNS resolution too;
|
|
62
|
+
catalog identity/ownership verification remains the catalog provider's job.
|
|
63
|
+
|
|
64
|
+
Products whose membership cannot be judged follow `unknownSelectorBehavior`
|
|
65
|
+
(`allow`, `flag`, or `reject`). Request-derived policies remain strict by
|
|
66
|
+
default, so the default unknown behavior is `reject`; choose `flag` explicitly
|
|
67
|
+
to retain uncertain products. Known disallowed properties still reject even
|
|
68
|
+
when another property is unknown. Diagnostics appear under
|
|
69
|
+
`metadata.productPropertyPolicy`, including per-product
|
|
70
|
+
`unresolved_property_identifiers` / `identifier_types` and
|
|
71
|
+
`request_property_list.unsupported_identifier_types` for unrecognized types.
|
|
72
|
+
|
|
73
|
+
## Identifier normalization and registration
|
|
74
|
+
|
|
75
|
+
`normalizePropertyIdentifier(type, value)` (also exported as
|
|
76
|
+
`validatePropertyIdentifier`) returns either `{ ok: true, value, rewrite? }`
|
|
77
|
+
or `{ ok: false, error: IdentifierValidationError }`. Named normalizers cover
|
|
78
|
+
domain, subdomain, iOS/Android bundles, Apple/Google/Roku/Samsung store IDs,
|
|
79
|
+
Fire TV ASINs, and Apple TV/generic bundles. Each normalizer validates too.
|
|
80
|
+
|
|
81
|
+
Domain normalization accepts HTTP(S) URLs and reports their reduction to a
|
|
82
|
+
hostname, lowercases IDNs, and preserves wildcards. It preserves `www.` and
|
|
83
|
+
`m.` because removing those labels would broaden an explicit allowlist.
|
|
84
|
+
`subdomain` rejects wildcards. Platform names preserve case; numeric IDs stay
|
|
85
|
+
strings without losing precision. Formats for app/store identifiers are SDK
|
|
86
|
+
input hygiene rules, not assertions of registration, ownership, or existence.
|
|
87
|
+
Opaque identifier families retain their spelling (including station names
|
|
88
|
+
with spaces). Use these opt-in helpers before storing or matching values:
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
import { normalizePropertyIdentifier } from '@adcp/sdk';
|
|
92
|
+
|
|
93
|
+
const checked = normalizePropertyIdentifier('domain', submittedValue);
|
|
94
|
+
if (!checked.ok) throw checked.error;
|
|
95
|
+
await identifierStore.save({ type: 'domain', value: checked.value });
|
|
96
|
+
// checked.rewrite, when present, records { from, to } for your review/audit UI.
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
`validatePublisherDomain(value)` is deliberately stricter: a publisher
|
|
100
|
+
registration must already be a lowercase bare hostname. URLs, wildcards,
|
|
101
|
+
ports, whitespace, trailing dots, malformed labels, and IP literals reject
|
|
102
|
+
with `IdentifierValidationError.code === 'invalid_publisher_domain'`.
|
|
103
|
+
`RegistryClient.saveProperty()` always performs this validation before HTTP.
|
|
104
|
+
`saveProperties()` uses the same check for every record; failed records retain
|
|
105
|
+
the existing `error` string and expose the typed error as `cause`.
|
|
106
|
+
|
|
107
|
+
## Bulk catalog contribution
|
|
108
|
+
|
|
109
|
+
`resolveIdentifiers()` in default `resolve` mode is the primary bulk catalog
|
|
110
|
+
registration path: it contributes provenance, creates missing entries, and
|
|
111
|
+
returns stable `property_rid` handles. Use `mode: 'lookup'` for read-only lookup.
|
|
112
|
+
A RID is a join handle and does not grant advertising authorization.
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
import { RegistryClient, isRegistryRequestError } from '@adcp/sdk';
|
|
116
|
+
|
|
117
|
+
const registry = new RegistryClient({ apiKey: process.env.ADCP_REGISTRY_API_KEY });
|
|
118
|
+
const outcomes = await registry.resolveIdentifiersAll({
|
|
119
|
+
identifiers,
|
|
120
|
+
provenance: { type: 'agency_allowlist', context: campaignId },
|
|
121
|
+
// Omit mode to register/contribute. Use mode: 'lookup' for a pure read.
|
|
122
|
+
}, {
|
|
123
|
+
batchSize: 10_000,
|
|
124
|
+
maxRequestBytes: 1024 * 1024,
|
|
125
|
+
concurrency: 2,
|
|
126
|
+
maxRetries: 3,
|
|
127
|
+
maxRetryDelayMs: 60_000,
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
for (const outcome of outcomes) {
|
|
131
|
+
if (outcome.success) useCatalogHandle(outcome.identifier, outcome.result.property_rid);
|
|
132
|
+
else if (isRegistryRequestError(outcome.error)) {
|
|
133
|
+
reportRegistryFailure(outcome.index, outcome.error.status, outcome.error.retryAfterMs);
|
|
134
|
+
} else reportInputFailure(outcome.index, outcome.error);
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
The helper caps each request at 10,000 identifiers and the configured UTF-8
|
|
139
|
+
JSON byte limit, including provenance. It limits concurrency, honors
|
|
140
|
+
`retryAfterMs` on HTTP 429, and returns an outcome for each input in input order,
|
|
141
|
+
including duplicates. Oversized individual items, missing/malformed response
|
|
142
|
+
entries, and failed batches become per-input errors; successful batches remain
|
|
143
|
+
available. Inputs should already use canonical identifier spelling so registry
|
|
144
|
+
results can be correlated by type and value. Empty input returns `[]`.
|
|
145
|
+
|
|
146
|
+
Only rate limits are retried automatically. A server delay exceeding
|
|
147
|
+
`maxRetryDelayMs` stops pending requests and returns the typed 429 (including
|
|
148
|
+
its `retryAfterMs`) for callers to reschedule, without retrying early.
|
|
149
|
+
Network and 5xx failures may have
|
|
150
|
+
already contributed activity: identifier-to-RID resolution is idempotent, but
|
|
151
|
+
the activity log is additive. The single-call API guards the 10,000-item limit;
|
|
152
|
+
use the helper for byte-based splitting. `/api/registry/resolve` accepts responses
|
|
153
|
+
up to 16 MiB by default; explicit `RegistryClientConfig.maxBodyBytes` still wins.
|
|
154
|
+
|
|
155
|
+
Use `saveProperty()` when publishing or updating hosted publisher
|
|
156
|
+
configuration (names, property identities, tags), rather than contributing a
|
|
157
|
+
large identifier catalog. `saveProperties()` is a bounded client-side fan-out
|
|
158
|
+
over that endpoint, intended for configuration updates. Its results are keyed
|
|
159
|
+
by publisher domain; submit one configuration per domain.
|
|
160
|
+
|
|
161
|
+
Registry HTTP failures use the exported `RegistryRequestError` /
|
|
162
|
+
`isRegistryRequestError` contract, with `status`, `method`, `retryAfterMs`, and
|
|
163
|
+
bounded untrusted `details`. Do not parse `error.message` for status or retry
|
|
164
|
+
timing. Input validation and transport failures are separate errors.
|
|
165
|
+
|
|
166
|
+
## Serve buyer-issued references over MCP
|
|
167
|
+
|
|
168
|
+
`registerPropertyListTool` registers a real MCP `get_property_list` tool using
|
|
169
|
+
the official MCP server. The reference's `auth_token` must authorize its exact
|
|
170
|
+
`list_id`. It arrives in `Authorization: Bearer` or `x-adcp-auth`, never as a
|
|
171
|
+
tool argument. The verifier runs before storage is read, on every page.
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
175
|
+
import { serve } from '@adcp/sdk';
|
|
176
|
+
import { registerPropertyListTool } from '@adcp/sdk/server';
|
|
177
|
+
|
|
178
|
+
serve(() => {
|
|
179
|
+
const server = new McpServer({ name: 'Buyer property lists', version: '1.0.0' });
|
|
180
|
+
registerPropertyListTool(server, {
|
|
181
|
+
verifyToken: ({ listId, token }) => listStore.verifyListToken(listId, token),
|
|
182
|
+
loadPage: (request, principal) => listStore.loadAuthorizedPage(request, principal),
|
|
183
|
+
});
|
|
184
|
+
return server;
|
|
185
|
+
}, {
|
|
186
|
+
path: '/property-lists/mcp',
|
|
187
|
+
publicUrl: 'https://buyer.example/property-lists/mcp',
|
|
188
|
+
authenticate: listEndpointAuthenticator,
|
|
189
|
+
});
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Use your list endpoint's authenticator to validate list tokens globally and
|
|
193
|
+
return `{ principal, token }`; the per-tool verifier still checks exact list
|
|
194
|
+
access. Mount this read-only endpoint separately if your normal platform auth
|
|
195
|
+
only accepts account bearers. Its proxy/auth middleware must allow the list
|
|
196
|
+
token through. Ordinary MCP initialization needs no list ID. The helper also
|
|
197
|
+
supports raw HTTP headers when a transport does not populate `authInfo`.
|
|
198
|
+
It denies conflicting credentials and does not expose verifier/storage errors.
|
|
199
|
+
|
|
200
|
+
`loadPage` receives a verified principal and the validated request, including
|
|
201
|
+
`resolve` and `pagination`. Return `{ list, identifiers, pagination?,
|
|
202
|
+
resolved_at?, cache_valid_until? }`. The list includes at least `list_id` and
|
|
203
|
+
`name`; `identifiers` must be an array when resolving, including `[]` for an
|
|
204
|
+
empty list. For `resolve: false`, return metadata without resolving identifiers.
|
|
205
|
+
Honor the requested page limit (default 1,000; maximum 10,000). Provide a
|
|
206
|
+
nonempty continuation cursor only when `has_more` is true, and bind cursors to
|
|
207
|
+
the list, principal, and catalog revision. `list.property_count` is the total
|
|
208
|
+
property count, not the current page's identifier count. Return coherent
|
|
209
|
+
`cache_valid_until` timestamps across pages. The helper adds completion status
|
|
210
|
+
and echoes request context on success and errors.
|
|
211
|
+
|
|
212
|
+
Issue references whose `agent_url` points at this MCP endpoint. A plain REST
|
|
213
|
+
URL returning JSON does not implement `get_property_list`.
|
|
214
|
+
|
|
215
|
+
## Signed change notifications and certification
|
|
216
|
+
|
|
217
|
+
Use your configured SDK `WebhookEmitter` (or the emitter from
|
|
218
|
+
`createPostgresWebhookRuntime`) so signing, bounded retries, durable delivery
|
|
219
|
+
bindings, and crash recovery use the same infrastructure as other webhooks:
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
import { sendPropertyListChanged } from '@adcp/sdk/server';
|
|
223
|
+
|
|
224
|
+
const result = await sendPropertyListChanged(
|
|
225
|
+
webhooks.emitter.forTenantScope(trustedTenantId),
|
|
226
|
+
{
|
|
227
|
+
url: subscriber.webhookUrl,
|
|
228
|
+
delivery_id: persistedDeliveryId,
|
|
229
|
+
notification: {
|
|
230
|
+
list_id: changedListId,
|
|
231
|
+
resolved_at: resolvedAt.toISOString(),
|
|
232
|
+
change_summary: { properties_added: added, properties_removed: removed, total_properties: total },
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
);
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Retain the same delivery ID and exact notification for retries. Each distinct
|
|
239
|
+
event/subscriber delivery needs its own ID. The helper selects RFC 9421,
|
|
240
|
+
includes the compatibility marker `signature: 'rfc9421'`, and lets the emitter
|
|
241
|
+
bind a stable cryptographic `idempotency_key`. It sends a summary; receivers
|
|
242
|
+
refresh through `get_property_list`. Configure production delivery storage,
|
|
243
|
+
recovery, signing keys, and per-attempt subscription authorization on the
|
|
244
|
+
emitter; see [the runtime reference](../TYPE-SUMMARY.md#postgresql-webhook-runtime).
|
|
245
|
+
|
|
246
|
+
Before certifying a buyer, resolve references it actually issued:
|
|
247
|
+
|
|
248
|
+
```typescript
|
|
249
|
+
import { runPropertyListConformance, runConformance } from '@adcp/sdk/conformance';
|
|
250
|
+
|
|
251
|
+
const report = await runPropertyListConformance(issuedReferences);
|
|
252
|
+
if (report.totalFailures) throw new Error('Buyer-issued property lists are not resolvable');
|
|
253
|
+
|
|
254
|
+
// Or include them in the main certification report/failure count:
|
|
255
|
+
const fullReport = await runConformance(buyerAgentUrl, {
|
|
256
|
+
propertyListReferences: issuedReferences,
|
|
257
|
+
});
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
Certification disables caches, validates each resolved page, checks list
|
|
261
|
+
identity, rejects failure envelopes/oversized pages/cursor cycles, and uses
|
|
262
|
+
the same MCP resolver as sellers. Reports identify inputs by index and never
|
|
263
|
+
echo the reference token, URL, or remote exception. Empty lists are valid;
|
|
264
|
+
unresolvable references fail the report rather than silently certifying.
|
|
@@ -503,6 +503,31 @@ app.post('/webhook', async (req, res) => {
|
|
|
503
503
|
});
|
|
504
504
|
```
|
|
505
505
|
|
|
506
|
+
Webhook-v1 uses unpadded Base64URL for `Signature` throughout AdCP 3.x,
|
|
507
|
+
including 3.2. Its URL canonicalization follows AdCP 3.2 independently of
|
|
508
|
+
that encoding. The 3.2 request-signing migration to padded standard Base64
|
|
509
|
+
does not change the webhook-v1 profile. Python senders should use
|
|
510
|
+
`adcp.signing.webhook_signer.sign_webhook` rather than a generic
|
|
511
|
+
`sign_request` call with `signing_profile_version="3.2"` and the webhook tag.
|
|
512
|
+
See the [protocol's binary-value encoding rules](https://github.com/adcontextprotocol/adcp/blob/v3.2.5/docs/building/by-layer/L1/security.mdx#adcp-rfc-9421-profile).
|
|
513
|
+
|
|
514
|
+
When a known peer uses a generic RFC 9421 signer that emits standard padded
|
|
515
|
+
Base64, configure that receiver route explicitly:
|
|
516
|
+
|
|
517
|
+
```typescript
|
|
518
|
+
await verifyWebhookSignature(req, {
|
|
519
|
+
jwks,
|
|
520
|
+
replayStore,
|
|
521
|
+
revocationStore,
|
|
522
|
+
signatureEncoding: 'rfc8941-base64',
|
|
523
|
+
});
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
`createWebhookVerifier` accepts the same option. Each mode strictly parses
|
|
527
|
+
its configured encoding; neither retries the other encoding. Select this
|
|
528
|
+
option from trusted integration configuration, independently of incoming
|
|
529
|
+
headers or webhook payloads.
|
|
530
|
+
|
|
506
531
|
## Step 6: Sign Outbound Webhooks (Seller)
|
|
507
532
|
|
|
508
533
|
Configure `createAdcpServerFromPlatform` with a webhook signing key:
|