ForgeTotal.js 5
fr

Total.js 5

Construire un flux de composants avec Flow.load() et l'exécuter avec exec()

Total.js 5 · total5@0.0.19 flowflowstreamcomponentsautomation Exécutée avec Total.js 5

Un FlowStream est un petit programme fait de composants (des boîtes avec des entrées et des sorties) et de connexions entre eux. Total.js l'exécute dans votre application : un message entre dans un composant, le composant envoie des résultats à ses sorties et les connexions les portent aux composants suivants. Le même schéma est ce que dessine le concepteur Flow de Total.js ; ici il est écrit à la main.

Flow.load(schema, [callback]) démarre un FlowStream et donne au callback (err, instance). Le schéma est un objet :

  • id et name : l'id est la clé dans Flow.instances (Flow.instances.demo) ; sans id, un id aléatoire est créé.
  • components : un objet avec le code source de chaque composant, sous forme de texte. Le texte est un script, <script total>...</script> (la forme que le concepteur enregistre).
  • design : les instances des composants. Chacune a un id, le component qu'elle utilise, sa config et connections, un objet dont les clés sont des ids de sortie et dont les valeurs sont des listes de { id, index } : l'instance et l'id d'entrée qui reçoivent ce qui est envoyé à cette sortie.
  • variables : des valeurs que les composants peuvent lire.

Un composant est un module : exports.id, exports.name, exports.inputs et exports.outputs (listes de { id, name }), exports.config (les valeurs par défaut) et exports.make = function(instance, config). Dans make(), le composant définit instance.message = function($) {}, qui reçoit chaque message : $.data est la donnée, $.send(outputid, data) l'envoie à une sortie et $.destroy() termine un message qui ne va pas plus loin.

Pour utiliser le flux depuis votre code, instance.exec({ id, data, callback }) envoie data à l'instance id et appelle callback(err, message) quand le message se termine ; le dernier message de la chaîne est message (donc message.data est ce que le dernier composant a reçu). instance.reconfigure(id, config) change la configuration d'une instance en marche et Flow.remove(id) arrête le FlowStream et l'oublie. FlowStream est le même objet que Flow : deux noms pour le gestionnaire.

Un composant est du code qui s'exécute avec tous les droits de l'application (dans le même thread, sauf si le schéma dit worker). Ne chargez jamais un schéma ou un composant qu'un visiteur peut écrire.

definitions/flow.js
const upper = `<script total>
exports.id = 'upper';
exports.name = 'Upper case';
exports.inputs = [{ id: 'input', name: 'Input' }];
exports.outputs = [{ id: 'output', name: 'Output' }];
exports.config = { prefix: '' };
exports.make = function(instance, config) {
	instance.message = function($) {
		$.send('output', config.prefix + String($.data).toUpperCase());
	};
};
</script>`;

const collect = `<script total>
exports.id = 'collect';
exports.name = 'Collect';
exports.inputs = [{ id: 'input', name: 'Input' }];
exports.make = function(instance) {
	instance.message = function($) {
		MAIN.collected.push($.data);
		$.destroy();
	};
};
</script>`;

MAIN.collected = [];

MAIN.flow = null;

Flow.load({
	id: 'demo',
	name: 'Demo',
	components: { upper: upper, collect: collect },
	design: {
		a: { id: 'a', component: 'upper', config: { prefix: '> ' }, connections: { output: [{ id: 'b', index: 'input' }] } },
		b: { id: 'b', component: 'collect', config: {}, connections: {} }
	},
	variables: {}
}, function(err, instance) {
	MAIN.flow = instance;
});
controllers/flow.js
exports.install = function() {
	ROUTE('GET /shout/', shout);
	ROUTE('POST /prefix/ @json', prefix);
	ROUTE('GET /collected/', collected);
	ROUTE('GET /info/', info);
	ROUTE('POST /stop/', stop);
};

function shout($) {
	if (!MAIN.flow)
		return $.invalid('The flow is not running');
	MAIN.flow.exec({ id: 'a', data: $.query.text, callback: (err, message) => $.json({ result: message ? message.data : null }) });
}

function prefix($) {
	MAIN.flow.reconfigure('a', { prefix: $.body.prefix });
	$.json({ ok: true });
}

function collected($) {
	$.json(MAIN.collected);
}

function info($) {
	$.json({ flows: Object.keys(Flow.instances), instances: Object.keys(MAIN.flow.flow.meta.flow), same: Flow === FlowStream });
}

function stop($) {
	Flow.remove('demo');
	MAIN.flow = null;
	$.json({ flows: Object.keys(Flow.instances) });
}