Files
xiaoju dbfaf01031
CI / check (pull_request) Successful in 2m35s
docs: add export/import to README/cards; tidy bundle.ts imports
Address reviewer feedback on #83:

- Replace dynamic `await import("./bootstrap-capable.js")` in
  bundle.ts with a static top-of-file import (no real cycle exists,
  bootstrap.ts is already statically imported).
- Remove unused `bootstrapSym` Symbol.for() / `void bootstrapSym`
  dead code in importBundle.
- Move the misplaced `import { decode } from "cborg"` to the top of
  bundle.ts with the other imports.
- Document the new `export` / `import` commands and `--store`,
  `--scope`, `-o` flags in:
  - root README.md (commands + global flags)
  - packages/cli/README.md (command table + bundles section + global flags)
  - packages/cli/prompts/usage.md (`ocas prompt usage` output)
  - packages/core/README.md (Closure & Bundles API surface)
  - .cards/cli.md (bundle commands + --store architecture note)
2026-06-07 03:19:59 +00:00

5.8 KiB

@ocas/core

Core CAS engine — hashing, schema, store, verify, bootstrap.

Overview

@ocas/core is the foundation of the ocas monorepo. It defines content-addressed nodes (CasNode), the Store interface, XXH64-based hashing with deterministic CBOR, JSON Schema registration and validation (including cas_ref links between nodes), bootstrap seeding, and integrity verification.

Other packages build on this layer: @ocas/fs provides persistence, and @ocas/cli exposes store operations on the command line.

Dependencies: ajv, cborg, liquidjs, xxhash-wasm

Installation

pnpm add @ocas/core

API

All symbols below are exported from src/index.ts.

Types

/** 13-character uppercase Crockford Base32 (XXH64) */
type Hash = string;

type CasNode<T = unknown> = {
  type: Hash;
  payload: T;
  timestamp: number; // Unix epoch ms
};

type Store = { cas: CasStore; var: VarStore; tag: TagStore; };

type JSONSchema = Record<string, unknown>;

type BootstrapCapableStore = Store & {
  [BOOTSTRAP_STORE](payload: unknown): Promise<Hash>;
};

Hashing

function computeHash(typeHash: Hash, payload: unknown): Promise<Hash>;
function computeSelfHash(payload: unknown): Promise<Hash>;
function cborEncode(value: unknown): Uint8Array;

computeHashXXH64(utf8(typeHash) ++ CBOR(payload)) for normal nodes.

computeSelfHashXXH64(CBOR(payload)) for bootstrap nodes where type === hash.

Bootstrap

const BOOTSTRAP_STORE: unique symbol;

async function bootstrap(store: Store): Promise<Hash>;

Writes the meta-schema seed node (idempotent). Requires a BootstrapCapableStore (e.g. from createMemoryStore()).

Schema

class SchemaValidationError extends Error;

async function putSchema(store: Store, jsonSchema: JSONSchema): Promise<Hash>;
function getSchema(store: Store, typeHash: Hash): JSONSchema | null;
function validate(store: Store, node: CasNode): boolean;
function refs(store: Store, node: CasNode): Hash[];
function walk(
  store: Store,
  rootHash: Hash,
  visitor: (hash: Hash, node: CasNode) => void,
): void;
  • putSchema — stores a schema typed by the meta-schema; returned hash is the typeHash for conforming payloads.
  • refs — collects all format: "cas_ref" values in the payload per schema shape.
  • walk — BFS from rootHash, following cas_ref edges; cycles are visited once.

Store

function createMemoryStore(): BootstrapCapableStore;

In-memory Store with type indexing, suitable for tests and ephemeral use.

Verify

async function verify(hash: Hash, node: CasNode): Promise<boolean>;

Recomputes hash from node and compares to hash (self-referencing vs normal rules).

Closure & Bundles

type ClosureResult = {
  nodes: Set<Hash>;
  vars: Variable[];
  tags: Map<Hash, Tag[]>;
};
function computeClosure(store: Store, roots: Hash[]): ClosureResult;

type ExportStats = { nodes: number; vars: number; tags: number };
type ImportOptions = { scope?: string };
type ImportStats = {
  nodes: { imported: number; skipped: number };
  vars: { created: number; updated: number };
  tags: number;
};
async function exportBundle(
  store: Store,
  roots: Hash[],
  outputPath: string,
): Promise<ExportStats>;
async function importBundle(
  bundlePath: string,
  target: Store,
  options?: ImportOptions,
): Promise<ImportStats>;
async function loadBundleStore(bundlePath: string): Promise<Store>;
  • computeClosure — walks cas_ref edges and schema chains from each root, also gathering every Variable whose value lands in the closure and every Tag attached to an in-closure target.
  • exportBundle — writes a self-contained POSIX-tar archive containing cas/<hash>.bin (CBOR-encoded payloads), vars.jsonl, and tags.jsonl.
  • importBundle — content-addressed merge into target. Idempotent: re-importing the same bundle yields zero imported and zero created. options.scope rewrites the leading @scope/ of every imported variable name except @ocas/* builtins.
  • loadBundleStore — convenience that returns an in-memory Store populated from a bundle (for read-only inspection without touching the persistent store).

Example

import {
  bootstrap,
  createMemoryStore,
  putSchema,
  refs,
  validate,
  walk,
} from "@ocas/core";

const store = createMemoryStore();
const metaHash = await bootstrap(store);

const personType = await putSchema(store, {
  type: "object",
  properties: {
    name: { type: "string" },
    friend: { type: "string", format: "cas_ref" },
  },
  required: ["name"],
  additionalProperties: false,
});

const aliceHash = await store.put(personType, { name: "Alice" });
const bobHash = await store.put(personType, {
  name: "Bob",
  friend: aliceHash,
});

const bob = store.get(bobHash)!;
console.log(validate(store, bob));           // true
console.log(refs(store, bob));               // [aliceHash]
walk(store, bobHash, (h) => console.log(h)); // bobHash, aliceHash

Internal Structure

File Purpose
types.ts Hash, CasNode, Store
hash.ts computeHash, computeSelfHash
cbor.ts Deterministic CBOR encoding
bootstrap-capable.ts BOOTSTRAP_STORE symbol and capability check
bootstrap.ts Meta-schema seed and bootstrap()
store.ts createMemoryStore()
mem-store.ts Alternate in-memory store (tests only; not exported)
schema.ts Schema put/get/validate, refs, walk
verify.ts Node integrity verification
closure.ts computeClosure — refs + schema chain traversal
bundle.ts exportBundle, importBundle, loadBundleStore (POSIX tar)
index.ts Public exports

Tests live in src/*.test.ts and tests/.