@datocms/rest-client-utils 5.4.9 → 5.8.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/dist/cjs/__tests__/errors.test.js +121 -0
- package/dist/cjs/__tests__/errors.test.js.map +1 -0
- package/dist/cjs/errors.js +55 -1
- package/dist/cjs/errors.js.map +1 -1
- package/dist/cjs/makeCancelablePromise.js +28 -0
- package/dist/cjs/makeCancelablePromise.js.map +1 -1
- package/dist/esm/__tests__/errors.test.d.ts +1 -0
- package/dist/esm/__tests__/errors.test.js +119 -0
- package/dist/esm/__tests__/errors.test.js.map +1 -0
- package/dist/esm/errors.d.ts +16 -0
- package/dist/esm/errors.js +55 -1
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/makeCancelablePromise.d.ts +9 -0
- package/dist/esm/makeCancelablePromise.js +28 -0
- package/dist/esm/makeCancelablePromise.js.map +1 -1
- package/dist/types/__tests__/errors.test.d.ts +1 -0
- package/dist/types/errors.d.ts +16 -0
- package/dist/types/makeCancelablePromise.d.ts +9 -0
- package/package.json +12 -2
- package/src/__tests__/errors.test.ts +141 -0
- package/src/errors.ts +74 -1
- package/src/makeCancelablePromise.ts +41 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const errors_1 = require("../errors");
|
|
4
|
+
const makeCancelablePromise_1 = require("../makeCancelablePromise");
|
|
5
|
+
function buildApiError() {
|
|
6
|
+
return new errors_1.ApiError({
|
|
7
|
+
request: {
|
|
8
|
+
url: '/items/bad-id',
|
|
9
|
+
method: 'GET',
|
|
10
|
+
headers: {},
|
|
11
|
+
body: undefined,
|
|
12
|
+
},
|
|
13
|
+
response: {
|
|
14
|
+
status: 422,
|
|
15
|
+
statusText: 'Unprocessable Entity',
|
|
16
|
+
headers: {},
|
|
17
|
+
body: {
|
|
18
|
+
data: [
|
|
19
|
+
{
|
|
20
|
+
id: '1',
|
|
21
|
+
type: 'api_error',
|
|
22
|
+
attributes: {
|
|
23
|
+
code: 'INVALID_FIELD',
|
|
24
|
+
doc_url: 'https://www.datocms.com/docs',
|
|
25
|
+
details: { field: 'item_id' },
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
function buildTimeoutError() {
|
|
34
|
+
return new errors_1.TimeoutError({
|
|
35
|
+
request: { url: '/items', method: 'GET', headers: {}, body: undefined },
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
describe('ApiError', () => {
|
|
39
|
+
it('keeps a correct prototype chain and name', () => {
|
|
40
|
+
const error = buildApiError();
|
|
41
|
+
expect(error).toBeInstanceOf(errors_1.ApiError);
|
|
42
|
+
expect(error).toBeInstanceOf(Error);
|
|
43
|
+
expect(error.name).toBe('ApiError');
|
|
44
|
+
expect(error.constructor.name).toBe('ApiError');
|
|
45
|
+
});
|
|
46
|
+
it('exposes request, response and parsed errors', () => {
|
|
47
|
+
const error = buildApiError();
|
|
48
|
+
expect(error.response.status).toBe(422);
|
|
49
|
+
expect(error.request.url).toBe('/items/bad-id');
|
|
50
|
+
expect(error.errors).toHaveLength(1);
|
|
51
|
+
expect(error.findError('INVALID_FIELD')).toBeTruthy();
|
|
52
|
+
});
|
|
53
|
+
// These packages ship parallel CJS and ESM builds, so a bundler can load two
|
|
54
|
+
// distinct copies of this module. `instanceof` must still work for an error
|
|
55
|
+
// produced by the other copy.
|
|
56
|
+
it('recognizes an instance coming from a duplicate copy of the module', () => {
|
|
57
|
+
const fromOtherCopy = Object.assign(new Error('API Error!'), {
|
|
58
|
+
name: 'ApiError',
|
|
59
|
+
[Symbol.for('@datocms/rest-client-utils:ApiError')]: true,
|
|
60
|
+
});
|
|
61
|
+
expect(fromOtherCopy).toBeInstanceOf(errors_1.ApiError);
|
|
62
|
+
});
|
|
63
|
+
it('does not recognize unrelated values', () => {
|
|
64
|
+
expect(new Error('nope')).not.toBeInstanceOf(errors_1.ApiError);
|
|
65
|
+
expect({}).not.toBeInstanceOf(errors_1.ApiError);
|
|
66
|
+
expect(null).not.toBeInstanceOf(errors_1.ApiError);
|
|
67
|
+
expect(buildTimeoutError()).not.toBeInstanceOf(errors_1.ApiError);
|
|
68
|
+
// merely borrowing the name is not enough
|
|
69
|
+
expect(Object.assign(new Error('impostor'), { name: 'ApiError' })).not.toBeInstanceOf(errors_1.ApiError);
|
|
70
|
+
});
|
|
71
|
+
it('keeps subclass checks exact', () => {
|
|
72
|
+
class SubclassedApiError extends errors_1.ApiError {
|
|
73
|
+
}
|
|
74
|
+
const parent = buildApiError();
|
|
75
|
+
const child = new SubclassedApiError({
|
|
76
|
+
request: { url: '/items', method: 'GET', headers: {}, body: undefined },
|
|
77
|
+
response: { status: 500, statusText: 'Error', headers: {}, body: {} },
|
|
78
|
+
});
|
|
79
|
+
expect(child).toBeInstanceOf(SubclassedApiError);
|
|
80
|
+
expect(child).toBeInstanceOf(errors_1.ApiError);
|
|
81
|
+
expect(parent).not.toBeInstanceOf(SubclassedApiError);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
describe('TimeoutError', () => {
|
|
85
|
+
it('keeps a correct prototype chain and name', () => {
|
|
86
|
+
const error = buildTimeoutError();
|
|
87
|
+
expect(error).toBeInstanceOf(errors_1.TimeoutError);
|
|
88
|
+
expect(error).toBeInstanceOf(Error);
|
|
89
|
+
expect(error.name).toBe('TimeoutError');
|
|
90
|
+
});
|
|
91
|
+
it('recognizes an instance coming from a duplicate copy of the module', () => {
|
|
92
|
+
const fromOtherCopy = Object.assign(new Error('API Error!'), {
|
|
93
|
+
name: 'TimeoutError',
|
|
94
|
+
[Symbol.for('@datocms/rest-client-utils:TimeoutError')]: true,
|
|
95
|
+
});
|
|
96
|
+
expect(fromOtherCopy).toBeInstanceOf(errors_1.TimeoutError);
|
|
97
|
+
});
|
|
98
|
+
it('is not confused with an ApiError', () => {
|
|
99
|
+
expect(buildApiError()).not.toBeInstanceOf(errors_1.TimeoutError);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
describe('CanceledPromiseError', () => {
|
|
103
|
+
it('keeps a correct prototype chain and name', () => {
|
|
104
|
+
const error = new makeCancelablePromise_1.CanceledPromiseError();
|
|
105
|
+
expect(error).toBeInstanceOf(makeCancelablePromise_1.CanceledPromiseError);
|
|
106
|
+
expect(error).toBeInstanceOf(Error);
|
|
107
|
+
expect(error.name).toBe('CanceledPromiseError');
|
|
108
|
+
});
|
|
109
|
+
it('recognizes an instance coming from a duplicate copy of the module', () => {
|
|
110
|
+
const fromOtherCopy = Object.assign(new Error('Promise canceled!'), {
|
|
111
|
+
name: 'CanceledPromiseError',
|
|
112
|
+
[Symbol.for('@datocms/rest-client-utils:CanceledPromiseError')]: true,
|
|
113
|
+
});
|
|
114
|
+
expect(fromOtherCopy).toBeInstanceOf(makeCancelablePromise_1.CanceledPromiseError);
|
|
115
|
+
});
|
|
116
|
+
it('is not confused with other error types', () => {
|
|
117
|
+
expect(buildApiError()).not.toBeInstanceOf(makeCancelablePromise_1.CanceledPromiseError);
|
|
118
|
+
expect(new Error('nope')).not.toBeInstanceOf(makeCancelablePromise_1.CanceledPromiseError);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
//# sourceMappingURL=errors.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.test.js","sourceRoot":"","sources":["../../../src/__tests__/errors.test.ts"],"names":[],"mappings":"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"}
|
package/dist/cjs/errors.js
CHANGED
|
@@ -24,12 +24,46 @@ function isErrorBody(body) {
|
|
|
24
24
|
}
|
|
25
25
|
return true;
|
|
26
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* Brands stamped on error instances.
|
|
29
|
+
*
|
|
30
|
+
* `Symbol.for()` looks up the cross-realm global symbol registry, so every copy
|
|
31
|
+
* of this module — CJS or ESM, inlined by a bundler or not — resolves these to
|
|
32
|
+
* the very same symbols. Since these packages ship parallel CJS and ESM builds
|
|
33
|
+
* with no guarantee that a consumer ends up with a single copy, identity is
|
|
34
|
+
* carried by these brands rather than by class identity alone, so `instanceof`
|
|
35
|
+
* keeps working across duplicated copies.
|
|
36
|
+
*/
|
|
37
|
+
const TIMEOUT_ERROR = Symbol.for('@datocms/rest-client-utils:TimeoutError');
|
|
38
|
+
const API_ERROR = Symbol.for('@datocms/rest-client-utils:ApiError');
|
|
27
39
|
class TimeoutError extends Error {
|
|
40
|
+
static [Symbol.hasInstance](value) {
|
|
41
|
+
if (typeof value !== 'object' ||
|
|
42
|
+
value === null ||
|
|
43
|
+
!(TIMEOUT_ERROR in value)) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
// `this` is the constructor `instanceof` was invoked on: when it is a
|
|
47
|
+
// subclass, defer to a real prototype-chain check so subclasses stay exact.
|
|
48
|
+
// biome-ignore lint/complexity/noThisInStatic: dynamic `this` is required here; hardcoding the class would break subclass checks
|
|
49
|
+
const invokedOn = this;
|
|
50
|
+
if (invokedOn !== TimeoutError) {
|
|
51
|
+
return Object.prototype.isPrototypeOf.call(invokedOn.prototype, value);
|
|
52
|
+
}
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
28
55
|
constructor(initObject) {
|
|
29
56
|
super('API Error!');
|
|
30
57
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
58
|
+
Object.defineProperty(this, TIMEOUT_ERROR, {
|
|
59
|
+
value: true,
|
|
60
|
+
enumerable: false,
|
|
61
|
+
writable: false,
|
|
62
|
+
configurable: false,
|
|
63
|
+
});
|
|
64
|
+
this.name = 'TimeoutError';
|
|
31
65
|
if ('captureStackTrace' in Error) {
|
|
32
|
-
Error.captureStackTrace(this,
|
|
66
|
+
Error.captureStackTrace(this, TimeoutError);
|
|
33
67
|
}
|
|
34
68
|
else {
|
|
35
69
|
this.stack = new Error().stack;
|
|
@@ -44,9 +78,29 @@ class TimeoutError extends Error {
|
|
|
44
78
|
}
|
|
45
79
|
exports.TimeoutError = TimeoutError;
|
|
46
80
|
class ApiError extends Error {
|
|
81
|
+
static [Symbol.hasInstance](value) {
|
|
82
|
+
if (typeof value !== 'object' || value === null || !(API_ERROR in value)) {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
// `this` is the constructor `instanceof` was invoked on: when it is a
|
|
86
|
+
// subclass, defer to a real prototype-chain check so subclasses stay exact.
|
|
87
|
+
// biome-ignore lint/complexity/noThisInStatic: dynamic `this` is required here; hardcoding the class would break subclass checks
|
|
88
|
+
const invokedOn = this;
|
|
89
|
+
if (invokedOn !== ApiError) {
|
|
90
|
+
return Object.prototype.isPrototypeOf.call(invokedOn.prototype, value);
|
|
91
|
+
}
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
47
94
|
constructor(initObject) {
|
|
48
95
|
super('API Error!');
|
|
49
96
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
97
|
+
Object.defineProperty(this, API_ERROR, {
|
|
98
|
+
value: true,
|
|
99
|
+
enumerable: false,
|
|
100
|
+
writable: false,
|
|
101
|
+
configurable: false,
|
|
102
|
+
});
|
|
103
|
+
this.name = 'ApiError';
|
|
50
104
|
if ('captureStackTrace' in Error) {
|
|
51
105
|
Error.captureStackTrace(this, ApiError);
|
|
52
106
|
}
|
package/dist/cjs/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/errors.ts"],"names":[],"mappings":";;;AAcA,SAAS,WAAW,CAAC,IAAa;IAChC,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,IAAI,CAAC,CAAC,MAAM,IAAI,IAAI,CAAC,EAAE;QAClE,OAAO,KAAK,CAAC;KACd;IAED,MAAM,YAAY,GAAG,IAAyB,CAAC;IAE/C,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE;QACrC,OAAO,KAAK,CAAC;KACd;IAED,MAAM,gBAAgB,GAAG,YAAmC,CAAC;IAE7D,IAAI,gBAAgB,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE;QACtC,OAAO,KAAK,CAAC;KACd;IAED,MAAM,OAAO,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAEzC,IACE,OAAO,OAAO,KAAK,QAAQ;QAC3B,OAAO,KAAK,IAAI;QAChB,CAAC,CAAC,IAAI,IAAI,OAAO,CAAC;QAClB,CAAC,CAAC,MAAM,IAAI,OAAO,CAAC;QACpB,CAAC,CAAC,YAAY,IAAI,OAAO,CAAC;QACzB,OAAuB,CAAC,IAAI,KAAK,WAAW,EAC7C;QACA,OAAO,KAAK,CAAC;KACd;IAED,OAAO,IAAI,CAAC;AACd,CAAC;
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/errors.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,10 +1,38 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.makeCancelablePromise = exports.CanceledPromiseError = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* See the note on the brands in `errors.ts`: identity is carried by a symbol
|
|
6
|
+
* from the cross-realm global registry so that `instanceof` keeps working even
|
|
7
|
+
* when a bundler ends up loading more than one copy of this module.
|
|
8
|
+
*/
|
|
9
|
+
const CANCELED_PROMISE_ERROR = Symbol.for('@datocms/rest-client-utils:CanceledPromiseError');
|
|
4
10
|
class CanceledPromiseError extends Error {
|
|
11
|
+
static [Symbol.hasInstance](value) {
|
|
12
|
+
if (typeof value !== 'object' ||
|
|
13
|
+
value === null ||
|
|
14
|
+
!(CANCELED_PROMISE_ERROR in value)) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
// `this` is the constructor `instanceof` was invoked on: when it is a
|
|
18
|
+
// subclass, defer to a real prototype-chain check so subclasses stay exact.
|
|
19
|
+
// biome-ignore lint/complexity/noThisInStatic: dynamic `this` is required here; hardcoding the class would break subclass checks
|
|
20
|
+
const invokedOn = this;
|
|
21
|
+
if (invokedOn !== CanceledPromiseError) {
|
|
22
|
+
return Object.prototype.isPrototypeOf.call(invokedOn.prototype, value);
|
|
23
|
+
}
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
5
26
|
constructor() {
|
|
6
27
|
super('Promise canceled!');
|
|
7
28
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
29
|
+
Object.defineProperty(this, CANCELED_PROMISE_ERROR, {
|
|
30
|
+
value: true,
|
|
31
|
+
enumerable: false,
|
|
32
|
+
writable: false,
|
|
33
|
+
configurable: false,
|
|
34
|
+
});
|
|
35
|
+
this.name = 'CanceledPromiseError';
|
|
8
36
|
}
|
|
9
37
|
}
|
|
10
38
|
exports.CanceledPromiseError = CanceledPromiseError;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"makeCancelablePromise.js","sourceRoot":"","sources":["../../src/makeCancelablePromise.ts"],"names":[],"mappings":";;;AAAA,MAAa,oBAAqB,SAAQ,KAAK;
|
|
1
|
+
{"version":3,"file":"makeCancelablePromise.js","sourceRoot":"","sources":["../../src/makeCancelablePromise.ts"],"names":[],"mappings":";;;AAAA;;;;GAIG;AACH,MAAM,sBAAsB,GAAG,MAAM,CAAC,GAAG,CACvC,iDAAiD,CAClD,CAAC;AAEF,MAAa,oBAAqB,SAAQ,KAAK;IAG7C,MAAM,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,KAAc;QACxC,IACE,OAAO,KAAK,KAAK,QAAQ;YACzB,KAAK,KAAK,IAAI;YACd,CAAC,CAAC,sBAAsB,IAAI,KAAK,CAAC,EAClC;YACA,OAAO,KAAK,CAAC;SACd;QAED,sEAAsE;QACtE,4EAA4E;QAC5E,iIAAiI;QACjI,MAAM,SAAS,GAAG,IAAwC,CAAC;QAE3D,IAAI,SAAS,KAAK,oBAAoB,EAAE;YACtC,OAAO,MAAM,CAAC,SAAS,CAAC,aAAa,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;SACxE;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAED;QACE,KAAK,CAAC,mBAAmB,CAAC,CAAC;QAC3B,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAElD,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,sBAAsB,EAAE;YAClD,KAAK,EAAE,IAAI;YACX,UAAU,EAAE,KAAK;YACjB,QAAQ,EAAE,KAAK;YACf,YAAY,EAAE,KAAK;SACpB,CAAC,CAAC;QAEH,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;IACrC,CAAC;CACF;AArCD,oDAqCC;AAMD,SAAgB,qBAAqB,CACnC,gBAAiD,EACjD,QAAqB;IAErB,IAAI,MAAM,GAAwB,IAAI,CAAC;IAEvC,MAAM,UAAU,GAAyB,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACvE,MAAM,GAAG,GAAG,EAAE;YACZ,IAAI;gBACF,IAAI,QAAQ,EAAE;oBACZ,QAAQ,EAAE,CAAC;iBACZ;gBACD,MAAM,CAAC,IAAI,oBAAoB,EAAE,CAAC,CAAC;aACpC;YAAC,OAAO,CAAC,EAAE;gBACV,MAAM,CAAC,CAAC,CAAC,CAAC;aACX;QACH,CAAC,CAAC;QAEF,MAAM,OAAO,GACX,OAAO,gBAAgB,KAAK,UAAU;YACpC,CAAC,CAAC,gBAAgB,EAAE;YACpB,CAAC,CAAC,gBAAgB,CAAC;QAEvB,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;IAEH,IAAI,MAAM,EAAE;QACV,UAAU,CAAC,MAAM,GAAG,MAAM,CAAC;KAC5B;IAED,OAAO,UAAU,CAAC;AACpB,CAAC;AA/BD,sDA+BC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { ApiError, TimeoutError } from '../errors';
|
|
2
|
+
import { CanceledPromiseError } from '../makeCancelablePromise';
|
|
3
|
+
function buildApiError() {
|
|
4
|
+
return new ApiError({
|
|
5
|
+
request: {
|
|
6
|
+
url: '/items/bad-id',
|
|
7
|
+
method: 'GET',
|
|
8
|
+
headers: {},
|
|
9
|
+
body: undefined,
|
|
10
|
+
},
|
|
11
|
+
response: {
|
|
12
|
+
status: 422,
|
|
13
|
+
statusText: 'Unprocessable Entity',
|
|
14
|
+
headers: {},
|
|
15
|
+
body: {
|
|
16
|
+
data: [
|
|
17
|
+
{
|
|
18
|
+
id: '1',
|
|
19
|
+
type: 'api_error',
|
|
20
|
+
attributes: {
|
|
21
|
+
code: 'INVALID_FIELD',
|
|
22
|
+
doc_url: 'https://www.datocms.com/docs',
|
|
23
|
+
details: { field: 'item_id' },
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
function buildTimeoutError() {
|
|
32
|
+
return new TimeoutError({
|
|
33
|
+
request: { url: '/items', method: 'GET', headers: {}, body: undefined },
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
describe('ApiError', () => {
|
|
37
|
+
it('keeps a correct prototype chain and name', () => {
|
|
38
|
+
const error = buildApiError();
|
|
39
|
+
expect(error).toBeInstanceOf(ApiError);
|
|
40
|
+
expect(error).toBeInstanceOf(Error);
|
|
41
|
+
expect(error.name).toBe('ApiError');
|
|
42
|
+
expect(error.constructor.name).toBe('ApiError');
|
|
43
|
+
});
|
|
44
|
+
it('exposes request, response and parsed errors', () => {
|
|
45
|
+
const error = buildApiError();
|
|
46
|
+
expect(error.response.status).toBe(422);
|
|
47
|
+
expect(error.request.url).toBe('/items/bad-id');
|
|
48
|
+
expect(error.errors).toHaveLength(1);
|
|
49
|
+
expect(error.findError('INVALID_FIELD')).toBeTruthy();
|
|
50
|
+
});
|
|
51
|
+
// These packages ship parallel CJS and ESM builds, so a bundler can load two
|
|
52
|
+
// distinct copies of this module. `instanceof` must still work for an error
|
|
53
|
+
// produced by the other copy.
|
|
54
|
+
it('recognizes an instance coming from a duplicate copy of the module', () => {
|
|
55
|
+
const fromOtherCopy = Object.assign(new Error('API Error!'), {
|
|
56
|
+
name: 'ApiError',
|
|
57
|
+
[Symbol.for('@datocms/rest-client-utils:ApiError')]: true,
|
|
58
|
+
});
|
|
59
|
+
expect(fromOtherCopy).toBeInstanceOf(ApiError);
|
|
60
|
+
});
|
|
61
|
+
it('does not recognize unrelated values', () => {
|
|
62
|
+
expect(new Error('nope')).not.toBeInstanceOf(ApiError);
|
|
63
|
+
expect({}).not.toBeInstanceOf(ApiError);
|
|
64
|
+
expect(null).not.toBeInstanceOf(ApiError);
|
|
65
|
+
expect(buildTimeoutError()).not.toBeInstanceOf(ApiError);
|
|
66
|
+
// merely borrowing the name is not enough
|
|
67
|
+
expect(Object.assign(new Error('impostor'), { name: 'ApiError' })).not.toBeInstanceOf(ApiError);
|
|
68
|
+
});
|
|
69
|
+
it('keeps subclass checks exact', () => {
|
|
70
|
+
class SubclassedApiError extends ApiError {
|
|
71
|
+
}
|
|
72
|
+
const parent = buildApiError();
|
|
73
|
+
const child = new SubclassedApiError({
|
|
74
|
+
request: { url: '/items', method: 'GET', headers: {}, body: undefined },
|
|
75
|
+
response: { status: 500, statusText: 'Error', headers: {}, body: {} },
|
|
76
|
+
});
|
|
77
|
+
expect(child).toBeInstanceOf(SubclassedApiError);
|
|
78
|
+
expect(child).toBeInstanceOf(ApiError);
|
|
79
|
+
expect(parent).not.toBeInstanceOf(SubclassedApiError);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
describe('TimeoutError', () => {
|
|
83
|
+
it('keeps a correct prototype chain and name', () => {
|
|
84
|
+
const error = buildTimeoutError();
|
|
85
|
+
expect(error).toBeInstanceOf(TimeoutError);
|
|
86
|
+
expect(error).toBeInstanceOf(Error);
|
|
87
|
+
expect(error.name).toBe('TimeoutError');
|
|
88
|
+
});
|
|
89
|
+
it('recognizes an instance coming from a duplicate copy of the module', () => {
|
|
90
|
+
const fromOtherCopy = Object.assign(new Error('API Error!'), {
|
|
91
|
+
name: 'TimeoutError',
|
|
92
|
+
[Symbol.for('@datocms/rest-client-utils:TimeoutError')]: true,
|
|
93
|
+
});
|
|
94
|
+
expect(fromOtherCopy).toBeInstanceOf(TimeoutError);
|
|
95
|
+
});
|
|
96
|
+
it('is not confused with an ApiError', () => {
|
|
97
|
+
expect(buildApiError()).not.toBeInstanceOf(TimeoutError);
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
describe('CanceledPromiseError', () => {
|
|
101
|
+
it('keeps a correct prototype chain and name', () => {
|
|
102
|
+
const error = new CanceledPromiseError();
|
|
103
|
+
expect(error).toBeInstanceOf(CanceledPromiseError);
|
|
104
|
+
expect(error).toBeInstanceOf(Error);
|
|
105
|
+
expect(error.name).toBe('CanceledPromiseError');
|
|
106
|
+
});
|
|
107
|
+
it('recognizes an instance coming from a duplicate copy of the module', () => {
|
|
108
|
+
const fromOtherCopy = Object.assign(new Error('Promise canceled!'), {
|
|
109
|
+
name: 'CanceledPromiseError',
|
|
110
|
+
[Symbol.for('@datocms/rest-client-utils:CanceledPromiseError')]: true,
|
|
111
|
+
});
|
|
112
|
+
expect(fromOtherCopy).toBeInstanceOf(CanceledPromiseError);
|
|
113
|
+
});
|
|
114
|
+
it('is not confused with other error types', () => {
|
|
115
|
+
expect(buildApiError()).not.toBeInstanceOf(CanceledPromiseError);
|
|
116
|
+
expect(new Error('nope')).not.toBeInstanceOf(CanceledPromiseError);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
//# sourceMappingURL=errors.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.test.js","sourceRoot":"","sources":["../../../src/__tests__/errors.test.ts"],"names":[],"mappings":"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"}
|
package/dist/esm/errors.d.ts
CHANGED
|
@@ -21,6 +21,18 @@ export type ApiErrorResponse = {
|
|
|
21
21
|
headers: Record<string, string>;
|
|
22
22
|
body?: unknown;
|
|
23
23
|
};
|
|
24
|
+
/**
|
|
25
|
+
* Brands stamped on error instances.
|
|
26
|
+
*
|
|
27
|
+
* `Symbol.for()` looks up the cross-realm global symbol registry, so every copy
|
|
28
|
+
* of this module — CJS or ESM, inlined by a bundler or not — resolves these to
|
|
29
|
+
* the very same symbols. Since these packages ship parallel CJS and ESM builds
|
|
30
|
+
* with no guarantee that a consumer ends up with a single copy, identity is
|
|
31
|
+
* carried by these brands rather than by class identity alone, so `instanceof`
|
|
32
|
+
* keeps working across duplicated copies.
|
|
33
|
+
*/
|
|
34
|
+
declare const TIMEOUT_ERROR: unique symbol;
|
|
35
|
+
declare const API_ERROR: unique symbol;
|
|
24
36
|
export type TimeoutErrorInitObject = {
|
|
25
37
|
request: ApiErrorRequest;
|
|
26
38
|
preCallStack?: string;
|
|
@@ -28,6 +40,8 @@ export type TimeoutErrorInitObject = {
|
|
|
28
40
|
export declare class TimeoutError extends Error {
|
|
29
41
|
request: ApiErrorRequest;
|
|
30
42
|
preCallStack?: string;
|
|
43
|
+
readonly [TIMEOUT_ERROR]: true;
|
|
44
|
+
static [Symbol.hasInstance](value: unknown): value is TimeoutError;
|
|
31
45
|
constructor(initObject: TimeoutErrorInitObject);
|
|
32
46
|
}
|
|
33
47
|
export type ApiErrorInitObject = {
|
|
@@ -39,6 +53,8 @@ export declare class ApiError extends Error {
|
|
|
39
53
|
request: ApiErrorRequest;
|
|
40
54
|
response: ApiErrorResponse;
|
|
41
55
|
preCallStack?: string;
|
|
56
|
+
readonly [API_ERROR]: true;
|
|
57
|
+
static [Symbol.hasInstance](value: unknown): value is ApiError;
|
|
42
58
|
constructor(initObject: ApiErrorInitObject);
|
|
43
59
|
get errors(): ErrorEntity[];
|
|
44
60
|
findError(codeOrCodes: string | string[], filterDetails?: Record<string, string> | FilterFn): ErrorEntity | undefined;
|
package/dist/esm/errors.js
CHANGED
|
@@ -21,12 +21,46 @@ function isErrorBody(body) {
|
|
|
21
21
|
}
|
|
22
22
|
return true;
|
|
23
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* Brands stamped on error instances.
|
|
26
|
+
*
|
|
27
|
+
* `Symbol.for()` looks up the cross-realm global symbol registry, so every copy
|
|
28
|
+
* of this module — CJS or ESM, inlined by a bundler or not — resolves these to
|
|
29
|
+
* the very same symbols. Since these packages ship parallel CJS and ESM builds
|
|
30
|
+
* with no guarantee that a consumer ends up with a single copy, identity is
|
|
31
|
+
* carried by these brands rather than by class identity alone, so `instanceof`
|
|
32
|
+
* keeps working across duplicated copies.
|
|
33
|
+
*/
|
|
34
|
+
const TIMEOUT_ERROR = Symbol.for('@datocms/rest-client-utils:TimeoutError');
|
|
35
|
+
const API_ERROR = Symbol.for('@datocms/rest-client-utils:ApiError');
|
|
24
36
|
export class TimeoutError extends Error {
|
|
37
|
+
static [Symbol.hasInstance](value) {
|
|
38
|
+
if (typeof value !== 'object' ||
|
|
39
|
+
value === null ||
|
|
40
|
+
!(TIMEOUT_ERROR in value)) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
// `this` is the constructor `instanceof` was invoked on: when it is a
|
|
44
|
+
// subclass, defer to a real prototype-chain check so subclasses stay exact.
|
|
45
|
+
// biome-ignore lint/complexity/noThisInStatic: dynamic `this` is required here; hardcoding the class would break subclass checks
|
|
46
|
+
const invokedOn = this;
|
|
47
|
+
if (invokedOn !== TimeoutError) {
|
|
48
|
+
return Object.prototype.isPrototypeOf.call(invokedOn.prototype, value);
|
|
49
|
+
}
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
25
52
|
constructor(initObject) {
|
|
26
53
|
super('API Error!');
|
|
27
54
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
55
|
+
Object.defineProperty(this, TIMEOUT_ERROR, {
|
|
56
|
+
value: true,
|
|
57
|
+
enumerable: false,
|
|
58
|
+
writable: false,
|
|
59
|
+
configurable: false,
|
|
60
|
+
});
|
|
61
|
+
this.name = 'TimeoutError';
|
|
28
62
|
if ('captureStackTrace' in Error) {
|
|
29
|
-
Error.captureStackTrace(this,
|
|
63
|
+
Error.captureStackTrace(this, TimeoutError);
|
|
30
64
|
}
|
|
31
65
|
else {
|
|
32
66
|
this.stack = new Error().stack;
|
|
@@ -40,9 +74,29 @@ export class TimeoutError extends Error {
|
|
|
40
74
|
}
|
|
41
75
|
}
|
|
42
76
|
export class ApiError extends Error {
|
|
77
|
+
static [Symbol.hasInstance](value) {
|
|
78
|
+
if (typeof value !== 'object' || value === null || !(API_ERROR in value)) {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
// `this` is the constructor `instanceof` was invoked on: when it is a
|
|
82
|
+
// subclass, defer to a real prototype-chain check so subclasses stay exact.
|
|
83
|
+
// biome-ignore lint/complexity/noThisInStatic: dynamic `this` is required here; hardcoding the class would break subclass checks
|
|
84
|
+
const invokedOn = this;
|
|
85
|
+
if (invokedOn !== ApiError) {
|
|
86
|
+
return Object.prototype.isPrototypeOf.call(invokedOn.prototype, value);
|
|
87
|
+
}
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
43
90
|
constructor(initObject) {
|
|
44
91
|
super('API Error!');
|
|
45
92
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
93
|
+
Object.defineProperty(this, API_ERROR, {
|
|
94
|
+
value: true,
|
|
95
|
+
enumerable: false,
|
|
96
|
+
writable: false,
|
|
97
|
+
configurable: false,
|
|
98
|
+
});
|
|
99
|
+
this.name = 'ApiError';
|
|
46
100
|
if ('captureStackTrace' in Error) {
|
|
47
101
|
Error.captureStackTrace(this, ApiError);
|
|
48
102
|
}
|
package/dist/esm/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/errors.ts"],"names":[],"mappings":"AAcA,SAAS,WAAW,CAAC,IAAa;IAChC,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,IAAI,CAAC,CAAC,MAAM,IAAI,IAAI,CAAC,EAAE;QAClE,OAAO,KAAK,CAAC;KACd;IAED,MAAM,YAAY,GAAG,IAAyB,CAAC;IAE/C,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE;QACrC,OAAO,KAAK,CAAC;KACd;IAED,MAAM,gBAAgB,GAAG,YAAmC,CAAC;IAE7D,IAAI,gBAAgB,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE;QACtC,OAAO,KAAK,CAAC;KACd;IAED,MAAM,OAAO,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAEzC,IACE,OAAO,OAAO,KAAK,QAAQ;QAC3B,OAAO,KAAK,IAAI;QAChB,CAAC,CAAC,IAAI,IAAI,OAAO,CAAC;QAClB,CAAC,CAAC,MAAM,IAAI,OAAO,CAAC;QACpB,CAAC,CAAC,YAAY,IAAI,OAAO,CAAC;QACzB,OAAuB,CAAC,IAAI,KAAK,WAAW,EAC7C;QACA,OAAO,KAAK,CAAC;KACd;IAED,OAAO,IAAI,CAAC;AACd,CAAC;
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/errors.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* See the note on the brands in `errors.ts`: identity is carried by a symbol
|
|
3
|
+
* from the cross-realm global registry so that `instanceof` keeps working even
|
|
4
|
+
* when a bundler ends up loading more than one copy of this module.
|
|
5
|
+
*/
|
|
6
|
+
declare const CANCELED_PROMISE_ERROR: unique symbol;
|
|
1
7
|
export declare class CanceledPromiseError extends Error {
|
|
8
|
+
readonly [CANCELED_PROMISE_ERROR]: true;
|
|
9
|
+
static [Symbol.hasInstance](value: unknown): value is CanceledPromiseError;
|
|
2
10
|
constructor();
|
|
3
11
|
}
|
|
4
12
|
export interface CancelablePromise<T> extends Promise<T> {
|
|
5
13
|
cancel(): void;
|
|
6
14
|
}
|
|
7
15
|
export declare function makeCancelablePromise<T>(promiseOrAsyncFn: Promise<T> | (() => Promise<T>), onCancel?: () => void): CancelablePromise<T>;
|
|
16
|
+
export {};
|
|
@@ -1,7 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* See the note on the brands in `errors.ts`: identity is carried by a symbol
|
|
3
|
+
* from the cross-realm global registry so that `instanceof` keeps working even
|
|
4
|
+
* when a bundler ends up loading more than one copy of this module.
|
|
5
|
+
*/
|
|
6
|
+
const CANCELED_PROMISE_ERROR = Symbol.for('@datocms/rest-client-utils:CanceledPromiseError');
|
|
1
7
|
export class CanceledPromiseError extends Error {
|
|
8
|
+
static [Symbol.hasInstance](value) {
|
|
9
|
+
if (typeof value !== 'object' ||
|
|
10
|
+
value === null ||
|
|
11
|
+
!(CANCELED_PROMISE_ERROR in value)) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
// `this` is the constructor `instanceof` was invoked on: when it is a
|
|
15
|
+
// subclass, defer to a real prototype-chain check so subclasses stay exact.
|
|
16
|
+
// biome-ignore lint/complexity/noThisInStatic: dynamic `this` is required here; hardcoding the class would break subclass checks
|
|
17
|
+
const invokedOn = this;
|
|
18
|
+
if (invokedOn !== CanceledPromiseError) {
|
|
19
|
+
return Object.prototype.isPrototypeOf.call(invokedOn.prototype, value);
|
|
20
|
+
}
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
2
23
|
constructor() {
|
|
3
24
|
super('Promise canceled!');
|
|
4
25
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
26
|
+
Object.defineProperty(this, CANCELED_PROMISE_ERROR, {
|
|
27
|
+
value: true,
|
|
28
|
+
enumerable: false,
|
|
29
|
+
writable: false,
|
|
30
|
+
configurable: false,
|
|
31
|
+
});
|
|
32
|
+
this.name = 'CanceledPromiseError';
|
|
5
33
|
}
|
|
6
34
|
}
|
|
7
35
|
export function makeCancelablePromise(promiseOrAsyncFn, onCancel) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"makeCancelablePromise.js","sourceRoot":"","sources":["../../src/makeCancelablePromise.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,oBAAqB,SAAQ,KAAK;
|
|
1
|
+
{"version":3,"file":"makeCancelablePromise.js","sourceRoot":"","sources":["../../src/makeCancelablePromise.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,sBAAsB,GAAG,MAAM,CAAC,GAAG,CACvC,iDAAiD,CAClD,CAAC;AAEF,MAAM,OAAO,oBAAqB,SAAQ,KAAK;IAG7C,MAAM,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,KAAc;QACxC,IACE,OAAO,KAAK,KAAK,QAAQ;YACzB,KAAK,KAAK,IAAI;YACd,CAAC,CAAC,sBAAsB,IAAI,KAAK,CAAC,EAClC;YACA,OAAO,KAAK,CAAC;SACd;QAED,sEAAsE;QACtE,4EAA4E;QAC5E,iIAAiI;QACjI,MAAM,SAAS,GAAG,IAAwC,CAAC;QAE3D,IAAI,SAAS,KAAK,oBAAoB,EAAE;YACtC,OAAO,MAAM,CAAC,SAAS,CAAC,aAAa,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;SACxE;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAED;QACE,KAAK,CAAC,mBAAmB,CAAC,CAAC;QAC3B,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAElD,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,sBAAsB,EAAE;YAClD,KAAK,EAAE,IAAI;YACX,UAAU,EAAE,KAAK;YACjB,QAAQ,EAAE,KAAK;YACf,YAAY,EAAE,KAAK;SACpB,CAAC,CAAC;QAEH,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;IACrC,CAAC;CACF;AAMD,MAAM,UAAU,qBAAqB,CACnC,gBAAiD,EACjD,QAAqB;IAErB,IAAI,MAAM,GAAwB,IAAI,CAAC;IAEvC,MAAM,UAAU,GAAyB,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACvE,MAAM,GAAG,GAAG,EAAE;YACZ,IAAI;gBACF,IAAI,QAAQ,EAAE;oBACZ,QAAQ,EAAE,CAAC;iBACZ;gBACD,MAAM,CAAC,IAAI,oBAAoB,EAAE,CAAC,CAAC;aACpC;YAAC,OAAO,CAAC,EAAE;gBACV,MAAM,CAAC,CAAC,CAAC,CAAC;aACX;QACH,CAAC,CAAC;QAEF,MAAM,OAAO,GACX,OAAO,gBAAgB,KAAK,UAAU;YACpC,CAAC,CAAC,gBAAgB,EAAE;YACpB,CAAC,CAAC,gBAAgB,CAAC;QAEvB,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;IAEH,IAAI,MAAM,EAAE;QACV,UAAU,CAAC,MAAM,GAAG,MAAM,CAAC;KAC5B;IAED,OAAO,UAAU,CAAC;AACpB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/types/errors.d.ts
CHANGED
|
@@ -21,6 +21,18 @@ export type ApiErrorResponse = {
|
|
|
21
21
|
headers: Record<string, string>;
|
|
22
22
|
body?: unknown;
|
|
23
23
|
};
|
|
24
|
+
/**
|
|
25
|
+
* Brands stamped on error instances.
|
|
26
|
+
*
|
|
27
|
+
* `Symbol.for()` looks up the cross-realm global symbol registry, so every copy
|
|
28
|
+
* of this module — CJS or ESM, inlined by a bundler or not — resolves these to
|
|
29
|
+
* the very same symbols. Since these packages ship parallel CJS and ESM builds
|
|
30
|
+
* with no guarantee that a consumer ends up with a single copy, identity is
|
|
31
|
+
* carried by these brands rather than by class identity alone, so `instanceof`
|
|
32
|
+
* keeps working across duplicated copies.
|
|
33
|
+
*/
|
|
34
|
+
declare const TIMEOUT_ERROR: unique symbol;
|
|
35
|
+
declare const API_ERROR: unique symbol;
|
|
24
36
|
export type TimeoutErrorInitObject = {
|
|
25
37
|
request: ApiErrorRequest;
|
|
26
38
|
preCallStack?: string;
|
|
@@ -28,6 +40,8 @@ export type TimeoutErrorInitObject = {
|
|
|
28
40
|
export declare class TimeoutError extends Error {
|
|
29
41
|
request: ApiErrorRequest;
|
|
30
42
|
preCallStack?: string;
|
|
43
|
+
readonly [TIMEOUT_ERROR]: true;
|
|
44
|
+
static [Symbol.hasInstance](value: unknown): value is TimeoutError;
|
|
31
45
|
constructor(initObject: TimeoutErrorInitObject);
|
|
32
46
|
}
|
|
33
47
|
export type ApiErrorInitObject = {
|
|
@@ -39,6 +53,8 @@ export declare class ApiError extends Error {
|
|
|
39
53
|
request: ApiErrorRequest;
|
|
40
54
|
response: ApiErrorResponse;
|
|
41
55
|
preCallStack?: string;
|
|
56
|
+
readonly [API_ERROR]: true;
|
|
57
|
+
static [Symbol.hasInstance](value: unknown): value is ApiError;
|
|
42
58
|
constructor(initObject: ApiErrorInitObject);
|
|
43
59
|
get errors(): ErrorEntity[];
|
|
44
60
|
findError(codeOrCodes: string | string[], filterDetails?: Record<string, string> | FilterFn): ErrorEntity | undefined;
|
|
@@ -1,7 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* See the note on the brands in `errors.ts`: identity is carried by a symbol
|
|
3
|
+
* from the cross-realm global registry so that `instanceof` keeps working even
|
|
4
|
+
* when a bundler ends up loading more than one copy of this module.
|
|
5
|
+
*/
|
|
6
|
+
declare const CANCELED_PROMISE_ERROR: unique symbol;
|
|
1
7
|
export declare class CanceledPromiseError extends Error {
|
|
8
|
+
readonly [CANCELED_PROMISE_ERROR]: true;
|
|
9
|
+
static [Symbol.hasInstance](value: unknown): value is CanceledPromiseError;
|
|
2
10
|
constructor();
|
|
3
11
|
}
|
|
4
12
|
export interface CancelablePromise<T> extends Promise<T> {
|
|
5
13
|
cancel(): void;
|
|
6
14
|
}
|
|
7
15
|
export declare function makeCancelablePromise<T>(promiseOrAsyncFn: Promise<T> | (() => Promise<T>), onCancel?: () => void): CancelablePromise<T>;
|
|
16
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datocms/rest-client-utils",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.8.0",
|
|
4
4
|
"description": "Utilities for DatoCMS REST API clients",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"datocms",
|
|
@@ -12,6 +12,16 @@
|
|
|
12
12
|
"main": "dist/cjs/index.js",
|
|
13
13
|
"module": "dist/esm/index.js",
|
|
14
14
|
"typings": "dist/types/index.d.ts",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./dist/types/index.d.ts",
|
|
18
|
+
"import": "./dist/esm/index.js",
|
|
19
|
+
"require": "./dist/cjs/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./dist/*": "./dist/*",
|
|
22
|
+
"./src/*": "./src/*",
|
|
23
|
+
"./package.json": "./package.json"
|
|
24
|
+
},
|
|
15
25
|
"sideEffects": false,
|
|
16
26
|
"directories": {
|
|
17
27
|
"lib": "dist",
|
|
@@ -38,7 +48,7 @@
|
|
|
38
48
|
"dependencies": {
|
|
39
49
|
"async-scheduler": "^1.4.4"
|
|
40
50
|
},
|
|
41
|
-
"gitHead": "
|
|
51
|
+
"gitHead": "6280620a11120a1f612a8114c2cbf879dc245599",
|
|
42
52
|
"devDependencies": {
|
|
43
53
|
"@types/qs": "^6.9.7"
|
|
44
54
|
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { ApiError, TimeoutError } from '../errors';
|
|
2
|
+
import { CanceledPromiseError } from '../makeCancelablePromise';
|
|
3
|
+
|
|
4
|
+
function buildApiError() {
|
|
5
|
+
return new ApiError({
|
|
6
|
+
request: {
|
|
7
|
+
url: '/items/bad-id',
|
|
8
|
+
method: 'GET',
|
|
9
|
+
headers: {},
|
|
10
|
+
body: undefined,
|
|
11
|
+
},
|
|
12
|
+
response: {
|
|
13
|
+
status: 422,
|
|
14
|
+
statusText: 'Unprocessable Entity',
|
|
15
|
+
headers: {},
|
|
16
|
+
body: {
|
|
17
|
+
data: [
|
|
18
|
+
{
|
|
19
|
+
id: '1',
|
|
20
|
+
type: 'api_error' as const,
|
|
21
|
+
attributes: {
|
|
22
|
+
code: 'INVALID_FIELD',
|
|
23
|
+
doc_url: 'https://www.datocms.com/docs',
|
|
24
|
+
details: { field: 'item_id' },
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function buildTimeoutError() {
|
|
34
|
+
return new TimeoutError({
|
|
35
|
+
request: { url: '/items', method: 'GET', headers: {}, body: undefined },
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
describe('ApiError', () => {
|
|
40
|
+
it('keeps a correct prototype chain and name', () => {
|
|
41
|
+
const error = buildApiError();
|
|
42
|
+
|
|
43
|
+
expect(error).toBeInstanceOf(ApiError);
|
|
44
|
+
expect(error).toBeInstanceOf(Error);
|
|
45
|
+
expect(error.name).toBe('ApiError');
|
|
46
|
+
expect(error.constructor.name).toBe('ApiError');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('exposes request, response and parsed errors', () => {
|
|
50
|
+
const error = buildApiError();
|
|
51
|
+
|
|
52
|
+
expect(error.response.status).toBe(422);
|
|
53
|
+
expect(error.request.url).toBe('/items/bad-id');
|
|
54
|
+
expect(error.errors).toHaveLength(1);
|
|
55
|
+
expect(error.findError('INVALID_FIELD')).toBeTruthy();
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// These packages ship parallel CJS and ESM builds, so a bundler can load two
|
|
59
|
+
// distinct copies of this module. `instanceof` must still work for an error
|
|
60
|
+
// produced by the other copy.
|
|
61
|
+
it('recognizes an instance coming from a duplicate copy of the module', () => {
|
|
62
|
+
const fromOtherCopy = Object.assign(new Error('API Error!'), {
|
|
63
|
+
name: 'ApiError',
|
|
64
|
+
[Symbol.for('@datocms/rest-client-utils:ApiError')]: true,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
expect(fromOtherCopy).toBeInstanceOf(ApiError);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('does not recognize unrelated values', () => {
|
|
71
|
+
expect(new Error('nope')).not.toBeInstanceOf(ApiError);
|
|
72
|
+
expect({}).not.toBeInstanceOf(ApiError);
|
|
73
|
+
expect(null).not.toBeInstanceOf(ApiError);
|
|
74
|
+
expect(buildTimeoutError()).not.toBeInstanceOf(ApiError);
|
|
75
|
+
// merely borrowing the name is not enough
|
|
76
|
+
expect(
|
|
77
|
+
Object.assign(new Error('impostor'), { name: 'ApiError' }),
|
|
78
|
+
).not.toBeInstanceOf(ApiError);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('keeps subclass checks exact', () => {
|
|
82
|
+
class SubclassedApiError extends ApiError {}
|
|
83
|
+
|
|
84
|
+
const parent = buildApiError();
|
|
85
|
+
const child = new SubclassedApiError({
|
|
86
|
+
request: { url: '/items', method: 'GET', headers: {}, body: undefined },
|
|
87
|
+
response: { status: 500, statusText: 'Error', headers: {}, body: {} },
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
expect(child).toBeInstanceOf(SubclassedApiError);
|
|
91
|
+
expect(child).toBeInstanceOf(ApiError);
|
|
92
|
+
expect(parent).not.toBeInstanceOf(SubclassedApiError);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
describe('TimeoutError', () => {
|
|
97
|
+
it('keeps a correct prototype chain and name', () => {
|
|
98
|
+
const error = buildTimeoutError();
|
|
99
|
+
|
|
100
|
+
expect(error).toBeInstanceOf(TimeoutError);
|
|
101
|
+
expect(error).toBeInstanceOf(Error);
|
|
102
|
+
expect(error.name).toBe('TimeoutError');
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('recognizes an instance coming from a duplicate copy of the module', () => {
|
|
106
|
+
const fromOtherCopy = Object.assign(new Error('API Error!'), {
|
|
107
|
+
name: 'TimeoutError',
|
|
108
|
+
[Symbol.for('@datocms/rest-client-utils:TimeoutError')]: true,
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
expect(fromOtherCopy).toBeInstanceOf(TimeoutError);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('is not confused with an ApiError', () => {
|
|
115
|
+
expect(buildApiError()).not.toBeInstanceOf(TimeoutError);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
describe('CanceledPromiseError', () => {
|
|
120
|
+
it('keeps a correct prototype chain and name', () => {
|
|
121
|
+
const error = new CanceledPromiseError();
|
|
122
|
+
|
|
123
|
+
expect(error).toBeInstanceOf(CanceledPromiseError);
|
|
124
|
+
expect(error).toBeInstanceOf(Error);
|
|
125
|
+
expect(error.name).toBe('CanceledPromiseError');
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('recognizes an instance coming from a duplicate copy of the module', () => {
|
|
129
|
+
const fromOtherCopy = Object.assign(new Error('Promise canceled!'), {
|
|
130
|
+
name: 'CanceledPromiseError',
|
|
131
|
+
[Symbol.for('@datocms/rest-client-utils:CanceledPromiseError')]: true,
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
expect(fromOtherCopy).toBeInstanceOf(CanceledPromiseError);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('is not confused with other error types', () => {
|
|
138
|
+
expect(buildApiError()).not.toBeInstanceOf(CanceledPromiseError);
|
|
139
|
+
expect(new Error('nope')).not.toBeInstanceOf(CanceledPromiseError);
|
|
140
|
+
});
|
|
141
|
+
});
|
package/src/errors.ts
CHANGED
|
@@ -59,6 +59,19 @@ export type ApiErrorResponse = {
|
|
|
59
59
|
body?: unknown;
|
|
60
60
|
};
|
|
61
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Brands stamped on error instances.
|
|
64
|
+
*
|
|
65
|
+
* `Symbol.for()` looks up the cross-realm global symbol registry, so every copy
|
|
66
|
+
* of this module — CJS or ESM, inlined by a bundler or not — resolves these to
|
|
67
|
+
* the very same symbols. Since these packages ship parallel CJS and ESM builds
|
|
68
|
+
* with no guarantee that a consumer ends up with a single copy, identity is
|
|
69
|
+
* carried by these brands rather than by class identity alone, so `instanceof`
|
|
70
|
+
* keeps working across duplicated copies.
|
|
71
|
+
*/
|
|
72
|
+
const TIMEOUT_ERROR = Symbol.for('@datocms/rest-client-utils:TimeoutError');
|
|
73
|
+
const API_ERROR = Symbol.for('@datocms/rest-client-utils:ApiError');
|
|
74
|
+
|
|
62
75
|
export type TimeoutErrorInitObject = {
|
|
63
76
|
request: ApiErrorRequest;
|
|
64
77
|
preCallStack?: string;
|
|
@@ -68,12 +81,44 @@ export class TimeoutError extends Error {
|
|
|
68
81
|
request: ApiErrorRequest;
|
|
69
82
|
preCallStack?: string;
|
|
70
83
|
|
|
84
|
+
declare readonly [TIMEOUT_ERROR]: true;
|
|
85
|
+
|
|
86
|
+
static [Symbol.hasInstance](value: unknown): value is TimeoutError {
|
|
87
|
+
if (
|
|
88
|
+
typeof value !== 'object' ||
|
|
89
|
+
value === null ||
|
|
90
|
+
!(TIMEOUT_ERROR in value)
|
|
91
|
+
) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// `this` is the constructor `instanceof` was invoked on: when it is a
|
|
96
|
+
// subclass, defer to a real prototype-chain check so subclasses stay exact.
|
|
97
|
+
// biome-ignore lint/complexity/noThisInStatic: dynamic `this` is required here; hardcoding the class would break subclass checks
|
|
98
|
+
const invokedOn = this as unknown as { prototype: object };
|
|
99
|
+
|
|
100
|
+
if (invokedOn !== TimeoutError) {
|
|
101
|
+
return Object.prototype.isPrototypeOf.call(invokedOn.prototype, value);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
|
|
71
107
|
constructor(initObject: TimeoutErrorInitObject) {
|
|
72
108
|
super('API Error!');
|
|
73
109
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
74
110
|
|
|
111
|
+
Object.defineProperty(this, TIMEOUT_ERROR, {
|
|
112
|
+
value: true,
|
|
113
|
+
enumerable: false,
|
|
114
|
+
writable: false,
|
|
115
|
+
configurable: false,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
this.name = 'TimeoutError';
|
|
119
|
+
|
|
75
120
|
if ('captureStackTrace' in Error) {
|
|
76
|
-
Error.captureStackTrace(this,
|
|
121
|
+
Error.captureStackTrace(this, TimeoutError);
|
|
77
122
|
} else {
|
|
78
123
|
this.stack = new (Error as any)().stack;
|
|
79
124
|
}
|
|
@@ -100,10 +145,38 @@ export class ApiError extends Error {
|
|
|
100
145
|
response: ApiErrorResponse;
|
|
101
146
|
preCallStack?: string;
|
|
102
147
|
|
|
148
|
+
declare readonly [API_ERROR]: true;
|
|
149
|
+
|
|
150
|
+
static [Symbol.hasInstance](value: unknown): value is ApiError {
|
|
151
|
+
if (typeof value !== 'object' || value === null || !(API_ERROR in value)) {
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// `this` is the constructor `instanceof` was invoked on: when it is a
|
|
156
|
+
// subclass, defer to a real prototype-chain check so subclasses stay exact.
|
|
157
|
+
// biome-ignore lint/complexity/noThisInStatic: dynamic `this` is required here; hardcoding the class would break subclass checks
|
|
158
|
+
const invokedOn = this as unknown as { prototype: object };
|
|
159
|
+
|
|
160
|
+
if (invokedOn !== ApiError) {
|
|
161
|
+
return Object.prototype.isPrototypeOf.call(invokedOn.prototype, value);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
|
|
103
167
|
constructor(initObject: ApiErrorInitObject) {
|
|
104
168
|
super('API Error!');
|
|
105
169
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
106
170
|
|
|
171
|
+
Object.defineProperty(this, API_ERROR, {
|
|
172
|
+
value: true,
|
|
173
|
+
enumerable: false,
|
|
174
|
+
writable: false,
|
|
175
|
+
configurable: false,
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
this.name = 'ApiError';
|
|
179
|
+
|
|
107
180
|
if ('captureStackTrace' in Error) {
|
|
108
181
|
Error.captureStackTrace(this, ApiError);
|
|
109
182
|
} else {
|
|
@@ -1,7 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* See the note on the brands in `errors.ts`: identity is carried by a symbol
|
|
3
|
+
* from the cross-realm global registry so that `instanceof` keeps working even
|
|
4
|
+
* when a bundler ends up loading more than one copy of this module.
|
|
5
|
+
*/
|
|
6
|
+
const CANCELED_PROMISE_ERROR = Symbol.for(
|
|
7
|
+
'@datocms/rest-client-utils:CanceledPromiseError',
|
|
8
|
+
);
|
|
9
|
+
|
|
1
10
|
export class CanceledPromiseError extends Error {
|
|
11
|
+
declare readonly [CANCELED_PROMISE_ERROR]: true;
|
|
12
|
+
|
|
13
|
+
static [Symbol.hasInstance](value: unknown): value is CanceledPromiseError {
|
|
14
|
+
if (
|
|
15
|
+
typeof value !== 'object' ||
|
|
16
|
+
value === null ||
|
|
17
|
+
!(CANCELED_PROMISE_ERROR in value)
|
|
18
|
+
) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// `this` is the constructor `instanceof` was invoked on: when it is a
|
|
23
|
+
// subclass, defer to a real prototype-chain check so subclasses stay exact.
|
|
24
|
+
// biome-ignore lint/complexity/noThisInStatic: dynamic `this` is required here; hardcoding the class would break subclass checks
|
|
25
|
+
const invokedOn = this as unknown as { prototype: object };
|
|
26
|
+
|
|
27
|
+
if (invokedOn !== CanceledPromiseError) {
|
|
28
|
+
return Object.prototype.isPrototypeOf.call(invokedOn.prototype, value);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
|
|
2
34
|
constructor() {
|
|
3
35
|
super('Promise canceled!');
|
|
4
36
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
37
|
+
|
|
38
|
+
Object.defineProperty(this, CANCELED_PROMISE_ERROR, {
|
|
39
|
+
value: true,
|
|
40
|
+
enumerable: false,
|
|
41
|
+
writable: false,
|
|
42
|
+
configurable: false,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
this.name = 'CanceledPromiseError';
|
|
5
46
|
}
|
|
6
47
|
}
|
|
7
48
|
|