@fkws/klonk 0.0.4
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/README.md +276 -0
- package/dist/cli.cjs +132 -0
- package/dist/cli.d.ts +0 -0
- package/dist/cli.js +115 -0
- package/dist/index.cjs +475 -0
- package/dist/index.d.ts +301 -0
- package/dist/index.js +429 -0
- package/package.json +61 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
type Railroad<
|
|
2
|
+
OutputType,
|
|
3
|
+
ErrorType = Error
|
|
4
|
+
> = {
|
|
5
|
+
readonly success: true
|
|
6
|
+
readonly data: OutputType
|
|
7
|
+
} | {
|
|
8
|
+
readonly success: false
|
|
9
|
+
readonly error: ErrorType
|
|
10
|
+
};
|
|
11
|
+
declare abstract class Task<
|
|
12
|
+
InputType,
|
|
13
|
+
OutputType,
|
|
14
|
+
IdentType extends string
|
|
15
|
+
> {
|
|
16
|
+
ident: IdentType;
|
|
17
|
+
constructor(ident: IdentType);
|
|
18
|
+
abstract run(input: InputType): Promise<Railroad<OutputType>>;
|
|
19
|
+
}
|
|
20
|
+
type InputBuilder<
|
|
21
|
+
SourceType,
|
|
22
|
+
AllOutputTypes,
|
|
23
|
+
TaskInputType
|
|
24
|
+
> = (source: SourceType, outputs: AllOutputTypes) => TaskInputType;
|
|
25
|
+
interface Machine<
|
|
26
|
+
SourceType,
|
|
27
|
+
AllOutputTypes,
|
|
28
|
+
TaskInputType,
|
|
29
|
+
TaskOutputType,
|
|
30
|
+
IdentType extends string
|
|
31
|
+
> {
|
|
32
|
+
task: Task<TaskInputType, TaskOutputType, IdentType>;
|
|
33
|
+
builder: InputBuilder<SourceType, AllOutputTypes, TaskInputType>;
|
|
34
|
+
}
|
|
35
|
+
type NoInfer<T> = [T][T extends any ? 0 : never];
|
|
36
|
+
declare class Playlist<
|
|
37
|
+
AllOutputTypes extends Record<string, any>,
|
|
38
|
+
SourceType = unknown
|
|
39
|
+
> {
|
|
40
|
+
machines: Machine<any, any, any, any, string>[];
|
|
41
|
+
finalizer?: (source: SourceType, outputs: Record<string, any>) => void | Promise<void>;
|
|
42
|
+
constructor(machines?: Machine<any, any, any, any, string>[], finalizer?: (source: SourceType, outputs: Record<string, any>) => void | Promise<void>);
|
|
43
|
+
addTask<
|
|
44
|
+
TaskInputType,
|
|
45
|
+
TaskOutputType,
|
|
46
|
+
const IdentType extends string
|
|
47
|
+
>(task: Task<TaskInputType, TaskOutputType, IdentType> & {
|
|
48
|
+
ident: IdentType
|
|
49
|
+
}, builder: (source: SourceType, outputs: AllOutputTypes) => NoInfer<TaskInputType>): Playlist<AllOutputTypes & { [K in IdentType] : Railroad<TaskOutputType> }, SourceType>;
|
|
50
|
+
finally(finalizer: (source: SourceType, outputs: AllOutputTypes) => void | Promise<void>): this;
|
|
51
|
+
run(source: SourceType): Promise<AllOutputTypes>;
|
|
52
|
+
}
|
|
53
|
+
type TriggerEvent<
|
|
54
|
+
IdentType extends string,
|
|
55
|
+
T
|
|
56
|
+
> = {
|
|
57
|
+
triggerIdent: IdentType
|
|
58
|
+
data: T
|
|
59
|
+
};
|
|
60
|
+
declare class EventQueue<TEventType> {
|
|
61
|
+
size: number;
|
|
62
|
+
queue: TEventType[];
|
|
63
|
+
constructor(size: number);
|
|
64
|
+
push(item: TEventType): void;
|
|
65
|
+
shift(): TEventType | undefined;
|
|
66
|
+
get length(): number;
|
|
67
|
+
}
|
|
68
|
+
declare abstract class Trigger<
|
|
69
|
+
IdentType extends string,
|
|
70
|
+
TData
|
|
71
|
+
> {
|
|
72
|
+
readonly ident: IdentType;
|
|
73
|
+
protected readonly queue: EventQueue<TriggerEvent<IdentType, TData>>;
|
|
74
|
+
constructor(ident: IdentType, queueSize?: number);
|
|
75
|
+
abstract stop(): Promise<void>;
|
|
76
|
+
protected pushEvent(data: TData): void;
|
|
77
|
+
poll(): TriggerEvent<IdentType, TData> | null;
|
|
78
|
+
}
|
|
79
|
+
declare class Workflow<
|
|
80
|
+
AllTriggerEvents extends TriggerEvent<string, any>,
|
|
81
|
+
TAllOutputs extends Record<string, any>,
|
|
82
|
+
TPlaylist extends Playlist<TAllOutputs, AllTriggerEvents> | null
|
|
83
|
+
> {
|
|
84
|
+
playlist: TPlaylist;
|
|
85
|
+
triggers: Trigger<string, any>[];
|
|
86
|
+
constructor(triggers: Trigger<string, any>[], playlist: TPlaylist);
|
|
87
|
+
addTrigger<
|
|
88
|
+
const TIdent extends string,
|
|
89
|
+
TData
|
|
90
|
+
>(trigger: Trigger<TIdent, TData>): Workflow<AllTriggerEvents | TriggerEvent<TIdent, TData>, TAllOutputs, Playlist<TAllOutputs, AllTriggerEvents | TriggerEvent<TIdent, TData>> | null>;
|
|
91
|
+
setPlaylist<
|
|
92
|
+
TBuilderOutputs extends Record<string, any>,
|
|
93
|
+
TFinalPlaylist extends Playlist<TBuilderOutputs, AllTriggerEvents>
|
|
94
|
+
>(builder: (p: Playlist<{}, AllTriggerEvents>) => TFinalPlaylist): Workflow<AllTriggerEvents, TBuilderOutputs, TFinalPlaylist>;
|
|
95
|
+
start({ interval, callback }?: {
|
|
96
|
+
interval?: number
|
|
97
|
+
callback?: (source: AllTriggerEvents, outputs: TAllOutputs) => any
|
|
98
|
+
}): Promise<void>;
|
|
99
|
+
static create(): Workflow<never, {}, null>;
|
|
100
|
+
}
|
|
101
|
+
import { Logger } from "pino";
|
|
102
|
+
type Transition<TStateData> = {
|
|
103
|
+
to: StateNode<TStateData> | null
|
|
104
|
+
condition: (stateData: TStateData) => Promise<boolean>
|
|
105
|
+
weight?: number
|
|
106
|
+
};
|
|
107
|
+
/**
|
|
108
|
+
* A node in the finite state machine.
|
|
109
|
+
* Each node owns a `Playlist` that is executed upon entering the node and
|
|
110
|
+
* contains weighted conditional transitions to other nodes.
|
|
111
|
+
*
|
|
112
|
+
* @template TStateData - The shape of the external mutable state carried through the machine.
|
|
113
|
+
*/
|
|
114
|
+
declare class StateNode<TStateData> {
|
|
115
|
+
transitions?: Transition<TStateData>[];
|
|
116
|
+
playlist: Playlist<any, TStateData>;
|
|
117
|
+
timeToNextTick: number;
|
|
118
|
+
ident: string;
|
|
119
|
+
tempTransitions?: {
|
|
120
|
+
to: string
|
|
121
|
+
condition: (stateData: TStateData) => Promise<boolean>
|
|
122
|
+
weight: number
|
|
123
|
+
}[];
|
|
124
|
+
retry: false | number;
|
|
125
|
+
maxRetries: false | number;
|
|
126
|
+
/**
|
|
127
|
+
* Create a `StateNode`.
|
|
128
|
+
*
|
|
129
|
+
* @param transitions - The resolved transitions from this node.
|
|
130
|
+
* @param playlist - The playlist to run when the node is entered.
|
|
131
|
+
*/
|
|
132
|
+
constructor(transitions: Transition<TStateData>[], playlist: Playlist<any, TStateData>);
|
|
133
|
+
/**
|
|
134
|
+
* Convenience factory for a new `StateNode` with no transitions and an empty playlist.
|
|
135
|
+
*
|
|
136
|
+
* @template TStateData
|
|
137
|
+
* @returns A new, unconfigured `StateNode`.
|
|
138
|
+
*/
|
|
139
|
+
static create<TStateData>(): StateNode<TStateData>;
|
|
140
|
+
/**
|
|
141
|
+
* Queue a transition to be resolved later during machine finalization.
|
|
142
|
+
* Use the target node `ident` instead of a direct reference; it will be
|
|
143
|
+
* resolved to a node instance by the machine.
|
|
144
|
+
*
|
|
145
|
+
* @param to - Target state `ident`.
|
|
146
|
+
* @param condition - Async predicate that decides if the transition should fire.
|
|
147
|
+
* @param weight - Higher weight wins when multiple conditions are true; ties keep insertion order.
|
|
148
|
+
* @returns This node for chaining.
|
|
149
|
+
*/
|
|
150
|
+
addTransition({ to, condition, weight }: {
|
|
151
|
+
to: string
|
|
152
|
+
condition: (stateData: TStateData) => Promise<boolean>
|
|
153
|
+
weight: number
|
|
154
|
+
}): StateNode<TStateData>;
|
|
155
|
+
/**
|
|
156
|
+
* Set or build the playlist that runs when entering this node.
|
|
157
|
+
*
|
|
158
|
+
* Overload 1: supply an already constructed `Playlist`.
|
|
159
|
+
* Overload 2: supply a builder function receiving an empty `Playlist` and returning a configured one.
|
|
160
|
+
*
|
|
161
|
+
* @param arg - Either a `Playlist` instance or a builder function that returns one.
|
|
162
|
+
* @returns This node for chaining.
|
|
163
|
+
*/
|
|
164
|
+
setPlaylist(playlist: Playlist<any, TStateData>): StateNode<TStateData>;
|
|
165
|
+
setPlaylist<
|
|
166
|
+
TBuilderOutputs extends Record<string, any>,
|
|
167
|
+
TFinalPlaylist extends Playlist<TBuilderOutputs, TStateData>
|
|
168
|
+
>(builder: (p: Playlist<{}, TStateData>) => TFinalPlaylist): StateNode<TStateData>;
|
|
169
|
+
/**
|
|
170
|
+
* Disable retry behavior for this node during `Machine.run` when no transition is available.
|
|
171
|
+
*
|
|
172
|
+
* @returns This node for chaining.
|
|
173
|
+
*/
|
|
174
|
+
preventRetry(): StateNode<TStateData>;
|
|
175
|
+
/**
|
|
176
|
+
* Set the delay between retry attempts for this node during `Machine.run`.
|
|
177
|
+
*
|
|
178
|
+
* @param delayMs - Delay in milliseconds between retries.
|
|
179
|
+
* @returns This node for chaining.
|
|
180
|
+
*/
|
|
181
|
+
retryDelayMs(delayMs: number): StateNode<TStateData>;
|
|
182
|
+
/**
|
|
183
|
+
* Set the maximum number of retries for this node during `Machine.run`.
|
|
184
|
+
* Use `preventRetry()` to disable retries entirely.
|
|
185
|
+
*
|
|
186
|
+
* @param maxRetries - Maximum number of retry attempts before giving up.
|
|
187
|
+
* @returns This node for chaining.
|
|
188
|
+
*/
|
|
189
|
+
retryLimit(maxRetries: number): StateNode<TStateData>;
|
|
190
|
+
/**
|
|
191
|
+
* Assign a unique identifier for this node. Required for transition resolution.
|
|
192
|
+
*
|
|
193
|
+
* @param ident - Unique node identifier.
|
|
194
|
+
* @returns This node for chaining.
|
|
195
|
+
*/
|
|
196
|
+
setIdent(ident: string): StateNode<TStateData>;
|
|
197
|
+
/**
|
|
198
|
+
* Depth-first search for a node by `ident` within the reachable subgraph from this node.
|
|
199
|
+
*
|
|
200
|
+
* @param ident - Identifier to search for.
|
|
201
|
+
* @param visited - Internal cycle-prevention list; callers can ignore.
|
|
202
|
+
* @returns The matching node or `null` if not found.
|
|
203
|
+
*/
|
|
204
|
+
getByIdent(ident: string, visited?: string[]): StateNode<TStateData> | null;
|
|
205
|
+
/**
|
|
206
|
+
* Evaluate transitions and select the next node.
|
|
207
|
+
* Transitions are considered in order of descending weight, with insertion order as a tie-breaker.
|
|
208
|
+
*
|
|
209
|
+
* @param data - Current external state used by transition conditions.
|
|
210
|
+
* @returns The next node if a condition passes; otherwise `null`.
|
|
211
|
+
*/
|
|
212
|
+
next(data: TStateData): Promise<StateNode<TStateData> | null>;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* A finite state machine that coordinates execution of `StateNode` playlists
|
|
216
|
+
* and transitions between them based on async conditions.
|
|
217
|
+
*
|
|
218
|
+
* @template TStateData - The shape of the external mutable state carried through the machine.
|
|
219
|
+
*/
|
|
220
|
+
declare class Machine2<TStateData> {
|
|
221
|
+
initialState: StateNode<TStateData> | null;
|
|
222
|
+
statesToCreate: StateNode<TStateData>[];
|
|
223
|
+
private currentState;
|
|
224
|
+
finalized: boolean;
|
|
225
|
+
logger?: Logger;
|
|
226
|
+
ident?: string;
|
|
227
|
+
/**
|
|
228
|
+
* Compute the set of reachable states starting from the initial state.
|
|
229
|
+
* Used by `run` to determine completion once all states have been visited.
|
|
230
|
+
*
|
|
231
|
+
* @returns An array of reachable `StateNode`s.
|
|
232
|
+
*/
|
|
233
|
+
private getAllStates;
|
|
234
|
+
/**
|
|
235
|
+
* Sleep helper used for retry delays.
|
|
236
|
+
*
|
|
237
|
+
* @param ms - Milliseconds to wait.
|
|
238
|
+
* @returns A promise that resolves after the delay.
|
|
239
|
+
*/
|
|
240
|
+
private sleep;
|
|
241
|
+
/**
|
|
242
|
+
* Convenience factory for a new `Machine`.
|
|
243
|
+
*
|
|
244
|
+
* @template TStateData
|
|
245
|
+
* @returns A new unfinalized machine instance.
|
|
246
|
+
*/
|
|
247
|
+
static create<TStateData>(): Machine2<TStateData>;
|
|
248
|
+
/**
|
|
249
|
+
* Finalize the machine by resolving state transitions and locking configuration.
|
|
250
|
+
* Must be called before `start` or `run`.
|
|
251
|
+
*
|
|
252
|
+
* @param options - Finalization options.
|
|
253
|
+
* @param options.verbose - Enable pino logging for the machine lifecycle.
|
|
254
|
+
* @param options.ident - Optional fixed identifier; if omitted, a UUID is generated.
|
|
255
|
+
* @returns This machine for chaining.
|
|
256
|
+
* @throws If there is no initial state, no states have been added, a state is missing an ident, or idents are duplicated.
|
|
257
|
+
*/
|
|
258
|
+
finalize({ verbose, ident }?: {
|
|
259
|
+
verbose?: boolean
|
|
260
|
+
ident?: string
|
|
261
|
+
}): Machine2<TStateData>;
|
|
262
|
+
/**
|
|
263
|
+
* Add a state to the machine.
|
|
264
|
+
*
|
|
265
|
+
* @param state - The state node to add.
|
|
266
|
+
* @param options - Options controlling how the state is added.
|
|
267
|
+
* @param options.initial - If true, marks this state as the initial state.
|
|
268
|
+
* @returns This machine for chaining.
|
|
269
|
+
*/
|
|
270
|
+
addState(state: StateNode<TStateData>, options?: {
|
|
271
|
+
initial?: boolean
|
|
272
|
+
}): Machine2<TStateData>;
|
|
273
|
+
/**
|
|
274
|
+
* Start the machine in a repeated tick loop. Executes the current state's playlist,
|
|
275
|
+
* evaluates transitions, and schedules the next tick with `setTimeout`.
|
|
276
|
+
* The method returns immediately after initializing the loop.
|
|
277
|
+
*
|
|
278
|
+
* @param stateData - Mutable external state provided to playlists and transition conditions.
|
|
279
|
+
* @param options - Start options.
|
|
280
|
+
* @param options.interval - Tick interval in milliseconds (default 1000ms).
|
|
281
|
+
* @returns A promise that resolves once the loop has been initiated.
|
|
282
|
+
* @throws If the machine has not been finalized or no initial state is set.
|
|
283
|
+
*/
|
|
284
|
+
start(stateData: TStateData, options?: {
|
|
285
|
+
interval?: number
|
|
286
|
+
}): Promise<void>;
|
|
287
|
+
/**
|
|
288
|
+
* Run the machine synchronously until it reaches a terminal condition:
|
|
289
|
+
* - A leaf state (no transitions)
|
|
290
|
+
* - No transition and retries disabled
|
|
291
|
+
* - Retry limit exhausted
|
|
292
|
+
* - Returning to the initial state
|
|
293
|
+
* - All reachable states have been visited
|
|
294
|
+
*
|
|
295
|
+
* @param stateData - Mutable external state provided to playlists and transition conditions.
|
|
296
|
+
* @returns A promise that resolves once the run completes.
|
|
297
|
+
* @throws If the machine has not been finalized or no initial state is set.
|
|
298
|
+
*/
|
|
299
|
+
run(stateData: TStateData): Promise<TStateData>;
|
|
300
|
+
}
|
|
301
|
+
export { Workflow, TriggerEvent, Trigger, Task, StateNode, Railroad, Playlist, Machine2 as Machine };
|