@mortice/mapwright 0.8.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/CHANGELOG.md +161 -0
- package/LICENSE +21 -0
- package/README.md +262 -0
- package/dist/catalogue/canonicalisation.d.ts +36 -0
- package/dist/catalogue/canonicalisation.js +262 -0
- package/dist/catalogue/config.d.ts +13 -0
- package/dist/catalogue/config.js +77 -0
- package/dist/catalogue/deterministic.d.ts +3 -0
- package/dist/catalogue/deterministic.js +25 -0
- package/dist/catalogue/diagnostics.d.ts +2 -0
- package/dist/catalogue/diagnostics.js +34 -0
- package/dist/catalogue/evidence.d.ts +1 -0
- package/dist/catalogue/evidence.js +10 -0
- package/dist/catalogue/examples.d.ts +3 -0
- package/dist/catalogue/examples.js +20 -0
- package/dist/catalogue/identity.d.ts +61 -0
- package/dist/catalogue/identity.js +114 -0
- package/dist/catalogue/ignore.d.ts +4 -0
- package/dist/catalogue/ignore.js +29 -0
- package/dist/catalogue/indexer.d.ts +20 -0
- package/dist/catalogue/indexer.js +1393 -0
- package/dist/catalogue/linkage.d.ts +16 -0
- package/dist/catalogue/linkage.js +419 -0
- package/dist/catalogue/moduleResolution.d.ts +4 -0
- package/dist/catalogue/moduleResolution.js +67 -0
- package/dist/catalogue/ordering.d.ts +13 -0
- package/dist/catalogue/ordering.js +86 -0
- package/dist/catalogue/packages.d.ts +12 -0
- package/dist/catalogue/packages.js +475 -0
- package/dist/catalogue/pythonReExports.d.ts +7 -0
- package/dist/catalogue/pythonReExports.js +113 -0
- package/dist/catalogue/resolveRefs.d.ts +40 -0
- package/dist/catalogue/resolveRefs.js +284 -0
- package/dist/catalogue/scanner.d.ts +8 -0
- package/dist/catalogue/scanner.js +99 -0
- package/dist/catalogue/schema.d.ts +289 -0
- package/dist/catalogue/schema.js +1 -0
- package/dist/catalogue/store.d.ts +41 -0
- package/dist/catalogue/store.js +209 -0
- package/dist/catalogue/templates.d.ts +18 -0
- package/dist/catalogue/templates.js +158 -0
- package/dist/catalogue/version.d.ts +12 -0
- package/dist/catalogue/version.js +30 -0
- package/dist/cli/commands/adoptionPlan.d.ts +8 -0
- package/dist/cli/commands/adoptionPlan.js +24 -0
- package/dist/cli/commands/auditDocs.d.ts +8 -0
- package/dist/cli/commands/auditDocs.js +24 -0
- package/dist/cli/commands/changeImpact.d.ts +10 -0
- package/dist/cli/commands/changeImpact.js +36 -0
- package/dist/cli/commands/changes.d.ts +12 -0
- package/dist/cli/commands/changes.js +31 -0
- package/dist/cli/commands/check.d.ts +9 -0
- package/dist/cli/commands/check.js +4 -0
- package/dist/cli/commands/compatibility.d.ts +10 -0
- package/dist/cli/commands/compatibility.js +43 -0
- package/dist/cli/commands/diff.d.ts +31 -0
- package/dist/cli/commands/diff.js +118 -0
- package/dist/cli/commands/dogfood.d.ts +8 -0
- package/dist/cli/commands/dogfood.js +24 -0
- package/dist/cli/commands/drift.d.ts +8 -0
- package/dist/cli/commands/drift.js +13 -0
- package/dist/cli/commands/index.d.ts +9 -0
- package/dist/cli/commands/index.js +9 -0
- package/dist/cli/commands/indexWorkspace.d.ts +8 -0
- package/dist/cli/commands/indexWorkspace.js +23 -0
- package/dist/cli/commands/recommendConfig.d.ts +7 -0
- package/dist/cli/commands/recommendConfig.js +20 -0
- package/dist/cli/commands/schema.d.ts +1 -0
- package/dist/cli/commands/schema.js +37 -0
- package/dist/cli/commands/template.d.ts +5 -0
- package/dist/cli/commands/template.js +44 -0
- package/dist/cli/commands/validate.d.ts +9 -0
- package/dist/cli/commands/validate.js +85 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +211 -0
- package/dist/comparison/artefactValidate.d.ts +19 -0
- package/dist/comparison/artefactValidate.js +334 -0
- package/dist/comparison/candidates.d.ts +12 -0
- package/dist/comparison/candidates.js +391 -0
- package/dist/comparison/compare.d.ts +15 -0
- package/dist/comparison/compare.js +240 -0
- package/dist/comparison/compatibility.d.ts +26 -0
- package/dist/comparison/compatibility.js +316 -0
- package/dist/comparison/drift.d.ts +34 -0
- package/dist/comparison/drift.js +71 -0
- package/dist/comparison/identity.d.ts +30 -0
- package/dist/comparison/identity.js +73 -0
- package/dist/comparison/impact.d.ts +14 -0
- package/dist/comparison/impact.js +291 -0
- package/dist/comparison/index.d.ts +6 -0
- package/dist/comparison/index.js +6 -0
- package/dist/comparison/integrity.d.ts +12 -0
- package/dist/comparison/integrity.js +199 -0
- package/dist/comparison/ordering.d.ts +4 -0
- package/dist/comparison/ordering.js +69 -0
- package/dist/comparison/projections.d.ts +19 -0
- package/dist/comparison/projections.js +185 -0
- package/dist/comparison/relationshipPolicy.d.ts +19 -0
- package/dist/comparison/relationshipPolicy.js +98 -0
- package/dist/comparison/schema.d.ts +178 -0
- package/dist/comparison/schema.js +16 -0
- package/dist/comparison/store.d.ts +58 -0
- package/dist/comparison/store.js +462 -0
- package/dist/comparison/validate.d.ts +23 -0
- package/dist/comparison/validate.js +137 -0
- package/dist/comparison/version.d.ts +9 -0
- package/dist/comparison/version.js +11 -0
- package/dist/extractors/docTags.d.ts +9 -0
- package/dist/extractors/docTags.js +106 -0
- package/dist/extractors/generic.d.ts +5 -0
- package/dist/extractors/generic.js +3 -0
- package/dist/extractors/javascript.d.ts +3 -0
- package/dist/extractors/javascript.js +4 -0
- package/dist/extractors/markdown.d.ts +7 -0
- package/dist/extractors/markdown.js +269 -0
- package/dist/extractors/python.d.ts +7 -0
- package/dist/extractors/python.js +314 -0
- package/dist/extractors/typescript.d.ts +7 -0
- package/dist/extractors/typescript.js +387 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/mcp/resources.d.ts +18 -0
- package/dist/mcp/resources.js +57 -0
- package/dist/mcp/server.d.ts +2 -0
- package/dist/mcp/server.js +95 -0
- package/dist/mcp/tools.d.ts +29 -0
- package/dist/mcp/tools.js +902 -0
- package/dist/queries/adoption.d.ts +62 -0
- package/dist/queries/adoption.js +366 -0
- package/dist/queries/capabilities.d.ts +45 -0
- package/dist/queries/capabilities.js +124 -0
- package/dist/queries/changes.d.ts +33 -0
- package/dist/queries/changes.js +53 -0
- package/dist/queries/dependencies.d.ts +17 -0
- package/dist/queries/dependencies.js +94 -0
- package/dist/queries/dogfood.d.ts +50 -0
- package/dist/queries/dogfood.js +116 -0
- package/dist/queries/editingContext.d.ts +84 -0
- package/dist/queries/editingContext.js +240 -0
- package/dist/queries/examples.d.ts +8 -0
- package/dist/queries/examples.js +16 -0
- package/dist/queries/files.d.ts +8 -0
- package/dist/queries/files.js +14 -0
- package/dist/queries/lists.d.ts +94 -0
- package/dist/queries/lists.js +184 -0
- package/dist/queries/overview.d.ts +2 -0
- package/dist/queries/overview.js +8 -0
- package/dist/queries/publicApis.d.ts +60 -0
- package/dist/queries/publicApis.js +120 -0
- package/dist/queries/ranking.d.ts +67 -0
- package/dist/queries/ranking.js +387 -0
- package/dist/queries/recommendConfig.d.ts +47 -0
- package/dist/queries/recommendConfig.js +123 -0
- package/dist/queries/relationships.d.ts +5 -0
- package/dist/queries/relationships.js +15 -0
- package/dist/queries/reuseCandidates.d.ts +34 -0
- package/dist/queries/reuseCandidates.js +181 -0
- package/dist/queries/symbols.d.ts +40 -0
- package/dist/queries/symbols.js +41 -0
- package/dist/security/pathPolicy.d.ts +12 -0
- package/dist/security/pathPolicy.js +46 -0
- package/dist/security/secretPatterns.d.ts +1 -0
- package/dist/security/secretPatterns.js +4 -0
- package/dist/security/sourceAccess.d.ts +12 -0
- package/dist/security/sourceAccess.js +35 -0
- package/package.json +60 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
const VALID_TAGS = new Set([
|
|
2
|
+
"catalogue",
|
|
3
|
+
"capability",
|
|
4
|
+
"publicApi",
|
|
5
|
+
"internalApi",
|
|
6
|
+
"stability",
|
|
7
|
+
"intendedUse",
|
|
8
|
+
"inputs",
|
|
9
|
+
"returns",
|
|
10
|
+
"contract",
|
|
11
|
+
"errors",
|
|
12
|
+
"example",
|
|
13
|
+
"tests",
|
|
14
|
+
"coversCapability",
|
|
15
|
+
"coversSymbol",
|
|
16
|
+
"coversPublicApi",
|
|
17
|
+
"editable",
|
|
18
|
+
"editPolicy"
|
|
19
|
+
]);
|
|
20
|
+
const VALID_STABILITY = ["stable", "experimental", "deprecated", "internal", "test-only", "unknown"];
|
|
21
|
+
const VALID_EDITABLE = ["none", "allowed", "restricted", "maintainer-only", "unknown"];
|
|
22
|
+
const VALID_VISIBILITY = [
|
|
23
|
+
"published",
|
|
24
|
+
"supported_internal",
|
|
25
|
+
"private",
|
|
26
|
+
"test_only",
|
|
27
|
+
"experimental",
|
|
28
|
+
"deprecated",
|
|
29
|
+
"ignored",
|
|
30
|
+
"unknown"
|
|
31
|
+
];
|
|
32
|
+
export function parseDocTags(text, filePath) {
|
|
33
|
+
const lines = text.split(/\r?\n/);
|
|
34
|
+
const tags = [];
|
|
35
|
+
const diagnostics = [];
|
|
36
|
+
const summaryLines = [];
|
|
37
|
+
for (const rawLine of lines) {
|
|
38
|
+
const line = rawLine.replace(/^\s*\*\s?/, "").trim();
|
|
39
|
+
if (!line)
|
|
40
|
+
continue;
|
|
41
|
+
const tagMatch = line.match(/^@([A-Za-z][A-Za-z0-9]*)\s*(.*)$/);
|
|
42
|
+
if (tagMatch) {
|
|
43
|
+
const [, tag, valueRaw] = tagMatch;
|
|
44
|
+
const value = valueRaw.trim();
|
|
45
|
+
if (!VALID_TAGS.has(tag)) {
|
|
46
|
+
diagnostics.push({
|
|
47
|
+
id: `diag:${filePath}:invalid_catalogue_tag:${tag}`,
|
|
48
|
+
severity: "warning",
|
|
49
|
+
category: "documentation",
|
|
50
|
+
code: "invalid_catalogue_tag",
|
|
51
|
+
message: `Unknown documentation tag @${tag}.`,
|
|
52
|
+
filePath,
|
|
53
|
+
provenance: { source: "doc-tags" }
|
|
54
|
+
});
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (tag === "stability" && value && !VALID_STABILITY.includes(value)) {
|
|
58
|
+
diagnostics.push({
|
|
59
|
+
id: `diag:${filePath}:invalid_stability_value:${value}`,
|
|
60
|
+
severity: "warning",
|
|
61
|
+
category: "documentation",
|
|
62
|
+
code: "invalid_stability_value",
|
|
63
|
+
message: `Invalid @stability value: ${value}.`,
|
|
64
|
+
filePath,
|
|
65
|
+
provenance: { source: "doc-tags" }
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
if (tag === "editable" && value && !VALID_EDITABLE.includes(value)) {
|
|
69
|
+
diagnostics.push({
|
|
70
|
+
id: `diag:${filePath}:invalid_editability_value:${value}`,
|
|
71
|
+
severity: "warning",
|
|
72
|
+
category: "documentation",
|
|
73
|
+
code: "invalid_editability_value",
|
|
74
|
+
message: `Invalid @editable value: ${value}.`,
|
|
75
|
+
filePath,
|
|
76
|
+
provenance: { source: "doc-tags" }
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
if (tag === "catalogue" && value && !VALID_VISIBILITY.includes(value) && !["public-api", "internal-api", "test", "example", "file", "package", "capability", "ignore"].includes(value)) {
|
|
80
|
+
diagnostics.push({
|
|
81
|
+
id: `diag:${filePath}:invalid_catalogue_tag:catalogue`,
|
|
82
|
+
severity: "warning",
|
|
83
|
+
category: "documentation",
|
|
84
|
+
code: "invalid_catalogue_tag",
|
|
85
|
+
message: `Potentially invalid @catalogue value: ${value}.`,
|
|
86
|
+
filePath,
|
|
87
|
+
provenance: { source: "doc-tags" }
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
tags.push({ tag, value });
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
summaryLines.push(line);
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
summary: summaryLines.join(" ").trim(),
|
|
97
|
+
tags,
|
|
98
|
+
diagnostics
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
export function firstTag(tags, name) {
|
|
102
|
+
return tags.find((t) => t.tag === name)?.value;
|
|
103
|
+
}
|
|
104
|
+
export function manyTags(tags, name) {
|
|
105
|
+
return tags.filter((t) => t.tag === name).map((t) => t.value).filter(Boolean);
|
|
106
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { FileRecord, PackageRecord } from "../catalogue/schema.js";
|
|
2
|
+
import { type TsExtractionResult } from "./typescript.js";
|
|
3
|
+
export declare function extractJavaScriptSymbols(workspaceRoot: string, file: FileRecord, packageRecord?: PackageRecord): TsExtractionResult;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { CapabilityRecord, ExampleRecord, PackageRecord } from "../catalogue/schema.js";
|
|
2
|
+
export interface MarkdownCatalogueResult {
|
|
3
|
+
packages: PackageRecord[];
|
|
4
|
+
capabilities: CapabilityRecord[];
|
|
5
|
+
examples: ExampleRecord[];
|
|
6
|
+
}
|
|
7
|
+
export declare function extractCatalogueMarkdown(workspaceRoot: string, filePath: string): MarkdownCatalogueResult;
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { buildDeterministicExampleId, normalizeExamplePath } from "../catalogue/examples.js";
|
|
4
|
+
import { packageIdentityId } from "../catalogue/identity.js";
|
|
5
|
+
export function extractCatalogueMarkdown(workspaceRoot, filePath) {
|
|
6
|
+
const absolute = path.resolve(workspaceRoot, filePath);
|
|
7
|
+
const text = readFileSync(absolute, "utf8");
|
|
8
|
+
const lines = text.split(/\r?\n/);
|
|
9
|
+
const packages = [];
|
|
10
|
+
const capabilities = [];
|
|
11
|
+
const examples = [];
|
|
12
|
+
const sectionRanges = splitSections(lines);
|
|
13
|
+
const normalizedPath = path.dirname(filePath).replaceAll(path.sep, "/") || ".";
|
|
14
|
+
const packageSection = sectionRanges.find((section) => section.title.toLowerCase() === "package");
|
|
15
|
+
const fallbackPackageName = readInlineField(lines, "Name:");
|
|
16
|
+
const packageName = readSectionField(lines, packageSection, "Name") ?? fallbackPackageName;
|
|
17
|
+
const packageId = packageName
|
|
18
|
+
? packageIdentityId({ ecosystem: "unknown", name: normalizeMarkdownValue(packageName), root: normalizedPath })
|
|
19
|
+
: undefined;
|
|
20
|
+
if (packageName) {
|
|
21
|
+
const purpose = readSectionField(lines, packageSection, "Purpose");
|
|
22
|
+
const summary = readSectionField(lines, packageSection, "Summary") ?? purpose;
|
|
23
|
+
const stability = readSectionField(lines, packageSection, "Stability") ?? "unknown";
|
|
24
|
+
packages.push({
|
|
25
|
+
id: packageId,
|
|
26
|
+
name: normalizeMarkdownValue(packageName),
|
|
27
|
+
path: normalizedPath,
|
|
28
|
+
summary,
|
|
29
|
+
capabilities: [],
|
|
30
|
+
publicApis: [],
|
|
31
|
+
tests: [],
|
|
32
|
+
stability: isStability(stability) ? stability : "unknown",
|
|
33
|
+
provenance: { source: "catalogue-md", locations: [filePath] }
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
for (const section of sectionRanges) {
|
|
37
|
+
const capabilityHeading = section.title.match(/^Capability:\s+(.+)$/i);
|
|
38
|
+
if (!capabilityHeading) {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
const rawCapability = normalizeMarkdownValue(capabilityHeading[1]);
|
|
42
|
+
const capabilityName = normalizeMarkdownValue(readSectionField(lines, section, "Name") ?? rawCapability);
|
|
43
|
+
const summary = readSectionField(lines, section, "Summary");
|
|
44
|
+
const stability = readSectionField(lines, section, "Stability") ?? "unknown";
|
|
45
|
+
const intendedUse = readSectionField(lines, section, "Intended use");
|
|
46
|
+
const publicApiRefs = readSectionBullets(lines, section, "Public APIs");
|
|
47
|
+
const capabilityTests = readSectionBullets(lines, section, "Tests");
|
|
48
|
+
const contracts = readSectionBullets(lines, section, "Contracts");
|
|
49
|
+
const limitations = readSectionBullets(lines, section, "Limitations");
|
|
50
|
+
const capabilityId = normalizeCapabilityId(rawCapability);
|
|
51
|
+
const capabilityExamples = readSectionBullets(lines, section, "Examples");
|
|
52
|
+
const exampleIds = [];
|
|
53
|
+
for (const reference of capabilityExamples) {
|
|
54
|
+
const parsedRef = parseExampleReference(reference);
|
|
55
|
+
const exampleId = buildDeterministicExampleId(parsedRef.filePath, parsedRef.anchor, reference);
|
|
56
|
+
exampleIds.push(exampleId);
|
|
57
|
+
examples.push({
|
|
58
|
+
id: exampleId,
|
|
59
|
+
title: parsedRef.title,
|
|
60
|
+
filePath: parsedRef.filePath,
|
|
61
|
+
anchor: parsedRef.anchor,
|
|
62
|
+
capabilityIds: [parsedRef.capabilityId ? normalizeCapabilityId(parsedRef.capabilityId) : capabilityId],
|
|
63
|
+
// Path-only bullets link the capability; public API linkage comes from explicit
|
|
64
|
+
// public-api: tokens and/or file-level @coversPublicApi annotations.
|
|
65
|
+
publicApiIds: parsedRef.publicApiRefs.map((ref) => normalizeMarkdownValue(ref)),
|
|
66
|
+
symbolIds: [],
|
|
67
|
+
summary: undefined,
|
|
68
|
+
provenance: {
|
|
69
|
+
source: "catalogue-md",
|
|
70
|
+
locations: [filePath],
|
|
71
|
+
warnings: parsedRef.warnings.length > 0 ? parsedRef.warnings : undefined
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
capabilities.push({
|
|
76
|
+
id: capabilityId,
|
|
77
|
+
name: capabilityName,
|
|
78
|
+
summary,
|
|
79
|
+
keywords: rawCapability.split(/[.\-]/).filter(Boolean),
|
|
80
|
+
status: "available",
|
|
81
|
+
stability: isStability(stability) ? stability : "unknown",
|
|
82
|
+
intendedUse,
|
|
83
|
+
publicApis: publicApiRefs,
|
|
84
|
+
symbols: [],
|
|
85
|
+
files: [filePath],
|
|
86
|
+
packages: packageId ? [packageId] : [],
|
|
87
|
+
examples: exampleIds,
|
|
88
|
+
tests: capabilityTests,
|
|
89
|
+
contracts,
|
|
90
|
+
limitations,
|
|
91
|
+
provenance: { source: "catalogue-md", locations: [filePath] }
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
packages: packages.sort((a, b) => a.id.localeCompare(b.id)),
|
|
96
|
+
capabilities: capabilities.sort((a, b) => a.id.localeCompare(b.id)),
|
|
97
|
+
examples: examples.sort((a, b) => a.id.localeCompare(b.id))
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function readInlineField(lines, label) {
|
|
101
|
+
const labelLower = label.toLowerCase();
|
|
102
|
+
for (const line of lines) {
|
|
103
|
+
const trimmed = line.trimStart();
|
|
104
|
+
if (!trimmed.toLowerCase().startsWith(labelLower)) {
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
const inline = trimmed.slice(label.length);
|
|
108
|
+
return normalizeMarkdownValue(inline.trim());
|
|
109
|
+
}
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
function splitSections(lines) {
|
|
113
|
+
const sections = [];
|
|
114
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
115
|
+
const match = lines[i]?.match(/^##\s+(.+)$/);
|
|
116
|
+
if (!match) {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
sections.push({ title: match[1].trim(), start: i + 1, end: lines.length - 1 });
|
|
120
|
+
}
|
|
121
|
+
for (let i = 0; i < sections.length; i += 1) {
|
|
122
|
+
const next = sections[i + 1];
|
|
123
|
+
if (next) {
|
|
124
|
+
sections[i].end = next.start - 2;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return sections;
|
|
128
|
+
}
|
|
129
|
+
function readSectionField(lines, section, label) {
|
|
130
|
+
if (!section) {
|
|
131
|
+
return undefined;
|
|
132
|
+
}
|
|
133
|
+
const labelPattern = new RegExp(`^${escapeForRegex(label)}\\s*:\\s*(.*)$`, "i");
|
|
134
|
+
for (let i = section.start; i <= section.end; i += 1) {
|
|
135
|
+
const line = lines[i] ?? "";
|
|
136
|
+
const match = line.trim().match(labelPattern);
|
|
137
|
+
if (!match) {
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
const inline = normalizeMarkdownValue(match[1] ?? "");
|
|
141
|
+
if (inline) {
|
|
142
|
+
return inline;
|
|
143
|
+
}
|
|
144
|
+
const out = [];
|
|
145
|
+
for (let j = i + 1; j <= section.end; j += 1) {
|
|
146
|
+
const raw = lines[j] ?? "";
|
|
147
|
+
const trimmed = raw.trim();
|
|
148
|
+
if (!trimmed) {
|
|
149
|
+
if (out.length > 0) {
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
if (/^[A-Za-z][A-Za-z\s]+\s*:/.test(trimmed)) {
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
if (/^-\s+/.test(trimmed)) {
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
out.push(normalizeMarkdownValue(trimmed));
|
|
161
|
+
}
|
|
162
|
+
return out.length > 0 ? out.join(" ").trim() : undefined;
|
|
163
|
+
}
|
|
164
|
+
return undefined;
|
|
165
|
+
}
|
|
166
|
+
function readSectionBullets(lines, section, label) {
|
|
167
|
+
const labelPattern = new RegExp(`^${escapeForRegex(label)}\\s*:\\s*$`, "i");
|
|
168
|
+
const out = [];
|
|
169
|
+
for (let i = section.start; i <= section.end; i += 1) {
|
|
170
|
+
const line = lines[i] ?? "";
|
|
171
|
+
if (!labelPattern.test(line.trim())) {
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
for (let j = i + 1; j <= section.end; j += 1) {
|
|
175
|
+
const item = lines[j] ?? "";
|
|
176
|
+
const trimmed = item.trim();
|
|
177
|
+
if (!trimmed) {
|
|
178
|
+
if (out.length > 0) {
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
const bullet = trimmed.match(/^-\s+(.+)$/);
|
|
184
|
+
if (!bullet) {
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
out.push(normalizeMarkdownValue(bullet[1]));
|
|
188
|
+
}
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
return out;
|
|
192
|
+
}
|
|
193
|
+
function normalizeCapabilityId(value) {
|
|
194
|
+
const normalized = value.replace(/^capability:/i, "").trim();
|
|
195
|
+
return `capability:${normalized}`;
|
|
196
|
+
}
|
|
197
|
+
function normalizeMarkdownValue(value) {
|
|
198
|
+
return value.replace(/`/g, "").trim();
|
|
199
|
+
}
|
|
200
|
+
function parseExampleReference(reference) {
|
|
201
|
+
const normalized = normalizeMarkdownValue(reference);
|
|
202
|
+
if (!normalized) {
|
|
203
|
+
return { publicApiRefs: [], warnings: ["example_reference_empty"] };
|
|
204
|
+
}
|
|
205
|
+
const metadataParts = normalized.split("|").map((part) => part.trim()).filter(Boolean);
|
|
206
|
+
let referencePart;
|
|
207
|
+
let capabilityId;
|
|
208
|
+
const publicApiRefs = [];
|
|
209
|
+
const warnings = [];
|
|
210
|
+
for (const part of metadataParts) {
|
|
211
|
+
const lower = part.toLowerCase();
|
|
212
|
+
if (lower.startsWith("capability:")) {
|
|
213
|
+
const value = part.slice("capability:".length).trim();
|
|
214
|
+
if (value) {
|
|
215
|
+
capabilityId = value;
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
warnings.push("example_metadata_capability_empty");
|
|
219
|
+
}
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
if (lower.startsWith("public-api:")) {
|
|
223
|
+
const value = part.slice("public-api:".length).trim();
|
|
224
|
+
if (value) {
|
|
225
|
+
publicApiRefs.push(value);
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
warnings.push("example_metadata_public_api_empty");
|
|
229
|
+
}
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
if (/^[a-z0-9_-]+:/i.test(part) && metadataParts.length > 1) {
|
|
233
|
+
warnings.push("example_metadata_unknown_token");
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
referencePart = part;
|
|
237
|
+
}
|
|
238
|
+
if (!referencePart && metadataParts.length === 1) {
|
|
239
|
+
referencePart = metadataParts[0];
|
|
240
|
+
}
|
|
241
|
+
if (!referencePart) {
|
|
242
|
+
warnings.push("example_missing_file_path");
|
|
243
|
+
return { capabilityId, publicApiRefs: [...new Set(publicApiRefs)].sort(), warnings };
|
|
244
|
+
}
|
|
245
|
+
const [rawPath, rawAnchor] = referencePart.split("#", 2);
|
|
246
|
+
const filePathCandidate = normalizeExamplePath(rawPath?.trim() || undefined);
|
|
247
|
+
const filePathLooksMetadata = typeof filePathCandidate === "string"
|
|
248
|
+
&& (filePathCandidate.toLowerCase().startsWith("capability:") || filePathCandidate.toLowerCase().startsWith("public-api:"));
|
|
249
|
+
const filePath = filePathLooksMetadata ? undefined : filePathCandidate;
|
|
250
|
+
if (!filePath) {
|
|
251
|
+
warnings.push("example_missing_file_path");
|
|
252
|
+
}
|
|
253
|
+
const anchor = rawAnchor?.trim() || undefined;
|
|
254
|
+
const title = anchor ? anchor.replace(/[-_]/g, " ") : filePath ? path.basename(filePath) : undefined;
|
|
255
|
+
return {
|
|
256
|
+
filePath,
|
|
257
|
+
anchor,
|
|
258
|
+
title,
|
|
259
|
+
capabilityId,
|
|
260
|
+
publicApiRefs: [...new Set(publicApiRefs)].sort(),
|
|
261
|
+
warnings: [...new Set(warnings)].sort()
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
function escapeForRegex(value) {
|
|
265
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
266
|
+
}
|
|
267
|
+
function isStability(value) {
|
|
268
|
+
return ["stable", "experimental", "deprecated", "internal", "test-only", "unknown"].includes(value);
|
|
269
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { DiagnosticRecord, FileRecord, PackageRecord, RelationshipRecord, SymbolRecord } from "../catalogue/schema.js";
|
|
2
|
+
export interface PythonExtractionResult {
|
|
3
|
+
symbols: SymbolRecord[];
|
|
4
|
+
relationships: RelationshipRecord[];
|
|
5
|
+
diagnostics: DiagnosticRecord[];
|
|
6
|
+
}
|
|
7
|
+
export declare function extractPythonSymbols(workspaceRoot: string, file: FileRecord, packageRecord?: PackageRecord): PythonExtractionResult;
|