5.5 KiB
Context
The current genesis bitpacker (packages/primitives/bitpacker/, ~20 files) is a greenfield build providing only low-level BitWriter/BitReader and a minimal defineBitSchema. It lacks the schema-driven packing system, advanced strategies, streaming, delta encoding, and enterprise features that suaveplan's bitpacker (packages/universal/bitpacker/, ~400 files) provides.
Suaveplan's bitpacker is monolithic — everything lives in one package. This creates three problems:
- Bundle bloat: Consumers who only need
BitWriter/BitReaderpull in game-state schemas, network simulation, and crypto checksums. - Slow builds: A change in
game/prediction/kalman-filter.tstriggers rebuilds for all bitpacker consumers. - Poor boundaries: The dependency graph is flat, so there's no clear API contract between core, strategies, and advanced modules.
The workspace glob packages/*/* already supports nested packages, so we can create packages/bitpacker/ as a plugin host without any toolchain changes.
Goals / Non-Goals
Goals:
- Create a
packages/bitpacker/directory that hosts 10 related workspace packages. - Core package (
@suaveplan/bitpacker) stays lightweight: bit I/O, strategy manager, base types. - Every advanced feature is an opt-in plugin with explicit
dependenciesin itspackage.json. - Preserve genesis additions (
zigzag,peek/seek,align) in core. - Downstream consumers (
packer,network-*,storage-*) continue to compile and test green. - Maintain backward compatibility for
defineBitSchemacall sites.
Non-Goals:
- No runtime code generation,
eval, or schema → JS compilation. - No replacement of standalone
BitOperations/ packer APIs (escape hatch stays). - No general expression language for conditional predicates.
- No changes to suaveplan source (this is a genesis-only restructure).
- No attempt to make every plugin tree-shakeable to zero bytes (just package-level separation).
Decisions
1. Package naming: @suaveplan/bitpacker-* for all plugins
Rationale: Keeps the npm namespace flat and discoverable. @suaveplan/bitpacker is the core; every plugin is a sibling name.
Alternative considered: @suaveplan/bitpacker/strategies subpath exports. Rejected because subpaths don't give independent versioning or build caching.
2. Core owns BitWriter, BitReader, PackingStrategyManager, and the BitPacker class
Rationale: The strategy manager is the coordination layer; it must live in core so that plugins can register strategies. The core does NOT own any concrete strategies beyond a minimal InvalidSchemaStrategy catch-all.
3. Strategies plugin owns ALL built-in strategies
Rationale: Even basic strategies (string, number, boolean) are moved out of core so that alternative strategy sets can be swapped in. Core consumers who need the default strategy set import @suaveplan/bitpacker-strategies and call registerDefaultStrategies(manager).
4. Preserve packages/primitives/bitpacker/ temporarily
Rationale: Avoid breaking downstream consumers mid-migration. The old package will be deprecated and removed in a follow-up change after all dependents are updated.
5. Each plugin re-uses suaveplan source structure internally
Rationale: Porting 400 files is error-prone; keeping the same internal directory layout (src/core/, src/packers/, src/schema/, etc.) within each plugin reduces cognitive load.
6. Schema plugin owns the schema registry, validation, and versioning
Rationale: Schema metadata is a cross-cutting concern. Centralizing it in one plugin prevents circular deps between strategies and validation.
7. Error hierarchy: core defines BitpackerError, plugins extend it
Rationale: Core must not depend on @suaveplan/error (to stay lightweight), but plugins can. This is a pragmatic compromise; if @suaveplan/error is lightweight enough we may migrate core later.
Risks / Trade-offs
- [Risk] Downstream consumers break due to import path changes → Mitigation: keep
packages/primitives/bitpacker/as a re-export shim until all dependents are migrated. - [Risk] Circular dependencies between plugins → Mitigation: enforce DAG via CI lint rule. Core → Strategies → Schema → Validation → Streaming/Delta/Game/Network/Crypto/Compressed.
- [Risk] Build complexity increases with 10 packages → Mitigation: turbo pipeline caches are per-package; incremental builds should be faster, not slower.
- [Risk] Plugin API churn during port → Mitigation: mark all plugin public APIs as
@betauntil Phase 4 verification is complete. - [Trade-off] More
package.jsonfiles to maintain → Acceptable; monorepo tooling (bun workspaces, turbo) handles this.
Migration Plan
- Create
packages/bitpacker/bitpacker/and port core frompackages/primitives/bitpacker/+ suaveplan core. - Create remaining 9 plugin packages, porting from suaveplan.
- Update
packages/primitives/bitpacker/to re-export from new core (shim). - Run dependent sweep (
bunx turbo test --filter=...@suaveplan/bitpacker). - Update downstream consumers to import from new paths.
- Remove shim package in follow-up change.
Open Questions
- Should
bitpacker-cryptodepend on@suaveplan/cryptoor stay self-contained with its own checksum implementations? - Should
bitpacker-gamelive inpackages/game/instead ofpackages/bitpacker/? (Leaning toward keeping it underbitpacker/for discoverability.) - Do we need a
bitpacker-testingplugin with shared test fixtures and property-test helpers?