Skip to content

Commit 52bff75

Browse files
refactor: extract shared FmcApiError base class from MarginApiError/PositionsError (#366)
Both MarginApiError (lib/margin-api.js) and PositionsError (lib/positions.js) shared an identical constructor pattern: extend Error, set this.type, set this.name. Extract the shared pattern into FmcApiError (lib/api-error.js): - this.type is set once in FmcApiError constructor - this.name is set via this.constructor.name, picking up 'MarginApiError' or 'PositionsError' automatically in each subclass - Error.cause chaining is inherited unchanged The two subclasses become empty one-liners, removing 14 lines of identical code. Any future addition to the shared constructor (e.g. a new field, a new guard) now only requires one edit rather than two identical changes in separate files. Add lib/api-error.js before lib/positions.js and lib/margin-api.js in manifest.json so FmcApiError is defined before both modules load. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 5dd948f commit 52bff75

4 files changed

Lines changed: 42 additions & 28 deletions

File tree

lib/api-error.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Shared error base class for FMC API client modules.
2+
// Both MarginApiError (lib/margin-api.js) and PositionsError (lib/positions.js)
3+
// follow the same pattern: extend Error, set this.type (one of FMC_CONSTANTS.ERROR_TYPES),
4+
// and support Error.cause chaining for stack-trace preservation.
5+
// Centralised here so adding new properties or changing the constructor signature only
6+
// requires one edit rather than two identical changes across both API modules.
7+
'use strict';
8+
9+
/**
10+
* Base class for typed API errors thrown by FMC's API client modules.
11+
* Subclasses (`MarginApiError`, `PositionsError`) inherit the `type` field and
12+
* Error.cause support; `this.name` is set automatically from the subclass constructor name.
13+
* Subclasses need no constructor of their own — they inherit this one, which sets
14+
* `this.name` to the subclass name via `this.constructor.name`.
15+
*
16+
* @extends Error
17+
*/
18+
class FmcApiError extends Error {
19+
/**
20+
* @param {string} message - Human-readable error description.
21+
* @param {string} type - One of `FMC_CONSTANTS.ERROR_TYPES`.
22+
* @param {ErrorOptions} [options] - Standard Error options; pass `{ cause: originalError }`
23+
* to chain the original fetch/parse exception so DevTools shows the full error stack.
24+
* Supported in Chrome 93+ (well within the manifest's minimum_chrome_version: 111).
25+
*/
26+
constructor(message, type, options) {
27+
super(message, options);
28+
this.type = type;
29+
// Set this.name to the concrete subclass name so stack traces read
30+
// 'MarginApiError' or 'PositionsError' rather than the generic 'Error'.
31+
// this.constructor.name resolves correctly for named class expressions.
32+
// No minification is applied (extension loaded unpacked), so names are stable.
33+
this.name = this.constructor.name;
34+
}
35+
}

lib/margin-api.js

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -220,20 +220,9 @@ fragment ImageDetails on Image {
220220
return ErrorType.CLIENT_ERROR;
221221
}
222222

223-
class MarginApiError extends Error {
224-
/**
225-
* @param {string} message - Human-readable error description.
226-
* @param {string} type - One of `FMC_CONSTANTS.ERROR_TYPES`.
227-
* @param {ErrorOptions} [options] - Standard Error options; pass `{ cause: originalError }`
228-
* to chain the original fetch/parse exception so DevTools shows the full error stack.
229-
* Supported in Chrome 93+ (well within the manifest's minimum_chrome_version: 111).
230-
*/
231-
constructor(message, type, options) {
232-
super(message, options);
233-
this.type = type;
234-
this.name = 'MarginApiError';
235-
}
236-
}
223+
// Inherits constructor, this.type assignment, and this.name = this.constructor.name
224+
// from FmcApiError (lib/api-error.js). No constructor needed here.
225+
class MarginApiError extends FmcApiError {}
237226

238227
const debugLog = makeDebugLog('[FMC-API]');
239228

lib/positions.js

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -87,20 +87,9 @@ const PositionsAPI = (() => {
8787
// RETRYABLE_TYPES) rather than being accessed via FMC_CONSTANTS inside doFetchPriceList.
8888
const MAX_RETRY_AFTER_MS = FMC_CONSTANTS.MAX_RETRY_AFTER_MS;
8989

90-
class PositionsError extends Error {
91-
/**
92-
* @param {string} message - Human-readable error description.
93-
* @param {string} type - One of `FMC_CONSTANTS.ERROR_TYPES`.
94-
* @param {ErrorOptions} [options] - Standard Error options; pass `{ cause: originalError }`
95-
* to chain the original fetch/parse exception so DevTools shows the full error stack.
96-
* Supported in Chrome 93+ (well within the manifest's minimum_chrome_version: 111).
97-
*/
98-
constructor(message, type, options) {
99-
super(message, options);
100-
this.type = type;
101-
this.name = 'PositionsError';
102-
}
103-
}
90+
// Inherits constructor, this.type assignment, and this.name = this.constructor.name
91+
// from FmcApiError (lib/api-error.js). No constructor needed here.
92+
class PositionsError extends FmcApiError {}
10493

10594
/**
10695
* Converts a raw portfolio position object from the portfolio GraphQL API into

manifest.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"lib/debug.js",
3030
"lib/retry.js",
3131
"lib/fetch-utils.js",
32+
"lib/api-error.js",
3233
"lib/positions.js",
3334
"lib/margin-api.js",
3435
"lib/margin-calc.js",

0 commit comments

Comments
 (0)