Canal de erros tipados
Toda falha do SignatureKit é um SignatureKitError no canal de erros do Effect — discrimine pelo code com Effect.catchTag/catchIf, sem throw.
Toda falha no core, no signer A1 e nos formatos é um SignatureKitError no canal de erros do Effect — não uma exceção lançada com throw. O tipo de retorno a carrega (Effect<SignatureArtifact, SignatureKitError>), então o tratamento é exaustivo e verificado pelo compilador: você discrimina pelo code, e tudo o que não tratar continua se propagando, tipado.
Canal de erros tipados
As falhas não são lançadas com throw: elas vivem no canal de erros do Effect como um SignatureKitError com um
code literal. O compilador obriga você a tratá-las — ou a deixá-las se propagar, tipadas — em vez de descobri-las em
tempo de execução.
O formato do erro
SignatureKitError é uma classe com um _tag fixo igual a "SignatureKitError". Seus campos:
code— um dos 21 literais"signature-kit.*"do catálogo.retryable— um booleano decidido no ponto da falha, não fixo por code. O mesmocodepode chegar comretryable: trueem um ponto de chamada efalseem outro.reason?— uma mensagem contextual opcional para logs e telemetria. A cópia de UI deve usarcode, não interpretar esse texto.operation?— umastringsimples com o nome da operação em que a falha ocorreu, útil para logging e telemetria.schemaName?— o nome do schema que falhou ao decodificar, quando a falha veio deSchema.decodeUnknownEffect.issueMessage?— o texto formatado do issue de decode (String(issue)) dessa falha de schema.provider?— o nome do provedor remoto, em falhas de API de provedor (Clicksign, Assinafy, ZapSign, DocuSeal, Documenso).status?— o status HTTP upstream, quando a falha veio de uma resposta HTTP de provedor.retryAfterEpochSeconds?— uma dica em epoch-seconds de quando tentar novamente, quando o upstream forneceu uma (por exemplo, uma resposta de rate limit).get message()— a mensagem padrão em inglês para o code, oureasonnos codes marcados como editable.
import type { Effect } from "effect"
import type { SignatureArtifact, SignatureKitError, SignatureKitErrorCode } from "@signature-kit/signatures"
// The error channel is typed: each failure is a SignatureKitError.
declare const sign: Effect.Effect<SignatureArtifact, SignatureKitError>
// _tag: "SignatureKitError"
class SignatureKitError {
readonly code: SignatureKitErrorCode // 21 "signature-kit.*" literals
readonly retryable: boolean // decided at the point of failure
readonly reason?: string // contextual message for logs
readonly operation?: string
readonly schemaName?: string
readonly issueMessage?: string
readonly provider?: string
readonly status?: number
readonly retryAfterEpochSeconds?: number
get message(): string // default per code, or reason for editable codes
}Não trate retryable como uma propriedade do code. Sempre leia error.retryable do valor recebido — só aquele
ponto de chamada sabe se a falha vale uma nova tentativa.
Tratamento por code
A união de erros tem uma única tag, então Effect.catchTag("SignatureKitError", ...) entra no canal e você discrimina pelo error.code. Um switch sobre code é exaustivo: o compilador exige todos os 21 literais.
import { Effect } from "effect"
import { a1SignaturesLayer } from "@signature-kit/a1/signer"
import { signatures } from "@signature-kit/signatures"
const program = Effect.gen(function* () {
return yield* signatures.sign({ content, algorithm: "rsa-sha256" })
}).pipe(Effect.provide(a1SignaturesLayer({ pfx, password })))
// The error union is a single tag — discriminate by code, not by class.
const handled = program.pipe(
Effect.catchTag("SignatureKitError", (error) => {
switch (error.code) {
case "signature-kit.WRONG_PASSWORD":
return Effect.fail("Incorrect certificate password — ask for the password again.")
case "signature-kit.SIGN_FAILED":
// retryable is decided at the point of failure, not fixed per code.
return error.retryable ? program : Effect.fail(error.message)
default:
return Effect.fail(error.message)
}
}),
)Para interceptar uma falha específica e deixar o restante se propagar, use Effect.catchIf com um predicado sobre _tag e code:
import { Effect } from "effect"
import type { SignatureKitError } from "@signature-kit/signatures"
// Filter only the failure this call site knows how to handle; the rest propagate, typed.
const recovered = program.pipe(
Effect.catchIf(
(error): error is SignatureKitError =>
error._tag === "SignatureKitError" && error.code === "signature-kit.WRONG_PASSWORD",
(error) =>
Effect.logWarning(`signing aborted at ${error.operation ?? "sign"}: ${error.message}`),
),
)Em handlers de retry, decida com base na combinação de error.code + error.retryable. Por exemplo, refaça uma
signature-kit.SIGN_FAILED apenas quando error.retryable for true.
Catálogo de erros
Todos os 21 codes de SignatureKitError e suas mensagens padrão. Os codes marcados como editable
resolvem para reason ?? default em message. Cada linha é ancorável como #err-<CODE>
(por exemplo #err-WRONG_PASSWORD), de modo que outras páginas façam deep link
direto para um code.
signature-kit.EMPTY_FILECertificate file is empty (0 bytes).signature-kit.INVALID_FORMATThe file is not a PKCS#12 (.pfx/.p12) certificate.· editablesignature-kit.INVALID_INPUTInvalid signing input.· editablesignature-kit.WRONG_PASSWORDWrong certificate password.signature-kit.CERTIFICATE_EXPIREDCertificate expired.signature-kit.CERTIFICATE_NOT_YET_VALIDCertificate is not valid yet.signature-kit.MISSING_BR_IDENTIFIERCertificate does not contain a Brazilian CPF or CNPJ.signature-kit.UNSUPPORTED_ALGORITHMThe certificate uses an unsupported encryption algorithm.· editablesignature-kit.NO_CERTIFICATEThe file does not contain a certificate.signature-kit.NO_PRIVATE_KEYThe file does not contain a private key.signature-kit.CORRUPTED_FILEThe file is corrupted or not a valid PKCS#12 certificate.signature-kit.X509_PARSE_FAILEDX.509 parsing failed.· editablesignature-kit.PEM_EXTRACTION_FAILEDFailed to extract PEM material from the PFX.signature-kit.KEY_IMPORT_FAILEDFailed to import the key into Web Crypto.· editablesignature-kit.DIGEST_FAILEDFailed to compute the certificate digest.signature-kit.SIGN_FAILEDFailed to sign the content.· editablesignature-kit.VERIFY_FAILEDFailed to verify the signature.· editablesignature-kit.HTTPHTTP request failed.· editablesignature-kit.RESPONSE_SHAPEHTTP response shape was invalid.· editablesignature-kit.UNSUPPORTED_OPERATIONOperation is unsupported.· editablesignature-kit.UNKNOWNUnknown SignatureKit failure.· editableMensagens de exibição localizadas
Não localize inspecionando classes, campos .code soltos ou substrings de reason. Esse caminho quebra quando o texto diagnóstico muda:
export function getCertificateErrorMessage(error: { reason?: string }) {
const reason = error.reason ?? ""
if (reason.includes("not valid on the current date")) return "Certificado expirado"
if (reason.includes("Brazilian CPF or CNPJ")) {
return "Certificado não contém CPF ou CNPJ brasileiro"
}
return "Arquivo de certificado inválido"
}Use @signature-kit/i18n na fronteira da aplicação. Passe apenas os catálogos que aquele código pode receber; o resolver verifica _tag e code de forma estrutural e cai para inglês quando a locale pedida não existe:
import { errorMessage } from "@signature-kit/i18n"
import { signatureKitErrorMessages } from "@signature-kit/signatures"
import { cryptoErrorMessages } from "@signature-kit/crypto/config"
const message = errorMessage(error, {
locale: "pt-BR",
catalogs: [signatureKitErrorMessages, cryptoErrorMessages],
overrides: {
"pt-BR": {
"signature-kit.WRONG_PASSWORD": "Senha do certificado incorreta",
},
},
})Erros de formato e de provedor
Além do SignatureKitError, os módulos de formato adicionam suas próprias famílias tipadas ao canal de erros. APIs de provedores não adicionam: inputs inválidos de request upstream, falhas HTTP, falhas de formato de resposta e operações não suportadas continuam como SignatureKitError.
XmlError
xml.*. signXml retorna XmlError | SignatureKitError; verifyXml retorna apenas XmlError (sem o serviço Signatures).PdfError
pdf.*. signPdf retorna PdfError | CmsError | SignatureKitError. Por exemplo, hashAlgorithm: "sha384" falha como pdf.SIGN_FAILED — o backend de signer atual não tem contraparte rsa-sha384; "sha1" é aceito (mapeado para rsa-sha1).APIs de provedores
SignatureKitError com provider, operation, schemaName e status quando esses metadados existem.import { Effect } from "effect"
import { signXml } from "@signature-kit/xml/sign"
import { xmlRuntimeLayer } from "@signature-kit/xml/runtime"
import { signPdf } from "@signature-kit/pdf/sign"
import { a1SignaturesLayer } from "@signature-kit/a1/signer"
const layer = a1SignaturesLayer({ pfx, password })
// signXml -> XmlError | SignatureKitError (xml.* codes)
const xml = signXml({ xml: source, referenceId: "nfe-1" }).pipe(
Effect.provide(layer),
Effect.provide(xmlRuntimeLayer),
Effect.catchTags({
XmlError: (error) => Effect.fail(`Invalid XML: ${error.code}`),
SignatureKitError: (error) => Effect.fail(`Signature: ${error.code}`),
}),
)
// signPdf -> PdfError | CmsError | SignatureKitError (pdf.* codes)
const pdf = signPdf({ pdf: bytes, policy: "pades-icp-brasil" }).pipe(
Effect.provide(layer),
Effect.catchTag("PdfError", (error) => Effect.fail(`PDF: ${error.code}`)),
)Nas APIs de provedores, as falhas de entrada, de HTTP e de formato de resposta chegam como SignatureKitError. Discrimine pelo error.code da mesma forma:
import { Effect } from "effect"
import { ClicksignSignatureRequest } from "@signature-kit/clicksign"
const request = ClicksignSignatureRequest("contract", {
title: "Contract",
documents: documentProps,
recipients,
}).pipe(
Effect.catchTag("SignatureKitError", (error) => {
switch (error.code) {
case "signature-kit.HTTP":
return Effect.fail(error.reason ?? "Remote HTTP failure.")
case "signature-kit.INVALID_INPUT":
case "signature-kit.RESPONSE_SHAPE":
case "signature-kit.UNSUPPORTED_OPERATION":
return Effect.fail(error.message)
default:
return Effect.fail(error.message)
}
}),
)Os formatos e os signers são pacotes separados — instale apenas o que cada caminho usa.
npm install @signature-kit/xml @signature-kit/pdf @signature-kit/http @signature-kit/clicksignComo está este guia?