Skip to content

Add Next.js fmBridge support#251

Merged
eluce2 merged 3 commits intomainfrom
t3code/214d37c1
May 7, 2026
Merged

Add Next.js fmBridge support#251
eluce2 merged 3 commits intomainfrom
t3code/214d37c1

Conversation

@eluce2
Copy link
Copy Markdown
Collaborator

@eluce2 eluce2 commented May 7, 2026

Summary

  • Add @proofkit/webviewer/nextjs for Next.js App Router and Pages Router dev integration.
  • Provide FmBridgeScript, getFmBridgeScriptProps(), and ResolvedFmBridgeScript for beforeInteractive injection or inline fallback.
  • Refactor fmBridge internals to reuse script URL and fallback runtime builders.
  • Update docs for Vite and Next.js usage, plus package exports and peer deps.
  • Add tests for production no-op, connected-file script src, fallback behavior, and rendered props.

Testing

  • pnpm run ci not run here.
  • Covered by packages/webviewer/tests/nextjs.test.ts.
  • Added package export/type checks via packages/webviewer/package.json and packages/webviewer/tsconfig.json updates.

Summary by CodeRabbit

  • New Features

    • Added Next.js fmBridge helper for local development integration with FileMaker, supporting both App Router and Pages Router architectures.
    • Enables using FileMaker functionality during development without rebuild or upload cycles.
  • Documentation

    • Expanded fmBridge documentation with Next.js-specific setup instructions and configuration options.
    • Updated package guidance to reference framework-specific development bridge solutions.

- expose `@proofkit/webviewer/nextjs`
- add App Router and Pages Router bridge helpers
- update docs and package exports
@vercel
Copy link
Copy Markdown

vercel Bot commented May 7, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
proofkit-docs Ready Ready Preview May 7, 2026 5:51pm

Request Review

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 7, 2026

🦋 Changeset detected

Latest commit: 5b4d168

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@proofkit/webviewer Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 7, 2026

Review Change Stack

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

This PR adds Next.js-specific FM Bridge support for local development. It extracts core URL/script building utilities, introduces Next.js components (FmBridgeScript, ResolvedFmBridgeScript) for App and Pages Router integration, updates package exports and build configuration, and includes comprehensive test coverage and documentation.

Changes

Next.js FM Bridge Implementation

Layer / File(s) Summary
Type Interfaces and Ambient Declarations
packages/webviewer/src/nextjs.ts, packages/webviewer/src/nextjs-runtime.d.ts
Defines FmBridgeOptions, NextFmBridgeScriptProps, and ResolvedFmBridgeScriptProps interfaces. Provides ambient module declarations for React and Next.js Script component types.
FM Bridge Core Utilities
packages/webviewer/src/fm-bridge.ts
Extracts buildMockScriptUrl helper to centralize fm-mock.js script URL construction with fileName, wsUrl, and optional debug query parameters. Refactors inline error script templates into buildNoConnectedFilesRuntimeScript function.
Package Exports and Module Entry Points
packages/webviewer/package.json, packages/webviewer/vite.config.ts
Adds ./nextjs subpath export with ESM and CommonJS entry points. Declares react and next as peer dependencies. Updates Vite configuration to include ./src/nextjs.ts entry point and treat react/next/script as external.
TypeScript Compilation Configuration
packages/webviewer/tsconfig.json
Expands include glob to cover src/*.tsx and src/*.d.ts files alongside src/*.ts.
Next.js Runtime Components
packages/webviewer/src/nextjs.ts
Implements getFmBridgeScriptProps async resolver that returns null in production or resolved script props in development (either external beforeInteractive src URL or fallback inline error script). Exports ResolvedFmBridgeScript and FmBridgeScript wrapper components for App/Pages Router integration.
Test Coverage
packages/webviewer/tests/nextjs.test.ts
Comprehensive Vitest suite verifying production safety, connected-file URL generation with correct parameters, disconnected-file fallback behavior, and component rendering for all public functions.
Documentation and Release
.changeset/nextjs-fm-bridge.md, apps/docs/content/docs/webviewer/fm-bridge.mdx, apps/docs/content/docs/webviewer/package.mdx
Documents Next.js FM Bridge setup for App Router (via FmBridgeScript in layouts) and Pages Router (via getInitialProps and ResolvedFmBridgeScript). Clarifies development-only behavior and lists supported frameworks. Records patch-level changeset.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add Next.js fmBridge support' directly and concisely summarizes the main change: introducing Next.js integration for the fmBridge development flow.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 t3code/214d37c1

Comment @coderabbitai help to get the list of available commands and usage tips.

- bump rolldown and related oxc/rolldown packages
- refresh webviewer next/react entries
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 7, 2026

Open in StackBlitz

@proofkit/better-auth

pnpm add https://pkg.pr.new/proofsh/proofkit/@proofkit/better-auth@251

@proofkit/cli

pnpm add https://pkg.pr.new/proofsh/proofkit/@proofkit/cli@251

create-proofkit

pnpm add https://pkg.pr.new/proofsh/proofkit/create-proofkit@251

@proofkit/fmdapi

pnpm add https://pkg.pr.new/proofsh/proofkit/@proofkit/fmdapi@251

@proofkit/fmodata

pnpm add https://pkg.pr.new/proofsh/proofkit/@proofkit/fmodata@251

@proofkit/typegen

pnpm add https://pkg.pr.new/proofsh/proofkit/@proofkit/typegen@251

@proofkit/webviewer

pnpm add https://pkg.pr.new/proofsh/proofkit/@proofkit/webviewer@251

commit: 7d2a915

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: 2

🧹 Nitpick comments (3)
packages/webviewer/tests/nextjs.test.ts (1)

169-193: ⚡ Quick win

Convert .then() chains to async/await in the ResolvedFmBridgeScript suite

Both tests in this describe block use .then() on nextjsModulePromise instead of async/await, inconsistent with the pattern used in the rest of the test file and the project's test coding guidelines.

♻️ Proposed refactor
-  it("renders null when script props are null", () => {
-    return nextjsModulePromise.then(({ ResolvedFmBridgeScript }) => {
-      expect(ResolvedFmBridgeScript({ script: null })).toBeNull();
-    });
-  });
+  it("renders null when script props are null", async () => {
+    const { ResolvedFmBridgeScript } = await nextjsModulePromise;
+    expect(ResolvedFmBridgeScript({ script: null })).toBeNull();
+  });

-  it("renders next/script with provided props", () => {
-    return nextjsModulePromise.then(({ ResolvedFmBridgeScript }) => {
-      expect(
-        ResolvedFmBridgeScript({
-          script: {
-            strategy: "beforeInteractive",
-            src: "http://localhost:1365/fm-mock.js?fileName=Contacts&wsUrl=ws%3A%2F%2Flocalhost%3A1365%2Fws",
-          },
-        }),
-      ).toMatchObject({
-        props: {
-          strategy: "beforeInteractive",
-          src: "http://localhost:1365/fm-mock.js?fileName=Contacts&wsUrl=ws%3A%2F%2Flocalhost%3A1365%2Fws",
-        },
-      });
-    });
-  });
+  it("renders next/script with provided props", async () => {
+    const { ResolvedFmBridgeScript } = await nextjsModulePromise;
+    expect(
+      ResolvedFmBridgeScript({
+        script: {
+          strategy: "beforeInteractive",
+          src: "http://localhost:1365/fm-mock.js?fileName=Contacts&wsUrl=ws%3A%2F%2Flocalhost%3A1365%2Fws",
+        },
+      }),
+    ).toMatchObject({
+      props: {
+        strategy: "beforeInteractive",
+        src: "http://localhost:1365/fm-mock.js?fileName=Contacts&wsUrl=ws%3A%2F%2Flocalhost%3A1365%2Fws",
+      },
+    });
+  });

As per coding guidelines: "Use async/await syntax instead of promise chains for better readability."

🤖 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/webviewer/tests/nextjs.test.ts` around lines 169 - 193, Update the
two tests in the "ResolvedFmBridgeScript" suite to use async/await instead of
.then() on nextjsModulePromise: mark each test callback async, await
nextjsModulePromise to get ResolvedFmBridgeScript, and then perform the same
assertions (removing the returned promise chain). Keep the same inputs and
expectations for ResolvedFmBridgeScript({ script: ... }) and for the null case.
packages/webviewer/src/nextjs.ts (2)

64-70: ⚡ Quick win

Replace the double type assertion with a properly typed cast

script as unknown as Record<string, unknown> completely bypasses TypeScript's prop-type checking for the Script component. Per coding guidelines, prefer type narrowing over type assertions. The root cause is likely that NextFmBridgeScriptProps isn't declared to extend next/script's ScriptProps. Aligning the interface with the actual component props (or using ComponentPropsWithoutRef<typeof Script> as the target type) eliminates the need for the double cast.

♻️ Proposed refactor
-import Script from "next/script";
+import Script, { type ScriptProps } from "next/script";
-export interface NextFmBridgeScriptProps {
-  strategy: "beforeInteractive";
-  src?: string;
-  id?: string;
-  dangerouslySetInnerHTML?: {
-    __html: string;
-  };
-}
+export type NextFmBridgeScriptProps = Pick<ScriptProps,
+  "strategy" | "src" | "id" | "dangerouslySetInnerHTML"
+>;

Then the assertion simplifies to a single, narrower cast:

-  return createElement(Script, script as unknown as Record<string, unknown>);
+  return createElement(Script, script as ScriptProps);

As per coding guidelines: "Leverage TypeScript's type narrowing instead of type assertions."

🤖 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/webviewer/src/nextjs.ts` around lines 64 - 70, The component
ResolvedFmBridgeScript currently uses a double-cast "script as unknown as
Record<string, unknown>" which bypasses TypeScript checks; update the type for
the incoming prop (ResolvedFmBridgeScriptProps / NextFmBridgeScriptProps) to
extend next/script's ScriptProps or use ComponentPropsWithoutRef<typeof Script>
as the proper script type, then replace the double assertion with a single,
correctly-typed cast (or no cast if you make the prop already the correct type)
when calling createElement(Script, ...), ensuring Script receives the correct
prop type.

64-79: 💤 Low value

Add explicit return type annotations to exported component functions

Neither ResolvedFmBridgeScript nor FmBridgeScript declare a return type. For exported React components, the return type carries useful documentation value and prevents silent regressions if the implementation changes.

♻️ Proposed annotations
-export const ResolvedFmBridgeScript = ({ script }: ResolvedFmBridgeScriptProps) => {
+export const ResolvedFmBridgeScript = ({ script }: ResolvedFmBridgeScriptProps): React.ReactElement | null => {
-export const FmBridgeScript = async (options: FmBridgeOptions = {}) => {
+export const FmBridgeScript = async (options: FmBridgeOptions = {}): Promise<React.ReactElement | null> => {

As per coding guidelines: "Use explicit types for function parameters and return values when they enhance clarity."

🤖 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/webviewer/src/nextjs.ts` around lines 64 - 79, Both exported
functions lack explicit return types; add explicit React return annotations to
prevent regressions: annotate ResolvedFmBridgeScript signature to return
React.ReactElement | null and annotate the async FmBridgeScript to return
Promise<React.ReactElement | null>. Ensure any necessary React types are
imported and update the function signatures for ResolvedFmBridgeScript and
FmBridgeScript accordingly (keeping parameters ResolvedFmBridgeScriptProps and
FmBridgeOptions).
🤖 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/webviewer/package.json`:
- Around line 59-62: The package.json currently lists next and react under
"peerDependencies" which causes spurious warnings for consumers that don't use
the ./nextjs subpath; add a top-level "peerDependenciesMeta" object in
package.json with "next": { "optional": true } and "react": { "optional": true }
to mark those peers as optional so package managers won't warn for Vite-only
consumers, ensuring the change is valid JSON and coexists with the existing
"peerDependencies" entry.

In `@packages/webviewer/src/nextjs.ts`:
- Around line 31-62: Wrap the await discoverConnectedFileName(baseUrl) call
inside getFmBridgeScriptProps in a try-catch so any thrown errors are swallowed
and treated as "no connected file" (i.e., leave resolvedFileName undefined/null)
so the function falls through to the existing fallback branch that returns
strategy: "beforeInteractive" with id: fallbackScriptId and
dangerouslySetInnerHTML using buildNoConnectedFilesRuntimeScript(baseUrl); keep
the rest of the logic (baseUrl, wsUrl, debug, buildMockScriptUrl) unchanged and
don't rethrow the error.

---

Nitpick comments:
In `@packages/webviewer/src/nextjs.ts`:
- Around line 64-70: The component ResolvedFmBridgeScript currently uses a
double-cast "script as unknown as Record<string, unknown>" which bypasses
TypeScript checks; update the type for the incoming prop
(ResolvedFmBridgeScriptProps / NextFmBridgeScriptProps) to extend next/script's
ScriptProps or use ComponentPropsWithoutRef<typeof Script> as the proper script
type, then replace the double assertion with a single, correctly-typed cast (or
no cast if you make the prop already the correct type) when calling
createElement(Script, ...), ensuring Script receives the correct prop type.
- Around line 64-79: Both exported functions lack explicit return types; add
explicit React return annotations to prevent regressions: annotate
ResolvedFmBridgeScript signature to return React.ReactElement | null and
annotate the async FmBridgeScript to return Promise<React.ReactElement | null>.
Ensure any necessary React types are imported and update the function signatures
for ResolvedFmBridgeScript and FmBridgeScript accordingly (keeping parameters
ResolvedFmBridgeScriptProps and FmBridgeOptions).

In `@packages/webviewer/tests/nextjs.test.ts`:
- Around line 169-193: Update the two tests in the "ResolvedFmBridgeScript"
suite to use async/await instead of .then() on nextjsModulePromise: mark each
test callback async, await nextjsModulePromise to get ResolvedFmBridgeScript,
and then perform the same assertions (removing the returned promise chain). Keep
the same inputs and expectations for ResolvedFmBridgeScript({ script: ... }) and
for the null case.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5b104c27-1ea2-4e91-b0f8-c8233b3ba222

📥 Commits

Reviewing files that changed from the base of the PR and between 105e1d5 and 7d2a915.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (10)
  • .changeset/nextjs-fm-bridge.md
  • apps/docs/content/docs/webviewer/fm-bridge.mdx
  • apps/docs/content/docs/webviewer/package.mdx
  • packages/webviewer/package.json
  • packages/webviewer/src/fm-bridge.ts
  • packages/webviewer/src/nextjs-runtime.d.ts
  • packages/webviewer/src/nextjs.ts
  • packages/webviewer/tests/nextjs.test.ts
  • packages/webviewer/tsconfig.json
  • packages/webviewer/vite.config.ts

Comment on lines +59 to +62
"peerDependencies": {
"next": ">=13.0.0",
"react": ">=18.0.0"
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add peerDependenciesMeta to mark next and react as optional peers.

next and react are only consumed by the ./nextjs subpath export. Without peerDependenciesMeta, every installer of @proofkit/webviewer — including Vite-only consumers — will receive a spurious peer-dependency warning about missing next and react.

peerDependenciesMeta lets you mark a peer dependency as optional so npm will not emit a warning if the package is not installed on the host. When set to true, the selected peer dependency will be marked as optional by the package manager; therefore, consumers omitting it will no longer be reported as an error.

🛠️ Proposed fix
  "peerDependencies": {
    "next": ">=13.0.0",
    "react": ">=18.0.0"
  },
+ "peerDependenciesMeta": {
+   "next": {
+     "optional": true
+   },
+   "react": {
+     "optional": true
+   }
+ },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"peerDependencies": {
"next": ">=13.0.0",
"react": ">=18.0.0"
},
"peerDependencies": {
"next": ">=13.0.0",
"react": ">=18.0.0"
},
"peerDependenciesMeta": {
"next": {
"optional": true
},
"react": {
"optional": 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 `@packages/webviewer/package.json` around lines 59 - 62, The package.json
currently lists next and react under "peerDependencies" which causes spurious
warnings for consumers that don't use the ./nextjs subpath; add a top-level
"peerDependenciesMeta" object in package.json with "next": { "optional": true }
and "react": { "optional": true } to mark those peers as optional so package
managers won't warn for Vite-only consumers, ensuring the change is valid JSON
and coexists with the existing "peerDependencies" entry.

Comment thread packages/webviewer/src/nextjs.ts
- fall back to inline script props when discovery throws
- add test for fetch rejection path
@eluce2 eluce2 merged commit 3d8f966 into main May 7, 2026
8 of 10 checks passed
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