@flaks/onerecord
v0.4.0
Published
Zero-dependency Zod-first JSON-LD mapper for IATA OneRecord cargo data model 3.2 + API spec 2.2.0
Maintainers
Readme
@flaks/onerecord
Zero-dependency Zod-first TypeScript mapper for the IATA OneRecord cargo data model 3.2.0 and API spec 2.2.0.
What it is
@flaks/onerecord provides round-trip-safe serialization and deserialization for
the OneRecord cargo data model class set (Ring 1–5, booking-flow, and FWB-equivalence
classes added in v0.3.0). Each class ships with a Zod schema, an application-layer
TypeScript type, a JsonLd<T> brand, serialize / serializeStrict / deserialize
functions, and a Codec bundle. A factory (createMapper) and a namespaced facade
(onerecord) cover the two main consumption patterns. Safety primitives (depth, node,
string, array, payload limits; prototype-pollution defense; IRI validation) run before
Zod so that invalid JSON-LD graphs never reach schema evaluation. All parse-error
variants are typed as a discriminated union (ParseError) so callers handle every
failure mode exhaustively.
Why use this library?
Air cargo is migrating to OneRecord — IATA's JSON-LD-based replacement for
EDIFACT / Cargo-IMP messaging. As of January 2026, roughly 8% of the
industry has adopted; the standard is endorsed but the TypeScript tooling
ecosystem is sparse. @flaks/onerecord fills that gap.
Use this library if you're building cargo software that needs to:
- Accept OneRecord JSON-LD from external endpoints with strict validation (Zod schemas + cardinality + IRI + a pre-Zod safety pass).
- Emit spec-compliant JSON-LD with stable round-trip semantics
(
serialize(deserialize(x)) == xper spec §7.1). - Refuse malformed graphs early — depth bombs, prototype-pollution attempts, empty arrays on the wire, circular references — before they reach Zod or your downstream code.
- Catch cross-node graph violations with
createMapper({ graphWalk: true })or theonerecord.dispatch.deserialize.<Class>namespace —duplicate_id_in_graph,missing_id,wrong_type_for_endpoint,missing_type, all surfaced as typedParseErrorvariants. Default per-class deserializers stay structural-only for v0.1.x compatibility; graph-walk is opt-in. - Type-check every parse-failure path:
ParseErroris a discriminated union, so the compiler tells you when you've forgotten a failure mode.
Skip it if you want a generic JSON-LD or RDF toolkit; this is purpose-built for the IATA cargo ontology and the ONE Record API spec 2.2.0.
Conformance and verifiability
This library is 3.2.0-conformant for the covered class set, with documented
deviations and 3.2.1-forward extensions. Specifically:
Shipment.securityDeclarations is accepted as a documented 3.2.1-forward
extension (deviation #12). Every deviation is recorded in
docs/spec-deviations.md — read that before
adopting if your integration must match the spec verbatim.
This library publishes verifiable artifacts so consumers can audit the conformance claim independently:
- All releases are GPG-signed git tags + signed npm tarballs (per
MAINTENANCE.md). - Every spec deviation is documented in
docs/spec-deviations.mdwith status banners (CLOSED, PARTIAL, deferred) and explicit guidance on what consumers should do today. - A
docs/conformance-matrix.mdmaps each class and field to its binding authority inIATA-1R-DM-Ontology.ttl. - Property-based round-trip tests for the Ring 1+2 classes
—
serialize(deserialize(x))is field-equivalent for any value anArbitraryproduces. - Contract tests at
test/contract/exercise the wire format end-to-end against the OLF-hosted reference NE:ONE Server (the IATA OneRecord reference implementation). SeeCONTRIBUTING.mdfor how to run them.
Status
v0.3.0 ships:
- The canonical cargo data model class set (Ring 1–5, booking-flow, plus 10 new FWB-equivalence classes in v0.3.0) with full round-trip codecs, Zod schemas, snapshot tests, and pre-Zod safety primitives.
- Opt-in graph-walk dispatcher (
createMapper({ graphWalk: true })oronerecord.dispatch.deserialize.<Class>) emitting all four cross-node integrity ParseError kinds. - Spec-correct
acceptBookingOptionViaRequesttransition that returns theBookingOptionRequestintermediate per spec §5.4. The legacyacceptBookingOptionis@deprecated(removal v0.3 unless IATA §5.4 reconciliation restores the §5.2 shortcut). - Property-based round-trip tests for all 8 Ring 1+2 classes.
- FSU code fixture regenerated with a real
sha256:blob hash, sourced from upstream IATA-Cargo via the siblingtjo099/onerecord-xlsx-toolstool. Library no longer carries thexlsxdevDep — closes the two high-severity Prototype Pollution + ReDoS advisories. - See
docs/roadmap.mdfor what's next.
The library is Apache-2.0 and ready for use within the v0.x stability
policy described in MIGRATING.md.
Spec compliance: cargo data model 3.2.0 (2025-07 endorsed standard, 3.2-rc2 file),
API spec 2.2.0. The library is 3.2.0-conformant for the covered class set,
with documented deviations and 3.2.1-forward extensions. See
docs/spec-deviations.md and
docs/conformance-matrix.md for the full picture.
Install
npm install @flaks/onerecord
# or
bun add @flaks/onerecord
# or
pnpm add @flaks/onerecordPin a minor range in production (e.g. "@flaks/onerecord": "^0.2.0").
Per MIGRATING.md, minor versions may introduce breaking changes
documented in CHANGELOG.md; patch versions will not.
Installing from a git tag is also supported for testing forks or unreleased branches:
bun add git+https://github.com/tjo099/onerecord-mapper#v0.2.0Usage
Round-trip with WaybillCodec
import { WaybillCodec } from '@flaks/onerecord'
// Deserialize raw JSON-LD from the wire
const result = WaybillCodec.deserialize(rawJsonLd)
if (result.ok) {
const waybill = result.value // typed as Waybill (application object)
const jsonLd = WaybillCodec.serialize(waybill) // back to JsonLd<Waybill>
} else {
console.error(formatError(result.error))
}createMapper with custom limits
import { createMapper } from '@flaks/onerecord'
const mapper = createMapper({
limits: { maxDepth: 12, maxNodes: 500, maxPayloadBytes: 512_000 },
iriStrategy: 'strict',
allowedSchemes: ['https'],
})
const result = mapper.deserialize.Waybill(rawJsonLd)
const jsonLd = mapper.serialize.Waybill(waybill)onerecord namespaced facade
import { onerecord, formatError } from '@flaks/onerecord'
// One-off operations, no factory state
const result = onerecord.deserialize.Shipment(rawJsonLd)
if (!result.ok) {
throw new Error(formatError(result.error))
}
const jsonLd = onerecord.serialize.Piece(piece)API overview
Per-class exports
Each registered class exposes:
| Export | Description |
|---|---|
| <Class>Schema | Zod schema for the application object |
| <Class>Codec | { serialize, serializeStrict, deserialize } bundle |
| serialize<Class>(obj) | Returns JsonLd<T> |
| deserialize<Class>(raw) | Returns Result<T, ParseError> |
Factory
createMapper(opts: {
limits?: Partial<SafetyLimits>
iriStrategy?: 'strict' | 'lenient'
allowedSchemes?: string[]
}): BoundMapperReturns a mapper with bound serialize.<Class> and deserialize.<Class> methods
using deep-frozen options set at creation time.
Facade
onerecord.deserialize.<Class>(raw): Result<T, ParseError>
onerecord.serialize.<Class>(obj): JsonLd<T>No bound state; suitable for one-off operations and tree-shake-friendly imports.
Errors
ParseError— discriminated union onkind; coverszod_validation,cardinality_violation,invalid_iri,depth_limit_exceeded,prototype_pollution_attempt,invalid_pointer, and moreSerializationError—code: 'invalid_application_object' | 'iri_construction_failed'formatError(e: ParseError): string— human-readable messageredactError(e: ParseError): ParseError— strips PII from error payloads before logging
Booking state machine
import { STATE_DIAGRAM, canTransition } from '@flaks/onerecord'
canTransition(from, to) // boolean predicate
acceptBookingRequest(req) // typed transition → BookingOption
acceptBookingOption(opt) // typed transition → Booking
rejectBookingOption(opt) // typed transition → BookingOption
revokeBookingOption(opt) // typed transition → BookingOptionOperations (JSON-Patch per spec §6.5)
import { applyChange, validateOperation, asJsonPointer } from '@flaks/onerecord'
applyChange(codec, obj, change) // returns Result<T, ParseError>
validateOperation(op, allowedFields) // validates op.s against allowed field list
asJsonPointer(path) // typed JsonPointer helperFSU codes
import { FSU_EVENT_CODES } from '@flaks/onerecord'
// Record<string, FsuEventCode> — 26-code map per IATA XFSU specSafety primitives
import { DEFAULT_SAFETY_LIMITS, mergeLimits } from '@flaks/onerecord'
// DEFAULT_SAFETY_LIMITS: SafetyLimits (depth, nodes, strings, arrays, payloadBytes)
// mergeLimits(base, overrides): SafetyLimitsIRI utilities
import { safeIri, validateIri, defaultIriStrategy } from '@flaks/onerecord'
// safeIri() — Zod helper; lifts IRI failures into kind: 'invalid_iri'
// validateIri(s) — Result<SafeIri, ParseError>
// defaultIriStrategy — IriStrategy with https-only scheme enforcementArchitecture notes
- Zero runtime dependencies other than Zod (peer).
- Round-trip safety: serialize(deserialize(x)) == x per spec §7.1 tolerance.
Deserialized application objects carry no
@contextor@typenoise;serializereconstructs the canonical JSON-LD envelope. - Pre-Zod safety pass: before any schema evaluation,
preValidateenforces depth, node count, string length, array length, and payload-byte limits; detects prototype-pollution keys (__proto__,constructor,prototype); and unwinds JS-level circular references. Zod never sees a malformed graph. - Empty arrays rejected at any depth as
cardinality_violation. The spec mandates omitting a field rather than sending[]; this is enforced pre-Zod. - Null-prototype output: all deserialized graphs are built on
Object.create(null)objects. Consumer code cannot accidentally read or polluteObject.prototypethrough a deserialized value. - Branded types:
SafeIri(validated IRI string) andJsonLd<T>(serialized graph) are distinct branded types. TypeScript prevents passing a raw string where aSafeIriis required, and passing an application object whereJsonLd<T>is expected.
Tree-shaking
For maximum bundle size optimization, import from the /codecs subpath:
import { WaybillCodec } from '@flaks/onerecord/codecs'
import { ShipmentCodec } from '@flaks/onerecord/codecs'Type-only imports (zero runtime cost):
import type { Waybill, ParseError } from '@flaks/onerecord/types'Version pins
| Spec | Version | |---|---| | Cargo data model | 3.2.0 (2025-07 endorsed standard, 3.2-rc2 ontology file) | | API spec | 2.2.0 |
Development
bun install
bun run test # see CI for current count
bun run lint # Biome
bun run typecheck # tsc --noEmit