fcis 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/.plans/001-fcis-analyzer.md +832 -0
- package/.plans/002-fcis-analyzer-improvements.md +205 -0
- package/README.md +272 -0
- package/TECHNICAL.md +386 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +1836 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +709 -0
- package/dist/index.js +1845 -0
- package/dist/index.js.map +1 -0
- package/package.json +47 -0
- package/pnpm-workspace.yaml +0 -0
- package/src/analyzer.ts +266 -0
- package/src/classification/classifier.ts +156 -0
- package/src/classification/derive-status.ts +171 -0
- package/src/classification/quality-scorer.ts +481 -0
- package/src/cli.ts +286 -0
- package/src/detection/detect-markers.ts +480 -0
- package/src/detection/markers.ts +332 -0
- package/src/extraction/extract-functions.ts +570 -0
- package/src/extraction/extractor.ts +188 -0
- package/src/index.ts +111 -0
- package/src/reporting/report-console.ts +416 -0
- package/src/reporting/report-json.ts +232 -0
- package/src/scoring/scorer.ts +504 -0
- package/src/types.ts +248 -0
- package/tests/classifier.test.ts +480 -0
- package/tests/derive-status.test.ts +464 -0
- package/tests/detect-markers.test.ts +639 -0
- package/tests/extractor.test.ts +155 -0
- package/tests/integration.test.ts +706 -0
- package/tests/quality-scorer.test.ts +650 -0
- package/tests/scorer.test.ts +768 -0
- package/tsconfig.json +34 -0
- package/tsup.config.ts +17 -0
- package/vendor/ts-morph/.editorconfig +10 -0
- package/vendor/ts-morph/.gitattributes +11 -0
- package/vendor/ts-morph/.github/CODE_OF_CONDUCT.md +77 -0
- package/vendor/ts-morph/.github/ISSUE_TEMPLATE/bug_report.md +29 -0
- package/vendor/ts-morph/.github/ISSUE_TEMPLATE/custom.md +4 -0
- package/vendor/ts-morph/.github/ISSUE_TEMPLATE/feature_request.md +18 -0
- package/vendor/ts-morph/.github/workflows/ci.yml +50 -0
- package/vendor/ts-morph/.github/workflows/publish.yml +53 -0
- package/vendor/ts-morph/.vscode/settings.json +10 -0
- package/vendor/ts-morph/CONTRIBUTING.md +23 -0
- package/vendor/ts-morph/DEVELOPMENT.md +32 -0
- package/vendor/ts-morph/LICENSE +21 -0
- package/vendor/ts-morph/deno.json +8 -0
- package/vendor/ts-morph/deno.lock +1233 -0
- package/vendor/ts-morph/docs/CNAME +1 -0
- package/vendor/ts-morph/docs/Gemfile +2 -0
- package/vendor/ts-morph/docs/_config.yml +5 -0
- package/vendor/ts-morph/docs/_layouts/default.html +159 -0
- package/vendor/ts-morph/docs/_script-templates/main.ts +116 -0
- package/vendor/ts-morph/docs/assets/css/style.scss +212 -0
- package/vendor/ts-morph/docs/details/ambient.md +38 -0
- package/vendor/ts-morph/docs/details/async.md +31 -0
- package/vendor/ts-morph/docs/details/classes.md +314 -0
- package/vendor/ts-morph/docs/details/comment-ranges.md +7 -0
- package/vendor/ts-morph/docs/details/comments.md +122 -0
- package/vendor/ts-morph/docs/details/decorators.md +119 -0
- package/vendor/ts-morph/docs/details/documentation.md +73 -0
- package/vendor/ts-morph/docs/details/enums.md +117 -0
- package/vendor/ts-morph/docs/details/exports.md +308 -0
- package/vendor/ts-morph/docs/details/expressions.md +46 -0
- package/vendor/ts-morph/docs/details/functions.md +150 -0
- package/vendor/ts-morph/docs/details/generators.md +27 -0
- package/vendor/ts-morph/docs/details/identifiers.md +79 -0
- package/vendor/ts-morph/docs/details/imports.md +191 -0
- package/vendor/ts-morph/docs/details/index.md +52 -0
- package/vendor/ts-morph/docs/details/initializers.md +40 -0
- package/vendor/ts-morph/docs/details/interfaces.md +218 -0
- package/vendor/ts-morph/docs/details/literals.md +20 -0
- package/vendor/ts-morph/docs/details/modifiers.md +38 -0
- package/vendor/ts-morph/docs/details/modules.md +113 -0
- package/vendor/ts-morph/docs/details/namespaces.md +7 -0
- package/vendor/ts-morph/docs/details/object-literal-expressions.md +106 -0
- package/vendor/ts-morph/docs/details/parameters.md +64 -0
- package/vendor/ts-morph/docs/details/signatures.md +41 -0
- package/vendor/ts-morph/docs/details/source-files.md +292 -0
- package/vendor/ts-morph/docs/details/type-aliases.md +34 -0
- package/vendor/ts-morph/docs/details/type-parameters.md +72 -0
- package/vendor/ts-morph/docs/details/types.md +254 -0
- package/vendor/ts-morph/docs/details/variables.md +110 -0
- package/vendor/ts-morph/docs/emitting.md +151 -0
- package/vendor/ts-morph/docs/index.md +25 -0
- package/vendor/ts-morph/docs/manipulation/code-writer.md +20 -0
- package/vendor/ts-morph/docs/manipulation/formatting.md +76 -0
- package/vendor/ts-morph/docs/manipulation/index.md +136 -0
- package/vendor/ts-morph/docs/manipulation/order.md +14 -0
- package/vendor/ts-morph/docs/manipulation/performance.md +222 -0
- package/vendor/ts-morph/docs/manipulation/removing.md +31 -0
- package/vendor/ts-morph/docs/manipulation/renaming.md +106 -0
- package/vendor/ts-morph/docs/manipulation/settings.md +76 -0
- package/vendor/ts-morph/docs/manipulation/structures.md +117 -0
- package/vendor/ts-morph/docs/manipulation/transforms.md +84 -0
- package/vendor/ts-morph/docs/metrics/performance.json +4 -0
- package/vendor/ts-morph/docs/navigation/ambient-modules.md +22 -0
- package/vendor/ts-morph/docs/navigation/compiler-nodes.md +82 -0
- package/vendor/ts-morph/docs/navigation/directories.md +287 -0
- package/vendor/ts-morph/docs/navigation/example.md +50 -0
- package/vendor/ts-morph/docs/navigation/finding-references.md +53 -0
- package/vendor/ts-morph/docs/navigation/getting-source-files.md +59 -0
- package/vendor/ts-morph/docs/navigation/images/getChildrenVsForEachChild.gif +0 -0
- package/vendor/ts-morph/docs/navigation/index.md +94 -0
- package/vendor/ts-morph/docs/navigation/language-service.md +23 -0
- package/vendor/ts-morph/docs/navigation/program.md +25 -0
- package/vendor/ts-morph/docs/navigation/type-checker.md +33 -0
- package/vendor/ts-morph/docs/setup/adding-source-files.md +145 -0
- package/vendor/ts-morph/docs/setup/ast-viewers.md +46 -0
- package/vendor/ts-morph/docs/setup/diagnostics.md +109 -0
- package/vendor/ts-morph/docs/setup/file-system.md +106 -0
- package/vendor/ts-morph/docs/setup/images/atom-ast.png +0 -0
- package/vendor/ts-morph/docs/setup/images/atom-ast_small.png +0 -0
- package/vendor/ts-morph/docs/setup/images/atom-command-palette.png +0 -0
- package/vendor/ts-morph/docs/setup/images/atom-file.png +0 -0
- package/vendor/ts-morph/docs/setup/images/ts-ast-viewer.png +0 -0
- package/vendor/ts-morph/docs/setup/index.md +94 -0
- package/vendor/ts-morph/docs/utilities.md +55 -0
- package/vendor/ts-morph/dprint.json +23 -0
- package/vendor/ts-morph/package.json +30 -0
- package/vendor/ts-morph/packages/bootstrap/LICENSE +21 -0
- package/vendor/ts-morph/packages/bootstrap/lib/ts-morph-bootstrap.d.ts +397 -0
- package/vendor/ts-morph/packages/bootstrap/package.json +46 -0
- package/vendor/ts-morph/packages/bootstrap/readme.md +200 -0
- package/vendor/ts-morph/packages/common/LICENSE +21 -0
- package/vendor/ts-morph/packages/common/lib/ts-morph-common.d.ts +1082 -0
- package/vendor/ts-morph/packages/common/lib/typescript.d.ts +11439 -0
- package/vendor/ts-morph/packages/common/package.json +65 -0
- package/vendor/ts-morph/packages/common/readme.md +5 -0
- package/vendor/ts-morph/packages/scripts/changeTypeScriptVersion.ts +28 -0
- package/vendor/ts-morph/packages/scripts/createDeclarationProject.ts +47 -0
- package/vendor/ts-morph/packages/scripts/deps.ts +2 -0
- package/vendor/ts-morph/packages/scripts/execScript.ts +31 -0
- package/vendor/ts-morph/packages/scripts/folders.ts +11 -0
- package/vendor/ts-morph/packages/scripts/getDevCompilerVersions.ts +19 -0
- package/vendor/ts-morph/packages/scripts/mod.ts +7 -0
- package/vendor/ts-morph/packages/scripts/utils/Memoize.ts +36 -0
- package/vendor/ts-morph/packages/scripts/utils/forEachTypeText.ts +23 -0
- package/vendor/ts-morph/packages/scripts/utils/makeConstructorsPrivate.ts +26 -0
- package/vendor/ts-morph/packages/scripts/utils/mod.ts +4 -0
- package/vendor/ts-morph/packages/scripts/utils/printDiagnostics.ts +10 -0
- package/vendor/ts-morph/packages/ts-morph/LICENSE +21 -0
- package/vendor/ts-morph/packages/ts-morph/lib/ts-morph.d.ts +11198 -0
- package/vendor/ts-morph/packages/ts-morph/package.json +78 -0
- package/vendor/ts-morph/packages/ts-morph/readme.md +111 -0
- package/vendor/ts-morph/readme.md +14 -0
- package/vendor/ts-morph/rfcs/README.md +13 -0
- package/vendor/ts-morph/rfcs/RFC-0001 - Inserting Into Statements Handling Comments.md +181 -0
- package/vendor/ts-morph/tsconfig.common.json +17 -0
- package/vitest.config.ts +16 -0
package/src/types.ts
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core type definitions for the FCIS Analyzer
|
|
3
|
+
*
|
|
4
|
+
* These types form the contract between the analyzer's layers:
|
|
5
|
+
* - Extraction layer produces ExtractedFunction and FileImports
|
|
6
|
+
* - Detection layer produces ImpurityMarker[]
|
|
7
|
+
* - Classification layer produces ClassifiedFunction
|
|
8
|
+
* - Scoring layer produces FileScore, DirectoryScore, ProjectScore
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* CallSite captures position and context for each call expression,
|
|
13
|
+
* enabling sequence analysis (GATHER→DECIDE→EXECUTE detection)
|
|
14
|
+
*/
|
|
15
|
+
export type CallSite = {
|
|
16
|
+
expression: string // e.g. "db.user.findFirst", "planAcceptInvite"
|
|
17
|
+
line: number // position within the function (relative to function start)
|
|
18
|
+
isAwaited: boolean // was this call preceded by `await`?
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Represents a function extracted from source code
|
|
23
|
+
*/
|
|
24
|
+
export type ExtractedFunction = {
|
|
25
|
+
name: string | null
|
|
26
|
+
filePath: string
|
|
27
|
+
startLine: number
|
|
28
|
+
endLine: number
|
|
29
|
+
isAsync: boolean
|
|
30
|
+
isExported: boolean
|
|
31
|
+
bodyLineCount: number
|
|
32
|
+
statementCount: number
|
|
33
|
+
hasConditionals: boolean
|
|
34
|
+
parentContext: string | null // e.g. class name, variable name
|
|
35
|
+
callSites: CallSite[] // enriched call expressions with position and await context
|
|
36
|
+
hasAwait: boolean // convenience flag: true if any callSite.isAwaited
|
|
37
|
+
propertyAccessChains: string[] // e.g. ["process.env.NODE_ENV", "db.user"]
|
|
38
|
+
kind:
|
|
39
|
+
| 'function'
|
|
40
|
+
| 'method'
|
|
41
|
+
| 'arrow'
|
|
42
|
+
| 'function-expression'
|
|
43
|
+
| 'getter'
|
|
44
|
+
| 'setter'
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Represents imports in a file
|
|
49
|
+
*/
|
|
50
|
+
export type FileImports = {
|
|
51
|
+
filePath: string
|
|
52
|
+
imports: {
|
|
53
|
+
moduleSpecifier: string // e.g. "@sai/database", "./organizations.pure"
|
|
54
|
+
namedImports: string[] // e.g. ["planAcceptInvite", "AcceptInviteDecision"]
|
|
55
|
+
}[]
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Strict marker type union — prevents typos, enables exhaustive matching
|
|
60
|
+
* Note: 'async-function' removed — async alone is not an impurity marker
|
|
61
|
+
* Note: React hook markers deferred to v2
|
|
62
|
+
*/
|
|
63
|
+
export type MarkerType =
|
|
64
|
+
| 'await-expression'
|
|
65
|
+
| 'database-call'
|
|
66
|
+
| 'network-fetch'
|
|
67
|
+
| 'network-http'
|
|
68
|
+
| 'fs-import'
|
|
69
|
+
| 'fs-call'
|
|
70
|
+
| 'env-access'
|
|
71
|
+
| 'console-log'
|
|
72
|
+
| 'logging'
|
|
73
|
+
| 'telemetry'
|
|
74
|
+
| 'queue-enqueue'
|
|
75
|
+
| 'event-emit'
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Represents a detected impurity marker in a function
|
|
79
|
+
*/
|
|
80
|
+
export type ImpurityMarker = {
|
|
81
|
+
type: MarkerType
|
|
82
|
+
detail: string // e.g. "db.user.findFirst"
|
|
83
|
+
line?: number // optional line number for better reporting
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Binary classification — objective, no heuristics
|
|
88
|
+
*/
|
|
89
|
+
export type FunctionClassification = 'pure' | 'impure'
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Derived status for UX — actionable guidance
|
|
93
|
+
*/
|
|
94
|
+
export type Status = 'ok' | 'review' | 'refactor'
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* A function that has been classified with markers, classification, quality score, and status
|
|
98
|
+
*/
|
|
99
|
+
export type ClassifiedFunction = ExtractedFunction & {
|
|
100
|
+
markers: ImpurityMarker[]
|
|
101
|
+
classification: FunctionClassification
|
|
102
|
+
qualityScore: number | null // 0-100 for impure functions; null for pure
|
|
103
|
+
status: Status // derived from classification + qualityScore
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Breakdown of function statuses
|
|
108
|
+
*/
|
|
109
|
+
export type StatusBreakdown = {
|
|
110
|
+
ok: number
|
|
111
|
+
review: number
|
|
112
|
+
refactor: number
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Refactoring candidate summary for reporting
|
|
117
|
+
*/
|
|
118
|
+
export type RefactoringCandidate = {
|
|
119
|
+
name: string | null
|
|
120
|
+
filePath: string
|
|
121
|
+
startLine: number
|
|
122
|
+
bodyLineCount: number
|
|
123
|
+
qualityScore: number
|
|
124
|
+
markers: MarkerType[]
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Score for a single file
|
|
129
|
+
*/
|
|
130
|
+
export type FileScore = {
|
|
131
|
+
filePath: string
|
|
132
|
+
purity: number // 0-100: pure / (pure + impure)
|
|
133
|
+
impurityQuality: number | null // 0-100: avg quality of impure functions; null if none
|
|
134
|
+
health: number // 0-100: functions with status 'ok' / total
|
|
135
|
+
pureCount: number
|
|
136
|
+
impureCount: number
|
|
137
|
+
excludedCount: number
|
|
138
|
+
statusBreakdown: StatusBreakdown
|
|
139
|
+
pureLineCount: number // total lines in pure functions
|
|
140
|
+
impureLineCount: number // total lines in impure functions
|
|
141
|
+
functions: ClassifiedFunction[]
|
|
142
|
+
refactoringCandidates: Omit<RefactoringCandidate, 'filePath'>[]
|
|
143
|
+
// Edge case flags
|
|
144
|
+
allExcluded?: boolean // true if all functions were excluded
|
|
145
|
+
typeOnly?: boolean // true if file has only type/interface/enum exports
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Score for a directory
|
|
150
|
+
*/
|
|
151
|
+
export type DirectoryScore = {
|
|
152
|
+
dirPath: string
|
|
153
|
+
purity: number
|
|
154
|
+
impurityQuality: number | null
|
|
155
|
+
health: number
|
|
156
|
+
pureCount: number
|
|
157
|
+
impureCount: number
|
|
158
|
+
excludedCount: number
|
|
159
|
+
statusBreakdown: StatusBreakdown
|
|
160
|
+
pureLineCount: number
|
|
161
|
+
impureLineCount: number
|
|
162
|
+
fileScores: FileScore[]
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Project-level score (top-level output)
|
|
167
|
+
*/
|
|
168
|
+
export type ProjectScore = {
|
|
169
|
+
purity: number // 0-100: percentage of pure functions
|
|
170
|
+
impurityQuality: number | null // 0-100: avg quality of impure functions
|
|
171
|
+
health: number // 0-100: percentage with status 'ok'
|
|
172
|
+
pureCount: number
|
|
173
|
+
impureCount: number
|
|
174
|
+
excludedCount: number
|
|
175
|
+
statusBreakdown: StatusBreakdown
|
|
176
|
+
pureLineCount: number
|
|
177
|
+
impureLineCount: number
|
|
178
|
+
directoryScores: DirectoryScore[]
|
|
179
|
+
timestamp: string
|
|
180
|
+
commitHash: string | null
|
|
181
|
+
// Sorted by bodyLineCount × (100 - qualityScore) descending (largest, lowest-quality first)
|
|
182
|
+
refactoringCandidates: RefactoringCandidate[]
|
|
183
|
+
// Edge case flags
|
|
184
|
+
allExcluded?: boolean // true if all functions were excluded
|
|
185
|
+
// Subset analysis metadata (when --files is used)
|
|
186
|
+
subset?: boolean // true if this is a subset analysis
|
|
187
|
+
filesGlob?: string // the glob pattern used for --files
|
|
188
|
+
// Errors encountered during analysis
|
|
189
|
+
errors?: AnalysisError[]
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Error encountered during analysis
|
|
194
|
+
*/
|
|
195
|
+
export type AnalysisError = {
|
|
196
|
+
filePath: string
|
|
197
|
+
error: string
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Configuration options for the analyzer
|
|
202
|
+
*/
|
|
203
|
+
export type AnalyzerConfig = {
|
|
204
|
+
tsconfigPath: string
|
|
205
|
+
filesGlob?: string
|
|
206
|
+
minHealth?: number
|
|
207
|
+
minPurity?: number
|
|
208
|
+
minQuality?: number
|
|
209
|
+
format?: 'console' | 'json' | 'summary'
|
|
210
|
+
outputPath?: string
|
|
211
|
+
quiet?: boolean
|
|
212
|
+
verbose?: boolean
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Quality scoring thresholds (configurable)
|
|
217
|
+
*/
|
|
218
|
+
export type QualityThresholds = {
|
|
219
|
+
okThreshold: number // >= this is 'ok' (default: 70)
|
|
220
|
+
reviewThreshold: number // >= this is 'review', below is 'refactor' (default: 40)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Default quality thresholds
|
|
225
|
+
*/
|
|
226
|
+
export const DEFAULT_QUALITY_THRESHOLDS: QualityThresholds = {
|
|
227
|
+
okThreshold: 70,
|
|
228
|
+
reviewThreshold: 40,
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Result of extracting functions from a file
|
|
233
|
+
*/
|
|
234
|
+
export type ExtractionResult = {
|
|
235
|
+
filePath: string
|
|
236
|
+
functions: ExtractedFunction[]
|
|
237
|
+
imports: FileImports
|
|
238
|
+
isTypeOnly: boolean
|
|
239
|
+
error?: string
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Detection context passed to marker detection
|
|
244
|
+
*/
|
|
245
|
+
export type DetectionContext = {
|
|
246
|
+
imports: FileImports
|
|
247
|
+
pureFileImports: Set<string> // module specifiers ending in .pure
|
|
248
|
+
}
|