@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 +7 -5
- package/dist/fetch-error.d.ts +1 -12
- package/dist/fetch-error.d.ts.map +1 -1
- package/dist/fetch-error.js +24 -39
- package/dist/fetch-error.js.map +1 -1
- package/dist/fetch-request.d.ts +9 -5
- package/dist/fetch-request.d.ts.map +1 -1
- package/dist/fetch-request.js +39 -13
- package/dist/fetch-request.js.map +1 -1
- package/dist/fetch-response.d.ts +30 -12
- package/dist/fetch-response.d.ts.map +1 -1
- package/dist/fetch-response.js +134 -81
- package/dist/fetch-response.js.map +1 -1
- package/dist/fetch-wrap.d.ts +42 -9
- package/dist/fetch-wrap.d.ts.map +1 -1
- package/dist/fetch-wrap.js +92 -61
- package/dist/fetch-wrap.js.map +1 -1
- package/dist/fetch.d.ts +8 -1
- package/dist/fetch.d.ts.map +1 -1
- package/dist/fetch.js +13 -0
- package/dist/fetch.js.map +1 -1
- package/dist/transformed-response.d.ts.map +1 -1
- package/dist/transformed-response.js +5 -2
- package/dist/transformed-response.js.map +1 -1
- package/dist/util.d.ts +45 -14
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +115 -24
- package/dist/util.js.map +1 -1
- package/package.json +6 -5
- package/src/fetch-error.ts +26 -44
- package/src/fetch-request.ts +52 -20
- package/src/fetch-response.ts +177 -111
- package/src/fetch-wrap.ts +104 -83
- package/src/fetch.ts +38 -3
- package/src/transformed-response.ts +5 -2
- package/src/util.ts +135 -25
- package/tsconfig.build.json +8 -0
- package/tsconfig.json +2 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
# @atproto-labs/fetch
|
|
2
2
|
|
|
3
|
-
## 0.0
|
|
3
|
+
## 0.1.0
|
|
4
4
|
|
|
5
|
-
###
|
|
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
|
-
|
|
9
|
+
### Patch Changes
|
|
8
10
|
|
|
9
|
-
- Updated dependencies [[`
|
|
10
|
-
- @atproto-labs/
|
|
11
|
+
- Updated dependencies [[`a8d6c1123`](https://github.com/bluesky-social/atproto/commit/a8d6c112359f5c4c0cfbe2df63443ed275f2a646)]:
|
|
12
|
+
- @atproto-labs/pipe@0.1.0
|
package/dist/fetch-error.d.ts
CHANGED
|
@@ -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
|
-
|
|
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,
|
|
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"}
|
package/dist/fetch-error.js
CHANGED
|
@@ -1,73 +1,58 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.FetchError = void 0;
|
|
4
4
|
class FetchError extends Error {
|
|
5
|
-
constructor(statusCode, message,
|
|
6
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 [
|
|
34
|
+
return [500, err];
|
|
51
35
|
}
|
|
52
36
|
if (!(err instanceof Error)) {
|
|
53
|
-
return [
|
|
37
|
+
return [500, 'Failed to fetch'];
|
|
54
38
|
}
|
|
55
|
-
|
|
39
|
+
const code = err['code'];
|
|
40
|
+
if (typeof code === 'string') {
|
|
56
41
|
switch (true) {
|
|
57
|
-
case
|
|
58
|
-
return [
|
|
59
|
-
case
|
|
42
|
+
case code === 'ENOTFOUND':
|
|
43
|
+
return [400, 'Invalid hostname'];
|
|
44
|
+
case code === 'ECONNREFUSED':
|
|
60
45
|
return [502, 'Connection refused'];
|
|
61
|
-
case
|
|
46
|
+
case code === 'DEPTH_ZERO_SELF_SIGNED_CERT':
|
|
62
47
|
return [502, 'Self-signed certificate'];
|
|
63
|
-
case
|
|
48
|
+
case code.startsWith('ERR_TLS'):
|
|
64
49
|
return [502, 'TLS error'];
|
|
65
|
-
case
|
|
50
|
+
case code.startsWith('ECONN'):
|
|
66
51
|
return [502, 'Connection error'];
|
|
52
|
+
default:
|
|
53
|
+
return [500, `${code} error`];
|
|
67
54
|
}
|
|
68
55
|
}
|
|
69
|
-
|
|
70
|
-
return [502, err.message];
|
|
56
|
+
return [500, err.message];
|
|
71
57
|
}
|
|
72
|
-
exports.extractInfo = extractInfo;
|
|
73
58
|
//# sourceMappingURL=fetch-error.js.map
|
package/dist/fetch-error.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch-error.js","sourceRoot":"","sources":["../src/fetch-error.ts"],"names":[],"mappings":";;;
|
|
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"}
|
package/dist/fetch-request.d.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export
|
|
3
|
-
|
|
4
|
-
|
|
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>):
|
|
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,
|
|
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"}
|
package/dist/fetch-request.js
CHANGED
|
@@ -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
|
|
9
|
-
const { protocol } =
|
|
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
|
|
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
|
|
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 } =
|
|
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
|
|
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
|
|
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 (
|
|
51
|
-
const { hostname } =
|
|
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
|
|
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
|
|
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":";;;
|
|
1
|
+
{"version":3,"file":"fetch-request.js","sourceRoot":"","sources":["../src/fetch-request.ts"],"names":[],"mappings":"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"}
|
package/dist/fetch-response.d.ts
CHANGED
|
@@ -1,24 +1,42 @@
|
|
|
1
|
-
import { Transformer } from '@atproto-labs/
|
|
2
|
-
import {
|
|
3
|
-
import { FetchError
|
|
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
|
-
|
|
9
|
-
|
|
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
|
|
14
|
-
export type
|
|
15
|
-
export type
|
|
16
|
-
export declare function fetchTypeProcessor(
|
|
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
|
|
22
|
-
export declare function fetchJsonProcessor<T = Json>(
|
|
23
|
-
export declare function fetchJsonZodProcessor<S extends
|
|
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,
|
|
1
|
+
{"version":3,"file":"fetch-response.d.ts","sourceRoot":"","sources":["../src/fetch-response.ts"],"names":[],"mappings":"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"}
|
package/dist/fetch-response.js
CHANGED
|
@@ -1,17 +1,32 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.fetchJsonZodProcessor = exports.fetchJsonProcessor = exports.
|
|
4
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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.
|
|
59
|
-
function
|
|
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
|
-
|
|
62
|
-
return response;
|
|
63
|
-
|
|
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
|
|
143
|
+
throw new TypeError('maxBytes must be a 0, Infinity or a positive number');
|
|
72
144
|
}
|
|
73
|
-
return
|
|
145
|
+
return cancelBodyOnError((response) => {
|
|
146
|
+
return fetchResponseMaxSizeChecker(response, maxBytes);
|
|
147
|
+
});
|
|
74
148
|
}
|
|
75
149
|
exports.fetchMaxSizeProcessor = fetchMaxSizeProcessor;
|
|
76
|
-
|
|
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
|
|
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.
|
|
104
|
-
function fetchTypeProcessor(
|
|
105
|
-
const isExpected = typeof
|
|
106
|
-
? (
|
|
107
|
-
:
|
|
108
|
-
? (
|
|
109
|
-
:
|
|
110
|
-
return
|
|
111
|
-
|
|
112
|
-
|
|
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
|
|
128
|
-
|
|
129
|
-
|
|
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 (
|
|
132
|
-
throw
|
|
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.
|
|
143
|
-
function fetchJsonProcessor(
|
|
144
|
-
return (0,
|
|
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) {
|