npm package: @fizzwiz/vanilla
Description: Tiny, semantic utilities for working with plain JSON objects
Author: Roberto Venditti
Latest version: 0.0.0-dev.5

# 🍦 @fizzwiz/vanilla

**Lightweight semantics for working with plain JavaScript objects**

`@fizzwiz/vanilla` is a small, dependency‑free library that adds **semantic structure** to everyday JavaScript objects. It focuses on *clarity*, *safety*, and *intent*, providing higher‑level abstractions for configuration storage, deep object navigation, coercion, and validation — while staying very close to plain JSON data and native objects.

Rather than introducing new data models or DSLs, Vanilla works *with* what you already have, making object traversal and input handling more expressive and predictable.

The library is designed to be useful both in:

* **Application code** (configuration, request handling, domain logic)
* **Low‑level tooling** where introducing heavy frameworks would be undesirable

---

## ✨ Features

### OptionStore

* Store options keyed by **types / classes** rather than strings
* Automatically resolve options by walking up the **prototype chain**
* Ideal for defaults, policies, and behavioral configuration in class hierarchies

### ObjNavigator

A semantic *cursor* over an object tree with a unified failure model.

* `get(expr)` — retrieve a value or compute one from the current scope
* `set(path, value)` — assign a value (optionally creating missing nodes)
* `delete(path)` — remove a property
* `select(predicate)` — filter object entries in place
* `coerce(path, fn, opts)` — coerce a value safely
* `validate(expr, predicate, opts)` — validate values with configurable failure behavior
* `within(expr)` / `without()` — scoped navigation across **derived objects**

All operations share a unified **failure model**. Each step may specify how failures are handled using the `opts` argument:

* `onError` — run a callback
* `errorEvent` — emit an event
* `bubbles` — bubble the error to parent navigators
* `throws` — throw immediately
* `payload` — control the emitted / thrown value

Once a navigator enters a failed state, subsequent operations become **no‑ops** unless explicitly handled.

---

## 📦 Installation

### NPM

```bash
npm install @fizzwiz/vanilla
```

### Browser (UMD bundle via jsDelivr)

```html
<script src="https://cdn.jsdelivr.net/npm/@fizzwiz/vanilla/dist/vanilla.bundle.js"></script>
<script>
  const { OptionStore, ObjNavigator } = window.vanilla;

  const nav = ObjNavigator.from({})
    .set('user.profile.name', 'Alice'); 
</script>
```

---

## 🚀 Quick Start

```js
import { OptionStore, ObjNavigator } from '@fizzwiz/vanilla';

// OptionStore example
class Base {}
class Derived extends Base {}

const options = OptionStore.as({});
options.set(Base, 'color', 'blue');
options.get(Derived, 'color'); // 'blue'

// ObjNavigator example
const nav = ObjNavigator.from({})
  .set('user.profile', {})  
  .with('user.profile')    
    .set('name', 'Alice')
    .set('age', 30)
  .without() 
```

---

## 🧭 DOM Navigation Example

`ObjNavigator` is not limited to JSON data. Because navigation steps can be **functions**, it can traverse *derived* or *computed* structures such as the DOM.

### Navigating the DOM with `querySelector`

```html
<div id="app">
  <section class="profile">
    <span class="name">Alice</span>
    <span class="age">30</span>
  </section>
</div>
```

```js

const { ObjNavigator } = window.vanilla;

const name = ObjNavigator.from(document)
  .with(doc => doc.querySelector('#app .profile .name'))
  .get('textContent');

console.log(name); // 'Alice'

```

---

## 🛂 Request / Handler Example (Coerce + Validate)

A common use case is handling **untrusted input** (e.g. HTTP query parameters), where errors should be **reported**, not thrown. In such cases, it is useful to emit an error event or handle the failure immediately through a dedicated callback.

```js
import { ObjNavigator } from '@fizzwiz/vanilla';

function handleRequest(req, res) {

  const nav = ObjNavigator.from({ ...req.query })
    .on('inputError', () => res.status(400).send('Malformed query'))
    .coerce('limit', Number, { errorEvent: 'inputError' })
    .validate('limit', v => Number.isInteger(v) && v > 0, { errorEvent: 'inputError' });

  if (nav.failed) return; // response already sent

  // ✅ Safe, validated input
  // Business logic here
}
```

In this model:

* `coerce()` converts input without throwing
* `validate()` enforces invariants
* Errors are **emitted as events**, not thrown
* Business logic only runs if input is known to be valid

---

## 🧠 Mental Model

* An **ObjNavigator** is a *cursor* over a value
* Each step either:

  * Produces a new value, or
  * Transitions into a **failed** state
* Failure handling is explicit and composable (callbacks, events, bubbling, throws)

This makes navigation, validation, and transformation **linear, readable, and intention‑revealing**.

---

## 🌐 Documentation & Resources

* Blog: [https://vanilla.blog.fizzwiz.cloud](https://vanilla.blog.fizzwiz.cloud)
* GitHub Pages: [https://fizzwiz.github.io/vanilla](https://fizzwiz.github.io/vanilla)

---

## 📄 License

MIT