@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,436 @@
1
+ /**
2
+ * KGEN Array Filters - P0 Essential Array Processing
3
+ *
4
+ * The 20% of array filters that provide 80% of value
5
+ * Deterministic implementations with comprehensive error handling
6
+ */
7
+
8
+ /**
9
+ * Join array elements with separator
10
+ * @param {any} arr - Input array
11
+ * @param {string} separator - Separator string
12
+ * @returns {string} Joined string
13
+ */
14
+ export const join = (arr, separator = ',') => {
15
+ if (!Array.isArray(arr)) {
16
+ if (arr === null || arr === undefined) return '';
17
+ return String(arr);
18
+ }
19
+ return arr.join(String(separator));
20
+ };
21
+
22
+ /**
23
+ * Split string into array
24
+ * @param {any} str - Input string
25
+ * @param {string} separator - Separator to split on
26
+ * @returns {Array} Array of strings
27
+ */
28
+ export const split = (str, separator = ',') => {
29
+ if (str === null || str === undefined) return [];
30
+ return String(str).split(String(separator));
31
+ };
32
+
33
+ /**
34
+ * Get unique values from array (deterministic order)
35
+ * @param {any} arr - Input array
36
+ * @returns {Array} Array with unique values
37
+ */
38
+ export const unique = (arr) => {
39
+ if (!Array.isArray(arr)) return arr;
40
+ return [...new Set(arr)];
41
+ };
42
+
43
+ /**
44
+ * Sort array (deterministic, stable sort)
45
+ * @param {any} arr - Input array
46
+ * @param {string} key - Optional key to sort by for objects
47
+ * @returns {Array} Sorted array
48
+ */
49
+ export const sort = (arr, key = null) => {
50
+ if (!Array.isArray(arr)) return arr;
51
+
52
+ const sorted = [...arr].sort((a, b) => {
53
+ let aVal = key ? a[key] : a;
54
+ let bVal = key ? b[key] : b;
55
+
56
+ // Handle numeric comparison for numbers, string comparison for strings
57
+ if (typeof aVal === 'number' && typeof bVal === 'number') {
58
+ return aVal - bVal;
59
+ }
60
+
61
+ // Convert to strings for consistent comparison of mixed types
62
+ aVal = String(aVal);
63
+ bVal = String(bVal);
64
+
65
+ if (aVal < bVal) return -1;
66
+ if (aVal > bVal) return 1;
67
+ return 0;
68
+ });
69
+
70
+ return sorted;
71
+ };
72
+
73
+ /**
74
+ * Reverse array
75
+ * @param {any} arr - Input array
76
+ * @returns {Array} Reversed array
77
+ */
78
+ export const reverse = (arr) => {
79
+ if (!Array.isArray(arr)) return arr;
80
+ return [...arr].reverse();
81
+ };
82
+
83
+ /**
84
+ * Get first n elements
85
+ * @param {any} arr - Input array
86
+ * @param {number} n - Number of elements to take
87
+ * @returns {any} First element if n=1, array otherwise
88
+ */
89
+ export const first = (arr, n = 1) => {
90
+ if (!Array.isArray(arr)) return arr;
91
+ if (n === 1) return arr[0];
92
+ return arr.slice(0, Math.max(0, n));
93
+ };
94
+
95
+ /**
96
+ * Get last n elements
97
+ * @param {any} arr - Input array
98
+ * @param {number} n - Number of elements to take
99
+ * @returns {any} Last element if n=1, array otherwise
100
+ */
101
+ export const last = (arr, n = 1) => {
102
+ if (!Array.isArray(arr)) return arr;
103
+ if (n === 1) return arr[arr.length - 1];
104
+ return arr.slice(-Math.max(0, n));
105
+ };
106
+
107
+ /**
108
+ * Get array length
109
+ * @param {any} arr - Input array
110
+ * @returns {number} Array length
111
+ */
112
+ export const length = (arr) => {
113
+ if (!Array.isArray(arr)) return 0;
114
+ return arr.length;
115
+ };
116
+
117
+ /**
118
+ * Check if array is empty
119
+ * @param {any} arr - Input array
120
+ * @returns {boolean} True if empty
121
+ */
122
+ export const isEmpty = (arr) => {
123
+ if (!Array.isArray(arr)) return true;
124
+ return arr.length === 0;
125
+ };
126
+
127
+ /**
128
+ * Filter array by property value
129
+ * @param {any} arr - Input array
130
+ * @param {string} key - Property key to filter by
131
+ * @param {any} value - Value to match
132
+ * @returns {Array} Filtered array
133
+ */
134
+ export const filterBy = (arr, key, value) => {
135
+ if (!Array.isArray(arr)) return [];
136
+ return arr.filter(item => {
137
+ const itemValue = key ? item[key] : item;
138
+ return itemValue === value;
139
+ });
140
+ };
141
+
142
+ /**
143
+ * Group array by property value
144
+ * @param {any} arr - Input array
145
+ * @param {string} key - Property key to group by
146
+ * @returns {Object} Grouped object
147
+ */
148
+ export const groupBy = (arr, key) => {
149
+ if (!Array.isArray(arr)) return {};
150
+
151
+ return arr.reduce((groups, item) => {
152
+ const groupKey = key ? item[key] : item;
153
+ const groupKeyStr = String(groupKey);
154
+
155
+ if (!groups[groupKeyStr]) {
156
+ groups[groupKeyStr] = [];
157
+ }
158
+ groups[groupKeyStr].push(item);
159
+ return groups;
160
+ }, {});
161
+ };
162
+
163
+ /**
164
+ * Map array to property values
165
+ * @param {any} arr - Input array
166
+ * @param {string} key - Property key to extract
167
+ * @returns {Array} Array of values
168
+ */
169
+ export const mapBy = (arr, key) => {
170
+ if (!Array.isArray(arr)) return [];
171
+ return arr.map(item => key ? item[key] : item);
172
+ };
173
+
174
+ /**
175
+ * Sort array by property (deterministic)
176
+ * @param {any} arr - Input array
177
+ * @param {string} key - Property key to sort by
178
+ * @returns {Array} Sorted array
179
+ */
180
+ export const sortBy = (arr, key) => {
181
+ if (!Array.isArray(arr)) return arr;
182
+
183
+ return [...arr].sort((a, b) => {
184
+ const aVal = String(key ? a[key] : a);
185
+ const bVal = String(key ? b[key] : b);
186
+
187
+ if (aVal < bVal) return -1;
188
+ if (aVal > bVal) return 1;
189
+ return 0;
190
+ });
191
+ };
192
+
193
+ /**
194
+ * Chunk array into groups of specified size
195
+ * @param {any} arr - Input array
196
+ * @param {number} size - Chunk size
197
+ * @returns {Array} Array of chunks
198
+ */
199
+ export const chunk = (arr, size = 1) => {
200
+ if (!Array.isArray(arr)) return [];
201
+ const chunkSize = Math.max(1, Math.floor(size));
202
+ const chunks = [];
203
+
204
+ for (let i = 0; i < arr.length; i += chunkSize) {
205
+ chunks.push(arr.slice(i, i + chunkSize));
206
+ }
207
+
208
+ return chunks;
209
+ };
210
+
211
+ /**
212
+ * Flatten nested arrays
213
+ * @param {any} arr - Input array
214
+ * @param {number} depth - Depth to flatten (default: Infinity)
215
+ * @returns {Array} Flattened array
216
+ */
217
+ export const flatten = (arr, depth = Infinity) => {
218
+ if (!Array.isArray(arr)) return arr;
219
+ return arr.flat(depth);
220
+ };
221
+
222
+ /**
223
+ * Get random element (deterministic based on content hash)
224
+ * @param {any} arr - Input array
225
+ * @returns {any} Deterministic "random" element
226
+ */
227
+ export const sample = (arr) => {
228
+ if (!Array.isArray(arr) || arr.length === 0) return null;
229
+
230
+ // Create deterministic "random" index based on array content
231
+ const contentHash = JSON.stringify(arr).split('').reduce((hash, char) => {
232
+ hash = ((hash << 5) - hash) + char.charCodeAt(0);
233
+ return hash & hash; // Convert to 32-bit integer
234
+ }, 0);
235
+
236
+ const index = Math.abs(contentHash) % arr.length;
237
+ return arr[index];
238
+ };
239
+
240
+ /**
241
+ * Check if array contains value
242
+ * @param {any} arr - Input array
243
+ * @param {any} value - Value to search for
244
+ * @returns {boolean} True if contains value
245
+ */
246
+ export const contains = (arr, value) => {
247
+ if (!Array.isArray(arr)) return false;
248
+ return arr.includes(value);
249
+ };
250
+
251
+ /**
252
+ * Get intersection of two arrays
253
+ * @param {any} arr1 - First array
254
+ * @param {any} arr2 - Second array
255
+ * @returns {Array} Intersection array
256
+ */
257
+ export const intersect = (arr1, arr2) => {
258
+ if (!Array.isArray(arr1) || !Array.isArray(arr2)) return [];
259
+ return arr1.filter(item => arr2.includes(item));
260
+ };
261
+
262
+ /**
263
+ * Get difference between two arrays (items in arr1 but not arr2)
264
+ * @param {any} arr1 - First array
265
+ * @param {any} arr2 - Second array
266
+ * @returns {Array} Difference array
267
+ */
268
+ export const difference = (arr1, arr2) => {
269
+ if (!Array.isArray(arr1)) return [];
270
+ if (!Array.isArray(arr2)) return [...arr1];
271
+ return arr1.filter(item => !arr2.includes(item));
272
+ };
273
+
274
+ /**
275
+ * Concatenate arrays
276
+ * @param {any} arr1 - First array
277
+ * @param {any} arr2 - Second array
278
+ * @returns {Array} Concatenated array
279
+ */
280
+ export const concat = (arr1, arr2) => {
281
+ const a1 = Array.isArray(arr1) ? arr1 : [arr1];
282
+ const a2 = Array.isArray(arr2) ? arr2 : [arr2];
283
+ return [...a1, ...a2];
284
+ };
285
+
286
+ /**
287
+ * Zip two arrays together into pairs
288
+ * @param {any} arr1 - First array
289
+ * @param {any} arr2 - Second array
290
+ * @returns {Array} Array of [item1, item2] pairs
291
+ */
292
+ export const zip = (arr1, arr2) => {
293
+ if (!Array.isArray(arr1) || !Array.isArray(arr2)) return [];
294
+ const minLength = Math.min(arr1.length, arr2.length);
295
+ const result = [];
296
+
297
+ for (let i = 0; i < minLength; i++) {
298
+ result.push([arr1[i], arr2[i]]);
299
+ }
300
+
301
+ return result;
302
+ };
303
+
304
+ /**
305
+ * Unzip array of pairs into two arrays
306
+ * @param {any} arr - Array of pairs
307
+ * @returns {Array} [array1, array2]
308
+ */
309
+ export const unzip = (arr) => {
310
+ if (!Array.isArray(arr)) return [[], []];
311
+ const result1 = [];
312
+ const result2 = [];
313
+
314
+ for (const item of arr) {
315
+ if (Array.isArray(item) && item.length >= 2) {
316
+ result1.push(item[0]);
317
+ result2.push(item[1]);
318
+ }
319
+ }
320
+
321
+ return [result1, result2];
322
+ };
323
+
324
+ /**
325
+ * Transpose a 2D array (matrix)
326
+ * @param {any} matrix - 2D array
327
+ * @returns {Array} Transposed matrix
328
+ */
329
+ export const transpose = (matrix) => {
330
+ if (!Array.isArray(matrix) || matrix.length === 0) return [];
331
+ if (!Array.isArray(matrix[0])) return matrix;
332
+
333
+ const rows = matrix.length;
334
+ const cols = matrix[0].length;
335
+ const result = [];
336
+
337
+ for (let col = 0; col < cols; col++) {
338
+ const newRow = [];
339
+ for (let row = 0; row < rows; row++) {
340
+ if (Array.isArray(matrix[row]) && col < matrix[row].length) {
341
+ newRow.push(matrix[row][col]);
342
+ }
343
+ }
344
+ result.push(newRow);
345
+ }
346
+
347
+ return result;
348
+ };
349
+
350
+ /**
351
+ * Get minimum value from array
352
+ * @param {any} arr - Input array
353
+ * @returns {any} Minimum value
354
+ */
355
+ export const min = (arr) => {
356
+ if (!Array.isArray(arr) || arr.length === 0) return null;
357
+ return Math.min(...arr.filter(x => typeof x === 'number' && !isNaN(x)));
358
+ };
359
+
360
+ /**
361
+ * Get maximum value from array
362
+ * @param {any} arr - Input array
363
+ * @returns {any} Maximum value
364
+ */
365
+ export const max = (arr) => {
366
+ if (!Array.isArray(arr) || arr.length === 0) return null;
367
+ return Math.max(...arr.filter(x => typeof x === 'number' && !isNaN(x)));
368
+ };
369
+
370
+ /**
371
+ * Sum all numeric values in array, optionally by property
372
+ * @param {any} arr - Input array
373
+ * @param {string} key - Optional property key to sum by
374
+ * @returns {number} Sum of values
375
+ */
376
+ export const sum = (arr, key = null) => {
377
+ if (!Array.isArray(arr)) return 0;
378
+
379
+ if (key) {
380
+ // Sum by property
381
+ return arr
382
+ .map(item => item && typeof item === 'object' ? item[key] : 0)
383
+ .filter(x => typeof x === 'number' && !isNaN(x))
384
+ .reduce((sum, val) => sum + val, 0);
385
+ }
386
+
387
+ // Sum numeric values directly
388
+ return arr
389
+ .filter(x => typeof x === 'number' && !isNaN(x))
390
+ .reduce((sum, val) => sum + val, 0);
391
+ };
392
+
393
+ /**
394
+ * Calculate average of numeric values in array
395
+ * @param {any} arr - Input array
396
+ * @returns {number} Average value
397
+ */
398
+ export const avg = (arr) => {
399
+ if (!Array.isArray(arr) || arr.length === 0) return 0;
400
+ const numbers = arr.filter(x => typeof x === 'number' && !isNaN(x));
401
+ return numbers.length > 0 ? sum(numbers) / numbers.length : 0;
402
+ };
403
+
404
+ // Collection of all array filters for easy import
405
+ export const arrayFilters = {
406
+ join,
407
+ split,
408
+ unique,
409
+ sort,
410
+ reverse,
411
+ first,
412
+ last,
413
+ length,
414
+ isEmpty,
415
+ filterBy,
416
+ groupBy,
417
+ mapBy,
418
+ map: mapBy, // Alias for compatibility
419
+ sortBy,
420
+ chunk,
421
+ flatten,
422
+ sample,
423
+ contains,
424
+ intersect,
425
+ difference,
426
+ concat,
427
+ zip,
428
+ unzip,
429
+ transpose,
430
+ min,
431
+ max,
432
+ sum,
433
+ avg
434
+ };
435
+
436
+ export default arrayFilters;