Skip to content

0.4: fix SSR bugs and split placement from sidecar config - #1

Merged
pjeweb merged 9 commits into
mainfrom
review-bugs
Sep 12, 2026
Merged

pjeweb merged 9 commits into
mainfrom
review-bugs

Conversation

@pjeweb

@pjeweb pjeweb commented Sep 12, 2026

Copy link
Copy Markdown
Member

Summary

  • Fix confirmed 0.3 bugs: header injection, locale/deviceClass not forwarded, cache behind the breaker, client errors tripping the breaker, comment-injected state, curl_close() on 8.5, purge-all on blank URLs, CSS without ?v=, and the legacy raw-HTML branch.
  • Breaking 0.4 reshape: SsrClient holds sidecar URL, timeouts, transport, cache, breaker, and logger; EmbedRequest is placement-only; render() returns RenderedEmbed with outcome and fragment parts; SsrPublish::purge() returns PurgeResult.
  • Hardening and host API: share-param URL normalisation, PSR-3 / PSR-16, cache TTL, JSON-only v1 contract, health() / warm(), PHPStan + CI gates, SECURITY.md.

Test plan

  • composer test (56 tests) on PHP 8.2–8.5
  • composer phpstan and composer validate --strict
  • Host package: switch Renderer($transport) to new Renderer(new SsrClient(...)); replace renderDocument() / afterFeatureSourcePublish() call sites
  • Confirm a warm cache still serves HTML when the breaker is open
  • Confirm purge(['']) throws and a failed sidecar purge does not flush PHP cache
  • Confirm requestId with CR/LF is rejected before any header is sent

Note

High Risk
Breaking public API and SSR/purge/cache semantics for all integrators, plus security-sensitive validation of URLs, headers, and sidecar JSON/HTML.

Overview
Breaking 0.4.0 splits placement (EmbedRequest) from sidecar wiring (SsrClient, shared by Renderer and SsrPublish). Hosts build SsrClient once (URL, timeouts, PSR-3 logger, optional PSR-16 cache with TTL); Renderer::render() is the only render entry and returns RenderedEmbed (outcome, timing, and split fragment parts). renderDocument() / HTML-only render() and SsrPublish::afterFeatureSourcePublish() are removed in favor of purge()PurgeResult.

SSR behavior is tightened: cache is checked before the breaker; only connect/timeout/5xx trip the breaker (4xx/parse errors do not). requestUrl is host-supplied only (no $_SERVER / getenv / config fallbacks), normalized with a share-param whitelist for cache, sidecar, and pageMeta. v1 responses must be JSON with stricter HTML/containerId checks, size caps, and no legacy raw-HTML path. Purge refuses blank URL lists and does not flush PHP cache if the sidecar POST fails.

The emitted fragment adds modulepreload, CSS ?v= busting, optional script nonce, and safer boot strings via json_encode. HTTP goes through SsrTransport::send() (render/health/purge); NativeSsrPurgeTransport is gone. Tooling: PHPStan max, composer validate --strict, prefer-lowest CI, deprecations fail tests, SECURITY.md.

Reviewed by Cursor Bugbot for commit 8cf37b8. Bugbot is set up for automated code reviews on this repo. Configure here.

…ail-open footguns.

Validate tokens and preset identifiers, forward locale/deviceClass, serve warm cache when the breaker is open, and distinguish client errors from sidecar outages.
Relative or javascript: URLs must not land in canonical or Open Graph tags.
…nly.

The host builds one client (transport, cache, breaker, logger) and reuses it for render, purge, health, and warm. The transport returns raw HTTP so the client owns the v1 contract.
@pjeweb

pjeweb commented Sep 12, 2026

Copy link
Copy Markdown
Member Author

bugbot run

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit e52b422. Configure here.

Comment thread src/Embed/NativeSsrTransport.php
$GLOBALS['http_response_header'] is empty on PHP 8.2/8.3, so the
no-curl fallback treated successful sidecar replies as transport failures.
…oad.

PHP 8.5 emits a deprecation when that identifier is compiled, even if
the <8.4 branch never runs. Load it from a sidecar class instead.
@pjeweb
pjeweb merged commit 9031522 into main Sep 12, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant