Skip to content

Conversation

@jchris
Copy link
Contributor

@jchris jchris commented Dec 2, 2025

Summary

  • 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
  • Upgraded @adviser/cement to 0.5.2 to match Fireproof dependencies
  • Updated tests to mock VibeContext for proper sync testing

Changes

  1. Conditional Sync Based on Vibe Context - Sync is now only enabled when vibeMetadata (titleId + installId) exists, which happens in the vibe-viewer route
  2. Simplified Token Strategy - ClerkTokenStrategy now just returns tokens without decoding claims
  3. Dependency Upgrade - @adviser/cement upgraded from 0.4.66 to 0.5.2
  4. Test Updates - Added mocks for Clerk auth and VibeContext

Test Results

All 748 tests passing ✅

🤖 Generated with Claude Code

@charliecreates charliecreates bot requested a review from CharlieHelps December 2, 2025 18:10
@netlify
Copy link

netlify bot commented Dec 2, 2025

Deploy Preview for fireproof-ai-builder canceled.

Name Link
🔨 Latest commit db45ee9
🔍 Latest deploy log https://app.netlify.com/projects/fireproof-ai-builder/deploys/69302e6620925900080948e4

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a 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".

Copy link
Contributor

@charliecreates charliecreates bot left a 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
    The ClerkTokenStrategy methods stop and open are 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
    toCloud now accepts a tokenStrategy in its options, but the implementation unconditionally forwards only the original opts spread plus hard-coded URIs and urls. If the caller passes opts.tokenStrategy, it is never forwarded to originalToCloud, so the ClerkTokenStrategy is 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
    useFireproof now unconditionally calls useAuth() from @clerk/clerk-react, and @clerk/clerk-react is only declared as a peer dependency. This means any consumer of @vibes.diy/use-vibes-base that 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
    getToken is captured in a useMemo dependency array, but depending on Clerk's implementation this function reference may be unstable (e.g., recreated on each render). That would cause ClerkTokenStrategy to 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—from wasSyncEnabled + localStorage to vibeMetadata presence—means that tests or code paths that previously exercised sync in non-vibe-viewer contexts will now always see syncEnabled === false unless useVibeContext returns metadata. The test fixes this by mocking useVibeContext, 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 mocks useVibeContext by 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/cement to ^0.5.2 and aligned use-fireproof lockfile entry with that version.
  • Added @clerk/clerk-react as a peer and dev dependency for the use-vibes/base package.
  • Introduced a new ClerkTokenStrategy class implementing Fireproof's TokenStrategie interface, delegating token acquisition to a provided getToken function and passing tokens through without decoding claims.
  • Updated the toCloud helper to accept an optional tokenStrategy and wired useFireproof to:
    • Use Clerk's useAuth().getToken via ClerkTokenStrategy.
    • Enable sync only when vibeMetadata exists and pass the tokenStrategy into toCloud.
    • Treat sync as enabled based solely on Fireproof attach state, removing localStorage-based sync toggling and stub enableSync/disableSync callbacks.
  • Adjusted tests to mock @clerk/clerk-react and VibeContext so sync can be exercised in tests that depend on body class behavior.

Comment on lines +35 to +41
"@clerk/clerk-react": ">=5.0.0",
"react": ">=19.1.0"
},
"devDependencies": {
"@chromatic-com/storybook": "^4.1.3",
"@clerk/clerk-react": "^5.57.0",
Copy link
Contributor

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.

jchris and others added 20 commits December 2, 2025 12:23
- 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>
@jchris jchris force-pushed the jchris/use-fireproof-default branch from 67d592c to 3d3b6c4 Compare December 2, 2025 20:28
jchris and others added 3 commits December 2, 2025 12:32
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>
@github-actions
Copy link

github-actions bot commented Dec 2, 2025

🎉 Hosting Preview Deployment

Preview URL: https://pr-716-vibes-hosting-v2.jchris.workers.dev
Worker: pr-716-vibes-hosting-v2
Commit: ce54f53

Test the preview:

  1. Visit the preview URL above
  2. Test hosting functionality (KV assets, queue operations)
  3. Verify API integrations work correctly
  4. Check domain routing is disabled (workers.dev only)

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>
- 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>
@jchris
Copy link
Contributor Author

jchris commented Dec 3, 2025

Import Resolution Fixed ✅

This PR now correctly handles the use-fireproofuse-vibes import transformation across all loading scenarios:

What Was Fixed

  1. Added importmap to use-vibes-base package.json - Tells esm.sh to resolve use-fireproof to the correct version (0.24.1-dev-memo) when bundling the package

  2. Updated vibe-viewer transform - The transformImportsDev function now transforms use-fireproof imports to use-vibes before executing vibe code

  3. Added browser-side importmap redirects - Redirects any remaining use-fireproof@0.24.0 requests to the dev-memo version

How It Works

When a vibe is loaded in the viewer at /vibe/:titleId/:installId:

  1. Vibe code is fetched from https://{titleId}.vibesdiy.app/App.jsx
  2. transformImportsDev transforms import { useFireproof } from "use-fireproof""use-vibes"
  3. Browser loads use-vibes@0.18.12-dev from esm.sh
  4. The package's importmap ensures any transitive use-fireproof imports resolve correctly
  5. useFireproof hook detects it's in a vibe-viewer context (has vibeMetadata + getToken)
  6. Sync is automatically enabled and logged to console

Console Output

You'll now see logs like:

[useFireproof] 🎉 First call to useFireproof hook
[useFireproof] RENDER 0.1234567
[useFireproof] getToken changed? true
[useFireproof] vibeMetadata changed? true
[useFireproof] augmentedDbName: vf-memo-vault-london-discovery-6406-42388213
[useFireproof] Got fpResult, attach state: attaching

Sync is now automatically enabled for all vibes! 🎉

@jchris
Copy link
Contributor Author

jchris commented Dec 3, 2025

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 });
Copy link
Contributor Author

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>
@jchris jchris force-pushed the jchris/use-fireproof-default branch from 2833b2d to 26c8ebe Compare December 3, 2025 02:22
@jchris jchris marked this pull request as draft December 3, 2025 02:23
jchris and others added 2 commits December 2, 2025 18:26
…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
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.

3 participants