Files
2026-05-27 23:29:53 +01:00

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:

  1. Bundle bloat: Consumers who only need BitWriter/BitReader pull in game-state schemas, network simulation, and crypto checksums.
  2. Slow builds: A change in game/prediction/kalman-filter.ts triggers rebuilds for all bitpacker consumers.
  3. 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 dependencies in its package.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 defineBitSchema call 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 @beta until Phase 4 verification is complete.
  • [Trade-off] More package.json files to maintain → Acceptable; monorepo tooling (bun workspaces, turbo) handles this.

Migration Plan

  1. Create packages/bitpacker/bitpacker/ and port core from packages/primitives/bitpacker/ + suaveplan core.
  2. Create remaining 9 plugin packages, porting from suaveplan.
  3. Update packages/primitives/bitpacker/ to re-export from new core (shim).
  4. Run dependent sweep (bunx turbo test --filter=...@suaveplan/bitpacker).
  5. Update downstream consumers to import from new paths.
  6. Remove shim package in follow-up change.

Open Questions

  • Should bitpacker-crypto depend on @suaveplan/crypto or stay self-contained with its own checksum implementations?
  • Should bitpacker-game live in packages/game/ instead of packages/bitpacker/? (Leaning toward keeping it under bitpacker/ for discoverability.)
  • Do we need a bitpacker-testing plugin with shared test fixtures and property-test helpers?