@openinc/parse-server-opendash 3.19.2 → 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.
- package/dist/featuremap.json +1 -0
- package/dist/features/config/helper/isFeatureEnabled.d.ts +2 -1
- package/dist/features/documentation/config/defaults.d.ts +39 -0
- package/dist/features/documentation/config/defaults.js +142 -0
- package/dist/features/documentation/config/index.d.ts +1 -0
- package/dist/features/documentation/config/index.js +17 -0
- package/dist/features/documentation/core/Cleanup.d.ts +30 -0
- package/dist/features/documentation/core/Cleanup.js +134 -0
- package/dist/features/documentation/core/Converter.d.ts +9 -0
- package/dist/features/documentation/core/Converter.js +65 -0
- package/dist/features/documentation/core/Importer.d.ts +21 -0
- package/dist/features/documentation/core/Importer.js +133 -0
- package/dist/features/documentation/core/Organizer.d.ts +11 -0
- package/dist/features/documentation/core/Organizer.js +52 -0
- package/dist/features/documentation/core/index.d.ts +4 -0
- package/dist/features/documentation/core/index.js +20 -0
- package/dist/features/documentation/functions/importDocs.d.ts +2 -0
- package/dist/features/documentation/functions/importDocs.js +40 -0
- package/dist/features/documentation/index.d.ts +8 -0
- package/dist/features/documentation/index.js +34 -0
- package/dist/features/documentation/services/ConfigApplier.d.ts +11 -0
- package/dist/features/documentation/services/ConfigApplier.js +110 -0
- package/dist/features/documentation/services/ContentLoader.d.ts +6 -0
- package/dist/features/documentation/services/ContentLoader.js +40 -0
- package/dist/features/documentation/services/FeatureFilter.d.ts +34 -0
- package/dist/features/documentation/services/FeatureFilter.js +154 -0
- package/dist/features/documentation/services/GitHubClient.d.ts +43 -0
- package/dist/features/documentation/services/GitHubClient.js +140 -0
- package/dist/features/documentation/services/MetadataEnricher.d.ts +5 -0
- package/dist/features/documentation/services/MetadataEnricher.js +29 -0
- package/dist/features/documentation/services/StructureBuilder.d.ts +7 -0
- package/dist/features/documentation/services/StructureBuilder.js +43 -0
- package/dist/features/documentation/services/TreeNormalizer.d.ts +10 -0
- package/dist/features/documentation/services/TreeNormalizer.js +73 -0
- package/dist/features/documentation/services/index.d.ts +7 -0
- package/dist/features/documentation/services/index.js +23 -0
- package/dist/features/documentation/types/GitHubTypes.d.ts +25 -0
- package/dist/features/documentation/types/GitHubTypes.js +2 -0
- package/dist/features/documentation/types/StructureTypes.d.ts +114 -0
- package/dist/features/documentation/types/StructureTypes.js +2 -0
- package/dist/features/documentation/types/index.d.ts +2 -0
- package/dist/features/documentation/types/index.js +18 -0
- package/dist/functions/openinc-documentation-import-docs.d.ts +1 -0
- package/dist/functions/openinc-documentation-import-docs.js +23 -0
- package/dist/hooks/Documentation_Config.d.ts +1 -0
- package/dist/hooks/Documentation_Config.js +17 -0
- package/dist/index.js +3 -0
- package/dist/types/Documentation_Category.d.ts +6 -0
- package/dist/types/Documentation_Category.js +12 -0
- package/dist/types/Documentation_Config.d.ts +21 -0
- package/dist/types/Documentation_Config.js +29 -0
- package/dist/types/Documentation_Document.d.ts +6 -0
- package/dist/types/Documentation_Document.js +12 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.js +5 -3
- package/package.json +3 -1
- package/schema/Documentation_Category.json +9 -0
- package/schema/Documentation_Config.json +50 -0
- 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,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,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
|
+
}
|