Het manifest
Alles wat Tabbr over je module moet weten, staat in één bestand: tabbr.config.json in de root van je
workspace. De CLI leest het bij het bouwen en publiceren, en de club ziet een deel ervan bij het installeren
(de rechten die je module vraagt).
Een volledig voorbeeld
Section titled “Een volledig voorbeeld”Dit is — licht ingekort — het manifest van de module Bevoegdheden & competenties:
{ "club": "", "apiHost": "https://api.tabbr.be", "moduleId": "qualifications", "package": "tabbr/qualifications", "version": "1.0.0", "title": "Bevoegdheden & competenties", "summary": "Bevoegdheden (wie mag wat, van wanneer tot wanneer) en competenties (wat kan iemand al).", "icon": "workspace_premium", "exposedModule": "./Module", "exportName": "Module", "engines": { "tabbr": "^1.3" }, "uses": { "sys.access": "^1" }, "optionalUses": { "tabbr.consent": "^1" }, "optionalDependencies": { "sys.messaging": "*" }, "permissions": { "entities": { "read": ["person"], "write": [] }, "personalData": ["identity"] }, "entities": [ { "name": "programme", "label": "Opleiding", "display": ["name"], "fields": [ { "key": "name", "label": "Naam", "type": "tabbr.text", "includeInSearch": true }, { "key": "archived", "label": "Gearchiveerd", "type": "tabbr.bool" } ] } ], "backend": { "entry": "backend/src/index.ts", "allowedHosts": [], "secretFieldNames": [], "triggers": [ { "type": "CRON", "name": "refreshAll", "cronExpression": "10 2 * * *", "handler": "refreshAll" }, { "type": "ENTITY_HOOK", "name": "programmeUpdated", "entity": "programme", "event": "onUpdated", "handler": "onProgrammeSaved" } ] }}Identiteit
Section titled “Identiteit”| Veld | Betekenis |
|---|---|
moduleId |
Unieke id in de hele store. Kan nooit veranderen — de server weigert een versie met een ander id. |
package |
<uitgever>/<naam>, bv. tabbr/groups. De uitgever bepaalt ook de naam van contracten die je aanbiedt. |
version |
Semver. Elke nieuwe versie moet hoger zijn dan alle vorige. |
title |
Reservetitel. De getoonde titel komt uit je vertalingen (module.title). |
summary |
De korte beschrijving in de Module Store. |
icon |
Een Material-icoon, bv. groups of verified_user. |
exposedModule, exportName |
Waar Tabbr je moduleklasse vindt: normaal ./Module en Module. |
visibility |
PUBLIC of UNLISTED in de store. |
club, apiHost, localApiHost |
Alleen voor de ontwikkelaar (dev preview, publiceren). Commit club niet. |
Compatibiliteit: engines
Section titled “Compatibiliteit: engines”engines.tabbr is het bereik van de module-API waartegen je bouwt, bv. ^1.3. De huidige module-API is
1.3.0. Een club op een Tabbr die buiten dat bereik valt, krijgt je module niet; de app laadt dan gewoon
zonder.
| API | Wat er bijkwam |
|---|---|
| 1.0 | De eerste gepubliceerde modules |
| 1.1 | Contracten: uses, MODULE_CONTRACTS, tabbr.uses |
| 1.2 | Toegangsregels op modulevelden, {entity.key}, gerelateerde filters één stap ver en via meerwaardige verwijzingen |
| 1.3 | userPrompts, provides, optionalUses |
Afhankelijkheden en contracten
Section titled “Afhankelijkheden en contracten”| Veld | Betekenis |
|---|---|
uses |
Contracten die je module nodig heeft, met een semver-bereik: { "sys.access": "^1" }. Je module kan dan alleen geïnstalleerd worden in een club die het contract heeft. |
optionalUses |
Contracten die je gebruikt als ze er zijn (API 1.3). |
provides |
Contracten die je module zelf aanbiedt — zie Contracten. |
dependencies / optionalDependencies |
Andere pakketten, of ingebouwde modules zoals sys.messaging. |
Rechten: permissions
Section titled “Rechten: permissions”Wat je hier vraagt, toont de Module Store in gewone taal aan de beheerder die je module installeert (“leest personen”, “verwerkt persoonsgegevens: contactgegevens”, “verstuurt mails”…). Vraag niet meer dan je nodig hebt.
"permissions": { "entities": { "read": ["person", "membership"], "write": ["flight"] }, "personalData": ["contact"], "messaging": true}Eigen entiteiten: entities
Section titled “Eigen entiteiten: entities”Een module brengt haar eigen gegevens mee. Per entiteit:
| Veld | Betekenis |
|---|---|
name |
[a-z][a-zA-Z0-9]{1,39}, uniek in de club, bv. groupMember. |
label |
De naam die de club ziet, bv. Groepslid. |
display |
De velden die een record voorstellen (in een keuzelijst, een verwijzing). |
sort |
De standaardsortering. |
fields[] |
De velden: key, label, type en opties. |
access |
De toegangsregels bij de eerste installatie (viewAccess, createAccess, updateAccess, deleteAccess). Daarna beslist de club. |
Veldtypes: tabbr.text, tabbr.paragraph, tabbr.int, tabbr.decimal, tabbr.bool, tabbr.date,
tabbr.email, tabbr.phone, tabbr.national-id, tabbr.entity (met target, multi, freeTextAllowed),
tabbr.single-select (met choices, multi), sys.file en tabbr.person.address. Per veld kan je ook
description, category, includeInSearch, personalDataCategory en een eigen access meegeven.
{ "name": "groupMember", "label": "Groepslid", "display": ["person"], "fields": [ { "key": "group", "label": "Groep", "type": "tabbr.entity", "target": "group" }, { "key": "person", "label": "Persoon", "type": "tabbr.entity", "target": "person" }, { "key": "role", "label": "Functie", "type": "tabbr.single-select", "choices": [ { "value": "member", "name": "Lid" }, { "value": "trainer", "name": "Trainer" } ] }, { "key": "from", "label": "Vanaf", "type": "tabbr.date" }, { "key": "until", "label": "Tot en met", "type": "tabbr.date" } ], "access": { "viewAccess": [ { "andFilterValues": [ { "filterId": "currentUser:roles", "operator": "HAS_ANY", "value": ["ADMIN"] } ] }, { "andFilterValues": [ { "filterId": "root.{person}:currentUser" } ] }, { "andFilterValues": [ { "filterId": "root.{group}:reverse.groupMember.{group}", "value": [ { "andFilterValues": [ { "filterId": "root.{person}:currentUser" }, { "filterId": "root:{role}", "operator": "EQ", "value": "trainer" }, { "filterId": "root:{until}", "operator": "IS_EMPTY" } ] } ] } ] } ] }}De drie regels (één ervan volstaat) lezen als: een ADMIN ziet alles, je ziet je eigen lidmaatschappen, en je ziet de leden van een groep waarvan jij een lopend lidmaatschap als trainer hebt.
In filter-id’s verwijs je naar je eigen velden met {fieldKey}, bv. root.{person}:currentUser (“de persoon
van dit record is de ingelogde gebruiker”). De volledige filtertaal staat in
Filters en toegangsregels.
Wat de club ermee krijgt
Section titled “Wat de club ermee krijgt”Zonder dat jij er iets voor bouwt, krijgt de club voor elke entiteit van je module:
- een plek in Instellingen › jouw module, met de gewone schermen voor velden, lijsten, toegang, hooks, acties en layout (zie Clubbeheer);
- eigen velden bovenop de jouwe, en lijsten in het menu;
- de omgekeerde filter “heeft een gerelateerde …” op elke entiteit waarnaar je entiteit verwijst;
- hooks en audit.
De velden die jij declareert, zijn beheerd: de club kan ze hernoemen, keuzes aanpassen en bepalen wie ze ziet, maar niet verwijderen of van type veranderen.
Backend: backend
Section titled “Backend: backend”| Veld | Betekenis |
|---|---|
entry |
Het TypeScript-bestand met je functies, bv. backend/src/index.ts. |
triggers[] |
Wanneer Tabbr je functies oproept: CRON of ENTITY_HOOK. |
allowedHosts |
De enige externe hosts die je backend mag aanspreken, bv. ["admin.gliding.app"]. |
localAllowedHosts |
Idem, voor een lokale Tabbr. |
secretFieldNames |
Instellingen die geheim zijn (een API-key): ze komen nooit terug naar de browser. |
manageRoles |
true als je via sys.access rollen toekent (zoals de groepenmodule). |
Meer daarover in De backend.