diff --git a/apps/rest-api-server/package.json b/apps/rest-api-server/package.json index cacc1b9a3..96408a340 100644 --- a/apps/rest-api-server/package.json +++ b/apps/rest-api-server/package.json @@ -16,6 +16,7 @@ "license": "MIT", "dependencies": { "@blobscan/api": "workspace:^0.9.0", + "@blobscan/env": "workspace:^0.0.1", "@blobscan/logger": "workspace:^0.1.0", "@blobscan/open-telemetry": "workspace:^0.0.7", "@blobscan/stats-syncer": "workspace:^0.1.8", diff --git a/apps/rest-api-server/sentry.ts b/apps/rest-api-server/sentry.ts index 7d96a1f1f..7b5d24ef5 100644 --- a/apps/rest-api-server/sentry.ts +++ b/apps/rest-api-server/sentry.ts @@ -1,6 +1,6 @@ import * as Sentry from "@sentry/node"; -import { env } from "./src/env"; +import { env } from "@blobscan/env"; Sentry.init({ dsn: env.SENTRY_DSN_API, diff --git a/apps/rest-api-server/src/env.ts b/apps/rest-api-server/src/env.ts deleted file mode 100644 index 09b03292a..000000000 --- a/apps/rest-api-server/src/env.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { - z, - booleanSchema, - createEnv, - presetEnvOptions, - nodeEnvSchema, - maskPassword, - networkSchema, -} from "@blobscan/zod"; - -export const env = createEnv({ - envOptions: { - server: { - BLOBSCAN_API_BASE_URL: z - .string() - .url() - .default("https://api.blobscan.com"), - BLOBSCAN_API_PORT: z.coerce.number().positive().default(3001), - NETWORK_NAME: networkSchema.default("mainnet"), - NODE_ENV: nodeEnvSchema.optional(), - TRACES_ENABLED: booleanSchema.default("false"), - METRICS_ENABLED: booleanSchema.default("false"), - REDIS_URI: z.string().default("redis://localhost:6379"), - DENCUN_FORK_SLOT: z.coerce.number().optional(), - STATS_SYNCER_DAILY_CRON_PATTERN: z.string().default("30 0 * * * *"), - STATS_SYNCER_OVERALL_CRON_PATTERN: z.string().default("*/15 * * * *"), - SENTRY_DSN_API: z.string().url().optional(), - }, - - ...presetEnvOptions, - }, - display(env) { - console.log( - `Configuration: network=${ - env.NETWORK_NAME - } sentryEnabled=${!!env.SENTRY_DSN_API} metrics=${ - env.METRICS_ENABLED - } traces=${env.TRACES_ENABLED} port=${ - env.BLOBSCAN_API_PORT - } redisUri=${maskPassword(env.REDIS_URI)} dailyStatsCron=${ - env.STATS_SYNCER_DAILY_CRON_PATTERN - } overallStatsCron=${ - env.STATS_SYNCER_OVERALL_CRON_PATTERN - } dencunForkSlot=${env.DENCUN_FORK_SLOT ?? "auto"}` - ); - }, -}); - -export type Environment = typeof env; diff --git a/apps/rest-api-server/src/index.ts b/apps/rest-api-server/src/index.ts index e62bf6f75..0ffea8111 100644 --- a/apps/rest-api-server/src/index.ts +++ b/apps/rest-api-server/src/index.ts @@ -13,10 +13,10 @@ import { metricsHandler, gracefulShutdown as apiGracefulShutdown, } from "@blobscan/api"; +import { env } from "@blobscan/env"; import { collectDefaultMetrics } from "@blobscan/open-telemetry"; import { StatsSyncer } from "@blobscan/stats-syncer"; -import { env } from "./env"; import { logger } from "./logger"; import { morganMiddleware } from "./morgan"; import { openApiDocument } from "./openapi"; diff --git a/apps/rest-api-server/src/openapi.ts b/apps/rest-api-server/src/openapi.ts index cd693323c..e18a0de0e 100644 --- a/apps/rest-api-server/src/openapi.ts +++ b/apps/rest-api-server/src/openapi.ts @@ -1,8 +1,7 @@ import { generateOpenApiDocument } from "trpc-openapi"; import { appRouter } from "@blobscan/api"; - -import { env } from "./env"; +import { env } from "@blobscan/env"; // Generate OpenAPI schema document export const openApiDocument = generateOpenApiDocument(appRouter, { diff --git a/apps/rest-api-server/src/scripts/instrumentation.ts b/apps/rest-api-server/src/scripts/instrumentation.ts index 44885a724..ef8b5a1e2 100644 --- a/apps/rest-api-server/src/scripts/instrumentation.ts +++ b/apps/rest-api-server/src/scripts/instrumentation.ts @@ -1,9 +1,8 @@ import { ExpressInstrumentation } from "@opentelemetry/instrumentation-express"; +import { env } from "@blobscan/env"; import { setUpOpenTelemetry } from "@blobscan/open-telemetry"; -import { env } from "../env"; - if (env.TRACES_ENABLED) { setUpOpenTelemetry("blobscan_rest_api", { instrumentations: [new ExpressInstrumentation()], diff --git a/apps/rest-api-server/src/scripts/print-banner.ts b/apps/rest-api-server/src/scripts/print-banner.ts index ae90fe90b..77da95031 100644 --- a/apps/rest-api-server/src/scripts/print-banner.ts +++ b/apps/rest-api-server/src/scripts/print-banner.ts @@ -1,6 +1,4 @@ -import { env as apiEnv } from "@blobscan/api"; - -import { env as restEnv } from "../env"; +import { env } from "@blobscan/env"; function run() { console.log(" ____ _ _"); @@ -11,8 +9,7 @@ function run() { console.log("Blobscan REST API (EIP-4844 blob explorer) - blobscan.com"); console.log("====================================================\n"); - restEnv.display(); - apiEnv.display(); + env.display(); } run(); diff --git a/apps/rest-api-server/src/utils.ts b/apps/rest-api-server/src/utils.ts index 16e7a9c01..c84fe8a92 100644 --- a/apps/rest-api-server/src/utils.ts +++ b/apps/rest-api-server/src/utils.ts @@ -1,4 +1,4 @@ -import type { Environment } from "./env"; +import type { Environment } from "@blobscan/env"; export function getNetworkDencunForkSlot( networkName: Environment["NETWORK_NAME"] diff --git a/apps/web/package.json b/apps/web/package.json index cb463c2fb..4828b0dd6 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -18,6 +18,7 @@ "@blobscan/api": "workspace:^0.9.2", "@blobscan/blob-decoder": "workspace:^0.1.0", "@blobscan/dayjs": "workspace:^0.0.2", + "@blobscan/env": "workspace:^0.0.1", "@blobscan/open-telemetry": "workspace:^0.0.7", "@fontsource/inter": "^4.5.15", "@fontsource/public-sans": "^4.5.12", diff --git a/clis/blob-propagation-jobs-cli/package.json b/clis/blob-propagation-jobs-cli/package.json index ab9946369..7a23b2c59 100644 --- a/clis/blob-propagation-jobs-cli/package.json +++ b/clis/blob-propagation-jobs-cli/package.json @@ -19,6 +19,7 @@ "dependencies": { "@blobscan/blob-propagator": "workspace:^0.2.2", "@blobscan/dayjs": "workspace:^0.0.2", + "@blobscan/env": "workspace:^0.0.1", "@blobscan/db": "workspace:^0.7.0", "@blobscan/zod": "workspace:^0.1.0", "bullmq": "^4.13.2", diff --git a/clis/blob-propagation-jobs-cli/src/commands/create.ts b/clis/blob-propagation-jobs-cli/src/commands/create.ts index 7ff55cba9..54046cf77 100644 --- a/clis/blob-propagation-jobs-cli/src/commands/create.ts +++ b/clis/blob-propagation-jobs-cli/src/commands/create.ts @@ -7,10 +7,10 @@ import type { BlobPropagationQueue, } from "@blobscan/blob-propagator"; import { prisma } from "@blobscan/db"; +import { env } from "@blobscan/env"; import type { QueueHumanName } from "../Context"; import { context } from "../context-instance"; -import { env } from "../env"; import type { Command } from "../types"; import { blobHashOptionDef, diff --git a/clis/blob-propagation-jobs-cli/src/context-instance.ts b/clis/blob-propagation-jobs-cli/src/context-instance.ts index 0746f3394..f2dde64e9 100644 --- a/clis/blob-propagation-jobs-cli/src/context-instance.ts +++ b/clis/blob-propagation-jobs-cli/src/context-instance.ts @@ -1,8 +1,8 @@ import type { $Enums } from "@blobscan/db"; import { BlobStorage } from "@blobscan/db"; +import { env } from "@blobscan/env"; import { Context } from "./Context"; -import { env } from "./env"; const availableStorages: $Enums.BlobStorage[] = []; diff --git a/clis/blob-propagation-jobs-cli/src/env.ts b/clis/blob-propagation-jobs-cli/src/env.ts deleted file mode 100644 index f00373652..000000000 --- a/clis/blob-propagation-jobs-cli/src/env.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { - createEnv, - z, - presetEnvOptions, - booleanSchema, - prismaBatchOperationsMaxSizeSchema, -} from "@blobscan/zod"; - -export const env = createEnv({ - envOptions: { - server: { - REDIS_URI: z.string().default("redis://localhost:6379/1"), - - POSTGRES_STORAGE_ENABLED: booleanSchema.default("false"), - GOOGLE_STORAGE_ENABLED: booleanSchema.default("false"), - SWARM_STORAGE_ENABLED: booleanSchema.default("false"), - - PRISMA_BATCH_OPERATIONS_MAX_SIZE: prismaBatchOperationsMaxSizeSchema, - }, - - ...presetEnvOptions, - }, -}); diff --git a/clis/blob-propagation-jobs-cli/test/commands/retry.test.ts b/clis/blob-propagation-jobs-cli/test/commands/retry.test.ts index 96f72b03c..69d03ff50 100644 --- a/clis/blob-propagation-jobs-cli/test/commands/retry.test.ts +++ b/clis/blob-propagation-jobs-cli/test/commands/retry.test.ts @@ -4,10 +4,10 @@ import IORedis from "ioredis"; import { afterAll, beforeEach, describe, expect, it } from "vitest"; import type { BlobPropagationWorker } from "@blobscan/blob-propagator"; +import { env } from "@blobscan/env"; import { retry, retryCommandUsage } from "../../src/commands"; import { context } from "../../src/context-instance"; -import { env } from "../../src/env"; import { processJobsManually, argHelpTest, diff --git a/docker-compose.local.yml b/docker-compose.local.yml index dbb83fedd..493711150 100644 --- a/docker-compose.local.yml +++ b/docker-compose.local.yml @@ -6,7 +6,6 @@ services: extends: file: docker-compose.yml service: web - image: blossomlabs/blobscan:local build: . ports: - 5556:5556 # Prisma studio @@ -15,7 +14,6 @@ services: extends: file: docker-compose.yml service: api - image: blossomlabs/blobscan:local build: . depends_on: - postgres @@ -24,6 +22,8 @@ services: postgres: image: postgres:15 + profiles: + - dev ports: - 5432:5432 volumes: @@ -35,6 +35,8 @@ services: storage: image: fsouza/fake-gcs-server + profiles: + - dev ports: - "4443:4443" command: ["-scheme", "http", "-port", "4443", "-data", "/data"] @@ -46,6 +48,8 @@ services: redis: image: "redis:alpine" + profiles: + - dev command: redis-server /usr/local/etc/redis/redis.conf ports: - "6379:6379" @@ -59,6 +63,8 @@ services: collector: image: otel/opentelemetry-collector:0.23.0 + profiles: + - dev command: "--config /etc/otel-config.yaml" volumes: - ./otel-config.yaml:/etc/otel-config.yaml diff --git a/docker-compose.yml b/docker-compose.yml index 1fe9ffe28..b5e44064b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,7 +3,7 @@ version: "3.7" services: web: - image: blossomlabs/blobscan:stable + image: blossomlabs/blobscan:${BLOBSCAN_TAG} command: web restart: always ports: @@ -12,7 +12,7 @@ services: - ".env" api: - image: blossomlabs/blobscan:stable + image: blossomlabs/blobscan:${BLOBSCAN_TAG} command: api restart: always ports: @@ -23,6 +23,8 @@ services: timeout: 10s retries: 5 start_period: 5s + volumes: + - /tmp/blobscan-blobs:/tmp/blobscan-blobs env_file: - ".env" diff --git a/packages/api/package.json b/packages/api/package.json index b6ef1b71f..8f7b053c6 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -19,6 +19,7 @@ "@blobscan/blob-storage-manager": "workspace:^0.3.2", "@blobscan/dayjs": "workspace:^0.0.2", "@blobscan/db": "workspace:^0.7.0", + "@blobscan/env": "workspace:^0.0.1", "@blobscan/logger": "workspace:^0.1.0", "@blobscan/open-telemetry": "workspace:^0.0.7", "@blobscan/zod": "workspace:^0.1.0", diff --git a/packages/api/src/context.ts b/packages/api/src/context.ts index 1670d9419..0225ea1cc 100644 --- a/packages/api/src/context.ts +++ b/packages/api/src/context.ts @@ -11,8 +11,7 @@ import jwt from "jsonwebtoken"; import { getBlobPropagator } from "@blobscan/blob-propagator"; import { getBlobStorageManager } from "@blobscan/blob-storage-manager"; import { prisma } from "@blobscan/db"; - -import { env } from "./env"; +import { env } from "@blobscan/env"; export type CreateContextOptions = | NodeHTTPCreateContextFnOptions diff --git a/packages/api/src/env.ts b/packages/api/src/env.ts deleted file mode 100644 index 3080bb482..000000000 --- a/packages/api/src/env.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { env as blobPropagatorEnv } from "@blobscan/blob-propagator"; -import { env as blobStorageManagerEnv } from "@blobscan/blob-storage-manager"; -import { env as openTelemetryEnv } from "@blobscan/open-telemetry"; -import { - z, - createEnv, - presetEnvOptions, - nodeEnvSchema, - booleanSchema, - maskSensitiveData, -} from "@blobscan/zod"; - -export const env = createEnv({ - envOptions: { - server: { - CHAIN_ID: z.coerce.number().positive().default(1), - SECRET_KEY: z.string(), - NODE_ENV: nodeEnvSchema.optional(), - METRICS_ENABLED: booleanSchema.default("false"), - }, - - ...presetEnvOptions, - }, - display(env) { - console.log( - `API configuration: secretKey: ${maskSensitiveData(env.SECRET_KEY)}` - ); - blobPropagatorEnv.display(); - blobStorageManagerEnv.display(); - openTelemetryEnv.display(); - }, -}); diff --git a/packages/api/src/index.ts b/packages/api/src/index.ts index 9084aaabc..7a132ada9 100644 --- a/packages/api/src/index.ts +++ b/packages/api/src/index.ts @@ -11,7 +11,6 @@ export type { export * from "@trpc/server/adapters/express"; -export { env } from "./env"; export { gracefulShutdown } from "./graceful-shutdown"; export { metricsHandler } from "./instrumentation"; export type { diff --git a/packages/api/src/instrumentation.ts b/packages/api/src/instrumentation.ts index 49452234a..dd0f8bff3 100644 --- a/packages/api/src/instrumentation.ts +++ b/packages/api/src/instrumentation.ts @@ -5,10 +5,9 @@ import type { import { prisma } from "@blobscan/db"; import type { MetricsClient } from "@blobscan/db"; +import { env } from "@blobscan/env"; import { api, promRegister } from "@blobscan/open-telemetry"; -import { env } from "./env"; - const scopeName = "blobscan_api"; function hasMetricsClient( diff --git a/packages/api/src/routers/indexer/indexData.utils.ts b/packages/api/src/routers/indexer/indexData.utils.ts index b9631683c..29f585ef3 100644 --- a/packages/api/src/routers/indexer/indexData.utils.ts +++ b/packages/api/src/routers/indexer/indexData.utils.ts @@ -5,8 +5,8 @@ import type { WithoutTimestampFields, } from "@blobscan/db"; import { Prisma, Rollup } from "@blobscan/db"; +import { env } from "@blobscan/env"; -import { env } from "../../env"; import type { IndexDataFormattedInput } from "./indexData"; const MIN_BLOB_BASE_FEE = BigInt(1); diff --git a/packages/api/test/helpers.ts b/packages/api/test/helpers.ts index 7feb358da..64a0a2e62 100644 --- a/packages/api/test/helpers.ts +++ b/packages/api/test/helpers.ts @@ -8,8 +8,8 @@ import { describe, expect, it } from "vitest"; import { createBlobPropagator } from "@blobscan/blob-propagator/src/blob-propagator"; import type { Rollup } from "@blobscan/db"; +import { env } from "@blobscan/env"; -import { env } from "../src"; import { createTRPCContext } from "../src/context"; import type { ZodExpandEnum } from "../src/middlewares/withExpands"; import type { FiltersSchema } from "../src/middlewares/withFilters"; diff --git a/packages/blob-propagator/package.json b/packages/blob-propagator/package.json index a897b78a9..5b4eeb2aa 100644 --- a/packages/blob-propagator/package.json +++ b/packages/blob-propagator/package.json @@ -17,6 +17,7 @@ "dependencies": { "@blobscan/blob-storage-manager": "workspace:^0.3.2", "@blobscan/db": "workspace:^0.7.0", + "@blobscan/env": "workspace:^0.0.1", "@blobscan/logger": "workspace:^0.1.0", "@blobscan/open-telemetry": "workspace:^0.0.7", "@blobscan/zod": "workspace:^0.1.0", diff --git a/packages/blob-propagator/setup.ts b/packages/blob-propagator/setup.ts index 0d5ad2655..1322c6ff4 100644 --- a/packages/blob-propagator/setup.ts +++ b/packages/blob-propagator/setup.ts @@ -2,8 +2,9 @@ import { Queue } from "bullmq"; import IORedis from "ioredis"; import { afterAll } from "vitest"; +import { env } from "@blobscan/env"; + import { FINALIZER_WORKER_NAME, STORAGE_WORKER_NAMES } from "./src/constants"; -import { env } from "./src/env"; afterAll(async () => { const queues = [ diff --git a/packages/blob-propagator/src/blob-propagator.ts b/packages/blob-propagator/src/blob-propagator.ts index 1a34af069..a60a780ea 100644 --- a/packages/blob-propagator/src/blob-propagator.ts +++ b/packages/blob-propagator/src/blob-propagator.ts @@ -7,9 +7,9 @@ import { import type { BlobStorageManager } from "@blobscan/blob-storage-manager"; import { prisma } from "@blobscan/db"; import type { BlobscanPrismaClient } from "@blobscan/db"; +import { env } from "@blobscan/env"; import { BlobPropagator } from "./BlobPropagator"; -import { env } from "./env"; async function createBlobPropagator( blobStorageManager: BlobStorageManager, diff --git a/packages/blob-propagator/src/constants.ts b/packages/blob-propagator/src/constants.ts index d667de81a..c0297924e 100644 --- a/packages/blob-propagator/src/constants.ts +++ b/packages/blob-propagator/src/constants.ts @@ -1,8 +1,7 @@ import type { JobsOptions, WorkerOptions } from "bullmq"; import { $Enums } from "@blobscan/db"; - -import { env } from "./env"; +import { env } from "@blobscan/env"; export const STORAGE_WORKER_NAMES = Object.values($Enums.BlobStorage).reduce< Record<$Enums.BlobStorage, string> diff --git a/packages/blob-propagator/src/env.ts b/packages/blob-propagator/src/env.ts deleted file mode 100644 index 83e2c244d..000000000 --- a/packages/blob-propagator/src/env.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { - blobStorageSchema, - booleanSchema, - createEnv, - maskPassword, - presetEnvOptions, - z, -} from "@blobscan/zod"; - -export const env = createEnv({ - envOptions: { - server: { - REDIS_URI: z.string().default("redis://localhost:6379"), - - POSTGRES_STORAGE_ENABLED: booleanSchema.default("false"), - GOOGLE_STORAGE_ENABLED: booleanSchema.default("false"), - SWARM_STORAGE_ENABLED: booleanSchema.default("false"), - BLOB_PROPAGATOR_ENABLED: booleanSchema.default("false"), - BLOB_PROPAGATOR_TMP_BLOB_STORAGE: - blobStorageSchema.default("FILE_SYSTEM"), - BLOB_PROPAGATOR_COMPLETED_JOBS_AGE: z.coerce - .number() - .default(24 * 60 * 60), - BLOB_PROPAGATOR_FAILED_JOBS_AGE: z.coerce - .number() - .default(7 * 24 * 60 * 60), - FILE_SYSTEM_STORAGE_PATH: z.string().optional(), - TEST: booleanSchema.optional(), - }, - - ...presetEnvOptions, - }, - display(env) { - console.log( - `Blob propagator configuration: enabled=${ - env.BLOB_PROPAGATOR_ENABLED - } redisUri=${maskPassword(env.REDIS_URI)} temporalBlobStorage=${ - env.BLOB_PROPAGATOR_TMP_BLOB_STORAGE - } completedJobsAge=${ - env.BLOB_PROPAGATOR_COMPLETED_JOBS_AGE - } seconds failedJobsAge=${env.BLOB_PROPAGATOR_FAILED_JOBS_AGE} seconds` - ); - }, -}); - -export type EnvVars = typeof env; diff --git a/packages/blob-propagator/src/index.ts b/packages/blob-propagator/src/index.ts index 8e5c9ed1c..cff9f693a 100644 --- a/packages/blob-propagator/src/index.ts +++ b/packages/blob-propagator/src/index.ts @@ -1,6 +1,6 @@ -export type { EnvVars as Environment } from "./env"; +export type { Environment } from "@blobscan/env"; -export { env } from "./env"; +export { env } from "@blobscan/env"; export * from "./errors"; export { BlobPropagator } from "./BlobPropagator"; export { getBlobPropagator } from "./blob-propagator"; diff --git a/packages/blob-propagator/test/BlobPropagator.test.ts b/packages/blob-propagator/test/BlobPropagator.test.ts index bfda626ae..4d1a8228a 100644 --- a/packages/blob-propagator/test/BlobPropagator.test.ts +++ b/packages/blob-propagator/test/BlobPropagator.test.ts @@ -9,9 +9,9 @@ import { import { BlobStorageManager } from "@blobscan/blob-storage-manager"; import type { FileSystemStorage } from "@blobscan/blob-storage-manager"; import { prisma } from "@blobscan/db"; +import { env } from "@blobscan/env"; import { testValidError } from "@blobscan/test"; -import { env } from "../src"; import { BlobPropagator } from "../src/BlobPropagator"; import { BlobPropagatorCreationError, diff --git a/packages/blob-propagator/test/finalizer-worker.test.ts b/packages/blob-propagator/test/finalizer-worker.test.ts index f732a6fbe..eb6bc261f 100644 --- a/packages/blob-propagator/test/finalizer-worker.test.ts +++ b/packages/blob-propagator/test/finalizer-worker.test.ts @@ -9,9 +9,9 @@ import type { BlobStorage, BlobStorageManager, } from "@blobscan/blob-storage-manager"; +import { env } from "@blobscan/env"; import { fixtures } from "@blobscan/test"; -import { env } from "../src/env"; import type { Blob, BlobPropagationFinalizerJob } from "../src/types"; import { finalizerProcessor } from "../src/worker-processors"; diff --git a/packages/blob-propagator/test/storage-workers.test.ts b/packages/blob-propagator/test/storage-workers.test.ts index e32047a43..93d5653df 100644 --- a/packages/blob-propagator/test/storage-workers.test.ts +++ b/packages/blob-propagator/test/storage-workers.test.ts @@ -21,9 +21,9 @@ import type { } from "@blobscan/blob-storage-manager"; import { prisma } from "@blobscan/db"; import type { BlobscanPrismaClient, Blob as DBBlob } from "@blobscan/db"; +import { env } from "@blobscan/env"; import { STORAGE_WORKER_PROCESSORS } from "../src/BlobPropagator"; -import { env } from "../src/env"; import type { BlobPropagationJob, BlobPropagationWorkerParams, diff --git a/packages/blob-storage-manager/package.json b/packages/blob-storage-manager/package.json index 33d9a87c8..f54b1e578 100644 --- a/packages/blob-storage-manager/package.json +++ b/packages/blob-storage-manager/package.json @@ -16,6 +16,7 @@ }, "dependencies": { "@blobscan/db": "workspace:^0.7.0", + "@blobscan/env": "workspace:^0.0.1", "@blobscan/logger": "workspace:^0.1.0", "@blobscan/open-telemetry": "workspace:^0.0.7", "@blobscan/zod": "workspace:^0.1.0", diff --git a/packages/blob-storage-manager/src/blob-storage-manager.ts b/packages/blob-storage-manager/src/blob-storage-manager.ts index 57936ab73..51b6f5402 100644 --- a/packages/blob-storage-manager/src/blob-storage-manager.ts +++ b/packages/blob-storage-manager/src/blob-storage-manager.ts @@ -1,7 +1,8 @@ +import { env } from "@blobscan/env"; +import type { Environment } from "@blobscan/env"; + import type { BlobStorage } from "./BlobStorage"; import { BlobStorageManager } from "./BlobStorageManager"; -import { env } from "./env"; -import type { Environment } from "./env"; import type { BlobStorageName } from "./types"; import { BLOB_STORAGE_NAMES, createStorageFromEnv } from "./utils"; diff --git a/packages/blob-storage-manager/src/env.ts b/packages/blob-storage-manager/src/env.ts deleted file mode 100644 index f9f79b5a3..000000000 --- a/packages/blob-storage-manager/src/env.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { - z, - booleanSchema, - createEnv, - presetEnvOptions, - maskSensitiveData, - conditionalRequiredSchema, -} from "@blobscan/zod"; - -import type { BlobStorageName } from "./types"; - -export function requiredStorageConfigSchema( - storageName: BlobStorageName, - schema: T -) { - return conditionalRequiredSchema( - schema, - process.env[`${storageName}_STORAGE_ENABLED`], - "true", - `This configuration variable is required when ${storageName} storage is enabled.` - ); -} - -export function parseEnv() { - return createEnv({ - envOptions: { - server: { - BEE_DEBUG_ENDPOINT: z.string().url().optional(), - BEE_ENDPOINT: requiredStorageConfigSchema("SWARM", z.string().url()), - CHAIN_ID: z.coerce.number().positive().default(1), - FILE_SYSTEM_STORAGE_ENABLED: booleanSchema.default("false"), - FILE_SYSTEM_STORAGE_PATH: z.string().default("/tmp/blobscan-blobs"), - GOOGLE_STORAGE_BUCKET_NAME: requiredStorageConfigSchema( - "GOOGLE", - z.string() - ), - GOOGLE_STORAGE_PROJECT_ID: z.string().optional(), - GOOGLE_SERVICE_KEY: z.string().optional(), - GOOGLE_STORAGE_API_ENDPOINT: z.string().optional(), - GOOGLE_STORAGE_ENABLED: booleanSchema.default("false"), - POSTGRES_STORAGE_ENABLED: booleanSchema.default("true"), - SWARM_STORAGE_ENABLED: booleanSchema.default("false"), - }, - - ...presetEnvOptions, - }, - display(env) { - console.log( - `Blob storage manager configuration: chainId=${env.CHAIN_ID}, file_system=${env.FILE_SYSTEM_STORAGE_ENABLED} postgres=${env.POSTGRES_STORAGE_ENABLED}, gcs=${env.GOOGLE_STORAGE_ENABLED}, swarm=${env.SWARM_STORAGE_ENABLED}` - ); - - if (env.GOOGLE_STORAGE_ENABLED) { - console.log( - `GCS configuration: bucketName=${ - env.GOOGLE_STORAGE_BUCKET_NAME - }, projectId=${maskSensitiveData( - env.GOOGLE_STORAGE_PROJECT_ID - )}, apiEndpoint=${env.GOOGLE_STORAGE_API_ENDPOINT}` - ); - } - - if (env.SWARM_STORAGE_ENABLED) { - console.log( - `Swarm configuration: beeEndpoint=${env.BEE_ENDPOINT}, debugEndpoint=${env.BEE_DEBUG_ENDPOINT}` - ); - } - - if (env.FILE_SYSTEM_STORAGE_ENABLED) { - console.log( - `File system configuration: blobDirPath=${env.FILE_SYSTEM_STORAGE_PATH}` - ); - } - }, - }); -} -export const env = parseEnv(); - -export type Environment = typeof env; diff --git a/packages/blob-storage-manager/src/index.ts b/packages/blob-storage-manager/src/index.ts index 6e044587d..56c045ba1 100644 --- a/packages/blob-storage-manager/src/index.ts +++ b/packages/blob-storage-manager/src/index.ts @@ -1,8 +1,6 @@ export { BlobStorage } from "./BlobStorage"; export { BlobStorageManager } from "./BlobStorageManager"; export * from "./blob-storage-manager"; -export { env } from "./env"; -export type { Environment } from "./env"; export * from "./errors"; export * from "./storages"; export * from "./types"; diff --git a/packages/blob-storage-manager/src/utils/storage.ts b/packages/blob-storage-manager/src/utils/storage.ts index e2ad88201..ccd5ff802 100644 --- a/packages/blob-storage-manager/src/utils/storage.ts +++ b/packages/blob-storage-manager/src/utils/storage.ts @@ -1,7 +1,7 @@ import { $Enums, prisma } from "@blobscan/db"; +import { env } from "@blobscan/env"; import type { BlobStorage } from "../BlobStorage"; -import { env } from "../env"; import { FileSystemStorage, GoogleStorage, diff --git a/packages/blob-storage-manager/test/BlobStorageManager.test.ts b/packages/blob-storage-manager/test/BlobStorageManager.test.ts index 5b0f5bcfb..96ae68c4d 100644 --- a/packages/blob-storage-manager/test/BlobStorageManager.test.ts +++ b/packages/blob-storage-manager/test/BlobStorageManager.test.ts @@ -3,10 +3,10 @@ import type { DeepMockProxy } from "vitest-mock-extended"; import { mockDeep } from "vitest-mock-extended"; import { prisma } from "@blobscan/db"; +import { env } from "@blobscan/env"; import { testValidError } from "@blobscan/test"; import { BlobStorageManager } from "../src/BlobStorageManager"; -import { env } from "../src/env"; import { BlobStorageError, BlobStorageManagerError } from "../src/errors"; import { GoogleStorage, PostgresStorage, SwarmStorage } from "../src/storages"; import { NEW_BLOB_DATA, NEW_BLOB_HASH } from "./fixtures"; diff --git a/packages/blob-storage-manager/test/storages/FileSystemStorage.test.ts b/packages/blob-storage-manager/test/storages/FileSystemStorage.test.ts index a9ba0fc77..d2f06e026 100644 --- a/packages/blob-storage-manager/test/storages/FileSystemStorage.test.ts +++ b/packages/blob-storage-manager/test/storages/FileSystemStorage.test.ts @@ -2,9 +2,9 @@ import fs from "fs"; import path from "path"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; +import { env } from "@blobscan/env"; import { testValidError } from "@blobscan/test"; -import { env } from "../../src/env"; import { BlobStorageError } from "../../src/errors"; import { FileSystemStorage } from "../../src/storages/FileSystemStorage"; import { NEW_BLOB_DATA, NEW_BLOB_HASH, NEW_BLOB_FILE_URI } from "../fixtures"; diff --git a/packages/blob-storage-manager/test/storages/GoogleStorage.test.ts b/packages/blob-storage-manager/test/storages/GoogleStorage.test.ts index f08c05093..36f075b20 100644 --- a/packages/blob-storage-manager/test/storages/GoogleStorage.test.ts +++ b/packages/blob-storage-manager/test/storages/GoogleStorage.test.ts @@ -2,8 +2,9 @@ import type { Storage } from "@google-cloud/storage"; import { beforeAll, describe, expect, it } from "vitest"; import { testValidError } from "@blobscan/test"; +import { env } from "@blobscan/env"; -import { GoogleStorage, env } from "../../src"; +import { GoogleStorage } from "../../src"; import { BlobStorageError } from "../../src/errors"; import type { GoogleStorageConfig } from "../../src/storages"; import { NEW_BLOB_DATA, NEW_BLOB_HASH } from "../fixtures"; diff --git a/packages/blob-storage-manager/test/storages/PostgresStorage.test.ts b/packages/blob-storage-manager/test/storages/PostgresStorage.test.ts index c70d2ddf7..90c12b6f6 100644 --- a/packages/blob-storage-manager/test/storages/PostgresStorage.test.ts +++ b/packages/blob-storage-manager/test/storages/PostgresStorage.test.ts @@ -3,8 +3,9 @@ import { afterEach, beforeAll, describe, expect, it, vi } from "vitest"; import { prisma } from "@blobscan/db"; import type { BlobscanPrismaClient, PrismaClient } from "@blobscan/db"; import { testValidError } from "@blobscan/test"; +import { env } from "@blobscan/env"; -import { PostgresStorage, env } from "../../src"; +import { PostgresStorage } from "../../src"; import { BlobStorageError } from "../../src/errors"; import { NEW_BLOB_HASH, HEX_DATA } from "../fixtures"; diff --git a/packages/blob-storage-manager/test/storages/SwarmStorage.test.ts b/packages/blob-storage-manager/test/storages/SwarmStorage.test.ts index 9cfe9f21d..3d57e6244 100644 --- a/packages/blob-storage-manager/test/storages/SwarmStorage.test.ts +++ b/packages/blob-storage-manager/test/storages/SwarmStorage.test.ts @@ -1,9 +1,9 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; import { prisma } from "@blobscan/db"; +import { env } from "@blobscan/env"; import { fixtures, testValidError } from "@blobscan/test"; -import { env } from "../../src/env"; import { SwarmStorage } from "../../src/storages/SwarmStorage"; import type { SwarmStorageConfig } from "../../src/storages/SwarmStorage"; import { NEW_BLOB_DATA, NEW_BLOB_HASH, SWARM_REFERENCE } from "../fixtures"; diff --git a/packages/env/index.ts b/packages/env/index.ts new file mode 100644 index 000000000..a5585cb13 --- /dev/null +++ b/packages/env/index.ts @@ -0,0 +1,151 @@ +import type { BlobStorageName } from "@blobscan/blob-storage-manager"; +import { + z, + blobStorageSchema, + booleanSchema, + conditionalRequiredSchema, + createEnv, + presetEnvOptions, + nodeEnvSchema, + maskPassword, + networkSchema, + maskSensitiveData, + prismaBatchOperationsMaxSizeSchema, +} from "@blobscan/zod"; + +export function requiredStorageConfigSchema( + storageName: BlobStorageName, + schema: T +) { + return conditionalRequiredSchema( + schema, + process.env[`${storageName}_STORAGE_ENABLED`], + "true", + `This configuration variable is required when ${storageName} storage is enabled.` + ); +} + +export const env = createEnv({ + envOptions: { + server: { + BEE_DEBUG_ENDPOINT: z.string().url().optional(), + BEE_ENDPOINT: requiredStorageConfigSchema("SWARM", z.string().url()), + BLOBSCAN_API_BASE_URL: z + .string() + .url() + .default("https://api.blobscan.com"), + BLOBSCAN_API_PORT: z.coerce.number().positive().default(3001), + BLOB_PROPAGATOR_ENABLED: booleanSchema.default("false"), + BLOB_PROPAGATOR_TMP_BLOB_STORAGE: + blobStorageSchema.default("FILE_SYSTEM"), + BLOB_PROPAGATOR_COMPLETED_JOBS_AGE: z.coerce + .number() + .default(24 * 60 * 60), + BLOB_PROPAGATOR_FAILED_JOBS_AGE: z.coerce + .number() + .default(7 * 24 * 60 * 60), + CHAIN_ID: z.coerce.number().positive().default(1), + DENCUN_FORK_SLOT: z.coerce.number().optional(), + FILE_SYSTEM_STORAGE_ENABLED: booleanSchema.default("false"), + FILE_SYSTEM_STORAGE_PATH: z.string().default("/tmp/blobscan-blobs"), + GOOGLE_STORAGE_API_ENDPOINT: z.string().optional(), + GOOGLE_STORAGE_BUCKET_NAME: requiredStorageConfigSchema( + "GOOGLE", + z.string() + ), + GOOGLE_STORAGE_ENABLED: booleanSchema.default("false"), + GOOGLE_STORAGE_PROJECT_ID: z.string().optional(), + GOOGLE_SERVICE_KEY: z.string().optional(), + LOG_LEVEL: z + .enum(["debug", "http", "info", "warn", "error"]) + .default("http"), + METRICS_ENABLED: booleanSchema.default("false"), + NETWORK_NAME: networkSchema.default("mainnet"), + NODE_ENV: nodeEnvSchema.optional(), + OTEL_EXPORTER_OTLP_PROTOCOL: z + .enum(["grpc", "http/protobuf", "http/json"]) + .default("http/protobuf"), + OTEL_EXPORTER_OTLP_ENDPOINT: z + .string() + .url() + .default("http://localhost:4318"), + OTEL_DIAG_ENABLED: z.boolean().default(false), + OTLP_AUTH_USERNAME: z.coerce.string().optional(), + OTLP_AUTH_PASSWORD: z.string().optional(), + POSTGRES_STORAGE_ENABLED: booleanSchema.default("false"), + PRISMA_BATCH_OPERATIONS_MAX_SIZE: prismaBatchOperationsMaxSizeSchema, + REDIS_URI: z.string().default("redis://localhost:6379"), + STATS_SYNCER_DAILY_CRON_PATTERN: z.string().default("30 0 * * * *"), + STATS_SYNCER_OVERALL_CRON_PATTERN: z.string().default("*/15 * * * *"), + SECRET_KEY: z.string(), + SENTRY_DSN_API: z.string().optional(), + SWARM_STORAGE_ENABLED: booleanSchema.default("false"), + TEST: booleanSchema.optional(), + TRACES_ENABLED: booleanSchema.default("false"), + }, + + ...presetEnvOptions, + }, + display(env) { + console.log( + `API configuration: secretKey: ${maskSensitiveData( + env.SECRET_KEY + )} Blob propagator configuration: enabled=${ + env.BLOB_PROPAGATOR_ENABLED + } redisUri=${maskPassword(env.REDIS_URI)} temporalBlobStorage=${ + env.BLOB_PROPAGATOR_TMP_BLOB_STORAGE + } completedJobsAge=${ + env.BLOB_PROPAGATOR_COMPLETED_JOBS_AGE + } seconds failedJobsAge=${ + env.BLOB_PROPAGATOR_FAILED_JOBS_AGE + } seconds Configuration: network=${ + env.NETWORK_NAME + } sentryEnabled=${!!env.SENTRY_DSN_API} metrics=${ + env.METRICS_ENABLED + } traces=${env.TRACES_ENABLED} port=${ + env.BLOBSCAN_API_PORT + } redisUri=${maskPassword(env.REDIS_URI)} dailyStatsCron=${ + env.STATS_SYNCER_DAILY_CRON_PATTERN + } overallStatsCron=${ + env.STATS_SYNCER_OVERALL_CRON_PATTERN + } dencunForkSlot=${env.DENCUN_FORK_SLOT ?? "auto"}` + ); + + console.log( + `Blob storage manager configuration: chainId=${env.CHAIN_ID}, file_system=${env.FILE_SYSTEM_STORAGE_ENABLED} postgres=${env.POSTGRES_STORAGE_ENABLED}, gcs=${env.GOOGLE_STORAGE_ENABLED}, swarm=${env.SWARM_STORAGE_ENABLED}` + ); + + if (env.GOOGLE_STORAGE_ENABLED) { + console.log( + `GCS configuration: bucketName=${ + env.GOOGLE_STORAGE_BUCKET_NAME + }, projectId=${maskSensitiveData( + env.GOOGLE_STORAGE_PROJECT_ID + )}, apiEndpoint=${env.GOOGLE_STORAGE_API_ENDPOINT}` + ); + } + + if (env.SWARM_STORAGE_ENABLED) { + console.log( + `Swarm configuration: beeEndpoint=${env.BEE_ENDPOINT}, debugEndpoint=${env.BEE_DEBUG_ENDPOINT}` + ); + } + + if (env.FILE_SYSTEM_STORAGE_ENABLED) { + console.log( + `File system configuration: blobDirPath=${env.FILE_SYSTEM_STORAGE_PATH}` + ); + } + console.log( + `Otel configuration: protocol=${ + env.OTEL_EXPORTER_OTLP_PROTOCOL + } exporterEndpoint=${maskSensitiveData( + env.OTEL_EXPORTER_OTLP_ENDPOINT + )} username=${env.OTLP_AUTH_USERNAME} password=${maskSensitiveData( + env.OTLP_AUTH_PASSWORD + )} diagEnabled=${env.OTEL_DIAG_ENABLED}` + ); + }, +}); + +export type Environment = typeof env; diff --git a/packages/env/package.json b/packages/env/package.json new file mode 100644 index 000000000..df82e6d61 --- /dev/null +++ b/packages/env/package.json @@ -0,0 +1,23 @@ +{ + "name": "@blobscan/env", + "version": "0.0.1", + "private": true, + "main": "./index.ts", + "types": "./index.ts", + "license": "MIT", + "scripts": { + "clean": "rm -rf .turbo node_modules", + "lint": "eslint .", + "lint:fix": "pnpm lint --fix", + "type-check": "tsc --noEmit" + }, + "dependencies": { + "@blobscan/zod": "workspace:^0.1.0" + }, + "eslintConfig": { + "root": true, + "extends": [ + "@blobscan/eslint-config/base" + ] + } +} diff --git a/packages/env/tsconfig.json b/packages/env/tsconfig.json new file mode 100644 index 000000000..40149e193 --- /dev/null +++ b/packages/env/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "@blobscan/tsconfig/base.production.json", + "include": [ + "src/**/*.ts", + "scripts/**/*.ts", + "test/**/*.ts", + "vitest.config.ts", + "index.ts" + ] +} diff --git a/packages/env/vitest.config.ts b/packages/env/vitest.config.ts new file mode 100644 index 000000000..8fdeaf1f5 --- /dev/null +++ b/packages/env/vitest.config.ts @@ -0,0 +1,5 @@ +import { defineProject } from "vitest/config"; + +import { sharedProjectConfig } from "../../vitest.shared"; + +export default defineProject(sharedProjectConfig); diff --git a/packages/logger/package.json b/packages/logger/package.json index d5ced39af..0d8fad559 100644 --- a/packages/logger/package.json +++ b/packages/logger/package.json @@ -12,6 +12,7 @@ "type-check": "tsc --noEmit" }, "dependencies": { + "@blobscan/env": "workspace:^0.0.1", "@blobscan/zod": "workspace:^0.1.0", "winston": "^3.10.0" }, diff --git a/packages/logger/src/env.ts b/packages/logger/src/env.ts deleted file mode 100644 index 1d6c6479b..000000000 --- a/packages/logger/src/env.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { booleanSchema, createEnv, presetEnvOptions, z } from "@blobscan/zod"; - -export const env = createEnv({ - envOptions: { - server: { - LOG_LEVEL: z - .enum(["debug", "http", "info", "warn", "error"]) - .default("http"), - TEST: booleanSchema.optional(), - }, - - ...presetEnvOptions, - }, -}); diff --git a/packages/logger/src/index.ts b/packages/logger/src/index.ts index 2788fde8c..de4057cd6 100644 --- a/packages/logger/src/index.ts +++ b/packages/logger/src/index.ts @@ -1,6 +1,6 @@ import winston from "winston"; -import { env } from "./env"; +import { env } from "@blobscan/env"; function buildErrorCause(err: Error) { let msg = ` - Cause: ${err.message}`; diff --git a/packages/open-telemetry/package.json b/packages/open-telemetry/package.json index fc7ae7093..9913586ac 100644 --- a/packages/open-telemetry/package.json +++ b/packages/open-telemetry/package.json @@ -12,6 +12,7 @@ "type-check": "tsc --noEmit" }, "dependencies": { + "@blobscan/env": "workspace:^0.0.1", "@blobscan/logger": "workspace:^0.1.0", "@blobscan/zod": "workspace:^0.1.0", "@opentelemetry/exporter-metrics-otlp-proto": "^0.41.1", diff --git a/packages/open-telemetry/src/env.ts b/packages/open-telemetry/src/env.ts deleted file mode 100644 index e23add92e..000000000 --- a/packages/open-telemetry/src/env.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { - createEnv, - maskSensitiveData, - nodeEnvSchema, - presetEnvOptions, - z, -} from "@blobscan/zod"; - -export const env = createEnv({ - envOptions: { - server: { - OTEL_EXPORTER_OTLP_PROTOCOL: z - .enum(["grpc", "http/protobuf", "http/json"]) - .default("http/protobuf"), - OTEL_EXPORTER_OTLP_ENDPOINT: z - .string() - .url() - .default("http://localhost:4318"), - OTEL_DIAG_ENABLED: z.boolean().default(false), - OTLP_AUTH_USERNAME: z.coerce.string().optional(), - OTLP_AUTH_PASSWORD: z.string().optional(), - NODE_ENV: nodeEnvSchema.optional(), - }, - - ...presetEnvOptions, - }, - display(env) { - console.log( - `Otel configuration: protocol=${ - env.OTEL_EXPORTER_OTLP_PROTOCOL - } exporterEndpoint=${maskSensitiveData( - env.OTEL_EXPORTER_OTLP_ENDPOINT - )} username=${env.OTLP_AUTH_USERNAME} password=${maskSensitiveData( - env.OTLP_AUTH_PASSWORD - )} diagEnabled=${env.OTEL_DIAG_ENABLED}` - ); - }, -}); diff --git a/packages/open-telemetry/src/index.ts b/packages/open-telemetry/src/index.ts index 511046c73..78d13e7a0 100644 --- a/packages/open-telemetry/src/index.ts +++ b/packages/open-telemetry/src/index.ts @@ -5,5 +5,4 @@ export { } from "@opentelemetry/semantic-conventions"; export { collectDefaultMetrics, register as promRegister } from "prom-client"; -export { env } from "./env"; export { setUpOpenTelemetry } from "./sdk"; diff --git a/packages/open-telemetry/src/sdk.ts b/packages/open-telemetry/src/sdk.ts index 1daa2688f..b98e2aa29 100644 --- a/packages/open-telemetry/src/sdk.ts +++ b/packages/open-telemetry/src/sdk.ts @@ -6,10 +6,9 @@ import { NodeSDK, resources, api } from "@opentelemetry/sdk-node"; import { SemanticResourceAttributes } from "@opentelemetry/semantic-conventions"; import { PrismaInstrumentation } from "@prisma/instrumentation"; +import { env } from "@blobscan/env"; import { logger } from "@blobscan/logger"; -import { env } from "./env"; - if (env.OTEL_DIAG_ENABLED) { api.diag.setLogger(new api.DiagConsoleLogger(), api.DiagLogLevel.INFO); } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e9cdf6713..c53db7236 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -165,6 +165,9 @@ importers: '@blobscan/api': specifier: workspace:^0.9.0 version: link:../../packages/api + '@blobscan/env': + specifier: workspace:^0.0.1 + version: link:../../packages/env '@blobscan/logger': specifier: workspace:^0.1.0 version: link:../../packages/logger @@ -226,6 +229,9 @@ importers: '@blobscan/dayjs': specifier: workspace:^0.0.2 version: link:../../packages/dayjs + '@blobscan/env': + specifier: workspace:^0.0.1 + version: link:../../packages/env '@blobscan/open-telemetry': specifier: workspace:^0.0.7 version: link:../../packages/open-telemetry @@ -362,6 +368,9 @@ importers: '@blobscan/db': specifier: workspace:^0.7.0 version: link:../../packages/db + '@blobscan/env': + specifier: workspace:^0.0.1 + version: link:../../packages/env '@blobscan/zod': specifier: workspace:^0.1.0 version: link:../../packages/zod @@ -407,6 +416,9 @@ importers: '@blobscan/db': specifier: workspace:^0.7.0 version: link:../db + '@blobscan/env': + specifier: workspace:^0.0.1 + version: link:../env '@blobscan/logger': specifier: workspace:^0.1.0 version: link:../logger @@ -477,6 +489,9 @@ importers: '@blobscan/db': specifier: workspace:^0.7.0 version: link:../db + '@blobscan/env': + specifier: workspace:^0.0.1 + version: link:../env '@blobscan/logger': specifier: workspace:^0.1.0 version: link:../logger @@ -498,6 +513,9 @@ importers: '@blobscan/db': specifier: workspace:^0.7.0 version: link:../db + '@blobscan/env': + specifier: workspace:^0.0.1 + version: link:../env '@blobscan/logger': specifier: workspace:^0.1.0 version: link:../logger @@ -545,8 +563,17 @@ importers: specifier: ^5.5.2 version: 5.13.0 + packages/env: + dependencies: + '@blobscan/zod': + specifier: workspace:^0.1.0 + version: link:../zod + packages/logger: dependencies: + '@blobscan/env': + specifier: workspace:^0.0.1 + version: link:../env '@blobscan/zod': specifier: workspace:^0.1.0 version: link:../zod @@ -556,6 +583,9 @@ importers: packages/open-telemetry: dependencies: + '@blobscan/env': + specifier: workspace:^0.0.1 + version: link:../env '@blobscan/logger': specifier: workspace:^0.1.0 version: link:../logger