Manifest referansı
module.json dosyasının taşıyabileceği her blok. Yalnızca ilk beşi zorunlu.
Kimlik
{
"id": "my-module",
"name": "My Module",
"description": "Tek satır; marketplace'te ve katalogda görünür.",
"version": "1.0.0",
"coreVersion": "^2.1.0",
"category": "content",
"icon": "Package",
"author": "Adın"
}
id, klasör adıyla aynı olmalı ve küçük harfli bir slug olmalı. coreVersion,
çekirdeğin modüle bakan API sürümü üzerinde bir semver aralığıdır; platform,
çalışan çekirdeğin karşılamadığı bir aralığa sahip modülü kurmayı ve
etkinleştirmeyi reddeder.
Sayfalar ve ekranlar
{
"routes": [
{ "path": "notices", "component": "pages/page.tsx", "titleKey": "notices.pageTitle" },
{ "path": "notices/[id]", "component": "pages/[id]/page.tsx", "resolver": "lib/notice-exists.ts" }
],
"adminRoutes": [
{ "path": "/notices", "component": "pages/admin/page.tsx" }
],
"menu": [
{ "label": "Notices", "path": "/notices", "icon": "Megaphone", "group": "content", "section": "publishing" }
]
}
Dinamik bir açık route'un resolver'a ihtiyacı vardır: o adresteki şeyin var
olup olmadığını söyleyen bir fonksiyon export eden dosya. Olmadığında sayfa 200
ile "burada bir şey yok" çizer, ve bu bir tarayıcıya sayfanın gerçek olduğunu
söyler.
menu.group, ekranın hangi yönetim grubuna ait olduğu; menu.section ise o
grubun içindeki hangi bölüme ait olduğudur. İkisini de bildirmeyen bir modül,
bir operatörün ya tesadüfen bulacağı ya da hiç bulamayacağı bir çekmeceye
düşer.
Endpoint'ler
{
"api": [
{ "path": "notices", "handler": "api/route.ts", "method": "ALL",
"rateLimit": { "maxRequests": 60, "windowMs": 60000 } },
{ "path": "webhook/acme", "handler": "api/webhook/route.ts", "providerCallback": true }
]
}
Veri değiştiren metodlar proxy tarafından CSRF kontrolünden geçer. Modülüne
geri çağrı yapan bir sağlayıcı CSRF token taşıyamaz; bu yüzden
providerCallback bildirir ve bunun yerine sabit bir ilk yol parçası alır.
İzinler ve veri
{
"permissions": ["notices.view", "notices.manage"],
"permissionResources": [{ "type": "notice", "label": "Notice" }],
"userDataExport": ["Notice"]
}
İzinler, modül kurulduğunda rol düzenleyicide belirir ve kaldırıldığında oradan
çıkar. userDataExport, bir kişiye ait tabloları adlandırır; böylece bir veri
dışa aktarma talebi, kimsenin bu modülün varlığını hatırlamasına gerek kalmadan
doğru cevaplanır.
Başka şeyleri genişletmek
{
"hooksEmitted": [{ "name": "notice.published", "description": "Bir duyuru yayına girdikten sonra" }],
"hookListeners": [{ "hook": "user.registered", "handler": "lib/on-register.ts" }],
"slots": [{ "name": "notices.aboveList" }],
"slotContents": [{ "slot": "layout.top", "component": "components/Banner.tsx" }],
"pageBlocks": [{ "id": "notice-list", "component": "components/blocks/List.tsx", "category": "content" }],
"cronJobs": [{ "name": "expire-notices", "schedule": "0 * * * *", "handler": "lib/expire.ts" }]
}
Bir modülün yaydığı hook tiplidir: çekirdek payload tiplerini bu bildirimlerden
üretir, yani hook'un göndermediği bir alanı okuyan bir dinleyici üretimde
undefined okumak yerine tip kontrolünde patlar.
Metinler
{
"translations": {
"en": { "notices": { "pageTitle": "Notices" } },
"tr": { "notices": { "pageTitle": "Duyurular" } }
}
}
Platformun getirdiği her dil, her seferinde. Kurulumda çeviri tablosuna senkronlanırlar, ve bir yöneticinin onlar üzerindeki düzenlemeleri hem güncellemeden hem kaldırmadan sağ çıkar.
Doğrulayıcı neyi reddeder
- Klasörle eşleşmeyen ya da ayrılmış bir kelime olan
id - İçinde
/admingeçen bir yönetim yolu (öneki çekirdek ekler) - Modül klasörünün dışını gösteren bir dosya referansı
- SDK yerine
@/core/lib/*import etmek - Resolver'ı olmayan dinamik bir açık route
- Kurulu hiçbir modülün karşılayamayacağı bir bağımlılık aralığı
- Payload'ı, hook'un bildirdiğiyle uyuşmayan bir hook dinleyicisi