@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
package/dist/lib/server/auth.mjs
CHANGED
|
@@ -208,7 +208,13 @@ function respondUnauthorized(_req, res, options = {}) {
|
|
|
208
208
|
if (options.errorDescription) parts.push(`error_description="${escapeQuotes(options.errorDescription)}"`);
|
|
209
209
|
if (options.resourceMetadata) parts.push(`resource_metadata="${escapeQuotes(options.resourceMetadata)}"`);
|
|
210
210
|
const scheme = useSignatureChallenge ? "Signature" : "Bearer";
|
|
211
|
+
const extra = Object.fromEntries(
|
|
212
|
+
Object.entries(options.headers ?? {}).filter(
|
|
213
|
+
([name]) => !["content-type", "www-authenticate"].includes(name.toLowerCase())
|
|
214
|
+
)
|
|
215
|
+
);
|
|
211
216
|
res.writeHead(options.status ?? 401, {
|
|
217
|
+
...extra,
|
|
212
218
|
"Content-Type": "application/json",
|
|
213
219
|
"WWW-Authenticate": `${scheme} ${parts.join(", ")}`
|
|
214
220
|
});
|
|
@@ -1438,6 +1438,8 @@ function buildSignedRequestsPreTransport(signedRequests, effectiveRequestSigning
|
|
|
1438
1438
|
"Web Bot Auth requires origin revocation: use createIdentity33PrincipalResolver({ revocation }) or supply isRevoked"
|
|
1439
1439
|
);
|
|
1440
1440
|
const requiredFor = effectiveRequestSigning.required_for ?? [];
|
|
1441
|
+
const advertisedProfiles = effectiveRequestSigning.profiles;
|
|
1442
|
+
const webBotAuthAdvertised = !!signedRequests.webBotAuth && !!advertisedProfiles?.includes("web-bot-auth");
|
|
1441
1443
|
const verifier = (0, import_middleware.createExpressVerifier)({
|
|
1442
1444
|
capability: {
|
|
1443
1445
|
supported: true,
|
|
@@ -1543,6 +1545,19 @@ function buildSignedRequestsPreTransport(signedRequests, effectiveRequestSigning
|
|
|
1543
1545
|
res.setHeader(k, v);
|
|
1544
1546
|
return {
|
|
1545
1547
|
json(body) {
|
|
1548
|
+
if (webBotAuthAdvertised && code === 401 && body?.error === "request_signature_required") {
|
|
1549
|
+
const hasBody = !!req.rawBody;
|
|
1550
|
+
if ((0, import_web_bot_auth2.advertisesOnlyWebBotAuth)(advertisedProfiles)) {
|
|
1551
|
+
const refused = (0, import_web_bot_auth2.unsignedWebBotAuthError)(hasBody);
|
|
1552
|
+
res.removeHeader("WWW-Authenticate");
|
|
1553
|
+
res.statusCode = refused.status;
|
|
1554
|
+
for (const [name, value] of Object.entries(refused.headers)) res.setHeader(name, value);
|
|
1555
|
+
res.setHeader("Content-Type", "application/json");
|
|
1556
|
+
res.end(JSON.stringify(refused.body));
|
|
1557
|
+
return;
|
|
1558
|
+
}
|
|
1559
|
+
res.setHeader("Accept-Signature", (0, import_web_bot_auth2.webBotAuthAcceptSignature)("sig1", hasBody));
|
|
1560
|
+
}
|
|
1546
1561
|
res.setHeader("Content-Type", "application/json");
|
|
1547
1562
|
res.end(JSON.stringify(body));
|
|
1548
1563
|
}
|
|
@@ -2127,6 +2142,7 @@ function createAdcpServer(config) {
|
|
|
2127
2142
|
throw new Error("Select Web Bot Auth on each trusted webhook registration, not as a server-wide default");
|
|
2128
2143
|
if (config.webhooks?.webBotAuth && !config.identity33)
|
|
2129
2144
|
throw new Error("Web Bot Auth webhook signing requires the identity33 experiment");
|
|
2145
|
+
if (config.webhooks?.webBotAuth?.key) (0, import_web_bot_auth.assertWebBotAuthSigningKey)(config.webhooks.webBotAuth.key);
|
|
2130
2146
|
const toolsWithErrorArm = (0, import_error_arm_tools.getToolsWithErrorArm)(adcpVersion);
|
|
2131
2147
|
const isProduction = process.env.NODE_ENV === "production";
|
|
2132
2148
|
const requestValidationMode = validationConfig?.requests ?? (isProduction ? "off" : "strict");
|
|
@@ -4558,9 +4574,9 @@ function createAdcpServer(config) {
|
|
|
4558
4574
|
algorithms: [
|
|
4559
4575
|
.../* @__PURE__ */ new Set([
|
|
4560
4576
|
algorithm,
|
|
4561
|
-
|
|
4562
|
-
|
|
4563
|
-
] : []
|
|
4577
|
+
// `algorithms` lists AdCP webhook-profile values; a Web Bot Auth key
|
|
4578
|
+
// is Ed25519 or RSA-PSS (adcp#7894), and only Ed25519 has a value here.
|
|
4579
|
+
...webhooks.webBotAuth && ("privateKey" in webhooks.webBotAuth.key ? webhooks.webBotAuth.key.privateKey.crv : webhooks.webBotAuth.key.publicKey.crv) === "Ed25519" ? ["ed25519"] : []
|
|
4564
4580
|
])
|
|
4565
4581
|
],
|
|
4566
4582
|
...webhooks.webBotAuth && { profiles: ["adcp/webhook-signing/v1", "web-bot-auth"] },
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getWebBotAuthLegacyOriginGate } from "../signing/web-bot-auth.mjs";
|
|
1
|
+
import { assertWebBotAuthSigningKey, getWebBotAuthLegacyOriginGate } from "../signing/web-bot-auth.mjs";
|
|
2
2
|
import {
|
|
3
3
|
isIdentity33SchemaScope,
|
|
4
4
|
withIdentity33SchemaScope,
|
|
@@ -12,6 +12,9 @@ import {
|
|
|
12
12
|
webBotAuthToolFailure,
|
|
13
13
|
selectSignatureProfile,
|
|
14
14
|
verifyWebBotAuth,
|
|
15
|
+
advertisesOnlyWebBotAuth,
|
|
16
|
+
unsignedWebBotAuthError,
|
|
17
|
+
webBotAuthAcceptSignature,
|
|
15
18
|
isWebBotAuthRevocationChecked,
|
|
16
19
|
webBotAuthUnavailableResponse,
|
|
17
20
|
WebBotAuthUnavailableError,
|
|
@@ -1526,6 +1529,8 @@ function buildSignedRequestsPreTransport(signedRequests, effectiveRequestSigning
|
|
|
1526
1529
|
"Web Bot Auth requires origin revocation: use createIdentity33PrincipalResolver({ revocation }) or supply isRevoked"
|
|
1527
1530
|
);
|
|
1528
1531
|
const requiredFor = effectiveRequestSigning.required_for ?? [];
|
|
1532
|
+
const advertisedProfiles = effectiveRequestSigning.profiles;
|
|
1533
|
+
const webBotAuthAdvertised = !!signedRequests.webBotAuth && !!advertisedProfiles?.includes("web-bot-auth");
|
|
1529
1534
|
const verifier = createExpressVerifier({
|
|
1530
1535
|
capability: {
|
|
1531
1536
|
supported: true,
|
|
@@ -1631,6 +1636,19 @@ function buildSignedRequestsPreTransport(signedRequests, effectiveRequestSigning
|
|
|
1631
1636
|
res.setHeader(k, v);
|
|
1632
1637
|
return {
|
|
1633
1638
|
json(body) {
|
|
1639
|
+
if (webBotAuthAdvertised && code === 401 && body?.error === "request_signature_required") {
|
|
1640
|
+
const hasBody = !!req.rawBody;
|
|
1641
|
+
if (advertisesOnlyWebBotAuth(advertisedProfiles)) {
|
|
1642
|
+
const refused = unsignedWebBotAuthError(hasBody);
|
|
1643
|
+
res.removeHeader("WWW-Authenticate");
|
|
1644
|
+
res.statusCode = refused.status;
|
|
1645
|
+
for (const [name, value] of Object.entries(refused.headers)) res.setHeader(name, value);
|
|
1646
|
+
res.setHeader("Content-Type", "application/json");
|
|
1647
|
+
res.end(JSON.stringify(refused.body));
|
|
1648
|
+
return;
|
|
1649
|
+
}
|
|
1650
|
+
res.setHeader("Accept-Signature", webBotAuthAcceptSignature("sig1", hasBody));
|
|
1651
|
+
}
|
|
1634
1652
|
res.setHeader("Content-Type", "application/json");
|
|
1635
1653
|
res.end(JSON.stringify(body));
|
|
1636
1654
|
}
|
|
@@ -2215,6 +2233,7 @@ function createAdcpServer(config) {
|
|
|
2215
2233
|
throw new Error("Select Web Bot Auth on each trusted webhook registration, not as a server-wide default");
|
|
2216
2234
|
if (config.webhooks?.webBotAuth && !config.identity33)
|
|
2217
2235
|
throw new Error("Web Bot Auth webhook signing requires the identity33 experiment");
|
|
2236
|
+
if (config.webhooks?.webBotAuth?.key) assertWebBotAuthSigningKey(config.webhooks.webBotAuth.key);
|
|
2218
2237
|
const toolsWithErrorArm = getToolsWithErrorArm(adcpVersion);
|
|
2219
2238
|
const isProduction = process.env.NODE_ENV === "production";
|
|
2220
2239
|
const requestValidationMode = validationConfig?.requests ?? (isProduction ? "off" : "strict");
|
|
@@ -4646,9 +4665,9 @@ function createAdcpServer(config) {
|
|
|
4646
4665
|
algorithms: [
|
|
4647
4666
|
.../* @__PURE__ */ new Set([
|
|
4648
4667
|
algorithm,
|
|
4649
|
-
|
|
4650
|
-
|
|
4651
|
-
] : []
|
|
4668
|
+
// `algorithms` lists AdCP webhook-profile values; a Web Bot Auth key
|
|
4669
|
+
// is Ed25519 or RSA-PSS (adcp#7894), and only Ed25519 has a value here.
|
|
4670
|
+
...webhooks.webBotAuth && ("privateKey" in webhooks.webBotAuth.key ? webhooks.webBotAuth.key.privateKey.crv : webhooks.webBotAuth.key.publicKey.crv) === "Ed25519" ? ["ed25519"] : []
|
|
4652
4671
|
])
|
|
4653
4672
|
],
|
|
4654
4673
|
...webhooks.webBotAuth && { profiles: ["adcp/webhook-signing/v1", "web-bot-auth"] },
|
|
@@ -311,7 +311,10 @@ function projectResponseCreativeIdentities(value, operation, legacyFormatConvert
|
|
|
311
311
|
let projected = dataRecord;
|
|
312
312
|
if ((0, import_types2.isProjectionProductInput)(projected)) {
|
|
313
313
|
if (wireMode === "canonical") {
|
|
314
|
-
const canonical = (0, import_augment_response.toCanonicalOnlyResponse)(
|
|
314
|
+
const canonical = (0, import_augment_response.toCanonicalOnlyResponse)(
|
|
315
|
+
{ products: [projected] },
|
|
316
|
+
{ legacyFormatConverter, _inferDualEmissionLegacyRefs: false }
|
|
317
|
+
);
|
|
315
318
|
if (canonical.diagnostics.length > 0 || canonical.response.products.length !== 1) {
|
|
316
319
|
throw new import_creative_delivery.CreativeFormatProjectionError(
|
|
317
320
|
operation,
|
|
@@ -550,7 +553,8 @@ function asCanonicalProductResponse(response, legacyFormatConverter) {
|
|
|
550
553
|
assertSafeGetProductsResponse(response);
|
|
551
554
|
validateDualProductFormatDeclarations(response, legacyFormatConverter);
|
|
552
555
|
const projected = (0, import_augment_response.toCanonicalOnlyResponse)(response, {
|
|
553
|
-
legacyFormatConverter
|
|
556
|
+
legacyFormatConverter,
|
|
557
|
+
_inferDualEmissionLegacyRefs: false
|
|
554
558
|
});
|
|
555
559
|
const blockingDiagnostics = projected.diagnostics.filter(
|
|
556
560
|
(diagnostic) => diagnostic.code !== "LEGACY_FORMAT_ID_DROPPED_UNMAPPED"
|
|
@@ -343,7 +343,10 @@ function projectResponseCreativeIdentities(value, operation, legacyFormatConvert
|
|
|
343
343
|
let projected = dataRecord;
|
|
344
344
|
if (isProjectionProductInput(projected)) {
|
|
345
345
|
if (wireMode === "canonical") {
|
|
346
|
-
const canonical = toCanonicalOnlyResponse(
|
|
346
|
+
const canonical = toCanonicalOnlyResponse(
|
|
347
|
+
{ products: [projected] },
|
|
348
|
+
{ legacyFormatConverter, _inferDualEmissionLegacyRefs: false }
|
|
349
|
+
);
|
|
347
350
|
if (canonical.diagnostics.length > 0 || canonical.response.products.length !== 1) {
|
|
348
351
|
throw new CreativeFormatProjectionError(
|
|
349
352
|
operation,
|
|
@@ -582,7 +585,8 @@ function asCanonicalProductResponse(response, legacyFormatConverter) {
|
|
|
582
585
|
assertSafeGetProductsResponse(response);
|
|
583
586
|
validateDualProductFormatDeclarations(response, legacyFormatConverter);
|
|
584
587
|
const projected = toCanonicalOnlyResponse(response, {
|
|
585
|
-
legacyFormatConverter
|
|
588
|
+
legacyFormatConverter,
|
|
589
|
+
_inferDualEmissionLegacyRefs: false
|
|
586
590
|
});
|
|
587
591
|
const blockingDiagnostics = projected.diagnostics.filter(
|
|
588
592
|
(diagnostic) => diagnostic.code !== "LEGACY_FORMAT_ID_DROPPED_UNMAPPED"
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* one-time fetch token sellers store in their secret manager; `delete_*`
|
|
22
22
|
* revokes the token. Tokens are scoped per-seller for revocation.
|
|
23
23
|
*
|
|
24
|
-
*
|
|
24
|
+
* For standalone list-token reads, use registerPropertyListTool from @adcp/sdk/server.
|
|
25
25
|
*
|
|
26
26
|
* @public
|
|
27
27
|
*/
|
|
@@ -42,19 +42,19 @@ export type ListCollectionListsPayload = ServerPayload<ListCollectionListsRespon
|
|
|
42
42
|
export type DeleteCollectionListPayload = ServerPayload<DeleteCollectionListResponse>;
|
|
43
43
|
export interface PropertyListsPlatform<TCtxMeta = Record<string, unknown>> {
|
|
44
44
|
/**
|
|
45
|
-
* Create a property list. Returns
|
|
45
|
+
* Create a property list. Returns an `auth_token` the buyer stores in
|
|
46
46
|
* their secret manager. Token is scoped to this list_id; MUST NOT be
|
|
47
47
|
* reused across lists.
|
|
48
48
|
*/
|
|
49
49
|
createPropertyList(req: CreatePropertyListRequest, ctx: Ctx<TCtxMeta>): Promise<CreatePropertyListPayload>;
|
|
50
50
|
/** Patch an existing property list. */
|
|
51
51
|
updatePropertyList(req: UpdatePropertyListRequest, ctx: Ctx<TCtxMeta>): Promise<UpdatePropertyListPayload>;
|
|
52
|
-
/** Read a property list by id. Sellers call this with the
|
|
52
|
+
/** Read a property list by id. Sellers call this with the auth_token. */
|
|
53
53
|
getPropertyList(req: GetPropertyListRequest, ctx: Ctx<TCtxMeta>): Promise<GetPropertyListPayload>;
|
|
54
54
|
/** Discover property lists the caller is authorized to read. */
|
|
55
55
|
listPropertyLists(req: ListPropertyListsRequest, ctx: Ctx<TCtxMeta>): Promise<ListPropertyListsPayload>;
|
|
56
56
|
/**
|
|
57
|
-
* Delete a property list. MUST revoke the
|
|
57
|
+
* Delete a property list. MUST revoke the auth_token immediately and
|
|
58
58
|
* signal cache invalidation to sellers (reduced cache_valid_until or
|
|
59
59
|
* a list-changed webhook). Compromise-driven revocation MUST also
|
|
60
60
|
* trigger this path.
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* one-time fetch token sellers store in their secret manager; `delete_*`
|
|
22
22
|
* revokes the token. Tokens are scoped per-seller for revocation.
|
|
23
23
|
*
|
|
24
|
-
*
|
|
24
|
+
* For standalone list-token reads, use registerPropertyListTool from @adcp/sdk/server.
|
|
25
25
|
*
|
|
26
26
|
* @public
|
|
27
27
|
*/
|
|
@@ -42,19 +42,19 @@ export type ListCollectionListsPayload = ServerPayload<ListCollectionListsRespon
|
|
|
42
42
|
export type DeleteCollectionListPayload = ServerPayload<DeleteCollectionListResponse>;
|
|
43
43
|
export interface PropertyListsPlatform<TCtxMeta = Record<string, unknown>> {
|
|
44
44
|
/**
|
|
45
|
-
* Create a property list. Returns
|
|
45
|
+
* Create a property list. Returns an `auth_token` the buyer stores in
|
|
46
46
|
* their secret manager. Token is scoped to this list_id; MUST NOT be
|
|
47
47
|
* reused across lists.
|
|
48
48
|
*/
|
|
49
49
|
createPropertyList(req: CreatePropertyListRequest, ctx: Ctx<TCtxMeta>): Promise<CreatePropertyListPayload>;
|
|
50
50
|
/** Patch an existing property list. */
|
|
51
51
|
updatePropertyList(req: UpdatePropertyListRequest, ctx: Ctx<TCtxMeta>): Promise<UpdatePropertyListPayload>;
|
|
52
|
-
/** Read a property list by id. Sellers call this with the
|
|
52
|
+
/** Read a property list by id. Sellers call this with the auth_token. */
|
|
53
53
|
getPropertyList(req: GetPropertyListRequest, ctx: Ctx<TCtxMeta>): Promise<GetPropertyListPayload>;
|
|
54
54
|
/** Discover property lists the caller is authorized to read. */
|
|
55
55
|
listPropertyLists(req: ListPropertyListsRequest, ctx: Ctx<TCtxMeta>): Promise<ListPropertyListsPayload>;
|
|
56
56
|
/**
|
|
57
|
-
* Delete a property list. MUST revoke the
|
|
57
|
+
* Delete a property list. MUST revoke the auth_token immediately and
|
|
58
58
|
* signal cache invalidation to sellers (reduced cache_valid_until or
|
|
59
59
|
* a list-changed webhook). Compromise-driven revocation MUST also
|
|
60
60
|
* trigger this path.
|
|
@@ -105,3 +105,6 @@ export { createDerivedAccountStore, type DerivedAccountStoreOptions, type Derive
|
|
|
105
105
|
export { ConformanceClient, WebSocketTransport, type ConformanceClientOptions, type ConformanceStatus, } from './socket-mode/index.mjs';
|
|
106
106
|
export { mediaBuyActionResolver } from './media-buy-action-resolver.mjs';
|
|
107
107
|
export type { SellerActionDecision, SellerActionResolutionOptions, SellerActionResolution, } from './media-buy-action-resolver.mjs';
|
|
108
|
+
export * from '../identifiers.mjs';
|
|
109
|
+
export { registerPropertyListTool, sendPropertyListChanged } from './property-lists.mjs';
|
|
110
|
+
export type { PropertyListPage, PropertyListToolOptions, PropertyListChangedNotification, SendPropertyListChangedOptions, } from './property-lists.mjs';
|
|
@@ -105,3 +105,6 @@ export { createDerivedAccountStore, type DerivedAccountStoreOptions, type Derive
|
|
|
105
105
|
export { ConformanceClient, WebSocketTransport, type ConformanceClientOptions, type ConformanceStatus, } from './socket-mode';
|
|
106
106
|
export { mediaBuyActionResolver } from './media-buy-action-resolver';
|
|
107
107
|
export type { SellerActionDecision, SellerActionResolutionOptions, SellerActionResolution, } from './media-buy-action-resolver';
|
|
108
|
+
export * from '../identifiers';
|
|
109
|
+
export { registerPropertyListTool, sendPropertyListChanged } from './property-lists';
|
|
110
|
+
export type { PropertyListPage, PropertyListToolOptions, PropertyListChangedNotification, SendPropertyListChangedOptions, } from './property-lists';
|
package/dist/lib/server/index.js
CHANGED
|
@@ -254,6 +254,7 @@ __export(server_exports, {
|
|
|
254
254
|
redisWebhookDeliveryRecoveryBackend: () => import_webhook_delivery.redisWebhookDeliveryRecoveryBackend,
|
|
255
255
|
redisWebhookDeliveryStore: () => import_webhook_delivery.redisWebhookDeliveryStore,
|
|
256
256
|
registerAdcpTaskTool: () => import_tasks.registerAdcpTaskTool,
|
|
257
|
+
registerPropertyListTool: () => import_property_lists.registerPropertyListTool,
|
|
257
258
|
registerTestController: () => import_test_controller.registerTestController,
|
|
258
259
|
replaceAccountFinancialsIfSeeded: () => import_test_controller_bridge.replaceAccountFinancialsIfSeeded,
|
|
259
260
|
requireAuthenticatedOrSigned: () => import_auth_signature.requireAuthenticatedOrSigned,
|
|
@@ -269,6 +270,7 @@ __export(server_exports, {
|
|
|
269
270
|
scopeCtxMetadataKey: () => import_ctx_metadata.scopeCtxMetadataKey,
|
|
270
271
|
scopedStore: () => import_state_store.scopedStore,
|
|
271
272
|
scrubExtensions: () => import_wire_safe.scrubExtensions,
|
|
273
|
+
sendPropertyListChanged: () => import_property_lists.sendPropertyListChanged,
|
|
272
274
|
serve: () => import_serve.serve,
|
|
273
275
|
signatureErrorCodeFromCause: () => import_auth.signatureErrorCodeFromCause,
|
|
274
276
|
stripCtxMetadata: () => import_ctx_metadata.stripCtxMetadata,
|
|
@@ -353,6 +355,8 @@ var import_keyed_account_backend = require('../adapters/keyed-account-backend.js
|
|
|
353
355
|
var import_derived_account_store = require('../adapters/derived-account-store.js');
|
|
354
356
|
var import_socket_mode = require('./socket-mode/index.js');
|
|
355
357
|
var import_media_buy_action_resolver = require('./media-buy-action-resolver.js');
|
|
358
|
+
__reExport(server_exports, require('../identifiers.js'), module.exports);
|
|
359
|
+
var import_property_lists = require('./property-lists.js');
|
|
356
360
|
// Annotate the CommonJS export names for ESM import in node:
|
|
357
361
|
0 && (module.exports = {
|
|
358
362
|
A2AInvocationError,
|
|
@@ -590,6 +594,7 @@ var import_media_buy_action_resolver = require('./media-buy-action-resolver.js')
|
|
|
590
594
|
redisWebhookDeliveryRecoveryBackend,
|
|
591
595
|
redisWebhookDeliveryStore,
|
|
592
596
|
registerAdcpTaskTool,
|
|
597
|
+
registerPropertyListTool,
|
|
593
598
|
registerTestController,
|
|
594
599
|
replaceAccountFinancialsIfSeeded,
|
|
595
600
|
requireAuthenticatedOrSigned,
|
|
@@ -605,6 +610,7 @@ var import_media_buy_action_resolver = require('./media-buy-action-resolver.js')
|
|
|
605
610
|
scopeCtxMetadataKey,
|
|
606
611
|
scopedStore,
|
|
607
612
|
scrubExtensions,
|
|
613
|
+
sendPropertyListChanged,
|
|
608
614
|
serve,
|
|
609
615
|
signatureErrorCodeFromCause,
|
|
610
616
|
stripCtxMetadata,
|
|
@@ -631,5 +637,6 @@ var import_media_buy_action_resolver = require('./media-buy-action-resolver.js')
|
|
|
631
637
|
verifyProposalTermsDigest,
|
|
632
638
|
verifySignatureAsAuthenticator,
|
|
633
639
|
wrapEnvelope,
|
|
634
|
-
...require('./decisioning/index.js')
|
|
640
|
+
...require('./decisioning/index.js'),
|
|
641
|
+
...require('../identifiers.js')
|
|
635
642
|
});
|
|
@@ -302,6 +302,8 @@ import {
|
|
|
302
302
|
WebSocketTransport
|
|
303
303
|
} from "./socket-mode/index.mjs";
|
|
304
304
|
import { mediaBuyActionResolver } from "./media-buy-action-resolver.mjs";
|
|
305
|
+
export * from "../identifiers.mjs";
|
|
306
|
+
import { registerPropertyListTool, sendPropertyListChanged } from "./property-lists.mjs";
|
|
305
307
|
export {
|
|
306
308
|
A2AInvocationError,
|
|
307
309
|
ACCOUNT_NOTIFICATION_TYPES,
|
|
@@ -538,6 +540,7 @@ export {
|
|
|
538
540
|
redisWebhookDeliveryRecoveryBackend,
|
|
539
541
|
redisWebhookDeliveryStore,
|
|
540
542
|
registerAdcpTaskTool,
|
|
543
|
+
registerPropertyListTool,
|
|
541
544
|
registerTestController,
|
|
542
545
|
replaceAccountFinancialsIfSeeded,
|
|
543
546
|
requireAuthenticatedOrSigned,
|
|
@@ -553,6 +556,7 @@ export {
|
|
|
553
556
|
scopeCtxMetadataKey,
|
|
554
557
|
scopedStore,
|
|
555
558
|
scrubExtensions,
|
|
559
|
+
sendPropertyListChanged,
|
|
556
560
|
serve,
|
|
557
561
|
signatureErrorCodeFromCause,
|
|
558
562
|
stripCtxMetadata,
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import type { GetPropertyListRequest, GetPropertyListResponse } from '../types/tools.generated.mjs';
|
|
3
|
+
import type { WebhookEmitter, WebhookEmitParams, WebhookEmitResult } from './webhook-emitter.mjs';
|
|
4
|
+
export type PropertyListPage = Pick<GetPropertyListResponse, 'list' | 'identifiers' | 'pagination' | 'resolved_at' | 'cache_valid_until' | 'coverage_gaps'>;
|
|
5
|
+
export interface PropertyListToolOptions<TPrincipal> {
|
|
6
|
+
/** Verify this token's access to this exact list on every request/page. Return null/false to deny access. */
|
|
7
|
+
verifyToken: (input: {
|
|
8
|
+
listId: string;
|
|
9
|
+
token: string;
|
|
10
|
+
}) => TPrincipal | null | false | Promise<TPrincipal | null | false>;
|
|
11
|
+
/** Load an authorized page. Honor resolve:false without resolving identifiers. Cursors must be bound to the list and principal. */
|
|
12
|
+
loadPage: (request: GetPropertyListRequest, principal: TPrincipal) => Promise<PropertyListPage>;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Register a read-only MCP tool using per-list tokens, independently of normal
|
|
16
|
+
* platform account credentials. Tokens arrive through Authorization: Bearer
|
|
17
|
+
* or x-adcp-auth, never tool arguments. Mount on an MCP endpoint whose HTTP
|
|
18
|
+
* auth layer permits list tokens through to this verifier.
|
|
19
|
+
*/
|
|
20
|
+
export declare function registerPropertyListTool<TPrincipal>(server: McpServer, options: PropertyListToolOptions<TPrincipal>): import("@modelcontextprotocol/sdk/server/mcp.js").RegisteredTool;
|
|
21
|
+
export interface PropertyListChangedNotification {
|
|
22
|
+
list_id: string;
|
|
23
|
+
list_name?: string;
|
|
24
|
+
resolved_at: string;
|
|
25
|
+
cache_valid_until?: string;
|
|
26
|
+
change_summary?: {
|
|
27
|
+
properties_added?: number;
|
|
28
|
+
properties_removed?: number;
|
|
29
|
+
total_properties?: number;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export type SendPropertyListChangedOptions = Pick<WebhookEmitParams, 'url' | 'delivery_id' | 'retries' | 'attemptAuthorizationContext'> & {
|
|
33
|
+
notification: PropertyListChangedNotification;
|
|
34
|
+
};
|
|
35
|
+
/** Send a summary-only notification through the SDK's signed, retrying delivery kernel. Reuse delivery_id and notification for retries. */
|
|
36
|
+
export declare function sendPropertyListChanged(emitter: WebhookEmitter, options: SendPropertyListChangedOptions): Promise<WebhookEmitResult>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import type { GetPropertyListRequest, GetPropertyListResponse } from '../types/tools.generated';
|
|
3
|
+
import type { WebhookEmitter, WebhookEmitParams, WebhookEmitResult } from './webhook-emitter';
|
|
4
|
+
export type PropertyListPage = Pick<GetPropertyListResponse, 'list' | 'identifiers' | 'pagination' | 'resolved_at' | 'cache_valid_until' | 'coverage_gaps'>;
|
|
5
|
+
export interface PropertyListToolOptions<TPrincipal> {
|
|
6
|
+
/** Verify this token's access to this exact list on every request/page. Return null/false to deny access. */
|
|
7
|
+
verifyToken: (input: {
|
|
8
|
+
listId: string;
|
|
9
|
+
token: string;
|
|
10
|
+
}) => TPrincipal | null | false | Promise<TPrincipal | null | false>;
|
|
11
|
+
/** Load an authorized page. Honor resolve:false without resolving identifiers. Cursors must be bound to the list and principal. */
|
|
12
|
+
loadPage: (request: GetPropertyListRequest, principal: TPrincipal) => Promise<PropertyListPage>;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Register a read-only MCP tool using per-list tokens, independently of normal
|
|
16
|
+
* platform account credentials. Tokens arrive through Authorization: Bearer
|
|
17
|
+
* or x-adcp-auth, never tool arguments. Mount on an MCP endpoint whose HTTP
|
|
18
|
+
* auth layer permits list tokens through to this verifier.
|
|
19
|
+
*/
|
|
20
|
+
export declare function registerPropertyListTool<TPrincipal>(server: McpServer, options: PropertyListToolOptions<TPrincipal>): import("@modelcontextprotocol/sdk/server/mcp.js").RegisteredTool;
|
|
21
|
+
export interface PropertyListChangedNotification {
|
|
22
|
+
list_id: string;
|
|
23
|
+
list_name?: string;
|
|
24
|
+
resolved_at: string;
|
|
25
|
+
cache_valid_until?: string;
|
|
26
|
+
change_summary?: {
|
|
27
|
+
properties_added?: number;
|
|
28
|
+
properties_removed?: number;
|
|
29
|
+
total_properties?: number;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export type SendPropertyListChangedOptions = Pick<WebhookEmitParams, 'url' | 'delivery_id' | 'retries' | 'attemptAuthorizationContext'> & {
|
|
33
|
+
notification: PropertyListChangedNotification;
|
|
34
|
+
};
|
|
35
|
+
/** Send a summary-only notification through the SDK's signed, retrying delivery kernel. Reuse delivery_id and notification for retries. */
|
|
36
|
+
export declare function sendPropertyListChanged(emitter: WebhookEmitter, options: SendPropertyListChangedOptions): Promise<WebhookEmitResult>;
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var property_lists_exports = {};
|
|
20
|
+
__export(property_lists_exports, {
|
|
21
|
+
registerPropertyListTool: () => registerPropertyListTool,
|
|
22
|
+
sendPropertyListChanged: () => sendPropertyListChanged
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(property_lists_exports);
|
|
25
|
+
var import_zod = require("zod");
|
|
26
|
+
var import_schemas = require('../types/schemas.generated.js');
|
|
27
|
+
var import_wire_shape = require('./ctx-metadata/wire-shape.js');
|
|
28
|
+
function registerPropertyListTool(server, options) {
|
|
29
|
+
return server.registerTool(
|
|
30
|
+
"get_property_list",
|
|
31
|
+
{
|
|
32
|
+
description: "Retrieve an authorized property list and a page of resolved identifiers.",
|
|
33
|
+
inputSchema: import_schemas.GetPropertyListRequestSchema,
|
|
34
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true }
|
|
35
|
+
},
|
|
36
|
+
async (request, extra) => {
|
|
37
|
+
const denied = () => toolError("PERMISSION_DENIED", "Property list access denied.", request.context);
|
|
38
|
+
const headers = extra.requestInfo?.headers ?? {};
|
|
39
|
+
for (const name of ["authorization", "x-adcp-auth"]) {
|
|
40
|
+
const values = Object.entries(headers).filter(([key]) => key.toLowerCase() === name);
|
|
41
|
+
if (values.length > 1 || values.length === 1 && typeof values[0][1] !== "string") return denied();
|
|
42
|
+
}
|
|
43
|
+
const readHeader = (name) => {
|
|
44
|
+
const values = Object.entries(headers).filter(([key]) => key.toLowerCase() === name);
|
|
45
|
+
if (values.length !== 1 || typeof values[0][1] !== "string") return void 0;
|
|
46
|
+
return values[0][1];
|
|
47
|
+
};
|
|
48
|
+
const authorization = readHeader("authorization");
|
|
49
|
+
const bearer = authorization?.match(/^Bearer ([^\s]+)$/i)?.[1];
|
|
50
|
+
const adcpToken = readHeader("x-adcp-auth");
|
|
51
|
+
const authenticatedToken = extra.authInfo?.token;
|
|
52
|
+
const tokens = [bearer, adcpToken, authenticatedToken].filter((token) => token !== void 0);
|
|
53
|
+
if (!tokens.length || tokens.some((token) => !token || token !== tokens[0]) || authorization !== void 0 && !bearer)
|
|
54
|
+
return denied();
|
|
55
|
+
let principal;
|
|
56
|
+
try {
|
|
57
|
+
principal = await options.verifyToken({ listId: request.list_id, token: tokens[0] });
|
|
58
|
+
} catch {
|
|
59
|
+
return denied();
|
|
60
|
+
}
|
|
61
|
+
if (principal === null || principal === false || principal === void 0) return denied();
|
|
62
|
+
try {
|
|
63
|
+
const page = await options.loadPage(request, principal);
|
|
64
|
+
if (page.list.list_id !== request.list_id) throw new Error("Invalid list identity");
|
|
65
|
+
if (request.resolve !== false && !Array.isArray(page.identifiers))
|
|
66
|
+
throw new Error("Missing resolved identifiers");
|
|
67
|
+
const limit = request.pagination?.max_results ?? 1e3;
|
|
68
|
+
if (request.resolve !== false && (page.identifiers?.length ?? 0) > limit) throw new Error("Invalid page size");
|
|
69
|
+
if (page.pagination?.has_more && !page.pagination.cursor) throw new Error("Missing continuation cursor");
|
|
70
|
+
if (!page.pagination?.has_more && page.pagination?.cursor) throw new Error("Unexpected continuation cursor");
|
|
71
|
+
const payload = import_schemas.GetPropertyListResponseSchema.parse({
|
|
72
|
+
list: Object.fromEntries(
|
|
73
|
+
[
|
|
74
|
+
"list_id",
|
|
75
|
+
"name",
|
|
76
|
+
"description",
|
|
77
|
+
"account",
|
|
78
|
+
"base_properties",
|
|
79
|
+
"filters",
|
|
80
|
+
"brand",
|
|
81
|
+
"webhook_url",
|
|
82
|
+
"cache_duration_hours",
|
|
83
|
+
"created_at",
|
|
84
|
+
"updated_at",
|
|
85
|
+
"property_count",
|
|
86
|
+
"pricing_options"
|
|
87
|
+
].filter((key) => Object.hasOwn(page.list, key)).map((key) => [key, page.list[key]])
|
|
88
|
+
),
|
|
89
|
+
...page.identifiers !== void 0 ? { identifiers: page.identifiers.map((identifier) => ({ type: identifier.type, value: identifier.value })) } : {},
|
|
90
|
+
...page.pagination !== void 0 ? {
|
|
91
|
+
pagination: {
|
|
92
|
+
has_more: page.pagination.has_more,
|
|
93
|
+
cursor: page.pagination.cursor,
|
|
94
|
+
total_count: page.pagination.total_count
|
|
95
|
+
}
|
|
96
|
+
} : {},
|
|
97
|
+
...page.resolved_at !== void 0 ? { resolved_at: page.resolved_at } : {},
|
|
98
|
+
...page.cache_valid_until !== void 0 ? { cache_valid_until: page.cache_valid_until } : {},
|
|
99
|
+
...page.coverage_gaps !== void 0 ? { coverage_gaps: page.coverage_gaps } : {},
|
|
100
|
+
status: "completed",
|
|
101
|
+
...request.resolve === false ? { identifiers: void 0, pagination: void 0 } : {},
|
|
102
|
+
...request.context ? { context: request.context } : {}
|
|
103
|
+
});
|
|
104
|
+
(0, import_wire_shape.stripCtxMetadata)({ property_list: payload.list });
|
|
105
|
+
return { content: [{ type: "text", text: JSON.stringify(payload) }], structuredContent: payload };
|
|
106
|
+
} catch {
|
|
107
|
+
return toolError("INTERNAL_ERROR", "Property list could not be loaded.", request.context);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
function toolError(code, message, context) {
|
|
113
|
+
const payload = { status: "failed", errors: [{ code, message }], ...context !== void 0 ? { context } : {} };
|
|
114
|
+
return {
|
|
115
|
+
isError: true,
|
|
116
|
+
content: [{ type: "text", text: JSON.stringify(payload) }],
|
|
117
|
+
structuredContent: payload
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
async function sendPropertyListChanged(emitter, options) {
|
|
121
|
+
const { notification, ...delivery } = options;
|
|
122
|
+
const dateTime = import_zod.z.string().datetime({ offset: true });
|
|
123
|
+
if (!notification.list_id || !dateTime.safeParse(notification.resolved_at).success || notification.cache_valid_until !== void 0 && !dateTime.safeParse(notification.cache_valid_until).success) {
|
|
124
|
+
throw new TypeError("Property-list notifications require a list_id and valid timestamps");
|
|
125
|
+
}
|
|
126
|
+
const summary = notification.change_summary === void 0 ? void 0 : Object.fromEntries(
|
|
127
|
+
["properties_added", "properties_removed", "total_properties"].filter(
|
|
128
|
+
(key) => Object.hasOwn(notification.change_summary, key) && notification.change_summary[key] !== void 0
|
|
129
|
+
).map((key) => [key, notification.change_summary[key]])
|
|
130
|
+
);
|
|
131
|
+
for (const count of Object.values(summary ?? {})) {
|
|
132
|
+
if (typeof count !== "number" || !Number.isSafeInteger(count) || count < 0)
|
|
133
|
+
throw new TypeError("Property-list change counts must be nonnegative integers");
|
|
134
|
+
}
|
|
135
|
+
return emitter.emit({
|
|
136
|
+
...delivery,
|
|
137
|
+
authentication: null,
|
|
138
|
+
signingProfile: "adcp/webhook-signing/v1",
|
|
139
|
+
payload: {
|
|
140
|
+
event: "property_list_changed",
|
|
141
|
+
list_id: notification.list_id,
|
|
142
|
+
resolved_at: notification.resolved_at,
|
|
143
|
+
...notification.list_name !== void 0 ? { list_name: notification.list_name } : {},
|
|
144
|
+
...notification.cache_valid_until !== void 0 ? { cache_valid_until: notification.cache_valid_until } : {},
|
|
145
|
+
...summary !== void 0 ? { change_summary: summary } : {},
|
|
146
|
+
signature: "rfc9421"
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
151
|
+
0 && (module.exports = {
|
|
152
|
+
registerPropertyListTool,
|
|
153
|
+
sendPropertyListChanged
|
|
154
|
+
});
|