@unlaxer/tramli 1.6.0 → 1.8.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.
@@ -3,11 +3,15 @@ import { FlowInstance } from './flow-instance.js';
3
3
  import type { InMemoryFlowStore } from './in-memory-flow-store.js';
4
4
  export declare class FlowEngine {
5
5
  private readonly store;
6
- constructor(store: InMemoryFlowStore);
6
+ private readonly strictMode;
7
+ constructor(store: InMemoryFlowStore, options?: {
8
+ strictMode?: boolean;
9
+ });
7
10
  startFlow<S extends string>(definition: FlowDefinition<S>, sessionId: string, initialData: Map<string, unknown>): Promise<FlowInstance<S>>;
8
11
  resumeAndExecute<S extends string>(flowId: string, definition: FlowDefinition<S>, externalData?: Map<string, unknown>): Promise<FlowInstance<S>>;
9
12
  private executeAutoChain;
10
13
  private executeSubFlow;
11
14
  private resumeSubFlow;
15
+ private verifyProduces;
12
16
  private handleError;
13
17
  }
@@ -7,8 +7,10 @@ const flow_error_js_1 = require("./flow-error");
7
7
  const MAX_CHAIN_DEPTH = 10;
8
8
  class FlowEngine {
9
9
  store;
10
- constructor(store) {
10
+ strictMode;
11
+ constructor(store, options) {
11
12
  this.store = store;
13
+ this.strictMode = options?.strictMode ?? false;
12
14
  }
13
15
  async startFlow(definition, sessionId, initialData) {
14
16
  const flowId = crypto.randomUUID();
@@ -119,8 +121,10 @@ class FlowEngine {
119
121
  const backup = flow.context.snapshot();
120
122
  try {
121
123
  if (autoOrBranch.type === 'auto') {
122
- if (autoOrBranch.processor)
124
+ if (autoOrBranch.processor) {
123
125
  await autoOrBranch.processor.process(flow.context);
126
+ this.verifyProduces(autoOrBranch.processor, flow.context);
127
+ }
124
128
  const from = flow.currentState;
125
129
  flow.transitionTo(autoOrBranch.to);
126
130
  this.store.recordTransition(flow.id, from, autoOrBranch.to, autoOrBranch.processor?.name ?? 'auto', flow.context);
@@ -225,6 +229,15 @@ class FlowEngine {
225
229
  this.store.save(parentFlow);
226
230
  return parentFlow;
227
231
  }
232
+ verifyProduces(processor, ctx) {
233
+ if (!this.strictMode)
234
+ return;
235
+ for (const prod of processor.produces) {
236
+ if (!ctx.has(prod)) {
237
+ throw new flow_error_js_1.FlowError('PRODUCES_VIOLATION', `Processor '${processor.name}' declares produces ${prod} but did not put it in context (strictMode)`);
238
+ }
239
+ }
240
+ }
228
241
  handleError(flow, fromState, cause) {
229
242
  if (cause)
230
243
  flow.setLastError(`${cause.constructor.name}: ${cause.message}`);
@@ -4,5 +4,7 @@ import { FlowEngine } from './flow-engine.js';
4
4
  import type { InMemoryFlowStore } from './in-memory-flow-store.js';
5
5
  export declare class Tramli {
6
6
  static define<S extends string>(name: string, stateConfig: Record<S, StateConfig>): Builder<S>;
7
- static engine(store: InMemoryFlowStore): FlowEngine;
7
+ static engine(store: InMemoryFlowStore, options?: {
8
+ strictMode?: boolean;
9
+ }): FlowEngine;
8
10
  }
@@ -7,8 +7,8 @@ class Tramli {
7
7
  static define(name, stateConfig) {
8
8
  return new flow_definition_js_1.Builder(name, stateConfig);
9
9
  }
10
- static engine(store) {
11
- return new flow_engine_js_1.FlowEngine(store);
10
+ static engine(store, options) {
11
+ return new flow_engine_js_1.FlowEngine(store, options);
12
12
  }
13
13
  }
14
14
  exports.Tramli = Tramli;
@@ -3,11 +3,15 @@ import { FlowInstance } from './flow-instance.js';
3
3
  import type { InMemoryFlowStore } from './in-memory-flow-store.js';
4
4
  export declare class FlowEngine {
5
5
  private readonly store;
6
- constructor(store: InMemoryFlowStore);
6
+ private readonly strictMode;
7
+ constructor(store: InMemoryFlowStore, options?: {
8
+ strictMode?: boolean;
9
+ });
7
10
  startFlow<S extends string>(definition: FlowDefinition<S>, sessionId: string, initialData: Map<string, unknown>): Promise<FlowInstance<S>>;
8
11
  resumeAndExecute<S extends string>(flowId: string, definition: FlowDefinition<S>, externalData?: Map<string, unknown>): Promise<FlowInstance<S>>;
9
12
  private executeAutoChain;
10
13
  private executeSubFlow;
11
14
  private resumeSubFlow;
15
+ private verifyProduces;
12
16
  private handleError;
13
17
  }
@@ -4,8 +4,10 @@ import { FlowError } from './flow-error.js';
4
4
  const MAX_CHAIN_DEPTH = 10;
5
5
  export class FlowEngine {
6
6
  store;
7
- constructor(store) {
7
+ strictMode;
8
+ constructor(store, options) {
8
9
  this.store = store;
10
+ this.strictMode = options?.strictMode ?? false;
9
11
  }
10
12
  async startFlow(definition, sessionId, initialData) {
11
13
  const flowId = crypto.randomUUID();
@@ -116,8 +118,10 @@ export class FlowEngine {
116
118
  const backup = flow.context.snapshot();
117
119
  try {
118
120
  if (autoOrBranch.type === 'auto') {
119
- if (autoOrBranch.processor)
121
+ if (autoOrBranch.processor) {
120
122
  await autoOrBranch.processor.process(flow.context);
123
+ this.verifyProduces(autoOrBranch.processor, flow.context);
124
+ }
121
125
  const from = flow.currentState;
122
126
  flow.transitionTo(autoOrBranch.to);
123
127
  this.store.recordTransition(flow.id, from, autoOrBranch.to, autoOrBranch.processor?.name ?? 'auto', flow.context);
@@ -222,6 +226,15 @@ export class FlowEngine {
222
226
  this.store.save(parentFlow);
223
227
  return parentFlow;
224
228
  }
229
+ verifyProduces(processor, ctx) {
230
+ if (!this.strictMode)
231
+ return;
232
+ for (const prod of processor.produces) {
233
+ if (!ctx.has(prod)) {
234
+ throw new FlowError('PRODUCES_VIOLATION', `Processor '${processor.name}' declares produces ${prod} but did not put it in context (strictMode)`);
235
+ }
236
+ }
237
+ }
225
238
  handleError(flow, fromState, cause) {
226
239
  if (cause)
227
240
  flow.setLastError(`${cause.constructor.name}: ${cause.message}`);
@@ -4,5 +4,7 @@ import { FlowEngine } from './flow-engine.js';
4
4
  import type { InMemoryFlowStore } from './in-memory-flow-store.js';
5
5
  export declare class Tramli {
6
6
  static define<S extends string>(name: string, stateConfig: Record<S, StateConfig>): Builder<S>;
7
- static engine(store: InMemoryFlowStore): FlowEngine;
7
+ static engine(store: InMemoryFlowStore, options?: {
8
+ strictMode?: boolean;
9
+ }): FlowEngine;
8
10
  }
@@ -4,7 +4,7 @@ export class Tramli {
4
4
  static define(name, stateConfig) {
5
5
  return new Builder(name, stateConfig);
6
6
  }
7
- static engine(store) {
8
- return new FlowEngine(store);
7
+ static engine(store, options) {
8
+ return new FlowEngine(store, options);
9
9
  }
10
10
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unlaxer/tramli",
3
- "version": "1.6.0",
3
+ "version": "1.8.0",
4
4
  "description": "Constrained flow engine — state machines that prevent invalid transitions at build time",
5
5
  "type": "module",
6
6
  "exports": {