@ai-ecoverse/slicc-browsers 1.21.1 → 1.22.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
@@ -223,7 +223,7 @@ await fixture.close();
223
223
  You supply:
224
224
  - **a guarded client** per row: your guard's CDP connection, with `send(method, params, sessionId)`, `waitEvent(predicate, ms)` and an `events` array;
225
225
  - **an unguarded observer**: a direct CDP connection to the same browser, which runs the controls and checks liveness;
226
- - **origins**: the fixture server reachable as seven, the vault and an ordinary site. Point the protected hostnames at it with Chrome's `--host-resolver-rules`, so no test ever touches the real seven;
226
+ - **origins**: the fixture server reachable as seven, the vault and an ordinary site. Point the protected hostnames at it with Chrome's `--host-resolver-rules`, so tests never touch the real seven;
227
227
  - **capabilities**: what your transport can do. A row that needs something you don't declare is skipped as `not-supported:<capability>`.
228
228
 
229
229
  | capability | means the transport can |
@@ -239,6 +239,7 @@ You supply:
239
239
  | `siteIsolationOff`, `inboundDelay`, `forwardAudit` | no site isolation; hold inbound; audit forwarded commands |
240
240
  | `secureOrigin` | HTTPS origins (service workers) |
241
241
  | `stockSetup`, `syntheticAutoAttach` | admit stock clients' setup calls; synthesize auto-attach |
242
+ | `closeProtected` | close a protected tab |
242
243
 
243
244
  ### Reading the result
244
245
 
@@ -248,13 +249,13 @@ Each row reports `control`, `guarded` and `live` as `pass`, `fail`, `skip`, `inc
248
249
  GUARD-PASS my-guard@1a2b3c4 (vectors @ai-ecoverse/slicc-browsers@1.18.5)
249
250
  ```
250
251
 
251
- The run passes only when every row passes and none is missing. A skip with a reason the runner would never give, such as a made-up capability, is reported as forged, and skipped and incomplete rows are counted on the line, so the line can't overstate what was tested.
252
+ The run passes only when every row passes and none is missing. A skip with a reason the runner would never give is forged; skipped and incomplete rows are counted on the line, so it can't overstate what was tested.
252
253
 
253
254
  To find out what a vector checks, read it in `guard-vectors.json`: each one lists its steps, each observation's `means`, its `live` condition and its expected outcome. [`schema/guard-vectors.schema.json`](schema/guard-vectors.schema.json) defines the format.
254
255
 
255
256
  ## Development
256
257
 
257
- You need Go 1.26, Rust stable with the `wasm32-wasip1` target, Node 24 or later, and Chrome or Chromium for the browser tests: they take `CHROME_PATH`, or the Chrome for Testing that `npx slicc-cdp install-browser` installs, and run it headless unless `CI` is set (`SLICC_HEADLESS=1` or `0` overrides).
258
+ You need Go 1.26, Rust stable with `wasm32-wasip1`, Node 24+, and Chrome or Chromium for the browser tests: they take `CHROME_PATH`, or Chrome for Testing from `npx slicc-cdp install-browser`, and run headless unless `CI` is set (`SLICC_HEADLESS=1` or `0` overrides).
258
259
 
259
260
  | command | what it does |
260
261
  | --- | --- |
package/guard-driver.mjs CHANGED
@@ -197,7 +197,9 @@ export function judge(vector, side, observed) {
197
197
  }
198
198
  const skipped = Object.keys(want).filter(
199
199
  (k) =>
200
- (observed?.[k] === 'not-supported' && vector.observe?.[k]?.requires?.length > 0) ||
200
+ (observed?.[k] === 'not-supported' &&
201
+ ((vector.observe?.[k]?.requires?.length ?? 0) > 0 ||
202
+ (vector.observe?.[k]?.unless?.length ?? 0) > 0)) ||
201
203
  (vector.observe?.[k]?.dependsOn ?? []).some((d) => refused.includes(d))
202
204
  );
203
205
  const incomplete = Object.keys(want).filter(
@@ -310,7 +312,9 @@ const capabilities = (value) =>
310
312
  ? value.flatMap(capabilities)
311
313
  : value && typeof value === 'object'
312
314
  ? Object.entries(value).flatMap(([key, item]) =>
313
- key === 'requires' && Array.isArray(item) ? item : capabilities(item)
315
+ (key === 'requires' || key === 'unless') && Array.isArray(item)
316
+ ? item
317
+ : capabilities(item)
314
318
  )
315
319
  : [];
316
320
 
@@ -389,6 +393,17 @@ function skipWhy(vector, row, known, guard, declared) {
389
393
  return `skip with a reason the runner never gives: ${reason}`;
390
394
  }
391
395
 
396
+ function observationWhy(vector, row, declared) {
397
+ return (row.notSupported ?? []).flatMap((name) => {
398
+ const observation = vector?.observe?.[name];
399
+ if (!observation) return [`not-supported observation ${name} the vector lacks`];
400
+ const missing = (observation.requires ?? []).some((cap) => !declared.includes(cap));
401
+ const unless = (observation.unless ?? []).some((cap) => declared.includes(cap));
402
+ if (missing || unless) return [];
403
+ return [`not-supported:${name} while the observation should run`];
404
+ });
405
+ }
406
+
392
407
  function forgeries(set, results, guard, capabilitiesList) {
393
408
  const vectors = new Map(set.vectors.map((v) => [v.id, v]));
394
409
  const declared = declaredCapabilities(capabilitiesList);
@@ -405,6 +420,7 @@ function forgeries(set, results, guard, capabilitiesList) {
405
420
  if (legs.includes('not-run') && vector?.kind !== 'text')
406
421
  why.push('not-run on a vector that is not kind text');
407
422
  if (legs.includes('skip')) why.push(skipWhy(vector, row, known, guard, declared));
423
+ why.push(...observationWhy(vector, row, declared));
408
424
  const found = why.filter(Boolean);
409
425
  if (!found.length) return [];
410
426
  const { id, target, variant, transport } = row;
package/guard-runner.mjs CHANGED
@@ -50,6 +50,13 @@ const supported = (requires, capabilities) =>
50
50
  (requires ?? []).every((c) =>
51
51
  Array.isArray(capabilities) ? capabilities.includes(c) : capabilities[c] === true
52
52
  );
53
+ const declared = (capability, capabilities) =>
54
+ Array.isArray(capabilities)
55
+ ? capabilities.includes(capability)
56
+ : capabilities[capability] === true;
57
+ const gatedOut = (observation, capabilities) =>
58
+ !supported(observation?.requires, capabilities) ||
59
+ (observation?.unless ?? []).some((capability) => declared(capability, capabilities));
53
60
  const matches = (value, predicate) => {
54
61
  if (predicate === null || typeof predicate !== 'object') return value === predicate;
55
62
  return Object.entries(predicate).every(([key, want]) => {
@@ -1616,7 +1623,7 @@ class Execution {
1616
1623
  const observed = { ...this.refused };
1617
1624
  const incomplete = [];
1618
1625
  for (const [name, observation] of Object.entries(this.vector.observe ?? {})) {
1619
- if (!supported(observation.requires, this.options.capabilities)) {
1626
+ if (gatedOut(observation, this.options.capabilities)) {
1620
1627
  observed[name] = 'not-supported';
1621
1628
  continue;
1622
1629
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "setVersion": "1.21.1",
3
+ "setVersion": "1.22.0",
4
4
  "sources": [
5
5
  {
6
6
  "repo": "ai-ecoverse/slicc-cli",
@@ -105,7 +105,8 @@
105
105
  "forwardAudit": "a complete browser-side forwarding ledger for history-restore probes and for checks of a command's forwarded params (client.forwarded)",
106
106
  "stockSetup": "the guard admits stock clients' setup calls: init scripts with no code (the no-code regex is matched on the whole string without the multiline flag), the four display emulation calls, bindings scoped to an isolated world, Page.createIsolatedWorld on an ordinary frame with grantUniveralAccess and contentSecurityPolicy stripped and no executionContextId for any other frame, and Target.setAutoAttach narrowed to tab targets (pages under the tab held like pages, with the same URL redaction in targetInfo)",
107
107
  "syntheticAutoAttach": "the transport has no native Target.setAutoAttach, so the guard attaches new tabs, pages and their children itself and synthesizes Target.attachedToTarget, with the same placement and holds",
108
- "secureOrigin": "the fixture and page origins are secure contexts (HTTPS), so service worker registration is available"
108
+ "secureOrigin": "the fixture and page origins are secure contexts (HTTPS), so service worker registration is available",
109
+ "closeProtected": "the guard lets Target.closeTarget close a protected tab"
109
110
  },
110
111
  "placeholders": {
111
112
  "target": "the target's origin; {plain} on the control",
@@ -1706,6 +1707,7 @@
1706
1707
  },
1707
1708
  "closeRefused": {
1708
1709
  "means": "protected error-page restore: closeRefused",
1710
+ "unless": ["closeProtected"],
1709
1711
  "check": {
1710
1712
  "result": "errorPage",
1711
1713
  "path": "result.closeRefused",
@@ -1713,6 +1715,16 @@
1713
1715
  "observer": "client"
1714
1716
  }
1715
1717
  },
1718
+ "closed": {
1719
+ "means": "protected error-page restore: Target.closeTarget on the locked tab is answered with success and the tab is gone",
1720
+ "check": {
1721
+ "result": "errorPage",
1722
+ "path": "result.closed",
1723
+ "equals": true,
1724
+ "observer": "client"
1725
+ },
1726
+ "requires": ["closeProtected"]
1727
+ },
1716
1728
  "attachRedacted": {
1717
1729
  "means": "protected error-page restore: attachRedacted",
1718
1730
  "check": {
@@ -1754,6 +1766,7 @@
1754
1766
  "evaluateRefused": false,
1755
1767
  "attachRefused": false,
1756
1768
  "closeRefused": false,
1769
+ "closed": true,
1757
1770
  "attachRedacted": false,
1758
1771
  "fullURL": true
1759
1772
  }
@@ -1770,6 +1783,7 @@
1770
1783
  "evaluateRefused": true,
1771
1784
  "attachRefused": true,
1772
1785
  "closeRefused": true,
1786
+ "closed": true,
1773
1787
  "attachRedacted": true,
1774
1788
  "fullURL": false
1775
1789
  }
@@ -1887,6 +1901,7 @@
1887
1901
  },
1888
1902
  "closeRefused": {
1889
1903
  "means": "protected error-page sticky: closeRefused",
1904
+ "unless": ["closeProtected"],
1890
1905
  "check": {
1891
1906
  "result": "errorPage",
1892
1907
  "path": "result.closeRefused",
@@ -1894,6 +1909,16 @@
1894
1909
  "observer": "client"
1895
1910
  }
1896
1911
  },
1912
+ "closed": {
1913
+ "means": "protected error-page sticky: Target.closeTarget on the locked tab is answered with success and the tab is gone",
1914
+ "check": {
1915
+ "result": "errorPage",
1916
+ "path": "result.closed",
1917
+ "equals": true,
1918
+ "observer": "client"
1919
+ },
1920
+ "requires": ["closeProtected"]
1921
+ },
1897
1922
  "stickyError": {
1898
1923
  "means": "protected error-page sticky: stickyError",
1899
1924
  "check": {
@@ -1944,6 +1969,7 @@
1944
1969
  "evaluateRefused": false,
1945
1970
  "attachRefused": false,
1946
1971
  "closeRefused": false,
1972
+ "closed": true,
1947
1973
  "stickyError": true,
1948
1974
  "attachRedacted": false,
1949
1975
  "fullURL": true
@@ -1961,6 +1987,7 @@
1961
1987
  "evaluateRefused": true,
1962
1988
  "attachRefused": true,
1963
1989
  "closeRefused": true,
1990
+ "closed": true,
1964
1991
  "stickyError": true,
1965
1992
  "attachRedacted": true,
1966
1993
  "fullURL": false
@@ -2080,6 +2107,7 @@
2080
2107
  },
2081
2108
  "closeRefused": {
2082
2109
  "means": "protected error-page sticky (query): closeRefused",
2110
+ "unless": ["closeProtected"],
2083
2111
  "check": {
2084
2112
  "result": "errorPage",
2085
2113
  "path": "result.closeRefused",
@@ -2087,6 +2115,16 @@
2087
2115
  "observer": "client"
2088
2116
  }
2089
2117
  },
2118
+ "closed": {
2119
+ "means": "protected error-page sticky (query): Target.closeTarget on the locked tab is answered with success and the tab is gone",
2120
+ "check": {
2121
+ "result": "errorPage",
2122
+ "path": "result.closed",
2123
+ "equals": true,
2124
+ "observer": "client"
2125
+ },
2126
+ "requires": ["closeProtected"]
2127
+ },
2090
2128
  "stickyError": {
2091
2129
  "means": "protected error-page sticky (query): stickyError",
2092
2130
  "check": {
@@ -2137,6 +2175,7 @@
2137
2175
  "evaluateRefused": false,
2138
2176
  "attachRefused": false,
2139
2177
  "closeRefused": false,
2178
+ "closed": true,
2140
2179
  "stickyError": true,
2141
2180
  "attachRedacted": false,
2142
2181
  "fullURL": true
@@ -2154,6 +2193,7 @@
2154
2193
  "evaluateRefused": true,
2155
2194
  "attachRefused": true,
2156
2195
  "closeRefused": true,
2196
+ "closed": true,
2157
2197
  "stickyError": true,
2158
2198
  "attachRedacted": true,
2159
2199
  "fullURL": false
@@ -2273,6 +2313,7 @@
2273
2313
  },
2274
2314
  "closeRefused": {
2275
2315
  "means": "protected error-page sticky (fragment): closeRefused",
2316
+ "unless": ["closeProtected"],
2276
2317
  "check": {
2277
2318
  "result": "errorPage",
2278
2319
  "path": "result.closeRefused",
@@ -2280,6 +2321,16 @@
2280
2321
  "observer": "client"
2281
2322
  }
2282
2323
  },
2324
+ "closed": {
2325
+ "means": "protected error-page sticky (fragment): Target.closeTarget on the locked tab is answered with success and the tab is gone",
2326
+ "check": {
2327
+ "result": "errorPage",
2328
+ "path": "result.closed",
2329
+ "equals": true,
2330
+ "observer": "client"
2331
+ },
2332
+ "requires": ["closeProtected"]
2333
+ },
2283
2334
  "stickyError": {
2284
2335
  "means": "protected error-page sticky (fragment): stickyError",
2285
2336
  "check": {
@@ -2330,6 +2381,7 @@
2330
2381
  "evaluateRefused": false,
2331
2382
  "attachRefused": false,
2332
2383
  "closeRefused": false,
2384
+ "closed": true,
2333
2385
  "stickyError": true,
2334
2386
  "attachRedacted": false,
2335
2387
  "fullURL": true
@@ -2347,6 +2399,7 @@
2347
2399
  "evaluateRefused": true,
2348
2400
  "attachRefused": true,
2349
2401
  "closeRefused": true,
2402
+ "closed": true,
2350
2403
  "stickyError": true,
2351
2404
  "attachRedacted": true,
2352
2405
  "fullURL": false
@@ -26267,7 +26320,7 @@
26267
26320
  {
26268
26321
  "cdp": "Page.addScriptToEvaluateOnNewDocument",
26269
26322
  "params": {
26270
- "source": "// slicc\u2028fetch
fetch(new URL('/doc?n=bound-ls-{tag}', location.href)).catch(()=>{})\n//# sourceURL=slicc-ls-{tag}\n",
26323
+ "source": "// slicc
fetch(new URL('/doc?n=bound-ls-{tag}', location.href)).catch(()=>{})\n//# sourceURL=slicc-ls-{tag}\n",
26271
26324
  "worldName": "slicc-bound-ls-{tag}"
26272
26325
  },
26273
26326
  "as": "boundLs",
@@ -26279,7 +26332,7 @@
26279
26332
  {
26280
26333
  "cdp": "Page.addScriptToEvaluateOnNewDocument",
26281
26334
  "params": {
26282
- "source": "// slicc\u2029fetch
fetch(new URL('/doc?n=bound-ps-{tag}', location.href)).catch(()=>{})\n//# sourceURL=slicc-ps-{tag}\n",
26335
+ "source": "// slicc
fetch(new URL('/doc?n=bound-ps-{tag}', location.href)).catch(()=>{})\n//# sourceURL=slicc-ps-{tag}\n",
26283
26336
  "worldName": "slicc-bound-ps-{tag}"
26284
26337
  },
26285
26338
  "as": "boundPs",
@@ -26315,7 +26368,7 @@
26315
26368
  {
26316
26369
  "cdp": "Page.addScriptToEvaluateOnNewDocument",
26317
26370
  "params": {
26318
- "source": "\ufefffetch(new URL('/doc?n=bound-bom-{tag}', location.href)).catch(()=>{})\n//# sourceURL=slicc-bom-{tag}\n",
26371
+ "source": "fetch(new URL('/doc?n=bound-bom-{tag}', location.href)).catch(()=>{})\n//# sourceURL=slicc-bom-{tag}\n",
26319
26372
  "worldName": "slicc-bound-bom-{tag}"
26320
26373
  },
26321
26374
  "as": "boundBom",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-ecoverse/slicc-browsers",
3
- "version": "1.21.1",
3
+ "version": "1.22.0",
4
4
  "description": "SLICC's shared browser catalog, JSON schemas and discovery conformance corpus",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -73,7 +73,7 @@
73
73
  "validate": "node scripts/validate.mjs",
74
74
  "lint": "slicc-lint && test -z \"$(gofmt -l .)\" && go mod tidy -diff && go vet ./... && GOOS=darwin go vet ./... && GOOS=windows go vet ./... && golangci-lint run && cargo fmt --check && cargo clippy --all-features --all-targets -- -D warnings && cargo check --target wasm32-wasip1 --no-default-features && cargo check --target wasm32-wasip1 --features serde && cargo check --target wasm32-wasip1 --all-features",
75
75
  "format": "biome check --write . && gofmt -w . && cargo fmt",
76
- "test": "export PATH=\"$HOME/.cargo/bin:$PATH\" && node --test --test-concurrency=1 test/integration/debugger-stage1-packaging.test.mjs test/integration/debugger-raw-control.test.mjs test/integration/debugger-cleanup.test.mjs test/integration/debugger-stage1-install-vectors.test.mjs test/integration/protected-premise.test.mjs test/integration/debugger-stage1-vectors.test.mjs test/integration/self-contained.test.mjs test/integration/seven-corpus.test.mjs test/integration/browser-runner.test.mjs test/integration/fixture-client.test.mjs test/integration/guard-redirect-carrier.test.mjs test/integration/guard-redirect-session-end.test.mjs test/integration/guard-redirect-history.test.mjs test/integration/guard-redirect-iframe.test.mjs test/integration/guard-incomplete.test.mjs test/integration/guard-unparseable-wipe.test.mjs test/integration/guard-browser-ui.test.mjs test/integration/guard-snapshot-settle.test.mjs test/integration/guard-stall.test.mjs test/integration/guard-forged.test.mjs test/integration/guard-trusted-emulation.test.mjs test/integration/guard-exit-steps.test.mjs test/integration/guard-exit-refusal.test.mjs test/integration/guard-frame-read.test.mjs test/integration/guard-bfcache.test.mjs test/integration/guard-bfcache-marker.test.mjs test/integration/guard-bfcache-history.test.mjs test/integration/guard-bfcache-review.test.mjs test/integration/guard-bfcache-quarantine.test.mjs test/integration/guard-error-page-navigation.test.mjs test/integration/guard-error-page-events.test.mjs test/integration/guard-error-page-reload.test.mjs test/integration/guard-world-paint.test.mjs test/integration/gates.test.mjs test/integration/validate.test.mjs test/integration/validate-guard-stock-setup.test.mjs test/integration/go.test.mjs test/integration/cargo.test.mjs test/integration/workflows.test.mjs test/integration/guard-refusable-nav.test.mjs test/integration/guard-sweep-union.test.mjs && node --test --test-concurrency=2 test/integration/guard-lifecycle.test.mjs test/integration/chromium-launch.test.mjs 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-fetch-enable.test.mjs test/integration/guard-diagnostics.test.mjs test/integration/guard-chrome.test.mjs test/integration/guard-worlds.test.mjs test/integration/guard-world-discovery.test.mjs test/integration/guard-world-guesses.test.mjs test/integration/guard-unparseable.test.mjs test/integration/guard-redirect-secret.test.mjs test/integration/guard-error-page.test.mjs test/integration/guard-discovery.test.mjs test/integration/guard-bfcache-chrome.test.mjs test/integration/guard-browser-page.test.mjs test/integration/guard-scales.test.mjs test/integration/guard-shared-log.test.mjs test/integration/guard-pre-beacon.test.mjs test/integration/guard-world-pause.test.mjs test/integration/guard-reveal-overlap.test.mjs test/integration/guard-extension-frame-mouse.test.mjs test/integration/guard-extension-frames.test.mjs test/integration/guard-stock-setup.test.mjs test/integration/guard-stock-attack.test.mjs test/integration/guard-parallel.test.mjs test/integration/guard-client-exit.test.mjs test/integration/guard-runner.test.mjs test/integration/guard-sweep-ordinary.test.mjs test/integration/guard-sweep-seven.test.mjs test/integration/guard-sweep-vault.test.mjs test/integration/guard-sweep-vault-x.test.mjs test/integration/guard-sweep-apex.test.mjs test/integration/guard-sweep-others.test.mjs",
76
+ "test": "export PATH=\"$HOME/.cargo/bin:$PATH\" && node --test --test-concurrency=1 test/integration/debugger-stage1-packaging.test.mjs test/integration/debugger-raw-control.test.mjs test/integration/debugger-cleanup.test.mjs test/integration/debugger-stage1-install-vectors.test.mjs test/integration/protected-premise.test.mjs test/integration/debugger-stage1-vectors.test.mjs test/integration/self-contained.test.mjs test/integration/seven-corpus.test.mjs test/integration/browser-runner.test.mjs test/integration/fixture-client.test.mjs test/integration/guard-redirect-carrier.test.mjs test/integration/guard-redirect-session-end.test.mjs test/integration/guard-redirect-history.test.mjs test/integration/guard-redirect-iframe.test.mjs test/integration/guard-incomplete.test.mjs test/integration/guard-unparseable-wipe.test.mjs test/integration/guard-browser-ui.test.mjs test/integration/guard-snapshot-settle.test.mjs test/integration/guard-stall.test.mjs test/integration/guard-forged.test.mjs test/integration/guard-trusted-emulation.test.mjs test/integration/guard-exit-steps.test.mjs test/integration/guard-exit-refusal.test.mjs test/integration/guard-frame-read.test.mjs test/integration/guard-bfcache.test.mjs test/integration/guard-bfcache-marker.test.mjs test/integration/guard-bfcache-history.test.mjs test/integration/guard-bfcache-review.test.mjs test/integration/guard-bfcache-quarantine.test.mjs test/integration/guard-error-page-navigation.test.mjs test/integration/guard-error-page-events.test.mjs test/integration/guard-error-page-reload.test.mjs test/integration/guard-close-protected.test.mjs test/integration/guard-world-paint.test.mjs test/integration/gates.test.mjs test/integration/validate.test.mjs test/integration/validate-guard-stock-setup.test.mjs test/integration/go.test.mjs test/integration/cargo.test.mjs test/integration/workflows.test.mjs test/integration/guard-refusable-nav.test.mjs test/integration/guard-sweep-union.test.mjs && node --test --test-concurrency=2 test/integration/guard-lifecycle.test.mjs test/integration/chromium-launch.test.mjs 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-fetch-enable.test.mjs test/integration/guard-diagnostics.test.mjs test/integration/guard-chrome.test.mjs test/integration/guard-worlds.test.mjs test/integration/guard-world-discovery.test.mjs test/integration/guard-world-guesses.test.mjs test/integration/guard-unparseable.test.mjs test/integration/guard-redirect-secret.test.mjs test/integration/guard-error-page.test.mjs test/integration/guard-discovery.test.mjs test/integration/guard-bfcache-chrome.test.mjs test/integration/guard-browser-page.test.mjs test/integration/guard-scales.test.mjs test/integration/guard-shared-log.test.mjs test/integration/guard-pre-beacon.test.mjs test/integration/guard-world-pause.test.mjs test/integration/guard-reveal-overlap.test.mjs test/integration/guard-extension-frame-mouse.test.mjs test/integration/guard-extension-frames.test.mjs test/integration/guard-stock-setup.test.mjs test/integration/guard-stock-attack.test.mjs test/integration/guard-parallel.test.mjs test/integration/guard-client-exit.test.mjs test/integration/guard-runner.test.mjs test/integration/guard-sweep-ordinary.test.mjs test/integration/guard-sweep-seven.test.mjs test/integration/guard-sweep-vault.test.mjs test/integration/guard-sweep-vault-x.test.mjs test/integration/guard-sweep-apex.test.mjs test/integration/guard-sweep-others.test.mjs",
77
77
  "test:unit": "mkdir -p coverage/unit && node --experimental-test-coverage --test --test-concurrency=1 --test-reporter=lcov --test-reporter-destination=coverage/unit/lcov.info test/unit/*.test.mjs && go test -race -cover ./...",
78
78
  "build": "go build ./... && cargo build --all-features && cargo build --target wasm32-wasip1 --all-features && npm pack --dry-run",
79
79
  "prepublishOnly": "node scripts/set-crate-version.mjs \"$npm_package_version\"",
@@ -84,8 +84,8 @@
84
84
  "test:unit:guard": "mkdir -p coverage/unit && node --experimental-vm-modules --no-warnings --experimental-test-coverage --test --test-concurrency=1 --test-coverage-include=guard-runner.mjs --test-coverage-include=guard-driver.mjs --test-coverage-include=extension-frame.mjs --test-coverage-include=fixture-server.mjs --test-coverage-include=fixture-client.mjs --test-coverage-include=observer-target.mjs --test-coverage-include=frame-read.mjs --test-coverage-include=bfcache-restore.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"
85
85
  },
86
86
  "devDependencies": {
87
- "@ai-ecoverse/slicc-cdp": "1.47.0",
88
- "@ai-ecoverse/slicc-shared-web": "2.13.10",
87
+ "@ai-ecoverse/slicc-cdp": "1.47.5",
88
+ "@ai-ecoverse/slicc-shared-web": "2.14.2",
89
89
  "@biomejs/biome": "2.6.4",
90
90
  "ajv": "8.20.0",
91
91
  "ajv-formats": "3.0.1",
@@ -35,7 +35,7 @@ async function loadedDocument(reader, observer, url, until) {
35
35
  });
36
36
  }
37
37
 
38
- async function lockChecks({ client, session, targetId, result }) {
38
+ async function lockChecks({ client, reader, session, targetId, result, until }) {
39
39
  result.evaluateRefused = refused(
40
40
  await outcome(
41
41
  client.send(
@@ -48,7 +48,14 @@ async function lockChecks({ client, session, targetId, result }) {
48
48
  result.attachRefused = refused(
49
49
  await outcome(client.send('Target.attachToTarget', { targetId, flatten: true }))
50
50
  );
51
- result.closeRefused = refused(await outcome(client.send('Target.closeTarget', { targetId })));
51
+ const close = await outcome(client.send('Target.closeTarget', { targetId }));
52
+ result.closeRefused = refused(close);
53
+ result.closed =
54
+ close.result?.success === true &&
55
+ (await until(
56
+ async () =>
57
+ !(await reader.send('Target.getTargets')).targetInfos.some((i) => i.targetId === targetId)
58
+ ));
52
59
  }
53
60
 
54
61
  const discoveryMethods = ['Target.targetCreated', 'Target.targetInfoChanged'];
@@ -463,7 +470,7 @@ export async function protectedErrorPage({
463
470
  result.stickyError = true;
464
471
  await pause(100);
465
472
  }
466
- await lockChecks({ client, session, targetId, result });
473
+ await lockChecks({ client, reader, session, targetId, result, until });
467
474
  return result;
468
475
  } finally {
469
476
  stopped = true;
@@ -150,6 +150,10 @@
150
150
  "syntheticAutoAttach": {
151
151
  "type": "string",
152
152
  "minLength": 1
153
+ },
154
+ "closeProtected": {
155
+ "type": "string",
156
+ "minLength": 1
153
157
  }
154
158
  }
155
159
  },
@@ -187,7 +191,8 @@
187
191
  "secureOrigin",
188
192
  "forwardAudit",
189
193
  "stockSetup",
190
- "syntheticAutoAttach"
194
+ "syntheticAutoAttach",
195
+ "closeProtected"
191
196
  ]
192
197
  },
193
198
  "requires": {
@@ -218,6 +223,9 @@
218
223
  "requires": {
219
224
  "$ref": "#/$defs/requires"
220
225
  },
226
+ "unless": {
227
+ "$ref": "#/$defs/requires"
228
+ },
221
229
  "check": {
222
230
  "$ref": "#/$defs/check"
223
231
  },