Документація / Плагіни

Плагіни

Розширюйте 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
    },
  };
}

Власні типи полів

Плагін поля постачає дві речі: підклас 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. Плагіни розширюють навколо них, а не замінюють їх.