@ai-ecoverse/slicc-browsers 1.8.0 → 1.8.1
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 +3 -1
- package/guard-runner.mjs +22 -10
- package/guard-vectors.json +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -300,7 +300,9 @@ every alternative of an `any`, or a required (`all`, `not` or plain) check, need
|
|
|
300
300
|
selects one variant or an array; omission selects all. `parallel` defaults to 1.
|
|
301
301
|
With the `browserContexts` capability each leg runs in its own browser context. A `Target.createBrowserContext` that fails with a timeout is retried once, and the row's `notes` records it; any other failure fails the row.
|
|
302
302
|
|
|
303
|
-
`settleScale` (default 1) multiplies
|
|
303
|
+
`settleScale` (default 1) multiplies the bounds of waits for events the leg expects to happen: settles (beacon, served, title and load) on the control leg and `required` settles on either leg, `paused` on the control leg, page and child loads, the wait for a background step's `as` result (`<as> did not complete within <ms>ms`), and the wait for an observation the leg expects to be `true`. Adopters on slow hosts can set 3 or 4. Every wait polls and returns as soon as its event arrives, so a larger bound only costs time when something is really missing, and each timeout error names the bound it used. An observation read from the fixture (`observer: "fixture"`, such as a beacon or a served request) that the leg expects to be `true` is polled for up to 5000 ms (scaled) instead of read once. One that the leg expects to be `false` is still read once.
|
|
304
|
+
|
|
305
|
+
`refusalScale` (default 1) multiplies the bounds of waits for what the guarded leg expects to be absent, the beacon, request, title or navigation that the guard should block: settles that are not `required`, and `paused`, on the guarded leg. Such a wait runs to its full bound whenever the guard works, so `settleScale` leaves it alone. The trade-off: a larger `refusalScale` gives more sensitivity to a leak that arrives late on a slow host, because the observations after the wait see it, at the cost of that much more time per guarded row. Adopters should keep `refusalScale` at 1 or higher, never below 1; the runner refuses a `refusalScale` below 1 with `refusalScale must be a number of at least 1`.
|
|
304
306
|
|
|
305
307
|
`onStart(row)` receives each row's `id`, `target`, `variant`, `transport` and `via` as the row starts. `run()` returns a promise that also has `inFlight()`, and its result has `inFlight` too. `inFlight()` lists the rows that are running now, each as `{ id, target, variant, leg, phase, step, clients }`, where `step` is the index of the current step in its `phase` (`setup`, `trigger` or `child`), and `clients` lists each client's pending CDP calls as `{ method, session, ageMs }` (`client`, plus `unguarded` when it is a separate client). With `rowTimeoutMs`, a row still running after that many milliseconds fails on its own. Its `errors` hold `row timed out after <ms>ms: <the in-flight view>`, `inFlight` holds the view, and the clients that `clientFactory` made for it are closed. The run then goes on to the next row. It is off by default, and a client passed as `client` is never closed.
|
|
306
308
|
|
package/guard-runner.mjs
CHANGED
|
@@ -117,6 +117,7 @@ class Execution {
|
|
|
117
117
|
(step) => step.children && step.where
|
|
118
118
|
);
|
|
119
119
|
this.scale = options.settleScale ?? 1;
|
|
120
|
+
this.refusalScale = options.refusalScale ?? 1;
|
|
120
121
|
this.childRuns = new Map();
|
|
121
122
|
this.named = new Map();
|
|
122
123
|
this.processed = new Set();
|
|
@@ -169,12 +170,12 @@ class Execution {
|
|
|
169
170
|
return out;
|
|
170
171
|
}
|
|
171
172
|
|
|
172
|
-
bound(ms) {
|
|
173
|
-
return Math.round(ms * this.scale);
|
|
173
|
+
bound(ms, absent = false) {
|
|
174
|
+
return Math.round(ms * (absent ? this.refusalScale : this.scale));
|
|
174
175
|
}
|
|
175
176
|
|
|
176
|
-
async poll(predicate, ms = 1500) {
|
|
177
|
-
const deadline = Date.now() + this.bound(ms);
|
|
177
|
+
async poll(predicate, ms = 1500, absent = false) {
|
|
178
|
+
const deadline = Date.now() + this.bound(ms, absent);
|
|
178
179
|
do {
|
|
179
180
|
if (await predicate()) return true;
|
|
180
181
|
await sleep(25);
|
|
@@ -524,7 +525,7 @@ class Execution {
|
|
|
524
525
|
event.params?.request?.url === url &&
|
|
525
526
|
this.rule(event)?.url === url
|
|
526
527
|
);
|
|
527
|
-
if (!(await this.poll(seen, ms)) && this.side === 'control')
|
|
528
|
+
if (!(await this.poll(seen, ms, this.side === 'guarded')) && this.side === 'control')
|
|
528
529
|
this.errors.push(`paused: no pause for ${url} within ${this.bound(ms)}ms`);
|
|
529
530
|
}
|
|
530
531
|
|
|
@@ -660,13 +661,14 @@ class Execution {
|
|
|
660
661
|
);
|
|
661
662
|
};
|
|
662
663
|
const alt = settle.or;
|
|
664
|
+
const absent = this.side === 'guarded' && !settle.required;
|
|
663
665
|
if (refused() && !alt) return skipped();
|
|
664
666
|
if (settle.variant) await sleep(this.delay);
|
|
665
667
|
if (settle.title && !this.reader) {
|
|
666
|
-
await sleep(this.bound(settle.ms ?? 1500));
|
|
668
|
+
await sleep(this.bound(settle.ms ?? 1500, absent));
|
|
667
669
|
return;
|
|
668
670
|
}
|
|
669
|
-
const
|
|
671
|
+
const arrived = async () => {
|
|
670
672
|
if (refused()) return !alt || (await this.check(alt));
|
|
671
673
|
if (settle.served) return this.requestCheck({ served: settle.served, tagged: false });
|
|
672
674
|
if (settle.beacon)
|
|
@@ -685,7 +687,8 @@ class Execution {
|
|
|
685
687
|
.slice(index + 1)
|
|
686
688
|
.some((e) => e.method === 'Page.loadEventFired' && e.sessionId === event.sessionId)
|
|
687
689
|
);
|
|
688
|
-
}
|
|
690
|
+
};
|
|
691
|
+
const met = await this.poll(arrived, settle.ms ?? 1500, absent);
|
|
689
692
|
if (refused() && !alt) return skipped();
|
|
690
693
|
if (settle.required && !met)
|
|
691
694
|
this.errors.push(
|
|
@@ -1419,13 +1422,22 @@ function validateBlocked(vectors) {
|
|
|
1419
1422
|
throw new TypeError(`${vector.id}: blocked needs exactly one url or nonempty anyOf`);
|
|
1420
1423
|
}
|
|
1421
1424
|
|
|
1422
|
-
function
|
|
1423
|
-
if (!opts.fixture?.log) throw new TypeError('fixture.log is required');
|
|
1425
|
+
function validateScales(opts) {
|
|
1424
1426
|
if (
|
|
1425
1427
|
opts.settleScale !== undefined &&
|
|
1426
1428
|
!(Number.isFinite(opts.settleScale) && opts.settleScale > 0)
|
|
1427
1429
|
)
|
|
1428
1430
|
throw new TypeError('settleScale must be a positive number');
|
|
1431
|
+
if (
|
|
1432
|
+
opts.refusalScale !== undefined &&
|
|
1433
|
+
!(Number.isFinite(opts.refusalScale) && opts.refusalScale >= 1)
|
|
1434
|
+
)
|
|
1435
|
+
throw new TypeError('refusalScale must be a number of at least 1');
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
function validate(set, opts) {
|
|
1439
|
+
if (!opts.fixture?.log) throw new TypeError('fixture.log is required');
|
|
1440
|
+
validateScales(opts);
|
|
1429
1441
|
for (const vector of set.vectors)
|
|
1430
1442
|
for (const [name, observation] of Object.entries(vector.observe ?? {}))
|
|
1431
1443
|
if (!observation.check) throw new TypeError(`${vector.id}: ${name} lacks a check`);
|
package/guard-vectors.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-ecoverse/slicc-browsers",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.1",
|
|
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,7 +65,7 @@
|
|
|
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 test/integration/guard-legs.test.mjs test/integration/guard-diagnostics.test.mjs test/integration/guard-chrome.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 test/integration/guard-diagnostics.test.mjs test/integration/guard-chrome.test.mjs test/integration/guard-scales.test.mjs",
|
|
69
69
|
"test:guard-sweep": "node scripts/test-guards.mjs",
|
|
70
70
|
"test:gates": "node --test test/integration/gates.test.mjs",
|
|
71
71
|
"test:guard-target": "node --test test/integration/guard-runner.test.mjs",
|