@ploaness/governance 1.0.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 (199) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +13 -0
  3. package/dist/access-boundary.d.ts +41 -0
  4. package/dist/access-boundary.d.ts.map +1 -0
  5. package/dist/access-boundary.js +26 -0
  6. package/dist/access-boundary.js.map +1 -0
  7. package/dist/agent-references.d.ts +12 -0
  8. package/dist/agent-references.d.ts.map +1 -0
  9. package/dist/agent-references.js +53 -0
  10. package/dist/agent-references.js.map +1 -0
  11. package/dist/architecture-layers.d.ts +11 -0
  12. package/dist/architecture-layers.d.ts.map +1 -0
  13. package/dist/architecture-layers.js +47 -0
  14. package/dist/architecture-layers.js.map +1 -0
  15. package/dist/asset-policy.d.ts +138 -0
  16. package/dist/asset-policy.d.ts.map +1 -0
  17. package/dist/asset-policy.js +312 -0
  18. package/dist/asset-policy.js.map +1 -0
  19. package/dist/banned-typography.d.ts +13 -0
  20. package/dist/banned-typography.d.ts.map +1 -0
  21. package/dist/banned-typography.js +78 -0
  22. package/dist/banned-typography.js.map +1 -0
  23. package/dist/bundle-budget.d.ts +16 -0
  24. package/dist/bundle-budget.d.ts.map +1 -0
  25. package/dist/bundle-budget.js +37 -0
  26. package/dist/bundle-budget.js.map +1 -0
  27. package/dist/commit-message.d.ts +37 -0
  28. package/dist/commit-message.d.ts.map +1 -0
  29. package/dist/commit-message.js +144 -0
  30. package/dist/commit-message.js.map +1 -0
  31. package/dist/config-references.d.ts +17 -0
  32. package/dist/config-references.d.ts.map +1 -0
  33. package/dist/config-references.js +54 -0
  34. package/dist/config-references.js.map +1 -0
  35. package/dist/container-failure.d.ts +40 -0
  36. package/dist/container-failure.d.ts.map +1 -0
  37. package/dist/container-failure.js +154 -0
  38. package/dist/container-failure.js.map +1 -0
  39. package/dist/container-files.d.ts +36 -0
  40. package/dist/container-files.d.ts.map +1 -0
  41. package/dist/container-files.js +74 -0
  42. package/dist/container-files.js.map +1 -0
  43. package/dist/container-freshness.d.ts +102 -0
  44. package/dist/container-freshness.d.ts.map +1 -0
  45. package/dist/container-freshness.js +129 -0
  46. package/dist/container-freshness.js.map +1 -0
  47. package/dist/dependency-freshness.d.ts +129 -0
  48. package/dist/dependency-freshness.d.ts.map +1 -0
  49. package/dist/dependency-freshness.js +161 -0
  50. package/dist/dependency-freshness.js.map +1 -0
  51. package/dist/doc-blocks.d.ts +13 -0
  52. package/dist/doc-blocks.d.ts.map +1 -0
  53. package/dist/doc-blocks.js +75 -0
  54. package/dist/doc-blocks.js.map +1 -0
  55. package/dist/document-references.d.ts +44 -0
  56. package/dist/document-references.d.ts.map +1 -0
  57. package/dist/document-references.js +94 -0
  58. package/dist/document-references.js.map +1 -0
  59. package/dist/editorconfig-policy.d.ts +24 -0
  60. package/dist/editorconfig-policy.d.ts.map +1 -0
  61. package/dist/editorconfig-policy.js +106 -0
  62. package/dist/editorconfig-policy.js.map +1 -0
  63. package/dist/environment-files.d.ts +42 -0
  64. package/dist/environment-files.d.ts.map +1 -0
  65. package/dist/environment-files.js +76 -0
  66. package/dist/environment-files.js.map +1 -0
  67. package/dist/file-roles.d.ts +39 -0
  68. package/dist/file-roles.d.ts.map +1 -0
  69. package/dist/file-roles.js +82 -0
  70. package/dist/file-roles.js.map +1 -0
  71. package/dist/generated-denial.d.ts +42 -0
  72. package/dist/generated-denial.d.ts.map +1 -0
  73. package/dist/generated-denial.js +87 -0
  74. package/dist/generated-denial.js.map +1 -0
  75. package/dist/history-policy.d.ts +32 -0
  76. package/dist/history-policy.d.ts.map +1 -0
  77. package/dist/history-policy.js +50 -0
  78. package/dist/history-policy.js.map +1 -0
  79. package/dist/image-assets.d.ts +14 -0
  80. package/dist/image-assets.d.ts.map +1 -0
  81. package/dist/image-assets.js +205 -0
  82. package/dist/image-assets.js.map +1 -0
  83. package/dist/index.d.ts +49 -0
  84. package/dist/index.d.ts.map +1 -0
  85. package/dist/index.js +53 -0
  86. package/dist/index.js.map +1 -0
  87. package/dist/install-policy.d.ts +35 -0
  88. package/dist/install-policy.d.ts.map +1 -0
  89. package/dist/install-policy.js +61 -0
  90. package/dist/install-policy.js.map +1 -0
  91. package/dist/json-shapes.d.ts +86 -0
  92. package/dist/json-shapes.d.ts.map +1 -0
  93. package/dist/json-shapes.js +124 -0
  94. package/dist/json-shapes.js.map +1 -0
  95. package/dist/license-allowlist.d.ts +15 -0
  96. package/dist/license-allowlist.d.ts.map +1 -0
  97. package/dist/license-allowlist.js +157 -0
  98. package/dist/license-allowlist.js.map +1 -0
  99. package/dist/network-policy.d.ts +69 -0
  100. package/dist/network-policy.d.ts.map +1 -0
  101. package/dist/network-policy.js +173 -0
  102. package/dist/network-policy.js.map +1 -0
  103. package/dist/paired-assets.d.ts +34 -0
  104. package/dist/paired-assets.d.ts.map +1 -0
  105. package/dist/paired-assets.js +63 -0
  106. package/dist/paired-assets.js.map +1 -0
  107. package/dist/payload-access.d.ts +9 -0
  108. package/dist/payload-access.d.ts.map +1 -0
  109. package/dist/payload-access.js +168 -0
  110. package/dist/payload-access.js.map +1 -0
  111. package/dist/payload-policy.d.ts +18 -0
  112. package/dist/payload-policy.d.ts.map +1 -0
  113. package/dist/payload-policy.js +124 -0
  114. package/dist/payload-policy.js.map +1 -0
  115. package/dist/payload-source.d.ts +29 -0
  116. package/dist/payload-source.d.ts.map +1 -0
  117. package/dist/payload-source.js +84 -0
  118. package/dist/payload-source.js.map +1 -0
  119. package/dist/release-age.d.ts +23 -0
  120. package/dist/release-age.d.ts.map +1 -0
  121. package/dist/release-age.js +38 -0
  122. package/dist/release-age.js.map +1 -0
  123. package/dist/request-limits.d.ts +39 -0
  124. package/dist/request-limits.d.ts.map +1 -0
  125. package/dist/request-limits.js +57 -0
  126. package/dist/request-limits.js.map +1 -0
  127. package/dist/run-plan.d.ts +32 -0
  128. package/dist/run-plan.d.ts.map +1 -0
  129. package/dist/run-plan.js +43 -0
  130. package/dist/run-plan.js.map +1 -0
  131. package/dist/run-policy.d.ts +32 -0
  132. package/dist/run-policy.d.ts.map +1 -0
  133. package/dist/run-policy.js +37 -0
  134. package/dist/run-policy.js.map +1 -0
  135. package/dist/secret-policy.d.ts +13 -0
  136. package/dist/secret-policy.d.ts.map +1 -0
  137. package/dist/secret-policy.js +69 -0
  138. package/dist/secret-policy.js.map +1 -0
  139. package/dist/settings-layering.d.ts +45 -0
  140. package/dist/settings-layering.d.ts.map +1 -0
  141. package/dist/settings-layering.js +134 -0
  142. package/dist/settings-layering.js.map +1 -0
  143. package/dist/settings.d.ts +238 -0
  144. package/dist/settings.d.ts.map +1 -0
  145. package/dist/settings.js +378 -0
  146. package/dist/settings.js.map +1 -0
  147. package/dist/skill-manifest.d.ts +18 -0
  148. package/dist/skill-manifest.d.ts.map +1 -0
  149. package/dist/skill-manifest.js +125 -0
  150. package/dist/skill-manifest.js.map +1 -0
  151. package/dist/source-text.d.ts +83 -0
  152. package/dist/source-text.d.ts.map +1 -0
  153. package/dist/source-text.js +310 -0
  154. package/dist/source-text.js.map +1 -0
  155. package/dist/suppression-ceiling.d.ts +48 -0
  156. package/dist/suppression-ceiling.d.ts.map +1 -0
  157. package/dist/suppression-ceiling.js +115 -0
  158. package/dist/suppression-ceiling.js.map +1 -0
  159. package/dist/tailwind-tokens.d.ts +16 -0
  160. package/dist/tailwind-tokens.d.ts.map +1 -0
  161. package/dist/tailwind-tokens.js +70 -0
  162. package/dist/tailwind-tokens.js.map +1 -0
  163. package/dist/text-escapes.d.ts +7 -0
  164. package/dist/text-escapes.d.ts.map +1 -0
  165. package/dist/text-escapes.js +14 -0
  166. package/dist/text-escapes.js.map +1 -0
  167. package/dist/toolchain-pins.d.ts +42 -0
  168. package/dist/toolchain-pins.d.ts.map +1 -0
  169. package/dist/toolchain-pins.js +99 -0
  170. package/dist/toolchain-pins.js.map +1 -0
  171. package/dist/url-safety.d.ts +12 -0
  172. package/dist/url-safety.d.ts.map +1 -0
  173. package/dist/url-safety.js +58 -0
  174. package/dist/url-safety.js.map +1 -0
  175. package/dist/version-policy.d.ts +99 -0
  176. package/dist/version-policy.d.ts.map +1 -0
  177. package/dist/version-policy.js +306 -0
  178. package/dist/version-policy.js.map +1 -0
  179. package/dist/vulnerability-policy.d.ts +66 -0
  180. package/dist/vulnerability-policy.d.ts.map +1 -0
  181. package/dist/vulnerability-policy.js +106 -0
  182. package/dist/vulnerability-policy.js.map +1 -0
  183. package/dist/wiring-policy.d.ts +226 -0
  184. package/dist/wiring-policy.d.ts.map +1 -0
  185. package/dist/wiring-policy.js +458 -0
  186. package/dist/wiring-policy.js.map +1 -0
  187. package/dist/wiring-violation.d.ts +6 -0
  188. package/dist/wiring-violation.d.ts.map +1 -0
  189. package/dist/wiring-violation.js +8 -0
  190. package/dist/wiring-violation.js.map +1 -0
  191. package/dist/workspace-policy.d.ts +177 -0
  192. package/dist/workspace-policy.d.ts.map +1 -0
  193. package/dist/workspace-policy.js +228 -0
  194. package/dist/workspace-policy.js.map +1 -0
  195. package/dist/yaml-blocks.d.ts +43 -0
  196. package/dist/yaml-blocks.d.ts.map +1 -0
  197. package/dist/yaml-blocks.js +87 -0
  198. package/dist/yaml-blocks.js.map +1 -0
  199. package/package.json +41 -0
@@ -0,0 +1,102 @@
1
+ /** A digest-pinned image reference, split into the parts a registry API addresses it by. */
2
+ export interface ContainerReference {
3
+ /** The analyzer the image runs, which is the property that declares it. */
4
+ readonly tool: string;
5
+ /** The repository as written, such as `zricethezav/gitleaks`. */
6
+ readonly name: string;
7
+ readonly namespace: string;
8
+ readonly repository: string;
9
+ /** The human-readable version tag, which is what a newer release is compared against. */
10
+ readonly tag: string;
11
+ /** The immutable digest the pin actually resolves to. */
12
+ readonly digest: string;
13
+ }
14
+ /**
15
+ * A stable numeric tag, and the syntax family it belongs to.
16
+ *
17
+ * The family matters as much as the number. A repository commonly publishes several schemes at once -
18
+ * `2.14.0` beside `v2.14`, and a date-stamped line beside both - and comparing across them reports an
19
+ * "update" that is a different artefact rather than a newer one.
20
+ */
21
+ export interface ContainerTag {
22
+ readonly raw: string;
23
+ /** The `v` a tag may lead with, kept because a repository that uses one uses it consistently. */
24
+ readonly prefix: string;
25
+ readonly parts: readonly number[];
26
+ /** True when the leading component reads as a year, which dates a line rather than versioning it. */
27
+ readonly isCalendar: boolean;
28
+ }
29
+ /**
30
+ * Read a digest-pinned reference into its addressable parts.
31
+ * @param tool the analyzer that runs the image, used to name the declaring property in a report.
32
+ * @param reference the `repository:tag@sha256:digest` reference.
33
+ * @returns the parts, or undefined when the reference is not pinned in that form.
34
+ */
35
+ export declare const parseContainerReference: (tool: string, reference: string) => ContainerReference | undefined;
36
+ /**
37
+ * Read a tag as a stable version, or refuse it.
38
+ * @param raw the tag as the registry publishes it.
39
+ * @returns the parsed tag, or undefined for anything that is not a stable numeric version.
40
+ */
41
+ export declare const parseContainerTag: (raw: string) => ContainerTag | undefined;
42
+ /**
43
+ * Whether two tags belong to the same published line, and are therefore comparable.
44
+ * @param one the pinned tag.
45
+ * @param other a candidate from the registry.
46
+ * @returns true when the prefix, the component count, and the calendar shape all agree.
47
+ */
48
+ export declare const matchesTagScheme: (one: ContainerTag, other: ContainerTag) => boolean;
49
+ /**
50
+ * Order two tags by version.
51
+ * @param one the first tag.
52
+ * @param other the second tag.
53
+ * @returns a negative number when `one` is older, positive when newer, zero when equal.
54
+ */
55
+ export declare const compareContainerTags: (one: ContainerTag, other: ContainerTag) => number;
56
+ /**
57
+ * The newest tag of the pinned tag's own scheme.
58
+ * @param current the pinned tag.
59
+ * @param available every tag the registry lists.
60
+ * @returns the newest comparable tag, which is `current` itself when nothing newer is published.
61
+ */
62
+ export declare const latestOfScheme: (current: ContainerTag, available: readonly string[]) => ContainerTag;
63
+ /** What a registry read established about one pinned image. */
64
+ export interface ContainerInspection {
65
+ readonly reference: ContainerReference;
66
+ /** Every tag the repository publishes. */
67
+ readonly available: readonly string[];
68
+ /** The digest the pinned TAG resolves to today, which a mutable tag may have moved. */
69
+ readonly currentDigest: string;
70
+ }
71
+ /** What the rule decided about one pinned image, with the registry reads already done. */
72
+ export interface ContainerVerdict {
73
+ readonly reference: ContainerReference;
74
+ /** The newer tag, or undefined when the pin is already the newest of its scheme. */
75
+ readonly newer: ContainerTag | undefined;
76
+ /** True when the declared tag no longer resolves to the digest pinned beside it. */
77
+ readonly hasDrifted: boolean;
78
+ /** What the declared tag resolves to now, which the drift line names beside the pin. */
79
+ readonly currentDigest: string;
80
+ }
81
+ /**
82
+ * Judge one inspected image.
83
+ * @param inspection the reference and what the registry answered about it.
84
+ * @returns the newer tag if one exists, and whether the declared tag has been repointed.
85
+ */
86
+ export declare const judgeContainer: (inspection: ContainerInspection) => ContainerVerdict;
87
+ /**
88
+ * The report line for an image with a newer tag available.
89
+ * @param reference the pinned image.
90
+ * @param newer the newer tag.
91
+ * @param newerDigest the digest that tag resolves to, which is what replaces the pin.
92
+ * @returns one line naming the declaring property and the replacement reference.
93
+ */
94
+ export declare const describeContainerUpdate: (reference: ContainerReference, newer: ContainerTag, newerDigest: string) => string;
95
+ /**
96
+ * The report line for a tag that no longer resolves to the digest pinned beside it.
97
+ * @param reference the pinned image.
98
+ * @param currentDigest the digest the tag resolves to now.
99
+ * @returns one line naming the declaring property and both digests.
100
+ */
101
+ export declare const describeContainerDrift: (reference: ContainerReference, currentDigest: string) => string;
102
+ //# sourceMappingURL=container-freshness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"container-freshness.d.ts","sourceRoot":"","sources":["../src/container-freshness.ts"],"names":[],"mappings":"AAWA,4FAA4F;AAC5F,MAAM,WAAW,kBAAkB;IACjC,2EAA2E;IAC3E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,iEAAiE;IACjE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,yFAAyF;IACzF,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,yDAAyD;IACzD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CACxB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,iGAAiG;IACjG,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;IACjC,qGAAqG;IACrG,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAA;CAC7B;AA8BD;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,GAClC,MAAM,MAAM,EACZ,WAAW,MAAM,KAChB,kBAAkB,GAAG,SAiBvB,CAAA;AAQD;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAAI,KAAK,MAAM,KAAG,YAAY,GAAG,SAY9D,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,GAAI,KAAK,YAAY,EAAE,OAAO,YAAY,KAAG,OAGrC,CAAA;AAErC;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,GAAI,KAAK,YAAY,EAAE,OAAO,YAAY,KAAG,MACxC,CAAA;AAEtC;;;;;GAKG;AACH,eAAO,MAAM,cAAc,GACzB,SAAS,YAAY,EACrB,WAAW,SAAS,MAAM,EAAE,KAC3B,YAUF,CAAA;AAED,+DAA+D;AAC/D,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,SAAS,EAAE,kBAAkB,CAAA;IACtC,0CAA0C;IAC1C,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAA;IACrC,uFAAuF;IACvF,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;CAC/B;AAED,0FAA0F;AAC1F,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,SAAS,EAAE,kBAAkB,CAAA;IACtC,oFAAoF;IACpF,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,SAAS,CAAA;IACxC,oFAAoF;IACpF,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAA;IAC5B,wFAAwF;IACxF,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;CAC/B;AAED;;;;GAIG;AACH,eAAO,MAAM,cAAc,GAAI,YAAY,mBAAmB,KAAG,gBAahE,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,GAClC,WAAW,kBAAkB,EAC7B,OAAO,YAAY,EACnB,aAAa,MAAM,KAClB,MAEkD,CAAA;AAErD;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,GACjC,WAAW,kBAAkB,EAC7B,eAAe,MAAM,KACpB,MAE2F,CAAA"}
@@ -0,0 +1,129 @@
1
+ // A stable tag is numeric and has at least two components. One component cannot be told apart from a
2
+ // build number, and anything carrying a suffix - `-alpine`, `-rc1`, `-debug` - is either a variant or a
3
+ // prerelease, neither of which a pin should move to on its own.
4
+ const STABLE_TAG = /^(?<prefix>v?)(?<version>\d+(?:\.\d+)+)$/;
5
+ const PINNED_REFERENCE = /^(?<name>[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._-]*):(?<tag>[A-Za-z0-9][\w.-]*)@(?<digest>sha256:[0-9a-f]{64})$/;
6
+ // Ordered by component rather than by the semver rule `dependency-freshness.ts` implements. A tag has
7
+ // no prerelease - the pattern above refuses any suffix - and its component COUNT is meaningful here,
8
+ // which a three-position core discards: it would read `2024.1` and `2024.1.0` as one version and drop a
9
+ // fourth component entirely. Different questions, so a different comparison rather than a shared one.
10
+ const compareParts = (one, other) => {
11
+ const width = Math.max(one.length, other.length);
12
+ const differences = Array.from({ length: width }, (_, index) => (one[index] ?? 0) - (other[index] ?? 0));
13
+ return differences.find((difference) => difference !== 0) ?? 0;
14
+ };
15
+ // A repository name is exactly one namespace and one repository, so the split takes both and no more.
16
+ const NAME_PARTS = 2;
17
+ const YEAR_LENGTH = 4;
18
+ const FIRST_CALENDAR_YEAR = 2000;
19
+ const LAST_CALENDAR_YEAR = 2099;
20
+ /**
21
+ * Read a digest-pinned reference into its addressable parts.
22
+ * @param tool the analyzer that runs the image, used to name the declaring property in a report.
23
+ * @param reference the `repository:tag@sha256:digest` reference.
24
+ * @returns the parts, or undefined when the reference is not pinned in that form.
25
+ */
26
+ export const parseContainerReference = (tool, reference) => {
27
+ const found = PINNED_REFERENCE.exec(reference);
28
+ if (found?.groups === undefined) {
29
+ return undefined;
30
+ }
31
+ const name = found.groups['name'] ?? '';
32
+ const [namespace, repo] = name.split('/', NAME_PARTS);
33
+ return namespace === undefined || repo === undefined
34
+ ? undefined
35
+ : {
36
+ tool,
37
+ name,
38
+ namespace,
39
+ repository: repo,
40
+ tag: found.groups['tag'] ?? '',
41
+ digest: found.groups['digest'] ?? '',
42
+ };
43
+ };
44
+ const isCalendarLead = (lead) => lead !== undefined &&
45
+ String(lead).length === YEAR_LENGTH &&
46
+ lead >= FIRST_CALENDAR_YEAR &&
47
+ lead <= LAST_CALENDAR_YEAR;
48
+ /**
49
+ * Read a tag as a stable version, or refuse it.
50
+ * @param raw the tag as the registry publishes it.
51
+ * @returns the parsed tag, or undefined for anything that is not a stable numeric version.
52
+ */
53
+ export const parseContainerTag = (raw) => {
54
+ const found = STABLE_TAG.exec(raw);
55
+ if (found?.groups === undefined) {
56
+ return undefined;
57
+ }
58
+ const parts = (found.groups['version'] ?? '').split('.').map(Number);
59
+ return {
60
+ raw,
61
+ prefix: found.groups['prefix'] ?? '',
62
+ parts,
63
+ isCalendar: isCalendarLead(parts[0]),
64
+ };
65
+ };
66
+ /**
67
+ * Whether two tags belong to the same published line, and are therefore comparable.
68
+ * @param one the pinned tag.
69
+ * @param other a candidate from the registry.
70
+ * @returns true when the prefix, the component count, and the calendar shape all agree.
71
+ */
72
+ export const matchesTagScheme = (one, other) => one.prefix === other.prefix &&
73
+ one.parts.length === other.parts.length &&
74
+ one.isCalendar === other.isCalendar;
75
+ /**
76
+ * Order two tags by version.
77
+ * @param one the first tag.
78
+ * @param other the second tag.
79
+ * @returns a negative number when `one` is older, positive when newer, zero when equal.
80
+ */
81
+ export const compareContainerTags = (one, other) => compareParts(one.parts, other.parts);
82
+ /**
83
+ * The newest tag of the pinned tag's own scheme.
84
+ * @param current the pinned tag.
85
+ * @param available every tag the registry lists.
86
+ * @returns the newest comparable tag, which is `current` itself when nothing newer is published.
87
+ */
88
+ export const latestOfScheme = (current, available) => {
89
+ const comparable = available
90
+ .map((raw) => parseContainerTag(raw))
91
+ .filter((tag) => tag !== undefined)
92
+ .filter((tag) => matchesTagScheme(current, tag));
93
+ return comparable.reduce((newest, tag) => compareContainerTags(tag, newest) > 0 ? tag : newest, current);
94
+ };
95
+ /**
96
+ * Judge one inspected image.
97
+ * @param inspection the reference and what the registry answered about it.
98
+ * @returns the newer tag if one exists, and whether the declared tag has been repointed.
99
+ */
100
+ export const judgeContainer = (inspection) => {
101
+ const current = parseContainerTag(inspection.reference.tag);
102
+ const newest = current === undefined ? undefined : latestOfScheme(current, inspection.available);
103
+ return {
104
+ reference: inspection.reference,
105
+ newer: newest === undefined || current === undefined || compareContainerTags(newest, current) <= 0
106
+ ? undefined
107
+ : newest,
108
+ hasDrifted: inspection.currentDigest !== inspection.reference.digest,
109
+ currentDigest: inspection.currentDigest,
110
+ };
111
+ };
112
+ /**
113
+ * The report line for an image with a newer tag available.
114
+ * @param reference the pinned image.
115
+ * @param newer the newer tag.
116
+ * @param newerDigest the digest that tag resolves to, which is what replaces the pin.
117
+ * @returns one line naming the declaring property and the replacement reference.
118
+ */
119
+ export const describeContainerUpdate = (reference, newer, newerDigest) => `update ${reference.tool} ${reference.name}:${reference.tag} -> ${newer.raw}; ` +
120
+ `pin ${reference.name}:${newer.raw}@${newerDigest}`;
121
+ /**
122
+ * The report line for a tag that no longer resolves to the digest pinned beside it.
123
+ * @param reference the pinned image.
124
+ * @param currentDigest the digest the tag resolves to now.
125
+ * @returns one line naming the declaring property and both digests.
126
+ */
127
+ export const describeContainerDrift = (reference, currentDigest) => `note ${reference.tool} ${reference.name}:${reference.tag} is pinned to ${reference.digest} ` +
128
+ `but that tag now resolves to ${currentDigest}; the pin still names the bytes it always did`;
129
+ //# sourceMappingURL=container-freshness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"container-freshness.js","sourceRoot":"","sources":["../src/container-freshness.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,129 @@
1
+ /** The build impact of a dependency's drift from its latest published release. */
2
+ export type FreshnessVerdict = 'ok' | 'update' | 'fail';
3
+ /** Major-version gap (`latest.major - current.major`) at or above which the gate FAILS the build. */
4
+ export declare const MAJOR_FAIL_THRESHOLD: number;
5
+ /** A dependency version parsed into its numeric core plus any prerelease tag. */
6
+ export interface ParsedVersion {
7
+ readonly major: number;
8
+ readonly minor: number;
9
+ readonly patch: number;
10
+ readonly prerelease: string;
11
+ }
12
+ /**
13
+ * Parse a version or range string into its numeric core plus any prerelease tag, tolerating a leading
14
+ * range operator (`^`, `~`, `>=`) or `v` and trailing build metadata. Returns `undefined` when there
15
+ * is no leading numeric major - for example `workspace:*`, `link:../pkg`, `*`, or a missing latest - so
16
+ * the caller treats such a dependency as unclassifiable and skips it.
17
+ * @param raw the version or range string to parse.
18
+ * @returns the parsed core and prerelease, or `undefined` when no numeric major is present.
19
+ */
20
+ export declare const parseVersion: (raw: string) => ParsedVersion | undefined;
21
+ /**
22
+ * Classify how far a current version has drifted from the latest published version. Unclassifiable
23
+ * inputs (a version that does not parse, such as `workspace:*` or an absent latest) are `ok` and
24
+ * silently skipped. Otherwise the verdict is `fail` when the latest major is MAJOR_FAIL_THRESHOLD or
25
+ * more ahead, `update` when the current is strictly behind by any lesser amount (a single major, or a
26
+ * minor/patch), and `ok` when the current is level with or ahead of the latest. Because the major gap
27
+ * uses the literal `major` field, a `0.x` package is compared on that leading `0` (so `0.35.2` to
28
+ * `0.35.3` needs an update, and only a jump to `2.x` fails).
29
+ * @param current the version currently declared/installed.
30
+ * @param latest the latest version published to the registry.
31
+ * @returns the freshness verdict for the pair.
32
+ */
33
+ export declare const classifyFreshness: (current: string, latest: string) => FreshnessVerdict;
34
+ /** One dependency, plugin, or parent as some manifest of the repository declares it. */
35
+ export interface DeclaredCoordinate {
36
+ /** The package name, or a `$schema` pseudo-dependency label. */
37
+ readonly name: string;
38
+ /** The manifest that declares it, so a finding names what the reader has to open. */
39
+ readonly owner: string;
40
+ /** The version currently declared/installed. */
41
+ readonly current: string;
42
+ /**
43
+ * True when the manifest is one the project INHERITS rather than one it wrote.
44
+ *
45
+ * The measurement is the same either way. What differs is who the finding belongs to: a project
46
+ * cannot edit a version ploaness declares, so the repair is upgrading the harness, and the build is
47
+ * not stopped over it. `HARNESS_EXCEPTIONS` in `vulnerability-policy.ts` draws the same distinction
48
+ * for an advisory carried by ploaness's own chain, and for the same reason.
49
+ */
50
+ readonly isInherited: boolean;
51
+ }
52
+ /** A declared coordinate paired with what the registry answered for it. */
53
+ export interface DependencyStatus extends DeclaredCoordinate {
54
+ /** The latest version published to the registry. */
55
+ readonly latest: string;
56
+ }
57
+ /** One parsed manifest, with the path a finding will name. */
58
+ export interface ManifestSource {
59
+ readonly path: string;
60
+ readonly packageJson: unknown;
61
+ /** True for a manifest the project inherits rather than one it tracks. */
62
+ readonly isInherited: boolean;
63
+ }
64
+ /**
65
+ * How the inheritance walk reaches one manifest from another.
66
+ *
67
+ * Injected so this module performs no I/O: the CLI supplies a resolver over the real filesystem, and a
68
+ * spec supplies one over a literal map. Which manifests a project inherits is a decision about what the
69
+ * standard counts, and a decision belongs where it can be tested without building an install.
70
+ */
71
+ export interface ManifestResolver {
72
+ /** The manifest `packageName` resolves to FROM `fromManifest`, or undefined when unreachable. */
73
+ readonly locate: (packageName: string, fromManifest: string) => string | undefined;
74
+ /** The parsed manifest at a path. */
75
+ readonly read: (manifestPath: string) => unknown;
76
+ }
77
+ /**
78
+ * The manifests a project inherits, in discovery order, starting from the harness it declares.
79
+ *
80
+ * Walked rather than enumerated. A list of ploaness's packages would be a second copy of what their own
81
+ * manifests already declare, and a package added later would then be inherited by every consumer and
82
+ * read by nothing. The walk re-roots at each manifest, because a resolver's answer depends on where it
83
+ * is asked from, and it carries `visited` across siblings for two reasons: the packages form a diamond,
84
+ * so a shared one would otherwise be reported twice, and a cycle would otherwise not terminate.
85
+ * @param entry the manifest the project resolves the harness to, or undefined when it declares none.
86
+ * @param resolver how to reach and read a manifest.
87
+ * @returns each inherited manifest path once, the entry first.
88
+ */
89
+ export declare const inheritedManifestPaths: (entry: string | undefined, resolver: ManifestResolver) => readonly string[];
90
+ /**
91
+ * Read every declared coordinate out of every manifest, the project's own and the ones it inherits.
92
+ *
93
+ * A workspace declares its toolchain across several manifests, and reading only the root one left the
94
+ * analyzers ploaness itself runs on unmeasured. The standard counts an INHERITED manifest too, and the
95
+ * manifests deciding which analyzer versions a governed project's gates run are ploaness's own - none
96
+ * of which the project tracks, so a reader of tracked files alone can never see them.
97
+ *
98
+ * A name declared in two manifests stays two coordinates
99
+ * rather than collapsing into one: the two may pin different versions, and collapsing would hide
100
+ * whichever of them is stale. No sort is applied because none is needed - the caller hands over the
101
+ * manifests in the order git lists them, and a parsed object preserves the order its file declared, so
102
+ * the same tree yields the same report.
103
+ * @param manifests the parsed manifests, each with the path that declares it.
104
+ * @returns one coordinate per declaration.
105
+ */
106
+ export declare const collectCoordinates: (manifests: readonly ManifestSource[]) => readonly DeclaredCoordinate[];
107
+ /** A dependency status that classified as a non-`ok` verdict, carried through for reporting. */
108
+ export interface FreshnessFinding extends DependencyStatus {
109
+ readonly verdict: 'update' | 'fail';
110
+ }
111
+ /**
112
+ * The findings for a set of dependency statuses; `ok` statuses are omitted.
113
+ *
114
+ * The split is by BUILD IMPACT, not by verdict: `failures` is what stops the run and `reported` is
115
+ * everything else worth printing. Each finding still carries the verdict it was measured at, so an
116
+ * inherited coordinate past the bound arrives in `reported` saying `fail` - which is the only way to
117
+ * report it honestly without stopping a project that cannot repair it.
118
+ */
119
+ export interface FreshnessReport {
120
+ readonly failures: readonly FreshnessFinding[];
121
+ readonly reported: readonly FreshnessFinding[];
122
+ }
123
+ /**
124
+ * Partition dependency statuses by what each does to the build, dropping the `ok` ones.
125
+ * @param statuses the declared dependencies (and `$schema` pseudo-dependencies) to classify.
126
+ * @returns the findings that stop the run, and the rest of the report; both empty when all are fresh.
127
+ */
128
+ export declare const findFreshnessViolations: (statuses: readonly DependencyStatus[]) => FreshnessReport;
129
+ //# sourceMappingURL=dependency-freshness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dependency-freshness.d.ts","sourceRoot":"","sources":["../src/dependency-freshness.ts"],"names":[],"mappings":"AAqBA,kFAAkF;AAClF,MAAM,MAAM,gBAAgB,GAAG,IAAI,GAAG,QAAQ,GAAG,MAAM,CAAA;AAEvD,qGAAqG;AACrG,eAAO,MAAM,oBAAoB,EAAE,MAAU,CAAA;AAE7C,iFAAiF;AACjF,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAC5B;AAQD;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,GAAI,KAAK,MAAM,KAAG,aAAa,GAAG,SAW1D,CAAA;AA0BD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,iBAAiB,GAAI,SAAS,MAAM,EAAE,QAAQ,MAAM,KAAG,gBAUnE,CAAA;AAED,wFAAwF;AACxF,MAAM,WAAW,kBAAkB;IACjC,gEAAgE;IAChE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,qFAAqF;IACrF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,gDAAgD;IAChD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB;;;;;;;OAOG;IACH,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAA;CAC9B;AAED,2EAA2E;AAC3E,MAAM,WAAW,gBAAiB,SAAQ,kBAAkB;IAC1D,oDAAoD;IACpD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CACxB;AAED,8DAA8D;AAC9D,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAA;IAC7B,0EAA0E;IAC1E,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAA;CAC9B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAC/B,iGAAiG;IACjG,QAAQ,CAAC,MAAM,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAA;IAClF,qCAAqC;IACrC,QAAQ,CAAC,IAAI,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAA;CACjD;AA6BD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,sBAAsB,GACjC,OAAO,MAAM,GAAG,SAAS,EACzB,UAAU,gBAAgB,KACzB,SAAS,MAAM,EACkE,CAAA;AAEpF;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,kBAAkB,GAC7B,WAAW,SAAS,cAAc,EAAE,KACnC,SAAS,kBAAkB,EAU3B,CAAA;AAEH,gGAAgG;AAChG,MAAM,WAAW,gBAAiB,SAAQ,gBAAgB;IACxD,QAAQ,CAAC,OAAO,EAAE,QAAQ,GAAG,MAAM,CAAA;CACpC;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,QAAQ,EAAE,SAAS,gBAAgB,EAAE,CAAA;IAC9C,QAAQ,CAAC,QAAQ,EAAE,SAAS,gBAAgB,EAAE,CAAA;CAC/C;AAYD;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,GAAI,UAAU,SAAS,gBAAgB,EAAE,KAAG,eAiB/E,CAAA"}
@@ -0,0 +1,161 @@
1
+ // Dependency-freshness policy: the pure, unit-tested logic that reads the coordinates a repository
2
+ // declares and classifies how far each has drifted from its latest published release. The `deps` gate in
3
+ // packages/cli/src/checks/dependencies.ts reads the manifests and asks the registry.
4
+ //
5
+ // Policy: a declared dependency (transitive deps never count) whose current major version is
6
+ // MAJOR_FAIL_THRESHOLD or more behind the latest published major FAILS the build; any lesser lag (a
7
+ // single major behind, or a minor/patch behind) appears in the non-failing update report. The Biome config `$schema`
8
+ // URL version is fed through the same classifier as a pseudo-dependency. There is no exemption list:
9
+ // every declared dependency is MEASURED against the same bar, so a deliberately old pin that falls two
10
+ // majors behind must be bumped or the pin dropped.
11
+ //
12
+ // One thing the bar does not decide is who the build stops. A coordinate an INHERITED manifest declares
13
+ // is measured identically and reported at its real verdict, and it never fails the build: the version
14
+ // belongs to ploaness, the project has no file in which to change it, and a gate that stopped every
15
+ // consumer at once over a pin none of them can edit would be reporting ploaness's defect as theirs.
16
+ // That is a statement about whose repair it is rather than an exemption from the measurement, which is
17
+ // why the verdict survives into the report instead of being softened into an ordinary update.
18
+ import { declaredDependencies } from './json-shapes.js';
19
+ import { isHarnessPackage } from './version-policy.js';
20
+ /** Major-version gap (`latest.major - current.major`) at or above which the gate FAILS the build. */
21
+ export const MAJOR_FAIL_THRESHOLD = 2;
22
+ // A leading range operator (`^ ~ >= <= = v`) or whitespace is tolerated, then the numeric core is
23
+ // read into positional groups (major, minor, patch, prerelease); an optional `-prerelease` tag is
24
+ // captured and build metadata after `+` is ignored. The core mirrors the `$schema` version regex the
25
+ // `biome-schema` gate uses in packages/cli/src/checks/toolchain.ts.
26
+ const VERSION_PATTERN = /^[\sv^~>=<]*(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:-([^+\s]+))?/;
27
+ /**
28
+ * Parse a version or range string into its numeric core plus any prerelease tag, tolerating a leading
29
+ * range operator (`^`, `~`, `>=`) or `v` and trailing build metadata. Returns `undefined` when there
30
+ * is no leading numeric major - for example `workspace:*`, `link:../pkg`, `*`, or a missing latest - so
31
+ * the caller treats such a dependency as unclassifiable and skips it.
32
+ * @param raw the version or range string to parse.
33
+ * @returns the parsed core and prerelease, or `undefined` when no numeric major is present.
34
+ */
35
+ export const parseVersion = (raw) => {
36
+ const match = VERSION_PATTERN.exec(raw);
37
+ if (match === null) {
38
+ return undefined;
39
+ }
40
+ return {
41
+ major: Number(match[1] ?? '0'),
42
+ minor: Number(match[2] ?? '0'),
43
+ patch: Number(match[3] ?? '0'),
44
+ prerelease: match[4] ?? '',
45
+ };
46
+ };
47
+ // Order two parsed versions: numeric core first, then a prerelease sorts BELOW the same core release
48
+ // (the semver rule), so `1.0.0-rc.1` is behind `1.0.0`.
49
+ const compareCore = (left, right) => {
50
+ if (left.major !== right.major) {
51
+ return left.major - right.major;
52
+ }
53
+ if (left.minor !== right.minor) {
54
+ return left.minor - right.minor;
55
+ }
56
+ if (left.patch !== right.patch) {
57
+ return left.patch - right.patch;
58
+ }
59
+ if (left.prerelease === right.prerelease) {
60
+ return 0;
61
+ }
62
+ if (left.prerelease === '') {
63
+ return 1;
64
+ }
65
+ if (right.prerelease === '') {
66
+ return -1;
67
+ }
68
+ return left.prerelease < right.prerelease ? -1 : 1;
69
+ };
70
+ /**
71
+ * Classify how far a current version has drifted from the latest published version. Unclassifiable
72
+ * inputs (a version that does not parse, such as `workspace:*` or an absent latest) are `ok` and
73
+ * silently skipped. Otherwise the verdict is `fail` when the latest major is MAJOR_FAIL_THRESHOLD or
74
+ * more ahead, `update` when the current is strictly behind by any lesser amount (a single major, or a
75
+ * minor/patch), and `ok` when the current is level with or ahead of the latest. Because the major gap
76
+ * uses the literal `major` field, a `0.x` package is compared on that leading `0` (so `0.35.2` to
77
+ * `0.35.3` needs an update, and only a jump to `2.x` fails).
78
+ * @param current the version currently declared/installed.
79
+ * @param latest the latest version published to the registry.
80
+ * @returns the freshness verdict for the pair.
81
+ */
82
+ export const classifyFreshness = (current, latest) => {
83
+ const currentVersion = parseVersion(current);
84
+ const latestVersion = parseVersion(latest);
85
+ if (currentVersion === undefined || latestVersion === undefined) {
86
+ return 'ok';
87
+ }
88
+ if (latestVersion.major - currentVersion.major >= MAJOR_FAIL_THRESHOLD) {
89
+ return 'fail';
90
+ }
91
+ return compareCore(currentVersion, latestVersion) < 0 ? 'update' : 'ok';
92
+ };
93
+ const stepInto = (walk, manifestPath, resolver) => {
94
+ if (manifestPath === undefined || walk.visited.has(manifestPath)) {
95
+ return walk;
96
+ }
97
+ const entered = {
98
+ visited: new Set([...walk.visited, manifestPath]),
99
+ manifests: [...walk.manifests, manifestPath],
100
+ };
101
+ return Object.keys(declaredDependencies(resolver.read(manifestPath)))
102
+ .filter((name) => isHarnessPackage(name))
103
+ .reduce((carried, name) => stepInto(carried, resolver.locate(name, manifestPath), resolver), entered);
104
+ };
105
+ /**
106
+ * The manifests a project inherits, in discovery order, starting from the harness it declares.
107
+ *
108
+ * Walked rather than enumerated. A list of ploaness's packages would be a second copy of what their own
109
+ * manifests already declare, and a package added later would then be inherited by every consumer and
110
+ * read by nothing. The walk re-roots at each manifest, because a resolver's answer depends on where it
111
+ * is asked from, and it carries `visited` across siblings for two reasons: the packages form a diamond,
112
+ * so a shared one would otherwise be reported twice, and a cycle would otherwise not terminate.
113
+ * @param entry the manifest the project resolves the harness to, or undefined when it declares none.
114
+ * @param resolver how to reach and read a manifest.
115
+ * @returns each inherited manifest path once, the entry first.
116
+ */
117
+ export const inheritedManifestPaths = (entry, resolver) => stepInto({ visited: new Set(), manifests: [] }, entry, resolver).manifests;
118
+ /**
119
+ * Read every declared coordinate out of every manifest, the project's own and the ones it inherits.
120
+ *
121
+ * A workspace declares its toolchain across several manifests, and reading only the root one left the
122
+ * analyzers ploaness itself runs on unmeasured. The standard counts an INHERITED manifest too, and the
123
+ * manifests deciding which analyzer versions a governed project's gates run are ploaness's own - none
124
+ * of which the project tracks, so a reader of tracked files alone can never see them.
125
+ *
126
+ * A name declared in two manifests stays two coordinates
127
+ * rather than collapsing into one: the two may pin different versions, and collapsing would hide
128
+ * whichever of them is stale. No sort is applied because none is needed - the caller hands over the
129
+ * manifests in the order git lists them, and a parsed object preserves the order its file declared, so
130
+ * the same tree yields the same report.
131
+ * @param manifests the parsed manifests, each with the path that declares it.
132
+ * @returns one coordinate per declaration.
133
+ */
134
+ export const collectCoordinates = (manifests) => manifests.flatMap((manifest) => Object.entries(declaredDependencies(manifest.packageJson)).map(([name, current]) => ({
135
+ name,
136
+ owner: manifest.path,
137
+ current,
138
+ isInherited: manifest.isInherited,
139
+ })));
140
+ // The one place the two questions are told apart: how far behind a coordinate is, and whether being
141
+ // that far behind stops this build.
142
+ const willStopTheBuild = (entry) => entry.verdict === 'fail' && !entry.status.isInherited;
143
+ /**
144
+ * Partition dependency statuses by what each does to the build, dropping the `ok` ones.
145
+ * @param statuses the declared dependencies (and `$schema` pseudo-dependencies) to classify.
146
+ * @returns the findings that stop the run, and the rest of the report; both empty when all are fresh.
147
+ */
148
+ export const findFreshnessViolations = (statuses) => {
149
+ // The verdict is computed once per status; the two reported groups are then filters over it rather
150
+ // than two lists filled in the same pass.
151
+ const judged = statuses.map((status) => ({
152
+ status,
153
+ verdict: classifyFreshness(status.current, status.latest),
154
+ }));
155
+ const findingsOf = (entries) => entries.flatMap((entry) => entry.verdict === 'ok' ? [] : [{ ...entry.status, verdict: entry.verdict }]);
156
+ return {
157
+ failures: findingsOf(judged.filter((entry) => willStopTheBuild(entry))),
158
+ reported: findingsOf(judged.filter((entry) => !willStopTheBuild(entry))),
159
+ };
160
+ };
161
+ //# sourceMappingURL=dependency-freshness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dependency-freshness.js","sourceRoot":"","sources":["../src/dependency-freshness.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ export declare const DOCUMENTED_EXTENSIONS: readonly string[];
2
+ /** The span of one documenting comment, as 1-based inclusive line numbers. */
3
+ export interface DocBlock {
4
+ readonly line: number;
5
+ readonly endLine: number;
6
+ }
7
+ /**
8
+ * Find every documenting comment that is followed by another one rather than by what it documents.
9
+ * @param text the file contents, with lines separated by "\n" or "\r\n".
10
+ * @returns one entry per orphaned block, in reading order.
11
+ */
12
+ export declare const findOrphanedDocBlocks: (text: string) => readonly DocBlock[];
13
+ //# sourceMappingURL=doc-blocks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doc-blocks.d.ts","sourceRoot":"","sources":["../src/doc-blocks.ts"],"names":[],"mappings":"AAiBA,eAAO,MAAM,qBAAqB,EAAE,SAAS,MAAM,EAQlD,CAAA;AAWD,8EAA8E;AAC9E,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CACzB;AAkDD;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,GAAI,MAAM,MAAM,KAAG,SAAS,QAAQ,EAKrE,CAAA"}
@@ -0,0 +1,75 @@
1
+ // Orphaned documenting comments: a `/** ... */` block whose next non-blank line opens ANOTHER such
2
+ // block, so the first documents nothing and the symbol it was written for carries no doc at all.
3
+ //
4
+ // This cannot be an eslint-plugin-jsdoc rule, and the reason is structural rather than a gap in that
5
+ // plugin. Every rule it ships visits the block ATTACHED to a syntax node, and an orphan is attached to
6
+ // nothing: the parser hands a declaration only the LAST block comment before it and discards the
7
+ // earlier ones as leading trivia. The defect is invisible to the one analyzer that exists for
8
+ // documenting comments, which is why it is a text rule here - the text is the only place it is still
9
+ // visible.
10
+ //
11
+ // It is a real class rather than a hypothetical. Four instances were found in this repository the
12
+ // first time anything looked, each one a doc that had been moved above the wrong symbol and left
13
+ // there, and each one carried the description of a symbol that was then undocumented.
14
+ // Extensions whose comment syntax this rule understands. A stylesheet shares the delimiters but has
15
+ // no JSDoc, so two adjacent blocks there are two comments rather than one orphan - which is why this
16
+ // list is narrower than CODE_EXTENSIONS.
17
+ export const DOCUMENTED_EXTENSIONS = [
18
+ '.ts',
19
+ '.tsx',
20
+ '.mts',
21
+ '.cts',
22
+ '.js',
23
+ '.mjs',
24
+ '.cjs',
25
+ ];
26
+ const DOC_OPEN = '/**';
27
+ const BLOCK_CLOSE = '*/';
28
+ /** The sentinel for "no block is currently open", distinguishable from every 1-based line number. */
29
+ const NO_OPEN_BLOCK = -1;
30
+ /** Array indices are 0-based and reported line numbers are 1-based. */
31
+ const FIRST_LINE = 1;
32
+ const EMPTY_SCAN = { open: NO_OPEN_BLOCK, blocks: [] };
33
+ // A block that opens and closes on one line, which is the shape most one-sentence docs take. The
34
+ // search starts past the opening marker so its own trailing asterisk cannot be read as the close.
35
+ const endsOnOpeningLine = (trimmed) => trimmed.slice(DOC_OPEN.length).includes(BLOCK_CLOSE);
36
+ const afterOpenLine = (state, trimmed, line) => {
37
+ if (!trimmed.startsWith(DOC_OPEN)) {
38
+ return state;
39
+ }
40
+ return endsOnOpeningLine(trimmed)
41
+ ? { open: NO_OPEN_BLOCK, blocks: [...state.blocks, { line, endLine: line }] }
42
+ : { open: line, blocks: state.blocks };
43
+ };
44
+ const afterBodyLine = (state, trimmed, line) => trimmed.includes(BLOCK_CLOSE)
45
+ ? { open: NO_OPEN_BLOCK, blocks: [...state.blocks, { line: state.open, endLine: line }] }
46
+ : state;
47
+ // Every documenting comment in the file, in reading order. Only blocks opened by `/**` at the start of
48
+ // a line are collected: a `/**` inside a string or trailing another statement is not a doc block, and
49
+ // treating it as one would report a defect at a position that holds none.
50
+ const docBlocks = (lines) => lines.reduce((state, raw, index) => {
51
+ const trimmed = raw.trim();
52
+ const line = index + FIRST_LINE;
53
+ return state.open === NO_OPEN_BLOCK
54
+ ? afterOpenLine(state, trimmed, line)
55
+ : afterBodyLine(state, trimmed, line);
56
+ }, EMPTY_SCAN).blocks;
57
+ // Blank lines are skipped rather than treated as separation. A doc block separated from the next doc
58
+ // block by an empty line is orphaned exactly as tightly as one that is not: what makes it an orphan is
59
+ // that no declaration follows it, and whitespace is not a declaration.
60
+ const opensAnotherBlock = (lines, endLine) => {
61
+ const next = lines
62
+ .slice(endLine)
63
+ .find((line) => line.trim().length > 0);
64
+ return (next ?? '').trimStart().startsWith(DOC_OPEN);
65
+ };
66
+ /**
67
+ * Find every documenting comment that is followed by another one rather than by what it documents.
68
+ * @param text the file contents, with lines separated by "\n" or "\r\n".
69
+ * @returns one entry per orphaned block, in reading order.
70
+ */
71
+ export const findOrphanedDocBlocks = (text) => {
72
+ const lines = text.replaceAll('\r\n', '\n').split('\n');
73
+ return docBlocks(lines).filter((block) => opensAnotherBlock(lines, block.endLine));
74
+ };
75
+ //# sourceMappingURL=doc-blocks.js.map