blastproof 0.12.1 → 0.13.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.
package/README.md CHANGED
@@ -198,6 +198,8 @@ ignore:
198
198
  - "**/*.md"
199
199
  ```
200
200
 
201
+ The key is the file glob and the value is the routes it can affect — the opposite way round from a test file's own `routes:`, which is a plain list of the routes that test covers. Inverted, the map matches nothing at all and every run reports a diff that affected no page, so `blastproof` refuses a map written that way rather than running green against it.
202
+
201
203
  Every changed file lands in one of three buckets: it matches `routes:` and contributes them, matches `ignore:` and is knowingly irrelevant, or **matches neither — nobody has said what it affects**. `--fail-on-unmapped` blocks on that third case, naming the files and both ways to resolve them.
202
204
 
203
205
  **Nothing is ignored by default**, on purpose: a default that guesses on your behalf would hide the first files worth thinking about. The flag is additive — a run can meet `--min-score` and still be blocked here, because "the tests I ran passed" and "something changed that nobody classified" are different claims.
package/dist/cli.js CHANGED
@@ -49,6 +49,9 @@ browser:
49
49
  # max_retries_per_step: 3
50
50
 
51
51
  # Impact hints mapping file globs to routes (used by \`blastproof run --impacted\`).
52
+ # Read each line as "if this file changed, these pages are at risk" \u2014 the key is the
53
+ # file glob, the value is the routes. Not the other way round: a test file's own
54
+ # \`routes:\` is a list of routes, and this one is a map keyed by source path.
52
55
  routes:
53
56
  "src/auth/**": ["/login"]
54
57
  "src/cart/**": ["/cart", "/checkout"]
@@ -988,11 +991,33 @@ var authSchema = z.object({
988
991
  });
989
992
  }
990
993
  });
994
+ var ROUTE_SHAPED = /^\/[^*?]*$/;
995
+ var FILE_SHAPED = /[*?]|\.[a-z0-9]{1,8}$/i;
996
+ function findInvertedRouteEntries(routes) {
997
+ const inverted = [];
998
+ for (const [key, values] of Object.entries(routes)) {
999
+ if (!ROUTE_SHAPED.test(key)) continue;
1000
+ const file = values.find((value) => FILE_SHAPED.test(value));
1001
+ if (file !== void 0) inverted.push({ key, file });
1002
+ }
1003
+ return inverted;
1004
+ }
991
1005
  var configSchema = z.object({
992
1006
  base_url: z.string().url(),
993
1007
  llm: llmSchema.default({}),
994
1008
  browser: browserSchema.default({}),
995
- routes: z.record(z.array(z.string())).optional(),
1009
+ routes: z.record(z.array(z.string())).superRefine((value, ctx) => {
1010
+ const inverted = findInvertedRouteEntries(value);
1011
+ const first = inverted[0];
1012
+ if (!first) return;
1013
+ const others = inverted.length > 1 ? ` (and ${inverted.length - 1} more)` : "";
1014
+ ctx.addIssue({
1015
+ code: z.ZodIssueCode.custom,
1016
+ message: `is the wrong way round${others} \u2014 the key is the file glob, the value is the routes it affects.
1017
+ found: "${first.key}": ["${first.file}"]
1018
+ expected: "${first.file}": ["${first.key}"]`
1019
+ });
1020
+ }).optional(),
996
1021
  /** Globs for files knowingly irrelevant to any route (docs, CI config, licences). */
997
1022
  ignore: z.array(z.string()).optional(),
998
1023
  /**
@@ -2893,7 +2918,7 @@ function parsePositiveNumber(flag) {
2893
2918
  };
2894
2919
  }
2895
2920
  var program = new Command();
2896
- program.name("blastproof").description("Open-source AI testing agent: plain-English YAML tests executed agentically on a real browser.").version("0.12.1");
2921
+ program.name("blastproof").description("Open-source AI testing agent: plain-English YAML tests executed agentically on a real browser.").version("0.13.0");
2897
2922
  program.command("init").description("Scaffold .blastproof/ (config, tests, sample tests) in the current directory").action(async () => {
2898
2923
  try {
2899
2924
  const result = await initProject(process.cwd());