diff --git a/apps/api/package.json b/apps/api/package.json index 650d51b0..2215e391 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -33,6 +33,7 @@ "hono": "4.11.7", "hono-openapi": "^1.1.2", "hono-rate-limiter": "0.5.3", + "std-env": "3.7.0", "superjson": "2.2.6" }, "devDependencies": { diff --git a/apps/api/src/shared/env.ts b/apps/api/src/shared/env.ts index 727cf431..6d58c270 100644 --- a/apps/api/src/shared/env.ts +++ b/apps/api/src/shared/env.ts @@ -1,11 +1,10 @@ import { createEnv } from "@init/env" -import { auth, db, inngest, kv, node, sentry } from "@init/env/presets" -import { isCI } from "@init/utils/environment" +import { auth, db, inngest, kv, sentry } from "@init/env/presets" import * as z from "@init/utils/schema" +import { isCI } from "std-env" export default createEnv({ extends: [ - node(), // Packages auth(), auth.providers.github(), @@ -28,5 +27,5 @@ export default createEnv({ BASE_URL: z.url(), PORT: z.coerce.number().default(3000), }, - skipValidation: isCI(), + skipValidation: isCI, }) diff --git a/apps/app/package.json b/apps/app/package.json index 1a57b013..8e02d3e8 100644 --- a/apps/app/package.json +++ b/apps/app/package.json @@ -35,6 +35,7 @@ "api": "workspace:*", "react": "19.1.0", "react-dom": "19.1.0", + "std-env": "3.7.0", "superjson": "2.2.6" }, "devDependencies": { diff --git a/apps/app/src/client.tsx b/apps/app/src/client.tsx index 13d9cb6b..2dd96644 100644 --- a/apps/app/src/client.tsx +++ b/apps/app/src/client.tsx @@ -3,7 +3,7 @@ import { StartClient } from "@tanstack/react-start/client" import { StrictMode } from "react" import { hydrateRoot } from "react-dom/client" -configureLogger() +configureLogger({ isDevelopment: import.meta.env.DEV }) hydrateRoot( document, diff --git a/apps/app/src/shared/env.ts b/apps/app/src/shared/env.ts index ef514c32..34f2df28 100644 --- a/apps/app/src/shared/env.ts +++ b/apps/app/src/shared/env.ts @@ -1,8 +1,8 @@ import { createEnv } from "@init/env" -import { auth, db, node } from "@init/env/presets" +import { auth, db } from "@init/env/presets" import { REACT_PUBLIC_ENV_PREFIX } from "@init/utils/constants" -import { isCI } from "@init/utils/environment" import * as z from "@init/utils/schema" +import { isCI } from "std-env" export default createEnv({ client: { @@ -10,10 +10,10 @@ export default createEnv({ PUBLIC_BASE_URL: z.url(), }, clientPrefix: REACT_PUBLIC_ENV_PREFIX, - extends: [node(), auth(), auth.providers.github(), auth.providers.google(), db()], + extends: [auth(), auth.providers.github(), auth.providers.google(), db()], // Load server environment variables (process.env) and client environment // variables (import.meta.env) runtimeEnv: { ...import.meta.env, ...process.env }, server: {}, - skipValidation: isCI(), + skipValidation: isCI, }) diff --git a/apps/app/src/shared/trpc.tsx b/apps/app/src/shared/trpc.tsx index 5478efae..d43d2f2d 100644 --- a/apps/app/src/shared/trpc.tsx +++ b/apps/app/src/shared/trpc.tsx @@ -1,6 +1,5 @@ import type { TRPCRouter } from "api/client" import type { ReactNode } from "react" -import { isDevelopment } from "@init/utils/environment" import { useQueryClient } from "@tanstack/react-query" import { createIsomorphicFn } from "@tanstack/react-start" import { getRequestHeaders } from "@tanstack/react-start/server" @@ -12,6 +11,7 @@ import { buildApiUrl } from "#shared/utils.ts" const { useTRPC, useTRPCClient, TRPCProvider: TRPCProviderBase } = createTRPCContext() const url = buildApiUrl("/trpc") +const isDevelopment = import.meta.env.DEV export const makeTRPCClient = createIsomorphicFn() .server(() => @@ -28,7 +28,7 @@ export const makeTRPCClient = createIsomorphicFn() .client(() => createTRPCClient({ links: [ - loggerLink({ colorMode: "ansi", enabled: () => isDevelopment() }), + loggerLink({ colorMode: "ansi", enabled: () => isDevelopment }), httpBatchStreamLink({ fetch: (requestUrl, options) => fetch(requestUrl, { diff --git a/apps/app/src/shared/utils.ts b/apps/app/src/shared/utils.ts index 9bd43336..afce84fd 100644 --- a/apps/app/src/shared/utils.ts +++ b/apps/app/src/shared/utils.ts @@ -1,13 +1,14 @@ -import { isProduction } from "@init/utils/environment" import { createUrlBuilder } from "@init/utils/url" import env from "#shared/env.ts" -export const buildUrl = createUrlBuilder(env.PUBLIC_BASE_URL, isProduction() ? "https" : "http") +const isProduction = import.meta.env.PROD + +export const buildUrl = createUrlBuilder(env.PUBLIC_BASE_URL, isProduction ? "https" : "http") export const baseUrl = buildUrl("/") // If you are using a separate API, you can use this function to build the API URLs. export const buildApiUrl = createUrlBuilder( env.PUBLIC_API_URL ?? `${env.PUBLIC_BASE_URL}/api`, - isProduction() ? "https" : "http" + isProduction ? "https" : "http" ) diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 9dc8014d..4e17e6a9 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -26,7 +26,8 @@ "@tauri-apps/api": "2.9.1", "@tauri-apps/plugin-opener": "2.5.3", "react": "19.1.0", - "react-dom": "19.1.0" + "react-dom": "19.1.0", + "std-env": "3.7.0" }, "devDependencies": { "@inlang/paraglide-js": "2.8.0", diff --git a/apps/desktop/src/main.tsx b/apps/desktop/src/main.tsx index 0b8255f0..d8263943 100644 --- a/apps/desktop/src/main.tsx +++ b/apps/desktop/src/main.tsx @@ -10,7 +10,7 @@ import { queryClient } from "#shared/query-client.ts" import "@init/ui/globals.css" -configureLogger() +configureLogger({ isDevelopment: import.meta.env.DEV }) export type RouterContext = { queryClient: QueryClient diff --git a/apps/desktop/src/shared/env.ts b/apps/desktop/src/shared/env.ts index 08dbfc9f..3818032d 100644 --- a/apps/desktop/src/shared/env.ts +++ b/apps/desktop/src/shared/env.ts @@ -1,7 +1,7 @@ import { createEnv } from "@init/env" import { tauri } from "@init/env/presets" -import { isCI } from "@init/utils/environment" import * as z from "@init/utils/schema" +import { isCI } from "std-env" export default createEnv({ client: { @@ -10,5 +10,5 @@ export default createEnv({ clientPrefix: "PUBLIC_", extends: [tauri()], runtimeEnv: import.meta.env, - skipValidation: isCI(), + skipValidation: isCI, }) diff --git a/apps/desktop/src/shared/utils.ts b/apps/desktop/src/shared/utils.ts index b419ca88..eb442968 100644 --- a/apps/desktop/src/shared/utils.ts +++ b/apps/desktop/src/shared/utils.ts @@ -1,5 +1,6 @@ -import { isProduction } from "@init/utils/environment" import { createUrlBuilder } from "@init/utils/url" import env from "#shared/env.ts" -export const buildApiUrl = createUrlBuilder(env.PUBLIC_API_URL, isProduction() ? "https" : "http") +const isProduction = import.meta.env.PROD + +export const buildApiUrl = createUrlBuilder(env.PUBLIC_API_URL, isProduction ? "https" : "http") diff --git a/apps/extension/package.json b/apps/extension/package.json index 02a69fae..5e369bc1 100644 --- a/apps/extension/package.json +++ b/apps/extension/package.json @@ -31,6 +31,7 @@ "@webext-core/proxy-service": "2.0.0", "react": "19.1.0", "react-dom": "19.1.0", + "std-env": "3.7.0", "wouter": "^3.7.1" }, "devDependencies": { diff --git a/apps/extension/src/entrypoints/background.ts b/apps/extension/src/entrypoints/background.ts index f419ece4..5b3e4479 100644 --- a/apps/extension/src/entrypoints/background.ts +++ b/apps/extension/src/entrypoints/background.ts @@ -3,7 +3,7 @@ import { browser } from "wxt/browser" import { defineBackground } from "wxt/utils/define-background" import { logger } from "#shared/logger.ts" -configureLogger() +configureLogger({ isDevelopment: import.meta.env.DEV }) export default defineBackground({ main: () => { diff --git a/apps/extension/src/shared/env.ts b/apps/extension/src/shared/env.ts index 77bc5ac9..ccc96f6c 100644 --- a/apps/extension/src/shared/env.ts +++ b/apps/extension/src/shared/env.ts @@ -1,6 +1,6 @@ import { createEnv } from "@init/env" -import { isCI } from "@init/utils/environment" import * as z from "@init/utils/schema" +import { isCI } from "std-env" export default createEnv({ client: { @@ -9,5 +9,5 @@ export default createEnv({ clientPrefix: "VITE_", extends: [], runtimeEnv: process.env, - skipValidation: isCI(), + skipValidation: isCI, }) diff --git a/apps/mobile/package.json b/apps/mobile/package.json index fca2e79a..114b4b8b 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -70,6 +70,7 @@ "react-native-unistyles": "3.0.13", "react-native-web": "~0.21.0", "react-native-worklets": "0.5.1", + "std-env": "3.7.0", "superjson": "2.2.6" }, "devDependencies": { diff --git a/apps/mobile/src/shared/env.ts b/apps/mobile/src/shared/env.ts index d0f188c2..dc88efbc 100644 --- a/apps/mobile/src/shared/env.ts +++ b/apps/mobile/src/shared/env.ts @@ -1,7 +1,7 @@ import { createEnv } from "@init/env" import { sentry } from "@init/env/presets" -import { isCI } from "@init/utils/environment" import * as z from "@init/utils/schema" +import { isCI } from "std-env" export default createEnv({ client: { @@ -10,5 +10,5 @@ export default createEnv({ clientPrefix: "EXPO_PUBLIC_", extends: [sentry.expo()], runtimeEnv: process.env, - skipValidation: isCI(), + skipValidation: isCI, }) diff --git a/apps/mobile/src/shared/utils.ts b/apps/mobile/src/shared/utils.ts index 10eb4ebe..4fc02dc3 100644 --- a/apps/mobile/src/shared/utils.ts +++ b/apps/mobile/src/shared/utils.ts @@ -1,11 +1,11 @@ -import { isProduction } from "@init/utils/environment" import { createUrlBuilder } from "@init/utils/url" import { Appearance, Platform } from "react-native" +import { isProduction } from "std-env" import env from "#shared/env.ts" export const buildApiUrl = createUrlBuilder( env.EXPO_PUBLIC_API_URL, - isProduction() ? "https" : "http" + isProduction ? "https" : "http" ) export const isDarkMode = Appearance.getColorScheme() === "dark" diff --git a/apps/web/package.json b/apps/web/package.json index 878bcc98..4bf43abf 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -24,7 +24,8 @@ "@init/utils": "workspace:*", "astro": "5.16.8", "react": "19.1.0", - "react-dom": "19.1.0" + "react-dom": "19.1.0", + "std-env": "3.7.0" }, "devDependencies": { "@astrojs/check": "0.9.6", diff --git a/apps/web/src/shared/env.ts b/apps/web/src/shared/env.ts index 9a4708f0..30d096d9 100644 --- a/apps/web/src/shared/env.ts +++ b/apps/web/src/shared/env.ts @@ -1,6 +1,6 @@ import { createEnv } from "@init/env" -import { isCI } from "@init/utils/environment" import * as z from "@init/utils/schema" +import { isCI } from "std-env" export default createEnv({ client: { @@ -11,5 +11,5 @@ export default createEnv({ server: { TEST_VAR: z.string(), }, - skipValidation: isCI(), + skipValidation: isCI, }) diff --git a/bun.lock b/bun.lock index b2b2744c..bc002ae0 100644 --- a/bun.lock +++ b/bun.lock @@ -46,6 +46,7 @@ "hono": "4.11.7", "hono-openapi": "^1.1.2", "hono-rate-limiter": "0.5.3", + "std-env": "3.7.0", "superjson": "2.2.6", }, "devDependencies": { @@ -76,6 +77,7 @@ "api": "workspace:*", "react": "19.1.0", "react-dom": "19.1.0", + "std-env": "3.7.0", "superjson": "2.2.6", }, "devDependencies": { @@ -109,6 +111,7 @@ "@tauri-apps/plugin-opener": "2.5.3", "react": "19.1.0", "react-dom": "19.1.0", + "std-env": "3.7.0", }, "devDependencies": { "@inlang/paraglide-js": "2.8.0", @@ -160,6 +163,7 @@ "@webext-core/proxy-service": "2.0.0", "react": "19.1.0", "react-dom": "19.1.0", + "std-env": "3.7.0", "wouter": "^3.7.1", }, "devDependencies": { @@ -224,6 +228,7 @@ "react-native-unistyles": "3.0.13", "react-native-web": "~0.21.0", "react-native-worklets": "0.5.1", + "std-env": "3.7.0", "superjson": "2.2.6", }, "devDependencies": { @@ -248,6 +253,7 @@ "astro": "5.16.8", "react": "19.1.0", "react-dom": "19.1.0", + "std-env": "3.7.0", }, "devDependencies": { "@astrojs/check": "0.9.6", @@ -312,6 +318,7 @@ "@tanstack/react-query": "^5.90.20", "convex": "1.31.6", "convex-helpers": "0.1.111", + "std-env": "3.7.0", }, "devDependencies": { "@tooling/tsconfig": "workspace:*", @@ -371,6 +378,7 @@ "dependencies": { "@init/utils": "workspace:*", "@t3-oss/env-core": "0.13.10", + "std-env": "3.7.0", }, "devDependencies": { "@tooling/tsconfig": "workspace:*", @@ -416,6 +424,7 @@ "@sentry/core": "10.32.1", "@sentry/node": "10.32.1", "@sentry/react-native": "~7.2.0", + "std-env": "3.7.0", }, "devDependencies": { "@tooling/tsconfig": "workspace:*", @@ -489,6 +498,7 @@ "@init/error": "workspace:*", "clsx": "2.1.1", "nanoid": "5.1.6", + "std-env": "3.7.0", "superjson": "2.2.6", "tailwind-merge": "3.4.0", "ufo": "1.5.4", @@ -4486,6 +4496,8 @@ "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], + "std-env": ["std-env@3.7.0", "", {}, "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg=="], + "stdin-discarder": ["stdin-discarder@0.2.2", "", {}, "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ=="], "stream-buffers": ["stream-buffers@2.2.0", "", {}, "sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg=="], diff --git a/packages/backend/package.json b/packages/backend/package.json index b45707d8..fdf51931 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -31,7 +31,8 @@ "@init/utils": "workspace:*", "@tanstack/react-query": "^5.90.20", "convex": "1.31.6", - "convex-helpers": "0.1.111" + "convex-helpers": "0.1.111", + "std-env": "3.7.0" }, "devDependencies": { "@tooling/tsconfig": "workspace:*", diff --git a/packages/backend/src/functions/shared/env.ts b/packages/backend/src/functions/shared/env.ts index a6c16ad7..31d05e80 100644 --- a/packages/backend/src/functions/shared/env.ts +++ b/packages/backend/src/functions/shared/env.ts @@ -1,7 +1,7 @@ import { createEnv } from "@init/env" import { auth } from "@init/env/presets" -import { isCI } from "@init/utils/environment" import * as z from "@init/utils/schema" +import { isCI } from "std-env" export default createEnv({ extends: [auth()], @@ -9,5 +9,5 @@ export default createEnv({ server: { CONVEX_SITE_URL: z.string(), }, - skipValidation: isCI(), + skipValidation: isCI, }) diff --git a/packages/env/package.json b/packages/env/package.json index 3d7db2cb..1be363b1 100644 --- a/packages/env/package.json +++ b/packages/env/package.json @@ -14,7 +14,8 @@ }, "dependencies": { "@init/utils": "workspace:*", - "@t3-oss/env-core": "0.13.10" + "@t3-oss/env-core": "0.13.10", + "std-env": "3.7.0" }, "devDependencies": { "@tooling/tsconfig": "workspace:*", diff --git a/packages/env/src/presets.ts b/packages/env/src/presets.ts index 414c4a64..21c3a77d 100644 --- a/packages/env/src/presets.ts +++ b/packages/env/src/presets.ts @@ -1,16 +1,7 @@ import { EXPO_PUBLIC_ENV_PREFIX, REACT_PUBLIC_ENV_PREFIX } from "@init/utils/constants" -import { isCI } from "@init/utils/environment" import * as z from "@init/utils/schema" import { createEnv } from "@t3-oss/env-core" - -export const node = () => - createEnv({ - runtimeEnv: process.env, - server: { - NODE_ENV: z.env(), - }, - skipValidation: isCI(), - }) +import { isCI } from "std-env" // Presets for system environment variables from popular services (Vercel, Neon, // Supabase, Render, etc.) @@ -27,7 +18,7 @@ export const auth = () => server: { AUTH_SECRET: z.string(), }, - skipValidation: isCI(), + skipValidation: isCI, }) auth.providers = { @@ -41,7 +32,7 @@ auth.providers = { GITHUB_CLIENT_ID: z.string(), GITHUB_CLIENT_SECRET: z.string(), }, - skipValidation: isCI(), + skipValidation: isCI, }), /** * Sign in with Google @@ -53,7 +44,7 @@ auth.providers = { GOOGLE_CLIENT_ID: z.string(), GOOGLE_CLIENT_SECRET: z.string(), }, - skipValidation: isCI(), + skipValidation: isCI, }), } @@ -66,7 +57,7 @@ export const convex = { }, clientPrefix: EXPO_PUBLIC_ENV_PREFIX, runtimeEnv: process.env, - skipValidation: isCI(), + skipValidation: isCI, }), react: () => createEnv({ @@ -76,7 +67,7 @@ export const convex = { }, clientPrefix: REACT_PUBLIC_ENV_PREFIX, runtimeEnv: import.meta.env, - skipValidation: isCI(), + skipValidation: isCI, }), } @@ -87,7 +78,7 @@ export const db = () => DATABASE_URL: z.url(), RUN_PRODUCTION_MIGRATIONS: z.stringbool().default(false), }, - skipValidation: isCI(), + skipValidation: isCI, }) export const inngest = () => @@ -98,7 +89,7 @@ export const inngest = () => INNGEST_SIGNING_KEY: z.string(), INNGEST_SIGNING_KEY_FALLBACK: z.string().optional(), }, - skipValidation: isCI(), + skipValidation: isCI, }) export const kv = () => @@ -107,7 +98,7 @@ export const kv = () => server: { REDIS_URL: z.url(), }, - skipValidation: isCI(), + skipValidation: isCI, }) export const s3 = () => @@ -120,7 +111,7 @@ export const s3 = () => S3_REGION: z.string().optional(), S3_SECRET_ACCESS_KEY: z.string(), }, - skipValidation: isCI(), + skipValidation: isCI, }) export const resend = () => @@ -131,7 +122,7 @@ export const resend = () => MOCK_RESEND: z.stringbool().default(false), RESEND_API_KEY: z.string(), }, - skipValidation: isCI(), + skipValidation: isCI, }) export const sentry = { @@ -143,7 +134,7 @@ export const sentry = { }, clientPrefix: REACT_PUBLIC_ENV_PREFIX, runtimeEnv: import.meta.env, - skipValidation: isCI(), + skipValidation: isCI, }), expo: () => createEnv({ @@ -177,7 +168,7 @@ export const sentry = { SENTRY_PROJECT: z.string(), SENTRY_SPOTLIGHT: z.stringbool().default(false), }, - skipValidation: isCI(), + skipValidation: isCI, }), } @@ -187,7 +178,7 @@ export const openai = () => server: { OPENAI_API_KEY: z.string(), }, - skipValidation: isCI(), + skipValidation: isCI, }) export const anthropic = () => @@ -196,7 +187,7 @@ export const anthropic = () => server: { ANTHROPIC_API_KEY: z.string(), }, - skipValidation: isCI(), + skipValidation: isCI, }) export const stripe = () => @@ -206,7 +197,7 @@ export const stripe = () => STRIPE_SECRET_KEY: z.string(), STRIPE_WEBHOOK_SECRET: z.string(), }, - skipValidation: isCI(), + skipValidation: isCI, }) export const posthog = { @@ -218,7 +209,7 @@ export const posthog = { }, clientPrefix: EXPO_PUBLIC_ENV_PREFIX, runtimeEnv: process.env, - skipValidation: isCI(), + skipValidation: isCI, }), react: () => createEnv({ @@ -228,7 +219,7 @@ export const posthog = { }, clientPrefix: REACT_PUBLIC_ENV_PREFIX, runtimeEnv: import.meta.env, - skipValidation: isCI(), + skipValidation: isCI, }), server: () => createEnv({ @@ -237,7 +228,7 @@ export const posthog = { POSTHOG_API_KEY: z.string(), POSTHOG_HOST: z.url(), }, - skipValidation: isCI(), + skipValidation: isCI, }), } @@ -253,5 +244,5 @@ export const tauri = () => }, clientPrefix: "TAURI_ENV_", runtimeEnv: import.meta.env, - skipValidation: isCI(), + skipValidation: isCI, }) diff --git a/packages/observability/package.json b/packages/observability/package.json index 08ff8bd9..bff04dd3 100644 --- a/packages/observability/package.json +++ b/packages/observability/package.json @@ -27,7 +27,8 @@ "@sentry/browser": "10.32.1", "@sentry/core": "10.32.1", "@sentry/node": "10.32.1", - "@sentry/react-native": "~7.2.0" + "@sentry/react-native": "~7.2.0", + "std-env": "3.7.0" }, "devDependencies": { "@tooling/tsconfig": "workspace:*", diff --git a/packages/observability/src/logger/index.ts b/packages/observability/src/logger/index.ts index c57e1178..c42e2594 100644 --- a/packages/observability/src/logger/index.ts +++ b/packages/observability/src/logger/index.ts @@ -1,4 +1,3 @@ -import { isDevelopment } from "@init/utils/environment" import { type Config, type Logger as LogtapeLogger, @@ -9,6 +8,7 @@ import { jsonLinesFormatter, } from "@logtape/logtape" import { getPrettyFormatter } from "@logtape/pretty" +import { isDevelopment } from "std-env" import { redactSink } from "./utils" export const LoggerCategory = { @@ -23,9 +23,14 @@ export const LoggerCategory = { type LoggerCategoryType = (typeof LoggerCategory)[keyof typeof LoggerCategory] -function buildConfig(nonBlocking: boolean): Config { +type LoggerConfigOptions = { + isDevelopment?: boolean +} + +function buildConfig(nonBlocking: boolean, options?: LoggerConfigOptions): Config { + const isDev = options?.isDevelopment ?? isDevelopment const consoleSink = getConsoleSink({ - formatter: isDevelopment() + formatter: isDev ? getPrettyFormatter({ categoryTruncate: "middle", categoryWidth: 15, @@ -83,12 +88,12 @@ function buildConfig(nonBlocking: boolean): Config { } } -export function configureLogger() { - configureSync(buildConfig(false)) +export function configureLogger(options?: LoggerConfigOptions) { + configureSync(buildConfig(false, options)) } -export async function configureLoggerAsync() { - await configure(buildConfig(true)) +export async function configureLoggerAsync(options?: LoggerConfigOptions) { + await configure(buildConfig(true, options)) } export function getLogger(category: LoggerCategoryType = LoggerCategory.DEFAULT) { diff --git a/packages/observability/src/monitoring/client.ts b/packages/observability/src/monitoring/client.ts index e2befbe1..1a11a4dd 100644 --- a/packages/observability/src/monitoring/client.ts +++ b/packages/observability/src/monitoring/client.ts @@ -1,17 +1,15 @@ import { sentry } from "@init/env/presets" -import { isProduction } from "@init/utils/environment" import * as Sentry from "@sentry/browser" +import { isProduction } from "std-env" export function initializeErrorMonitoring() { - const monitoringSampleRate = isProduction() ? 0.1 : 1 + const monitoringSampleRate = isProduction ? 0.1 : 1 const env = sentry.client() Sentry.init({ debug: env.PUBLIC_SENTRY_DEBUG, dsn: env.PUBLIC_SENTRY_DSN, - enableLogs: true, - integrations: [ Sentry.browserTracingIntegration(), Sentry.replayIntegration({ @@ -19,13 +17,9 @@ export function initializeErrorMonitoring() { maskAllText: true, }), ], - replaysOnErrorSampleRate: 1, - replaysSessionSampleRate: monitoringSampleRate, - sendDefaultPii: true, - tracesSampleRate: monitoringSampleRate, }) } diff --git a/packages/observability/src/monitoring/expo.ts b/packages/observability/src/monitoring/expo.ts index c0f6ebae..616b1e60 100644 --- a/packages/observability/src/monitoring/expo.ts +++ b/packages/observability/src/monitoring/expo.ts @@ -1,10 +1,10 @@ import { sentry } from "@init/env/presets" -import { isProduction } from "@init/utils/environment" import * as Sentry from "@sentry/react-native" +import { isProduction } from "std-env" export function initializeErrorMonitoring() { const env = sentry.expo() - const monitoringSampleRate = isProduction() ? 0.1 : 1 + const monitoringSampleRate = isProduction ? 0.1 : 1 Sentry.init({ dsn: env.EXPO_PUBLIC_SENTRY_DSN, diff --git a/packages/observability/src/monitoring/server.ts b/packages/observability/src/monitoring/server.ts index bc97740e..fb6dfb06 100644 --- a/packages/observability/src/monitoring/server.ts +++ b/packages/observability/src/monitoring/server.ts @@ -1,11 +1,11 @@ -import { node, sentry } from "@init/env/presets" -import { isProduction } from "@init/utils/environment" +import { sentry } from "@init/env/presets" import * as Sentry from "@sentry/node" +import { isProduction, isTest } from "std-env" export function initializeErrorMonitoring() { - const monitoringSampleRate = isProduction() ? 0.1 : 1 + const monitoringSampleRate = isProduction ? 0.1 : 1 const env = sentry.server() - const nodeEnv = node() + const environment = isProduction ? "production" : isTest ? "test" : "development" Sentry.init({ debug: env.SENTRY_DEBUG, @@ -13,7 +13,7 @@ export function initializeErrorMonitoring() { enableLogs: true, - environment: nodeEnv.NODE_ENV, + environment, integrations: [], diff --git a/packages/utils/package.json b/packages/utils/package.json index 3ade08a2..aa3a2bc7 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -16,6 +16,7 @@ "@init/error": "workspace:*", "clsx": "2.1.1", "nanoid": "5.1.6", + "std-env": "3.7.0", "superjson": "2.2.6", "tailwind-merge": "3.4.0", "ufo": "1.5.4", diff --git a/packages/utils/src/__tests__/url.test.ts b/packages/utils/src/__tests__/url.test.ts index 03c4207d..31ec958a 100644 --- a/packages/utils/src/__tests__/url.test.ts +++ b/packages/utils/src/__tests__/url.test.ts @@ -19,10 +19,10 @@ function expectUrlEqual(actual: string, expected: string) { } describe("createUrlBuilder", () => { - test("uses environment-based default protocol", () => { + test("defaults to https protocol", () => { const buildUrl = createUrlBuilder("example.com") const result = buildUrl("/test") - expect(result).toBe("http://example.com/test") + expect(result).toBe("https://example.com/test") }) test("uses specified protocol", () => { @@ -33,19 +33,19 @@ describe("createUrlBuilder", () => { test("handles base paths", () => { const buildUrl = createUrlBuilder("example.com/api/v1") - expect(buildUrl("/users")).toBe("http://example.com/api/v1/users") + expect(buildUrl("/users")).toBe("https://example.com/api/v1/users") const buildUrl2 = createUrlBuilder("example.com/api/v1/resources") - expect(buildUrl2("/items")).toBe("http://example.com/api/v1/resources/items") + expect(buildUrl2("/items")).toBe("https://example.com/api/v1/resources/items") }) test("normalizes pathnames", () => { const buildUrl = createUrlBuilder("example.com") - expect(buildUrl("/test")).toBe("http://example.com/test") - expect(buildUrl("test")).toBe("http://example.com/test") - expect(buildUrl("///test")).toBe("http://example.com/test") - expect(buildUrl("/api/v1/users/123")).toBe("http://example.com/api/v1/users/123") - expect(buildUrl("/test/")).toBe("http://example.com/test/") + expect(buildUrl("/test")).toBe("https://example.com/test") + expect(buildUrl("test")).toBe("https://example.com/test") + expect(buildUrl("///test")).toBe("https://example.com/test") + expect(buildUrl("/api/v1/users/123")).toBe("https://example.com/api/v1/users/123") + expect(buildUrl("/test/")).toBe("https://example.com/test/") }) test("handles query parameters", () => { @@ -53,21 +53,21 @@ describe("createUrlBuilder", () => { expectUrlEqual( buildUrl("/test", { query: { limit: 10, page: 1 } }), - "http://example.com/test?page=1&limit=10" + "https://example.com/test?page=1&limit=10" ) expectUrlEqual( buildUrl("/test", { query: { active: true, count: 0, page: 1, search: "hello world" }, }), - "http://example.com/test?page=1&active=true&search=hello+world&count=0" + "https://example.com/test?page=1&active=true&search=hello+world&count=0" ) expectUrlEqual( buildUrl("/test", { query: { filter: undefined, page: 1, search: "test" }, }), - "http://example.com/test?page=1&search=test" + "https://example.com/test?page=1&search=test" ) }) @@ -76,14 +76,14 @@ describe("createUrlBuilder", () => { expectUrlEqual( buildUrl("/test", { query: { search: "hello world" } }), - "http://example.com/test?search=hello+world" + "https://example.com/test?search=hello+world" ) expectUrlEqual( buildUrl("/search", { query: { filter: "type:email", q: "test@example.com" }, }), - "http://example.com/search?q=test%40example.com&filter=type%3Aemail" + "https://example.com/search?q=test%40example.com&filter=type%3Aemail" ) }) @@ -91,46 +91,46 @@ describe("createUrlBuilder", () => { const buildUrl = createUrlBuilder("api.example.com/v2/resources") expectUrlEqual( buildUrl("/users/123/posts", { query: { include: "comments" } }), - "http://api.example.com/v2/resources/users/123/posts?include=comments" + "https://api.example.com/v2/resources/users/123/posts?include=comments" ) const buildUrl2 = createUrlBuilder("localhost:3000/api", "http") expect(buildUrl2("/health")).toBe("http://localhost:3000/api/health") const buildUrl3 = createUrlBuilder("example.com/api//v1") - expect(buildUrl3("//users//123")).toBe("http://example.com/api/v1/users/123") + expect(buildUrl3("//users//123")).toBe("https://example.com/api/v1/users/123") }) test("handles edge cases", () => { const buildUrl = createUrlBuilder("example.com") - expect(buildUrl("")).toBe("http://example.com/") - expect(buildUrl("/")).toBe("http://example.com/") + expect(buildUrl("")).toBe("https://example.com/") + expect(buildUrl("/")).toBe("https://example.com/") expectUrlEqual( buildUrl("/test", { query: { active: false, verified: true } }), - "http://example.com/test?active=false&verified=true" + "https://example.com/test?active=false&verified=true" ) }) - test("preserves existing protocols", () => { + test("applies default protocol to URLs with existing protocols", () => { const buildUrl1 = createUrlBuilder("https://example.com") expect(buildUrl1("/test")).toBe("https://example.com/test") const buildUrl2 = createUrlBuilder("http://example.com") - expect(buildUrl2("/test")).toBe("http://example.com/test") + expect(buildUrl2("/test")).toBe("https://example.com/test") + + const buildUrl3 = createUrlBuilder("HTTP://example.com") + expect(buildUrl3("/test")).toBe("https://example.com/test") }) test("overrides existing protocols when specified", () => { - const buildUrl = createUrlBuilder("http://example.com", "https") - expect(buildUrl("/test")).toBe("https://example.com/test") + const buildUrl = createUrlBuilder("https://example.com", "http") + expect(buildUrl("/test")).toBe("http://example.com/test") - const buildUrl2 = createUrlBuilder("https://example.com/api/v1") + const buildUrl2 = createUrlBuilder("http://example.com/api/v1", "https") expect(buildUrl2("/users")).toBe("https://example.com/api/v1/users") - const buildUrl3 = createUrlBuilder("http://localhost:3000/api") + const buildUrl3 = createUrlBuilder("https://localhost:3000/api", "http") expect(buildUrl3("/health")).toBe("http://localhost:3000/api/health") - - const buildUrl4 = createUrlBuilder("HTTP://example.com", "https") - expect(buildUrl4("/test")).toBe("https://example.com/test") }) }) diff --git a/packages/utils/src/environment.ts b/packages/utils/src/environment.ts deleted file mode 100644 index b93a9caf..00000000 --- a/packages/utils/src/environment.ts +++ /dev/null @@ -1,55 +0,0 @@ -export const isDevelopment = () => { - try { - if (typeof import.meta.env.DEV === "boolean") { - return import.meta.env.DEV - } - - if (typeof import.meta.env.NODE_ENV === "string") { - return import.meta.env.NODE_ENV === "development" - } - } catch { - // Import.meta not available - } - - return process.env.NODE_ENV === "development" -} - -export function isProduction() { - try { - if (typeof import.meta.env.PROD === "boolean") { - return import.meta.env.PROD - } - - if (typeof import.meta.env.NODE_ENV === "string") { - return import.meta.env.NODE_ENV === "production" - } - } catch { - // Import.meta not available - } - - return process.env.NODE_ENV === "production" -} - -export function isTest() { - return ( - typeof process !== "undefined" && - typeof process.env.NODE_ENV === "string" && - process.env.NODE_ENV === "test" - ) -} - -export function isCI() { - return ( - typeof process !== "undefined" && - typeof process.env.CI === "string" && - process.env.CI === "true" - ) -} - -export function isClient() { - return typeof globalThis !== "undefined" && "window" in globalThis -} - -export function isServer() { - return !isClient() -} diff --git a/packages/utils/src/url.ts b/packages/utils/src/url.ts index f5f990ea..be9cc0b0 100644 --- a/packages/utils/src/url.ts +++ b/packages/utils/src/url.ts @@ -1,5 +1,4 @@ import { cleanDoubleSlashes, joinURL, normalizeURL, withQuery, withTrailingSlash } from "ufo" -import { isProduction } from "./environment" /** * Creates a URL builder function for a given base URL. @@ -10,13 +9,11 @@ import { isProduction } from "./environment" * @throws {Error} If the base URL contains a dangerous or unsupported protocol * @throws {Error} If the base URL contains credentials */ -export function createUrlBuilder(baseUrl: string, protocol?: "http" | "https") { +export function createUrlBuilder(baseUrl: string, protocol: "http" | "https" = "https") { const trimmedBaseUrl = baseUrl.trim() const base = /^https?:\/\//i.test(trimmedBaseUrl) - ? protocol - ? trimmedBaseUrl.replace(/^https?:\/\//i, `${protocol}://`) - : trimmedBaseUrl - : `${protocol ?? (isProduction() ? "https" : "http")}://${trimmedBaseUrl}` + ? trimmedBaseUrl.replace(/^https?:\/\//i, `${protocol}://`) + : `${protocol}://${trimmedBaseUrl}` return function buildUrl( pathname: T, diff --git a/turbo.json b/turbo.json index 64ab4ee9..bb367f9b 100644 --- a/turbo.json +++ b/turbo.json @@ -1,6 +1,6 @@ { "$schema": "https://turbo.build/schema.json", - "globalPassThroughEnv": ["NODE_ENV", "CI"], + "globalPassThroughEnv": ["CI"], "concurrency": "15", "tasks": { "build": {