78 lines
3.4 KiB
Markdown
78 lines
3.4 KiB
Markdown
# Smartstate Implementation Notes
|
|
|
|
## Current API (as of v2.0.31)
|
|
|
|
### State Part Initialization
|
|
- State parts can be created with different init modes: 'soft' (default), 'mandatory', 'force', 'persistent'
|
|
- 'soft' - returns existing state part if exists, creates new if not
|
|
- 'mandatory' - requires state part to not exist, fails if it does
|
|
- 'force' - always creates new state part, overwriting any existing
|
|
- 'persistent' - like 'soft' but with WebStore persistence (IndexedDB)
|
|
- Persistent mode automatically calls init() internally
|
|
- State merge order fixed: initial state takes precedence over stored state
|
|
|
|
### Actions
|
|
- Actions are created with `createAction()` method
|
|
- Two ways to dispatch: `stateAction.trigger(payload)` or `statePart.dispatchAction(stateAction, payload)`
|
|
- Both return Promise<TStatePayload>
|
|
|
|
### State Management Methods
|
|
- `select(fn?, { signal? })` - returns Observable, memoized by selector fn ref, supports AbortSignal
|
|
- `waitUntilPresent(fn?, number | { timeoutMs?, signal? })` - waits for state condition, backward compat with number arg
|
|
- `stateSetup()` - async state initialization with cumulative defer
|
|
- `notifyChangeCumulative()` - defers notification to end of call stack
|
|
- `getState()` - returns current state or undefined
|
|
- `setState()` - runs middleware, validates, persists, notifies
|
|
- `addMiddleware(fn)` - intercepts setState, returns removal function
|
|
|
|
### Middleware
|
|
- Type: `(newState, oldState) => newState | Promise<newState>`
|
|
- Runs sequentially in insertion order before validation/persistence
|
|
- Throw to reject state changes (atomic — state unchanged on error)
|
|
- Does NOT run during initial createStatePart() hydration
|
|
|
|
### Selector Memoization
|
|
- Uses WeakMap<Function, Observable> for fn-keyed cache
|
|
- `defaultSelectObservable` for no-arg select()
|
|
- Wrapped in `shareReplay({ bufferSize: 1, refCount: true })`
|
|
- NOT cached when AbortSignal is provided
|
|
|
|
### Batch Updates
|
|
- `smartstate.batch(async () => {...})` — defers notifications until batch completes
|
|
- Supports nesting — only flushes at outermost level
|
|
- StatePart has `smartstateRef` set by `createStatePart()` for batch awareness
|
|
- State parts created via `new StatePart()` directly work without batching
|
|
|
|
### Computed State
|
|
- `computed(sources, fn)` — standalone function using `combineLatest` + `map`
|
|
- Also available as `smartstate.computed(sources, fn)`
|
|
- Lazy — only subscribes when subscribed to
|
|
|
|
### Context Protocol Bridge
|
|
- `attachContextProvider(element, { context, statePart, selectorFn? })` — returns cleanup fn
|
|
- Listens for `context-request` CustomEvent on element
|
|
- Supports one-shot and subscription modes
|
|
- Works with Lit @consume(), FAST, or any Context Protocol consumer
|
|
|
|
### State Hash Detection
|
|
- Uses SHA256 hash to detect actual state changes
|
|
- Hash comparison properly awaits async hash calculation
|
|
- Prevents duplicate notifications for identical state values
|
|
|
|
### State Validation
|
|
- Basic validation ensures state is not null/undefined
|
|
- `validateState()` can be overridden in subclasses
|
|
|
|
### Key Notes
|
|
- `smartstateRef` creates circular ref between StatePart and Smartstate
|
|
- Use `===` not deep equality for StatePart comparison in tests
|
|
- Direct rxjs imports used for: Observable, shareReplay, takeUntil, combineLatest, map
|
|
|
|
## Dependency Versions (v2.0.31)
|
|
- @git.zone/tsbuild: ^4.1.2
|
|
- @git.zone/tsbundle: ^2.9.0
|
|
- @git.zone/tsrun: ^2.0.1
|
|
- @git.zone/tstest: ^3.1.8
|
|
- @push.rocks/smartjson: ^6.0.0
|
|
- @types/node: ^25.3.2
|