@foxlight/core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -0
- package/dist/index.d.ts +669 -0
- package/dist/index.js +1484 -0
- package/package.json +39 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,669 @@
|
|
|
1
|
+
/** Unique identifier for a component within a project. */
|
|
2
|
+
type ComponentId = string;
|
|
3
|
+
/** The front-end framework a component belongs to. */
|
|
4
|
+
type Framework = 'react' | 'vue' | 'svelte' | 'angular' | 'web-component' | 'unknown';
|
|
5
|
+
/** How a component is exported from its module. */
|
|
6
|
+
type ExportKind = 'named' | 'default' | 're-export';
|
|
7
|
+
/**
|
|
8
|
+
* Core representation of a UI component discovered in the codebase.
|
|
9
|
+
* This is the central entity that all Foxlight tools operate on.
|
|
10
|
+
*/
|
|
11
|
+
interface ComponentInfo {
|
|
12
|
+
/** Stable identifier (typically: filePath#componentName) */
|
|
13
|
+
id: ComponentId;
|
|
14
|
+
/** Human-readable component name (e.g. "DataTable") */
|
|
15
|
+
name: string;
|
|
16
|
+
/** Absolute file path where the component is defined */
|
|
17
|
+
filePath: string;
|
|
18
|
+
/** Line number where the component definition starts */
|
|
19
|
+
line: number;
|
|
20
|
+
/** Detected framework */
|
|
21
|
+
framework: Framework;
|
|
22
|
+
/** How the component is exported */
|
|
23
|
+
exportKind: ExportKind;
|
|
24
|
+
/** Props / inputs the component accepts */
|
|
25
|
+
props: PropInfo[];
|
|
26
|
+
/** Components this component renders (children in the tree) */
|
|
27
|
+
children: ComponentId[];
|
|
28
|
+
/** Components that render this component (parents in the tree) */
|
|
29
|
+
usedBy: ComponentId[];
|
|
30
|
+
/** npm packages this component directly imports */
|
|
31
|
+
dependencies: string[];
|
|
32
|
+
/** Custom metadata that plugins can attach */
|
|
33
|
+
metadata: Record<string, unknown>;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* A single prop / input on a component.
|
|
37
|
+
*/
|
|
38
|
+
interface PropInfo {
|
|
39
|
+
/** Prop name */
|
|
40
|
+
name: string;
|
|
41
|
+
/** TypeScript type as a string (e.g. "string", "() => void") */
|
|
42
|
+
type: string;
|
|
43
|
+
/** Whether the prop is required */
|
|
44
|
+
required: boolean;
|
|
45
|
+
/** Default value expression, if any */
|
|
46
|
+
defaultValue?: string;
|
|
47
|
+
/** JSDoc / comment description */
|
|
48
|
+
description?: string;
|
|
49
|
+
}
|
|
50
|
+
/** An edge in the import graph between two modules. */
|
|
51
|
+
interface ImportEdge {
|
|
52
|
+
/** Absolute path of the importing module */
|
|
53
|
+
source: string;
|
|
54
|
+
/** Absolute path or package name of the imported module */
|
|
55
|
+
target: string;
|
|
56
|
+
/** The specific symbols imported */
|
|
57
|
+
specifiers: ImportSpecifier[];
|
|
58
|
+
/** Whether this is a type-only import */
|
|
59
|
+
typeOnly: boolean;
|
|
60
|
+
}
|
|
61
|
+
interface ImportSpecifier {
|
|
62
|
+
/** Imported name (or "default" / "*") */
|
|
63
|
+
imported: string;
|
|
64
|
+
/** Local alias, if renamed */
|
|
65
|
+
local: string;
|
|
66
|
+
}
|
|
67
|
+
/** Size information for a single module or component. */
|
|
68
|
+
interface SizeInfo {
|
|
69
|
+
/** Raw (uncompressed) size in bytes */
|
|
70
|
+
raw: number;
|
|
71
|
+
/** Gzipped size in bytes */
|
|
72
|
+
gzip: number;
|
|
73
|
+
/** Brotli-compressed size in bytes */
|
|
74
|
+
brotli?: number;
|
|
75
|
+
}
|
|
76
|
+
/** Bundle contribution for a single component. */
|
|
77
|
+
interface ComponentBundleInfo {
|
|
78
|
+
componentId: ComponentId;
|
|
79
|
+
/** Size of the component's own code */
|
|
80
|
+
selfSize: SizeInfo;
|
|
81
|
+
/** Size including all unique dependencies (not shared with other components) */
|
|
82
|
+
exclusiveSize: SizeInfo;
|
|
83
|
+
/** Total size including all dependencies (including shared) */
|
|
84
|
+
totalSize: SizeInfo;
|
|
85
|
+
/** The chunk(s) this component ends up in */
|
|
86
|
+
chunks: string[];
|
|
87
|
+
}
|
|
88
|
+
/** Aggregated health score for a component. */
|
|
89
|
+
interface ComponentHealth {
|
|
90
|
+
componentId: ComponentId;
|
|
91
|
+
/** Overall health score 0-100 */
|
|
92
|
+
score: number;
|
|
93
|
+
/** Individual metric scores */
|
|
94
|
+
metrics: HealthMetrics;
|
|
95
|
+
/** When these metrics were last computed */
|
|
96
|
+
computedAt: string;
|
|
97
|
+
}
|
|
98
|
+
interface HealthMetrics {
|
|
99
|
+
/** Bundle size score (smaller = better) */
|
|
100
|
+
bundleSize: MetricScore;
|
|
101
|
+
/** Test coverage score */
|
|
102
|
+
testCoverage: MetricScore;
|
|
103
|
+
/** Accessibility score (from axe-core scans) */
|
|
104
|
+
accessibility: MetricScore;
|
|
105
|
+
/** Code staleness (recently modified = better) */
|
|
106
|
+
freshness: MetricScore;
|
|
107
|
+
/** Runtime performance (render time) */
|
|
108
|
+
performance: MetricScore;
|
|
109
|
+
/** Error rate in production */
|
|
110
|
+
reliability: MetricScore;
|
|
111
|
+
}
|
|
112
|
+
interface MetricScore {
|
|
113
|
+
/** Score 0-100 */
|
|
114
|
+
score: number;
|
|
115
|
+
/** Raw value (e.g. "45KB", "87%", "120ms") */
|
|
116
|
+
value: string;
|
|
117
|
+
/** Human-readable label */
|
|
118
|
+
label: string;
|
|
119
|
+
/** Severity level */
|
|
120
|
+
level: 'good' | 'warning' | 'critical';
|
|
121
|
+
}
|
|
122
|
+
/** Cost model for a hosting provider. */
|
|
123
|
+
interface CostModel {
|
|
124
|
+
provider: 'vercel' | 'netlify' | 'aws' | 'cloudflare' | 'custom';
|
|
125
|
+
/** Cost per 1M function invocations (USD) */
|
|
126
|
+
invocationCostPer1M: number;
|
|
127
|
+
/** Cost per GB bandwidth (USD) */
|
|
128
|
+
bandwidthCostPerGB: number;
|
|
129
|
+
/** Cost per GB storage (USD/month) */
|
|
130
|
+
storageCostPerGB: number;
|
|
131
|
+
/** Cost per 1M edge function invocations */
|
|
132
|
+
edgeCostPer1M?: number;
|
|
133
|
+
/** Base monthly cost (USD) */
|
|
134
|
+
baseCost: number;
|
|
135
|
+
}
|
|
136
|
+
/** Cost impact estimate for a code change. */
|
|
137
|
+
interface CostImpact {
|
|
138
|
+
/** Estimated monthly cost delta (USD) */
|
|
139
|
+
monthlyDelta: number;
|
|
140
|
+
/** Breakdown by cost category */
|
|
141
|
+
breakdown: CostBreakdownItem[];
|
|
142
|
+
/** Current estimated monthly cost */
|
|
143
|
+
currentMonthlyCost: number;
|
|
144
|
+
/** Projected monthly cost after change */
|
|
145
|
+
projectedMonthlyCost: number;
|
|
146
|
+
}
|
|
147
|
+
interface CostBreakdownItem {
|
|
148
|
+
category: 'invocations' | 'bandwidth' | 'storage' | 'edge' | 'base';
|
|
149
|
+
description: string;
|
|
150
|
+
currentCost: number;
|
|
151
|
+
projectedCost: number;
|
|
152
|
+
delta: number;
|
|
153
|
+
}
|
|
154
|
+
/** Result of analyzing a dependency upgrade. */
|
|
155
|
+
interface UpgradePreview {
|
|
156
|
+
/** Package being upgraded */
|
|
157
|
+
packageName: string;
|
|
158
|
+
/** Current version */
|
|
159
|
+
fromVersion: string;
|
|
160
|
+
/** Target version */
|
|
161
|
+
toVersion: string;
|
|
162
|
+
/** Risk assessment */
|
|
163
|
+
risk: 'low' | 'medium' | 'high';
|
|
164
|
+
/** Individual checks that were run */
|
|
165
|
+
checks: UpgradeCheck[];
|
|
166
|
+
}
|
|
167
|
+
interface UpgradeCheck {
|
|
168
|
+
name: string;
|
|
169
|
+
status: 'pass' | 'warn' | 'fail';
|
|
170
|
+
summary: string;
|
|
171
|
+
details?: string;
|
|
172
|
+
}
|
|
173
|
+
/** User-facing configuration (foxlight.config.ts). */
|
|
174
|
+
interface FoxlightConfig {
|
|
175
|
+
/** Root directory of the project */
|
|
176
|
+
rootDir: string;
|
|
177
|
+
/** Glob patterns for source files to analyze */
|
|
178
|
+
include: string[];
|
|
179
|
+
/** Glob patterns to exclude */
|
|
180
|
+
exclude: string[];
|
|
181
|
+
/** Framework hint (auto-detected if omitted) */
|
|
182
|
+
framework?: Framework;
|
|
183
|
+
/** Storybook config path, if applicable */
|
|
184
|
+
storybook?: string;
|
|
185
|
+
/** Cost model configuration */
|
|
186
|
+
costModel?: CostModel;
|
|
187
|
+
/** Baseline storage config for visual regression */
|
|
188
|
+
baselines?: BaselineConfig;
|
|
189
|
+
/** Plugin configurations */
|
|
190
|
+
plugins?: PluginConfig[];
|
|
191
|
+
}
|
|
192
|
+
interface BaselineConfig {
|
|
193
|
+
/** Storage provider */
|
|
194
|
+
provider: 's3' | 'gcs' | 'r2' | 'local' | 'git-lfs';
|
|
195
|
+
/** Bucket or directory name */
|
|
196
|
+
bucket: string;
|
|
197
|
+
/** Optional prefix / path within the bucket */
|
|
198
|
+
prefix?: string;
|
|
199
|
+
/** Region (for cloud storage) */
|
|
200
|
+
region?: string;
|
|
201
|
+
}
|
|
202
|
+
interface PluginConfig {
|
|
203
|
+
name: string;
|
|
204
|
+
options?: Record<string, unknown>;
|
|
205
|
+
}
|
|
206
|
+
/** A point-in-time snapshot of the entire project analysis. */
|
|
207
|
+
interface ProjectSnapshot {
|
|
208
|
+
/** Unique snapshot ID */
|
|
209
|
+
id: string;
|
|
210
|
+
/** Git commit SHA */
|
|
211
|
+
commitSha: string;
|
|
212
|
+
/** Git branch */
|
|
213
|
+
branch: string;
|
|
214
|
+
/** When the snapshot was created */
|
|
215
|
+
createdAt: string;
|
|
216
|
+
/** All discovered components */
|
|
217
|
+
components: ComponentInfo[];
|
|
218
|
+
/** Import graph edges */
|
|
219
|
+
imports: ImportEdge[];
|
|
220
|
+
/** Bundle info per component */
|
|
221
|
+
bundleInfo: ComponentBundleInfo[];
|
|
222
|
+
/** Health scores */
|
|
223
|
+
health: ComponentHealth[];
|
|
224
|
+
}
|
|
225
|
+
/** Diff between two snapshots. */
|
|
226
|
+
interface SnapshotDiff {
|
|
227
|
+
base: {
|
|
228
|
+
id: string;
|
|
229
|
+
commitSha: string;
|
|
230
|
+
};
|
|
231
|
+
head: {
|
|
232
|
+
id: string;
|
|
233
|
+
commitSha: string;
|
|
234
|
+
};
|
|
235
|
+
components: {
|
|
236
|
+
added: ComponentInfo[];
|
|
237
|
+
removed: ComponentInfo[];
|
|
238
|
+
modified: ComponentModification[];
|
|
239
|
+
};
|
|
240
|
+
bundleDiff: BundleDiffEntry[];
|
|
241
|
+
healthDiff: HealthDiffEntry[];
|
|
242
|
+
}
|
|
243
|
+
interface ComponentModification {
|
|
244
|
+
componentId: ComponentId;
|
|
245
|
+
changes: string[];
|
|
246
|
+
propsAdded: string[];
|
|
247
|
+
propsRemoved: string[];
|
|
248
|
+
propsModified: string[];
|
|
249
|
+
}
|
|
250
|
+
interface BundleDiffEntry {
|
|
251
|
+
componentId: ComponentId;
|
|
252
|
+
before: SizeInfo;
|
|
253
|
+
after: SizeInfo;
|
|
254
|
+
delta: SizeInfo;
|
|
255
|
+
}
|
|
256
|
+
interface HealthDiffEntry {
|
|
257
|
+
componentId: ComponentId;
|
|
258
|
+
beforeScore: number;
|
|
259
|
+
afterScore: number;
|
|
260
|
+
delta: number;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* The ComponentRegistry is the shared data layer for Foxlight.
|
|
265
|
+
* It holds all discovered components, their relationships,
|
|
266
|
+
* bundle data, and health scores.
|
|
267
|
+
*/
|
|
268
|
+
declare class ComponentRegistry {
|
|
269
|
+
private components;
|
|
270
|
+
private imports;
|
|
271
|
+
private bundleInfo;
|
|
272
|
+
private health;
|
|
273
|
+
/** Register a discovered component. */
|
|
274
|
+
addComponent(component: ComponentInfo): void;
|
|
275
|
+
/** Register multiple components at once. */
|
|
276
|
+
addComponents(components: ComponentInfo[]): void;
|
|
277
|
+
/** Get a component by ID. */
|
|
278
|
+
getComponent(id: ComponentId): ComponentInfo | undefined;
|
|
279
|
+
/** Get all registered components. */
|
|
280
|
+
getAllComponents(): ComponentInfo[];
|
|
281
|
+
/** Check if a component exists in the registry. */
|
|
282
|
+
hasComponent(id: ComponentId): boolean;
|
|
283
|
+
/** Remove a component from the registry. */
|
|
284
|
+
removeComponent(id: ComponentId): boolean;
|
|
285
|
+
/** Total number of registered components. */
|
|
286
|
+
get size(): number;
|
|
287
|
+
/** Add an import edge to the graph. */
|
|
288
|
+
addImport(edge: ImportEdge): void;
|
|
289
|
+
/** Add multiple import edges. */
|
|
290
|
+
addImports(edges: ImportEdge[]): void;
|
|
291
|
+
/** Get all imports originating from a file. */
|
|
292
|
+
getImportsFrom(filePath: string): ImportEdge[];
|
|
293
|
+
/** Get all imports targeting a file or package. */
|
|
294
|
+
getImportsTo(target: string): ImportEdge[];
|
|
295
|
+
/** Get the full import graph. */
|
|
296
|
+
getAllImports(): ImportEdge[];
|
|
297
|
+
/** Set bundle size info for a component. */
|
|
298
|
+
setBundleInfo(info: ComponentBundleInfo): void;
|
|
299
|
+
/** Get bundle info for a component. */
|
|
300
|
+
getBundleInfo(id: ComponentId): ComponentBundleInfo | undefined;
|
|
301
|
+
/** Get all bundle info entries. */
|
|
302
|
+
getAllBundleInfo(): ComponentBundleInfo[];
|
|
303
|
+
/** Set health score for a component. */
|
|
304
|
+
setHealth(h: ComponentHealth): void;
|
|
305
|
+
/** Get health score for a component. */
|
|
306
|
+
getHealth(id: ComponentId): ComponentHealth | undefined;
|
|
307
|
+
/** Get all health scores. */
|
|
308
|
+
getAllHealth(): ComponentHealth[];
|
|
309
|
+
/** Find components that use the given component (parents). */
|
|
310
|
+
getConsumers(id: ComponentId): ComponentInfo[];
|
|
311
|
+
/** Find components that the given component renders (children). */
|
|
312
|
+
getDependents(id: ComponentId): ComponentInfo[];
|
|
313
|
+
/** Get components that have no parents (top-level / page components). */
|
|
314
|
+
getRootComponents(): ComponentInfo[];
|
|
315
|
+
/** Get components that have no children (leaf / primitive components). */
|
|
316
|
+
getLeafComponents(): ComponentInfo[];
|
|
317
|
+
/**
|
|
318
|
+
* Find all components reachable from the given component (full subtree).
|
|
319
|
+
* Uses BFS to avoid stack overflow on deep trees.
|
|
320
|
+
*/
|
|
321
|
+
getSubtree(id: ComponentId): ComponentInfo[];
|
|
322
|
+
/** Create a point-in-time snapshot of the registry. */
|
|
323
|
+
createSnapshot(commitSha: string, branch: string): ProjectSnapshot;
|
|
324
|
+
/** Load a snapshot into the registry, replacing current state. */
|
|
325
|
+
loadSnapshot(snapshot: ProjectSnapshot): void;
|
|
326
|
+
/** Compute the diff between two snapshots. */
|
|
327
|
+
static diff(base: ProjectSnapshot, head: ProjectSnapshot): SnapshotDiff;
|
|
328
|
+
/** Clear all data from the registry. */
|
|
329
|
+
clear(): void;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* A directed graph built from import relationships.
|
|
334
|
+
* Supports cycle detection, topological sorting, and
|
|
335
|
+
* impact analysis (what's affected if a module changes).
|
|
336
|
+
*/
|
|
337
|
+
declare class DependencyGraph {
|
|
338
|
+
private nodes;
|
|
339
|
+
/** Build the graph from a list of import edges. */
|
|
340
|
+
static fromImports(edges: ImportEdge[]): DependencyGraph;
|
|
341
|
+
/** Add a directed edge from source to target. */
|
|
342
|
+
addEdge(source: string, target: string): void;
|
|
343
|
+
/** Get all direct dependencies of a module. */
|
|
344
|
+
getDependencies(id: string): string[];
|
|
345
|
+
/** Get all modules that directly depend on a module. */
|
|
346
|
+
getDependents(id: string): string[];
|
|
347
|
+
/**
|
|
348
|
+
* Get all modules transitively affected if the given module changes.
|
|
349
|
+
* Walks the "incoming" edges (dependents) recursively.
|
|
350
|
+
*/
|
|
351
|
+
getImpactedModules(id: string): string[];
|
|
352
|
+
/**
|
|
353
|
+
* Get all transitive dependencies of a module.
|
|
354
|
+
* Walks "outgoing" edges recursively.
|
|
355
|
+
*/
|
|
356
|
+
getTransitiveDependencies(id: string): string[];
|
|
357
|
+
/**
|
|
358
|
+
* Detect cycles in the graph.
|
|
359
|
+
* Returns an array of cycles, where each cycle is an array of module IDs.
|
|
360
|
+
*/
|
|
361
|
+
detectCycles(): string[][];
|
|
362
|
+
/**
|
|
363
|
+
* Topological sort of the graph.
|
|
364
|
+
* Returns null if the graph has cycles.
|
|
365
|
+
*/
|
|
366
|
+
topologicalSort(): string[] | null;
|
|
367
|
+
/**
|
|
368
|
+
* Find shared dependencies between two modules.
|
|
369
|
+
* Useful for understanding which code is shared vs. unique.
|
|
370
|
+
*/
|
|
371
|
+
getSharedDependencies(idA: string, idB: string): string[];
|
|
372
|
+
/**
|
|
373
|
+
* Find dependencies unique to a module (not shared with any other top-level module).
|
|
374
|
+
* Used for calculating "exclusive" bundle size.
|
|
375
|
+
*/
|
|
376
|
+
getExclusiveDependencies(id: string, allTopLevel: string[]): string[];
|
|
377
|
+
/** Get all node IDs in the graph. */
|
|
378
|
+
getAllNodes(): string[];
|
|
379
|
+
/** Get the total number of nodes. */
|
|
380
|
+
get nodeCount(): number;
|
|
381
|
+
/** Get the total number of edges. */
|
|
382
|
+
get edgeCount(): number;
|
|
383
|
+
private ensureNode;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Resolve and load the Foxlight configuration.
|
|
388
|
+
* Searches for config files in the given directory, or returns defaults.
|
|
389
|
+
*/
|
|
390
|
+
declare function loadConfig(rootDir: string): Promise<FoxlightConfig>;
|
|
391
|
+
/**
|
|
392
|
+
* Create a default configuration for the given root directory.
|
|
393
|
+
*/
|
|
394
|
+
declare function createDefaultConfig(rootDir: string): FoxlightConfig;
|
|
395
|
+
/**
|
|
396
|
+
* Auto-detect the framework used in a project by examining package.json.
|
|
397
|
+
*/
|
|
398
|
+
declare function detectFramework(rootDir: string): Promise<Framework>;
|
|
399
|
+
|
|
400
|
+
/** Default weights for each health metric (must sum to 1). */
|
|
401
|
+
interface HealthWeights {
|
|
402
|
+
bundleSize: number;
|
|
403
|
+
testCoverage: number;
|
|
404
|
+
accessibility: number;
|
|
405
|
+
freshness: number;
|
|
406
|
+
performance: number;
|
|
407
|
+
reliability: number;
|
|
408
|
+
}
|
|
409
|
+
declare const DEFAULT_WEIGHTS: HealthWeights;
|
|
410
|
+
/** All available data for computing a component's health. */
|
|
411
|
+
interface HealthInput {
|
|
412
|
+
component: ComponentInfo;
|
|
413
|
+
bundleInfo?: ComponentBundleInfo;
|
|
414
|
+
/** Test coverage percentage (0-100) */
|
|
415
|
+
testCoverage?: number;
|
|
416
|
+
/** Accessibility score (0-100, e.g., from axe-core) */
|
|
417
|
+
accessibilityScore?: number;
|
|
418
|
+
/** Days since the component was last modified */
|
|
419
|
+
daysSinceModified?: number;
|
|
420
|
+
/** Average render time in milliseconds */
|
|
421
|
+
renderTimeMs?: number;
|
|
422
|
+
/** Error rate (0-1, e.g., 0.02 = 2% error rate) */
|
|
423
|
+
errorRate?: number;
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Compute the full health score for a component.
|
|
427
|
+
*/
|
|
428
|
+
declare function computeComponentHealth(input: HealthInput, weights?: HealthWeights): ComponentHealth;
|
|
429
|
+
/**
|
|
430
|
+
* Compute health scores for multiple components.
|
|
431
|
+
*/
|
|
432
|
+
declare function computeAllHealth(inputs: HealthInput[], weights?: HealthWeights): ComponentHealth[];
|
|
433
|
+
|
|
434
|
+
declare const COST_MODELS: Record<string, CostModel>;
|
|
435
|
+
/** Monthly traffic assumptions for cost estimation. */
|
|
436
|
+
interface TrafficProfile {
|
|
437
|
+
/** Monthly page views */
|
|
438
|
+
monthlyPageViews: number;
|
|
439
|
+
/** Average number of SSR/serverless function invocations per page view */
|
|
440
|
+
invocationsPerPageView: number;
|
|
441
|
+
/** Percentage of requests handled at the edge (0-1) */
|
|
442
|
+
edgeRatio: number;
|
|
443
|
+
}
|
|
444
|
+
declare const DEFAULT_TRAFFIC: TrafficProfile;
|
|
445
|
+
/**
|
|
446
|
+
* Estimate the monthly cost impact of a change in bundle size.
|
|
447
|
+
*
|
|
448
|
+
* Computes costs based on:
|
|
449
|
+
* - Bandwidth: more bytes served = higher bandwidth cost
|
|
450
|
+
* - Invocations: unchanged (but included for completeness)
|
|
451
|
+
* - Storage: cost of storing assets on CDN/hosting
|
|
452
|
+
* - Edge: edge function invocation costs
|
|
453
|
+
*/
|
|
454
|
+
declare function estimateCostImpact(currentBundleInfo: ComponentBundleInfo[], updatedBundleInfo: ComponentBundleInfo[], costModel: CostModel, traffic?: TrafficProfile): CostImpact;
|
|
455
|
+
/**
|
|
456
|
+
* Estimate cost for a single set of bundle info (not a delta).
|
|
457
|
+
*/
|
|
458
|
+
declare function estimateMonthlyCost(bundleInfo: ComponentBundleInfo[], costModel: CostModel, traffic?: TrafficProfile): number;
|
|
459
|
+
|
|
460
|
+
interface UpgradeAnalysisOptions {
|
|
461
|
+
/** Project root directory */
|
|
462
|
+
rootDir: string;
|
|
463
|
+
/** Package name to analyze (e.g., "react") */
|
|
464
|
+
packageName: string;
|
|
465
|
+
/** Target version to upgrade to (e.g., "19.0.0"). If omitted, uses latest. */
|
|
466
|
+
targetVersion?: string;
|
|
467
|
+
/** Components that depend on this package (for impact analysis) */
|
|
468
|
+
affectedComponents?: ComponentInfo[];
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Analyze the impact of upgrading a dependency.
|
|
472
|
+
*
|
|
473
|
+
* Runs several checks:
|
|
474
|
+
* 1. Version resolution (current vs target)
|
|
475
|
+
* 2. Semver risk assessment (major/minor/patch)
|
|
476
|
+
* 3. Component impact (which components use this dep)
|
|
477
|
+
* 4. Peer dependency compatibility
|
|
478
|
+
* 5. Bundle size change estimate
|
|
479
|
+
*/
|
|
480
|
+
declare function analyzeUpgrade(options: UpgradeAnalysisOptions): Promise<UpgradePreview>;
|
|
481
|
+
/**
|
|
482
|
+
* Analyze multiple upgrades at once.
|
|
483
|
+
*/
|
|
484
|
+
declare function analyzeUpgrades(rootDir: string, packages: Array<{
|
|
485
|
+
name: string;
|
|
486
|
+
targetVersion?: string;
|
|
487
|
+
}>, allComponents?: ComponentInfo[]): Promise<UpgradePreview[]>;
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* Extract ES module import statements from a script string
|
|
491
|
+
* using regex matching. Handles:
|
|
492
|
+
* - Default imports: `import Foo from '...'`
|
|
493
|
+
* - Named imports: `import { a, b } from '...'`
|
|
494
|
+
* - Mixed imports: `import Foo, { a } from '...'`
|
|
495
|
+
* - Namespace imports: `import * as Foo from '...'`
|
|
496
|
+
* - Type imports: `import type { ... } from '...'`
|
|
497
|
+
*
|
|
498
|
+
* @param script — The JavaScript/TypeScript source text to scan.
|
|
499
|
+
* @param filePath — The file this script belongs to (used as the `source` in ImportEdge).
|
|
500
|
+
*/
|
|
501
|
+
declare function extractImportsFromScript(script: string, filePath: string): ImportEdge[];
|
|
502
|
+
|
|
503
|
+
/** Istanbul coverage data for a single file. */
|
|
504
|
+
interface IstanbulCoverageFile {
|
|
505
|
+
path: string;
|
|
506
|
+
statementMap: Record<string, {
|
|
507
|
+
start: {
|
|
508
|
+
line: number;
|
|
509
|
+
};
|
|
510
|
+
end: {
|
|
511
|
+
line: number;
|
|
512
|
+
};
|
|
513
|
+
}>;
|
|
514
|
+
fnMap: Record<string, {
|
|
515
|
+
name: string;
|
|
516
|
+
decl: {
|
|
517
|
+
start: {
|
|
518
|
+
line: number;
|
|
519
|
+
};
|
|
520
|
+
};
|
|
521
|
+
}>;
|
|
522
|
+
branchMap: Record<string, {
|
|
523
|
+
loc: {
|
|
524
|
+
start: {
|
|
525
|
+
line: number;
|
|
526
|
+
};
|
|
527
|
+
};
|
|
528
|
+
}>;
|
|
529
|
+
s: Record<string, number>;
|
|
530
|
+
f: Record<string, number>;
|
|
531
|
+
b: Record<string, number[]>;
|
|
532
|
+
}
|
|
533
|
+
interface ComponentCoverage {
|
|
534
|
+
componentId: string;
|
|
535
|
+
filePath: string;
|
|
536
|
+
statementsCovered: number;
|
|
537
|
+
statementsTotal: number;
|
|
538
|
+
functionsCovered: number;
|
|
539
|
+
functionsTotal: number;
|
|
540
|
+
percentage: number;
|
|
541
|
+
isCovered: boolean;
|
|
542
|
+
}
|
|
543
|
+
interface CoverageReport {
|
|
544
|
+
components: Map<string, ComponentCoverage>;
|
|
545
|
+
totalStatementsCovered: number;
|
|
546
|
+
totalStatementsTotal: number;
|
|
547
|
+
overallPercentage: number;
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* Load coverage data from Istanbul JSON format.
|
|
551
|
+
* Looks for coverage/coverage-final.json by default.
|
|
552
|
+
*/
|
|
553
|
+
declare function loadCoverageData(projectRoot: string, customCoveragePath?: string): Promise<Record<string, IstanbulCoverageFile>>;
|
|
554
|
+
/**
|
|
555
|
+
* Map coverage data to components.
|
|
556
|
+
* Matches files in coverage data to component file paths.
|
|
557
|
+
*/
|
|
558
|
+
declare function mapCoverageToComponents(coverageData: Record<string, IstanbulCoverageFile>, componentFilePaths: Set<string>): CoverageReport;
|
|
559
|
+
/**
|
|
560
|
+
* Get coverage percentage for a component by file path or ID.
|
|
561
|
+
* Returns 0 if no coverage data found (uncovered component).
|
|
562
|
+
*/
|
|
563
|
+
declare function getComponentCoverage(componentFilePath: string, coverage: CoverageReport): number;
|
|
564
|
+
/**
|
|
565
|
+
* Find components with no test coverage (0% coverage).
|
|
566
|
+
*/
|
|
567
|
+
declare function findUncoveredComponents(coverage: CoverageReport): ComponentCoverage[];
|
|
568
|
+
/**
|
|
569
|
+
* Find components with low test coverage (below threshold).
|
|
570
|
+
*/
|
|
571
|
+
declare function findLowCoverageComponents(coverage: CoverageReport, threshold?: number): ComponentCoverage[];
|
|
572
|
+
/**
|
|
573
|
+
* Generate a coverage report summary.
|
|
574
|
+
*/
|
|
575
|
+
declare function summarizeCoverage(coverage: CoverageReport): string;
|
|
576
|
+
|
|
577
|
+
interface UnusedComponent {
|
|
578
|
+
id: string;
|
|
579
|
+
name: string;
|
|
580
|
+
filePath: string;
|
|
581
|
+
potentialSavings?: string;
|
|
582
|
+
reason: 'never_imported' | 'unused_export' | 'orphaned';
|
|
583
|
+
}
|
|
584
|
+
interface UnusedExport {
|
|
585
|
+
filePath: string;
|
|
586
|
+
exportName: string;
|
|
587
|
+
reason: 'exported_but_unused' | 're_exported_unused';
|
|
588
|
+
}
|
|
589
|
+
interface DeadCodeReport {
|
|
590
|
+
unusedComponents: UnusedComponent[];
|
|
591
|
+
orphanedComponents: UnusedComponent[];
|
|
592
|
+
unusedExports: UnusedExport[];
|
|
593
|
+
totalPotentialBytes: number;
|
|
594
|
+
}
|
|
595
|
+
/**
|
|
596
|
+
* Analyze the component registry to find dead code.
|
|
597
|
+
*/
|
|
598
|
+
declare function detectDeadCode(registry: ComponentRegistry): DeadCodeReport;
|
|
599
|
+
/**
|
|
600
|
+
* Get components that should definitely be safe to remove.
|
|
601
|
+
* These are unused components with no dependencies.
|
|
602
|
+
*/
|
|
603
|
+
declare function findSafeRemovalCandidates(report: DeadCodeReport): UnusedComponent[];
|
|
604
|
+
/**
|
|
605
|
+
* Format dead code report for display.
|
|
606
|
+
*/
|
|
607
|
+
declare function formatDeadCodeReport(report: DeadCodeReport): string;
|
|
608
|
+
|
|
609
|
+
interface ComponentAPI {
|
|
610
|
+
componentId: string;
|
|
611
|
+
name: string;
|
|
612
|
+
filePath: string;
|
|
613
|
+
exportKind: 'named' | 'default' | 're-export';
|
|
614
|
+
props: PropInfo[];
|
|
615
|
+
timestamp: string;
|
|
616
|
+
version?: string;
|
|
617
|
+
}
|
|
618
|
+
interface APISnapshot {
|
|
619
|
+
components: Map<string, ComponentAPI>;
|
|
620
|
+
timestamp: string;
|
|
621
|
+
hash?: string;
|
|
622
|
+
}
|
|
623
|
+
type BreakingChangeType = 'prop_removed' | 'prop_required_changed' | 'export_removed' | 'export_kind_changed';
|
|
624
|
+
interface BreakingChange {
|
|
625
|
+
componentId: string;
|
|
626
|
+
componentName: string;
|
|
627
|
+
changeType: BreakingChangeType;
|
|
628
|
+
description: string;
|
|
629
|
+
affectedItems: string[];
|
|
630
|
+
severity: 'critical' | 'high' | 'medium';
|
|
631
|
+
}
|
|
632
|
+
interface APIChangeSummary {
|
|
633
|
+
addedComponents: ComponentAPI[];
|
|
634
|
+
removedComponents: ComponentAPI[];
|
|
635
|
+
modifiedComponents: {
|
|
636
|
+
component: ComponentAPI;
|
|
637
|
+
changes: APIChange[];
|
|
638
|
+
}[];
|
|
639
|
+
breaking: BreakingChange[];
|
|
640
|
+
nonBreaking: APIChange[];
|
|
641
|
+
}
|
|
642
|
+
interface APIChange {
|
|
643
|
+
type: 'added' | 'removed' | 'modified';
|
|
644
|
+
field: 'prop' | 'export_kind';
|
|
645
|
+
oldValue?: unknown;
|
|
646
|
+
newValue?: unknown;
|
|
647
|
+
}
|
|
648
|
+
/**
|
|
649
|
+
* Create a snapshot of component APIs.
|
|
650
|
+
*/
|
|
651
|
+
declare function createAPISnapshot(components: ComponentInfo[], hash?: string): APISnapshot;
|
|
652
|
+
/**
|
|
653
|
+
* Serialize snapshot to JSON for storage.
|
|
654
|
+
*/
|
|
655
|
+
declare function snapshotToJSON(snapshot: APISnapshot): string;
|
|
656
|
+
/**
|
|
657
|
+
* Deserialize snapshot from JSON.
|
|
658
|
+
*/
|
|
659
|
+
declare function snapshotFromJSON(json: string): APISnapshot;
|
|
660
|
+
/**
|
|
661
|
+
* Compare two API snapshots and detect breaking changes.
|
|
662
|
+
*/
|
|
663
|
+
declare function compareSnapshots(oldSnapshot: APISnapshot, newSnapshot: APISnapshot): APIChangeSummary;
|
|
664
|
+
/**
|
|
665
|
+
* Format API changes for display.
|
|
666
|
+
*/
|
|
667
|
+
declare function formatAPIChangeSummary(summary: APIChangeSummary): string;
|
|
668
|
+
|
|
669
|
+
export { type APIChangeSummary, type APISnapshot, type BaselineConfig, type BreakingChange, type BundleDiffEntry, COST_MODELS, type ComponentAPI, type ComponentBundleInfo, type ComponentCoverage, type ComponentHealth, type ComponentId, type ComponentInfo, type ComponentModification, ComponentRegistry, type CostBreakdownItem, type CostImpact, type CostModel, type CoverageReport, DEFAULT_TRAFFIC, DEFAULT_WEIGHTS, type DeadCodeReport, DependencyGraph, type ExportKind, type FoxlightConfig, type Framework, type HealthDiffEntry, type HealthInput, type HealthMetrics, type HealthWeights, type ImportEdge, type ImportSpecifier, type MetricScore, type PluginConfig, type ProjectSnapshot, type PropInfo, type SizeInfo, type SnapshotDiff, type TrafficProfile, type UnusedComponent, type UnusedExport, type UpgradeAnalysisOptions, type UpgradeCheck, type UpgradePreview, analyzeUpgrade, analyzeUpgrades, compareSnapshots, computeAllHealth, computeComponentHealth, createAPISnapshot, createDefaultConfig, detectDeadCode, detectFramework, estimateCostImpact, estimateMonthlyCost, extractImportsFromScript, findLowCoverageComponents, findSafeRemovalCandidates, findUncoveredComponents, formatAPIChangeSummary, formatDeadCodeReport, getComponentCoverage, loadConfig, loadCoverageData, mapCoverageToComponents, snapshotFromJSON, snapshotToJSON, summarizeCoverage };
|