@interfere/next 11.2.1 → 12.0.0
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 +25 -25
- package/dist/config.d.mts +7 -8
- package/dist/config.mjs +1 -1
- package/dist/instrument-client.d.mts +5 -5
- package/dist/instrument-client.mjs +1 -1
- package/dist/instrumentation.d.mts +11 -8
- package/dist/instrumentation.edge.d.mts +3 -3
- package/dist/instrumentation.edge.mjs +1 -1
- package/dist/instrumentation.mjs +1 -1
- package/dist/internal/build/configure-build.d.mts +21 -15
- package/dist/internal/build/configure-build.mjs +1 -1
- package/dist/internal/build/detect-bundler.d.mts +3 -4
- package/dist/internal/build/pipeline.d.mts +40 -27
- package/dist/internal/build/pipeline.mjs +1 -1
- package/dist/internal/build/release/git.d.mts +2 -3
- package/dist/internal/build/release/index.d.mts +51 -5
- package/dist/internal/build/release/index.mjs +1 -1
- package/dist/internal/build/source-maps/discover-turbopack.d.mts +2 -3
- package/dist/internal/build/source-maps/discover-turbopack.mjs +1 -1
- package/dist/internal/build/source-maps/discover-webpack.d.mts +3 -4
- package/dist/internal/build/source-maps/discover-webpack.mjs +1 -1
- package/dist/internal/build/source-maps/discover.d.mts +9 -5
- package/dist/internal/build/source-maps/discover.mjs +1 -1
- package/dist/internal/build/source-maps/guard.d.mts +2 -3
- package/dist/internal/build/source-maps/guard.mjs +1 -1
- package/dist/internal/build/source-maps/paths.d.mts +5 -6
- package/dist/internal/build/source-maps/paths.mjs +1 -1
- package/dist/internal/build/telemetry.d.mts +5 -0
- package/dist/internal/build/telemetry.mjs +1 -0
- package/dist/internal/build/value-injection-loader.d.mts +5 -8
- package/dist/internal/build/value-injection-loader.mjs +2 -2
- package/dist/internal/build-marker-guard.d.mts +5 -6
- package/dist/internal/build-marker-guard.mjs +1 -1
- package/dist/internal/env.d.mts +13 -15
- package/dist/internal/env.mjs +1 -1
- package/dist/internal/logger.d.mts +5 -6
- package/dist/internal/logger.mjs +1 -1
- package/dist/internal/release-slug.d.mts +3 -5
- package/dist/internal/release-slug.mjs +1 -1
- package/dist/internal/server/cache-safe-trace-context.d.mts +24 -0
- package/dist/internal/server/cache-safe-trace-context.mjs +1 -0
- package/dist/internal/server/capture.d.mts +3 -4
- package/dist/internal/server/capture.mjs +1 -1
- package/dist/internal/server/console-bridge.d.mts +2 -3
- package/dist/internal/server/console-bridge.mjs +1 -1
- package/dist/internal/server/edge-exporter.d.mts +5 -6
- package/dist/internal/server/edge-exporter.mjs +1 -1
- package/dist/internal/server/freshness-propagator.d.mts +41 -0
- package/dist/internal/server/freshness-propagator.mjs +1 -0
- package/dist/internal/server/global-handlers.d.mts +4 -5
- package/dist/internal/server/global-handlers.mjs +1 -1
- package/dist/internal/server/id-generator.d.mts +2 -3
- package/dist/internal/server/instrumentation-options.d.mts +3 -4
- package/dist/internal/server/remote-config.d.mts +3 -4
- package/dist/internal/server/remote-config.mjs +1 -1
- package/dist/internal/server/server-action-marker.d.mts +10 -5
- package/dist/internal/server/server-action-marker.mjs +1 -1
- package/dist/internal/server/server-flush.d.mts +3 -4
- package/dist/internal/server/server-flush.mjs +1 -1
- package/dist/internal/server/static-generation.d.mts +17 -0
- package/dist/internal/server/static-generation.mjs +1 -0
- package/dist/internal/server/trace-meta.d.mts +13 -26
- package/dist/internal/server/trace-meta.mjs +1 -1
- package/dist/internal/server/traceparent.d.mts +2 -3
- package/dist/internal/server/traceparent.mjs +1 -1
- package/dist/internal/server/types.d.mts +5 -8
- package/dist/internal/server/wrap-proxy.d.mts +3 -4
- package/dist/internal/server/wrap-proxy.mjs +1 -1
- package/dist/internal/server/wrap-server-action.d.mts +2 -3
- package/dist/internal/server/wrap-server-action.mjs +1 -1
- package/dist/internal/setup-warnings.d.mts +3 -4
- package/dist/internal/setup-warnings.mjs +1 -1
- package/dist/internal/version.d.mts +2 -3
- package/dist/package.mjs +1 -1
- package/dist/provider.d.mts +3 -3
- package/dist/provider.mjs +1 -1
- package/package.json +49 -42
- package/dist/internal/build/release/destinations/cli.d.mts +0 -6
- package/dist/internal/build/release/destinations/cli.mjs +0 -1
- package/dist/internal/build/release/destinations/cloudflare.d.mts +0 -5
- package/dist/internal/build/release/destinations/cloudflare.mjs +0 -1
- package/dist/internal/build/release/destinations/index.d.mts +0 -14
- package/dist/internal/build/release/destinations/index.mjs +0 -1
- package/dist/internal/build/release/destinations/vercel.d.mts +0 -5
- package/dist/internal/build/release/destinations/vercel.mjs +0 -1
- package/dist/internal/build/release/sources/github.d.mts +0 -8
- package/dist/internal/build/release/sources/github.mjs +0 -1
- package/dist/internal/build/release/sources/index.d.mts +0 -20
- package/dist/internal/build/release/sources/index.mjs +0 -1
- package/dist/internal/build/source-maps/concurrency.d.mts +0 -4
- package/dist/internal/build/source-maps/concurrency.mjs +0 -1
- package/dist/internal/build/source-maps/index.d.mts +0 -6
- package/dist/internal/build/source-maps/index.mjs +0 -1
- package/dist/internal/build/source-maps/upload.d.mts +0 -39
- package/dist/internal/build/source-maps/upload.mjs +0 -1
- package/dist/internal/url.d.mts +0 -4
- package/dist/internal/url.mjs +0 -1
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { SourceMapFile } from "./discover.mjs";
|
|
2
2
|
//#region src/internal/build/source-maps/discover-webpack.d.ts
|
|
3
|
-
declare function injectDebugIdIntoMap(content: string, debugId: string): string;
|
|
4
|
-
declare function discoverWebpack(opts: {
|
|
3
|
+
export declare function injectDebugIdIntoMap(content: string, debugId: string): string;
|
|
4
|
+
export declare function discoverWebpack(opts: {
|
|
5
5
|
projectDir: string;
|
|
6
6
|
distDir: string;
|
|
7
7
|
}): Promise<{
|
|
8
8
|
files: SourceMapFile[];
|
|
9
9
|
sourceFileCount: number;
|
|
10
10
|
}>;
|
|
11
|
-
//#endregion
|
|
12
|
-
export { discoverWebpack, injectDebugIdIntoMap };
|
|
11
|
+
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{resolveDistDir,toPublicPath,walkDistTrees}from"./paths.mjs";import{relative}from"node:path";import{createHash,randomUUID}from"node:crypto";import{
|
|
1
|
+
import{resolveDistDir,toPublicPath,walkDistTrees}from"./paths.mjs";import{relative}from"node:path";import{createHash,randomUUID}from"node:crypto";import{injectDebugIdIntoJs,readDebugIdFromJs}from"@interfere/types/data/source-maps";import{mapWithConcurrency}from"@interfere/helpers/map-with-concurrency";import{readFile,writeFile}from"node:fs/promises";const SOURCEMAPPING_RE=/\/\/[#@]\s*sourceMappingURL=(?<sourceMap>\S+)\s*$/u;function injectDebugIdIntoMap(content,debugId){let json=JSON.parse(content);if(typeof json!=`object`||!json||Array.isArray(json))throw TypeError(`Source map must be a JSON object`);return Reflect.set(json,`debugId`,debugId),JSON.stringify(json)}async function discoverWebpack(opts){let absDistDir=resolveDistDir(opts.projectDir,opts.distDir),relDistDir=relative(opts.projectDir,absDistDir),jsPaths=await walkDistTrees(absDistDir,`.js`),discovered=await mapWithConcurrency(jsPaths,16,async jsAbs=>{let content=await readFile(jsAbs,`utf8`),match=SOURCEMAPPING_RE.exec(content);if(!match?.groups?.sourceMap)return null;let mapAbs=`${jsAbs.slice(0,jsAbs.lastIndexOf(`/`)+1)}${decodeURIComponent(match.groups.sourceMap)}`,jsPublic=toPublicPath(relative(opts.projectDir,jsAbs),relDistDir);return[toPublicPath(relative(opts.projectDir,mapAbs),relDistDir),{existingDebugId:readDebugIdFromJs(content),jsAbs,jsContent:content,jsPublic,mapAbs}]}),pairs=new Map;for(let[mapPublic,pair]of discovered.filter(entry=>entry!==null)){let chunkAbsolutes=pairs.get(mapPublic)?.chunkAbsolutes??[];pairs.set(mapPublic,{...pair,chunkAbsolutes:[...chunkAbsolutes,pair.jsAbs]})}return{files:(await Promise.all(Array.from(pairs.entries(),async([mapPublic,pair])=>{let mapContent=await readFile(pair.mapAbs,`utf8`).catch(()=>null);if(mapContent===null)return null;let debugId=pair.existingDebugId??randomUUID(),mapJson=JSON.parse(mapContent),finalMap=typeof mapJson==`object`&&mapJson&&`debugId`in mapJson&&mapJson.debugId===debugId?mapContent:injectDebugIdIntoMap(mapContent,debugId);if(!pair.existingDebugId){let injectedJs=injectDebugIdIntoJs(pair.jsContent,debugId);await writeFile(pair.jsAbs,injectedJs,`utf8`)}return finalMap!==mapContent&&await writeFile(pair.mapAbs,finalMap,`utf8`),{absolute:pair.mapAbs,chunkAbsolutes:pair.chunkAbsolutes,chunkUrl:pair.jsPublic,content:finalMap,debugId,hash:createHash(`sha256`).update(finalMap).digest(`hex`),path:mapPublic}}))).filter(f=>f!==null),sourceFileCount:pairs.size}}export{discoverWebpack,injectDebugIdIntoMap};
|
|
@@ -2,22 +2,26 @@ import { NextBuildBundler } from "../detect-bundler.mjs";
|
|
|
2
2
|
import { injectDebugIdIntoMap } from "./discover-webpack.mjs";
|
|
3
3
|
import { injectDebugIdIntoJs } from "@interfere/types/data/source-maps";
|
|
4
4
|
//#region src/internal/build/source-maps/discover.d.ts
|
|
5
|
-
interface SourceMapFile {
|
|
5
|
+
export interface SourceMapFile {
|
|
6
6
|
readonly absolute: string;
|
|
7
|
+
readonly chunkAbsolutes: string[];
|
|
7
8
|
readonly chunkUrl: string;
|
|
8
9
|
readonly content: string;
|
|
9
10
|
readonly debugId: string;
|
|
10
11
|
readonly hash: string;
|
|
11
12
|
readonly path: string;
|
|
12
13
|
}
|
|
13
|
-
interface DiscoveryResult {
|
|
14
|
+
export interface DiscoveryResult {
|
|
14
15
|
readonly files: SourceMapFile[];
|
|
15
16
|
readonly sourceFileCount: number;
|
|
16
17
|
}
|
|
17
|
-
declare function discoverSourceMaps(opts: {
|
|
18
|
+
export declare function discoverSourceMaps(opts: {
|
|
18
19
|
bundler: NextBuildBundler;
|
|
19
20
|
projectDir: string;
|
|
20
21
|
distDir: string;
|
|
21
|
-
}): Promise<
|
|
22
|
+
}): Promise<{
|
|
23
|
+
files: SourceMapFile[];
|
|
24
|
+
sourceFileCount: number;
|
|
25
|
+
}>;
|
|
22
26
|
//#endregion
|
|
23
|
-
export {
|
|
27
|
+
export { injectDebugIdIntoJs, injectDebugIdIntoMap };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{normalizeDistDir}from"./paths.mjs";import{discoverTurbopack}from"./discover-turbopack.mjs";import{discoverWebpack,injectDebugIdIntoMap}from"./discover-webpack.mjs";import{injectDebugIdIntoJs}from"@interfere/types/data/source-maps";async function discoverSourceMaps(opts){let distDir=normalizeDistDir(opts.distDir);
|
|
1
|
+
import{normalizeDistDir}from"./paths.mjs";import{discoverTurbopack}from"./discover-turbopack.mjs";import{discoverWebpack,injectDebugIdIntoMap}from"./discover-webpack.mjs";import{injectDebugIdIntoJs}from"@interfere/types/data/source-maps";async function discoverSourceMaps(opts){let distDir=normalizeDistDir(opts.distDir);return{webpack:discoverWebpack,turbopack:discoverTurbopack}[opts.bundler]({projectDir:opts.projectDir,distDir})}export{discoverSourceMaps,injectDebugIdIntoJs,injectDebugIdIntoMap};
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { SourceMapFile } from "./discover.mjs";
|
|
2
2
|
//#region src/internal/build/source-maps/guard.d.ts
|
|
3
|
-
declare function assertClientChunksAreMapped(opts: {
|
|
3
|
+
export declare function assertClientChunksAreMapped(opts: {
|
|
4
4
|
projectDir: string;
|
|
5
5
|
distDir: string;
|
|
6
6
|
discovered: readonly SourceMapFile[];
|
|
7
7
|
}): Promise<void>;
|
|
8
|
-
//#endregion
|
|
9
|
-
export { assertClientChunksAreMapped };
|
|
8
|
+
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{log}from"../../logger.mjs";import{resolveDistDir,walkDistTrees}from"./paths.mjs";import{
|
|
1
|
+
import{log}from"../../logger.mjs";import{resolveDistDir,walkDistTrees}from"./paths.mjs";import{readDebugIdFromJs}from"@interfere/types/data/source-maps";import{readFile}from"node:fs/promises";async function assertClientChunksAreMapped(opts){if(opts.discovered.some(file=>file.chunkUrl.includes(`static/chunks`)))return;let absDistDir=resolveDistDir(opts.projectDir,opts.distDir),clientChunks=(await walkDistTrees(absDistDir,`.js`)).filter(path=>path.replaceAll(`\\`,`/`).includes(`/static/chunks/`)),stampedCount=(await Promise.all(clientChunks.map(async jsAbs=>{let content=await readFile(jsAbs,`utf8`).catch(()=>``);return readDebugIdFromJs(content)!==null}))).filter(Boolean).length;stampedCount!==0&&log.fatal(`Client source maps were not uploaded`,[`${stampedCount} client chunk(s) carry debug IDs but no source maps were uploaded — browser errors from this release won't symbolicate.`,`A coexisting build tool likely deleted them before withInterfere() ran; make withInterfere() the outer wrapper so its discovery runs first, or disable that tool's post-upload map deletion.`])}export{assertClientChunksAreMapped};
|
|
@@ -3,19 +3,19 @@
|
|
|
3
3
|
* Strips Windows path separators and leading `./` segments so the rest of
|
|
4
4
|
* the discovery code can assume POSIX-style paths relative to `projectDir`.
|
|
5
5
|
*/
|
|
6
|
-
declare function normalizeDistDir(distDir?: string): string;
|
|
6
|
+
export declare function normalizeDistDir(distDir?: string): string;
|
|
7
7
|
/**
|
|
8
8
|
* Resolves an absolute filesystem path for the build's `distDir`. Accepts
|
|
9
9
|
* either an absolute path (passed through) or a path relative to
|
|
10
10
|
* `projectDir`.
|
|
11
11
|
*/
|
|
12
|
-
declare function resolveDistDir(projectDir: string, distDir: string): string;
|
|
12
|
+
export declare function resolveDistDir(projectDir: string, distDir: string): string;
|
|
13
13
|
/**
|
|
14
14
|
* Maps an on-disk path (relative to `projectDir`) to the URL path Next.js
|
|
15
15
|
* will serve it at. Both `_next/` and `.next/` shapes resolve to the public
|
|
16
16
|
* `_next/` prefix the browser sees in `//# sourceMappingURL=...` comments.
|
|
17
17
|
*/
|
|
18
|
-
declare function toPublicPath(rel: string, distDir: string): string;
|
|
18
|
+
export declare function toPublicPath(rel: string, distDir: string): string;
|
|
19
19
|
/**
|
|
20
20
|
* Recursively collects files under `<absDistDir>/static` and
|
|
21
21
|
* `<absDistDir>/server` matching the given suffix. Both subtrees are
|
|
@@ -23,6 +23,5 @@ declare function toPublicPath(rel: string, distDir: string): string;
|
|
|
23
23
|
* leave one missing, so a missing directory is treated as empty rather
|
|
24
24
|
* than fatal.
|
|
25
25
|
*/
|
|
26
|
-
declare function walkDistTrees(absDistDir: string, suffix: ".js" | ".js.map"): Promise<string[]>;
|
|
27
|
-
//#endregion
|
|
28
|
-
export { normalizeDistDir, resolveDistDir, toPublicPath, walkDistTrees };
|
|
26
|
+
export declare function walkDistTrees(absDistDir: string, suffix: ".js" | ".js.map"): Promise<string[]>;
|
|
27
|
+
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isAbsolute,join,resolve}from"node:path";import{readdir}from"node:fs/promises";const NEXT_PUBLIC_PREFIX=`_next/`;function normalizeDistDir(distDir=`.next`){return distDir.replaceAll(`\\`,`/`).replace(/^(
|
|
1
|
+
import{isAbsolute,join,resolve}from"node:path";import{readdir}from"node:fs/promises";const NEXT_PUBLIC_PREFIX=`_next/`;function normalizeDistDir(distDir=`.next`){return distDir.replaceAll(`\\`,`/`).replace(/^(?:\.\/)+/u,``)}function resolveDistDir(projectDir,distDir){return isAbsolute(distDir)?distDir:resolve(projectDir,distDir)}function toPublicPath(rel,distDir){let p=rel.replaceAll(`\\`,`/`);return p.startsWith(`${distDir}/`)?`${NEXT_PUBLIC_PREFIX}${p.slice(distDir.length+1)}`:p.startsWith(`.next/`)?`${NEXT_PUBLIC_PREFIX}${p.slice(6)}`:p}async function walkDistTrees(absDistDir,suffix){return(await Promise.all([`static`,`server`].map(async subtree=>{let root=join(absDistDir,subtree),entries=await readdir(root,{recursive:!0}).catch(()=>[]),out=[];for(let entry of entries)entry.endsWith(suffix)&&out.push(join(root,entry));return out}))).flat()}export{normalizeDistDir,resolveDistDir,toPublicPath,walkDistTrees};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Interfere } from "@interfere/javascript";
|
|
2
|
+
//#region src/internal/build/telemetry.d.ts
|
|
3
|
+
export type BuildTelemetry = Omit<Interfere.TelemetryRequest, "result" | "preflight" | "releaseSlug">;
|
|
4
|
+
export declare const EMPTY_TELEMETRY: BuildTelemetry;
|
|
5
|
+
//#endregion
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const EMPTY_TELEMETRY={cleanup:0,createRelease:0,discover:0,fileCount:0,total:0,totalBytes:0,upload:0};export{EMPTY_TELEMETRY};
|
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
//#region src/internal/build/value-injection-loader.d.ts
|
|
2
|
-
|
|
3
|
-
interface ValueInjectionLoaderOptions {
|
|
2
|
+
export interface ValueInjectionLoaderOptions {
|
|
4
3
|
readonly serializedFetch?: string;
|
|
5
4
|
readonly serializedValues?: string;
|
|
6
5
|
}
|
|
7
6
|
interface LoaderThis<T> {
|
|
8
|
-
async()
|
|
9
|
-
getOptions()
|
|
7
|
+
async: () => (error: Error | null, content?: string) => void;
|
|
8
|
+
getOptions: () => T;
|
|
10
9
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
//#endregion
|
|
14
|
-
export { ValueInjectionLoaderOptions, valueInjectionLoader as default, remoteGlobals };
|
|
10
|
+
export default function valueInjectionLoader(this: LoaderThis<ValueInjectionLoaderOptions>, userCode: string): void;
|
|
11
|
+
//#endregion
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{omitUndefined}from"@interfere/helpers/omit-undefined";import{INGEST_URL_GLOBAL}from"@interfere/types/sdk/injected-globals";import{SERVER_TRACING_GLOBAL}from"@interfere/types/sdk/server-tracing";import{PLUGINS_GLOBAL
|
|
2
|
-
`)}\n${userCode}`}function resolveRemoteConfig(fetchOptions){return fetchOptions?(
|
|
1
|
+
import{omitUndefined}from"@interfere/helpers/omit-undefined";import{INGEST_URL_GLOBAL}from"@interfere/types/sdk/injected-globals";import{SERVER_TRACING_GLOBAL}from"@interfere/types/sdk/server-tracing";import{PLUGINS_GLOBAL}from"@interfere/types/sdk/remote-config";import{LOG_SOURCING_GLOBAL}from"@interfere/types/sdk/log-sourcing";import{callbackify}from"node:util";import{createBuildConfigResolver}from"@interfere/build/config";const resolveBuildConfig=createBuildConfigResolver();function valueInjectionLoader(userCode){let options=this.getOptions(),callback=this.async(),values=resolveValues(options);callbackify(async()=>{let globals=await resolveRemoteConfig(resolveFetch(options)).catch(()=>({}));return render({...values,...globals},userCode)})(callback)}function remoteGlobals(config){return omitUndefined({[SERVER_TRACING_GLOBAL]:config.serverTracing,[LOG_SOURCING_GLOBAL]:config.logSourcing,[INGEST_URL_GLOBAL]:config.ingestUrl,[PLUGINS_GLOBAL]:config.plugins})}function render(values,userCode){let lines=Object.entries(values).map(([key,value])=>`globalThis[${JSON.stringify(key)}] = ${JSON.stringify(value)};`);return lines.length===0?userCode:`${lines.join(`
|
|
2
|
+
`)}\n${userCode}`}async function resolveRemoteConfig(fetchOptions){return fetchOptions?resolveBuildConfig(fetchOptions.apiUrl,fetchOptions.publicKey,fetchOptions.cacheScope).then(remoteGlobals):{}}function parseJson(text){if(typeof text==`string`)try{return JSON.parse(text)}catch{return}}function resolveFetch(options){let parsed=parseJson(options.serializedFetch);return isRemoteConfigFetch(parsed)?parsed:null}function isRemoteConfigFetch(value){return isRecord(value)&&typeof value.apiUrl==`string`&&typeof value.publicKey==`string`&&(value.cacheScope===void 0||typeof value.cacheScope==`string`)}function resolveValues(options){let parsed=parseJson(options.serializedValues);return isRecord(parsed)?parsed:{}}function isRecord(value){return typeof value==`object`&&!!value&&!Array.isArray(value)}export{valueInjectionLoader as default};
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
//#region src/internal/build-marker-guard.d.ts
|
|
2
|
-
declare function buildIntegrationRan(): boolean;
|
|
3
|
-
declare function hasEnvCredentials(): boolean;
|
|
4
|
-
declare function warnBuildIntegrationMissing(): void;
|
|
5
|
-
declare function warnBuildIntegrationDegraded(): void;
|
|
6
|
-
//#endregion
|
|
7
|
-
export { buildIntegrationRan, hasEnvCredentials, warnBuildIntegrationDegraded, warnBuildIntegrationMissing };
|
|
2
|
+
export declare function buildIntegrationRan(): boolean;
|
|
3
|
+
export declare function hasEnvCredentials(): boolean;
|
|
4
|
+
export declare function warnBuildIntegrationMissing(): void;
|
|
5
|
+
export declare function warnBuildIntegrationDegraded(): void;
|
|
6
|
+
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{BUILD_MARKER_GLOBAL}from"@interfere/types/sdk/injected-globals";function buildIntegrationRan(){return
|
|
1
|
+
import{BUILD_MARKER_GLOBAL}from"@interfere/types/sdk/injected-globals";function buildIntegrationRan(){let globals=globalThis;return BUILD_MARKER_GLOBAL in globals&&globals[BUILD_MARKER_GLOBAL]!==void 0}function hasEnvCredentials(){return typeof process>`u`?!1:!!process.env.INTERFERE_PUBLIC_KEY||!!process.env.NEXT_PUBLIC_INTERFERE_FORCE_ENABLE}function warnBuildIntegrationMissing(){console.warn(`[interfere] withInterfere() did not run for this build, so Interfere is inactive and will send no events. Another Next config wrapper can silently drop it when wrapping withInterfere(). Make withInterfere() the outermost wrapper in next.config, or upgrade the wrapping tool.`)}function warnBuildIntegrationDegraded(){console.warn(`[interfere] withInterfere() did not run for this build. Interfere will still send events using credentials from the environment, but release association and sourcemaps are unavailable. Make withInterfere() the outermost wrapper in next.config to restore them.`)}export{buildIntegrationRan,hasEnvCredentials,warnBuildIntegrationDegraded,warnBuildIntegrationMissing};
|
package/dist/internal/env.d.mts
CHANGED
|
@@ -1,15 +1,4 @@
|
|
|
1
1
|
//#region src/internal/env.d.ts
|
|
2
|
-
interface InterfereEnv {
|
|
3
|
-
readonly apiKey: string | null;
|
|
4
|
-
readonly apiUrl: string;
|
|
5
|
-
readonly nextRuntime: string | null;
|
|
6
|
-
readonly nodeEnvironment: string;
|
|
7
|
-
readonly publicKey: string | null;
|
|
8
|
-
readonly release: {
|
|
9
|
-
readonly sourceId: string | null;
|
|
10
|
-
readonly destinationId: string | null;
|
|
11
|
-
};
|
|
12
|
-
}
|
|
13
2
|
/**
|
|
14
3
|
* Server-side gate for the proxy route handler, the `captureError` /
|
|
15
4
|
* `onRequestError` helpers, and the remote-config fetcher. Distinct
|
|
@@ -19,7 +8,16 @@ interface InterfereEnv {
|
|
|
19
8
|
* opt-in, and there's no "unknown runtime, default enabled" case the
|
|
20
9
|
* way the browser SDK has for plain-browser / Vite hosts.
|
|
21
10
|
*/
|
|
22
|
-
declare function isEnabledOnServer(): boolean;
|
|
23
|
-
declare function readInterfereEnv():
|
|
24
|
-
|
|
25
|
-
|
|
11
|
+
export declare function isEnabledOnServer(): boolean;
|
|
12
|
+
export declare function readInterfereEnv(): {
|
|
13
|
+
apiKey: string | null;
|
|
14
|
+
apiUrl: string;
|
|
15
|
+
nextRuntime: string | null;
|
|
16
|
+
nodeEnvironment: string;
|
|
17
|
+
publicKey: string | null;
|
|
18
|
+
release: {
|
|
19
|
+
destinationId: string | null;
|
|
20
|
+
sourceId: string | null;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
//#endregion
|
package/dist/internal/env.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{API_URL}from"@interfere/constants/api";import{parseEnvValue}from"@interfere/types/sdk/env";import{resolveEnvironment}from"@interfere/types/sdk/runtime";function isEnabledOnServer(){return process.env.NODE_ENV===`production`||!!process.env.NEXT_PUBLIC_INTERFERE_FORCE_ENABLE}function readInterfereEnv(){let nodeEnvironment=resolveEnvironment({env:process.env});return{apiKey:parseEnvValue(process.env.INTERFERE_API_KEY),apiUrl:parseEnvValue(process.env.INTERFERE_API_URL)??API_URL,nextRuntime:parseEnvValue(process.env.NEXT_RUNTIME),nodeEnvironment,publicKey:parseEnvValue(process.env.INTERFERE_PUBLIC_KEY),release:{
|
|
1
|
+
import{API_URL}from"@interfere/constants/api";import{ENVIRONMENT_GLOBAL}from"@interfere/types/sdk/injected-globals";import{getStringGlobal}from"@interfere/react/util/global";import{parseEnvValue}from"@interfere/types/sdk/env";import{resolveEnvironment}from"@interfere/types/sdk/runtime";function isEnabledOnServer(){return process.env.NODE_ENV===`production`||!!process.env.NEXT_PUBLIC_INTERFERE_FORCE_ENABLE}function readInterfereEnv(){let nodeEnvironment=resolveEnvironment({env:process.env,explicit:getStringGlobal(ENVIRONMENT_GLOBAL)});return{apiKey:parseEnvValue(process.env.INTERFERE_API_KEY),apiUrl:parseEnvValue(process.env.INTERFERE_API_URL)??API_URL,nextRuntime:parseEnvValue(process.env.NEXT_RUNTIME),nodeEnvironment,publicKey:parseEnvValue(process.env.INTERFERE_PUBLIC_KEY),release:{destinationId:parseEnvValue(process.env.NEXT_PUBLIC_INTERFERE_RELEASE_ID),sourceId:parseEnvValue(process.env.NEXT_PUBLIC_INTERFERE_BUILD_ID)}}}export{isEnabledOnServer,readInterfereEnv};
|
|
@@ -4,14 +4,13 @@
|
|
|
4
4
|
* appropriate. Tagged so callers' catch blocks can re-throw fatal errors and
|
|
5
5
|
* keep test-env behavior aligned with the production hard-exit.
|
|
6
6
|
*/
|
|
7
|
-
declare class FatalError extends Error {
|
|
7
|
+
export declare class FatalError extends Error {
|
|
8
8
|
readonly name = "FatalError";
|
|
9
9
|
}
|
|
10
|
-
declare const log: {
|
|
11
|
-
info: (title: string, lines: string[]) => void;
|
|
12
|
-
warn: (title: string, lines: string[]) => void;
|
|
10
|
+
export declare const log: {
|
|
13
11
|
error: (title: string, lines: string[]) => void;
|
|
14
12
|
fatal(title: string, lines: string[]): never;
|
|
13
|
+
info: (title: string, lines: string[]) => void;
|
|
14
|
+
warn: (title: string, lines: string[]) => void;
|
|
15
15
|
};
|
|
16
|
-
//#endregion
|
|
17
|
-
export { FatalError, log };
|
|
16
|
+
//#endregion
|
package/dist/internal/logger.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import chalk from"chalk";const styles={
|
|
1
|
+
import chalk from"chalk";const styles={error:{content:chalk.redBright,prefix:`${chalk.red.bold(`⨯`)} `,prependLevel:!0,text:chalk.red.bold},fatal:{content:chalk.redBright,prefix:`${chalk.red.bold(`⨯`)} `,prependLevel:!0,text:chalk.red.bold},info:{content:chalk.white,prefix:chalk.whiteBright.bold(`❖`),prependLevel:!1,text:chalk.cyan.bold},warn:{content:chalk.yellowBright,prefix:`${chalk.yellow.bold(`⚠`)} `,prependLevel:!1,text:chalk.yellow.bold}},consoleMethods={error:`error`,fatal:`error`,info:`log`,warn:`warn`};function isTestEnv(){return!!(process.env.VITEST||process.env.VITEST_WORKER_ID)}var FatalError=class extends Error{name=`FatalError`};function emit(level,title,lines){if(isTestEnv())return;let style=styles[level],method=consoleMethods[level],fn=globalThis.console[method];if(typeof fn!=`function`)return;let prependLevel=style.prependLevel?`[${style.text(level.toUpperCase())}] `:``;fn.call(globalThis.console,`${prependLevel} ${chalk.white(`Interfere →`)} ${style.text(title)}`);for(let[i,line]of lines.entries()){let connector=i===lines.length-1?`└`:`├`;fn.call(globalThis.console,`${prependLevel} ${connector} ${style.content(line)}`)}}const log={error:(title,lines)=>{emit(`error`,title,lines)},fatal(title,lines){if(emit(`fatal`,title,lines),isTestEnv())throw new FatalError(title);process.exit(1)},info:(title,lines)=>{emit(`info`,title,lines)},warn:(title,lines)=>{emit(`warn`,title,lines)}};export{FatalError,log};
|
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import { ReleaseSlug } from "@interfere/types/releases/slug";
|
|
2
1
|
import { RunGitCommand } from "@interfere/types/sdk/github-source";
|
|
3
2
|
//#region src/internal/release-slug.d.ts
|
|
4
|
-
declare function resolveReleaseSlug(runGit?: RunGitCommand): {
|
|
3
|
+
export declare function resolveReleaseSlug(runGit?: RunGitCommand): {
|
|
5
4
|
commitSha: string | null;
|
|
6
|
-
slug: ReleaseSlug | null;
|
|
5
|
+
slug: (`rel_${string}` & import("zod").$brand<"ReleaseSlug">) | null;
|
|
7
6
|
};
|
|
8
|
-
//#endregion
|
|
9
|
-
export { resolveReleaseSlug };
|
|
7
|
+
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{deriveReleaseSlug,releaseSlugSchema}from"@interfere/types/releases/slug";import{RELEASE_SLUG_GLOBAL}from"@interfere/types/sdk/injected-globals";import{resolveReleaseCommitSha}from"@interfere/types/sdk/github-source";const noGit=()=>null;function readBakedReleaseSlug(){let
|
|
1
|
+
import{deriveReleaseSlug,releaseSlugSchema}from"@interfere/types/releases/slug";import{RELEASE_SLUG_GLOBAL}from"@interfere/types/sdk/injected-globals";import{resolveReleaseCommitSha}from"@interfere/types/sdk/github-source";const noGit=()=>null;function readBakedReleaseSlug(){let globals=globalThis;if(!(RELEASE_SLUG_GLOBAL in globals))return null;let parsed=releaseSlugSchema.safeParse(globals[RELEASE_SLUG_GLOBAL]);return parsed.success?parsed.data:null}function resolveReleaseSlug(runGit=noGit){let baked=readBakedReleaseSlug();if(baked)return{commitSha:null,slug:baked};let commitSha=resolveReleaseCommitSha(process.env,runGit);return{commitSha,slug:commitSha?deriveReleaseSlug(commitSha):null}}export{resolveReleaseSlug};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Context, TextMapSetter } from "@opentelemetry/api";
|
|
2
|
+
import { W3CTraceContextPropagator } from "@opentelemetry/core";
|
|
3
|
+
//#region src/internal/server/cache-safe-trace-context.d.ts
|
|
4
|
+
export interface CacheSafeTraceContextPropagatorOptions {
|
|
5
|
+
isStaticRender?: () => boolean;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* W3C trace-context propagator that refuses to feed `clientTraceMetadata`
|
|
9
|
+
* during static-generation renders (build prerender, runtime ISR
|
|
10
|
+
* revalidation). Trace context injected into such a render is baked into
|
|
11
|
+
* cached HTML and served verbatim to every visitor in the cache window —
|
|
12
|
+
* there is no per-visitor server trace for a cache hit, so the idiomatic
|
|
13
|
+
* output is no trace meta at all, exactly what Next produces for
|
|
14
|
+
* build-time prerenders. Detected by the carrier shape: Next's
|
|
15
|
+
* `clientTraceMetadata` collector is the only inject path that passes an
|
|
16
|
+
* array carrier. Every other carrier (outgoing fetch headers during a
|
|
17
|
+
* revalidation render included) injects normally.
|
|
18
|
+
*/
|
|
19
|
+
export declare class CacheSafeTraceContextPropagator extends W3CTraceContextPropagator {
|
|
20
|
+
private readonly isStaticRender;
|
|
21
|
+
constructor(options?: CacheSafeTraceContextPropagatorOptions);
|
|
22
|
+
inject<Carrier>(context: Context, carrier: Carrier, setter: TextMapSetter<Carrier>): void;
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{isStaticGenerationRender}from"./static-generation.mjs";import{W3CTraceContextPropagator}from"@opentelemetry/core";var CacheSafeTraceContextPropagator=class extends W3CTraceContextPropagator{isStaticRender;constructor(options={}){super(),this.isStaticRender=options.isStaticRender??isStaticGenerationRender}inject(context,carrier,setter){Array.isArray(carrier)&&this.isStaticRender()||super.inject(context,carrier,setter)}};export{CacheSafeTraceContextPropagator};
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { CaptureErrorContext, OnRequestErrorContext } from "./types.mjs";
|
|
2
2
|
//#region src/internal/server/capture.d.ts
|
|
3
|
-
declare function captureError(error: unknown, _request?: unknown, context?: CaptureErrorContext): void;
|
|
3
|
+
export declare function captureError(error: unknown, _request?: unknown, context?: CaptureErrorContext): void;
|
|
4
4
|
interface OnRequestErrorRequest {
|
|
5
5
|
readonly headers?: Record<string, string>;
|
|
6
6
|
readonly method?: string;
|
|
7
7
|
readonly path?: string;
|
|
8
8
|
}
|
|
9
|
-
declare function onRequestError(error: Error & {
|
|
9
|
+
export declare function onRequestError(error: Error & {
|
|
10
10
|
digest?: string;
|
|
11
11
|
}, request: OnRequestErrorRequest | undefined, context: OnRequestErrorContext): Promise<void>;
|
|
12
|
-
//#endregion
|
|
13
|
-
export { captureError, onRequestError };
|
|
12
|
+
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isEnabledOnServer}from"../env.mjs";import{flushServerTelemetry}from"./server-flush.mjs";import{isPluginEnabled}from"./remote-config.mjs";import{readServerAction}from"./server-action-marker.mjs";import{SpanKind,SpanStatusCode,trace}from"@opentelemetry/api";import{MECHANISM_TYPE,isNonErrorException,toException}from"@interfere/types/sdk/errors";const ON_REQUEST_ERROR_MECHANISM={type:MECHANISM_TYPE.nextjs.onRequestError,handled:!1,synthetic:!1
|
|
1
|
+
import{isEnabledOnServer}from"../env.mjs";import{flushServerTelemetry}from"./server-flush.mjs";import{isPluginEnabled}from"./remote-config.mjs";import{readServerAction}from"./server-action-marker.mjs";import{omitUndefined}from"@interfere/helpers/omit-undefined";import{SpanKind,SpanStatusCode,trace}from"@opentelemetry/api";import{MECHANISM_TYPE,isNonErrorException,toException}from"@interfere/types/sdk/errors";const ON_REQUEST_ERROR_MECHANISM={handled:!1,synthetic:!1,type:MECHANISM_TYPE.nextjs.onRequestError},SERVER_ACTION_MECHANISM={handled:!1,synthetic:!1,type:MECHANISM_TYPE.nextjs.serverAction},ROUTE_HANDLER_MECHANISM={handled:!1,synthetic:!1,type:MECHANISM_TYPE.nextjs.routeHandler},DEFAULT_CAPTURE_ERROR_MECHANISM={handled:!0,type:MECHANISM_TYPE.nextjs.captureError},CONTROL_FLOW_DIGEST_PREFIXES=[`NEXT_REDIRECT`,`NEXT_NOT_FOUND`,`NEXT_HTTP_ERROR_FALLBACK`];function isNextControlFlowError(error){if(!(error instanceof Error))return!1;let digest=`digest`in error?error.digest:void 0;return typeof digest==`string`&&CONTROL_FLOW_DIGEST_PREFIXES.some(prefix=>digest.startsWith(prefix))}const seen=new WeakSet;function addNextjsAttrs(attrs,nextjs,handled){nextjs&&(nextjs.errorDigest&&(attrs[`interfere.error.digest`]=nextjs.errorDigest),nextjs.requestMethod&&(attrs[`http.request.method`]=nextjs.requestMethod),nextjs.requestPath&&(attrs[`url.path`]=nextjs.requestPath),nextjs.routePath&&(attrs[`http.route`]=nextjs.routePath),nextjs.routeType===`route`&&!handled&&(attrs[`http.response.status_code`]=500))}function addRequestContextAttrs(attrs,context){if(context?.serverAction&&(attrs[`interfere.server_action.name`]=context.serverAction.name,context.serverAction.argsShape&&(attrs[`interfere.server_action.args_shape`]=context.serverAction.argsShape)),context?.requestHeaders)for(let[name,headerValue]of Object.entries(context.requestHeaders))attrs[`http.request.header.${name}`]=headerValue}function addExceptionAttrs(attrs,value){if(isNonErrorException(value)){attrs[`exception.type`]=value.type,attrs[`exception.message`]=value.value,attrs[`interfere.exception.kind`]=`non-error`;return}attrs[`exception.type`]=value.name,attrs[`exception.message`]=value.message,attrs[`interfere.exception.kind`]=`error`,value.stack&&(attrs[`exception.stacktrace`]=value.stack)}function buildAttrs(value,mechanism,context){let attrs={"interfere.exception.handled":mechanism.handled,"interfere.exception.mechanism":mechanism.type};return addNextjsAttrs(attrs,context?.nextjs,mechanism.handled),addRequestContextAttrs(attrs,context),addExceptionAttrs(attrs,value),attrs}function recordException({error,mechanism,context}){let value=toException(error),attrs=buildAttrs(value,mechanism,context),tracer=trace.getTracer(`@interfere/next/server`),active=trace.getActiveSpan();if(active?.isRecording()){active.addEvent(`exception`,attrs),mechanism.handled||active.setStatus({code:SpanStatusCode.ERROR,message:isNonErrorException(value)?value.value:value.message});return}let span=tracer.startSpan(`interfere.captureError`,{kind:SpanKind.INTERNAL});span.addEvent(`exception`,attrs),mechanism.handled||span.setStatus({code:SpanStatusCode.ERROR,message:isNonErrorException(value)?value.value:value.message}),span.end()}function captureError(error,_request,context){if(isEnabledOnServer()&&isPluginEnabled(`errors`)&&!isNextControlFlowError(error)){if(error instanceof Error){if(seen.has(error))return;seen.add(error)}recordException({context,error,mechanism:context?.mechanism??DEFAULT_CAPTURE_ERROR_MECHANISM})}}const CAPTURED_REQUEST_HEADERS=[`accept`,`accept-language`,`content-length`,`content-type`,`host`,`referer`,`user-agent`,`x-forwarded-for`,`x-forwarded-host`,`x-forwarded-proto`,`x-vercel-id`];function pickAllowedHeaders(headers){if(!headers)return;let picked={};for(let name of CAPTURED_REQUEST_HEADERS){let value=headers[name];typeof value==`string`&&(picked[name]=value)}return Object.keys(picked).length>0?picked:void 0}async function onRequestError(error,request,context){if(isNextControlFlowError(error)||seen.has(error)||(seen.add(error),!isEnabledOnServer())||!isPluginEnabled(`errors`))return;let action=readServerAction(error),mechanism=ON_REQUEST_ERROR_MECHANISM;action?mechanism=SERVER_ACTION_MECHANISM:context.routeType===`route`&&(mechanism=ROUTE_HANDLER_MECHANISM);let requestHeaders=pickAllowedHeaders(request?.headers);recordException({context:omitUndefined({mechanism,nextjs:omitUndefined({...context,requestMethod:request?.method||void 0,requestPath:request?.path||void 0,errorDigest:error.digest||void 0}),requestHeaders,serverAction:action??void 0}),error,mechanism});try{await flushServerTelemetry()}catch{return}}export{captureError,onRequestError};
|
|
@@ -19,6 +19,5 @@
|
|
|
19
19
|
* call only captures the active context; an arg mutated before the
|
|
20
20
|
* microtask runs is serialized in its mutated state.
|
|
21
21
|
*/
|
|
22
|
-
declare function bridgeConsoleToOtel(): () => void;
|
|
23
|
-
//#endregion
|
|
24
|
-
export { bridgeConsoleToOtel };
|
|
22
|
+
export declare function bridgeConsoleToOtel(): () => void;
|
|
23
|
+
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{context,trace}from"@opentelemetry/api";import{logs}from"@opentelemetry/api-logs";const CONSOLE_METHODS=[`debug`,`log`,`info`,`warn`,`error`],SEVERITY={debug:5,
|
|
1
|
+
import{omitUndefined}from"@interfere/helpers/omit-undefined";import{context,trace}from"@opentelemetry/api";import{logs}from"@opentelemetry/api-logs";const CONSOLE_METHODS=[`debug`,`log`,`info`,`warn`,`error`],SEVERITY={debug:5,error:17,info:9,log:9,warn:13},MAX_BODY_CHARS=8192;function buildBody(args){let body=args.map(element=>stringify(element)).join(` `);return body.length>MAX_BODY_CHARS?`${body.slice(0,MAX_BODY_CHARS)}…`:body}function stringify(value){if(typeof value==`string`)return value;if(value instanceof Error)return value.stack??`${value.name}: ${value.message}`;try{return JSON.stringify(value)}catch{return String(value)}}function extractException(args){for(let arg of args)if(arg instanceof Error)return arg}function buildExceptionAttributes(args){let exception=extractException(args);if(exception)return omitUndefined({"exception.type":exception.name,"exception.message":exception.message,"exception.stacktrace":exception.stack||void 0})}function bridgeConsoleToOtel(){let logger=logs.getLogger(`@interfere/next/console`),emitting=!1,bridgeFailures=0,original={debug:console.debug,error:console.error,info:console.info,log:console.log,warn:console.warn};function emitLog(orig,severity,method,args,activeContext,activeSpan){if(!emitting){emitting=!0;try{let attributes=buildExceptionAttributes(args);logger.emit(omitUndefined({body:buildBody(args),severityNumber:severity,severityText:method.toUpperCase(),context:activeSpan?activeContext:void 0,attributes}))}catch(error){bridgeFailures+=1,bridgeFailures%100==1&&orig.call(console,`[interfere] console→OTel bridge emission failed (${bridgeFailures} total):`,error)}finally{emitting=!1}}}for(let method of CONSOLE_METHODS){let orig=original[method],severity=SEVERITY[method];console[method]=(...args)=>{orig.apply(console,args);let activeContext=context.active(),activeSpan=trace.getSpan(activeContext);queueMicrotask(()=>{emitLog(orig,severity,method,args,activeContext,activeSpan)})}}return(()=>{for(let method of CONSOLE_METHODS)console[method]=original[method]})}export{bridgeConsoleToOtel};
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { ReadableSpan, SpanExporter } from "@opentelemetry/sdk-trace-base";
|
|
2
|
+
import { InterfereClient } from "@interfere/javascript";
|
|
2
3
|
import { ExportResult } from "@opentelemetry/core";
|
|
3
4
|
//#region src/internal/server/edge-exporter.d.ts
|
|
4
|
-
declare class FetchTraceExporter implements SpanExporter {
|
|
5
|
-
private readonly
|
|
6
|
-
|
|
7
|
-
constructor(url: string, headers: Record<string, string>);
|
|
5
|
+
export declare class FetchTraceExporter implements SpanExporter {
|
|
6
|
+
private readonly client;
|
|
7
|
+
constructor(options: InterfereClient.Options);
|
|
8
8
|
export(spans: ReadableSpan[], resultCallback: (result: ExportResult) => void): void;
|
|
9
9
|
shutdown(): Promise<void>;
|
|
10
10
|
forceFlush(): Promise<void>;
|
|
11
11
|
}
|
|
12
|
-
//#endregion
|
|
13
|
-
export { FetchTraceExporter };
|
|
12
|
+
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{ExportResultCode}from"@opentelemetry/core";import{JsonTraceSerializer}from"@opentelemetry/otlp-transformer
|
|
1
|
+
import{InterfereClient}from"@interfere/javascript";import{toError}from"@interfere/types/sdk/errors";import{ExportResultCode}from"@opentelemetry/core";import{JsonTraceSerializer}from"@opentelemetry/otlp-transformer";import{z}from"zod";var FetchTraceExporter=class{client;constructor(options){this.client=new InterfereClient({...options,maxRetries:0,fetch:Object.assign(async(url,init)=>globalThis.fetch(url,{...init,keepalive:!0}),globalThis.fetch)})}export(spans,resultCallback){if(spans.length===0){resultCallback({code:ExportResultCode.SUCCESS});return}let payload=JsonTraceSerializer.serializeRequest(spans);if(!payload||payload.byteLength===0){resultCallback({code:ExportResultCode.SUCCESS});return}(async()=>{try{let body=z.record(z.string(),z.unknown()).parse(JSON.parse(new TextDecoder().decode(payload)));await this.client.drain.otlpExport({signal:`traces`,body}),resultCallback({code:ExportResultCode.SUCCESS})}catch(error){resultCallback({code:ExportResultCode.FAILED,error:toError(error)})}})()}async shutdown(){return Promise.resolve()}async forceFlush(){return Promise.resolve()}};export{FetchTraceExporter};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Context, TextMapPropagator, TextMapSetter } from "@opentelemetry/api";
|
|
2
|
+
//#region src/internal/server/freshness-propagator.d.ts
|
|
3
|
+
export interface FreshnessStampingPropagatorOptions {
|
|
4
|
+
isStaticRender?: () => boolean;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Stamp-only propagator that writes a `tracestate` freshness entry
|
|
8
|
+
* (`interfere=<unix seconds>`) at inject time. It never touches
|
|
9
|
+
* `traceparent`, so it composes cleanly after any W3C trace-context
|
|
10
|
+
* propagator — the standalone `register()` path pairs it with
|
|
11
|
+
* `CacheSafeTraceContextPropagator`, and composed bootstraps
|
|
12
|
+
* (`@vercel/otel`'s `propagators: ["auto", ...kit.propagators]`,
|
|
13
|
+
* dd-trace, custom providers) pick it up through `kit.propagators`
|
|
14
|
+
* without conflicting with their own trace-context propagator.
|
|
15
|
+
*
|
|
16
|
+
* Why: Next's `clientTraceMetadata` bakes the injected trace context
|
|
17
|
+
* into any HTML it caches — ISR revalidations and CDN full-page caches
|
|
18
|
+
* then serve one render's `traceparent` to every visitor in the cache
|
|
19
|
+
* window, and the browser SDK would stitch all of those sessions onto a
|
|
20
|
+
* single stale trace. The client's propagation reader uses this stamp
|
|
21
|
+
* to drop traceparents older than its freshness window; a re-injected
|
|
22
|
+
* stamp always overwrites an inherited one, so the value reflects this
|
|
23
|
+
* render, not the upstream request that carried it in.
|
|
24
|
+
*
|
|
25
|
+
* Static-generation renders (build prerender, runtime ISR revalidation)
|
|
26
|
+
* feed `clientTraceMetadata` through an array carrier. On those, the
|
|
27
|
+
* stamp is written only when a trace-context propagator ahead of this
|
|
28
|
+
* one already wrote `traceparent` into the carrier — a composed setup
|
|
29
|
+
* whose own propagator this SDK cannot gate, where the stamp is the
|
|
30
|
+
* only staleness defense. In the standalone `register()` path the
|
|
31
|
+
* cache-safe propagator skips `traceparent` first, so no meta is
|
|
32
|
+
* emitted at all, matching Next's build-time prerender output.
|
|
33
|
+
*/
|
|
34
|
+
export declare class FreshnessStampingPropagator implements TextMapPropagator {
|
|
35
|
+
private readonly isStaticRender;
|
|
36
|
+
constructor(options?: FreshnessStampingPropagatorOptions);
|
|
37
|
+
inject<Carrier>(context: Context, carrier: Carrier, setter: TextMapSetter<Carrier>): void;
|
|
38
|
+
extract(context: Context): Context;
|
|
39
|
+
fields(): string[];
|
|
40
|
+
}
|
|
41
|
+
//#endregion
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{isStaticGenerationRender}from"./static-generation.mjs";import{isSpanContextValid,trace}from"@opentelemetry/api";import{TraceState,isTracingSuppressed}from"@opentelemetry/core";import{TRACESTATE_FRESHNESS_KEY,freshnessStamp}from"@interfere/types/sdk/trace-freshness";const TRACE_STATE_HEADER=`tracestate`;function carrierHoldsTraceparent(carrier){return carrier.some(entry=>typeof entry==`object`&&!!entry&&`key`in entry&&entry.key===`traceparent`)}var FreshnessStampingPropagator=class{isStaticRender;constructor(options={}){this.isStaticRender=options.isStaticRender??isStaticGenerationRender}inject(context,carrier,setter){let spanContext=trace.getSpanContext(context);if(!spanContext||!isSpanContextValid(spanContext)||isTracingSuppressed(context)||Array.isArray(carrier)&&this.isStaticRender()&&!carrierHoldsTraceparent(carrier))return;let traceState=(spanContext.traceState??new TraceState).set(TRACESTATE_FRESHNESS_KEY,freshnessStamp(Date.now()));setter.set(carrier,TRACE_STATE_HEADER,traceState.serialize())}extract(context){return context}fields(){return[TRACE_STATE_HEADER]}};export{FreshnessStampingPropagator};
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
//#region src/internal/server/global-handlers.d.ts
|
|
2
|
-
interface GlobalHandlerOptions {
|
|
2
|
+
export interface GlobalHandlerOptions {
|
|
3
3
|
readonly captureUncaughtException: boolean;
|
|
4
4
|
readonly captureUnhandledRejection: boolean;
|
|
5
5
|
readonly exitOnUncaughtException: boolean;
|
|
6
6
|
readonly flush: () => Promise<void>;
|
|
7
7
|
}
|
|
8
|
-
declare function installGlobalHandlers(options: GlobalHandlerOptions): void;
|
|
9
|
-
declare function uninstallGlobalHandlers(): void;
|
|
10
|
-
//#endregion
|
|
11
|
-
export { GlobalHandlerOptions, installGlobalHandlers, uninstallGlobalHandlers };
|
|
8
|
+
export declare function installGlobalHandlers(options: GlobalHandlerOptions): void;
|
|
9
|
+
export declare function uninstallGlobalHandlers(): void;
|
|
10
|
+
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{captureError}from"./capture.mjs";import{MECHANISM_TYPE}from"@interfere/types/sdk/errors";let uncaughtHandler=null,unhandledHandler=null;function installGlobalHandlers(options){options.captureUncaughtException&&uncaughtHandler===null&&(uncaughtHandler=error=>{handleUncaughtException(error,options)},process.on(`uncaughtException`,uncaughtHandler)),options.captureUnhandledRejection&&unhandledHandler===null&&(unhandledHandler=reason=>{handleUnhandledRejection(reason,options)},process.on(`unhandledRejection`,unhandledHandler))}function uninstallGlobalHandlers(){uncaughtHandler!==null&&(process.off(`uncaughtException`,uncaughtHandler),uncaughtHandler=null),unhandledHandler!==null&&(process.off(`unhandledRejection`,unhandledHandler),unhandledHandler=null)}async function handleUncaughtException(error,options){captureError(error,void 0,{mechanism:{type:MECHANISM_TYPE.node.uncaughtException
|
|
1
|
+
import{captureError}from"./capture.mjs";import{MECHANISM_TYPE}from"@interfere/types/sdk/errors";let uncaughtHandler=null,unhandledHandler=null;function installGlobalHandlers(options){options.captureUncaughtException&&uncaughtHandler===null&&(uncaughtHandler=error=>{handleUncaughtException(error,options)},process.on(`uncaughtException`,uncaughtHandler)),options.captureUnhandledRejection&&unhandledHandler===null&&(unhandledHandler=reason=>{handleUnhandledRejection(reason,options)},process.on(`unhandledRejection`,unhandledHandler))}function uninstallGlobalHandlers(){uncaughtHandler!==null&&(process.off(`uncaughtException`,uncaughtHandler),uncaughtHandler=null),unhandledHandler!==null&&(process.off(`unhandledRejection`,unhandledHandler),unhandledHandler=null)}async function handleUncaughtException(error,options){captureError(error,void 0,{mechanism:{handled:!1,type:MECHANISM_TYPE.node.uncaughtException}}),await flushWithTimeout(options.flush),options.exitOnUncaughtException&&process.exit(1)}async function handleUnhandledRejection(reason,options){captureError(reason,void 0,{mechanism:{handled:!1,type:MECHANISM_TYPE.node.unhandledRejection}}),await flushWithTimeout(options.flush)}async function flushWithTimeout(flush){let{promise:timedOut,resolve:expire}=Promise.withResolvers(),timer=setTimeout(()=>{expire(void 0)},2e3);timer.unref?.();try{await Promise.race([Promise.allSettled([flush()]),timedOut])}finally{clearTimeout(timer)}}export{installGlobalHandlers,uninstallGlobalHandlers};
|
|
@@ -21,7 +21,7 @@ import { IdGenerator } from "@opentelemetry/sdk-trace-base";
|
|
|
21
21
|
* a monotonic counter. Counter widths are masked to keep IDs at their
|
|
22
22
|
* spec-mandated lengths even after wrap.
|
|
23
23
|
*/
|
|
24
|
-
declare class PrerenderSafeIdGenerator implements IdGenerator {
|
|
24
|
+
export declare class PrerenderSafeIdGenerator implements IdGenerator {
|
|
25
25
|
private readonly random;
|
|
26
26
|
private readonly tracePrefix;
|
|
27
27
|
private readonly spanPrefix;
|
|
@@ -33,5 +33,4 @@ declare class PrerenderSafeIdGenerator implements IdGenerator {
|
|
|
33
33
|
private fallbackTraceId;
|
|
34
34
|
private fallbackSpanId;
|
|
35
35
|
}
|
|
36
|
-
//#endregion
|
|
37
|
-
export { PrerenderSafeIdGenerator };
|
|
36
|
+
//#endregion
|
|
@@ -21,7 +21,7 @@ import { LogRecordProcessor } from "@opentelemetry/sdk-logs";
|
|
|
21
21
|
* so they're erased before bundling — the Edge runtime bundle never sees
|
|
22
22
|
* them at all.
|
|
23
23
|
*/
|
|
24
|
-
interface ServerInstrumentationOptions {
|
|
24
|
+
export interface ServerInstrumentationOptions {
|
|
25
25
|
/** @internal */
|
|
26
26
|
_internalAdditionalLogRecordProcessors?: LogRecordProcessor[];
|
|
27
27
|
/**
|
|
@@ -47,7 +47,7 @@ interface ServerInstrumentationOptions {
|
|
|
47
47
|
captureUnhandledRejection?: boolean;
|
|
48
48
|
/**
|
|
49
49
|
* OTLP payload compression. Default `"none"`. `"gzip"` shrinks each
|
|
50
|
-
* `/
|
|
50
|
+
* `/drain/v1/*` POST body.
|
|
51
51
|
*/
|
|
52
52
|
compression?: "gzip" | "none";
|
|
53
53
|
/**
|
|
@@ -63,5 +63,4 @@ interface ServerInstrumentationOptions {
|
|
|
63
63
|
*/
|
|
64
64
|
serviceName?: string;
|
|
65
65
|
}
|
|
66
|
-
//#endregion
|
|
67
|
-
export { ServerInstrumentationOptions };
|
|
66
|
+
//#endregion
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
//#region src/internal/server/remote-config.d.ts
|
|
2
|
-
declare function fetchAndCacheRemoteConfig(): Promise<void>;
|
|
3
|
-
declare function isPluginEnabled(plugin: string): boolean;
|
|
4
|
-
//#endregion
|
|
5
|
-
export { fetchAndCacheRemoteConfig, isPluginEnabled };
|
|
2
|
+
export declare function fetchAndCacheRemoteConfig(): Promise<void>;
|
|
3
|
+
export declare function isPluginEnabled(plugin: string): boolean;
|
|
4
|
+
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isEnabledOnServer,readInterfereEnv}from"../env.mjs";import{
|
|
1
|
+
import{isEnabledOnServer,readInterfereEnv}from"../env.mjs";import{InterfereClient}from"@interfere/javascript";import{readBakedPlugins,remoteConfigInputSchema}from"@interfere/types/sdk/remote-config";let cachedConfig=null;async function fetchAndCacheRemoteConfig(){if(readBakedPlugins()||!isEnabledOnServer())return;let env=readInterfereEnv();if(env.publicKey!==null)try{let client=new InterfereClient({baseUrl:env.apiUrl,publicKeyHeader:{apiKey:env.publicKey},timeoutInSeconds:10,maxRetries:0}),config=remoteConfigInputSchema.parse(await client.config.getSdkConfig());config.plugins&&(cachedConfig=config.plugins)}catch{return}}function isPluginEnabled(plugin){let config=readBakedPlugins()??cachedConfig;return!config||config[plugin]!==!1}export{fetchAndCacheRemoteConfig,isPluginEnabled};
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
//#region src/internal/server/server-action-marker.d.ts
|
|
2
|
-
interface ServerActionMarker {
|
|
2
|
+
export interface ServerActionMarker {
|
|
3
3
|
readonly argsShape?: string;
|
|
4
4
|
readonly name: string;
|
|
5
5
|
}
|
|
6
|
-
declare function markServerAction(error: unknown, info: ServerActionMarker): void;
|
|
7
|
-
declare function readServerAction(error: unknown):
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
export declare function markServerAction(error: unknown, info: ServerActionMarker): void;
|
|
7
|
+
export declare function readServerAction(error: unknown): {
|
|
8
|
+
name: string;
|
|
9
|
+
argsShape?: never;
|
|
10
|
+
} | {
|
|
11
|
+
name: string;
|
|
12
|
+
argsShape: string;
|
|
13
|
+
} | undefined;
|
|
14
|
+
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const MARKER=`__interfere_server_action__`;function markServerAction(error,info){typeof error
|
|
1
|
+
const MARKER=`__interfere_server_action__`;function markServerAction(error,info){typeof error==`object`&&error&&Object.defineProperty(error,MARKER,{configurable:!0,enumerable:!1,value:info,writable:!0})}function readServerAction(error){if(typeof error!=`object`||!error||!(MARKER in error))return;let marker=error[MARKER];if(typeof marker==`object`&&marker&&`name`in marker&&typeof marker.name==`string`){if(!(`argsShape`in marker)||marker.argsShape===void 0)return{name:marker.name};if(typeof marker.argsShape==`string`)return{name:marker.name,argsShape:marker.argsShape}}}export{markServerAction,readServerAction};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
//#region src/internal/server/server-flush.d.ts
|
|
2
|
-
declare function setServerFlush(fn: () => Promise<void>): void;
|
|
3
|
-
declare function flushServerTelemetry(): Promise<void>;
|
|
4
|
-
//#endregion
|
|
5
|
-
export { flushServerTelemetry, setServerFlush };
|
|
2
|
+
export declare function setServerFlush(fn: () => Promise<void>): void;
|
|
3
|
+
export declare function flushServerTelemetry(): Promise<void>;
|
|
4
|
+
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
let flushFn=null;function setServerFlush(fn){flushFn=fn}function flushServerTelemetry(){return flushFn?flushFn():Promise.resolve()}export{flushServerTelemetry,setServerFlush};
|
|
1
|
+
let flushFn=null;function setServerFlush(fn){flushFn=fn}async function flushServerTelemetry(){return flushFn?flushFn():Promise.resolve()}export{flushServerTelemetry,setServerFlush};
|