Architecture: packages, plugins and the shell
Architecture: packages, plugins and the shell
Section intitulée « Architecture: packages, plugins and the shell »L’application est organisée en trois couches :
packages/ Données + logique pure (aucun kernel, aucun React)plugins/ Couche d'adaptation : événements, handlers, UI déclarativesrc/ (kernel+shell) Bus d'événements, store, transactions, surfaces UILe contrat des actions vient de @scfloworg/actions, copié depuis scflow-core :
type, validation JSON Schema, registre, lots avec références ${nom.chemin},
export en outils IA et journal d’exécution. src/kernel/actions.ts (ActionManager)
n’ajoute que ce qui relève de l’application : transaction du kernel, événements,
dialogues et contexte par défaut. Le shell ne dépend que de ce paquet neutre,
jamais du moteur MCL.
Paquets (packages/<nom>/)
Section intitulée « Paquets (packages/<nom>/) »Un paquet est un module TypeScript autonome :
- les données : types du modèle, valeurs initiales (référencés dans le store) ;
- les traitements : fonctions pures
modèle -> modèle, sélecteurs (référencés dans les handlers du plugin).
Un paquet ne connaît ni le kernel ni React : il est testable directement et réutilisable par plusieurs plugins. Convention : n’exporter que des fonctions et des types (les constantes de valeur ne passent pas par le pont de partage).
Exemple : packages/notes-core (modèle NotesModel + createDocument,
changeContents, renameDocument, markSaved, closeDocument…).
Plugins (plugins/<nom>/)
Section intitulée « Plugins (plugins/<nom>/) »Un plugin est la couche qui relie un ou plusieurs paquets au shell :
- il déclare son UI dans
ui.json(commandes, menus, toolbars, panneaux, réglages, traductions) et la charge avecctx.ui.load("ui.json"); - il enregistre les handlers qui adaptent les événements aux fonctions
pures du paquet (
slice.set((prev) => changeContents(prev, …))) ; - il ne crée des composants que lorsque les composants déclaratifs du shell ne suffisent pas (ex. l’éditeur de notes).
Partage de paquets entre plugins
Section intitulée « Partage de paquets entre plugins »Quand un plugin dépendant réutilise un paquet déjà bundlé par un plugin dont il dépend, il ne doit pas le re-bundler (deux instances d’état/mémoire).
- PL-A (propriétaire) bundlé le paquet A et expose l’instance :
ctx.providePackage("A", A)danssetup(). - PL-B (dépendant) déclare
"dependsOn": ["pl-a"]et marque le paquet dansplugins/pl-b/plugin.json:{ "shared": ["A"] }. - Le builder réécrit alors tous les imports de
Adans le bundle de PL-B — y compris ceux faits à l’intérieur d’un paquet B bundlé — vers un pont paresseux qui lit l’instance enregistrée par PL-A (registre surglobalThis, partagé entre bundles).
Résolution paresseuse : le pont ne lit le registre qu’à l’appel, donc l’ordre
d’évaluation des modules n’a pas d’importance ; dependsOn garantit que PL-A
est actif avant le premier appel de PL-B. Si PL-A est désactivé, PL-B l’est
aussi (désactivation en cascade).
provideApi / useApi restent le mécanisme pour les services de plugin
(un plugin appelle une fonction d’un autre plugin) ; le partage de paquets ne
concerne que les imports statiques de modules.
Démo : plugins/demo-shared-a + plugins/demo-shared-b avec
packages/demo-greeter et packages/demo-farewell. Test :
bun test tests/shared-packages.test.ts.
Documentation sous licence CC BY 4.0 · © les contributeurs de SCFlow ·Retour à supplychainflow.org