Плагіни
Розширюйте Hopak власними типами полів, middleware та boot-хуками — один явний виклик use().
Додано у 1.0
Плагін — це іменований об’єкт із функцією setup. Реєструється одним явним викликом — ніщо не активується побічним ефектом імпорту:
// main.ts
import { hopak } from '@hopak/core';
import uuid from '@hopak/uuid';
await hopak().use(uuid()).listen();
setup виконується один раз під час boot-у, до сканування моделей — тож тип поля, зареєстрований плагіном, доступний кожному файлу моделі.
Контракт
import type { HopakPlugin } from '@hopak/core';
export default function myPlugin(): HopakPlugin {
return {
name: 'my-plugin',
setup(ctx) {
// ctx.config — resolved HopakConfig (read-only)
// ctx.log — the app logger
// ctx.registerField(type, adapter)
// ctx.before(...) / ctx.after(...) / ctx.wrap(...)
// ctx.onBoot(hook) — runs after every plugin's setup, before routes load
},
};
}
- Плагіни виконуються в порядку реєстрації.
use()ідемпотентний заname— повторна реєстрація того самого плагіна є no-op.use()післяlisten()кидає помилку.- Два різні плагіни, що претендують на той самий тип поля, кидають
PluginErrorз іменем першого власника.
Власні типи полів
Плагін поля постачає дві речі: підклас FieldBuilder (для типізованих моделей) та адаптер (для DDL + валідації):
// hopak-plugin-uuid/index.ts
import * as v from 'valibot';
import { FieldBuilder, type HopakPlugin } from '@hopak/core';
class UuidField extends FieldBuilder<string, false> {
constructor() {
super('uuid');
}
required() {
return this.markAs<UuidField & { __required: true }>(true);
}
}
export const uuid = () => new UuidField();
export default function uuidPlugin(): HopakPlugin {
return {
name: 'hopak-plugin-uuid',
setup(ctx) {
ctx.registerField('uuid', {
storage: 'text',
schema: () => v.pipe(v.string(), v.uuid()),
});
},
};
}
storage каже, як значення зберігається; Hopak сам мапить його на потрібну колонку та DDL в усіх трьох діалектах. Доступні: text, integer, real, boolean, timestamp, json. schema повертає Valibot-схему, яку використовує валідація запитів.
Далі моделі використовують його як будь-яке вбудоване поле, з повним виведенням типів:
import { model, text } from '@hopak/core';
import { uuid } from 'hopak-plugin-uuid';
export default model('device', {
name: text().required(),
externalId: uuid().required().unique(),
});
Middleware плагінів
Middleware, доданий плагіном, виконується перед middleware, доданим через hopak().before() — плагіни працюють на рівні фреймворку:
setup(ctx) {
ctx.before((reqCtx) => {
reqCtx.setHeader('X-Powered-By', 'Hopak');
});
}
Тестування проєкту з плагінами
createTestServer потребує тих самих плагінів, що реєструє застосунок — реєстр полів наповнюється під час setup-у плагіна:
const env = await createTestServer({ rootDir: '.', plugins: [uuidPlugin()] });
Конвенція іменування
Офіційні плагіни живуть під @hopak/*; плагіни спільноти використовують префікс hopak-plugin-* (як eslint-plugin-*).
Що ніколи не буває плагіном
model(), defineRoute(), crud.*, вбудовані типи полів, зв’язки, валідація та серіалізація — це ідентичність Hopak. Плагіни розширюють навколо них, а не замінюють їх.