@adcp/sdk 14.0.0 → 14.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (234) hide show
  1. package/dist/lib/adapters/implicit-account-store.d.mts +12 -7
  2. package/dist/lib/adapters/implicit-account-store.d.ts +12 -7
  3. package/dist/lib/adapters/implicit-account-store.js +69 -15
  4. package/dist/lib/adapters/implicit-account-store.mjs +69 -15
  5. package/dist/lib/core/AgentClient.d.mts +1 -0
  6. package/dist/lib/core/AgentClient.d.ts +1 -0
  7. package/dist/lib/core/AgentClient.js +3 -0
  8. package/dist/lib/core/AgentClient.mjs +3 -0
  9. package/dist/lib/core/SingleAgentClient.d.mts +35 -2
  10. package/dist/lib/core/SingleAgentClient.d.ts +35 -2
  11. package/dist/lib/core/SingleAgentClient.js +377 -36
  12. package/dist/lib/core/SingleAgentClient.mjs +387 -38
  13. package/dist/lib/core/TaskExecutor.d.mts +3 -1
  14. package/dist/lib/core/TaskExecutor.d.ts +3 -1
  15. package/dist/lib/core/TaskExecutor.js +17 -12
  16. package/dist/lib/core/TaskExecutor.mjs +17 -12
  17. package/dist/lib/core/account-key.d.mts +3 -0
  18. package/dist/lib/core/account-key.d.ts +3 -0
  19. package/dist/lib/core/account-key.js +41 -0
  20. package/dist/lib/core/account-key.mjs +17 -0
  21. package/dist/lib/core/account-resolution.d.mts +2 -0
  22. package/dist/lib/core/account-resolution.d.ts +2 -0
  23. package/dist/lib/core/buyer-account-registry.d.mts +93 -0
  24. package/dist/lib/core/buyer-account-registry.d.ts +93 -0
  25. package/dist/lib/core/buyer-account-registry.js +602 -0
  26. package/dist/lib/core/buyer-account-registry.mjs +578 -0
  27. package/dist/lib/core/product-cache.d.mts +18 -0
  28. package/dist/lib/core/product-cache.d.ts +18 -0
  29. package/dist/lib/core/product-cache.js +137 -0
  30. package/dist/lib/core/product-cache.mjs +112 -0
  31. package/dist/lib/errors/index.d.mts +40 -1
  32. package/dist/lib/errors/index.d.ts +40 -1
  33. package/dist/lib/errors/index.js +69 -3
  34. package/dist/lib/errors/index.mjs +64 -3
  35. package/dist/lib/governance/authorization.d.mts +17 -1
  36. package/dist/lib/governance/authorization.d.ts +17 -1
  37. package/dist/lib/governance/authorization.js +55 -7
  38. package/dist/lib/governance/authorization.mjs +59 -7
  39. package/dist/lib/governance/index.d.mts +2 -2
  40. package/dist/lib/governance/index.d.ts +2 -2
  41. package/dist/lib/governance/index.js +2 -0
  42. package/dist/lib/governance/index.mjs +3 -1
  43. package/dist/lib/index.d.mts +7 -4
  44. package/dist/lib/index.d.ts +7 -4
  45. package/dist/lib/index.js +29 -0
  46. package/dist/lib/index.mjs +31 -1
  47. package/dist/lib/net/agent-transport-fetch.d.mts +4 -0
  48. package/dist/lib/net/agent-transport-fetch.d.ts +4 -0
  49. package/dist/lib/net/agent-transport-fetch.js +18 -5
  50. package/dist/lib/net/agent-transport-fetch.mjs +17 -5
  51. package/dist/lib/protocols/a2a.js +9 -1
  52. package/dist/lib/protocols/a2a.mjs +9 -1
  53. package/dist/lib/protocols/index.js +9 -2
  54. package/dist/lib/protocols/index.mjs +9 -2
  55. package/dist/lib/protocols/mcp-modern.js +2 -1
  56. package/dist/lib/protocols/mcp-modern.mjs +2 -1
  57. package/dist/lib/protocols/mcp.js +5 -2
  58. package/dist/lib/protocols/mcp.mjs +5 -2
  59. package/dist/lib/protocols/rawResponseCapture.d.mts +6 -0
  60. package/dist/lib/protocols/rawResponseCapture.d.ts +6 -0
  61. package/dist/lib/protocols/rawResponseCapture.js +41 -29
  62. package/dist/lib/protocols/rawResponseCapture.mjs +40 -29
  63. package/dist/lib/protocols/signedRequestRejection.d.mts +9 -0
  64. package/dist/lib/protocols/signedRequestRejection.d.ts +9 -0
  65. package/dist/lib/protocols/signedRequestRejection.js +209 -0
  66. package/dist/lib/protocols/signedRequestRejection.mjs +189 -0
  67. package/dist/lib/protocols/transportDiagnostics.d.mts +1 -0
  68. package/dist/lib/protocols/transportDiagnostics.d.ts +1 -0
  69. package/dist/lib/protocols/transportDiagnostics.js +2 -0
  70. package/dist/lib/protocols/transportDiagnostics.mjs +1 -0
  71. package/dist/lib/registry/types.generated.d.mts +112 -45
  72. package/dist/lib/registry/types.generated.d.ts +112 -45
  73. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  74. package/dist/lib/server/account-provisioning.d.mts +2 -0
  75. package/dist/lib/server/account-provisioning.d.ts +2 -0
  76. package/dist/lib/server/account-provisioning.js +30 -0
  77. package/dist/lib/server/account-provisioning.mjs +6 -0
  78. package/dist/lib/server/account-reference-warnings.d.mts +12 -0
  79. package/dist/lib/server/account-reference-warnings.d.ts +12 -0
  80. package/dist/lib/server/account-reference-warnings.js +48 -0
  81. package/dist/lib/server/account-reference-warnings.mjs +23 -0
  82. package/dist/lib/server/auth-signature.js +1 -0
  83. package/dist/lib/server/auth-signature.mjs +1 -0
  84. package/dist/lib/server/create-adcp-server.d.mts +34 -0
  85. package/dist/lib/server/create-adcp-server.d.ts +34 -0
  86. package/dist/lib/server/create-adcp-server.js +225 -14
  87. package/dist/lib/server/create-adcp-server.mjs +225 -14
  88. package/dist/lib/server/decisioning/account.d.mts +2 -0
  89. package/dist/lib/server/decisioning/account.d.ts +2 -0
  90. package/dist/lib/server/decisioning/runtime/from-platform.js +49 -10
  91. package/dist/lib/server/decisioning/runtime/from-platform.mjs +49 -10
  92. package/dist/lib/server/index.d.mts +2 -2
  93. package/dist/lib/server/index.d.ts +2 -2
  94. package/dist/lib/server/index.js +2 -0
  95. package/dist/lib/server/index.mjs +3 -1
  96. package/dist/lib/signing/agent-resolver/consistency.d.mts +6 -13
  97. package/dist/lib/signing/agent-resolver/consistency.d.ts +6 -13
  98. package/dist/lib/signing/agent-resolver/consistency.js +0 -1
  99. package/dist/lib/signing/agent-resolver/consistency.mjs +0 -1
  100. package/dist/lib/signing/agent-resolver/errors.d.mts +3 -1
  101. package/dist/lib/signing/agent-resolver/errors.d.ts +3 -1
  102. package/dist/lib/signing/agent-resolver/errors.js +6 -0
  103. package/dist/lib/signing/agent-resolver/errors.mjs +6 -0
  104. package/dist/lib/signing/agent-resolver/fetch-helpers.d.mts +11 -0
  105. package/dist/lib/signing/agent-resolver/fetch-helpers.d.ts +11 -0
  106. package/dist/lib/signing/agent-resolver/fetch-helpers.js +37 -2
  107. package/dist/lib/signing/agent-resolver/fetch-helpers.mjs +40 -3
  108. package/dist/lib/signing/agent-resolver/jwks-set.js +24 -3
  109. package/dist/lib/signing/agent-resolver/jwks-set.mjs +24 -3
  110. package/dist/lib/signing/agent-resolver/legacy-brand.d.mts +15 -0
  111. package/dist/lib/signing/agent-resolver/legacy-brand.d.ts +15 -0
  112. package/dist/lib/signing/agent-resolver/legacy-brand.js +60 -0
  113. package/dist/lib/signing/agent-resolver/legacy-brand.mjs +36 -0
  114. package/dist/lib/signing/agent-resolver/operator-authorization.d.mts +13 -0
  115. package/dist/lib/signing/agent-resolver/operator-authorization.d.ts +13 -0
  116. package/dist/lib/signing/agent-resolver/operator-authorization.js +108 -0
  117. package/dist/lib/signing/agent-resolver/operator-authorization.mjs +84 -0
  118. package/dist/lib/signing/agent-resolver/resolve-agent.d.mts +14 -4
  119. package/dist/lib/signing/agent-resolver/resolve-agent.d.ts +14 -4
  120. package/dist/lib/signing/agent-resolver/resolve-agent.js +148 -137
  121. package/dist/lib/signing/agent-resolver/resolve-agent.mjs +157 -139
  122. package/dist/lib/signing/agent-resolver/resolved-agent-jwks.d.mts +7 -1
  123. package/dist/lib/signing/agent-resolver/resolved-agent-jwks.d.ts +7 -1
  124. package/dist/lib/signing/agent-resolver/resolved-agent-jwks.js +50 -17
  125. package/dist/lib/signing/agent-resolver/resolved-agent-jwks.mjs +50 -17
  126. package/dist/lib/signing/agent-resolver/select-agent.d.mts +14 -15
  127. package/dist/lib/signing/agent-resolver/select-agent.d.ts +14 -15
  128. package/dist/lib/signing/agent-resolver/select-agent.js +98 -13
  129. package/dist/lib/signing/agent-resolver/select-agent.mjs +95 -13
  130. package/dist/lib/signing/brand-jwks.d.mts +29 -75
  131. package/dist/lib/signing/brand-jwks.d.ts +29 -75
  132. package/dist/lib/signing/brand-jwks.js +120 -182
  133. package/dist/lib/signing/brand-jwks.mjs +120 -182
  134. package/dist/lib/signing/errors.d.mts +7 -3
  135. package/dist/lib/signing/errors.d.ts +7 -3
  136. package/dist/lib/signing/errors.js +7 -2
  137. package/dist/lib/signing/errors.mjs +7 -2
  138. package/dist/lib/signing/jwks-https.d.mts +7 -0
  139. package/dist/lib/signing/jwks-https.d.ts +7 -0
  140. package/dist/lib/signing/jwks-https.js +31 -8
  141. package/dist/lib/signing/jwks-https.mjs +31 -8
  142. package/dist/lib/signing/jwks.d.mts +8 -0
  143. package/dist/lib/signing/jwks.d.ts +8 -0
  144. package/dist/lib/signing/middleware.js +2 -1
  145. package/dist/lib/signing/middleware.mjs +2 -1
  146. package/dist/lib/signing/publisher-pins.d.mts +11 -0
  147. package/dist/lib/signing/publisher-pins.d.ts +11 -0
  148. package/dist/lib/signing/publisher-pins.js +125 -0
  149. package/dist/lib/signing/publisher-pins.mjs +101 -0
  150. package/dist/lib/signing/server.d.mts +1 -0
  151. package/dist/lib/signing/server.d.ts +1 -0
  152. package/dist/lib/signing/types.d.mts +5 -0
  153. package/dist/lib/signing/types.d.ts +5 -0
  154. package/dist/lib/signing/verifier.js +50 -5
  155. package/dist/lib/signing/verifier.mjs +50 -5
  156. package/dist/lib/signing/webhook-verifier.d.mts +7 -2
  157. package/dist/lib/signing/webhook-verifier.d.ts +7 -2
  158. package/dist/lib/signing/webhook-verifier.js +42 -2
  159. package/dist/lib/signing/webhook-verifier.mjs +42 -2
  160. package/dist/lib/testing/storyboard/account-policy.d.mts +2 -0
  161. package/dist/lib/testing/storyboard/account-policy.d.ts +2 -0
  162. package/dist/lib/testing/storyboard/account-policy.js +35 -0
  163. package/dist/lib/testing/storyboard/account-policy.mjs +11 -0
  164. package/dist/lib/testing/storyboard/context.js +6 -0
  165. package/dist/lib/testing/storyboard/context.mjs +6 -0
  166. package/dist/lib/testing/storyboard/request-builder.js +12 -2
  167. package/dist/lib/testing/storyboard/request-builder.mjs +12 -2
  168. package/dist/lib/testing/storyboard/runner.js +3 -2
  169. package/dist/lib/testing/storyboard/runner.mjs +3 -2
  170. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  171. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  172. package/dist/lib/types/accept-proposal.d.ts +19 -1
  173. package/dist/lib/types/buy-products.d.ts +19 -1
  174. package/dist/lib/types/check-governance.d.ts +19 -1
  175. package/dist/lib/types/comply-test-controller.d.ts +19 -1
  176. package/dist/lib/types/control-media-buy.d.ts +19 -1
  177. package/dist/lib/types/core.generated.d.mts +14 -1
  178. package/dist/lib/types/core.generated.d.ts +14 -1
  179. package/dist/lib/types/create-media-buy.d.ts +14 -1
  180. package/dist/lib/types/get-media-buys.d.ts +14 -1
  181. package/dist/lib/types/get-products.d.ts +19 -1
  182. package/dist/lib/types/list-products.d.ts +19 -1
  183. package/dist/lib/types/refine-proposals.d.ts +19 -1
  184. package/dist/lib/types/request-proposals.d.ts +19 -1
  185. package/dist/lib/types/schemas.generated.d.ts +12 -3
  186. package/dist/lib/types/schemas.generated.js +4 -1
  187. package/dist/lib/types/schemas.generated.mjs +4 -1
  188. package/dist/lib/types/tools.generated.d.mts +14 -1
  189. package/dist/lib/types/tools.generated.d.ts +14 -1
  190. package/dist/lib/types/update-media-buy.d.ts +14 -1
  191. package/dist/lib/version.d.mts +3 -3
  192. package/dist/lib/version.d.ts +3 -3
  193. package/dist/lib/version.js +3 -3
  194. package/dist/lib/version.mjs +3 -3
  195. package/dist/lib/webhooks/index.d.mts +24 -0
  196. package/dist/lib/webhooks/index.d.ts +24 -0
  197. package/dist/lib/webhooks/index.js +50 -24
  198. package/dist/lib/webhooks/index.mjs +49 -24
  199. package/dist/lib/wholesale-feed-sync/index.d.mts +2 -0
  200. package/dist/lib/wholesale-feed-sync/index.d.ts +2 -0
  201. package/dist/lib/wholesale-feed-sync/index.js +7 -0
  202. package/dist/lib/wholesale-feed-sync/index.mjs +4 -0
  203. package/dist/lib/wholesale-feed-sync/mirror.d.mts +97 -0
  204. package/dist/lib/wholesale-feed-sync/mirror.d.ts +97 -0
  205. package/dist/lib/wholesale-feed-sync/mirror.js +350 -0
  206. package/dist/lib/wholesale-feed-sync/mirror.mjs +322 -0
  207. package/dist/lib/wholesale-feed-sync/sync.d.mts +13 -29
  208. package/dist/lib/wholesale-feed-sync/sync.d.ts +13 -29
  209. package/dist/lib/wholesale-feed-sync/sync.js +208 -281
  210. package/dist/lib/wholesale-feed-sync/sync.mjs +213 -281
  211. package/dist/lib/wholesale-feed-sync/types.d.mts +4 -3
  212. package/dist/lib/wholesale-feed-sync/types.d.ts +4 -3
  213. package/docs/README.md +6 -0
  214. package/docs/TYPE-SUMMARY.md +2 -2
  215. package/docs/guides/BUILD-AN-AGENT.md +2 -2
  216. package/docs/guides/BUYER-QUICKSTART-3.2.md +2 -0
  217. package/docs/guides/BUYER-STORAGE.md +3 -0
  218. package/docs/guides/FIRST-CALL-TO-A-SELLER.md +106 -0
  219. package/docs/guides/SIGNING-GUIDE.md +16 -7
  220. package/docs/guides/account-resolution.md +132 -10
  221. package/docs/llms.txt +3 -2
  222. package/docs/migration-14.0-to-14.1.md +85 -0
  223. package/docs/migration-14.x-rc-worksheet.md +4 -4
  224. package/docs/migration-4.x-to-5.x.md +1 -0
  225. package/docs/migration-agent-resolution-3.3.md +125 -0
  226. package/docs/recipes/verifying-inbound-webhooks.md +60 -15
  227. package/package.json +3 -2
  228. package/skills/adcp-brand.previous/SKILL.md +0 -200
  229. package/skills/adcp-creative.previous/SKILL.md +0 -305
  230. package/skills/adcp-governance.previous/SKILL.md +0 -566
  231. package/skills/adcp-measurement.previous/SKILL.md +0 -136
  232. package/skills/adcp-media-buy.previous/SKILL.md +0 -556
  233. package/skills/adcp-si.previous/SKILL.md +0 -206
  234. package/skills/adcp-signals.previous/SKILL.md +0 -204
@@ -22,6 +22,7 @@ __export(webhooks_exports, {
22
22
  WholesaleFeedWebhookNotificationError: () => import_webhook_notification.WholesaleFeedWebhookNotificationError,
23
23
  normalizeWholesaleFeedWebhookNotification: () => import_webhook_notification.normalizeWholesaleFeedWebhookNotification,
24
24
  parseWholesaleFeedWebhookNotification: () => import_webhook_notification.parseWholesaleFeedWebhookNotification,
25
+ preflightWebhookRequest: () => preflightWebhookRequest,
25
26
  verifyWebhookRequest: () => verifyWebhookRequest
26
27
  });
27
28
  module.exports = __toCommonJS(webhooks_exports);
@@ -29,49 +30,73 @@ var import_crypto = require("crypto");
29
30
  __reExport(webhooks_exports, require('../notifications/account-change-recorded.js'), module.exports);
30
31
  var import_webhook_notification = require('../wholesale-feed-sync/webhook-notification.js');
31
32
  function verifyWebhookRequest(options) {
33
+ const preflight = preflightWebhookRequest(options, {
34
+ now: options.now,
35
+ maxSkewSeconds: options.maxSkewSeconds ?? options.skewSeconds
36
+ });
37
+ if (!preflight.ok) return preflight;
32
38
  const secret = options.secret ?? options.globalSecret;
33
39
  if (!secret) {
34
40
  return fail("missing_secret", "A webhook HMAC secret is required.");
35
41
  }
36
- const signature = resolveHeader("x-adcp-signature", options.signature, options.headers);
37
- if (signature.reason) return fail(signature.reason, signature.message);
38
- const timestamp = resolveHeader("x-adcp-timestamp", options.timestamp, options.headers);
39
- if (timestamp.reason) return fail(timestamp.reason, timestamp.message);
40
- if (!signature.value || !timestamp.value) {
41
- return fail("missing_headers", "Webhook is missing x-adcp-signature or x-adcp-timestamp.");
42
- }
43
- const parsedTimestamp = parseTimestamp(timestamp.value);
44
- if (parsedTimestamp === void 0) {
45
- return fail("invalid_timestamp", "x-adcp-timestamp must be an integer unix timestamp in seconds.");
46
- }
47
- const now = options.now ? Math.floor(options.now()) : Math.floor(Date.now() / 1e3);
48
- const skewSeconds = options.skewSeconds ?? 300;
49
- if (!Number.isFinite(skewSeconds) || skewSeconds < 0) {
50
- return fail("invalid_timestamp", "skewSeconds must be a non-negative number.");
51
- }
52
- if (Math.abs(now - parsedTimestamp) > skewSeconds) {
53
- return fail("stale_timestamp", `Webhook timestamp is outside the allowed ${skewSeconds}s skew window.`);
54
- }
55
- if (!/^sha256=[a-f0-9]{64}$/.test(signature.value)) {
56
- return fail("malformed_signature", "x-adcp-signature must match sha256=<64 lowercase hex chars>.");
57
- }
42
+ const { signature, timestamp: parsedTimestamp, checkedAt: now } = preflight;
58
43
  const hmac = (0, import_crypto.createHmac)("sha256", secret);
59
44
  hmac.update(String(parsedTimestamp), "utf8");
60
45
  hmac.update(".", "utf8");
61
46
  hmac.update(toBodyBytes(options.rawBody));
62
47
  const expected = `sha256=${hmac.digest("hex")}`;
63
48
  const expectedBytes = Buffer.from(expected, "utf8");
64
- const actualBytes = Buffer.from(signature.value, "utf8");
49
+ const actualBytes = Buffer.from(signature, "utf8");
65
50
  if (actualBytes.length !== expectedBytes.length || !(0, import_crypto.timingSafeEqual)(actualBytes, expectedBytes)) {
66
51
  return fail("bad_signature", "Webhook HMAC signature does not match the request body and timestamp.");
67
52
  }
68
53
  return {
69
54
  ok: true,
70
- signature: signature.value,
55
+ signature,
71
56
  timestamp: parsedTimestamp,
72
57
  verifiedAt: now
73
58
  };
74
59
  }
60
+ function preflightWebhookRequest(request, options = {}) {
61
+ const signature = resolveHeader("x-adcp-signature", request.signature, request.headers);
62
+ if (signature.reason) return { ok: false, reason: signature.reason, message: signature.message };
63
+ const timestamp = resolveHeader("x-adcp-timestamp", request.timestamp, request.headers);
64
+ if (timestamp.reason) return { ok: false, reason: timestamp.reason, message: timestamp.message };
65
+ if (!signature.value || !timestamp.value)
66
+ return {
67
+ ok: false,
68
+ reason: "missing_headers",
69
+ message: "Webhook is missing x-adcp-signature or x-adcp-timestamp."
70
+ };
71
+ const parsedTimestamp = parseTimestamp(timestamp.value);
72
+ if (parsedTimestamp === void 0)
73
+ return {
74
+ ok: false,
75
+ reason: "invalid_timestamp",
76
+ message: "x-adcp-timestamp must be an integer unix timestamp in seconds."
77
+ };
78
+ const now = Math.floor(options.now ? options.now() : Date.now() / 1e3);
79
+ const skewSeconds = options.maxSkewSeconds ?? options.skewSeconds ?? 300;
80
+ if (!Number.isFinite(now) || !Number.isFinite(skewSeconds) || skewSeconds < 0)
81
+ return {
82
+ ok: false,
83
+ reason: "invalid_timestamp",
84
+ message: "Clock and skew must be finite; skew must be non-negative."
85
+ };
86
+ if (Math.abs(now - parsedTimestamp) > skewSeconds)
87
+ return {
88
+ ok: false,
89
+ reason: "stale_timestamp",
90
+ message: `Webhook timestamp is outside the allowed ${skewSeconds}s skew window.`
91
+ };
92
+ if (!/^sha256=[a-f0-9]{64}$/.test(signature.value))
93
+ return {
94
+ ok: false,
95
+ reason: "malformed_signature",
96
+ message: "x-adcp-signature must match sha256=<64 lowercase hex chars>."
97
+ };
98
+ return { ok: true, signature: signature.value, timestamp: parsedTimestamp, checkedAt: now };
99
+ }
75
100
  function fail(reason, message) {
76
101
  return { ok: false, reason, message };
77
102
  }
@@ -148,6 +173,7 @@ function isHeaders(headers) {
148
173
  WholesaleFeedWebhookNotificationError,
149
174
  normalizeWholesaleFeedWebhookNotification,
150
175
  parseWholesaleFeedWebhookNotification,
176
+ preflightWebhookRequest,
151
177
  verifyWebhookRequest,
152
178
  ...require('../notifications/account-change-recorded.js')
153
179
  });
@@ -6,49 +6,73 @@ import {
6
6
  WholesaleFeedWebhookNotificationError
7
7
  } from "../wholesale-feed-sync/webhook-notification.mjs";
8
8
  function verifyWebhookRequest(options) {
9
+ const preflight = preflightWebhookRequest(options, {
10
+ now: options.now,
11
+ maxSkewSeconds: options.maxSkewSeconds ?? options.skewSeconds
12
+ });
13
+ if (!preflight.ok) return preflight;
9
14
  const secret = options.secret ?? options.globalSecret;
10
15
  if (!secret) {
11
16
  return fail("missing_secret", "A webhook HMAC secret is required.");
12
17
  }
13
- const signature = resolveHeader("x-adcp-signature", options.signature, options.headers);
14
- if (signature.reason) return fail(signature.reason, signature.message);
15
- const timestamp = resolveHeader("x-adcp-timestamp", options.timestamp, options.headers);
16
- if (timestamp.reason) return fail(timestamp.reason, timestamp.message);
17
- if (!signature.value || !timestamp.value) {
18
- return fail("missing_headers", "Webhook is missing x-adcp-signature or x-adcp-timestamp.");
19
- }
20
- const parsedTimestamp = parseTimestamp(timestamp.value);
21
- if (parsedTimestamp === void 0) {
22
- return fail("invalid_timestamp", "x-adcp-timestamp must be an integer unix timestamp in seconds.");
23
- }
24
- const now = options.now ? Math.floor(options.now()) : Math.floor(Date.now() / 1e3);
25
- const skewSeconds = options.skewSeconds ?? 300;
26
- if (!Number.isFinite(skewSeconds) || skewSeconds < 0) {
27
- return fail("invalid_timestamp", "skewSeconds must be a non-negative number.");
28
- }
29
- if (Math.abs(now - parsedTimestamp) > skewSeconds) {
30
- return fail("stale_timestamp", `Webhook timestamp is outside the allowed ${skewSeconds}s skew window.`);
31
- }
32
- if (!/^sha256=[a-f0-9]{64}$/.test(signature.value)) {
33
- return fail("malformed_signature", "x-adcp-signature must match sha256=<64 lowercase hex chars>.");
34
- }
18
+ const { signature, timestamp: parsedTimestamp, checkedAt: now } = preflight;
35
19
  const hmac = createHmac("sha256", secret);
36
20
  hmac.update(String(parsedTimestamp), "utf8");
37
21
  hmac.update(".", "utf8");
38
22
  hmac.update(toBodyBytes(options.rawBody));
39
23
  const expected = `sha256=${hmac.digest("hex")}`;
40
24
  const expectedBytes = Buffer.from(expected, "utf8");
41
- const actualBytes = Buffer.from(signature.value, "utf8");
25
+ const actualBytes = Buffer.from(signature, "utf8");
42
26
  if (actualBytes.length !== expectedBytes.length || !timingSafeEqual(actualBytes, expectedBytes)) {
43
27
  return fail("bad_signature", "Webhook HMAC signature does not match the request body and timestamp.");
44
28
  }
45
29
  return {
46
30
  ok: true,
47
- signature: signature.value,
31
+ signature,
48
32
  timestamp: parsedTimestamp,
49
33
  verifiedAt: now
50
34
  };
51
35
  }
36
+ function preflightWebhookRequest(request, options = {}) {
37
+ const signature = resolveHeader("x-adcp-signature", request.signature, request.headers);
38
+ if (signature.reason) return { ok: false, reason: signature.reason, message: signature.message };
39
+ const timestamp = resolveHeader("x-adcp-timestamp", request.timestamp, request.headers);
40
+ if (timestamp.reason) return { ok: false, reason: timestamp.reason, message: timestamp.message };
41
+ if (!signature.value || !timestamp.value)
42
+ return {
43
+ ok: false,
44
+ reason: "missing_headers",
45
+ message: "Webhook is missing x-adcp-signature or x-adcp-timestamp."
46
+ };
47
+ const parsedTimestamp = parseTimestamp(timestamp.value);
48
+ if (parsedTimestamp === void 0)
49
+ return {
50
+ ok: false,
51
+ reason: "invalid_timestamp",
52
+ message: "x-adcp-timestamp must be an integer unix timestamp in seconds."
53
+ };
54
+ const now = Math.floor(options.now ? options.now() : Date.now() / 1e3);
55
+ const skewSeconds = options.maxSkewSeconds ?? options.skewSeconds ?? 300;
56
+ if (!Number.isFinite(now) || !Number.isFinite(skewSeconds) || skewSeconds < 0)
57
+ return {
58
+ ok: false,
59
+ reason: "invalid_timestamp",
60
+ message: "Clock and skew must be finite; skew must be non-negative."
61
+ };
62
+ if (Math.abs(now - parsedTimestamp) > skewSeconds)
63
+ return {
64
+ ok: false,
65
+ reason: "stale_timestamp",
66
+ message: `Webhook timestamp is outside the allowed ${skewSeconds}s skew window.`
67
+ };
68
+ if (!/^sha256=[a-f0-9]{64}$/.test(signature.value))
69
+ return {
70
+ ok: false,
71
+ reason: "malformed_signature",
72
+ message: "x-adcp-signature must match sha256=<64 lowercase hex chars>."
73
+ };
74
+ return { ok: true, signature: signature.value, timestamp: parsedTimestamp, checkedAt: now };
75
+ }
52
76
  function fail(reason, message) {
53
77
  return { ok: false, reason, message };
54
78
  }
@@ -124,5 +148,6 @@ export {
124
148
  WholesaleFeedWebhookNotificationError,
125
149
  normalizeWholesaleFeedWebhookNotification,
126
150
  parseWholesaleFeedWebhookNotification,
151
+ preflightWebhookRequest,
127
152
  verifyWebhookRequest
128
153
  };
@@ -5,3 +5,5 @@ export { normalizeWholesaleFeedWebhookNotification, parseWholesaleFeedWebhookNot
5
5
  export type { WholesaleFeedSyncClient, WholesaleFeedSyncConfig, WholesaleFeedSyncEvents, WholesaleFeedSyncMode, WholesaleFeedSyncPersistedState, WholesaleFeedSyncPersistenceHooks, WholesaleFeedSyncState, ProductFilter, ResolvedCapabilities, SignalFilter, } from './types.mjs';
6
6
  export type { NormalizedWholesaleFeedWebhookNotification, WholesaleFeedWebhookAffectedEntityType, WholesaleFeedWebhookCacheScope, WholesaleFeedWebhookNotificationErrorCode, WholesaleFeedWebhookNotificationErrorDetails, WholesaleFeedWebhookNotificationType, } from './webhook-notification.mjs';
7
7
  export type { LegacyWholesaleFeedEvent, LegacyWholesaleFeedWebhook, LegacyWholesaleProduct, LegacyWholesaleSignal, } from './protocol-types.mjs';
8
+ export { refreshWholesaleFeed, applyWholesaleFeedWebhook, InMemoryWholesaleFeedMirrorStore } from './mirror.mjs';
9
+ export type { WholesaleFeedMirrorStore, WholesaleFeedMirrorScope, WholesaleFeedMirrorItem, WholesaleFeedMirrorSnapshot, WholesaleFeedMirrorCommit, WholesaleFeedRefreshOutcome, RefreshWholesaleFeedOptions, ApplyWholesaleFeedWebhookOptions, } from './mirror.mjs';
@@ -5,3 +5,5 @@ export { normalizeWholesaleFeedWebhookNotification, parseWholesaleFeedWebhookNot
5
5
  export type { WholesaleFeedSyncClient, WholesaleFeedSyncConfig, WholesaleFeedSyncEvents, WholesaleFeedSyncMode, WholesaleFeedSyncPersistedState, WholesaleFeedSyncPersistenceHooks, WholesaleFeedSyncState, ProductFilter, ResolvedCapabilities, SignalFilter, } from './types';
6
6
  export type { NormalizedWholesaleFeedWebhookNotification, WholesaleFeedWebhookAffectedEntityType, WholesaleFeedWebhookCacheScope, WholesaleFeedWebhookNotificationErrorCode, WholesaleFeedWebhookNotificationErrorDetails, WholesaleFeedWebhookNotificationType, } from './webhook-notification';
7
7
  export type { LegacyWholesaleFeedEvent, LegacyWholesaleFeedWebhook, LegacyWholesaleProduct, LegacyWholesaleSignal, } from './protocol-types';
8
+ export { refreshWholesaleFeed, applyWholesaleFeedWebhook, InMemoryWholesaleFeedMirrorStore } from './mirror';
9
+ export type { WholesaleFeedMirrorStore, WholesaleFeedMirrorScope, WholesaleFeedMirrorItem, WholesaleFeedMirrorSnapshot, WholesaleFeedMirrorCommit, WholesaleFeedRefreshOutcome, RefreshWholesaleFeedOptions, ApplyWholesaleFeedWebhookOptions, } from './mirror';
@@ -18,23 +18,30 @@ var __copyProps = (to, from, except, desc) => {
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
  var wholesale_feed_sync_exports = {};
20
20
  __export(wholesale_feed_sync_exports, {
21
+ InMemoryWholesaleFeedMirrorStore: () => import_mirror.InMemoryWholesaleFeedMirrorStore,
21
22
  WholesaleFeedSync: () => import_sync.WholesaleFeedSync,
22
23
  WholesaleFeedWebhookNotificationError: () => import_webhook_notification.WholesaleFeedWebhookNotificationError,
23
24
  WholesaleFeedWebhookRegistrationError: () => import_register.WholesaleFeedWebhookRegistrationError,
25
+ applyWholesaleFeedWebhook: () => import_mirror.applyWholesaleFeedWebhook,
24
26
  normalizeWholesaleFeedWebhookNotification: () => import_webhook_notification.normalizeWholesaleFeedWebhookNotification,
25
27
  parseWholesaleFeedWebhookNotification: () => import_webhook_notification.parseWholesaleFeedWebhookNotification,
28
+ refreshWholesaleFeed: () => import_mirror.refreshWholesaleFeed,
26
29
  registerWholesaleFeedWebhooks: () => import_register.registerWholesaleFeedWebhooks
27
30
  });
28
31
  module.exports = __toCommonJS(wholesale_feed_sync_exports);
29
32
  var import_sync = require('./sync.js');
30
33
  var import_register = require('./register.js');
31
34
  var import_webhook_notification = require('./webhook-notification.js');
35
+ var import_mirror = require('./mirror.js');
32
36
  // Annotate the CommonJS export names for ESM import in node:
33
37
  0 && (module.exports = {
38
+ InMemoryWholesaleFeedMirrorStore,
34
39
  WholesaleFeedSync,
35
40
  WholesaleFeedWebhookNotificationError,
36
41
  WholesaleFeedWebhookRegistrationError,
42
+ applyWholesaleFeedWebhook,
37
43
  normalizeWholesaleFeedWebhookNotification,
38
44
  parseWholesaleFeedWebhookNotification,
45
+ refreshWholesaleFeed,
39
46
  registerWholesaleFeedWebhooks
40
47
  });
@@ -5,11 +5,15 @@ import {
5
5
  parseWholesaleFeedWebhookNotification,
6
6
  WholesaleFeedWebhookNotificationError
7
7
  } from "./webhook-notification.mjs";
8
+ import { refreshWholesaleFeed, applyWholesaleFeedWebhook, InMemoryWholesaleFeedMirrorStore } from "./mirror.mjs";
8
9
  export {
10
+ InMemoryWholesaleFeedMirrorStore,
9
11
  WholesaleFeedSync,
10
12
  WholesaleFeedWebhookNotificationError,
11
13
  WholesaleFeedWebhookRegistrationError,
14
+ applyWholesaleFeedWebhook,
12
15
  normalizeWholesaleFeedWebhookNotification,
13
16
  parseWholesaleFeedWebhookNotification,
17
+ refreshWholesaleFeed,
14
18
  registerWholesaleFeedWebhooks
15
19
  };
@@ -0,0 +1,97 @@
1
+ import type { AccountReference } from '../types/index.mjs';
2
+ import type { AdcpErrorInfo } from '../core/ConversationTypes.mjs';
3
+ import type { WholesaleFeedSyncClient } from './types.mjs';
4
+ import type { LegacyWholesaleFeedEvent, LegacyWholesaleFeedWebhook, LegacyWholesaleProduct, LegacyWholesaleSignal } from './protocol-types.mjs';
5
+ export type WholesaleFeedMirrorItem = LegacyWholesaleProduct | LegacyWholesaleSignal;
6
+ /** accountKey must include the caller/principal and account overlay, even for public catalogs. */
7
+ export interface WholesaleFeedMirrorScope {
8
+ agentUrl: string;
9
+ accountKey: string;
10
+ entity: 'product' | 'signal';
11
+ }
12
+ export interface WholesaleFeedMirrorSnapshot {
13
+ revision: number;
14
+ items: WholesaleFeedMirrorItem[];
15
+ wholesaleFeedVersion?: string;
16
+ pricingVersion?: string;
17
+ cacheScope: 'public' | 'account';
18
+ lastSyncedAt?: string;
19
+ lastWebhookEventId?: string;
20
+ lastWebhookIdempotencyKey?: string;
21
+ error?: AdcpErrorInfo;
22
+ }
23
+ export interface WholesaleFeedMirrorCommit {
24
+ upserts: WholesaleFeedMirrorItem[];
25
+ deletes: string[];
26
+ wholesaleFeedVersion?: string;
27
+ pricingVersion?: string;
28
+ cacheScope: 'public' | 'account';
29
+ lastSyncedAt: string;
30
+ lastWebhookEventId?: string;
31
+ lastWebhookIdempotencyKey?: string;
32
+ /** Persist this delivery receipt atomically with the row/token commit. */
33
+ webhookReceipt?: {
34
+ eventId: string;
35
+ idempotencyKey: string;
36
+ };
37
+ }
38
+ /** Consistent reads; atomically check/increment revisions with rows, tokens, and receipts. */
39
+ export interface WholesaleFeedMirrorStore {
40
+ read(scope: WholesaleFeedMirrorScope): Promise<WholesaleFeedMirrorSnapshot>;
41
+ hasWebhookReceipt(scope: WholesaleFeedMirrorScope, idempotencyKey: string): Promise<boolean>;
42
+ commit(scope: WholesaleFeedMirrorScope, expectedRevision: number, change: WholesaleFeedMirrorCommit): Promise<boolean>;
43
+ recordError(scope: WholesaleFeedMirrorScope, expectedRevision: number, error: AdcpErrorInfo): Promise<boolean>;
44
+ }
45
+ export type WholesaleFeedRefreshOutcome = {
46
+ outcome: 'unchanged';
47
+ snapshot: WholesaleFeedMirrorSnapshot;
48
+ } | {
49
+ outcome: 'applied';
50
+ snapshot: WholesaleFeedMirrorSnapshot;
51
+ diff: LegacyWholesaleFeedEvent[];
52
+ } | {
53
+ outcome: 'degraded';
54
+ error: AdcpErrorInfo;
55
+ cause: Error;
56
+ } | {
57
+ outcome: 'superseded';
58
+ };
59
+ export interface RefreshWholesaleFeedOptions {
60
+ client: WholesaleFeedSyncClient;
61
+ store: WholesaleFeedMirrorStore;
62
+ scope: WholesaleFeedMirrorScope;
63
+ account?: AccountReference;
64
+ /** Disable against sellers that do not advertise wholesale-feed versioning. Defaults to true. */
65
+ conditional?: boolean;
66
+ /** Local cancellation fence; no timer is started by the core. */
67
+ isCurrent?: () => boolean;
68
+ }
69
+ export declare class InMemoryWholesaleFeedMirrorStore implements WholesaleFeedMirrorStore {
70
+ private readonly maxWebhookReceipts;
71
+ private readonly records;
72
+ private readonly receipts;
73
+ private receiptCount;
74
+ /** Fail closed at capacity rather than evicting receipts inside the delivery retry window. */
75
+ constructor(maxWebhookReceipts?: number);
76
+ hasWebhookReceipt(scope: WholesaleFeedMirrorScope, idempotencyKey: string): Promise<boolean>;
77
+ private key;
78
+ read(scope: WholesaleFeedMirrorScope): Promise<WholesaleFeedMirrorSnapshot>;
79
+ /** Seed a detached record when restoring a shell snapshot. */
80
+ restore(scope: WholesaleFeedMirrorScope, snapshot: WholesaleFeedMirrorSnapshot): void;
81
+ commit(scope: WholesaleFeedMirrorScope, expectedRevision: number, change: WholesaleFeedMirrorCommit): Promise<boolean>;
82
+ recordError(scope: WholesaleFeedMirrorScope, expectedRevision: number, error: AdcpErrorInfo): Promise<boolean>;
83
+ }
84
+ /** Refresh one legacy get_products/get_signals feed without owning scheduling or storage. */
85
+ export declare function refreshWholesaleFeed(options: RefreshWholesaleFeedOptions): Promise<WholesaleFeedRefreshOutcome>;
86
+ export interface ApplyWholesaleFeedWebhookOptions extends RefreshWholesaleFeedOptions {
87
+ webhook: LegacyWholesaleFeedWebhook;
88
+ /** Expected subscription route, supplied after authentication by the receiver. */
89
+ webhookScope?: {
90
+ accountId?: string;
91
+ subscriberId?: string;
92
+ };
93
+ }
94
+ /** Apply an authenticated legacy delta, or repair from the seller on bulk change/order/version mismatch. */
95
+ export declare function applyWholesaleFeedWebhook(options: ApplyWholesaleFeedWebhookOptions): Promise<WholesaleFeedRefreshOutcome>;
96
+ export declare function applyWholesaleFeedEvent(entity: WholesaleFeedMirrorScope['entity'], previous: WholesaleFeedMirrorItem[], event: LegacyWholesaleFeedEvent): WholesaleFeedMirrorItem[] | undefined;
97
+ export declare function diffWholesaleFeed(entity: WholesaleFeedMirrorScope['entity'], previous: WholesaleFeedMirrorItem[], next: WholesaleFeedMirrorItem[], cacheScope: 'public' | 'account', includeAllChanges?: boolean): LegacyWholesaleFeedEvent[];
@@ -0,0 +1,97 @@
1
+ import type { AccountReference } from '../types';
2
+ import type { AdcpErrorInfo } from '../core/ConversationTypes';
3
+ import type { WholesaleFeedSyncClient } from './types';
4
+ import type { LegacyWholesaleFeedEvent, LegacyWholesaleFeedWebhook, LegacyWholesaleProduct, LegacyWholesaleSignal } from './protocol-types';
5
+ export type WholesaleFeedMirrorItem = LegacyWholesaleProduct | LegacyWholesaleSignal;
6
+ /** accountKey must include the caller/principal and account overlay, even for public catalogs. */
7
+ export interface WholesaleFeedMirrorScope {
8
+ agentUrl: string;
9
+ accountKey: string;
10
+ entity: 'product' | 'signal';
11
+ }
12
+ export interface WholesaleFeedMirrorSnapshot {
13
+ revision: number;
14
+ items: WholesaleFeedMirrorItem[];
15
+ wholesaleFeedVersion?: string;
16
+ pricingVersion?: string;
17
+ cacheScope: 'public' | 'account';
18
+ lastSyncedAt?: string;
19
+ lastWebhookEventId?: string;
20
+ lastWebhookIdempotencyKey?: string;
21
+ error?: AdcpErrorInfo;
22
+ }
23
+ export interface WholesaleFeedMirrorCommit {
24
+ upserts: WholesaleFeedMirrorItem[];
25
+ deletes: string[];
26
+ wholesaleFeedVersion?: string;
27
+ pricingVersion?: string;
28
+ cacheScope: 'public' | 'account';
29
+ lastSyncedAt: string;
30
+ lastWebhookEventId?: string;
31
+ lastWebhookIdempotencyKey?: string;
32
+ /** Persist this delivery receipt atomically with the row/token commit. */
33
+ webhookReceipt?: {
34
+ eventId: string;
35
+ idempotencyKey: string;
36
+ };
37
+ }
38
+ /** Consistent reads; atomically check/increment revisions with rows, tokens, and receipts. */
39
+ export interface WholesaleFeedMirrorStore {
40
+ read(scope: WholesaleFeedMirrorScope): Promise<WholesaleFeedMirrorSnapshot>;
41
+ hasWebhookReceipt(scope: WholesaleFeedMirrorScope, idempotencyKey: string): Promise<boolean>;
42
+ commit(scope: WholesaleFeedMirrorScope, expectedRevision: number, change: WholesaleFeedMirrorCommit): Promise<boolean>;
43
+ recordError(scope: WholesaleFeedMirrorScope, expectedRevision: number, error: AdcpErrorInfo): Promise<boolean>;
44
+ }
45
+ export type WholesaleFeedRefreshOutcome = {
46
+ outcome: 'unchanged';
47
+ snapshot: WholesaleFeedMirrorSnapshot;
48
+ } | {
49
+ outcome: 'applied';
50
+ snapshot: WholesaleFeedMirrorSnapshot;
51
+ diff: LegacyWholesaleFeedEvent[];
52
+ } | {
53
+ outcome: 'degraded';
54
+ error: AdcpErrorInfo;
55
+ cause: Error;
56
+ } | {
57
+ outcome: 'superseded';
58
+ };
59
+ export interface RefreshWholesaleFeedOptions {
60
+ client: WholesaleFeedSyncClient;
61
+ store: WholesaleFeedMirrorStore;
62
+ scope: WholesaleFeedMirrorScope;
63
+ account?: AccountReference;
64
+ /** Disable against sellers that do not advertise wholesale-feed versioning. Defaults to true. */
65
+ conditional?: boolean;
66
+ /** Local cancellation fence; no timer is started by the core. */
67
+ isCurrent?: () => boolean;
68
+ }
69
+ export declare class InMemoryWholesaleFeedMirrorStore implements WholesaleFeedMirrorStore {
70
+ private readonly maxWebhookReceipts;
71
+ private readonly records;
72
+ private readonly receipts;
73
+ private receiptCount;
74
+ /** Fail closed at capacity rather than evicting receipts inside the delivery retry window. */
75
+ constructor(maxWebhookReceipts?: number);
76
+ hasWebhookReceipt(scope: WholesaleFeedMirrorScope, idempotencyKey: string): Promise<boolean>;
77
+ private key;
78
+ read(scope: WholesaleFeedMirrorScope): Promise<WholesaleFeedMirrorSnapshot>;
79
+ /** Seed a detached record when restoring a shell snapshot. */
80
+ restore(scope: WholesaleFeedMirrorScope, snapshot: WholesaleFeedMirrorSnapshot): void;
81
+ commit(scope: WholesaleFeedMirrorScope, expectedRevision: number, change: WholesaleFeedMirrorCommit): Promise<boolean>;
82
+ recordError(scope: WholesaleFeedMirrorScope, expectedRevision: number, error: AdcpErrorInfo): Promise<boolean>;
83
+ }
84
+ /** Refresh one legacy get_products/get_signals feed without owning scheduling or storage. */
85
+ export declare function refreshWholesaleFeed(options: RefreshWholesaleFeedOptions): Promise<WholesaleFeedRefreshOutcome>;
86
+ export interface ApplyWholesaleFeedWebhookOptions extends RefreshWholesaleFeedOptions {
87
+ webhook: LegacyWholesaleFeedWebhook;
88
+ /** Expected subscription route, supplied after authentication by the receiver. */
89
+ webhookScope?: {
90
+ accountId?: string;
91
+ subscriberId?: string;
92
+ };
93
+ }
94
+ /** Apply an authenticated legacy delta, or repair from the seller on bulk change/order/version mismatch. */
95
+ export declare function applyWholesaleFeedWebhook(options: ApplyWholesaleFeedWebhookOptions): Promise<WholesaleFeedRefreshOutcome>;
96
+ export declare function applyWholesaleFeedEvent(entity: WholesaleFeedMirrorScope['entity'], previous: WholesaleFeedMirrorItem[], event: LegacyWholesaleFeedEvent): WholesaleFeedMirrorItem[] | undefined;
97
+ export declare function diffWholesaleFeed(entity: WholesaleFeedMirrorScope['entity'], previous: WholesaleFeedMirrorItem[], next: WholesaleFeedMirrorItem[], cacheScope: 'public' | 'account', includeAllChanges?: boolean): LegacyWholesaleFeedEvent[];