@kyneta/machine 1.3.1 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +45 -42
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +153 -138
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
//#region src/machine.d.ts
|
|
1
2
|
/** Dispatch a message into a running program. */
|
|
2
3
|
type Dispatch<Msg> = (msg: Msg) => void;
|
|
3
4
|
/** An effect is a continuation that may dispatch messages. */
|
|
@@ -15,9 +16,9 @@ type Effect<Msg> = (dispatch: Dispatch<Msg>) => void;
|
|
|
15
16
|
* the runtime is disposed.
|
|
16
17
|
*/
|
|
17
18
|
type Program<Msg, Model, Fx = Effect<Msg>> = {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
init: [Model, ...Fx[]];
|
|
20
|
+
update(msg: Msg, model: Model): [Model, ...Fx[]];
|
|
21
|
+
done?(model: Model): void;
|
|
21
22
|
};
|
|
22
23
|
/** Dispose a running program — stops message processing and calls `done`. */
|
|
23
24
|
type Disposer = () => void;
|
|
@@ -37,7 +38,8 @@ type Disposer = () => void;
|
|
|
37
38
|
* after the current dispatch cycle completes (queue-based).
|
|
38
39
|
*/
|
|
39
40
|
declare function runtime<Msg, Model>(program: Program<Msg, Model>, view?: (model: Model, dispatch: Dispatch<Msg>) => void): Disposer;
|
|
40
|
-
|
|
41
|
+
//#endregion
|
|
42
|
+
//#region src/observable.d.ts
|
|
41
43
|
/**
|
|
42
44
|
* A state transition event — from one model to another.
|
|
43
45
|
*
|
|
@@ -45,9 +47,9 @@ declare function runtime<Msg, Model>(program: Program<Msg, Model>, view?: (model
|
|
|
45
47
|
* transport packages re-export or alias it for their specific state types.
|
|
46
48
|
*/
|
|
47
49
|
type StateTransition<S> = {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
50
|
+
from: S;
|
|
51
|
+
to: S;
|
|
52
|
+
timestamp: number;
|
|
51
53
|
};
|
|
52
54
|
/**
|
|
53
55
|
* Listener for state transitions.
|
|
@@ -61,40 +63,40 @@ type TransitionListener<S> = (transition: StateTransition<S>) => void;
|
|
|
61
63
|
* matches the surface of the former `ClientStateMachine<S>`.
|
|
62
64
|
*/
|
|
63
65
|
interface ObservableHandle<Msg, Model> {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
66
|
+
/** Dispatch a message into the program. */
|
|
67
|
+
dispatch: Dispatch<Msg>;
|
|
68
|
+
/** Get the current model synchronously. */
|
|
69
|
+
getState(): Model;
|
|
70
|
+
/**
|
|
71
|
+
* Subscribe to state transitions.
|
|
72
|
+
*
|
|
73
|
+
* Transitions are delivered synchronously after each update.
|
|
74
|
+
* Returns an unsubscribe function.
|
|
75
|
+
*/
|
|
76
|
+
subscribeToTransitions(listener: TransitionListener<Model>): () => void;
|
|
77
|
+
/**
|
|
78
|
+
* Wait for a specific state.
|
|
79
|
+
*
|
|
80
|
+
* Resolves immediately if the current state matches the predicate.
|
|
81
|
+
* Otherwise waits for a transition that matches.
|
|
82
|
+
*/
|
|
83
|
+
waitForState(predicate: (state: Model) => boolean, options?: {
|
|
84
|
+
timeoutMs?: number;
|
|
85
|
+
}): Promise<Model>;
|
|
86
|
+
/**
|
|
87
|
+
* Wait for a specific status string on a model with a `status` discriminant.
|
|
88
|
+
*
|
|
89
|
+
* Convenience wrapper around `waitForState()`.
|
|
90
|
+
*/
|
|
91
|
+
waitForStatus<S extends {
|
|
92
|
+
status: string;
|
|
93
|
+
}>(this: ObservableHandle<Msg, S>, status: S["status"], options?: {
|
|
94
|
+
timeoutMs?: number;
|
|
95
|
+
}): Promise<S>;
|
|
96
|
+
/**
|
|
97
|
+
* Dispose the program — stops dispatch and calls `program.done`.
|
|
98
|
+
*/
|
|
99
|
+
dispose(): void;
|
|
98
100
|
}
|
|
99
101
|
/**
|
|
100
102
|
* Run a program with data effects and state observation.
|
|
@@ -117,5 +119,6 @@ interface ObservableHandle<Msg, Model> {
|
|
|
117
119
|
* @returns An observable handle for the running program.
|
|
118
120
|
*/
|
|
119
121
|
declare function createObservableProgram<Msg, Model, Fx>(program: Program<Msg, Model, Fx>, executor: (effect: Fx, dispatch: Dispatch<Msg>) => void): ObservableHandle<Msg, Model>;
|
|
120
|
-
|
|
122
|
+
//#endregion
|
|
121
123
|
export { type Dispatch, type Disposer, type Effect, type ObservableHandle, type Program, type StateTransition, type TransitionListener, createObservableProgram, runtime };
|
|
124
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/machine.ts","../src/observable.ts"],"mappings":";;KACY,QAAA,SAAiB,GAAA,EAAK,GAAA;;KAGtB,MAAA,SAAe,QAAA,EAAU,QAAA,CAAS,GAAA;;;;;;;AAA9C;;;;;;KAcY,OAAA,kBAAyB,MAAA,CAAO,GAAA;EAC1C,IAAA,GAAO,KAAA,KAAU,EAAA;EACjB,MAAA,CAAO,GAAA,EAAK,GAAA,EAAK,KAAA,EAAO,KAAA,IAAS,KAAA,KAAU,EAAA;EAC3C,IAAA,EAAM,KAAA,EAAO,KAAA;AAAA;;KAIH,QAAA;;;;;;;;;;;;;;;;iBAiBI,OAAA,YAAA,CACd,OAAA,EAAS,OAAA,CAAQ,GAAA,EAAK,KAAA,GACtB,IAAA,IAAQ,KAAA,EAAO,KAAA,EAAO,QAAA,EAAU,QAAA,CAAS,GAAA,aACxC,QAAA;;;AA5CH;;;;;;AAAA,KC8BY,eAAA;EACV,IAAA,EAAM,CAAA;EACN,EAAA,EAAI,CAAA;EACJ,SAAA;AAAA;;;;KAMU,kBAAA,OAAyB,UAAA,EAAY,eAAA,CAAgB,CAAA;;;;;ADtBjE;;;UCmCiB,gBAAA;EDnCoB;ECqCnC,QAAA,EAAU,QAAA,CAAS,GAAA;EDpCF;ECuCjB,QAAA,IAAY,KAAA;EDtCY;;;;;;EC8CxB,sBAAA,CAAuB,QAAA,EAAU,kBAAA,CAAmB,KAAA;EDhD7B;;;;;;ECwDvB,YAAA,CACE,SAAA,GAAY,KAAA,EAAO,KAAA,cACnB,OAAA;IAAY,SAAA;EAAA,IACX,OAAA,CAAQ,KAAA;EDzDJ;;;;;ECgEP,aAAA;IAA0B,MAAA;EAAA,GACxB,IAAA,EAAM,gBAAA,CAAiB,GAAA,EAAK,CAAA,GAC5B,MAAA,EAAQ,CAAA,YACR,OAAA;IAAY,SAAA;EAAA,IACX,OAAA,CAAQ,CAAA;ED/DD;;;ECoEV,OAAA;AAAA;ADnDF;;;;;;;;;;;;;;;;;;;;AAAA,iBC8EgB,uBAAA,gBAAA,CACd,OAAA,EAAS,OAAA,CAAQ,GAAA,EAAK,KAAA,EAAO,EAAA,GAC7B,QAAA,GAAW,MAAA,EAAQ,EAAA,EAAI,QAAA,EAAU,QAAA,CAAS,GAAA,aACzC,gBAAA,CAAiB,GAAA,EAAK,KAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,143 +1,158 @@
|
|
|
1
|
-
|
|
1
|
+
//#region src/machine.ts
|
|
2
|
+
/**
|
|
3
|
+
* Run a program whose effects are `Effect<Msg>` closures.
|
|
4
|
+
*
|
|
5
|
+
* The runtime:
|
|
6
|
+
* 1. Extracts `[model, ...effects]` from `program.init`.
|
|
7
|
+
* 2. Executes each initial effect with `dispatch`.
|
|
8
|
+
* 3. Calls `view(model, dispatch)` if provided.
|
|
9
|
+
* 4. On `dispatch(msg)`: calls `update(msg, state)`, updates state,
|
|
10
|
+
* executes effects, calls `view`.
|
|
11
|
+
* 5. Returns a `Disposer` that stops dispatch and calls `program.done`.
|
|
12
|
+
*
|
|
13
|
+
* Effects are executed synchronously in order. An effect may call
|
|
14
|
+
* `dispatch` re-entrantly — the runtime processes re-entrant messages
|
|
15
|
+
* after the current dispatch cycle completes (queue-based).
|
|
16
|
+
*/
|
|
2
17
|
function runtime(program, view) {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
if (!isRunning) return;
|
|
34
|
-
isRunning = false;
|
|
35
|
-
program.done?.(state);
|
|
36
|
-
};
|
|
18
|
+
let state;
|
|
19
|
+
let isRunning = true;
|
|
20
|
+
const pending = [];
|
|
21
|
+
let isDispatching = false;
|
|
22
|
+
function dispatch(msg) {
|
|
23
|
+
if (!isRunning) return;
|
|
24
|
+
pending.push(msg);
|
|
25
|
+
if (isDispatching) return;
|
|
26
|
+
isDispatching = true;
|
|
27
|
+
try {
|
|
28
|
+
while (pending.length > 0) {
|
|
29
|
+
const next = pending.shift();
|
|
30
|
+
const [newModel, ...effects] = program.update(next, state);
|
|
31
|
+
state = newModel;
|
|
32
|
+
for (const effect of effects) effect(dispatch);
|
|
33
|
+
if (view) view(state, dispatch);
|
|
34
|
+
}
|
|
35
|
+
} finally {
|
|
36
|
+
isDispatching = false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const [initialModel, ...initialEffects] = program.init;
|
|
40
|
+
state = initialModel;
|
|
41
|
+
for (const effect of initialEffects) effect(dispatch);
|
|
42
|
+
if (view) view(state, dispatch);
|
|
43
|
+
return () => {
|
|
44
|
+
if (!isRunning) return;
|
|
45
|
+
isRunning = false;
|
|
46
|
+
program.done?.(state);
|
|
47
|
+
};
|
|
37
48
|
}
|
|
38
|
-
|
|
39
|
-
|
|
49
|
+
//#endregion
|
|
50
|
+
//#region src/observable.ts
|
|
51
|
+
/**
|
|
52
|
+
* Run a program with data effects and state observation.
|
|
53
|
+
*
|
|
54
|
+
* Like `runtime()`, but instead of executing closure effects directly,
|
|
55
|
+
* it delegates to a custom `executor` for each data effect. This enables
|
|
56
|
+
* programs whose effects are inspectable data types (not opaque closures).
|
|
57
|
+
*
|
|
58
|
+
* The runtime:
|
|
59
|
+
* 1. Extracts `[model, ...effects]` from `program.init`.
|
|
60
|
+
* 2. Executes each initial effect via `executor(effect, dispatch)`.
|
|
61
|
+
* 3. On `dispatch(msg)`: calls `update(msg, state)`, updates state,
|
|
62
|
+
* notifies transition listeners, executes effects.
|
|
63
|
+
* 4. Re-entrant dispatch (effect calls dispatch) is queued and processed
|
|
64
|
+
* after the current dispatch cycle completes.
|
|
65
|
+
* 5. `dispose()` stops dispatch and calls `program.done`.
|
|
66
|
+
*
|
|
67
|
+
* @param program - The program algebra: init, update, done.
|
|
68
|
+
* @param executor - Interprets data effects as I/O.
|
|
69
|
+
* @returns An observable handle for the running program.
|
|
70
|
+
*/
|
|
40
71
|
function createObservableProgram(program, executor) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
program.done?.(state);
|
|
124
|
-
}
|
|
125
|
-
const [initialModel, ...initialEffects] = program.init;
|
|
126
|
-
state = initialModel;
|
|
127
|
-
for (const effect of initialEffects) {
|
|
128
|
-
executor(effect, dispatch);
|
|
129
|
-
}
|
|
130
|
-
return {
|
|
131
|
-
dispatch,
|
|
132
|
-
getState,
|
|
133
|
-
subscribeToTransitions,
|
|
134
|
-
waitForState,
|
|
135
|
-
waitForStatus,
|
|
136
|
-
dispose
|
|
137
|
-
};
|
|
72
|
+
let state;
|
|
73
|
+
let isRunning = true;
|
|
74
|
+
const pending = [];
|
|
75
|
+
let isDispatching = false;
|
|
76
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
77
|
+
function notifyTransition(from, to) {
|
|
78
|
+
if (from === to) return;
|
|
79
|
+
const transition = {
|
|
80
|
+
from,
|
|
81
|
+
to,
|
|
82
|
+
timestamp: Date.now()
|
|
83
|
+
};
|
|
84
|
+
for (const listener of listeners) try {
|
|
85
|
+
listener(transition);
|
|
86
|
+
} catch {}
|
|
87
|
+
}
|
|
88
|
+
function dispatch(msg) {
|
|
89
|
+
if (!isRunning) return;
|
|
90
|
+
pending.push(msg);
|
|
91
|
+
if (isDispatching) return;
|
|
92
|
+
isDispatching = true;
|
|
93
|
+
try {
|
|
94
|
+
while (pending.length > 0) {
|
|
95
|
+
const next = pending.shift();
|
|
96
|
+
const prev = state;
|
|
97
|
+
const [newModel, ...effects] = program.update(next, state);
|
|
98
|
+
state = newModel;
|
|
99
|
+
notifyTransition(prev, state);
|
|
100
|
+
for (const effect of effects) executor(effect, dispatch);
|
|
101
|
+
}
|
|
102
|
+
} finally {
|
|
103
|
+
isDispatching = false;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function getState() {
|
|
107
|
+
return state;
|
|
108
|
+
}
|
|
109
|
+
function subscribeToTransitions(listener) {
|
|
110
|
+
listeners.add(listener);
|
|
111
|
+
return () => {
|
|
112
|
+
listeners.delete(listener);
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
function waitForState(predicate, options) {
|
|
116
|
+
if (predicate(state)) return Promise.resolve(state);
|
|
117
|
+
return new Promise((resolve, reject) => {
|
|
118
|
+
let timeoutId;
|
|
119
|
+
const unsubscribe = subscribeToTransitions((transition) => {
|
|
120
|
+
if (predicate(transition.to)) {
|
|
121
|
+
cleanup();
|
|
122
|
+
resolve(transition.to);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
const cleanup = () => {
|
|
126
|
+
unsubscribe();
|
|
127
|
+
if (timeoutId !== void 0) clearTimeout(timeoutId);
|
|
128
|
+
};
|
|
129
|
+
if (options?.timeoutMs !== void 0) timeoutId = setTimeout(() => {
|
|
130
|
+
cleanup();
|
|
131
|
+
reject(/* @__PURE__ */ new Error(`Timeout waiting for state after ${options.timeoutMs}ms`));
|
|
132
|
+
}, options.timeoutMs);
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
function waitForStatus(status, options) {
|
|
136
|
+
return this.waitForState((s) => s.status === status, options);
|
|
137
|
+
}
|
|
138
|
+
function dispose() {
|
|
139
|
+
if (!isRunning) return;
|
|
140
|
+
isRunning = false;
|
|
141
|
+
program.done?.(state);
|
|
142
|
+
}
|
|
143
|
+
const [initialModel, ...initialEffects] = program.init;
|
|
144
|
+
state = initialModel;
|
|
145
|
+
for (const effect of initialEffects) executor(effect, dispatch);
|
|
146
|
+
return {
|
|
147
|
+
dispatch,
|
|
148
|
+
getState,
|
|
149
|
+
subscribeToTransitions,
|
|
150
|
+
waitForState,
|
|
151
|
+
waitForStatus,
|
|
152
|
+
dispose
|
|
153
|
+
};
|
|
138
154
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
};
|
|
155
|
+
//#endregion
|
|
156
|
+
export { createObservableProgram, runtime };
|
|
157
|
+
|
|
143
158
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/machine.ts","../src/observable.ts"],"sourcesContent":["/** Dispatch a message into a running program. */\nexport type Dispatch<Msg> = (msg: Msg) => void\n\n/** An effect is a continuation that may dispatch messages. */\nexport type Effect<Msg> = (dispatch: Dispatch<Msg>) => void\n\n/**\n * A Mealy machine — pure state transitions with effect outputs.\n *\n * `Fx` defaults to `Effect<Msg>` (closure effects) but can be any\n * data type for programs with custom effect executors.\n *\n * - `init`: initial state and zero or more effects to execute at startup.\n * - `update`: pure transition — given a message and the current state,\n * return the new state and zero or more effects.\n * - `done`: optional teardown hook, called with the final state when\n * the runtime is disposed.\n */\nexport type Program<Msg, Model, Fx = Effect<Msg>> = {\n init: [Model, ...Fx[]]\n update(msg: Msg, model: Model): [Model, ...Fx[]]\n done?(model: Model): void\n}\n\n/** Dispose a running program — stops message processing and calls `done`. */\nexport type Disposer = () => void\n\n/**\n * Run a program whose effects are `Effect<Msg>` closures.\n *\n * The runtime:\n * 1. Extracts `[model, ...effects]` from `program.init`.\n * 2. Executes each initial effect with `dispatch`.\n * 3. Calls `view(model, dispatch)` if provided.\n * 4. On `dispatch(msg)`: calls `update(msg, state)`, updates state,\n * executes effects, calls `view`.\n * 5. Returns a `Disposer` that stops dispatch and calls `program.done`.\n *\n * Effects are executed synchronously in order. An effect may call\n * `dispatch` re-entrantly — the runtime processes re-entrant messages\n * after the current dispatch cycle completes (queue-based).\n */\nexport function runtime<Msg, Model>(\n program: Program<Msg, Model>,\n view?: (model: Model, dispatch: Dispatch<Msg>) => void,\n): Disposer {\n let state: Model\n let isRunning = true\n const pending: Msg[] = []\n let isDispatching = false\n\n function dispatch(msg: Msg): void {\n if (!isRunning) return\n\n pending.push(msg)\n if (isDispatching) return\n\n isDispatching = true\n try {\n while (pending.length > 0) {\n const next = pending.shift()!\n const [newModel, ...effects] = program.update(next, state)\n state = newModel\n for (const effect of effects) {\n effect(dispatch)\n }\n if (view) view(state, dispatch)\n }\n } finally {\n isDispatching = false\n }\n }\n\n // Initialize\n const [initialModel, ...initialEffects] = program.init\n state = initialModel\n for (const effect of initialEffects) {\n effect(dispatch)\n }\n if (view) view(state, dispatch)\n\n // Return disposer\n return () => {\n if (!isRunning) return\n isRunning = false\n program.done?.(state)\n }\n}\n","// observable — data-effect runtime with state observation.\n//\n// createObservableProgram() is the data-effect counterpart to runtime().\n// Where runtime() executes closure effects (Effect<Msg>), this function\n// accepts a custom executor for data effects (Fx). It also provides\n// state observation: subscribeToTransitions, waitForState, waitForStatus.\n//\n// This subsumes ClientStateMachine's observation API and the peer program's\n// hand-rolled dispatch loop. Transition delivery is synchronous — the\n// listener fires after each update. The microtask-batched delivery from\n// ClientStateMachine is unnecessary complexity that no consumer depends on.\n\nimport type { Dispatch, Program } from \"./machine.js\"\n\n// ---------------------------------------------------------------------------\n// Ambient declarations for timer APIs (not in lib: [\"ESNext\"])\n// ---------------------------------------------------------------------------\n\ndeclare function setTimeout(callback: () => void, ms: number): unknown\ndeclare function clearTimeout(id: unknown): void\n\n// ---------------------------------------------------------------------------\n// Observation types\n// ---------------------------------------------------------------------------\n\n/**\n * A state transition event — from one model to another.\n *\n * Generic over the model type. This is the machine-level primitive;\n * transport packages re-export or alias it for their specific state types.\n */\nexport type StateTransition<S> = {\n from: S\n to: S\n timestamp: number\n}\n\n/**\n * Listener for state transitions.\n */\nexport type TransitionListener<S> = (transition: StateTransition<S>) => void\n\n// ---------------------------------------------------------------------------\n// ObservableHandle\n// ---------------------------------------------------------------------------\n\n/**\n * Handle for a running observable program.\n *\n * Provides dispatch, state access, transition observation, and disposal.\n * The observation API (`subscribeToTransitions`, `waitForState`, `waitForStatus`)\n * matches the surface of the former `ClientStateMachine<S>`.\n */\nexport interface ObservableHandle<Msg, Model> {\n /** Dispatch a message into the program. */\n dispatch: Dispatch<Msg>\n\n /** Get the current model synchronously. */\n getState(): Model\n\n /**\n * Subscribe to state transitions.\n *\n * Transitions are delivered synchronously after each update.\n * Returns an unsubscribe function.\n */\n subscribeToTransitions(listener: TransitionListener<Model>): () => void\n\n /**\n * Wait for a specific state.\n *\n * Resolves immediately if the current state matches the predicate.\n * Otherwise waits for a transition that matches.\n */\n waitForState(\n predicate: (state: Model) => boolean,\n options?: { timeoutMs?: number },\n ): Promise<Model>\n\n /**\n * Wait for a specific status string on a model with a `status` discriminant.\n *\n * Convenience wrapper around `waitForState()`.\n */\n waitForStatus<S extends { status: string }>(\n this: ObservableHandle<Msg, S>,\n status: S[\"status\"],\n options?: { timeoutMs?: number },\n ): Promise<S>\n\n /**\n * Dispose the program — stops dispatch and calls `program.done`.\n */\n dispose(): void\n}\n\n// ---------------------------------------------------------------------------\n// createObservableProgram\n// ---------------------------------------------------------------------------\n\n/**\n * Run a program with data effects and state observation.\n *\n * Like `runtime()`, but instead of executing closure effects directly,\n * it delegates to a custom `executor` for each data effect. This enables\n * programs whose effects are inspectable data types (not opaque closures).\n *\n * The runtime:\n * 1. Extracts `[model, ...effects]` from `program.init`.\n * 2. Executes each initial effect via `executor(effect, dispatch)`.\n * 3. On `dispatch(msg)`: calls `update(msg, state)`, updates state,\n * notifies transition listeners, executes effects.\n * 4. Re-entrant dispatch (effect calls dispatch) is queued and processed\n * after the current dispatch cycle completes.\n * 5. `dispose()` stops dispatch and calls `program.done`.\n *\n * @param program - The program algebra: init, update, done.\n * @param executor - Interprets data effects as I/O.\n * @returns An observable handle for the running program.\n */\nexport function createObservableProgram<Msg, Model, Fx>(\n program: Program<Msg, Model, Fx>,\n executor: (effect: Fx, dispatch: Dispatch<Msg>) => void,\n): ObservableHandle<Msg, Model> {\n let state: Model\n let isRunning = true\n const pending: Msg[] = []\n let isDispatching = false\n const listeners = new Set<TransitionListener<Model>>()\n\n // --------------------------------------------------------------------------\n // Transition notification\n // --------------------------------------------------------------------------\n\n function notifyTransition(from: Model, to: Model): void {\n if (from === to) return\n\n const transition: StateTransition<Model> = {\n from,\n to,\n timestamp: Date.now(),\n }\n\n for (const listener of listeners) {\n try {\n listener(transition)\n } catch {\n // Swallow listener errors — observers must not break dispatch.\n }\n }\n }\n\n // --------------------------------------------------------------------------\n // Dispatch\n // --------------------------------------------------------------------------\n\n function dispatch(msg: Msg): void {\n if (!isRunning) return\n\n pending.push(msg)\n if (isDispatching) return\n\n isDispatching = true\n try {\n while (pending.length > 0) {\n const next = pending.shift()!\n const prev = state\n const [newModel, ...effects] = program.update(next, state)\n state = newModel\n notifyTransition(prev, state)\n for (const effect of effects) {\n executor(effect, dispatch)\n }\n }\n } finally {\n isDispatching = false\n }\n }\n\n // --------------------------------------------------------------------------\n // Observation\n // --------------------------------------------------------------------------\n\n function getState(): Model {\n return state\n }\n\n function subscribeToTransitions(\n listener: TransitionListener<Model>,\n ): () => void {\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n }\n\n function waitForState(\n predicate: (state: Model) => boolean,\n options?: { timeoutMs?: number },\n ): Promise<Model> {\n // Resolve immediately if already matching\n if (predicate(state)) {\n return Promise.resolve(state)\n }\n\n return new Promise((resolve, reject) => {\n let timeoutId: unknown\n\n const unsubscribe = subscribeToTransitions(transition => {\n if (predicate(transition.to)) {\n cleanup()\n resolve(transition.to)\n }\n })\n\n const cleanup = () => {\n unsubscribe()\n if (timeoutId !== undefined) {\n clearTimeout(timeoutId)\n }\n }\n\n if (options?.timeoutMs !== undefined) {\n timeoutId = setTimeout(() => {\n cleanup()\n reject(\n new Error(`Timeout waiting for state after ${options.timeoutMs}ms`),\n )\n }, options.timeoutMs)\n }\n })\n }\n\n function waitForStatus<S extends { status: string }>(\n this: ObservableHandle<Msg, S>,\n status: S[\"status\"],\n options?: { timeoutMs?: number },\n ): Promise<S> {\n return this.waitForState((s: S) => s.status === status, options)\n }\n\n function dispose(): void {\n if (!isRunning) return\n isRunning = false\n program.done?.(state)\n }\n\n // --------------------------------------------------------------------------\n // Initialize\n // --------------------------------------------------------------------------\n\n const [initialModel, ...initialEffects] = program.init\n state = initialModel\n for (const effect of initialEffects) {\n executor(effect, dispatch)\n }\n\n // --------------------------------------------------------------------------\n // Return handle\n // --------------------------------------------------------------------------\n\n return {\n dispatch,\n getState,\n subscribeToTransitions,\n waitForState,\n waitForStatus,\n dispose,\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/machine.ts","../src/observable.ts"],"sourcesContent":["/** Dispatch a message into a running program. */\nexport type Dispatch<Msg> = (msg: Msg) => void\n\n/** An effect is a continuation that may dispatch messages. */\nexport type Effect<Msg> = (dispatch: Dispatch<Msg>) => void\n\n/**\n * A Mealy machine — pure state transitions with effect outputs.\n *\n * `Fx` defaults to `Effect<Msg>` (closure effects) but can be any\n * data type for programs with custom effect executors.\n *\n * - `init`: initial state and zero or more effects to execute at startup.\n * - `update`: pure transition — given a message and the current state,\n * return the new state and zero or more effects.\n * - `done`: optional teardown hook, called with the final state when\n * the runtime is disposed.\n */\nexport type Program<Msg, Model, Fx = Effect<Msg>> = {\n init: [Model, ...Fx[]]\n update(msg: Msg, model: Model): [Model, ...Fx[]]\n done?(model: Model): void\n}\n\n/** Dispose a running program — stops message processing and calls `done`. */\nexport type Disposer = () => void\n\n/**\n * Run a program whose effects are `Effect<Msg>` closures.\n *\n * The runtime:\n * 1. Extracts `[model, ...effects]` from `program.init`.\n * 2. Executes each initial effect with `dispatch`.\n * 3. Calls `view(model, dispatch)` if provided.\n * 4. On `dispatch(msg)`: calls `update(msg, state)`, updates state,\n * executes effects, calls `view`.\n * 5. Returns a `Disposer` that stops dispatch and calls `program.done`.\n *\n * Effects are executed synchronously in order. An effect may call\n * `dispatch` re-entrantly — the runtime processes re-entrant messages\n * after the current dispatch cycle completes (queue-based).\n */\nexport function runtime<Msg, Model>(\n program: Program<Msg, Model>,\n view?: (model: Model, dispatch: Dispatch<Msg>) => void,\n): Disposer {\n let state: Model\n let isRunning = true\n const pending: Msg[] = []\n let isDispatching = false\n\n function dispatch(msg: Msg): void {\n if (!isRunning) return\n\n pending.push(msg)\n if (isDispatching) return\n\n isDispatching = true\n try {\n while (pending.length > 0) {\n const next = pending.shift()!\n const [newModel, ...effects] = program.update(next, state)\n state = newModel\n for (const effect of effects) {\n effect(dispatch)\n }\n if (view) view(state, dispatch)\n }\n } finally {\n isDispatching = false\n }\n }\n\n // Initialize\n const [initialModel, ...initialEffects] = program.init\n state = initialModel\n for (const effect of initialEffects) {\n effect(dispatch)\n }\n if (view) view(state, dispatch)\n\n // Return disposer\n return () => {\n if (!isRunning) return\n isRunning = false\n program.done?.(state)\n }\n}\n","// observable — data-effect runtime with state observation.\n//\n// createObservableProgram() is the data-effect counterpart to runtime().\n// Where runtime() executes closure effects (Effect<Msg>), this function\n// accepts a custom executor for data effects (Fx). It also provides\n// state observation: subscribeToTransitions, waitForState, waitForStatus.\n//\n// This subsumes ClientStateMachine's observation API and the peer program's\n// hand-rolled dispatch loop. Transition delivery is synchronous — the\n// listener fires after each update. The microtask-batched delivery from\n// ClientStateMachine is unnecessary complexity that no consumer depends on.\n\nimport type { Dispatch, Program } from \"./machine.js\"\n\n// ---------------------------------------------------------------------------\n// Ambient declarations for timer APIs (not in lib: [\"ESNext\"])\n// ---------------------------------------------------------------------------\n\ndeclare function setTimeout(callback: () => void, ms: number): unknown\ndeclare function clearTimeout(id: unknown): void\n\n// ---------------------------------------------------------------------------\n// Observation types\n// ---------------------------------------------------------------------------\n\n/**\n * A state transition event — from one model to another.\n *\n * Generic over the model type. This is the machine-level primitive;\n * transport packages re-export or alias it for their specific state types.\n */\nexport type StateTransition<S> = {\n from: S\n to: S\n timestamp: number\n}\n\n/**\n * Listener for state transitions.\n */\nexport type TransitionListener<S> = (transition: StateTransition<S>) => void\n\n// ---------------------------------------------------------------------------\n// ObservableHandle\n// ---------------------------------------------------------------------------\n\n/**\n * Handle for a running observable program.\n *\n * Provides dispatch, state access, transition observation, and disposal.\n * The observation API (`subscribeToTransitions`, `waitForState`, `waitForStatus`)\n * matches the surface of the former `ClientStateMachine<S>`.\n */\nexport interface ObservableHandle<Msg, Model> {\n /** Dispatch a message into the program. */\n dispatch: Dispatch<Msg>\n\n /** Get the current model synchronously. */\n getState(): Model\n\n /**\n * Subscribe to state transitions.\n *\n * Transitions are delivered synchronously after each update.\n * Returns an unsubscribe function.\n */\n subscribeToTransitions(listener: TransitionListener<Model>): () => void\n\n /**\n * Wait for a specific state.\n *\n * Resolves immediately if the current state matches the predicate.\n * Otherwise waits for a transition that matches.\n */\n waitForState(\n predicate: (state: Model) => boolean,\n options?: { timeoutMs?: number },\n ): Promise<Model>\n\n /**\n * Wait for a specific status string on a model with a `status` discriminant.\n *\n * Convenience wrapper around `waitForState()`.\n */\n waitForStatus<S extends { status: string }>(\n this: ObservableHandle<Msg, S>,\n status: S[\"status\"],\n options?: { timeoutMs?: number },\n ): Promise<S>\n\n /**\n * Dispose the program — stops dispatch and calls `program.done`.\n */\n dispose(): void\n}\n\n// ---------------------------------------------------------------------------\n// createObservableProgram\n// ---------------------------------------------------------------------------\n\n/**\n * Run a program with data effects and state observation.\n *\n * Like `runtime()`, but instead of executing closure effects directly,\n * it delegates to a custom `executor` for each data effect. This enables\n * programs whose effects are inspectable data types (not opaque closures).\n *\n * The runtime:\n * 1. Extracts `[model, ...effects]` from `program.init`.\n * 2. Executes each initial effect via `executor(effect, dispatch)`.\n * 3. On `dispatch(msg)`: calls `update(msg, state)`, updates state,\n * notifies transition listeners, executes effects.\n * 4. Re-entrant dispatch (effect calls dispatch) is queued and processed\n * after the current dispatch cycle completes.\n * 5. `dispose()` stops dispatch and calls `program.done`.\n *\n * @param program - The program algebra: init, update, done.\n * @param executor - Interprets data effects as I/O.\n * @returns An observable handle for the running program.\n */\nexport function createObservableProgram<Msg, Model, Fx>(\n program: Program<Msg, Model, Fx>,\n executor: (effect: Fx, dispatch: Dispatch<Msg>) => void,\n): ObservableHandle<Msg, Model> {\n let state: Model\n let isRunning = true\n const pending: Msg[] = []\n let isDispatching = false\n const listeners = new Set<TransitionListener<Model>>()\n\n // --------------------------------------------------------------------------\n // Transition notification\n // --------------------------------------------------------------------------\n\n function notifyTransition(from: Model, to: Model): void {\n if (from === to) return\n\n const transition: StateTransition<Model> = {\n from,\n to,\n timestamp: Date.now(),\n }\n\n for (const listener of listeners) {\n try {\n listener(transition)\n } catch {\n // Swallow listener errors — observers must not break dispatch.\n }\n }\n }\n\n // --------------------------------------------------------------------------\n // Dispatch\n // --------------------------------------------------------------------------\n\n function dispatch(msg: Msg): void {\n if (!isRunning) return\n\n pending.push(msg)\n if (isDispatching) return\n\n isDispatching = true\n try {\n while (pending.length > 0) {\n const next = pending.shift()!\n const prev = state\n const [newModel, ...effects] = program.update(next, state)\n state = newModel\n notifyTransition(prev, state)\n for (const effect of effects) {\n executor(effect, dispatch)\n }\n }\n } finally {\n isDispatching = false\n }\n }\n\n // --------------------------------------------------------------------------\n // Observation\n // --------------------------------------------------------------------------\n\n function getState(): Model {\n return state\n }\n\n function subscribeToTransitions(\n listener: TransitionListener<Model>,\n ): () => void {\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n }\n\n function waitForState(\n predicate: (state: Model) => boolean,\n options?: { timeoutMs?: number },\n ): Promise<Model> {\n // Resolve immediately if already matching\n if (predicate(state)) {\n return Promise.resolve(state)\n }\n\n return new Promise((resolve, reject) => {\n let timeoutId: unknown\n\n const unsubscribe = subscribeToTransitions(transition => {\n if (predicate(transition.to)) {\n cleanup()\n resolve(transition.to)\n }\n })\n\n const cleanup = () => {\n unsubscribe()\n if (timeoutId !== undefined) {\n clearTimeout(timeoutId)\n }\n }\n\n if (options?.timeoutMs !== undefined) {\n timeoutId = setTimeout(() => {\n cleanup()\n reject(\n new Error(`Timeout waiting for state after ${options.timeoutMs}ms`),\n )\n }, options.timeoutMs)\n }\n })\n }\n\n function waitForStatus<S extends { status: string }>(\n this: ObservableHandle<Msg, S>,\n status: S[\"status\"],\n options?: { timeoutMs?: number },\n ): Promise<S> {\n return this.waitForState((s: S) => s.status === status, options)\n }\n\n function dispose(): void {\n if (!isRunning) return\n isRunning = false\n program.done?.(state)\n }\n\n // --------------------------------------------------------------------------\n // Initialize\n // --------------------------------------------------------------------------\n\n const [initialModel, ...initialEffects] = program.init\n state = initialModel\n for (const effect of initialEffects) {\n executor(effect, dispatch)\n }\n\n // --------------------------------------------------------------------------\n // Return handle\n // --------------------------------------------------------------------------\n\n return {\n dispatch,\n getState,\n subscribeToTransitions,\n waitForState,\n waitForStatus,\n dispose,\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;AA0CA,SAAgB,QACd,SACA,MACU;CACV,IAAI;CACJ,IAAI,YAAY;CAChB,MAAM,UAAiB,EAAE;CACzB,IAAI,gBAAgB;CAEpB,SAAS,SAAS,KAAgB;AAChC,MAAI,CAAC,UAAW;AAEhB,UAAQ,KAAK,IAAI;AACjB,MAAI,cAAe;AAEnB,kBAAgB;AAChB,MAAI;AACF,UAAO,QAAQ,SAAS,GAAG;IACzB,MAAM,OAAO,QAAQ,OAAO;IAC5B,MAAM,CAAC,UAAU,GAAG,WAAW,QAAQ,OAAO,MAAM,MAAM;AAC1D,YAAQ;AACR,SAAK,MAAM,UAAU,QACnB,QAAO,SAAS;AAElB,QAAI,KAAM,MAAK,OAAO,SAAS;;YAEzB;AACR,mBAAgB;;;CAKpB,MAAM,CAAC,cAAc,GAAG,kBAAkB,QAAQ;AAClD,SAAQ;AACR,MAAK,MAAM,UAAU,eACnB,QAAO,SAAS;AAElB,KAAI,KAAM,MAAK,OAAO,SAAS;AAG/B,cAAa;AACX,MAAI,CAAC,UAAW;AAChB,cAAY;AACZ,UAAQ,OAAO,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;ACmCzB,SAAgB,wBACd,SACA,UAC8B;CAC9B,IAAI;CACJ,IAAI,YAAY;CAChB,MAAM,UAAiB,EAAE;CACzB,IAAI,gBAAgB;CACpB,MAAM,4BAAY,IAAI,KAAgC;CAMtD,SAAS,iBAAiB,MAAa,IAAiB;AACtD,MAAI,SAAS,GAAI;EAEjB,MAAM,aAAqC;GACzC;GACA;GACA,WAAW,KAAK,KAAK;GACtB;AAED,OAAK,MAAM,YAAY,UACrB,KAAI;AACF,YAAS,WAAW;UACd;;CAUZ,SAAS,SAAS,KAAgB;AAChC,MAAI,CAAC,UAAW;AAEhB,UAAQ,KAAK,IAAI;AACjB,MAAI,cAAe;AAEnB,kBAAgB;AAChB,MAAI;AACF,UAAO,QAAQ,SAAS,GAAG;IACzB,MAAM,OAAO,QAAQ,OAAO;IAC5B,MAAM,OAAO;IACb,MAAM,CAAC,UAAU,GAAG,WAAW,QAAQ,OAAO,MAAM,MAAM;AAC1D,YAAQ;AACR,qBAAiB,MAAM,MAAM;AAC7B,SAAK,MAAM,UAAU,QACnB,UAAS,QAAQ,SAAS;;YAGtB;AACR,mBAAgB;;;CAQpB,SAAS,WAAkB;AACzB,SAAO;;CAGT,SAAS,uBACP,UACY;AACZ,YAAU,IAAI,SAAS;AACvB,eAAa;AACX,aAAU,OAAO,SAAS;;;CAI9B,SAAS,aACP,WACA,SACgB;AAEhB,MAAI,UAAU,MAAM,CAClB,QAAO,QAAQ,QAAQ,MAAM;AAG/B,SAAO,IAAI,SAAS,SAAS,WAAW;GACtC,IAAI;GAEJ,MAAM,cAAc,wBAAuB,eAAc;AACvD,QAAI,UAAU,WAAW,GAAG,EAAE;AAC5B,cAAS;AACT,aAAQ,WAAW,GAAG;;KAExB;GAEF,MAAM,gBAAgB;AACpB,iBAAa;AACb,QAAI,cAAc,KAAA,EAChB,cAAa,UAAU;;AAI3B,OAAI,SAAS,cAAc,KAAA,EACzB,aAAY,iBAAiB;AAC3B,aAAS;AACT,2BACE,IAAI,MAAM,mCAAmC,QAAQ,UAAU,IAAI,CACpE;MACA,QAAQ,UAAU;IAEvB;;CAGJ,SAAS,cAEP,QACA,SACY;AACZ,SAAO,KAAK,cAAc,MAAS,EAAE,WAAW,QAAQ,QAAQ;;CAGlE,SAAS,UAAgB;AACvB,MAAI,CAAC,UAAW;AAChB,cAAY;AACZ,UAAQ,OAAO,MAAM;;CAOvB,MAAM,CAAC,cAAc,GAAG,kBAAkB,QAAQ;AAClD,SAAQ;AACR,MAAK,MAAM,UAAU,eACnB,UAAS,QAAQ,SAAS;AAO5B,QAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACD"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kyneta/machine",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Universal Mealy machine algebra — Program, Effect, Dispatch, runtime",
|
|
5
5
|
"author": "Duane Johnson",
|
|
6
6
|
"license": "MIT",
|
|
@@ -30,12 +30,12 @@
|
|
|
30
30
|
"./src/*": "./src/*"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
|
-
"
|
|
33
|
+
"tsdown": "^0.21.9",
|
|
34
34
|
"typescript": "^5.9.2",
|
|
35
35
|
"vitest": "^4.0.17"
|
|
36
36
|
},
|
|
37
37
|
"scripts": {
|
|
38
|
-
"build": "
|
|
38
|
+
"build": "tsdown",
|
|
39
39
|
"test": "verify logic",
|
|
40
40
|
"verify": "verify"
|
|
41
41
|
}
|