An official website of the Disclosure Foundation

Introduction

Overview

Modules

ParsingRegistryJSON Schema

Registry

ExtensionRegistry — the runtime mirror of the TypeScript augmentation

TypeScript module augmentation makes slots typed, but types vanish at runtime and are invisible to JSON Schema, Python, and the CLI. The ExtensionRegistry records the same facts as data: which slots exist, who owns them, how to validate them, and what their JSON Schema is.

The default registry

Importing @disclosureos/schema registers the first-party slots on defaultRegistry — the runtime counterpart of importing both satellite packages for their type augmentation:

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

defaultRegistry.list();
// [
//   { slot: 'observableAssessments', owner: '@disclosureos/observables', ... },
//   { slot: 'origin',                owner: '@disclosureos/origins', ... },
// ]

defaultRegistry.has('origin');  // true
defaultRegistry.get('origin');  // the full registration

What a registration carries

interface SlotRegistration {
  slot: string;        // property name on Observation
  owner: string;       // owning package
  schemaId: string;    // canonical, versioned $id of the slot's JSON Schema
  version: string;     // the slot's x-schema-version
  jsonSchema: () => Record<string, unknown>;       // standalone JSON Schema artifact
  validate: (value: unknown) => ValidationIssue[]; // the owner's canonical validator
}

Both consumers of the registry are fully driven by it:

  • parseEnrichedObservation walks list() and calls each validate on present slots.
  • composeObservationSchema walks list() and folds each jsonSchema() into the enriched artifact.

Registering a slot makes it validated and composed — no edits to either code path.

Registering a third-party slot

Experimental

Third-party registration works today, but the extensibility contract — how external slots version, compose, and resolve conflicts — is still settling and may change in a minor release. For most third-party data, the extensions bag is the stable extension area.

import { defaultRegistry } from '@disclosureos/schema';
import { z } from 'zod';
import { validateWith } from '@disclosureos/records/shared';

const AcousticSchema = z.object({
  infrasoundDetected: z.boolean(),
  peakFrequencyHz: z.number().optional(),
});

defaultRegistry.register({
  slot: 'acousticAnalysis',
  owner: '@my-org/disclosureos-acoustics',
  schemaId: 'https://my-org.example/schema/acoustics/1.0.0/acoustics.json',
  version: '1.0.0',
  jsonSchema: () => z.toJSONSchema(AcousticSchema, { target: 'draft-2020-12' }),
  validate: validateWith(AcousticSchema),
});

From that point, parseEnrichedObservation validates acousticAnalysis when present, and composeObservationSchema includes it in the enriched artifact. Pair it with your own module augmentation for compile-time typing:

declare module '@disclosureos/records' {
  interface ObservationExtensions {
    acousticAnalysis?: z.infer<typeof AcousticSchema>;
  }
}

Rules enforced by the registry:

  • One owner per slot — re-registering an existing slot name throws.
  • Disjoint $defs — composition throws on a $defs name collision rather than silently overwriting.

Custom registries

composeObservationSchema(registry) accepts a custom ExtensionRegistry for composing a subset of slots — useful in tests or when emitting a deliberately narrower contract:

import { ExtensionRegistry, composeObservationSchema } from '@disclosureos/schema';

const coreOnly = new ExtensionRegistry();
const schema = composeObservationSchema(coreOnly); // records core, no slots

Parsing

parseEnrichedObservation — non-stripping validation of the whole enriched record

JSON Schema

The portable artifact — composed, versioned, and usable from any language

On this page

The default registry
What a registration carries
Registering a third-party slot
Custom registries