@blamejs/exceptd-skills 0.18.18 → 0.18.20

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.
@@ -27,7 +27,65 @@ const path = require("node:path");
27
27
 
28
28
  const ROOT = path.join(__dirname, "..");
29
29
  const AGENTS = path.join(ROOT, "AGENTS.md");
30
- const COLLECTOR_DIR = path.join(ROOT, "lib", "collectors");
30
+ const REAL_COLLECTOR_DIR = path.join(ROOT, "lib", "collectors");
31
+ // EXCEPTD_COLLECTOR_DIR lets the test suite drive the gate against a throwaway
32
+ // tempdir (so the require-throw / leaked-fixture behaviours are exercised
33
+ // without writing into the real lib/collectors/). It is honored ONLY when the
34
+ // explicit test-only switch EXCEPTD_COLLECTOR_DIR_TESTONLY=1 is also set, so a
35
+ // stray env var in CI or a developer shell can never point the release gate at
36
+ // an alternate directory and let missing/broken real collectors pass.
37
+ const COLLECTOR_DIR =
38
+ process.env.EXCEPTD_COLLECTOR_DIR_TESTONLY === "1" && process.env.EXCEPTD_COLLECTOR_DIR
39
+ ? path.resolve(process.env.EXCEPTD_COLLECTOR_DIR)
40
+ : REAL_COLLECTOR_DIR;
41
+
42
+ // A real shipped collector is named [a-z0-9-]+.js — the AGENTS.md enumeration
43
+ // regex (`lib/collectors/([a-z0-9-]+\.js)`) cannot even reference an
44
+ // underscore-prefixed file. So a `__`-prefixed file is never a collector:
45
+ // it is test scaffolding or a stray artifact. classifyCollectors SKIPS it so a
46
+ // leaked fixture cannot poison the count/enumeration/load-error scan; the gate
47
+ // separately FORBIDS it (see findReservedFixtures) so a leaked fixture cannot
48
+ // silently ship in the wholesale-published lib/ tree either.
49
+ function isReservedFixture(f) {
50
+ return f.startsWith("__");
51
+ }
52
+
53
+ // Reserved-prefix .js files present in `dir`. These are test scaffolding and
54
+ // must never ship; the gate fails hard if any are found in the directory it
55
+ // validates.
56
+ function findReservedFixtures(dir) {
57
+ try {
58
+ return fs.readdirSync(dir).filter((f) => f.endsWith(".js") && isReservedFixture(f));
59
+ } catch {
60
+ return [];
61
+ }
62
+ }
63
+
64
+ // Classify every <dir>/*.js into collectors (require succeeds + exports
65
+ // collect()), helpers (require succeeds, no collect() — silently excluded),
66
+ // and load-errors (require throws — surfaced, never silently dropped).
67
+ // Exported so the test suite can drive it against a tempdir.
68
+ function classifyCollectors(dir) {
69
+ const jsFiles = fs.readdirSync(dir)
70
+ .filter((f) => f.endsWith(".js") && !isReservedFixture(f))
71
+ .sort();
72
+ const collectorFiles = [];
73
+ const loadErrors = [];
74
+ for (const f of jsFiles) {
75
+ let mod;
76
+ try {
77
+ mod = require(path.join(dir, f));
78
+ } catch (e) {
79
+ loadErrors.push(`lib/collectors/${f}: ${e.message.split("\n")[0]}`);
80
+ continue;
81
+ }
82
+ if (typeof mod.collect === "function") {
83
+ collectorFiles.push(`lib/collectors/${f}`);
84
+ }
85
+ }
86
+ collectorFiles.sort();
87
+ return { collectorFiles, loadErrors };
88
+ }
31
89
 
32
90
  const WORD_TO_NUMBER = {
33
91
  one: 1, two: 2, three: 3, four: 4, five: 5, six: 6, seven: 7,
@@ -46,19 +104,24 @@ function ok(msg) {
46
104
  }
47
105
 
48
106
  function main() {
49
- let agents;
50
- try { agents = fs.readFileSync(AGENTS, "utf8"); }
51
- catch (e) {
52
- console.error(`[check-agents-md-collectors] cannot read AGENTS.md: ${e.message}`);
107
+ // Leak-guard (P2: forbid leaked reserved fixtures before packing). A
108
+ // `__`-prefixed file in the collectors dir is stray test scaffolding;
109
+ // because lib/ is published wholesale, a leaked one would otherwise ship.
110
+ // Fail hard, naming the file, so the release path deletes it.
111
+ const stray = findReservedFixtures(COLLECTOR_DIR);
112
+ if (stray.length > 0) {
113
+ console.error(
114
+ `[check-agents-md-collectors] reserved-prefix file(s) must not ship from lib/collectors/ ` +
115
+ `- delete the stray test scaffolding: ${stray.join(", ")}`
116
+ );
53
117
  process.exitCode = 2;
54
118
  return;
55
119
  }
56
120
 
57
- let jsFiles;
58
- try {
59
- jsFiles = fs.readdirSync(COLLECTOR_DIR).filter(f => f.endsWith(".js")).sort();
60
- } catch (e) {
61
- console.error(`[check-agents-md-collectors] cannot read ${COLLECTOR_DIR}: ${e.message}`);
121
+ let agents;
122
+ try { agents = fs.readFileSync(AGENTS, "utf8"); }
123
+ catch (e) {
124
+ console.error(`[check-agents-md-collectors] cannot read AGENTS.md: ${e.message}`);
62
125
  process.exitCode = 2;
63
126
  return;
64
127
  }
@@ -75,21 +138,14 @@ function main() {
75
138
  // count and the enumeration cross-check, so a file that
76
139
  // still ships in the tarball passes the gate undetected.
77
140
  // Surface it as a parse error (exit 2) naming the file.
78
- const collectorFiles = [];
79
- const loadErrors = [];
80
- for (const f of jsFiles) {
81
- let mod;
82
- try {
83
- mod = require(path.join(COLLECTOR_DIR, f));
84
- } catch (e) {
85
- loadErrors.push(`lib/collectors/${f}: ${e.message.split("\n")[0]}`);
86
- continue;
87
- }
88
- if (typeof mod.collect === "function") {
89
- collectorFiles.push(`lib/collectors/${f}`);
90
- }
141
+ let collectorFiles, loadErrors;
142
+ try {
143
+ ({ collectorFiles, loadErrors } = classifyCollectors(COLLECTOR_DIR));
144
+ } catch (e) {
145
+ console.error(`[check-agents-md-collectors] cannot read ${COLLECTOR_DIR}: ${e.message}`);
146
+ process.exitCode = 2;
147
+ return;
91
148
  }
92
- collectorFiles.sort();
93
149
 
94
150
  if (loadErrors.length > 0) {
95
151
  console.error(
@@ -146,4 +202,8 @@ function main() {
146
202
  }
147
203
  }
148
204
 
149
- main();
205
+ if (require.main === module) {
206
+ main();
207
+ }
208
+
209
+ module.exports = { classifyCollectors };