@astrasyncai/verification-gateway 5.13.0 → 5.15.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 (98) hide show
  1. package/README.md +27 -17
  2. package/dist/adapters/express.d.mts +1 -1
  3. package/dist/adapters/express.d.ts +1 -1
  4. package/dist/adapters/express.js +504 -249
  5. package/dist/adapters/express.js.map +1 -1
  6. package/dist/adapters/express.mjs +504 -249
  7. package/dist/adapters/express.mjs.map +1 -1
  8. package/dist/adapters/mcp.d.mts +1 -1
  9. package/dist/adapters/mcp.d.ts +1 -1
  10. package/dist/adapters/mcp.js +20 -6
  11. package/dist/adapters/mcp.js.map +1 -1
  12. package/dist/adapters/mcp.mjs +20 -6
  13. package/dist/adapters/mcp.mjs.map +1 -1
  14. package/dist/adapters/nextjs.d.mts +1 -1
  15. package/dist/adapters/nextjs.d.ts +1 -1
  16. package/dist/adapters/nextjs.js +487 -248
  17. package/dist/adapters/nextjs.js.map +1 -1
  18. package/dist/adapters/nextjs.mjs +487 -248
  19. package/dist/adapters/nextjs.mjs.map +1 -1
  20. package/dist/adapters/sdk.d.mts +18 -10
  21. package/dist/adapters/sdk.d.ts +18 -10
  22. package/dist/adapters/sdk.js +80 -23
  23. package/dist/adapters/sdk.js.map +1 -1
  24. package/dist/adapters/sdk.mjs +80 -23
  25. package/dist/adapters/sdk.mjs.map +1 -1
  26. package/dist/agent/index.js +1 -1
  27. package/dist/agent/index.js.map +1 -1
  28. package/dist/agent/index.mjs +1 -1
  29. package/dist/agent/index.mjs.map +1 -1
  30. package/dist/bin/astrasync-claude-hook.js +82 -20
  31. package/dist/bin/astrasync-codex-hook.js +82 -20
  32. package/dist/bin/astrasync-guard.js +82 -20
  33. package/dist/bin/astrasync.js +316 -43
  34. package/dist/browser/background.js +77 -15
  35. package/dist/browser/background.js.map +1 -1
  36. package/dist/browser/background.mjs +87 -15
  37. package/dist/browser/background.mjs.map +1 -1
  38. package/dist/cli/index.js +1 -1
  39. package/dist/cli/index.js.map +1 -1
  40. package/dist/cli/index.mjs +1 -1
  41. package/dist/cli/index.mjs.map +1 -1
  42. package/dist/codex/index.js +82 -20
  43. package/dist/codex/index.js.map +1 -1
  44. package/dist/codex/index.mjs +92 -20
  45. package/dist/codex/index.mjs.map +1 -1
  46. package/dist/cursor/extension.js +82 -20
  47. package/dist/cursor/extension.js.map +1 -1
  48. package/dist/cursor/extension.mjs +92 -20
  49. package/dist/cursor/extension.mjs.map +1 -1
  50. package/dist/edge-config.d.mts +31 -2
  51. package/dist/edge-config.d.ts +31 -2
  52. package/dist/edge-config.js +35 -5
  53. package/dist/edge-config.js.map +1 -1
  54. package/dist/edge-config.mjs +32 -4
  55. package/dist/edge-config.mjs.map +1 -1
  56. package/dist/edge-core/index.d.mts +43 -6
  57. package/dist/edge-core/index.d.ts +43 -6
  58. package/dist/edge-core/index.js +120 -19
  59. package/dist/edge-core/index.js.map +1 -1
  60. package/dist/edge-core/index.mjs +118 -19
  61. package/dist/edge-core/index.mjs.map +1 -1
  62. package/dist/gateway/gateway.js +77 -15
  63. package/dist/gateway/gateway.js.map +1 -1
  64. package/dist/gateway/gateway.mjs +87 -15
  65. package/dist/gateway/gateway.mjs.map +1 -1
  66. package/dist/git-trigger/git-hooks.d.mts +1 -1
  67. package/dist/git-trigger/git-hooks.d.ts +1 -1
  68. package/dist/index.d.mts +1314 -34
  69. package/dist/index.d.ts +1314 -34
  70. package/dist/index.js +5143 -2621
  71. package/dist/index.js.map +1 -1
  72. package/dist/index.mjs +5090 -2623
  73. package/dist/index.mjs.map +1 -1
  74. package/dist/registration/index.d.mts +28 -2
  75. package/dist/registration/index.d.ts +28 -2
  76. package/dist/registration/index.js +18 -3
  77. package/dist/registration/index.js.map +1 -1
  78. package/dist/registration/index.mjs +15 -2
  79. package/dist/registration/index.mjs.map +1 -1
  80. package/dist/transport/index.d.mts +7 -5
  81. package/dist/transport/index.d.ts +7 -5
  82. package/dist/transport/index.js +61 -5
  83. package/dist/transport/index.js.map +1 -1
  84. package/dist/transport/index.mjs +67 -11
  85. package/dist/transport/index.mjs.map +1 -1
  86. package/dist/{types-C_b4QTyO.d.ts → types-DtJjlW4O.d.ts} +104 -10
  87. package/dist/{types-BCkwBA84.d.mts → types-Dxf9DBpr.d.mts} +106 -12
  88. package/dist/{types-BCkwBA84.d.ts → types-Dxf9DBpr.d.ts} +106 -12
  89. package/dist/{types-DlshIIR-.d.mts → types-Eg6VoBNH.d.mts} +104 -10
  90. package/dist/ui/index.d.mts +1 -1
  91. package/dist/ui/index.d.ts +1 -1
  92. package/dist/verify.d.mts +1 -1
  93. package/dist/verify.d.ts +1 -1
  94. package/dist/verify.js +20 -6
  95. package/dist/verify.js.map +1 -1
  96. package/dist/verify.mjs +20 -6
  97. package/dist/verify.mjs.map +1 -1
  98. package/package.json +1 -1
@@ -1,5 +1,419 @@
1
+ // src/metadata-capture.ts
2
+ var CAPTURE_SCHEMA_VERSION = 1;
3
+ var SECRET_HEADER_NAMES = /* @__PURE__ */ new Set([
4
+ "cookie",
5
+ "set-cookie",
6
+ "x-csrf-token",
7
+ "proxy-authorization",
8
+ "x-astrasync-signature",
9
+ "x-astrasync-secret",
10
+ "x-hub-signature",
11
+ "x-hub-signature-256",
12
+ // Short-lived verify dedupe token — must never persist in stored metadata.
13
+ "x-astra-verified-hop"
14
+ ]);
15
+ var SECRET_HEADER_PREFIXES = ["x-astrasync-", "kya-", "kya_"];
16
+ var CREDENTIAL_HEADER_NAMES = /* @__PURE__ */ new Set([
17
+ "authorization",
18
+ "x-api-key",
19
+ "x-goog-api-key",
20
+ "api-key"
21
+ ]);
22
+ function isSignatureHeader(name) {
23
+ return name === "signature" || name.endsWith("-signature") || name.endsWith("-signature-256");
24
+ }
25
+ var PLATFORM_HEADER_NAMES = /* @__PURE__ */ new Set([
26
+ "openai-organization",
27
+ "openai-project",
28
+ "openai-version",
29
+ "openai-processing-ms",
30
+ "anthropic-version",
31
+ "anthropic-beta",
32
+ "x-goog-user-project",
33
+ "x-goog-api-client",
34
+ "user-agent",
35
+ "x-agent-card-url",
36
+ "x-agent-provider"
37
+ ]);
38
+ var PLATFORM_HEADER_PREFIXES = [
39
+ "x-goog-",
40
+ "x-stainless-",
41
+ "x-anthropic-",
42
+ "x-openai-",
43
+ "sec-ch-"
44
+ ];
45
+ var KEY_FORMAT_PREFIXES = [
46
+ "sk-ant-api03",
47
+ "sk-ant-api",
48
+ "sk-ant",
49
+ "sk-proj",
50
+ "sk-svcacct",
51
+ "sk-or-v1",
52
+ "sk-or",
53
+ "sk-lf",
54
+ "sk-",
55
+ "gsk_",
56
+ "aiza",
57
+ "ya29",
58
+ "ghp_",
59
+ "gho_",
60
+ "github_pat_",
61
+ "xai-",
62
+ "pplx-",
63
+ "r8_",
64
+ "hf_",
65
+ "astra-",
66
+ "astrae-"
67
+ ];
68
+ var MAX_HEADERS = 64;
69
+ var MAX_HEADER_VALUE_BYTES = 1024;
70
+ var MAX_TOTAL_BYTES = 16 * 1024;
71
+ function byteLength(s) {
72
+ let bytes = 0;
73
+ for (let i = 0; i < s.length; i++) {
74
+ const code = s.charCodeAt(i);
75
+ if (code < 128) bytes += 1;
76
+ else if (code < 2048) bytes += 2;
77
+ else if (code >= 55296 && code <= 56319) {
78
+ bytes += 4;
79
+ i++;
80
+ } else bytes += 3;
81
+ }
82
+ return bytes;
83
+ }
84
+ function extractApiKeyFormat(rawValue) {
85
+ if (!rawValue) return void 0;
86
+ let value = rawValue.trim();
87
+ const spaceIdx = value.indexOf(" ");
88
+ if (spaceIdx > 0) {
89
+ const scheme = value.slice(0, spaceIdx).toLowerCase();
90
+ if (scheme === "bearer" || scheme === "basic" || scheme === "token") {
91
+ value = value.slice(spaceIdx + 1).trim();
92
+ }
93
+ }
94
+ const lower = value.toLowerCase();
95
+ for (const prefix of KEY_FORMAT_PREFIXES) {
96
+ if (lower.startsWith(prefix)) {
97
+ return prefix;
98
+ }
99
+ }
100
+ return void 0;
101
+ }
102
+ function isSecretHeader(name) {
103
+ if (SECRET_HEADER_NAMES.has(name)) return true;
104
+ if (SECRET_HEADER_PREFIXES.some((p) => name.startsWith(p))) return true;
105
+ if (isSignatureHeader(name)) return true;
106
+ return false;
107
+ }
108
+ function isPlatformHeader(name) {
109
+ if (PLATFORM_HEADER_NAMES.has(name)) return true;
110
+ return PLATFORM_HEADER_PREFIXES.some((p) => name.startsWith(p));
111
+ }
112
+ function sanitizeHeaders(raw) {
113
+ const headers = {};
114
+ const platformHeaders = {};
115
+ let apiKeyFormat;
116
+ let cookieCount;
117
+ let totalBytes = 0;
118
+ let count = 0;
119
+ if (!raw) return { headers };
120
+ for (const rawName of Object.keys(raw)) {
121
+ if (count >= MAX_HEADERS) break;
122
+ const name = rawName.toLowerCase();
123
+ const rawValue = raw[rawName];
124
+ if (rawValue === void 0) continue;
125
+ const joined = Array.isArray(rawValue) ? rawValue.join(", ") : String(rawValue);
126
+ if (CREDENTIAL_HEADER_NAMES.has(name)) {
127
+ const fmt = extractApiKeyFormat(joined);
128
+ if (fmt && !apiKeyFormat) apiKeyFormat = fmt;
129
+ continue;
130
+ }
131
+ if (name === "cookie") {
132
+ cookieCount = joined.split(";").filter((s) => s.trim().length > 0).length;
133
+ }
134
+ if (isSecretHeader(name)) continue;
135
+ let value = joined;
136
+ if (byteLength(value) > MAX_HEADER_VALUE_BYTES) {
137
+ value = value.slice(0, MAX_HEADER_VALUE_BYTES);
138
+ }
139
+ const entryBytes = byteLength(name) + byteLength(value);
140
+ if (totalBytes + entryBytes > MAX_TOTAL_BYTES) continue;
141
+ totalBytes += entryBytes;
142
+ count++;
143
+ headers[name] = value;
144
+ if (isPlatformHeader(name)) platformHeaders[name] = value;
145
+ }
146
+ const result = { headers };
147
+ if (apiKeyFormat) result.apiKeyFormat = apiKeyFormat;
148
+ if (Object.keys(platformHeaders).length > 0) result.platformHeaders = platformHeaders;
149
+ if (cookieCount !== void 0) result.cookieCount = cookieCount;
150
+ return result;
151
+ }
152
+ var CONNECTION_HEADER_SOURCES = [
153
+ ["ip", ["cf-connecting-ip", "fastly-client-ip", "true-client-ip", "x-real-ip"]],
154
+ [
155
+ "country",
156
+ ["cf-ipcountry", "x-vercel-ip-country", "cloudfront-viewer-country", "fastly-geo-country"]
157
+ ],
158
+ ["region", ["x-vercel-ip-country-region", "cloudfront-viewer-country-region"]],
159
+ ["countryRegionName", ["cloudfront-viewer-country-region-name"]],
160
+ ["city", ["x-vercel-ip-city", "cloudfront-viewer-city"]],
161
+ ["latitude", ["x-vercel-ip-latitude", "cloudfront-viewer-latitude"]],
162
+ ["longitude", ["x-vercel-ip-longitude", "cloudfront-viewer-longitude"]],
163
+ ["postalCode", ["x-vercel-ip-postal-code", "cloudfront-viewer-postal-code"]],
164
+ ["asn", ["cloudfront-viewer-asn"]],
165
+ ["tlsVersion", ["cloudfront-viewer-tls"]],
166
+ ["httpVersion", ["cloudfront-viewer-http-version"]],
167
+ ["timeZone", ["x-vercel-ip-timezone", "cloudfront-viewer-time-zone"]],
168
+ ["metroCode", ["cloudfront-viewer-metro-code"]],
169
+ // TLS fingerprints can only be computed where TLS terminates. When a CDN
170
+ // terminates TLS in front of the merchant, its fingerprint headers ARE the
171
+ // connection-layer truth — normalising them here is the permanent design.
172
+ // CloudFront delivers JA3/JA4 only via an origin request policy and only
173
+ // for HTTPS viewer connections.
174
+ ["tlsFingerprint", ["cf-ja3-hash", "cloudfront-viewer-ja3-fingerprint"]],
175
+ ["ja4", ["cf-ja4", "cloudfront-viewer-ja4-fingerprint"]],
176
+ // Header-structure fingerprint: browser/SDK-distinctive header ordering.
177
+ ["headerOrder", ["cloudfront-viewer-header-order"]],
178
+ ["headerCount", ["cloudfront-viewer-header-count"]]
179
+ ];
180
+ var MAX_CONNECTION_VALUE_CHARS = 256;
181
+ function deriveConnectionFromHeaders(headers) {
182
+ if (!headers) return void 0;
183
+ const lower = {};
184
+ for (const name of Object.keys(headers)) {
185
+ const value = headers[name];
186
+ if (typeof value === "string" && value.length > 0) lower[name.toLowerCase()] = value;
187
+ }
188
+ const connection = {};
189
+ for (const [key, sources] of CONNECTION_HEADER_SOURCES) {
190
+ for (const source of sources) {
191
+ const value = lower[source]?.trim();
192
+ if (value) {
193
+ connection[key] = value.slice(0, MAX_CONNECTION_VALUE_CHARS);
194
+ break;
195
+ }
196
+ }
197
+ }
198
+ {
199
+ const addr = lower["cloudfront-viewer-address"]?.trim();
200
+ const portMatch = addr?.match(/:(\d+)$/);
201
+ if (portMatch) connection.sourcePort = portMatch[1];
202
+ if (!connection.ip && addr) connection.ip = addr.replace(/:\d+$/, "");
203
+ }
204
+ if (!connection.ip) {
205
+ const xff = lower["x-forwarded-for"];
206
+ const client = xff?.split(",")[0]?.trim();
207
+ if (client) connection.ip = client;
208
+ }
209
+ if (!connection.tlsCipher) {
210
+ const cipher = connection.tlsVersion?.split(":")[1];
211
+ if (cipher) connection.tlsCipher = cipher;
212
+ }
213
+ return Object.keys(connection).length > 0 ? connection : void 0;
214
+ }
215
+ function buildSdkObservedMetadata(rawHeaders) {
216
+ const { headers, apiKeyFormat, platformHeaders, cookieCount } = sanitizeHeaders(rawHeaders);
217
+ const flat = {};
218
+ if (rawHeaders) {
219
+ for (const name of Object.keys(rawHeaders)) {
220
+ const value = rawHeaders[name];
221
+ if (value === void 0) continue;
222
+ flat[name.toLowerCase()] = Array.isArray(value) ? value.join(", ") : String(value);
223
+ }
224
+ }
225
+ const connection = deriveConnectionFromHeaders(flat);
226
+ return {
227
+ headers,
228
+ ...apiKeyFormat && { apiKeyFormat },
229
+ ...platformHeaders && { platformHeaders },
230
+ ...connection && { connection },
231
+ ...cookieCount !== void 0 && { cookieCount },
232
+ schemaVersion: CAPTURE_SCHEMA_VERSION
233
+ };
234
+ }
235
+
236
+ // src/edge-core/request.ts
237
+ function headerValue(req, name) {
238
+ return req.headers[name.toLowerCase()];
239
+ }
240
+ function requestUrl(req, host) {
241
+ const qs = req.querystring ? `?${req.querystring}` : "";
242
+ return `https://${host}${req.uri}${qs}`;
243
+ }
244
+
245
+ // src/edge-core/detect.ts
246
+ var SD_JWT_PATTERN = /^eyJ[\w-]+\.[\w-]*\.[\w-]+~/;
247
+ function decodeBase64Url(seg) {
248
+ const b64 = seg.replace(/-/g, "+").replace(/_/g, "/");
249
+ const padded = b64 + "=".repeat((4 - b64.length % 4) % 4);
250
+ const binary = atob(padded);
251
+ const bytes = new Uint8Array(binary.length);
252
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
253
+ return new TextDecoder().decode(bytes);
254
+ }
255
+ function sniffJwtHeader(compact) {
256
+ try {
257
+ const seg = compact.split(".")[0];
258
+ return JSON.parse(decodeBase64Url(seg));
259
+ } catch {
260
+ return void 0;
261
+ }
262
+ }
263
+ function parseJsonBody(bodyText) {
264
+ if (!bodyText) return void 0;
265
+ try {
266
+ const parsed = JSON.parse(bodyText);
267
+ return typeof parsed === "object" && parsed !== null ? parsed : void 0;
268
+ } catch {
269
+ return void 0;
270
+ }
271
+ }
272
+ function detectCommerce(req, host) {
273
+ const url = requestUrl(req, host);
274
+ const method = req.method;
275
+ const headers = req.headers;
276
+ const jsonBody = parseJsonBody(req.bodyText);
277
+ const truncated = req.bodyTruncated ? { bodyTruncated: true } : {};
278
+ const authHeader = headerValue(req, "authorization");
279
+ const bearer = authHeader?.replace(/^Bearer\s+/i, "");
280
+ const mandateHeader = headerValue(req, "x-payment-mandate") ?? headerValue(req, "payment-mandate");
281
+ const sdJwtCandidate = [mandateHeader, bearer].find((v) => v && SD_JWT_PATTERN.test(v));
282
+ if (sdJwtCandidate) {
283
+ const typ = sniffJwtHeader(sdJwtCandidate)?.typ;
284
+ return {
285
+ protocol: "vi-sd-jwt",
286
+ artifacts: { viSdJwt: sdJwtCandidate },
287
+ evidence: { typ, ...truncated }
288
+ };
289
+ }
290
+ const signature = headerValue(req, "signature");
291
+ const signatureInput = headerValue(req, "signature-input");
292
+ if (signature && signatureInput) {
293
+ const signatureAgent = headerValue(req, "signature-agent");
294
+ const registryHint = headerValue(req, "x-agent-registry");
295
+ const lowerInput = signatureInput.toLowerCase();
296
+ const protocol = signatureAgent ? "rfc9421-web-bot-auth" : registryHint?.toLowerCase().includes("visa") || lowerInput.includes("visa") ? "rfc9421-tap" : registryHint?.toLowerCase().includes("mastercard") || lowerInput.includes("mastercard") ? "rfc9421-agent-pay" : "rfc9421";
297
+ return {
298
+ protocol,
299
+ artifacts: {
300
+ rfc9421: { headers, method, url, ...req.bodyText && { body: req.bodyText } }
301
+ },
302
+ evidence: {
303
+ ...signatureAgent && { signatureAgent },
304
+ ...registryHint && { registryHint },
305
+ keyid: signatureInput.match(/keyid="([^"]+)"/)?.[1],
306
+ ...truncated
307
+ }
308
+ };
309
+ }
310
+ const acpTimestamp = headerValue(req, "timestamp");
311
+ if (signature && !signatureInput && (acpTimestamp || headerValue(req, "idempotency-key"))) {
312
+ return {
313
+ protocol: "acp",
314
+ artifacts: {
315
+ acpRequest: {
316
+ method,
317
+ url,
318
+ headers,
319
+ ...req.bodyText && { rawBody: req.bodyText },
320
+ ...jsonBody && { body: jsonBody }
321
+ }
322
+ },
323
+ evidence: { hasTimestamp: Boolean(acpTimestamp), ...truncated }
324
+ };
325
+ }
326
+ if (/\/checkout[-_]sessions(\/|$)/.test(req.uri)) {
327
+ return {
328
+ protocol: "ucp",
329
+ artifacts: { ucpRequest: { method, url, ...jsonBody && { body: jsonBody } } },
330
+ evidence: { path: req.uri, ...truncated }
331
+ };
332
+ }
333
+ if (jsonBody) {
334
+ const intent = jsonBody.intent_mandate ?? jsonBody.intentMandate;
335
+ const cart = jsonBody.cart_mandate ?? jsonBody.cartMandate;
336
+ const payment = jsonBody.payment_mandate ?? jsonBody.paymentMandate;
337
+ if (typeof intent === "string" || typeof cart === "string" || typeof payment === "string") {
338
+ return {
339
+ protocol: "ap2",
340
+ artifacts: {
341
+ ap2Mandates: {
342
+ ...typeof intent === "string" && { intent },
343
+ ...typeof cart === "string" && { cart },
344
+ ...typeof payment === "string" && { payment }
345
+ }
346
+ },
347
+ evidence: {
348
+ mandatesPresent: [
349
+ typeof intent === "string" && "intent",
350
+ typeof cart === "string" && "cart",
351
+ typeof payment === "string" && "payment"
352
+ ].filter(Boolean),
353
+ ...truncated
354
+ }
355
+ };
356
+ }
357
+ }
358
+ if (authHeader && /^Payment\s+/i.test(authHeader) || headerValue(req, "payment-authorization")) {
359
+ return {
360
+ protocol: "mpp",
361
+ artifacts: {
362
+ mppRequest: {
363
+ method,
364
+ url,
365
+ headers,
366
+ ...req.bodyText && { rawBody: req.bodyText },
367
+ ...jsonBody && { body: jsonBody }
368
+ }
369
+ },
370
+ evidence: truncated
371
+ };
372
+ }
373
+ if (headerValue(req, "payment-signature") || headerValue(req, "x-payment")) {
374
+ return {
375
+ protocol: "x402",
376
+ artifacts: {
377
+ x402Request: { method, url, headers, ...jsonBody && { body: jsonBody } }
378
+ },
379
+ evidence: truncated
380
+ };
381
+ }
382
+ return void 0;
383
+ }
384
+
385
+ // src/adapters/commerce-detect.ts
386
+ var MAX_BODY_BYTES = 64 * 1024;
387
+ function detect(req, host) {
388
+ try {
389
+ return detectCommerce(req, host)?.artifacts;
390
+ } catch {
391
+ return void 0;
392
+ }
393
+ }
394
+ async function detectFetchCommerce(request) {
395
+ const url = new URL(request.url);
396
+ const headers = {};
397
+ request.headers.forEach((v, k) => headers[k.toLowerCase()] = v);
398
+ let bodyText;
399
+ const length = Number(headers["content-length"] ?? 0);
400
+ if (request.method !== "GET" && request.method !== "HEAD" && length <= MAX_BODY_BYTES) {
401
+ bodyText = await request.clone().text().catch(() => void 0);
402
+ }
403
+ return detect(
404
+ {
405
+ method: request.method,
406
+ uri: url.pathname,
407
+ querystring: url.search.replace(/^\?/, ""),
408
+ headers,
409
+ ...bodyText ? { bodyText } : {}
410
+ },
411
+ headers["x-forwarded-host"] ?? url.host
412
+ );
413
+ }
414
+
1
415
  // src/version.ts
2
- var SDK_VERSION = "5.13.0";
416
+ var SDK_VERSION = "5.15.0";
3
417
 
4
418
  // src/http.ts
5
419
  var SDK_USER_AGENT = `astrasync-sdk/${SDK_VERSION}`;
@@ -463,6 +877,7 @@ async function callVerifyAccessAPI(config, request) {
463
877
  if (requestData.invocationProtocol) body.invocationProtocol = requestData.invocationProtocol;
464
878
  if (requestData.commercePhase) body.commercePhase = requestData.commercePhase;
465
879
  if (requestData.checkoutSessionId) body.checkoutSessionId = requestData.checkoutSessionId;
880
+ if (requestData.checkoutJwt) body.checkoutJwt = requestData.checkoutJwt;
466
881
  if (requestData.checkoutItems) body.checkoutItems = requestData.checkoutItems;
467
882
  if (requestData.buyerEmail) body.buyerEmail = requestData.buyerEmail;
468
883
  if (requestData.commerceArtifacts) body.commerceArtifacts = requestData.commerceArtifacts;
@@ -613,7 +1028,8 @@ async function verify(config, request, options) {
613
1028
  "[VerificationGateway] minTrustScore / minTrustScoreForFull are deprecated in v2.3.0 and have no effect. Server is now the single source of truth for access decisions (verified ID + runtime challenge + PDLSS + trust score). To gate access to an endpoint, configure the endpoint's trust_score_requirement server-side."
614
1029
  );
615
1030
  }
616
- if (mergedConfig.cacheTtl !== 0 && !options?.bypassCache) {
1031
+ const uncacheable = Boolean(request.commerceArtifacts);
1032
+ if (mergedConfig.cacheTtl !== 0 && !options?.bypassCache && !uncacheable) {
617
1033
  const cached = getCachedResult(request, mergedConfig.counterpartyId);
618
1034
  if (cached) {
619
1035
  if (mergedConfig.debug) {
@@ -694,7 +1110,10 @@ async function verify(config, request, options) {
694
1110
  // MERCHANT caller only (bridge callers strip it before the agent plane).
695
1111
  settlementToken: apiResponse.settlementToken,
696
1112
  // 5.6.0: fulfilment contact — merchant-only lane, same strip rule.
697
- fulfilment: apiResponse.fulfilment
1113
+ fulfilment: apiResponse.fulfilment,
1114
+ mandates: apiResponse.mandates,
1115
+ kyaToken: apiResponse.kyaToken,
1116
+ aep: apiResponse.aep
698
1117
  };
699
1118
  return result2;
700
1119
  }
@@ -704,7 +1123,10 @@ async function verify(config, request, options) {
704
1123
  trustScore: apiResponse.agent.trustScore,
705
1124
  trustLevel: getTrustLevel(apiResponse.agent.trustScore),
706
1125
  blockchainAnchored: apiResponse.agent.blockchainStatus === "anchored",
707
- status: apiResponse.agent.agentStatus
1126
+ status: apiResponse.agent.agentStatus,
1127
+ ...apiResponse.agent.protocolIdentities && {
1128
+ protocolIdentities: apiResponse.agent.protocolIdentities
1129
+ }
708
1130
  } : void 0;
709
1131
  const developer = apiResponse.developer ? {
710
1132
  astradId: apiResponse.developer.kyaOwnerId,
@@ -755,7 +1177,11 @@ async function verify(config, request, options) {
755
1177
  settlementOutcome: apiResponse.settlementOutcome,
756
1178
  settlementToken: apiResponse.settlementToken,
757
1179
  // 5.6.0: fulfilment contact — merchant-only lane, same strip rule.
758
- fulfilment: apiResponse.fulfilment
1180
+ fulfilment: apiResponse.fulfilment,
1181
+ // 5.15.0: merchant-lane AP2 mandates + KYAPay identity token.
1182
+ mandates: apiResponse.mandates,
1183
+ kyaToken: apiResponse.kyaToken,
1184
+ aep: apiResponse.aep
759
1185
  };
760
1186
  if (result.recommendation === "deny") {
761
1187
  result.policyAllowed = false;
@@ -775,7 +1201,9 @@ async function verify(config, request, options) {
775
1201
  }
776
1202
  const escalates = result.requiresStepUp === true || result.requiresApproval === true || result.recommendation === "step_up_required";
777
1203
  if (mergedConfig.cacheTtl !== 0 && result.recommendation !== "deny" && !escalates) {
778
- cacheResult(request, result, mergedConfig.cacheTtl, mergedConfig.counterpartyId);
1204
+ if (!uncacheable) {
1205
+ cacheResult(request, result, mergedConfig.cacheTtl, mergedConfig.counterpartyId);
1206
+ }
779
1207
  }
780
1208
  return result;
781
1209
  }
@@ -929,239 +1357,30 @@ function performCounterpartyPreCheck(routeConfig, astraCreds, purpose) {
929
1357
  return failures;
930
1358
  }
931
1359
 
932
- // src/metadata-capture.ts
933
- var CAPTURE_SCHEMA_VERSION = 1;
934
- var SECRET_HEADER_NAMES = /* @__PURE__ */ new Set([
935
- "cookie",
936
- "set-cookie",
937
- "x-csrf-token",
938
- "proxy-authorization",
939
- "x-astrasync-signature",
940
- "x-astrasync-secret",
941
- "x-hub-signature",
942
- "x-hub-signature-256",
943
- // Short-lived verify dedupe token — must never persist in stored metadata.
944
- "x-astra-verified-hop"
945
- ]);
946
- var SECRET_HEADER_PREFIXES = ["x-astrasync-", "kya-", "kya_"];
947
- var CREDENTIAL_HEADER_NAMES = /* @__PURE__ */ new Set([
948
- "authorization",
949
- "x-api-key",
950
- "x-goog-api-key",
951
- "api-key"
952
- ]);
953
- function isSignatureHeader(name) {
954
- return name === "signature" || name.endsWith("-signature") || name.endsWith("-signature-256");
955
- }
956
- var PLATFORM_HEADER_NAMES = /* @__PURE__ */ new Set([
957
- "openai-organization",
958
- "openai-project",
959
- "openai-version",
960
- "openai-processing-ms",
961
- "anthropic-version",
962
- "anthropic-beta",
963
- "x-goog-user-project",
964
- "x-goog-api-client",
965
- "user-agent",
966
- "x-agent-card-url",
967
- "x-agent-provider"
968
- ]);
969
- var PLATFORM_HEADER_PREFIXES = [
970
- "x-goog-",
971
- "x-stainless-",
972
- "x-anthropic-",
973
- "x-openai-",
974
- "sec-ch-"
975
- ];
976
- var KEY_FORMAT_PREFIXES = [
977
- "sk-ant-api03",
978
- "sk-ant-api",
979
- "sk-ant",
980
- "sk-proj",
981
- "sk-svcacct",
982
- "sk-or-v1",
983
- "sk-or",
984
- "sk-lf",
985
- "sk-",
986
- "gsk_",
987
- "aiza",
988
- "ya29",
989
- "ghp_",
990
- "gho_",
991
- "github_pat_",
992
- "xai-",
993
- "pplx-",
994
- "r8_",
995
- "hf_",
996
- "astra-",
997
- "astrae-"
998
- ];
999
- var MAX_HEADERS = 64;
1000
- var MAX_HEADER_VALUE_BYTES = 1024;
1001
- var MAX_TOTAL_BYTES = 16 * 1024;
1002
- function byteLength(s) {
1003
- let bytes = 0;
1004
- for (let i = 0; i < s.length; i++) {
1005
- const code = s.charCodeAt(i);
1006
- if (code < 128) bytes += 1;
1007
- else if (code < 2048) bytes += 2;
1008
- else if (code >= 55296 && code <= 56319) {
1009
- bytes += 4;
1010
- i++;
1011
- } else bytes += 3;
1012
- }
1013
- return bytes;
1014
- }
1015
- function extractApiKeyFormat(rawValue) {
1016
- if (!rawValue) return void 0;
1017
- let value = rawValue.trim();
1018
- const spaceIdx = value.indexOf(" ");
1019
- if (spaceIdx > 0) {
1020
- const scheme = value.slice(0, spaceIdx).toLowerCase();
1021
- if (scheme === "bearer" || scheme === "basic" || scheme === "token") {
1022
- value = value.slice(spaceIdx + 1).trim();
1023
- }
1024
- }
1025
- const lower = value.toLowerCase();
1026
- for (const prefix of KEY_FORMAT_PREFIXES) {
1027
- if (lower.startsWith(prefix)) {
1028
- return prefix;
1029
- }
1030
- }
1031
- return void 0;
1032
- }
1033
- function isSecretHeader(name) {
1034
- if (SECRET_HEADER_NAMES.has(name)) return true;
1035
- if (SECRET_HEADER_PREFIXES.some((p) => name.startsWith(p))) return true;
1036
- if (isSignatureHeader(name)) return true;
1037
- return false;
1038
- }
1039
- function isPlatformHeader(name) {
1040
- if (PLATFORM_HEADER_NAMES.has(name)) return true;
1041
- return PLATFORM_HEADER_PREFIXES.some((p) => name.startsWith(p));
1042
- }
1043
- function sanitizeHeaders(raw) {
1044
- const headers = {};
1045
- const platformHeaders = {};
1046
- let apiKeyFormat;
1047
- let cookieCount;
1048
- let totalBytes = 0;
1049
- let count = 0;
1050
- if (!raw) return { headers };
1051
- for (const rawName of Object.keys(raw)) {
1052
- if (count >= MAX_HEADERS) break;
1053
- const name = rawName.toLowerCase();
1054
- const rawValue = raw[rawName];
1055
- if (rawValue === void 0) continue;
1056
- const joined = Array.isArray(rawValue) ? rawValue.join(", ") : String(rawValue);
1057
- if (CREDENTIAL_HEADER_NAMES.has(name)) {
1058
- const fmt = extractApiKeyFormat(joined);
1059
- if (fmt && !apiKeyFormat) apiKeyFormat = fmt;
1060
- continue;
1061
- }
1062
- if (name === "cookie") {
1063
- cookieCount = joined.split(";").filter((s) => s.trim().length > 0).length;
1064
- }
1065
- if (isSecretHeader(name)) continue;
1066
- let value = joined;
1067
- if (byteLength(value) > MAX_HEADER_VALUE_BYTES) {
1068
- value = value.slice(0, MAX_HEADER_VALUE_BYTES);
1069
- }
1070
- const entryBytes = byteLength(name) + byteLength(value);
1071
- if (totalBytes + entryBytes > MAX_TOTAL_BYTES) continue;
1072
- totalBytes += entryBytes;
1073
- count++;
1074
- headers[name] = value;
1075
- if (isPlatformHeader(name)) platformHeaders[name] = value;
1076
- }
1077
- const result = { headers };
1078
- if (apiKeyFormat) result.apiKeyFormat = apiKeyFormat;
1079
- if (Object.keys(platformHeaders).length > 0) result.platformHeaders = platformHeaders;
1080
- if (cookieCount !== void 0) result.cookieCount = cookieCount;
1081
- return result;
1082
- }
1083
- var CONNECTION_HEADER_SOURCES = [
1084
- ["ip", ["cf-connecting-ip", "fastly-client-ip", "true-client-ip", "x-real-ip"]],
1085
- [
1086
- "country",
1087
- ["cf-ipcountry", "x-vercel-ip-country", "cloudfront-viewer-country", "fastly-geo-country"]
1088
- ],
1089
- ["region", ["x-vercel-ip-country-region", "cloudfront-viewer-country-region"]],
1090
- ["countryRegionName", ["cloudfront-viewer-country-region-name"]],
1091
- ["city", ["x-vercel-ip-city", "cloudfront-viewer-city"]],
1092
- ["latitude", ["x-vercel-ip-latitude", "cloudfront-viewer-latitude"]],
1093
- ["longitude", ["x-vercel-ip-longitude", "cloudfront-viewer-longitude"]],
1094
- ["postalCode", ["x-vercel-ip-postal-code", "cloudfront-viewer-postal-code"]],
1095
- ["asn", ["cloudfront-viewer-asn"]],
1096
- ["tlsVersion", ["cloudfront-viewer-tls"]],
1097
- ["httpVersion", ["cloudfront-viewer-http-version"]],
1098
- ["timeZone", ["x-vercel-ip-timezone", "cloudfront-viewer-time-zone"]],
1099
- ["metroCode", ["cloudfront-viewer-metro-code"]],
1100
- // TLS fingerprints can only be computed where TLS terminates. When a CDN
1101
- // terminates TLS in front of the merchant, its fingerprint headers ARE the
1102
- // connection-layer truth — normalising them here is the permanent design.
1103
- // CloudFront delivers JA3/JA4 only via an origin request policy and only
1104
- // for HTTPS viewer connections.
1105
- ["tlsFingerprint", ["cf-ja3-hash", "cloudfront-viewer-ja3-fingerprint"]],
1106
- ["ja4", ["cf-ja4", "cloudfront-viewer-ja4-fingerprint"]],
1107
- // Header-structure fingerprint: browser/SDK-distinctive header ordering.
1108
- ["headerOrder", ["cloudfront-viewer-header-order"]],
1109
- ["headerCount", ["cloudfront-viewer-header-count"]]
1110
- ];
1111
- var MAX_CONNECTION_VALUE_CHARS = 256;
1112
- function deriveConnectionFromHeaders(headers) {
1113
- if (!headers) return void 0;
1114
- const lower = {};
1115
- for (const name of Object.keys(headers)) {
1116
- const value = headers[name];
1117
- if (typeof value === "string" && value.length > 0) lower[name.toLowerCase()] = value;
1118
- }
1119
- const connection = {};
1120
- for (const [key, sources] of CONNECTION_HEADER_SOURCES) {
1121
- for (const source of sources) {
1122
- const value = lower[source]?.trim();
1123
- if (value) {
1124
- connection[key] = value.slice(0, MAX_CONNECTION_VALUE_CHARS);
1125
- break;
1126
- }
1360
+ // src/route-pattern.ts
1361
+ function routePatternToRegExp(pattern, opts = {}) {
1362
+ let source = "";
1363
+ for (let i = 0; i < pattern.length; i++) {
1364
+ const ch = pattern[i];
1365
+ if (ch === "*") {
1366
+ source += ".*";
1367
+ } else if (ch === ":" && /[A-Za-z_]/.test(pattern[i + 1] ?? "") && (i === 0 || pattern[i - 1] === "/")) {
1368
+ let j = i + 1;
1369
+ while (j < pattern.length && /[A-Za-z0-9_]/.test(pattern[j])) j++;
1370
+ source += "[^/]+";
1371
+ i = j - 1;
1372
+ } else {
1373
+ source += ch.replace(/[.+?^${}()|[\]\\/]/g, "\\$&");
1127
1374
  }
1128
1375
  }
1129
- {
1130
- const addr = lower["cloudfront-viewer-address"]?.trim();
1131
- const portMatch = addr?.match(/:(\d+)$/);
1132
- if (portMatch) connection.sourcePort = portMatch[1];
1133
- if (!connection.ip && addr) connection.ip = addr.replace(/:\d+$/, "");
1134
- }
1135
- if (!connection.ip) {
1136
- const xff = lower["x-forwarded-for"];
1137
- const client = xff?.split(",")[0]?.trim();
1138
- if (client) connection.ip = client;
1139
- }
1140
- if (!connection.tlsCipher) {
1141
- const cipher = connection.tlsVersion?.split(":")[1];
1142
- if (cipher) connection.tlsCipher = cipher;
1376
+ try {
1377
+ return new RegExp(`^${source}$`, opts.caseInsensitive ? "i" : "");
1378
+ } catch {
1379
+ return null;
1143
1380
  }
1144
- return Object.keys(connection).length > 0 ? connection : void 0;
1145
1381
  }
1146
- function buildSdkObservedMetadata(rawHeaders) {
1147
- const { headers, apiKeyFormat, platformHeaders, cookieCount } = sanitizeHeaders(rawHeaders);
1148
- const flat = {};
1149
- if (rawHeaders) {
1150
- for (const name of Object.keys(rawHeaders)) {
1151
- const value = rawHeaders[name];
1152
- if (value === void 0) continue;
1153
- flat[name.toLowerCase()] = Array.isArray(value) ? value.join(", ") : String(value);
1154
- }
1155
- }
1156
- const connection = deriveConnectionFromHeaders(flat);
1157
- return {
1158
- headers,
1159
- ...apiKeyFormat && { apiKeyFormat },
1160
- ...platformHeaders && { platformHeaders },
1161
- ...connection && { connection },
1162
- ...cookieCount !== void 0 && { cookieCount },
1163
- schemaVersion: CAPTURE_SCHEMA_VERSION
1164
- };
1382
+ function matchRoutePattern(pattern, path, opts = {}) {
1383
+ return routePatternToRegExp(pattern, opts)?.test(path) ?? false;
1165
1384
  }
1166
1385
 
1167
1386
  // src/edge-config.ts
@@ -1196,6 +1415,7 @@ function isEdgeConfig(value) {
1196
1415
  if (r.sampling !== void 0 && !isSamplingShape(r.sampling)) return false;
1197
1416
  }
1198
1417
  if (!isSamplingShape(v.sampling)) return false;
1418
+ if (v.minTls !== void 0 && v.minTls !== "1.2" && v.minTls !== "1.3") return false;
1199
1419
  if (v.queryValueAllowlist !== void 0) {
1200
1420
  if (!Array.isArray(v.queryValueAllowlist) || v.queryValueAllowlist.length > 16 || !v.queryValueAllowlist.every((e) => typeof e === "string" && e.length > 0 && e.length <= 64)) {
1201
1421
  return false;
@@ -1446,6 +1666,11 @@ async function withBudget(promise, ms) {
1446
1666
  if (timer) clearTimeout(timer);
1447
1667
  }
1448
1668
  }
1669
+ var CONTAINER_ID = (() => {
1670
+ const c = globalThis.crypto;
1671
+ if (c?.randomUUID) return c.randomUUID();
1672
+ return `c-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 12)}`;
1673
+ })();
1449
1674
  var destKeyOf = (d) => `${d.apiBaseUrl}\0${d.apiKey ?? ""}\0${d.counterparty}\0${d.adapter ?? ""}`;
1450
1675
  var BeaconBuffer = class {
1451
1676
  constructor(options = {}) {
@@ -1453,6 +1678,7 @@ var BeaconBuffer = class {
1453
1678
  this.droppedSinceLast = 0;
1454
1679
  this.droppedTotal = 0;
1455
1680
  this.deliveredTotal = 0;
1681
+ this.enqueuedTotal = 0;
1456
1682
  this.backoffUntil = 0;
1457
1683
  this.collapse = /* @__PURE__ */ new Map();
1458
1684
  /** Keys the backend answered 401 for — later batches go unauthenticated. */
@@ -1465,13 +1691,16 @@ var BeaconBuffer = class {
1465
1691
  collapseMaxKeys: options.collapseMaxKeys ?? 1e3,
1466
1692
  requestTimeoutMs: options.requestTimeoutMs ?? 1e4,
1467
1693
  retryBackoffMs: options.retryBackoffMs ?? 1e3,
1694
+ holdRepeats: options.holdRepeats ?? false,
1695
+ containerId: options.containerId ?? CONTAINER_ID,
1468
1696
  now: options.now ?? Date.now
1469
1697
  };
1470
1698
  }
1471
1699
  /**
1472
1700
  * Queue one beacon. With a `collapseKey`, a repeat inside the window is
1473
- * merged into a held aggregate (sent when the window closes) instead of
1474
- * becoming its own item.
1701
+ * merged into this key's item while that item is still queued (not yet in
1702
+ * flight). Once it has left, the repeat becomes a new item, sendable on
1703
+ * this flush — nothing waits for a later invocation (round 27.6).
1475
1704
  */
1476
1705
  enqueue(dest, item, collapseKey) {
1477
1706
  const now = this.opts.now();
@@ -1484,12 +1713,15 @@ var BeaconBuffer = class {
1484
1713
  return;
1485
1714
  }
1486
1715
  }
1487
- const held = this.push(
1488
- dest,
1489
- { ...item, repeatCount: 1 },
1490
- entry.firstSeenAt + this.opts.collapseWindowMs
1491
- );
1492
- entry.held = held;
1716
+ if (this.opts.holdRepeats) {
1717
+ entry.held = this.push(
1718
+ dest,
1719
+ { ...item, repeatCount: 1 },
1720
+ entry.firstSeenAt + this.opts.collapseWindowMs
1721
+ );
1722
+ return;
1723
+ }
1724
+ entry.first = this.push(dest, { ...item, repeatCount: 1 }, now);
1493
1725
  return;
1494
1726
  }
1495
1727
  this.collapse.delete(collapseKey);
@@ -1525,6 +1757,7 @@ var BeaconBuffer = class {
1525
1757
  droppedSinceLast: this.droppedSinceLast,
1526
1758
  droppedTotal: this.droppedTotal,
1527
1759
  deliveredTotal: this.deliveredTotal,
1760
+ enqueuedTotal: this.enqueuedTotal,
1528
1761
  flushing: this.inFlight !== void 0
1529
1762
  };
1530
1763
  }
@@ -1539,6 +1772,7 @@ var BeaconBuffer = class {
1539
1772
  inFlight: false
1540
1773
  };
1541
1774
  this.queue.push(entry);
1775
+ this.enqueuedTotal += 1;
1542
1776
  return entry;
1543
1777
  }
1544
1778
  dropOldest() {
@@ -1584,7 +1818,11 @@ var BeaconBuffer = class {
1584
1818
  droppedSinceLast: reportedDropped,
1585
1819
  queueDepth: this.queue.length,
1586
1820
  adapter: dest.adapter ?? "edge-core",
1587
- version: SDK_VERSION
1821
+ version: SDK_VERSION,
1822
+ containerId: this.opts.containerId,
1823
+ enqueuedTotal: this.enqueuedTotal,
1824
+ deliveredTotal: this.deliveredTotal,
1825
+ droppedTotal: this.droppedTotal
1588
1826
  };
1589
1827
  const controller = typeof AbortController !== "undefined" ? new AbortController() : void 0;
1590
1828
  const response = await withBudget(
@@ -1893,9 +2131,7 @@ function extractCredentialsFromNextRequest(request) {
1893
2131
  return credentials;
1894
2132
  }
1895
2133
  function matchRoute(pattern, path) {
1896
- const regexPattern = pattern.replace(/\*/g, ".*").replace(/\//g, "\\/");
1897
- const regex = new RegExp(`^${regexPattern}$`);
1898
- return regex.test(path);
2134
+ return matchRoutePattern(pattern, path);
1899
2135
  }
1900
2136
  function findRouteConfig(routes, path, method) {
1901
2137
  return routes.find((route) => {
@@ -2086,7 +2322,8 @@ function createMiddleware(options) {
2086
2322
  skipPaths = [],
2087
2323
  showInterstitial,
2088
2324
  showCommerceShield,
2089
- enableRuntimeChallenge = true,
2325
+ // Round 27.8: opt-in only (the merchant turns it on); was default-on.
2326
+ enableRuntimeChallenge = false,
2090
2327
  routesRefreshMs = DEFAULT_ROUTES_REFRESH_MS,
2091
2328
  anonymousBeacon = true,
2092
2329
  trustVerifiedHop = false,
@@ -2223,8 +2460,10 @@ function createMiddleware(options) {
2223
2460
  const originalClientIp = forwardedFor?.split(",")[0]?.trim();
2224
2461
  const attemptHeader = request.headers.get("x-astra-attempt-id");
2225
2462
  const inboundAttemptId = attemptHeader && /^att_[0-9a-f]{32}$/.test(attemptHeader) ? attemptHeader : void 0;
2463
+ const commerceArtifacts = await detectFetchCommerce(request);
2226
2464
  const verificationRequest = {
2227
2465
  credentials,
2466
+ ...commerceArtifacts && { commerceArtifacts },
2228
2467
  purpose,
2229
2468
  action: pdlssPair.action,
2230
2469
  resource: pathname,