Architecture: packages, plugins and the shell
Architecture: packages, plugins and the shell
Section titled “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 UIActions
Section titled “Actions”Le 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 titled “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 titled “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 titled “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 under CC BY 4.0 · © SCFlow contributors ·Back to supplychainflow.org