@adcp/sdk 16.0.0-beta.2 → 16.0.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/README.md +54 -0
  2. package/dist/lib/core/ConversationTypes.d.mts +8 -3
  3. package/dist/lib/core/ConversationTypes.d.ts +8 -3
  4. package/dist/lib/core/SingleAgentClient.d.mts +5 -3
  5. package/dist/lib/core/SingleAgentClient.d.ts +5 -3
  6. package/dist/lib/core/TaskExecutor.js +5 -1
  7. package/dist/lib/core/TaskExecutor.mjs +5 -1
  8. package/dist/lib/errors/index.d.mts +9 -4
  9. package/dist/lib/errors/index.d.ts +9 -4
  10. package/dist/lib/index.d.mts +2 -2
  11. package/dist/lib/index.d.ts +2 -2
  12. package/dist/lib/index.js +6 -0
  13. package/dist/lib/index.mjs +7 -1
  14. package/dist/lib/negotiation/types.d.mts +20 -39
  15. package/dist/lib/negotiation/types.d.ts +20 -39
  16. package/dist/lib/protocols/index.d.mts +60 -24
  17. package/dist/lib/protocols/index.d.ts +60 -24
  18. package/dist/lib/protocols/index.js +9 -4
  19. package/dist/lib/protocols/index.mjs +3 -0
  20. package/dist/lib/protocols/mcp-modern.js +1 -0
  21. package/dist/lib/protocols/mcp-modern.mjs +2 -1
  22. package/dist/lib/protocols/mcp.js +1 -0
  23. package/dist/lib/protocols/mcp.mjs +2 -1
  24. package/dist/lib/protocols/responseSizeLimit.d.mts +91 -5
  25. package/dist/lib/protocols/responseSizeLimit.d.ts +91 -5
  26. package/dist/lib/protocols/responseSizeLimit.js +195 -31
  27. package/dist/lib/protocols/responseSizeLimit.mjs +192 -31
  28. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  29. package/dist/lib/signing/crypto.d.mts +15 -0
  30. package/dist/lib/signing/crypto.d.ts +15 -0
  31. package/dist/lib/signing/crypto.js +75 -3
  32. package/dist/lib/signing/crypto.mjs +78 -3
  33. package/dist/lib/signing/server.d.mts +1 -1
  34. package/dist/lib/signing/server.d.ts +1 -1
  35. package/dist/lib/signing/server.js +2 -0
  36. package/dist/lib/signing/server.mjs +2 -1
  37. package/dist/lib/signing/signer.js +2 -8
  38. package/dist/lib/signing/signer.mjs +3 -9
  39. package/dist/lib/signing/testing.js +2 -6
  40. package/dist/lib/signing/testing.mjs +3 -7
  41. package/dist/lib/signing/verifier.js +2 -2
  42. package/dist/lib/signing/verifier.mjs +3 -3
  43. package/dist/lib/signing/web-bot-auth.js +20 -7
  44. package/dist/lib/signing/web-bot-auth.mjs +21 -8
  45. package/dist/lib/signing/webhook-verifier.js +2 -2
  46. package/dist/lib/signing/webhook-verifier.mjs +3 -3
  47. package/dist/lib/substitution/compile.js +2 -1
  48. package/dist/lib/substitution/compile.mjs +2 -1
  49. package/dist/lib/substitution/consent-macros.d.mts +4 -0
  50. package/dist/lib/substitution/consent-macros.d.ts +4 -0
  51. package/dist/lib/substitution/consent-macros.js +45 -0
  52. package/dist/lib/substitution/consent-macros.mjs +20 -0
  53. package/dist/lib/substitution/index.d.mts +1 -1
  54. package/dist/lib/substitution/index.d.ts +1 -1
  55. package/dist/lib/substitution/index.js +2 -0
  56. package/dist/lib/substitution/index.mjs +2 -1
  57. package/dist/lib/substitution/translate.d.mts +15 -2
  58. package/dist/lib/substitution/translate.d.ts +15 -2
  59. package/dist/lib/substitution/translate.js +148 -26
  60. package/dist/lib/substitution/translate.mjs +147 -26
  61. package/dist/lib/testing/client.js +2 -1
  62. package/dist/lib/testing/client.mjs +2 -1
  63. package/dist/lib/testing/scenarios/capabilities.js +1 -1
  64. package/dist/lib/testing/scenarios/capabilities.mjs +1 -1
  65. package/dist/lib/testing/storyboard/request-signing/builder.js +2 -5
  66. package/dist/lib/testing/storyboard/request-signing/builder.mjs +3 -6
  67. package/dist/lib/testing/storyboard/signer-grader/grader.js +2 -7
  68. package/dist/lib/testing/storyboard/signer-grader/grader.mjs +2 -7
  69. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  70. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  71. package/dist/lib/types/enums.generated.d.mts +3 -0
  72. package/dist/lib/types/enums.generated.d.ts +3 -0
  73. package/dist/lib/types/enums.generated.js +9 -0
  74. package/dist/lib/types/enums.generated.mjs +6 -0
  75. package/dist/lib/validation/schema-loader.js +25 -0
  76. package/dist/lib/validation/schema-loader.mjs +24 -0
  77. package/dist/lib/version.d.mts +3 -3
  78. package/dist/lib/version.d.ts +3 -3
  79. package/dist/lib/version.js +3 -3
  80. package/dist/lib/version.mjs +3 -3
  81. package/docs/TYPE-SUMMARY.md +6 -1
  82. package/docs/guides/DISCOVERY-CACHE-AND-SESSION-REUSE.md +3 -2
  83. package/docs/guides/UNIVERSAL-MACRO-COMPILATION.md +38 -0
  84. package/docs/guides/VALIDATE-YOUR-AGENT.md +12 -0
  85. package/docs/llms.txt +1 -1
  86. package/docs/migration-14.x-rc-worksheet.md +4 -4
  87. package/package.json +2 -1
@@ -18,6 +18,9 @@ var __copyProps = (to, from, except, desc) => {
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
  var responseSizeLimit_exports = {};
20
20
  __export(responseSizeLimit_exports, {
21
+ DEFAULT_MAX_RESPONSE_BYTES: () => DEFAULT_MAX_RESPONSE_BYTES,
22
+ SSE_CUMULATIVE_BUDGET_MULTIPLIER: () => SSE_CUMULATIVE_BUDGET_MULTIPLIER,
23
+ responseSizeCapCacheKey: () => responseSizeCapCacheKey,
21
24
  responseSizeLimitStorage: () => responseSizeLimitStorage,
22
25
  withResponseSizeLimit: () => withResponseSizeLimit,
23
26
  wrapFetchWithSizeLimit: () => wrapFetchWithSizeLimit
@@ -25,52 +28,116 @@ __export(responseSizeLimit_exports, {
25
28
  module.exports = __toCommonJS(responseSizeLimit_exports);
26
29
  var import_global_async_local_storage = require('../utils/global-async-local-storage.js');
27
30
  var import_errors = require('../errors/index.js');
31
+ var import_transportDiagnostics = require('./transportDiagnostics.js');
32
+ const DEFAULT_MAX_RESPONSE_BYTES = 4 * 1024 * 1024;
33
+ const SSE_CUMULATIVE_BUDGET_MULTIPLIER = 2;
28
34
  const responseSizeLimitStorage = (0, import_global_async_local_storage.globalAsyncLocalStorage)("responseSizeLimit");
35
+ function resolveResponseSizeCap(maxResponseBytes) {
36
+ if (maxResponseBytes === void 0) return DEFAULT_MAX_RESPONSE_BYTES;
37
+ if (maxResponseBytes === 0) return null;
38
+ if (!Number.isFinite(maxResponseBytes) || maxResponseBytes < 0) return DEFAULT_MAX_RESPONSE_BYTES;
39
+ return maxResponseBytes;
40
+ }
41
+ function newSlot(maxResponseBytes) {
42
+ const resolved = resolveResponseSizeCap(maxResponseBytes);
43
+ if (resolved === null) return { maxResponseBytes: null };
44
+ const enclosing = responseSizeLimitStorage.getStore();
45
+ const limit = sseBudgetFor(resolved);
46
+ return {
47
+ // Nesting narrows both numbers. `?? resolved` deliberately also covers an
48
+ // enclosing opt-out (`null`): an inner scope that asks for a cap is the
49
+ // more specific instruction, so it still gets one.
50
+ maxResponseBytes: Math.min(resolved, enclosing?.maxResponseBytes ?? resolved),
51
+ streamLimit: Math.min(limit, enclosing?.streamLimit ?? limit),
52
+ streamMeter: enclosing?.streamMeter ?? { consumed: 0 }
53
+ };
54
+ }
55
+ function sseBudgetFor(maxResponseBytes) {
56
+ return Math.min(maxResponseBytes * SSE_CUMULATIVE_BUDGET_MULTIPLIER, Number.MAX_SAFE_INTEGER);
57
+ }
58
+ function responseSizeCapCacheKey() {
59
+ const slot = responseSizeLimitStorage.getStore();
60
+ const cap = slot ? slot.maxResponseBytes : DEFAULT_MAX_RESPONSE_BYTES;
61
+ return `max-response-bytes:${cap === null ? "off" : cap}`;
62
+ }
29
63
  function withResponseSizeLimit(maxResponseBytes, fn) {
30
- if (!maxResponseBytes || !Number.isFinite(maxResponseBytes) || maxResponseBytes <= 0) {
64
+ if (maxResponseBytes === void 0 && responseSizeLimitStorage.getStore()) {
31
65
  return fn();
32
66
  }
33
- return responseSizeLimitStorage.run({ maxResponseBytes }, fn);
67
+ const slot = newSlot(maxResponseBytes);
68
+ return responseSizeLimitStorage.run(slot, () => rethrowRecordedViolation(slot, fn));
69
+ }
70
+ async function rethrowRecordedViolation(slot, fn) {
71
+ try {
72
+ return await fn();
73
+ } catch (error) {
74
+ const violation = slot.violation;
75
+ if (!violation || error instanceof import_errors.ResponseTooLargeError) throw error;
76
+ const surfaced = new import_errors.ResponseTooLargeError(
77
+ violation.limit,
78
+ violation.bytesRead,
79
+ violation.url,
80
+ violation.contentLengthHeader
81
+ );
82
+ Object.defineProperty(surfaced, "cause", { value: error, configurable: true });
83
+ throw surfaced;
84
+ }
85
+ }
86
+ function recordViolation(slot, error) {
87
+ if (slot && !slot.violation) slot.violation = error;
88
+ return error;
34
89
  }
35
90
  function urlOfInput(input) {
36
91
  if (typeof input === "string") return input;
37
92
  if (input instanceof URL) return input.toString();
38
93
  return input.url;
39
94
  }
40
- function redactUrlForError(url) {
41
- try {
42
- const parsed = new URL(url);
43
- parsed.search = "";
44
- return parsed.toString();
45
- } catch {
46
- return url;
47
- }
48
- }
49
- function withIdentityEncoding(init) {
50
- const headers = new Headers(init?.headers);
51
- if (!headers.has("accept-encoding")) {
52
- headers.set("accept-encoding", "identity");
53
- }
54
- return { ...init ?? {}, headers };
55
- }
56
95
  function wrapFetchWithSizeLimit(upstream) {
57
96
  const wrapped = async (input, init) => {
58
97
  const slot = responseSizeLimitStorage.getStore();
59
- if (!slot) return upstream(input, init);
60
- const response = await upstream(input, withIdentityEncoding(init));
61
- return enforceSizeLimit(response, slot.maxResponseBytes, redactUrlForError(urlOfInput(input)));
98
+ const maxBytes = slot ? slot.maxResponseBytes : DEFAULT_MAX_RESPONSE_BYTES;
99
+ if (maxBytes === null) return upstream(input, init);
100
+ const response = await upstream(input, init);
101
+ if (NULL_BODY_STATUSES.has(response.status)) return response;
102
+ const skipDeclaredLength = methodOf(input, init) === "HEAD";
103
+ const url = (0, import_transportDiagnostics.sanitizeTransportUrl)(urlOfInput(input));
104
+ if (isEventStream(response)) {
105
+ const connectionScoped = methodOf(input, init) === "GET";
106
+ const { limit, meter } = streamBudgetFor(connectionScoped, slot, maxBytes);
107
+ return enforceEventStreamLimit(
108
+ response,
109
+ maxBytes,
110
+ limit,
111
+ meter,
112
+ url,
113
+ connectionScoped ? void 0 : slot,
114
+ skipDeclaredLength
115
+ );
116
+ }
117
+ return enforceSizeLimit(response, maxBytes, url, slot, skipDeclaredLength);
62
118
  };
63
119
  return wrapped;
64
120
  }
65
- function enforceSizeLimit(response, maxBytes, url) {
66
- if (response.headers.get("content-type")?.toLowerCase().startsWith("text/event-stream")) {
67
- return response;
68
- }
69
- const declared = parseContentLength(response.headers.get("content-length"));
121
+ function streamBudgetFor(connectionScoped, slot, maxBytes) {
122
+ if (connectionScoped) return { limit: sseBudgetFor(maxBytes), meter: { consumed: 0 } };
123
+ return {
124
+ limit: slot?.streamLimit ?? sseBudgetFor(maxBytes),
125
+ meter: slot?.streamMeter ?? { consumed: 0 }
126
+ };
127
+ }
128
+ function methodOf(input, init) {
129
+ const method = init?.method ?? (typeof Request !== "undefined" && input instanceof Request ? input.method : "GET");
130
+ return method.toUpperCase();
131
+ }
132
+ const NULL_BODY_STATUSES = /* @__PURE__ */ new Set([101, 103, 204, 205, 304]);
133
+ function isEventStream(response) {
134
+ return response.headers.get("content-type")?.toLowerCase().startsWith("text/event-stream") === true;
135
+ }
136
+ function enforceSizeLimit(response, maxBytes, url, slot, skipDeclaredLength = false) {
137
+ const declared = skipDeclaredLength ? void 0 : parseContentLength(response.headers.get("content-length"));
70
138
  if (declared !== void 0 && declared > maxBytes) {
71
- response.body?.cancel().catch(() => {
72
- });
73
- throw new import_errors.ResponseTooLargeError(maxBytes, 0, url, declared);
139
+ cancelBody(response);
140
+ throw recordViolation(slot, new import_errors.ResponseTooLargeError(maxBytes, 0, url, declared));
74
141
  }
75
142
  if (!response.body) return response;
76
143
  let bytesRead = 0;
@@ -78,13 +145,107 @@ function enforceSizeLimit(response, maxBytes, url) {
78
145
  transform(chunk, controller) {
79
146
  bytesRead += chunk.byteLength;
80
147
  if (bytesRead > maxBytes) {
81
- controller.error(new import_errors.ResponseTooLargeError(maxBytes, bytesRead, url));
148
+ controller.error(recordViolation(slot, new import_errors.ResponseTooLargeError(maxBytes, bytesRead, url)));
82
149
  return;
83
150
  }
84
151
  controller.enqueue(chunk);
85
152
  }
86
153
  });
87
- return new Response(response.body.pipeThrough(counter), {
154
+ return respondWith(response, response.body.pipeThrough(counter));
155
+ }
156
+ const CR = 13;
157
+ const LF = 10;
158
+ function enforceEventStreamLimit(response, maxBytes, streamLimit, meter, url, slot, skipDeclaredLength = false) {
159
+ const remaining = streamLimit - meter.consumed;
160
+ const declared = skipDeclaredLength ? void 0 : parseContentLength(response.headers.get("content-length"));
161
+ if (declared !== void 0 && declared > remaining) {
162
+ cancelBody(response);
163
+ throw recordViolation(slot, new import_errors.ResponseTooLargeError(streamLimit, meter.consumed, url, declared));
164
+ }
165
+ if (!response.body) return response;
166
+ let bytesInEvent = 0;
167
+ let lineEndings = 0;
168
+ let pendingCR = false;
169
+ let separatorTail = false;
170
+ let pendingReset = false;
171
+ const counter = new TransformStream({
172
+ transform(chunk, controller) {
173
+ meter.consumed += chunk.byteLength;
174
+ if (meter.consumed > streamLimit) {
175
+ controller.error(recordViolation(slot, new import_errors.ResponseTooLargeError(streamLimit, meter.consumed, url)));
176
+ return;
177
+ }
178
+ if (chunk.length === 0) {
179
+ controller.enqueue(chunk);
180
+ return;
181
+ }
182
+ if (chunk.indexOf(LF) === -1 && chunk.indexOf(CR) === -1) {
183
+ if (pendingReset) {
184
+ pendingReset = false;
185
+ separatorTail = false;
186
+ bytesInEvent = 0;
187
+ }
188
+ bytesInEvent += chunk.length;
189
+ lineEndings = 0;
190
+ pendingCR = false;
191
+ if (bytesInEvent > maxBytes) {
192
+ controller.error(recordViolation(slot, new import_errors.ResponseTooLargeError(maxBytes, bytesInEvent, url)));
193
+ return;
194
+ }
195
+ controller.enqueue(chunk);
196
+ return;
197
+ }
198
+ for (let i = 0; i < chunk.length; i += 1) {
199
+ const byte = chunk[i];
200
+ if (pendingReset) {
201
+ pendingReset = false;
202
+ if (separatorTail && byte === LF) {
203
+ separatorTail = false;
204
+ pendingCR = false;
205
+ bytesInEvent += 1;
206
+ if (bytesInEvent > maxBytes) {
207
+ controller.error(recordViolation(slot, new import_errors.ResponseTooLargeError(maxBytes, bytesInEvent, url)));
208
+ return;
209
+ }
210
+ bytesInEvent = 0;
211
+ continue;
212
+ }
213
+ separatorTail = false;
214
+ bytesInEvent = 0;
215
+ }
216
+ bytesInEvent += 1;
217
+ if (byte === CR) {
218
+ lineEndings += 1;
219
+ pendingCR = true;
220
+ } else if (byte === LF) {
221
+ if (!pendingCR) lineEndings += 1;
222
+ pendingCR = false;
223
+ } else {
224
+ lineEndings = 0;
225
+ pendingCR = false;
226
+ }
227
+ if (bytesInEvent > maxBytes) {
228
+ controller.error(recordViolation(slot, new import_errors.ResponseTooLargeError(maxBytes, bytesInEvent, url)));
229
+ return;
230
+ }
231
+ if (lineEndings >= 2) {
232
+ lineEndings = 0;
233
+ pendingReset = true;
234
+ separatorTail = pendingCR;
235
+ pendingCR = false;
236
+ }
237
+ }
238
+ controller.enqueue(chunk);
239
+ }
240
+ });
241
+ return respondWith(response, response.body.pipeThrough(counter));
242
+ }
243
+ function cancelBody(response) {
244
+ response.body?.cancel().catch(() => {
245
+ });
246
+ }
247
+ function respondWith(response, body) {
248
+ return new Response(body, {
88
249
  status: response.status,
89
250
  statusText: response.statusText,
90
251
  headers: response.headers
@@ -99,6 +260,9 @@ function parseContentLength(value) {
99
260
  }
100
261
  // Annotate the CommonJS export names for ESM import in node:
101
262
  0 && (module.exports = {
263
+ DEFAULT_MAX_RESPONSE_BYTES,
264
+ SSE_CUMULATIVE_BUDGET_MULTIPLIER,
265
+ responseSizeCapCacheKey,
102
266
  responseSizeLimitStorage,
103
267
  withResponseSizeLimit,
104
268
  wrapFetchWithSizeLimit
@@ -5,52 +5,116 @@ const __dirname = __adcpDirname(__adcpFileURLToPath(import.meta.url));
5
5
  const require2 = __adcpCreateRequire(import.meta.url);
6
6
  import { globalAsyncLocalStorage } from "../utils/global-async-local-storage.mjs";
7
7
  import { ResponseTooLargeError } from "../errors/index.mjs";
8
+ import { sanitizeTransportUrl } from "./transportDiagnostics.mjs";
9
+ const DEFAULT_MAX_RESPONSE_BYTES = 4 * 1024 * 1024;
10
+ const SSE_CUMULATIVE_BUDGET_MULTIPLIER = 2;
8
11
  const responseSizeLimitStorage = globalAsyncLocalStorage("responseSizeLimit");
12
+ function resolveResponseSizeCap(maxResponseBytes) {
13
+ if (maxResponseBytes === void 0) return DEFAULT_MAX_RESPONSE_BYTES;
14
+ if (maxResponseBytes === 0) return null;
15
+ if (!Number.isFinite(maxResponseBytes) || maxResponseBytes < 0) return DEFAULT_MAX_RESPONSE_BYTES;
16
+ return maxResponseBytes;
17
+ }
18
+ function newSlot(maxResponseBytes) {
19
+ const resolved = resolveResponseSizeCap(maxResponseBytes);
20
+ if (resolved === null) return { maxResponseBytes: null };
21
+ const enclosing = responseSizeLimitStorage.getStore();
22
+ const limit = sseBudgetFor(resolved);
23
+ return {
24
+ // Nesting narrows both numbers. `?? resolved` deliberately also covers an
25
+ // enclosing opt-out (`null`): an inner scope that asks for a cap is the
26
+ // more specific instruction, so it still gets one.
27
+ maxResponseBytes: Math.min(resolved, enclosing?.maxResponseBytes ?? resolved),
28
+ streamLimit: Math.min(limit, enclosing?.streamLimit ?? limit),
29
+ streamMeter: enclosing?.streamMeter ?? { consumed: 0 }
30
+ };
31
+ }
32
+ function sseBudgetFor(maxResponseBytes) {
33
+ return Math.min(maxResponseBytes * SSE_CUMULATIVE_BUDGET_MULTIPLIER, Number.MAX_SAFE_INTEGER);
34
+ }
35
+ function responseSizeCapCacheKey() {
36
+ const slot = responseSizeLimitStorage.getStore();
37
+ const cap = slot ? slot.maxResponseBytes : DEFAULT_MAX_RESPONSE_BYTES;
38
+ return `max-response-bytes:${cap === null ? "off" : cap}`;
39
+ }
9
40
  function withResponseSizeLimit(maxResponseBytes, fn) {
10
- if (!maxResponseBytes || !Number.isFinite(maxResponseBytes) || maxResponseBytes <= 0) {
41
+ if (maxResponseBytes === void 0 && responseSizeLimitStorage.getStore()) {
11
42
  return fn();
12
43
  }
13
- return responseSizeLimitStorage.run({ maxResponseBytes }, fn);
44
+ const slot = newSlot(maxResponseBytes);
45
+ return responseSizeLimitStorage.run(slot, () => rethrowRecordedViolation(slot, fn));
46
+ }
47
+ async function rethrowRecordedViolation(slot, fn) {
48
+ try {
49
+ return await fn();
50
+ } catch (error) {
51
+ const violation = slot.violation;
52
+ if (!violation || error instanceof ResponseTooLargeError) throw error;
53
+ const surfaced = new ResponseTooLargeError(
54
+ violation.limit,
55
+ violation.bytesRead,
56
+ violation.url,
57
+ violation.contentLengthHeader
58
+ );
59
+ Object.defineProperty(surfaced, "cause", { value: error, configurable: true });
60
+ throw surfaced;
61
+ }
62
+ }
63
+ function recordViolation(slot, error) {
64
+ if (slot && !slot.violation) slot.violation = error;
65
+ return error;
14
66
  }
15
67
  function urlOfInput(input) {
16
68
  if (typeof input === "string") return input;
17
69
  if (input instanceof URL) return input.toString();
18
70
  return input.url;
19
71
  }
20
- function redactUrlForError(url) {
21
- try {
22
- const parsed = new URL(url);
23
- parsed.search = "";
24
- return parsed.toString();
25
- } catch {
26
- return url;
27
- }
28
- }
29
- function withIdentityEncoding(init) {
30
- const headers = new Headers(init?.headers);
31
- if (!headers.has("accept-encoding")) {
32
- headers.set("accept-encoding", "identity");
33
- }
34
- return { ...init ?? {}, headers };
35
- }
36
72
  function wrapFetchWithSizeLimit(upstream) {
37
73
  const wrapped = async (input, init) => {
38
74
  const slot = responseSizeLimitStorage.getStore();
39
- if (!slot) return upstream(input, init);
40
- const response = await upstream(input, withIdentityEncoding(init));
41
- return enforceSizeLimit(response, slot.maxResponseBytes, redactUrlForError(urlOfInput(input)));
75
+ const maxBytes = slot ? slot.maxResponseBytes : DEFAULT_MAX_RESPONSE_BYTES;
76
+ if (maxBytes === null) return upstream(input, init);
77
+ const response = await upstream(input, init);
78
+ if (NULL_BODY_STATUSES.has(response.status)) return response;
79
+ const skipDeclaredLength = methodOf(input, init) === "HEAD";
80
+ const url = sanitizeTransportUrl(urlOfInput(input));
81
+ if (isEventStream(response)) {
82
+ const connectionScoped = methodOf(input, init) === "GET";
83
+ const { limit, meter } = streamBudgetFor(connectionScoped, slot, maxBytes);
84
+ return enforceEventStreamLimit(
85
+ response,
86
+ maxBytes,
87
+ limit,
88
+ meter,
89
+ url,
90
+ connectionScoped ? void 0 : slot,
91
+ skipDeclaredLength
92
+ );
93
+ }
94
+ return enforceSizeLimit(response, maxBytes, url, slot, skipDeclaredLength);
42
95
  };
43
96
  return wrapped;
44
97
  }
45
- function enforceSizeLimit(response, maxBytes, url) {
46
- if (response.headers.get("content-type")?.toLowerCase().startsWith("text/event-stream")) {
47
- return response;
48
- }
49
- const declared = parseContentLength(response.headers.get("content-length"));
98
+ function streamBudgetFor(connectionScoped, slot, maxBytes) {
99
+ if (connectionScoped) return { limit: sseBudgetFor(maxBytes), meter: { consumed: 0 } };
100
+ return {
101
+ limit: slot?.streamLimit ?? sseBudgetFor(maxBytes),
102
+ meter: slot?.streamMeter ?? { consumed: 0 }
103
+ };
104
+ }
105
+ function methodOf(input, init) {
106
+ const method = init?.method ?? (typeof Request !== "undefined" && input instanceof Request ? input.method : "GET");
107
+ return method.toUpperCase();
108
+ }
109
+ const NULL_BODY_STATUSES = /* @__PURE__ */ new Set([101, 103, 204, 205, 304]);
110
+ function isEventStream(response) {
111
+ return response.headers.get("content-type")?.toLowerCase().startsWith("text/event-stream") === true;
112
+ }
113
+ function enforceSizeLimit(response, maxBytes, url, slot, skipDeclaredLength = false) {
114
+ const declared = skipDeclaredLength ? void 0 : parseContentLength(response.headers.get("content-length"));
50
115
  if (declared !== void 0 && declared > maxBytes) {
51
- response.body?.cancel().catch(() => {
52
- });
53
- throw new ResponseTooLargeError(maxBytes, 0, url, declared);
116
+ cancelBody(response);
117
+ throw recordViolation(slot, new ResponseTooLargeError(maxBytes, 0, url, declared));
54
118
  }
55
119
  if (!response.body) return response;
56
120
  let bytesRead = 0;
@@ -58,13 +122,107 @@ function enforceSizeLimit(response, maxBytes, url) {
58
122
  transform(chunk, controller) {
59
123
  bytesRead += chunk.byteLength;
60
124
  if (bytesRead > maxBytes) {
61
- controller.error(new ResponseTooLargeError(maxBytes, bytesRead, url));
125
+ controller.error(recordViolation(slot, new ResponseTooLargeError(maxBytes, bytesRead, url)));
62
126
  return;
63
127
  }
64
128
  controller.enqueue(chunk);
65
129
  }
66
130
  });
67
- return new Response(response.body.pipeThrough(counter), {
131
+ return respondWith(response, response.body.pipeThrough(counter));
132
+ }
133
+ const CR = 13;
134
+ const LF = 10;
135
+ function enforceEventStreamLimit(response, maxBytes, streamLimit, meter, url, slot, skipDeclaredLength = false) {
136
+ const remaining = streamLimit - meter.consumed;
137
+ const declared = skipDeclaredLength ? void 0 : parseContentLength(response.headers.get("content-length"));
138
+ if (declared !== void 0 && declared > remaining) {
139
+ cancelBody(response);
140
+ throw recordViolation(slot, new ResponseTooLargeError(streamLimit, meter.consumed, url, declared));
141
+ }
142
+ if (!response.body) return response;
143
+ let bytesInEvent = 0;
144
+ let lineEndings = 0;
145
+ let pendingCR = false;
146
+ let separatorTail = false;
147
+ let pendingReset = false;
148
+ const counter = new TransformStream({
149
+ transform(chunk, controller) {
150
+ meter.consumed += chunk.byteLength;
151
+ if (meter.consumed > streamLimit) {
152
+ controller.error(recordViolation(slot, new ResponseTooLargeError(streamLimit, meter.consumed, url)));
153
+ return;
154
+ }
155
+ if (chunk.length === 0) {
156
+ controller.enqueue(chunk);
157
+ return;
158
+ }
159
+ if (chunk.indexOf(LF) === -1 && chunk.indexOf(CR) === -1) {
160
+ if (pendingReset) {
161
+ pendingReset = false;
162
+ separatorTail = false;
163
+ bytesInEvent = 0;
164
+ }
165
+ bytesInEvent += chunk.length;
166
+ lineEndings = 0;
167
+ pendingCR = false;
168
+ if (bytesInEvent > maxBytes) {
169
+ controller.error(recordViolation(slot, new ResponseTooLargeError(maxBytes, bytesInEvent, url)));
170
+ return;
171
+ }
172
+ controller.enqueue(chunk);
173
+ return;
174
+ }
175
+ for (let i = 0; i < chunk.length; i += 1) {
176
+ const byte = chunk[i];
177
+ if (pendingReset) {
178
+ pendingReset = false;
179
+ if (separatorTail && byte === LF) {
180
+ separatorTail = false;
181
+ pendingCR = false;
182
+ bytesInEvent += 1;
183
+ if (bytesInEvent > maxBytes) {
184
+ controller.error(recordViolation(slot, new ResponseTooLargeError(maxBytes, bytesInEvent, url)));
185
+ return;
186
+ }
187
+ bytesInEvent = 0;
188
+ continue;
189
+ }
190
+ separatorTail = false;
191
+ bytesInEvent = 0;
192
+ }
193
+ bytesInEvent += 1;
194
+ if (byte === CR) {
195
+ lineEndings += 1;
196
+ pendingCR = true;
197
+ } else if (byte === LF) {
198
+ if (!pendingCR) lineEndings += 1;
199
+ pendingCR = false;
200
+ } else {
201
+ lineEndings = 0;
202
+ pendingCR = false;
203
+ }
204
+ if (bytesInEvent > maxBytes) {
205
+ controller.error(recordViolation(slot, new ResponseTooLargeError(maxBytes, bytesInEvent, url)));
206
+ return;
207
+ }
208
+ if (lineEndings >= 2) {
209
+ lineEndings = 0;
210
+ pendingReset = true;
211
+ separatorTail = pendingCR;
212
+ pendingCR = false;
213
+ }
214
+ }
215
+ controller.enqueue(chunk);
216
+ }
217
+ });
218
+ return respondWith(response, response.body.pipeThrough(counter));
219
+ }
220
+ function cancelBody(response) {
221
+ response.body?.cancel().catch(() => {
222
+ });
223
+ }
224
+ function respondWith(response, body) {
225
+ return new Response(body, {
68
226
  status: response.status,
69
227
  statusText: response.statusText,
70
228
  headers: response.headers
@@ -78,6 +236,9 @@ function parseContentLength(value) {
78
236
  return Number.isFinite(parsed) ? parsed : void 0;
79
237
  }
80
238
  export {
239
+ DEFAULT_MAX_RESPONSE_BYTES,
240
+ SSE_CUMULATIVE_BUDGET_MULTIPLIER,
241
+ responseSizeCapCacheKey,
81
242
  responseSizeLimitStorage,
82
243
  withResponseSizeLimit,
83
244
  wrapFetchWithSizeLimit
@@ -4,5 +4,5 @@
4
4
  "source_sha": "4e553ad955f83b49c7d221ab5c3ff78237ad02e3",
5
5
  "source_tarball_sha256": "580656d6466ef9f0d1119985e6726c2efea718dc671e2ad30957fcb2fd54af0f",
6
6
  "upstream_adcp_version": "2.5.3",
7
- "synced_at": "2026-10-15T16:11:40.051Z"
7
+ "synced_at": "2026-10-15T19:30:04.842Z"
8
8
  }
@@ -2,3 +2,18 @@ import { type KeyObject } from 'crypto';
2
2
  import type { AdcpJsonWebKey } from './types.mjs';
3
3
  export declare function jwkToPublicKey(jwk: AdcpJsonWebKey): KeyObject;
4
4
  export declare function verifySignature(alg: string, publicKey: KeyObject, data: Uint8Array, signature: Uint8Array): boolean;
5
+ /**
6
+ * Verify with WebCrypto when available. ES256 uses `crypto.subtle`, whose
7
+ * ECDSA signatures are IEEE P1363 (r||s) natively, so edge runtimes such as
8
+ * Cloudflare workerd verify without node:crypto option-shape differences
9
+ * (#3243). Other algorithms, and runtimes without WebCrypto, use
10
+ * {@link verifySignature}.
11
+ */
12
+ export declare function verifySignatureAsync(alg: string, publicKey: KeyObject | AdcpJsonWebKey, data: Uint8Array, signature: Uint8Array): Promise<boolean>;
13
+ /**
14
+ * Sign with a private JWK. ES256 output is IEEE P1363 (r||s). The node:crypto
15
+ * path passes the key as a JWK so workerd accepts it (#3243).
16
+ */
17
+ export declare function signWithPrivateJwk(alg: string, privateJwk: AdcpJsonWebKey, data: Uint8Array): Uint8Array;
18
+ /** {@link signWithPrivateJwk}, preferring WebCrypto for ES256 when available. */
19
+ export declare function signWithPrivateJwkAsync(alg: string, privateJwk: AdcpJsonWebKey, data: Uint8Array): Promise<Uint8Array>;
@@ -2,3 +2,18 @@ import { type KeyObject } from 'crypto';
2
2
  import type { AdcpJsonWebKey } from './types';
3
3
  export declare function jwkToPublicKey(jwk: AdcpJsonWebKey): KeyObject;
4
4
  export declare function verifySignature(alg: string, publicKey: KeyObject, data: Uint8Array, signature: Uint8Array): boolean;
5
+ /**
6
+ * Verify with WebCrypto when available. ES256 uses `crypto.subtle`, whose
7
+ * ECDSA signatures are IEEE P1363 (r||s) natively, so edge runtimes such as
8
+ * Cloudflare workerd verify without node:crypto option-shape differences
9
+ * (#3243). Other algorithms, and runtimes without WebCrypto, use
10
+ * {@link verifySignature}.
11
+ */
12
+ export declare function verifySignatureAsync(alg: string, publicKey: KeyObject | AdcpJsonWebKey, data: Uint8Array, signature: Uint8Array): Promise<boolean>;
13
+ /**
14
+ * Sign with a private JWK. ES256 output is IEEE P1363 (r||s). The node:crypto
15
+ * path passes the key as a JWK so workerd accepts it (#3243).
16
+ */
17
+ export declare function signWithPrivateJwk(alg: string, privateJwk: AdcpJsonWebKey, data: Uint8Array): Uint8Array;
18
+ /** {@link signWithPrivateJwk}, preferring WebCrypto for ES256 when available. */
19
+ export declare function signWithPrivateJwkAsync(alg: string, privateJwk: AdcpJsonWebKey, data: Uint8Array): Promise<Uint8Array>;