@amaster.ai/http-client 1.0.0-beta.0 → 1.0.0-beta.2
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/README.md +0 -169
- package/dist/index.cjs +1 -102
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -100
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,170 +1 @@
|
|
|
1
1
|
# @amaster.ai/http-client
|
|
2
|
-
|
|
3
|
-
Base HTTP client with error handling and response unwrapping for backend API communication.
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
|
|
7
|
-
- 🔄 Automatic backend response unwrapping (`{ status: 0, data: {...} }` → `{...}`)
|
|
8
|
-
- 📅 Backend datetime format conversion (`"2026-01-05 10:30:45"` → `"2026-01-05T10:30:45.000Z"`)
|
|
9
|
-
- ⚠️ Consistent error handling with normalized error messages
|
|
10
|
-
- 🔌 Built on Axios with full TypeScript support
|
|
11
|
-
- 🌳 Tree-shakeable ESM/CJS builds
|
|
12
|
-
|
|
13
|
-
## Installation
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
pnpm add @amaster.ai/http-client axios
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
> **Note:** `axios` is a peer dependency and must be installed separately.
|
|
20
|
-
|
|
21
|
-
## Usage
|
|
22
|
-
|
|
23
|
-
### Basic Usage
|
|
24
|
-
|
|
25
|
-
```typescript
|
|
26
|
-
import { createHttpClient } from "@amaster.ai/http-client";
|
|
27
|
-
|
|
28
|
-
const client = createHttpClient();
|
|
29
|
-
|
|
30
|
-
const result = await client.request({
|
|
31
|
-
url: "/api/users",
|
|
32
|
-
method: "get",
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
if (result.data) {
|
|
36
|
-
console.log(result.data);
|
|
37
|
-
} else {
|
|
38
|
-
console.error(result.error.message);
|
|
39
|
-
}
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
### With Custom Axios Instance
|
|
43
|
-
|
|
44
|
-
```typescript
|
|
45
|
-
import axios from "axios";
|
|
46
|
-
import { createHttpClient } from "@amaster.ai/http-client";
|
|
47
|
-
|
|
48
|
-
const instance = axios.create({
|
|
49
|
-
baseURL: "https://api.example.com",
|
|
50
|
-
timeout: 5000,
|
|
51
|
-
headers: {
|
|
52
|
-
"Authorization": "Bearer token",
|
|
53
|
-
},
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
const client = createHttpClient(instance);
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
### Response Format
|
|
60
|
-
|
|
61
|
-
All requests return a `ClientResult<T>`:
|
|
62
|
-
|
|
63
|
-
```typescript
|
|
64
|
-
type ClientResult<T> = {
|
|
65
|
-
data: T | null; // Response data (null on error)
|
|
66
|
-
error: ClientError | null; // Error details (null on success)
|
|
67
|
-
status: number; // HTTP status code
|
|
68
|
-
};
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
### Error Handling
|
|
72
|
-
|
|
73
|
-
```typescript
|
|
74
|
-
const result = await client.request({
|
|
75
|
-
url: "/api/users/123",
|
|
76
|
-
method: "get",
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
if (result.error) {
|
|
80
|
-
console.error("Error:", result.error.message);
|
|
81
|
-
console.error("Status:", result.error.status);
|
|
82
|
-
console.error("Details:", result.error.details);
|
|
83
|
-
}
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
## Features in Detail
|
|
87
|
-
|
|
88
|
-
### Backend Response Unwrapping
|
|
89
|
-
|
|
90
|
-
The client automatically unwraps backend standard response format:
|
|
91
|
-
|
|
92
|
-
```typescript
|
|
93
|
-
// Backend returns:
|
|
94
|
-
{
|
|
95
|
-
"status": 0,
|
|
96
|
-
"data": { "id": 1, "name": "John" }
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Client returns:
|
|
100
|
-
{
|
|
101
|
-
data: { "id": 1, "name": "John" },
|
|
102
|
-
error: null,
|
|
103
|
-
status: 200
|
|
104
|
-
}
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
### Datetime Conversion
|
|
108
|
-
|
|
109
|
-
Backend datetime strings are automatically converted to ISO 8601 format:
|
|
110
|
-
|
|
111
|
-
```typescript
|
|
112
|
-
// Backend returns:
|
|
113
|
-
{
|
|
114
|
-
"createdAt": "2026-01-05 10:30:45"
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// Client returns:
|
|
118
|
-
{
|
|
119
|
-
"createdAt": "2026-01-05T10:30:45.000Z"
|
|
120
|
-
}
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
This works recursively for nested objects and arrays.
|
|
124
|
-
|
|
125
|
-
### Error Normalization
|
|
126
|
-
|
|
127
|
-
The client normalizes various backend error formats:
|
|
128
|
-
|
|
129
|
-
```typescript
|
|
130
|
-
// Backend may return any of:
|
|
131
|
-
{ "message": "Error" }
|
|
132
|
-
{ "error": "Error" }
|
|
133
|
-
{ "msg": "Error" }
|
|
134
|
-
{ "detail": "Error" }
|
|
135
|
-
|
|
136
|
-
// Client always returns:
|
|
137
|
-
{
|
|
138
|
-
error: {
|
|
139
|
-
message: "Error",
|
|
140
|
-
status: 400,
|
|
141
|
-
details: { /* original response */ }
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
## TypeScript Support
|
|
147
|
-
|
|
148
|
-
Full TypeScript support with generic types:
|
|
149
|
-
|
|
150
|
-
```typescript
|
|
151
|
-
interface User {
|
|
152
|
-
id: number;
|
|
153
|
-
name: string;
|
|
154
|
-
email: string;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const result = await client.request<User>({
|
|
158
|
-
url: "/api/users/1",
|
|
159
|
-
method: "get",
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
if (result.data) {
|
|
163
|
-
// result.data is typed as User
|
|
164
|
-
console.log(result.data.name);
|
|
165
|
-
}
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
## License
|
|
169
|
-
|
|
170
|
-
MIT
|
package/dist/index.cjs
CHANGED
|
@@ -1,103 +1,2 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
// src/http.ts
|
|
4
|
-
function normalizeErrorMessage(payload) {
|
|
5
|
-
if (!payload) {
|
|
6
|
-
return "Request failed";
|
|
7
|
-
}
|
|
8
|
-
if (typeof payload === "string") {
|
|
9
|
-
return payload;
|
|
10
|
-
}
|
|
11
|
-
if (typeof payload === "object") {
|
|
12
|
-
const errorPayload = payload;
|
|
13
|
-
return errorPayload.message || errorPayload.error || errorPayload.msg || errorPayload.detail || "Request failed";
|
|
14
|
-
}
|
|
15
|
-
return "Request failed";
|
|
16
|
-
}
|
|
17
|
-
function isBackendStandardFormat(data) {
|
|
18
|
-
if (!data || typeof data !== "object" || !("data" in data)) {
|
|
19
|
-
return false;
|
|
20
|
-
}
|
|
21
|
-
if ("status" in data) {
|
|
22
|
-
const status = data.status;
|
|
23
|
-
return status === 0 || status === 1 || status === "0" || status === "1";
|
|
24
|
-
}
|
|
25
|
-
return false;
|
|
26
|
-
}
|
|
27
|
-
function unwrapBackendResponse(responseData) {
|
|
28
|
-
if (isBackendStandardFormat(responseData)) {
|
|
29
|
-
return responseData.data;
|
|
30
|
-
}
|
|
31
|
-
return responseData;
|
|
32
|
-
}
|
|
33
|
-
var BACKEND_DATETIME_REGEX = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/;
|
|
34
|
-
function isBackendDatetime(value) {
|
|
35
|
-
return BACKEND_DATETIME_REGEX.test(value);
|
|
36
|
-
}
|
|
37
|
-
function parseBackendDatetime(value) {
|
|
38
|
-
return `${value.replace(" ", "T")}.000Z`;
|
|
39
|
-
}
|
|
40
|
-
function processResponseDates(data) {
|
|
41
|
-
if (data === null || data === void 0) {
|
|
42
|
-
return data;
|
|
43
|
-
}
|
|
44
|
-
if (Array.isArray(data)) {
|
|
45
|
-
return data.map((item) => processResponseDates(item));
|
|
46
|
-
}
|
|
47
|
-
if (typeof data === "object") {
|
|
48
|
-
const processed = {};
|
|
49
|
-
for (const [key, value] of Object.entries(data)) {
|
|
50
|
-
processed[key] = processResponseDates(value);
|
|
51
|
-
}
|
|
52
|
-
return processed;
|
|
53
|
-
}
|
|
54
|
-
if (typeof data === "string" && isBackendDatetime(data)) {
|
|
55
|
-
return parseBackendDatetime(data);
|
|
56
|
-
}
|
|
57
|
-
return data;
|
|
58
|
-
}
|
|
59
|
-
function createHttpClient(axiosInstance) {
|
|
60
|
-
let instance = axiosInstance;
|
|
61
|
-
return {
|
|
62
|
-
async request(config) {
|
|
63
|
-
if (!instance) {
|
|
64
|
-
const axios = await import('axios');
|
|
65
|
-
instance = axios.default.create();
|
|
66
|
-
}
|
|
67
|
-
try {
|
|
68
|
-
const resp = await instance(config);
|
|
69
|
-
const status = resp?.status ?? 0;
|
|
70
|
-
if (status >= 200 && status < 300) {
|
|
71
|
-
const unwrappedData = unwrapBackendResponse(resp?.data);
|
|
72
|
-
const processedData = processResponseDates(unwrappedData);
|
|
73
|
-
return { data: processedData ?? null, error: null, status };
|
|
74
|
-
}
|
|
75
|
-
return {
|
|
76
|
-
data: null,
|
|
77
|
-
error: {
|
|
78
|
-
status,
|
|
79
|
-
message: normalizeErrorMessage(resp?.data) || `HTTP ${status}`,
|
|
80
|
-
details: resp?.data
|
|
81
|
-
},
|
|
82
|
-
status
|
|
83
|
-
};
|
|
84
|
-
} catch (error_) {
|
|
85
|
-
const error = error_;
|
|
86
|
-
const status = error.response?.status || 0;
|
|
87
|
-
return {
|
|
88
|
-
data: null,
|
|
89
|
-
error: {
|
|
90
|
-
status,
|
|
91
|
-
message: normalizeErrorMessage(error.response?.data) || error.message || "Network error",
|
|
92
|
-
details: error.response?.data
|
|
93
|
-
},
|
|
94
|
-
status
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
exports.createHttpClient = createHttpClient;
|
|
102
|
-
//# sourceMappingURL=index.cjs.map
|
|
1
|
+
'use strict';function a(e){if(!e)return "Request failed";if(typeof e=="string")return e;if(typeof e=="object"){let t=e;return t.message||t.error||t.msg||t.detail||"Request failed"}return "Request failed"}function u(e){if(!e||typeof e!="object"||!("data"in e))return false;if("status"in e){let t=e.status;return t===0||t===1||t==="0"||t==="1"}return false}function l(e){return u(e)?e.data:e}var c=/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/;function f(e){return c.test(e)}function d(e){return `${e.replace(" ","T")}.000Z`}function i(e){if(e==null)return e;if(Array.isArray(e))return e.map(t=>i(t));if(typeof e=="object"){let t={};for(let[o,r]of Object.entries(e))t[o]=i(r);return t}return typeof e=="string"&&f(e)?d(e):e}function p(e){let t=e;return {async request(o){t||(t=(await import('axios')).default.create());try{let r=await t(o),n=r?.status??0;if(n>=200&&n<300){let s=l(r?.data);return {data:i(s)??null,error:null,status:n}}return {data:null,error:{status:n,message:a(r?.data)||`HTTP ${n}`,details:r?.data},status:n}}catch(r){let n=r,s=n.response?.status||0;return {data:null,error:{status:s,message:a(n.response?.data)||n.message||"Network error",details:n.response?.data},status:s}}}}}exports.createHttpClient=p;//# sourceMappingURL=index.cjs.map
|
|
103
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/http.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/http.ts"],"names":["normalizeErrorMessage","payload","errorPayload","isBackendStandardFormat","data","status","unwrapBackendResponse","responseData","BACKEND_DATETIME_REGEX","isBackendDatetime","value","parseBackendDatetime","processResponseDates","item","processed","key","createHttpClient","axiosInstance","instance","config","resp","unwrappedData","error_","error"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { AxiosInstance, AxiosRequestConfig } from \"axios\";\n\nexport type ClientError = {\n message: string;\n status?: number;\n code?: string;\n details?: unknown;\n};\n\nexport type ClientResult<T> = {\n data: T | null;\n error: ClientError | null;\n status: number;\n};\n\nexport type HttpClient = {\n request<T>(\n config: AxiosRequestConfig & {\n url: string;\n method: NonNullable<AxiosRequestConfig[\"method\"]>;\n }\n ): Promise<ClientResult<T>>;\n};\n\ninterface ErrorPayload {\n message?: string;\n error?: string;\n msg?: string;\n detail?: string;\n}\n\nfunction normalizeErrorMessage(payload: unknown): string {\n if (!payload) {\n return \"Request failed\";\n }\n if (typeof payload === \"string\") {\n return payload;\n }\n if (typeof payload === \"object\") {\n const errorPayload = payload as ErrorPayload;\n return (\n errorPayload.message ||\n errorPayload.error ||\n errorPayload.msg ||\n errorPayload.detail ||\n \"Request failed\"\n );\n }\n return \"Request failed\";\n}\n\ninterface BackendStandardResponse {\n status: number | string;\n data: unknown;\n}\n\n/**\n * Check if response data matches backend standard format: { status: 0/1, data: {...} }\n * Handles both number and string status values\n */\nfunction isBackendStandardFormat(data: unknown): data is BackendStandardResponse {\n if (!data || typeof data !== \"object\" || !(\"data\" in data)) {\n return false;\n }\n\n // Check status field - handle both number and string values\n if (\"status\" in data) {\n const status = (data as BackendStandardResponse).status;\n // Accept 0, 1, \"0\", \"1\" as valid status values\n return status === 0 || status === 1 || status === \"0\" || status === \"1\";\n }\n\n return false;\n}\n\n/**\n * Unwrap backend standard format response\n * Backend returns: { status: 0, data: {...} }\n * We extract the inner 'data' field for cleaner client usage\n */\nfunction unwrapBackendResponse<T>(responseData: unknown): T {\n if (isBackendStandardFormat(responseData)) {\n // Extract inner data field\n return responseData.data as T;\n }\n // Return as-is if not standard format (e.g., BPM/Workflow APIs)\n return responseData as T;\n}\n\n/**\n * Backend datetime format regex: \"YYYY-MM-DD HH:MM:SS\"\n */\nconst BACKEND_DATETIME_REGEX = /^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$/;\n\n/**\n * Check if a string matches backend datetime format\n */\nfunction isBackendDatetime(value: string): boolean {\n return BACKEND_DATETIME_REGEX.test(value);\n}\n\n/**\n * Convert backend datetime string to ISO 8601 format\n * \"2026-01-05 10:30:45\" → \"2026-01-05T10:30:45.000Z\"\n */\nfunction parseBackendDatetime(value: string): string {\n // Replace space with T and append Z for UTC\n return `${value.replace(\" \", \"T\")}.000Z`;\n}\n\n/**\n * Recursively process response data to convert backend datetime strings to ISO format\n * Handles nested objects and arrays\n */\nfunction processResponseDates(data: unknown): unknown {\n if (data === null || data === undefined) {\n return data;\n }\n\n // Handle arrays\n if (Array.isArray(data)) {\n return data.map((item) => processResponseDates(item));\n }\n\n // Handle objects\n if (typeof data === \"object\") {\n const processed: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(data)) {\n processed[key] = processResponseDates(value);\n }\n return processed;\n }\n\n // Handle backend datetime strings\n if (typeof data === \"string\" && isBackendDatetime(data)) {\n return parseBackendDatetime(data);\n }\n\n return data;\n}\n\n/**\n * Create an HTTP client instance\n * \n * @param axiosInstance - Optional axios instance to use (defaults to a basic axios instance)\n * @returns HttpClient with request method\n * \n * @example\n * ```typescript\n * import axios from \"axios\";\n * import { createHttpClient } from \"@amaster.ai/http-client\";\n * \n * const instance = axios.create({ baseURL: \"https://api.example.com\" });\n * const client = createHttpClient(instance);\n * \n * const result = await client.request({\n * url: \"/users\",\n * method: \"get\",\n * });\n * ```\n */\nexport function createHttpClient(axiosInstance?: AxiosInstance): HttpClient {\n // Import axios dynamically to avoid bundling it\n let instance = axiosInstance;\n \n return {\n async request<T>(\n config: AxiosRequestConfig & {\n url: string;\n method: NonNullable<AxiosRequestConfig[\"method\"]>;\n }\n ) {\n // Lazy load axios if not provided\n if (!instance) {\n const axios = await import(\"axios\");\n instance = axios.default.create();\n }\n\n try {\n const resp = await instance(config);\n const status = resp?.status ?? 0;\n\n if (status >= 200 && status < 300) {\n // Unwrap backend standard format: { status: 0, data: {...} } -> {...}\n const unwrappedData = unwrapBackendResponse<T>(resp?.data);\n // Convert backend datetime strings to ISO format\n const processedData = processResponseDates(unwrappedData);\n return { data: (processedData ?? null) as T | null, error: null, status };\n }\n\n return {\n data: null,\n error: {\n status,\n message: normalizeErrorMessage(resp?.data) || `HTTP ${status}`,\n details: resp?.data,\n },\n status,\n };\n } catch (error_: unknown) {\n // Catch network errors, timeouts, and other axios exceptions\n const error = error_ as {\n response?: { status?: number; data?: unknown };\n message?: string;\n };\n const status = error.response?.status || 0;\n return {\n data: null,\n error: {\n status,\n message:\n normalizeErrorMessage(error.response?.data) || error.message || \"Network error\",\n details: error.response?.data,\n },\n status,\n };\n }\n },\n };\n}\n"]}
|
package/dist/index.js
CHANGED
|
@@ -1,101 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
function normalizeErrorMessage(payload) {
|
|
3
|
-
if (!payload) {
|
|
4
|
-
return "Request failed";
|
|
5
|
-
}
|
|
6
|
-
if (typeof payload === "string") {
|
|
7
|
-
return payload;
|
|
8
|
-
}
|
|
9
|
-
if (typeof payload === "object") {
|
|
10
|
-
const errorPayload = payload;
|
|
11
|
-
return errorPayload.message || errorPayload.error || errorPayload.msg || errorPayload.detail || "Request failed";
|
|
12
|
-
}
|
|
13
|
-
return "Request failed";
|
|
14
|
-
}
|
|
15
|
-
function isBackendStandardFormat(data) {
|
|
16
|
-
if (!data || typeof data !== "object" || !("data" in data)) {
|
|
17
|
-
return false;
|
|
18
|
-
}
|
|
19
|
-
if ("status" in data) {
|
|
20
|
-
const status = data.status;
|
|
21
|
-
return status === 0 || status === 1 || status === "0" || status === "1";
|
|
22
|
-
}
|
|
23
|
-
return false;
|
|
24
|
-
}
|
|
25
|
-
function unwrapBackendResponse(responseData) {
|
|
26
|
-
if (isBackendStandardFormat(responseData)) {
|
|
27
|
-
return responseData.data;
|
|
28
|
-
}
|
|
29
|
-
return responseData;
|
|
30
|
-
}
|
|
31
|
-
var BACKEND_DATETIME_REGEX = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/;
|
|
32
|
-
function isBackendDatetime(value) {
|
|
33
|
-
return BACKEND_DATETIME_REGEX.test(value);
|
|
34
|
-
}
|
|
35
|
-
function parseBackendDatetime(value) {
|
|
36
|
-
return `${value.replace(" ", "T")}.000Z`;
|
|
37
|
-
}
|
|
38
|
-
function processResponseDates(data) {
|
|
39
|
-
if (data === null || data === void 0) {
|
|
40
|
-
return data;
|
|
41
|
-
}
|
|
42
|
-
if (Array.isArray(data)) {
|
|
43
|
-
return data.map((item) => processResponseDates(item));
|
|
44
|
-
}
|
|
45
|
-
if (typeof data === "object") {
|
|
46
|
-
const processed = {};
|
|
47
|
-
for (const [key, value] of Object.entries(data)) {
|
|
48
|
-
processed[key] = processResponseDates(value);
|
|
49
|
-
}
|
|
50
|
-
return processed;
|
|
51
|
-
}
|
|
52
|
-
if (typeof data === "string" && isBackendDatetime(data)) {
|
|
53
|
-
return parseBackendDatetime(data);
|
|
54
|
-
}
|
|
55
|
-
return data;
|
|
56
|
-
}
|
|
57
|
-
function createHttpClient(axiosInstance) {
|
|
58
|
-
let instance = axiosInstance;
|
|
59
|
-
return {
|
|
60
|
-
async request(config) {
|
|
61
|
-
if (!instance) {
|
|
62
|
-
const axios = await import('axios');
|
|
63
|
-
instance = axios.default.create();
|
|
64
|
-
}
|
|
65
|
-
try {
|
|
66
|
-
const resp = await instance(config);
|
|
67
|
-
const status = resp?.status ?? 0;
|
|
68
|
-
if (status >= 200 && status < 300) {
|
|
69
|
-
const unwrappedData = unwrapBackendResponse(resp?.data);
|
|
70
|
-
const processedData = processResponseDates(unwrappedData);
|
|
71
|
-
return { data: processedData ?? null, error: null, status };
|
|
72
|
-
}
|
|
73
|
-
return {
|
|
74
|
-
data: null,
|
|
75
|
-
error: {
|
|
76
|
-
status,
|
|
77
|
-
message: normalizeErrorMessage(resp?.data) || `HTTP ${status}`,
|
|
78
|
-
details: resp?.data
|
|
79
|
-
},
|
|
80
|
-
status
|
|
81
|
-
};
|
|
82
|
-
} catch (error_) {
|
|
83
|
-
const error = error_;
|
|
84
|
-
const status = error.response?.status || 0;
|
|
85
|
-
return {
|
|
86
|
-
data: null,
|
|
87
|
-
error: {
|
|
88
|
-
status,
|
|
89
|
-
message: normalizeErrorMessage(error.response?.data) || error.message || "Network error",
|
|
90
|
-
details: error.response?.data
|
|
91
|
-
},
|
|
92
|
-
status
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export { createHttpClient };
|
|
100
|
-
//# sourceMappingURL=index.js.map
|
|
1
|
+
function a(e){if(!e)return "Request failed";if(typeof e=="string")return e;if(typeof e=="object"){let t=e;return t.message||t.error||t.msg||t.detail||"Request failed"}return "Request failed"}function u(e){if(!e||typeof e!="object"||!("data"in e))return false;if("status"in e){let t=e.status;return t===0||t===1||t==="0"||t==="1"}return false}function l(e){return u(e)?e.data:e}var c=/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/;function f(e){return c.test(e)}function d(e){return `${e.replace(" ","T")}.000Z`}function i(e){if(e==null)return e;if(Array.isArray(e))return e.map(t=>i(t));if(typeof e=="object"){let t={};for(let[o,r]of Object.entries(e))t[o]=i(r);return t}return typeof e=="string"&&f(e)?d(e):e}function p(e){let t=e;return {async request(o){t||(t=(await import('axios')).default.create());try{let r=await t(o),n=r?.status??0;if(n>=200&&n<300){let s=l(r?.data);return {data:i(s)??null,error:null,status:n}}return {data:null,error:{status:n,message:a(r?.data)||`HTTP ${n}`,details:r?.data},status:n}}catch(r){let n=r,s=n.response?.status||0;return {data:null,error:{status:s,message:a(n.response?.data)||n.message||"Network error",details:n.response?.data},status:s}}}}}export{p as createHttpClient};//# sourceMappingURL=index.js.map
|
|
101
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/http.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/http.ts"],"names":["normalizeErrorMessage","payload","errorPayload","isBackendStandardFormat","data","status","unwrapBackendResponse","responseData","BACKEND_DATETIME_REGEX","isBackendDatetime","value","parseBackendDatetime","processResponseDates","item","processed","key","createHttpClient","axiosInstance","instance","config","resp","unwrappedData","error_","error"],"mappings":"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","file":"index.js","sourcesContent":["import type { AxiosInstance, AxiosRequestConfig } from \"axios\";\n\nexport type ClientError = {\n message: string;\n status?: number;\n code?: string;\n details?: unknown;\n};\n\nexport type ClientResult<T> = {\n data: T | null;\n error: ClientError | null;\n status: number;\n};\n\nexport type HttpClient = {\n request<T>(\n config: AxiosRequestConfig & {\n url: string;\n method: NonNullable<AxiosRequestConfig[\"method\"]>;\n }\n ): Promise<ClientResult<T>>;\n};\n\ninterface ErrorPayload {\n message?: string;\n error?: string;\n msg?: string;\n detail?: string;\n}\n\nfunction normalizeErrorMessage(payload: unknown): string {\n if (!payload) {\n return \"Request failed\";\n }\n if (typeof payload === \"string\") {\n return payload;\n }\n if (typeof payload === \"object\") {\n const errorPayload = payload as ErrorPayload;\n return (\n errorPayload.message ||\n errorPayload.error ||\n errorPayload.msg ||\n errorPayload.detail ||\n \"Request failed\"\n );\n }\n return \"Request failed\";\n}\n\ninterface BackendStandardResponse {\n status: number | string;\n data: unknown;\n}\n\n/**\n * Check if response data matches backend standard format: { status: 0/1, data: {...} }\n * Handles both number and string status values\n */\nfunction isBackendStandardFormat(data: unknown): data is BackendStandardResponse {\n if (!data || typeof data !== \"object\" || !(\"data\" in data)) {\n return false;\n }\n\n // Check status field - handle both number and string values\n if (\"status\" in data) {\n const status = (data as BackendStandardResponse).status;\n // Accept 0, 1, \"0\", \"1\" as valid status values\n return status === 0 || status === 1 || status === \"0\" || status === \"1\";\n }\n\n return false;\n}\n\n/**\n * Unwrap backend standard format response\n * Backend returns: { status: 0, data: {...} }\n * We extract the inner 'data' field for cleaner client usage\n */\nfunction unwrapBackendResponse<T>(responseData: unknown): T {\n if (isBackendStandardFormat(responseData)) {\n // Extract inner data field\n return responseData.data as T;\n }\n // Return as-is if not standard format (e.g., BPM/Workflow APIs)\n return responseData as T;\n}\n\n/**\n * Backend datetime format regex: \"YYYY-MM-DD HH:MM:SS\"\n */\nconst BACKEND_DATETIME_REGEX = /^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$/;\n\n/**\n * Check if a string matches backend datetime format\n */\nfunction isBackendDatetime(value: string): boolean {\n return BACKEND_DATETIME_REGEX.test(value);\n}\n\n/**\n * Convert backend datetime string to ISO 8601 format\n * \"2026-01-05 10:30:45\" → \"2026-01-05T10:30:45.000Z\"\n */\nfunction parseBackendDatetime(value: string): string {\n // Replace space with T and append Z for UTC\n return `${value.replace(\" \", \"T\")}.000Z`;\n}\n\n/**\n * Recursively process response data to convert backend datetime strings to ISO format\n * Handles nested objects and arrays\n */\nfunction processResponseDates(data: unknown): unknown {\n if (data === null || data === undefined) {\n return data;\n }\n\n // Handle arrays\n if (Array.isArray(data)) {\n return data.map((item) => processResponseDates(item));\n }\n\n // Handle objects\n if (typeof data === \"object\") {\n const processed: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(data)) {\n processed[key] = processResponseDates(value);\n }\n return processed;\n }\n\n // Handle backend datetime strings\n if (typeof data === \"string\" && isBackendDatetime(data)) {\n return parseBackendDatetime(data);\n }\n\n return data;\n}\n\n/**\n * Create an HTTP client instance\n * \n * @param axiosInstance - Optional axios instance to use (defaults to a basic axios instance)\n * @returns HttpClient with request method\n * \n * @example\n * ```typescript\n * import axios from \"axios\";\n * import { createHttpClient } from \"@amaster.ai/http-client\";\n * \n * const instance = axios.create({ baseURL: \"https://api.example.com\" });\n * const client = createHttpClient(instance);\n * \n * const result = await client.request({\n * url: \"/users\",\n * method: \"get\",\n * });\n * ```\n */\nexport function createHttpClient(axiosInstance?: AxiosInstance): HttpClient {\n // Import axios dynamically to avoid bundling it\n let instance = axiosInstance;\n \n return {\n async request<T>(\n config: AxiosRequestConfig & {\n url: string;\n method: NonNullable<AxiosRequestConfig[\"method\"]>;\n }\n ) {\n // Lazy load axios if not provided\n if (!instance) {\n const axios = await import(\"axios\");\n instance = axios.default.create();\n }\n\n try {\n const resp = await instance(config);\n const status = resp?.status ?? 0;\n\n if (status >= 200 && status < 300) {\n // Unwrap backend standard format: { status: 0, data: {...} } -> {...}\n const unwrappedData = unwrapBackendResponse<T>(resp?.data);\n // Convert backend datetime strings to ISO format\n const processedData = processResponseDates(unwrappedData);\n return { data: (processedData ?? null) as T | null, error: null, status };\n }\n\n return {\n data: null,\n error: {\n status,\n message: normalizeErrorMessage(resp?.data) || `HTTP ${status}`,\n details: resp?.data,\n },\n status,\n };\n } catch (error_: unknown) {\n // Catch network errors, timeouts, and other axios exceptions\n const error = error_ as {\n response?: { status?: number; data?: unknown };\n message?: string;\n };\n const status = error.response?.status || 0;\n return {\n data: null,\n error: {\n status,\n message:\n normalizeErrorMessage(error.response?.data) || error.message || \"Network error\",\n details: error.response?.data,\n },\n status,\n };\n }\n },\n };\n}\n"]}
|