Browse documentation

Utilities

Standalone helpers exported from @comwit/state for working with reactive proxies and inspecting state during development.

import { snapshot, isProxy, initDevTools } from '@comwit/state'

snapshot()

function snapshot<T extends object>(state: T): T

Returns a detached snapshot of a comwit reactive proxy. Later store mutations do not change it. Nested reactive objects are snapshotted recursively; properties are non-writable and objects are non-extensible. Treat the result as read-only and use ordinary actions to change state.

state must be a proxy (a model proxy or a nested proxy slice). Passing a plain object throws Error('Please use proxy object').

import { snapshot } from '@comwit/state'

const plain = snapshot(this.model.filter)

Top-level vs nested slices

The top-level model proxy also exposes a .snapshot() method, so for the whole model you can call it directly:

await savePostAction(this.model.snapshot())

For nested slices (e.g. state.filter, state.list.data), use the standalone snapshot() helper. It is the type-safe path — .snapshot() is only typed on the top-level proxy so that plain assignments like state.user = userFromApi keep working:

import { snapshot } from '@comwit/state'

await fetchCoders({ filter: snapshot(this.model.filter) })

This is the recommended way to convert proxies to plain objects before passing them to server actions or other APIs that require serializable data.

isProxy()

function isProxy(value: unknown): boolean

Returns true if value is a comwit reactive proxy — either a top-level model proxy or a nested proxy slice. Returns false for plain objects, primitives, and snapshots.

Useful for boundary code that must convert proxies to plain objects before serialization (server actions, logging, postMessage):

import { isProxy, snapshot } from '@comwit/state'

function toPlain<T extends object>(value: T): T {
  return isProxy(value) ? snapshot(value) : value
}

initDevTools()

function initDevTools(options?: { maxLogSize?: number }): ComwitDevTools | undefined

Initializes the in-memory devtools instance and stores it on globalThis.__COMWIT_DEVTOOLS__. It tracks registered stores, captures state-change and action events, and keeps a rolling action log.

  • options.maxLogSize — maximum number of action log entries kept (default 50). Older entries are dropped once the limit is exceeded.
  • Returns the existing instance if one was already initialized (idempotent).
  • Returns undefined and does nothing when process.env.NODE_ENV === 'production'.
import { initDevTools } from '@comwit/state'

if (process.env.NODE_ENV !== 'production') {
  initDevTools({ maxLogSize: 100 })
}

The returned ComwitDevTools object exposes:

  • storesMap of model key to { model, entry, unsubscribe }.
  • getStoreSnapshot(model){ state, subscriberCount } for a model, or null if not registered. Note: subscriberCount currently reports the total number of registered stores, not the subscriber count for that specific model.
  • actionLog — array of recent action entries ({ name, args, result?, error?, timestamp }).
  • subscribe(type, handler) — listen for 'state-change', 'action', or 'query' events; returns an unsubscribe function.