Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/GETTING-STARTED.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
<AutterErrorBoundary fallback={<ErrorPage />}>
Expand Down
2 changes: 1 addition & 1 deletion docs/INTEGRATIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ initAutterBrowser({
```

React render errors: wrap with `<AutterErrorBoundary>` (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`).

Expand Down
21 changes: 17 additions & 4 deletions packages/runtime-next/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down Expand Up @@ -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",
Expand All @@ -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`.
12 changes: 11 additions & 1 deletion packages/runtime-next/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": [
Expand All @@ -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",
Expand Down
25 changes: 25 additions & 0 deletions packages/runtime-next/src/client.ts
Original file line number Diff line number Diff line change
@@ -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";
68 changes: 5 additions & 63 deletions packages/runtime-next/src/index.ts
Original file line number Diff line number Diff line change
@@ -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<Response>;
} {
return { POST: createBrowserRelayFetchHandler(options) };
}
export * from "./server.js";
52 changes: 52 additions & 0 deletions packages/runtime-next/src/server.ts
Original file line number Diff line number Diff line change
@@ -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<Response>;
} {
return { POST: createBrowserRelayFetchHandler(options) };
}
21 changes: 21 additions & 0 deletions packages/runtime-next/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -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";' },
},
]);
Loading