{"_id":"@broberg/gravatar","_rev":"3-ab17ab95b2df7e4cabbfe59549f784f3","name":"@broberg/gravatar","dist-tags":{"latest":"0.2.1"},"versions":{"0.1.0":{"name":"@broberg/gravatar","version":"0.1.0","keywords":["gravatar","avatar","sha256","initials","react","preact","broberg"],"license":"MIT","_id":"@broberg/gravatar@0.1.0","maintainers":[{"name":"cbroberg","email":"cb@webhouse.dk"}],"homepage":"https://github.com/broberg-ai/components#readme","bugs":{"url":"https://github.com/broberg-ai/components/issues"},"dist":{"shasum":"debbcc34807e43413ef96fe2a0e6f2a5f1a7d65b","tarball":"https://registry.npmjs.org/@broberg/gravatar/-/gravatar-0.1.0.tgz","fileCount":19,"integrity":"sha512-1QrJmaD7eDMrAqK7ydU3IeXRGSwcH9vLzYzs51BOKVQ5BKS/J4XqNwIoMGwJWWJzwlH/lYuTf+QjLKU2xANuMw==","signatures":[{"sig":"MEUCIQDv85fFjOZqkH1D/JbZQlym27lSA+NPD6m9UAgGz/5JqQIgLy8Vfsjd/cyb66CybXqhsQW65nKuOtCnR/i4T8MKpyk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":61246},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./react":{"types":"./dist/react.d.ts","import":"./dist/react.js","require":"./dist/react.cjs"},"./preact":{"types":"./dist/preact.d.ts","import":"./dist/preact.js","require":"./dist/preact.cjs"}},"gitHead":"d8d1bcd04227861bfedc839d288486917a9d6f32","scripts":{"test":"vitest run","build":"tsup","typecheck":"tsc --noEmit"},"_npmUser":{"name":"cbroberg","email":"cb@webhouse.dk"},"repository":{"url":"git+https://github.com/broberg-ai/components.git","type":"git","directory":"packages/gravatar"},"_npmVersion":"11.10.1","description":"Isomorphic Gravatar helper (SHA-256) + initials fallback. Headless core + React and Preact adapter components with 3-tier fallback: uploaded picture → Gravatar → generated initials.","directories":{},"sideEffects":false,"_nodeVersion":"25.7.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.0","react":"^19.0.0","preact":"^10.23.0","vitest":"^2.1.0","typescript":"^5.6.0","@types/react":"^19.0.0","@radix-ui/react-avatar":"^1.1.0"},"peerDependencies":{"react":">=18.0.0","preact":">=10.0.0","@radix-ui/react-avatar":">=1.0.0"},"peerDependenciesMeta":{"react":{"optional":true},"preact":{"optional":true},"@radix-ui/react-avatar":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/gravatar_0.1.0_1782801519609_0.05738293050255305","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@broberg/gravatar","version":"0.2.0","keywords":["gravatar","avatar","sha256","initials","react","preact","broberg"],"license":"MIT","_id":"@broberg/gravatar@0.2.0","maintainers":[{"name":"cbroberg","email":"cb@webhouse.dk"}],"homepage":"https://github.com/broberg-ai/components#readme","bugs":{"url":"https://github.com/broberg-ai/components/issues"},"dist":{"shasum":"273d38f9eca1b74907f2c67b6640d5134658b67d","tarball":"https://registry.npmjs.org/@broberg/gravatar/-/gravatar-0.2.0.tgz","fileCount":20,"integrity":"sha512-sVJYUYmJRp9im8bM8uPHl8gxXD8hvraevwt+deigBW6aK1Z/84PRLPa19CseAKMYjRRIIYzUmlzWD77jsEkuzQ==","signatures":[{"sig":"MEYCIQCpAc05+3UlTB4i8YF1sYjof0B/xAiHDVNrr+3SJnUjawIhAK+g083bq9G5MB0L5O2ITGO+3N2rxkd0KFlfjRG4OTpB","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@broberg%2fgravatar@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":90596},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./react":{"types":"./dist/react.d.ts","import":"./dist/react.js","require":"./dist/react.cjs"},"./preact":{"types":"./dist/preact.d.ts","import":"./dist/preact.js","require":"./dist/preact.cjs"}},"gitHead":"b4554a79a9465edc67531efcfc6c41bbae1b29fe","scripts":{"test":"vitest run","build":"rm -rf dist && tsup && node verify-exports.mjs","typecheck":"tsc --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:6af0046c-e967-4fd1-98e3-df1cb5b8876b"}},"repository":{"url":"git+https://github.com/broberg-ai/components.git","type":"git","directory":"packages/gravatar"},"_npmVersion":"11.5.1","description":"Isomorphic Gravatar helper (SHA-256) + initials fallback. Headless core + React and Preact adapter components with 3-tier fallback: uploaded picture → Gravatar → generated initials.","directories":{},"sideEffects":false,"_nodeVersion":"22.23.2","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.0","react":"^19.0.0","preact":"^10.23.0","vitest":"^2.1.0","typescript":"^5.6.0","@types/react":"^19.0.0","@radix-ui/react-avatar":"^1.1.0"},"peerDependencies":{"react":">=18.0.0","preact":">=10.0.0","@radix-ui/react-avatar":">=1.0.0"},"peerDependenciesMeta":{"react":{"optional":true},"preact":{"optional":true},"@radix-ui/react-avatar":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/gravatar_0.2.0_1787422484253_0.9031687979872962","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"name":"@broberg/gravatar","version":"0.2.1","description":"Isomorphic Gravatar helper (SHA-256) + initials fallback. Headless core + React and Preact adapter components with 3-tier fallback: uploaded picture → Gravatar → generated initials.","type":"module","license":"MIT","sideEffects":false,"main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./react":{"types":"./dist/react.d.ts","import":"./dist/react.js","require":"./dist/react.cjs"},"./preact":{"types":"./dist/preact.d.ts","import":"./dist/preact.js","require":"./dist/preact.cjs"}},"scripts":{"build":"rm -rf dist && tsup && node verify-exports.mjs","test":"vitest run","typecheck":"tsc --noEmit"},"peerDependencies":{"@radix-ui/react-avatar":">=1.0.0","preact":">=10.0.0","react":">=18.0.0"},"peerDependenciesMeta":{"@radix-ui/react-avatar":{"optional":true},"preact":{"optional":true},"react":{"optional":true}},"devDependencies":{"@radix-ui/react-avatar":"^1.1.0","@types/react":"^19.0.0","preact":"^10.23.0","react":"^19.0.0","tsup":"^8.3.0","typescript":"^5.6.0","vitest":"^2.1.0"},"keywords":["gravatar","avatar","sha256","initials","react","preact","broberg"],"repository":{"type":"git","url":"git+https://github.com/broberg-ai/components.git","directory":"packages/gravatar"},"publishConfig":{"access":"public"},"_id":"@broberg/gravatar@0.2.1","gitHead":"63baa6ae5aef802930ce8154c4414dceb2c69e2b","bugs":{"url":"https://github.com/broberg-ai/components/issues"},"homepage":"https://github.com/broberg-ai/components#readme","_nodeVersion":"22.23.2","_npmVersion":"11.5.1","dist":{"integrity":"sha512-pAk+gipHuwjWc8TRYlyHOo+AZMDjj0XnfPOU2r4AKWEEVgAh6ROMa/nG9+XwMg6g1ozifKkQG7+skf3IxAp4kw==","shasum":"f5ab0af45089281c2a14e5fe969825f8bc7b1c70","tarball":"https://registry.npmjs.org/@broberg/gravatar/-/gravatar-0.2.1.tgz","fileCount":20,"unpackedSize":96452,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@broberg%2fgravatar@0.2.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIDACg+jovX593NdQXXtA/hKz3F2YUBVlT2SezR/9t2ptAiEA0lYYfy8yQfds/P/xEoGNpDLmDZ5llGQm4wikG5Dw8qM="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:6af0046c-e967-4fd1-98e3-df1cb5b8876b"}},"directories":{},"maintainers":[{"name":"cbroberg","email":"cb@webhouse.dk"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/gravatar_0.2.1_1787423432507_0.5943838559494363"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-30T06:38:39.391Z","modified":"2026-08-22T18:30:33.065Z","0.1.0":"2026-06-30T06:38:39.744Z","0.2.0":"2026-08-22T18:14:44.385Z","0.2.1":"2026-08-22T18:30:32.671Z"},"bugs":{"url":"https://github.com/broberg-ai/components/issues"},"license":"MIT","homepage":"https://github.com/broberg-ai/components#readme","keywords":["gravatar","avatar","sha256","initials","react","preact","broberg"],"repository":{"type":"git","url":"git+https://github.com/broberg-ai/components.git","directory":"packages/gravatar"},"description":"Isomorphic Gravatar helper (SHA-256) + initials fallback. Headless core + React and Preact adapter components with 3-tier fallback: uploaded picture → Gravatar → generated initials.","maintainers":[{"name":"cbroberg","email":"cb@webhouse.dk"}],"readme":"# @broberg/gravatar\n\nGravatar URLs and avatar initials for the fleet. Zero runtime dependencies, isomorphic.\n\n```bash\nnpm i @broberg/gravatar\n```\n\n```ts\nimport { gravatarUrl, gravatarLookup, getInitials } from \"@broberg/gravatar\";\n\nawait gravatarUrl(\"cb@webhouse.dk\", { size: 80, default: \"404\" });\nawait gravatarLookup(\"cb@webhouse.dk\");   // 'yes' | 'no' | 'unknown'\ngetInitials(\"Christian Broberg\");          // 'CB'\n```\n\nAlso ships `@broberg/gravatar/react` and `@broberg/gravatar/preact`.\n\n## `gravatarLookup()` — three outcomes, because there are three\n\n```ts\nconst presence = await gravatarLookup(\"cb@webhouse.dk\");\nif (presence !== \"unknown\") cache.set(email, presence);   // never cache a guess\n```\n\n| Response | Result | Meaning |\n|---|---|---|\n| `404` | `no` | genuinely no avatar |\n| `200` | `yes` | |\n| 5xx · a throw · a timeout · a surprise status | `unknown` | **do not cache this as a no** |\n\n**Why it exists (v0.2.0).** The old boolean collapsed three facts into two: a 503 from Automattic and a dropped connection both came back `false`, which reads as *\"there is no picture\"*.\n\nAnd you **cache** that answer — you have to, or you ask Automattic on every page render. So a ten-second hiccup cost a user their avatar until the cache expired, with nothing at the call-site able to tell. Found by a consumer on the day they adopted this package.\n\n`gravatarExists()` is unchanged for existing callers and **still cannot tell those two apart** — both are `false`. If you store its result, you are storing a guess on every failure. Use `gravatarLookup` for anything you cache.\n\n## `getInitials()` — letters, not characters\n\n```ts\ngetInitials(\"Lens (verifikation)\");   // 'LV'    (was 'L(' before v0.2.0)\ngetInitials(null, \"x@webhouse.dk\");   // 'X'     (was 'X@')\ngetInitials(\"  \");                    // '??'    (was two spaces)\ngetInitials(\"李 明\");                  // '李明'\ngetInitials(\"José\");                  // 'JO'\n```\n\nSplits on non-letter/non-digit, unicode-aware — so CJK and accented Latin work, which an ASCII-only test suite would never have caught.\n\nThe email branch takes the **prefix** before `@`. That is what its documentation always claimed; the code read the whole address and only looked right because most addresses happen to begin with two letters.\n\n**An email in the `name` slot works (v0.2.1).** `getInitials(\"cb@webhouse.dk\")` → `CB`, identical to `getInitials(null, \"cb@webhouse.dk\")` — because `navn = bruger.navn || bruger.email` is an ordinary call-site and the two arguments must agree. Detection is conservative: exactly one `@`, non-empty and whitespace-free on both sides, so `\"Anne @ Hansen\"` stays a name.\n\n> 0.2.0 got this wrong and shipped it: every address in the name slot came out ending in the TLD's first letter (`cb@webhouse.dk` → `CD`). The acceptance criterion that should have caught it asserted the same string in the *email* argument only — the claim was about the common case, the test was about one argument position, and both were green.\n\n**Known limitation, deliberate.** `getInitials(\"Jens Hansen, direktør\")` returns `JD` — it takes the title as the surname. Truncating at the first comma would fix the Danish *\"Name, Title\"* form and **break** the equally common *\"Surname, Firstname\"* export. Two conventions, opposite fixes, and nothing in the string to tell them apart. Left alone rather than guessed at.\n\n## The core is `async`, on purpose\n\n`gravatarHash` and `gravatarUrl` return promises because they use `crypto.subtle` — the one hashing API that exists in Node, Bun, Deno, workers *and* the browser.\n\nA Node-only consumer expects a synchronous `createHash` and will be surprised. That is the isomorphism tax, and it is deliberate: the alternative is two code paths and a bundler condition on every consumer.\n\n## API\n\n| Export | |\n|---|---|\n| `gravatarHash(email)` | SHA-256 of the trimmed, lowercased address |\n| `gravatarUrl(email, { size?, default? })` | the full image URL |\n| `gravatarLookup(email, size?)` | `'yes' \\| 'no' \\| 'unknown'` |\n| `gravatarExists(email, size?)` | `boolean` — lossy; see above |\n| `getInitials(name?, email?)` | up to two uppercase characters, or `'??'` |\n","readmeFilename":"README.md"}