@interfere/next 0.0.15-alpha.8 → 0.1.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -266
- package/dist/_virtual/_rolldown/runtime.mjs +13 -0
- package/dist/config.d.mts +11 -0
- package/dist/config.d.mts.map +1 -0
- package/dist/config.mjs +116 -0
- package/dist/config.mjs.map +1 -0
- package/dist/instrument-client.d.mts +2 -0
- package/dist/instrument-client.mjs +2 -0
- package/dist/internal/build/configure-build.d.mts +22 -0
- package/dist/internal/build/configure-build.d.mts.map +1 -0
- package/dist/internal/build/configure-build.mjs +87 -0
- package/dist/internal/build/configure-build.mjs.map +1 -0
- package/dist/internal/build/injected.d.mts +12 -0
- package/dist/internal/build/injected.d.mts.map +1 -0
- package/dist/internal/build/injected.mjs +9 -0
- package/dist/internal/build/injected.mjs.map +1 -0
- package/dist/internal/build/logger.d.mts +8 -0
- package/dist/internal/build/logger.d.mts.map +1 -0
- package/dist/internal/build/logger.mjs +44 -0
- package/dist/internal/build/logger.mjs.map +1 -0
- package/dist/internal/build/release/destinations/vercel.d.mts +6 -0
- package/dist/internal/build/release/destinations/vercel.d.mts.map +1 -0
- package/dist/internal/build/release/destinations/vercel.mjs +25 -0
- package/dist/internal/build/release/destinations/vercel.mjs.map +1 -0
- package/dist/internal/build/release/git.d.mts +4 -0
- package/dist/internal/build/release/git.d.mts.map +1 -0
- package/dist/internal/build/release/git.mjs +21 -0
- package/dist/internal/build/release/git.mjs.map +1 -0
- package/dist/internal/build/release/index.d.mts +7 -0
- package/dist/internal/build/release/index.d.mts.map +1 -0
- package/dist/internal/build/release/index.mjs +26 -0
- package/dist/internal/build/release/index.mjs.map +1 -0
- package/dist/internal/build/release/sources/github.d.mts +6 -0
- package/dist/internal/build/release/sources/github.d.mts.map +1 -0
- package/dist/internal/build/release/sources/github.mjs +15 -0
- package/dist/internal/build/release/sources/github.mjs.map +1 -0
- package/dist/internal/build/release/types.d.mts +11 -0
- package/dist/internal/build/release/types.d.mts.map +1 -0
- package/dist/internal/build/release/types.mjs +1 -0
- package/dist/internal/build/source-maps/discover.d.mts +14 -0
- package/dist/internal/build/source-maps/discover.d.mts.map +1 -0
- package/dist/internal/build/source-maps/discover.mjs +61 -0
- package/dist/internal/build/source-maps/discover.mjs.map +1 -0
- package/dist/internal/build/source-maps/index.d.mts +23 -0
- package/dist/internal/build/source-maps/index.d.mts.map +1 -0
- package/dist/internal/build/source-maps/index.mjs +33 -0
- package/dist/internal/build/source-maps/index.mjs.map +1 -0
- package/dist/internal/build/value-injection-loader.d.mts +10 -0
- package/dist/internal/build/value-injection-loader.d.mts.map +1 -0
- package/dist/internal/build/value-injection-loader.mjs +24 -0
- package/dist/internal/build/value-injection-loader.mjs.map +1 -0
- package/dist/internal/env.d.mts +15 -0
- package/dist/internal/env.d.mts.map +1 -0
- package/dist/internal/env.mjs +18 -0
- package/dist/internal/env.mjs.map +1 -0
- package/dist/internal/route/cors.d.mts +4 -0
- package/dist/internal/route/cors.d.mts.map +1 -0
- package/dist/internal/route/cors.mjs +15 -0
- package/dist/internal/route/cors.mjs.map +1 -0
- package/dist/internal/route/handle-get.d.mts +4 -0
- package/dist/internal/route/handle-get.d.mts.map +1 -0
- package/dist/internal/route/handle-get.mjs +15 -0
- package/dist/internal/route/handle-get.mjs.map +1 -0
- package/dist/internal/route/handle-post.d.mts +4 -0
- package/dist/internal/route/handle-post.d.mts.map +1 -0
- package/dist/internal/route/handle-post.mjs +105 -0
- package/dist/internal/route/handle-post.mjs.map +1 -0
- package/dist/internal/route/sw-script.d.mts +4 -0
- package/dist/internal/route/sw-script.d.mts.map +1 -0
- package/dist/internal/route/sw-script.mjs +32 -0
- package/dist/internal/route/sw-script.mjs.map +1 -0
- package/dist/internal/server/capture.d.mts +9 -0
- package/dist/internal/server/capture.d.mts.map +1 -0
- package/dist/internal/server/capture.mjs +46 -0
- package/dist/internal/server/capture.mjs.map +1 -0
- package/dist/internal/server/dedupe.d.mts +5 -0
- package/dist/internal/server/dedupe.d.mts.map +1 -0
- package/dist/internal/server/dedupe.mjs +11 -0
- package/dist/internal/server/dedupe.mjs.map +1 -0
- package/dist/internal/server/envelope.d.mts +14 -0
- package/dist/internal/server/envelope.d.mts.map +1 -0
- package/dist/internal/server/envelope.mjs +41 -0
- package/dist/internal/server/envelope.mjs.map +1 -0
- package/dist/internal/server/mechanisms.d.mts +7 -0
- package/dist/internal/server/mechanisms.d.mts.map +1 -0
- package/dist/internal/server/mechanisms.mjs +12 -0
- package/dist/internal/server/mechanisms.mjs.map +1 -0
- package/dist/internal/server/normalize-request.d.mts +7 -0
- package/dist/internal/server/normalize-request.d.mts.map +1 -0
- package/dist/internal/server/normalize-request.mjs +50 -0
- package/dist/internal/server/normalize-request.mjs.map +1 -0
- package/dist/internal/server/runtime.d.mts +14 -0
- package/dist/internal/server/runtime.d.mts.map +1 -0
- package/dist/internal/server/runtime.mjs +18 -0
- package/dist/internal/server/runtime.mjs.map +1 -0
- package/dist/internal/server/session.d.mts +11 -0
- package/dist/internal/server/session.d.mts.map +1 -0
- package/dist/internal/server/session.mjs +15 -0
- package/dist/internal/server/session.mjs.map +1 -0
- package/dist/internal/server/transport.d.mts +12 -0
- package/dist/internal/server/transport.d.mts.map +1 -0
- package/dist/internal/server/transport.mjs +17 -0
- package/dist/internal/server/transport.mjs.map +1 -0
- package/dist/internal/server/types.d.mts +17 -0
- package/dist/internal/server/types.d.mts.map +1 -0
- package/dist/internal/server/types.mjs +1 -0
- package/dist/provider.d.mts +2 -0
- package/dist/provider.mjs +3 -0
- package/dist/route-handler.d.mts +7 -0
- package/dist/route-handler.d.mts.map +1 -0
- package/dist/route-handler.mjs +18 -0
- package/dist/route-handler.mjs.map +1 -0
- package/dist/server.d.mts +8 -0
- package/dist/server.d.mts.map +1 -0
- package/dist/server.mjs +6 -0
- package/dist/server.mjs.map +1 -0
- package/package.json +63 -71
- package/LICENSE +0 -21
- package/dist/build/env-config.d.mts +0 -7
- package/dist/build/env-config.d.mts.map +0 -1
- package/dist/build/env-config.mjs +0 -17
- package/dist/build/env-config.mjs.map +0 -1
- package/dist/build/logger.d.mts +0 -11
- package/dist/build/logger.d.mts.map +0 -1
- package/dist/build/logger.mjs +0 -155
- package/dist/build/logger.mjs.map +0 -1
- package/dist/build/release-program.d.mts +0 -19
- package/dist/build/release-program.d.mts.map +0 -1
- package/dist/build/release-program.mjs +0 -92
- package/dist/build/release-program.mjs.map +0 -1
- package/dist/build/secret-key.d.mts +0 -10
- package/dist/build/secret-key.d.mts.map +0 -1
- package/dist/build/secret-key.mjs +0 -16
- package/dist/build/secret-key.mjs.map +0 -1
- package/dist/build/services/config.service.d.mts +0 -9
- package/dist/build/services/config.service.d.mts.map +0 -1
- package/dist/build/services/config.service.mjs +0 -8
- package/dist/build/services/config.service.mjs.map +0 -1
- package/dist/build/services/preflight.service.d.mts +0 -19
- package/dist/build/services/preflight.service.d.mts.map +0 -1
- package/dist/build/services/preflight.service.mjs +0 -76
- package/dist/build/services/preflight.service.mjs.map +0 -1
- package/dist/build/services/release-identity.service.d.mts +0 -22
- package/dist/build/services/release-identity.service.d.mts.map +0 -1
- package/dist/build/services/release-identity.service.mjs +0 -48
- package/dist/build/services/release-identity.service.mjs.map +0 -1
- package/dist/build/services/source-map.service.d.mts +0 -24
- package/dist/build/services/source-map.service.d.mts.map +0 -1
- package/dist/build/services/source-map.service.mjs +0 -58
- package/dist/build/services/source-map.service.mjs.map +0 -1
- package/dist/build/source-maps/api.d.mts +0 -35
- package/dist/build/source-maps/api.d.mts.map +0 -1
- package/dist/build/source-maps/api.mjs +0 -61
- package/dist/build/source-maps/api.mjs.map +0 -1
- package/dist/build/source-maps/client.d.mts +0 -73
- package/dist/build/source-maps/client.d.mts.map +0 -1
- package/dist/build/source-maps/client.mjs +0 -228
- package/dist/build/source-maps/client.mjs.map +0 -1
- package/dist/build/source-maps/errors.d.mts +0 -109
- package/dist/build/source-maps/errors.d.mts.map +0 -1
- package/dist/build/source-maps/errors.mjs +0 -22
- package/dist/build/source-maps/errors.mjs.map +0 -1
- package/dist/build/source-maps/files.d.mts +0 -35
- package/dist/build/source-maps/files.d.mts.map +0 -1
- package/dist/build/source-maps/files.mjs +0 -222
- package/dist/build/source-maps/files.mjs.map +0 -1
- package/dist/build/source-maps/providers/deployment/detector.d.mts +0 -26
- package/dist/build/source-maps/providers/deployment/detector.d.mts.map +0 -1
- package/dist/build/source-maps/providers/deployment/detector.mjs +0 -22
- package/dist/build/source-maps/providers/deployment/detector.mjs.map +0 -1
- package/dist/build/source-maps/providers/deployment/types.d.mts +0 -12
- package/dist/build/source-maps/providers/deployment/types.d.mts.map +0 -1
- package/dist/build/source-maps/providers/deployment/types.mjs +0 -3
- package/dist/build/source-maps/providers/deployment/vercel.d.mts +0 -6
- package/dist/build/source-maps/providers/deployment/vercel.d.mts.map +0 -1
- package/dist/build/source-maps/providers/deployment/vercel.mjs +0 -44
- package/dist/build/source-maps/providers/deployment/vercel.mjs.map +0 -1
- package/dist/build/source-maps/providers/source-control/detector.d.mts +0 -15
- package/dist/build/source-maps/providers/source-control/detector.d.mts.map +0 -1
- package/dist/build/source-maps/providers/source-control/detector.mjs +0 -22
- package/dist/build/source-maps/providers/source-control/detector.mjs.map +0 -1
- package/dist/build/source-maps/providers/source-control/git.d.mts +0 -6
- package/dist/build/source-maps/providers/source-control/git.d.mts.map +0 -1
- package/dist/build/source-maps/providers/source-control/git.mjs +0 -50
- package/dist/build/source-maps/providers/source-control/git.mjs.map +0 -1
- package/dist/build/source-maps/providers/source-control/types.d.mts +0 -12
- package/dist/build/source-maps/providers/source-control/types.d.mts.map +0 -1
- package/dist/build/source-maps/providers/source-control/types.mjs +0 -3
- package/dist/build/with-interfere.d.mts +0 -48
- package/dist/build/with-interfere.d.mts.map +0 -1
- package/dist/build/with-interfere.mjs +0 -75
- package/dist/build/with-interfere.mjs.map +0 -1
- package/dist/client/client.d.mts +0 -3
- package/dist/client/client.mjs +0 -5
- package/dist/client/provider.d.mts +0 -22
- package/dist/client/provider.d.mts.map +0 -1
- package/dist/client/provider.mjs +0 -37
- package/dist/client/provider.mjs.map +0 -1
- package/dist/lib/env.d.mts +0 -12
- package/dist/lib/env.d.mts.map +0 -1
- package/dist/lib/env.mjs +0 -17
- package/dist/lib/env.mjs.map +0 -1
- package/dist/lib/test-utils/make-next-request.d.mts +0 -6
- package/dist/lib/test-utils/make-next-request.d.mts.map +0 -1
- package/dist/lib/test-utils/make-next-request.mjs +0 -12
- package/dist/lib/test-utils/make-next-request.mjs.map +0 -1
- package/dist/lib/types.d.mts +0 -22
- package/dist/lib/types.d.mts.map +0 -1
- package/dist/lib/types.mjs +0 -7
- package/dist/lib/types.mjs.map +0 -1
- package/dist/server/middleware.d.mts +0 -11
- package/dist/server/middleware.d.mts.map +0 -1
- package/dist/server/middleware.mjs +0 -85
- package/dist/server/middleware.mjs.map +0 -1
- package/dist/server/proxy.d.mts +0 -6
- package/dist/server/proxy.d.mts.map +0 -1
- package/dist/server/proxy.mjs +0 -30
- package/dist/server/proxy.mjs.map +0 -1
- package/dist/server/route-handler.d.mts +0 -9
- package/dist/server/route-handler.d.mts.map +0 -1
- package/dist/server/route-handler.mjs +0 -172
- package/dist/server/route-handler.mjs.map +0 -1
- package/dist/server/services/config.service.d.mts +0 -21
- package/dist/server/services/config.service.d.mts.map +0 -1
- package/dist/server/services/config.service.mjs +0 -43
- package/dist/server/services/config.service.mjs.map +0 -1
- package/dist/server/services/error-tracking.service.d.mts +0 -19
- package/dist/server/services/error-tracking.service.d.mts.map +0 -1
- package/dist/server/services/error-tracking.service.mjs +0 -31
- package/dist/server/services/error-tracking.service.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -1,317 +1,108 @@
|
|
|
1
1
|
# @interfere/next
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Next.js SDK v2 for Interfere (`next >= 15`, `react >= 19`).
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Install
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
|
|
9
|
-
# or
|
|
10
|
-
yarn add @interfere/next
|
|
11
|
-
# or
|
|
12
|
-
pnpm add @interfere/next
|
|
8
|
+
bun add @interfere/next
|
|
13
9
|
```
|
|
14
10
|
|
|
15
11
|
## Quick Start
|
|
16
12
|
|
|
17
|
-
|
|
13
|
+
1. Wrap your Next config.
|
|
18
14
|
|
|
19
|
-
|
|
15
|
+
```ts
|
|
16
|
+
// next.config.ts
|
|
17
|
+
import { withInterfere } from "@interfere/next/config";
|
|
18
|
+
import type { NextConfig } from "next";
|
|
20
19
|
|
|
21
|
-
|
|
22
|
-
import { init } from '@interfere/next';
|
|
20
|
+
const config: NextConfig = {};
|
|
23
21
|
|
|
24
|
-
export
|
|
25
|
-
project: process.env.NEXT_PUBLIC_INTERFERE_PROJECT_ID!,
|
|
26
|
-
options: {
|
|
27
|
-
env: process.env.NODE_ENV as 'development' | 'preview' | 'production',
|
|
28
|
-
debug: process.env.NODE_ENV === 'development',
|
|
29
|
-
},
|
|
30
|
-
});
|
|
22
|
+
export default withInterfere(config);
|
|
31
23
|
```
|
|
32
24
|
|
|
33
|
-
|
|
25
|
+
2. Wire Next instrumentation.
|
|
34
26
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
export { GET, OPTIONS, POST } from '@interfere/next/route-handler';
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
This route proxies client-side events to Interfere's ingest API using your `INTERFERE_SECRET_KEY` environment variable. Add the key to your `.env.local`:
|
|
42
|
-
|
|
43
|
-
```bash
|
|
44
|
-
INTERFERE_SECRET_KEY=if_sk_xxx
|
|
27
|
+
```ts
|
|
28
|
+
// instrumentation.ts
|
|
29
|
+
export { onRequestError } from "@interfere/next/server";
|
|
45
30
|
```
|
|
46
31
|
|
|
47
|
-
|
|
32
|
+
3. Mount the ingest route.
|
|
48
33
|
|
|
49
|
-
|
|
34
|
+
```ts
|
|
35
|
+
// app/api/interfere/[[...path]]/route.ts
|
|
36
|
+
import {
|
|
37
|
+
GET as handleGet,
|
|
38
|
+
OPTIONS as handleOptions,
|
|
39
|
+
POST as handlePost,
|
|
40
|
+
} from "@interfere/next/route-handler";
|
|
50
41
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
import { interfere } from '@/lib/interfere';
|
|
54
|
-
|
|
55
|
-
export default function RootLayout({
|
|
56
|
-
children,
|
|
57
|
-
}: {
|
|
58
|
-
children: React.ReactNode;
|
|
59
|
-
}) {
|
|
60
|
-
return (
|
|
61
|
-
<html lang="en">
|
|
62
|
-
<body>
|
|
63
|
-
<InterfereProvider>
|
|
64
|
-
<InterfereErrorBoundary>
|
|
65
|
-
{children}
|
|
66
|
-
</InterfereErrorBoundary>
|
|
67
|
-
</InterfereProvider>
|
|
68
|
-
</body>
|
|
69
|
-
</html>
|
|
70
|
-
);
|
|
42
|
+
export function GET(request: Request): Response {
|
|
43
|
+
return handleGet(request);
|
|
71
44
|
}
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
### 4. Add Error Handler (App Directory)
|
|
75
45
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
'use client';
|
|
80
|
-
|
|
81
|
-
import { useEffect } from 'react';
|
|
82
|
-
import { captureErrorBoundaryError } from '@interfere/next';
|
|
83
|
-
|
|
84
|
-
export default function Error({
|
|
85
|
-
error,
|
|
86
|
-
reset,
|
|
87
|
-
}: {
|
|
88
|
-
error: Error & { digest?: string };
|
|
89
|
-
reset: () => void;
|
|
90
|
-
}) {
|
|
91
|
-
useEffect(() => {
|
|
92
|
-
captureErrorBoundaryError(error, {
|
|
93
|
-
componentStack: error.stack || '',
|
|
94
|
-
});
|
|
95
|
-
}, [error]);
|
|
46
|
+
export function POST(request: Request): Promise<Response> {
|
|
47
|
+
return handlePost(request);
|
|
48
|
+
}
|
|
96
49
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
<h2>Something went wrong!</h2>
|
|
100
|
-
<button onClick={() => reset()}>Try again</button>
|
|
101
|
-
</div>
|
|
102
|
-
);
|
|
50
|
+
export function OPTIONS(): Response {
|
|
51
|
+
return handleOptions();
|
|
103
52
|
}
|
|
104
53
|
```
|
|
105
54
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
Create `app/global-error.tsx`:
|
|
55
|
+
4. Add the provider in your app layout.
|
|
109
56
|
|
|
110
57
|
```tsx
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
import { createInterfereErrorHandler } from '@interfere/next';
|
|
58
|
+
import { InterfereProvider } from "@interfere/next/provider";
|
|
114
59
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
export default function GlobalError({
|
|
118
|
-
error,
|
|
119
|
-
reset,
|
|
60
|
+
export default function RootLayout({
|
|
61
|
+
children,
|
|
120
62
|
}: {
|
|
121
|
-
|
|
122
|
-
reset: () => void;
|
|
63
|
+
children: React.ReactNode;
|
|
123
64
|
}) {
|
|
124
|
-
errorHandler(error, { digest: error.digest });
|
|
125
|
-
|
|
126
65
|
return (
|
|
127
|
-
<html>
|
|
66
|
+
<html lang="en">
|
|
128
67
|
<body>
|
|
129
|
-
<
|
|
130
|
-
<button onClick={() => reset()}>Try again</button>
|
|
68
|
+
<InterfereProvider>{children}</InterfereProvider>
|
|
131
69
|
</body>
|
|
132
70
|
</html>
|
|
133
71
|
);
|
|
134
72
|
}
|
|
135
73
|
```
|
|
136
74
|
|
|
137
|
-
##
|
|
138
|
-
|
|
139
|
-
### Automatic Error Capture
|
|
140
|
-
|
|
141
|
-
Wrap your API route handlers:
|
|
142
|
-
|
|
143
|
-
```typescript
|
|
144
|
-
// app/api/users/route.ts
|
|
145
|
-
import { withInterfereApiRoute } from '@interfere/next';
|
|
146
|
-
|
|
147
|
-
export const GET = withInterfereApiRoute(async (request) => {
|
|
148
|
-
// Your API logic here
|
|
149
|
-
const users = await fetchUsers();
|
|
150
|
-
return Response.json(users);
|
|
151
|
-
});
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
### Manual Error Capture
|
|
75
|
+
## Manual Server Capture
|
|
155
76
|
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
import { captureServerError } from '@interfere/next';
|
|
77
|
+
```ts
|
|
78
|
+
import { captureError } from "@interfere/next/server";
|
|
159
79
|
|
|
160
|
-
export async function
|
|
80
|
+
export async function GET(request: Request): Promise<Response> {
|
|
161
81
|
try {
|
|
162
|
-
|
|
163
|
-
// Process webhook
|
|
82
|
+
throw new Error("boom");
|
|
164
83
|
} catch (error) {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
type: 'webhook_error',
|
|
168
|
-
});
|
|
169
|
-
return Response.json({ error: 'Webhook failed' }, { status: 500 });
|
|
84
|
+
await captureError(error, request);
|
|
85
|
+
throw error;
|
|
170
86
|
}
|
|
171
87
|
}
|
|
172
88
|
```
|
|
173
89
|
|
|
174
|
-
##
|
|
90
|
+
## Required Env
|
|
175
91
|
|
|
176
|
-
|
|
92
|
+
- `INTERFERE_API_KEY`
|
|
177
93
|
|
|
178
|
-
|
|
179
|
-
// middleware.ts
|
|
180
|
-
import { withInterfereMiddleware } from '@interfere/next';
|
|
181
|
-
import { NextResponse } from 'next/server';
|
|
94
|
+
Optional:
|
|
182
95
|
|
|
183
|
-
|
|
184
|
-
// Your middleware logic
|
|
185
|
-
return NextResponse.next();
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
export const config = {
|
|
189
|
-
matcher: '/api/:path*',
|
|
190
|
-
};
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
## Server Components
|
|
194
|
-
|
|
195
|
-
Wrap async server components:
|
|
196
|
-
|
|
197
|
-
```typescript
|
|
198
|
-
// app/dashboard/page.tsx
|
|
199
|
-
import { withInterfereServerComponent } from '@interfere/next';
|
|
200
|
-
|
|
201
|
-
async function DashboardPage() {
|
|
202
|
-
const data = await fetchDashboardData();
|
|
203
|
-
return <Dashboard data={data} />;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
export default withInterfereServerComponent(DashboardPage, 'DashboardPage');
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
## Custom Event Capture
|
|
210
|
-
|
|
211
|
-
Capture custom events and errors:
|
|
212
|
-
|
|
213
|
-
```typescript
|
|
214
|
-
import { capture, captureServerError } from '@interfere/next';
|
|
215
|
-
|
|
216
|
-
// Capture custom events
|
|
217
|
-
capture('custom', {
|
|
218
|
-
action: 'user_signup',
|
|
219
|
-
userId: user.id,
|
|
220
|
-
plan: 'premium',
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
// Capture handled errors
|
|
224
|
-
try {
|
|
225
|
-
await riskyOperation();
|
|
226
|
-
} catch (error) {
|
|
227
|
-
captureServerError(error, undefined, {
|
|
228
|
-
operation: 'risky_operation',
|
|
229
|
-
context: { userId: user.id },
|
|
230
|
-
});
|
|
231
|
-
}
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
## React Hook
|
|
235
|
-
|
|
236
|
-
Use the `useInterfere` hook in client components:
|
|
237
|
-
|
|
238
|
-
```tsx
|
|
239
|
-
'use client';
|
|
240
|
-
|
|
241
|
-
import { useInterfere } from '@interfere/next';
|
|
242
|
-
|
|
243
|
-
export function Button() {
|
|
244
|
-
const { capture } = useInterfere();
|
|
245
|
-
|
|
246
|
-
const handleClick = () => {
|
|
247
|
-
capture('ui_event', {
|
|
248
|
-
action: 'button_click',
|
|
249
|
-
label: 'cta_button',
|
|
250
|
-
});
|
|
251
|
-
};
|
|
252
|
-
|
|
253
|
-
return <button onClick={handleClick}>Click me</button>;
|
|
254
|
-
}
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
## Configuration Options
|
|
258
|
-
|
|
259
|
-
```typescript
|
|
260
|
-
init({
|
|
261
|
-
project: 'if_proj_xxx', // Your project ID
|
|
262
|
-
options: {
|
|
263
|
-
env: 'production', // 'development' | 'preview' | 'production'
|
|
264
|
-
flushInterval: 5000, // Flush interval in ms (client-side only)
|
|
265
|
-
debug: false, // Enable debug logging
|
|
266
|
-
sessionId: 'custom-session-id', // Optional custom session ID
|
|
267
|
-
},
|
|
268
|
-
});
|
|
269
|
-
```
|
|
270
|
-
|
|
271
|
-
## Event Types
|
|
272
|
-
|
|
273
|
-
The SDK automatically captures these event types:
|
|
274
|
-
|
|
275
|
-
- `error` - Runtime errors (client, server, and edge); origin is indicated by `errorSource` in the payload (`client` | `server` | `edge`).
|
|
276
|
-
- `ui_event` - User interface events
|
|
277
|
-
- `custom` - Custom application events
|
|
278
|
-
- `rage_click` - rage clicks
|
|
279
|
-
|
|
280
|
-
## Best Practices
|
|
281
|
-
|
|
282
|
-
1. **Initialize Early**: Initialize Interfere as early as possible in your application lifecycle.
|
|
283
|
-
|
|
284
|
-
2. **Use Error Boundaries**: Always wrap your app with `InterfereErrorBoundary` to catch React errors.
|
|
285
|
-
|
|
286
|
-
3. **Wrap Async Functions**: Use `withErrorCapture` or specific wrappers for automatic error tracking.
|
|
287
|
-
|
|
288
|
-
4. **Add Context**: Include relevant context when capturing errors manually:
|
|
289
|
-
```typescript
|
|
290
|
-
captureServerError(error, request, {
|
|
291
|
-
userId: session.userId,
|
|
292
|
-
action: 'update_profile',
|
|
293
|
-
metadata: { ... },
|
|
294
|
-
});
|
|
295
|
-
```
|
|
296
|
-
|
|
297
|
-
5. **Environment Variables**: Store your project ID in environment variables:
|
|
298
|
-
```bash
|
|
299
|
-
NEXT_PUBLIC_INTERFERE_PROJECT_ID=if_proj_xxx
|
|
300
|
-
```
|
|
301
|
-
|
|
302
|
-
## TypeScript
|
|
303
|
-
|
|
304
|
-
The SDK is fully typed. Import types as needed:
|
|
305
|
-
|
|
306
|
-
```typescript
|
|
307
|
-
import type {
|
|
308
|
-
Config,
|
|
309
|
-
InitConfig,
|
|
310
|
-
EventType,
|
|
311
|
-
Envelope,
|
|
312
|
-
} from '@interfere/next';
|
|
313
|
-
```
|
|
96
|
+
- `INTERFERE_API_URL`
|
|
314
97
|
|
|
315
|
-
##
|
|
98
|
+
## Migration Notes (from `@interfere/next`)
|
|
316
99
|
|
|
317
|
-
|
|
100
|
+
- Legacy package remains frozen at `@interfere/next`.
|
|
101
|
+
- `@interfere/next` exposes only:
|
|
102
|
+
- `@interfere/next/config`
|
|
103
|
+
- `@interfere/next/provider`
|
|
104
|
+
- `@interfere/next/server`
|
|
105
|
+
- `@interfere/next/route-handler`
|
|
106
|
+
- No `middleware` wrapper exports.
|
|
107
|
+
- No `server/auto-init` export.
|
|
108
|
+
- Preferred instrumentation wiring is a single re-export from `./server`.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//#region \0rolldown/runtime.js
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __exportAll = (all, no_symbols) => {
|
|
4
|
+
let target = {};
|
|
5
|
+
for (var name in all) __defProp(target, name, {
|
|
6
|
+
get: all[name],
|
|
7
|
+
enumerable: true
|
|
8
|
+
});
|
|
9
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
10
|
+
return target;
|
|
11
|
+
};
|
|
12
|
+
//#endregion
|
|
13
|
+
export { __exportAll };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Envelope } from "@interfere/types/sdk/envelope";
|
|
2
|
+
import { NextConfig } from "next";
|
|
3
|
+
|
|
4
|
+
//#region src/config.d.ts
|
|
5
|
+
interface InterfereConfig extends Partial<Pick<Envelope, "buildId" | "releaseId">> {}
|
|
6
|
+
type NextConfigWithInterfere = NextConfig & {
|
|
7
|
+
interfere?: InterfereConfig;
|
|
8
|
+
};
|
|
9
|
+
declare function withInterfere(nextConfig?: NextConfigWithInterfere): NextConfig;
|
|
10
|
+
//#endregion
|
|
11
|
+
export { InterfereConfig, NextConfigWithInterfere, withInterfere };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.mts","names":[],"sources":["../src/config.ts"],"mappings":";;;;UAkBiB,eAAA,SACP,OAAA,CAAQ,IAAA,CAAK,QAAA;AAAA,KAEX,uBAAA,GAA0B,UAAA;EACpC,SAAA,GAAY,eAAA;AAAA;AAAA,iBAGE,aAAA,CACd,UAAA,GAAY,uBAAA,GACX,UAAA"}
|
package/dist/config.mjs
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { configureBuild } from "./internal/build/configure-build.mjs";
|
|
2
|
+
import { buildInjectedValues } from "./internal/build/injected.mjs";
|
|
3
|
+
import { buildLog } from "./internal/build/logger.mjs";
|
|
4
|
+
import { runGitCommand } from "./internal/build/release/git.mjs";
|
|
5
|
+
import { readInterfereEnv } from "./internal/env.mjs";
|
|
6
|
+
import { releaseDestinationIdEnvKeys, releaseSourceIdEnvKeys } from "@interfere/types/integrations";
|
|
7
|
+
import { parseEnvValue, readFirstEnvValue } from "@interfere/types/sdk/env";
|
|
8
|
+
//#region src/config.ts
|
|
9
|
+
function withInterfere(nextConfig = {}) {
|
|
10
|
+
const { interfere, env: userEnv, webpack, turbopack, compiler, productionBrowserSourceMaps, ...rest } = nextConfig;
|
|
11
|
+
const metadata = resolveBuildMetadata(interfere);
|
|
12
|
+
const injectedValues = buildInjectedValues(metadata);
|
|
13
|
+
const hasApiKey = readInterfereEnv().apiKey !== null;
|
|
14
|
+
const build = configureBuild({
|
|
15
|
+
existingWebpack: webpack,
|
|
16
|
+
existingTurbopack: turbopack,
|
|
17
|
+
projectDir: process.cwd(),
|
|
18
|
+
values: injectedValues
|
|
19
|
+
});
|
|
20
|
+
if (hasApiKey && !build.webpack && !build.turbopack) throw new Error("[Interfere] INTERFERE_API_KEY is set but no instrumentation-client file was found. Create an instrumentation-client.ts file in your project root or src/ directory. See: https://interfere.com/docs/nextjs/setup");
|
|
21
|
+
return {
|
|
22
|
+
...rest,
|
|
23
|
+
env: mergeEnvConfig(userEnv, metadata),
|
|
24
|
+
compiler: buildCompilerConfig(compiler, hasApiKey),
|
|
25
|
+
webpack: build.webpack,
|
|
26
|
+
turbopack: build.turbopack,
|
|
27
|
+
productionBrowserSourceMaps: hasApiKey ? true : productionBrowserSourceMaps
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function resolveBuildMetadata(config) {
|
|
31
|
+
const buildId = parseEnvValue(config?.buildId) ?? readFirstEnvValue(process.env, releaseSourceIdEnvKeys) ?? runGitCommand("git rev-parse HEAD");
|
|
32
|
+
return {
|
|
33
|
+
buildId,
|
|
34
|
+
releaseId: parseEnvValue(config?.releaseId) ?? readFirstEnvValue(process.env, releaseDestinationIdEnvKeys) ?? buildId
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function mergeEnvConfig(userEnv, metadata) {
|
|
38
|
+
const merged = {};
|
|
39
|
+
if (metadata.buildId !== null) merged.NEXT_PUBLIC_INTERFERE_BUILD_ID = metadata.buildId;
|
|
40
|
+
if (metadata.releaseId !== null) merged.NEXT_PUBLIC_INTERFERE_RELEASE_ID = metadata.releaseId;
|
|
41
|
+
return {
|
|
42
|
+
...merged,
|
|
43
|
+
...userEnv
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function buildCompilerConfig(existingCompiler, enabled) {
|
|
47
|
+
const compiler = existingCompiler;
|
|
48
|
+
const existingHook = compiler?.runAfterProductionCompile;
|
|
49
|
+
if (!(existingHook || enabled)) return existingCompiler;
|
|
50
|
+
return {
|
|
51
|
+
...compiler ?? {},
|
|
52
|
+
async runAfterProductionCompile(context) {
|
|
53
|
+
if (existingHook) await existingHook(context);
|
|
54
|
+
if (enabled) await runBuildReleasePipeline(context);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
async function runBuildReleasePipeline(context) {
|
|
59
|
+
const env = readInterfereEnv();
|
|
60
|
+
const { apiKey, apiUrl } = env;
|
|
61
|
+
if (apiKey === null) {
|
|
62
|
+
buildLog.warn("Skipping", ["Missing INTERFERE_API_KEY."]);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const buildId = env.release.sourceId;
|
|
66
|
+
if (buildId === null) {
|
|
67
|
+
buildLog.error("Build ID missing", [
|
|
68
|
+
"Could not resolve a build ID from config, environment variables, or git.",
|
|
69
|
+
"Source maps will not be uploaded and errors will have unresolved stack traces.",
|
|
70
|
+
"Set INTERFERE_BUILD_ID, or ensure git is available in your build environment."
|
|
71
|
+
]);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const { createRelease } = await import("./internal/build/release/index.mjs");
|
|
75
|
+
const { runSourceMapPipeline } = await import("./internal/build/source-maps/index.mjs");
|
|
76
|
+
const client = {
|
|
77
|
+
async createRelease() {
|
|
78
|
+
const release = await createRelease(apiKey, buildId);
|
|
79
|
+
return {
|
|
80
|
+
slug: release.destination.slug,
|
|
81
|
+
orgSlug: release.org.slug,
|
|
82
|
+
buildId: release.build.hash ?? buildId
|
|
83
|
+
};
|
|
84
|
+
},
|
|
85
|
+
async uploadSourceMaps(releaseSlug, body) {
|
|
86
|
+
const url = `${apiUrl}/v1/releases/${releaseSlug}/source-maps`;
|
|
87
|
+
const response = await fetch(url, {
|
|
88
|
+
method: "POST",
|
|
89
|
+
headers: { "x-api-key": apiKey },
|
|
90
|
+
body
|
|
91
|
+
});
|
|
92
|
+
if (!response.ok) {
|
|
93
|
+
const text = await response.text().catch(() => "");
|
|
94
|
+
throw new Error(`Source map upload failed: ${response.status} ${response.statusText} ${text}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
try {
|
|
99
|
+
const result = await runSourceMapPipeline(context.projectDir, context.distDir, client);
|
|
100
|
+
if (!result.ready) {
|
|
101
|
+
buildLog.warn("Skipping", ["No source maps found"]);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
buildLog.info("Completed", [
|
|
105
|
+
`https://interfere.com/~/${result.orgSlug}`,
|
|
106
|
+
`Release: ${result.releaseSlug}`,
|
|
107
|
+
`Build: ${result.buildId}`,
|
|
108
|
+
`Artifacts: ${result.fileCount} source maps`
|
|
109
|
+
]);
|
|
110
|
+
} catch (error) {
|
|
111
|
+
buildLog.error("Error", [error instanceof Error ? error.message : String(error)]);
|
|
112
|
+
throw error;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
//#endregion
|
|
116
|
+
export { withInterfere };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.mjs","names":[],"sources":["../src/config.ts"],"sourcesContent":["import {\n releaseDestinationIdEnvKeys,\n releaseSourceIdEnvKeys,\n} from \"@interfere/types/integrations\";\nimport { parseEnvValue, readFirstEnvValue } from \"@interfere/types/sdk/env\";\nimport type { Envelope } from \"@interfere/types/sdk/envelope\";\n\nimport type { NextConfig } from \"next\";\n\nimport { configureBuild } from \"./internal/build/configure-build.js\";\nimport {\n buildInjectedValues,\n type InterfereBuildMetadata,\n} from \"./internal/build/injected.js\";\nimport { buildLog } from \"./internal/build/logger.js\";\nimport { runGitCommand } from \"./internal/build/release/git.js\";\nimport { readInterfereEnv } from \"./internal/env.js\";\n\nexport interface InterfereConfig\n extends Partial<Pick<Envelope, \"buildId\" | \"releaseId\">> {}\n\nexport type NextConfigWithInterfere = NextConfig & {\n interfere?: InterfereConfig;\n};\n\nexport function withInterfere(\n nextConfig: NextConfigWithInterfere = {}\n): NextConfig {\n const {\n interfere,\n env: userEnv,\n webpack,\n turbopack,\n compiler,\n productionBrowserSourceMaps,\n ...rest\n } = nextConfig;\n const metadata = resolveBuildMetadata(interfere);\n const injectedValues = buildInjectedValues(metadata);\n const hasApiKey = readInterfereEnv().apiKey !== null;\n const build = configureBuild({\n existingWebpack: webpack,\n existingTurbopack: turbopack,\n projectDir: process.cwd(),\n values: injectedValues,\n });\n\n if (hasApiKey && !build.webpack && !build.turbopack) {\n throw new Error(\n \"[Interfere] INTERFERE_API_KEY is set but no instrumentation-client file was found. \" +\n \"Create an instrumentation-client.ts file in your project root or src/ directory. \" +\n \"See: https://interfere.com/docs/nextjs/setup\"\n );\n }\n\n return {\n ...rest,\n env: mergeEnvConfig(userEnv, metadata),\n compiler: buildCompilerConfig(compiler, hasApiKey),\n webpack: build.webpack,\n turbopack: build.turbopack,\n productionBrowserSourceMaps: hasApiKey ? true : productionBrowserSourceMaps,\n };\n}\n\nfunction resolveBuildMetadata(\n config?: InterfereConfig\n): InterfereBuildMetadata {\n const buildId =\n parseEnvValue(config?.buildId) ??\n readFirstEnvValue(process.env, releaseSourceIdEnvKeys) ??\n runGitCommand(\"git rev-parse HEAD\");\n const releaseId =\n parseEnvValue(config?.releaseId) ??\n readFirstEnvValue(process.env, releaseDestinationIdEnvKeys) ??\n buildId;\n\n return { buildId, releaseId };\n}\n\nfunction mergeEnvConfig(\n userEnv: NextConfig[\"env\"] | undefined,\n metadata: InterfereBuildMetadata\n): NextConfig[\"env\"] {\n const merged: Record<string, string> = {};\n\n if (metadata.buildId !== null) {\n merged.NEXT_PUBLIC_INTERFERE_BUILD_ID = metadata.buildId;\n }\n\n if (metadata.releaseId !== null) {\n merged.NEXT_PUBLIC_INTERFERE_RELEASE_ID = metadata.releaseId;\n }\n\n return { ...merged, ...userEnv };\n}\n\ninterface ProductionCompileContext {\n readonly distDir: string;\n readonly projectDir: string;\n}\n\ntype NextCompilerWithProductionHook = NonNullable<NextConfig[\"compiler\"]> & {\n runAfterProductionCompile?: (\n context: ProductionCompileContext\n ) => void | Promise<void>;\n};\n\nfunction buildCompilerConfig(\n existingCompiler: NextConfig[\"compiler\"] | undefined,\n enabled: boolean\n): NextConfig[\"compiler\"] | undefined {\n const compiler = existingCompiler as\n | NextCompilerWithProductionHook\n | undefined;\n const existingHook = compiler?.runAfterProductionCompile;\n\n if (!(existingHook || enabled)) {\n return existingCompiler;\n }\n\n return {\n ...(compiler ?? {}),\n async runAfterProductionCompile(context: ProductionCompileContext) {\n if (existingHook) {\n await existingHook(context);\n }\n\n if (enabled) {\n await runBuildReleasePipeline(context);\n }\n },\n } as NextConfig[\"compiler\"];\n}\n\nasync function runBuildReleasePipeline(\n context: ProductionCompileContext\n): Promise<void> {\n const env = readInterfereEnv();\n\n const { apiKey, apiUrl } = env;\n\n if (apiKey === null) {\n buildLog.warn(\"Skipping\", [\"Missing INTERFERE_API_KEY.\"]);\n return;\n }\n\n const buildId = env.release.sourceId;\n\n if (buildId === null) {\n buildLog.error(\"Build ID missing\", [\n \"Could not resolve a build ID from config, environment variables, or git.\",\n \"Source maps will not be uploaded and errors will have unresolved stack traces.\",\n \"Set INTERFERE_BUILD_ID, or ensure git is available in your build environment.\",\n ]);\n return;\n }\n\n const { createRelease } = await import(\"./internal/build/release/index.js\");\n const { runSourceMapPipeline } = await import(\n \"./internal/build/source-maps/index.js\"\n );\n\n const client: import(\"./internal/build/source-maps/index.js\").BuildClient = {\n async createRelease() {\n const release = await createRelease(apiKey, buildId);\n return {\n slug: release.destination.slug,\n orgSlug: release.org.slug,\n buildId: release.build.hash ?? buildId,\n };\n },\n async uploadSourceMaps(releaseSlug, body) {\n const url = `${apiUrl}/v1/releases/${releaseSlug}/source-maps`;\n const response = await fetch(url, {\n method: \"POST\",\n headers: { \"x-api-key\": apiKey },\n body,\n });\n\n if (!response.ok) {\n const text = await response.text().catch(() => \"\");\n throw new Error(\n `Source map upload failed: ${response.status} ${response.statusText} ${text}`\n );\n }\n },\n };\n\n try {\n const result = await runSourceMapPipeline(\n context.projectDir,\n context.distDir,\n client\n );\n\n if (!result.ready) {\n buildLog.warn(\"Skipping\", [\"No source maps found\"]);\n return;\n }\n\n buildLog.info(\"Completed\", [\n `https://interfere.com/~/${result.orgSlug}`,\n `Release: ${result.releaseSlug}`,\n `Build: ${result.buildId}`,\n `Artifacts: ${result.fileCount} source maps`,\n ]);\n } catch (error) {\n buildLog.error(\"Error\", [\n error instanceof Error ? error.message : String(error),\n ]);\n throw error;\n }\n}\n"],"mappings":";;;;;;;;AAyBA,SAAgB,cACd,aAAsC,EAAE,EAC5B;CACZ,MAAM,EACJ,WACA,KAAK,SACL,SACA,WACA,UACA,6BACA,GAAG,SACD;CACJ,MAAM,WAAW,qBAAqB,UAAU;CAChD,MAAM,iBAAiB,oBAAoB,SAAS;CACpD,MAAM,YAAY,kBAAkB,CAAC,WAAW;CAChD,MAAM,QAAQ,eAAe;EAC3B,iBAAiB;EACjB,mBAAmB;EACnB,YAAY,QAAQ,KAAK;EACzB,QAAQ;EACT,CAAC;AAEF,KAAI,aAAa,CAAC,MAAM,WAAW,CAAC,MAAM,UACxC,OAAM,IAAI,MACR,mNAGD;AAGH,QAAO;EACL,GAAG;EACH,KAAK,eAAe,SAAS,SAAS;EACtC,UAAU,oBAAoB,UAAU,UAAU;EAClD,SAAS,MAAM;EACf,WAAW,MAAM;EACjB,6BAA6B,YAAY,OAAO;EACjD;;AAGH,SAAS,qBACP,QACwB;CACxB,MAAM,UACJ,cAAc,QAAQ,QAAQ,IAC9B,kBAAkB,QAAQ,KAAK,uBAAuB,IACtD,cAAc,qBAAqB;AAMrC,QAAO;EAAE;EAAS,WAJhB,cAAc,QAAQ,UAAU,IAChC,kBAAkB,QAAQ,KAAK,4BAA4B,IAC3D;EAE2B;;AAG/B,SAAS,eACP,SACA,UACmB;CACnB,MAAM,SAAiC,EAAE;AAEzC,KAAI,SAAS,YAAY,KACvB,QAAO,iCAAiC,SAAS;AAGnD,KAAI,SAAS,cAAc,KACzB,QAAO,mCAAmC,SAAS;AAGrD,QAAO;EAAE,GAAG;EAAQ,GAAG;EAAS;;AAclC,SAAS,oBACP,kBACA,SACoC;CACpC,MAAM,WAAW;CAGjB,MAAM,eAAe,UAAU;AAE/B,KAAI,EAAE,gBAAgB,SACpB,QAAO;AAGT,QAAO;EACL,GAAI,YAAY,EAAE;EAClB,MAAM,0BAA0B,SAAmC;AACjE,OAAI,aACF,OAAM,aAAa,QAAQ;AAG7B,OAAI,QACF,OAAM,wBAAwB,QAAQ;;EAG3C;;AAGH,eAAe,wBACb,SACe;CACf,MAAM,MAAM,kBAAkB;CAE9B,MAAM,EAAE,QAAQ,WAAW;AAE3B,KAAI,WAAW,MAAM;AACnB,WAAS,KAAK,YAAY,CAAC,6BAA6B,CAAC;AACzD;;CAGF,MAAM,UAAU,IAAI,QAAQ;AAE5B,KAAI,YAAY,MAAM;AACpB,WAAS,MAAM,oBAAoB;GACjC;GACA;GACA;GACD,CAAC;AACF;;CAGF,MAAM,EAAE,kBAAkB,MAAM,OAAO;CACvC,MAAM,EAAE,yBAAyB,MAAM,OACrC;CAGF,MAAM,SAAsE;EAC1E,MAAM,gBAAgB;GACpB,MAAM,UAAU,MAAM,cAAc,QAAQ,QAAQ;AACpD,UAAO;IACL,MAAM,QAAQ,YAAY;IAC1B,SAAS,QAAQ,IAAI;IACrB,SAAS,QAAQ,MAAM,QAAQ;IAChC;;EAEH,MAAM,iBAAiB,aAAa,MAAM;GACxC,MAAM,MAAM,GAAG,OAAO,eAAe,YAAY;GACjD,MAAM,WAAW,MAAM,MAAM,KAAK;IAChC,QAAQ;IACR,SAAS,EAAE,aAAa,QAAQ;IAChC;IACD,CAAC;AAEF,OAAI,CAAC,SAAS,IAAI;IAChB,MAAM,OAAO,MAAM,SAAS,MAAM,CAAC,YAAY,GAAG;AAClD,UAAM,IAAI,MACR,6BAA6B,SAAS,OAAO,GAAG,SAAS,WAAW,GAAG,OACxE;;;EAGN;AAED,KAAI;EACF,MAAM,SAAS,MAAM,qBACnB,QAAQ,YACR,QAAQ,SACR,OACD;AAED,MAAI,CAAC,OAAO,OAAO;AACjB,YAAS,KAAK,YAAY,CAAC,uBAAuB,CAAC;AACnD;;AAGF,WAAS,KAAK,aAAa;GACzB,2BAA2B,OAAO;GAClC,YAAY,OAAO;GACnB,UAAU,OAAO;GACjB,cAAc,OAAO,UAAU;GAChC,CAAC;UACK,OAAO;AACd,WAAS,MAAM,SAAS,CACtB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,CACvD,CAAC;AACF,QAAM"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { InterfereInjectedValues } from "./injected.mjs";
|
|
2
|
+
import { NextConfig } from "next";
|
|
3
|
+
|
|
4
|
+
//#region src/internal/build/configure-build.d.ts
|
|
5
|
+
interface ConfigureBuildInput {
|
|
6
|
+
readonly existingWebpack: NextConfig["webpack"] | undefined;
|
|
7
|
+
readonly existingTurbopack: NextConfig["turbopack"] | undefined;
|
|
8
|
+
readonly projectDir: string;
|
|
9
|
+
readonly values: InterfereInjectedValues;
|
|
10
|
+
}
|
|
11
|
+
interface ConfigureBuildOutput {
|
|
12
|
+
readonly webpack: NextConfig["webpack"] | undefined;
|
|
13
|
+
readonly turbopack: NextConfig["turbopack"] | undefined;
|
|
14
|
+
}
|
|
15
|
+
declare function configureBuild({
|
|
16
|
+
existingWebpack,
|
|
17
|
+
existingTurbopack,
|
|
18
|
+
projectDir,
|
|
19
|
+
values
|
|
20
|
+
}: ConfigureBuildInput): ConfigureBuildOutput;
|
|
21
|
+
//#endregion
|
|
22
|
+
export { configureBuild };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configure-build.d.mts","names":[],"sources":["../../../src/internal/build/configure-build.ts"],"mappings":";;;;UAyBU,mBAAA;EAAA,SACC,eAAA,EAAiB,UAAA;EAAA,SACjB,iBAAA,EAAmB,UAAA;EAAA,SACnB,UAAA;EAAA,SACA,MAAA,EAAQ,uBAAA;AAAA;AAAA,UAGT,oBAAA;EAAA,SACC,OAAA,EAAS,UAAA;EAAA,SACT,SAAA,EAAW,UAAA;AAAA;AAAA,iBAGN,cAAA,CAAA;EACd,eAAA;EACA,iBAAA;EACA,UAAA;EACA;AAAA,GACC,mBAAA,GAAsB,oBAAA"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { basename, dirname, join, normalize, resolve } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
//#region src/internal/build/configure-build.ts
|
|
5
|
+
const INSTRUMENTATION_CLIENT_FILES = [
|
|
6
|
+
"instrumentation-client.ts",
|
|
7
|
+
"instrumentation-client.tsx",
|
|
8
|
+
"instrumentation-client.js",
|
|
9
|
+
"instrumentation-client.jsx",
|
|
10
|
+
"src/instrumentation-client.ts",
|
|
11
|
+
"src/instrumentation-client.tsx",
|
|
12
|
+
"src/instrumentation-client.js",
|
|
13
|
+
"src/instrumentation-client.jsx"
|
|
14
|
+
];
|
|
15
|
+
const LOADER_FILE_CANDIDATES = [
|
|
16
|
+
"value-injection-loader.ts",
|
|
17
|
+
"value-injection-loader.js",
|
|
18
|
+
"value-injection-loader.mjs",
|
|
19
|
+
"value-injection-loader.cjs"
|
|
20
|
+
];
|
|
21
|
+
function configureBuild({ existingWebpack, existingTurbopack, projectDir, values }) {
|
|
22
|
+
const instrumentationClientPath = resolveInstrumentationClientPath(projectDir);
|
|
23
|
+
return {
|
|
24
|
+
webpack: buildWebpackHook(existingWebpack, instrumentationClientPath, values),
|
|
25
|
+
turbopack: buildTurbopackConfig(existingTurbopack, instrumentationClientPath, values)
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function hasInjectedMetadata(metadata) {
|
|
29
|
+
return metadata.__INTERFERE_BUILD_ID__ !== null || metadata.__INTERFERE_RELEASE_ID__ !== null;
|
|
30
|
+
}
|
|
31
|
+
function buildWebpackHook(existingWebpack, instrumentationClientPath, values) {
|
|
32
|
+
if (!(instrumentationClientPath && hasInjectedMetadata(values))) return existingWebpack;
|
|
33
|
+
const normalizedPath = normalize(instrumentationClientPath);
|
|
34
|
+
const loaderPath = resolveLoaderPath();
|
|
35
|
+
return (webpackConfig, options) => {
|
|
36
|
+
const outputConfig = existingWebpack ? existingWebpack(webpackConfig, options) : webpackConfig;
|
|
37
|
+
const mutableConfig = outputConfig;
|
|
38
|
+
const injectionRule = {
|
|
39
|
+
test: (resource) => Boolean(resource) && normalize(resource) === normalizedPath,
|
|
40
|
+
use: [{
|
|
41
|
+
loader: loaderPath,
|
|
42
|
+
options: { values }
|
|
43
|
+
}]
|
|
44
|
+
};
|
|
45
|
+
mutableConfig.module ??= {};
|
|
46
|
+
mutableConfig.module.rules ??= [];
|
|
47
|
+
mutableConfig.module.rules.push(injectionRule);
|
|
48
|
+
return outputConfig;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function buildTurbopackConfig(existingTurbopack, instrumentationClientPath, values) {
|
|
52
|
+
if (!(instrumentationClientPath && hasInjectedMetadata(values))) return existingTurbopack;
|
|
53
|
+
const baseConfig = existingTurbopack ?? {};
|
|
54
|
+
const ruleKey = `**/${basename(instrumentationClientPath)}`;
|
|
55
|
+
const existingRule = baseConfig.rules?.[ruleKey] ?? {};
|
|
56
|
+
const existingLoaders = Array.isArray(existingRule.loaders) ? existingRule.loaders : [];
|
|
57
|
+
return {
|
|
58
|
+
...baseConfig,
|
|
59
|
+
rules: {
|
|
60
|
+
...baseConfig.rules,
|
|
61
|
+
[ruleKey]: {
|
|
62
|
+
...existingRule,
|
|
63
|
+
loaders: [...existingLoaders, {
|
|
64
|
+
loader: resolveLoaderPath(),
|
|
65
|
+
options: { serializedValues: JSON.stringify(values) }
|
|
66
|
+
}]
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function resolveInstrumentationClientPath(projectDir) {
|
|
72
|
+
for (const candidate of INSTRUMENTATION_CLIENT_FILES) {
|
|
73
|
+
const filePath = resolve(projectDir, candidate);
|
|
74
|
+
if (existsSync(filePath)) return filePath;
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
function resolveLoaderPath() {
|
|
79
|
+
const directory = resolve(dirname(fileURLToPath(import.meta.url)));
|
|
80
|
+
for (const candidate of LOADER_FILE_CANDIDATES) {
|
|
81
|
+
const filePath = join(directory, candidate);
|
|
82
|
+
if (existsSync(filePath)) return filePath;
|
|
83
|
+
}
|
|
84
|
+
return join(directory, "value-injection-loader.js");
|
|
85
|
+
}
|
|
86
|
+
//#endregion
|
|
87
|
+
export { configureBuild };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configure-build.mjs","names":[],"sources":["../../../src/internal/build/configure-build.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { basename, dirname, join, normalize, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { NextConfig } from \"next\";\n\nimport type { InterfereInjectedValues } from \"./injected.js\";\n\nconst INSTRUMENTATION_CLIENT_FILES = [\n \"instrumentation-client.ts\",\n \"instrumentation-client.tsx\",\n \"instrumentation-client.js\",\n \"instrumentation-client.jsx\",\n \"src/instrumentation-client.ts\",\n \"src/instrumentation-client.tsx\",\n \"src/instrumentation-client.js\",\n \"src/instrumentation-client.jsx\",\n] as const;\n\nconst LOADER_FILE_CANDIDATES = [\n \"value-injection-loader.ts\",\n \"value-injection-loader.js\",\n \"value-injection-loader.mjs\",\n \"value-injection-loader.cjs\",\n] as const;\n\ninterface ConfigureBuildInput {\n readonly existingWebpack: NextConfig[\"webpack\"] | undefined;\n readonly existingTurbopack: NextConfig[\"turbopack\"] | undefined;\n readonly projectDir: string;\n readonly values: InterfereInjectedValues;\n}\n\ninterface ConfigureBuildOutput {\n readonly webpack: NextConfig[\"webpack\"] | undefined;\n readonly turbopack: NextConfig[\"turbopack\"] | undefined;\n}\n\nexport function configureBuild({\n existingWebpack,\n existingTurbopack,\n projectDir,\n values,\n}: ConfigureBuildInput): ConfigureBuildOutput {\n const instrumentationClientPath = resolveInstrumentationClientPath(projectDir);\n\n return {\n webpack: buildWebpackHook(existingWebpack, instrumentationClientPath, values),\n turbopack: buildTurbopackConfig(\n existingTurbopack,\n instrumentationClientPath,\n values\n ),\n };\n}\n\nfunction hasInjectedMetadata(metadata: InterfereInjectedValues): boolean {\n return (\n metadata.__INTERFERE_BUILD_ID__ !== null ||\n metadata.__INTERFERE_RELEASE_ID__ !== null\n );\n}\n\nfunction buildWebpackHook(\n existingWebpack: NextConfig[\"webpack\"] | undefined,\n instrumentationClientPath: string | null,\n values: InterfereInjectedValues\n): NextConfig[\"webpack\"] | undefined {\n if (!(instrumentationClientPath && hasInjectedMetadata(values))) {\n return existingWebpack;\n }\n\n const normalizedPath = normalize(instrumentationClientPath);\n const loaderPath = resolveLoaderPath();\n\n return (webpackConfig, options) => {\n const outputConfig = existingWebpack\n ? existingWebpack(webpackConfig, options)\n : webpackConfig;\n const mutableConfig = outputConfig as {\n module?: { rules?: unknown[] };\n };\n\n const injectionRule = {\n test: (resource: string): boolean =>\n Boolean(resource) && normalize(resource) === normalizedPath,\n use: [\n {\n loader: loaderPath,\n options: { values },\n },\n ],\n };\n\n mutableConfig.module ??= {};\n mutableConfig.module.rules ??= [];\n mutableConfig.module.rules.push(injectionRule);\n\n return outputConfig;\n };\n}\n\nfunction buildTurbopackConfig(\n existingTurbopack: NextConfig[\"turbopack\"] | undefined,\n instrumentationClientPath: string | null,\n values: InterfereInjectedValues\n): NextConfig[\"turbopack\"] | undefined {\n if (!(instrumentationClientPath && hasInjectedMetadata(values))) {\n return existingTurbopack;\n }\n\n const baseConfig = (existingTurbopack ?? {}) as {\n rules?: Record<string, unknown>;\n };\n const ruleKey = `**/${basename(instrumentationClientPath)}`;\n const existingRule = (baseConfig.rules?.[ruleKey] ?? {}) as {\n loaders?: unknown[];\n };\n const existingLoaders = Array.isArray(existingRule.loaders)\n ? existingRule.loaders\n : [];\n\n return {\n ...baseConfig,\n rules: {\n ...baseConfig.rules,\n [ruleKey]: {\n ...existingRule,\n loaders: [\n ...existingLoaders,\n {\n loader: resolveLoaderPath(),\n options: {\n serializedValues: JSON.stringify(values),\n },\n },\n ],\n },\n },\n } as NextConfig[\"turbopack\"];\n}\n\nfunction resolveInstrumentationClientPath(projectDir: string): string | null {\n for (const candidate of INSTRUMENTATION_CLIENT_FILES) {\n const filePath = resolve(projectDir, candidate);\n if (existsSync(filePath)) {\n return filePath;\n }\n }\n\n return null;\n}\n\nfunction resolveLoaderPath(): string {\n const directory = resolve(dirname(fileURLToPath(import.meta.url)));\n\n for (const candidate of LOADER_FILE_CANDIDATES) {\n const filePath = join(directory, candidate);\n if (existsSync(filePath)) {\n return filePath;\n }\n }\n\n return join(directory, \"value-injection-loader.js\");\n}\n"],"mappings":";;;;AAOA,MAAM,+BAA+B;CACnC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AAED,MAAM,yBAAyB;CAC7B;CACA;CACA;CACA;CACD;AAcD,SAAgB,eAAe,EAC7B,iBACA,mBACA,YACA,UAC4C;CAC5C,MAAM,4BAA4B,iCAAiC,WAAW;AAE9E,QAAO;EACL,SAAS,iBAAiB,iBAAiB,2BAA2B,OAAO;EAC7E,WAAW,qBACT,mBACA,2BACA,OACD;EACF;;AAGH,SAAS,oBAAoB,UAA4C;AACvE,QACE,SAAS,2BAA2B,QACpC,SAAS,6BAA6B;;AAI1C,SAAS,iBACP,iBACA,2BACA,QACmC;AACnC,KAAI,EAAE,6BAA6B,oBAAoB,OAAO,EAC5D,QAAO;CAGT,MAAM,iBAAiB,UAAU,0BAA0B;CAC3D,MAAM,aAAa,mBAAmB;AAEtC,SAAQ,eAAe,YAAY;EACjC,MAAM,eAAe,kBACjB,gBAAgB,eAAe,QAAQ,GACvC;EACJ,MAAM,gBAAgB;EAItB,MAAM,gBAAgB;GACpB,OAAO,aACL,QAAQ,SAAS,IAAI,UAAU,SAAS,KAAK;GAC/C,KAAK,CACH;IACE,QAAQ;IACR,SAAS,EAAE,QAAQ;IACpB,CACF;GACF;AAED,gBAAc,WAAW,EAAE;AAC3B,gBAAc,OAAO,UAAU,EAAE;AACjC,gBAAc,OAAO,MAAM,KAAK,cAAc;AAE9C,SAAO;;;AAIX,SAAS,qBACP,mBACA,2BACA,QACqC;AACrC,KAAI,EAAE,6BAA6B,oBAAoB,OAAO,EAC5D,QAAO;CAGT,MAAM,aAAc,qBAAqB,EAAE;CAG3C,MAAM,UAAU,MAAM,SAAS,0BAA0B;CACzD,MAAM,eAAgB,WAAW,QAAQ,YAAY,EAAE;CAGvD,MAAM,kBAAkB,MAAM,QAAQ,aAAa,QAAQ,GACvD,aAAa,UACb,EAAE;AAEN,QAAO;EACL,GAAG;EACH,OAAO;GACL,GAAG,WAAW;IACb,UAAU;IACT,GAAG;IACH,SAAS,CACP,GAAG,iBACH;KACE,QAAQ,mBAAmB;KAC3B,SAAS,EACP,kBAAkB,KAAK,UAAU,OAAO,EACzC;KACF,CACF;IACF;GACF;EACF;;AAGH,SAAS,iCAAiC,YAAmC;AAC3E,MAAK,MAAM,aAAa,8BAA8B;EACpD,MAAM,WAAW,QAAQ,YAAY,UAAU;AAC/C,MAAI,WAAW,SAAS,CACtB,QAAO;;AAIX,QAAO;;AAGT,SAAS,oBAA4B;CACnC,MAAM,YAAY,QAAQ,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC,CAAC;AAElE,MAAK,MAAM,aAAa,wBAAwB;EAC9C,MAAM,WAAW,KAAK,WAAW,UAAU;AAC3C,MAAI,WAAW,SAAS,CACtB,QAAO;;AAIX,QAAO,KAAK,WAAW,4BAA4B"}
|