@flowselections/core
v1.0.21
Published
Fundament-module: authenticatie, shell-layout (sidebar/header), instellingen en de gedeelde UI-componenten. Elke module heeft core als `peerDependency`; de shell bepaalt welke core-versie draait.
Readme
@flowselections/core
Fundament-module: authenticatie, shell-layout (sidebar/header), instellingen en de
gedeelde UI-componenten. Elke module heeft core als peerDependency; de shell
bepaalt welke core-versie draait.
Modulecontract
interface FlowModule {
id: string; // pakket-id (npm), NIET de externe sleutel
name: string;
version: string;
nav: NavItem;
settingsCards: SettingsCard[];
registryKey?: string; // optionele expliciete externe sleutel
}De modulesleutel
Core matcht extern (module_registry, rechten) altijd op moduleKey(mod) en
nooit op mod.id — id is een pakket-id, terwijl de registry route-slugs bevat.
moduleKey():
registryKeyals die is opgegeven;- anders het eerste padsegment van
nav.href; - afsluitende underscores worden gestript.
Regel 3 is een expliciete beslissing: een afsluitende underscore in een routepad
(/voorraad_/detail) is een TanStack un-nesting-conventie en geen betekenisvol
segment. Zonder strippen krijgt dezelfde module twee sleutels (voorraad en
voorraad_) en verdwijnt hij half uit de zijbalk. Er is gekozen voor strippen in
plaats van registryKey verplicht maken, omdat dat laatste bestaande modules
stil zou breken. Wil je afwijken, zet dan registryKey expliciet.
Core kent geen enkele module- of domeinnaam; de regel is volledig generiek.
Shell-extensiepunten
Alles optioneel. Zonder provider gedraagt een shell zich exact zoals voorheen: default-allow, code-volgorde.
// src/routes/_authenticated.tsx
import { ShellExtensionsProvider, ModuleGuardSlot, Sidebar, useModulePermissionTransform } from "@flowselections/core";
import { useModuleOrderTransform } from "@flowselections/sidebar-ordening-installatie-techniek";
<ShellExtensionsProvider
useModuleList={[useModulePermissionTransform, useModuleOrderTransform]}
>
<Sidebar auth={auth} modules={modules} />
<ModuleGuardSlot>
<Outlet />
</ModuleGuardSlot>
</ShellExtensionsProvider>Pipeline-volgorde is de array-volgorde: eerst rechten filteren, dan sorteren. Meegegeven hooks moeten stabiele referenties zijn (module-scope functies, geen inline lambdas), zodat de hook-volgorde tussen renders nooit verandert.
Bij laden, een ontbrekende tabel/RPC of een fout in een transform komt de lijst ongewijzigd terug. Een lege zijbalk is nooit een geldige uitkomst.
Parent/child-gedrag in de zijbalk
- Groep waarvan alle kinderen wegvallen → groep wordt niet gerenderd.
- Parent-sleutel geweigerd maar een kind toegestaan → groep blijft staan met
alleen de toegestane kinderen.
NavItem.hrefis op een groep een uitklapknop, geen echte bestemming, dus de parent-sleutel wordt voor groepen niet gebruikt om zichtbaarheid te bepalen. - Platte items worden op hun eigen sleutel beoordeeld.
Rechten (leeskant)
useMyModulePermissions()/useModulePermission(moduleId, featureId?)— React Query op RPCmy_module_permissions(),queryKey: ["my-module-permissions"].useModulePermissionTransform()— laat zijbalkitems metnoneweg.ModuleAccessGuard— pad-gebaseerde guard (module-id uit het eerste URL-segment).requireModulePermission()— server-only, alleen via@flowselections/core/server.
Core leest of schrijft geen enkele tabel uit het rechtenschema. Alleen de twee
RPC's my_module_permissions() en effective_module_permission(). Het schema
blijft eigendom van de admin-add-on.
ModuleAccessGuard is fail-open — met opzet
ModuleAccessGuard en useModulePermission laten door bij:
- laden (eerste render, eerste login);
- een ontbrekende RPC (add-on niet geïnstalleerd op die database — PostgREST 404 / Postgres 42883);
- elke andere fout in de query;
- een module zonder rij in
module_registry(default-allow).
Dit is een bewuste keuze: zichtbaarheid is geen beveiliging, en een dichte guard zou bij elke laadfout of ontbrekende add-on de hele app onbruikbaar maken.
De echte afscherming is requireModulePermission() server-side plus RLS/policies
op de database. Lees deze guard niet als beveiligingslaag.
De permissie-query heeft retry: false: een ontbrekende RPC mag nooit een
retry-storm veroorzaken.
Supabase
De browser-client is env-gedreven en lazy (VITE_SUPABASE_URL /
VITE_SUPABASE_PUBLISHABLE_KEY, met SUPABASE_* fallback voor SSR). Er staat
geen projectref of key in de code, ook niet als fallback.
