Files
ocas/CLAUDE.md
T
xiaoju 9ac08e5893 chore: 去掉 Bun,切换到 pnpm + 纯 Node runtime
- bun.lock → pnpm-lock.yaml + pnpm-workspace.yaml
- 删除 sqlite-adapter.ts(Bun/Node 双 runtime 兼容层,未被使用)
- package.json 删除 workspaces 字段,加 pnpm.onlyBuiltDependencies
- 加 vite 8 显式依赖(vitest 4.x peer dep)
- CLAUDE.md 全面更新:Runtime/Commands/Release 流程
- .gitignore 加 bun.lock

36/36 files pass, 617/617 tests pass

Fixes #66
2026-06-03 07:43:09 +00:00

163 lines
6.6 KiB
Markdown

# CLAUDE.md — OCAS
Object Content Addressable Store — self-describing CAS with JSON Schema typed nodes.
## Project Structure
Monorepo with 3 packages under `packages/`:
| Package | Directory | Description |
|---------|-----------|-------------|
| `@ocas/core` | `packages/core` | Core CAS engine — hashing, schema, store, verify, bootstrap |
| `@ocas/fs` | `packages/fs` | Filesystem-backed CAS store |
| `@ocas/cli` | `packages/cli` | CLI tool (`ocas` binary) |
## Tech Stack
- **Runtime:** Node.js
- **Language:** TypeScript (strict mode, `exactOptionalPropertyTypes`, `noUncheckedIndexedAccess`)
- **Build:** `tsc` (composite project references, sequential: core → fs → cli)
- **Test:** Vitest (`npx vitest run`)
- **Package Manager:** pnpm (workspace)
- **Lint/Format:** Biome (`biome check .` / `biome format --write .`)
- **Publish:** Changesets + `pnpm publish` → npmjs (`@ocas/*`)
## Commands
```bash
pnpm run test # Run all tests (vitest)
pnpm run build # Build all packages (tsc via proman)
pnpm run check # Biome lint
pnpm run format # Biome format (auto-fix)
```
## Code Conventions
### TypeScript
- **Strict mode** — no `any`, no unchecked index access, no implicit overrides
- **`verbatimModuleSyntax`** — use `import type` for type-only imports
- **Import paths** — use `.js` extension in imports (ESM convention with bundler resolution)
- **Export style** — named exports only, re-export from `index.ts`
### Biome Rules
- `noConsole: "error"` globally (except `packages/cli`)
- Recommended ruleset enabled
- Auto-organize imports via `assist.actions.source.organizeImports`
- Indent: 2 spaces
### Naming
- Types: `PascalCase` (`CasNode`, `Hash`, `Store`)
- Functions: `camelCase` (`computeHash`, `createMemoryStore`)
- Constants: `UPPER_SNAKE_CASE` (`BOOTSTRAP_STORE`)
- Files: `kebab-case.ts`
- Test files: co-located as `*.test.ts`
### Key Types
- `Hash` — 13-character uppercase Crockford Base32 string (XXH64)
- `CasNode` — content-addressed node with schema
- `Store` — unified storage interface `{ cas: CasStore, var: VarStore, tag: TagStore }`
- `ListOptions` — sorting/pagination options (`sort`, `desc`, `limit`, `offset`)
- `ListEntry` — list result entry (`hash`, `created`, `updated`)
### List Utilities
`packages/core/src/list-utils.ts` provides `applyListOptions()` — in-memory sort/paginate for `ListEntry[]` arrays. Used by `MemoryStore`; `FsStore` pushes sort/limit to SQLite. Core layer treats `limit: undefined` as "no limit"; the CLI defaults to 100 in `parseListOptions()`.
### Architecture Notes
- **No "alias" concept** — every name resolution flows through `store.var`. Builtin schemas (`@ocas/schema`, `@ocas/string`, `@ocas/output/*`, …) are registered as variables during `bootstrap(store)`, alongside user-defined variables created via `ocas var set`.
- **`bootstrap(store)`** synchronously writes builtin name → hash bindings into the unified store; called automatically by `openStore()`.
- **`resolveHash(input, store)`** is the unified hash/name resolver in the CLI. If `input` matches the 13-char hash format it is returned as-is; otherwise `store.var` is queried by exact name. This means every CLI command that accepts a hash argument also accepts a variable name (schema names, user vars, etc.).
- **Variable naming**: all names must follow `@scope/name` format (`@[a-zA-Z][a-zA-Z0-9]*/segments`). `@ocas/*` is reserved for builtins. The `@` prefix ensures names are visually distinct from hashes.
- **`openStore()`** returns a unified `Store` with `cas`, `var`, and `tag` sub-stores, and bootstraps automatically. `@ocas/core` has zero SQLite dependency.
### Internal Dependencies
Workspace packages reference each other with `workspace:*` in `package.json`.
This is resolved to real version numbers only during publishing (see below).
## Git
- Commit format: `type: description` (conventional commits)
- Reference issues: `Fixes #N` / `Closes #N`
- Author: `小橘 <xiaoju@shazhou.work>`
## Project Rules
- [docs/sync-readme.md](docs/sync-readme.md) — README sync conventions
## Before Submitting
1. `pnpm run test` — all tests pass
2. `pnpm run check` — no lint errors
3. `pnpm run build` — builds cleanly
## Release Process
Releases use a **release branch** workflow with three phases: prepare → candidate → finalize.
`main` always keeps `workspace:*` for internal deps; release branches fix them to real versions.
Changeset files are **only consumed once** during finalize — prerelease (rc) never touches them.
### Adding a Changeset
Add changesets alongside feature PRs on `main`:
```markdown
<!-- .changeset/my-change.md -->
---
"@ocas/cli": patch
---
Description of the change
```
Changesets live in `.changeset/` as markdown files. Bump types: `patch` / `minor` / `major`.
One changeset can cover multiple packages.
### Phase 1: Prepare (cut release branch)
- **Precondition:** on `main`, clean tree, `.changeset/` has pending changesets
- **Steps:**
1. Determine target version (from changeset bump types or manually)
2. `git checkout -b release/<version>`
3. Fix `workspace:*` → real version numbers in all `package.json`
4. Commit
- **Does NOT** run `changeset version`, does NOT write CHANGELOG
### Phase 2: Candidate (publish rc for validation)
- **Precondition:** on `release/*` branch
- **Steps:**
1. Set version to `<version>-rc.N` (first time rc.1, increment on subsequent runs)
2. `pnpm install && pnpm run build && pnpm run test && pnpm run check`
3. Publish: `pnpm publish --tag rc` (order: core → fs → cli)
4. Commit + push
- **Repeatable:** fix bugs → add new changesets on the release branch → rc.N+1
- **Does NOT** consume changesets, does NOT write CHANGELOG
- Install for testing: `pnpm add -g @ocas/cli@rc`
### Phase 3: Finalize (official release)
- **Precondition:** on `release/*` branch, rc validated
- **Steps:**
1. Consume all `.changeset/*.md` → write CHANGELOG entries (use `changeset version` or manual)
2. Set final version `<version>` (remove `-rc.N`)
3. `pnpm install && pnpm run build && pnpm run test && pnpm run check`
4. Publish: `pnpm publish --tag latest` (order: core → fs → cli)
5. Git tag `v<version>`
6. Merge back to `main` (CHANGELOG comes along)
7. Restore `workspace:*` on `main`
8. Delete release branch
### Key Rules
- **Publish order** is always `@ocas/core``@ocas/fs``@ocas/cli`
- **`workspace:*`** must be fixed before any publish — `pnpm publish` does NOT auto-replace them
- **CHANGELOG** only contains official releases, never rc entries
- **Changesets added on release branch** (bug fixes during rc) are consumed together at finalize