@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.
- package/LICENSE +21 -0
- package/README.md +210 -0
- package/package.json +90 -0
- package/src/MIGRATION_COMPLETE.md +186 -0
- package/src/PORT-MAP.md +302 -0
- package/src/base/filter-templates.js +479 -0
- package/src/base/index.js +92 -0
- package/src/base/injection-targets.js +583 -0
- package/src/base/macro-templates.js +298 -0
- package/src/base/macro-templates.js.bak +461 -0
- package/src/base/shacl-templates.js +617 -0
- package/src/base/template-base.js +388 -0
- package/src/core/attestor.js +381 -0
- package/src/core/filters.js +518 -0
- package/src/core/index.js +21 -0
- package/src/core/kgen-engine.js +372 -0
- package/src/core/parser.js +447 -0
- package/src/core/post-processor.js +313 -0
- package/src/core/renderer.js +469 -0
- package/src/doc-generator/cli.mjs +122 -0
- package/src/doc-generator/index.mjs +28 -0
- package/src/doc-generator/mdx-generator.mjs +71 -0
- package/src/doc-generator/nav-generator.mjs +136 -0
- package/src/doc-generator/parser.mjs +291 -0
- package/src/doc-generator/rdf-builder.mjs +306 -0
- package/src/doc-generator/scanner.mjs +189 -0
- package/src/engine/index.js +42 -0
- package/src/engine/pipeline.js +448 -0
- package/src/engine/renderer.js +604 -0
- package/src/engine/template-engine.js +566 -0
- package/src/filters/array.js +436 -0
- package/src/filters/data.js +479 -0
- package/src/filters/index.js +270 -0
- package/src/filters/rdf.js +264 -0
- package/src/filters/text.js +369 -0
- package/src/index.js +109 -0
- package/src/inheritance/index.js +40 -0
- package/src/injection/api.js +260 -0
- package/src/injection/atomic-writer.js +327 -0
- package/src/injection/constants.js +136 -0
- package/src/injection/idempotency-manager.js +295 -0
- package/src/injection/index.js +28 -0
- package/src/injection/injection-engine.js +378 -0
- package/src/injection/integration.js +339 -0
- package/src/injection/modes/index.js +341 -0
- package/src/injection/rollback-manager.js +373 -0
- package/src/injection/target-resolver.js +323 -0
- package/src/injection/tests/atomic-writer.test.js +382 -0
- package/src/injection/tests/injection-engine.test.js +611 -0
- package/src/injection/tests/integration.test.js +392 -0
- package/src/injection/tests/run-tests.js +283 -0
- package/src/injection/validation-engine.js +547 -0
- package/src/linter/determinism-linter.js +473 -0
- package/src/linter/determinism.js +410 -0
- package/src/linter/index.js +6 -0
- package/src/linter/test-doubles.js +475 -0
- package/src/parser/frontmatter.js +228 -0
- package/src/parser/variables.js +344 -0
- package/src/renderer/deterministic.js +245 -0
- package/src/renderer/index.js +6 -0
- package/src/templates/latex/academic-paper.njk +186 -0
- package/src/templates/latex/index.js +104 -0
- package/src/templates/nextjs/app-page.njk +66 -0
- package/src/templates/nextjs/index.js +80 -0
- package/src/templates/office/docx/document.njk +368 -0
- package/src/templates/office/index.js +79 -0
- package/src/templates/office/word-report.njk +129 -0
- 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;
|