@softwear/latestcollectioncore 1.0.151 → 1.0.153
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/articleStatus.js +11 -4
- package/dist/buildPropertyMappingFn.js +3 -3
- package/dist/cryptography.d.ts +1 -1
- package/dist/cryptography.js +8 -8
- package/dist/imageBinder.js +10 -10
- package/dist/index.d.ts +2 -0
- package/dist/index.js +7 -1
- package/dist/lcAxios.d.ts +51 -0
- package/dist/lcAxios.js +330 -0
- package/dist/types.d.ts +1 -1
- package/package.json +33 -34
- package/src/articleStatus.ts +9 -2
- package/src/buildPropertyMappingFn.ts +3 -3
- package/src/cryptography.ts +2 -2
- package/src/imageBinder.ts +23 -26
- package/src/index.ts +16 -0
- package/src/lcAxios.ts +349 -0
- package/src/types.ts +1 -1
- package/test/lcAxios.spec.ts +172 -0
- package/vitest.config.ts +1 -1
|
@@ -47,7 +47,7 @@ export default function (brands: Array<BrandSettingI>, strategy: mappingStrategy
|
|
|
47
47
|
})
|
|
48
48
|
if (strategy == 'clean')
|
|
49
49
|
return function (sku: SkuI): SkuI {
|
|
50
|
-
const brand = sku.
|
|
50
|
+
const brand = sku.brandhash || hashBrand(sku.brand)
|
|
51
51
|
const mapping = indexedByBrandPropertyMapping[brand]
|
|
52
52
|
mappingPairs.forEach((pair) => {
|
|
53
53
|
const from = evaluateFromExpr(sku, pair.from) || ''
|
|
@@ -59,7 +59,7 @@ export default function (brands: Array<BrandSettingI>, strategy: mappingStrategy
|
|
|
59
59
|
}
|
|
60
60
|
if (strategy == 'markMissingMapping')
|
|
61
61
|
return function (sku: SkuI): SkuI {
|
|
62
|
-
const brand = sku.
|
|
62
|
+
const brand = sku.brandhash || hashBrand(sku.brand)
|
|
63
63
|
const mapping = indexedByBrandPropertyMapping[brand]
|
|
64
64
|
mappingPairs.forEach((pair) => {
|
|
65
65
|
const from = evaluateFromExpr(sku, pair.from) || ''
|
|
@@ -72,7 +72,7 @@ export default function (brands: Array<BrandSettingI>, strategy: mappingStrategy
|
|
|
72
72
|
return sku
|
|
73
73
|
}
|
|
74
74
|
return function (sku: SkuI): SkuI {
|
|
75
|
-
const brand = sku.
|
|
75
|
+
const brand = sku.brandhash || hashBrand(sku.brand)
|
|
76
76
|
const mapping = indexedByBrandPropertyMapping[brand]
|
|
77
77
|
mappingPairs.forEach((pair) => {
|
|
78
78
|
if (sku[pair.to]) return // Do not replace sku-level-user-supplied values
|
package/src/cryptography.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Buffer } from 'buffer'
|
|
2
|
-
import { createCipheriv, createDecipheriv, randomBytes } from 'crypto'
|
|
1
|
+
import { Buffer } from 'node:buffer'
|
|
2
|
+
import { createCipheriv, createDecipheriv, randomBytes } from 'node:crypto'
|
|
3
3
|
|
|
4
4
|
export function encrypt(plain: string, KEY: Buffer): string {
|
|
5
5
|
const iv = randomBytes(12)
|
package/src/imageBinder.ts
CHANGED
|
@@ -30,8 +30,8 @@ const GLOBAL_PATTERNS = [
|
|
|
30
30
|
|
|
31
31
|
function prepFilterSkusImages(filteredSkus: SkuI[], merge = true, fileNames: Array<string>) {
|
|
32
32
|
const skusCopy = deepCopy(filteredSkus)
|
|
33
|
-
const preppedSkus
|
|
34
|
-
const fileNames2skip
|
|
33
|
+
const preppedSkus: Array<Record<string, any>> = []
|
|
34
|
+
const fileNames2skip: Array<string> = []
|
|
35
35
|
let idx = 0
|
|
36
36
|
skusCopy.forEach((sku) => {
|
|
37
37
|
const fieldsToBeMerged = {}
|
|
@@ -45,7 +45,7 @@ function prepFilterSkusImages(filteredSkus: SkuI[], merge = true, fileNames: Arr
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
for (const fieldName in sku) {
|
|
48
|
-
if (fieldName == '__created' || fieldName == '__modified' || fieldName == 'images' || fieldName == '
|
|
48
|
+
if (fieldName == '__created' || fieldName == '__modified' || fieldName == 'images' || fieldName == 'brandhash' || (merge == true && fieldName == 'colorFamily')) continue
|
|
49
49
|
let fieldVal = sku[fieldName]
|
|
50
50
|
if (fieldVal == '') continue
|
|
51
51
|
|
|
@@ -81,7 +81,6 @@ function prepFilterSkusImages(filteredSkus: SkuI[], merge = true, fileNames: Arr
|
|
|
81
81
|
}
|
|
82
82
|
preppedSkus.push(newSku)
|
|
83
83
|
}
|
|
84
|
-
|
|
85
84
|
})
|
|
86
85
|
const fileNames2Match = fileNames.filter((fileName) => {
|
|
87
86
|
return fileNames2skip.includes(fileName) == false
|
|
@@ -156,8 +155,8 @@ function testQuickSearch(
|
|
|
156
155
|
filteredSkus: {},
|
|
157
156
|
quickSearchOrder: Array<string>
|
|
158
157
|
) {
|
|
159
|
-
const indicesToReturn
|
|
160
|
-
const indicesWithoutLast
|
|
158
|
+
const indicesToReturn: Array<number> = []
|
|
159
|
+
const indicesWithoutLast: Array<number> = []
|
|
161
160
|
let patternStringFound = ''
|
|
162
161
|
quickSearchOrder.every((quickSearchPattern) => {
|
|
163
162
|
if (quickSearchPattern in quickSearch) {
|
|
@@ -184,7 +183,7 @@ function testQuickSearch(
|
|
|
184
183
|
|
|
185
184
|
function prepQuickSearch(preppedSkus: any): Record<string, Array<any>> {
|
|
186
185
|
const quickSearch = {}
|
|
187
|
-
const patternStrings
|
|
186
|
+
const patternStrings: Array<string> = []
|
|
188
187
|
GLOBAL_PATTERNS.forEach((pattern) => {
|
|
189
188
|
patternStrings.push(pattern.join('-'))
|
|
190
189
|
})
|
|
@@ -204,7 +203,7 @@ function prepQuickSearch(preppedSkus: any): Record<string, Array<any>> {
|
|
|
204
203
|
}
|
|
205
204
|
|
|
206
205
|
function addNewPatternToQuickSearch(preppedSkus: any, pattern: Array<string>, quickSearch: Record<string, Array<Array<any>>>): Record<string, Array<any>> {
|
|
207
|
-
const newQuickSearchArray
|
|
206
|
+
const newQuickSearchArray: Array<Array<any>> = []
|
|
208
207
|
let idx = 0
|
|
209
208
|
preppedSkus.forEach((sku) => {
|
|
210
209
|
const proposedFilename = pattern.reduce((out, field) => out + (field in sku ? sku[field] : ''), '')
|
|
@@ -219,8 +218,8 @@ function fallbackSearch(cleanedFileName: string, fileNameWithoutLast: string, pr
|
|
|
219
218
|
// This function looks for new patterns based on substring matches between filenames and skus
|
|
220
219
|
// If a new pattern is found, it is sent to the outbox for human check and added to the global patterns
|
|
221
220
|
|
|
222
|
-
let pattern_found
|
|
223
|
-
let exactMatches
|
|
221
|
+
let pattern_found: Array<string> = []
|
|
222
|
+
let exactMatches: Array<number> = []
|
|
224
223
|
let currentBestSubstringPatterns: Record<string, Array<number>> = {}
|
|
225
224
|
let maxPatternlength = 0
|
|
226
225
|
preppedSkus.forEach((sku, idx) => {
|
|
@@ -232,8 +231,8 @@ function fallbackSearch(cleanedFileName: string, fileNameWithoutLast: string, pr
|
|
|
232
231
|
}
|
|
233
232
|
return
|
|
234
233
|
}
|
|
235
|
-
const substrings
|
|
236
|
-
const substringPattern
|
|
234
|
+
const substrings: Array<string> = []
|
|
235
|
+
const substringPattern: Array<string> = []
|
|
237
236
|
for (const fieldName in sku) {
|
|
238
237
|
const fieldVal = sku[fieldName]
|
|
239
238
|
// Check if any fields are 100% match, then we've discovered a new pattern
|
|
@@ -311,7 +310,7 @@ function fallbackSearch(cleanedFileName: string, fileNameWithoutLast: string, pr
|
|
|
311
310
|
else {
|
|
312
311
|
let matchFound = false
|
|
313
312
|
Object.keys(currentBestSubstringPatterns).every((patternString) => {
|
|
314
|
-
const uniqueFieldVals
|
|
313
|
+
const uniqueFieldVals: Array<string> = []
|
|
315
314
|
const pattern = patternString.split('-')
|
|
316
315
|
const indexArray = currentBestSubstringPatterns[patternString]
|
|
317
316
|
indexArray.forEach((idx) => {
|
|
@@ -390,7 +389,7 @@ function imageBinder(fileNames: Array<string>, filteredSkus: SkuI[], brand: stri
|
|
|
390
389
|
let matchFound = false
|
|
391
390
|
const cleanedFileName = cleanFilename(fileName)
|
|
392
391
|
const fileNameWithoutLast = checkWithoutLast(fileName)
|
|
393
|
-
let indicesForFileName
|
|
392
|
+
let indicesForFileName: Array<number> = []
|
|
394
393
|
// Method #1: try quicksearch, an object with patterns as keys and arrays of [proposedFilename, sku] as values
|
|
395
394
|
// based on existing patterns in GLOBAL_PATTERNS
|
|
396
395
|
const quickSearchReturn = testQuickSearch(cleanedFileName, fileName, fileNameWithoutLast, quickSearch, filteredSkus, quickSearchOrder)
|
|
@@ -453,17 +452,16 @@ function onlySaveIDFromSkus(matchDict: {}): Record<string, number[]> {
|
|
|
453
452
|
}
|
|
454
453
|
|
|
455
454
|
async function testBatch(brand: string) {
|
|
456
|
-
|
|
457
455
|
const [skusModule, imagesModule, matchedModule] = await Promise.all([
|
|
458
456
|
import(`../test/imageBinderTestData/brandSkus/${brand}.json`),
|
|
459
457
|
import(`../test/imageBinderTestData/brandImages/${brand}.json`),
|
|
460
|
-
import(`../test/imageBinderTestData/brandDataMatched/${brand}.json`)
|
|
458
|
+
import(`../test/imageBinderTestData/brandDataMatched/${brand}.json`),
|
|
461
459
|
])
|
|
462
460
|
|
|
463
461
|
const fileNames = imagesModule.default
|
|
464
462
|
const filteredSkus = skusModule.default
|
|
465
463
|
const expectedRaw = matchedModule.default
|
|
466
|
-
|
|
464
|
+
|
|
467
465
|
// 1) Call imageBinder
|
|
468
466
|
const t0 = Date.now()
|
|
469
467
|
const imageBinderResult = imageBinder(fileNames, filteredSkus, brand, ['auto'], true)
|
|
@@ -493,16 +491,15 @@ async function testBatch(brand: string) {
|
|
|
493
491
|
}
|
|
494
492
|
|
|
495
493
|
return {
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
494
|
+
Brand: brand,
|
|
495
|
+
Files: fileNames.length,
|
|
496
|
+
Skus: filteredSkus.length,
|
|
497
|
+
Mismatches: mismatchCount,
|
|
498
|
+
Time: Number(((t1 - t0) / 1000).toFixed(3)),
|
|
499
|
+
QuickSearch: imageBinderResult.quickCount,
|
|
500
|
+
FallbackSearch: imageBinderResult.fallbackCount,
|
|
501
|
+
Patterns: imageBinderResult.patternCounter,
|
|
504
502
|
}
|
|
505
|
-
|
|
506
503
|
}
|
|
507
504
|
|
|
508
505
|
export default { imageBinder, testBatch }
|
package/src/index.ts
CHANGED
|
@@ -18,3 +18,19 @@ export { default as transaction } from './transaction'
|
|
|
18
18
|
export * from './types'
|
|
19
19
|
export * from './consts'
|
|
20
20
|
export * from './cryptography'
|
|
21
|
+
export {
|
|
22
|
+
default as lcAxios,
|
|
23
|
+
createAxiosInstance,
|
|
24
|
+
axiosRetry,
|
|
25
|
+
exponentialDelay,
|
|
26
|
+
isAxiosError,
|
|
27
|
+
} from './lcAxios'
|
|
28
|
+
export type {
|
|
29
|
+
AxiosRequestConfig,
|
|
30
|
+
AxiosResponse,
|
|
31
|
+
AxiosPromise,
|
|
32
|
+
AxiosError,
|
|
33
|
+
AxiosTransformer,
|
|
34
|
+
AxiosRetryOptions,
|
|
35
|
+
AxiosRequestConfigAny,
|
|
36
|
+
} from './lcAxios'
|
package/src/lcAxios.ts
ADDED
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetch-based axios-compatible client (minimal surface used by apps).
|
|
3
|
+
* Zero extra npm deps: uses global fetch, URL, Headers, AbortController.
|
|
4
|
+
*
|
|
5
|
+
* `decompress` on the request config is accepted for axios API compatibility only;
|
|
6
|
+
* it is ignored at runtime — fetch already returns decompressed response bodies for typical reads.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export interface AxiosRequestConfig<D = any> {
|
|
10
|
+
url?: string
|
|
11
|
+
baseURL?: string
|
|
12
|
+
method?: string
|
|
13
|
+
data?: D
|
|
14
|
+
params?: Record<string, any>
|
|
15
|
+
headers?: Record<string, string | undefined> | any
|
|
16
|
+
timeout?: number
|
|
17
|
+
responseType?: 'json' | 'text' | 'blob' | 'arraybuffer'
|
|
18
|
+
validateStatus?: (status: number) => boolean
|
|
19
|
+
transformRequest?: AxiosTransformer | AxiosTransformer[]
|
|
20
|
+
transformResponse?: AxiosTransformer | AxiosTransformer[]
|
|
21
|
+
withCredentials?: boolean
|
|
22
|
+
/** Axios-only; ignored — fetch handles Content-Encoding when reading the body */
|
|
23
|
+
decompress?: boolean
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type AxiosTransformer = (data: any, headers?: any) => any | Promise<any>
|
|
27
|
+
|
|
28
|
+
export interface AxiosResponse<T = any, D = any> {
|
|
29
|
+
data: T
|
|
30
|
+
status: number
|
|
31
|
+
statusText: string
|
|
32
|
+
headers: Record<string, string>
|
|
33
|
+
config: AxiosRequestConfig<D>
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type AxiosPromise<T = any> = Promise<AxiosResponse<T>>
|
|
37
|
+
|
|
38
|
+
export interface AxiosError<T = any, D = any> extends Error {
|
|
39
|
+
isAxiosError: true
|
|
40
|
+
config?: AxiosRequestConfig<D>
|
|
41
|
+
response?: AxiosResponse<T, D>
|
|
42
|
+
request?: any
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export type AxiosRequestConfigAny = AxiosRequestConfig
|
|
46
|
+
|
|
47
|
+
function isAxiosError(payload: any): payload is AxiosError {
|
|
48
|
+
return payload && payload.isAxiosError === true
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function mergeDeep(base: any, patch: any): any {
|
|
52
|
+
if (patch === undefined || patch === null) return base
|
|
53
|
+
if (base === undefined || base === null) {
|
|
54
|
+
if (typeof patch === 'object' && patch !== null && !Array.isArray(patch)) {
|
|
55
|
+
return mergeDeep({}, patch)
|
|
56
|
+
}
|
|
57
|
+
return patch
|
|
58
|
+
}
|
|
59
|
+
if (Array.isArray(patch)) return patch.slice()
|
|
60
|
+
const out: any = { ...base }
|
|
61
|
+
for (const k of Object.keys(patch)) {
|
|
62
|
+
const pv = patch[k]
|
|
63
|
+
const bv = base[k]
|
|
64
|
+
if (pv && typeof pv === 'object' && !Array.isArray(pv) && pv.constructor === Object && bv && typeof bv === 'object' && !Array.isArray(bv)) {
|
|
65
|
+
out[k] = mergeDeep(bv, pv)
|
|
66
|
+
} else if (pv !== undefined) {
|
|
67
|
+
out[k] = pv
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return out
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Like axios mergeConfig: concat transformRequest / transformResponse arrays */
|
|
74
|
+
function mergeRequestConfig(defaults: AxiosRequestConfig, config: AxiosRequestConfig): AxiosRequestConfig {
|
|
75
|
+
const m = mergeDeep(mergeDeep({}, defaults), config)
|
|
76
|
+
const dt = defaults.transformRequest
|
|
77
|
+
const ct = config.transformRequest
|
|
78
|
+
if (dt && ct) {
|
|
79
|
+
m.transformRequest = ([] as any[]).concat(Array.isArray(dt) ? dt : [dt]).concat(Array.isArray(ct) ? ct : [ct])
|
|
80
|
+
}
|
|
81
|
+
const dtr = defaults.transformResponse
|
|
82
|
+
const ctr = config.transformResponse
|
|
83
|
+
if (dtr && ctr) {
|
|
84
|
+
m.transformResponse = ([] as any[]).concat(Array.isArray(dtr) ? dtr : [dtr]).concat(Array.isArray(ctr) ? ctr : [ctr])
|
|
85
|
+
}
|
|
86
|
+
return m
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function buildURL(baseURL: string | undefined, url: string | undefined, params?: Record<string, any>): string {
|
|
90
|
+
const path = joinURL(baseURL, url || '')
|
|
91
|
+
if (!params || !Object.keys(params).length) return path
|
|
92
|
+
let u: URL
|
|
93
|
+
if (/^https?:\/\//i.test(path)) {
|
|
94
|
+
u = new URL(path)
|
|
95
|
+
} else if (typeof window !== 'undefined' && window.location) {
|
|
96
|
+
u = new URL(path, window.location.href)
|
|
97
|
+
} else {
|
|
98
|
+
u = new URL(path, 'http://127.0.0.1')
|
|
99
|
+
}
|
|
100
|
+
for (const [k, v] of Object.entries(params)) {
|
|
101
|
+
if (v === undefined || v === null) continue
|
|
102
|
+
if (Array.isArray(v)) v.forEach((item) => u.searchParams.append(k, String(item)))
|
|
103
|
+
else u.searchParams.set(k, String(v))
|
|
104
|
+
}
|
|
105
|
+
if (/^https?:\/\//i.test(path)) return u.toString()
|
|
106
|
+
return `${u.pathname}${u.search}${u.hash}`
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function joinURL(baseURL: string | undefined, url: string): string {
|
|
110
|
+
if (!baseURL) return url
|
|
111
|
+
if (!url) return baseURL.replace(/\/$/, '')
|
|
112
|
+
const b = baseURL.replace(/\/$/, '')
|
|
113
|
+
const p = url.startsWith('/') ? url : `/${url}`
|
|
114
|
+
return `${b}${p}`
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function flattenHeaders(h: any, method: string): Record<string, string> {
|
|
118
|
+
const out: Record<string, string> = {}
|
|
119
|
+
if (!h) return out
|
|
120
|
+
const common = h.common || {}
|
|
121
|
+
const byMethod = (h[method.toLowerCase()] || h[method] || {}) as Record<string, string>
|
|
122
|
+
for (const s of [common, byMethod, h]) {
|
|
123
|
+
if (!s || typeof s !== 'object') continue
|
|
124
|
+
for (const [k, v] of Object.entries(s)) {
|
|
125
|
+
if (['common', 'get', 'post', 'put', 'patch', 'delete', 'head'].includes(k)) continue
|
|
126
|
+
if (v != null && v !== '') out[k] = String(v)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return out
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async function applyTransformRequest(fns: AxiosTransformer | AxiosTransformer[] | undefined, data: any, headers: any): Promise<any> {
|
|
133
|
+
if (!fns) return data
|
|
134
|
+
const list = Array.isArray(fns) ? fns : [fns]
|
|
135
|
+
let d = data
|
|
136
|
+
for (const fn of list) {
|
|
137
|
+
d = await Promise.resolve(fn(d, headers))
|
|
138
|
+
}
|
|
139
|
+
return d
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function applyTransformResponse(fns: AxiosTransformer | AxiosTransformer[] | undefined, data: any, headers: any): any {
|
|
143
|
+
if (!fns) return data
|
|
144
|
+
const list = Array.isArray(fns) ? fns : [fns]
|
|
145
|
+
let d = data
|
|
146
|
+
for (const fn of list) {
|
|
147
|
+
d = fn(d, headers)
|
|
148
|
+
}
|
|
149
|
+
return d
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function headersFromFetch(h: Headers): Record<string, string> {
|
|
153
|
+
const o: Record<string, string> = {}
|
|
154
|
+
h.forEach((v, k) => {
|
|
155
|
+
o[k] = v
|
|
156
|
+
})
|
|
157
|
+
return o
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export function exponentialDelay(retryNumber = 0): number {
|
|
161
|
+
const delay = Math.pow(2, retryNumber) * 1000
|
|
162
|
+
const randomSum = delay * 0.2 * Math.random() * 2 - 1
|
|
163
|
+
return delay + randomSum
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export interface AxiosRetryOptions {
|
|
167
|
+
retries?: number
|
|
168
|
+
retryDelay?: (retryCount: number) => number
|
|
169
|
+
retryCondition?: (error: any) => boolean
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function axiosRetry(instance: any, options: AxiosRetryOptions = {}) {
|
|
173
|
+
const retries = options.retries ?? 3
|
|
174
|
+
const retryDelay = options.retryDelay ?? exponentialDelay
|
|
175
|
+
const retryCondition = options.retryCondition ?? (() => false)
|
|
176
|
+
const original = instance.request.bind(instance)
|
|
177
|
+
instance.request = async function axiosRetryRequest(req: AxiosRequestConfig) {
|
|
178
|
+
let attempt = 0
|
|
179
|
+
while (true) {
|
|
180
|
+
try {
|
|
181
|
+
return await original(req)
|
|
182
|
+
} catch (err: any) {
|
|
183
|
+
if (attempt >= retries || !retryCondition(err)) throw err
|
|
184
|
+
await new Promise((r) => setTimeout(r, retryDelay(attempt)))
|
|
185
|
+
attempt++
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return instance
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const defaultValidateStatus = (status: number) => status >= 200 && status < 300
|
|
193
|
+
|
|
194
|
+
function getDefaultDefaults(): AxiosRequestConfig {
|
|
195
|
+
return {
|
|
196
|
+
headers: { common: {} },
|
|
197
|
+
transformRequest: [
|
|
198
|
+
function defaultTransformRequest(data: any, headers: any) {
|
|
199
|
+
if (data == null) return data
|
|
200
|
+
if (typeof data === 'string') return data
|
|
201
|
+
if (typeof FormData !== 'undefined' && data instanceof FormData) return data
|
|
202
|
+
if (typeof Blob !== 'undefined' && data instanceof Blob) return data
|
|
203
|
+
if (typeof Buffer !== 'undefined' && (Buffer as any).isBuffer?.(data)) return data
|
|
204
|
+
// JSON.stringify on Uint8Array produces {"0":byte,...} — never do that; binary must pass through
|
|
205
|
+
if (typeof ArrayBuffer !== 'undefined' && data instanceof ArrayBuffer) return data
|
|
206
|
+
if (typeof Uint8Array !== 'undefined' && data instanceof Uint8Array) return data
|
|
207
|
+
if (typeof data === 'object') {
|
|
208
|
+
if (!headers['Content-Type'] && !headers['content-type']) {
|
|
209
|
+
headers['Content-Type'] = 'application/json'
|
|
210
|
+
}
|
|
211
|
+
return JSON.stringify(data)
|
|
212
|
+
}
|
|
213
|
+
return data
|
|
214
|
+
},
|
|
215
|
+
],
|
|
216
|
+
transformResponse: [
|
|
217
|
+
function defaultTransformResponse(data: any) {
|
|
218
|
+
return data
|
|
219
|
+
},
|
|
220
|
+
],
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export function createAxiosInstance(initialDefaults?: AxiosRequestConfig): any {
|
|
225
|
+
const defaults: AxiosRequestConfig = initialDefaults
|
|
226
|
+
? mergeRequestConfig(mergeDeep({}, getDefaultDefaults()), initialDefaults)
|
|
227
|
+
: mergeDeep({}, getDefaultDefaults())
|
|
228
|
+
|
|
229
|
+
async function dispatchRequest(config: AxiosRequestConfig): Promise<AxiosResponse> {
|
|
230
|
+
const merged = mergeRequestConfig(defaults, config)
|
|
231
|
+
const method = (merged.method || 'get').toUpperCase()
|
|
232
|
+
const url = buildURL(merged.baseURL, merged.url, merged.params)
|
|
233
|
+
const headers: any = { ...flattenHeaders(merged.headers, method) }
|
|
234
|
+
|
|
235
|
+
let data = merged.data
|
|
236
|
+
data = await applyTransformRequest(merged.transformRequest, data, headers)
|
|
237
|
+
|
|
238
|
+
const validateStatus = merged.validateStatus || defaultValidateStatus
|
|
239
|
+
|
|
240
|
+
const controller = new AbortController()
|
|
241
|
+
const t = merged.timeout
|
|
242
|
+
let timer: any
|
|
243
|
+
if (t != null && t > 0) {
|
|
244
|
+
timer = setTimeout(() => controller.abort(), t)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const init: RequestInit = {
|
|
248
|
+
method,
|
|
249
|
+
headers,
|
|
250
|
+
signal: controller.signal,
|
|
251
|
+
}
|
|
252
|
+
if (merged.withCredentials) {
|
|
253
|
+
init.credentials = 'include'
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (method !== 'GET' && method !== 'HEAD' && data !== undefined) {
|
|
257
|
+
init.body = data as any
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
let res: Response
|
|
261
|
+
try {
|
|
262
|
+
res = await fetch(url, init)
|
|
263
|
+
} catch (e: any) {
|
|
264
|
+
if (timer) clearTimeout(timer)
|
|
265
|
+
const err = new Error(e?.name === 'AbortError' ? 'timeout of ' + t + 'ms exceeded' : 'Network Error') as AxiosError
|
|
266
|
+
err.isAxiosError = true
|
|
267
|
+
err.config = merged
|
|
268
|
+
err.request = { aborted: e?.name === 'AbortError' }
|
|
269
|
+
throw err
|
|
270
|
+
}
|
|
271
|
+
if (timer) clearTimeout(timer)
|
|
272
|
+
|
|
273
|
+
const resHeaders = headersFromFetch(res.headers)
|
|
274
|
+
const ct = res.headers.get('content-type') || ''
|
|
275
|
+
const rt = merged.responseType || 'json'
|
|
276
|
+
|
|
277
|
+
let parsed: any
|
|
278
|
+
if (rt === 'blob') {
|
|
279
|
+
parsed = await res.blob()
|
|
280
|
+
} else if (rt === 'arraybuffer') {
|
|
281
|
+
parsed = await res.arrayBuffer()
|
|
282
|
+
} else if (rt === 'text') {
|
|
283
|
+
parsed = await res.text()
|
|
284
|
+
} else if (ct.includes('application/json')) {
|
|
285
|
+
// Declared JSON: empty body → null; invalid JSON → raw string (axios-like)
|
|
286
|
+
const text = await res.text()
|
|
287
|
+
try {
|
|
288
|
+
parsed = text ? JSON.parse(text) : null
|
|
289
|
+
} catch {
|
|
290
|
+
parsed = text
|
|
291
|
+
}
|
|
292
|
+
} else {
|
|
293
|
+
// Other content-types: still try JSON.parse so mislabeled JSON works; empty body → '' (not null)
|
|
294
|
+
const text = await res.text()
|
|
295
|
+
try {
|
|
296
|
+
parsed = text ? JSON.parse(text) : text
|
|
297
|
+
} catch {
|
|
298
|
+
parsed = text
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
parsed = applyTransformResponse(merged.transformResponse, parsed, resHeaders)
|
|
303
|
+
|
|
304
|
+
const response: AxiosResponse = {
|
|
305
|
+
data: parsed,
|
|
306
|
+
status: res.status,
|
|
307
|
+
statusText: res.statusText,
|
|
308
|
+
headers: resHeaders,
|
|
309
|
+
config: merged,
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (!validateStatus(res.status)) {
|
|
313
|
+
const err = new Error('Request failed with status code ' + res.status) as AxiosError
|
|
314
|
+
err.isAxiosError = true
|
|
315
|
+
err.config = merged
|
|
316
|
+
err.response = response
|
|
317
|
+
throw err
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return response
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const instance: any = function request(configOrUrl: string | AxiosRequestConfig, maybe?: AxiosRequestConfig): Promise<AxiosResponse> {
|
|
324
|
+
if (typeof configOrUrl === 'string') {
|
|
325
|
+
return instance.request({ ...(maybe || {}), url: configOrUrl })
|
|
326
|
+
}
|
|
327
|
+
return instance.request(configOrUrl)
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
instance.defaults = defaults
|
|
331
|
+
instance.request = (c: AxiosRequestConfig) => dispatchRequest(mergeRequestConfig(defaults, c))
|
|
332
|
+
instance.get = (url: string, c?: AxiosRequestConfig) => instance.request({ ...(c || {}), url, method: 'GET' })
|
|
333
|
+
instance.delete = (url: string, c?: AxiosRequestConfig) => instance.request({ ...(c || {}), url, method: 'DELETE' })
|
|
334
|
+
instance.head = (url: string, c?: AxiosRequestConfig) => instance.request({ ...(c || {}), url, method: 'HEAD' })
|
|
335
|
+
instance.post = (url: string, data?: any, c?: AxiosRequestConfig) =>
|
|
336
|
+
instance.request({ ...(c || {}), url, method: 'POST', data })
|
|
337
|
+
instance.put = (url: string, data?: any, c?: AxiosRequestConfig) =>
|
|
338
|
+
instance.request({ ...(c || {}), url, method: 'PUT', data })
|
|
339
|
+
instance.patch = (url: string, data?: any, c?: AxiosRequestConfig) =>
|
|
340
|
+
instance.request({ ...(c || {}), url, method: 'PATCH', data })
|
|
341
|
+
instance.create = (cfg?: AxiosRequestConfig) => createAxiosInstance(mergeRequestConfig(mergeDeep({}, defaults), cfg || {}))
|
|
342
|
+
|
|
343
|
+
return instance
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const lcAxios = createAxiosInstance()
|
|
347
|
+
|
|
348
|
+
export default lcAxios
|
|
349
|
+
export { lcAxios, isAxiosError }
|