UNPKG

@bemedev/app-solid

Version:

Middleware between @bemedev/app-ts and solidjs

186 lines (183 loc) 5.88 kB
import { interpret as interpret$1, decomposeSV } from '@bemedev/app-ts'; import { DEFAULT_DELIMITER } from '@bemedev/app-ts/lib/constants/strings.js'; import { INIT_EVENT } from '@bemedev/app-ts/lib/events/constants.js'; import { merge } from '@bemedev/app-ts/lib/utils/merge.js'; import { createSignal, untrack, createMemo } from 'solid-js'; import { defaultSelector } from './default.js'; import { asyncify } from './helpers.js'; class Interpreter { #status = 'idle'; #machine; #service; #mainState; #options; interpreterOptions; get #setState() { return this.#mainState[1]; } get #state() { return this.#mainState[0]; } get #canPerform() { return this.#status === 'started' || this.#status === 'working'; } get __stateSignal() { return undefined; } #setUI = () => { this.#setState(state => { const out = { ...this.#initialState, ...state, uiThread: Object.entries({ ...this.#options?.uiThread, }).reduce((acc, [key, value]) => { acc[key] = value?.[0](); return acc; }, {}), }; return out; }); }; constructor({ machine, options: interpreterOptions, uiThread, }) { this.#machine = machine; this.interpreterOptions = interpreterOptions; this.#service = interpret$1(this.#machine, this.interpreterOptions); this.subscribe(next => { this.#setState(prev => merge(prev, next)); }); if (uiThread) { if (!this.#options) this.#options = {}; this.#options.uiThread = Object.entries(uiThread).reduce((acc, [key, value]) => { acc[key] = createSignal(value); return acc; }, {}); } const _ui = this.#options?.uiThread; this.#initialState = { context: this.#service.context, status: 'idle', value: this.#service.initialValue, event: INIT_EVENT, uiThread: _ui ? Object.entries(_ui) .map(([key, [signal]]) => [key, signal()]) .reduce((acc, [key, value]) => { acc[key] = value; return acc; }, {}) : undefined, }; this.#mainState = createSignal(this.#initialState); this.#setUI(); } get start() { this.#status = 'started'; return this.#service.start; } get pause() { return this.#service.pause; } get resume() { return this.#service.resume; } stop = () => { this.#status = 'stopped'; this.#service.stop(); untrack(this.#mainState[0]); }; get subscribe() { return this.#service.subscribe; } #initialState; get send() { return this.#service.send; } sendUI = event => { if (!this.#canPerform) return; const fn = this.#options?.uiThread?.[event.type]?.[1]; const out = fn(event.payload); this.#setUI(); return out; }; state = (accessor = defaultSelector, equals) => { return createMemo(() => { const value = this.#state(); return accessor(value); }, this.#initialState, { equals }); }; watcher = (accessor) => { return (equals) => { return this.state(accessor, equals); }; }; reducer = (accessor) => { return (_accessor = defaultSelector, equals) => { return this.state(state => { const step1 = accessor(state); const step2 = _accessor(step1); return step2; }, equals); }; }; context = this.reducer(state => state.context); value = this.watcher(state => state.value); status = this.watcher(state => state.status); tags = this.watcher(state => state.tags ?? []); ui = this.reducer(state => state.uiThread); hasTags = (...tags) => { const currentTags = this.state(({ tags }) => tags)(); if (!currentTags) return false; return tags.every(tag => currentTags.includes(tag)); }; /** * @deprecated * Only for testing purposes */ get __isUsedUi() { const state = this.#options?.uiThread; return !!state && Object.keys(state).length > 0; } dps = this.watcher(({ value }) => decomposeSV .low(value) .map(entry => entry.replaceAll('.', DEFAULT_DELIMITER))); matches = (...values) => { const dps = this.dps()(); return createMemo(() => values.every(value => dps.includes(value))); }; contains = (...values) => { const dps = this.dps()(); return createMemo(() => values.some(value => dps.includes(value))); }; addOptions = option => { this.#options = merge(this.#options, this.#service.addOptions(option)); this.#setUI(); return this.#options; }; provideOptions = (option) => { const instance = new Interpreter({ machine: this.#machine, options: this.interpreterOptions, }); instance.addOptions(option); return instance; }; dispose = () => { this.stop(); this.#service.dispose(); this.#options = undefined; this.#mainState = undefined; this.interpreterOptions = undefined; }; [Symbol.dispose] = this.dispose; [Symbol.asyncDispose] = asyncify(this.dispose); } const createInterpreter = args => { return new Interpreter(args); }; const interpret = createInterpreter; export { createInterpreter, interpret }; //# sourceMappingURL=interpreter.js.map