Система плагінів - Rete.js

Система плагінів

Вихідний код

Плагіни пропонують можливість додавати нові функції переважно через одну точку входу. Вони спілкуються один з одним за допомогою сигналів, які поширюються від батьківських плагінів до дочірніх. Оскільки плагіни можуть мати кілька дочірніх плагінів, ці сигнали передаються в тому порядку, в якому вони підключені (це може бути важливо під час включення плагінів, таких як rete-readonly-plugin)

Архітектура

Наступний приклад коду демонструє створення двох скоупів: батьківського та дочірнього. Обидва скоупи логують сигнали.

ts
import { Scope } from 'rete';

const parentScope = new Scope<number>('parent'); // number це продукуємий тип
const childScope = new Scope<string, [number]>('child'); // [number] це очікуваний тип батьківського ланцюга

parentScope.addPipe((context) => { // додати пайп до батьківської області
  console.log('parent', context); // number

  return context;
});

childScope.addPipe((context) => { // додати пайп до дочірньої області
  console.log('child', context); // string | number

  return context;
});

parentScope.use(childScope); // forward all signals to child scope

const returnedNumber = await parentScope.emit(1); // can emit number
const returnedString = await childScope.emit('a'); // can emit string

Майте на увазі, що порядок use і addPipe впливає на порядок виклику батьківського та дочірнього обробників.

Логи:

log
parent 1
child 1
child a

У деяких випадках сигнали можна змінювати або блокувати.

Блокувати або змінити
ts
parentScope.addPipe((context) => {
  return context * 2;
});
childScope.addPipe((context) => {
  if (context === 'b') return // запобігання поширення 'b'
  return context;
});

const doubledNumber = await parentScope.emit(1); // 2
const expectedString = await childScope.emit('a'); // 'a'
const expectedUndefined = await childScope.emit('b'); // undefined

Статична типізація використовується для гарантії того, що очікувані сигнали використовуваних плагінів сумісні з сигналами, створеними батьківським плагіном.

ts
import { Scope } from 'rete';

const parentScope = new Scope<number>('parent');
const childScope = new Scope<string, [number | boolean]>('child');

parentScope.use(childScope); // Type 'boolean' is not assignable to type 'string | number'.ts(2345)

Дочірні плагіни можуть отримати доступ до екземпляра батьківського плагіна як для прямого доступу до його інтерфейсів, так і для продукування сигналів від імені батьківського плагіна

ts
import { Scope } from 'rete';

class Root extends Scope<number> {
  isRoot = true
}

class Root2 extends Scope<number> {
  isRoot2 = true
}

const parentScope = new Root('parent');
const childScope = new Scope<string, [number]>('child');

parentScope.use(childScope);

const parent = childScope.parentScope(); // екземпляр Root, але Scope з точки зору TS
const root = childScope.parentScope<Root>(Root); // екземпляр Root
const wrongInstance = childScope.parentScope<Root2>(Root2); // викидає виняток