diff --git a/.env.example b/.env.example index e9353ac..85de15f 100644 --- a/.env.example +++ b/.env.example @@ -18,3 +18,7 @@ S3_BUCKET=solid-replays S3_ACCESS_KEY_ID=solid S3_SECRET_ACCESS_KEY=solidsecret S3_FORCE_PATH_STYLE=true +# Sentry/GlitchTip (errors-only). Leave SENTRY_DSN empty to disable reporting (SDK no-op). +# In staging/prod the DSN is injected by infra; SENTRY_RELEASE is an optional build SHA. +SENTRY_DSN= +SENTRY_RELEASE= diff --git a/.env.production.example b/.env.production.example index 2872498..4741463 100644 --- a/.env.production.example +++ b/.env.production.example @@ -18,6 +18,10 @@ S3_BUCKET=solid-replays S3_ACCESS_KEY_ID=solid S3_SECRET_ACCESS_KEY=change-me S3_FORCE_PATH_STYLE=true +# Sentry/GlitchTip (errors-only). Injected by infra via the server-2-runtime Secret; +# leave SENTRY_DSN empty to disable reporting (SDK no-op). SENTRY_RELEASE is an optional build SHA. +SENTRY_DSN= +SENTRY_RELEASE= POSTGRES_PASSWORD=change-me RABBITMQ_PASSWORD=change-me diff --git a/package.json b/package.json index 26a3732..7b9e162d 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "@fastify/swagger": "^9.7.0", "@fastify/swagger-ui": "^5.2.6", "@fastify/type-provider-typebox": "^6.1.0", + "@sentry/node": "^10.57.0", "@sinclair/typebox": "^0.34.49", "amqplib": "^1.0.7", "dotenv": "^17.4.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 707507e..29dae5c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15,13 +15,16 @@ importers: version: 3.1045.0 "@fastify/swagger": specifier: ^9.7.0 - version: 9.7.0 + version: 9.7.0(supports-color@10.2.2) "@fastify/swagger-ui": specifier: ^5.2.6 version: 5.2.6 "@fastify/type-provider-typebox": specifier: ^6.1.0 version: 6.1.0(typebox@1.1.38) + "@sentry/node": + specifier: ^10.57.0 + version: 10.57.0 "@sinclair/typebox": specifier: ^0.34.49 version: 0.34.49 @@ -52,7 +55,7 @@ importers: devDependencies: "@eslint/js": specifier: ^10.0.1 - version: 10.0.1(eslint@10.3.0) + version: 10.0.1(eslint@10.3.0(supports-color@10.2.2)) "@types/amqplib": specifier: ^0.10.8 version: 0.10.8 @@ -67,16 +70,16 @@ importers: version: 4.1.5(vitest@4.1.5) eslint: specifier: ^10.3.0 - version: 10.3.0 + version: 10.3.0(supports-color@10.2.2) eslint-import-resolver-typescript: specifier: ^4.4.4 - version: 4.4.4(eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.59.2(eslint@10.3.0)(typescript@6.0.3))(eslint@10.3.0))(eslint@10.3.0) + version: 4.4.4(eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.59.2(eslint@10.3.0(supports-color@10.2.2))(typescript@6.0.3))(eslint@10.3.0(supports-color@10.2.2))(supports-color@10.2.2))(eslint@10.3.0(supports-color@10.2.2))(supports-color@10.2.2) eslint-plugin-import-x: specifier: ^4.16.2 - version: 4.16.2(@typescript-eslint/utils@8.59.2(eslint@10.3.0)(typescript@6.0.3))(eslint@10.3.0) + version: 4.16.2(@typescript-eslint/utils@8.59.2(eslint@10.3.0(supports-color@10.2.2))(typescript@6.0.3))(eslint@10.3.0(supports-color@10.2.2))(supports-color@10.2.2) eslint-plugin-unicorn: specifier: ^64.0.0 - version: 64.0.0(eslint@10.3.0) + version: 64.0.0(eslint@10.3.0(supports-color@10.2.2)) openapi-typescript: specifier: ^7.13.0 version: 7.13.0(typescript@6.0.3) @@ -91,7 +94,7 @@ importers: version: 6.0.3 typescript-eslint: specifier: ^8.59.2 - version: 8.59.2(eslint@10.3.0)(typescript@6.0.3) + version: 8.59.2(eslint@10.3.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) vitest: specifier: ^4.1.5 version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@25.6.2)(@vitest/coverage-v8@4.1.5)(vite@8.0.11(@types/node@25.6.2)(esbuild@0.27.7)(tsx@4.21.0)(yaml@2.8.4)) @@ -904,6 +907,13 @@ packages: integrity: sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA==, } + "@opentelemetry/api-logs@0.214.0": + resolution: + { + integrity: sha512-40lSJeqYO8Uz2Yj7u94/SJWE/wONa7rmMKjI1ZcIjgf3MHNHv1OZUCrCETGuaRF62d5pQD1wKIW+L4lmSMTzZA==, + } + engines: { node: ">=8.0.0" } + "@opentelemetry/api@1.9.1": resolution: { @@ -911,6 +921,49 @@ packages: } engines: { node: ">=8.0.0" } + "@opentelemetry/core@2.8.0": + resolution: + { + integrity: sha512-hd1Lfh8p545nNz+jq1Ejfz+Mn1hyLuxYn1YzTfFNrxr8urEWMNQLPf1Th8kjOH+HxwawCrtgBp8JpBUR4ZSgww==, + } + engines: { node: ^18.19.0 || >=20.6.0 } + peerDependencies: + "@opentelemetry/api": ">=1.0.0 <1.10.0" + + "@opentelemetry/instrumentation@0.214.0": + resolution: + { + integrity: sha512-MHqEX5Dk59cqVah5LiARMACku7jXSVk9iVDWOea4x3cr7VfdByeDCURK6o1lntT1JS/Tsovw01UJrBhN3/uC5w==, + } + engines: { node: ^18.19.0 || >=20.6.0 } + peerDependencies: + "@opentelemetry/api": ^1.3.0 + + "@opentelemetry/resources@2.8.0": + resolution: + { + integrity: sha512-qmXQ27ilDbUK/vGMqwL8D4/rhn76C+sherM4wTbjlfknR8Nvfc/hCxjRJPhkzZzUsPiNg16SA31NxMabwttRjg==, + } + engines: { node: ^18.19.0 || >=20.6.0 } + peerDependencies: + "@opentelemetry/api": ">=1.3.0 <1.10.0" + + "@opentelemetry/sdk-trace-base@2.8.0": + resolution: + { + integrity: sha512-mhU4jp+vW0mGbFRd+GeXHvmfA4aDqWjBjLC3pE5XMpLs0IE2ryYb019Ts2AQrOq67gaTF25D91+fgvEHDZEnuQ==, + } + engines: { node: ^18.19.0 || >=20.6.0 } + peerDependencies: + "@opentelemetry/api": ">=1.3.0 <1.10.0" + + "@opentelemetry/semantic-conventions@1.41.1": + resolution: + { + integrity: sha512-/UhIkaZgPutTFmQ7RnIJGgDXZmtEJ7Dvi86xNTFWcnRxVRNk/aotsqDJYeEvDP+FSMB2SdW+pQzNMcWP0rwuNA==, + } + engines: { node: ">=14" } + "@oxc-project/types@0.128.0": resolution: { @@ -1094,6 +1147,66 @@ packages: integrity: sha512-CUY5Mnhe64xQBGZEEXQ5WyZwsc1JU3vAZLIxtrsBt3LO6UOb+C8GunVKqe9sT8NeWb4lqSaoJtp2xo6GxT1MNw==, } + "@sentry-internal/server-utils@10.57.0": + resolution: + { + integrity: sha512-Qu8ETmX/ITzteG7Im46b9HOxKKzeaIeqNvftaIlFURu1RUQdHbtGerS7QOmXzwnhuqNGNeiCQYkduB798IfRqA==, + } + engines: { node: ">=18" } + + "@sentry/core@10.57.0": + resolution: + { + integrity: sha512-kntItTA2kiT0YpL7encXaF6mkdZMB+y48lwj8w1wkfBpfJAC7sifdgrzLQZqmsqVNE3crg9VfufaAGA+78uFMg==, + } + engines: { node: ">=18" } + + "@sentry/node-core@10.57.0": + resolution: + { + integrity: sha512-2v2IF6MfTiu7pimWEq2rYhZsmlwyNbs3bHUsrYFPeP/Rpa6ObDuUWPdVEzJjfyK+AqqYZYxZdV0l3+B13kTEmQ==, + } + engines: { node: ">=18" } + peerDependencies: + "@opentelemetry/api": ^1.9.0 + "@opentelemetry/core": ^1.30.1 || ^2.1.0 + "@opentelemetry/exporter-trace-otlp-http": ">=0.57.0 <1" + "@opentelemetry/instrumentation": ">=0.57.1 <1" + "@opentelemetry/sdk-trace-base": ^1.30.1 || ^2.1.0 + "@opentelemetry/semantic-conventions": ^1.39.0 + peerDependenciesMeta: + "@opentelemetry/api": + optional: true + "@opentelemetry/core": + optional: true + "@opentelemetry/exporter-trace-otlp-http": + optional: true + "@opentelemetry/instrumentation": + optional: true + "@opentelemetry/sdk-trace-base": + optional: true + "@opentelemetry/semantic-conventions": + optional: true + + "@sentry/node@10.57.0": + resolution: + { + integrity: sha512-7KEStrJ97wPf1fA5nU5ONeTTcIIlh7oT8OMffEVA1PXmlhFoXhcQZVzr4rM+zj9tfMWT01og5Ng/Grgh3dN+FA==, + } + engines: { node: ">=18" } + + "@sentry/opentelemetry@10.57.0": + resolution: + { + integrity: sha512-iwRz8cEK0GOISG34aJRO8GdYOk3nfpuT6dT2GDQrxw8f7JjkJKx9LPU8MaenOFa4MhY+Z02hI6NNcrbsoI3cXg==, + } + engines: { node: ">=18" } + peerDependencies: + "@opentelemetry/api": ^1.9.0 + "@opentelemetry/core": ^1.30.1 || ^2.1.0 + "@opentelemetry/sdk-trace-base": ^1.30.1 || ^2.1.0 + "@opentelemetry/semantic-conventions": ^1.39.0 + "@sinclair/typebox@0.34.49": resolution: { @@ -1848,6 +1961,14 @@ packages: integrity: sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==, } + acorn-import-attributes@1.9.5: + resolution: + { + integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==, + } + peerDependencies: + acorn: ^8 + acorn-jsx@5.3.2: resolution: { @@ -2033,6 +2154,12 @@ packages: } engines: { node: ">=8" } + cjs-module-lexer@2.2.0: + resolution: + { + integrity: sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==, + } + clean-regexp@1.0.0: resolution: { @@ -2523,6 +2650,13 @@ packages: } engines: { node: ">= 4" } + import-in-the-middle@3.0.2: + resolution: + { + integrity: sha512-LGLYRl0A2gtyUJb2WDliBHmk6TtlHwdDjxonacZ8QrEs/ZW+YDgNv2QAfjRQWpS8HqvNcq6GGnN6jrOa5FysDQ==, + } + engines: { node: ">=18" } + imurmurhash@0.1.4: resolution: { @@ -2880,6 +3014,12 @@ packages: } engines: { node: ">=16 || 14 >=14.17" } + module-details-from-path@1.0.4: + resolution: + { + integrity: sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==, + } + ms@2.1.3: resolution: { @@ -3211,6 +3351,13 @@ packages: } engines: { node: ">=0.10.0" } + require-in-the-middle@8.0.1: + resolution: + { + integrity: sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==, + } + engines: { node: ">=9.3.0 || >=8.10.0 <9.0.0" } + resolve-pkg-maps@1.0.0: resolution: { @@ -4257,12 +4404,12 @@ snapshots: "@eslint-community/eslint-utils@4.9.1(eslint@10.3.0)": dependencies: - eslint: 10.3.0 + eslint: 10.3.0(supports-color@10.2.2) eslint-visitor-keys: 3.4.3 "@eslint-community/regexpp@4.12.2": {} - "@eslint/config-array@0.23.5": + "@eslint/config-array@0.23.5(supports-color@10.2.2)": dependencies: "@eslint/object-schema": 3.0.5 debug: 4.4.3(supports-color@10.2.2) @@ -4278,9 +4425,9 @@ snapshots: dependencies: "@types/json-schema": 7.0.15 - "@eslint/js@10.0.1(eslint@10.3.0)": + "@eslint/js@10.0.1(eslint@10.3.0(supports-color@10.2.2))": optionalDependencies: - eslint: 10.3.0 + eslint: 10.3.0(supports-color@10.2.2) "@eslint/object-schema@3.0.5": {} @@ -4339,10 +4486,10 @@ snapshots: rfdc: 1.4.1 yaml: 2.8.4 - "@fastify/swagger@9.7.0": + "@fastify/swagger@9.7.0(supports-color@10.2.2)": dependencies: fastify-plugin: 5.1.0 - json-schema-resolver: 3.0.0 + json-schema-resolver: 3.0.0(supports-color@10.2.2) openapi-types: 12.1.3 rfdc: 1.4.1 yaml: 2.8.4 @@ -4396,8 +4543,41 @@ snapshots: "@nodable/entities@2.1.0": {} + "@opentelemetry/api-logs@0.214.0": + dependencies: + "@opentelemetry/api": 1.9.1 + "@opentelemetry/api@1.9.1": {} + "@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1)": + dependencies: + "@opentelemetry/api": 1.9.1 + "@opentelemetry/semantic-conventions": 1.41.1 + + "@opentelemetry/instrumentation@0.214.0(@opentelemetry/api@1.9.1)": + dependencies: + "@opentelemetry/api": 1.9.1 + "@opentelemetry/api-logs": 0.214.0 + import-in-the-middle: 3.0.2 + require-in-the-middle: 8.0.1 + transitivePeerDependencies: + - supports-color + + "@opentelemetry/resources@2.8.0(@opentelemetry/api@1.9.1)": + dependencies: + "@opentelemetry/api": 1.9.1 + "@opentelemetry/core": 2.8.0(@opentelemetry/api@1.9.1) + "@opentelemetry/semantic-conventions": 1.41.1 + + "@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1)": + dependencies: + "@opentelemetry/api": 1.9.1 + "@opentelemetry/core": 2.8.0(@opentelemetry/api@1.9.1) + "@opentelemetry/resources": 2.8.0(@opentelemetry/api@1.9.1) + "@opentelemetry/semantic-conventions": 1.41.1 + + "@opentelemetry/semantic-conventions@1.41.1": {} + "@oxc-project/types@0.128.0": {} "@package-json/types@0.0.12": {} @@ -4478,6 +4658,48 @@ snapshots: "@rolldown/pluginutils@1.0.0-rc.18": {} + "@sentry-internal/server-utils@10.57.0": + dependencies: + "@sentry/core": 10.57.0 + + "@sentry/core@10.57.0": {} + + "@sentry/node-core@10.57.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.214.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.41.1)": + dependencies: + "@sentry/core": 10.57.0 + "@sentry/opentelemetry": 10.57.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.41.1) + import-in-the-middle: 3.0.2 + optionalDependencies: + "@opentelemetry/api": 1.9.1 + "@opentelemetry/core": 2.8.0(@opentelemetry/api@1.9.1) + "@opentelemetry/instrumentation": 0.214.0(@opentelemetry/api@1.9.1) + "@opentelemetry/sdk-trace-base": 2.8.0(@opentelemetry/api@1.9.1) + "@opentelemetry/semantic-conventions": 1.41.1 + + "@sentry/node@10.57.0": + dependencies: + "@opentelemetry/api": 1.9.1 + "@opentelemetry/core": 2.8.0(@opentelemetry/api@1.9.1) + "@opentelemetry/instrumentation": 0.214.0(@opentelemetry/api@1.9.1) + "@opentelemetry/sdk-trace-base": 2.8.0(@opentelemetry/api@1.9.1) + "@opentelemetry/semantic-conventions": 1.41.1 + "@sentry-internal/server-utils": 10.57.0 + "@sentry/core": 10.57.0 + "@sentry/node-core": 10.57.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.214.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.41.1) + "@sentry/opentelemetry": 10.57.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.41.1) + import-in-the-middle: 3.0.2 + transitivePeerDependencies: + - "@opentelemetry/exporter-trace-otlp-http" + - supports-color + + "@sentry/opentelemetry@10.57.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.41.1)": + dependencies: + "@opentelemetry/api": 1.9.1 + "@opentelemetry/core": 2.8.0(@opentelemetry/api@1.9.1) + "@opentelemetry/sdk-trace-base": 2.8.0(@opentelemetry/api@1.9.1) + "@opentelemetry/semantic-conventions": 1.41.1 + "@sentry/core": 10.57.0 + "@sinclair/typebox@0.34.49": {} "@smithy/chunked-blob-reader-native@4.2.3": @@ -4847,15 +5069,15 @@ snapshots: pg-protocol: 1.13.0 pg-types: 2.2.0 - "@typescript-eslint/eslint-plugin@8.59.2(@typescript-eslint/parser@8.59.2(eslint@10.3.0)(typescript@6.0.3))(eslint@10.3.0)(typescript@6.0.3)": + "@typescript-eslint/eslint-plugin@8.59.2(@typescript-eslint/parser@8.59.2(eslint@10.3.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.3.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)": dependencies: "@eslint-community/regexpp": 4.12.2 - "@typescript-eslint/parser": 8.59.2(eslint@10.3.0)(typescript@6.0.3) + "@typescript-eslint/parser": 8.59.2(eslint@10.3.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) "@typescript-eslint/scope-manager": 8.59.2 - "@typescript-eslint/type-utils": 8.59.2(eslint@10.3.0)(typescript@6.0.3) - "@typescript-eslint/utils": 8.59.2(eslint@10.3.0)(typescript@6.0.3) + "@typescript-eslint/type-utils": 8.59.2(eslint@10.3.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + "@typescript-eslint/utils": 8.59.2(eslint@10.3.0(supports-color@10.2.2))(typescript@6.0.3) "@typescript-eslint/visitor-keys": 8.59.2 - eslint: 10.3.0 + eslint: 10.3.0(supports-color@10.2.2) ignore: 7.0.5 natural-compare: 1.4.0 ts-api-utils: 2.5.0(typescript@6.0.3) @@ -4863,14 +5085,14 @@ snapshots: transitivePeerDependencies: - supports-color - "@typescript-eslint/parser@8.59.2(eslint@10.3.0)(typescript@6.0.3)": + "@typescript-eslint/parser@8.59.2(eslint@10.3.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)": dependencies: "@typescript-eslint/scope-manager": 8.59.2 "@typescript-eslint/types": 8.59.2 "@typescript-eslint/typescript-estree": 8.59.2(typescript@6.0.3) "@typescript-eslint/visitor-keys": 8.59.2 debug: 4.4.3(supports-color@10.2.2) - eslint: 10.3.0 + eslint: 10.3.0(supports-color@10.2.2) typescript: 6.0.3 transitivePeerDependencies: - supports-color @@ -4893,13 +5115,13 @@ snapshots: dependencies: typescript: 6.0.3 - "@typescript-eslint/type-utils@8.59.2(eslint@10.3.0)(typescript@6.0.3)": + "@typescript-eslint/type-utils@8.59.2(eslint@10.3.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)": dependencies: "@typescript-eslint/types": 8.59.2 "@typescript-eslint/typescript-estree": 8.59.2(typescript@6.0.3) - "@typescript-eslint/utils": 8.59.2(eslint@10.3.0)(typescript@6.0.3) + "@typescript-eslint/utils": 8.59.2(eslint@10.3.0(supports-color@10.2.2))(typescript@6.0.3) debug: 4.4.3(supports-color@10.2.2) - eslint: 10.3.0 + eslint: 10.3.0(supports-color@10.2.2) ts-api-utils: 2.5.0(typescript@6.0.3) typescript: 6.0.3 transitivePeerDependencies: @@ -4922,13 +5144,13 @@ snapshots: transitivePeerDependencies: - supports-color - "@typescript-eslint/utils@8.59.2(eslint@10.3.0)(typescript@6.0.3)": + "@typescript-eslint/utils@8.59.2(eslint@10.3.0(supports-color@10.2.2))(typescript@6.0.3)": dependencies: "@eslint-community/eslint-utils": 4.9.1(eslint@10.3.0) "@typescript-eslint/scope-manager": 8.59.2 "@typescript-eslint/types": 8.59.2 "@typescript-eslint/typescript-estree": 8.59.2(typescript@6.0.3) - eslint: 10.3.0 + eslint: 10.3.0(supports-color@10.2.2) typescript: 6.0.3 transitivePeerDependencies: - supports-color @@ -5054,6 +5276,10 @@ snapshots: abstract-logging@2.0.1: {} + acorn-import-attributes@1.9.5(acorn@8.16.0): + dependencies: + acorn: 8.16.0 + acorn-jsx@5.3.2(acorn@8.16.0): dependencies: acorn: 8.16.0 @@ -5141,6 +5367,8 @@ snapshots: ci-info@4.4.0: {} + cjs-module-lexer@2.2.0: {} + clean-regexp@1.0.0: dependencies: escape-string-regexp: 1.0.5 @@ -5233,10 +5461,10 @@ snapshots: optionalDependencies: unrs-resolver: 1.11.1 - eslint-import-resolver-typescript@4.4.4(eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.59.2(eslint@10.3.0)(typescript@6.0.3))(eslint@10.3.0))(eslint@10.3.0): + eslint-import-resolver-typescript@4.4.4(eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.59.2(eslint@10.3.0(supports-color@10.2.2))(typescript@6.0.3))(eslint@10.3.0(supports-color@10.2.2))(supports-color@10.2.2))(eslint@10.3.0(supports-color@10.2.2))(supports-color@10.2.2): dependencies: debug: 4.4.3(supports-color@10.2.2) - eslint: 10.3.0 + eslint: 10.3.0(supports-color@10.2.2) eslint-import-context: 0.1.9(unrs-resolver@1.11.1) get-tsconfig: 4.14.0 is-bun-module: 2.0.0 @@ -5244,17 +5472,17 @@ snapshots: tinyglobby: 0.2.16 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import-x: 4.16.2(@typescript-eslint/utils@8.59.2(eslint@10.3.0)(typescript@6.0.3))(eslint@10.3.0) + eslint-plugin-import-x: 4.16.2(@typescript-eslint/utils@8.59.2(eslint@10.3.0(supports-color@10.2.2))(typescript@6.0.3))(eslint@10.3.0(supports-color@10.2.2))(supports-color@10.2.2) transitivePeerDependencies: - supports-color - eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.59.2(eslint@10.3.0)(typescript@6.0.3))(eslint@10.3.0): + eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.59.2(eslint@10.3.0(supports-color@10.2.2))(typescript@6.0.3))(eslint@10.3.0(supports-color@10.2.2))(supports-color@10.2.2): dependencies: "@package-json/types": 0.0.12 "@typescript-eslint/types": 8.59.2 comment-parser: 1.4.6 debug: 4.4.3(supports-color@10.2.2) - eslint: 10.3.0 + eslint: 10.3.0(supports-color@10.2.2) eslint-import-context: 0.1.9(unrs-resolver@1.11.1) is-glob: 4.0.3 minimatch: 10.2.5 @@ -5262,11 +5490,11 @@ snapshots: stable-hash-x: 0.2.0 unrs-resolver: 1.11.1 optionalDependencies: - "@typescript-eslint/utils": 8.59.2(eslint@10.3.0)(typescript@6.0.3) + "@typescript-eslint/utils": 8.59.2(eslint@10.3.0(supports-color@10.2.2))(typescript@6.0.3) transitivePeerDependencies: - supports-color - eslint-plugin-unicorn@64.0.0(eslint@10.3.0): + eslint-plugin-unicorn@64.0.0(eslint@10.3.0(supports-color@10.2.2)): dependencies: "@babel/helper-validator-identifier": 7.28.5 "@eslint-community/eslint-utils": 4.9.1(eslint@10.3.0) @@ -5274,7 +5502,7 @@ snapshots: ci-info: 4.4.0 clean-regexp: 1.0.0 core-js-compat: 3.49.0 - eslint: 10.3.0 + eslint: 10.3.0(supports-color@10.2.2) find-up-simple: 1.0.1 globals: 17.6.0 indent-string: 5.0.0 @@ -5297,11 +5525,11 @@ snapshots: eslint-visitor-keys@5.0.1: {} - eslint@10.3.0: + eslint@10.3.0(supports-color@10.2.2): dependencies: "@eslint-community/eslint-utils": 4.9.1(eslint@10.3.0) "@eslint-community/regexpp": 4.12.2 - "@eslint/config-array": 0.23.5 + "@eslint/config-array": 0.23.5(supports-color@10.2.2) "@eslint/config-helpers": 0.5.5 "@eslint/core": 1.2.1 "@eslint/plugin-kit": 0.7.1 @@ -5484,6 +5712,13 @@ snapshots: ignore@7.0.5: {} + import-in-the-middle@3.0.2: + dependencies: + acorn: 8.16.0 + acorn-import-attributes: 1.9.5(acorn@8.16.0) + cjs-module-lexer: 2.2.0 + module-details-from-path: 1.0.4 + imurmurhash@0.1.4: {} indent-string@5.0.0: {} @@ -5541,7 +5776,7 @@ snapshots: dependencies: dequal: 2.0.3 - json-schema-resolver@3.0.0: + json-schema-resolver@3.0.0(supports-color@10.2.2): dependencies: debug: 4.4.3(supports-color@10.2.2) fast-uri: 3.1.2 @@ -5653,6 +5888,8 @@ snapshots: minipass@7.1.3: {} + module-details-from-path@1.0.4: {} + ms@2.1.3: {} nanoid@3.3.12: {} @@ -5819,6 +6056,13 @@ snapshots: require-from-string@2.0.2: {} + require-in-the-middle@8.0.1: + dependencies: + debug: 4.4.3(supports-color@10.2.2) + module-details-from-path: 1.0.4 + transitivePeerDependencies: + - supports-color + resolve-pkg-maps@1.0.0: {} ret@0.5.0: {} @@ -5940,13 +6184,13 @@ snapshots: typebox@1.1.38: {} - typescript-eslint@8.59.2(eslint@10.3.0)(typescript@6.0.3): + typescript-eslint@8.59.2(eslint@10.3.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3): dependencies: - "@typescript-eslint/eslint-plugin": 8.59.2(@typescript-eslint/parser@8.59.2(eslint@10.3.0)(typescript@6.0.3))(eslint@10.3.0)(typescript@6.0.3) - "@typescript-eslint/parser": 8.59.2(eslint@10.3.0)(typescript@6.0.3) + "@typescript-eslint/eslint-plugin": 8.59.2(@typescript-eslint/parser@8.59.2(eslint@10.3.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.3.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + "@typescript-eslint/parser": 8.59.2(eslint@10.3.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) "@typescript-eslint/typescript-estree": 8.59.2(typescript@6.0.3) - "@typescript-eslint/utils": 8.59.2(eslint@10.3.0)(typescript@6.0.3) - eslint: 10.3.0 + "@typescript-eslint/utils": 8.59.2(eslint@10.3.0(supports-color@10.2.2))(typescript@6.0.3) + eslint: 10.3.0(supports-color@10.2.2) typescript: 6.0.3 transitivePeerDependencies: - supports-color diff --git a/src/instrument.ts b/src/instrument.ts new file mode 100644 index 0000000..c2e7124 --- /dev/null +++ b/src/instrument.ts @@ -0,0 +1,29 @@ +// Sentry SDK init — errors-only, reporting to the self-hosted GlitchTip. +// +// This file MUST be imported before any other module (see src/server.ts) so the +// SDK can install its global error/unhandled-rejection handlers before the app +// and framework load. Reading process.env directly here is intentional: this is a +// pre-boot side-effect file that runs before the validated config loader (envalid) +// — the same exception we make for `dotenv/config`. +// +// `dotenv/config` runs FIRST, before Sentry.init, so a DSN supplied via the env +// file (see .env.example) is in process.env when the SDK reads it; without this, +// instrument.ts would init before dotenv populated the file values and the DSN +// would be undefined (SDK no-op). Infra-injected env vars are already present +// regardless. The import is idempotent — ESM caches it, so the matching import +// in server.ts (now removed) would only have run once anyway. +import "dotenv/config"; + +import * as Sentry from "@sentry/node"; + +Sentry.init({ + // Empty/absent DSN makes the SDK a no-op — no init gate needed. + // The DSN is injected by infra via the server-2-runtime k8s Secret. + dsn: process.env["SENTRY_DSN"], + environment: process.env["NODE_ENV"] ?? "staging", + // Optional build SHA; undefined when not provided. + release: process.env["SENTRY_RELEASE"], + // Errors-only: no performance tracing, no profiling, no session replay. + // Default integrations still capture errors + unhandled rejections/exceptions. + tracesSampleRate: 0, +}); diff --git a/src/server.ts b/src/server.ts index 7e4c461..ac23a63 100644 --- a/src/server.ts +++ b/src/server.ts @@ -1,4 +1,7 @@ -import "dotenv/config"; +// FIRST — loads .env and initializes Sentry before anything else loads. +// instrument.js imports `dotenv/config` itself (before Sentry.init), so env +// values are available to both the SDK and the config loader below. +import "./instrument.js"; import { buildApp, createDefaultAuthOptions } from "./app.js"; import { loadConfig, redactConfigForLogs } from "./config/env.js"; diff --git a/vitest.config.ts b/vitest.config.ts index 5b373c4..4a0056a 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -6,6 +6,7 @@ export default defineConfig({ exclude: [ "dist/**", "src/infra/db/migrate.ts", + "src/instrument.ts", "src/openapi/export-openapi.ts", "src/openapi/schema.ts", "src/openapi/verify-openapi.ts",