@unavatar/core 3.8.0 → 3.9.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/README.md CHANGED
@@ -1,5 +1,6 @@
1
1
  ![logo](https://unavatar.io/banner.png ':id=banner')
2
2
 
3
+ - [Introduction](#introduction)
3
4
  - [Quick start](#quick-start)
4
5
  - [Query parameters](#query-parameters)
5
6
  - [TTL](#ttl)
@@ -16,13 +17,15 @@
16
17
  - [GitLab](#gitlab)
17
18
  - [LinkedIn](#linkedin)
18
19
  - [Google](#google)
19
- - [Gravatar](#gravatar)
20
20
  - [Instagram](#instagram)
21
+ - [Ko-fi](#ko-fi)
22
+ - [Medium](#medium)
21
23
  - [Microlink](#microlink)
22
24
  - [Mastodon](#mastodon)
23
25
  - [OnlyFans](#onlyfans)
24
26
  - [OpenStreetMap](#openstreetmap)
25
27
  - [Patreon](#patreon)
28
+ - [Printables](#printables)
26
29
  - [Reddit](#reddit)
27
30
  - [SoundCloud](#soundcloud)
28
31
  - [Spotify](#spotify)
@@ -32,18 +35,17 @@
32
35
  - [Twitch](#twitch)
33
36
  - [Vimeo](#vimeo)
34
37
  - [WhatsApp](#whatsapp)
35
- - [X/Twitter](#xtwitter)
36
38
  - [YouTube](#youtube)
37
39
  - [Response Format](#response-format)
38
40
  - [Response Headers](#response-headers)
39
- - [Response Errors](#response-errors)
40
- - [Contact](#contact)
41
41
 
42
42
  ---
43
43
 
44
+ ## Introduction
45
+
44
46
  Welcome to **unavatar.io**, the ultimate avatar service that offers everything you need to easily retrieve user avatars:
45
47
 
46
- - **Versatile**: A wide range of platforms and services including [TikTok](#tiktok), [Instagram](#instagram), [YouTube](#youtube), [X/Twitter](#xtwitter), [Gravatar](#gravatar), etc., meaning you can rule all of them just querying against unavatar.
48
+ - **Versatile**: A wide range of platforms and services including [TikTok](https://unavatar.io/docs#tiktok), [Instagram](https://unavatar.io/docs#instagram), [YouTube](https://unavatar.io/docs#youtube), [X/Twitter](https://unavatar.io/docs#xtwitter), [Gravatar](https://unavatar.io/docs#gravatar), etc., meaning you can rule all of them just querying against unavatar.
47
49
 
48
50
  - **Speed**: Designed to be fast and efficient with a 97% cache hit rate, serving 24.3 TB of data across 522M requests.
49
51
 
@@ -61,14 +63,14 @@ The service is exposed in **unavatar.io** via provider endpoints:
61
63
  - an **username**: [unavatar.io/github/kikobeats](https://unavatar.io/github/kikobeats)
62
64
  - a **domain**: [unavatar.io/google/reddit.com](https://unavatar.io/google/reddit.com)
63
65
 
64
- Use the `/:provider/:key` format for all lookups. You can read more about available providers in [providers](#providers).
66
+ Use the `/:provider/:key` format for all lookups. You can read more about available providers in [providers](https://unavatar.io/docs#providers).
65
67
 
66
68
  ## Query parameters
67
69
 
68
70
  ### TTL
69
71
 
70
- Type: `number` or `string`<br>
71
- Default: `'24h'`<br>
72
+ Type: `number` or `string`\
73
+ Default: `'24h'`\
72
74
  Range: from `'1h'` to `'28d'`
73
75
 
74
76
  It determines the maximum quantity of time an avatar is considered fresh.
@@ -125,23 +127,25 @@ You can verify for your rate limit state checking the following headers in the r
125
127
  - `x-rate-limit-remaining`: The number of requests remaining in the current rate limit window.
126
128
  - `x-rate-limit-reset`: The time at which the current rate limit window resets in UTC epoch seconds.
127
129
 
128
- For higher usage, the plan is a usage-based plan billed monthly that removes rate limits and unlocks custom TTL.
130
+ For higher usage, the **[PRO](https://unavatar.io/checkout)** plan is a usage-based plan billed monthly that removes rate limits and unlocks custom TTL.
129
131
 
130
132
  Every request has a cost in tokens (**\$0.001 per token**) based on the proxy tier needed to resolve the avatar:
131
133
 
132
134
  | Proxy tier | Tokens | Cost |
133
- |-------------|--------|---------|
135
+ | ----------- | ------ | ------- |
134
136
  | Origin | 1 | \$0.001 |
135
137
  | Datacenter | +2 | \$0.003 |
136
138
  | Residential | +4 | \$0.007 |
137
139
 
138
140
  The proxy tier used is returned in the `x-proxy-tier` response header, and the total cost in the `x-unavatar-cost` header.
139
141
 
140
- $ curl -I -H "x-api-key: YOUR_API_KEY" https://unavatar.io/instagram/kikobeats
142
+ ``` bash
143
+ $ curl -I -H "x-api-key: YOUR_API_KEY" https://[unavatar.io/instagram/kikobeats](https://unavatar.io/instagram/kikobeats)
141
144
 
142
- x-pricing-tier: pro
143
- x-proxy-tier: origin
144
- x-unavatar-cost: 1
145
+ x-pricing-tier: pro
146
+ x-proxy-tier: origin
147
+ x-unavatar-cost: 1
148
+ ```
145
149
 
146
150
  To upgrade, visit [unavatar.io/checkout](https://unavatar.io/checkout). After completing the payment, you'll receive an API key.
147
151
 
@@ -151,7 +155,7 @@ To upgrade, visit [unavatar.io/checkout](https://unavatar.io/checkout). After co
151
155
 
152
156
  Get artwork for any Apple Music artist, album, or song. Search by name or look up directly by numeric Apple Music ID.
153
157
 
154
- e.g., [unavatar.io/apple-music/daft%20punk](https://unavatar.io/apple-music/daft%20punk)
158
+ e.g., [unavatar.io/apple-music/artist:daft%20punk](https://unavatar.io/apple-music/artist:daft%20punk)
155
159
 
156
160
  The endpoint supports explicit type as part of the input.
157
161
 
@@ -184,7 +188,7 @@ Get any DeviantArt user's profile picture by their username.
184
188
 
185
189
  Available inputs:
186
190
 
187
- - Username, e.g., [unavatar.io/deviantart/spyed](https://unavatar.io/deviantart/spyed)
191
+ - slug, e.g., [unavatar.io/deviantart/spyed](https://unavatar.io/deviantart/spyed)
188
192
 
189
193
  ### Dribbble
190
194
 
@@ -192,7 +196,7 @@ Get any Dribbble designer's profile picture by their username.
192
196
 
193
197
  Available inputs:
194
198
 
195
- - Username, e.g., [unavatar.io/dribbble/omidnikrah](https://unavatar.io/dribbble/omidnikrah)
199
+ - slug, e.g., [unavatar.io/dribbble/omidnikrah](https://unavatar.io/dribbble/omidnikrah)
196
200
 
197
201
  ### DuckDuckGo
198
202
 
@@ -200,7 +204,7 @@ Get the favicon or logo for any domain via DuckDuckGo's icon service. Useful as
200
204
 
201
205
  Available inputs:
202
206
 
203
- - Domain, e.g., [unavatar.io/duckduckgo/gummibeer.dev](https://unavatar.io/duckduckgo/gummibeer.dev)
207
+ - Domain, e.g., [unavatar.io/duckduckgo/microsoft.com](https://unavatar.io/duckduckgo/microsoft.com)
204
208
 
205
209
  ### GitHub
206
210
 
@@ -213,20 +217,27 @@ Available inputs:
213
217
 
214
218
  ### GitLab
215
219
 
216
- Get any GitLab user or group's profile picture by their username.
220
+ Get any GitLab user or organization's profile picture by their username.
217
221
 
218
222
  Available inputs:
219
223
 
220
224
  - User, e.g., [unavatar.io/gitlab/sytses](https://unavatar.io/gitlab/sytses)
221
- - Group, e.g., [unavatar.io/gitlab/inkscape](https://unavatar.io/gitlab/inkscape)
225
+ - Organization, e.g., [unavatar.io/gitlab/inkscape](https://unavatar.io/gitlab/inkscape)
222
226
 
223
227
  ### LinkedIn
224
228
 
225
- Get any LinkedIn user's profile picture by their public profile slug.
229
+ Get any LinkedIn user or company profile picture by their public profile slug.
226
230
 
227
- Available inputs:
231
+ e.g., [unavatar.io/linkedin/user:wesbos](https://unavatar.io/linkedin/user:wesbos)
228
232
 
229
- - Profile slug, e.g., [unavatar.io/linkedin/kikobeats](https://unavatar.io/linkedin/kikobeats)
233
+ The input supports a URI format `type:id`.
234
+
235
+ When no type is provided, it defaults to `user` (user profile).
236
+
237
+ Available URI format inputs:
238
+
239
+ - `user` (default): [unavatar.io/linkedin/user:wesbos](https://unavatar.io/linkedin/user:wesbos)
240
+ - `company`: [unavatar.io/linkedin/company:microlinkhq](https://unavatar.io/linkedin/company:microlinkhq)
230
241
 
231
242
  ### Google
232
243
 
@@ -234,9 +245,7 @@ Get the favicon or logo for any domain using Google's favicon service.
234
245
 
235
246
  Available inputs:
236
247
 
237
- - Domain, e.g., [unavatar.io/google/netflix.com](https://unavatar.io/google/netflix.com)
238
-
239
- ### Gravatar
248
+ - Domain, e.g., [unavatar.io/google/stremio.com](https://unavatar.io/google/stremio.com)
240
249
 
241
250
  Get any user's avatar by their email address via Gravatar. The most widely used global avatar service — if your users have a Gravatar set up, this is the fastest way to retrieve it.
242
251
 
@@ -250,7 +259,23 @@ Get any Instagram user's profile picture by their username. No authentication or
250
259
 
251
260
  Available inputs:
252
261
 
253
- - Username, e.g., [unavatar.io/instagram/willsmith](https://unavatar.io/instagram/willsmith)
262
+ - slug, e.g., [unavatar.io/instagram/willsmith](https://unavatar.io/instagram/willsmith)
263
+
264
+ ### Ko-fi
265
+
266
+ Get any Ko-fi page's profile picture by their public creator page slug.
267
+
268
+ Available inputs:
269
+
270
+ - Page slug, e.g., [unavatar.io/ko-fi/geekshock](https://unavatar.io/ko-fi/geekshock)
271
+
272
+ ### Medium
273
+
274
+ Get any Medium author's profile picture by their username.
275
+
276
+ Available inputs:
277
+
278
+ - slug, e.g., [unavatar.io/medium/juancalmaraz](https://unavatar.io/medium/juancalmaraz)
254
279
 
255
280
  ### Microlink
256
281
 
@@ -274,7 +299,7 @@ Get any OnlyFans creator's profile picture by their username.
274
299
 
275
300
  Available inputs:
276
301
 
277
- - Username, e.g., [unavatar.io/onlyfans/amandaribas](https://unavatar.io/onlyfans/amandaribas)
302
+ - slug, e.g., [unavatar.io/onlyfans/amandaribas](https://unavatar.io/onlyfans/amandaribas)
278
303
 
279
304
  ### OpenStreetMap
280
305
 
@@ -283,7 +308,7 @@ Get any OpenStreetMap contributor's profile picture. Accepts either a numeric us
283
308
  Available inputs:
284
309
 
285
310
  - Numeric user ID, e.g., [unavatar.io/openstreetmap/98672](https://unavatar.io/openstreetmap/98672)
286
- - Username, e.g., [unavatar.io/openstreetmap/Terence%20Eden](https://unavatar.io/openstreetmap/Terence%20Eden)
311
+ - slug, e.g., [unavatar.io/openstreetmap/Terence%20Eden](https://unavatar.io/openstreetmap/Terence%20Eden)
287
312
 
288
313
  ### Patreon
289
314
 
@@ -291,7 +316,15 @@ Get any Patreon creator's profile picture by their username.
291
316
 
292
317
  Available inputs:
293
318
 
294
- - Username, e.g., [unavatar.io/patreon/kikobeats](https://unavatar.io/patreon/kikobeats)
319
+ - slug, e.g., [unavatar.io/patreon/gametestro](https://unavatar.io/patreon/gametestro)
320
+
321
+ ### Printables
322
+
323
+ Get any Printables user's profile picture by their username.
324
+
325
+ Available inputs:
326
+
327
+ - slug, e.g., [unavatar.io/printables/DukeDoks](https://unavatar.io/printables/DukeDoks)
295
328
 
296
329
  ### Reddit
297
330
 
@@ -299,7 +332,7 @@ Get any Reddit user's avatar by their username.
299
332
 
300
333
  Available inputs:
301
334
 
302
- - Username, e.g., [unavatar.io/reddit/kikobeats](https://unavatar.io/reddit/kikobeats)
335
+ - slug, e.g., [unavatar.io/reddit/kikobeats](https://unavatar.io/reddit/kikobeats)
303
336
 
304
337
  ### SoundCloud
305
338
 
@@ -307,13 +340,13 @@ Get any SoundCloud artist's profile picture by their username.
307
340
 
308
341
  Available inputs:
309
342
 
310
- - Username, e.g., [unavatar.io/soundcloud/gorillaz](https://unavatar.io/soundcloud/gorillaz)
343
+ - slug, e.g., [unavatar.io/soundcloud/gorillaz](https://unavatar.io/soundcloud/gorillaz)
311
344
 
312
345
  ### Spotify
313
346
 
314
347
  Get artwork for any Spotify entity — users, artists, albums, playlists, shows, episodes, or tracks. Look up by username or Spotify ID.
315
348
 
316
- e.g., [unavatar.io/spotify/kikobeats](https://unavatar.io/spotify/kikobeats)
349
+ e.g., [unavatar.io/spotify/album:7I9Wh2IgvI3Nnr8Z1ZSWby](https://unavatar.io/spotify/album:7I9Wh2IgvI3Nnr8Z1ZSWby)
317
350
 
318
351
  The endpoint supports explicit type as part of the input.
319
352
 
@@ -321,13 +354,13 @@ If explicit type is not provided, it defaults to `user`.
321
354
 
322
355
  Available URI format inputs:
323
356
 
324
- - `user`: [unavatar.io/spotify/kikobeats](https://unavatar.io/spotify/kikobeats)
325
- - `artist`: [unavatar.io/spotify/artist:6sFIWsNpZYqbRiDnNOkZCA](https://unavatar.io/spotify/artist:6sFIWsNpZYqbRiDnNOkZCA)
326
- - `playlist`: [unavatar.io/spotify/playlist:37i9dQZF1DXcBWIGoYBM5M](https://unavatar.io/spotify/playlist:37i9dQZF1DXcBWIGoYBM5M)
327
- - `album`: [unavatar.io/spotify/album:4aawyAB9vmqN3uQ7FjRGTy](https://unavatar.io/spotify/album:4aawyAB9vmqN3uQ7FjRGTy)
328
- - `show`: [unavatar.io/spotify/show:6UCtBYL29hRg064d4i5W2i](https://unavatar.io/spotify/show:6UCtBYL29hRg064d4i5W2i)
329
- - `episode`: [unavatar.io/spotify/episode:512ojhOuo1ktJprKbVcKyQ](https://unavatar.io/spotify/episode:512ojhOuo1ktJprKbVcKyQ)
330
- - `track`: [unavatar.io/spotify/track:11dFghVXANMlKmJXsNCbNl](https://unavatar.io/spotify/track:11dFghVXANMlKmJXsNCbNl)
357
+ - `album`: [unavatar.io/spotify/album:7I9Wh2IgvI3Nnr8Z1ZSWby](https://unavatar.io/spotify/album:7I9Wh2IgvI3Nnr8Z1ZSWby)
358
+ - `artist`: [unavatar.io/spotify/artist:1vCWHaC5f2uS3yhpwWbIA6](https://unavatar.io/spotify/artist:1vCWHaC5f2uS3yhpwWbIA6)
359
+ - `episode`: [unavatar.io/spotify/episode:1YNm34Q8ofC2CDTYYLaFMj](https://unavatar.io/spotify/episode:1YNm34Q8ofC2CDTYYLaFMj)
360
+ - `playlist`: [unavatar.io/spotify/playlist:37i9dQZF1DZ06evO3KIUZW](https://unavatar.io/spotify/playlist:37i9dQZF1DZ06evO3KIUZW)
361
+ - `show`: [unavatar.io/spotify/show:0iykbhPkRz53QF8LR2UyNO](https://unavatar.io/spotify/show:0iykbhPkRz53QF8LR2UyNO)
362
+ - `track`: [unavatar.io/spotify/track:4OROzZUy6gOWN4UGQVaZMF](https://unavatar.io/spotify/track:4OROzZUy6gOWN4UGQVaZMF)
363
+ - `user` (default): [unavatar.io/spotify/user:kikobeats](https://unavatar.io/spotify/user:kikobeats)
331
364
 
332
365
  ### Substack
333
366
 
@@ -343,7 +376,7 @@ Get any Telegram user's profile picture by their username.
343
376
 
344
377
  Available inputs:
345
378
 
346
- - Username, e.g., [unavatar.io/telegram/drsdavidsoft](https://unavatar.io/telegram/drsdavidsoft)
379
+ - slug, e.g., [unavatar.io/telegram/drsdavidsoft](https://unavatar.io/telegram/drsdavidsoft)
347
380
 
348
381
  ### TikTok
349
382
 
@@ -351,7 +384,7 @@ Get any TikTok user's profile picture by their username. No authentication or AP
351
384
 
352
385
  Available inputs:
353
386
 
354
- - Username, e.g., [unavatar.io/tiktok/carlosazaustre](https://unavatar.io/tiktok/carlosazaustre)
387
+ - slug, e.g., [unavatar.io/tiktok/carlosazaustre](https://unavatar.io/tiktok/carlosazaustre)
355
388
 
356
389
  ### Twitch
357
390
 
@@ -359,7 +392,7 @@ Get any Twitch streamer's profile picture by their username.
359
392
 
360
393
  Available inputs:
361
394
 
362
- - Username, e.g., [unavatar.io/twitch/midudev](https://unavatar.io/twitch/midudev)
395
+ - slug, e.g., [unavatar.io/twitch/midudev](https://unavatar.io/twitch/midudev)
363
396
 
364
397
  ### Vimeo
365
398
 
@@ -367,28 +400,29 @@ Get any Vimeo user's profile picture by their username.
367
400
 
368
401
  Available inputs:
369
402
 
370
- - Username, e.g., [unavatar.io/vimeo/staff](https://unavatar.io/vimeo/staff)
403
+ - slug, e.g., [unavatar.io/vimeo/ladieswithlenses](https://unavatar.io/vimeo/ladieswithlenses)
371
404
 
372
405
  ### WhatsApp
373
406
 
374
407
  Get the profile picture for a WhatsApp phone number, channel, chat, or group.
375
408
 
376
- The input supports a URI format `type:id`. When no type is provided, it defaults to `phone`.
409
+ e.g., [unavatar.io/whatsapp/phone:34660021551](https://unavatar.io/whatsapp/phone:34660021551)
377
410
 
378
- Available URI format inputs:
411
+ The input supports a URI format `type:id`.
379
412
 
380
- - `phone` (default): [unavatar.io/whatsapp/34612345678](https://unavatar.io/whatsapp/34612345678)
381
- - `channel`: [unavatar.io/whatsapp/channel:0029VaABC1234abcDEF56789](https://unavatar.io/whatsapp/channel:0029VaABC1234abcDEF56789)
382
- - `chat`: [unavatar.io/whatsapp/chat:ABC1234DEFghi](https://unavatar.io/whatsapp/chat:ABC1234DEFghi)
383
- - `group`: [unavatar.io/whatsapp/group:ABC1234DEFghi](https://unavatar.io/whatsapp/group:ABC1234DEFghi)
413
+ When no type is provided, it defaults to `phone`.
384
414
 
385
- ### X/Twitter
415
+ Available URI format inputs:
416
+
417
+ - `phone` (default): [unavatar.io/whatsapp/phone:34660021551](https://unavatar.io/whatsapp/phone:34660021551)
418
+ - `channel`: [unavatar.io/whatsapp/channel:0029VaARuQ7KwqSXh9fiMc0m](https://unavatar.io/whatsapp/channel:0029VaARuQ7KwqSXh9fiMc0m)
419
+ - `chat`: [unavatar.io/whatsapp/chat:D2FFycjQXrEIKG8qQjbwZz](https://unavatar.io/whatsapp/chat:D2FFycjQXrEIKG8qQjbwZz)
386
420
 
387
421
  Get any X (formerly Twitter) user's profile picture by their username.
388
422
 
389
423
  Available inputs:
390
424
 
391
- - Username, e.g., [unavatar.io/x/kikobeats](https://unavatar.io/x/kikobeats)
425
+ - slug, e.g., [unavatar.io/x/kikobeats](https://unavatar.io/x/kikobeats)
392
426
 
393
427
  ### YouTube
394
428
 
@@ -409,35 +443,33 @@ Available inputs:
409
443
 
410
444
  A response is returning the user avatar by default.
411
445
 
412
- However, you can get a [json](#json) as response payload.
446
+ However, you can get a [json](https://unavatar.io/docs#json) as response payload.
413
447
 
414
448
  When an endpoint returns JSON, the shape is predictable so you can parse it reliably in your app:
415
449
 
416
- | Field | Type | Present in | Description |
417
- |----|----|----|----|
418
- | `status` | `string` | all JSON responses | One of: `success`, `fail`, `error`. |
419
- | `message` | `string` | all JSON responses | Human-readable summary for display/logging. |
420
- | `data` | `object` | `success` | Response payload for successful requests. |
421
- | `code` | `string` | `fail`, `error` | Stable machine-readable error code. |
422
- | `more` | `string (URL)` | most `fail`/`error` responses | Documentation URL with troubleshooting details. |
423
- | `report` | `string` | some `error` responses | Support contact channel (for example `mailto:`). |
450
+ | Field | Type | Present in | Description |
451
+ | --------- | -------------- | ------------------------------- | ------------------------------------------------ |
452
+ | `status` | `string` | all JSON responses | One of: `success`, `fail`, `error`. |
453
+ | `message` | `string` | all JSON responses | Human-readable summary for display/logging. |
454
+ | `data` | `object` | `success` | Response payload for successful requests. |
455
+ | `code` | `string` | `fail`, `error` | Stable machine-readable error code. |
456
+ | `more` | `string (URL)` | most `fail` / `error` responses | Documentation URL with troubleshooting details. |
457
+ | `report` | `string` | some `error` responses | Support contact channel (for example `mailto:`). |
424
458
 
425
459
  ## Response Headers
426
460
 
427
461
  These headers help you understand pricing, limits, and request diagnostics.
428
462
 
429
- | Header | Purpose |
430
- |----|----|
431
- | `x-pricing-tier` | `free` or `pro` — the plan used for this request |
432
- | `x-timestamp` | Server timestamp when request was received |
433
- | `x-unavatar-cost` | Token cost of the request (avatar routes only) |
434
- | `x-proxy-tier` | Proxy tier used: `origin`, `datacenter`, or `residential` |
435
- | `x-rate-limit-limit` | Maximum requests allowed per window (free tier only) |
436
- | `x-rate-limit-remaining` | Remaining requests in current window (free tier only) |
437
- | `x-rate-limit-reset` | UTC epoch seconds when window resets (free tier only) |
438
- | `retry-after` | Seconds until rate limit resets (only on 429 responses) |
439
-
440
- ## Response Errors
463
+ | Header | Purpose |
464
+ | ------------------------ | --------------------------------------------------------- |
465
+ | `x-pricing-tier` | `free` or `pro` — the plan used for this request |
466
+ | `x-timestamp` | Server timestamp when request was received |
467
+ | `x-unavatar-cost` | Token cost of the request (avatar routes only) |
468
+ | `x-proxy-tier` | Proxy tier used: `origin`, `datacenter`, or `residential` |
469
+ | `x-rate-limit-limit` | Maximum requests allowed per window (free tier only) |
470
+ | `x-rate-limit-remaining` | Remaining requests in current window (free tier only) |
471
+ | `x-rate-limit-reset` | UTC epoch seconds when window resets (free tier only) |
472
+ | `retry-after` | Seconds until rate limit resets (only on 429 responses) |
441
473
 
442
474
  Expected errors are known operational cases returned with stable codes.
443
475
 
@@ -450,7 +482,7 @@ Expected errors are known operational cases returned with stable codes.
450
482
  - `report` (when present) indicates how to contact support for server errors.
451
483
 
452
484
  | HTTP | Code | Typical trigger |
453
- |------|----------------------|---------------------------------------------|
485
+ | ---- | -------------------- | ------------------------------------------- |
454
486
  | 400 | `ESESSIONID` | Missing `session_id` in `/checkout/success` |
455
487
  | 400 | `ESESSION` | Checkout session not paid or not found |
456
488
  | 400 | `ESIGNATURE` | Missing `stripe-signature` header |
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.8.0",
5
+ "version": "3.9.1",
6
6
  "main": "src/index.js",
7
7
  "exports": {
8
8
  ".": "./src/index.js",
@@ -131,13 +131,13 @@
131
131
  "got": "~11.8.6",
132
132
  "html-get": "~2.22.0",
133
133
  "https-tls": "~1.0.24",
134
+ "ipaddr.js": "~2.3.0",
134
135
  "is-absolute-url": "~3.0.3",
135
136
  "is-antibot": "~1.3.0",
136
137
  "is-email-like": "~1.0.0",
137
138
  "lodash": "~4.17.23",
138
139
  "ms": "~2.1.3",
139
140
  "p-any": "~3.0.0",
140
- "p-cancelable": "2.1.1",
141
141
  "p-timeout": "~4.1.0",
142
142
  "puppeteer": "~24.40.0",
143
143
  "re2": "~1.23.3",
@@ -66,8 +66,8 @@ const factory = ({ constants, providers, providersBy, reachableUrl }) => {
66
66
  return { type: 'url', data: url, provider }
67
67
  }
68
68
 
69
- const getAvatar = async (fn, provider, args) => {
70
- const promise = Promise.resolve(fn(args))
69
+ const getAvatar = async (fn, provider, input, context) => {
70
+ const promise = Promise.resolve(fn(input, context))
71
71
  .then(getAvatarContent(provider))
72
72
  .catch(error => {
73
73
  isIterable.forEach(error, error => {
@@ -83,26 +83,20 @@ const factory = ({ constants, providers, providersBy, reachableUrl }) => {
83
83
  })
84
84
  }
85
85
 
86
- const resolveAutoByType = async (inputType, args) => {
86
+ const resolveAutoByType = async (inputType, input, context) => {
87
87
  const collection = providerEntriesByType[inputType]
88
88
  const promises = new Array(collection.length)
89
89
 
90
90
  for (let index = 0; index < collection.length; index++) {
91
91
  const [provider, fn] = collection[index]
92
- promises[index] = getAvatar(fn, provider, args)
92
+ promises[index] = getAvatar(fn, provider, input, context)
93
93
  }
94
94
 
95
95
  return pAny(promises)
96
96
  }
97
97
 
98
- const auto = inputTypeOrArgs => {
99
- if (typeof inputTypeOrArgs === 'string') {
100
- return args => resolveAutoByType(inputTypeOrArgs, args)
101
- }
102
-
103
- const args = inputTypeOrArgs
104
- return resolveAutoByType(getInputType(args.input), args)
105
- }
98
+ const auto = inputType => (input, context) =>
99
+ resolveAutoByType(inputType, input, context)
106
100
 
107
101
  return { auto, getInputType, getAvatar }
108
102
  }
package/src/index.js CHANGED
@@ -12,12 +12,21 @@ module.exports = ({ constants: userConstants, redis, onFetchHTML } = {}) => {
12
12
  constants.DNS_TIMEOUT = Math.floor(constants.REQUEST_TIMEOUT * (1 / 5))
13
13
  }
14
14
 
15
- const { createMultiCache, createRedisCache } = require('./util/keyv')({ ...constants, redis })
15
+ const { createMultiCache, createRedisCache } = require('./util/keyv')({
16
+ ...constants,
17
+ redis
18
+ })
16
19
  const cache = require('./util/cache')({ createMultiCache, createRedisCache })
17
- const cacheableLookup = require('./util/cacheable-lookup')({ ...constants, cache: cache.dnsCache })
20
+ const cacheableLookup = require('./util/cacheable-lookup')({
21
+ ...constants,
22
+ cache: cache.dnsCache
23
+ })
18
24
  const isReservedIp = require('./util/is-reserved-ip')({ cacheableLookup })
19
25
  const got = require('./util/got')({ cacheableLookup })
20
- const reachableUrl = require('./util/reachable-url')({ got, pingCache: cache.pingCache })
26
+ const reachableUrl = require('./util/reachable-url')({
27
+ got,
28
+ pingCache: cache.pingCache
29
+ })
21
30
  const createBrowser = require('./util/browserless')(constants)
22
31
  const getHTML = require('./util/html-get')({ createBrowser, got })
23
32
  const { createHtmlProvider, getOgImage } = require('./util/html-provider')({
@@ -43,13 +52,10 @@ module.exports = ({ constants: userConstants, redis, onFetchHTML } = {}) => {
43
52
  reachableUrl
44
53
  })
45
54
 
46
- const normalizeArgs = input =>
47
- typeof input === 'string' ? { input } : input
48
-
49
- const unavatar = input => auto(normalizeArgs(input))
55
+ const unavatar = input => auto(getInputType(input))(input, {})
50
56
 
51
57
  Object.keys(providers).forEach(name => {
52
- unavatar[name] = input => getAvatar(providers[name], name, normalizeArgs(input))
58
+ unavatar[name] = input => getAvatar(providers[name], name, input, {})
53
59
  })
54
60
 
55
61
  unavatar.auto = auto
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
3
  module.exports = () =>
4
- function duckduckgo ({ input }) {
4
+ function duckduckgo (input) {
5
5
  return `https://icons.duckduckgo.com/ip3/${input}.ico`
6
6
  }
@@ -3,7 +3,7 @@
3
3
  const { stringify } = require('querystring')
4
4
 
5
5
  module.exports = ({ constants }) =>
6
- async function github ({ input }) {
6
+ function github (input) {
7
7
  return `https://github.com/${input}.png?${stringify({
8
8
  size: constants.AVATAR_SIZE
9
9
  })}`
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
3
  module.exports = () =>
4
- function google ({ input }) {
4
+ function google (input) {
5
5
  return `https://www.google.com/s2/favicons?domain_url=${input}&sz=128`
6
6
  }
@@ -7,8 +7,10 @@ const stringify = require('../util/stringify')
7
7
  const md5 = str => crypto.createHash('md5').update(str).digest('hex')
8
8
 
9
9
  module.exports = ({ constants }) =>
10
- function gravatar ({ input }) {
11
- return `https://gravatar.com/avatar/${md5(input.trim().toLowerCase())}?${stringify({
10
+ function gravatar (input) {
11
+ return `https://gravatar.com/avatar/${md5(
12
+ input.trim().toLowerCase()
13
+ )}?${stringify({
12
14
  size: constants.AVATAR_SIZE,
13
15
  d: '404'
14
16
  })}`
@@ -35,7 +35,7 @@ const parseMastodonInput = input => {
35
35
  }
36
36
 
37
37
  module.exports = ({ got, isReservedIp }) => {
38
- const mastodon = async function ({ input }) {
38
+ const mastodon = async function (input) {
39
39
  const parsed = parseMastodonInput(input)
40
40
  if (!parsed) return undefined
41
41
 
@@ -1,15 +1,15 @@
1
1
  'use strict'
2
2
 
3
- const PCancelable = require('p-cancelable')
4
3
  const mql = require('@microlink/mql')
5
4
  const { get } = require('lodash')
6
5
 
7
6
  module.exports = ({ constants }) =>
8
- PCancelable.fn(async function microlink ({ input, req = {} }, onCancel) {
9
- const promise = mql(`https://${input}`, {
10
- apiKey: req.isPro ? constants.MICROLINK_API_KEY : req.headers?.['x-api-key']
7
+ async function microlink (input, context) {
8
+ const req = context?.req
9
+ const { data } = await mql(`https://${input}`, {
10
+ apiKey: req?.isPro
11
+ ? constants.MICROLINK_API_KEY
12
+ : req?.headers?.['x-api-key']
11
13
  })
12
- onCancel(() => promise.onCancel())
13
- const { data } = await promise
14
14
  return get(data, 'logo.url')
15
- })
15
+ }
@@ -12,12 +12,12 @@ module.exports = ({ got, createHtmlProvider }) => {
12
12
  getter: $ => $('img.user_image').attr('src')
13
13
  })
14
14
 
15
- return function openstreetmap (ctx) {
16
- return OPENSTREETMAP_USER_ID_REGEX.test(ctx.input)
17
- ? got(`${OPENSTREETMAP_API_URL}/${ctx.input}.json`, {
15
+ return function openstreetmap (input, context) {
16
+ return OPENSTREETMAP_USER_ID_REGEX.test(input)
17
+ ? got(`${OPENSTREETMAP_API_URL}/${input}.json`, {
18
18
  responseType: 'json'
19
19
  }).then(({ body }) => body?.user?.img?.href)
20
- : fromUsername(ctx)
20
+ : fromUsername(input, context)
21
21
  }
22
22
  }
23
23
 
@@ -39,9 +39,8 @@ module.exports = ({ PROXY_TIMEOUT, getHTML, onFetchHTML }) => {
39
39
  * @param {() => object} [opts.htmlOpts] - Returns extra options merged into the fetch call.
40
40
  */
41
41
  const createHtmlProvider = ({ name, url, getter, isBlocked, htmlOpts }) => {
42
- const provider = async function ({ input, req = {}, res = {} }) {
42
+ const provider = async function (input, context) {
43
43
  const providerUrl = await url(input)
44
- const context = { provider: name, input, providerUrl }
45
44
 
46
45
  const attempt = async gotOpts => {
47
46
  const providerOpts = htmlOpts?.() ?? {}
@@ -56,7 +55,7 @@ module.exports = ({ PROXY_TIMEOUT, getHTML, onFetchHTML }) => {
56
55
  const fetchOpts = gotOpts ? { ...defaultOpts, ...gotOpts } : defaultOpts
57
56
  const tier = fetchOpts.tier ?? 'origin'
58
57
 
59
- const log = debug.duration({ ...context, tier })
58
+ const log = debug.duration({ provider: name, input, providerUrl, tier })
60
59
 
61
60
  const {
62
61
  $,
@@ -116,7 +115,7 @@ module.exports = ({ PROXY_TIMEOUT, getHTML, onFetchHTML }) => {
116
115
  }
117
116
 
118
117
  if (typeof onFetchHTML === 'function') {
119
- return onFetchHTML({ attempt, provider: name, providerUrl, req, res })
118
+ return onFetchHTML({ ...context, attempt, provider: name, providerUrl })
120
119
  }
121
120
 
122
121
  const result = await attempt()