@atproto-labs/fetch 0.0.1 → 0.1.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.
package/CHANGELOG.md CHANGED
@@ -1,10 +1,12 @@
1
1
  # @atproto-labs/fetch
2
2
 
3
- ## 0.0.1
3
+ ## 0.1.0
4
4
 
5
- ### Patch Changes
5
+ ### Minor Changes
6
+
7
+ - [#2482](https://github.com/bluesky-social/atproto/pull/2482) [`a8d6c1123`](https://github.com/bluesky-social/atproto/commit/a8d6c112359f5c4c0cfbe2df63443ed275f2a646) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Add OAuth provider capability & support for DPoP signed tokens
6
8
 
7
- - [`e134c79a0`](https://github.com/bluesky-social/atproto/commit/e134c79a0ffb000b2cb36437815673fa6bda664b) Thanks [@devinivy](https://github.com/devinivy)! - Initial publish of experimental oauth packages to @atproto-labs
9
+ ### Patch Changes
8
10
 
9
- - Updated dependencies [[`e134c79a0`](https://github.com/bluesky-social/atproto/commit/e134c79a0ffb000b2cb36437815673fa6bda664b)]:
10
- - @atproto-labs/transformer@0.0.1
11
+ - Updated dependencies [[`a8d6c1123`](https://github.com/bluesky-social/atproto/commit/a8d6c112359f5c4c0cfbe2df63443ed275f2a646)]:
12
+ - @atproto-labs/pipe@0.1.0
@@ -1,16 +1,5 @@
1
- import { Transformer } from '@atproto-labs/transformer';
2
- export type FetchErrorOptions = {
3
- cause?: unknown;
4
- request?: Request;
5
- response?: Response;
6
- };
7
1
  export declare class FetchError extends Error {
8
2
  readonly statusCode: number;
9
- readonly request?: Request;
10
- readonly response?: Response;
11
- constructor(statusCode: number, message?: string, { cause, request, response }?: FetchErrorOptions);
12
- static from(err: unknown): Promise<FetchError>;
3
+ constructor(statusCode?: number, message?: string, options?: ErrorOptions);
13
4
  }
14
- export declare const fetchFailureHandler: Transformer<unknown, never>;
15
- export declare function extractInfo(err: unknown): [statusCode: number, message: string];
16
5
  //# sourceMappingURL=fetch-error.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fetch-error.d.ts","sourceRoot":"","sources":["../src/fetch-error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAA;AAEvD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,QAAQ,CAAC,EAAE,QAAQ,CAAA;CACpB,CAAA;AAED,qBAAa,UAAW,SAAQ,KAAK;aAKjB,UAAU,EAAE,MAAM;IAJpC,SAAgB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjC,SAAgB,QAAQ,CAAC,EAAE,QAAQ,CAAA;gBAGjB,UAAU,EAAE,MAAM,EAClC,OAAO,CAAC,EAAE,MAAM,EAChB,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAE,iBAAsB;WAOzC,IAAI,CAAC,GAAG,EAAE,OAAO;CAI/B;AAED,eAAO,MAAM,mBAAmB,EAAE,WAAW,CAAC,OAAO,EAAE,KAAK,CAI3D,CAAA;AAgBD,wBAAgB,WAAW,CACzB,GAAG,EAAE,OAAO,GACX,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,CA0BvC"}
1
+ {"version":3,"file":"fetch-error.d.ts","sourceRoot":"","sources":["../src/fetch-error.ts"],"names":[],"mappings":"AAAA,qBAAa,UAAW,SAAQ,KAAK;IACnC,SAAgB,UAAU,EAAE,MAAM,CAAA;gBAEtB,UAAU,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;CAW1E"}
@@ -1,73 +1,58 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.extractInfo = exports.fetchFailureHandler = exports.FetchError = void 0;
3
+ exports.FetchError = void 0;
4
4
  class FetchError extends Error {
5
- constructor(statusCode, message, { cause, request, response } = {}) {
6
- super(message, { cause });
5
+ constructor(statusCode, message, options) {
6
+ if (statusCode == null || !message) {
7
+ const info = extractInfo(extractRootCause(options?.cause));
8
+ statusCode = statusCode ?? info[0];
9
+ message = message || info[1];
10
+ }
11
+ super(message, options);
7
12
  Object.defineProperty(this, "statusCode", {
8
- enumerable: true,
9
- configurable: true,
10
- writable: true,
11
- value: statusCode
12
- });
13
- Object.defineProperty(this, "request", {
14
13
  enumerable: true,
15
14
  configurable: true,
16
15
  writable: true,
17
16
  value: void 0
18
17
  });
19
- Object.defineProperty(this, "response", {
20
- enumerable: true,
21
- configurable: true,
22
- writable: true,
23
- value: void 0
24
- });
25
- this.request = request;
26
- this.response = response;
27
- }
28
- static async from(err) {
29
- const cause = extractCause(err);
30
- return new FetchError(...extractInfo(cause), { cause });
18
+ this.statusCode = statusCode;
31
19
  }
32
20
  }
33
21
  exports.FetchError = FetchError;
34
- const fetchFailureHandler = async (err) => {
35
- throw await FetchError.from(err);
36
- };
37
- exports.fetchFailureHandler = fetchFailureHandler;
38
- function extractCause(err) {
22
+ function extractRootCause(err) {
39
23
  // Unwrap the Network error from undici (i.e. Node's internal fetch() implementation)
40
24
  // https://github.com/nodejs/undici/blob/3274c975947ce11a08508743df026f73598bfead/lib/web/fetch/index.js#L223-L228
41
25
  if (err instanceof TypeError &&
42
26
  err.message === 'fetch failed' &&
43
- err.cause instanceof Error) {
27
+ err.cause !== undefined) {
44
28
  return err.cause;
45
29
  }
46
30
  return err;
47
31
  }
48
32
  function extractInfo(err) {
49
33
  if (typeof err === 'string' && err.length > 0) {
50
- return [502, err];
34
+ return [500, err];
51
35
  }
52
36
  if (!(err instanceof Error)) {
53
- return [502, 'Unable to fetch'];
37
+ return [500, 'Failed to fetch'];
54
38
  }
55
- if ('code' in err && typeof err.code === 'string') {
39
+ const code = err['code'];
40
+ if (typeof code === 'string') {
56
41
  switch (true) {
57
- case err.code === 'ENOTFOUND':
58
- return [404, 'Invalid hostname'];
59
- case err.code === 'ECONNREFUSED':
42
+ case code === 'ENOTFOUND':
43
+ return [400, 'Invalid hostname'];
44
+ case code === 'ECONNREFUSED':
60
45
  return [502, 'Connection refused'];
61
- case err.code === 'DEPTH_ZERO_SELF_SIGNED_CERT':
46
+ case code === 'DEPTH_ZERO_SELF_SIGNED_CERT':
62
47
  return [502, 'Self-signed certificate'];
63
- case err.code.startsWith('ERR_TLS'):
48
+ case code.startsWith('ERR_TLS'):
64
49
  return [502, 'TLS error'];
65
- case err.code.startsWith('ECONN'):
50
+ case code.startsWith('ECONN'):
66
51
  return [502, 'Connection error'];
52
+ default:
53
+ return [500, `${code} error`];
67
54
  }
68
55
  }
69
- // Let's assume that other errors are "bad gateway" errors
70
- return [502, err.message];
56
+ return [500, err.message];
71
57
  }
72
- exports.extractInfo = extractInfo;
73
58
  //# sourceMappingURL=fetch-error.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"fetch-error.js","sourceRoot":"","sources":["../src/fetch-error.ts"],"names":[],"mappings":";;;AAQA,MAAa,UAAW,SAAQ,KAAK;IAInC,YACkB,UAAkB,EAClC,OAAgB,EAChB,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,KAAwB,EAAE;QAEpD,KAAK,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,CAAA;QAJzB;;;;mBAAgB,UAAU;WAAQ;QAJpB;;;;;WAAiB;QACjB;;;;;WAAmB;QAQjC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;QACtB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAA;IAC1B,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,GAAY;QAC5B,MAAM,KAAK,GAAG,YAAY,CAAC,GAAG,CAAC,CAAA;QAC/B,OAAO,IAAI,UAAU,CAAC,GAAG,WAAW,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,CAAA;IACzD,CAAC;CACF;AAlBD,gCAkBC;AAEM,MAAM,mBAAmB,GAAgC,KAAK,EACnE,GAAY,EACZ,EAAE;IACF,MAAM,MAAM,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AAClC,CAAC,CAAA;AAJY,QAAA,mBAAmB,uBAI/B;AAED,SAAS,YAAY,CAAC,GAAY;IAChC,qFAAqF;IACrF,kHAAkH;IAClH,IACE,GAAG,YAAY,SAAS;QACxB,GAAG,CAAC,OAAO,KAAK,cAAc;QAC9B,GAAG,CAAC,KAAK,YAAY,KAAK,EAC1B,CAAC;QACD,OAAO,GAAG,CAAC,KAAK,CAAA;IAClB,CAAC;IAED,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,SAAgB,WAAW,CACzB,GAAY;IAEZ,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9C,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;IACnB,CAAC;IAED,IAAI,CAAC,CAAC,GAAG,YAAY,KAAK,CAAC,EAAE,CAAC;QAC5B,OAAO,CAAC,GAAG,EAAE,iBAAiB,CAAC,CAAA;IACjC,CAAC;IAED,IAAI,MAAM,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAClD,QAAQ,IAAI,EAAE,CAAC;YACb,KAAK,GAAG,CAAC,IAAI,KAAK,WAAW;gBAC3B,OAAO,CAAC,GAAG,EAAE,kBAAkB,CAAC,CAAA;YAClC,KAAK,GAAG,CAAC,IAAI,KAAK,cAAc;gBAC9B,OAAO,CAAC,GAAG,EAAE,oBAAoB,CAAC,CAAA;YACpC,KAAK,GAAG,CAAC,IAAI,KAAK,6BAA6B;gBAC7C,OAAO,CAAC,GAAG,EAAE,yBAAyB,CAAC,CAAA;YACzC,KAAK,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;gBACjC,OAAO,CAAC,GAAG,EAAE,WAAW,CAAC,CAAA;YAC3B,KAAK,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;gBAC/B,OAAO,CAAC,GAAG,EAAE,kBAAkB,CAAC,CAAA;QACpC,CAAC;IACH,CAAC;IAED,0DAA0D;IAC1D,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,OAAO,CAAC,CAAA;AAC3B,CAAC;AA5BD,kCA4BC"}
1
+ {"version":3,"file":"fetch-error.js","sourceRoot":"","sources":["../src/fetch-error.ts"],"names":[],"mappings":";;;AAAA,MAAa,UAAW,SAAQ,KAAK;IAGnC,YAAY,UAAmB,EAAE,OAAgB,EAAE,OAAsB;QACvE,IAAI,UAAU,IAAI,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACnC,MAAM,IAAI,GAAG,WAAW,CAAC,gBAAgB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAA;YAC1D,UAAU,GAAG,UAAU,IAAI,IAAI,CAAC,CAAC,CAAC,CAAA;YAClC,OAAO,GAAG,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,CAAA;QAC9B,CAAC;QAED,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;QATT;;;;;WAAkB;QAWhC,IAAI,CAAC,UAAU,GAAG,UAAU,CAAA;IAC9B,CAAC;CACF;AAdD,gCAcC;AAED,SAAS,gBAAgB,CAAC,GAAY;IACpC,qFAAqF;IACrF,kHAAkH;IAClH,IACE,GAAG,YAAY,SAAS;QACxB,GAAG,CAAC,OAAO,KAAK,cAAc;QAC9B,GAAG,CAAC,KAAK,KAAK,SAAS,EACvB,CAAC;QACD,OAAO,GAAG,CAAC,KAAK,CAAA;IAClB,CAAC;IAED,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,SAAS,WAAW,CAAC,GAAY;IAC/B,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9C,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;IACnB,CAAC;IAED,IAAI,CAAC,CAAC,GAAG,YAAY,KAAK,CAAC,EAAE,CAAC;QAC5B,OAAO,CAAC,GAAG,EAAE,iBAAiB,CAAC,CAAA;IACjC,CAAC;IAED,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,CAAA;IACxB,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC7B,QAAQ,IAAI,EAAE,CAAC;YACb,KAAK,IAAI,KAAK,WAAW;gBACvB,OAAO,CAAC,GAAG,EAAE,kBAAkB,CAAC,CAAA;YAClC,KAAK,IAAI,KAAK,cAAc;gBAC1B,OAAO,CAAC,GAAG,EAAE,oBAAoB,CAAC,CAAA;YACpC,KAAK,IAAI,KAAK,6BAA6B;gBACzC,OAAO,CAAC,GAAG,EAAE,yBAAyB,CAAC,CAAA;YACzC,KAAK,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;gBAC7B,OAAO,CAAC,GAAG,EAAE,WAAW,CAAC,CAAA;YAC3B,KAAK,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;gBAC3B,OAAO,CAAC,GAAG,EAAE,kBAAkB,CAAC,CAAA;YAClC;gBACE,OAAO,CAAC,GAAG,EAAE,GAAG,IAAI,QAAQ,CAAC,CAAA;QACjC,CAAC;IACH,CAAC;IAED,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,OAAO,CAAC,CAAA;AAC3B,CAAC"}
@@ -1,7 +1,11 @@
1
- import { Transformer } from '@atproto-labs/transformer';
2
- export type RequestTranformer = Transformer<Request>;
3
- export declare function protocolCheckRequestTransform(protocols: Iterable<string>): RequestTranformer;
4
- export declare function requireHostHeaderTranform(): RequestTranformer;
1
+ import { FetchError } from './fetch-error.js';
2
+ export declare class FetchRequestError extends FetchError {
3
+ readonly request: Request;
4
+ constructor(request: Request, statusCode?: number, message?: string, options?: ErrorOptions);
5
+ static from(request: Request, cause: unknown): FetchRequestError;
6
+ }
7
+ export declare function protocolCheckRequestTransform(protocols: Iterable<string>): (input: Request | string | URL, init?: RequestInit) => Request;
8
+ export declare function requireHostHeaderTranform(): (input: Request | string | URL, init?: RequestInit) => Request;
5
9
  export declare const DEFAULT_FORBIDDEN_DOMAIN_NAMES: string[];
6
- export declare function forbiddenDomainNameRequestTransform(denyList?: Iterable<string>): RequestTranformer;
10
+ export declare function forbiddenDomainNameRequestTransform(denyList?: Iterable<string>): ((request: any) => Promise<any>) | ((input: Request | string | URL, init?: RequestInit) => Promise<Request>);
7
11
  //# sourceMappingURL=fetch-request.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fetch-request.d.ts","sourceRoot":"","sources":["../src/fetch-request.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAA;AAKvD,MAAM,MAAM,iBAAiB,GAAG,WAAW,CAAC,OAAO,CAAC,CAAA;AAEpD,wBAAgB,6BAA6B,CAC3C,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,GAC1B,iBAAiB,CAYnB;AAED,wBAAgB,yBAAyB,IAAI,iBAAiB,CAkB7D;AAED,eAAO,MAAM,8BAA8B,UAS1C,CAAA;AAED,wBAAgB,mCAAmC,CACjD,QAAQ,GAAE,QAAQ,CAAC,MAAM,CAAkC,GAC1D,iBAAiB,CA6BnB"}
1
+ {"version":3,"file":"fetch-request.d.ts","sourceRoot":"","sources":["../src/fetch-request.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAI7C,qBAAa,iBAAkB,SAAQ,UAAU;aAE7B,OAAO,EAAE,OAAO;gBAAhB,OAAO,EAAE,OAAO,EAChC,UAAU,CAAC,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,YAAY;IAKxB,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,iBAAiB;CAIjE;AASD,wBAAgB,6BAA6B,CAAC,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,WAGxD,OAAO,GAAG,MAAM,GAAG,GAAG,SAAS,WAAW,aAe1D;AAED,wBAAgB,yBAAyB,YACxB,OAAO,GAAG,MAAM,GAAG,GAAG,SAAS,WAAW,aAuB1D;AAED,eAAO,MAAM,8BAA8B,UAS1C,CAAA;AAED,wBAAgB,mCAAmC,CACjD,QAAQ,GAAE,QAAQ,CAAC,MAAM,CAAkC,+CAUtC,OAAO,GAAG,MAAM,GAAG,GAAG,SAAS,WAAW,uBAsBhE"}
@@ -1,30 +1,55 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.forbiddenDomainNameRequestTransform = exports.DEFAULT_FORBIDDEN_DOMAIN_NAMES = exports.requireHostHeaderTranform = exports.protocolCheckRequestTransform = void 0;
3
+ exports.forbiddenDomainNameRequestTransform = exports.DEFAULT_FORBIDDEN_DOMAIN_NAMES = exports.requireHostHeaderTranform = exports.protocolCheckRequestTransform = exports.FetchRequestError = void 0;
4
4
  const fetch_error_js_1 = require("./fetch-error.js");
5
+ const fetch_js_1 = require("./fetch.js");
5
6
  const util_js_1 = require("./util.js");
7
+ class FetchRequestError extends fetch_error_js_1.FetchError {
8
+ constructor(request, statusCode, message, options) {
9
+ super(statusCode, message, options);
10
+ Object.defineProperty(this, "request", {
11
+ enumerable: true,
12
+ configurable: true,
13
+ writable: true,
14
+ value: request
15
+ });
16
+ }
17
+ static from(request, cause) {
18
+ if (cause instanceof FetchRequestError)
19
+ return cause;
20
+ return new FetchRequestError(request, undefined, undefined, { cause });
21
+ }
22
+ }
23
+ exports.FetchRequestError = FetchRequestError;
24
+ const extractUrl = (input) => typeof input === 'string'
25
+ ? new URL(input)
26
+ : input instanceof URL
27
+ ? input
28
+ : new URL(input.url);
6
29
  function protocolCheckRequestTransform(protocols) {
7
30
  const allowedProtocols = new Set(protocols);
8
- return async (request) => {
9
- const { protocol } = new URL(request.url);
31
+ return (input, init) => {
32
+ const { protocol } = extractUrl(input);
33
+ const request = (0, fetch_js_1.asRequest)(input, init);
10
34
  if (!allowedProtocols.has(protocol)) {
11
- throw new fetch_error_js_1.FetchError(400, `${protocol} is not allowed`, { request });
35
+ throw new FetchRequestError(request, 400, `"${protocol}" protocol is not allowed`);
12
36
  }
13
37
  return request;
14
38
  };
15
39
  }
16
40
  exports.protocolCheckRequestTransform = protocolCheckRequestTransform;
17
41
  function requireHostHeaderTranform() {
18
- return async (request) => {
42
+ return (input, init) => {
19
43
  // Note that fetch() will automatically add the Host header from the URL and
20
44
  // discard any Host header manually set in the request.
21
- const { protocol, hostname } = new URL(request.url);
45
+ const { protocol, hostname } = extractUrl(input);
46
+ const request = (0, fetch_js_1.asRequest)(input, init);
22
47
  // "Host" header only makes sense in the context of an HTTP request
23
- if (protocol !== 'http:' && protocol !== 'https') {
24
- throw new fetch_error_js_1.FetchError(400, `Forbidden protocol ${protocol}`, { request });
48
+ if (protocol !== 'http:' && protocol !== 'https:') {
49
+ throw new FetchRequestError(request, 400, `"${protocol}" requests are not allowed`);
25
50
  }
26
51
  if (!hostname || (0, util_js_1.isIp)(hostname)) {
27
- throw new fetch_error_js_1.FetchError(400, 'Invalid hostname', { request });
52
+ throw new FetchRequestError(request, 400, 'Invalid hostname');
28
53
  }
29
54
  return request;
30
55
  };
@@ -47,18 +72,19 @@ function forbiddenDomainNameRequestTransform(denyList = exports.DEFAULT_FORBIDDE
47
72
  if (denySet.size === 0) {
48
73
  return async (request) => request;
49
74
  }
50
- return async (request) => {
51
- const { hostname } = new URL(request.url);
75
+ return async (input, init) => {
76
+ const { hostname } = extractUrl(input);
77
+ const request = (0, fetch_js_1.asRequest)(input, init);
52
78
  // Full domain name check
53
79
  if (denySet.has(hostname)) {
54
- throw new fetch_error_js_1.FetchError(403, 'Forbidden hostname', { request });
80
+ throw new FetchRequestError(request, 403, 'Forbidden hostname');
55
81
  }
56
82
  // Sub domain name check
57
83
  let curDot = hostname.indexOf('.');
58
84
  while (curDot !== -1) {
59
85
  const subdomain = hostname.slice(curDot + 1);
60
86
  if (denySet.has(`*.${subdomain}`)) {
61
- throw new fetch_error_js_1.FetchError(403, 'Forbidden hostname', { request });
87
+ throw new FetchRequestError(request, 403, 'Forbidden hostname');
62
88
  }
63
89
  curDot = hostname.indexOf('.', curDot + 1);
64
90
  }
@@ -1 +1 @@
1
- {"version":3,"file":"fetch-request.js","sourceRoot":"","sources":["../src/fetch-request.ts"],"names":[],"mappings":";;;AAEA,qDAA6C;AAC7C,uCAAgC;AAIhC,SAAgB,6BAA6B,CAC3C,SAA2B;IAE3B,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAS,SAAS,CAAC,CAAA;IAEnD,OAAO,KAAK,EAAE,OAAO,EAAE,EAAE;QACvB,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QAEzC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YACpC,MAAM,IAAI,2BAAU,CAAC,GAAG,EAAE,GAAG,QAAQ,iBAAiB,EAAE,EAAE,OAAO,EAAE,CAAC,CAAA;QACtE,CAAC;QAED,OAAO,OAAO,CAAA;IAChB,CAAC,CAAA;AACH,CAAC;AAdD,sEAcC;AAED,SAAgB,yBAAyB;IACvC,OAAO,KAAK,EAAE,OAAO,EAAE,EAAE;QACvB,4EAA4E;QAC5E,uDAAuD;QAEvD,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QAEnD,mEAAmE;QACnE,IAAI,QAAQ,KAAK,OAAO,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;YACjD,MAAM,IAAI,2BAAU,CAAC,GAAG,EAAE,sBAAsB,QAAQ,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,CAAA;QAC1E,CAAC;QAED,IAAI,CAAC,QAAQ,IAAI,IAAA,cAAI,EAAC,QAAQ,CAAC,EAAE,CAAC;YAChC,MAAM,IAAI,2BAAU,CAAC,GAAG,EAAE,kBAAkB,EAAE,EAAE,OAAO,EAAE,CAAC,CAAA;QAC5D,CAAC;QAED,OAAO,OAAO,CAAA;IAChB,CAAC,CAAA;AACH,CAAC;AAlBD,8DAkBC;AAEY,QAAA,8BAA8B,GAAG;IAC5C,aAAa;IACb,eAAe;IACf,aAAa;IACb,eAAe;IACf,aAAa;IACb,eAAe;IACf,uBAAuB;IACvB,yBAAyB;CAC1B,CAAA;AAED,SAAgB,mCAAmC,CACjD,WAA6B,sCAA8B;IAE3D,MAAM,OAAO,GAAG,IAAI,GAAG,CAAS,QAAQ,CAAC,CAAA;IAEzC,2EAA2E;IAC3E,kBAAkB;IAClB,IAAI,OAAO,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,KAAK,EAAE,OAAO,EAAE,EAAE,CAAC,OAAO,CAAA;IACnC,CAAC;IAED,OAAO,KAAK,EAAE,OAAO,EAAE,EAAE;QACvB,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QAEzC,yBAAyB;QACzB,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,2BAAU,CAAC,GAAG,EAAE,oBAAoB,EAAE,EAAE,OAAO,EAAE,CAAC,CAAA;QAC9D,CAAC;QAED,wBAAwB;QACxB,IAAI,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QAClC,OAAO,MAAM,KAAK,CAAC,CAAC,EAAE,CAAC;YACrB,MAAM,SAAS,GAAG,QAAQ,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;YAC5C,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,SAAS,EAAE,CAAC,EAAE,CAAC;gBAClC,MAAM,IAAI,2BAAU,CAAC,GAAG,EAAE,oBAAoB,EAAE,EAAE,OAAO,EAAE,CAAC,CAAA;YAC9D,CAAC;YACD,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,CAAC,CAAA;QAC5C,CAAC;QAED,OAAO,OAAO,CAAA;IAChB,CAAC,CAAA;AACH,CAAC;AA/BD,kFA+BC"}
1
+ {"version":3,"file":"fetch-request.js","sourceRoot":"","sources":["../src/fetch-request.ts"],"names":[],"mappings":"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"}
@@ -1,24 +1,42 @@
1
- import { Transformer } from '@atproto-labs/transformer';
2
- import { z } from 'zod';
3
- import { FetchError, FetchErrorOptions } from './fetch-error.js';
1
+ import { Transformer } from '@atproto-labs/pipe';
2
+ import type { ZodTypeAny, ParseParams, TypeOf } from 'zod';
3
+ import { FetchError } from './fetch-error.js';
4
4
  import { Json } from './util.js';
5
5
  export type ResponseTranformer = Transformer<Response>;
6
6
  export type ResponseMessageGetter = Transformer<Response, string | undefined>;
7
7
  export declare class FetchResponseError extends FetchError {
8
- constructor(response: Response, statusCode?: number, message?: string, options?: Omit<FetchErrorOptions, 'response'>);
9
- static from(response: Response, statusCode?: number, customMessage?: string | ResponseMessageGetter, options?: Omit<FetchErrorOptions, 'response'>): Promise<FetchResponseError>;
8
+ readonly response: Response;
9
+ constructor(response: Response, statusCode?: number, message?: string, options?: ErrorOptions);
10
+ static from(response: Response, customMessage?: string | ResponseMessageGetter, statusCode?: number, options?: ErrorOptions): Promise<FetchResponseError>;
10
11
  }
12
+ export declare function peekJson(response: Response, maxSize?: number): Promise<undefined | Json>;
13
+ export declare function checkLength(response: Response, maxBytes: number): number | undefined;
14
+ export declare function extractLength(response: Response): number | undefined;
15
+ export declare function extractMime(response: Response): string | undefined;
16
+ /**
17
+ * If the transformer results in an error, ensure that the response body is
18
+ * consumed as, in some environments (Node 👀), the response will not
19
+ * automatically be GC'd.
20
+ *
21
+ * @see {@link https://undici.nodejs.org/#/?id=garbage-collection}
22
+ * @param [onCancellationError] - Callback to handle any async body cancelling
23
+ * error. Defaults to logging the error. Do not use `null` if the request is
24
+ * cloned.
25
+ */
26
+ export declare function cancelBodyOnError<T>(transformer: Transformer<Response, T>, onCancellationError?: null | ((err: unknown) => void)): (response: Response) => Promise<T>;
11
27
  export declare function fetchOkProcessor(customMessage?: string | ResponseMessageGetter): ResponseTranformer;
28
+ export declare function fetchOkTransformer(response: Response, customMessage?: string | ResponseMessageGetter): Promise<Response>;
12
29
  export declare function fetchMaxSizeProcessor(maxBytes: number): ResponseTranformer;
13
- export declare function fetchResponseMaxSize(response: Response, maxBytes: number): Promise<Response>;
14
- export type ContentTypeCheckFn = (contentType: string) => boolean;
15
- export type ContentTypeCheck = string | RegExp | ContentTypeCheckFn;
16
- export declare function fetchTypeProcessor(expectedType: ContentTypeCheck, contentTypeRequired?: boolean): ResponseTranformer;
30
+ export declare function fetchResponseMaxSizeChecker(response: Response, maxBytes: number): Response;
31
+ export type MimeTypeCheckFn = (mimeType: string) => boolean;
32
+ export type MimeTypeCheck = string | RegExp | MimeTypeCheckFn;
33
+ export declare function fetchTypeProcessor(expectedMime: MimeTypeCheck, contentTypeRequired?: boolean): ResponseTranformer;
34
+ export declare function fetchResponseTypeChecker(response: Response, isExpectedMime: MimeTypeCheckFn, contentTypeRequired?: boolean): Promise<Response>;
17
35
  export type ParsedJsonResponse<T = Json> = {
18
36
  response: Response;
19
37
  json: T;
20
38
  };
21
- export declare function jsonTranformer<T = Json>(response: Response): Promise<ParsedJsonResponse<T>>;
22
- export declare function fetchJsonProcessor<T = Json>(contentType?: ContentTypeCheck, contentTypeRequired?: boolean): Transformer<Response, ParsedJsonResponse<T>>;
23
- export declare function fetchJsonZodProcessor<S extends z.ZodTypeAny>(schema: S, params?: Partial<z.ParseParams>): Transformer<ParsedJsonResponse, z.infer<S>>;
39
+ export declare function fetchResponseJsonTranformer<T = Json>(response: Response): Promise<ParsedJsonResponse<T>>;
40
+ export declare function fetchJsonProcessor<T = Json>(expectedMime?: MimeTypeCheck, contentTypeRequired?: boolean): Transformer<Response, ParsedJsonResponse<T>>;
41
+ export declare function fetchJsonZodProcessor<S extends ZodTypeAny>(schema: S, params?: Partial<ParseParams>): Transformer<ParsedJsonResponse, TypeOf<S>>;
24
42
  //# sourceMappingURL=fetch-response.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fetch-response.d.ts","sourceRoot":"","sources":["../src/fetch-response.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAW,MAAM,2BAA2B,CAAA;AAChE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AAChE,OAAO,EAAE,IAAI,EAAsB,MAAM,WAAW,CAAA;AAGpD,MAAM,MAAM,kBAAkB,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAA;AACtD,MAAM,MAAM,qBAAqB,GAAG,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;AAmC7E,qBAAa,kBAAmB,SAAQ,UAAU;gBAE9C,QAAQ,EAAE,QAAQ,EAClB,UAAU,GAAE,MAAwB,EACpC,OAAO,GAAE,MAA4B,EACrC,OAAO,CAAC,EAAE,IAAI,CAAC,iBAAiB,EAAE,UAAU,CAAC;WAKlC,IAAI,CACf,QAAQ,EAAE,QAAQ,EAClB,UAAU,SAAkB,EAC5B,aAAa,GAAE,MAAM,GAAG,qBAA8C,EACtE,OAAO,CAAC,EAAE,IAAI,CAAC,iBAAiB,EAAE,UAAU,CAAC;CAehD;AAED,wBAAgB,gBAAgB,CAC9B,aAAa,CAAC,EAAE,MAAM,GAAG,qBAAqB,GAC7C,kBAAkB,CAKpB;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,kBAAkB,CAM1E;AAED,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,QAAQ,CAAC,CA8BnB;AAED,MAAM,MAAM,kBAAkB,GAAG,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAA;AACjE,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,MAAM,GAAG,kBAAkB,CAAA;AAEnE,wBAAgB,kBAAkB,CAChC,YAAY,EAAE,gBAAgB,EAC9B,mBAAmB,UAAO,GACzB,kBAAkB,CAgCpB;AAED,MAAM,MAAM,kBAAkB,CAAC,CAAC,GAAG,IAAI,IAAI;IACzC,QAAQ,EAAE,QAAQ,CAAA;IAClB,IAAI,EAAE,CAAC,CAAA;CACR,CAAA;AAED,wBAAsB,cAAc,CAAC,CAAC,GAAG,IAAI,EAC3C,QAAQ,EAAE,QAAQ,GACjB,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAoBhC;AAED,wBAAgB,kBAAkB,CAAC,CAAC,GAAG,IAAI,EACzC,WAAW,GAAE,gBAAoD,EACjE,mBAAmB,UAAO,GACzB,WAAW,CAAC,QAAQ,EAAE,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAK9C;AAED,wBAAgB,qBAAqB,CAAC,CAAC,SAAS,CAAC,CAAC,UAAU,EAC1D,MAAM,EAAE,CAAC,EACT,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,GAC9B,WAAW,CAAC,kBAAkB,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAG7C"}
1
+ {"version":3,"file":"fetch-response.d.ts","sourceRoot":"","sources":["../src/fetch-response.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAQ,MAAM,oBAAoB,CAAA;AAGtD,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,KAAK,CAAA;AAE1D,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAE7C,OAAO,EACL,IAAI,EAML,MAAM,WAAW,CAAA;AAElB,MAAM,MAAM,kBAAkB,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAA;AACtD,MAAM,MAAM,qBAAqB,GAAG,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;AAE7E,qBAAa,kBAAmB,SAAQ,UAAU;aAE9B,QAAQ,EAAE,QAAQ;gBAAlB,QAAQ,EAAE,QAAQ,EAClC,UAAU,GAAE,MAAwB,EACpC,OAAO,GAAE,MAA4B,EACrC,OAAO,CAAC,EAAE,YAAY;WAKX,IAAI,CACf,QAAQ,EAAE,QAAQ,EAClB,aAAa,GAAE,MAAM,GAAG,qBAA8C,EACtE,UAAU,SAAkB,EAC5B,OAAO,CAAC,EAAE,YAAY;CAWzB;AA8BD,wBAAsB,QAAQ,CAC5B,QAAQ,EAAE,QAAQ,EAClB,OAAO,SAAW,GACjB,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAoB3B;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,sBAU/D;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,QAAQ,sBAW/C;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,QAAQ,sBAK7C;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EACjC,WAAW,EAAE,WAAW,CAAC,QAAQ,EAAE,CAAC,CAAC,EACrC,mBAAmB,GAAE,IAAI,GAAG,CAAC,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAwB,GAC1E,CAAC,QAAQ,EAAE,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,CASpC;AAED,wBAAgB,gBAAgB,CAC9B,aAAa,CAAC,EAAE,MAAM,GAAG,qBAAqB,GAC7C,kBAAkB,CAIpB;AAED,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,QAAQ,EAClB,aAAa,CAAC,EAAE,MAAM,GAAG,qBAAqB,qBAI/C;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,kBAAkB,CAQ1E;AAED,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,MAAM,GACf,QAAQ,CAUV;AAED,MAAM,MAAM,eAAe,GAAG,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAA;AAC3D,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,MAAM,GAAG,eAAe,CAAA;AAE7D,wBAAgB,kBAAkB,CAChC,YAAY,EAAE,aAAa,EAC3B,mBAAmB,UAAO,GACzB,kBAAkB,CAWpB;AAED,wBAAsB,wBAAwB,CAC5C,QAAQ,EAAE,QAAQ,EAClB,cAAc,EAAE,eAAe,EAC/B,mBAAmB,UAAO,GACzB,OAAO,CAAC,QAAQ,CAAC,CAmBnB;AAED,MAAM,MAAM,kBAAkB,CAAC,CAAC,GAAG,IAAI,IAAI;IACzC,QAAQ,EAAE,QAAQ,CAAA;IAClB,IAAI,EAAE,CAAC,CAAA;CACR,CAAA;AAED,wBAAsB,2BAA2B,CAAC,CAAC,GAAG,IAAI,EACxD,QAAQ,EAAE,QAAQ,GACjB,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAYhC;AAED,wBAAgB,kBAAkB,CAAC,CAAC,GAAG,IAAI,EACzC,YAAY,GAAE,aAAiD,EAC/D,mBAAmB,UAAO,GACzB,WAAW,CAAC,QAAQ,EAAE,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAK9C;AAED,wBAAgB,qBAAqB,CAAC,CAAC,SAAS,UAAU,EACxD,MAAM,EAAE,CAAC,EACT,MAAM,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAC5B,WAAW,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAG5C"}
@@ -1,17 +1,32 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.fetchJsonZodProcessor = exports.fetchJsonProcessor = exports.jsonTranformer = exports.fetchTypeProcessor = exports.fetchResponseMaxSize = exports.fetchMaxSizeProcessor = exports.fetchOkProcessor = exports.FetchResponseError = void 0;
4
- const transformer_1 = require("@atproto-labs/transformer");
3
+ exports.fetchJsonZodProcessor = exports.fetchJsonProcessor = exports.fetchResponseJsonTranformer = exports.fetchResponseTypeChecker = exports.fetchTypeProcessor = exports.fetchResponseMaxSizeChecker = exports.fetchMaxSizeProcessor = exports.fetchOkTransformer = exports.fetchOkProcessor = exports.cancelBodyOnError = exports.extractMime = exports.extractLength = exports.checkLength = exports.peekJson = exports.FetchResponseError = void 0;
4
+ const pipe_1 = require("@atproto-labs/pipe");
5
5
  const fetch_error_js_1 = require("./fetch-error.js");
6
- const util_js_1 = require("./util.js");
7
6
  const transformed_response_js_1 = require("./transformed-response.js");
7
+ const util_js_1 = require("./util.js");
8
+ class FetchResponseError extends fetch_error_js_1.FetchError {
9
+ constructor(response, statusCode = response.status, message = response.statusText, options) {
10
+ super(statusCode, message, options);
11
+ Object.defineProperty(this, "response", {
12
+ enumerable: true,
13
+ configurable: true,
14
+ writable: true,
15
+ value: response
16
+ });
17
+ }
18
+ static async from(response, customMessage = extractResponseMessage, statusCode = response.status, options) {
19
+ const message = typeof customMessage === 'string'
20
+ ? customMessage
21
+ : typeof customMessage === 'function'
22
+ ? await customMessage(response)
23
+ : undefined;
24
+ return new FetchResponseError(response, statusCode, message, options);
25
+ }
26
+ }
27
+ exports.FetchResponseError = FetchResponseError;
8
28
  const extractResponseMessage = async (response) => {
9
- if (!response.body)
10
- return undefined;
11
- const contentType = response.headers.get('content-type');
12
- if (!contentType)
13
- return undefined;
14
- const mimeType = contentType.split(';')[0].trim();
29
+ const mimeType = extractMime(response);
15
30
  if (!mimeType)
16
31
  return undefined;
17
32
  try {
@@ -38,99 +53,137 @@ const extractResponseMessage = async (response) => {
38
53
  }
39
54
  return undefined;
40
55
  };
41
- class FetchResponseError extends fetch_error_js_1.FetchError {
42
- constructor(response, statusCode = response.status, message = response.statusText, options) {
43
- super(statusCode, message, { response, ...options });
56
+ async function peekJson(response, maxSize = Infinity) {
57
+ const type = extractMime(response);
58
+ if (type !== 'application/json')
59
+ return undefined;
60
+ checkLength(response, maxSize);
61
+ // 1) Clone the request so we can consume the body
62
+ const clonedResponse = response.clone();
63
+ // 2) Make sure the request's body is not too large
64
+ const limitedResponse = response.body && maxSize < Infinity
65
+ ? new transformed_response_js_1.TransformedResponse(clonedResponse, new util_js_1.MaxBytesTransformStream(maxSize))
66
+ : // Note: some runtimes (e.g. react-native) don't expose a body property
67
+ clonedResponse;
68
+ // 3) Parse the JSON
69
+ return limitedResponse.json();
70
+ }
71
+ exports.peekJson = peekJson;
72
+ function checkLength(response, maxBytes) {
73
+ // Note: negation accounts for invalid value types (NaN, non numbers)
74
+ if (!(maxBytes >= 0)) {
75
+ throw new TypeError('maxBytes must be a non-negative number');
44
76
  }
45
- static async from(response, statusCode = response.status, customMessage = extractResponseMessage, options) {
46
- const message = typeof customMessage === 'string'
47
- ? customMessage
48
- : typeof customMessage === 'function'
49
- ? await customMessage(response)
50
- : undefined;
51
- // Make sure the body gets consumed as, in some environments (Node 👀), the
52
- // response will not automatically be GC'd.
53
- if (!response.bodyUsed)
54
- await response.body?.cancel();
55
- return new FetchResponseError(response, statusCode, message, options);
77
+ const length = extractLength(response);
78
+ if (length != null && length > maxBytes) {
79
+ throw new FetchResponseError(response, 502, 'Response too large');
56
80
  }
81
+ return length;
57
82
  }
58
- exports.FetchResponseError = FetchResponseError;
59
- function fetchOkProcessor(customMessage) {
83
+ exports.checkLength = checkLength;
84
+ function extractLength(response) {
85
+ const contentLength = response.headers.get('Content-Length');
86
+ if (contentLength == null)
87
+ return undefined;
88
+ if (!/^\d+$/.test(contentLength)) {
89
+ throw new FetchResponseError(response, 502, 'Invalid Content-Length');
90
+ }
91
+ const length = Number(contentLength);
92
+ if (!Number.isSafeInteger(length)) {
93
+ throw new FetchResponseError(response, 502, 'Content-Length too large');
94
+ }
95
+ return length;
96
+ }
97
+ exports.extractLength = extractLength;
98
+ function extractMime(response) {
99
+ const contentType = response.headers.get('Content-Type');
100
+ if (contentType == null)
101
+ return undefined;
102
+ return contentType.split(';', 1)[0].trim();
103
+ }
104
+ exports.extractMime = extractMime;
105
+ /**
106
+ * If the transformer results in an error, ensure that the response body is
107
+ * consumed as, in some environments (Node 👀), the response will not
108
+ * automatically be GC'd.
109
+ *
110
+ * @see {@link https://undici.nodejs.org/#/?id=garbage-collection}
111
+ * @param [onCancellationError] - Callback to handle any async body cancelling
112
+ * error. Defaults to logging the error. Do not use `null` if the request is
113
+ * cloned.
114
+ */
115
+ function cancelBodyOnError(transformer, onCancellationError = util_js_1.logCancellationError) {
60
116
  return async (response) => {
61
- if (response.ok)
62
- return response;
63
- throw await FetchResponseError.from(response, undefined, customMessage);
117
+ try {
118
+ return await transformer(response);
119
+ }
120
+ catch (err) {
121
+ await (0, util_js_1.cancelBody)(response, onCancellationError ?? undefined);
122
+ throw err;
123
+ }
64
124
  };
65
125
  }
126
+ exports.cancelBodyOnError = cancelBodyOnError;
127
+ function fetchOkProcessor(customMessage) {
128
+ return cancelBodyOnError((response) => {
129
+ return fetchOkTransformer(response, customMessage);
130
+ });
131
+ }
66
132
  exports.fetchOkProcessor = fetchOkProcessor;
133
+ async function fetchOkTransformer(response, customMessage) {
134
+ if (response.ok)
135
+ return response;
136
+ throw await FetchResponseError.from(response, customMessage);
137
+ }
138
+ exports.fetchOkTransformer = fetchOkTransformer;
67
139
  function fetchMaxSizeProcessor(maxBytes) {
68
140
  if (maxBytes === Infinity)
69
141
  return (response) => response;
70
142
  if (!Number.isFinite(maxBytes) || maxBytes < 0) {
71
- throw new TypeError('maxBytes must be a non-negative number');
143
+ throw new TypeError('maxBytes must be a 0, Infinity or a positive number');
72
144
  }
73
- return async (response) => fetchResponseMaxSize(response, maxBytes);
145
+ return cancelBodyOnError((response) => {
146
+ return fetchResponseMaxSizeChecker(response, maxBytes);
147
+ });
74
148
  }
75
149
  exports.fetchMaxSizeProcessor = fetchMaxSizeProcessor;
76
- async function fetchResponseMaxSize(response, maxBytes) {
150
+ function fetchResponseMaxSizeChecker(response, maxBytes) {
77
151
  if (maxBytes === Infinity)
78
152
  return response;
153
+ checkLength(response, maxBytes);
154
+ // Some engines (react-native 👀) don't expose a body property. In that case,
155
+ // we will only rely on the Content-Length header.
79
156
  if (!response.body)
80
157
  return response;
81
- const contentLength = response.headers.get('content-length');
82
- if (contentLength) {
83
- const length = Number(contentLength);
84
- if (!(length < maxBytes)) {
85
- const err = new FetchResponseError(response, 502, 'Response too large');
86
- await response.body.cancel(err);
87
- throw err;
88
- }
89
- }
90
- let bytesRead = 0;
91
- const transform = new TransformStream({
92
- transform: (chunk, ctrl) => {
93
- if ((bytesRead += chunk.length) <= maxBytes) {
94
- ctrl.enqueue(chunk);
95
- }
96
- else {
97
- ctrl.error(new FetchResponseError(response, 502, 'Response too large'));
98
- }
99
- },
100
- });
158
+ const transform = new util_js_1.MaxBytesTransformStream(maxBytes);
101
159
  return new transformed_response_js_1.TransformedResponse(response, transform);
102
160
  }
103
- exports.fetchResponseMaxSize = fetchResponseMaxSize;
104
- function fetchTypeProcessor(expectedType, contentTypeRequired = true) {
105
- const isExpected = typeof expectedType === 'string'
106
- ? (ct) => ct === expectedType
107
- : expectedType instanceof RegExp
108
- ? (ct) => expectedType.test(ct)
109
- : expectedType;
110
- return async (response) => {
111
- const contentType = response.headers
112
- .get('content-type')
113
- ?.split(';')[0]
114
- .trim();
115
- if (contentType) {
116
- if (!isExpected(contentType)) {
117
- throw await FetchResponseError.from(response, 502, `Unexpected response Content-Type (${contentType})`);
118
- }
119
- }
120
- else if (contentTypeRequired) {
121
- throw await FetchResponseError.from(response, 502, 'Missing response Content-Type header');
122
- }
123
- return response;
124
- };
161
+ exports.fetchResponseMaxSizeChecker = fetchResponseMaxSizeChecker;
162
+ function fetchTypeProcessor(expectedMime, contentTypeRequired = true) {
163
+ const isExpected = typeof expectedMime === 'string'
164
+ ? (mimeType) => mimeType === expectedMime
165
+ : expectedMime instanceof RegExp
166
+ ? (mimeType) => expectedMime.test(mimeType)
167
+ : expectedMime;
168
+ return cancelBodyOnError((response) => {
169
+ return fetchResponseTypeChecker(response, isExpected, contentTypeRequired);
170
+ });
125
171
  }
126
172
  exports.fetchTypeProcessor = fetchTypeProcessor;
127
- async function jsonTranformer(response) {
128
- if (response.body === null) {
129
- throw new FetchResponseError(response, 502, 'No response body');
173
+ async function fetchResponseTypeChecker(response, isExpectedMime, contentTypeRequired = true) {
174
+ const mimeType = extractMime(response);
175
+ if (mimeType) {
176
+ if (!isExpectedMime(mimeType)) {
177
+ throw await FetchResponseError.from(response, `Unexpected response Content-Type (${mimeType})`, 502);
178
+ }
130
179
  }
131
- if (response.bodyUsed) {
132
- throw new FetchResponseError(response, 500, 'Response body already used');
180
+ else if (contentTypeRequired) {
181
+ throw await FetchResponseError.from(response, 'Missing response Content-Type header', 502);
133
182
  }
183
+ return response;
184
+ }
185
+ exports.fetchResponseTypeChecker = fetchResponseTypeChecker;
186
+ async function fetchResponseJsonTranformer(response) {
134
187
  try {
135
188
  const json = (await response.json());
136
189
  return { response, json };
@@ -139,9 +192,9 @@ async function jsonTranformer(response) {
139
192
  throw new FetchResponseError(response, 502, 'Unable to parse response as JSON', { cause });
140
193
  }
141
194
  }
142
- exports.jsonTranformer = jsonTranformer;
143
- function fetchJsonProcessor(contentType = /^application\/(?:[^+]+\+)?json$/, contentTypeRequired = true) {
144
- return (0, transformer_1.compose)(fetchTypeProcessor(contentType, contentTypeRequired), (jsonTranformer));
195
+ exports.fetchResponseJsonTranformer = fetchResponseJsonTranformer;
196
+ function fetchJsonProcessor(expectedMime = /^application\/(?:[^+]+\+)?json$/, contentTypeRequired = true) {
197
+ return (0, pipe_1.pipe)(fetchTypeProcessor(expectedMime, contentTypeRequired), cancelBodyOnError((fetchResponseJsonTranformer)));
145
198
  }
146
199
  exports.fetchJsonProcessor = fetchJsonProcessor;
147
200
  function fetchJsonZodProcessor(schema, params) {