@nomalism-com/api 0.45.63 → 0.45.65

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/index.d.ts CHANGED
@@ -1,2 +1,4 @@
1
1
  import * as Nomalism from './main';
2
+ export { ApiError, isApiError, parseApiError } from './lib/apiError';
3
+ export type { ApiErrorKind, IApiErrorFields } from './lib/apiError';
2
4
  export default Nomalism;
package/dist/index.js CHANGED
@@ -129,9 +129,85 @@ __export(main_exports, {
129
129
  Workflow: () => workflow_exports,
130
130
  ZipCode: () => zipCode_exports
131
131
  });
132
- import axios, { AxiosHeaders } from "axios";
132
+ import axios2, { AxiosHeaders } from "axios";
133
133
  import Nomalism from "@nomalism-com/types";
134
134
 
135
+ // src/lib/apiError.ts
136
+ import axios from "axios";
137
+ var RAW_ERROR = /* @__PURE__ */ Symbol("rawError");
138
+ var ApiError = class _ApiError extends Error {
139
+ constructor(fields, cause) {
140
+ super(fields.message);
141
+ this.name = "ApiError";
142
+ this.kind = fields.kind;
143
+ this.status = fields.status;
144
+ this.code = fields.code;
145
+ this.method = fields.method;
146
+ this.url = fields.url;
147
+ this.data = fields.data;
148
+ Object.defineProperty(this, RAW_ERROR, {
149
+ value: cause,
150
+ enumerable: false,
151
+ writable: false,
152
+ configurable: true
153
+ });
154
+ const captureStackTrace = Error.captureStackTrace;
155
+ captureStackTrace?.(this, _ApiError);
156
+ }
157
+ /**
158
+ * The original underlying error (e.g. the raw AxiosError) for deep debugging.
159
+ * Lives on a non-enumerable Symbol slot, so it is never printed by loggers.
160
+ */
161
+ get raw() {
162
+ return this[RAW_ERROR];
163
+ }
164
+ /** Controls what `JSON.stringify(err)` and most loggers emit — kept compact. */
165
+ toJSON() {
166
+ return {
167
+ kind: this.kind,
168
+ message: this.message,
169
+ status: this.status,
170
+ code: this.code,
171
+ method: this.method,
172
+ url: this.url,
173
+ data: this.data
174
+ };
175
+ }
176
+ };
177
+ function isApiError(error) {
178
+ return error instanceof ApiError;
179
+ }
180
+ function extractServerMessage(data, fallback) {
181
+ if (typeof data === "string" && data.trim()) return data;
182
+ if (data && typeof data === "object") {
183
+ const record = data;
184
+ const candidate = record.message ?? record.error ?? record.detail ?? record.title;
185
+ if (typeof candidate === "string" && candidate.trim()) return candidate;
186
+ }
187
+ return fallback;
188
+ }
189
+ function parseApiError(error) {
190
+ if (error instanceof ApiError) return error;
191
+ if (!axios.isAxiosError(error)) {
192
+ const message = error instanceof Error ? error.message : String(error);
193
+ return new ApiError({ kind: "unknown", message }, error);
194
+ }
195
+ const axiosError = error;
196
+ const method = axiosError.config?.method?.toUpperCase();
197
+ const url = axiosError.config?.url;
198
+ const code = axiosError.code;
199
+ if (axiosError.response) {
200
+ const { status, data } = axiosError.response;
201
+ const message = extractServerMessage(data, `Request failed with status ${status}`);
202
+ return new ApiError({ kind: "response", message, status, code, method, url, data }, error);
203
+ }
204
+ if (axiosError.request) {
205
+ const message = code === "ECONNABORTED" ? `Request timed out${url ? ` (${method} ${url})` : ""}` : `No response received${url ? ` from ${method} ${url}` : ""}`;
206
+ return new ApiError({ kind: "request", message, code, method, url }, error);
207
+ }
208
+ return new ApiError({ kind: "setup", message: axiosError.message, code, method, url }, error);
209
+ }
210
+
135
211
  // src/modules/view/webSocket.ts
136
212
  var CLOSE_AUTH_REJECTED = 4401;
137
213
  var CLOSE_AUTH_UNAVAILABLE = 4503;
@@ -4453,10 +4529,14 @@ var API = class {
4453
4529
  if (tokenBearer) {
4454
4530
  this.defaultHeaders.setAuthorization(tokenBearer);
4455
4531
  }
4456
- this.client = axios.create({
4532
+ this.client = axios2.create({
4457
4533
  baseURL: gatewayUrl,
4458
4534
  headers: this.defaultHeaders
4459
4535
  });
4536
+ this.client.interceptors.response.use(
4537
+ (response) => response,
4538
+ (error) => Promise.reject(parseApiError(error))
4539
+ );
4460
4540
  const getServicePath = (service) => {
4461
4541
  const baseUrl = services[service];
4462
4542
  const servicePath = processEnvironment === "localhost" ? "/" : `${service}/`;
@@ -4749,5 +4829,8 @@ var API = class {
4749
4829
  // src/index.ts
4750
4830
  var index_default = main_exports;
4751
4831
  export {
4752
- index_default as default
4832
+ ApiError,
4833
+ index_default as default,
4834
+ isApiError,
4835
+ parseApiError
4753
4836
  };
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Where the failure happened in the axios lifecycle.
3
+ *
4
+ * - `response` — the server replied with a non-2xx status (HTTP / application layer).
5
+ * - `request` — the request was sent but no response came back (network, timeout, CORS, offline).
6
+ * - `setup` — the request was never sent (bad config, invalid URL, serialization error).
7
+ * - `unknown` — the thrown value was not an axios error at all.
8
+ */
9
+ export type ApiErrorKind = 'response' | 'request' | 'setup' | 'unknown';
10
+ export interface IApiErrorFields {
11
+ kind: ApiErrorKind;
12
+ message: string;
13
+ status?: number;
14
+ code?: string;
15
+ method?: string;
16
+ url?: string;
17
+ data?: unknown;
18
+ }
19
+ /**
20
+ * A compact, log-friendly error that replaces axios's huge error object.
21
+ *
22
+ * Only small primitives are kept as enumerable fields, so `console.log(err)`,
23
+ * `JSON.stringify(err)` and `err.stack` stay short. The original axios error is
24
+ * preserved on a non-enumerable Symbol slot, reachable via `err.raw` for deep
25
+ * debugging, but never dumped when the error is logged.
26
+ */
27
+ export declare class ApiError extends Error {
28
+ readonly kind: ApiErrorKind;
29
+ /** HTTP status code, when the server responded. */
30
+ readonly status?: number;
31
+ /** Axios error code, e.g. 'ECONNABORTED', 'ERR_NETWORK', 'ERR_BAD_REQUEST'. */
32
+ readonly code?: string;
33
+ /** HTTP method of the failed request, uppercased. */
34
+ readonly method?: string;
35
+ /** Request URL (baseURL + path) of the failed request. */
36
+ readonly url?: string;
37
+ /** The server-provided error payload (response body), when present. */
38
+ readonly data?: unknown;
39
+ constructor(fields: IApiErrorFields, cause?: unknown);
40
+ /**
41
+ * The original underlying error (e.g. the raw AxiosError) for deep debugging.
42
+ * Lives on a non-enumerable Symbol slot, so it is never printed by loggers.
43
+ */
44
+ get raw(): unknown;
45
+ /** Controls what `JSON.stringify(err)` and most loggers emit — kept compact. */
46
+ toJSON(): IApiErrorFields;
47
+ }
48
+ /** Type guard for the simplified error. */
49
+ export declare function isApiError(error: unknown): error is ApiError;
50
+ /**
51
+ * Convert ANY thrown value into a compact {@link ApiError}.
52
+ *
53
+ * This is the single, DRY place that understands axios's four failure modes,
54
+ * so individual modules never have to inspect `error.response` / `error.request`
55
+ * themselves.
56
+ */
57
+ export declare function parseApiError(error: unknown): ApiError;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@nomalism-com/api",
3
3
  "description": "A nomalism API package for performing HTTP requests on API endpoints",
4
- "version": "0.45.63",
4
+ "version": "0.45.65",
5
5
  "author": "Nomalism <it.nomalism@gmail.com> (https://https://nomalism.com/)",
6
6
  "license": "UNLICENSED",
7
7
  "type": "module",
@@ -27,15 +27,15 @@
27
27
  "axios": "^1.17.0"
28
28
  },
29
29
  "devDependencies": {
30
- "@swc/core": "^1.15.40",
31
- "@types/node": "^24.13.1",
30
+ "@swc/core": "^1.15.41",
31
+ "@types/node": "^24.13.2",
32
32
  "@typescript-eslint/eslint-plugin": "^8.61.0",
33
33
  "@typescript-eslint/parser": "^8.61.0",
34
34
  "eslint": "^9.39.4",
35
35
  "eslint-config-prettier": "^10.1.8",
36
36
  "eslint-import-resolver-typescript": "^4.4.5",
37
37
  "eslint-plugin-prettier": "^5.5.6",
38
- "prettier": "^3.8.3",
38
+ "prettier": "^3.8.4",
39
39
  "tsup": "^8.5.1"
40
40
  },
41
41
  "repository": {