@unlaxer/tramli 1.14.0 → 1.15.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.
@@ -68,7 +68,10 @@ export declare class FromBuilder<S extends string> {
68
68
  private readonly fromState;
69
69
  constructor(builder: Builder<S>, fromState: S);
70
70
  auto(to: S, processor: StateProcessor<S>): Builder<S>;
71
- external(to: S, guard: TransitionGuard<S>, processor?: StateProcessor<S>): Builder<S>;
71
+ external(to: S, guard: TransitionGuard<S>, processorOrOptions?: StateProcessor<S> | {
72
+ processor?: StateProcessor<S>;
73
+ timeout?: number;
74
+ }): Builder<S>;
72
75
  branch(branch: BranchProcessor<S>): BranchBuilder<S>;
73
76
  subFlow(subFlowDef: FlowDefinition<any>): SubFlowBuilder<S>;
74
77
  }
@@ -434,10 +434,20 @@ class FromBuilder {
434
434
  });
435
435
  return this.builder;
436
436
  }
437
- external(to, guard, processor) {
437
+ external(to, guard, processorOrOptions) {
438
+ let processor;
439
+ let timeout;
440
+ if (processorOrOptions && 'process' in processorOrOptions) {
441
+ processor = processorOrOptions;
442
+ }
443
+ else if (processorOrOptions) {
444
+ const opts = processorOrOptions;
445
+ processor = opts.processor;
446
+ timeout = opts.timeout;
447
+ }
438
448
  this.builder.addTransition({
439
449
  from: this.fromState, to, type: 'external', processor,
440
- guard, branch: undefined, branchTargets: new Map(),
450
+ guard, branch: undefined, branchTargets: new Map(), timeout,
441
451
  });
442
452
  return this.builder;
443
453
  }
@@ -68,6 +68,15 @@ class FlowEngine {
68
68
  const transition = definition.externalFrom(currentState);
69
69
  if (!transition)
70
70
  throw flow_error_js_1.FlowError.invalidTransition(currentState, currentState);
71
+ // Per-state timeout check
72
+ if (transition.timeout != null) {
73
+ const deadline = new Date(flow.stateEnteredAt.getTime() + transition.timeout);
74
+ if (new Date() > deadline) {
75
+ flow.complete('EXPIRED');
76
+ this.store.save(flow);
77
+ return flow;
78
+ }
79
+ }
71
80
  const guard = transition.guard;
72
81
  if (guard) {
73
82
  const output = await guard.validate(flow.context);
@@ -13,6 +13,7 @@ export declare class FlowInstance<S extends string> {
13
13
  private _exitState;
14
14
  private _activeSubFlow;
15
15
  private _lastError;
16
+ private _stateEnteredAt;
16
17
  constructor(id: string, sessionId: string, definition: FlowDefinition<S>, context: FlowContext, currentState: S, expiresAt: Date);
17
18
  /**
18
19
  * Restore a FlowInstance from persisted state.
@@ -39,6 +40,7 @@ export declare class FlowInstance<S extends string> {
39
40
  waitingFor(): string[];
40
41
  /** Return a copy with the given version. For FlowStore optimistic locking. */
41
42
  withVersion(newVersion: number): FlowInstance<S>;
43
+ get stateEnteredAt(): Date;
42
44
  /** @internal */ transitionTo(state: S): void;
43
45
  /** @internal */ incrementGuardFailure(): void;
44
46
  /** @internal */ complete(exitState: string): void;
@@ -14,6 +14,7 @@ class FlowInstance {
14
14
  _exitState;
15
15
  _activeSubFlow = null;
16
16
  _lastError = null;
17
+ _stateEnteredAt = new Date();
17
18
  constructor(id, sessionId, definition, context, currentState, expiresAt) {
18
19
  this.id = id;
19
20
  this.sessionId = sessionId;
@@ -98,7 +99,8 @@ class FlowInstance {
98
99
  copy.setActiveSubFlow(this._activeSubFlow);
99
100
  return copy;
100
101
  }
101
- /** @internal */ transitionTo(state) { this._currentState = state; }
102
+ get stateEnteredAt() { return this._stateEnteredAt; }
103
+ /** @internal */ transitionTo(state) { this._currentState = state; this._stateEnteredAt = new Date(); }
102
104
  /** @internal */ incrementGuardFailure() { this._guardFailureCount++; }
103
105
  /** @internal */ complete(exitState) { this._exitState = exitState; }
104
106
  /** @internal */ setVersion(version) { this._version = version; }
@@ -28,6 +28,8 @@ export interface Transition<S extends string> {
28
28
  branchTargets: Map<string, S>;
29
29
  subFlowDefinition?: import('./flow-definition.js').FlowDefinition<any>;
30
30
  exitMappings?: Map<string, S>;
31
+ /** Per-state timeout in milliseconds. If set, resumeAndExecute checks this before guard. */
32
+ timeout?: number;
31
33
  }
32
34
  /**
33
35
  * Processes a state transition.
@@ -68,7 +68,10 @@ export declare class FromBuilder<S extends string> {
68
68
  private readonly fromState;
69
69
  constructor(builder: Builder<S>, fromState: S);
70
70
  auto(to: S, processor: StateProcessor<S>): Builder<S>;
71
- external(to: S, guard: TransitionGuard<S>, processor?: StateProcessor<S>): Builder<S>;
71
+ external(to: S, guard: TransitionGuard<S>, processorOrOptions?: StateProcessor<S> | {
72
+ processor?: StateProcessor<S>;
73
+ timeout?: number;
74
+ }): Builder<S>;
72
75
  branch(branch: BranchProcessor<S>): BranchBuilder<S>;
73
76
  subFlow(subFlowDef: FlowDefinition<any>): SubFlowBuilder<S>;
74
77
  }
@@ -429,10 +429,20 @@ export class FromBuilder {
429
429
  });
430
430
  return this.builder;
431
431
  }
432
- external(to, guard, processor) {
432
+ external(to, guard, processorOrOptions) {
433
+ let processor;
434
+ let timeout;
435
+ if (processorOrOptions && 'process' in processorOrOptions) {
436
+ processor = processorOrOptions;
437
+ }
438
+ else if (processorOrOptions) {
439
+ const opts = processorOrOptions;
440
+ processor = opts.processor;
441
+ timeout = opts.timeout;
442
+ }
433
443
  this.builder.addTransition({
434
444
  from: this.fromState, to, type: 'external', processor,
435
- guard, branch: undefined, branchTargets: new Map(),
445
+ guard, branch: undefined, branchTargets: new Map(), timeout,
436
446
  });
437
447
  return this.builder;
438
448
  }
@@ -65,6 +65,15 @@ export class FlowEngine {
65
65
  const transition = definition.externalFrom(currentState);
66
66
  if (!transition)
67
67
  throw FlowError.invalidTransition(currentState, currentState);
68
+ // Per-state timeout check
69
+ if (transition.timeout != null) {
70
+ const deadline = new Date(flow.stateEnteredAt.getTime() + transition.timeout);
71
+ if (new Date() > deadline) {
72
+ flow.complete('EXPIRED');
73
+ this.store.save(flow);
74
+ return flow;
75
+ }
76
+ }
68
77
  const guard = transition.guard;
69
78
  if (guard) {
70
79
  const output = await guard.validate(flow.context);
@@ -13,6 +13,7 @@ export declare class FlowInstance<S extends string> {
13
13
  private _exitState;
14
14
  private _activeSubFlow;
15
15
  private _lastError;
16
+ private _stateEnteredAt;
16
17
  constructor(id: string, sessionId: string, definition: FlowDefinition<S>, context: FlowContext, currentState: S, expiresAt: Date);
17
18
  /**
18
19
  * Restore a FlowInstance from persisted state.
@@ -39,6 +40,7 @@ export declare class FlowInstance<S extends string> {
39
40
  waitingFor(): string[];
40
41
  /** Return a copy with the given version. For FlowStore optimistic locking. */
41
42
  withVersion(newVersion: number): FlowInstance<S>;
43
+ get stateEnteredAt(): Date;
42
44
  /** @internal */ transitionTo(state: S): void;
43
45
  /** @internal */ incrementGuardFailure(): void;
44
46
  /** @internal */ complete(exitState: string): void;
@@ -11,6 +11,7 @@ export class FlowInstance {
11
11
  _exitState;
12
12
  _activeSubFlow = null;
13
13
  _lastError = null;
14
+ _stateEnteredAt = new Date();
14
15
  constructor(id, sessionId, definition, context, currentState, expiresAt) {
15
16
  this.id = id;
16
17
  this.sessionId = sessionId;
@@ -95,7 +96,8 @@ export class FlowInstance {
95
96
  copy.setActiveSubFlow(this._activeSubFlow);
96
97
  return copy;
97
98
  }
98
- /** @internal */ transitionTo(state) { this._currentState = state; }
99
+ get stateEnteredAt() { return this._stateEnteredAt; }
100
+ /** @internal */ transitionTo(state) { this._currentState = state; this._stateEnteredAt = new Date(); }
99
101
  /** @internal */ incrementGuardFailure() { this._guardFailureCount++; }
100
102
  /** @internal */ complete(exitState) { this._exitState = exitState; }
101
103
  /** @internal */ setVersion(version) { this._version = version; }
@@ -28,6 +28,8 @@ export interface Transition<S extends string> {
28
28
  branchTargets: Map<string, S>;
29
29
  subFlowDefinition?: import('./flow-definition.js').FlowDefinition<any>;
30
30
  exitMappings?: Map<string, S>;
31
+ /** Per-state timeout in milliseconds. If set, resumeAndExecute checks this before guard. */
32
+ timeout?: number;
31
33
  }
32
34
  /**
33
35
  * Processes a state transition.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unlaxer/tramli",
3
- "version": "1.14.0",
3
+ "version": "1.15.0",
4
4
  "description": "Constrained flow engine — state machines that prevent invalid transitions at build time",
5
5
  "type": "module",
6
6
  "exports": {