@unavatar/core 3.25.5 → 3.25.7

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
@@ -4,6 +4,9 @@
4
4
 
5
5
  - [Introduction](#introduction)
6
6
  - [Quick start](#quick-start)
7
+ - [Attribution](#attribution)
8
+ - [How to add attribution](#how-to-add-attribution)
9
+ - [Remove attribution](#remove-attribution)
7
10
  - [Authentication](#authentication)
8
11
  - [Pricing](#pricing)
9
12
  - [Cache](#cache)
@@ -73,18 +76,70 @@ It's proudly powered by [microlink.io](https://microlink.io/), the headless brow
73
76
 
74
77
  ## Quick start
75
78
 
76
- The service is exposed in **unavatar.io** via provider endpoints:
79
+ The service is exposed in **unavatar.io** via endpoints.
77
80
 
78
- - an **email (auto-detect)**: [unavatar.io/hello@microlink.io](https://unavatar.io/hello@microlink.io) tries Gravatar, then GitHub
79
- - an **email** via Gravatar: [unavatar.io/gravatar/hello@microlink.io](https://unavatar.io/gravatar/hello@microlink.io)
80
- - a **Gravatar SHA-256** (same email, pre-hashed): [unavatar.io/gravatar/b1f507c7a29adfa84eaa521036774b0577c58f23f2f3f42e068d6ac256cffae2](https://unavatar.io/gravatar/b1f507c7a29adfa84eaa521036774b0577c58f23f2f3f42e068d6ac256cffae2)
81
- - an **email** via GitHub: [unavatar.io/github/sindresorhus@gmail.com](https://unavatar.io/github/sindresorhus@gmail.com)
81
+ An endpoint determines how the avatar is looked up and what parameters are required.
82
+
83
+ For example, you can get an avatar for:
84
+
85
+ - an **email**: [unavatar.io/email/hello@microlink.io](https://unavatar.io/email/hello@microlink.io)
82
86
  - an **username**: [unavatar.io/github/kikobeats](https://unavatar.io/github/kikobeats)
83
- - a **domain**: [unavatar.io/google/reddit.com](https://unavatar.io/google/reddit.com)
87
+ - a **domain**: [unavatar.io/domain/reddit.com](https://unavatar.io/domain/reddit.com)
88
+
89
+ Read [providers](https://unavatar.io/docs#providers) to know more. Each provider can accept different parameters.
90
+
91
+ ## Attribution
92
+
93
+ Free plans require attribution. Upgrade to any paid plan to remove this requirement.
94
+
95
+ Attribution ensures proper domain verification and helps us keep unavatar.io free for the community.
96
+
97
+ Your attribution link:
98
+
99
+ - Must be on your production site (not staging or localhost).
100
+ - Must be publicly accessible and viewable in browsers.
101
+ - Must not use rel="nofollow", redirects, or obfuscation.
102
+
103
+ ### How to add attribution
104
+
105
+ Add this link on any page or surface displaying unavatar.io avatars:
106
+
107
+ ```html
108
+ [Avatars provided by Unavatar](https://unavatar.io)
109
+ ```
110
+
111
+ **Example**
112
+
113
+ ```html
114
+
115
+ <img src="https://unavatar.io/github/kikobeats" alt="Kiko" />
116
+ <img src="https://unavatar.io/x/josebaseba" alt="Joseba" />
117
+
118
+ <p class="attribution">
119
+ [Avatars provided by Unavatar](https://unavatar.io)
120
+ </p>
121
+ ```
122
+
123
+ **Valid placements**
124
+
125
+ Site footer, about page, credits page, or any page that renders avatars.
126
+
127
+ **Mobile apps and non-web products**
128
+
129
+ If avatars are displayed in a mobile app, desktop app, browser extension, email, or any non-web surface, add the attribution link on your app's marketing site, app store listing description, or credits screen.
130
+
131
+ **Requirements for attribution to be valid**
132
+
133
+ We reserve the right to revoke free-tier access for any website or product that does not comply with the attribution requirements described in this document:
134
+
135
+ - Double-check the href. It must point to [https://unavatar.io](https://unavatar.io/) directly — no redirects, no URL shorteners, no tracking wrappers.
136
+ - Make sure it's crawlable. The link must be present in the rendered HTML and not blocked by robots.txt, authentication, or region gating.
137
+ - Avoid nofollow or noindex. We need a standard, followable link.
138
+ - target="\_blank" is allowed.
84
139
 
85
- Use `/:provider/:key` for provider-specific lookups, or pass an email as the only path segment for automatic resolution. You can read more in [Email avatars](https://unavatar.io/email) and [providers](https://unavatar.io/docs#providers).
140
+ ### Remove attribution
86
141
 
87
- For **Gravatar**, `key` can be a plain email or a precomputed identifier: emails are trimmed, lowercased, and hashed with **SHA-256** before calling Gravatar. Values that already look like a **64-character hex SHA-256** or **32-character hex MD5** are sent as-is (hex is normalized to lowercase).
142
+ Attribution is only required on the free plan. [Upgrade to the PRO plan](https://unavatar.io/checkout) to remove the requirement.
88
143
 
89
144
  ## Authentication
90
145
 
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.25.5",
5
+ "version": "3.25.7",
6
6
  "main": "src/index.js",
7
7
  "exports": {
8
8
  ".": "./src/index.js",
@@ -206,6 +206,7 @@
206
206
  },
207
207
  "nano-staged": {
208
208
  "*.js": [
209
+ "npx -y @kikobeats/prettier-standard",
209
210
  "standard --fix"
210
211
  ],
211
212
  "package.json": [
package/src/index.js CHANGED
@@ -2,7 +2,12 @@
2
2
 
3
3
  const DEFAULTS = require('./constant')
4
4
 
5
- module.exports = ({ constants: userConstants, redis, onFetchHTML } = {}) => {
5
+ module.exports = ({
6
+ constants: userConstants,
7
+ redis,
8
+ onFetchHTML,
9
+ userAgent
10
+ } = {}) => {
6
11
  const constants = { ...DEFAULTS, ...userConstants }
7
12
 
8
13
  if (userConstants?.REQUEST_TIMEOUT && !userConstants?.PROXY_TIMEOUT) {
@@ -33,7 +38,8 @@ module.exports = ({ constants: userConstants, redis, onFetchHTML } = {}) => {
33
38
  require('./util/html-provider')({
34
39
  ...constants,
35
40
  getHTML,
36
- onFetchHTML
41
+ onFetchHTML,
42
+ userAgent
37
43
  })
38
44
 
39
45
  const providerCtx = {
@@ -61,7 +67,8 @@ module.exports = ({ constants: userConstants, redis, onFetchHTML } = {}) => {
61
67
  unavatar[name] = input => getAvatar(providers[name], name, input, {})
62
68
  })
63
69
 
64
- const createTypedAutoResolver = inputType => input => auto(inputType)(input, {})
70
+ const createTypedAutoResolver = inputType => input =>
71
+ auto(inputType)(input, {})
65
72
 
66
73
  unavatar.email = createTypedAutoResolver('email')
67
74
  unavatar.domain = createTypedAutoResolver('domain')
@@ -4,7 +4,6 @@ const { normalizeUrl } = require('@metascraper/helpers')
4
4
  const debug = require('debug-logfmt')('html-provider')
5
5
  const isAntibot = require('is-antibot')
6
6
 
7
- const randomCrawlerAgent = require('./crawler-agent')
8
7
  const getOgImage = require('./get-og-image')
9
8
  const httpStatus = require('./http-status')
10
9
  const ExtendableError = require('./error')
@@ -34,7 +33,12 @@ const createErrorCause = ({ html, headers, statusCode }) => ({
34
33
  statusCode
35
34
  })
36
35
 
37
- module.exports = ({ PROXY_TIMEOUT, getHTML, onFetchHTML }) => {
36
+ module.exports = ({
37
+ PROXY_TIMEOUT,
38
+ getHTML,
39
+ onFetchHTML,
40
+ userAgent = require('./crawler-agent')
41
+ }) => {
38
42
  /**
39
43
  * @param {object} opts
40
44
  * @param {string} opts.name - Provider identifier used in logs and metrics.
@@ -55,13 +59,13 @@ module.exports = ({ PROXY_TIMEOUT, getHTML, onFetchHTML }) => {
55
59
  const defaultOpts = {
56
60
  ...providerOpts,
57
61
  headers: {
58
- 'user-agent': randomCrawlerAgent(),
62
+ 'user-agent': userAgent({ provider: name }),
59
63
  ...providerOpts.headers
60
64
  },
61
65
  timeout: PROXY_TIMEOUT
62
66
  }
63
67
  const fetchOpts = { ...defaultOpts, ...gotOpts }
64
- const userAgent = fetchOpts.headers['user-agent']
68
+ const resolvedUserAgent = fetchOpts.headers['user-agent']
65
69
  const tier = fetchOpts.tier ?? 'origin'
66
70
 
67
71
  const log = debug.duration({ provider: name, input, providerUrl, tier })
@@ -151,7 +155,7 @@ module.exports = ({ PROXY_TIMEOUT, getHTML, onFetchHTML }) => {
151
155
  statusCode,
152
156
  status: 'blocked',
153
157
  antibot: antibotProvider ?? undefined,
154
- userAgent
158
+ userAgent: resolvedUserAgent
155
159
  })
156
160
 
157
161
  throw error
@@ -167,7 +171,7 @@ module.exports = ({ PROXY_TIMEOUT, getHTML, onFetchHTML }) => {
167
171
  log.error({
168
172
  statusCode,
169
173
  antibot: antibotProvider ?? undefined,
170
- userAgent
174
+ userAgent: resolvedUserAgent
171
175
  })
172
176
 
173
177
  throw error