@webpieces/http-client-core 0.4.795 → 0.4.797

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webpieces/http-client-core",
3
- "version": "0.4.795",
3
+ "version": "0.4.797",
4
4
  "description": "Isomorphic core of the webpieces HTTP client: the decorator-driven ProxyClient, error translation, and the Proxy trap shared by http-client-node and http-client-browser",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -21,6 +21,6 @@
21
21
  "access": "public"
22
22
  },
23
23
  "dependencies": {
24
- "@webpieces/core-util": "0.4.795"
24
+ "@webpieces/core-util": "0.4.797"
25
25
  }
26
26
  }
@@ -42,7 +42,7 @@ export declare class ClientRequest {
42
42
  readonly requestDto: unknown;
43
43
  /**
44
44
  * Encoded route path + query for this call, resolved from its typed arguments. May be `''`
45
- * for a contract declaring `@ApiPath('')` + `@Endpoint('', ...)`, in which case the url is
45
+ * for a contract declaring `@ApiPath('')` + `@Endpoint(POST, '', ...)`, in which case the url is
46
46
  * the base URL byte for byte.
47
47
  */
48
48
  readonly resolvedPath: string;
@@ -88,7 +88,7 @@ export declare class ClientRequest {
88
88
  requestDto: unknown,
89
89
  /**
90
90
  * Encoded route path + query for this call, resolved from its typed arguments. May be `''`
91
- * for a contract declaring `@ApiPath('')` + `@Endpoint('', ...)`, in which case the url is
91
+ * for a contract declaring `@ApiPath('')` + `@Endpoint(POST, '', ...)`, in which case the url is
92
92
  * the base URL byte for byte.
93
93
  */
94
94
  resolvedPath?: string);
@@ -77,7 +77,7 @@ class ClientRequest {
77
77
  requestDto,
78
78
  /**
79
79
  * Encoded route path + query for this call, resolved from its typed arguments. May be `''`
80
- * for a contract declaring `@ApiPath('')` + `@Endpoint('', ...)`, in which case the url is
80
+ * for a contract declaring `@ApiPath('')` + `@Endpoint(POST, '', ...)`, in which case the url is
81
81
  * the base URL byte for byte.
82
82
  */
83
83
  resolvedPath = route.path) {
@@ -1 +1 @@
1
- {"version":3,"file":"ClientRequest.js","sourceRoot":"","sources":["../../../../../packages/http/http-client-core/src/ClientRequest.ts"],"names":[],"mappings":";;;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAa,aAAa;IAgCF;IAEA;IAGA;IAET;IAOS;IAMA;IAnDpB,8EAA8E;IACtE,cAAc,CAAS;IAE/B,2DAA2D;IACnD,UAAU,CAAS;IAE3B;;;;;;;;;;;;OAYG;IACK,SAAS,GAAG,KAAK,CAAC;IAE1B;;;;;OAKG;IACH,eAAe,GAAG,IAAI,CAAC;IAEvB;IACI,qEAAqE;IACrD,KAAoB;IACpC,sFAAsF;IACtE,YAAoB,EACpC,OAAe;IACf,+FAA+F;IAC/E,OAA4B;IAC5C,mFAAmF;IAC5E,IAAwB;IAC/B;;;;OAIG;IACH,6FAA6F;IAC7E,UAAmB;IACnC;;;;OAIG;IACa,eAAuB,KAAK,CAAC,IAAI;QApBjC,UAAK,GAAL,KAAK,CAAe;QAEpB,iBAAY,GAAZ,YAAY,CAAQ;QAGpB,YAAO,GAAP,OAAO,CAAqB;QAErC,SAAI,GAAJ,IAAI,CAAoB;QAOf,eAAU,GAAV,UAAU,CAAS;QAMnB,iBAAY,GAAZ,YAAY,CAAqB;QAEjD,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC;QAC9B,IAAI,CAAC,UAAU,GAAG,GAAG,OAAO,GAAG,YAAY,EAAE,CAAC;IAClD,CAAC;IAED,kEAAkE;IAClE,IAAI,OAAO;QACP,OAAO,IAAI,CAAC,cAAc,CAAC;IAC/B,CAAC;IAED,kDAAkD;IAClD,IAAI,GAAG;QACH,OAAO,IAAI,CAAC,UAAU,CAAC;IAC3B,CAAC;IAED;;;OAGG;IACH,IAAI,uBAAuB;QACvB,OAAO,IAAI,CAAC,SAAS,CAAC;IAC1B,CAAC;IAED;;;;;;;;;OASG;IACH,cAAc,CAAC,OAAe;QAC1B,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC;QAC9B,IAAI,CAAC,UAAU,GAAG,GAAG,OAAO,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;QACnD,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;IAC1B,CAAC;IAED;;;;;;;;;;;;;;;;OAgBG;IACH,cAAc,CAAC,OAAe;QAC1B,IAAI,CAAC,cAAc,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC;QAC9C,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC;QAC1B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;IAC1B,CAAC;IAED;;;;;;;;OAQG;IACH,gBAAgB,CAAC,WAAmB;QAChC,IAAI,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC;IACrC,CAAC;IAED,oDAAoD;IACpD,eAAe;QACX,MAAM,MAAM,GAA2B,EAAE,CAAC;QAC1C,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;YACzC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QAChC,CAAC;QACD,OAAO,MAAM,CAAC;IAClB,CAAC;CACJ;AAxID,sCAwIC","sourcesContent":["import { RouteMetadata } from '@webpieces/core-util';\n\n/**\n * The OUTBOUND request as the client filter chain sees it — the client-side twin of the server's\n * `MethodMeta`, and the `REQ` of every `Filter<ClientRequest, Response>`.\n *\n * MUTABLE on purpose, and that is the whole point of the chain: a filter re-points the URL, adds a\n * header, or replaces the serialized bytes, and whatever this object holds when the chain bottoms\n * out is EXACTLY what goes on the wire.\n *\n * ## Why the URL is behind mutators instead of two public fields\n *\n * A call has both a base URL (the host we are talking to — the OIDC audience, the thing an SSRF\n * policy judges) and a full URL (base + this route's path, or an absolute Location we were\n * redirected to). Exposing both as writable fields makes it possible for them to disagree, and a\n * base URL that disagrees with the URL actually fetched is precisely the shape of an SSRF bypass.\n * So both are private, and the only ways to move this request are {@link pointAtBaseUrl},\n * {@link pointAtFullUrl} and {@link followRedirectTo}, each of which sets BOTH consistently.\n *\n * ## Signing\n *\n * {@link body} holds the serialized bytes, not the DTO — serialization happens BEFORE the chain\n * runs. A signing filter computes its HMAC over `request.body` and adds a header, and the transport\n * sends `request.body` verbatim, so the bytes signed and the bytes sent cannot differ. That was\n * impossible while a client owned serialization internally with no seam, which is why outbound\n * webhook senders were forced back to hand-rolling `JSON.stringify` and a raw HTTP library — a\n * library that re-serializes internally would sign one byte sequence and send another.\n */\nexport class ClientRequest {\n /** The base URL half of {@link url} — the host, without this route's path. */\n private currentBaseUrl: string;\n\n /** The absolute URL this call will actually be sent to. */\n private currentUrl: string;\n\n /**\n * FALSE while this request still points where the client's own configuration put it; TRUE the\n * moment anything moved it — {@link pointAtBaseUrl}, {@link pointAtFullUrl} or {@link followRedirectTo}.\n *\n * This flag is what makes the SSRF guard automatic AND free. A URL that came out of\n * `ClientRegistry` is an address WE chose, so judging it would mean resolving DNS on every\n * internal RPC in order to re-derive a fact we already know. A URL a filter substituted is not:\n * it arrived from outside the call — a partner-editable row, an OAuth callback, a `Location`\n * header — and that is precisely the input an SSRF policy exists to judge.\n *\n * So the trigger is the ACT of re-pointing rather than a per-client setting, which means no app\n * can forget to switch the guard on and none can switch it off by omitting an argument.\n */\n private rePointed = false;\n\n /**\n * Whether the transport may follow a 3xx itself. The SSRF guard sets this FALSE so it can read\n * the `Location`, judge it under the same policy as the original URL, and only then re-invoke\n * the chain — a partner URL that 302s must not be able to bounce our POST at an internal\n * address.\n */\n followRedirects = true;\n\n constructor(\n /** The route being called — its path, http method, and auth mode. */\n public readonly route: RouteMetadata,\n /** The API contract's class name, e.g. 'PartnerWebhookApi'. For messages and logs. */\n public readonly contractName: string,\n baseUrl: string,\n /** Outbound headers. Mutable: a filter adds its signature/idempotency/tracing headers here. */\n public readonly headers: Map<string, string>,\n /** The EXACT serialized request body, or undefined for a call with no argument. */\n public body: string | undefined,\n /**\n * The request DTO the caller passed, BEFORE serialization. Read-only, and deliberately not\n * the thing that gets sent: a filter that wants to change what goes on the wire changes\n * {@link body}, so there is never a question of which of the two won.\n */\n // webpieces-disable no-any-unknown -- the request DTO's type is erased at the proxy boundary\n public readonly requestDto: unknown,\n /**\n * Encoded route path + query for this call, resolved from its typed arguments. May be `''`\n * for a contract declaring `@ApiPath('')` + `@Endpoint('', ...)`, in which case the url is\n * the base URL byte for byte.\n */\n public readonly resolvedPath: string = route.path,\n ) {\n this.currentBaseUrl = baseUrl;\n this.currentUrl = `${baseUrl}${resolvedPath}`;\n }\n\n /** The host this call is currently addressed to, with no path. */\n get baseUrl(): string {\n return this.currentBaseUrl;\n }\n\n /** The absolute URL this call will be sent to. */\n get url(): string {\n return this.currentUrl;\n }\n\n /**\n * TRUE once anything has moved this request off the address the client resolved for itself —\n * i.e. once the destination is DATA rather than a peer we named. See {@link rePointed}.\n */\n get destinationCameFromData(): boolean {\n return this.rePointed;\n }\n\n /**\n * Re-point this ONE call at another host, keeping this route's path. `ContextBaseUrlFilter`\n * calls this with the URL a partner registered; nothing here is remembered by the client, so\n * the next call through the same client starts from its configured host again.\n *\n * It also flips {@link destinationCameFromData}, which is what arms the SSRF guard sitting\n * below every app filter. That coupling is deliberate and lives HERE rather than in the guard:\n * a filter cannot move the request without saying so, because moving it is only possible\n * through this method.\n */\n pointAtBaseUrl(baseUrl: string): void {\n this.currentBaseUrl = baseUrl;\n this.currentUrl = `${baseUrl}${this.resolvedPath}`;\n this.rePointed = true;\n }\n\n /**\n * Re-point this ONE call at a COMPLETE url — host, path and query — used VERBATIM. The contract's\n * path (and any `@QueryParam` values resolved into it) is NOT appended and no separator is\n * inserted, because the url is the whole destination: a partner webhook row stores\n * `https://hooks.partner.example/in/abc?token=xyz`, and that string, byte for byte, is what must\n * be dialled. `ContextFullUrlFilter` calls this with `WebpiecesCoreHeaders.OVERRIDE_FULL_URL`.\n *\n * The base URL becomes that url's origin, so a policy that judges hosts (and the OIDC audience)\n * sees the host we are actually about to talk to. Flips {@link destinationCameFromData} for the\n * same reason {@link pointAtBaseUrl} does: the url is data, not an address we chose, so the SSRF\n * guard is armed by this call and cannot be forgotten.\n *\n * Compare {@link pointAtBaseUrl}, which keeps this route's path: that is for a partner that\n * implements OUR contract at their own host; this is for a partner that gave us one opaque url.\n *\n * @throws TypeError if `fullUrl` is not a parseable absolute URL.\n */\n pointAtFullUrl(fullUrl: string): void {\n this.currentBaseUrl = new URL(fullUrl).origin;\n this.currentUrl = fullUrl;\n this.rePointed = true;\n }\n\n /**\n * Follow a redirect to an ABSOLUTE url. A `Location` is a complete destination, so this is\n * exactly {@link pointAtFullUrl} — one implementation, so the two can never disagree about what\n * the base URL becomes or whether the SSRF guard is armed (it is: a `Location` header is the far\n * end's data, not an address we chose). Kept as its own name because the CALL SITE reads\n * differently: the guard is following a hop, not an app choosing a destination.\n *\n * @throws TypeError if `absoluteUrl` is not a parseable absolute URL.\n */\n followRedirectTo(absoluteUrl: string): void {\n this.pointAtFullUrl(absoluteUrl);\n }\n\n /** The headers in the shape the transport wants. */\n headersAsRecord(): Record<string, string> {\n const record: Record<string, string> = {};\n for (const entry of this.headers.entries()) {\n record[entry[0]] = entry[1];\n }\n return record;\n }\n}\n"]}
1
+ {"version":3,"file":"ClientRequest.js","sourceRoot":"","sources":["../../../../../packages/http/http-client-core/src/ClientRequest.ts"],"names":[],"mappings":";;;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAa,aAAa;IAgCF;IAEA;IAGA;IAET;IAOS;IAMA;IAnDpB,8EAA8E;IACtE,cAAc,CAAS;IAE/B,2DAA2D;IACnD,UAAU,CAAS;IAE3B;;;;;;;;;;;;OAYG;IACK,SAAS,GAAG,KAAK,CAAC;IAE1B;;;;;OAKG;IACH,eAAe,GAAG,IAAI,CAAC;IAEvB;IACI,qEAAqE;IACrD,KAAoB;IACpC,sFAAsF;IACtE,YAAoB,EACpC,OAAe;IACf,+FAA+F;IAC/E,OAA4B;IAC5C,mFAAmF;IAC5E,IAAwB;IAC/B;;;;OAIG;IACH,6FAA6F;IAC7E,UAAmB;IACnC;;;;OAIG;IACa,eAAuB,KAAK,CAAC,IAAI;QApBjC,UAAK,GAAL,KAAK,CAAe;QAEpB,iBAAY,GAAZ,YAAY,CAAQ;QAGpB,YAAO,GAAP,OAAO,CAAqB;QAErC,SAAI,GAAJ,IAAI,CAAoB;QAOf,eAAU,GAAV,UAAU,CAAS;QAMnB,iBAAY,GAAZ,YAAY,CAAqB;QAEjD,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC;QAC9B,IAAI,CAAC,UAAU,GAAG,GAAG,OAAO,GAAG,YAAY,EAAE,CAAC;IAClD,CAAC;IAED,kEAAkE;IAClE,IAAI,OAAO;QACP,OAAO,IAAI,CAAC,cAAc,CAAC;IAC/B,CAAC;IAED,kDAAkD;IAClD,IAAI,GAAG;QACH,OAAO,IAAI,CAAC,UAAU,CAAC;IAC3B,CAAC;IAED;;;OAGG;IACH,IAAI,uBAAuB;QACvB,OAAO,IAAI,CAAC,SAAS,CAAC;IAC1B,CAAC;IAED;;;;;;;;;OASG;IACH,cAAc,CAAC,OAAe;QAC1B,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC;QAC9B,IAAI,CAAC,UAAU,GAAG,GAAG,OAAO,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;QACnD,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;IAC1B,CAAC;IAED;;;;;;;;;;;;;;;;OAgBG;IACH,cAAc,CAAC,OAAe;QAC1B,IAAI,CAAC,cAAc,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC;QAC9C,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC;QAC1B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;IAC1B,CAAC;IAED;;;;;;;;OAQG;IACH,gBAAgB,CAAC,WAAmB;QAChC,IAAI,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC;IACrC,CAAC;IAED,oDAAoD;IACpD,eAAe;QACX,MAAM,MAAM,GAA2B,EAAE,CAAC;QAC1C,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;YACzC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QAChC,CAAC;QACD,OAAO,MAAM,CAAC;IAClB,CAAC;CACJ;AAxID,sCAwIC","sourcesContent":["import { RouteMetadata } from '@webpieces/core-util';\n\n/**\n * The OUTBOUND request as the client filter chain sees it — the client-side twin of the server's\n * `MethodMeta`, and the `REQ` of every `Filter<ClientRequest, Response>`.\n *\n * MUTABLE on purpose, and that is the whole point of the chain: a filter re-points the URL, adds a\n * header, or replaces the serialized bytes, and whatever this object holds when the chain bottoms\n * out is EXACTLY what goes on the wire.\n *\n * ## Why the URL is behind mutators instead of two public fields\n *\n * A call has both a base URL (the host we are talking to — the OIDC audience, the thing an SSRF\n * policy judges) and a full URL (base + this route's path, or an absolute Location we were\n * redirected to). Exposing both as writable fields makes it possible for them to disagree, and a\n * base URL that disagrees with the URL actually fetched is precisely the shape of an SSRF bypass.\n * So both are private, and the only ways to move this request are {@link pointAtBaseUrl},\n * {@link pointAtFullUrl} and {@link followRedirectTo}, each of which sets BOTH consistently.\n *\n * ## Signing\n *\n * {@link body} holds the serialized bytes, not the DTO — serialization happens BEFORE the chain\n * runs. A signing filter computes its HMAC over `request.body` and adds a header, and the transport\n * sends `request.body` verbatim, so the bytes signed and the bytes sent cannot differ. That was\n * impossible while a client owned serialization internally with no seam, which is why outbound\n * webhook senders were forced back to hand-rolling `JSON.stringify` and a raw HTTP library — a\n * library that re-serializes internally would sign one byte sequence and send another.\n */\nexport class ClientRequest {\n /** The base URL half of {@link url} — the host, without this route's path. */\n private currentBaseUrl: string;\n\n /** The absolute URL this call will actually be sent to. */\n private currentUrl: string;\n\n /**\n * FALSE while this request still points where the client's own configuration put it; TRUE the\n * moment anything moved it — {@link pointAtBaseUrl}, {@link pointAtFullUrl} or {@link followRedirectTo}.\n *\n * This flag is what makes the SSRF guard automatic AND free. A URL that came out of\n * `ClientRegistry` is an address WE chose, so judging it would mean resolving DNS on every\n * internal RPC in order to re-derive a fact we already know. A URL a filter substituted is not:\n * it arrived from outside the call — a partner-editable row, an OAuth callback, a `Location`\n * header — and that is precisely the input an SSRF policy exists to judge.\n *\n * So the trigger is the ACT of re-pointing rather than a per-client setting, which means no app\n * can forget to switch the guard on and none can switch it off by omitting an argument.\n */\n private rePointed = false;\n\n /**\n * Whether the transport may follow a 3xx itself. The SSRF guard sets this FALSE so it can read\n * the `Location`, judge it under the same policy as the original URL, and only then re-invoke\n * the chain — a partner URL that 302s must not be able to bounce our POST at an internal\n * address.\n */\n followRedirects = true;\n\n constructor(\n /** The route being called — its path, http method, and auth mode. */\n public readonly route: RouteMetadata,\n /** The API contract's class name, e.g. 'PartnerWebhookApi'. For messages and logs. */\n public readonly contractName: string,\n baseUrl: string,\n /** Outbound headers. Mutable: a filter adds its signature/idempotency/tracing headers here. */\n public readonly headers: Map<string, string>,\n /** The EXACT serialized request body, or undefined for a call with no argument. */\n public body: string | undefined,\n /**\n * The request DTO the caller passed, BEFORE serialization. Read-only, and deliberately not\n * the thing that gets sent: a filter that wants to change what goes on the wire changes\n * {@link body}, so there is never a question of which of the two won.\n */\n // webpieces-disable no-any-unknown -- the request DTO's type is erased at the proxy boundary\n public readonly requestDto: unknown,\n /**\n * Encoded route path + query for this call, resolved from its typed arguments. May be `''`\n * for a contract declaring `@ApiPath('')` + `@Endpoint(POST, '', ...)`, in which case the url is\n * the base URL byte for byte.\n */\n public readonly resolvedPath: string = route.path,\n ) {\n this.currentBaseUrl = baseUrl;\n this.currentUrl = `${baseUrl}${resolvedPath}`;\n }\n\n /** The host this call is currently addressed to, with no path. */\n get baseUrl(): string {\n return this.currentBaseUrl;\n }\n\n /** The absolute URL this call will be sent to. */\n get url(): string {\n return this.currentUrl;\n }\n\n /**\n * TRUE once anything has moved this request off the address the client resolved for itself —\n * i.e. once the destination is DATA rather than a peer we named. See {@link rePointed}.\n */\n get destinationCameFromData(): boolean {\n return this.rePointed;\n }\n\n /**\n * Re-point this ONE call at another host, keeping this route's path. `ContextBaseUrlFilter`\n * calls this with the URL a partner registered; nothing here is remembered by the client, so\n * the next call through the same client starts from its configured host again.\n *\n * It also flips {@link destinationCameFromData}, which is what arms the SSRF guard sitting\n * below every app filter. That coupling is deliberate and lives HERE rather than in the guard:\n * a filter cannot move the request without saying so, because moving it is only possible\n * through this method.\n */\n pointAtBaseUrl(baseUrl: string): void {\n this.currentBaseUrl = baseUrl;\n this.currentUrl = `${baseUrl}${this.resolvedPath}`;\n this.rePointed = true;\n }\n\n /**\n * Re-point this ONE call at a COMPLETE url — host, path and query — used VERBATIM. The contract's\n * path (and any `@QueryParam` values resolved into it) is NOT appended and no separator is\n * inserted, because the url is the whole destination: a partner webhook row stores\n * `https://hooks.partner.example/in/abc?token=xyz`, and that string, byte for byte, is what must\n * be dialled. `ContextFullUrlFilter` calls this with `WebpiecesCoreHeaders.OVERRIDE_FULL_URL`.\n *\n * The base URL becomes that url's origin, so a policy that judges hosts (and the OIDC audience)\n * sees the host we are actually about to talk to. Flips {@link destinationCameFromData} for the\n * same reason {@link pointAtBaseUrl} does: the url is data, not an address we chose, so the SSRF\n * guard is armed by this call and cannot be forgotten.\n *\n * Compare {@link pointAtBaseUrl}, which keeps this route's path: that is for a partner that\n * implements OUR contract at their own host; this is for a partner that gave us one opaque url.\n *\n * @throws TypeError if `fullUrl` is not a parseable absolute URL.\n */\n pointAtFullUrl(fullUrl: string): void {\n this.currentBaseUrl = new URL(fullUrl).origin;\n this.currentUrl = fullUrl;\n this.rePointed = true;\n }\n\n /**\n * Follow a redirect to an ABSOLUTE url. A `Location` is a complete destination, so this is\n * exactly {@link pointAtFullUrl} — one implementation, so the two can never disagree about what\n * the base URL becomes or whether the SSRF guard is armed (it is: a `Location` header is the far\n * end's data, not an address we chose). Kept as its own name because the CALL SITE reads\n * differently: the guard is following a hop, not an app choosing a destination.\n *\n * @throws TypeError if `absoluteUrl` is not a parseable absolute URL.\n */\n followRedirectTo(absoluteUrl: string): void {\n this.pointAtFullUrl(absoluteUrl);\n }\n\n /** The headers in the shape the transport wants. */\n headersAsRecord(): Record<string, string> {\n const record: Record<string, string> = {};\n for (const entry of this.headers.entries()) {\n record[entry[0]] = entry[1];\n }\n return record;\n }\n}\n"]}