@datocms/rest-client-utils 5.4.9 → 5.7.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.
@@ -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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"}
@@ -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, ApiError);
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
  }
@@ -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;AAqBD,MAAa,YAAa,SAAQ,KAAK;IAIrC,YAAY,UAAkC;QAC5C,KAAK,CAAC,YAAY,CAAC,CAAC;QACpB,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAElD,IAAI,mBAAmB,IAAI,KAAK,EAAE;YAChC,KAAK,CAAC,iBAAiB,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;SACzC;aAAM;YACL,IAAI,CAAC,KAAK,GAAG,IAAK,KAAa,EAAE,CAAC,KAAK,CAAC;SACzC;QAED,IAAI,CAAC,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC;QAClC,IAAI,CAAC,YAAY,GAAG,UAAU,CAAC,YAAY,CAAC;QAE5C,IAAI,CAAC,OAAO,GAAG,GAAG,UAAU,CAAC,OAAO,CAAC,MAAM,IAAI,UAAU,CAAC,OAAO,CAAC,GAAG,iBAAiB,CAAC;QAEvF,IAAI,IAAI,CAAC,YAAY,EAAE;YACrB,IAAI,CAAC,KAAK,IAAI,iBAAiB,IAAI,CAAC,YAAY,EAAE,CAAC;SACpD;IACH,CAAC;CACF;AAvBD,oCAuBC;AAQD,MAAa,QAAS,SAAQ,KAAK;IAKjC,YAAY,UAA8B;QACxC,KAAK,CAAC,YAAY,CAAC,CAAC;QACpB,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAElD,IAAI,mBAAmB,IAAI,KAAK,EAAE;YAChC,KAAK,CAAC,iBAAiB,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;SACzC;aAAM;YACL,IAAI,CAAC,KAAK,GAAG,IAAK,KAAa,EAAE,CAAC,KAAK,CAAC;SACzC;QAED,IAAI,CAAC,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC;QAClC,IAAI,CAAC,QAAQ,GAAG,UAAU,CAAC,QAAQ,CAAC;QACpC,IAAI,CAAC,YAAY,GAAG,UAAU,CAAC,YAAY,CAAC;QAE5C,IAAI,OAAO,GAAG,GAAG,UAAU,CAAC,OAAO,CAAC,MAAM,IAAI,UAAU,CAAC,OAAO,CAAC,GAAG,KAAK,IAAI,CAAC,QAAQ,CAAC,MAAM,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC;QAE5H,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE;YAC1B,OAAO,IAAI,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;SAC1D;QAED,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QAEvB,IAAI,IAAI,CAAC,YAAY,EAAE;YACrB,IAAI,CAAC,KAAK,IAAI,iBAAiB,IAAI,CAAC,YAAY,EAAE,CAAC;SACpD;IACH,CAAC;IAED,IAAI,MAAM;QACR,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE;YACpC,OAAO,EAAE,CAAC;SACX;QAED,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC;IACjC,CAAC;IAED,SAAS,CACP,WAA8B,EAC9B,aAAiD;QAEjD,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;QACvE,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CACrB,CAAC,KAAK,EAAE,EAAE,CACR,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC;YACrC,CAAC,CAAC,aAAa;gBACb,CAAC,OAAO,aAAa,KAAK,UAAU;oBAClC,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC;oBACzC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,KAAK,CACjC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,CAC1D,CAAC,CAAC,CACZ,CAAC;IACJ,CAAC;CACF;AAxDD,4BAwDC"}
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;IAC7C;QACE,KAAK,CAAC,mBAAmB,CAAC,CAAC;QAC3B,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACpD,CAAC;CACF;AALD,oDAKC;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"}
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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"}
@@ -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;
@@ -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, ApiError);
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
  }
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/errors.ts"],"names":[],"mappings":"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"}
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;IAC7C;QACE,KAAK,CAAC,mBAAmB,CAAC,CAAC;QAC3B,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACpD,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"}
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 {};
@@ -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.4.9",
3
+ "version": "5.7.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": "84f6eadac6b5bfaa8c8646e112041a361c842a46",
51
+ "gitHead": "d6d11483b6ecb2d1803f4de831ecfd638a3a8265",
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, ApiError);
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