@filipebraida/adonis-function-points 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 (80) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +427 -0
  3. package/bin/cli.js +4 -0
  4. package/build/commands/fp_calibrate.d.ts +16 -0
  5. package/build/commands/fp_count.d.ts +11 -0
  6. package/build/commands/fp_diff.d.ts +16 -0
  7. package/build/commands/fp_explain.d.ts +15 -0
  8. package/build/commands/fp_inventory.d.ts +9 -0
  9. package/build/commands/main.d.ts +5 -0
  10. package/build/commands/main.js +120 -0
  11. package/build/commands/printer.d.ts +9 -0
  12. package/build/configure.d.ts +2 -0
  13. package/build/configure.js +10 -0
  14. package/build/define_config-DOqWyPwV.js +19 -0
  15. package/build/index.d.ts +5 -0
  16. package/build/index.js +4 -0
  17. package/build/pipeline-BzP-ITGN.js +2306 -0
  18. package/build/resolvers-CU9HKYpn.js +555 -0
  19. package/build/runners-Bt8tbISi.js +630 -0
  20. package/build/scripts/smoke_package.d.ts +1 -0
  21. package/build/src/albrecht/calibration.d.ts +62 -0
  22. package/build/src/albrecht/counter.d.ts +48 -0
  23. package/build/src/albrecht/data_functions.d.ts +32 -0
  24. package/build/src/albrecht/diff.d.ts +66 -0
  25. package/build/src/albrecht/index.d.ts +13 -0
  26. package/build/src/albrecht/tables.d.ts +19 -0
  27. package/build/src/albrecht/technical_filter.d.ts +25 -0
  28. package/build/src/albrecht/transactional_functions.d.ts +35 -0
  29. package/build/src/cli/load_config.d.ts +28 -0
  30. package/build/src/cli/print.d.ts +19 -0
  31. package/build/src/cli/runners.d.ts +52 -0
  32. package/build/src/cli.d.ts +19 -0
  33. package/build/src/cli.js +198 -0
  34. package/build/src/define_config.d.ts +137 -0
  35. package/build/src/inventory/app_context.d.ts +73 -0
  36. package/build/src/inventory/detectors/lucid.d.ts +77 -0
  37. package/build/src/inventory/graph/call_graph.d.ts +80 -0
  38. package/build/src/inventory/graph/noise.d.ts +9 -0
  39. package/build/src/inventory/index.d.ts +15 -0
  40. package/build/src/inventory/paths.d.ts +22 -0
  41. package/build/src/inventory/resolvers/action_object.d.ts +14 -0
  42. package/build/src/inventory/resolvers/index.d.ts +23 -0
  43. package/build/src/inventory/resolvers/index.js +2 -0
  44. package/build/src/inventory/resolvers/job_dispatch.d.ts +18 -0
  45. package/build/src/inventory/resolvers/module_function.d.ts +11 -0
  46. package/build/src/inventory/resolvers/property_service.d.ts +18 -0
  47. package/build/src/inventory/resolvers/same_class_method.d.ts +17 -0
  48. package/build/src/inventory/resolvers/static_service.d.ts +13 -0
  49. package/build/src/inventory/resolvers/transformer.d.ts +25 -0
  50. package/build/src/inventory/resolvers/types.d.ts +65 -0
  51. package/build/src/inventory/source.d.ts +39 -0
  52. package/build/src/inventory/sources/data_stores.d.ts +31 -0
  53. package/build/src/inventory/sources/json_schemas.d.ts +32 -0
  54. package/build/src/inventory/sources/routes_ast.d.ts +28 -0
  55. package/build/src/metrics/structure.d.ts +72 -0
  56. package/build/src/pipeline.d.ts +44 -0
  57. package/build/src/pipeline.js +2 -0
  58. package/build/src/reporters/table.d.ts +6 -0
  59. package/build/src/types.d.ts +256 -0
  60. package/build/src/types.js +1 -0
  61. package/build/stubs/config.stub +37 -0
  62. package/build/tmp/probe.d.ts +1 -0
  63. package/build/tmp/probe_cli.d.ts +1 -0
  64. package/build/tmp/probe_cmp.d.ts +1 -0
  65. package/build/tmp/probe_count.d.ts +1 -0
  66. package/build/tmp/probe_data.d.ts +1 -0
  67. package/build/tmp/probe_diff.d.ts +1 -0
  68. package/build/tmp/probe_gap.d.ts +1 -0
  69. package/build/tmp/probe_graph.d.ts +1 -0
  70. package/build/tmp/probe_metrics.d.ts +1 -0
  71. package/build/tmp/probe_miss.d.ts +1 -0
  72. package/build/tmp/probe_names.d.ts +1 -0
  73. package/build/tmp/probe_nodata.d.ts +1 -0
  74. package/build/tmp/probe_one.d.ts +1 -0
  75. package/build/tmp/probe_perf.d.ts +1 -0
  76. package/build/tmp/probe_routes.d.ts +1 -0
  77. package/build/tmp/probe_unres.d.ts +1 -0
  78. package/build/tmp/probe_vazquez.d.ts +1 -0
  79. package/build/tsdown.config.d.ts +2 -0
  80. package/package.json +133 -0
@@ -0,0 +1,39 @@
1
+ /**
2
+ * What a count counted.
3
+ *
4
+ * A saved count used to say which RULESET produced it and nothing about its
5
+ * subject — no revision, no branch, no date. Two such files compare cleanly and
6
+ * mean nothing: the same application a month apart, or two different
7
+ * applications, are indistinguishable once the number is on an invoice.
8
+ *
9
+ * This is not hypothetical. A production application here moved from 714 to 768
10
+ * points, and the first explanation reached for was a defect in the counter.
11
+ * It was a month of development: the counter returned identical totals across
12
+ * twelve of its own commits. Half a day answering a question the artefact
13
+ * should have answered itself.
14
+ */
15
+ export type CountSource = {
16
+ /**
17
+ * Identity of the application counted.
18
+ *
19
+ * The package.json name when there is one, the directory name otherwise —
20
+ * never the absolute path, which says where the machine keeps its files and
21
+ * travels with every count sent anywhere.
22
+ */
23
+ app: string;
24
+ /** commit counted, when the root is a git repository */
25
+ revision?: string;
26
+ branch?: string;
27
+ /**
28
+ * Uncommitted changes were present.
29
+ *
30
+ * The field that matters most in billing: a count taken over a dirty tree
31
+ * cannot be reproduced from any revision, and whoever receives the invoice is
32
+ * entitled to know that before paying it.
33
+ */
34
+ dirty?: boolean;
35
+ countedAt: string;
36
+ /** configuration file that shaped the count, or null for the defaults */
37
+ config: string | null;
38
+ };
39
+ export declare function describeSource(root: string, config: string | null): CountSource;
@@ -0,0 +1,31 @@
1
+ import type { AppContext } from '../app_context.js';
2
+ import type { DataStore, UnresolvedCall } from '../../types.js';
3
+ /**
4
+ * Collects the logical data stores — ILF/EIF candidates.
5
+ *
6
+ * The failure this module exists to make impossible: an application can hold
7
+ * dozens of model files and ZERO `extends BaseModel` from Lucid, because the
8
+ * models extend schema classes generated from migrations. Reading only the
9
+ * model file would count zero for the whole application, **in silence**.
10
+ *
11
+ * The unit of work is therefore the INHERITANCE CHAIN, not the file:
12
+ *
13
+ * class User extends BaseModel (direct)
14
+ * class User extends UserSchema (generated schema)
15
+ * class User extends compose(UserSchema, Auditable) (mixin)
16
+ *
17
+ * Where the chain leaves the application — a mixin coming from a package —
18
+ * collection stops and **reports**. A soft-delete mixin adds `deletedAt`;
19
+ * pretending it does not exist would be counting wrong without warning.
20
+ */
21
+ export type ColumnSource = 'ast' | 'generated-schema';
22
+ export type CollectedDataStore = DataStore & {
23
+ /** where the columns came from; counts from different sources are not equivalent */
24
+ columnSource: ColumnSource;
25
+ };
26
+ export type DataStoreCollection = {
27
+ stores: CollectedDataStore[];
28
+ /** chains that left the application, required by AFP §6.5.3 */
29
+ unresolved: UnresolvedCall[];
30
+ };
31
+ export declare function collectDataStores(app: AppContext): Promise<DataStoreCollection>;
@@ -0,0 +1,32 @@
1
+ import type { AppContext } from '../app_context.js';
2
+ import type { Provenance } from '../../types.js';
3
+ /**
4
+ * JSON Schema literals declared in the application's own code.
5
+ *
6
+ * Some applications store what the user fills as data — a form definition in a
7
+ * JSON column — which counting-decisions §8 says counts as 1 DET, because the
8
+ * schema is runtime data. That is true of the row in the database and false of
9
+ * the literal that seeded it: when the schema is an object literal in a seeder,
10
+ * it is code, and ts-morph reads it.
11
+ *
12
+ * This matters more than the number it yields. A hand-declared DET count
13
+ * freezes: someone adds a field, the count does not move, and `fp:diff` reports
14
+ * no change for real functional growth — undercounting silently and
15
+ * progressively, which is worse than undercounting once. Reading the schema
16
+ * keeps the number coming from the code, which is the property the whole package
17
+ * rests on.
18
+ *
19
+ * The leaf rules are §7's, unchanged — the same ones applied to VineJS
20
+ * validators. Only the recognition differs: `properties` and `items` instead of
21
+ * `vine.object` and `vine.array`.
22
+ */
23
+ export type DiscoveredSchema = {
24
+ /** the declared name, which is what a config override refers to */
25
+ name: string;
26
+ /** user-recognisable fields, by the §7 leaf rules */
27
+ fields: number;
28
+ /** the leaf paths, so `fp:explain` can show where the number came from */
29
+ leaves: string[];
30
+ provenance: Provenance;
31
+ };
32
+ export declare function collectJsonSchemas(app: AppContext): Map<string, DiscoveredSchema>;
@@ -0,0 +1,28 @@
1
+ import type { AppContext } from '../app_context.js';
2
+ import type { EntryPoint, UnresolvedCall } from '../../types.js';
3
+ /**
4
+ * Reads the HTTP entry points from the AST of the route files.
5
+ *
6
+ * On v7 the runtime is the primary source (`router.toJSON()` after boot), but
7
+ * this parser is what has to work first: fixtures do not boot, and the golden
8
+ * invariant depends on an application with no generated artefact at all.
9
+ *
10
+ * Traps, each one covered by a regression test:
11
+ *
12
+ * router multi-line route: the expression text contains the line
13
+ * .post(...) break, and without normalising it most routes never match
14
+ *
15
+ * .resource(p, C) expands to up to 7 routes, filtered by .only()/.apiOnly()
16
+ * .group().prefix() the prefix must reach the final pattern, and accumulates
17
+ * controllers.a.B the bare name collides across modules: resolve by path
18
+ * router.on(p) entry point with no handler at all
19
+ */
20
+ export type CollectedEntryPoint = EntryPoint & {
21
+ /** key that stays stable across versions — counting-decisions §5 */
22
+ identity: string;
23
+ };
24
+ export type EntryPointCollection = {
25
+ entryPoints: CollectedEntryPoint[];
26
+ unresolved: UnresolvedCall[];
27
+ };
28
+ export declare function collectEntryPoints(app: AppContext): Promise<EntryPointCollection>;
@@ -0,0 +1,72 @@
1
+ import type { Inventory } from '../types.js';
2
+ import type { CountResult } from '../types.js';
3
+ /**
4
+ * Structural and density metrics, derived from the SAME inventory.
5
+ *
6
+ * Nothing here collects a new fact: once the graph knows which transactions
7
+ * reach which stores and which modules they pass through, coupling and density
8
+ * are arithmetic over that. It is also why the inventory layer knows nothing of
9
+ * FPA.
10
+ *
11
+ * Why they belong beside function points: if function points pay, the team
12
+ * optimises function points — more models, more endpoints, less reuse. Density
13
+ * and coupling on the same dashboard are the counterweight; without them the
14
+ * measure becomes a target.
15
+ */
16
+ export type ModuleMetrics = {
17
+ module: string;
18
+ functionPoints: number;
19
+ /** transactions this module exposes */
20
+ transactions: number;
21
+ /** data stores this module declares */
22
+ dataStores: number;
23
+ /** modules this one depends on: its transactions reach their data */
24
+ dependsOn: string[];
25
+ /** modules that depend on this one */
26
+ dependedOnBy: string[];
27
+ /**
28
+ * Martin's instability: `Ce / (Ca + Ce)`.
29
+ *
30
+ * 0 means stable (everyone depends on it, it depends on nobody); 1 means
31
+ * unstable. A stable module that changes often is where change hurts.
32
+ */
33
+ instability: number;
34
+ };
35
+ export type StructureMetrics = {
36
+ modules: ModuleMetrics[];
37
+ /** module pairs with mutual dependency — cycle candidates */
38
+ mutualDependencies: [string, string][];
39
+ /** function points per data store: functional density */
40
+ pointsPerDataStore: number;
41
+ /** transactions per store: how much each entity is exercised */
42
+ transactionsPerDataStore: number;
43
+ };
44
+ export declare function measureStructure(inventory: Inventory, count: CountResult): StructureMetrics;
45
+ /**
46
+ * Conformance to the application's own conventions.
47
+ *
48
+ * Measures neither size nor quality: it measures whether the team follows what
49
+ * it agreed on. It comes free from the inventory, and in a software factory it
50
+ * is what turns into a standards audit.
51
+ */
52
+ export type Conformance = {
53
+ /** write transactions whose input fields come from a validator */
54
+ writesWithValidator: {
55
+ ok: number;
56
+ total: number;
57
+ ratio: number;
58
+ };
59
+ /** entry points whose handler was resolved */
60
+ entryPointsWithHandler: {
61
+ ok: number;
62
+ total: number;
63
+ ratio: number;
64
+ };
65
+ /** data stores reached by at least one transaction */
66
+ dataStoresReached: {
67
+ ok: number;
68
+ total: number;
69
+ ratio: number;
70
+ };
71
+ };
72
+ export declare function measureConformance(inventory: Inventory): Conformance;
@@ -0,0 +1,44 @@
1
+ import type { CountOptions } from './albrecht/counter.js';
2
+ import type { CallResolver } from './inventory/resolvers/types.js';
3
+ import type { CountResult, Inventory } from './types.js';
4
+ /**
5
+ * The whole pipeline, in one place.
6
+ *
7
+ * Ace commands stay thin on purpose: they hold no logic and only print what
8
+ * this module returns. That is what makes it possible to test counting without
9
+ * booting an AdonisJS application — fixtures do not boot.
10
+ *
11
+ * The order is not arbitrary. Data stores come before any handler analysis,
12
+ * because `Model.create()` and `Service.create()` are indistinguishable by
13
+ * shape; and counting comes last, because ILF vs EIF depends on HOW
14
+ * transactions use each store (AFP §6.5.4).
15
+ */
16
+ export type AnalysisOptions = CountOptions & {
17
+ /** call-graph depth from the handler */
18
+ maxDepth?: number;
19
+ /** custom tracing strategies, running before the built-in ones */
20
+ resolvers?: {
21
+ call?: CallResolver[];
22
+ };
23
+ /**
24
+ * Minimum tracing coverage. Below it the analysis fails rather than emitting
25
+ * a number that looks right.
26
+ */
27
+ minCoverage?: number;
28
+ /**
29
+ * Configuration file that produced these options, recorded in the count's
30
+ * `source`. The pipeline does not read it — the front-ends do — but the
31
+ * artefact has to say which configuration shaped the number.
32
+ */
33
+ configFile?: string | null;
34
+ };
35
+ export type Analysis = {
36
+ inventory: Inventory;
37
+ count: CountResult;
38
+ };
39
+ export declare class CoverageTooLowError extends Error {
40
+ readonly ratio: number;
41
+ readonly minimum: number;
42
+ constructor(ratio: number, minimum: number);
43
+ }
44
+ export declare function analyze(root: string, options?: AnalysisOptions): Promise<Analysis>;
@@ -0,0 +1,2 @@
1
+ import { n as analyze, t as CoverageTooLowError } from "../pipeline-BzP-ITGN.js";
2
+ export { CoverageTooLowError, analyze };
@@ -0,0 +1,6 @@
1
+ import type { CountResult, CountedFunction } from '../types.js';
2
+ import type { FunctionPointDiff } from '../albrecht/diff.js';
3
+ export declare function renderCount(result: CountResult): string;
4
+ /** `fp:explain`: a function's provenance, which is what supports a dispute */
5
+ export declare function renderExplain(fn: CountedFunction): string;
6
+ export declare function renderDiff(diff: FunctionPointDiff): string;
@@ -0,0 +1,256 @@
1
+ /**
2
+ * Domain model of the package.
3
+ *
4
+ * Two deliberately independent layers:
5
+ *
6
+ * Inventory — raw facts extracted from the application. Knows nothing of FPA.
7
+ * Albrecht — IFPUG/AFP rules applied over the inventory.
8
+ *
9
+ * `src/inventory/**` must never import from `src/albrecht/**`.
10
+ */
11
+ export type Provenance = {
12
+ file: string;
13
+ line?: number;
14
+ /** name of the collector/resolver/detector that produced the fact */
15
+ by: string;
16
+ };
17
+ export type { CountSource } from './inventory/source.js';
18
+ import type { CountSource } from './inventory/source.js';
19
+ /** A logical data store persisted by the application. */
20
+ export type DataStore = {
21
+ id: string;
22
+ /** name as the user would recognise it (model, table) */
23
+ name: string;
24
+ module: string;
25
+ /** physical table, when known */
26
+ table?: string;
27
+ /** persisted attributes, excluding technical identifiers */
28
+ attributes: Attribute[];
29
+ /** candidate logical subgroups (composition relations) */
30
+ subgroups: string[];
31
+ /**
32
+ * Declared relations: property name -> target store name.
33
+ *
34
+ * This is what allows `.preload('author')` to resolve to the `Author` store.
35
+ * Without it, a table read only through a relation is reached by no
36
+ * transaction and drops out of the count under AFP §6.5.4 — when it is in
37
+ * fact a legitimate EIF.
38
+ */
39
+ relations: Record<string, string>;
40
+ /** maintained by this application, or by an external system? */
41
+ maintainedExternally: boolean;
42
+ provenance: Provenance;
43
+ };
44
+ export type Attribute = {
45
+ name: string;
46
+ type?: string;
47
+ isIdentifier: boolean;
48
+ provenance: Provenance;
49
+ };
50
+ /**
51
+ * An application entry point: HTTP route, ace command, job, listener.
52
+ * Transactions are not only HTTP — a scheduled command that imports a file is
53
+ * as transactional as a POST.
54
+ */
55
+ export type EntryPoint = {
56
+ id: string;
57
+ kind: 'http' | 'command' | 'job' | 'listener';
58
+ module: string;
59
+ /** HTTP verb, command name, event name… */
60
+ trigger: string;
61
+ /** route pattern, command signature… */
62
+ signature: string;
63
+ /** route name when present; not the identity used across versions */
64
+ name?: string;
65
+ handler: HandlerRef | null;
66
+ provenance: Provenance;
67
+ };
68
+ export type HandlerRef = {
69
+ file: string;
70
+ /** class method; absent for single-action handlers */
71
+ member?: string;
72
+ /**
73
+ * Body line, for a handler with no name: an inline closure declared on the
74
+ * route itself (`router.get('/', ({ response }) => …)`).
75
+ *
76
+ * Its body is business code like any other and must be walked by the graph.
77
+ */
78
+ line?: number;
79
+ };
80
+ /** What the code reachable from an EntryPoint actually does. */
81
+ export type HandlerBehavior = {
82
+ entryPointId: string;
83
+ /** does it write to any DataStore inside the boundary? */
84
+ writes: boolean;
85
+ /** DataStores reached (ids) */
86
+ touches: string[];
87
+ /** declared input fields (validators) */
88
+ inputFields: Field[];
89
+ /** declared output fields (transformers, DTOs) */
90
+ outputFields: Field[];
91
+ /** path walked through the call graph — what `fp:explain` prints */
92
+ trace: TraceStep[];
93
+ /** calls no resolver knew how to follow */
94
+ unresolved: UnresolvedCall[];
95
+ };
96
+ export type Field = {
97
+ name: string;
98
+ optional?: boolean;
99
+ provenance: Provenance;
100
+ };
101
+ export type TraceStep = {
102
+ file: string;
103
+ member?: string;
104
+ depth: number;
105
+ /** resolver that produced this step */
106
+ by: string;
107
+ writes: boolean;
108
+ };
109
+ /**
110
+ * A call the graph could not follow.
111
+ *
112
+ * This is NOT log noise — it is the inventory's coverage metric. Many
113
+ * unresolved calls mean an unreliable count, and the report must say so rather
114
+ * than feign precision.
115
+ */
116
+ export type UnresolvedCall = {
117
+ file: string;
118
+ line: number;
119
+ expression: string;
120
+ reason: string;
121
+ };
122
+ export type Inventory = {
123
+ /** format version, for diffs across releases */
124
+ version: 1;
125
+ generatedAt: string;
126
+ app: string;
127
+ /** framework versions the count was made against — goes into the report */
128
+ framework: {
129
+ core?: number;
130
+ lucid?: number;
131
+ orm: string;
132
+ };
133
+ dataStores: DataStore[];
134
+ entryPoints: EntryPoint[];
135
+ behaviors: HandlerBehavior[];
136
+ coverage: {
137
+ entryPointsTotal: number;
138
+ entryPointsResolved: number;
139
+ unresolvedCalls: number;
140
+ /** fraction of entry points whose handler was traced to completion */
141
+ ratio: number;
142
+ };
143
+ };
144
+ export type FunctionType = 'ILF' | 'EIF' | 'EI' | 'EO' | 'EQ';
145
+ export type Complexity = 'low' | 'average' | 'high';
146
+ export type CountedFunction = {
147
+ id: string;
148
+ name: string;
149
+ module: string;
150
+ type: FunctionType;
151
+ /** DET — data element types */
152
+ det: number;
153
+ /** RET for data functions, FTR for transactional ones */
154
+ refs: number;
155
+ complexity: Complexity;
156
+ points: number;
157
+ /**
158
+ * Hash of the implementation scope.
159
+ *
160
+ * This is what `fp:diff` compares to decide whether a function CHANGED. It
161
+ * combines normalised-AST hashes of the bodies reached, so formatting and
162
+ * comments are excluded: running a formatter must not produce an invoice.
163
+ *
164
+ * Absent for data functions, whose scope is the declaration itself.
165
+ */
166
+ scopeHash?: string;
167
+ /** why it was classified this way — feeds `fp:explain` */
168
+ rationale: Rationale;
169
+ };
170
+ export type Rationale = {
171
+ /** rule applied, e.g. 'afp:6.5.3 modifies a data store -> EI' */
172
+ rule: string;
173
+ /** where each DET came from */
174
+ detSources: string[];
175
+ /** where the FTR/RET came from */
176
+ refSources: string[];
177
+ /**
178
+ * Manual overrides applied via config, with the required justification.
179
+ *
180
+ * `fields` says which numbers the override actually replaced, so the report
181
+ * can mark the DET line and leave RET alone rather than implying both came
182
+ * from a person.
183
+ */
184
+ overrides?: {
185
+ reason: string;
186
+ by: string;
187
+ fields: ('det' | 'refs')[];
188
+ }[];
189
+ trace?: TraceStep[];
190
+ };
191
+ export type CountResult = {
192
+ /** identifies the rule set — counts are comparable only if these match */
193
+ ruleset: string;
194
+ rulesetVersion: string;
195
+ /**
196
+ * What was counted: application, revision, whether the tree was dirty.
197
+ *
198
+ * Optional because `count()` can be called directly with an inventory built
199
+ * by hand; every count the CLI writes carries it. Without it two saved counts
200
+ * are comparable in form and meaningless in substance — the same application
201
+ * a month apart looks exactly like a defect in the counter.
202
+ */
203
+ source?: CountSource;
204
+ functions: CountedFunction[];
205
+ totals: {
206
+ unadjusted: number;
207
+ byType: Record<FunctionType, {
208
+ count: number;
209
+ points: number;
210
+ }>;
211
+ byModule: Record<string, number>;
212
+ };
213
+ /** flags when the count does not deserve confidence */
214
+ confidence: {
215
+ unresolvedCalls: number;
216
+ entryPointsWithoutHandler: number;
217
+ warnings: string[];
218
+ };
219
+ };
220
+ /** Maintenance type, for enhancement-project counting. */
221
+ export type ChangeType = 'added' | 'changed' | 'removed' | 'unchanged';
222
+ /**
223
+ * Why a function counts as changed.
224
+ *
225
+ * The three causes used to collapse into one boolean, so a pure refactor and a
226
+ * genuine growth in functional size were indistinguishable in the report and
227
+ * priced identically. AEP does not ask for that collapse — it treats a type
228
+ * change separately and grades modification by effort — and a factory pricing
229
+ * a month of work needs to see which of the three it is paying for.
230
+ *
231
+ * type reclassified: ILF <-> EIF, EI <-> EO
232
+ * size DET or FTR moved, so the functional size really changed
233
+ * implementation same size, different code — refactoring
234
+ */
235
+ export type ChangeReason = 'type' | 'size' | 'implementation';
236
+ export type DiffEntry = {
237
+ function: CountedFunction;
238
+ change: ChangeType;
239
+ previous?: CountedFunction;
240
+ /** present only when `change` is 'changed' */
241
+ reason?: ChangeReason;
242
+ };
243
+ export type DiffResult = {
244
+ from: string;
245
+ to: string;
246
+ entries: DiffEntry[];
247
+ totals: Record<ChangeType, {
248
+ count: number;
249
+ points: number;
250
+ }>;
251
+ /** the `changed` total split by cause — where an invoice actually comes from */
252
+ changedByReason: Record<ChangeReason, {
253
+ count: number;
254
+ points: number;
255
+ }>;
256
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,37 @@
1
+ {{{
2
+ exports({ to: app.configPath('function_points.ts') })
3
+ }}}
4
+ import { defineConfig } from '@filipebraida/adonis-function-points'
5
+
6
+ export default defineConfig({
7
+ /**
8
+ * The application boundary is a business decision, not a technical one.
9
+ * Review it with whoever signs the contract, not only with the team.
10
+ */
11
+ boundary: {
12
+ infrastructure: ['access_tokens', 'reset_password_tokens', 'audits'],
13
+ ignoreEntryPoints: ['drive.fs.serve', 'prometheus.metrics'],
14
+ externallyMaintained: [],
15
+ },
16
+
17
+ /** Logical subgroups (RET). `constant` pins it at 1, which is honest. */
18
+ retStrategy: 'constant',
19
+
20
+ /** How far to follow the call graph from the handler. */
21
+ maxDepth: 3,
22
+
23
+ /** Refuses to emit a count if tracing covers less than this. */
24
+ minCoverage: 0.85,
25
+
26
+ /**
27
+ * Facts static analysis cannot read, declared with a required justification.
28
+ *
29
+ * The case this exists for: fields the user fills that live in a JSON column
30
+ * whose schema is stored in the database. See counting-decisions §8 — and use
31
+ * it sparingly, because `fp:count` reports what share of the total came from
32
+ * here.
33
+ */
34
+ // overrides: {
35
+ // 'POST /petitions': { det: 42, reason: 'JSON Schema form; 42 user fields' },
36
+ // },
37
+ })
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ declare const _default: import("tsdown").UserConfig;
2
+ export default _default;