# provider-storage

Read a JSON value out of `localStorage` / `sessionStorage` into `provision`,
declaratively — and keep it in sync across tabs. No app JS required to display
cached client state.

## Features

- **Declarative read** Point `key-name` at a storage key and read the result
- **Local or session** `store-name` picks `localStorage` (default) /
  `sessionStorage`
- **Live across tabs** A write from another tab re-reads and fires
  `provider-storage-changed`
- **Reactive to attributes** Changing `key-name` / `store-name` re-reads
  immediately
- **Tiny, read-only** Never writes

## Installation


`@excom/provider-storage` v0.1.4

```bash
pnpm add @excom/provider-storage
```

```bash
npm install @excom/provider-storage
```

```bash
yarn add @excom/provider-storage
```

### Import

```ts
import "@excom/provider-storage";
```



## Usage

```html
<provider-storage key-name="user-preferences"></provider-storage>
<provider-storage key-name="checkout-draft" store-name="session"></provider-storage>
```

**Read-only.** It reads on every `key-name` / `store-name` set/change and never
writes. Removing `key-name` clears `provision` and both states.

**Live across tabs, not within one.** Browsers fire `storage` only in *other*
tabs, so:

- A write from another tab (or a `clear()` there) re-reads and fires
  `provider-storage-changed` — no app code needed. `sessionStorage` is
  per-tab, so this only applies to `store-name="local"`.
- A write by your own app code (`localStorage.setItem(...)`) in the same tab
  won't appear until you re-trigger a read — re-set `key-name` (e.g. to `""`
  and back) after writing.

### API Reference


#### Attributes

| Name | Surface | Type | Default | Values | Description |
| --- | --- | --- | --- | --- | --- |
| `key-name` | option | `string` |  | `<storage key>` | Storage key to read. Setting or changing it re-reads immediately (parsed as JSON; `null` if absent). Removing it clears `provision` and both states. |
| `store-name` | option | `string` | `"local"` | `local, session` | Which Web Storage area to read. `local` survives the tab / browser closing and syncs across tabs; `session` is per-tab and gone when the tab closes. Changing it re-reads `key-name` from the new store. |
| `is-success` | state | `boolean` |  |  | The last read (parse) succeeded — including a legitimately absent key (`provision` is `null`, not an error). |
| `is-error` | state | `boolean` |  |  | `JSON.parse` threw on the last read (the stored value isn't valid JSON). `provision` is `null`. |

#### Provision

| Name | Type | Description |
| --- | --- | --- |
| `provision` | `object` | `JSON.parse(store.getItem(keyName))`, or `null` if the key is absent or the read failed. Not reflected as an attribute. |

#### Fires

| Name | Type | Description |
| --- | --- | --- |
| `provider-storage-changed` | `ProviderStorageChangedEvent` (`CustomEvent & { type: "provider-storage-changed"; detail: { keyName: string; oldValue: unknown; newValue: unknown; }; bubbles: true; cancelable: true; composed: true }`) | Dispatched after a `storage` event from another tab for this element's `store-name` + `key-name` (or a `clear()` of that store) has been re-read into `provision` — so `provision`, `is-success` / `is-error` are already updated when it fires. The read also publishes `neutron-provision`, as usual. |



### Examples

#### Seed & re-read

Since this element never writes, the demo below seeds a value from a button
(re-setting `key-name` afterward to force the same-tab re-read) so you can see
it work without opening devtools. Open this page in a second tab and click
there too — this tab updates on its own.


```html
<div>
  <provider-storage key-name="demo-provider-storage">
    <p>Seeded at: <output></output></p>
  </provider-storage>
  <button type="button">Seed localStorage &amp; re-read</button>
  <small role="note">Same-tab writes only show up after
    <code>key-name</code> is re-set; a write from another tab is picked up
    live.</small>
  <quark-sheet>
    @use "/demo-utils" as *;

    provider-storage {
      $stored: prop("provision");
      output { content: $stored.seededAt or "never"; }
    }
    button { @on click (handle: seedDemoStorage); }
  </quark-sheet>
</div>
```

## Release notes

### 0.1.1 (2026-09-23)

- Ship only dist (and declared extras) in the npm tarball; drop build logs, tests and sources
