@adcp/sdk 14.0.0 → 14.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (234) hide show
  1. package/dist/lib/adapters/implicit-account-store.d.mts +12 -7
  2. package/dist/lib/adapters/implicit-account-store.d.ts +12 -7
  3. package/dist/lib/adapters/implicit-account-store.js +69 -15
  4. package/dist/lib/adapters/implicit-account-store.mjs +69 -15
  5. package/dist/lib/core/AgentClient.d.mts +1 -0
  6. package/dist/lib/core/AgentClient.d.ts +1 -0
  7. package/dist/lib/core/AgentClient.js +3 -0
  8. package/dist/lib/core/AgentClient.mjs +3 -0
  9. package/dist/lib/core/SingleAgentClient.d.mts +35 -2
  10. package/dist/lib/core/SingleAgentClient.d.ts +35 -2
  11. package/dist/lib/core/SingleAgentClient.js +377 -36
  12. package/dist/lib/core/SingleAgentClient.mjs +387 -38
  13. package/dist/lib/core/TaskExecutor.d.mts +3 -1
  14. package/dist/lib/core/TaskExecutor.d.ts +3 -1
  15. package/dist/lib/core/TaskExecutor.js +17 -12
  16. package/dist/lib/core/TaskExecutor.mjs +17 -12
  17. package/dist/lib/core/account-key.d.mts +3 -0
  18. package/dist/lib/core/account-key.d.ts +3 -0
  19. package/dist/lib/core/account-key.js +41 -0
  20. package/dist/lib/core/account-key.mjs +17 -0
  21. package/dist/lib/core/account-resolution.d.mts +2 -0
  22. package/dist/lib/core/account-resolution.d.ts +2 -0
  23. package/dist/lib/core/buyer-account-registry.d.mts +93 -0
  24. package/dist/lib/core/buyer-account-registry.d.ts +93 -0
  25. package/dist/lib/core/buyer-account-registry.js +602 -0
  26. package/dist/lib/core/buyer-account-registry.mjs +578 -0
  27. package/dist/lib/core/product-cache.d.mts +18 -0
  28. package/dist/lib/core/product-cache.d.ts +18 -0
  29. package/dist/lib/core/product-cache.js +137 -0
  30. package/dist/lib/core/product-cache.mjs +112 -0
  31. package/dist/lib/errors/index.d.mts +40 -1
  32. package/dist/lib/errors/index.d.ts +40 -1
  33. package/dist/lib/errors/index.js +69 -3
  34. package/dist/lib/errors/index.mjs +64 -3
  35. package/dist/lib/governance/authorization.d.mts +17 -1
  36. package/dist/lib/governance/authorization.d.ts +17 -1
  37. package/dist/lib/governance/authorization.js +55 -7
  38. package/dist/lib/governance/authorization.mjs +59 -7
  39. package/dist/lib/governance/index.d.mts +2 -2
  40. package/dist/lib/governance/index.d.ts +2 -2
  41. package/dist/lib/governance/index.js +2 -0
  42. package/dist/lib/governance/index.mjs +3 -1
  43. package/dist/lib/index.d.mts +7 -4
  44. package/dist/lib/index.d.ts +7 -4
  45. package/dist/lib/index.js +29 -0
  46. package/dist/lib/index.mjs +31 -1
  47. package/dist/lib/net/agent-transport-fetch.d.mts +4 -0
  48. package/dist/lib/net/agent-transport-fetch.d.ts +4 -0
  49. package/dist/lib/net/agent-transport-fetch.js +18 -5
  50. package/dist/lib/net/agent-transport-fetch.mjs +17 -5
  51. package/dist/lib/protocols/a2a.js +9 -1
  52. package/dist/lib/protocols/a2a.mjs +9 -1
  53. package/dist/lib/protocols/index.js +9 -2
  54. package/dist/lib/protocols/index.mjs +9 -2
  55. package/dist/lib/protocols/mcp-modern.js +2 -1
  56. package/dist/lib/protocols/mcp-modern.mjs +2 -1
  57. package/dist/lib/protocols/mcp.js +5 -2
  58. package/dist/lib/protocols/mcp.mjs +5 -2
  59. package/dist/lib/protocols/rawResponseCapture.d.mts +6 -0
  60. package/dist/lib/protocols/rawResponseCapture.d.ts +6 -0
  61. package/dist/lib/protocols/rawResponseCapture.js +41 -29
  62. package/dist/lib/protocols/rawResponseCapture.mjs +40 -29
  63. package/dist/lib/protocols/signedRequestRejection.d.mts +9 -0
  64. package/dist/lib/protocols/signedRequestRejection.d.ts +9 -0
  65. package/dist/lib/protocols/signedRequestRejection.js +209 -0
  66. package/dist/lib/protocols/signedRequestRejection.mjs +189 -0
  67. package/dist/lib/protocols/transportDiagnostics.d.mts +1 -0
  68. package/dist/lib/protocols/transportDiagnostics.d.ts +1 -0
  69. package/dist/lib/protocols/transportDiagnostics.js +2 -0
  70. package/dist/lib/protocols/transportDiagnostics.mjs +1 -0
  71. package/dist/lib/registry/types.generated.d.mts +112 -45
  72. package/dist/lib/registry/types.generated.d.ts +112 -45
  73. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  74. package/dist/lib/server/account-provisioning.d.mts +2 -0
  75. package/dist/lib/server/account-provisioning.d.ts +2 -0
  76. package/dist/lib/server/account-provisioning.js +30 -0
  77. package/dist/lib/server/account-provisioning.mjs +6 -0
  78. package/dist/lib/server/account-reference-warnings.d.mts +12 -0
  79. package/dist/lib/server/account-reference-warnings.d.ts +12 -0
  80. package/dist/lib/server/account-reference-warnings.js +48 -0
  81. package/dist/lib/server/account-reference-warnings.mjs +23 -0
  82. package/dist/lib/server/auth-signature.js +1 -0
  83. package/dist/lib/server/auth-signature.mjs +1 -0
  84. package/dist/lib/server/create-adcp-server.d.mts +34 -0
  85. package/dist/lib/server/create-adcp-server.d.ts +34 -0
  86. package/dist/lib/server/create-adcp-server.js +225 -14
  87. package/dist/lib/server/create-adcp-server.mjs +225 -14
  88. package/dist/lib/server/decisioning/account.d.mts +2 -0
  89. package/dist/lib/server/decisioning/account.d.ts +2 -0
  90. package/dist/lib/server/decisioning/runtime/from-platform.js +49 -10
  91. package/dist/lib/server/decisioning/runtime/from-platform.mjs +49 -10
  92. package/dist/lib/server/index.d.mts +2 -2
  93. package/dist/lib/server/index.d.ts +2 -2
  94. package/dist/lib/server/index.js +2 -0
  95. package/dist/lib/server/index.mjs +3 -1
  96. package/dist/lib/signing/agent-resolver/consistency.d.mts +6 -13
  97. package/dist/lib/signing/agent-resolver/consistency.d.ts +6 -13
  98. package/dist/lib/signing/agent-resolver/consistency.js +0 -1
  99. package/dist/lib/signing/agent-resolver/consistency.mjs +0 -1
  100. package/dist/lib/signing/agent-resolver/errors.d.mts +3 -1
  101. package/dist/lib/signing/agent-resolver/errors.d.ts +3 -1
  102. package/dist/lib/signing/agent-resolver/errors.js +6 -0
  103. package/dist/lib/signing/agent-resolver/errors.mjs +6 -0
  104. package/dist/lib/signing/agent-resolver/fetch-helpers.d.mts +11 -0
  105. package/dist/lib/signing/agent-resolver/fetch-helpers.d.ts +11 -0
  106. package/dist/lib/signing/agent-resolver/fetch-helpers.js +37 -2
  107. package/dist/lib/signing/agent-resolver/fetch-helpers.mjs +40 -3
  108. package/dist/lib/signing/agent-resolver/jwks-set.js +24 -3
  109. package/dist/lib/signing/agent-resolver/jwks-set.mjs +24 -3
  110. package/dist/lib/signing/agent-resolver/legacy-brand.d.mts +15 -0
  111. package/dist/lib/signing/agent-resolver/legacy-brand.d.ts +15 -0
  112. package/dist/lib/signing/agent-resolver/legacy-brand.js +60 -0
  113. package/dist/lib/signing/agent-resolver/legacy-brand.mjs +36 -0
  114. package/dist/lib/signing/agent-resolver/operator-authorization.d.mts +13 -0
  115. package/dist/lib/signing/agent-resolver/operator-authorization.d.ts +13 -0
  116. package/dist/lib/signing/agent-resolver/operator-authorization.js +108 -0
  117. package/dist/lib/signing/agent-resolver/operator-authorization.mjs +84 -0
  118. package/dist/lib/signing/agent-resolver/resolve-agent.d.mts +14 -4
  119. package/dist/lib/signing/agent-resolver/resolve-agent.d.ts +14 -4
  120. package/dist/lib/signing/agent-resolver/resolve-agent.js +148 -137
  121. package/dist/lib/signing/agent-resolver/resolve-agent.mjs +157 -139
  122. package/dist/lib/signing/agent-resolver/resolved-agent-jwks.d.mts +7 -1
  123. package/dist/lib/signing/agent-resolver/resolved-agent-jwks.d.ts +7 -1
  124. package/dist/lib/signing/agent-resolver/resolved-agent-jwks.js +50 -17
  125. package/dist/lib/signing/agent-resolver/resolved-agent-jwks.mjs +50 -17
  126. package/dist/lib/signing/agent-resolver/select-agent.d.mts +14 -15
  127. package/dist/lib/signing/agent-resolver/select-agent.d.ts +14 -15
  128. package/dist/lib/signing/agent-resolver/select-agent.js +98 -13
  129. package/dist/lib/signing/agent-resolver/select-agent.mjs +95 -13
  130. package/dist/lib/signing/brand-jwks.d.mts +29 -75
  131. package/dist/lib/signing/brand-jwks.d.ts +29 -75
  132. package/dist/lib/signing/brand-jwks.js +120 -182
  133. package/dist/lib/signing/brand-jwks.mjs +120 -182
  134. package/dist/lib/signing/errors.d.mts +7 -3
  135. package/dist/lib/signing/errors.d.ts +7 -3
  136. package/dist/lib/signing/errors.js +7 -2
  137. package/dist/lib/signing/errors.mjs +7 -2
  138. package/dist/lib/signing/jwks-https.d.mts +7 -0
  139. package/dist/lib/signing/jwks-https.d.ts +7 -0
  140. package/dist/lib/signing/jwks-https.js +31 -8
  141. package/dist/lib/signing/jwks-https.mjs +31 -8
  142. package/dist/lib/signing/jwks.d.mts +8 -0
  143. package/dist/lib/signing/jwks.d.ts +8 -0
  144. package/dist/lib/signing/middleware.js +2 -1
  145. package/dist/lib/signing/middleware.mjs +2 -1
  146. package/dist/lib/signing/publisher-pins.d.mts +11 -0
  147. package/dist/lib/signing/publisher-pins.d.ts +11 -0
  148. package/dist/lib/signing/publisher-pins.js +125 -0
  149. package/dist/lib/signing/publisher-pins.mjs +101 -0
  150. package/dist/lib/signing/server.d.mts +1 -0
  151. package/dist/lib/signing/server.d.ts +1 -0
  152. package/dist/lib/signing/types.d.mts +5 -0
  153. package/dist/lib/signing/types.d.ts +5 -0
  154. package/dist/lib/signing/verifier.js +50 -5
  155. package/dist/lib/signing/verifier.mjs +50 -5
  156. package/dist/lib/signing/webhook-verifier.d.mts +7 -2
  157. package/dist/lib/signing/webhook-verifier.d.ts +7 -2
  158. package/dist/lib/signing/webhook-verifier.js +42 -2
  159. package/dist/lib/signing/webhook-verifier.mjs +42 -2
  160. package/dist/lib/testing/storyboard/account-policy.d.mts +2 -0
  161. package/dist/lib/testing/storyboard/account-policy.d.ts +2 -0
  162. package/dist/lib/testing/storyboard/account-policy.js +35 -0
  163. package/dist/lib/testing/storyboard/account-policy.mjs +11 -0
  164. package/dist/lib/testing/storyboard/context.js +6 -0
  165. package/dist/lib/testing/storyboard/context.mjs +6 -0
  166. package/dist/lib/testing/storyboard/request-builder.js +12 -2
  167. package/dist/lib/testing/storyboard/request-builder.mjs +12 -2
  168. package/dist/lib/testing/storyboard/runner.js +3 -2
  169. package/dist/lib/testing/storyboard/runner.mjs +3 -2
  170. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  171. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  172. package/dist/lib/types/accept-proposal.d.ts +19 -1
  173. package/dist/lib/types/buy-products.d.ts +19 -1
  174. package/dist/lib/types/check-governance.d.ts +19 -1
  175. package/dist/lib/types/comply-test-controller.d.ts +19 -1
  176. package/dist/lib/types/control-media-buy.d.ts +19 -1
  177. package/dist/lib/types/core.generated.d.mts +14 -1
  178. package/dist/lib/types/core.generated.d.ts +14 -1
  179. package/dist/lib/types/create-media-buy.d.ts +14 -1
  180. package/dist/lib/types/get-media-buys.d.ts +14 -1
  181. package/dist/lib/types/get-products.d.ts +19 -1
  182. package/dist/lib/types/list-products.d.ts +19 -1
  183. package/dist/lib/types/refine-proposals.d.ts +19 -1
  184. package/dist/lib/types/request-proposals.d.ts +19 -1
  185. package/dist/lib/types/schemas.generated.d.ts +12 -3
  186. package/dist/lib/types/schemas.generated.js +4 -1
  187. package/dist/lib/types/schemas.generated.mjs +4 -1
  188. package/dist/lib/types/tools.generated.d.mts +14 -1
  189. package/dist/lib/types/tools.generated.d.ts +14 -1
  190. package/dist/lib/types/update-media-buy.d.ts +14 -1
  191. package/dist/lib/version.d.mts +3 -3
  192. package/dist/lib/version.d.ts +3 -3
  193. package/dist/lib/version.js +3 -3
  194. package/dist/lib/version.mjs +3 -3
  195. package/dist/lib/webhooks/index.d.mts +24 -0
  196. package/dist/lib/webhooks/index.d.ts +24 -0
  197. package/dist/lib/webhooks/index.js +50 -24
  198. package/dist/lib/webhooks/index.mjs +49 -24
  199. package/dist/lib/wholesale-feed-sync/index.d.mts +2 -0
  200. package/dist/lib/wholesale-feed-sync/index.d.ts +2 -0
  201. package/dist/lib/wholesale-feed-sync/index.js +7 -0
  202. package/dist/lib/wholesale-feed-sync/index.mjs +4 -0
  203. package/dist/lib/wholesale-feed-sync/mirror.d.mts +97 -0
  204. package/dist/lib/wholesale-feed-sync/mirror.d.ts +97 -0
  205. package/dist/lib/wholesale-feed-sync/mirror.js +350 -0
  206. package/dist/lib/wholesale-feed-sync/mirror.mjs +322 -0
  207. package/dist/lib/wholesale-feed-sync/sync.d.mts +13 -29
  208. package/dist/lib/wholesale-feed-sync/sync.d.ts +13 -29
  209. package/dist/lib/wholesale-feed-sync/sync.js +208 -281
  210. package/dist/lib/wholesale-feed-sync/sync.mjs +213 -281
  211. package/dist/lib/wholesale-feed-sync/types.d.mts +4 -3
  212. package/dist/lib/wholesale-feed-sync/types.d.ts +4 -3
  213. package/docs/README.md +6 -0
  214. package/docs/TYPE-SUMMARY.md +2 -2
  215. package/docs/guides/BUILD-AN-AGENT.md +2 -2
  216. package/docs/guides/BUYER-QUICKSTART-3.2.md +2 -0
  217. package/docs/guides/BUYER-STORAGE.md +3 -0
  218. package/docs/guides/FIRST-CALL-TO-A-SELLER.md +106 -0
  219. package/docs/guides/SIGNING-GUIDE.md +16 -7
  220. package/docs/guides/account-resolution.md +132 -10
  221. package/docs/llms.txt +3 -2
  222. package/docs/migration-14.0-to-14.1.md +85 -0
  223. package/docs/migration-14.x-rc-worksheet.md +4 -4
  224. package/docs/migration-4.x-to-5.x.md +1 -0
  225. package/docs/migration-agent-resolution-3.3.md +125 -0
  226. package/docs/recipes/verifying-inbound-webhooks.md +60 -15
  227. package/package.json +3 -2
  228. package/skills/adcp-brand.previous/SKILL.md +0 -200
  229. package/skills/adcp-creative.previous/SKILL.md +0 -305
  230. package/skills/adcp-governance.previous/SKILL.md +0 -566
  231. package/skills/adcp-measurement.previous/SKILL.md +0 -136
  232. package/skills/adcp-media-buy.previous/SKILL.md +0 -556
  233. package/skills/adcp-si.previous/SKILL.md +0 -206
  234. package/skills/adcp-signals.previous/SKILL.md +0 -204
@@ -1,7 +1,11 @@
1
+ import { classifyDiscoveryFailure, isPermanentDiscoveryFailure } from "./agent-resolver/fetch-helpers.mjs";
1
2
  import { ssrfSafeFetch } from "../net/index.mjs";
2
- import { HttpsJwksResolver } from "./jwks-https.mjs";
3
+ import { ResolvedAgentJwksResolver } from "./agent-resolver/resolved-agent-jwks.mjs";
4
+ import { parseStrictJson } from "./agent-resolver/strict-json.mjs";
5
+ import { canonicalAgentUrl } from "./agent-resolver/select-agent.mjs";
3
6
  class BrandJsonResolverError extends Error {
4
7
  code;
8
+ recovery;
5
9
  cause;
6
10
  /** HTTP status for `fetch_failed` responses, when a response was received. */
7
11
  httpStatus;
@@ -11,6 +15,7 @@ class BrandJsonResolverError extends Error {
11
15
  this.code = code;
12
16
  this.httpStatus = details.httpStatus;
13
17
  this.cause = details.cause;
18
+ this.recovery = code !== "fetch_failed" || isPermanentDiscoveryFailure(classifyDiscoveryFailure(this)) ? "terminal" : "transient";
14
19
  }
15
20
  }
16
21
  const DEFAULT_MIN_COOLDOWN_SECONDS = 30;
@@ -18,115 +23,115 @@ const DEFAULT_MAX_AGE_SECONDS = 3600;
18
23
  const DEFAULT_MAX_REDIRECTS = 3;
19
24
  const BARE_HOSTNAME = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/;
20
25
  class BrandJsonJwksResolver {
21
- url;
22
- selector;
23
- minCooldown;
24
- maxAge;
25
- maxRedirects;
26
- allowPrivateIp;
27
- lookup;
28
- jwksOptions;
29
- now;
30
- snapshot;
31
- inner;
32
- inFlight;
33
26
  constructor(brandJsonUrl, options) {
34
- this.url = brandJsonUrl;
35
- this.selector = {
27
+ this.brandJsonUrl = brandJsonUrl;
28
+ this.options = options;
29
+ for (const lifetime of [options.maxAgeSeconds, options.jwksOptions?.maxAgeSeconds]) {
30
+ if (lifetime !== void 0 && (!Number.isFinite(lifetime) || lifetime <= 0))
31
+ throw new TypeError("maxAgeSeconds must be a finite positive number.");
32
+ }
33
+ for (const cooldown of [options.minCooldownSeconds, options.jwksOptions?.minCooldownSeconds]) {
34
+ if (cooldown !== void 0 && (!Number.isFinite(cooldown) || cooldown < 0))
35
+ throw new TypeError("minCooldownSeconds must be a finite non-negative number.");
36
+ }
37
+ if (options.agentUrl !== void 0) this.resolver = this.createResolver(options.agentUrl, brandJsonUrl);
38
+ }
39
+ brandJsonUrl;
40
+ options;
41
+ resolver;
42
+ onboarding;
43
+ lastOnboardingAttempt = Number.NEGATIVE_INFINITY;
44
+ lastOnboardingError;
45
+ createResolver(agentUrl, operatorUrl) {
46
+ const options = this.options;
47
+ return new ResolvedAgentJwksResolver(agentUrl, options.protocol ?? "mcp", {
36
48
  agentType: options.agentType,
37
- ...options.agentId !== void 0 && { agentId: options.agentId },
38
- ...options.brandId !== void 0 && { brandId: options.brandId }
39
- };
40
- this.minCooldown = options.minCooldownSeconds ?? DEFAULT_MIN_COOLDOWN_SECONDS;
41
- this.maxAge = options.maxAgeSeconds ?? DEFAULT_MAX_AGE_SECONDS;
42
- this.maxRedirects = options.maxRedirects ?? DEFAULT_MAX_REDIRECTS;
43
- this.allowPrivateIp = options.allowPrivateIp ?? false;
44
- this.lookup = options.lookup;
45
- this.jwksOptions = options.jwksOptions ?? {};
46
- this.now = options.now ?? (() => Math.floor(Date.now() / 1e3));
49
+ agentId: options.agentId,
50
+ expectedBrandJsonUrl: operatorUrl,
51
+ fetchCapabilities: options.fetchCapabilities,
52
+ legacyWebhookFallback: options.legacyWebhookFallback ?? true,
53
+ allowPrivateIp: options.allowPrivateIp,
54
+ lookup: options.lookup,
55
+ now: options.now,
56
+ cacheTtlSeconds: Math.min(
57
+ options.maxAgeSeconds ?? DEFAULT_MAX_AGE_SECONDS,
58
+ options.jwksOptions?.maxAgeSeconds ?? 1800
59
+ ),
60
+ unknownKidCooldownSeconds: options.minCooldownSeconds ?? options.jwksOptions?.minCooldownSeconds ?? DEFAULT_MIN_COOLDOWN_SECONDS
61
+ });
47
62
  }
48
- /**
49
- * Resolve a JWK by `kid`. On a cold cache, fetches brand.json first; on an
50
- * expired brand.json snapshot, refreshes respecting the cooldown. Unknown
51
- * kids cascade: first ask the inner HttpsJwksResolver (which will refetch
52
- * its own URL if cooldown has elapsed); if still unknown, refresh
53
- * brand.json in case `jwks_uri` rotated.
54
- */
55
- async resolve(keyid) {
56
- if (!this.snapshot || !this.inner) {
57
- await this.refresh();
58
- } else if (this.now() > this.snapshot.expiresAt && this.now() - this.snapshot.fetchedAt >= this.minCooldown) {
59
- await this.refresh().catch(() => {
60
- });
61
- }
62
- if (!this.inner) return null;
63
- const hit = await this.inner.resolve(keyid);
64
- if (hit) return hit;
65
- if (this.snapshot && this.now() - this.snapshot.fetchedAt >= this.minCooldown) {
66
- await this.refresh().catch(() => {
63
+ async confirmedResolver() {
64
+ if (this.resolver) return this.resolver;
65
+ if (this.onboarding) return this.onboarding;
66
+ const now = this.options.now?.() ?? Date.now() / 1e3;
67
+ if (now - this.lastOnboardingAttempt < 30)
68
+ throw this.lastOnboardingError ?? new BrandJsonResolverError("fetch_failed", "Operator onboarding discovery is in its retry cooldown.");
69
+ this.lastOnboardingAttempt = now;
70
+ this.onboarding = (async () => {
71
+ const record = await fetchBrandJson({
72
+ startUrl: this.brandJsonUrl,
73
+ maxRedirects: this.options.maxRedirects,
74
+ allowPrivateIp: this.options.allowPrivateIp,
75
+ lookup: this.options.lookup
67
76
  });
68
- return this.inner ? this.inner.resolve(keyid) : null;
69
- }
70
- return null;
77
+ const agentUrl = inferOnboardingAgentUrl(record.data, this.options);
78
+ const resolver = this.createResolver(agentUrl, record.finalUrl);
79
+ await resolver.forceRefresh();
80
+ this.resolver = resolver;
81
+ this.lastOnboardingError = void 0;
82
+ return resolver;
83
+ })().catch((error) => {
84
+ this.lastOnboardingError = error;
85
+ throw error;
86
+ }).finally(() => {
87
+ this.onboarding = void 0;
88
+ });
89
+ return this.onboarding;
90
+ }
91
+ async resolve(keyid) {
92
+ return (await this.confirmedResolver()).resolve(keyid);
93
+ }
94
+ async resolveWithMetadata(keyid) {
95
+ return (await this.confirmedResolver()).resolveWithMetadata(keyid);
71
96
  }
72
- /**
73
- * The agent URL we resolved `jwks_uri` from. Populated after the first
74
- * successful refresh; useful for verifier result attribution
75
- * (`VerifyWebhookOptions.agentUrlForKeyid`).
76
- */
77
97
  get agentUrl() {
78
- return this.snapshot?.agentUrl;
98
+ return this.resolver?.resolvedAgentUrl;
79
99
  }
80
- /** Force a refetch of both brand.json and the inner JWKS, bypassing the cooldown. */
81
100
  async forceRefresh() {
82
- this.snapshot = void 0;
83
- this.inner = void 0;
84
- await this.refresh();
85
- }
86
- async refresh() {
87
- if (this.inFlight) {
88
- await this.inFlight;
89
- return;
90
- }
91
- this.inFlight = this.doRefresh().finally(() => {
92
- this.inFlight = void 0;
93
- });
94
- await this.inFlight;
101
+ if (this.resolver) await this.resolver.forceRefresh();
102
+ else await this.confirmedResolver();
95
103
  }
96
- async doRefresh() {
97
- const fetched = await fetchBrandJson({
98
- startUrl: this.url,
99
- currentEtag: this.snapshot?.etag,
100
- maxRedirects: this.maxRedirects,
101
- allowPrivateIp: this.allowPrivateIp,
102
- lookup: this.lookup
103
- });
104
- if (fetched.status === "not_modified" && this.snapshot) {
105
- this.snapshot = {
106
- ...this.snapshot,
107
- ...fetched.etag && { etag: fetched.etag },
108
- fetchedAt: this.now(),
109
- expiresAt: this.now() + computeLifetime(fetched.cacheControl, this.maxAge)
110
- };
111
- return;
104
+ }
105
+ function inferOnboardingAgentUrl(document, selector) {
106
+ const record = document;
107
+ const candidates = (agents) => !Array.isArray(agents) ? [] : agents.filter(
108
+ (entry) => entry?.type === selector.agentType && (selector.agentId === void 0 || entry.id === selector.agentId) && typeof entry.url === "string"
109
+ ).flatMap((entry) => {
110
+ try {
111
+ return [canonicalAgentUrl(entry.url)];
112
+ } catch {
113
+ return [];
112
114
  }
113
- const agent = selectAgent(fetched.data, fetched.finalUrl, this.selector);
114
- if (!this.inner || this.snapshot?.jwksUri !== agent.jwksUri) {
115
- this.inner = new HttpsJwksResolver(agent.jwksUri, {
116
- ...this.jwksOptions,
117
- allowPrivateIp: this.allowPrivateIp,
118
- lookup: this.lookup,
119
- now: this.now
120
- });
115
+ });
116
+ let urls;
117
+ if (isPortfolioHouse(record.house)) {
118
+ const house = record.house;
119
+ urls = [];
120
+ if (selector.brandId !== void 0 && Array.isArray(record.brands)) {
121
+ const brands = record.brands.filter((brand) => brand?.id === selector.brandId);
122
+ if (brands.length > 1)
123
+ throw new BrandJsonResolverError("agent_ambiguous", "Onboarding brand selector is ambiguous.");
124
+ urls = candidates(brands[0]?.agents);
121
125
  }
122
- this.snapshot = {
123
- jwksUri: agent.jwksUri,
124
- agentUrl: agent.url,
125
- ...fetched.etag && { etag: fetched.etag },
126
- fetchedAt: this.now(),
127
- expiresAt: this.now() + computeLifetime(fetched.cacheControl, this.maxAge)
128
- };
129
- }
126
+ if (urls.length === 0) urls = candidates(house.agents);
127
+ } else urls = candidates(record.agents);
128
+ const unique = [...new Set(urls)];
129
+ if (unique.length !== 1)
130
+ throw new BrandJsonResolverError(
131
+ unique.length === 0 ? "agent_not_found" : "agent_ambiguous",
132
+ "Onboarding must identify exactly one agent URL; supply agentUrl explicitly."
133
+ );
134
+ return unique[0];
130
135
  }
131
136
  const MAX_BRAND_JSON_TIMEOUT_MS = 1e4;
132
137
  const MAX_BRAND_JSON_BODY_BYTES = 262144;
@@ -147,6 +152,8 @@ async function fetchBrandJson(args) {
147
152
  const allowPrivateIp = args.allowPrivateIp === true;
148
153
  const seen = /* @__PURE__ */ new Set();
149
154
  let url = canonicalizeUrl(args.startUrl, allowPrivateIp);
155
+ const initial = new URL(url);
156
+ let httpRedirects = 0;
150
157
  for (let hop = 0; hop <= maxRedirects; hop++) {
151
158
  if (seen.has(url)) {
152
159
  throw new BrandJsonResolverError("redirect_loop", `brand.json redirect loop detected`);
@@ -169,6 +176,19 @@ async function fetchBrandJson(args) {
169
176
  } catch (cause) {
170
177
  throw new BrandJsonResolverError("fetch_failed", "Unable to fetch brand.json", { cause });
171
178
  }
179
+ if (args.domainDerived && hop === 0 && [301, 302, 303, 307, 308].includes(res.status)) {
180
+ const location = res.headers.location;
181
+ if (!location || httpRedirects++ >= 3)
182
+ throw new BrandJsonResolverError("fetch_failed", "Invalid domain-derived redirect");
183
+ const target = new URL(location, url);
184
+ const baseHost = initial.hostname.startsWith("www.") ? initial.hostname.slice(4) : initial.hostname;
185
+ if (target.hostname !== baseHost && target.hostname !== `www.${baseHost}` || target.port !== initial.port || target.protocol !== initial.protocol || target.username || target.password) {
186
+ throw new BrandJsonResolverError("invalid_url", "brand.json redirect leaves its original host/www boundary");
187
+ }
188
+ url = canonicalizeUrl(target.href, allowPrivateIp);
189
+ hop--;
190
+ continue;
191
+ }
172
192
  if (hop === 0 && res.status === 304) {
173
193
  return {
174
194
  status: "not_modified",
@@ -186,7 +206,7 @@ async function fetchBrandJson(args) {
186
206
  const text = Buffer.from(res.body).toString("utf8");
187
207
  let parsed;
188
208
  try {
189
- parsed = JSON.parse(text);
209
+ parsed = parseStrictJson(text);
190
210
  } catch {
191
211
  throw new BrandJsonResolverError("invalid_body", `brand.json response is not valid JSON`);
192
212
  }
@@ -278,88 +298,6 @@ function assertBrandJsonShape(obj) {
278
298
  }
279
299
  }
280
300
  }
281
- function selectAgent(data, finalBrandUrl, selector) {
282
- if (!data || typeof data !== "object") {
283
- throw new BrandJsonResolverError(
284
- "agent_not_found",
285
- `brand.json has no agent matching ${describeSelector(selector)}`
286
- );
287
- }
288
- const obj = data;
289
- let picked;
290
- if (isPortfolioHouse(obj.house)) {
291
- const house = obj.house;
292
- if (selector.brandId !== void 0) {
293
- const brands = Array.isArray(obj.brands) ? obj.brands : [];
294
- const brand = brands.find((b) => b && b.id === selector.brandId);
295
- if (brand) picked = pickAgent(brand.agents, finalBrandUrl, selector);
296
- }
297
- picked ??= pickAgent(house.agents, finalBrandUrl, selector);
298
- } else {
299
- picked = pickAgent(obj.agents, finalBrandUrl, selector);
300
- }
301
- if (!picked) {
302
- throw new BrandJsonResolverError(
303
- "agent_not_found",
304
- `brand.json has no agent matching ${describeSelector(selector)}`
305
- );
306
- }
307
- return picked;
308
- }
309
- function pickAgent(agents, finalBrandUrl, selector) {
310
- if (!Array.isArray(agents)) return void 0;
311
- const matches = agents.filter((a) => {
312
- if (!a || typeof a !== "object") return false;
313
- const e = a;
314
- if (e.type !== selector.agentType) return false;
315
- if (selector.agentId !== void 0 && e.id !== selector.agentId) return false;
316
- return typeof e.url === "string";
317
- });
318
- if (matches.length === 0) return void 0;
319
- if (matches.length > 1 && selector.agentId === void 0) {
320
- throw new BrandJsonResolverError(
321
- "agent_ambiguous",
322
- `brand.json declares ${matches.length} agents of type "${selector.agentType}"; pass \`agentId\` to disambiguate (choices: ${matches.map((m) => m.id ?? "<no-id>").join(", ")})`
323
- );
324
- }
325
- const agent = matches[0];
326
- const url = agent.url;
327
- const jwksUri = agent.jwks_uri ?? defaultJwksUri(url, finalBrandUrl);
328
- return { url, jwksUri };
329
- }
330
- function defaultJwksUri(agentUrl, finalBrandUrl) {
331
- let agent;
332
- try {
333
- agent = new URL(agentUrl);
334
- } catch {
335
- throw new BrandJsonResolverError("invalid_url", `agent.url is not a valid URL`);
336
- }
337
- const brand = new URL(finalBrandUrl);
338
- if (agent.origin !== brand.origin) {
339
- throw new BrandJsonResolverError(
340
- "jwks_origin_mismatch",
341
- `agent.url origin (${agent.origin}) does not match brand.json origin (${brand.origin}); publisher must declare an explicit jwks_uri for cross-origin agents`
342
- );
343
- }
344
- return `${agent.origin}/.well-known/jwks.json`;
345
- }
346
- function describeSelector(selector) {
347
- const parts = [`type=${selector.agentType}`];
348
- if (selector.agentId !== void 0) parts.push(`id=${selector.agentId}`);
349
- if (selector.brandId !== void 0) parts.push(`brand=${selector.brandId}`);
350
- return parts.join(" ");
351
- }
352
- function computeLifetime(cacheControl, maxAge) {
353
- if (!cacheControl) return maxAge;
354
- const lower = cacheControl.toLowerCase();
355
- if (/\bno-store\b|\bno-cache\b/.test(lower)) return 0;
356
- const match = /max-age\s*=\s*(\d+)/.exec(lower);
357
- if (match) {
358
- const serverMax = Number(match[1]);
359
- if (Number.isFinite(serverMax)) return Math.min(serverMax, maxAge);
360
- }
361
- return maxAge;
362
- }
363
301
  export {
364
302
  BrandJsonJwksResolver,
365
303
  BrandJsonResolverError,
@@ -1,12 +1,14 @@
1
1
  import { ADCPError } from '../errors/index.mjs';
2
2
  import type { RequestSigningErrorCode } from '../types/core.generated.mjs';
3
- export { RequestSigningErrorCodeMetadata } from '../types/enums.generated.mjs';
3
+ import { RequestSigningErrorCodeMetadata } from '../types/enums.generated.mjs';
4
+ export { RequestSigningErrorCodeMetadata };
4
5
  /** Canonical rc.6 request-signing transport vocabulary. */
5
6
  export type RequestSignatureErrorCode = RequestSigningErrorCode;
6
7
  export declare class RequestSignatureError extends ADCPError {
7
8
  readonly code: RequestSignatureErrorCode;
8
9
  readonly failedStep: number;
9
- constructor(code: RequestSignatureErrorCode, failedStep: number, message: string, details?: unknown);
10
+ readonly recovery: 'transient' | 'correctable' | 'terminal';
11
+ constructor(code: RequestSignatureErrorCode, failedStep: number, message: string, details?: unknown, recovery?: 'transient' | 'correctable' | 'terminal');
10
12
  }
11
13
  /**
12
14
  * Error codes surfaced by the RFC 9421 webhook-signing verifier. Maps onto
@@ -24,7 +26,9 @@ export type WebhookSignatureErrorCode = 'webhook_signature_header_malformed' | '
24
26
  export declare class WebhookSignatureError extends ADCPError {
25
27
  readonly code: WebhookSignatureErrorCode;
26
28
  readonly failedStep: number;
27
- constructor(code: WebhookSignatureErrorCode, failedStep: number, message: string, details?: unknown);
29
+ /** Local retry signal; the wire error code stays in the webhook vocabulary. */
30
+ readonly retryable: boolean;
31
+ constructor(code: WebhookSignatureErrorCode, failedStep: number, message: string, details?: unknown, retryable?: boolean);
28
32
  }
29
33
  export type ResponseSignatureErrorCode = 'response_signature_key_purpose_invalid';
30
34
  export declare class ResponseSignatureError extends ADCPError {
@@ -1,12 +1,14 @@
1
1
  import { ADCPError } from '../errors';
2
2
  import type { RequestSigningErrorCode } from '../types/core.generated';
3
- export { RequestSigningErrorCodeMetadata } from '../types/enums.generated';
3
+ import { RequestSigningErrorCodeMetadata } from '../types/enums.generated';
4
+ export { RequestSigningErrorCodeMetadata };
4
5
  /** Canonical rc.6 request-signing transport vocabulary. */
5
6
  export type RequestSignatureErrorCode = RequestSigningErrorCode;
6
7
  export declare class RequestSignatureError extends ADCPError {
7
8
  readonly code: RequestSignatureErrorCode;
8
9
  readonly failedStep: number;
9
- constructor(code: RequestSignatureErrorCode, failedStep: number, message: string, details?: unknown);
10
+ readonly recovery: 'transient' | 'correctable' | 'terminal';
11
+ constructor(code: RequestSignatureErrorCode, failedStep: number, message: string, details?: unknown, recovery?: 'transient' | 'correctable' | 'terminal');
10
12
  }
11
13
  /**
12
14
  * Error codes surfaced by the RFC 9421 webhook-signing verifier. Maps onto
@@ -24,7 +26,9 @@ export type WebhookSignatureErrorCode = 'webhook_signature_header_malformed' | '
24
26
  export declare class WebhookSignatureError extends ADCPError {
25
27
  readonly code: WebhookSignatureErrorCode;
26
28
  readonly failedStep: number;
27
- constructor(code: WebhookSignatureErrorCode, failedStep: number, message: string, details?: unknown);
29
+ /** Local retry signal; the wire error code stays in the webhook vocabulary. */
30
+ readonly retryable: boolean;
31
+ constructor(code: WebhookSignatureErrorCode, failedStep: number, message: string, details?: unknown, retryable?: boolean);
28
32
  }
29
33
  export type ResponseSignatureErrorCode = 'response_signature_key_purpose_invalid';
30
34
  export declare class ResponseSignatureError extends ADCPError {
@@ -30,19 +30,24 @@ var import_enums = require('../types/enums.generated.js');
30
30
  class RequestSignatureError extends import_errors.ADCPError {
31
31
  code;
32
32
  failedStep;
33
- constructor(code, failedStep, message, details) {
33
+ recovery;
34
+ constructor(code, failedStep, message, details, recovery) {
34
35
  super(message, details);
35
36
  this.code = code;
36
37
  this.failedStep = failedStep;
38
+ this.recovery = recovery ?? import_enums.RequestSigningErrorCodeMetadata[code].recovery;
37
39
  }
38
40
  }
39
41
  class WebhookSignatureError extends import_errors.ADCPError {
40
42
  code;
41
43
  failedStep;
42
- constructor(code, failedStep, message, details) {
44
+ /** Local retry signal; the wire error code stays in the webhook vocabulary. */
45
+ retryable;
46
+ constructor(code, failedStep, message, details, retryable = false) {
43
47
  super(message, details);
44
48
  this.code = code;
45
49
  this.failedStep = failedStep;
50
+ this.retryable = retryable;
46
51
  }
47
52
  }
48
53
  class ResponseSignatureError extends import_errors.ADCPError {
@@ -3,19 +3,24 @@ import { RequestSigningErrorCodeMetadata } from "../types/enums.generated.mjs";
3
3
  class RequestSignatureError extends ADCPError {
4
4
  code;
5
5
  failedStep;
6
- constructor(code, failedStep, message, details) {
6
+ recovery;
7
+ constructor(code, failedStep, message, details, recovery) {
7
8
  super(message, details);
8
9
  this.code = code;
9
10
  this.failedStep = failedStep;
11
+ this.recovery = recovery ?? RequestSigningErrorCodeMetadata[code].recovery;
10
12
  }
11
13
  }
12
14
  class WebhookSignatureError extends ADCPError {
13
15
  code;
14
16
  failedStep;
15
- constructor(code, failedStep, message, details) {
17
+ /** Local retry signal; the wire error code stays in the webhook vocabulary. */
18
+ retryable;
19
+ constructor(code, failedStep, message, details, retryable = false) {
16
20
  super(message, details);
17
21
  this.code = code;
18
22
  this.failedStep = failedStep;
23
+ this.retryable = retryable;
19
24
  }
20
25
  }
21
26
  class ResponseSignatureError extends ADCPError {
@@ -15,6 +15,10 @@ export interface HttpsJwksResolverOptions {
15
15
  * longer. Default 3600s (1 hour).
16
16
  */
17
17
  maxAgeSeconds?: number;
18
+ /** Protocol cache floor. Defaults to 0; governance uses 60 seconds. */
19
+ minCacheAgeSeconds?: number;
20
+ /** Reject refresh failures instead of serving an expired snapshot. Default false. */
21
+ failClosed?: boolean;
18
22
  /** Allow `http://` / private-IP JWKS URLs (dev loops only). Default false. */
19
23
  allowPrivateIp?: boolean;
20
24
  /** DNS resolver forwarded to the SSRF-safe fetch path. Defaults to `dns/promises.lookup`. */
@@ -45,11 +49,14 @@ export declare class HttpsJwksResolver implements JwksResolver {
45
49
  private readonly url;
46
50
  private readonly minCooldown;
47
51
  private readonly maxAge;
52
+ private readonly minCacheAge;
53
+ private readonly failClosed;
48
54
  private readonly allowPrivateIp;
49
55
  private readonly lookup;
50
56
  private readonly now;
51
57
  private cache;
52
58
  private inFlight;
59
+ private lastAttemptAt;
53
60
  constructor(url: string, options?: HttpsJwksResolverOptions);
54
61
  resolve(keyid: string): Promise<AdcpJsonWebKey | null>;
55
62
  /**
@@ -15,6 +15,10 @@ export interface HttpsJwksResolverOptions {
15
15
  * longer. Default 3600s (1 hour).
16
16
  */
17
17
  maxAgeSeconds?: number;
18
+ /** Protocol cache floor. Defaults to 0; governance uses 60 seconds. */
19
+ minCacheAgeSeconds?: number;
20
+ /** Reject refresh failures instead of serving an expired snapshot. Default false. */
21
+ failClosed?: boolean;
18
22
  /** Allow `http://` / private-IP JWKS URLs (dev loops only). Default false. */
19
23
  allowPrivateIp?: boolean;
20
24
  /** DNS resolver forwarded to the SSRF-safe fetch path. Defaults to `dns/promises.lookup`. */
@@ -45,11 +49,14 @@ export declare class HttpsJwksResolver implements JwksResolver {
45
49
  private readonly url;
46
50
  private readonly minCooldown;
47
51
  private readonly maxAge;
52
+ private readonly minCacheAge;
53
+ private readonly failClosed;
48
54
  private readonly allowPrivateIp;
49
55
  private readonly lookup;
50
56
  private readonly now;
51
57
  private cache;
52
58
  private inFlight;
59
+ private lastAttemptAt;
53
60
  constructor(url: string, options?: HttpsJwksResolverOptions);
54
61
  resolve(keyid: string): Promise<AdcpJsonWebKey | null>;
55
62
  /**
@@ -22,40 +22,61 @@ __export(jwks_https_exports, {
22
22
  });
23
23
  module.exports = __toCommonJS(jwks_https_exports);
24
24
  var import_net = require('../net/index.js');
25
+ var import_strict_json = require('./agent-resolver/strict-json.js');
25
26
  const DEFAULT_MIN_COOLDOWN_SECONDS = 30;
26
27
  const DEFAULT_MAX_AGE_SECONDS = 3600;
27
28
  class HttpsJwksResolver {
28
29
  url;
29
30
  minCooldown;
30
31
  maxAge;
32
+ minCacheAge;
33
+ failClosed;
31
34
  allowPrivateIp;
32
35
  lookup;
33
36
  now;
34
37
  cache;
35
38
  inFlight;
39
+ lastAttemptAt = Number.NEGATIVE_INFINITY;
36
40
  constructor(url, options = {}) {
37
41
  this.url = url;
38
42
  this.minCooldown = options.minCooldownSeconds ?? DEFAULT_MIN_COOLDOWN_SECONDS;
39
43
  this.maxAge = options.maxAgeSeconds ?? DEFAULT_MAX_AGE_SECONDS;
44
+ this.minCacheAge = Math.min(options.minCacheAgeSeconds ?? 0, this.maxAge);
45
+ if (!Number.isFinite(this.maxAge) || this.maxAge < 0) {
46
+ throw new TypeError("maxAgeSeconds must be a finite non-negative number");
47
+ }
48
+ if (!Number.isFinite(this.minCooldown) || this.minCooldown < 0) {
49
+ throw new TypeError("minCooldownSeconds must be a finite non-negative number");
50
+ }
51
+ if (!Number.isFinite(options.minCacheAgeSeconds ?? 0) || (options.minCacheAgeSeconds ?? 0) < 0) {
52
+ throw new TypeError("minCacheAgeSeconds must be a finite non-negative number");
53
+ }
54
+ this.failClosed = options.failClosed === true;
40
55
  this.allowPrivateIp = options.allowPrivateIp ?? false;
41
56
  this.lookup = options.lookup;
42
57
  this.now = options.now ?? (() => Math.floor(Date.now() / 1e3));
43
58
  }
44
59
  async resolve(keyid) {
45
60
  if (!this.cache) {
61
+ if (!this.inFlight && this.now() - this.lastAttemptAt < this.minCooldown) {
62
+ throw new Error("JWKS lookup is in its retry cooldown");
63
+ }
46
64
  await this.refresh();
47
65
  } else if (this.cache.keys.has(keyid)) {
48
- if (this.now() <= this.cache.expiresAt) {
66
+ if (this.now() < this.cache.expiresAt) {
49
67
  return this.cache.keys.get(keyid) ?? null;
50
68
  }
51
- if (this.now() - this.cache.fetchedAt >= this.minCooldown) {
52
- await this.refresh().catch(() => {
69
+ if (this.now() - this.lastAttemptAt >= this.minCooldown) {
70
+ await this.refresh().catch((error) => {
71
+ if (this.failClosed) throw error;
53
72
  });
54
73
  }
55
- } else if (this.now() - this.cache.fetchedAt >= this.minCooldown) {
56
- await this.refresh().catch(() => {
74
+ } else if (this.now() - this.lastAttemptAt >= this.minCooldown) {
75
+ await this.refresh().catch((error) => {
76
+ if (this.failClosed) throw error;
57
77
  });
58
78
  }
79
+ if (this.failClosed && this.cache && this.now() >= this.cache.expiresAt) throw new Error("JWKS snapshot expired");
59
80
  return this.cache?.keys.get(keyid) ?? null;
60
81
  }
61
82
  /**
@@ -72,6 +93,7 @@ class HttpsJwksResolver {
72
93
  await this.inFlight;
73
94
  return;
74
95
  }
96
+ this.lastAttemptAt = this.now();
75
97
  this.inFlight = this.doRefresh().finally(() => {
76
98
  this.inFlight = void 0;
77
99
  });
@@ -103,7 +125,7 @@ class HttpsJwksResolver {
103
125
  const text = Buffer.from(res.body).toString("utf8");
104
126
  let parsed;
105
127
  try {
106
- parsed = JSON.parse(text);
128
+ parsed = (0, import_strict_json.parseStrictJson)(text);
107
129
  } catch {
108
130
  throw new Error(`JWKS fetch ${this.url} returned non-JSON body`);
109
131
  }
@@ -118,6 +140,7 @@ class HttpsJwksResolver {
118
140
  for (const entry of keysField) {
119
141
  if (entry && typeof entry === "object" && typeof entry.kid === "string") {
120
142
  const jwk = entry;
143
+ if (byKid.has(jwk.kid)) throw new Error("JWKS has duplicate key identifiers");
121
144
  byKid.set(jwk.kid, jwk);
122
145
  }
123
146
  }
@@ -130,11 +153,11 @@ class HttpsJwksResolver {
130
153
  }
131
154
  computeExpiry(headers) {
132
155
  const cacheCtl = headers["cache-control"]?.toLowerCase() ?? "";
133
- if (/\bno-store\b|\bno-cache\b/.test(cacheCtl)) return this.now();
156
+ if (/\bno-store\b|\bno-cache\b/.test(cacheCtl)) return this.now() + this.minCacheAge;
134
157
  const match = /max-age\s*=\s*(\d+)/.exec(cacheCtl);
135
158
  if (match) {
136
159
  const serverMax = Number(match[1]);
137
- if (Number.isFinite(serverMax)) return this.now() + Math.min(serverMax, this.maxAge);
160
+ if (Number.isFinite(serverMax)) return this.now() + Math.max(this.minCacheAge, Math.min(serverMax, this.maxAge));
138
161
  }
139
162
  return this.now() + this.maxAge;
140
163
  }