collectRefs silently skipped oneOf even though it is in the meta-schema's
allowed keys. uwf step nodes use the standard JSON-Schema idiom
oneOf: [{type:"null"}, {type:"string", format:"ocas_ref"}] for nullable
prev/detail/start refs, so walk() never reached the chain and gc swept
the intermediate steps as false orphans. Mirror the anyOf branch in
collectRefs so every oneOf variant contributes refs.
Also align gc with closure.ts Phase 3: walk @ocas/template/text/<schema>
content for every reachable schema so rendered template nodes survive
when their schema is reachable, and are still collected when the schema
itself is unreachable.
Fixes#93
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>