diff --git a/docs/GETTING-STARTED.md b/docs/GETTING-STARTED.md
index e518e5c..7394266 100644
--- a/docs/GETTING-STARTED.md
+++ b/docs/GETTING-STARTED.md
@@ -172,7 +172,7 @@ setUser("u_8f2k1"); // opaque id — never a
```
React render errors don't reach `window.onerror` — add the boundary
-(exported from `@autter/runtime-next`, works in any React app):
+(exported from `@autter/runtime-next/client`, works in any React app):
```tsx
}>
diff --git a/docs/INTEGRATIONS.md b/docs/INTEGRATIONS.md
index 3014b35..14f8f10 100644
--- a/docs/INTEGRATIONS.md
+++ b/docs/INTEGRATIONS.md
@@ -39,7 +39,7 @@ initAutterBrowser({
```
React render errors: wrap with `` (from
-`@autter/runtime-next`, works in any React app). Vue/Svelte/Angular: call
+`@autter/runtime-next/client`, works in any React app). Vue/Svelte/Angular: call
`captureException(err)` from the framework's error hook
(`app.config.errorHandler`, `handleError`, `ErrorHandler`).
diff --git a/packages/runtime-next/README.md b/packages/runtime-next/README.md
index 4c1b155..ccc4a1a 100644
--- a/packages/runtime-next/README.md
+++ b/packages/runtime-next/README.md
@@ -9,7 +9,15 @@ same-origin relay route, and a React error boundary.
npm install @autter/runtime-next
```
-Set `AUTTER_RUNTIME_KEY` in your environment. Then three small files:
+Set `AUTTER_RUNTIME_KEY` in your environment. Then three small files.
+
+The package has two entry points — this split is what keeps the Node
+OpenTelemetry SDK out of your browser bundle:
+
+- `@autter/runtime-next` (or `@autter/runtime-next/server`) — server only:
+ `instrumentation.ts`, route handlers, server components.
+- `@autter/runtime-next/client` — client components: browser tracker +
+ error boundary. Never imports Node code.
**1. `instrumentation.ts`** — server tracing/errors:
@@ -41,7 +49,7 @@ export const { POST } = createAutterRelayRoute({
```tsx
"use client";
-import { initAutterBrowser, AutterErrorBoundary } from "@autter/runtime-next";
+import { initAutterBrowser, AutterErrorBoundary } from "@autter/runtime-next/client";
initAutterBrowser({
endpoint: "/api/autter-runtime",
@@ -62,5 +70,10 @@ React render errors don't reach `window.onerror` — the boundary reports
them via `captureException` and then renders your fallback.
Also re-exported for convenience: `captureException`, `captureMessage`,
-`trackEvent`, `setUser`, `setContext`, `flush` (browser) and
-`captureServerException`, `captureServerMessage` (server).
+`trackEvent`, `setUser`, `setContext`, `flush` (from
+`@autter/runtime-next/client`) and `captureServerException`,
+`captureServerMessage` (from the root / `@autter/runtime-next/server`).
+
+> Importing the root entry from a client component pulls the Node OTel SDK
+> into the browser bundle and fails the build (`fs` cannot be resolved).
+> Client code must always use `@autter/runtime-next/client`.
diff --git a/packages/runtime-next/package.json b/packages/runtime-next/package.json
index 6058c40..73c13a7 100644
--- a/packages/runtime-next/package.json
+++ b/packages/runtime-next/package.json
@@ -12,6 +12,16 @@
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"require": "./dist/index.cjs"
+ },
+ "./server": {
+ "types": "./dist/server.d.ts",
+ "import": "./dist/server.js",
+ "require": "./dist/server.cjs"
+ },
+ "./client": {
+ "types": "./dist/client.d.ts",
+ "import": "./dist/client.js",
+ "require": "./dist/client.cjs"
}
},
"files": [
@@ -23,7 +33,7 @@
"directory": "packages/runtime-next"
},
"scripts": {
- "build": "tsup src/index.ts --format esm,cjs --dts --target node20 --external react --clean"
+ "build": "tsup"
},
"dependencies": {
"@autter/runtime-browser": "^1.0.0",
diff --git a/packages/runtime-next/src/client.ts b/packages/runtime-next/src/client.ts
new file mode 100644
index 0000000..21e2a5d
--- /dev/null
+++ b/packages/runtime-next/src/client.ts
@@ -0,0 +1,25 @@
+/**
+ * Client half of @autter/runtime-next — import from `@autter/runtime-next/client`
+ * in client components only. Contains zero Node code, so nothing OTel-shaped
+ * ever reaches the browser bundle.
+ *
+ * "use client";
+ * import { initAutterBrowser, AutterErrorBoundary } from "@autter/runtime-next/client";
+ *
+ * initAutterBrowser({ endpoint: "/api/autter-runtime", service: "web-app" });
+ */
+
+export {
+ initAutterBrowser,
+ captureException,
+ captureMessage,
+ trackEvent,
+ setUser,
+ setContext,
+ flush,
+ type AutterSeverity,
+} from "@autter/runtime-browser";
+export {
+ AutterErrorBoundary,
+ type AutterErrorBoundaryProps,
+} from "./error-boundary.js";
diff --git a/packages/runtime-next/src/index.ts b/packages/runtime-next/src/index.ts
index 87ce212..8fd16ee 100644
--- a/packages/runtime-next/src/index.ts
+++ b/packages/runtime-next/src/index.ts
@@ -1,68 +1,10 @@
/**
* @autter/runtime-next — one-command Autter Runtime for Next.js.
*
- * 1. `instrumentation.ts` (server tracing):
- *
- * export async function register() {
- * if (process.env.NEXT_RUNTIME === "nodejs") {
- * const { registerAutter } = await import("@autter/runtime-next");
- * registerAutter({
- * apiKey: process.env.AUTTER_RUNTIME_KEY!,
- * service: "web-app",
- * release: process.env.GIT_SHA,
- * });
- * }
- * }
- *
- * 2. `app/api/autter-runtime/route.ts` (browser relay):
- *
- * import { createAutterRelayRoute } from "@autter/runtime-next";
- * export const { POST } = createAutterRelayRoute({
- * apiKey: process.env.AUTTER_RUNTIME_KEY!,
- * });
- *
- * 3. A client component (browser tracker + boundary):
- *
- * "use client";
- * import { initAutterBrowser, AutterErrorBoundary } from "@autter/runtime-next";
- * initAutterBrowser({ endpoint: "/api/autter-runtime", service: "web-app" });
+ * The root entry is SERVER-ONLY (`instrumentation.ts`, route handlers).
+ * Client components must import from `@autter/runtime-next/client` —
+ * importing the root from client code would pull the Node OpenTelemetry
+ * SDK into the browser bundle and break the build.
*/
-import {
- createBrowserRelayFetchHandler,
- initAutterServer,
- type AutterServer,
- type AutterServerOptions,
- type RelayOptions,
-} from "@autter/runtime-node";
-
-export {
- initAutterBrowser,
- captureException,
- captureMessage,
- trackEvent,
- setUser,
- setContext,
- flush,
- type AutterSeverity,
-} from "@autter/runtime-browser";
-export {
- AutterErrorBoundary,
- type AutterErrorBoundaryProps,
-} from "./error-boundary.js";
-export {
- captureException as captureServerException,
- captureMessage as captureServerMessage,
-} from "@autter/runtime-node";
-
-/** Server OTel init for Next.js `instrumentation.ts`. */
-export function registerAutter(options: AutterServerOptions): AutterServer {
- return initAutterServer(options);
-}
-
-/** App Router relay route: `export const { POST } = createAutterRelayRoute({...})`. */
-export function createAutterRelayRoute(options: RelayOptions): {
- POST: (request: Request) => Promise;
-} {
- return { POST: createBrowserRelayFetchHandler(options) };
-}
+export * from "./server.js";
diff --git a/packages/runtime-next/src/server.ts b/packages/runtime-next/src/server.ts
new file mode 100644
index 0000000..c1e23a5
--- /dev/null
+++ b/packages/runtime-next/src/server.ts
@@ -0,0 +1,52 @@
+/**
+ * Server half of @autter/runtime-next. Safe to import anywhere Node runs:
+ * `instrumentation.ts`, route handlers, server components, server actions.
+ *
+ * 1. `instrumentation.ts` (server tracing):
+ *
+ * export async function register() {
+ * if (process.env.NEXT_RUNTIME === "nodejs") {
+ * const { registerAutter } = await import("@autter/runtime-next");
+ * registerAutter({
+ * apiKey: process.env.AUTTER_RUNTIME_KEY!,
+ * service: "web-app",
+ * release: process.env.GIT_SHA,
+ * });
+ * }
+ * }
+ *
+ * 2. `app/api/autter-runtime/route.ts` (browser relay):
+ *
+ * import { createAutterRelayRoute } from "@autter/runtime-next";
+ * export const { POST } = createAutterRelayRoute({
+ * apiKey: process.env.AUTTER_RUNTIME_KEY!,
+ * });
+ *
+ * Browser tracker + error boundary live in `@autter/runtime-next/client`.
+ */
+
+import {
+ createBrowserRelayFetchHandler,
+ initAutterServer,
+ type AutterServer,
+ type AutterServerOptions,
+ type RelayOptions,
+} from "@autter/runtime-node";
+
+export {
+ captureException as captureServerException,
+ captureMessage as captureServerMessage,
+} from "@autter/runtime-node";
+export type { AutterServer, AutterServerOptions, RelayOptions };
+
+/** Server OTel init for Next.js `instrumentation.ts`. */
+export function registerAutter(options: AutterServerOptions): AutterServer {
+ return initAutterServer(options);
+}
+
+/** App Router relay route: `export const { POST } = createAutterRelayRoute({...})`. */
+export function createAutterRelayRoute(options: RelayOptions): {
+ POST: (request: Request) => Promise;
+} {
+ return { POST: createBrowserRelayFetchHandler(options) };
+}
diff --git a/packages/runtime-next/tsup.config.ts b/packages/runtime-next/tsup.config.ts
new file mode 100644
index 0000000..92a0731
--- /dev/null
+++ b/packages/runtime-next/tsup.config.ts
@@ -0,0 +1,21 @@
+import { defineConfig } from "tsup";
+
+export default defineConfig([
+ {
+ entry: { index: "src/index.ts", server: "src/server.ts" },
+ format: ["esm", "cjs"],
+ dts: true,
+ target: "node20",
+ clean: true,
+ },
+ {
+ entry: { client: "src/client.ts" },
+ format: ["esm", "cjs"],
+ dts: true,
+ target: "es2019",
+ external: ["react"],
+ // Next.js needs the directive on the bundled file, or the error
+ // boundary can't be used from server-component trees.
+ banner: { js: '"use client";' },
+ },
+]);