@unlaxer/tramli-plugins 3.0.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/api/plugin-registry.d.ts +19 -0
- package/dist/cjs/api/plugin-registry.js +55 -0
- package/dist/cjs/api/types.d.ts +51 -0
- package/dist/cjs/api/types.js +23 -0
- package/dist/cjs/audit/audit-store-plugin.d.ts +7 -0
- package/dist/cjs/audit/audit-store-plugin.js +14 -0
- package/dist/cjs/audit/auditing-flow-store.d.ts +24 -0
- package/dist/cjs/audit/auditing-flow-store.js +33 -0
- package/dist/cjs/diagram/diagram-generation-plugin.d.ts +9 -0
- package/dist/cjs/diagram/diagram-generation-plugin.js +19 -0
- package/dist/cjs/diagram/diagram-plugin.d.ts +5 -0
- package/dist/cjs/diagram/diagram-plugin.js +16 -0
- package/dist/cjs/diagram/types.d.ts +5 -0
- package/dist/cjs/diagram/types.js +2 -0
- package/dist/cjs/docs/documentation-plugin.d.ts +7 -0
- package/dist/cjs/docs/documentation-plugin.js +33 -0
- package/dist/cjs/docs/flow-documentation-plugin.d.ts +8 -0
- package/dist/cjs/docs/flow-documentation-plugin.js +19 -0
- package/dist/cjs/eventstore/compensation-service.d.ts +11 -0
- package/dist/cjs/eventstore/compensation-service.js +22 -0
- package/dist/cjs/eventstore/event-log-store-decorator.d.ts +24 -0
- package/dist/cjs/eventstore/event-log-store-decorator.js +50 -0
- package/dist/cjs/eventstore/event-log-store-plugin.d.ts +7 -0
- package/dist/cjs/eventstore/event-log-store-plugin.js +14 -0
- package/dist/cjs/eventstore/replay-service.d.ts +14 -0
- package/dist/cjs/eventstore/replay-service.js +33 -0
- package/dist/cjs/eventstore/types.d.ts +19 -0
- package/dist/cjs/eventstore/types.js +2 -0
- package/dist/cjs/hierarchy/entry-exit-compiler.d.ts +8 -0
- package/dist/cjs/hierarchy/entry-exit-compiler.js +39 -0
- package/dist/cjs/hierarchy/hierarchy-code-generator.d.ts +10 -0
- package/dist/cjs/hierarchy/hierarchy-code-generator.js +48 -0
- package/dist/cjs/hierarchy/hierarchy-generation-plugin.d.ts +8 -0
- package/dist/cjs/hierarchy/hierarchy-generation-plugin.js +22 -0
- package/dist/cjs/hierarchy/types.d.ts +27 -0
- package/dist/cjs/hierarchy/types.js +21 -0
- package/dist/cjs/idempotency/idempotency-runtime-plugin.d.ts +11 -0
- package/dist/cjs/idempotency/idempotency-runtime-plugin.js +22 -0
- package/dist/cjs/idempotency/idempotent-rich-resume-executor.d.ts +9 -0
- package/dist/cjs/idempotency/idempotent-rich-resume-executor.js +22 -0
- package/dist/cjs/idempotency/in-memory-idempotency-registry.d.ts +5 -0
- package/dist/cjs/idempotency/in-memory-idempotency-registry.js +14 -0
- package/dist/cjs/idempotency/types.d.ts +13 -0
- package/dist/cjs/idempotency/types.js +2 -0
- package/dist/cjs/index.d.ts +36 -0
- package/dist/cjs/index.js +72 -0
- package/dist/cjs/lint/default-flow-policies.d.ts +2 -0
- package/dist/cjs/lint/default-flow-policies.js +41 -0
- package/dist/cjs/lint/policy-lint-plugin.d.ts +11 -0
- package/dist/cjs/lint/policy-lint-plugin.js +27 -0
- package/dist/cjs/lint/types.d.ts +5 -0
- package/dist/cjs/lint/types.js +2 -0
- package/dist/cjs/observability/observability-plugin.d.ts +25 -0
- package/dist/cjs/observability/observability-plugin.js +36 -0
- package/dist/cjs/resume/rich-resume.d.ts +21 -0
- package/dist/cjs/resume/rich-resume.js +47 -0
- package/dist/cjs/subflow/guaranteed-subflow-validator.d.ts +8 -0
- package/dist/cjs/subflow/guaranteed-subflow-validator.js +31 -0
- package/dist/cjs/testing/scenario-generation-plugin.d.ts +9 -0
- package/dist/cjs/testing/scenario-generation-plugin.js +19 -0
- package/dist/cjs/testing/scenario-test-plugin.d.ts +8 -0
- package/dist/cjs/testing/scenario-test-plugin.js +28 -0
- package/dist/cjs/testing/types.d.ts +7 -0
- package/dist/cjs/testing/types.js +2 -0
- package/dist/esm/api/plugin-registry.d.ts +19 -0
- package/dist/esm/api/plugin-registry.js +51 -0
- package/dist/esm/api/types.d.ts +51 -0
- package/dist/esm/api/types.js +19 -0
- package/dist/esm/audit/audit-store-plugin.d.ts +7 -0
- package/dist/esm/audit/audit-store-plugin.js +10 -0
- package/dist/esm/audit/auditing-flow-store.d.ts +24 -0
- package/dist/esm/audit/auditing-flow-store.js +29 -0
- package/dist/esm/diagram/diagram-generation-plugin.d.ts +9 -0
- package/dist/esm/diagram/diagram-generation-plugin.js +15 -0
- package/dist/esm/diagram/diagram-plugin.d.ts +5 -0
- package/dist/esm/diagram/diagram-plugin.js +12 -0
- package/dist/esm/diagram/types.d.ts +5 -0
- package/dist/esm/diagram/types.js +1 -0
- package/dist/esm/docs/documentation-plugin.d.ts +7 -0
- package/dist/esm/docs/documentation-plugin.js +29 -0
- package/dist/esm/docs/flow-documentation-plugin.d.ts +8 -0
- package/dist/esm/docs/flow-documentation-plugin.js +15 -0
- package/dist/esm/eventstore/compensation-service.d.ts +11 -0
- package/dist/esm/eventstore/compensation-service.js +18 -0
- package/dist/esm/eventstore/event-log-store-decorator.d.ts +24 -0
- package/dist/esm/eventstore/event-log-store-decorator.js +46 -0
- package/dist/esm/eventstore/event-log-store-plugin.d.ts +7 -0
- package/dist/esm/eventstore/event-log-store-plugin.js +10 -0
- package/dist/esm/eventstore/replay-service.d.ts +14 -0
- package/dist/esm/eventstore/replay-service.js +28 -0
- package/dist/esm/eventstore/types.d.ts +19 -0
- package/dist/esm/eventstore/types.js +1 -0
- package/dist/esm/hierarchy/entry-exit-compiler.d.ts +8 -0
- package/dist/esm/hierarchy/entry-exit-compiler.js +35 -0
- package/dist/esm/hierarchy/hierarchy-code-generator.d.ts +10 -0
- package/dist/esm/hierarchy/hierarchy-code-generator.js +44 -0
- package/dist/esm/hierarchy/hierarchy-generation-plugin.d.ts +8 -0
- package/dist/esm/hierarchy/hierarchy-generation-plugin.js +18 -0
- package/dist/esm/hierarchy/types.d.ts +27 -0
- package/dist/esm/hierarchy/types.js +16 -0
- package/dist/esm/idempotency/idempotency-runtime-plugin.d.ts +11 -0
- package/dist/esm/idempotency/idempotency-runtime-plugin.js +18 -0
- package/dist/esm/idempotency/idempotent-rich-resume-executor.d.ts +9 -0
- package/dist/esm/idempotency/idempotent-rich-resume-executor.js +18 -0
- package/dist/esm/idempotency/in-memory-idempotency-registry.d.ts +5 -0
- package/dist/esm/idempotency/in-memory-idempotency-registry.js +10 -0
- package/dist/esm/idempotency/types.d.ts +13 -0
- package/dist/esm/idempotency/types.js +1 -0
- package/dist/esm/index.d.ts +36 -0
- package/dist/esm/index.js +38 -0
- package/dist/esm/lint/default-flow-policies.d.ts +2 -0
- package/dist/esm/lint/default-flow-policies.js +38 -0
- package/dist/esm/lint/policy-lint-plugin.d.ts +11 -0
- package/dist/esm/lint/policy-lint-plugin.js +23 -0
- package/dist/esm/lint/types.d.ts +5 -0
- package/dist/esm/lint/types.js +1 -0
- package/dist/esm/observability/observability-plugin.d.ts +25 -0
- package/dist/esm/observability/observability-plugin.js +31 -0
- package/dist/esm/resume/rich-resume.d.ts +21 -0
- package/dist/esm/resume/rich-resume.js +42 -0
- package/dist/esm/subflow/guaranteed-subflow-validator.d.ts +8 -0
- package/dist/esm/subflow/guaranteed-subflow-validator.js +27 -0
- package/dist/esm/testing/scenario-generation-plugin.d.ts +9 -0
- package/dist/esm/testing/scenario-generation-plugin.js +15 -0
- package/dist/esm/testing/scenario-test-plugin.d.ts +8 -0
- package/dist/esm/testing/scenario-test-plugin.js +24 -0
- package/dist/esm/testing/types.d.ts +7 -0
- package/dist/esm/testing/types.js +1 -0
- package/package.json +39 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { HierarchicalFlowSpec } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Generates TypeScript source from a hierarchical flow spec.
|
|
4
|
+
* Flattens the hierarchy into a flat state config + builder skeleton.
|
|
5
|
+
*/
|
|
6
|
+
export declare class HierarchyCodeGenerator {
|
|
7
|
+
generateStateConfig(spec: HierarchicalFlowSpec): string;
|
|
8
|
+
generateBuilderSkeleton(spec: HierarchicalFlowSpec): string;
|
|
9
|
+
private flatten;
|
|
10
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates TypeScript source from a hierarchical flow spec.
|
|
3
|
+
* Flattens the hierarchy into a flat state config + builder skeleton.
|
|
4
|
+
*/
|
|
5
|
+
export class HierarchyCodeGenerator {
|
|
6
|
+
generateStateConfig(spec) {
|
|
7
|
+
const flatStates = [];
|
|
8
|
+
this.flatten(spec.rootStates, '', flatStates);
|
|
9
|
+
const lines = [];
|
|
10
|
+
lines.push(`// Generated state config for ${spec.flowName}`);
|
|
11
|
+
lines.push(`import { Tramli } from '@unlaxer/tramli';`);
|
|
12
|
+
lines.push('');
|
|
13
|
+
lines.push(`export const ${spec.enumName} = {`);
|
|
14
|
+
for (const s of flatStates) {
|
|
15
|
+
lines.push(` ${s.enumName}: { terminal: ${s.terminal}, initial: ${s.initial} },`);
|
|
16
|
+
}
|
|
17
|
+
lines.push('} as const;');
|
|
18
|
+
lines.push('');
|
|
19
|
+
lines.push(`export type ${spec.enumName}State = keyof typeof ${spec.enumName};`);
|
|
20
|
+
return lines.join('\n');
|
|
21
|
+
}
|
|
22
|
+
generateBuilderSkeleton(spec) {
|
|
23
|
+
const lines = [];
|
|
24
|
+
lines.push(`// Generated builder skeleton for ${spec.flowName}`);
|
|
25
|
+
lines.push(`import { Tramli } from '@unlaxer/tramli';`);
|
|
26
|
+
lines.push(`import { ${spec.enumName} } from './${spec.enumName}.js';`);
|
|
27
|
+
lines.push('');
|
|
28
|
+
lines.push(`export function build${spec.flowName}() {`);
|
|
29
|
+
lines.push(` const b = Tramli.define('${spec.flowName}', ${spec.enumName});`);
|
|
30
|
+
for (const t of spec.transitions) {
|
|
31
|
+
lines.push(` // ${t.trigger}: ${t.from} -> ${t.to} requires [${t.requires.join(', ')}] produces [${t.produces.join(', ')}]`);
|
|
32
|
+
}
|
|
33
|
+
lines.push(' return b.build();');
|
|
34
|
+
lines.push('}');
|
|
35
|
+
return lines.join('\n');
|
|
36
|
+
}
|
|
37
|
+
flatten(states, prefix, out) {
|
|
38
|
+
for (const state of states) {
|
|
39
|
+
const flat = (prefix ? `${prefix}_${state.name}` : state.name).toUpperCase();
|
|
40
|
+
out.push({ enumName: flat, terminal: state.terminal, initial: state.initial });
|
|
41
|
+
this.flatten(state.children, flat, out);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { GenerationPlugin, PluginDescriptor } from '../api/types.js';
|
|
2
|
+
import type { HierarchicalFlowSpec } from './types.js';
|
|
3
|
+
export declare class HierarchyGenerationPlugin implements GenerationPlugin<HierarchicalFlowSpec, Map<string, string>> {
|
|
4
|
+
private readonly generator;
|
|
5
|
+
descriptor(): PluginDescriptor;
|
|
6
|
+
kind(): "GENERATION";
|
|
7
|
+
generate(input: HierarchicalFlowSpec): Map<string, string>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { HierarchyCodeGenerator } from './hierarchy-code-generator.js';
|
|
2
|
+
export class HierarchyGenerationPlugin {
|
|
3
|
+
generator = new HierarchyCodeGenerator();
|
|
4
|
+
descriptor() {
|
|
5
|
+
return {
|
|
6
|
+
id: 'hierarchy',
|
|
7
|
+
displayName: 'Hierarchy Generator',
|
|
8
|
+
description: 'Compiles hierarchical authoring specs into flat TypeScript state config and builder skeleton.',
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
kind() { return 'GENERATION'; }
|
|
12
|
+
generate(input) {
|
|
13
|
+
return new Map([
|
|
14
|
+
[`${input.enumName}.ts`, this.generator.generateStateConfig(input)],
|
|
15
|
+
[`${input.flowName}Generated.ts`, this.generator.generateBuilderSkeleton(input)],
|
|
16
|
+
]);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface HierarchicalStateSpec {
|
|
2
|
+
name: string;
|
|
3
|
+
initial: boolean;
|
|
4
|
+
terminal: boolean;
|
|
5
|
+
entryProduces: string[];
|
|
6
|
+
exitProduces: string[];
|
|
7
|
+
children: HierarchicalStateSpec[];
|
|
8
|
+
}
|
|
9
|
+
export interface HierarchicalTransitionSpec {
|
|
10
|
+
from: string;
|
|
11
|
+
to: string;
|
|
12
|
+
trigger: string;
|
|
13
|
+
requires: string[];
|
|
14
|
+
produces: string[];
|
|
15
|
+
}
|
|
16
|
+
export interface HierarchicalFlowSpec {
|
|
17
|
+
flowName: string;
|
|
18
|
+
enumName: string;
|
|
19
|
+
rootStates: HierarchicalStateSpec[];
|
|
20
|
+
transitions: HierarchicalTransitionSpec[];
|
|
21
|
+
}
|
|
22
|
+
export declare function stateSpec(name: string, opts?: {
|
|
23
|
+
initial?: boolean;
|
|
24
|
+
terminal?: boolean;
|
|
25
|
+
}): HierarchicalStateSpec;
|
|
26
|
+
export declare function transitionSpec(from: string, to: string, trigger: string): HierarchicalTransitionSpec;
|
|
27
|
+
export declare function flowSpec(flowName: string, enumName: string): HierarchicalFlowSpec;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export function stateSpec(name, opts) {
|
|
2
|
+
return {
|
|
3
|
+
name,
|
|
4
|
+
initial: opts?.initial ?? false,
|
|
5
|
+
terminal: opts?.terminal ?? false,
|
|
6
|
+
entryProduces: [],
|
|
7
|
+
exitProduces: [],
|
|
8
|
+
children: [],
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export function transitionSpec(from, to, trigger) {
|
|
12
|
+
return { from, to, trigger, requires: [], produces: [] };
|
|
13
|
+
}
|
|
14
|
+
export function flowSpec(flowName, enumName) {
|
|
15
|
+
return { flowName, enumName, rootStates: [], transitions: [] };
|
|
16
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { FlowEngine } from '@unlaxer/tramli';
|
|
2
|
+
import type { RuntimeAdapterPlugin, PluginDescriptor } from '../api/types.js';
|
|
3
|
+
import type { IdempotencyRegistry } from './types.js';
|
|
4
|
+
import { IdempotentRichResumeExecutor } from './idempotent-rich-resume-executor.js';
|
|
5
|
+
export declare class IdempotencyRuntimePlugin implements RuntimeAdapterPlugin<IdempotentRichResumeExecutor> {
|
|
6
|
+
private readonly registry;
|
|
7
|
+
constructor(registry: IdempotencyRegistry);
|
|
8
|
+
descriptor(): PluginDescriptor;
|
|
9
|
+
kind(): "RUNTIME_ADAPTER";
|
|
10
|
+
bind(engine: FlowEngine): IdempotentRichResumeExecutor;
|
|
11
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { IdempotentRichResumeExecutor } from './idempotent-rich-resume-executor.js';
|
|
2
|
+
export class IdempotencyRuntimePlugin {
|
|
3
|
+
registry;
|
|
4
|
+
constructor(registry) {
|
|
5
|
+
this.registry = registry;
|
|
6
|
+
}
|
|
7
|
+
descriptor() {
|
|
8
|
+
return {
|
|
9
|
+
id: 'idempotency',
|
|
10
|
+
displayName: 'Idempotency',
|
|
11
|
+
description: 'Binds a FlowEngine to duplicate-suppression helpers.',
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
kind() { return 'RUNTIME_ADAPTER'; }
|
|
15
|
+
bind(engine) {
|
|
16
|
+
return new IdempotentRichResumeExecutor(engine, this.registry);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { FlowEngine, FlowDefinition } from '@unlaxer/tramli';
|
|
2
|
+
import type { IdempotencyRegistry, CommandEnvelope } from './types.js';
|
|
3
|
+
import { type RichResumeResult } from '../resume/rich-resume.js';
|
|
4
|
+
export declare class IdempotentRichResumeExecutor {
|
|
5
|
+
private readonly registry;
|
|
6
|
+
private readonly delegate;
|
|
7
|
+
constructor(engine: FlowEngine, registry: IdempotencyRegistry);
|
|
8
|
+
resume<S extends string>(flowId: string, definition: FlowDefinition<S>, envelope: CommandEnvelope, knownBeforeState: S): Promise<RichResumeResult<S>>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { RichResumeExecutor } from '../resume/rich-resume.js';
|
|
2
|
+
export class IdempotentRichResumeExecutor {
|
|
3
|
+
registry;
|
|
4
|
+
delegate;
|
|
5
|
+
constructor(engine, registry) {
|
|
6
|
+
this.registry = registry;
|
|
7
|
+
this.delegate = new RichResumeExecutor(engine);
|
|
8
|
+
}
|
|
9
|
+
async resume(flowId, definition, envelope, knownBeforeState) {
|
|
10
|
+
if (!this.registry.markIfFirstSeen(flowId, envelope.commandId)) {
|
|
11
|
+
return {
|
|
12
|
+
status: 'ALREADY_COMPLETE',
|
|
13
|
+
error: new Error(`duplicate commandId ${envelope.commandId}`),
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
return this.delegate.resume(flowId, definition, envelope.externalData, knownBeforeState);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Command envelope — wraps external data with a unique command ID for dedup.
|
|
3
|
+
*/
|
|
4
|
+
export interface CommandEnvelope {
|
|
5
|
+
commandId: string;
|
|
6
|
+
externalData: Map<string, unknown>;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Idempotency registry — tracks which commands have already been processed.
|
|
10
|
+
*/
|
|
11
|
+
export interface IdempotencyRegistry {
|
|
12
|
+
markIfFirstSeen(flowId: string, commandId: string): boolean;
|
|
13
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export { PluginReport } from './api/types.js';
|
|
2
|
+
export type { PluginKind, PluginDescriptor, FlowPlugin, AnalysisPlugin, StorePlugin, EnginePlugin, RuntimeAdapterPlugin, GenerationPlugin, DocumentationPlugin as DocumentationPluginSPI, } from './api/types.js';
|
|
3
|
+
export { PluginRegistry } from './api/plugin-registry.js';
|
|
4
|
+
export { AuditStorePlugin } from './audit/audit-store-plugin.js';
|
|
5
|
+
export { AuditingFlowStore } from './audit/auditing-flow-store.js';
|
|
6
|
+
export type { AuditedTransitionRecord } from './audit/auditing-flow-store.js';
|
|
7
|
+
export { EventLogStorePlugin } from './eventstore/event-log-store-plugin.js';
|
|
8
|
+
export { EventLogStoreDecorator } from './eventstore/event-log-store-decorator.js';
|
|
9
|
+
export { ReplayService, ProjectionReplayService } from './eventstore/replay-service.js';
|
|
10
|
+
export { CompensationService } from './eventstore/compensation-service.js';
|
|
11
|
+
export type { VersionedTransitionEvent, CompensationPlan, CompensationResolver, ProjectionReducer, } from './eventstore/types.js';
|
|
12
|
+
export { ObservabilityEnginePlugin, InMemoryTelemetrySink } from './observability/observability-plugin.js';
|
|
13
|
+
export type { TelemetryEvent, TelemetrySink } from './observability/observability-plugin.js';
|
|
14
|
+
export { RichResumeExecutor, RichResumeRuntimePlugin } from './resume/rich-resume.js';
|
|
15
|
+
export type { RichResumeStatus, RichResumeResult } from './resume/rich-resume.js';
|
|
16
|
+
export { InMemoryIdempotencyRegistry } from './idempotency/in-memory-idempotency-registry.js';
|
|
17
|
+
export { IdempotentRichResumeExecutor } from './idempotency/idempotent-rich-resume-executor.js';
|
|
18
|
+
export { IdempotencyRuntimePlugin } from './idempotency/idempotency-runtime-plugin.js';
|
|
19
|
+
export type { CommandEnvelope, IdempotencyRegistry } from './idempotency/types.js';
|
|
20
|
+
export { EntryExitCompiler } from './hierarchy/entry-exit-compiler.js';
|
|
21
|
+
export { HierarchyCodeGenerator } from './hierarchy/hierarchy-code-generator.js';
|
|
22
|
+
export { HierarchyGenerationPlugin } from './hierarchy/hierarchy-generation-plugin.js';
|
|
23
|
+
export type { HierarchicalStateSpec, HierarchicalTransitionSpec, HierarchicalFlowSpec, } from './hierarchy/types.js';
|
|
24
|
+
export { stateSpec, transitionSpec, flowSpec } from './hierarchy/types.js';
|
|
25
|
+
export { DiagramPlugin } from './diagram/diagram-plugin.js';
|
|
26
|
+
export { DiagramGenerationPlugin } from './diagram/diagram-generation-plugin.js';
|
|
27
|
+
export type { DiagramBundle } from './diagram/types.js';
|
|
28
|
+
export { DocumentationPlugin } from './docs/documentation-plugin.js';
|
|
29
|
+
export { FlowDocumentationPlugin } from './docs/flow-documentation-plugin.js';
|
|
30
|
+
export { PolicyLintPlugin } from './lint/policy-lint-plugin.js';
|
|
31
|
+
export { allDefaultPolicies } from './lint/default-flow-policies.js';
|
|
32
|
+
export type { FlowPolicy } from './lint/types.js';
|
|
33
|
+
export { ScenarioTestPlugin } from './testing/scenario-test-plugin.js';
|
|
34
|
+
export { ScenarioGenerationPlugin } from './testing/scenario-generation-plugin.js';
|
|
35
|
+
export type { FlowScenario, FlowTestPlan } from './testing/types.js';
|
|
36
|
+
export { GuaranteedSubflowValidator } from './subflow/guaranteed-subflow-validator.js';
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// ── API framework ──
|
|
2
|
+
export { PluginReport } from './api/types.js';
|
|
3
|
+
export { PluginRegistry } from './api/plugin-registry.js';
|
|
4
|
+
// ── Audit ──
|
|
5
|
+
export { AuditStorePlugin } from './audit/audit-store-plugin.js';
|
|
6
|
+
export { AuditingFlowStore } from './audit/auditing-flow-store.js';
|
|
7
|
+
// ── Event Store ──
|
|
8
|
+
export { EventLogStorePlugin } from './eventstore/event-log-store-plugin.js';
|
|
9
|
+
export { EventLogStoreDecorator } from './eventstore/event-log-store-decorator.js';
|
|
10
|
+
export { ReplayService, ProjectionReplayService } from './eventstore/replay-service.js';
|
|
11
|
+
export { CompensationService } from './eventstore/compensation-service.js';
|
|
12
|
+
// ── Observability ──
|
|
13
|
+
export { ObservabilityEnginePlugin, InMemoryTelemetrySink } from './observability/observability-plugin.js';
|
|
14
|
+
// ── Rich Resume ──
|
|
15
|
+
export { RichResumeExecutor, RichResumeRuntimePlugin } from './resume/rich-resume.js';
|
|
16
|
+
// ── Idempotency ──
|
|
17
|
+
export { InMemoryIdempotencyRegistry } from './idempotency/in-memory-idempotency-registry.js';
|
|
18
|
+
export { IdempotentRichResumeExecutor } from './idempotency/idempotent-rich-resume-executor.js';
|
|
19
|
+
export { IdempotencyRuntimePlugin } from './idempotency/idempotency-runtime-plugin.js';
|
|
20
|
+
// ── Hierarchy ──
|
|
21
|
+
export { EntryExitCompiler } from './hierarchy/entry-exit-compiler.js';
|
|
22
|
+
export { HierarchyCodeGenerator } from './hierarchy/hierarchy-code-generator.js';
|
|
23
|
+
export { HierarchyGenerationPlugin } from './hierarchy/hierarchy-generation-plugin.js';
|
|
24
|
+
export { stateSpec, transitionSpec, flowSpec } from './hierarchy/types.js';
|
|
25
|
+
// ── Diagram ──
|
|
26
|
+
export { DiagramPlugin } from './diagram/diagram-plugin.js';
|
|
27
|
+
export { DiagramGenerationPlugin } from './diagram/diagram-generation-plugin.js';
|
|
28
|
+
// ── Documentation ──
|
|
29
|
+
export { DocumentationPlugin } from './docs/documentation-plugin.js';
|
|
30
|
+
export { FlowDocumentationPlugin } from './docs/flow-documentation-plugin.js';
|
|
31
|
+
// ── Lint ──
|
|
32
|
+
export { PolicyLintPlugin } from './lint/policy-lint-plugin.js';
|
|
33
|
+
export { allDefaultPolicies } from './lint/default-flow-policies.js';
|
|
34
|
+
// ── Testing ──
|
|
35
|
+
export { ScenarioTestPlugin } from './testing/scenario-test-plugin.js';
|
|
36
|
+
export { ScenarioGenerationPlugin } from './testing/scenario-generation-plugin.js';
|
|
37
|
+
// ── SubFlow ──
|
|
38
|
+
export { GuaranteedSubflowValidator } from './subflow/guaranteed-subflow-validator.js';
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
function warnTerminalWithOutgoing(def, report) {
|
|
2
|
+
for (const state of def.terminalStates) {
|
|
3
|
+
if (def.transitionsFrom(state).length > 0) {
|
|
4
|
+
report.warn('policy/terminal-outgoing', `terminal state ${state} has outgoing transitions`);
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
function warnTooManyExternals(def, report) {
|
|
9
|
+
for (const state of def.allStates()) {
|
|
10
|
+
const externals = def.transitionsFrom(state).filter(t => t.type === 'external');
|
|
11
|
+
if (externals.length > 3) {
|
|
12
|
+
report.warn('policy/external-count', `state ${state} has ${externals.length} external transitions`);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function warnDeadProducedData(def, report) {
|
|
17
|
+
if (!def.dataFlowGraph)
|
|
18
|
+
return;
|
|
19
|
+
const dead = def.dataFlowGraph.deadData();
|
|
20
|
+
for (const key of dead) {
|
|
21
|
+
report.warn('policy/dead-data', `produced but never consumed: ${key}`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function warnOverwideProcessors(def, report) {
|
|
25
|
+
for (const t of def.transitions) {
|
|
26
|
+
if (t.processor && t.processor.produces.length > 3) {
|
|
27
|
+
report.warn('policy/overwide-processor', `${t.processor.name} produces ${t.processor.produces.length} types; consider splitting it`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export function allDefaultPolicies() {
|
|
32
|
+
return [
|
|
33
|
+
warnTerminalWithOutgoing,
|
|
34
|
+
warnTooManyExternals,
|
|
35
|
+
warnDeadProducedData,
|
|
36
|
+
warnOverwideProcessors,
|
|
37
|
+
];
|
|
38
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { FlowDefinition } from '@unlaxer/tramli';
|
|
2
|
+
import type { AnalysisPlugin, PluginDescriptor, PluginReport } from '../api/types.js';
|
|
3
|
+
import type { FlowPolicy } from './types.js';
|
|
4
|
+
export declare class PolicyLintPlugin<S extends string> implements AnalysisPlugin<S> {
|
|
5
|
+
private readonly policies;
|
|
6
|
+
constructor(policies: FlowPolicy<S>[]);
|
|
7
|
+
static defaults<S extends string>(): PolicyLintPlugin<S>;
|
|
8
|
+
descriptor(): PluginDescriptor;
|
|
9
|
+
kind(): "ANALYSIS";
|
|
10
|
+
analyze(definition: FlowDefinition<S>, report: PluginReport): void;
|
|
11
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { allDefaultPolicies } from './default-flow-policies.js';
|
|
2
|
+
export class PolicyLintPlugin {
|
|
3
|
+
policies;
|
|
4
|
+
constructor(policies) {
|
|
5
|
+
this.policies = policies;
|
|
6
|
+
}
|
|
7
|
+
static defaults() {
|
|
8
|
+
return new PolicyLintPlugin(allDefaultPolicies());
|
|
9
|
+
}
|
|
10
|
+
descriptor() {
|
|
11
|
+
return {
|
|
12
|
+
id: 'policy-lint',
|
|
13
|
+
displayName: 'Policy Lint',
|
|
14
|
+
description: 'Applies design-time lint policies to a flow definition.',
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
kind() { return 'ANALYSIS'; }
|
|
18
|
+
analyze(definition, report) {
|
|
19
|
+
for (const policy of this.policies) {
|
|
20
|
+
policy(definition, report);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { FlowEngine } from '@unlaxer/tramli';
|
|
2
|
+
import type { EnginePlugin, PluginDescriptor } from '../api/types.js';
|
|
3
|
+
export interface TelemetryEvent {
|
|
4
|
+
type: 'transition' | 'guard' | 'error' | 'state';
|
|
5
|
+
flowId: string;
|
|
6
|
+
flowName: string;
|
|
7
|
+
data: Record<string, unknown>;
|
|
8
|
+
timestamp: Date;
|
|
9
|
+
}
|
|
10
|
+
export interface TelemetrySink {
|
|
11
|
+
emit(event: TelemetryEvent): void;
|
|
12
|
+
events(): readonly TelemetryEvent[];
|
|
13
|
+
}
|
|
14
|
+
export declare class InMemoryTelemetrySink implements TelemetrySink {
|
|
15
|
+
private log;
|
|
16
|
+
emit(event: TelemetryEvent): void;
|
|
17
|
+
events(): readonly TelemetryEvent[];
|
|
18
|
+
}
|
|
19
|
+
export declare class ObservabilityEnginePlugin implements EnginePlugin {
|
|
20
|
+
private readonly sink;
|
|
21
|
+
constructor(sink: TelemetrySink);
|
|
22
|
+
descriptor(): PluginDescriptor;
|
|
23
|
+
kind(): "ENGINE";
|
|
24
|
+
install(engine: FlowEngine): void;
|
|
25
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export class InMemoryTelemetrySink {
|
|
2
|
+
log = [];
|
|
3
|
+
emit(event) { this.log.push(event); }
|
|
4
|
+
events() { return this.log; }
|
|
5
|
+
}
|
|
6
|
+
export class ObservabilityEnginePlugin {
|
|
7
|
+
sink;
|
|
8
|
+
constructor(sink) {
|
|
9
|
+
this.sink = sink;
|
|
10
|
+
}
|
|
11
|
+
descriptor() {
|
|
12
|
+
return { id: 'observability', displayName: 'Observability', description: 'Telemetry via engine logger hooks' };
|
|
13
|
+
}
|
|
14
|
+
kind() { return 'ENGINE'; }
|
|
15
|
+
install(engine) {
|
|
16
|
+
engine.setTransitionLogger(entry => {
|
|
17
|
+
this.sink.emit({
|
|
18
|
+
type: 'transition', flowId: entry.flowId, flowName: '',
|
|
19
|
+
data: { from: entry.from, to: entry.to, trigger: entry.trigger },
|
|
20
|
+
timestamp: new Date(),
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
engine.setErrorLogger(entry => {
|
|
24
|
+
this.sink.emit({
|
|
25
|
+
type: 'error', flowId: entry.flowId, flowName: '',
|
|
26
|
+
data: { from: entry.from, to: entry.to, trigger: entry.trigger, cause: entry.cause?.message },
|
|
27
|
+
timestamp: new Date(),
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { FlowEngine, FlowDefinition, FlowInstance } from '@unlaxer/tramli';
|
|
2
|
+
import type { RuntimeAdapterPlugin, PluginDescriptor } from '../api/types.js';
|
|
3
|
+
export type RichResumeStatus = 'TRANSITIONED' | 'ALREADY_COMPLETE' | 'NO_APPLICABLE_TRANSITION' | 'REJECTED' | 'EXCEPTION_ROUTED';
|
|
4
|
+
export interface RichResumeResult<S extends string> {
|
|
5
|
+
status: RichResumeStatus;
|
|
6
|
+
flow?: FlowInstance<S>;
|
|
7
|
+
error?: Error;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Enhanced resumeAndExecute with explicit status classification.
|
|
11
|
+
*/
|
|
12
|
+
export declare class RichResumeExecutor {
|
|
13
|
+
private readonly engine;
|
|
14
|
+
constructor(engine: FlowEngine);
|
|
15
|
+
resume<S extends string>(flowId: string, definition: FlowDefinition<S>, externalData: Map<string, unknown>, previousState: S): Promise<RichResumeResult<S>>;
|
|
16
|
+
}
|
|
17
|
+
export declare class RichResumeRuntimePlugin implements RuntimeAdapterPlugin<RichResumeExecutor> {
|
|
18
|
+
descriptor(): PluginDescriptor;
|
|
19
|
+
kind(): "RUNTIME_ADAPTER";
|
|
20
|
+
bind(engine: FlowEngine): RichResumeExecutor;
|
|
21
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enhanced resumeAndExecute with explicit status classification.
|
|
3
|
+
*/
|
|
4
|
+
export class RichResumeExecutor {
|
|
5
|
+
engine;
|
|
6
|
+
constructor(engine) {
|
|
7
|
+
this.engine = engine;
|
|
8
|
+
}
|
|
9
|
+
async resume(flowId, definition, externalData, previousState) {
|
|
10
|
+
try {
|
|
11
|
+
const flow = await this.engine.resumeAndExecute(flowId, definition, externalData);
|
|
12
|
+
if (flow.isCompleted && flow.currentState === previousState) {
|
|
13
|
+
return { status: 'ALREADY_COMPLETE', flow };
|
|
14
|
+
}
|
|
15
|
+
if (flow.currentState === previousState && !flow.isCompleted) {
|
|
16
|
+
return { status: 'REJECTED', flow };
|
|
17
|
+
}
|
|
18
|
+
return { status: 'TRANSITIONED', flow };
|
|
19
|
+
}
|
|
20
|
+
catch (e) {
|
|
21
|
+
if (e.code === 'FLOW_ALREADY_COMPLETED') {
|
|
22
|
+
return { status: 'ALREADY_COMPLETE', error: e };
|
|
23
|
+
}
|
|
24
|
+
if (e.code === 'FLOW_NOT_FOUND') {
|
|
25
|
+
return { status: 'NO_APPLICABLE_TRANSITION', error: e };
|
|
26
|
+
}
|
|
27
|
+
if (e.code === 'INVALID_TRANSITION') {
|
|
28
|
+
return { status: 'NO_APPLICABLE_TRANSITION', error: e };
|
|
29
|
+
}
|
|
30
|
+
return { status: 'EXCEPTION_ROUTED', error: e };
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export class RichResumeRuntimePlugin {
|
|
35
|
+
descriptor() {
|
|
36
|
+
return { id: 'rich-resume', displayName: 'Rich Resume', description: 'Enhanced resume with status classification' };
|
|
37
|
+
}
|
|
38
|
+
kind() { return 'RUNTIME_ADAPTER'; }
|
|
39
|
+
bind(engine) {
|
|
40
|
+
return new RichResumeExecutor(engine);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { FlowDefinition } from '@unlaxer/tramli';
|
|
2
|
+
/**
|
|
3
|
+
* Validates that a subflow's entry requirements are satisfied
|
|
4
|
+
* by the parent flow's available data at a given state plus any guaranteed types.
|
|
5
|
+
*/
|
|
6
|
+
export declare class GuaranteedSubflowValidator {
|
|
7
|
+
validate<S extends string, T extends string>(parent: FlowDefinition<S>, parentState: S, subflow: FlowDefinition<T>, guaranteedTypes: Set<string>): void;
|
|
8
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validates that a subflow's entry requirements are satisfied
|
|
3
|
+
* by the parent flow's available data at a given state plus any guaranteed types.
|
|
4
|
+
*/
|
|
5
|
+
export class GuaranteedSubflowValidator {
|
|
6
|
+
validate(parent, parentState, subflow, guaranteedTypes) {
|
|
7
|
+
if (!parent.dataFlowGraph || !subflow.dataFlowGraph) {
|
|
8
|
+
throw new Error('DataFlowGraph is required for subflow validation');
|
|
9
|
+
}
|
|
10
|
+
const available = new Set(parent.dataFlowGraph.availableAt(parentState));
|
|
11
|
+
for (const t of guaranteedTypes)
|
|
12
|
+
available.add(t);
|
|
13
|
+
const initialState = subflow.initialState;
|
|
14
|
+
if (initialState == null) {
|
|
15
|
+
throw new Error(`Subflow ${subflow.name} has no initial state`);
|
|
16
|
+
}
|
|
17
|
+
const requiredAtEntry = subflow.dataFlowGraph.availableAt(initialState);
|
|
18
|
+
const missing = [];
|
|
19
|
+
for (const req of requiredAtEntry) {
|
|
20
|
+
if (!available.has(req))
|
|
21
|
+
missing.push(req);
|
|
22
|
+
}
|
|
23
|
+
if (missing.length > 0) {
|
|
24
|
+
throw new Error(`Subflow ${subflow.name} is missing guaranteed types at entry: [${missing.join(', ')}]`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { FlowDefinition } from '@unlaxer/tramli';
|
|
2
|
+
import type { GenerationPlugin, PluginDescriptor } from '../api/types.js';
|
|
3
|
+
import type { FlowTestPlan } from './types.js';
|
|
4
|
+
export declare class ScenarioGenerationPlugin<S extends string> implements GenerationPlugin<FlowDefinition<S>, FlowTestPlan> {
|
|
5
|
+
private readonly delegate;
|
|
6
|
+
descriptor(): PluginDescriptor;
|
|
7
|
+
kind(): "GENERATION";
|
|
8
|
+
generate(input: FlowDefinition<S>): FlowTestPlan;
|
|
9
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ScenarioTestPlugin } from './scenario-test-plugin.js';
|
|
2
|
+
export class ScenarioGenerationPlugin {
|
|
3
|
+
delegate = new ScenarioTestPlugin();
|
|
4
|
+
descriptor() {
|
|
5
|
+
return {
|
|
6
|
+
id: 'scenario-tests',
|
|
7
|
+
displayName: 'Scenario Test Generator',
|
|
8
|
+
description: 'Produces scenario-oriented test plans from a flow definition.',
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
kind() { return 'GENERATION'; }
|
|
12
|
+
generate(input) {
|
|
13
|
+
return this.delegate.generate(input);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { FlowDefinition } from '@unlaxer/tramli';
|
|
2
|
+
import type { FlowTestPlan } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Generates BDD-style test scenarios from a flow definition.
|
|
5
|
+
*/
|
|
6
|
+
export declare class ScenarioTestPlugin {
|
|
7
|
+
generate<S extends string>(definition: FlowDefinition<S>): FlowTestPlan;
|
|
8
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates BDD-style test scenarios from a flow definition.
|
|
3
|
+
*/
|
|
4
|
+
export class ScenarioTestPlugin {
|
|
5
|
+
generate(definition) {
|
|
6
|
+
const scenarios = [];
|
|
7
|
+
for (const t of definition.transitions) {
|
|
8
|
+
const steps = [];
|
|
9
|
+
steps.push(`given flow in ${t.from}`);
|
|
10
|
+
if (t.type === 'external' && t.guard) {
|
|
11
|
+
steps.push(`when external data satisfies guard ${t.guard.name}`);
|
|
12
|
+
}
|
|
13
|
+
if (t.type === 'auto' && t.processor) {
|
|
14
|
+
steps.push(`when auto processor ${t.processor.name} runs`);
|
|
15
|
+
}
|
|
16
|
+
if (t.type === 'branch' && t.branch) {
|
|
17
|
+
steps.push(`when branch ${t.branch.name} selects a route`);
|
|
18
|
+
}
|
|
19
|
+
steps.push(`then flow reaches ${t.to}`);
|
|
20
|
+
scenarios.push({ name: `${t.from}_to_${t.to}`, steps });
|
|
21
|
+
}
|
|
22
|
+
return { scenarios };
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|