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 18 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, mais específica do que a padrão.operation?— a operação em que a falha ocorreu, útil para logging e telemetria.get message()— a mensagem legível por humanos; resolve parareasonquando presente, caso contrário para o texto padrão do code.
import { Effect } from "effect"
import type { SignatureArtifact, SignatureKitError } from "@signature-kit/core/config"
// The error channel is typed: each failure is a SignatureKitError.
declare const sign: Effect.Effect<SignatureArtifact, SignatureKitError>
// _tag: "SignatureKitError"
class SignatureKitError {
readonly code: SignatureKitErrorCode // 18 "signature-kit.*" literals
readonly retryable: boolean // decided at the point of failure
readonly reason?: string // contextual message
readonly operation?: SignatureKitOperation
get message(): string // default per code (reason ?? default)
}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 18 literais.
import { Effect } from "effect"
import { a1SignaturesLayer } from "@signature-kit/a1/signer"
import { signatures } from "@signature-kit/core/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"
// 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 18 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.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.HTTPRemote signature HTTP request failed.· editablesignature-kit.RESPONSE_SHAPERemote signature response shape was invalid.· editablesignature-kit.UNSUPPORTED_OPERATIONRemote signature operation is unsupported.· editablesignature-kit.UNKNOWNUnknown SignatureKit failure.· editableErros 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, um hashAlgorithm fora de sha256/sha512 falha como pdf.SIGN_FAILED.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 { createClicksignSignatureRequest } from "@signature-kit/clicksign"
import { signatureHttpClientLive } from "@signature-kit/core/http"
const request = createClicksignSignatureRequest(options, {
title: "Contract",
documents,
recipients,
}).pipe(
Effect.provide(signatureHttpClientLive),
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/core @signature-kit/clicksign