@principal-ai/codebase-composition 0.1.0

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 (129) hide show
  1. package/README.md +67 -0
  2. package/dist/index.d.ts +9 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +23 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/modules/DependencyLayerModule.d.ts +44 -0
  7. package/dist/modules/DependencyLayerModule.d.ts.map +1 -0
  8. package/dist/modules/DependencyLayerModule.js +286 -0
  9. package/dist/modules/DependencyLayerModule.js.map +1 -0
  10. package/dist/modules/FileSystemModule.d.ts +30 -0
  11. package/dist/modules/FileSystemModule.d.ts.map +1 -0
  12. package/dist/modules/FileSystemModule.js +46 -0
  13. package/dist/modules/FileSystemModule.js.map +1 -0
  14. package/dist/modules/FileTypeLayerModule.d.ts +34 -0
  15. package/dist/modules/FileTypeLayerModule.d.ts.map +1 -0
  16. package/dist/modules/FileTypeLayerModule.js +169 -0
  17. package/dist/modules/FileTypeLayerModule.js.map +1 -0
  18. package/dist/modules/FrameworkLayerModule.d.ts +22 -0
  19. package/dist/modules/FrameworkLayerModule.d.ts.map +1 -0
  20. package/dist/modules/FrameworkLayerModule.js +388 -0
  21. package/dist/modules/FrameworkLayerModule.js.map +1 -0
  22. package/dist/modules/PackageLayerModule.d.ts +23 -0
  23. package/dist/modules/PackageLayerModule.d.ts.map +1 -0
  24. package/dist/modules/PackageLayerModule.js +810 -0
  25. package/dist/modules/PackageLayerModule.js.map +1 -0
  26. package/dist/modules/TypeExtractionModule.d.ts +37 -0
  27. package/dist/modules/TypeExtractionModule.d.ts.map +1 -0
  28. package/dist/modules/TypeExtractionModule.js +180 -0
  29. package/dist/modules/TypeExtractionModule.js.map +1 -0
  30. package/dist/modules/VersionControlLayerModule.d.ts +10 -0
  31. package/dist/modules/VersionControlLayerModule.d.ts.map +1 -0
  32. package/dist/modules/VersionControlLayerModule.js +32 -0
  33. package/dist/modules/VersionControlLayerModule.js.map +1 -0
  34. package/dist/modules/__fixtures__/typescript-packages/complex-types/src/index.d.ts +4 -0
  35. package/dist/modules/__fixtures__/typescript-packages/complex-types/src/index.d.ts.map +1 -0
  36. package/dist/modules/__fixtures__/typescript-packages/complex-types/src/index.js +7 -0
  37. package/dist/modules/__fixtures__/typescript-packages/complex-types/src/index.js.map +1 -0
  38. package/dist/modules/__fixtures__/typescript-packages/complex-types/src/models/category.d.ts +15 -0
  39. package/dist/modules/__fixtures__/typescript-packages/complex-types/src/models/category.d.ts.map +1 -0
  40. package/dist/modules/__fixtures__/typescript-packages/complex-types/src/models/category.js +3 -0
  41. package/dist/modules/__fixtures__/typescript-packages/complex-types/src/models/category.js.map +1 -0
  42. package/dist/modules/__fixtures__/typescript-packages/complex-types/src/models/product.d.ts +34 -0
  43. package/dist/modules/__fixtures__/typescript-packages/complex-types/src/models/product.d.ts.map +1 -0
  44. package/dist/modules/__fixtures__/typescript-packages/complex-types/src/models/product.js +23 -0
  45. package/dist/modules/__fixtures__/typescript-packages/complex-types/src/models/product.js.map +1 -0
  46. package/dist/modules/__fixtures__/typescript-packages/simple-types/index.d.ts +39 -0
  47. package/dist/modules/__fixtures__/typescript-packages/simple-types/index.d.ts.map +1 -0
  48. package/dist/modules/__fixtures__/typescript-packages/simple-types/index.js +39 -0
  49. package/dist/modules/__fixtures__/typescript-packages/simple-types/index.js.map +1 -0
  50. package/dist/modules/extractors/TypeScriptExtractor.d.ts +18 -0
  51. package/dist/modules/extractors/TypeScriptExtractor.d.ts.map +1 -0
  52. package/dist/modules/extractors/TypeScriptExtractor.js +361 -0
  53. package/dist/modules/extractors/TypeScriptExtractor.js.map +1 -0
  54. package/dist/modules/index.d.ts +13 -0
  55. package/dist/modules/index.d.ts.map +1 -0
  56. package/dist/modules/index.js +21 -0
  57. package/dist/modules/index.js.map +1 -0
  58. package/dist/providers/GitVersionControlProvider.d.ts +108 -0
  59. package/dist/providers/GitVersionControlProvider.d.ts.map +1 -0
  60. package/dist/providers/GitVersionControlProvider.js +380 -0
  61. package/dist/providers/GitVersionControlProvider.js.map +1 -0
  62. package/dist/providers/PackageManagerApiProvider.d.ts +78 -0
  63. package/dist/providers/PackageManagerApiProvider.d.ts.map +1 -0
  64. package/dist/providers/PackageManagerApiProvider.js +14 -0
  65. package/dist/providers/PackageManagerApiProvider.js.map +1 -0
  66. package/dist/providers/index.d.ts +4 -0
  67. package/dist/providers/index.d.ts.map +1 -0
  68. package/dist/providers/index.js +10 -0
  69. package/dist/providers/index.js.map +1 -0
  70. package/dist/services/FilesystemService.d.ts +59 -0
  71. package/dist/services/FilesystemService.d.ts.map +1 -0
  72. package/dist/services/FilesystemService.js +391 -0
  73. package/dist/services/FilesystemService.js.map +1 -0
  74. package/dist/services/index.d.ts +2 -0
  75. package/dist/services/index.d.ts.map +1 -0
  76. package/dist/services/index.js +7 -0
  77. package/dist/services/index.js.map +1 -0
  78. package/dist/types/file-system.d.ts +7 -0
  79. package/dist/types/file-system.d.ts.map +1 -0
  80. package/dist/types/file-system.js +7 -0
  81. package/dist/types/file-system.js.map +1 -0
  82. package/dist/types/index.d.ts +4 -0
  83. package/dist/types/index.d.ts.map +1 -0
  84. package/dist/types/index.js +3 -0
  85. package/dist/types/index.js.map +1 -0
  86. package/dist/types/layer-types.d.ts +187 -0
  87. package/dist/types/layer-types.d.ts.map +1 -0
  88. package/dist/types/layer-types.js +7 -0
  89. package/dist/types/layer-types.js.map +1 -0
  90. package/dist/types/version-control-layer.d.ts +53 -0
  91. package/dist/types/version-control-layer.d.ts.map +1 -0
  92. package/dist/types/version-control-layer.js +3 -0
  93. package/dist/types/version-control-layer.js.map +1 -0
  94. package/dist/types/workspace-boundaries.d.ts +17 -0
  95. package/dist/types/workspace-boundaries.d.ts.map +1 -0
  96. package/dist/types/workspace-boundaries.js +7 -0
  97. package/dist/types/workspace-boundaries.js.map +1 -0
  98. package/package.json +42 -0
  99. package/src/index.ts +62 -0
  100. package/src/modules/DependencyLayerModule.ts +329 -0
  101. package/src/modules/FileSystemModule.ts +65 -0
  102. package/src/modules/FileTypeLayerModule.ts +199 -0
  103. package/src/modules/FrameworkLayerModule.ts +437 -0
  104. package/src/modules/PackageLayerModule.ts +979 -0
  105. package/src/modules/TypeExtractionModule.test.ts +340 -0
  106. package/src/modules/TypeExtractionModule.ts +180 -0
  107. package/src/modules/VersionControlLayerModule.ts +31 -0
  108. package/src/modules/__fixtures__/typescript-packages/complex-types/package.json +6 -0
  109. package/src/modules/__fixtures__/typescript-packages/complex-types/src/index.ts +6 -0
  110. package/src/modules/__fixtures__/typescript-packages/complex-types/src/models/category.ts +15 -0
  111. package/src/modules/__fixtures__/typescript-packages/complex-types/src/models/product.ts +48 -0
  112. package/src/modules/__fixtures__/typescript-packages/javascript-only/index.js +18 -0
  113. package/src/modules/__fixtures__/typescript-packages/javascript-only/package.json +5 -0
  114. package/src/modules/__fixtures__/typescript-packages/simple-types/index.ts +53 -0
  115. package/src/modules/__fixtures__/typescript-packages/simple-types/package.json +6 -0
  116. package/src/modules/extractors/README.md +55 -0
  117. package/src/modules/extractors/TypeScriptExtractor.ts +409 -0
  118. package/src/modules/index.ts +13 -0
  119. package/src/providers/GitVersionControlProvider.ts +500 -0
  120. package/src/providers/PackageManagerApiProvider.ts +108 -0
  121. package/src/providers/README.md +88 -0
  122. package/src/providers/index.ts +17 -0
  123. package/src/services/FilesystemService.ts +530 -0
  124. package/src/services/index.ts +2 -0
  125. package/src/types/file-system.ts +11 -0
  126. package/src/types/index.ts +24 -0
  127. package/src/types/layer-types.ts +264 -0
  128. package/src/types/version-control-layer.ts +87 -0
  129. package/src/types/workspace-boundaries.ts +17 -0
@@ -0,0 +1,810 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.PackageLayerModule = void 0;
37
+ const TOML = __importStar(require("js-toml"));
38
+ const pip_requirements_js_1 = require("pip-requirements-js");
39
+ // Node.js package.json parser
40
+ class NodePackageParser {
41
+ constructor() {
42
+ this.manifestFileName = 'package.json';
43
+ this.packageType = 'node';
44
+ }
45
+ canParse(filename) {
46
+ return filename.endsWith('package.json');
47
+ }
48
+ parseContent(rawContent) {
49
+ try {
50
+ return JSON.parse(rawContent);
51
+ }
52
+ catch {
53
+ return null;
54
+ }
55
+ }
56
+ extractPackageData(content, path) {
57
+ // Handle cases where path is just "package.json" (root package)
58
+ // or a proper path like "packages/foo/package.json"
59
+ let packagePath;
60
+ if (path === 'package.json') {
61
+ packagePath = ''; // Root directory - empty string for root
62
+ }
63
+ else if (path.endsWith('/package.json')) {
64
+ packagePath = path.slice(0, -13); // Remove '/package.json'
65
+ }
66
+ else {
67
+ // Shouldn't happen, but handle gracefully
68
+ packagePath = path;
69
+ }
70
+ const availableCommands = this.extractCommands(content, packagePath);
71
+ return {
72
+ name: content.name || 'unnamed',
73
+ version: content.version,
74
+ path: packagePath,
75
+ packageManager: 'unknown',
76
+ dependencies: content.dependencies || {},
77
+ devDependencies: content.devDependencies || {},
78
+ peerDependencies: content.peerDependencies || {},
79
+ isMonorepoRoot: !!content.workspaces,
80
+ isWorkspace: false, // Will be determined by context
81
+ parentPackage: undefined,
82
+ availableCommands,
83
+ };
84
+ }
85
+ extractCommands(content, packagePath) {
86
+ const commands = [];
87
+ // Extract npm scripts
88
+ if (content.scripts) {
89
+ Object.entries(content.scripts).forEach(([name, script]) => {
90
+ // Use npm as default, will be updated with correct package manager later
91
+ commands.push({
92
+ name,
93
+ command: `npm run ${name}`,
94
+ description: script.length > 50 ? script.substring(0, 47) + '...' : script,
95
+ type: 'script',
96
+ workingDirectory: packagePath,
97
+ });
98
+ });
99
+ }
100
+ // Add standard npm commands - these will also be updated with correct package manager
101
+ const standardCommands = [
102
+ { name: 'install', command: 'npm install', description: 'Install dependencies' },
103
+ { name: 'ci', command: 'npm ci', description: 'Clean install dependencies' },
104
+ { name: 'update', command: 'npm update', description: 'Update dependencies' },
105
+ ];
106
+ standardCommands.forEach(cmd => {
107
+ commands.push({
108
+ ...cmd,
109
+ type: 'standard',
110
+ workingDirectory: packagePath,
111
+ });
112
+ });
113
+ return commands;
114
+ }
115
+ detectWorkspaces(content) {
116
+ if (Array.isArray(content.workspaces)) {
117
+ return content.workspaces;
118
+ }
119
+ if (content.workspaces &&
120
+ typeof content.workspaces === 'object' &&
121
+ 'packages' in content.workspaces) {
122
+ return content.workspaces.packages || null;
123
+ }
124
+ return null;
125
+ }
126
+ detectPackageManager(content, lockFiles) {
127
+ // Check for packageManager field (corepack)
128
+ if (content.packageManager) {
129
+ const pm = content.packageManager.split('@')[0];
130
+ if (['npm', 'yarn', 'pnpm'].includes(pm)) {
131
+ return pm;
132
+ }
133
+ }
134
+ // Check lock files
135
+ if (lockFiles.some((f) => f.endsWith('yarn.lock')))
136
+ return 'yarn';
137
+ if (lockFiles.some((f) => f.endsWith('pnpm-lock.yaml')))
138
+ return 'pnpm';
139
+ if (lockFiles.some((f) => f.endsWith('package-lock.json')))
140
+ return 'npm';
141
+ // Default to npm instead of unknown for Node packages
142
+ return 'npm';
143
+ }
144
+ detectConfigs(packagePath, fileTree, manifestContent) {
145
+ const configs = {};
146
+ // Define config patterns for Node/JavaScript projects
147
+ const configPatterns = {
148
+ knip: [
149
+ 'knip.json',
150
+ 'knip.jsonc',
151
+ '.knip.json',
152
+ 'knip.config.js',
153
+ 'knip.config.ts',
154
+ 'knip.config.mjs',
155
+ ],
156
+ eslint: [
157
+ '.eslintrc',
158
+ '.eslintrc.js',
159
+ '.eslintrc.json',
160
+ '.eslintrc.yml',
161
+ '.eslintrc.yaml',
162
+ 'eslint.config.js',
163
+ 'eslint.config.mjs',
164
+ '.eslintrc.cjs',
165
+ ],
166
+ prettier: [
167
+ '.prettierrc',
168
+ '.prettierrc.js',
169
+ '.prettierrc.json',
170
+ '.prettierrc.yml',
171
+ '.prettierrc.yaml',
172
+ 'prettier.config.js',
173
+ '.prettierrc.toml',
174
+ ],
175
+ typescript: ['tsconfig.json', 'tsconfig.base.json'],
176
+ jest: [
177
+ 'jest.config.js',
178
+ 'jest.config.ts',
179
+ 'jest.config.mjs',
180
+ 'jest.config.cjs',
181
+ 'jest.config.json',
182
+ ],
183
+ vitest: ['vitest.config.js', 'vitest.config.ts', 'vitest.config.mjs'],
184
+ webpack: ['webpack.config.js', 'webpack.config.ts', 'webpack.config.mjs'],
185
+ vite: ['vite.config.js', 'vite.config.ts', 'vite.config.mjs'],
186
+ rollup: ['rollup.config.js', 'rollup.config.ts', 'rollup.config.mjs'],
187
+ babel: ['.babelrc', '.babelrc.js', '.babelrc.json', 'babel.config.js', 'babel.config.json'],
188
+ dockerfile: ['Dockerfile', 'dockerfile', 'Dockerfile.dev', 'Dockerfile.prod'],
189
+ gitignore: ['.gitignore'],
190
+ editorconfig: ['.editorconfig'],
191
+ };
192
+ // Normalize package path
193
+ const normalizedPath = packagePath === '.' || packagePath === ''
194
+ ? ''
195
+ : packagePath.endsWith('/')
196
+ ? packagePath
197
+ : packagePath + '/';
198
+ // Check files in tree
199
+ if (fileTree.allFiles) {
200
+ for (const [tool, patterns] of Object.entries(configPatterns)) {
201
+ for (const pattern of patterns) {
202
+ const fullPath = normalizedPath + pattern;
203
+ const fileExists = fileTree.allFiles.some(f => f.path === fullPath || f.relativePath === fullPath);
204
+ if (fileExists) {
205
+ configs[tool] = {
206
+ path: fullPath,
207
+ exists: true,
208
+ type: this.getConfigFileType(pattern),
209
+ };
210
+ break; // Found config for this tool
211
+ }
212
+ }
213
+ }
214
+ }
215
+ // Check for inline configs in package.json
216
+ if (manifestContent) {
217
+ if (manifestContent.eslintConfig) {
218
+ configs.eslint = {
219
+ path: normalizedPath + 'package.json',
220
+ exists: true,
221
+ type: 'json',
222
+ isInline: true,
223
+ inlineField: 'eslintConfig',
224
+ };
225
+ }
226
+ if (manifestContent.prettier) {
227
+ configs.prettier = {
228
+ path: normalizedPath + 'package.json',
229
+ exists: true,
230
+ type: 'json',
231
+ isInline: true,
232
+ inlineField: 'prettier',
233
+ };
234
+ }
235
+ if (manifestContent.jest) {
236
+ configs.jest = {
237
+ path: normalizedPath + 'package.json',
238
+ exists: true,
239
+ type: 'json',
240
+ isInline: true,
241
+ inlineField: 'jest',
242
+ };
243
+ }
244
+ if (manifestContent.babel) {
245
+ configs.babel = {
246
+ path: normalizedPath + 'package.json',
247
+ exists: true,
248
+ type: 'json',
249
+ isInline: true,
250
+ inlineField: 'babel',
251
+ };
252
+ }
253
+ }
254
+ return configs;
255
+ }
256
+ getConfigFileType(filename) {
257
+ if (filename.endsWith('.json') || filename.endsWith('.jsonc'))
258
+ return 'json';
259
+ if (filename.endsWith('.yml') || filename.endsWith('.yaml'))
260
+ return 'yaml';
261
+ if (filename.endsWith('.toml'))
262
+ return 'toml';
263
+ if (filename.endsWith('.js') || filename.endsWith('.mjs') || filename.endsWith('.cjs'))
264
+ return 'js';
265
+ if (filename.endsWith('.ts') || filename.endsWith('.mts') || filename.endsWith('.cts'))
266
+ return 'ts';
267
+ if (filename.endsWith('.ini'))
268
+ return 'ini';
269
+ return 'custom';
270
+ }
271
+ detectDocsFolder(packagePath, fileTree, _manifestContent) {
272
+ // Look for common documentation folder names
273
+ const docsFolderNames = ['docs', 'documentation', 'doc'];
274
+ // Normalize package path
275
+ let packagePrefix = packagePath === '.' || packagePath === '' ? '' : packagePath;
276
+ if (packagePrefix && !packagePrefix.endsWith('/')) {
277
+ packagePrefix += '/';
278
+ }
279
+ if (!fileTree.allFiles)
280
+ return undefined;
281
+ // Look for root-level docs folders first (preferred)
282
+ for (const folderName of docsFolderNames) {
283
+ const expectedPath = packagePrefix + folderName;
284
+ const hasFiles = fileTree.allFiles.some(file => file.path?.toLowerCase().startsWith(expectedPath.toLowerCase() + '/'));
285
+ if (hasFiles) {
286
+ // Find the actual folder name with correct case
287
+ const actualPath = fileTree.allFiles.find(file => file.path?.toLowerCase().startsWith(expectedPath.toLowerCase() + '/'))?.path;
288
+ if (actualPath) {
289
+ const actualFolderPath = actualPath.substring(0, actualPath.indexOf('/', packagePrefix.length));
290
+ return actualFolderPath.substring(packagePrefix.length);
291
+ }
292
+ return folderName; // Fallback to lowercase version
293
+ }
294
+ }
295
+ // If no explicit docs folder found, look for directories with high concentration of docs
296
+ const potentialFolders = new Map();
297
+ for (const file of fileTree.allFiles) {
298
+ if (!file.path)
299
+ continue;
300
+ // Only consider files within this package
301
+ if (packagePrefix !== '' && !file.path.startsWith(packagePrefix)) {
302
+ continue;
303
+ }
304
+ const relativePath = packagePrefix === '' ? file.path : file.path.substring(packagePrefix.length);
305
+ const pathParts = relativePath.split('/');
306
+ // Only look at direct subdirectories (depth 1)
307
+ if (pathParts.length === 2) {
308
+ const dirName = pathParts[0];
309
+ const fileName = pathParts[1];
310
+ if (!potentialFolders.has(dirName)) {
311
+ potentialFolders.set(dirName, { docFiles: 0, totalFiles: 0 });
312
+ }
313
+ const stats = potentialFolders.get(dirName);
314
+ stats.totalFiles++;
315
+ if (this.isDocumentationFile(fileName)) {
316
+ stats.docFiles++;
317
+ }
318
+ }
319
+ }
320
+ // Find the best candidate folder (highest percentage of docs, minimum 2 doc files)
321
+ let bestFolder;
322
+ let bestScore = 0.5; // Must be at least 50% docs
323
+ for (const [folderName, stats] of Array.from(potentialFolders.entries())) {
324
+ if (stats.docFiles >= 2) {
325
+ const score = stats.docFiles / stats.totalFiles;
326
+ if (score > bestScore) {
327
+ bestScore = score;
328
+ bestFolder = folderName;
329
+ }
330
+ }
331
+ }
332
+ return bestFolder;
333
+ }
334
+ isDocumentationFile(fileName) {
335
+ const lowerName = fileName.toLowerCase();
336
+ // Documentation file extensions
337
+ const docExtensions = ['.md', '.mdx', '.rst', '.txt', '.adoc', '.asciidoc'];
338
+ const hasDocExtension = docExtensions.some(ext => lowerName.endsWith(ext));
339
+ if (hasDocExtension)
340
+ return true;
341
+ // Documentation file name patterns
342
+ const docPatterns = [
343
+ /^readme/i,
344
+ /^changelog/i,
345
+ /^changes/i,
346
+ /^history/i,
347
+ /^license/i,
348
+ /^copying/i,
349
+ /^install/i,
350
+ /^usage/i,
351
+ /^guide/i,
352
+ /^tutorial/i,
353
+ /^manual/i,
354
+ /^faq/i,
355
+ /^api/i,
356
+ /^reference/i,
357
+ /^spec/i,
358
+ /^specification/i,
359
+ ];
360
+ return docPatterns.some(pattern => pattern.test(fileName));
361
+ }
362
+ }
363
+ // Python pyproject.toml parser - simplified version
364
+ class PythonPackageParser {
365
+ constructor() {
366
+ this.manifestFileName = 'pyproject.toml';
367
+ this.packageType = 'python';
368
+ }
369
+ canParse(filename) {
370
+ return (filename.endsWith('pyproject.toml') ||
371
+ filename.endsWith('setup.py') ||
372
+ filename.endsWith('requirements.txt'));
373
+ }
374
+ parseContent(rawContent) {
375
+ try {
376
+ return TOML.load(rawContent);
377
+ }
378
+ catch {
379
+ return null;
380
+ }
381
+ }
382
+ extractPackageData(content, path) {
383
+ // Extract package directory path from manifest path
384
+ let packagePath;
385
+ const manifestName = path.split('/').pop() || '';
386
+ if (path === manifestName) {
387
+ // Just the filename, package is at root
388
+ packagePath = ''; // Empty string for root
389
+ }
390
+ else {
391
+ // Remove the manifest filename from path
392
+ packagePath = path.substring(0, path.lastIndexOf('/'));
393
+ }
394
+ // Handle pyproject.toml structure
395
+ const poetryData = content.tool?.poetry;
396
+ const projectData = content.project;
397
+ const name = poetryData?.name || projectData?.name || 'unnamed';
398
+ const version = poetryData?.version || projectData?.version;
399
+ // Extract dependencies
400
+ const dependencies = {};
401
+ const devDependencies = {};
402
+ // Poetry format
403
+ if (poetryData?.dependencies) {
404
+ Object.entries(poetryData.dependencies).forEach(([key, value]) => {
405
+ if (key !== 'python') {
406
+ dependencies[key] = typeof value === 'string' ? value : JSON.stringify(value);
407
+ }
408
+ });
409
+ }
410
+ if (poetryData?.['dev-dependencies']) {
411
+ Object.entries(poetryData['dev-dependencies']).forEach(([key, value]) => {
412
+ devDependencies[key] = typeof value === 'string' ? value : JSON.stringify(value);
413
+ });
414
+ }
415
+ // PEP 621 format
416
+ if (projectData?.dependencies && Array.isArray(projectData.dependencies)) {
417
+ try {
418
+ // Use pip-requirements-js to properly parse PEP 508 dependency specifications
419
+ const depString = projectData.dependencies.join('\n');
420
+ const requirements = (0, pip_requirements_js_1.parsePipRequirementsFile)(depString);
421
+ requirements.forEach((req) => {
422
+ if (req.type === 'ProjectName' && req.name) {
423
+ let version = '*';
424
+ if (req.versionSpec && req.versionSpec.length > 0) {
425
+ // Use the first version spec for simplicity
426
+ const firstSpec = req.versionSpec[0];
427
+ version = firstSpec.version || '*';
428
+ }
429
+ dependencies[req.name] = version;
430
+ }
431
+ });
432
+ }
433
+ catch (error) {
434
+ console.warn('Failed to parse PEP 621 dependencies with pip-requirements-js, falling back to simple parsing:', error);
435
+ // Fallback to simple parsing if pip-requirements-js fails
436
+ projectData.dependencies.forEach((dep) => {
437
+ const cleanDep = dep.trim();
438
+ const spaceIndex = cleanDep.indexOf(' ');
439
+ if (spaceIndex > 0) {
440
+ dependencies[cleanDep.substring(0, spaceIndex)] = '*';
441
+ }
442
+ else {
443
+ dependencies[cleanDep] = '*';
444
+ }
445
+ });
446
+ }
447
+ }
448
+ const packageManager = this.detectPythonPackageManager(content);
449
+ return {
450
+ name,
451
+ version,
452
+ path: packagePath,
453
+ packageManager,
454
+ dependencies,
455
+ devDependencies,
456
+ peerDependencies: {},
457
+ isMonorepoRoot: false,
458
+ isWorkspace: false,
459
+ parentPackage: undefined,
460
+ availableCommands: [],
461
+ };
462
+ }
463
+ detectWorkspaces(_content) {
464
+ // Python doesn't have built-in workspace support like npm
465
+ return null;
466
+ }
467
+ detectPackageManager(content, _lockFiles) {
468
+ return this.detectPythonPackageManager(content);
469
+ }
470
+ detectPythonPackageManager(content) {
471
+ // Check for Poetry
472
+ if (content.tool?.poetry)
473
+ return 'poetry';
474
+ // Default to pip
475
+ return 'pip';
476
+ }
477
+ detectDocsFolder(packagePath, fileTree, _manifestContent) {
478
+ // Reuse the same logic as NodePackageParser
479
+ const nodeParser = new NodePackageParser();
480
+ return nodeParser.detectDocsFolder(packagePath, fileTree, {});
481
+ }
482
+ }
483
+ // Rust Cargo.toml parser - simplified version
484
+ class CargoPackageParser {
485
+ constructor() {
486
+ this.manifestFileName = 'Cargo.toml';
487
+ this.packageType = 'cargo';
488
+ }
489
+ canParse(filename) {
490
+ return filename.endsWith('Cargo.toml');
491
+ }
492
+ parseContent(rawContent) {
493
+ try {
494
+ return TOML.load(rawContent);
495
+ }
496
+ catch {
497
+ return null;
498
+ }
499
+ }
500
+ extractPackageData(content, path) {
501
+ const packageData = content.package;
502
+ if (!packageData)
503
+ return null;
504
+ // Extract package directory path from manifest path
505
+ let packagePath;
506
+ if (path === 'Cargo.toml') {
507
+ packagePath = ''; // Root directory - empty string for root
508
+ }
509
+ else if (path.endsWith('/Cargo.toml')) {
510
+ packagePath = path.slice(0, -11); // Remove '/Cargo.toml'
511
+ }
512
+ else {
513
+ // Shouldn't happen, but handle gracefully
514
+ packagePath = path;
515
+ }
516
+ // Extract dependencies
517
+ const dependencies = {};
518
+ const devDependencies = {};
519
+ if (content.dependencies) {
520
+ Object.entries(content.dependencies).forEach(([key, value]) => {
521
+ dependencies[key] = typeof value === 'string' ? value : JSON.stringify(value);
522
+ });
523
+ }
524
+ if (content['dev-dependencies']) {
525
+ Object.entries(content['dev-dependencies']).forEach(([key, value]) => {
526
+ devDependencies[key] = typeof value === 'string' ? value : JSON.stringify(value);
527
+ });
528
+ }
529
+ return {
530
+ name: packageData.name || 'unnamed',
531
+ version: packageData.version,
532
+ path: packagePath,
533
+ packageManager: 'cargo',
534
+ dependencies,
535
+ devDependencies,
536
+ peerDependencies: {},
537
+ isMonorepoRoot: !!content.workspace,
538
+ isWorkspace: false,
539
+ parentPackage: undefined,
540
+ availableCommands: [],
541
+ };
542
+ }
543
+ detectWorkspaces(content) {
544
+ return content.workspace?.members || null;
545
+ }
546
+ detectPackageManager() {
547
+ return 'cargo';
548
+ }
549
+ detectDocsFolder(packagePath, fileTree, _manifestContent) {
550
+ // Reuse the same logic as NodePackageParser
551
+ const nodeParser = new NodePackageParser();
552
+ return nodeParser.detectDocsFolder(packagePath, fileTree, {});
553
+ }
554
+ }
555
+ class PackageLayerModule {
556
+ constructor() {
557
+ this.parsers = [
558
+ new NodePackageParser(),
559
+ new PythonPackageParser(),
560
+ new CargoPackageParser(),
561
+ ];
562
+ }
563
+ /**
564
+ * Extract package information from the file tree
565
+ * @param fileTree - The file tree to analyze
566
+ * @param fileReader - Optional function to read file contents. If not provided, manifest detection only.
567
+ */
568
+ async discoverPackages(fileTree, fileReader) {
569
+ const packages = [];
570
+ if (!fileTree.allFiles)
571
+ return packages;
572
+ // Find all package manifest files
573
+ const manifestFiles = fileTree.allFiles.filter(file => {
574
+ if (!file.path)
575
+ return false;
576
+ return this.parsers.some(parser => parser.canParse(file.path));
577
+ });
578
+ // Get all files for lock file detection
579
+ const allFilePaths = fileTree.allFiles.map(f => f.path).filter(Boolean);
580
+ for (const manifestFile of manifestFiles) {
581
+ if (!manifestFile.path)
582
+ continue;
583
+ // Find appropriate parser
584
+ const parser = this.parsers.find(p => p.canParse(manifestFile.path));
585
+ if (!parser)
586
+ continue;
587
+ // Get manifest content
588
+ let content = null;
589
+ if (fileReader) {
590
+ try {
591
+ const fileContent = await fileReader(manifestFile.path);
592
+ if (fileContent) {
593
+ // Use the parser's parseContent method for type-safe parsing
594
+ content = parser.parseContent(fileContent);
595
+ }
596
+ }
597
+ catch (error) {
598
+ console.warn(`Could not read or parse ${manifestFile.path}:`, error);
599
+ continue;
600
+ }
601
+ }
602
+ else {
603
+ // If no file reader provided, create minimal package info from path
604
+ const packageName = manifestFile.path.split('/').slice(-2, -1)[0] || 'unnamed';
605
+ content = this.createMinimalManifest(parser.packageType, packageName);
606
+ }
607
+ if (!content)
608
+ continue;
609
+ // Extract package data
610
+ const packageData = parser.extractPackageData(content, manifestFile.path);
611
+ if (!packageData) {
612
+ console.warn(`[PackageLayerModule] extractPackageData returned null for ${manifestFile.path}`, { content, parser: parser.packageType });
613
+ continue;
614
+ }
615
+ // Detect package manager
616
+ const dirPath = manifestFile.path.substring(0, manifestFile.path.lastIndexOf('/'));
617
+ const lockFilesInDir = allFilePaths.filter(f => f.startsWith(dirPath) && this.isLockFile(f));
618
+ packageData.packageManager = parser.detectPackageManager(content, lockFilesInDir);
619
+ // Update commands with correct package manager for Node packages
620
+ if (parser.packageType === 'node' && packageData.availableCommands) {
621
+ // Only replace if we detected a valid package manager
622
+ if (packageData.packageManager &&
623
+ packageData.packageManager !== 'unknown' &&
624
+ packageData.packageManager !== 'npm') {
625
+ packageData.availableCommands = packageData.availableCommands.map(cmd => {
626
+ // Update both script and standard commands
627
+ if (cmd.command.includes('npm ')) {
628
+ let pmCommand = cmd.command;
629
+ // Handle different package manager syntaxes
630
+ if (packageData.packageManager === 'yarn') {
631
+ pmCommand = pmCommand
632
+ .replace('npm run', 'yarn')
633
+ .replace('npm install', 'yarn install')
634
+ .replace('npm ci', 'yarn install --frozen-lockfile')
635
+ .replace('npm update', 'yarn upgrade');
636
+ }
637
+ else if (packageData.packageManager === 'pnpm') {
638
+ pmCommand = pmCommand.replace('npm', 'pnpm');
639
+ }
640
+ return { ...cmd, command: pmCommand };
641
+ }
642
+ return cmd;
643
+ });
644
+ }
645
+ }
646
+ // Create file set for the manifest
647
+ const fileSet = {
648
+ id: `package-manifest-${packageData.path}`,
649
+ name: parser.manifestFileName,
650
+ patterns: [
651
+ {
652
+ type: 'exact',
653
+ pattern: manifestFile.path,
654
+ description: `${parser.packageType} package manifest`,
655
+ },
656
+ ],
657
+ matchedFiles: [manifestFile.path],
658
+ fileCount: 1,
659
+ };
660
+ // Detect configuration files if the parser supports it
661
+ let configFiles;
662
+ if (parser.detectConfigs) {
663
+ configFiles = parser.detectConfigs(packageData.path, fileTree, content);
664
+ }
665
+ // Detect documentation folder if the parser supports it
666
+ let docsFolder;
667
+ if (parser.detectDocsFolder) {
668
+ docsFolder = parser.detectDocsFolder(packageData.path, fileTree, content);
669
+ }
670
+ // Create package layer
671
+ const layer = {
672
+ id: `package-${parser.packageType}-${packageData.path.replace(/[^a-zA-Z0-9-]/g, '-')}`,
673
+ name: packageData.name,
674
+ type: parser.packageType,
675
+ enabled: true,
676
+ derivedFrom: {
677
+ fileSets: [fileSet],
678
+ derivationType: 'content',
679
+ description: `${parser.packageType} package defined in ${parser.manifestFileName}`,
680
+ contentExtraction: {
681
+ method: 'parse',
682
+ parser: {
683
+ format: parser.manifestFileName.endsWith('.json') ? 'json' : 'toml',
684
+ paths: parser.packageType === 'node'
685
+ ? ['']
686
+ : parser.packageType === 'python'
687
+ ? ['tool.poetry', 'project']
688
+ : ['package'],
689
+ },
690
+ },
691
+ },
692
+ packageData,
693
+ configFiles,
694
+ docsFolder,
695
+ pillar: 'foundationHealth',
696
+ };
697
+ packages.push(layer);
698
+ }
699
+ // Determine workspace relationships
700
+ this.resolveWorkspaceRelationships(packages);
701
+ return packages;
702
+ }
703
+ /**
704
+ * Create package layers from workspace boundaries (backwards compatibility)
705
+ */
706
+ createPackageLayersFromBoundaries(boundaries) {
707
+ return boundaries.map(boundary => {
708
+ const fileSet = {
709
+ id: `package-manifest-${boundary.rootPath || 'root'}`,
710
+ name: 'package.json',
711
+ patterns: [
712
+ {
713
+ type: 'exact',
714
+ pattern: boundary.packageJsonPath,
715
+ description: 'Node.js package manifest',
716
+ },
717
+ ],
718
+ matchedFiles: [boundary.packageJsonPath],
719
+ fileCount: 1,
720
+ };
721
+ const packageData = {
722
+ name: boundary.packageData?.name || boundary.name,
723
+ version: boundary.packageData?.version,
724
+ path: boundary.rootPath || '.',
725
+ packageManager: 'unknown',
726
+ dependencies: {},
727
+ devDependencies: {},
728
+ peerDependencies: {},
729
+ isMonorepoRoot: !!boundary.packageData?.workspaces,
730
+ isWorkspace: !boundary.isRoot && boundaries.length > 1,
731
+ parentPackage: boundary.isRoot ? undefined : 'root',
732
+ };
733
+ const layer = {
734
+ id: `package-node-${boundary.id}`,
735
+ name: packageData.name,
736
+ type: 'node',
737
+ enabled: true,
738
+ derivedFrom: {
739
+ fileSets: [fileSet],
740
+ derivationType: 'content',
741
+ description: 'Node.js package defined in package.json',
742
+ },
743
+ packageData,
744
+ pillar: 'foundationHealth',
745
+ };
746
+ return layer;
747
+ });
748
+ }
749
+ isLockFile(path) {
750
+ const lockFiles = [
751
+ 'package-lock.json',
752
+ 'yarn.lock',
753
+ 'pnpm-lock.yaml',
754
+ 'poetry.lock',
755
+ 'Pipfile.lock',
756
+ 'Cargo.lock',
757
+ ];
758
+ return lockFiles.some(lock => path.endsWith(lock));
759
+ }
760
+ resolveWorkspaceRelationships(packages) {
761
+ // Find root packages (typically at the repository root)
762
+ const _rootPackages = packages.filter(p => p.packageData.path === '.' || p.packageData.path === '');
763
+ // Mark workspace packages
764
+ packages.forEach(pkg => {
765
+ if (pkg.packageData.path !== '.' && pkg.packageData.path !== '') {
766
+ pkg.packageData.isWorkspace = true;
767
+ // Find parent package
768
+ const parentPath = pkg.packageData.path.includes('/')
769
+ ? pkg.packageData.path.substring(0, pkg.packageData.path.lastIndexOf('/'))
770
+ : '.';
771
+ const parent = packages.find(p => p.packageData.path === parentPath);
772
+ if (parent) {
773
+ pkg.packageData.parentPackage = parent.packageData.name;
774
+ }
775
+ }
776
+ });
777
+ }
778
+ /**
779
+ * Create a minimal manifest for when file content isn't available
780
+ */
781
+ createMinimalManifest(packageType, packageName) {
782
+ switch (packageType) {
783
+ case 'node':
784
+ return {
785
+ name: packageName,
786
+ version: '0.0.0',
787
+ dependencies: {},
788
+ devDependencies: {},
789
+ };
790
+ case 'python':
791
+ return {
792
+ project: {
793
+ name: packageName,
794
+ version: '0.0.0',
795
+ },
796
+ };
797
+ case 'cargo':
798
+ return {
799
+ package: {
800
+ name: packageName,
801
+ version: '0.0.0',
802
+ },
803
+ };
804
+ default:
805
+ return {};
806
+ }
807
+ }
808
+ }
809
+ exports.PackageLayerModule = PackageLayerModule;
810
+ //# sourceMappingURL=PackageLayerModule.js.map