Repository navigation
feat(realtime): policy issuance stream, clock-skew normalization, bundle budget, payload size guards - #431
Open
Lucky-Ekpa-800 wants to merge 1 commit into
Conversation
…dle budget, payload size guards Closes Refract-Protocol#246, Refract-Protocol#247, Refract-Protocol#248, Refract-Protocol#249. - Refract-Protocol#246: `policy.<address>` topic (accepted/issued/premium_charged/cancelled/failed), persisted pending-purchase store keyed by tx hash, usePolicyStream hook with bounded-wait REST fallback and cross-tab sync, PolicyProgress UI on /cover. - Refract-Protocol#247: transport-independent ClockOffsetEstimator (min-RTT median, clock-jump invalidation, skew diagnostic), normalizeServerTs helpers, useServerRelativeTime. - Refract-Protocol#248: lazy `realtime` chunk via loadRealtime(), 8 kB size-limit budget, scripts/check-realtime-bundle.mjs CI assertion, ESLint import restrictions. - Refract-Protocol#249: shared decodeFrame with a 256 KiB pre-parse size guard, protocol-error path and counters, permessage-deflate options and negotiation diagnostics. Also repairs the truncated JSX in src/app/cover/page.tsx (and moves its hard-coded colours onto design tokens), removes the empty vitest.config.ts that shadowed vitest.config.mts, fixes the invalid ESLint selector that crashed lint, and drops the duplicate size-limit config from package.json that hid .size-limit.json. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Closes #246
Closes #247
Closes #248
Closes #249
Full design notes:
docs/realtime.md.#246: Stream policy issuance into
/coverpolicy.<address>, keyed by wallet. Event types:accepted,issued,premium_charged,cancelled,failed. zod schemas live insrc/lib/realtime/policyEvents.ts./coveradds an optimistic entry keyed by that hash to a persisted store (pendingPolicies.ts).usePolicyStream(address)moves the entrysubmitted → accepted → active, or rolls it back tofailed. Events never move an entry backwards, so duplicates and out-of-order delivery are harmless.activewithconfirmedBy: "rest"./cover. On return the hook re-subscribes and the same row updates; nothing is duplicated.storagelistener keeps tabs in sync. Adding the same tx hash merges into one row, and an identical in-flight purchase blocks the Buy button.externalrows instead of being dropped.PolicyProgressshows the progression, failure reason, and premium charged. A "Your recent purchases" panel lists other pending purchases.#247: Clock-skew detection and timestamp normalization
ClockOffsetEstimatortakes NTP-style mid-point samples from heartbeat ping/pong. It has no transport dependency, so SSE and long-poll can feed it too.skewed: trueas a diagnostic, not an error.normalizeServerTs,serverEventAgeMs,isServerTsStale, and thegetClockSync()read-only accessor.useServerRelativeTimere-renders only when the displayed text changes. Subscribers are only notified when the offset moves by 250 ms or more.#248: Bundle-size and runtime budget
loadRealtime(), a named dynamic import that builds to.next/static/chunks/realtime.<hash>.js..size-limit.json. The chunk's own code measures about 3.1 kB gzipped when bundled in isolation. zod is shared with/cover's form validation, so it isn't counted. The headroom is for the SSE and long-poll transports; revisit the number when the transport matrix is final. The shared-bundle budget now excludes the lazy chunk.scripts/check-realtime-bundle.mjs(npm run size:realtime, added to CI afternpm run size) fails if:src/lib/realtime/dev) appears anywhere in.next/static, orno-restricted-importsblocks static imports of the client, the lazy entry, ordev/*outside the realtime module and tests.package.jsonhad its ownsize-limitkey, which takes precedence and meant.size-limit.jsonwas silently ignored. It's removed so.size-limit.jsonis the single config.#249: permessage-deflate and payload size guards
decodeFrame()for all transports. It rejects frames overMAX_MESSAGE_BYTES(256 KiB, measured in UTF-8 bytes) beforeJSON.parse. The limit is sized against the snapshot contract documented indocs/realtime.md.oversizeDrops/malformedDrops), recorded aslastError, and passed toonProtocolErrorlisteners. The connection stays open.PERMESSAGE_DEFLATE_OPTIONSasks for no context takeover. Browsers negotiate the extension themselves, so this mostly documents the expected backend config. Graceful decline is tested.negotiatedExtensionsandcompressionActive(fromWebSocket.extensions)bytesReceived(decoded bytes)wireStats(compressed vs uncompressed bytes, reported by the server, since browsers don't expose compressed sizes to script)Repo fixes needed along the way
src/app/cover/page.tsx: the JSX had been truncated since update #161 and didn't parse. Steps 3–4 of the wizard were never finished. I rebuilt the cut-off sections, fixed an undefinedholderinsubmitBuy, and stringifiedtoStroops()results (abigintmakesJSON.stringifythrow). The hard-coded colours that pre-commit lint flags are nowpm-*tokens.vitest.config.ts: an empty file that shadowedvitest.config.mts, so the@/alias was missing in every test. Removed. The real config now also compiles JSX..eslintrc.json:Literal[value=1e7]is an invalid selector and crashed ESLint on every file. Changed to10000000.src/components/ui/index.ts: added the missing re-exports forAsyncState,SuccessBurst,QuickAmountChipsandTour.Known pre-existing breakage on
main(not addressed here)next buildandtscstill fail onmainfor reasons outside these issues:src/app/layout.tsx,src/components/ui/Button.tsx,src/components/layout/Navbar.tsx,src/components/wallet/index.ts,useCoverageTypes,useCoverageBounds,useOracleStatusanduseRecentClaims. Because of this:useOracleStatus/useRecentClaimscan't adopt the clock normalization yet; they should useuseServerRelativeTimeonce restored.{ flightNumber }, butBuyPolicyParamsrequires{ coverageType: 4, params: { flightNumber, departureDate } }.main.Tests
54 new tests, all passing:
The bundle-check script was verified against synthetic
.nextoutput, both passing and failing (dev marker shipped, client in an eager chunk).🤖 Generated with Claude Code