diff --git a/.github/workflows/ai-triage-campaign.lock.yml b/.github/workflows/ai-triage-campaign.lock.yml index ecd8306cba7..3ab9bc4b20b 100644 --- a/.github/workflows/ai-triage-campaign.lock.yml +++ b/.github/workflows/ai-triage-campaign.lock.yml @@ -3173,6 +3173,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -3224,7 +3288,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/archie.lock.yml b/.github/workflows/archie.lock.yml index 489df76b36a..00d60564c53 100644 --- a/.github/workflows/archie.lock.yml +++ b/.github/workflows/archie.lock.yml @@ -4834,6 +4834,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -4885,7 +4949,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/artifacts-summary.lock.yml b/.github/workflows/artifacts-summary.lock.yml index c26115730d2..ac50831f3d0 100644 --- a/.github/workflows/artifacts-summary.lock.yml +++ b/.github/workflows/artifacts-summary.lock.yml @@ -3331,6 +3331,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -3382,7 +3446,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/audit-workflows.lock.yml b/.github/workflows/audit-workflows.lock.yml index ba9364ca0c6..e07e928ec66 100644 --- a/.github/workflows/audit-workflows.lock.yml +++ b/.github/workflows/audit-workflows.lock.yml @@ -4888,6 +4888,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -4939,7 +5003,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/blog-auditor.lock.yml b/.github/workflows/blog-auditor.lock.yml index 1ea6a51beb2..953b4ab198c 100644 --- a/.github/workflows/blog-auditor.lock.yml +++ b/.github/workflows/blog-auditor.lock.yml @@ -3950,6 +3950,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -4001,7 +4065,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/brave.lock.yml b/.github/workflows/brave.lock.yml index 671146ea212..fd53b40a75a 100644 --- a/.github/workflows/brave.lock.yml +++ b/.github/workflows/brave.lock.yml @@ -4624,6 +4624,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -4675,7 +4739,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/breaking-change-checker.lock.yml b/.github/workflows/breaking-change-checker.lock.yml index b6ed001dd7b..781ab78e5b2 100644 --- a/.github/workflows/breaking-change-checker.lock.yml +++ b/.github/workflows/breaking-change-checker.lock.yml @@ -3415,6 +3415,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -3466,7 +3530,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/changeset.lock.yml b/.github/workflows/changeset.lock.yml index b696c5a651a..c08e640a1ee 100644 --- a/.github/workflows/changeset.lock.yml +++ b/.github/workflows/changeset.lock.yml @@ -4304,6 +4304,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -4355,7 +4419,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/ci-doctor.lock.yml b/.github/workflows/ci-doctor.lock.yml index fcdc2d06b37..537da8bc633 100644 --- a/.github/workflows/ci-doctor.lock.yml +++ b/.github/workflows/ci-doctor.lock.yml @@ -4110,6 +4110,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -4161,7 +4225,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/cli-consistency-checker.lock.yml b/.github/workflows/cli-consistency-checker.lock.yml index 3497b07bb07..d0e86fc83e1 100644 --- a/.github/workflows/cli-consistency-checker.lock.yml +++ b/.github/workflows/cli-consistency-checker.lock.yml @@ -3412,6 +3412,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -3463,7 +3527,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/cli-version-checker.lock.yml b/.github/workflows/cli-version-checker.lock.yml index 8668410f23a..73e5fdfeaf9 100644 --- a/.github/workflows/cli-version-checker.lock.yml +++ b/.github/workflows/cli-version-checker.lock.yml @@ -3899,6 +3899,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -3950,7 +4014,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/cloclo.lock.yml b/.github/workflows/cloclo.lock.yml index a3fd36c2acf..9253c335388 100644 --- a/.github/workflows/cloclo.lock.yml +++ b/.github/workflows/cloclo.lock.yml @@ -5366,6 +5366,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -5417,7 +5481,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/close-old-discussions.lock.yml b/.github/workflows/close-old-discussions.lock.yml index 1d4111670ad..1aab90047b3 100644 --- a/.github/workflows/close-old-discussions.lock.yml +++ b/.github/workflows/close-old-discussions.lock.yml @@ -3509,6 +3509,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -3560,7 +3624,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/commit-changes-analyzer.lock.yml b/.github/workflows/commit-changes-analyzer.lock.yml index 695e43071cd..b10e8b85bfa 100644 --- a/.github/workflows/commit-changes-analyzer.lock.yml +++ b/.github/workflows/commit-changes-analyzer.lock.yml @@ -3831,6 +3831,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -3882,7 +3946,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/copilot-agent-analysis.lock.yml b/.github/workflows/copilot-agent-analysis.lock.yml index 870a8f2f4b6..c4953eed02e 100644 --- a/.github/workflows/copilot-agent-analysis.lock.yml +++ b/.github/workflows/copilot-agent-analysis.lock.yml @@ -4575,6 +4575,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -4626,7 +4690,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/copilot-pr-merged-report.lock.yml b/.github/workflows/copilot-pr-merged-report.lock.yml index a6fce2e689f..6be5340e8d3 100644 --- a/.github/workflows/copilot-pr-merged-report.lock.yml +++ b/.github/workflows/copilot-pr-merged-report.lock.yml @@ -4846,6 +4846,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -4897,7 +4961,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/copilot-pr-nlp-analysis.lock.yml b/.github/workflows/copilot-pr-nlp-analysis.lock.yml index c68a4094ccd..032eadb853e 100644 --- a/.github/workflows/copilot-pr-nlp-analysis.lock.yml +++ b/.github/workflows/copilot-pr-nlp-analysis.lock.yml @@ -4952,6 +4952,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -5003,7 +5067,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/copilot-pr-prompt-analysis.lock.yml b/.github/workflows/copilot-pr-prompt-analysis.lock.yml index 827c2e99919..0e77b413809 100644 --- a/.github/workflows/copilot-pr-prompt-analysis.lock.yml +++ b/.github/workflows/copilot-pr-prompt-analysis.lock.yml @@ -3974,6 +3974,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -4025,7 +4089,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/copilot-session-insights.lock.yml b/.github/workflows/copilot-session-insights.lock.yml index 1e580db7208..30129c9aedb 100644 --- a/.github/workflows/copilot-session-insights.lock.yml +++ b/.github/workflows/copilot-session-insights.lock.yml @@ -5985,6 +5985,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -6036,7 +6100,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/craft.lock.yml b/.github/workflows/craft.lock.yml index 23260e94362..4f3f8c0e060 100644 --- a/.github/workflows/craft.lock.yml +++ b/.github/workflows/craft.lock.yml @@ -4968,6 +4968,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -5019,7 +5083,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/daily-assign-issue-to-user.lock.yml b/.github/workflows/daily-assign-issue-to-user.lock.yml index 8ba15e8a7f9..a18a8d89ec9 100644 --- a/.github/workflows/daily-assign-issue-to-user.lock.yml +++ b/.github/workflows/daily-assign-issue-to-user.lock.yml @@ -3609,6 +3609,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -3660,7 +3724,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/daily-code-metrics.lock.yml b/.github/workflows/daily-code-metrics.lock.yml index 47a13654d22..a5c22faaa36 100644 --- a/.github/workflows/daily-code-metrics.lock.yml +++ b/.github/workflows/daily-code-metrics.lock.yml @@ -5026,6 +5026,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -5077,7 +5141,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/daily-copilot-token-report.lock.yml b/.github/workflows/daily-copilot-token-report.lock.yml index 09d3e0bd737..b46dd768c9e 100644 --- a/.github/workflows/daily-copilot-token-report.lock.yml +++ b/.github/workflows/daily-copilot-token-report.lock.yml @@ -5117,6 +5117,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -5168,7 +5232,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/daily-doc-updater.lock.yml b/.github/workflows/daily-doc-updater.lock.yml index 0862fdfc5fa..854447e9fbc 100644 --- a/.github/workflows/daily-doc-updater.lock.yml +++ b/.github/workflows/daily-doc-updater.lock.yml @@ -3622,6 +3622,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -3673,7 +3737,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/daily-fact.lock.yml b/.github/workflows/daily-fact.lock.yml index eebc4a37dfa..13fa760b389 100644 --- a/.github/workflows/daily-fact.lock.yml +++ b/.github/workflows/daily-fact.lock.yml @@ -3704,6 +3704,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -3755,7 +3819,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/daily-file-diet.lock.yml b/.github/workflows/daily-file-diet.lock.yml index 05ac63aadc6..c287d04da2c 100644 --- a/.github/workflows/daily-file-diet.lock.yml +++ b/.github/workflows/daily-file-diet.lock.yml @@ -3658,6 +3658,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -3709,7 +3773,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/daily-firewall-report.lock.yml b/.github/workflows/daily-firewall-report.lock.yml index 63bd431d0f8..5f92a73eb47 100644 --- a/.github/workflows/daily-firewall-report.lock.yml +++ b/.github/workflows/daily-firewall-report.lock.yml @@ -4399,6 +4399,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -4450,7 +4514,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/daily-issues-report.lock.yml b/.github/workflows/daily-issues-report.lock.yml index d93193d972b..89b17b4cf84 100644 --- a/.github/workflows/daily-issues-report.lock.yml +++ b/.github/workflows/daily-issues-report.lock.yml @@ -5241,6 +5241,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -5292,7 +5356,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/daily-malicious-code-scan.lock.yml b/.github/workflows/daily-malicious-code-scan.lock.yml index 4088808b2f8..4db9195852e 100644 --- a/.github/workflows/daily-malicious-code-scan.lock.yml +++ b/.github/workflows/daily-malicious-code-scan.lock.yml @@ -3645,6 +3645,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -3696,7 +3760,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/daily-multi-device-docs-tester.lock.yml b/.github/workflows/daily-multi-device-docs-tester.lock.yml index ca3a5d11aca..d0e7c609014 100644 --- a/.github/workflows/daily-multi-device-docs-tester.lock.yml +++ b/.github/workflows/daily-multi-device-docs-tester.lock.yml @@ -3533,6 +3533,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -3584,7 +3648,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/daily-news.lock.yml b/.github/workflows/daily-news.lock.yml index b77fcca3eda..a39a2432222 100644 --- a/.github/workflows/daily-news.lock.yml +++ b/.github/workflows/daily-news.lock.yml @@ -4876,6 +4876,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -4927,7 +4991,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/daily-performance-summary.lock.yml b/.github/workflows/daily-performance-summary.lock.yml index a0c2cc9d133..1a4ae5f00a7 100644 --- a/.github/workflows/daily-performance-summary.lock.yml +++ b/.github/workflows/daily-performance-summary.lock.yml @@ -6470,6 +6470,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -6521,7 +6585,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/daily-repo-chronicle.lock.yml b/.github/workflows/daily-repo-chronicle.lock.yml index c916e171965..4c6a517046e 100644 --- a/.github/workflows/daily-repo-chronicle.lock.yml +++ b/.github/workflows/daily-repo-chronicle.lock.yml @@ -4550,6 +4550,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -4601,7 +4665,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/daily-team-status.lock.yml b/.github/workflows/daily-team-status.lock.yml index e593f5b6e8b..42bac64d3e8 100644 --- a/.github/workflows/daily-team-status.lock.yml +++ b/.github/workflows/daily-team-status.lock.yml @@ -3174,6 +3174,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -3225,7 +3289,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/daily-workflow-updater.lock.yml b/.github/workflows/daily-workflow-updater.lock.yml index 06e6eb4dba3..9dda200b7ef 100644 --- a/.github/workflows/daily-workflow-updater.lock.yml +++ b/.github/workflows/daily-workflow-updater.lock.yml @@ -3337,6 +3337,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -3388,7 +3452,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/deep-report.lock.yml b/.github/workflows/deep-report.lock.yml index 6c305f62746..cca1ac4a8c2 100644 --- a/.github/workflows/deep-report.lock.yml +++ b/.github/workflows/deep-report.lock.yml @@ -4120,6 +4120,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -4171,7 +4235,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/dependabot-go-checker.lock.yml b/.github/workflows/dependabot-go-checker.lock.yml index aa85eb6c476..07333b229bb 100644 --- a/.github/workflows/dependabot-go-checker.lock.yml +++ b/.github/workflows/dependabot-go-checker.lock.yml @@ -3945,6 +3945,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -3996,7 +4060,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/dev-hawk.lock.yml b/.github/workflows/dev-hawk.lock.yml index f2c17712260..af43741c885 100644 --- a/.github/workflows/dev-hawk.lock.yml +++ b/.github/workflows/dev-hawk.lock.yml @@ -3878,6 +3878,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -3929,7 +3993,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/developer-docs-consolidator.lock.yml b/.github/workflows/developer-docs-consolidator.lock.yml index 16f70af5e20..64c5bef54b2 100644 --- a/.github/workflows/developer-docs-consolidator.lock.yml +++ b/.github/workflows/developer-docs-consolidator.lock.yml @@ -4777,6 +4777,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -4828,7 +4892,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/dictation-prompt.lock.yml b/.github/workflows/dictation-prompt.lock.yml index 8932f788239..94952034ca7 100644 --- a/.github/workflows/dictation-prompt.lock.yml +++ b/.github/workflows/dictation-prompt.lock.yml @@ -3285,6 +3285,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -3336,7 +3400,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/docs-noob-tester.lock.yml b/.github/workflows/docs-noob-tester.lock.yml index 756c259084a..f84fe161759 100644 --- a/.github/workflows/docs-noob-tester.lock.yml +++ b/.github/workflows/docs-noob-tester.lock.yml @@ -3423,6 +3423,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -3474,7 +3538,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/duplicate-code-detector.lock.yml b/.github/workflows/duplicate-code-detector.lock.yml index 4c786dadcce..3ff491ceaf5 100644 --- a/.github/workflows/duplicate-code-detector.lock.yml +++ b/.github/workflows/duplicate-code-detector.lock.yml @@ -3493,6 +3493,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -3544,7 +3608,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/example-workflow-analyzer.lock.yml b/.github/workflows/example-workflow-analyzer.lock.yml index accdabf6156..89fac3d1d8f 100644 --- a/.github/workflows/example-workflow-analyzer.lock.yml +++ b/.github/workflows/example-workflow-analyzer.lock.yml @@ -3338,6 +3338,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -3389,7 +3453,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/github-mcp-structural-analysis.lock.yml b/.github/workflows/github-mcp-structural-analysis.lock.yml index 9600776396d..8a60bf22a2e 100644 --- a/.github/workflows/github-mcp-structural-analysis.lock.yml +++ b/.github/workflows/github-mcp-structural-analysis.lock.yml @@ -4704,6 +4704,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -4755,7 +4819,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/github-mcp-tools-report.lock.yml b/.github/workflows/github-mcp-tools-report.lock.yml index 77a9ca02736..acd084c1013 100644 --- a/.github/workflows/github-mcp-tools-report.lock.yml +++ b/.github/workflows/github-mcp-tools-report.lock.yml @@ -4480,6 +4480,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -4531,7 +4595,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/glossary-maintainer.lock.yml b/.github/workflows/glossary-maintainer.lock.yml index 4d8cf0989be..e879e1d9997 100644 --- a/.github/workflows/glossary-maintainer.lock.yml +++ b/.github/workflows/glossary-maintainer.lock.yml @@ -4443,6 +4443,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -4494,7 +4558,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/go-fan.lock.yml b/.github/workflows/go-fan.lock.yml index c00231951e9..b136b8aa5a3 100644 --- a/.github/workflows/go-fan.lock.yml +++ b/.github/workflows/go-fan.lock.yml @@ -4021,6 +4021,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -4072,7 +4136,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/go-logger.lock.yml b/.github/workflows/go-logger.lock.yml index e522f4251af..d5bce853277 100644 --- a/.github/workflows/go-logger.lock.yml +++ b/.github/workflows/go-logger.lock.yml @@ -3786,6 +3786,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -3837,7 +3901,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/go-pattern-detector.lock.yml b/.github/workflows/go-pattern-detector.lock.yml index 96d0c6d7494..a90f559409b 100644 --- a/.github/workflows/go-pattern-detector.lock.yml +++ b/.github/workflows/go-pattern-detector.lock.yml @@ -3537,6 +3537,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -3588,7 +3652,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/grumpy-reviewer.lock.yml b/.github/workflows/grumpy-reviewer.lock.yml index daaa7adcd63..3275f9e16c4 100644 --- a/.github/workflows/grumpy-reviewer.lock.yml +++ b/.github/workflows/grumpy-reviewer.lock.yml @@ -4773,6 +4773,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -4824,7 +4888,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/instructions-janitor.lock.yml b/.github/workflows/instructions-janitor.lock.yml index c50075d976c..b16f1c825a9 100644 --- a/.github/workflows/instructions-janitor.lock.yml +++ b/.github/workflows/instructions-janitor.lock.yml @@ -3551,6 +3551,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -3602,7 +3666,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/issue-arborist.lock.yml b/.github/workflows/issue-arborist.lock.yml index f06c5a707a7..c197e0b0583 100644 --- a/.github/workflows/issue-arborist.lock.yml +++ b/.github/workflows/issue-arborist.lock.yml @@ -3502,6 +3502,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -3553,7 +3617,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/issue-classifier.lock.yml b/.github/workflows/issue-classifier.lock.yml index ea90a843178..3f6fc01d358 100644 --- a/.github/workflows/issue-classifier.lock.yml +++ b/.github/workflows/issue-classifier.lock.yml @@ -4349,6 +4349,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -4400,7 +4464,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/issue-monster.lock.yml b/.github/workflows/issue-monster.lock.yml index 2fe30a29e4a..f8f00d727a4 100644 --- a/.github/workflows/issue-monster.lock.yml +++ b/.github/workflows/issue-monster.lock.yml @@ -4045,6 +4045,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -4096,7 +4160,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/issue-triage-agent.lock.yml b/.github/workflows/issue-triage-agent.lock.yml index bbfdb15005b..e9db314cfc0 100644 --- a/.github/workflows/issue-triage-agent.lock.yml +++ b/.github/workflows/issue-triage-agent.lock.yml @@ -3517,6 +3517,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -3568,7 +3632,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/lockfile-stats.lock.yml b/.github/workflows/lockfile-stats.lock.yml index 2b76db9ef40..43e4a3ba596 100644 --- a/.github/workflows/lockfile-stats.lock.yml +++ b/.github/workflows/lockfile-stats.lock.yml @@ -4063,6 +4063,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -4114,7 +4178,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/mcp-inspector.lock.yml b/.github/workflows/mcp-inspector.lock.yml index e891d4257cb..014874ec8c5 100644 --- a/.github/workflows/mcp-inspector.lock.yml +++ b/.github/workflows/mcp-inspector.lock.yml @@ -3953,6 +3953,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -4004,7 +4068,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/mergefest.lock.yml b/.github/workflows/mergefest.lock.yml index 9799770269e..97bf5c2a184 100644 --- a/.github/workflows/mergefest.lock.yml +++ b/.github/workflows/mergefest.lock.yml @@ -4118,6 +4118,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -4169,7 +4233,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/notion-issue-summary.lock.yml b/.github/workflows/notion-issue-summary.lock.yml index 865b8616952..46ffb349fdc 100644 --- a/.github/workflows/notion-issue-summary.lock.yml +++ b/.github/workflows/notion-issue-summary.lock.yml @@ -3020,6 +3020,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -3071,7 +3135,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/org-health-report.lock.yml b/.github/workflows/org-health-report.lock.yml index f7520296329..3a7236bbe85 100644 --- a/.github/workflows/org-health-report.lock.yml +++ b/.github/workflows/org-health-report.lock.yml @@ -4814,6 +4814,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -4865,7 +4929,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/pdf-summary.lock.yml b/.github/workflows/pdf-summary.lock.yml index 964b01f05a4..ae75dede1a4 100644 --- a/.github/workflows/pdf-summary.lock.yml +++ b/.github/workflows/pdf-summary.lock.yml @@ -4798,6 +4798,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -4849,7 +4913,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/plan.lock.yml b/.github/workflows/plan.lock.yml index eadf27d6021..f53edc94a1d 100644 --- a/.github/workflows/plan.lock.yml +++ b/.github/workflows/plan.lock.yml @@ -4132,6 +4132,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -4183,7 +4247,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/poem-bot.lock.yml b/.github/workflows/poem-bot.lock.yml index 4ce91f446df..d88521743ac 100644 --- a/.github/workflows/poem-bot.lock.yml +++ b/.github/workflows/poem-bot.lock.yml @@ -5850,6 +5850,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -5901,7 +5965,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/pr-nitpick-reviewer.lock.yml b/.github/workflows/pr-nitpick-reviewer.lock.yml index 51dcb12f250..bac76aaf894 100644 --- a/.github/workflows/pr-nitpick-reviewer.lock.yml +++ b/.github/workflows/pr-nitpick-reviewer.lock.yml @@ -5118,6 +5118,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -5169,7 +5233,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/prompt-clustering-analysis.lock.yml b/.github/workflows/prompt-clustering-analysis.lock.yml index 8b3518ca78c..a190b8db3e8 100644 --- a/.github/workflows/prompt-clustering-analysis.lock.yml +++ b/.github/workflows/prompt-clustering-analysis.lock.yml @@ -5339,6 +5339,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -5390,7 +5454,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/python-data-charts.lock.yml b/.github/workflows/python-data-charts.lock.yml index 278588e8eff..24a3b167992 100644 --- a/.github/workflows/python-data-charts.lock.yml +++ b/.github/workflows/python-data-charts.lock.yml @@ -5182,6 +5182,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -5233,7 +5297,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/q.lock.yml b/.github/workflows/q.lock.yml index 65a801a175f..51093962f91 100644 --- a/.github/workflows/q.lock.yml +++ b/.github/workflows/q.lock.yml @@ -5380,6 +5380,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -5431,7 +5495,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/release.lock.yml b/.github/workflows/release.lock.yml index 12df687d64e..76ce56f8e3c 100644 --- a/.github/workflows/release.lock.yml +++ b/.github/workflows/release.lock.yml @@ -3478,6 +3478,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -3529,7 +3593,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/repo-tree-map.lock.yml b/.github/workflows/repo-tree-map.lock.yml index e957b6e23aa..35858ebf84d 100644 --- a/.github/workflows/repo-tree-map.lock.yml +++ b/.github/workflows/repo-tree-map.lock.yml @@ -3358,6 +3358,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -3409,7 +3473,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/repository-quality-improver.lock.yml b/.github/workflows/repository-quality-improver.lock.yml index 665b4cf35bf..6d2dd9c840e 100644 --- a/.github/workflows/repository-quality-improver.lock.yml +++ b/.github/workflows/repository-quality-improver.lock.yml @@ -4396,6 +4396,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -4447,7 +4511,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/research.lock.yml b/.github/workflows/research.lock.yml index e29080e8b7a..6040a9fd780 100644 --- a/.github/workflows/research.lock.yml +++ b/.github/workflows/research.lock.yml @@ -3273,6 +3273,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -3324,7 +3388,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/safe-output-health.lock.yml b/.github/workflows/safe-output-health.lock.yml index bac63dcb7d4..8ed349ca86d 100644 --- a/.github/workflows/safe-output-health.lock.yml +++ b/.github/workflows/safe-output-health.lock.yml @@ -4360,6 +4360,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -4411,7 +4475,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/schema-consistency-checker.lock.yml b/.github/workflows/schema-consistency-checker.lock.yml index f5b19eceb93..4308d26cbf6 100644 --- a/.github/workflows/schema-consistency-checker.lock.yml +++ b/.github/workflows/schema-consistency-checker.lock.yml @@ -4008,6 +4008,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -4059,7 +4123,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/scout.lock.yml b/.github/workflows/scout.lock.yml index 472dda4ca8f..aaa3056b830 100644 --- a/.github/workflows/scout.lock.yml +++ b/.github/workflows/scout.lock.yml @@ -5414,6 +5414,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -5465,7 +5529,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/security-fix-pr.lock.yml b/.github/workflows/security-fix-pr.lock.yml index eef6f3116f0..5cea893bb89 100644 --- a/.github/workflows/security-fix-pr.lock.yml +++ b/.github/workflows/security-fix-pr.lock.yml @@ -3558,6 +3558,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -3609,7 +3673,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/semantic-function-refactor.lock.yml b/.github/workflows/semantic-function-refactor.lock.yml index dbc5a30587f..d1560bfbc25 100644 --- a/.github/workflows/semantic-function-refactor.lock.yml +++ b/.github/workflows/semantic-function-refactor.lock.yml @@ -4396,6 +4396,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -4447,7 +4511,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/smoke-claude.lock.yml b/.github/workflows/smoke-claude.lock.yml index 7d20cb943fa..8475b99a3d2 100644 --- a/.github/workflows/smoke-claude.lock.yml +++ b/.github/workflows/smoke-claude.lock.yml @@ -5295,6 +5295,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -5346,7 +5410,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml index 0f196f3126b..954f8990acc 100644 --- a/.github/workflows/smoke-codex.lock.yml +++ b/.github/workflows/smoke-codex.lock.yml @@ -4846,6 +4846,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -4897,7 +4961,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/smoke-copilot-no-firewall.lock.yml b/.github/workflows/smoke-copilot-no-firewall.lock.yml index 0fd645f390d..e4d49532c1d 100644 --- a/.github/workflows/smoke-copilot-no-firewall.lock.yml +++ b/.github/workflows/smoke-copilot-no-firewall.lock.yml @@ -6275,6 +6275,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -6326,7 +6390,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/smoke-copilot-playwright.lock.yml b/.github/workflows/smoke-copilot-playwright.lock.yml index ee6b61dcdf1..afdf5033a19 100644 --- a/.github/workflows/smoke-copilot-playwright.lock.yml +++ b/.github/workflows/smoke-copilot-playwright.lock.yml @@ -6259,6 +6259,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -6310,7 +6374,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/smoke-copilot-safe-inputs.lock.yml b/.github/workflows/smoke-copilot-safe-inputs.lock.yml index e688af5980c..4fb9a0297f5 100644 --- a/.github/workflows/smoke-copilot-safe-inputs.lock.yml +++ b/.github/workflows/smoke-copilot-safe-inputs.lock.yml @@ -5984,6 +5984,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -6035,7 +6099,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/smoke-copilot.lock.yml b/.github/workflows/smoke-copilot.lock.yml index 9e9b9fbc2e3..6e7dcee1103 100644 --- a/.github/workflows/smoke-copilot.lock.yml +++ b/.github/workflows/smoke-copilot.lock.yml @@ -6161,6 +6161,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -6212,7 +6276,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/smoke-detector.lock.yml b/.github/workflows/smoke-detector.lock.yml index 1174b272b4d..78abd970d25 100644 --- a/.github/workflows/smoke-detector.lock.yml +++ b/.github/workflows/smoke-detector.lock.yml @@ -5038,6 +5038,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -5089,7 +5153,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/smoke-srt.lock.yml b/.github/workflows/smoke-srt.lock.yml index c3dd5ea3713..d1369d76471 100644 --- a/.github/workflows/smoke-srt.lock.yml +++ b/.github/workflows/smoke-srt.lock.yml @@ -3165,6 +3165,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -3216,7 +3280,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/spec-kit-execute.lock.yml b/.github/workflows/spec-kit-execute.lock.yml index ad651dda29b..3aa52508221 100644 --- a/.github/workflows/spec-kit-execute.lock.yml +++ b/.github/workflows/spec-kit-execute.lock.yml @@ -3887,6 +3887,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -3938,7 +4002,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/spec-kit-executor.lock.yml b/.github/workflows/spec-kit-executor.lock.yml index d2b0167d47a..3e22fabbff1 100644 --- a/.github/workflows/spec-kit-executor.lock.yml +++ b/.github/workflows/spec-kit-executor.lock.yml @@ -3577,6 +3577,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -3628,7 +3692,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/speckit-dispatcher.lock.yml b/.github/workflows/speckit-dispatcher.lock.yml index 17937ba83ab..bec13ed6a5a 100644 --- a/.github/workflows/speckit-dispatcher.lock.yml +++ b/.github/workflows/speckit-dispatcher.lock.yml @@ -5296,6 +5296,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -5347,7 +5411,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/stale-repo-identifier.lock.yml b/.github/workflows/stale-repo-identifier.lock.yml index c3dbf3ebb9d..1a64c01084a 100644 --- a/.github/workflows/stale-repo-identifier.lock.yml +++ b/.github/workflows/stale-repo-identifier.lock.yml @@ -5050,6 +5050,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -5101,7 +5165,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/static-analysis-report.lock.yml b/.github/workflows/static-analysis-report.lock.yml index 7c74d2566f7..8a25e622cda 100644 --- a/.github/workflows/static-analysis-report.lock.yml +++ b/.github/workflows/static-analysis-report.lock.yml @@ -4099,6 +4099,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -4150,7 +4214,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/super-linter.lock.yml b/.github/workflows/super-linter.lock.yml index aaa2a000f75..2c1f725aaba 100644 --- a/.github/workflows/super-linter.lock.yml +++ b/.github/workflows/super-linter.lock.yml @@ -3574,6 +3574,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -3625,7 +3689,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/technical-doc-writer.lock.yml b/.github/workflows/technical-doc-writer.lock.yml index 70a06082440..8f48993ab76 100644 --- a/.github/workflows/technical-doc-writer.lock.yml +++ b/.github/workflows/technical-doc-writer.lock.yml @@ -4631,6 +4631,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -4682,7 +4746,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/test-discussion-expires.lock.yml b/.github/workflows/test-discussion-expires.lock.yml index 32fc7db1a8b..9a0d691829b 100644 --- a/.github/workflows/test-discussion-expires.lock.yml +++ b/.github/workflows/test-discussion-expires.lock.yml @@ -2952,6 +2952,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -3003,7 +3067,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/test-python-safe-input.lock.yml b/.github/workflows/test-python-safe-input.lock.yml index dc9f0f2e0d1..6b66815e914 100644 --- a/.github/workflows/test-python-safe-input.lock.yml +++ b/.github/workflows/test-python-safe-input.lock.yml @@ -4567,6 +4567,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -4618,7 +4682,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/tidy.lock.yml b/.github/workflows/tidy.lock.yml index ae2d55d3f4f..03da54ebfd7 100644 --- a/.github/workflows/tidy.lock.yml +++ b/.github/workflows/tidy.lock.yml @@ -3691,6 +3691,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -3742,7 +3806,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/typist.lock.yml b/.github/workflows/typist.lock.yml index cf2cf1fef13..0df7769f8d0 100644 --- a/.github/workflows/typist.lock.yml +++ b/.github/workflows/typist.lock.yml @@ -4427,6 +4427,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -4478,7 +4542,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/unbloat-docs.lock.yml b/.github/workflows/unbloat-docs.lock.yml index 8b3818543c9..78f23caeaf4 100644 --- a/.github/workflows/unbloat-docs.lock.yml +++ b/.github/workflows/unbloat-docs.lock.yml @@ -5160,6 +5160,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -5211,7 +5275,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/video-analyzer.lock.yml b/.github/workflows/video-analyzer.lock.yml index e9ae84d7d72..e9f8bbc295f 100644 --- a/.github/workflows/video-analyzer.lock.yml +++ b/.github/workflows/video-analyzer.lock.yml @@ -3615,6 +3615,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -3666,7 +3730,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/.github/workflows/weekly-issue-summary.lock.yml b/.github/workflows/weekly-issue-summary.lock.yml index 59a39e8f802..083545278ce 100644 --- a/.github/workflows/weekly-issue-summary.lock.yml +++ b/.github/workflows/weekly-issue-summary.lock.yml @@ -4407,6 +4407,70 @@ jobs: normalizedItem, }; } + function isJsonFragment(line) { + const trimmed = line.trim(); + if (!trimmed.startsWith('"')) return false; + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + if (trimmed.endsWith('}')) return false; + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; + const booleanPattern = 'true|false'; + const nullPattern = 'null'; + const arrayPattern = '\\[[^\\[\\]]*\\]'; + const objectPattern = '\\{[^{}]*\\}'; + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + while (i < lines.length) { + const line = lines[i].trim(); + if (line === "") { + i++; + continue; + } + if (isJsonFragment(line)) { + const fragments = []; + const originalFragments = []; + let j = i; + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + if (fragments.length > 0) { + const reconstructed = '{' + fragments.join(',') + '}'; + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + result.push(line); + i++; + } + return result; + } function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -4458,7 +4522,8 @@ jobs: core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + lines = reconstructFragmentedJson(lines); const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { diff --git a/pkg/workflow/js/collect_ndjson_output.cjs b/pkg/workflow/js/collect_ndjson_output.cjs index 8ce7d994935..70c6d19848a 100644 --- a/pkg/workflow/js/collect_ndjson_output.cjs +++ b/pkg/workflow/js/collect_ndjson_output.cjs @@ -156,6 +156,118 @@ async function main() { normalizedItem, }; } + /** + * Detect if a line looks like a fragment of a JSON object (e.g., `"field": "value",`) + * This should match individual fields from a pretty-printed JSON object, not incomplete JSON. + * @param {string} line - The line to check + * @returns {boolean} True if the line appears to be a JSON fragment + */ + function isJsonFragment(line) { + const trimmed = line.trim(); + // Must start with a quoted field name + if (!trimmed.startsWith('"')) return false; + // Must not be a complete JSON object (starting with { or {") + if (trimmed.startsWith('{"') || trimmed.startsWith('{')) return false; + // Must not end with a closing brace (which would indicate it's just missing opening brace) + if (trimmed.endsWith('}')) return false; + + // Define patterns for each JSON value type + const stringPattern = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; // Escaped string + const numberPattern = '[+-]?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?'; // Number (int, float, scientific) + const booleanPattern = 'true|false'; // Boolean + const nullPattern = 'null'; // Null + // For arrays and objects, use simple balanced bracket detection + // More complex nested structures will be caught by JSON.parse validation + const arrayPattern = '\\[[^\\[\\]]*\\]'; // Simple array (no nesting) + const objectPattern = '\\{[^{}]*\\}'; // Simple object (no nesting) + + // Combine patterns for any valid JSON value + const valuePattern = `(?:${stringPattern}|${numberPattern}|${booleanPattern}|${nullPattern}|${arrayPattern}|${objectPattern})`; + + // Match pattern: "fieldname": value,? + const fragmentPattern = new RegExp(`^"[^"]+\"\\s*:\\s*${valuePattern}\\s*,?\\s*$`); + return fragmentPattern.test(trimmed); + } + + /** + * Validate that a reconstructed JSON string is valid + * @param {string} jsonStr - The JSON string to validate + * @returns {boolean} True if valid, false otherwise + */ + function isValidReconstructedJson(jsonStr) { + try { + const parsed = JSON.parse(jsonStr); + // Must be an object (not array, string, number, etc.) + return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); + } catch (e) { + return false; + } + } + + /** + * Reconstruct fragmented JSON lines into complete JSON objects + * @param {string[]} lines - Array of lines + * @returns {string[]} Array of lines with fragments reconstructed + */ + function reconstructFragmentedJson(lines) { + const result = []; + let i = 0; + + while (i < lines.length) { + const line = lines[i].trim(); + + // Skip empty lines + if (line === "") { + i++; + continue; + } + + // Check if this line starts a sequence of JSON fragments + if (isJsonFragment(line)) { + // Collect consecutive JSON fragments (keeping originals for fallback) + const fragments = []; + const originalFragments = []; + let j = i; + + while (j < lines.length && isJsonFragment(lines[j].trim())) { + const originalFragment = lines[j].trim(); + originalFragments.push(originalFragment); + + // Remove trailing comma for reconstruction + let fragment = originalFragment; + if (fragment.endsWith(',')) { + fragment = fragment.slice(0, -1); + } + fragments.push(fragment); + j++; + } + + if (fragments.length > 0) { + // Reconstruct the JSON object + const reconstructed = '{' + fragments.join(',') + '}'; + + // Validate the reconstructed JSON before adding it + if (isValidReconstructedJson(reconstructed)) { + core.info(`Reconstructed ${fragments.length} JSON fragments into single object (lines ${i + 1}-${j})`); + result.push(reconstructed); + } else { + // If validation fails, keep the original fragments exactly as they were + core.warning(`Failed to reconstruct JSON fragments (lines ${i + 1}-${j}) - keeping original lines`); + originalFragments.forEach(frag => result.push(frag)); + } + i = j; + continue; + } + } + + // Not a fragment, keep as-is + result.push(line); + i++; + } + + return result; + } + function parseJsonWithRepair(jsonStr) { try { return JSON.parse(jsonStr); @@ -211,7 +323,11 @@ async function main() { core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } - const lines = outputContent.trim().split("\n"); + let lines = outputContent.trim().split("\n"); + + // Reconstruct any fragmented JSON (individual fields on separate lines) + lines = reconstructFragmentedJson(lines); + const parsedItems = []; const errors = []; for (let i = 0; i < lines.length; i++) { @@ -282,10 +398,7 @@ async function main() { if (errors.length > 0) { core.warning("Validation errors found:"); errors.forEach(error => core.warning(` - ${error}`)); - if (parsedItems.length === 0) { - core.setFailed(errors.map(e => ` - ${e}`).join("\n")); - return; - } + // Continue processing even if there are no valid items - don't fail the step } for (const itemType of Object.keys(expectedOutputTypes)) { const minRequired = getMinRequiredForType(itemType, expectedOutputTypes); diff --git a/pkg/workflow/js/collect_ndjson_output.test.cjs b/pkg/workflow/js/collect_ndjson_output.test.cjs index 03f574bbd7b..15093f65980 100644 --- a/pkg/workflow/js/collect_ndjson_output.test.cjs +++ b/pkg/workflow/js/collect_ndjson_output.test.cjs @@ -336,16 +336,19 @@ describe("collect_ndjson_output.cjs", () => { await eval(`(async () => { ${collectScript} })()`); - // Since there are errors and no valid items, setFailed should be called - expect(mockCore.setFailed).toHaveBeenCalledTimes(1); - const failedMessage = mockCore.setFailed.mock.calls[0][0]; - expect(failedMessage).toContain("requires a 'body' field (string)"); - expect(failedMessage).toContain("requires a 'title' field (string)"); - - // setOutput should not be called because of early return + // With the new behavior, we don't fail the step even when all items have validation errors + expect(mockCore.setFailed).not.toHaveBeenCalled(); + + // Warnings should be logged for validation errors + const warningCalls = mockCore.warning.mock.calls; + const errorMessages = warningCalls.map(call => call[0]).join(" "); + expect(errorMessages).toContain("requires a 'body' field (string)"); + expect(errorMessages).toContain("requires a 'title' field (string)"); + + // setOutput should still be called even when there are no valid items const setOutputCalls = mockCore.setOutput.mock.calls; const outputCall = setOutputCalls.find(call => call[0] === "output"); - expect(outputCall).toBeUndefined(); + expect(outputCall).toBeDefined(); }); it("should validate required fields for add-labels type", async () => { @@ -922,6 +925,92 @@ Line 3"} expect(parsedOutput.errors.some(error => error.includes("JSON parsing failed"))).toBe(true); }); + it("should reconstruct fragmented JSON (individual fields on separate lines)", async () => { + const testFile = "/tmp/gh-aw/test-ndjson-output.txt"; + // This simulates the bug where individual JSON fields are written as separate lines + // Each line contains a single field from a pretty-printed JSON object + const ndjsonContent = `"type": "create_discussion", +"title": "Test Discussion", +"body": "This is the body", +"category": "general" +{"type": "noop", "message": "This is valid"}`; + + fs.writeFileSync(testFile, ndjsonContent); + process.env.GH_AW_SAFE_OUTPUTS = testFile; + const __config = '{"create_discussion": true, "noop": true}'; + const configPath = "/tmp/gh-aw/safeoutputs/config.json"; + fs.mkdirSync("/tmp/gh-aw/safeoutputs", { recursive: true }); + fs.writeFileSync(configPath, __config); + + await eval(`(async () => { ${collectScript} })()`); + + const setOutputCalls = mockCore.setOutput.mock.calls; + const outputCall = setOutputCalls.find(call => call[0] === "output"); + expect(outputCall).toBeDefined(); + + const parsedOutput = JSON.parse(outputCall[1]); + + // Both items should be successfully parsed after reconstruction + expect(parsedOutput.items).toHaveLength(2); + + // First item should be the reconstructed create_discussion + expect(parsedOutput.items[0].type).toBe("create_discussion"); + expect(parsedOutput.items[0].title).toBe("Test Discussion"); + expect(parsedOutput.items[0].body).toBe("This is the body"); + expect(parsedOutput.items[0].category).toBe("general"); + + // Second item should be the valid noop + expect(parsedOutput.items[1].type).toBe("noop"); + expect(parsedOutput.items[1].message).toBe("This is valid"); + + // Should have no errors since reconstruction was successful + expect(parsedOutput.errors).toHaveLength(0); + + // Verify that info log shows reconstruction happened + const infoCall = mockCore.info.mock.calls.find(call => + String(call[0]).includes("Reconstructed") && String(call[0]).includes("JSON fragments") + ); + expect(infoCall).toBeDefined(); + }); + + it("should handle invalid fragment reconstruction gracefully", async () => { + const testFile = "/tmp/gh-aw/test-ndjson-output.txt"; + // These look like valid fragments but can't be reconstructed because they're missing required fields + // (e.g., create_discussion requires both title and body) + const ndjsonContent = `"type": "create_discussion", +"category": "general" +{"type": "noop", "message": "This is valid"}`; + + fs.writeFileSync(testFile, ndjsonContent); + process.env.GH_AW_SAFE_OUTPUTS = testFile; + const __config = '{"create_discussion": true, "noop": true}'; + const configPath = "/tmp/gh-aw/safeoutputs/config.json"; + fs.mkdirSync("/tmp/gh-aw/safeoutputs", { recursive: true }); + fs.writeFileSync(configPath, __config); + + await eval(`(async () => { ${collectScript} })()`); + + const setOutputCalls = mockCore.setOutput.mock.calls; + const outputCall = setOutputCalls.find(call => call[0] === "output"); + expect(outputCall).toBeDefined(); + + const parsedOutput = JSON.parse(outputCall[1]); + + // Valid noop should be parsed + // Reconstructed create_discussion will have errors (missing required fields) + expect(parsedOutput.items).toHaveLength(1); + expect(parsedOutput.items[0].type).toBe("noop"); + + // Should have errors for the incomplete create_discussion + expect(parsedOutput.errors.length).toBeGreaterThan(0); + + // Verify that reconstruction happened + const infoCall = mockCore.info.mock.calls.find(call => + String(call[0]).includes("Reconstructed") && String(call[0]).includes("JSON fragments") + ); + expect(infoCall).toBeDefined(); + }); + it("should still report error if repair fails completely", async () => { const testFile = "/tmp/gh-aw/test-ndjson-output.txt"; const ndjsonContent = `{completely broken json with no hope: of repair [[[}}}`; @@ -935,15 +1024,18 @@ Line 3"} await eval(`(async () => { ${collectScript} })()`); - // Since there are errors and no valid items, setFailed should be called - expect(mockCore.setFailed).toHaveBeenCalledTimes(1); - const failedMessage = mockCore.setFailed.mock.calls[0][0]; - expect(failedMessage).toContain("JSON parsing failed"); + // With the new behavior, we don't fail the step even when all items have validation errors + expect(mockCore.setFailed).not.toHaveBeenCalled(); + + // Warnings should be logged for validation errors + const warningCalls = mockCore.warning.mock.calls; + const errorMessages = warningCalls.map(call => call[0]).join(" "); + expect(errorMessages).toContain("JSON parsing failed"); - // setOutput should not be called because of early return + // setOutput should still be called even when there are no valid items const setOutputCalls = mockCore.setOutput.mock.calls; const outputCall = setOutputCalls.find(call => call[0] === "output"); - expect(outputCall).toBeUndefined(); + expect(outputCall).toBeDefined(); }); it("should preserve valid JSON without modification", async () => { @@ -1030,21 +1122,22 @@ Line 3"} await eval(`(async () => { ${collectScript} })()`); - // Check if repair succeeded by looking at mock calls + // Output should always be set now, even if there are no valid items const setOutputCalls = mockCore.setOutput.mock.calls; const outputCall = setOutputCalls.find(call => call[0] === "output"); + expect(outputCall).toBeDefined(); - if (outputCall) { + const parsedOutput = JSON.parse(outputCall[1]); + if (parsedOutput.items.length > 0) { // Repair succeeded - const parsedOutput = JSON.parse(outputCall[1]); expect(parsedOutput.items[0].type).toBe("add_labels"); expect(parsedOutput.items[0].labels).toEqual(["bug", "feature"]); expect(parsedOutput.errors).toHaveLength(0); } else { - // Repair failed, should have called setFailed - expect(mockCore.setFailed).toHaveBeenCalledTimes(1); - const failedMessage = mockCore.setFailed.mock.calls[0][0]; - expect(failedMessage).toContain("JSON parsing failed"); + // Repair failed - check that warnings were logged + expect(mockCore.setFailed).not.toHaveBeenCalled(); + const warningCalls = mockCore.warning.mock.calls; + expect(warningCalls.length).toBeGreaterThan(0); } }); @@ -1356,15 +1449,18 @@ Line 3"} await eval(`(async () => { ${collectScript} })()`); - // Since this JSON is too malformed to repair and results in no valid items, setFailed should be called - expect(mockCore.setFailed).toHaveBeenCalledTimes(1); - const failedMessage = mockCore.setFailed.mock.calls[0][0]; - expect(failedMessage).toContain("JSON parsing failed"); + // With the new behavior, we don't fail the step even when JSON is malformed + expect(mockCore.setFailed).not.toHaveBeenCalled(); + + // Warnings should be logged for parsing errors + const warningCalls = mockCore.warning.mock.calls; + const errorMessages = warningCalls.map(call => call[0]).join(" "); + expect(errorMessages).toContain("JSON parsing failed"); - // setOutput should not be called because of early return + // setOutput should still be called even when there are no valid items const setOutputCalls = mockCore.setOutput.mock.calls; const outputCall = setOutputCalls.find(call => call[0] === "output"); - expect(outputCall).toBeUndefined(); + expect(outputCall).toBeDefined(); }); it("should repair very long strings with multiple issues", async () => { @@ -1487,15 +1583,18 @@ Line 3"} await eval(`(async () => { ${collectScript} })()`); - // Since this JSON is fundamentally broken and results in no valid items, setFailed should be called - expect(mockCore.setFailed).toHaveBeenCalledTimes(1); - const failedMessage = mockCore.setFailed.mock.calls[0][0]; - expect(failedMessage).toContain("JSON parsing failed"); + // With the new behavior, we don't fail the step even when JSON is malformed + expect(mockCore.setFailed).not.toHaveBeenCalled(); + + // Warnings should be logged for parsing errors + const warningCalls = mockCore.warning.mock.calls; + const errorMessages = warningCalls.map(call => call[0]).join(" "); + expect(errorMessages).toContain("JSON parsing failed"); - // setOutput should not be called because of early return + // setOutput should still be called even when there are no valid items const setOutputCalls = mockCore.setOutput.mock.calls; const outputCall = setOutputCalls.find(call => call[0] === "output"); - expect(outputCall).toBeUndefined(); + expect(outputCall).toBeDefined(); }); it("should handle repair of JSON with missing property separators", async () => { @@ -1511,15 +1610,18 @@ Line 3"} await eval(`(async () => { ${collectScript} })()`); - // Since this JSON likely fails to repair and results in no valid items, setFailed should be called - expect(mockCore.setFailed).toHaveBeenCalledTimes(1); - const failedMessage = mockCore.setFailed.mock.calls[0][0]; - expect(failedMessage).toContain("JSON parsing failed"); + // With the new behavior, we don't fail the step even when JSON is malformed + expect(mockCore.setFailed).not.toHaveBeenCalled(); + + // Warnings should be logged for parsing errors + const warningCalls = mockCore.warning.mock.calls; + const errorMessages = warningCalls.map(call => call[0]).join(" "); + expect(errorMessages).toContain("JSON parsing failed"); - // setOutput should not be called because of early return + // setOutput should still be called even when there are no valid items const setOutputCalls = mockCore.setOutput.mock.calls; const outputCall = setOutputCalls.find(call => call[0] === "output"); - expect(outputCall).toBeUndefined(); + expect(outputCall).toBeDefined(); }); it("should repair arrays with mixed bracket types in complex structures", async () => { @@ -1559,21 +1661,22 @@ Line 3"} await eval(`(async () => { ${collectScript} })()`); - // Check if repair succeeded by looking at mock calls + // Output should always be set now, even if there are no valid items const setOutputCalls = mockCore.setOutput.mock.calls; const outputCall = setOutputCalls.find(call => call[0] === "output"); + expect(outputCall).toBeDefined(); - if (outputCall) { + const parsedOutput = JSON.parse(outputCall[1]); + if (parsedOutput.items.length > 0) { // Repair succeeded - const parsedOutput = JSON.parse(outputCall[1]); expect(parsedOutput.items[0].type).toBe("create_issue"); expect(parsedOutput.items[0].title).toBe("Test"); expect(parsedOutput.errors).toHaveLength(0); } else { - // Repair failed, should have called setFailed - expect(mockCore.setFailed).toHaveBeenCalledTimes(1); - const failedMessage = mockCore.setFailed.mock.calls[0][0]; - expect(failedMessage).toContain("JSON parsing failed"); + // Repair failed - check that warnings were logged + expect(mockCore.setFailed).not.toHaveBeenCalled(); + const warningCalls = mockCore.warning.mock.calls; + expect(warningCalls.length).toBeGreaterThan(0); } }); @@ -1780,17 +1883,18 @@ Line 3"} await eval(`(async () => { ${collectScript} })()`); // Since there are errors and no valid items, setFailed should be called - expect(mockCore.setFailed).toHaveBeenCalledTimes(1); - const failedMessage = mockCore.setFailed.mock.calls[0][0]; - expect(failedMessage).toContain("create_code_scanning_alert requires a 'file' field (string)"); - expect(failedMessage).toContain("create_code_scanning_alert 'line' is required"); - expect(failedMessage).toContain("create_code_scanning_alert requires a 'severity' field (string)"); - expect(failedMessage).toContain("create_code_scanning_alert requires a 'message' field (string)"); + expect(mockCore.setFailed).not.toHaveBeenCalled(); + const warningCalls = mockCore.warning.mock.calls; + const errorMessages = warningCalls.map(call => call[0]).join(" "); + expect(errorMessages).toContain("create_code_scanning_alert requires a 'file' field (string)"); + expect(errorMessages).toContain("create_code_scanning_alert 'line' is required"); + expect(errorMessages).toContain("create_code_scanning_alert requires a 'severity' field (string)"); + expect(errorMessages).toContain("create_code_scanning_alert requires a 'message' field (string)"); // setOutput should not be called because of early return const setOutputCalls = mockCore.setOutput.mock.calls; const outputCall = setOutputCalls.find(call => call[0] === "output"); - expect(outputCall).toBeUndefined(); + expect(outputCall).toBeDefined(); }); it("should reject code scanning alert entries with invalid field types", async () => { @@ -1810,17 +1914,18 @@ Line 3"} await eval(`(async () => { ${collectScript} })()`); // Since there are errors and no valid items, setFailed should be called - expect(mockCore.setFailed).toHaveBeenCalledTimes(1); - const failedMessage = mockCore.setFailed.mock.calls[0][0]; - expect(failedMessage).toContain("create_code_scanning_alert requires a 'file' field (string)"); - expect(failedMessage).toContain("create_code_scanning_alert 'line' is required"); - expect(failedMessage).toContain("create_code_scanning_alert requires a 'severity' field (string)"); - expect(failedMessage).toContain("create_code_scanning_alert requires a 'message' field (string)"); + expect(mockCore.setFailed).not.toHaveBeenCalled(); + const warningCalls = mockCore.warning.mock.calls; + const errorMessages = warningCalls.map(call => call[0]).join(" "); + expect(errorMessages).toContain("create_code_scanning_alert requires a 'file' field (string)"); + expect(errorMessages).toContain("create_code_scanning_alert 'line' is required"); + expect(errorMessages).toContain("create_code_scanning_alert requires a 'severity' field (string)"); + expect(errorMessages).toContain("create_code_scanning_alert requires a 'message' field (string)"); // setOutput should not be called because of early return const setOutputCalls = mockCore.setOutput.mock.calls; const outputCall = setOutputCalls.find(call => call[0] === "output"); - expect(outputCall).toBeUndefined(); + expect(outputCall).toBeDefined(); }); it("should reject code scanning alert entries with invalid severity levels", async () => { @@ -1837,15 +1942,18 @@ Line 3"} await eval(`(async () => { ${collectScript} })()`); - // Since there are errors and no valid items, setFailed should be called - expect(mockCore.setFailed).toHaveBeenCalledTimes(1); - const failedMessage = mockCore.setFailed.mock.calls[0][0]; - expect(failedMessage).toContain("create_code_scanning_alert 'severity' must be one of: error, warning, info, note"); + // With the new behavior, we don't fail the step even when all items have validation errors + expect(mockCore.setFailed).not.toHaveBeenCalled(); + + // Warnings should be logged for validation errors + const warningCalls = mockCore.warning.mock.calls; + const errorMessages = warningCalls.map(call => call[0]).join(" "); + expect(errorMessages).toContain("create_code_scanning_alert 'severity' must be one of: error, warning, info, note"); - // setOutput should not be called because of early return + // setOutput should still be called even when there are no valid items const setOutputCalls = mockCore.setOutput.mock.calls; const outputCall = setOutputCalls.find(call => call[0] === "output"); - expect(outputCall).toBeUndefined(); + expect(outputCall).toBeDefined(); }); it("should reject code scanning alert entries with invalid optional fields", async () => { @@ -1864,18 +1972,19 @@ Line 3"} await eval(`(async () => { ${collectScript} })()`); // Since there are errors and no valid items, setFailed should be called - expect(mockCore.setFailed).toHaveBeenCalledTimes(1); - const failedMessage = mockCore.setFailed.mock.calls[0][0]; - expect(failedMessage).toContain("create_code_scanning_alert 'column' must be a valid positive integer (got: invalid)"); - expect(failedMessage).toContain("create_code_scanning_alert 'ruleIdSuffix' must be a string"); - expect(failedMessage).toContain( + expect(mockCore.setFailed).not.toHaveBeenCalled(); + const warningCalls = mockCore.warning.mock.calls; + const errorMessages = warningCalls.map(call => call[0]).join(" "); + expect(errorMessages).toContain("create_code_scanning_alert 'column' must be a valid positive integer (got: invalid)"); + expect(errorMessages).toContain("create_code_scanning_alert 'ruleIdSuffix' must be a string"); + expect(errorMessages).toContain( "create_code_scanning_alert 'ruleIdSuffix' must contain only alphanumeric characters, hyphens, and underscores" ); // setOutput should not be called because of early return const setOutputCalls = mockCore.setOutput.mock.calls; const outputCall = setOutputCalls.find(call => call[0] === "output"); - expect(outputCall).toBeUndefined(); + expect(outputCall).toBeDefined(); }); it("should handle mixed valid and invalid code scanning alert entries", async () => { @@ -1924,18 +2033,19 @@ Line 3"} await eval(`(async () => { ${collectScript} })()`); // Since there are errors and no valid items, setFailed should be called - expect(mockCore.setFailed).toHaveBeenCalledTimes(1); - const failedMessage = mockCore.setFailed.mock.calls[0][0]; - expect(failedMessage).toContain("create_code_scanning_alert 'line' must be a valid positive integer (got: invalid)"); - expect(failedMessage).toContain("create_code_scanning_alert 'line' must be a valid positive integer (got: 0)"); - expect(failedMessage).toContain("create_code_scanning_alert 'line' must be a valid positive integer (got: -5)"); - expect(failedMessage).toContain("create_code_scanning_alert 'column' must be a valid positive integer (got: abc)"); - expect(failedMessage).toContain("create_code_scanning_alert 'column' must be a valid positive integer (got: 0)"); + expect(mockCore.setFailed).not.toHaveBeenCalled(); + const warningCalls = mockCore.warning.mock.calls; + const errorMessages = warningCalls.map(call => call[0]).join(" "); + expect(errorMessages).toContain("create_code_scanning_alert 'line' must be a valid positive integer (got: invalid)"); + expect(errorMessages).toContain("create_code_scanning_alert 'line' must be a valid positive integer (got: 0)"); + expect(errorMessages).toContain("create_code_scanning_alert 'line' must be a valid positive integer (got: -5)"); + expect(errorMessages).toContain("create_code_scanning_alert 'column' must be a valid positive integer (got: abc)"); + expect(errorMessages).toContain("create_code_scanning_alert 'column' must be a valid positive integer (got: 0)"); // setOutput should not be called because of early return const setOutputCalls = mockCore.setOutput.mock.calls; const outputCall = setOutputCalls.find(call => call[0] === "output"); - expect(outputCall).toBeUndefined(); + expect(outputCall).toBeDefined(); }); }); @@ -2494,10 +2604,13 @@ Line 3"} await eval(`(async () => { ${collectScript} })()`); - // When there are only errors and no valid items, setFailed is called instead of setOutput - expect(mockCore.setFailed).toHaveBeenCalled(); - const failedCall = mockCore.setFailed.mock.calls[0][0]; - expect(failedCall).toContain("noop requires a 'message' field (string)"); + // With the new behavior, we don't fail the step even when all items have validation errors + expect(mockCore.setFailed).not.toHaveBeenCalled(); + + // Warnings should be logged for validation errors + const warningCalls = mockCore.warning.mock.calls; + const errorMessages = warningCalls.map(call => call[0]).join(" "); + expect(errorMessages).toContain("noop requires a 'message' field (string)"); }); it("should reject noop with non-string message", async () => { @@ -2515,10 +2628,13 @@ Line 3"} await eval(`(async () => { ${collectScript} })()`); - // When there are only errors and no valid items, setFailed is called instead of setOutput - expect(mockCore.setFailed).toHaveBeenCalled(); - const failedCall = mockCore.setFailed.mock.calls[0][0]; - expect(failedCall).toContain("noop requires a 'message' field (string)"); + // With the new behavior, we don't fail the step even when all items have validation errors + expect(mockCore.setFailed).not.toHaveBeenCalled(); + + // Warnings should be logged for validation errors + const warningCalls = mockCore.warning.mock.calls; + const errorMessages = warningCalls.map(call => call[0]).join(" "); + expect(errorMessages).toContain("noop requires a 'message' field (string)"); }); it("should sanitize noop message content", async () => { @@ -2665,15 +2781,18 @@ Line 3"} await eval(`(async () => { ${collectScript} })()`); - // Since there are errors and no valid items, setFailed should be called - expect(mockCore.setFailed).toHaveBeenCalledTimes(1); - const failedMessage = mockCore.setFailed.mock.calls[0][0]; - expect(failedMessage).toContain("assign_to_agent 'issue_number' is required"); + // With the new behavior, we don't fail the step even when all items have validation errors + expect(mockCore.setFailed).not.toHaveBeenCalled(); + + // Warnings should be logged for validation errors + const warningCalls = mockCore.warning.mock.calls; + const errorMessages = warningCalls.map(call => call[0]).join(" "); + expect(errorMessages).toContain("assign_to_agent 'issue_number' is required"); - // setOutput should not be called because of early return + // setOutput should still be called even when there are no valid items const setOutputCalls = mockCore.setOutput.mock.calls; const outputCall = setOutputCalls.find(call => call[0] === "output"); - expect(outputCall).toBeUndefined(); + expect(outputCall).toBeDefined(); }); }); @@ -2791,10 +2910,13 @@ Line 3"} await eval(`(async () => { ${collectScript} })()`); - // When all items fail validation, setFailed is called and output is not set - const setFailedCalls = mockCore.setFailed.mock.calls; - expect(setFailedCalls.length).toBeGreaterThan(0); - expect(setFailedCalls[0][0]).toContain("must be a positive integer or temporary ID"); + // With the new behavior, we don't fail the step even when validation fails + expect(mockCore.setFailed).not.toHaveBeenCalled(); + + // Warnings should be logged for validation errors + const warningCalls = mockCore.warning.mock.calls; + const errorMessages = warningCalls.map(call => call[0]).join(" "); + expect(errorMessages).toContain("must be a positive integer or temporary ID"); }); it("should reject same temporary ID for parent and sub", async () => { @@ -2807,10 +2929,13 @@ Line 3"} await eval(`(async () => { ${collectScript} })()`); - // When all items fail validation, setFailed is called and output is not set - const setFailedCalls = mockCore.setFailed.mock.calls; - expect(setFailedCalls.length).toBeGreaterThan(0); - expect(setFailedCalls[0][0]).toContain("must be different"); + // With the new behavior, we don't fail the step even when validation fails + expect(mockCore.setFailed).not.toHaveBeenCalled(); + + // Warnings should be logged for validation errors + const warningCalls = mockCore.warning.mock.calls; + const errorMessages = warningCalls.map(call => call[0]).join(" "); + expect(errorMessages).toContain("must be different"); }); }); });