Skip to content

Types & Error Codes

This page documents all TypeScript types and error codes exported from @etapsky/sdf-kit.

Core types import

import type {
SDFMeta,
SDFParseResult,
SDFParseResultJSON,
SDFValidationResult,
VersionCheckResult,
SDFKeyPair,
} from '@etapsky/sdf-kit';
import { SDFError } from '@etapsky/sdf-kit';

SDFMeta

Represents the contents of meta.json inside an .sdf archive. All required fields are specified by the SDF format specification.

interface SDFMeta {
/** SDF format version. Currently "0.1". */
sdf_version: string;
/** UUID v4 generated at production time. Never derived from business data. */
document_id: string;
/**
* Document type identifier.
* Examples: "invoice", "purchase_order", "nomination", "contract"
*/
document_type: string;
/** Human-readable name of the document issuer. */
issuer: string;
/** ISO 8601 datetime string of when the document was produced. */
created_at: string;
/** Optional unique identifier for the issuer (e.g. tax ID, GLN, UUID). */
issuer_id?: string;
/** Optional human-readable name of the document recipient. */
recipient?: string;
/**
* Optional schema identifier URI.
* Example: "invoice/v1.0"
* Referenced in the schema registry.
*/
schema_id?: string;
/**
* Optional BCP 47 language tag.
* Controls PDF rendering locale.
* Example: "en-US", "de-DE", "tr-TR"
*/
locale?: string;
/**
* Optional nomination reference for document matching workflows.
* Used in B2B and G2G scenarios where sender and receiver pre-agree on a reference.
*/
nomination_ref?: string;
}

SDFParseResult

Returned by parseSDF(). Contains all four layers of an .sdf file.

interface SDFParseResult {
/** Parsed and validated contents of meta.json */
meta: SDFMeta;
/** Parsed contents of data.json */
data: Record<string, unknown>;
/** Parsed contents of schema.json (JSON Schema Draft 2020-12) */
schema: Record<string, unknown>;
/** Raw bytes of visual.pdf */
pdfBytes: Uint8Array;
}

SDFParseResultJSON

Returned by extractJSON(). Contains only the JSON layers — visual.pdf is not loaded.

interface SDFParseResultJSON {
/** Parsed and validated contents of meta.json */
meta: SDFMeta;
/** Parsed contents of data.json */
data: Record<string, unknown>;
/** Parsed contents of schema.json (JSON Schema Draft 2020-12) */
schema: Record<string, unknown>;
}

SDFValidationResult

Returned by validateSchema().

interface SDFValidationResult {
/** true if data fully conforms to schema, false otherwise */
valid: boolean;
/**
* Array of validation errors. Empty when valid is true.
* Each entry identifies the failing location and describes the problem.
*/
errors: Array<{
/**
* JSON Pointer (RFC 6901) to the failing value in data.
* Example: "/total/amount" means data.total.amount failed.
* Empty string ("") means the root object failed.
*/
instancePath: string;
/** Human-readable description of the validation failure. */
message: string;
}>;
}

VersionCheckResult

Returned by checkVersion().

interface VersionCheckResult {
/** true if the given sdf_version is supported by this sdf-kit release */
supported: boolean;
/** The highest SDF spec version supported by this sdf-kit release */
current: string;
}

SDFKeyPair

Returned by generateSDFKeyPair().

interface SDFKeyPair {
/** PEM-encoded private key (PKCS#8 format). Keep secret. */
privateKey: string;
/** PEM-encoded public key (SubjectPublicKeyInfo format). Safe to distribute. */
publicKey: string;
}

SDFError

All spec-level failures throw SDFError. It extends the native Error class and adds a code string for programmatic error handling.

class SDFError extends Error {
/** Machine-readable error code. One of the constants listed below. */
readonly code: string;
constructor(code: string, message: string);
}

Usage pattern

import { SDFError } from '@etapsky/sdf-kit';
try {
const buffer = await buildSDF({ data, schema, issuer, documentType });
} catch (err) {
if (err instanceof SDFError) {
// err.code is one of the constants below
// err.message is a human-readable description
console.error(`[${err.code}] ${err.message}`);
} else {
throw err; // re-throw non-SDF errors
}
}

Error code reference

CodeThrown byDescription
SDF_ERROR_NOT_ZIPparseSDF, extractJSON, signSDF, verifySIGThe input buffer is not a valid ZIP archive. The file may be corrupted or is not an .sdf file.
SDF_ERROR_MISSING_FILEparseSDF, extractJSONA required entry is absent from the archive. One of visual.pdf, data.json, schema.json, or meta.json is missing.
SDF_ERROR_INVALID_METAparseSDF, extractJSON, buildSDF, validateMetameta.json fails structural validation. A required field is absent, has the wrong type, or document_id is not a valid UUID v4.
SDF_ERROR_SCHEMA_MISMATCHbuildSDFdata does not conform to the provided schema. The errors property of the thrown error contains AJV validation details.
SDF_ERROR_INVALID_SCHEMAbuildSDFThe schema object is not valid JSON Schema Draft 2020-12.
SDF_ERROR_UNSUPPORTED_VERSIONparseSDF, extractJSONThe sdf_version value in meta.json is not supported by this version of @etapsky/sdf-kit.
SDF_ERROR_INVALID_SIGNATUREverifySIGThe signature.sig entry is present but structurally malformed. Distinct from a valid-structure-but-wrong-key signature, which causes verifySIG() to return false rather than throw.
SDF_ERROR_INVALID_ARCHIVEparseSDF, extractJSONThe archive contains path traversal sequences (..) in entry names, or is otherwise structurally corrupted.
SDF_ERROR_ARCHIVE_TOO_LARGEparseSDF, extractJSONThe archive exceeds 50 MB compressed or 200 MB uncompressed. This is a ZIP bomb protection limit.

Type-narrowing example

When you need to handle multiple error codes differently:

typed-error-handling.ts
import { parseSDF } from '@etapsky/sdf-kit/reader';
import { SDFError } from '@etapsky/sdf-kit';
type SDFErrorCode =
| 'SDF_ERROR_NOT_ZIP'
| 'SDF_ERROR_MISSING_FILE'
| 'SDF_ERROR_INVALID_META'
| 'SDF_ERROR_SCHEMA_MISMATCH'
| 'SDF_ERROR_INVALID_SCHEMA'
| 'SDF_ERROR_UNSUPPORTED_VERSION'
| 'SDF_ERROR_INVALID_SIGNATURE'
| 'SDF_ERROR_INVALID_ARCHIVE'
| 'SDF_ERROR_ARCHIVE_TOO_LARGE';
function isSdfError(err: unknown, code: SDFErrorCode): err is SDFError {
return err instanceof SDFError && err.code === code;
}
try {
const result = await parseSDF(buffer);
} catch (err) {
if (isSdfError(err, 'SDF_ERROR_ARCHIVE_TOO_LARGE')) {
return reply.status(413).send({ error: 'File exceeds maximum allowed size' });
}
if (isSdfError(err, 'SDF_ERROR_NOT_ZIP')) {
return reply.status(422).send({ error: 'File is not a valid SDF archive' });
}
if (err instanceof SDFError) {
return reply.status(422).send({ error: err.message, code: err.code });
}
throw err;
}