An official website of the Disclosure Foundation

Introduction

Overview

Modules

ParsingRegistryJSON Schema

Parsing

parseEnrichedObservation — non-stripping validation of the whole enriched record

parseEnrichedObservation is the validation entry point for any record that may carry slots. One call validates everything; nothing is stripped.

Usage

import { parseEnrichedObservation } from '@disclosureos/schema';

const result = parseEnrichedObservation(record);

The result

interface EnrichedParseResult {
  success: boolean;
  data?: EnrichedObservation;   // reference-equal to the input, when success
  issues: ValidationIssue[];    // empty iff success
}
if (!result.success) {
  for (const issue of result.issues) {
    console.error(`${issue.path}: ${issue.message}`);
  }
  // temporal.date: Invalid input
  // observableAssessments.technology.warp_drive: unknown observable id
  // origin[0].confidence: Too big: expected number to be <=1
  // discloureRating: unknown top-level key "discloureRating" — third-party data belongs under "extensions".
}

Slot-local issue paths are prefixed with the slot name, so every issue points at the exact field — ready for form mapping or import reports.

What one call checks

  1. The records core — via records' own validateObservation.
  2. Every present slot — via the owner package's canonical validator (validateObservableAssessments, validateOriginClassification), discovered through the registry. Absent slots are simply skipped — a core-only record passes.
  3. Top-level key allowlist — any key that is neither a core property nor a registered slot is rejected with a pointer to the extensions bag.

Validation composes by delegation, not by re-parsing through one mega-schema. Each package validates its own slot with its own Zod instance; the contract just orchestrates. This is the same pipeline disclosureos validate runs against files.

Why it never strips

On success the returned data is the same object reference you passed in. There is no .parse() round-trip constructing a new object from recognized keys — which is precisely how ObservationSchema.parse() loses slot data:

// ❌ The hazard this function exists to close:
const stripped = ObservationSchema.parse(enriched);
stripped.origin;                  // undefined — silently gone

// ✅ The contract way:
const result = parseEnrichedObservation(enriched);
result.data === enriched;         // true
result.data?.origin;              // intact

Unknown keys vs. the extensions bag

The closed root protects against typos and stale field names — the difference between an error today and corrupted data discovered in two years:

parseEnrichedObservation({ ...base, sigthing_quality: 'high' });
// issue: unknown top-level key "sigthing_quality"

parseEnrichedObservation({
  ...base,
  extensions: { myPipeline: { sightingQuality: 'high' } },
});
// ✅ valid — extensions content is yours, passed through untouched

EnrichedObservation

The convenience type for the complete shape — Observation with both slots typed (the package imports both satellites, so the augmentation is active):

import type { EnrichedObservation } from '@disclosureos/schema';

function publish(obs: EnrichedObservation) {
  obs.observableAssessments;  // typed
  obs.origin;                 // typed
}

Overview

@disclosureos/schema — the portable contract that binds records and package-owned slots into one validated observation

Registry

ExtensionRegistry — the runtime mirror of the TypeScript augmentation

On this page

Usage
The result
What one call checks
Why it never strips
Unknown keys vs. the extensions bag
EnrichedObservation