Compare commits

...

5 Commits

Author SHA1 Message Date
Bhagya Amarasinghe
e1bfb7d8ad chore: add tests and opt-in global-agent proxy support 2025-12-15 18:51:24 +05:30
Bhagya Amarasinghe
adab35d828 fix: externalize global-agent and exclude Node built-ins from webpack 2025-12-15 17:05:21 +05:30
Bhagya Amarasinghe
5ebf406552 fix: guard global-agent to node runtime and declare proxy envs 2025-12-15 16:44:59 +05:30
Bhagya Amarasinghe
f1deb4893e fix: add proxy env vars to turbo.json 2025-12-15 16:23:41 +05:30
Bhagya Amarasinghe
56e79ea467 feat: add opt-in global-agent proxy support for SSO/OIDC behind corporate proxies (#6706) 2025-12-15 16:17:23 +05:30
9 changed files with 396 additions and 1 deletions

View File

@@ -1,10 +1,15 @@
import * as Sentry from "@sentry/nextjs";
import { IS_PRODUCTION, PROMETHEUS_ENABLED, SENTRY_DSN } from "@/lib/constants";
import { setupGlobalAgentProxy } from "@/lib/setupGlobalAgentProxy";
export const onRequestError = Sentry.captureRequestError;
export const register = async () => {
if (process.env.NEXT_RUNTIME === "nodejs") {
// Initialize global-agent proxy support (opt-in via USE_GLOBAL_AGENT_PROXY=1)
// Must run before any outbound HTTP requests to ensure proxy settings are applied
setupGlobalAgentProxy();
if (PROMETHEUS_ENABLED) {
await import("./instrumentation-node");
}

View File

@@ -32,6 +32,9 @@ export const env = createEnv({
GOOGLE_SHEETS_REDIRECT_URL: z.string().optional(),
HTTP_PROXY: z.string().url().optional(),
HTTPS_PROXY: z.string().url().optional(),
GLOBAL_AGENT_NO_PROXY: z.string().optional(),
NO_PROXY: z.string().optional(),
USE_GLOBAL_AGENT_PROXY: z.enum(["1", "0"]).optional(),
IMPRINT_URL: z
.string()
.url()
@@ -159,6 +162,9 @@ export const env = createEnv({
GOOGLE_SHEETS_REDIRECT_URL: process.env.GOOGLE_SHEETS_REDIRECT_URL,
HTTP_PROXY: process.env.HTTP_PROXY,
HTTPS_PROXY: process.env.HTTPS_PROXY,
GLOBAL_AGENT_NO_PROXY: process.env.GLOBAL_AGENT_NO_PROXY,
NO_PROXY: process.env.NO_PROXY,
USE_GLOBAL_AGENT_PROXY: process.env.USE_GLOBAL_AGENT_PROXY,
IMPRINT_URL: process.env.IMPRINT_URL,
IMPRINT_ADDRESS: process.env.IMPRINT_ADDRESS,
INVITE_DISABLED: process.env.INVITE_DISABLED,

View File

@@ -0,0 +1,235 @@
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
import { logger } from "@formbricks/logger";
import { env } from "./env";
// Note: We don't mock global-agent because the code uses dynamic require("global" + "-agent")
// which can't be easily mocked. The real module will run in tests, which is acceptable.
// Import after mocks are set up
import { setupGlobalAgentProxy } from "./setupGlobalAgentProxy";
// Mock logger
vi.mock("@formbricks/logger", () => ({
logger: {
info: vi.fn(),
error: vi.fn(),
},
}));
// Mock env
vi.mock("./env", () => ({
env: {
USE_GLOBAL_AGENT_PROXY: undefined,
GLOBAL_AGENT_NO_PROXY: undefined,
NO_PROXY: undefined,
},
}));
describe("setupGlobalAgentProxy", () => {
const originalWindow = globalThis.window;
const originalProcess = globalThis.process;
const originalGlobalAgentInitialized = globalThis.__FORMBRICKS_GLOBAL_AGENT_INITIALIZED;
beforeEach(() => {
vi.clearAllMocks();
// Reset global state
delete (globalThis as any).window;
delete (globalThis as any).__FORMBRICKS_GLOBAL_AGENT_INITIALIZED;
// Reset process to valid Node.js
globalThis.process = {
release: { name: "node" },
versions: { node: "20.0.0" },
env: {},
} as any;
// Reset env mocks
vi.mocked(env).USE_GLOBAL_AGENT_PROXY = undefined;
vi.mocked(env).GLOBAL_AGENT_NO_PROXY = undefined;
vi.mocked(env).NO_PROXY = undefined;
});
afterEach(() => {
// Restore original values
if (originalWindow !== undefined) {
globalThis.window = originalWindow;
}
if (originalProcess !== undefined) {
globalThis.process = originalProcess;
}
if (originalGlobalAgentInitialized !== undefined) {
globalThis.__FORMBRICKS_GLOBAL_AGENT_INITIALIZED = originalGlobalAgentInitialized;
} else {
delete (globalThis as any).__FORMBRICKS_GLOBAL_AGENT_INITIALIZED;
}
});
describe("browser environment", () => {
test("should return early if window is defined", () => {
globalThis.window = {} as any;
setupGlobalAgentProxy();
expect(logger.info).not.toHaveBeenCalled();
});
});
describe("non-Node environment", () => {
test("should return early if process is undefined", () => {
delete (globalThis as any).process;
setupGlobalAgentProxy();
expect(logger.info).not.toHaveBeenCalled();
});
test("should return early if process.release.name is not 'node'", () => {
globalThis.process = {
release: { name: "deno" },
versions: { node: "20.0.0" },
env: {},
} as any;
setupGlobalAgentProxy();
expect(logger.info).not.toHaveBeenCalled();
});
test("should return early if process.versions.node is undefined", () => {
globalThis.process = {
release: { name: "node" },
versions: {},
env: {},
} as any;
setupGlobalAgentProxy();
expect(logger.info).not.toHaveBeenCalled();
});
});
describe("idempotent initialization", () => {
test("should return early if already initialized", () => {
globalThis.__FORMBRICKS_GLOBAL_AGENT_INITIALIZED = true;
vi.mocked(env).USE_GLOBAL_AGENT_PROXY = "1";
setupGlobalAgentProxy();
expect(logger.info).not.toHaveBeenCalled();
});
});
describe("opt-in flag", () => {
test("should return early if USE_GLOBAL_AGENT_PROXY is not '1'", () => {
vi.mocked(env).USE_GLOBAL_AGENT_PROXY = "0";
setupGlobalAgentProxy();
expect(logger.info).not.toHaveBeenCalled();
});
test("should return early if USE_GLOBAL_AGENT_PROXY is undefined", () => {
vi.mocked(env).USE_GLOBAL_AGENT_PROXY = undefined;
setupGlobalAgentProxy();
expect(logger.info).not.toHaveBeenCalled();
});
});
describe("NO_PROXY resolution", () => {
test("should use GLOBAL_AGENT_NO_PROXY when set", () => {
vi.mocked(env).USE_GLOBAL_AGENT_PROXY = "1";
vi.mocked(env).GLOBAL_AGENT_NO_PROXY = "keycloak.local,adfs.local";
setupGlobalAgentProxy();
expect(process.env.GLOBAL_AGENT_NO_PROXY).toBe("keycloak.local,adfs.local");
// Verify bootstrap was attempted (either success or error logged)
const infoCalled = vi
.mocked(logger.info)
.mock.calls.some(
(call) => call[0] === "Enabled global-agent proxy support for outbound HTTP requests"
);
const errorCalled = vi.mocked(logger.error).mock.calls.length > 0;
expect(infoCalled || errorCalled).toBe(true);
});
test("should use NO_PROXY when GLOBAL_AGENT_NO_PROXY is not set", () => {
vi.mocked(env).USE_GLOBAL_AGENT_PROXY = "1";
vi.mocked(env).NO_PROXY = "auth.service.company.local";
setupGlobalAgentProxy();
expect(process.env.GLOBAL_AGENT_NO_PROXY).toBe("auth.service.company.local");
// Verify bootstrap was attempted
const infoCalled = vi
.mocked(logger.info)
.mock.calls.some(
(call) => call[0] === "Enabled global-agent proxy support for outbound HTTP requests"
);
const errorCalled = vi.mocked(logger.error).mock.calls.length > 0;
expect(infoCalled || errorCalled).toBe(true);
});
test("should prefer GLOBAL_AGENT_NO_PROXY over NO_PROXY", () => {
vi.mocked(env).USE_GLOBAL_AGENT_PROXY = "1";
vi.mocked(env).GLOBAL_AGENT_NO_PROXY = "keycloak.local";
vi.mocked(env).NO_PROXY = "auth.service.company.local";
setupGlobalAgentProxy();
expect(process.env.GLOBAL_AGENT_NO_PROXY).toBe("keycloak.local");
// Verify bootstrap was attempted
const infoCalled = vi.mocked(logger.info).mock.calls.length > 0;
const errorCalled = vi.mocked(logger.error).mock.calls.length > 0;
expect(infoCalled || errorCalled).toBe(true);
});
test("should not set GLOBAL_AGENT_NO_PROXY when neither is set", () => {
vi.mocked(env).USE_GLOBAL_AGENT_PROXY = "1";
// eslint-disable-next-line turbo/no-undeclared-env-vars
const originalNoProxy = process.env.GLOBAL_AGENT_NO_PROXY;
// eslint-disable-next-line turbo/no-undeclared-env-vars
delete process.env.GLOBAL_AGENT_NO_PROXY;
setupGlobalAgentProxy();
// eslint-disable-next-line turbo/no-undeclared-env-vars
expect(process.env.GLOBAL_AGENT_NO_PROXY).toBeUndefined();
// Verify bootstrap was attempted
const infoCalled = vi
.mocked(logger.info)
.mock.calls.some(
(call) => call[0] === "Enabled global-agent proxy support for outbound HTTP requests"
);
const errorCalled = vi.mocked(logger.error).mock.calls.length > 0;
expect(infoCalled || errorCalled).toBe(true);
// Restore
if (originalNoProxy !== undefined) {
// eslint-disable-next-line turbo/no-undeclared-env-vars
process.env.GLOBAL_AGENT_NO_PROXY = originalNoProxy;
}
});
});
describe("successful initialization", () => {
test("should attempt to bootstrap global-agent when enabled", () => {
vi.mocked(env).USE_GLOBAL_AGENT_PROXY = "1";
setupGlobalAgentProxy();
// Verify bootstrap was attempted (either success or error)
const infoCalled = vi
.mocked(logger.info)
.mock.calls.some(
(call) => call[0] === "Enabled global-agent proxy support for outbound HTTP requests"
);
const errorCalled = vi.mocked(logger.error).mock.calls.length > 0;
expect(infoCalled || errorCalled).toBe(true);
// If successful, flag should be set
if (infoCalled) {
expect(globalThis.__FORMBRICKS_GLOBAL_AGENT_INITIALIZED).toBe(true);
}
});
});
});

View File

@@ -0,0 +1,56 @@
import "server-only";
import { logger } from "@formbricks/logger";
import { env } from "./env";
declare global {
// eslint-disable-next-line no-var
var __FORMBRICKS_GLOBAL_AGENT_INITIALIZED: boolean | undefined;
}
export const setupGlobalAgentProxy = (): void => {
// Only run in a Node.js runtime; skip edge/serverless where Node built-ins (net/tls) are missing
if (globalThis.window !== undefined) {
return;
}
// Hard guard: only run in real Node.js runtime (not edge/serverless)
if (
globalThis.process === undefined ||
globalThis.process.release?.name !== "node" ||
globalThis.process.versions?.node === undefined
) {
return;
}
if (globalThis.__FORMBRICKS_GLOBAL_AGENT_INITIALIZED) {
return;
}
const isEnabled = env.USE_GLOBAL_AGENT_PROXY === "1";
if (!isEnabled) {
return;
}
// Resolve NO_PROXY value from validated env
const noProxy = env.GLOBAL_AGENT_NO_PROXY ?? env.NO_PROXY;
// Set GLOBAL_AGENT_NO_PROXY in process.env for global-agent to read
if (noProxy) {
// eslint-disable-next-line turbo/no-undeclared-env-vars
process.env.GLOBAL_AGENT_NO_PROXY = noProxy;
}
// Mark as initialized before attempting bootstrap to avoid repeated attempts
globalThis.__FORMBRICKS_GLOBAL_AGENT_INITIALIZED = true;
try {
// Dynamic require prevents bundling into edge/serverless builds
// Using string concatenation to prevent webpack from statically analyzing the require
// eslint-disable-next-line @typescript-eslint/no-var-requires, turbo/no-undeclared-env-vars
const { bootstrap } = require("global" + "-agent");
bootstrap();
logger.info("Enabled global-agent proxy support for outbound HTTP requests");
} catch (error) {
logger.error(error, "Failed to enable global-agent proxy support");
}
};

View File

@@ -19,7 +19,7 @@ const nextConfig = {
output: "standalone",
poweredByHeader: false,
productionBrowserSourceMaps: true,
serverExternalPackages: ["@aws-sdk", "@opentelemetry/instrumentation", "pino", "pino-pretty"],
serverExternalPackages: ["@aws-sdk", "@opentelemetry/instrumentation", "pino", "pino-pretty", "global-agent"],
outputFileTracingIncludes: {
"/api/auth/**/*": ["../../node_modules/jose/**/*"],
},
@@ -113,6 +113,9 @@ const nextConfig = {
config.resolve.fallback = {
http: false, // Prevents Next.js from trying to bundle 'http'
https: false,
net: false, // Prevents Next.js from trying to bundle 'net' (used by global-agent)
tls: false, // Prevents Next.js from trying to bundle 'tls' (used by global-agent)
domain: false, // Prevents Next.js from trying to bundle 'domain' (used by global-agent dependencies)
};
return config;
},

View File

@@ -90,6 +90,7 @@
"file-loader": "6.2.0",
"framer-motion": "12.10.0",
"googleapis": "148.0.0",
"global-agent": "3.0.0",
"heic-convert": "2.1.0",
"https-proxy-agent": "7.0.6",
"i18next": "25.5.2",

View File

@@ -41,3 +41,12 @@ OIDC_SIGNING_ALGORITHM=HS256
- Restart your Formbricks instance.
- You're all set! Users can now sign up & log in using their OIDC credentials.
## Use behind a proxy
If outbound traffic must pass through a corporate proxy and your IdP should bypass it:
- Set `USE_GLOBAL_AGENT_PROXY=1` to enable proxy handling for all Auth.js HTTP requests.
- Set `HTTP_PROXY` / `HTTPS_PROXY` to your proxy endpoints.
- Set `NO_PROXY` (or `GLOBAL_AGENT_NO_PROXY`) to include your IdP host (for example, `auth.service.company.local,keycloak`).
- Restart the app to apply the settings.

77
pnpm-lock.yaml generated
View File

@@ -321,6 +321,9 @@ importers:
framer-motion:
specifier: 12.10.0
version: 12.10.0(react-dom@19.1.2(react@19.1.2))(react@19.1.2)
global-agent:
specifier: 3.0.0
version: 3.0.0
googleapis:
specifier: 148.0.0
version: 148.0.0(encoding@0.1.13)
@@ -5391,6 +5394,10 @@ packages:
boolbase@1.0.0:
resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
boolean@3.2.0:
resolution: {integrity: sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==}
deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.
boring-avatars@2.0.1:
resolution: {integrity: sha512-TeBnZrp7WxHcQPuLhGQamklgNqaL7eUAUh3E11kFj9rTn0Hari2ZKVTchqNrp62UOHN/XOe5bZGcbzVGwHjHwg==}
peerDependencies:
@@ -5909,6 +5916,9 @@ packages:
detect-node-es@1.1.0:
resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==}
detect-node@2.1.0:
resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==}
didyoumean@1.2.2:
resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
@@ -6090,6 +6100,9 @@ packages:
resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==}
engines: {node: '>= 0.4'}
es6-error@4.1.1:
resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==}
esbuild-register@3.6.0:
resolution: {integrity: sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==}
peerDependencies:
@@ -6672,6 +6685,10 @@ packages:
resolution: {integrity: sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==}
engines: {node: '>=16 || 14 >=14.17'}
global-agent@3.0.0:
resolution: {integrity: sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==}
engines: {node: '>=10.0'}
globals@13.24.0:
resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==}
engines: {node: '>=8'}
@@ -7244,6 +7261,9 @@ packages:
json-stable-stringify-without-jsonify@1.0.1:
resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
json-stringify-safe@5.0.1:
resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==}
json5@1.0.2:
resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==}
hasBin: true
@@ -7473,6 +7493,10 @@ packages:
engines: {node: '>= 16'}
hasBin: true
matcher@3.0.0:
resolution: {integrity: sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==}
engines: {node: '>=10'}
math-intrinsics@1.1.0:
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
engines: {node: '>= 0.4'}
@@ -8755,6 +8779,10 @@ packages:
ripemd160@2.0.2:
resolution: {integrity: sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==}
roarr@2.15.4:
resolution: {integrity: sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==}
engines: {node: '>=8.0'}
rollup@4.52.5:
resolution: {integrity: sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==}
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
@@ -8833,6 +8861,9 @@ packages:
selderee@0.11.0:
resolution: {integrity: sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==}
semver-compare@1.0.0:
resolution: {integrity: sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==}
semver@5.7.2:
resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==}
hasBin: true
@@ -8854,6 +8885,10 @@ packages:
seq-queue@0.0.5:
resolution: {integrity: sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==}
serialize-error@7.0.1:
resolution: {integrity: sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==}
engines: {node: '>=10'}
serialize-javascript@6.0.2:
resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==}
@@ -9498,6 +9533,10 @@ packages:
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
engines: {node: '>= 0.8.0'}
type-fest@0.13.1:
resolution: {integrity: sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==}
engines: {node: '>=10'}
type-fest@0.20.2:
resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==}
engines: {node: '>=10'}
@@ -15930,6 +15969,8 @@ snapshots:
boolbase@1.0.0: {}
boolean@3.2.0: {}
boring-avatars@2.0.1(react-dom@19.1.2(react@19.1.2))(react@19.1.2):
dependencies:
react: 19.1.2
@@ -16435,6 +16476,8 @@ snapshots:
detect-node-es@1.1.0: {}
detect-node@2.1.0: {}
didyoumean@1.2.2: {}
diff@3.5.0: {}
@@ -16677,6 +16720,8 @@ snapshots:
is-date-object: 1.1.0
is-symbol: 1.1.1
es6-error@4.1.1: {}
esbuild-register@3.6.0(esbuild@0.25.10):
dependencies:
debug: 4.4.3
@@ -17450,6 +17495,15 @@ snapshots:
minipass: 4.2.8
path-scurry: 1.11.1
global-agent@3.0.0:
dependencies:
boolean: 3.2.0
es6-error: 4.1.1
matcher: 3.0.0
roarr: 2.15.4
semver: 7.7.3
serialize-error: 7.0.1
globals@13.24.0:
dependencies:
type-fest: 0.20.2
@@ -18052,6 +18106,8 @@ snapshots:
json-stable-stringify-without-jsonify@1.0.1: {}
json-stringify-safe@5.0.1: {}
json5@1.0.2:
dependencies:
minimist: 1.2.8
@@ -18320,6 +18376,10 @@ snapshots:
marked@7.0.4: {}
matcher@3.0.0:
dependencies:
escape-string-regexp: 4.0.0
math-intrinsics@1.1.0: {}
md-to-react-email@5.0.5(react@19.1.2):
@@ -19594,6 +19654,15 @@ snapshots:
hash-base: 3.1.2
inherits: 2.0.4
roarr@2.15.4:
dependencies:
boolean: 3.2.0
detect-node: 2.1.0
globalthis: 1.0.4
json-stringify-safe: 5.0.1
semver-compare: 1.0.0
sprintf-js: 1.1.3
rollup@4.52.5:
dependencies:
'@types/estree': 1.0.8
@@ -19708,6 +19777,8 @@ snapshots:
dependencies:
parseley: 0.12.1
semver-compare@1.0.0: {}
semver@5.7.2: {}
semver@6.3.1: {}
@@ -19720,6 +19791,10 @@ snapshots:
seq-queue@0.0.5: {}
serialize-error@7.0.1:
dependencies:
type-fest: 0.13.1
serialize-javascript@6.0.2:
dependencies:
randombytes: 2.1.0
@@ -20468,6 +20543,8 @@ snapshots:
dependencies:
prelude-ls: 1.2.1
type-fest@0.13.1: {}
type-fest@0.20.2: {}
type-fest@0.6.0: {}

View File

@@ -140,9 +140,12 @@
"NOTION_OAUTH_CLIENT_ID",
"NOTION_OAUTH_CLIENT_SECRET",
"HEROKU_APP_NAME",
"GLOBAL_AGENT_NO_PROXY",
"HTTP_PROXY",
"HTTPS_PROXY",
"IMPRINT_URL",
"NO_PROXY",
"USE_GLOBAL_AGENT_PROXY",
"IMPRINT_ADDRESS",
"INVITE_DISABLED",
"IS_FORMBRICKS_CLOUD",