@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,426 @@
1
+ /**
2
+ * Template Utilities - High-level convenience functions
3
+ * Migrated from ~/unjucks with enhanced error handling and validation
4
+ */
5
+
6
+ import { TemplateEngine } from '../engine/template-engine.js';
7
+ import { TemplateLinter } from '../linter/determinism.js';
8
+ import { VariableExtractor } from '../parser/variables.js';
9
+ import { FrontmatterParser } from '../parser/frontmatter.js';
10
+
11
+ /**
12
+ * Quick template rendering with default options
13
+ */
14
+ export async function renderTemplate(templatePath, context = {}, options = {}) {
15
+ const engine = new TemplateEngine({
16
+ templatesDir: options.templatesDir,
17
+ deterministicMode: options.deterministicMode !== false,
18
+ strictMode: options.strictMode !== false,
19
+ ...options.engineOptions
20
+ });
21
+
22
+ return await engine.render(templatePath, context, options);
23
+ }
24
+
25
+ /**
26
+ * Render template from string content
27
+ */
28
+ export function renderString(templateString, context = {}, options = {}) {
29
+ const engine = new TemplateEngine({
30
+ deterministicMode: options.deterministicMode !== false,
31
+ strictMode: options.strictMode !== false,
32
+ ...options.engineOptions
33
+ });
34
+
35
+ return engine.renderString(templateString, context, options);
36
+ }
37
+
38
+ /**
39
+ * Validate template for determinism and common issues
40
+ */
41
+ export async function validateTemplate(templatePath, options = {}) {
42
+ try {
43
+ // Read template content
44
+ const fs = await import('fs/promises');
45
+ const path = await import('path');
46
+
47
+ const fullPath = options.templatesDir
48
+ ? path.resolve(options.templatesDir, templatePath)
49
+ : path.resolve(templatePath);
50
+
51
+ const content = await fs.readFile(fullPath, 'utf8');
52
+
53
+ // Parse frontmatter
54
+ const parser = new FrontmatterParser();
55
+ const { frontmatter } = parser.parse(content);
56
+
57
+ // Lint template
58
+ const linter = new TemplateLinter({
59
+ strict: options.strict !== false,
60
+ customRules: options.customRules
61
+ });
62
+
63
+ const lintResult = linter.lint(content, frontmatter);
64
+
65
+ return {
66
+ success: true,
67
+ templatePath: fullPath,
68
+ ...lintResult
69
+ };
70
+
71
+ } catch (error) {
72
+ return {
73
+ success: false,
74
+ error: error.message,
75
+ templatePath
76
+ };
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Extract variables from template
82
+ */
83
+ export async function extractVariables(templatePath, options = {}) {
84
+ try {
85
+ // Read template content
86
+ const fs = await import('fs/promises');
87
+ const path = await import('path');
88
+
89
+ const fullPath = options.templatesDir
90
+ ? path.resolve(options.templatesDir, templatePath)
91
+ : path.resolve(templatePath);
92
+
93
+ const content = await fs.readFile(fullPath, 'utf8');
94
+
95
+ // Parse frontmatter to get content without frontmatter
96
+ const parser = new FrontmatterParser();
97
+ const { frontmatter, content: templateContent } = parser.parse(content);
98
+
99
+ // Extract variables
100
+ const extractor = new VariableExtractor({
101
+ includeFilters: options.includeFilters !== false,
102
+ includeFunctions: options.includeFunctions !== false
103
+ });
104
+
105
+ const extracted = extractor.extract(templateContent);
106
+
107
+ return {
108
+ success: true,
109
+ templatePath: fullPath,
110
+ frontmatter,
111
+ ...extracted
112
+ };
113
+
114
+ } catch (error) {
115
+ return {
116
+ success: false,
117
+ error: error.message,
118
+ templatePath
119
+ };
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Lint template for determinism issues
125
+ */
126
+ export async function lintTemplate(templatePath, options = {}) {
127
+ try {
128
+ // Read template content
129
+ const fs = await import('fs/promises');
130
+ const path = await import('path');
131
+
132
+ const fullPath = options.templatesDir
133
+ ? path.resolve(options.templatesDir, templatePath)
134
+ : path.resolve(templatePath);
135
+
136
+ const content = await fs.readFile(fullPath, 'utf8');
137
+
138
+ // Parse frontmatter
139
+ const parser = new FrontmatterParser();
140
+ const { frontmatter } = parser.parse(content);
141
+
142
+ // Lint template
143
+ const linter = new TemplateLinter({
144
+ strict: options.strict !== false,
145
+ warnOnly: options.warnOnly === true,
146
+ customRules: options.customRules || []
147
+ });
148
+
149
+ const result = linter.lint(content, frontmatter);
150
+
151
+ // Auto-fix if requested
152
+ if (options.autoFix) {
153
+ const fixResult = linter.autoFix(content);
154
+ result.autoFix = fixResult;
155
+ }
156
+
157
+ return {
158
+ success: true,
159
+ templatePath: fullPath,
160
+ ...result
161
+ };
162
+
163
+ } catch (error) {
164
+ return {
165
+ success: false,
166
+ error: error.message,
167
+ templatePath
168
+ };
169
+ }
170
+ }
171
+
172
+ /**
173
+ * Analyze template comprehensively
174
+ */
175
+ export async function analyzeTemplate(templatePath, options = {}) {
176
+ const results = await Promise.all([
177
+ extractVariables(templatePath, options),
178
+ validateTemplate(templatePath, options),
179
+ lintTemplate(templatePath, options)
180
+ ]);
181
+
182
+ const [variables, validation, linting] = results;
183
+
184
+ if (!variables.success || !validation.success || !linting.success) {
185
+ const errors = [
186
+ variables.error,
187
+ validation.error,
188
+ linting.error
189
+ ].filter(Boolean);
190
+
191
+ return {
192
+ success: false,
193
+ error: `Analysis failed: ${errors.join(', ')}`,
194
+ templatePath
195
+ };
196
+ }
197
+
198
+ return {
199
+ success: true,
200
+ templatePath: variables.templatePath,
201
+ analysis: {
202
+ variables: variables.variables,
203
+ filters: variables.filters,
204
+ functions: variables.functions,
205
+ complexity: variables.complexity,
206
+ frontmatter: variables.frontmatter,
207
+ deterministic: validation.deterministic,
208
+ score: validation.score,
209
+ issues: validation.issues,
210
+ warnings: validation.warnings,
211
+ suggestions: validation.suggestions,
212
+ linting: {
213
+ passesLint: linting.summary.passesLint,
214
+ totalIssues: linting.summary.totalIssues,
215
+ criticalIssues: linting.summary.criticalIssues
216
+ }
217
+ },
218
+ summary: {
219
+ variableCount: variables.variables.length,
220
+ filterCount: variables.filters.length,
221
+ determinismScore: validation.score,
222
+ isDeterministic: validation.deterministic,
223
+ hasIssues: validation.issues.length > 0,
224
+ complexity: variables.complexity
225
+ }
226
+ };
227
+ }
228
+
229
+ /**
230
+ * Test template with sample data
231
+ */
232
+ export async function testTemplate(templatePath, testData = {}, options = {}) {
233
+ try {
234
+ // Extract required variables
235
+ const variableResult = await extractVariables(templatePath, options);
236
+ if (!variableResult.success) {
237
+ return variableResult;
238
+ }
239
+
240
+ // Check if test data provides required variables
241
+ const missingVars = variableResult.variables.filter(
242
+ varName => !(varName in testData) && !varName.startsWith('_')
243
+ );
244
+
245
+ if (missingVars.length > 0 && options.strict !== false) {
246
+ return {
247
+ success: false,
248
+ error: `Missing test data for variables: ${missingVars.join(', ')}`,
249
+ missingVariables: missingVars,
250
+ templatePath
251
+ };
252
+ }
253
+
254
+ // Render template with test data
255
+ const renderResult = await renderTemplate(templatePath, testData, {
256
+ ...options,
257
+ validateVars: options.strict !== false
258
+ });
259
+
260
+ if (!renderResult.success) {
261
+ return renderResult;
262
+ }
263
+
264
+ // Test determinism if requested
265
+ let determinismTest = null;
266
+ if (options.testDeterminism !== false) {
267
+ const engine = new TemplateEngine({
268
+ deterministicMode: true,
269
+ ...options.engineOptions
270
+ });
271
+
272
+ const verification = await engine.deterministicRenderer.verifyDeterminism(
273
+ engine.env,
274
+ renderResult.content,
275
+ testData,
276
+ options.determinismIterations || 3
277
+ );
278
+
279
+ determinismTest = verification;
280
+ }
281
+
282
+ return {
283
+ success: true,
284
+ templatePath: renderResult.templatePath,
285
+ renderResult,
286
+ determinismTest,
287
+ testData,
288
+ missingVariables: missingVars
289
+ };
290
+
291
+ } catch (error) {
292
+ return {
293
+ success: false,
294
+ error: error.message,
295
+ templatePath
296
+ };
297
+ }
298
+ }
299
+
300
+ /**
301
+ * Discover templates in directory
302
+ */
303
+ export async function discoverTemplates(directory, options = {}) {
304
+ try {
305
+ const fs = await import('fs/promises');
306
+ const path = await import('path');
307
+
308
+ const templates = [];
309
+ const extensions = options.extensions || ['.njk', '.j2', '.html'];
310
+
311
+ async function scanDirectory(dir, relativePath = '') {
312
+ const entries = await fs.readdir(dir, { withFileTypes: true });
313
+
314
+ for (const entry of entries) {
315
+ if (entry.isDirectory() && !entry.name.startsWith('.')) {
316
+ if (options.recursive !== false) {
317
+ await scanDirectory(
318
+ path.join(dir, entry.name),
319
+ path.join(relativePath, entry.name)
320
+ );
321
+ }
322
+ } else if (entry.isFile()) {
323
+ const ext = path.extname(entry.name);
324
+ if (extensions.includes(ext)) {
325
+ const templatePath = path.join(relativePath, entry.name);
326
+ const fullPath = path.join(dir, entry.name);
327
+
328
+ const template = {
329
+ name: path.basename(entry.name, ext),
330
+ path: templatePath,
331
+ fullPath,
332
+ extension: ext,
333
+ directory: relativePath,
334
+ size: (await fs.stat(fullPath)).size,
335
+ modified: (await fs.stat(fullPath)).mtime.toISOString()
336
+ };
337
+
338
+ // Add analysis if requested
339
+ if (options.analyze) {
340
+ try {
341
+ const analysis = await analyzeTemplate(fullPath, options);
342
+ template.analysis = analysis.success ? analysis.summary : null;
343
+ template.analysisError = analysis.success ? null : analysis.error;
344
+ } catch (error) {
345
+ template.analysisError = error.message;
346
+ }
347
+ }
348
+
349
+ templates.push(template);
350
+ }
351
+ }
352
+ }
353
+ }
354
+
355
+ await scanDirectory(directory);
356
+
357
+ return {
358
+ success: true,
359
+ directory,
360
+ templates: templates.sort((a, b) => a.path.localeCompare(b.path)),
361
+ count: templates.length
362
+ };
363
+
364
+ } catch (error) {
365
+ return {
366
+ success: false,
367
+ error: error.message,
368
+ directory
369
+ };
370
+ }
371
+ }
372
+
373
+ /**
374
+ * Batch process templates
375
+ */
376
+ export async function batchProcess(templates, operation, options = {}) {
377
+ const results = [];
378
+ const concurrency = options.concurrency || 5;
379
+
380
+ // Process in batches for better performance
381
+ for (let i = 0; i < templates.length; i += concurrency) {
382
+ const batch = templates.slice(i, i + concurrency);
383
+ const batchPromises = batch.map(async (template) => {
384
+ try {
385
+ const result = await operation(template, options);
386
+ return {
387
+ template,
388
+ result,
389
+ success: true
390
+ };
391
+ } catch (error) {
392
+ return {
393
+ template,
394
+ error: error.message,
395
+ success: false
396
+ };
397
+ }
398
+ });
399
+
400
+ const batchResults = await Promise.all(batchPromises);
401
+ results.push(...batchResults);
402
+ }
403
+
404
+ const successful = results.filter(r => r.success);
405
+ const failed = results.filter(r => !r.success);
406
+
407
+ return {
408
+ total: results.length,
409
+ successful: successful.length,
410
+ failed: failed.length,
411
+ results,
412
+ errors: failed.map(f => ({ template: f.template, error: f.error }))
413
+ };
414
+ }
415
+
416
+ export default {
417
+ renderTemplate,
418
+ renderString,
419
+ validateTemplate,
420
+ extractVariables,
421
+ lintTemplate,
422
+ analyzeTemplate,
423
+ testTemplate,
424
+ discoverTemplates,
425
+ batchProcess
426
+ };