Skip to content

fix(nextjs): lazy-load edge WASM and make next/server resolvable when externalized#59

Merged
msalvatti merged 1 commit into
mainfrom
fix/nextjs-lazy-wasm-and-server-resolution
Jul 2, 2026
Merged

fix(nextjs): lazy-load edge WASM and make next/server resolvable when externalized#59
msalvatti merged 1 commit into
mainfrom
fix/nextjs-lazy-wasm-and-server-resolution

Conversation

@msalvatti

Copy link
Copy Markdown
Member

Problem

A Next 16 app that installs @bymax-one/rust-auth and marks it
serverExternalPackages (required today because the /nextjs subpath ships a
WASM verifier) could not next build, and could not import the /nextjs barrel
under Node's ESM loader:

  1. Eager WASM. src/nextjs/jwt.ts imported the WASM glue at module load; the
    glue self-initializes (__wbindgen_start) on import, which Next's page-data
    collection sandbox cannot execute. Importing the /nextjs barrel therefore had
    a WASM side effect at load time.
  2. next/server unresolvable. proxy.ts and handlers.ts imported the bare
    subpath next/server. next ships no exports map, so Node's native ESM
    resolver (used for externalized packages) cannot resolve an extensionless
    subpath — it fails with ERR_MODULE_NOT_FOUND … next/server and the hint
    "Did you mean to import next/server.js?". (The CJS require path worked; only
    the ESM .mjs build broke, which is what a type: module Next 16 app loads.)

Fix

  • Lazy WASM: load the glue via a memoized dynamic import() on first use
    inside the jwt helpers. Importing the module — or the /nextjs barrel — now has
    no WASM side effect. decodeJwtToken becomes async to match verifyJwtToken
    (both await the shared, single-instance loader). No other public export changes.
  • Resolvable next/server: import the fully-specified next/server.js in the
    proxy and route handlers, and keep it external in the tsup build. This resolves
    under Node's raw ESM loader while remaining resolvable under bundlers
    (Next/Turbopack/webpack) and Vitest. next stays an optional peer dependency.

Verification

  • Library gates green: typecheck, lint, test (48 passing), build.
  • Verified against the rust-auth-example console (Next 16.2.10): next build
    succeeds (edge middleware + route handlers), the .wasm is emitted as an
    on-demand code-split chunk, and the web unit suite passes (123 tests).
  • Backward-compatible for other consumers: the sibling nest-auth-example depends
    on the separate @bymax-one/nest-auth package, not this one; the change keeps
    the same public export names and both ESM and CJS builds.

… externalized

The `/nextjs` subpath eagerly imported the WASM glue at module load, whose
top-level `__wbindgen_start` a Next build's page-data collection cannot run, and
imported `next/server` as an extensionless subpath, which Node's native ESM
resolver cannot resolve because `next` ships no `exports` map (so an externalized
`serverExternalPackages` consumer failed `next build`).

- Defer the WASM to a memoized dynamic `import()` on first use inside the jwt
  helpers; importing the module (or the barrel) now has no WASM side effect.
  `decodeJwtToken` becomes async to match `verifyJwtToken`.
- Import `next/server.js` (fully specified) in the proxy and route handlers so the
  built ESM/CJS resolve under Node's raw loader; unchanged under bundlers/Vitest.
  Keep `next/server.js` external in the tsup build.
Copilot AI review requested due to automatic review settings July 2, 2026 21:29

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes Next 16 “externalized package” compatibility for the ./nextjs subpath by (1) removing eager WASM initialization at module-load time and (2) making next/server resolvable under Node’s native ESM resolver when serverExternalPackages is used.

Changes:

  • Lazy-load the edge WASM glue via a memoized dynamic import() and use it inside JWT helpers.
  • Switch Next route/middleware helpers to import next/server.js (fully specified ESM subpath).
  • Update tsup externals and Vitest coverage for the now-async decodeJwtToken.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/rust-auth/tsup.config.ts Externalizes next/server.js so the fully-specified import stays external in the build output.
packages/rust-auth/tests/nextjs.test.ts Updates decode tests to await decodeJwtToken after it became async.
packages/rust-auth/src/nextjs/proxy.ts Imports NextResponse / NextRequest from next/server.js to support Node ESM resolution when externalized.
packages/rust-auth/src/nextjs/jwt.ts Introduces memoized dynamic WASM loader; makes decodeJwtToken async and loads wasm on first use.
packages/rust-auth/src/nextjs/handlers.ts Imports NextResponse / NextRequest from next/server.js for the same ESM-resolution reason as proxy.ts.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/rust-auth/src/nextjs/jwt.ts
Comment thread packages/rust-auth/tests/nextjs.test.ts
@msalvatti

Copy link
Copy Markdown
Member Author

Both threads are intentional/minor:

  • decodeJwtToken is now async — required by the lazy WASM init (the glue no longer self-executes at import). Its pair verifyJwtToken was already async; no current consumer calls the sync decode, and the export name is unchanged.
  • The malformed-token test decoding twice is negligible async work; kept for the distinct assertions.

Resolving.

@msalvatti
msalvatti merged commit 70e73c8 into main Jul 2, 2026
26 checks passed
@msalvatti
msalvatti deleted the fix/nextjs-lazy-wasm-and-server-resolution branch July 2, 2026 21:34

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.

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.

2 participants