refactor: unified Store type — CasStore + VarStore + TagStore #38

Closed
opened 2026-06-02 03:29:37 +00:00 by xiaoju · 1 comment
Owner

Problem

packages/core/src/variable-store.ts directly imports bun:sqlite, breaking core's store-agnostic principle. Additionally, Store and VariableStore are separate unrelated types, making the API surface fragmented.

Design

Unified Store type

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

CasStore

type CasStore = {
  get(hash: Hash): CasNode | null
  put(type: Hash, payload: unknown): Hash
  has(hash: Hash): boolean
  delete(hash: Hash): boolean
  listByType(type: Hash, options?: ListOptions): ListEntry[]
  listMeta(options?: ListOptions): ListEntry[]
  listSchemas(options?: ListOptions): ListEntry[]
}

VarStore

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
}

TagStore (NEW)

type TagStore = {
  tag(target: Hash, operations: TagOp[]): Tag[]
  untag(target: Hash, keys: string[]): void
  tags(target: Hash): Tag[]
  listByTag(tag: string, options?: ListOptions): Hash[]
}

Tags become a first-class concept on CAS nodes directly, not just on variables. Variable tags are unified as tags on variable.value (hash).

Implementation Plan

  1. Define CasStore, VarStore, TagStore, Store types in core/src/types.ts
  2. Implement createMemoryStore(): Store in core (pure in-memory, all three)
  3. Move SQLite VariableStore to fs, implement as part of createFsStore(): Store
  4. Extract tag storage from variable-store into TagStore implementation
  5. Update bootstrap(), gc(), render() to accept Store (single param)
  6. Update CLI to use store.cas.*, store.var.*, store.tag.*
  7. Ensure @ocas/core has zero bun:sqlite imports

API Change

// Before
const store = await openStore()
const varStore = createVariableStore(db)
bootstrap(store, varStore)
store.get(hash)
varStore.set(name, hash)

// After
const store = await openStore()  // returns Store
bootstrap(store)
store.cas.get(hash)
store.var.set(name, hash)
store.tag.tag(hash, [{key: "env", value: "prod"}])

Breaking Change

All public API surfaces change. Major version bump candidate (0.2.0).


小橘 🍊(NEKO Team)

## Problem `packages/core/src/variable-store.ts` directly imports `bun:sqlite`, breaking core's store-agnostic principle. Additionally, `Store` and `VariableStore` are separate unrelated types, making the API surface fragmented. ## Design ### Unified `Store` type ```ts type Store = { cas: CasStore var: VarStore tag: TagStore } ``` ### CasStore ```ts type CasStore = { get(hash: Hash): CasNode | null put(type: Hash, payload: unknown): Hash has(hash: Hash): boolean delete(hash: Hash): boolean listByType(type: Hash, options?: ListOptions): ListEntry[] listMeta(options?: ListOptions): ListEntry[] listSchemas(options?: ListOptions): ListEntry[] } ``` ### VarStore ```ts 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 } ``` ### TagStore (NEW) ```ts type TagStore = { tag(target: Hash, operations: TagOp[]): Tag[] untag(target: Hash, keys: string[]): void tags(target: Hash): Tag[] listByTag(tag: string, options?: ListOptions): Hash[] } ``` Tags become a first-class concept on CAS nodes directly, not just on variables. Variable tags are unified as tags on `variable.value` (hash). ## Implementation Plan 1. Define `CasStore`, `VarStore`, `TagStore`, `Store` types in `core/src/types.ts` 2. Implement `createMemoryStore(): Store` in core (pure in-memory, all three) 3. Move SQLite `VariableStore` to `fs`, implement as part of `createFsStore(): Store` 4. Extract tag storage from variable-store into `TagStore` implementation 5. Update `bootstrap()`, `gc()`, `render()` to accept `Store` (single param) 6. Update CLI to use `store.cas.*`, `store.var.*`, `store.tag.*` 7. Ensure `@ocas/core` has zero `bun:sqlite` imports ## API Change ```ts // Before const store = await openStore() const varStore = createVariableStore(db) bootstrap(store, varStore) store.get(hash) varStore.set(name, hash) // After const store = await openStore() // returns Store bootstrap(store) store.cas.get(hash) store.var.set(name, hash) store.tag.tag(hash, [{key: "env", value: "prod"}]) ``` ## Breaking Change All public API surfaces change. Major version bump candidate (0.2.0). --- 小橘 🍊(NEKO Team)
xiaoju changed title from refactor: move VariableStore implementation from core to fs to refactor: unified Store type — CasStore + VarStore + TagStore 2026-06-02 05:27:44 +00:00
Author
Owner

#38 Complete

All 5 phases done:

  • Phase 1 (#39) — Type definitions (PR #44)
  • Phase 2 (#40) — MemoryStore implements OcasStore (PR #45)
  • Phase 3 (#41) — Core function signatures unified (PR #46)
  • Phase 4 (#42) — FsStore implements OcasStore (PR #48)
  • Phase 5 (#43) — CLI adapted (done in PR #46, no separate PR needed)

Results:

  • Store = { cas: CasStore, var: VarStore, tag: TagStore }
  • Zero bun:sqlite in @ocas/core
  • 591 tests pass
  • Cleanup items tracked in #47

Ready for v0.2.0.

小橘 🍊(NEKO Team)

## #38 Complete ✅ All 5 phases done: - Phase 1 (#39) — Type definitions (PR #44) - Phase 2 (#40) — MemoryStore implements OcasStore (PR #45) - Phase 3 (#41) — Core function signatures unified (PR #46) - Phase 4 (#42) — FsStore implements OcasStore (PR #48) - Phase 5 (#43) — CLI adapted (done in PR #46, no separate PR needed) **Results:** - `Store = { cas: CasStore, var: VarStore, tag: TagStore }` ✅ - Zero `bun:sqlite` in `@ocas/core` ✅ - 591 tests pass ✅ - Cleanup items tracked in #47 Ready for v0.2.0. 小橘 🍊(NEKO Team)
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: shazhou/ocas#38