feat(core): define CasStore, VarStore, TagStore, Store types

Fixes #39
This commit is contained in:
2026-06-02 05:54:32 +00:00
parent c83e98cd7e
commit 828514def5
3 changed files with 260 additions and 0 deletions
+9
View File
@@ -25,11 +25,20 @@ export {
export { createMemoryStore } from "./store.js"; export { createMemoryStore } from "./store.js";
export type { export type {
CasNode, CasNode,
CasStore,
Hash, Hash,
HistoryEntry,
ListEntry, ListEntry,
ListOptions, ListOptions,
ListSort, ListSort,
OcasStore,
Store, Store,
Tag,
TagOp,
TagStore,
VarListOptions,
VarSetOptions,
VarStore,
} from "./types.js"; } from "./types.js";
export type { Variable } from "./variable.js"; export type { Variable } from "./variable.js";
export { export {
+154
View File
@@ -0,0 +1,154 @@
import { describe, expect, test } from "bun:test";
import { readFileSync } from "node:fs";
import { join } from "node:path";
import type {
CasNode,
CasStore,
Hash,
HistoryEntry,
ListEntry,
ListOptions,
OcasStore,
Tag,
TagOp,
TagStore,
Variable,
VarListOptions,
VarSetOptions,
VarStore,
} from "./index.js";
describe("CasStore type", () => {
test("has all required methods with correct signatures", () => {
const stub: CasStore = {
get: (_h: Hash): CasNode | null => null,
put: (_t: Hash, _p: unknown): Hash => "0000000000000",
has: (_h: Hash): boolean => false,
delete: (_h: Hash): boolean => false,
listByType: (_t: Hash, _o?: ListOptions): ListEntry[] => [],
listMeta: (_o?: ListOptions): ListEntry[] => [],
listSchemas: (_o?: ListOptions): ListEntry[] => [],
};
expect(typeof stub.get).toBe("function");
expect(typeof stub.put).toBe("function");
expect(typeof stub.has).toBe("function");
expect(typeof stub.delete).toBe("function");
expect(typeof stub.listByType).toBe("function");
expect(typeof stub.listMeta).toBe("function");
expect(typeof stub.listSchemas).toBe("function");
});
});
describe("VarStore type", () => {
test("VarStore shape", () => {
const sample: Variable = {
name: "@x/y",
schema: "0",
value: "0",
created: 0,
updated: 0,
tags: {},
labels: [],
};
const stub: VarStore = {
set: (_n: string, _h: Hash, _o?: VarSetOptions): Variable => sample,
get: (_n: string, _s?: Hash): Variable | null => null,
remove: (_n: string, _s?: Hash): Variable[] => [],
update: (_n: string, _h: Hash, _o?: VarSetOptions): Variable => sample,
list: (_o?: VarListOptions): Variable[] => [],
history: (_n: string, _s?: Hash): HistoryEntry[] => [],
close: (): void => {},
};
expect(typeof stub.set).toBe("function");
expect(typeof stub.get).toBe("function");
expect(typeof stub.remove).toBe("function");
expect(typeof stub.update).toBe("function");
expect(typeof stub.list).toBe("function");
expect(typeof stub.history).toBe("function");
expect(typeof stub.close).toBe("function");
});
test("VarSetOptions shape", () => {
const opts: VarSetOptions = { tags: { k: "v" }, labels: ["l"] };
expect(opts.tags?.k).toBe("v");
});
test("VarListOptions extends ListOptions", () => {
const opts: VarListOptions = {
namePrefix: "@x/",
exactName: "@x/y",
schema: "0",
tags: { k: "v" },
labels: ["l"],
sort: "created",
desc: true,
limit: 10,
offset: 0,
};
expect(opts.namePrefix).toBe("@x/");
});
test("HistoryEntry shape", () => {
const e: HistoryEntry = { value: "0", position: 0, setAt: 0 };
expect(e.position).toBe(0);
});
});
describe("TagStore type", () => {
test("TagStore shape", () => {
const stub: TagStore = {
tag: (_t: Hash, _ops: TagOp[]): Tag[] => [],
untag: (_t: Hash, _k: string[]): void => {},
tags: (_t: Hash): Tag[] => [],
listByTag: (_t: string, _o?: ListOptions): Hash[] => [],
};
expect(typeof stub.tag).toBe("function");
expect(typeof stub.untag).toBe("function");
expect(typeof stub.tags).toBe("function");
expect(typeof stub.listByTag).toBe("function");
});
test("Tag and TagOp shapes", () => {
const t: Tag = { key: "k", value: "v", target: "0", created: 0 };
const tNull: Tag = { key: "label", value: null, target: "0", created: 0 };
const op1: TagOp = { op: "set", key: "k", value: "v" };
const op2: TagOp = { op: "delete", key: "k" };
expect(t.value).toBe("v");
expect(tNull.value).toBeNull();
expect(op1.op).toBe("set");
expect(op2.op).toBe("delete");
});
});
describe("Aggregate OcasStore type", () => {
test("has cas, var, tag fields", () => {
type _AssertCas = OcasStore["cas"] extends CasStore ? true : false;
type _AssertVar = OcasStore["var"] extends VarStore ? true : false;
type _AssertTag = OcasStore["tag"] extends TagStore ? true : false;
const a: _AssertCas = true;
const b: _AssertVar = true;
const c: _AssertTag = true;
expect(a && b && c).toBe(true);
});
});
describe("source convention", () => {
test("types.ts uses 'type' not 'interface' for new types", () => {
const src = readFileSync(join(import.meta.dir, "types.ts"), "utf8");
for (const name of ["CasStore", "VarStore", "TagStore"]) {
expect(src).toMatch(new RegExp(`export\\s+type\\s+${name}\\b`));
expect(src).not.toMatch(new RegExp(`interface\\s+${name}\\b`));
}
});
});
describe("Exports surface", () => {
test("@ocas/core exports the new type names (compile-time only)", () => {
type _C = CasStore extends object ? true : never;
type _V = VarStore extends object ? true : never;
type _T = TagStore extends object ? true : never;
type _O = OcasStore extends object ? true : never;
const _checks: [_C, _V, _T, _O] = [true, true, true, true];
expect(_checks.length).toBe(4);
});
});
+97
View File
@@ -1,3 +1,5 @@
import type { Variable } from "./variable.js";
/** /**
* 13-character uppercase Crockford Base32 string produced by XXH64. * 13-character uppercase Crockford Base32 string produced by XXH64.
*/ */
@@ -58,3 +60,98 @@ export type Store = {
listSchemas(options?: ListOptions): ListEntry[]; listSchemas(options?: ListOptions): ListEntry[];
delete(hash: Hash): void; delete(hash: Hash): void;
}; };
/**
* Synchronous content-addressable store interface (new unified design).
* Unlike legacy `Store`, `put` returns the hash synchronously.
*/
export type CasStore = {
get(hash: Hash): CasNode | null;
put(typeHash: Hash, payload: unknown): Hash;
has(hash: Hash): boolean;
delete(hash: Hash): boolean;
listByType(typeHash: Hash, options?: ListOptions): ListEntry[];
listMeta(options?: ListOptions): ListEntry[];
listSchemas(options?: ListOptions): ListEntry[];
};
/**
* Options for setting/updating a variable.
*/
export type VarSetOptions = {
tags?: Record<string, string>;
labels?: string[];
};
/**
* Options for listing variables.
*/
export type VarListOptions = ListOptions & {
namePrefix?: string;
exactName?: string;
schema?: Hash;
tags?: Record<string, string>;
labels?: string[];
};
/**
* One entry in a variable's history (most-recent-first per `position`).
*/
export type HistoryEntry = {
value: Hash;
position: number;
setAt: number;
};
/**
* Variable store interface — mutable bindings (name, schema) → hash.
*/
export type VarStore = {
set(name: string, hash: Hash, options?: VarSetOptions): Variable;
get(name: string, schema?: Hash): Variable | null;
remove(name: string, schema?: Hash): Variable[];
update(name: string, hash: Hash, options?: VarSetOptions): Variable;
list(options?: VarListOptions): Variable[];
history(name: string, schema?: Hash): HistoryEntry[];
close(): void;
};
/**
* A tag attached to a CAS target. `value === null` indicates a label
* (bare identifier); otherwise a key-value tag.
*/
export type Tag = {
key: string;
value: string | null;
target: Hash;
created: number;
};
/**
* A tag mutation operation: set (with value) or delete (key only).
*/
export type TagOp = {
op: "set" | "delete";
key: string;
value?: string;
};
/**
* Tag store interface — manages key-value tags and labels on CAS targets.
*/
export type TagStore = {
tag(target: Hash, operations: TagOp[]): Tag[];
untag(target: Hash, keys: string[]): void;
tags(target: Hash): Tag[];
listByTag(tag: string, options?: ListOptions): Hash[];
};
/**
* Aggregate OCAS store: bundles CAS, variable, and tag stores.
* Named `OcasStore` to avoid colliding with the legacy `Store` export.
*/
export type OcasStore = {
cas: CasStore;
var: VarStore;
tag: TagStore;
};