-
Notifications
You must be signed in to change notification settings - Fork 37
Enable sync only for vibe-viewer context + simplify ClerkTokenStrategy #716
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
✅ Deploy Preview for fireproof-ai-builder canceled.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codex Review
Here are some automated review suggestions for this pull request.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The main functional issue is that toCloud accepts a tokenStrategy but never forwards it to originalToCloud, so ClerkTokenStrategy is not actually used for sync auth. There are also architectural concerns: useFireproof now hard-depends on Clerk at runtime and has removed prior user-controlled sync toggling, which may be an intentional but breaking behavior change. Token fetching in ClerkTokenStrategy lacks any caching, which could introduce unnecessary load on Clerk. Tests correctly mock new dependencies but may benefit from a small abstraction to make sync gating via VibeContext more explicit and reusable.
Additional notes (6)
- Readability |
use-vibes/base/clerk-token-strategy.ts:22-29
TheClerkTokenStrategymethodsstopandopenare currently empty no-ops. While that may be correct for Clerk, the lack of any logging or comments about potential future behavior makes it easy for someone to miss these hooks if additional lifecycle work becomes necessary (e.g., revoking listeners, reacting to device changes).
Given this is a strategy used in a sync pipeline, it would be safer either to document that no lifecycle management is required or to add minimal logging to aid in debugging.
- Maintainability |
use-vibes/base/index.ts:72-80
toCloudnow accepts atokenStrategyin its options, but the implementation unconditionally forwards only the originaloptsspread plus hard-coded URIs andurls. If the caller passesopts.tokenStrategy, it is never forwarded tooriginalToCloud, so theClerkTokenStrategyis effectively ignored.
This is a logical bug: useFireproof computes tokenStrategy and passes it into toCloud({ tokenStrategy: tokenStrategy as TokenStrategie }), but the strategy is dropped here, meaning the intended Clerk-based auth for sync is not actually wired up. This contradicts the PR summary that ClerkTokenStrategy is integrated as the sync auth mechanism.
- Maintainability |
use-vibes/base/index.ts:109-109
useFireproofnow unconditionally callsuseAuth()from@clerk/clerk-react, and@clerk/clerk-reactis only declared as a peer dependency. This means any consumer of@vibes.diy/use-vibes-basethat doesn’t have Clerk set up (e.g., internal tools, tests, or different auth providers) will hit a runtime error when this hook is used, even if they never intend to sync.
This is a coupling concern: a base hook that’s meant to be generally reusable in the use-vibes ecosystem now hard-depends on Clerk at runtime rather than making auth pluggable. A failure to provide a Clerk provider will likely throw during render, which is brittle for non-viewer contexts the PR aims to treat differently. It also makes it harder to swap auth providers in the future.
- Performance |
use-vibes/base/index.ts:128-133
getTokenis captured in auseMemodependency array, but depending on Clerk's implementation this function reference may be unstable (e.g., recreated on each render). That would causeClerkTokenStrategyto be re-instantiated frequently, which may defeat the purpose of memoization and could lead to subtle behavior differences if the strategy is expected to be long-lived.
If useAuth().getToken is stable per Clerk's docs, this is fine; if not, you risk unnecessary churn in the token strategy construction on every render.
Given the current code, the useMemo adds complexity with limited benefit unless you can rely on getToken stability.
- Maintainability |
use-vibes/tests/useFireproof.bodyClass.test.tsx:28-28
The change in sync gating—fromwasSyncEnabled+ localStorage tovibeMetadatapresence—means that tests or code paths that previously exercised sync in non-vibe-viewercontexts will now always seesyncEnabled === falseunlessuseVibeContextreturns metadata. The test fixes this by mockinguseVibeContext, but there’s still an implicit coupling here: any future tests around sync must remember to mock the context, or they’ll silently see sync as disabled.
To make tests more robust and less dependent on implicit knowledge of context gating, it may be worth centralizing a small helper or factory for test setups that require sync, or at least documenting in the test suite that useVibeContext must be mocked when testing sync-related behavior.
- Maintainability |
use-vibes/tests/useFireproof.bodyClass.test.tsx:28-36
The test now mocksuseVibeContextby intercepting@vibes.diy/use-vibes-base, which is also the module under test. This pattern works but tightly couples the test to the package's barrel exports and may be fragile if you refactor entry points or split the context into a separate file.
If VibeContext is defined in a dedicated module (e.g., ./contexts/VibeContext), it would be more robust to mock that module directly rather than the entire base package.
Summary of changes
Summary of Changes
- Upgraded
@adviser/cementto^0.5.2and aligneduse-fireprooflockfile entry with that version. - Added
@clerk/clerk-reactas a peer and dev dependency for theuse-vibes/basepackage. - Introduced a new
ClerkTokenStrategyclass implementing Fireproof'sTokenStrategieinterface, delegating token acquisition to a providedgetTokenfunction and passing tokens through without decoding claims. - Updated the
toCloudhelper to accept an optionaltokenStrategyand wireduseFireproofto:- Use Clerk's
useAuth().getTokenviaClerkTokenStrategy. - Enable sync only when
vibeMetadataexists and pass thetokenStrategyintotoCloud. - Treat sync as enabled based solely on Fireproof attach state, removing localStorage-based sync toggling and stub
enableSync/disableSynccallbacks.
- Use Clerk's
- Adjusted tests to mock
@clerk/clerk-reactandVibeContextso sync can be exercised in tests that depend on body class behavior.
| "@clerk/clerk-react": ">=5.0.0", | ||
| "react": ">=19.1.0" | ||
| }, | ||
| "devDependencies": { | ||
| "@chromatic-com/storybook": "^4.1.3", | ||
| "@clerk/clerk-react": "^5.57.0", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new @clerk/clerk-react peer dependency is versioned as >=5.0.0, while the devDependency is pinned with ^5.57.0. This is a fairly wide range for consumers, and if Clerk introduces breaking changes in a later 5.x patch/minor that affect useAuth().getToken, consumers could see runtime issues that won't be caught by your dev setup.
Given you directly depend on useAuth behavior, a tighter peer range (or at least documenting tested versions) would help avoid subtle integration bugs in downstream apps.
Suggestion
Consider narrowing the peer dependency range to the versions you actively test against, for example:
"peerDependencies": {
"@clerk/clerk-react": "^5.57.0",
"react": ">=19.1.0"
},
"devDependencies": {
"@clerk/clerk-react": "^5.57.0",
// ...
}Alternatively, document in the README which Clerk major/minor you validate, and adjust the semver range accordingly. Reply with "@CharlieHelps yes please" if you'd like a commit updating the peer range and adding a brief note to your package docs/changelog.
- Create ClerkTokenStrategy implementing TokenStrategie pattern
- Strategy uses Clerk getToken({ template: 'with-email' }) for auth
- Pass tokenStrategy to toCloud() for automatic sync enablement
- Remove stub enableSync/disableSync functions (sync always on)
- Add @clerk/clerk-react as peer/dev dependency
Benefits:
- Sync automatically enabled for all useFireproof calls
- Uses Fireproof's native TokenStrategie pattern
- Bypasses backend JWT verification issues
- Follows same pattern as SimpleTokenStrategy
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
ClerkTokenStrategy is now the only auth method. This prevents the Fireproof popup from appearing when token verification fails. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Brings back the dashboard popup as an alternative auth method when ClerkTokenStrategy token verification fails. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Upgraded @adviser/cement from 0.4.66 to 0.5.2 to match Fireproof's version - Fixed ClerkTokenStrategy to properly implement TokenStrategie interface - Updated method signatures: open(), tryToken(), waitForToken() with correct parameters - Added Clerk useAuth mock to useFireproof.bodyClass.test.tsx This resolves type incompatibility issues between different versions of @adviser/cement and ensures proper integration with Fireproof's cloud sync token strategy pattern. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
…trategy - Modified useFireproof to conditionally enable sync only when vibeMetadata exists - This ensures only instance-specific databases (running in vibe-viewer) get synced - Simplified ClerkTokenStrategy to pass tokens through without decoding/parsing claims - Updated tests to mock VibeContext for proper sync testing 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Added dashboard API integration to exchange Clerk tokens for cloud session tokens - Cloud tokens are per-deviceId (local database name) with tenant+ledger permissions - Tokens cached in KeyBag with automatic expiration checking (60s buffer) - Added @fireproof/core-protocols-dashboard dependency This ensures each database gets the correct cloud token for its tenant+ledger combination. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Updated lockfile after adding @fireproof/core-protocols-dashboard dependency. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This dev version includes ClerkTokenStrategy for cloud sync authentication with Fireproof dashboard. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Added test-driven implementation to properly forward the tokenStrategy parameter to Fireproof's strategy field. This ensures ClerkTokenStrategy reaches the core sync logic instead of falling back to redirect strategy. - Added toCloud test suite with 8 tests - Maps opts.tokenStrategy to strategy field - All 756 tests passing 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Fixed "Cannot read properties of null (reading 'useContext')" error on deploy preview by pinning React to 19.2.0 in the import map. This ensures all modules load the same React instance from esm.sh. - Pin react@19.2.0, react-dom@19.2.0 - Prevents multiple React instances - Fixes Clerk context errors on deploy preview 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
KeyBag already handles token caching internally, so the additional in-memory cache layer (cachedToken, cachedTokenExpiresAt, isCachedTokenValid, updateCacheFromClaims) was unnecessary and added complexity. Simplified to check token expiry directly from KeyBag with a 60s buffer. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Ensure `syncEnabled` is only true when running inside a vibe-viewer context (i.e. `vibeMetadata` is present) and the attach state is `attached` or `attaching`, instead of relying solely on attach state. - Update `useFireproof` to require `vibeMetadata` for sync to be enabled - Derive `rawAttachState` once and reuse it for clarity - Refactor body class tests to: - Use `VibeContextProvider` instead of mocking `useVibeContext` - Remove localStorage-based sync preference assumptions - Explicitly test body class behavior only in the viewer-context setup - Keep attach-state based behavior for body class management, but now scoped to a proper vibe-viewer context This aligns sync and visual indicators (body class) with the intended vibe-viewer-only behavior.
Changed architecture to avoid prop drilling by providing getToken through VibeContext. This fixes the error on deploy previews where user vibes loaded from esm.sh would fail with "useAuth can only be used within <ClerkProvider>". The vibe-viewer wraps content in VibeContextProvider with getToken from useAuth(). The useFireproof hook gets getToken from context via useVibeGetToken(). User vibes in iframes don't have a provider and will run without sync (local-only mode). Changes: - VibeContext: Add getToken to context value and new useVibeGetToken hook - useFireproof: Get getToken from context instead of parameter - vibe-viewer: Wrap in VibeContextProvider with getToken from useAuth - Export VibeContextProvider and related types from use-vibes package 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Thread getToken function through the entire mounting chain so user vibes can access Clerk authentication from React Context. This enables cloud sync in authenticated contexts while maintaining local-only mode where ClerkProvider is not available. Changes: - Add getToken parameter to mountVibeCode, mountVibeWithCleanup - Update MountVibesAppOptions interface and VibesApp component - Pass getToken to VibeContextProvider when mounting vibes - vibe-viewer: Pass getToken to enable sync for authenticated users - InlinePreview: Don't pass getToken to maintain local-only mode This fixes the "useAuth can only be used within ClerkProvider" error by providing authentication through context instead of requiring direct access to Clerk hooks. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Update import map to use newly published version with getToken fix. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
67d592c to
3d3b6c4
Compare
The self-mapping "https://esm.sh/use-vibes" → "https://esm.sh/use-vibes@VERSION" was causing esm.sh to repeatedly fetch and evaluate the same module when resolving dependencies, leading to CPU spikes on /groups and /vibe/{slug} pages. The bare specifier mapping ("use-vibes" → versioned URL) is sufficient. esm.sh handles versioned URLs natively without import map intervention. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Added console.log statements throughout the call chain: - GroupsRoute component - GroupsContent component - useAllGroups hook - useFireproof hook This will help identify where the infinite loop or CPU spike is occurring. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
The hook was creating new function and object references on every render, causing React to continuously re-render the GroupsContent component. Changes: - Memoize the filter function passed to useLiveQuery with useMemo - Memoize the return value object to prevent new references - Both are keyed on their actual dependencies (userId, groups, groupsResult.docs) This fixes the CPU spike on /groups page. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
🎉 Hosting Preview DeploymentPreview URL: https://pr-716-vibes-hosting-v2.jchris.workers.dev Test the preview:
This preview will be automatically updated on each push to this PR |
useAllGroups and useVibeInstances were incorrectly importing useFireproof from use-vibes, which added unnecessary cloud sync setup for non-vibe databases. These hooks access plain "vibes-groups" database without vibe context, so they should use the base useFireproof from use-fireproof, not the wrapped version. Benefits: - Avoids unnecessary Clerk token exchange attempts - Prevents circuit breaker from triggering on non-vibe databases - Reduces CPU usage and prevents page locks 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Move use-fireproof → use-vibes transformation from global importmap into vibe code transformation function. This ensures only ejected vibe code (with vibe context) uses the cloud sync wrapper, while app-level database code uses plain use-fireproof. Changes: - Add import transformation in component-transforms.ts - Update importmap to point use-fireproof to correct version - Remove global redirect that forced all use-fireproof → use-vibes This fixes page locks caused by unnecessary cloud sync attempts on non-vibe databases like vibes-groups catalog. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
… and enhance useFireproof hook logging
- Add importmap to use-vibes-base package.json to resolve use-fireproof - Update VIBES_VERSION to 0.18.12-dev - Add redirects for use-fireproof 0.24.0 to dev-memo version in importmap 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
The transformImportsDev function wasn't transforming use-fireproof to use-vibes, so vibes were importing the wrong package. Now it applies the same transformation that component-transforms.ts uses. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Import Resolution Fixed ✅This PR now correctly handles the What Was Fixed
How It WorksWhen a vibe is loaded in the viewer at
Console OutputYou'll now see logs like: Sync is now automatically enabled for all vibes! 🎉 |
|
I think the remaining issue might be on the fireproof side with dashboard env handling |
| } | ||
|
|
||
| // Fallback: simple 30s in-memory TTL when we don't have a usable exp. | ||
| this.cacheByDevice.set(deviceId, { token: jwt, expiresAt: now + 30000 }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe this is all we need
- Add window.__VIBES_CONNECT_API_URL__ configuration in root.tsx - Update clerk-token-strategy to read API URL from window variable - Fix Vite dev mode to use local React instead of importmap (prevents duplicate React errors) - Allows switching between dev/staging/production dashboard endpoints via env.ts This enables testing Clerk token verification against dev dashboard without hardcoding production URLs. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2833b2d to
26c8ebe
Compare
…0.24.0 - Updated all package.json dependencies to use stable 0.24.0 - Removed importmap redirects to dev-memo version - Cleaned up use-fireproof importmap references This reverts to the stable release version. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Replace manual Map-based cache with Lazy wrapper using resetAfter: 30000 - Consolidate token fetch logic into single tokenCache function - Store sthis and logger as instance variables for Lazy closure - Simplify tryToken to just call tokenCache() - Maintains same 30-second cache window with cleaner code
Summary
useFireproofto conditionally enable sync only when vibeMetadata existsClerkTokenStrategyto pass tokens through without decoding/parsing claimsChanges
vibeMetadata(titleId + installId) exists, which happens in the vibe-viewer routeClerkTokenStrategynow just returns tokens without decoding claimsTest Results
All 748 tests passing ✅
🤖 Generated with Claude Code