@ai-ecoverse/slicc-browsers 1.14.6 → 1.14.8

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
@@ -309,9 +309,10 @@ It exports:
309
309
  - `waitFor(check, ms, step?)`: polls `check` every `step` ms (default 25) until it holds, checking once more after the bound. A timer heartbeat every `step` runs for the whole wait, including while `check` is awaiting a reply, and any gap between beats longer than a second (or four steps, if longer) extends the bound by that gap, so a synchronous event-loop stall elsewhere in the process does not consume it. The extensions add up to at most `max(4 × ms, 30000)` ms, so a throttled page cannot stretch a wait without limit. `load`, the runner's waits and the error-page and redirect-body probes use it;
310
310
  - `judge(vector, side, observed)`: `{ pass, refused, skipped, mismatches }`, where `refused` lists the leg's refusals and `skipped` the observations that depend on them or explicitly record unavailable observation capabilities;
311
311
  - `result(vector, run)`: a report row from `{ target, variant, transport, via?, capabilities?, control, guarded, live }`. `capabilities` is the transport's; without it, the transport has them all;
312
+ - `knownOrigins` and `originDefaults`: the origin names a runner can miss, and the origins `plain`, `cross`, `log` and `own` fall back to when unset;
312
313
  - `parseURL(value, base?)`: a `URL`, or `null` for anything `new URL` rejects or a non-string; every parse of a fixture log entry or a Chrome-reported URL in the runner and the fixture goes through it (`protected.mjs` keeps its own copy, since it imports nothing), so an unparseable entry never throws;
313
314
  - `owned(entries, tag)`: the fixture log entries whose raw URL contains `tag`, the row's attribution through its `n=`, `tag=` or `to=` query;
314
- - `summary({ guard, sha, set, results })`: `set` is the parsed `guard-vectors.json`. It returns `{ set, schemaVersion, guard, sha, variants, targets, transports, results, skipped, notRun, missing, forged, pass, line }`. `missing` lists each `{ id, target, variant, transport }` without a row, for every vector whose `scope` is `any` or the guard, every target it lists, every variant in `variants` or `results`, and every transport in `results`; `pass` needs it empty, where `line` is `GUARD-PASS <guard>@<sha> (vectors @ai-ecoverse/slicc-browsers@X.Y.Z)` or `GUARD-FAIL …`. `forged` lists each `{ id, target, variant, transport, why }` that is `not-run` on a vector that isn't `kind: "text"`, or `skip` without a reason the runner gives (`not-applicable`, `not-applicable:no-<origin>-origin`, the vector's own `skip.reason` on its target, or `not-supported:<capability>` for a capability the set requires, `browserSession` or `unguarded-observer`); `pass` needs it empty too, and `line` then ends in `forged <n>`.
315
+ - `summary({ guard, sha, set, results })`: `set` is the parsed `guard-vectors.json`. It returns `{ set, schemaVersion, guard, sha, variants, targets, transports, results, skipped, notRun, missing, forged, pass, line }`. `missing` lists each `{ id, target, variant, transport }` without a row, for every vector whose `scope` is `any` or the guard, every target it lists, every variant in `variants` or `results`, and every transport in `results`; `pass` needs it empty, where `line` is `GUARD-PASS <guard>@<sha> (vectors @ai-ecoverse/slicc-browsers@X.Y.Z)` or `GUARD-FAIL …`. `forged` lists each `{ id, target, variant, transport, why }` that is `not-run` on a vector that isn't `kind: "text"`, or `skip` without a reason the runner gives (`not-applicable`, `not-applicable:no-<origin>-origin` for an origin in `knownOrigins` that the row's target, `plain`, the control's origin, the steps or a check names, directly or through `originDefaults`, where only the first branch of an `any` counts, the vector's own `skip.reason` on its target, or `not-supported:<capability>` for a capability the set requires, `browserSession` or `unguarded-observer`); `pass` needs it empty too, and `line` then ends in `forged <n>`.
315
316
 
316
317
  **`npm run validate`** checks that:
317
318
  - the file matches the schema, and ids are unique;
package/guard-driver.mjs CHANGED
@@ -1,5 +1,25 @@
1
1
  const q = JSON.stringify;
2
2
 
3
+ export const originDefaults = {
4
+ plain: 'ordinary',
5
+ cross: 'client',
6
+ log: 'ordinary',
7
+ own: 'client',
8
+ };
9
+ export const knownOrigins = [
10
+ 'ordinary',
11
+ 'ordinary-same-site',
12
+ 'seven',
13
+ 'vault',
14
+ 'vault-x',
15
+ 'apex',
16
+ 'own',
17
+ 'client',
18
+ 'plain',
19
+ 'cross',
20
+ 'log',
21
+ ];
22
+
3
23
  export const beacon = (log, tag) =>
4
24
  `try { const u = ${q(log)} + '/beacon?tag=' + ${q(tag)} + '&origin=' + encodeURIComponent(location.origin); if (!navigator.sendBeacon(u)) fetch(u, { keepalive: true, mode: 'no-cors' }).catch(() => {}); } catch {}`;
5
25
 
@@ -285,11 +305,41 @@ const capabilities = (value) =>
285
305
  )
286
306
  : [];
287
307
 
308
+ const originNames = (value) =>
309
+ [...q(value ?? null).matchAll(/\{([A-Za-z][A-Za-z-]*)\}/g)]
310
+ .map((match) => (match[1] === 'beacon' || match[1] === 'probe' ? 'log' : match[1]))
311
+ .flatMap((name) => [name, originDefaults[name]]);
312
+
313
+ const checkOrigins = (check) => {
314
+ if (!check || typeof check !== 'object') return [];
315
+ if (check.any) return checkOrigins(check.any[0]);
316
+ if (check.all || check.not) return (check.all ?? [check.not]).flatMap(checkOrigins);
317
+ return originNames(check);
318
+ };
319
+
320
+ function originReason(vector, row, reason) {
321
+ const origin = /^not-applicable:no-(.+)-origin$/.exec(reason)?.[1];
322
+ if (!knownOrigins.includes(origin)) return false;
323
+ const fixed = [
324
+ row.target,
325
+ '{plain}',
326
+ vector?.control?.origin,
327
+ vector?.setup,
328
+ vector?.trigger,
329
+ ].map((value) => (typeof value === 'string' && !value.startsWith('{') ? `{${value}}` : value));
330
+ return [
331
+ ...originNames(fixed),
332
+ ...checkOrigins(vector?.liveCheck),
333
+ ...Object.values(vector?.observe ?? {}).flatMap((observation) =>
334
+ checkOrigins(observation?.check)
335
+ ),
336
+ ].includes(origin);
337
+ }
338
+
288
339
  function skipWhy(vector, row, known) {
289
340
  const { reason } = row;
290
341
  if (typeof reason !== 'string' || reason === '') return 'skip without a reason';
291
- if (reason === 'not-applicable' || /^not-applicable:no-[A-Za-z][A-Za-z-]*-origin$/.test(reason))
292
- return null;
342
+ if (reason === 'not-applicable' || originReason(vector, row, reason)) return null;
293
343
  if (vector?.skip?.target === row.target && vector.skip.reason === reason) return null;
294
344
  if (reason.startsWith('not-supported:') && known.has(reason.slice('not-supported:'.length)))
295
345
  return null;
package/guard-runner.mjs CHANGED
@@ -9,7 +9,9 @@ import {
9
9
  expand,
10
10
  hop,
11
11
  isRefusal,
12
+ knownOrigins,
12
13
  loadedAfter,
14
+ originDefaults,
13
15
  outcome,
14
16
  owned,
15
17
  parseURL,
@@ -1302,21 +1304,6 @@ const browserContextStep = (vector, capabilities) =>
1302
1304
  supported(['browserContexts'], capabilities))
1303
1305
  );
1304
1306
 
1305
- const originDefaults = { plain: 'ordinary', cross: 'client', log: 'ordinary', own: 'client' };
1306
- const knownOrigins = [
1307
- 'ordinary',
1308
- 'ordinary-same-site',
1309
- 'seven',
1310
- 'vault',
1311
- 'vault-x',
1312
- 'apex',
1313
- 'own',
1314
- 'client',
1315
- 'plain',
1316
- 'cross',
1317
- 'log',
1318
- ];
1319
-
1320
1307
  function nullOrigins(value, targets) {
1321
1308
  return [...q(value).matchAll(/\{([A-Za-z][A-Za-z-]*)\}/g)]
1322
1309
  .map((match) => (match[1] === 'beacon' || match[1] === 'probe' ? 'log' : match[1]))
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "setVersion": "1.14.6",
3
+ "setVersion": "1.14.8",
4
4
  "sources": [
5
5
  {
6
6
  "repo": "ai-ecoverse/slicc-cli",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-ecoverse/slicc-browsers",
3
- "version": "1.14.6",
3
+ "version": "1.14.8",
4
4
  "description": "SLICC's shared browser catalog, JSON schemas and discovery conformance corpus",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -81,7 +81,7 @@
81
81
  "test:unit:guard": "mkdir -p coverage/unit && node --experimental-vm-modules --no-warnings --experimental-test-coverage --test --test-coverage-include=guard-runner.mjs --test-coverage-include=fixture-server.mjs --test-coverage-include=fixture-client.mjs --test-coverage-include=observer-target.mjs --test-coverage-include=protected-error-page.mjs --test-coverage-include=unparseable-url.mjs --test-coverage-include=scripts/browser-runner.mjs --test-coverage-include=scripts/validate.mjs --test-reporter=lcov --test-reporter-destination=coverage/unit/guard.lcov test/unit/guard-*.test.mjs && slicc-diff-cover coverage/unit/guard.lcov"
82
82
  },
83
83
  "devDependencies": {
84
- "@ai-ecoverse/slicc-shared-web": "2.3.6",
84
+ "@ai-ecoverse/slicc-shared-web": "2.4.0",
85
85
  "@biomejs/biome": "2.5.15",
86
86
  "ajv": "8.20.0",
87
87
  "ajv-formats": "3.0.1",