@futdevpro/fsm-dynamo 1.15.11 → 1.15.13
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/_specifications/BACKLOG.md +43 -0
- package/build/_modules/state-machine/_models/state-machine-config.interface.d.ts +19 -0
- package/build/_modules/state-machine/_models/state-machine-config.interface.d.ts.map +1 -0
- package/build/_modules/state-machine/_models/state-machine-config.interface.js +3 -0
- package/build/_modules/state-machine/_models/state-machine-config.interface.js.map +1 -0
- package/build/_modules/state-machine/_models/state-machine-event.interface.d.ts +26 -0
- package/build/_modules/state-machine/_models/state-machine-event.interface.d.ts.map +1 -0
- package/build/_modules/state-machine/_models/state-machine-event.interface.js +3 -0
- package/build/_modules/state-machine/_models/state-machine-event.interface.js.map +1 -0
- package/build/_modules/state-machine/_models/state-machine.control-model.d.ts +113 -0
- package/build/_modules/state-machine/_models/state-machine.control-model.d.ts.map +1 -0
- package/build/_modules/state-machine/_models/state-machine.control-model.js +264 -0
- package/build/_modules/state-machine/_models/state-machine.control-model.js.map +1 -0
- package/build/_modules/state-machine/_models/state-transition-result.interface.d.ts +40 -0
- package/build/_modules/state-machine/_models/state-transition-result.interface.d.ts.map +1 -0
- package/build/_modules/state-machine/_models/state-transition-result.interface.js +3 -0
- package/build/_modules/state-machine/_models/state-transition-result.interface.js.map +1 -0
- package/build/_modules/state-machine/_models/state-transition.interface.d.ts +31 -0
- package/build/_modules/state-machine/_models/state-transition.interface.d.ts.map +1 -0
- package/build/_modules/state-machine/_models/state-transition.interface.js +3 -0
- package/build/_modules/state-machine/_models/state-transition.interface.js.map +1 -0
- package/build/_modules/state-machine/index.d.ts +6 -0
- package/build/_modules/state-machine/index.d.ts.map +1 -0
- package/build/_modules/state-machine/index.js +10 -0
- package/build/_modules/state-machine/index.js.map +1 -0
- package/package.json +10 -1
- package/src/_modules/state-machine/_models/state-machine-config.interface.ts +19 -0
- package/src/_modules/state-machine/_models/state-machine-event.interface.ts +25 -0
- package/src/_modules/state-machine/_models/state-machine.control-model.spec.ts +314 -0
- package/src/_modules/state-machine/_models/state-machine.control-model.ts +298 -0
- package/src/_modules/state-machine/_models/state-transition-result.interface.ts +53 -0
- package/src/_modules/state-machine/_models/state-transition.interface.ts +30 -0
- package/src/_modules/state-machine/index.ts +6 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# BACKLOG.md
|
|
2
|
+
|
|
3
|
+
> STRICT PATTERN (MUST FOLLOW EXACTLY)
|
|
4
|
+
> - Entry header: "- [TYPE] (BL-YYYYMMDD-###) TITLE"
|
|
5
|
+
> - Immediately followed by (exact keys, colon-space, indented 2 spaces):
|
|
6
|
+
> - " status: <EMOJI status-key>"
|
|
7
|
+
> - " priority: <low|medium|high|urgent>"
|
|
8
|
+
> - " source: <user|system|assistant>"
|
|
9
|
+
> - " area: <ui|backend|infra|docs|tests|ux|general>"
|
|
10
|
+
> - " details: <single-line summary>"
|
|
11
|
+
> - NO blank line between header and fields (parser reads i+1..i+5).
|
|
12
|
+
> - One blank line between entries (readability).
|
|
13
|
+
> - TYPE values: FEATURE, BUG, IMPROVEMENT, QUESTION, RESEARCH, TASK
|
|
14
|
+
> - STATUS EMOJI: ❌ ⏳ 🔄 ✅ ⚠️ ❓
|
|
15
|
+
> - IDs: unique, format BL-YYYYMMDD-### (increment ###).
|
|
16
|
+
|
|
17
|
+
- [IMPROVEMENT] (BL-20260518-001) DyFM_StateMachine: explicit `_FailureResult` return type a `_failure()` helper-en (TS narrowing)
|
|
18
|
+
status: ⏳ pending
|
|
19
|
+
priority: low
|
|
20
|
+
source: assistant
|
|
21
|
+
area: backend
|
|
22
|
+
details: A `DyFM_StateMachine._failure()` jelenleg `DyFM_StateMachineTransition_Result<TState>` union-t ad vissza. A TS inference emiatt nem narrow-ol az `if (!r.ok) {...}` blokkban (a private helper visszatérése a union mind két ágát beilleszti, és a `_failure()` belső object literal `{ ok: false, ... }` típusa nem narrow-ódik le `false` literal-re). Konzumens kódban `as DyFM_StateMachineTransition_FailureResult<TState>` cast szükséges. Fix: `_failure()` return type-ja explicit `DyFM_StateMachineTransition_FailureResult<TState>` (NEM union). 1-line type-annotation change, 0 viselkedés-változás.
|
|
23
|
+
|
|
24
|
+
- [IMPROVEMENT] (BL-20260518-002) DyFM_StateMachine: `DyFM_Error` wrapping a result.error mezőben
|
|
25
|
+
status: ⏳ pending
|
|
26
|
+
priority: low
|
|
27
|
+
source: assistant
|
|
28
|
+
area: backend
|
|
29
|
+
details: A jelenlegi `DyFM_StateMachineTransition_FailureResult.error` `DyFM_Error | Error` típusú, de a `_failure()` raw `Error`-t propagál (callback throw-jából). Konzisztencia a többi `DyFM_*` modullal: `DyFM_Error({ status: 422, errorCode: 'DyFM-SM-<subcode>', message, error: originalError, issuerService: 'DyFM_StateMachine' })` wrappingot adni a 4 throw-elhető helyen (guard/onLeave/onEnter/persist). Subcode-ok pl. 'DyFM-SM-GR' (guard-rejected throw), 'DyFM-SM-OL' (on-leave), 'DyFM-SM-OE' (on-enter), 'DyFM-SM-PE' (persist). Spec: minden 4 error-path-on `DyFM_Error.getErrorCode(f.error)` assertion.
|
|
30
|
+
|
|
31
|
+
- [TASK] (BL-20260518-003) DyFM_StateMachine: guard-throw + self-loop spec coverage
|
|
32
|
+
status: ⏳ pending
|
|
33
|
+
priority: low
|
|
34
|
+
source: assistant
|
|
35
|
+
area: tests
|
|
36
|
+
details: Két explicit spec hiányzik a FR-006 (52354ba) 22-spec szettjéből: (1) guard callback throw esetén `transition()` `guard-rejected` reason-t ad-e, és a `canTransition()` `false`-t (mindkét helyen catch-eli a impl, de NEM bizonyított teszttel); (2) self-loop transition (`from === to`) explicit spec — a `_findTransition` matchel rá, de nincs lefedve. ~30 LOC spec, no impl change.
|
|
37
|
+
|
|
38
|
+
- [FEATURE] (BL-20260518-004) DyFM_StateMachine: Mongoose persist adapter
|
|
39
|
+
status: ⏳ pending
|
|
40
|
+
priority: medium
|
|
41
|
+
source: assistant
|
|
42
|
+
area: backend
|
|
43
|
+
details: Az MVP-ben (FR-006 / 52354ba) a `persist` callback caller-supplied (CCAP MP1-D maga írja a Mongo updateOne logikát stateVersion optimistic-lock-kal). Egy genericikus adapter ami `DyFM_dataModel`-kötött (Mongoose ControlModel) state-document-et frissít automatikusan, kevesebb boilerplate-tel. API: `createMongoosePersistAdapter({ collection: Model<T>, docFilter: { _id: 'rag-status' } }): DyFM_StateMachineConfig['persist']`. Optimistic-lock: `findOneAndUpdate({ ...filter, stateVersion: prev }, { $set: { state, stateVersion: next, lastTransitionAt: timestamp } })` — match nélkül `'persist-failed'`. Tervek szerint a consumer real-world tapasztalat (MP1-D landolása) után írandó, hogy a tényleges minta visszahatáson alapuljon.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { DyFM_StateMachineEvent } from './state-machine-event.interface';
|
|
2
|
+
import { DyFM_StateTransition } from './state-transition.interface';
|
|
3
|
+
/**
|
|
4
|
+
* Config-objektum a `DyFM_StateMachine` konstruktorhoz.
|
|
5
|
+
*
|
|
6
|
+
* - `initial`: induloallapot
|
|
7
|
+
* - `transitions`: a megengedett atmenetek listaja (transitionMap)
|
|
8
|
+
* - `persist`: opcionalis aszinkron callback minden sikeres atmenet utan
|
|
9
|
+
* (NEM a state-valtas elott — a state mar uj-erteku amikor a `persist`
|
|
10
|
+
* fut, igy a callback-be erkezo event tartalmazza a `to`-t es a `stateVersion`-t).
|
|
11
|
+
* Throw-ja kivaltja a `'persist-failed'` reason-t es a state automatikusan
|
|
12
|
+
* visszaall a from-ra (atomicity).
|
|
13
|
+
*/
|
|
14
|
+
export interface DyFM_StateMachineConfig<TState extends string, TContext = unknown> {
|
|
15
|
+
initial: TState;
|
|
16
|
+
transitions: DyFM_StateTransition<TState, TContext>[];
|
|
17
|
+
persist?: (event: DyFM_StateMachineEvent<TState, TContext>) => Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=state-machine-config.interface.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-machine-config.interface.d.ts","sourceRoot":"","sources":["../../../../src/_modules/state-machine/_models/state-machine-config.interface.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AACzE,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAEpE;;;;;;;;;;GAUG;AACH,MAAM,WAAW,uBAAuB,CAAC,MAAM,SAAS,MAAM,EAAE,QAAQ,GAAG,OAAO;IAChF,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,oBAAoB,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,CAAC;IACtD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,sBAAsB,CAAC,MAAM,EAAE,QAAQ,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9E"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-machine-config.interface.js","sourceRoot":"","sources":["../../../../src/_modules/state-machine/_models/state-machine-config.interface.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Egy sikeres atmenet utan kibocsatott esemeny.
|
|
3
|
+
*
|
|
4
|
+
* A `DyFM_StateMachine.events$` Observable-en at szubszkribalhato. Az opcionalis
|
|
5
|
+
* `persist` callback is ezt az objektumot kapja meg, igy a konzumens (pl.
|
|
6
|
+
* Mongoose ControlModel) eldontheti milyen mezoket frissit.
|
|
7
|
+
*
|
|
8
|
+
* `stateVersion` minden sikeres atmenet utan inkrementalodik (0 -> 1 -> 2 -> ...).
|
|
9
|
+
* Optimistic-concurrency token-kent hasznalhato a `persist` callback-ben
|
|
10
|
+
* (`$inc: { stateVersion: 1 }` + `findOneAndUpdate({ stateVersion: prevVersion })`
|
|
11
|
+
* mintara) hogy a race conditions detektalhatoak legyenek tobb-folyamatos
|
|
12
|
+
* deploy-okban.
|
|
13
|
+
*/
|
|
14
|
+
export interface DyFM_StateMachineEvent<TState extends string, TContext = unknown> {
|
|
15
|
+
/** Az atmenet elotti state. */
|
|
16
|
+
from: TState;
|
|
17
|
+
/** Az atmenet utani state. */
|
|
18
|
+
to: TState;
|
|
19
|
+
/** A frissitett state-version szam (>= 1). */
|
|
20
|
+
stateVersion: number;
|
|
21
|
+
/** Az opcionalis context, ahogy a `transition()`-be erkezett. */
|
|
22
|
+
context?: TContext;
|
|
23
|
+
/** Az atmenet idobelyege (ms epoch). */
|
|
24
|
+
timestamp: number;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=state-machine-event.interface.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-machine-event.interface.d.ts","sourceRoot":"","sources":["../../../../src/_modules/state-machine/_models/state-machine-event.interface.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,sBAAsB,CAAC,MAAM,SAAS,MAAM,EAAE,QAAQ,GAAG,OAAO;IAC/E,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,8BAA8B;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,8CAA8C;IAC9C,YAAY,EAAE,MAAM,CAAC;IACrB,iEAAiE;IACjE,OAAO,CAAC,EAAE,QAAQ,CAAC;IACnB,wCAAwC;IACxC,SAAS,EAAE,MAAM,CAAC;CACnB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-machine-event.interface.js","sourceRoot":"","sources":["../../../../src/_modules/state-machine/_models/state-machine-event.interface.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { Observable } from 'rxjs';
|
|
2
|
+
import { DyFM_StateMachineConfig } from './state-machine-config.interface';
|
|
3
|
+
import { DyFM_StateMachineEvent } from './state-machine-event.interface';
|
|
4
|
+
import { DyFM_StateMachineTransition_Result } from './state-transition-result.interface';
|
|
5
|
+
/**
|
|
6
|
+
* Generic finite state machine (FR-006, BL-20260518-004).
|
|
7
|
+
*
|
|
8
|
+
* Konfiguralhato `initial` state-tel + `transitions[]` listaval. Reaktiv
|
|
9
|
+
* (`state$` BehaviorSubject + `events$` Subject), single-instance mutex-szel
|
|
10
|
+
* vedett, opcionalis aszinkron `persist` callback-kel.
|
|
11
|
+
*
|
|
12
|
+
* **Hasznalat (CCAP MP1-D RAG ingest minta):**
|
|
13
|
+
* ```typescript
|
|
14
|
+
* enum RagIngestState { Idle='idle', Ingesting='ingesting', Embedding='embedding',
|
|
15
|
+
* Ready='ready', Degraded='degraded', Reingesting='reingesting' }
|
|
16
|
+
*
|
|
17
|
+
* const ragFsm = new DyFM_StateMachine<RagIngestState>({
|
|
18
|
+
* initial: RagIngestState.Idle,
|
|
19
|
+
* transitions: [
|
|
20
|
+
* { from: RagIngestState.Idle, to: RagIngestState.Ingesting },
|
|
21
|
+
* { from: RagIngestState.Ingesting, to: RagIngestState.Embedding,
|
|
22
|
+
* guard: ctx => ctx?.pendingChunks === 0 },
|
|
23
|
+
* { from: RagIngestState.Embedding, to: RagIngestState.Ready,
|
|
24
|
+
* onEnter: () => persistCorpusVersion() },
|
|
25
|
+
* { from: RagIngestState.Ready, to: RagIngestState.Degraded },
|
|
26
|
+
* { from: RagIngestState.Degraded, to: RagIngestState.Reingesting },
|
|
27
|
+
* // Wildcard array: barmely "futasi" allapotbol degraded-re
|
|
28
|
+
* { from: [RagIngestState.Ingesting, RagIngestState.Embedding,
|
|
29
|
+
* RagIngestState.Reingesting], to: RagIngestState.Degraded },
|
|
30
|
+
* ],
|
|
31
|
+
* persist: async event => await mongoCollection.updateOne(
|
|
32
|
+
* { _id: 'rag-status', stateVersion: event.stateVersion - 1 },
|
|
33
|
+
* { $set: { state: event.to, stateVersion: event.stateVersion } },
|
|
34
|
+
* ),
|
|
35
|
+
* });
|
|
36
|
+
*
|
|
37
|
+
* const result = await ragFsm.transition(RagIngestState.Ingesting);
|
|
38
|
+
* if (result.ok) { console.log('Now in:', result.to, 'v:', result.stateVersion); }
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* **Atomicity:** Az `onEnter` vagy `persist` callback throw-ja eseten a state
|
|
42
|
+
* automatikusan visszaall a from-ra (revert). Ha az `onLeave` throw-ol, a
|
|
43
|
+
* state-valtas mar el sem kezdodik (early-return).
|
|
44
|
+
*
|
|
45
|
+
* **Mutex:** Egy-instance-en belul a `transition()` hivasok sorosak (in-memory
|
|
46
|
+
* Promise-queue). Tobb-instance / tobb-folyamatos race-condition-re az
|
|
47
|
+
* opcionalis `persist` callback `stateVersion`-t hasznalja optimistic-locking-hez.
|
|
48
|
+
*/
|
|
49
|
+
export declare class DyFM_StateMachine<TState extends string, TContext = unknown> {
|
|
50
|
+
/** Aktualis state. */
|
|
51
|
+
private _state;
|
|
52
|
+
/** Aktualis state-version (0 = initial; minden sikeres atmenet utan +1). */
|
|
53
|
+
private _stateVersion;
|
|
54
|
+
/** Reactive state-source. */
|
|
55
|
+
private readonly _state$;
|
|
56
|
+
/** Public reactive state observable. */
|
|
57
|
+
readonly state$: Observable<TState>;
|
|
58
|
+
/** Reactive event-stream — minden sikeres atmenet utan emittal. */
|
|
59
|
+
private readonly _events$;
|
|
60
|
+
/** Public reactive event observable. */
|
|
61
|
+
readonly events$: Observable<DyFM_StateMachineEvent<TState, TContext>>;
|
|
62
|
+
/** Config snapshot. */
|
|
63
|
+
private readonly _config;
|
|
64
|
+
/** Mutex tail — soros transition() hivasokhoz. */
|
|
65
|
+
private _mutexTail;
|
|
66
|
+
constructor(config: DyFM_StateMachineConfig<TState, TContext>);
|
|
67
|
+
/**
|
|
68
|
+
* Sync getter — az aktualis state.
|
|
69
|
+
*/
|
|
70
|
+
state(): TState;
|
|
71
|
+
/**
|
|
72
|
+
* Sync getter — az aktualis state-version.
|
|
73
|
+
*/
|
|
74
|
+
stateVersion(): number;
|
|
75
|
+
/**
|
|
76
|
+
* Megnezi hogy van-e olyan transition ami a jelenlegi state-bol a kert
|
|
77
|
+
* `to`-ba vezet, ES (ha van guard) a guard `true`-t adna a kontextusra.
|
|
78
|
+
*
|
|
79
|
+
* NEM fut le onLeave / onEnter / persist callback.
|
|
80
|
+
*/
|
|
81
|
+
canTransition(to: TState, ctx?: TContext): Promise<boolean>;
|
|
82
|
+
/**
|
|
83
|
+
* Atmenet vegrehajtasa a kert `to` state-re.
|
|
84
|
+
*
|
|
85
|
+
* Flow:
|
|
86
|
+
* 1. Mutex-acquire — soros vegrehajtas egy instance-en
|
|
87
|
+
* 2. Transition-keres (`_findTransition`); ha nincs → `invalid-transition`
|
|
88
|
+
* 3. `guard(ctx)` — ha false → `guard-rejected`
|
|
89
|
+
* 4. `onLeave(ctx)` — throw → `on-leave-failed` (state meg NEM valtozott)
|
|
90
|
+
* 5. State + version frissites, `state$.next()` emit
|
|
91
|
+
* 6. `onEnter(ctx)` — throw → revert state + `on-enter-failed`
|
|
92
|
+
* 7. `persist(event)` if configured — throw → revert state + `persist-failed`
|
|
93
|
+
* 8. `events$.next(event)` emit
|
|
94
|
+
* 9. Mutex-release, return ok
|
|
95
|
+
*/
|
|
96
|
+
transition(to: TState, ctx?: TContext): Promise<DyFM_StateMachineTransition_Result<TState>>;
|
|
97
|
+
/** Mutex-protected belso transition logika. */
|
|
98
|
+
private _transitionInternal;
|
|
99
|
+
/**
|
|
100
|
+
* Lookup: a `transitions[]` listaban olyat keres, aminek `from` matchel a
|
|
101
|
+
* `current`-tel (vagy `'*'` wildcard), ES `to` matchel a `target`-tel.
|
|
102
|
+
*
|
|
103
|
+
* Az elso talalatot adja vissza (a kepes ezert a config-ban a specifikusabb
|
|
104
|
+
* jojjon eloszor a wildcardnel — de a peldakban a wildcard tartalmazza az
|
|
105
|
+
* 'invalidat' is, igy az ordering MUST nem szigoru a tipikus eseteknel).
|
|
106
|
+
*/
|
|
107
|
+
private _findTransition;
|
|
108
|
+
/**
|
|
109
|
+
* Belso helper a sikertelen result epitesere.
|
|
110
|
+
*/
|
|
111
|
+
private _failure;
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=state-machine.control-model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-machine.control-model.d.ts","sourceRoot":"","sources":["../../../../src/_modules/state-machine/_models/state-machine.control-model.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,UAAU,EAAW,MAAM,MAAM,CAAC;AAE5D,OAAO,EAAE,uBAAuB,EAAE,MAAM,kCAAkC,CAAC;AAC3E,OAAO,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AAEzE,OAAO,EAEL,kCAAkC,EACnC,MAAM,qCAAqC,CAAC;AAG7C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,qBAAa,iBAAiB,CAAC,MAAM,SAAS,MAAM,EAAE,QAAQ,GAAG,OAAO;IAEtE,sBAAsB;IACtB,OAAO,CAAC,MAAM,CAAS;IAEvB,4EAA4E;IAC5E,OAAO,CAAC,aAAa,CAAa;IAElC,6BAA6B;IAC7B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA0B;IAElD,wCAAwC;IACxC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;IAEpC,mEAAmE;IACnE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoD;IAE7E,wCAAwC;IACxC,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC,sBAAsB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;IAEvE,uBAAuB;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA4C;IAEpE,kDAAkD;IAClD,OAAO,CAAC,UAAU,CAAuC;gBAG7C,MAAM,EAAE,uBAAuB,CAAC,MAAM,EAAE,QAAQ,CAAC;IAmB7D;;OAEG;IACH,KAAK,IAAI,MAAM;IAIf;;OAEG;IACH,YAAY,IAAI,MAAM;IAItB;;;;;OAKG;IACG,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;IAejE;;;;;;;;;;;;;OAaG;IACG,UAAU,CACd,EAAE,EAAE,MAAM,EACV,GAAG,CAAC,EAAE,QAAQ,GACb,OAAO,CAAC,kCAAkC,CAAC,MAAM,CAAC,CAAC;IAgBtD,+CAA+C;YACjC,mBAAmB;IAsFjC;;;;;;;OAOG;IACH,OAAO,CAAC,eAAe;IAgBvB;;OAEG;IACH,OAAO,CAAC,QAAQ;CAcjB"}
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DyFM_StateMachine = void 0;
|
|
4
|
+
const rxjs_1 = require("rxjs");
|
|
5
|
+
/**
|
|
6
|
+
* Generic finite state machine (FR-006, BL-20260518-004).
|
|
7
|
+
*
|
|
8
|
+
* Konfiguralhato `initial` state-tel + `transitions[]` listaval. Reaktiv
|
|
9
|
+
* (`state$` BehaviorSubject + `events$` Subject), single-instance mutex-szel
|
|
10
|
+
* vedett, opcionalis aszinkron `persist` callback-kel.
|
|
11
|
+
*
|
|
12
|
+
* **Hasznalat (CCAP MP1-D RAG ingest minta):**
|
|
13
|
+
* ```typescript
|
|
14
|
+
* enum RagIngestState { Idle='idle', Ingesting='ingesting', Embedding='embedding',
|
|
15
|
+
* Ready='ready', Degraded='degraded', Reingesting='reingesting' }
|
|
16
|
+
*
|
|
17
|
+
* const ragFsm = new DyFM_StateMachine<RagIngestState>({
|
|
18
|
+
* initial: RagIngestState.Idle,
|
|
19
|
+
* transitions: [
|
|
20
|
+
* { from: RagIngestState.Idle, to: RagIngestState.Ingesting },
|
|
21
|
+
* { from: RagIngestState.Ingesting, to: RagIngestState.Embedding,
|
|
22
|
+
* guard: ctx => ctx?.pendingChunks === 0 },
|
|
23
|
+
* { from: RagIngestState.Embedding, to: RagIngestState.Ready,
|
|
24
|
+
* onEnter: () => persistCorpusVersion() },
|
|
25
|
+
* { from: RagIngestState.Ready, to: RagIngestState.Degraded },
|
|
26
|
+
* { from: RagIngestState.Degraded, to: RagIngestState.Reingesting },
|
|
27
|
+
* // Wildcard array: barmely "futasi" allapotbol degraded-re
|
|
28
|
+
* { from: [RagIngestState.Ingesting, RagIngestState.Embedding,
|
|
29
|
+
* RagIngestState.Reingesting], to: RagIngestState.Degraded },
|
|
30
|
+
* ],
|
|
31
|
+
* persist: async event => await mongoCollection.updateOne(
|
|
32
|
+
* { _id: 'rag-status', stateVersion: event.stateVersion - 1 },
|
|
33
|
+
* { $set: { state: event.to, stateVersion: event.stateVersion } },
|
|
34
|
+
* ),
|
|
35
|
+
* });
|
|
36
|
+
*
|
|
37
|
+
* const result = await ragFsm.transition(RagIngestState.Ingesting);
|
|
38
|
+
* if (result.ok) { console.log('Now in:', result.to, 'v:', result.stateVersion); }
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* **Atomicity:** Az `onEnter` vagy `persist` callback throw-ja eseten a state
|
|
42
|
+
* automatikusan visszaall a from-ra (revert). Ha az `onLeave` throw-ol, a
|
|
43
|
+
* state-valtas mar el sem kezdodik (early-return).
|
|
44
|
+
*
|
|
45
|
+
* **Mutex:** Egy-instance-en belul a `transition()` hivasok sorosak (in-memory
|
|
46
|
+
* Promise-queue). Tobb-instance / tobb-folyamatos race-condition-re az
|
|
47
|
+
* opcionalis `persist` callback `stateVersion`-t hasznalja optimistic-locking-hez.
|
|
48
|
+
*/
|
|
49
|
+
class DyFM_StateMachine {
|
|
50
|
+
/** Aktualis state. */
|
|
51
|
+
_state;
|
|
52
|
+
/** Aktualis state-version (0 = initial; minden sikeres atmenet utan +1). */
|
|
53
|
+
_stateVersion = 0;
|
|
54
|
+
/** Reactive state-source. */
|
|
55
|
+
_state$;
|
|
56
|
+
/** Public reactive state observable. */
|
|
57
|
+
state$;
|
|
58
|
+
/** Reactive event-stream — minden sikeres atmenet utan emittal. */
|
|
59
|
+
_events$;
|
|
60
|
+
/** Public reactive event observable. */
|
|
61
|
+
events$;
|
|
62
|
+
/** Config snapshot. */
|
|
63
|
+
_config;
|
|
64
|
+
/** Mutex tail — soros transition() hivasokhoz. */
|
|
65
|
+
_mutexTail = Promise.resolve();
|
|
66
|
+
constructor(config) {
|
|
67
|
+
if (!config || typeof config !== 'object') {
|
|
68
|
+
throw new Error('DyFM_StateMachine: config object required');
|
|
69
|
+
}
|
|
70
|
+
if (typeof config.initial !== 'string') {
|
|
71
|
+
throw new Error('DyFM_StateMachine: config.initial must be a string state value');
|
|
72
|
+
}
|
|
73
|
+
if (!Array.isArray(config.transitions) || config.transitions.length === 0) {
|
|
74
|
+
throw new Error('DyFM_StateMachine: config.transitions must be a non-empty array');
|
|
75
|
+
}
|
|
76
|
+
this._config = config;
|
|
77
|
+
this._state = config.initial;
|
|
78
|
+
this._state$ = new rxjs_1.BehaviorSubject(this._state);
|
|
79
|
+
this.state$ = this._state$.asObservable();
|
|
80
|
+
this._events$ = new rxjs_1.Subject();
|
|
81
|
+
this.events$ = this._events$.asObservable();
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Sync getter — az aktualis state.
|
|
85
|
+
*/
|
|
86
|
+
state() {
|
|
87
|
+
return this._state;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Sync getter — az aktualis state-version.
|
|
91
|
+
*/
|
|
92
|
+
stateVersion() {
|
|
93
|
+
return this._stateVersion;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Megnezi hogy van-e olyan transition ami a jelenlegi state-bol a kert
|
|
97
|
+
* `to`-ba vezet, ES (ha van guard) a guard `true`-t adna a kontextusra.
|
|
98
|
+
*
|
|
99
|
+
* NEM fut le onLeave / onEnter / persist callback.
|
|
100
|
+
*/
|
|
101
|
+
async canTransition(to, ctx) {
|
|
102
|
+
const transition = this._findTransition(this._state, to);
|
|
103
|
+
if (!transition) {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
if (transition.guard) {
|
|
107
|
+
try {
|
|
108
|
+
const guardResult = await transition.guard(ctx);
|
|
109
|
+
return guardResult === true;
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Atmenet vegrehajtasa a kert `to` state-re.
|
|
119
|
+
*
|
|
120
|
+
* Flow:
|
|
121
|
+
* 1. Mutex-acquire — soros vegrehajtas egy instance-en
|
|
122
|
+
* 2. Transition-keres (`_findTransition`); ha nincs → `invalid-transition`
|
|
123
|
+
* 3. `guard(ctx)` — ha false → `guard-rejected`
|
|
124
|
+
* 4. `onLeave(ctx)` — throw → `on-leave-failed` (state meg NEM valtozott)
|
|
125
|
+
* 5. State + version frissites, `state$.next()` emit
|
|
126
|
+
* 6. `onEnter(ctx)` — throw → revert state + `on-enter-failed`
|
|
127
|
+
* 7. `persist(event)` if configured — throw → revert state + `persist-failed`
|
|
128
|
+
* 8. `events$.next(event)` emit
|
|
129
|
+
* 9. Mutex-release, return ok
|
|
130
|
+
*/
|
|
131
|
+
async transition(to, ctx) {
|
|
132
|
+
const release = { resolve: () => { } };
|
|
133
|
+
const myTurn = new Promise((res) => {
|
|
134
|
+
release.resolve = res;
|
|
135
|
+
});
|
|
136
|
+
const prevTail = this._mutexTail;
|
|
137
|
+
this._mutexTail = myTurn;
|
|
138
|
+
try {
|
|
139
|
+
await prevTail;
|
|
140
|
+
return await this._transitionInternal(to, ctx);
|
|
141
|
+
}
|
|
142
|
+
finally {
|
|
143
|
+
release.resolve();
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/** Mutex-protected belso transition logika. */
|
|
147
|
+
async _transitionInternal(to, ctx) {
|
|
148
|
+
const from = this._state;
|
|
149
|
+
// 1. Transition lookup
|
|
150
|
+
const transition = this._findTransition(from, to);
|
|
151
|
+
if (!transition) {
|
|
152
|
+
return this._failure(from, to, 'invalid-transition');
|
|
153
|
+
}
|
|
154
|
+
// 2. Guard check
|
|
155
|
+
if (transition.guard) {
|
|
156
|
+
let guardResult;
|
|
157
|
+
try {
|
|
158
|
+
guardResult = await transition.guard(ctx);
|
|
159
|
+
}
|
|
160
|
+
catch (err) {
|
|
161
|
+
return this._failure(from, to, 'guard-rejected', err);
|
|
162
|
+
}
|
|
163
|
+
if (guardResult !== true) {
|
|
164
|
+
return this._failure(from, to, 'guard-rejected');
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
// 3. onLeave (pre-change)
|
|
168
|
+
if (transition.onLeave) {
|
|
169
|
+
try {
|
|
170
|
+
await transition.onLeave(ctx);
|
|
171
|
+
}
|
|
172
|
+
catch (err) {
|
|
173
|
+
return this._failure(from, to, 'on-leave-failed', err);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// 4. State + version transition (the actual change)
|
|
177
|
+
const newVersion = this._stateVersion + 1;
|
|
178
|
+
this._state = to;
|
|
179
|
+
this._stateVersion = newVersion;
|
|
180
|
+
this._state$.next(this._state);
|
|
181
|
+
// 5. onEnter (post-change, pre-persist)
|
|
182
|
+
if (transition.onEnter) {
|
|
183
|
+
try {
|
|
184
|
+
await transition.onEnter(ctx);
|
|
185
|
+
}
|
|
186
|
+
catch (err) {
|
|
187
|
+
// Revert
|
|
188
|
+
this._state = from;
|
|
189
|
+
this._stateVersion = newVersion - 1;
|
|
190
|
+
this._state$.next(this._state);
|
|
191
|
+
return this._failure(from, to, 'on-enter-failed', err);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
// 6. Persist (post-state-change, post-onEnter)
|
|
195
|
+
const event = {
|
|
196
|
+
from: from,
|
|
197
|
+
to: to,
|
|
198
|
+
stateVersion: newVersion,
|
|
199
|
+
context: ctx,
|
|
200
|
+
timestamp: Date.now(),
|
|
201
|
+
};
|
|
202
|
+
if (this._config.persist) {
|
|
203
|
+
try {
|
|
204
|
+
await this._config.persist(event);
|
|
205
|
+
}
|
|
206
|
+
catch (err) {
|
|
207
|
+
// Revert
|
|
208
|
+
this._state = from;
|
|
209
|
+
this._stateVersion = newVersion - 1;
|
|
210
|
+
this._state$.next(this._state);
|
|
211
|
+
return this._failure(from, to, 'persist-failed', err);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
// 7. Emit event
|
|
215
|
+
this._events$.next(event);
|
|
216
|
+
return {
|
|
217
|
+
ok: true,
|
|
218
|
+
from: from,
|
|
219
|
+
to: to,
|
|
220
|
+
stateVersion: newVersion,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Lookup: a `transitions[]` listaban olyat keres, aminek `from` matchel a
|
|
225
|
+
* `current`-tel (vagy `'*'` wildcard), ES `to` matchel a `target`-tel.
|
|
226
|
+
*
|
|
227
|
+
* Az elso talalatot adja vissza (a kepes ezert a config-ban a specifikusabb
|
|
228
|
+
* jojjon eloszor a wildcardnel — de a peldakban a wildcard tartalmazza az
|
|
229
|
+
* 'invalidat' is, igy az ordering MUST nem szigoru a tipikus eseteknel).
|
|
230
|
+
*/
|
|
231
|
+
_findTransition(current, target) {
|
|
232
|
+
for (const t of this._config.transitions) {
|
|
233
|
+
if (t.to !== target) {
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
if (t.from === '*') {
|
|
237
|
+
return t;
|
|
238
|
+
}
|
|
239
|
+
if (Array.isArray(t.from)) {
|
|
240
|
+
if (t.from.includes(current)) {
|
|
241
|
+
return t;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
else if (t.from === current) {
|
|
245
|
+
return t;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return undefined;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Belso helper a sikertelen result epitesere.
|
|
252
|
+
*/
|
|
253
|
+
_failure(from, attemptedTo, reason, error) {
|
|
254
|
+
return {
|
|
255
|
+
ok: false,
|
|
256
|
+
reason: reason,
|
|
257
|
+
currentState: this._state,
|
|
258
|
+
attemptedTo: attemptedTo,
|
|
259
|
+
error: error,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
exports.DyFM_StateMachine = DyFM_StateMachine;
|
|
264
|
+
//# sourceMappingURL=state-machine.control-model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-machine.control-model.js","sourceRoot":"","sources":["../../../../src/_modules/state-machine/_models/state-machine.control-model.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { DyFM_Error } from '../../../_models/control-models/error.control-model';
|
|
2
|
+
/**
|
|
3
|
+
* Failure reason-ok a `DyFM_StateMachineTransition_Result.reason` mezohoz.
|
|
4
|
+
*
|
|
5
|
+
* - `invalid-transition`: nincs olyan transition a config-ban ami a jelenlegi
|
|
6
|
+
* state-bol a kert `to`-ba vezet.
|
|
7
|
+
* - `guard-rejected`: a transition `guard` callback-je `false`-t adott.
|
|
8
|
+
* - `on-leave-failed`: a transition `onLeave` callback-je throw-olt; state NEM valtozott.
|
|
9
|
+
* - `on-enter-failed`: az `onEnter` callback throw-olt az uj state beallitasa utan; state revert-elve a from-ra.
|
|
10
|
+
* - `persist-failed`: a `persist` callback throw-olt; state revert-elve a from-ra.
|
|
11
|
+
*/
|
|
12
|
+
export type DyFM_StateMachineTransition_FailureReason = 'invalid-transition' | 'guard-rejected' | 'on-leave-failed' | 'on-enter-failed' | 'persist-failed';
|
|
13
|
+
/**
|
|
14
|
+
* Sikeres atmenet eredmenye.
|
|
15
|
+
*/
|
|
16
|
+
export interface DyFM_StateMachineTransition_SuccessResult<TState extends string> {
|
|
17
|
+
ok: true;
|
|
18
|
+
from: TState;
|
|
19
|
+
to: TState;
|
|
20
|
+
/** A frissitett state-version szam (>= 1). */
|
|
21
|
+
stateVersion: number;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Sikertelen atmenet eredmenye.
|
|
25
|
+
*/
|
|
26
|
+
export interface DyFM_StateMachineTransition_FailureResult<TState extends string> {
|
|
27
|
+
ok: false;
|
|
28
|
+
reason: DyFM_StateMachineTransition_FailureReason;
|
|
29
|
+
/** A jelenlegi state amikor a sikertelenseg torent. */
|
|
30
|
+
currentState: TState;
|
|
31
|
+
/** A megkiserelt cel-state. */
|
|
32
|
+
attemptedTo: TState;
|
|
33
|
+
/** Opcionalis hiba ha az atmenet error-ral bukott (pl. on-enter throw, persist throw). */
|
|
34
|
+
error?: DyFM_Error | Error;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Discriminated union — `ok` flag-gel branchol.
|
|
38
|
+
*/
|
|
39
|
+
export type DyFM_StateMachineTransition_Result<TState extends string> = DyFM_StateMachineTransition_SuccessResult<TState> | DyFM_StateMachineTransition_FailureResult<TState>;
|
|
40
|
+
//# sourceMappingURL=state-transition-result.interface.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-transition-result.interface.d.ts","sourceRoot":"","sources":["../../../../src/_modules/state-machine/_models/state-transition-result.interface.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,qDAAqD,CAAC;AAEjF;;;;;;;;;GASG;AACH,MAAM,MAAM,yCAAyC,GACjD,oBAAoB,GACpB,gBAAgB,GAChB,iBAAiB,GACjB,iBAAiB,GACjB,gBAAgB,CAAC;AAGrB;;GAEG;AACH,MAAM,WAAW,yCAAyC,CAAC,MAAM,SAAS,MAAM;IAC9E,EAAE,EAAE,IAAI,CAAC;IACT,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,8CAA8C;IAC9C,YAAY,EAAE,MAAM,CAAC;CACtB;AAGD;;GAEG;AACH,MAAM,WAAW,yCAAyC,CAAC,MAAM,SAAS,MAAM;IAC9E,EAAE,EAAE,KAAK,CAAC;IACV,MAAM,EAAE,yCAAyC,CAAC;IAClD,uDAAuD;IACvD,YAAY,EAAE,MAAM,CAAC;IACrB,+BAA+B;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,0FAA0F;IAC1F,KAAK,CAAC,EAAE,UAAU,GAAG,KAAK,CAAC;CAC5B;AAGD;;GAEG;AACH,MAAM,MAAM,kCAAkC,CAAC,MAAM,SAAS,MAAM,IAChE,yCAAyC,CAAC,MAAM,CAAC,GACjD,yCAAyC,CAAC,MAAM,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-transition-result.interface.js","sourceRoot":"","sources":["../../../../src/_modules/state-machine/_models/state-transition-result.interface.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Egyetlen atmenet (transition) leiras a `DyFM_StateMachine` transitionMap-jeben.
|
|
3
|
+
*
|
|
4
|
+
* `from` lehet:
|
|
5
|
+
* - egy konkret state (TState)
|
|
6
|
+
* - tobb state array-je
|
|
7
|
+
* - `'*'` wildcard (barmely jelenlegi state)
|
|
8
|
+
*
|
|
9
|
+
* `guard` opcionalis pre-check: ha visszater `false` (vagy resolved-`false` Promise),
|
|
10
|
+
* az atmenet visszautasitva `'guard-rejected'` reason-nel.
|
|
11
|
+
*
|
|
12
|
+
* `onLeave` opcionalis side-effect ami a state-valtas ELOTT fut. Throw-ja
|
|
13
|
+
* `'on-leave-failed'` reason-t valt ki.
|
|
14
|
+
*
|
|
15
|
+
* `onEnter` opcionalis side-effect ami az uj state beallitasa UTAN, de a
|
|
16
|
+
* `persist` callback elott fut. Throw-ja `'on-enter-failed'` reason-t valt ki
|
|
17
|
+
* — ilyenkor a state visszaall (revert).
|
|
18
|
+
*/
|
|
19
|
+
export interface DyFM_StateTransition<TState extends string, TContext = unknown> {
|
|
20
|
+
/** Forras state(ek). Wildcard `'*'` = barmilyen jelenlegi state. */
|
|
21
|
+
from: TState | TState[] | '*';
|
|
22
|
+
/** Cel state. */
|
|
23
|
+
to: TState;
|
|
24
|
+
/** Optional pre-check; ha `false`-t ad, az atmenet elutasitva. */
|
|
25
|
+
guard?: (ctx?: TContext) => boolean | Promise<boolean>;
|
|
26
|
+
/** Optional side-effect a state-valtas ELOTT. */
|
|
27
|
+
onLeave?: (ctx?: TContext) => Promise<void>;
|
|
28
|
+
/** Optional side-effect a state-valtas UTAN, a `persist` elott. */
|
|
29
|
+
onEnter?: (ctx?: TContext) => Promise<void>;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=state-transition.interface.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-transition.interface.d.ts","sourceRoot":"","sources":["../../../../src/_modules/state-machine/_models/state-transition.interface.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,oBAAoB,CAAC,MAAM,SAAS,MAAM,EAAE,QAAQ,GAAG,OAAO;IAC7E,oEAAoE;IACpE,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,GAAG,CAAC;IAC9B,iBAAiB;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,kEAAkE;IAClE,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,QAAQ,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACvD,iDAAiD;IACjD,OAAO,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,QAAQ,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,mEAAmE;IACnE,OAAO,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,QAAQ,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-transition.interface.js","sourceRoot":"","sources":["../../../../src/_modules/state-machine/_models/state-transition.interface.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export * from './_models/state-machine.control-model';
|
|
2
|
+
export * from './_models/state-machine-config.interface';
|
|
3
|
+
export * from './_models/state-machine-event.interface';
|
|
4
|
+
export * from './_models/state-transition.interface';
|
|
5
|
+
export * from './_models/state-transition-result.interface';
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/_modules/state-machine/index.ts"],"names":[],"mappings":"AACA,cAAc,uCAAuC,CAAC;AACtD,cAAc,0CAA0C,CAAC;AACzD,cAAc,yCAAyC,CAAC;AACxD,cAAc,sCAAsC,CAAC;AACrD,cAAc,6CAA6C,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
// MODELS
|
|
5
|
+
tslib_1.__exportStar(require("./_models/state-machine.control-model"), exports);
|
|
6
|
+
tslib_1.__exportStar(require("./_models/state-machine-config.interface"), exports);
|
|
7
|
+
tslib_1.__exportStar(require("./_models/state-machine-event.interface"), exports);
|
|
8
|
+
tslib_1.__exportStar(require("./_models/state-transition.interface"), exports);
|
|
9
|
+
tslib_1.__exportStar(require("./_models/state-transition-result.interface"), exports);
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/_modules/state-machine/index.ts"],"names":[],"mappings":";;;AAAA,SAAS;AACT,gFAAsD;AACtD,mFAAyD;AACzD,kFAAwD;AACxD,+EAAqD;AACrD,sFAA4D"}
|