Address reviewer feedback on #83:
- Replace dynamic `await import("./bootstrap-capable.js")` in
bundle.ts with a static top-of-file import (no real cycle exists,
bootstrap.ts is already statically imported).
- Remove unused `bootstrapSym` Symbol.for() / `void bootstrapSym`
dead code in importBundle.
- Move the misplaced `import { decode } from "cborg"` to the top of
bundle.ts with the other imports.
- Document the new `export` / `import` commands and `--store`,
`--scope`, `-o` flags in:
- root README.md (commands + global flags)
- packages/cli/README.md (command table + bundles section + global flags)
- packages/cli/prompts/usage.md (`ocas prompt usage` output)
- packages/core/README.md (Closure & Bundles API surface)
- .cards/cli.md (bundle commands + --store architecture note)
Implements `ocas export` / `ocas import` for shipping a self-contained
closure of CAS nodes, variables and tags between stores, plus a
read-only `--store <bundle.tar>` flag for inspecting bundles without
extracting them.
- core: computeClosure walks refs + schema chains and gathers vars/tags
- core: exportBundle / importBundle / loadBundleStore use a custom
POSIX/ustar tar (no external deps); content-addressed dedup on import,
optional --scope remap of non-@ocas variable names
- core: new @ocas/output/export and @ocas/output/import builtin schemas
- cli: new export and import commands, --store read-only mode, write
commands rejected with a clear error when --store is set
Closes#83
FsStore previously CBOR-decoded all .bin nodes into memory at startup,
making cold-open O(n) in time and memory. Now it scans only filenames
into a Set<Hash> at init and reads/decodes nodes from disk on first
get(). has() and listAll() use the filename set; put() write-throughs
to cache; delete() clears cache and disk. Index/meta migration still
performs a one-time scan when _index/ is missing.
Adds 12 new tests (L1-L12) covering startup-no-decode, lazy get,
filename-based has/listAll, write-through put, delete cleanup, list
operations, and migration/bootstrap regression. All existing tests
pass unchanged.
Fixes#85
Restructure the FsStore on-disk layout so that all `<HASH>.bin` node
files live under a `nodes/` subdirectory of the store root, instead of
being interleaved with metadata directories (`_index/`, `_store.db`,
`_meta`) at the top level.
Pre-existing flat-layout stores are auto-migrated on first open: any
`.bin` files found in the store root are renamed (not copied) into
`nodes/`. Migration is idempotent and safe to re-run.
Fixes#84
The local runCli helpers in edge-cases.test.ts (Phase 3/4/7) and raw
execFileSync calls in schema-validation.test.ts (tests 2.1, 2.3) were
missing NODE_NO_WARNINGS=1, causing ExperimentalWarning to leak into
stderr snapshots. Added env override to match what helpers.runCli
already does.
prompts/*.md files are not copied to dist/ during tsc build,
causing `ocas prompt setup/usage` to fail with ENOENT.
- Change join(__dirname, "prompts") → join(__dirname, "..", "prompts")
- Add prompts/ to package.json "files" for npm publishing
- Update snapshots (Node.js SQLite ExperimentalWarning + version string)
- Use NODE_NO_WARNINGS=1 in execFileSync env instead of --no-warnings flag
- Remove overly broad process.removeAllListeners('warning') from CLI entry
- Add engines field requiring Node >=22.5.0 (node:sqlite availability)
- Update proman to 0.4.2
- Replace better-sqlite3 with built-in node:sqlite (DatabaseSync)
- Add transaction() helper for manual BEGIN/COMMIT/ROLLBACK
- Suppress ExperimentalWarning in CLI tests with --no-warnings
- Remove better-sqlite3 from dependencies and onlyBuiltDependencies
- No more native addon compilation issues across Node versions
- Bump all packages to 0.2.2
- Fix noNonNullAssertion in cli/index.ts
- Fix unused imports in fs/sqlite-store.ts
- Biome 2.4.16 migration + format all files
- Update snapshots for version change
Critical fixes:
1. LIKE ESCAPE '\' clause added for namePrefix queries
2. list() limit/offset now applied AFTER tag/label post-filter
3. tag()/untag() wrapped in db.transaction()
Warning fixes:
4. set()/update() wrapped in db.transaction() (txnSetVar)
5. listByTag sort/limit/offset pushed to SQL
6. close() idempotent (double-close guard)
7. JSONL → SQLite migration on first open (vars + tags)
8. History truncation simplified (NOT IN subquery)
9. Removed unnecessary pre-fetch in upsert (ON CONFLICT handles it)
Nits:
10. Database.Database type instead of InstanceType
11. row.name instead of row["name"]
12. DESC index on var_history position
13. key+value composite index on tags
When a CAS node has tags, ocas get includes them in the envelope's
value as a `tags` array. When a variable's value hash has tags,
ocas var get includes them as a `valueTags` array (separate from
the variable's own tags/labels). Untagged nodes/values produce
byte-identical output as before (no empty array serialized).
Schemas @ocas/output/get and @ocas/output/var-get extended to
accept the new optional fields.
Closes#53
Adds `ocas tag <target> <tag>...` and `ocas untag <target> <tag>...`
top-level CLI commands operating on store.tag.* (TagStore). Targets
may be hashes or @scope/name variables (resolved via resolveHash).
The redundant `ocas var tag` subcommand is removed; `var tag` now
falls through to "Unknown var subcommand: tag".
Registers `@ocas/output/tag` and `@ocas/output/untag` schemas and
templates in bootstrap; removes `@ocas/output/var-tag`.
Closes#52
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove legacy `Store` type with `Promise<Hash>` `put`; rename `OcasStore` → `Store`
across @ocas/core, @ocas/fs, @ocas/cli (production + tests).
- Migrate `BootstrapCapableStore` to `CasStore`; `[BOOTSTRAP_STORE]` returns `Hash`
synchronously.
- Make `bootstrap()` and `putSchema()` synchronous; remove `await` at all call sites.
- Extract pure VarStore helpers into `packages/core/src/var-store-helpers.ts`
(`varKey`, `addNameIndex`, `removeNameIndex`, `extractSchema`, `checkTagLabelConflict`,
`pushHistory`, `cloneVarRecord`, `VarRecord`); both `MemoryVarStore` (-74 lines) and
`FsVarStore` (-63 lines) now delegate to them while keeping persistence separate.
Refs #47
Rewrite createMemoryStore() to return an OcasStore with three sub-stores:
cas, var, tag. The cas sub-store keeps the existing Map-based logic, but
its put is now synchronous; the in-memory VarStore and TagStore are new
Map-based implementations of the types defined in #39.
The legacy Store.put signature is widened to Hash | Promise<Hash> so that
the new sync cas can still be passed to helpers (bootstrap, gc, render,
schema, …) that have not yet been migrated to the unified surface.
Tests in packages/core were mechanically updated from store.* to
store.cas.* and new test suites for VarStore (var-store.test.ts) and
TagStore (tag-store.test.ts) cover the spec from issue #40.
Closes#40
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>