This repo contains the source code of the Web demo based on rudof-lib WebAssembly features
The demo converts RDF data and property graphs between formats, validates them
with ShEx, SHACL, DCTAP or PGSchema, generates synthetic data from a SHACL, ShEx
or PGSchema schema, queries RDF data with SPARQL, and compares RDF graphs and
schemas, all in the browser. rudof (@rudof/rudof)
and PlantUML are loaded from jsDelivr when the page runs; everything else is
built from this repo.
You need Node.js 22 (22.12 or later) and npm.
npm ci # install the dependencies, as in package-lock.json
npm run dev # serve the page at http://localhost:5173, reloaded on every change
npm run build # build the page into dist/
npm run preview # serve dist/ at http://localhost:4173dist/ is a static site: its paths are relative, so it can be published in
any folder of any web server, as is.
The inputs that ask for RDF data, a schema or a query are one component
(inputs.js), so that they look and work the same in all the
tabs. index.html writes only their textarea, with the kind of the input,
its label and its example:
<div class="field">
<span class="hint" id="shex-data-unused" data-head hidden>Not used: …</span>
<textarea id="shex-data" data-input="rdf" data-label="RDF data">…</textarea>
</div>inputs.js runs before the editors are made (codemirror-setup.js), and
makes the head of each input: its label, the elements of the field marked
data-head, and the menu of its formats (<id>-format) or the name of its
only format. Then each one has Text | URL (below), and Visualize when
it can be drawn (the RDF data and the ShEx schemas).
data-input="rdf": RDF data, in the RDF formats (RDF_FORMATSofschema-convert.js): the RDF data of the RDF, ShEx, SHACL, DCTAP and SPARQL tabs, the target declarations of SHACL, the two of Compare, and the RDF data added to a tab (+ Add RDF data).data-input="schema": a schema, in the formats of its language (data-language:shex,shacl,dctaporpgschema;LANGUAGESofschema-convert.js): the schemas of the Validate tabs. When the page chooses the language (the Schema tab, Generate, Compare), the menu is empty and the page fills it, or the textarea names a menu of its own (data-format-select).data-input="query": a query (data-language="sparql"): the SPARQL query.
The other inputs (property graphs, shape maps, type maps, the prefixes of a DCTAP profile, the configuration of Generate) are editors as before.
The examples of the inputs are the same in all the tabs: the files of
examples/person/, which a textarea names with
data-example="person/data.ttl" (any textarea can have one). They are
persons with a name, maybe a birth date, who know other persons:
| File | Example of |
|---|---|
data.ttl |
the RDF data of the RDF, ShEx, SHACL, DCTAP, SPARQL and Compare tabs |
schema.shex |
the ShEx schema of the ShEx, Schema and Compare tabs |
shapes.ttl, profile.csv, schema.pgs |
the same schema in SHACL, DCTAP and PGSchema |
shapemap.txt |
the shape map of ShEx and DCTAP |
data.pg, typemap.txt |
the same data as a property graph (Data and PGSchema), and its type map |
query.rq |
the SPARQL query (the first of its examples) |
All the schemas say the same: Alice and Carol conform, and Bob doesn't (two
names, and a birth date that is not a date). The Generate tab has them as
its first example. The build publishes examples/person/ with the page.
As in RDFShape, the RDF data, the
schemas and the queries can be given as Text (the editor) or by a
URL: the switch next to their names (input-source.js).
With URL, a field takes the place of the editor, and the page reads the URL
when the tab runs (with fetch: its server must allow other sites to read
it, CORS). Its text goes to the input, so rudof reads it as any other text,
and the format menu takes the format of the extension of the file (.ttl,
.nt, .shex…), when it has one. Under the field, the page says what it
read.
- In the tabs with an endpoint, the URL can be a prefixed name, with the
prefixes of the endpoint (and common prefixes): on Wikidata,
es:E371ishttps://www.wikidata.org/wiki/Special:EntitySchemaText/E371, the EntitySchema E371. The field shows the URL of the prefixed name. - Text gives back the text that the input had, and URL the last URL. Edit as text reads the URL and puts its text in the editor.
- Permalinks have the URL of the input (
schema-url=es:E371), not its text. - The relative imports of a ShEx schema from a URL are relative to the URL.
- An example of a manifest, or a result used as input, puts its text in the input, as text.
All the inputs of RDF data, schemas and queries (see The inputs of RDF data, schemas and queries) can be given by a URL.
Visualize, next to the format menu of the inputs of RDF data (the RDF
data of the RDF, ShEx, SHACL, DCTAP and SPARQL tabs, the target declarations
of SHACL, and the two of Compare) and of ShEx schemas (of the ShEx tab, and
the two of Compare), shows the input as a
diagram in place of its editor. rudof writes the diagram in PlantUML
(serializeData and serializeCurrentShex), and PlantUML draws it, as for
the UML diagrams of the results. The button is then Edit, which shows the
editor again.
- The RDF data of a tab is drawn with the RDF data added to it; the other inputs of RDF data (Compare, the target declarations) alone. An input given by a URL is read first. The relative imports of a ShEx schema are read too.
- Show PlantUML source shows the source of the diagram, and Download SVG saves the diagram.
- When the input has an error, the box says it.
- An example, a result used as input, or Swap in Compare, shows the editor again.
The inputs of the demo are CodeMirror 6 editors,
set up by codemirror-setup.js:
-
Each
<textarea data-editor>ofindex.htmlis replaced by an editor when the page loads. The textarea stays, hidden, and stands for the editor, sodemo.jsreads and setstextarea.valueas before. -
The format of the text is fixed with
data-language="sparql", or chosen in a select of the page withdata-format-select="<id of the select>". -
The language of each format is in
languages/index.js, and is loaded the first time it is needed. Turtle (also TriG, N-Triples, N-Quads and N3), ShExC and shape maps have their own parsers inlanguages/; RDF/XML, JSON-LD, ShExJ and SPARQL use the CodeMirror packages. -
Property graphs, their schemas and type maps have Lezer grammars, each language in its own folder:
languages/yars-pg/: YARS-PG (yars-pg.grammar). It reads the syntax of rudof (pg.rustemoof rudof'spgschemacrate) and YARS-PG 5 (YARSpg.ebnf): metadata, variables, graphs, undirected edges, sets, structs, meta-properties and schemas.languages/pgschema/: PGSchemaC (pgschema.grammar, frompgs.rustemo) and type maps (typemap.grammar, frommap.rustemo).
Each folder has the grammar,
highlight.js(the highlighting tag of each node of the grammar) andindex.js(the CodeMirror language, with folding and indentation).@lezer/generatorcompiles the grammars when the page is built and tested (thelezer()plugin ofvite.config.jsandvitest.config.js). Text that does not follow the grammar is still shown; the highlighting goes on after it. -
The editors have line numbers, undo history, search (Ctrl+F), bracket matching and closing, and take the colors of the page, light or dark. Ctrl+Enter still validates.
-
The editors of Turtle (TriG, N3), ShExC and SPARQL complete prefixes (
prefix-completion.js): afterPREFIX(or@prefix), the declarations of the prefixes known; in the text, their aliases (wd:), and choosing one that the text doesn't declare also adds its declaration, with the others, in the style of the text. The prefixes known are those of the text, those of the endpoint of the tab, and common ones (prefixes.js). Ctrl+Space shows them. -
The results of conversions (RDF data, property graphs, and ShEx and SHACL schemas), of validations and of queries are shown in read-only editors, highlighted in the format chosen for the result. Results in CSV have a CSV | Table switch, to see them as a table; the browser remembers it. demo.js still writes the result in its
<pre>, which is hidden, and the viewer follows it (pre[data-viewer]).
The Schema section (between Data and Validate) converts a schema in
ShEx, SHACL, DCTAP or PGSchema, in one of its formats, to another format, to
another schema language, to a UML diagram or to a SPARQL query.
schema-convert.js has what each language converts to:
| From \ To | ShEx | SHACL | DCTAP | PGSchema | UML | SPARQL |
|---|---|---|---|---|---|---|
| ShEx | ✓ | – | – | – | ✓ | ✓ |
| SHACL | ✓ | ✓ | – | – | ✓ | ✓ |
| DCTAP | ✓ | – | ✓ | – | ✓ | ✓ |
| PGSchema | – | – | – | ✓ | – | – |
The conversions to ShEx, UML and SPARQL go through ShEx (ShExJ); SHACL, DCTAP and PGSchema are written again as rudof reads them (DCTAP as text or JSON, PGSchema as text: rudof 0.3.25 can't write DCTAP CSV or PGSchemaC yet). What rudof can't do yet (–) is in the menus, marked "not in rudof yet", and can't be chosen. A DCTAP profile has its prefixes and its table view, as in the DCTAP tab of Validate. Use as input reads the result as the schema, when it is a schema in a format the tab reads.
The Compare section compares two RDF graphs (Data) or two schemas
(Schemas), with compare.js. What rudof can't compare yet
is in the menus, and can't be chosen: property graphs, and the schemas of
SHACL, DCTAP and PGSchema.
- Data (rudof 0.3.36,
diffData) says how the first RDF data and the second one are related, up to the names of their blank nodes: they are isomorphic (the same graph), equivalent (each one is contained in the other, but not isomorphic), the first one is contained in the second one (subgraph) or the second one in the first one (supergraph), or they are different. Blank nodes says how the blank nodes of a graph may be mapped to the terms of the other one: injective (two blank nodes can't go to the same term; the default), homomorphic (they can), or entailment (a blank node can also go to an IRI or a literal).- Table has the triples that are only in one of the graphs, as in the
other tables of results (sorted, filtered, with links), written with the
prefixes of the two inputs, and the blank nodes of the first graph matched
in the second one. Report is the report of rudof, and JSON what
diffDatareturns. - The names of the graphs of TriG and N-Quads are not compared.
- When an input can't be read, the result says which one.
- Table has the triples that are only in one of the graphs, as in the
other tables of results (sorted, filtered, with links), written with the
prefixes of the two inputs, and the blank nodes of the first graph matched
in the second one. Report is the report of rudof, and JSON what
- Schemas (
compareSchemas) compares a shape of the first ShEx schema with a shape of the second one, property by property, and shows the report of rudof. Shape is the shape of each schema (a prefixed name of the prefixes of the schema, or an IRI); when it is empty, it is the first shape of the schema. rudof doesn't say yet whether the schemas are equivalent, or one contains the other, and it only reads ShExC for this (ShExJ and ShExR are in the menu, and can't be chosen). In rudof 0.3.36,compareSchemascrashes with SHACL and ShExR (a panic, "not yet implemented"), so the page doesn't call it for them. - Swap swaps the two inputs, with their formats and shapes. Permalinks
have both inputs:
dataanddata2, orschemaandschema2, with#compare/compare-dataor#compare/compare-schemas.
A ShEx schema can import other schemas (IMPORT <iri> in ShExC, imports
in ShExJ). Since rudof 0.3.31, rudof with endpoints gets them with fetch
(readShexAsync, which goes through setFetch too), so the page reads a
schema with imports with it (schema-convert.js,
readShexSchema): in the ShEx tab (validations, also on endpoints, and
Convert schema) and in the Schema tab. Schemas without imports are read
as before (readShex). While rudof gets the imports, the result says
"Reading the imported schemas…". A schema with imports is read in a new
session: a session that waits can't do anything else.
- The servers of the imported schemas must allow other sites to read them (CORS).
- Relative imports (
IMPORT <book.shex>) are relative to the file of the schema, when it is the schema of an example of a manifest. - What rudof writes keeps the imports (
IMPORT,imports): ShEx in other formats, and UML diagrams, are written from the schema read, with its imports. Conversions that read the schema again (ShEx to SPARQL) can't get the imports yet. - Only rudof with endpoints has
readShexAsync(it needs JSPI): in Firefox and Safari, the page says that the imports need it.
The DCTAP tab of Validate reads a
DCTAP profile in CSV
(spreadsheets can be saved as CSV). The names of the profile are prefixed
names (foaf:name) or names without prefix (Person), so the tab also has
Prefixes of the profile, in Turtle syntax: PREFIX foaf: <…>, and
BASE <…> for the names without prefix. The basic prefixes of rudof (rdf,
rdfs, xsd, sh, dc) are always there. The values of picklists are
separated by spaces, as in DCTAP, or by |, as rudof does by default.
dctap.js turns the prefixes into the configuration of rudof
(TOML).
- Validate converts the profile to ShEx and validates the data with it, for the shape map. rudof can't convert DCTAP to SHACL yet: the SHACL option of Validate with is there for when it can.
- Convert the profile to ShEx (ShExC, ShExJ or ShExR), a UML diagram, a SPARQL query, or DCTAP as rudof reads it (text or JSON). SHACL will come with rudof.
The profile is highlighted by column (languages/dctap/):
the header names the column of each cell. The CSV | Table switch of the
profile shows it as a table (table-view.js, with
csv.js), whose cells can be edited: Enter goes to the cell below,
Add row and × add and delete rows, and each change is written back to
the CSV, which stays the input. The browser remembers the view.
The Generate section (the last one, after Compare) generates synthetic
data from a schema with generate of rudof (since rudof 0.3.37), and
validates it with the same schema right away. It has a tab for each schema
language, SHACL, ShEx and PGSchema, which share their panel
(data-panel="gen"): the tab chooses the language (data-select,
data-value), and the permalink keeps it (#generate/gen-shex).
generate.js has the views of the result.
As in the tabs of Validate, the schema is the first box, and Generated data the second one: Generate writes in it the data it generated, under them the Generator configuration, and the result is below.
- The examples are in a manifest,
examples/generate/manifest.yml, with the schemas next to it, which the build publishes with the page. Each section is an example (with anid), and each entry of a section a kind of data (kind), withaction: generate, the schema in each language (shaclURL,shexURL,pgschemaURL) and the configuration of the generator (config). The Examples menu opens it (Generate examples), and so does the link above the boxes: an entry of it opens the Generate tab, with its schema and its configuration. An example ofaction: generateof another manifest also goes to the tab; help.html documents the keys. - The first example, which the tab has at first, is the one of the other
tabs (persons,
examples/person/), in SHACL, ShEx and PGSchema: a small schema to edit, to write your own. The others are six models, each written in the three languages: references, keys, facets, labels, LUBM and LDBC SNB. They are written over the RDF encoding of a property graph, so the three schemas generate the same graph, and SHACL and ShEx also get the property graph. The kinds of data write the generator's configuration (TOML, version 2), which can be edited before Generate: valid data, dense or sparse data, a target coherence, or data with errors (20% of the entities broken, one constraint each). - The line above the boxes says which example and kind of data the tab has (the first one of the manifest at first). A permalink has the schema and the configuration: when it opens, the example is the one with this schema (and the kind of data the one with this configuration), so its other languages are there too.
- Generated data has the RDF data (Turtle), or, from a PGSchema, the property graph (YARS-PG); its menu shows the other one when the data has it (the examples of SHACL and ShEx have a property graph too). It can be edited: Validate with, Open in and Download take its text, and the menu keeps the text of each one. Permalinks don't have it.
- Show: a summary (nodes, the property graph's nodes and edges, the report, what the validator rejects, the nodes of each shape), the type map of the property graph, the ShapeMap, the error report and the validation. With errors, the report says which nodes a correct validator must reject (direct, the broken node, and induced, a node that refers to it) and which changes leave the data valid (trap). The verdict compares it with what the validator rejects. Download saves the view shown, or, with the summary, the generated data.
- Validate with SHACL / ShEx / PGSchema validates the data here, with the example's schema in that language. Open in SHACL / ShEx / PGSchema copies the data and that schema to the tab of Validate instead. PGSchema needs a property graph, and a schema of your own (one that is edited, or that an example has in one language only) has no other language.
- The schema, the generated data, the configuration and the result have a height of their own (their handle, or Full screen, makes them bigger): the data can be long.
In the SPARQL, ShEx, SHACL and DCTAP tabs, the Endpoint field runs the query or the validation on a SPARQL endpoint instead of the RDF data of the page. Its menu has None (the RDF data of the page), the endpoints that rudof knows (Wikidata, DBpedia, UniProt, …) and Other, to write the URL of any other endpoint in the field next to it.
rudof goes to the endpoint: useEndpoint, then runQueryAsync,
validateShexAsync or validateShaclAsync. ShEx asks the endpoint for the
data of each node of the shape map, and SHACL for the data of its targets.
The page describes the endpoint to rudof (endpointConfig in
endpoint.js) with the prefixes of the schema, so the shape
map can use them: the data of an endpoint has no prefixes.
Under the field, Prefixes of … lists the prefixes that rudof declares
for the endpoint (in rudof_rdf/endpoints/<name>.toml of its repository,
which the page reads from jsDelivr for the version of rudof it loads: the
npm package doesn't have them), or that the settings give it. The editors
of the tab offer them, and the validations on the endpoint can use them in
their shape maps, with the prefixes of the schema.
With the RDF data, next to the field, also uses the RDF data of the tab:
rudof merges it with the data of the endpoint (useEndpoint, then
readData with merge), so a validation or a query sees both. SPARQL
SERVICE to an endpoint is not available: rudof says that the service is
not supported.
This needs rudof built with the endpoints feature (@rudof/rudof/endpoints,
in the package since rudof 0.3.27), and its WebAssembly needs JSPI
(JavaScript Promise Integration): Chrome and Edge 137 or later have it,
Firefox and Safari don't yet. So the page loads
@rudof/rudof@<version>/endpoints/web/rudof_wasm.js in browsers with JSPI,
and the usual web/ build in the others, or when it can't be loaded. The
status line says "with endpoints" when it has them. When rudof with
endpoints can't be loaded in a browser with JSPI, the hint of the Endpoint
fields says why (as does the tooltip of the status line).
Without them, the page sends SPARQL queries to the endpoint itself
(endpoint.js, with the SPARQL 1.1 protocol), and the
validations can't use endpoints.
While a run waits for an endpoint, the result shows its queries: how many
were sent, answered, waiting and failed, the time of the run (in the head of
the result), t
2360
he limits of the requests, Stop, and a table of the
queries, the last one first. The table has, for each query, when it was sent,
the status of the answer, how long it took, its size, the nodes it asks about
(rudof asks about several nodes of a shape map in one query, with VALUES)
and the query. A click on a query shows all of it, the endpoint and the start
of the answer (at most 4000 characters; JSON indented). The table shows the
last 500 queries (it is drawn again at each one). After the result, the
switch Result | Queries (n) of the result goes from one to the other,
also when the run fails or is stopped.
Stop aborts the queries that wait, and stops the run. rudof can't be stopped from JavaScript: the session goes on with the validation until it gives up. Until then, the requests of the stopped run fail at once, without going to the network (the summary says "stopping: rudof gives up" and how many requests were not sent): else rudof would go on asking the endpoint, and reading its answers, in the page, which can hang it. rudof gives up when its requests fail (after its retries, some seconds). A new run of the tab waits until then (at most 15 s), and says "Stopping the previous run…": the requests of the two runs could not be told apart.
rudof has no logs in WebAssembly, so the page gets the requests from the
function that rudof uses to send them (setFetch):
endpoint-log.js sends them with fetch and logs them. The
query of the SPARQL tab that the page sends itself (without rudof with
endpoints) is shown too. rudof counts the requests of a session
(requestCount), but can't say it while the session waits, so the page says
it after the run, when it differs from the list (the list can have the
queries of other tabs that run at the same time).
rudof 0.3.29 waits between two requests to an endpoint, so that public
endpoints such as Wikidata don't block it, and asks about the nodes of a shape
map in batches. The Endpoints section of the settings (also Options,
next to the Endpoint field) changes this, in [rdf] of the configuration:
- Milliseconds between two requests (
min_request_interval_ms): 1100 by default, 0 forlocalhost. 0 doesn't wait. rudof still waits longer when an endpoint answers429 Too Many Requests, and sends a query again when the answer is 429, 502, 503 or 504, at most 3 times. - Requests at the same time (
max_concurrent_requests, 1 by default): when rudof gets the nodes of a shape map before it validates them. - Nodes in one query (
max_nodes_per_query, 20 by default; 1 sends a query for each node). - How SHACL gets the data of an endpoint (
[shacl] fetch_strategy, rudof 0.3.30): node by node, when the validation needs it (on-demand, the default), or shape by shape before the validation (by-shape), with the data of many focus nodes in one query: far fewer requests when there are many focus nodes. Withby-shape, nodes in one query (prefetch_batch_size, 50 by default) and the depth of the data fetched before (prefetch_max_rounds, 16 shape references by default, from the shapes with targets; deeper data is fetched when the validation needs it). - How validations get the data of the nodes: SPARQL queries (the
default), or the RDF of the IRIs of the nodes (
dereference, the strategy ofuseEndpoint), which needs servers that allow other sites to read them (CORS), and https IRIs. The SPARQL tab always queries the endpoint.
The endpoints that rudof knows have their own limits in their TOML files
(such as min_request_interval_ms = 1100 of Wikidata), and so can the
endpoints of the settings ([rdf.endpoints.<name>]). The page gives them to
rudof with the description of the endpoint (endpointConfig), so they win
over the settings, as in rudof: Wikidata keeps 1100 ms when the settings say
0. The result shows the limits that apply to each run: the time between two
queries, the requests at the same time and the nodes in one query in the
ShEx and DCTAP tabs; the time between two queries and how SHACL gets the data
in the SHACL tab; the time between two queries in the SPARQL tab (one query).
A limit that the endpoint sets itself is marked so ("1 query every 1.1 s at
most (set by Wikidata)"); the others are those of the settings, or the
defaults of rudof.
SELECT results show in the table (with the prefixes of the query), as JSON or as CSV. The graphs of CONSTRUCT and DESCRIBE can be written in every RDF format.
The endpoint must allow other sites to query it
(CORS), as Wikidata
does, and the page (https) can't query an http: endpoint.
The browser tests run with both builds of rudof: with endpoints (from
endpoints/web/ of the package) and without them. To test another build of
rudof with endpoints, such as one built from the rudof repository, give its
folder in RUDOF_ENDPOINTS_DIR:
RUDOF_ENDPOINTS_DIR=<folder with rudof_wasm.js and rudof_wasm_bg.wasm> npm run test:e2eThe header of the demo and of the help page (header.js) has
Examples (the examples of the
ISWC 2026 tutorial,
and how to write a manifest of your own), Help, and About (the
manual of rudof, rudof and this demo on GitHub, the npm package, and the
versions: the rudof that the page loads, and the version of the demo, in
VERSION, with its commit, which the build takes from
GITHUB_SHA or from git). Examples and About are
<details> menus, which open without JavaScript too.
Next to them, the theme (the sun or the moon): As the system,
Light or Dark. The browser keeps it, and an inline script in the
<head> of the pages applies it b
E870
efore they are drawn. The dark colors are
those of :root[data-theme="dark"] in demo.css, and of the
system when no theme is chosen; a unit test checks that both are the same.
The gear of the header opens the settings of rudof
(settings.js, settings-dialog.js):
the configuration of rudof
that applies to every operation of the page. The dialog has the settings
that change what rudof does in the browser, by sections:
| Section | Settings |
|---|---|
| General | the base IRI, the indentation of JSON and JSON-LD results |
| Endpoints | the time between two requests, the requests at the same time, the nodes in one query, how SHACL gets the data of an endpoint (node by node or shape by shape), and how validations get the data of the nodes (see above) |
| ShEx validation | the maximum number of steps, the negation requirement |
| UML diagrams | direction, lines, shadows, labels for IRIs and the properties of the labels, colors |
| RDF diagrams | the colors of IRIs, blank nodes and literals (RDF data converted to a diagram) |
| SPARQL queries (from ShEx) | the variable of the focus node |
| SHACL to ShEx | target classes as rdf:type, blank-node shapes embedded |
| DCTAP | delimiter, quote, flexible rows, delimiter of value shapes, base of datatypes |
More settings (TOML) takes any other setting, over those of the form,
such as endpoints ([rdf.endpoints.<name>] with name, query_url, a
[prefixmap] and the limits of their requests), which are added to the
Endpoint menus with their prefixes.
The configuration that rudof gets shows the result. Settings that rudof
can't read are not saved, and the dialog says why. The browser keeps the
settings that differ from the defaults (not the permalinks), and the gear
shows how many there are; Reset to defaults forgets them.
The configuration of each operation (the base IRI of the page, the prefixes
of a DCTAP profile, the options of SHACL, the description of an endpoint)
gets the settings over it (configWithSettings), merged as TOML tables with
smol-toml.
Under the RDF data of the RDF, ShEx, SHACL, DCTAP and SPARQL tabs, + Add
RDF data adds another editor of RDF data, with its own format
(extra-data.js). rudof merges all of them (readData with
merge) before it converts, validates or queries them, also with the data
of an endpoint. Remove removes one. Permalinks have them as data-2,
data-2-format, data-3… An example of a manifest, and Use as input
of the RDF tab, replace them.
The results of ShEx, SHACL, DCTAP and PGSchema validations are shown as a
table by default (the Table result format): a row for each node and
shape, with its result and its details. For ShEx, SHACL and DCTAP, the table
is the details text of rudof, read back by
box-table.js. The other formats are as rudof writes them.
The tables of results (validations, SPARQL solutions, CSV results) work as
in a spreadsheet (table-tools.js):
- Clicking the name of a column sorts the rows by it: in ascending order, then descending, then in their order again. Numbers are sorted as numbers.
- The ▾ button next to the name opens the filter of the column: its values (in the rows that the other filters show) with how many rows have each one, a search box, (Select all), the sort orders and Clear filter. The filters of all the columns apply together, the button of a filtered column is marked, and a line under the table says how many rows it shows.
- Dragging the right border of the name of a column changes its width (the
arrow keys too, when the border has the focus). Then the table keeps the
widths of its columns (
table-layout: fixed): it grows, with a scroll bar, or becomes smaller, and the text that doesn't fit is cut. The widths stay for the next table with the same columns (the tab run again, or another result format and back), until the page is reloaded. A double click on a border gives the table its widths again.
The IRIs of the results are links, which open in another tab
(iri-links.js): in the tables of validations, of SPARQL
solutions and of CSV results, a cell wd:Q5685 is a link to
http://www.wikidata.org/entity/Q5685. The prefixed names are read with the
prefixes declared in the inputs of the tab (its data, schema, shape map…),
those of its endpoint, and common prefixes. Only http and https IRIs are
links; literals and blank nodes are text, and sorting and filters read the
text of the cells, as before.
The other tables of the page have these borders too (makeResizable of
table-tools.js): the table view of the DCTAP profile
(whose widths stay when it is drawn again, as the text changes; the column of
the buttons has none) and the table of the queries to an endpoint (drawn again
at each query, with the same widths, also while a border is dragged).
When rudof crashes (a panic, or it runs out of stack), its WebAssembly module can't be used again. The result says what happened, and the page loads a new instance of rudof (the same module, from the cache), so that the tabs can run again without a reload; the status says "loaded again after an error". The results shown before can't be reformatted.
Next to the Validate button of the SHACL tab:
-
Recursion: what rudof does with shapes that refer to themselves, in a cycle (
recursion_semantics, whose values arenone,lfpandgfpsince rudof 0.3.28;cautiousandbraveare their old names):- No recursion refuses a shapes graph with a cycle.
- LFP: Conservative Mode (least fixed point, the default) is false unless proven: a node in a cycle conforms only when this can be shown without assuming that it conforms.
- GFP: Permissive Mode (greatest fixed point) is true unless disproven: a node in a cycle conforms unless something shows that it does not.
The options and the ⓘ next to the menu say this in their tooltips. Permalinks with the old names (
recursion=cautious,recursion=brave) still work. -
Show: the Errors (the violations and other results), the Evidences (why the nodes conform), and whether the evidences are only those of shapes, or also those of each constraint. By default, the errors and the evidences of shapes.
The Shapes graph is optional: when it is empty, rudof takes the shapes
from the RDF data (readShacl without shapes), and the verdict says so.
The same goes for SHACL examples of manifests without schema.
Under the shapes graph, the optional Target declarations are RDF triples
that say which nodes the shapes apply to (such as
:PersonShape sh:targetNode :alice .), in their own format. When they are
not empty, rudof reads the shapes graph (or the RDF data, without a shapes
graph) and the target declarations as RDF data in a session of their own,
merges them, and reads the merged graph as the shapes. The verdict says
"(with the target declarations)". In manifests, SHACL examples give them with
targets or targetsURL, and targetsFormat (Turtle by default, or the
format of the file extension). In permalinks, they are targets and
targets-format.
They are the [shacl] section of the configuration of rudof
(recursion_semantics, store_errors, store_evidences and
evidence_shapes_only). Without the errors, the verdict doesn't count them.
layout.js lets you arrange the boxes of every tab (Data,
Validate and Query):
- Layout menu, next to Permalink: the two boxes of a row side by side or one above the other, in their order or swapped (for example the result left of the data, or the schema above the data). The browser remembers the choice. On narrow screens the boxes are always one above the other.
- Splitter: side by side, drag the line between the two boxes to change their widths, or focus it (Tab) and use the arrow keys. A double click makes the widths equal again. The browser remembers the widths of each tab.
- Heights: drag the handle at the bottom right corner of an editor or a result.
- Full screen: the button at the top right of each editor and each result shows it on the whole window of the browser. The button again, or Esc, gives the normal size back. Ctrl+Enter still runs the tab.
examples/ has examples of every input format (RDF/XML, Turtle,
TriG, N-Triples, N-Quads, N3, JSON-LD, SPARQL, ShExC and ShExJ), to try the
editors with. The tests check that rudof accepts all of them.
The Help page of the demo (help.html) explains how to run
the examples of a manifest and documents the format of manifests, with an
example for each action and validation engine.
The page can list the examples of a manifest, a YAML file such as the one of shex.js, and load the example chosen into the editors:
https://rudof-project.github.io/web-demo/?manifestURL=https://shex.js.org/doc/tests-manifest.yaml
manifest.js reads the manifest (it is loaded, with its YAML
parser, only when the page has a manifestURL). Each entry is an example, in
the vocabulary of shex.js:
- schemaLabel: clinical observation # the examples are grouped by schema
schemaURL: ClinObs.shex # or the schema itself: schema: |
dataLabel: the least an Observation can be
data: | # or dataURL:
<Obs1> :subject <Patient2> .
queryMap: <Obs1>@START # the shape map, or queryMapURL:
status: conformant # or nonconformant
comment: Shown in the list of examplesA manifest can also put its examples in sections, which can have sections
too. An item with section is a section: its title, an optional comment
and examples, a list of examples and sections. A manifest without sections
is a list of examples, as before, and keeps the menu of examples (grouped by
schemaLabel). A manifest with sections shows them as a tree
(parseSections in manifest.js, showSections in demo.js): the titles of
the sections, closed, with the number of their examples; a click on a title
opens the section (<details>), with its comment, and another click closes
it. The examples are numbered in the order of the manifest, depth first, so
&example=<n> still names one, and the demo opens its sections.
- section: Knowledge Graphs
comment: Examples about Knowledge Graphs
examples:
- comment: Tim Berners-Lee in RDF (Turtle)
dataURL: ex01.ttl
action: data
- section: RDF 1.2
examples:
- comment: RDF 1.2 (annotated triples)
dataURL: ex05_annotated.ttl
action: data
- comment: An example out of the sections
dataURL: ex02.ttl
action: dataOptional keys say what to do with an example. Without them, an entry is a ShEx validation, as in the manifests of shex.js:
| Key | Values | Default |
|---|---|---|
action |
data (load the data in the Data tab), validate (validate it in the Validate tab), query (run a query on it in the Query tab), schema (show the schema in the Schema tab, to convert it) |
validate |
dataFormat |
an RDF format (turtle, ntriples, rdfxml, jsonld, trig, n3, nquads), or yars-pg for property graphs |
turtle |
schemaFormat |
shex (ShExC), shexj or json (ShExJ), an RDF format (ShExR, or SHACL shapes; json is JSON-LD for SHACL), pgschema, or csv (DCTAP) |
shex |
validationEngine |
shex, shacl or pgschema |
pgschema for property graphs or pgschema schemas, otherwise shex |
query, queryURL |
the query, for action: query |
|
queryFormat |
sparql |
sparql |
queryEngine |
sparql |
sparql |
endpoint |
the SPARQL endpoint the query or the validation (ShEx, SHACL, DCTAP) runs on, instead of the data (then the data is optional). Validations need a browser with JSPI | |
typeMap, typeMapURL |
the type map of PGSchema (or queryMap) |
|
schemaLanguage (or schemaEngine) |
shex, shacl, pgschema or dctap: the language of the schema. DCTAP profiles go to the DCTAP tab, which validates them with ShEx. With action: schema, the language of the Schema tab |
dctap for CSV schemas (schemaFormat: csv or .csv files) |
prefixes, prefixesURL |
the prefixes of a DCTAP profile, as Turtle declarations (PREFIX, BASE) |
|
picklistSeparator |
space or "|": the separator of the values of picklists in DCTAP |
space |
- dataLabel: Tim Berners Lee in RDF (SPARQL query)
dataURL: ex01.ttl
dataFormat: turtle
queryURL: ex01.sparql
action: query
- dataLabel: Tim Berners Lee as a property graph
dataURL: ex02.pg
dataFormat: yars-pg
action: data
- schemaURL: ex11_researcher.shex
action: schema # opens the schema in the Schema tabURLs are relative to the manifest. Without a format, the format of a file is
taken from its extension (.shex, .json, .ttl, .nt, .jsonld, .pg…),
and the one of inline text is the default (or JSON-LD and ShExJ for text that
starts with {). The list shows the comment of each example (or its
dataLabel, if it has no comment). Choosing an example opens the tab of its
action and loads its inputs; press the button of the tab (Validate,
Run query, Convert) to run it. When an
example is validated as loaded, the verdict says whether the result is the
one of its status. Examples that the demo can't run are listed but can't be
loaded; Why … examples can't be run, below the list, says why, with a
hint to change each entry and the entry as the demo read it. They are
validations on an endpoint in browsers without JSPI, validations that read
their data as they go (neighborhood: of shex.js) without an endpoint,
queries on property graphs, and formats that the tab can't read (such as
TriX).
&example=<n> loads the n-th example (from 1) when the page opens; the URL
gets it when an example is chosen, so it can be shared. Choosing an example
also puts its inputs in the URL (see Permalinks); the
Reload the examples link, next to the list, opens the page again with
only ?manifestURL=….
The manifest and its files are read by the browser, so their server must allow
other sites to read them (CORS);
GitHub Pages and raw.githubusercontent.com do. The tab of each action and
validation engine, and the formats it reads, are in TARGETS of
manifest.js.
After each operation (converting, validating, running a query, changing a result format, choosing a tab or an example), the query of the URL gets the inputs and options of the tab shown, and the fragment names the tab:
?schema=PREFIX%20…&data=PREFIX%20…&shape-map=%3CObs1%3E%40START%0A&schema-format=shexc&…#validate/shex
An input given by a URL is its URL, in the parameter of the input with
-url (schema-url=es:E371). The Permalink button writes the inputs as they are now in the URL, and
copies it. Opening the URL puts the inputs and options back in their tab.
permalink.js has the parameters of each tab:
| Tab | Parameters |
|---|---|
| Convert (Schema) | schema-language, schema-format, schema, prefixes, picklist, to, to-format |
| RDF | data, data-format, result-format |
| Property graph | data, result-format |
| ShEx | schema, data, shape-map, endpoint, endpoint-data, schema-format, data-format, result-format, schema-result-format |
| SHACL | shapes, data, endpoint, endpoint-data, shapes-format, data-format, mode, recursion, errors, evidences, evidences-shapes, result-format, schema-result-format |
| DCTAP | dctap, prefixes, picklist, data, endpoint, endpoint-data, shape-map, data-format, engine, result-format, schema-result-format |
| PGSchema | schema, data, type-map, result-format |
| SPARQL | data, data-format, query, endpoint, endpoint-data, result-format |
The RDF data added to a tab are data-2, data-2-format… (see More RDF data). Checkboxes (such as errors of SHACL) are true or false. manifestURL and example are kept. A URL without a fragment opens the tab
of its parameters (ShEx for shape-map or schema), so links in the style of
shex.js (?schema=…&data=…&shape-map=…) open the ShEx tab. When a URL has an
example and inputs too, the inputs of the URL are used: they are the example
as it was edited.
Servers refuse URLs that are too long (414 URI Too Long), and the inputs
can be long (the schemas of Generate are up to 9,000 characters, 13,500 in a
URL). When the query of a permalink is longer than 2,000 characters, its
inputs go in one parameter, z: their query, compressed with deflate
(fflate) and written in base64url,
about ten times shorter. manifestURL and example stay as they are.
Opening the URL expands them; a link that was cut, and can't be expanded,
opens the page without its inputs.
The inputs are sent to the server of the page with the URL, and servers limit the length of URLs (often to about 8 KB), so very long inputs may not fit in a permalink.
npm test # unit tests (Vitest, with jsdom)
npm run test:e2e # the built page in Chromium (Playwright)The browser tests build the page and serve it with npm run preview. rudof is
served to the page from node_modules, so they don't need the network. The
first time, install Chromium with:
npx playwright install --with-deps chromiumor use a Chromium you already have:
CHROMIUM_PATH=/path/to/chromium npm run test:e2edemo.js loads the rudof version of package.json (the one the tests use),
so updating rudof is only a change of package.json and package-lock.json
(npm install --save-dev --save-exact @rudof/rudof@<version>).
The GitHub Actions workflow runs the tests
and builds the page on every push and pull request, and publishes dist/ to
the gh-pages branch on every push to main.
update-rudof.yml updates @rudof/rudof
to its latest release on npm once a day (or when run by hand from the Actions
tab): it tests the update, pushes it to main and publishes the page. To
update the page as soon as rudof is released, rudof's release workflow can
send it a repository_dispatch, once the package is on npm:
- uses: peter-evans/repository-dispatch@v3
with:
token: ${{ secrets.WEB_DEMO_TOKEN }} # may write to rudof-project/web-demo
repository: rudof-project/web-demo
event-type: rudof-released