Skip to main content

Traversal

All schema data processing is performed by a recursive traversal of the input data in synchronicity with the schema hierarchy. It's straightforward, in theory:

  1. load the input
  2. if a container, recurse into all child property schemas
  3. produce the output

In practice, there are some complexities:

  • Schemas support incremental assignments.
  • We can discover new or replacement input data during processing.
  • Some schemas need pre-order processing, some need post-order processing.
  • We might treat objects/arrays as opaque values, or have child schemas defining their internals.
  • Conditions may disable or temporarily suppress schema subtrees.
  • Unions may not be resolved yet, thus we don't have a matching schema for an input.
  • Containers might be populated incrementally, or need to be built all at once.
  • Values may populate dynamically or conditionally.
  • Values may depend on peer or parent context.
  • The schema may have circular dependencies.
  • Dependencies might need to be transformed before they can be referenced.

To accommodate these needs, the state of each "path" within the output value structure is tracked, and the schema performs a multi-pass traversal when needed, repeatedly walking the input and schema hierarchy until a stable value is achieved.

Handlers execute during each traversal. At any step, they might throw an error to indicate failure, return a value, return null to explicitly mean "no value", or return undefined to indicate "no current value". Any undefined value is retried on the next traversal pass. If a traversal is made and no updates are made to the output, a finalization pass is run, where these undefined values are treated as missing (which may be ok, or may be an error if the value was marked as required.)