@unrdf/kgn 5.0.1

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 (68) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +210 -0
  3. package/package.json +90 -0
  4. package/src/MIGRATION_COMPLETE.md +186 -0
  5. package/src/PORT-MAP.md +302 -0
  6. package/src/base/filter-templates.js +479 -0
  7. package/src/base/index.js +92 -0
  8. package/src/base/injection-targets.js +583 -0
  9. package/src/base/macro-templates.js +298 -0
  10. package/src/base/macro-templates.js.bak +461 -0
  11. package/src/base/shacl-templates.js +617 -0
  12. package/src/base/template-base.js +388 -0
  13. package/src/core/attestor.js +381 -0
  14. package/src/core/filters.js +518 -0
  15. package/src/core/index.js +21 -0
  16. package/src/core/kgen-engine.js +372 -0
  17. package/src/core/parser.js +447 -0
  18. package/src/core/post-processor.js +313 -0
  19. package/src/core/renderer.js +469 -0
  20. package/src/doc-generator/cli.mjs +122 -0
  21. package/src/doc-generator/index.mjs +28 -0
  22. package/src/doc-generator/mdx-generator.mjs +71 -0
  23. package/src/doc-generator/nav-generator.mjs +136 -0
  24. package/src/doc-generator/parser.mjs +291 -0
  25. package/src/doc-generator/rdf-builder.mjs +306 -0
  26. package/src/doc-generator/scanner.mjs +189 -0
  27. package/src/engine/index.js +42 -0
  28. package/src/engine/pipeline.js +448 -0
  29. package/src/engine/renderer.js +604 -0
  30. package/src/engine/template-engine.js +566 -0
  31. package/src/filters/array.js +436 -0
  32. package/src/filters/data.js +479 -0
  33. package/src/filters/index.js +270 -0
  34. package/src/filters/rdf.js +264 -0
  35. package/src/filters/text.js +369 -0
  36. package/src/index.js +109 -0
  37. package/src/inheritance/index.js +40 -0
  38. package/src/injection/api.js +260 -0
  39. package/src/injection/atomic-writer.js +327 -0
  40. package/src/injection/constants.js +136 -0
  41. package/src/injection/idempotency-manager.js +295 -0
  42. package/src/injection/index.js +28 -0
  43. package/src/injection/injection-engine.js +378 -0
  44. package/src/injection/integration.js +339 -0
  45. package/src/injection/modes/index.js +341 -0
  46. package/src/injection/rollback-manager.js +373 -0
  47. package/src/injection/target-resolver.js +323 -0
  48. package/src/injection/tests/atomic-writer.test.js +382 -0
  49. package/src/injection/tests/injection-engine.test.js +611 -0
  50. package/src/injection/tests/integration.test.js +392 -0
  51. package/src/injection/tests/run-tests.js +283 -0
  52. package/src/injection/validation-engine.js +547 -0
  53. package/src/linter/determinism-linter.js +473 -0
  54. package/src/linter/determinism.js +410 -0
  55. package/src/linter/index.js +6 -0
  56. package/src/linter/test-doubles.js +475 -0
  57. package/src/parser/frontmatter.js +228 -0
  58. package/src/parser/variables.js +344 -0
  59. package/src/renderer/deterministic.js +245 -0
  60. package/src/renderer/index.js +6 -0
  61. package/src/templates/latex/academic-paper.njk +186 -0
  62. package/src/templates/latex/index.js +104 -0
  63. package/src/templates/nextjs/app-page.njk +66 -0
  64. package/src/templates/nextjs/index.js +80 -0
  65. package/src/templates/office/docx/document.njk +368 -0
  66. package/src/templates/office/index.js +79 -0
  67. package/src/templates/office/word-report.njk +129 -0
  68. package/src/utils/template-utils.js +426 -0
@@ -0,0 +1,617 @@
1
+ /**
2
+ * KGEN SHACL Templates - RDF validation shape templates
3
+ *
4
+ * Provides templates for creating SHACL (Shapes Constraint Language) shapes
5
+ * for validating RDF knowledge graphs used in KGEN template generation
6
+ */
7
+
8
+ export class KGenSHACLTemplates {
9
+ constructor(options = {}) {
10
+ this.options = {
11
+ deterministicMode: options.deterministicMode !== false,
12
+ staticBuildTime: options.staticBuildTime || '2024-01-01T00:00:00.000Z',
13
+ namespace: options.namespace || 'http://kgen.ai/shacl#',
14
+ baseIRI: options.baseIRI || 'http://kgen.ai/',
15
+ ...options
16
+ };
17
+
18
+ this.shapeTemplates = new Map();
19
+ this.initializeSHACLTemplates();
20
+ }
21
+
22
+ /**
23
+ * Initialize built-in SHACL shape templates
24
+ */
25
+ initializeSHACLTemplates() {
26
+ // Basic node shape template
27
+ this.registerShapeTemplate('basic_node_shape', {
28
+ name: '{{ shapeName }}Shape',
29
+ description: 'Basic node shape for {{ targetClass | default("Resource") }}',
30
+ template: `@prefix sh: <http://www.w3.org/ns/shacl#> .
31
+ @prefix kgen: <${this.options.namespace}> .
32
+ @prefix ex: <${this.options.baseIRI}> .
33
+
34
+ ex:{{ shapeName }}Shape
35
+ a sh:NodeShape ;
36
+ sh:targetClass ex:{{ targetClass | default(shapeName) }} ;
37
+ {% if description -%}
38
+ rdfs:comment "{{ description }}" ;
39
+ {% endif -%}
40
+ {% for property in properties | default([]) -%}
41
+ sh:property [
42
+ sh:path ex:{{ property.path }} ;
43
+ {% if property.datatype -%}
44
+ sh:datatype {{ property.datatype }} ;
45
+ {% endif -%}
46
+ {% if property.minCount -%}
47
+ sh:minCount {{ property.minCount }} ;
48
+ {% endif -%}
49
+ {% if property.maxCount -%}
50
+ sh:maxCount {{ property.maxCount }} ;
51
+ {% endif -%}
52
+ {% if property.pattern -%}
53
+ sh:pattern "{{ property.pattern }}" ;
54
+ {% endif -%}
55
+ {% if property.message -%}
56
+ sh:message "{{ property.message }}" ;
57
+ {% endif -%}
58
+ ] ;
59
+ {% endfor -%}
60
+ .`
61
+ });
62
+
63
+ // Property shape template
64
+ this.registerShapeTemplate('property_shape', {
65
+ name: '{{ shapeName }}PropertyShape',
66
+ description: 'Property shape for {{ propertyPath }}',
67
+ template: `@prefix sh: <http://www.w3.org/ns/shacl#> .
68
+ @prefix kgen: <${this.options.namespace}> .
69
+ @prefix ex: <${this.options.baseIRI}> .
70
+
71
+ ex:{{ shapeName }}PropertyShape
72
+ a sh:PropertyShape ;
73
+ sh:path ex:{{ propertyPath }} ;
74
+ {% if datatype -%}
75
+ sh:datatype {{ datatype }} ;
76
+ {% endif -%}
77
+ {% if nodeKind -%}
78
+ sh:nodeKind {{ nodeKind }} ;
79
+ {% endif -%}
80
+ {% if minCount !== undefined -%}
81
+ sh:minCount {{ minCount }} ;
82
+ {% endif -%}
83
+ {% if maxCount !== undefined -%}
84
+ sh:maxCount {{ maxCount }} ;
85
+ {% endif -%}
86
+ {% if minLength -%}
87
+ sh:minLength {{ minLength }} ;
88
+ {% endif -%}
89
+ {% if maxLength -%}
90
+ sh:maxLength {{ maxLength }} ;
91
+ {% endif -%}
92
+ {% if pattern -%}
93
+ sh:pattern "{{ pattern }}" ;
94
+ {% endif -%}
95
+ {% if in -%}
96
+ sh:in ( {{ in | join(' ') }} ) ;
97
+ {% endif -%}
98
+ {% if message -%}
99
+ sh:message "{{ message }}" ;
100
+ {% endif -%}
101
+ {% if severity -%}
102
+ sh:severity {{ severity }} ;
103
+ {% endif -%}
104
+ .`
105
+ });
106
+
107
+ // Template validation shape
108
+ this.registerShapeTemplate('template_shape', {
109
+ name: 'TemplateShape',
110
+ description: 'Validation shape for KGEN templates',
111
+ template: `@prefix sh: <http://www.w3.org/ns/shacl#> .
112
+ @prefix kgen: <${this.options.namespace}> .
113
+ @prefix ex: <${this.options.baseIRI}> .
114
+ @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
115
+
116
+ kgen:TemplateShape
117
+ a sh:NodeShape ;
118
+ sh:targetClass kgen:Template ;
119
+ rdfs:comment "Validation shape for KGEN templates" ;
120
+
121
+ # Template must have a name
122
+ sh:property [
123
+ sh:path kgen:templateName ;
124
+ sh:datatype xsd:string ;
125
+ sh:minCount 1 ;
126
+ sh:maxCount 1 ;
127
+ sh:pattern "^[a-zA-Z][a-zA-Z0-9_-]*$" ;
128
+ sh:message "Template name must be a valid identifier" ;
129
+ ] ;
130
+
131
+ # Template must have content
132
+ sh:property [
133
+ sh:path kgen:templateContent ;
134
+ sh:datatype xsd:string ;
135
+ sh:minCount 1 ;
136
+ sh:maxCount 1 ;
137
+ sh:minLength 1 ;
138
+ sh:message "Template must have content" ;
139
+ ] ;
140
+
141
+ # Template version
142
+ sh:property [
143
+ sh:path kgen:version ;
144
+ sh:datatype xsd:string ;
145
+ sh:pattern "^\\d+\\.\\d+\\.\\d+$" ;
146
+ sh:message "Version must follow semantic versioning" ;
147
+ ] ;
148
+
149
+ # Template category
150
+ sh:property [
151
+ sh:path kgen:category ;
152
+ sh:datatype xsd:string ;
153
+ sh:in ( "component" "layout" "module" "utility" "custom" ) ;
154
+ sh:message "Category must be one of: component, layout, module, utility, custom" ;
155
+ ] ;
156
+
157
+ # Deterministic flag
158
+ sh:property [
159
+ sh:path kgen:isDeterministic ;
160
+ sh:datatype xsd:boolean ;
161
+ sh:maxCount 1 ;
162
+ ] ;
163
+ .`
164
+ });
165
+
166
+ // Filter validation shape
167
+ this.registerShapeTemplate('filter_shape', {
168
+ name: 'FilterShape',
169
+ description: 'Validation shape for KGEN filters',
170
+ template: `@prefix sh: <http://www.w3.org/ns/shacl#> .
171
+ @prefix kgen: <${this.options.namespace}> .
172
+ @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
173
+
174
+ kgen:FilterShape
175
+ a sh:NodeShape ;
176
+ sh:targetClass kgen:Filter ;
177
+ rdfs:comment "Validation shape for KGEN filters" ;
178
+
179
+ # Filter must have a name
180
+ sh:property [
181
+ sh:path kgen:filterName ;
182
+ sh:datatype xsd:string ;
183
+ sh:minCount 1 ;
184
+ sh:maxCount 1 ;
185
+ sh:pattern "^[a-zA-Z][a-zA-Z0-9_]*$" ;
186
+ sh:message "Filter name must be a valid JavaScript identifier" ;
187
+ ] ;
188
+
189
+ # Filter function code
190
+ sh:property [
191
+ sh:path kgen:filterCode ;
192
+ sh:datatype xsd:string ;
193
+ sh:minCount 1 ;
194
+ sh:maxCount 1 ;
195
+ sh:message "Filter must have implementation code" ;
196
+ ] ;
197
+
198
+ # Filter must be deterministic
199
+ sh:property [
200
+ sh:path kgen:isDeterministic ;
201
+ sh:datatype xsd:boolean ;
202
+ sh:hasValue true ;
203
+ sh:message "All KGEN filters must be deterministic" ;
204
+ ] ;
205
+ .`
206
+ });
207
+
208
+ // Knowledge graph shape
209
+ this.registerShapeTemplate('knowledge_graph_shape', {
210
+ name: 'KnowledgeGraphShape',
211
+ description: 'Validation shape for KGEN knowledge graphs',
212
+ template: `@prefix sh: <http://www.w3.org/ns/shacl#> .
213
+ @prefix kgen: <${this.options.namespace}> .
214
+ @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
215
+ @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
216
+
217
+ kgen:KnowledgeGraphShape
218
+ a sh:NodeShape ;
219
+ sh:targetClass kgen:KnowledgeGraph ;
220
+ rdfs:comment "Validation shape for KGEN knowledge graphs" ;
221
+
222
+ # Graph must have an identifier
223
+ sh:property [
224
+ sh:path kgen:graphId ;
225
+ sh:datatype xsd:string ;
226
+ sh:minCount 1 ;
227
+ sh:maxCount 1 ;
228
+ sh:pattern "^[a-zA-Z][a-zA-Z0-9_-]*$" ;
229
+ sh:message "Graph ID must be a valid identifier" ;
230
+ ] ;
231
+
232
+ # Graph version
233
+ sh:property [
234
+ sh:path kgen:graphVersion ;
235
+ sh:datatype xsd:string ;
236
+ sh:pattern "^\\d+\\.\\d+\\.\\d+$" ;
237
+ sh:message "Graph version must follow semantic versioning" ;
238
+ ] ;
239
+
240
+ # Creation timestamp
241
+ sh:property [
242
+ sh:path kgen:createdAt ;
243
+ sh:datatype xsd:dateTime ;
244
+ sh:maxCount 1 ;
245
+ ] ;
246
+
247
+ # Modification timestamp
248
+ sh:property [
249
+ sh:path kgen:modifiedAt ;
250
+ sh:datatype xsd:dateTime ;
251
+ sh:maxCount 1 ;
252
+ ] ;
253
+ .`
254
+ });
255
+
256
+ // Component shape template
257
+ this.registerShapeTemplate('component_shape', {
258
+ name: 'ComponentShape',
259
+ description: 'Validation shape for template components',
260
+ template: `@prefix sh: <http://www.w3.org/ns/shacl#> .
261
+ @prefix kgen: <${this.options.namespace}> .
262
+ @prefix ex: <${this.options.baseIRI}> .
263
+ @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
264
+
265
+ ex:{{ shapeName | default('Component') }}Shape
266
+ a sh:NodeShape ;
267
+ sh:targetClass ex:{{ targetClass | default('Component') }} ;
268
+ rdfs:comment "{{ description | default('Validation shape for components') }}" ;
269
+
270
+ {% for property in requiredProperties | default([]) -%}
271
+ # Required property: {{ property.name }}
272
+ sh:property [
273
+ sh:path ex:{{ property.name }} ;
274
+ {% if property.datatype -%}
275
+ sh:datatype {{ property.datatype }} ;
276
+ {% endif -%}
277
+ sh:minCount 1 ;
278
+ {% if property.maxCount -%}
279
+ sh:maxCount {{ property.maxCount }} ;
280
+ {% endif -%}
281
+ {% if property.pattern -%}
282
+ sh:pattern "{{ property.pattern }}" ;
283
+ {% endif -%}
284
+ sh:message "{{ property.message | default('Property ' + property.name + ' is required') }}" ;
285
+ ] ;
286
+
287
+ {% endfor -%}
288
+ {% for property in optionalProperties | default([]) -%}
289
+ # Optional property: {{ property.name }}
290
+ sh:property [
291
+ sh:path ex:{{ property.name }} ;
292
+ {% if property.datatype -%}
293
+ sh:datatype {{ property.datatype }} ;
294
+ {% endif -%}
295
+ {% if property.maxCount -%}
296
+ sh:maxCount {{ property.maxCount }} ;
297
+ {% endif -%}
298
+ {% if property.pattern -%}
299
+ sh:pattern "{{ property.pattern }}" ;
300
+ {% endif -%}
301
+ ] ;
302
+
303
+ {% endfor -%}
304
+ .`
305
+ });
306
+
307
+ // Validation rule template
308
+ this.registerShapeTemplate('validation_rule', {
309
+ name: 'ValidationRuleShape',
310
+ description: 'Template for custom validation rules',
311
+ template: `@prefix sh: <http://www.w3.org/ns/shacl#> .
312
+ @prefix kgen: <${this.options.namespace}> .
313
+ @prefix ex: <${this.options.baseIRI}> .
314
+ @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
315
+
316
+ ex:{{ ruleName }}Rule
317
+ a sh:NodeShape ;
318
+ {% if targetClass -%}
319
+ sh:targetClass ex:{{ targetClass }} ;
320
+ {% endif -%}
321
+ {% if targetNode -%}
322
+ sh:targetNode ex:{{ targetNode }} ;
323
+ {% endif -%}
324
+ rdfs:comment "{{ description | default('Custom validation rule') }}" ;
325
+
326
+ {% if sparqlConstraint -%}
327
+ sh:sparql [
328
+ sh:message "{{ sparqlMessage | default('SPARQL constraint violation') }}" ;
329
+ sh:prefixes [
330
+ sh:declare [
331
+ sh:prefix "kgen" ;
332
+ sh:namespace "${this.options.namespace}" ;
333
+ ] ;
334
+ sh:declare [
335
+ sh:prefix "ex" ;
336
+ sh:namespace "${this.options.baseIRI}" ;
337
+ ] ;
338
+ ] ;
339
+ sh:select """
340
+ {{ sparqlConstraint }}
341
+ """ ;
342
+ ] ;
343
+ {% endif -%}
344
+ .`
345
+ });
346
+ }
347
+
348
+ /**
349
+ * Register a SHACL shape template
350
+ */
351
+ registerShapeTemplate(name, template) {
352
+ this.shapeTemplates.set(name, {
353
+ ...template,
354
+ registered: this.options.staticBuildTime,
355
+ namespace: this.options.namespace
356
+ });
357
+ }
358
+
359
+ /**
360
+ * Generate SHACL shape from template
361
+ */
362
+ generateShape(templateName, context = {}) {
363
+ const template = this.shapeTemplates.get(templateName);
364
+ if (!template) {
365
+ throw new Error(`SHACL template '${templateName}' not found`);
366
+ }
367
+
368
+ // Simple template substitution for SHACL
369
+ let shapeContent = template.template;
370
+
371
+ // Add standard prefixes if not present
372
+ if (!shapeContent.includes('@prefix sh:')) {
373
+ const prefixes = [
374
+ '@prefix sh: <http://www.w3.org/ns/shacl#> .',
375
+ `@prefix kgen: <${this.options.namespace}> .`,
376
+ `@prefix ex: <${this.options.baseIRI}> .`,
377
+ '@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .',
378
+ '@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .',
379
+ ''
380
+ ].join('\n');
381
+
382
+ shapeContent = prefixes + shapeContent;
383
+ }
384
+
385
+ // Replace template variables
386
+ for (const [key, value] of Object.entries(context)) {
387
+ const regex = new RegExp(`{{\\s*${key}\\s*(?:\\|[^}]*)?\\s*}}`, 'g');
388
+
389
+ const matches = shapeContent.match(regex);
390
+ if (matches) {
391
+ for (const match of matches) {
392
+ const defaultMatch = match.match(/\|\s*default\('([^']*)'\)/);
393
+ const replacement = value !== undefined ? value : (defaultMatch ? defaultMatch[1] : match);
394
+ shapeContent = shapeContent.replace(match, String(replacement));
395
+ }
396
+ }
397
+ }
398
+
399
+ // Process simple loops and conditions
400
+ shapeContent = this.processTemplateLogic(shapeContent, context);
401
+
402
+ return {
403
+ name: this.interpolate(template.name, context),
404
+ description: this.interpolate(template.description, context),
405
+ content: shapeContent,
406
+ metadata: {
407
+ template: templateName,
408
+ generated: this.options.staticBuildTime,
409
+ namespace: this.options.namespace,
410
+ context: Object.keys(context)
411
+ }
412
+ };
413
+ }
414
+
415
+ /**
416
+ * Process template logic (loops and conditions)
417
+ */
418
+ processTemplateLogic(content, context) {
419
+ let result = content;
420
+
421
+ // Process {% for %} loops
422
+ const forLoopRegex = /{%\s*for\s+(\w+)\s+in\s+([\w.\[\]|()]+)\s*%}([\s\S]*?){%\s*endfor\s*%}/g;
423
+
424
+ result = result.replace(forLoopRegex, (match, itemVar, listExpr, loopContent) => {
425
+ const listValue = this.evaluateExpression(listExpr, context);
426
+ if (!Array.isArray(listValue)) {
427
+ return '';
428
+ }
429
+
430
+ return listValue.map((item, index) => {
431
+ const loopContext = {
432
+ ...context,
433
+ [itemVar]: item,
434
+ loop: {
435
+ index: index,
436
+ first: index === 0,
437
+ last: index === listValue.length - 1
438
+ }
439
+ };
440
+
441
+ return this.interpolateWithContext(loopContent, loopContext);
442
+ }).join('');
443
+ });
444
+
445
+ // Process {% if %} conditions
446
+ const ifRegex = /{%\s*if\s+([\w.\[\]|()\s!]+)\s*%}([\s\S]*?)(?:{%\s*else\s*%}([\s\S]*?))?{%\s*endif\s*%}/g;
447
+
448
+ result = result.replace(ifRegex, (match, condition, ifContent, elseContent) => {
449
+ const conditionValue = this.evaluateExpression(condition, context);
450
+ return conditionValue ? ifContent : (elseContent || '');
451
+ });
452
+
453
+ return result;
454
+ }
455
+
456
+ /**
457
+ * Evaluate simple expressions
458
+ */
459
+ evaluateExpression(expr, context) {
460
+ // Handle default expressions
461
+ const defaultMatch = expr.match(/(\w+)\s*\|\s*default\('([^']*)'\)/);
462
+ if (defaultMatch) {
463
+ const value = this.getNestedValue(context, defaultMatch[1]);
464
+ return value !== undefined ? value : defaultMatch[2];
465
+ }
466
+
467
+ // Handle simple property access
468
+ return this.getNestedValue(context, expr.trim());
469
+ }
470
+
471
+ /**
472
+ * Get nested value from object
473
+ */
474
+ getNestedValue(obj, path) {
475
+ return path.split('.').reduce((current, key) => {
476
+ return current && current[key] !== undefined ? current[key] : undefined;
477
+ }, obj);
478
+ }
479
+
480
+ /**
481
+ * Interpolate template with context
482
+ */
483
+ interpolateWithContext(template, context) {
484
+ let result = template;
485
+
486
+ for (const [key, value] of Object.entries(context)) {
487
+ if (typeof value === 'object' && value !== null) {
488
+ // Handle nested objects
489
+ for (const [nestedKey, nestedValue] of Object.entries(value)) {
490
+ const regex = new RegExp(`{{\\s*${key}\\.${nestedKey}\\s*}}`, 'g');
491
+ result = result.replace(regex, String(nestedValue));
492
+ }
493
+ } else {
494
+ const regex = new RegExp(`{{\\s*${key}\\s*}}`, 'g');
495
+ result = result.replace(regex, String(value || ''));
496
+ }
497
+ }
498
+
499
+ return result;
500
+ }
501
+
502
+ /**
503
+ * Simple template interpolation
504
+ */
505
+ interpolate(template, context) {
506
+ let result = template;
507
+
508
+ for (const [key, value] of Object.entries(context)) {
509
+ const regex = new RegExp(`{{\\s*${key}\\s*(?:\\|[^}]*)?\\s*}}`, 'g');
510
+
511
+ const matches = result.match(regex);
512
+ if (matches) {
513
+ for (const match of matches) {
514
+ const defaultMatch = match.match(/\|\s*default\('([^']*)'\)/);
515
+ const replacement = value !== undefined ? value : (defaultMatch ? defaultMatch[1] : match);
516
+ result = result.replace(match, replacement);
517
+ }
518
+ }
519
+ }
520
+
521
+ return result;
522
+ }
523
+
524
+ /**
525
+ * Generate complete SHACL validation file
526
+ */
527
+ generateValidationFile(shapes = [], filename = 'validation.ttl') {
528
+ const header = [
529
+ '# KGEN SHACL Validation Shapes',
530
+ `# Generated: ${this.options.staticBuildTime}`,
531
+ `# Namespace: ${this.options.namespace}`,
532
+ '',
533
+ '@prefix sh: <http://www.w3.org/ns/shacl#> .',
534
+ `@prefix kgen: <${this.options.namespace}> .`,
535
+ `@prefix ex: <${this.options.baseIRI}> .`,
536
+ '@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .',
537
+ '@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .',
538
+ ''
539
+ ];
540
+
541
+ const generatedShapes = shapes.map(shape =>
542
+ this.generateShape(shape.template, shape.context)
543
+ );
544
+
545
+ const content = [
546
+ ...header,
547
+ ...generatedShapes.map(shape => [
548
+ `# ${shape.description}`,
549
+ shape.content,
550
+ ''
551
+ ]).flat()
552
+ ];
553
+
554
+ return {
555
+ filename,
556
+ content: content.join('\n'),
557
+ shapes: generatedShapes,
558
+ metadata: {
559
+ shapeCount: generatedShapes.length,
560
+ generated: this.options.staticBuildTime,
561
+ namespace: this.options.namespace
562
+ }
563
+ };
564
+ }
565
+
566
+ /**
567
+ * Get available shape template names
568
+ */
569
+ getTemplateNames() {
570
+ return Array.from(this.shapeTemplates.keys()).sort();
571
+ }
572
+
573
+ /**
574
+ * Get shape template by name
575
+ */
576
+ getTemplate(name) {
577
+ return this.shapeTemplates.get(name);
578
+ }
579
+
580
+ /**
581
+ * Export SHACL templates
582
+ */
583
+ exportTemplates(templateNames = []) {
584
+ const templatesToExport = templateNames.length > 0
585
+ ? templateNames.filter(name => this.shapeTemplates.has(name))
586
+ : Array.from(this.shapeTemplates.keys());
587
+
588
+ const exported = {
589
+ format: 'kgen-shacl-templates',
590
+ version: '1.0.0',
591
+ generated: this.options.staticBuildTime,
592
+ namespace: this.options.namespace,
593
+ baseIRI: this.options.baseIRI,
594
+ templates: {}
595
+ };
596
+
597
+ for (const name of templatesToExport) {
598
+ exported.templates[name] = this.shapeTemplates.get(name);
599
+ }
600
+
601
+ return exported;
602
+ }
603
+
604
+ /**
605
+ * Get template statistics
606
+ */
607
+ getStats() {
608
+ return {
609
+ totalTemplates: this.shapeTemplates.size,
610
+ namespace: this.options.namespace,
611
+ baseIRI: this.options.baseIRI,
612
+ templates: Array.from(this.shapeTemplates.keys()).sort()
613
+ };
614
+ }
615
+ }
616
+
617
+ export default KGenSHACLTemplates;