@unrdf/kgn 5.0.1 → 26.4.3

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 +18 -14
  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
package/package.json CHANGED
@@ -1,12 +1,14 @@
1
1
  {
2
2
  "name": "@unrdf/kgn",
3
- "version": "5.0.1",
3
+ "version": "26.4.3",
4
4
  "description": "Deterministic Nunjucks template system with custom filters and frontmatter support",
5
5
  "type": "module",
6
- "main": "src/index.js",
6
+ "main": "dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
7
8
  "exports": {
8
9
  ".": {
9
- "import": "./src/index.js"
10
+ "import": "./dist/index.mjs",
11
+ "types": "./dist/index.d.ts"
10
12
  },
11
13
  "./engine": {
12
14
  "import": "./src/engine/index.js"
@@ -23,26 +25,27 @@
23
25
  "./templates/*": "./src/templates/*"
24
26
  },
25
27
  "dependencies": {
28
+ "@unrdf/core": "26.4.3",
26
29
  "fs-extra": "^11.3.1",
27
30
  "gray-matter": "^4.0.3",
28
31
  "nunjucks": "^3.2.4",
29
32
  "yaml": "^2.8.1",
30
- "@unrdf/core": "5.0.1",
31
- "@unrdf/test-utils": "5.0.1"
33
+ "zod": "^3.25.76"
32
34
  },
33
35
  "devDependencies": {
34
- "@amiceli/vitest-cucumber": "^4.1.1",
36
+ "@amiceli/vitest-cucumber": "^6.2.0",
35
37
  "@babel/parser": "^7.28.5",
36
38
  "@babel/traverse": "^7.28.5",
37
39
  "comment-parser": "^1.4.1",
38
40
  "eslint": "^8.56.0",
39
41
  "nodemon": "^3.0.1",
40
- "vitest": "^2.1.9"
42
+ "vitest": "^4.0.16"
41
43
  },
42
44
  "engines": {
43
45
  "node": ">=18.0.0",
44
- "pnpm": ">=8.0.0"
46
+ "pnpm": ">=7.0.0"
45
47
  },
48
+ "sideEffects": false,
46
49
  "keywords": [
47
50
  "unrdf",
48
51
  "kgn",
@@ -57,13 +60,13 @@
57
60
  "license": "MIT",
58
61
  "repository": {
59
62
  "type": "git",
60
- "url": "https://github.com/seanchatmangpt/unrdf.git",
63
+ "url": "https://github.com/unrdf/unrdf.git",
61
64
  "directory": "packages/kgn"
62
65
  },
63
66
  "bugs": {
64
- "url": "https://github.com/seanchatmangpt/unrdf/issues"
67
+ "url": "https://github.com/unrdf/unrdf/issues"
65
68
  },
66
- "homepage": "https://github.com/seanchatmangpt/unrdf#readme",
69
+ "homepage": "https://github.com/unrdf/unrdf#readme",
67
70
  "files": [
68
71
  "src/",
69
72
  "README.md"
@@ -72,7 +75,8 @@
72
75
  "access": "public"
73
76
  },
74
77
  "scripts": {
75
- "build": "echo '@unrdf/kgn - ES modules verified'",
78
+ "build": "unbuild && tsc --emitDeclarationOnly || true",
79
+ "build:types": "tsc --emitDeclarationOnly",
76
80
  "build:watch": "nodemon --watch src --exec 'npm run build'",
77
81
  "test": "vitest run",
78
82
  "test:watch": "vitest",
@@ -80,8 +84,8 @@
80
84
  "test:cucumber:watch": "vitest --config vitest.cucumber.config.js --watch",
81
85
  "test:all": "npm run test && npm run test:cucumber",
82
86
  "test:coverage": "vitest run --coverage",
83
- "lint": "eslint src/ --ext .js,.mjs",
84
- "lint:fix": "eslint src/ --ext .js,.mjs --fix",
87
+ "lint": "eslint src/",
88
+ "lint:fix": "eslint src/ --fix",
85
89
  "dev": "nodemon --watch src --exec 'echo @unrdf/kgn dev mode - watching for changes'",
86
90
  "typecheck": "echo '@unrdf/kgn - JavaScript validation not configured'",
87
91
  "clean": "rm -rf coverage .nyc_output dist build",
@@ -5,9 +5,15 @@
5
5
  * All filter templates are designed to be composable and reusable
6
6
  */
7
7
 
8
- import crypto from 'crypto';
8
+ import _crypto from 'crypto';
9
9
 
10
+ /**
11
+ *
12
+ */
10
13
  export class KGenFilterTemplates {
14
+ /**
15
+ *
16
+ */
11
17
  constructor(options = {}) {
12
18
  this.options = {
13
19
  deterministicMode: options.deterministicMode !== false,
package/src/base/index.js CHANGED
@@ -1,31 +1,36 @@
1
1
  /**
2
2
  * KGEN Base Templates - Foundation template system
3
- *
3
+ *
4
4
  * Exports all base template classes and utilities for building
5
5
  * deterministic, reusable template components
6
6
  */
7
7
 
8
- // Base template foundation
9
- export { KGenTemplateBase } from './template-base.js';
8
+ // Import classes for local use
9
+ import { KGenTemplateBase } from './template-base.js';
10
+ import { KGenMacroTemplates } from './macro-templates.js';
11
+ import { KGenFilterTemplates } from './filter-templates.js';
12
+ import { KGenSHACLTemplates } from './shacl-templates.js';
13
+ import { KGenInjectionTargets } from './injection-targets.js';
10
14
 
11
- // Macro system for reusable components
15
+ // Re-export for external use
16
+ export { KGenTemplateBase } from './template-base.js';
12
17
  export { KGenMacroTemplates } from './macro-templates.js';
13
-
14
- // Filter system for deterministic data processing
15
18
  export { KGenFilterTemplates } from './filter-templates.js';
16
-
17
- // SHACL validation templates for RDF knowledge graphs
18
19
  export { KGenSHACLTemplates } from './shacl-templates.js';
19
-
20
- // Injection targets for code modification
21
20
  export { KGenInjectionTargets } from './injection-targets.js';
22
21
 
23
22
  // Convenience factory function
23
+ /**
24
+ *
25
+ */
24
26
  export function createBaseTemplate(options = {}) {
25
27
  return new KGenTemplateBase(options);
26
28
  }
27
29
 
28
30
  // Template system factory
31
+ /**
32
+ *
33
+ */
29
34
  export function createTemplateSystem(options = {}) {
30
35
  const base = new KGenTemplateBase(options);
31
36
  const macros = new KGenMacroTemplates(options);
@@ -5,7 +5,13 @@
5
5
  * Supports marker-based, semantic-based, and position-based injection strategies
6
6
  */
7
7
 
8
+ /**
9
+ *
10
+ */
8
11
  export class KGenInjectionTargets {
12
+ /**
13
+ *
14
+ */
9
15
  constructor(options = {}) {
10
16
  this.options = {
11
17
  deterministicMode: options.deterministicMode !== false,
@@ -5,7 +5,13 @@
5
5
  * All macros are deterministic and compatible with KGEN template engine
6
6
  */
7
7
 
8
+ /**
9
+ *
10
+ */
8
11
  export class KGenMacroTemplates {
12
+ /**
13
+ *
14
+ */
9
15
  constructor(options = {}) {
10
16
  this.options = {
11
17
  deterministicMode: options.deterministicMode !== false,
@@ -5,7 +5,13 @@
5
5
  * for validating RDF knowledge graphs used in KGEN template generation
6
6
  */
7
7
 
8
+ /**
9
+ *
10
+ */
8
11
  export class KGenSHACLTemplates {
12
+ /**
13
+ *
14
+ */
9
15
  constructor(options = {}) {
10
16
  this.options = {
11
17
  deterministicMode: options.deterministicMode !== false,
@@ -9,7 +9,13 @@ import crypto from 'crypto';
9
9
  import { KGenTemplateEngine } from '../core/kgen-engine.js';
10
10
  import { TemplateInheritanceEngine } from '../inheritance/index.js';
11
11
 
12
+ /**
13
+ *
14
+ */
12
15
  export class KGenTemplateBase {
16
+ /**
17
+ *
18
+ */
13
19
  constructor(options = {}) {
14
20
  this.options = {
15
21
  strictMode: options.strictMode !== false,
@@ -231,7 +237,7 @@ export default {{ moduleName }};
231
237
  /**
232
238
  * Generate template from registered template
233
239
  */
234
- async generateFromTemplate(templateName, context = {}, options = {}) {
240
+ async generateFromTemplate(templateName, context = {}, _options = {}) {
235
241
  if (!this.templates.has(templateName)) {
236
242
  throw new Error(`Template '${templateName}' not found`);
237
243
  }
@@ -1,6 +1,8 @@
1
1
  /**
2
- * KGEN Attestor - Generate cryptographic attestations for deterministic output
2
+ * @file KGEN Attestor - Generate cryptographic attestations for deterministic output
3
+ * @module @unrdf/kgn/core/attestor
3
4
  *
5
+ * @description
4
6
  * Provides:
5
7
  * - Content integrity verification
6
8
  * - Deterministic output attestation
@@ -10,7 +12,21 @@
10
12
 
11
13
  import crypto from 'crypto';
12
14
 
15
+ /**
16
+ * KGEN Attestor class - Cryptographic attestation system
17
+ */
13
18
  export class KGenAttestor {
19
+ /**
20
+ * Create a new KGenAttestor instance
21
+ * @param {Object} [options={}] - Attestor configuration options
22
+ * @param {boolean} [options.enableAttestation=true] - Enable attestation generation
23
+ * @param {string} [options.algorithm='sha256'] - Hash algorithm to use
24
+ * @param {string} [options.attestorId='kgen-templates'] - Attestor identifier
25
+ * @param {string} [options.version='2.0.0'] - Attestor version
26
+ * @param {string} [options.staticBuildTime] - Static timestamp for deterministic mode
27
+ * @param {boolean} [options.deterministicMode=true] - Enable deterministic mode
28
+ * @param {boolean} [options.includeMetadata=true] - Include additional metadata
29
+ */
14
30
  constructor(options = {}) {
15
31
  this.options = {
16
32
  enableAttestation: options.enableAttestation !== false,
@@ -26,6 +42,9 @@ export class KGenAttestor {
26
42
 
27
43
  /**
28
44
  * Generate attestation for rendered content
45
+ * @param {string} content - Content to attest
46
+ * @param {Object} [context={}] - Context data for attestation
47
+ * @returns {Promise<Object>} Attestation result
29
48
  */
30
49
  async attest(content, context = {}) {
31
50
  if (!this.options.enableAttestation) {
@@ -61,6 +80,9 @@ export class KGenAttestor {
61
80
 
62
81
  /**
63
82
  * Create complete attestation record
83
+ * @param {string} content - Content to attest
84
+ * @param {Object} [context={}] - Context data
85
+ * @returns {Promise<Object>} Complete attestation record
64
86
  */
65
87
  async createAttestation(content, context = {}) {
66
88
  const contentHash = this.createContentHash(content);
@@ -104,6 +126,9 @@ export class KGenAttestor {
104
126
 
105
127
  /**
106
128
  * Verify attestation integrity
129
+ * @param {string} content - Content to verify
130
+ * @param {Object} attestation - Attestation to verify against
131
+ * @returns {Promise<Object>} Verification result
107
132
  */
108
133
  async verify(content, attestation) {
109
134
  try {
@@ -159,6 +184,10 @@ export class KGenAttestor {
159
184
 
160
185
  /**
161
186
  * Create reproducibility proof
187
+ * @param {string} content - Content to test reproducibility
188
+ * @param {Object} context - Context data
189
+ * @param {number} [iterations=3] - Number of iterations to test
190
+ * @returns {Promise<Object>} Reproducibility proof
162
191
  */
163
192
  async createReproducibilityProof(content, context, iterations = 3) {
164
193
  const proofs = [];
@@ -194,6 +223,8 @@ export class KGenAttestor {
194
223
 
195
224
  /**
196
225
  * Create audit trail for template execution
226
+ * @param {Array<Object>} [steps=[]] - Execution steps to audit
227
+ * @returns {Object} Audit trail record
197
228
  */
198
229
  createAuditTrail(steps = []) {
199
230
  return {
@@ -214,6 +245,9 @@ export class KGenAttestor {
214
245
 
215
246
  /**
216
247
  * Compare two attestations
248
+ * @param {Object} attestation1 - First attestation
249
+ * @param {Object} attestation2 - Second attestation
250
+ * @returns {Object} Comparison result
217
251
  */
218
252
  compareAttestations(attestation1, attestation2) {
219
253
  const differences = [];
@@ -264,6 +298,8 @@ export class KGenAttestor {
264
298
 
265
299
  /**
266
300
  * Create content hash using specified algorithm
301
+ * @param {string} content - Content to hash
302
+ * @returns {string} Content hash as hex string
267
303
  */
268
304
  createContentHash(content) {
269
305
  return crypto.createHash(this.options.algorithm)
@@ -273,6 +309,8 @@ export class KGenAttestor {
273
309
 
274
310
  /**
275
311
  * Create signature for attestation data
312
+ * @param {Object} data - Data to sign
313
+ * @returns {string} Signature as hex string
276
314
  */
277
315
  createSignature(data) {
278
316
  // Simple signature using hash of data + attestor ID
@@ -284,6 +322,9 @@ export class KGenAttestor {
284
322
 
285
323
  /**
286
324
  * Get additional metadata if enabled
325
+ * @param {string} content - Content being attested
326
+ * @param {Object} context - Context data
327
+ * @returns {Object} Additional metadata
287
328
  */
288
329
  getAdditionalMetadata(content, context) {
289
330
  return {
@@ -306,6 +347,7 @@ export class KGenAttestor {
306
347
 
307
348
  /**
308
349
  * Get deterministic or real timestamp
350
+ * @returns {string} ISO 8601 timestamp
309
351
  */
310
352
  getTimestamp() {
311
353
  return this.options.deterministicMode ?
@@ -315,6 +357,9 @@ export class KGenAttestor {
315
357
 
316
358
  /**
317
359
  * Export attestation in standard format
360
+ * @param {Object} attestation - Attestation to export
361
+ * @param {string} [format='json'] - Export format (json, compact, yaml, base64)
362
+ * @returns {string} Exported attestation
318
363
  */
319
364
  exportAttestation(attestation, format = 'json') {
320
365
  switch (format.toLowerCase()) {
@@ -340,6 +385,9 @@ export class KGenAttestor {
340
385
 
341
386
  /**
342
387
  * Import attestation from standard format
388
+ * @param {string} data - Attestation data to import
389
+ * @param {string} [format='json'] - Import format (json, compact, base64)
390
+ * @returns {Object} Imported attestation object
343
391
  */
344
392
  importAttestation(data, format = 'json') {
345
393
  try {
@@ -362,6 +410,7 @@ export class KGenAttestor {
362
410
 
363
411
  /**
364
412
  * Get attestor statistics
413
+ * @returns {Object} Attestor configuration and capabilities
365
414
  */
366
415
  getStats() {
367
416
  return {
@@ -1,6 +1,8 @@
1
1
  /**
2
- * KGEN Filter System - Deterministic template filters
2
+ * @file KGEN Filter System - Deterministic template filters
3
+ * @module @unrdf/kgn/core/filters
3
4
  *
5
+ * @description
4
6
  * Implements all v1 Lock specification filters:
5
7
  * - Text: upper, lower, trim, replace, split, join, slice
6
8
  * - Data: default, unique, sort, groupby, map, sum, count
@@ -12,7 +14,16 @@
12
14
 
13
15
  import crypto from 'crypto';
14
16
 
17
+ /**
18
+ * KGEN Filters class - Template filter system
19
+ */
15
20
  export class KGenFilters {
21
+ /**
22
+ * Create a new KGenFilters instance
23
+ * @param {Object} [options={}] - Filter configuration options
24
+ * @param {boolean} [options.deterministicMode=true] - Enable deterministic mode
25
+ * @param {boolean} [options.strictMode=true] - Enable strict error handling
26
+ */
16
27
  constructor(options = {}) {
17
28
  this.options = {
18
29
  deterministicMode: options.deterministicMode !== false,
@@ -26,6 +37,12 @@ export class KGenFilters {
26
37
 
27
38
  /**
28
39
  * Register a filter function
40
+ * @param {string} name - Filter name
41
+ * @param {Function} filterFunction - Filter implementation
42
+ * @param {Object} [options={}] - Filter options
43
+ * @param {boolean} [options.deterministic=true] - Whether filter is deterministic
44
+ * @param {string} [options.description=''] - Filter description
45
+ * @param {string} [options.category='custom'] - Filter category
29
46
  */
30
47
  register(name, filterFunction, options = {}) {
31
48
  if (typeof filterFunction !== 'function') {
@@ -42,6 +59,10 @@ export class KGenFilters {
42
59
 
43
60
  /**
44
61
  * Apply filter to value
62
+ * @param {string} filterName - Name of filter to apply
63
+ * @param {*} value - Value to filter
64
+ * @param {...*} args - Additional filter arguments
65
+ * @returns {*} Filtered value
45
66
  */
46
67
  apply(filterName, value, ...args) {
47
68
  const filter = this.filters.get(filterName);
@@ -70,6 +91,8 @@ export class KGenFilters {
70
91
 
71
92
  /**
72
93
  * Check if filter exists
94
+ * @param {string} filterName - Filter name to check
95
+ * @returns {boolean} True if filter exists
73
96
  */
74
97
  has(filterName) {
75
98
  return this.filters.has(filterName);
@@ -77,6 +100,7 @@ export class KGenFilters {
77
100
 
78
101
  /**
79
102
  * Get filter count
103
+ * @returns {number} Number of registered filters
80
104
  */
81
105
  getFilterCount() {
82
106
  return this.filters.size;
@@ -84,6 +108,7 @@ export class KGenFilters {
84
108
 
85
109
  /**
86
110
  * Register all core filters
111
+ * Registers text, data, format, RDF, validation, CAS, and utility filters
87
112
  */
88
113
  registerCoreFilters() {
89
114
  this.registerTextFilters();
@@ -97,6 +122,7 @@ export class KGenFilters {
97
122
 
98
123
  /**
99
124
  * Text processing filters
125
+ * Registers upper, lower, trim, replace, split, join, slice filters
100
126
  */
101
127
  registerTextFilters() {
102
128
  this.register('upper', (str) => String(str || '').toUpperCase(), {
@@ -146,10 +172,27 @@ export class KGenFilters {
146
172
  category: 'text',
147
173
  description: 'Extract substring by position'
148
174
  });
175
+
176
+ this.register('truncate', (str, length = 10, suffix = '...') => {
177
+ const s = String(str || '');
178
+ if (s.length <= length) return s;
179
+ return s.slice(0, length) + suffix;
180
+ }, {
181
+ category: 'text',
182
+ description: 'Truncate string to specified length'
183
+ });
184
+
185
+ this.register('reverse', (str) => {
186
+ return String(str || '').split('').reverse().join('');
187
+ }, {
188
+ category: 'text',
189
+ description: 'Reverse string'
190
+ });
149
191
  }
150
192
 
151
193
  /**
152
194
  * Data processing filters
195
+ * Registers default, unique, sort, groupby, map, sum, count filters
153
196
  */
154
197
  registerDataFilters() {
155
198
  this.register('default', (value, defaultValue = '') => {
@@ -356,6 +399,7 @@ export class KGenFilters {
356
399
 
357
400
  /**
358
401
  * Validation filters
402
+ * Registers shaclReport, validate filters
359
403
  */
360
404
  registerValidationFilters() {
361
405
  this.register('shaclReport', (data, shaclShapes = {}) => {
@@ -425,6 +469,7 @@ export class KGenFilters {
425
469
 
426
470
  /**
427
471
  * Utility filters
472
+ * Registers typeof, length, keys, values, entries, formatDate filters
428
473
  */
429
474
  registerUtilityFilters() {
430
475
  // Non-deterministic filters that throw in deterministic mode
@@ -460,6 +505,93 @@ export class KGenFilters {
460
505
  deterministic: false,
461
506
  description: 'Generate UUID (non-deterministic)'
462
507
  });
508
+
509
+ // Date arithmetic filters
510
+ this.register('dateAdd', (dateStr, amount, unit = 'day') => {
511
+ const date = new Date(dateStr);
512
+ switch (unit) {
513
+ case 'day':
514
+ date.setDate(date.getDate() + amount);
515
+ break;
516
+ case 'month':
517
+ date.setMonth(date.getMonth() + amount);
518
+ break;
519
+ case 'year':
520
+ date.setFullYear(date.getFullYear() + amount);
521
+ break;
522
+ default:
523
+ throw new Error(`Unknown unit: ${unit}`);
524
+ }
525
+ return date.toISOString();
526
+ }, {
527
+ category: 'utility',
528
+ description: 'Add time to date'
529
+ });
530
+
531
+ this.register('dateSub', (dateStr, amount, unit = 'day') => {
532
+ const date = new Date(dateStr);
533
+ switch (unit) {
534
+ case 'day':
535
+ date.setDate(date.getDate() - amount);
536
+ break;
537
+ case 'month':
538
+ date.setMonth(date.getMonth() - amount);
539
+ break;
540
+ case 'year':
541
+ date.setFullYear(date.getFullYear() - amount);
542
+ break;
543
+ default:
544
+ throw new Error(`Unknown unit: ${unit}`);
545
+ }
546
+ return date.toISOString();
547
+ }, {
548
+ category: 'utility',
549
+ description: 'Subtract time from date'
550
+ });
551
+
552
+ // Path utility filters
553
+ this.register('resolve', (base, relative) => {
554
+ // Simple path resolution (normalize and join)
555
+ const baseParts = base.split('/').filter(p => p);
556
+ const relativeParts = relative.split('/').filter(p => p);
557
+
558
+ for (const part of relativeParts) {
559
+ if (part === '..') {
560
+ baseParts.pop();
561
+ } else if (part !== '.') {
562
+ baseParts.push(part);
563
+ }
564
+ }
565
+
566
+ return '/' + baseParts.join('/');
567
+ }, {
568
+ category: 'utility',
569
+ description: 'Resolve path'
570
+ });
571
+
572
+ this.register('relative', (from, to) => {
573
+ // Simple relative path calculation
574
+ const fromParts = from.split('/').filter(p => p);
575
+ const toParts = to.split('/').filter(p => p);
576
+
577
+ // Find common base
578
+ let i = 0;
579
+ while (i < fromParts.length && i < toParts.length && fromParts[i] === toParts[i]) {
580
+ i++;
581
+ }
582
+
583
+ // Go up from 'from' directory
584
+ const upCount = fromParts.length - i;
585
+ const upParts = Array(upCount).fill('..');
586
+
587
+ // Then append remaining 'to' parts
588
+ const remainingParts = toParts.slice(i);
589
+
590
+ return [...upParts, ...remainingParts].join('/');
591
+ }, {
592
+ category: 'utility',
593
+ description: 'Calculate relative path'
594
+ });
463
595
  }
464
596
 
465
597
  /**
@@ -489,6 +621,7 @@ export class KGenFilters {
489
621
 
490
622
  /**
491
623
  * Get filter statistics
624
+ * @returns {Object} Filter system statistics
492
625
  */
493
626
  getStats() {
494
627
  const categories = {};
package/src/core/index.js CHANGED
@@ -5,6 +5,10 @@
5
5
  * Features: Deterministic rendering, custom filters, attestation support
6
6
  */
7
7
 
8
+ // Import class first so it can be used in factory function
9
+ import { KGenTemplateEngine } from './kgen-engine.js';
10
+
11
+ // Export all classes
8
12
  export { KGenTemplateEngine } from './kgen-engine.js';
9
13
  export { KGenParser } from './parser.js';
10
14
  export { KGenFilters } from './filters.js';
@@ -13,9 +17,12 @@ export { KGenPostProcessor } from './post-processor.js';
13
17
  export { KGenAttestor } from './attestor.js';
14
18
 
15
19
  // Convenience factory function
20
+ /**
21
+ *
22
+ */
16
23
  export function createKGenEngine(options = {}) {
17
24
  return new KGenTemplateEngine(options);
18
25
  }
19
26
 
20
27
  // Default export
21
- export { KGenTemplateEngine as default };
28
+ export default KGenTemplateEngine;