@unrdf/kgn 5.0.1 → 26.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/index.mjs +9207 -0
  2. package/package.json +33 -28
  3. package/src/base/filter-templates.js +7 -1
  4. package/src/base/index.js +15 -10
  5. package/src/base/injection-targets.js +6 -0
  6. package/src/base/macro-templates.js +6 -0
  7. package/src/base/shacl-templates.js +6 -0
  8. package/src/base/template-base.js +7 -1
  9. package/src/core/attestor.js +50 -1
  10. package/src/core/filters.js +134 -1
  11. package/src/core/index.js +8 -1
  12. package/src/core/kgen-engine.js +49 -1
  13. package/src/core/parser.js +52 -1
  14. package/src/core/post-processor.js +7 -1
  15. package/src/core/renderer.js +67 -1
  16. package/src/doc-generator/mdx-generator.mjs +1 -1
  17. package/src/doc-generator/nav-generator.mjs +1 -1
  18. package/src/doc-generator/rdf-builder.mjs +2 -2
  19. package/src/engine/index.js +9 -0
  20. package/src/engine/pipeline.js +7 -1
  21. package/src/engine/renderer.js +18 -3
  22. package/src/engine/template-engine.js +12 -3
  23. package/src/filters/array.js +14 -6
  24. package/src/filters/index.js +165 -17
  25. package/src/filters/rdf.js +3 -3
  26. package/src/{index.js → index.mjs} +46 -0
  27. package/src/index.test.mjs +40 -0
  28. package/src/inheritance/index.js +19 -1
  29. package/src/injection/atomic-writer.js +22 -1
  30. package/src/injection/idempotency-manager.js +33 -0
  31. package/src/injection/injection-engine.js +46 -1
  32. package/src/injection/integration.js +3 -3
  33. package/src/injection/modes/index.js +30 -0
  34. package/src/injection/rollback-manager.js +26 -2
  35. package/src/injection/target-resolver.js +48 -3
  36. package/src/injection/tests/injection-engine.test.js +3 -3
  37. package/src/injection/tests/integration.test.js +2 -1
  38. package/src/injection/tests/run-tests.js +3 -0
  39. package/src/injection/validation-engine.js +71 -5
  40. package/src/linter/determinism-linter.js +20 -5
  41. package/src/linter/determinism.js +8 -2
  42. package/src/linter/index.js +3 -1
  43. package/src/linter/test-doubles.js +151 -4
  44. package/src/parser/frontmatter.js +6 -0
  45. package/src/parser/variables.js +7 -1
  46. package/src/rdf/filters.js +393 -0
  47. package/src/rdf/index.js +444 -0
  48. package/src/renderer/deterministic.js +6 -0
  49. package/src/renderer/index.js +3 -1
  50. package/src/templates/rdf/DELIVERY-SUMMARY.md +266 -0
  51. package/src/templates/rdf/README.md +595 -0
  52. package/src/templates/rdf/dataset.njk +83 -0
  53. package/src/templates/rdf/index.js +106 -0
  54. package/src/templates/rdf/jsonld-context.njk +63 -0
  55. package/src/templates/rdf/ontology.njk +107 -0
  56. package/src/templates/rdf/schema.njk +79 -0
  57. package/src/templates/rdf/shapes.njk +89 -0
  58. package/src/templates/rdf/sparql-queries.njk +70 -0
  59. package/src/templates/rdf/vocabulary.njk +79 -0
@@ -0,0 +1,595 @@
1
+ # RDF Templates for @unrdf/kgn
2
+
3
+ Comprehensive Nunjucks templates for generating RDF/Semantic Web content with frontmatter-driven configuration.
4
+
5
+ ## Overview
6
+
7
+ This template library provides production-ready templates for common RDF formats and vocabularies:
8
+
9
+ - **ontology.njk** - OWL ontologies with classes and properties
10
+ - **schema.njk** - RDFS vocabularies and schemas
11
+ - **dataset.njk** - DCAT dataset metadata
12
+ - **vocabulary.njk** - SKOS concept schemes and taxonomies
13
+ - **shapes.njk** - SHACL validation shapes
14
+ - **sparql-queries.njk** - SPARQL query collections
15
+ - **jsonld-context.njk** - JSON-LD context mappings
16
+
17
+ All templates:
18
+ - Accept structured data via YAML frontmatter
19
+ - Generate valid RDF/Turtle or JSON-LD output
20
+ - Include comprehensive prefix declarations
21
+ - Support customization through variables
22
+ - Include inline documentation and examples
23
+
24
+ ## Usage
25
+
26
+ ### Basic Pattern
27
+
28
+ ```javascript
29
+ import { createEngine } from '@unrdf/kgn';
30
+
31
+ const engine = createEngine({
32
+ templatePaths: ['./node_modules/@unrdf/kgn/src/templates/rdf']
33
+ });
34
+
35
+ const output = await engine.render('ontology.njk', {
36
+ ontologyIRI: 'http://example.org/myonto',
37
+ title: 'My Ontology',
38
+ classes: [
39
+ {
40
+ iri: 'http://example.org/myonto#Person',
41
+ label: 'Person',
42
+ comment: 'Represents a person'
43
+ }
44
+ ]
45
+ });
46
+ ```
47
+
48
+ ### With Frontmatter Files
49
+
50
+ Create a `.njk` file with frontmatter:
51
+
52
+ ```yaml
53
+ ---
54
+ ontologyIRI: http://example.org/ontology/library
55
+ version: 1.0.0
56
+ title: Library Ontology
57
+ description: An ontology for library systems
58
+ creator: http://example.org/people/librarian
59
+ created: 2026-01-11
60
+ classes:
61
+ - iri: http://example.org/ontology/library#Book
62
+ label: Book
63
+ comment: Represents a book in the library
64
+ - iri: http://example.org/ontology/library#Author
65
+ label: Author
66
+ comment: Represents an author
67
+ objectProperties:
68
+ - iri: http://example.org/ontology/library#writtenBy
69
+ label: written by
70
+ comment: Links a book to its author
71
+ domain: http://example.org/ontology/library#Book
72
+ range: http://example.org/ontology/library#Author
73
+ ---
74
+ ```
75
+
76
+ Then render:
77
+
78
+ ```javascript
79
+ const output = await engine.renderFile('my-ontology.njk');
80
+ ```
81
+
82
+ ## Template Reference
83
+
84
+ ### 1. ontology.njk - OWL Ontology
85
+
86
+ Generates complete OWL ontologies with metadata, classes, object properties, and datatype properties.
87
+
88
+ **Required Variables:**
89
+ - `ontologyIRI` (string) - The IRI of the ontology
90
+ - `title` (string) - Ontology title
91
+
92
+ **Optional Variables:**
93
+ - `version` (string) - Version string (e.g., "1.0.0")
94
+ - `description` (string) - Ontology description
95
+ - `creator` (string) - Creator IRI
96
+ - `created` (string) - Creation date (ISO format)
97
+ - `license` (string) - License IRI
98
+ - `prefix` (string) - Short prefix for the ontology namespace
99
+ - `classes` (array) - Class definitions
100
+ - `iri` - Class IRI
101
+ - `label` - rdfs:label
102
+ - `comment` - rdfs:comment (optional)
103
+ - `subClassOf` - Parent class IRI (optional)
104
+ - `objectProperties` (array) - Object property definitions
105
+ - `iri` - Property IRI
106
+ - `label` - rdfs:label
107
+ - `comment` - rdfs:comment (optional)
108
+ - `domain` - Domain class IRI (optional)
109
+ - `range` - Range class IRI (optional)
110
+ - `datatypeProperties` (array) - Datatype property definitions
111
+ - `iri` - Property IRI
112
+ - `label` - rdfs:label
113
+ - `comment` - rdfs:comment (optional)
114
+ - `domain` - Domain class IRI (optional)
115
+ - `range` - XSD datatype IRI (optional)
116
+
117
+ **Example Output:**
118
+
119
+ ```turtle
120
+ @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
121
+ @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
122
+ @prefix owl: <http://www.w3.org/2002/07/owl#> .
123
+
124
+ <http://example.org/ontology/myonto> a owl:Ontology ;
125
+ dc:title "My Example Ontology"@en ;
126
+ owl:versionInfo "1.0.0" .
127
+
128
+ <http://example.org/ontology/myonto#Person> a owl:Class ;
129
+ rdfs:label "Person"@en ;
130
+ rdfs:isDefinedBy <http://example.org/ontology/myonto> .
131
+ ```
132
+
133
+ ### 2. schema.njk - RDFS Schema
134
+
135
+ Generates RDFS vocabularies with classes and properties.
136
+
137
+ **Required Variables:**
138
+ - `schemaIRI` (string) - The IRI of the schema
139
+ - `title` (string) - Schema title
140
+
141
+ **Optional Variables:**
142
+ - `description` (string) - Schema description
143
+ - `version` (string) - Version string
144
+ - `prefix` (string) - Short prefix
145
+ - `classes` (array) - RDFS class definitions
146
+ - `iri` - Class IRI
147
+ - `label` - rdfs:label
148
+ - `comment` - rdfs:comment (optional)
149
+ - `subClassOf` - Parent class IRI (optional)
150
+ - `properties` (array) - RDF property definitions
151
+ - `iri` - Property IRI
152
+ - `label` - rdfs:label
153
+ - `comment` - rdfs:comment (optional)
154
+ - `domain` - Domain class IRI (optional)
155
+ - `range` - Range class/datatype IRI (optional)
156
+ - `subPropertyOf` - Parent property IRI (optional)
157
+
158
+ **Example:**
159
+
160
+ ```javascript
161
+ const schema = await engine.render('schema.njk', {
162
+ schemaIRI: 'http://example.org/schema/vocab',
163
+ title: 'Document Vocabulary',
164
+ classes: [
165
+ {
166
+ iri: 'http://example.org/schema/vocab#Document',
167
+ label: 'Document',
168
+ comment: 'A textual document'
169
+ }
170
+ ],
171
+ properties: [
172
+ {
173
+ iri: 'http://example.org/schema/vocab#title',
174
+ label: 'title',
175
+ domain: 'http://example.org/schema/vocab#Document',
176
+ range: 'http://www.w3.org/2001/XMLSchema#string'
177
+ }
178
+ ]
179
+ });
180
+ ```
181
+
182
+ ### 3. dataset.njk - DCAT Dataset
183
+
184
+ Generates DCAT dataset descriptions for data catalogs.
185
+
186
+ **Required Variables:**
187
+ - `datasetIRI` (string) - The IRI of the dataset
188
+ - `title` (string) - Dataset title
189
+ - `description` (string) - Dataset description
190
+
191
+ **Optional Variables:**
192
+ - `publisher` (string) - Publisher IRI
193
+ - `creator` (string) - Creator IRI
194
+ - `issued` (string) - Issue date (ISO format)
195
+ - `modified` (string) - Modification date (ISO format)
196
+ - `license` (string) - License IRI
197
+ - `keywords` (array of strings) - Keywords
198
+ - `theme` (array of strings) - Theme IRIs
199
+ - `spatial` (string) - Spatial coverage IRI
200
+ - `temporal` (string) - Temporal coverage description
201
+ - `distributions` (array) - Distribution objects
202
+ - `iri` - Distribution IRI
203
+ - `title` - Distribution title
204
+ - `format` - Media type (e.g., "text/turtle")
205
+ - `accessURL` - Access URL
206
+ - `downloadURL` - Download URL (optional)
207
+
208
+ **Example:**
209
+
210
+ ```javascript
211
+ const dataset = await engine.render('dataset.njk', {
212
+ datasetIRI: 'http://example.org/datasets/census-2020',
213
+ title: 'Census Data 2020',
214
+ description: 'Population census data for 2020',
215
+ license: 'http://creativecommons.org/licenses/by/4.0/',
216
+ keywords: ['census', 'population', 'statistics'],
217
+ distributions: [
218
+ {
219
+ iri: 'http://example.org/datasets/census-2020/csv',
220
+ title: 'CSV Distribution',
221
+ format: 'text/csv',
222
+ accessURL: 'http://example.org/data/census-2020.csv'
223
+ }
224
+ ]
225
+ });
226
+ ```
227
+
228
+ ### 4. vocabulary.njk - SKOS Vocabulary
229
+
230
+ Generates SKOS concept schemes for taxonomies and controlled vocabularies.
231
+
232
+ **Required Variables:**
233
+ - `schemeIRI` (string) - The IRI of the concept scheme
234
+ - `title` (string) - Scheme title
235
+
236
+ **Optional Variables:**
237
+ - `description` (string) - Scheme description
238
+ - `creator` (string) - Creator IRI
239
+ - `created` (string) - Creation date (ISO format)
240
+ - `prefix` (string) - Short prefix
241
+ - `concepts` (array) - Concept definitions
242
+ - `iri` - Concept IRI
243
+ - `prefLabel` - Preferred label
244
+ - `altLabel` (array of strings) - Alternative labels (optional)
245
+ - `definition` - Definition text (optional)
246
+ - `notation` - Notation string (optional)
247
+ - `broader` - Broader concept IRI (optional)
248
+ - `narrower` (array of strings) - Narrower concept IRIs (optional)
249
+ - `related` (array of strings) - Related concept IRIs (optional)
250
+
251
+ **Example:**
252
+
253
+ ```javascript
254
+ const vocab = await engine.render('vocabulary.njk', {
255
+ schemeIRI: 'http://example.org/vocab/topics',
256
+ title: 'Topic Vocabulary',
257
+ concepts: [
258
+ {
259
+ iri: 'http://example.org/vocab/topics#Science',
260
+ prefLabel: 'Science',
261
+ definition: 'Natural and formal sciences',
262
+ narrower: [
263
+ 'http://example.org/vocab/topics#Physics',
264
+ 'http://example.org/vocab/topics#Biology'
265
+ ]
266
+ }
267
+ ]
268
+ });
269
+ ```
270
+
271
+ ### 5. shapes.njk - SHACL Shapes
272
+
273
+ Generates SHACL shapes for RDF data validation.
274
+
275
+ **Required Variables:**
276
+ - `shapesGraphIRI` (string) - The IRI of the shapes graph
277
+ - `shapes` (array) - Shape definitions
278
+
279
+ **Optional Variables:**
280
+ - `title` (string) - Shapes graph title
281
+ - `description` (string) - Description
282
+ - `prefix` (string) - Short prefix
283
+
284
+ **Shape Object:**
285
+ - `iri` - Shape IRI
286
+ - `targetClass` - Target class IRI (optional)
287
+ - `targetNode` - Target node IRI (optional)
288
+ - `label` - Shape label (optional)
289
+ - `description` - Shape description (optional)
290
+ - `closed` (boolean) - Whether shape is closed (optional)
291
+ - `properties` (array) - Property constraints
292
+ - `path` - Property path IRI
293
+ - `minCount` (number) - Minimum cardinality (optional)
294
+ - `maxCount` (number) - Maximum cardinality (optional)
295
+ - `datatype` - Datatype IRI (optional)
296
+ - `class` - Class IRI (optional)
297
+ - `pattern` - Regex pattern (optional)
298
+ - `minLength` (number) - Min string length (optional)
299
+ - `maxLength` (number) - Max string length (optional)
300
+ - `minInclusive` (number) - Min numeric value (optional)
301
+ - `maxInclusive` (number) - Max numeric value (optional)
302
+
303
+ **Example:**
304
+
305
+ ```javascript
306
+ const shapes = await engine.render('shapes.njk', {
307
+ shapesGraphIRI: 'http://example.org/shapes/person',
308
+ title: 'Person Shapes',
309
+ shapes: [
310
+ {
311
+ iri: 'http://example.org/shapes/person#PersonShape',
312
+ targetClass: 'http://xmlns.com/foaf/0.1/Person',
313
+ properties: [
314
+ {
315
+ path: 'http://xmlns.com/foaf/0.1/name',
316
+ minCount: 1,
317
+ maxCount: 1,
318
+ datatype: 'http://www.w3.org/2001/XMLSchema#string'
319
+ }
320
+ ]
321
+ }
322
+ ]
323
+ });
324
+ ```
325
+
326
+ ### 6. sparql-queries.njk - SPARQL Queries
327
+
328
+ Generates collections of SPARQL queries with metadata and comments.
329
+
330
+ **Required Variables:**
331
+ - `title` (string) - Query collection title
332
+ - `queries` (array) - Query definitions
333
+
334
+ **Optional Variables:**
335
+ - `description` (string) - Collection description
336
+
337
+ **Query Object:**
338
+ - `name` - Query name/identifier
339
+ - `description` - Query description
340
+ - `type` - Query type: "SELECT", "CONSTRUCT", "ASK", "DESCRIBE"
341
+ - `prefixes` (object) - Prefix mappings (e.g., `{foaf: 'http://xmlns.com/foaf/0.1/'}`)
342
+ - `variables` (array of strings) - Variable names for SELECT (optional)
343
+ - `where` (array of strings) - WHERE clause patterns
344
+ - `construct` (array of strings) - CONSTRUCT patterns (for CONSTRUCT queries)
345
+ - `resources` (array of strings) - Resources for DESCRIBE (optional)
346
+ - `orderBy` (array of strings) - ORDER BY variables (optional)
347
+ - `limit` (number) - LIMIT value (optional)
348
+ - `offset` (number) - OFFSET value (optional)
349
+
350
+ **Example:**
351
+
352
+ ```javascript
353
+ const queries = await engine.render('sparql-queries.njk', {
354
+ title: 'Person Queries',
355
+ queries: [
356
+ {
357
+ name: 'getAllPersons',
358
+ description: 'Get all persons with names',
359
+ type: 'SELECT',
360
+ prefixes: {
361
+ foaf: 'http://xmlns.com/foaf/0.1/'
362
+ },
363
+ variables: ['person', 'name'],
364
+ where: [
365
+ '?person a foaf:Person',
366
+ '?person foaf:name ?name'
367
+ ],
368
+ orderBy: ['name'],
369
+ limit: 100
370
+ }
371
+ ]
372
+ });
373
+ ```
374
+
375
+ ### 7. jsonld-context.njk - JSON-LD Context
376
+
377
+ Generates JSON-LD context files for mapping JSON to RDF.
378
+
379
+ **Required Variables:**
380
+ - `terms` (object) - Term mappings
381
+
382
+ **Optional Variables:**
383
+ - `contextIRI` (string) - Context IRI (for @id)
384
+ - `vocab` (string) - Default vocabulary IRI (for @vocab)
385
+ - `base` (string) - Base IRI (for @base)
386
+ - `prefixes` (object) - Prefix mappings
387
+
388
+ **Terms Object:**
389
+ - Simple mapping: `termName: "iri"`
390
+ - Complex mapping: `termName: { id: "iri", type: "@id|@vocab|xsd:type", container: "@list|@set", ... }`
391
+
392
+ **Example:**
393
+
394
+ ```javascript
395
+ const context = await engine.render('jsonld-context.njk', {
396
+ contextIRI: 'http://example.org/contexts/person',
397
+ vocab: 'http://schema.org/',
398
+ prefixes: {
399
+ foaf: 'http://xmlns.com/foaf/0.1/',
400
+ xsd: 'http://www.w3.org/2001/XMLSchema#'
401
+ },
402
+ terms: {
403
+ Person: 'foaf:Person',
404
+ name: 'foaf:name',
405
+ email: {
406
+ id: 'foaf:mbox',
407
+ type: '@id'
408
+ },
409
+ age: {
410
+ id: 'foaf:age',
411
+ type: 'xsd:integer'
412
+ }
413
+ }
414
+ });
415
+ ```
416
+
417
+ ## Best Practices
418
+
419
+ ### 1. Use Consistent IRI Patterns
420
+
421
+ ```yaml
422
+ # Good - consistent namespace
423
+ ontologyIRI: http://example.org/ontology/library
424
+ classes:
425
+ - iri: http://example.org/ontology/library#Book
426
+ - iri: http://example.org/ontology/library#Author
427
+
428
+ # Avoid - mixed namespaces without justification
429
+ classes:
430
+ - iri: http://example.org/Book
431
+ - iri: http://another.org/Author
432
+ ```
433
+
434
+ ### 2. Provide Comprehensive Metadata
435
+
436
+ ```yaml
437
+ # Good - complete metadata
438
+ ontologyIRI: http://example.org/onto
439
+ version: 1.0.0
440
+ title: Example Ontology
441
+ description: Comprehensive description of the ontology purpose
442
+ creator: http://example.org/people/john-doe
443
+ created: 2026-01-11
444
+ license: http://creativecommons.org/licenses/by/4.0/
445
+ ```
446
+
447
+ ### 3. Use Labels and Comments
448
+
449
+ ```yaml
450
+ # Good - human-readable annotations
451
+ classes:
452
+ - iri: http://example.org/onto#Person
453
+ label: Person
454
+ comment: Represents a human being with social and legal rights
455
+ ```
456
+
457
+ ### 4. Leverage Template Inheritance
458
+
459
+ For complex ontologies, compose from multiple templates:
460
+
461
+ ```javascript
462
+ // Generate base classes
463
+ const baseClasses = await engine.render('ontology.njk', baseClassesData);
464
+
465
+ // Generate specialized properties
466
+ const properties = await engine.render('ontology.njk', propertiesData);
467
+
468
+ // Combine outputs
469
+ const combined = baseClasses + '\n\n' + properties;
470
+ ```
471
+
472
+ ### 5. Validate Generated RDF
473
+
474
+ ```javascript
475
+ import { Parser } from 'n3';
476
+
477
+ const parser = new Parser({ format: 'text/turtle' });
478
+ const quads = parser.parse(generatedTurtle);
479
+
480
+ // Validates syntax and structure
481
+ if (quads.length > 0) {
482
+ console.log('Valid RDF generated:', quads.length, 'triples');
483
+ }
484
+ ```
485
+
486
+ ### 6. Use Prefixes for Readability
487
+
488
+ ```yaml
489
+ # Define reusable prefix
490
+ prefix: lib
491
+
492
+ # Results in cleaner output:
493
+ # @prefix lib: <http://example.org/ontology/library#> .
494
+ # lib:Book a owl:Class .
495
+ ```
496
+
497
+ ## Integration Examples
498
+
499
+ ### With @unrdf/core
500
+
501
+ ```javascript
502
+ import { createEngine } from '@unrdf/kgn';
503
+ import { createStore, loadTurtle } from '@unrdf/core';
504
+
505
+ const engine = createEngine({
506
+ templatePaths: ['./node_modules/@unrdf/kgn/src/templates/rdf']
507
+ });
508
+
509
+ // Generate ontology
510
+ const turtle = await engine.render('ontology.njk', ontologyData);
511
+
512
+ // Load into RDF store
513
+ const store = createStore();
514
+ await loadTurtle(store, turtle);
515
+
516
+ // Query the generated ontology
517
+ const classes = store.getQuads(null, RDF.type, OWL.Class);
518
+ console.log('Generated classes:', classes.length);
519
+ ```
520
+
521
+ ### Batch Generation
522
+
523
+ ```javascript
524
+ const templates = [
525
+ { template: 'ontology.njk', data: ontologyData, output: 'onto.ttl' },
526
+ { template: 'shapes.njk', data: shapesData, output: 'shapes.ttl' },
527
+ { template: 'dataset.njk', data: datasetData, output: 'metadata.ttl' }
528
+ ];
529
+
530
+ for (const { template, data, output } of templates) {
531
+ const result = await engine.render(template, data);
532
+ await fs.writeFile(output, result, 'utf-8');
533
+ }
534
+ ```
535
+
536
+ ### Dynamic Frontmatter
537
+
538
+ ```javascript
539
+ import matter from 'gray-matter';
540
+
541
+ // Load template with frontmatter
542
+ const templateContent = await fs.readFile('my-ontology.njk', 'utf-8');
543
+ const { data, content } = matter(templateContent);
544
+
545
+ // Modify frontmatter programmatically
546
+ data.version = '2.0.0';
547
+ data.modified = new Date().toISOString().split('T')[0];
548
+
549
+ // Render with updated data
550
+ const result = await engine.render('ontology.njk', data);
551
+ ```
552
+
553
+ ## Troubleshooting
554
+
555
+ ### Common Issues
556
+
557
+ **Empty output:**
558
+ - Check that all required variables are provided
559
+ - Verify frontmatter YAML is valid
560
+ - Ensure template path is correct
561
+
562
+ **Invalid RDF:**
563
+ - Validate IRIs are absolute (http://, https://)
564
+ - Check for proper string escaping in labels/comments
565
+ - Ensure array variables are actual arrays, not strings
566
+
567
+ **Missing prefixes:**
568
+ - Add prefixes to `prefixes` object or template header
569
+ - Verify namespace IRIs end with # or /
570
+
571
+ ### Debugging
572
+
573
+ Enable template debugging:
574
+
575
+ ```javascript
576
+ const engine = createEngine({
577
+ templatePaths: ['./node_modules/@unrdf/kgn/src/templates/rdf'],
578
+ debug: true
579
+ });
580
+ ```
581
+
582
+ Validate template syntax:
583
+
584
+ ```javascript
585
+ import { lintTemplate } from '@unrdf/kgn';
586
+
587
+ const issues = await lintTemplate('ontology.njk');
588
+ if (issues.length > 0) {
589
+ console.error('Template issues:', issues);
590
+ }
591
+ ```
592
+
593
+ ## License
594
+
595
+ MIT License - see @unrdf/kgn package for details.
@@ -0,0 +1,83 @@
1
+ {#
2
+ DCAT Dataset Template
3
+
4
+ Generates a DCAT (Data Catalog Vocabulary) dataset description.
5
+
6
+ Expected frontmatter variables:
7
+ - datasetIRI: The IRI of the dataset (required)
8
+ - title: Dataset title (required)
9
+ - description: Dataset description (required)
10
+ - publisher: Publisher name/IRI (optional)
11
+ - creator: Creator name/IRI (optional)
12
+ - issued: Issue date (ISO format) (optional)
13
+ - modified: Modification date (ISO format) (optional)
14
+ - license: License IRI (optional)
15
+ - keywords: Array of keyword strings (optional)
16
+ - theme: Array of theme IRIs (optional)
17
+ - distributions: Array of distribution objects (optional)
18
+ - iri: Distribution IRI
19
+ - title: Distribution title
20
+ - format: Format (e.g., "text/turtle", "application/json")
21
+ - accessURL: Access URL
22
+ - downloadURL: Download URL (optional)
23
+ - spatial: Spatial coverage IRI (optional)
24
+ - temporal: Temporal coverage description (optional)
25
+
26
+ Example:
27
+ ---
28
+ datasetIRI: http://example.org/datasets/sample-data
29
+ title: Sample Dataset
30
+ description: A comprehensive sample dataset for testing
31
+ publisher: http://example.org/organizations/acme
32
+ issued: 2026-01-11
33
+ license: http://creativecommons.org/licenses/by/4.0/
34
+ keywords:
35
+ - sample
36
+ - test
37
+ - rdf
38
+ distributions:
39
+ - iri: http://example.org/datasets/sample-data/dist/turtle
40
+ title: Turtle Distribution
41
+ format: text/turtle
42
+ accessURL: http://example.org/datasets/sample-data.ttl
43
+ ---
44
+ #}
45
+ @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
46
+ @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
47
+ @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
48
+ @prefix dcat: <http://www.w3.org/ns/dcat#> .
49
+ @prefix dcterms: <http://purl.org/dc/terms/> .
50
+ @prefix foaf: <http://xmlns.com/foaf/0.1/> .
51
+ @prefix vcard: <http://www.w3.org/2006/vcard/ns#> .
52
+
53
+ # Dataset Description
54
+ <{{ datasetIRI }}> a dcat:Dataset ;
55
+ dcterms:title "{{ title }}"@en ;
56
+ dcterms:description "{{ description }}"@en ;
57
+ {% if publisher %} dcterms:publisher <{{ publisher }}> ;{% endif %}
58
+ {% if creator %} dcterms:creator <{{ creator }}> ;{% endif %}
59
+ {% if issued %} dcterms:issued "{{ issued }}"^^xsd:date ;{% endif %}
60
+ {% if modified %} dcterms:modified "{{ modified }}"^^xsd:date ;{% else %} dcterms:modified "{{ now | date('YYYY-MM-DD') }}"^^xsd:date ;{% endif %}
61
+ {% if license %} dcterms:license <{{ license }}> ;{% endif %}
62
+ {% if spatial %} dcterms:spatial <{{ spatial }}> ;{% endif %}
63
+ {% if temporal %} dcterms:temporal "{{ temporal }}"@en ;{% endif %}
64
+ {% if keywords and keywords.length > 0 %}{% for keyword in keywords %} dcat:keyword "{{ keyword }}"@en ;
65
+ {% endfor %}{% endif %}
66
+ {% if theme and theme.length > 0 %}{% for t in theme %} dcat:theme <{{ t }}> ;
67
+ {% endfor %}{% endif %}
68
+ {% if distributions and distributions.length > 0 %}{% for dist in distributions %} dcat:distribution <{{ dist.iri }}> ;
69
+ {% endfor %}{% endif %}
70
+ .
71
+
72
+ {% if distributions and distributions.length > 0 %}
73
+ # Distributions
74
+ {% for dist in distributions %}
75
+ <{{ dist.iri }}> a dcat:Distribution ;
76
+ dcterms:title "{{ dist.title }}"@en ;
77
+ {% if dist.format %} dcat:mediaType "{{ dist.format }}" ;{% endif %}
78
+ {% if dist.accessURL %} dcat:accessURL <{{ dist.accessURL }}> ;{% endif %}
79
+ {% if dist.downloadURL %} dcat:downloadURL <{{ dist.downloadURL }}> ;{% endif %}
80
+ dcterms:format "{{ dist.format }}" .
81
+
82
+ {% endfor %}
83
+ {% endif %}