Merge pull request 'feat: rebrand internal namespaces to @ocas/ scope' (#10) from feat/namespace-rebranding into main

This commit was merged in pull request #10.
This commit is contained in:
2026-06-01 08:13:49 +00:00
25 changed files with 488 additions and 466 deletions
+68 -52
View File
@@ -89,7 +89,9 @@ const { flags, positional } = parseArgs(process.argv.slice(2));
const defaultStorePath = join(homedir(), ".ocas");
const storePath =
typeof flags.store === "string" ? flags.store : defaultStorePath;
typeof flags.store === "string"
? flags.store
: (process.env["OCAS_HOME"] ?? defaultStorePath);
const compact = flags.json === true;
const defaultVarDbPath = join(storePath, "variables.db");
@@ -214,11 +216,11 @@ async function cmdPut(args: string[]): Promise<void> {
// Schema nodes: use putSchema() which validates via isValidSchema() (recursive)
// instead of ajv against meta-schema (which can't express recursive constraints)
const builtinSchemas = await bootstrap(store);
const metaHash = builtinSchemas["@schema"];
const metaHash = builtinSchemas["@ocas/schema"];
if (typeHash === metaHash) {
try {
const hash = await putSchema(store, payload as Record<string, unknown>);
out(await wrapEnvelope(store, "@output/put", hash));
out(await wrapEnvelope(store, "@ocas/output/put", hash));
} catch (_e) {
console.error(
`Validation failed: payload in ${file} does not match schema ${typeHash}`,
@@ -245,7 +247,7 @@ async function cmdPut(args: string[]): Promise<void> {
}
const hash = await store.put(typeHash, payload);
out(await wrapEnvelope(store, "@output/put", hash));
out(await wrapEnvelope(store, "@ocas/output/put", hash));
}
async function cmdGet(args: string[]): Promise<void> {
@@ -254,14 +256,14 @@ async function cmdGet(args: string[]): Promise<void> {
const store = await openStore();
const node = store.get(hash);
if (node === null) die(`Node not found: ${hash}`);
out(await wrapEnvelope(store, "@output/get", node));
out(await wrapEnvelope(store, "@ocas/output/get", node));
}
async function cmdHas(args: string[]): Promise<void> {
const hash = args[0];
if (!hash) die("Usage: ocas has <hash>");
const store = await openStore();
out(await wrapEnvelope(store, "@output/has", store.has(hash)));
out(await wrapEnvelope(store, "@ocas/output/has", store.has(hash)));
}
async function cmdVerify(args: string[]): Promise<void> {
@@ -277,7 +279,7 @@ async function cmdVerify(args: string[]): Promise<void> {
} else {
status = validate(store, node) ? "ok" : "invalid";
}
out(await wrapEnvelope(store, "@output/verify", status));
out(await wrapEnvelope(store, "@ocas/output/verify", status));
}
async function cmdRefs(args: string[]): Promise<void> {
@@ -287,7 +289,7 @@ async function cmdRefs(args: string[]): Promise<void> {
const node = store.get(hash);
if (node === null) die(`Node not found: ${hash}`);
const refHashes = refs(store, node);
out(await wrapEnvelope(store, "@output/refs", refHashes));
out(await wrapEnvelope(store, "@ocas/output/refs", refHashes));
}
async function cmdWalk(args: string[]): Promise<void> {
@@ -323,13 +325,13 @@ async function cmdWalk(args: string[]): Promise<void> {
}
printNode(hash, "", true);
out(await wrapEnvelope(store, "@output/walk", lines.join("\n")));
out(await wrapEnvelope(store, "@ocas/output/walk", lines.join("\n")));
} else {
const hashes: Hash[] = [];
walk(store, hash, (h) => {
hashes.push(h);
});
out(await wrapEnvelope(store, "@output/walk", hashes));
out(await wrapEnvelope(store, "@ocas/output/walk", hashes));
}
}
@@ -347,7 +349,7 @@ async function cmdHash(args: string[]): Promise<void> {
const payload = isPipe ? await readStdinJson() : readJsonFile(file as string);
const hash = await computeHash(typeHash, payload);
const store = await openStore();
out(await wrapEnvelope(store, "@output/hash", hash));
out(await wrapEnvelope(store, "@ocas/output/hash", hash));
}
async function cmdRender(args: string[]): Promise<void> {
@@ -469,6 +471,10 @@ async function cmdVarSet(args: string[]): Promise<void> {
die("Usage: ocas var set <name> <hash> [--tag <tag>...]");
}
if (name.startsWith("@ocas/")) {
die("The @ocas/ namespace is reserved and cannot be modified directly.");
}
const store = await openStore();
const varStore = createVariableStore(resolve(varDbPath), store);
@@ -497,7 +503,7 @@ async function cmdVarSet(args: string[]): Promise<void> {
: undefined;
const variable = varStore.set(name, value, options);
out(await wrapEnvelope(store, "@output/var-set", variable));
out(await wrapEnvelope(store, "@ocas/output/var-set", variable));
} catch (e) {
if (
e instanceof InvalidVariableNameError ||
@@ -528,7 +534,7 @@ async function cmdVarGet(args: string[]): Promise<void> {
if (variable === null) {
die(`Error: Variable not found: name=${name}, schema=${schema}`);
}
out(await wrapEnvelope(store, "@output/var-get", variable));
out(await wrapEnvelope(store, "@ocas/output/var-get", variable));
} finally {
varStore.close();
}
@@ -542,6 +548,10 @@ async function cmdVarDelete(args: string[]): Promise<void> {
die("Usage: ocas var delete <name> [--schema <hash>]");
}
if (name.startsWith("@ocas/")) {
die("The @ocas/ namespace is reserved and cannot be modified directly.");
}
const store = await openStore();
const varStore = createVariableStore(resolve(varDbPath), store);
@@ -549,11 +559,11 @@ async function cmdVarDelete(args: string[]): Promise<void> {
if (schema !== undefined) {
// Precise deletion: remove specific (name, schema) variant
const variable = varStore.remove(name, schema);
out(await wrapEnvelope(store, "@output/var-delete", variable));
out(await wrapEnvelope(store, "@ocas/output/var-delete", variable));
} else {
// Batch deletion: remove all variants for this name
const variables = varStore.remove(name);
out(await wrapEnvelope(store, "@output/var-delete", variables));
out(await wrapEnvelope(store, "@ocas/output/var-delete", variables));
}
} catch (e) {
if (e instanceof VariableNotFoundError) {
@@ -590,7 +600,7 @@ async function cmdVarTag(args: string[]): Promise<void> {
delete: deleteNames.length > 0 ? deleteNames : undefined,
});
out(await wrapEnvelope(store, "@output/var-tag", variable));
out(await wrapEnvelope(store, "@ocas/output/var-tag", variable));
} catch (e) {
if (
e instanceof VariableNotFoundError ||
@@ -633,7 +643,7 @@ async function cmdVarList(args: string[]): Promise<void> {
tags: Object.keys(tags).length > 0 ? tags : undefined,
labels: labels.length > 0 ? labels : undefined,
});
out(await wrapEnvelope(store, "@output/var-list", variables));
out(await wrapEnvelope(store, "@ocas/output/var-list", variables));
} catch (e) {
if (e instanceof InvalidVariableNameError) {
die(`Error: ${e.message}`);
@@ -691,7 +701,7 @@ async function cmdTemplateSet(args: string[]): Promise<void> {
}
// Store content in CAS under @string schema
const stringHash = await resolveTypeHash("@string");
const stringHash = await resolveTypeHash("@ocas/string");
const contentHash = await store.put(stringHash, content);
// Create variable binding: @ocas/template/text/<schema-hash>
@@ -699,7 +709,7 @@ async function cmdTemplateSet(args: string[]): Promise<void> {
varStore.set(varName, contentHash);
out(
await wrapEnvelope(store, "@output/template-set", {
await wrapEnvelope(store, "@ocas/output/template-set", {
schemaHash,
contentHash,
}),
@@ -726,7 +736,7 @@ async function cmdTemplateGet(args: string[]): Promise<void> {
try {
const varName = `@ocas/template/text/${schemaHash}`;
const stringHash = await resolveTypeHash("@string");
const stringHash = await resolveTypeHash("@ocas/string");
const variable = varStore.get(varName, stringHash);
if (variable === null) {
@@ -740,7 +750,11 @@ async function cmdTemplateGet(args: string[]): Promise<void> {
}
out(
await wrapEnvelope(store, "@output/template-get", node.payload as string),
await wrapEnvelope(
store,
"@ocas/output/template-get",
node.payload as string,
),
);
} finally {
varStore.close();
@@ -752,7 +766,7 @@ async function cmdTemplateList(_args: string[]): Promise<void> {
const varStore = createVariableStore(resolve(varDbPath), store);
try {
const stringHash = await resolveTypeHash("@string");
const stringHash = await resolveTypeHash("@ocas/string");
const variables = varStore.list({
namePrefix: "@ocas/template/text/",
schema: stringHash,
@@ -763,7 +777,7 @@ async function cmdTemplateList(_args: string[]): Promise<void> {
contentHash: v.value,
}));
out(await wrapEnvelope(store, "@output/template-list", templates));
out(await wrapEnvelope(store, "@ocas/output/template-list", templates));
} finally {
varStore.close();
}
@@ -781,11 +795,13 @@ async function cmdTemplateDelete(args: string[]): Promise<void> {
try {
const varName = `@ocas/template/text/${schemaHash}`;
const stringHash = await resolveTypeHash("@string");
const stringHash = await resolveTypeHash("@ocas/string");
varStore.remove(varName, stringHash);
out(
await wrapEnvelope(store, "@output/template-delete", { deleted: true }),
await wrapEnvelope(store, "@ocas/output/template-delete", {
deleted: true,
}),
);
} catch (e) {
if (e instanceof VariableNotFoundError) {
@@ -803,7 +819,7 @@ async function cmdGc(_args: string[]): Promise<void> {
try {
const stats = gc(store, varStore);
out(await wrapEnvelope(store, "@output/gc", stats));
out(await wrapEnvelope(store, "@ocas/output/gc", stats));
} finally {
varStore.close();
}
@@ -816,19 +832,19 @@ async function cmdList(_args: string[]): Promise<void> {
const typeHash = await resolveTypeHash(typeFlag);
const store = await openStore();
const hashes = Array.from(store.listByType(typeHash));
out(await wrapEnvelope(store, "@output/list", hashes));
out(await wrapEnvelope(store, "@ocas/output/list", hashes));
}
async function cmdListMeta(_args: string[]): Promise<void> {
const store = await openStore();
const hashes = store.listMeta();
out(await wrapEnvelope(store, "@output/list-meta", hashes));
out(await wrapEnvelope(store, "@ocas/output/list-meta", hashes));
}
async function cmdListSchema(_args: string[]): Promise<void> {
const store = await openStore();
const hashes = store.listSchemas();
out(await wrapEnvelope(store, "@output/list-schema", hashes));
out(await wrapEnvelope(store, "@ocas/output/list-schema", hashes));
}
function printUsage(): void {
@@ -836,35 +852,35 @@ function printUsage(): void {
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
\`render -p\` to render its value (cas_ref hashes are expanded).
command's @ocas/output/* schema (shown in parentheses); pipe any envelope into
\`render -p\` to render its value (ocas_ref hashes are expanded).
Commands:
put <type-hash> <file.json|--pipe> Store node, print envelope (value=hash) (@output/put)
get <hash> Print node as envelope (@output/get)
has <hash> Print envelope (value=boolean) (@output/has)
verify <hash> Verify integrity + schema (value=ok/corrupted/invalid) (@output/verify)
refs <hash> List direct cas_ref edges (@output/refs)
walk <hash> [--format tree] Recursive traversal (@output/walk)
hash <type-hash> <file.json|--pipe> Compute hash without storing (@output/hash)
put <type-hash> <file.json|--pipe> Store node, print envelope (value=hash) (@ocas/output/put)
get <hash> Print node as envelope (@ocas/output/get)
has <hash> Print envelope (value=boolean) (@ocas/output/has)
verify <hash> Verify integrity + schema (value=ok/corrupted/invalid) (@ocas/output/verify)
refs <hash> List direct ocas_ref edges (@ocas/output/refs)
walk <hash> [--format tree] Recursive traversal (@ocas/output/walk)
hash <type-hash> <file.json|--pipe> Compute hash without storing (@ocas/output/hash)
render <hash> [options] Render node as text with resolution decay (raw output)
render --pipe/-p [options] Render { type, value } from stdin (raw output)
list --type <hash-or-alias> List hashes for a type (value=string[]) (@output/list)
list-meta List meta-schema hashes (value=string[]) (@output/list-meta)
list-schema List all schema hashes (value=string[]) (@output/list-schema)
var set <name> <hash> [--tag <tag>...] Create/update a variable (@output/var-set)
var get <name> --schema <hash> Get a variable by name + schema (@output/var-get)
var delete <name> [--schema <hash>] Delete variable(s) (@output/var-delete)
var list [prefix] [--schema <hash>] [--tag <tag>...] List variables (@output/var-list)
var tag <name> --schema <hash> <operations...> Modify tags/labels (@output/var-tag)
template set <schema-hash> <file> | --inline <text> Set template for schema (@output/template-set)
template get <schema-hash> Get template content (value=string) (@output/template-get)
template list List all templates (@output/template-list)
template delete <schema-hash> Delete template for schema (@output/template-delete)
gc Run garbage collection (@output/gc)
list --type <hash-or-alias> List hashes for a type (value=string[]) (@ocas/output/list)
list-meta List meta-schema hashes (value=string[]) (@ocas/output/list-meta)
list-schema List all schema hashes (value=string[]) (@ocas/output/list-schema)
var set <name> <hash> [--tag <tag>...] Create/update a variable (@ocas/output/var-set)
var get <name> --schema <hash> Get a variable by name + schema (@ocas/output/var-get)
var delete <name> [--schema <hash>] Delete variable(s) (@ocas/output/var-delete)
var list [prefix] [--schema <hash>] [--tag <tag>...] List variables (@ocas/output/var-list)
var tag <name> --schema <hash> <operations...> Modify tags/labels (@ocas/output/var-tag)
template set <schema-hash> <file> | --inline <text> Set template for schema (@ocas/output/template-set)
template get <schema-hash> Get template content (value=string) (@ocas/output/template-get)
template list List all templates (@ocas/output/template-list)
template delete <schema-hash> Delete template for schema (@ocas/output/template-delete)
gc Run garbage collection (@ocas/output/gc)
Flags:
--store <path> Store directory (default: ~/.ocas)
--store <path> Store directory (default: $OCAS_HOME or ~/.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
@@ -10,32 +10,32 @@ exports[`Phase 7: Edge Cases 7.5 no subcommand shows help text 1`] = `
"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
\`render -p\` to render its value (cas_ref hashes are expanded).
command's @ocas/output/* schema (shown in parentheses); pipe any envelope into
\`render -p\` to render its value (ocas_ref hashes are expanded).
Commands:
put <type-hash> <file.json|--pipe> Store node, print envelope (value=hash) (@output/put)
get <hash> Print node as envelope (@output/get)
has <hash> Print envelope (value=boolean) (@output/has)
verify <hash> Verify integrity + schema (value=ok/corrupted/invalid) (@output/verify)
refs <hash> List direct cas_ref edges (@output/refs)
walk <hash> [--format tree] Recursive traversal (@output/walk)
hash <type-hash> <file.json|--pipe> Compute hash without storing (@output/hash)
put <type-hash> <file.json|--pipe> Store node, print envelope (value=hash) (@ocas/output/put)
get <hash> Print node as envelope (@ocas/output/get)
has <hash> Print envelope (value=boolean) (@ocas/output/has)
verify <hash> Verify integrity + schema (value=ok/corrupted/invalid) (@ocas/output/verify)
refs <hash> List direct ocas_ref edges (@ocas/output/refs)
walk <hash> [--format tree] Recursive traversal (@ocas/output/walk)
hash <type-hash> <file.json|--pipe> Compute hash without storing (@ocas/output/hash)
render <hash> [options] Render node as text with resolution decay (raw output)
render --pipe/-p [options] Render { type, value } from stdin (raw output)
list --type <hash-or-alias> List hashes for a type (value=string[]) (@output/list)
list-meta List meta-schema hashes (value=string[]) (@output/list-meta)
list-schema List all schema hashes (value=string[]) (@output/list-schema)
var set <name> <hash> [--tag <tag>...] Create/update a variable (@output/var-set)
var get <name> --schema <hash> Get a variable by name + schema (@output/var-get)
var delete <name> [--schema <hash>] Delete variable(s) (@output/var-delete)
var list [prefix] [--schema <hash>] [--tag <tag>...] List variables (@output/var-list)
var tag <name> --schema <hash> <operations...> Modify tags/labels (@output/var-tag)
template set <schema-hash> <file> | --inline <text> Set template for schema (@output/template-set)
template get <schema-hash> Get template content (value=string) (@output/template-get)
template list List all templates (@output/template-list)
template delete <schema-hash> Delete template for schema (@output/template-delete)
gc Run garbage collection (@output/gc)
list --type <hash-or-alias> List hashes for a type (value=string[]) (@ocas/output/list)
list-meta List meta-schema hashes (value=string[]) (@ocas/output/list-meta)
list-schema List all schema hashes (value=string[]) (@ocas/output/list-schema)
var set <name> <hash> [--tag <tag>...] Create/update a variable (@ocas/output/var-set)
var get <name> --schema <hash> Get a variable by name + schema (@ocas/output/var-get)
var delete <name> [--schema <hash>] Delete variable(s) (@ocas/output/var-delete)
var list [prefix] [--schema <hash>] [--tag <tag>...] List variables (@ocas/output/var-list)
var tag <name> --schema <hash> <operations...> Modify tags/labels (@ocas/output/var-tag)
template set <schema-hash> <file> | --inline <text> Set template for schema (@ocas/output/template-set)
template get <schema-hash> Get template content (value=string) (@ocas/output/template-get)
template list List all templates (@ocas/output/template-list)
template delete <schema-hash> Delete template for schema (@ocas/output/template-delete)
gc Run garbage collection (@ocas/output/gc)
Flags:
--store <path> Store directory (default: ~/.ocas)
@@ -52,7 +52,7 @@ Flags:
exports[`Phase 3: Variable System 3.1 var set creates variable 1`] = `
{
"type": "50CG6QYD1FY8J",
"type": "0Q5EMYK4SYSS9",
"value": {
"labels": [],
"name": "myapp/config",
@@ -65,7 +65,7 @@ exports[`Phase 3: Variable System 3.1 var set creates variable 1`] = `
exports[`Phase 3: Variable System 3.2 var get returns variable 1`] = `
{
"type": "DSXYC9DNCYAK0",
"type": "7C75FQT98KKQD",
"value": {
"labels": [],
"name": "myapp/config",
@@ -78,7 +78,7 @@ exports[`Phase 3: Variable System 3.2 var get returns variable 1`] = `
exports[`Phase 3: Variable System 3.3 var list shows all variables 1`] = `
{
"type": "8F5ENFRC57Y7H",
"type": "AF0XACGXHPMC1",
"value": [
{
"labels": [],
@@ -93,7 +93,7 @@ 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": "8F5ENFRC57Y7H",
"type": "AF0XACGXHPMC1",
"value": [
{
"labels": [],
@@ -108,7 +108,7 @@ 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": "50CG6QYD1FY8J",
"type": "0Q5EMYK4SYSS9",
"value": {
"labels": [],
"name": "myapp/config",
@@ -121,7 +121,7 @@ exports[`Phase 3: Variable System 3.5 var set upsert updates existing variable 1
exports[`Phase 3: Variable System 3.6 var tag adds kv tag and label 1`] = `
{
"type": "6D1HG60NTZY87",
"type": "9103EYRMM949A",
"value": {
"labels": [
"important",
@@ -138,7 +138,7 @@ exports[`Phase 3: Variable System 3.6 var tag adds kv tag and label 1`] = `
exports[`Phase 3: Variable System 3.7 var list --tag env:prod filters by kv tag 1`] = `
{
"type": "8F5ENFRC57Y7H",
"type": "AF0XACGXHPMC1",
"value": [
{
"labels": [
@@ -157,7 +157,7 @@ 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": "8F5ENFRC57Y7H",
"type": "AF0XACGXHPMC1",
"value": [
{
"labels": [
@@ -176,7 +176,7 @@ 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": "6D1HG60NTZY87",
"type": "9103EYRMM949A",
"value": {
"labels": [],
"name": "myapp/config",
@@ -191,7 +191,7 @@ exports[`Phase 3: Variable System 3.9 var tag remove deletes label 1`] = `
exports[`Phase 3: Variable System 3.10 var delete removes variable 1`] = `
{
"type": "25VAMSJT0X51K",
"type": "C3MYPR5RGQFZT",
"value": [
{
"labels": [],
@@ -210,7 +210,7 @@ exports[`Phase 3: Variable System 3.11 var get deleted variable returns not foun
exports[`Phase 4: Template System 4.1 template set registers template 1`] = `
{
"type": "7R13JNSN53R67",
"type": "BJDHPAE4Q8TXM",
"value": {
"contentHash": "6WW8WNB38GTTP",
"schemaHash": "FRBAB1BF0ZBCS",
@@ -227,7 +227,7 @@ exports[`Phase 4: Template System 4.2 template get returns template text 1`] = `
exports[`Phase 4: Template System 4.3 template list shows registered templates 1`] = `
{
"type": "5GE3Y0EQS0HP4",
"type": "8917JQTD1R5JF",
"value": [
{
"contentHash": "6WW8WNB38GTTP",
@@ -2,7 +2,7 @@
exports[`Phase 1: CAS Core 1.6 get returns node JSON (snapshot) 1`] = `
{
"type": "CCVN1ECY8JKQ0",
"type": "FB4K0SXG68ZFS",
"value": {
"payload": {
"age": 30,
@@ -9,7 +9,7 @@ exports[`Phase 1: CAS Core 1.9 verify returns ok for valid node 1`] = `
exports[`Phase 1: CAS Core 1.10 refs lists direct references (snapshot) 1`] = `
"{
"type": "35QJB2WESFFGQ",
"type": "2TKP4RGBJ4V43",
"value": []
}"
`;
+5 -5
View File
@@ -75,7 +75,7 @@ describe("@ Alias Resolution - put", () => {
const { stdout, stderr, exitCode } = await runCliAlias(
"put",
"@string",
"@ocas/string",
payloadFile,
);
@@ -93,7 +93,7 @@ describe("@ Alias Resolution - put", () => {
const { stdout, exitCode } = await runCliAlias(
"put",
"@number",
"@ocas/number",
payloadFile,
);
@@ -109,7 +109,7 @@ describe("@ Alias Resolution - put", () => {
const { stdout, exitCode } = await runCliAlias(
"put",
"@object",
"@ocas/object",
payloadFile,
);
@@ -157,7 +157,7 @@ describe("@ Alias Resolution - put", () => {
const { stdout, stderr, exitCode } = await runCliAlias(
"put",
"@schema",
"@ocas/schema",
schemaFile,
);
@@ -176,7 +176,7 @@ describe("@ Alias Resolution - hash", () => {
const { stdout, stderr, exitCode } = await runCliAlias(
"hash",
"@string",
"@ocas/string",
payloadFile,
);
+4 -4
View File
@@ -21,7 +21,7 @@ describe("list-meta CLI command", () => {
test("E1. list-meta on bootstrapped store contains exactly the meta-schema hash", async () => {
// First, get @schema hash by calling has on it (also triggers bootstrap)
const { stdout: hashOut, exitCode: hashCode } = await runCli(
["hash", "@schema", "--pipe"],
["hash", "@ocas/schema", "--pipe"],
storePath,
);
// ensure bootstrap by running a no-op command:
@@ -30,7 +30,7 @@ describe("list-meta CLI command", () => {
// Bootstrap fully via 'list --type @schema'
const { stdout: schemaListOut } = await runCli(
["list", "--type", "@schema"],
["list", "--type", "@ocas/schema"],
storePath,
);
const schemaList = envValue(schemaListOut) as string[];
@@ -85,11 +85,11 @@ describe("usage help", () => {
});
describe("F1. output schemas registered", () => {
test("@output/list-meta and @output/list-schema schemas exist", async () => {
test("@ocas/output/list-meta and @ocas/output/list-schema schemas exist", async () => {
const { stdout, exitCode } = await runCli(["list-meta"], storePath);
expect(exitCode).toBe(0);
const parsed = JSON.parse(stdout) as { type: string };
// type hash references the @output/list-meta schema, must be retrievable
// type hash references the @ocas/output/list-meta schema, must be retrievable
const { stdout: getOut, exitCode: getCode } = await runCli(
["get", parsed.type],
storePath,
+12 -4
View File
@@ -91,7 +91,7 @@ describe("Phase 8: Pipe Composition", () => {
]);
expect(putExit).toBe(0);
// The put envelope value is a cas_ref hash; render -p dereferences it and
// The put envelope value is a ocas_ref hash; render -p dereferences it and
// renders the stored node's payload.
const { stdout, exitCode } = await runCliWithStdin(
["render", "--pipe"],
@@ -102,7 +102,11 @@ describe("Phase 8: Pipe Composition", () => {
});
test("8.3 list --type @schema emits a parseable envelope of hashes", async () => {
const { stdout, exitCode } = await runCli(["list", "--type", "@schema"]);
const { stdout, exitCode } = await runCli([
"list",
"--type",
"@ocas/schema",
]);
expect(exitCode).toBe(0);
// Downstream consumers (jq, etc.) read the `value` array of hashes.
@@ -114,8 +118,12 @@ describe("Phase 8: Pipe Composition", () => {
});
test("8.4 list --type @schema | render -p expands the schema list", async () => {
const { stdout: listOut } = await runCli(["list", "--type", "@schema"]);
// list result items are cas_ref hashes; render -p dereferences each one
const { stdout: listOut } = await runCli([
"list",
"--type",
"@ocas/schema",
]);
// list result items are ocas_ref hashes; render -p dereferences each one
// and renders the schema contents.
const { stdout, exitCode } = await runCliWithStdin(
["render", "--pipe"],
+27 -41
View File
@@ -179,18 +179,15 @@ describe("Suite 6: CLI Integration with Templates", () => {
);
const nodeHash = envValue(nodeOut) as string;
// Create template file (JSON-encoded string)
const templateFile = join(tmpStore, "template.json");
writeFileSync(templateFile, JSON.stringify("Hello {{ payload.name }}!"));
const { stdout: tmplOut } = await runCli(
["put", "@string", templateFile],
tmpStore,
);
const tmplHash = envValue(tmplOut) as string;
// Register template
// Register template via template set --inline
await runCli(
["var", "set", `@ocas/template/text/${schemaHash.trim()}`, tmplHash],
[
"template",
"set",
schemaHash.trim(),
"--inline",
"Hello {{ payload.name }}!",
],
tmpStore,
);
@@ -221,7 +218,7 @@ describe("Suite 6: CLI Integration with Templates", () => {
properties: {
value: { type: "string" },
child: {
anyOf: [{ type: "string", format: "cas_ref" }, { type: "null" }],
anyOf: [{ type: "string", format: "ocas_ref" }, { type: "null" }],
},
},
}),
@@ -249,21 +246,15 @@ describe("Suite 6: CLI Integration with Templates", () => {
);
const parentHash = envValue(parentOut) as string;
// Create template showing resolution (JSON-encoded string)
const templateFile = join(tmpStore, "template.json");
writeFileSync(
templateFile,
JSON.stringify("{{ payload.value }}(res={{ resolution }})"),
);
const { stdout: tmplOut } = await runCli(
["put", "@string", templateFile],
tmpStore,
);
const tmplHash = envValue(tmplOut) as string;
// Register template
// Register template via template set --inline
await runCli(
["var", "set", `@ocas/template/text/${schemaHash.trim()}`, tmplHash],
[
"template",
"set",
schemaHash.trim(),
"--inline",
"{{ payload.value }}(res={{ resolution }})",
],
tmpStore,
);
@@ -303,20 +294,15 @@ describe("Suite 6: CLI Integration with Templates", () => {
);
const nodeHash = envValue(nodeOut) as string;
// Create template (JSON-encoded string)
const templateFile = join(tmpStore, "template.json");
writeFileSync(
templateFile,
JSON.stringify("Greetings {{ payload.name }}!"),
);
const { stdout: tmplOut } = await runCli(
["put", "@string", templateFile],
tmpStore,
);
const tmplHash = envValue(tmplOut) as string;
// Register template via template set --inline
await runCli(
["var", "set", `@ocas/template/text/${schemaHash.trim()}`, tmplHash],
[
"template",
"set",
schemaHash.trim(),
"--inline",
"Greetings {{ payload.name }}!",
],
tmpStore,
);
@@ -438,7 +424,7 @@ describe("Suite 6: CLI Integration with Templates", () => {
// Get @string type hash via bootstrap
const store = createFsStore(tmpStore);
const types = await bootstrap(store);
const stringType = types["@string"];
const stringType = types["@ocas/string"];
// Create and store a simple string node
const nodeFile = join(tmpStore, "test.json");
@@ -473,7 +459,7 @@ describe("Suite 6: CLI Integration with Templates", () => {
// Get @string type hash via bootstrap
const store = createFsStore(tmpStore);
const types = await bootstrap(store);
const stringType = types["@string"];
const stringType = types["@ocas/string"];
// Create envelope and pipe to render
const envelope = JSON.stringify({ type: stringType, value: "test" });
+6 -6
View File
@@ -142,7 +142,7 @@ describe("Issue #50: Schema Validation in put", () => {
writeFileSync(payloadFile, JSON.stringify("hello world"));
const { exitCode, stdout } = await runCli(
["put", "@string", payloadFile],
["put", "@ocas/string", payloadFile],
tmpStore,
);
@@ -422,25 +422,25 @@ describe("Issue #50: Schema Validation in put", () => {
}
});
test("T4.2: Validation respects cas_ref format in schemas", async () => {
test("T4.2: Validation respects ocas_ref format in schemas", async () => {
const tmpStore = mkdtempSync(join(tmpdir(), "ocas-test-"));
try {
await runCli(["init"], tmpStore);
// Schema with cas_ref format
// Schema with ocas_ref format
const schemaFile = join(tmpStore, "schema.json");
writeFileSync(
schemaFile,
JSON.stringify({
type: "object",
properties: {
ref: { type: "string", format: "cas_ref" },
ref: { type: "string", format: "ocas_ref" },
},
}),
);
const schemaHash = await putSchemaFile(tmpStore, schemaFile);
// Valid cas_ref
// Valid ocas_ref
const validFile = join(tmpStore, "valid.json");
writeFileSync(validFile, JSON.stringify({ ref: "0000000000000" }));
@@ -450,7 +450,7 @@ describe("Issue #50: Schema Validation in put", () => {
);
expect(validExitCode).toBe(0);
// Invalid cas_ref (wrong length)
// Invalid ocas_ref (wrong length)
const invalidFile = join(tmpStore, "invalid.json");
writeFileSync(invalidFile, JSON.stringify({ ref: "short" }));
+1 -1
View File
@@ -80,7 +80,7 @@ async function runCli(...args: string[]): Promise<{
*/
async function getStringHash(store: Store): Promise<Hash> {
const builtinSchemas = await bootstrap(store);
return builtinSchemas["@string"] ?? "";
return builtinSchemas["@ocas/string"] ?? "";
}
// ---- Tests ----
+1 -1
View File
@@ -91,7 +91,7 @@ async function createTestNode(
*/
async function getBootstrapHash(store: Store): Promise<Hash> {
const builtinSchemas = await bootstrap(store);
return builtinSchemas["@schema"] ?? "";
return builtinSchemas["@ocas/schema"] ?? "";
}
// ---- Tests ----
+79 -79
View File
@@ -5,26 +5,26 @@ import { getSchema } from "./schema.js";
import { createMemoryStore } from "./store.js";
const OUTPUT_ALIASES = [
"@output/put",
"@output/get",
"@output/has",
"@output/hash",
"@output/verify",
"@output/refs",
"@output/walk",
"@output/list",
"@output/list-meta",
"@output/list-schema",
"@output/var-set",
"@output/var-get",
"@output/var-delete",
"@output/var-tag",
"@output/var-list",
"@output/template-set",
"@output/template-get",
"@output/template-list",
"@output/template-delete",
"@output/gc",
"@ocas/output/put",
"@ocas/output/get",
"@ocas/output/has",
"@ocas/output/hash",
"@ocas/output/verify",
"@ocas/output/refs",
"@ocas/output/walk",
"@ocas/output/list",
"@ocas/output/list-meta",
"@ocas/output/list-schema",
"@ocas/output/var-set",
"@ocas/output/var-get",
"@ocas/output/var-delete",
"@ocas/output/var-tag",
"@ocas/output/var-list",
"@ocas/output/template-set",
"@ocas/output/template-get",
"@ocas/output/template-list",
"@ocas/output/template-delete",
"@ocas/output/gc",
] as const;
// ──────────────────────────────────────────────────────────────────────────────
@@ -37,12 +37,12 @@ describe("bootstrap - Built-in Schemas", () => {
const builtinSchemas = await bootstrap(store);
// Should return object with 6 primitive + 20 output aliases = 26
expect(builtinSchemas).toHaveProperty("@schema");
expect(builtinSchemas).toHaveProperty("@string");
expect(builtinSchemas).toHaveProperty("@number");
expect(builtinSchemas).toHaveProperty("@object");
expect(builtinSchemas).toHaveProperty("@array");
expect(builtinSchemas).toHaveProperty("@bool");
expect(builtinSchemas).toHaveProperty("@ocas/schema");
expect(builtinSchemas).toHaveProperty("@ocas/string");
expect(builtinSchemas).toHaveProperty("@ocas/number");
expect(builtinSchemas).toHaveProperty("@ocas/object");
expect(builtinSchemas).toHaveProperty("@ocas/array");
expect(builtinSchemas).toHaveProperty("@ocas/bool");
for (const alias of OUTPUT_ALIASES) {
expect(builtinSchemas).toHaveProperty(alias);
@@ -61,7 +61,7 @@ describe("bootstrap - Built-in Schemas", () => {
const store = createMemoryStore();
const builtinSchemas = await bootstrap(store);
const metaHash = builtinSchemas["@schema"];
const metaHash = builtinSchemas["@ocas/schema"];
if (!metaHash) throw new Error("@schema not found");
const metaSchema = getSchema(store, metaHash);
@@ -74,7 +74,7 @@ describe("bootstrap - Built-in Schemas", () => {
const store = createMemoryStore();
const builtinSchemas = await bootstrap(store);
const stringHash = builtinSchemas["@string"];
const stringHash = builtinSchemas["@ocas/string"];
if (!stringHash) throw new Error("@string not found");
const stringSchema = getSchema(store, stringHash);
@@ -85,7 +85,7 @@ describe("bootstrap - Built-in Schemas", () => {
const store = createMemoryStore();
const builtinSchemas = await bootstrap(store);
const numberHash = builtinSchemas["@number"];
const numberHash = builtinSchemas["@ocas/number"];
if (!numberHash) throw new Error("@number not found");
const numberSchema = getSchema(store, numberHash);
@@ -96,7 +96,7 @@ describe("bootstrap - Built-in Schemas", () => {
const store = createMemoryStore();
const builtinSchemas = await bootstrap(store);
const objectHash = builtinSchemas["@object"];
const objectHash = builtinSchemas["@ocas/object"];
if (!objectHash) throw new Error("@object not found");
const objectSchema = getSchema(store, objectHash);
@@ -107,19 +107,19 @@ describe("bootstrap - Built-in Schemas", () => {
const store = createMemoryStore();
const builtinSchemas = await bootstrap(store);
const arrayHash = builtinSchemas["@array"];
const arrayHash = builtinSchemas["@ocas/array"];
if (!arrayHash) throw new Error("@array not found");
const arraySchema = getSchema(store, arrayHash);
expect(arraySchema).toEqual({ type: "array" });
});
test("should register @bool schema correctly", async () => {
test("should register @ocas/bool schema correctly", async () => {
const store = createMemoryStore();
const builtinSchemas = await bootstrap(store);
const boolHash = builtinSchemas["@bool"];
if (!boolHash) throw new Error("@bool not found");
const boolHash = builtinSchemas["@ocas/bool"];
if (!boolHash) throw new Error("@ocas/bool not found");
const boolSchema = getSchema(store, boolHash);
expect(boolSchema).toEqual({ type: "boolean" });
@@ -133,23 +133,23 @@ describe("bootstrap - Built-in Schemas", () => {
expect(first).toEqual(second);
// Verify each alias points to same hash
expect(first["@string"]).toBe(second["@string"]);
expect(first["@number"]).toBe(second["@number"]);
expect(first["@object"]).toBe(second["@object"]);
expect(first["@array"]).toBe(second["@array"]);
expect(first["@bool"]).toBe(second["@bool"]);
expect(first["@schema"]).toBe(second["@schema"]);
expect(first["@ocas/string"]).toBe(second["@ocas/string"]);
expect(first["@ocas/number"]).toBe(second["@ocas/number"]);
expect(first["@ocas/object"]).toBe(second["@ocas/object"]);
expect(first["@ocas/array"]).toBe(second["@ocas/array"]);
expect(first["@ocas/bool"]).toBe(second["@ocas/bool"]);
expect(first["@ocas/schema"]).toBe(second["@ocas/schema"]);
});
test("all built-in schemas should be typed by meta-schema", async () => {
const store = createMemoryStore();
const builtinSchemas = await bootstrap(store);
const metaHash = builtinSchemas["@schema"];
const metaHash = builtinSchemas["@ocas/schema"];
if (!metaHash) throw new Error("@schema not found");
for (const [alias, hash] of Object.entries(builtinSchemas)) {
if (alias === "@schema") continue; // meta-schema is self-typed
if (alias === "@ocas/schema") continue; // meta-schema is self-typed
const node = store.get(hash);
expect(node).not.toBeNull();
@@ -159,11 +159,11 @@ describe("bootstrap - Built-in Schemas", () => {
});
// ──────────────────────────────────────────────────────────────────────────────
// @output/* Schema Registration Tests
// @ocas/output/* Schema Registration Tests
// ──────────────────────────────────────────────────────────────────────────────
describe("bootstrap - @output/* Schemas", () => {
test("each @output/* schema has a title", async () => {
describe("bootstrap - @ocas/output/* Schemas", () => {
test("each @ocas/output/* schema has a title", async () => {
const store = createMemoryStore();
const aliases = await bootstrap(store);
@@ -178,41 +178,41 @@ describe("bootstrap - @output/* Schemas", () => {
}
});
test("@output/put schema describes a cas_ref string", async () => {
test("@ocas/output/put schema describes a ocas_ref string", async () => {
const store = createMemoryStore();
const aliases = await bootstrap(store);
const hash = aliases["@output/put"];
if (!hash) throw new Error("@output/put not found");
const hash = aliases["@ocas/output/put"];
if (!hash) throw new Error("@ocas/output/put not found");
const schema = getSchema(store, hash);
expect(schema).toEqual({
type: "string",
format: "cas_ref",
format: "ocas_ref",
title: "ocas put result",
});
});
test("@output/get schema describes object with type, payload, timestamp", async () => {
test("@ocas/output/get schema describes object with type, payload, timestamp", async () => {
const store = createMemoryStore();
const aliases = await bootstrap(store);
const hash = aliases["@output/get"];
if (!hash) throw new Error("@output/get not found");
const hash = aliases["@ocas/output/get"];
if (!hash) throw new Error("@ocas/output/get not found");
const schema = getSchema(store, hash) as JSONSchema;
expect(schema.type).toBe("object");
expect(schema.title).toBe("ocas get result");
const props = schema.properties as Record<string, JSONSchema>;
expect(props.type).toEqual({ type: "string", format: "cas_ref" });
expect(props.type).toEqual({ type: "string", format: "ocas_ref" });
expect(props.payload).toEqual({});
expect(props.timestamp).toEqual({ type: "number" });
});
test("@output/has schema describes a boolean", async () => {
test("@ocas/output/has schema describes a boolean", async () => {
const store = createMemoryStore();
const aliases = await bootstrap(store);
const hash = aliases["@output/has"];
if (!hash) throw new Error("@output/has not found");
const hash = aliases["@ocas/output/has"];
if (!hash) throw new Error("@ocas/output/has not found");
expect(getSchema(store, hash)).toEqual({
type: "boolean",
@@ -220,11 +220,11 @@ describe("bootstrap - @output/* Schemas", () => {
});
});
test("@output/verify schema describes enum of ok|corrupted|invalid", async () => {
test("@ocas/output/verify schema describes enum of ok|corrupted|invalid", async () => {
const store = createMemoryStore();
const aliases = await bootstrap(store);
const hash = aliases["@output/verify"];
if (!hash) throw new Error("@output/verify not found");
const hash = aliases["@ocas/output/verify"];
if (!hash) throw new Error("@ocas/output/verify not found");
const schema = getSchema(store, hash);
expect(schema).toEqual({
@@ -234,24 +234,24 @@ describe("bootstrap - @output/* Schemas", () => {
});
});
test("@output/refs schema describes array of cas_ref strings", async () => {
test("@ocas/output/refs schema describes array of ocas_ref strings", async () => {
const store = createMemoryStore();
const aliases = await bootstrap(store);
const hash = aliases["@output/refs"];
if (!hash) throw new Error("@output/refs not found");
const hash = aliases["@ocas/output/refs"];
if (!hash) throw new Error("@ocas/output/refs not found");
expect(getSchema(store, hash)).toEqual({
type: "array",
items: { type: "string", format: "cas_ref" },
items: { type: "string", format: "ocas_ref" },
title: "ocas refs result",
});
});
test("@output/gc schema describes object with gc stats fields", async () => {
test("@ocas/output/gc schema describes object with gc stats fields", async () => {
const store = createMemoryStore();
const aliases = await bootstrap(store);
const hash = aliases["@output/gc"];
if (!hash) throw new Error("@output/gc not found");
const hash = aliases["@ocas/output/gc"];
if (!hash) throw new Error("@ocas/output/gc not found");
const schema = getSchema(store, hash) as JSONSchema;
expect(schema.type).toBe("object");
@@ -264,11 +264,11 @@ describe("bootstrap - @output/* Schemas", () => {
expect(props.scanned).toEqual({ type: "number" });
});
test("@output/var-set schema describes a Variable object", async () => {
test("@ocas/output/var-set schema describes a Variable object", async () => {
const store = createMemoryStore();
const aliases = await bootstrap(store);
const hash = aliases["@output/var-set"];
if (!hash) throw new Error("@output/var-set not found");
const hash = aliases["@ocas/output/var-set"];
if (!hash) throw new Error("@ocas/output/var-set not found");
const schema = getSchema(store, hash) as JSONSchema;
expect(schema.type).toBe("object");
@@ -276,15 +276,15 @@ describe("bootstrap - @output/* Schemas", () => {
const props = schema.properties as Record<string, JSONSchema>;
expect(props.name).toEqual({ type: "string" });
expect(props.schema).toEqual({ type: "string", format: "cas_ref" });
expect(props.value).toEqual({ type: "string", format: "cas_ref" });
expect(props.schema).toEqual({ type: "string", format: "ocas_ref" });
expect(props.value).toEqual({ type: "string", format: "ocas_ref" });
});
test("@output/var-list schema describes array of Variable objects", async () => {
test("@ocas/output/var-list schema describes array of Variable objects", async () => {
const store = createMemoryStore();
const aliases = await bootstrap(store);
const hash = aliases["@output/var-list"];
if (!hash) throw new Error("@output/var-list not found");
const hash = aliases["@ocas/output/var-list"];
if (!hash) throw new Error("@ocas/output/var-list not found");
const schema = getSchema(store, hash) as JSONSchema;
expect(schema.type).toBe("array");
@@ -296,11 +296,11 @@ describe("bootstrap - @output/* Schemas", () => {
expect(props.name).toEqual({ type: "string" });
});
test("@output/template-delete schema describes object with deleted boolean", async () => {
test("@ocas/output/template-delete schema describes object with deleted boolean", async () => {
const store = createMemoryStore();
const aliases = await bootstrap(store);
const hash = aliases["@output/template-delete"];
if (!hash) throw new Error("@output/template-delete not found");
const hash = aliases["@ocas/output/template-delete"];
if (!hash) throw new Error("@ocas/output/template-delete not found");
expect(getSchema(store, hash)).toEqual({
type: "object",
@@ -309,7 +309,7 @@ describe("bootstrap - @output/* Schemas", () => {
});
});
test("all @output/* schemas are distinct hashes", async () => {
test("all @ocas/output/* schemas are distinct hashes", async () => {
const store = createMemoryStore();
const aliases = await bootstrap(store);
@@ -323,7 +323,7 @@ describe("bootstrap - meta and schemas indexes (D1)", () => {
test("listMeta contains the bootstrap meta-schema hash", async () => {
const store = createMemoryStore();
const aliases = await bootstrap(store);
const metaHash = aliases["@schema"];
const metaHash = aliases["@ocas/schema"];
expect(store.listMeta()).toContain(metaHash as string);
});
+41 -41
View File
@@ -108,8 +108,8 @@ const BOOTSTRAP_PAYLOAD = {
const VARIABLE_PROPERTIES = {
name: { type: "string" },
schema: { type: "string", format: "cas_ref" },
value: { type: "string", format: "cas_ref" },
schema: { type: "string", format: "ocas_ref" },
value: { type: "string", format: "ocas_ref" },
created: { type: "number" },
updated: { type: "number" },
tags: { type: "object" },
@@ -120,28 +120,28 @@ const OUTPUT_SCHEMAS: ReadonlyArray<
readonly [alias: string, schema: Record<string, unknown>]
> = [
[
"@output/put",
{ type: "string", format: "cas_ref", title: "ocas put result" },
"@ocas/output/put",
{ type: "string", format: "ocas_ref", title: "ocas put result" },
],
[
"@output/get",
"@ocas/output/get",
{
type: "object",
properties: {
type: { type: "string", format: "cas_ref" },
type: { type: "string", format: "ocas_ref" },
payload: {},
timestamp: { type: "number" },
},
title: "ocas get result",
},
],
["@output/has", { type: "boolean", title: "ocas has result" }],
["@ocas/output/has", { type: "boolean", title: "ocas has result" }],
[
"@output/hash",
{ type: "string", format: "cas_ref", title: "ocas hash result" },
"@ocas/output/hash",
{ type: "string", format: "ocas_ref", title: "ocas hash result" },
],
[
"@output/verify",
"@ocas/output/verify",
{
type: "string",
enum: ["ok", "corrupted", "invalid"],
@@ -149,15 +149,15 @@ const OUTPUT_SCHEMAS: ReadonlyArray<
},
],
[
"@output/refs",
"@ocas/output/refs",
{
type: "array",
items: { type: "string", format: "cas_ref" },
items: { type: "string", format: "ocas_ref" },
title: "ocas refs result",
},
],
[
"@output/walk",
"@ocas/output/walk",
{
type: "array",
items: { type: "string" },
@@ -165,31 +165,31 @@ const OUTPUT_SCHEMAS: ReadonlyArray<
},
],
[
"@output/list",
"@ocas/output/list",
{
type: "array",
items: { type: "string", format: "cas_ref" },
items: { type: "string", format: "ocas_ref" },
title: "ocas list result",
},
],
[
"@output/list-meta",
"@ocas/output/list-meta",
{
type: "array",
items: { type: "string", format: "cas_ref" },
items: { type: "string", format: "ocas_ref" },
title: "ocas list-meta result",
},
],
[
"@output/list-schema",
"@ocas/output/list-schema",
{
type: "array",
items: { type: "string", format: "cas_ref" },
items: { type: "string", format: "ocas_ref" },
title: "ocas list-schema result",
},
],
[
"@output/var-set",
"@ocas/output/var-set",
{
type: "object",
properties: { ...VARIABLE_PROPERTIES },
@@ -197,7 +197,7 @@ const OUTPUT_SCHEMAS: ReadonlyArray<
},
],
[
"@output/var-get",
"@ocas/output/var-get",
{
type: "object",
properties: { ...VARIABLE_PROPERTIES },
@@ -205,7 +205,7 @@ const OUTPUT_SCHEMAS: ReadonlyArray<
},
],
[
"@output/var-delete",
"@ocas/output/var-delete",
{
type: "object",
properties: { ...VARIABLE_PROPERTIES },
@@ -213,7 +213,7 @@ const OUTPUT_SCHEMAS: ReadonlyArray<
},
],
[
"@output/var-tag",
"@ocas/output/var-tag",
{
type: "object",
properties: { ...VARIABLE_PROPERTIES },
@@ -221,7 +221,7 @@ const OUTPUT_SCHEMAS: ReadonlyArray<
},
],
[
"@output/var-list",
"@ocas/output/var-list",
{
type: "array",
items: { type: "object", properties: { ...VARIABLE_PROPERTIES } },
@@ -229,36 +229,36 @@ const OUTPUT_SCHEMAS: ReadonlyArray<
},
],
[
"@output/template-set",
"@ocas/output/template-set",
{
type: "object",
properties: {
schemaHash: { type: "string", format: "cas_ref" },
contentHash: { type: "string", format: "cas_ref" },
schemaHash: { type: "string", format: "ocas_ref" },
contentHash: { type: "string", format: "ocas_ref" },
},
title: "ocas template set result",
},
],
[
"@output/template-get",
"@ocas/output/template-get",
{ type: "string", title: "ocas template get result" },
],
[
"@output/template-list",
"@ocas/output/template-list",
{
type: "array",
items: {
type: "object",
properties: {
schemaHash: { type: "string", format: "cas_ref" },
contentHash: { type: "string", format: "cas_ref" },
schemaHash: { type: "string", format: "ocas_ref" },
contentHash: { type: "string", format: "ocas_ref" },
},
},
title: "ocas template list result",
},
],
[
"@output/template-delete",
"@ocas/output/template-delete",
{
type: "object",
properties: { deleted: { type: "boolean" } },
@@ -266,7 +266,7 @@ const OUTPUT_SCHEMAS: ReadonlyArray<
},
],
[
"@output/gc",
"@ocas/output/gc",
{
type: "object",
properties: {
@@ -283,7 +283,7 @@ const OUTPUT_SCHEMAS: ReadonlyArray<
/**
* Write the meta-schema seed node into the store and register built-in schemas.
* The returned object contains aliases for the meta-schema, 5 primitive schemas,
* and 18 @output/* schemas (24 total).
* and 18 @ocas/output/* schemas (24 total).
* Idempotent: calling bootstrap multiple times returns the same hashes.
*/
export async function bootstrap(store: Store): Promise<Record<string, Hash>> {
@@ -301,14 +301,14 @@ export async function bootstrap(store: Store): Promise<Record<string, Hash>> {
const arrayHash = await store.put(metaHash, { type: "array" });
const boolHash = await store.put(metaHash, { type: "boolean" });
// 3. Register @output/* schemas
// 3. Register @ocas/output/* schemas
const aliases: Record<string, Hash> = {
"@schema": metaHash,
"@string": stringHash,
"@number": numberHash,
"@object": objectHash,
"@array": arrayHash,
"@bool": boolHash,
"@ocas/schema": metaHash,
"@ocas/string": stringHash,
"@ocas/number": numberHash,
"@ocas/object": objectHash,
"@ocas/array": arrayHash,
"@ocas/bool": boolHash,
};
for (const [alias, schema] of OUTPUT_SCHEMAS) {
+16 -16
View File
@@ -198,16 +198,16 @@ describe("createMemoryStore – listByType", () => {
test("bootstrap node is listed under its self type", async () => {
const store = createMemoryStore();
const builtinSchemas = await bootstrap(store);
const hash = builtinSchemas["@schema"] ?? "";
const hash = builtinSchemas["@ocas/schema"] ?? "";
// All built-in schemas should be typed by the meta-schema
const allTypedByMeta = store.listByType(hash);
expect(allTypedByMeta).toContain(hash); // meta-schema itself
expect(allTypedByMeta).toContain(builtinSchemas["@string"] ?? "");
expect(allTypedByMeta).toContain(builtinSchemas["@number"] ?? "");
expect(allTypedByMeta).toContain(builtinSchemas["@object"] ?? "");
expect(allTypedByMeta).toContain(builtinSchemas["@array"] ?? "");
expect(allTypedByMeta).toContain(builtinSchemas["@bool"] ?? "");
expect(allTypedByMeta).toContain(builtinSchemas["@ocas/string"] ?? "");
expect(allTypedByMeta).toContain(builtinSchemas["@ocas/number"] ?? "");
expect(allTypedByMeta).toContain(builtinSchemas["@ocas/object"] ?? "");
expect(allTypedByMeta).toContain(builtinSchemas["@ocas/array"] ?? "");
expect(allTypedByMeta).toContain(builtinSchemas["@ocas/bool"] ?? "");
});
});
@@ -268,12 +268,12 @@ describe("bootstrap", () => {
const store = createMemoryStore();
const builtinSchemas = await bootstrap(store);
expect(builtinSchemas).toHaveProperty("@schema");
expect(builtinSchemas).toHaveProperty("@string");
expect(builtinSchemas).toHaveProperty("@number");
expect(builtinSchemas).toHaveProperty("@object");
expect(builtinSchemas).toHaveProperty("@array");
expect(builtinSchemas).toHaveProperty("@bool");
expect(builtinSchemas).toHaveProperty("@ocas/schema");
expect(builtinSchemas).toHaveProperty("@ocas/string");
expect(builtinSchemas).toHaveProperty("@ocas/number");
expect(builtinSchemas).toHaveProperty("@ocas/object");
expect(builtinSchemas).toHaveProperty("@ocas/array");
expect(builtinSchemas).toHaveProperty("@ocas/bool");
// All values should be valid hashes
for (const hash of Object.values(builtinSchemas)) {
@@ -287,7 +287,7 @@ describe("bootstrap", () => {
test("meta-schema node is stored and retrievable", async () => {
const store = createMemoryStore();
const builtinSchemas = await bootstrap(store);
const metaHash = builtinSchemas["@schema"] ?? "";
const metaHash = builtinSchemas["@ocas/schema"] ?? "";
expect(store.has(metaHash)).toBe(true);
const node = store.get(metaHash);
@@ -297,7 +297,7 @@ describe("bootstrap", () => {
test("meta-schema node is self-referencing: type === hash", async () => {
const store = createMemoryStore();
const builtinSchemas = await bootstrap(store);
const metaHash = builtinSchemas["@schema"] ?? "";
const metaHash = builtinSchemas["@ocas/schema"] ?? "";
const node = store.get(metaHash) as CasNode;
expect(node.type).toBe(metaHash);
@@ -306,7 +306,7 @@ describe("bootstrap", () => {
test("bootstrap node passes verify()", async () => {
const store = createMemoryStore();
const builtinSchemas = await bootstrap(store);
const metaHash = builtinSchemas["@schema"] ?? "";
const metaHash = builtinSchemas["@ocas/schema"] ?? "";
const node = store.get(metaHash) as CasNode;
expect(await verify(metaHash, node)).toBe(true);
@@ -319,6 +319,6 @@ describe("bootstrap", () => {
expect(h1).toEqual(h2);
// All 26 built-in schemas should be typed by the meta-schema
expect(store.listByType(h1["@schema"] ?? "")).toHaveLength(26);
expect(store.listByType(h1["@ocas/schema"] ?? "")).toHaveLength(26);
});
});
+21 -21
View File
@@ -78,7 +78,7 @@ describe("Suite 2: Custom {% render %} Tag Implementation", () => {
type: "object",
properties: {
name: { type: "string" },
child: { type: "string", format: "cas_ref" },
child: { type: "string", format: "ocas_ref" },
},
});
const parentHash = await store.put(parentSchema, {
@@ -124,7 +124,7 @@ describe("Suite 2: Custom {% render %} Tag Implementation", () => {
properties: {
level: { type: "number" },
child: {
anyOf: [{ type: "string", format: "cas_ref" }, { type: "null" }],
anyOf: [{ type: "string", format: "ocas_ref" }, { type: "null" }],
},
},
});
@@ -181,8 +181,8 @@ describe("Suite 2: Custom {% render %} Tag Implementation", () => {
const parentSchema = await putSchema(store, {
type: "object",
properties: {
left: { type: "string", format: "cas_ref" },
right: { type: "string", format: "cas_ref" },
left: { type: "string", format: "ocas_ref" },
right: { type: "string", format: "ocas_ref" },
},
});
const parentHash = await store.put(parentSchema, {
@@ -228,7 +228,7 @@ describe("Suite 2: Custom {% render %} Tag Implementation", () => {
properties: {
name: { type: "string" },
child: {
anyOf: [{ type: "string", format: "cas_ref" }, { type: "null" }],
anyOf: [{ type: "string", format: "ocas_ref" }, { type: "null" }],
},
},
});
@@ -268,7 +268,7 @@ describe("Suite 2: Custom {% render %} Tag Implementation", () => {
type: "object",
properties: {
name: { type: "string" },
child: { type: "string", format: "cas_ref" },
child: { type: "string", format: "ocas_ref" },
},
});
const nodeHash = await store.put(nodeSchema, {
@@ -311,7 +311,7 @@ describe("Suite 2: Custom {% render %} Tag Implementation", () => {
const parentSchema = await putSchema(store, {
type: "object",
properties: {
child: { type: "string", format: "cas_ref" },
child: { type: "string", format: "ocas_ref" },
},
});
const parentHash = await store.put(parentSchema, { child: childHash });
@@ -680,7 +680,7 @@ describe("Suite 5: Decay Priority Chain", () => {
const parentSchema = await putSchema(store, {
type: "object",
properties: {
child: { type: "string", format: "cas_ref" },
child: { type: "string", format: "ocas_ref" },
},
});
const parentHash = await store.put(parentSchema, { child: childHash });
@@ -727,7 +727,7 @@ describe("Suite 5: Decay Priority Chain", () => {
const parentSchema = await putSchema(store, {
type: "object",
properties: {
child: { type: "string", format: "cas_ref" },
child: { type: "string", format: "ocas_ref" },
},
});
const parentHash = await store.put(parentSchema, { child: childHash });
@@ -774,7 +774,7 @@ describe("Suite 5: Decay Priority Chain", () => {
const parentSchema = await putSchema(store, {
type: "object",
properties: {
child: { type: "string", format: "cas_ref" },
child: { type: "string", format: "ocas_ref" },
},
});
const parentHash = await store.put(parentSchema, { child: childHash });
@@ -818,7 +818,7 @@ describe("Suite 6: Recursive Rendering Edge Cases", () => {
properties: {
level: { type: "number" },
next: {
anyOf: [{ type: "string", format: "cas_ref" }, { type: "null" }],
anyOf: [{ type: "string", format: "ocas_ref" }, { type: "null" }],
},
},
});
@@ -865,7 +865,7 @@ describe("Suite 6: Recursive Rendering Edge Cases", () => {
properties: {
name: { type: "string" },
ref: {
anyOf: [{ type: "string", format: "cas_ref" }, { type: "null" }],
anyOf: [{ type: "string", format: "ocas_ref" }, { type: "null" }],
},
},
});
@@ -893,7 +893,7 @@ describe("Suite 6: Recursive Rendering Edge Cases", () => {
}
});
test("6.3 Array of cas_ref with Template", async () => {
test("6.3 Array of ocas_ref with Template", async () => {
const { store, varStore, cleanup } = await createTempVarStore();
try {
@@ -912,7 +912,7 @@ describe("Suite 6: Recursive Rendering Edge Cases", () => {
properties: {
items: {
type: "array",
items: { type: "string", format: "cas_ref" },
items: { type: "string", format: "ocas_ref" },
},
},
});
@@ -998,7 +998,7 @@ describe("Suite 7: Error Handling & Edge Cases", () => {
const parentSchema = await putSchema(store, {
type: "object",
properties: {
child: { type: "string", format: "cas_ref" },
child: { type: "string", format: "ocas_ref" },
},
});
const parentHash = await store.put(parentSchema, { child: childHash });
@@ -1038,7 +1038,7 @@ describe("Suite 7: Error Handling & Edge Cases", () => {
const parentSchema = await putSchema(store, {
type: "object",
properties: {
child: { type: "string", format: "cas_ref" },
child: { type: "string", format: "ocas_ref" },
},
});
const parentHash = await store.put(parentSchema, { child: childHash });
@@ -1078,7 +1078,7 @@ describe("Suite 7: Error Handling & Edge Cases", () => {
const parentSchema = await putSchema(store, {
type: "object",
properties: {
child: { type: "string", format: "cas_ref" },
child: { type: "string", format: "ocas_ref" },
},
});
const parentHash = await store.put(parentSchema, { child: childHash });
@@ -1118,7 +1118,7 @@ describe("Suite 7: Error Handling & Edge Cases", () => {
const parentSchema = await putSchema(store, {
type: "object",
properties: {
child: { type: "string", format: "cas_ref" },
child: { type: "string", format: "ocas_ref" },
},
});
const parentHash = await store.put(parentSchema, { child: childHash });
@@ -1206,7 +1206,7 @@ describe("Suite 8: Performance & Scalability", () => {
properties: {
items: {
type: "array",
items: { type: "string", format: "cas_ref" },
items: { type: "string", format: "ocas_ref" },
},
},
});
@@ -1748,7 +1748,7 @@ describe("Suite 10: Context Variable Completeness", () => {
type: "object",
properties: {
name: { type: "string" },
child: { type: "string", format: "cas_ref" },
child: { type: "string", format: "ocas_ref" },
},
});
const parentHash = await store.put(parentSchema, {
@@ -1808,7 +1808,7 @@ describe("Suite 10: Context Variable Completeness", () => {
type: "object",
properties: {
custom: { type: "string" },
child: { type: "string", format: "cas_ref" },
child: { type: "string", format: "ocas_ref" },
},
});
const parentHash = await store.put(parentSchema, {
+25 -25
View File
@@ -10,24 +10,24 @@ import type { VariableStore } from "./variable-store.js";
import { createVariableStore } from "./variable-store.js";
const OUTPUT_ALIASES = [
"@output/put",
"@output/get",
"@output/has",
"@output/hash",
"@output/verify",
"@output/refs",
"@output/walk",
"@output/list",
"@output/var-set",
"@output/var-get",
"@output/var-delete",
"@output/var-tag",
"@output/var-list",
"@output/template-set",
"@output/template-get",
"@output/template-list",
"@output/template-delete",
"@output/gc",
"@ocas/output/put",
"@ocas/output/get",
"@ocas/output/has",
"@ocas/output/hash",
"@ocas/output/verify",
"@ocas/output/refs",
"@ocas/output/walk",
"@ocas/output/list",
"@ocas/output/var-set",
"@ocas/output/var-get",
"@ocas/output/var-delete",
"@ocas/output/var-tag",
"@ocas/output/var-list",
"@ocas/output/template-set",
"@ocas/output/template-get",
"@ocas/output/template-list",
"@ocas/output/template-delete",
"@ocas/output/gc",
] as const;
describe("registerOutputTemplates", () => {
@@ -40,7 +40,7 @@ describe("registerOutputTemplates", () => {
await rm(tempDir, { recursive: true });
});
test("registers a template for every @output/* schema", async () => {
test("registers a template for every @ocas/output/* schema", async () => {
tempDir = await mkdtemp(join(tmpdir(), "ocas-tmpl-"));
store = createMemoryStore();
await bootstrap(store);
@@ -63,7 +63,7 @@ describe("registerOutputTemplates", () => {
await registerOutputTemplates(store, varStore);
const stringHash = aliases["@string"];
const stringHash = aliases["@ocas/string"];
if (!stringHash) throw new Error("@string not found");
for (const alias of OUTPUT_ALIASES) {
@@ -93,7 +93,7 @@ describe("registerOutputTemplates", () => {
expect(first).toEqual(second);
});
test("@output/put template contains payload reference", async () => {
test("@ocas/output/put template contains payload reference", async () => {
tempDir = await mkdtemp(join(tmpdir(), "ocas-tmpl-"));
store = createMemoryStore();
const aliases = await bootstrap(store);
@@ -101,14 +101,14 @@ describe("registerOutputTemplates", () => {
await registerOutputTemplates(store, varStore);
const putHash = aliases["@output/put"];
if (!putHash) throw new Error("@output/put not found");
const stringHash = aliases["@string"];
const putHash = aliases["@ocas/output/put"];
if (!putHash) throw new Error("@ocas/output/put not found");
const stringHash = aliases["@ocas/string"];
if (!stringHash) throw new Error("@string not found");
const variable = varStore.get(`@ocas/template/text/${putHash}`, stringHash);
if (variable === null)
throw new Error("@output/put template variable not found");
throw new Error("@ocas/output/put template variable not found");
const templateNode = store.get(variable.value);
if (templateNode === null) throw new Error("Template node not found");
+20 -20
View File
@@ -5,55 +5,55 @@ import type { VariableStore } from "./variable-store.js";
const DEFAULT_TEMPLATES: ReadonlyArray<
readonly [alias: string, template: string]
> = [
["@output/put", "{{ payload }}"],
["@ocas/output/put", "{{ payload }}"],
[
"@output/get",
"@ocas/output/get",
"type: {{ payload.type }}\ntimestamp: {{ payload.timestamp }}",
],
["@output/has", "{{ payload }}"],
["@output/hash", "{{ payload }}"],
["@output/verify", "{{ payload }}"],
["@output/refs", "{% for ref in payload %}{{ ref }}\n{% endfor %}"],
["@output/walk", "{% for item in payload %}{{ item }}\n{% endfor %}"],
["@output/list", "{% for item in payload %}{{ item }}\n{% endfor %}"],
["@ocas/output/has", "{{ payload }}"],
["@ocas/output/hash", "{{ payload }}"],
["@ocas/output/verify", "{{ payload }}"],
["@ocas/output/refs", "{% for ref in payload %}{{ ref }}\n{% endfor %}"],
["@ocas/output/walk", "{% for item in payload %}{{ item }}\n{% endfor %}"],
["@ocas/output/list", "{% for item in payload %}{{ item }}\n{% endfor %}"],
[
"@output/var-set",
"@ocas/output/var-set",
"name: {{ payload.name }}\nschema: {{ payload.schema }}\nvalue: {{ payload.value }}",
],
[
"@output/var-get",
"@ocas/output/var-get",
"name: {{ payload.name }}\nschema: {{ payload.schema }}\nvalue: {{ payload.value }}",
],
[
"@output/var-delete",
"@ocas/output/var-delete",
"name: {{ payload.name }}\nschema: {{ payload.schema }}\nvalue: {{ payload.value }}",
],
[
"@output/var-tag",
"@ocas/output/var-tag",
"name: {{ payload.name }}\nschema: {{ payload.schema }}\nvalue: {{ payload.value }}",
],
[
"@output/var-list",
"@ocas/output/var-list",
"{% for v in payload %}name: {{ v.name }}\nschema: {{ v.schema }}\nvalue: {{ v.value }}\n{% endfor %}",
],
[
"@output/template-set",
"@ocas/output/template-set",
"schemaHash: {{ payload.schemaHash }}\ncontentHash: {{ payload.contentHash }}",
],
["@output/template-get", "{{ payload }}"],
["@ocas/output/template-get", "{{ payload }}"],
[
"@output/template-list",
"@ocas/output/template-list",
"{% for t in payload %}schemaHash: {{ t.schemaHash }}\ncontentHash: {{ t.contentHash }}\n{% endfor %}",
],
["@output/template-delete", "deleted: {{ payload.deleted }}"],
["@ocas/output/template-delete", "deleted: {{ payload.deleted }}"],
[
"@output/gc",
"@ocas/output/gc",
"total: {{ payload.total }}\nreachable: {{ payload.reachable }}\ncollected: {{ payload.collected }}\nscanned: {{ payload.scanned }}",
],
];
/**
* Register default LiquidJS templates for all @output/* schemas.
* Register default LiquidJS templates for all @ocas/output/* schemas.
* Each template is stored as a @string CAS node and bound to
* the variable `@ocas/template/text/<schema-hash>`.
*
@@ -64,7 +64,7 @@ export async function registerOutputTemplates(
varStore: VariableStore,
): Promise<Record<string, Hash>> {
const aliases = await bootstrap(store);
const stringHash = aliases["@string"];
const stringHash = aliases["@ocas/string"];
if (stringHash === undefined) {
throw new Error("@string schema not found in bootstrap result");
}
+37 -37
View File
@@ -94,7 +94,7 @@ describe("Suite 2: Resolution Decay Model", () => {
type: "object",
properties: {
title: { type: "string" },
child: { type: "string", format: "cas_ref" },
child: { type: "string", format: "ocas_ref" },
},
});
const parentHash = await store.put(parentSchema, {
@@ -123,7 +123,7 @@ describe("Suite 2: Resolution Decay Model", () => {
properties: {
value: { type: "number" },
next: {
anyOf: [{ type: "string", format: "cas_ref" }, { type: "null" }],
anyOf: [{ type: "string", format: "ocas_ref" }, { type: "null" }],
},
},
});
@@ -159,7 +159,7 @@ describe("Suite 2: Resolution Decay Model", () => {
properties: {
level: { type: "number" },
child: {
anyOf: [{ type: "string", format: "cas_ref" }, { type: "null" }],
anyOf: [{ type: "string", format: "ocas_ref" }, { type: "null" }],
},
},
});
@@ -197,7 +197,7 @@ describe("Suite 2: Resolution Decay Model", () => {
properties: {
level: { type: "number" },
next: {
anyOf: [{ type: "string", format: "cas_ref" }, { type: "null" }],
anyOf: [{ type: "string", format: "ocas_ref" }, { type: "null" }],
},
},
});
@@ -232,7 +232,7 @@ describe("Suite 2: Resolution Decay Model", () => {
properties: {
level: { type: "number" },
next: {
anyOf: [{ type: "string", format: "cas_ref" }, { type: "null" }],
anyOf: [{ type: "string", format: "ocas_ref" }, { type: "null" }],
},
},
});
@@ -281,7 +281,7 @@ describe("Suite 3: Complex Graph Structures", () => {
properties: {
items: {
type: "array",
items: { type: "string", format: "cas_ref" },
items: { type: "string", format: "ocas_ref" },
},
},
});
@@ -300,7 +300,7 @@ describe("Suite 3: Complex Graph Structures", () => {
expect(output).toContain("item3");
});
test("3.2 Object with Multiple cas_ref Fields", async () => {
test("3.2 Object with Multiple ocas_ref Fields", async () => {
const store = createMemoryStore();
await bootstrap(store);
@@ -317,8 +317,8 @@ describe("Suite 3: Complex Graph Structures", () => {
const parentSchema = await putSchema(store, {
type: "object",
properties: {
left: { type: "string", format: "cas_ref" },
right: { type: "string", format: "cas_ref" },
left: { type: "string", format: "ocas_ref" },
right: { type: "string", format: "ocas_ref" },
data: { type: "string" },
},
});
@@ -348,7 +348,7 @@ describe("Suite 3: Complex Graph Structures", () => {
properties: {
name: { type: "string" },
ref: {
anyOf: [{ type: "string", format: "cas_ref" }, { type: "null" }],
anyOf: [{ type: "string", format: "ocas_ref" }, { type: "null" }],
},
},
});
@@ -387,7 +387,7 @@ describe("Suite 3: Complex Graph Structures", () => {
type: "object",
properties: {
name: { type: "string" },
child: { type: "string", format: "cas_ref" },
child: { type: "string", format: "ocas_ref" },
},
});
const branchA = await store.put(branchSchema, {
@@ -402,8 +402,8 @@ describe("Suite 3: Complex Graph Structures", () => {
const rootSchema = await putSchema(store, {
type: "object",
properties: {
left: { type: "string", format: "cas_ref" },
right: { type: "string", format: "cas_ref" },
left: { type: "string", format: "ocas_ref" },
right: { type: "string", format: "ocas_ref" },
},
});
const rootHash = await store.put(rootSchema, {
@@ -431,10 +431,10 @@ describe("Suite 3: Complex Graph Structures", () => {
properties: {
value: { type: "number" },
left: {
anyOf: [{ type: "string", format: "cas_ref" }, { type: "null" }],
anyOf: [{ type: "string", format: "ocas_ref" }, { type: "null" }],
},
right: {
anyOf: [{ type: "string", format: "cas_ref" }, { type: "null" }],
anyOf: [{ type: "string", format: "ocas_ref" }, { type: "null" }],
},
},
});
@@ -502,7 +502,7 @@ describe("Suite 4: Epsilon Boundary Cases", () => {
properties: {
level: { type: "number" },
next: {
anyOf: [{ type: "string", format: "cas_ref" }, { type: "null" }],
anyOf: [{ type: "string", format: "ocas_ref" }, { type: "null" }],
},
},
});
@@ -537,7 +537,7 @@ describe("Suite 4: Epsilon Boundary Cases", () => {
properties: {
level: { type: "number" },
next: {
anyOf: [{ type: "string", format: "cas_ref" }, { type: "null" }],
anyOf: [{ type: "string", format: "ocas_ref" }, { type: "null" }],
},
},
});
@@ -639,7 +639,7 @@ describe("Suite 5: YAML Output Format", () => {
const parentSchema = await putSchema(store, {
type: "object",
properties: {
child: { type: "string", format: "cas_ref" },
child: { type: "string", format: "ocas_ref" },
},
});
const parentHash = await store.put(parentSchema, { child: childHash });
@@ -673,7 +673,7 @@ describe("Suite 5: YAML Output Format", () => {
type: "object",
properties: {
ref: {
anyOf: [{ type: "string", format: "cas_ref" }, { type: "null" }],
anyOf: [{ type: "string", format: "ocas_ref" }, { type: "null" }],
},
},
});
@@ -686,7 +686,7 @@ describe("Suite 5: YAML Output Format", () => {
});
describe("Suite 6: Schema Integration", () => {
test("6.1 Detect cas_ref Fields via Schema", async () => {
test("6.1 Detect ocas_ref Fields via Schema", async () => {
const store = createMemoryStore();
await bootstrap(store);
@@ -701,7 +701,7 @@ describe("Suite 6: Schema Integration", () => {
const parentSchema = await putSchema(store, {
type: "object",
properties: {
link: { type: "string", format: "cas_ref" },
link: { type: "string", format: "ocas_ref" },
},
});
const parentHash = await store.put(parentSchema, { link: childHash });
@@ -715,7 +715,7 @@ describe("Suite 6: Schema Integration", () => {
expect(output).toContain("child");
});
test("6.2 Non-cas_ref String Not Expanded", async () => {
test("6.2 Non-ocas_ref String Not Expanded", async () => {
const store = createMemoryStore();
await bootstrap(store);
const objSchema = await putSchema(store, {
@@ -733,7 +733,7 @@ describe("Suite 6: Schema Integration", () => {
expect(output).not.toMatch(/cas:[0-9A-HJKMNP-TV-Z]{13}/);
});
test("6.3 Array of cas_ref", async () => {
test("6.3 Array of ocas_ref", async () => {
const store = createMemoryStore();
await bootstrap(store);
@@ -748,7 +748,7 @@ describe("Suite 6: Schema Integration", () => {
const arraySchema = await putSchema(store, {
type: "array",
items: { type: "string", format: "cas_ref" },
items: { type: "string", format: "ocas_ref" },
});
const arrayHash = await store.put(arraySchema, [item1, item2]);
@@ -762,7 +762,7 @@ describe("Suite 6: Schema Integration", () => {
expect(output).toContain("item2");
});
test("6.4 anyOf with cas_ref (Nullable Reference)", async () => {
test("6.4 anyOf with ocas_ref (Nullable Reference)", async () => {
const store = createMemoryStore();
await bootstrap(store);
@@ -778,7 +778,7 @@ describe("Suite 6: Schema Integration", () => {
type: "object",
properties: {
ref: {
anyOf: [{ type: "string", format: "cas_ref" }, { type: "null" }],
anyOf: [{ type: "string", format: "ocas_ref" }, { type: "null" }],
},
},
});
@@ -796,7 +796,7 @@ describe("Suite 6: Schema Integration", () => {
test("6.5 Schema-less Node (Bootstrap Node)", async () => {
const store = createMemoryStore();
const types = await bootstrap(store);
const schemaHash = types["@schema"];
const schemaHash = types["@ocas/schema"];
const output = render(store, schemaHash);
@@ -813,7 +813,7 @@ describe("Suite 7: Error Handling", () => {
const parentSchema = await putSchema(store, {
type: "object",
properties: {
child: { type: "string", format: "cas_ref" },
child: { type: "string", format: "ocas_ref" },
},
});
const fakeChildHash = "ZZZZZZZZZZZZZ" as Hash;
@@ -895,7 +895,7 @@ describe("Suite 8: Performance & Edge Cases", () => {
const parentSchema = await putSchema(store, {
type: "array",
items: { type: "string", format: "cas_ref" },
items: { type: "string", format: "ocas_ref" },
});
const parentHash = await store.put(parentSchema, children);
@@ -984,7 +984,7 @@ describe("Suite 9: renderDirect (in-memory rendering)", () => {
expect(output).toContain("active: true");
});
test("9.5 Render with store expands cas_ref fields", async () => {
test("9.5 Render with store expands ocas_ref fields", async () => {
const store = createMemoryStore();
await bootstrap(store);
@@ -995,15 +995,15 @@ describe("Suite 9: renderDirect (in-memory rendering)", () => {
});
const childHash = await store.put(childSchema, { msg: "inner" });
// Parent schema with cas_ref
// Parent schema with ocas_ref
const parentSchema = await putSchema(store, {
type: "object",
properties: {
child: { type: "string", format: "cas_ref" },
child: { type: "string", format: "ocas_ref" },
},
});
// Render directly with store — cas_ref should expand
// Render directly with store — ocas_ref should expand
const output = renderDirect(
parentSchema,
{ child: childHash },
@@ -1041,8 +1041,8 @@ describe("Suite 9: renderDirect (in-memory rendering)", () => {
expect(output.trim()).toBe("null");
});
test("9.9 cas_ref without store renders as cas: reference", () => {
// Without store, can't identify cas_ref fields — hash strings stay as strings
test("9.9 ocas_ref without store renders as cas: reference", () => {
// Without store, can't identify ocas_ref fields — hash strings stay as strings
const fakeTypeHash = "0000000000000" as Hash;
const someHash = "ABCDEFGH12345" as Hash;
const output = renderDirect(fakeTypeHash, { ref: someHash }, null, null);
@@ -1099,7 +1099,7 @@ describe("Suite 10: Missing Root Hash Error Handling (Issue #53)", () => {
type: "object",
properties: {
title: { type: "string" },
child: { type: "string", format: "cas_ref" },
child: { type: "string", format: "ocas_ref" },
},
});
@@ -1124,7 +1124,7 @@ describe("Suite 10: Missing Root Hash Error Handling (Issue #53)", () => {
properties: {
level: { type: "number" },
next: {
anyOf: [{ type: "string", format: "cas_ref" }, { type: "null" }],
anyOf: [{ type: "string", format: "ocas_ref" }, { type: "null" }],
},
},
});
+4 -4
View File
@@ -112,7 +112,7 @@ export async function renderAsync(
/**
* Render a value directly (in-memory) without requiring it to be stored.
* Accepts a raw { type, value } pair. Store is optional and read-only
* used only for schema lookup and expanding nested cas_ref references.
* used only for schema lookup and expanding nested ocas_ref references.
* No data is written to the store.
*/
export function renderDirect(
@@ -123,7 +123,7 @@ export function renderDirect(
): string {
const { resolution, decay, epsilon } = validateAndExtractOptions(options);
// Try to get schema from store to identify cas_ref fields
// Try to get schema from store to identify ocas_ref fields
let refSet = new Set<Hash>();
if (store !== null) {
const schema = getSchema(store, typeHash);
@@ -197,7 +197,7 @@ function renderNode(
// Calculate child resolution for next level
const childResolution = currentResolution * decay;
// Render the payload with recursive expansion of cas_ref fields
// Render the payload with recursive expansion of ocas_ref fields
const rendered = renderValue(
store,
node.payload,
@@ -229,7 +229,7 @@ function renderValue(
// Handle primitives
if (typeof value === "string") {
// Check if this string is a cas_ref
// Check if this string is a ocas_ref
if (refHashes.has(value as Hash)) {
// Recursively render the referenced node
return renderNode(
+25 -25
View File
@@ -30,7 +30,7 @@ describe("putSchema", () => {
test("schema node type equals the meta-schema hash", async () => {
const store = createMemoryStore();
const builtinSchemas = await bootstrap(store);
const metaHash = builtinSchemas["@schema"] ?? "";
const metaHash = builtinSchemas["@ocas/schema"] ?? "";
const schemaHash = await putSchema(store, { type: "string" });
const node = store.get(schemaHash) as CasNode;
@@ -78,7 +78,7 @@ describe("getSchema", () => {
type: "object",
required: ["id"],
properties: {
id: { type: "string", format: "cas_ref" },
id: { type: "string", format: "ocas_ref" },
label: { type: "string" },
},
};
@@ -143,10 +143,10 @@ describe("validate", () => {
});
// ──────────────────────────────────────────────────────────────────────────────
// Step 4: refs() — extract cas_ref hashes from a node's payload
// Step 4: refs() — extract ocas_ref hashes from a node's payload
// ──────────────────────────────────────────────────────────────────────────────
describe("refs", () => {
test("returns empty array when schema has no cas_ref fields", async () => {
test("returns empty array when schema has no ocas_ref fields", async () => {
const store = createMemoryStore();
const schemaHash = await putSchema(store, {
type: "object",
@@ -158,12 +158,12 @@ describe("refs", () => {
expect(refs(store, node)).toEqual([]);
});
test("returns the cas_ref hash values from payload", async () => {
test("returns the ocas_ref hash values from payload", async () => {
const store = createMemoryStore();
const schemaHash = await putSchema(store, {
type: "object",
properties: {
parentHash: { type: "string", format: "cas_ref" },
parentHash: { type: "string", format: "ocas_ref" },
label: { type: "string" },
},
});
@@ -178,13 +178,13 @@ describe("refs", () => {
expect(refs(store, node)).toEqual([targetHash]);
});
test("collects multiple cas_ref fields", async () => {
test("collects multiple ocas_ref fields", async () => {
const store = createMemoryStore();
const schemaHash = await putSchema(store, {
type: "object",
properties: {
leftHash: { type: "string", format: "cas_ref" },
rightHash: { type: "string", format: "cas_ref" },
leftHash: { type: "string", format: "ocas_ref" },
rightHash: { type: "string", format: "ocas_ref" },
},
});
@@ -202,12 +202,12 @@ describe("refs", () => {
expect(result).toContain(h2);
});
test("skips null/undefined cas_ref values", async () => {
test("skips null/undefined ocas_ref values", async () => {
const store = createMemoryStore();
const schemaHash = await putSchema(store, {
type: "object",
properties: {
optionalRef: { type: "string", format: "cas_ref" },
optionalRef: { type: "string", format: "ocas_ref" },
label: { type: "string" },
},
});
@@ -231,7 +231,7 @@ describe("refs", () => {
});
// ──────────────────────────────────────────────────────────────────────────────
// Step 5: walk() — BFS traversal via cas_ref links
// Step 5: walk() — BFS traversal via ocas_ref links
// ──────────────────────────────────────────────────────────────────────────────
describe("walk", () => {
test("visits a single node with no refs", async () => {
@@ -253,7 +253,7 @@ describe("walk", () => {
const schemaHash = await putSchema(store, {
type: "object",
properties: {
nextHash: { type: "string", format: "cas_ref" },
nextHash: { type: "string", format: "ocas_ref" },
val: { type: "number" },
},
});
@@ -277,7 +277,7 @@ describe("walk", () => {
const schemaHash = await putSchema(store, {
type: "object",
properties: {
peerHash: { type: "string", format: "cas_ref" },
peerHash: { type: "string", format: "ocas_ref" },
val: { type: "number" },
},
});
@@ -295,8 +295,8 @@ describe("walk", () => {
const schemaHash2 = await putSchema(store, {
type: "object",
properties: {
leftHash: { type: "string", format: "cas_ref" },
rightHash: { type: "string", format: "cas_ref" },
leftHash: { type: "string", format: "ocas_ref" },
rightHash: { type: "string", format: "ocas_ref" },
},
});
const rootHash = await store.put(schemaHash2, {
@@ -319,7 +319,7 @@ describe("walk", () => {
const store = createMemoryStore();
const schemaHash = await putSchema(store, {
type: "object",
properties: { ref: { type: "string", format: "cas_ref" } },
properties: { ref: { type: "string", format: "ocas_ref" } },
});
const nodeHash = await store.put(schemaHash, { ref: "0000000000000" });
@@ -357,7 +357,7 @@ describe("bootstrap meta-schema self-reference", () => {
test("metaNode.type === metaHash (self-referencing)", async () => {
const store = createMemoryStore();
const builtinSchemas = await bootstrap(store);
const metaHash = builtinSchemas["@schema"] ?? "";
const metaHash = builtinSchemas["@ocas/schema"] ?? "";
const metaNode = store.get(metaHash) as CasNode;
expect(metaNode.type).toBe(metaHash);
@@ -366,7 +366,7 @@ describe("bootstrap meta-schema self-reference", () => {
test("schema nodes have type === metaHash", async () => {
const store = createMemoryStore();
const builtinSchemas = await bootstrap(store);
const metaHash = builtinSchemas["@schema"] ?? "";
const metaHash = builtinSchemas["@ocas/schema"] ?? "";
const schemaHash = await putSchema(store, { type: "string" });
const schemaNode = store.get(schemaHash) as CasNode;
@@ -376,7 +376,7 @@ describe("bootstrap meta-schema self-reference", () => {
test("data nodes have type === schemaHash (not metaHash)", async () => {
const store = createMemoryStore();
const builtinSchemas = await bootstrap(store);
const metaHash = builtinSchemas["@schema"] ?? "";
const metaHash = builtinSchemas["@ocas/schema"] ?? "";
const schemaHash = await putSchema(store, {
type: "object",
properties: { val: { type: "number" } },
@@ -492,7 +492,7 @@ describe("bootstrap meta-schema self-reference", () => {
test("bootstrap is idempotent across putSchema calls", async () => {
const store = createMemoryStore();
const builtinSchemas = await bootstrap(store);
const metaHash = builtinSchemas["@schema"] ?? "";
const metaHash = builtinSchemas["@ocas/schema"] ?? "";
await putSchema(store, { type: "string" });
await putSchema(store, { type: "number" });
@@ -620,7 +620,7 @@ describe("bootstrap meta-schema self-reference", () => {
allOf: [
{
type: "object",
properties: { ref: { type: "string", format: "cas_ref" } },
properties: { ref: { type: "string", format: "ocas_ref" } },
},
],
});
@@ -638,7 +638,7 @@ describe("bootstrap meta-schema self-reference", () => {
const schema = await putSchema(store, {
type: "object",
patternProperties: {
"^ref_": { type: "string", format: "cas_ref" },
"^ref_": { type: "string", format: "ocas_ref" },
},
});
@@ -654,7 +654,7 @@ describe("bootstrap meta-schema self-reference", () => {
const innerSchema = await putSchema(store, { type: "string" });
const schema = await putSchema(store, {
type: "array",
prefixItems: [{ type: "string", format: "cas_ref" }, { type: "number" }],
prefixItems: [{ type: "string", format: "ocas_ref" }, { type: "number" }],
});
const targetHash = await store.put(innerSchema, "hello");
@@ -741,7 +741,7 @@ describe("bootstrap meta-schema self-reference", () => {
const innerSchema = await putSchema(store, { type: "string" });
const schema = await putSchema(store, {
type: "array",
contains: { type: "string", format: "cas_ref" },
contains: { type: "string", format: "ocas_ref" },
});
const targetHash = await store.put(innerSchema, "hello");
+5 -5
View File
@@ -20,7 +20,7 @@ export class SchemaValidationError extends Error {
}
const ajv = new Ajv();
ajv.addFormat("cas_ref", /^[0-9A-HJKMNP-TV-Z]{13}$/);
ajv.addFormat("ocas_ref", /^[0-9A-HJKMNP-TV-Z]{13}$/);
const ALLOWED_SCHEMA_KEYS = new Set([
"type",
@@ -253,7 +253,7 @@ export async function putSchema(
jsonSchema: JSONSchema,
): Promise<Hash> {
const builtinSchemas = await bootstrap(store);
const metaHash = builtinSchemas["@schema"];
const metaHash = builtinSchemas["@ocas/schema"];
if (!metaHash) {
throw new Error("Meta-schema not found in bootstrap result");
}
@@ -292,7 +292,7 @@ export function validate(store: Store, node: CasNode): boolean {
}
/**
* Recursively collect values of all properties whose schema has format: 'cas_ref'.
* Recursively collect values of all properties whose schema has format: 'ocas_ref'.
* Handles: direct format, anyOf/allOf (combinators), oneOf, if/then/else (conditionals),
* not, contains, items + prefixItems (arrays), properties (nested objects),
* additionalProperties (record refs), and patternProperties (regex-keyed refs).
@@ -300,7 +300,7 @@ export function validate(store: Store, node: CasNode): boolean {
export function collectRefs(schema: JSONSchema, value: unknown): Hash[] {
const result: Hash[] = [];
if (schema.format === "cas_ref") {
if (schema.format === "ocas_ref") {
if (typeof value === "string") {
result.push(value as Hash);
}
@@ -413,7 +413,7 @@ export function collectRefs(schema: JSONSchema, value: unknown): Hash[] {
}
/**
* Return all hashes referenced by this node via cas_ref fields in its schema.
* Return all hashes referenced by this node via ocas_ref fields in its schema.
* Null/undefined values are skipped.
*/
export function refs(store: Store, node: CasNode): Hash[] {
+28 -16
View File
@@ -4,34 +4,38 @@ import { createMemoryStore } from "./store.js";
import { wrapEnvelope } from "./wrap-envelope.js";
describe("wrapEnvelope", () => {
test("resolves @output/put alias and returns envelope", async () => {
test("resolves @ocas/output/put alias and returns envelope", async () => {
const store = createMemoryStore();
const aliases = await bootstrap(store);
const envelope = await wrapEnvelope(store, "@output/put", "AAAAAAAAAAAAA");
const envelope = await wrapEnvelope(
store,
"@ocas/output/put",
"AAAAAAAAAAAAA",
);
expect(envelope.type).toBe(aliases["@output/put"]);
expect(envelope.type).toBe(aliases["@ocas/output/put"]);
expect(envelope.value).toBe("AAAAAAAAAAAAA");
});
test("resolves @output/has alias with boolean value", async () => {
test("resolves @ocas/output/has alias with boolean value", async () => {
const store = createMemoryStore();
const aliases = await bootstrap(store);
const envelope = await wrapEnvelope(store, "@output/has", true);
const envelope = await wrapEnvelope(store, "@ocas/output/has", true);
expect(envelope.type).toBe(aliases["@output/has"]);
expect(envelope.type).toBe(aliases["@ocas/output/has"]);
expect(envelope.value).toBe(true);
});
test("resolves @output/gc alias with object value", async () => {
test("resolves @ocas/output/gc alias with object value", async () => {
const store = createMemoryStore();
const aliases = await bootstrap(store);
const gcStats = { total: 100, reachable: 80, collected: 20, scanned: 5 };
const envelope = await wrapEnvelope(store, "@output/gc", gcStats);
const envelope = await wrapEnvelope(store, "@ocas/output/gc", gcStats);
expect(envelope.type).toBe(aliases["@output/gc"]);
expect(envelope.type).toBe(aliases["@ocas/output/gc"]);
expect(envelope.value).toEqual(gcStats);
});
@@ -39,9 +43,9 @@ describe("wrapEnvelope", () => {
const store = createMemoryStore();
const aliases = await bootstrap(store);
const envelope = await wrapEnvelope(store, "@string", "hello");
const envelope = await wrapEnvelope(store, "@ocas/string", "hello");
expect(envelope.type).toBe(aliases["@string"]);
expect(envelope.type).toBe(aliases["@ocas/string"]);
expect(envelope.value).toBe("hello");
});
@@ -50,15 +54,19 @@ describe("wrapEnvelope", () => {
await bootstrap(store);
await expect(
wrapEnvelope(store, "@output/nonexistent", "value"),
).rejects.toThrow("Unknown schema alias: @output/nonexistent");
wrapEnvelope(store, "@ocas/output/nonexistent", "value"),
).rejects.toThrow("Unknown schema alias: @ocas/output/nonexistent");
});
test("is idempotent — same alias returns same type hash", async () => {
const store = createMemoryStore();
const first = await wrapEnvelope(store, "@output/verify", "ok");
const second = await wrapEnvelope(store, "@output/verify", "corrupted");
const first = await wrapEnvelope(store, "@ocas/output/verify", "ok");
const second = await wrapEnvelope(
store,
"@ocas/output/verify",
"corrupted",
);
expect(first.type).toBe(second.type);
expect(first.value).toBe("ok");
@@ -78,7 +86,11 @@ describe("wrapEnvelope", () => {
tags: { env: "prod" },
labels: ["stable"],
};
const envelope = await wrapEnvelope(store, "@output/var-set", original);
const envelope = await wrapEnvelope(
store,
"@ocas/output/var-set",
original,
);
expect(envelope.value).toEqual(original);
});
+1 -1
View File
@@ -2,7 +2,7 @@ import { bootstrap } from "./bootstrap.js";
import type { Hash, Store } from "./types.js";
/**
* Resolve a schema alias (e.g. "@output/put") to its hash via bootstrap,
* Resolve a schema alias (e.g. "@ocas/output/put") to its hash via bootstrap,
* then return a typed envelope ready for store.put() or direct rendering.
*/
export async function wrapEnvelope(
+13 -13
View File
@@ -16,7 +16,7 @@ describe("Test Suite 1: Meta-Schema Structure and Self-Validation", () => {
test("1.1: Meta-schema is a valid JSON Schema", async () => {
const store = new MemStore();
const builtinSchemas = await bootstrap(store);
const metaHash = builtinSchemas["@schema"] ?? "";
const metaHash = builtinSchemas["@ocas/schema"] ?? "";
const metaNode = store.get(metaHash);
expect(metaNode).not.toBeNull();
@@ -27,7 +27,7 @@ describe("Test Suite 1: Meta-Schema Structure and Self-Validation", () => {
test("1.2: Meta-schema self-validates", async () => {
const store = new MemStore();
const builtinSchemas = await bootstrap(store);
const metaHash = builtinSchemas["@schema"] ?? "";
const metaHash = builtinSchemas["@ocas/schema"] ?? "";
const metaNode = store.get(metaHash);
expect(metaNode).not.toBeNull();
@@ -37,7 +37,7 @@ describe("Test Suite 1: Meta-Schema Structure and Self-Validation", () => {
test("1.3: Meta-schema defines all supported keywords", async () => {
const store = new MemStore();
const builtinSchemas = await bootstrap(store);
const metaHash = builtinSchemas["@schema"] ?? "";
const metaHash = builtinSchemas["@ocas/schema"] ?? "";
const metaSchema = getSchema(store, metaHash);
expect(metaSchema).not.toBeNull();
@@ -61,7 +61,7 @@ describe("Test Suite 1: Meta-Schema Structure and Self-Validation", () => {
test("1.4: Meta-schema does not include unsupported keywords", async () => {
const store = new MemStore();
const builtinSchemas = await bootstrap(store);
const metaHash = builtinSchemas["@schema"] ?? "";
const metaHash = builtinSchemas["@ocas/schema"] ?? "";
const metaSchema = getSchema(store, metaHash);
expect(metaSchema).not.toBeNull();
@@ -99,7 +99,7 @@ describe("Test Suite 1: Meta-Schema Structure and Self-Validation", () => {
test("1.5: Meta-schema node type equals its own hash", async () => {
const store = new MemStore();
const builtinSchemas = await bootstrap(store);
const metaHash = builtinSchemas["@schema"] ?? "";
const metaHash = builtinSchemas["@ocas/schema"] ?? "";
const metaNode = store.get(metaHash);
expect(metaNode).not.toBeNull();
@@ -199,7 +199,7 @@ describe("Test Suite 2: putSchema Validation - Valid Schemas", () => {
await bootstrap(store);
const hash = await putSchema(store, {
type: "string",
format: "cas_ref",
format: "ocas_ref",
});
expect(hash).toBeTruthy();
});
@@ -239,13 +239,13 @@ describe("Test Suite 2: putSchema Validation - Valid Schemas", () => {
type: "object",
required: ["type", "payload"],
properties: {
type: { type: "string", format: "cas_ref" },
type: { type: "string", format: "ocas_ref" },
payload: {
anyOf: [{ type: "object" }, { type: "null" }],
},
refs: {
type: "array",
items: { type: "string", format: "cas_ref" },
items: { type: "string", format: "ocas_ref" },
},
},
additionalProperties: false,
@@ -469,7 +469,7 @@ describe("Test Suite 5: Backward Compatibility and Migration", () => {
// This is a documentation test - the old hash was different
const store = new MemStore();
const builtinSchemas = await bootstrap(store);
const newMetaHash = builtinSchemas["@schema"] ?? "";
const newMetaHash = builtinSchemas["@ocas/schema"] ?? "";
// The new hash should be different from the old system metadata hash
// We just verify it's a valid hash format
@@ -555,14 +555,14 @@ describe("Test Suite 6: Integration with Existing Functionality", () => {
expect(validate(store, invalidNode as CasNode)).toBe(false);
});
test("6.3: refs() works with validated schemas containing cas_ref", async () => {
test("6.3: refs() works with validated schemas containing ocas_ref", async () => {
const store = new MemStore();
await bootstrap(store);
const schemaHash = await putSchema(store, {
type: "object",
properties: {
ref: { type: "string", format: "cas_ref" },
ref: { type: "string", format: "ocas_ref" },
},
});
@@ -581,7 +581,7 @@ describe("Test Suite 6: Integration with Existing Functionality", () => {
type: "object",
properties: {
next: {
anyOf: [{ type: "string", format: "cas_ref" }, { type: "null" }],
anyOf: [{ type: "string", format: "ocas_ref" }, { type: "null" }],
},
},
});
@@ -612,7 +612,7 @@ describe("Test Suite 7: Meta-Schema Content Validation", () => {
test("7.1: Meta-schema allows recursive schema definitions", async () => {
const store = new MemStore();
const builtinSchemas = await bootstrap(store);
const metaHash = builtinSchemas["@schema"] ?? "";
const metaHash = builtinSchemas["@ocas/schema"] ?? "";
const metaSchema = getSchema(store, metaHash);
expect(metaSchema).not.toBeNull();
+13 -13
View File
@@ -52,7 +52,7 @@ describe("createFsStore – init and bootstrap", () => {
test("bootstrap returns a valid 13-char self-referencing hash", async () => {
const store = createFsStore(dir);
const builtinSchemas = await bootstrap(store);
const hash = builtinSchemas["@schema"] ?? "";
const hash = builtinSchemas["@ocas/schema"] ?? "";
expect(hash).toHaveLength(13);
expect(hash).toMatch(/^[0-9A-HJKMNP-TV-Z]{13}$/);
@@ -67,7 +67,7 @@ describe("createFsStore – init and bootstrap", () => {
const h2 = await bootstrap(store);
expect(h1).toEqual(h2);
expect(store.listByType(h1["@schema"] ?? "")).toHaveLength(26);
expect(store.listByType(h1["@ocas/schema"] ?? "")).toHaveLength(26);
});
});
@@ -114,7 +114,7 @@ describe("createFsStore – persistence round-trip", () => {
test("bootstrap survives round-trip: self-referencing node reloads correctly", async () => {
const store1 = createFsStore(dir);
const builtinSchemas = await bootstrap(store1);
const hash = builtinSchemas["@schema"] ?? "";
const hash = builtinSchemas["@ocas/schema"] ?? "";
const store2 = createFsStore(dir);
const node = store2.get(hash) as CasNode;
@@ -262,7 +262,7 @@ describe("createFsStore – listByType", () => {
test("bootstrap node is listed under its self type after reload", async () => {
const store1 = createFsStore(dir);
const builtinSchemas = await bootstrap(store1);
const hash = builtinSchemas["@schema"] ?? "";
const hash = builtinSchemas["@ocas/schema"] ?? "";
const store2 = createFsStore(dir);
expect(store2.listByType(hash)).toContain(hash);
@@ -296,7 +296,7 @@ describe("createFsStore – verify on disk-loaded nodes", () => {
test("verify passes on a disk-loaded bootstrap node", async () => {
const store1 = createFsStore(dir);
const builtinSchemas = await bootstrap(store1);
const hash = builtinSchemas["@schema"] ?? "";
const hash = builtinSchemas["@ocas/schema"] ?? "";
const store2 = createFsStore(dir);
const node = store2.get(hash) as CasNode;
@@ -376,18 +376,18 @@ describe("openStore – async with auto-bootstrap", () => {
// Check that bootstrap schemas exist
const builtinSchemas = await bootstrap(store);
const metaHash = builtinSchemas["@schema"];
const metaHash = builtinSchemas["@ocas/schema"];
expect(metaHash).toBeDefined();
expect(store.has(metaHash as string)).toBe(true);
// Verify all core schemas exist
expect(store.has(builtinSchemas["@string"] as string)).toBe(true);
expect(store.has(builtinSchemas["@number"] as string)).toBe(true);
expect(store.has(builtinSchemas["@object"] as string)).toBe(true);
expect(store.has(builtinSchemas["@array"] as string)).toBe(true);
expect(store.has(builtinSchemas["@bool"] as string)).toBe(true);
expect(store.has(builtinSchemas["@schema"] as string)).toBe(true);
expect(store.has(builtinSchemas["@ocas/string"] as string)).toBe(true);
expect(store.has(builtinSchemas["@ocas/number"] as string)).toBe(true);
expect(store.has(builtinSchemas["@ocas/object"] as string)).toBe(true);
expect(store.has(builtinSchemas["@ocas/array"] as string)).toBe(true);
expect(store.has(builtinSchemas["@ocas/bool"] as string)).toBe(true);
expect(store.has(builtinSchemas["@ocas/schema"] as string)).toBe(true);
});
test("openStore bootstrap is idempotent on subsequent opens", async () => {
@@ -426,7 +426,7 @@ describe("openStore – async with auto-bootstrap", () => {
const store2 = await openStore(dir);
const schemas = await bootstrap(store2);
expect(store2.has(schemas["@schema"] as string)).toBe(true);
expect(store2.has(schemas["@ocas/schema"] as string)).toBe(true);
// Old data still exists
expect(store2.listByType(typeHash)).toHaveLength(1);
});