# Data Processing Gotchas

Non-obvious behavior and pitfalls across puffy-core's data handling modules: `db`, `date`, `crypto`, `collection`, `obj`, `math`, and `converter`.

## Table of Contents
- [newId Returns BigInt](#newid-returns-bigint)
- [addYears Uses Deprecated setYear](#addyears-uses-deprecated-setyear)
- [encoder Is Multi-Level Curried](#encoder-is-multi-level-curried)
- [formatDate Replacement Order Matters](#formatdate-replacement-order-matters)
- [flattenUniq Dedup Only Works for Primitives](#flattenuniq-dedup-only-works-for-primitives)
- [same Array Comparison Is Quadratic](#same-array-comparison-is-quadratic)
- [getRandomNumber Single Arg Is Upper Bound](#getrandomnumber-single-arg-is-upper-bound)
- [Object Converters Skip Date Instances](#object-converters-skip-date-instances)

## newId Returns BigInt

`newId()` from `puffy-core/db` returns a `BigInt`, not a number or string. Standard `JSON.stringify` throws on BigInt values. Use `safeStringify` from `puffy-core/string` instead.

```js
import { newId } from 'puffy-core/db'
import { safeStringify } from 'puffy-core/string'

const id = newId() // 1737111960607002926n (BigInt)

JSON.stringify({ id })        // TypeError: Do not know how to serialize a BigInt
safeStringify({ id })         // '{"id":"1737111960607002926"}'
```

## addYears Uses Deprecated setYear

`addYears()` internally uses the deprecated `Date.prototype.setYear()` method instead of `setFullYear()`. In practice this still works, but it may behave unexpectedly with years before 1900 or after 9999 in some engines, since `setYear()` interprets two-digit years as 1900-based offsets.

## encoder Is Multi-Level Curried

The `encoder()` function from `puffy-core/crypto` requires **three** stages of calls to produce a result. Forgetting a stage returns a function instead of the encoded value.

```js
import { encoder } from 'puffy-core/crypto'

// Correct: all three stages
const result = encoder()('base64')('hello world') // 'aGVsbG8gd29ybGQ='

// Common mistake: missing a stage
const oops = encoder('base64')('hello world') // Returns a FUNCTION, not a string
// encoder('base64') returns "from base64" encoder
// encoder('base64')('hex') returns the actual conversion function
// encoder('base64')('hex')('aGVsbG8gd29ybGQ=') returns '68656C6C6F20776F726C64'
```

The stages are: `encoder(fromEncoding)(toEncoding)(data)`.

## formatDate Replacement Order Matters

`formatDate()` replaces format tokens by scanning the format string sequentially. Internally it processes `yyyy` before `yy` and `MM` (month) before `MMM` (month name). This means custom format strings work correctly, but be aware that partial tokens could cause unexpected results if the internal ordering ever changes.

## flattenUniq Dedup Only Works for Primitives

`flattenUniq()` uses `Set` for deduplication, which means it only removes duplicate **primitives** (numbers, strings, booleans). Object references are compared by identity, not by value.

```js
import { flattenUniq } from 'puffy-core/collection'

flattenUniq([1, 1], [2, 2])                    // [1, 2] - works
flattenUniq([{ a: 1 }], [{ a: 1 }])            // [{ a: 1 }, { a: 1 }] - not deduped
```

Use `uniq()` with an identity function if you need value-based deduplication for objects.

## same Array Comparison Is Quadratic

`same(arr1, arr2)` from `puffy-core/obj` compares arrays by checking every element in `arr1` against every element in `arr2` (O(n^2)). This is because it treats arrays as unordered sets — `[1, 2]` and `[2, 1]` are considered equal. Avoid using it on large arrays.

## getRandomNumber Single Arg Is Upper Bound

When `getRandomNumber()` receives only a `start` option (no `end`), the range becomes `[0, start)` — the `start` value acts as the **upper bound**, not the lower bound.

```js
import { getRandomNumber } from 'puffy-core/math'

getRandomNumber({ start: 100 })           // Random int in [0, 100)
getRandomNumber({ start: 100, end: 200 }) // Random int in [100, 200)
```

## Object Converters Skip Date Instances

The recursive object key converters (`objectC2Scase`, `objectS2Ccase`, `objectCapital2Ccase`) from `puffy-core/converter` and the `merge`/`diff` functions from `puffy-core/obj` all skip `Date` instances during recursion. Dates are treated as leaf values and passed through unchanged. This is intentional — without this check, the converter would attempt to recurse into Date's internal properties.
