@microlink/mql 0.15.0 → 0.16.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.md +0 -0
- package/dist/index.js +1005 -406
- package/dist/index.umd.js +1005 -406
- package/package.json +23 -23
- package/src/constants.js +8 -2
- package/src/index.js +164 -31
- package/src/factory.js +0 -144
package/package.json
CHANGED
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
"name": "@microlink/mql",
|
|
3
3
|
"description": "Microlink Query Language. The official HTTP client to interact with Microlink API for Node.js, browsers & Deno.",
|
|
4
4
|
"homepage": "https://microlink.io/mql",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.16.0",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"exports": {
|
|
8
|
-
"
|
|
9
|
-
"
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"require": "./src/index.js",
|
|
10
|
+
"import": "./src/main.mjs",
|
|
11
|
+
"default": "./src/main.mjs"
|
|
10
12
|
},
|
|
11
13
|
"author": {
|
|
12
14
|
"email": "josefrancisco.verdu@gmail.com",
|
|
@@ -47,7 +49,7 @@
|
|
|
47
49
|
],
|
|
48
50
|
"dependencies": {
|
|
49
51
|
"flattie": "~1.1.1",
|
|
50
|
-
"ky": "~
|
|
52
|
+
"ky": "~2.0.0"
|
|
51
53
|
},
|
|
52
54
|
"devDependencies": {
|
|
53
55
|
"@commitlint/cli": "latest",
|
|
@@ -74,31 +76,14 @@
|
|
|
74
76
|
"tsd": "latest"
|
|
75
77
|
},
|
|
76
78
|
"engines": {
|
|
77
|
-
"node": ">=
|
|
79
|
+
"node": ">= 22"
|
|
78
80
|
},
|
|
79
81
|
"files": [
|
|
80
82
|
"dist",
|
|
81
83
|
"src/constants.js",
|
|
82
|
-
"src/factory.js",
|
|
83
84
|
"src/index.js",
|
|
84
85
|
"src/main.mjs"
|
|
85
86
|
],
|
|
86
|
-
"scripts": {
|
|
87
|
-
"build": "rollup -c rollup.config.js --bundleConfigAsCjs",
|
|
88
|
-
"clean": "rm -rf node_modules",
|
|
89
|
-
"clean:build": "rm -rf dist/index.js",
|
|
90
|
-
"contributors": "(npx git-authors-cli && npx finepack && git add package.json && git commit -m 'build: contributors' --no-verify) || true",
|
|
91
|
-
"dev": "npm run build -- -w",
|
|
92
|
-
"lint": "standard && tsd",
|
|
93
|
-
"postrelease": "npm run release:tags && npm run release:github && (ci-publish || npm publish --access=public)",
|
|
94
|
-
"prebuild": "npm run clean:build",
|
|
95
|
-
"prepublishOnly": "npm run build",
|
|
96
|
-
"pretest": "npm run lint && npm run build",
|
|
97
|
-
"release": "standard-version -a",
|
|
98
|
-
"release:github": "github-generate-release",
|
|
99
|
-
"release:tags": "git push --follow-tags origin HEAD:master",
|
|
100
|
-
"test": "c8 ava --verbose"
|
|
101
|
-
},
|
|
102
87
|
"license": "MIT",
|
|
103
88
|
"ava": {
|
|
104
89
|
"files": [
|
|
@@ -149,5 +134,20 @@
|
|
|
149
134
|
}
|
|
150
135
|
},
|
|
151
136
|
"directory": "test"
|
|
137
|
+
},
|
|
138
|
+
"scripts": {
|
|
139
|
+
"build": "rollup -c rollup.config.js --bundleConfigAsCjs",
|
|
140
|
+
"clean": "rm -rf node_modules",
|
|
141
|
+
"clean:build": "rm -rf dist/index.js",
|
|
142
|
+
"contributors": "(npx git-authors-cli && npx finepack && git add package.json && git commit -m 'build: contributors' --no-verify) || true",
|
|
143
|
+
"dev": "npm run build -- -w",
|
|
144
|
+
"lint": "standard && tsd",
|
|
145
|
+
"postrelease": "npm run release:tags && npm run release:github && (ci-publish || npm publish --access=public)",
|
|
146
|
+
"prebuild": "npm run clean:build",
|
|
147
|
+
"pretest": "npm run lint && npm run build",
|
|
148
|
+
"release": "standard-version -a",
|
|
149
|
+
"release:github": "github-generate-release",
|
|
150
|
+
"release:tags": "git push --follow-tags origin HEAD:master",
|
|
151
|
+
"test": "c8 ava --verbose"
|
|
152
152
|
}
|
|
153
|
-
}
|
|
153
|
+
}
|
package/src/constants.js
CHANGED
|
@@ -6,7 +6,13 @@ module.exports = {
|
|
|
6
6
|
VERSION,
|
|
7
7
|
USER_AGENT: `mql/${VERSION}`,
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
9
|
+
* Based on Ky default retry status codes, excluding 429:
|
|
10
|
+
* https://github.com/sindresorhus/ky/blob/main/source/core/constants.ts
|
|
10
11
|
*/
|
|
11
|
-
RETRY_STATUS_CODES: [408, 413, 500, 502, 503, 504, 521, 522, 524]
|
|
12
|
+
RETRY_STATUS_CODES: [408, 413, 500, 502, 503, 504, 521, 522, 524],
|
|
13
|
+
/**
|
|
14
|
+
* Based on Ky default Retry-After status codes, excluding 429:
|
|
15
|
+
* https://github.com/sindresorhus/ky/blob/main/source/core/constants.ts
|
|
16
|
+
*/
|
|
17
|
+
RETRY_AFTER_STATUS_CODES: [413, 503]
|
|
12
18
|
}
|
package/src/index.js
CHANGED
|
@@ -3,14 +3,59 @@
|
|
|
3
3
|
const { flattie: flatten } = require('flattie')
|
|
4
4
|
const { default: ky } = require('ky')
|
|
5
5
|
|
|
6
|
-
const {
|
|
6
|
+
const {
|
|
7
|
+
VERSION,
|
|
8
|
+
USER_AGENT,
|
|
9
|
+
RETRY_STATUS_CODES,
|
|
10
|
+
RETRY_AFTER_STATUS_CODES
|
|
11
|
+
} = require('./constants')
|
|
12
|
+
|
|
13
|
+
const ENDPOINT = {
|
|
14
|
+
FREE: 'https://api.microlink.io/',
|
|
15
|
+
PRO: 'https://pro.microlink.io/'
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const STREAM_RESPONSE_TYPE = 'arrayBuffer'
|
|
7
19
|
|
|
8
20
|
const kyInstance = ky.extend({
|
|
9
21
|
headers: { 'user-agent': USER_AGENT },
|
|
10
|
-
retry: {
|
|
22
|
+
retry: {
|
|
23
|
+
statusCodes: RETRY_STATUS_CODES,
|
|
24
|
+
afterStatusCodes: RETRY_AFTER_STATUS_CODES
|
|
25
|
+
}
|
|
11
26
|
})
|
|
12
27
|
|
|
13
|
-
const
|
|
28
|
+
const isObject = input => input !== null && typeof input === 'object'
|
|
29
|
+
|
|
30
|
+
const isBuffer = input =>
|
|
31
|
+
typeof input?.constructor?.isBuffer === 'function' &&
|
|
32
|
+
input.constructor.isBuffer(input)
|
|
33
|
+
|
|
34
|
+
const parseBody = (input, error, url) => {
|
|
35
|
+
try {
|
|
36
|
+
return JSON.parse(input)
|
|
37
|
+
} catch (_) {
|
|
38
|
+
const message = input || error.message
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
status: 'error',
|
|
42
|
+
data: { url: message },
|
|
43
|
+
more: 'https://microlink.io/efatalclient',
|
|
44
|
+
code: 'EFATALCLIENT',
|
|
45
|
+
message,
|
|
46
|
+
url
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const isURL = url => {
|
|
52
|
+
try {
|
|
53
|
+
const { protocol } = new URL(url)
|
|
54
|
+
return protocol === 'http:' || protocol === 'https:'
|
|
55
|
+
} catch (_) {
|
|
56
|
+
return false
|
|
57
|
+
}
|
|
58
|
+
}
|
|
14
59
|
|
|
15
60
|
class MicrolinkError extends Error {
|
|
16
61
|
constructor (props) {
|
|
@@ -24,44 +69,132 @@ class MicrolinkError extends Error {
|
|
|
24
69
|
}
|
|
25
70
|
}
|
|
26
71
|
|
|
27
|
-
const
|
|
72
|
+
const assertUrl = (url = '') => {
|
|
73
|
+
if (!isURL(url)) {
|
|
74
|
+
const message = `The \`url\` as \`${url}\` is not valid. Ensure it has protocol (http or https) and hostname.`
|
|
75
|
+
throw new MicrolinkError({
|
|
76
|
+
status: 'fail',
|
|
77
|
+
data: { url: message },
|
|
78
|
+
more: 'https://microlink.io/einvalurlclient',
|
|
79
|
+
code: 'EINVALURLCLIENT',
|
|
80
|
+
message,
|
|
81
|
+
url
|
|
82
|
+
})
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const mapRules = rules => {
|
|
87
|
+
if (!isObject(rules)) return
|
|
88
|
+
return Object.fromEntries(
|
|
89
|
+
Object.entries(flatten(rules)).map(([key, value]) => [
|
|
90
|
+
`data.${key}`,
|
|
91
|
+
value.toString()
|
|
92
|
+
])
|
|
93
|
+
)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const doFetch = async (apiUrl, { responseType, ...opts }) => {
|
|
97
|
+
if (opts.timeout === undefined) opts.timeout = false
|
|
98
|
+
const response = await kyInstance(apiUrl, opts)
|
|
99
|
+
const body = await response[responseType]()
|
|
100
|
+
const { headers, status: statusCode } = response
|
|
101
|
+
return { url: response.url, body, headers, statusCode }
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const fetchFromApi = async (apiUrl, opts = {}) => {
|
|
28
105
|
try {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
return { url: response.url, body, headers, statusCode }
|
|
106
|
+
const response = await doFetch(apiUrl, opts)
|
|
107
|
+
return opts.responseType === STREAM_RESPONSE_TYPE
|
|
108
|
+
? response
|
|
109
|
+
: { ...response.body, response }
|
|
34
110
|
} catch (error) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
111
|
+
const { response = {} } = error
|
|
112
|
+
const { statusCode: responseStatusCode, status } = response
|
|
113
|
+
const {
|
|
114
|
+
body: rawBody,
|
|
115
|
+
headers: responseHeaders,
|
|
116
|
+
url: uri = apiUrl
|
|
117
|
+
} = response
|
|
118
|
+
|
|
119
|
+
const statusCode = responseStatusCode ?? status
|
|
120
|
+
const headers =
|
|
121
|
+
typeof responseHeaders?.entries === 'function'
|
|
122
|
+
? Object.fromEntries(responseHeaders.entries())
|
|
123
|
+
: responseHeaders || {}
|
|
124
|
+
|
|
125
|
+
let bodyInput = error.data ?? rawBody
|
|
126
|
+
const isBodyReadableStream = typeof bodyInput?.getReader === 'function'
|
|
127
|
+
|
|
128
|
+
if (
|
|
129
|
+
(bodyInput === undefined || isBodyReadableStream) &&
|
|
130
|
+
typeof response.text === 'function'
|
|
131
|
+
) {
|
|
132
|
+
try {
|
|
133
|
+
bodyInput = await response.text()
|
|
134
|
+
} catch (_) {
|
|
135
|
+
bodyInput = undefined
|
|
48
136
|
}
|
|
49
137
|
}
|
|
50
|
-
|
|
138
|
+
|
|
139
|
+
const isBodyBuffer = isBuffer(bodyInput)
|
|
140
|
+
const body =
|
|
141
|
+
isObject(bodyInput) && !isBodyBuffer
|
|
142
|
+
? bodyInput
|
|
143
|
+
: parseBody(isBodyBuffer ? bodyInput.toString() : bodyInput, error, uri)
|
|
144
|
+
|
|
145
|
+
throw new MicrolinkError({
|
|
146
|
+
...body,
|
|
147
|
+
url: uri,
|
|
148
|
+
statusCode,
|
|
149
|
+
headers
|
|
150
|
+
})
|
|
51
151
|
}
|
|
52
152
|
}
|
|
53
153
|
|
|
54
|
-
|
|
154
|
+
const getApiUrl = (
|
|
155
|
+
url,
|
|
156
|
+
{ data, apiKey, endpoint, ...opts } = {},
|
|
157
|
+
{ responseType = 'json', headers: responseHeaders = {}, ...gotOpts } = {}
|
|
158
|
+
) => {
|
|
159
|
+
const isPro = !!apiKey
|
|
160
|
+
const apiEndpoint = endpoint || ENDPOINT[isPro ? 'PRO' : 'FREE']
|
|
55
161
|
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
162
|
+
const apiUrl = `${apiEndpoint}?${new URLSearchParams({
|
|
163
|
+
url,
|
|
164
|
+
...mapRules(data),
|
|
165
|
+
...flatten(opts)
|
|
166
|
+
})}`
|
|
167
|
+
|
|
168
|
+
const headers = isPro
|
|
169
|
+
? { ...responseHeaders, 'x-api-key': apiKey }
|
|
170
|
+
: responseHeaders
|
|
171
|
+
|
|
172
|
+
if (opts.stream) responseType = STREAM_RESPONSE_TYPE
|
|
173
|
+
|
|
174
|
+
return [apiUrl, { ...gotOpts, responseType, headers }]
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const createMql = defaultOpts => async (url, opts, gotOpts) => {
|
|
178
|
+
assertUrl(url)
|
|
179
|
+
const [apiUrl, fetchOpts] = getApiUrl(url, opts, {
|
|
180
|
+
...defaultOpts,
|
|
181
|
+
...gotOpts
|
|
182
|
+
})
|
|
183
|
+
return fetchFromApi(apiUrl, fetchOpts)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const mql = createMql()
|
|
187
|
+
|
|
188
|
+
mql.extend = createMql
|
|
189
|
+
mql.MicrolinkError = MicrolinkError
|
|
190
|
+
mql.getApiUrl = getApiUrl
|
|
191
|
+
mql.fetchFromApi = fetchFromApi
|
|
192
|
+
mql.mapRules = mapRules
|
|
193
|
+
mql.version = VERSION
|
|
194
|
+
mql.stream = (...args) => kyInstance(...args).then(res => res.body)
|
|
62
195
|
|
|
63
196
|
module.exports = mql
|
|
64
|
-
module.exports.arrayBuffer = mql.extend({ responseType:
|
|
197
|
+
module.exports.arrayBuffer = mql.extend({ responseType: STREAM_RESPONSE_TYPE })
|
|
65
198
|
module.exports.buffer = module.exports.arrayBuffer
|
|
66
199
|
module.exports.extend = mql.extend
|
|
67
200
|
module.exports.fetchFromApi = mql.fetchFromApi
|
package/src/factory.js
DELETED
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
const ENDPOINT = {
|
|
2
|
-
FREE: 'https://api.microlink.io/',
|
|
3
|
-
PRO: 'https://pro.microlink.io/'
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
const isObject = input => input !== null && typeof input === 'object'
|
|
7
|
-
|
|
8
|
-
const isBuffer = input =>
|
|
9
|
-
input != null &&
|
|
10
|
-
input.constructor != null &&
|
|
11
|
-
typeof input.constructor.isBuffer === 'function' &&
|
|
12
|
-
input.constructor.isBuffer(input)
|
|
13
|
-
|
|
14
|
-
const parseBody = (input, error, url) => {
|
|
15
|
-
try {
|
|
16
|
-
return JSON.parse(input)
|
|
17
|
-
} catch (_) {
|
|
18
|
-
const message = input || error.message
|
|
19
|
-
|
|
20
|
-
return {
|
|
21
|
-
status: 'error',
|
|
22
|
-
data: { url: message },
|
|
23
|
-
more: 'https://microlink.io/efatalclient',
|
|
24
|
-
code: 'EFATALCLIENT',
|
|
25
|
-
message,
|
|
26
|
-
url
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const isURL = url => {
|
|
32
|
-
try {
|
|
33
|
-
return /^https?:\/\//i.test(new URL(url).href)
|
|
34
|
-
} catch (_) {
|
|
35
|
-
return false
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const factory = streamResponseType => ({
|
|
40
|
-
VERSION,
|
|
41
|
-
MicrolinkError,
|
|
42
|
-
got,
|
|
43
|
-
flatten
|
|
44
|
-
}) => {
|
|
45
|
-
const assertUrl = (url = '') => {
|
|
46
|
-
if (!isURL(url)) {
|
|
47
|
-
const message = `The \`url\` as \`${url}\` is not valid. Ensure it has protocol (http or https) and hostname.`
|
|
48
|
-
throw new MicrolinkError({
|
|
49
|
-
status: 'fail',
|
|
50
|
-
data: { url: message },
|
|
51
|
-
more: 'https://microlink.io/einvalurlclient',
|
|
52
|
-
code: 'EINVALURLCLIENT',
|
|
53
|
-
message,
|
|
54
|
-
url
|
|
55
|
-
})
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const mapRules = rules => {
|
|
60
|
-
if (!isObject(rules)) return
|
|
61
|
-
const flatRules = flatten(rules)
|
|
62
|
-
return Object.keys(flatRules).reduce((acc, key) => {
|
|
63
|
-
acc[`data.${key}`] = flatRules[key].toString()
|
|
64
|
-
return acc
|
|
65
|
-
}, {})
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const fetchFromApi = async (apiUrl, opts = {}) => {
|
|
69
|
-
try {
|
|
70
|
-
const response = await got(apiUrl, opts)
|
|
71
|
-
return opts.responseType === streamResponseType
|
|
72
|
-
? response
|
|
73
|
-
: { ...response.body, response }
|
|
74
|
-
} catch (error) {
|
|
75
|
-
const { response = {} } = error
|
|
76
|
-
const {
|
|
77
|
-
statusCode,
|
|
78
|
-
body: rawBody,
|
|
79
|
-
headers = {},
|
|
80
|
-
url: uri = apiUrl
|
|
81
|
-
} = response
|
|
82
|
-
const isBodyBuffer = isBuffer(rawBody)
|
|
83
|
-
|
|
84
|
-
const body =
|
|
85
|
-
isObject(rawBody) && !isBodyBuffer
|
|
86
|
-
? rawBody
|
|
87
|
-
: parseBody(isBodyBuffer ? rawBody.toString() : rawBody, error, uri)
|
|
88
|
-
|
|
89
|
-
throw new MicrolinkError({
|
|
90
|
-
...body,
|
|
91
|
-
message: body.message,
|
|
92
|
-
url: uri,
|
|
93
|
-
statusCode,
|
|
94
|
-
headers
|
|
95
|
-
})
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const getApiUrl = (
|
|
100
|
-
url,
|
|
101
|
-
{ data, apiKey, endpoint, ...opts } = {},
|
|
102
|
-
{ responseType = 'json', headers: gotHeaders, ...gotOpts } = {}
|
|
103
|
-
) => {
|
|
104
|
-
const isPro = !!apiKey
|
|
105
|
-
const apiEndpoint = endpoint || ENDPOINT[isPro ? 'PRO' : 'FREE']
|
|
106
|
-
|
|
107
|
-
const apiUrl = `${apiEndpoint}?${new URLSearchParams({
|
|
108
|
-
url,
|
|
109
|
-
...mapRules(data),
|
|
110
|
-
...flatten(opts)
|
|
111
|
-
}).toString()}`
|
|
112
|
-
|
|
113
|
-
const headers = isPro
|
|
114
|
-
? { ...gotHeaders, 'x-api-key': apiKey }
|
|
115
|
-
: { ...gotHeaders }
|
|
116
|
-
|
|
117
|
-
if (opts.stream) {
|
|
118
|
-
responseType = streamResponseType
|
|
119
|
-
}
|
|
120
|
-
return [apiUrl, { ...gotOpts, responseType, headers }]
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const createMql = defaultOpts => async (url, opts, gotOpts) => {
|
|
124
|
-
assertUrl(url)
|
|
125
|
-
const [apiUrl, fetchOpts] = getApiUrl(url, opts, {
|
|
126
|
-
...defaultOpts,
|
|
127
|
-
...gotOpts
|
|
128
|
-
})
|
|
129
|
-
return fetchFromApi(apiUrl, fetchOpts)
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const mql = createMql()
|
|
133
|
-
mql.extend = createMql
|
|
134
|
-
mql.MicrolinkError = MicrolinkError
|
|
135
|
-
mql.getApiUrl = getApiUrl
|
|
136
|
-
mql.fetchFromApi = fetchFromApi
|
|
137
|
-
mql.mapRules = mapRules
|
|
138
|
-
mql.version = VERSION
|
|
139
|
-
mql.stream = got.stream
|
|
140
|
-
|
|
141
|
-
return mql
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
module.exports = factory
|