# @zebec-network/core-utils

Core TypeScript utilities used across the Zebec Network SDK and services. The package collects small, dependency-light helpers for financial-rate conversion, buffer validation, SHA-256 hashing, date comparison, email shape checks, and async timing — the kinds of primitives that get re-implemented (slightly differently) in every package.

It is published as CommonJS with bundled type declarations and targets Node.js.

## Table of contents

- [Installation](#installation)
- [Usage](#usage)
- [API](#api)
  - [`bpsUtils`](#bpsutils)
  - [`bufferUtils`](#bufferutils)
  - [`cryptoUtils`](#cryptoutils)
  - [`dateUtils`](#dateutils)
  - [`emailUtils`](#emailutils)
  - [`timeUtils`](#timeutils)
- [Testing](#testing)
- [Build](#build)
- [License](#license)

## Installation

```bash
npm install @zebec-network/core-utils
```

```bash
pnpm add @zebec-network/core-utils
```

```bash
yarn add @zebec-network/core-utils
```

Requires Node.js (the package uses `node:crypto` and `Buffer`).

## Usage

All helpers are re-exported from the package root:

```ts
import {
  percentToBps,
  bpsToPercent,
  assertBufferSize,
  sha256Hash,
  sha256HashBuffer,
  areDatesOfSameDay,
  isEmailValid,
  sleep,
} from "@zebec-network/core-utils";

const fee = percentToBps("0.25");          // "25"
const digest = sha256Hash("hello");        // "2cf24dba5fb0a30e..."
await sleep(500);                          // pause 500ms
```

## API

### `bpsUtils`

Helpers for converting between percentages and basis points. Both functions are backed by [`bignumber.js`](https://www.npmjs.com/package/bignumber.js) and **floor** (rather than round) any excess precision.

#### `percentToBps(percent: string | number): string`

Converts a percentage to basis points (1% = 100 bps). Returns an integer string.

```ts
percentToBps(1.25);    // "125"
percentToBps("0.5");   // "50"
percentToBps("0.999"); // "99"  (floored)
```

#### `bpsToPercent(bps: string | number | bigint): string`

Converts basis points to a percentage. Returns a string with exactly two decimal places. Accepts `bigint` for values beyond `Number.MAX_SAFE_INTEGER`.

```ts
bpsToPercent(125);    // "1.25"
bpsToPercent("50");   // "0.50"
bpsToPercent(9999n);  // "99.99"
```

### `bufferUtils`

#### `type BufferLike = Buffer | Uint8Array`

#### `assertBufferSize(buffer: BufferLike, expectedSize: number): void`

Throws if `buffer.length !== expectedSize`. Useful as a precondition check before decoding fixed-size byte payloads.

```ts
assertBufferSize(sha256HashBuffer("hello"), 32); // ok
assertBufferSize(Buffer.alloc(16), 32);
// throws: "Buffer size mismatch: expected 32, got 16"
```

### `cryptoUtils`

Synchronous SHA-256 helpers built on `node:crypto`.

#### `sha256Hash(input: string): string`

Returns a 64-character lowercase hex digest.

```ts
sha256Hash("hello");
// "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
```

#### `sha256HashBuffer(input: string): Buffer`

Returns the raw 32-byte digest as a `Buffer`.

```ts
sha256HashBuffer("hello").length; // 32
```

#### Deprecated

The async variants below still work but are kept only for backwards compatibility — prefer the synchronous functions above.

- `hashSHA256(input: string): Promise<string>` — use `sha256Hash` instead.
- `hashSHA256ToBuffer(input: string): Promise<Buffer>` — use `sha256HashBuffer` instead.

### `dateUtils`

#### `areDatesOfSameDay(date1: Date, date2: Date): boolean`

Returns `true` if both dates share the same UTC year, month, and day. Time-of-day and local timezone offsets are ignored.

```ts
areDatesOfSameDay(
  new Date("2026-05-11T01:00:00Z"),
  new Date("2026-05-11T23:59:59Z"),
); // true
```

### `emailUtils`

#### `isEmailValid(value: string): boolean`

Best-effort syntactic check aligned with the HTML5 / WHATWG `<input type="email">` definition. Enforces RFC 5321 length limits (local part ≤ 64, total ≤ 254) and rejects leading/trailing or consecutive dots in the local part.

It does **not** implement the full RFC 5322 grammar (no quoted local parts, no IP-literal or IDN/Unicode domains) and does not confirm the mailbox exists. For authoritative validation, send a confirmation email.

```ts
isEmailValid("user@example.com");      // true
isEmailValid("user.name+tag@host.co"); // true
isEmailValid(".user@example.com");     // false
isEmailValid("a..b@example.com");      // false
```

### `timeUtils`

#### `sleep(ms: number): Promise<unknown>`

Resolves after `ms` milliseconds via `setTimeout`. Useful for delays, back-off, or pacing polling loops.

```ts
console.log("before");
await sleep(1000);
console.log("one second later");
```

## Testing

Tests live in [`test/`](test/) as `*.spec.ts` files and run with [Mocha](https://mochajs.org) via [`ts-mocha`](https://www.npmjs.com/package/ts-mocha).

Run the full suite:

```bash
npm test
```

Run a single spec file:

```bash
npm run test:single -- test/bpsUtils.spec.ts
```

Each module under [`src/`](src/) has a matching spec:

| Module | Spec |
| --- | --- |
| [src/bpsUtils.ts](src/bpsUtils.ts) | [test/bpsUtils.spec.ts](test/bpsUtils.spec.ts) |
| [src/bufferUtils.ts](src/bufferUtils.ts) | [test/bufferUtils.spec.ts](test/bufferUtils.spec.ts) |
| [src/cryptoUtils.ts](src/cryptoUtils.ts) | [test/cryptoUtils.spec.ts](test/cryptoUtils.spec.ts) |
| [src/dateUtils.ts](src/dateUtils.ts) | [test/dateUtils.spec.ts](test/dateUtils.spec.ts) |
| [src/emailUtils.ts](src/emailUtils.ts) | [test/emailUtils.spec.ts](test/emailUtils.spec.ts) |
| [src/timeUtils.ts](src/timeUtils.ts) | [test/timeUtils.spec.ts](test/timeUtils.spec.ts) |

When adding a new helper, drop a sibling `*.spec.ts` next to the existing ones — the glob in `npm test` will pick it up automatically.

## Build

```bash
npm run build
```

Produces CommonJS output and `.d.ts` declaration files in [`dist/`](dist/) (the only directory shipped to npm). `npm run clean` removes the `dist/` directory.

Formatting is handled by [Biome](https://biomejs.dev):

```bash
npm run format
```

## License

[MIT](LICENSE)
