feat: rebrand json-cas → OCAS #9

Merged
xiaoju merged 1 commits from feat/rebranding into main 2026-06-01 06:29:16 +00:00
70 changed files with 394 additions and 402 deletions
+2 -2
View File
@@ -1,8 +1,8 @@
{
"$schema": "https://unpkg.com/@changesets/config@3.1.1/schema.json",
"changelog": ["@changesets/changelog-github", { "repo": "uncaged/json-cas" }],
"changelog": ["@changesets/changelog-github", { "repo": "shazhou-ww/ocas" }],
"commit": false,
"fixed": [["@uncaged/*"]],
"fixed": [["@ocas/*"]],
"linked": [],
"access": "public",
"baseBranch": "main",
+6 -6
View File
@@ -1,4 +1,4 @@
# CLAUDE.md — json-cas
# CLAUDE.md — ocas
Self-describing content-addressable storage with JSON Schema typed nodes.
@@ -8,9 +8,9 @@ Monorepo with 4 packages under `packages/`:
| Package | Description |
|---------|-------------|
| `json-cas` | Core CAS engine — hashing, schema, store, verify, bootstrap |
| `json-cas-fs` | Filesystem-backed CAS store |
| `cli-json-cas` | CLI tool |
| `ocas` | Core CAS engine — hashing, schema, store, verify, bootstrap |
| `ocas-fs` | Filesystem-backed CAS store |
| `cli` | CLI tool |
## Tech Stack
@@ -19,7 +19,7 @@ Monorepo with 4 packages under `packages/`:
- **Build:** `tsc --build` (composite project references)
- **Test:** `bun test`
- **Lint/Format:** Biome (`biome check .` / `biome format --write .`)
- **Publish:** Changesets → npmjs (`@uncaged/*`)
- **Publish:** Changesets → npmjs (`@ocas/*`)
## Commands
@@ -41,7 +41,7 @@ bun run format # Biome format (auto-fix)
### Biome Rules
- `noConsole: "error"` globally (except `cli-json-cas`)
- `noConsole: "error"` globally (except `cli`)
- Recommended ruleset enabled
- Auto-organize imports via `assist.actions.source.organizeImports`
- Indent: 2 spaces
+26 -26
View File
@@ -1,10 +1,10 @@
# json-cas
# ocas
Self-describing content-addressable storage with JSON Schema typed nodes.
## Overview
json-cas is a monorepo for storing and validating JSON data in a content-addressable store (CAS). Each node has a typed payload: its `type` field is the hash of a JSON Schema node that describes the payload shape. Hashes are 13-character Crockford Base32 strings derived from XXH64 over deterministic CBOR encoding.
ocas is a monorepo for storing and validating JSON data in a content-addressable store (CAS). Each node has a typed payload: its `type` field is the hash of a JSON Schema node that describes the payload shape. Hashes are 13-character Crockford Base32 strings derived from XXH64 over deterministic CBOR encoding.
A bootstrap meta-schema is stored as a self-referencing seed node (`type === hash`). All other schemas are registered as nodes typed by that meta-schema. Payloads can reference other nodes via `format: "cas_ref"` fields; the library provides traversal, reference extraction, and integrity verification.
@@ -14,39 +14,39 @@ Use the in-memory store for tests and embedded apps, the filesystem store for pe
```
┌─────────────────┐
│ cli-json-cas │
│ cli-ocas │
└────────┬────────┘
┌─────────────────┐
json-cas-fs │
ocas-fs │
└────────┬────────┘
┌─────────────────┐
json-cas │ (core)
ocas │ (core)
└─────────────────┘
```
| Layer | Package | Role |
|-------|---------|------|
| Core | `@uncaged/json-cas` | Hashing, schemas, stores, verify, bootstrap |
| Storage | `@uncaged/json-cas-fs` | Filesystem-backed `Store` |
| CLI | `@uncaged/cli-json-cas` | `ucas` command-line tool |
| Core | `@ocas/core` | Hashing, schemas, stores, verify, bootstrap |
| Storage | `@ocas/fs` | Filesystem-backed `Store` |
| CLI | `@ocas/cli` | `ocas` command-line tool |
## Packages
| Package | Description | Type |
|---------|-------------|------|
| [`@uncaged/json-cas`](packages/json-cas/README.md) | Core CAS engine — hashing, schema, store, verify, bootstrap | lib |
| [`@uncaged/json-cas-fs`](packages/json-cas-fs/README.md) | Filesystem-backed CAS store | lib |
| [`@uncaged/cli-json-cas`](packages/cli-json-cas/README.md) | CLI tool (`ucas` binary) | cli |
| [`@ocas/core`](packages/ocas/README.md) | Core CAS engine — hashing, schema, store, verify, bootstrap | lib |
| [`@ocas/fs`](packages/ocas-fs/README.md) | Filesystem-backed CAS store | lib |
| [`@ocas/cli`](packages/cli-ocas/README.md) | CLI tool (`ocas` binary) | cli |
## Quick Start
```bash
git clone <repo-url>
cd json-cas
cd ocas
bun install --no-cache
bun run build
```
@@ -57,7 +57,7 @@ import {
createMemoryStore,
putSchema,
validate,
} from "@uncaged/json-cas";
} from "@ocas/core";
const store = createMemoryStore();
await bootstrap(store);
@@ -77,20 +77,20 @@ console.log(validate(store, node!)); // true
For a persistent store:
```typescript
import { createFsStore } from "@uncaged/json-cas-fs";
import { bootstrap } from "@uncaged/json-cas";
import { createFsStore } from "@ocas/fs";
import { bootstrap } from "@ocas/core";
const store = createFsStore("/path/to/store");
await bootstrap(store);
```
Or use the CLI (see [CLI Reference](#cli-reference) and [`packages/cli-json-cas/README.md`](packages/cli-json-cas/README.md)).
Or use the CLI (see [CLI Reference](#cli-reference) and [`packages/cli-ocas/README.md`](packages/cli-ocas/README.md)).
## CLI Reference
Binary: `ucas` (from `@uncaged/cli-json-cas`; legacy alias `json-cas` is deprecated). Default store:
`~/.uncaged/json-cas`. The store is auto-created and bootstrapped on first use — there is
no `init`/`bootstrap` command, and schemas are ordinary `@schema`-typed nodes (`ucas put
Binary: `ocas` (from `@ocas/cli`; legacy alias `ocas` is deprecated). Default store:
`~/.uncaged/ocas`. The store is auto-created and bootstrapped on first use — there is
no `init`/`bootstrap` command, and schemas are ordinary `@schema`-typed nodes (`ocas put
@schema file.json`), so there is no `schema` subcommand.
### Envelope format
@@ -102,12 +102,12 @@ output self-describing and pipeable: feed any envelope into `render -p` to rende
raw (non-envelope) text.
```jsonc
// ucas has <hash>
// ocas has <hash>
{ "type": "AYHQD2YA9G667", "value": true }
```
```
Usage: ucas [--store <path>] [--json] <command> [args]
Usage: ocas [--store <path>] [--json] <command> [args]
Commands (all emit a { type, value } envelope unless noted):
put <type-hash> <file.json> Store node (value = hash) (@output/put)
@@ -127,7 +127,7 @@ Commands (all emit a { type, value } envelope unless noted):
gc Garbage collection (@output/gc)
Flags:
--store <path> Store directory (default: ~/.uncaged/json-cas)
--store <path> Store directory (default: ~/.uncaged/ocas)
--json Compact JSON output
--pipe, -p Read a { type, value } envelope from stdin for render
```
@@ -137,13 +137,13 @@ Flags:
```bash
# Store a node, then render the stored content (the put envelope's hash is
# a cas_ref, so render -p dereferences and renders it):
ucas put @schema ./schemas/item.json | ucas render -p
ocas put @schema ./schemas/item.json | ocas render -p
# Render garbage-collection stats:
ucas gc | ucas render -p
ocas gc | ocas render -p
# List every schema, then consume the envelope's value array with jq:
ucas list --type @schema | jq -r '.value[]'
ocas list --type @schema | jq -r '.value[]'
```
## Development
@@ -161,7 +161,7 @@ bun test # run all package tests
Releases use [Changesets](https://github.com/changesets/changesets). From the repo root:
```bash
bun run release # changeset version → build → publish to npm (@uncaged/*)
bun run release # changeset version → build → publish to npm (@ocas/*)
```
Individual packages block `prepublishOnly` and expect releases via the workspace `release` script.
+1 -1
View File
@@ -15,7 +15,7 @@
},
"overrides": [
{
"includes": ["packages/cli-json-cas/**"],
"includes": ["packages/cli/**"],
"linter": {
"rules": {
"suspicious": { "noConsole": "off" }
+19 -19
View File
@@ -13,20 +13,20 @@
"ulidx": "^2.4.1",
},
},
"packages/cli-json-cas": {
"name": "@uncaged/cli-json-cas",
"version": "0.5.3",
"packages/cli": {
"name": "@ocas/cli",
"version": "0.6.0",
"bin": {
"json-cas": "./src/index.ts",
"ocas": "src/index.ts",
},
"dependencies": {
"@uncaged/json-cas": "workspace:^",
"@uncaged/json-cas-fs": "workspace:^",
"@ocas/core": "^0.6.0",
"@ocas/fs": "^0.6.0",
},
},
"packages/json-cas": {
"name": "@uncaged/json-cas",
"version": "0.5.3",
"packages/core": {
"name": "@ocas/core",
"version": "0.6.0",
"dependencies": {
"ajv": "^8.20.0",
"cborg": "^4.2.3",
@@ -34,11 +34,11 @@
"xxhash-wasm": "^1.1.0",
},
},
"packages/json-cas-fs": {
"name": "@uncaged/json-cas-fs",
"version": "0.5.3",
"packages/fs": {
"name": "@ocas/fs",
"version": "0.6.0",
"dependencies": {
"@uncaged/json-cas": "workspace:^",
"@ocas/core": "^0.6.0",
"cborg": "^4.2.3",
},
},
@@ -114,14 +114,14 @@
"@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
"@ocas/cli": ["@ocas/cli@workspace:packages/cli"],
"@ocas/core": ["@ocas/core@workspace:packages/core"],
"@ocas/fs": ["@ocas/fs@workspace:packages/fs"],
"@types/node": ["@types/node@25.8.0", "", { "dependencies": { "undici-types": ">=7.24.0 <7.24.7" } }, "sha512-TCFSk8IZh+iLX1xtksoBVtdmgL+1IX0fC9BeU4QqFSuNdN/K+HUlhqOzEmSYYpZUVsLYcPqc9KX+60iDuninSQ=="],
"@uncaged/cli-json-cas": ["@uncaged/cli-json-cas@workspace:packages/cli-json-cas"],
"@uncaged/json-cas": ["@uncaged/json-cas@workspace:packages/json-cas"],
"@uncaged/json-cas-fs": ["@uncaged/json-cas-fs@workspace:packages/json-cas-fs"],
"ajv": ["ajv@8.20.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA=="],
"ansi-colors": ["ansi-colors@4.1.3", "", {}, "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw=="],
+1 -1
View File
@@ -16,7 +16,7 @@ The root README should have these sections in order:
3. **Architecture** — dependency layer diagram (text-based)
4. **Packages** — table with ALL packages from packages/ directory, columns: Package, Description, Type (cli/lib)
5. **Quick Start** — install, build, basic usage
6. **CLI Reference** — brief command list, detailed usage in cli-json-cas README
6. **CLI Reference** — brief command list, detailed usage in cli README
7. **Development** — bun install / build / check / test
8. **Publishing** — changeset workflow (bun run release)
+2 -2
View File
@@ -1,5 +1,5 @@
{
"name": "@uncaged/json-cas-workspace",
"name": "@ocas/workspace",
"private": true,
"workspaces": [
"packages/*"
@@ -13,7 +13,7 @@
"ulidx": "^2.4.1"
},
"scripts": {
"build": "tsc --build packages/json-cas packages/json-cas-fs",
"build": "tsc --build packages/core packages/fs",
"test": "bun test",
"check": "biome check .",
"format": "biome format --write .",
@@ -1,44 +1,44 @@
# @uncaged/cli-json-cas
# @ocas/cli
CLI tool for json-cas stores.
CLI tool for ocas stores.
## Overview
`@uncaged/cli-json-cas` provides the `json-cas` command (also aliased `ucas`) for managing a filesystem-backed store: node CRUD, integrity checks, reference listing, graph walks, variables, and output templates. It uses `@uncaged/json-cas-fs` for persistence and `@uncaged/json-cas` for core operations.
`@ocas/cli` provides the `ocas` command (also aliased `ocas`) for managing a filesystem-backed store: node CRUD, integrity checks, reference listing, graph walks, variables, and output templates. It uses `@ocas/fs` for persistence and `@ocas/core` for core operations.
The store is **auto-created and bootstrapped** on first use, so there is no `init`/`bootstrap` command. Schemas are ordinary `@schema`-typed nodes — register one with `ucas put @schema file.json` and list them with `ucas list --type @schema`; there is no dedicated `schema` subcommand.
The store is **auto-created and bootstrapped** on first use, so there is no `init`/`bootstrap` command. Schemas are ordinary `@schema`-typed nodes — register one with `ocas put @schema file.json` and list them with `ocas list --type @schema`; there is no dedicated `schema` subcommand.
**Dependencies:** `@uncaged/json-cas`, `@uncaged/json-cas-fs`
**Dependencies:** `@ocas/core`, `@ocas/fs`
## Installation
Published as an npm package with a binary entry:
```bash
bun add -g @uncaged/cli-json-cas
bun add -g @ocas/cli
# or from the monorepo workspace:
bun link
```
**Binary name:** `json-cas` (points to `src/index.ts`, run with Bun).
**Binary name:** `ocas` (points to `src/index.ts`, run with Bun).
In development:
```bash
bun packages/cli-json-cas/src/index.ts <command> [args]
bun packages/cli-ocas/src/index.ts <command> [args]
```
## CLI Usage
```
Usage: json-cas [--store <path>] [--json] <command> [args]
Usage: ocas [--store <path>] [--json] <command> [args]
```
### Global flags
| Flag | Description |
|------|-------------|
| `--store <path>` | Store directory (default: `~/.uncaged/json-cas`) |
| `--store <path>` | Store directory (default: `~/.uncaged/ocas`) |
| `--var-db <path>` | Variable database path (default: `<store>/variables.db`) |
| `--json` | Compact (single-line) JSON output |
@@ -51,10 +51,10 @@ is therefore self-describing and pipeable: feed any envelope into `render -p` to
raw, non-envelope text.
```jsonc
// json-cas has <hash>
// ocas has <hash>
{ "type": "AYHQD2YA9G667", "value": true }
// json-cas template set <schema-hash> --inline "Hi {{ payload.name }}"
// ocas template set <schema-hash> --inline "Hi {{ payload.name }}"
{ "type": "9YJZ09DDAYAWR", "value": { "schemaHash": "7XX5H51CVD9H0", "contentHash": "FC8WACA792B6F" } }
```
@@ -87,18 +87,18 @@ raw, non-envelope text.
```bash
# Register a schema (schemas are plain @schema nodes) and store a payload
json-cas put @schema ./schemas/item.json
ocas put @schema ./schemas/item.json
# → { "type": "...", "value": "0123456789ABC" } (the schema's type hash)
json-cas put 0123456789ABC ./payloads/item.json
ocas put 0123456789ABC ./payloads/item.json
# → { "type": "...", "value": "<content-hash>" }
json-cas get <content-hash> --json
json-cas verify <content-hash>
json-cas walk <content-hash> --format tree
ocas get <content-hash> --json
ocas verify <content-hash>
ocas walk <content-hash> --format tree
# List every registered schema, then extract the hashes with jq
json-cas list --type @schema | jq -r '.value[]'
ocas list --type @schema | jq -r '.value[]'
```
### Pipe composition
@@ -108,25 +108,25 @@ Because every command shares the `{ type, value }` envelope, output composes dir
```bash
# put emits a cas_ref hash envelope; render -p dereferences and renders the node
json-cas put @schema ./schemas/item.json | json-cas render -p
ocas put @schema ./schemas/item.json | ocas render -p
# render gc statistics
json-cas gc | json-cas render -p
ocas gc | ocas render -p
# render every schema referenced by a list result
json-cas list --type @schema | json-cas render -p
ocas list --type @schema | ocas render -p
```
### Templates
`template` commands manage the LiquidJS template bound to a schema (stored as a
`@ucas/template/text/<schema-hash>` variable). `render <hash>` uses the template registered
`@ocas/template/text/<schema-hash>` variable). `render <hash>` uses the template registered
for the node's type, falling back to YAML when none exists.
```bash
# Bind a template to a schema, then render a node of that type
json-cas template set 0123456789ABC --inline "Item: {{ payload.name }}"
json-cas render <content-hash>
ocas template set 0123456789ABC --inline "Item: {{ payload.name }}"
ocas render <content-hash>
# → Item: Widget
```
@@ -142,6 +142,6 @@ There is no separate `src/` module tree; the CLI is a single entry file. Tests (
| Setting | Default | Override |
|---------|---------|----------|
| Store directory | `~/.uncaged/json-cas` | `--store <path>` |
| Store directory | `~/.uncaged/ocas` | `--store <path>` |
No config file is read; all behavior is controlled via flags and command arguments.
@@ -1,17 +1,16 @@
{
"name": "@uncaged/cli-json-cas",
"name": "@ocas/cli",
"version": "0.6.0",
"type": "module",
"bin": {
"json-cas": "./src/index.ts",
"ucas": "./src/index.ts"
"ocas": "src/index.ts"
},
"scripts": {
"test": "bun test",
"prepublishOnly": "bash ../../scripts/check-workspace-deps.sh"
},
"dependencies": {
"@uncaged/json-cas": "^0.6.0",
"@uncaged/json-cas-fs": "^0.6.0"
"@ocas/core": "^0.6.0",
"@ocas/fs": "^0.6.0"
}
}
@@ -3,7 +3,7 @@
import { existsSync, readFileSync } from "node:fs";
import { homedir } from "node:os";
import { join, resolve } from "node:path";
import type { Hash, Store, VariableStore } from "@uncaged/json-cas";
import type { Hash, Store, VariableStore } from "@ocas/core";
import {
bootstrap,
CasNodeNotFoundError,
@@ -23,8 +23,8 @@ import {
verify,
walk,
wrapEnvelope,
} from "@uncaged/json-cas";
import { openStore as openFsStore } from "@uncaged/json-cas-fs";
} from "@ocas/core";
import { openStore as openFsStore } from "@ocas/fs";
// ---- Argument parsing ----
@@ -87,7 +87,7 @@ function parseArgs(argv: string[]): { flags: Flags; positional: string[] } {
const { flags, positional } = parseArgs(process.argv.slice(2));
const defaultStorePath = join(homedir(), ".uncaged", "json-cas");
const defaultStorePath = join(homedir(), ".ocas");
const storePath =
typeof flags.store === "string" ? flags.store : defaultStorePath;
const compact = flags.json === true;
@@ -203,7 +203,7 @@ async function cmdPut(args: string[]): Promise<void> {
const file = isPipe ? undefined : args[1];
if (!typeHashOrAlias || (!isPipe && !file))
die(
"Usage: json-cas put <type-hash> <file.json>\n json-cas put <type-hash> --pipe/-p",
"Usage: ocas put <type-hash> <file.json>\n ocas put <type-hash> --pipe/-p",
);
if (isPipe && args[1])
die("Cannot use --pipe/-p with a file argument. Use one or the other.");
@@ -250,7 +250,7 @@ async function cmdPut(args: string[]): Promise<void> {
async function cmdGet(args: string[]): Promise<void> {
const hash = args[0];
if (!hash) die("Usage: json-cas get <hash>");
if (!hash) die("Usage: ocas get <hash>");
const store = await openStore();
const node = store.get(hash);
if (node === null) die(`Node not found: ${hash}`);
@@ -259,14 +259,14 @@ async function cmdGet(args: string[]): Promise<void> {
async function cmdHas(args: string[]): Promise<void> {
const hash = args[0];
if (!hash) die("Usage: json-cas has <hash>");
if (!hash) die("Usage: ocas has <hash>");
const store = await openStore();
out(await wrapEnvelope(store, "@output/has", store.has(hash)));
}
async function cmdVerify(args: string[]): Promise<void> {
const hash = args[0];
if (!hash) die("Usage: json-cas verify <hash>");
if (!hash) die("Usage: ocas verify <hash>");
const store = await openStore();
const node = store.get(hash);
if (node === null) die(`Node not found: ${hash}`);
@@ -282,7 +282,7 @@ async function cmdVerify(args: string[]): Promise<void> {
async function cmdRefs(args: string[]): Promise<void> {
const hash = args[0];
if (!hash) die("Usage: json-cas refs <hash>");
if (!hash) die("Usage: ocas refs <hash>");
const store = await openStore();
const node = store.get(hash);
if (node === null) die(`Node not found: ${hash}`);
@@ -292,7 +292,7 @@ async function cmdRefs(args: string[]): Promise<void> {
async function cmdWalk(args: string[]): Promise<void> {
const hash = args[0];
if (!hash) die("Usage: json-cas walk <hash> [--format tree]");
if (!hash) die("Usage: ocas walk <hash> [--format tree]");
const store = await openStore();
const format = flags.format;
@@ -339,7 +339,7 @@ async function cmdHash(args: string[]): Promise<void> {
const file = isPipe ? undefined : args[1];
if (!typeHashOrAlias || (!isPipe && !file))
die(
"Usage: json-cas hash <type-hash> <file.json>\n json-cas hash <type-hash> --pipe/-p",
"Usage: ocas hash <type-hash> <file.json>\n ocas hash <type-hash> --pipe/-p",
);
if (isPipe && args[1])
die("Cannot use --pipe/-p with a file argument. Use one or the other.");
@@ -360,7 +360,7 @@ async function cmdRender(args: string[]): Promise<void> {
if (!isPipe && !hash) {
die(
"Usage: ucas render <hash> [--resolution <n>] [--decay <n>] [--epsilon <n>]\n ucas render --pipe/-p [--resolution <n>] [--decay <n>] [--epsilon <n>]",
"Usage: ocas render <hash> [--resolution <n>] [--decay <n>] [--epsilon <n>]\n ocas render --pipe/-p [--resolution <n>] [--decay <n>] [--epsilon <n>]",
);
}
@@ -466,7 +466,7 @@ async function cmdVarSet(args: string[]): Promise<void> {
const tagFlags = flags.tag;
if (!name || !value) {
die("Usage: json-cas var set <name> <hash> [--tag <tag>...]");
die("Usage: ocas var set <name> <hash> [--tag <tag>...]");
}
const store = await openStore();
@@ -517,7 +517,7 @@ async function cmdVarGet(args: string[]): Promise<void> {
const schema = flags.schema as string | undefined;
if (!name || !schema) {
die("Usage: json-cas var get <name> --schema <hash>");
die("Usage: ocas var get <name> --schema <hash>");
}
const store = await openStore();
@@ -539,7 +539,7 @@ async function cmdVarDelete(args: string[]): Promise<void> {
const schema = flags.schema as string | undefined;
if (!name) {
die("Usage: json-cas var delete <name> [--schema <hash>]");
die("Usage: ocas var delete <name> [--schema <hash>]");
}
const store = await openStore();
@@ -570,12 +570,12 @@ async function cmdVarTag(args: string[]): Promise<void> {
const schema = flags.schema as string | undefined;
if (!name || !schema) {
die("Usage: json-cas var tag <name> --schema <hash> <operations...>");
die("Usage: ocas var tag <name> --schema <hash> <operations...>");
}
const tagArgs = args.slice(1);
if (tagArgs.length === 0) {
die("Usage: json-cas var tag <name> --schema <hash> <operations...>");
die("Usage: ocas var tag <name> --schema <hash> <operations...>");
}
const store = await openStore();
@@ -649,7 +649,7 @@ async function cmdTemplateSet(args: string[]): Promise<void> {
const inlineFlag = flags.inline;
if (!schemaHash) {
die("Usage: json-cas template set <schema-hash> <file> | --inline <text>");
die("Usage: ocas template set <schema-hash> <file> | --inline <text>");
}
const store = await openStore();
@@ -675,18 +675,14 @@ async function cmdTemplateSet(args: string[]): Promise<void> {
// --inline flag present but no value
const contentArg = args[1];
if (!contentArg) {
die(
"Usage: json-cas template set <schema-hash> <file> | --inline <text>",
);
die("Usage: ocas template set <schema-hash> <file> | --inline <text>");
}
content = contentArg;
} else {
// File mode
const file = args[1];
if (!file) {
die(
"Usage: json-cas template set <schema-hash> <file> | --inline <text>",
);
die("Usage: ocas template set <schema-hash> <file> | --inline <text>");
}
if (!existsSync(file)) {
die(`Error: File not found: ${file}`);
@@ -698,8 +694,8 @@ async function cmdTemplateSet(args: string[]): Promise<void> {
const stringHash = await resolveTypeHash("@string");
const contentHash = await store.put(stringHash, content);
// Create variable binding: @ucas/template/text/<schema-hash>
const varName = `@ucas/template/text/${schemaHash}`;
// Create variable binding: @ocas/template/text/<schema-hash>
const varName = `@ocas/template/text/${schemaHash}`;
varStore.set(varName, contentHash);
out(
@@ -722,14 +718,14 @@ async function cmdTemplateGet(args: string[]): Promise<void> {
const schemaHash = args[0];
if (!schemaHash) {
die("Usage: json-cas template get <schema-hash>");
die("Usage: ocas template get <schema-hash>");
}
const store = await openStore();
const varStore = createVariableStore(resolve(varDbPath), store);
try {
const varName = `@ucas/template/text/${schemaHash}`;
const varName = `@ocas/template/text/${schemaHash}`;
const stringHash = await resolveTypeHash("@string");
const variable = varStore.get(varName, stringHash);
@@ -758,12 +754,12 @@ async function cmdTemplateList(_args: string[]): Promise<void> {
try {
const stringHash = await resolveTypeHash("@string");
const variables = varStore.list({
namePrefix: "@ucas/template/text/",
namePrefix: "@ocas/template/text/",
schema: stringHash,
});
const templates = variables.map((v) => ({
schemaHash: v.name.replace("@ucas/template/text/", ""),
schemaHash: v.name.replace("@ocas/template/text/", ""),
contentHash: v.value,
}));
@@ -777,14 +773,14 @@ async function cmdTemplateDelete(args: string[]): Promise<void> {
const schemaHash = args[0];
if (!schemaHash) {
die("Usage: json-cas template delete <schema-hash>");
die("Usage: ocas template delete <schema-hash>");
}
const store = await openStore();
const varStore = createVariableStore(resolve(varDbPath), store);
try {
const varName = `@ucas/template/text/${schemaHash}`;
const varName = `@ocas/template/text/${schemaHash}`;
const stringHash = await resolveTypeHash("@string");
varStore.remove(varName, stringHash);
@@ -816,7 +812,7 @@ async function cmdGc(_args: string[]): Promise<void> {
async function cmdList(_args: string[]): Promise<void> {
const typeFlag = flags.type;
if (typeof typeFlag !== "string")
die("Usage: json-cas list --type <hash-or-alias>");
die("Usage: ocas list --type <hash-or-alias>");
const typeHash = await resolveTypeHash(typeFlag);
const store = await openStore();
const hashes = Array.from(store.listByType(typeHash));
@@ -837,7 +833,7 @@ async function cmdListSchema(_args: string[]): Promise<void> {
function printUsage(): void {
console.log(`\
Usage: json-cas [--store <path>] [--json] <command> [args]
Usage: ocas [--store <path>] [--json] <command> [args]
All JSON commands emit a { type, value } envelope. The type is the hash of the
command's @output/* schema (shown in parentheses); pipe any envelope into
@@ -868,7 +864,7 @@ Commands:
gc Run garbage collection (@output/gc)
Flags:
--store <path> Store directory (default: ~/.uncaged/json-cas)
--store <path> Store directory (default: ~/.ocas)
--var-db <path> Variable database path (default: <store>/variables.db)
--json Compact JSON output
--schema <hash> Schema hash filter for var get/delete/tag/list
@@ -2,12 +2,12 @@
exports[`Phase 7: Edge Cases 7.1 get non-existent hash errors gracefully 1`] = `"Node not found: AAAAAAAAAAAAA"`;
exports[`Phase 7: Edge Cases 7.3 var set empty name errors 1`] = `"Usage: json-cas var set <name> <hash> [--tag <tag>...]"`;
exports[`Phase 7: Edge Cases 7.3 var set empty name errors 1`] = `"Usage: ocas var set <name> <hash> [--tag <tag>...]"`;
exports[`Phase 7: Edge Cases 7.4 var set name with invalid chars errors 1`] = `"Error: Invalid variable name "invalid name!": Segment "invalid name!" contains invalid characters (only @, a-z, A-Z, 0-9, ., _, - allowed)"`;
exports[`Phase 7: Edge Cases 7.5 no subcommand shows help text 1`] = `
"Usage: json-cas [--store <path>] [--json] <command> [args]
"Usage: ocas [--store <path>] [--json] <command> [args]
All JSON commands emit a { type, value } envelope. The type is the hash of the
command's @output/* schema (shown in parentheses); pipe any envelope into
@@ -38,7 +38,7 @@ Commands:
gc Run garbage collection (@output/gc)
Flags:
--store <path> Store directory (default: ~/.uncaged/json-cas)
--store <path> Store directory (default: ~/.ocas)
--var-db <path> Variable database path (default: <store>/variables.db)
--json Compact JSON output
--schema <hash> Schema hash filter for var get/delete/tag/list
@@ -52,40 +52,40 @@ Flags:
exports[`Phase 3: Variable System 3.1 var set creates variable 1`] = `
{
"type": "5KVHSESSX7N96",
"type": "50CG6QYD1FY8J",
"value": {
"labels": [],
"name": "myapp/config",
"schema": "8WAZV39SD724T",
"schema": "FRBAB1BF0ZBCS",
"tags": {},
"value": "6KZ930XYK2MHB",
"value": "9W3MGR3184QYE",
},
}
`;
exports[`Phase 3: Variable System 3.2 var get returns variable 1`] = `
{
"type": "7D4R2MJ1EYF08",
"type": "DSXYC9DNCYAK0",
"value": {
"labels": [],
"name": "myapp/config",
"schema": "8WAZV39SD724T",
"schema": "FRBAB1BF0ZBCS",
"tags": {},
"value": "6KZ930XYK2MHB",
"value": "9W3MGR3184QYE",
},
}
`;
exports[`Phase 3: Variable System 3.3 var list shows all variables 1`] = `
{
"type": "E8158M25YNR9M",
"type": "8F5ENFRC57Y7H",
"value": [
{
"labels": [],
"name": "myapp/config",
"schema": "8WAZV39SD724T",
"schema": "FRBAB1BF0ZBCS",
"tags": {},
"value": "6KZ930XYK2MHB",
"value": "9W3MGR3184QYE",
},
],
}
@@ -93,14 +93,14 @@ exports[`Phase 3: Variable System 3.3 var list shows all variables 1`] = `
exports[`Phase 3: Variable System 3.4 var list prefix filters by prefix 1`] = `
{
"type": "E8158M25YNR9M",
"type": "8F5ENFRC57Y7H",
"value": [
{
"labels": [],
"name": "myapp/config",
"schema": "8WAZV39SD724T",
"schema": "FRBAB1BF0ZBCS",
"tags": {},
"value": "6KZ930XYK2MHB",
"value": "9W3MGR3184QYE",
},
],
}
@@ -108,48 +108,48 @@ exports[`Phase 3: Variable System 3.4 var list prefix filters by prefix 1`] = `
exports[`Phase 3: Variable System 3.5 var set upsert updates existing variable 1`] = `
{
"type": "5KVHSESSX7N96",
"type": "50CG6QYD1FY8J",
"value": {
"labels": [],
"name": "myapp/config",
"schema": "8WAZV39SD724T",
"schema": "FRBAB1BF0ZBCS",
"tags": {},
"value": "AE80669JYG4K2",
"value": "A6QPKJAFR68NP",
},
}
`;
exports[`Phase 3: Variable System 3.6 var tag adds kv tag and label 1`] = `
{
"type": "2WX4XRYSACRWR",
"type": "6D1HG60NTZY87",
"value": {
"labels": [
"important",
],
"name": "myapp/config",
"schema": "8WAZV39SD724T",
"schema": "FRBAB1BF0ZBCS",
"tags": {
"env": "prod",
},
"value": "6KZ930XYK2MHB",
"value": "9W3MGR3184QYE",
},
}
`;
exports[`Phase 3: Variable System 3.7 var list --tag env:prod filters by kv tag 1`] = `
{
"type": "E8158M25YNR9M",
"type": "8F5ENFRC57Y7H",
"value": [
{
"labels": [
"important",
],
"name": "myapp/config",
"schema": "8WAZV39SD724T",
"schema": "FRBAB1BF0ZBCS",
"tags": {
"env": "prod",
},
"value": "6KZ930XYK2MHB",
"value": "9W3MGR3184QYE",
},
],
}
@@ -157,18 +157,18 @@ exports[`Phase 3: Variable System 3.7 var list --tag env:prod filters by kv tag
exports[`Phase 3: Variable System 3.8 var list --tag important filters by label 1`] = `
{
"type": "E8158M25YNR9M",
"type": "8F5ENFRC57Y7H",
"value": [
{
"labels": [
"important",
],
"name": "myapp/config",
"schema": "8WAZV39SD724T",
"schema": "FRBAB1BF0ZBCS",
"tags": {
"env": "prod",
},
"value": "6KZ930XYK2MHB",
"value": "9W3MGR3184QYE",
},
],
}
@@ -176,62 +176,62 @@ exports[`Phase 3: Variable System 3.8 var list --tag important filters by label
exports[`Phase 3: Variable System 3.9 var tag remove deletes label 1`] = `
{
"type": "2WX4XRYSACRWR",
"type": "6D1HG60NTZY87",
"value": {
"labels": [],
"name": "myapp/config",
"schema": "8WAZV39SD724T",
"schema": "FRBAB1BF0ZBCS",
"tags": {
"env": "prod",
},
"value": "6KZ930XYK2MHB",
"value": "9W3MGR3184QYE",
},
}
`;
exports[`Phase 3: Variable System 3.10 var delete removes variable 1`] = `
{
"type": "F89626QEK09YY",
"type": "25VAMSJT0X51K",
"value": [
{
"labels": [],
"name": "myapp/config",
"schema": "8WAZV39SD724T",
"schema": "FRBAB1BF0ZBCS",
"tags": {
"env": "prod",
},
"value": "6KZ930XYK2MHB",
"value": "9W3MGR3184QYE",
},
],
}
`;
exports[`Phase 3: Variable System 3.11 var get deleted variable returns not found 1`] = `"Error: Variable not found: name=myapp/config, schema=8WAZV39SD724T"`;
exports[`Phase 3: Variable System 3.11 var get deleted variable returns not found 1`] = `"Error: Variable not found: name=myapp/config, schema=FRBAB1BF0ZBCS"`;
exports[`Phase 4: Template System 4.1 template set registers template 1`] = `
{
"type": "A8RGY9B1JSCDJ",
"type": "7R13JNSN53R67",
"value": {
"contentHash": "0359SHMP7VBFD",
"schemaHash": "8WAZV39SD724T",
"contentHash": "6WW8WNB38GTTP",
"schemaHash": "FRBAB1BF0ZBCS",
},
}
`;
exports[`Phase 4: Template System 4.2 template get returns template text 1`] = `
{
"type": "64FP3TWKK69YH",
"type": "0B0HBHZGYHR84",
"value": "Name: {{ payload.name }}, Age: {{ payload.age }}",
}
`;
exports[`Phase 4: Template System 4.3 template list shows registered templates 1`] = `
{
"type": "BE20H482GBNM3",
"type": "5GE3Y0EQS0HP4",
"value": [
{
"contentHash": "0359SHMP7VBFD",
"schemaHash": "8WAZV39SD724T",
"contentHash": "6WW8WNB38GTTP",
"schemaHash": "FRBAB1BF0ZBCS",
},
],
}
@@ -239,11 +239,11 @@ exports[`Phase 4: Template System 4.3 template list shows registered templates 1
exports[`Phase 4: Template System 4.4 template delete removes template 1`] = `
{
"type": "0FXB2GS8Y9R1R",
"type": "BY7BGZJND3N7R",
"value": {
"deleted": true,
},
}
`;
exports[`Phase 4: Template System 4.5 template get deleted template returns not found 1`] = `"Error: Template not found for schema: 8WAZV39SD724T"`;
exports[`Phase 4: Template System 4.5 template get deleted template returns not found 1`] = `"Error: Template not found for schema: FRBAB1BF0ZBCS"`;
@@ -2,13 +2,13 @@
exports[`Phase 1: CAS Core 1.6 get returns node JSON (snapshot) 1`] = `
{
"type": "CE40D09H75NFZ",
"type": "CCVN1ECY8JKQ0",
"value": {
"payload": {
"age": 30,
"name": "Alice",
},
"type": "8WAZV39SD724T",
"type": "FRBAB1BF0ZBCS",
},
}
`;
@@ -2,23 +2,23 @@
exports[`Phase 1: CAS Core 1.9 verify returns ok for valid node 1`] = `
{
"type": "BZ31JDDWX2AWH",
"type": "52HEFB52BD0GF",
"value": "ok",
}
`;
exports[`Phase 1: CAS Core 1.10 refs lists direct references (snapshot) 1`] = `
"{
"type": "DG8SAB75PV9P7",
"type": "35QJB2WESFFGQ",
"value": []
}"
`;
exports[`Phase 1: CAS Core 1.11 walk shows traversal tree (snapshot) 1`] = `
"{
"type": "EVHG7Q7FK83H0",
"type": "4HG6MD3XG5H5C",
"value": [
"6KZ930XYK2MHB"
"9W3MGR3184QYE"
]
}"
`;
@@ -13,7 +13,7 @@ beforeEach(() => {
// Create unique temp directory for each test
testDir = join(
tmpdir(),
`json-cas-cli-test-${Date.now()}-${Math.random().toString(36).slice(2)}`,
`ocas-cli-test-${Date.now()}-${Math.random().toString(36).slice(2)}`,
);
storePath = join(testDir, "store");
cliPath = join(import.meta.dir, "../src/index.ts");
@@ -67,7 +67,7 @@ function envValue(json: string): unknown {
}
describe("@ Alias Resolution - put", () => {
test("ucas put @string <file> should resolve alias", async () => {
test("ocas put @string <file> should resolve alias", async () => {
await runCliAlias("init");
const payloadFile = join(testDir, "payload.json");
@@ -85,7 +85,7 @@ describe("@ Alias Resolution - put", () => {
expect(envValue(stdout)).toMatch(/^[0-9A-HJKMNP-TV-Z]{13}$/);
});
test("ucas put @number <file> should resolve alias", async () => {
test("ocas put @number <file> should resolve alias", async () => {
await runCliAlias("init");
const payloadFile = join(testDir, "payload.json");
@@ -101,7 +101,7 @@ describe("@ Alias Resolution - put", () => {
expect(envValue(stdout)).toMatch(/^[0-9A-HJKMNP-TV-Z]{13}$/);
});
test("ucas put @object <file> should resolve alias", async () => {
test("ocas put @object <file> should resolve alias", async () => {
await runCliAlias("init");
const payloadFile = join(testDir, "payload.json");
@@ -117,7 +117,7 @@ describe("@ Alias Resolution - put", () => {
expect(envValue(stdout)).toMatch(/^[0-9A-HJKMNP-TV-Z]{13}$/);
});
test("ucas put @invalid <file> should fail", async () => {
test("ocas put @invalid <file> should fail", async () => {
await runCliAlias("init");
const payloadFile = join(testDir, "payload.json");
@@ -133,7 +133,7 @@ describe("@ Alias Resolution - put", () => {
expect(stderr.length).toBeGreaterThan(0);
});
test("ucas put @schema with nested type constraints should succeed", async () => {
test("ocas put @schema with nested type constraints should succeed", async () => {
await runCliAlias("init");
const schemaFile = join(testDir, "constrained-schema.json");
@@ -168,7 +168,7 @@ describe("@ Alias Resolution - put", () => {
});
describe("@ Alias Resolution - hash", () => {
test("ucas hash @string <file> should compute hash without storing", async () => {
test("ocas hash @string <file> should compute hash without storing", async () => {
await runCliAlias("init");
const payloadFile = join(testDir, "payload.json");
@@ -7,20 +7,22 @@ import { envValue, stripVolatile } from "./helpers";
const entrypoint = resolve(import.meta.dir, "../src/index.ts");
const pkgPath = resolve(import.meta.dir, "../package.json");
// --- ucas command alias tests (from cli.test.ts) ---
// --- ocas command alias tests (from cli.test.ts) ---
describe("ucas command alias", () => {
test("T1: ucas bin entry exists in package.json", async () => {
describe("ocas binary", () => {
test("T1: ocas bin entry exists in package.json", async () => {
const pkg = await Bun.file(pkgPath).json();
expect(pkg.bin.ucas).toBe("./src/index.ts");
expect(pkg.bin.ocas).toBe("src/index.ts");
});
test("T2: json-cas bin entry is preserved in package.json", async () => {
test("T2: no legacy bin entries (json-cas, ucas)", async () => {
const pkg = await Bun.file(pkgPath).json();
expect(pkg.bin["json-cas"]).toBe("./src/index.ts");
expect(pkg.bin["json-cas"]).toBeUndefined();
expect(pkg.bin["ucas"]).toBeUndefined();
expect(Object.keys(pkg.bin)).toEqual(["ocas"]);
});
test("T3: ucas command is executable and shows help", async () => {
test("T3: ocas command is executable and shows help", async () => {
const proc = Bun.spawn(["bun", entrypoint, "--help"], {
stdout: "pipe",
stderr: "pipe",
@@ -30,11 +32,6 @@ describe("ucas command alias", () => {
expect(exitCode).toBe(0);
expect(stdout.length).toBeGreaterThan(0);
});
test("T4: both commands point to the same entrypoint", async () => {
const pkg = await Bun.file(pkgPath).json();
expect(pkg.bin.ucas).toBe(pkg.bin["json-cas"]);
});
});
// --- e2e Phase 7: Edge Cases ---
@@ -59,7 +56,7 @@ describe("Phase 7: Edge Cases", () => {
}
beforeAll(async () => {
tmpStore = mkdtempSync(join(tmpdir(), "json-cas-e2e-"));
tmpStore = mkdtempSync(join(tmpdir(), "ocas-e2e-"));
varDbPath = join(tmpStore, "variables.db");
const schemaFile = join(tmpStore, "test-schema.json");
@@ -72,8 +69,8 @@ describe("Phase 7: Edge Cases", () => {
additionalProperties: false,
}),
);
const { openStore: openFsStore } = await import("@uncaged/json-cas-fs");
const { putSchema } = await import("@uncaged/json-cas");
const { openStore: openFsStore } = await import("@ocas/fs");
const { putSchema } = await import("@ocas/core");
const store = await openFsStore(tmpStore);
typeHash = await putSchema(
store,
@@ -178,7 +175,7 @@ describe("Phase 3: Variable System", () => {
}
beforeAll(async () => {
tmpStore = mkdtempSync(join(tmpdir(), "json-cas-e2e-"));
tmpStore = mkdtempSync(join(tmpdir(), "ocas-e2e-"));
varDbPath = join(tmpStore, "variables.db");
const schemaFile = join(tmpStore, "test-schema.json");
@@ -191,8 +188,8 @@ describe("Phase 3: Variable System", () => {
additionalProperties: false,
}),
);
const { openStore: openFsStore } = await import("@uncaged/json-cas-fs");
const { putSchema } = await import("@uncaged/json-cas");
const { openStore: openFsStore } = await import("@ocas/fs");
const { putSchema } = await import("@ocas/core");
const store = await openFsStore(tmpStore);
typeHash = await putSchema(
store,
@@ -367,7 +364,7 @@ describe("Phase 4: Template System", () => {
}
beforeAll(async () => {
tmpStore = mkdtempSync(join(tmpdir(), "json-cas-e2e-"));
tmpStore = mkdtempSync(join(tmpdir(), "ocas-e2e-"));
varDbPath = join(tmpStore, "variables.db");
const schemaFile = join(tmpStore, "test-schema.json");
@@ -380,8 +377,8 @@ describe("Phase 4: Template System", () => {
additionalProperties: false,
}),
);
const { openStore: openFsStore } = await import("@uncaged/json-cas-fs");
const { putSchema } = await import("@uncaged/json-cas");
const { openStore: openFsStore } = await import("@ocas/fs");
const { putSchema } = await import("@ocas/core");
const store = await openFsStore(tmpStore);
typeHash = await putSchema(
store,
@@ -12,7 +12,7 @@ let typeHash: string;
let nodeHash: string;
beforeAll(async () => {
tmpStore = mkdtempSync(join(tmpdir(), "json-cas-e2e-"));
tmpStore = mkdtempSync(join(tmpdir(), "ocas-e2e-"));
varDbPath = join(tmpStore, "variables.db");
const schemaFile = join(tmpStore, "test-schema.json");
@@ -25,8 +25,8 @@ beforeAll(async () => {
additionalProperties: false,
}),
);
const { openStore: openFsStore } = await import("@uncaged/json-cas-fs");
const { putSchema } = await import("@uncaged/json-cas");
const { openStore: openFsStore } = await import("@ocas/fs");
const { putSchema } = await import("@ocas/core");
const store = await openFsStore(tmpStore);
typeHash = await putSchema(
store,
@@ -7,9 +7,9 @@ import {
} from "node:fs";
import { tmpdir } from "node:os";
import { join, resolve } from "node:path";
import type { JSONSchema } from "@uncaged/json-cas";
import { putSchema } from "@uncaged/json-cas";
import { openStore as openFsStore } from "@uncaged/json-cas-fs";
import type { JSONSchema } from "@ocas/core";
import { putSchema } from "@ocas/core";
import { openStore as openFsStore } from "@ocas/fs";
export {
join,
@@ -2,14 +2,14 @@ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
import { mkdirSync, mkdtempSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { BOOTSTRAP_STORE } from "@uncaged/json-cas";
import { openStore as openFsStore } from "@uncaged/json-cas-fs";
import { BOOTSTRAP_STORE } from "@ocas/core";
import { openStore as openFsStore } from "@ocas/fs";
import { envValue, runCli } from "./helpers.js";
let storePath: string;
beforeEach(() => {
storePath = mkdtempSync(join(tmpdir(), "json-cas-list-meta-schema-"));
storePath = mkdtempSync(join(tmpdir(), "ocas-list-meta-schema-"));
mkdirSync(storePath, { recursive: true });
});
@@ -98,7 +98,7 @@ describe("F1. output schemas registered", () => {
const node = envValue(getOut) as {
payload: { title?: string };
};
expect(node.payload.title).toBe("ucas list-meta result");
expect(node.payload.title).toBe("ocas list-meta result");
const { stdout: schemaOut } = await runCli(["list-schema"], storePath);
const schemaParsed = JSON.parse(schemaOut) as { type: string };
@@ -110,7 +110,7 @@ describe("F1. output schemas registered", () => {
const schNode = envValue(getSchOut) as {
payload: { title?: string };
};
expect(schNode.payload.title).toBe("ucas list-schema result");
expect(schNode.payload.title).toBe("ocas list-schema result");
});
});
@@ -12,7 +12,7 @@ let typeHash: string;
let nodeHash: string;
beforeAll(async () => {
tmpStore = mkdtempSync(join(tmpdir(), "json-cas-e2e-"));
tmpStore = mkdtempSync(join(tmpdir(), "ocas-e2e-"));
varDbPath = join(tmpStore, "variables.db");
const schemaFile = join(tmpStore, "test-schema.json");
@@ -25,8 +25,8 @@ beforeAll(async () => {
additionalProperties: false,
}),
);
const { openStore: openFsStore } = await import("@uncaged/json-cas-fs");
const { putSchema } = await import("@uncaged/json-cas");
const { openStore: openFsStore } = await import("@ocas/fs");
const { putSchema } = await import("@ocas/core");
const store = await openFsStore(tmpStore);
typeHash = await putSchema(
store,
@@ -12,7 +12,7 @@ let typeHash: string;
let nodeHash: string;
beforeAll(async () => {
tmpStore = mkdtempSync(join(tmpdir(), "json-cas-e2e-"));
tmpStore = mkdtempSync(join(tmpdir(), "ocas-e2e-"));
varDbPath = join(tmpStore, "variables.db");
const schemaFile = join(tmpStore, "test-schema.json");
@@ -28,8 +28,8 @@ beforeAll(async () => {
additionalProperties: false,
}),
);
const { openStore: openFsStore } = await import("@uncaged/json-cas-fs");
const { putSchema } = await import("@uncaged/json-cas");
const { openStore: openFsStore } = await import("@ocas/fs");
const { putSchema } = await import("@ocas/core");
const store = await openFsStore(tmpStore);
typeHash = await putSchema(
store,
@@ -2,15 +2,15 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test";
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join, resolve } from "node:path";
import { bootstrap } from "@uncaged/json-cas";
import { createFsStore } from "@uncaged/json-cas-fs";
import { bootstrap } from "@ocas/core";
import { createFsStore } from "@ocas/fs";
import { envValue, putSchemaFile, runCli, runCliWithStdin } from "./helpers";
const entrypoint = resolve(import.meta.dir, "../src/index.ts");
// --- Standalone render tests from cli.test.ts ---
describe("ucas render command", () => {
describe("ocas render command", () => {
test("R1: render requires hash argument", async () => {
const { exitCode, stderr } = await runCli(["render"]);
expect(exitCode).not.toBe(0);
@@ -18,7 +18,7 @@ describe("ucas render command", () => {
});
test("R2: render with missing hash shows error", async () => {
const tmpStore = mkdtempSync(join(tmpdir(), "json-cas-test-"));
const tmpStore = mkdtempSync(join(tmpdir(), "ocas-test-"));
try {
await runCli(["init"], tmpStore);
const { exitCode, stderr } = await runCli(
@@ -35,7 +35,7 @@ describe("ucas render command", () => {
});
test("R3: render with invalid numeric flag fails", async () => {
const tmpStore = mkdtempSync(join(tmpdir(), "json-cas-test-"));
const tmpStore = mkdtempSync(join(tmpdir(), "ocas-test-"));
try {
await runCli(["init"], tmpStore);
const { exitCode, stderr } = await runCli(
@@ -88,7 +88,7 @@ describe("Phase 5: Render", () => {
}
beforeAll(async () => {
tmpStore = mkdtempSync(join(tmpdir(), "json-cas-e2e-"));
tmpStore = mkdtempSync(join(tmpdir(), "ocas-e2e-"));
varDbPath = join(tmpStore, "variables.db");
const schemaFile = join(tmpStore, "test-schema.json");
@@ -101,8 +101,8 @@ describe("Phase 5: Render", () => {
additionalProperties: false,
}),
);
const { openStore: openFsStore } = await import("@uncaged/json-cas-fs");
const { putSchema } = await import("@uncaged/json-cas");
const { openStore: openFsStore } = await import("@ocas/fs");
const { putSchema } = await import("@ocas/core");
const store = await openFsStore(tmpStore);
typeHash = await putSchema(
store,
@@ -154,7 +154,7 @@ describe("Phase 5: Render", () => {
describe("Suite 6: CLI Integration with Templates", () => {
test("6.1 CLI with Template (Default Parameters)", async () => {
const tmpStore = mkdtempSync(join(tmpdir(), "json-cas-test-"));
const tmpStore = mkdtempSync(join(tmpdir(), "ocas-test-"));
try {
// Initialize store
await runCli(["init"], tmpStore);
@@ -190,7 +190,7 @@ describe("Suite 6: CLI Integration with Templates", () => {
// Register template
await runCli(
["var", "set", `@ucas/template/text/${schemaHash.trim()}`, tmplHash],
["var", "set", `@ocas/template/text/${schemaHash.trim()}`, tmplHash],
tmpStore,
);
@@ -208,7 +208,7 @@ describe("Suite 6: CLI Integration with Templates", () => {
});
test("6.2 CLI with Template + Custom Decay", async () => {
const tmpStore = mkdtempSync(join(tmpdir(), "json-cas-test-"));
const tmpStore = mkdtempSync(join(tmpdir(), "ocas-test-"));
try {
await runCli(["init"], tmpStore);
@@ -263,7 +263,7 @@ describe("Suite 6: CLI Integration with Templates", () => {
// Register template
await runCli(
["var", "set", `@ucas/template/text/${schemaHash.trim()}`, tmplHash],
["var", "set", `@ocas/template/text/${schemaHash.trim()}`, tmplHash],
tmpStore,
);
@@ -281,7 +281,7 @@ describe("Suite 6: CLI Integration with Templates", () => {
});
test("6.3 CLI with Template + All Parameters", async () => {
const tmpStore = mkdtempSync(join(tmpdir(), "json-cas-test-"));
const tmpStore = mkdtempSync(join(tmpdir(), "ocas-test-"));
try {
await runCli(["init"], tmpStore);
@@ -316,7 +316,7 @@ describe("Suite 6: CLI Integration with Templates", () => {
const tmplHash = envValue(tmplOut) as string;
await runCli(
["var", "set", `@ucas/template/text/${schemaHash.trim()}`, tmplHash],
["var", "set", `@ocas/template/text/${schemaHash.trim()}`, tmplHash],
tmpStore,
);
@@ -342,7 +342,7 @@ describe("Suite 6: CLI Integration with Templates", () => {
});
test("6.4 CLI with Non-templated Node (YAML Fallback)", async () => {
const tmpStore = mkdtempSync(join(tmpdir(), "json-cas-test-"));
const tmpStore = mkdtempSync(join(tmpdir(), "ocas-test-"));
try {
await runCli(["init"], tmpStore);
@@ -379,7 +379,7 @@ describe("Suite 6: CLI Integration with Templates", () => {
});
test("6.5 CLI Error: Invalid Decay Value", async () => {
const tmpStore = mkdtempSync(join(tmpdir(), "json-cas-test-"));
const tmpStore = mkdtempSync(join(tmpdir(), "ocas-test-"));
try {
await runCli(["init"], tmpStore);
@@ -414,7 +414,7 @@ describe("Suite 6: CLI Integration with Templates", () => {
});
test("R8: render with non-existent hash exits with error", async () => {
const tmpStore = mkdtempSync(join(tmpdir(), "json-cas-test-"));
const tmpStore = mkdtempSync(join(tmpdir(), "ocas-test-"));
try {
await runCli(["init"], tmpStore);
const { exitCode, stderr, stdout } = await runCli(
@@ -431,7 +431,7 @@ describe("Suite 6: CLI Integration with Templates", () => {
});
test("R9: render with valid hash exits successfully", async () => {
const tmpStore = mkdtempSync(join(tmpdir(), "json-cas-test-"));
const tmpStore = mkdtempSync(join(tmpdir(), "ocas-test-"));
try {
await runCli(["init"], tmpStore);
@@ -466,7 +466,7 @@ describe("Suite 6: CLI Integration with Templates", () => {
});
test("R10: render --pipe with valid envelope succeeds", async () => {
const tmpStore = mkdtempSync(join(tmpdir(), "json-cas-test-"));
const tmpStore = mkdtempSync(join(tmpdir(), "ocas-test-"));
try {
await runCli(["init"], tmpStore);
@@ -492,7 +492,7 @@ describe("Suite 6: CLI Integration with Templates", () => {
});
test("R11: render --pipe with invalid type hash still renders", async () => {
const tmpStore = mkdtempSync(join(tmpdir(), "json-cas-test-"));
const tmpStore = mkdtempSync(join(tmpdir(), "ocas-test-"));
try {
await runCli(["init"], tmpStore);
@@ -9,7 +9,7 @@ import { envValue, putSchemaFile, runCli } from "./helpers";
describe("Issue #50: Schema Validation in put", () => {
describe("Test Group 1: Valid Data (Regression Tests)", () => {
test("T1.1: Valid data matching schema should be accepted", async () => {
const tmpStore = mkdtempSync(join(tmpdir(), "json-cas-test-"));
const tmpStore = mkdtempSync(join(tmpdir(), "ocas-test-"));
try {
await runCli(["init"], tmpStore);
@@ -49,7 +49,7 @@ describe("Issue #50: Schema Validation in put", () => {
});
test("T1.2: Valid data with optional properties should be accepted", async () => {
const tmpStore = mkdtempSync(join(tmpdir(), "json-cas-test-"));
const tmpStore = mkdtempSync(join(tmpdir(), "ocas-test-"));
try {
await runCli(["init"], tmpStore);
@@ -86,7 +86,7 @@ describe("Issue #50: Schema Validation in put", () => {
});
test("T1.3: Valid data with nested objects should be accepted", async () => {
const tmpStore = mkdtempSync(join(tmpdir(), "json-cas-test-"));
const tmpStore = mkdtempSync(join(tmpdir(), "ocas-test-"));
try {
await runCli(["init"], tmpStore);
@@ -134,7 +134,7 @@ describe("Issue #50: Schema Validation in put", () => {
});
test("T1.4: Valid data using @ alias for type-hash should be accepted", async () => {
const tmpStore = mkdtempSync(join(tmpdir(), "json-cas-test-"));
const tmpStore = mkdtempSync(join(tmpdir(), "ocas-test-"));
try {
await runCli(["init"], tmpStore);
@@ -156,7 +156,7 @@ describe("Issue #50: Schema Validation in put", () => {
describe("Test Group 2: Type Mismatches (New Validation)", () => {
test("T2.1: Wrong property type should be rejected", async () => {
const tmpStore = mkdtempSync(join(tmpdir(), "json-cas-test-"));
const tmpStore = mkdtempSync(join(tmpdir(), "ocas-test-"));
try {
await runCli(["init"], tmpStore);
@@ -200,7 +200,7 @@ describe("Issue #50: Schema Validation in put", () => {
});
test("T2.2: Missing required property should be rejected", async () => {
const tmpStore = mkdtempSync(join(tmpdir(), "json-cas-test-"));
const tmpStore = mkdtempSync(join(tmpdir(), "ocas-test-"));
try {
await runCli(["init"], tmpStore);
@@ -234,7 +234,7 @@ describe("Issue #50: Schema Validation in put", () => {
});
test("T2.3: Additional properties when disallowed should be rejected", async () => {
const tmpStore = mkdtempSync(join(tmpdir(), "json-cas-test-"));
const tmpStore = mkdtempSync(join(tmpdir(), "ocas-test-"));
try {
await runCli(["init"], tmpStore);
@@ -270,7 +270,7 @@ describe("Issue #50: Schema Validation in put", () => {
});
test("T2.4: Wrong root type should be rejected", async () => {
const tmpStore = mkdtempSync(join(tmpdir(), "json-cas-test-"));
const tmpStore = mkdtempSync(join(tmpdir(), "ocas-test-"));
try {
await runCli(["init"], tmpStore);
@@ -302,7 +302,7 @@ describe("Issue #50: Schema Validation in put", () => {
});
test("T2.5: Nested type mismatch should be rejected", async () => {
const tmpStore = mkdtempSync(join(tmpdir(), "json-cas-test-"));
const tmpStore = mkdtempSync(join(tmpdir(), "ocas-test-"));
try {
await runCli(["init"], tmpStore);
@@ -346,7 +346,7 @@ describe("Issue #50: Schema Validation in put", () => {
describe("Test Group 3: Schema Errors (Edge Cases)", () => {
test("T3.1: Non-existent type-hash should fail gracefully", async () => {
const tmpStore = mkdtempSync(join(tmpdir(), "json-cas-test-"));
const tmpStore = mkdtempSync(join(tmpdir(), "ocas-test-"));
try {
await runCli(["init"], tmpStore);
@@ -366,7 +366,7 @@ describe("Issue #50: Schema Validation in put", () => {
});
test("T3.3: Invalid @ alias should fail before validation", async () => {
const tmpStore = mkdtempSync(join(tmpdir(), "json-cas-test-"));
const tmpStore = mkdtempSync(join(tmpdir(), "ocas-test-"));
try {
await runCli(["init"], tmpStore);
@@ -388,7 +388,7 @@ describe("Issue #50: Schema Validation in put", () => {
describe("Test Group 4: Integration with Existing Features", () => {
test("T4.1: Hash command should not validate (dry-run consistency)", async () => {
const tmpStore = mkdtempSync(join(tmpdir(), "json-cas-test-"));
const tmpStore = mkdtempSync(join(tmpdir(), "ocas-test-"));
try {
await runCli(["init"], tmpStore);
@@ -423,7 +423,7 @@ describe("Issue #50: Schema Validation in put", () => {
});
test("T4.2: Validation respects cas_ref format in schemas", async () => {
const tmpStore = mkdtempSync(join(tmpdir(), "json-cas-test-"));
const tmpStore = mkdtempSync(join(tmpdir(), "ocas-test-"));
try {
await runCli(["init"], tmpStore);
@@ -466,7 +466,7 @@ describe("Issue #50: Schema Validation in put", () => {
});
test("T4.3: Schema self-validation still works", async () => {
const tmpStore = mkdtempSync(join(tmpdir(), "json-cas-test-"));
const tmpStore = mkdtempSync(join(tmpdir(), "ocas-test-"));
try {
await runCli(["init"], tmpStore);
@@ -490,7 +490,7 @@ describe("Issue #50: Schema Validation in put", () => {
describe("Test Group 5: Error Message Quality", () => {
test("T5.1: Error message should be helpful", async () => {
const tmpStore = mkdtempSync(join(tmpdir(), "json-cas-test-"));
const tmpStore = mkdtempSync(join(tmpdir(), "ocas-test-"));
try {
await runCli(["init"], tmpStore);
@@ -522,7 +522,7 @@ describe("Issue #50: Schema Validation in put", () => {
});
test("T5.2: Error should go to stderr, not stdout", async () => {
const tmpStore = mkdtempSync(join(tmpdir(), "json-cas-test-"));
const tmpStore = mkdtempSync(join(tmpdir(), "ocas-test-"));
try {
await runCli(["init"], tmpStore);
@@ -563,7 +563,7 @@ describe("Phase 2: Schema Validation", () => {
const entrypoint = resolve(import.meta.dir, "../src/index.ts");
beforeAll(async () => {
tmpStore = mkdtempSync(join(tmpdir(), "json-cas-e2e-"));
tmpStore = mkdtempSync(join(tmpdir(), "ocas-e2e-"));
varDbPath = join(tmpStore, "variables.db");
const schemaFile = join(tmpStore, "test-schema.json");
@@ -576,8 +576,8 @@ describe("Phase 2: Schema Validation", () => {
additionalProperties: false,
}),
);
const { openStore: openFsStore } = await import("@uncaged/json-cas-fs");
const { putSchema } = await import("@uncaged/json-cas");
const { openStore: openFsStore } = await import("@ocas/fs");
const { putSchema } = await import("@ocas/core");
const store = await openFsStore(tmpStore);
typeHash = await putSchema(
store,
@@ -2,9 +2,9 @@ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
import { mkdirSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import type { Hash, Store } from "@uncaged/json-cas";
import { bootstrap } from "@uncaged/json-cas";
import { createFsStore } from "@uncaged/json-cas-fs";
import type { Hash, Store } from "@ocas/core";
import { bootstrap } from "@ocas/core";
import { createFsStore } from "@ocas/fs";
// ---- Test helpers ----
@@ -17,7 +17,7 @@ beforeEach(() => {
// Create unique temp directory for each test
testDir = join(
tmpdir(),
`json-cas-test-${Date.now()}-${Math.random().toString(36).slice(2)}`,
`ocas-test-${Date.now()}-${Math.random().toString(36).slice(2)}`,
);
storePath = join(testDir, "store");
varDbPath = join(testDir, "variables.db");
@@ -403,7 +403,7 @@ describe("template list", () => {
// Create a template
await runCli("template", "set", stringHash, "--inline", "Template");
// Create a regular variable (not under @ucas/template/text/)
// Create a regular variable (not under @ocas/template/text/)
const hash = await store.put(stringHash, "regular var content");
await runCli("var", "set", "regular/var", hash);
@@ -600,7 +600,7 @@ describe("template integration", () => {
await runCli("template", "set", stringHash, "--inline", "Content");
// List via var command - should see template variable
const { stdout } = await runCli("var", "list", "@ucas/template/text/");
const { stdout } = await runCli("var", "list", "@ocas/template/text/");
const output = JSON.parse(stdout);
expect(output.value.length).toBeGreaterThan(0);
@@ -2,9 +2,9 @@ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
import { mkdirSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import type { Hash, Store } from "@uncaged/json-cas";
import { bootstrap, putSchema } from "@uncaged/json-cas";
import { createFsStore } from "@uncaged/json-cas-fs";
import type { Hash, Store } from "@ocas/core";
import { bootstrap, putSchema } from "@ocas/core";
import { createFsStore } from "@ocas/fs";
// ---- Test helpers ----
@@ -17,7 +17,7 @@ beforeEach(() => {
// Create unique temp directory for each test
testDir = join(
tmpdir(),
`json-cas-test-${Date.now()}-${Math.random().toString(36).slice(2)}`,
`ocas-test-${Date.now()}-${Math.random().toString(36).slice(2)}`,
);
storePath = join(testDir, "store");
varDbPath = join(testDir, "variables.db");
@@ -347,7 +347,7 @@ describe("var get", () => {
const { stderr, exitCode } = await runCli("var", "get", "config");
expect(exitCode).toBe(1);
expect(stderr).toContain("Usage: json-cas var get <name> --schema <hash>");
expect(stderr).toContain("Usage: ocas var get <name> --schema <hash>");
});
test("distinguish variants by schema", async () => {
@@ -904,7 +904,7 @@ describe("var tag", () => {
expect(exitCode).toBe(1);
expect(stderr).toContain(
"Usage: json-cas var tag <name> --schema <hash> <operations...>",
"Usage: ocas var tag <name> --schema <hash> <operations...>",
);
});
@@ -922,7 +922,7 @@ describe("var tag", () => {
expect(exitCode).toBe(1);
expect(stderr).toContain(
"Usage: json-cas var tag <name> --schema <hash> <operations...>",
"Usage: ocas var tag <name> --schema <hash> <operations...>",
);
});
});
@@ -12,7 +12,7 @@ let typeHash: string;
let nodeHash: string;
beforeAll(async () => {
tmpStore = mkdtempSync(join(tmpdir(), "json-cas-e2e-"));
tmpStore = mkdtempSync(join(tmpdir(), "ocas-e2e-"));
varDbPath = join(tmpStore, "variables.db");
const schemaFile = join(tmpStore, "test-schema.json");
@@ -25,8 +25,8 @@ beforeAll(async () => {
additionalProperties: false,
}),
);
const { openStore: openFsStore } = await import("@uncaged/json-cas-fs");
const { putSchema } = await import("@uncaged/json-cas");
const { openStore: openFsStore } = await import("@ocas/fs");
const { putSchema } = await import("@ocas/core");
const store = await openFsStore(tmpStore);
typeHash = await putSchema(
store,
@@ -1,19 +1,19 @@
# @uncaged/json-cas
# @ocas/core
Core CAS engine — hashing, schema, store, verify, bootstrap.
## Overview
`@uncaged/json-cas` is the foundation of the json-cas 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.
`@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: `json-cas-fs` provides persistence, and `cli-json-cas` exposes store operations on the command line.
Other packages build on this layer: `ocas-fs` provides persistence, and `cli-ocas` exposes store operations on the command line.
**Dependencies:** `ajv`, `cborg`, `xxhash-wasm`
## Installation
```bash
bun add @uncaged/json-cas
bun add @ocas/core
```
## API
@@ -114,7 +114,7 @@ import {
refs,
validate,
walk,
} from "@uncaged/json-cas";
} from "@ocas/core";
const store = createMemoryStore();
const metaHash = await bootstrap(store);
@@ -1,5 +1,5 @@
{
"name": "@uncaged/json-cas",
"name": "@ocas/core",
"version": "0.6.0",
"type": "module",
"main": "./dist/index.js",
@@ -1,7 +1,7 @@
import type { Hash, Store } from "./types.js";
/** @internal Store implementations attach this for bootstrap() only. */
export const BOOTSTRAP_STORE = Symbol.for("@uncaged/json-cas/bootstrap-store");
export const BOOTSTRAP_STORE = Symbol.for("@ocas/core/bootstrap-store");
export type BootstrapCapableStore = Store & {
[BOOTSTRAP_STORE](payload: unknown): Promise<Hash>;
@@ -67,7 +67,7 @@ describe("bootstrap - Built-in Schemas", () => {
const metaSchema = getSchema(store, metaHash);
expect(metaSchema).not.toBeNull();
expect(metaSchema?.type).toBe("object");
expect(metaSchema?.description).toBe("json-cas JSON Schema meta-schema");
expect(metaSchema?.description).toBe("ocas JSON Schema meta-schema");
});
test("should register @string schema correctly", async () => {
@@ -174,7 +174,7 @@ describe("bootstrap - @output/* Schemas", () => {
const schema = getSchema(store, hash) as JSONSchema;
expect(schema).not.toBeNull();
expect(typeof schema.title).toBe("string");
expect((schema.title as string).startsWith("ucas ")).toBe(true);
expect((schema.title as string).startsWith("ocas ")).toBe(true);
}
});
@@ -188,7 +188,7 @@ describe("bootstrap - @output/* Schemas", () => {
expect(schema).toEqual({
type: "string",
format: "cas_ref",
title: "ucas put result",
title: "ocas put result",
});
});
@@ -200,7 +200,7 @@ describe("bootstrap - @output/* Schemas", () => {
const schema = getSchema(store, hash) as JSONSchema;
expect(schema.type).toBe("object");
expect(schema.title).toBe("ucas get result");
expect(schema.title).toBe("ocas get result");
const props = schema.properties as Record<string, JSONSchema>;
expect(props.type).toEqual({ type: "string", format: "cas_ref" });
@@ -216,7 +216,7 @@ describe("bootstrap - @output/* Schemas", () => {
expect(getSchema(store, hash)).toEqual({
type: "boolean",
title: "ucas has result",
title: "ocas has result",
});
});
@@ -230,7 +230,7 @@ describe("bootstrap - @output/* Schemas", () => {
expect(schema).toEqual({
type: "string",
enum: ["ok", "corrupted", "invalid"],
title: "ucas verify result",
title: "ocas verify result",
});
});
@@ -243,7 +243,7 @@ describe("bootstrap - @output/* Schemas", () => {
expect(getSchema(store, hash)).toEqual({
type: "array",
items: { type: "string", format: "cas_ref" },
title: "ucas refs result",
title: "ocas refs result",
});
});
@@ -255,7 +255,7 @@ describe("bootstrap - @output/* Schemas", () => {
const schema = getSchema(store, hash) as JSONSchema;
expect(schema.type).toBe("object");
expect(schema.title).toBe("ucas gc result");
expect(schema.title).toBe("ocas gc result");
const props = schema.properties as Record<string, JSONSchema>;
expect(props.total).toEqual({ type: "number" });
@@ -272,7 +272,7 @@ describe("bootstrap - @output/* Schemas", () => {
const schema = getSchema(store, hash) as JSONSchema;
expect(schema.type).toBe("object");
expect(schema.title).toBe("ucas var set result");
expect(schema.title).toBe("ocas var set result");
const props = schema.properties as Record<string, JSONSchema>;
expect(props.name).toEqual({ type: "string" });
@@ -288,7 +288,7 @@ describe("bootstrap - @output/* Schemas", () => {
const schema = getSchema(store, hash) as JSONSchema;
expect(schema.type).toBe("array");
expect(schema.title).toBe("ucas var list result");
expect(schema.title).toBe("ocas var list result");
const items = schema.items as JSONSchema;
expect(items.type).toBe("object");
@@ -305,7 +305,7 @@ describe("bootstrap - @output/* Schemas", () => {
expect(getSchema(store, hash)).toEqual({
type: "object",
properties: { deleted: { type: "boolean" } },
title: "ucas template delete result",
title: "ocas template delete result",
});
});
@@ -21,7 +21,7 @@ const JSON_SCHEMA_TYPES = [
const BOOTSTRAP_PAYLOAD = {
type: "object",
additionalProperties: false,
description: "json-cas JSON Schema meta-schema",
description: "ocas JSON Schema meta-schema",
properties: {
type: {
anyOf: [
@@ -121,7 +121,7 @@ const OUTPUT_SCHEMAS: ReadonlyArray<
> = [
[
"@output/put",
{ type: "string", format: "cas_ref", title: "ucas put result" },
{ type: "string", format: "cas_ref", title: "ocas put result" },
],
[
"@output/get",
@@ -132,20 +132,20 @@ const OUTPUT_SCHEMAS: ReadonlyArray<
payload: {},
timestamp: { type: "number" },
},
title: "ucas get result",
title: "ocas get result",
},
],
["@output/has", { type: "boolean", title: "ucas has result" }],
["@output/has", { type: "boolean", title: "ocas has result" }],
[
"@output/hash",
{ type: "string", format: "cas_ref", title: "ucas hash result" },
{ type: "string", format: "cas_ref", title: "ocas hash result" },
],
[
"@output/verify",
{
type: "string",
enum: ["ok", "corrupted", "invalid"],
title: "ucas verify result",
title: "ocas verify result",
},
],
[
@@ -153,7 +153,7 @@ const OUTPUT_SCHEMAS: ReadonlyArray<
{
type: "array",
items: { type: "string", format: "cas_ref" },
title: "ucas refs result",
title: "ocas refs result",
},
],
[
@@ -161,7 +161,7 @@ const OUTPUT_SCHEMAS: ReadonlyArray<
{
type: "array",
items: { type: "string" },
title: "ucas walk result",
title: "ocas walk result",
},
],
[
@@ -169,7 +169,7 @@ const OUTPUT_SCHEMAS: ReadonlyArray<
{
type: "array",
items: { type: "string", format: "cas_ref" },
title: "ucas list result",
title: "ocas list result",
},
],
[
@@ -177,7 +177,7 @@ const OUTPUT_SCHEMAS: ReadonlyArray<
{
type: "array",
items: { type: "string", format: "cas_ref" },
title: "ucas list-meta result",
title: "ocas list-meta result",
},
],
[
@@ -185,7 +185,7 @@ const OUTPUT_SCHEMAS: ReadonlyArray<
{
type: "array",
items: { type: "string", format: "cas_ref" },
title: "ucas list-schema result",
title: "ocas list-schema result",
},
],
[
@@ -193,7 +193,7 @@ const OUTPUT_SCHEMAS: ReadonlyArray<
{
type: "object",
properties: { ...VARIABLE_PROPERTIES },
title: "ucas var set result",
title: "ocas var set result",
},
],
[
@@ -201,7 +201,7 @@ const OUTPUT_SCHEMAS: ReadonlyArray<
{
type: "object",
properties: { ...VARIABLE_PROPERTIES },
title: "ucas var get result",
title: "ocas var get result",
},
],
[
@@ -209,7 +209,7 @@ const OUTPUT_SCHEMAS: ReadonlyArray<
{
type: "object",
properties: { ...VARIABLE_PROPERTIES },
title: "ucas var delete result",
title: "ocas var delete result",
},
],
[
@@ -217,7 +217,7 @@ const OUTPUT_SCHEMAS: ReadonlyArray<
{
type: "object",
properties: { ...VARIABLE_PROPERTIES },
title: "ucas var tag result",
title: "ocas var tag result",
},
],
[
@@ -225,7 +225,7 @@ const OUTPUT_SCHEMAS: ReadonlyArray<
{
type: "array",
items: { type: "object", properties: { ...VARIABLE_PROPERTIES } },
title: "ucas var list result",
title: "ocas var list result",
},
],
[
@@ -236,12 +236,12 @@ const OUTPUT_SCHEMAS: ReadonlyArray<
schemaHash: { type: "string", format: "cas_ref" },
contentHash: { type: "string", format: "cas_ref" },
},
title: "ucas template set result",
title: "ocas template set result",
},
],
[
"@output/template-get",
{ type: "string", title: "ucas template get result" },
{ type: "string", title: "ocas template get result" },
],
[
"@output/template-list",
@@ -254,7 +254,7 @@ const OUTPUT_SCHEMAS: ReadonlyArray<
contentHash: { type: "string", format: "cas_ref" },
},
},
title: "ucas template list result",
title: "ocas template list result",
},
],
[
@@ -262,7 +262,7 @@ const OUTPUT_SCHEMAS: ReadonlyArray<
{
type: "object",
properties: { deleted: { type: "boolean" } },
title: "ucas template delete result",
title: "ocas template delete result",
},
],
[
@@ -275,7 +275,7 @@ const OUTPUT_SCHEMAS: ReadonlyArray<
collected: { type: "number" },
scanned: { type: "number" },
},
title: "ucas gc result",
title: "ocas gc result",
},
],
];
@@ -11,7 +11,7 @@ import { createVariableStore } from "./variable-store.js";
// Helper to create a temporary variable store
async function createTempVarStore() {
const tempDir = await mkdtemp(join(tmpdir(), "json-cas-test-"));
const tempDir = await mkdtemp(join(tmpdir(), "ocas-test-"));
const dbPath = join(tempDir, "vars.db");
const store = createMemoryStore();
await bootstrap(store);
@@ -92,14 +92,14 @@ describe("Suite 2: Custom {% render %} Tag Implementation", () => {
templateSchema,
"Parent: {{ payload.name }}\n{% render payload.child %}",
);
varStore.set(`@ucas/template/text/${parentSchema}`, templateHash);
varStore.set(`@ocas/template/text/${parentSchema}`, templateHash);
// Register template for child
const childTemplateHash = await store.put(
templateSchema,
"Child: {{ payload.value }}",
);
varStore.set(`@ucas/template/text/${childSchema}`, childTemplateHash);
varStore.set(`@ocas/template/text/${childSchema}`, childTemplateHash);
const output = await renderWithTemplate(store, varStore, parentHash, {
resolution: 1.0,
@@ -147,7 +147,7 @@ describe("Suite 2: Custom {% render %} Tag Implementation", () => {
templateSchema,
"Level {{ payload.level }}\n{% render payload.child, decay: 0.7 %}",
);
varStore.set(`@ucas/template/text/${nodeSchema}`, template);
varStore.set(`@ocas/template/text/${nodeSchema}`, template);
const output = await renderWithTemplate(store, varStore, rootHash, {
resolution: 1.0,
@@ -195,13 +195,13 @@ describe("Suite 2: Custom {% render %} Tag Implementation", () => {
templateSchema,
"Left:\n{% render payload.left %}\nRight:\n{% render payload.right %}",
);
varStore.set(`@ucas/template/text/${parentSchema}`, parentTemplate);
varStore.set(`@ocas/template/text/${parentSchema}`, parentTemplate);
const childTemplate = await store.put(
templateSchema,
"Value: {{ payload.value }}",
);
varStore.set(`@ucas/template/text/${childSchema}`, childTemplate);
varStore.set(`@ocas/template/text/${childSchema}`, childTemplate);
const output = await renderWithTemplate(store, varStore, parentHash, {
resolution: 1.0,
@@ -242,7 +242,7 @@ describe("Suite 2: Custom {% render %} Tag Implementation", () => {
templateSchema,
"Before\n{% render payload.child %}\nAfter",
);
varStore.set(`@ucas/template/text/${nodeSchema}`, template);
varStore.set(`@ocas/template/text/${nodeSchema}`, template);
const output = await renderWithTemplate(store, varStore, nodeHash, {
resolution: 1.0,
@@ -281,7 +281,7 @@ describe("Suite 2: Custom {% render %} Tag Implementation", () => {
templateSchema,
"{% render payload.child %}",
);
varStore.set(`@ucas/template/text/${nodeSchema}`, template);
varStore.set(`@ocas/template/text/${nodeSchema}`, template);
const output = await renderWithTemplate(store, varStore, nodeHash, {
resolution: 1.0,
@@ -321,7 +321,7 @@ describe("Suite 2: Custom {% render %} Tag Implementation", () => {
templateSchema,
"{% render payload.child %}",
);
varStore.set(`@ucas/template/text/${parentSchema}`, parentTemplate);
varStore.set(`@ocas/template/text/${parentSchema}`, parentTemplate);
// resolution=0.02, decay=0.5, child gets 0.01 which equals epsilon
const output = await renderWithTemplate(store, varStore, parentHash, {
@@ -356,7 +356,7 @@ describe("Suite 3: Template Context Variables", () => {
templateSchema,
"Resolution: {{ resolution }}",
);
varStore.set(`@ucas/template/text/${nodeSchema}`, template);
varStore.set(`@ocas/template/text/${nodeSchema}`, template);
const output = await renderWithTemplate(store, varStore, nodeHash, {
resolution: 0.75,
@@ -388,7 +388,7 @@ describe("Suite 3: Template Context Variables", () => {
templateSchema,
"Epsilon: {{ epsilon }}",
);
varStore.set(`@ucas/template/text/${nodeSchema}`, template);
varStore.set(`@ocas/template/text/${nodeSchema}`, template);
const output = await renderWithTemplate(store, varStore, nodeHash, {
resolution: 1.0,
@@ -417,7 +417,7 @@ describe("Suite 3: Template Context Variables", () => {
const templateSchema = await putSchema(store, { type: "string" });
const template = await store.put(templateSchema, "Hash: {{ hash }}");
varStore.set(`@ucas/template/text/${nodeSchema}`, template);
varStore.set(`@ocas/template/text/${nodeSchema}`, template);
const output = await renderWithTemplate(store, varStore, nodeHash, {
resolution: 1.0,
@@ -450,7 +450,7 @@ describe("Suite 3: Template Context Variables", () => {
templateSchema,
"Name: {{ payload.name }}, Count: {{ payload.count }}",
);
varStore.set(`@ucas/template/text/${nodeSchema}`, template);
varStore.set(`@ocas/template/text/${nodeSchema}`, template);
const output = await renderWithTemplate(store, varStore, nodeHash, {
resolution: 1.0,
@@ -479,7 +479,7 @@ describe("Suite 3: Template Context Variables", () => {
const templateSchema = await putSchema(store, { type: "string" });
const template = await store.put(templateSchema, "Type: {{ type }}");
varStore.set(`@ucas/template/text/${nodeSchema}`, template);
varStore.set(`@ocas/template/text/${nodeSchema}`, template);
const output = await renderWithTemplate(store, varStore, nodeHash, {
resolution: 1.0,
@@ -511,7 +511,7 @@ describe("Suite 3: Template Context Variables", () => {
templateSchema,
"Timestamp: {{ timestamp }}",
);
varStore.set(`@ucas/template/text/${nodeSchema}`, template);
varStore.set(`@ocas/template/text/${nodeSchema}`, template);
const output = await renderWithTemplate(store, varStore, nodeHash, {
resolution: 1.0,
@@ -548,7 +548,7 @@ Epsilon: {{ epsilon }}
Payload: {{ payload.name }}
Timestamp: {{ timestamp }}`,
);
varStore.set(`@ucas/template/text/${nodeSchema}`, template);
varStore.set(`@ocas/template/text/${nodeSchema}`, template);
const output = await renderWithTemplate(store, varStore, nodeHash, {
resolution: 0.8,
@@ -587,7 +587,7 @@ describe("Suite 4: Render Flow Integration", () => {
templateSchema,
"Custom template: {{ payload.name }}",
);
varStore.set(`@ucas/template/text/${nodeSchema}`, template);
varStore.set(`@ocas/template/text/${nodeSchema}`, template);
const output = await renderWithTemplate(store, varStore, nodeHash, {
resolution: 1.0,
@@ -616,7 +616,7 @@ describe("Suite 4: Render Flow Integration", () => {
const templateSchema = await putSchema(store, { type: "string" });
const template = await store.put(templateSchema, "");
varStore.set(`@ucas/template/text/${nodeSchema}`, template);
varStore.set(`@ocas/template/text/${nodeSchema}`, template);
const output = await renderWithTemplate(store, varStore, nodeHash, {
resolution: 1.0,
@@ -648,7 +648,7 @@ describe("Suite 4: Render Flow Integration", () => {
templateSchema,
"{% render %}", // Invalid: no variable
);
varStore.set(`@ucas/template/text/${nodeSchema}`, template);
varStore.set(`@ocas/template/text/${nodeSchema}`, template);
await expect(async () => {
await renderWithTemplate(store, varStore, nodeHash, {
@@ -690,13 +690,13 @@ describe("Suite 5: Decay Priority Chain", () => {
templateSchema,
"{% render payload.child, decay: 0.7 %}",
);
varStore.set(`@ucas/template/text/${parentSchema}`, parentTemplate);
varStore.set(`@ocas/template/text/${parentSchema}`, parentTemplate);
const childTemplate = await store.put(
templateSchema,
"Resolution: {{ resolution }}",
);
varStore.set(`@ucas/template/text/${childSchema}`, childTemplate);
varStore.set(`@ocas/template/text/${childSchema}`, childTemplate);
const output = await renderWithTemplate(store, varStore, parentHash, {
resolution: 1.0,
@@ -737,13 +737,13 @@ describe("Suite 5: Decay Priority Chain", () => {
templateSchema,
"{% render payload.child %}", // No explicit decay
);
varStore.set(`@ucas/template/text/${parentSchema}`, parentTemplate);
varStore.set(`@ocas/template/text/${parentSchema}`, parentTemplate);
const childTemplate = await store.put(
templateSchema,
"Resolution: {{ resolution }}",
);
varStore.set(`@ucas/template/text/${childSchema}`, childTemplate);
varStore.set(`@ocas/template/text/${childSchema}`, childTemplate);
const output = await renderWithTemplate(store, varStore, parentHash, {
resolution: 1.0,
@@ -784,13 +784,13 @@ describe("Suite 5: Decay Priority Chain", () => {
templateSchema,
"{% render payload.child %}",
);
varStore.set(`@ucas/template/text/${parentSchema}`, parentTemplate);
varStore.set(`@ocas/template/text/${parentSchema}`, parentTemplate);
const childTemplate = await store.put(
templateSchema,
"Resolution: {{ resolution }}",
);
varStore.set(`@ucas/template/text/${childSchema}`, childTemplate);
varStore.set(`@ocas/template/text/${childSchema}`, childTemplate);
const output = await renderWithTemplate(
store,
@@ -837,7 +837,7 @@ describe("Suite 6: Recursive Rendering Edge Cases", () => {
templateSchema,
"Level {{ payload.level }}\n{% render payload.next %}",
);
varStore.set(`@ucas/template/text/${nodeSchema}`, template);
varStore.set(`@ocas/template/text/${nodeSchema}`, template);
const output = await renderWithTemplate(
store,
@@ -878,7 +878,7 @@ describe("Suite 6: Recursive Rendering Edge Cases", () => {
templateSchema,
"Node {{ payload.name }}\n{% render payload.ref %}",
);
varStore.set(`@ucas/template/text/${nodeSchema}`, template);
varStore.set(`@ocas/template/text/${nodeSchema}`, template);
const output = await renderWithTemplate(store, varStore, nodeAHash, {
resolution: 1.0,
@@ -925,13 +925,13 @@ describe("Suite 6: Recursive Rendering Edge Cases", () => {
templateSchema,
"{% for item in payload.items %}{% render item %}\n{% endfor %}",
);
varStore.set(`@ucas/template/text/${parentSchema}`, parentTemplate);
varStore.set(`@ocas/template/text/${parentSchema}`, parentTemplate);
const itemTemplate = await store.put(
templateSchema,
"Item: {{ payload.name }}",
);
varStore.set(`@ucas/template/text/${itemSchema}`, itemTemplate);
varStore.set(`@ocas/template/text/${itemSchema}`, itemTemplate);
const output = await renderWithTemplate(store, varStore, parentHash, {
resolution: 1.0,
@@ -967,7 +967,7 @@ describe("Suite 7: Error Handling & Edge Cases", () => {
templateSchema,
"{% render missingVar %}",
);
varStore.set(`@ucas/template/text/${nodeSchema}`, template);
varStore.set(`@ocas/template/text/${nodeSchema}`, template);
// Should complete without throwing
const output = await renderWithTemplate(store, varStore, nodeHash, {
@@ -1008,7 +1008,7 @@ describe("Suite 7: Error Handling & Edge Cases", () => {
templateSchema,
"{% render payload.child, decay: 1.5 %}",
);
varStore.set(`@ucas/template/text/${parentSchema}`, template);
varStore.set(`@ocas/template/text/${parentSchema}`, template);
await expect(async () => {
await renderWithTemplate(store, varStore, parentHash, {
@@ -1048,7 +1048,7 @@ describe("Suite 7: Error Handling & Edge Cases", () => {
templateSchema,
"{% render payload.child, decay: -0.5 %}",
);
varStore.set(`@ucas/template/text/${parentSchema}`, template);
varStore.set(`@ocas/template/text/${parentSchema}`, template);
await expect(async () => {
await renderWithTemplate(store, varStore, parentHash, {
@@ -1088,7 +1088,7 @@ describe("Suite 7: Error Handling & Edge Cases", () => {
templateSchema,
"{% render payload.child, decay: 0 %}",
);
varStore.set(`@ucas/template/text/${parentSchema}`, template);
varStore.set(`@ocas/template/text/${parentSchema}`, template);
await expect(async () => {
await renderWithTemplate(store, varStore, parentHash, {
@@ -1128,13 +1128,13 @@ describe("Suite 7: Error Handling & Edge Cases", () => {
templateSchema,
"{% render payload.child, decay: 1 %}",
);
varStore.set(`@ucas/template/text/${parentSchema}`, parentTemplate);
varStore.set(`@ocas/template/text/${parentSchema}`, parentTemplate);
const childTemplate = await store.put(
templateSchema,
"Resolution: {{ resolution }}",
);
varStore.set(`@ucas/template/text/${childSchema}`, childTemplate);
varStore.set(`@ocas/template/text/${childSchema}`, childTemplate);
const output = await renderWithTemplate(store, varStore, parentHash, {
resolution: 0.5,
@@ -1167,7 +1167,7 @@ describe("Suite 7: Error Handling & Edge Cases", () => {
templateSchema,
"你好: {{ payload.name }} 🌍",
);
varStore.set(`@ucas/template/text/${nodeSchema}`, template);
varStore.set(`@ocas/template/text/${nodeSchema}`, template);
const output = await renderWithTemplate(store, varStore, nodeHash, {
resolution: 1.0,
@@ -1217,13 +1217,13 @@ describe("Suite 8: Performance & Scalability", () => {
templateSchema,
"{% for child in payload.items %}{% render child %}{% endfor %}",
);
varStore.set(`@ucas/template/text/${parentSchema}`, parentTemplate);
varStore.set(`@ocas/template/text/${parentSchema}`, parentTemplate);
const itemTemplate = await store.put(
templateSchema,
"{{ payload.value }}",
);
varStore.set(`@ucas/template/text/${itemSchema}`, itemTemplate);
varStore.set(`@ocas/template/text/${itemSchema}`, itemTemplate);
const start = Date.now();
const output = await renderWithTemplate(store, varStore, parentHash, {
@@ -1268,7 +1268,7 @@ describe("Suite 9: E2E Template Variable Rendering (Issue #52)", () => {
templateSchema,
"Name: {{ name }}, Age: {{ age }}",
);
varStore.set(`@ucas/template/text/${personSchema}`, templateHash);
varStore.set(`@ocas/template/text/${personSchema}`, templateHash);
// Render - should produce empty values
const output = await renderWithTemplate(store, varStore, personHash, {
@@ -1309,7 +1309,7 @@ describe("Suite 9: E2E Template Variable Rendering (Issue #52)", () => {
templateSchema,
"Name: {{ payload.name }}, Age: {{ payload.age }}",
);
varStore.set(`@ucas/template/text/${personSchema}`, templateHash);
varStore.set(`@ocas/template/text/${personSchema}`, templateHash);
// Render - should produce correct values
const output = await renderWithTemplate(store, varStore, personHash, {
@@ -1349,7 +1349,7 @@ describe("Suite 9: E2E Template Variable Rendering (Issue #52)", () => {
templateSchema,
"User: {{ payload.name }}, Age: {{ payload.age }}",
);
varStore.set(`@ucas/template/text/${personSchema}`, templateHash);
varStore.set(`@ocas/template/text/${personSchema}`, templateHash);
// This simulates the CLI flow
const output = await renderWithTemplate(store, varStore, personHash, {
@@ -1382,7 +1382,7 @@ describe("Suite 9: E2E Template Variable Rendering (Issue #52)", () => {
templateSchema,
"Value is: {{ payload }}",
);
varStore.set(`@ucas/template/text/${stringSchema}`, templateHash);
varStore.set(`@ocas/template/text/${stringSchema}`, templateHash);
// Render
const output = await renderWithTemplate(store, varStore, stringHash, {
@@ -1414,7 +1414,7 @@ describe("Suite 9: E2E Template Variable Rendering (Issue #52)", () => {
templateSchema,
"The answer is {{ payload }}",
);
varStore.set(`@ucas/template/text/${numberSchema}`, templateHash);
varStore.set(`@ocas/template/text/${numberSchema}`, templateHash);
// Render
const output = await renderWithTemplate(store, varStore, numberHash, {
@@ -1469,7 +1469,7 @@ describe("Suite 9: E2E Template Variable Rendering (Issue #52)", () => {
templateSchema,
"User {{ payload.user.name }} lives in {{ payload.user.address.city }}",
);
varStore.set(`@ucas/template/text/${userSchema}`, templateHash);
varStore.set(`@ocas/template/text/${userSchema}`, templateHash);
// Render
const output = await renderWithTemplate(store, varStore, userHash, {
@@ -1511,7 +1511,7 @@ describe("Suite 9: E2E Template Variable Rendering (Issue #52)", () => {
templateSchema,
"Tags: {% for tag in payload.tags %}{{ tag }}{% unless forloop.last %}, {% endunless %}{% endfor %}",
);
varStore.set(`@ucas/template/text/${tagsSchema}`, templateHash);
varStore.set(`@ocas/template/text/${tagsSchema}`, templateHash);
// Render
const output = await renderWithTemplate(store, varStore, tagsHash, {
@@ -1550,7 +1550,7 @@ describe("Suite 9: E2E Template Variable Rendering (Issue #52)", () => {
templateSchema,
"Name: {{ payload.name }}, Age: {{ payload.age }}",
);
varStore.set(`@ucas/template/text/${personSchema}`, templateHash);
varStore.set(`@ocas/template/text/${personSchema}`, templateHash);
// Render - age should be empty
const output = await renderWithTemplate(store, varStore, personHash, {
@@ -1591,7 +1591,7 @@ describe("Suite 9: E2E Template Variable Rendering (Issue #52)", () => {
templateSchema,
"Name: {{ payload.name }}, Email: {{ payload.email }}",
);
varStore.set(`@ucas/template/text/${personSchema}`, templateHash);
varStore.set(`@ocas/template/text/${personSchema}`, templateHash);
// Render - email should be empty
const output = await renderWithTemplate(store, varStore, personHash, {
@@ -1632,7 +1632,7 @@ describe("Suite 9: E2E Template Variable Rendering (Issue #52)", () => {
templateSchema,
"User {{ payload.name }} is {% if payload.active %}active{% else %}inactive{% endif %}",
);
varStore.set(`@ucas/template/text/${userSchema}`, templateHash);
varStore.set(`@ocas/template/text/${userSchema}`, templateHash);
// Render
const output = await renderWithTemplate(store, varStore, userHash, {
@@ -1673,7 +1673,7 @@ describe("Suite 9: E2E Template Variable Rendering (Issue #52)", () => {
templateSchema,
"Name: '{{ payload.name }}', Count: {{ payload.count }}",
);
varStore.set(`@ucas/template/text/${dataSchema}`, templateHash);
varStore.set(`@ocas/template/text/${dataSchema}`, templateHash);
// Render - zero and empty string should appear
const output = await renderWithTemplate(store, varStore, dataHash, {
@@ -1712,7 +1712,7 @@ describe("Suite 9: E2E Template Variable Rendering (Issue #52)", () => {
templateSchema,
"Text: {{ payload.text }}",
);
varStore.set(`@ucas/template/text/${textSchema}`, templateHash);
varStore.set(`@ocas/template/text/${textSchema}`, templateHash);
// Render
const output = await renderWithTemplate(store, varStore, textHash, {
@@ -1762,14 +1762,14 @@ describe("Suite 10: Context Variable Completeness", () => {
templateSchema,
"Parent: {{ payload.name }}\n{% render payload.child %}",
);
varStore.set(`@ucas/template/text/${parentSchema}`, parentTemplateHash);
varStore.set(`@ocas/template/text/${parentSchema}`, parentTemplateHash);
// Register child template that accesses context variables
const childTemplateHash = await store.put(
templateSchema,
"Child: {{ payload.name }}, Hash: {{ hash }}, Resolution: {{ resolution }}",
);
varStore.set(`@ucas/template/text/${childSchema}`, childTemplateHash);
varStore.set(`@ocas/template/text/${childSchema}`, childTemplateHash);
// Render
const output = await renderWithTemplate(store, varStore, parentHash, {
@@ -1822,14 +1822,14 @@ describe("Suite 10: Context Variable Completeness", () => {
templateSchema,
"Parent custom: {{ payload.custom }}\n{% render payload.child %}",
);
varStore.set(`@ucas/template/text/${parentSchema}`, parentTemplateHash);
varStore.set(`@ocas/template/text/${parentSchema}`, parentTemplateHash);
// Register child template
const childTemplateHash = await store.put(
templateSchema,
"Child custom: {{ payload.custom }}",
);
varStore.set(`@ucas/template/text/${childSchema}`, childTemplateHash);
varStore.set(`@ocas/template/text/${childSchema}`, childTemplateHash);
// Render
const output = await renderWithTemplate(store, varStore, parentHash, {
@@ -11,7 +11,7 @@ const FLOAT_TOLERANCE = 1e-10;
/**
* Render a CAS node using LiquidJS templates with resolution-based decay.
* Templates are discovered via variables: @ucas/template/text/<type-hash>
* Templates are discovered via variables: @ocas/template/text/<type-hash>
*/
export async function renderWithTemplate(
store: Store,
@@ -220,7 +220,7 @@ async function findTemplate(
varStore: VariableStore,
typeHash: Hash,
): Promise<string | null> {
const varName = `@ucas/template/text/${typeHash}`;
const varName = `@ocas/template/text/${typeHash}`;
try {
// Find the string schema hash (we need this to query variables)
@@ -41,7 +41,7 @@ describe("registerOutputTemplates", () => {
});
test("registers a template for every @output/* schema", async () => {
tempDir = await mkdtemp(join(tmpdir(), "json-cas-tmpl-"));
tempDir = await mkdtemp(join(tmpdir(), "ocas-tmpl-"));
store = createMemoryStore();
await bootstrap(store);
varStore = createVariableStore(join(tempDir, "vars.db"), store);
@@ -55,8 +55,8 @@ describe("registerOutputTemplates", () => {
}
});
test("each template is retrievable via @ucas/template/text/<hash>", async () => {
tempDir = await mkdtemp(join(tmpdir(), "json-cas-tmpl-"));
test("each template is retrievable via @ocas/template/text/<hash>", async () => {
tempDir = await mkdtemp(join(tmpdir(), "ocas-tmpl-"));
store = createMemoryStore();
const aliases = await bootstrap(store);
varStore = createVariableStore(join(tempDir, "vars.db"), store);
@@ -70,7 +70,7 @@ describe("registerOutputTemplates", () => {
const schemaHash = aliases[alias];
if (!schemaHash) throw new Error(`${alias} not found`);
const varName = `@ucas/template/text/${schemaHash}`;
const varName = `@ocas/template/text/${schemaHash}`;
const variable = varStore.get(varName, stringHash);
if (variable === null) throw new Error(`Variable ${varName} not found`);
@@ -82,7 +82,7 @@ describe("registerOutputTemplates", () => {
});
test("is idempotent — safe to call multiple times", async () => {
tempDir = await mkdtemp(join(tmpdir(), "json-cas-tmpl-"));
tempDir = await mkdtemp(join(tmpdir(), "ocas-tmpl-"));
store = createMemoryStore();
await bootstrap(store);
varStore = createVariableStore(join(tempDir, "vars.db"), store);
@@ -94,7 +94,7 @@ describe("registerOutputTemplates", () => {
});
test("@output/put template contains payload reference", async () => {
tempDir = await mkdtemp(join(tmpdir(), "json-cas-tmpl-"));
tempDir = await mkdtemp(join(tmpdir(), "ocas-tmpl-"));
store = createMemoryStore();
const aliases = await bootstrap(store);
varStore = createVariableStore(join(tempDir, "vars.db"), store);
@@ -106,7 +106,7 @@ describe("registerOutputTemplates", () => {
const stringHash = aliases["@string"];
if (!stringHash) throw new Error("@string not found");
const variable = varStore.get(`@ucas/template/text/${putHash}`, stringHash);
const variable = varStore.get(`@ocas/template/text/${putHash}`, stringHash);
if (variable === null)
throw new Error("@output/put template variable not found");
@@ -55,7 +55,7 @@ const DEFAULT_TEMPLATES: ReadonlyArray<
/**
* Register default LiquidJS templates for all @output/* schemas.
* Each template is stored as a @string CAS node and bound to
* the variable `@ucas/template/text/<schema-hash>`.
* the variable `@ocas/template/text/<schema-hash>`.
*
* Idempotent: safe to call multiple times.
*/
@@ -78,7 +78,7 @@ export async function registerOutputTemplates(
}
const contentHash = await store.put(stringHash, template);
const varName = `@ucas/template/text/${schemaHash}`;
const varName = `@ocas/template/text/${schemaHash}`;
varStore.set(varName, contentHash);
registered[alias] = contentHash;
}
@@ -154,7 +154,7 @@ async function hasTemplate(
varStore: VariableStore,
typeHash: Hash,
): Promise<boolean> {
const varName = `@ucas/template/text/${typeHash}`;
const varName = `@ocas/template/text/${typeHash}`;
try {
const stringSchema = await putSchema(store, { type: "string" });
const variable = varStore.get(varName, stringSchema);
@@ -259,7 +259,7 @@ export async function putSchema(
}
if (!isValidSchema(jsonSchema)) {
throw new SchemaValidationError(
"Invalid schema: input does not conform to the json-cas JSON Schema meta-schema",
"Invalid schema: input does not conform to the ocas JSON Schema meta-schema",
);
}
return store.put(metaHash, jsonSchema);
@@ -1622,12 +1622,12 @@ describe("VariableStore - @ Prefix Variable Names", () => {
const varStore = new VariableStore(dbPath, store);
// Should succeed
const variable = varStore.set("@ucas/test/foo", hash);
expect(variable.name).toBe("@ucas/test/foo");
const variable = varStore.set("@ocas/test/foo", hash);
expect(variable.name).toBe("@ocas/test/foo");
const retrieved = varStore.get("@ucas/test/foo", schemaHash);
const retrieved = varStore.get("@ocas/test/foo", schemaHash);
expect(retrieved).not.toBeNull();
expect(retrieved?.name).toBe("@ucas/test/foo");
expect(retrieved?.name).toBe("@ocas/test/foo");
expect(retrieved?.value).toBe(hash);
varStore.close();
@@ -1662,7 +1662,7 @@ describe("VariableStore - @ Prefix Variable Names", () => {
// Multiple valid patterns
const validNames = [
"@ucas/render/template",
"@ocas/render/template",
"@system/config",
"@foo.bar/baz",
"@app-1/test_2",
@@ -1,19 +1,19 @@
# @uncaged/json-cas-fs
# @ocas/fs
Filesystem-backed CAS store.
## Overview
`@uncaged/json-cas-fs` implements a persistent `Store` on disk. Each node is stored as `<hash>.bin` (CBOR-encoded `CasNode`). A `_index/` directory maps type hashes to content hashes for `listByType`. Stores support bootstrap via the same `BOOTSTRAP_STORE` symbol as the in-memory implementation.
`@ocas/fs` implements a persistent `Store` on disk. Each node is stored as `<hash>.bin` (CBOR-encoded `CasNode`). A `_index/` directory maps type hashes to content hashes for `listByType`. Stores support bootstrap via the same `BOOTSTRAP_STORE` symbol as the in-memory implementation.
Depends on `@uncaged/json-cas` for hashing, CBOR encoding, and types.
Depends on `@ocas/core` for hashing, CBOR encoding, and types.
**Dependencies:** `@uncaged/json-cas`, `cborg`
**Dependencies:** `@ocas/core`, `cborg`
## Installation
```bash
bun add @uncaged/json-cas-fs
bun add @ocas/fs
```
## API
@@ -24,13 +24,13 @@ Exported from `src/index.ts`:
function createFsStore(dir: string): BootstrapCapableStore;
```
Returns a `BootstrapCapableStore` from `@uncaged/json-cas`. The store loads existing `.bin` files on open and migrates or builds the type index on first use.
Returns a `BootstrapCapableStore` from `@ocas/core`. The store loads existing `.bin` files on open and migrates or builds the type index on first use.
### Example
```typescript
import { bootstrap, putSchema } from "@uncaged/json-cas";
import { createFsStore } from "@uncaged/json-cas-fs";
import { bootstrap, putSchema } from "@ocas/core";
import { createFsStore } from "@ocas/fs";
const store = createFsStore("./my-cas-store");
await bootstrap(store);
@@ -1,5 +1,5 @@
{
"name": "@uncaged/json-cas-fs",
"name": "@ocas/fs",
"version": "0.6.0",
"type": "module",
"main": "./dist/index.js",
@@ -19,7 +19,7 @@
"prepublishOnly": "bash ../../scripts/check-workspace-deps.sh"
},
"dependencies": {
"@uncaged/json-cas": "^0.6.0",
"cborg": "^4.2.3"
"cborg": "^4.2.3",
"@ocas/core": "^0.6.0"
}
}
@@ -9,19 +9,19 @@ import {
} from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import type { CasNode } from "@uncaged/json-cas";
import type { CasNode } from "@ocas/core";
import {
BOOTSTRAP_STORE,
bootstrap,
computeHash,
computeSelfHash,
verify,
} from "@uncaged/json-cas";
} from "@ocas/core";
import { createFsStore, openStore } from "./store.js";
function makeTmpDir(): string {
return mkdtempSync(join(tmpdir(), "json-cas-fs-test-"));
return mkdtempSync(join(tmpdir(), "ocas-fs-test-"));
}
// ──────────────────────────────────────────────────────────────────────────────
@@ -438,7 +438,7 @@ describe("openStore – async with auto-bootstrap", () => {
describe("createFsStore – listMeta and listSchemas", () => {
let dir: string;
beforeEach(() => {
dir = mkdtempSync(join(tmpdir(), "json-cas-fs-meta-"));
dir = mkdtempSync(join(tmpdir(), "ocas-fs-meta-"));
});
afterEach(() => {
rmSync(dir, { recursive: true, force: true });
@@ -10,7 +10,7 @@ import {
writeFileSync,
} from "node:fs";
import { join } from "node:path";
import type { BootstrapCapableStore, CasNode, Hash } from "@uncaged/json-cas";
import type { BootstrapCapableStore, CasNode, Hash } from "@ocas/core";
import {
BOOTSTRAP_STORE,
@@ -18,7 +18,7 @@ import {
cborEncode,
computeHash,
computeSelfHash,
} from "@uncaged/json-cas";
} from "@ocas/core";
import { decode } from "cborg";
const INDEX_DIR = "_index";
@@ -6,5 +6,5 @@
},
"include": ["src"],
"exclude": ["src/**/*.test.ts"],
"references": [{ "path": "../json-cas" }]
"references": [{ "path": "../core" }]
}
+2 -2
View File
@@ -11,8 +11,8 @@
"verbatimModuleSyntax": true,
"skipLibCheck": true,
"paths": {
"@uncaged/json-cas": ["./packages/json-cas/src/index.ts"],
"@uncaged/json-cas-fs": ["./packages/json-cas-fs/src/index.ts"]
"@ocas/core": ["./packages/core/src/index.ts"],
"@ocas/fs": ["./packages/fs/src/index.ts"]
},
"composite": true,
"declaration": true,