gwchq-textjam 0.3.6 → 0.3.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.
@@ -133,6 +133,8 @@ const PyodideWorker = () => {
133
133
  let useSyncXhrStdin = false;
134
134
  /** True when current input request was cancelled from outside the worker. */
135
135
  let stdinCancelled = false;
136
+ /** True when the sync-XHR stdin transport failed (SW unreachable / request escaped). */
137
+ let stdinTransportFailed = false;
136
138
  /** Used when SharedArrayBuffer is unavailable: resolve for the pending input() call. */
137
139
  let pendingStdinResolve = null;
138
140
  // Until Pyodide is fully initialised, keep stdout/stderr in the dev console only.
@@ -185,6 +187,7 @@ const PyodideWorker = () => {
185
187
  case "runPython":
186
188
  currentRunId = data.runId || null;
187
189
  stdinCancelled = false;
190
+ stdinTransportFailed = false;
188
191
  runPython(data.python, data.userModuleNames);
189
192
  break;
190
193
  case "stopPython": {
@@ -238,10 +241,7 @@ const PyodideWorker = () => {
238
241
  await runUserCode();
239
242
  });
240
243
  } catch (error) {
241
- const isStdinControlError =
242
- error?.message === "PYODIDE_STDIN_CANCELLED" ||
243
- error?.message === "PYODIDE_STDIN_TIMEOUT" ||
244
- error?.message === "PYODIDE_STDIN_ABORTED";
244
+ const isStdinControlError = error?.message === "PYODIDE_STDIN_CANCELLED";
245
245
 
246
246
  if (stdinCancelled || isStdinControlError) {
247
247
  postMessage({
@@ -256,6 +256,19 @@ const PyodideWorker = () => {
256
256
  return;
257
257
  }
258
258
 
259
+ if (stdinTransportFailed) {
260
+ postMessage({
261
+ method: "handleError",
262
+ file: "main.py",
263
+ line: "",
264
+ mistake: "",
265
+ type: "InputUnavailableError",
266
+ info: "Input timed out. Rerun your code and try again.",
267
+ });
268
+ await clearPyodideData(userModuleNames);
269
+ return;
270
+ }
271
+
259
272
  if (!(error instanceof pyodide.ffi.PythonError)) {
260
273
  postMessage({
261
274
  method: "handleError",
@@ -691,59 +704,64 @@ const PyodideWorker = () => {
691
704
  };
692
705
 
693
706
  const readInputViaSyncXhr = (runId) => {
694
- const requestId = crypto.randomUUID();
695
- const requestUrl = new URL(
696
- stdinFallbackConfig.endpointPath,
697
- // eslint-disable-next-line no-restricted-globals
698
- self.location.origin,
699
- );
700
- requestUrl.searchParams.set("runId", runId || "");
701
- requestUrl.searchParams.set("requestId", requestId);
702
- requestUrl.searchParams.set("clientId", stdinFallbackConfig.clientId);
703
-
704
- const xhr = new XMLHttpRequest();
705
- xhr.open("GET", requestUrl.toString(), false);
706
- xhr.setRequestHeader("X-Pyodide-Stdin-Request", "true");
707
- xhr.setRequestHeader("Cache-Control", "no-store");
708
-
707
+ // Mark any failure with XHR setup as stdinTransportFailed for a clean error
709
708
  try {
709
+ const requestId = crypto.randomUUID();
710
+ const requestUrl = new URL(
711
+ stdinFallbackConfig.endpointPath,
712
+ // eslint-disable-next-line no-restricted-globals
713
+ self.location.origin,
714
+ );
715
+ requestUrl.searchParams.set("runId", runId || "");
716
+ requestUrl.searchParams.set("requestId", requestId);
717
+ requestUrl.searchParams.set("clientId", stdinFallbackConfig.clientId);
718
+
719
+ const xhr = new XMLHttpRequest();
720
+ xhr.open("GET", requestUrl.toString(), false);
721
+ xhr.setRequestHeader("X-Pyodide-Stdin-Request", "true");
722
+ xhr.setRequestHeader("Cache-Control", "no-store");
710
723
  xhr.send(null);
711
- } catch (_) {
712
- throw new Error("Failed to read Python input via stdin fallback");
713
- }
714
724
 
715
- if (xhr.status === STDIN_CANCELLED_RESPONSE_CODE) {
716
- stdinCancelled = true;
717
- throw new Error("PYODIDE_STDIN_CANCELLED");
718
- }
725
+ if (xhr.status === STDIN_CANCELLED_RESPONSE_CODE) {
726
+ stdinCancelled = true;
727
+ throw new Error("PYODIDE_STDIN_CANCELLED");
728
+ }
719
729
 
720
- if (xhr.status === STDIN_TIMEOUT_RESPONSE_CODE) {
721
- throw new Error("PYODIDE_STDIN_TIMEOUT");
722
- }
730
+ if (xhr.status === STDIN_TIMEOUT_RESPONSE_CODE) {
731
+ stdinTransportFailed = true;
732
+ throw new Error("PYODIDE_STDIN_TIMEOUT");
733
+ }
723
734
 
724
- if (xhr.status === STDIN_ABORTED_RESPONSE_CODE) {
725
- throw new Error("PYODIDE_STDIN_ABORTED");
726
- }
735
+ if (xhr.status === STDIN_ABORTED_RESPONSE_CODE) {
736
+ stdinTransportFailed = true;
737
+ throw new Error("PYODIDE_STDIN_ABORTED");
738
+ }
727
739
 
728
- // EOF: orchestrator signalled Ctrl+D / no more input. Returning null tells
729
- // Pyodide's line-based stdin handler to raise EOFError at the input() call
730
- // site, rather than escalating to a KeyboardInterrupt that tears down the run.
731
- if (xhr.status === STDIN_EOF_RESPONSE_CODE) {
732
- return null;
733
- }
740
+ // EOF: orchestrator signalled Ctrl+D / no more input. Returning null tells
741
+ // Pyodide's line-based stdin handler to raise EOFError at the input() call
742
+ // site, rather than escalating to a KeyboardInterrupt that tears down the run.
743
+ if (xhr.status === STDIN_EOF_RESPONSE_CODE) {
744
+ return null;
745
+ }
734
746
 
735
- if (xhr.status !== 200) {
736
- throw new Error(
737
- `Python input request failed with status ${xhr.status}: ${xhr.responseText}`,
738
- );
739
- }
747
+ if (xhr.status !== 200) {
748
+ throw new Error(
749
+ `Python input request failed with status ${xhr.status}: ${xhr.responseText}`,
750
+ );
751
+ }
740
752
 
741
- let content = xhr.responseText ?? "";
742
- if (!content.endsWith("\n")) {
743
- content += "\n";
744
- }
753
+ let content = xhr.responseText ?? "";
754
+ if (!content.endsWith("\n")) {
755
+ content += "\n";
756
+ }
745
757
 
746
- return content;
758
+ return content;
759
+ } catch (err) {
760
+ if (!stdinCancelled) {
761
+ stdinTransportFailed = true;
762
+ }
763
+ throw err;
764
+ }
747
765
  };
748
766
 
749
767
  const clearPyodideData = async (userModuleNames) => {