@codefast/cli 0.13.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/README.md +58 -39
  3. package/dist/audit/command.js +23 -22
  4. package/dist/audit/comments/domain/comment-content.d.ts +12 -0
  5. package/dist/audit/comments/domain/comment-content.js +17 -4
  6. package/dist/audit/domain/types.d.ts +32 -31
  7. package/dist/audit/layers/cli-result.d.ts +13 -0
  8. package/dist/audit/{constants → layers}/cli-result.js +6 -6
  9. package/dist/audit/layers/cli-schema.d.ts +29 -0
  10. package/dist/audit/layers/cli-schema.js +17 -0
  11. package/dist/audit/layers/domain/layering.d.ts +46 -0
  12. package/dist/audit/layers/domain/layering.js +191 -0
  13. package/dist/audit/layers/output.d.ts +7 -0
  14. package/dist/audit/{constants → layers}/output.js +5 -5
  15. package/dist/audit/layers/prepare.d.ts +25 -0
  16. package/dist/audit/layers/prepare.js +66 -0
  17. package/dist/audit/layers/run.d.ts +19 -0
  18. package/dist/audit/layers/run.js +62 -0
  19. package/dist/audit/prepare.d.ts +13 -1
  20. package/dist/audit/prepare.js +16 -1
  21. package/dist/core/config/schema.d.ts +12 -4
  22. package/dist/core/config/schema.js +13 -1
  23. package/dist/tag/cli-result.d.ts +3 -0
  24. package/dist/tag/cli-result.js +8 -2
  25. package/dist/tag/domain/types.d.ts +15 -0
  26. package/dist/tag/output.js +13 -6
  27. package/dist/tag/run.js +2 -0
  28. package/dist/tag/writer/since-writer.d.ts +5 -0
  29. package/dist/tag/writer/since-writer.js +41 -9
  30. package/package.json +1 -1
  31. package/dist/audit/constants/cli-result.d.ts +0 -13
  32. package/dist/audit/constants/cli-schema.d.ts +0 -18
  33. package/dist/audit/constants/cli-schema.js +0 -12
  34. package/dist/audit/constants/domain/constants.d.ts +0 -8
  35. package/dist/audit/constants/domain/constants.js +0 -66
  36. package/dist/audit/constants/output.d.ts +0 -7
  37. package/dist/audit/constants/prepare.d.ts +0 -16
  38. package/dist/audit/constants/prepare.js +0 -37
  39. package/dist/audit/constants/run.d.ts +0 -14
  40. package/dist/audit/constants/run.js +0 -64
package/CHANGELOG.md CHANGED
@@ -1,5 +1,34 @@
1
1
  # @codefast/cli
2
2
 
3
+ ## 0.14.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#1007](https://github.com/codefastlabs/codefast/pull/1007) Add `codefast audit layers`, which holds a package's `src/` to the layering its architecture states.
8
+ `audit.layers.packages` lists each package's layers bottom to top, every entry a family directly under the root — a
9
+ directory, or a lone module sitting flat — and the audit reports every value import or re-export that points up the
10
+ list, plus every module no layer places. Type-only imports pass whichever way they point; a dynamic `import()` counts as
11
+ a value import. A configured name the workspace does not hold, an entry nested below the root, and a family placed twice
12
+ are refused before anything is scanned. `audit.layers.allowlist` takes the offending import as written, or
13
+ `path:<import>`.
14
+
15
+ - [#990](https://github.com/codefastlabs/codefast/pull/990) Remove `codefast audit constants`, the audit that required every upper-case numeric `const` to name its kind in the
16
+ comment above it.
17
+
18
+ Breaking: the `audit constants` subcommand is gone, and `audit.constants` is no longer a config key, so a
19
+ `codefast.config` that still sets it fails validation as an unknown key; delete that section.
20
+
21
+ - [#992](https://github.com/codefastlabs/codefast/pull/992) `codefast tag` stamps every overload signature again, each in its own doc block. Since the move to `oxc-parser` it
22
+ stamped only an overloaded function's implementation, the one signature a `.d.ts` drops, so a released overload reached
23
+ consumers with no `@since` on any signature they can see.
24
+
25
+ A declaration with no doc block and a `//` comment on the line above it now fails the run instead of getting a fresh
26
+ block there, where the block would stack under a note (which `audit comments` rejects) or split a directive from the
27
+ code it governs. The run names each such declaration by file and line, lists it under `blockedDeclarations` in `--json`,
28
+ leaves its file untouched, and exits `1`: once a release ships the declaration unstamped, a later run can only stamp a
29
+ version that did not introduce it. The `--json` `ok` field now follows the exit code, so a failed target also reports
30
+ `ok: false`.
31
+
3
32
  ## 0.13.0
4
33
 
5
34
  ### Minor Changes
package/README.md CHANGED
@@ -111,9 +111,9 @@ codefast # Codefast monorepo developer CLI
111
111
  │ ├─ links [target] # markdown links pointing at a missing path/anchor
112
112
  │ ├─ imports [target] # banned import forms (React by-name, Zod namespace in front-end, …)
113
113
  │ ├─ assertions [target] # double type assertions through unknown/any (x as unknown as T)
114
- │ ├─ constants [target] # numeric constants whose comment names none of the three kinds
115
114
  │ ├─ display-names [target] # token()/tag()/module names breaking the <namespace>:<Name> convention
116
115
  │ ├─ publish [target] # what breaks a consumer's install: #/ imports, unshipped targets, @source paths
116
+ │ ├─ layers [target] # value imports pointing up a package's configured layers
117
117
  │ └─ comments [target] # section dividers not in the one allowed form
118
118
  │ └─ --fix # rewrite every fixable divider in place (the only audit that writes)
119
119
  │ (each audit also takes [target] + --json)
@@ -145,16 +145,16 @@ Every command also responds to `--help`; each command's section below explains w
145
145
  | `audit links` | Report markdown cross-references that resolve to nothing | no |
146
146
  | `audit imports` | Enforce the import policy (React by-name, Zod namespace in front-end) | no (report only) |
147
147
  | `audit assertions` | Report double type assertions through `unknown` / `any`, tests included | no |
148
- | `audit constants` | Require every tuned numeric constant to name the kind of number it is | no |
149
148
  | `audit display-names` | Enforce the `namespace:Name` display-name convention | no |
150
149
  | `audit publish` | Report what would break a consumer's install of a published package | no |
150
+ | `audit layers` | Report value imports that point up a package's configured layers | no |
151
151
  | `audit comments` | Check doc-comment conventions; repair section dividers | `--fix` only |
152
152
 
153
- **Which of these are for you?** `arrange`, `mirror`, `pack-slim`, `tag`, `audit links`, `audit assertions`, and
154
- `audit publish` are general-purpose — they work for any pnpm workspace or single package that builds with `tsc`. The
155
- other five audits encode codefast's own house style (logical Tailwind directions, named React imports, a specific
156
- comment/divider grammar, a `namespace:Name` scheme for `@codefast/di` tokens, a named kind for every tuned numeric
157
- constant). Adopt them if they fit your project; otherwise skip them, or use an allowlist to narrow their scope.
153
+ **Which of these are for you?** `arrange`, `mirror`, `pack-slim`, `tag`, `audit links`, `audit assertions`,
154
+ `audit layers`, and `audit publish` are general-purpose — they work for any pnpm workspace or single package that builds
155
+ with `tsc`. The other four audits encode codefast's own house style (logical Tailwind directions, named React imports, a
156
+ specific comment/divider grammar, a `namespace:Name` scheme for `@codefast/di` tokens). Adopt them if they fit your
157
+ project; otherwise skip them, or use an allowlist to narrow their scope.
158
158
 
159
159
  ## `arrange`
160
160
 
@@ -276,9 +276,15 @@ Exits `1` when any package fails, `0` otherwise.
276
276
  ## `tag`
277
277
 
278
278
  Adds `@since <version>` tags to the doc comments of exported declarations that lack one, creating the doc block when
279
- there is none. The version comes from the nearest `package.json` above each target file, and declarations that already
280
- carry `@since` are left alone. Run it at release time so published APIs carry accurate version metadata — never
281
- hand-write `@since`.
279
+ there is none. Overload signatures are declarations too, each stamped on its own, because the `.d.ts` keeps every
280
+ overload's doc block and drops the implementation's. The version comes from the nearest `package.json` above each target
281
+ file, and declarations that already carry `@since` are left alone. Run it at release time so published APIs carry
282
+ accurate version metadata — never hand-write `@since`.
283
+
284
+ A declaration with no doc block and a `//` comment on the line above it is reported instead: a block written there would
285
+ stack under a note, which `audit comments` rejects, or split a directive from the code it governs. Its file is left as
286
+ it is, so every reported line stays accurate. Write the doc block by hand before the release that ships the declaration,
287
+ since a later run can only stamp a version that did not introduce it.
282
288
 
283
289
  ```bash
284
290
  codefast tag # auto-discover packages from cwd (or the single package)
@@ -286,7 +292,8 @@ codefast tag packages/ui/src # tag one directory or file
286
292
  codefast tag --dry-run # summary only, no writes
287
293
  ```
288
294
 
289
- Exits `1` when no target is selected, when any target fails, or when the `tag.onAfterWrite` hook fails.
295
+ Exits `1` when no target is selected, when any target fails, when a declaration is left unstamped, or when the
296
+ `tag.onAfterWrite` hook fails.
290
297
 
291
298
  ## `audit`
292
299
 
@@ -385,6 +392,41 @@ codefast audit publish # every published package in the work
385
392
  codefast audit publish --json # machine-readable summary
386
393
  ```
387
394
 
395
+ ### `audit layers`
396
+
397
+ _General-purpose._ Holds a package's `src/` to the layering its architecture states. You list the layers in
398
+ configuration, bottom to top, each naming the families directly under the root — a directory, or a lone module sitting
399
+ flat (`errors.ts`) — and the audit reports every value import or re-export that points up the list, plus every module no
400
+ layer places. Type-only imports erase at build time and couple nothing, so they may point anywhere; a dynamic `import()`
401
+ counts as a value import wherever it sits.
402
+
403
+ ```js
404
+ // codefast.config.js
405
+ export default {
406
+ audit: {
407
+ layers: {
408
+ packages: {
409
+ "@acme/di": {
410
+ root: "src", // where the layers sit, relative to the package (default)
411
+ layers: [["core", "errors.ts"], ["engine"], ["container"], ["index.ts"]],
412
+ },
413
+ },
414
+ allowlist: [],
415
+ },
416
+ },
417
+ };
418
+ ```
419
+
420
+ ```bash
421
+ codefast audit layers # every package audit.layers.packages names
422
+ codefast audit layers packages/di # one package, or a directory beneath its root
423
+ codefast audit layers --json # machine-readable summary
424
+ ```
425
+
426
+ A configured package name the workspace does not hold, a layer entry nested below the root, and a family placed twice
427
+ are refused before anything is scanned. Configure exceptions via `audit.layers.allowlist` — each entry is the offending
428
+ import as written or `repo/relative/path.ts:<import>`.
429
+
388
430
  ### `audit comments`
389
431
 
390
432
  _House style._ Checks doc-comment conventions. Section dividers not in the one allowed form are mechanical, so `--fix`
@@ -420,30 +462,6 @@ codefast audit display-names --json # machine-readable summary
420
462
  Configure exceptions via `audit.displayNames.allowlist` — each entry is the call as written, through its closing quote
421
463
  (or parenthesis when the name is the only argument), or `repo/relative/path.ts:<call>`.
422
464
 
423
- ### `audit constants`
424
-
425
- _House style._ Holds library sources to one rule for tuned numbers: a numeric constant says which kind of number it is.
426
- It flags an upper-case `const NAME = <number>` in a `.ts`/`.tsx` file under a `src` directory — tests, benchmarks, apps,
427
- and examples are out of scope — unless the comment directly above it, a `/* … */` block or a run of `//` lines, names
428
- one of three kinds:
429
-
430
- - `a constant of the machine` — a width of the platform the code runs on;
431
- - `a value the contract fixes` — a number the documented contract promises;
432
- - `derived from bind-time data` — a figure computed from what a caller hands in.
433
-
434
- A count that merely looks reasonable is none of them, so it is reported. `0`, `1`, and `-1` are skipped: they stand for
435
- absence or identity, not for a tuned size.
436
-
437
- ```bash
438
- codefast audit constants # uses audit.constants.target from config
439
- codefast audit constants packages/di/src # explicit target
440
- codefast audit constants --json # machine-readable summary
441
- ```
442
-
443
- With no `[target]`, the scan root is `audit.constants.target` from the config; when neither is set the command fails.
444
- Configure exceptions via `audit.constants.allowlist` — each entry is the constant's name or `repo/relative/path.ts:NAME`
445
- — for a measured policy that has to stay a tuned number.
446
-
447
465
  ## Configuration
448
466
 
449
467
  **You do not need a config file.** Every command has sensible defaults and works with none. Add a `codefast.config.*`
@@ -579,9 +597,10 @@ export default {
579
597
  imports: { allowlist: [] }, // offending import text as written, or `repo/relative/path.tsx:<text>`
580
598
  assertions: { allowlist: [] }, // assertion as written, or `repo/relative/path.ts:<assertion>`
581
599
  displayNames: { allowlist: [] }, // call as written, or `repo/relative/path.ts:<call>`
582
- constants: {
583
- target: "packages/core/src", // default scan root when no CLI arg is passed
584
- allowlist: [], // constant name, or `repo/relative/path.ts:NAME`
600
+ layers: {
601
+ // per package: its layers bottom to top, each entry a directory or module file under `root` (default `src`)
602
+ packages: { "@acme/di": { layers: [["core", "errors.ts"], ["engine"], ["index.ts"]] } },
603
+ allowlist: [], // import as written, or `repo/relative/path.ts:<import>`
585
604
  },
586
605
  },
587
606
  };
@@ -635,7 +654,7 @@ pnpm run cli:audit:comments # codefast audit comments
635
654
  pnpm run cli:audit:imports # codefast audit imports
636
655
  pnpm run cli:audit:assertions # codefast audit assertions
637
656
  pnpm run cli:audit:display-names # codefast audit display-names
638
- pnpm run cli:audit:constants # codefast audit constants
657
+ pnpm run cli:audit:layers # codefast audit layers
639
658
  pnpm run cli:audit:publish # codefast audit publish
640
659
  ```
641
660
 
@@ -9,11 +9,6 @@ import { commentAuditRunRequestSchema } from "#audit/comments/cli-schema";
9
9
  import { presentCommentAuditResult } from "#audit/comments/output";
10
10
  import { prepareCommentAudit } from "#audit/comments/prepare";
11
11
  import { runCommentAudit } from "#audit/comments/run";
12
- import { exitCodeForConstantAuditResult, formatConstantAuditJsonOutput } from "#audit/constants/cli-result";
13
- import { constantAuditRunRequestSchema } from "#audit/constants/cli-schema";
14
- import { presentConstantAuditResult } from "#audit/constants/output";
15
- import { prepareConstantAudit } from "#audit/constants/prepare";
16
- import { runConstantAudit } from "#audit/constants/run";
17
12
  import { exitCodeForDisplayNameAuditResult, formatDisplayNameAuditJsonOutput } from "#audit/display-names/cli-result";
18
13
  import { displayNameAuditRunRequestSchema } from "#audit/display-names/cli-schema";
19
14
  import { presentDisplayNameAuditResult } from "#audit/display-names/output";
@@ -24,6 +19,11 @@ import { importsAuditRunRequestSchema } from "#audit/imports/cli-schema";
24
19
  import { presentImportsAuditResult } from "#audit/imports/output";
25
20
  import { prepareImportsAudit } from "#audit/imports/prepare";
26
21
  import { runImportsAudit } from "#audit/imports/run";
22
+ import { exitCodeForLayersAuditResult, formatLayersAuditJsonOutput } from "#audit/layers/cli-result";
23
+ import { layersAuditRunRequestSchema } from "#audit/layers/cli-schema";
24
+ import { presentLayersAuditResult } from "#audit/layers/output";
25
+ import { prepareLayersAudit } from "#audit/layers/prepare";
26
+ import { runLayersAudit } from "#audit/layers/run";
27
27
  import { exitCodeForLinkAuditResult, formatLinkAuditJsonOutput } from "#audit/links/cli-result";
28
28
  import { linkAuditRunRequestSchema } from "#audit/links/cli-schema";
29
29
  import { presentLinkAuditResult } from "#audit/links/output";
@@ -121,22 +121,6 @@ const displayNamesCheck = {
121
121
  formatJson: formatDisplayNameAuditJsonOutput,
122
122
  exitCode: exitCodeForDisplayNameAuditResult,
123
123
  };
124
- const constantsCheck = {
125
- name: "constants",
126
- description: "Report numeric constants whose comment names none of the three kinds a number may be",
127
- targetHelp: "Directory or file to scan (default: audit.constants.target from config)",
128
- schema: constantAuditRunRequestSchema,
129
- prepare: prepareConstantAudit,
130
- buildRequest: baseAuditRequest,
131
- run: (fs, request) => runConstantAudit(fs, {
132
- rootDir: request.rootDir,
133
- targetPath: request.targetPath,
134
- allowlist: request.allowlist ?? [],
135
- }),
136
- present: presentConstantAuditResult,
137
- formatJson: formatConstantAuditJsonOutput,
138
- exitCode: exitCodeForConstantAuditResult,
139
- };
140
124
  const commentsCheck = {
141
125
  name: "comments",
142
126
  description: "Report section dividers that are not in the repo's one allowed form",
@@ -157,6 +141,23 @@ const commentsCheck = {
157
141
  command.option("--fix", "Rewrite every mechanically fixable divider in place", false);
158
142
  },
159
143
  };
144
+ const layersCheck = {
145
+ name: "layers",
146
+ description: "Report value imports that point up a package's configured layers, and modules sitting in no layer",
147
+ targetHelp: "Directory or file to scan (default: the repo root, reaching every package audit.layers names)",
148
+ schema: layersAuditRunRequestSchema,
149
+ prepare: prepareLayersAudit,
150
+ buildRequest: (prelude, opts) => ({ ...baseAuditRequest(prelude, opts), packages: prelude.packages }),
151
+ run: (fs, request) => runLayersAudit(fs, {
152
+ rootDir: request.rootDir,
153
+ targetPath: request.targetPath,
154
+ allowlist: request.allowlist ?? [],
155
+ packages: request.packages,
156
+ }),
157
+ present: presentLayersAuditResult,
158
+ formatJson: formatLayersAuditJsonOutput,
159
+ exitCode: exitCodeForLayersAuditResult,
160
+ };
160
161
  const publishCheck = {
161
162
  name: "publish",
162
163
  description: "Report what breaks a consumer's install: #/ imports, unshipped exports/imports targets, and stylesheet @source paths reaching nothing shipped",
@@ -200,8 +201,8 @@ export function createAuditCommand() {
200
201
  registerPipelineSubcommand(cmd, nodeFilesystem, auditCheckToPipeline(importsCheck));
201
202
  registerPipelineSubcommand(cmd, nodeFilesystem, auditCheckToPipeline(assertionsCheck));
202
203
  registerPipelineSubcommand(cmd, nodeFilesystem, auditCheckToPipeline(displayNamesCheck));
203
- registerPipelineSubcommand(cmd, nodeFilesystem, auditCheckToPipeline(constantsCheck));
204
204
  registerPipelineSubcommand(cmd, nodeFilesystem, auditCheckToPipeline(commentsCheck));
205
+ registerPipelineSubcommand(cmd, nodeFilesystem, auditCheckToPipeline(layersCheck));
205
206
  registerPipelineSubcommand(cmd, nodeFilesystem, auditCheckToPipeline(publishCheck));
206
207
  return cmd;
207
208
  }
@@ -18,6 +18,18 @@ export interface CommentContentFinding {
18
18
  readonly raw: string;
19
19
  readonly defect: CommentContentDefectKind;
20
20
  }
21
+ /**
22
+ * Returns whether a line is a `//` note: a line comment that is neither a divider nor a tooling directive.
23
+ *
24
+ * @since 0.14.0
25
+ */
26
+ export declare function isNoteLine(line: string): boolean;
27
+ /**
28
+ * Returns whether a line is a `//` tooling directive, which governs the code below it.
29
+ *
30
+ * @since 0.14.0
31
+ */
32
+ export declare function isDirectiveLine(line: string): boolean;
21
33
  /**
22
34
  * Scans a source file's comments for banned content, in source order.
23
35
  *
@@ -18,6 +18,22 @@ const declarationPattern = /^[ \t]*(?:export|const|let|var|function|class|interf
18
18
  // A divider or a tooling directive above a doc block is not a stacked note.
19
19
  const dividerLinePattern = /^[ \t]*\/\/[ \t]*[-=─_*~#]{2,}/;
20
20
  const directiveLinePattern = /^[ \t]*\/\/[ \t]*(?:oxlint-|eslint-|@ts-|prettier-)/;
21
+ /**
22
+ * Returns whether a line is a `//` note: a line comment that is neither a divider nor a tooling directive.
23
+ *
24
+ * @since 0.14.0
25
+ */
26
+ export function isNoteLine(line) {
27
+ return lineCommentPattern.test(line) && !dividerLinePattern.test(line) && !isDirectiveLine(line);
28
+ }
29
+ /**
30
+ * Returns whether a line is a `//` tooling directive, which governs the code below it.
31
+ *
32
+ * @since 0.14.0
33
+ */
34
+ export function isDirectiveLine(line) {
35
+ return directiveLinePattern.test(line);
36
+ }
21
37
  /**
22
38
  * Scans a source file's comments for banned content, in source order.
23
39
  *
@@ -37,10 +53,7 @@ export function scanCommentContent(content, language) {
37
53
  // A `//` run stacked directly above a doc block reads as a second doc — it belongs inside.
38
54
  if (language === "js" && !insideBlock && trimmed.startsWith("/**")) {
39
55
  let runStart = index;
40
- while (runStart > 0 &&
41
- lineCommentPattern.test(lines[runStart - 1]) &&
42
- !dividerLinePattern.test(lines[runStart - 1]) &&
43
- !directiveLinePattern.test(lines[runStart - 1])) {
56
+ while (runStart > 0 && isNoteLine(lines[runStart - 1])) {
44
57
  runStart--;
45
58
  }
46
59
  if (runStart < index) {
@@ -135,37 +135,6 @@ export type DisplayNameAuditResult = {
135
135
  readonly allowlistedCount: number;
136
136
  readonly scannedFileCount: number;
137
137
  };
138
- /**
139
- * A numeric constant whose comment names none of the three kinds.
140
- *
141
- * @since 0.11.0
142
- */
143
- export type ConstantViolation = {
144
- readonly line: number;
145
- /** The declaration's name and value, as `NAME = 32`. */
146
- readonly raw: string;
147
- readonly reason: string;
148
- };
149
- /**
150
- * The numeric-constant violations found in one file.
151
- *
152
- * @since 0.11.0
153
- */
154
- export type ConstantFileViolations = {
155
- readonly relativePath: string;
156
- readonly violations: Array<ConstantViolation>;
157
- };
158
- /**
159
- * Outcome of one `audit constants` run.
160
- *
161
- * @since 0.11.0
162
- */
163
- export type ConstantAuditResult = {
164
- readonly files: Array<ConstantFileViolations>;
165
- readonly violationCount: number;
166
- readonly allowlistedCount: number;
167
- readonly scannedFileCount: number;
168
- };
169
138
  /**
170
139
  * A broken link or anchor found by the link audit.
171
140
  *
@@ -296,4 +265,36 @@ export type PublishAuditResult = {
296
265
  readonly legacyImportCount: number;
297
266
  readonly scannedFileCount: number;
298
267
  readonly packageCount: number;
268
+ };
269
+ /**
270
+ * A module outside every configured layer, or a value import that points up the layers.
271
+ *
272
+ * @since 0.14.0
273
+ */
274
+ export type LayerViolation = {
275
+ readonly line: number;
276
+ /** The import or re-export as written, or the module path when the module itself is unplaced. */
277
+ readonly raw: string;
278
+ readonly reason: string;
279
+ };
280
+ /**
281
+ * The layering violations found in one file.
282
+ *
283
+ * @since 0.14.0
284
+ */
285
+ export type LayerFileViolations = {
286
+ readonly relativePath: string;
287
+ readonly violations: Array<LayerViolation>;
288
+ };
289
+ /**
290
+ * Outcome of one `audit layers` run.
291
+ *
292
+ * @since 0.14.0
293
+ */
294
+ export type LayersAuditResult = {
295
+ readonly files: Array<LayerFileViolations>;
296
+ readonly violationCount: number;
297
+ readonly allowlistedCount: number;
298
+ readonly scannedFileCount: number;
299
+ readonly packageCount: number;
299
300
  };
@@ -0,0 +1,13 @@
1
+ import type { LayersAuditResult } from "#audit/domain/types";
2
+ /**
3
+ * Exit `1` when any non-allowlisted layering violation remains.
4
+ *
5
+ * @since 0.14.0
6
+ */
7
+ export declare function exitCodeForLayersAuditResult(result: LayersAuditResult): number;
8
+ /**
9
+ * Machine-readable layering summary for `--json`.
10
+ *
11
+ * @since 0.14.0
12
+ */
13
+ export declare function formatLayersAuditJsonOutput(result: LayersAuditResult, rootDir: string): string;
@@ -1,18 +1,18 @@
1
1
  import { CLI_EXIT_GENERAL_ERROR, CLI_EXIT_SUCCESS } from "#core/exit-codes";
2
2
  /**
3
- * Exit `1` when any non-allowlisted numeric constant is unlabelled.
3
+ * Exit `1` when any non-allowlisted layering violation remains.
4
4
  *
5
- * @since 0.11.0
5
+ * @since 0.14.0
6
6
  */
7
- export function exitCodeForConstantAuditResult(result) {
7
+ export function exitCodeForLayersAuditResult(result) {
8
8
  return result.violationCount > 0 ? CLI_EXIT_GENERAL_ERROR : CLI_EXIT_SUCCESS;
9
9
  }
10
10
  /**
11
- * Machine-readable numeric-constant summary for `--json`.
11
+ * Machine-readable layering summary for `--json`.
12
12
  *
13
- * @since 0.11.0
13
+ * @since 0.14.0
14
14
  */
15
- export function formatConstantAuditJsonOutput(result, rootDir) {
15
+ export function formatLayersAuditJsonOutput(result, rootDir) {
16
16
  return JSON.stringify({
17
17
  schemaVersion: 1,
18
18
  ok: result.violationCount === 0,
@@ -0,0 +1,29 @@
1
+ import * as z from "zod";
2
+ /**
3
+ * One layered package as the run reads it: its name, the absolute root the layers sit under, and the layers.
4
+ *
5
+ * @since 0.14.0
6
+ */
7
+ export type LayersAuditPackage = {
8
+ readonly name: string;
9
+ readonly rootPath: string;
10
+ readonly layers: ReadonlyArray<ReadonlyArray<string>>;
11
+ };
12
+ /**
13
+ * Resolved request for a single layering audit run.
14
+ *
15
+ * @since 0.14.0
16
+ */
17
+ export type LayersAuditRunRequest = {
18
+ readonly rootDir: string;
19
+ readonly targetPath: string;
20
+ readonly allowlist?: ReadonlyArray<string> | undefined;
21
+ readonly json: boolean;
22
+ readonly packages: ReadonlyArray<LayersAuditPackage>;
23
+ };
24
+ /**
25
+ * Zod schema for {@link LayersAuditRunRequest}.
26
+ *
27
+ * @since 0.14.0
28
+ */
29
+ export declare const layersAuditRunRequestSchema: z.ZodType<LayersAuditRunRequest>;
@@ -0,0 +1,17 @@
1
+ import * as z from "zod";
2
+ /**
3
+ * Zod schema for {@link LayersAuditRunRequest}.
4
+ *
5
+ * @since 0.14.0
6
+ */
7
+ export const layersAuditRunRequestSchema = z.object({
8
+ rootDir: z.string().min(1),
9
+ targetPath: z.string().min(1),
10
+ allowlist: z.array(z.string()).optional(),
11
+ json: z.boolean(),
12
+ packages: z.array(z.object({
13
+ name: z.string().min(1),
14
+ rootPath: z.string().min(1),
15
+ layers: z.array(z.array(z.string().min(1))),
16
+ })),
17
+ });
@@ -0,0 +1,46 @@
1
+ import type { LayerViolation } from "#audit/domain/types";
2
+ /**
3
+ * The key a layer entry and a module path share: the first path segment, without a module extension.
4
+ *
5
+ * @remarks `errors.ts`, `errors/` and `errors/taxonomy.ts` all key as `errors`, so a layer entry names
6
+ * a family directly under the root — a directory, or a lone module sitting flat.
7
+ *
8
+ * @since 0.14.0
9
+ */
10
+ export declare function layerKeyOf(modulePath: string): string;
11
+ /**
12
+ * Returns why a layer list breaks its contract, or `undefined` when every entry is a family under the root, placed once.
13
+ *
14
+ * @since 0.14.0
15
+ */
16
+ export declare function invalidLayerEntry(layers: ReadonlyArray<ReadonlyArray<string>>): string | undefined;
17
+ /**
18
+ * Where a module sits: its layer's position from the bottom, and the entry that placed it there.
19
+ *
20
+ * @since 0.14.0
21
+ */
22
+ export interface LayerPlacement {
23
+ readonly index: number;
24
+ readonly entry: string;
25
+ }
26
+ /**
27
+ * A package's layers, bottom to top, answering the placement of any module path under the root.
28
+ *
29
+ * @since 0.14.0
30
+ */
31
+ export declare class LayerMap {
32
+ #private;
33
+ constructor(layers: ReadonlyArray<ReadonlyArray<string>>);
34
+ /** The placement of a root-relative module path, or `undefined` when no layer names its family. */
35
+ placementOf(modulePath: string): LayerPlacement | undefined;
36
+ }
37
+ /**
38
+ * Scans one module against its package's layers and returns the violations: the module sitting in no
39
+ * layer, or a value import or re-export whose target sits in a higher layer, or in none.
40
+ *
41
+ * @remarks Type-only imports and re-exports erase at build time and couple nothing, so they pass
42
+ * whichever way they point. Dynamic `import()` counts as a value import wherever it sits.
43
+ *
44
+ * @since 0.14.0
45
+ */
46
+ export declare function auditLayeringSource(filePath: string, modulePath: string, sourceText: string, layers: LayerMap): Array<LayerViolation>;