ForgeTotal.js 5
it

Total.js 5

Costruisci un flusso di componenti con Flow.load() ed eseguilo con exec()

Total.js 5 · total5@0.0.19 flowflowstreamcomponentsautomation Eseguita con Total.js 5

Un FlowStream è un piccolo programma fatto di componenti (caselle con ingressi e uscite) e connessioni tra loro. Total.js lo esegue dentro la tua applicazione: un messaggio entra in un componente, il componente invia risultati alle sue uscite e le connessioni li portano ai componenti successivi. Lo stesso schema è ciò che disegna il designer di Flow di Total.js; qui è scritto a mano.

Flow.load(schema, [callback]) avvia un FlowStream e dà al callback (err, instance). Lo schema è un oggetto:

  • id e name: l'id è la chiave in Flow.instances (Flow.instances.demo); senza id ne viene creato uno casuale.
  • components: un oggetto con il codice sorgente di ogni componente, come testo. Il testo è uno script, <script total>...</script> (la forma che salva il designer).
  • design: le istanze dei componenti. Ognuna ha un id, il component che usa, la sua config e connections, un oggetto le cui chiavi sono id di uscita e i cui valori sono liste di { id, index }: l'istanza e l'id di ingresso che ricevono ciò che viene inviato a quell'uscita.
  • variables: valori che i componenti possono leggere.

Un componente è un modulo: exports.id, exports.name, exports.inputs e exports.outputs (liste di { id, name }), exports.config (i valori predefiniti) e exports.make = function(instance, config). In make() il componente imposta instance.message = function($) {}, che riceve ogni messaggio: $.data sono i dati, $.send(outputid, data) li invia a un'uscita e $.destroy() termina un messaggio che non va oltre.

Per usare il flusso dal tuo codice, instance.exec({ id, data, callback }) invia data all'istanza id e chiama callback(err, message) quando il messaggio termina; l'ultimo messaggio della catena è message (quindi message.data è ciò che ha ricevuto l'ultimo componente). instance.reconfigure(id, config) cambia la configurazione di un'istanza in esecuzione e Flow.remove(id) ferma il FlowStream e lo dimentica. FlowStream è lo stesso oggetto di Flow: due nomi per il gestore.

Un componente è codice che gira con tutti i diritti dell'applicazione (nello stesso thread a meno che lo schema dica worker). Non caricare mai uno schema o un componente che un visitatore può scrivere.

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) });
}