Skip to content

move auth helpers to auth package#27

Merged
cobraprojects merged 2 commits into
mainfrom
move-auth-helpers-to-auth-packages
May 7, 2026
Merged

move auth helpers to auth package#27
cobraprojects merged 2 commits into
mainfrom
move-auth-helpers-to-auth-packages

Conversation

@cobraprojects
Copy link
Copy Markdown
Owner

@cobraprojects cobraprojects commented May 7, 2026

Summary by CodeRabbit

  • New Features

    • Added composable route protection (guest-only and auth-only) protecting admin routes.
  • Improvements

    • Centralized framework auth clients for Next/Nuxt/SvelteKit and consolidated auth flows.
    • Login/register/verify flows now refresh user state after successful actions.
  • Documentation

    • Updated auth docs with new client package guidance and route protection examples.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 7, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 083c506b-14de-4e49-9434-d1c040712919

📥 Commits

Reviewing files that changed from the base of the PR and between 80974ba and 9a8d908.

📒 Files selected for processing (9)
  • apps/blog-next/tests/run.mjs
  • apps/blog-nuxt/app/pages/login.vue
  • apps/blog-sveltekit/tests/run.mjs
  • apps/docs/docs/forms/server-validation.md
  • packages/adapter-nuxt/tests/package.test.ts
  • packages/auth/src/next/client.ts
  • packages/auth/src/next/server.ts
  • packages/auth/src/nuxt/server.ts
  • packages/auth/tests/framework.test.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • apps/blog-next/tests/run.mjs
  • apps/blog-nuxt/app/pages/login.vue
  • apps/docs/docs/forms/server-validation.md
  • packages/adapter-nuxt/tests/package.test.ts
  • packages/auth/src/next/server.ts

📝 Walkthrough

Walkthrough

This PR extracts framework auth helpers into a new @holo-js/auth package (Next/Nuxt/SvelteKit entrypoints), implements route-protection primitives (guestOnly, authOnly, protectRoutes) and request-context helpers, removes auth surface from framework adapters, updates apps to use the new auth imports and route guards, and adjusts tests/docs for the new boundaries and redirect behavior.

Changes

Auth package extraction & framework integration

Layer / File(s) Summary
Contracts / Types
packages/auth/src/contracts.ts (implied), packages/auth/src/nuxt-shim.d.ts
New auth contracts/types and Nuxt shims added for framework-specific clients.
Next.js: client & server
packages/auth/src/next/client.ts
packages/auth/src/next/server.ts
packages/auth/src/next/request-context.ts
Adds AuthProvider, useAuth, request-context AsyncLocalStorage helpers, auth(), guestOnly(), authOnly(), protectRoutes(), and route-matching utilities.
Nuxt: composable & server
packages/auth/src/nuxt.ts
packages/auth/src/nuxt/server.ts
Adds useAuth composable, guestOnly/authOnly middleware, and isSamePath/route-matching helpers.
SvelteKit: client & server
packages/auth/src/sveltekit/client.ts
packages/auth/src/sveltekit/server.ts
Adds SvelteKit useAuth client/context API and server route-protection helpers including isSameUrl.
Auth build / manifest
packages/auth/package.json
packages/auth/tsup.config.ts
Adds framework peer deps (optional) and tsup entries for framework-specific client/server bundles.

Adapter package cleanup

Layer / File(s) Summary
Remove auth from adapters
packages/adapter-next/src/client.ts
packages/adapter-sveltekit/src/client.ts
packages/adapter-nuxt/package.json
Auth provider/hook implementations and re-exports removed from adapter client files; adapters no longer depend on @holo-js/auth.
Request-context move
packages/adapter-next/src/request-context.ts
Switches module-level AsyncLocalStorage to a lazily-initialized globalThis-backed store.
Build/config updates
packages/adapter-*/tsup.config.ts
packages/adapter-*/tsconfig.json
packages/adapter-*/vitest.config.ts
Removes server build entries and @holo-js/auth* path/alias mappings; adjusts package tests to reflect new boundaries.
Adapter tests
packages/adapter-*/tests/*
Updated type tests and package-boundary tests to import/use auth from @holo-js/auth/* and to assert auth surface removed from adapters; adapter-nuxt protection tests removed.

Application migrations & route protection wiring

Layer / File(s) Summary
Next.js app imports & proxy
apps/blog-next/app/layout.tsx
apps/blog-next/app/auth-nav.tsx
apps/blog-next/app/login/page.tsx
apps/blog-next/app/verify-email/page.tsx
apps/blog-next/proxy.ts
Switched AuthProvider, useAuth, and auth() imports to @holo-js/auth/next/*; proxy now composes guestOnly(...) and authOnly(...) and expands matcher to include /admin/:path*.
Nuxt app composables & middleware
apps/blog-nuxt/app/app.vue
apps/blog-nuxt/app/pages/*.vue
apps/blog-nuxt/app/middleware/*.global.ts
Pages/components now call useAuth from @holo-js/auth/nuxt and call refreshUser() after successful login/register/verify; new auth-only.global.ts middleware added and guest-only import switched to @holo-js/auth/nuxt/server.
SvelteKit app updates
apps/blog-sveltekit/src/routes/+layout.server.ts
apps/blog-sveltekit/src/routes/+layout.svelte
apps/blog-sveltekit/src/routes/*
apps/blog-sveltekit/src/hooks.server.ts
Imports moved to @holo-js/auth/sveltekit/*; handle middleware composed via sequence(guestOnly(...), authOnly(...)) to protect /admin/*.

Tests, helpers & docs

Layer / File(s) Summary
Redirect helper & test updates
apps/blog-*/tests/run.mjs
apps/blog-*/tests/*
apps/blog-next/tests/run.mjs
apps/blog-sveltekit/tests/run.mjs
Adds waitForRedirect(url, expectedPath, timeoutMs) helper and replaces admin-page content checks with redirect assertions to /login.
Example / integration checks
tests/example-app-auth-flow.mjs
Adds authenticated /admin/posts content assertion for logged-in sessions.
Docs updates
apps/docs/docs/auth/*
apps/docs/docs/forms/*
Documentation updated to recommend @holo-js/auth/* client imports and adds a new “Protecting Routes” section with framework examples.
Auth tests & type checks
packages/auth/tests/*
Adds framework typing assertions and framework helper tests (including React mock tests and route-protection behavior checks).

Sequence Diagram(s)

sequenceDiagram
    participant Browser as Browser (Login Page)
    participant Form as useForm
    participant AuthClient as useAuth
    participant API as /api/login
    participant AuthServer as /api/auth/user

    Browser->>Form: submit credentials
    Form->>API: POST /api/login
    API-->>Browser: { ok: true, redirectTo }
    Browser->>AuthClient: refreshUser()
    AuthClient->>AuthServer: GET current-auth
    AuthServer-->>AuthClient: HoloAuthUser
    AuthClient->>Browser: update user state
    Browser->>Browser: navigate to redirectTo
Loading
sequenceDiagram
    participant User as Browser
    participant App as App Server
    participant Middleware as guestOnly/authOnly
    participant AuthServer as auth()
    participant Login as /login

    User->>App: GET /admin/posts
    App->>Middleware: protectRoutes(...) runs
    Middleware->>AuthServer: auth()
    AuthServer-->>Middleware: { authenticated: false }
    Middleware-->>App: redirect 303 -> /login
    App->>Login: GET /login
    Login-->>User: login form
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 From adapter burrows I hop and sing,

auth helpers nested in a new package spring.
Routes guarded, redirects neat and spry,
apps updated, tests now spy.
Hop-hop — refresh and away I fly!

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch move-auth-helpers-to-auth-packages

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/blog-nuxt/app/pages/login.vue (1)

12-17: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

refreshUser rejection will silently swallow the navigateTo call.

If refreshUser() throws (e.g., a transient network error immediately after a successful server-side login), the navigateTo on line 14 is never reached. The user is authenticated server-side but remains stuck on the login page. Prior to this PR, navigateTo was called directly without this intermediate step.

🐛 Proposed fix — navigate regardless of refresh outcome
  if (submission?.ok === true && typeof submission.data?.redirectTo === 'string') {
-   await refreshUser()
+   try { await refreshUser() } catch { /* best-effort; auth state will sync on next page load */ }
    await navigateTo(submission.data.redirectTo, {
      external: true,
    })
  }
🤖 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 `@apps/blog-nuxt/app/pages/login.vue` around lines 12 - 17, The code currently
awaits refreshUser() before calling navigateTo(), so if refreshUser() rejects
the navigateTo(submission.data.redirectTo) is never executed; update the
submit-success path in the login page to ensure navigateTo is invoked regardless
of refreshUser outcome by moving navigateTo into a finally block or by catching
errors from refreshUser (call refreshUser() in a try/catch and log/ignore
errors) and then calling navigateTo(submission.data.redirectTo, { external: true
}) unconditionally; reference refreshUser and navigateTo in the login.vue
success handling to locate and change the flow.
🧹 Nitpick comments (2)
packages/adapter-nuxt/tests/package.test.ts (1)

29-32: ⚡ Quick win

Reduce brittleness in dynamic-import assertions.

These toContain(...) checks are tied to exact formatting (as string, quote style). Prefer pattern matching so refactors don’t break boundary tests without behavior changes.

Proposed update
-    expect(moduleEntry).toContain("import('@holo-js/storage')")
-    expect(moduleEntry).toContain("import('@holo-js/storage-s3' as string)")
+    expect(moduleEntry).toMatch(/import\(['"]@holo-js\/storage['"]\)/)
+    expect(moduleEntry).toMatch(/import\(['"]@holo-js\/storage-s3['"](?:\s+as\s+string)?\)/)
🤖 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/adapter-nuxt/tests/package.test.ts` around lines 29 - 32, The
assertions in package.test.ts are brittle because they depend on exact
formatting like quote style and the literal "as string"; update the assertions
to use pattern matching (regex) against moduleEntry and runtimeEntry instead of
exact substrings: replace the toContain checks for import('@holo-js/storage')
and import('@holo-js/storage-s3' as string) with regex-based expectations (e.g.,
expecting an import of `@holo-js/storage` or `@holo-js/storage-s3` regardless of
quotes or whitespace), and keep the negative check for "@holo-js/forms" but use
a regex/contains check that is tolerant to formatting changes; update references
to moduleEntry and runtimeEntry accordingly (use expect(...).toMatch or
expect.stringMatching).
apps/docs/docs/forms/server-validation.md (1)

247-387: 💤 Low value

Consider adding a SvelteKit useForm + useAuth tab for parity.

The Next.js and Nuxt tabs in this code-group now both demonstrate useForm combined with useAuth (and the post-submit refreshUser() call). The SvelteKit tab shows a form actions pattern instead, which is entirely server-side and doesn't use useAuth. A reader following the SvelteKit path won't find the equivalent client-side useAuth from @holo-js/auth/sveltekit/client usage here — they'd have to look elsewhere.

Adding a second SvelteKit tab (e.g., [SvelteKit — useForm client]) that mirrors the Next.js/Nuxt pattern with useAuth from @holo-js/auth/sveltekit/client would make the three frameworks comparable in this section.

🤖 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 `@apps/docs/docs/forms/server-validation.md` around lines 247 - 387, Add a
second SvelteKit tab demonstrating the client-side useForm + useAuth pattern to
match Next.js/Nuxt: create a "[SvelteKit — useForm client]" example that imports
useAuth from '@holo-js/auth/sveltekit/client' and useForm from
'@holo-js/adapter-sveltekit/client', initializes the form with loginForm and
csrf/initialValues, implements submitter which posts formData and calls
refreshUser() (or auth.refreshUser()) on success, and binds form.values.*, uses
form.fields.email.onInput/onBlur etc and form.submit() in the template so
readers can see the same client-side flow; ensure the tab mirrors the symbols
used in the other examples (useAuth, useForm, loginForm, submitter, refreshUser,
form.fields.*, form.values.*, form.submit).
🤖 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 `@apps/blog-next/tests/run.mjs`:
- Around line 157-183: The waitForRedirect loop can hang if a single fetch never
resolves; to fix, add an AbortController for each fetch inside waitForRedirect
and pass its signal to fetch, scheduling a per-request timeout (e.g., timeout =
Math.max(100, Math.min(remainingMs, 5000))) using setTimeout to call
controller.abort(); compute remainingMs = timeoutMs - (Date.now() - startedAt)
before each request, clear the timeout after fetch completes, and handle abort
errors in the existing catch so the overall timeout still stops the loop —
update the fetch call and surrounding try/catch in waitForRedirect accordingly.

In `@apps/blog-sveltekit/tests/run.mjs`:
- Around line 156-182: The polling helper waitForRedirect can hang because an
in-flight fetch is not bounded by timeoutMs; modify waitForRedirect to create an
AbortController for each fetch call, pass controller.signal into fetch, and set
a per-request timer (e.g., Math.min(remainingTime, 5000) or a fixed 5s) that
calls controller.abort() when it expires; clear that timer in a finally block so
aborted fetches throw and are caught by the existing catch block (preserve
lastError handling) and ensure you compute remainingTime from startedAt to avoid
overrunning the overall timeoutMs.

In `@packages/auth/src/next/client.ts`:
- Around line 62-73: The hook treats any passed options object (e.g. {}) as
truthy causing refreshOnMount to be true and bypassing the provider; update
useAuth to treat only an absent options argument as "no options": replace
Boolean(options) with options === undefined in the refreshOnMount expression and
change the provider short-circuit to check options === undefined (i.e. if
(options === undefined && context) return context) so useAuth({}) behaves the
same as useAuth().

In `@packages/auth/src/next/server.ts`:
- Around line 75-79: The isSameUrl function currently compares only pathname,
search and hash and therefore treats URLs with different origins as equal;
update isSameUrl to also compare left.origin === right.origin so the function
returns false when origins differ. Locate the isSameUrl function and add origin
to the equality check (alongside pathname, search and hash) so absolute
redirectTo values on other origins are not treated as self-redirects.

In `@packages/auth/src/nuxt/server.ts`:
- Around line 68-70: The isSamePath guard in isSamePath(path: string,
redirectTo: string) compares normalizePathname(path) to
normalizePathname(redirectTo) but fails when redirectTo contains a query or hash
(e.g., "/login?returnUrl=..."), causing redirect loops; update isSamePath to
first strip query string and fragment from redirectTo (and optionally path)
before normalizing and comparing — e.g., parse redirectTo to extract only the
pathname (using URL or string split on '?' / '#') and then call
normalizePathname on that value so "/login" and "/login?returnUrl=..." are
treated as the same path.

---

Outside diff comments:
In `@apps/blog-nuxt/app/pages/login.vue`:
- Around line 12-17: The code currently awaits refreshUser() before calling
navigateTo(), so if refreshUser() rejects the
navigateTo(submission.data.redirectTo) is never executed; update the
submit-success path in the login page to ensure navigateTo is invoked regardless
of refreshUser outcome by moving navigateTo into a finally block or by catching
errors from refreshUser (call refreshUser() in a try/catch and log/ignore
errors) and then calling navigateTo(submission.data.redirectTo, { external: true
}) unconditionally; reference refreshUser and navigateTo in the login.vue
success handling to locate and change the flow.

---

Nitpick comments:
In `@apps/docs/docs/forms/server-validation.md`:
- Around line 247-387: Add a second SvelteKit tab demonstrating the client-side
useForm + useAuth pattern to match Next.js/Nuxt: create a "[SvelteKit — useForm
client]" example that imports useAuth from '@holo-js/auth/sveltekit/client' and
useForm from '@holo-js/adapter-sveltekit/client', initializes the form with
loginForm and csrf/initialValues, implements submitter which posts formData and
calls refreshUser() (or auth.refreshUser()) on success, and binds form.values.*,
uses form.fields.email.onInput/onBlur etc and form.submit() in the template so
readers can see the same client-side flow; ensure the tab mirrors the symbols
used in the other examples (useAuth, useForm, loginForm, submitter, refreshUser,
form.fields.*, form.values.*, form.submit).

In `@packages/adapter-nuxt/tests/package.test.ts`:
- Around line 29-32: The assertions in package.test.ts are brittle because they
depend on exact formatting like quote style and the literal "as string"; update
the assertions to use pattern matching (regex) against moduleEntry and
runtimeEntry instead of exact substrings: replace the toContain checks for
import('@holo-js/storage') and import('@holo-js/storage-s3' as string) with
regex-based expectations (e.g., expecting an import of `@holo-js/storage` or
`@holo-js/storage-s3` regardless of quotes or whitespace), and keep the negative
check for "@holo-js/forms" but use a regex/contains check that is tolerant to
formatting changes; update references to moduleEntry and runtimeEntry
accordingly (use expect(...).toMatch or expect.stringMatching).
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b0dace85-f9a1-407d-bb5a-b86191a93130

📥 Commits

Reviewing files that changed from the base of the PR and between a2cd999 and 80974ba.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (70)
  • apps/blog-next/app/auth-nav.tsx
  • apps/blog-next/app/layout.tsx
  • apps/blog-next/app/login/page.tsx
  • apps/blog-next/app/verify-email/page.tsx
  • apps/blog-next/proxy.ts
  • apps/blog-next/tests/run.mjs
  • apps/blog-nuxt/app/app.vue
  • apps/blog-nuxt/app/middleware/auth-only.global.ts
  • apps/blog-nuxt/app/middleware/guest-only.global.ts
  • apps/blog-nuxt/app/pages/login.vue
  • apps/blog-nuxt/app/pages/register.vue
  • apps/blog-nuxt/app/pages/verify-email.vue
  • apps/blog-nuxt/tests/run.mjs
  • apps/blog-sveltekit/src/hooks.server.ts
  • apps/blog-sveltekit/src/routes/+layout.server.ts
  • apps/blog-sveltekit/src/routes/+layout.svelte
  • apps/blog-sveltekit/src/routes/login/+page.svelte
  • apps/blog-sveltekit/src/routes/register/+page.svelte
  • apps/blog-sveltekit/src/routes/verify-email/+page.svelte
  • apps/blog-sveltekit/tests/run.mjs
  • apps/docs/docs/auth/current-auth-client.md
  • apps/docs/docs/auth/index.md
  • apps/docs/docs/forms/framework-integration.md
  • apps/docs/docs/forms/server-validation.md
  • packages/adapter-next/package.json
  • packages/adapter-next/src/client.ts
  • packages/adapter-next/src/request-context.ts
  • packages/adapter-next/src/server.ts
  • packages/adapter-next/tests/adapter.type.test.ts
  • packages/adapter-next/tests/client.test.ts
  • packages/adapter-next/tests/package.test.ts
  • packages/adapter-next/tests/server.test.ts
  • packages/adapter-next/tsconfig.json
  • packages/adapter-next/tsup.config.ts
  • packages/adapter-next/vitest.config.ts
  • packages/adapter-nuxt/package.json
  • packages/adapter-nuxt/src/module.ts
  • packages/adapter-nuxt/src/runtime/composables/auth.d.ts
  • packages/adapter-nuxt/src/runtime/server/protection.d.ts
  • packages/adapter-nuxt/src/runtime/shims.d.ts
  • packages/adapter-nuxt/tests/client.test.ts
  • packages/adapter-nuxt/tests/module.test.ts
  • packages/adapter-nuxt/tests/package.test.ts
  • packages/adapter-nuxt/tests/protection.test.ts
  • packages/adapter-nuxt/tests/setup.test.ts
  • packages/adapter-nuxt/tsconfig.json
  • packages/adapter-nuxt/vitest.config.ts
  • packages/adapter-sveltekit/package.json
  • packages/adapter-sveltekit/src/client.ts
  • packages/adapter-sveltekit/tests/adapter.type.test.ts
  • packages/adapter-sveltekit/tests/client.test.ts
  • packages/adapter-sveltekit/tests/package.test.ts
  • packages/adapter-sveltekit/tests/server.test.ts
  • packages/adapter-sveltekit/tsconfig.json
  • packages/adapter-sveltekit/tsup.config.ts
  • packages/adapter-sveltekit/vitest.config.ts
  • packages/auth/package.json
  • packages/auth/src/next/client.ts
  • packages/auth/src/next/request-context.ts
  • packages/auth/src/next/server.ts
  • packages/auth/src/nuxt-shim.d.ts
  • packages/auth/src/nuxt.ts
  • packages/auth/src/nuxt/server.ts
  • packages/auth/src/sveltekit/client.ts
  • packages/auth/src/sveltekit/server.ts
  • packages/auth/tests/contracts.type.test.ts
  • packages/auth/tests/docs-smoke.test.ts
  • packages/auth/tests/package.test.ts
  • packages/auth/tsup.config.ts
  • tests/example-app-auth-flow.mjs
💤 Files with no reviewable changes (21)
  • packages/adapter-nuxt/src/runtime/server/protection.d.ts
  • packages/adapter-next/tests/client.test.ts
  • packages/adapter-sveltekit/tsup.config.ts
  • packages/adapter-next/package.json
  • packages/adapter-sveltekit/package.json
  • packages/adapter-nuxt/src/runtime/composables/auth.d.ts
  • packages/adapter-nuxt/tsconfig.json
  • packages/adapter-nuxt/tests/client.test.ts
  • packages/adapter-nuxt/package.json
  • packages/adapter-next/tests/server.test.ts
  • packages/adapter-nuxt/vitest.config.ts
  • packages/adapter-sveltekit/tests/server.test.ts
  • packages/adapter-next/tsup.config.ts
  • packages/adapter-sveltekit/tsconfig.json
  • packages/adapter-next/src/server.ts
  • packages/adapter-next/vitest.config.ts
  • packages/adapter-sveltekit/vitest.config.ts
  • packages/adapter-next/tsconfig.json
  • packages/adapter-nuxt/tests/protection.test.ts
  • packages/adapter-nuxt/src/module.ts
  • packages/adapter-sveltekit/src/client.ts

Comment thread apps/blog-next/tests/run.mjs
Comment thread apps/blog-sveltekit/tests/run.mjs
Comment thread packages/auth/src/next/client.ts
Comment thread packages/auth/src/next/server.ts
Comment thread packages/auth/src/nuxt/server.ts
@cobraprojects
Copy link
Copy Markdown
Owner Author

@CodeRabbit review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 7, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cobraprojects cobraprojects merged commit 1a86137 into main May 7, 2026
1 check passed
@cobraprojects cobraprojects deleted the move-auth-helpers-to-auth-packages branch May 7, 2026 16:59
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.

1 participant