feat(kit): add conditional entitlement refresh - #258
Conversation
Expose bounded user-scoped subscription snapshots with conditional ETag responses for cold-start, stale-foreground, and explicit refresh flows.\n\nDocument persistent client caching, rate-limit handling, expiry evaluation, and the hosted-only rollout while preserving inbound store webhooks and keeping outbound event streams removed.
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
|
Warning Review limit reached
Next review available in: 19 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThe change adds bounded subscription evaluation snapshots, conditional status and entitlement responses with ETags, updated subscription API routes, and documentation for polling-based refresh without outbound event streams. ChangesSubscription snapshots
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant App
participant FlyAPI
participant Convex
App->>FlyAPI: Request status or entitlements with If-None-Match
FlyAPI->>Convex: Load subscriptionEvaluationSnapshot
Convex-->>FlyAPI: Return bounded candidates and fallback
FlyAPI-->>App: Return 200 snapshot or 304
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
packages/kit/server/api/v1/subscriptions.test.ts (1)
306-306: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winRestore the
Date.nowspy in a teardown hook so a failing assertion can't leak it.
dateNow.mockRestore()runs at the end of the test body; if any precedingexpectthrows,Date.nowstays stubbed and later time-dependent tests in this file observe a frozen clock. Move restoration intoafterEach(or enablerestoreMocksin the Vitest config) so it always runs.♻️ Sketch
+ afterEach(() => { + vi.restoreAllMocks(); + });Also applies to: 364-364, 499-499, 543-543
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/kit/server/api/v1/subscriptions.test.ts` at line 306, Move restoration of the Date.now spy created by dateNow in the affected tests into an afterEach teardown hook, ensuring it runs even when assertions fail. Apply the same cleanup to all listed occurrences and remove the test-body-only dateNow.mockRestore() calls.packages/kit/server/api/v1/subscriptions.ts (1)
50-70: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsider deriving these types from the generated Convex API instead of hand-mirroring
subscriptionShape.
SubscriptionSnapshotRowduplicates every field ofsubscriptionShapeinpackages/kit/convex/subscriptions/query.ts. A future field added to the Convex validator won't surface here as a type error, and the ETag/response payload silently diverges from the Convex contract.♻️ Sketch
type SubscriptionEvaluationSnapshot = Awaited< ReturnType<typeof client.query<typeof api.subscriptions.query.subscriptionEvaluationSnapshot>> >; type SubscriptionSnapshotRow = SubscriptionEvaluationSnapshot["candidates"][number];🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/kit/server/api/v1/subscriptions.ts` around lines 50 - 70, Replace the hand-maintained SubscriptionSnapshotRow and SubscriptionEvaluationSnapshot definitions with types derived from the generated Convex API, using the return type of api.subscriptions.query.subscriptionEvaluationSnapshot and extracting candidates[number] for each row. Update the surrounding client/query type references as needed so the ETag and response payload remain aligned with the Convex contract.packages/docs/src/pages/docs/kit-backend.tsx (1)
605-696: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUndeclared
iapkitPublishableKey(and other free variables) in new conditional-refresh code samples. Both new TypeScript examples reference identifiers that are never declared or explained, breaking from this codebase's existing convention of using explicit placeholders (e.g.'<IAPKIT_PUBLISHABLE_KEY>') or env vars for keys in copy-pasteable snippets.
packages/docs/src/pages/docs/kit-backend.tsx#L605-L696: therefreshEntitlementsfunction body usesiapkitPublishableKey(line 644) without declaring it as a parameter or module-level placeholder; add a comment or parameter matching the file's existing'<IAPKIT_PUBLISHABLE_KEY>'/ env-var convention.packages/kit/src/pages/docs/sections/api.tsx#L248-L319: the "Conditional entitlement refresh" block additionally leavescached,maxStaleMs, anduserIdundeclared with no enclosing function at all; wrap the snippet in a function signature (mirroringkit-backend.tsx'srefreshEntitlements) so readers can see where each value originates.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/docs/src/pages/docs/kit-backend.tsx` around lines 605 - 696, Update packages/docs/src/pages/docs/kit-backend.tsx:605-696 by declaring or parameterizing iapkitPublishableKey in refreshEntitlements using the existing '<IAPKIT_PUBLISHABLE_KEY>' or environment-variable convention. Update packages/kit/src/pages/docs/sections/api.tsx:248-319 by wrapping the Conditional entitlement refresh snippet in a function signature mirroring refreshEntitlements, with cached, maxStaleMs, and userId explicitly provided; preserve the existing refresh logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/kit/server/api/v1/subscriptions.ts`:
- Around line 530-536: Update the active subscription selection in the status
response to use an explicit deterministic comparator instead of
activeSubscriptions[0]. Match the previous selectMostRecentlyUpdatedSubscription
behavior by ordering by updatedAt and applying a stable secondary tie-breaker
such as startedAt or id, while preserving the existing active flag and fallback
behavior.
---
Nitpick comments:
In `@packages/docs/src/pages/docs/kit-backend.tsx`:
- Around line 605-696: Update
packages/docs/src/pages/docs/kit-backend.tsx:605-696 by declaring or
parameterizing iapkitPublishableKey in refreshEntitlements using the existing
'<IAPKIT_PUBLISHABLE_KEY>' or environment-variable convention. Update
packages/kit/src/pages/docs/sections/api.tsx:248-319 by wrapping the Conditional
entitlement refresh snippet in a function signature mirroring
refreshEntitlements, with cached, maxStaleMs, and userId explicitly provided;
preserve the existing refresh logic.
In `@packages/kit/server/api/v1/subscriptions.test.ts`:
- Line 306: Move restoration of the Date.now spy created by dateNow in the
affected tests into an afterEach teardown hook, ensuring it runs even when
assertions fail. Apply the same cleanup to all listed occurrences and remove the
test-body-only dateNow.mockRestore() calls.
In `@packages/kit/server/api/v1/subscriptions.ts`:
- Around line 50-70: Replace the hand-maintained SubscriptionSnapshotRow and
SubscriptionEvaluationSnapshot definitions with types derived from the generated
Convex API, using the return type of
api.subscriptions.query.subscriptionEvaluationSnapshot and extracting
candidates[number] for each row. Update the surrounding client/query type
references as needed so the ETag and response payload remain aligned with the
Convex contract.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 7e99caf8-d54f-463d-baa4-bdef5f006400
📒 Files selected for processing (17)
packages/docs/public/llms-full.txtpackages/docs/public/llms.txtpackages/docs/src/pages/docs/kit-backend.tsxpackages/docs/src/pages/docs/updates/releases.tsxpackages/kit/COST-SAFETY.mdpackages/kit/README.mdpackages/kit/convex/schema.tspackages/kit/convex/subscriptions/query.test.tspackages/kit/convex/subscriptions/query.tspackages/kit/public/llms-full.txtpackages/kit/public/llms.txtpackages/kit/server/api/v1/subscriptions.test.tspackages/kit/server/api/v1/subscriptions.tspackages/kit/src/pages/docs/nav.tspackages/kit/src/pages/docs/sections/api.tsxpackages/kit/src/pages/docs/sections/quickstart.tsxpackages/kit/src/pages/docs/sections/release-notes.tsx
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/kit/src/pages/docs/sections/api.tsx`:
- Around line 271-306: Update canUseCachedSnapshot and the request-header logic
in the entitlement-fetch flow to reuse cached data and send cached.etag only
when cached.snapshot.userId === userId. Ensure both offline error fallback and
429 handling reject snapshots belonging to another user while preserving valid
same-user caching behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 00456367-71e0-4e7e-a24d-61b3d585d18b
📒 Files selected for processing (6)
packages/docs/src/pages/docs/kit-backend.tsxpackages/kit/convex/subscriptions/query.test.tspackages/kit/convex/subscriptions/query.tspackages/kit/server/api/v1/subscriptions.test.tspackages/kit/server/api/v1/subscriptions.tspackages/kit/src/pages/docs/sections/api.tsx
🚧 Files skipped from review as they are similar to previous changes (5)
- packages/docs/src/pages/docs/kit-backend.tsx
- packages/kit/convex/subscriptions/query.test.ts
- packages/kit/server/api/v1/subscriptions.ts
- packages/kit/server/api/v1/subscriptions.test.ts
- packages/kit/convex/subscriptions/query.ts
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
@coderabbitai review |
✅ Action performedReview finished.
|
Summary
304 Not ModifiedresponsesChanges
Hosted subscription refresh
(projectId, userId, updatedAt)lookup with a 201-row overflow probe and a fail-closed 200-row public contractActive/InGracePeriodcandidates plus one latest fallback for Fly-side evaluationexpiresAtagainst the Fly server clock on every HTTP requestHTTP and cache safety
Vary: Authorizationand private cache directives for publishable-key responsesprivate, no-storewithout ETagsRetry-AfterbehaviorDocumentation
Rollout
Deploy the additive Convex schema/query before the Fly server. Existing SDK helpers remain unconditional body-only reads, so this hosted-only change does not require a native or framework package version bump.
Test plan
404git diff --checkPreview
A visual recording is not applicable because this is a hosted HTTP/Convex contract and documentation change with no new interactive UI. The compiled-server smoke probes and regression tests above cover the changed surface.
Summary by CodeRabbit
ETag/If-None-Matchand304reuse.429Retry-Afterguidance and stale-age/offline behavior.ETag, and caching edge cases.