@@ -930,26 +930,52 @@ function parseJsonOutput(stdout) {
|
930 | 930 | if (!trimmed) { |
931 | 931 | throw new Error("gateway call produced no JSON output"); |
932 | 932 | } |
933 | | -try { |
934 | | - return JSON.parse(trimmed); |
935 | | -} catch { |
936 | | - const jsonStart = trimmed.indexOf("{"); |
937 | | - if (jsonStart >= 0) { |
938 | | - try { |
939 | | - return JSON.parse(trimmed.slice(jsonStart)); |
940 | | - } catch { |
941 | | - // Fall through to the line-oriented fallback below. |
942 | | -} |
| 933 | +const parsed = parseJsonValue(trimmed); |
| 934 | +if (parsed.ok) { |
| 935 | + return parsed.value; |
| 936 | +} |
| 937 | + |
| 938 | +let lastParsed; |
| 939 | +const lines = trimmed.split(/\r?\n/u); |
| 940 | +for (let start = lines.length - 1; start >= 0; start -= 1) { |
| 941 | +if (!lines[start].trimStart().startsWith("{")) { |
| 942 | +continue; |
943 | 943 | } |
944 | | -const jsonLine = trimmed |
945 | | -.split(/\r?\n/u) |
946 | | -.toReversed() |
947 | | -.find((line) => line.trim().startsWith("{")); |
948 | | -if (!jsonLine) { |
949 | | -throw new Error(`gateway call JSON output was not parseable:\n${trimmed}`); |
| 944 | +let candidate = ""; |
| 945 | +for (let end = start; end < lines.length; end += 1) { |
| 946 | +candidate = candidate ? `${candidate}\n${lines[end]}` : lines[end]; |
| 947 | +const candidateParsed = parseJsonValue(candidate); |
| 948 | +if (!candidateParsed.ok) { |
| 949 | +continue; |
| 950 | +} |
| 951 | +lastParsed ??= candidateParsed.value; |
| 952 | +if (isGatewayJsonOutput(candidateParsed.value)) { |
| 953 | +return candidateParsed.value; |
| 954 | +} |
| 955 | +break; |
950 | 956 | } |
951 | | -return JSON.parse(jsonLine); |
952 | 957 | } |
| 958 | +if (lastParsed !== undefined) { |
| 959 | +return lastParsed; |
| 960 | +} |
| 961 | +throw new Error(`gateway call JSON output was not parseable:\n${trimmed}`); |
| 962 | +} |
| 963 | + |
| 964 | +function parseJsonValue(text) { |
| 965 | +try { |
| 966 | +return { ok: true, value: JSON.parse(text) }; |
| 967 | +} catch { |
| 968 | +return { ok: false }; |
| 969 | +} |
| 970 | +} |
| 971 | + |
| 972 | +function isGatewayJsonOutput(raw) { |
| 973 | +return ( |
| 974 | +raw?.ok === false || |
| 975 | +hasOwnPayloadField(raw, "result") || |
| 976 | +hasOwnPayloadField(raw, "payload") || |
| 977 | +hasOwnPayloadField(raw, "data") |
| 978 | +); |
953 | 979 | } |
954 | 980 | |
955 | 981 | function hasOwnPayloadField(raw, field) { |
|