@unlaxer/tramli 3.3.0 → 3.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.
@@ -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;
@@ -201,7 +201,7 @@ class FlowEngine {
201
201
  if (!target) {
202
202
  throw new flow_error_js_1.FlowError('UNKNOWN_BRANCH', `Branch '${branch.name}' returned unknown label: ${label}`);
203
203
  }
204
- const specific = transitions.find(t => t.type === 'branch' && t.to === target) ?? autoOrBranch;
204
+ const specific = transitions.find(t => t.type === 'branch' && t.branchLabel === label) ?? transitions.find(t => t.type === 'branch' && t.to === target) ?? autoOrBranch;
205
205
  if (specific.processor)
206
206
  await specific.processor.process(flow.context);
207
207
  const from = flow.currentState;
@@ -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;
@@ -198,7 +198,7 @@ export class FlowEngine {
198
198
  if (!target) {
199
199
  throw new FlowError('UNKNOWN_BRANCH', `Branch '${branch.name}' returned unknown label: ${label}`);
200
200
  }
201
- const specific = transitions.find(t => t.type === 'branch' && t.to === target) ?? autoOrBranch;
201
+ const specific = transitions.find(t => t.type === 'branch' && t.branchLabel === label) ?? transitions.find(t => t.type === 'branch' && t.to === target) ?? autoOrBranch;
202
202
  if (specific.processor)
203
203
  await specific.processor.process(flow.context);
204
204
  const from = flow.currentState;
@@ -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. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unlaxer/tramli",
3
- "version": "3.3.0",
3
+ "version": "3.4.0",
4
4
  "description": "Constrained flow engine — state machines that prevent invalid transitions at build time",
5
5
  "type": "module",
6
6
  "exports": {