satori-cf 0.0.1 → 0.0.2

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.
Files changed (2) hide show
  1. package/README.md +260 -5
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,13 +1,28 @@
1
1
  # satori-cf
2
2
 
3
- > SVG-only, batch-optimized Open Graph image generation for Cloudflare Workers
3
+ [![npm version](https://img.shields.io/npm/v/satori-cf.svg)](https://www.npmjs.com/package/satori-cf)
4
+ [![npm downloads](https://img.shields.io/npm/dm/satori-cf.svg)](https://www.npmjs.com/package/satori-cf)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4
6
 
5
- See the [main README](../../README.md) for full documentation.
7
+ > Lightweight, SVG-only Open Graph image generation for Cloudflare Workers
8
+
9
+ Generate dynamic OG images at the edge with minimal overhead. Built on [Satori](https://github.com/vercel/satori), optimized for Cloudflare Workers.
10
+
11
+ ## Features
12
+
13
+ - **Tiny footprint** - ~90KB WASM (Yoga only), no resvg (~1.3MB saved)
14
+ - **SVG output** - Clean, scalable vectors that work everywhere
15
+ - **Batch processing** - Generate multiple images with single WASM init
16
+ - **Streaming API** - Handle large batches without memory issues
17
+ - **Google Fonts** - Load any font with built-in Cloudflare caching
18
+ - **Emoji support** - Twemoji, OpenMoji, Noto, and more
19
+ - **TypeScript** - Full type safety with Result pattern error handling
6
20
 
7
21
  ## Installation
8
22
 
9
23
  ```bash
10
24
  npm install satori-cf
25
+
11
26
  ```
12
27
 
13
28
  ## Quick Start
@@ -18,9 +33,9 @@ import { generateOG, createSVGResponse } from "satori-cf";
18
33
  export default {
19
34
  async fetch(request: Request): Promise<Response> {
20
35
  const result = await generateOG(
21
- `<div style="display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; background: #667eea; color: white; font-size: 60px;">
36
+ `<div style="display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; font-size: 60px;">
22
37
  Hello World
23
- </div>`
38
+ </div>`,
24
39
  );
25
40
 
26
41
  if (result.ok) {
@@ -32,6 +47,246 @@ export default {
32
47
  };
33
48
  ```
34
49
 
50
+ ## Usage
51
+
52
+ ### HTML String
53
+
54
+ ```typescript
55
+ const result = await generateOG(`
56
+ <div style="display: flex; flex-direction: column; padding: 40px; background: #1a1a2e; color: white; width: 100%; height: 100%;">
57
+ <h1 style="font-size: 48px; margin: 0;">My Blog Post</h1>
58
+ <p style="font-size: 24px; color: #888;">Published on January 29, 2026</p>
59
+ </div>
60
+ `);
61
+ ```
62
+
63
+ ### React/JSX Element
64
+
65
+ ```typescript
66
+ import { generateOG } from "satori-cf";
67
+
68
+ const element = (
69
+ <div style={{ display: "flex", background: "#000", color: "#fff", width: "100%", height: "100%" }}>
70
+ <h1>Hello from JSX</h1>
71
+ </div>
72
+ );
73
+
74
+ const result = await generateOG(element);
75
+ ```
76
+
77
+ ### With Custom Fonts
78
+
79
+ ```typescript
80
+ import { generateOG, loadGoogleFont } from "satori-cf";
81
+
82
+ const fontData = await loadGoogleFont({
83
+ family: "Inter",
84
+ weight: 600,
85
+ });
86
+
87
+ const result = await generateOG(element, {
88
+ fonts: [
89
+ {
90
+ name: "Inter",
91
+ data: fontData,
92
+ weight: 600,
93
+ style: "normal",
94
+ },
95
+ ],
96
+ });
97
+ ```
98
+
99
+ ### With Emojis
100
+
101
+ ```typescript
102
+ const result = await generateOG(
103
+ `<div style="display: flex; font-size: 64px;">Hello 👋 World 🌍</div>`,
104
+ { emoji: "twemoji" },
105
+ );
106
+ ```
107
+
108
+ Supported emoji sets: `twemoji`, `openmoji`, `blobmoji`, `noto`, `fluent`, `fluentFlat`
109
+
110
+ ### Batch Generation
111
+
112
+ Generate multiple images efficiently with shared WASM initialization:
113
+
114
+ ```typescript
115
+ import { generateOGBatch } from "satori-cf";
116
+
117
+ const items = [
118
+ { element: '<div style="color: red;">Image 1</div>' },
119
+ { element: '<div style="color: blue;">Image 2</div>' },
120
+ { element: '<div style="color: green;">Image 3</div>' },
121
+ ];
122
+
123
+ const results = await generateOGBatch(items, {
124
+ concurrency: 5,
125
+ failFast: false,
126
+ });
127
+
128
+ for (const { index, result } of results) {
129
+ if (result.ok) {
130
+ console.log(`Image ${index}: ${result.value.length} bytes`);
131
+ } else {
132
+ console.error(`Image ${index} failed:`, result.error);
133
+ }
134
+ }
135
+ ```
136
+
137
+ ### Streaming Large Batches
138
+
139
+ For memory-efficient processing of large batches:
140
+
141
+ ```typescript
142
+ import { OGStream } from "satori-cf";
143
+
144
+ const items = generateManyItems(); // Could be hundreds of items
145
+
146
+ for await (const { index, result } of OGStream(items, { prefetch: 3 })) {
147
+ if (result.ok) {
148
+ await saveToR2(result.value); // Save as you go
149
+ }
150
+ }
151
+ ```
152
+
153
+ ## API Reference
154
+
155
+ ### `generateOG(element, options?)`
156
+
157
+ Generate a single SVG image.
158
+
159
+ | Parameter | Type | Description |
160
+ | --------- | --------------------- | ---------------------------- |
161
+ | `element` | `string \| ReactNode` | HTML string or React element |
162
+ | `options` | `OGOptions` | Optional configuration |
163
+
164
+ Returns: `Promise<OGResult<string>>`
165
+
166
+ ### `generateOGBatch(items, options?)`
167
+
168
+ Generate multiple images with amortized initialization.
169
+
170
+ | Parameter | Type | Description |
171
+ | --------- | -------------- | -------------------------------- |
172
+ | `items` | `BatchItem[]` | Array of `{ element, options? }` |
173
+ | `options` | `BatchOptions` | Batch configuration |
174
+
175
+ Returns: `Promise<BatchResult[]>`
176
+
177
+ ### `OGStream(items, options?)`
178
+
179
+ AsyncGenerator for streaming large batches.
180
+
181
+ | Parameter | Type | Description |
182
+ | --------- | --------------------- | -------------------- |
183
+ | `items` | `Iterable<BatchItem>` | Items to process |
184
+ | `options` | `StreamOptions` | Stream configuration |
185
+
186
+ Returns: `AsyncGenerator<BatchResult>`
187
+
188
+ ### `loadGoogleFont(options)`
189
+
190
+ Load a font from Google Fonts with caching.
191
+
192
+ ```typescript
193
+ const fontData = await loadGoogleFont({
194
+ family: "Roboto", // Font family name
195
+ weight: 400, // Font weight (optional)
196
+ text: "Hello", // Subset to specific characters (optional)
197
+ });
198
+ ```
199
+
200
+ Returns: `Promise<ArrayBuffer>`
201
+
202
+ ### `createSVGResponse(svg, options?)`
203
+
204
+ Create an HTTP Response with proper SVG headers.
205
+
206
+ ```typescript
207
+ return createSVGResponse(svg, {
208
+ status: 200,
209
+ headers: { "X-Custom": "header" },
210
+ debug: false, // Set true to disable caching
211
+ });
212
+ ```
213
+
214
+ ## Options
215
+
216
+ ### OGOptions
217
+
218
+ | Option | Type | Default | Description |
219
+ | -------- | ----------- | ----------- | ------------------------ |
220
+ | `width` | `number` | `1200` | Image width in pixels |
221
+ | `height` | `number` | `630` | Image height in pixels |
222
+ | `fonts` | `Font[]` | Auto-loaded | Custom fonts array |
223
+ | `emoji` | `EmojiType` | - | Emoji provider |
224
+ | `debug` | `boolean` | `false` | Disable response caching |
225
+
226
+ ### BatchOptions
227
+
228
+ Extends `OGOptions`:
229
+
230
+ | Option | Type | Default | Description |
231
+ | ------------- | --------- | ------- | ------------------------ |
232
+ | `concurrency` | `number` | `5` | Max parallel generations |
233
+ | `failFast` | `boolean` | `false` | Stop on first error |
234
+
235
+ ### StreamOptions
236
+
237
+ Extends `OGOptions`:
238
+
239
+ | Option | Type | Default | Description |
240
+ | ---------- | -------- | ------- | ----------------------- |
241
+ | `prefetch` | `number` | `3` | Items to prefetch ahead |
242
+
243
+ ## Result Pattern
244
+
245
+ All generators return a discriminated union for type-safe error handling:
246
+
247
+ ```typescript
248
+ type OGResult<T> = { ok: true; value: T } | { ok: false; error: Error };
249
+
250
+ const result = await generateOG(element);
251
+
252
+ if (result.ok) {
253
+ // result.value is the SVG string
254
+ return createSVGResponse(result.value);
255
+ } else {
256
+ // result.error is the Error object
257
+ console.error(result.error.message);
258
+ return new Response("Failed", { status: 500 });
259
+ }
260
+ ```
261
+
262
+ ## Performance
263
+
264
+ Benchmarks on Cloudflare Workers (cold start):
265
+
266
+ | Operation | Time |
267
+ | ----------------------- | -------------------------- |
268
+ | First image (WASM init) | ~270ms |
269
+ | Subsequent images | ~90ms |
270
+ | Batch of 10 images | ~280ms total (~28ms/image) |
271
+
272
+ ## Comparison
273
+
274
+ | Feature | satori-cf | workers-og | @vercel/og |
275
+ | --------- | ---------- | ---------- | ----------- |
276
+ | Output | SVG | PNG + SVG | PNG |
277
+ | WASM size | ~90KB | ~1.4MB | ~1.4MB |
278
+ | Platform | CF Workers | CF Workers | Vercel Edge |
279
+ | Batch API | Yes | No | No |
280
+ | Streaming | Yes | No | No |
281
+
282
+ ## Credits
283
+
284
+ Built on the shoulders of giants:
285
+
286
+ - [Satori](https://github.com/vercel/satori) by Vercel - SVG generation engine
287
+ - [workers-og](https://github.com/kvnang/workers-og) by Kevin Ang - CF Workers patterns
288
+ - [@vercel/og](https://vercel.com/docs/functions/og-image-generation) - API inspiration
289
+
35
290
  ## License
36
291
 
37
- MIT
292
+ MIT © [Pavle Dzuverovic](https://github.com/PavleDz)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "satori-cf",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "type": "module",
5
5
  "description": "SVG-only, batch-optimized Open Graph image generation for Cloudflare Workers",
6
6
  "author": "Pavle Dzuverovic",