@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 @@
1
+ {"version":3,"file":"install-policy.js","sourceRoot":"","sources":["../src/install-policy.ts"],"names":[],"mappings":"AAAA,6FAA6F;AAC7F,EAAE;AACF,oGAAoG;AACpG,mGAAmG;AACnG,uGAAuG;AACvG,uFAAuF;AACvF,EAAE;AACF,yGAAyG;AACzG,sGAAsG;AACtG,UAAU;AACV,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAC1C,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAA;AAE9E;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,aAAa,GAAsB;IAC9C,WAAW;IACX,aAAa;IACb,qBAAqB;IACrB,mBAAmB;CACpB,CAAA;AAED,0EAA0E;AAC1E,MAAM,aAAa,GAAW,uBAAuB,CAAA;AAErD,uGAAuG;AACvG,MAAM,cAAc,GAAsB,CAAC,YAAY,EAAE,aAAa,CAAC,CAAA;AAUvE;;;;GAIG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,aAAqB,EAA4B,EAAE,CAC/E,aAAa,CAAC,OAAO,CAAC,CAAC,GAAW,EAA4B,EAAE,CAC9D,sBAAsB,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC,GAAG,CAC5C,CAAC,CAAC,WAAW,EAAE,SAAS,CAA4B,EAAiB,EAAE,CAAC,CAAC;IACvE,GAAG;IACH,WAAW;IACX,SAAS;CACV,CAAC,CACH,CACF,CAAA;AAEH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAC5C,aAAqB,EACrB,WAAoB,EACX,EAAE;IACX,IAAI,mBAAmB,CAAC,aAAa,EAAE,aAAa,CAAC,EAAE,CAAC;QACtD,OAAO,IAAI,CAAA;IACb,CAAC;IACD,OAAO,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,WAAW,EAAE,MAAM,CAAC,EAAE,aAAa,CAAC,CAAC,CAAA;AAC5E,CAAC,CAAA;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,WAAoB,EAAqB,EAAE;IAChF,MAAM,WAAW,GAAY,OAAO,CAAC,OAAO,CAAC,WAAW,EAAE,MAAM,CAAC,EAAE,aAAa,CAAC,CAAA;IACjF,OAAO,cAAc,CAAC,MAAM,CAAC,CAAC,GAAW,EAAW,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,CAAC,CAAA;AAClG,CAAC,CAAA"}
@@ -0,0 +1,86 @@
1
+ /** A parsed document, or the reason it could not be parsed. */
2
+ export interface ParsedJson {
3
+ readonly value: unknown;
4
+ /** Undefined when the text parsed; the parser's own message otherwise. */
5
+ readonly problem: string | undefined;
6
+ }
7
+ /**
8
+ * Parse JSON that may carry comments and trailing commas.
9
+ *
10
+ * `tsconfig.json` legally carries both, and so does a Biome config - which meant the rules that judge
11
+ * them threw a SyntaxError out of a pure function on a project scaffolded by `create-payload-app`. The
12
+ * gate that caught it is a precondition, so the whole run halted saying only that the gate "could not
13
+ * run". Comments are stripped by the same reader the source rules use rather than by a second regex,
14
+ * because the hard part is not finding `//` but knowing when it is inside a string.
15
+ * @param text the document to read.
16
+ * @returns the parsed value, or the reason it could not be parsed.
17
+ */
18
+ export declare const parseJsonc: (text: string) => ParsedJson;
19
+ /**
20
+ * Whether a parsed value can be read as an object with string keys.
21
+ * @param raw the value to narrow, typically a fragment of a parsed manifest.
22
+ * @returns whether it is a non-null object.
23
+ */
24
+ export declare const isRecord: (raw: unknown) => raw is Record<string, unknown>;
25
+ /**
26
+ * Read a parsed value as an object, or as an empty one when it is not.
27
+ *
28
+ * A malformed value yields no keys rather than an error, so a typo in a manifest leaves a rule reading
29
+ * its defaults instead of failing the whole run. Every default in this package is the strict end of its
30
+ * setting, which is what makes that safe.
31
+ * @param raw the value to read.
32
+ * @returns the value narrowed to an object, or an empty object.
33
+ */
34
+ export declare const asRecord: (raw: unknown) => Record<string, unknown>;
35
+ /**
36
+ * Read a parsed value as an object of strings, dropping every key whose value is not one.
37
+ * @param raw the value to read.
38
+ * @returns the string-valued keys of the value, or an empty object.
39
+ */
40
+ export declare const asStringRecord: (raw: unknown) => Record<string, string>;
41
+ /**
42
+ * Whether a parsed value is an array of values still to be narrowed.
43
+ *
44
+ * `Array.isArray` alone narrows an `unknown` to `any[]`, which hands every element back untyped and
45
+ * puts the narrowing beyond the compiler's reach again one step later. This says the same thing and
46
+ * leaves each element `unknown`, so the caller has to state what it expects.
47
+ * @param raw the value to narrow.
48
+ * @returns whether it is an array.
49
+ */
50
+ export declare const isArray: (raw: unknown) => raw is readonly unknown[];
51
+ /**
52
+ * Read a parsed value as text, or as the empty string when it is not text.
53
+ * @param raw the value to read.
54
+ * @returns the string, or an empty one.
55
+ */
56
+ export declare const asText: (raw: unknown) => string;
57
+ /**
58
+ * Read a parsed value as text, or as undefined when it is not text.
59
+ *
60
+ * Distinct from `asText` because the two answer different questions. An empty string is a value a rule
61
+ * compares against; `undefined` says the value was never declared, which is how a caller states that a
62
+ * rule has nothing to apply. Collapsing the two would turn "ploaness pins no package manager" into
63
+ * "ploaness requires the package manager to be the empty string".
64
+ * @param raw the value to read.
65
+ * @returns the string, or undefined.
66
+ */
67
+ export declare const asOptionalText: (raw: unknown) => string | undefined;
68
+ /**
69
+ * Read one key of a parsed value.
70
+ * @param raw the value holding the key.
71
+ * @param key the key to read.
72
+ * @returns the value at that key, or undefined when the value is not an object.
73
+ */
74
+ export declare const readKey: (raw: unknown, key: string) => unknown;
75
+ /**
76
+ * Every package a manifest declares, whichever block it sits in.
77
+ *
78
+ * Three modules asked this question and three of them answered it, which is the shape of a drift rather
79
+ * than of a helper: the CLI's preflight copy read the versions as unknowns and its freshness copy read
80
+ * them as strings, so the two disagreed about what a manifest declaring a non-string version had said.
81
+ * It lives here because the question is about the shape of a parsed manifest and not about any one rule.
82
+ * @param packageJson the parsed manifest.
83
+ * @returns the declared name-to-version pairs, a devDependency winning a name declared in both blocks.
84
+ */
85
+ export declare const declaredDependencies: (packageJson: unknown) => Record<string, string>;
86
+ //# sourceMappingURL=json-shapes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-shapes.d.ts","sourceRoot":"","sources":["../src/json-shapes.ts"],"names":[],"mappings":"AAYA,+DAA+D;AAC/D,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;IACvB,0EAA0E;IAC1E,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;CACrC;AAkBD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,UAAU,GAAI,MAAM,MAAM,KAAG,UASzC,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,QAAQ,GAAI,KAAK,OAAO,KAAG,GAAG,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAC5B,CAAA;AAEzC;;;;;;;;GAQG;AACH,eAAO,MAAM,QAAQ,GAAI,KAAK,OAAO,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAA+B,CAAA;AAK7F;;;;GAIG;AACH,eAAO,MAAM,cAAc,GAAI,KAAK,OAAO,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CACM,CAAA;AAEzE;;;;;;;;GAQG;AACH,eAAO,MAAM,OAAO,GAAI,KAAK,OAAO,KAAG,GAAG,IAAI,SAAS,OAAO,EAAwB,CAAA;AAEtF;;;;GAIG;AACH,eAAO,MAAM,MAAM,GAAI,KAAK,OAAO,KAAG,MAA8C,CAAA;AAEpF;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,GAAI,KAAK,OAAO,KAAG,MAAM,GAAG,SACZ,CAAA;AAE3C;;;;;GAKG;AACH,eAAO,MAAM,OAAO,GAAI,KAAK,OAAO,EAAE,KAAK,MAAM,KAAG,OAA6B,CAAA;AAEjF;;;;;;;;;GASG;AACH,eAAO,MAAM,oBAAoB,GAAI,aAAa,OAAO,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAY/E,CAAA"}
@@ -0,0 +1,124 @@
1
+ // Narrowing a parsed manifest, which arrives as `unknown` and has to be read as an object.
2
+ //
3
+ // Six modules each carried their own copy of these two functions, and every copy narrowed with a type
4
+ // ASSERTION. That is the wrong instrument twice over: an assertion is a claim the compiler accepts
5
+ // without checking, and `type-coverage --strict` counts each one as untyped, which is what put a 100%
6
+ // score out of reach here. A user-defined type guard says exactly the same thing and IS checked, so the
7
+ // narrowing survives a refactor that changes the shape underneath it.
8
+ //
9
+ // An array narrows to a record, as it did before: `typeof [] === 'object'`, and a manifest key holding
10
+ // an array is read by whichever caller cares, not refused here.
11
+ import { maskLiterals, stripComments } from './source-text.js';
12
+ // A trailing comma is legal in JSONC and in nothing `JSON.parse` accepts, so it is removed after the
13
+ // comments are, when a comma followed only by whitespace and a closing bracket can be recognised.
14
+ const TRAILING_COMMA = /,(?=\s*[\]}])/g;
15
+ // Located on a mask rather than on the document, and removed from the document by offset. Run over the
16
+ // text itself the pattern reads inside a string too, and a glob such as `{ts,}` in a Biome `includes`
17
+ // lost the comma it declared - a silent edit to a parsed value, in the reader every wiring rule uses.
18
+ // Removed from the end backwards so each remaining offset still names the character it named.
19
+ const withoutTrailingCommas = (text) => [...maskLiterals(text).matchAll(TRAILING_COMMA)]
20
+ .map((match) => match.index)
21
+ .reduceRight((current, at) => current.slice(0, at) + current.slice(at + 1), text);
22
+ /**
23
+ * Parse JSON that may carry comments and trailing commas.
24
+ *
25
+ * `tsconfig.json` legally carries both, and so does a Biome config - which meant the rules that judge
26
+ * them threw a SyntaxError out of a pure function on a project scaffolded by `create-payload-app`. The
27
+ * gate that caught it is a precondition, so the whole run halted saying only that the gate "could not
28
+ * run". Comments are stripped by the same reader the source rules use rather than by a second regex,
29
+ * because the hard part is not finding `//` but knowing when it is inside a string.
30
+ * @param text the document to read.
31
+ * @returns the parsed value, or the reason it could not be parsed.
32
+ */
33
+ export const parseJsonc = (text) => {
34
+ try {
35
+ return {
36
+ value: JSON.parse(withoutTrailingCommas(stripComments(text))),
37
+ problem: undefined,
38
+ };
39
+ }
40
+ catch (error) {
41
+ return { value: undefined, problem: error instanceof Error ? error.message : String(error) };
42
+ }
43
+ };
44
+ /**
45
+ * Whether a parsed value can be read as an object with string keys.
46
+ * @param raw the value to narrow, typically a fragment of a parsed manifest.
47
+ * @returns whether it is a non-null object.
48
+ */
49
+ export const isRecord = (raw) => typeof raw === 'object' && raw !== null;
50
+ /**
51
+ * Read a parsed value as an object, or as an empty one when it is not.
52
+ *
53
+ * A malformed value yields no keys rather than an error, so a typo in a manifest leaves a rule reading
54
+ * its defaults instead of failing the whole run. Every default in this package is the strict end of its
55
+ * setting, which is what makes that safe.
56
+ * @param raw the value to read.
57
+ * @returns the value narrowed to an object, or an empty object.
58
+ */
59
+ export const asRecord = (raw) => (isRecord(raw) ? raw : {});
60
+ const isStringEntry = (entry) => typeof entry[1] === 'string';
61
+ /**
62
+ * Read a parsed value as an object of strings, dropping every key whose value is not one.
63
+ * @param raw the value to read.
64
+ * @returns the string-valued keys of the value, or an empty object.
65
+ */
66
+ export const asStringRecord = (raw) => Object.fromEntries(Object.entries(asRecord(raw)).filter(isStringEntry));
67
+ /**
68
+ * Whether a parsed value is an array of values still to be narrowed.
69
+ *
70
+ * `Array.isArray` alone narrows an `unknown` to `any[]`, which hands every element back untyped and
71
+ * puts the narrowing beyond the compiler's reach again one step later. This says the same thing and
72
+ * leaves each element `unknown`, so the caller has to state what it expects.
73
+ * @param raw the value to narrow.
74
+ * @returns whether it is an array.
75
+ */
76
+ export const isArray = (raw) => Array.isArray(raw);
77
+ /**
78
+ * Read a parsed value as text, or as the empty string when it is not text.
79
+ * @param raw the value to read.
80
+ * @returns the string, or an empty one.
81
+ */
82
+ export const asText = (raw) => (typeof raw === 'string' ? raw : '');
83
+ /**
84
+ * Read a parsed value as text, or as undefined when it is not text.
85
+ *
86
+ * Distinct from `asText` because the two answer different questions. An empty string is a value a rule
87
+ * compares against; `undefined` says the value was never declared, which is how a caller states that a
88
+ * rule has nothing to apply. Collapsing the two would turn "ploaness pins no package manager" into
89
+ * "ploaness requires the package manager to be the empty string".
90
+ * @param raw the value to read.
91
+ * @returns the string, or undefined.
92
+ */
93
+ export const asOptionalText = (raw) => typeof raw === 'string' ? raw : undefined;
94
+ /**
95
+ * Read one key of a parsed value.
96
+ * @param raw the value holding the key.
97
+ * @param key the key to read.
98
+ * @returns the value at that key, or undefined when the value is not an object.
99
+ */
100
+ export const readKey = (raw, key) => asRecord(raw)[key];
101
+ /**
102
+ * Every package a manifest declares, whichever block it sits in.
103
+ *
104
+ * Three modules asked this question and three of them answered it, which is the shape of a drift rather
105
+ * than of a helper: the CLI's preflight copy read the versions as unknowns and its freshness copy read
106
+ * them as strings, so the two disagreed about what a manifest declaring a non-string version had said.
107
+ * It lives here because the question is about the shape of a parsed manifest and not about any one rule.
108
+ * @param packageJson the parsed manifest.
109
+ * @returns the declared name-to-version pairs, a devDependency winning a name declared in both blocks.
110
+ */
111
+ export const declaredDependencies = (packageJson) => ({
112
+ ...asStringRecord(readKey(packageJson, 'dependencies')),
113
+ ...asStringRecord(readKey(packageJson, 'devDependencies')),
114
+ // Optional dependencies are INSTALLED dependencies whose absence is tolerated, so every rule about a
115
+ // declared version applies to them: a range still floats, a pin still has to match, and a Payload
116
+ // package still has to agree with its family. Left out, they were the one block a project could put a
117
+ // stale version in and have nothing notice - which is where a real consumer's platform-specific image
118
+ // packages sat, still naming the sharp release the project had just moved off.
119
+ //
120
+ // `peerDependencies` are deliberately NOT here. A peer is a requirement on the consumer's tree rather
121
+ // than a version this package installs, and a range is the correct way to express one.
122
+ ...asStringRecord(readKey(packageJson, 'optionalDependencies')),
123
+ });
124
+ //# sourceMappingURL=json-shapes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-shapes.js","sourceRoot":"","sources":["../src/json-shapes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Evaluate an SPDX licence expression against the policy.
3
+ *
4
+ * An OR passes when ANY operand does, because the consumer may choose that operand; an AND requires
5
+ * every one; a parenthesised group is evaluated as a unit rather than flattened away.
6
+ * @param expression the declared licence, which may be a compound SPDX expression.
7
+ * @returns whether the policy permits it.
8
+ */
9
+ export declare const isLicenseAllowed: (expression: string) => boolean;
10
+ export interface LicensedPackage {
11
+ readonly name: string;
12
+ readonly license: string;
13
+ }
14
+ export declare const findLicenseViolations: (packages: readonly LicensedPackage[]) => readonly LicensedPackage[];
15
+ //# sourceMappingURL=license-allowlist.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"license-allowlist.d.ts","sourceRoot":"","sources":["../src/license-allowlist.ts"],"names":[],"mappings":"AAsKA;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,GAAI,YAAY,MAAM,KAAG,OAWrD,CAAA;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CACzB;AAGD,eAAO,MAAM,qBAAqB,GAChC,UAAU,SAAS,eAAe,EAAE,KACnC,SAAS,eAAe,EACmE,CAAA"}
@@ -0,0 +1,157 @@
1
+ import { escapeForRegex } from './text-escapes.js';
2
+ // License allowlist policy for dependencies. The pure logic - the allowlist and the SPDX-expression
3
+ // evaluator - lives here so it is unit-tested; the `licenses` gate in
4
+ // packages/cli/src/checks/dependencies.ts shells out to pnpm and reports.
5
+ // The two groups are arrays rather than sets so the allowlist can be assembled with a single
6
+ // constructor call. Merging two sets would want `Set.prototype.union`, which the ES2023 lib target does
7
+ // not carry, and a lint fix that reaches for it produces code that does not compile.
8
+ // Permissive licenses: no copyleft obligations, safe for an MIT-licensed template.
9
+ // Three entries are here for a reason the identifier does not show. `PSF-2.0` and `Python-2.0` are the
10
+ // same terms under two names, which is not a distinction a consumer can act on: `argparse` - reached
11
+ // through `js-yaml`, so present in almost every tree rather than chosen - shipped 3.0.0 as the second
12
+ // and 3.0.1 as the first. `EDL-1.0` carries the Eclipse name but is the BSD-3-Clause text, so it
13
+ // belongs beside BSD rather than beside EPL. `X11` is a separate SPDX id from `MIT` for the same
14
+ // terms, and a package that picks it is not asking for anything MIT does not already grant.
15
+ const PERMISSIVE = [
16
+ '0BSD',
17
+ 'AFL-2.1',
18
+ 'AFL-3.0',
19
+ 'Apache-2.0',
20
+ 'Artistic-2.0',
21
+ 'BSD-2-Clause',
22
+ 'BSD-3-Clause',
23
+ 'BSL-1.0',
24
+ 'BlueOak-1.0.0',
25
+ 'CC-BY-3.0',
26
+ 'CC-BY-4.0',
27
+ 'CC0-1.0',
28
+ 'EDL-1.0',
29
+ 'ISC',
30
+ 'MIT',
31
+ 'MIT-0',
32
+ 'MulanPSL-2.0',
33
+ 'NCSA',
34
+ 'OFL-1.1',
35
+ 'PSF-2.0',
36
+ 'PostgreSQL',
37
+ 'Python-2.0',
38
+ 'UPL-1.0',
39
+ 'Unicode-3.0',
40
+ 'Unicode-DFS-2016',
41
+ 'Unlicense',
42
+ 'W3C',
43
+ 'WTFPL',
44
+ 'X11',
45
+ 'Zlib',
46
+ ];
47
+ // Weak copyleft, permitted only as UNMODIFIED dependencies: the file-level (MPL, EPL, CDDL) and
48
+ // dynamic-linking (LGPL) copyleft does not reach this project's own source. Each id is listed
49
+ // explicitly so it stays a conscious decision. Strong copyleft (GPL, AGPL) is deliberately absent and
50
+ // therefore rejected, and so is a license that restricts the field of use - `Hippocratic-2.1` reads as
51
+ // an ethical licence and is not an open-source one, which is a distinction only this list records.
52
+ //
53
+ // The bare `LGPL-2.1` and `LGPL-3.0` are DEPRECATED SPDX ids, and they are here because a policy
54
+ // admits a license rather than a spelling of one: listing `-only` and `-or-later` while omitting the
55
+ // form most packages actually write rejected the licence this list was written to permit. The `+`
56
+ // suffix reaches the bare id too, so `LGPL-3.0+` resolves here rather than nowhere.
57
+ const WEAK_COPYLEFT = [
58
+ 'CDDL-1.0',
59
+ 'CDDL-1.1',
60
+ 'EPL-1.0',
61
+ 'EPL-2.0',
62
+ 'LGPL-2.1',
63
+ 'LGPL-2.1-only',
64
+ 'LGPL-2.1-or-later',
65
+ 'LGPL-3.0',
66
+ 'LGPL-3.0-only',
67
+ 'LGPL-3.0-or-later',
68
+ 'MPL-1.1',
69
+ 'MPL-2.0',
70
+ ];
71
+ const ALLOWED = new Set([...PERMISSIVE, ...WEAK_COPYLEFT]);
72
+ // SPDX ids are case-insensitive in the registries that emit them, and `Apache-2.0` arrives spelled at
73
+ // least three ways. The allowlist is matched on a folded id so a casing difference is not a licence
74
+ // decision. A `WITH` exception (`GPL-2.0 WITH Classpath-exception-2.0`) names the same licence plus a
75
+ // grant, so the licence half decides; a `+` suffix means "or later", which is the same licence too.
76
+ const ALLOWED_FOLDED = new Set([...ALLOWED].map((id) => id.toLowerCase()));
77
+ // Anchored on a single space run rather than a nested quantifier: `\s+WITH\s+(?:\S.*)?$` backtracks
78
+ // super-linearly on a long id, and an expression comes from a registry rather than from this repository.
79
+ const WITH_EXCEPTION = /\sWITH\s[\s\S]*$/i;
80
+ const OR_LATER = /\+$/;
81
+ const isAllowedId = (id) => ALLOWED_FOLDED.has(id.trim().replace(WITH_EXCEPTION, '').replace(OR_LATER, '').toLowerCase());
82
+ // The operators, lowest precedence first. SPDX binds AND more tightly than OR, so a correct evaluator
83
+ // splits on OR before AND - and the ONLY correct order is that one. Splitting on AND first, after
84
+ // stripping the parentheses, turned `(MIT OR Apache-2.0) AND ISC` into the operands
85
+ // `MIT OR Apache-2.0` and `ISC`, and then failed the first as though it were an unknown id: every
86
+ // mixed expression was reported as a licence violation whatever it actually said.
87
+ const OR = ' OR ';
88
+ const AND = ' AND ';
89
+ const stepDepth = (depth, character) => {
90
+ if (character === '(') {
91
+ return depth + 1;
92
+ }
93
+ return character === ')' ? depth - 1 : depth;
94
+ };
95
+ // Both figures come from one walk, because the two questions below are about the same characters: how
96
+ // far a fragment leaves the parentheses open, and whether it ever closed a group it had not opened.
97
+ const walkParentheses = (text) =>
98
+ // Only the two parentheses are counted, and neither of them decomposes.
99
+ // eslint-disable-next-line @typescript-eslint/no-misused-spread -- see the note above
100
+ [...text].reduce((state, character) => {
101
+ const depth = stepDepth(state.depth, character);
102
+ return { depth, lowest: Math.min(state.lowest, depth) };
103
+ }, { depth: 0, lowest: 0 });
104
+ // How far a fragment leaves the parentheses open, so an operator inside a group is not split on.
105
+ const balanceOf = (text) => walkParentheses(text).depth;
106
+ // Where the operator occurs at parenthesis depth zero, which is the only place it separates operands.
107
+ // An occurrence inside a group belongs to that group: in `(MIT OR Apache-2.0) AND ISC` the OR is the
108
+ // group's own, and splitting on it would produce operands the author never wrote.
109
+ const splitPoints = (expression, operator) => [...expression.matchAll(new RegExp(escapeForRegex(operator), 'g'))]
110
+ .map((match) => match.index)
111
+ .filter((index) => balanceOf(expression.slice(0, index)) === 0);
112
+ const splitOutside = (expression, operator) => {
113
+ const points = splitPoints(expression, operator);
114
+ const ends = [...points, expression.length];
115
+ return [0, ...points.map((point) => point + operator.length)].map((start, index) => expression.slice(start, ends[index] ?? expression.length));
116
+ };
117
+ // Whether the outer parentheses enclose the WHOLE expression, rather than opening one group and closing
118
+ // a different one. A net balance of zero does not answer that, and answering it that way was wrong in
119
+ // both directions: `(MIT) OR (ISC)` also nets to zero, and stripping its outer characters left
120
+ // `MIT) OR (ISC`, where the early `)` drives the running depth negative so no operator sits at depth
121
+ // zero any more. Such an expression was then read as one unrecognised id and rejected - while
122
+ // `(MIT OR GPL-3.0-only) AND (GPL-3.0-only OR AGPL-3.0-only)` split at two ORs that were never
123
+ // top-level, found `MIT` among the pieces, and passed. The lowest depth the inner text reaches is what
124
+ // separates the two cases.
125
+ const isWhollyEnclosed = (inner) => {
126
+ const walk = walkParentheses(inner);
127
+ return walk.depth === 0 && walk.lowest === 0;
128
+ };
129
+ const unwrap = (expression) => {
130
+ const trimmed = expression.trim();
131
+ return trimmed.startsWith('(') && trimmed.endsWith(')') && isWhollyEnclosed(trimmed.slice(1, -1))
132
+ ? unwrap(trimmed.slice(1, -1))
133
+ : trimmed;
134
+ };
135
+ /**
136
+ * Evaluate an SPDX licence expression against the policy.
137
+ *
138
+ * An OR passes when ANY operand does, because the consumer may choose that operand; an AND requires
139
+ * every one; a parenthesised group is evaluated as a unit rather than flattened away.
140
+ * @param expression the declared licence, which may be a compound SPDX expression.
141
+ * @returns whether the policy permits it.
142
+ */
143
+ export const isLicenseAllowed = (expression) => {
144
+ const clean = unwrap(expression);
145
+ const alternatives = splitOutside(clean, OR);
146
+ if (alternatives.length > 1) {
147
+ return alternatives.some((part) => isLicenseAllowed(part));
148
+ }
149
+ const conjuncts = splitOutside(clean, AND);
150
+ if (conjuncts.length > 1) {
151
+ return conjuncts.every((part) => isLicenseAllowed(part));
152
+ }
153
+ return isAllowedId(clean);
154
+ };
155
+ // Return the packages whose license is not permitted by policy; an empty array means a clean tree.
156
+ export const findLicenseViolations = (packages) => packages.filter((package_) => !isLicenseAllowed(package_.license));
157
+ //# sourceMappingURL=license-allowlist.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"license-allowlist.js","sourceRoot":"","sources":["../src/license-allowlist.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,69 @@
1
+ /** The sentence of the governing standard this module decides, quoted back in every refusal. */
2
+ export declare const NETWORK_RULE: string;
3
+ /** Which runtime entry point asked. Each reaches its verdict differently, so it is data, not a flag. */
4
+ export type NetworkApi = 'connect' | 'lookup' | 'resolve' | 'fetch';
5
+ /** One attempted reach, normalised from whichever entry point a test called. */
6
+ export interface NetworkAttempt {
7
+ readonly api: NetworkApi;
8
+ /** The host as the caller wrote it, or undefined for a unix socket, which is not a network at all. */
9
+ readonly host: string | undefined;
10
+ /** What a refusal names, for example `127.0.0.1:5432` or `https://api.example.com/v1`. */
11
+ readonly destination: string;
12
+ }
13
+ /** One entry of `os.networkInterfaces()`, narrowed to the single field this module reads. */
14
+ export interface NetworkInterfaceAddress {
15
+ readonly address: string;
16
+ }
17
+ /** The resolver entry points, every one of which queries a nameserver rather than reading a hosts file. */
18
+ export declare const RESOLVER_METHODS: readonly string[];
19
+ /**
20
+ * The addresses that name this machine, read out of an `os.networkInterfaces()` result.
21
+ *
22
+ * A test that connects to the box's own routable address is still on the machine it runs on, so those
23
+ * addresses join the loopback range rather than being refused with everything else.
24
+ * @param interfaces the interface map, as `os.networkInterfaces()` returns it.
25
+ * @returns every address it carries, in the canonical spelling {@link isMachineLocalHost} compares against.
26
+ */
27
+ export declare const localAddresses: (interfaces: Readonly<Record<string, readonly NetworkInterfaceAddress[] | undefined>>) => ReadonlySet<string>;
28
+ /**
29
+ * Whether a host names the machine the test runs on.
30
+ *
31
+ * A name that is not one of the localhost spellings is refused on its face rather than resolved, because
32
+ * a resolution is itself the thing being refused. That is why a bare service name such as `postgres`,
33
+ * and a docker gateway such as `host.docker.internal`, do not pass: neither can be shown to be this
34
+ * machine without asking a nameserver.
35
+ * @param host the host as the caller wrote it, or undefined when the caller named none.
36
+ * @param local the addresses this machine carries, from {@link localAddresses}.
37
+ * @returns true when the reach stays on this machine.
38
+ */
39
+ export declare const isMachineLocalHost: (host: string | undefined, local: ReadonlySet<string>) => boolean;
40
+ /**
41
+ * The host a URL names.
42
+ *
43
+ * Undefined when the value names no host - a relative path, which under a DOM environment is the page's
44
+ * own origin and reaches nothing, or a string that is not a URL at all.
45
+ * @param rawUrl the value a test passed to fetch.
46
+ * @returns the host, or undefined.
47
+ */
48
+ export declare const hostOfUrl: (rawUrl: string) => string | undefined;
49
+ /**
50
+ * Read one `net.Socket.prototype.connect` call, whichever of its three forms the caller used.
51
+ *
52
+ * A unix socket reports no host, because a path is not a network: the two ends are the same machine by
53
+ * construction, and refusing one would break every test that talks to a local service over a socket file.
54
+ * @param callArguments the arguments the call was made with.
55
+ * @returns the attempt, normalised.
56
+ */
57
+ export declare const describeSocketTarget: (callArguments: readonly unknown[]) => NetworkAttempt;
58
+ /**
59
+ * Decide one attempted reach.
60
+ *
61
+ * A resolver query is refused whatever host it names, loopback included. The resolver family bypasses
62
+ * the hosts file and sends a packet to a nameserver, so `resolve4('localhost')` leaves the machine as
63
+ * surely as any other name does.
64
+ * @param attempt the reach to judge.
65
+ * @param local the addresses this machine carries, from {@link localAddresses}.
66
+ * @returns undefined when the reach is allowed, or the message a refusal should carry.
67
+ */
68
+ export declare const findNetworkEscape: (attempt: NetworkAttempt, local: ReadonlySet<string>) => string | undefined;
69
+ //# sourceMappingURL=network-policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"network-policy.d.ts","sourceRoot":"","sources":["../src/network-policy.ts"],"names":[],"mappings":"AAcA,gGAAgG;AAChG,eAAO,MAAM,YAAY,EAAE,MAAmE,CAAA;AAE9F,wGAAwG;AACxG,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,CAAA;AAEnE,gFAAgF;AAChF,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAA;IACxB,sGAAsG;IACtG,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAA;IACjC,0FAA0F;IAC1F,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;CAC7B;AAED,6FAA6F;AAC7F,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CACzB;AAED,2GAA2G;AAC3G,eAAO,MAAM,gBAAgB,EAAE,SAAS,MAAM,EAe7C,CAAA;AA6CD;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,GACzB,YAAY,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,uBAAuB,EAAE,GAAG,SAAS,CAAC,CAAC,KACnF,WAAW,CAAC,MAAM,CASlB,CAAA;AAEH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kBAAkB,GAC7B,MAAM,MAAM,GAAG,SAAS,EACxB,OAAO,WAAW,CAAC,MAAM,CAAC,KACzB,OAMF,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,SAAS,GAAI,QAAQ,MAAM,KAAG,MAAM,GAAG,SAMnD,CAAA;AAuBD;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,GAAI,eAAe,SAAS,OAAO,EAAE,KAAG,cAcxE,CAAA;AAUD;;;;;;;;;GASG;AACH,eAAO,MAAM,iBAAiB,GAC5B,SAAS,cAAc,EACvB,OAAO,WAAW,CAAC,MAAM,CAAC,KACzB,MAAM,GAAG,SAKX,CAAA"}