{"id":50992875,"url":"https://github.com/renderffx/ppr-scratch","last_synced_at":"2026-06-20T05:04:37.445Z","repository":{"id":360504147,"uuid":"1250325935","full_name":"renderffx/ppr-scratch","owner":"renderffx","description":"partial-pre-rendering engine react 19 ","archived":false,"fork":false,"pushed_at":"2026-05-26T17:53:03.000Z","size":97,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-26T18:27:10.514Z","etag":null,"topics":["ppr","render","static-hole"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"isc","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/renderffx.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-26T14:18:45.000Z","updated_at":"2026-05-26T17:42:43.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/renderffx/ppr-scratch","commit_stats":null,"previous_names":["renderffx/ppr-scratch"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/renderffx/ppr-scratch","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/renderffx%2Fppr-scratch","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/renderffx%2Fppr-scratch/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/renderffx%2Fppr-scratch/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/renderffx%2Fppr-scratch/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/renderffx","download_url":"https://codeload.github.com/renderffx/ppr-scratch/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/renderffx%2Fppr-scratch/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34557553,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-20T02:00:06.407Z","response_time":98,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ppr","render","static-hole"],"created_at":"2026-06-20T05:04:37.355Z","updated_at":"2026-06-20T05:04:37.438Z","avatar_url":"https://github.com/renderffx.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ppr-scratch\n\n[![CI](https://github.com/renderffx/ppr-scratch/actions/workflows/ci.yml/badge.svg)](https://github.com/renderffx/ppr-scratch/actions/workflows/ci.yml)\n\nNext.js-style Partial Prerendering engine using React 19 Canary (`prerenderToNodeStream` / `resumeToPipeableStream`).\n\n## Quick Start\n\n```bash\nnpm install\nnpm run build\nnpm start\n# http://localhost:3000\n```\n\n## Commands\n\n| Command | Description |\n|---------|-------------|\n| `npm run build` | Full pipeline: prereqs → clean → bundle → RSC flight → prerender |\n| `npm start` | Start Express server on port 3000 |\n| `npm run test` | Artifact contract tests |\n| `npm run test:unit` | Unit tests (phase, dynamic-apis, flight-cache) |\n| `npm run test:e2e` | End-to-end prerender→resume pipeline test |\n| `npm run test:all` | All tests |\n| `npm run dev:loop` | Build + all tests |\n| `npm run clean` | Remove `dist/` |\n| `npm run clean:cache` | Remove `.rsc_cache/` |\n| `npm run prewarm` | Prewarm RSC cache entries |\n\n## API\n\n| Method | Path | Description |\n|--------|------|-------------|\n| GET | `/` | Serves static prerendered shell HTML |\n| GET | `/rsc-payload` | Raw RSC Flight v1 binary payload |\n| GET | `/cache/:name` | Cached RSC entry by component name |\n| POST | `/resume` | Resume postponed boundaries (JSON) |\n| POST | `/resume/stream` | Resume boundaries as chunked HTML |\n| GET | `/api/status` | Build status and artifact health |\n| GET | `/api/manifest` | Raw build manifest |\n\nEnvironment: `PORT` (default 3000), `PPR_RESUME_TIMEOUT` (default 10000ms).\n\n## Architecture\n\n### Build Pipeline\n\n```\nsrc/App.js ──esbuild──▶ dist/App.bundle.js\n                            │\nbuild-rsc.js                │\n  └── react-server-dom-webpack ──▶ dist/rsc-payload.bin\n\nbuild.js\n  ├── setPhase('prerender')\n  ├── prewarm-cache → .rsc_cache/*.bin\n  ├── prerenderToNodeStream(App)\n  │     └── dynamic components throw NEVER promise\n  │     └── Suspense boundaries → postponed state\n  │     └── onShellReady fires → abort(request) via patch\n  │     └── resolves with {postponed, prelude}\n  ├── prelude → dist/shell.html (\u003c!--$?--\u003e markers)\n  ├── postponed JSON → dist/postponed.json\n  ├── RSC payload embedded in shell \u003c/body\u003e\n  └── manifest → dist/manifest.json\n```\n\n### Request Pipeline\n\n```\nGET  /        → shell.html (instant, no JS required)\nPOST /resume  → resumeToPipeableStream(App, postponed)\n               → pipes resumed content + $RC() swap scripts\nPOST /resume/stream → same, chunked HTML transfer\n```\n\n## React Patch\n\nReact's `prerenderToNodeStream` waits for all content (blocks on never-resolving promises). The `postinstall` script patches `onShellReady` from `void 0` to a function that calls `abort(request)`, which marks pending Suspense boundaries as real postponed state.\n\nTargets: `node_modules/react-dom/cjs/react-dom-server.node.{production,development}.js`\n\nRun manually: `node scripts/patch-react-dom.mjs`\n\n## Project Structure\n\n```\n├── src/\n│   ├── App.js            React app with static + dynamic boundaries\n│   ├── phase.js          Prerender/request phase management\n│   ├── dynamic-apis.js   Cookies, headers, searchParams (suspend during prerender)\n│   ├── ErrorBoundary.js  Catch errors per boundary\n│   ├── flight-cache.js   RSC Flight cache (memory + disk, TTL 5min)\n│   └── cache-registry.js Component definitions for cache prewarming\n├── scripts/\n│   ├── patch-react-dom.mjs    React abort-on-shellReady patch\n│   └── check-prereqs.mjs      Node/npm/patch validation\n├── tests/\n│   ├── ppr.test.js       Artifact contract tests\n│   ├── unit.test.js      Unit tests (phase, dynamic-apis, flight-cache)\n│   └── ppr-e2e.test.js   Prerender→resume end-to-end\n├── server.js             Express server\n├── build.js              PPR build orchestrator\n├── build-rsc.js          RSC Flight payload builder\n└── prewarm-cache.mjs     Cache prewarming\n```\n\n## Output Artifacts\n\n| File | Contents |\n|------|----------|\n| `dist/shell.html` | Static HTML with `\u003c!--$?--\u003e` markers + embedded RSC payload |\n| `dist/postponed.json` | React postponed state (resumableState, replayNodes) |\n| `dist/rsc-payload.bin` | RSC Flight v1 binary protocol payload |\n| `dist/manifest.json` | Build metadata (boundaries, cache entries, timestamps) |\n| `.rsc_cache/*.bin` | Per-component RSC Flight cache entries |\n\n## License\n\nISC \u0026mdash; \u0026copy; 2026 renderffx\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frenderffx%2Fppr-scratch","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frenderffx%2Fppr-scratch","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frenderffx%2Fppr-scratch/lists"}