Merge pull request 'fix: uwf cas — JSON output + meta-schema in schema list' (#326) from fix/319-cas-json-output into main
This commit is contained in:
+31
-20
@@ -184,10 +184,11 @@ cas
|
||||
.command("get")
|
||||
.description("Read a CAS node as JSON")
|
||||
.argument("<hash>", "CAS hash (13 char)")
|
||||
.option("--json", "Compact JSON output")
|
||||
.action((hash: string, opts: { json?: boolean }) => {
|
||||
.action((hash: string) => {
|
||||
const storageRoot = resolveStorageRoot();
|
||||
runAction(() => cmdCasGet(storageRoot, hash, opts));
|
||||
runAction(async () => {
|
||||
writeJson(await cmdCasGet(storageRoot, hash));
|
||||
});
|
||||
});
|
||||
|
||||
cas
|
||||
@@ -195,10 +196,11 @@ cas
|
||||
.description("Output a CAS node (--payload for payload only)")
|
||||
.argument("<hash>", "CAS hash (13 char)")
|
||||
.option("--payload", "Output only the payload")
|
||||
.option("--json", "Compact JSON output")
|
||||
.action((hash: string, opts: { payload?: boolean; json?: boolean }) => {
|
||||
.action((hash: string, opts: { payload?: boolean }) => {
|
||||
const storageRoot = resolveStorageRoot();
|
||||
runAction(() => cmdCasCat(storageRoot, hash, opts));
|
||||
runAction(async () => {
|
||||
writeJson(await cmdCasCat(storageRoot, hash, opts));
|
||||
});
|
||||
});
|
||||
|
||||
cas
|
||||
@@ -206,19 +208,22 @@ cas
|
||||
.description("Store a node, print its hash")
|
||||
.argument("<type-hash>", "Type (schema) hash")
|
||||
.argument("<data>", "JSON file path or inline JSON string")
|
||||
.option("--json", "Compact JSON output")
|
||||
.action((typeHash: string, data: string, opts: { json?: boolean }) => {
|
||||
.action((typeHash: string, data: string) => {
|
||||
const storageRoot = resolveStorageRoot();
|
||||
runAction(() => cmdCasPut(storageRoot, typeHash, data, opts));
|
||||
runAction(async () => {
|
||||
writeJson(await cmdCasPut(storageRoot, typeHash, data));
|
||||
});
|
||||
});
|
||||
|
||||
cas
|
||||
.command("has")
|
||||
.description("Check if a hash exists (prints true/false)")
|
||||
.description("Check if a hash exists")
|
||||
.argument("<hash>", "CAS hash (13 char)")
|
||||
.action((hash: string) => {
|
||||
const storageRoot = resolveStorageRoot();
|
||||
runAction(() => cmdCasHas(storageRoot, hash));
|
||||
runAction(async () => {
|
||||
writeJson(await cmdCasHas(storageRoot, hash));
|
||||
});
|
||||
});
|
||||
|
||||
cas
|
||||
@@ -227,37 +232,43 @@ cas
|
||||
.argument("<hash>", "CAS hash (13 char)")
|
||||
.action((hash: string) => {
|
||||
const storageRoot = resolveStorageRoot();
|
||||
runAction(() => cmdCasRefs(storageRoot, hash));
|
||||
runAction(async () => {
|
||||
writeJson(await cmdCasRefs(storageRoot, hash));
|
||||
});
|
||||
});
|
||||
|
||||
cas
|
||||
.command("walk")
|
||||
.description("Recursive traversal from a node")
|
||||
.argument("<hash>", "CAS hash (13 char)")
|
||||
.option("--format <fmt>", "Output format: flat (default) or tree")
|
||||
.action((hash: string, opts: { format?: string }) => {
|
||||
.action((hash: string) => {
|
||||
const storageRoot = resolveStorageRoot();
|
||||
runAction(() => cmdCasWalk(storageRoot, hash, opts));
|
||||
runAction(async () => {
|
||||
writeJson(await cmdCasWalk(storageRoot, hash));
|
||||
});
|
||||
});
|
||||
|
||||
const casSchema = cas.command("schema").description("CAS schema operations");
|
||||
|
||||
casSchema
|
||||
.command("list")
|
||||
.description("List all registered schemas (hash + name)")
|
||||
.description("List all registered schemas")
|
||||
.action(() => {
|
||||
const storageRoot = resolveStorageRoot();
|
||||
runAction(() => cmdCasSchemaList(storageRoot));
|
||||
runAction(async () => {
|
||||
writeJson(await cmdCasSchemaList(storageRoot));
|
||||
});
|
||||
});
|
||||
|
||||
casSchema
|
||||
.command("get")
|
||||
.description("Show a schema by its type hash")
|
||||
.argument("<hash>", "Schema type hash")
|
||||
.option("--json", "Compact JSON output")
|
||||
.action((hash: string, opts: { json?: boolean }) => {
|
||||
.action((hash: string) => {
|
||||
const storageRoot = resolveStorageRoot();
|
||||
runAction(() => cmdCasSchemaGet(storageRoot, hash, opts));
|
||||
runAction(async () => {
|
||||
writeJson(await cmdCasSchemaGet(storageRoot, hash));
|
||||
});
|
||||
});
|
||||
|
||||
program.parseAsync(process.argv).catch((e: unknown) => {
|
||||
|
||||
@@ -11,12 +11,7 @@ function openStore(storageRoot: string): Store {
|
||||
return createFsStore(join(storageRoot, "cas"));
|
||||
}
|
||||
|
||||
function out(data: unknown, compact = false): void {
|
||||
console.log(compact ? JSON.stringify(data) : JSON.stringify(data, null, 2));
|
||||
}
|
||||
|
||||
function readJsonArg(fileOrInline: string): unknown {
|
||||
// Try as inline JSON first, then as file path
|
||||
try {
|
||||
return JSON.parse(fileOrInline);
|
||||
} catch {
|
||||
@@ -28,138 +23,114 @@ function readJsonArg(fileOrInline: string): unknown {
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Commands ----
|
||||
// ---- Commands (all return JSON-serializable data) ----
|
||||
|
||||
export async function cmdCasGet(
|
||||
storageRoot: string,
|
||||
hash: string,
|
||||
opts: { json?: boolean },
|
||||
): Promise<void> {
|
||||
): Promise<unknown> {
|
||||
const store = openStore(storageRoot);
|
||||
const node = store.get(hash);
|
||||
if (node === null) {
|
||||
throw new Error(`Node not found: ${hash}`);
|
||||
}
|
||||
out(node, opts.json);
|
||||
return node;
|
||||
}
|
||||
|
||||
export async function cmdCasCat(
|
||||
storageRoot: string,
|
||||
hash: string,
|
||||
opts: { payload?: boolean; json?: boolean },
|
||||
): Promise<void> {
|
||||
opts: { payload?: boolean },
|
||||
): Promise<unknown> {
|
||||
const store = openStore(storageRoot);
|
||||
const node = store.get(hash);
|
||||
if (node === null) {
|
||||
throw new Error(`Node not found: ${hash}`);
|
||||
}
|
||||
out(opts.payload ? node.payload : node, opts.json);
|
||||
return opts.payload ? node.payload : node;
|
||||
}
|
||||
|
||||
export async function cmdCasPut(
|
||||
storageRoot: string,
|
||||
typeHash: string,
|
||||
data: string,
|
||||
opts: { json?: boolean },
|
||||
): Promise<void> {
|
||||
): Promise<{ hash: string }> {
|
||||
const store = openStore(storageRoot);
|
||||
const payload = readJsonArg(data);
|
||||
const hash = store.put(typeHash, payload);
|
||||
console.log(hash);
|
||||
const hash = await store.put(typeHash, payload);
|
||||
return { hash };
|
||||
}
|
||||
|
||||
export async function cmdCasHas(
|
||||
storageRoot: string,
|
||||
hash: string,
|
||||
): Promise<void> {
|
||||
): Promise<{ exists: boolean }> {
|
||||
const store = openStore(storageRoot);
|
||||
console.log(String(store.has(hash)));
|
||||
return { exists: store.has(hash) };
|
||||
}
|
||||
|
||||
export async function cmdCasList(storageRoot: string): Promise<void> {
|
||||
const store = openStore(storageRoot);
|
||||
for (const hash of store.list()) {
|
||||
console.log(hash);
|
||||
}
|
||||
}
|
||||
|
||||
export async function cmdCasRefs(storageRoot: string, hash: string): Promise<void> {
|
||||
export async function cmdCasRefs(
|
||||
storageRoot: string,
|
||||
hash: string,
|
||||
): Promise<{ refs: string[] }> {
|
||||
const store = openStore(storageRoot);
|
||||
const node = store.get(hash);
|
||||
if (node === null) {
|
||||
throw new Error(`Node not found: ${hash}`);
|
||||
}
|
||||
const refHashes = refs(store, node);
|
||||
for (const r of refHashes) {
|
||||
console.log(r);
|
||||
}
|
||||
return { refs: refs(store, node) };
|
||||
}
|
||||
|
||||
export async function cmdCasWalk(
|
||||
storageRoot: string,
|
||||
hash: string,
|
||||
opts: { format?: string },
|
||||
): Promise<void> {
|
||||
): Promise<{ hashes: string[] }> {
|
||||
const store = openStore(storageRoot);
|
||||
|
||||
if (opts.format === "tree") {
|
||||
const childMap = new Map<Hash, Hash[]>();
|
||||
walk(store, hash, (h, node) => {
|
||||
childMap.set(h, refs(store, node));
|
||||
});
|
||||
|
||||
const printed = new Set<Hash>();
|
||||
|
||||
function printNode(h: Hash, prefix: string, isLast: boolean): void {
|
||||
const connector = prefix === "" ? "" : isLast ? "└── " : "├── ";
|
||||
if (printed.has(h)) {
|
||||
console.log(`${prefix}${connector}${h} (seen)`);
|
||||
return;
|
||||
}
|
||||
printed.add(h);
|
||||
console.log(`${prefix}${connector}${h}`);
|
||||
|
||||
const kids = childMap.get(h) ?? [];
|
||||
const childPrefix = prefix === "" ? "" : prefix + (isLast ? " " : "│ ");
|
||||
for (let i = 0; i < kids.length; i++) {
|
||||
printNode(kids[i] as Hash, childPrefix, i === kids.length - 1);
|
||||
}
|
||||
}
|
||||
|
||||
printNode(hash, "", true);
|
||||
} else {
|
||||
walk(store, hash, (h) => {
|
||||
console.log(h);
|
||||
});
|
||||
}
|
||||
const result: string[] = [];
|
||||
walk(store, hash, (h) => {
|
||||
result.push(h);
|
||||
});
|
||||
return { hashes: result };
|
||||
}
|
||||
|
||||
export async function cmdCasSchemaList(storageRoot: string): Promise<void> {
|
||||
export type SchemaListEntry = {
|
||||
hash: string;
|
||||
title: string;
|
||||
};
|
||||
|
||||
export async function cmdCasSchemaList(
|
||||
storageRoot: string,
|
||||
): Promise<SchemaListEntry[]> {
|
||||
const store = openStore(storageRoot);
|
||||
const metaHash = await bootstrap(store);
|
||||
const entries: SchemaListEntry[] = [];
|
||||
|
||||
// Include meta-schema itself
|
||||
entries.push({ hash: metaHash, title: "(meta-schema)" });
|
||||
|
||||
for (const hash of store.list()) {
|
||||
if (hash === metaHash) continue;
|
||||
const node = store.get(hash);
|
||||
if (node !== null && node.type === metaHash) {
|
||||
const schema = node.payload as JSONSchema;
|
||||
const name =
|
||||
const title =
|
||||
(schema.title as string | undefined) ??
|
||||
(schema.description as string | undefined) ??
|
||||
"(unnamed)";
|
||||
console.log(`${hash} ${name}`);
|
||||
entries.push({ hash, title });
|
||||
}
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
export async function cmdCasSchemaGet(
|
||||
storageRoot: string,
|
||||
hash: string,
|
||||
opts: { json?: boolean },
|
||||
): Promise<void> {
|
||||
): Promise<unknown> {
|
||||
const store = openStore(storageRoot);
|
||||
const schema = getSchema(store, hash);
|
||||
if (schema === null) {
|
||||
throw new Error(`Schema not found: ${hash}`);
|
||||
}
|
||||
out(schema, opts.json);
|
||||
return schema;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user