Files
suaveplan/docs/superpowers/specs/2026-04-20-utils-migration-design.md
2026-05-27 23:29:53 +01:00

7.9 KiB

Design: @suaveplan/utils Migration — genesis

Date: 2026-04-20 Branch: genesis Source: @suaveplan/utils from main (/mnt/s/web/suaveplan/packages/universal/utils) Reference: /mnt/s/web/suaveplan/enterprise/utils.md


Context

The old @suaveplan/utils package is a 14-module grab-bag imported by 109 reverse-consumer workspaces. On genesis, we rewrite it fresh at enterprise quality and split it into four packages, each with a clear, single responsibility. Nothing is ported mechanically — every module is rewritten to meet the genesis enterprise checklist.


Decision: Split into 4 packages

Package Domain path Purpose
@suaveplan/utils universal/data/utils Zero-external-dep odds-and-sods utilities
@suaveplan/json universal/data/json Prototype-pollution-safe JSON parse/stringify
@suaveplan/bytes universal/primitives/bytes Byte encoding / format conversion
@suaveplan/locale-data universal/i18n/locale-data ISO country + language static data

Scrapped: string/parseTemplate only — duplicate of @suaveplan/templates which already handles {{var}} interpolation. string/parseTextWithLinks is distinct (link annotation, not template interpolation) and is retained in @suaveplan/utils.


Package 1: @suaveplan/utils

Principle: Zero external deps beyond the genesis standard (zod, @opentelemetry/api, @suaveplan/error, @suaveplan/types). Odds-and-sods utilities that are not worth a dedicated package.

Modules

Module Key exports
debug debug.log/warn/error — dedup-buffered console logger (suppresses repeated messages within a rolling 10-item buffer)
object deepMerge, deepMergeAll, deepClone, pick, omit, flattenObject, getNestedValue, setNestedValue, hasPath, isEmpty, isObject, isPlainObject, isObjectOrArray, keepKeys, removeKeys
platform getPlatform, getMemoryUsage, getCpuUsage, isNode, isBrowser, isBun — injectable platform adapter
number packFloat16/32/64, unpackFloat16/32/64, isInFloat16/32/64Range, min16/32/64, max16/32/64
random pseudoRandom<T>(arr, index, seed?) — seeded deterministic array element selection
email normalizeEmail — Gmail dots/plus, Yahoo dashes, Microsoft plus, generic normalization
sound parseSoundVariations, getSoundKey, convertSprites — audio sprite key parsing + variation selection
string generateId, parseTextWithLinks
validate isHex
validation validateSize, formatSize, parseSize, truncateToSize, isWithinSizeLimit, getDataSize, MessageSizeError, SIZE_LIMITS, DEFAULT_MAX_MESSAGE_SIZE

Dependencies

@suaveplan/error   workspace:^
@suaveplan/types   workspace:^
zod                catalog:zod
@opentelemetry/api catalog:otel-api

Package 2: @suaveplan/json

Principle: Safe, hardened JSON operations. No external deps.

Modules

Module Key exports
safe-json safeParse, safeStringify, tryParse, isValidJSON, getStringifySize, SafeJSONError

Features

  • Prototype pollution guard: rejects __proto__, constructor, prototype keys by default (configurable)
  • Circular reference detection with structured SafeJSONError
  • tryParse returns undefined instead of throwing
  • getStringifySize measures serialised byte length without full allocation
  • All errors extend @suaveplan/error with stable codes (JSON.PARSE_FAILED, JSON.CIRCULAR, JSON.PROTOTYPE_POLLUTION)

Dependencies

@suaveplan/error   workspace:^
@suaveplan/types   workspace:^
zod                catalog:zod
@opentelemetry/api catalog:otel-api

Package 3: @suaveplan/bytes

Principle: Single canonical home for byte encoding and format conversion in the monorepo. @suaveplan/crypto-utils currently duplicates some of this; a follow-up task updates crypto-utils to consume @suaveplan/bytes instead.

Modules

Module Key exports
encoding bytesToHex, hexToBytes, bytesToBase64, base64ToBytes, bytesToUtf8, utf8ToBytes
uint8 concatUint8Arrays, sliceUint8Array, uint8ArraysEqual, inputToUint8Array, detectInputFormat, validateInputTypeFormat, formatOutput, convertDataFormat
uuid uint8ArrayToUuid, uuidToUint8Array, generateUuid — uses crypto.randomUUID() natively; no uuid npm package needed

Features

  • Format auto-detection (detectInputFormat: hex, base64, utf8, Uint8Array)
  • Round-trip guarantees tested via fast-check property tests
  • All errors extend @suaveplan/error with stable codes (BYTES.INVALID_FORMAT, BYTES.ENCODE_FAILED, etc.)

Dependencies

@suaveplan/error   workspace:^
@suaveplan/types   workspace:^
zod                catalog:zod
@opentelemetry/api catalog:otel-api

Follow-up

After @suaveplan/bytes is published, open an OpenSpec change to update @suaveplan/crypto-utils to depend on @suaveplan/bytes and remove the duplicate hex/base64/utf8/bytes/concat encoding utilities. base58 stays in crypto-utils — it is blockchain/crypto-specific (Bitcoin address encoding) and not a general-purpose byte encoding concern; moving it to bytes would introduce a crypto-domain concept into a general primitive.


Package 4: @suaveplan/locale-data

Principle: Static ISO reference data. Too large and domain-specific to be an odds-and-sods utility. Feeds @suaveplan/i18n.

Modules

Module Key exports
countries CountryItem type, per-country named exports (AD, AE, ...), getCountry(code), getAllCountries(), getCountriesByContinent(continent), getCountriesByCurrency(currency)
languages LanguageItem type, getLanguages(), getLanguage(code), getRtlLanguages(), isRtlLanguage(code)

Features

  • All data typed with Zod schemas
  • Lookup helpers with structured errors for unknown codes
  • Zero runtime deps — data is bundled at build time, not fetched

Dependencies

@suaveplan/error   workspace:^
@suaveplan/types   workspace:^
zod                catalog:zod
@opentelemetry/api catalog:otel-api

Build sequence

All four packages share the same dependency set (genesis standard), so they can land in parallel or any order. Recommended sequence:

  1. @suaveplan/bytes — unblocks crypto-utils refactor
  2. @suaveplan/json — self-contained
  3. @suaveplan/locale-data — self-contained; enables i18n to consume it
  4. @suaveplan/utils — self-contained; the catch-all

Enterprise checklist (applies to all four)

  • 100% statement + branch + function + line coverage (Istanbul)
  • 100% docs coverage (bun run docs:check)
  • Every .ts implementation file has a sibling .md (≥200 words, simple + advanced examples, API reference)
  • All public types have Zod schemas in sibling *.schemas.ts
  • All thrown errors extend @suaveplan/error with stable PACKAGE.DOMAIN.REASON codes
  • Injectable clock, rng, signal where applicable
  • AbortSignal on every iterative helper
  • OTel counters + histograms + spans on public entrypoints
  • fast-check property tests for pure functions
  • mitata benchmarks with CI regression gate
  • sideEffects: false; one subpath export per feature module
  • @vitest/coverage-istanbul in devDependencies
  • OpenSpec change created, verified, archived

OpenSpec changes to create

Change ID Scope
enterprise-bytes New @suaveplan/bytes package
enterprise-json New @suaveplan/json package
enterprise-locale-data New @suaveplan/locale-data package
enterprise-utils New @suaveplan/utils package (genesis rewrite)
crypto-utils-bytes-refactor Update @suaveplan/crypto-utils to consume @suaveplan/bytes (follow-up)