Plugin fejlesztés
Projekt létrehozása
Szekció neve “Projekt létrehozása”A leggyorsabb módja egy új plugin projekt indításának a @racona/cli CLI:
bunx @racona/cliA wizard végigvezet a beállításokon:
- App ID — kebab-case azonosító (pl.
my-app) - Display Name — megjelenítendő név a Racona-ben
- Description — rövid leírás
- Author — neved és email-ed
- Features — válaszd ki a szükséges funkciókat
- Install dependencies? — automatikusan futtatja a
bun install-t
Elérhető feature-ök
Szekció neve “Elérhető feature-ök”| Feature | Mit ad hozzá |
|---|---|
sidebar | Oldalsáv navigáció (menu.json, AppLayout mód, több oldal komponens) |
database | SQL migrációk, sdk.data.query() támogatás, lokális dev adatbázis Docker-rel |
remote_functions | server/functions.ts, sdk.remote.call(), lokális dev szerver |
notifications | sdk.notifications.send() támogatás |
i18n | locales/hu.json + locales/en.json, sdk.i18n.t() támogatás |
datatable | DataTable komponens insert formmal, sor akciókkal (duplikálás/törlés), teljes i18n |
Generált projekt struktúra
Szekció neve “Generált projekt struktúra”A struktúra a kiválasztott feature-öktől függ. Teljes példa (minden feature engedélyezve):
my-app/├── manifest.json # App metaadatok és jogosultságok├── package.json├── vite.config.ts├── tsconfig.json├── menu.json # (ha sidebar)├── build-all.js # (ha sidebar)├── dev-server.ts # (ha remote_functions)├── docker-compose.dev.yml # (ha database)├── .env.example # (ha database)├── src/│ ├── App.svelte│ ├── main.ts│ ├── plugin.ts│ └── components/ # (ha sidebar)│ ├── Overview.svelte│ ├── Settings.svelte│ ├── Datatable.svelte # (ha datatable)│ ├── Notifications.svelte # (ha notifications)│ └── Remote.svelte # (ha remote_functions)├── server/ # (ha remote_functions)│ └── functions.ts├── migrations/ # (ha database)│ ├── 001_init.sql│ └── dev/│ └── 000_auth_seed.sql├── locales/ # (ha i18n)│ ├── hu.json│ └── en.json└── assets/ └── icon.svgFejlesztői workflow
Szekció neve “Fejlesztői workflow”A generált projekt scriptkészlete a kiválasztott feature-öktől függ.
Alap scriptek (minden projektnél)
Szekció neve “Alap scriptek (minden projektnél)”bun dev # Vite dev szerver (standalone, Mock SDK) — http://localhost:5174bun run build # IIFE bundle elkészítése (dist/index.iife.js)bun run build:watch # Build figyelő módbanbun run package # .elyospkg csomag elkészítéseHa remote_functions engedélyezve van
Szekció neve “Ha remote_functions engedélyezve van”bun run dev:server # Dev szerver indítása — http://localhost:5175A dev:server egy Bun HTTP szervert indít, amely:
- Statikus fájlokat szolgál ki a
dist/mappából és a projekt gyökeréből (CORS fejlécekkel) POST /api/remote/:functionNameendpointot biztosít aserver/functions.tsfüggvényeinek hívásához
Ha database is engedélyezve van
Szekció neve “Ha database is engedélyezve van”bun db:up # Docker Postgres konténer indításabun db:down # Docker Postgres konténer leállításabun run dev:full # dev:server + dev párhuzamosan (egy terminálban)A dev:full egyszerre indítja a Vite dev szervert (5174) és a dev szervert (5175), így nem kell két terminál.
Első indítás adatbázissal
Szekció neve “Első indítás adatbázissal”cp .env.example .env # Környezeti változók beállításabun db:up # Postgres konténer indítása (Docker szükséges)bun run dev:full # Dev szerver + Vite egyszerreA .env.example tartalmazza az alapértelmezett kapcsolati URL-t a Docker Compose által indított adatbázishoz:
DATABASE_URL=postgresql://postgres:postgres@localhost:5433/{plugin_id}_devPORT=5175DEV_USER_ID=dev-userStandalone fejlesztés (Mock SDK)
Szekció neve “Standalone fejlesztés (Mock SDK)”Az alkalmazás fejleszthető futó Racona példány nélkül is. A @racona/sdk/dev csomag egy Mock SDK-t biztosít, amely szimulálja az összes SDK szolgáltatást:
| SDK szolgáltatás | Mock viselkedés |
|---|---|
ui.toast() | console.log-ba ír |
ui.dialog() | window.confirm / window.prompt |
data.set/get/delete() | localStorage-t használ (devapp:{appId}: kulcs prefix alatt) |
data.query() | Üres tömböt ad vissza |
remote.call() | Konfigurálható mock handler |
i18n.t() | A megadott fordítási mapből olvas |
notifications.send() | console.log-ba ír |
Dev szerver indítása
Szekció neve “Dev szerver indítása”bun devA Vite dev szerver elindul a http://localhost:5174 címen. A hot reload automatikusan frissíti a böngészőt minden mentéskor.
Ha remote_functions is engedélyezve van, a bun dev mellé párhuzamosan szükséges a bun run dev:server is (vagy használd a bun run dev:full parancsot, ha database is engedélyezve van).
Mock SDK inicializálás
Szekció neve “Mock SDK inicializálás”A src/main.ts fájlban a Mock SDK inicializálása automatikusan megtörténik:
import { MockWebOSSDK } from '@racona/sdk/dev';import App from './App.svelte';import { mount } from 'svelte';
// Csak akkor fut le, ha NEM Racona-ben vagyunkif (typeof window !== 'undefined' && !window.webOS) { MockWebOSSDK.initialize({ i18n: { locale: 'en', translations: { en: { title: 'My App', welcome: 'Welcome!' }, hu: { title: 'Alkalmazás', welcome: 'Üdvözöljük!' } } }, context: { pluginId: 'my-app', user: { id: 'dev-user', name: 'Developer', email: 'dev@localhost', roles: ['admin'], groups: [] }, permissions: ['database', 'notifications', 'remote_functions'] } });}
const target = document.getElementById('app');if (target) mount(App, { target });Az initialize() összes konfigurációs lehetősége:
| Opció | Típus | Leírás |
|---|---|---|
i18n.locale | string | Alapértelmezett nyelv (pl. 'hu') |
i18n.translations | Record<string, Record<string, string>> | Fordítási kulcsok nyelvenkénti mapje |
context.pluginId | string | Szimulált alkalmazás ID |
context.user | UserInfo | Szimulált bejelentkezett felhasználó |
context.permissions | string[] | Szimulált jogosultságok |
data.initialData | Record<string, unknown> | Előre feltöltött localStorage adatok |
remote.handlers | Record<string, Function> | Mock szerver függvény handlerek |
assets.baseUrl | string | Asset URL prefix |
Amikor a Racona betölti az alkalmazást élesben, a window.webOS már létezik, ezért az if (!window.webOS) feltétel miatt a Mock SDK nem fut le.
Remote call mock-olása
Szekció neve “Remote call mock-olása”Ha szerver függvényeket is tesztelsz standalone módban:
MockWebOSSDK.initialize({ remote: { handlers: { getServerTime: async () => ({ iso: new Date().toISOString(), locale: new Date().toLocaleString('hu-HU') }), calculate: async ({ a, b, operation }) => { if (operation === 'add') return { result: a + b }; throw new Error('Unsupported operation'); } } }});Dev szerver port konfigurálhatóság
Szekció neve “Dev szerver port konfigurálhatóság”A dev:server alapértelmezetten az 5175-ös portot használja (a Vite dev szerver a 5174-est). Ha egyszerre több alkalmazást fejlesztesz, a port a PORT környezeti változóval felülírható a .env fájlban vagy közvetlenül:
PORT=5176 bun run dev:serverA Racona Dev Alkalmazások betöltőjében az URL-t ennek megfelelően add meg: http://localhost:5176.
Tesztelés futó Racona-ben
Szekció neve “Tesztelés futó Racona-ben”A standalone dev mód (Mock SDK) csak a UI-t teszteli. Ha valódi SDK hívásokat, adatbázist vagy szerver függvényeket is tesztelni szeretnél, az alkalmazást be kell tölteni egy futó Racona példányba.
A folyamat lényege: buildeld le az alkalmazást, indíts egy statikus HTTP szervert (dev:server), majd töltsd be a Racona-be URL alapján. Nincs automatikus hot reload — ha változtattál a kódon, újra kell buildelni és újra megnyitni az alkalmazás ablakát.
1. lépés — Racona core indítása
Szekció neve “1. lépés — Racona core indítása”Az elyos-core monorepo gyökerében:
# .env.local fájlban engedélyezd a dev alkalmazás betöltést:# DEV_MODE=true
bun app:devA Racona alapértelmezetten a http://localhost:5173 címen érhető el. Jelentkezz be admin fiókkal.
2. lépés — Alkalmazás buildelése
Szekció neve “2. lépés — Alkalmazás buildelése”Az alkalmazás projekt mappájában:
bun run buildEz létrehozza a dist/index.iife.js fájlt — ezt tölti be a Racona.
3. lépés — Plugin dev szerver indítása
Szekció neve “3. lépés — Plugin dev szerver indítása”bun run dev:serverEz elindítja a dev-server.ts Bun HTTP szervert a http://localhost:5175 címen. A szerver a dist/ mappából és a projekt gyökeréből szolgálja ki a fájlokat CORS fejlécekkel.
Ha database is engedélyezve van, a szerver induláskor automatikusan futtatja a migrációkat, és a POST /api/remote/:functionName endpointon keresztül elérhetők a server/functions.ts függvényei.
4. lépés — Alkalmazás betöltése a Racona-be
Szekció neve “4. lépés — Alkalmazás betöltése a Racona-be”- Nyisd meg a Racona-t a böngészőben
- Start menü → Alkalmazás Manager
- A bal oldalsávban kattints a “Dev Alkalmazások” menüpontra
- Megjelenik egy URL beviteli mező
http://localhost:5175alapértelmezett értékkel - Kattints a “Load” gombra
A Racona lekéri a manifest.json-t a dev szerverről, majd betölti az IIFE bundle-t és Web Component-ként regisztrálja az alkalmazást.
Módosítás utáni újratöltés
Szekció neve “Módosítás utáni újratöltés”# 1. Újrabuildelésbun run build
# 2. A Racona-ben: zárd be az alkalmazás ablakát, majd nyisd meg újra# (a "Load" gombot nem kell újra megnyomni — az alkalmazás már a listában van)Teljes dev workflow összefoglalva
Szekció neve “Teljes dev workflow összefoglalva”Alap (remote_functions nélkül):
# Terminál 1 — Racona corecd elyos-core && bun app:dev
# Terminál 2 — Alkalmazás build + szervercd my-appbun run build # IIFE bundle elkészítésebun run dev:server # statikus szerver indítása (http://localhost:5175)
# Racona-ben: Alkalmazás Manager → Dev Alkalmazások → Load → http://localhost:5175Adatbázissal (database + remote_functions):
# Terminál 1 — Racona corecd elyos-core && bun app:dev
# Terminál 2 — Alkalmazás (első alkalommal)cd my-appcp .env.example .env # DATABASE_URL és PORT beállításabun db:up # Postgres konténer indítása
# Terminál 2 — Alkalmazás (minden alkalommal)bun run build # IIFE bundle elkészítésebun run dev:server # dev szerver + migrációk + remote endpoint (http://localhost:5175)
# Racona-ben: Alkalmazás Manager → Dev Alkalmazások → Load → http://localhost:5175Plugin telepítése (.elyospkg)
Szekció neve “Plugin telepítése (.elyospkg)”Ha az alkalmazás fejlesztése kész, csomagold be és telepítsd a Racona-be.
Csomag elkészítése
Szekció neve “Csomag elkészítése”bun run build # IIFE bundle elkészítésebun run package # .elyospkg fájl létrehozásaEz létrehozza a {id}-{version}.elyospkg fájlt (pl. my-app-1.0.0.elyospkg). A csomag egy ZIP archívum, amely tartalmazza:
manifest.jsondist/— build output (IIFE bundle)locales/— fordítások (ha van)assets/— statikus fájlok (ha van)menu.json— oldalsáv konfiguráció (ha van)server/— szerver oldali függvények (ha van)migrations/— adatbázis migrációk (ha van, dev seed fájlok nélkül)
Feltöltés a Racona-be
Szekció neve “Feltöltés a Racona-be”- Start menü → Alkalmazás Manager → Plugin Feltöltés
- Húzd rá a
.elyospkgfájlt, vagy kattints a böngészés gombra - A Racona validálja a csomagot, majd megmutatja az előnézetet
- Kattints a Telepítés gombra
A telepítés során a Racona:
- Kicsomagolja a fájlokat a plugin tárolóba
- Regisztrálja az alkalmazást az app registry-ben
- Importálja a fordításokat (ha van
locales/) - Létrehozza a plugin adatbázis sémát (ha
databasejogosultság van) - Regisztrálja az email template-eket (ha
notificationsjogosultság van)
Manifest fájl
Szekció neve “Manifest fájl”A manifest.json az alkalmazás metaadatait tartalmazza. Kötelező és opcionális mezők:
{ "id": "my-app", "name": { "hu": "Alkalmazásom", "en": "My App" }, "version": "1.0.0", "description": { "hu": "Rövid leírás", "en": "Short description" }, "author": "Szerző Neve <email@example.com>", "entry": "dist/index.iife.js", "icon": "assets/icon.svg", "iconStyle": "cover", "category": "utilities", "permissions": ["database", "notifications", "remote_functions"], "multiInstance": false, "defaultSize": { "width": 800, "height": 600 }, "minSize": { "width": 400, "height": 300 }, "maxSize": { "width": 1920, "height": 1080 }, "keywords": ["example", "demo"], "isPublic": false, "sortOrder": 100, "dependencies": { "svelte": "^5.0.0", "@lucide/svelte": "^1.0.0" }, "minWebOSVersion": "2.0.0", "locales": ["hu", "en"]}Jogosultságok
Szekció neve “Jogosultságok”| Jogosultság | Leírás | SDK funkciók |
|---|---|---|
database | Adatbázis hozzáférés | data.set(), data.get(), data.query() |
notifications | Értesítések küldése | notifications.send() |
remote_functions | Szerver oldali függvények | remote.call() |
file_access | Fájl hozzáférés | (tervezett) |
user_data | Felhasználói adatok | (tervezett) |
ID formátum szabályok
Szekció neve “ID formátum szabályok”- Csak kisbetűk, számok és kötőjel (
kebab-case) - Minimum 3, maximum 50 karakter
- Regex:
^[a-z0-9-]+$
"id": "my-app" // ✅ Helyes"id": "MyApp" // ❌ Hibás"id": "my_app" // ❌ HibásWebOS SDK API
Szekció neve “WebOS SDK API”Az SDK a window.webOS globális objektumon keresztül érhető el:
const sdk = window.webOS!;UI Service
Szekció neve “UI Service”// Toast értesítéssdk.ui.toast('Üzenet', 'success');// type: 'info' | 'success' | 'warning' | 'error'
// Dialógusconst result = await sdk.ui.dialog({ title: 'Cím', message: 'Üzenet', type: 'confirm' // 'info' | 'confirm' | 'prompt'});Remote Service
Szekció neve “Remote Service”// Szerver függvény hívásaconst result = await sdk.remote.call('functionName', { param: 'value' });
// Generikus visszatérési típussalconst result = await sdk.remote.call<MyResult>('functionName', params);Data Service
Szekció neve “Data Service”// Kulcs-érték tárolásawait sdk.data.set('key', { value: 123 });const value = await sdk.data.get('key');await sdk.data.delete('key');
// SQL lekérdezés (csak a plugin saját sémájában!)const rows = await sdk.data.query('SELECT * FROM my_table WHERE id = $1', [123]);
// Tranzakcióawait sdk.data.transaction(async (tx) => { await tx.query('INSERT INTO ...'); await tx.query('UPDATE ...'); await tx.commit();});I18n Service
Szekció neve “I18n Service”// Fordításconst text = sdk.i18n.t('key');
// Paraméterekkelconst text = sdk.i18n.t('welcome', { name: 'John' });
// Aktuális nyelvconst locale = sdk.i18n.locale; // 'hu' | 'en'
// Nyelv váltásawait sdk.i18n.setLocale('en');Notification Service
Szekció neve “Notification Service”await sdk.notifications.send({ userId: 'user-123', title: 'Cím', message: 'Üzenet', type: 'info' // 'info' | 'success' | 'warning' | 'error'});Context Service
Szekció neve “Context Service”const pluginId = sdk.context.pluginId;const user = sdk.context.user;const permissions = sdk.context.permissions;
// Ablak vezérlőksdk.context.window.close();sdk.context.window.setTitle('Új cím');Asset Service
Szekció neve “Asset Service”const iconUrl = sdk.assets.getUrl('icon.svg');const imageUrl = sdk.assets.getUrl('images/logo.png');TypeScript és autocomplete
Szekció neve “TypeScript és autocomplete”Az @racona/sdk teljes TypeScript típusdefiníciókat tartalmaz. A window.webOS típusa automatikusan elérhető:
// Automatikus típus — nincs szükség importraconst sdk = window.webOS!;
sdk.ui.toast('Hello!', 'success'); // ✅ autocompletesdk.data.set('key', { value: 123 }); // ✅ típusellenőrzéssdk.remote.call<MyResult>('fn', params); // ✅ generikus visszatérési típusExplicit típusimport szükség esetén:
import type { WebOSSDKInterface, UserInfo } from '@racona/sdk/types';
const user: UserInfo = sdk.context.user;Svelte 5 runes a pluginban
Szekció neve “Svelte 5 runes a pluginban”A plugin Svelte 5 runes-alapú reaktivitást használ. A vite.config.ts-ben a runes: true compiler opció be van kapcsolva:
<script lang="ts"> const sdk = window.webOS!;
let count = $state(0); let doubled = $derived(count * 2);
$effect(() => { sdk.ui.toast(`Count: ${count}`, 'info'); });</script>
<button onclick={() => count++}> {count} (doubled: {doubled})</button>Szerver oldali függvények
Szekció neve “Szerver oldali függvények”Ha a remote_functions feature engedélyezve van, a server/functions.ts fájlban definiálhatók szerver oldali függvények:
import type { PluginFunctionContext } from '@racona/sdk/types';
export async function getItems( params: { page: number; pageSize: number }, context: PluginFunctionContext) { const { db, pluginId } = context; const schema = `app__${pluginId.replace(/-/g, '_')}`;
const rows = await db.query( `SELECT * FROM ${schema}.items LIMIT $1 OFFSET $2`, [params.pageSize, (params.page - 1) * params.pageSize] );
return { success: true, data: rows };}Hívás a kliensről:
const result = await sdk.remote.call('getItems', { page: 1, pageSize: 20 });Adatbázis migrációk
Szekció neve “Adatbázis migrációk”Ha a database feature engedélyezve van, a migrations/ mappában SQL fájlok definiálják a plugin saját adatbázis sémáját. A fájlok névsorrendben futnak le (pl. 001_init.sql, 002_add_column.sql).
-- migrations/001_init.sqlCREATE TABLE items ( id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, value JSONB, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW());A migrations/dev/ mappában lévő fájlok csak fejlesztési célra szolgálnak (pl. seed adatok) — a .elyospkg csomagba nem kerülnek bele.
Stílus kezelés
Szekció neve “Stílus kezelés”CSS injektálás
Szekció neve “CSS injektálás”A plugin CSS-e az IIFE build során a vite-plugin-css-injected-by-js plugin segítségével automatikusan a JS bundle-be kerül. Ez a plugin a @racona/cli által generált vite.config.ts-ben már benne van — nem kell kézzel hozzáadni.
Specificitási ütközések
Szekció neve “Specificitási ütközések”A core app Tailwind stílusai (base layer resetok) felülírhatják a plugin stílusait. A Svelte scoped CSS button.svelte-xxxx selectorokat generál, de a Tailwind button { ... } resetje magasabb specificitással töltődik be.
A megoldás: mindig egy saját konténer osztályon belül definiáld a stílusokat:
<!-- ❌ Rossz — a core stílusai felülírják --><style> button { border: 1px solid #ccc; }</style>
<!-- ✅ Helyes — konténer osztályon belül scopelve --><style> .my-plugin button { border: 1px solid #ccc; }</style>Ha a core stílusai felülírnak egy elemet, az all: revert visszaállítja a böngésző natív stílusát:
<style> .my-plugin button { all: revert; cursor: pointer; border: 1px solid #ccc; border-radius: 0.25rem; padding: 0.5rem 1rem; }</style>Összefoglalás
Szekció neve “Összefoglalás”| Szabály | Miért |
|---|---|
Konténer osztályon belül scopelj (.my-plugin button) | A core Tailwind stílusai felülírják a nyers tag selectorokat |
Szükség esetén használj all: revert-et | Visszaállítja a böngésző natív stílusát |
| Adj egyedi osztálynevet a gyökér konténernek | Elkerüli az ütközést más pluginok stílusaival |
Biztonsági szabályok
Szekció neve “Biztonsági szabályok”Tiltott
Szekció neve “Tiltott”eval()ésFunction()konstruktorinnerHTMLésdocument.write()- Külső domain-ekre fetch/XHR
- Dinamikus import külső URL-ről
- Más plugin sémák elérése
- System sémák elérése (
platform,auth,public)
Engedélyezett függőségek
Szekció neve “Engedélyezett függőségek”A manifest dependencies mezőjében csak fehérlistán lévő package-ek szerepelhetnek:
svelte(^5.x.x)@lucide/svelte/lucide-sveltephosphor-svelte@elyos/*és@elyos-dev/*(minden verzió) — deprecated, használd helyette@racona/*@racona/*(minden verzió)
Gyakori hibák
Szekció neve “Gyakori hibák”| Hiba | Megoldás |
|---|---|
"Invalid plugin ID format" | Használj kebab-case-t: my-plugin |
"Permission denied" | Add hozzá a jogosultságot a manifest.json-ban |
"Module not found" | Futtasd le: bun run build |
"Plugin already exists" | Az adott ID-vel már telepítve van egy plugin — távolítsd el előbb |
"Plugin is inactive" | A plugin inaktív állapotban van — aktiváld az Alkalmazás Managerben |
| Dev alkalmazás nem jelenik meg | Ellenőrizd, hogy DEV_MODE=true van-e a Racona .env.local-ban |