♻️ refactor(web-sdk_angular): Align SSR with isomorphic runtime#357
Merged
Conversation
…ization-web/runtime
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Refactor the Angular reference implementation to adopt the
OptimizationRuntime/OptimizationSnapshotseam introduced in #354/#355, so Angular renders personalized content during SSR from snapshot state and hydrates into the live browser SDK using the same primitives React Web uses. Along the way, remove local machinery that now duplicates behavior available in@contentful/optimization-core/@contentful/optimization-web, and consolidate the merge-tag rendering pattern with Next.js.To make one of those primitives Angular-consumable, promote
WebOptimizationRuntime/createWebSnapshotRuntimefrom a React-internal file into@contentful/optimization-web/runtimeso every framework layer (React Web, Angular, and future adapters) reads from one canonical seam.Motivation
platform: 'server' | 'browser'runtime discriminator, its own pre-resolved-entries TransferState payload, its own rich-text merge-tag walker, and its ownifBrowser/ifLiveescape hatches — all things the isomorphic runtime primitives now cover uniformly.runtime().*unconditionally, merge tags resolve at render time via agetMergeTagValueclosure, and the framework layer onlyhandles the constructor branch. Angular should match.
Changes
1. Angular reference implementation — adopt the isomorphic seam
services/optimization.ts— singleSignal<WebOptimizationRuntime>seeded viacreateWebSnapshotRuntime(snapshot); promoted to the liveContentfulOptimizationafterhydrateOptimizationDataresolves. Preflight simplified to emit oneOptimizationSnapshot(matches the shape Next.js uses forserverOptimizationState).ifBrowser/ifLiveescape hatches deleted — every SDK-facing call site now goes throughruntime().*unconditionally.services/entry.ts— collapsed the server/browser branches: one path callsruntime.resolveOptimizedEntryandruntime.getMergeTagValue. Deleted the TransferState lift, deletedmergeTagResolvedfrom the entry-resolution layer, droppedisPlatformBrowserfromsetupManualTrackingsinceruntime().tracking.*NOOPs on the snapshot runtime.components/entry-card/index.ts— rich-text walker takes agetMergeTagValueclosure captured fromruntime()+profile()and substitutesINLINES.EMBEDDED_ENTRYmerge-tag targets in place, matching Next.js'screateRichTextRenderOptionspattern. One shared walk produces both the HTML and the merge-tag badge signal.components/tracking-log/index.ts— the inlineswitch (raw?.type)became a 4-line dispatcher; each event branch is now a privatehandle*method typed withExtract<OptimizationEventStreamEvent, { type: '…' }>. Sequence counters moved from closure locals to private class fields.services/live-updates.ts—previewPanelAttached/previewPanelOpenread fromruntime().states.*instead of the previousifBrowserwrapping.components/control-panel/index.ts,pages/page-two/index.ts— callruntime().consent,runtime().identify,runtime().reset,runtime().page,runtime().trackViewunconditionally.src/app/transfer-state-keys.ts— the twoStateKeys now live next to their sole owners (SERVER_BASELINES_KEYincontentful-client.ts,SERVER_OPTIMIZATION_KEYinternal tooptimization.ts).src/app/services/merge-tags.ts— the ~52-LOCresolveEntryMergeTagspre-walker; substitution now happens at render time.2. Package change — publish
WebOptimizationRuntimefromoptimization-web/runtimeWebOptimizationRuntimeandcreateWebSnapshotRuntimemoved frompackages/web/frameworks/react-web-sdk/src/runtime/webRuntime.ts(internal) topackages/web/web-sdk/src/runtime.ts(public, alongside the existing pass-through of@contentful/optimization-core/runtime). This composes the universalOptimizationRuntimewith the browser-only web SDK surface (tracking,trackCurrentPage) once, so every framework consumer imports the same isomorphic runtime object.OptimizationContext/OptimizationProviderre-pointed at@contentful/optimization-web/runtime.webRuntime.tsfile deleted.Design notes
isPlatformBrowserchecks kept intentionally. Inservices/optimization.tsit gatesnew ContentfulOptimization()(the SDK readslocalStorageat construction time — this is the root of the runtime seam, not a caller of it). Inservices/consent.tsit gates a rawdocument.cookiewrite (unrelated to the SDK). Both guard things the runtime seam explicitly does not cover.runServerPreflight) is kept. Next.js's equivalent is baked intocreateNextjsAppRouterOptimization, which is Next.js-shaped. Angular'sprovideServerOptimizationInitializer()is the Angular flavor of the same per-framework concern; it emits oneOptimizationSnapshotand stops there.SERVER_RESOLVED_ENTRIES_KEYintentionally dropped. Server-resolved entries are recomputable fromdata.selectedOptimizations+ baseline via the snapshot runtime's pure resolvers; shipping them wastes bytes and creates a second source of truth. Bit-identical output.typescript:S7059without introducing dead-codethison instance methods.Breaking changes
None for consumers of Angular reference impl or the SDK packages. The moved
WebOptimizationRuntimewas internal toreact-web-sdk; the new public location is additive.Test plan
pnpm implementation:run -- web-sdk_angular typecheck— cleanpnpm exec eslint implementations/web-sdk_angular/src/— cleanpnpm exec prettier --check implementations/web-sdk_angular/src/— cleanpnpm --filter @contentful/optimization-web typecheck+test:unit— cleanpnpm --filter @contentful/optimization-react-web typecheck+test:unit— cleanpnpm build:pkgs— cleanpnpm setup:e2e:web-sdk_angular && pnpm test:e2e:web-sdk_angular— Playwright SSR/first-paint suite (please verify in CI)pnpm implementation:run -- web-sdk_angular dev, exercise home + page-two routes, consent toggle, identify/reset, live-updates toggle, preview panel