build(spf): build26 from 45504a2b

This commit is contained in:
publish
2026-08-05 18:57:39 +02:00
commit 8920135306
424 changed files with 17907 additions and 0 deletions
+26
View File
@@ -0,0 +1,26 @@
import { Machine, MachineSnapshot } from "../machine.js";
//#region src/core/actors/actor.d.ts
/**
* Complete actor snapshot: finite state + non-finite context.
* Extends `MachineSnapshot` with context — the non-finite data managed by the actor.
*/
interface ActorSnapshot<State extends string, Context extends object> extends MachineSnapshot<State> {
context: Context;
}
/** Generic actor interface: owns its snapshot as a reactive signal. */
interface SignalActor<State extends string, Context extends object> extends Machine<ActorSnapshot<State, Context>> {}
/**
* A message-driven actor with no reactive snapshot.
*
* Use for actors that coordinate async work but have no state that external
* consumers need to observe. Analogous to XState's `fromCallback`.
*/
interface CallbackActor<Message extends {
type: string;
}> {
send(message: Message): void;
destroy(): void;
}
//#endregion
export { ActorSnapshot, CallbackActor, SignalActor };
//# sourceMappingURL=actor.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"actor.d.ts","names":[],"sources":["../../../../src/core/actors/actor.ts"],"mappings":";;;;;;UAeiB,cAAc,sBAAsB,gCAAgC,gBAAgB;EACnG,SAAS;;;UAIM,YAAY,sBAAsB,gCACzC,QAAQ,cAAc,OAAO;;;;;;;UAQtB,cAAc;EAAkB;;EAC/C,KAAK,SAAS;EACd"}
+128
View File
@@ -0,0 +1,128 @@
import { TaskLike } from "../tasks/task.js";
import { SignalActor } from "./actor.js";
//#region src/core/actors/create-machine-actor.d.ts
/**
* Minimal interface for any runner that can be used with createMachineActor.
*/
interface RunnerLike {
schedule<Value = void, Err = unknown>(task: TaskLike<Value, Err>): Promise<Value>;
abortAll(): void;
destroy(): void;
whenSettled(callback: () => void): void;
}
/**
* Context passed to message handlers.
* `runner` is present and typed as the exact runner instance only when the
* definition includes a runner factory.
*/
type HandlerContext<UserState extends string, Context extends object, RunnerFactory extends (() => RunnerLike) | undefined> = {
transition: (to: UserState) => void;
/** Context snapshot captured at dispatch time. Stale after any `setContext` call. */
context: Context;
/**
* Live untracked read of the current context. Use in async task closures that
* execute after the handler returns — e.g. `getCtx: getContext` passed to tasks
* scheduled on the runner, so each task reads the context committed by the
* previous task rather than the stale snapshot from dispatch time.
*/
getContext: () => Context;
setContext: (next: Context) => void;
} & (RunnerFactory extends (() => infer R) ? {
runner: R;
} : object);
/**
* Definition for a single user-defined state.
*/
type ActorStateDefinition<UserState extends string, Context extends object, Message extends {
type: string;
}, RunnerFactory extends (() => RunnerLike) | undefined> = {
/**
* When the actor's runner settles while in this state, automatically
* transition to this state. The framework owns the generation-token logic —
* re-registering after each `runner.schedule()` call so that
* `abortAll()` + reschedule correctly supersedes stale callbacks.
*/
onSettled?: UserState;
/** Message handlers active in this state. Messages with no handler are silently dropped. */
on?: { [M in Message as M['type']]?: (message: Extract<Message, {
type: M['type'];
}>, ctx: HandlerContext<UserState, Context, RunnerFactory>) => void; };
};
/**
* Full actor definition passed to `createMachineActor`.
*
* `UserState` is the set of domain-meaningful states. `'destroyed'` is always
* added by the framework as the implicit terminal state — do not include it here.
*/
type ActorDefinition<UserState extends string, Context extends object, Message extends {
type: string;
}, RunnerFactory extends (() => RunnerLike) | undefined = undefined> = {
/**
* Runner factory — called once at `createMachineActor()` time.
* The runner lives for the full actor lifetime and is destroyed with it.
*
* @example
* runner: () => new SerialRunner()
*/
runner?: RunnerFactory;
/** Initial state. */
initial: UserState;
/** Initial context. */
context: Context;
/**
* Per-state definitions. States with no definition silently drop all messages.
* All user-defined states must appear as keys in the `UserState` union.
*/
states: Partial<Record<UserState, ActorStateDefinition<UserState, Context, Message, RunnerFactory>>>;
};
/** Live actor instance returned by `createMachineActor`. */
interface MessageActor<State extends string, Context extends object, Message extends {
type: string;
}> extends SignalActor<State, Context> {
send(message: Message): void;
}
/**
* Creates a message-driven actor from a declarative definition.
*
* The actor owns a reactive snapshot signal (state + context), an optional
* runner, and dispatches incoming messages to per-state handlers. `'destroyed'`
* is always the implicit terminal state — `destroy()` transitions there
* unconditionally and all subsequent `send()` calls are no-ops.
*
* When a state declares `onSettled`, the framework calls `runner.whenSettled()`
* after the handler returns. The runner owns the generation-token logic — if
* new tasks are scheduled before the current batch settles, the callback is
* automatically superseded.
*
* @example
* const actor = createMachineActor({
* runner: () => new SerialRunner(),
* initial: 'idle',
* context: {},
* states: {
* idle: {
* on: {
* load: (msg, { transition, runner }) => {
* segments.forEach(s => runner.schedule(new Task(...)));
* transition('loading');
* }
* }
* },
* loading: {
* onSettled: 'idle',
* on: {
* load: (msg, { runner }) => {
* runner.abortAll();
* segments.forEach(s => runner.schedule(new Task(...)));
* }
* }
* }
* }
* });
*/
declare function createMachineActor<UserState extends string, Context extends object, Message extends {
type: string;
}, RunnerFactory extends (() => RunnerLike) | undefined = undefined>(def: ActorDefinition<UserState, Context, Message, RunnerFactory>): MessageActor<UserState | 'destroyed', Context, Message>;
//#endregion
export { ActorDefinition, ActorStateDefinition, HandlerContext, MessageActor, RunnerLike, createMachineActor };
//# sourceMappingURL=create-machine-actor.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"create-machine-actor.d.ts","names":[],"sources":["../../../../src/core/actors/create-machine-actor.ts"],"mappings":";;;;;;UAYiB;EACf,SAAS,cAAc,eAAe,MAAM,SAAS,OAAO,OAAO,QAAQ;EAC3E;EACA;EACA,YAAY;;;;;;;KAYF,eACV,0BACA,wBACA,6BAA6B;EAE7B,aAAa,IAAI;;EAEjB,SAAS;;;;;;;EAOT,kBAAkB;EAClB,aAAa,MAAM;KAChB,mCAAkC;EAAM,QAAQ;;;;;KAKzC,qBACV,0BACA,wBACA;EAAkB;GAClB,6BAA6B;;;;;;;EAQ7B,YAAY;;EAEZ,QACG,KAAK,WAAW,cACf,SAAS,QAAQ;IAAW,MAAM;MAClC,KAAK,eAAe,WAAW,SAAS;;;;;;;;KAWlC,gBACV,0BACA,wBACA;EAAkB;GAClB,6BAA6B;;;;;;;;EAS7B,SAAS;;EAET,SAAS;;EAET,SAAS;;;;;EAKT,QAAQ,QAAQ,OAAO,WAAW,qBAAqB,WAAW,SAAS,SAAS;;;UAQrE,aAAa,sBAAsB,wBAAwB;EAAkB;WACpF,YAAY,OAAO;EAC3B,KAAK,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA8CA,mBACd,0BACA,wBACA;EAAkB;GAClB,6BAA6B,qCAE7B,KAAK,gBAAgB,WAAW,SAAS,SAAS,iBACjD,aAAa,yBAAyB,SAAS"}
+91
View File
@@ -0,0 +1,91 @@
import { untrack, update } from "../signals/primitives.js";
import { createMachineCore } from "../machine.js";
//#region src/core/actors/create-machine-actor.ts
/**
* Creates a message-driven actor from a declarative definition.
*
* The actor owns a reactive snapshot signal (state + context), an optional
* runner, and dispatches incoming messages to per-state handlers. `'destroyed'`
* is always the implicit terminal state — `destroy()` transitions there
* unconditionally and all subsequent `send()` calls are no-ops.
*
* When a state declares `onSettled`, the framework calls `runner.whenSettled()`
* after the handler returns. The runner owns the generation-token logic — if
* new tasks are scheduled before the current batch settles, the callback is
* automatically superseded.
*
* @example
* const actor = createMachineActor({
* runner: () => new SerialRunner(),
* initial: 'idle',
* context: {},
* states: {
* idle: {
* on: {
* load: (msg, { transition, runner }) => {
* segments.forEach(s => runner.schedule(new Task(...)));
* transition('loading');
* }
* }
* },
* loading: {
* onSettled: 'idle',
* on: {
* load: (msg, { runner }) => {
* runner.abortAll();
* segments.forEach(s => runner.schedule(new Task(...)));
* }
* }
* }
* }
* });
*/
function createMachineActor(def) {
const runner = def.runner?.();
const { snapshotSignal, getState, transition } = createMachineCore({
value: def.initial,
context: def.context
});
const getContext = () => untrack(() => snapshotSignal.get().context);
const setContext = (context) => {
update(snapshotSignal, { context });
};
return {
get snapshot() {
return snapshotSignal;
},
send(message) {
const state = getState();
if (state === "destroyed") return;
const handler = def.states[state]?.on?.[message.type];
if (!handler) return;
handler(message, {
context: getContext(),
getContext,
transition: (to) => transition(to),
setContext,
...runner ? { runner } : {}
});
const newState = getState();
if (newState !== "destroyed") {
const newStateDef = def.states[newState];
if (newStateDef?.onSettled && runner) {
const targetState = newStateDef.onSettled;
runner.whenSettled(() => {
if (getState() !== newState) return;
transition(targetState);
});
}
}
},
destroy() {
if (getState() === "destroyed") return;
runner?.destroy();
transition("destroyed");
}
};
}
//#endregion
export { createMachineActor };
//# sourceMappingURL=create-machine-actor.js.map
File diff suppressed because one or more lines are too long
+41
View File
@@ -0,0 +1,41 @@
import { ActorSnapshot } from "./actor.js";
import { Machine } from "../machine.js";
//#region src/core/actors/create-transition-actor.d.ts
/**
* A reducer-shaped actor: `(context, message) => context`.
*
* No finite states the snapshot carries `value: 'active' | 'destroyed'`
* as a universal lifecycle marker rather than domain state. The interesting
* state is entirely in the context, which is reactive via `snapshot`.
*
* Use this when the actor has context that needs to be reactive but no
* meaningful state machine (e.g., a message-driven model with DOM side
* effects). For actors that need per-state behavior, use `createMachineActor`.
*/
interface TransitionActor<Context extends object, Message extends {
type: string;
}> extends Machine<ActorSnapshot<'active' | 'destroyed', Context>> {
send(message: Message): void;
}
/**
* Creates a reducer-shaped actor from an initial context and a reducer function.
*
* The reducer receives the current context and a message and returns the next
* context. Returning the same reference (by identity) skips the signal update
* so early-returning `context` unchanged is both the no-op and the optimization.
*
* Side effects (e.g. DOM mutations) may be performed inside the reducer.
* They run synchronously before the signal is updated.
*
* @example
* const actor = createTransitionActor(
* { count: 0 },
* (context, message: { type: 'increment' }) => ({ count: context.count + 1 })
* );
*/
declare function createTransitionActor<Context extends object, Message extends {
type: string;
}>(initialContext: Context, reducer: (context: Context, message: Message) => Context): TransitionActor<Context, Message>;
//#endregion
export { TransitionActor, createTransitionActor };
//# sourceMappingURL=create-transition-actor.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"create-transition-actor.d.ts","names":[],"sources":["../../../../src/core/actors/create-transition-actor.ts"],"mappings":";;;;;;;;;;;;;;UAoBiB,gBAAgB,wBAAwB;EAAkB;WACjE,QAAQ,sCAAsC;EACtD,KAAK,SAAS;;;;;;;;;;;;;;;;;;iBAuBA,sBAAsB,wBAAwB;EAAkB;GAC9E,gBAAgB,SAChB,UAAU,SAAS,SAAS,SAAS,YAAY,UAChD,gBAAgB,SAAS"}
+46
View File
@@ -0,0 +1,46 @@
import { untrack, update } from "../signals/primitives.js";
import { createMachineCore } from "../machine.js";
//#region src/core/actors/create-transition-actor.ts
/**
* Creates a reducer-shaped actor from an initial context and a reducer function.
*
* The reducer receives the current context and a message and returns the next
* context. Returning the same reference (by identity) skips the signal update
* so early-returning `context` unchanged is both the no-op and the optimization.
*
* Side effects (e.g. DOM mutations) may be performed inside the reducer.
* They run synchronously before the signal is updated.
*
* @example
* const actor = createTransitionActor(
* { count: 0 },
* (context, message: { type: 'increment' }) => ({ count: context.count + 1 })
* );
*/
function createTransitionActor(initialContext, reducer) {
const { snapshotSignal, getState, transition } = createMachineCore({
value: "active",
context: initialContext
});
const getContext = () => untrack(() => snapshotSignal.get().context);
const setContext = (context) => update(snapshotSignal, { context });
return {
get snapshot() {
return snapshotSignal;
},
send(message) {
if (getState() === "destroyed") return;
const context = getContext();
const newContext = reducer(context, message);
if (newContext !== context) setContext(newContext);
},
destroy() {
if (getState() === "destroyed") return;
transition("destroyed");
}
};
}
//#endregion
export { createTransitionActor };
//# sourceMappingURL=create-transition-actor.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"create-transition-actor.js","names":[],"sources":["../../../../src/core/actors/create-transition-actor.ts"],"sourcesContent":["import type { Machine } from '../machine';\nimport { createMachineCore } from '../machine';\nimport { untrack, update } from '../signals/primitives';\nimport type { ActorSnapshot } from './actor';\n\n// =============================================================================\n// Definition types\n// =============================================================================\n\n/**\n * A reducer-shaped actor: `(context, message) => context`.\n *\n * No finite states — the snapshot carries `value: 'active' | 'destroyed'`\n * as a universal lifecycle marker rather than domain state. The interesting\n * state is entirely in the context, which is reactive via `snapshot`.\n *\n * Use this when the actor has context that needs to be reactive but no\n * meaningful state machine (e.g., a message-driven model with DOM side\n * effects). For actors that need per-state behavior, use `createMachineActor`.\n */\nexport interface TransitionActor<Context extends object, Message extends { type: string }>\n extends Machine<ActorSnapshot<'active' | 'destroyed', Context>> {\n send(message: Message): void;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\n/**\n * Creates a reducer-shaped actor from an initial context and a reducer function.\n *\n * The reducer receives the current context and a message and returns the next\n * context. Returning the same reference (by identity) skips the signal update —\n * so early-returning `context` unchanged is both the no-op and the optimization.\n *\n * Side effects (e.g. DOM mutations) may be performed inside the reducer.\n * They run synchronously before the signal is updated.\n *\n * @example\n * const actor = createTransitionActor(\n * { count: 0 },\n * (context, message: { type: 'increment' }) => ({ count: context.count + 1 })\n * );\n */\nexport function createTransitionActor<Context extends object, Message extends { type: string }>(\n initialContext: Context,\n reducer: (context: Context, message: Message) => Context\n): TransitionActor<Context, Message> {\n const { snapshotSignal, getState, transition } = createMachineCore<\n 'active' | 'destroyed',\n ActorSnapshot<'active' | 'destroyed', Context>\n >({ value: 'active', context: initialContext });\n\n const getContext = (): Context => untrack(() => snapshotSignal.get().context);\n const setContext = (context: Context): void => update(snapshotSignal, { context });\n\n return {\n get snapshot() {\n return snapshotSignal;\n },\n\n send(message: Message): void {\n if (getState() === 'destroyed') return;\n const context = getContext();\n const newContext = reducer(context, message);\n if (newContext !== context) setContext(newContext);\n },\n\n destroy(): void {\n if (getState() === 'destroyed') return;\n transition('destroyed');\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA6CA,SAAgB,sBACd,gBACA,SACmC;CACnC,MAAM,EAAE,gBAAgB,UAAU,eAAe,kBAG/C;EAAE,OAAO;EAAU,SAAS;CAAe,CAAC;CAE9C,MAAM,mBAA4B,cAAc,eAAe,IAAI,CAAC,CAAC,OAAO;CAC5E,MAAM,cAAc,YAA2B,OAAO,gBAAgB,EAAE,QAAQ,CAAC;CAEjF,OAAO;EACL,IAAI,WAAW;GACb,OAAO;EACT;EAEA,KAAK,SAAwB;GAC3B,IAAI,SAAS,MAAM,aAAa;GAChC,MAAM,UAAU,WAAW;GAC3B,MAAM,aAAa,QAAQ,SAAS,OAAO;GAC3C,IAAI,eAAe,SAAS,WAAW,UAAU;EACnD;EAEA,UAAgB;GACd,IAAI,SAAS,MAAM,aAAa;GAChC,WAAW,WAAW;EACxB;CACF;AACF"}