@interfere/next 11.2.1 → 12.0.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.
Files changed (97) hide show
  1. package/README.md +25 -25
  2. package/dist/config.d.mts +7 -8
  3. package/dist/config.mjs +1 -1
  4. package/dist/instrument-client.d.mts +5 -5
  5. package/dist/instrument-client.mjs +1 -1
  6. package/dist/instrumentation.d.mts +11 -8
  7. package/dist/instrumentation.edge.d.mts +3 -3
  8. package/dist/instrumentation.edge.mjs +1 -1
  9. package/dist/instrumentation.mjs +1 -1
  10. package/dist/internal/build/configure-build.d.mts +21 -15
  11. package/dist/internal/build/configure-build.mjs +1 -1
  12. package/dist/internal/build/detect-bundler.d.mts +3 -4
  13. package/dist/internal/build/pipeline.d.mts +40 -27
  14. package/dist/internal/build/pipeline.mjs +1 -1
  15. package/dist/internal/build/release/git.d.mts +2 -3
  16. package/dist/internal/build/release/index.d.mts +51 -5
  17. package/dist/internal/build/release/index.mjs +1 -1
  18. package/dist/internal/build/source-maps/discover-turbopack.d.mts +2 -3
  19. package/dist/internal/build/source-maps/discover-turbopack.mjs +1 -1
  20. package/dist/internal/build/source-maps/discover-webpack.d.mts +3 -4
  21. package/dist/internal/build/source-maps/discover-webpack.mjs +1 -1
  22. package/dist/internal/build/source-maps/discover.d.mts +9 -5
  23. package/dist/internal/build/source-maps/discover.mjs +1 -1
  24. package/dist/internal/build/source-maps/guard.d.mts +2 -3
  25. package/dist/internal/build/source-maps/guard.mjs +1 -1
  26. package/dist/internal/build/source-maps/paths.d.mts +5 -6
  27. package/dist/internal/build/source-maps/paths.mjs +1 -1
  28. package/dist/internal/build/telemetry.d.mts +5 -0
  29. package/dist/internal/build/telemetry.mjs +1 -0
  30. package/dist/internal/build/value-injection-loader.d.mts +5 -8
  31. package/dist/internal/build/value-injection-loader.mjs +2 -2
  32. package/dist/internal/build-marker-guard.d.mts +5 -6
  33. package/dist/internal/build-marker-guard.mjs +1 -1
  34. package/dist/internal/env.d.mts +13 -15
  35. package/dist/internal/env.mjs +1 -1
  36. package/dist/internal/logger.d.mts +5 -6
  37. package/dist/internal/logger.mjs +1 -1
  38. package/dist/internal/release-slug.d.mts +3 -5
  39. package/dist/internal/release-slug.mjs +1 -1
  40. package/dist/internal/server/cache-safe-trace-context.d.mts +24 -0
  41. package/dist/internal/server/cache-safe-trace-context.mjs +1 -0
  42. package/dist/internal/server/capture.d.mts +3 -4
  43. package/dist/internal/server/capture.mjs +1 -1
  44. package/dist/internal/server/console-bridge.d.mts +2 -3
  45. package/dist/internal/server/console-bridge.mjs +1 -1
  46. package/dist/internal/server/edge-exporter.d.mts +5 -6
  47. package/dist/internal/server/edge-exporter.mjs +1 -1
  48. package/dist/internal/server/freshness-propagator.d.mts +41 -0
  49. package/dist/internal/server/freshness-propagator.mjs +1 -0
  50. package/dist/internal/server/global-handlers.d.mts +4 -5
  51. package/dist/internal/server/global-handlers.mjs +1 -1
  52. package/dist/internal/server/id-generator.d.mts +2 -3
  53. package/dist/internal/server/instrumentation-options.d.mts +3 -4
  54. package/dist/internal/server/remote-config.d.mts +3 -4
  55. package/dist/internal/server/remote-config.mjs +1 -1
  56. package/dist/internal/server/server-action-marker.d.mts +10 -5
  57. package/dist/internal/server/server-action-marker.mjs +1 -1
  58. package/dist/internal/server/server-flush.d.mts +3 -4
  59. package/dist/internal/server/server-flush.mjs +1 -1
  60. package/dist/internal/server/static-generation.d.mts +17 -0
  61. package/dist/internal/server/static-generation.mjs +1 -0
  62. package/dist/internal/server/trace-meta.d.mts +13 -26
  63. package/dist/internal/server/trace-meta.mjs +1 -1
  64. package/dist/internal/server/traceparent.d.mts +2 -3
  65. package/dist/internal/server/traceparent.mjs +1 -1
  66. package/dist/internal/server/types.d.mts +5 -8
  67. package/dist/internal/server/wrap-proxy.d.mts +3 -4
  68. package/dist/internal/server/wrap-proxy.mjs +1 -1
  69. package/dist/internal/server/wrap-server-action.d.mts +2 -3
  70. package/dist/internal/server/wrap-server-action.mjs +1 -1
  71. package/dist/internal/setup-warnings.d.mts +3 -4
  72. package/dist/internal/setup-warnings.mjs +1 -1
  73. package/dist/internal/version.d.mts +2 -3
  74. package/dist/package.mjs +1 -1
  75. package/dist/provider.d.mts +3 -3
  76. package/dist/provider.mjs +1 -1
  77. package/package.json +49 -42
  78. package/dist/internal/build/release/destinations/cli.d.mts +0 -6
  79. package/dist/internal/build/release/destinations/cli.mjs +0 -1
  80. package/dist/internal/build/release/destinations/cloudflare.d.mts +0 -5
  81. package/dist/internal/build/release/destinations/cloudflare.mjs +0 -1
  82. package/dist/internal/build/release/destinations/index.d.mts +0 -14
  83. package/dist/internal/build/release/destinations/index.mjs +0 -1
  84. package/dist/internal/build/release/destinations/vercel.d.mts +0 -5
  85. package/dist/internal/build/release/destinations/vercel.mjs +0 -1
  86. package/dist/internal/build/release/sources/github.d.mts +0 -8
  87. package/dist/internal/build/release/sources/github.mjs +0 -1
  88. package/dist/internal/build/release/sources/index.d.mts +0 -20
  89. package/dist/internal/build/release/sources/index.mjs +0 -1
  90. package/dist/internal/build/source-maps/concurrency.d.mts +0 -4
  91. package/dist/internal/build/source-maps/concurrency.mjs +0 -1
  92. package/dist/internal/build/source-maps/index.d.mts +0 -6
  93. package/dist/internal/build/source-maps/index.mjs +0 -1
  94. package/dist/internal/build/source-maps/upload.d.mts +0 -39
  95. package/dist/internal/build/source-maps/upload.mjs +0 -1
  96. package/dist/internal/url.d.mts +0 -4
  97. package/dist/internal/url.mjs +0 -1
@@ -0,0 +1,17 @@
1
+ //#region src/internal/server/static-generation.d.ts
2
+ /**
3
+ * Whether the current render is a static-generation render — a build-time
4
+ * prerender or a runtime ISR revalidation. Trace context injected into such
5
+ * a render is baked into cached HTML and served to every visitor in the
6
+ * cache window, so injection paths that feed `clientTraceMetadata` must
7
+ * skip it, mirroring what Next itself produces for build-time prerenders
8
+ * (no meta at all).
9
+ *
10
+ * Reads Next's `workAsyncStorage` external — the same store Next consults
11
+ * for its own static/dynamic decisions, importable on both the Node.js and
12
+ * Edge runtimes. Outside any render (no store) this returns `false`; the
13
+ * client-side `tracestate` freshness guard remains the backstop for
14
+ * caching the server cannot see (CDN full-page caches).
15
+ */
16
+ export declare function isStaticGenerationRender(): boolean;
17
+ //#endregion
@@ -0,0 +1 @@
1
+ import{workAsyncStorage}from"next/dist/server/app-render/work-async-storage.external.js";function isStaticGenerationRender(){try{return workAsyncStorage.getStore()?.isStaticGeneration===!0}catch{return!1}}export{isStaticGenerationRender};
@@ -1,32 +1,19 @@
1
1
  //#region src/internal/server/trace-meta.d.ts
2
2
  /**
3
3
  * Server Component that emits a `<meta name="traceparent">` tag so the
4
- * client SDK can stitch every browser span onto the server-side trace.
4
+ * client SDK can stitch browser spans onto the server-side trace.
5
5
  *
6
- * Customer usage — drop into the root layout's `<head>`:
6
+ * @deprecated `withInterfere()` enables Next's native trace-meta injection
7
+ * (`experimental.clientTraceMetadata`), which also carries the `tracestate`
8
+ * freshness stamp that guards cached HTML — this component does not.
9
+ * Remove `<TraceMeta />` from your layout; with `withInterfere()` in your
10
+ * Next config the meta tags are injected for you. This export will be
11
+ * removed in the next major version.
7
12
  *
8
- * ```tsx
9
- * import { TraceMeta } from "@interfere/next/server";
10
- *
11
- * export default function RootLayout({ children }) {
12
- * return (
13
- * <html>
14
- * <head>
15
- * <TraceMeta />
16
- * </head>
17
- * <body>{children}</body>
18
- * </html>
19
- * );
20
- * }
21
- * ```
22
- *
23
- * Renders nothing when no OTel span is active (dev without OTel
24
- * registered, edge runtime where `@interfere/next/instrumentation`'s
25
- * async `register()` hasn't completed before the layout renders, or
26
- * unsampled traces). The client SDK's propagation reader handles a
27
- * missing meta tag the same as no parent — every browser span starts a
28
- * fresh root, which is at least internally consistent.
13
+ * Renders nothing when no sampled OTel span is active, and nothing during
14
+ * static-generation renders (build prerender, runtime ISR revalidation) —
15
+ * trace context baked into cached HTML would stitch every visitor in the
16
+ * cache window onto one stale trace.
29
17
  */
30
- declare function TraceMeta(): import("react").JSX.Element | null;
31
- //#endregion
32
- export { TraceMeta };
18
+ export declare function TraceMeta(): import("react").JSX.Element | null;
19
+ //#endregion
@@ -1 +1 @@
1
- import{activeTraceparent}from"./traceparent.mjs";import{jsx}from"react/jsx-runtime";function TraceMeta(){let traceparent=activeTraceparent();return traceparent?jsx(`meta`,{content:traceparent,name:`traceparent`}):null}export{TraceMeta};
1
+ import{isStaticGenerationRender}from"./static-generation.mjs";import{activeTraceparent}from"./traceparent.mjs";import{jsx}from"react/jsx-runtime";function TraceMeta(){if(isStaticGenerationRender())return null;let traceparent=activeTraceparent();return traceparent?jsx(`meta`,{content:traceparent,name:`traceparent`}):null}export{TraceMeta};
@@ -11,6 +11,5 @@
11
11
  * recorded server segments. Returning null lets the browser fall back
12
12
  * to its own root, which is at least internally consistent.
13
13
  */
14
- declare function activeTraceparent(): string | null;
15
- //#endregion
16
- export { activeTraceparent };
14
+ export declare function activeTraceparent(): string | null;
15
+ //#endregion
@@ -1 +1 @@
1
- import{TraceFlags,trace}from"@opentelemetry/api";const SAMPLED_BIT=TraceFlags.SAMPLED;function activeTraceparent(){let span=trace.getActiveSpan();if(!span)return null;let ctx=span.spanContext();if((ctx.traceFlags&SAMPLED_BIT)!==SAMPLED_BIT)return null;let flags=ctx.traceFlags.toString(16).padStart(2,`0`);return`00-${ctx.traceId}-${ctx.spanId}-${flags}`}export{activeTraceparent};
1
+ import{TraceFlags,trace}from"@opentelemetry/api";const SAMPLED_BIT=TraceFlags.SAMPLED;function activeTraceparent(){let span=trace.getActiveSpan();if(!span)return null;let ctx=span.spanContext();if((ctx.traceFlags&SAMPLED_BIT)===0)return null;let flags=ctx.traceFlags.toString(16).padStart(2,`0`);return`00-${ctx.traceId}-${ctx.spanId}-${flags}`}export{activeTraceparent};
@@ -1,15 +1,12 @@
1
+ import { ServerActionMarker } from "./server-action-marker.mjs";
1
2
  import { NextjsContext } from "@interfere/types/sdk/plugins/context/next";
2
3
  import { ErrorMechanism } from "@interfere/types/sdk/plugins/payload/errors";
3
4
  //#region src/internal/server/types.d.ts
4
- type OnRequestErrorContext = Omit<NextjsContext, "errorDigest" | "requestMethod" | "requestPath" | "runtime">;
5
- interface CaptureErrorContext {
5
+ export type OnRequestErrorContext = Omit<NextjsContext, "errorDigest" | "requestMethod" | "requestPath" | "runtime">;
6
+ export interface CaptureErrorContext {
6
7
  readonly mechanism?: ErrorMechanism;
7
8
  readonly nextjs?: Omit<NextjsContext, "runtime">;
8
9
  readonly requestHeaders?: Readonly<Record<string, string>>;
9
- readonly serverAction?: {
10
- readonly name: string;
11
- readonly argsShape?: string;
12
- };
10
+ readonly serverAction?: ServerActionMarker;
13
11
  }
14
- //#endregion
15
- export { CaptureErrorContext, OnRequestErrorContext };
12
+ //#endregion
@@ -1,4 +1,4 @@
1
- import { NextMiddleware } from "next/server.js";
1
+ import { NextMiddleware, NextRequest } from "next/server";
2
2
  //#region src/internal/server/wrap-proxy.d.ts
3
3
  /**
4
4
  * Wraps a Next.js proxy (formerly `middleware`) so thrown errors are captured
@@ -16,6 +16,5 @@ import { NextMiddleware } from "next/server.js";
16
16
  * });
17
17
  * ```
18
18
  */
19
- declare function withInterfereProxy(proxy: NextMiddleware): NextMiddleware;
20
- //#endregion
21
- export { withInterfereProxy };
19
+ export declare function withInterfereProxy(proxy: NextMiddleware): (request: NextRequest, event: import("next/server.js").NextFetchEvent) => void | Promise<import("next/dist/server/web/types.js").NextMiddlewareResult> | Response | null;
20
+ //#endregion
@@ -1 +1 @@
1
- import{captureError}from"./capture.mjs";import{MECHANISM_TYPE}from"@interfere/types/sdk/errors";function withInterfereProxy(proxy){return(request,event)=>{try{let result=proxy(request,event);return result instanceof Promise?result.catch(error=>{throw reportProxyError(error,request),error}):result}catch(error){throw reportProxyError(error,request),error}}}function reportProxyError(error,request){captureError(error,request,{mechanism:{type:MECHANISM_TYPE.nextjs.proxy,handled:!1},nextjs:{requestMethod:request.method,requestPath:request.nextUrl.pathname}})}export{withInterfereProxy};
1
+ import{captureError}from"./capture.mjs";import{MECHANISM_TYPE}from"@interfere/types/sdk/errors";function withInterfereProxy(proxy){return((request,event)=>{try{let result=proxy(request,event);return result instanceof Promise?result.catch(error=>{throw reportProxyError(error,request),error}):result}catch(error){throw reportProxyError(error,request),error}})}function reportProxyError(error,request){captureError(error,request,{mechanism:{handled:!1,type:MECHANISM_TYPE.nextjs.proxy},nextjs:{requestMethod:request.method,requestPath:request.nextUrl.pathname}})}export{withInterfereProxy};
@@ -29,6 +29,5 @@ interface ServerActionOptions {
29
29
  * }
30
30
  * ```
31
31
  */
32
- declare function withInterfereServerAction<T>(name: string, action: () => T | Promise<T>, options?: ServerActionOptions): Promise<T>;
33
- //#endregion
34
- export { withInterfereServerAction };
32
+ export declare function withInterfereServerAction<T>(name: string, action: () => T | Promise<T>, options?: ServerActionOptions): Promise<T>;
33
+ //#endregion
@@ -1 +1 @@
1
- import{markServerAction}from"./server-action-marker.mjs";import{SpanStatusCode,trace}from"@opentelemetry/api";function withInterfereServerAction(name,action,options){return trace.getTracer(`@interfere/next/server`).startActiveSpan(`serverAction ${name}`,async span=>{span.setAttribute(`interfere.server_action.name`,name);let argsShape=options?.args?describeArgsShape(options.args):null;argsShape&&span.setAttribute(`interfere.server_action.args_shape`,argsShape);try{let result=await action();return span.setAttribute(`interfere.server_action.outcome`,`success`),result}catch(error){throw span.setAttribute(`interfere.server_action.outcome`,`error`),span.setStatus({code:SpanStatusCode.ERROR}),markServerAction(error,{name,...argsShape?{argsShape}:{}}),error}finally{span.end()}})}function describeArgsShape(args){return args.map(arg=>arg===null?`null`:arg===void 0?`undefined`:typeof arg==`object`?arg.constructor?.name??`object`:typeof arg).join(`,`)}export{withInterfereServerAction};
1
+ import{markServerAction}from"./server-action-marker.mjs";import{omitUndefined}from"@interfere/helpers/omit-undefined";import{SpanStatusCode,trace}from"@opentelemetry/api";async function withInterfereServerAction(name,action,options){return trace.getTracer(`@interfere/next/server`).startActiveSpan(`serverAction ${name}`,async span=>{span.setAttribute(`interfere.server_action.name`,name);let argsShape=options?.args?describeArgsShape(options.args):null;argsShape&&span.setAttribute(`interfere.server_action.args_shape`,argsShape);try{let result=await action();return span.setAttribute(`interfere.server_action.outcome`,`success`),result}catch(error){throw span.setAttribute(`interfere.server_action.outcome`,`error`),span.setStatus({code:SpanStatusCode.ERROR}),markServerAction(error,omitUndefined({name,argsShape:argsShape||void 0})),error}finally{span.end()}})}function describeArgsShape(args){return args.map(arg=>arg===null?`null`:arg===void 0?`undefined`:typeof arg==`object`?arg.constructor?.name??`object`:typeof arg).join(`,`)}export{withInterfereServerAction};
@@ -6,12 +6,11 @@
6
6
  * them. Never blocks the build: some customers genuinely don't want
7
7
  * server-side tracing.
8
8
  */
9
- declare function warnIfServerInstrumentationMissing(projectDir: string): void;
9
+ export declare function warnIfServerInstrumentationMissing(projectDir: string): void;
10
10
  /**
11
11
  * Reads the customer's installed `next` package version. Returns `null` if
12
12
  * Next isn't resolvable from `projectDir` (e.g. running outside a Next app),
13
13
  * in which case callers should skip version-conditional behaviour.
14
14
  */
15
- declare function readNextMajorVersion(projectDir: string): number | null;
16
- //#endregion
17
- export { readNextMajorVersion, warnIfServerInstrumentationMissing };
15
+ export declare function readNextMajorVersion(projectDir: string): number | null;
16
+ //#endregion
@@ -1 +1 @@
1
- import{log}from"./logger.mjs";import{createRequire}from"node:module";import{existsSync}from"node:fs";import path from"node:path";const SERVER_INSTRUMENTATION_FILES=[`instrumentation.ts`,`instrumentation.tsx`,`instrumentation.js`,`instrumentation.jsx`,`src/instrumentation.ts`,`src/instrumentation.tsx`,`src/instrumentation.js`,`src/instrumentation.jsx`];function hasServerInstrumentation(projectDir){return SERVER_INSTRUMENTATION_FILES.some(c=>existsSync(path.resolve(projectDir,c)))}function warnIfServerInstrumentationMissing(projectDir){hasServerInstrumentation(projectDir)||log.warn(`No instrumentation.ts found`,[`Server-side traces will be skipped. To enable, create instrumentation.ts at the project root with:`,` export { register } from '@interfere/next/instrumentation';`])}function readNextMajorVersion(projectDir){try{let pkg=createRequire(`${projectDir}/_`)(`next/package.json`),major=Number.parseInt(pkg.version?.split(`.`)[0]??``,10);return Number.isFinite(major)?major:null}catch{return null}}export{readNextMajorVersion,warnIfServerInstrumentationMissing};
1
+ import{log}from"./logger.mjs";import{createRequire}from"node:module";import{existsSync}from"node:fs";import path from"node:path";const SERVER_INSTRUMENTATION_FILES=[`instrumentation.ts`,`instrumentation.tsx`,`instrumentation.js`,`instrumentation.jsx`,`src/instrumentation.ts`,`src/instrumentation.tsx`,`src/instrumentation.js`,`src/instrumentation.jsx`];function hasServerInstrumentation(projectDir){return SERVER_INSTRUMENTATION_FILES.some(c=>existsSync(path.resolve(projectDir,c)))}function warnIfServerInstrumentationMissing(projectDir){hasServerInstrumentation(projectDir)||log.warn(`No instrumentation.ts found`,[`Server-side traces will be skipped. To enable, create instrumentation.ts at the project root with:`,` export { register } from '@interfere/next/instrumentation';`])}function readNextMajorVersion(projectDir){try{let pkg=createRequire(`${projectDir}/_`)(`next/package.json`);if(typeof pkg!=`object`||!pkg||!(`version`in pkg)||typeof pkg.version!=`string`)return null;let major=Number.parseInt(pkg.version.split(`.`)[0]??``,10);return Number.isFinite(major)?major:null}catch{return null}}export{readNextMajorVersion,warnIfServerInstrumentationMissing};
@@ -1,4 +1,3 @@
1
1
  //#region src/internal/version.d.ts
2
- declare const PRODUCER_VERSION: string;
3
- //#endregion
4
- export { PRODUCER_VERSION };
2
+ export declare const PRODUCER_VERSION: string;
3
+ //#endregion
package/dist/package.mjs CHANGED
@@ -1 +1 @@
1
- var name=`@interfere/next`,version=`11.2.1`;export{name,version};
1
+ var name=`@interfere/next`,version=`12.0.1`;export{name,version};
@@ -1,5 +1,5 @@
1
1
  import { PluginOverrides, useInterfere, useSession } from "@interfere/react/provider";
2
- import { PropsWithChildren, ReactNode } from "react";
2
+ import { PropsWithChildren } from "react";
3
3
  import { ConsentCategory, ConsentState, ConsentState as ConsentState$1, GateableCategory } from "@interfere/types/sdk/plugins/manifest";
4
4
  //#region src/provider.d.ts
5
5
  interface InterfereProviderProps extends PropsWithChildren {
@@ -15,6 +15,6 @@ interface InterfereProviderProps extends PropsWithChildren {
15
15
  * (`init()` is client-only); the core provider mounts with null-safe
16
16
  * accessors and switches to the real kernel on client hydration.
17
17
  */
18
- declare function InterfereProvider({ children, consent, plugins, errorBoundary }: InterfereProviderProps): ReactNode;
18
+ export declare function InterfereProvider({ children, consent, plugins, errorBoundary }: InterfereProviderProps): import("react").JSX.Element;
19
19
  //#endregion
20
- export { type ConsentCategory, type ConsentState, type GateableCategory, InterfereProvider, useInterfere, useSession };
20
+ export { type ConsentCategory, type ConsentState, type GateableCategory, useInterfere, useSession };
package/dist/provider.mjs CHANGED
@@ -1 +1 @@
1
- "use client";import{getKernelOrNull,subscribeToKernel}from"./instrument-client.mjs";import{omitUndefined}from"@interfere/helpers/omit-undefined";import{InterfereProvider as InterfereProvider$1,useInterfere,useSession}from"@interfere/react/provider";import{useSyncExternalStore}from"react";import{jsx}from"react/jsx-runtime";const nullSnapshot=()=>null;function InterfereProvider({children,consent,plugins,errorBoundary}){let kernel=useSyncExternalStore(subscribeToKernel,getKernelOrNull,nullSnapshot);return jsx(InterfereProvider$1,{kernel,...omitUndefined({consent,plugins,errorBoundary}),children})}export{InterfereProvider,useInterfere,useSession};
1
+ "use client";import{getKernelOrNull,subscribeToKernel}from"./instrument-client.mjs";import{omitUndefined}from"@interfere/helpers/omit-undefined";import{InterfereProvider as InterfereProvider$1,useInterfere,useSession}from"@interfere/react/provider";import{useSyncExternalStore}from"react";import{jsx}from"react/jsx-runtime";const nullSnapshot=()=>null;function InterfereProvider({children,consent,plugins,errorBoundary}){let kernel=useSyncExternalStore(subscribeToKernel,getKernelOrNull,nullSnapshot);return jsx(InterfereProvider$1,{kernel,...omitUndefined({consent,errorBoundary,plugins}),children})}export{InterfereProvider,useInterfere,useSession};
package/package.json CHANGED
@@ -1,19 +1,19 @@
1
1
  {
2
2
  "name": "@interfere/next",
3
- "version": "11.2.1",
4
- "license": "MIT",
3
+ "version": "12.0.1",
5
4
  "description": "Build software that never breaks.",
6
5
  "keywords": [
6
+ "error-tracking",
7
+ "nextjs",
7
8
  "observability",
8
- "typescript",
9
9
  "react",
10
- "nextjs",
11
- "error-tracking"
10
+ "typescript"
12
11
  ],
13
12
  "homepage": "https://interfere.com",
14
13
  "bugs": {
15
14
  "url": "mailto:support@interfere.com"
16
15
  },
16
+ "license": "MIT",
17
17
  "author": "Interfere <support@interfere.com> (https://interfere.com)",
18
18
  "repository": {
19
19
  "type": "git",
@@ -24,6 +24,7 @@
24
24
  "dist"
25
25
  ],
26
26
  "type": "module",
27
+ "sideEffects": false,
27
28
  "exports": {
28
29
  "./config": {
29
30
  "monorepo": "./src/config.ts",
@@ -83,36 +84,60 @@
83
84
  "default": "./dist/server.mjs"
84
85
  }
85
86
  },
86
- "sideEffects": false,
87
87
  "publishConfig": {
88
88
  "access": "public"
89
89
  },
90
90
  "scripts": {
91
91
  "build": "tsdown",
92
92
  "test": "vitest run",
93
+ "test:e2e": "playwright test",
93
94
  "typecheck": "tsc --noEmit"
94
95
  },
95
96
  "dependencies": {
96
- "@interfere/constants": "^9.0.6",
97
- "@interfere/helpers": "^9.1.2",
98
- "@interfere/react": "^11.2.1",
99
- "@interfere/sdk": "^11.2.1",
100
- "@interfere/types": "^10.1.2",
97
+ "@interfere/build": "^0.2.1",
98
+ "@interfere/constants": "^9.1.0",
99
+ "@interfere/helpers": "^9.2.0",
100
+ "@interfere/javascript": "^0.2.1",
101
+ "@interfere/react": "^12.0.1",
102
+ "@interfere/types": "^10.2.0",
101
103
  "@opentelemetry/api": "^1.9.1",
102
- "@opentelemetry/api-logs": "^0.221.0",
104
+ "@opentelemetry/api-logs": "^0.222.0",
103
105
  "@opentelemetry/baggage-span-processor": "^0.5.0",
104
- "@opentelemetry/context-async-hooks": "^2.7.1",
105
- "@opentelemetry/core": "^2.7.0",
106
- "@opentelemetry/exporter-logs-otlp-http": "^0.221.0",
107
- "@opentelemetry/exporter-trace-otlp-http": "^0.221.0",
108
- "@opentelemetry/instrumentation": "^0.221.0",
109
- "@opentelemetry/otlp-exporter-base": "^0.221.0",
110
- "@opentelemetry/otlp-transformer": "^0.221.0",
111
- "@opentelemetry/resources": "^2.7.0",
112
- "@opentelemetry/sdk-logs": "^0.221.0",
113
- "@opentelemetry/sdk-trace-base": "^2.7.0",
114
- "@opentelemetry/sdk-trace-node": "^2.7.1",
115
- "chalk": "^6.0.0"
106
+ "@opentelemetry/context-async-hooks": "^2.11.0",
107
+ "@opentelemetry/core": "^2.11.0",
108
+ "@opentelemetry/exporter-logs-otlp-http": "^0.222.0",
109
+ "@opentelemetry/exporter-trace-otlp-http": "^0.222.0",
110
+ "@opentelemetry/instrumentation": "^0.222.0",
111
+ "@opentelemetry/otlp-exporter-base": "^0.222.0",
112
+ "@opentelemetry/otlp-transformer": "^0.222.0",
113
+ "@opentelemetry/resources": "^2.11.0",
114
+ "@opentelemetry/sdk-logs": "^0.222.0",
115
+ "@opentelemetry/sdk-trace-base": "^2.11.0",
116
+ "@opentelemetry/sdk-trace-node": "^2.11.0",
117
+ "chalk": "^6.0.0",
118
+ "zod": "^4.5.4"
119
+ },
120
+ "devDependencies": {
121
+ "@interfere/typescript-config": "^9.0.2",
122
+ "@interfere/vitest-config": "0.0.0",
123
+ "@playwright/test": "^1.63.0",
124
+ "@types/node": "^26.4.1",
125
+ "@types/react": "19.2.18",
126
+ "@types/react-dom": "19.2.7",
127
+ "@vitejs/plugin-react": "^6.1.1",
128
+ "@vitest/browser": "5.0.1",
129
+ "@vitest/browser-playwright": "5.0.1",
130
+ "@vitest/coverage-v8": "5.0.1",
131
+ "msw": "^2.14.6",
132
+ "next": "^16.3.8",
133
+ "playwright": "^1.63.0",
134
+ "react": "19.3.0",
135
+ "react-dom": "19.3.0",
136
+ "tsdown": "^0.23.0",
137
+ "typescript": "^7.0.2",
138
+ "vitest": "5.0.1",
139
+ "vitest-browser-react": "2.3.0",
140
+ "webpack": "^5.111.1"
116
141
  },
117
142
  "peerDependencies": {
118
143
  "next": "^15.3.0 || ^16.0.0",
@@ -129,23 +154,5 @@
129
154
  "react-dom": {
130
155
  "optional": true
131
156
  }
132
- },
133
- "devDependencies": {
134
- "@interfere/test-utils": "^9.1.2",
135
- "@interfere/typescript-config": "^9.0.2",
136
- "@types/node": "^26.2.0",
137
- "@types/react": "19.2.18",
138
- "@types/react-dom": "19.2.4",
139
- "@vitest/browser": "4.1.10",
140
- "@vitest/browser-playwright": "4.1.10",
141
- "@vitest/coverage-v8": "4.1.10",
142
- "next": "^16.3.0",
143
- "playwright": "^1.62.1",
144
- "react": "^19.2.6",
145
- "react-dom": "19.2.8",
146
- "tsdown": "^0.22.2",
147
- "typescript": "^7.0.2",
148
- "vitest": "4.1.10",
149
- "vitest-browser-react": "2.2.0"
150
157
  }
151
158
  }
@@ -1,6 +0,0 @@
1
- import { ReleaseSlug } from "@interfere/types/releases/slug";
2
- import { ReleaseDestinationMetadata } from "@interfere/types/integrations";
3
- //#region src/internal/build/release/destinations/cli.d.ts
4
- declare function resolve(releaseSlug: ReleaseSlug): ReleaseDestinationMetadata;
5
- //#endregion
6
- export { resolve };
@@ -1 +0,0 @@
1
- import{resolveCliReleaseDestination}from"@interfere/types/sdk/cli-destination";function resolve(releaseSlug){return resolveCliReleaseDestination(process.env,releaseSlug)}export{resolve};
@@ -1,5 +0,0 @@
1
- import { ReleaseDestinationMetadata } from "@interfere/types/integrations";
2
- //#region src/internal/build/release/destinations/cloudflare.d.ts
3
- declare function resolve(): ReleaseDestinationMetadata;
4
- //#endregion
5
- export { resolve };
@@ -1 +0,0 @@
1
- import{parseEnvValue}from"@interfere/types/sdk/env";function parseVersionMetadata(){let raw=parseEnvValue(process.env.CF_VERSION_METADATA);if(!raw)return null;try{return JSON.parse(raw)}catch{return null}}function resolveDeploymentUrl(){let candidate=parseEnvValue(process.env.CF_PAGES_URL)??parseEnvValue(process.env.WORKERS_CI_DEPLOY_URL);return candidate===null?null:candidate.startsWith(`https://`)||candidate.startsWith(`http://`)?candidate:`https://${candidate}`}function resolve(){let versionMetadata=parseVersionMetadata(),versionId=parseEnvValue(process.env.CF_VERSION_ID)??versionMetadata?.id??parseEnvValue(process.env.WORKERS_CI_BUILD_UUID)??null,scriptName=parseEnvValue(process.env.CF_WORKER_NAME)??parseEnvValue(process.env.WORKERS_CI_SCRIPT_NAME)??null,accountId=parseEnvValue(process.env.CF_ACCOUNT_ID)??parseEnvValue(process.env.CLOUDFLARE_ACCOUNT_ID)??null,environment=parseEnvValue(process.env.WORKERS_CI_BRANCH)??versionMetadata?.tag??null;return{provider:`cloudflare`,destinationReleaseId:versionId,environment,versionId,scriptName,accountId,deploymentUrl:resolveDeploymentUrl(),environmentName:environment,environmentTarget:environment}}export{resolve};
@@ -1,14 +0,0 @@
1
- import { ReleaseSlug } from "@interfere/types/releases/slug";
2
- import { ReleaseDestinationMetadata, ReleaseDestinationProvider } from "@interfere/types/integrations";
3
- //#region src/internal/build/release/destinations/index.d.ts
4
- /**
5
- * Per-provider destination metadata resolvers. Mirrors `sources/` —
6
- * each entry knows how to read its platform's deployment env vars and
7
- * returns a `ReleaseDestinationMetadata` blob.
8
- *
9
- * Adding a new destination (fly, render, …) is one new file under this
10
- * directory + one entry here.
11
- */
12
- declare const destinations: Record<ReleaseDestinationProvider, (releaseSlug: ReleaseSlug) => ReleaseDestinationMetadata>;
13
- //#endregion
14
- export { destinations };
@@ -1 +0,0 @@
1
- import{resolve}from"./cli.mjs";import{resolve as resolve$1}from"./cloudflare.mjs";import{resolve as resolve$2}from"./vercel.mjs";const destinations={vercel:resolve$2,cloudflare:resolve$1,cli:resolve};export{destinations};
@@ -1,5 +0,0 @@
1
- import { ReleaseDestinationMetadata } from "@interfere/types/integrations";
2
- //#region src/internal/build/release/destinations/vercel.d.ts
3
- declare function resolve(): ReleaseDestinationMetadata;
4
- //#endregion
5
- export { resolve };
@@ -1 +0,0 @@
1
- import{parseEnvValue}from"@interfere/types/sdk/env";function resolve(){let environment=parseEnvValue(process.env.VERCEL_ENV??process.env.VERCEL_TARGET_ENV),deploymentId=parseEnvValue(process.env.VERCEL_DEPLOYMENT_ID);return{provider:`vercel`,destinationReleaseId:deploymentId,environment,deploymentId,deploymentUrl:resolveDeploymentUrl(),environmentName:environment,environmentTarget:environment}}function resolveDeploymentUrl(){let deploymentUrl=parseEnvValue(process.env.VERCEL_URL);return deploymentUrl===null?null:deploymentUrl.startsWith(`https://`)||deploymentUrl.startsWith(`http://`)?deploymentUrl:`https://${deploymentUrl}`}export{resolve};
@@ -1,8 +0,0 @@
1
- import { ReleaseSourceMetadata } from "@interfere/types/integrations";
2
- //#region src/internal/build/release/sources/github.d.ts
3
- type GithubReleaseSource = Extract<ReleaseSourceMetadata, {
4
- provider: "github";
5
- }>;
6
- declare function resolve(): GithubReleaseSource;
7
- //#endregion
8
- export { resolve };
@@ -1 +0,0 @@
1
- import{runGitCommand}from"../git.mjs";import{resolveGithubReleaseSource}from"@interfere/types/sdk/github-source";function resolve(){return resolveGithubReleaseSource(process.env,runGitCommand)}export{resolve};
@@ -1,20 +0,0 @@
1
- import { ReleaseSourceMetadata, SourceProvider } from "@interfere/types/integrations";
2
- //#region src/internal/build/release/sources/index.d.ts
3
- /**
4
- * Per-provider source metadata resolvers. Each entry knows how to read
5
- * its platform's env vars (and falls back to local git where applicable)
6
- * and returns a `ReleaseSourceMetadata` blob keyed to the provider.
7
- *
8
- * Adding a new provider (gitlab, bitbucket, …) is one new file under
9
- * this directory + one entry here. Every consumer that walks this
10
- * registry — release-create payload assembly in the build pipeline —
11
- * picks up the new provider for free.
12
- *
13
- * Lives under `internal/build/` because resolvers shell out to git via
14
- * `runGitCommand`, which depends on `node:child_process` (not Edge-safe).
15
- * Anything imported from `instrumentation.ts` must stay edge-safe; this
16
- * tree is build-only.
17
- */
18
- declare const sources: Record<SourceProvider, () => ReleaseSourceMetadata>;
19
- //#endregion
20
- export { sources };
@@ -1 +0,0 @@
1
- import{resolve}from"./github.mjs";const sources={github:resolve,ghe_cloud:resolve,ghe_server:resolve};export{sources};
@@ -1,4 +0,0 @@
1
- //#region src/internal/build/source-maps/concurrency.d.ts
2
- declare function mapWithConcurrency<T, R>(items: readonly T[], concurrency: number, fn: (item: T, index: number) => Promise<R>): Promise<R[]>;
3
- //#endregion
4
- export { mapWithConcurrency };
@@ -1 +0,0 @@
1
- async function mapWithConcurrency(items,concurrency,fn){let results=Array(items.length),pending=items.entries();async function worker(){for(let[index,item]of pending)results[index]=await fn(item,index)}return await Promise.all(Array.from({length:Math.max(1,Math.min(concurrency,items.length))},()=>worker())),results}export{mapWithConcurrency};
@@ -1,6 +0,0 @@
1
- import { SourceMapFile } from "./discover.mjs";
2
- import { uploadSourceMaps } from "./upload.mjs";
3
- //#region src/internal/build/source-maps/index.d.ts
4
- declare function cleanupSourceMaps(files: SourceMapFile[]): Promise<void>;
5
- //#endregion
6
- export { cleanupSourceMaps, uploadSourceMaps };
@@ -1 +0,0 @@
1
- import{log}from"../../logger.mjs";import{uploadSourceMaps}from"./upload.mjs";import{unlink}from"node:fs/promises";async function cleanupSourceMaps(files){let results=await Promise.allSettled(files.map(f=>unlink(f.absolute))),failures=[];for(let[i,r]of results.entries()){if(r.status!==`rejected`)continue;let err=r.reason;if(err.code===`ENOENT`)continue;let file=files[i];failures.push(`${file?.absolute??`<unknown>`}: ${err.code??err.message??String(err)}`)}failures.length>0&&log.warn(`Source-map cleanup left files on disk`,failures)}export{cleanupSourceMaps,uploadSourceMaps};
@@ -1,39 +0,0 @@
1
- import { DiscoveryResult } from "./discover.mjs";
2
- import { ReleaseSlug } from "@interfere/types/releases/slug";
3
- import { HTTPClient } from "@interfere/sdk";
4
- import { ManifestBundler } from "@interfere/types/data/source-maps";
5
- //#region src/internal/build/source-maps/upload.d.ts
6
- interface UploadResult {
7
- fileCount: number;
8
- totalBytes: number;
9
- }
10
- interface UploadParams {
11
- apiUrl: string;
12
- bundler: ManifestBundler;
13
- discovered: DiscoveryResult;
14
- httpClient: HTTPClient;
15
- releaseSlug: ReleaseSlug;
16
- }
17
- /**
18
- * Drives the three-step presigned-URL flow:
19
- *
20
- * 1. POST `/v1/releases/:slug/source-maps/sign` with per-file metadata.
21
- * 2. PUT each file's content directly to R2 in parallel (concurrency
22
- * bounded so a build with 1000+ chunks doesn't open 1000 sockets).
23
- * 3. POST `/v1/releases/:slug/source-maps/complete` to materialize the
24
- * manifest and update the release row.
25
- *
26
- * The collector never sees source-map bytes — uploads scale with R2
27
- * limits, not Cloudflare worker / load-balancer body-size caps. The
28
- * legacy multipart endpoint is still wired for older SDKs.
29
- */
30
- declare function uploadSourceMaps({ apiUrl, bundler, discovered, httpClient, releaseSlug }: UploadParams): Promise<UploadResult>;
31
- interface SourceMapRichness {
32
- hasSourcesContent: boolean;
33
- hasNames: boolean;
34
- hasFile: boolean;
35
- mappingsPresent: boolean;
36
- }
37
- declare function extractRichness(content: string): SourceMapRichness;
38
- //#endregion
39
- export { UploadResult, extractRichness, uploadSourceMaps };
@@ -1 +0,0 @@
1
- import{mapWithConcurrency}from"./concurrency.mjs";async function uploadSourceMaps({apiUrl,bundler,discovered,httpClient,releaseSlug}){let filesByPath=new Map(discovered.files.map(f=>[f.path,f])),signRequest={files:discovered.files.map(file=>({path:file.path,sizeBytes:byteLengthOf(file.content),...extractRichness(file.content)}))},signResponse=await postJson(httpClient,new URL(`/v1/releases/${encodeURIComponent(releaseSlug)}/source-maps/sign`,apiUrl),signRequest),totalBytes=0;await mapWithConcurrency(signResponse.uploads,64,async upload=>{let file=filesByPath.get(upload.path);if(!file)throw Error(`Sign response referenced unknown path "${upload.path}"`);totalBytes+=byteLengthOf(file.content),await putToR2(upload.presignedUrl,file.content)});let completeRequest={files:discovered.files.map(file=>({path:file.path,hash:file.hash,debugId:file.debugId,chunkUrl:file.chunkUrl})),sourceFileCount:discovered.sourceFileCount,bundler};return{fileCount:(await postJson(httpClient,new URL(`/v1/releases/${encodeURIComponent(releaseSlug)}/source-maps/complete`,apiUrl),completeRequest)).fileCount,totalBytes}}function byteLengthOf(content){return new TextEncoder().encode(content).byteLength}const EMPTY_RICHNESS={hasSourcesContent:!1,hasNames:!1,hasFile:!1,mappingsPresent:!1};function leafRichness(map){let sourcesContent=map.sourcesContent,names=map.names,file=map.file,mappings=map.mappings;return{hasSourcesContent:Array.isArray(sourcesContent)&&sourcesContent.length>0,hasNames:Array.isArray(names)&&names.length>0,hasFile:typeof file==`string`&&file.length>0,mappingsPresent:typeof mappings==`string`&&mappings.length>0}}function extractRichness(content){let parsed;try{parsed=JSON.parse(content)}catch{return EMPTY_RICHNESS}if(!(parsed&&typeof parsed==`object`))return EMPTY_RICHNESS;let top=parsed,sections=top.sections;if(!Array.isArray(sections))return leafRichness(top);let merged={hasSourcesContent:!1,hasNames:!1,hasFile:typeof top.file==`string`&&top.file.length>0,mappingsPresent:!1};for(let section of sections){let inner=section?.map;if(!(inner&&typeof inner==`object`))continue;let r=leafRichness(inner);merged.hasSourcesContent||=r.hasSourcesContent,merged.hasNames||=r.hasNames,merged.mappingsPresent||=r.mappingsPresent}return merged}async function postJson(httpClient,url,body){let response=await httpClient.request(new Request(url,{method:`POST`,headers:{"content-type":`application/json`,accept:`application/json`},body:JSON.stringify(body)}));if(!response.ok){let detail=await response.text().catch(()=>``);throw Error(`Source-map API request failed: POST ${url.pathname} -> ${response.status} ${detail}`)}return await response.json()}async function putToR2(presignedUrl,content){let response=await fetch(presignedUrl,{method:`PUT`,headers:{"content-type":`application/json`},body:content});if(!response.ok){let detail=await response.text().catch(()=>``);throw Error(`Release metadata file upload failed: ${response.status} ${detail}`)}}export{extractRichness,uploadSourceMaps};
@@ -1,4 +0,0 @@
1
- //#region src/internal/url.d.ts
2
- declare function withPublicKey(url: string, publicKey: string): string;
3
- //#endregion
4
- export { withPublicKey };
@@ -1 +0,0 @@
1
- function withPublicKey(url,publicKey){let next=new URL(url);return next.searchParams.set(`pk`,publicKey),next.toString()}export{withPublicKey};