@ai-ecoverse/slicc-browsers 1.7.6 → 1.7.7

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
@@ -191,7 +191,7 @@ Popups are observed by `Target.targetCreated` with the opener's `openerId`, or b
191
191
 
192
192
  **The control rule.** Every vector has a control on an ordinary origin: the same setup and trigger against an unprotected origin must succeed. A missing or failing control fails the id. With `via: "guard"`, the control runs through the guard. With `via: "unguarded"`, the runner runs setup and trigger through its own CDP against the ordinary origin and against the protected stand-in, and both must show the effect. The runner reports the control's transport as `via`.
193
193
 
194
- **Refusals.** Each refusable step has a refusal name: its `as`, or `refused.<i>`, where `i` is its index in the vector's steps, setup then trigger, with each `children` step's steps right after it, counted from 0 before `on` is applied. When a guard refuses the step and the error matches `refusal.match`, the runner sets that name to `true` in the leg's observations. On that leg, an observation whose `dependsOn` lists the name is skipped; every other observation is checked as usual. A guard that refuses the step on every origin passes in one of two ways: through the guard, with the step refused on both legs, or with an unguarded control and the guard's matched refusal on the guarded leg. With `via: "guard"`, a refusal on one leg only fails that leg. A guard that allows the step must pass its control through the guard.
194
+ **Refusals.** Each refusable step has a refusal name: its `as`, or `refused.<i>`, where `i` is its index in the vector's steps, setup then trigger, with each `children` step's steps right after it, counted from 0 before `on` is applied. When a guard refuses the step and the error matches `refusal.match`, the runner sets that name to `true` in the leg's observations. On that leg, an observation whose `dependsOn` lists the name is skipped; every other observation is checked as usual. A guard that refuses the step on every origin passes in one of two ways: through the guard, with the step refused on both legs, or with an unguarded control and the guard's matched refusal on the guarded leg. With `via: "guard"`, a refusal on one leg only fails that leg. The comparison only counts steps that ran on both legs. A step that ran on one leg only (for example a `children` step on the control's popup, when the guarded leg never gets that popup attached) doesn't fail the row by itself, and the row reports it in `ran`, as `{ "refused.5": "control-only" }` or `"guarded-only"`. Observations that depend on such a step follow their `dependsOn` on each leg. A guard that allows the step must pass its control through the guard.
195
195
 
196
196
  **Liveness.** A runner checks `live` through its own CDP, never through the guard path: either the protected document loaded (title, marker or URL), or the guard returned its own refusal for a refusable step. A failing `live` fails the id.
197
197
 
@@ -285,7 +285,7 @@ target. In observations and liveness checks, an `any` alternative that reference
285
285
  a `null` origin is dropped and the rest are evaluated; the row skips only when
286
286
  every alternative of an `any`, or a required (`all`, `not` or plain) check, needs it. Tags and cookie names are unique per leg. `variant`
287
287
  selects one variant or an array; omission selects all. `parallel` defaults to 1.
288
- `log(row)` receives completed rows. A variant supplies the wait before an
288
+ `log(row)` receives completed rows. Besides `control`, `guarded`, `live`, `why` and `errors`, a row may carry `ran` (steps that ran on one leg only) and `notes` (settles skipped because their registration was refused). A variant supplies the wait before an
289
289
  explicit `Target.detachFromTarget` step and for a `settle.variant` step.
290
290
 
291
291
  Every runnable vector has a declarative `liveCheck`; every observation has a
@@ -336,7 +336,7 @@ load event never ends a wait. After a child's `children` steps that include `Pag
336
336
  the runner waits, bounded at 5 s, for the child's first document to commit and load before
337
337
  any later child step. `{ "settle": { "beacon", "required": true, "dependsOn"? } }` waits
338
338
  for that beacon at the fixture and fails the row with `settle: no beacon from <origin>
339
- within <ms>ms` when it never arrives, unless a refusal it `dependsOn` was recorded; the rows
339
+ within <ms>ms` when it never arrives, unless a refusal it `dependsOn` was recorded. It checks `dependsOn` again while it waits, since a child's `children` steps may still be running. When the registration it depends on was refused, the settle is skipped, the row's `notes` gets `<leg> settle:skipped-refused beacon <origin>`, and the row is judged on its other observations. The rows
340
340
  whose `allowedBeacon` comes from an earlier document settle on it before their trigger.
341
341
  Beacons use `navigator.sendBeacon`, with a `fetch(…, { keepalive: true })` fallback. An observation that reads a background step's `as` result
342
342
  waits for that step, up to 10 s, and fails with `<as> did not complete within …` past it. Redirect
package/guard-driver.mjs CHANGED
@@ -173,7 +173,18 @@ export function result(vector, run) {
173
173
  }
174
174
  const c = run.control ? judge(vector, 'control', run.control) : null;
175
175
  const g = run.guarded ? judge(vector, 'guarded', run.guarded) : null;
176
- const only = (a, b) => (a?.refused ?? []).filter((name) => !(b?.refused ?? []).includes(name));
176
+ const ranOn = (observed, name) => typeof observed?.[name] === 'boolean';
177
+ const both = (name) => ranOn(run.control, name) && ranOn(run.guarded, name);
178
+ const only = (a, b) =>
179
+ (a?.refused ?? []).filter((name) => both(name) && !(b?.refused ?? []).includes(name));
180
+ const ran = Object.fromEntries(
181
+ refusals(vector)
182
+ .filter(
183
+ (name) =>
184
+ run.control && run.guarded && ranOn(run.control, name) !== ranOn(run.guarded, name)
185
+ )
186
+ .map((name) => [name, ranOn(run.control, name) ? 'control-only' : 'guarded-only'])
187
+ );
177
188
  const why = [];
178
189
  let control = c?.pass ? 'pass' : 'fail';
179
190
  if (!c) why.push('no control');
@@ -195,7 +206,14 @@ export function result(vector, run) {
195
206
  } else why.push(...g.mismatches);
196
207
  const live = run.live === true ? 'pass' : 'fail';
197
208
  if (live === 'fail') why.push('not live');
198
- return { ...row, control, guarded, live, ...(why.length ? { why: why.join('; ') } : {}) };
209
+ return {
210
+ ...row,
211
+ control,
212
+ guarded,
213
+ live,
214
+ ...(Object.keys(ran).length ? { ran } : {}),
215
+ ...(why.length ? { why: why.join('; ') } : {}),
216
+ };
199
217
  }
200
218
 
201
219
  const passed = (r) =>
package/guard-runner.mjs CHANGED
@@ -107,6 +107,7 @@ class Execution {
107
107
  this.contexts = [];
108
108
  this.pending = [];
109
109
  this.errors = [];
110
+ this.notes = [];
110
111
  this.answers = [];
111
112
  this.attachSettings = new Map();
112
113
  this.childSteps = [];
@@ -604,13 +605,20 @@ class Execution {
604
605
  }
605
606
 
606
607
  async settle(settle) {
607
- if ((settle.dependsOn ?? []).some((name) => this.refused[name])) return;
608
+ const refused = () => (settle.dependsOn ?? []).some((name) => this.refused[name]);
609
+ const skipped = () => {
610
+ this.notes.push(
611
+ `settle:skipped-refused ${settle.beacon ? `beacon ${settle.beacon}` : settle.title ? `title ${settle.title}` : 'load'}`
612
+ );
613
+ };
614
+ if (refused()) return skipped();
608
615
  if (settle.variant) await sleep(this.delay);
609
616
  if (settle.title && !this.reader) {
610
617
  await sleep(settle.ms ?? 1500);
611
618
  return;
612
619
  }
613
620
  const met = await this.poll(async () => {
621
+ if (refused()) return true;
614
622
  if (settle.beacon)
615
623
  return (
616
624
  (await this.requestCheck({ beacon: settle.beacon }, this.actionRequestStart)) &&
@@ -628,6 +636,7 @@ class Execution {
628
636
  .some((e) => e.method === 'Page.loadEventFired' && e.sessionId === event.sessionId)
629
637
  );
630
638
  }, settle.ms ?? 1500);
639
+ if (refused()) return skipped();
631
640
  if (settle.required && !met)
632
641
  this.errors.push(
633
642
  `settle: no ${settle.beacon ? `beacon from ${settle.beacon}` : settle.title ? `title ${settle.title}` : 'load'} within ${settle.ms ?? 1500}ms`
@@ -986,7 +995,7 @@ class Execution {
986
995
  side: this.side,
987
996
  });
988
997
  const live = this.side === 'control' ? true : await this.check(this.vector.liveCheck);
989
- return { observed, live, errors: this.errors };
998
+ return { observed, live, errors: this.errors, notes: this.notes };
990
999
  } finally {
991
1000
  this.stopped = true;
992
1001
  await pumping;
@@ -1218,6 +1227,11 @@ async function executeRow(job, opts, clients, set, slot, index) {
1218
1227
  errors: [...legs.control.errors, ...legs.guarded.errors],
1219
1228
  designDependent: vector.designDependent ?? [],
1220
1229
  };
1230
+ const notes = [
1231
+ ...(legs.control.notes ?? []).map((note) => `control ${note}`),
1232
+ ...(legs.guarded.notes ?? []).map((note) => `guarded ${note}`),
1233
+ ];
1234
+ if (notes.length) row.notes = notes;
1221
1235
  if (row.errors.length) {
1222
1236
  row.control = 'fail';
1223
1237
  row.guarded = 'fail';
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "setVersion": "1.7.6",
3
+ "setVersion": "1.7.7",
4
4
  "sources": [
5
5
  {
6
6
  "repo": "ai-ecoverse/slicc-cli",
@@ -5172,7 +5172,7 @@
5172
5172
  },
5173
5173
  "beacon": {
5174
5174
  "means": "a beacon from {target}",
5175
- "dependsOn": ["refused.7", "refused.8"],
5175
+ "dependsOn": ["refused.3", "refused.7", "refused.8"],
5176
5176
  "requires": ["autoAttach"],
5177
5177
  "check": {
5178
5178
  "beacon": "{target}",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-ecoverse/slicc-browsers",
3
- "version": "1.7.6",
3
+ "version": "1.7.7",
4
4
  "description": "SLICC's shared browser catalog, JSON schemas and discovery conformance corpus",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -65,13 +65,13 @@
65
65
  "trust:dry": "npx fledgling sync --dry-run -y --skip-publish",
66
66
  "release": "semantic-release",
67
67
  "test:guard": "npm run test:guard-integration && npm run test:guard-sweep",
68
- "test:guard-integration": "node --test test/integration/guard-fit.test.mjs test/integration/guard-chain.test.mjs test/integration/guard-validation.test.mjs test/integration/guard-liveness.test.mjs test/integration/guard-observations.test.mjs test/integration/guard-child-latency.test.mjs test/integration/guard-adoption.test.mjs test/integration/guard-scoping.test.mjs test/integration/guard-late-pause.test.mjs",
68
+ "test:guard-integration": "node --test test/integration/guard-fit.test.mjs test/integration/guard-chain.test.mjs test/integration/guard-validation.test.mjs test/integration/guard-liveness.test.mjs test/integration/guard-observations.test.mjs test/integration/guard-child-latency.test.mjs test/integration/guard-adoption.test.mjs test/integration/guard-scoping.test.mjs test/integration/guard-late-pause.test.mjs test/integration/guard-legs.test.mjs",
69
69
  "test:guard-sweep": "node scripts/test-guards.mjs",
70
70
  "test:guard-target": "node --test test/integration/guard-runner.test.mjs",
71
71
  "test:unit:guard": "node --experimental-test-coverage --test --test-coverage-include=guard-runner.mjs --test-coverage-include=fixture-server.mjs --test-coverage-lines=100 test/unit/guard-*.test.mjs"
72
72
  },
73
73
  "devDependencies": {
74
- "@ai-ecoverse/slicc-shared-web": "1.23.0",
74
+ "@ai-ecoverse/slicc-shared-web": "1.23.1",
75
75
  "@biomejs/biome": "2.5.15",
76
76
  "ajv": "8.20.0",
77
77
  "ajv-formats": "3.0.1",