@mostajs/workflow 0.0.1
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/LICENSE +15 -0
- package/README.md +65 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/preset.d.ts +24 -0
- package/dist/preset.d.ts.map +1 -0
- package/dist/preset.js +101 -0
- package/dist/preset.js.map +1 -0
- package/dist/types.d.ts +56 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +16 -0
- package/dist/types.js.map +1 -0
- package/dist/workflow.d.ts +31 -0
- package/dist/workflow.d.ts.map +1 -0
- package/dist/workflow.js +94 -0
- package/dist/workflow.js.map +1 -0
- package/llms.txt +65 -0
- package/package.json +67 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
@mostajs/workflow
|
|
2
|
+
Copyright (C) 2026 Dr Hamid MADANI <drmdh@msn.com>
|
|
3
|
+
|
|
4
|
+
This program is free software: you can redistribute it and/or modify it under
|
|
5
|
+
the terms of the GNU Affero General Public License as published by the Free
|
|
6
|
+
Software Foundation, either version 3 of the License, or (at your option) any
|
|
7
|
+
later version.
|
|
8
|
+
|
|
9
|
+
This program is distributed in the hope that it will be useful, but WITHOUT
|
|
10
|
+
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
11
|
+
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
12
|
+
details.
|
|
13
|
+
|
|
14
|
+
You should have received a copy of the GNU Affero General Public License
|
|
15
|
+
along with this program. If not, see <https://www.gnu.org/licenses/agpl-3.0.html>.
|
package/README.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# @mostajs/workflow
|
|
2
|
+
|
|
3
|
+
> La **stack** qui assemble le triptyque **State · Rule · Trigger** — `@mostajs/statemachine` (états + historique immuable) + `@mostajs/rules` (gardes, injecté) + `@mostajs/trigger` (réactions) — derrière **une API déclarative**. Aucune logique propre : **câblage + presets**. Modèle Drupal *Workflow + Rules + Trigger*, pattern `-stack` de l'écosystème.
|
|
4
|
+
|
|
5
|
+
**Auteur** : Dr Hamid MADANI <drmdh@msn.com> · **Licence** : AGPL-3.0-or-later
|
|
6
|
+
|
|
7
|
+
C'est la **seule dépendance workflow** de `@mostajs/crm` (+ `@mostajs/numbering`) : le domaine ne câble pas les trois primitives lui-même.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm i @mostajs/workflow @mostajs/statemachine @mostajs/trigger
|
|
13
|
+
# @mostajs/rules : optionnel (seulement si une transition utilise when: { rule })
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Exemple
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import { defineWorkflow, orderWorkflowDef } from "@mostajs/workflow";
|
|
20
|
+
|
|
21
|
+
const base = orderWorkflowDef(); // preset : automate fini de commande (9 états, couleurs + party)
|
|
22
|
+
const emails: string[] = [];
|
|
23
|
+
|
|
24
|
+
const wf = defineWorkflow(
|
|
25
|
+
{
|
|
26
|
+
...base,
|
|
27
|
+
transitions: base.transitions.map((t) =>
|
|
28
|
+
t.id === "notifier" ? { ...t, then: [(tr) => emails.push(`notify:${tr.toState}`)] } : t,
|
|
29
|
+
),
|
|
30
|
+
},
|
|
31
|
+
{ checkPermission: (actorId) => actorId === "admin" },
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const ref = { entityType: "order", entityId: "42" };
|
|
35
|
+
await wf.transition(ref, "init", "notification_attente", { actorId: "admin" });
|
|
36
|
+
// init → notification_attente → relance_emetteur_attente → reponse_attente →
|
|
37
|
+
// relance_destinataire_attente → validation_attente → finalisation_attente → client_informe
|
|
38
|
+
await wf.history(ref); // historique immuable (qui / quand / from→to)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Ce que la stack câble
|
|
42
|
+
|
|
43
|
+
| Déclaratif | Câblé vers |
|
|
44
|
+
|---|---|
|
|
45
|
+
| `transition.when` (fonction ou `{ rule }`) | garde `@mostajs/statemachine` (`{ rule }` résolu via `RuleEvaluator` = `@mostajs/rules`) |
|
|
46
|
+
| `transition.then[]` | triggers `@mostajs/trigger` sur `transition:<id>` |
|
|
47
|
+
| `onPost` de la FSM | `hub.fire("transition:<id>")` puis `"enter:<to>"` (fire-and-forget) |
|
|
48
|
+
| historique immuable | `@mostajs/statemachine` (via `AuditSink` / `@mostajs/audit`) |
|
|
49
|
+
|
|
50
|
+
## Garde déclarative via `@mostajs/rules`
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
import { createKnowledgeBase } from "@mostajs/rules";
|
|
54
|
+
const kb = createKnowledgeBase();
|
|
55
|
+
// transition { id:"valid", from:["attente_admin"], to:"validee", when:{ rule:"transition_allowed(...)" } }
|
|
56
|
+
const wf = defineWorkflow(def, { rules: kb }); // kb.holds(goal, ctx)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## API
|
|
60
|
+
|
|
61
|
+
`defineWorkflow(def, opts?)` → `Workflow`. `wf.availableTransitions/canTransition/transition/history`, `wf.on(event, action)`, `wf.hub`. `WorkflowOptions = { audit?, checkPermission?, now?, rules?, hub? }`. Preset : `orderWorkflowDef()`.
|
|
62
|
+
|
|
63
|
+
## Statut
|
|
64
|
+
|
|
65
|
+
**v0.0.1** — assemblage **implémenté et testé** (`defineWorkflow` câblant `statemachine` + `trigger`, `when` fonction + `{ rule }`, `then`→triggers) + preset `orderWorkflow`. Feuille de route : `docs/03-PLAN-DEV-WORKFLOW.md`. Étude : `docs/01-ETUDE-ETAT-ART-WORKFLOW-07062026.md`.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mostajs/workflow — point d'entrée public (STACK State·Rule·Trigger).
|
|
3
|
+
*
|
|
4
|
+
* Assemble @mostajs/statemachine + @mostajs/rules (injecté) + @mostajs/trigger
|
|
5
|
+
* en un workflow clé en main. Seule dépendance de @mostajs/crm (+ numbering).
|
|
6
|
+
*
|
|
7
|
+
* @author Dr Hamid MADANI <drmdh@msn.com>
|
|
8
|
+
* @license AGPL-3.0-or-later
|
|
9
|
+
*/
|
|
10
|
+
export { type RuleEvaluator, type RuleRef, type WhenSpec, type WorkflowAction, type WorkflowTransition, type WorkflowDef, type WorkflowOptions, WorkflowError, MissingRuleEvaluatorError, } from "./types.js";
|
|
11
|
+
export { Workflow, defineWorkflow } from "./workflow.js";
|
|
12
|
+
export { orderWorkflowDef, orderWorkflowBundles, ORDER_STATE_IDS, type OrderBundle, } from "./preset.js";
|
|
13
|
+
export type { EntityRef, ExecutedTransition, State } from "@mostajs/statemachine";
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,OAAO,EACZ,KAAK,QAAQ,EACb,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,aAAa,EACb,yBAAyB,GAC1B,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,eAAe,EACf,KAAK,WAAW,GACjB,MAAM,aAAa,CAAC;AAGrB,YAAY,EAAE,SAAS,EAAE,kBAAkB,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mostajs/workflow — point d'entrée public (STACK State·Rule·Trigger).
|
|
3
|
+
*
|
|
4
|
+
* Assemble @mostajs/statemachine + @mostajs/rules (injecté) + @mostajs/trigger
|
|
5
|
+
* en un workflow clé en main. Seule dépendance de @mostajs/crm (+ numbering).
|
|
6
|
+
*
|
|
7
|
+
* @author Dr Hamid MADANI <drmdh@msn.com>
|
|
8
|
+
* @license AGPL-3.0-or-later
|
|
9
|
+
*/
|
|
10
|
+
export { WorkflowError, MissingRuleEvaluatorError, } from "./types.js";
|
|
11
|
+
export { Workflow, defineWorkflow } from "./workflow.js";
|
|
12
|
+
export { orderWorkflowDef, orderWorkflowBundles, ORDER_STATE_IDS, } from "./preset.js";
|
|
13
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAQL,aAAa,EACb,yBAAyB,GAC1B,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,eAAe,GAEhB,MAAM,aAAa,CAAC"}
|
package/dist/preset.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mostajs/workflow — preset `orderWorkflow` : cycle de vie d'une commande de trading.
|
|
3
|
+
*
|
|
4
|
+
* Les 8 états du cahier des charges (§3.5), enrichis d'un **code couleur** (UI), d'une
|
|
5
|
+
* **`party`** (émetteur=interne / receiver=fournisseur) et d'un **libellé = clé i18n**
|
|
6
|
+
* (`order:state.<id>`, traduit FR/EN/AR via `orderWorkflowBundles()`).
|
|
7
|
+
*
|
|
8
|
+
* @author Dr Hamid MADANI <drmdh@msn.com>
|
|
9
|
+
* @license AGPL-3.0-or-later
|
|
10
|
+
*/
|
|
11
|
+
import type { WorkflowDef } from "./types.js";
|
|
12
|
+
/** Bundle de traduction (forme compatible @mostajs/i18n `Bundle`). */
|
|
13
|
+
export interface OrderBundle {
|
|
14
|
+
locale: string;
|
|
15
|
+
namespace: string;
|
|
16
|
+
messages: Record<string, unknown>;
|
|
17
|
+
}
|
|
18
|
+
/** Ordre canonique des états (CDC §3.5). */
|
|
19
|
+
export declare const ORDER_STATE_IDS: readonly ["nouvelle", "en_cours", "attente_fournisseur", "attente_admin", "proformat_etabli", "validee", "terminee", "annulee"];
|
|
20
|
+
/** Définition du workflow de commande (8 états CDC + transitions + permissions par transition). */
|
|
21
|
+
export declare function orderWorkflowDef(): WorkflowDef;
|
|
22
|
+
/** Bundles de traduction (namespace `order`) pour @mostajs/i18n — FR (défaut), EN, AR. */
|
|
23
|
+
export declare function orderWorkflowBundles(): OrderBundle[];
|
|
24
|
+
//# sourceMappingURL=preset.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preset.d.ts","sourceRoot":"","sources":["../src/preset.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,sEAAsE;AACtE,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,4CAA4C;AAC5C,eAAO,MAAM,eAAe,iIASlB,CAAC;AAEX,mGAAmG;AACnG,wBAAgB,gBAAgB,IAAI,WAAW,CA8B9C;AAED,0FAA0F;AAC1F,wBAAgB,oBAAoB,IAAI,WAAW,EAAE,CA6CpD"}
|
package/dist/preset.js
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mostajs/workflow — preset `orderWorkflow` : cycle de vie d'une commande de trading.
|
|
3
|
+
*
|
|
4
|
+
* Les 8 états du cahier des charges (§3.5), enrichis d'un **code couleur** (UI), d'une
|
|
5
|
+
* **`party`** (émetteur=interne / receiver=fournisseur) et d'un **libellé = clé i18n**
|
|
6
|
+
* (`order:state.<id>`, traduit FR/EN/AR via `orderWorkflowBundles()`).
|
|
7
|
+
*
|
|
8
|
+
* @author Dr Hamid MADANI <drmdh@msn.com>
|
|
9
|
+
* @license AGPL-3.0-or-later
|
|
10
|
+
*/
|
|
11
|
+
/** Ordre canonique des états (CDC §3.5). */
|
|
12
|
+
export const ORDER_STATE_IDS = [
|
|
13
|
+
"nouvelle",
|
|
14
|
+
"en_cours",
|
|
15
|
+
"attente_fournisseur",
|
|
16
|
+
"attente_admin",
|
|
17
|
+
"proformat_etabli",
|
|
18
|
+
"validee",
|
|
19
|
+
"terminee",
|
|
20
|
+
"annulee",
|
|
21
|
+
];
|
|
22
|
+
/** Définition du workflow de commande (8 états CDC + transitions + permissions par transition). */
|
|
23
|
+
export function orderWorkflowDef() {
|
|
24
|
+
return {
|
|
25
|
+
name: "order",
|
|
26
|
+
states: [
|
|
27
|
+
{ id: "nouvelle", label: "order:state.nouvelle", initial: true, weight: 0, color: "#3B82F6" },
|
|
28
|
+
{ id: "en_cours", label: "order:state.en_cours", weight: 1, color: "#F59E0B", meta: { party: "sender" } },
|
|
29
|
+
{ id: "attente_fournisseur", label: "order:state.attente_fournisseur", weight: 2, color: "#F97316", meta: { party: "receiver" } },
|
|
30
|
+
{ id: "attente_admin", label: "order:state.attente_admin", weight: 3, color: "#EAB308", meta: { party: "sender" } },
|
|
31
|
+
{ id: "proformat_etabli", label: "order:state.proformat_etabli", weight: 4, color: "#8B5CF6", meta: { party: "receiver" } },
|
|
32
|
+
{ id: "validee", label: "order:state.validee", weight: 5, color: "#10B981", meta: { party: "sender" } },
|
|
33
|
+
{ id: "terminee", label: "order:state.terminee", terminal: true, weight: 6, color: "#059669" },
|
|
34
|
+
{ id: "annulee", label: "order:state.annulee", terminal: true, weight: 7, color: "#EF4444" },
|
|
35
|
+
],
|
|
36
|
+
transitions: [
|
|
37
|
+
{ id: "start", from: ["nouvelle"], to: "en_cours", label: "order:transition.start", permission: "crm.order.transition.start" },
|
|
38
|
+
{ id: "to_supplier", from: ["en_cours"], to: "attente_fournisseur", label: "order:transition.to_supplier", permission: "crm.order.transition.to_supplier" },
|
|
39
|
+
{ id: "supplier_back", from: ["attente_fournisseur"], to: "en_cours", label: "order:transition.supplier_back", permission: "crm.order.transition.supplier_back" },
|
|
40
|
+
{ id: "to_admin", from: ["en_cours", "attente_fournisseur"], to: "attente_admin", label: "order:transition.to_admin", permission: "crm.order.transition.to_admin" },
|
|
41
|
+
{ id: "set_proforma", from: ["attente_admin"], to: "proformat_etabli", label: "order:transition.set_proforma", permission: "crm.order.transition.set_proforma" },
|
|
42
|
+
{ id: "validate", from: ["proformat_etabli"], to: "validee", label: "order:transition.validate", permission: "crm.order.transition.validate" },
|
|
43
|
+
{ id: "finish", from: ["validee"], to: "terminee", label: "order:transition.finish", permission: "crm.order.transition.finish" },
|
|
44
|
+
{
|
|
45
|
+
id: "cancel",
|
|
46
|
+
from: ["nouvelle", "en_cours", "attente_fournisseur", "attente_admin", "proformat_etabli", "validee"],
|
|
47
|
+
to: "annulee",
|
|
48
|
+
label: "order:transition.cancel",
|
|
49
|
+
permission: "crm.order.transition.cancel",
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/** Bundles de traduction (namespace `order`) pour @mostajs/i18n — FR (défaut), EN, AR. */
|
|
55
|
+
export function orderWorkflowBundles() {
|
|
56
|
+
return [
|
|
57
|
+
{
|
|
58
|
+
locale: "fr", namespace: "order",
|
|
59
|
+
messages: {
|
|
60
|
+
state: {
|
|
61
|
+
nouvelle: "Nouvelle commande", en_cours: "En cours de traitement", attente_fournisseur: "En attente fournisseur",
|
|
62
|
+
attente_admin: "En attente admin", proformat_etabli: "Proformat établi", validee: "Validée",
|
|
63
|
+
terminee: "Terminée", annulee: "Annulée",
|
|
64
|
+
},
|
|
65
|
+
transition: {
|
|
66
|
+
start: "Prendre en charge", to_supplier: "Vers fournisseur", supplier_back: "Retour fournisseur",
|
|
67
|
+
to_admin: "Vers admin", set_proforma: "Établir le proformat", validate: "Valider", finish: "Terminer", cancel: "Annuler",
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
locale: "en", namespace: "order",
|
|
73
|
+
messages: {
|
|
74
|
+
state: {
|
|
75
|
+
nouvelle: "New order", en_cours: "In progress", attente_fournisseur: "Awaiting supplier",
|
|
76
|
+
attente_admin: "Awaiting admin", proformat_etabli: "Proforma issued", validee: "Validated",
|
|
77
|
+
terminee: "Completed", annulee: "Cancelled",
|
|
78
|
+
},
|
|
79
|
+
transition: {
|
|
80
|
+
start: "Take in charge", to_supplier: "To supplier", supplier_back: "Supplier reply",
|
|
81
|
+
to_admin: "To admin", set_proforma: "Issue proforma", validate: "Validate", finish: "Complete", cancel: "Cancel",
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
locale: "ar", namespace: "order",
|
|
87
|
+
messages: {
|
|
88
|
+
state: {
|
|
89
|
+
nouvelle: "طلب جديد", en_cours: "قيد المعالجة", attente_fournisseur: "في انتظار المورّد",
|
|
90
|
+
attente_admin: "في انتظار المدير", proformat_etabli: "فاتورة أولية مُعدّة", validee: "مُصادق عليها",
|
|
91
|
+
terminee: "مكتملة", annulee: "ملغاة",
|
|
92
|
+
},
|
|
93
|
+
transition: {
|
|
94
|
+
start: "تكفّل", to_supplier: "إلى المورّد", supplier_back: "رد المورّد",
|
|
95
|
+
to_admin: "إلى المدير", set_proforma: "إعداد الفاتورة الأولية", validate: "مصادقة", finish: "إنهاء", cancel: "إلغاء",
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
];
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=preset.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preset.js","sourceRoot":"","sources":["../src/preset.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mostajs/workflow — types de la stack d'assemblage.
|
|
3
|
+
*
|
|
4
|
+
* @author Dr Hamid MADANI <drmdh@msn.com>
|
|
5
|
+
* @license AGPL-3.0-or-later
|
|
6
|
+
*/
|
|
7
|
+
import type { State, Guard, AuditSink, PermissionCheck, ExecutedTransition } from "@mostajs/statemachine";
|
|
8
|
+
import type { TriggerHub } from "@mostajs/trigger";
|
|
9
|
+
/**
|
|
10
|
+
* Évaluateur de règle **injecté** (interface minimale, satisfaite par
|
|
11
|
+
* `@mostajs/rules` : `(goal, ctx) => holds`). Évite une dépendance dure à rules.
|
|
12
|
+
*/
|
|
13
|
+
export interface RuleEvaluator {
|
|
14
|
+
holds(goal: string, ctx: Record<string, unknown>): boolean | Promise<boolean>;
|
|
15
|
+
}
|
|
16
|
+
/** Référence à une règle nommée/textuelle (Prolog) — résolue via `RuleEvaluator`. */
|
|
17
|
+
export interface RuleRef {
|
|
18
|
+
rule: string;
|
|
19
|
+
}
|
|
20
|
+
/** Garde d'une transition : soit une fonction (statemachine), soit une référence de règle (rules). */
|
|
21
|
+
export type WhenSpec = Guard | RuleRef;
|
|
22
|
+
/** Réaction exécutée après une transition (déléguée à @mostajs/trigger). */
|
|
23
|
+
export type WorkflowAction = (transition: ExecutedTransition) => void | Promise<void>;
|
|
24
|
+
/** Transition de workflow (déclarative) : chemin + permission + garde + réactions. */
|
|
25
|
+
export interface WorkflowTransition {
|
|
26
|
+
id: string;
|
|
27
|
+
from: string[];
|
|
28
|
+
to: string;
|
|
29
|
+
label?: string;
|
|
30
|
+
permission?: string;
|
|
31
|
+
when?: WhenSpec;
|
|
32
|
+
then?: WorkflowAction[];
|
|
33
|
+
}
|
|
34
|
+
/** Définition d'un workflow. */
|
|
35
|
+
export interface WorkflowDef {
|
|
36
|
+
name: string;
|
|
37
|
+
states: State[];
|
|
38
|
+
transitions: WorkflowTransition[];
|
|
39
|
+
}
|
|
40
|
+
/** Options d'assemblage (toutes injectées). */
|
|
41
|
+
export interface WorkflowOptions {
|
|
42
|
+
audit?: AuditSink;
|
|
43
|
+
checkPermission?: PermissionCheck;
|
|
44
|
+
now?: () => Date;
|
|
45
|
+
/** Évaluateur de règles (requis si une transition utilise `when: { rule }`). */
|
|
46
|
+
rules?: RuleEvaluator;
|
|
47
|
+
/** Hub de triggers (défaut : un hub neuf). */
|
|
48
|
+
hub?: TriggerHub;
|
|
49
|
+
}
|
|
50
|
+
export declare class WorkflowError extends Error {
|
|
51
|
+
constructor(message: string);
|
|
52
|
+
}
|
|
53
|
+
/** Une transition utilise `when: { rule }` mais aucun `RuleEvaluator` n'a été injecté. */
|
|
54
|
+
export declare class MissingRuleEvaluatorError extends WorkflowError {
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,KAAK,EACL,KAAK,EACL,SAAS,EACT,eAAe,EACf,kBAAkB,EACnB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC/E;AAED,qFAAqF;AACrF,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,sGAAsG;AACtG,MAAM,MAAM,QAAQ,GAAG,KAAK,GAAG,OAAO,CAAC;AAEvC,4EAA4E;AAC5E,MAAM,MAAM,cAAc,GAAG,CAAC,UAAU,EAAE,kBAAkB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAEtF,sFAAsF;AACtF,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,IAAI,CAAC,EAAE,cAAc,EAAE,CAAC;CACzB;AAED,gCAAgC;AAChC,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,WAAW,EAAE,kBAAkB,EAAE,CAAC;CACnC;AAED,+CAA+C;AAC/C,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IACjB,gFAAgF;IAChF,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,8CAA8C;IAC9C,GAAG,CAAC,EAAE,UAAU,CAAC;CAClB;AAED,qBAAa,aAAc,SAAQ,KAAK;gBAC1B,OAAO,EAAE,MAAM;CAI5B;AACD,0FAA0F;AAC1F,qBAAa,yBAA0B,SAAQ,aAAa;CAAG"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mostajs/workflow — types de la stack d'assemblage.
|
|
3
|
+
*
|
|
4
|
+
* @author Dr Hamid MADANI <drmdh@msn.com>
|
|
5
|
+
* @license AGPL-3.0-or-later
|
|
6
|
+
*/
|
|
7
|
+
export class WorkflowError extends Error {
|
|
8
|
+
constructor(message) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = new.target.name;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
/** Une transition utilise `when: { rule }` mais aucun `RuleEvaluator` n'a été injecté. */
|
|
14
|
+
export class MissingRuleEvaluatorError extends WorkflowError {
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA2DH,MAAM,OAAO,aAAc,SAAQ,KAAK;IACtC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC;IAC9B,CAAC;CACF;AACD,0FAA0F;AAC1F,MAAM,OAAO,yBAA0B,SAAQ,aAAa;CAAG"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mostajs/workflow — la stack : assemble State·Rule·Trigger. AUCUNE logique propre.
|
|
3
|
+
*
|
|
4
|
+
* Câble : `when` → garde @mostajs/statemachine (via RuleEvaluator si `{rule}`),
|
|
5
|
+
* `then` → triggers @mostajs/trigger (sur `transition:<id>`), historique → audit
|
|
6
|
+
* (assuré par statemachine). C'est la seule dépendance de @mostajs/crm (+ numbering).
|
|
7
|
+
*
|
|
8
|
+
* @author Dr Hamid MADANI <drmdh@msn.com>
|
|
9
|
+
* @license AGPL-3.0-or-later
|
|
10
|
+
*/
|
|
11
|
+
import { type ConfigTransition, type EntityRef, type ExecutedTransition, type State, type CanResult, type TransitionInput } from "@mostajs/statemachine";
|
|
12
|
+
import { type TriggerHub, type Action } from "@mostajs/trigger";
|
|
13
|
+
import { type WorkflowDef, type WorkflowOptions } from "./types.js";
|
|
14
|
+
export declare class Workflow {
|
|
15
|
+
readonly name: string;
|
|
16
|
+
readonly hub: TriggerHub;
|
|
17
|
+
private readonly machine;
|
|
18
|
+
constructor(def: WorkflowDef, opts?: WorkflowOptions);
|
|
19
|
+
get states(): ReadonlyMap<string, State>;
|
|
20
|
+
get initial(): string;
|
|
21
|
+
availableTransitions(current: string, input?: TransitionInput): Promise<ConfigTransition[]>;
|
|
22
|
+
canTransition(from: string, to: string, input?: TransitionInput): Promise<CanResult>;
|
|
23
|
+
/** Exécute la transition (journalise via statemachine ; déclenche les réactions via trigger). */
|
|
24
|
+
transition(ref: EntityRef, from: string, to: string, input?: TransitionInput): Promise<ExecutedTransition>;
|
|
25
|
+
history(ref: EntityRef): Promise<ExecutedTransition[]>;
|
|
26
|
+
/** Abonner une réaction supplémentaire (ex. `on("enter:validee", ...)`). */
|
|
27
|
+
on(event: string, action: Action): this;
|
|
28
|
+
}
|
|
29
|
+
/** Définit (et assemble) un workflow à partir des trois primitives. */
|
|
30
|
+
export declare function defineWorkflow(def: WorkflowDef, opts?: WorkflowOptions): Workflow;
|
|
31
|
+
//# sourceMappingURL=workflow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow.d.ts","sourceRoot":"","sources":["../src/workflow.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAGL,KAAK,gBAAgB,EAGrB,KAAK,SAAS,EACd,KAAK,kBAAkB,EACvB,KAAK,KAAK,EACV,KAAK,SAAS,EACd,KAAK,eAAe,EACrB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAoB,KAAK,UAAU,EAAE,KAAK,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAClF,OAAO,EACL,KAAK,WAAW,EAEhB,KAAK,eAAe,EAKrB,MAAM,YAAY,CAAC;AA8BpB,qBAAa,QAAQ;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;gBAEtB,GAAG,EAAE,WAAW,EAAE,IAAI,GAAE,eAAoB;IAkCxD,IAAI,MAAM,IAAI,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,CAEvC;IACD,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAG3F,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,CAAC;IAGpF,iGAAiG;IACjG,UAAU,CACR,GAAG,EAAE,SAAS,EACd,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,EACV,KAAK,CAAC,EAAE,eAAe,GACtB,OAAO,CAAC,kBAAkB,CAAC;IAG9B,OAAO,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAItD,4EAA4E;IAC5E,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;CAIxC;AAED,uEAAuE;AACvE,wBAAgB,cAAc,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,CAAC,EAAE,eAAe,GAAG,QAAQ,CAEjF"}
|
package/dist/workflow.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mostajs/workflow — la stack : assemble State·Rule·Trigger. AUCUNE logique propre.
|
|
3
|
+
*
|
|
4
|
+
* Câble : `when` → garde @mostajs/statemachine (via RuleEvaluator si `{rule}`),
|
|
5
|
+
* `then` → triggers @mostajs/trigger (sur `transition:<id>`), historique → audit
|
|
6
|
+
* (assuré par statemachine). C'est la seule dépendance de @mostajs/crm (+ numbering).
|
|
7
|
+
*
|
|
8
|
+
* @author Dr Hamid MADANI <drmdh@msn.com>
|
|
9
|
+
* @license AGPL-3.0-or-later
|
|
10
|
+
*/
|
|
11
|
+
import { defineMachine, } from "@mostajs/statemachine";
|
|
12
|
+
import { createTriggerHub } from "@mostajs/trigger";
|
|
13
|
+
import { MissingRuleEvaluatorError, } from "./types.js";
|
|
14
|
+
function isRuleRef(when) {
|
|
15
|
+
return typeof when === "object" && when !== null && "rule" in when;
|
|
16
|
+
}
|
|
17
|
+
/** Convertit le `when` déclaratif en garde statemachine (résout `{rule}` via l'évaluateur injecté). */
|
|
18
|
+
function resolveGuard(when, transitionId, rules) {
|
|
19
|
+
if (!when)
|
|
20
|
+
return undefined;
|
|
21
|
+
if (typeof when === "function")
|
|
22
|
+
return when;
|
|
23
|
+
// when est une RuleRef → nécessite un RuleEvaluator (rules)
|
|
24
|
+
if (!rules) {
|
|
25
|
+
throw new MissingRuleEvaluatorError(`Transition "${transitionId}" utilise when:{ rule } mais aucun RuleEvaluator (@mostajs/rules) n'est injecté.`);
|
|
26
|
+
}
|
|
27
|
+
const goal = when.rule;
|
|
28
|
+
return (ctx) => rules.holds(goal, {
|
|
29
|
+
from: ctx.from,
|
|
30
|
+
to: ctx.to,
|
|
31
|
+
actorId: ctx.actorId,
|
|
32
|
+
...(ctx.data ?? {}),
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
export class Workflow {
|
|
36
|
+
name;
|
|
37
|
+
hub;
|
|
38
|
+
machine;
|
|
39
|
+
constructor(def, opts = {}) {
|
|
40
|
+
this.name = def.name;
|
|
41
|
+
this.hub = opts.hub ?? createTriggerHub();
|
|
42
|
+
// 1) Mapper les transitions de workflow → transitions de configuration statemachine.
|
|
43
|
+
const mapped = def.transitions.map((t) => ({
|
|
44
|
+
id: t.id,
|
|
45
|
+
from: t.from,
|
|
46
|
+
to: t.to,
|
|
47
|
+
label: t.label,
|
|
48
|
+
permission: t.permission,
|
|
49
|
+
guard: resolveGuard(t.when, t.id, opts.rules),
|
|
50
|
+
}));
|
|
51
|
+
this.machine = defineMachine({ states: def.states, transitions: mapped }, { audit: opts.audit, checkPermission: opts.checkPermission, now: opts.now });
|
|
52
|
+
// 2) Enregistrer les réactions `then` comme triggers sur `transition:<id>`.
|
|
53
|
+
for (const t of def.transitions) {
|
|
54
|
+
(t.then ?? []).forEach((action, i) => {
|
|
55
|
+
const handler = (ctx) => action(ctx.transition);
|
|
56
|
+
this.hub.define({ name: `${def.name}:${t.id}#${i}`, on: `transition:${t.id}`, do: handler });
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
// 3) Câbler les hooks de la FSM vers le hub (réactions fire-and-forget).
|
|
60
|
+
this.machine.onPost(async (tr) => {
|
|
61
|
+
await this.hub.fire(`transition:${tr.transitionId}`, { transition: tr });
|
|
62
|
+
await this.hub.fire(`enter:${tr.toState}`, { transition: tr });
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
get states() {
|
|
66
|
+
return this.machine.states;
|
|
67
|
+
}
|
|
68
|
+
get initial() {
|
|
69
|
+
return this.machine.initial;
|
|
70
|
+
}
|
|
71
|
+
availableTransitions(current, input) {
|
|
72
|
+
return this.machine.availableTransitions(current, input);
|
|
73
|
+
}
|
|
74
|
+
canTransition(from, to, input) {
|
|
75
|
+
return this.machine.canTransition(from, to, input);
|
|
76
|
+
}
|
|
77
|
+
/** Exécute la transition (journalise via statemachine ; déclenche les réactions via trigger). */
|
|
78
|
+
transition(ref, from, to, input) {
|
|
79
|
+
return this.machine.transition(ref, from, to, input);
|
|
80
|
+
}
|
|
81
|
+
history(ref) {
|
|
82
|
+
return this.machine.history(ref);
|
|
83
|
+
}
|
|
84
|
+
/** Abonner une réaction supplémentaire (ex. `on("enter:validee", ...)`). */
|
|
85
|
+
on(event, action) {
|
|
86
|
+
this.hub.on(event, action);
|
|
87
|
+
return this;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/** Définit (et assemble) un workflow à partir des trois primitives. */
|
|
91
|
+
export function defineWorkflow(def, opts) {
|
|
92
|
+
return new Workflow(def, opts);
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=workflow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow.js","sourceRoot":"","sources":["../src/workflow.ts"],"names":[],"mappings":"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"}
|
package/llms.txt
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# @mostajs/workflow — fiche LLM
|
|
2
|
+
|
|
3
|
+
RÔLE
|
|
4
|
+
STACK d'assemblage du triptyque State·Rule·Trigger (modèle Drupal Workflow+Rules+Trigger).
|
|
5
|
+
AUCUNE logique propre : câble @mostajs/statemachine (états + historique immuable),
|
|
6
|
+
@mostajs/rules (garde `when`, injectée) et @mostajs/trigger (réactions `then`) derrière
|
|
7
|
+
UNE API déclarative defineWorkflow(...). Fournit le preset orderWorkflow (8 états CDC).
|
|
8
|
+
C'est la SEULE dépendance workflow de @mostajs/crm (+ @mostajs/numbering) — le domaine
|
|
9
|
+
ne voit pas les 3 primitives.
|
|
10
|
+
|
|
11
|
+
INSTALL
|
|
12
|
+
npm i @mostajs/workflow @mostajs/statemachine @mostajs/trigger
|
|
13
|
+
# @mostajs/rules : peer OPTIONNELLE (requise seulement si une transition utilise when:{ rule })
|
|
14
|
+
|
|
15
|
+
EXPORTS
|
|
16
|
+
defineWorkflow(def, opts?) -> Workflow
|
|
17
|
+
Workflow : states ; initial ; availableTransitions(current, input?) ; canTransition(from,to,input?)
|
|
18
|
+
transition(ref, from, to, input?) -> Promise<ExecutedTransition> ; history(ref) ; on(event, action) ; hub
|
|
19
|
+
orderWorkflowDef() -> WorkflowDef # preset 8 états CDC
|
|
20
|
+
Types : WorkflowDef, WorkflowTransition { id, from[], to, label?, permission?, when?, then? },
|
|
21
|
+
WhenSpec = Guard | { rule: string }, WorkflowAction = (ExecutedTransition)=>void|Promise,
|
|
22
|
+
RuleEvaluator { holds(goal, ctx) }, WorkflowOptions { audit?, checkPermission?, now?, rules?, hub? }
|
|
23
|
+
Ré-exports : EntityRef, ExecutedTransition, State (depuis @mostajs/statemachine)
|
|
24
|
+
Erreurs : WorkflowError, MissingRuleEvaluatorError
|
|
25
|
+
|
|
26
|
+
CÂBLAGE (ce que fait la stack)
|
|
27
|
+
- transition.when (fonction OU { rule }) -> garde @mostajs/statemachine
|
|
28
|
+
({ rule } est résolu via opts.rules.holds(goal, ctx) — sinon MissingRuleEvaluatorError)
|
|
29
|
+
- transition.then[] -> triggers @mostajs/trigger sur l'événement "transition:<id>"
|
|
30
|
+
- statemachine.onPost -> hub.fire("transition:<id>") puis hub.fire("enter:<to>") (fire-and-forget)
|
|
31
|
+
- historique immuable -> assuré par statemachine (via AuditSink / @mostajs/audit)
|
|
32
|
+
|
|
33
|
+
EXEMPLE
|
|
34
|
+
import { defineWorkflow, orderWorkflowDef } from "@mostajs/workflow";
|
|
35
|
+
const emails = [];
|
|
36
|
+
const wf = defineWorkflow(
|
|
37
|
+
{
|
|
38
|
+
...orderWorkflowDef(),
|
|
39
|
+
transitions: orderWorkflowDef().transitions.map((t) =>
|
|
40
|
+
t.id === "notifier" ? { ...t, then: [(tr) => emails.push(tr.toState)] } : t),
|
|
41
|
+
},
|
|
42
|
+
{ checkPermission: (actorId) => actorId === "admin" }, // sinon permission refusée
|
|
43
|
+
);
|
|
44
|
+
const ref = { entityType: "order", entityId: "42" };
|
|
45
|
+
await wf.transition(ref, "init", "notification_attente", { actorId: "admin" });
|
|
46
|
+
// automate : init → notification_attente → relance_emetteur_attente → reponse_attente →
|
|
47
|
+
// relance_destinataire_attente → validation_attente → finalisation_attente → client_informe
|
|
48
|
+
// chaque état porte color (#RRGGBB) + meta.party ("sender"|"receiver")
|
|
49
|
+
await wf.history(ref); // historique immuable
|
|
50
|
+
|
|
51
|
+
// garde déclarative via @mostajs/rules (when: { rule })
|
|
52
|
+
// defineWorkflow(def, { rules: kb }) où kb.holds(goal, ctx) provient de createKnowledgeBase()
|
|
53
|
+
|
|
54
|
+
PIÈGES
|
|
55
|
+
- Toutes les opérations sont ASYNCHRONES.
|
|
56
|
+
- when:{ rule } SANS opts.rules => MissingRuleEvaluatorError (injecter @mostajs/rules).
|
|
57
|
+
- `then` exécutées en fire-and-forget (trigger) : une réaction qui échoue n'interrompt pas la transition.
|
|
58
|
+
- permission par transition enforcée seulement si opts.checkPermission injecté (sinon métadonnée).
|
|
59
|
+
- ZÉRO logique dans la stack : toute logique appartient à une primitive.
|
|
60
|
+
- Versions peer des 3 primitives à garder compatibles (SemVer).
|
|
61
|
+
|
|
62
|
+
STATUT
|
|
63
|
+
v0.0.1 — assemblage IMPLÉMENTÉ (defineWorkflow câblant statemachine+trigger, when fonction + { rule },
|
|
64
|
+
then->triggers) + preset orderWorkflow, testé. Voir docs/03-PLAN-DEV-WORKFLOW.md.
|
|
65
|
+
Licence AGPL-3.0-or-later. Auteur Dr Hamid MADANI.
|
package/package.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mostajs/workflow",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Turnkey workflow STACK assembling the State·Rule·Trigger triptych: @mostajs/statemachine (states + immutable history) + @mostajs/rules (guards, injected) + @mostajs/trigger (reactions). Single declarative API + orderWorkflow preset. No business logic — wiring only.",
|
|
5
|
+
"author": "Dr Hamid MADANI <drmdh@msn.com>",
|
|
6
|
+
"license": "AGPL-3.0-or-later",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "dist/index.js",
|
|
9
|
+
"types": "dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js",
|
|
14
|
+
"require": "./dist/index.js",
|
|
15
|
+
"default": "./dist/index.js"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"peerDependencies": {
|
|
19
|
+
"@mostajs/statemachine": "^0.0.1",
|
|
20
|
+
"@mostajs/trigger": "^0.0.1",
|
|
21
|
+
"@mostajs/rules": "^0.0.1"
|
|
22
|
+
},
|
|
23
|
+
"peerDependenciesMeta": {
|
|
24
|
+
"@mostajs/rules": {
|
|
25
|
+
"optional": true
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"dist",
|
|
30
|
+
"LICENSE",
|
|
31
|
+
"README.md",
|
|
32
|
+
"llms.txt"
|
|
33
|
+
],
|
|
34
|
+
"keywords": [
|
|
35
|
+
"workflow",
|
|
36
|
+
"stack",
|
|
37
|
+
"state-machine",
|
|
38
|
+
"rules",
|
|
39
|
+
"trigger",
|
|
40
|
+
"eca",
|
|
41
|
+
"orchestration",
|
|
42
|
+
"mosta",
|
|
43
|
+
"mostajs"
|
|
44
|
+
],
|
|
45
|
+
"repository": {
|
|
46
|
+
"type": "git",
|
|
47
|
+
"url": "https://github.com/apolocine/mosta-workflow"
|
|
48
|
+
},
|
|
49
|
+
"homepage": "https://mostajs.dev/packages/workflow",
|
|
50
|
+
"bugs": {
|
|
51
|
+
"url": "https://github.com/apolocine/mosta-workflow/issues"
|
|
52
|
+
},
|
|
53
|
+
"engines": {
|
|
54
|
+
"node": ">=18.0.0"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@types/node": "^22.0.0",
|
|
58
|
+
"typescript": "^5.6.0",
|
|
59
|
+
"@mostajs/statemachine": "^0.0.1",
|
|
60
|
+
"@mostajs/trigger": "^0.0.1"
|
|
61
|
+
},
|
|
62
|
+
"scripts": {
|
|
63
|
+
"build": "tsc",
|
|
64
|
+
"dev": "tsc --watch",
|
|
65
|
+
"test": "node --test test-scripts/*.test.mjs"
|
|
66
|
+
}
|
|
67
|
+
}
|