@adcp/sdk 14.4.0 → 15.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 (188) hide show
  1. package/bin/adcp-grade.js +46 -2
  2. package/bin/adcp.js +13 -3
  3. package/dist/lib/auth/oauth/ClientCredentialsFlow.js +4 -1
  4. package/dist/lib/auth/oauth/ClientCredentialsFlow.mjs +4 -1
  5. package/dist/lib/auth/oauth/MCPOAuthProvider.d.mts +19 -6
  6. package/dist/lib/auth/oauth/MCPOAuthProvider.d.ts +19 -6
  7. package/dist/lib/auth/oauth/MCPOAuthProvider.js +95 -30
  8. package/dist/lib/auth/oauth/MCPOAuthProvider.mjs +96 -30
  9. package/dist/lib/auth/oauth/NonInteractiveFlowHandler.d.mts +1 -1
  10. package/dist/lib/auth/oauth/NonInteractiveFlowHandler.d.ts +1 -1
  11. package/dist/lib/auth/oauth/NonInteractiveFlowHandler.js +1 -1
  12. package/dist/lib/auth/oauth/NonInteractiveFlowHandler.mjs +1 -1
  13. package/dist/lib/auth/oauth/client-credentials-schema.d.mts +17 -0
  14. package/dist/lib/auth/oauth/client-credentials-schema.d.ts +17 -0
  15. package/dist/lib/auth/oauth/client-credentials-schema.js +37 -0
  16. package/dist/lib/auth/oauth/client-credentials-schema.mjs +13 -0
  17. package/dist/lib/auth/oauth/file-storage.d.mts +3 -3
  18. package/dist/lib/auth/oauth/file-storage.d.ts +3 -3
  19. package/dist/lib/auth/oauth/file-storage.js +12 -5
  20. package/dist/lib/auth/oauth/file-storage.mjs +12 -5
  21. package/dist/lib/auth/oauth/index.d.mts +10 -6
  22. package/dist/lib/auth/oauth/index.d.ts +10 -6
  23. package/dist/lib/auth/oauth/index.js +9 -3
  24. package/dist/lib/auth/oauth/index.mjs +8 -3
  25. package/dist/lib/auth/oauth/types.d.mts +9 -2
  26. package/dist/lib/auth/oauth/types.d.ts +9 -2
  27. package/dist/lib/auth/oauth/types.js +3 -2
  28. package/dist/lib/auth/oauth/types.mjs +3 -2
  29. package/dist/lib/auth/oauth/web-flow.d.mts +2 -0
  30. package/dist/lib/auth/oauth/web-flow.d.ts +2 -0
  31. package/dist/lib/auth/oauth/web-flow.js +42 -9
  32. package/dist/lib/auth/oauth/web-flow.mjs +42 -9
  33. package/dist/lib/canonical-references/index.d.mts +19 -1
  34. package/dist/lib/canonical-references/index.d.ts +19 -1
  35. package/dist/lib/canonical-references/index.js +81 -9
  36. package/dist/lib/canonical-references/index.mjs +80 -9
  37. package/dist/lib/client/core.d.mts +1 -0
  38. package/dist/lib/client/core.d.ts +1 -0
  39. package/dist/lib/core/AgentClient.d.mts +9 -1
  40. package/dist/lib/core/AgentClient.d.ts +9 -1
  41. package/dist/lib/core/AgentClient.js +10 -0
  42. package/dist/lib/core/AgentClient.mjs +10 -0
  43. package/dist/lib/core/SingleAgentClient.d.mts +13 -2
  44. package/dist/lib/core/SingleAgentClient.d.ts +13 -2
  45. package/dist/lib/core/SingleAgentClient.js +23 -5
  46. package/dist/lib/core/SingleAgentClient.mjs +23 -5
  47. package/dist/lib/index.d.mts +2 -2
  48. package/dist/lib/index.d.ts +2 -2
  49. package/dist/lib/index.js +2 -0
  50. package/dist/lib/index.mjs +3 -1
  51. package/dist/lib/reporting/gcs/adapter.d.mts +31 -0
  52. package/dist/lib/reporting/gcs/adapter.d.ts +31 -0
  53. package/dist/lib/reporting/gcs/adapter.js +342 -0
  54. package/dist/lib/reporting/gcs/adapter.mjs +325 -0
  55. package/dist/lib/reporting/gcs/bucket-policy.d.mts +3 -0
  56. package/dist/lib/reporting/gcs/bucket-policy.d.ts +3 -0
  57. package/dist/lib/reporting/gcs/bucket-policy.js +31 -0
  58. package/dist/lib/reporting/gcs/bucket-policy.mjs +7 -0
  59. package/dist/lib/reporting/gcs/errors.d.mts +5 -0
  60. package/dist/lib/reporting/gcs/errors.d.ts +5 -0
  61. package/dist/lib/reporting/gcs/errors.js +41 -0
  62. package/dist/lib/reporting/gcs/errors.mjs +16 -0
  63. package/dist/lib/reporting/gcs/fence.d.mts +36 -0
  64. package/dist/lib/reporting/gcs/fence.d.ts +36 -0
  65. package/dist/lib/reporting/gcs/fence.js +250 -0
  66. package/dist/lib/reporting/gcs/fence.mjs +230 -0
  67. package/dist/lib/reporting/gcs/index.d.mts +7 -0
  68. package/dist/lib/reporting/gcs/index.d.ts +7 -0
  69. package/dist/lib/reporting/gcs/index.js +41 -0
  70. package/dist/lib/reporting/gcs/index.mjs +12 -0
  71. package/dist/lib/reporting/gcs/io.d.mts +7 -0
  72. package/dist/lib/reporting/gcs/io.d.ts +7 -0
  73. package/dist/lib/reporting/gcs/io.js +95 -0
  74. package/dist/lib/reporting/gcs/io.mjs +69 -0
  75. package/dist/lib/reporting/gcs/reader.d.mts +43 -0
  76. package/dist/lib/reporting/gcs/reader.d.ts +43 -0
  77. package/dist/lib/reporting/gcs/reader.js +193 -0
  78. package/dist/lib/reporting/gcs/reader.mjs +169 -0
  79. package/dist/lib/reporting/gcs/validation.d.mts +5 -0
  80. package/dist/lib/reporting/gcs/validation.d.ts +5 -0
  81. package/dist/lib/reporting/gcs/validation.js +49 -0
  82. package/dist/lib/reporting/gcs/validation.mjs +25 -0
  83. package/dist/lib/reporting/ledger/handler.d.mts +1 -0
  84. package/dist/lib/reporting/ledger/handler.d.ts +1 -0
  85. package/dist/lib/reporting/ledger/index.d.mts +1 -0
  86. package/dist/lib/reporting/ledger/index.d.ts +1 -0
  87. package/dist/lib/reporting/ledger/index.js +2 -0
  88. package/dist/lib/reporting/ledger/index.mjs +1 -0
  89. package/dist/lib/reporting/ledger/managed-postgres.d.mts +16 -1
  90. package/dist/lib/reporting/ledger/managed-postgres.d.ts +16 -1
  91. package/dist/lib/reporting/ledger/managed-postgres.js +243 -2
  92. package/dist/lib/reporting/ledger/managed-postgres.mjs +248 -2
  93. package/dist/lib/reporting/ledger/object-writes.d.mts +69 -0
  94. package/dist/lib/reporting/ledger/object-writes.d.ts +69 -0
  95. package/dist/lib/reporting/ledger/object-writes.js +147 -0
  96. package/dist/lib/reporting/ledger/object-writes.mjs +114 -0
  97. package/dist/lib/schemas/index.d.mts +2 -0
  98. package/dist/lib/schemas/index.d.ts +2 -0
  99. package/dist/lib/schemas/index.js +3 -0
  100. package/dist/lib/schemas/index.mjs +2 -0
  101. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  102. package/dist/lib/server/a2a-adapter.d.mts +50 -1
  103. package/dist/lib/server/a2a-adapter.d.ts +50 -1
  104. package/dist/lib/server/a2a-adapter.js +230 -7
  105. package/dist/lib/server/a2a-adapter.mjs +237 -7
  106. package/dist/lib/server/auth-signature.d.mts +84 -46
  107. package/dist/lib/server/auth-signature.d.ts +84 -46
  108. package/dist/lib/server/auth-signature.js +48 -14
  109. package/dist/lib/server/auth-signature.mjs +58 -15
  110. package/dist/lib/server/create-adcp-server.js +5 -16
  111. package/dist/lib/server/create-adcp-server.mjs +5 -16
  112. package/dist/lib/server/index.d.mts +1 -1
  113. package/dist/lib/server/index.d.ts +1 -1
  114. package/dist/lib/server/index.js +2 -0
  115. package/dist/lib/server/index.mjs +3 -1
  116. package/dist/lib/signing/agent-fetch.js +3 -33
  117. package/dist/lib/signing/agent-fetch.mjs +3 -33
  118. package/dist/lib/signing/middleware.d.mts +9 -1
  119. package/dist/lib/signing/middleware.d.ts +9 -1
  120. package/dist/lib/signing/operation-resolution.d.mts +37 -0
  121. package/dist/lib/signing/operation-resolution.d.ts +37 -0
  122. package/dist/lib/signing/operation-resolution.js +154 -0
  123. package/dist/lib/signing/operation-resolution.mjs +125 -0
  124. package/dist/lib/signing/postgres-replay-store.d.mts +1 -1
  125. package/dist/lib/signing/postgres-replay-store.d.ts +1 -1
  126. package/dist/lib/signing/redis-replay-store.d.mts +1 -1
  127. package/dist/lib/signing/redis-replay-store.d.ts +1 -1
  128. package/dist/lib/signing/server.d.mts +1 -0
  129. package/dist/lib/signing/server.d.ts +1 -0
  130. package/dist/lib/signing/server.js +7 -0
  131. package/dist/lib/signing/server.mjs +8 -0
  132. package/dist/lib/signing/verifier.d.mts +36 -1
  133. package/dist/lib/signing/verifier.d.ts +36 -1
  134. package/dist/lib/signing/verifier.js +66 -34
  135. package/dist/lib/signing/verifier.mjs +61 -34
  136. package/dist/lib/testing/client.js +8 -3
  137. package/dist/lib/testing/client.mjs +8 -3
  138. package/dist/lib/testing/storyboard/request-signing/a2a-dispatch.d.mts +12 -0
  139. package/dist/lib/testing/storyboard/request-signing/a2a-dispatch.d.ts +12 -0
  140. package/dist/lib/testing/storyboard/request-signing/a2a-dispatch.js +29 -2
  141. package/dist/lib/testing/storyboard/request-signing/a2a-dispatch.mjs +27 -1
  142. package/dist/lib/testing/storyboard/request-signing/a2a-operation-resolution.d.mts +61 -0
  143. package/dist/lib/testing/storyboard/request-signing/a2a-operation-resolution.d.ts +61 -0
  144. package/dist/lib/testing/storyboard/request-signing/a2a-operation-resolution.js +298 -0
  145. package/dist/lib/testing/storyboard/request-signing/a2a-operation-resolution.mjs +276 -0
  146. package/dist/lib/testing/storyboard/request-signing/grader.d.mts +31 -1
  147. package/dist/lib/testing/storyboard/request-signing/grader.d.ts +31 -1
  148. package/dist/lib/testing/storyboard/request-signing/grader.js +11 -0
  149. package/dist/lib/testing/storyboard/request-signing/grader.mjs +11 -0
  150. package/dist/lib/testing/storyboard/request-signing/index.d.mts +5 -4
  151. package/dist/lib/testing/storyboard/request-signing/index.d.ts +5 -4
  152. package/dist/lib/testing/storyboard/request-signing/index.js +13 -0
  153. package/dist/lib/testing/storyboard/request-signing/index.mjs +18 -2
  154. package/dist/lib/testing/storyboard/request-signing/types.d.mts +43 -0
  155. package/dist/lib/testing/storyboard/request-signing/types.d.ts +43 -0
  156. package/dist/lib/testing/storyboard/request-signing/types.js +3 -0
  157. package/dist/lib/testing/storyboard/request-signing/types.mjs +2 -0
  158. package/dist/lib/testing/storyboard/request-signing/vector-loader.d.mts +42 -1
  159. package/dist/lib/testing/storyboard/request-signing/vector-loader.d.ts +42 -1
  160. package/dist/lib/testing/storyboard/request-signing/vector-loader.js +100 -1
  161. package/dist/lib/testing/storyboard/request-signing/vector-loader.mjs +104 -3
  162. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  163. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  164. package/dist/lib/testing/types.d.mts +10 -1
  165. package/dist/lib/testing/types.d.ts +10 -1
  166. package/dist/lib/version.d.mts +3 -3
  167. package/dist/lib/version.d.ts +3 -3
  168. package/dist/lib/version.js +3 -3
  169. package/dist/lib/version.mjs +3 -3
  170. package/docs/TYPE-SUMMARY.md +2 -2
  171. package/docs/ZOD-SCHEMAS.md +19 -0
  172. package/docs/guides/BUILD-AN-AGENT.md +9 -4
  173. package/docs/guides/CREATIVE-DELIVERY.md +34 -0
  174. package/docs/guides/OAUTH-ISSUER-BINDING.md +46 -0
  175. package/docs/guides/REPORTING-GCS-FENCE.md +52 -0
  176. package/docs/guides/REPORTING-GCS-MANAGED.md +56 -0
  177. package/docs/guides/REPORTING-OPERATIONS.md +4 -0
  178. package/docs/guides/SIGNING-GUIDE.md +20 -6
  179. package/docs/guides/VALIDATE-YOUR-AGENT.md +4 -0
  180. package/docs/guides/WEB-OAUTH.md +58 -163
  181. package/docs/guides/account-resolution.md +1 -1
  182. package/docs/llms.txt +2 -2
  183. package/docs/migration-14.0-to-14.1.md +1 -1
  184. package/docs/migration-14.x-rc-worksheet.md +8 -7
  185. package/docs/migration-buyer-agent-registry.md +1 -1
  186. package/docs/migration-oauth-issuer-bindings.md +14 -79
  187. package/package.json +19 -1
  188. package/skills/build-seller-agent/specialisms/signed-requests.md +2 -0
@@ -0,0 +1,125 @@
1
+ import { parseStrictJson, StrictJsonError } from "./agent-resolver/strict-json.mjs";
2
+ const UNRESOLVABLE_OPERATION = /* @__PURE__ */ Symbol.for("@adcp/client.signing.unresolvableOperation");
3
+ function isUnresolvableOperation(value) {
4
+ return value === UNRESOLVABLE_OPERATION;
5
+ }
6
+ const A2A_MESSAGE_METHODS = /* @__PURE__ */ new Set([
7
+ "SendMessage",
8
+ "SendStreamingMessage",
9
+ "message/send",
10
+ "message/stream"
11
+ ]);
12
+ const CONTENT_MEMBERS = ["text", "data", "raw", "url", "file"];
13
+ const FILE_MEMBERS = /* @__PURE__ */ new Set(["raw", "url", "file"]);
14
+ const HTTP_JSON_MESSAGE_PATH = /(?:^|\/)message:(?:send|stream)$/;
15
+ const ENVELOPE_MEMBERS = ["method", "params"];
16
+ const TOOLS_CALL_PARAMS_MEMBERS = ["name"];
17
+ const CARRIER_MEMBERS = ["message"];
18
+ const MESSAGE_MEMBERS = ["parts", "taskId"];
19
+ const PART_MEMBERS = [...CONTENT_MEMBERS, "kind"];
20
+ const DATA_MEMBERS = ["skill"];
21
+ function resolveRequestOperationDetailed(input) {
22
+ if (input.method !== void 0 && input.method.toUpperCase() !== "POST") return { kind: "none" };
23
+ const raw = typeof input.rawBody === "string" ? input.rawBody : input.rawBody?.toString("utf8");
24
+ if (raw === void 0 || raw.trim() === "") return unresolvable("request body is empty");
25
+ if (raw.charCodeAt(0) === 65279) return unresolvable("body starts with a byte order mark");
26
+ let body;
27
+ try {
28
+ body = parseStrictJson(raw);
29
+ } catch (err) {
30
+ return unresolvable(err instanceof StrictJsonError ? `body is not strict JSON (${err.code})` : "body is not JSON");
31
+ }
32
+ if (!isPlainObject(body)) {
33
+ return unresolvable(Array.isArray(body) ? "JSON-RPC batches are not supported" : "body is not a JSON object");
34
+ }
35
+ if (isHttpJsonMessagePath(input.url)) {
36
+ return resolveMessageCarrier(body);
37
+ }
38
+ if (hasCaseVariantMember(body, ENVELOPE_MEMBERS)) return unresolvable("case variant of an envelope member");
39
+ const method = body.method;
40
+ if (method === void 0 && isJsonRpcResponse(body)) {
41
+ return { kind: "none" };
42
+ }
43
+ if (typeof method !== "string") return unresolvable("JSON-RPC request has no string method");
44
+ if (method === "tools/call") {
45
+ const params = body.params;
46
+ if (!isPlainObject(params)) return unresolvable("tools/call params is not an object");
47
+ if (hasCaseVariantMember(params, TOOLS_CALL_PARAMS_MEMBERS)) return unresolvable("case variant of a params member");
48
+ const name = params.name;
49
+ if (typeof name !== "string" || name.length === 0) return unresolvable("tools/call params.name is not a string");
50
+ return { kind: "operation", operation: name };
51
+ }
52
+ if (A2A_MESSAGE_METHODS.has(method)) {
53
+ const params = body.params;
54
+ if (!isPlainObject(params)) return unresolvable("message method params is not an object");
55
+ return resolveMessageCarrier(params);
56
+ }
57
+ return { kind: "none" };
58
+ }
59
+ function resolveRequestOperation(input) {
60
+ const resolution = resolveRequestOperationDetailed(input);
61
+ if (resolution.kind === "operation") return resolution.operation;
62
+ return resolution.kind === "none" ? void 0 : UNRESOLVABLE_OPERATION;
63
+ }
64
+ function describeUnresolvable(input) {
65
+ const resolution = resolveRequestOperationDetailed(input);
66
+ return resolution.kind === "unresolvable" ? resolution.reason : void 0;
67
+ }
68
+ function resolveMessageCarrier(container) {
69
+ if (hasCaseVariantMember(container, CARRIER_MEMBERS)) return unresolvable("case variant of the message member");
70
+ const message = container.message;
71
+ if (!isPlainObject(message)) return unresolvable("request carries no message object");
72
+ if (hasCaseVariantMember(message, MESSAGE_MEMBERS)) return unresolvable("case variant of a message member");
73
+ const parts = message.parts;
74
+ if (!Array.isArray(parts)) return unresolvable("message.parts is not an array");
75
+ let invocation;
76
+ for (const part of parts) {
77
+ if (!isPlainObject(part)) return unresolvable("a Part is not an object");
78
+ if (hasCaseVariantMember(part, PART_MEMBERS)) return unresolvable("case variant of a Part member");
79
+ const present = CONTENT_MEMBERS.filter((member2) => Object.prototype.hasOwnProperty.call(part, member2));
80
+ if (present.length !== 1) return unresolvable("a Part must carry exactly one content member");
81
+ const member = present[0];
82
+ if (FILE_MEMBERS.has(member)) return unresolvable("FileParts are not allowed on an AdCP invocation");
83
+ const kind = part.kind;
84
+ if (kind !== void 0 && kind !== member) return unresolvable("a Part kind disagrees with its content member");
85
+ if (member === "data") {
86
+ if (invocation !== void 0) return unresolvable("message carries more than one DataPart");
87
+ if (!isPlainObject(part.data)) return unresolvable("DataPart data is not an object");
88
+ invocation = part.data;
89
+ }
90
+ }
91
+ if (invocation === void 0) return unresolvable("message carries no DataPart");
92
+ if (hasCaseVariantMember(invocation, DATA_MEMBERS)) return unresolvable("case variant of skill");
93
+ const skill = invocation.skill;
94
+ if (typeof skill !== "string" || skill.length === 0) return unresolvable("DataPart skill is not a non-empty string");
95
+ return { kind: "operation", operation: skill };
96
+ }
97
+ function isJsonRpcResponse(body) {
98
+ const has = (key) => Object.prototype.hasOwnProperty.call(body, key);
99
+ return (has("result") || has("error")) && !has("params");
100
+ }
101
+ function unresolvable(reason) {
102
+ return { kind: "unresolvable", reason };
103
+ }
104
+ function isHttpJsonMessagePath(url) {
105
+ if (!url) return false;
106
+ const queryStart = url.search(/[?#]/);
107
+ return HTTP_JSON_MESSAGE_PATH.test(queryStart === -1 ? url : url.slice(0, queryStart));
108
+ }
109
+ function isPlainObject(value) {
110
+ return value !== null && typeof value === "object" && !Array.isArray(value);
111
+ }
112
+ function hasCaseVariantMember(obj, recognized) {
113
+ return Object.keys(obj).some((key) => {
114
+ const lowered = key.toLowerCase();
115
+ return recognized.some((name) => name.toLowerCase() === lowered && name !== key);
116
+ });
117
+ }
118
+ export {
119
+ A2A_MESSAGE_METHODS,
120
+ UNRESOLVABLE_OPERATION,
121
+ describeUnresolvable,
122
+ isUnresolvableOperation,
123
+ resolveRequestOperation,
124
+ resolveRequestOperationDetailed
125
+ };
@@ -24,7 +24,7 @@
24
24
  * capability: { ... },
25
25
  * jwks,
26
26
  * replayStore, // <-- shared across instances
27
- * resolveOperation: mcpToolNameResolver,
27
+ * resolveOperation: adcpOperationResolver,
28
28
  * }));
29
29
  *
30
30
  * // Schedule the sweeper somewhere (cron, app timer, pg_cron, etc.):
@@ -24,7 +24,7 @@
24
24
  * capability: { ... },
25
25
  * jwks,
26
26
  * replayStore, // <-- shared across instances
27
- * resolveOperation: mcpToolNameResolver,
27
+ * resolveOperation: adcpOperationResolver,
28
28
  * }));
29
29
  *
30
30
  * // Schedule the sweeper somewhere (cron, app timer, pg_cron, etc.):
@@ -74,7 +74,7 @@
74
74
  * capability: { ... },
75
75
  * jwks,
76
76
  * replayStore, // <-- shared across instances
77
- * resolveOperation: mcpToolNameResolver,
77
+ * resolveOperation: adcpOperationResolver,
78
78
  * }));
79
79
  * ```
80
80
  */
@@ -74,7 +74,7 @@
74
74
  * capability: { ... },
75
75
  * jwks,
76
76
  * replayStore, // <-- shared across instances
77
- * resolveOperation: mcpToolNameResolver,
77
+ * resolveOperation: adcpOperationResolver,
78
78
  * }));
79
79
  * ```
80
80
  */
@@ -25,6 +25,7 @@ export { InMemoryRevocationStore, type RevocationStore } from './revocation.mjs'
25
25
  export { HttpsRevocationStore, type HttpsRevocationStoreOptions } from './revocation-https.mjs';
26
26
  export { ALLOWED_ALGS, CLOCK_SKEW_TOLERANCE_SECONDS, MANDATORY_COMPONENTS, MAX_SIGNATURE_WINDOW_SECONDS, REQUEST_SIGNING_TAG, RESPONSE_MANDATORY_COMPONENTS, RESPONSE_SIGNING_TAG, type AdcpJsonWebKey, type ContentDigestPolicy, type RevocationSnapshot, type VerifiedSigner, type VerifierCapability, type VerifyResult, } from './types.mjs';
27
27
  export { verifyRequestSignature, type VerifyRequestOptions } from './verifier.mjs';
28
+ export { isUnresolvableOperation, resolveRequestOperation, UNRESOLVABLE_OPERATION, type OperationResolutionInput, type ResolvedOperation, type UnresolvableOperation, } from './operation-resolution.mjs';
28
29
  export { createWebhookVerifier, isWebhookLoopbackHost, verifyWebhookSignature, WEBHOOK_MANDATORY_COMPONENTS, WEBHOOK_SIGNING_TAG, type CreateWebhookVerifierOptions, type VerifyWebhookOptions, type VerifyWebhookResult, type WebhookRequestLike, } from './webhook-verifier.mjs';
29
30
  export { createExpressVerifier, type ExpressLike, type ExpressMiddlewareOptions } from './middleware.mjs';
30
31
  export { finalizeResponseSignature, prepareResponseSignature, signResponse, type PreparedResponseSignature, type SignedResponse, type SignResponseOptions, } from './signer.mjs';
@@ -25,6 +25,7 @@ export { InMemoryRevocationStore, type RevocationStore } from './revocation';
25
25
  export { HttpsRevocationStore, type HttpsRevocationStoreOptions } from './revocation-https';
26
26
  export { ALLOWED_ALGS, CLOCK_SKEW_TOLERANCE_SECONDS, MANDATORY_COMPONENTS, MAX_SIGNATURE_WINDOW_SECONDS, REQUEST_SIGNING_TAG, RESPONSE_MANDATORY_COMPONENTS, RESPONSE_SIGNING_TAG, type AdcpJsonWebKey, type ContentDigestPolicy, type RevocationSnapshot, type VerifiedSigner, type VerifierCapability, type VerifyResult, } from './types';
27
27
  export { verifyRequestSignature, type VerifyRequestOptions } from './verifier';
28
+ export { isUnresolvableOperation, resolveRequestOperation, UNRESOLVABLE_OPERATION, type OperationResolutionInput, type ResolvedOperation, type UnresolvableOperation, } from './operation-resolution';
28
29
  export { createWebhookVerifier, isWebhookLoopbackHost, verifyWebhookSignature, WEBHOOK_MANDATORY_COMPONENTS, WEBHOOK_SIGNING_TAG, type CreateWebhookVerifierOptions, type VerifyWebhookOptions, type VerifyWebhookResult, type WebhookRequestLike, } from './webhook-verifier';
29
30
  export { createExpressVerifier, type ExpressLike, type ExpressMiddlewareOptions } from './middleware';
30
31
  export { finalizeResponseSignature, prepareResponseSignature, signResponse, type PreparedResponseSignature, type SignedResponse, type SignResponseOptions, } from './signer';
@@ -41,6 +41,7 @@ __export(server_exports, {
41
41
  ResolvedAgentJwksResolver: () => import_agent_resolver.ResolvedAgentJwksResolver,
42
42
  ResponseSignatureError: () => import_errors.ResponseSignatureError,
43
43
  StaticJwksResolver: () => import_jwks.StaticJwksResolver,
44
+ UNRESOLVABLE_OPERATION: () => import_operation_resolution.UNRESOLVABLE_OPERATION,
44
45
  WEBHOOK_MANDATORY_COMPONENTS: () => import_webhook_verifier.WEBHOOK_MANDATORY_COMPONENTS,
45
46
  WEBHOOK_SIGNING_TAG: () => import_webhook_verifier.WEBHOOK_SIGNING_TAG,
46
47
  WebhookSignatureError: () => import_errors.WebhookSignatureError,
@@ -61,6 +62,7 @@ __export(server_exports, {
61
62
  getAgentJwks: () => import_agent_resolver.getAgentJwks,
62
63
  getHeaderValue: () => import_canonicalize.getHeaderValue,
63
64
  getReplayStoreMigration: () => import_postgres_replay_store.getReplayStoreMigration,
65
+ isUnresolvableOperation: () => import_operation_resolution.isUnresolvableOperation,
64
66
  isWebhookLoopbackHost: () => import_webhook_verifier.isWebhookLoopbackHost,
65
67
  jwkToPublicKey: () => import_crypto.jwkToPublicKey,
66
68
  parseContentDigest: () => import_content_digest.parseContentDigest,
@@ -73,6 +75,7 @@ __export(server_exports, {
73
75
  requestContextFromFetch: () => import_request_context.requestContextFromFetch,
74
76
  requestContextFromLambda: () => import_request_context.requestContextFromLambda,
75
77
  resolveAgent: () => import_agent_resolver.resolveAgent,
78
+ resolveRequestOperation: () => import_operation_resolution.resolveRequestOperation,
76
79
  signResponse: () => import_signer.signResponse,
77
80
  signResponseAsync: () => import_signer_async.signResponseAsync,
78
81
  sweepExpiredReplays: () => import_postgres_replay_store.sweepExpiredReplays,
@@ -97,6 +100,7 @@ var import_revocation = require('./revocation.js');
97
100
  var import_revocation_https = require('./revocation-https.js');
98
101
  var import_types = require('./types.js');
99
102
  var import_verifier = require('./verifier.js');
103
+ var import_operation_resolution = require('./operation-resolution.js');
100
104
  var import_webhook_verifier = require('./webhook-verifier.js');
101
105
  var import_middleware = require('./middleware.js');
102
106
  var import_signer = require('./signer.js');
@@ -127,6 +131,7 @@ var import_agent_resolver = require('./agent-resolver/index.js');
127
131
  ResolvedAgentJwksResolver,
128
132
  ResponseSignatureError,
129
133
  StaticJwksResolver,
134
+ UNRESOLVABLE_OPERATION,
130
135
  WEBHOOK_MANDATORY_COMPONENTS,
131
136
  WEBHOOK_SIGNING_TAG,
132
137
  WebhookSignatureError,
@@ -147,6 +152,7 @@ var import_agent_resolver = require('./agent-resolver/index.js');
147
152
  getAgentJwks,
148
153
  getHeaderValue,
149
154
  getReplayStoreMigration,
155
+ isUnresolvableOperation,
150
156
  isWebhookLoopbackHost,
151
157
  jwkToPublicKey,
152
158
  parseContentDigest,
@@ -159,6 +165,7 @@ var import_agent_resolver = require('./agent-resolver/index.js');
159
165
  requestContextFromFetch,
160
166
  requestContextFromLambda,
161
167
  resolveAgent,
168
+ resolveRequestOperation,
162
169
  signResponse,
163
170
  signResponseAsync,
164
171
  sweepExpiredReplays,
@@ -52,6 +52,11 @@ import {
52
52
  RESPONSE_SIGNING_TAG
53
53
  } from "./types.mjs";
54
54
  import { verifyRequestSignature } from "./verifier.mjs";
55
+ import {
56
+ isUnresolvableOperation,
57
+ resolveRequestOperation,
58
+ UNRESOLVABLE_OPERATION
59
+ } from "./operation-resolution.mjs";
55
60
  import {
56
61
  createWebhookVerifier,
57
62
  isWebhookLoopbackHost,
@@ -101,6 +106,7 @@ export {
101
106
  ResolvedAgentJwksResolver,
102
107
  ResponseSignatureError,
103
108
  StaticJwksResolver,
109
+ UNRESOLVABLE_OPERATION,
104
110
  WEBHOOK_MANDATORY_COMPONENTS,
105
111
  WEBHOOK_SIGNING_TAG,
106
112
  WebhookSignatureError,
@@ -121,6 +127,7 @@ export {
121
127
  getAgentJwks,
122
128
  getHeaderValue,
123
129
  getReplayStoreMigration,
130
+ isUnresolvableOperation,
124
131
  isWebhookLoopbackHost,
125
132
  jwkToPublicKey,
126
133
  parseContentDigest,
@@ -133,6 +140,7 @@ export {
133
140
  requestContextFromFetch,
134
141
  requestContextFromLambda,
135
142
  resolveAgent,
143
+ resolveRequestOperation,
136
144
  signResponse,
137
145
  signResponseAsync,
138
146
  sweepExpiredReplays,
@@ -1,4 +1,6 @@
1
1
  import { type RequestLike } from './canonicalize.mjs';
2
+ import { RequestSignatureError } from './errors.mjs';
3
+ import { type UnresolvableOperation } from './operation-resolution.mjs';
2
4
  import type { JwksResolver } from './jwks.mjs';
3
5
  import type { ReplayStore } from './replay.mjs';
4
6
  import type { RevocationStore } from './revocation.mjs';
@@ -15,8 +17,13 @@ export interface VerifyRequestOptions {
15
17
  * the verifier treats the operation as "not in any required_for list" and
16
18
  * returns an unsigned result rather than rejecting — callers in
17
19
  * always-verify mode (where every request is signed) can leave this blank.
20
+ *
21
+ * Pass {@link UNRESOLVABLE_OPERATION} when the request body does not resolve
22
+ * to exactly one operation (see `resolveRequestOperation`). The verifier
23
+ * then rejects with `request_body_malformed` before it looks at any
24
+ * signature header or capability list, whether or not the request is signed.
18
25
  */
19
- operation?: string;
26
+ operation?: string | UnresolvableOperation;
20
27
  /**
21
28
  * Trusted endpoint release pin; never inferred from request payload data.
22
29
  * A 3.2+ pin parses `Signature` and `Content-Digest` strictly as RFC 8941
@@ -31,3 +38,31 @@ export interface VerifyRequestOptions {
31
38
  agentUrlForKeyid?: (keyid: string) => string | undefined;
32
39
  }
33
40
  export declare function verifyRequestSignature(request: RequestLike, options: VerifyRequestOptions): Promise<VerifyResult>;
41
+ /** The `request_body_malformed` rejection for a body that resolves to no single operation. */
42
+ export declare function unresolvableOperationError(): RequestSignatureError;
43
+ /**
44
+ * Throw `request_signature_required` when an UNSIGNED request must be signed:
45
+ * its resolved operation is in `capability.required_for`, or its JSON-RPC
46
+ * method is in `capability.protocol_methods_required_for`, or it carries
47
+ * webhook authentication. Returns normally when none applies.
48
+ *
49
+ * "Unsigned" is the caller's contract: no `Signature` / `Signature-Input`
50
+ * header. The checks do not consult any fallback credential; a caller that
51
+ * lets a configured fallback authenticator satisfy `required_for` (the
52
+ * presence-gated composer) runs the pieces separately.
53
+ */
54
+ export declare function assertUnsignedRequestAllowed(body: string | undefined, operation: string | undefined, capability: Pick<VerifierCapability, 'required_for' | 'protocol_methods_required_for'>): void;
55
+ /** `required_for` half of {@link assertUnsignedRequestAllowed}. */
56
+ export declare function assertUnsignedOperationNotRequired(operation: string | undefined, capability: Pick<VerifierCapability, 'required_for'>): void;
57
+ /** `protocol_methods_required_for` half of {@link assertUnsignedRequestAllowed}. */
58
+ export declare function assertUnsignedProtocolMethodNotRequired(body: string | undefined, capability: Pick<VerifierCapability, 'protocol_methods_required_for'>): void;
59
+ /**
60
+ * Payload-driven elevation: any request carrying webhook receiver credentials
61
+ * (task, reporting, artifact, or revocation callbacks) MUST be RFC 9421
62
+ * signed, regardless of whether the operation appears in `required_for`
63
+ * (#webhook-security downgrade-resistance). A bearer-only channel would
64
+ * otherwise let an attacker who captured a token register or update webhook
65
+ * credentials and redirect callbacks to a hostile URL. Unlike `required_for`,
66
+ * a valid fallback credential does not satisfy this.
67
+ */
68
+ export declare function assertUnsignedWebhookAuthenticationAbsent(body: string | undefined): void;
@@ -1,4 +1,6 @@
1
1
  import { type RequestLike } from './canonicalize';
2
+ import { RequestSignatureError } from './errors';
3
+ import { type UnresolvableOperation } from './operation-resolution';
2
4
  import type { JwksResolver } from './jwks';
3
5
  import type { ReplayStore } from './replay';
4
6
  import type { RevocationStore } from './revocation';
@@ -15,8 +17,13 @@ export interface VerifyRequestOptions {
15
17
  * the verifier treats the operation as "not in any required_for list" and
16
18
  * returns an unsigned result rather than rejecting — callers in
17
19
  * always-verify mode (where every request is signed) can leave this blank.
20
+ *
21
+ * Pass {@link UNRESOLVABLE_OPERATION} when the request body does not resolve
22
+ * to exactly one operation (see `resolveRequestOperation`). The verifier
23
+ * then rejects with `request_body_malformed` before it looks at any
24
+ * signature header or capability list, whether or not the request is signed.
18
25
  */
19
- operation?: string;
26
+ operation?: string | UnresolvableOperation;
20
27
  /**
21
28
  * Trusted endpoint release pin; never inferred from request payload data.
22
29
  * A 3.2+ pin parses `Signature` and `Content-Digest` strictly as RFC 8941
@@ -31,3 +38,31 @@ export interface VerifyRequestOptions {
31
38
  agentUrlForKeyid?: (keyid: string) => string | undefined;
32
39
  }
33
40
  export declare function verifyRequestSignature(request: RequestLike, options: VerifyRequestOptions): Promise<VerifyResult>;
41
+ /** The `request_body_malformed` rejection for a body that resolves to no single operation. */
42
+ export declare function unresolvableOperationError(): RequestSignatureError;
43
+ /**
44
+ * Throw `request_signature_required` when an UNSIGNED request must be signed:
45
+ * its resolved operation is in `capability.required_for`, or its JSON-RPC
46
+ * method is in `capability.protocol_methods_required_for`, or it carries
47
+ * webhook authentication. Returns normally when none applies.
48
+ *
49
+ * "Unsigned" is the caller's contract: no `Signature` / `Signature-Input`
50
+ * header. The checks do not consult any fallback credential; a caller that
51
+ * lets a configured fallback authenticator satisfy `required_for` (the
52
+ * presence-gated composer) runs the pieces separately.
53
+ */
54
+ export declare function assertUnsignedRequestAllowed(body: string | undefined, operation: string | undefined, capability: Pick<VerifierCapability, 'required_for' | 'protocol_methods_required_for'>): void;
55
+ /** `required_for` half of {@link assertUnsignedRequestAllowed}. */
56
+ export declare function assertUnsignedOperationNotRequired(operation: string | undefined, capability: Pick<VerifierCapability, 'required_for'>): void;
57
+ /** `protocol_methods_required_for` half of {@link assertUnsignedRequestAllowed}. */
58
+ export declare function assertUnsignedProtocolMethodNotRequired(body: string | undefined, capability: Pick<VerifierCapability, 'protocol_methods_required_for'>): void;
59
+ /**
60
+ * Payload-driven elevation: any request carrying webhook receiver credentials
61
+ * (task, reporting, artifact, or revocation callbacks) MUST be RFC 9421
62
+ * signed, regardless of whether the operation appears in `required_for`
63
+ * (#webhook-security downgrade-resistance). A bearer-only channel would
64
+ * otherwise let an attacker who captured a token register or update webhook
65
+ * credentials and redirect callbacks to a hostile URL. Unlike `required_for`,
66
+ * a valid fallback credential does not satisfy this.
67
+ */
68
+ export declare function assertUnsignedWebhookAuthenticationAbsent(body: string | undefined): void;
@@ -18,6 +18,11 @@ var __copyProps = (to, from, except, desc) => {
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
  var verifier_exports = {};
20
20
  __export(verifier_exports, {
21
+ assertUnsignedOperationNotRequired: () => assertUnsignedOperationNotRequired,
22
+ assertUnsignedProtocolMethodNotRequired: () => assertUnsignedProtocolMethodNotRequired,
23
+ assertUnsignedRequestAllowed: () => assertUnsignedRequestAllowed,
24
+ assertUnsignedWebhookAuthenticationAbsent: () => assertUnsignedWebhookAuthenticationAbsent,
25
+ unresolvableOperationError: () => unresolvableOperationError,
21
26
  verifyRequestSignature: () => verifyRequestSignature
22
27
  });
23
28
  module.exports = __toCommonJS(verifier_exports);
@@ -29,6 +34,7 @@ var import_errors2 = require('./agent-resolver/errors.js');
29
34
  var import_strict_json = require('./agent-resolver/strict-json.js');
30
35
  var import_parser = require('./parser.js');
31
36
  var import_crypto = require('./crypto.js');
37
+ var import_operation_resolution = require('./operation-resolution.js');
32
38
  var import_webhook_auth_detection = require('./webhook-auth-detection.js');
33
39
  var import_types = require('./types.js');
34
40
  async function verifyRequestSignature(request, options) {
@@ -37,39 +43,10 @@ async function verifyRequestSignature(request, options) {
37
43
  const pinnedBinaryEncoding = options.adcpVersion !== void 0 ? (0, import_content_digest.requestSigningEncodingForVersion)(options.adcpVersion) : void 0;
38
44
  const sigInputHeader = (0, import_canonicalize.getHeaderValue)(request.headers, "Signature-Input");
39
45
  const sigHeader = (0, import_canonicalize.getHeaderValue)(request.headers, "Signature");
46
+ if ((0, import_operation_resolution.isUnresolvableOperation)(options.operation)) throw unresolvableOperationError();
47
+ const operation = options.operation;
40
48
  if (!sigInputHeader && !sigHeader) {
41
- const operation = options.operation;
42
- if (operation && options.capability.required_for.includes(operation)) {
43
- throw new import_errors.RequestSignatureError(
44
- "request_signature_required",
45
- 0,
46
- `Operation "${operation}" requires a signed request`
47
- );
48
- }
49
- const protocolMethodsRequiredFor = options.capability.protocol_methods_required_for ?? [];
50
- if (protocolMethodsRequiredFor.length > 0 && exceedsUnsignedBodyInspectionCap(request.body)) {
51
- throw new import_errors.RequestSignatureError(
52
- "request_signature_required",
53
- 0,
54
- "Unsigned request body exceeds the protocol method inspection cap while protocol methods require signing"
55
- );
56
- }
57
- const protocolMethods = jsonRpcProtocolMethods(request.body);
58
- const requiredProtocolMethod = protocolMethods.find((method) => protocolMethodsRequiredFor.includes(method));
59
- if (requiredProtocolMethod) {
60
- throw new import_errors.RequestSignatureError(
61
- "request_signature_required",
62
- 0,
63
- `Protocol method "${requiredProtocolMethod}" requires a signed request`
64
- );
65
- }
66
- if (carriesWebhookAuthentication(request)) {
67
- throw new import_errors.RequestSignatureError(
68
- "request_signature_required",
69
- 0,
70
- "Requests carrying webhook authentication must be RFC 9421 signed"
71
- );
72
- }
49
+ assertUnsignedRequestAllowed(request.body, operation, options.capability);
73
50
  return { status: "unsigned", verified_at: now };
74
51
  }
75
52
  if (!sigInputHeader || !sigHeader) {
@@ -292,6 +269,57 @@ function signatureEncodingCandidates(pinned, digestPolicy) {
292
269
  if (pinned === "rfc8941-base64" || digestPolicy !== "either") return [pinned];
293
270
  return ["legacy-base64url", "rfc8941-base64"];
294
271
  }
272
+ function unresolvableOperationError() {
273
+ return new import_errors.RequestSignatureError(
274
+ "request_body_malformed",
275
+ 0,
276
+ "Request body does not resolve to exactly one AdCP operation"
277
+ );
278
+ }
279
+ function assertUnsignedRequestAllowed(body, operation, capability) {
280
+ assertUnsignedOperationNotRequired(operation, capability);
281
+ assertUnsignedProtocolMethodNotRequired(body, capability);
282
+ assertUnsignedWebhookAuthenticationAbsent(body);
283
+ }
284
+ function assertUnsignedOperationNotRequired(operation, capability) {
285
+ if (operation && capability.required_for.includes(operation)) {
286
+ throw new import_errors.RequestSignatureError(
287
+ "request_signature_required",
288
+ 0,
289
+ `Operation "${operation}" requires a signed request`
290
+ );
291
+ }
292
+ }
293
+ function assertUnsignedProtocolMethodNotRequired(body, capability) {
294
+ const protocolMethodsRequiredFor = capability.protocol_methods_required_for ?? [];
295
+ if (protocolMethodsRequiredFor.length === 0) return;
296
+ if (exceedsUnsignedBodyInspectionCap(body)) {
297
+ throw new import_errors.RequestSignatureError(
298
+ "request_signature_required",
299
+ 0,
300
+ "Unsigned request body exceeds the protocol method inspection cap while protocol methods require signing"
301
+ );
302
+ }
303
+ const requiredProtocolMethod = jsonRpcProtocolMethods(body).find(
304
+ (method) => protocolMethodsRequiredFor.includes(method)
305
+ );
306
+ if (requiredProtocolMethod) {
307
+ throw new import_errors.RequestSignatureError(
308
+ "request_signature_required",
309
+ 0,
310
+ `Protocol method "${requiredProtocolMethod}" requires a signed request`
311
+ );
312
+ }
313
+ }
314
+ function assertUnsignedWebhookAuthenticationAbsent(body) {
315
+ if (carriesWebhookAuthentication(body)) {
316
+ throw new import_errors.RequestSignatureError(
317
+ "request_signature_required",
318
+ 0,
319
+ "Requests carrying webhook authentication must be RFC 9421 signed"
320
+ );
321
+ }
322
+ }
295
323
  function contentDigestEncodingCandidates(pinned) {
296
324
  if (pinned === "rfc8941-base64") return [pinned];
297
325
  return ["rfc8941-base64", "legacy-base64url"];
@@ -498,8 +526,7 @@ const MAX_UNSIGNED_BODY_INSPECTION_BYTES = 1048576;
498
526
  function exceedsUnsignedBodyInspectionCap(body) {
499
527
  return typeof body === "string" && body.length > MAX_UNSIGNED_BODY_INSPECTION_BYTES;
500
528
  }
501
- function carriesWebhookAuthentication(request) {
502
- const body = request.body;
529
+ function carriesWebhookAuthentication(body) {
503
530
  if (!body) return false;
504
531
  if (exceedsUnsignedBodyInspectionCap(body)) return true;
505
532
  let parsed;
@@ -546,5 +573,10 @@ function validateCoveredComponents(components, capability, request) {
546
573
  }
547
574
  // Annotate the CommonJS export names for ESM import in node:
548
575
  0 && (module.exports = {
576
+ assertUnsignedOperationNotRequired,
577
+ assertUnsignedProtocolMethodNotRequired,
578
+ assertUnsignedRequestAllowed,
579
+ assertUnsignedWebhookAuthenticationAbsent,
580
+ unresolvableOperationError,
549
581
  verifyRequestSignature
550
582
  });
@@ -16,6 +16,7 @@ import { AgentResolverError } from "./agent-resolver/errors.mjs";
16
16
  import { parseStrictJson, StrictJsonError } from "./agent-resolver/strict-json.mjs";
17
17
  import { parseSignature, parseSignatureInput } from "./parser.mjs";
18
18
  import { jwkToPublicKey, verifySignature } from "./crypto.mjs";
19
+ import { isUnresolvableOperation } from "./operation-resolution.mjs";
19
20
  import { containsWebhookAuthentication, WEBHOOK_AUTH_TRAVERSAL_DEPTH } from "./webhook-auth-detection.mjs";
20
21
  import {
21
22
  ALLOWED_ALGS,
@@ -30,39 +31,10 @@ async function verifyRequestSignature(request, options) {
30
31
  const pinnedBinaryEncoding = options.adcpVersion !== void 0 ? requestSigningEncodingForVersion(options.adcpVersion) : void 0;
31
32
  const sigInputHeader = getHeaderValue(request.headers, "Signature-Input");
32
33
  const sigHeader = getHeaderValue(request.headers, "Signature");
34
+ if (isUnresolvableOperation(options.operation)) throw unresolvableOperationError();
35
+ const operation = options.operation;
33
36
  if (!sigInputHeader && !sigHeader) {
34
- const operation = options.operation;
35
- if (operation && options.capability.required_for.includes(operation)) {
36
- throw new RequestSignatureError(
37
- "request_signature_required",
38
- 0,
39
- `Operation "${operation}" requires a signed request`
40
- );
41
- }
42
- const protocolMethodsRequiredFor = options.capability.protocol_methods_required_for ?? [];
43
- if (protocolMethodsRequiredFor.length > 0 && exceedsUnsignedBodyInspectionCap(request.body)) {
44
- throw new RequestSignatureError(
45
- "request_signature_required",
46
- 0,
47
- "Unsigned request body exceeds the protocol method inspection cap while protocol methods require signing"
48
- );
49
- }
50
- const protocolMethods = jsonRpcProtocolMethods(request.body);
51
- const requiredProtocolMethod = protocolMethods.find((method) => protocolMethodsRequiredFor.includes(method));
52
- if (requiredProtocolMethod) {
53
- throw new RequestSignatureError(
54
- "request_signature_required",
55
- 0,
56
- `Protocol method "${requiredProtocolMethod}" requires a signed request`
57
- );
58
- }
59
- if (carriesWebhookAuthentication(request)) {
60
- throw new RequestSignatureError(
61
- "request_signature_required",
62
- 0,
63
- "Requests carrying webhook authentication must be RFC 9421 signed"
64
- );
65
- }
37
+ assertUnsignedRequestAllowed(request.body, operation, options.capability);
66
38
  return { status: "unsigned", verified_at: now };
67
39
  }
68
40
  if (!sigInputHeader || !sigHeader) {
@@ -285,6 +257,57 @@ function signatureEncodingCandidates(pinned, digestPolicy) {
285
257
  if (pinned === "rfc8941-base64" || digestPolicy !== "either") return [pinned];
286
258
  return ["legacy-base64url", "rfc8941-base64"];
287
259
  }
260
+ function unresolvableOperationError() {
261
+ return new RequestSignatureError(
262
+ "request_body_malformed",
263
+ 0,
264
+ "Request body does not resolve to exactly one AdCP operation"
265
+ );
266
+ }
267
+ function assertUnsignedRequestAllowed(body, operation, capability) {
268
+ assertUnsignedOperationNotRequired(operation, capability);
269
+ assertUnsignedProtocolMethodNotRequired(body, capability);
270
+ assertUnsignedWebhookAuthenticationAbsent(body);
271
+ }
272
+ function assertUnsignedOperationNotRequired(operation, capability) {
273
+ if (operation && capability.required_for.includes(operation)) {
274
+ throw new RequestSignatureError(
275
+ "request_signature_required",
276
+ 0,
277
+ `Operation "${operation}" requires a signed request`
278
+ );
279
+ }
280
+ }
281
+ function assertUnsignedProtocolMethodNotRequired(body, capability) {
282
+ const protocolMethodsRequiredFor = capability.protocol_methods_required_for ?? [];
283
+ if (protocolMethodsRequiredFor.length === 0) return;
284
+ if (exceedsUnsignedBodyInspectionCap(body)) {
285
+ throw new RequestSignatureError(
286
+ "request_signature_required",
287
+ 0,
288
+ "Unsigned request body exceeds the protocol method inspection cap while protocol methods require signing"
289
+ );
290
+ }
291
+ const requiredProtocolMethod = jsonRpcProtocolMethods(body).find(
292
+ (method) => protocolMethodsRequiredFor.includes(method)
293
+ );
294
+ if (requiredProtocolMethod) {
295
+ throw new RequestSignatureError(
296
+ "request_signature_required",
297
+ 0,
298
+ `Protocol method "${requiredProtocolMethod}" requires a signed request`
299
+ );
300
+ }
301
+ }
302
+ function assertUnsignedWebhookAuthenticationAbsent(body) {
303
+ if (carriesWebhookAuthentication(body)) {
304
+ throw new RequestSignatureError(
305
+ "request_signature_required",
306
+ 0,
307
+ "Requests carrying webhook authentication must be RFC 9421 signed"
308
+ );
309
+ }
310
+ }
288
311
  function contentDigestEncodingCandidates(pinned) {
289
312
  if (pinned === "rfc8941-base64") return [pinned];
290
313
  return ["rfc8941-base64", "legacy-base64url"];
@@ -491,8 +514,7 @@ const MAX_UNSIGNED_BODY_INSPECTION_BYTES = 1048576;
491
514
  function exceedsUnsignedBodyInspectionCap(body) {
492
515
  return typeof body === "string" && body.length > MAX_UNSIGNED_BODY_INSPECTION_BYTES;
493
516
  }
494
- function carriesWebhookAuthentication(request) {
495
- const body = request.body;
517
+ function carriesWebhookAuthentication(body) {
496
518
  if (!body) return false;
497
519
  if (exceedsUnsignedBodyInspectionCap(body)) return true;
498
520
  let parsed;
@@ -538,5 +560,10 @@ function validateCoveredComponents(components, capability, request) {
538
560
  }
539
561
  }
540
562
  export {
563
+ assertUnsignedOperationNotRequired,
564
+ assertUnsignedProtocolMethodNotRequired,
565
+ assertUnsignedRequestAllowed,
566
+ assertUnsignedWebhookAuthenticationAbsent,
567
+ unresolvableOperationError,
541
568
  verifyRequestSignature
542
569
  };