@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.
Files changed (166) hide show
  1. package/CHANGELOG.md +161 -0
  2. package/LICENSE +21 -0
  3. package/README.md +262 -0
  4. package/dist/catalogue/canonicalisation.d.ts +36 -0
  5. package/dist/catalogue/canonicalisation.js +262 -0
  6. package/dist/catalogue/config.d.ts +13 -0
  7. package/dist/catalogue/config.js +77 -0
  8. package/dist/catalogue/deterministic.d.ts +3 -0
  9. package/dist/catalogue/deterministic.js +25 -0
  10. package/dist/catalogue/diagnostics.d.ts +2 -0
  11. package/dist/catalogue/diagnostics.js +34 -0
  12. package/dist/catalogue/evidence.d.ts +1 -0
  13. package/dist/catalogue/evidence.js +10 -0
  14. package/dist/catalogue/examples.d.ts +3 -0
  15. package/dist/catalogue/examples.js +20 -0
  16. package/dist/catalogue/identity.d.ts +61 -0
  17. package/dist/catalogue/identity.js +114 -0
  18. package/dist/catalogue/ignore.d.ts +4 -0
  19. package/dist/catalogue/ignore.js +29 -0
  20. package/dist/catalogue/indexer.d.ts +20 -0
  21. package/dist/catalogue/indexer.js +1393 -0
  22. package/dist/catalogue/linkage.d.ts +16 -0
  23. package/dist/catalogue/linkage.js +419 -0
  24. package/dist/catalogue/moduleResolution.d.ts +4 -0
  25. package/dist/catalogue/moduleResolution.js +67 -0
  26. package/dist/catalogue/ordering.d.ts +13 -0
  27. package/dist/catalogue/ordering.js +86 -0
  28. package/dist/catalogue/packages.d.ts +12 -0
  29. package/dist/catalogue/packages.js +475 -0
  30. package/dist/catalogue/pythonReExports.d.ts +7 -0
  31. package/dist/catalogue/pythonReExports.js +113 -0
  32. package/dist/catalogue/resolveRefs.d.ts +40 -0
  33. package/dist/catalogue/resolveRefs.js +284 -0
  34. package/dist/catalogue/scanner.d.ts +8 -0
  35. package/dist/catalogue/scanner.js +99 -0
  36. package/dist/catalogue/schema.d.ts +289 -0
  37. package/dist/catalogue/schema.js +1 -0
  38. package/dist/catalogue/store.d.ts +41 -0
  39. package/dist/catalogue/store.js +209 -0
  40. package/dist/catalogue/templates.d.ts +18 -0
  41. package/dist/catalogue/templates.js +158 -0
  42. package/dist/catalogue/version.d.ts +12 -0
  43. package/dist/catalogue/version.js +30 -0
  44. package/dist/cli/commands/adoptionPlan.d.ts +8 -0
  45. package/dist/cli/commands/adoptionPlan.js +24 -0
  46. package/dist/cli/commands/auditDocs.d.ts +8 -0
  47. package/dist/cli/commands/auditDocs.js +24 -0
  48. package/dist/cli/commands/changeImpact.d.ts +10 -0
  49. package/dist/cli/commands/changeImpact.js +36 -0
  50. package/dist/cli/commands/changes.d.ts +12 -0
  51. package/dist/cli/commands/changes.js +31 -0
  52. package/dist/cli/commands/check.d.ts +9 -0
  53. package/dist/cli/commands/check.js +4 -0
  54. package/dist/cli/commands/compatibility.d.ts +10 -0
  55. package/dist/cli/commands/compatibility.js +43 -0
  56. package/dist/cli/commands/diff.d.ts +31 -0
  57. package/dist/cli/commands/diff.js +118 -0
  58. package/dist/cli/commands/dogfood.d.ts +8 -0
  59. package/dist/cli/commands/dogfood.js +24 -0
  60. package/dist/cli/commands/drift.d.ts +8 -0
  61. package/dist/cli/commands/drift.js +13 -0
  62. package/dist/cli/commands/index.d.ts +9 -0
  63. package/dist/cli/commands/index.js +9 -0
  64. package/dist/cli/commands/indexWorkspace.d.ts +8 -0
  65. package/dist/cli/commands/indexWorkspace.js +23 -0
  66. package/dist/cli/commands/recommendConfig.d.ts +7 -0
  67. package/dist/cli/commands/recommendConfig.js +20 -0
  68. package/dist/cli/commands/schema.d.ts +1 -0
  69. package/dist/cli/commands/schema.js +37 -0
  70. package/dist/cli/commands/template.d.ts +5 -0
  71. package/dist/cli/commands/template.js +44 -0
  72. package/dist/cli/commands/validate.d.ts +9 -0
  73. package/dist/cli/commands/validate.js +85 -0
  74. package/dist/cli/index.d.ts +2 -0
  75. package/dist/cli/index.js +211 -0
  76. package/dist/comparison/artefactValidate.d.ts +19 -0
  77. package/dist/comparison/artefactValidate.js +334 -0
  78. package/dist/comparison/candidates.d.ts +12 -0
  79. package/dist/comparison/candidates.js +391 -0
  80. package/dist/comparison/compare.d.ts +15 -0
  81. package/dist/comparison/compare.js +240 -0
  82. package/dist/comparison/compatibility.d.ts +26 -0
  83. package/dist/comparison/compatibility.js +316 -0
  84. package/dist/comparison/drift.d.ts +34 -0
  85. package/dist/comparison/drift.js +71 -0
  86. package/dist/comparison/identity.d.ts +30 -0
  87. package/dist/comparison/identity.js +73 -0
  88. package/dist/comparison/impact.d.ts +14 -0
  89. package/dist/comparison/impact.js +291 -0
  90. package/dist/comparison/index.d.ts +6 -0
  91. package/dist/comparison/index.js +6 -0
  92. package/dist/comparison/integrity.d.ts +12 -0
  93. package/dist/comparison/integrity.js +199 -0
  94. package/dist/comparison/ordering.d.ts +4 -0
  95. package/dist/comparison/ordering.js +69 -0
  96. package/dist/comparison/projections.d.ts +19 -0
  97. package/dist/comparison/projections.js +185 -0
  98. package/dist/comparison/relationshipPolicy.d.ts +19 -0
  99. package/dist/comparison/relationshipPolicy.js +98 -0
  100. package/dist/comparison/schema.d.ts +178 -0
  101. package/dist/comparison/schema.js +16 -0
  102. package/dist/comparison/store.d.ts +58 -0
  103. package/dist/comparison/store.js +462 -0
  104. package/dist/comparison/validate.d.ts +23 -0
  105. package/dist/comparison/validate.js +137 -0
  106. package/dist/comparison/version.d.ts +9 -0
  107. package/dist/comparison/version.js +11 -0
  108. package/dist/extractors/docTags.d.ts +9 -0
  109. package/dist/extractors/docTags.js +106 -0
  110. package/dist/extractors/generic.d.ts +5 -0
  111. package/dist/extractors/generic.js +3 -0
  112. package/dist/extractors/javascript.d.ts +3 -0
  113. package/dist/extractors/javascript.js +4 -0
  114. package/dist/extractors/markdown.d.ts +7 -0
  115. package/dist/extractors/markdown.js +269 -0
  116. package/dist/extractors/python.d.ts +7 -0
  117. package/dist/extractors/python.js +314 -0
  118. package/dist/extractors/typescript.d.ts +7 -0
  119. package/dist/extractors/typescript.js +387 -0
  120. package/dist/index.d.ts +2 -0
  121. package/dist/index.js +2 -0
  122. package/dist/mcp/resources.d.ts +18 -0
  123. package/dist/mcp/resources.js +57 -0
  124. package/dist/mcp/server.d.ts +2 -0
  125. package/dist/mcp/server.js +95 -0
  126. package/dist/mcp/tools.d.ts +29 -0
  127. package/dist/mcp/tools.js +902 -0
  128. package/dist/queries/adoption.d.ts +62 -0
  129. package/dist/queries/adoption.js +366 -0
  130. package/dist/queries/capabilities.d.ts +45 -0
  131. package/dist/queries/capabilities.js +124 -0
  132. package/dist/queries/changes.d.ts +33 -0
  133. package/dist/queries/changes.js +53 -0
  134. package/dist/queries/dependencies.d.ts +17 -0
  135. package/dist/queries/dependencies.js +94 -0
  136. package/dist/queries/dogfood.d.ts +50 -0
  137. package/dist/queries/dogfood.js +116 -0
  138. package/dist/queries/editingContext.d.ts +84 -0
  139. package/dist/queries/editingContext.js +240 -0
  140. package/dist/queries/examples.d.ts +8 -0
  141. package/dist/queries/examples.js +16 -0
  142. package/dist/queries/files.d.ts +8 -0
  143. package/dist/queries/files.js +14 -0
  144. package/dist/queries/lists.d.ts +94 -0
  145. package/dist/queries/lists.js +184 -0
  146. package/dist/queries/overview.d.ts +2 -0
  147. package/dist/queries/overview.js +8 -0
  148. package/dist/queries/publicApis.d.ts +60 -0
  149. package/dist/queries/publicApis.js +120 -0
  150. package/dist/queries/ranking.d.ts +67 -0
  151. package/dist/queries/ranking.js +387 -0
  152. package/dist/queries/recommendConfig.d.ts +47 -0
  153. package/dist/queries/recommendConfig.js +123 -0
  154. package/dist/queries/relationships.d.ts +5 -0
  155. package/dist/queries/relationships.js +15 -0
  156. package/dist/queries/reuseCandidates.d.ts +34 -0
  157. package/dist/queries/reuseCandidates.js +181 -0
  158. package/dist/queries/symbols.d.ts +40 -0
  159. package/dist/queries/symbols.js +41 -0
  160. package/dist/security/pathPolicy.d.ts +12 -0
  161. package/dist/security/pathPolicy.js +46 -0
  162. package/dist/security/secretPatterns.d.ts +1 -0
  163. package/dist/security/secretPatterns.js +4 -0
  164. package/dist/security/sourceAccess.d.ts +12 -0
  165. package/dist/security/sourceAccess.js +35 -0
  166. package/package.json +60 -0
@@ -0,0 +1,284 @@
1
+ export function isGeneratedArtefactPath(filePath) {
2
+ const normalized = filePath.replaceAll("\\", "/");
3
+ return /(^|\/)dist\//.test(normalized) || /(^|\/)build\//.test(normalized) || /(^|\/)out\//.test(normalized);
4
+ }
5
+ /** Prefer source symbols/APIs over generated dist/build artefacts when ranking matches. */
6
+ export function preferSourceIds(ids, filePathForId) {
7
+ if (ids.length <= 1) {
8
+ return ids;
9
+ }
10
+ const source = ids.filter((id) => {
11
+ const filePath = filePathForId(id);
12
+ return filePath ? !isGeneratedArtefactPath(filePath) : true;
13
+ });
14
+ return source.length > 0 ? source : ids;
15
+ }
16
+ export function buildSymbolRefIndex(symbols, packageByFilePath) {
17
+ const byId = new Map(symbols.map((symbol) => [symbol.id, symbol]));
18
+ const byQualifiedName = new Map(symbols.map((symbol) => [symbol.qualifiedName, symbol.id]));
19
+ const byPathName = new Map();
20
+ const byName = new Map();
21
+ const byPackageName = new Map();
22
+ for (const symbol of symbols) {
23
+ const pathNameKey = `${symbol.filePath}:${symbol.name}`;
24
+ pushUnique(byPathName, pathNameKey, symbol.id);
25
+ pushUnique(byName, symbol.name, symbol.id);
26
+ const packageRecord = packageByFilePath?.get(symbol.filePath);
27
+ const packageName = packageRecord?.name?.trim();
28
+ if (packageName) {
29
+ pushUnique(byPackageName, `${packageName}:${symbol.name}`, symbol.id);
30
+ pushUnique(byPackageName, `${packageName}/${symbol.name}`, symbol.id);
31
+ }
32
+ }
33
+ return { byId, byQualifiedName, byPathName, byName, byPackageName };
34
+ }
35
+ export function buildPublicApiRefIndex(publicApis, symbols, packageByFilePath) {
36
+ const symbolById = new Map(symbols.map((symbol) => [symbol.id, symbol]));
37
+ const byId = new Map(publicApis.map((api) => [api.id, api]));
38
+ const byPathName = new Map();
39
+ const byName = new Map();
40
+ const byPackageName = new Map();
41
+ for (const api of publicApis) {
42
+ const symbol = symbolById.get(api.symbolId);
43
+ pushUnique(byName, api.name.toLowerCase(), api.id);
44
+ if (symbol) {
45
+ pushUnique(byPathName, `${symbol.filePath}:${symbol.name}`, api.id);
46
+ const packageRecord = packageByFilePath?.get(symbol.filePath);
47
+ const packageName = packageRecord?.name?.trim();
48
+ if (packageName) {
49
+ pushUnique(byPackageName, `${packageName}:${api.name}`.toLowerCase(), api.id);
50
+ pushUnique(byPackageName, `${packageName}/${api.name}`.toLowerCase(), api.id);
51
+ }
52
+ }
53
+ }
54
+ return { byId, byPathName, byName, byPackageName, symbolById };
55
+ }
56
+ export function resolveSymbolReference(reference, index) {
57
+ const ref = reference.trim();
58
+ if (!ref) {
59
+ return { status: "unknown", value: reference };
60
+ }
61
+ if (index.byId.has(ref)) {
62
+ return { status: "resolved", value: ref };
63
+ }
64
+ const byQualifiedName = index.byQualifiedName.get(ref);
65
+ if (byQualifiedName) {
66
+ return { status: "resolved", value: byQualifiedName };
67
+ }
68
+ const pathNameCandidates = [
69
+ ref,
70
+ ref.includes("::") ? ref.replace("::", ":") : undefined,
71
+ ref.startsWith("symbol:") ? stripSymbolLineSuffix(ref) : undefined
72
+ ].filter((value) => Boolean(value));
73
+ for (const candidate of pathNameCandidates) {
74
+ const pathName = normalizePathNameKey(candidate);
75
+ if (!pathName) {
76
+ continue;
77
+ }
78
+ const matches = preferSourceIds(index.byPathName.get(pathName) ?? [], (id) => index.byId.get(id)?.filePath);
79
+ if (matches.length === 1) {
80
+ return { status: "resolved", value: matches[0] };
81
+ }
82
+ if (matches.length > 1) {
83
+ return { status: "ambiguous", value: ref };
84
+ }
85
+ }
86
+ const lineTolerant = parseSymbolPathName(ref);
87
+ if (lineTolerant) {
88
+ const matches = preferSourceIds(index.byPathName.get(`${lineTolerant.filePath}:${lineTolerant.name}`) ?? [], (id) => index.byId.get(id)?.filePath);
89
+ if (matches.length === 1) {
90
+ return { status: "resolved", value: matches[0] };
91
+ }
92
+ if (matches.length > 1) {
93
+ return { status: "ambiguous", value: ref };
94
+ }
95
+ const byParsedName = preferSourceIds(index.byName.get(lineTolerant.name) ?? [], (id) => index.byId.get(id)?.filePath);
96
+ if (byParsedName.length === 1) {
97
+ return { status: "resolved", value: byParsedName[0] };
98
+ }
99
+ if (byParsedName.length > 1) {
100
+ return { status: "ambiguous", value: ref };
101
+ }
102
+ }
103
+ const packageKey = normalizePackageQualifiedKey(ref);
104
+ if (packageKey) {
105
+ const matches = preferSourceIds(index.byPackageName.get(packageKey) ?? [], (id) => index.byId.get(id)?.filePath);
106
+ if (matches.length === 1) {
107
+ return { status: "resolved", value: matches[0] };
108
+ }
109
+ if (matches.length > 1) {
110
+ return { status: "ambiguous", value: ref };
111
+ }
112
+ }
113
+ const bareName = stripPublicApiPrefix(ref);
114
+ const byName = preferSourceIds(index.byName.get(bareName) ?? [], (id) => index.byId.get(id)?.filePath);
115
+ if (byName.length === 1) {
116
+ return { status: "resolved", value: byName[0] };
117
+ }
118
+ if (byName.length > 1) {
119
+ return { status: "ambiguous", value: ref };
120
+ }
121
+ return { status: "unknown", value: ref };
122
+ }
123
+ export function resolvePublicApiReference(reference, index) {
124
+ const ref = reference.trim();
125
+ if (!ref) {
126
+ return { status: "unknown", value: reference };
127
+ }
128
+ if (index.byId.has(ref)) {
129
+ return { status: "resolved", value: ref };
130
+ }
131
+ const lineTolerant = parsePublicApiPathName(ref);
132
+ if (lineTolerant) {
133
+ const matches = preferSourceIds(index.byPathName.get(`${lineTolerant.filePath}:${lineTolerant.name}`) ?? [], (id) => index.symbolById.get(index.byId.get(id)?.symbolId ?? "")?.filePath);
134
+ if (matches.length === 1) {
135
+ return { status: "resolved", value: matches[0] };
136
+ }
137
+ if (matches.length > 1) {
138
+ return { status: "ambiguous", value: ref };
139
+ }
140
+ // Stale path (e.g. generated dist id) — fall back to unique name from the parsed id.
141
+ const byParsedName = preferSourceIds(index.byName.get(lineTolerant.name.toLowerCase()) ?? [], (id) => index.symbolById.get(index.byId.get(id)?.symbolId ?? "")?.filePath);
142
+ if (byParsedName.length === 1) {
143
+ return { status: "resolved", value: byParsedName[0] };
144
+ }
145
+ if (byParsedName.length > 1) {
146
+ return { status: "ambiguous", value: ref };
147
+ }
148
+ }
149
+ const pathName = normalizePathNameKey(stripPublicApiPrefix(ref));
150
+ if (pathName && pathName.includes("/")) {
151
+ const matches = preferSourceIds(index.byPathName.get(pathName) ?? [], (id) => index.symbolById.get(index.byId.get(id)?.symbolId ?? "")?.filePath);
152
+ if (matches.length === 1) {
153
+ return { status: "resolved", value: matches[0] };
154
+ }
155
+ if (matches.length > 1) {
156
+ return { status: "ambiguous", value: ref };
157
+ }
158
+ }
159
+ const packageKey = normalizePackageQualifiedKey(stripPublicApiPrefix(ref))?.toLowerCase();
160
+ if (packageKey) {
161
+ const matches = preferSourceIds(index.byPackageName.get(packageKey) ?? [], (id) => index.symbolById.get(index.byId.get(id)?.symbolId ?? "")?.filePath);
162
+ if (matches.length === 1) {
163
+ return { status: "resolved", value: matches[0] };
164
+ }
165
+ if (matches.length > 1) {
166
+ return { status: "ambiguous", value: ref };
167
+ }
168
+ }
169
+ const bareName = stripPublicApiPrefix(ref).toLowerCase();
170
+ const byName = preferSourceIds(index.byName.get(bareName) ?? [], (id) => index.symbolById.get(index.byId.get(id)?.symbolId ?? "")?.filePath);
171
+ if (byName.length === 1) {
172
+ return { status: "resolved", value: byName[0] };
173
+ }
174
+ if (byName.length > 1) {
175
+ return { status: "ambiguous", value: ref };
176
+ }
177
+ return { status: "unknown", value: ref };
178
+ }
179
+ function pushUnique(map, key, value) {
180
+ const current = map.get(key) ?? [];
181
+ if (!current.includes(value)) {
182
+ current.push(value);
183
+ current.sort();
184
+ map.set(key, current);
185
+ }
186
+ }
187
+ function stripPublicApiPrefix(ref) {
188
+ return ref.replace(/^public-api:/i, "").trim();
189
+ }
190
+ function stripSymbolLineSuffix(ref) {
191
+ const parsed = parseSymbolPathName(ref);
192
+ if (!parsed) {
193
+ return undefined;
194
+ }
195
+ return `${parsed.filePath}:${parsed.name}`;
196
+ }
197
+ /**
198
+ * Parse `symbol:path/to/file.ts:Name:12` or `symbol:path/to/file.ts:Name`.
199
+ * Relative catalogue paths are POSIX-style and do not contain colons.
200
+ */
201
+ export function parseSymbolPathName(ref) {
202
+ const trimmed = ref.trim();
203
+ if (!trimmed.startsWith("symbol:")) {
204
+ return undefined;
205
+ }
206
+ const body = trimmed.slice("symbol:".length);
207
+ const parts = body.split(":");
208
+ if (parts.length < 2) {
209
+ return undefined;
210
+ }
211
+ let name;
212
+ let pathParts;
213
+ if (parts.length >= 3 && /^\d+$/.test(parts[parts.length - 1] ?? "")) {
214
+ name = parts[parts.length - 2] ?? "";
215
+ pathParts = parts.slice(0, -2);
216
+ }
217
+ else {
218
+ name = parts[parts.length - 1] ?? "";
219
+ pathParts = parts.slice(0, -1);
220
+ }
221
+ const filePath = pathParts.join(":");
222
+ if (!filePath || !name || !filePath.includes("/")) {
223
+ return undefined;
224
+ }
225
+ return { filePath, name };
226
+ }
227
+ export function parsePublicApiPathName(ref) {
228
+ const trimmed = stripPublicApiPrefix(ref.trim());
229
+ if (!trimmed.startsWith("symbol:")) {
230
+ return undefined;
231
+ }
232
+ return parseSymbolPathName(trimmed);
233
+ }
234
+ function normalizePathNameKey(ref) {
235
+ let value = stripPublicApiPrefix(ref.trim());
236
+ if (value.startsWith("symbol:")) {
237
+ const parsed = parseSymbolPathName(value);
238
+ return parsed ? `${parsed.filePath}:${parsed.name}` : undefined;
239
+ }
240
+ value = value.replace("::", ":");
241
+ if (!value.includes(":")) {
242
+ return undefined;
243
+ }
244
+ const lastColon = value.lastIndexOf(":");
245
+ const filePath = value.slice(0, lastColon).trim();
246
+ const name = value.slice(lastColon + 1).trim();
247
+ if (!filePath || !name) {
248
+ return undefined;
249
+ }
250
+ // Drop trailing :line if caller passed path:name:line without symbol: prefix.
251
+ if (/^\d+$/.test(name)) {
252
+ const previous = filePath.lastIndexOf(":");
253
+ if (previous > 0) {
254
+ return `${filePath.slice(0, previous)}:${filePath.slice(previous + 1)}`;
255
+ }
256
+ }
257
+ return `${filePath}:${name}`;
258
+ }
259
+ /**
260
+ * Accept `pkg:Name`, `@scope/pkg:Name`, or `pkg/Name` when the left side looks
261
+ * like a package name (not a source file path).
262
+ */
263
+ function normalizePackageQualifiedKey(ref) {
264
+ const value = stripPublicApiPrefix(ref.trim());
265
+ if (!value || value.startsWith("symbol:")) {
266
+ return undefined;
267
+ }
268
+ const lastColon = value.lastIndexOf(":");
269
+ if (lastColon > 0) {
270
+ const pkg = value.slice(0, lastColon).trim();
271
+ const name = value.slice(lastColon + 1).trim();
272
+ if (pkg && name && !name.includes("/") && !/^\d+$/.test(name) && !looksLikeSourceFilePath(pkg)) {
273
+ return `${pkg}:${name}`;
274
+ }
275
+ }
276
+ const slash = value.match(/^(@?[\w.-]+(?:\/[\w.-]+)?)\/([\w.-]+)$/);
277
+ if (slash && !looksLikeSourceFilePath(slash[1])) {
278
+ return `${slash[1]}:${slash[2]}`;
279
+ }
280
+ return undefined;
281
+ }
282
+ function looksLikeSourceFilePath(value) {
283
+ return /\.(ts|tsx|js|jsx|mjs|cjs|py|d\.ts)$/i.test(value) || value.includes("\\");
284
+ }
@@ -0,0 +1,8 @@
1
+ import type { CatalogueConfig, DiagnosticRecord, FileRecord } from "./schema.js";
2
+ export interface ScanResult {
3
+ files: FileRecord[];
4
+ diagnostics: DiagnosticRecord[];
5
+ }
6
+ export declare function isExamplesTreePath(filePath: string): boolean;
7
+ export declare function detectLanguage(filePath: string): string;
8
+ export declare function scanWorkspace(workspaceRoot: string, config: CatalogueConfig): ScanResult;
@@ -0,0 +1,99 @@
1
+ import { readFileSync, statSync } from "node:fs";
2
+ import path from "node:path";
3
+ import fg from "fast-glob";
4
+ import { hashContent } from "./deterministic.js";
5
+ import { createIgnoreMatcher, isIncludedByGlob, normalize } from "./ignore.js";
6
+ import { isSecretLikeFile } from "../security/secretPatterns.js";
7
+ export function isExamplesTreePath(filePath) {
8
+ return /(^|\/)examples\//.test(filePath.replaceAll("\\", "/"));
9
+ }
10
+ export function detectLanguage(filePath) {
11
+ const ext = path.extname(filePath).toLowerCase();
12
+ if (ext === ".ts" || ext === ".tsx")
13
+ return "typescript";
14
+ if (ext === ".js" || ext === ".jsx" || ext === ".mjs" || ext === ".cjs")
15
+ return "javascript";
16
+ if (ext === ".py")
17
+ return "python";
18
+ if (ext === ".md")
19
+ return "markdown";
20
+ if (ext === ".json")
21
+ return "json";
22
+ if ([".yml", ".yaml", ".toml", ".ini", ".conf"].includes(ext))
23
+ return "config";
24
+ return "unknown";
25
+ }
26
+ export function scanWorkspace(workspaceRoot, config) {
27
+ const diagnostics = [];
28
+ const ignoreMatcher = createIgnoreMatcher(workspaceRoot, config);
29
+ const entries = fg.sync(["**/*"], {
30
+ cwd: workspaceRoot,
31
+ dot: true,
32
+ onlyFiles: true,
33
+ absolute: false,
34
+ followSymbolicLinks: false
35
+ }).sort();
36
+ const selected = entries
37
+ .map((entry) => normalize(entry))
38
+ .filter((entry) => !ignoreMatcher(entry))
39
+ .filter((entry) => isIncludedByGlob(entry, config.include));
40
+ const limited = selected.slice(0, config.limits.maxIndexedFiles);
41
+ const files = [];
42
+ for (const relativePath of limited) {
43
+ const absolute = path.resolve(workspaceRoot, relativePath);
44
+ const st = statSync(absolute);
45
+ if (st.size > config.limits.maxFileSizeBytes) {
46
+ diagnostics.push({
47
+ id: `diag:${relativePath}:file_too_large`,
48
+ severity: "warning",
49
+ category: "indexing",
50
+ code: "file_too_large",
51
+ message: `Skipped file larger than ${config.limits.maxFileSizeBytes} bytes.`,
52
+ filePath: relativePath,
53
+ provenance: { source: "scanner" }
54
+ });
55
+ continue;
56
+ }
57
+ if (isSecretLikeFile(path.basename(relativePath))) {
58
+ diagnostics.push({
59
+ id: `diag:${relativePath}:ignored_secret_like_file`,
60
+ severity: "info",
61
+ category: "security",
62
+ code: "ignored_secret_like_file",
63
+ message: "Skipped secret-like file.",
64
+ filePath: relativePath,
65
+ provenance: { source: "scanner" }
66
+ });
67
+ continue;
68
+ }
69
+ const content = readFileSync(absolute, "utf8");
70
+ const language = detectLanguage(relativePath);
71
+ const isTest = /(^|\/)tests?\//.test(relativePath) || /\.(test|spec)\./.test(relativePath);
72
+ const isExample = isExamplesTreePath(relativePath);
73
+ const isDocumentation = language === "markdown" || /CATALOGUE\.md$/i.test(relativePath);
74
+ const isConfig = language === "config" || /(^|\/)mapwright\.config\.json$/.test(relativePath);
75
+ const kind = isTest ? "test" : isDocumentation ? "documentation" : isConfig ? "config" : "source";
76
+ files.push({
77
+ path: relativePath,
78
+ language,
79
+ kind,
80
+ sizeBytes: Buffer.byteLength(content, "utf8"),
81
+ lineCount: content.length === 0 ? 0 : content.split(/\r?\n/).length,
82
+ hash: hashContent(content),
83
+ isGenerated: /generated|\.gen\./i.test(relativePath),
84
+ isTest,
85
+ isExample,
86
+ isConfig,
87
+ isDocumentation,
88
+ symbols: [],
89
+ capabilities: [],
90
+ publicApis: [],
91
+ provenance: {
92
+ source: "scanner",
93
+ extractors: ["workspace-scanner"]
94
+ }
95
+ });
96
+ }
97
+ files.sort((a, b) => a.path.localeCompare(b.path));
98
+ return { files, diagnostics };
99
+ }
@@ -0,0 +1,289 @@
1
+ export type CatalogueMode = "committed" | "local" | "memory";
2
+ export type SymbolKind = "function" | "class" | "method" | "interface" | "type" | "enum" | "constant" | "variable" | "module" | "namespace" | "component" | "route" | "test" | "unknown";
3
+ export type Visibility = "published" | "supported_internal" | "private" | "test_only" | "experimental" | "deprecated" | "ignored" | "unknown";
4
+ export type Stability = "stable" | "experimental" | "deprecated" | "internal" | "test-only" | "unknown";
5
+ export type Editability = "none" | "allowed" | "restricted" | "maintainer-only" | "unknown";
6
+ export interface CatalogueDocTag {
7
+ tag: string;
8
+ value: string;
9
+ }
10
+ export interface DocumentationSourceRecord {
11
+ kind: "jsdoc" | "docstring" | "markdown" | "comment" | "unknown";
12
+ filePath: string;
13
+ symbolId?: string;
14
+ snippet?: string;
15
+ }
16
+ export interface ProvenanceRecord {
17
+ source: string;
18
+ extractors?: string[];
19
+ locations?: string[];
20
+ confidence?: number;
21
+ warnings?: string[];
22
+ }
23
+ export interface EditPolicyRecord {
24
+ id: string;
25
+ target: {
26
+ kind: "package" | "file" | "capability" | "symbol";
27
+ id: string;
28
+ };
29
+ editable: boolean;
30
+ status: Editability;
31
+ conditions: string[];
32
+ provenance: ProvenanceRecord;
33
+ }
34
+ export interface FileRecord {
35
+ path: string;
36
+ language: string;
37
+ kind: "source" | "test" | "documentation" | "config" | "generated" | "unknown";
38
+ sizeBytes: number;
39
+ lineCount: number;
40
+ hash: string;
41
+ isGenerated: boolean;
42
+ isTest: boolean;
43
+ isExample: boolean;
44
+ isConfig: boolean;
45
+ isDocumentation: boolean;
46
+ packageName?: string;
47
+ symbols: string[];
48
+ capabilities: string[];
49
+ publicApis: string[];
50
+ provenance: ProvenanceRecord;
51
+ }
52
+ export type PackageEcosystem = "npm" | "python" | "unknown";
53
+ export interface PackageRecord {
54
+ id: string;
55
+ name: string;
56
+ path: string;
57
+ /** Package ecosystem for identity and ordering. */
58
+ ecosystem?: PackageEcosystem;
59
+ /** Python import package roots (dotted names); not a separate package ID. */
60
+ importPackages?: string[];
61
+ /** Declared console scripts / entry points (Python project.scripts, etc.). */
62
+ consoleScripts?: Array<{
63
+ name: string;
64
+ target: string;
65
+ }>;
66
+ summary?: string;
67
+ capabilities: string[];
68
+ publicApis: string[];
69
+ tests: string[];
70
+ stability: Stability;
71
+ entrypoints?: string[];
72
+ modules?: string[];
73
+ internalPaths?: string[];
74
+ publicPaths?: string[];
75
+ dependencyPolicy?: {
76
+ allowedInternalImports?: string[];
77
+ forbiddenImports?: string[];
78
+ };
79
+ provenance: ProvenanceRecord;
80
+ }
81
+ export interface CapabilityRecord {
82
+ id: string;
83
+ name: string;
84
+ summary?: string;
85
+ description?: string;
86
+ keywords: string[];
87
+ status: "available" | "experimental" | "deprecated" | "internal" | "unknown";
88
+ stability: Stability;
89
+ intendedUse?: string;
90
+ publicApis: string[];
91
+ symbols: string[];
92
+ files: string[];
93
+ packages: string[];
94
+ examples: string[];
95
+ tests: string[];
96
+ contracts: string[];
97
+ limitations: string[];
98
+ editable?: {
99
+ status: Editability;
100
+ policyId?: string;
101
+ };
102
+ provenance: ProvenanceRecord;
103
+ }
104
+ export type PublicExportKind = "named" | "default" | "namespace" | "type_only_export";
105
+ export interface PublicApiRecord {
106
+ id: string;
107
+ symbolId: string;
108
+ name: string;
109
+ importPath?: string;
110
+ /** package.json exports key when known (e.g. "./components"). */
111
+ exportKey?: string;
112
+ exportKind?: PublicExportKind;
113
+ packageId?: string;
114
+ signature?: string;
115
+ capabilityIds: string[];
116
+ stability: Stability;
117
+ intendedUse?: string;
118
+ inputs?: string;
119
+ returns?: string;
120
+ contracts: string[];
121
+ errors: string[];
122
+ examples: string[];
123
+ tests: string[];
124
+ provenance: ProvenanceRecord;
125
+ }
126
+ export interface ExampleRecord {
127
+ id: string;
128
+ title?: string;
129
+ filePath?: string;
130
+ anchor?: string;
131
+ capabilityIds: string[];
132
+ publicApiIds: string[];
133
+ symbolIds: string[];
134
+ summary?: string;
135
+ provenance: ProvenanceRecord;
136
+ }
137
+ export type SymbolProvenanceKind = "authored_source" | "generated_declaration" | "generated_only";
138
+ export interface SymbolRecord {
139
+ id: string;
140
+ name: string;
141
+ qualifiedName: string;
142
+ kind: SymbolKind;
143
+ language: string;
144
+ filePath: string;
145
+ packageId?: string;
146
+ /** Owning package-relative module path used in identity. */
147
+ modulePath?: string;
148
+ qualifiedContainerPath?: string;
149
+ range?: {
150
+ startLine: number;
151
+ startColumn?: number;
152
+ endLine?: number;
153
+ endColumn?: number;
154
+ };
155
+ signature?: string;
156
+ signatureFingerprint?: string;
157
+ implementationFingerprint?: string;
158
+ /** Self if canonical; otherwise points at preferred authored declaration. */
159
+ canonicalSymbolId?: string;
160
+ duplicateOf?: string | null;
161
+ symbolProvenance?: SymbolProvenanceKind;
162
+ visibility: Visibility;
163
+ exported: boolean;
164
+ doc?: {
165
+ present: boolean;
166
+ text?: string;
167
+ tags?: CatalogueDocTag[];
168
+ };
169
+ capabilities: string[];
170
+ publicApi?: string;
171
+ parents: string[];
172
+ children: string[];
173
+ confidence: number;
174
+ provenance: ProvenanceRecord;
175
+ }
176
+ export type RelationshipType = "defines" | "contains" | "documents" | "tests" | "covers" | "imports" | "exports" | "explicit_coverage" | "explicit_example" | "resolved_direct_import" | "resolved_direct_usage";
177
+ export interface RelationshipEvidence {
178
+ filePath?: string;
179
+ line?: number;
180
+ startLine?: number;
181
+ endLine?: number;
182
+ text?: string;
183
+ extractedFromId?: string;
184
+ extractedToId?: string;
185
+ }
186
+ export interface RelationshipRecord {
187
+ id: string;
188
+ type: RelationshipType;
189
+ from: {
190
+ kind: "file" | "symbol" | "capability" | "publicApi" | "test" | "package" | "externalPackage";
191
+ id: string;
192
+ };
193
+ to: {
194
+ kind: "file" | "symbol" | "capability" | "publicApi" | "test" | "package" | "externalPackage";
195
+ id: string;
196
+ };
197
+ /** Semantic discriminant (binding, alias, runtime|type-only, etc.). Never line/order. */
198
+ localDiscriminant?: string | null;
199
+ evidence?: RelationshipEvidence;
200
+ evidenceRanges?: RelationshipEvidence[];
201
+ confidence: number;
202
+ provenance: ProvenanceRecord;
203
+ }
204
+ export interface TestRecord {
205
+ id: string;
206
+ filePath: string;
207
+ name: string;
208
+ /** Classification metadata — not part of identity. */
209
+ testKind?: "unit" | "example_linked" | "unknown";
210
+ framework?: string;
211
+ suitePath?: string;
212
+ implementationFingerprint?: string;
213
+ symbolId?: string;
214
+ coversCapabilities: string[];
215
+ coversSymbols: string[];
216
+ coversPublicApis: string[];
217
+ contracts: string[];
218
+ provenance: ProvenanceRecord;
219
+ }
220
+ export interface DiagnosticRecord {
221
+ id: string;
222
+ severity: "info" | "warning" | "error";
223
+ category: "documentation" | "indexing" | "resolution" | "style" | "security" | "determinism";
224
+ code: string;
225
+ message: string;
226
+ filePath?: string;
227
+ range?: {
228
+ startLine: number;
229
+ endLine?: number;
230
+ };
231
+ target?: {
232
+ kind: "file" | "symbol" | "capability" | "publicApi" | "test" | "package";
233
+ id: string;
234
+ };
235
+ provenance: ProvenanceRecord;
236
+ }
237
+ export interface CatalogueSnapshot {
238
+ schemaVersion: 2;
239
+ /** Required identity scheme; v0.7 uses stable-sha256-v1. */
240
+ identityScheme: "stable-sha256-v1";
241
+ catalogueId: string;
242
+ generator: {
243
+ /** New catalogues emit "mapwright"; "code-atlas" is accepted when loading pre-release catalogues. */
244
+ name: "mapwright" | "code-atlas";
245
+ version: string;
246
+ };
247
+ mode: CatalogueMode;
248
+ stats: {
249
+ fileCount: number;
250
+ symbolCount: number;
251
+ capabilityCount: number;
252
+ publicApiCount: number;
253
+ exampleCount: number;
254
+ diagnosticCount: number;
255
+ languages: Record<string, number>;
256
+ };
257
+ files: FileRecord[];
258
+ packages: PackageRecord[];
259
+ capabilities: CapabilityRecord[];
260
+ publicApis: PublicApiRecord[];
261
+ examples: ExampleRecord[];
262
+ symbols: SymbolRecord[];
263
+ relationships: RelationshipRecord[];
264
+ tests: TestRecord[];
265
+ diagnostics: DiagnosticRecord[];
266
+ provenance: ProvenanceRecord & {
267
+ documentationSources?: DocumentationSourceRecord[];
268
+ };
269
+ }
270
+ export interface CatalogueConfig {
271
+ schemaVersion: 2;
272
+ catalogueDir: string;
273
+ committedCatalogue: boolean;
274
+ languages: string[];
275
+ include: string[];
276
+ exclude: string[];
277
+ documentation: {
278
+ requirePublicApiDocs: boolean;
279
+ requireCapabilityForPublicApi: boolean;
280
+ };
281
+ validation: {
282
+ failOn: "info" | "warning" | "error" | "never";
283
+ };
284
+ limits: {
285
+ maxFileSizeBytes: number;
286
+ maxIndexedFiles: number;
287
+ maxSourcePreviewLines: number;
288
+ };
289
+ }
@@ -0,0 +1 @@
1
+ export {};