{"_id":"cf-workers-og","_rev":"5-8a158e60a60ab3509bc55f5969c9e9df","name":"cf-workers-og","dist-tags":{"latest":"3.0.1"},"versions":{"1.0.0":{"name":"cf-workers-og","version":"1.0.0","keywords":["cloudflare","workers","og","opengraph","image","satori","vite"],"license":"MIT","_id":"cf-workers-og@1.0.0","maintainers":[{"name":"jilles","email":"jilleswf@gmail.com"}],"homepage":"https://github.com/jillesme/cf-workers-og#readme","bugs":{"url":"https://github.com/jillesme/cf-workers-og/issues"},"dist":{"shasum":"8ce13b6003782abc44bffdc4f678d00f8318051a","tarball":"https://registry.npmjs.org/cf-workers-og/-/cf-workers-og-1.0.0.tgz","fileCount":17,"integrity":"sha512-+/gkxxkJUzQNcUJdGJ1O/195pVP1Cl9AfQnhM2z55xcs2aE0/9A6uuPFZnB8PX515wSeuJgSrQXmt43AOyWL7w==","signatures":[{"sig":"MEQCIAZwMV6rNWkK4//aKEiw5d6qUbZjMYCoTKAm30Ilo9ssAiBw/MluRmS3QD3ivVvisriRQ9kPBsSwSDnoITJ07sFvAQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/cf-workers-og@1.0.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":58147},"main":"./dist/index.js","type":"module","_from":"file:cf-workers-og-1.0.0.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"node":{"types":"./dist/index.node.d.ts","import":"./dist/index.node.js"},"default":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"workerd":{"types":"./dist/index.d.ts","import":"./dist/index.js"}}},"scripts":{"dev":"vite build --watch","lint":"eslint src/","test":"vitest run","build":"vite build && tsc --emitDeclarationOnly","format":"prettier --write src/","lint:fix":"eslint src/ --fix","typecheck":"tsc --noEmit","test:watch":"vitest","format:check":"prettier --check src/","test:coverage":"vitest run --coverage"},"_npmUser":{"name":"jilles","email":"jilleswf@gmail.com"},"_resolved":"/tmp/5394482d79cac5e91fda717fa7af3948/cf-workers-og-1.0.0.tgz","_integrity":"sha512-+/gkxxkJUzQNcUJdGJ1O/195pVP1Cl9AfQnhM2z55xcs2aE0/9A6uuPFZnB8PX515wSeuJgSrQXmt43AOyWL7w==","repository":{"url":"git+https://github.com/jillesme/cf-workers-og.git","type":"git"},"_npmVersion":"10.9.4","description":"Generate Open Graph images on Cloudflare Workers with Vite support","directories":{},"_nodeVersion":"22.21.1","dependencies":{"@cf-wasm/og":"^0.3.0","htmlparser2":"^10.0.0","style-to-js":"^1.1.21"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^7.0.0","eslint":"^9.0.0","vitest":"^2.0.0","prettier":"^3.3.0","@eslint/js":"^9.0.0","typescript":"^5.8.3","@types/react":"^18.3.0","vite-plugin-dts":"^4.5.0","typescript-eslint":"^8.0.0","@vitest/coverage-v8":"^2.0.0","@cloudflare/workers-types":"^4.20250610.0"},"peerDependencies":{"react":">=18.0.0"},"peerDependenciesMeta":{"react":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/cf-workers-og_1.0.0_1768157673094_0.3807238706394711","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"cf-workers-og","version":"1.0.1","keywords":["cloudflare","workers","og","opengraph","image","satori","vite"],"license":"MIT","_id":"cf-workers-og@1.0.1","maintainers":[{"name":"jilles","email":"jilleswf@gmail.com"}],"homepage":"https://github.com/jillesme/cf-workers-og#readme","bugs":{"url":"https://github.com/jillesme/cf-workers-og/issues"},"dist":{"shasum":"69b8e504860bef63e98eaebfec1fc2ac646828c4","tarball":"https://registry.npmjs.org/cf-workers-og/-/cf-workers-og-1.0.1.tgz","fileCount":17,"integrity":"sha512-4jqvZTs4fec73R+Zkzk7rG0r/InKwb79MR/ot0WezmVTkEKqGjqWv+3/CsYQMD9KAO5g53Aspdrrft/MtdbE8w==","signatures":[{"sig":"MEYCIQCL/LPOlNf5sF5TRj/jkpGoYbuMCZSyX4kDe+Dv2+r4NAIhAI7E0/cXztxpZQXYRgCTtO42QBtNpulMhTarTXxo0sMe","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/cf-workers-og@1.0.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":58260},"main":"./dist/index.js","type":"module","_from":"file:cf-workers-og-1.0.1.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"node":{"types":"./dist/index.node.d.ts","import":"./dist/index.node.js"},"default":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"workerd":{"types":"./dist/index.d.ts","import":"./dist/index.js"}}},"scripts":{"dev":"vite build --watch","lint":"eslint src/","test":"vitest run","build":"vite build && tsc --emitDeclarationOnly","format":"prettier --write src/","lint:fix":"eslint src/ --fix","typecheck":"tsc --noEmit","test:watch":"vitest","format:check":"prettier --check src/","test:coverage":"vitest run --coverage"},"_npmUser":{"name":"jilles","email":"jilleswf@gmail.com"},"_resolved":"/tmp/a0ce9ea6a75f45439c8ba9b4678a76ce/cf-workers-og-1.0.1.tgz","_integrity":"sha512-4jqvZTs4fec73R+Zkzk7rG0r/InKwb79MR/ot0WezmVTkEKqGjqWv+3/CsYQMD9KAO5g53Aspdrrft/MtdbE8w==","repository":{"url":"git+https://github.com/jillesme/cf-workers-og.git","type":"git"},"_npmVersion":"10.9.4","description":"Generate Open Graph images on Cloudflare Workers with Vite support","directories":{},"_nodeVersion":"22.21.1","dependencies":{"@cf-wasm/og":"^0.3.0","htmlparser2":"^10.0.0","style-to-js":"^1.1.21"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^7.0.0","eslint":"^9.0.0","vitest":"^2.0.0","prettier":"^3.3.0","@eslint/js":"^9.0.0","typescript":"^5.8.3","@types/react":"^18.3.0","vite-plugin-dts":"^4.5.0","typescript-eslint":"^8.0.0","@vitest/coverage-v8":"^2.0.0","@cloudflare/workers-types":"^4.20250610.0"},"peerDependencies":{"react":">=18.0.0"},"peerDependenciesMeta":{"react":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/cf-workers-og_1.0.1_1768160277345_0.9222390640782709","host":"s3://npm-registry-packages-npm-production"}},"2.0.0":{"name":"cf-workers-og","version":"2.0.0","keywords":["cloudflare","workers","og","opengraph","image","satori","vite"],"license":"MIT","_id":"cf-workers-og@2.0.0","maintainers":[{"name":"jilles","email":"jilleswf@gmail.com"}],"homepage":"https://github.com/jillesme/cf-workers-og#readme","bugs":{"url":"https://github.com/jillesme/cf-workers-og/issues"},"dist":{"shasum":"b591365fbb561d63fef099cac5aca6dbcc952968","tarball":"https://registry.npmjs.org/cf-workers-og/-/cf-workers-og-2.0.0.tgz","fileCount":33,"integrity":"sha512-qWIrgERMdgyLnWNEKEbDMpZjviKd5aImzEYz+xHNqcpFgG5HTwZOu5gDrzkUU2jSq+ORQFcWsjRdmCzblnsRDw==","signatures":[{"sig":"MEQCIDrURif5/gZkeDxCEu/WHigpi7fe1U7I+7CRQNa/LGu1AiBbMbEspXgQbIOiCTOHjZlA44kkL9Y88SDZAIT1O+pUXg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/cf-workers-og@2.0.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":53930},"main":"./dist/index.js","type":"module","_from":"file:cf-workers-og-2.0.0.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"node":{"types":"./dist/index.node.d.ts","import":"./dist/index.node.js"},"default":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"workerd":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"./html":{"node":{"types":"./dist/html.node.d.ts","import":"./dist/html.node.js"},"default":{"types":"./dist/html.d.ts","import":"./dist/html.js"},"workerd":{"types":"./dist/html.d.ts","import":"./dist/html.js"}},"./node":{"types":"./dist/index.node.d.ts","import":"./dist/index.node.js"},"./compat":{"node":{"types":"./dist/compat.node.d.ts","import":"./dist/compat.node.js"},"default":{"types":"./dist/compat.d.ts","import":"./dist/compat.js"},"workerd":{"types":"./dist/compat.d.ts","import":"./dist/compat.js"}},"./workerd":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./node/html":{"types":"./dist/html.node.d.ts","import":"./dist/html.node.js"},"./node/compat":{"types":"./dist/compat.node.d.ts","import":"./dist/compat.node.js"},"./workerd/html":{"types":"./dist/html.d.ts","import":"./dist/html.js"},"./workerd/compat":{"types":"./dist/compat.d.ts","import":"./dist/compat.js"}},"scripts":{"dev":"vite build --watch","lint":"eslint src/","test":"vitest run","build":"vite build && tsc --emitDeclarationOnly","format":"prettier --write src/","lint:fix":"eslint src/ --fix","typecheck":"tsc --noEmit","test:watch":"vitest","format:check":"prettier --check src/","test:coverage":"vitest run --coverage"},"_npmUser":{"name":"jilles","email":"jilleswf@gmail.com"},"_resolved":"/tmp/b5a4c2d5819ab44bb7e14730c87392e9/cf-workers-og-2.0.0.tgz","_integrity":"sha512-qWIrgERMdgyLnWNEKEbDMpZjviKd5aImzEYz+xHNqcpFgG5HTwZOu5gDrzkUU2jSq+ORQFcWsjRdmCzblnsRDw==","repository":{"url":"git+https://github.com/jillesme/cf-workers-og.git","type":"git"},"_npmVersion":"10.9.4","description":"Generate Open Graph images on Cloudflare Workers with Vite support","directories":{},"_nodeVersion":"22.21.1","dependencies":{"@cf-wasm/og":"^0.3.0","htmlparser2":"^10.0.0","style-to-js":"^1.1.21"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^7.0.0","eslint":"^9.0.0","vitest":"^2.0.0","prettier":"^3.3.0","@eslint/js":"^9.0.0","typescript":"^5.8.3","@types/react":"^18.3.0","vite-plugin-dts":"^4.5.0","typescript-eslint":"^8.0.0","@vitest/coverage-v8":"^2.0.0","@cloudflare/workers-types":"^4.20250610.0"},"peerDependencies":{"react":">=18.0.0"},"peerDependenciesMeta":{"react":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/cf-workers-og_2.0.0_1768222349509_0.9288219150294228","host":"s3://npm-registry-packages-npm-production"}},"3.0.0":{"name":"cf-workers-og","version":"3.0.0","keywords":["cloudflare","workers","og","opengraph","image","satori","vite"],"license":"MIT","_id":"cf-workers-og@3.0.0","maintainers":[{"name":"jilles","email":"jilleswf@gmail.com"}],"homepage":"https://github.com/jillesme/cf-workers-og#readme","bugs":{"url":"https://github.com/jillesme/cf-workers-og/issues"},"dist":{"shasum":"154d63d8e6af76a0eb3532b71e721a74f1ea44c0","tarball":"https://registry.npmjs.org/cf-workers-og/-/cf-workers-og-3.0.0.tgz","fileCount":41,"integrity":"sha512-o/pS3CsdSqcqkXPud4jJkAyzqhWHiHNrQ0plbuYqdc/WDge0IRMkKWQEmxS4Ys5zJgEQeXthsZqOn398Om4YAQ==","signatures":[{"sig":"MEYCIQCS3oy28LRtTcF9PMOO9zHAhS5MksJ3b/LOtsTz4JtCWwIhALzJRu/4Am1wXXlbKRDGre7ymTPr2GtvDM/dNw5ht7MQ","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/cf-workers-og@3.0.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":3091357},"main":"./dist/index.js","type":"module","_from":"file:cf-workers-og-3.0.0.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"node":{"types":"./dist/index.node.d.ts","import":"./dist/index.node.js"},"default":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"workerd":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"./html":{"node":{"types":"./dist/html.node.d.ts","import":"./dist/html.node.js"},"default":{"types":"./dist/html.d.ts","import":"./dist/html.js"},"workerd":{"types":"./dist/html.d.ts","import":"./dist/html.js"}},"./node":{"types":"./dist/index.node.d.ts","import":"./dist/index.node.js"},"./workerd":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./node/html":{"types":"./dist/html.node.d.ts","import":"./dist/html.node.js"},"./workerd/html":{"types":"./dist/html.d.ts","import":"./dist/html.js"}},"scripts":{"dev":"vite build --watch","lint":"eslint src/","test":"vitest run","build":"vite build && node scripts/copy-wasm.mjs && tsc --emitDeclarationOnly","format":"prettier --write src/","lint:fix":"eslint src/ --fix","typecheck":"tsc --noEmit","test:watch":"vitest","format:check":"prettier --check src/","test:coverage":"vitest run --coverage"},"_npmUser":{"name":"jilles","email":"jilleswf@gmail.com"},"_resolved":"/tmp/a737f0b97810175468d940c5bbc259bc/cf-workers-og-3.0.0.tgz","_integrity":"sha512-o/pS3CsdSqcqkXPud4jJkAyzqhWHiHNrQ0plbuYqdc/WDge0IRMkKWQEmxS4Ys5zJgEQeXthsZqOn398Om4YAQ==","repository":{"url":"git+https://github.com/jillesme/cf-workers-og.git","type":"git"},"_npmVersion":"10.9.4","description":"Generate Open Graph images on Cloudflare Workers with Vite support","directories":{},"_nodeVersion":"22.21.1","dependencies":{"satori":"0.18.3","htmlparser2":"^10.0.0","style-to-js":"^1.1.21","@resvg/resvg-wasm":"^2.6.2"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^7.0.0","eslint":"^9.0.0","vitest":"^2.0.0","prettier":"^3.3.0","@eslint/js":"^9.0.0","typescript":"^5.8.3","@types/react":"^18.3.0","vite-plugin-dts":"^4.5.0","typescript-eslint":"^8.0.0","@vitest/coverage-v8":"^2.0.0","@cloudflare/workers-types":"^4.20250610.0"},"peerDependencies":{"react":">=18.0.0"},"peerDependenciesMeta":{"react":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/cf-workers-og_3.0.0_1768428634271_0.5823385314877876","host":"s3://npm-registry-packages-npm-production"}},"3.0.1":{"name":"cf-workers-og","version":"3.0.1","type":"module","description":"Generate Open Graph images on Cloudflare Workers with Vite support","main":"./dist/index.js","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"node":{"types":"./dist/index.node.d.ts","import":"./dist/index.node.js"},"workerd":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"default":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"./node":{"types":"./dist/index.node.d.ts","import":"./dist/index.node.js"},"./workerd":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./html":{"node":{"types":"./dist/html.node.d.ts","import":"./dist/html.node.js"},"workerd":{"types":"./dist/html.d.ts","import":"./dist/html.js"},"default":{"types":"./dist/html.d.ts","import":"./dist/html.js"}},"./node/html":{"types":"./dist/html.node.d.ts","import":"./dist/html.node.js"},"./workerd/html":{"types":"./dist/html.d.ts","import":"./dist/html.js"}},"dependencies":{"@resvg/resvg-wasm":"^2.6.2","htmlparser2":"^10.0.0","satori":"0.18.3","style-to-js":"^1.1.21"},"devDependencies":{"@cloudflare/workers-types":"^4.20250610.0","@eslint/js":"^9.0.0","@types/react":"^18.3.0","@vitest/coverage-v8":"^2.0.0","eslint":"^9.0.0","prettier":"^3.3.0","typescript":"^5.8.3","typescript-eslint":"^8.0.0","vite":"^7.0.0","vite-plugin-dts":"^4.5.0","vitest":"^2.0.0"},"peerDependencies":{"react":">=18.0.0"},"peerDependenciesMeta":{"react":{"optional":true}},"keywords":["cloudflare","workers","og","opengraph","image","satori","vite"],"license":"MIT","repository":{"type":"git","url":"git+https://github.com/jillesme/cf-workers-og.git"},"bugs":{"url":"https://github.com/jillesme/cf-workers-og/issues"},"homepage":"https://github.com/jillesme/cf-workers-og#readme","engines":{"node":">=18"},"scripts":{"build":"vite build && node scripts/copy-wasm.mjs && tsc --emitDeclarationOnly","dev":"vite build --watch","typecheck":"tsc --noEmit","test":"vitest run","test:watch":"vitest","test:coverage":"vitest run --coverage","lint":"eslint src/","lint:fix":"eslint src/ --fix","format":"prettier --write src/","format:check":"prettier --check src/"},"_id":"cf-workers-og@3.0.1","_integrity":"sha512-oIuLE1eIWMl8m6RBZdNHKrnJlcGU0qvkp0U8hOLth4r1YV5f922vuqereY3iOYqmtdeiISPh+sHFci3HKqOvaw==","_resolved":"/tmp/7ecc626df0bb06d666cfc5f18c8ffd80/cf-workers-og-3.0.1.tgz","_from":"file:cf-workers-og-3.0.1.tgz","_nodeVersion":"22.21.1","_npmVersion":"10.9.4","dist":{"integrity":"sha512-oIuLE1eIWMl8m6RBZdNHKrnJlcGU0qvkp0U8hOLth4r1YV5f922vuqereY3iOYqmtdeiISPh+sHFci3HKqOvaw==","shasum":"4790f1bee1c676c1d49aa72b948446c3295c3d55","tarball":"https://registry.npmjs.org/cf-workers-og/-/cf-workers-og-3.0.1.tgz","fileCount":41,"unpackedSize":5057200,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/cf-workers-og@3.0.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCICOnjE+37+CBhYDWR/QFSPlxbk9ZISqEFbwAwZc/YHYjAiBbLAz0FBIZKe+cdj9lj6FQVV813eUfHu6T9ccAgoPYKQ=="}]},"_npmUser":{"name":"jilles","email":"jilleswf@gmail.com"},"directories":{},"maintainers":[{"name":"jilles","email":"jilleswf@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/cf-workers-og_3.0.1_1768430658560_0.3471158925274258"},"_hasShrinkwrap":false}},"time":{"created":"2026-01-11T18:54:32.990Z","modified":"2026-01-14T22:44:19.010Z","1.0.0":"2026-01-11T18:54:33.239Z","1.0.1":"2026-01-11T19:37:57.487Z","2.0.0":"2026-01-12T12:52:29.661Z","3.0.0":"2026-01-14T22:10:34.478Z","3.0.1":"2026-01-14T22:44:18.738Z"},"bugs":{"url":"https://github.com/jillesme/cf-workers-og/issues"},"license":"MIT","homepage":"https://github.com/jillesme/cf-workers-og#readme","keywords":["cloudflare","workers","og","opengraph","image","satori","vite"],"repository":{"type":"git","url":"git+https://github.com/jillesme/cf-workers-og.git"},"description":"Generate Open Graph images on Cloudflare Workers with Vite support","maintainers":[{"name":"jilles","email":"jilleswf@gmail.com"}],"readme":"# cf-workers-og\n\nGenerate Open Graph images on Cloudflare Workers with Node.js bindings for local Vite dev.\n\nA Workers-first wrapper around [Satori](https://github.com/vercel/satori) + Yoga WASM\nwith PNG output via resvg that provides:\n\n- Designed for Workers; includes Node.js bindings for local dev\n- Works with both **Vite dev** and **Wrangler dev**\n- Uses modern, maintained WASM dependencies\n- SVG and PNG output (PNG via resvg WASM)\n- Optional HTML string parsing (using battle-tested libraries)\n- TypeScript support\n\n## V3 Release Highlights (3.0.0)\n\n- Latest Satori 0.18.3 + Yoga 3.2.1 with Workers-safe WASM initialization\n- PNG output by default (SVG still available with `format: \"svg\"`)\n- SVG -> PNG via `@resvg/resvg-wasm` with vendored WASM assets\n- HTML strings accepted directly in `cf-workers-og/html`\n- Bundled Roboto Regular fallback font (no more \"no fonts loaded\" errors)\n\n## Installation\n\n```bash\nnpm install cf-workers-og\n# or\npnpm add cf-workers-og\n```\n\n## Quick Start\n\n### Basic Usage (JSX, recommended)\n\n```tsx\nimport { ImageResponse } from \"cf-workers-og\";\n\nexport default {\n  async fetch(request: Request, env: Env, ctx: ExecutionContext) {\n    return ImageResponse.create(\n      <div\n        style={{\n          display: \"flex\",\n          alignItems: \"center\",\n          justifyContent: \"center\",\n          width: \"100%\",\n          height: \"100%\",\n          background: \"linear-gradient(135deg, #667eea 0%, #764ba2 100%)\",\n          color: \"white\",\n          fontSize: 60,\n        }}\n      >\n        Hello World\n      </div>,\n      { width: 1200, height: 630 }\n    );\n  },\n};\n```\n\n### With Google Fonts\n\nFonts are optional; if you don’t pass any, the bundled Roboto Regular is used.\n\n```tsx\nimport { ImageResponse, GoogleFont, cache } from \"cf-workers-og\";\n\nexport default {\n  async fetch(request: Request, env: Env, ctx: ExecutionContext) {\n    // Required when using GoogleFont\n    cache.setExecutionContext(ctx);\n\n    return ImageResponse.create(\n      <div\n        style={{\n          display: \"flex\",\n          alignItems: \"center\",\n          justifyContent: \"center\",\n          width: \"100%\",\n          height: \"100%\",\n          background: \"#000\",\n          color: \"#fff\",\n          fontFamily: \"Inter\",\n          fontSize: 60,\n        }}\n      >\n        Hello World\n      </div>,\n      {\n        width: 1200,\n        height: 630,\n        fonts: [new GoogleFont(\"Inter\", { weight: 700 })],\n      }\n    );\n  },\n};\n```\n\n### HTML String Usage\n\nUse HTML parsing only if you need it. For new projects, JSX is the simplest and most reliable.\nHTML parsing is available via the opt-in `cf-workers-og/html` entrypoint, and you can pass\nraw HTML strings directly to `ImageResponse.create(...)`.\n\n```typescript\nimport { ImageResponse } from \"cf-workers-og/html\";\n\nexport default {\n  async fetch(request: Request, env: Env, ctx: ExecutionContext) {\n    const html = `\n      <div style=\"display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; background: #000; color: #fff;\">\n        <h1 style=\"font-size: 60px;\">Hello from HTML</h1>\n      </div>\n    `;\n\n    return ImageResponse.create(html, {\n      width: 1200,\n      height: 630,\n    });\n  },\n};\n```\n\n## Vite Configuration\n\nAdd the Cloudflare Vite plugin to your `vite.config.ts`:\n\n```typescript\nimport { defineConfig } from \"vite\";\nimport { cloudflare } from \"@cloudflare/vite-plugin\";\n\nexport default defineConfig({\n  plugins: [cloudflare()],\n});\n```\n\nVite dev runs in Node.js, so this package ships Node bindings that should be picked automatically. If your bundler does not respect export conditions, use explicit paths like `cf-workers-og/node`.\n\n## Which entrypoint should I use?\n\n- `cf-workers-og` (recommended): JSX input only, clean API for new users.\n- `cf-workers-og/html`: accepts raw HTML strings in `ImageResponse.create`.\n- If your bundler ignores export conditions, use explicit paths like `cf-workers-og/node`,\n  `cf-workers-og/workerd`, and their `/html` variants.\n\n## How cf-workers-og works (and why it is reliable)\n\nThis package is intentionally small, but the pipeline is carefully tuned for the Workers runtime:\n\n- **Inputs**: You can pass JSX directly, or (via `cf-workers-og/html`) supply a raw HTML string.\n  The HTML entrypoint uses `htmlparser2` + `style-to-js` to turn HTML into React nodes that\n  Satori understands.\n- **Layout**: Satori performs layout with Yoga WASM. In Workers, raw-byte compilation is\n  disallowed, so Yoga is imported as a WebAssembly module and initialized once. The loader is\n  patched to accept `WebAssembly.Module`/`WebAssembly.Instance`, which is the only safe path\n  in workerd.\n- **Rendering**: Satori outputs SVG. For PNG output, the SVG is rendered by `@resvg/resvg-wasm`,\n  again using a precompiled WASM module so there is no runtime compilation.\n- **Assets**: WASM binaries are vendored and copied into `dist/wasm`, so they can be loaded\n  without network access at runtime.\n- **Fonts**: A bundled Roboto Regular fallback prevents layout failure when no fonts are\n  provided. If you use `GoogleFont`, the Cloudflare Cache API is used to avoid re-fetching\n  (and requires `cache.setExecutionContext(ctx)`).\n\nThe result is a Workers-first pipeline that runs in both **Vite dev** (Node) and **Wrangler dev**\nworkerd with consistent behavior. We do not claim it is the only solution, but it is a practical\nand well-scoped one that matches Workers' constraints without sacrificing output quality.\n\n## Why Not workers-og?\n\nThe original [workers-og](https://github.com/syedashar1/workers-og) has fundamental issues that make it unsuitable for production use.\n\n### The blockers we hit in Workers\n\n- **WASM byte compilation is disallowed** in workerd, so loaders that call\n  `WebAssembly.instantiate(bytes)` fail at runtime.\n- **Outdated Yoga WASM**: `yoga-wasm-web@0.3.3` is unmaintained and incompatible\n  with Yoga 3’s `SINGLE_FILE=1` base64 output (also not Workers-safe).\n- **Brittle HTML/CSS parsing**: regex-based parsing breaks on real-world CSS.\n- **Build tooling gap**: workers-og is esbuild-only and doesn’t play well with\n  Vite library builds and export conditions.\n\n### How cf-workers-og v3 solves this (engineering highlights)\n\n- **Latest Satori + Yoga**: uses `satori@0.18.3` with `yoga-layout@3.2.1` and a\n  patched loader that accepts `WebAssembly.Module/Instance`.\n- **Workers-safe WASM**: Yoga + resvg WASM are vendored and imported as modules,\n  never compiled from raw bytes at runtime.\n- **PNG by default**: SVG → PNG via `@resvg/resvg-wasm`, with `format: \"svg\"`\n  available when you want raw SVG.\n- **Zero-config fonts**: bundled Roboto Regular so layout never fails when users\n  omit fonts; custom fonts still fully supported.\n- **Reliable HTML/CSS**: `htmlparser2` + `style-to-js` handle edge cases that\n  workers-og’s regex parsing misses.\n- **Vite + Wrangler ready**: proper export conditions, wasm assets copied to\n  `dist/wasm`, and no bundler-specific hacks required.\n\n### Design Decisions\n\n**Why Satori + Yoga instead of building from scratch?**\n\nWASM on Cloudflare Workers is genuinely hard. Workers cannot compile WASM from arbitrary data blobs - you must import as modules. Satori already provides a well-tested SVG renderer, so we focus on making its Yoga WASM initialization work in Workers.\n\n**Why htmlparser2 instead of html-react-parser?**\n\n`html-react-parser` uses `html-dom-parser` which detects the environment. Cloudflare Workers is incorrectly detected as a browser, causing it to use `document.implementation.createHTMLDocument` which doesn't exist. `htmlparser2` is a pure streaming parser that works everywhere.\n\n## API Reference\n\n### Entry points\n\nUse `cf-workers-og` for Workers with JSX input and `cf-workers-og/html` for HTML strings.\nIf your bundler ignores export conditions, use explicit paths like `cf-workers-og/node` or\n`cf-workers-og/workerd` (and the `/html` variants).\n\n### `ImageResponse.create(element, options)`\n\nGenerate an OG image Response.\nMain entrypoint expects a React element; `cf-workers-og/html` also accepts HTML strings.\n\n```typescript\nconst response = await ImageResponse.create(element, {\n  width: 1200, // Default: 1200\n  height: 630, // Default: 630\n  format: \"png\", // 'png' | 'svg', Default: 'png'\n  fonts: [], // Font configurations (defaults to bundled Roboto Regular)\n  emoji: \"twemoji\", // Emoji provider\n  debug: false, // Disable caching for debugging\n  headers: {}, // Additional response headers\n  status: 200, // HTTP status code\n  statusText: \"\", // HTTP status text\n});\n```\n\n### `GoogleFont(family, options)`\n\nLoad a Google Font.\n\n```typescript\nconst font = new GoogleFont(\"Inter\", { weight: 700 });\n```\n\n### `loadGoogleFont(options)` (Deprecated)\n\nBackwards-compatible function for loading Google Fonts. Prefer `GoogleFont` class.\n\n```typescript\nconst fontData = await loadGoogleFont({ family: \"Inter\", weight: 700 });\n```\n\n### `cache.setExecutionContext(ctx)`\n\n**Only required when using `GoogleFont`**. Not needed if you use the default font or `CustomFont`.\n\n```typescript\n// Only if using GoogleFont:\ncache.setExecutionContext(ctx);\n\nconst response = await ImageResponse.create(element, {\n  fonts: [new GoogleFont(\"Inter\", { weight: 700 })],\n});\n```\n\nThe `GoogleFont` class caches font files using Cloudflare's Cache API to avoid re-fetching on every request. The Cache API requires access to the execution context.\n\n**When you DON'T need it:**\n- Using no fonts (default Noto Sans is bundled)\n- Using `CustomFont` with your own font data\n\n## Migrating from workers-og\n\nNote that cache is only used if you use GoogleFonts. Otherwise it is a drop-in replacement.\n\n```diff\n- import { ImageResponse } from 'workers-og';\n+ import { ImageResponse, cache } from 'cf-workers-og';\n\nexport default {\n  async fetch(request, env, ctx) {\n+   cache.setExecutionContext(ctx);\n\n    return ImageResponse.create(element, options);\n  }\n};\n```\n\nFor HTML string users:\n\n```diff\n- return new ImageResponse(htmlString, options);\n+ import { ImageResponse } from 'cf-workers-og/html';\n+ return ImageResponse.create(htmlString, options);\n```\n\n## Architecture\n\nThis package is a **thin wrapper** around Satori + Yoga. The heavy lifting is done by:\n\n| Package | Purpose | Notes |\n|---------|---------|-------|\n| `satori` | SVG rendering | Outputs SVG |\n| `yoga-layout` | Flexbox layout (WASM) | Yoga 3; wasm is vendored for Workers |\n| `@resvg/resvg-wasm` | SVG → PNG | WASM renderer |\n| `htmlparser2` | HTML parsing (pure JS) | Optional entrypoint |\n\n## License\n\nMIT\n","readmeFilename":"README.md"}