> ## Documentation Index
> Fetch the complete documentation index at: https://sprincul.sinfullycoded.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Sprincul Static Class: Full API Reference and Examples

> Complete reference for the Sprincul static class: register, registerAll, init, onReady, and the global store API with types and usage examples.

The `Sprincul` class is the entry point for the framework. All methods are static — you never instantiate it directly. You use it to register your model classes, run initialization, listen for the ready event, and interact with the global key/value store. Everything must be registered before you call `Sprincul.init()`.

## Registration

### `Sprincul.register(name, modelClass)`

Registers a single model class under a name. The name you provide here must match the value you set in `data-model` attributes in your HTML. Call this before `Sprincul.init()`.

<ParamField path="name" type="string" required>
  The identifier for this model. Must match the `data-model` attribute value in your HTML.
</ParamField>

<ParamField path="modelClass" type="SprinculModelConstructor" required>
  A class that extends `SprinculModel`. Sprincul calls `new modelClass(element)` for each matching element it finds.
</ParamField>

```js theme={null}
import { Sprincul } from 'sprincul';
import Counter from './Counter.js';

Sprincul.register('Counter', Counter);
Sprincul.init();
```

***

### `Sprincul.registerAll(models)`

Registers multiple model classes at once from a plain object. Each key becomes the `data-model` name for that class. Use this when you have several models to register — it keeps your entry point tidy.

<ParamField path="models" type="Record<string, SprinculModelConstructor>" required>
  An object whose keys are model names and whose values are classes that extend `SprinculModel`.
</ParamField>

```js theme={null}
import { Sprincul } from 'sprincul';
import Counter from './Counter.js';
import UserProfile from './UserProfile.js';
import ShoppingCart from './ShoppingCart.js';

Sprincul.registerAll({
  Counter,
  UserProfile,
  ShoppingCart,
});

Sprincul.init();
```

<Note>
  The object keys are used verbatim as the model names. If you name the key `Counter`, your HTML must use `data-model="Counter"` — casing matters.
</Note>

***

## Initialization

### `Sprincul.init(options?)`

Scans the DOM for all elements with a `[data-model]` attribute, creates a model instance for each one, wires up bindings and event listeners, and runs the lifecycle hooks (`beforeInit`, then `afterInit`) on each instance. Page-level `data-cloaked` elements (those not on a `data-model` element itself) are uncloaked immediately after all `afterInit` hooks are called.

Calling `init()` more than once is safe — elements that have already been initialized are skipped. This means you can inject new markup and call `init()` again to hydrate only the new roots.

<ParamField path="options" type="object">
  Optional configuration object.
</ParamField>

<ParamField path="options.devMode" type="boolean" default="false">
  When `true`, Sprincul logs console warnings for misconfigured bindings and handlers, and includes the live model `instance` on each `SprinculModelInfo` object in the ready callback and `sprincul:ready` event. Disable in production — exposing instances allows console access to internal state.
</ParamField>

```js theme={null}
// Production
Sprincul.init();

// Development
Sprincul.init({ devMode: true });
```

```js theme={null}
// Hydrating dynamically injected markup
document.body.insertAdjacentHTML('beforeend', '<div data-model="Counter">...</div>');
Sprincul.init(); // already-initialized roots are skipped
```

<Warning>
  Model registrations and `onReady` callbacks must be set up **before** calling `Sprincul.init()`. Anything registered after `init()` runs will not be picked up in that cycle.
</Warning>

***

### `Sprincul.onReady(callback)`

Registers a function to be called once all models' `afterInit` hooks have been invoked. Use this to run logic that depends on the entire page being wired up — for example, reading initial model state or connecting cross-model integrations.

`onReady` callbacks are **one-shot per init cycle**. After they fire, the internal list is cleared. If you call `Sprincul.init()` again (for example, after injecting new markup), you must re-register any callbacks you want to run in the new cycle.

<ParamField path="callback" type="(models: SprinculModelInfo[]) => void" required>
  A function that receives an array of `SprinculModelInfo` objects — one for each model root that was initialized in this cycle.
</ParamField>

```js theme={null}
import { Sprincul } from 'sprincul';
import Counter from './Counter.js';

Sprincul.register('Counter', Counter);

Sprincul.onReady((models) => {
  console.log(`Initialized ${models.length} model(s)`);
  models.forEach(({ name, element }) => {
    console.log(`Model "${name}" on:`, element);
  });
});

Sprincul.init();
```

In `devMode`, each item in the array also contains `instance`:

```js theme={null}
Sprincul.onReady((models) => {
  models.forEach(({ name, element, instance }) => {
    console.log(`"${name}" instance:`, instance);
  });
});

Sprincul.init({ devMode: true });
```

You can also listen for the `sprincul:ready` DOM event directly if you prefer not to use the helper:

```js theme={null}
document.addEventListener('sprincul:ready', ({ detail }) => {
  const { models } = detail;
  console.log(`Initialized ${models.length} model(s)`);
});
```

***

## Global store

`Sprincul.store` is a lightweight, reactive key/value store shared across all model instances. You can seed it before `init()`, read it from inside models, and subscribe to changes anywhere on the page. It uses [nanostores](https://github.com/nanostores/nanostores) atoms under the hood, so updates are efficient and subscription callbacks fire synchronously after each change.

### `Sprincul.store.get<T>(key)`

Returns the current value for a key, or `undefined` if no value has been set.

<ParamField path="key" type="string" required>
  The store key to read.
</ParamField>

```js theme={null}
const theme = Sprincul.store.get('theme'); // 'dark' | undefined
```

***

### `Sprincul.store.set<T>(key, value)`

Sets a value for a key. Creates the entry if it does not exist yet. All active subscribers for this key are notified immediately.

<ParamField path="key" type="string" required>
  The store key to write.
</ParamField>

<ParamField path="value" type="T" required>
  The value to store. Can be any serializable type.
</ParamField>

```js theme={null}
Sprincul.store.set('theme', 'dark');
Sprincul.store.set('currentUser', { id: 1, name: 'Jane Doe' });
```

***

### `Sprincul.store.subscribe<T>(key, callback)`

Subscribes to changes for a key. The callback fires **after** a value is set — it does not fire with the current value at subscription time. To read the current value immediately, call `store.get()` first or seed a default with `store.set()`.

Returns an unsubscribe function. Call it to stop receiving updates.

<ParamField path="key" type="string" required>
  The store key to watch.
</ParamField>

<ParamField path="callback" type="(value: T | undefined) => void" required>
  Called with the new value whenever the key is updated. Receives `undefined` if the key is cleared.
</ParamField>

```js theme={null}
// Read current value first, then subscribe to future changes
const currentTheme = Sprincul.store.get('theme') ?? 'light';
applyTheme(currentTheme);

const unsubscribe = Sprincul.store.subscribe('theme', (value) => {
  applyTheme(value ?? 'light');
});

// Later, when you no longer need updates:
unsubscribe();
```

***

### `Sprincul.store.clear()`

Removes all keys and their subscribers from the store. Useful in tests or when tearing down a page section entirely.

```js theme={null}
Sprincul.store.clear();
```

***

## Exported types

These types are exported from the `sprincul` package and available for use in TypeScript projects.

### `SprinculModelInfo`

Describes a single model instance returned by `onReady` callbacks and the `sprincul:ready` event.

<ResponseField name="name" type="string" required>
  The model name as registered with `Sprincul.register()` or `Sprincul.registerAll()`.
</ResponseField>

<ResponseField name="element" type="HTMLElement" required>
  The DOM element marked with `data-model`.
</ResponseField>

<ResponseField name="instance" type="SprinculModel">
  The live model instance. Only present when `devMode: true` is passed to `Sprincul.init()`.
</ResponseField>

```ts theme={null}
import type { SprinculModelInfo } from 'sprincul';

Sprincul.onReady((models: SprinculModelInfo[]) => {
  models.forEach(({ name, element, instance }) => {
    console.log(name, element, instance);
  });
});
```

***

### `SprinculModelConstructor`

The constructor signature that `Sprincul.register()` and `Sprincul.registerAll()` expect. Any class that extends `SprinculModel` satisfies this type automatically.

```ts theme={null}
import type { SprinculModelConstructor } from 'sprincul';

// Equivalent to: new (element: HTMLElement) => SprinculModel
const registry: Record<string, SprinculModelConstructor> = {
  Counter,
  Profile,
};

Sprincul.registerAll(registry);
```
