@openinc/parse-server-opendash 3.19.1 → 3.20.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 (61) hide show
  1. package/dist/featuremap.json +1 -0
  2. package/dist/features/config/helper/isFeatureEnabled.d.ts +2 -1
  3. package/dist/features/documentation/config/defaults.d.ts +39 -0
  4. package/dist/features/documentation/config/defaults.js +142 -0
  5. package/dist/features/documentation/config/index.d.ts +1 -0
  6. package/dist/features/documentation/config/index.js +17 -0
  7. package/dist/features/documentation/core/Cleanup.d.ts +30 -0
  8. package/dist/features/documentation/core/Cleanup.js +134 -0
  9. package/dist/features/documentation/core/Converter.d.ts +9 -0
  10. package/dist/features/documentation/core/Converter.js +65 -0
  11. package/dist/features/documentation/core/Importer.d.ts +21 -0
  12. package/dist/features/documentation/core/Importer.js +133 -0
  13. package/dist/features/documentation/core/Organizer.d.ts +11 -0
  14. package/dist/features/documentation/core/Organizer.js +52 -0
  15. package/dist/features/documentation/core/index.d.ts +4 -0
  16. package/dist/features/documentation/core/index.js +20 -0
  17. package/dist/features/documentation/functions/importDocs.d.ts +2 -0
  18. package/dist/features/documentation/functions/importDocs.js +40 -0
  19. package/dist/features/documentation/index.d.ts +8 -0
  20. package/dist/features/documentation/index.js +34 -0
  21. package/dist/features/documentation/services/ConfigApplier.d.ts +11 -0
  22. package/dist/features/documentation/services/ConfigApplier.js +110 -0
  23. package/dist/features/documentation/services/ContentLoader.d.ts +6 -0
  24. package/dist/features/documentation/services/ContentLoader.js +40 -0
  25. package/dist/features/documentation/services/FeatureFilter.d.ts +34 -0
  26. package/dist/features/documentation/services/FeatureFilter.js +154 -0
  27. package/dist/features/documentation/services/GitHubClient.d.ts +43 -0
  28. package/dist/features/documentation/services/GitHubClient.js +140 -0
  29. package/dist/features/documentation/services/MetadataEnricher.d.ts +5 -0
  30. package/dist/features/documentation/services/MetadataEnricher.js +29 -0
  31. package/dist/features/documentation/services/StructureBuilder.d.ts +7 -0
  32. package/dist/features/documentation/services/StructureBuilder.js +43 -0
  33. package/dist/features/documentation/services/TreeNormalizer.d.ts +10 -0
  34. package/dist/features/documentation/services/TreeNormalizer.js +73 -0
  35. package/dist/features/documentation/services/index.d.ts +7 -0
  36. package/dist/features/documentation/services/index.js +23 -0
  37. package/dist/features/documentation/types/GitHubTypes.d.ts +25 -0
  38. package/dist/features/documentation/types/GitHubTypes.js +2 -0
  39. package/dist/features/documentation/types/StructureTypes.d.ts +114 -0
  40. package/dist/features/documentation/types/StructureTypes.js +2 -0
  41. package/dist/features/documentation/types/index.d.ts +2 -0
  42. package/dist/features/documentation/types/index.js +18 -0
  43. package/dist/features/permissions/types/Permissions.d.ts +2 -0
  44. package/dist/features/permissions/types/Permissions.js +2 -0
  45. package/dist/functions/openinc-documentation-import-docs.d.ts +1 -0
  46. package/dist/functions/openinc-documentation-import-docs.js +23 -0
  47. package/dist/hooks/Documentation_Config.d.ts +1 -0
  48. package/dist/hooks/Documentation_Config.js +17 -0
  49. package/dist/index.js +3 -0
  50. package/dist/types/Documentation_Category.d.ts +6 -0
  51. package/dist/types/Documentation_Category.js +12 -0
  52. package/dist/types/Documentation_Config.d.ts +21 -0
  53. package/dist/types/Documentation_Config.js +29 -0
  54. package/dist/types/Documentation_Document.d.ts +6 -0
  55. package/dist/types/Documentation_Document.js +12 -0
  56. package/dist/types/index.d.ts +2 -0
  57. package/dist/types/index.js +5 -3
  58. package/package.json +3 -1
  59. package/schema/Documentation_Category.json +9 -0
  60. package/schema/Documentation_Config.json +50 -0
  61. package/schema/Documentation_Document.json +9 -0
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DocumentationOrganizer = void 0;
4
+ const TreeNormalizer_1 = require("../services/TreeNormalizer");
5
+ const StructureBuilder_1 = require("../services/StructureBuilder");
6
+ const ConfigApplier_1 = require("../services/ConfigApplier");
7
+ const MetadataEnricher_1 = require("../services/MetadataEnricher");
8
+ const ContentLoader_1 = require("../services/ContentLoader");
9
+ const FeatureFilter_1 = require("../services/FeatureFilter");
10
+ /**
11
+ * Documentation Structure Organizer
12
+ */
13
+ class DocumentationOrganizer {
14
+ /**
15
+ * Organize GitHub tree data into a hierarchical structure
16
+ */
17
+ static async organizeRepositoryStructure(treeData, fileFilter, rootPath, githubClient, owner, repo, branch, defaultFolderConfig) {
18
+ // 1. Normalize tree into flat file list
19
+ const { files: normalizedFiles } = TreeNormalizer_1.TreeNormalizer.normalize(treeData, rootPath, fileFilter);
20
+ // 2. Build folder structure & identify config.json files
21
+ const { root: builtRoot, configFiles } = StructureBuilder_1.StructureBuilder.build(normalizedFiles, rootPath, defaultFolderConfig);
22
+ // 3. Apply configs
23
+ await ConfigApplier_1.ConfigApplier.apply(builtRoot, configFiles, githubClient, owner, repo, branch, rootPath);
24
+ // 4. Apply feature filtering
25
+ console.log(`[DocumentationOrganizer] Applying feature-based filtering...`);
26
+ const preFilterFileCount = normalizedFiles.length;
27
+ const filteredRoot = FeatureFilter_1.FeatureFilter.filterByFeature(builtRoot);
28
+ const filteredFiles = FeatureFilter_1.FeatureFilter.filterFileList(normalizedFiles, filteredRoot);
29
+ const postFilterFileCount = filteredFiles.length;
30
+ if (preFilterFileCount !== postFilterFileCount) {
31
+ console.log(`[DocumentationOrganizer] Feature filtering applied: ${preFilterFileCount - postFilterFileCount} files filtered out`);
32
+ }
33
+ else {
34
+ console.log(`[DocumentationOrganizer] Feature filtering applied: no files filtered`);
35
+ }
36
+ // 5. Enrich commit metadata
37
+ if (githubClient && owner && repo) {
38
+ await MetadataEnricher_1.MetadataEnricher.enrichCommits(filteredFiles, githubClient, owner, repo, branch, rootPath);
39
+ // 6. Load content
40
+ await ContentLoader_1.ContentLoader.populate(filteredFiles, githubClient, owner, repo, branch, rootPath);
41
+ }
42
+ // Build extension map
43
+ const filesByExtension = new Map();
44
+ for (const f of filteredFiles) {
45
+ if (!filesByExtension.has(f.extension))
46
+ filesByExtension.set(f.extension, []);
47
+ filesByExtension.get(f.extension).push(f);
48
+ }
49
+ return { root: filteredRoot, allFiles: filteredFiles, filesByExtension };
50
+ }
51
+ }
52
+ exports.DocumentationOrganizer = DocumentationOrganizer;
@@ -0,0 +1,4 @@
1
+ export * from "./Importer";
2
+ export * from "./Cleanup";
3
+ export * from "./Converter";
4
+ export * from "./Organizer";
@@ -0,0 +1,20 @@
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 __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./Importer"), exports);
18
+ __exportStar(require("./Cleanup"), exports);
19
+ __exportStar(require("./Converter"), exports);
20
+ __exportStar(require("./Organizer"), exports);
@@ -0,0 +1,2 @@
1
+ import "dotenv/config";
2
+ export declare function importDocs(): Promise<void>;
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.importDocs = importDocs;
4
+ require("dotenv/config");
5
+ const core_1 = require("../core");
6
+ const config_1 = require("../config");
7
+ const Converter_1 = require("../core/Converter");
8
+ const types_1 = require("../../../types");
9
+ const GITHUB_TOKEN = process.env.OI_DOCUMENTATION_GITHUB_ACCESS_TOKEN;
10
+ async function importDocs() {
11
+ try {
12
+ if (!GITHUB_TOKEN) {
13
+ throw new Error("Missing GitHub token - Please set OI_DOCUMENTATION_GITHUB_ACCESS_TOKEN environment variable");
14
+ }
15
+ const importer = new core_1.DocumentationImporter(GITHUB_TOKEN);
16
+ // Use the configuration helper to create import options
17
+ const config = (0, config_1.createImportConfig)({
18
+ token: GITHUB_TOKEN,
19
+ });
20
+ const userId = process.env.OI_DOCUMENTATION_USER_ID;
21
+ const user = userId
22
+ ? await new Parse.Query(Parse.User)
23
+ .equalTo("objectId", userId)
24
+ .include("tenant")
25
+ .first({ useMasterKey: true })
26
+ : undefined;
27
+ const tenantId = process.env.OI_DOCUMENTATION_TENANT_ID;
28
+ const tenant = tenantId
29
+ ? new types_1.Tenant({ objectId: tenantId })
30
+ : user?.get("tenant");
31
+ const result = await importer.importFromRepository(config, user, tenant);
32
+ if (result.metadata.skipped)
33
+ return;
34
+ if (result.structure)
35
+ await new Converter_1.DocumentationConverter(result).convert(user, tenant);
36
+ }
37
+ catch (err) {
38
+ console.error("Error importing documentation:", err);
39
+ }
40
+ }
@@ -0,0 +1,8 @@
1
+ export * from "./core";
2
+ export * from "./services";
3
+ export * from "./types";
4
+ export * from "./config";
5
+ export { DocumentationImporter } from "./core/Importer";
6
+ export { GitHubClient } from "./services/GitHubClient";
7
+ export { DocumentationOrganizer } from "./core/Organizer";
8
+ export { createImportConfig, DEFAULT_IMPORT_CONFIG, IMPORT_PRESETS, } from "./config/defaults";
@@ -0,0 +1,34 @@
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 __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.IMPORT_PRESETS = exports.DEFAULT_IMPORT_CONFIG = exports.createImportConfig = exports.DocumentationOrganizer = exports.GitHubClient = exports.DocumentationImporter = void 0;
18
+ // Core exports
19
+ __exportStar(require("./core"), exports);
20
+ __exportStar(require("./services"), exports);
21
+ __exportStar(require("./types"), exports);
22
+ __exportStar(require("./config"), exports);
23
+ // Main classes for easy access
24
+ var Importer_1 = require("./core/Importer");
25
+ Object.defineProperty(exports, "DocumentationImporter", { enumerable: true, get: function () { return Importer_1.DocumentationImporter; } });
26
+ var GitHubClient_1 = require("./services/GitHubClient");
27
+ Object.defineProperty(exports, "GitHubClient", { enumerable: true, get: function () { return GitHubClient_1.GitHubClient; } });
28
+ var Organizer_1 = require("./core/Organizer");
29
+ Object.defineProperty(exports, "DocumentationOrganizer", { enumerable: true, get: function () { return Organizer_1.DocumentationOrganizer; } });
30
+ // Configuration helpers
31
+ var defaults_1 = require("./config/defaults");
32
+ Object.defineProperty(exports, "createImportConfig", { enumerable: true, get: function () { return defaults_1.createImportConfig; } });
33
+ Object.defineProperty(exports, "DEFAULT_IMPORT_CONFIG", { enumerable: true, get: function () { return defaults_1.DEFAULT_IMPORT_CONFIG; } });
34
+ Object.defineProperty(exports, "IMPORT_PRESETS", { enumerable: true, get: function () { return defaults_1.IMPORT_PRESETS; } });
@@ -0,0 +1,11 @@
1
+ import { DocumentationFolder, DocumentationFile } from "../types";
2
+ import { GitHubClient } from "./GitHubClient";
3
+ export declare class ConfigApplier {
4
+ static apply(root: DocumentationFolder, configFiles: DocumentationFile[], githubClient?: GitHubClient, owner?: string, repo?: string, branch?: string, rootPath?: string): Promise<void>;
5
+ /** Compare raw vs sanitized folder config and log warnings for dropped / invalid entries */
6
+ private static diffAndWarnFolderConfig;
7
+ private static diffAndWarnFileConfig;
8
+ private static fetchConfigFromGitHub;
9
+ private static applyFileConfigs;
10
+ private static findFolderByPath;
11
+ }
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ConfigApplier = void 0;
4
+ const config_1 = require("../config");
5
+ class ConfigApplier {
6
+ static async apply(root, configFiles, githubClient, owner, repo, branch, rootPath) {
7
+ if (!configFiles.length)
8
+ return;
9
+ for (const cfgFile of configFiles) {
10
+ let folderPath = "";
11
+ if (cfgFile.path.endsWith("/config.json"))
12
+ folderPath = cfgFile.path.slice(0, -"/config.json".length);
13
+ const folder = folderPath
14
+ ? this.findFolderByPath(root, folderPath)
15
+ : root;
16
+ if (!folder)
17
+ continue;
18
+ try {
19
+ if (githubClient && owner && repo) {
20
+ const customRaw = await this.fetchConfigFromGitHub(cfgFile, githubClient, owner, repo, branch, rootPath);
21
+ const custom = (0, config_1.sanitizeFolderConfig)(customRaw);
22
+ this.diffAndWarnFolderConfig(customRaw, custom, cfgFile.path);
23
+ folder.config = { ...folder.config, ...custom };
24
+ if (folder.config.files)
25
+ this.applyFileConfigs(folder);
26
+ }
27
+ }
28
+ catch (e) {
29
+ console.warn(`Failed to apply config ${cfgFile.path}`, e);
30
+ }
31
+ }
32
+ }
33
+ /** Compare raw vs sanitized folder config and log warnings for dropped / invalid entries */
34
+ static diffAndWarnFolderConfig(raw, sanitized, sourcePath) {
35
+ if (!raw || typeof raw !== "object")
36
+ return;
37
+ const allowedSet = new Set(config_1.FOLDER_CONFIG_ALLOWED_KEYS);
38
+ for (const key of Object.keys(raw)) {
39
+ if (!allowedSet.has(key)) {
40
+ console.warn(`[ConfigApplier] Unknown folder config key '${key}' in ${sourcePath}. Allowed: ${config_1.FOLDER_CONFIG_ALLOWED_KEYS.join(", ")}`);
41
+ }
42
+ }
43
+ // File config diffs
44
+ if (raw.files && typeof raw.files === "object") {
45
+ for (const [fileBase, rawFile] of Object.entries(raw.files)) {
46
+ const sanitizedFile = sanitized.files?.[fileBase];
47
+ this.diffAndWarnFileConfig(rawFile, sanitizedFile || {}, sourcePath, fileBase);
48
+ }
49
+ }
50
+ }
51
+ static diffAndWarnFileConfig(raw, sanitized, sourcePath, fileBase) {
52
+ if (!raw || typeof raw !== "object")
53
+ return;
54
+ const allowedSet = new Set(config_1.FILE_CONFIG_ALLOWED_KEYS);
55
+ for (const key of Object.keys(raw)) {
56
+ if (!allowedSet.has(key)) {
57
+ console.warn(`[ConfigApplier] Unknown file config key '${key}' for '${fileBase}' in ${sourcePath}. Allowed: ${config_1.FILE_CONFIG_ALLOWED_KEYS.join(", ")}`);
58
+ }
59
+ }
60
+ // Basic type checks for known keys
61
+ if ("order" in raw && raw.order != null && typeof raw.order !== "number") {
62
+ console.warn(`[ConfigApplier] Invalid type for 'order' (expected number) in file '${fileBase}' (${sourcePath})`);
63
+ }
64
+ if ("icon" in raw && raw.icon != null && typeof raw.icon !== "string") {
65
+ console.warn(`[ConfigApplier] Invalid type for 'icon' (expected string) in file '${fileBase}' (${sourcePath})`);
66
+ }
67
+ if ("title" in raw && raw.title != null && typeof raw.title !== "string") {
68
+ console.warn(`[ConfigApplier] Invalid type for 'title' (expected string) in file '${fileBase}' (${sourcePath})`);
69
+ }
70
+ if ("locations" in raw &&
71
+ raw.locations != null &&
72
+ (!Array.isArray(raw.locations) ||
73
+ raw.locations.some((v) => typeof v !== "string"))) {
74
+ console.warn(`[ConfigApplier] Invalid type for 'locations' (expected string[]) in file '${fileBase}' (${sourcePath})`);
75
+ }
76
+ }
77
+ static async fetchConfigFromGitHub(cfgFile, githubClient, owner, repo, branch, rootPath) {
78
+ const normalizedRoot = rootPath?.replace(/^\/+/g, "").replace(/\/+$/g, "");
79
+ const fullPath = normalizedRoot
80
+ ? `${normalizedRoot}/${cfgFile.path}`
81
+ : cfgFile.path;
82
+ const fileContent = await githubClient.getFileContent(owner, repo, fullPath, branch);
83
+ const json = Buffer.from(fileContent.content, "base64").toString("utf-8");
84
+ return JSON.parse(json);
85
+ }
86
+ static applyFileConfigs(folder) {
87
+ if (!folder.config.files)
88
+ return;
89
+ for (const file of folder.files) {
90
+ const cfg = folder.config.files[file.name];
91
+ if (cfg) {
92
+ file.config = { ...config_1.DEFAULT_FILE_CONFIG, ...cfg };
93
+ }
94
+ }
95
+ }
96
+ static findFolderByPath(root, path) {
97
+ if (!path)
98
+ return root;
99
+ const parts = path.split("/").filter(Boolean);
100
+ let current = root;
101
+ for (const segment of parts) {
102
+ const next = current.subfolders.get(segment);
103
+ if (!next)
104
+ return null;
105
+ current = next;
106
+ }
107
+ return current;
108
+ }
109
+ }
110
+ exports.ConfigApplier = ConfigApplier;
@@ -0,0 +1,6 @@
1
+ import { DocumentationFile } from "../types";
2
+ import { GitHubClient } from "./GitHubClient";
3
+ export declare class ContentLoader {
4
+ private static readonly TEXT_EXTENSIONS;
5
+ static populate(files: DocumentationFile[], githubClient: GitHubClient, owner: string, repo: string, branch?: string, rootPath?: string): Promise<void>;
6
+ }
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ContentLoader = void 0;
4
+ class ContentLoader {
5
+ static async populate(files, githubClient, owner, repo, branch, rootPath) {
6
+ const candidates = files.filter((f) => this.TEXT_EXTENSIONS.has(f.extension));
7
+ if (!candidates.length)
8
+ return;
9
+ const normalizedRoot = rootPath?.replace(/^\/+/g, "").replace(/\/+$/g, "");
10
+ let fetched = 0;
11
+ for (const file of candidates) {
12
+ try {
13
+ const fullPath = normalizedRoot
14
+ ? `${normalizedRoot}/${file.path}`
15
+ : file.path;
16
+ const ghContent = await githubClient.getFileContent(owner, repo, fullPath, branch);
17
+ if (ghContent?.content) {
18
+ file.content = Buffer.from(ghContent.content, "base64").toString("utf-8");
19
+ fetched++;
20
+ }
21
+ }
22
+ catch (e) {
23
+ console.warn(`Failed to fetch content for ${file.path}:`, e);
24
+ }
25
+ }
26
+ }
27
+ }
28
+ exports.ContentLoader = ContentLoader;
29
+ ContentLoader.TEXT_EXTENSIONS = new Set([
30
+ "md",
31
+ "markdown",
32
+ "txt",
33
+ "json",
34
+ "yml",
35
+ "yaml",
36
+ "html",
37
+ "css",
38
+ "ts",
39
+ "js",
40
+ ]);
@@ -0,0 +1,34 @@
1
+ import { DocumentationFolder, DocumentationFile } from "../types";
2
+ /**
3
+ * Feature-based filtering service for documentation
4
+ */
5
+ export declare class FeatureFilter {
6
+ /**
7
+ * Filter folders and files based on their feature configuration
8
+ */
9
+ static filterByFeature(root: DocumentationFolder): DocumentationFolder;
10
+ /**
11
+ * Recursively filter a folder and its contents based on feature enablement
12
+ */
13
+ private static filterFolder;
14
+ /**
15
+ * Check if a folder has any content (files or non-empty subfolders)
16
+ */
17
+ private static hasContent;
18
+ /**
19
+ * Filter the flat file list to match the filtered folder structure
20
+ */
21
+ static filterFileList(files: DocumentationFile[], filteredRoot: DocumentationFolder): DocumentationFile[];
22
+ /**
23
+ * Recursively collect all allowed file paths from the filtered folder structure
24
+ */
25
+ private static collectAllowedPaths;
26
+ /**
27
+ * Count total files recursively in a folder structure
28
+ */
29
+ private static countTotalFiles;
30
+ /**
31
+ * Count total folders recursively in a folder structure
32
+ */
33
+ private static countTotalFolders;
34
+ }
@@ -0,0 +1,154 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FeatureFilter = void 0;
4
+ const isFeatureEnabled_1 = require("../../config/helper/isFeatureEnabled");
5
+ /**
6
+ * Feature-based filtering service for documentation
7
+ */
8
+ class FeatureFilter {
9
+ /**
10
+ * Filter folders and files based on their feature configuration
11
+ */
12
+ static filterByFeature(root) {
13
+ console.log(`[FeatureFilter] Starting feature-based filtering for root folder: ${root.name}`);
14
+ const originalFileCount = this.countTotalFiles(root);
15
+ const originalFolderCount = this.countTotalFolders(root);
16
+ const filtered = this.filterFolder(root);
17
+ const filteredFileCount = this.countTotalFiles(filtered);
18
+ const filteredFolderCount = this.countTotalFolders(filtered);
19
+ console.log(`[FeatureFilter] Filtering complete:`, {
20
+ originalFiles: originalFileCount,
21
+ filteredFiles: filteredFileCount,
22
+ excludedFiles: originalFileCount - filteredFileCount,
23
+ originalFolders: originalFolderCount,
24
+ filteredFolders: filteredFolderCount,
25
+ excludedFolders: originalFolderCount - filteredFolderCount,
26
+ });
27
+ return filtered;
28
+ }
29
+ /**
30
+ * Recursively filter a folder and its contents based on feature enablement
31
+ */
32
+ static filterFolder(folder) {
33
+ const folderPath = folder.path || folder.name;
34
+ // Check if this folder should be excluded based on feature
35
+ if (folder.config.feature && !(0, isFeatureEnabled_1.isFeatureEnabled)(folder.config.feature)) {
36
+ console.log(`[FeatureFilter] Excluding folder '${folderPath}' (feature '${folder.config.feature}' is disabled)`);
37
+ // Return an empty folder structure to effectively exclude this folder
38
+ return {
39
+ ...folder,
40
+ files: [],
41
+ subfolders: new Map(),
42
+ };
43
+ }
44
+ const originalFileCount = folder.files.length;
45
+ const originalSubfolderCount = folder.subfolders.size;
46
+ // Filter files in this folder
47
+ const filteredFiles = folder.files.filter((file) => {
48
+ // If file has no feature config, include it
49
+ if (!file.config?.feature)
50
+ return true;
51
+ // If file has feature config, check if it's enabled
52
+ const isEnabled = (0, isFeatureEnabled_1.isFeatureEnabled)(file.config.feature);
53
+ if (!isEnabled) {
54
+ console.log(`[FeatureFilter] Excluding file '${file.path}' (feature '${file.config.feature}' is disabled)`);
55
+ }
56
+ return isEnabled;
57
+ });
58
+ // Recursively filter subfolders
59
+ const filteredSubfolders = new Map();
60
+ for (const [name, subfolder] of folder.subfolders) {
61
+ const filteredSubfolder = this.filterFolder(subfolder);
62
+ // Only include subfolders that have content after filtering
63
+ // or don't have a feature restriction
64
+ if (this.hasContent(filteredSubfolder) || !subfolder.config.feature) {
65
+ filteredSubfolders.set(name, filteredSubfolder);
66
+ }
67
+ else {
68
+ console.log(`[FeatureFilter] Removing empty subfolder '${subfolder.path || subfolder.name}' after filtering`);
69
+ }
70
+ }
71
+ const excludedFiles = originalFileCount - filteredFiles.length;
72
+ const excludedSubfolders = originalSubfolderCount - filteredSubfolders.size;
73
+ if (excludedFiles > 0 || excludedSubfolders > 0) {
74
+ console.log(`[FeatureFilter] Filtered folder '${folderPath}':`, {
75
+ excludedFiles,
76
+ excludedSubfolders,
77
+ remainingFiles: filteredFiles.length,
78
+ remainingSubfolders: filteredSubfolders.size,
79
+ });
80
+ }
81
+ return {
82
+ ...folder,
83
+ files: filteredFiles,
84
+ subfolders: filteredSubfolders,
85
+ };
86
+ }
87
+ /**
88
+ * Check if a folder has any content (files or non-empty subfolders)
89
+ */
90
+ static hasContent(folder) {
91
+ // Has files
92
+ if (folder.files.length > 0)
93
+ return true;
94
+ // Has non-empty subfolders
95
+ for (const subfolder of folder.subfolders.values()) {
96
+ if (this.hasContent(subfolder))
97
+ return true;
98
+ }
99
+ return false;
100
+ }
101
+ /**
102
+ * Filter the flat file list to match the filtered folder structure
103
+ */
104
+ static filterFileList(files, filteredRoot) {
105
+ console.log(`[FeatureFilter] Filtering file list: ${files.length} total files`);
106
+ const allowedPaths = new Set();
107
+ this.collectAllowedPaths(filteredRoot, "", allowedPaths);
108
+ console.log(`[FeatureFilter] Collected ${allowedPaths.size} allowed file paths`);
109
+ const filteredFiles = files.filter((file) => allowedPaths.has(file.path));
110
+ const excludedCount = files.length - filteredFiles.length;
111
+ if (excludedCount > 0) {
112
+ console.log(`[FeatureFilter] File list filtering complete: ${excludedCount} files excluded, ${filteredFiles.length} files remaining`);
113
+ }
114
+ else {
115
+ console.log(`[FeatureFilter] File list filtering complete: no files excluded`);
116
+ }
117
+ return filteredFiles;
118
+ }
119
+ /**
120
+ * Recursively collect all allowed file paths from the filtered folder structure
121
+ */
122
+ static collectAllowedPaths(folder, currentPath, allowedPaths) {
123
+ // Add all files in this folder
124
+ for (const file of folder.files) {
125
+ allowedPaths.add(file.path);
126
+ }
127
+ // Recursively process subfolders
128
+ for (const [name, subfolder] of folder.subfolders) {
129
+ const subfolderPath = currentPath ? `${currentPath}/${name}` : name;
130
+ this.collectAllowedPaths(subfolder, subfolderPath, allowedPaths);
131
+ }
132
+ }
133
+ /**
134
+ * Count total files recursively in a folder structure
135
+ */
136
+ static countTotalFiles(folder) {
137
+ let count = folder.files.length;
138
+ for (const subfolder of folder.subfolders.values()) {
139
+ count += this.countTotalFiles(subfolder);
140
+ }
141
+ return count;
142
+ }
143
+ /**
144
+ * Count total folders recursively in a folder structure
145
+ */
146
+ static countTotalFolders(folder) {
147
+ let count = folder.subfolders.size;
148
+ for (const subfolder of folder.subfolders.values()) {
149
+ count += this.countTotalFolders(subfolder);
150
+ }
151
+ return count;
152
+ }
153
+ }
154
+ exports.FeatureFilter = FeatureFilter;
@@ -0,0 +1,43 @@
1
+ import { GitHubTreeResponse, GitHubBranch, GitHubRepository } from "../types";
2
+ /**
3
+ * GitHub API Client for fetching repository data
4
+ */
5
+ export declare class GitHubClient {
6
+ private readonly baseUrl;
7
+ private readonly headers;
8
+ constructor(token: string);
9
+ /**
10
+ * Validate the GitHub token
11
+ */
12
+ validateToken(): Promise<{
13
+ login: string;
14
+ }>;
15
+ /**
16
+ * Get repository information
17
+ */
18
+ getRepository(owner: string, repo: string): Promise<GitHubRepository>;
19
+ /**
20
+ * Get all branches for a repository
21
+ */
22
+ getBranches(owner: string, repo: string): Promise<GitHubBranch[]>;
23
+ /**
24
+ * Get a specific branch
25
+ */
26
+ getBranch(owner: string, repo: string, branch: string): Promise<GitHubBranch>;
27
+ /**
28
+ * Get repository tree (file structure)
29
+ */
30
+ getTree(owner: string, repo: string, sha: string, recursive?: boolean): Promise<GitHubTreeResponse>;
31
+ /**
32
+ * Get file content
33
+ */
34
+ getFileContent(owner: string, repo: string, path: string, ref?: string): Promise<any>;
35
+ /**
36
+ * Get the last commit information for a specific file
37
+ */
38
+ getFileLastCommit(owner: string, repo: string, path: string, ref?: string): Promise<any>;
39
+ /**
40
+ * Get commit information for multiple files in batch
41
+ */
42
+ getMultipleFileCommits(owner: string, repo: string, paths: string[], ref?: string): Promise<Map<string, any>>;
43
+ }