@unlaxer/tramli 3.3.0 → 3.5.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/cjs/data-flow-graph.d.ts +1 -1
- package/dist/cjs/data-flow-graph.js +9 -4
- package/dist/cjs/flow-definition.d.ts +3 -0
- package/dist/cjs/flow-definition.js +13 -1
- package/dist/cjs/flow-engine.d.ts +4 -0
- package/dist/cjs/flow-engine.js +16 -12
- package/dist/cjs/pipeline.js +1 -1
- package/dist/cjs/types.d.ts +2 -0
- package/dist/esm/data-flow-graph.d.ts +1 -1
- package/dist/esm/data-flow-graph.js +9 -4
- package/dist/esm/flow-definition.d.ts +3 -0
- package/dist/esm/flow-definition.js +13 -1
- package/dist/esm/flow-engine.d.ts +4 -0
- package/dist/esm/flow-engine.js +16 -12
- package/dist/esm/pipeline.js +1 -1
- package/dist/esm/types.d.ts +2 -0
- package/package.json +1 -1
|
@@ -88,5 +88,5 @@ export declare class DataFlowGraph<S extends string> {
|
|
|
88
88
|
private static collectEdges;
|
|
89
89
|
/** Version compatibility: check if v1 instances can resume on v2 definition. */
|
|
90
90
|
static versionCompatibility<S extends string>(before: DataFlowGraph<S>, after: DataFlowGraph<S>): string[];
|
|
91
|
-
static build<S extends string>(def: FlowDefinition<S>, initiallyAvailable: string[]): DataFlowGraph<S>;
|
|
91
|
+
static build<S extends string>(def: FlowDefinition<S>, initiallyAvailable: string[], externallyProvided?: string[]): DataFlowGraph<S>;
|
|
92
92
|
}
|
|
@@ -370,14 +370,15 @@ class DataFlowGraph {
|
|
|
370
370
|
return issues;
|
|
371
371
|
}
|
|
372
372
|
// ─── Builder ─────────────────────────────────────────────
|
|
373
|
-
static build(def, initiallyAvailable) {
|
|
373
|
+
static build(def, initiallyAvailable, externallyProvided = []) {
|
|
374
374
|
const stateAvail = new Map();
|
|
375
375
|
const producers = new Map();
|
|
376
376
|
const consumers = new Map();
|
|
377
377
|
const allProduced = new Set(initiallyAvailable);
|
|
378
378
|
const allConsumed = new Set();
|
|
379
|
+
const extSet = new Set(externallyProvided);
|
|
379
380
|
if (def.initialState) {
|
|
380
|
-
traverse(def, def.initialState, new Set(initiallyAvailable), stateAvail, producers, consumers, allProduced, allConsumed);
|
|
381
|
+
traverse(def, def.initialState, new Set(initiallyAvailable), extSet, stateAvail, producers, consumers, allProduced, allConsumed);
|
|
381
382
|
// Mark initially available types as produced by "initial"
|
|
382
383
|
for (const key of initiallyAvailable) {
|
|
383
384
|
if (!producers.has(key))
|
|
@@ -391,7 +392,7 @@ class DataFlowGraph {
|
|
|
391
392
|
}
|
|
392
393
|
}
|
|
393
394
|
exports.DataFlowGraph = DataFlowGraph;
|
|
394
|
-
function traverse(def, state, available, stateAvail, producers, consumers, allProduced, allConsumed) {
|
|
395
|
+
function traverse(def, state, available, externallyProvided, stateAvail, producers, consumers, allProduced, allConsumed) {
|
|
395
396
|
if (stateAvail.has(state)) {
|
|
396
397
|
const existing = stateAvail.get(state);
|
|
397
398
|
let isSubset = true;
|
|
@@ -413,6 +414,10 @@ function traverse(def, state, available, stateAvail, producers, consumers, allPr
|
|
|
413
414
|
}
|
|
414
415
|
for (const t of def.transitionsFrom(state)) {
|
|
415
416
|
const newAvail = new Set(stateAvail.get(state));
|
|
417
|
+
if (t.type === 'external') {
|
|
418
|
+
for (const k of externallyProvided)
|
|
419
|
+
newAvail.add(k);
|
|
420
|
+
}
|
|
416
421
|
if (t.guard) {
|
|
417
422
|
for (const req of t.guard.requires) {
|
|
418
423
|
addTo(consumers, req, { name: t.guard.name, fromState: t.from, toState: t.to, kind: 'guard' });
|
|
@@ -441,7 +446,7 @@ function traverse(def, state, available, stateAvail, producers, consumers, allPr
|
|
|
441
446
|
newAvail.add(prod);
|
|
442
447
|
}
|
|
443
448
|
}
|
|
444
|
-
traverse(def, t.to, newAvail, stateAvail, producers, consumers, allProduced, allConsumed);
|
|
449
|
+
traverse(def, t.to, newAvail, externallyProvided, stateAvail, producers, consumers, allProduced, allConsumed);
|
|
445
450
|
}
|
|
446
451
|
}
|
|
447
452
|
function addTo(map, key, info) {
|
|
@@ -45,9 +45,12 @@ export declare class Builder<S extends string> {
|
|
|
45
45
|
private readonly _enterActions;
|
|
46
46
|
private readonly _exitActions;
|
|
47
47
|
private readonly initiallyAvailableKeys;
|
|
48
|
+
private readonly externallyProvidedKeys;
|
|
48
49
|
private _perpetual;
|
|
49
50
|
constructor(name: string, stateConfig: Record<S, StateConfig>);
|
|
50
51
|
initiallyAvailable(...keys: FlowKey<unknown>[]): this;
|
|
52
|
+
/** Declare data keys injected via resumeAndExecute(externalData), not available at start. */
|
|
53
|
+
externallyProvided(...keys: FlowKey<unknown>[]): this;
|
|
51
54
|
setTtl(ms: number): this;
|
|
52
55
|
setMaxGuardRetries(max: number): this;
|
|
53
56
|
from(state: S): FromBuilder<S>;
|
|
@@ -113,6 +113,7 @@ class Builder {
|
|
|
113
113
|
_enterActions = new Map();
|
|
114
114
|
_exitActions = new Map();
|
|
115
115
|
initiallyAvailableKeys = [];
|
|
116
|
+
externallyProvidedKeys = [];
|
|
116
117
|
_perpetual = false;
|
|
117
118
|
constructor(name, stateConfig) {
|
|
118
119
|
this.name = name;
|
|
@@ -123,6 +124,12 @@ class Builder {
|
|
|
123
124
|
this.initiallyAvailableKeys.push(k);
|
|
124
125
|
return this;
|
|
125
126
|
}
|
|
127
|
+
/** Declare data keys injected via resumeAndExecute(externalData), not available at start. */
|
|
128
|
+
externallyProvided(...keys) {
|
|
129
|
+
for (const k of keys)
|
|
130
|
+
this.externallyProvidedKeys.push(k);
|
|
131
|
+
return this;
|
|
132
|
+
}
|
|
126
133
|
setTtl(ms) { this.ttl = ms; return this; }
|
|
127
134
|
setMaxGuardRetries(max) { this.maxGuardRetries = max; return this; }
|
|
128
135
|
from(state) {
|
|
@@ -185,7 +192,7 @@ class Builder {
|
|
|
185
192
|
result.terminalStates = terminals;
|
|
186
193
|
result.dataFlowGraph = null;
|
|
187
194
|
this.validate(result);
|
|
188
|
-
result.dataFlowGraph = data_flow_graph_js_1.DataFlowGraph.build(result, this.initiallyAvailableKeys);
|
|
195
|
+
result.dataFlowGraph = data_flow_graph_js_1.DataFlowGraph.build(result, this.initiallyAvailableKeys, this.externallyProvidedKeys);
|
|
189
196
|
// Build warnings
|
|
190
197
|
const warnings = [];
|
|
191
198
|
const perpetual = terminals.size === 0;
|
|
@@ -354,6 +361,10 @@ class Builder {
|
|
|
354
361
|
}
|
|
355
362
|
for (const t of def.transitionsFrom(state)) {
|
|
356
363
|
const newAvailable = new Set(stateAvailable.get(state));
|
|
364
|
+
if (t.type === 'external') {
|
|
365
|
+
for (const k of this.externallyProvidedKeys)
|
|
366
|
+
newAvailable.add(k);
|
|
367
|
+
}
|
|
357
368
|
if (t.guard) {
|
|
358
369
|
for (const req of t.guard.requires) {
|
|
359
370
|
if (!newAvailable.has(req))
|
|
@@ -534,6 +545,7 @@ class BranchBuilder {
|
|
|
534
545
|
processor: this.processors.get(label),
|
|
535
546
|
guard: undefined, branch: this.branch,
|
|
536
547
|
branchTargets: new Map(this.targets),
|
|
548
|
+
branchLabel: label,
|
|
537
549
|
});
|
|
538
550
|
}
|
|
539
551
|
return this.builder;
|
|
@@ -53,6 +53,10 @@ export declare class FlowEngine {
|
|
|
53
53
|
setStateLogger(logger: ((entry: StateLogEntry) => void) | null): void;
|
|
54
54
|
setErrorLogger(logger: ((entry: ErrorLogEntry) => void) | null): void;
|
|
55
55
|
setGuardLogger(logger: ((entry: GuardLogEntry) => void) | null): void;
|
|
56
|
+
getTransitionLogger(): ((entry: TransitionLogEntry) => void) | undefined;
|
|
57
|
+
getStateLogger(): ((entry: StateLogEntry) => void) | undefined;
|
|
58
|
+
getErrorLogger(): ((entry: ErrorLogEntry) => void) | undefined;
|
|
59
|
+
getGuardLogger(): ((entry: GuardLogEntry) => void) | undefined;
|
|
56
60
|
removeAllLoggers(): void;
|
|
57
61
|
startFlow<S extends string>(definition: FlowDefinition<S>, sessionId: string, initialData: Map<string, unknown>): Promise<FlowInstance<S>>;
|
|
58
62
|
resumeAndExecute<S extends string>(flowId: string, definition: FlowDefinition<S>, externalData?: Map<string, unknown>): Promise<FlowInstance<S>>;
|
package/dist/cjs/flow-engine.js
CHANGED
|
@@ -31,6 +31,10 @@ class FlowEngine {
|
|
|
31
31
|
setGuardLogger(logger) {
|
|
32
32
|
this.guardLogger = logger ?? undefined;
|
|
33
33
|
}
|
|
34
|
+
getTransitionLogger() { return this.transitionLogger; }
|
|
35
|
+
getStateLogger() { return this.stateLogger; }
|
|
36
|
+
getErrorLogger() { return this.errorLogger; }
|
|
37
|
+
getGuardLogger() { return this.guardLogger; }
|
|
34
38
|
removeAllLoggers() {
|
|
35
39
|
this.transitionLogger = undefined;
|
|
36
40
|
this.stateLogger = undefined;
|
|
@@ -97,13 +101,13 @@ class FlowEngine {
|
|
|
97
101
|
}
|
|
98
102
|
const guard = transition.guard;
|
|
99
103
|
if (guard) {
|
|
100
|
-
const guardStart =
|
|
104
|
+
const guardStart = performance.now();
|
|
101
105
|
const output = await guard.validate(flow.context);
|
|
102
|
-
const guardDurationMicros =
|
|
106
|
+
const guardDurationMicros = Math.round((performance.now() - guardStart) * 1000);
|
|
103
107
|
switch (output.type) {
|
|
104
108
|
case 'accepted': {
|
|
105
109
|
this.logGuard(flow, currentState, guard.name, 'accepted', guardDurationMicros);
|
|
106
|
-
const transStart =
|
|
110
|
+
const transStart = performance.now();
|
|
107
111
|
const backup = flow.context.snapshot();
|
|
108
112
|
if (output.data) {
|
|
109
113
|
for (const [key, value] of output.data)
|
|
@@ -145,7 +149,7 @@ class FlowEngine {
|
|
|
145
149
|
}
|
|
146
150
|
}
|
|
147
151
|
else {
|
|
148
|
-
const transStart =
|
|
152
|
+
const transStart = performance.now();
|
|
149
153
|
const from = flow.currentState;
|
|
150
154
|
this.fireExit(flow, from);
|
|
151
155
|
flow.transitionTo(transition.to);
|
|
@@ -179,7 +183,7 @@ class FlowEngine {
|
|
|
179
183
|
if (!autoOrBranch)
|
|
180
184
|
break;
|
|
181
185
|
const backup = flow.context.snapshot();
|
|
182
|
-
const stepStart =
|
|
186
|
+
const stepStart = performance.now();
|
|
183
187
|
try {
|
|
184
188
|
if (autoOrBranch.type === 'auto') {
|
|
185
189
|
if (autoOrBranch.processor) {
|
|
@@ -201,7 +205,7 @@ class FlowEngine {
|
|
|
201
205
|
if (!target) {
|
|
202
206
|
throw new flow_error_js_1.FlowError('UNKNOWN_BRANCH', `Branch '${branch.name}' returned unknown label: ${label}`);
|
|
203
207
|
}
|
|
204
|
-
const specific = transitions.find(t => t.type === 'branch' && t.to === target) ?? autoOrBranch;
|
|
208
|
+
const specific = transitions.find(t => t.type === 'branch' && t.branchLabel === label) ?? transitions.find(t => t.type === 'branch' && t.to === target) ?? autoOrBranch;
|
|
205
209
|
if (specific.processor)
|
|
206
210
|
await specific.processor.process(flow.context);
|
|
207
211
|
const from = flow.currentState;
|
|
@@ -234,7 +238,7 @@ class FlowEngine {
|
|
|
234
238
|
parentFlow.setActiveSubFlow(null);
|
|
235
239
|
const target = exitMappings.get(subFlow.exitState);
|
|
236
240
|
if (target) {
|
|
237
|
-
const sfStart =
|
|
241
|
+
const sfStart = performance.now();
|
|
238
242
|
const from = parentFlow.currentState;
|
|
239
243
|
this.fireExit(parentFlow, from);
|
|
240
244
|
parentFlow.transitionTo(target);
|
|
@@ -259,15 +263,15 @@ class FlowEngine {
|
|
|
259
263
|
}
|
|
260
264
|
const guard = transition.guard;
|
|
261
265
|
if (guard) {
|
|
262
|
-
const guardStart =
|
|
266
|
+
const guardStart = performance.now();
|
|
263
267
|
const output = await guard.validate(parentFlow.context);
|
|
264
|
-
const guardDur =
|
|
268
|
+
const guardDur = Math.round((performance.now() - guardStart) * 1000);
|
|
265
269
|
if (output.type === 'accepted') {
|
|
266
270
|
if (output.data) {
|
|
267
271
|
for (const [key, value] of output.data)
|
|
268
272
|
parentFlow.context.put(key, value);
|
|
269
273
|
}
|
|
270
|
-
const sfStart =
|
|
274
|
+
const sfStart = performance.now();
|
|
271
275
|
const sfFrom = subFlow.currentState;
|
|
272
276
|
subFlow.transitionTo(transition.to);
|
|
273
277
|
this.store.recordTransition(parentFlow.id, sfFrom, transition.to, guard.name, parentFlow.context);
|
|
@@ -299,7 +303,7 @@ class FlowEngine {
|
|
|
299
303
|
if (subFlowT?.exitMappings) {
|
|
300
304
|
const target = subFlowT.exitMappings.get(subFlow.exitState);
|
|
301
305
|
if (target) {
|
|
302
|
-
const exitStart =
|
|
306
|
+
const exitStart = performance.now();
|
|
303
307
|
const from = parentFlow.currentState;
|
|
304
308
|
this.fireExit(parentFlow, from);
|
|
305
309
|
parentFlow.transitionTo(target);
|
|
@@ -349,7 +353,7 @@ class FlowEngine {
|
|
|
349
353
|
this.guardLogger?.({ flowId: flow.id, flowName: flow.definition.name, state, guardName, result, reason, durationMicros });
|
|
350
354
|
}
|
|
351
355
|
handleError(flow, fromState, cause) {
|
|
352
|
-
const errorStart =
|
|
356
|
+
const errorStart = performance.now();
|
|
353
357
|
if (cause) {
|
|
354
358
|
flow.setLastError(`${cause.constructor.name}: ${cause.message}`);
|
|
355
359
|
if (cause instanceof flow_error_js_1.FlowError) {
|
package/dist/cjs/pipeline.js
CHANGED
|
@@ -92,7 +92,7 @@ class Pipeline {
|
|
|
92
92
|
const completed = [];
|
|
93
93
|
let prev = 'initial';
|
|
94
94
|
for (const step of this.steps) {
|
|
95
|
-
const stepStart =
|
|
95
|
+
const stepStart = performance.now();
|
|
96
96
|
this.transitionLogger?.({ flowId, flowName: this.name, from: prev, to: step.name, trigger: step.name, durationMicros: 0 });
|
|
97
97
|
const keysBefore = this.stateLogger ? new Set(ctx.snapshot().keys()) : null;
|
|
98
98
|
try {
|
package/dist/cjs/types.d.ts
CHANGED
|
@@ -26,6 +26,8 @@ export interface Transition<S extends string> {
|
|
|
26
26
|
guard?: TransitionGuard<S>;
|
|
27
27
|
branch?: BranchProcessor<S>;
|
|
28
28
|
branchTargets: Map<string, S>;
|
|
29
|
+
/** Label assigned by builder .to(target, label, processor). Used for branch label-specific processor matching. */
|
|
30
|
+
branchLabel?: string;
|
|
29
31
|
subFlowDefinition?: import('./flow-definition.js').FlowDefinition<any>;
|
|
30
32
|
exitMappings?: Map<string, S>;
|
|
31
33
|
/** Per-state timeout in milliseconds. If set, resumeAndExecute checks this before guard. */
|
|
@@ -88,5 +88,5 @@ export declare class DataFlowGraph<S extends string> {
|
|
|
88
88
|
private static collectEdges;
|
|
89
89
|
/** Version compatibility: check if v1 instances can resume on v2 definition. */
|
|
90
90
|
static versionCompatibility<S extends string>(before: DataFlowGraph<S>, after: DataFlowGraph<S>): string[];
|
|
91
|
-
static build<S extends string>(def: FlowDefinition<S>, initiallyAvailable: string[]): DataFlowGraph<S>;
|
|
91
|
+
static build<S extends string>(def: FlowDefinition<S>, initiallyAvailable: string[], externallyProvided?: string[]): DataFlowGraph<S>;
|
|
92
92
|
}
|
|
@@ -367,14 +367,15 @@ export class DataFlowGraph {
|
|
|
367
367
|
return issues;
|
|
368
368
|
}
|
|
369
369
|
// ─── Builder ─────────────────────────────────────────────
|
|
370
|
-
static build(def, initiallyAvailable) {
|
|
370
|
+
static build(def, initiallyAvailable, externallyProvided = []) {
|
|
371
371
|
const stateAvail = new Map();
|
|
372
372
|
const producers = new Map();
|
|
373
373
|
const consumers = new Map();
|
|
374
374
|
const allProduced = new Set(initiallyAvailable);
|
|
375
375
|
const allConsumed = new Set();
|
|
376
|
+
const extSet = new Set(externallyProvided);
|
|
376
377
|
if (def.initialState) {
|
|
377
|
-
traverse(def, def.initialState, new Set(initiallyAvailable), stateAvail, producers, consumers, allProduced, allConsumed);
|
|
378
|
+
traverse(def, def.initialState, new Set(initiallyAvailable), extSet, stateAvail, producers, consumers, allProduced, allConsumed);
|
|
378
379
|
// Mark initially available types as produced by "initial"
|
|
379
380
|
for (const key of initiallyAvailable) {
|
|
380
381
|
if (!producers.has(key))
|
|
@@ -387,7 +388,7 @@ export class DataFlowGraph {
|
|
|
387
388
|
return new DataFlowGraph(stateAvail, producers, consumers, allProduced, allConsumed);
|
|
388
389
|
}
|
|
389
390
|
}
|
|
390
|
-
function traverse(def, state, available, stateAvail, producers, consumers, allProduced, allConsumed) {
|
|
391
|
+
function traverse(def, state, available, externallyProvided, stateAvail, producers, consumers, allProduced, allConsumed) {
|
|
391
392
|
if (stateAvail.has(state)) {
|
|
392
393
|
const existing = stateAvail.get(state);
|
|
393
394
|
let isSubset = true;
|
|
@@ -409,6 +410,10 @@ function traverse(def, state, available, stateAvail, producers, consumers, allPr
|
|
|
409
410
|
}
|
|
410
411
|
for (const t of def.transitionsFrom(state)) {
|
|
411
412
|
const newAvail = new Set(stateAvail.get(state));
|
|
413
|
+
if (t.type === 'external') {
|
|
414
|
+
for (const k of externallyProvided)
|
|
415
|
+
newAvail.add(k);
|
|
416
|
+
}
|
|
412
417
|
if (t.guard) {
|
|
413
418
|
for (const req of t.guard.requires) {
|
|
414
419
|
addTo(consumers, req, { name: t.guard.name, fromState: t.from, toState: t.to, kind: 'guard' });
|
|
@@ -437,7 +442,7 @@ function traverse(def, state, available, stateAvail, producers, consumers, allPr
|
|
|
437
442
|
newAvail.add(prod);
|
|
438
443
|
}
|
|
439
444
|
}
|
|
440
|
-
traverse(def, t.to, newAvail, stateAvail, producers, consumers, allProduced, allConsumed);
|
|
445
|
+
traverse(def, t.to, newAvail, externallyProvided, stateAvail, producers, consumers, allProduced, allConsumed);
|
|
441
446
|
}
|
|
442
447
|
}
|
|
443
448
|
function addTo(map, key, info) {
|
|
@@ -45,9 +45,12 @@ export declare class Builder<S extends string> {
|
|
|
45
45
|
private readonly _enterActions;
|
|
46
46
|
private readonly _exitActions;
|
|
47
47
|
private readonly initiallyAvailableKeys;
|
|
48
|
+
private readonly externallyProvidedKeys;
|
|
48
49
|
private _perpetual;
|
|
49
50
|
constructor(name: string, stateConfig: Record<S, StateConfig>);
|
|
50
51
|
initiallyAvailable(...keys: FlowKey<unknown>[]): this;
|
|
52
|
+
/** Declare data keys injected via resumeAndExecute(externalData), not available at start. */
|
|
53
|
+
externallyProvided(...keys: FlowKey<unknown>[]): this;
|
|
51
54
|
setTtl(ms: number): this;
|
|
52
55
|
setMaxGuardRetries(max: number): this;
|
|
53
56
|
from(state: S): FromBuilder<S>;
|
|
@@ -109,6 +109,7 @@ export class Builder {
|
|
|
109
109
|
_enterActions = new Map();
|
|
110
110
|
_exitActions = new Map();
|
|
111
111
|
initiallyAvailableKeys = [];
|
|
112
|
+
externallyProvidedKeys = [];
|
|
112
113
|
_perpetual = false;
|
|
113
114
|
constructor(name, stateConfig) {
|
|
114
115
|
this.name = name;
|
|
@@ -119,6 +120,12 @@ export class Builder {
|
|
|
119
120
|
this.initiallyAvailableKeys.push(k);
|
|
120
121
|
return this;
|
|
121
122
|
}
|
|
123
|
+
/** Declare data keys injected via resumeAndExecute(externalData), not available at start. */
|
|
124
|
+
externallyProvided(...keys) {
|
|
125
|
+
for (const k of keys)
|
|
126
|
+
this.externallyProvidedKeys.push(k);
|
|
127
|
+
return this;
|
|
128
|
+
}
|
|
122
129
|
setTtl(ms) { this.ttl = ms; return this; }
|
|
123
130
|
setMaxGuardRetries(max) { this.maxGuardRetries = max; return this; }
|
|
124
131
|
from(state) {
|
|
@@ -181,7 +188,7 @@ export class Builder {
|
|
|
181
188
|
result.terminalStates = terminals;
|
|
182
189
|
result.dataFlowGraph = null;
|
|
183
190
|
this.validate(result);
|
|
184
|
-
result.dataFlowGraph = DataFlowGraph.build(result, this.initiallyAvailableKeys);
|
|
191
|
+
result.dataFlowGraph = DataFlowGraph.build(result, this.initiallyAvailableKeys, this.externallyProvidedKeys);
|
|
185
192
|
// Build warnings
|
|
186
193
|
const warnings = [];
|
|
187
194
|
const perpetual = terminals.size === 0;
|
|
@@ -350,6 +357,10 @@ export class Builder {
|
|
|
350
357
|
}
|
|
351
358
|
for (const t of def.transitionsFrom(state)) {
|
|
352
359
|
const newAvailable = new Set(stateAvailable.get(state));
|
|
360
|
+
if (t.type === 'external') {
|
|
361
|
+
for (const k of this.externallyProvidedKeys)
|
|
362
|
+
newAvailable.add(k);
|
|
363
|
+
}
|
|
353
364
|
if (t.guard) {
|
|
354
365
|
for (const req of t.guard.requires) {
|
|
355
366
|
if (!newAvailable.has(req))
|
|
@@ -527,6 +538,7 @@ export class BranchBuilder {
|
|
|
527
538
|
processor: this.processors.get(label),
|
|
528
539
|
guard: undefined, branch: this.branch,
|
|
529
540
|
branchTargets: new Map(this.targets),
|
|
541
|
+
branchLabel: label,
|
|
530
542
|
});
|
|
531
543
|
}
|
|
532
544
|
return this.builder;
|
|
@@ -53,6 +53,10 @@ export declare class FlowEngine {
|
|
|
53
53
|
setStateLogger(logger: ((entry: StateLogEntry) => void) | null): void;
|
|
54
54
|
setErrorLogger(logger: ((entry: ErrorLogEntry) => void) | null): void;
|
|
55
55
|
setGuardLogger(logger: ((entry: GuardLogEntry) => void) | null): void;
|
|
56
|
+
getTransitionLogger(): ((entry: TransitionLogEntry) => void) | undefined;
|
|
57
|
+
getStateLogger(): ((entry: StateLogEntry) => void) | undefined;
|
|
58
|
+
getErrorLogger(): ((entry: ErrorLogEntry) => void) | undefined;
|
|
59
|
+
getGuardLogger(): ((entry: GuardLogEntry) => void) | undefined;
|
|
56
60
|
removeAllLoggers(): void;
|
|
57
61
|
startFlow<S extends string>(definition: FlowDefinition<S>, sessionId: string, initialData: Map<string, unknown>): Promise<FlowInstance<S>>;
|
|
58
62
|
resumeAndExecute<S extends string>(flowId: string, definition: FlowDefinition<S>, externalData?: Map<string, unknown>): Promise<FlowInstance<S>>;
|
package/dist/esm/flow-engine.js
CHANGED
|
@@ -28,6 +28,10 @@ export class FlowEngine {
|
|
|
28
28
|
setGuardLogger(logger) {
|
|
29
29
|
this.guardLogger = logger ?? undefined;
|
|
30
30
|
}
|
|
31
|
+
getTransitionLogger() { return this.transitionLogger; }
|
|
32
|
+
getStateLogger() { return this.stateLogger; }
|
|
33
|
+
getErrorLogger() { return this.errorLogger; }
|
|
34
|
+
getGuardLogger() { return this.guardLogger; }
|
|
31
35
|
removeAllLoggers() {
|
|
32
36
|
this.transitionLogger = undefined;
|
|
33
37
|
this.stateLogger = undefined;
|
|
@@ -94,13 +98,13 @@ export class FlowEngine {
|
|
|
94
98
|
}
|
|
95
99
|
const guard = transition.guard;
|
|
96
100
|
if (guard) {
|
|
97
|
-
const guardStart =
|
|
101
|
+
const guardStart = performance.now();
|
|
98
102
|
const output = await guard.validate(flow.context);
|
|
99
|
-
const guardDurationMicros =
|
|
103
|
+
const guardDurationMicros = Math.round((performance.now() - guardStart) * 1000);
|
|
100
104
|
switch (output.type) {
|
|
101
105
|
case 'accepted': {
|
|
102
106
|
this.logGuard(flow, currentState, guard.name, 'accepted', guardDurationMicros);
|
|
103
|
-
const transStart =
|
|
107
|
+
const transStart = performance.now();
|
|
104
108
|
const backup = flow.context.snapshot();
|
|
105
109
|
if (output.data) {
|
|
106
110
|
for (const [key, value] of output.data)
|
|
@@ -142,7 +146,7 @@ export class FlowEngine {
|
|
|
142
146
|
}
|
|
143
147
|
}
|
|
144
148
|
else {
|
|
145
|
-
const transStart =
|
|
149
|
+
const transStart = performance.now();
|
|
146
150
|
const from = flow.currentState;
|
|
147
151
|
this.fireExit(flow, from);
|
|
148
152
|
flow.transitionTo(transition.to);
|
|
@@ -176,7 +180,7 @@ export class FlowEngine {
|
|
|
176
180
|
if (!autoOrBranch)
|
|
177
181
|
break;
|
|
178
182
|
const backup = flow.context.snapshot();
|
|
179
|
-
const stepStart =
|
|
183
|
+
const stepStart = performance.now();
|
|
180
184
|
try {
|
|
181
185
|
if (autoOrBranch.type === 'auto') {
|
|
182
186
|
if (autoOrBranch.processor) {
|
|
@@ -198,7 +202,7 @@ export class FlowEngine {
|
|
|
198
202
|
if (!target) {
|
|
199
203
|
throw new FlowError('UNKNOWN_BRANCH', `Branch '${branch.name}' returned unknown label: ${label}`);
|
|
200
204
|
}
|
|
201
|
-
const specific = transitions.find(t => t.type === 'branch' && t.to === target) ?? autoOrBranch;
|
|
205
|
+
const specific = transitions.find(t => t.type === 'branch' && t.branchLabel === label) ?? transitions.find(t => t.type === 'branch' && t.to === target) ?? autoOrBranch;
|
|
202
206
|
if (specific.processor)
|
|
203
207
|
await specific.processor.process(flow.context);
|
|
204
208
|
const from = flow.currentState;
|
|
@@ -231,7 +235,7 @@ export class FlowEngine {
|
|
|
231
235
|
parentFlow.setActiveSubFlow(null);
|
|
232
236
|
const target = exitMappings.get(subFlow.exitState);
|
|
233
237
|
if (target) {
|
|
234
|
-
const sfStart =
|
|
238
|
+
const sfStart = performance.now();
|
|
235
239
|
const from = parentFlow.currentState;
|
|
236
240
|
this.fireExit(parentFlow, from);
|
|
237
241
|
parentFlow.transitionTo(target);
|
|
@@ -256,15 +260,15 @@ export class FlowEngine {
|
|
|
256
260
|
}
|
|
257
261
|
const guard = transition.guard;
|
|
258
262
|
if (guard) {
|
|
259
|
-
const guardStart =
|
|
263
|
+
const guardStart = performance.now();
|
|
260
264
|
const output = await guard.validate(parentFlow.context);
|
|
261
|
-
const guardDur =
|
|
265
|
+
const guardDur = Math.round((performance.now() - guardStart) * 1000);
|
|
262
266
|
if (output.type === 'accepted') {
|
|
263
267
|
if (output.data) {
|
|
264
268
|
for (const [key, value] of output.data)
|
|
265
269
|
parentFlow.context.put(key, value);
|
|
266
270
|
}
|
|
267
|
-
const sfStart =
|
|
271
|
+
const sfStart = performance.now();
|
|
268
272
|
const sfFrom = subFlow.currentState;
|
|
269
273
|
subFlow.transitionTo(transition.to);
|
|
270
274
|
this.store.recordTransition(parentFlow.id, sfFrom, transition.to, guard.name, parentFlow.context);
|
|
@@ -296,7 +300,7 @@ export class FlowEngine {
|
|
|
296
300
|
if (subFlowT?.exitMappings) {
|
|
297
301
|
const target = subFlowT.exitMappings.get(subFlow.exitState);
|
|
298
302
|
if (target) {
|
|
299
|
-
const exitStart =
|
|
303
|
+
const exitStart = performance.now();
|
|
300
304
|
const from = parentFlow.currentState;
|
|
301
305
|
this.fireExit(parentFlow, from);
|
|
302
306
|
parentFlow.transitionTo(target);
|
|
@@ -346,7 +350,7 @@ export class FlowEngine {
|
|
|
346
350
|
this.guardLogger?.({ flowId: flow.id, flowName: flow.definition.name, state, guardName, result, reason, durationMicros });
|
|
347
351
|
}
|
|
348
352
|
handleError(flow, fromState, cause) {
|
|
349
|
-
const errorStart =
|
|
353
|
+
const errorStart = performance.now();
|
|
350
354
|
if (cause) {
|
|
351
355
|
flow.setLastError(`${cause.constructor.name}: ${cause.message}`);
|
|
352
356
|
if (cause instanceof FlowError) {
|
package/dist/esm/pipeline.js
CHANGED
|
@@ -87,7 +87,7 @@ export class Pipeline {
|
|
|
87
87
|
const completed = [];
|
|
88
88
|
let prev = 'initial';
|
|
89
89
|
for (const step of this.steps) {
|
|
90
|
-
const stepStart =
|
|
90
|
+
const stepStart = performance.now();
|
|
91
91
|
this.transitionLogger?.({ flowId, flowName: this.name, from: prev, to: step.name, trigger: step.name, durationMicros: 0 });
|
|
92
92
|
const keysBefore = this.stateLogger ? new Set(ctx.snapshot().keys()) : null;
|
|
93
93
|
try {
|
package/dist/esm/types.d.ts
CHANGED
|
@@ -26,6 +26,8 @@ export interface Transition<S extends string> {
|
|
|
26
26
|
guard?: TransitionGuard<S>;
|
|
27
27
|
branch?: BranchProcessor<S>;
|
|
28
28
|
branchTargets: Map<string, S>;
|
|
29
|
+
/** Label assigned by builder .to(target, label, processor). Used for branch label-specific processor matching. */
|
|
30
|
+
branchLabel?: string;
|
|
29
31
|
subFlowDefinition?: import('./flow-definition.js').FlowDefinition<any>;
|
|
30
32
|
exitMappings?: Map<string, S>;
|
|
31
33
|
/** Per-state timeout in milliseconds. If set, resumeAndExecute checks this before guard. */
|