@shiinasaku/github-card 4.1.0 → 5.0.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/README.md CHANGED
@@ -1,46 +1,111 @@
1
1
  <div align="center">
2
2
 
3
- <img src="assets/logo.svg" alt="GitHub Card" width="480" />
3
+ <img src="assets/logo.svg" alt="GitHub Card" width="520" />
4
4
 
5
- # GitHub Card
5
+ ### Beautiful, blazing-fast GitHub profile cards
6
6
 
7
- **Beautiful, blazing-fast GitHub profile cards as dynamic SVGs.**
8
- Drop one line in your README — get a live, auto-updating stats card.
7
+ Live SVG cards for your README — stars, commits, languages, all auto-updating. One line, zero config, edge-cached.
9
8
 
10
- [![Bun](https://img.shields.io/badge/Bun-1.x-f9f1e1?style=flat-square&logo=bun&logoColor=000)](assets/bun.svg)
11
- [![Elysia](https://img.shields.io/badge/ElysiaJS-1.x-7c3aed?style=flat-square)](assets/elysia.svg)
9
+ <br/>
10
+
11
+ [![npm](https://img.shields.io/npm/v/@shiinasaku/github-card?style=flat-square&label=npm&color=000)](https://www.npmjs.com/package/@shiinasaku/github-card)
12
+ [![Bun](https://img.shields.io/badge/Bun-1.x-f9f1e1?style=flat-square&logo=bun&logoColor=000)](https://bun.sh)
13
+ [![Elysia](https://img.shields.io/badge/Elysia-2_RC-7c3aed?style=flat-square)](https://next.elysiajs.com)
12
14
  [![TypeScript](https://img.shields.io/badge/TypeScript-strict-3178c6?style=flat-square&logo=typescript&logoColor=fff)](https://www.typescriptlang.org)
13
15
  [![License: MIT](https://img.shields.io/badge/License-MIT-34d399?style=flat-square)](LICENSE)
14
16
  [![Deploy with Vercel](https://img.shields.io/badge/Deploy-Vercel-000?style=flat-square&logo=vercel)](https://vercel.com/new/clone?repository-url=https://github.com/ShiinaSaku/Github-Card)
15
17
 
16
- <img src="https://card.shiina.xyz/card/shiinasaku?theme=shiina" alt="Live demo card" width="500" />
18
+ <a href="https://card.shiina.xyz/card/shiinasaku?theme=shiina">
19
+ <img src="https://card.shiina.xyz/card/shiinasaku?theme=shiina" alt="Live demo — shiinasaku" width="520" />
20
+ </a>
21
+
22
+ <sub>Live instance · <code>shiinasaku</code> · theme <code>shiina</code> — updates itself as you code</sub>
17
23
 
18
24
  </div>
19
25
 
20
26
  ---
21
27
 
28
+ ### ✨ What's new in 4.2
29
+
30
+ > System-font only · no shipped `.woff2` · crisper text (`geometricPrecision`, tabular numbers) · new 24 px icons (MingCute / Huge / Tabler) · airy stat stack with 7 px gaps, no boxes · default `affiliations=owner` — org stars excluded unless `?affiliations=affiliated` · fully typed `github.ts` & `github-client`, cache v9.
31
+
32
+ ---
33
+
22
34
  ## Quickstart
23
35
 
24
- Paste this into your profile README (replace `shiinasaku` with your username):
36
+ Paste into your profile README — replace `shiinasaku` with your username:
25
37
 
26
38
  ```md
27
39
  [![GitHub Card](https://card.shiina.xyz/card/shiinasaku?theme=shiina)](https://github.com/shiinasaku)
28
40
  ```
29
41
 
30
- That's it. No sign-up, no build step — the card updates itself as you code.
42
+ That's it. No sign-up, no build — the card refreshes automatically as cached data expires.
43
+
44
+ <details>
45
+ <summary><b>Self-host / local dev</b></summary>
46
+
47
+ Requires Bun 1.4.4 or newer. The API uses Elysia 2 RC with pinned framework and plugin versions.
48
+
49
+ ```bash
50
+ bun install
51
+ cp .env.example .env # add GITHUB_TOKEN
52
+ bun dev # http://localhost:3000
53
+ ```
54
+
55
+ | Command | Action |
56
+ | ------------------- | -------------------- |
57
+ | `bun test` | Run tests |
58
+ | `bun run typecheck` | Native `bun check` |
59
+ | `bun run fmt` | Bun native formatter |
60
+ | `bun run fmt:check` | Check formatting |
61
+
62
+ Formatting uses Bun's built-in `bun fmt`; no separate formatter or linter is installed.
63
+ Bun 1.4.4 does not provide a built-in `bun lint` command. `bun check` enforces strict typing and
64
+ unused-symbol checks. TypeScript is retained only to emit the library’s `.d.ts` files during builds.
65
+
66
+ `src/index.ts` serves as the entrypoint for Vercel and local development, starting a listener
67
+ only when run directly. The rendering library is unchanged.
68
+
69
+ </details>
70
+
71
+ ---
31
72
 
32
73
  ## Why GitHub Card?
33
74
 
34
- - **One line, zero config** — an `<img>` tag is the entire integration.
35
- - **Edge-fast** — two-tier cache (in-memory + Redis) with stale-while-revalidate, ETag/304 support, and CDN cache headers. Warm requests render in microseconds.
36
- - **21 hand-tuned themes** — plus full per-slot color overrides with hex _or_ Tailwind palette tokens.
37
- - **Retina avatars** — profile images inlined as Base64 at 2x resolution.
38
- - **Adaptive layout** — width, text wrapping, and language legends are computed mathematically; nothing ever overlaps.
39
- - **Users _and_ orgs** — works for organization accounts out of the box.
40
- - **Glassmorphic depth** — layered ambient gradients track your accent color for volumetric light with zero runtime cost (pure SVG).
41
- - **OpenAPI documented** — explore the API interactively at [`/openapi`](https://card.shiina.xyz/openapi).
75
+ <table>
76
+ <tr>
77
+ <td width="50%">
78
+
79
+ **One line, zero config**<br/>
80
+ An `<img>` tag is the entire integration. No OAuth, no action.
81
+
82
+ **Edge-fast**<br/>
83
+ L1 memory + L2 Redis SWR, `s-maxage=1800`, `stale-while-revalidate`, `ETag`/`304`. Warm hits render in microseconds.
84
+
85
+ **Retina avatars**<br/>
86
+ Profile images inlined as Base64 at 2×, clipped + ringed in pure SVG.
87
+
88
+ </td>
89
+ <td width="50%">
90
+
91
+ **Adaptive layout**<br/>
92
+ Width, wrapping, and legends measured with per-glyph metrics — nothing ever overlaps or clips.
93
+
94
+ **Users _and_ orgs**<br/>
95
+ `GET /card/:login` works for users and organizations.
96
+
97
+ **Glassmorphic depth**<br/>
98
+ Radial + linear ambient gradients track your accent for volumetric light at zero runtime cost.
99
+
100
+ </td>
101
+ </tr>
102
+ </table>
103
+
104
+ OpenAPI docs live at [`/openapi`](https://card.shiina.xyz/openapi). Pure ESM lib, zero server dep for `renderCard`.
105
+
106
+ ---
42
107
 
43
- ## Themes
108
+ ## Themes — 21 hand-tuned
44
109
 
45
110
  Preview **every** theme with your own data:
46
111
 
@@ -54,31 +119,51 @@ Preview **every** theme with your own data:
54
119
  | `github_dark` · `dark` · `nord` · `dracula` | `forest` · `rose` · `cobalt` |
55
120
  | `tokyonight` · `synthwave` · `radical` · `monokai` | `gruvbox` · `merko` · `onedark` · `highcontrast` |
56
121
 
122
+ Add `?theme=tokyonight` or override any slot with hex or Tailwind tokens: `pink-400`, `#ff79c6`, `ff79c6`.
123
+
124
+ ---
125
+
57
126
  ## Customization
58
127
 
59
- Every visual slot is overridable. Colors accept hex (`ff79c6` or `#ff79c6`) and Tailwind tokens (`pink-400`).
60
-
61
- | Parameter | Type | Description |
62
- | -------------- | -------- | ------------------------------------------------------------------ |
63
- | `theme` | string | Built-in theme name (see table above) |
64
- | `bg_color` | string | Background fill |
65
- | `title_color` | string | Name / title text |
66
- | `text_color` | string | Stats, labels, and body text |
67
- | `icon_color` | string | Stat icons and ambient glow |
68
- | `border_color` | string | Card outline stroke |
69
- | `hide_border` | boolean | Remove the card outline |
70
- | `compact` | boolean | Slim layout: hides bio, pronouns, Twitter, and the language legend |
71
- | `animate` | boolean | Fade-in stats + animated language bar |
72
- | `hide` | string[] | Hide stats: `stars`, `commits`, `issues`, `repos`, `prs` |
73
- | `hide_langs` | string[] | Exclude languages by name (`hide_langs=html,css`) |
74
- | `show_langs` | string[] | Include _only_ these languages |
75
- | `lang_count` | 1–10 | Number of top languages (default `5`) |
76
- | `scope` | string | `personal` (default), `org`, or `all` contribution scope |
77
- | `orgs` | string[] | Restrict org-scope stats to these organizations |
78
- | `affiliations` | string | `affiliated` (default) or `owner` repositories |
79
- | `fields` | string[] | Fetch only what you need (e.g. `fields=stats`) |
80
-
81
- ### Examples
128
+ | Parameter | Type | Description |
129
+ | -------------- | :--------: | ----------------------------------------------------------------- |
130
+ | `theme` | `string` | Built-in theme (table above) |
131
+ | `bg_color` | `string` | Background fill |
132
+ | `title_color` | `string` | Name / title |
133
+ | `text_color` | `string` | Stats, labels, body |
134
+ | `icon_color` | `string` | Icons + ambient glow |
135
+ | `border_color` | `string` | Card outline |
136
+ | `hide_border` | `boolean` | Remove outline |
137
+ | `compact` | `boolean` | Slim — hides bio/pronouns/Twitter/legend |
138
+ | `animate` | `boolean` | Fade-in stats + animated language bar |
139
+ | `hide` | `string[]` | Hide stats: `stars`, `commits`, `issues`, `repos`, `prs` |
140
+ | `hide_langs` | `string[]` | Exclude `hide_langs=html,css` |
141
+ | `show_langs` | `string[]` | Only `show_langs=typescript,rust` |
142
+ | `lang_count` | `1–10` | Top languages (default `5`) |
143
+ | `scope` | `string` | `personal` (default), `org`, `all` |
144
+ | `orgs` | `string[]` | Restrict org stats to `orgs=vercel,elysiajs` |
145
+ | `affiliations` | `string` | `owner` (default) or `affiliated` — org stars excluded by default |
146
+ | `fields` | `string[]` | Fetch only `fields=stats` for speed |
147
+
148
+ #### What the numbers mean
149
+
150
+ - Stars, repositories, and languages come from non-fork repositories in the requested scope.
151
+ `affiliations=owner` is the default for both the HTTP API and `getProfileData`.
152
+ - User commits are contributions credited by GitHub from January 1 (UTC) to now, not lifetime
153
+ commit counts. Default user PR/issue totals are lifetime authored totals. With `scope=org`,
154
+ commits, PRs, and issues are this year's contributions to the selected organizations.
155
+ - `scope=all` combines personal and contributed organization repositories without counting a repo
156
+ twice; its activity totals remain the user's totals. `orgs` filters the organization portion.
157
+ - Organization accounts have repository/star/language data, but no computed activity totals.
158
+ Those metrics display `—` (and are `null` in `UserStats`), not a misleading zero.
159
+ - Language filters run before `lang_count`. Percentages describe the displayed languages by code
160
+ size, not time spent coding. The bar and legend use the same “Other” group.
161
+ - Results depend on what the GitHub token can access. GitHub's query limits still apply:
162
+ the top 10 languages per repository and up to 100 repositories in each org contribution
163
+ breakdown. Repository pagination supports up to 10,000 repos per connection; reaching that
164
+ limit with more pages remaining returns an error rather than caching incomplete totals.
165
+
166
+ #### Examples
82
167
 
83
168
  ```md
84
169
  <!-- Dracula, no border, animated -->
@@ -89,107 +174,106 @@ Every visual slot is overridable. Colors accept hex (`ff79c6` or `#ff79c6`) and
89
174
 
90
175
  ![card](https://card.shiina.xyz/card/shiinasaku?bg_color=0d1117&title_color=58a6ff&text_color=9da7b3&icon_color=58a6ff)
91
176
 
92
- <!-- Compact, stats only, no languages -->
177
+ <!-- Compact, no issues/prs -->
93
178
 
94
179
  ![card](https://card.shiina.xyz/card/shiinasaku?compact=true&hide=issues,prs&fields=stats)
95
180
 
96
- <!-- Organization card -->
181
+ <!-- Include org stars explicitly -->
182
+
183
+ ![card](https://card.shiina.xyz/card/shiinasaku?affiliations=affiliated)
184
+
185
+ <!-- Organization -->
97
186
 
98
187
  ![card](https://card.shiina.xyz/card/elysiajs)
99
188
  ```
100
189
 
190
+ ---
191
+
101
192
  ## Deploy your own
102
193
 
103
194
  [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/ShiinaSaku/Github-Card)
104
195
 
105
- 1. Click the button above (or fork + import into Vercel).
106
- 2. Add environment variables:
196
+ 1. Click the button (or fork + import).
197
+ 2. Add env vars:
107
198
 
108
- | Variable | Required | Purpose |
109
- | -------------- | -------- | ----------------------------------------- |
110
- | `GITHUB_TOKEN` | ✅ | GitHub PAT (read-only is enough) |
111
- | `REDIS_URL` | ➖ | Redis/Upstash URL for the shared L2 cache |
199
+ | Variable | Required | Purpose |
200
+ | :------------: | :------: | ----------------------------------- |
201
+ | `GITHUB_TOKEN` | ✅ | GitHub PAT — read-only is enough |
202
+ | `REDIS_URL` | ➖ | Redis / Upstash for shared L2 cache |
112
203
 
113
- 3. Done. Your instance serves `/card/:username` on Vercel's CDN with `s-maxage=1800, stale-while-revalidate=1800`.
204
+ 3. Done — serves `/card/:username` on Vercel CDN `s-maxage=1800, stale-while-revalidate=1800`.
114
205
 
115
- > Redis is optional — without it, the in-memory cache still keeps each warm instance fast.
206
+ > Without Redis, bounded in-memory cache (1000 entries, SWR 30 min) still keeps warm instances fast.
207
+
208
+ ---
116
209
 
117
210
  ## API
118
211
 
119
212
  | Endpoint | Description |
120
213
  | ----------------------- | ------------------------------------------- |
121
- | `GET /card/:username` | SVG card (all query params above) |
122
- | `GET /:username/themes` | One SVG showing **all themes** for a user |
214
+ | `GET /card/:username` | SVG card — all params above |
215
+ | `GET /:username/themes` | One SVG with **all themes** for a user |
123
216
  | `GET /meta` | Service metadata + theme list |
124
217
  | `GET /health` | Uptime, cache telemetry, Redis reachability |
125
218
  | `GET /openapi` | Interactive OpenAPI docs |
126
219
 
127
- Responses are `image/svg+xml` with `ETag`, CORS (`*`), and CDN cache headers. Conditional requests (`If-None-Match`) return `304`.
220
+ Responses: `image/svg+xml`, `ETag` + `CDN-Cache-Control` + CORS `*`. Conditional `If-None-Match` → `304`.
221
+
222
+ ---
128
223
 
129
224
  ## Use as a library
130
225
 
131
226
  ```bash
132
227
  bun add @shiinasaku/github-card # Bun
133
- npm install @shiinasaku/github-card # Node / Next.js / Vite
228
+ npm install @shiinasaku/github-card # Node / Next / Vite
134
229
  ```
135
230
 
136
231
  ```ts
137
232
  import { renderCard, themes } from "@shiinasaku/github-card";
138
233
 
139
234
  const svg = renderCard(user, stats, languages, { theme: "tokyonight" });
235
+ // → SVG string, no network or server required
140
236
  ```
141
237
 
142
- Ships as a prebuilt ESM bundle with full `.d.ts` types (Bun resolves the raw TypeScript source automatically via the `bun` export condition). The renderer is pure — no server, no network, works in any JS runtime.
238
+ Prebuilt `dist/lib.js` + `.d.ts`; Bun resolves raw TS via `bun` export condition. Pure renderer — no network, no server.
143
239
 
144
- ## Releasing
240
+ ---
145
241
 
146
- Releases are fully automated:
242
+ ## Architecture
147
243
 
148
- 1. **Actions → Bump** → choose `patch` / `minor` / `major`.
149
- 2. The workflow bumps `package.json`, commits, and tags `vX.Y.Z`.
150
- 3. The tag triggers **Release**: format, lint, typecheck, tests, dist build, tag↔version guard, `npm pack` verification, then publish to npm with [provenance](https://docs.npmjs.com/generating-provenance-statements) and a GitHub Release with auto-generated notes.
244
+ ```mermaid
245
+ flowchart LR
246
+ A[GET /card/:username] --> B[Elysia<br/>validation · CORS · OpenAPI · timing]
247
+ B --> C{L1 memory<br/>SWR 30m}
248
+ C -- hit --> G[render SVG<br/>ETag/304 → CDN]
249
+ C -- miss --> D{L2 Redis<br/>SWR 30m}
250
+ D -- hit --> G
251
+ D -- miss --> E[GitHub GraphQL<br/>100/page, up to 100 pages, 6s/request]
252
+ E --> F[typed fetch<br/>+ avatar 150px]
253
+ F --> G
254
+ G --> H[Pure SVG<br/>system fonts · measured layout · glass]
255
+ ```
151
256
 
152
- ## Local development
257
+ - **Bun + Elysia** — one of the fastest HTTP stacks.
258
+ - **Two-tier SWR** — stale served instantly + background refresh, request coalescing via `inFlight`.
259
+ - **Typed upstream** — `RepoNode`/`ContributionEntry` generics, `T=unknown`, `TimeoutError`/`RateLimitError` mapping.
153
260
 
154
- ```bash
155
- bun install
156
- cp .env.example .env # add your GITHUB_TOKEN
157
- bun dev # http://localhost:3000
158
- ```
261
+ ---
159
262
 
160
- | Command | Action |
161
- | ------------------- | --------------------- |
162
- | `bun test` | Run the test suite |
163
- | `bun run typecheck` | Strict `tsc --noEmit` |
164
- | `bun run lint` | oxlint |
165
- | `bun run fmt` | oxfmt |
263
+ ## Releasing
166
264
 
167
- ## Architecture
265
+ Fully automated:
168
266
 
169
- ```
170
- GET /card/:username
171
- │
172
- ▼
173
- Elysia (validation, CORS, OpenAPI, server-timing)
174
- │
175
- ▼
176
- L1 in-memory cache ──hit──► render SVG ──► ETag/304 ──► CDN
177
- │miss ▲
178
- ▼ │ stale-while-revalidate
179
- L2 Redis (SWR) ──hit──► serve stale + background refresh
180
- │miss
181
- ▼
182
- GitHub GraphQL (batched, paginated 100/page, 6s timeout)
183
- │
184
- ▼
185
- Pure SVG renderer (zero-dependency, dynamic layout engine)
186
- ```
267
+ 1. **Actions → Bump** → `patch` / `minor` / `major`.
268
+ 2. Workflow bumps `package.json`, commits, tags `vX.Y.Z`.
269
+ 3. Tag triggers **Release**: `bun fmt --check` · `bun check` · `tests` · `dist` build · tag↔version guard · `npm pack` verify → publish with [provenance](https://docs.npmjs.com/generating-provenance-statements) + GitHub Release.
187
270
 
188
- - **Bun + Elysia** — one of the fastest HTTP stacks available.
189
- - **Two-tier SWR cache** — users never wait on GitHub's API.
190
- - **Request coalescing** — concurrent misses share a single upstream fetch.
191
- - **Defensive upstream** — timeouts, size caps, and typed errors (`401/404/429/502`).
271
+ ---
192
272
 
193
- ## License
273
+ <div align="center">
194
274
 
195
- [MIT](LICENSE) © [saku shiina](https://www.shiina.xyz)
275
+ **[MIT](LICENSE) © [saku shiina](https://www.shiina.xyz)** — built with Bun, Elysia, and system fonts.
276
+
277
+ <sub>Stars default to <code>owner</code> only in 4.2 — add <code>?affiliations=affiliated</code> to include orgs.</sub>
278
+
279
+ </div>
package/dist/card.d.ts CHANGED
@@ -1,16 +1,3 @@
1
- import type { UserProfile, UserStats, LanguageStat } from "./types";
2
- export type CardOpts = {
3
- theme?: string;
4
- title_color?: string;
5
- text_color?: string;
6
- icon_color?: string;
7
- bg_color?: string;
8
- border_color?: string;
9
- hide_border?: boolean;
10
- compact?: boolean;
11
- hide?: string[];
12
- hide_langs?: string[];
13
- show_langs?: string[];
14
- animate?: boolean;
15
- };
1
+ import type { UserProfile, UserStats, LanguageStat, CardOptions } from "./types";
2
+ export type CardOpts = CardOptions;
16
3
  export declare function renderCard(user: UserProfile, stats: UserStats, langs: LanguageStat[], opts?: CardOpts): string;
package/dist/lib.d.ts CHANGED
@@ -7,7 +7,7 @@
7
7
  export { renderCard, type CardOpts } from "./card";
8
8
  export type { UserProfile, UserStats, LanguageStat, ProfileData, CardOptions } from "./types";
9
9
  export { themes, resolveColors, type Theme } from "./utils/themes";
10
- export { kFormat, escapeXml, wrapText } from "./utils/format";
10
+ export { kFormat, escapeXml, wrapText, measureText } from "./utils/format";
11
11
  export { icons, icon } from "./utils/icons";
12
12
  export { getLangColor } from "./utils/languages";
13
13
  export { FONT_FACE, FONT_FAMILY } from "./utils/font";