@interfere/next 0.1.0-alpha.2 → 0.2.0-alpha.3
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/dist/config.d.mts.map +1 -1
- package/dist/config.mjs +31 -40
- package/dist/config.mjs.map +1 -1
- package/dist/internal/build/release/git.mjs +2 -2
- package/dist/internal/build/release/git.mjs.map +1 -1
- package/dist/internal/build/release/index.d.mts +1 -1
- package/dist/internal/build/release/index.d.mts.map +1 -1
- package/dist/internal/build/release/index.mjs +1 -3
- package/dist/internal/build/release/index.mjs.map +1 -1
- package/dist/internal/env.d.mts +1 -0
- package/dist/internal/env.d.mts.map +1 -1
- package/dist/internal/env.mjs +1 -0
- package/dist/internal/env.mjs.map +1 -1
- package/dist/internal/{build/logger.d.mts → logger.d.mts} +3 -3
- package/dist/internal/logger.d.mts.map +1 -0
- package/dist/internal/{build/logger.mjs → logger.mjs} +3 -3
- package/dist/internal/logger.mjs.map +1 -0
- package/dist/internal/route/handle-post.d.mts.map +1 -1
- package/dist/internal/route/handle-post.mjs +4 -4
- package/dist/internal/route/handle-post.mjs.map +1 -1
- package/dist/internal/server/runtime.mjs +1 -1
- package/dist/internal/server/runtime.mjs.map +1 -1
- package/dist/internal/server/transport.d.mts +3 -1
- package/dist/internal/server/transport.d.mts.map +1 -1
- package/dist/internal/server/transport.mjs +0 -1
- package/dist/internal/server/transport.mjs.map +1 -1
- package/package.json +6 -6
- package/dist/internal/build/logger.d.mts.map +0 -1
- package/dist/internal/build/logger.mjs.map +0 -1
package/dist/config.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.mts","names":[],"sources":["../src/config.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"config.d.mts","names":[],"sources":["../src/config.ts"],"mappings":";;;;UAeiB,eAAA,SACP,OAAA,CAAQ,IAAA,CAAK,QAAA;AAAA,KAEX,uBAAA,GAA0B,UAAA;EACpC,SAAA,GAAY,eAAA;AAAA;AAAA,iBAUE,aAAA,CACd,UAAA,GAAY,uBAAA,GACX,UAAA"}
|
package/dist/config.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { configureBuild } from "./internal/build/configure-build.mjs";
|
|
2
2
|
import { buildInjectedValues } from "./internal/build/injected.mjs";
|
|
3
|
-
import {
|
|
3
|
+
import { log } from "./internal/logger.mjs";
|
|
4
4
|
import { runGitCommand } from "./internal/build/release/git.mjs";
|
|
5
5
|
import { readInterfereEnv } from "./internal/env.mjs";
|
|
6
6
|
import { releaseDestinationIdEnvKeys, releaseSourceIdEnvKeys } from "@interfere/types/integrations";
|
|
@@ -8,63 +8,54 @@ import { parseEnvValue, readFirstEnvValue } from "@interfere/types/sdk/env";
|
|
|
8
8
|
//#region src/config.ts
|
|
9
9
|
function withInterfere(nextConfig = {}) {
|
|
10
10
|
const { interfere, env: userEnv, webpack, turbopack, compiler, productionBrowserSourceMaps, ...rest } = nextConfig;
|
|
11
|
-
const
|
|
12
|
-
const injectedValues = buildInjectedValues(metadata);
|
|
13
|
-
const hasApiKey = readInterfereEnv().apiKey !== null;
|
|
11
|
+
const config = resolveBuildConfig(interfere);
|
|
14
12
|
const build = configureBuild({
|
|
15
13
|
existingWebpack: webpack,
|
|
16
14
|
existingTurbopack: turbopack,
|
|
17
15
|
projectDir: process.cwd(),
|
|
18
|
-
values:
|
|
16
|
+
values: buildInjectedValues(config)
|
|
19
17
|
});
|
|
20
|
-
if (
|
|
18
|
+
if (config.apiKey !== null && !build.webpack && !build.turbopack) throw new Error("[Interfere] INTERFERE_API_KEY is set but no instrumentation-client file was found. Create an instrumentation-client.ts file in your project root or src/ directory. See: https://interfere.com/docs/nextjs/setup");
|
|
19
|
+
const existingHook = compiler?.runAfterProductionCompile;
|
|
21
20
|
return {
|
|
22
21
|
...rest,
|
|
23
|
-
env: mergeEnvConfig(userEnv,
|
|
24
|
-
compiler:
|
|
22
|
+
env: mergeEnvConfig(userEnv, config),
|
|
23
|
+
compiler: {
|
|
24
|
+
...compiler ?? {},
|
|
25
|
+
async runAfterProductionCompile(context) {
|
|
26
|
+
if (existingHook) await existingHook(context);
|
|
27
|
+
await runBuildReleasePipeline(context, config);
|
|
28
|
+
}
|
|
29
|
+
},
|
|
25
30
|
webpack: build.webpack,
|
|
26
31
|
turbopack: build.turbopack,
|
|
27
|
-
productionBrowserSourceMaps:
|
|
32
|
+
productionBrowserSourceMaps: config.apiKey !== null ? true : productionBrowserSourceMaps
|
|
28
33
|
};
|
|
29
34
|
}
|
|
30
|
-
function
|
|
31
|
-
const
|
|
35
|
+
function resolveBuildConfig(interfere) {
|
|
36
|
+
const { apiKey, apiUrl } = readInterfereEnv();
|
|
37
|
+
const buildId = parseEnvValue(interfere?.buildId) ?? readFirstEnvValue(process.env, releaseSourceIdEnvKeys) ?? runGitCommand("git rev-parse HEAD");
|
|
32
38
|
return {
|
|
39
|
+
apiKey,
|
|
40
|
+
apiUrl,
|
|
33
41
|
buildId,
|
|
34
|
-
releaseId: parseEnvValue(
|
|
42
|
+
releaseId: parseEnvValue(interfere?.releaseId) ?? readFirstEnvValue(process.env, releaseDestinationIdEnvKeys) ?? buildId
|
|
35
43
|
};
|
|
36
44
|
}
|
|
37
|
-
function mergeEnvConfig(userEnv,
|
|
45
|
+
function mergeEnvConfig(userEnv, config) {
|
|
38
46
|
const merged = {};
|
|
39
|
-
if (
|
|
40
|
-
if (
|
|
47
|
+
if (config.buildId !== null) merged.NEXT_PUBLIC_INTERFERE_BUILD_ID = config.buildId;
|
|
48
|
+
if (config.releaseId !== null) merged.NEXT_PUBLIC_INTERFERE_RELEASE_ID = config.releaseId;
|
|
41
49
|
return {
|
|
42
50
|
...merged,
|
|
43
51
|
...userEnv
|
|
44
52
|
};
|
|
45
53
|
}
|
|
46
|
-
function
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
if (!(existingHook || enabled)) return existingCompiler;
|
|
50
|
-
return {
|
|
51
|
-
...compiler ?? {},
|
|
52
|
-
async runAfterProductionCompile(context) {
|
|
53
|
-
if (existingHook) await existingHook(context);
|
|
54
|
-
if (enabled) await runBuildReleasePipeline(context);
|
|
55
|
-
}
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
async function runBuildReleasePipeline(context) {
|
|
59
|
-
const env = readInterfereEnv();
|
|
60
|
-
const { apiKey, apiUrl } = env;
|
|
61
|
-
if (apiKey === null) {
|
|
62
|
-
buildLog.warn("Skipping", ["Missing INTERFERE_API_KEY."]);
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
const buildId = env.release.sourceId;
|
|
54
|
+
async function runBuildReleasePipeline(context, config) {
|
|
55
|
+
const { apiKey, apiUrl, buildId } = config;
|
|
56
|
+
if (apiKey === null) throw new Error("[Interfere] INTERFERE_API_KEY is not set. withInterfere() requires an API key to upload source maps during production builds. Set the INTERFERE_API_KEY environment variable, or remove withInterfere() from your Next.js config. See: https://interfere.com/docs/nextjs/setup");
|
|
66
57
|
if (buildId === null) {
|
|
67
|
-
|
|
58
|
+
log.error("Build ID missing", [
|
|
68
59
|
"Could not resolve a build ID from config, environment variables, or git.",
|
|
69
60
|
"Source maps will not be uploaded and errors will have unresolved stack traces.",
|
|
70
61
|
"Set INTERFERE_BUILD_ID, or ensure git is available in your build environment."
|
|
@@ -75,7 +66,7 @@ async function runBuildReleasePipeline(context) {
|
|
|
75
66
|
const { runSourceMapPipeline } = await import("./internal/build/source-maps/index.mjs");
|
|
76
67
|
const client = {
|
|
77
68
|
async createRelease() {
|
|
78
|
-
const release = await createRelease(apiKey, buildId);
|
|
69
|
+
const release = await createRelease(apiKey, apiUrl, buildId);
|
|
79
70
|
return {
|
|
80
71
|
slug: release.destination.slug,
|
|
81
72
|
orgSlug: release.org.slug,
|
|
@@ -98,17 +89,17 @@ async function runBuildReleasePipeline(context) {
|
|
|
98
89
|
try {
|
|
99
90
|
const result = await runSourceMapPipeline(context.projectDir, context.distDir, client);
|
|
100
91
|
if (!result.ready) {
|
|
101
|
-
|
|
92
|
+
log.warn("Skipping", ["No source maps found"]);
|
|
102
93
|
return;
|
|
103
94
|
}
|
|
104
|
-
|
|
95
|
+
log.info("Completed", [
|
|
105
96
|
`https://interfere.com/~/${result.orgSlug}`,
|
|
106
97
|
`Release: ${result.releaseSlug}`,
|
|
107
98
|
`Build: ${result.buildId}`,
|
|
108
99
|
`Artifacts: ${result.fileCount} source maps`
|
|
109
100
|
]);
|
|
110
101
|
} catch (error) {
|
|
111
|
-
|
|
102
|
+
log.error("Error", [error instanceof Error ? error.message : String(error)]);
|
|
112
103
|
throw error;
|
|
113
104
|
}
|
|
114
105
|
}
|
package/dist/config.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.mjs","names":[],"sources":["../src/config.ts"],"sourcesContent":["import {\n releaseDestinationIdEnvKeys,\n releaseSourceIdEnvKeys,\n} from \"@interfere/types/integrations\";\nimport { parseEnvValue, readFirstEnvValue } from \"@interfere/types/sdk/env\";\nimport type { Envelope } from \"@interfere/types/sdk/envelope\";\n\nimport type { NextConfig } from \"next\";\n\nimport { configureBuild } from \"./internal/build/configure-build.js\";\nimport {
|
|
1
|
+
{"version":3,"file":"config.mjs","names":[],"sources":["../src/config.ts"],"sourcesContent":["import {\n releaseDestinationIdEnvKeys,\n releaseSourceIdEnvKeys,\n} from \"@interfere/types/integrations\";\nimport { parseEnvValue, readFirstEnvValue } from \"@interfere/types/sdk/env\";\nimport type { Envelope } from \"@interfere/types/sdk/envelope\";\n\nimport type { NextConfig } from \"next\";\n\nimport { configureBuild } from \"./internal/build/configure-build.js\";\nimport { buildInjectedValues } from \"./internal/build/injected.js\";\nimport { runGitCommand } from \"./internal/build/release/git.js\";\nimport { readInterfereEnv } from \"./internal/env.js\";\nimport { log } from \"./internal/logger.js\";\n\nexport interface InterfereConfig\n extends Partial<Pick<Envelope, \"buildId\" | \"releaseId\">> {}\n\nexport type NextConfigWithInterfere = NextConfig & {\n interfere?: InterfereConfig;\n};\n\ninterface ResolvedBuildConfig {\n readonly apiKey: string | null;\n readonly apiUrl: string;\n readonly buildId: string | null;\n readonly releaseId: string | null;\n}\n\nexport function withInterfere(\n nextConfig: NextConfigWithInterfere = {}\n): NextConfig {\n const {\n interfere,\n env: userEnv,\n webpack,\n turbopack,\n compiler,\n productionBrowserSourceMaps,\n ...rest\n } = nextConfig;\n\n const config = resolveBuildConfig(interfere);\n const build = configureBuild({\n existingWebpack: webpack,\n existingTurbopack: turbopack,\n projectDir: process.cwd(),\n values: buildInjectedValues(config),\n });\n\n if (config.apiKey !== null && !build.webpack && !build.turbopack) {\n throw new Error(\n \"[Interfere] INTERFERE_API_KEY is set but no instrumentation-client file was found. \" +\n \"Create an instrumentation-client.ts file in your project root or src/ directory. \" +\n \"See: https://interfere.com/docs/nextjs/setup\"\n );\n }\n\n const existingHook = (compiler as NextCompilerWithProductionHook | undefined)\n ?.runAfterProductionCompile;\n\n return {\n ...rest,\n env: mergeEnvConfig(userEnv, config),\n compiler: {\n ...(compiler ?? {}),\n async runAfterProductionCompile(context: ProductionCompileContext) {\n if (existingHook) {\n await existingHook(context);\n }\n\n await runBuildReleasePipeline(context, config);\n },\n } as NextConfig[\"compiler\"],\n webpack: build.webpack,\n turbopack: build.turbopack,\n productionBrowserSourceMaps:\n config.apiKey !== null ? true : productionBrowserSourceMaps,\n };\n}\n\nfunction resolveBuildConfig(interfere?: InterfereConfig): ResolvedBuildConfig {\n const { apiKey, apiUrl } = readInterfereEnv();\n\n const buildId =\n parseEnvValue(interfere?.buildId) ??\n readFirstEnvValue(process.env, releaseSourceIdEnvKeys) ??\n runGitCommand(\"git rev-parse HEAD\");\n const releaseId =\n parseEnvValue(interfere?.releaseId) ??\n readFirstEnvValue(process.env, releaseDestinationIdEnvKeys) ??\n buildId;\n\n return { apiKey, apiUrl, buildId, releaseId };\n}\n\nfunction mergeEnvConfig(\n userEnv: NextConfig[\"env\"] | undefined,\n config: ResolvedBuildConfig\n): NextConfig[\"env\"] {\n const merged: Record<string, string> = {};\n\n if (config.buildId !== null) {\n merged.NEXT_PUBLIC_INTERFERE_BUILD_ID = config.buildId;\n }\n\n if (config.releaseId !== null) {\n merged.NEXT_PUBLIC_INTERFERE_RELEASE_ID = config.releaseId;\n }\n\n return { ...merged, ...userEnv };\n}\n\ninterface ProductionCompileContext {\n readonly distDir: string;\n readonly projectDir: string;\n}\n\ntype NextCompilerWithProductionHook = NonNullable<NextConfig[\"compiler\"]> & {\n runAfterProductionCompile?: (\n context: ProductionCompileContext\n ) => void | Promise<void>;\n};\n\nasync function runBuildReleasePipeline(\n context: ProductionCompileContext,\n config: ResolvedBuildConfig\n): Promise<void> {\n const { apiKey, apiUrl, buildId } = config;\n\n if (apiKey === null) {\n throw new Error(\n \"[Interfere] INTERFERE_API_KEY is not set. \" +\n \"withInterfere() requires an API key to upload source maps during production builds. \" +\n \"Set the INTERFERE_API_KEY environment variable, or remove withInterfere() from your Next.js config. \" +\n \"See: https://interfere.com/docs/nextjs/setup\"\n );\n }\n\n if (buildId === null) {\n log.error(\"Build ID missing\", [\n \"Could not resolve a build ID from config, environment variables, or git.\",\n \"Source maps will not be uploaded and errors will have unresolved stack traces.\",\n \"Set INTERFERE_BUILD_ID, or ensure git is available in your build environment.\",\n ]);\n return;\n }\n\n const { createRelease } = await import(\"./internal/build/release/index.js\");\n const { runSourceMapPipeline } = await import(\n \"./internal/build/source-maps/index.js\"\n );\n\n const client: import(\"./internal/build/source-maps/index.js\").BuildClient = {\n async createRelease() {\n const release = await createRelease(apiKey, apiUrl, buildId);\n return {\n slug: release.destination.slug,\n orgSlug: release.org.slug,\n buildId: release.build.hash ?? buildId,\n };\n },\n async uploadSourceMaps(releaseSlug, body) {\n const url = `${apiUrl}/v1/releases/${releaseSlug}/source-maps`;\n const response = await fetch(url, {\n method: \"POST\",\n headers: { \"x-api-key\": apiKey },\n body,\n });\n\n if (!response.ok) {\n const text = await response.text().catch(() => \"\");\n throw new Error(\n `Source map upload failed: ${response.status} ${response.statusText} ${text}`\n );\n }\n },\n };\n\n try {\n const result = await runSourceMapPipeline(\n context.projectDir,\n context.distDir,\n client\n );\n\n if (!result.ready) {\n log.warn(\"Skipping\", [\"No source maps found\"]);\n return;\n }\n\n log.info(\"Completed\", [\n `https://interfere.com/~/${result.orgSlug}`,\n `Release: ${result.releaseSlug}`,\n `Build: ${result.buildId}`,\n `Artifacts: ${result.fileCount} source maps`,\n ]);\n } catch (error) {\n log.error(\"Error\", [\n error instanceof Error ? error.message : String(error),\n ]);\n throw error;\n }\n}\n"],"mappings":";;;;;;;;AA6BA,SAAgB,cACd,aAAsC,EAAE,EAC5B;CACZ,MAAM,EACJ,WACA,KAAK,SACL,SACA,WACA,UACA,6BACA,GAAG,SACD;CAEJ,MAAM,SAAS,mBAAmB,UAAU;CAC5C,MAAM,QAAQ,eAAe;EAC3B,iBAAiB;EACjB,mBAAmB;EACnB,YAAY,QAAQ,KAAK;EACzB,QAAQ,oBAAoB,OAAO;EACpC,CAAC;AAEF,KAAI,OAAO,WAAW,QAAQ,CAAC,MAAM,WAAW,CAAC,MAAM,UACrD,OAAM,IAAI,MACR,mNAGD;CAGH,MAAM,eAAgB,UAClB;AAEJ,QAAO;EACL,GAAG;EACH,KAAK,eAAe,SAAS,OAAO;EACpC,UAAU;GACR,GAAI,YAAY,EAAE;GAClB,MAAM,0BAA0B,SAAmC;AACjE,QAAI,aACF,OAAM,aAAa,QAAQ;AAG7B,UAAM,wBAAwB,SAAS,OAAO;;GAEjD;EACD,SAAS,MAAM;EACf,WAAW,MAAM;EACjB,6BACE,OAAO,WAAW,OAAO,OAAO;EACnC;;AAGH,SAAS,mBAAmB,WAAkD;CAC5E,MAAM,EAAE,QAAQ,WAAW,kBAAkB;CAE7C,MAAM,UACJ,cAAc,WAAW,QAAQ,IACjC,kBAAkB,QAAQ,KAAK,uBAAuB,IACtD,cAAc,qBAAqB;AAMrC,QAAO;EAAE;EAAQ;EAAQ;EAAS,WAJhC,cAAc,WAAW,UAAU,IACnC,kBAAkB,QAAQ,KAAK,4BAA4B,IAC3D;EAE2C;;AAG/C,SAAS,eACP,SACA,QACmB;CACnB,MAAM,SAAiC,EAAE;AAEzC,KAAI,OAAO,YAAY,KACrB,QAAO,iCAAiC,OAAO;AAGjD,KAAI,OAAO,cAAc,KACvB,QAAO,mCAAmC,OAAO;AAGnD,QAAO;EAAE,GAAG;EAAQ,GAAG;EAAS;;AAclC,eAAe,wBACb,SACA,QACe;CACf,MAAM,EAAE,QAAQ,QAAQ,YAAY;AAEpC,KAAI,WAAW,KACb,OAAM,IAAI,MACR,iRAID;AAGH,KAAI,YAAY,MAAM;AACpB,MAAI,MAAM,oBAAoB;GAC5B;GACA;GACA;GACD,CAAC;AACF;;CAGF,MAAM,EAAE,kBAAkB,MAAM,OAAO;CACvC,MAAM,EAAE,yBAAyB,MAAM,OACrC;CAGF,MAAM,SAAsE;EAC1E,MAAM,gBAAgB;GACpB,MAAM,UAAU,MAAM,cAAc,QAAQ,QAAQ,QAAQ;AAC5D,UAAO;IACL,MAAM,QAAQ,YAAY;IAC1B,SAAS,QAAQ,IAAI;IACrB,SAAS,QAAQ,MAAM,QAAQ;IAChC;;EAEH,MAAM,iBAAiB,aAAa,MAAM;GACxC,MAAM,MAAM,GAAG,OAAO,eAAe,YAAY;GACjD,MAAM,WAAW,MAAM,MAAM,KAAK;IAChC,QAAQ;IACR,SAAS,EAAE,aAAa,QAAQ;IAChC;IACD,CAAC;AAEF,OAAI,CAAC,SAAS,IAAI;IAChB,MAAM,OAAO,MAAM,SAAS,MAAM,CAAC,YAAY,GAAG;AAClD,UAAM,IAAI,MACR,6BAA6B,SAAS,OAAO,GAAG,SAAS,WAAW,GAAG,OACxE;;;EAGN;AAED,KAAI;EACF,MAAM,SAAS,MAAM,qBACnB,QAAQ,YACR,QAAQ,SACR,OACD;AAED,MAAI,CAAC,OAAO,OAAO;AACjB,OAAI,KAAK,YAAY,CAAC,uBAAuB,CAAC;AAC9C;;AAGF,MAAI,KAAK,aAAa;GACpB,2BAA2B,OAAO;GAClC,YAAY,OAAO;GACnB,UAAU,OAAO;GACjB,cAAc,OAAO,UAAU;GAChC,CAAC;UACK,OAAO;AACd,MAAI,MAAM,SAAS,CACjB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,CACvD,CAAC;AACF,QAAM"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { log } from "../../logger.mjs";
|
|
2
2
|
import { execSync } from "node:child_process";
|
|
3
3
|
//#region src/internal/build/release/git.ts
|
|
4
4
|
function runGitCommand(command) {
|
|
@@ -13,7 +13,7 @@ function runGitCommand(command) {
|
|
|
13
13
|
}).trim();
|
|
14
14
|
return output.length > 0 ? output : null;
|
|
15
15
|
} catch {
|
|
16
|
-
|
|
16
|
+
log.warn("Git unavailable", [`Failed: ${command}`]);
|
|
17
17
|
return null;
|
|
18
18
|
}
|
|
19
19
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git.mjs","names":[],"sources":["../../../../src/internal/build/release/git.ts"],"sourcesContent":["import { execSync } from \"node:child_process\";\n\nimport {
|
|
1
|
+
{"version":3,"file":"git.mjs","names":[],"sources":["../../../../src/internal/build/release/git.ts"],"sourcesContent":["import { execSync } from \"node:child_process\";\n\nimport { log } from \"../../logger.js\";\n\nexport function runGitCommand(command: string): string | null {\n try {\n const output = execSync(command, {\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n }).trim();\n\n return output.length > 0 ? output : null;\n } catch {\n log.warn(\"Git unavailable\", [`Failed: ${command}`]);\n return null;\n }\n}\n"],"mappings":";;;AAIA,SAAgB,cAAc,SAAgC;AAC5D,KAAI;EACF,MAAM,SAAS,SAAS,SAAS;GAC/B,UAAU;GACV,OAAO;IAAC;IAAU;IAAQ;IAAS;GACpC,CAAC,CAAC,MAAM;AAET,SAAO,OAAO,SAAS,IAAI,SAAS;SAC9B;AACN,MAAI,KAAK,mBAAmB,CAAC,WAAW,UAAU,CAAC;AACnD,SAAO"}
|
|
@@ -2,6 +2,6 @@ import { runGitCommand } from "./git.mjs";
|
|
|
2
2
|
import { CreateReleaseResponse } from "@interfere/types/releases/definition";
|
|
3
3
|
|
|
4
4
|
//#region src/internal/build/release/index.d.ts
|
|
5
|
-
declare function createRelease(apiKey: string, buildId: string): Promise<CreateReleaseResponse>;
|
|
5
|
+
declare function createRelease(apiKey: string, apiUrl: string, buildId: string): Promise<CreateReleaseResponse>;
|
|
6
6
|
//#endregion
|
|
7
7
|
export { createRelease, runGitCommand };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../../../../src/internal/build/release/index.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../../../src/internal/build/release/index.ts"],"mappings":";;;;iBAuBsB,aAAA,CACpB,MAAA,UACA,MAAA,UACA,OAAA,WACC,OAAA,CAAQ,qBAAA"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { runGitCommand } from "./git.mjs";
|
|
2
|
-
import { readInterfereEnv } from "../../env.mjs";
|
|
3
2
|
import { vercel_exports } from "./destinations/vercel.mjs";
|
|
4
3
|
import { github_exports } from "./sources/github.mjs";
|
|
5
4
|
import { Interfere } from "@interfere/sdk";
|
|
@@ -7,8 +6,7 @@ import { InterfereHTTPError } from "@interfere/sdk/models/errors/interfere-http-
|
|
|
7
6
|
//#region src/internal/build/release/index.ts
|
|
8
7
|
const sources = { github: github_exports };
|
|
9
8
|
const destinations = { vercel: vercel_exports };
|
|
10
|
-
async function createRelease(apiKey, buildId) {
|
|
11
|
-
const { apiUrl } = readInterfereEnv();
|
|
9
|
+
async function createRelease(apiKey, apiUrl, buildId) {
|
|
12
10
|
const sdk = new Interfere({ serverURL: apiUrl });
|
|
13
11
|
const request = {
|
|
14
12
|
source: sources.github.resolve(),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/internal/build/release/index.ts"],"sourcesContent":["import type {\n DestinationProvider,\n SourceProvider,\n} from \"@interfere/types/integrations\";\nimport type { CreateReleaseResponse } from \"@interfere/types/releases/definition\";\n\nimport { Interfere } from \"@interfere/sdk\";\nimport { InterfereHTTPError } from \"@interfere/sdk/models/errors/interfere-http-error.js\";\n\nimport
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/internal/build/release/index.ts"],"sourcesContent":["import type {\n DestinationProvider,\n SourceProvider,\n} from \"@interfere/types/integrations\";\nimport type { CreateReleaseResponse } from \"@interfere/types/releases/definition\";\n\nimport { Interfere } from \"@interfere/sdk\";\nimport { InterfereHTTPError } from \"@interfere/sdk/models/errors/interfere-http-error.js\";\n\nimport * as vercel from \"./destinations/vercel.js\";\nimport * as github from \"./sources/github.js\";\nimport type { DestinationResolver, SourceResolver } from \"./types.js\";\n\nexport { runGitCommand } from \"./git.js\";\n\nconst sources: Record<SourceProvider, SourceResolver> = {\n github,\n};\n\nconst destinations: Record<DestinationProvider, DestinationResolver> = {\n vercel,\n};\n\nexport async function createRelease(\n apiKey: string,\n apiUrl: string,\n buildId: string,\n): Promise<CreateReleaseResponse> {\n const sdk = new Interfere({ serverURL: apiUrl });\n\n const request = {\n source: sources.github.resolve(),\n destination: destinations.vercel.resolve(),\n buildId,\n };\n\n try {\n return await sdk.releases.createRelease(request, {\n headers: { \"x-api-key\": apiKey },\n });\n } catch (error) {\n if (error instanceof InterfereHTTPError && error.statusCode === 404) {\n throw new Error(\n `Interfere release API returned 404 at '${error.rawResponse.url}'. Is INTERFERE_API_URL incorrectly set?`,\n );\n }\n\n throw error;\n }\n}\n"],"mappings":";;;;;;AAeA,MAAM,UAAkD,EACtD,QAAA,gBACD;AAED,MAAM,eAAiE,EACrE,QAAA,gBACD;AAED,eAAsB,cACpB,QACA,QACA,SACgC;CAChC,MAAM,MAAM,IAAI,UAAU,EAAE,WAAW,QAAQ,CAAC;CAEhD,MAAM,UAAU;EACd,QAAQ,QAAQ,OAAO,SAAS;EAChC,aAAa,aAAa,OAAO,SAAS;EAC1C;EACD;AAED,KAAI;AACF,SAAO,MAAM,IAAI,SAAS,cAAc,SAAS,EAC/C,SAAS,EAAE,aAAa,QAAQ,EACjC,CAAC;UACK,OAAO;AACd,MAAI,iBAAiB,sBAAsB,MAAM,eAAe,IAC9D,OAAM,IAAI,MACR,0CAA0C,MAAM,YAAY,IAAI,0CACjE;AAGH,QAAM"}
|
package/dist/internal/env.d.mts
CHANGED
|
@@ -4,6 +4,7 @@ import { Env } from "@interfere/types/sdk/runtime";
|
|
|
4
4
|
interface InterfereEnv {
|
|
5
5
|
readonly apiKey: string | null;
|
|
6
6
|
readonly apiUrl: string;
|
|
7
|
+
readonly nextRuntime: string | null;
|
|
7
8
|
readonly nodeEnvironment: Exclude<Env, null>;
|
|
8
9
|
readonly release: {
|
|
9
10
|
readonly sourceId: string | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env.d.mts","names":[],"sources":["../../src/internal/env.ts"],"mappings":";;;UAKiB,YAAA;EAAA,SACN,MAAA;EAAA,SACA,MAAA;EAAA,SACA,eAAA,EAAiB,OAAA,CAAQ,GAAA;EAAA,SACzB,OAAA;IAAA,SACE,QAAA;IAAA,SACA,aAAA;EAAA;AAAA;AAAA,iBAIG,gBAAA,CAAA,GAAoB,YAAA"}
|
|
1
|
+
{"version":3,"file":"env.d.mts","names":[],"sources":["../../src/internal/env.ts"],"mappings":";;;UAKiB,YAAA;EAAA,SACN,MAAA;EAAA,SACA,MAAA;EAAA,SACA,WAAA;EAAA,SACA,eAAA,EAAiB,OAAA,CAAQ,GAAA;EAAA,SACzB,OAAA;IAAA,SACE,QAAA;IAAA,SACA,aAAA;EAAA;AAAA;AAAA,iBAIG,gBAAA,CAAA,GAAoB,YAAA"}
|
package/dist/internal/env.mjs
CHANGED
|
@@ -7,6 +7,7 @@ function readInterfereEnv() {
|
|
|
7
7
|
return {
|
|
8
8
|
apiKey: parseEnvValue(process.env.INTERFERE_API_KEY),
|
|
9
9
|
apiUrl: parseEnvValue(process.env.INTERFERE_API_URL) ?? API_URL,
|
|
10
|
+
nextRuntime: parseEnvValue(process.env.NEXT_RUNTIME),
|
|
10
11
|
nodeEnvironment,
|
|
11
12
|
release: {
|
|
12
13
|
sourceId: parseEnvValue(process.env.NEXT_PUBLIC_INTERFERE_BUILD_ID),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env.mjs","names":[],"sources":["../../src/internal/env.ts"],"sourcesContent":["import { API_URL } from \"@interfere/constants/api\";\nimport { parseEnvValue } from \"@interfere/types/sdk/env\";\nimport type { Env } from \"@interfere/types/sdk/runtime\";\nimport { normalizeEnv } from \"@interfere/types/sdk/runtime\";\n\nexport interface InterfereEnv {\n readonly apiKey: string | null;\n readonly apiUrl: string;\n readonly nodeEnvironment: Exclude<Env, null>;\n readonly release: {\n readonly sourceId: string | null;\n readonly destinationId: string | null;\n };\n}\n\nexport function readInterfereEnv(): InterfereEnv {\n const nodeEnvironment = normalizeEnv(process.env.NODE_ENV) ?? \"production\";\n return {\n apiKey: parseEnvValue(process.env.INTERFERE_API_KEY),\n apiUrl: parseEnvValue(process.env.INTERFERE_API_URL) ?? API_URL,\n nodeEnvironment,\n release: {\n sourceId: parseEnvValue(process.env.NEXT_PUBLIC_INTERFERE_BUILD_ID),\n destinationId: parseEnvValue(\n process.env.NEXT_PUBLIC_INTERFERE_RELEASE_ID\n ),\n },\n };\n}\n"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"env.mjs","names":[],"sources":["../../src/internal/env.ts"],"sourcesContent":["import { API_URL } from \"@interfere/constants/api\";\nimport { parseEnvValue } from \"@interfere/types/sdk/env\";\nimport type { Env } from \"@interfere/types/sdk/runtime\";\nimport { normalizeEnv } from \"@interfere/types/sdk/runtime\";\n\nexport interface InterfereEnv {\n readonly apiKey: string | null;\n readonly apiUrl: string;\n readonly nextRuntime: string | null;\n readonly nodeEnvironment: Exclude<Env, null>;\n readonly release: {\n readonly sourceId: string | null;\n readonly destinationId: string | null;\n };\n}\n\nexport function readInterfereEnv(): InterfereEnv {\n const nodeEnvironment = normalizeEnv(process.env.NODE_ENV) ?? \"production\";\n return {\n apiKey: parseEnvValue(process.env.INTERFERE_API_KEY),\n apiUrl: parseEnvValue(process.env.INTERFERE_API_URL) ?? API_URL,\n nextRuntime: parseEnvValue(process.env.NEXT_RUNTIME),\n nodeEnvironment,\n release: {\n sourceId: parseEnvValue(process.env.NEXT_PUBLIC_INTERFERE_BUILD_ID),\n destinationId: parseEnvValue(\n process.env.NEXT_PUBLIC_INTERFERE_RELEASE_ID\n ),\n },\n };\n}\n"],"mappings":";;;;AAgBA,SAAgB,mBAAiC;CAC/C,MAAM,kBAAkB,aAAa,QAAQ,IAAI,SAAS,IAAI;AAC9D,QAAO;EACL,QAAQ,cAAc,QAAQ,IAAI,kBAAkB;EACpD,QAAQ,cAAc,QAAQ,IAAI,kBAAkB,IAAI;EACxD,aAAa,cAAc,QAAQ,IAAI,aAAa;EACpD;EACA,SAAS;GACP,UAAU,cAAc,QAAQ,IAAI,+BAA+B;GACnE,eAAe,cACb,QAAQ,IAAI,iCACb;GACF;EACF"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
//#region src/internal/
|
|
2
|
-
declare const
|
|
1
|
+
//#region src/internal/logger.d.ts
|
|
2
|
+
declare const log: {
|
|
3
3
|
info: (title: string, lines: string[]) => void;
|
|
4
4
|
warn: (title: string, lines: string[]) => void;
|
|
5
5
|
error: (title: string, lines: string[]) => void;
|
|
6
6
|
};
|
|
7
7
|
//#endregion
|
|
8
|
-
export {
|
|
8
|
+
export { log };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.mts","names":[],"sources":["../../src/internal/logger.ts"],"mappings":";cA0Da,GAAA;wBACS,KAAA;wBACA,KAAA;yBACC,KAAA;AAAA"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
|
-
//#region src/internal/
|
|
2
|
+
//#region src/internal/logger.ts
|
|
3
3
|
const styles = {
|
|
4
4
|
info: {
|
|
5
5
|
prefix: `${chalk.whiteBright.bold("❖")}`,
|
|
@@ -35,10 +35,10 @@ function emit(level, title, lines) {
|
|
|
35
35
|
invoke(`${style.prefix} ${chalk.white("Interfere →")} ${style.text(title)}`);
|
|
36
36
|
for (const [i, line] of lines.entries()) invoke(`${i === lines.length - 1 ? "└" : "├"} ${style.content(line)}`);
|
|
37
37
|
}
|
|
38
|
-
const
|
|
38
|
+
const log = {
|
|
39
39
|
info: (title, lines) => emit("info", title, lines),
|
|
40
40
|
warn: (title, lines) => emit("warn", title, lines),
|
|
41
41
|
error: (title, lines) => emit("error", title, lines)
|
|
42
42
|
};
|
|
43
43
|
//#endregion
|
|
44
|
-
export {
|
|
44
|
+
export { log };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.mjs","names":[],"sources":["../../src/internal/logger.ts"],"sourcesContent":["import chalk from \"chalk\";\n\ntype LogLevel = \"info\" | \"warn\" | \"error\";\n\nconst styles = {\n info: {\n prefix: `${chalk.whiteBright.bold(\"❖\")}`,\n text: chalk.cyan.bold,\n content: chalk.white,\n },\n warn: {\n prefix: `${chalk.yellow.bold(\"⚠\")} `,\n text: chalk.yellow.bold,\n content: chalk.yellowBright,\n },\n error: {\n prefix: `${chalk.red.bold(\"⨯\")} `,\n text: chalk.red.bold,\n content: chalk.redBright,\n },\n} satisfies Record<\n LogLevel,\n { prefix: string; text: typeof chalk.bold; content: typeof chalk }\n>;\n\nconst consoleMethods = {\n info: \"log\",\n warn: \"warn\",\n error: \"error\",\n} satisfies Record<LogLevel, string>;\n\nfunction isTestEnv() {\n return Boolean(process.env.VITEST || process.env.VITEST_WORKER_ID);\n}\n\nfunction emit(level: LogLevel, title: string, lines: string[]) {\n if (isTestEnv()) {\n return;\n }\n\n const style = styles[level];\n const method = consoleMethods[level] as keyof Console;\n const fn = globalThis.console[method];\n if (typeof fn !== \"function\") {\n return;\n }\n\n const invoke = (...args: unknown[]) =>\n Reflect.apply(fn, globalThis.console, args);\n\n invoke(`${style.prefix} ${chalk.white(\"Interfere →\")} ${style.text(title)}`);\n\n for (const [i, line] of lines.entries()) {\n const connector = i === lines.length - 1 ? \"└\" : \"├\";\n invoke(`${connector} ${style.content(line)}`);\n }\n}\n\nexport const log = {\n info: (title: string, lines: string[]) => emit(\"info\", title, lines),\n warn: (title: string, lines: string[]) => emit(\"warn\", title, lines),\n error: (title: string, lines: string[]) => emit(\"error\", title, lines),\n};\n"],"mappings":";;AAIA,MAAM,SAAS;CACb,MAAM;EACJ,QAAQ,GAAG,MAAM,YAAY,KAAK,IAAI;EACtC,MAAM,MAAM,KAAK;EACjB,SAAS,MAAM;EAChB;CACD,MAAM;EACJ,QAAQ,GAAG,MAAM,OAAO,KAAK,IAAI,CAAC;EAClC,MAAM,MAAM,OAAO;EACnB,SAAS,MAAM;EAChB;CACD,OAAO;EACL,QAAQ,GAAG,MAAM,IAAI,KAAK,IAAI,CAAC;EAC/B,MAAM,MAAM,IAAI;EAChB,SAAS,MAAM;EAChB;CACF;AAKD,MAAM,iBAAiB;CACrB,MAAM;CACN,MAAM;CACN,OAAO;CACR;AAED,SAAS,YAAY;AACnB,QAAO,QAAQ,QAAQ,IAAI,UAAU,QAAQ,IAAI,iBAAiB;;AAGpE,SAAS,KAAK,OAAiB,OAAe,OAAiB;AAC7D,KAAI,WAAW,CACb;CAGF,MAAM,QAAQ,OAAO;CACrB,MAAM,SAAS,eAAe;CAC9B,MAAM,KAAK,WAAW,QAAQ;AAC9B,KAAI,OAAO,OAAO,WAChB;CAGF,MAAM,UAAU,GAAG,SACjB,QAAQ,MAAM,IAAI,WAAW,SAAS,KAAK;AAE7C,QAAO,GAAG,MAAM,OAAO,GAAG,MAAM,MAAM,cAAc,CAAC,GAAG,MAAM,KAAK,MAAM,GAAG;AAE5E,MAAK,MAAM,CAAC,GAAG,SAAS,MAAM,SAAS,CAErC,QAAO,GADW,MAAM,MAAM,SAAS,IAAI,MAAM,IAC7B,GAAG,MAAM,QAAQ,KAAK,GAAG;;AAIjD,MAAa,MAAM;CACjB,OAAO,OAAe,UAAoB,KAAK,QAAQ,OAAO,MAAM;CACpE,OAAO,OAAe,UAAoB,KAAK,QAAQ,OAAO,MAAM;CACpE,QAAQ,OAAe,UAAoB,KAAK,SAAS,OAAO,MAAM;CACvE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handle-post.d.mts","names":[],"sources":["../../../src/internal/route/handle-post.ts"],"mappings":";
|
|
1
|
+
{"version":3,"file":"handle-post.d.mts","names":[],"sources":["../../../src/internal/route/handle-post.ts"],"mappings":";iBA2CsB,UAAA,CAAW,OAAA,EAAS,OAAA,GAAU,OAAA,CAAQ,QAAA"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { log } from "../logger.mjs";
|
|
1
2
|
import { readInterfereEnv } from "../env.mjs";
|
|
2
3
|
import { API_PATHS } from "@interfere/constants/api";
|
|
3
4
|
//#region src/internal/route/handle-post.ts
|
|
@@ -20,7 +21,7 @@ function extractSubPath(request) {
|
|
|
20
21
|
async function handlePost(request) {
|
|
21
22
|
const env = readInterfereEnv();
|
|
22
23
|
if (env.apiKey === null) {
|
|
23
|
-
|
|
24
|
+
log.warn("Not configured", ["INTERFERE_API_KEY is not set. The proxy route will return 503."]);
|
|
24
25
|
return Response.json({
|
|
25
26
|
code: "INTERFERE_NOT_CONFIGURED",
|
|
26
27
|
message: "INTERFERE_API_KEY is required."
|
|
@@ -36,7 +37,7 @@ async function handlePost(request) {
|
|
|
36
37
|
if (subPath === API_PATHS.INGEST) return await handleIngest(request, authed);
|
|
37
38
|
return await forwardToCollector(request, authed, subPath);
|
|
38
39
|
} catch (error) {
|
|
39
|
-
|
|
40
|
+
log.error(`Proxy ${request.method} ${subPath} failed`, [error instanceof Error ? error.message : String(error)]);
|
|
40
41
|
return Response.json({
|
|
41
42
|
code: "INTERFERE_PROXY_ERROR",
|
|
42
43
|
message: "Proxy request failed."
|
|
@@ -77,7 +78,6 @@ async function handleIngest(request, env) {
|
|
|
77
78
|
async function forwardToCollector(request, env, subPath) {
|
|
78
79
|
const url = `${env.apiUrl}${subPath}`;
|
|
79
80
|
const traceparent = request.headers.get("traceparent");
|
|
80
|
-
console.debug(`[interfere] proxy ${request.method} ${subPath} -> ${url}`);
|
|
81
81
|
const upstream = await fetch(url, {
|
|
82
82
|
method: request.method,
|
|
83
83
|
headers: {
|
|
@@ -90,7 +90,7 @@ async function forwardToCollector(request, env, subPath) {
|
|
|
90
90
|
});
|
|
91
91
|
if (!upstream.ok) {
|
|
92
92
|
const body = await upstream.text().catch(() => "");
|
|
93
|
-
|
|
93
|
+
log.error(`Upstream ${upstream.status} for ${request.method} ${subPath}`, [body]);
|
|
94
94
|
return Response.json({
|
|
95
95
|
code: "INTERFERE_UPSTREAM_ERROR",
|
|
96
96
|
message: body
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handle-post.mjs","names":[],"sources":["../../../src/internal/route/handle-post.ts"],"sourcesContent":["import { API_PATHS } from \"@interfere/constants/api\";\nimport type { Envelope } from \"@interfere/types/sdk/envelope\";\n\nimport { type InterfereEnv, readInterfereEnv } from \"../env.js\";\n\nfunction parseEnvelopes(value: unknown): Envelope[] | null {\n if (!Array.isArray(value)) {\n return null;\n }\n\n return value as Envelope[];\n}\n\nfunction injectReleaseMetadata(\n envelopes: Envelope[],\n metadata: { sourceId: string | null; destinationId: string | null }\n): Envelope[] {\n if (metadata.sourceId === null && metadata.destinationId === null) {\n return envelopes;\n }\n\n return envelopes.map((envelope) => ({\n ...envelope,\n buildId: metadata.sourceId ?? envelope.buildId,\n releaseId: metadata.destinationId ?? envelope.releaseId,\n }));\n}\n\nconst PROXY_PATH_PATTERN = /\\/api\\/interfere(\\/.*)/;\n\nfunction extractSubPath(request: Request): string {\n const url = new URL(request.url);\n const match = url.pathname.match(PROXY_PATH_PATTERN);\n return match?.[1] ?? \"/\";\n}\n\ninterface AuthenticatedEnv {\n apiKey: string;\n apiUrl: string;\n release: InterfereEnv[\"release\"];\n}\n\nexport async function handlePost(request: Request): Promise<Response> {\n const env = readInterfereEnv();\n\n if (env.apiKey === null) {\n
|
|
1
|
+
{"version":3,"file":"handle-post.mjs","names":[],"sources":["../../../src/internal/route/handle-post.ts"],"sourcesContent":["import { API_PATHS } from \"@interfere/constants/api\";\nimport type { Envelope } from \"@interfere/types/sdk/envelope\";\n\nimport { type InterfereEnv, readInterfereEnv } from \"../env.js\";\nimport { log } from \"../logger.js\";\n\nfunction parseEnvelopes(value: unknown): Envelope[] | null {\n if (!Array.isArray(value)) {\n return null;\n }\n\n return value as Envelope[];\n}\n\nfunction injectReleaseMetadata(\n envelopes: Envelope[],\n metadata: { sourceId: string | null; destinationId: string | null }\n): Envelope[] {\n if (metadata.sourceId === null && metadata.destinationId === null) {\n return envelopes;\n }\n\n return envelopes.map((envelope) => ({\n ...envelope,\n buildId: metadata.sourceId ?? envelope.buildId,\n releaseId: metadata.destinationId ?? envelope.releaseId,\n }));\n}\n\nconst PROXY_PATH_PATTERN = /\\/api\\/interfere(\\/.*)/;\n\nfunction extractSubPath(request: Request): string {\n const url = new URL(request.url);\n const match = url.pathname.match(PROXY_PATH_PATTERN);\n return match?.[1] ?? \"/\";\n}\n\ninterface AuthenticatedEnv {\n apiKey: string;\n apiUrl: string;\n release: InterfereEnv[\"release\"];\n}\n\nexport async function handlePost(request: Request): Promise<Response> {\n const env = readInterfereEnv();\n\n if (env.apiKey === null) {\n log.warn(\"Not configured\", [\n \"INTERFERE_API_KEY is not set. The proxy route will return 503.\",\n ]);\n\n return Response.json(\n {\n code: \"INTERFERE_NOT_CONFIGURED\",\n message: \"INTERFERE_API_KEY is required.\",\n },\n { status: 503 }\n );\n }\n\n const authed: AuthenticatedEnv = {\n apiKey: env.apiKey,\n apiUrl: env.apiUrl,\n release: env.release,\n };\n\n const subPath = extractSubPath(request);\n\n try {\n if (subPath === API_PATHS.INGEST) {\n return await handleIngest(request, authed);\n }\n\n return await forwardToCollector(request, authed, subPath);\n } catch (error) {\n log.error(`Proxy ${request.method} ${subPath} failed`, [\n error instanceof Error ? error.message : String(error),\n ]);\n return Response.json(\n { code: \"INTERFERE_PROXY_ERROR\", message: \"Proxy request failed.\" },\n { status: 502 }\n );\n }\n}\n\nasync function handleIngest(\n request: Request,\n env: AuthenticatedEnv\n): Promise<Response> {\n let payload: unknown;\n try {\n payload = await request.json();\n } catch {\n return Response.json(\n {\n code: \"INTERFERE_INVALID_JSON\",\n message: \"Request body must be valid JSON.\",\n },\n { status: 400 }\n );\n }\n\n const envelopes = parseEnvelopes(payload);\n if (envelopes === null) {\n return Response.json(\n {\n code: \"INTERFERE_INVALID_ENVELOPES\",\n message: \"Request body must be an array of envelopes.\",\n },\n { status: 400 }\n );\n }\n\n const traceparent = request.headers.get(\"traceparent\");\n const upstream = await fetch(`${env.apiUrl}${API_PATHS.INGEST}`, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n \"x-api-key\": env.apiKey,\n ...(traceparent ? { traceparent } : {}),\n },\n body: JSON.stringify(injectReleaseMetadata(envelopes, env.release)),\n keepalive: true,\n });\n\n return new Response(upstream.body, {\n status: upstream.status,\n headers: {\n \"content-type\":\n upstream.headers.get(\"content-type\") ?? \"application/json\",\n },\n });\n}\n\nasync function forwardToCollector(\n request: Request,\n env: AuthenticatedEnv,\n subPath: string\n): Promise<Response> {\n const url = `${env.apiUrl}${subPath}`;\n const traceparent = request.headers.get(\"traceparent\");\n\n const upstream = await fetch(url, {\n method: request.method,\n headers: {\n \"content-type\": request.headers.get(\"content-type\") ?? \"application/json\",\n \"x-api-key\": env.apiKey,\n ...(traceparent ? { traceparent } : {}),\n },\n body: request.body,\n keepalive: true,\n });\n\n if (!upstream.ok) {\n const body = await upstream.text().catch(() => \"\");\n log.error(`Upstream ${upstream.status} for ${request.method} ${subPath}`, [\n body,\n ]);\n return Response.json(\n { code: \"INTERFERE_UPSTREAM_ERROR\", message: body },\n { status: upstream.status }\n );\n }\n\n return new Response(upstream.body, {\n status: upstream.status,\n headers: {\n \"content-type\":\n upstream.headers.get(\"content-type\") ?? \"application/json\",\n },\n });\n}\n"],"mappings":"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"}
|
|
@@ -9,7 +9,7 @@ function resolveServerCaptureRuntime() {
|
|
|
9
9
|
apiKey: env.apiKey,
|
|
10
10
|
ingestUrl: `${env.apiUrl}${API_PATHS.INGEST}`,
|
|
11
11
|
environment: env.nodeEnvironment,
|
|
12
|
-
runtime: inferRuntime({ nextRuntime:
|
|
12
|
+
runtime: inferRuntime({ nextRuntime: env.nextRuntime }),
|
|
13
13
|
buildId: env.release.sourceId,
|
|
14
14
|
releaseId: env.release.destinationId
|
|
15
15
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.mjs","names":[],"sources":["../../../src/internal/server/runtime.ts"],"sourcesContent":["import { API_PATHS } from \"@interfere/constants/api\";\nimport type { Env, Runtime } from \"@interfere/types/sdk/runtime\";\nimport { inferRuntime } from \"@interfere/types/sdk/runtime\";\n\nimport { readInterfereEnv } from \"../env.js\";\n\nexport interface ServerCaptureRuntime {\n readonly apiKey: string | null;\n readonly buildId: string;\n readonly environment: Env;\n readonly ingestUrl: string;\n readonly releaseId: string | null;\n readonly runtime: Runtime;\n}\n\nexport function resolveServerCaptureRuntime(): ServerCaptureRuntime {\n const env = readInterfereEnv();\n\n if (env.release.sourceId === null) {\n throw new Error(\n \"[interfere] Missing NEXT_PUBLIC_INTERFERE_BUILD_ID at runtime. \" +\n \"Ensure withInterfere() wraps your Next.js config.\"\n );\n }\n\n return {\n apiKey: env.apiKey,\n ingestUrl: `${env.apiUrl}${API_PATHS.INGEST}`,\n environment: env.nodeEnvironment,\n runtime: inferRuntime({ nextRuntime:
|
|
1
|
+
{"version":3,"file":"runtime.mjs","names":[],"sources":["../../../src/internal/server/runtime.ts"],"sourcesContent":["import { API_PATHS } from \"@interfere/constants/api\";\nimport type { Env, Runtime } from \"@interfere/types/sdk/runtime\";\nimport { inferRuntime } from \"@interfere/types/sdk/runtime\";\n\nimport { readInterfereEnv } from \"../env.js\";\n\nexport interface ServerCaptureRuntime {\n readonly apiKey: string | null;\n readonly buildId: string;\n readonly environment: Env;\n readonly ingestUrl: string;\n readonly releaseId: string | null;\n readonly runtime: Runtime;\n}\n\nexport function resolveServerCaptureRuntime(): ServerCaptureRuntime {\n const env = readInterfereEnv();\n\n if (env.release.sourceId === null) {\n throw new Error(\n \"[interfere] Missing NEXT_PUBLIC_INTERFERE_BUILD_ID at runtime. \" +\n \"Ensure withInterfere() wraps your Next.js config.\"\n );\n }\n\n return {\n apiKey: env.apiKey,\n ingestUrl: `${env.apiUrl}${API_PATHS.INGEST}`,\n environment: env.nodeEnvironment,\n runtime: inferRuntime({ nextRuntime: env.nextRuntime }),\n buildId: env.release.sourceId,\n releaseId: env.release.destinationId,\n };\n}\n"],"mappings":";;;;AAeA,SAAgB,8BAAoD;CAClE,MAAM,MAAM,kBAAkB;AAE9B,KAAI,IAAI,QAAQ,aAAa,KAC3B,OAAM,IAAI,MACR,mHAED;AAGH,QAAO;EACL,QAAQ,IAAI;EACZ,WAAW,GAAG,IAAI,SAAS,UAAU;EACrC,aAAa,IAAI;EACjB,SAAS,aAAa,EAAE,aAAa,IAAI,aAAa,CAAC;EACvD,SAAS,IAAI,QAAQ;EACrB,WAAW,IAAI,QAAQ;EACxB"}
|
|
@@ -4,7 +4,9 @@ import { Envelope } from "@interfere/types/sdk/envelope";
|
|
|
4
4
|
//#region src/internal/server/transport.d.ts
|
|
5
5
|
interface SendEnvelopeInput {
|
|
6
6
|
readonly envelope: Envelope<"error">;
|
|
7
|
-
readonly runtime: ServerCaptureRuntime
|
|
7
|
+
readonly runtime: ServerCaptureRuntime & {
|
|
8
|
+
apiKey: string;
|
|
9
|
+
};
|
|
8
10
|
readonly traceparent?: string;
|
|
9
11
|
}
|
|
10
12
|
declare function sendEnvelope(input: SendEnvelopeInput): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transport.d.mts","names":[],"sources":["../../../src/internal/server/transport.ts"],"mappings":";;;;UAIiB,iBAAA;EAAA,SACN,QAAA,EAAU,QAAA;EAAA,SACV,OAAA,EAAS,oBAAA;EAAA,
|
|
1
|
+
{"version":3,"file":"transport.d.mts","names":[],"sources":["../../../src/internal/server/transport.ts"],"mappings":";;;;UAIiB,iBAAA;EAAA,SACN,QAAA,EAAU,QAAA;EAAA,SACV,OAAA,EAAS,oBAAA;IAAyB,MAAA;EAAA;EAAA,SAClC,WAAA;AAAA;AAAA,iBAGW,YAAA,CAAa,KAAA,EAAO,iBAAA,GAAoB,OAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transport.mjs","names":[],"sources":["../../../src/internal/server/transport.ts"],"sourcesContent":["import type { Envelope } from \"@interfere/types/sdk/envelope\";\n\nimport type { ServerCaptureRuntime } from \"./runtime.js\";\n\nexport interface SendEnvelopeInput {\n readonly envelope: Envelope<\"error\">;\n readonly runtime: ServerCaptureRuntime;\n readonly traceparent?: string;\n}\n\nexport async function sendEnvelope(input: SendEnvelopeInput): Promise<void> {\n const { envelope, runtime, traceparent } = input;\n\n
|
|
1
|
+
{"version":3,"file":"transport.mjs","names":[],"sources":["../../../src/internal/server/transport.ts"],"sourcesContent":["import type { Envelope } from \"@interfere/types/sdk/envelope\";\n\nimport type { ServerCaptureRuntime } from \"./runtime.js\";\n\nexport interface SendEnvelopeInput {\n readonly envelope: Envelope<\"error\">;\n readonly runtime: ServerCaptureRuntime & { apiKey: string };\n readonly traceparent?: string;\n}\n\nexport async function sendEnvelope(input: SendEnvelopeInput): Promise<void> {\n const { envelope, runtime, traceparent } = input;\n\n await fetch(runtime.ingestUrl, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n \"x-api-key\": runtime.apiKey,\n ...(traceparent ? { traceparent } : {}),\n },\n body: JSON.stringify([envelope]),\n keepalive: true,\n });\n}\n"],"mappings":";AAUA,eAAsB,aAAa,OAAyC;CAC1E,MAAM,EAAE,UAAU,SAAS,gBAAgB;AAE3C,OAAM,MAAM,QAAQ,WAAW;EAC7B,QAAQ;EACR,SAAS;GACP,gBAAgB;GAChB,aAAa,QAAQ;GACrB,GAAI,cAAc,EAAE,aAAa,GAAG,EAAE;GACvC;EACD,MAAM,KAAK,UAAU,CAAC,SAAS,CAAC;EAChC,WAAW;EACZ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@interfere/next",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0-alpha.3",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Next.js SDK v2 for Interfere.",
|
|
6
6
|
"keywords": [
|
|
@@ -64,10 +64,10 @@
|
|
|
64
64
|
"test": "bun run test:unit && bun run test:browser"
|
|
65
65
|
},
|
|
66
66
|
"dependencies": {
|
|
67
|
-
"@interfere/constants": "0.
|
|
68
|
-
"@interfere/react": "0.
|
|
69
|
-
"@interfere/sdk": "0.
|
|
70
|
-
"@interfere/types": "0.
|
|
67
|
+
"@interfere/constants": "0.2.0-alpha.1",
|
|
68
|
+
"@interfere/react": "0.2.0-alpha.1",
|
|
69
|
+
"@interfere/sdk": "0.2.0-alpha.1",
|
|
70
|
+
"@interfere/types": "0.2.0-alpha.1",
|
|
71
71
|
"chalk": "^5.6.2",
|
|
72
72
|
"glob": "^13.0.6",
|
|
73
73
|
"uuid": "^13.0.0"
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
"@interfere/typescript-config": "1.1.0-alpha.3",
|
|
83
83
|
"@interfere/vitest-config": "1.1.0-alpha.3",
|
|
84
84
|
"@testing-library/react": "^16.3.2",
|
|
85
|
-
"@types/node": "^24.
|
|
85
|
+
"@types/node": "^24.12.0",
|
|
86
86
|
"@types/react": "19.2.14",
|
|
87
87
|
"@types/react-dom": "19.2.3",
|
|
88
88
|
"@vitejs/plugin-react": "^5.1.4",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"logger.d.mts","names":[],"sources":["../../../src/internal/build/logger.ts"],"mappings":";cAyCa,QAAA;wBACS,KAAA;wBACA,KAAA;yBACC,KAAA;AAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"logger.mjs","names":[],"sources":["../../../src/internal/build/logger.ts"],"sourcesContent":["import chalk from \"chalk\";\n\ntype LogLevel = \"info\" | \"warn\" | \"error\";\n\nconst styles = {\n info: { prefix: `${chalk.whiteBright.bold(\"❖\")}`, text: chalk.cyan.bold, content: chalk.white },\n warn: { prefix: `${chalk.yellow.bold(\"⚠\")} `, text: chalk.yellow.bold, content: chalk.yellowBright },\n error: { prefix: `${chalk.red.bold(\"⨯\")} `, text: chalk.red.bold, content: chalk.redBright },\n} satisfies Record<LogLevel, { prefix: string; text: typeof chalk.bold; content: typeof chalk }>;\n\nconst consoleMethods = {\n info: \"log\",\n warn: \"warn\",\n error: \"error\",\n} satisfies Record<LogLevel, string>;\n\nfunction isTestEnv() {\n return Boolean(process.env.VITEST || process.env.VITEST_WORKER_ID);\n}\n\nfunction emit(level: LogLevel, title: string, lines: string[]) {\n if (isTestEnv()) return;\n\n const style = styles[level];\n const method = consoleMethods[level] as keyof Console;\n const fn = globalThis.console[method];\n if (typeof fn !== \"function\") return;\n\n const invoke = (...args: unknown[]) =>\n Reflect.apply(fn, globalThis.console, args);\n\n invoke(\n `${style.prefix} ${chalk.white(\"Interfere →\")} ${style.text(title)}`\n );\n\n for (const [i, line] of lines.entries()) {\n const connector = i === lines.length - 1 ? \"└\" : \"├\";\n invoke(`${connector} ${style.content(line)}`);\n }\n}\n\nexport const buildLog = {\n info: (title: string, lines: string[]) => emit(\"info\", title, lines),\n warn: (title: string, lines: string[]) => emit(\"warn\", title, lines),\n error: (title: string, lines: string[]) => emit(\"error\", title, lines),\n};\n"],"mappings":";;AAIA,MAAM,SAAS;CACb,MAAM;EAAE,QAAQ,GAAG,MAAM,YAAY,KAAK,IAAI;EAAI,MAAM,MAAM,KAAK;EAAM,SAAS,MAAM;EAAO;CAC/F,MAAM;EAAE,QAAQ,GAAG,MAAM,OAAO,KAAK,IAAI,CAAC;EAAI,MAAM,MAAM,OAAO;EAAM,SAAS,MAAM;EAAc;CACpG,OAAO;EAAE,QAAQ,GAAG,MAAM,IAAI,KAAK,IAAI,CAAC;EAAI,MAAM,MAAM,IAAI;EAAM,SAAS,MAAM;EAAW;CAC7F;AAED,MAAM,iBAAiB;CACrB,MAAM;CACN,MAAM;CACN,OAAO;CACR;AAED,SAAS,YAAY;AACnB,QAAO,QAAQ,QAAQ,IAAI,UAAU,QAAQ,IAAI,iBAAiB;;AAGpE,SAAS,KAAK,OAAiB,OAAe,OAAiB;AAC7D,KAAI,WAAW,CAAE;CAEjB,MAAM,QAAQ,OAAO;CACrB,MAAM,SAAS,eAAe;CAC9B,MAAM,KAAK,WAAW,QAAQ;AAC9B,KAAI,OAAO,OAAO,WAAY;CAE9B,MAAM,UAAU,GAAG,SACjB,QAAQ,MAAM,IAAI,WAAW,SAAS,KAAK;AAE7C,QACE,GAAG,MAAM,OAAO,GAAG,MAAM,MAAM,cAAc,CAAC,GAAG,MAAM,KAAK,MAAM,GACnE;AAED,MAAK,MAAM,CAAC,GAAG,SAAS,MAAM,SAAS,CAErC,QAAO,GADW,MAAM,MAAM,SAAS,IAAI,MAAM,IAC7B,GAAG,MAAM,QAAQ,KAAK,GAAG;;AAIjD,MAAa,WAAW;CACtB,OAAO,OAAe,UAAoB,KAAK,QAAQ,OAAO,MAAM;CACpE,OAAO,OAAe,UAAoB,KAAK,QAAQ,OAAO,MAAM;CACpE,QAAQ,OAAe,UAAoB,KAAK,SAAS,OAAO,MAAM;CACvE"}
|