@poe-platform/safe-bash 0.1.785 → 0.1.787

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
@@ -640,7 +640,8 @@ Browser snapshots have no byte limit, including automatic snapshots after
640
640
  navigation. Legacy `maxSnapshotBytes` settings are ignored.
641
641
  Pass `onSnapshot` to `createPlaywrightCli` or `createPlaywrightController` to inspect
642
642
  navigation response headers and call `recapture()` before publishing YAML or JSON.
643
- Page URL and title reflect changes made by the hook. The `/playwright` entrypoint
643
+ Page URL, title and console events reflect changes made by the hook. Recaptures
644
+ re-resolve target selectors and finish before the session accepts another command. The `/playwright` entrypoint
644
645
  also exports `getPlaywrightMainFrameNavigation(context, page)` for passive metadata
645
646
  reads. See the [snapshot hook contract](src/contracts/playwright-snapshot.md).
646
647
  `playwright-cli --help` and `playwright-cli show --help` report dashboard
@@ -34,8 +34,18 @@ export function observePlaywrightCapabilities(context, registerCleanup, limits)
34
34
  history.set(record, { bytes: size, evict });
35
35
  if (size > state.maxBytes)
36
36
  forget(record);
37
- while (history.size > (limits.maxEventEntries ?? Infinity) || bytes > state.maxBytes)
38
- forget(history.keys().next().value);
37
+ while (history.size > (limits.maxEventEntries ?? Infinity) || bytes > state.maxBytes) {
38
+ // Preserve active navigation metadata ahead of rolling diagnostics, while
39
+ // still applying the configured byte and entry budgets to every record.
40
+ let diagnostic;
41
+ for (const record of history.keys()) {
42
+ if (!('mainFrameNavigation' in record) || !record.mainFrameNavigation) {
43
+ diagnostic = record;
44
+ break;
45
+ }
46
+ }
47
+ forget(diagnostic ?? history.keys().next().value);
48
+ }
39
49
  };
40
50
  const byteLength = (text) => new TextEncoder().encode(text).byteLength;
41
51
  const pageEvents = (page) => {
@@ -969,12 +969,7 @@ export function createPlaywrightController(options = {}) {
969
969
  return { sections };
970
970
  }
971
971
  sections.push(...await downloadSections(session));
972
- if (invocation.writeArtifact) {
973
- const consoleLink = await flushPlaywrightConsole(session.lease.context, page, { ...(session.configuration ? { configuration: session.configuration } : {}), writeArtifact });
974
- if (consoleLink)
975
- sections.push({ title: 'Events', content: `- New console entries: ${consoleLink}` });
976
- }
977
- const pageSectionIndex = sections.length;
972
+ let pageSectionIndex = sections.length;
978
973
  if (snapshot !== 'none' && (page.frames || page.ariaSnapshot || page._snapshotForAI || page.ariaSnapshotJSON || session.lease?.captureSnapshotJSON)) {
979
974
  const snapshotOptions = { timeout: sessionSnapshotTimeout(session), ...(session.configuration?.snapshot?.boxes === undefined ? {} : { boxes: session.configuration.snapshot.boxes }), ...(session.lease?.captureSnapshotReferences ? { captureReferences: session.lease.captureSnapshotReferences } : {}) };
980
975
  if (parsed.command === 'snapshot') {
@@ -986,18 +981,24 @@ export function createPlaywrightController(options = {}) {
986
981
  }
987
982
  if (parsed.options.boxes)
988
983
  snapshotOptions.boxes = true;
989
- if (parsed.args[0])
990
- snapshotOptions.root = await resolveTarget(session, parsed.args[0]);
991
984
  }
992
985
  const captureWithHook = async (format, capture) => {
993
986
  let active = true;
994
- const recapture = async () => {
987
+ let pending = Promise.resolve();
988
+ const recapture = () => {
995
989
  if (!active)
996
- throw new Error('Playwright snapshot hook has completed');
997
- check();
998
- const value = await capture();
999
- check();
1000
- return value;
990
+ return Promise.reject(new Error('Playwright snapshot hook has completed'));
991
+ const operation = pending.then(async () => {
992
+ check();
993
+ if (parsed.command === 'snapshot' && parsed.args[0])
994
+ snapshotOptions.root = await resolveTarget(session, parsed.args[0]);
995
+ const value = await capture();
996
+ check();
997
+ return value;
998
+ });
999
+ // Observe failures immediately, and serialize all admitted captures.
1000
+ pending = operation.then(() => { }, () => { });
1001
+ return operation;
1001
1002
  };
1002
1003
  try {
1003
1004
  const initial = await recapture();
@@ -1011,6 +1012,7 @@ export function createPlaywrightController(options = {}) {
1011
1012
  }
1012
1013
  finally {
1013
1014
  active = false;
1015
+ await pending;
1014
1016
  }
1015
1017
  };
1016
1018
  retained = false;
@@ -1019,24 +1021,30 @@ export function createPlaywrightController(options = {}) {
1019
1021
  check();
1020
1022
  retained = parsed.command === 'snapshot';
1021
1023
  sections.push({ title: 'Snapshot', content: { json: tree }, codeframe: 'json' });
1022
- return { sections };
1023
1024
  }
1024
- retained = false;
1025
- const text = await captureWithHook('yaml', () => session.snapshot.capture(page, local.signal, snapshotOptions));
1026
- check();
1027
- retained = parsed.command === 'snapshot';
1028
- if (filename !== undefined || snapshot === 'file' && invocation.writeArtifact) {
1029
- const target = filename ?? capabilityArtifactName('page', 'yml', session.configuration);
1030
- const bytes = new TextEncoder().encode(text);
1031
- if (bytes.byteLength > maxArtifactBytes)
1032
- throw new PlaywrightResourceLimitError('Artifact byte limit exceeded');
1033
- if (!invocation.writeArtifact)
1034
- throw new Error('Artifact byte destination unsupported');
1035
- await writeArtifact(bytes, target);
1036
- sections.push({ title: 'Snapshot', content: `- [Snapshot](${target})` });
1025
+ else {
1026
+ retained = false;
1027
+ const text = await captureWithHook('yaml', () => session.snapshot.capture(page, local.signal, snapshotOptions));
1028
+ check();
1029
+ retained = parsed.command === 'snapshot';
1030
+ if (filename !== undefined || snapshot === 'file' && invocation.writeArtifact) {
1031
+ const target = filename ?? capabilityArtifactName('page', 'yml', session.configuration);
1032
+ const bytes = new TextEncoder().encode(text);
1033
+ if (bytes.byteLength > maxArtifactBytes)
1034
+ throw new PlaywrightResourceLimitError('Artifact byte limit exceeded');
1035
+ if (!invocation.writeArtifact)
1036
+ throw new Error('Artifact byte destination unsupported');
1037
+ await writeArtifact(bytes, target);
1038
+ sections.push({ title: 'Snapshot', content: `- [Snapshot](${target})` });
1039
+ }
1040
+ else
1041
+ sections.push({ title: 'Snapshot', content: text.trimEnd(), codeframe: 'yaml' });
1037
1042
  }
1038
- else
1039
- sections.push({ title: 'Snapshot', content: text.trimEnd(), codeframe: 'yaml' });
1043
+ }
1044
+ if (invocation.writeArtifact) {
1045
+ const consoleLink = await flushPlaywrightConsole(session.lease.context, page, { ...(session.configuration ? { configuration: session.configuration } : {}), writeArtifact });
1046
+ if (consoleLink)
1047
+ sections.splice(pageSectionIndex++, 0, { title: 'Events', content: `- New console entries: ${consoleLink}` });
1040
1048
  }
1041
1049
  if (parsed.command !== 'snapshot' || !parsed.json) {
1042
1050
  const title = await page.title?.();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poe-platform/safe-bash",
3
- "version": "0.1.785",
3
+ "version": "0.1.787",
4
4
  "description": "Extensible virtual shell with pluggable filesystems and streaming commands",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -841,7 +841,7 @@
841
841
  "access": "public"
842
842
  },
843
843
  "dependencies": {
844
- "@poe-platform/safe-fs": "0.1.785",
844
+ "@poe-platform/safe-fs": "0.1.787",
845
845
  "pdf-lib": "1.17.1",
846
846
  "@noble/hashes": "2.4.0",
847
847
  "pako": "3.0.1",