@restranger/repi-lib0.0.1-ca9b82c
Contracts, service seam registry, TUI display primitives, and session utilities for repi plugins
Untested on omp
This is a pi package. No one has reported whether it works on omp yet. If you try it, add your result below.
Reports 0
No reports yet.
Readme
@restranger/repi-lib
Общая библиотека для независимых repi-плагинов. Это не плагин и не ядро: lib не загружает
расширения, не знает про permissions, modes или ui и не имеет зависимостей от других
@restranger/repi-* пакетов.
Зачем
Pi загружает каждое расширение отдельно и даёт ему ExtensionAPI. Плагинам нужен только один
общий слой, чтобы находить друг друга, когда они установлены вместе, и не падать, когда соседа нет.
lib даёт четыре вещи:
- contracts — id контракта с мажорной версией (
re:permissions@1,re:modes@1,re:ui@1), формы сервисов иReadyEvent; - seam — process-global реестр сервисов с ключом
contract + sessionId; - session — единый способ получить
sessionIdиз контекста хоста; - shared types —
ViewNode,Tone,Disposableи структурные типы Pi, чтобы плагинам не приходилось импортировать друг друга или тянуть тяжёлый host-пакет в тесты.
Провайдер
import { announceReady, getSessionId, publishService } from "@restranger/repi-lib";
export default function permissionsPlugin(pi) {
const registrations = new Map();
pi.on("session_start", (_event, ctx) => {
const sessionId = getSessionId(ctx);
registrations.get(sessionId)?.dispose();
const registration = publishService({
contract: "re:permissions@1",
sessionId,
owner: "repi:permissions",
impl: permissionsService,
});
registrations.set(sessionId, registration);
announceReady(pi.events, {
contract: "re:permissions@1",
version: 1,
sessionId,
owner: "repi:permissions",
capabilities: ["policy-layer", "decision-events"],
});
});
pi.on("session_shutdown", (_event, ctx) => {
const sessionId = getSessionId(ctx);
registrations.get(sessionId)?.dispose();
registrations.delete(sessionId);
});
}
Потребитель
import { getService, onService } from "@restranger/repi-lib";
const current = getService<PermissionsService>("re:permissions@1", sessionId);
if (current) {
attach(current);
} else {
// Standalone path: permissions not installed, keep working without it.
}
const registration = onService<PermissionsService>(
pi.events,
"re:permissions@1",
sessionId,
(permissions) => attach(permissions),
);
pi.on("session_shutdown", () => registration.dispose());
onService сначала подписывается на repi:ready:v1, затем делает pull из реестра: публикация
между этими шагами не теряется. Повторный announce того же сервиса не вызывает обработчик дважды.
Правила
- Плагин зависит от
@restranger/repi-lib, но никогда от другого плагина. - В рантайме — только
getService/onService; никаких статических импортов чужого плагина. import typeиз чужого плагина тоже не нужен: общие типы живут вlib.sessionId— часть ключа. Один процесс может содержать несколько сессий; сервисы не должны протекать между ними.publishServiceодного владельца заменяет свой же сервис при reload; другой владелец — ошибка.disposeидемпотентен и удаляет только свою запись.globalThis+Symbol.forиспользуются потому, что jiti изолирует модули между расширениями. Обычный module-level singleton не будет общим.