@chrono-os/env-schema
v0.1.1
Published
Helpers Zod para validação de env vars em services Chrono — optionalStr/optionalUrl (string vazia vira undefined), requireIfPresent (regra condicional de segurança), parseEnvOrExit (boot Fastify) e validateForNest (ConfigModule.forRoot).
Downloads
257
Maintainers
Readme
@chrono-os/env-schema
Helpers Zod para validar env vars em services Chrono. Extraído do Plano 3B.7: optionalStr/optionalUrl viviam byte-idênticos (com o mesmo comentário) em 7 backends do workspace Naírio — este pacote é a fonte única.
Install
yarn add @chrono-os/env-schema zodzod é peer dependency (^3.23.0 || ^4.0.0) — este pacote não fixa a versão que
o consumer usa. A suíte de testes roda contra zod@^3.23.8 (a maioria dos
consumers atuais) e foi validada manualmente contra zod@^4.4.3 (bpmn-saas-backend
usa v4) antes do release 0.1.0 — typecheck + build + os 16 testes passam nas
duas majors sem alterar código.
Uso
Padrão Fastify — parse no topo do módulo + process.exit(1)
import { z } from 'zod'
import { optionalStr, optionalUrl, requireIfPresent, parseEnvOrExit } from '@chrono-os/env-schema'
const baseSchema = z.object({
NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
DATABASE_URL: z.string().url(),
ADMIN_SECRET: optionalStr(32),
SENTRY_DSN: optionalUrl(),
MP_ACCESS_TOKEN: optionalStr(),
MP_WEBHOOK_SECRET: optionalStr(16),
})
const schema = requireIfPresent(baseSchema, 'MP_ACCESS_TOKEN', 'MP_WEBHOOK_SECRET')
export const env = parseEnvOrExit(schema, process.env)Padrão NestJS — ConfigModule.forRoot({ validate })
import { z } from 'zod'
import { optionalStr, validateForNest } from '@chrono-os/env-schema'
export const envSchema = z.object({
DATABASE_URL: z.string().url(),
JWT_SECRET: z.string().min(32),
DASHBOARD_SERVICE_TOKEN: optionalStr(32),
})
export const validateEnv = validateForNest(envSchema)// app.module.ts
ConfigModule.forRoot({ validate: validateEnv })parseEnvOrExit chama process.exit(1) (padrão Fastify — o processo aborta cedo, fora do controle do framework). validateForNest lança Error em vez disso, porque o Nest não usa process.exit para interromper o bootstrap.
API
optionalStr(min = 1)— schema Zod: string vazia ("") viraundefinedANTES de validar.min(min). Sem isso, uma var opcional setada em branco no Coolify (ou.env.examplecom linhaFOO=) derruba o boot com"String must contain..."numa var que a própria documentação jura ser opcional.optionalUrl()— idem, para.url().requireIfPresent(schema, presentField, requiredField, message?)— recebe umz.object({...})e devolve o schema com um.superRefine: sepresentFieldestá definido (nãoundefined/null/""),requiredFieldtambém precisa estar, senão adiciona issue no path derequiredField. Generaliza a regra "se existe token de pagamento, exige webhook secret" (implementada à mão em SVA e MeResponda).parseEnvOrExit(schema, env)—safeParse; se inválido, imprime as chaves inválidas (nome + código do erro — nunca o valor, para não vazar secret em log) eprocess.exit(1); se válido, devolve os dados tipados.validateForNest(schema)— devolve a função queConfigModule.forRoot({ validate })espera: mesma validação, mas lançaErrorem vez deprocess.exit.
optionalInt/booleanStr — NÃO incluídos
Nenhuma das cópias reais lidas (7 backends do Naírio + SVA/Legaris/MeResponda) tem um helper nomeado equivalente a optionalInt ou booleanStr. O que existe são casos pontuais resolvidos inline com z.preprocess (ex.: PORT_NEST em calculadora-institucional-backend, GHL_ENVIO_AUTOMATICO em painel-conteudo-backend) ou com z.enum(['true', 'false']) direto no schema — nenhum dos dois duplicado o bastante para justificar extração agora. Se um padrão nomeado surgir em 3+ lugares, adicionar em versão minor.
Versionamento
Keep a Changelog + SemVer. Pacote pré-1.0: minor pode trazer mudança de comportamento.
Origem
Plano 3B.7 do ecossistema @chrono-os/* — extração de calculadora-backend/src/config/env.ts e mais 6 cópias (ver CHANGELOG).
