@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,136 @@
1
+ /**
2
+ * @fileoverview Navigation generator for Nextra _meta.json files
3
+ * Creates sidebar navigation from scanned packages
4
+ */
5
+
6
+ /**
7
+ * Generate _meta.json for Nextra navigation
8
+ * @param {Object} groupedFiles - Files grouped by package/module
9
+ * @param {Object} options - Generation options
10
+ * @returns {Object} Nextra _meta.json structure
11
+ */
12
+ export function generateNavigation(groupedFiles, options = {}) {
13
+ const {
14
+ rootTitle = 'API Reference',
15
+ includeIndex = true,
16
+ } = options;
17
+
18
+ const meta = {
19
+ index: includeIndex ? 'Overview' : undefined,
20
+ };
21
+
22
+ // Add each package to navigation
23
+ Object.entries(groupedFiles).forEach(([pkgName, pkgData]) => {
24
+ const pkgKey = `api/${pkgName}`;
25
+
26
+ // Package entry
27
+ meta[pkgKey] = {
28
+ title: `@unrdf/${pkgName}`,
29
+ type: 'page',
30
+ };
31
+
32
+ // Add modules within package
33
+ Object.keys(pkgData.modules).forEach(moduleName => {
34
+ if (moduleName === 'root') {
35
+ // Files in root of src/ - add directly under package
36
+ meta[`${pkgKey}/index`] = `${pkgName} API`;
37
+ } else {
38
+ // Submodule
39
+ meta[`${pkgKey}/${moduleName}`] = {
40
+ title: moduleName.charAt(0).toUpperCase() + moduleName.slice(1),
41
+ };
42
+ }
43
+ });
44
+ });
45
+
46
+ return meta;
47
+ }
48
+
49
+ /**
50
+ * Generate package index page content
51
+ * @param {string} packageName - Package name
52
+ * @param {Object} packageData - Package data from scanner
53
+ * @returns {string} MDX content for package index
54
+ */
55
+ export function generatePackageIndex(packageName, packageData) {
56
+ const { modules, relativePath } = packageData;
57
+ const moduleNames = Object.keys(modules);
58
+
59
+ return `# @unrdf/${packageName}
60
+
61
+ > Package: \`${relativePath}\`
62
+
63
+ ## Modules
64
+
65
+ This package contains ${moduleNames.length} module${moduleNames.length !== 1 ? 's' : ''}:
66
+
67
+ ${moduleNames.map(name => {
68
+ const files = modules[name];
69
+ return `### ${name === 'root' ? 'Core' : name.charAt(0).toUpperCase() + name.slice(1)}
70
+
71
+ ${files.length} source file${files.length !== 1 ? 's' : ''}
72
+
73
+ ${files.slice(0, 5).map(f => {
74
+ const fileName = f.split('/').pop();
75
+ const moduleLink = name === 'root' ? fileName.replace(/\.(m?js)$/, '') : `${name}/${fileName.replace(/\.(m?js)$/, '')}`;
76
+ return `- [\`${fileName}\`](./${moduleLink})`;
77
+ }).join('\n')}
78
+
79
+ ${files.length > 5 ? `\n...and ${files.length - 5} more` : ''}
80
+ `;
81
+ }).join('\n')}
82
+
83
+ ---
84
+
85
+ *Generated by @unrdf/kgn documentation generator*
86
+ `;
87
+ }
88
+
89
+ /**
90
+ * Generate root API index page
91
+ * @param {Object} groupedFiles - All packages and modules
92
+ * @returns {string} MDX content for API index
93
+ */
94
+ export function generateAPIIndex(groupedFiles) {
95
+ const packages = Object.entries(groupedFiles);
96
+
97
+ return `# API Reference
98
+
99
+ Welcome to the @unrdf workspace API reference.
100
+
101
+ ## Packages
102
+
103
+ The workspace contains ${packages.length} package${packages.length !== 1 ? 's' : ''}:
104
+
105
+ ${packages.map(([pkgName, pkgData]) => {
106
+ const moduleCount = Object.keys(pkgData.modules).length;
107
+ const fileCount = pkgData.sourceFiles.length;
108
+
109
+ return `### [@unrdf/${pkgName}](./${pkgName})
110
+
111
+ ${moduleCount} module${moduleCount !== 1 ? 's' : ''}, ${fileCount} source file${fileCount !== 1 ? 's' : ''}
112
+
113
+ ${pkgData.modules ? Object.keys(pkgData.modules).slice(0, 3).map(m =>
114
+ `- ${m === 'root' ? 'Core' : m}`
115
+ ).join('\n') : ''}
116
+ ${moduleCount > 3 ? `- ...and ${moduleCount - 3} more` : ''}
117
+ `;
118
+ }).join('\n')}
119
+
120
+ ---
121
+
122
+ ## Search
123
+
124
+ Use the search bar above to find specific functions, classes, or modules.
125
+
126
+ ---
127
+
128
+ *Documentation generated from JSDoc comments using @unrdf/kgn*
129
+ `;
130
+ }
131
+
132
+ export default {
133
+ generateNavigation,
134
+ generatePackageIndex,
135
+ generateAPIIndex,
136
+ };
@@ -0,0 +1,291 @@
1
+ /**
2
+ * @fileoverview JSDoc Parser with Babel AST Analysis
3
+ * Extracts documentation from JavaScript/TypeScript source files
4
+ */
5
+
6
+ import { parse as parseComments } from 'comment-parser';
7
+ import { parse as parseBabel } from '@babel/parser';
8
+ import traverse from '@babel/traverse';
9
+ import { readFileSync } from 'fs';
10
+ import { relative } from 'path';
11
+
12
+ /**
13
+ * Parse JSDoc comments and AST from a source file
14
+ * @param {string} filePath - Absolute path to source file
15
+ * @param {string} rootDir - Workspace root directory
16
+ * @returns {Object} Parsed documentation data
17
+ */
18
+ export function parseFile(filePath, rootDir = process.cwd()) {
19
+ const code = readFileSync(filePath, 'utf-8');
20
+ const relativePath = relative(rootDir, filePath);
21
+
22
+ // Parse JSDoc comments
23
+ const comments = extractJSDocComments(code);
24
+
25
+ // Parse AST
26
+ const ast = parseBabel(code, {
27
+ sourceType: 'module',
28
+ plugins: ['jsx', 'typescript'],
29
+ });
30
+
31
+ // Extract exports and their metadata
32
+ const exports = extractExports(ast, comments);
33
+
34
+ // Extract imports for cross-referencing
35
+ const imports = extractImports(ast);
36
+
37
+ return {
38
+ file: filePath,
39
+ relativePath,
40
+ exports,
41
+ imports,
42
+ comments: comments.length,
43
+ };
44
+ }
45
+
46
+ /**
47
+ * Extract JSDoc comments from source code
48
+ * @param {string} code - Source code
49
+ * @returns {Array} Parsed JSDoc blocks
50
+ */
51
+ function extractJSDocComments(code) {
52
+ // Extract all comments matching JSDoc pattern
53
+ const jsDocRegex = /\/\*\*[\s\S]*?\*\//g;
54
+ const matches = code.match(jsDocRegex) || [];
55
+
56
+ return matches.map(block => {
57
+ const parsed = parseComments(block)[0];
58
+ return {
59
+ description: parsed.description,
60
+ tags: parsed.tags.map(tag => ({
61
+ tag: tag.tag,
62
+ name: tag.name,
63
+ type: tag.type,
64
+ description: tag.description,
65
+ optional: tag.optional,
66
+ default: tag.default,
67
+ })),
68
+ source: block,
69
+ };
70
+ });
71
+ }
72
+
73
+ /**
74
+ * Extract exports from AST
75
+ * @param {Object} ast - Babel AST
76
+ * @param {Array} comments - Parsed JSDoc comments
77
+ * @returns {Array} Export declarations with metadata
78
+ */
79
+ function extractExports(ast, comments) {
80
+ const exports = [];
81
+ let commentIndex = 0;
82
+
83
+ traverse.default(ast, {
84
+ ExportNamedDeclaration(path) {
85
+ const { declaration } = path.node;
86
+
87
+ if (!declaration) return;
88
+
89
+ // Find associated JSDoc comment
90
+ const jsdoc = commentIndex < comments.length ? comments[commentIndex++] : null;
91
+
92
+ if (declaration.type === 'FunctionDeclaration') {
93
+ exports.push(extractFunctionExport(declaration, jsdoc));
94
+ } else if (declaration.type === 'VariableDeclaration') {
95
+ declaration.declarations.forEach(decl => {
96
+ if (decl.init && (decl.init.type === 'FunctionExpression' || decl.init.type === 'ArrowFunctionExpression')) {
97
+ exports.push(extractFunctionExport(decl, jsdoc));
98
+ } else {
99
+ exports.push(extractVariableExport(decl, jsdoc));
100
+ }
101
+ });
102
+ } else if (declaration.type === 'ClassDeclaration') {
103
+ exports.push(extractClassExport(declaration, jsdoc));
104
+ }
105
+ },
106
+
107
+ ExportDefaultDeclaration(path) {
108
+ const { declaration } = path.node;
109
+ const jsdoc = commentIndex < comments.length ? comments[commentIndex++] : null;
110
+
111
+ exports.push({
112
+ name: 'default',
113
+ type: declaration.type,
114
+ isDefault: true,
115
+ jsdoc,
116
+ });
117
+ },
118
+ });
119
+
120
+ return exports;
121
+ }
122
+
123
+ /**
124
+ * Extract function export metadata
125
+ * @param {Object} node - AST node
126
+ * @param {Object} jsdoc - JSDoc comment
127
+ * @returns {Object} Function metadata
128
+ */
129
+ function extractFunctionExport(node, jsdoc) {
130
+ const name = node.id?.name || node.key?.name;
131
+ const params = extractParams(node.params || node.init?.params || []);
132
+
133
+ // Extract metadata from JSDoc
134
+ const paramDocs = jsdoc?.tags.filter(t => t.tag === 'param') || [];
135
+ const returnDoc = jsdoc?.tags.find(t => t.tag === 'returns' || t.tag === 'return');
136
+ const examples = jsdoc?.tags.filter(t => t.tag === 'example').map(t => t.description) || [];
137
+
138
+ return {
139
+ name,
140
+ type: 'function',
141
+ description: jsdoc?.description || '',
142
+ params: params.map((param, i) => ({
143
+ name: param.name,
144
+ type: paramDocs[i]?.type || 'any',
145
+ description: paramDocs[i]?.description || '',
146
+ optional: param.optional || paramDocs[i]?.optional || false,
147
+ default: param.default || paramDocs[i]?.default,
148
+ })),
149
+ returns: {
150
+ type: returnDoc?.type || 'void',
151
+ description: returnDoc?.description || '',
152
+ },
153
+ examples,
154
+ async: node.async || node.init?.async || false,
155
+ };
156
+ }
157
+
158
+ /**
159
+ * Extract variable export metadata
160
+ * @param {Object} node - AST node
161
+ * @param {Object} jsdoc - JSDoc comment
162
+ * @returns {Object} Variable metadata
163
+ */
164
+ function extractVariableExport(node, jsdoc) {
165
+ return {
166
+ name: node.id?.name,
167
+ type: 'variable',
168
+ description: jsdoc?.description || '',
169
+ valueType: node.init?.type || 'unknown',
170
+ };
171
+ }
172
+
173
+ /**
174
+ * Extract class export metadata
175
+ * @param {Object} node - AST node
176
+ * @param {Object} jsdoc - JSDoc comment
177
+ * @returns {Object} Class metadata
178
+ */
179
+ function extractClassExport(node, jsdoc) {
180
+ const methods = [];
181
+
182
+ node.body.body.forEach(member => {
183
+ if (member.type === 'ClassMethod') {
184
+ methods.push({
185
+ name: member.key.name,
186
+ kind: member.kind, // 'constructor', 'method', 'get', 'set'
187
+ static: member.static,
188
+ async: member.async,
189
+ params: extractParams(member.params),
190
+ });
191
+ }
192
+ });
193
+
194
+ return {
195
+ name: node.id.name,
196
+ type: 'class',
197
+ description: jsdoc?.description || '',
198
+ methods,
199
+ extends: node.superClass?.name,
200
+ };
201
+ }
202
+
203
+ /**
204
+ * Extract parameter metadata from AST nodes
205
+ * @param {Array} params - Parameter AST nodes
206
+ * @returns {Array} Parameter metadata
207
+ */
208
+ function extractParams(params) {
209
+ return params.map(param => {
210
+ if (param.type === 'Identifier') {
211
+ return { name: param.name, optional: false };
212
+ } else if (param.type === 'AssignmentPattern') {
213
+ return {
214
+ name: param.left.name,
215
+ optional: true,
216
+ default: extractDefaultValue(param.right),
217
+ };
218
+ } else if (param.type === 'RestElement') {
219
+ return { name: param.argument.name, rest: true };
220
+ } else if (param.type === 'ObjectPattern') {
221
+ return { name: 'options', destructured: true };
222
+ }
223
+ return { name: 'unknown' };
224
+ });
225
+ }
226
+
227
+ /**
228
+ * Extract default value from AST node
229
+ * @param {Object} node - AST node
230
+ * @returns {any} Default value
231
+ */
232
+ function extractDefaultValue(node) {
233
+ if (node.type === 'Literal') return node.value;
234
+ if (node.type === 'NumericLiteral') return node.value;
235
+ if (node.type === 'StringLiteral') return node.value;
236
+ if (node.type === 'BooleanLiteral') return node.value;
237
+ if (node.type === 'NullLiteral') return null;
238
+ if (node.type === 'ObjectExpression') return {};
239
+ if (node.type === 'ArrayExpression') return [];
240
+ return undefined;
241
+ }
242
+
243
+ /**
244
+ * Extract import statements for cross-referencing
245
+ * @param {Object} ast - Babel AST
246
+ * @returns {Array} Import declarations
247
+ */
248
+ function extractImports(ast) {
249
+ const imports = [];
250
+
251
+ traverse.default(ast, {
252
+ ImportDeclaration(path) {
253
+ const { source, specifiers } = path.node;
254
+
255
+ imports.push({
256
+ source: source.value,
257
+ specifiers: specifiers.map(spec => ({
258
+ imported: spec.imported?.name || spec.local.name,
259
+ local: spec.local.name,
260
+ type: spec.type, // 'ImportSpecifier', 'ImportDefaultSpecifier', 'ImportNamespaceSpecifier'
261
+ })),
262
+ });
263
+ },
264
+ });
265
+
266
+ return imports;
267
+ }
268
+
269
+ /**
270
+ * Parse multiple files in batch
271
+ * @param {Array<string>} filePaths - Array of file paths
272
+ * @param {string} rootDir - Workspace root directory
273
+ * @returns {Array} Parsed data for all files
274
+ */
275
+ export function parseFiles(filePaths, rootDir = process.cwd()) {
276
+ return filePaths.map(filePath => {
277
+ try {
278
+ return parseFile(filePath, rootDir);
279
+ } catch (error) {
280
+ console.error(`Failed to parse ${filePath}:`, error.message);
281
+ return {
282
+ file: filePath,
283
+ error: error.message,
284
+ exports: [],
285
+ imports: [],
286
+ };
287
+ }
288
+ });
289
+ }
290
+
291
+ export default { parseFile, parseFiles };