# @suaveplan/utils — Specification **Package:** `@suaveplan/utils` **Version at graduation:** `0.1.0` **Layer:** Universal — zero browser/React/Node-specific APIs **Published:** 2026-04-20 --- ## Purpose Zero-external-dependency utility library covering debugging, object manipulation, platform detection, IEEE 754 float packing, pseudo-random selection, email normalization, audio sprite management, string utilities, hex validation, and byte-size enforcement. --- ## Modules ### `./errors` - `UtilsError` — typed `SuaveplanError` subclass - `UTILS_CODES` — stable code registry keyed by dot-path - `UtilsCode` — union type of all code values ### `./debug` - `assertDefined(val, message?)` — throws if val is null/undefined - `invariant(condition, message?)` — throws if condition is false - `unreachable(message?)` — always throws (exhaustiveness check) - `assert(condition, message?)` — alias for invariant ### `./object` - `isObject(value)` / `isPlainObject(value)` / `isObjectOrArray(value)` / `isEmpty(obj)` - `deepMerge(a, b, skipUndefined?)` / `deepMergeAll(...objects)` - `pick(obj, keys)` / `omit(obj, keys)` / `keepKeys` / `removeKeys` - `flattenObject(obj, prefix?, separator?)` - `getNestedValue(obj, path, default?)` / `setNestedValue(obj, path, value)` / `hasPath(obj, path)` - `deepClone(obj)` ### `./platform` - `isBrowser()` / `isNode()` / `isBun()` / `isEdge()` - `isSSR()` / `supportsWebWorkers()` / `supportsServiceWorkers()` - `getPlatform()` — returns `"browser" | "node" | "bun" | "edge" | "unknown"` ### `./number` - Constants: `min16`, `max16`, `min32`, `max32`, `min64`, `max64` - `packFloat16(v)` / `unpackFloat16(buf)` — IEEE 754 half-precision - `packFloat32(v)` / `unpackFloat32(buf)` — IEEE 754 single-precision - `packFloat64(v)` / `unpackFloat64(buf)` — IEEE 754 double-precision - `isInFloat16Range(v)` / `isInFloat32Range(v)` / `isInFloat64Range(v)` ### `./random` - `pseudoRandom(arr, index, seed?, opts?)` — deterministic element selection ### `./email` - `normalizeEmail(email)` — provider-aware canonicalization (Gmail/Yahoo/Microsoft/generic) ### `./sound` - `parseSoundVariations(sprites)` — group `_` keys by base name - `getSoundKey(name, variations)` — random variation selection; throws `SOUND.NO_VARIATIONS` - `convertSprites(sprites)` — alias for `parseSoundVariations` - Types: `SoundSprites`, `SoundVariations` ### `./string` - `generateId(opts?)` — random hex identifier via `crypto.randomUUID()` - `parseTextWithLinks(text)` — extract HTTP/HTTPS URLs → `TextLink[]` - Types: `GenerateIdOptions`, `TextLink` ### `./validate` - `isHex(i)` — type-guard: non-empty, even-length, hex-only string ### `./validation` - Constants: `SIZE_LIMITS`, `DEFAULT_MAX_MESSAGE_SIZE` - `getDataSize(data)` — UTF-8 bytes for string, byteLength for binary, 8 for number - `validateSize(data, maxSize?)` — throws `VALIDATION.SIZE_EXCEEDED` - `isWithinSizeLimit(data, maxSize?)` — non-throwing boolean - `formatSize(bytes, decimals?)` — `"1.50 KB"` format - `parseSize(str)` — `"1.5MB"` → integer bytes; throws `VALIDATION.PARSE_FAILED` - `truncateToSize(data, maxSize)` — binary-search string truncation --- ## Error Codes | Code | Description | |------|-------------| | `UTILS/OBJECT.PROTOTYPE_POLLUTION` | `__proto__` / constructor key detected | | `UTILS/OBJECT.CIRCULAR` | Circular reference in deepClone | | `UTILS/NUMBER.OUT_OF_RANGE` | Float value outside target precision range | | `UTILS/RANDOM.EMPTY_ARRAY` | pseudoRandom called with empty array | | `UTILS/RANDOM.INDEX_OUT_OF_BOUNDS` | index >= array.length | | `UTILS/VALIDATION.SIZE_EXCEEDED` | Data exceeds maxSize bytes | | `UTILS/VALIDATION.PARSE_FAILED` | Invalid size string format | | `UTILS/PLATFORM.UNAVAILABLE` | Requested platform feature unavailable | | `UTILS/SOUND.NO_VARIATIONS` | getSoundKey called for unknown sound name | --- ## Quality Gates (at ship) - Istanbul coverage: 100% statements / branches / functions / lines - Docs coverage: 100% (12/12 files) - Lint: clean (biome, 0 errors) - TypeScript: clean (noEmit) - Tests: 169 passing