Files
2026-09-18 15:52:01 +02:00

49 KiB
Raw Permalink Blame History

JSON-export- en importformaat van Racket Wiki

Deze specificatie beschrijft racket-wiki-cmap-bundle, formaatversie 1, op basis van het JSON Schema, de implementatie in deze repository en de drie aangeleverde exports van Racket Wiki 0.2.122: modelgids-lts-opzet-cmap.json, rws-dingen-cmap.json en rws-lbs-architectuur-cmap.json. Een bundel bevat één of meer concept maps (CMaps), gedeelde concepten, gekoppelde wikipagina's en eventueel hun bijlagen. Het is geen volledige databaseback-up: gebruikers, instellingen, versiegeschiedenis en aliassen maken geen deel uit van dit formaat.

1. Status en conventies

Het machineleesbare contract staat in racket-wiki-cmap-bundle-v1.schema.json en gebruikt JSON Schema Draft 2020-12. De browser voert daarnaast eigen controles uit in interchange.js.

MOET, MAG en BEHOORT betekenen respectievelijk een eis, een toegestane mogelijkheid en een aanbeveling. De veldtabellen beschrijven het schema; aanvullende voorwaarden staan bij de betreffende velden en in § 9. Verschillen met de huidige uitvoering staan expliciet in § 12. Succesvolle browservalidatie is dus niet hetzelfde als volledige schemavalidatie.

Het bestand bevat één JSON-object. Gebruik UTF-8, echte JSON-getallen en booleans; gebruik geen commentaar of afsluitende komma's. Een optioneel veld MAG ontbreken. null is uitsluitend toegestaan waar dat is vermeld. De volgorde van objectvelden heeft geen betekenis.

1.1 Nadere typering

Naast het JSON-basistype gebruikt dit document benoemde typen voor betekenis en interne structuur. T[] betekent een array van elementen van type T; T of null staat expliciet null toe. Record<K, V> is een JSON-object waarvan de veldnamen sleutels van type K zijn en de waarden type V hebben. Een lege array in een voorbeeld bewijst niet welk elementtype is toegestaan.

De tabellen met schema als aanduiding beschrijven het bestaande contract. Tabellen met model/export beschrijven de huidige modelrepresentatie, waar nodig aangevuld met waarnemingen uit de drie aangeleverde exports. Een waargenomen waarde is geen uitputtende opsomming van toegestane waarden. Een gewenste semantische wijziging wordt afzonderlijk aangeduid en is geen bewering dat de huidige software die wijziging al uitvoert.

1.2 Verbindingszin: betekenis en huidige representatie

Het beoogde domeinuitgangspunt is: een verbindingszin drukt een relatie tussen concepten uit en is zelf een concept. Die betekenis is onafhankelijk van de kaart waarop zij wordt getoond. De plaatsing, opmaak en getekende lijnstukken zijn wel kaartgebonden.

De huidige export vertegenwoordigt dit uitgangspunt nog niet volledig. Daarin is omvat een item met kind: "phrase", id: 2 en conceptId: null. De tekst staat op het item, niet in de gedeelde concepts-tabel. Het model ConceptMapPhrase bewaart dit eveneens zo. De eerdere formulering “een verbindingszin is kaartlokaal” was daarom te algemeen: de huidige opslag van de verbindingszin is kaartlokaal; haar bedoelde betekenis is dat niet.

De code kent daarnaast een ConceptRelation met bronconcept, doelconcept en een tekstueel label (§ 5.3). Dat is een semantische relatie, maar maakt het label nog niet tot een zelfstandig gedeeld concept. Geen van de drie exports bevat conceptRelations, conceptOwnerships of een relationId op de connectoren. Alle phrase-items hebben conceptId: null. De editorfunctie connectWithPhrase maakt een phrase en connectoren aan, zonder daarbij een ConceptRelation te registreren.

Om het domeinuitgangspunt volledig te ondersteunen, zouden verbindingszinnen een gedeelde conceptidentiteit moeten hebben en zouden semantische relaties die identiteit moeten gebruiken. Het huidige formaat bevat daarvoor geen expliciete rolverwijzing naar het verbindingsconcept. Alleen een conceptId invullen op een phrase volstaat niet: het huidige phrase-model bewaart die koppeling niet. Dit is een verschil tussen het bedoelde domeinmodel en de implementatie, geen wijziging die deze documentatie in het formaat aanbrengt.

1.3 Verbindingszinnen in model en database

Verbindingszinnen zijn wel onderdeel van de persistent opgeslagen wikidata. De opslagcode behandelt ze als volgt:

Laag Huidige representatie
JavaScript-model ConceptMapPhrase, met eigen lokale id, label en synopsis.
CMap-document Een object in document.items met kind: "phrase"; connectoren verwijzen naar dit item via sourceId/targetId.
Database Het phrase-item, inclusief tekst, blijft staan in de JSONB-kolom concept_maps.document.
Gedeeld conceptregister concept_definitions bevat de gedeelde conceptinhoud. Phrases worden door concept-placement? expliciet uitgesloten van conceptplaatsingen.

concept-map-storage-document verwijdert gedeelde inhoud uit gewone conceptplaatsingen, maar laat phrase-items ongewijzigd. create-concept-map! en update-concept-map! schrijven dit document naar PostgreSQL. Ook de migraties die gedeelde conceptinhoud uit items verwijderen, behouden expliciet de tekst van phrases.

De conclusie is daarom: persistent in het kaartmodel, maar niet als zelfstandig gedeeld concept gemodelleerd. De tekst staat niet op de connector zelf en wordt niet uitsluitend door de presentatielaag bijgehouden. Een apart ConceptRelation-object in de JavaScript-code verandert dit niet automatisch: het aanmaken van een phrase vult die semantische relatietabel momenteel niet.

Deze vaststelling is gebaseerd op de opslagcode en migraties in deze repository en op de drie exports; er is geen rechtstreekse inspectie van de actieve database uitgevoerd.

2. Structuur van de bundel

Veld Type Verplicht Betekenis
$schema string Nee Verwijzing naar het schema; de exporter schrijft /schemas/racket-wiki-cmap-bundle-v1.schema.json.
format string Ja Exact racket-wiki-cmap-bundle.
formatVersion integer Ja Exact 1. Andere versies worden afgewezen.
exportedAt string Nee Exporttijdstip in date-time-notatie, bijvoorbeeld 2026-09-18T12:00:00.000Z.
generator string Nee Naam van de producerende toepassing; standaard Racket Wiki.
rootCmapSlug slug Ja Slug van de CMap die na import wordt geopend; MOET voorkomen in cmaps.
cmaps array van CMap-objecten Ja Ten minste één CMap; zie § 5.
concepts array van conceptobjecten Ja Gedeelde conceptinhoud; zie § 4. MAG leeg zijn.
pages array van paginaobjecten Ja Meegeleverde wikipagina's; zie § 7. MAG leeg zijn.
missing object Nee Niet geladen afhankelijkheden; zie § 8.

De exporter schrijft al deze velden. Op het hoofdniveau zijn andere velden volgens het schema niet toegestaan.

3. Identificatie en verwijzingen

3.1 Slug

Een slug begint met een Unicode-letter of -cijfer. Daarna zijn ook ., _ en - toegestaan. De totale lengte is 1 tot en met 120 tekens, volgens deze JavaScript-reguliere expressie met Unicode-modus:

^[\p{L}\p{N}][\p{L}\p{N}._-]{0,119}$

Dit geldt voor CMap-slugs en voor het sluggedeelte van paginaverwijzingen. Een CMap-slug bevat geen dubbele punt. Identiteiten en verwijzingen moeten exact overeenkomen; de bundelvalidator vergelijkt ze hoofdlettergevoelig.

3.2 Paginaverwijzing

Een paginaverwijzing heeft de vorm slug of namespace:slug. Beide delen voldoen aan de slugregels, met maximaal 80 tekens voor de namespace. Voorbeelden: inleiding, proces:ontwerp en cmap:architectuur. Het is een wiki-adres, geen URL of bestandsnaam.

3.3 Concept-id

Een concept-id is een string met één van de volgende vormen:

Vorm Regel Voorbeeld
UUID Hexadecimale groepen van 8-4-4-4-12 tekens; hoofdletters toegestaan. 24d27086-9f8b-4b57-a7fb-47b513277555
Tijdelijke id new: gevolgd door 1–120 ASCII-tekens; eerst een letter of cijfer, daarna ook ., _ en -. new:proces

De UUID-controle toetst de tekstvorm, niet de UUID-versie. Gebruik bij voorkeur kleine letters. Externe generators MOGEN tijdelijke id's gebruiken. De server zet nieuwe identiteiten bij opslag om naar UUID's en kan concepten op basis van een reeds bestaande naam aan een bestaande identiteit koppelen (§ 11).

3.4 Lokale item-id

Een document.items[].id is een integer die uniek is binnen zijn CMap. Een andere CMap MAG dezelfde item-id gebruiken. Een item-id identificeert een plaatsing of verbindingszin; een concept-id identificeert gedeelde inhoud. Connectoren verwijzen naar lokale item-id's, nooit rechtstreeks naar concept-id's.

3.5 Benoemde waardetypen

Type JSON-basistype Nadere typering
Slug string Adres volgens § 3.1.
PageReference string slug of namespace:slug, volgens § 3.2.
ConceptId string UUID of tijdelijke id volgens § 3.3.
ItemId integer Verwijzing binnen één diagram; § 3.4.
ConnectorId integer Identiteit binnen de connectoren van één diagram.
RelationId string Niet-lege identiteit van een ConceptRelation; het model legt geen UUID-patroon op.
ContextKey string root of de id van een interne kaartreferentie, bijvoorbeeld cmap-7.
CssColor string CSS-kleur; waargenomen zijn hexkleuren zoals #edf7e8, de korte vorm #333 en transparent. Geen kleurenenum in het bundelschema.
FontSize number of string Huidige model/exportwaarde: getal of CSS-lettergrootte, bijvoorbeeld "11pt", "9pt", "0.84em". Het schema staat op items nu alleen number toe.
FontFamily string CSS-lettertypefamilie of lijst daarvan, bijvoorbeeld "Arial, Helvetica, sans-serif".
FontWeight string CSS-gewicht als tekst, in de export "400" of "700"; dit zijn geen JSON-getallen.
FontStyle string CSS-letterstijl; de voorbeelden bevatten "normal" en "italic". Het schema legt geen enum op.
HttpUrl string Volledige URL met protocol http: of https:.
UploadUrl string Lokale URL die begint met /uploads/.
Base64 string Gecodeerde bestandsbytes volgens § 7.2.

Voor FontSize verwerkt de lettergroottehelper getallen en eenheden pt, px, em en % bij omzetting naar punten. Dat is geen volledige CSS-validatie door de bundelvalidator. Behoud bij overdracht de oorspronkelijke waarde en eenheid; "0.84em" mag niet zonder betekenisverlies 0.84 worden.

4. Gedeelde concepten: concepts[]

Veld Type Verplicht Betekenis
id concept-id Ja Unieke identiteit binnen de bundel.
label string Ja Naam; MOET na trimmen niet leeg zijn.
synopsis string Nee Korte beschrijving.
aspects array van strings Nee Aspecten van het concept.
tags array; model: ConceptTag[] Nee Concepttags; zie § 4.1.
descriptionPageSlug string of null; niet-leeg: PageReference Nee Paginaverwijzing naar conceptuitleg.
pageSlug string of null; niet-leeg: PageReference Nee Gekoppelde wikipagina.
cmapSlug string of null; niet-leeg: Slug Nee Gekoppelde CMap.
externalUrl HttpUrl of null Nee Volledige HTTP- of HTTPS-URL. Een lege string is ongeldig.
imageSource string of null Nee Afbeeldingsbron zoals opgeslagen door het conceptmodel.

Andere conceptvelden zijn volgens het schema niet toegestaan. Niet-lege paginaverwijzingen en CMap-verwijzingen MOETEN aan § 3 voldoen. Gebruik voor een ontbrekende verwijzing bij voorkeur weglaten of null.

Conceptnamen MOETEN uniek zijn na trimmen en omzetting naar kleine letters (toLocaleLowerCase() in de validator). Ieder concept MOET op ten minste één CMap in de bundel als niet-phrase-item geplaatst zijn. Hetzelfde concept MAG meerdere plaatsingen hebben en op meerdere CMaps voorkomen.

Het schema laat de inhoud van tags vrij. Het huidige model gebruikt objecten met type en value, bijvoorbeeld {"type":"person","value":"Hans"} of {"type":"label","value":"ontwerp"}. Strings worden omgezet naar tags van type label; bij objecten kan name als alternatief voor value worden gelezen. Paginatags (§ 7) zijn uitsluitend strings.

4.1 Concepttag: ConceptTag

Veld Type in model/export Betekenis
type string Tagcategorie, bijvoorbeeld label of person. Het model bewaart ook andere niet-lege categorienamen; dit is geen gesloten enum.
value string Tagwaarde, bijvoorbeeld een label of persoonsnaam.

Het model schrijft beide velden. Bij invoer wordt een ontbrekend of leeg type aangevuld met label. value en de compatibiliteitsinvoer name worden getrimd. De Modelgids-export bevat alleen lege concepttagarrays; deze elementtypering is daarom ontleend aan het conceptmodel.

De aanvullende export rws-dingen-cmap.json bevat wel gevulde tags, bijvoorbeeld {"type":"person","value":"Hans"}. Daarmee bevestigt die export de objectstructuur. aspects bevat daar onder meer "TODO", "Todo" en "todo": het is een array van vrije strings, geen enum en geen lijst van ConceptTag-objecten. Het model schrijft deze strings niet naar één vaste schrijfwijze om.

Bij opslag in het gedeelde conceptregister bewaart de huidige server via concept-content uitsluitend concepttags met type: "person". Andere tagtypen kunnen dus door het JSON Schema en het JavaScript-model worden geaccepteerd zonder na opslag behouden te blijven. Voor gewone aspecten staat het afzonderlijke veld aspects ter beschikking.

5. CMaps: cmaps[]

Veld Type Verplicht Betekenis
slug slug Ja Uniek CMap-adres binnen de bundel.
title string Ja Titel; MOET na trimmen niet leeg zijn.
document object Ja Diagramdocument volgens onderstaande tabel.

Andere velden op dit CMap-object zijn volgens het schema niet toegestaan.

5.1 Diagramdocument

Veld Type Verplicht Betekenis
schemaVersion integer Nee Versie van het diagrammodel; het huidige model gebruikt standaard 2. Dit is onafhankelijk van formatVersion.
metadata object Nee CMap-metadata; zie hieronder.
derivedView object Nee Verwijzing naar een afgeleide weergave; zie § 5.2.
concepts array van { "id": concept-id } Ja Verwijzingen naar de gedeelde concepttabel; geen conceptinhoud. Id's MOETEN binnen deze array uniek zijn.
items array van items Ja Plaatsingen en verbindingszinnen; zie § 6.
connectors array van connectoren Ja Diagramverbindingen; zie § 6.2.
conceptMaps array; model: MapReference[] Nee Interne kaartreferenties volgens § 6.4; leeg in de Modelgids-export en gevuld in de twee aanvullende exports.
conceptRelations model: ConceptRelation[] Nee Semantische relaties tussen concepten; modeluitbreiding volgens § 5.3.
conceptOwnerships model: ConceptOwnership[] Nee Semantische ouder-kindrelaties; modeluitbreiding volgens § 5.3.
viewport object Nee Toegestaan door het schema; het huidige model bewaart dit niet bij import.

Een lege CMap is toegestaan: de drie verplichte arrays mogen leeg zijn. Gebruik bij voorkeur een document.concepts-verwijzing voor ieder geplaatst concept. De importer verzamelt ook concept-id's rechtstreeks uit de items.

Het huidige model gebruikt de volgende metadata:

Veld Gebruikelijk type Betekenis
namespace string Namespace van de kaart.
tags array van strings Kaarttags.
summary string Samenvatting.
explanationPageSlug string Paginaverwijzing naar kaartuitleg, of "" indien afwezig.

Het schema staat extra velden toe op document en metadata. Dat garandeert niet dat de importer onbekende velden bewaart: het diagrammodel wordt opnieuw opgebouwd. Modeluitbreidingen zoals conceptRelations en conceptOwnerships vallen onder deze open documentstructuur. Hun huidige modeltypen worden hieronder beschreven; het bundelschema specificeert of valideert die interne structuur niet.

5.2 Afgeleide CMap: derivedView

Veld Type Verplicht Betekenis
sourceCmapSlug slug Ja Bron-CMap van de afgeleide weergave.
rootItemId integer Ja Lokaal item in de bron-CMap dat de wortel van de weergave vormt.

Extra velden zijn hier niet toegestaan. Voor een bruikbare afgeleide weergave moeten bronkaart en wortelitem beschikbaar zijn. De exporter probeert de bronkaart ook bij exportdiepte 0 mee te nemen. De browservalidator controleert het bestaan van deze bron en dit item niet.

De export rws-lbs-architectuur-cmap.json bevat vijf concrete afgeleide kaarten (§ 13.3). Hun concepts, items en connectors zijn lege arrays: de bronkaart levert de diagraminhoud. Een lege lokale itemarray betekent bij een derivedView dus niet dat de zichtbare kaart geen inhoud heeft.

5.3 Semantische relaties en eigenaarschap in het model

ConceptRelation is het elementtype van document.conceptRelations:

Veld Type in model/export Betekenis
id RelationId Identiteit van de relatie, onafhankelijk van een lokale connector-id.
sourceConceptId ConceptId Bronconcept.
targetConceptId ConceptId Doelconcept.
label string Relatietekst; standaard "". Er is hier nog geen verwijzing naar een concept dat de verbindingszin vertegenwoordigt.
tags ConceptTag[] Relatietags; standaard [].

Het model schrijft alle vijf velden. Voor constructie zijn een niet-lege id, bron en doel nodig. De repository controleert dat bron en doel in de concepten van het opgebouwde kaartmodel voorkomen. Dit is een latere modelcontrole, niet een controle van validateBundle.

ConceptOwnership is het elementtype van document.conceptOwnerships:

Veld Type in model/export Betekenis
parentConceptId ConceptId Semantisch ouderconcept.
childConceptId ConceptId Semantisch kindconcept.

Beide velden zijn nodig. De repository vereist bestaande, verschillende concepten en wijst cyclisch eigenaarschap af. Zij bewaart één ouder per kind. Dit eigenaarschap is iets anders dan de plaatsing onder parentSubmapId. Een verbindingszin met het label omvat creëert niet automatisch een ConceptOwnership. Beide arrays ontbreken in de Modelgids-export.

6. Diagramitems en connectoren

6.1 Items: cmaps[].document.items[]

Veld Type volgens schema Nadere typering in model/export Verplicht volgens schema
id integer ItemId, lokale identiteit van de plaatsing. Ja
x, y number Diagramcoördinaten; negatieve waarden en decimalen toegestaan. Ja
kind enum van strings concept, page, submap of phrase; standaard concept. Nee
conceptId concept-id ConceptId bij conceptplaatsingen; bij phrase exporteert het model null. Tenzij kind = "phrase"
label string Tekst van de huidige phrase-representatie; conceptplaatsingen halen de naam uit concepts. Bij kind = "phrase"
width, height number Afmetingen in diagramcoördinaten. Nee
parentSubmapId integer of null ItemId van de bovenliggende submap, of null. Nee
hidden boolean Niet bewaard door het huidige model; dit gebruikt hiddenContexts. Nee
backgroundColor, borderColor, textColor string CssColor. Nee
fontFamily string FontFamily. Nee
fontWeight string FontWeight. Nee
fontStyle string FontStyle. Nee
fontSize, synopsisFontSize number FontSize; in de Modelgids-export strings met eenheid. Nee
synopsisFontWeight string FontWeight voor de synopsis. Nee
synopsisFontStyle string FontStyle voor de synopsis. Nee
submapBackgroundColor, submapBorderColor string CssColor of null; in de Modelgids-export steeds null. Nee

De laatste twee verschillen — lettergroottes als strings en nullable submapkleuren — worden door de browser geaccepteerd, maar vallen buiten de huidige veldtypen van het schema (§ 12).

Een phrase-item is de huidige diagramrepresentatie van een verbindingszin; het vereist in versie 1 geen gedeeld conceptobject. Dit beschrijft de opslag, niet de bedoelde conceptsemantiek (§ 1.2). Voor schema-conforme versie-1-invoer BEHOORT conceptId op een phrase weggelaten te worden; de huidige exporter schrijft daar zelf null.

Bij conceptplaatsingen wordt gedeelde inhoud zoals label, synopsis, aspects, tags en links door de exporter van het item verwijderd. Die inhoud staat uitsluitend in de gedeelde concepttabel.

Extra itemvelden zijn toegestaan door het schema. Hun modeltypen staan in § 6.3–6.5. Behoud deze velden bij bewerking van een bestaande export om diagramgedrag en opmaak zo veel mogelijk te behouden.

6.2 Connectoren: cmaps[].document.connectors[]

Veld Type Verplicht volgens schema Betekenis
id ConnectorId (integer) Nee Lokale connector-id; de huidige importer vereist deze wel.
sourceId ItemId (integer) Ja Bestaande item-id in dezelfde CMap.
targetId ItemId (integer) Ja Bestaande item-id in dezelfde CMap.
hasArrow boolean Nee Pijlpunt; standaard true in het model.
relationId model: RelationId (string) Nee, open uitbreiding Koppeling naar een semantische relatie; ontbreekt in de Modelgids-export.
lineColor model: CssColor (string) Nee, open uitbreiding Lijnkleur; standaard "#333".
lineWidth model: number Nee, open uitbreiding Lijndikte; standaard 2.

Extra velden zijn toegestaan. Lever iedere connector met een unieke integer-id binnen zijn kaart aan voor succesvolle import. Connector-id's en item-id's hebben afzonderlijke verzamelingen.

Een connector is één getekend lijnstuk. Bij een constructie bronconcept → phrase → doelconcept zijn er twee connectoren voor één semantische uitspraak. Een phrase kan meerdere inkomende of uitgaande connectoren hebben. hasArrow: false onderdrukt de pijlpunt; sourceId en targetId blijven de geregistreerde bron en het doel van dat lijnstuk.

De exporter verwijdert connectoren waarvan een eindpunt niet meer bestaat. De importer wijst zulke connectoren af als ze toch in het bestand staan.

6.3 Aanvullende itemvelden

Deze velden vallen onder additionalProperties: true van het itemschema. Onderstaande typen beschrijven het huidige model en de editor; de bundelvalidator dwingt deze nadere typering niet af.

Veld Type in model/export Betekenis en waarneming in de Modelgids-export
synopsis string Phrase-eigen toelichting in de huidige representatie; bij omvat is dit "". Voor concepten staat deze inhoud in de gedeelde tabel.
parentCmapLink boolean Markering van een plaatsing als terugkoppeling naar een bovenliggende kaart; hier overal false.
groupId string of null Groepsidentiteit voor plaatsingen; hier null. Door de editor gebruikte vorm: group-<nummer>.
childMap string of null Naam van een onderliggende kaart/submap; hier null. Dit is geen ingebed diagramdocument.
submapDepth integer Nestingsniveau in de editor, met 0 voor het hoofdniveau; hier overal 0. Het schema specificeert dit veld niet.
expanded boolean Of de submap uitgeklapt is; hier false.
submapInitialized boolean Of de submapinhoud is geïnitialiseerd; hier false.
separateMap boolean Of de submap een afzonderlijke interne kaartreferentie heeft; hier false.
mapReference MapReference of null Interne kaartreferentie volgens § 6.4; hier null.
hiddenContexts ContextKey[] Contexten waarin de plaatsing verborgen is; hier [].
layouts Record<ContextKey, Layout> Opgeslagen plaatsing en opmaak per context; hier steeds een object met sleutel root. Zie § 6.5.
autoWidth, autoHeight boolean Automatische bepaling van breedte respectievelijk hoogte. Beide blijven booleans, ook als width en height zijn ingevuld.
synopsisTextColor CssColor Tekstkleur van de synopsis.
synopsisFontFamily FontFamily Lettertypefamilie van de synopsis.

Alleen null in een export toont niet welk niet-null type mogelijk is. De typering van groupId, childMap en mapReference is daarom aangevuld vanuit de editorcode. De twee aanvullende exports bevestigen childMap als string en mapReference als object. Ze bevatten ook integerwaarden voor parentSubmapId, submapdieptes groter dan nul en daadwerkelijke kleuren voor de nullable submapkleurvelden. groupId blijft in alle drie voorbeelden null; hiddenContexts blijft steeds een lege array.

6.4 Interne kaartreferentie: MapReference

Dit is het objecttype voor item.mapReference en de elementen van document.conceptMaps. De submapcontroller maakt de volgende structuur:

Veld Type in model/export Betekenis
id string Interne referentie-id, door de editor opgebouwd als cmap-<ItemId>. Ook bruikbaar als ContextKey; geen opgeslagen CMap-slug.
title string Titel van de interne kaart.
rootItemId ItemId Wortelitem binnen het diagram.
itemIds ItemId[] Items die onder die wortel vallen.

Deze structuur verschilt van derivedView: een afgeleide opgeslagen kaart gebruikt een sourceCmapSlug om een andere opgeslagen CMap aan te wijzen. In de Modelgids-export is conceptMaps leeg en is ieder mapReference null; de structuur hierboven is ontleend aan de model- en controllercode. De twee aanvullende exports bevatten deze structuur daadwerkelijk, onder meer de referentie cmap-23 naar de submap RWS TODO (§ 13.2).

itemIds is een opgeslagen lijst van lokale verwijzingen. De huidige bundelvalidator controleert die lijst niet op volledigheid of overeenstemming met parentSubmapId. Ook in de voorbeelden zijn zulke lijsten niet altijd actueel. Gebruik ze daarom niet als de enige bron voor de submapstructuur; het huidige model bepaalt afstammelingen via parentSubmapId.

6.5 Layout per context: Layout

De export bevat bijvoorbeeld items[0].layouts.root. root is hier een objectsleutel van type ContextKey, geen vast veld in elk layoutobject. Iedere waarde in layouts heeft de volgende structuur:

Veld Type in model/export Voorbeeld uit de Modelgids-export
x, y number 33, 75, 152.66666666666669
width, height number 380, 76
backgroundColor, borderColor, textColor CssColor "#edf7e8", "#57834a", "#222222"; bij de phrase ook "transparent" als randkleur
fontFamily FontFamily "Arial, Helvetica, sans-serif"
fontSize FontSize "11pt"
fontWeight FontWeight "700"
fontStyle FontStyle "normal"
synopsisTextColor CssColor "#4d4d4d"
synopsisFontFamily FontFamily "Arial, Helvetica, sans-serif"
synopsisFontSize FontSize "9pt"; bij de phrase "0.84em"
synopsisFontWeight FontWeight "400"; bij de phrase "700"
synopsisFontStyle FontStyle "normal"

Dit zijn de velden die de layoutcontroller vastlegt. Het bundelschema legt geen verplichte deelvelden of typen voor Layout vast. De directe itemvelden beschrijven de plaatsing; de contextlayout bevat een opgeslagen weergave daarvan. Bij openen van een context past de editor de bijbehorende layout toe. Wie coördinaten of opmaak in een bestaande export wijzigt, moet dus ook de relevante contextlayout consistent aanpassen.

De aanvullende exports bevatten behalve root onder meer cmap-23, cmap-26 en cmap-89 als contexten. Ook afmetingen kunnen decimalen bevatten, bijvoorbeeld width: 180.888916015625. Gebruik voor alle vier geometrievelden dus number, niet integer. Een contextkey is lokaal aan zijn diagram; dezelfde tekst in twee verschillende CMaps maakt de contexten niet identiek.

7. Wikipagina's en bijlagen

7.1 Pagina: pages[]

Veld Type Verplicht Betekenis
reference paginaverwijzing Ja Uniek pagina-adres binnen de bundel, inclusief eventuele namespace.
title string Ja Titel; MOET na trimmen niet leeg zijn.
markdown string Ja Actuele Markdown-bron; MAG leeg zijn.
tags array van strings Ja Paginatags; MAG leeg zijn.
attachments array van bijlagen Nee Ingebedde bestanden; ontbrekend betekent geen meegeleverde bijlagen.

Andere paginavelden zijn volgens het schema niet toegestaan. Bundels van vóór de toevoeging van bijlagen blijven geldig zonder wijziging van formatVersion.

7.2 Bijlage: pages[].attachments[]

Veld Type Verplicht Betekenis
url UploadUrl (string) Ja Oorspronkelijke lokale URL, beginnend met /uploads/; uniek binnen de pagina.
name string Ja Niet-lege bestandsnaam.
mimeType string Ja Niet-leeg MIME-type, bijvoorbeeld image/png.
contentBase64 Base64 (string) Ja Bestandsbytes als standaard Base64.

Andere bijlagevelden zijn niet toegestaan. Base64 gebruikt A–Z, a–z, 0–9, +, / en waar nodig afsluitende =-padding. Witruimte, regeleinden en een data:-prefix zijn niet toegestaan. Een lege string vertegenwoordigt een leeg bestand en wordt door de bundelvalidator geaccepteerd.

Bij import worden bestanden opnieuw geüpload onder de betreffende pagina. De oorspronkelijke url dient als vervangsleutel: voorkomens in de Markdown worden vervangen door de nieuwe upload-URL. Bestandsnamen en URL's hoeven na import dus niet identiek te zijn aan de export.

8. Ontbrekende afhankelijkheden: missing

Veld Type Verplicht Betekenis
cmaps array van slugs Nee CMaps waarvoor het laden tijdens export is mislukt.
pages array van niet-lege strings Nee Paginaverwijzingen waarvoor export is mislukt.

Extra velden zijn niet toegestaan. De exporter schrijft beide arrays, ook als ze leeg zijn. Een mislukte bijlage-export laat de hele betreffende pagina uit pages weg en vermeldt haar verwijzing in missing.pages.

missing bevat geen vervangende inhoud. De importer maakt voor deze records geen lege pagina's of kaarten aan en probeert ze niet alsnog op te halen. Een CMap die uitsluitend wegens de gekozen dieptelimiet niet is meegenomen, wordt niet als ontbrekend vermeld.

9. Aanvullende validatieregels

Naast de veldtypen gelden de volgende controles in validateBundle:

  1. CMap-slugs, pagina-adressen en gedeelde concept-id's MOETEN binnen hun eigen verzameling uniek zijn. Conceptnamen voldoen bovendien aan § 4.
  2. rootCmapSlug MOET exact naar een meegeleverde CMap verwijzen.
  3. Iedere document.concepts[].id en iedere niet-phrase-conceptId MOET bestaan in de gedeelde concepttabel.
  4. Elk gedeeld concept MOET ten minste één niet-phrase-plaatsing hebben. Een vermelding in alleen document.concepts is onvoldoende.
  5. Item-id's MOETEN per kaart uniek zijn; alle connector-eindpunten MOETEN binnen dezelfde kaart bestaan.
  6. Iedere niet-lege pageSlug, descriptionPageSlug en metadata.explanationPageSlug MOET voorkomen als pages[].reference of als exacte verwijzing in missing.pages.
  7. Bijlage-URL's MOETEN per pagina uniek zijn en hun inhoud MOET aan de Base64-regel voldoen.

Een gekoppelde cmapSlug hoeft niet in cmaps of missing.cmaps voor te komen: export kan bewust op een bepaalde diepte stoppen. Ook extra pagina's zonder verwijzing vanuit een concept of kaart zijn toegestaan.

Er is geen controle dat iedere upload-URL in Markdown een bijlage heeft, noch dat iedere meegeleverde bijlage daadwerkelijk wordt gebruikt. Voor een zelfstandige overdracht BEHOORT de generator alle benodigde lokale bestanden mee te leveren.

10. Selectie tijdens export

De normale JSON-export slaat eerst openstaande kaartwijzigingen op en neemt de gekozen wortelkaart mee. De ingestelde volgdiepte wordt begrensd op 0 tot en met 10; standaard is 0.

  • Diepte 0 exporteert de wortelkaart en haar pagina-afhankelijkheden.
  • Een gevolgde cmapSlug van een geplaatst concept verhoogt de diepte met één.
  • De bron van een derivedView wordt op dezelfde diepte geladen, ook als de dieptelimiet al is bereikt.
  • Iedere CMap-slug wordt hoogstens één keer bezocht, zodat cycli niet tot eindeloze recursie leiden. De huidige implementatie volgt diepte-eerst; de eerste bezochte route bepaalt de verdere verwerking van een kaart.
  • De exporter verzamelt pageSlug en descriptionPageSlug van geplaatste concepten, plus metadata.explanationPageSlug van meegenomen kaarten.
  • Gewone links in de Markdown van die pagina's worden niet recursief gevolgd.
  • Herkende lokale /uploads/-verwijzingen in Markdown en src/href-attributen worden als bijlagen ingebed, eenmaal per URL per pagina. Externe bestanden en imageSource worden niet zelfstandig als bijlage opgehaald.

Bij gelijke concept-id's verzamelt de bundelbouwer de eerst aangetroffen conceptinhoud. In normale exports komt die inhoud uit het gedeelde conceptmodel. De resulterende bundel wordt vóór teruggave met validateBundle gecontroleerd.

11. Importgedrag en limieten

De browser leest en valideert het geselecteerde bestand vóór de importwrites. De maximale bestandsgrootte is 256 MiB (268435456 bytes), inclusief JSON en Base64. Een afzonderlijke gedecodeerde bijlage mag maximaal 50 MiB (52428800 bytes) bevatten.

De server begrenst daarnaast ieder geserialiseerd CMap-opslagdocument op 10 MiB (10485760 bytes). De bestandsgroottegrens van de bundel vervangt deze afzonderlijke opslaggrens niet.

Conflicten worden bepaald op pages[].reference en cmaps[].slug ten opzichte van de geladen catalogi. De gebruiker kiest voor vervangen of overslaan van bestaande records. Nieuwe records worden in beide gevallen geïmporteerd. Overslaan van een pagina slaat ook haar bijlagen over.

De importer verwerkt eerst pagina's en daarna CMaps, telkens in arrayvolgorde. Bij een nieuwe pagina met bijlagen wordt eerst de pagina aangemaakt, daarna worden bestanden geüpload en wordt de Markdown met nieuwe URL's opgeslagen. Bij een bestaande pagina worden de bestanden vóór de pagina-update geüpload. Pagina-updates gebruiken baseVersion; CMap-updates laden de bestaande kaart en slaan via de repository op.

Voor iedere CMap voegt de importer de gedeelde conceptinhoud weer samen met de lokale plaatsingen en bouwt een CmapModel. De server normaliseert conceptidentiteiten. Bij gelijke namen na trimmen en omzetting naar kleine letters kan een bestaande conceptidentiteit worden gebruikt. Als daarbij de id verandert naar die van een bestaand concept, kan de bestaande inhoud worden behouden. Bij updates van gedeelde conceptinhoud kunnen ook andere kaarten met datzelfde concept de gewijzigde inhoud tonen.

De overdracht is geen enkele database-transactie voor de gehele bundel. Een fout tijdens upload, modelopbouw of opslag kan optreden nadat eerdere records al zijn geïmporteerd. De grens van 50 MiB wordt pas bij het decoderen voor upload gecontroleerd. Geldige bundelvalidatie garandeert daarom niet dat alle vervolgstappen slagen. Na succesvolle import worden de catalogi vernieuwd en wordt rootCmapSlug geopend. Versiegeschiedenis uit de bron wordt niet hersteld; de doelwiki legt normale nieuwe versies vast.

12. Bekende verschillen tussen schema en uitvoering

Deze punten beschrijven het huidige gedrag, geen uitbreiding van het schema:

Onderwerp JSON Schema Huidige uitvoering
Validatiemethode Volledige structurele veldregels. De importer roept validateBundle aan en laadt het schema niet.
Onbekende velden Op meerdere objecttypen verboden. De browservalidator wijst onbekende velden niet algemeen af. Het model kan ze vervolgens weglaten.
Exportmetadata Typen voor $schema, exportedAt en generator; date-time voor het tijdstip. Deze velden worden niet door validateBundle getoetst.
Documentarrays concepts, items en connectors verplicht. Ontbrekende of niet-array waarden worden in meerdere validatorlussen als lege arrays behandeld.
Getallen JSON-getallen vereist. De validator gebruikt bij item-id's, posities en connector-eindpunten Number(...), waardoor sommige andere JSON-typen de controle kunnen passeren.
Verbindingszin Indien aanwezig moet conceptId een geldige concept-id zijn. Het huidige model exporteert bij phrase expliciet conceptId: null; de browser accepteert dit, het schema niet.
Lettergroottes fontSize en synopsisFontSize op items zijn number. De Modelgids-export bevat "11pt", "9pt" en "0.84em"; model en browservalidator accepteren die strings.
Submapkleuren submapBackgroundColor en submapBorderColor zijn string. De Modelgids-export bevat expliciet null; model en browservalidator accepteren dit.
Connector-id Optioneel. CmapModel vereist een integer-id en wijst dubbele connector-id's af, pas bij de modelopbouw tijdens import.
Optionele velden Onder meer typen voor kind, label, opmaak en conceptinhoud. Niet al deze typen en verplichte conditionele velden worden door validateBundle gecontroleerd.
Submapstructuur parentSubmapId heeft een type; derivedView heeft veldregels. De bundelvalidator controleert geen submapcycli, ontbrekende ouders of het bestaan van de afgeleide bron/wortel.
Behoud van velden Open uitbreiding van document, items en connectoren; onder meer viewport en hidden toegestaan. Het huidige model serialiseert alleen ondersteunde velden; viewport en itemveld hidden worden niet bewaard.

De Modelgids-export voldoet dus niet volledig aan het gepubliceerde JSON Schema, hoewel de huidige bundelvalidator hem accepteert. Het schema zou onder meer de lettergroottes, nullable submapkleuren en de bestaande phrase-representatie moeten afstemmen op de uitvoering. Dat staat los van de verdere domeinwijziging om verbindingszinnen als gedeelde concepten te modelleren (§ 1.2).

Voor een nieuwe externe generator: voldoe aan het schema én § 9, lever unieke connector-id's, laat conceptId bij verbindingszinnen weg en gebruik de gedocumenteerde veldtypen. Reken niet op een byte-identieke export na import.

13. Voorbeelden

13.1 Analyse van de aangeleverde Modelgids-export

Bronbestand: modelgids-lts-opzet-cmap.json. De export vermeldt generator Racket Wiki 0.2.122 en tijdstip 2026-09-18T13:35:14.928Z.

Onderdeel Aantal / waarde
CMaps 1: modelgids-lts-opzet
Gedeelde concepten 15: het hoofdconcept en 14 hoofdstukconcepten
Diagramitems 16: 15 conceptplaatsingen en 1 phrase-item
Phrase Item 2, label omvat, conceptId: null
Connectoren 15: één lijnstuk van item 1 naar phrase 2, veertien van phrase 2 naar items 3 t/m 16
Wikipagina's 15: veertien conceptuitlegpagina's en de CMap-uitlegpagina
Bijlagen 12, alle met MIME-type image/png
conceptRelations, conceptOwnerships Beide afwezig
conceptMaps Lege array
Ontbrekende afhankelijkheden missing.cmaps en missing.pages beide leeg

Onderstaand fragment selecteert de eerste drie items en de eerste twee connectoren. Alleen de genoemde velden zijn overgenomen; dit is geen zelfstandige importbundel:

{
  "items": [
    {
      "id": 1,
      "conceptId": "fdaf8df6-57b5-4eaf-98af-d2fcc00c1439",
      "kind": "concept"
    },
    {
      "id": 2,
      "conceptId": null,
      "kind": "phrase",
      "label": "omvat"
    },
    {
      "id": 3,
      "conceptId": "7aa9ca75-a272-4a74-aca2-f2b586849bfa",
      "kind": "concept"
    }
  ],
  "connectors": [
    { "id": 1, "sourceId": 1, "targetId": 2, "hasArrow": false },
    { "id": 2, "sourceId": 2, "targetId": 3, "hasArrow": true }
  ]
}

Door de concept-id's op te zoeken in concepts volgt de uitspraak:

Modelgids LTS Opzet — omvat → 1. Inleiding, doel en scope

De dertien andere uitgaande connectoren drukken dezelfde relatie uit naar de overige hoofdstukken. Daarmee verbeeldt één phrase-item veertien uitspraken. De tekst omvat heeft in deze export geen eigen ConceptId. Ook heeft geen van deze uitspraken een afzonderlijk geëxporteerd ConceptRelation-record. Hun betekenis is hier af te leiden uit de diagramstructuur, niet uit een expliciet opgeslagen semantische relatietabel.

In het bedoelde domeinmodel zou het verbindingsconcept omvat een eigen gedeelde identiteit hebben, en zouden de uitspraken bronconcept, verbindingsconcept en doelconcept verbinden. De plaatsing met item-id 2 zou dan alleen een weergave van dat verbindingsconcept zijn. Dit is de gewenste interpretatie; bovenstaande JSON toont de huidige representatie.

13.2 Aanvullend voorbeeld: RWS Dingen

Bronbestand: rws-dingen-cmap.json. Deze bundel van 2026-09-18T13:41:32.898Z bevat 5 CMaps, 161 gedeelde concepten, 77 pagina's en 14 bijlagen. missing.pages bevat 87 verwijzingen; missing.cmaps is leeg. Dat betekent dat de betreffende pagina-inhoud niet is meegeleverd, niet dat die verwijzingen uit de concepten verdwijnen. De export vermeldt niet waarom het ophalen per pagina is mislukt.

CMap Items Phrases Connectoren
rws-dingen 38 9 41
model-lts 45 10 42
rws-lbs-architectuur 69 21 71
review-zuidasdok-augustus-2026 59 8 63
onderhoudsgui-lts 6 3 6

Alle 51 phrases hebben conceptId: null. Een phrase-label hoeft grammaticaal geen werkwoord te zijn: deze export bevat bijvoorbeeld ook Architectuur, Personen en Introductie. label is vrije tekst.

De 166 niet-phrase-plaatsingen verwijzen naar 161 gedeelde concepten. Het concept RWS Model LTS staat bijvoorbeeld tweemaal op rws-dingen, met item-id's 2 en 33 maar dezelfde concept-id. Architectuurprincipes komt met dezelfde concept-id voor op twee verschillende kaarten. Dat illustreert waarom ItemId en ConceptId verschillende typen en verschillende identiteitsbereiken hebben.

Onderstaand fragment is de daadwerkelijke mapReference van item 23 (kind: "submap") op rws-dingen; het is geen zelfstandige bundel:

{
  "id": "cmap-23",
  "itemIds": [6, 7, 8, 9, 15, 20, 21, 22, 24, 29, 31, 32, 33, 34, 35, 40, 41, 42, 43],
  "rootItemId": 23,
  "title": "RWS TODO"
}

Dit item heeft separateMap: true, childMap: "RWS TODO" en layouts voor zowel root als cmap-23. De referentie beschrijft een interne weergave van de kaart; er hoeft geen afzonderlijk cmaps[]-object met slug cmap-23 te bestaan.

13.3 Aanvullend voorbeeld: RWS LBS Architectuur

Bronbestand: rws-lbs-architectuur-cmap.json. Deze bundel van 2026-09-18T13:44:02.082Z bevat 8 CMaps, 96 gedeelde concepten, 40 pagina's en 23 bijlagen. missing.pages bevat 55 verwijzingen; missing.cmaps is leeg. De drie kaarten met eigen diagramitems zijn rws-lbs-architectuur, model-lts en modelgids-lts-opzet.

De overige vijf kaarten gebruiken derivedView:

Afgeleide kaart sourceCmapSlug rootItemId
kaderdocumenten-beoordelen rws-lbs-architectuur 36
sd-inhoud rws-lbs-architectuur 48
metamodel-lbs rws-lbs-architectuur 89
refactoring-lts-model model-lts 26
lts-aspecten-voor-modelverniewing-en-versimpeling model-lts 37

Bijvoorbeeld: kaderdocumenten-beoordelen bevat geen eigen items of connectoren, maar verwijst met dit daadwerkelijke derivedView-object naar de bronkaart:

{
  "rootItemId": 36,
  "sourceCmapSlug": "rws-lbs-architectuur"
}

rootItemId moet hier in de bronkaart worden opgezocht. sourceCmapSlug is een opgeslagen CMap-adres, terwijl cmap-36 in een MapReference een interne referentie- en contextkey is. Deze waarden zijn niet uitwisselbaar. Dat ook model-lts in deze bundel staat, past bij de exportregel die de bronkaart van een afgeleide weergave meeneemt.

De twee bronkaarten rws-lbs-architectuur en model-lts zijn inhoudelijk gelijk aan hun records in de RWS Dingen-export. Deze voorbeelden zijn daarmee overlappende bundels, geen afzonderlijke definities van dezelfde concepten. Ook deze derde bundel bevat phrases zonder gedeelde concept-id en geen afzonderlijk geëxporteerde semantische relaties.

13.4 Volledige kleine versie-1-bundel

Deze bundel toont twee gedeelde concepten, een verbindingszin, twee connectoren, een pagina en een ingebed tekstbestand. SGFsbG8K is Base64 voor Hallo gevolgd door een newline. De tijdelijke concept-id's worden bij opslag opgelost. De verbindingszin gebruikt de huidige versie-1-representatie. Het voorbeeld is dus geen implementatie van het gewenste verbindingsconcept uit § 1.2.

{
  "$schema": "/schemas/racket-wiki-cmap-bundle-v1.schema.json",
  "format": "racket-wiki-cmap-bundle",
  "formatVersion": 1,
  "exportedAt": "2026-09-18T12:00:00.000Z",
  "generator": "Voorbeeldgenerator",
  "rootCmapSlug": "voorbeeld",
  "cmaps": [
    {
      "slug": "voorbeeld",
      "title": "Voorbeeldkaart",
      "document": {
        "schemaVersion": 2,
        "metadata": {
          "namespace": "",
          "tags": ["documentatie"],
          "summary": "Een eenvoudige voorbeeldkaart",
          "explanationPageSlug": ""
        },
        "concepts": [
          { "id": "new:proces" },
          { "id": "new:documentatie" }
        ],
        "items": [
          {
            "id": 1,
            "conceptId": "new:proces",
            "kind": "concept",
            "x": 100,
            "y": 80,
            "width": 200,
            "height": 90,
            "parentSubmapId": null
          },
          {
            "id": 2,
            "kind": "phrase",
            "label": "heeft documentatie",
            "x": 360,
            "y": 100
          },
          {
            "id": 3,
            "conceptId": "new:documentatie",
            "kind": "concept",
            "x": 600,
            "y": 80
          }
        ],
        "connectors": [
          { "id": 1, "sourceId": 1, "targetId": 2, "hasArrow": false },
          { "id": 2, "sourceId": 2, "targetId": 3, "hasArrow": true }
        ]
      }
    }
  ],
  "concepts": [
    {
      "id": "new:proces",
      "label": "Proces",
      "synopsis": "Een reeks samenhangende activiteiten",
      "aspects": ["ontwerp"],
      "tags": [{ "type": "person", "value": "Hans" }],
      "pageSlug": "proces:beschrijving",
      "externalUrl": "https://example.com/proces"
    },
    {
      "id": "new:documentatie",
      "label": "Documentatie"
    }
  ],
  "pages": [
    {
      "reference": "proces:beschrijving",
      "title": "Procesbeschrijving",
      "markdown": "# Proces\n\n[Voorbeeldbestand](/uploads/proces:beschrijving/hallo.txt)",
      "tags": ["voorbeeld"],
      "attachments": [
        {
          "url": "/uploads/proces:beschrijving/hallo.txt",
          "name": "hallo.txt",
          "mimeType": "text/plain",
          "contentBase64": "SGFsbG8K"
        }
      ]
    }
  ],
  "missing": { "cmaps": [], "pages": [] }
}

14. Bronnen in de repository