@unavatar/core 3.45.26 → 3.46.1
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/package.json +2 -1
- package/src/avatar/auto.js +71 -25
- package/src/index.js +11 -5
- package/src/providers/bimi.js +19 -0
- package/src/providers/index.js +85 -72
- package/src/util/cache.js +13 -3
- package/src/util/cacheable-lookup.js +2 -14
- package/src/util/dns-resolver.js +18 -0
- package/src/util/got.js +33 -2
- package/src/util/is-reserved-ip.js +9 -7
- package/src/util/reachable-url.js +6 -5
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@unavatar/core",
|
|
3
3
|
"description": "Get unified user avatar from social networks, including Instagram, SoundCloud, Telegram, Twitter, YouTube & more.",
|
|
4
4
|
"homepage": "https://unavatar.io",
|
|
5
|
-
"version": "3.
|
|
5
|
+
"version": "3.46.1",
|
|
6
6
|
"main": "src/index.js",
|
|
7
7
|
"exports": {
|
|
8
8
|
".": "./src/index.js",
|
|
@@ -128,6 +128,7 @@
|
|
|
128
128
|
"@metascraper/helpers": "~5.52.0",
|
|
129
129
|
"@microlink/mql": "~0.17.0",
|
|
130
130
|
"@microlink/ping-url": "~1.4.16",
|
|
131
|
+
"bimi-url": "~1.0.0",
|
|
131
132
|
"browserless": "~13.6.0",
|
|
132
133
|
"cacheable-lookup": "~6.1.0",
|
|
133
134
|
"data-uri-regex": "~0.1.4",
|
package/src/avatar/auto.js
CHANGED
|
@@ -8,6 +8,7 @@ const isEmail = require('is-email-like')
|
|
|
8
8
|
const pTimeout = require('p-timeout')
|
|
9
9
|
const pAny = require('p-any')
|
|
10
10
|
|
|
11
|
+
const { RESERVED_ADDRESS_CODE } = require('../util/is-reserved-ip')
|
|
11
12
|
const httpStatus = require('../util/http-status')
|
|
12
13
|
const isIterable = require('../util/is-iterable')
|
|
13
14
|
const ExtendableError = require('../util/error')
|
|
@@ -23,14 +24,28 @@ const getInputType = input => {
|
|
|
23
24
|
return 'username'
|
|
24
25
|
}
|
|
25
26
|
|
|
26
|
-
const factory = ({
|
|
27
|
+
const factory = ({
|
|
28
|
+
constants,
|
|
29
|
+
providers,
|
|
30
|
+
providerTiers,
|
|
31
|
+
reachableUrl,
|
|
32
|
+
isReservedIp
|
|
33
|
+
}) => {
|
|
27
34
|
const { REQUEST_TIMEOUT } = constants
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
35
|
+
|
|
36
|
+
const refuseReservedAddress = provider => {
|
|
37
|
+
throw new ExtendableError({
|
|
38
|
+
message: 'The URL points to a reserved address.',
|
|
39
|
+
provider,
|
|
40
|
+
statusCode: httpStatus.FORBIDDEN
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const assertPublicUrl = async (url, provider) => {
|
|
45
|
+
if (await isReservedIp(new URL(url).hostname)) {
|
|
46
|
+
refuseReservedAddress(provider)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
34
49
|
|
|
35
50
|
const getAvatarContent = provider => async output => {
|
|
36
51
|
if (typeof output !== 'string' || output === '') {
|
|
@@ -55,7 +70,12 @@ const factory = ({ constants, providers, providersBy, reachableUrl }) => {
|
|
|
55
70
|
})
|
|
56
71
|
}
|
|
57
72
|
|
|
58
|
-
|
|
73
|
+
await assertPublicUrl(output, provider)
|
|
74
|
+
|
|
75
|
+
const { statusCode, url, reservedAddress } = await reachableUrl(output)
|
|
76
|
+
|
|
77
|
+
if (reservedAddress) refuseReservedAddress(provider)
|
|
78
|
+
await assertPublicUrl(url, provider)
|
|
59
79
|
|
|
60
80
|
if (!reachableUrl.isReachable({ statusCode })) {
|
|
61
81
|
throw new ExtendableError({
|
|
@@ -68,43 +88,69 @@ const factory = ({ constants, providers, providersBy, reachableUrl }) => {
|
|
|
68
88
|
return { type: 'url', data: url, provider }
|
|
69
89
|
}
|
|
70
90
|
|
|
71
|
-
const getAvatar = async (
|
|
91
|
+
const getAvatar = async (
|
|
92
|
+
fn,
|
|
93
|
+
provider,
|
|
94
|
+
input,
|
|
95
|
+
context,
|
|
96
|
+
deadline = Date.now() + REQUEST_TIMEOUT
|
|
97
|
+
) => {
|
|
72
98
|
const promise = Promise.resolve(fn(input, context))
|
|
73
99
|
.then(getAvatarContent(provider))
|
|
74
100
|
.catch(error => {
|
|
75
101
|
isIterable.forEach(error, error => {
|
|
102
|
+
if (error.code === RESERVED_ADDRESS_CODE) {
|
|
103
|
+
error.statusCode = httpStatus.FORBIDDEN
|
|
104
|
+
}
|
|
76
105
|
error.statusCode = error.statusCode ?? error.response?.statusCode
|
|
77
106
|
error.provider = provider
|
|
78
107
|
})
|
|
79
108
|
throw error
|
|
80
109
|
})
|
|
81
110
|
|
|
82
|
-
return pTimeout(promise,
|
|
83
|
-
error
|
|
84
|
-
|
|
85
|
-
|
|
111
|
+
return pTimeout(promise, Math.max(0, deadline - Date.now())).catch(
|
|
112
|
+
error => {
|
|
113
|
+
error.provider = provider
|
|
114
|
+
throw error
|
|
115
|
+
}
|
|
116
|
+
)
|
|
86
117
|
}
|
|
87
118
|
|
|
88
|
-
const
|
|
89
|
-
|
|
119
|
+
const raceTier = (tier, input, context, deadline) =>
|
|
120
|
+
pAny(
|
|
121
|
+
tier.map(provider =>
|
|
122
|
+
getAvatar(providers[provider], provider, input, context, deadline)
|
|
123
|
+
)
|
|
124
|
+
)
|
|
90
125
|
|
|
91
|
-
|
|
92
|
-
|
|
126
|
+
const auto = inputType => async (input, context) => {
|
|
127
|
+
const tierKey =
|
|
128
|
+
inputType === 'email' && isHash(input) ? 'emailHash' : inputType
|
|
129
|
+
const tiers = providerTiers[tierKey]
|
|
130
|
+
|
|
131
|
+
if (!tiers?.length) {
|
|
132
|
+
throw new ExtendableError({
|
|
133
|
+
message: `No providers declared for \`${tierKey}\`.`,
|
|
134
|
+
statusCode: httpStatus.NOT_FOUND
|
|
135
|
+
})
|
|
93
136
|
}
|
|
94
137
|
|
|
95
|
-
const
|
|
138
|
+
const deadline = Date.now() + REQUEST_TIMEOUT
|
|
139
|
+
|
|
140
|
+
let firstError
|
|
96
141
|
|
|
97
|
-
for (
|
|
98
|
-
|
|
99
|
-
|
|
142
|
+
for (const tier of tiers) {
|
|
143
|
+
try {
|
|
144
|
+
return await raceTier(tier, input, context, deadline)
|
|
145
|
+
} catch (error) {
|
|
146
|
+
firstError ??= error
|
|
147
|
+
if (Date.now() >= deadline) break
|
|
148
|
+
}
|
|
100
149
|
}
|
|
101
150
|
|
|
102
|
-
|
|
151
|
+
throw firstError
|
|
103
152
|
}
|
|
104
153
|
|
|
105
|
-
const auto = inputType => (input, context) =>
|
|
106
|
-
resolveAutoByType(inputType, input, context)
|
|
107
|
-
|
|
108
154
|
return { auto, getInputType, getAvatar }
|
|
109
155
|
}
|
|
110
156
|
|
package/src/index.js
CHANGED
|
@@ -22,12 +22,14 @@ module.exports = ({
|
|
|
22
22
|
redis
|
|
23
23
|
})
|
|
24
24
|
const cache = require('./util/cache')({ createMultiCache, createRedisCache })
|
|
25
|
+
const dnsResolver = require('./util/dns-resolver')(constants)
|
|
25
26
|
const cacheableLookup = require('./util/cacheable-lookup')({
|
|
26
27
|
...constants,
|
|
27
|
-
cache: cache.dnsCache
|
|
28
|
+
cache: cache.dnsCache,
|
|
29
|
+
resolver: dnsResolver
|
|
28
30
|
})
|
|
29
31
|
const isReservedIp = require('./util/is-reserved-ip')({ cacheableLookup })
|
|
30
|
-
const got = require('./util/got')({ cacheableLookup })
|
|
32
|
+
const got = require('./util/got')({ cacheableLookup, isReservedIp })
|
|
31
33
|
const reachableUrl = require('./util/reachable-url')({
|
|
32
34
|
got,
|
|
33
35
|
pingCache: cache.pingCache
|
|
@@ -50,16 +52,20 @@ module.exports = ({
|
|
|
50
52
|
got,
|
|
51
53
|
reachableUrl,
|
|
52
54
|
isReservedIp,
|
|
55
|
+
dnsResolver,
|
|
56
|
+
bimiCache: cache.bimiCache,
|
|
53
57
|
githubSearchCache: cache.githubSearchCache,
|
|
54
58
|
itunesSearchCache: cache.itunesSearchCache
|
|
55
59
|
}
|
|
56
|
-
const { providers, providersBy } =
|
|
60
|
+
const { providers, providerTiers, providersBy } =
|
|
61
|
+
require('./providers')(providerCtx)
|
|
57
62
|
|
|
58
63
|
const { auto, getInputType, getAvatar } = require('./avatar/auto')({
|
|
59
64
|
constants,
|
|
60
65
|
providers,
|
|
61
|
-
|
|
62
|
-
reachableUrl
|
|
66
|
+
providerTiers,
|
|
67
|
+
reachableUrl,
|
|
68
|
+
isReservedIp
|
|
63
69
|
})
|
|
64
70
|
|
|
65
71
|
const unavatar = input => auto(getInputType(input))(input, {})
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const createGetLogo = require('bimi-url')
|
|
4
|
+
|
|
5
|
+
const parseInput = input => input.slice(input.lastIndexOf('@') + 1)
|
|
6
|
+
|
|
7
|
+
module.exports = ({ bimiCache, dnsResolver, got }) => {
|
|
8
|
+
const getLogo = createGetLogo({
|
|
9
|
+
gotOpts: got.gotOpts,
|
|
10
|
+
keyvOpts: bimiCache,
|
|
11
|
+
resolveTxt: hostname => dnsResolver.resolveTxt(hostname)
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
return function bimi (input) {
|
|
15
|
+
return getLogo(parseInput(input))
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
module.exports.parseInput = parseInput
|
package/src/providers/index.js
CHANGED
|
@@ -1,86 +1,99 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
email: ['gravatar', 'github'],
|
|
3
|
+
const providerTiers = {
|
|
4
|
+
email: [['gravatar', 'github'], ['bimi']],
|
|
5
|
+
emailHash: [['gravatar']],
|
|
5
6
|
username: [
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
7
|
+
[
|
|
8
|
+
'apple-music',
|
|
9
|
+
'apple-store',
|
|
10
|
+
'behance',
|
|
11
|
+
'bilibili',
|
|
12
|
+
'bluesky',
|
|
13
|
+
'buymeacoffee',
|
|
14
|
+
'cashapp',
|
|
15
|
+
'codepen',
|
|
16
|
+
'cults3d',
|
|
17
|
+
'cursor',
|
|
18
|
+
'deezer',
|
|
19
|
+
'deviantart',
|
|
20
|
+
'discord',
|
|
21
|
+
'dockerhub',
|
|
22
|
+
'dribbble',
|
|
23
|
+
'facebook',
|
|
24
|
+
'flickr',
|
|
25
|
+
'gitee',
|
|
26
|
+
'github',
|
|
27
|
+
'gitlab',
|
|
28
|
+
'google-play',
|
|
29
|
+
'huggingface',
|
|
30
|
+
'hevy',
|
|
31
|
+
'instagram',
|
|
32
|
+
'juejin',
|
|
33
|
+
'ko-fi',
|
|
34
|
+
'linkedin',
|
|
35
|
+
'mastodon',
|
|
36
|
+
'medium',
|
|
37
|
+
'netease-music',
|
|
38
|
+
'npm',
|
|
39
|
+
'onlyfans',
|
|
40
|
+
'openstreetmap',
|
|
41
|
+
'patreon',
|
|
42
|
+
'paypal',
|
|
43
|
+
'pinterest',
|
|
44
|
+
'printables',
|
|
45
|
+
'primal',
|
|
46
|
+
'producthunt',
|
|
47
|
+
'psnprofiles',
|
|
48
|
+
'qq',
|
|
49
|
+
'raycast',
|
|
50
|
+
'reddit',
|
|
51
|
+
'revolut',
|
|
52
|
+
'snapchat',
|
|
53
|
+
'soundcloud',
|
|
54
|
+
'spotify',
|
|
55
|
+
'stackoverflow',
|
|
56
|
+
'steam',
|
|
57
|
+
'strava',
|
|
58
|
+
'substack',
|
|
59
|
+
'telegram',
|
|
60
|
+
'thingiverse',
|
|
61
|
+
'threads',
|
|
62
|
+
'tidal',
|
|
63
|
+
'tieba',
|
|
64
|
+
'tiktok',
|
|
65
|
+
'tumblr',
|
|
66
|
+
'twitch',
|
|
67
|
+
'venmo',
|
|
68
|
+
'vimeo',
|
|
69
|
+
'weibo',
|
|
70
|
+
'whatsapp',
|
|
71
|
+
'wise',
|
|
72
|
+
'x',
|
|
73
|
+
'xboxgamertag',
|
|
74
|
+
'youtube',
|
|
75
|
+
'zhihu'
|
|
76
|
+
]
|
|
74
77
|
],
|
|
75
|
-
domain: ['duckduckgo', 'google', 'microlink']
|
|
78
|
+
domain: [['bimi'], ['duckduckgo', 'google', 'microlink']]
|
|
76
79
|
}
|
|
77
80
|
|
|
81
|
+
const { emailHash, ...publicTiers } = providerTiers
|
|
82
|
+
|
|
83
|
+
const providersBy = Object.fromEntries(
|
|
84
|
+
Object.entries(publicTiers).map(([inputType, tiers]) => [
|
|
85
|
+
inputType,
|
|
86
|
+
tiers.flat()
|
|
87
|
+
])
|
|
88
|
+
)
|
|
89
|
+
|
|
78
90
|
module.exports = ctx => {
|
|
79
91
|
const providers = {
|
|
80
92
|
'apple-music': require('./apple-music')(ctx),
|
|
81
93
|
'apple-store': require('./apple-store')(ctx),
|
|
82
94
|
behance: require('./behance')(ctx),
|
|
83
95
|
bilibili: require('./bilibili')(ctx),
|
|
96
|
+
bimi: require('./bimi')(ctx),
|
|
84
97
|
bluesky: require('./bluesky')(ctx),
|
|
85
98
|
buymeacoffee: require('./buymeacoffee')(ctx),
|
|
86
99
|
cashapp: require('./cashapp')(ctx),
|
|
@@ -151,5 +164,5 @@ module.exports = ctx => {
|
|
|
151
164
|
zhihu: require('./zhihu')(ctx)
|
|
152
165
|
}
|
|
153
166
|
|
|
154
|
-
return { providers, providersBy }
|
|
167
|
+
return { providers, providerTiers, providersBy }
|
|
155
168
|
}
|
package/src/util/cache.js
CHANGED
|
@@ -3,11 +3,21 @@
|
|
|
3
3
|
const ms = require('ms')
|
|
4
4
|
|
|
5
5
|
module.exports = ({ createMultiCache, createRedisCache }) => ({
|
|
6
|
-
|
|
6
|
+
bimiCache: createMultiCache(
|
|
7
|
+
createRedisCache({ namespace: 'bimi', ttl: ms('1d') })
|
|
8
|
+
),
|
|
9
|
+
dnsCache: createMultiCache(
|
|
10
|
+
createRedisCache({ namespace: 'dns', ttl: ms('1d') })
|
|
11
|
+
),
|
|
7
12
|
githubSearchCache: createRedisCache({
|
|
8
13
|
namespace: 'github-search',
|
|
9
14
|
ttl: ms('1d')
|
|
10
15
|
}),
|
|
11
|
-
pingCache: createMultiCache(
|
|
12
|
-
|
|
16
|
+
pingCache: createMultiCache(
|
|
17
|
+
createRedisCache({ namespace: 'ping', ttl: ms('1d') })
|
|
18
|
+
),
|
|
19
|
+
itunesSearchCache: createRedisCache({
|
|
20
|
+
namespace: 'itunes-search',
|
|
21
|
+
ttl: ms('7d')
|
|
22
|
+
})
|
|
13
23
|
})
|
|
@@ -1,22 +1,10 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const CacheableLookup = require('cacheable-lookup')
|
|
4
|
-
const Tangerine = require('tangerine')
|
|
5
4
|
|
|
6
|
-
module.exports = ({ TTL_DEFAULT,
|
|
5
|
+
module.exports = ({ TTL_DEFAULT, cache, resolver }) =>
|
|
7
6
|
new CacheableLookup({
|
|
8
7
|
maxTtl: TTL_DEFAULT,
|
|
9
8
|
cache,
|
|
10
|
-
resolver
|
|
11
|
-
{
|
|
12
|
-
cache: false,
|
|
13
|
-
timeout: DNS_TIMEOUT,
|
|
14
|
-
servers: DNS_SERVERS
|
|
15
|
-
},
|
|
16
|
-
require('got').extend({
|
|
17
|
-
responseType: 'buffer',
|
|
18
|
-
decompress: false,
|
|
19
|
-
retry: 0
|
|
20
|
-
})
|
|
21
|
-
)
|
|
9
|
+
resolver
|
|
22
10
|
})
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const Tangerine = require('tangerine')
|
|
4
|
+
const got = require('got')
|
|
5
|
+
|
|
6
|
+
module.exports = ({ DNS_TIMEOUT, DNS_SERVERS }) =>
|
|
7
|
+
new Tangerine(
|
|
8
|
+
{
|
|
9
|
+
cache: false,
|
|
10
|
+
timeout: DNS_TIMEOUT,
|
|
11
|
+
servers: DNS_SERVERS
|
|
12
|
+
},
|
|
13
|
+
got.extend({
|
|
14
|
+
responseType: 'buffer',
|
|
15
|
+
decompress: false,
|
|
16
|
+
retry: 0
|
|
17
|
+
})
|
|
18
|
+
)
|
package/src/util/got.js
CHANGED
|
@@ -5,6 +5,8 @@ const tlsHook = require('https-tls/hook')
|
|
|
5
5
|
const uaHints = require('ua-hints')
|
|
6
6
|
const got = require('got')
|
|
7
7
|
|
|
8
|
+
const { RESERVED_ADDRESS_CODE } = require('./is-reserved-ip')
|
|
9
|
+
|
|
8
10
|
const topUserAgents = require('top-user-agents')
|
|
9
11
|
const randomUserAgent = uniqueRandomArray(topUserAgents)
|
|
10
12
|
|
|
@@ -36,11 +38,40 @@ const userAgentHook = options => {
|
|
|
36
38
|
}
|
|
37
39
|
}
|
|
38
40
|
|
|
39
|
-
module.exports = ({ cacheableLookup }) => {
|
|
41
|
+
module.exports = ({ cacheableLookup, isReservedIp }) => {
|
|
42
|
+
/**
|
|
43
|
+
* The refused address is reported on `options.context` because a caller
|
|
44
|
+
* reading the outcome through `reachable-url` never sees the rejection: it is
|
|
45
|
+
* swallowed there and served back as a response.
|
|
46
|
+
*
|
|
47
|
+
* Only a context the caller owns is written: the one got defaults to is
|
|
48
|
+
* frozen and shared between requests, so asking to be told is what passing a
|
|
49
|
+
* context means.
|
|
50
|
+
*
|
|
51
|
+
* got replaces the error with a RequestError that keeps the message and the
|
|
52
|
+
* code, so the refusal is named by a code rather than by its type.
|
|
53
|
+
*/
|
|
54
|
+
const reservedAddressHook = async options => {
|
|
55
|
+
const hostname = options.url?.hostname
|
|
56
|
+
if (hostname && (await isReservedIp(hostname))) {
|
|
57
|
+
if (Object.isExtensible(options.context)) {
|
|
58
|
+
options.context.reservedAddress = hostname
|
|
59
|
+
}
|
|
60
|
+
const error = new Error(
|
|
61
|
+
`Refusing to request a reserved address: ${hostname}`
|
|
62
|
+
)
|
|
63
|
+
error.code = RESERVED_ADDRESS_CODE
|
|
64
|
+
throw error
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
40
68
|
const gotOpts = {
|
|
41
69
|
dnsCache: cacheableLookup,
|
|
42
70
|
https: { rejectUnauthorized: false },
|
|
43
|
-
hooks: {
|
|
71
|
+
hooks: {
|
|
72
|
+
beforeRequest: [reservedAddressHook, userAgentHook, tlsHook],
|
|
73
|
+
beforeRedirect: [reservedAddressHook]
|
|
74
|
+
}
|
|
44
75
|
}
|
|
45
76
|
|
|
46
77
|
const instance = got.extend(gotOpts)
|
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
const ip = require('ipaddr.js')
|
|
4
4
|
|
|
5
|
+
const unbracket = hostname =>
|
|
6
|
+
hostname.startsWith('[') && hostname.endsWith(']')
|
|
7
|
+
? hostname.slice(1, -1)
|
|
8
|
+
: hostname
|
|
9
|
+
|
|
5
10
|
module.exports = ({ cacheableLookup }) => {
|
|
6
11
|
const getIpAddress = async hostname => {
|
|
7
12
|
if (ip.IPv4.isIPv4(hostname)) return hostname
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
hostname.endsWith(']') &&
|
|
11
|
-
ip.IPv6.isIPv6(hostname.slice(1, -1))
|
|
12
|
-
) {
|
|
13
|
-
return hostname.slice(1, -1)
|
|
14
|
-
}
|
|
13
|
+
const literal = unbracket(hostname)
|
|
14
|
+
if (ip.IPv6.isIPv6(literal)) return literal
|
|
15
15
|
const { address } = await cacheableLookup.lookupAsync(hostname)
|
|
16
16
|
return address
|
|
17
17
|
}
|
|
@@ -21,3 +21,5 @@ module.exports = ({ cacheableLookup }) => {
|
|
|
21
21
|
return ip.process(ipAddress).range() !== 'unicast'
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
|
+
|
|
25
|
+
module.exports.RESERVED_ADDRESS_CODE = 'ERESERVEDADDRESSRANGE'
|
|
@@ -7,11 +7,12 @@ module.exports = ({ got, pingCache }) => {
|
|
|
7
7
|
value: ({ url, statusCode }) => ({ url, statusCode })
|
|
8
8
|
})
|
|
9
9
|
|
|
10
|
-
const reachableUrl = (url, opts) =>
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
10
|
+
const reachableUrl = async (url, opts) => {
|
|
11
|
+
const context = {}
|
|
12
|
+
const response = await pingUrl(url, { ...got.gotOpts, ...opts, context })
|
|
13
|
+
const { reservedAddress } = context
|
|
14
|
+
return reservedAddress ? { ...response, reservedAddress } : response
|
|
15
|
+
}
|
|
15
16
|
|
|
16
17
|
reachableUrl.isReachable = createPingUrl.isReachable
|
|
17
18
|
|