@codometer/cli 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,1477 @@
1
+ # ⏲️ Codometer
2
+
3
+ **Measure a repository and report what it found.**
4
+
5
+ Codometer walks a directory, parses everything it recognizes, and writes
6
+ what it counted as a markdown badge block, a JSON report, or both. It counts
7
+ languages the way you would expect — files, lines, classes, functions — and it
8
+ also counts the conventions a repository holds _itself_ to, which is usually
9
+ the more interesting number.
10
+
11
+ ```bash
12
+ npm install --save-dev @codometer/cli
13
+ ```
14
+
15
+ ```bash
16
+ codometer
17
+ ```
18
+
19
+ <!-- The badge block below this README's own Codometer section is produced by exactly this. -->
20
+
21
+ ```markdown
22
+ ### TypeScript & JavaScript
23
+
24
+ ![TypeScript Files](https://img.shields.io/badge/TypeScript_Files-989-3178c6?style=flat-square)
25
+ ![Test Files](https://img.shields.io/badge/Test_Files-247-10b981?style=flat-square)
26
+ ![Classes](https://img.shields.io/badge/Classes-352-7c3aed?style=flat-square)
27
+ ```
28
+
29
+ ## Why
30
+
31
+ "1,034 source files" tells you almost nothing. "159 of them are services and
32
+ 226 are the unit tests for them" tells you what the repository is actually made
33
+ of, and watching that ratio move tells you where it is going.
34
+
35
+ No language analyzer can produce the second number, because what a
36
+ `*.service.ts` means is a property of your repository rather than of
37
+ TypeScript. Codometer measures both halves: the built-in language counters, and
38
+ the ones you declare.
39
+
40
+ ## Usage
41
+
42
+ Measuring the repository is codometer's default command, so `codometer` on its
43
+ own is `codometer measure`:
44
+
45
+ ```bash
46
+ codometer --config configuration/codometer.config.ts --check limits
47
+ ```
48
+
49
+ | Flag | Purpose |
50
+ | ---- | ------- |
51
+ | `--check [set]` | Fail on a comma-separated set drawn from `reports` and `limits` |
52
+ | `--config [path]` | Configuration file to read; searched for when omitted |
53
+ | `-f, --format <format>` | What to print to standard output, one of `json` and `markdown` |
54
+ | `--inputs [globs...]` | Glob array measured in place of every configured input, with no configured input — not even the built-in `codebase` one — active |
55
+ | `--output-json [path]` | Write the JSON report, at the given path or the configured one |
56
+ | `--output-markdown [path]` | Write the markdown badge block, at the given path or the configured one |
57
+
58
+ There is no `--directory`: a run always measures the process's working
59
+ directory, with no per-invocation override. `cd` into what you want measured,
60
+ or narrow it with `--inputs`.
61
+
62
+ **Every flag is independent.** `--output-json`/`--output-markdown` each write
63
+ their own destination, `--check limits` fails on a breached limit, `--check
64
+ reports` fails on a stale one, `--format` prints, and none of them turns
65
+ another on:
66
+
67
+ | Invocation | Writes | Fails on staleness | Fails on a breach |
68
+ | ---------- | ------ | ------------------ | ----------------- |
69
+ | `codometer` | no | no | no |
70
+ | `codometer --check limits` | no | no | yes |
71
+ | `codometer --check reports` | no | yes | no |
72
+ | `codometer --check limits,reports` | no | yes | yes |
73
+ | `codometer --output-json --output-markdown` | yes, both | no | no |
74
+ | `codometer --output-json --check limits` | yes | no | yes, after writing |
75
+
76
+ `--output-json`/`--output-markdown` passed **bare** write to whatever the
77
+ resolved configuration's own `json`/`markdown` output declares, and are
78
+ refused before anything is measured when the configuration names no such
79
+ output — there is then nowhere to write it. Passed **with a path**, that path
80
+ is used for this run alone, whether or not the configuration declares an
81
+ output of that kind.
82
+
83
+ A run that both writes and gates produces **every** output before it fails, so
84
+ the report is on disk even when the gate trips. Combining an `--output-*` flag
85
+ with `--check reports` is refused rather than obeyed: nothing can be stale in
86
+ the run that just wrote it. So is a `--check` value the tool does not know, or
87
+ one passed with no value at all — a valueless `--check` used to mean "check
88
+ everything", and a set with nothing in it looks exactly like the flag having
89
+ been left off. Every complaint about one command line is reported together
90
+ rather than one run at a time.
91
+
92
+ A **breach** and **staleness** are different findings and are never reported as
93
+ one. A `warn` breach is printed and leaves the exit code alone; a `fail` breach
94
+ exits 1, but only where `--check limits` asked for a gate.
95
+
96
+ `--check reports` compares a committed report against a fresh measurement, so it
97
+ is only as stable as the numbers it re-measures. Compressed sizes are
98
+ Node-version dependent — the bundled zlib differs between releases — so a report
99
+ written on one runtime and checked on another reads as stale when nothing
100
+ changed at all. Check on the runtime the repository pins, or expect false
101
+ staleness rather than a real finding.
102
+
103
+ ```yaml
104
+ - run: npx @codometer/cli --check reports,limits
105
+ ```
106
+
107
+ ## One folder at a time
108
+
109
+ Codometer measures **one directory**, and knows nothing about workspaces, task
110
+ runners, or project graphs. Run it in a project and that project is what gets
111
+ measured — its own sources, and whatever inputs the configuration declares for
112
+ it:
113
+
114
+ ```bash
115
+ cd packages/logger && codometer --check limits
116
+ ```
117
+
118
+ With no `--config`, the configuration is found by walking upward from that
119
+ folder and taking the **first** file found. The nearest one wins outright:
120
+ nothing from a further ancestor is folded into it, because a merged
121
+ configuration leaves a limit that never applied looking exactly like one that
122
+ did.
123
+
124
+ A configuration file is always read as a plain object — one exported as a
125
+ function is not recognized, and falls back to an empty configuration, the
126
+ same way a file exporting `42` does. So a project answering for itself writes
127
+ its own `codometer.config.ts`, typically by importing and spreading a shared
128
+ object a workspace root exports, the way this repository's own projects each
129
+ spread [`configuration/codometer.config.ts`](../../configuration/codometer.config.ts).
130
+
131
+ ## What gets measured
132
+
133
+ Discovery walks the directory itself and reads every `.gitignore` it passes,
134
+ so **`.gitignore` is already in force** — a build directory or a virtual
135
+ environment is pruned where its ignore file names it, and no exclusion has to
136
+ name it again. Git is never invoked, so a directory that is not a repository
137
+ at all is measured the same way as one that is.
138
+
139
+ What is measured is the working tree rather than the index: a file that exists
140
+ and is not ignored counts, whether or not it has been committed yet.
141
+
142
+ | Group | Counts |
143
+ | ----- | ------ |
144
+ | Repository | Lines of code, repository size, folders, source files |
145
+ | TypeScript & JavaScript | Files, tests, classes, functions, methods, interfaces, generics, enums, constants, imports, decorators, exported symbols, doc comments, TODOs |
146
+ | Python | Files, lines, classes, functions, protocols, constants, imports, decorators, docstrings |
147
+ | Jupyter | Notebooks, cells by kind, executions, outputs, plus the code and prose inside them |
148
+ | JSON | Files, objects, arrays, properties, scalars by type, node count, max depth |
149
+ | YAML | Documents, mappings, sequences, keys, scalars, anchors, aliases, max depth |
150
+ | Markdown | Headings by level, paragraphs, lists, tables, links, images, code blocks, block quotes |
151
+ | SQL | Statements by kind — selects, inserts, updates, deletes, creates, joins, CTEs |
152
+ | Shell | Functions, variables, exports, conditionals, loops, pipelines, shebangs |
153
+ | TOML | Tables, array tables, keys, arrays |
154
+ | HCL | Blocks, resources, variables, outputs, attributes, interpolations |
155
+ | CSS | Rules, selectors, declarations, at-rules, media queries, custom properties |
156
+ | Conventions | Whatever you declare — see below |
157
+
158
+ Notebooks are measured by composition rather than by a fourth parser: the
159
+ document is handed to the JSON analyzer, its code cells to the Python analyzer,
160
+ and its markdown cells to the markdown analyzer, leaving only cells, outputs,
161
+ and executions for the notebook analyzer itself.
162
+
163
+ Python analysis runs through an interpreter, `python3` by default. Point it
164
+ elsewhere with `python: { command: "uv run python" }` when Python lives in a
165
+ virtual environment.
166
+
167
+ ## Inputs
168
+
169
+ An **input** is a named set of files, declared by include and exclude globs
170
+ together with the analyses run over it. `inputs` is an array, and one entry is
171
+ always there whether or not a configuration declares any: the built-in
172
+ `codebase` scan, which measures everything the ignore files leave behind and
173
+ runs `language` analysis over it. Declaring an input named `codebase`
174
+ replaces that built-in entry outright rather than adding beside it.
175
+
176
+ An input names its files by glob, which is what lets one measure compiled
177
+ output: a directory every `.gitignore` claims, and therefore the one place
178
+ ignore rules must not reach — ignore files are never consulted for a declared
179
+ input.
180
+
181
+ ```ts
182
+ inputs: [
183
+ {
184
+ analyses: ["size"],
185
+ compression: "gzip",
186
+ exclude: ["dist/vendor/**"],
187
+ include: ["dist/**/*.js", "!dist/**/*.map.js"],
188
+ name: "compiled",
189
+ },
190
+ ],
191
+ ```
192
+
193
+ | Field | Required | Default | Meaning |
194
+ | ----- | -------- | ------- | ------- |
195
+ | `name` | yes | — | What the input is called. Two inputs may not share one |
196
+ | `include` | yes | — | Globs that add files. At least one must add rather than remove |
197
+ | `exclude` | no | none | Globs that remove files |
198
+ | `analyses` | yes | — | `language`, `size`, or both. At least one |
199
+ | `compression` | no | `gzip` | `gzip`, `brotli`, or `none` for the bytes on disk |
200
+ | `directory` | no | `.` | Where the input's globs start, relative to the process's working directory |
201
+
202
+ `language` runs the analyzers above over the input's files. `size` compresses
203
+ each matched file on its own and sums the results — never all of them together
204
+ — at gzip level 9 or brotli quality 11, both stated rather than defaulted.
205
+
206
+ A `!` prefix in `include` removes files. Negations form one set applied to the
207
+ whole input rather than being read in order, so rearranging the array cannot
208
+ change what the input holds. Dot files are excluded unless a glob spells one
209
+ out, directories never match, and a file that was matched but cannot be read
210
+ fails the run rather than counting as zero bytes.
211
+
212
+ `--inputs [globs...]` on the command line replaces every configured input for
213
+ that one run, running only `language` analysis over exactly the globs given —
214
+ with no other input, not even the built-in `codebase` one, active.
215
+
216
+ ## Limits
217
+
218
+ A **limit** is how high one measured metric may go. Any metric can carry one — a
219
+ compressed size, a line count, a counter for one of the conventions below — and
220
+ a metric nothing limits is measured and reported exactly as before, gated by
221
+ nothing.
222
+
223
+ ```ts
224
+ defaultInput: "codebase",
225
+ limits: [
226
+ { metric: "Compiled JavaScript.size", value: "8 KB" },
227
+ { label: "Interfaces", metric: "typescript.interfaces", value: 500 },
228
+ { metric: "linesOfCode", severity: "warn", value: 100_000 },
229
+ ],
230
+ ```
231
+
232
+ | Field | Required | Default | Meaning |
233
+ | ----- | -------- | ------- | ------- |
234
+ | `metric` | yes | — | Dotted path of the metric this limits |
235
+ | `value` | yes | — | How high the metric may go, as a number or a string with a unit |
236
+ | `severity` | no | `fail` | `fail` stops the run on a breach; `warn` reports it |
237
+ | `label` | no | the path | What to call the limit in a report |
238
+
239
+ A metric is addressed by the input's name followed by its path within that
240
+ input — `codebase.typescript.interfaces`, `codebase.markdown.files`,
241
+ `Compiled JavaScript.size`. Every input carries `files`, an input running size
242
+ analysis carries `size`, and one running language analysis carries every
243
+ counter the tables above list, with configured counters under `custom.<label>`.
244
+ Set `defaultInput` and a path naming no input is read as that input's.
245
+
246
+ Where a `defaultInput` is set, a path is read as that input's whenever no
247
+ input name prefixes it — so with `defaultInput: "codebase"` and an input
248
+ called `typescript`, `typescript.interfaces` is the codebase's, because the
249
+ `typescript` input has no `interfaces` metric of its own to compete with it.
250
+ Write the input name in full wherever an input and a metric group share one.
251
+
252
+ **Ambiguity is refused, never resolved.** A path that could name two metrics —
253
+ an input called `markdown` beside the codebase's own `markdown.files` — fails
254
+ the run naming both readings, as does a path naming none, or one naming a
255
+ metric from an analysis the input never ran. A limit that quietly bound to the
256
+ wrong metric would look exactly like one that works.
257
+
258
+ A value written as a string carries a **decimal** unit whose trailing `b` is
259
+ required: `"8 KB"` is 8000 bytes and `"1 MB"` is 1000000, while `"8 K"` is not
260
+ a size and is refused rather than read as anything. A value nothing can read
261
+ fails the run instead of being taken as zero.
262
+
263
+ An input that matched **no files** fails the run if and only if a limit is
264
+ written against it. Declaring a limit asserts the files are there, so an empty
265
+ match is a glob that stopped matching or a build that never ran — while an
266
+ input nobody limited simply measured zero, which is unremarkable.
267
+
268
+ ### Reading every limit at once
269
+
270
+ A repository that declares its limits one per project has no single file left
271
+ to read them from. `codometer configuration` is that reading:
272
+
273
+ ```bash
274
+ codometer configuration --limits
275
+ ```
276
+
277
+ ```text
278
+ | Directory | Metric | Label | Severity | Value | Declared in |
279
+ | --- | --- | --- | --- | --- | --- |
280
+ | packages/logger | `Compiled JavaScript.size`| — | fail | 12.00 kB | `packages/logger/codometer.config.ts`|
281
+ ```
282
+
283
+ It walks for every configuration file beneath the directory it is given — in
284
+ any of the formats codometer accepts — and resolves each one **for its own
285
+ folder**, so what it reports is what a per-project run actually sees. The walk
286
+ honors the exclusions of whatever configuration answers for the directory being
287
+ listed, so a configuration inside a dependency or a generator template is never
288
+ listed as something the repository configures.
289
+
290
+ **Name that configuration with `--config` where the directory has none of its
291
+ own.** A workspace often states its shared configuration in a file every
292
+ project spreads rather than at its own root, and the upward search then finds
293
+ nothing:
294
+
295
+ ```bash
296
+ codometer configuration --limits --config configuration/codometer.config.ts
297
+ ```
298
+
299
+ Drop `--limits` to list everything each configuration resolved to: its inputs,
300
+ outputs and their custom statistics, exclusions, and Python command. Add
301
+ `--format json` for a machine-readable listing.
302
+
303
+ It reports configuration and never measurement. No build is required and no
304
+ limit is evaluated, so it runs in milliseconds — and no single unreadable file
305
+ takes it down. A configuration file that cannot be loaded is listed as
306
+ unreadable, and a walk root nothing answers for falls back to the built-in
307
+ exclusions, says so at the top of the listing, and still reports every limit
308
+ the tree declares. That last case exits 1 rather than 0: the listing is real,
309
+ but the exclusions the repository declares were never consulted, and a clean
310
+ exit would claim otherwise.
311
+
312
+ ## Custom statistics
313
+
314
+ A repository that names files by convention has a vocabulary no analyzer knows
315
+ about. Each output destination carries its own `custom` list to count them —
316
+ there is no longer one shared `statistics` array, so a JSON report and a
317
+ markdown report may count entirely different things:
318
+
319
+ ```ts
320
+ outputs: [
321
+ {
322
+ custom: [
323
+ { label: "Service Files", patterns: ["**/*.service.ts"] },
324
+ { label: "Unit Tests", patterns: ["**/*.unit.test.ts"] },
325
+ { color: "16a34a", label: "Migrations", patterns: ["**/migrations/*.sql"] },
326
+ ],
327
+ path: "codometer-report.json",
328
+ type: "json",
329
+ },
330
+ ],
331
+ ```
332
+
333
+ Counters can also match _declarations_ rather than files, by shape:
334
+
335
+ ```ts
336
+ custom: [
337
+ {
338
+ group: "typescript",
339
+ label: "Static Methods",
340
+ symbols: { kinds: ["method"], modifiers: ["static"] },
341
+ },
342
+ ];
343
+ ```
344
+
345
+ Or select a **comment budget** — how much prose one comment block may hold —
346
+ rather than counting files or declarations at all:
347
+
348
+ ```ts
349
+ custom: [
350
+ { comment: { language: "yaml", maximumWords: 128 }, label: "YAML Comment Budget" },
351
+ { comment: { kind: "class", maximumLines: 24 }, label: "Class Comment Budget" },
352
+ ];
353
+ ```
354
+
355
+ A `comment` entry's `value` is how many blocks breached the selector's own
356
+ `maximumCharacters`/`maximumLines`/`maximumWords`, and its `instances` name
357
+ each one's file and line — the same shape any other custom counter reports, so
358
+ a comment-budget breach gates through an ordinary `limits[]` entry addressing
359
+ its `custom.<label>` metric path exactly the way a file or symbol counter's
360
+ limit does. There is no separate flag for it, and no inheritance between
361
+ `comment` selectors: a selector naming no `language` covers every language
362
+ codometer measures comments in, so expressing "every language at one budget,
363
+ except one language at another" means writing the general budget once per
364
+ covered language rather than once for all of them, leaving the exception for
365
+ its own, narrower selector. See
366
+ [`configuration/codometer.config.ts`](../../configuration/codometer.config.ts)
367
+ for how this repository derives one selector per language from its own list of
368
+ comment-bearing languages.
369
+
370
+ Each entry becomes one badge, in the order configured, rendered into the group
371
+ it names — `conventions` by default. Symbol counting happens during the walk
372
+ the TypeScript analyzer already makes, so any number of these costs one pass
373
+ over the sources.
374
+
375
+ The full reference — kinds, modifiers, colors, groups, the comment-selector
376
+ fields, and how `patterns` narrows a symbol counter rather than being what it
377
+ counts — is in
378
+ [**@codometer/configuration**](../codometer-configuration/README.md#custom-statistics).
379
+
380
+ ## Output
381
+
382
+ What a run prints and what it writes are asked for separately, and neither
383
+ implies the other:
384
+
385
+ | Sink | Flag | What lands there |
386
+ | ---- | ---- | ---------------- |
387
+ | Console | `-f, --format <format>` | The report as `json`, or the rendered badges as `markdown` |
388
+ | Report | `--output-json [path]` | The structured report below |
389
+ | Markdown | `--output-markdown [path]` | The badge block, in a markdown file |
390
+
391
+ **One markdown sink, not two.** `--output-markdown` splices the badge block
392
+ between its markers when the file already carries them, appends it with them
393
+ when it does not, and creates the file when it is not there. A README somebody
394
+ else wrote the rest of and a file holding nothing but badges are the same case,
395
+ so neither needs a flag of its own.
396
+
397
+ **Each `--output-*` flag's value is optional, and the two meanings differ.**
398
+ Passed bare, the flag writes wherever the resolved configuration's own output
399
+ of that kind declares — refused before anything is measured when the
400
+ configuration names no such output, since there is then nowhere to write it.
401
+ Passed with a path, that path is used for this run alone, whether or not the
402
+ configuration declares an output of that kind:
403
+
404
+ ```bash
405
+ codometer --output-json # writes the configured json output's path
406
+ codometer --output-json report.json # writes report.json instead, even with no json output configured
407
+ ```
408
+
409
+ **`--format` falls back to the resolved configuration's own `format` field**
410
+ when the flag is left off — never inferred from which other flags are present,
411
+ so omitting it prints the same thing whether or not the run also writes a
412
+ file. `format` is required in every configuration, so this fallback is never
413
+ undefined.
414
+
415
+ **A named destination stands for all of them.** `--output-json` on its own asks
416
+ for the report and nothing else, whatever the configuration file also describes;
417
+ `--output-markdown` on its own writes only the badge block. Each flag governs
418
+ its own destination and never turns another on.
419
+
420
+ **Standard output carries the result; every diagnostic goes to standard error.**
421
+ `codometer --format json > report.json` has to produce a file something can
422
+ parse, so a log line never shares that stream — including the exclusion notice
423
+ below, which is still on the console and still in front of a human, just not
424
+ inside the data. Only `--format` ever writes to that stream: a file sink that
425
+ could also print is how one run put two documents on it.
426
+
427
+ The rendered badges are a description paragraph followed by shields.io badges
428
+ under one `###` heading per language. A run scoped to one project puts its own
429
+ `##` section heading above all of it, inside the markers, because that block
430
+ lands in a document somebody else wrote the rest of and `###` groups with
431
+ nothing above them would read as a continuation of whatever section came
432
+ before. A run measuring a whole repository renders none: that README titles the
433
+ section above the markers itself. Beyond the language groups there is also —
434
+ for a run that measured a declared input's size — a `Measured Targets` group
435
+ carrying each input's size under the compression it was measured with. That
436
+ group is how a project's README reports the size of what it ships; a run that
437
+ declared no such input renders no such group, which is why the whole-repository
438
+ report carries only its own `Repository Size`.
439
+
440
+ Spliced, the badges sit between two markers,
441
+ named `<!-- codometer:start -->
442
+
443
+ ## ⏲️ Codometer
444
+
445
+ ### Project
446
+
447
+ ![Lines of Code](https://img.shields.io/badge/Lines_of_Code-3336-22c55e?style=flat-square)
448
+ ![Repository Size](https://img.shields.io/badge/Repository_Size-116.60_kB-6b7280?style=flat-square)
449
+ ![Folders](https://img.shields.io/badge/Folders-6-4a4a4a?style=flat-square)
450
+ ![Source Files](https://img.shields.io/badge/Source_Files-35-3178c6?style=flat-square)
451
+
452
+ ### Measured Targets
453
+
454
+ ![Compiled JavaScript Size](https://img.shields.io/badge/Compiled_JavaScript_Size-6.13_kB_gzip-6b7280?style=flat-square)
455
+
456
+ ### TypeScript
457
+
458
+ ![TypeScript Files](https://img.shields.io/badge/TypeScript_Files-35-3178c6?style=flat-square)
459
+ ![Interfaces](https://img.shields.io/badge/Interfaces-4-0ea5e9?style=flat-square)
460
+ ![Generic Declarations](https://img.shields.io/badge/Generic_Declarations-0-0369a1?style=flat-square)
461
+ ![Enums](https://img.shields.io/badge/Enums-0-f97316?style=flat-square)
462
+ ![Decorators](https://img.shields.io/badge/Decorators-25-db2777?style=flat-square)
463
+ ![Doc Comments](https://img.shields.io/badge/Doc_Comments-61-6366f1?style=flat-square)
464
+ ![Static Methods](https://img.shields.io/badge/Static_Methods-0-166534?style=flat-square)
465
+
466
+ ### JavaScript
467
+
468
+ ![JavaScript Files](https://img.shields.io/badge/JavaScript_Files-0-f7df1e?style=flat-square)
469
+ ![Test Files](https://img.shields.io/badge/Test_Files-7-10b981?style=flat-square)
470
+ ![External Packages](https://img.shields.io/badge/External_Packages-19-8b5cf6?style=flat-square)
471
+ ![Classes](https://img.shields.io/badge/Classes-7-7c3aed?style=flat-square)
472
+ ![Functions](https://img.shields.io/badge/Functions-158-16a34a?style=flat-square)
473
+ ![Methods](https://img.shields.io/badge/Methods-34-15803d?style=flat-square)
474
+ ![Sync Functions](https://img.shields.io/badge/Sync_Functions-127-4ade80?style=flat-square)
475
+ ![Async Functions](https://img.shields.io/badge/Async_Functions-65-059669?style=flat-square)
476
+ ![Constants](https://img.shields.io/badge/Constants-109-dc2626?style=flat-square)
477
+ ![Imports](https://img.shields.io/badge/Imports-133-0284c7?style=flat-square)
478
+ ![Exported Symbols](https://img.shields.io/badge/Exported_Symbols-25-ea580c?style=flat-square)
479
+ ![Comments](https://img.shields.io/badge/Comments-139-64748b?style=flat-square)
480
+ ![Comment Lines](https://img.shields.io/badge/Comment_Lines-335-475569?style=flat-square)
481
+ ![TODO Comments](https://img.shields.io/badge/TODO_Comments-0-ca8a04?style=flat-square)
482
+
483
+ ### Python
484
+
485
+ ![Python Files](https://img.shields.io/badge/Python_Files-0-3776ab?style=flat-square)
486
+ ![Python Lines](https://img.shields.io/badge/Python_Lines-0-4b8bbe?style=flat-square)
487
+ ![Python Classes](https://img.shields.io/badge/Python_Classes-0-7c3aed?style=flat-square)
488
+ ![Python Functions](https://img.shields.io/badge/Python_Functions-0-16a34a?style=flat-square)
489
+ ![Python Protocols](https://img.shields.io/badge/Python_Protocols-0-0ea5e9?style=flat-square)
490
+ ![Python Constants](https://img.shields.io/badge/Python_Constants-0-dc2626?style=flat-square)
491
+ ![Python Imports](https://img.shields.io/badge/Python_Imports-0-0284c7?style=flat-square)
492
+ ![Python Decorators](https://img.shields.io/badge/Python_Decorators-0-db2777?style=flat-square)
493
+ ![Docstrings](https://img.shields.io/badge/Docstrings-0-6366f1?style=flat-square)
494
+ ![Docstring Lines](https://img.shields.io/badge/Docstring_Lines-0-818cf8?style=flat-square)
495
+ ![Python Comments](https://img.shields.io/badge/Python_Comments-0-64748b?style=flat-square)
496
+ ![Python Comment Lines](https://img.shields.io/badge/Python_Comment_Lines-0-475569?style=flat-square)
497
+
498
+ ### JSON
499
+
500
+ ![JSON Files](https://img.shields.io/badge/JSON_Files-4-a16207?style=flat-square)
501
+ ![JSON Lines](https://img.shields.io/badge/JSON_Lines-193-ca8a04?style=flat-square)
502
+ ![JSON Objects](https://img.shields.io/badge/JSON_Objects-43-7c3aed?style=flat-square)
503
+ ![JSON Arrays](https://img.shields.io/badge/JSON_Arrays-13-8b5cf6?style=flat-square)
504
+ ![JSON Properties](https://img.shields.io/badge/JSON_Properties-124-0284c7?style=flat-square)
505
+ ![JSON Strings](https://img.shields.io/badge/JSON_Strings-107-16a34a?style=flat-square)
506
+ ![JSON Numbers](https://img.shields.io/badge/JSON_Numbers-1-059669?style=flat-square)
507
+ ![JSON Booleans](https://img.shields.io/badge/JSON_Booleans-8-0ea5e9?style=flat-square)
508
+ ![JSON Nulls](https://img.shields.io/badge/JSON_Nulls-0-64748b?style=flat-square)
509
+ ![JSON Items](https://img.shields.io/badge/JSON_Items-44-475569?style=flat-square)
510
+ ![JSON Nodes](https://img.shields.io/badge/JSON_Nodes-172-dc2626?style=flat-square)
511
+ ![JSON Max Depth](https://img.shields.io/badge/JSON_Max_Depth-6-ea580c?style=flat-square)
512
+
513
+ ### YAML
514
+
515
+ ![YAML Files](https://img.shields.io/badge/YAML_Files-0-cb171e?style=flat-square)
516
+ ![YAML Lines](https://img.shields.io/badge/YAML_Lines-0-e34c26?style=flat-square)
517
+ ![YAML Documents](https://img.shields.io/badge/YAML_Documents-0-f97316?style=flat-square)
518
+ ![YAML Mappings](https://img.shields.io/badge/YAML_Mappings-0-7c3aed?style=flat-square)
519
+ ![YAML Sequences](https://img.shields.io/badge/YAML_Sequences-0-8b5cf6?style=flat-square)
520
+ ![YAML Keys](https://img.shields.io/badge/YAML_Keys-0-0284c7?style=flat-square)
521
+ ![YAML Scalars](https://img.shields.io/badge/YAML_Scalars-0-16a34a?style=flat-square)
522
+ ![YAML Anchors](https://img.shields.io/badge/YAML_Anchors-0-059669?style=flat-square)
523
+ ![YAML Aliases](https://img.shields.io/badge/YAML_Aliases-0-10b981?style=flat-square)
524
+ ![YAML Comments](https://img.shields.io/badge/YAML_Comments-0-64748b?style=flat-square)
525
+ ![YAML Max Depth](https://img.shields.io/badge/YAML_Max_Depth-0-ea580c?style=flat-square)
526
+
527
+ ### TOML
528
+
529
+ ![TOML Files](https://img.shields.io/badge/TOML_Files-0-9c4221?style=flat-square)
530
+ ![TOML Lines](https://img.shields.io/badge/TOML_Lines-0-b45309?style=flat-square)
531
+ ![TOML Tables](https://img.shields.io/badge/TOML_Tables-0-7c3aed?style=flat-square)
532
+ ![TOML Array Tables](https://img.shields.io/badge/TOML_Array_Tables-0-8b5cf6?style=flat-square)
533
+ ![TOML Keys](https://img.shields.io/badge/TOML_Keys-0-0284c7?style=flat-square)
534
+ ![TOML Arrays](https://img.shields.io/badge/TOML_Arrays-0-16a34a?style=flat-square)
535
+ ![TOML Comments](https://img.shields.io/badge/TOML_Comments-0-64748b?style=flat-square)
536
+
537
+ ### Shell
538
+
539
+ ![Shell Files](https://img.shields.io/badge/Shell_Files-0-89e051?style=flat-square)
540
+ ![Shell Lines](https://img.shields.io/badge/Shell_Lines-0-4eaa25?style=flat-square)
541
+ ![Shell Functions](https://img.shields.io/badge/Shell_Functions-0-16a34a?style=flat-square)
542
+ ![Shell Variables](https://img.shields.io/badge/Shell_Variables-0-0284c7?style=flat-square)
543
+ ![Shell Exports](https://img.shields.io/badge/Shell_Exports-0-ea580c?style=flat-square)
544
+ ![Shell Conditionals](https://img.shields.io/badge/Shell_Conditionals-0-7c3aed?style=flat-square)
545
+ ![Shell Loops](https://img.shields.io/badge/Shell_Loops-0-8b5cf6?style=flat-square)
546
+ ![Shell Pipelines](https://img.shields.io/badge/Shell_Pipelines-0-059669?style=flat-square)
547
+ ![Shebangs](https://img.shields.io/badge/Shebangs-0-6b7280?style=flat-square)
548
+ ![Shell Comments](https://img.shields.io/badge/Shell_Comments-0-64748b?style=flat-square)
549
+ ![Shell Comment Lines](https://img.shields.io/badge/Shell_Comment_Lines-0-475569?style=flat-square)
550
+
551
+ ### SQL
552
+
553
+ ![SQL Files](https://img.shields.io/badge/SQL_Files-0-e38c00?style=flat-square)
554
+ ![SQL Lines](https://img.shields.io/badge/SQL_Lines-0-f29111?style=flat-square)
555
+ ![SQL Statements](https://img.shields.io/badge/SQL_Statements-0-7c3aed?style=flat-square)
556
+ ![SQL Selects](https://img.shields.io/badge/SQL_Selects-0-16a34a?style=flat-square)
557
+ ![SQL Inserts](https://img.shields.io/badge/SQL_Inserts-0-22c55e?style=flat-square)
558
+ ![SQL Updates](https://img.shields.io/badge/SQL_Updates-0-0ea5e9?style=flat-square)
559
+ ![SQL Deletes](https://img.shields.io/badge/SQL_Deletes-0-dc2626?style=flat-square)
560
+ ![SQL Creates](https://img.shields.io/badge/SQL_Creates-0-0284c7?style=flat-square)
561
+ ![SQL Joins](https://img.shields.io/badge/SQL_Joins-0-8b5cf6?style=flat-square)
562
+ ![SQL CTEs](https://img.shields.io/badge/SQL_CTEs-0-059669?style=flat-square)
563
+ ![SQL Comments](https://img.shields.io/badge/SQL_Comments-0-64748b?style=flat-square)
564
+
565
+ ### HCL
566
+
567
+ ![HCL Files](https://img.shields.io/badge/HCL_Files-0-844fba?style=flat-square)
568
+ ![HCL Lines](https://img.shields.io/badge/HCL_Lines-0-a78bfa?style=flat-square)
569
+ ![HCL Blocks](https://img.shields.io/badge/HCL_Blocks-0-7c3aed?style=flat-square)
570
+ ![HCL Resources](https://img.shields.io/badge/HCL_Resources-0-0284c7?style=flat-square)
571
+ ![HCL Variables](https://img.shields.io/badge/HCL_Variables-0-16a34a?style=flat-square)
572
+ ![HCL Outputs](https://img.shields.io/badge/HCL_Outputs-0-059669?style=flat-square)
573
+ ![HCL Attributes](https://img.shields.io/badge/HCL_Attributes-0-0ea5e9?style=flat-square)
574
+ ![HCL Interpolations](https://img.shields.io/badge/HCL_Interpolations-0-db2777?style=flat-square)
575
+ ![HCL Comments](https://img.shields.io/badge/HCL_Comments-0-64748b?style=flat-square)
576
+
577
+ ### CSS
578
+
579
+ ![CSS Files](https://img.shields.io/badge/CSS_Files-0-264de4?style=flat-square)
580
+ ![CSS Lines](https://img.shields.io/badge/CSS_Lines-0-2965f1?style=flat-square)
581
+ ![CSS Rules](https://img.shields.io/badge/CSS_Rules-0-7c3aed?style=flat-square)
582
+ ![CSS Selectors](https://img.shields.io/badge/CSS_Selectors-0-8b5cf6?style=flat-square)
583
+ ![CSS Declarations](https://img.shields.io/badge/CSS_Declarations-0-0284c7?style=flat-square)
584
+ ![CSS At Rules](https://img.shields.io/badge/CSS_At_Rules-0-f97316?style=flat-square)
585
+ ![CSS Media Queries](https://img.shields.io/badge/CSS_Media_Queries-0-ea580c?style=flat-square)
586
+ ![CSS Custom Properties](https://img.shields.io/badge/CSS_Custom_Properties-0-16a34a?style=flat-square)
587
+ ![CSS Comments](https://img.shields.io/badge/CSS_Comments-0-64748b?style=flat-square)
588
+
589
+ ### Conventions
590
+
591
+ ![Module Files](https://img.shields.io/badge/Module_Files-4-7c3aed?style=flat-square)
592
+ ![Service Files](https://img.shields.io/badge/Service_Files-0-0284c7?style=flat-square)
593
+ ![Command Files](https://img.shields.io/badge/Command_Files-3-16a34a?style=flat-square)
594
+ ![Constants Files](https://img.shields.io/badge/Constants_Files-3-ea580c?style=flat-square)
595
+ ![Types Files](https://img.shields.io/badge/Types_Files-3-db2777?style=flat-square)
596
+ ![Utilities Files](https://img.shields.io/badge/Utilities_Files-1-0ea5e9?style=flat-square)
597
+ ![TypeORM Entities](https://img.shields.io/badge/TypeORM_Entities-0-059669?style=flat-square)
598
+ ![Unit Tests](https://img.shields.io/badge/Unit_Tests-5-ca8a04?style=flat-square)
599
+ ![Integration Tests](https://img.shields.io/badge/Integration_Tests-1-7c3aed?style=flat-square)
600
+ ![End To End Tests](https://img.shields.io/badge/End_To_End_Tests-1-0284c7?style=flat-square)
601
+ ![CSS Comment Budget](https://img.shields.io/badge/CSS_Comment_Budget-0-16a34a?style=flat-square)
602
+ ![HCL Comment Budget](https://img.shields.io/badge/HCL_Comment_Budget-0-ea580c?style=flat-square)
603
+ ![Python Comment Budget](https://img.shields.io/badge/Python_Comment_Budget-0-db2777?style=flat-square)
604
+ ![SQL Comment Budget](https://img.shields.io/badge/SQL_Comment_Budget-0-0ea5e9?style=flat-square)
605
+ ![TOML Comment Budget](https://img.shields.io/badge/TOML_Comment_Budget-0-059669?style=flat-square)
606
+ ![TypeScript Comment Budget](https://img.shields.io/badge/TypeScript_Comment_Budget-0-ca8a04?style=flat-square)
607
+ ![YAML Comment Budget](https://img.shields.io/badge/YAML_Comment_Budget-0-7c3aed?style=flat-square)
608
+ ![Shell Comment Budget](https://img.shields.io/badge/Shell_Comment_Budget-0-0284c7?style=flat-square)
609
+
610
+ ### Jupyter
611
+
612
+ ![Notebooks](https://img.shields.io/badge/Notebooks-0-f37626?style=flat-square)
613
+ ![Notebook Cells](https://img.shields.io/badge/Notebook_Cells-0-e8a33d?style=flat-square)
614
+ ![Code Cells](https://img.shields.io/badge/Code_Cells-0-3776ab?style=flat-square)
615
+ ![Markdown Cells](https://img.shields.io/badge/Markdown_Cells-0-083fa1?style=flat-square)
616
+ ![Raw Cells](https://img.shields.io/badge/Raw_Cells-0-9ca3af?style=flat-square)
617
+ ![Executed Cells](https://img.shields.io/badge/Executed_Cells-0-16a34a?style=flat-square)
618
+ ![Cell Outputs](https://img.shields.io/badge/Cell_Outputs-0-059669?style=flat-square)
619
+ ![Notebook Code Lines](https://img.shields.io/badge/Notebook_Code_Lines-0-4b8bbe?style=flat-square)
620
+ ![Notebook Classes](https://img.shields.io/badge/Notebook_Classes-0-7c3aed?style=flat-square)
621
+ ![Notebook Functions](https://img.shields.io/badge/Notebook_Functions-0-22c55e?style=flat-square)
622
+ ![Notebook Imports](https://img.shields.io/badge/Notebook_Imports-0-0284c7?style=flat-square)
623
+ ![Notebook Decorators](https://img.shields.io/badge/Notebook_Decorators-0-db2777?style=flat-square)
624
+ ![Notebook Prose Lines](https://img.shields.io/badge/Notebook_Prose_Lines-0-1f6feb?style=flat-square)
625
+ ![Notebook Headings](https://img.shields.io/badge/Notebook_Headings-0-a78bfa?style=flat-square)
626
+ ![Notebook Links](https://img.shields.io/badge/Notebook_Links-0-10b981?style=flat-square)
627
+ ![Notebook Images](https://img.shields.io/badge/Notebook_Images-0-34d399?style=flat-square)
628
+ ![Notebook Code Blocks](https://img.shields.io/badge/Notebook_Code_Blocks-0-dc2626?style=flat-square)
629
+ ![Notebook Properties](https://img.shields.io/badge/Notebook_Properties-0-ca8a04?style=flat-square)
630
+ ![Notebook Nodes](https://img.shields.io/badge/Notebook_Nodes-0-a16207?style=flat-square)
631
+ ![Notebook Max Depth](https://img.shields.io/badge/Notebook_Max_Depth-0-ea580c?style=flat-square)
632
+
633
+ ### Markdown
634
+
635
+ ![Markdown Files](https://img.shields.io/badge/Markdown_Files-1-083fa1?style=flat-square)
636
+ ![Markdown Lines](https://img.shields.io/badge/Markdown_Lines-247-1f6feb?style=flat-square)
637
+ ![H1](https://img.shields.io/badge/H1-1-7c3aed?style=flat-square)
638
+ ![H2](https://img.shields.io/badge/H2-7-8b5cf6?style=flat-square)
639
+ ![H3](https://img.shields.io/badge/H3-14-a78bfa?style=flat-square)
640
+ ![H4](https://img.shields.io/badge/H4-0-c4b5fd?style=flat-square)
641
+ ![H5](https://img.shields.io/badge/H5-0-ddd6fe?style=flat-square)
642
+ ![H6](https://img.shields.io/badge/H6-0-ede9fe?style=flat-square)
643
+ ![Paragraphs](https://img.shields.io/badge/Paragraphs-49-64748b?style=flat-square)
644
+ ![Lists](https://img.shields.io/badge/Lists-6-16a34a?style=flat-square)
645
+ ![List Items](https://img.shields.io/badge/List_Items-28-22c55e?style=flat-square)
646
+ ![Task List Items](https://img.shields.io/badge/Task_List_Items-0-4ade80?style=flat-square)
647
+ ![Tables](https://img.shields.io/badge/Tables-2-0284c7?style=flat-square)
648
+ ![Table Rows](https://img.shields.io/badge/Table_Rows-10-0ea5e9?style=flat-square)
649
+ ![Links](https://img.shields.io/badge/Links-9-059669?style=flat-square)
650
+ ![Images](https://img.shields.io/badge/Images-0-10b981?style=flat-square)
651
+ ![Code Blocks](https://img.shields.io/badge/Code_Blocks-13-dc2626?style=flat-square)
652
+ ![Inline Code](https://img.shields.io/badge/Inline_Code-78-ef4444?style=flat-square)
653
+ ![Block Quotes](https://img.shields.io/badge/Block_Quotes-0-ca8a04?style=flat-square)
654
+ ![Thematic Breaks](https://img.shields.io/badge/Thematic_Breaks-0-a16207?style=flat-square)
655
+ <!-- codometer:end -->` unless a configuration
656
+ renames them — as this example does, so that documenting the markers does not
657
+ make this document splice its own badges into the example:
658
+
659
+ ```markdown
660
+ <!-- STATISTICS_START -->
661
+ <!-- STATISTICS_END -->
662
+ ```
663
+
664
+ The block is appended when the markers are absent, and the file is created when
665
+ it does not exist. A markdown output's `write` function is the whole of that
666
+ customizable behavior — it both decides what the report says and where it
667
+ lands, in place of what used to be two separate `render` and `write`
668
+ callbacks — and leaving it unset keeps the built-in rendering and writing. See
669
+ [writing markdown](../codometer-configuration/README.md#writing-markdown).
670
+
671
+ ### What codometer writes, it does not measure
672
+
673
+ Every file a run would write is left out of what it measures, and the run says
674
+ so on the console. No configuration, and no ignore-file entry: codometer knows
675
+ its own destinations.
676
+
677
+ A badge is an image inside a link, so a spliced block moves `markdown.images`,
678
+ `markdown.links`, and `markdown.lines`, which moves the badges, which moves the
679
+ counts. Left in, a written report would be stale the moment it landed. The
680
+ exclusion is applied identically whatever the flags say, so a run that writes
681
+ and a `--check reports` run always measure the same tree.
682
+
683
+ ### The report
684
+
685
+ Codometer's own shape rather than any other tool's. Every metric carries its
686
+ value, and where something limits it, that limit's value, severity, and whether
687
+ it was breached — a limit that held is written out exactly like one that did
688
+ not, so a consumer can render the headroom rather than only the failures.
689
+
690
+ ```json
691
+ {
692
+ "failures": [
693
+ {
694
+ "kind": "limit",
695
+ "reason": "Cannot bind the limit written against \"nowhere.at.all\": nothing measured answers to it.",
696
+ "subject": "nowhere.at.all"
697
+ }
698
+ ],
699
+ "targets": [
700
+ {
701
+ "empty": false,
702
+ "files": 1,
703
+ "metrics": [
704
+ {
705
+ "instances": null,
706
+ "limits": [],
707
+ "name": "compiled.files",
708
+ "path": "files",
709
+ "unit": null,
710
+ "value": 1
711
+ },
712
+ {
713
+ "instances": null,
714
+ "limits": [
715
+ { "breached": true, "label": null, "severity": "warn", "value": 900 },
716
+ { "breached": true, "label": "Bundle", "severity": "fail", "value": 1000 }
717
+ ],
718
+ "name": "compiled.size",
719
+ "path": "size",
720
+ "unit": "bytes",
721
+ "value": 5195
722
+ },
723
+ {
724
+ "instances": [
725
+ { "file": "src/modules/codometer/codometer.service.ts", "line": 32, "measured": 9 }
726
+ ],
727
+ "limits": [
728
+ { "breached": true, "label": null, "severity": "fail", "value": 0 }
729
+ ],
730
+ "name": "compiled.custom.Class Comment Budget",
731
+ "path": "custom.Class Comment Budget",
732
+ "unit": null,
733
+ "value": 1
734
+ }
735
+ ],
736
+ "name": "compiled"
737
+ }
738
+ ]
739
+ }
740
+ ```
741
+
742
+ | Field | Meaning |
743
+ | ----- | ------- |
744
+ | `name` | The metric's name across runs — its target, then its path. The join key a later run is compared on |
745
+ | `path` | The metric's path within its target, with no target name on the front |
746
+ | `unit` | `"bytes"` where the value counts bytes, `null` for a plain count |
747
+ | `limits` | Every limit declared on the metric, in the order written. Empty where nothing limits it — never an absent key |
748
+ | `instances` | Where each instance a per-instance selector produced was found — a `comment` selector's breaches today. `null` for a metric that only counts, never an empty array standing in for it |
749
+ | `empty` | Said outright when a target's globs matched nothing |
750
+ | `failures` | Whatever the run could not do: a target that would not measure, a limit that bound to nothing |
751
+
752
+ **A `comment` selector's metric reports every breaching block, not the whole
753
+ selector's activity.** `value` is the count of blocks that broke the
754
+ selector's own `maximumCharacters`/`maximumLines`/`maximumWords`, and
755
+ `instances` names each one's `file` and 1-indexed `line` plus how much it
756
+ `measured`. A block that stayed within budget contributes to neither.
757
+
758
+ **A metric may carry more than one limit.** The configuration accepts a `warn`
759
+ short of a `fail` on a single metric on purpose — that is how a repository sees
760
+ a number coming before it stops a change — and the gate enforces all of them, so
761
+ the report lists all of them. A consumer deciding what to show picks
762
+ deliberately: the `fail` limit is what stops a change, and a
763
+ breached `warn` beneath it is advice, not a failure.
764
+
765
+ **Byte values are raw and decimal.** A renderer showing kilobytes divides by
766
+ 1000, the same units a limit written `"8 KB"` is read in.
767
+
768
+ **Nothing is signalled by a missing field.** A target that matched nothing says
769
+ `"empty": true` and any limit written against it joins `failures`, rather than
770
+ leaving a consumer to infer an empty match from an absent limit beside a
771
+ passing verdict.
772
+
773
+ A failure is neither a breach nor staleness: it is the run not having finished.
774
+ Every one of them is collected and reported together, so a configuration with
775
+ three broken limits is one run to diagnose rather than three. A failure fails
776
+ any run that writes or gates; a bare run reports it and exits clean, exactly as
777
+ the flag table promises.
778
+
779
+ ## Packages
780
+
781
+ | Package | Role |
782
+ | ------- | ---- |
783
+ | [`@codometer/cli`](README.md) | Measures the repository and writes the reports. Knows nothing about any particular repository |
784
+ | [`@codometer/configuration`](../codometer-configuration/README.md) | Reads `codometer.config.ts` and resolves exclusions, output destinations, custom statistics, and the Python interpreter |
785
+ | [`@codometer/examples`](../codometer-examples/README.md) | A sample corpus with known contents and one runnable example per behavior above, each asserted by a test |
786
+
787
+ Which paths to skip, where the output goes, and how Python is reached are all
788
+ configuration. That split is what lets the CLI be a general tool rather than
789
+ one repository's script.
790
+
791
+ ## Agent skills
792
+
793
+ Agent skills for coding agents working with codometer are published in
794
+ [`@codometer/agents`](https://github.com/Organizzolini/codebase/tree/main/packages/ic-suite/codometer/codometer-agents):
795
+
796
+ | Skill | Description |
797
+ | ----- | ----------- |
798
+ | [`codometer-measure`](https://github.com/Organizzolini/codebase/tree/main/packages/ic-suite/codometer/codometer-agents/skills/codometer-measure) | Run code measurements and produce markdown or JSON reports |
799
+ | [`codometer-configure`](https://github.com/Organizzolini/codebase/tree/main/packages/ic-suite/codometer/codometer-agents/skills/codometer-configure) | Configure measurement inputs, custom metrics, and limits in `codometer.config.ts` |
800
+ | [`codometer-triage`](https://github.com/Organizzolini/codebase/tree/main/packages/ic-suite/codometer/codometer-agents/skills/codometer-triage) | Triage limit breaches and measurement failures |
801
+
802
+ ## Examples
803
+
804
+ Everything above has a runnable example in
805
+ [`@codometer/examples`](https://github.com/Organizzolini/codebase/tree/main/packages/ic-suite/codometer/codometer-examples), measured against a
806
+ sample corpus whose counts are stated and checked — including a reproduction of
807
+ each refusal, which is where a reader is most likely to be stuck. An agent that
808
+ has already been handed a refusal and needs the fix should start from that
809
+ package's [AGENTS.md](https://github.com/Organizzolini/codebase/tree/main/packages/ic-suite/codometer/codometer-examples/AGENTS.md), which maps each message
810
+ codometer prints to the example that reproduces it.
811
+
812
+ ## Start
813
+
814
+ ```bash
815
+ nx run codometer-cli:start
816
+ ```
817
+
818
+ Pass the flags from [Usage](#usage) after `--`, so
819
+ `nx run codometer-cli:start -- --check limits` gates the current directory
820
+ from source.
821
+
822
+ ## Test
823
+
824
+ ```bash
825
+ nx run codometer-cli:vitest
826
+ ```
827
+
828
+ ## Contributing
829
+
830
+ ```bash
831
+ nx run codometer-cli:build # Compile
832
+ ```
833
+
834
+ ## License
835
+
836
+ MIT — see [LICENSE](../../LICENSE).
837
+
838
+ ## 👔 Conformetry
839
+
840
+ This project was generated from the [nestjs-command-project](../../configuration/conformetry-templates/nestjs-command-project) conformetry template.
841
+
842
+ <!-- callidescope:start -->
843
+
844
+ ## 🔭 Callidescope
845
+
846
+ Call stacks traced through `packages/ic-suite/codometer/codometer-cli`, deepest first. Each frame shows what it takes, what it returns, and what its documentation says.
847
+
848
+ | Measure | Value |
849
+ | --- | --- |
850
+ | Callables | 40 |
851
+ | Files | 24 |
852
+ | Calls traced | 45 |
853
+ | Call stacks | 11 |
854
+ | Deepest stack | 15 |
855
+ | Stacks through recursion | 0 |
856
+ | Unfollowable calls | 3 |
857
+
858
+ ### Limits
859
+
860
+ What this project is judged against, as declared in its own `callidescope.config.ts`.
861
+
862
+ | Limit | Value |
863
+ | --- | --- |
864
+ | `maximumDepth` | 15 |
865
+ | `maximumBreadth` | 9 |
866
+
867
+ ### Call stacks (depth)
868
+
869
+ **1. `MeasureCommand.run`** — depth ≥ 15 · decorated-method
870
+
871
+ ```text
872
+ 🚀 MeasureCommand.run(_passedParameters: string[], options: MeasureCommandOptions): Promise<void> [packages/ic-suite/codometer/codometer-cli/src/modules/measure/measure.command.ts:398]
873
+ ↳ Measure the repository and produce every resolved output.
874
+ └─> MeasureService.measure(args: MeasureArguments): MeasurementResult [packages/ic-suite/codometer/codometer-measurement/src/modules/measure/measure.service.ts:312]
875
+ ↳ Measure every input the configuration declares.
876
+ └─> MeasureService.measureInput(args: MeasureInputArguments): InputMeasurement [packages/ic-suite/codometer/codometer-measurement/src/modules/measure/measure.service.ts:253]
877
+ ↳ Measure one declared input with whichever analyses it asked for.
878
+ └─> MeasureService.analyzeFiles(args: AnalyzeFilesArguments): CodeStatisticsResult [packages/ic-suite/codometer/codometer-measurement/src/modules/measure/measure.service.ts:75]
879
+ ↳ Run every analyzer over one set of files and shape the result.
880
+ └─> LanguagesService.analyze(args: AnalyzeLanguagesArguments): LanguageResults [packages/ic-suite/codometer/codometer-languages/src/modules/languages/languages.service.ts:56]
881
+ ↳ Analyze every language present in the discovered files.
882
+ └─> LanguageCommentsService.measure(args: MeasureLanguageCommentsArguments): Record<string, CommentMeasurement[]> [packages/ic-suite/codometer/codometer-languages/src/modules/comments/language-comments.service.ts:212]
883
+ ↳ Measures every counter that names no declaration kind, keyed by the custom statistic's label.
884
+ └─> LanguageCommentsService.flatMap(…)(…): CodometerCommentMeasurement[] [packages/ic-suite/codometer/codometer-languages/src/modules/comments/language-comments.service.ts:234]
885
+ └─> LanguageCommentsService.measureOneLanguage(…): CodometerCommentMeasurement[] [packages/ic-suite/codometer/codometer-languages/src/modules/comments/language-comments.service.ts:96]
886
+ ↳ Measures one counter's budget against one language's discovered files.
887
+ └─> LanguageCommentsService.measurePython(…): CodometerCommentMeasurement[] [packages/ic-suite/codometer/codometer-languages/src/modules/comments/language-comments.service.ts:162]
888
+ ↳ Measures Python's comments, which its own analyzer already found.
889
+ └─> LanguageCommentsService.flatMap(…)(…): CodometerCommentMeasurement[] [packages/ic-suite/codometer/codometer-languages/src/modules/comments/language-comments.service.ts:175]
890
+ └─> CommentsService.measure(args: MeasureCommentsArguments): CommentMeasurement[] [packages/ic-suite/codometer/codometer-languages/src/modules/comments/comments.service.ts:164]
891
+ ↳ Measures every block a file's comment tokens form, breached or not.
892
+ └─> CommentsService.flatMap(…)(this: undefined, block: CommentBlock): CodometerCommentMeasurement[] [packages/ic-suite/codometer/codometer-languages/src/modules/comments/comments.service.ts:165]
893
+ └─> CommentsService.measureText(args: MeasureCommentTextArguments): CommentMeasurement[] [packages/ic-suite/codometer/codometer-languages/src/modules/comments/comments.service.ts:187]
894
+ ↳ Measures one comment's text against every maximum declared for it.
895
+ └─> CommentsService.declaredLimits(…): { limit: number; measured: number; unit: CodometerDocumentationUnit; }[] [packages/ic-suite/codometer/codometer-languages/src/modules/comments/comments.service.ts:64]
896
+ ↳ Every declared maximum, paired with what this comment measured.
897
+ └─> CommentsService.countWords(prose: string): number [packages/ic-suite/codometer/codometer-languages/src/modules/comments/comments.service.ts:50]
898
+ ↳ Counts the words in a comment's prose, markers already stripped.
899
+ ```
900
+
901
+ **2. `ConfigurationCommand.run`** — depth ≥ 12 · decorated-method
902
+
903
+ ```text
904
+ 🚀 ConfigurationCommand.run(…): Promise<void> [packages/ic-suite/codometer/codometer-cli/src/modules/configuration/configuration.command.ts:110]
905
+ ↳ Lists what the tree beneath the given directory configures.
906
+ └─> ConfigurationListingService.describeConfigurations(args: DescribeConfigurationsArguments): Promise<ConfiguredTree> [packages/ic-suite/codometer/codometer-output/src/modules/configuration-listing/configuration-listing.service.ts:155]
907
+ ↳ Resolves the configuration each file in a tree answers with.
908
+ └─> ConfigurationListingService.findConfigurationFiles(args: DescribeConfigurationsArguments): Promise<DiscoveredConfigurationFiles> [packages/ic-suite/codometer/codometer-output/src/modules/configuration-listing/configuration-listing.service.ts:188]
909
+ ↳ Finds every configuration file beneath a directory.
910
+ └─> ConfigurationListingService.resolveWalkExclusions(args: DescribeConfigurationsArguments): Promise<WalkExclusions> [packages/ic-suite/codometer/codometer-output/src/modules/configuration-listing/configuration-listing.service.ts:120]
911
+ ↳ Resolves the exclusions the walk uses, reporting rather than throwing.
912
+ └─> ConfigurationService.loadConfigurationFile(args?: LoadConfigurationArguments): Promise<LoadedConfiguration> [packages/ic-suite/codometer/codometer-configuration/src/modules/configuration/configuration.service.ts:85]
913
+ ↳ Loads a configuration and says which file answered.
914
+ └─> ConfigurationService.resolveConfiguration(configuration: CodometerConfiguration): ResolvedCodometerConfiguration [packages/ic-suite/codometer/codometer-configuration/src/modules/configuration/configuration.service.ts:130]
915
+ ↳ Fills in every field a configuration file may leave out.
916
+ └─> ConfigurationResolverService.resolveConfiguration(configuration: CodometerConfiguration): ResolvedCodometerConfiguration [packages/ic-suite/codometer/codometer-configuration/src/modules/configuration/configuration-resolver.service.ts:336]
917
+ ↳ Fills in every field a configuration file may leave out.
918
+ └─> ConfigurationResolverService.resolveLimits(limits: CodometerLimit[] | undefined): ResolvedCodometerLimit[] [packages/ic-suite/codometer/codometer-configuration/src/modules/configuration/configuration-resolver.service.ts:258]
919
+ ↳ Gives every limit its severity and a value read as a number.
920
+ └─> ConfigurationResolverService.map(…)(…): { label: string | undefined; metric: string; severity: CodometerSeverity; value: number; } [packages/ic-suite/codometer/codometer-configuration/src/modules/configuration/configuration-resolver.service.ts:261]
921
+ └─> ConfigurationResolverService.parseLimitValue(limit: CodometerLimit): number [packages/ic-suite/codometer/codometer-configuration/src/modules/configuration/configuration-resolver.service.ts:94]
922
+ ↳ Reads a limit's value, in decimal units when it was written as a string.
923
+ └─> ConfigurationResolverService.parseLimitValueText(metric: string, text: string): number [packages/ic-suite/codometer/codometer-configuration/src/modules/configuration/configuration-resolver.service.ts:114]
924
+ ↳ Reads a limit written as a string, unit and all.
925
+ └─> InvalidLimitValueError.constructor(metric: string, value: string): InvalidLimitValueError [packages/ic-suite/codometer/codometer-core/src/modules/codometer-core/codometer-core.constants.ts:35]
926
+ ```
927
+
928
+ **3. `ChangesCommand.run`** — depth ≥ 11 · decorated-method
929
+
930
+ ```text
931
+ 🚀 ChangesCommand.run(_passedParameters: string[], options: ChangesCommandOptions): Promise<void> [packages/ic-suite/codometer/codometer-cli/src/modules/changes/changes.command.ts:97]
932
+ ↳ Diffs every project's report against the baseline, and emits the result.
933
+ └─> ChangesService.collect(args: CollectRowsArguments): MetricCollection [packages/ic-suite/codometer/codometer-output/src/modules/changes/changes.service.ts:292]
934
+ ↳ Joins every current report to the baseline snapshot.
935
+ └─> ChangesService.map(…)(reportPath: string): MetricCollection [packages/ic-suite/codometer/codometer-output/src/modules/changes/changes.service.ts:293]
936
+ └─> ChangesService.collectProjectRows(args: CollectProjectRowsArguments): MetricCollection [packages/ic-suite/codometer/codometer-output/src/modules/changes/changes.service.ts:110]
937
+ ↳ Joins one project's current report to its baseline.
938
+ └─> ChangesService.readBaseline(args: CollectProjectRowsArguments): Map<string, ReportMetric> [packages/ic-suite/codometer/codometer-output/src/modules/changes/changes.service.ts:154]
939
+ ↳ Reads a baseline report into a name-to-metric lookup.
940
+ └─> ChangesService.readReport(workingDirectory: string, reportPath: string): ProjectReport [packages/ic-suite/codometer/codometer-output/src/modules/changes/changes.service.ts:243]
941
+ ↳ Parses a codometer report, tolerating an absent or malformed file.
942
+ └─> ChangesService.flatMap(…)(…): ReportMetric[] [packages/ic-suite/codometer/codometer-output/src/modules/changes/changes.service.ts:259]
943
+ └─> ChangesService.readMetrics(target: ReportTarget): ReportMetric[] [packages/ic-suite/codometer/codometer-output/src/modules/changes/changes.service.ts:222]
944
+ ↳ Pulls every metric a target produced out of the report.
945
+ └─> ChangesService.map(…)(…): { breach: MetricSeverity | undefined; empty: boolean; label: string; limit: number | undefined; name: string; unit: "bytes" | null; value: number; } [packages/ic-suite/codometer/codometer-output/src/modules/changes/changes.service.ts:223]
946
+ └─> ChangesService.readBreach(limits: readonly ReportLimit[]): MetricSeverity | undefined [packages/ic-suite/codometer/codometer-output/src/modules/changes/changes.service.ts:175]
947
+ ↳ The severity of the worst limit a metric breached, if it breached one.
948
+ └─> ChangesService.filter(…)(…): boolean [packages/ic-suite/codometer/codometer-output/src/modules/changes/changes.service.ts:178]
949
+ ```
950
+
951
+ <details>
952
+ <summary>8 more call stacks</summary>
953
+
954
+ **4. `ChangesCommand.parseDirectory`** — depth 5 · decorated-method
955
+
956
+ ```text
957
+ 🚀 ChangesCommand.parseDirectory(value: unknown): string [packages/ic-suite/codometer/codometer-cli/src/modules/changes/changes.command.ts:70]
958
+ ↳ Parse the directory to look for codometer reports in.
959
+ └─> ConfigurationService.parseDirectoryOption(value: unknown): string [packages/ic-suite/codometer/codometer-configuration/src/modules/configuration/configuration.service.ts:120]
960
+ ↳ Reads a directory option, falling back to the working directory.
961
+ └─> ConfigurationFlagsService.parseDirectoryOption(value: unknown): string [packages/ic-suite/codometer/codometer-configuration/src/modules/configuration/configuration-flags.service.ts:126]
962
+ ↳ Reads a directory option, falling back to the working directory.
963
+ └─> ConfigurationFlagsService.parseDefaultedOption(value: unknown, fallback: string): string [packages/ic-suite/codometer/codometer-configuration/src/modules/configuration/configuration-flags.service.ts:115]
964
+ ↳ Reads an option that carries a default when it was left off.
965
+ └─> ConfigurationFlagsService.parseOptionalOption(value: unknown): string | undefined [packages/ic-suite/codometer/codometer-configuration/src/modules/configuration/configuration-flags.service.ts:142]
966
+ ↳ Reads an option that carries text, or nothing at all.
967
+ ```
968
+
969
+ **5. `ConfigurationCommand.parseDirectory`** — depth 5 · decorated-method
970
+
971
+ ```text
972
+ 🚀 ConfigurationCommand.parseDirectory(value: unknown): string [packages/ic-suite/codometer/codometer-cli/src/modules/configuration/configuration.command.ts:74]
973
+ ↳ Parse the directory to look for configuration files beneath.
974
+ └─> ConfigurationService.parseDirectoryOption(value: unknown): string [packages/ic-suite/codometer/codometer-configuration/src/modules/configuration/configuration.service.ts:120]
975
+ ↳ Reads a directory option, falling back to the working directory.
976
+ └─> ConfigurationFlagsService.parseDirectoryOption(value: unknown): string [packages/ic-suite/codometer/codometer-configuration/src/modules/configuration/configuration-flags.service.ts:126]
977
+ ↳ Reads a directory option, falling back to the working directory.
978
+ └─> ConfigurationFlagsService.parseDefaultedOption(value: unknown, fallback: string): string [packages/ic-suite/codometer/codometer-configuration/src/modules/configuration/configuration-flags.service.ts:115]
979
+ ↳ Reads an option that carries a default when it was left off.
980
+ └─> ConfigurationFlagsService.parseOptionalOption(value: unknown): string | undefined [packages/ic-suite/codometer/codometer-configuration/src/modules/configuration/configuration-flags.service.ts:142]
981
+ ↳ Reads an option that carries text, or nothing at all.
982
+ ```
983
+
984
+ **6. `ConfigurationCommand.parseFormat`** — depth 4 · decorated-method
985
+
986
+ ```text
987
+ 🚀 ConfigurationCommand.parseFormat(value: unknown): string [packages/ic-suite/codometer/codometer-cli/src/modules/configuration/configuration.command.ts:83]
988
+ ↳ Parse the output format the listing is rendered in.
989
+ └─> ConfigurationService.parseDefaultedOption(value: unknown, fallback: string): string [packages/ic-suite/codometer/codometer-configuration/src/modules/configuration/configuration.service.ts:115]
990
+ ↳ Reads an option that carries a default when it was left off.
991
+ └─> ConfigurationFlagsService.parseDefaultedOption(value: unknown, fallback: string): string [packages/ic-suite/codometer/codometer-configuration/src/modules/configuration/configuration-flags.service.ts:115]
992
+ ↳ Reads an option that carries a default when it was left off.
993
+ └─> ConfigurationFlagsService.parseOptionalOption(value: unknown): string | undefined [packages/ic-suite/codometer/codometer-configuration/src/modules/configuration/configuration-flags.service.ts:142]
994
+ ↳ Reads an option that carries text, or nothing at all.
995
+ ```
996
+
997
+ **7. `ChangesCommand.parseBaseline`** — depth 3 · decorated-method
998
+
999
+ ```text
1000
+ 🚀 ChangesCommand.parseBaseline(value: unknown): string | undefined [packages/ic-suite/codometer/codometer-cli/src/modules/changes/changes.command.ts:52]
1001
+ ↳ Parse the baseline directory holding a snapshot of the reports.
1002
+ └─> ConfigurationService.parseOptionalOption(value: unknown): string | undefined [packages/ic-suite/codometer/codometer-configuration/src/modules/configuration/configuration.service.ts:125]
1003
+ ↳ Reads an option that carries text, or nothing at all.
1004
+ └─> ConfigurationFlagsService.parseOptionalOption(value: unknown): string | undefined [packages/ic-suite/codometer/codometer-configuration/src/modules/configuration/configuration-flags.service.ts:142]
1005
+ ↳ Reads an option that carries text, or nothing at all.
1006
+ ```
1007
+
1008
+ **8. `ChangesCommand.parseBaselineUrl`** — depth 3 · decorated-method
1009
+
1010
+ ```text
1011
+ 🚀 ChangesCommand.parseBaselineUrl(value: unknown): string | undefined [packages/ic-suite/codometer/codometer-cli/src/modules/changes/changes.command.ts:61]
1012
+ ↳ Parse the run URL the baseline came from, linked from the summary.
1013
+ └─> ConfigurationService.parseOptionalOption(value: unknown): string | undefined [packages/ic-suite/codometer/codometer-configuration/src/modules/configuration/configuration.service.ts:125]
1014
+ ↳ Reads an option that carries text, or nothing at all.
1015
+ └─> ConfigurationFlagsService.parseOptionalOption(value: unknown): string | undefined [packages/ic-suite/codometer/codometer-configuration/src/modules/configuration/configuration-flags.service.ts:142]
1016
+ ↳ Reads an option that carries text, or nothing at all.
1017
+ ```
1018
+
1019
+ **9. `ChangesCommand.parseMarkdown`** — depth 3 · decorated-method
1020
+
1021
+ ```text
1022
+ 🚀 ChangesCommand.parseMarkdown(value: unknown): string | undefined [packages/ic-suite/codometer/codometer-cli/src/modules/changes/changes.command.ts:79]
1023
+ ↳ Parse the markdown document the report is spliced into.
1024
+ └─> ConfigurationService.parseOptionalOption(value: unknown): string | undefined [packages/ic-suite/codometer/codometer-configuration/src/modules/configuration/configuration.service.ts:125]
1025
+ ↳ Reads an option that carries text, or nothing at all.
1026
+ └─> ConfigurationFlagsService.parseOptionalOption(value: unknown): string | undefined [packages/ic-suite/codometer/codometer-configuration/src/modules/configuration/configuration-flags.service.ts:142]
1027
+ ↳ Reads an option that carries text, or nothing at all.
1028
+ ```
1029
+
1030
+ **10. `ChangesCommand.parseOutput`** — depth 3 · decorated-method
1031
+
1032
+ ```text
1033
+ 🚀 ChangesCommand.parseOutput(value: unknown): string | undefined [packages/ic-suite/codometer/codometer-cli/src/modules/changes/changes.command.ts:88]
1034
+ ↳ Parse the file the report is written to on its own.
1035
+ └─> ConfigurationService.parseOptionalOption(value: unknown): string | undefined [packages/ic-suite/codometer/codometer-configuration/src/modules/configuration/configuration.service.ts:125]
1036
+ ↳ Reads an option that carries text, or nothing at all.
1037
+ └─> ConfigurationFlagsService.parseOptionalOption(value: unknown): string | undefined [packages/ic-suite/codometer/codometer-configuration/src/modules/configuration/configuration-flags.service.ts:142]
1038
+ ↳ Reads an option that carries text, or nothing at all.
1039
+ ```
1040
+
1041
+ **11. `main`** — depth 2 · module-bootstrap
1042
+
1043
+ ```text
1044
+ 🚀 main(): Promise<void> [packages/ic-suite/codometer/codometer-cli/src/main.ts:26]
1045
+ ↳ Bootstraps the codometer CLI command application.
1046
+ └─> withDefaultCommand(argv: readonly string[]): string[] [packages/ic-suite/codometer/codometer-cli/src/main.utilities.ts:14]
1047
+ ↳ Inserts the default `measure` subcommand when the command line names no registered command.
1048
+ ```
1049
+
1050
+ </details>
1051
+
1052
+ ### Breadth
1053
+
1054
+ | Callable | Breadth | Calls directly | Location |
1055
+ | --- | --- | --- | --- |
1056
+ | `MeasureCommand.run` | 9 | `MeasureCommand.resolveWorkingDirectory`, `MeasureCommand.resolveRunPlan`, `DestinationsService.listOutputPaths`, `MeasureCommand.announceOutputPaths`, `MeasureService.measure`, `ReportService.build`, `DeliveryService.deliver`, `DestinationsService.selectScope`, `MeasureCommand.reportFindings` | `packages/ic-suite/codometer/codometer-cli/src/modules/measure/measure.command.ts:398` |
1057
+ | `MeasureCommand.resolveRunPlan` | 7 | `ConfigurationService.selectMode`, `MeasureCommand.rejectCommandLine`, `MeasureCommand.readConfiguration`, `MeasureCommand.applyInputsOverride`, `ConfigurationService.resolveFormat`, `DestinationsService.resolveDestinations`, `DestinationsService.resolveConsoleMarkdown` | `packages/ic-suite/codometer/codometer-cli/src/modules/measure/measure.command.ts:226` |
1058
+ | `ChangesCommand.run` | 5 | `ConfigurationService.parseOptionalOption`, `ConfigurationService.parseDirectoryOption`, `ChangesService.collect`, `RenderService.renderSection`, `DocumentsService.emit` | `packages/ic-suite/codometer/codometer-cli/src/modules/changes/changes.command.ts:97` |
1059
+
1060
+ <details>
1061
+ <summary>12 more callables</summary>
1062
+
1063
+ | Callable | Breadth | Calls directly | Location |
1064
+ | --- | --- | --- | --- |
1065
+ | `ConfigurationCommand.run` | 4 | `ConfigurationListingService.describeConfigurations`, `ConfigurationCommand.filter(…)`, `RenderConfigurationService.render`, `ConfigurationListingService.toLimitRows` | `packages/ic-suite/codometer/codometer-cli/src/modules/configuration/configuration.command.ts:110` |
1066
+ | `MeasureCommand.reportFindings` | 4 | `MeasureCommand.reportFailures`, `MeasureCommand.reportStaleness`, `MeasureCommand.reportBreaches`, `MeasureCommand.filter(…)` | `packages/ic-suite/codometer/codometer-cli/src/modules/measure/measure.command.ts:189` |
1067
+ | `MeasureCommand.reportBreaches` | 3 | `MeasureCommand.filter(…)`, `MeasureCommand.filter(…)`, `MeasureCommand.filter(…)` | `packages/ic-suite/codometer/codometer-cli/src/modules/measure/measure.command.ts:142` |
1068
+ | `ChangesCommand.parseBaseline` | 1 | `ConfigurationService.parseOptionalOption` | `packages/ic-suite/codometer/codometer-cli/src/modules/changes/changes.command.ts:52` |
1069
+ | `ChangesCommand.parseBaselineUrl` | 1 | `ConfigurationService.parseOptionalOption` | `packages/ic-suite/codometer/codometer-cli/src/modules/changes/changes.command.ts:61` |
1070
+ | `ChangesCommand.parseDirectory` | 1 | `ConfigurationService.parseDirectoryOption` | `packages/ic-suite/codometer/codometer-cli/src/modules/changes/changes.command.ts:70` |
1071
+ | `ChangesCommand.parseMarkdown` | 1 | `ConfigurationService.parseOptionalOption` | `packages/ic-suite/codometer/codometer-cli/src/modules/changes/changes.command.ts:79` |
1072
+ | `ChangesCommand.parseOutput` | 1 | `ConfigurationService.parseOptionalOption` | `packages/ic-suite/codometer/codometer-cli/src/modules/changes/changes.command.ts:88` |
1073
+ | `ConfigurationCommand.parseDirectory` | 1 | `ConfigurationService.parseDirectoryOption` | `packages/ic-suite/codometer/codometer-cli/src/modules/configuration/configuration.command.ts:74` |
1074
+ | `ConfigurationCommand.parseFormat` | 1 | `ConfigurationService.parseDefaultedOption` | `packages/ic-suite/codometer/codometer-cli/src/modules/configuration/configuration.command.ts:83` |
1075
+ | `MeasureCommand.readConfiguration` | 1 | `ConfigurationService.loadConfiguration` | `packages/ic-suite/codometer/codometer-cli/src/modules/measure/measure.command.ts:111` |
1076
+ | `main` | 1 | `withDefaultCommand` | `packages/ic-suite/codometer/codometer-cli/src/main.ts:26` |
1077
+
1078
+ </details>
1079
+ <!-- callidescope:end -->
1080
+
1081
+ ## 🕸️ Codependix
1082
+
1083
+ Dependency graphs exported by [codependix](https://github.com/Organizzolini/codebase/tree/main/packages/ic-suite/codependix/codependix-cli), regenerated by `nx run codebase:codependix:write`.
1084
+
1085
+ ### Nx Neighborhood
1086
+
1087
+ <!-- codependix:start name="codependix-nx-projects" -->
1088
+ ```mermaid
1089
+ graph LR
1090
+ codometer_cli["codometer-cli"]
1091
+ codometer_configuration["codometer-configuration"]
1092
+ codometer_core["codometer-core"]
1093
+ codometer_examples["codometer-examples"]
1094
+ codometer_measurement["codometer-measurement"]
1095
+ codometer_output["codometer-output"]
1096
+ logger["logger"]
1097
+ codometer_cli --> codometer_configuration
1098
+ codometer_cli --> codometer_core
1099
+ codometer_cli --> codometer_measurement
1100
+ codometer_cli --> codometer_output
1101
+ codometer_cli --> logger
1102
+ codometer_examples -.-> codometer_cli
1103
+ classDef subject fill:#7c3aed,color:#fff,stroke:#4c1d95,stroke-width:2px
1104
+ class codometer_cli subject
1105
+ ```
1106
+
1107
+ _Dashed edges are dependencies Nx inferred from configuration rather than from code._
1108
+ <!-- codependix:end name="codependix-nx-projects" -->
1109
+
1110
+ ### NestJS Module Graph
1111
+
1112
+ <!-- codependix:start name="codependix-nestjs-modules" -->
1113
+ ```mermaid
1114
+ flowchart LR
1115
+ ChangesModule
1116
+ CommentsModule
1117
+ ConfigModule([ConfigModule])
1118
+ ConfigurationListingModule
1119
+ ConfigurationModule
1120
+ CssModule
1121
+ CustomizationModule
1122
+ DeliveryModule
1123
+ DestinationsModule
1124
+ DiscoveryModule
1125
+ DocumentsModule
1126
+ HclModule
1127
+ InputsModule
1128
+ JsonModule
1129
+ JupyterModule
1130
+ LanguagesModule
1131
+ LimitsModule
1132
+ LoggerModule([LoggerModule])
1133
+ MainModule
1134
+ MarkdownModule
1135
+ MeasureModule
1136
+ PythonModule
1137
+ RenderModule
1138
+ ReportModule
1139
+ ShellModule
1140
+ SizeModule
1141
+ SqlModule
1142
+ TomlModule
1143
+ TypescriptModule
1144
+ YamlModule
1145
+ ChangesModule --> ChangesModule
1146
+ ChangesModule --> ConfigurationModule
1147
+ ChangesModule --> DocumentsModule
1148
+ ChangesModule --> RenderModule
1149
+ ConfigurationListingModule --> ConfigurationModule
1150
+ ConfigurationListingModule --> DiscoveryModule
1151
+ ConfigurationModule --> ConfigurationListingModule
1152
+ ConfigurationModule --> ConfigurationModule
1153
+ DeliveryModule --> JsonModule
1154
+ DeliveryModule --> MarkdownModule
1155
+ JupyterModule --> JsonModule
1156
+ JupyterModule --> MarkdownModule
1157
+ JupyterModule --> PythonModule
1158
+ LanguagesModule --> CommentsModule
1159
+ LanguagesModule --> CssModule
1160
+ LanguagesModule --> HclModule
1161
+ LanguagesModule --> JsonModule
1162
+ LanguagesModule --> JupyterModule
1163
+ LanguagesModule --> MarkdownModule
1164
+ LanguagesModule --> PythonModule
1165
+ LanguagesModule --> ShellModule
1166
+ LanguagesModule --> SqlModule
1167
+ LanguagesModule --> TomlModule
1168
+ LanguagesModule --> TypescriptModule
1169
+ LanguagesModule --> YamlModule
1170
+ MainModule --> ChangesModule
1171
+ MainModule --> ConfigurationModule
1172
+ MainModule --> ConfigurationModule
1173
+ MainModule --> DiscoveryModule
1174
+ MainModule --> DiscoveryModule
1175
+ MainModule --> JsonModule
1176
+ MainModule --> MarkdownModule
1177
+ MainModule --> MeasureModule
1178
+ MeasureModule --> ConfigurationModule
1179
+ MeasureModule --> ConfigurationModule
1180
+ MeasureModule --> CustomizationModule
1181
+ MeasureModule --> DeliveryModule
1182
+ MeasureModule --> DestinationsModule
1183
+ MeasureModule --> DiscoveryModule
1184
+ MeasureModule --> InputsModule
1185
+ MeasureModule --> LanguagesModule
1186
+ MeasureModule --> LimitsModule
1187
+ MeasureModule --> MeasureModule
1188
+ MeasureModule --> ReportModule
1189
+ MeasureModule --> SizeModule
1190
+ TypescriptModule --> CommentsModule
1191
+ ```
1192
+
1193
+ _Rounded modules are global: every module can inject them, so their edges are left out._
1194
+ <!-- codependix:end name="codependix-nestjs-modules" -->
1195
+
1196
+ ### File Imports
1197
+
1198
+ <!-- codependix:start name="codependix-file-imports" -->
1199
+ ```mermaid
1200
+ graph LR
1201
+ file_callidescope_config_ts["callidescope.config.ts"]
1202
+ file_codependix_config_ts["codependix.config.ts"]
1203
+ file_codometer_config_ts["codometer.config.ts"]
1204
+ file_eslint_config_ts["eslint.config.ts"]
1205
+ file_src_constants_ts["src/constants.ts"]
1206
+ file_src_index_ts["src/index.ts"]
1207
+ file_src_main_end_to_end_test_ts["src/main.end-to-end.test.ts"]
1208
+ file_src_main_module_ts["src/main.module.ts"]
1209
+ file_src_main_ts["src/main.ts"]
1210
+ file_src_main_utilities_ts["src/main.utilities.ts"]
1211
+ file_src_main_utilities_unit_test_ts["src/main.utilities.unit.test.ts"]
1212
+ file_src_modules_changes_changes_command_ts["src/modules/changes/changes.command.ts"]
1213
+ file_src_modules_changes_changes_command_unit_test_ts["src/modules/changes/changes.command.unit.test.ts"]
1214
+ file_src_modules_changes_changes_constants_ts["src/modules/changes/changes.constants.ts"]
1215
+ file_src_modules_changes_changes_module_ts["src/modules/changes/changes.module.ts"]
1216
+ file_src_modules_changes_changes_types_ts["src/modules/changes/changes.types.ts"]
1217
+ file_src_modules_configuration_configuration_command_ts["src/modules/configuration/configuration.command.ts"]
1218
+ file_src_modules_configuration_configuration_command_unit_test_ts["src/modules/configuration/configuration.command.unit.test.ts"]
1219
+ file_src_modules_configuration_configuration_constants_ts["src/modules/configuration/configuration.constants.ts"]
1220
+ file_src_modules_configuration_configuration_module_ts["src/modules/configuration/configuration.module.ts"]
1221
+ file_src_modules_configuration_configuration_types_ts["src/modules/configuration/configuration.types.ts"]
1222
+ file_src_modules_measure_measure_command_integration_test_ts["src/modules/measure/measure.command.integration.test.ts"]
1223
+ file_src_modules_measure_measure_command_ts["src/modules/measure/measure.command.ts"]
1224
+ file_src_modules_measure_measure_command_unit_test_ts["src/modules/measure/measure.command.unit.test.ts"]
1225
+ file_src_modules_measure_measure_constants_ts["src/modules/measure/measure.constants.ts"]
1226
+ file_src_modules_measure_measure_module_ts["src/modules/measure/measure.module.ts"]
1227
+ file_src_modules_measure_measure_types_ts["src/modules/measure/measure.types.ts"]
1228
+ file_src_repl_ts["src/repl.ts"]
1229
+ file_src_repl_unit_test_ts["src/repl.unit.test.ts"]
1230
+ file_testing_fixture_tree_ts["testing/fixture-tree.ts"]
1231
+ file_testing_mocks_ts["testing/mocks.ts"]
1232
+ file_testing_setup_ts["testing/setup.ts"]
1233
+ file_testing_target_tree_ts["testing/target-tree.ts"]
1234
+ file_vite_config_ts["vite.config.ts"]
1235
+ file_vitest_config_ts["vitest.config.ts"]
1236
+ file_src_main_end_to_end_test_ts --> file_src_constants_ts
1237
+ file_src_main_end_to_end_test_ts --> file_testing_fixture_tree_ts
1238
+ file_src_main_module_ts --> file_src_constants_ts
1239
+ file_src_main_module_ts --> file_src_modules_changes_changes_module_ts
1240
+ file_src_main_module_ts --> file_src_modules_configuration_configuration_module_ts
1241
+ file_src_main_module_ts --> file_src_modules_measure_measure_module_ts
1242
+ file_src_main_ts --> file_src_main_module_ts
1243
+ file_src_main_ts --> file_src_main_utilities_ts
1244
+ file_src_main_utilities_unit_test_ts --> file_src_main_utilities_ts
1245
+ file_src_modules_changes_changes_command_ts --> file_src_modules_changes_changes_types_ts
1246
+ file_src_modules_changes_changes_command_unit_test_ts --> file_src_modules_changes_changes_command_ts
1247
+ file_src_modules_changes_changes_module_ts --> file_src_modules_changes_changes_command_ts
1248
+ file_src_modules_configuration_configuration_command_ts --> file_src_modules_configuration_configuration_constants_ts
1249
+ file_src_modules_configuration_configuration_command_ts --> file_src_modules_configuration_configuration_types_ts
1250
+ file_src_modules_configuration_configuration_command_unit_test_ts --> file_src_modules_configuration_configuration_command_ts
1251
+ file_src_modules_configuration_configuration_module_ts --> file_src_modules_configuration_configuration_command_ts
1252
+ file_src_modules_measure_measure_command_integration_test_ts --> file_src_main_module_ts
1253
+ file_src_modules_measure_measure_command_integration_test_ts --> file_src_modules_measure_measure_command_ts
1254
+ file_src_modules_measure_measure_command_integration_test_ts --> file_testing_fixture_tree_ts
1255
+ file_src_modules_measure_measure_command_ts --> file_src_modules_measure_measure_types_ts
1256
+ file_src_modules_measure_measure_command_unit_test_ts --> file_src_modules_measure_measure_command_ts
1257
+ file_src_modules_measure_measure_command_unit_test_ts --> file_testing_mocks_ts
1258
+ file_src_modules_measure_measure_module_ts --> file_src_modules_measure_measure_command_ts
1259
+ file_src_repl_ts --> file_src_main_module_ts
1260
+ ```
1261
+ <!-- codependix:end name="codependix-file-imports" -->
1262
+
1263
+ <!-- codometer:start -->
1264
+
1265
+ ## ⏲️ Codometer Output
1266
+
1267
+ ### Project
1268
+
1269
+ ![Lines of Code](https://img.shields.io/badge/Lines_of_Code-8988-22c55e?style=flat-square)
1270
+ ![Repository Size](https://img.shields.io/badge/Repository_Size-292.72_kB-6b7280?style=flat-square)
1271
+ ![Folders](https://img.shields.io/badge/Folders-10-4a4a4a?style=flat-square)
1272
+ ![Source Files](https://img.shields.io/badge/Source_Files-62-3178c6?style=flat-square)
1273
+
1274
+ ### Measured Targets
1275
+
1276
+ ![Compiled JavaScript Size](https://img.shields.io/badge/Compiled_JavaScript_Size-37.60_kB_gzip-6b7280?style=flat-square)
1277
+
1278
+ ### TypeScript
1279
+
1280
+ ![TypeScript Files](https://img.shields.io/badge/TypeScript_Files-62-3178c6?style=flat-square)
1281
+ ![Interfaces](https://img.shields.io/badge/Interfaces-44-0ea5e9?style=flat-square)
1282
+ ![Generic Declarations](https://img.shields.io/badge/Generic_Declarations-0-0369a1?style=flat-square)
1283
+ ![Enums](https://img.shields.io/badge/Enums-0-f97316?style=flat-square)
1284
+ ![Decorators](https://img.shields.io/badge/Decorators-37-db2777?style=flat-square)
1285
+ ![Doc Comments](https://img.shields.io/badge/Doc_Comments-253-6366f1?style=flat-square)
1286
+ ![Static Methods](https://img.shields.io/badge/Static_Methods-0-166534?style=flat-square)
1287
+
1288
+ ### JavaScript
1289
+
1290
+ ![JavaScript Files](https://img.shields.io/badge/JavaScript_Files-0-f7df1e?style=flat-square)
1291
+ ![Test Files](https://img.shields.io/badge/Test_Files-16-10b981?style=flat-square)
1292
+ ![External Packages](https://img.shields.io/badge/External_Packages-21-8b5cf6?style=flat-square)
1293
+ ![Classes](https://img.shields.io/badge/Classes-21-7c3aed?style=flat-square)
1294
+ ![Functions](https://img.shields.io/badge/Functions-346-16a34a?style=flat-square)
1295
+ ![Methods](https://img.shields.io/badge/Methods-123-15803d?style=flat-square)
1296
+ ![Sync Functions](https://img.shields.io/badge/Sync_Functions-388-4ade80?style=flat-square)
1297
+ ![Async Functions](https://img.shields.io/badge/Async_Functions-81-059669?style=flat-square)
1298
+ ![Constants](https://img.shields.io/badge/Constants-317-dc2626?style=flat-square)
1299
+ ![Imports](https://img.shields.io/badge/Imports-305-0284c7?style=flat-square)
1300
+ ![Exported Symbols](https://img.shields.io/badge/Exported_Symbols-101-ea580c?style=flat-square)
1301
+ ![Comments](https://img.shields.io/badge/Comments-511-64748b?style=flat-square)
1302
+ ![Comment Lines](https://img.shields.io/badge/Comment_Lines-1166-475569?style=flat-square)
1303
+ ![TODO Comments](https://img.shields.io/badge/TODO_Comments-0-ca8a04?style=flat-square)
1304
+
1305
+ ### Python
1306
+
1307
+ ![Python Files](https://img.shields.io/badge/Python_Files-0-3776ab?style=flat-square)
1308
+ ![Python Lines](https://img.shields.io/badge/Python_Lines-0-4b8bbe?style=flat-square)
1309
+ ![Python Classes](https://img.shields.io/badge/Python_Classes-0-7c3aed?style=flat-square)
1310
+ ![Python Functions](https://img.shields.io/badge/Python_Functions-0-16a34a?style=flat-square)
1311
+ ![Python Protocols](https://img.shields.io/badge/Python_Protocols-0-0ea5e9?style=flat-square)
1312
+ ![Python Constants](https://img.shields.io/badge/Python_Constants-0-dc2626?style=flat-square)
1313
+ ![Python Imports](https://img.shields.io/badge/Python_Imports-0-0284c7?style=flat-square)
1314
+ ![Python Decorators](https://img.shields.io/badge/Python_Decorators-0-db2777?style=flat-square)
1315
+ ![Docstrings](https://img.shields.io/badge/Docstrings-0-6366f1?style=flat-square)
1316
+ ![Docstring Lines](https://img.shields.io/badge/Docstring_Lines-0-818cf8?style=flat-square)
1317
+ ![Python Comments](https://img.shields.io/badge/Python_Comments-0-64748b?style=flat-square)
1318
+ ![Python Comment Lines](https://img.shields.io/badge/Python_Comment_Lines-0-475569?style=flat-square)
1319
+
1320
+ ### JSON
1321
+
1322
+ ![JSON Files](https://img.shields.io/badge/JSON_Files-4-a16207?style=flat-square)
1323
+ ![JSON Lines](https://img.shields.io/badge/JSON_Lines-173-ca8a04?style=flat-square)
1324
+ ![JSON Objects](https://img.shields.io/badge/JSON_Objects-36-7c3aed?style=flat-square)
1325
+ ![JSON Arrays](https://img.shields.io/badge/JSON_Arrays-13-8b5cf6?style=flat-square)
1326
+ ![JSON Properties](https://img.shields.io/badge/JSON_Properties-114-0284c7?style=flat-square)
1327
+ ![JSON Strings](https://img.shields.io/badge/JSON_Strings-95-16a34a?style=flat-square)
1328
+ ![JSON Numbers](https://img.shields.io/badge/JSON_Numbers-1-059669?style=flat-square)
1329
+ ![JSON Booleans](https://img.shields.io/badge/JSON_Booleans-9-0ea5e9?style=flat-square)
1330
+ ![JSON Nulls](https://img.shields.io/badge/JSON_Nulls-0-64748b?style=flat-square)
1331
+ ![JSON Items](https://img.shields.io/badge/JSON_Items-36-475569?style=flat-square)
1332
+ ![JSON Nodes](https://img.shields.io/badge/JSON_Nodes-154-dc2626?style=flat-square)
1333
+ ![JSON Max Depth](https://img.shields.io/badge/JSON_Max_Depth-7-ea580c?style=flat-square)
1334
+
1335
+ ### YAML
1336
+
1337
+ ![YAML Files](https://img.shields.io/badge/YAML_Files-0-cb171e?style=flat-square)
1338
+ ![YAML Lines](https://img.shields.io/badge/YAML_Lines-0-e34c26?style=flat-square)
1339
+ ![YAML Documents](https://img.shields.io/badge/YAML_Documents-0-f97316?style=flat-square)
1340
+ ![YAML Mappings](https://img.shields.io/badge/YAML_Mappings-0-7c3aed?style=flat-square)
1341
+ ![YAML Sequences](https://img.shields.io/badge/YAML_Sequences-0-8b5cf6?style=flat-square)
1342
+ ![YAML Keys](https://img.shields.io/badge/YAML_Keys-0-0284c7?style=flat-square)
1343
+ ![YAML Scalars](https://img.shields.io/badge/YAML_Scalars-0-16a34a?style=flat-square)
1344
+ ![YAML Anchors](https://img.shields.io/badge/YAML_Anchors-0-059669?style=flat-square)
1345
+ ![YAML Aliases](https://img.shields.io/badge/YAML_Aliases-0-10b981?style=flat-square)
1346
+ ![YAML Comments](https://img.shields.io/badge/YAML_Comments-0-64748b?style=flat-square)
1347
+ ![YAML Max Depth](https://img.shields.io/badge/YAML_Max_Depth-0-ea580c?style=flat-square)
1348
+
1349
+ ### TOML
1350
+
1351
+ ![TOML Files](https://img.shields.io/badge/TOML_Files-0-9c4221?style=flat-square)
1352
+ ![TOML Lines](https://img.shields.io/badge/TOML_Lines-0-b45309?style=flat-square)
1353
+ ![TOML Tables](https://img.shields.io/badge/TOML_Tables-0-7c3aed?style=flat-square)
1354
+ ![TOML Array Tables](https://img.shields.io/badge/TOML_Array_Tables-0-8b5cf6?style=flat-square)
1355
+ ![TOML Keys](https://img.shields.io/badge/TOML_Keys-0-0284c7?style=flat-square)
1356
+ ![TOML Arrays](https://img.shields.io/badge/TOML_Arrays-0-16a34a?style=flat-square)
1357
+ ![TOML Comments](https://img.shields.io/badge/TOML_Comments-0-64748b?style=flat-square)
1358
+
1359
+ ### Shell
1360
+
1361
+ ![Shell Files](https://img.shields.io/badge/Shell_Files-0-89e051?style=flat-square)
1362
+ ![Shell Lines](https://img.shields.io/badge/Shell_Lines-0-4eaa25?style=flat-square)
1363
+ ![Shell Functions](https://img.shields.io/badge/Shell_Functions-0-16a34a?style=flat-square)
1364
+ ![Shell Variables](https://img.shields.io/badge/Shell_Variables-0-0284c7?style=flat-square)
1365
+ ![Shell Exports](https://img.shields.io/badge/Shell_Exports-0-ea580c?style=flat-square)
1366
+ ![Shell Conditionals](https://img.shields.io/badge/Shell_Conditionals-0-7c3aed?style=flat-square)
1367
+ ![Shell Loops](https://img.shields.io/badge/Shell_Loops-0-8b5cf6?style=flat-square)
1368
+ ![Shell Pipelines](https://img.shields.io/badge/Shell_Pipelines-0-059669?style=flat-square)
1369
+ ![Shebangs](https://img.shields.io/badge/Shebangs-0-6b7280?style=flat-square)
1370
+ ![Shell Comments](https://img.shields.io/badge/Shell_Comments-0-64748b?style=flat-square)
1371
+ ![Shell Comment Lines](https://img.shields.io/badge/Shell_Comment_Lines-0-475569?style=flat-square)
1372
+
1373
+ ### SQL
1374
+
1375
+ ![SQL Files](https://img.shields.io/badge/SQL_Files-0-e38c00?style=flat-square)
1376
+ ![SQL Lines](https://img.shields.io/badge/SQL_Lines-0-f29111?style=flat-square)
1377
+ ![SQL Statements](https://img.shields.io/badge/SQL_Statements-0-7c3aed?style=flat-square)
1378
+ ![SQL Selects](https://img.shields.io/badge/SQL_Selects-0-16a34a?style=flat-square)
1379
+ ![SQL Inserts](https://img.shields.io/badge/SQL_Inserts-0-22c55e?style=flat-square)
1380
+ ![SQL Updates](https://img.shields.io/badge/SQL_Updates-0-0ea5e9?style=flat-square)
1381
+ ![SQL Deletes](https://img.shields.io/badge/SQL_Deletes-0-dc2626?style=flat-square)
1382
+ ![SQL Creates](https://img.shields.io/badge/SQL_Creates-0-0284c7?style=flat-square)
1383
+ ![SQL Joins](https://img.shields.io/badge/SQL_Joins-0-8b5cf6?style=flat-square)
1384
+ ![SQL CTEs](https://img.shields.io/badge/SQL_CTEs-0-059669?style=flat-square)
1385
+ ![SQL Comments](https://img.shields.io/badge/SQL_Comments-0-64748b?style=flat-square)
1386
+
1387
+ ### HCL
1388
+
1389
+ ![HCL Files](https://img.shields.io/badge/HCL_Files-0-844fba?style=flat-square)
1390
+ ![HCL Lines](https://img.shields.io/badge/HCL_Lines-0-a78bfa?style=flat-square)
1391
+ ![HCL Blocks](https://img.shields.io/badge/HCL_Blocks-0-7c3aed?style=flat-square)
1392
+ ![HCL Resources](https://img.shields.io/badge/HCL_Resources-0-0284c7?style=flat-square)
1393
+ ![HCL Variables](https://img.shields.io/badge/HCL_Variables-0-16a34a?style=flat-square)
1394
+ ![HCL Outputs](https://img.shields.io/badge/HCL_Outputs-0-059669?style=flat-square)
1395
+ ![HCL Attributes](https://img.shields.io/badge/HCL_Attributes-0-0ea5e9?style=flat-square)
1396
+ ![HCL Interpolations](https://img.shields.io/badge/HCL_Interpolations-0-db2777?style=flat-square)
1397
+ ![HCL Comments](https://img.shields.io/badge/HCL_Comments-0-64748b?style=flat-square)
1398
+
1399
+ ### CSS
1400
+
1401
+ ![CSS Files](https://img.shields.io/badge/CSS_Files-0-264de4?style=flat-square)
1402
+ ![CSS Lines](https://img.shields.io/badge/CSS_Lines-0-2965f1?style=flat-square)
1403
+ ![CSS Rules](https://img.shields.io/badge/CSS_Rules-0-7c3aed?style=flat-square)
1404
+ ![CSS Selectors](https://img.shields.io/badge/CSS_Selectors-0-8b5cf6?style=flat-square)
1405
+ ![CSS Declarations](https://img.shields.io/badge/CSS_Declarations-0-0284c7?style=flat-square)
1406
+ ![CSS At Rules](https://img.shields.io/badge/CSS_At_Rules-0-f97316?style=flat-square)
1407
+ ![CSS Media Queries](https://img.shields.io/badge/CSS_Media_Queries-0-ea580c?style=flat-square)
1408
+ ![CSS Custom Properties](https://img.shields.io/badge/CSS_Custom_Properties-0-16a34a?style=flat-square)
1409
+ ![CSS Comments](https://img.shields.io/badge/CSS_Comments-0-64748b?style=flat-square)
1410
+
1411
+ ### Conventions
1412
+
1413
+ ![Module Files](https://img.shields.io/badge/Module_Files-8-7c3aed?style=flat-square)
1414
+ ![Service Files](https://img.shields.io/badge/Service_Files-8-0284c7?style=flat-square)
1415
+ ![Command Files](https://img.shields.io/badge/Command_Files-3-16a34a?style=flat-square)
1416
+ ![Constants Files](https://img.shields.io/badge/Constants_Files-7-ea580c?style=flat-square)
1417
+ ![Types Files](https://img.shields.io/badge/Types_Files-7-db2777?style=flat-square)
1418
+ ![Utilities Files](https://img.shields.io/badge/Utilities_Files-1-0ea5e9?style=flat-square)
1419
+ ![TypeORM Entities](https://img.shields.io/badge/TypeORM_Entities-0-059669?style=flat-square)
1420
+ ![Unit Tests](https://img.shields.io/badge/Unit_Tests-13-ca8a04?style=flat-square)
1421
+ ![Integration Tests](https://img.shields.io/badge/Integration_Tests-2-7c3aed?style=flat-square)
1422
+ ![End To End Tests](https://img.shields.io/badge/End_To_End_Tests-1-0284c7?style=flat-square)
1423
+ ![CSS Comment Budget](https://img.shields.io/badge/CSS_Comment_Budget-0-16a34a?style=flat-square)
1424
+ ![HCL Comment Budget](https://img.shields.io/badge/HCL_Comment_Budget-0-ea580c?style=flat-square)
1425
+ ![Python Comment Budget](https://img.shields.io/badge/Python_Comment_Budget-0-db2777?style=flat-square)
1426
+ ![SQL Comment Budget](https://img.shields.io/badge/SQL_Comment_Budget-0-0ea5e9?style=flat-square)
1427
+ ![TOML Comment Budget](https://img.shields.io/badge/TOML_Comment_Budget-0-059669?style=flat-square)
1428
+ ![TypeScript Comment Budget](https://img.shields.io/badge/TypeScript_Comment_Budget-0-ca8a04?style=flat-square)
1429
+ ![YAML Comment Budget](https://img.shields.io/badge/YAML_Comment_Budget-0-7c3aed?style=flat-square)
1430
+ ![Shell Comment Budget](https://img.shields.io/badge/Shell_Comment_Budget-0-0284c7?style=flat-square)
1431
+
1432
+ ### Jupyter
1433
+
1434
+ ![Notebooks](https://img.shields.io/badge/Notebooks-0-f37626?style=flat-square)
1435
+ ![Notebook Cells](https://img.shields.io/badge/Notebook_Cells-0-e8a33d?style=flat-square)
1436
+ ![Code Cells](https://img.shields.io/badge/Code_Cells-0-3776ab?style=flat-square)
1437
+ ![Markdown Cells](https://img.shields.io/badge/Markdown_Cells-0-083fa1?style=flat-square)
1438
+ ![Raw Cells](https://img.shields.io/badge/Raw_Cells-0-9ca3af?style=flat-square)
1439
+ ![Executed Cells](https://img.shields.io/badge/Executed_Cells-0-16a34a?style=flat-square)
1440
+ ![Cell Outputs](https://img.shields.io/badge/Cell_Outputs-0-059669?style=flat-square)
1441
+ ![Notebook Code Lines](https://img.shields.io/badge/Notebook_Code_Lines-0-4b8bbe?style=flat-square)
1442
+ ![Notebook Classes](https://img.shields.io/badge/Notebook_Classes-0-7c3aed?style=flat-square)
1443
+ ![Notebook Functions](https://img.shields.io/badge/Notebook_Functions-0-22c55e?style=flat-square)
1444
+ ![Notebook Imports](https://img.shields.io/badge/Notebook_Imports-0-0284c7?style=flat-square)
1445
+ ![Notebook Decorators](https://img.shields.io/badge/Notebook_Decorators-0-db2777?style=flat-square)
1446
+ ![Notebook Prose Lines](https://img.shields.io/badge/Notebook_Prose_Lines-0-1f6feb?style=flat-square)
1447
+ ![Notebook Headings](https://img.shields.io/badge/Notebook_Headings-0-a78bfa?style=flat-square)
1448
+ ![Notebook Links](https://img.shields.io/badge/Notebook_Links-0-10b981?style=flat-square)
1449
+ ![Notebook Images](https://img.shields.io/badge/Notebook_Images-0-34d399?style=flat-square)
1450
+ ![Notebook Code Blocks](https://img.shields.io/badge/Notebook_Code_Blocks-0-dc2626?style=flat-square)
1451
+ ![Notebook Properties](https://img.shields.io/badge/Notebook_Properties-0-ca8a04?style=flat-square)
1452
+ ![Notebook Nodes](https://img.shields.io/badge/Notebook_Nodes-0-a16207?style=flat-square)
1453
+ ![Notebook Max Depth](https://img.shields.io/badge/Notebook_Max_Depth-0-ea580c?style=flat-square)
1454
+
1455
+ ### Markdown
1456
+
1457
+ ![Markdown Files](https://img.shields.io/badge/Markdown_Files-1-083fa1?style=flat-square)
1458
+ ![Markdown Lines](https://img.shields.io/badge/Markdown_Lines-247-1f6feb?style=flat-square)
1459
+ ![H1](https://img.shields.io/badge/H1-1-7c3aed?style=flat-square)
1460
+ ![H2](https://img.shields.io/badge/H2-7-8b5cf6?style=flat-square)
1461
+ ![H3](https://img.shields.io/badge/H3-14-a78bfa?style=flat-square)
1462
+ ![H4](https://img.shields.io/badge/H4-0-c4b5fd?style=flat-square)
1463
+ ![H5](https://img.shields.io/badge/H5-0-ddd6fe?style=flat-square)
1464
+ ![H6](https://img.shields.io/badge/H6-0-ede9fe?style=flat-square)
1465
+ ![Paragraphs](https://img.shields.io/badge/Paragraphs-49-64748b?style=flat-square)
1466
+ ![Lists](https://img.shields.io/badge/Lists-6-16a34a?style=flat-square)
1467
+ ![List Items](https://img.shields.io/badge/List_Items-28-22c55e?style=flat-square)
1468
+ ![Task List Items](https://img.shields.io/badge/Task_List_Items-0-4ade80?style=flat-square)
1469
+ ![Tables](https://img.shields.io/badge/Tables-2-0284c7?style=flat-square)
1470
+ ![Table Rows](https://img.shields.io/badge/Table_Rows-10-0ea5e9?style=flat-square)
1471
+ ![Links](https://img.shields.io/badge/Links-9-059669?style=flat-square)
1472
+ ![Images](https://img.shields.io/badge/Images-0-10b981?style=flat-square)
1473
+ ![Code Blocks](https://img.shields.io/badge/Code_Blocks-13-dc2626?style=flat-square)
1474
+ ![Inline Code](https://img.shields.io/badge/Inline_Code-78-ef4444?style=flat-square)
1475
+ ![Block Quotes](https://img.shields.io/badge/Block_Quotes-0-ca8a04?style=flat-square)
1476
+ ![Thematic Breaks](https://img.shields.io/badge/Thematic_Breaks-0-a16207?style=flat-square)
1477
+ <!-- codometer:end -->