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.
Files changed (151) hide show
  1. package/.plans/001-fcis-analyzer.md +832 -0
  2. package/.plans/002-fcis-analyzer-improvements.md +205 -0
  3. package/README.md +272 -0
  4. package/TECHNICAL.md +386 -0
  5. package/dist/cli.d.ts +1 -0
  6. package/dist/cli.js +1836 -0
  7. package/dist/cli.js.map +1 -0
  8. package/dist/index.d.ts +709 -0
  9. package/dist/index.js +1845 -0
  10. package/dist/index.js.map +1 -0
  11. package/package.json +47 -0
  12. package/pnpm-workspace.yaml +0 -0
  13. package/src/analyzer.ts +266 -0
  14. package/src/classification/classifier.ts +156 -0
  15. package/src/classification/derive-status.ts +171 -0
  16. package/src/classification/quality-scorer.ts +481 -0
  17. package/src/cli.ts +286 -0
  18. package/src/detection/detect-markers.ts +480 -0
  19. package/src/detection/markers.ts +332 -0
  20. package/src/extraction/extract-functions.ts +570 -0
  21. package/src/extraction/extractor.ts +188 -0
  22. package/src/index.ts +111 -0
  23. package/src/reporting/report-console.ts +416 -0
  24. package/src/reporting/report-json.ts +232 -0
  25. package/src/scoring/scorer.ts +504 -0
  26. package/src/types.ts +248 -0
  27. package/tests/classifier.test.ts +480 -0
  28. package/tests/derive-status.test.ts +464 -0
  29. package/tests/detect-markers.test.ts +639 -0
  30. package/tests/extractor.test.ts +155 -0
  31. package/tests/integration.test.ts +706 -0
  32. package/tests/quality-scorer.test.ts +650 -0
  33. package/tests/scorer.test.ts +768 -0
  34. package/tsconfig.json +34 -0
  35. package/tsup.config.ts +17 -0
  36. package/vendor/ts-morph/.editorconfig +10 -0
  37. package/vendor/ts-morph/.gitattributes +11 -0
  38. package/vendor/ts-morph/.github/CODE_OF_CONDUCT.md +77 -0
  39. package/vendor/ts-morph/.github/ISSUE_TEMPLATE/bug_report.md +29 -0
  40. package/vendor/ts-morph/.github/ISSUE_TEMPLATE/custom.md +4 -0
  41. package/vendor/ts-morph/.github/ISSUE_TEMPLATE/feature_request.md +18 -0
  42. package/vendor/ts-morph/.github/workflows/ci.yml +50 -0
  43. package/vendor/ts-morph/.github/workflows/publish.yml +53 -0
  44. package/vendor/ts-morph/.vscode/settings.json +10 -0
  45. package/vendor/ts-morph/CONTRIBUTING.md +23 -0
  46. package/vendor/ts-morph/DEVELOPMENT.md +32 -0
  47. package/vendor/ts-morph/LICENSE +21 -0
  48. package/vendor/ts-morph/deno.json +8 -0
  49. package/vendor/ts-morph/deno.lock +1233 -0
  50. package/vendor/ts-morph/docs/CNAME +1 -0
  51. package/vendor/ts-morph/docs/Gemfile +2 -0
  52. package/vendor/ts-morph/docs/_config.yml +5 -0
  53. package/vendor/ts-morph/docs/_layouts/default.html +159 -0
  54. package/vendor/ts-morph/docs/_script-templates/main.ts +116 -0
  55. package/vendor/ts-morph/docs/assets/css/style.scss +212 -0
  56. package/vendor/ts-morph/docs/details/ambient.md +38 -0
  57. package/vendor/ts-morph/docs/details/async.md +31 -0
  58. package/vendor/ts-morph/docs/details/classes.md +314 -0
  59. package/vendor/ts-morph/docs/details/comment-ranges.md +7 -0
  60. package/vendor/ts-morph/docs/details/comments.md +122 -0
  61. package/vendor/ts-morph/docs/details/decorators.md +119 -0
  62. package/vendor/ts-morph/docs/details/documentation.md +73 -0
  63. package/vendor/ts-morph/docs/details/enums.md +117 -0
  64. package/vendor/ts-morph/docs/details/exports.md +308 -0
  65. package/vendor/ts-morph/docs/details/expressions.md +46 -0
  66. package/vendor/ts-morph/docs/details/functions.md +150 -0
  67. package/vendor/ts-morph/docs/details/generators.md +27 -0
  68. package/vendor/ts-morph/docs/details/identifiers.md +79 -0
  69. package/vendor/ts-morph/docs/details/imports.md +191 -0
  70. package/vendor/ts-morph/docs/details/index.md +52 -0
  71. package/vendor/ts-morph/docs/details/initializers.md +40 -0
  72. package/vendor/ts-morph/docs/details/interfaces.md +218 -0
  73. package/vendor/ts-morph/docs/details/literals.md +20 -0
  74. package/vendor/ts-morph/docs/details/modifiers.md +38 -0
  75. package/vendor/ts-morph/docs/details/modules.md +113 -0
  76. package/vendor/ts-morph/docs/details/namespaces.md +7 -0
  77. package/vendor/ts-morph/docs/details/object-literal-expressions.md +106 -0
  78. package/vendor/ts-morph/docs/details/parameters.md +64 -0
  79. package/vendor/ts-morph/docs/details/signatures.md +41 -0
  80. package/vendor/ts-morph/docs/details/source-files.md +292 -0
  81. package/vendor/ts-morph/docs/details/type-aliases.md +34 -0
  82. package/vendor/ts-morph/docs/details/type-parameters.md +72 -0
  83. package/vendor/ts-morph/docs/details/types.md +254 -0
  84. package/vendor/ts-morph/docs/details/variables.md +110 -0
  85. package/vendor/ts-morph/docs/emitting.md +151 -0
  86. package/vendor/ts-morph/docs/index.md +25 -0
  87. package/vendor/ts-morph/docs/manipulation/code-writer.md +20 -0
  88. package/vendor/ts-morph/docs/manipulation/formatting.md +76 -0
  89. package/vendor/ts-morph/docs/manipulation/index.md +136 -0
  90. package/vendor/ts-morph/docs/manipulation/order.md +14 -0
  91. package/vendor/ts-morph/docs/manipulation/performance.md +222 -0
  92. package/vendor/ts-morph/docs/manipulation/removing.md +31 -0
  93. package/vendor/ts-morph/docs/manipulation/renaming.md +106 -0
  94. package/vendor/ts-morph/docs/manipulation/settings.md +76 -0
  95. package/vendor/ts-morph/docs/manipulation/structures.md +117 -0
  96. package/vendor/ts-morph/docs/manipulation/transforms.md +84 -0
  97. package/vendor/ts-morph/docs/metrics/performance.json +4 -0
  98. package/vendor/ts-morph/docs/navigation/ambient-modules.md +22 -0
  99. package/vendor/ts-morph/docs/navigation/compiler-nodes.md +82 -0
  100. package/vendor/ts-morph/docs/navigation/directories.md +287 -0
  101. package/vendor/ts-morph/docs/navigation/example.md +50 -0
  102. package/vendor/ts-morph/docs/navigation/finding-references.md +53 -0
  103. package/vendor/ts-morph/docs/navigation/getting-source-files.md +59 -0
  104. package/vendor/ts-morph/docs/navigation/images/getChildrenVsForEachChild.gif +0 -0
  105. package/vendor/ts-morph/docs/navigation/index.md +94 -0
  106. package/vendor/ts-morph/docs/navigation/language-service.md +23 -0
  107. package/vendor/ts-morph/docs/navigation/program.md +25 -0
  108. package/vendor/ts-morph/docs/navigation/type-checker.md +33 -0
  109. package/vendor/ts-morph/docs/setup/adding-source-files.md +145 -0
  110. package/vendor/ts-morph/docs/setup/ast-viewers.md +46 -0
  111. package/vendor/ts-morph/docs/setup/diagnostics.md +109 -0
  112. package/vendor/ts-morph/docs/setup/file-system.md +106 -0
  113. package/vendor/ts-morph/docs/setup/images/atom-ast.png +0 -0
  114. package/vendor/ts-morph/docs/setup/images/atom-ast_small.png +0 -0
  115. package/vendor/ts-morph/docs/setup/images/atom-command-palette.png +0 -0
  116. package/vendor/ts-morph/docs/setup/images/atom-file.png +0 -0
  117. package/vendor/ts-morph/docs/setup/images/ts-ast-viewer.png +0 -0
  118. package/vendor/ts-morph/docs/setup/index.md +94 -0
  119. package/vendor/ts-morph/docs/utilities.md +55 -0
  120. package/vendor/ts-morph/dprint.json +23 -0
  121. package/vendor/ts-morph/package.json +30 -0
  122. package/vendor/ts-morph/packages/bootstrap/LICENSE +21 -0
  123. package/vendor/ts-morph/packages/bootstrap/lib/ts-morph-bootstrap.d.ts +397 -0
  124. package/vendor/ts-morph/packages/bootstrap/package.json +46 -0
  125. package/vendor/ts-morph/packages/bootstrap/readme.md +200 -0
  126. package/vendor/ts-morph/packages/common/LICENSE +21 -0
  127. package/vendor/ts-morph/packages/common/lib/ts-morph-common.d.ts +1082 -0
  128. package/vendor/ts-morph/packages/common/lib/typescript.d.ts +11439 -0
  129. package/vendor/ts-morph/packages/common/package.json +65 -0
  130. package/vendor/ts-morph/packages/common/readme.md +5 -0
  131. package/vendor/ts-morph/packages/scripts/changeTypeScriptVersion.ts +28 -0
  132. package/vendor/ts-morph/packages/scripts/createDeclarationProject.ts +47 -0
  133. package/vendor/ts-morph/packages/scripts/deps.ts +2 -0
  134. package/vendor/ts-morph/packages/scripts/execScript.ts +31 -0
  135. package/vendor/ts-morph/packages/scripts/folders.ts +11 -0
  136. package/vendor/ts-morph/packages/scripts/getDevCompilerVersions.ts +19 -0
  137. package/vendor/ts-morph/packages/scripts/mod.ts +7 -0
  138. package/vendor/ts-morph/packages/scripts/utils/Memoize.ts +36 -0
  139. package/vendor/ts-morph/packages/scripts/utils/forEachTypeText.ts +23 -0
  140. package/vendor/ts-morph/packages/scripts/utils/makeConstructorsPrivate.ts +26 -0
  141. package/vendor/ts-morph/packages/scripts/utils/mod.ts +4 -0
  142. package/vendor/ts-morph/packages/scripts/utils/printDiagnostics.ts +10 -0
  143. package/vendor/ts-morph/packages/ts-morph/LICENSE +21 -0
  144. package/vendor/ts-morph/packages/ts-morph/lib/ts-morph.d.ts +11198 -0
  145. package/vendor/ts-morph/packages/ts-morph/package.json +78 -0
  146. package/vendor/ts-morph/packages/ts-morph/readme.md +111 -0
  147. package/vendor/ts-morph/readme.md +14 -0
  148. package/vendor/ts-morph/rfcs/README.md +13 -0
  149. package/vendor/ts-morph/rfcs/RFC-0001 - Inserting Into Statements Handling Comments.md +181 -0
  150. package/vendor/ts-morph/tsconfig.common.json +17 -0
  151. package/vitest.config.ts +16 -0
@@ -0,0 +1,332 @@
1
+ /**
2
+ * Marker Definitions and Catalog
3
+ *
4
+ * This module defines the impurity marker types and provides a catalog
5
+ * of patterns for detecting each marker type. This is PURE code - all
6
+ * functions take data in and return data out with no I/O.
7
+ *
8
+ * The marker catalog is defined as a data structure to enable:
9
+ * - Extensibility (custom markers can be added)
10
+ * - Documentation (each marker has a description)
11
+ * - Testing (patterns can be validated in isolation)
12
+ */
13
+
14
+ import type { MarkerType } from '../types.js'
15
+
16
+ /**
17
+ * Marker definition with detection patterns
18
+ */
19
+ export type MarkerDefinition = {
20
+ type: MarkerType
21
+ description: string
22
+ category: 'io' | 'state' | 'side-effect' | 'environment'
23
+ severity: 'high' | 'medium' | 'low'
24
+ }
25
+
26
+ /**
27
+ * Database operations that indicate I/O
28
+ */
29
+ export const DATABASE_OPERATIONS = new Set([
30
+ 'findFirst',
31
+ 'findMany',
32
+ 'findUnique',
33
+ 'findUniqueOrThrow',
34
+ 'findFirstOrThrow',
35
+ 'create',
36
+ 'createMany',
37
+ 'update',
38
+ 'updateMany',
39
+ 'delete',
40
+ 'deleteMany',
41
+ 'upsert',
42
+ 'aggregate',
43
+ 'count',
44
+ 'groupBy',
45
+ '$transaction',
46
+ '$queryRaw',
47
+ '$executeRaw',
48
+ ])
49
+
50
+ /**
51
+ * Database client prefixes
52
+ */
53
+ export const DATABASE_PREFIXES = ['db', 'prisma', 'ctx.db', 'ctx.prisma']
54
+
55
+ /**
56
+ * File system modules that indicate I/O
57
+ */
58
+ export const FS_MODULES = new Set([
59
+ 'fs',
60
+ 'node:fs',
61
+ 'fs/promises',
62
+ 'node:fs/promises',
63
+ ])
64
+
65
+ /**
66
+ * HTTP client modules
67
+ */
68
+ export const HTTP_MODULES = new Set(['axios', 'node-fetch', 'got', 'ky'])
69
+
70
+ /**
71
+ * Console methods that indicate logging
72
+ */
73
+ export const CONSOLE_METHODS = new Set([
74
+ 'log',
75
+ 'error',
76
+ 'warn',
77
+ 'info',
78
+ 'debug',
79
+ 'trace',
80
+ ])
81
+
82
+ /**
83
+ * Analytics/telemetry function patterns
84
+ */
85
+ export const TELEMETRY_PATTERNS = [
86
+ /^track[A-Z]/,
87
+ /^analytics\./,
88
+ /^segment\./,
89
+ /^posthog\./,
90
+ /^mixpanel\./,
91
+ /^amplitude\./,
92
+ ]
93
+
94
+ /**
95
+ * Marker catalog - all recognized impurity markers
96
+ */
97
+ export const MARKER_CATALOG: Record<MarkerType, MarkerDefinition> = {
98
+ 'await-expression': {
99
+ type: 'await-expression',
100
+ description: 'Await expression indicates I/O suspension point',
101
+ category: 'io',
102
+ severity: 'high',
103
+ },
104
+ 'database-call': {
105
+ type: 'database-call',
106
+ description: 'Database operation (Prisma/similar ORM call)',
107
+ category: 'io',
108
+ severity: 'high',
109
+ },
110
+ 'network-fetch': {
111
+ type: 'network-fetch',
112
+ description: 'Native fetch() call for HTTP requests',
113
+ category: 'io',
114
+ severity: 'high',
115
+ },
116
+ 'network-http': {
117
+ type: 'network-http',
118
+ description: 'HTTP client library call (axios, got, etc.)',
119
+ category: 'io',
120
+ severity: 'high',
121
+ },
122
+ 'fs-import': {
123
+ type: 'fs-import',
124
+ description: 'Import from file system module',
125
+ category: 'io',
126
+ severity: 'medium',
127
+ },
128
+ 'fs-call': {
129
+ type: 'fs-call',
130
+ description: 'File system operation call',
131
+ category: 'io',
132
+ severity: 'high',
133
+ },
134
+ 'env-access': {
135
+ type: 'env-access',
136
+ description: 'Access to process.env environment variables',
137
+ category: 'environment',
138
+ severity: 'medium',
139
+ },
140
+ 'console-log': {
141
+ type: 'console-log',
142
+ description: 'Console logging call',
143
+ category: 'side-effect',
144
+ severity: 'low',
145
+ },
146
+ logging: {
147
+ type: 'logging',
148
+ description: 'Logger service call',
149
+ category: 'side-effect',
150
+ severity: 'low',
151
+ },
152
+ telemetry: {
153
+ type: 'telemetry',
154
+ description: 'Analytics/tracking/telemetry call',
155
+ category: 'side-effect',
156
+ severity: 'low',
157
+ },
158
+ 'queue-enqueue': {
159
+ type: 'queue-enqueue',
160
+ description: 'Message queue enqueue operation',
161
+ category: 'io',
162
+ severity: 'high',
163
+ },
164
+ 'event-emit': {
165
+ type: 'event-emit',
166
+ description: 'Event emitter emit call',
167
+ category: 'side-effect',
168
+ severity: 'medium',
169
+ },
170
+ }
171
+
172
+ /**
173
+ * Get marker definition by type
174
+ */
175
+ export function getMarkerDefinition(type: MarkerType): MarkerDefinition {
176
+ return MARKER_CATALOG[type]
177
+ }
178
+
179
+ /**
180
+ * Get all marker types
181
+ */
182
+ export function getAllMarkerTypes(): MarkerType[] {
183
+ return Object.keys(MARKER_CATALOG) as MarkerType[]
184
+ }
185
+
186
+ /**
187
+ * Get markers by category
188
+ */
189
+ export function getMarkersByCategory(
190
+ category: MarkerDefinition['category'],
191
+ ): MarkerDefinition[] {
192
+ return Object.values(MARKER_CATALOG).filter(m => m.category === category)
193
+ }
194
+
195
+ /**
196
+ * Get markers by severity
197
+ */
198
+ export function getMarkersBySeverity(
199
+ severity: MarkerDefinition['severity'],
200
+ ): MarkerDefinition[] {
201
+ return Object.values(MARKER_CATALOG).filter(m => m.severity === severity)
202
+ }
203
+
204
+ /**
205
+ * Check if a call expression matches a database operation pattern
206
+ * e.g., "db.user.findFirst", "prisma.user.create", "ctx.db.user.update"
207
+ */
208
+ export function isDatabaseCall(expression: string): boolean {
209
+ // Check if expression starts with a known database prefix
210
+ const lowerExpr = expression.toLowerCase()
211
+
212
+ for (const prefix of DATABASE_PREFIXES) {
213
+ if (lowerExpr.startsWith(prefix + '.')) {
214
+ // Extract the operation (last part of the chain)
215
+ const parts = expression.split('.')
216
+ const operation = parts[parts.length - 1]
217
+ if (operation && DATABASE_OPERATIONS.has(operation)) {
218
+ return true
219
+ }
220
+ }
221
+ }
222
+
223
+ return false
224
+ }
225
+
226
+ /**
227
+ * Check if a call expression is a fetch call
228
+ */
229
+ export function isFetchCall(expression: string): boolean {
230
+ return expression === 'fetch' || expression.endsWith('.fetch')
231
+ }
232
+
233
+ /**
234
+ * Check if a call expression is an HTTP client call
235
+ */
236
+ export function isHttpClientCall(
237
+ expression: string,
238
+ importedModules: Set<string>,
239
+ ): boolean {
240
+ // Check if axios or similar is imported
241
+ for (const module of HTTP_MODULES) {
242
+ if (importedModules.has(module)) {
243
+ // Check for axios.get, axios.post, etc.
244
+ if (
245
+ expression.startsWith('axios.') ||
246
+ expression === 'axios' ||
247
+ expression.startsWith('got.') ||
248
+ expression === 'got' ||
249
+ expression.startsWith('ky.')
250
+ ) {
251
+ return true
252
+ }
253
+ }
254
+ }
255
+
256
+ return false
257
+ }
258
+
259
+ /**
260
+ * Check if a call expression is a file system call
261
+ */
262
+ export function isFsCall(expression: string): boolean {
263
+ return (
264
+ expression.startsWith('fs.') ||
265
+ expression.startsWith('fsPromises.') ||
266
+ expression.startsWith('fsp.')
267
+ )
268
+ }
269
+
270
+ /**
271
+ * Check if a module specifier is a file system module
272
+ */
273
+ export function isFsModule(moduleSpecifier: string): boolean {
274
+ return FS_MODULES.has(moduleSpecifier)
275
+ }
276
+
277
+ /**
278
+ * Check if a property access is an environment variable access
279
+ */
280
+ export function isEnvAccess(propertyChain: string): boolean {
281
+ return (
282
+ propertyChain.startsWith('process.env.') || propertyChain === 'process.env'
283
+ )
284
+ }
285
+
286
+ /**
287
+ * Check if a call expression is a console log call
288
+ */
289
+ export function isConsoleCall(expression: string): boolean {
290
+ if (!expression.startsWith('console.')) return false
291
+
292
+ const method = expression.slice('console.'.length)
293
+ return CONSOLE_METHODS.has(method)
294
+ }
295
+
296
+ /**
297
+ * Check if a call expression is a logging call
298
+ */
299
+ export function isLoggingCall(expression: string): boolean {
300
+ return (
301
+ expression.startsWith('logger.') ||
302
+ expression.startsWith('log.') ||
303
+ expression === 'log' ||
304
+ expression.startsWith('Logger.')
305
+ )
306
+ }
307
+
308
+ /**
309
+ * Check if a call expression is a telemetry/analytics call
310
+ */
311
+ export function isTelemetryCall(expression: string): boolean {
312
+ for (const pattern of TELEMETRY_PATTERNS) {
313
+ if (pattern.test(expression)) {
314
+ return true
315
+ }
316
+ }
317
+ return false
318
+ }
319
+
320
+ /**
321
+ * Check if a call expression is a queue enqueue call
322
+ */
323
+ export function isQueueEnqueueCall(expression: string): boolean {
324
+ return expression.endsWith('.enqueue') || expression.endsWith('.add')
325
+ }
326
+
327
+ /**
328
+ * Check if a call expression is an event emit call
329
+ */
330
+ export function isEventEmitCall(expression: string): boolean {
331
+ return expression.endsWith('.emit') || expression.endsWith('.dispatch')
332
+ }