@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,369 @@
1
+ /**
2
+ * KGEN Text Filters - P0 Essential Text Processing
3
+ *
4
+ * The 20% of text filters that provide 80% of value
5
+ * Deterministic implementations with comprehensive error handling
6
+ */
7
+
8
+ /**
9
+ * Convert string to uppercase
10
+ * @param {any} str - Input value to convert
11
+ * @returns {string} Uppercase string
12
+ */
13
+ export const upper = (str) => {
14
+ if (str === null || str === undefined) return '';
15
+ return String(str).toUpperCase();
16
+ };
17
+
18
+ /**
19
+ * Convert string to lowercase
20
+ * @param {any} str - Input value to convert
21
+ * @returns {string} Lowercase string
22
+ */
23
+ export const lower = (str) => {
24
+ if (str === null || str === undefined) return '';
25
+ return String(str).toLowerCase();
26
+ };
27
+
28
+ /**
29
+ * Trim whitespace from string
30
+ * @param {any} str - Input value to trim
31
+ * @returns {string} Trimmed string
32
+ */
33
+ export const trim = (str) => {
34
+ if (str === null || str === undefined) return '';
35
+ return String(str).trim();
36
+ };
37
+
38
+ /**
39
+ * Replace all occurrences of find with replace
40
+ * @param {any} str - Input string
41
+ * @param {string} find - String to find
42
+ * @param {string} replace - Replacement string
43
+ * @returns {string} String with replacements
44
+ */
45
+ export const replace = (str, find = '', replace = '') => {
46
+ if (str === null || str === undefined) return '';
47
+ if (find === null || find === undefined) return String(str);
48
+ return String(str).replace(new RegExp(String(find), 'g'), String(replace));
49
+ };
50
+
51
+ /**
52
+ * Replace first occurrence of find with replace
53
+ * @param {any} str - Input string
54
+ * @param {string} find - String to find
55
+ * @param {string} replace - Replacement string
56
+ * @returns {string} String with first replacement
57
+ */
58
+ export const replaceFirst = (str, find = '', replace = '') => {
59
+ if (str === null || str === undefined) return '';
60
+ if (find === null || find === undefined) return String(str);
61
+ return String(str).replace(String(find), String(replace));
62
+ };
63
+
64
+ /**
65
+ * Truncate string to specified length
66
+ * @param {any} str - Input string
67
+ * @param {number} length - Maximum length
68
+ * @param {string} suffix - Suffix to add if truncated
69
+ * @returns {string} Truncated string
70
+ */
71
+ export const truncate = (str, length = 100, suffix = '...') => {
72
+ if (str === null || str === undefined) return '';
73
+ const text = String(str);
74
+ if (text.length <= length) return text;
75
+ return text.substring(0, length) + suffix;
76
+ };
77
+
78
+ /**
79
+ * Create URL-friendly slug from string
80
+ * @param {any} str - Input string
81
+ * @returns {string} URL-safe slug
82
+ */
83
+ export const slug = (str) => {
84
+ if (str === null || str === undefined) return '';
85
+ return String(str)
86
+ .toLowerCase()
87
+ .replace(/[^\w\s-]/g, '') // Remove special chars except word chars, spaces, hyphens
88
+ .replace(/[-\s]+/g, '-') // Replace spaces and multiple hyphens with single hyphen
89
+ .replace(/^-+|-+$/g, ''); // Remove leading/trailing hyphens
90
+ };
91
+
92
+ /**
93
+ * Convert string to title case
94
+ * @param {any} str - Input string
95
+ * @returns {string} Title case string
96
+ */
97
+ export const title = (str) => {
98
+ if (str === null || str === undefined) return '';
99
+ return String(str)
100
+ .toLowerCase()
101
+ .replace(/\b\w/g, char => char.toUpperCase());
102
+ };
103
+
104
+ /**
105
+ * Capitalize first letter
106
+ * @param {any} str - Input string
107
+ * @returns {string} Capitalized string
108
+ */
109
+ export const capitalize = (str) => {
110
+ if (str === null || str === undefined) return '';
111
+ const text = String(str);
112
+ return text.charAt(0).toUpperCase() + text.slice(1);
113
+ };
114
+
115
+ /**
116
+ * Convert to camelCase
117
+ * @param {any} str - Input string
118
+ * @returns {string} camelCase string
119
+ */
120
+ export const camelCase = (str) => {
121
+ if (str === null || str === undefined) return '';
122
+ return String(str)
123
+ .replace(/[-_\s]+(.)?/g, (_, char) => char ? char.toUpperCase() : '');
124
+ };
125
+
126
+ /**
127
+ * Convert to PascalCase
128
+ * @param {any} str - Input string
129
+ * @returns {string} PascalCase string
130
+ */
131
+ export const pascalCase = (str) => {
132
+ if (str === null || str === undefined) return '';
133
+ const camel = camelCase(str);
134
+ return camel.charAt(0).toUpperCase() + camel.slice(1);
135
+ };
136
+
137
+ /**
138
+ * Convert to kebab-case
139
+ * @param {any} str - Input string
140
+ * @returns {string} kebab-case string
141
+ */
142
+ export const kebabCase = (str) => {
143
+ if (str === null || str === undefined) return '';
144
+ return String(str)
145
+ .replace(/([a-z])([A-Z])/g, '$1-$2')
146
+ .replace(/[\s_]+/g, '-')
147
+ .toLowerCase();
148
+ };
149
+
150
+ /**
151
+ * Convert to snake_case
152
+ * @param {any} str - Input string
153
+ * @returns {string} snake_case string
154
+ */
155
+ export const snakeCase = (str) => {
156
+ if (str === null || str === undefined) return '';
157
+ return String(str)
158
+ .replace(/([a-z])([A-Z])/g, '$1_$2')
159
+ .replace(/[\s-]+/g, '_')
160
+ .toLowerCase();
161
+ };
162
+
163
+ /**
164
+ * Convert to CONSTANT_CASE
165
+ * @param {any} str - Input string
166
+ * @returns {string} CONSTANT_CASE string
167
+ */
168
+ export const constantCase = (str) => {
169
+ return snakeCase(str).toUpperCase();
170
+ };
171
+
172
+ /**
173
+ * Escape HTML characters
174
+ * @param {any} str - Input string
175
+ * @returns {string} HTML-escaped string
176
+ */
177
+ export const escape = (str) => {
178
+ if (str === null || str === undefined) return '';
179
+ return String(str)
180
+ .replace(/&/g, '&amp;')
181
+ .replace(/</g, '&lt;')
182
+ .replace(/>/g, '&gt;')
183
+ .replace(/"/g, '&quot;')
184
+ .replace(/'/g, '&#x27;');
185
+ };
186
+
187
+ /**
188
+ * Reverse string
189
+ * @param {any} str - Input string
190
+ * @returns {string} Reversed string
191
+ */
192
+ export const reverse = (str) => {
193
+ if (str === null || str === undefined) return '';
194
+ return String(str).split('').reverse().join('');
195
+ };
196
+
197
+ /**
198
+ * Count words in string
199
+ * @param {any} str - Input string
200
+ * @returns {number} Word count
201
+ */
202
+ export const wordCount = (str) => {
203
+ if (str === null || str === undefined) return 0;
204
+ return String(str).trim().split(/\s+/).filter(word => word.length > 0).length;
205
+ };
206
+
207
+ /**
208
+ * Get length of string
209
+ * @param {any} str - Input string
210
+ * @returns {number} String length
211
+ */
212
+ export const length = (str) => {
213
+ if (str === null || str === undefined) return 0;
214
+ return String(str).length;
215
+ };
216
+
217
+ /**
218
+ * Pad string to specified length on the left
219
+ * @param {any} str - Input string
220
+ * @param {number} targetLength - Target length
221
+ * @param {string} padString - String to pad with
222
+ * @returns {string} Left-padded string
223
+ */
224
+ export const padStart = (str, targetLength, padString = ' ') => {
225
+ if (str === null || str === undefined) return '';
226
+ return String(str).padStart(targetLength, padString);
227
+ };
228
+
229
+ /**
230
+ * Pad string to specified length on the right
231
+ * @param {any} str - Input string
232
+ * @param {number} targetLength - Target length
233
+ * @param {string} padString - String to pad with
234
+ * @returns {string} Right-padded string
235
+ */
236
+ export const padEnd = (str, targetLength, padString = ' ') => {
237
+ if (str === null || str === undefined) return '';
238
+ return String(str).padEnd(targetLength, padString);
239
+ };
240
+
241
+ /**
242
+ * Indent all lines with specified number of spaces
243
+ * @param {any} str - Input string
244
+ * @param {number} spaces - Number of spaces to indent
245
+ * @returns {string} Indented string
246
+ */
247
+ export const indent = (str, spaces = 2) => {
248
+ if (str === null || str === undefined) return '';
249
+ const indentation = ' '.repeat(Math.max(0, spaces));
250
+ return String(str)
251
+ .split('\n')
252
+ .map(line => indentation + line)
253
+ .join('\n');
254
+ };
255
+
256
+ /**
257
+ * Add quotes around string with specified style
258
+ * @param {any} str - Input string
259
+ * @param {string} style - Quote style: 'single', 'double', 'backtick'
260
+ * @returns {string} Quoted string
261
+ */
262
+ export const quote = (str, style = 'double') => {
263
+ if (str === null || str === undefined) return '';
264
+ const text = String(str);
265
+
266
+ switch (style) {
267
+ case 'single':
268
+ return `'${text.replace(/'/g, "\\'")}'`;
269
+ case 'double':
270
+ return `"${text.replace(/"/g, '\\"')}"`;
271
+ case 'backtick':
272
+ return `\`${text.replace(/`/g, '\\`')}\``;
273
+ default:
274
+ return `"${text.replace(/"/g, '\\"')}"`;
275
+ }
276
+ };
277
+
278
+ /**
279
+ * Remove surrounding quotes from string
280
+ * @param {any} str - Input string
281
+ * @returns {string} Unquoted string
282
+ */
283
+ export const unquote = (str) => {
284
+ if (str === null || str === undefined) return '';
285
+ const text = String(str).trim();
286
+
287
+ // Remove matching quotes
288
+ if ((text.startsWith('"') && text.endsWith('"')) ||
289
+ (text.startsWith("'") && text.endsWith("'")) ||
290
+ (text.startsWith('`') && text.endsWith('`'))) {
291
+ return text.slice(1, -1);
292
+ }
293
+
294
+ return text;
295
+ };
296
+
297
+ /**
298
+ * Wrap text to specified width
299
+ * @param {any} str - Input string
300
+ * @param {number} width - Maximum line width
301
+ * @param {string} breakChar - Character to break on
302
+ * @returns {string} Wrapped text
303
+ */
304
+ export const wrap = (str, width = 80, breakChar = ' ') => {
305
+ if (str === null || str === undefined) return '';
306
+ const text = String(str);
307
+
308
+ if (width <= 0) return text;
309
+
310
+ const words = text.split(breakChar);
311
+ const lines = [];
312
+ let currentLine = '';
313
+
314
+ for (const word of words) {
315
+ if (currentLine.length + word.length + 1 <= width) {
316
+ currentLine += (currentLine ? breakChar : '') + word;
317
+ } else {
318
+ if (currentLine) lines.push(currentLine);
319
+ currentLine = word;
320
+ }
321
+ }
322
+
323
+ if (currentLine) lines.push(currentLine);
324
+ return lines.join('\n');
325
+ };
326
+
327
+ /**
328
+ * Extract file extension from path
329
+ * @param {any} path - File path
330
+ * @returns {string} File extension with dot
331
+ */
332
+ export const extname = (path) => {
333
+ if (path === null || path === undefined) return '';
334
+ const pathStr = String(path);
335
+ const filename = pathStr.split('/').pop().split('\\').pop();
336
+ const lastDot = filename.lastIndexOf('.');
337
+ return lastDot > 0 ? filename.substring(lastDot) : '';
338
+ };
339
+
340
+ // Collection of all text filters for easy import
341
+ export const textFilters = {
342
+ upper,
343
+ lower,
344
+ trim,
345
+ replace,
346
+ replaceFirst,
347
+ truncate,
348
+ slug,
349
+ title,
350
+ capitalize,
351
+ camelCase,
352
+ pascalCase,
353
+ kebabCase,
354
+ snakeCase,
355
+ constantCase,
356
+ escape,
357
+ reverse,
358
+ wordCount,
359
+ length,
360
+ padStart,
361
+ padEnd,
362
+ indent,
363
+ quote,
364
+ unquote,
365
+ wrap,
366
+ extname
367
+ };
368
+
369
+ export default textFilters;
package/src/index.js ADDED
@@ -0,0 +1,109 @@
1
+ /**
2
+ * @kgn/templates - Deterministic Nunjucks Template System
3
+ *
4
+ * Enhanced with KGEN Injection Operations System:
5
+ * - Nunjucks integration with custom filters
6
+ * - Frontmatter parsing (YAML)
7
+ * - Variable extraction and validation
8
+ * - Deterministic rendering
9
+ * - Template linting for determinism
10
+ * - Atomic, idempotent injection operations (NEW)
11
+ * - Marker-based targeting and rollback capabilities (NEW)
12
+ */
13
+
14
+ export { TemplateEngine, EnhancedTemplateEngine } from './engine/template-engine.js';
15
+ export { TemplateInheritanceEngine } from './inheritance/index.js';
16
+ export { createCustomFilters } from './filters/index.js';
17
+ export { FrontmatterParser } from './parser/frontmatter.js';
18
+ export { VariableExtractor } from './parser/variables.js';
19
+ export { TemplateLinter } from './linter/determinism.js';
20
+ export { DeterministicRenderer } from './renderer/deterministic.js';
21
+
22
+ // Base template system
23
+ export * as BaseTemplates from './base/index.js';
24
+ export { KGenTemplateBase } from './base/template-base.js';
25
+
26
+ // Template packs
27
+ export * as NextJSTemplates from './templates/nextjs/index.js';
28
+ export * as OfficeTemplates from './templates/office/index.js';
29
+ export * as LaTeXTemplates from './templates/latex/index.js';
30
+
31
+ // Re-export commonly used types and utilities
32
+ export {
33
+ renderTemplate,
34
+ validateTemplate,
35
+ extractVariables,
36
+ lintTemplate
37
+ } from './utils/template-utils.js';
38
+
39
+ // INJECTION SYSTEM EXPORTS (NEW)
40
+ // Main injection API
41
+ export {
42
+ inject,
43
+ dryRun,
44
+ undo,
45
+ initializeInjection,
46
+ getOperationHistory,
47
+ batchInject,
48
+ validateInjectionConfig,
49
+ getInjectionStatus,
50
+ clearCaches
51
+ } from './injection/api.js';
52
+
53
+ // Integration helpers
54
+ export {
55
+ enhanceKgenWithInjection,
56
+ createInjectionTemplateLoader,
57
+ injectionValidators
58
+ } from './injection/integration.js';
59
+
60
+ // Core injection classes (for advanced usage)
61
+ export {
62
+ InjectionEngine,
63
+ AtomicWriter,
64
+ IdempotencyManager,
65
+ TargetResolver,
66
+ ValidationEngine,
67
+ RollbackManager
68
+ } from './injection/index.js';
69
+
70
+ // Constants and types
71
+ export {
72
+ INJECTION_MODES,
73
+ ERROR_CODES,
74
+ DEFAULT_CONFIG as INJECTION_CONFIG
75
+ } from './injection/constants.js';
76
+
77
+ // Enhanced engine factory with inheritance and injection support
78
+ export function createEngine(options = {}) {
79
+ // Use EnhancedTemplateEngine by default for inheritance support
80
+ const engineClass = options.useBasicEngine ? TemplateEngine : EnhancedTemplateEngine;
81
+ const engine = new engineClass(options);
82
+
83
+ // Enhance with injection capabilities by default
84
+ if (options.enableInjection !== false) {
85
+ return enhanceKgenWithInjection(engine, options.injection || {});
86
+ }
87
+
88
+ return engine;
89
+ }
90
+
91
+ // Inheritance-specific engine factory
92
+ export function createInheritanceEngine(options = {}) {
93
+ return new TemplateInheritanceEngine(options);
94
+ }
95
+
96
+ // Enhanced template engine factory (inheritance + all features)
97
+ export function createEnhancedEngine(options = {}) {
98
+ return new EnhancedTemplateEngine({
99
+ enableInheritance: true,
100
+ deterministicMode: true,
101
+ enableCache: true,
102
+ ...options
103
+ });
104
+ }
105
+
106
+ // Injection-specific engine factory
107
+ export function createInjectionEngine(injectionConfig = {}) {
108
+ return initializeInjection(injectionConfig);
109
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Template Inheritance System - Placeholder for future implementation
3
+ */
4
+
5
+ export class TemplateInheritanceEngine {
6
+ constructor(options = {}) {
7
+ this.options = options;
8
+ }
9
+
10
+ async processTemplate(templatePath, context = {}) {
11
+ // Basic implementation for build compatibility
12
+ return {
13
+ templatePath,
14
+ compiled: { content: '', metadata: {} },
15
+ metadata: { processingTime: 0 },
16
+ dependencies: [],
17
+ hash: ''
18
+ };
19
+ }
20
+
21
+ getStats() {
22
+ return {
23
+ templatesProcessed: 0,
24
+ blocksProcessed: 0,
25
+ macrosExpanded: 0,
26
+ cacheHits: 0,
27
+ cacheMisses: 0
28
+ };
29
+ }
30
+
31
+ async clearCache() {
32
+ // No-op for now
33
+ }
34
+
35
+ getConfig() {
36
+ return this.options;
37
+ }
38
+ }
39
+
40
+ export default TemplateInheritanceEngine;