mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
build(spf): build26 from 45504a2b
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
import { Machine, MachineSnapshot } from "../machine.js";
|
||||
//#region src/core/reactors/create-machine-reactor.d.ts
|
||||
/**
|
||||
* A reactive state-deriving function used in the `monitor` field.
|
||||
*
|
||||
* Returns the target state the reactor should be in. Any signals read inside
|
||||
* the fn body create reactive dependencies — the framework re-evaluates it when
|
||||
* those signals change and automatically calls `transition()` when the returned
|
||||
* state differs from the current one.
|
||||
*/
|
||||
type ReactorDeriveFn<State extends string> = () => State;
|
||||
/**
|
||||
* An effect function used in reactor `entry` and `effects` blocks.
|
||||
*
|
||||
* May return a cleanup function that runs before each re-evaluation and on
|
||||
* state exit (including destroy).
|
||||
*/
|
||||
type ReactorEffectFn = () => (() => void) | {
|
||||
abort(): void;
|
||||
} | void;
|
||||
/**
|
||||
* Per-state effect grouping for a single reactor state.
|
||||
*
|
||||
* - `entry` effects run once on state entry. The fn body is automatically
|
||||
* untracked — no `untrack()` calls are needed inside. Use this for
|
||||
* one-time setup: reading current values, attaching event listeners, etc.
|
||||
* - `effects` run on state entry and re-run whenever a signal read inside
|
||||
* the fn body changes. Use `untrack()` for reads you do not want to track.
|
||||
* Use this for work that must stay in sync with reactive state.
|
||||
*
|
||||
* Both are optional; pass `{}` for states with no effects.
|
||||
*/
|
||||
type ReactorStateDefinition = {
|
||||
entry?: ReactorEffectFn | ReactorEffectFn[];
|
||||
effects?: ReactorEffectFn | ReactorEffectFn[];
|
||||
};
|
||||
/**
|
||||
* Full reactor definition passed to `createMachineReactor`.
|
||||
*
|
||||
* `State` is the set of domain-meaningful states. `'destroying'` and
|
||||
* `'destroyed'` are always added by the framework as implicit terminal states —
|
||||
* do not include them here.
|
||||
*/
|
||||
type ReactorDefinition<State extends string> = {
|
||||
/** Initial state. */
|
||||
initial: State;
|
||||
/**
|
||||
* Reactive state derivation. Registered before per-state effects — the
|
||||
* ordering guarantee ensures transitions fired here take effect before
|
||||
* per-state effects re-evaluate in the same flush.
|
||||
*/
|
||||
monitor?: ReactorDeriveFn<State> | ReactorDeriveFn<State>[];
|
||||
/**
|
||||
* Per-state effect groupings. Every valid state must be declared — pass `{}`
|
||||
* for states with no effects. `entry` and `effects` each become independent
|
||||
* `effect()` calls gated on that state, with their own cleanup lifecycles.
|
||||
*/
|
||||
states: Record<State, ReactorStateDefinition>;
|
||||
};
|
||||
/** Live reactor instance returned by `createMachineReactor`. */
|
||||
type Reactor<State extends string> = Machine<MachineSnapshot<State>>;
|
||||
/**
|
||||
* Creates a reactive Reactor from a declarative definition.
|
||||
*
|
||||
* A Reactor is driven by subscriptions to external signals rather than
|
||||
* imperative messages. Each state holds an array of effect functions —
|
||||
* every element becomes one independent `effect()` call gated on that state,
|
||||
* with its own dependency tracking and cleanup lifecycle.
|
||||
*
|
||||
* `'destroying'` and `'destroyed'` are always implicit terminal states.
|
||||
* `destroy()` transitions through both in sequence: `'destroying'` first (for
|
||||
* potential async teardown in a future extension), then immediately `'destroyed'`
|
||||
* for the synchronous base case. Active effect cleanups fire via disposal.
|
||||
*
|
||||
* @example
|
||||
* const reactor = createMachineReactor({
|
||||
* initial: 'waiting',
|
||||
* monitor: () => srcSignal.get() ? 'active' : 'waiting',
|
||||
* states: {
|
||||
* active: {
|
||||
* // entry: runs once on state entry; fn body is automatically untracked.
|
||||
* entry: () => listen(el, 'play', handler),
|
||||
* // effects: re-runs whenever tracked signals change.
|
||||
* effects: () => { currentTimeSignal.get(); return cleanup; },
|
||||
* },
|
||||
* waiting: {},
|
||||
* }
|
||||
* });
|
||||
*/
|
||||
declare function createMachineReactor<State extends string>(def: ReactorDefinition<State>): Reactor<State | 'destroying' | 'destroyed'>;
|
||||
//#endregion
|
||||
export { Reactor, ReactorDefinition, ReactorDeriveFn, ReactorEffectFn, ReactorStateDefinition, createMachineReactor };
|
||||
//# sourceMappingURL=create-machine-reactor.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"create-machine-reactor.d.ts","names":[],"sources":["../../../../src/core/reactors/create-machine-reactor.ts"],"mappings":";;;;;;;;;;KAiBY,gBAAgB,8BAA8B;;;;;;;KAQ9C;EAAyC;;;;;;;;;;;;;;KAczC;EACV,QAAQ,kBAAkB;EAC1B,UAAU,kBAAkB;;;;;;;;;KAUlB,kBAAkB;;EAE5B,SAAS;;;;;;EAMT,UAAU,gBAAgB,SAAS,gBAAgB;;;;;;EAMnD,QAAQ,OAAO,OAAO;;;KAQZ,QAAQ,wBAAwB,QAAQ,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAwCpD,qBAAqB,sBACnC,KAAK,kBAAkB,SACtB,QAAQ"}
|
||||
@@ -0,0 +1,83 @@
|
||||
import { untrack } from "../signals/primitives.js";
|
||||
import { effect } from "../signals/effect.js";
|
||||
import { createMachineCore } from "../machine.js";
|
||||
//#region src/core/reactors/create-machine-reactor.ts
|
||||
const toArray = (x) => x === void 0 ? [] : Array.isArray(x) ? x : [x];
|
||||
/**
|
||||
* Creates a reactive Reactor from a declarative definition.
|
||||
*
|
||||
* A Reactor is driven by subscriptions to external signals rather than
|
||||
* imperative messages. Each state holds an array of effect functions —
|
||||
* every element becomes one independent `effect()` call gated on that state,
|
||||
* with its own dependency tracking and cleanup lifecycle.
|
||||
*
|
||||
* `'destroying'` and `'destroyed'` are always implicit terminal states.
|
||||
* `destroy()` transitions through both in sequence: `'destroying'` first (for
|
||||
* potential async teardown in a future extension), then immediately `'destroyed'`
|
||||
* for the synchronous base case. Active effect cleanups fire via disposal.
|
||||
*
|
||||
* @example
|
||||
* const reactor = createMachineReactor({
|
||||
* initial: 'waiting',
|
||||
* monitor: () => srcSignal.get() ? 'active' : 'waiting',
|
||||
* states: {
|
||||
* active: {
|
||||
* // entry: runs once on state entry; fn body is automatically untracked.
|
||||
* entry: () => listen(el, 'play', handler),
|
||||
* // effects: re-runs whenever tracked signals change.
|
||||
* effects: () => { currentTimeSignal.get(); return cleanup; },
|
||||
* },
|
||||
* waiting: {},
|
||||
* }
|
||||
* });
|
||||
*/
|
||||
function createMachineReactor(def) {
|
||||
const { snapshotSignal, getState, transition } = createMachineCore({ value: def.initial });
|
||||
const effectDisposals = [];
|
||||
const wrapResult = (result) => {
|
||||
if (!result) return void 0;
|
||||
if (typeof result === "function") return result;
|
||||
return () => result.abort();
|
||||
};
|
||||
const untracked = (baseCall) => () => untrack(baseCall);
|
||||
const isTerminal = (snapshot) => snapshot.value === "destroying" || snapshot.value === "destroyed";
|
||||
const descriptors = [...toArray(def.monitor).map((fn) => ({
|
||||
fn: () => {
|
||||
const target = fn();
|
||||
if (target !== getState()) transition(target);
|
||||
},
|
||||
shouldSkip: isTerminal
|
||||
})), ...Object.entries(def.states).flatMap(([state, stateDef]) => {
|
||||
const isNotState = (snapshot) => snapshot.value !== state;
|
||||
return [...toArray(stateDef.entry).map((fn) => ({
|
||||
fn,
|
||||
shouldSkip: isNotState,
|
||||
toFnCall: untracked
|
||||
})), ...toArray(stateDef.effects).map((fn) => ({
|
||||
fn,
|
||||
shouldSkip: isNotState
|
||||
}))];
|
||||
})];
|
||||
const toEffect = ({ fn, shouldSkip, toFnCall = (baseCall) => baseCall }) => effect(() => {
|
||||
if (shouldSkip(snapshotSignal.get())) return;
|
||||
const baseCall = () => fn();
|
||||
return wrapResult(toFnCall(baseCall)());
|
||||
});
|
||||
effectDisposals.push(...descriptors.map(toEffect));
|
||||
return {
|
||||
get snapshot() {
|
||||
return snapshotSignal;
|
||||
},
|
||||
destroy() {
|
||||
const state = getState();
|
||||
if (state === "destroying" || state === "destroyed") return;
|
||||
transition("destroying");
|
||||
transition("destroyed");
|
||||
for (const dispose of effectDisposals) dispose();
|
||||
}
|
||||
};
|
||||
}
|
||||
//#endregion
|
||||
export { createMachineReactor };
|
||||
|
||||
//# sourceMappingURL=create-machine-reactor.js.map
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user