accessio 1.0.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/LICENSE +21 -0
- package/README.md +487 -0
- package/cjs/accessio.cjs +208 -0
- package/cjs/accessio.cjs.map +1 -0
- package/cjs/constants/errorCodes.cjs +76 -0
- package/cjs/constants/errorCodes.cjs.map +1 -0
- package/cjs/core/accessioError.cjs +93 -0
- package/cjs/core/accessioError.cjs.map +1 -0
- package/cjs/core/buildURL.cjs +100 -0
- package/cjs/core/buildURL.cjs.map +1 -0
- package/cjs/core/mergeConfig.cjs +73 -0
- package/cjs/core/mergeConfig.cjs.map +1 -0
- package/cjs/core/request.cjs +259 -0
- package/cjs/core/request.cjs.map +1 -0
- package/cjs/core/retry.cjs +109 -0
- package/cjs/core/retry.cjs.map +1 -0
- package/cjs/defaults/index.cjs +55 -0
- package/cjs/defaults/index.cjs.map +1 -0
- package/cjs/defaults/transforms.cjs +59 -0
- package/cjs/defaults/transforms.cjs.map +1 -0
- package/cjs/helpers/debug.cjs +96 -0
- package/cjs/helpers/debug.cjs.map +1 -0
- package/cjs/helpers/parseHeaders.cjs +52 -0
- package/cjs/helpers/parseHeaders.cjs.map +1 -0
- package/cjs/helpers/rateLimiter.cjs +98 -0
- package/cjs/helpers/rateLimiter.cjs.map +1 -0
- package/cjs/helpers/settle.cjs +50 -0
- package/cjs/helpers/settle.cjs.map +1 -0
- package/cjs/helpers/transformData.cjs +57 -0
- package/cjs/helpers/transformData.cjs.map +1 -0
- package/cjs/index.cjs +121 -0
- package/cjs/index.cjs.map +1 -0
- package/cjs/interceptors/interceptorManager.cjs +31 -0
- package/cjs/interceptors/interceptorManager.cjs.map +1 -0
- package/index.d.ts +454 -0
- package/package.json +116 -0
- package/src/accessio.ts +251 -0
- package/src/constants/errorCodes.ts +29 -0
- package/src/core/accessioError.ts +74 -0
- package/src/core/buildURL.ts +99 -0
- package/src/core/mergeConfig.ts +78 -0
- package/src/core/request.ts +284 -0
- package/src/core/retry.ts +117 -0
- package/src/defaults/index.ts +36 -0
- package/src/defaults/transforms.ts +44 -0
- package/src/helpers/debug.ts +103 -0
- package/src/helpers/parseHeaders.ts +35 -0
- package/src/helpers/rateLimiter.ts +96 -0
- package/src/helpers/settle.ts +26 -0
- package/src/helpers/transformData.ts +36 -0
- package/src/index.ts +102 -0
- package/src/interceptors/interceptorManager.ts +5 -0
- package/src/types.ts +159 -0
package/package.json
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "accessio",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Fast, flexible HTTP client for Node.js and browsers — simple, modular, and dependency-free",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./cjs/index.cjs",
|
|
7
|
+
"module": "./src/index.ts",
|
|
8
|
+
"types": "./index.d.ts",
|
|
9
|
+
"publishConfig": {
|
|
10
|
+
"access": "public",
|
|
11
|
+
"registry": "https://registry.npmjs.org/"
|
|
12
|
+
},
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./index.d.ts",
|
|
16
|
+
"import": "./src/index.ts",
|
|
17
|
+
"require": "./cjs/index.cjs"
|
|
18
|
+
},
|
|
19
|
+
"./helpers/rateLimiter": {
|
|
20
|
+
"types": "./index.d.ts",
|
|
21
|
+
"import": "./src/helpers/rateLimiter.ts",
|
|
22
|
+
"require": "./cjs/helpers/rateLimiter.cjs"
|
|
23
|
+
},
|
|
24
|
+
"./helpers/debug": {
|
|
25
|
+
"types": "./index.d.ts",
|
|
26
|
+
"import": "./src/helpers/debug.ts",
|
|
27
|
+
"require": "./cjs/helpers/debug.cjs"
|
|
28
|
+
},
|
|
29
|
+
"./core/buildURL": {
|
|
30
|
+
"types": "./index.d.ts",
|
|
31
|
+
"import": "./src/core/buildURL.ts",
|
|
32
|
+
"require": "./cjs/helpers/buildURL.cjs"
|
|
33
|
+
},
|
|
34
|
+
"./core/mergeConfig": {
|
|
35
|
+
"types": "./index.d.ts",
|
|
36
|
+
"import": "./src/core/mergeConfig.ts",
|
|
37
|
+
"require": "./cjs/core/mergeConfig.cjs"
|
|
38
|
+
},
|
|
39
|
+
"./core/request": {
|
|
40
|
+
"types": "./index.d.ts",
|
|
41
|
+
"import": "./src/core/request.ts",
|
|
42
|
+
"require": "./cjs/core/request.cjs"
|
|
43
|
+
},
|
|
44
|
+
"./core/retry": {
|
|
45
|
+
"types": "./index.d.ts",
|
|
46
|
+
"import": "./src/core/retry.ts",
|
|
47
|
+
"require": "./cjs/core/retry.cjs"
|
|
48
|
+
},
|
|
49
|
+
"./helpers/parseHeaders": {
|
|
50
|
+
"types": "./index.d.ts",
|
|
51
|
+
"import": "./src/helpers/parseHeaders.ts",
|
|
52
|
+
"require": "./cjs/helpers/parseHeaders.cjs"
|
|
53
|
+
},
|
|
54
|
+
"./helpers/settle": {
|
|
55
|
+
"types": "./index.d.ts",
|
|
56
|
+
"import": "./src/helpers/settle.ts",
|
|
57
|
+
"require": "./cjs/helpers/settle.cjs"
|
|
58
|
+
},
|
|
59
|
+
"./helpers/transformData": {
|
|
60
|
+
"types": "./index.d.ts",
|
|
61
|
+
"import": "./src/helpers/transformData.ts",
|
|
62
|
+
"require": "./cjs/helpers/transformData.cjs"
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"files": [
|
|
66
|
+
"src/",
|
|
67
|
+
"cjs/",
|
|
68
|
+
"index.d.ts",
|
|
69
|
+
"README.md",
|
|
70
|
+
"LICENSE"
|
|
71
|
+
],
|
|
72
|
+
"scripts": {
|
|
73
|
+
"build:cjs": "tsup",
|
|
74
|
+
"build": "npm run build:cjs",
|
|
75
|
+
"prepublishOnly": "npm run build",
|
|
76
|
+
"lint": "eslint .",
|
|
77
|
+
"lint:fix": "eslint . --fix",
|
|
78
|
+
"test": "vitest run",
|
|
79
|
+
"test:watch": "vitest",
|
|
80
|
+
"test:coverage": "vitest run --coverage",
|
|
81
|
+
"test:browser": "vitest run --config vitest.browser.config.js",
|
|
82
|
+
"release:npm": "gh workflow run publish-npm.yml -f publish_tag=$(git describe --tags --abbrev=0)",
|
|
83
|
+
"typecheck": "tsc --noEmit"
|
|
84
|
+
},
|
|
85
|
+
"keywords": [
|
|
86
|
+
"http",
|
|
87
|
+
"client",
|
|
88
|
+
"ajax",
|
|
89
|
+
"fetch",
|
|
90
|
+
"promise",
|
|
91
|
+
"request",
|
|
92
|
+
"accessio"
|
|
93
|
+
],
|
|
94
|
+
"author": "salvatorecorvaglia",
|
|
95
|
+
"license": "MIT",
|
|
96
|
+
"repository": {
|
|
97
|
+
"type": "git",
|
|
98
|
+
"url": "git+https://github.com/salvatorecorvaglia/accessio.git"
|
|
99
|
+
},
|
|
100
|
+
"bugs": {
|
|
101
|
+
"url": "https://github.com/salvatorecorvaglia/accessio/issues"
|
|
102
|
+
},
|
|
103
|
+
"homepage": "https://github.com/salvatorecorvaglia/accessio#readme",
|
|
104
|
+
"sideEffects": false,
|
|
105
|
+
"engines": {
|
|
106
|
+
"node": ">=18.0.0"
|
|
107
|
+
},
|
|
108
|
+
"devDependencies": {
|
|
109
|
+
"@vitest/coverage-v8": "^3.1.0",
|
|
110
|
+
"eslint": "^9.0.0",
|
|
111
|
+
"jsdom": "^29.0.2",
|
|
112
|
+
"tsup": "^8.0.0",
|
|
113
|
+
"typescript": "^5.0.0",
|
|
114
|
+
"vitest": "^3.1.0"
|
|
115
|
+
}
|
|
116
|
+
}
|
package/src/accessio.ts
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import InterceptorManager from './interceptors/interceptorManager';
|
|
2
|
+
import AccessioError from './core/accessioError';
|
|
3
|
+
import mergeConfig from './core/mergeConfig';
|
|
4
|
+
import dispatchRequest from './core/request';
|
|
5
|
+
import buildURL from './core/buildURL';
|
|
6
|
+
import retryRequest from './core/retry';
|
|
7
|
+
import { logRequest, logResponse, logError } from './helpers/debug';
|
|
8
|
+
import { rateLimitedRequest } from './helpers/rateLimiter';
|
|
9
|
+
import type {
|
|
10
|
+
AccessioRequestConfig,
|
|
11
|
+
AccessioResponse,
|
|
12
|
+
Interceptors,
|
|
13
|
+
InterceptorHandler,
|
|
14
|
+
} from './types';
|
|
15
|
+
|
|
16
|
+
export class Accessio {
|
|
17
|
+
defaults: AccessioRequestConfig;
|
|
18
|
+
interceptors: Interceptors;
|
|
19
|
+
|
|
20
|
+
constructor(instanceConfig: AccessioRequestConfig = {}) {
|
|
21
|
+
this.defaults = instanceConfig;
|
|
22
|
+
this.interceptors = {
|
|
23
|
+
request: new InterceptorManager(),
|
|
24
|
+
response: new InterceptorManager(),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
request<T = any>(
|
|
29
|
+
configOrUrl: string | AccessioRequestConfig,
|
|
30
|
+
config?: AccessioRequestConfig,
|
|
31
|
+
): Promise<AccessioResponse<T>> {
|
|
32
|
+
if (typeof configOrUrl === 'string') {
|
|
33
|
+
config = { ...config, url: configOrUrl };
|
|
34
|
+
} else {
|
|
35
|
+
config = configOrUrl ? { ...configOrUrl } : {};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const mergedConfig = mergeConfig(this.defaults, config);
|
|
39
|
+
|
|
40
|
+
mergedConfig.method = (mergedConfig.method || 'get').toLowerCase();
|
|
41
|
+
|
|
42
|
+
if (!mergedConfig.url && !mergedConfig.baseURL) {
|
|
43
|
+
throw new AccessioError(
|
|
44
|
+
'Request URL is required. Provide a `url` or `baseURL` in the config.',
|
|
45
|
+
AccessioError.ERR_BAD_OPTION,
|
|
46
|
+
mergedConfig,
|
|
47
|
+
null,
|
|
48
|
+
null,
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const requestInterceptors: any[] = [];
|
|
53
|
+
const responseInterceptors: any[] = [];
|
|
54
|
+
|
|
55
|
+
this.interceptors.request.forEach((interceptor: InterceptorHandler) => {
|
|
56
|
+
if (interceptor.runWhen && !interceptor.runWhen(mergedConfig)) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
requestInterceptors.unshift(interceptor);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
this.interceptors.response.forEach((interceptor: InterceptorHandler) => {
|
|
63
|
+
responseInterceptors.push(interceptor);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
let promise: Promise<any> = Promise.resolve(mergedConfig);
|
|
67
|
+
|
|
68
|
+
for (const interceptor of requestInterceptors) {
|
|
69
|
+
promise = promise.then(
|
|
70
|
+
(value: any) => {
|
|
71
|
+
if (interceptor.fulfilled) {
|
|
72
|
+
return interceptor.fulfilled(value);
|
|
73
|
+
}
|
|
74
|
+
return value;
|
|
75
|
+
},
|
|
76
|
+
interceptor.rejected,
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
promise = promise.then((cfg: any) => {
|
|
81
|
+
const fullUrl = buildURL(
|
|
82
|
+
cfg.url ?? '',
|
|
83
|
+
cfg.baseURL,
|
|
84
|
+
cfg.params,
|
|
85
|
+
cfg.paramsSerializer,
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
logRequest(cfg, fullUrl);
|
|
89
|
+
|
|
90
|
+
const enrichedCfg =
|
|
91
|
+
fullUrl !== (cfg.url || '')
|
|
92
|
+
? { ...cfg, _builtUrl: fullUrl }
|
|
93
|
+
: cfg;
|
|
94
|
+
|
|
95
|
+
const dispatchFn = cfg.rateLimiter
|
|
96
|
+
? (config: AccessioRequestConfig) =>
|
|
97
|
+
rateLimitedRequest(dispatchRequest, config.rateLimiter!, config)
|
|
98
|
+
: dispatchRequest;
|
|
99
|
+
|
|
100
|
+
return retryRequest(dispatchFn, enrichedCfg);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
promise = promise.then(
|
|
104
|
+
(value: any) => {
|
|
105
|
+
logResponse(value);
|
|
106
|
+
return value;
|
|
107
|
+
},
|
|
108
|
+
(error: any) => {
|
|
109
|
+
logError(error, mergedConfig);
|
|
110
|
+
throw error;
|
|
111
|
+
},
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
for (const interceptor of responseInterceptors) {
|
|
115
|
+
promise = promise.then(
|
|
116
|
+
(value: any) => {
|
|
117
|
+
if (interceptor.fulfilled) {
|
|
118
|
+
return interceptor.fulfilled(value);
|
|
119
|
+
}
|
|
120
|
+
return value;
|
|
121
|
+
},
|
|
122
|
+
interceptor.rejected,
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return promise;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
getUri(config?: AccessioRequestConfig): string {
|
|
130
|
+
const merged = mergeConfig(this.defaults, config);
|
|
131
|
+
return buildURL(
|
|
132
|
+
merged.url ?? '',
|
|
133
|
+
merged.baseURL,
|
|
134
|
+
merged.params,
|
|
135
|
+
merged.paramsSerializer,
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
get<T = any>(
|
|
140
|
+
url: string,
|
|
141
|
+
config?: AccessioRequestConfig,
|
|
142
|
+
): Promise<AccessioResponse<T>> {
|
|
143
|
+
return this.request<T>(
|
|
144
|
+
mergeConfig(config || {}, { method: 'get', url }),
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
delete<T = any>(
|
|
149
|
+
url: string,
|
|
150
|
+
config?: AccessioRequestConfig,
|
|
151
|
+
): Promise<AccessioResponse<T>> {
|
|
152
|
+
return this.request<T>(
|
|
153
|
+
mergeConfig(config || {}, { method: 'delete', url }),
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
head<T = any>(
|
|
158
|
+
url: string,
|
|
159
|
+
config?: AccessioRequestConfig,
|
|
160
|
+
): Promise<AccessioResponse<T>> {
|
|
161
|
+
return this.request<T>(
|
|
162
|
+
mergeConfig(config || {}, { method: 'head', url }),
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
options<T = any>(
|
|
167
|
+
url: string,
|
|
168
|
+
config?: AccessioRequestConfig,
|
|
169
|
+
): Promise<AccessioResponse<T>> {
|
|
170
|
+
return this.request<T>(
|
|
171
|
+
mergeConfig(config || {}, { method: 'options', url }),
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
post<T = any>(
|
|
176
|
+
url: string,
|
|
177
|
+
data?: any,
|
|
178
|
+
config?: AccessioRequestConfig,
|
|
179
|
+
): Promise<AccessioResponse<T>> {
|
|
180
|
+
return this.request<T>(
|
|
181
|
+
mergeConfig(config || {}, { method: 'post', url, data }),
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
put<T = any>(
|
|
186
|
+
url: string,
|
|
187
|
+
data?: any,
|
|
188
|
+
config?: AccessioRequestConfig,
|
|
189
|
+
): Promise<AccessioResponse<T>> {
|
|
190
|
+
return this.request<T>(
|
|
191
|
+
mergeConfig(config || {}, { method: 'put', url, data }),
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
patch<T = any>(
|
|
196
|
+
url: string,
|
|
197
|
+
data?: any,
|
|
198
|
+
config?: AccessioRequestConfig,
|
|
199
|
+
): Promise<AccessioResponse<T>> {
|
|
200
|
+
return this.request<T>(
|
|
201
|
+
mergeConfig(config || {}, { method: 'patch', url, data }),
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
postForm<T = any>(
|
|
206
|
+
url: string,
|
|
207
|
+
data?: any,
|
|
208
|
+
config?: AccessioRequestConfig,
|
|
209
|
+
): Promise<AccessioResponse<T>> {
|
|
210
|
+
return this.request<T>(
|
|
211
|
+
mergeConfig(config || {}, {
|
|
212
|
+
method: 'post',
|
|
213
|
+
url,
|
|
214
|
+
data,
|
|
215
|
+
headers: { 'Content-Type': 'multipart/form-data' },
|
|
216
|
+
}),
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
putForm<T = any>(
|
|
221
|
+
url: string,
|
|
222
|
+
data?: any,
|
|
223
|
+
config?: AccessioRequestConfig,
|
|
224
|
+
): Promise<AccessioResponse<T>> {
|
|
225
|
+
return this.request<T>(
|
|
226
|
+
mergeConfig(config || {}, {
|
|
227
|
+
method: 'put',
|
|
228
|
+
url,
|
|
229
|
+
data,
|
|
230
|
+
headers: { 'Content-Type': 'multipart/form-data' },
|
|
231
|
+
}),
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
patchForm<T = any>(
|
|
236
|
+
url: string,
|
|
237
|
+
data?: any,
|
|
238
|
+
config?: AccessioRequestConfig,
|
|
239
|
+
): Promise<AccessioResponse<T>> {
|
|
240
|
+
return this.request<T>(
|
|
241
|
+
mergeConfig(config || {}, {
|
|
242
|
+
method: 'patch',
|
|
243
|
+
url,
|
|
244
|
+
data,
|
|
245
|
+
headers: { 'Content-Type': 'multipart/form-data' },
|
|
246
|
+
}),
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export default Accessio;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const ErrorCodes = {
|
|
2
|
+
ERR_BAD_OPTION_VALUE: 'ERR_BAD_OPTION_VALUE',
|
|
3
|
+
ERR_BAD_OPTION: 'ERR_BAD_OPTION',
|
|
4
|
+
ECONNABORTED: 'ECONNABORTED',
|
|
5
|
+
ETIMEDOUT: 'ETIMEDOUT',
|
|
6
|
+
ERR_NETWORK: 'ERR_NETWORK',
|
|
7
|
+
ERR_FR_TOO_MANY_REDIRECTS: 'ERR_FR_TOO_MANY_REDIRECTS',
|
|
8
|
+
ERR_BAD_RESPONSE: 'ERR_BAD_RESPONSE',
|
|
9
|
+
ERR_BAD_REQUEST: 'ERR_BAD_REQUEST',
|
|
10
|
+
ERR_CANCELED: 'ERR_CANCELED',
|
|
11
|
+
ERR_NOT_SUPPORT: 'ERR_NOT_SUPPORT',
|
|
12
|
+
ERR_INVALID_URL: 'ERR_INVALID_URL',
|
|
13
|
+
} as const;
|
|
14
|
+
|
|
15
|
+
export default ErrorCodes;
|
|
16
|
+
|
|
17
|
+
export const {
|
|
18
|
+
ERR_BAD_OPTION_VALUE,
|
|
19
|
+
ERR_BAD_OPTION,
|
|
20
|
+
ECONNABORTED,
|
|
21
|
+
ETIMEDOUT,
|
|
22
|
+
ERR_NETWORK,
|
|
23
|
+
ERR_FR_TOO_MANY_REDIRECTS,
|
|
24
|
+
ERR_BAD_RESPONSE,
|
|
25
|
+
ERR_BAD_REQUEST,
|
|
26
|
+
ERR_CANCELED,
|
|
27
|
+
ERR_NOT_SUPPORT,
|
|
28
|
+
ERR_INVALID_URL,
|
|
29
|
+
} = ErrorCodes;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import ErrorCodes from '../constants/errorCodes';
|
|
2
|
+
import type { AccessioRequestConfig, AccessioResponse } from '../types';
|
|
3
|
+
|
|
4
|
+
export class AccessioError extends Error {
|
|
5
|
+
static ERR_BAD_OPTION_VALUE: string = ErrorCodes.ERR_BAD_OPTION_VALUE;
|
|
6
|
+
static ERR_BAD_OPTION: string = ErrorCodes.ERR_BAD_OPTION;
|
|
7
|
+
static ECONNABORTED: string = ErrorCodes.ECONNABORTED;
|
|
8
|
+
static ETIMEDOUT: string = ErrorCodes.ETIMEDOUT;
|
|
9
|
+
static ERR_NETWORK: string = ErrorCodes.ERR_NETWORK;
|
|
10
|
+
static ERR_FR_TOO_MANY_REDIRECTS: string = ErrorCodes.ERR_FR_TOO_MANY_REDIRECTS;
|
|
11
|
+
static ERR_BAD_RESPONSE: string = ErrorCodes.ERR_BAD_RESPONSE;
|
|
12
|
+
static ERR_BAD_REQUEST: string = ErrorCodes.ERR_BAD_REQUEST;
|
|
13
|
+
static ERR_CANCELED: string = ErrorCodes.ERR_CANCELED;
|
|
14
|
+
static ERR_NOT_SUPPORT: string = ErrorCodes.ERR_NOT_SUPPORT;
|
|
15
|
+
static ERR_INVALID_URL: string = ErrorCodes.ERR_INVALID_URL;
|
|
16
|
+
|
|
17
|
+
readonly code: string | null;
|
|
18
|
+
readonly config: AccessioRequestConfig | null;
|
|
19
|
+
readonly request: unknown;
|
|
20
|
+
readonly response: AccessioResponse | null;
|
|
21
|
+
readonly isAccessioError: true;
|
|
22
|
+
cause?: Error;
|
|
23
|
+
|
|
24
|
+
constructor(
|
|
25
|
+
message: string,
|
|
26
|
+
code: string | null,
|
|
27
|
+
config: AccessioRequestConfig | null,
|
|
28
|
+
request: unknown,
|
|
29
|
+
response: AccessioResponse | null,
|
|
30
|
+
) {
|
|
31
|
+
super(message);
|
|
32
|
+
this.name = 'AccessioError';
|
|
33
|
+
this.code = code ?? null;
|
|
34
|
+
this.config = config ?? null;
|
|
35
|
+
this.request = request ?? null;
|
|
36
|
+
this.response = response ?? null;
|
|
37
|
+
this.isAccessioError = true;
|
|
38
|
+
|
|
39
|
+
if (Error.captureStackTrace) {
|
|
40
|
+
Error.captureStackTrace(this, AccessioError);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
toJSON(): Record<string, unknown> {
|
|
45
|
+
return {
|
|
46
|
+
name: this.name,
|
|
47
|
+
message: this.message,
|
|
48
|
+
code: this.code,
|
|
49
|
+
status: this.response ? this.response.status : null,
|
|
50
|
+
config: this.config,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
static from(
|
|
55
|
+
error: Error,
|
|
56
|
+
code: string,
|
|
57
|
+
config: AccessioRequestConfig | null,
|
|
58
|
+
request: unknown,
|
|
59
|
+
response: AccessioResponse | null,
|
|
60
|
+
): AccessioError {
|
|
61
|
+
const accessioError = new AccessioError(
|
|
62
|
+
error.message,
|
|
63
|
+
code,
|
|
64
|
+
config,
|
|
65
|
+
request,
|
|
66
|
+
response,
|
|
67
|
+
);
|
|
68
|
+
accessioError.cause = error;
|
|
69
|
+
accessioError.stack = error.stack;
|
|
70
|
+
return accessioError;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export default AccessioError;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { ParamsSerializer } from '../types';
|
|
2
|
+
|
|
3
|
+
function serializeParams(
|
|
4
|
+
params: Record<string, unknown>,
|
|
5
|
+
paramsSerializer?: ParamsSerializer,
|
|
6
|
+
): string {
|
|
7
|
+
if (!params) return '';
|
|
8
|
+
|
|
9
|
+
if (typeof paramsSerializer === 'function') {
|
|
10
|
+
return paramsSerializer(params);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (
|
|
14
|
+
typeof URLSearchParams !== 'undefined' &&
|
|
15
|
+
params instanceof URLSearchParams
|
|
16
|
+
) {
|
|
17
|
+
return params.toString();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const parts: string[] = [];
|
|
21
|
+
|
|
22
|
+
function encode(prefix: string, value: unknown): void {
|
|
23
|
+
if (value === null || value === undefined) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (Array.isArray(value)) {
|
|
28
|
+
value.forEach((item, index) => {
|
|
29
|
+
if (typeof item === 'object' && item !== null) {
|
|
30
|
+
encode(`${prefix}[${index}]`, item);
|
|
31
|
+
} else {
|
|
32
|
+
encode(`${prefix}[]`, item);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
} else if (typeof value === 'object' && !(value instanceof Date)) {
|
|
36
|
+
Object.keys(value as Record<string, unknown>).forEach((key) => {
|
|
37
|
+
encode(`${prefix}[${key}]`, (value as Record<string, unknown>)[key]);
|
|
38
|
+
});
|
|
39
|
+
} else {
|
|
40
|
+
const encodedValue = value instanceof Date ? value.toISOString() : value;
|
|
41
|
+
parts.push(
|
|
42
|
+
`${encodeURIComponent(prefix)}=${encodeURIComponent(
|
|
43
|
+
encodedValue as string,
|
|
44
|
+
)}`,
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
Object.keys(params).forEach((key) => {
|
|
50
|
+
encode(key, params[key]);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
return parts.join('&');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function combineURLs(baseURL: string, relativeURL: string): string {
|
|
57
|
+
if (!baseURL) return relativeURL || '';
|
|
58
|
+
if (!relativeURL) return baseURL;
|
|
59
|
+
|
|
60
|
+
let base = baseURL;
|
|
61
|
+
while (base.endsWith('/')) {
|
|
62
|
+
base = base.slice(0, -1);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let relative = relativeURL;
|
|
66
|
+
while (relative.startsWith('/')) {
|
|
67
|
+
relative = relative.slice(1);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return `${base}/${relative}`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function isAbsoluteURL(url: string): boolean {
|
|
74
|
+
return /^([a-z][a-z\d+\-.]*:)?\/\//i.test(url);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export default function buildURL(
|
|
78
|
+
url: string,
|
|
79
|
+
baseURL?: string,
|
|
80
|
+
params?: Record<string, unknown>,
|
|
81
|
+
paramsSerializer?: ParamsSerializer,
|
|
82
|
+
): string {
|
|
83
|
+
let fullURL =
|
|
84
|
+
baseURL && !isAbsoluteURL(url) ? combineURLs(baseURL, url) : url || '';
|
|
85
|
+
|
|
86
|
+
const serialized = serializeParams(params as Record<string, unknown>, paramsSerializer);
|
|
87
|
+
if (serialized) {
|
|
88
|
+
const hashIndex = fullURL.indexOf('#');
|
|
89
|
+
if (hashIndex !== -1) {
|
|
90
|
+
fullURL = fullURL.slice(0, hashIndex);
|
|
91
|
+
}
|
|
92
|
+
fullURL +=
|
|
93
|
+
(fullURL.indexOf('?') === -1 ? '?' : '&') + serialized;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return fullURL;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export { serializeParams, combineURLs, isAbsoluteURL };
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { AccessioRequestConfig } from '../types';
|
|
2
|
+
|
|
3
|
+
function deepMerge(...sources: any[]): Record<string, any> {
|
|
4
|
+
const result: Record<string, any> = {};
|
|
5
|
+
|
|
6
|
+
for (const source of sources) {
|
|
7
|
+
if (!source || typeof source !== 'object') continue;
|
|
8
|
+
|
|
9
|
+
for (const key of Object.keys(source)) {
|
|
10
|
+
const value = source[key];
|
|
11
|
+
|
|
12
|
+
if (
|
|
13
|
+
value &&
|
|
14
|
+
typeof value === 'object' &&
|
|
15
|
+
!Array.isArray(value)
|
|
16
|
+
) {
|
|
17
|
+
if (
|
|
18
|
+
value instanceof Date ||
|
|
19
|
+
value instanceof RegExp ||
|
|
20
|
+
value instanceof Map ||
|
|
21
|
+
value instanceof Set ||
|
|
22
|
+
value instanceof Error ||
|
|
23
|
+
(typeof ArrayBuffer !== 'undefined' &&
|
|
24
|
+
value instanceof ArrayBuffer) ||
|
|
25
|
+
(typeof Blob !== 'undefined' && value instanceof Blob)
|
|
26
|
+
) {
|
|
27
|
+
result[key] = value;
|
|
28
|
+
} else if (
|
|
29
|
+
result[key] &&
|
|
30
|
+
typeof result[key] === 'object' &&
|
|
31
|
+
!Array.isArray(result[key])
|
|
32
|
+
) {
|
|
33
|
+
result[key] = deepMerge(result[key], value);
|
|
34
|
+
} else {
|
|
35
|
+
result[key] = deepMerge(value);
|
|
36
|
+
}
|
|
37
|
+
} else if (value !== undefined) {
|
|
38
|
+
result[key] = value;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const requestOnlyKeys = new Set<string>(['url', 'data', 'signal']);
|
|
47
|
+
const deepMergeKeys = new Set<string>(['headers']);
|
|
48
|
+
|
|
49
|
+
export default function mergeConfig(
|
|
50
|
+
config1: AccessioRequestConfig = {},
|
|
51
|
+
config2: AccessioRequestConfig = {},
|
|
52
|
+
): AccessioRequestConfig {
|
|
53
|
+
const merged: any = {};
|
|
54
|
+
|
|
55
|
+
const allKeys = new Set<string>([
|
|
56
|
+
...Object.keys(config1),
|
|
57
|
+
...Object.keys(config2),
|
|
58
|
+
]);
|
|
59
|
+
|
|
60
|
+
for (const key of allKeys) {
|
|
61
|
+
const val1 = config1[key as keyof AccessioRequestConfig];
|
|
62
|
+
const val2 = config2[key as keyof AccessioRequestConfig];
|
|
63
|
+
|
|
64
|
+
if (requestOnlyKeys.has(key)) {
|
|
65
|
+
if (val2 !== undefined) {
|
|
66
|
+
merged[key] = val2;
|
|
67
|
+
}
|
|
68
|
+
} else if (deepMergeKeys.has(key)) {
|
|
69
|
+
merged[key] = deepMerge(val1 || {}, val2 || {});
|
|
70
|
+
} else {
|
|
71
|
+
merged[key] = val2 !== undefined ? val2 : val1;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return merged;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export { deepMerge };
|