@filipebraida/adonis-function-points 0.1.0 → 0.2.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 (54) hide show
  1. package/CHANGELOG.md +99 -0
  2. package/README.md +103 -2
  3. package/build/commands/fp_metrics.d.ts +10 -0
  4. package/build/commands/main.d.ts +6 -5
  5. package/build/commands/main.js +48 -114
  6. package/build/decorate-D6enDn9D.js +24 -0
  7. package/build/fp_calibrate-Cm079xWL.js +25 -0
  8. package/build/fp_count-CfcXPuj5.js +22 -0
  9. package/build/fp_diff-DE_t3twv.js +25 -0
  10. package/build/fp_explain-MKyEoi0h.js +24 -0
  11. package/build/fp_inventory-DSrCVhEy.js +18 -0
  12. package/build/fp_metrics-BUWj9dLw.js +20 -0
  13. package/build/index.d.ts +2 -1
  14. package/build/index.js +3 -2
  15. package/build/{pipeline-BzP-ITGN.js → pipeline-DySlMWcN.js} +298 -40
  16. package/build/{resolvers-CU9HKYpn.js → resolvers-MFjRl2ef.js} +225 -5
  17. package/build/{runners-Bt8tbISi.js → runners-DpMd-yZM.js} +252 -64
  18. package/build/src/albrecht/counter.d.ts +17 -1
  19. package/build/src/albrecht/data_functions.d.ts +6 -0
  20. package/build/src/albrecht/diff.d.ts +19 -1
  21. package/build/src/cli/runners.d.ts +12 -0
  22. package/build/src/cli.js +11 -2
  23. package/build/src/define_config.d.ts +35 -1
  24. package/build/src/inventory/graph/call_graph.d.ts +27 -0
  25. package/build/src/inventory/graph/noise.d.ts +13 -0
  26. package/build/src/inventory/resolvers/event_dispatch.d.ts +17 -0
  27. package/build/src/inventory/resolvers/index.js +1 -1
  28. package/build/src/inventory/resolvers/types.d.ts +35 -0
  29. package/build/src/inventory/sources/event_bindings.d.ts +44 -0
  30. package/build/src/metrics/structure.d.ts +16 -2
  31. package/build/src/pipeline.js +1 -1
  32. package/build/src/reporters/table.d.ts +11 -1
  33. package/build/src/types.d.ts +10 -0
  34. package/build/stubs/config.stub +27 -1
  35. package/package.json +2 -1
  36. package/build/scripts/smoke_package.d.ts +0 -1
  37. package/build/tmp/probe.d.ts +0 -1
  38. package/build/tmp/probe_cli.d.ts +0 -1
  39. package/build/tmp/probe_cmp.d.ts +0 -1
  40. package/build/tmp/probe_count.d.ts +0 -1
  41. package/build/tmp/probe_data.d.ts +0 -1
  42. package/build/tmp/probe_diff.d.ts +0 -1
  43. package/build/tmp/probe_gap.d.ts +0 -1
  44. package/build/tmp/probe_graph.d.ts +0 -1
  45. package/build/tmp/probe_metrics.d.ts +0 -1
  46. package/build/tmp/probe_miss.d.ts +0 -1
  47. package/build/tmp/probe_names.d.ts +0 -1
  48. package/build/tmp/probe_nodata.d.ts +0 -1
  49. package/build/tmp/probe_one.d.ts +0 -1
  50. package/build/tmp/probe_perf.d.ts +0 -1
  51. package/build/tmp/probe_routes.d.ts +0 -1
  52. package/build/tmp/probe_unres.d.ts +0 -1
  53. package/build/tmp/probe_vazquez.d.ts +0 -1
  54. package/build/tsdown.config.d.ts +0 -2
package/CHANGELOG.md ADDED
@@ -0,0 +1,99 @@
1
+ # Changelog
2
+
3
+ Counting rules carry their own version, separate from the package version, and
4
+ `fp:diff` **refuses** to compare counts produced by different rule sets. When a
5
+ release moves the number for unchanged code, the rule set version moves with it —
6
+ otherwise the difference would measure the tool's change rather than the work, and
7
+ that difference becomes an invoice.
8
+
9
+ ## 0.2.0
10
+
11
+ **Rule set `afp@1.1.0`.** A baseline saved with 0.1.0 cannot be compared against
12
+ this release: recount it, or `fp:diff` will refuse. That refusal is the feature.
13
+
14
+ ### Counting
15
+
16
+ - **ILF vs EIF is decided across the whole project, not from HTTP routes.** AFP
17
+ §6.5.4 asks who _maintains_ a store; reading that off a walk from routes
18
+ answered a narrower question, so a table written only by a job or a seeder came
19
+ out as somebody else's table. On three production applications this moved 5
20
+ stores out of EIF. A store that is only ever read is still an EIF.
21
+ - **A job is followed into its execution method**, which is `handle`,
22
+ `process`, `run` or `perform` depending on the queue package. Looking only for
23
+ `handle` resolved the file, found no body, reported the dispatch as unknown and
24
+ left every write inside it uncounted.
25
+ - **An event dispatch is followed into its listeners.** Bindings are read from
26
+ `emitter.on(event, [listeners])`; both generated-registry shapes and the direct
27
+ class reference are handled, as is a binding that names the method.
28
+ - **`request.input('x')` and `request.only([…])` count as input DETs.** §7.2 asks
29
+ whether a user-recognisable field crosses the boundary, not how it was declared.
30
+ Deduplicated against validator fields, so nothing is paid for twice.
31
+
32
+ ### Billing
33
+
34
+ - `fp:diff` factors are reachable from `config/function_points.ts` under `diff`.
35
+ They were a typed extension point only a test could use.
36
+ - **`diff.reasonFactors`** prices a modified function by _what_ changed about it:
37
+ `type`, `size`, or `implementation` — same type, same DET, same FTR, different
38
+ body. On a real pair of releases, 151 of 378 FP billed as change were
39
+ implementation only. The default does not move: AEP grades the modification
40
+ factor from 0.25 to 1.75 through Effort Complexity variation, which needs
41
+ cyclomatic complexity this package does not measure, so it stays at 1 and every
42
+ diff says so with the amount at stake.
43
+ - The billable total is rounded to cents. `485.00000000000006` is arithmetically
44
+ the same number and not the same document.
45
+ - Warnings print **above** the per-function list. On a real diff that list is over
46
+ a hundred lines, and a caveat that has to be scrolled to is not a caveat.
47
+
48
+ ### New
49
+
50
+ - **`fp:metrics`** — density, coupling (Martin's instability) and conformance,
51
+ from the same inventory as the count. If function points pay, the team optimises
52
+ function points; this is the counterweight. Cycles between modules are reported,
53
+ not scored.
54
+ - **`CallResolver.ignores()`** — a third outcome. `resolve` returning `[]` means
55
+ _not recognised_, so a strategy that recognised a call and knew it reached no
56
+ data store had no way to say so. The volume still appears in the confidence
57
+ block: the escape hatch buys coverage, never function points.
58
+ - **`boundary.business`** — restores a store the AFP naming filter (§6.5.2.1.3)
59
+ excluded by accident.
60
+ - `fp:count` reports transactions that read the request without enumerating
61
+ fields (`all()`, `body()`, `except()`), which sit at the floor of their band.
62
+
63
+ ### Breaking
64
+
65
+ - `Conformance.writesWithValidator` is now `inputsWithValidator`, and its
66
+ denominator is the transactions that _take_ input rather than every write.
67
+ Measured over every write it read 39% on a healthy application, inviting the
68
+ conclusion that 61% of its writes were unvalidated — they were not: most were
69
+ workflow triggers carrying nothing beyond the route parameter.
70
+ - `HandlerBehavior` gained `requestFields` and `opaqueRequest`.
71
+ - `ResolverContext` gained `exportedAs` and `eventBindings`.
72
+
73
+ ### Fixed
74
+
75
+ - An aliased import resolved to the local name, so `import { x as y }` searched
76
+ for `y` in a file that exports `x`.
77
+ - A call on the result of a call was reported as unknown even though the inner
78
+ call — where the unknown is — is reported in the same body.
79
+ - `X.map(callback)` was claimed by `static-service`, which resolved the module and
80
+ reported a missing `map` in it.
81
+ - The package was unusable through ace: `commands/main.ts` exported classes
82
+ instead of the `getMetaData`/`getCommand` contract ace calls, which broke every
83
+ command in the host application.
84
+ - `configure` generated `config/function_points.ts` and no command read it.
85
+ - Generated-schema detection was disabled on Windows by a path-separator mismatch.
86
+
87
+ Coverage across four production applications went from 79.7 / 85.9 / 83.5 / 91.4%
88
+ to 94.8 / 95.1 / 98.4 / 91.4%.
89
+
90
+ ## 0.1.0
91
+
92
+ First release. Counts unadjusted function points from AdonisJS 7 + Lucid 22
93
+ source, following OMG Automated Function Points 1.0 (ISO/IEC 19515), with
94
+ `fp:count`, `fp:inventory`, `fp:explain`, `fp:diff` and `fp:calibrate` in both an
95
+ ace and a standalone front-end.
96
+
97
+ Validated against the case study published in Vazquez, Simões & Albert (2011): 46
98
+ FP, exact, with the fixture and the reference count frozen before the counter was
99
+ run against them.
package/README.md CHANGED
@@ -13,7 +13,7 @@ node ace fp:count
13
13
 
14
14
  ```
15
15
  Unadjusted count: 46 FP
16
- Ruleset: afp@1.0.0
16
+ Ruleset: afp@1.1.0
17
17
 
18
18
  type n FP
19
19
  ILF 2 14
@@ -134,6 +134,7 @@ owes you is a number that is still defensible wherever it lands.
134
134
  | ------------------------------------- | ---------------------------------------------------------- |
135
135
  | `node ace fp:count` | counts unadjusted function points |
136
136
  | `node ace fp:inventory` | the raw facts: stores, routes, tracing coverage |
137
+ | `node ace fp:metrics` | density, coupling and conformance, from the same run |
137
138
  | `node ace fp:explain <name>` | why one function was counted that way |
138
139
  | `node ace fp:diff <previous.json>` | additions / modifications / deletions, and the billable FP |
139
140
  | `node ace fp:calibrate <samples.csv>` | correction factors against a manual count |
@@ -143,6 +144,74 @@ saved count against the current state of the application. It deliberately does
143
144
  **not** take a git ref: booting an older checkout, with possibly different
144
145
  dependencies, is a problem not worth solving.
145
146
 
147
+ ### How change is priced
148
+
149
+ AEP §6.5 gives explicit anchors for added (1) and deleted (0.4). For a **modified**
150
+ function it grades the factor from 0.25 to 1.75 through Effort Complexity
151
+ variation, which needs cyclomatic complexity this package does not measure — so it
152
+ defaults to 1, which overestimates, and every diff says so with the amount at
153
+ stake.
154
+
155
+ What the default leaves on the table is a distinction the tool already measures:
156
+
157
+ ```
158
+ changed 87 functions 378 FP × 1
159
+ type 4 functions 23 FP
160
+ size 38 functions 204 FP
161
+ implementation 45 functions 151 FP
162
+ ```
163
+
164
+ `implementation` means same type, same DET, same FTR, different body — a refactor.
165
+ On a real pair of releases that was 151 of 378 FP billed as change. Pricing it at
166
+ full functional value is not defensible, and pricing it at a number this package
167
+ invented would be worse, so the number comes from the contract:
168
+
169
+ ```ts
170
+ export default defineConfig({
171
+ diff: {
172
+ reasonFactors: { implementation: 0.25 },
173
+ },
174
+ })
175
+ ```
176
+
177
+ ### `fp:metrics` — the counterweight
178
+
179
+ If function points pay, the team optimises function points: more models, more
180
+ endpoints, less reuse. So density and coupling are reported from the **same**
181
+ inventory, and this command exists to put them on the same page as the number.
182
+
183
+ ```
184
+ Density
185
+ FP per data store: 27.0
186
+ transactions per data store: 5.4
187
+
188
+ Conformance
189
+ inputs with a validator 100.0% (30/30)
190
+ entry points with a handler 100.0% (163/163)
191
+ data stores reached 96.7% (29/30)
192
+ tracing coverage 95.1% (15 unresolved calls)
193
+
194
+ module FP trans stores I depends on
195
+ pedidos 211 33 8 0.60 inventores, tecnologias, users
196
+ portal 69 15 0 1.00 contato, documentos, inventores, ...
197
+ inpi 70 11 6 0.00
198
+
199
+ Mutual dependencies (cycle candidates)
200
+ inventores <-> tecnologias
201
+ ```
202
+
203
+ The denominator of the first line is the transactions that **take** input, not
204
+ every write. Measured over every write it read 39% on a healthy application, which
205
+ invites the conclusion that 61% of its writes are unvalidated — and they are not:
206
+ most are workflow triggers (`POST /orders/:id/submit`) that carry nothing beyond
207
+ the route parameter. A metric that makes the reader draw a false conclusion is
208
+ worse than no metric.
209
+
210
+ `I` is Martin's instability, `Ce / (Ca + Ce)`: 0 means everyone depends on it and
211
+ it depends on nobody, 1 means the reverse. A module at 0 that changes often is
212
+ where change hurts. Cycles are **reported, not scored** — what to do about one is
213
+ the team's decision, and a number would hide it.
214
+
146
215
  ### `fp:explain` — the number has to be defensible
147
216
 
148
217
  ```
@@ -251,6 +320,7 @@ export default defineConfig({
251
320
  boundary: {
252
321
  infrastructure: ['access_tokens', 'audits'], // excluded, with the reason in the report
253
322
  externallyMaintained: ['erp_customers'], // counted as EIF instead of ILF
323
+ business: ['chat_sessions'], // the AFP naming filter caught it by accident
254
324
  ignoreEntryPoints: ['prometheus.metrics'],
255
325
  },
256
326
 
@@ -294,7 +364,38 @@ detail: `CreateUserJob.dispatch(p)`, `UserService.create(p)` and `User.find(p)`
294
364
  are all `Identifier.method(args)`, and only ordering tells them apart.
295
365
 
296
366
  Built-in strategies, most specific first: `same-class-method`, `action-object`,
297
- `job-dispatch`, `static-service`, `property-service`, `module-function`.
367
+ `event-dispatch`, `job-dispatch`, `transformer`, `static-service`,
368
+ `property-service`, `module-function`.
369
+
370
+ A job dispatch and an event dispatch are followed as part of the **same**
371
+ transaction: the user clicks and the effect happens, whatever thread runs it.
372
+ Event bindings are read from `emitter.on(event, [listeners])`, so a listener's
373
+ reads and writes count towards the transaction that dispatched the event.
374
+
375
+ ### Declaring that a call reaches no data
376
+
377
+ `resolve` has two outcomes — _followed_ and _not mine_ — and sometimes a third
378
+ is the truth: the call is recognised, and it reaches no data store. A wrapper
379
+ over a rate limiter or an attachment variant is a real example. Without a way
380
+ to say so, such a call stays unresolved and drags the coverage gate down.
381
+
382
+ ```ts
383
+ const limiterIsDataFree: CallResolver = {
384
+ name: 'login-limiter',
385
+ order: 1,
386
+ resolve: () => [],
387
+ ignores(call) {
388
+ // true means: this is mine, and it touches no data store
389
+ return call.getExpression().getText().startsWith('this.loginLimiter.')
390
+ },
391
+ }
392
+ ```
393
+
394
+ `ignores` is asked before `resolve`, in the same order, so a later and more
395
+ generic strategy cannot follow the call into a body it has no business reading.
396
+ It is deliberately more expensive than a list of method names to silence: the
397
+ volume still appears in the confidence block of `fp:count`, because a silent
398
+ drop is the worst defect this package can have — whoever writes it.
298
399
 
299
400
  ## Support
300
401
 
@@ -0,0 +1,10 @@
1
+ import { BaseCommand } from '@adonisjs/core/ace';
2
+ import type { CommandOptions } from '@adonisjs/core/types/ace';
3
+ export default class FpMetrics extends BaseCommand {
4
+ static commandName: string;
5
+ static description: string;
6
+ static options: CommandOptions;
7
+ out?: string;
8
+ json?: boolean;
9
+ run(): Promise<void>;
10
+ }
@@ -1,5 +1,6 @@
1
- export { default as FpInventory } from './fp_inventory.js';
2
- export { default as FpCount } from './fp_count.js';
3
- export { default as FpExplain } from './fp_explain.js';
4
- export { default as FpDiff } from './fp_diff.js';
5
- export { default as FpCalibrate } from './fp_calibrate.js';
1
+ import type { CommandMetaData } from '@adonisjs/core/types/ace';
2
+ export declare function getMetaData(): Promise<CommandMetaData[]>;
3
+ export declare function getCommand(metaData: CommandMetaData): Promise<{
4
+ commandName: string;
5
+ serialize(): CommandMetaData;
6
+ } | null>;
@@ -1,120 +1,54 @@
1
- import { a as runInventory, i as runExplain, n as runCount, r as runDiff, s as printResult, t as runCalibrate } from "../runners-Bt8tbISi.js";
2
- import { BaseCommand, args, flags } from "@adonisjs/core/ace";
3
- //#region commands/printer.ts
1
+ //#region commands/main.ts
4
2
  /**
5
- * Maps a `RunResult` onto ace's logger.
3
+ * Static index of the commands this package ships.
6
4
  *
7
- * Notes go to `info`, not `log`: they are diagnostics about the run, and under
8
- * `--json` they must not be mistaken for output.
9
- */
10
- function printerFor(command) {
11
- return {
12
- log: (message) => command.logger.log(message),
13
- error: (message) => command.logger.error(message),
14
- note: (message) => command.logger.info(message)
15
- };
16
- }
17
- //#endregion
18
- //#region \0@oxc-project+runtime@0.127.0/helpers/decorate.js
19
- function __decorate(decorators, target, key, desc) {
20
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
21
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
22
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
23
- return c > 3 && r && Object.defineProperty(target, key, r), r;
24
- }
25
- //#endregion
26
- //#region commands/fp_inventory.ts
27
- var FpInventory = class extends BaseCommand {
28
- static commandName = "fp:inventory";
29
- static description = "Extract the raw facts of the application: stores, routes and tracing";
30
- static options = { startApp: false };
31
- async run() {
32
- this.exitCode = printResult(await runInventory({
33
- root: this.app.makePath(),
34
- out: this.out
35
- }), printerFor(this));
36
- }
37
- };
38
- __decorate([flags.string({ description: "Write the inventory as JSON to the given path" })], FpInventory.prototype, "out", void 0);
39
- //#endregion
40
- //#region commands/fp_count.ts
41
- var FpCount = class extends BaseCommand {
42
- static commandName = "fp:count";
43
- static description = "Count the unadjusted function points of the application";
44
- static options = { startApp: false };
45
- async run() {
46
- this.exitCode = printResult(await runCount({
47
- root: this.app.makePath(),
48
- out: this.out,
49
- json: this.json,
50
- minCoverage: this.minCoverage
51
- }), printerFor(this));
52
- }
53
- };
54
- __decorate([flags.string({ description: "Write the result as JSON to the given path" })], FpCount.prototype, "out", void 0);
55
- __decorate([flags.boolean({ description: "Print JSON instead of a table" })], FpCount.prototype, "json", void 0);
56
- __decorate([flags.number({ description: "Minimum tracing coverage (0 to 1)" })], FpCount.prototype, "minCoverage", void 0);
57
- //#endregion
58
- //#region commands/fp_explain.ts
59
- /**
60
- * Why a function was counted the way it was.
61
- *
62
- * Not a convenience: if function points get invoiced, someone will dispute a
63
- * number, and a number without provenance is indefensible.
64
- */
65
- var FpExplain = class extends BaseCommand {
66
- static commandName = "fp:explain";
67
- static description = "Show the provenance of a function's count";
68
- static options = { startApp: false };
69
- async run() {
70
- this.exitCode = printResult(await runExplain({
71
- root: this.app.makePath(),
72
- name: this.name
73
- }), printerFor(this));
74
- }
75
- };
76
- __decorate([args.string({ description: "Function name, e.g. \"POST /books\" or \"Invite\"" })], FpExplain.prototype, "name", void 0);
77
- //#endregion
78
- //#region commands/fp_diff.ts
79
- /**
80
- * Additions, changes and deletions between two counts — what gets invoiced.
5
+ * AdonisJS resolves a package's commands by importing its `commands` entry and
6
+ * calling `getMetaData()` and `getCommand()`. Exporting the classes instead —
7
+ * which this module did until now — does not merely fail to register them: ace
8
+ * cannot read the entry at all, so EVERY command breaks, `migration:run`
9
+ * included. Adding the package to `adonisrc.ts` made the application worse than
10
+ * not having it.
81
11
  *
82
- * It works on a SAVED count (`fp:count --out`) compared against the current
83
- * state, never on two checkouts: booting the older version, with possibly
84
- * different dependencies, is a problem not worth solving.
12
+ * The shape mirrors how `@adonisjs/lucid` exposes its own commands.
85
13
  */
86
- var FpDiff = class extends BaseCommand {
87
- static commandName = "fp:diff";
88
- static description = "Compare a saved count against the current state of the application";
89
- static options = { startApp: false };
90
- async run() {
91
- this.exitCode = printResult(await runDiff({
92
- root: this.app.makePath(),
93
- previous: this.previous
94
- }), printerFor(this));
14
+ const commands = [
15
+ {
16
+ commandName: "fp:inventory",
17
+ importer: () => import("../fp_inventory-DSrCVhEy.js")
18
+ },
19
+ {
20
+ commandName: "fp:metrics",
21
+ importer: () => import("../fp_metrics-BUWj9dLw.js")
22
+ },
23
+ {
24
+ commandName: "fp:count",
25
+ importer: () => import("../fp_count-CfcXPuj5.js")
26
+ },
27
+ {
28
+ commandName: "fp:explain",
29
+ importer: () => import("../fp_explain-MKyEoi0h.js")
30
+ },
31
+ {
32
+ commandName: "fp:diff",
33
+ importer: () => import("../fp_diff-DE_t3twv.js")
34
+ },
35
+ {
36
+ commandName: "fp:calibrate",
37
+ importer: () => import("../fp_calibrate-Cm079xWL.js")
95
38
  }
96
- };
97
- __decorate([args.string({ description: "Path to the JSON produced by `fp:count --out`" })], FpDiff.prototype, "previous", void 0);
98
- //#endregion
99
- //#region commands/fp_calibrate.ts
100
- /**
101
- * Measures the counter's bias against a manual count.
102
- *
103
- * It does NOT apply the factor: calibrating is a decision for whoever signs the
104
- * contract, and a factor applied silently would stop the count from being
105
- * reproducible from the code.
106
- */
107
- var FpCalibrate = class extends BaseCommand {
108
- static commandName = "fp:calibrate";
109
- static description = "Compare the automatic count against manual counts";
110
- static options = { startApp: false };
111
- async run() {
112
- this.exitCode = printResult(await runCalibrate({
113
- root: this.app.makePath(),
114
- samples: this.samples
115
- }), printerFor(this));
116
- }
117
- };
118
- __decorate([args.string({ description: "CSV of `function,fp` counted by hand" })], FpCalibrate.prototype, "samples", void 0);
39
+ ];
40
+ let cache = null;
41
+ async function getMetaData() {
42
+ if (cache) return cache;
43
+ cache = await Promise.all(commands.map(async ({ importer }) => {
44
+ return (await importer()).default.serialize();
45
+ }));
46
+ return cache;
47
+ }
48
+ async function getCommand(metaData) {
49
+ const match = commands.find(({ commandName }) => commandName === metaData.commandName);
50
+ if (!match) return null;
51
+ return (await match.importer()).default;
52
+ }
119
53
  //#endregion
120
- export { FpCalibrate, FpCount, FpDiff, FpExplain, FpInventory };
54
+ export { getCommand, getMetaData };
@@ -0,0 +1,24 @@
1
+ //#region commands/printer.ts
2
+ /**
3
+ * Maps a `RunResult` onto ace's logger.
4
+ *
5
+ * Notes go to `info`, not `log`: they are diagnostics about the run, and under
6
+ * `--json` they must not be mistaken for output.
7
+ */
8
+ function printerFor(command) {
9
+ return {
10
+ log: (message) => command.logger.log(message),
11
+ error: (message) => command.logger.error(message),
12
+ note: (message) => command.logger.info(message)
13
+ };
14
+ }
15
+ //#endregion
16
+ //#region \0@oxc-project+runtime@0.127.0/helpers/decorate.js
17
+ function __decorate(decorators, target, key, desc) {
18
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
19
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
20
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
21
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
22
+ }
23
+ //#endregion
24
+ export { printerFor as n, __decorate as t };
@@ -0,0 +1,25 @@
1
+ import { s as printResult, t as runCalibrate } from "./runners-DpMd-yZM.js";
2
+ import { n as printerFor, t as __decorate } from "./decorate-D6enDn9D.js";
3
+ import { BaseCommand, args } from "@adonisjs/core/ace";
4
+ //#region commands/fp_calibrate.ts
5
+ /**
6
+ * Measures the counter's bias against a manual count.
7
+ *
8
+ * It does NOT apply the factor: calibrating is a decision for whoever signs the
9
+ * contract, and a factor applied silently would stop the count from being
10
+ * reproducible from the code.
11
+ */
12
+ var FpCalibrate = class extends BaseCommand {
13
+ static commandName = "fp:calibrate";
14
+ static description = "Compare the automatic count against manual counts";
15
+ static options = { startApp: false };
16
+ async run() {
17
+ this.exitCode = printResult(await runCalibrate({
18
+ root: this.app.makePath(),
19
+ samples: this.samples
20
+ }), printerFor(this));
21
+ }
22
+ };
23
+ __decorate([args.string({ description: "CSV of `function,fp` counted by hand" })], FpCalibrate.prototype, "samples", void 0);
24
+ //#endregion
25
+ export { FpCalibrate as default };
@@ -0,0 +1,22 @@
1
+ import { n as runCount, s as printResult } from "./runners-DpMd-yZM.js";
2
+ import { n as printerFor, t as __decorate } from "./decorate-D6enDn9D.js";
3
+ import { BaseCommand, flags } from "@adonisjs/core/ace";
4
+ //#region commands/fp_count.ts
5
+ var FpCount = class extends BaseCommand {
6
+ static commandName = "fp:count";
7
+ static description = "Count the unadjusted function points of the application";
8
+ static options = { startApp: false };
9
+ async run() {
10
+ this.exitCode = printResult(await runCount({
11
+ root: this.app.makePath(),
12
+ out: this.out,
13
+ json: this.json,
14
+ minCoverage: this.minCoverage
15
+ }), printerFor(this));
16
+ }
17
+ };
18
+ __decorate([flags.string({ description: "Write the result as JSON to the given path" })], FpCount.prototype, "out", void 0);
19
+ __decorate([flags.boolean({ description: "Print JSON instead of a table" })], FpCount.prototype, "json", void 0);
20
+ __decorate([flags.number({ description: "Minimum tracing coverage (0 to 1)" })], FpCount.prototype, "minCoverage", void 0);
21
+ //#endregion
22
+ export { FpCount as default };
@@ -0,0 +1,25 @@
1
+ import { r as runDiff, s as printResult } from "./runners-DpMd-yZM.js";
2
+ import { n as printerFor, t as __decorate } from "./decorate-D6enDn9D.js";
3
+ import { BaseCommand, args } from "@adonisjs/core/ace";
4
+ //#region commands/fp_diff.ts
5
+ /**
6
+ * Additions, changes and deletions between two counts — what gets invoiced.
7
+ *
8
+ * It works on a SAVED count (`fp:count --out`) compared against the current
9
+ * state, never on two checkouts: booting the older version, with possibly
10
+ * different dependencies, is a problem not worth solving.
11
+ */
12
+ var FpDiff = class extends BaseCommand {
13
+ static commandName = "fp:diff";
14
+ static description = "Compare a saved count against the current state of the application";
15
+ static options = { startApp: false };
16
+ async run() {
17
+ this.exitCode = printResult(await runDiff({
18
+ root: this.app.makePath(),
19
+ previous: this.previous
20
+ }), printerFor(this));
21
+ }
22
+ };
23
+ __decorate([args.string({ description: "Path to the JSON produced by `fp:count --out`" })], FpDiff.prototype, "previous", void 0);
24
+ //#endregion
25
+ export { FpDiff as default };
@@ -0,0 +1,24 @@
1
+ import { i as runExplain, s as printResult } from "./runners-DpMd-yZM.js";
2
+ import { n as printerFor, t as __decorate } from "./decorate-D6enDn9D.js";
3
+ import { BaseCommand, args } from "@adonisjs/core/ace";
4
+ //#region commands/fp_explain.ts
5
+ /**
6
+ * Why a function was counted the way it was.
7
+ *
8
+ * Not a convenience: if function points get invoiced, someone will dispute a
9
+ * number, and a number without provenance is indefensible.
10
+ */
11
+ var FpExplain = class extends BaseCommand {
12
+ static commandName = "fp:explain";
13
+ static description = "Show the provenance of a function's count";
14
+ static options = { startApp: false };
15
+ async run() {
16
+ this.exitCode = printResult(await runExplain({
17
+ root: this.app.makePath(),
18
+ name: this.name
19
+ }), printerFor(this));
20
+ }
21
+ };
22
+ __decorate([args.string({ description: "Function name, e.g. \"POST /books\" or \"Invite\"" })], FpExplain.prototype, "name", void 0);
23
+ //#endregion
24
+ export { FpExplain as default };
@@ -0,0 +1,18 @@
1
+ import { a as runInventory, s as printResult } from "./runners-DpMd-yZM.js";
2
+ import { n as printerFor, t as __decorate } from "./decorate-D6enDn9D.js";
3
+ import { BaseCommand, flags } from "@adonisjs/core/ace";
4
+ //#region commands/fp_inventory.ts
5
+ var FpInventory = class extends BaseCommand {
6
+ static commandName = "fp:inventory";
7
+ static description = "Extract the raw facts of the application: stores, routes and tracing";
8
+ static options = { startApp: false };
9
+ async run() {
10
+ this.exitCode = printResult(await runInventory({
11
+ root: this.app.makePath(),
12
+ out: this.out
13
+ }), printerFor(this));
14
+ }
15
+ };
16
+ __decorate([flags.string({ description: "Write the inventory as JSON to the given path" })], FpInventory.prototype, "out", void 0);
17
+ //#endregion
18
+ export { FpInventory as default };
@@ -0,0 +1,20 @@
1
+ import { o as runMetrics, s as printResult } from "./runners-DpMd-yZM.js";
2
+ import { n as printerFor, t as __decorate } from "./decorate-D6enDn9D.js";
3
+ import { BaseCommand, flags } from "@adonisjs/core/ace";
4
+ //#region commands/fp_metrics.ts
5
+ var FpMetrics = class extends BaseCommand {
6
+ static commandName = "fp:metrics";
7
+ static description = "Density, coupling and conformance, derived from the same inventory";
8
+ static options = { startApp: false };
9
+ async run() {
10
+ this.exitCode = printResult(await runMetrics({
11
+ root: this.app.makePath(),
12
+ out: this.out,
13
+ json: this.json
14
+ }), printerFor(this));
15
+ }
16
+ };
17
+ __decorate([flags.string({ description: "Write the metrics as JSON to the given path" })], FpMetrics.prototype, "out", void 0);
18
+ __decorate([flags.boolean({ description: "Print JSON instead of a table" })], FpMetrics.prototype, "json", void 0);
19
+ //#endregion
20
+ export { FpMetrics as default };
package/build/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
+ export { configure } from './configure.js';
1
2
  export { defineConfig } from './src/define_config.js';
2
- export type { FunctionPointsConfig } from './src/define_config.js';
3
+ export type { FunctionPointsConfig, FunctionOverride } from './src/define_config.js';
3
4
  export * from './src/types.js';
4
5
  export { BUILTIN_CALL_RESOLVERS } from './src/inventory/resolvers/index.js';
5
6
  export type { CallResolver, ResolverContext } from './src/inventory/resolvers/types.js';
package/build/index.js CHANGED
@@ -1,4 +1,5 @@
1
+ import { configure } from "./configure.js";
1
2
  import { n as defineConfig } from "./define_config-DOqWyPwV.js";
2
3
  import "./src/types.js";
3
- import { t as BUILTIN_CALL_RESOLVERS } from "./resolvers-CU9HKYpn.js";
4
- export { BUILTIN_CALL_RESOLVERS, defineConfig };
4
+ import { t as BUILTIN_CALL_RESOLVERS } from "./resolvers-MFjRl2ef.js";
5
+ export { BUILTIN_CALL_RESOLVERS, configure, defineConfig };