@adcp/sdk 16.0.0-beta.1 → 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 (155) 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 +15 -4
  5. package/dist/lib/core/SingleAgentClient.d.ts +15 -4
  6. package/dist/lib/core/SingleAgentClient.js +29 -8
  7. package/dist/lib/core/SingleAgentClient.mjs +29 -8
  8. package/dist/lib/core/TaskExecutor.js +5 -1
  9. package/dist/lib/core/TaskExecutor.mjs +5 -1
  10. package/dist/lib/errors/index.d.mts +9 -4
  11. package/dist/lib/errors/index.d.ts +9 -4
  12. package/dist/lib/identity-3.3/generated-compat.d.mts +34 -0
  13. package/dist/lib/identity-3.3/generated-compat.d.ts +34 -0
  14. package/dist/lib/identity-3.3/generated-compat.js +16 -0
  15. package/dist/lib/identity-3.3/generated-compat.mjs +0 -0
  16. package/dist/lib/identity-3.3/index.d.mts +2 -2
  17. package/dist/lib/identity-3.3/index.d.ts +2 -2
  18. package/dist/lib/identity-3.3/index.js +4 -0
  19. package/dist/lib/identity-3.3/index.mjs +5 -1
  20. package/dist/lib/identity-3.3/lifecycle.d.mts +17 -5
  21. package/dist/lib/identity-3.3/lifecycle.d.ts +17 -5
  22. package/dist/lib/identity-3.3/lifecycle.js +32 -5
  23. package/dist/lib/identity-3.3/lifecycle.mjs +38 -6
  24. package/dist/lib/identity-3.3/revocation.d.mts +39 -3
  25. package/dist/lib/identity-3.3/revocation.d.ts +39 -3
  26. package/dist/lib/identity-3.3/revocation.js +88 -18
  27. package/dist/lib/identity-3.3/revocation.mjs +89 -19
  28. package/dist/lib/identity-3.3/server.d.mts +12 -0
  29. package/dist/lib/identity-3.3/server.d.ts +12 -0
  30. package/dist/lib/identity-3.3/server.js +103 -43
  31. package/dist/lib/identity-3.3/server.mjs +111 -45
  32. package/dist/lib/identity-3.3/trust.d.mts +62 -1
  33. package/dist/lib/identity-3.3/trust.d.ts +62 -1
  34. package/dist/lib/identity-3.3/trust.js +126 -21
  35. package/dist/lib/identity-3.3/trust.mjs +124 -21
  36. package/dist/lib/identity-3.3/types.d.mts +39 -11
  37. package/dist/lib/identity-3.3/types.d.ts +39 -11
  38. package/dist/lib/index.d.mts +3 -3
  39. package/dist/lib/index.d.ts +3 -3
  40. package/dist/lib/index.js +8 -0
  41. package/dist/lib/index.mjs +9 -1
  42. package/dist/lib/negotiation/types.d.mts +20 -39
  43. package/dist/lib/negotiation/types.d.ts +20 -39
  44. package/dist/lib/protocols/index.d.mts +61 -25
  45. package/dist/lib/protocols/index.d.ts +61 -25
  46. package/dist/lib/protocols/index.js +11 -6
  47. package/dist/lib/protocols/index.mjs +5 -2
  48. package/dist/lib/protocols/mcp-modern.js +1 -0
  49. package/dist/lib/protocols/mcp-modern.mjs +2 -1
  50. package/dist/lib/protocols/mcp.js +1 -0
  51. package/dist/lib/protocols/mcp.mjs +2 -1
  52. package/dist/lib/protocols/responseSizeLimit.d.mts +91 -5
  53. package/dist/lib/protocols/responseSizeLimit.d.ts +91 -5
  54. package/dist/lib/protocols/responseSizeLimit.js +195 -31
  55. package/dist/lib/protocols/responseSizeLimit.mjs +192 -31
  56. package/dist/lib/reporting/bigquery/index.js +8 -0
  57. package/dist/lib/reporting/bigquery/index.mjs +8 -0
  58. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  59. package/dist/lib/server/auth-signature.d.mts +6 -1
  60. package/dist/lib/server/auth-signature.d.ts +6 -1
  61. package/dist/lib/server/auth-signature.js +24 -13
  62. package/dist/lib/server/auth-signature.mjs +29 -13
  63. package/dist/lib/server/auth.d.mts +2 -2
  64. package/dist/lib/server/auth.d.ts +2 -2
  65. package/dist/lib/server/create-adcp-server.js +31 -10
  66. package/dist/lib/server/create-adcp-server.mjs +37 -11
  67. package/dist/lib/signing/crypto.d.mts +15 -0
  68. package/dist/lib/signing/crypto.d.ts +15 -0
  69. package/dist/lib/signing/crypto.js +83 -3
  70. package/dist/lib/signing/crypto.mjs +86 -3
  71. package/dist/lib/signing/fetch-async.js +1 -1
  72. package/dist/lib/signing/fetch-async.mjs +1 -1
  73. package/dist/lib/signing/fetch.js +1 -1
  74. package/dist/lib/signing/fetch.mjs +1 -1
  75. package/dist/lib/signing/middleware.js +9 -0
  76. package/dist/lib/signing/middleware.mjs +14 -0
  77. package/dist/lib/signing/parser.d.mts +8 -5
  78. package/dist/lib/signing/parser.d.ts +8 -5
  79. package/dist/lib/signing/parser.js +77 -61
  80. package/dist/lib/signing/parser.mjs +77 -61
  81. package/dist/lib/signing/revocation.js +3 -0
  82. package/dist/lib/signing/revocation.mjs +3 -0
  83. package/dist/lib/signing/server.d.mts +1 -1
  84. package/dist/lib/signing/server.d.ts +1 -1
  85. package/dist/lib/signing/server.js +2 -0
  86. package/dist/lib/signing/server.mjs +2 -1
  87. package/dist/lib/signing/signer.js +2 -8
  88. package/dist/lib/signing/signer.mjs +3 -9
  89. package/dist/lib/signing/testing.js +2 -6
  90. package/dist/lib/signing/testing.mjs +3 -7
  91. package/dist/lib/signing/verifier.js +3 -3
  92. package/dist/lib/signing/verifier.mjs +4 -4
  93. package/dist/lib/signing/web-bot-auth.d.mts +84 -10
  94. package/dist/lib/signing/web-bot-auth.d.ts +84 -10
  95. package/dist/lib/signing/web-bot-auth.js +278 -88
  96. package/dist/lib/signing/web-bot-auth.mjs +279 -94
  97. package/dist/lib/signing/webhook-verifier.js +3 -3
  98. package/dist/lib/signing/webhook-verifier.mjs +4 -4
  99. package/dist/lib/substitution/compile.js +2 -1
  100. package/dist/lib/substitution/compile.mjs +2 -1
  101. package/dist/lib/substitution/consent-macros.d.mts +4 -0
  102. package/dist/lib/substitution/consent-macros.d.ts +4 -0
  103. package/dist/lib/substitution/consent-macros.js +45 -0
  104. package/dist/lib/substitution/consent-macros.mjs +20 -0
  105. package/dist/lib/substitution/index.d.mts +1 -1
  106. package/dist/lib/substitution/index.d.ts +1 -1
  107. package/dist/lib/substitution/index.js +2 -0
  108. package/dist/lib/substitution/index.mjs +2 -1
  109. package/dist/lib/substitution/translate.d.mts +15 -2
  110. package/dist/lib/substitution/translate.d.ts +15 -2
  111. package/dist/lib/substitution/translate.js +148 -26
  112. package/dist/lib/substitution/translate.mjs +147 -26
  113. package/dist/lib/testing/client.js +2 -1
  114. package/dist/lib/testing/client.mjs +2 -1
  115. package/dist/lib/testing/scenarios/capabilities.js +1 -1
  116. package/dist/lib/testing/scenarios/capabilities.mjs +1 -1
  117. package/dist/lib/testing/storyboard/request-signing/builder.js +2 -5
  118. package/dist/lib/testing/storyboard/request-signing/builder.mjs +3 -6
  119. package/dist/lib/testing/storyboard/signer-grader/grader.js +2 -7
  120. package/dist/lib/testing/storyboard/signer-grader/grader.mjs +2 -7
  121. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  122. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  123. package/dist/lib/types/enums.generated.d.mts +3 -0
  124. package/dist/lib/types/enums.generated.d.ts +3 -0
  125. package/dist/lib/types/enums.generated.js +9 -0
  126. package/dist/lib/types/enums.generated.mjs +6 -0
  127. package/dist/lib/v2/projection/catalog-snapshot.js +3 -1
  128. package/dist/lib/v2/projection/catalog-snapshot.mjs +3 -1
  129. package/dist/lib/v2/projection/creative-delivery.d.mts +31 -9
  130. package/dist/lib/v2/projection/creative-delivery.d.ts +31 -9
  131. package/dist/lib/v2/projection/index.d.mts +1 -1
  132. package/dist/lib/v2/projection/index.d.ts +1 -1
  133. package/dist/lib/v2/projection/index.js +2 -0
  134. package/dist/lib/v2/projection/index.mjs +2 -0
  135. package/dist/lib/v2/projection/legacy-routes.d.mts +10 -0
  136. package/dist/lib/v2/projection/legacy-routes.d.ts +10 -0
  137. package/dist/lib/v2/projection/legacy-routes.js +7 -0
  138. package/dist/lib/v2/projection/legacy-routes.mjs +6 -0
  139. package/dist/lib/validation/schema-loader.js +25 -0
  140. package/dist/lib/validation/schema-loader.mjs +24 -0
  141. package/dist/lib/version.d.mts +3 -3
  142. package/dist/lib/version.d.ts +3 -3
  143. package/dist/lib/version.js +3 -3
  144. package/dist/lib/version.mjs +3 -3
  145. package/docs/TYPE-SUMMARY.md +7 -2
  146. package/docs/guides/DISCOVERY-CACHE-AND-SESSION-REUSE.md +3 -2
  147. package/docs/guides/REPORTING-LEDGER.md +15 -5
  148. package/docs/guides/REPORTING-OPERATIONS.md +17 -11
  149. package/docs/guides/UNIVERSAL-MACRO-COMPILATION.md +38 -0
  150. package/docs/guides/VALIDATE-YOUR-AGENT.md +12 -0
  151. package/docs/llms.txt +2 -2
  152. package/docs/migration-12-to-13.md +8 -0
  153. package/docs/migration-14.x-rc-worksheet.md +4 -4
  154. package/docs/migration-agent-identity-3.3.md +150 -11
  155. package/package.json +4 -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
@@ -67,6 +67,7 @@ ALTER TABLE adcp_reporting_warehouse_sinks
67
67
  const MAX_LOAD_TIMEOUT = 6 * 36e5;
68
68
  const MAX_TIMEOUT_DOUBLINGS = 16;
69
69
  let warnedLabelsIgnored = false;
70
+ let warnedLocationUnset = false;
70
71
  const PROJECT = /^[a-z][a-z0-9-]{4,61}[a-z0-9]$/;
71
72
  const DATASET_OR_TABLE = /^[A-Za-z_][A-Za-z0-9_]{0,1023}$/;
72
73
  const NAME = /^[A-Za-z0-9_.:-]{1,128}$/;
@@ -97,6 +98,13 @@ function createBigQueryReportingWarehouseSinkV1(options) {
97
98
  { code: "ADCP_BIGQUERY_SINK_LABELS_IGNORED", type: "DeprecationWarning" }
98
99
  );
99
100
  }
101
+ if (!options.location && !warnedLocationUnset) {
102
+ warnedLocationUnset = true;
103
+ process.emitWarning(
104
+ 'BigQuery warehouse sink option `location` is not set. Replay lookups of load jobs need it for datasets outside the US and EU multi-regions: without it BigQuery answers 404, the sink uploads again, gets a 409, and then fails with a "check projectId and location" error.',
105
+ { code: "ADCP_BIGQUERY_SINK_LOCATION_UNSET" }
106
+ );
107
+ }
100
108
  const tables = {
101
109
  projectId: options.projectId,
102
110
  datasetId: options.datasetId,
@@ -29,6 +29,7 @@ ALTER TABLE adcp_reporting_warehouse_sinks
29
29
  const MAX_LOAD_TIMEOUT = 6 * 36e5;
30
30
  const MAX_TIMEOUT_DOUBLINGS = 16;
31
31
  let warnedLabelsIgnored = false;
32
+ let warnedLocationUnset = false;
32
33
  const PROJECT = /^[a-z][a-z0-9-]{4,61}[a-z0-9]$/;
33
34
  const DATASET_OR_TABLE = /^[A-Za-z_][A-Za-z0-9_]{0,1023}$/;
34
35
  const NAME = /^[A-Za-z0-9_.:-]{1,128}$/;
@@ -59,6 +60,13 @@ function createBigQueryReportingWarehouseSinkV1(options) {
59
60
  { code: "ADCP_BIGQUERY_SINK_LABELS_IGNORED", type: "DeprecationWarning" }
60
61
  );
61
62
  }
63
+ if (!options.location && !warnedLocationUnset) {
64
+ warnedLocationUnset = true;
65
+ process.emitWarning(
66
+ 'BigQuery warehouse sink option `location` is not set. Replay lookups of load jobs need it for datasets outside the US and EU multi-regions: without it BigQuery answers 404, the sink uploads again, gets a 409, and then fails with a "check projectId and location" error.',
67
+ { code: "ADCP_BIGQUERY_SINK_LOCATION_UNSET" }
68
+ );
69
+ }
62
70
  const tables = {
63
71
  projectId: options.projectId,
64
72
  datasetId: options.datasetId,
@@ -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-14T04:36:25.896Z"
7
+ "synced_at": "2026-10-15T19:30:04.842Z"
8
8
  }
@@ -49,7 +49,12 @@ export interface VerifySignatureAsAuthenticatorOptions {
49
49
  /** Explicit experimental profile. Verification never creates a principal or fetches trust.json. */
50
50
  webBotAuth?: {
51
51
  directory: import('../signing/web-bot-auth.mjs').SignatureDirectoryResolver;
52
- isRevoked: (origin: string, keyid: string) => Promise<boolean>;
52
+ /**
53
+ * @deprecated Revocation belongs to the identity layer: use
54
+ * `createIdentity33PrincipalResolver({ revocation })`. Required only when
55
+ * `resolvePrincipal` does not check revocation itself.
56
+ */
57
+ isRevoked?: (origin: string, keyid: string) => Promise<boolean>;
53
58
  /** Resolve only an existing audited binding; handle identity-established callers with anonymous scope. */
54
59
  resolvePrincipal: (signers: readonly VerifiedWebBotSigner[], operation: ResolvedOperation) => Promise<AuthPrincipal>;
55
60
  };
@@ -49,7 +49,12 @@ export interface VerifySignatureAsAuthenticatorOptions {
49
49
  /** Explicit experimental profile. Verification never creates a principal or fetches trust.json. */
50
50
  webBotAuth?: {
51
51
  directory: import('../signing/web-bot-auth').SignatureDirectoryResolver;
52
- isRevoked: (origin: string, keyid: string) => Promise<boolean>;
52
+ /**
53
+ * @deprecated Revocation belongs to the identity layer: use
54
+ * `createIdentity33PrincipalResolver({ revocation })`. Required only when
55
+ * `resolvePrincipal` does not check revocation itself.
56
+ */
57
+ isRevoked?: (origin: string, keyid: string) => Promise<boolean>;
53
58
  /** Resolve only an existing audited binding; handle identity-established callers with anonymous scope. */
54
59
  resolvePrincipal: (signers: readonly VerifiedWebBotSigner[], operation: ResolvedOperation) => Promise<AuthPrincipal>;
55
60
  };