@exercode/problem-utils 4.2.6 → 4.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/dist/cli/checkAllProblems.cjs +1 -1
  2. package/dist/cli/checkAllProblems.cjs.map +1 -1
  3. package/dist/cli/checkAllProblems.js +1 -1
  4. package/dist/cli/checkAllProblems.js.map +1 -1
  5. package/dist/helpers/checkProblemDirIsolation.cjs +1 -1
  6. package/dist/helpers/checkProblemDirIsolation.cjs.map +1 -1
  7. package/dist/helpers/checkProblemDirIsolation.js +1 -1
  8. package/dist/helpers/checkProblemDirIsolation.js.map +1 -1
  9. package/dist/helpers/findModelAnswerDirs.cjs +1 -1
  10. package/dist/helpers/findModelAnswerDirs.cjs.map +1 -1
  11. package/dist/helpers/findModelAnswerDirs.js +1 -1
  12. package/dist/helpers/findModelAnswerDirs.js.map +1 -1
  13. package/dist/helpers/guiRecording.cjs +2 -0
  14. package/dist/helpers/guiRecording.cjs.map +1 -0
  15. package/dist/helpers/guiRecording.d.ts +32 -0
  16. package/dist/helpers/guiRecording.js +2 -0
  17. package/dist/helpers/guiRecording.js.map +1 -0
  18. package/dist/helpers/judgeByStaticAnalysis.cjs +1 -1
  19. package/dist/helpers/judgeByStaticAnalysis.cjs.map +1 -1
  20. package/dist/helpers/judgeByStaticAnalysis.js +1 -1
  21. package/dist/helpers/judgeByStaticAnalysis.js.map +1 -1
  22. package/dist/helpers/readTestCases.cjs +1 -1
  23. package/dist/helpers/readTestCases.cjs.map +1 -1
  24. package/dist/helpers/readTestCases.js +1 -1
  25. package/dist/helpers/readTestCases.js.map +1 -1
  26. package/dist/helpers/runCommandInTemporaryPackageManagerProject.cjs +1 -1
  27. package/dist/helpers/runCommandInTemporaryPackageManagerProject.cjs.map +1 -1
  28. package/dist/helpers/runCommandInTemporaryPackageManagerProject.js +1 -1
  29. package/dist/helpers/runCommandInTemporaryPackageManagerProject.js.map +1 -1
  30. package/dist/helpers/runHarnessProcess.cjs +1 -1
  31. package/dist/helpers/runHarnessProcess.cjs.map +1 -1
  32. package/dist/helpers/runHarnessProcess.js +1 -1
  33. package/dist/helpers/runHarnessProcess.js.map +1 -1
  34. package/dist/helpers/spawnWithLimits.cjs +1 -1
  35. package/dist/helpers/spawnWithLimits.cjs.map +1 -1
  36. package/dist/helpers/spawnWithLimits.js +1 -1
  37. package/dist/helpers/spawnWithLimits.js.map +1 -1
  38. package/dist/index.cjs +1 -1
  39. package/dist/index.d.ts +1 -0
  40. package/dist/index.js +1 -1
  41. package/dist/learningMaterial/fsHelpers.cjs +1 -1
  42. package/dist/learningMaterial/fsHelpers.cjs.map +1 -1
  43. package/dist/learningMaterial/fsHelpers.js +1 -1
  44. package/dist/learningMaterial/fsHelpers.js.map +1 -1
  45. package/dist/learningMaterial/validateContest.cjs +1 -1
  46. package/dist/learningMaterial/validateContest.cjs.map +1 -1
  47. package/dist/learningMaterial/validateContest.js +1 -1
  48. package/dist/learningMaterial/validateContest.js.map +1 -1
  49. package/dist/learningMaterial/validateCourse.cjs +1 -1
  50. package/dist/learningMaterial/validateCourse.cjs.map +1 -1
  51. package/dist/learningMaterial/validateCourse.js +1 -1
  52. package/dist/learningMaterial/validateCourse.js.map +1 -1
  53. package/dist/learningMaterial/validateMaterial.cjs +3 -3
  54. package/dist/learningMaterial/validateMaterial.cjs.map +1 -1
  55. package/dist/learningMaterial/validateMaterial.js +3 -3
  56. package/dist/learningMaterial/validateMaterial.js.map +1 -1
  57. package/dist/learningMaterial/validateProblem.cjs +1 -1
  58. package/dist/learningMaterial/validateProblem.cjs.map +1 -1
  59. package/dist/learningMaterial/validateProblem.js +1 -1
  60. package/dist/learningMaterial/validateProblem.js.map +1 -1
  61. package/dist/learningMaterial/validationResult.cjs +1 -1
  62. package/dist/learningMaterial/validationResult.cjs.map +1 -1
  63. package/dist/learningMaterial/validationResult.js +1 -1
  64. package/dist/learningMaterial/validationResult.js.map +1 -1
  65. package/dist/presets/command.cjs +1 -1
  66. package/dist/presets/command.cjs.map +1 -1
  67. package/dist/presets/command.js +1 -1
  68. package/dist/presets/command.js.map +1 -1
  69. package/dist/presets/evaluation.cjs +2 -2
  70. package/dist/presets/evaluation.cjs.map +1 -1
  71. package/dist/presets/evaluation.js +2 -2
  72. package/dist/presets/evaluation.js.map +1 -1
  73. package/dist/presets/guiCommand.cjs +3 -3
  74. package/dist/presets/guiCommand.cjs.map +1 -1
  75. package/dist/presets/guiCommand.d.ts +17 -3
  76. package/dist/presets/guiCommand.js +3 -3
  77. package/dist/presets/guiCommand.js.map +1 -1
  78. package/dist/presets/springBoot.cjs +4 -4
  79. package/dist/presets/springBoot.cjs.map +1 -1
  80. package/dist/presets/springBoot.js +4 -4
  81. package/dist/presets/springBoot.js.map +1 -1
  82. package/dist/presets/stdio.cjs +1 -1
  83. package/dist/presets/stdio.cjs.map +1 -1
  84. package/dist/presets/stdio.js +1 -1
  85. package/dist/presets/stdio.js.map +1 -1
  86. package/dist/presets/tomcat.cjs +4 -4
  87. package/dist/presets/tomcat.cjs.map +1 -1
  88. package/dist/presets/tomcat.js +4 -4
  89. package/dist/presets/tomcat.js.map +1 -1
  90. package/package.json +5 -4
@@ -1 +1 @@
1
- {"version":3,"file":"judgeByStaticAnalysis.cjs","names":["fs","path","findLanguageDefinitionByPath","removeCommentsInSourceCode","DecisionCode","normalizeCodeRule"],"sources":["../../src/helpers/judgeByStaticAnalysis.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { DecisionCode } from '../types/decisionCode.js';\nimport type { ProblemMarkdownFrontMatter } from '../types/problem.js';\nimport { normalizeCodeRule } from '../types/problem.js';\nimport type { TestCaseResult } from '../types/testCaseResult.js';\n\nimport { findLanguageDefinitionByPath } from './findLanguageDefinitionByPath.js';\nimport { removeCommentsInSourceCode } from './removeCommentsInSourceCode.js';\n\ninterface ForbiddenRuleViolation {\n /** Learner-facing description of the rule; the raw pattern when the problem declares no message. */\n label: string;\n matches: { path: string; match: string }[];\n}\n\nexport async function judgeByStaticAnalysis(\n cwd: string,\n problemMarkdownFrontMatterLike: Pick<\n ProblemMarkdownFrontMatter,\n 'forbiddenRegExpsInCode' | 'forbiddenTextsInCode' | 'requiredRegExpsInCode' | 'requiredSubmissionFilePaths'\n >\n): Promise<Pick<TestCaseResult, 'decisionCode' | 'feedbackMarkdown'> | undefined> {\n const needsSourceCode =\n !!problemMarkdownFrontMatterLike.forbiddenRegExpsInCode?.length ||\n !!problemMarkdownFrontMatterLike.forbiddenTextsInCode?.length ||\n !!problemMarkdownFrontMatterLike.requiredRegExpsInCode?.length;\n if (!needsSourceCode && !problemMarkdownFrontMatterLike.requiredSubmissionFilePaths?.length) return;\n\n const filePathSet = new Set<string>();\n const sourceCodeWithoutCommentFiles: { path: string; data: string }[] = [];\n\n for (const dirent of await fs.promises.readdir(cwd, { withFileTypes: true, recursive: true })) {\n if (!dirent.isFile()) continue;\n\n const relativePath = path.relative(cwd, path.join(dirent.parentPath, dirent.name));\n filePathSet.add(relativePath);\n\n if (!needsSourceCode) continue;\n const languageDefinition = findLanguageDefinitionByPath(dirent.name);\n if (!languageDefinition) continue;\n\n const text = await fs.promises.readFile(path.join(dirent.parentPath, dirent.name), 'utf8');\n const isBinary = text.includes('\\uFFFD');\n if (isBinary) continue;\n\n sourceCodeWithoutCommentFiles.push({\n path: relativePath,\n data: languageDefinition.grammer ? removeCommentsInSourceCode(languageDefinition.grammer, text) : text,\n });\n }\n\n if (problemMarkdownFrontMatterLike.requiredSubmissionFilePaths) {\n const missingFilePaths = problemMarkdownFrontMatterLike.requiredSubmissionFilePaths\n .filter((p) => !filePathSet.has(p))\n .toSorted();\n\n if (missingFilePaths.length > 0) {\n return {\n decisionCode: DecisionCode.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,\n feedbackMarkdown: `ファイルが不足しています。\n次のファイルを追加してから再度提出してください。\n\n${missingFilePaths.map((p) => `- \\`${p}\\``).join('\\n')}\n`,\n };\n }\n }\n\n const violations: ForbiddenRuleViolation[] = [];\n\n for (const rule of problemMarkdownFrontMatterLike.forbiddenRegExpsInCode ?? []) {\n const { pattern, message } = normalizeCodeRule(rule);\n const re = new RegExp(pattern, 'g');\n const matches = sourceCodeWithoutCommentFiles.flatMap((file) =>\n [...file.data.matchAll(re)].map((match) => ({ path: file.path, match: match[0] }))\n );\n if (matches.length > 0) violations.push({ label: message ?? `禁止パターン \\`${re.toString()}\\``, matches });\n }\n for (const rule of problemMarkdownFrontMatterLike.forbiddenTextsInCode ?? []) {\n const { pattern, message } = normalizeCodeRule(rule);\n const matches = sourceCodeWithoutCommentFiles\n .filter((file) => file.data.includes(pattern))\n .map((file) => ({ path: file.path, match: pattern }));\n if (matches.length > 0) violations.push({ label: message ?? `禁止文字列 \\`${pattern}\\``, matches });\n }\n\n if (violations.length > 0) {\n return {\n decisionCode: DecisionCode.FORBIDDEN_PATTERNS_IN_CODE_ERROR,\n feedbackMarkdown: `ソースコード中に禁止された文字列が含まれています。\nソースコードを修正してから再度提出してください。\n\n${violations.map(formatForbiddenRuleViolation).join('\\n')}\n`,\n };\n }\n\n const missingRequiredLabels: string[] = [];\n\n for (const rule of problemMarkdownFrontMatterLike.requiredRegExpsInCode ?? []) {\n const { pattern, message } = normalizeCodeRule(rule);\n const re = new RegExp(pattern);\n const isFound = sourceCodeWithoutCommentFiles.some((f) => re.test(f.data));\n if (!isFound) missingRequiredLabels.push(message ?? `\\`${re.toString()}\\``);\n }\n\n if (missingRequiredLabels.length > 0) {\n return {\n decisionCode: DecisionCode.REQUIRED_PATTERNS_IN_CODE_ERROR,\n feedbackMarkdown: `ソースコード中に必要な文字列が含まれていません。\nソースコードを修正してから再度提出してください。\n\n${missingRequiredLabels.map((label) => `- ${label}`).join('\\n')}\n`,\n };\n }\n\n return;\n}\n\nfunction formatForbiddenRuleViolation(violation: ForbiddenRuleViolation): string {\n const locations = [...new Set(violation.matches.map(({ path, match }) => ` - \\`${path}\\`: \\`${match}\\``))];\n return `- ${violation.label}\\n${locations.join('\\n')}`;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"judgeByStaticAnalysis.cjs","names":["fs","path","findLanguageDefinitionByPath","removeCommentsInSourceCode","DecisionCode","normalizeCodeRule"],"sources":["../../src/helpers/judgeByStaticAnalysis.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { DecisionCode } from '../types/decisionCode.js';\nimport type { ProblemMarkdownFrontMatter } from '../types/problem.js';\nimport { normalizeCodeRule } from '../types/problem.js';\nimport type { TestCaseResult } from '../types/testCaseResult.js';\n\nimport { findLanguageDefinitionByPath } from './findLanguageDefinitionByPath.js';\nimport { removeCommentsInSourceCode } from './removeCommentsInSourceCode.js';\n\ninterface ForbiddenRuleViolation {\n /** Learner-facing description of the rule; the raw pattern when the problem declares no message. */\n label: string;\n matches: { path: string; match: string }[];\n}\n\nexport async function judgeByStaticAnalysis(\n cwd: string,\n problemMarkdownFrontMatterLike: Pick<\n ProblemMarkdownFrontMatter,\n 'forbiddenRegExpsInCode' | 'forbiddenTextsInCode' | 'requiredRegExpsInCode' | 'requiredSubmissionFilePaths'\n >\n): Promise<Pick<TestCaseResult, 'decisionCode' | 'feedbackMarkdown'> | undefined> {\n const needsSourceCode =\n !!problemMarkdownFrontMatterLike.forbiddenRegExpsInCode?.length ||\n !!problemMarkdownFrontMatterLike.forbiddenTextsInCode?.length ||\n !!problemMarkdownFrontMatterLike.requiredRegExpsInCode?.length;\n if (!needsSourceCode && !problemMarkdownFrontMatterLike.requiredSubmissionFilePaths?.length) return;\n\n const filePathSet = new Set<string>();\n const sourceCodeWithoutCommentFiles: { path: string; data: string }[] = [];\n\n for (const dirent of await fs.promises.readdir(cwd, { withFileTypes: true, recursive: true })) {\n if (!dirent.isFile()) continue;\n\n const relativePath = path.relative(cwd, path.join(dirent.parentPath, dirent.name));\n filePathSet.add(relativePath);\n\n if (!needsSourceCode) continue;\n const languageDefinition = findLanguageDefinitionByPath(dirent.name);\n if (!languageDefinition) continue;\n\n const text = await fs.promises.readFile(path.join(dirent.parentPath, dirent.name), 'utf8');\n const isBinary = text.includes('\\uFFFD');\n if (isBinary) continue;\n\n sourceCodeWithoutCommentFiles.push({\n path: relativePath,\n data: languageDefinition.grammer ? removeCommentsInSourceCode(languageDefinition.grammer, text) : text,\n });\n }\n\n if (problemMarkdownFrontMatterLike.requiredSubmissionFilePaths) {\n const missingFilePaths = problemMarkdownFrontMatterLike.requiredSubmissionFilePaths\n .filter((p) => !filePathSet.has(p))\n .toSorted();\n\n if (missingFilePaths.length > 0) {\n return {\n decisionCode: DecisionCode.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,\n feedbackMarkdown: `ファイルが不足しています。\n次のファイルを追加してから再度提出してください。\n\n${missingFilePaths.map((p) => `- \\`${p}\\``).join('\\n')}\n`,\n };\n }\n }\n\n const violations: ForbiddenRuleViolation[] = [];\n\n for (const rule of problemMarkdownFrontMatterLike.forbiddenRegExpsInCode ?? []) {\n const { pattern, message } = normalizeCodeRule(rule);\n const re = new RegExp(pattern, 'g');\n const matches = sourceCodeWithoutCommentFiles.flatMap((file) =>\n [...file.data.matchAll(re)].map((match) => ({ path: file.path, match: match[0] }))\n );\n if (matches.length > 0) violations.push({ label: message ?? `禁止パターン \\`${re.toString()}\\``, matches });\n }\n for (const rule of problemMarkdownFrontMatterLike.forbiddenTextsInCode ?? []) {\n const { pattern, message } = normalizeCodeRule(rule);\n const matches = sourceCodeWithoutCommentFiles\n .filter((file) => file.data.includes(pattern))\n .map((file) => ({ path: file.path, match: pattern }));\n if (matches.length > 0) violations.push({ label: message ?? `禁止文字列 \\`${pattern}\\``, matches });\n }\n\n if (violations.length > 0) {\n return {\n decisionCode: DecisionCode.FORBIDDEN_PATTERNS_IN_CODE_ERROR,\n feedbackMarkdown: `ソースコード中に禁止された文字列が含まれています。\nソースコードを修正してから再度提出してください。\n\n${violations.map(formatForbiddenRuleViolation).join('\\n')}\n`,\n };\n }\n\n const missingRequiredLabels: string[] = [];\n\n for (const rule of problemMarkdownFrontMatterLike.requiredRegExpsInCode ?? []) {\n const { pattern, message } = normalizeCodeRule(rule);\n const re = new RegExp(pattern);\n const isFound = sourceCodeWithoutCommentFiles.some((f) => re.test(f.data));\n if (!isFound) missingRequiredLabels.push(message ?? `\\`${re.toString()}\\``);\n }\n\n if (missingRequiredLabels.length > 0) {\n return {\n decisionCode: DecisionCode.REQUIRED_PATTERNS_IN_CODE_ERROR,\n feedbackMarkdown: `ソースコード中に必要な文字列が含まれていません。\nソースコードを修正してから再度提出してください。\n\n${missingRequiredLabels.map((label) => `- ${label}`).join('\\n')}\n`,\n };\n }\n\n return;\n}\n\nfunction formatForbiddenRuleViolation(violation: ForbiddenRuleViolation): string {\n const locations = [...new Set(violation.matches.map(({ path, match }) => ` - \\`${path}\\`: \\`${match}\\``))];\n return `- ${violation.label}\\n${locations.join('\\n')}`;\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import{removeCommentsInSourceCode as e}from"./removeCommentsInSourceCode.js";import{normalizeCodeRule as t}from"../types/problem.js";import{findLanguageDefinitionByPath as n}from"./findLanguageDefinitionByPath.js";import{DecisionCode as r}from"../types/decisionCode.js";import i from"node:fs";import a from"node:path";async function o(o,c){let l=!!c.forbiddenRegExpsInCode?.length||!!c.forbiddenTextsInCode?.length||!!c.requiredRegExpsInCode?.length;if(!l&&!c.requiredSubmissionFilePaths?.length)return;let u=new Set,d=[];for(let t of await i.promises.readdir(o,{withFileTypes:!0,recursive:!0})){if(!t.isFile())continue;let r=a.relative(o,a.join(t.parentPath,t.name));if(u.add(r),!l)continue;let s=n(t.name);if(!s)continue;let c=await i.promises.readFile(a.join(t.parentPath,t.name),`utf8`);c.includes(`�`)||d.push({path:r,data:s.grammer?e(s.grammer,c):c})}if(c.requiredSubmissionFilePaths){let e=c.requiredSubmissionFilePaths.filter(e=>!u.has(e)).toSorted();if(e.length>0)return{decisionCode:r.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,feedbackMarkdown:`ファイルが不足しています。
1
+ import{removeCommentsInSourceCode as e}from"./removeCommentsInSourceCode.js";import{normalizeCodeRule as t}from"../types/problem.js";import{findLanguageDefinitionByPath as n}from"./findLanguageDefinitionByPath.js";import{DecisionCode as r}from"../types/decisionCode.js";import i from"node:fs";import a from"node:path";async function o(o,c){let l=!!c.forbiddenRegExpsInCode?.length||!!c.forbiddenTextsInCode?.length||!!c.requiredRegExpsInCode?.length;if(!l&&!c.requiredSubmissionFilePaths?.length)return;let u=/* @__PURE__ */ new Set,d=[];for(let t of await i.promises.readdir(o,{withFileTypes:!0,recursive:!0})){if(!t.isFile())continue;let r=a.relative(o,a.join(t.parentPath,t.name));if(u.add(r),!l)continue;let s=n(t.name);if(!s)continue;let c=await i.promises.readFile(a.join(t.parentPath,t.name),`utf8`);c.includes(`�`)||d.push({path:r,data:s.grammer?e(s.grammer,c):c})}if(c.requiredSubmissionFilePaths){let e=c.requiredSubmissionFilePaths.filter(e=>!u.has(e)).toSorted();if(e.length>0)return{decisionCode:r.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,feedbackMarkdown:`ファイルが不足しています。
2
2
  次のファイルを追加してから再度提出してください。
3
3
 
4
4
  ${e.map(e=>`- \`${e}\``).join(`
@@ -1 +1 @@
1
- {"version":3,"file":"judgeByStaticAnalysis.js","names":[],"sources":["../../src/helpers/judgeByStaticAnalysis.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { DecisionCode } from '../types/decisionCode.js';\nimport type { ProblemMarkdownFrontMatter } from '../types/problem.js';\nimport { normalizeCodeRule } from '../types/problem.js';\nimport type { TestCaseResult } from '../types/testCaseResult.js';\n\nimport { findLanguageDefinitionByPath } from './findLanguageDefinitionByPath.js';\nimport { removeCommentsInSourceCode } from './removeCommentsInSourceCode.js';\n\ninterface ForbiddenRuleViolation {\n /** Learner-facing description of the rule; the raw pattern when the problem declares no message. */\n label: string;\n matches: { path: string; match: string }[];\n}\n\nexport async function judgeByStaticAnalysis(\n cwd: string,\n problemMarkdownFrontMatterLike: Pick<\n ProblemMarkdownFrontMatter,\n 'forbiddenRegExpsInCode' | 'forbiddenTextsInCode' | 'requiredRegExpsInCode' | 'requiredSubmissionFilePaths'\n >\n): Promise<Pick<TestCaseResult, 'decisionCode' | 'feedbackMarkdown'> | undefined> {\n const needsSourceCode =\n !!problemMarkdownFrontMatterLike.forbiddenRegExpsInCode?.length ||\n !!problemMarkdownFrontMatterLike.forbiddenTextsInCode?.length ||\n !!problemMarkdownFrontMatterLike.requiredRegExpsInCode?.length;\n if (!needsSourceCode && !problemMarkdownFrontMatterLike.requiredSubmissionFilePaths?.length) return;\n\n const filePathSet = new Set<string>();\n const sourceCodeWithoutCommentFiles: { path: string; data: string }[] = [];\n\n for (const dirent of await fs.promises.readdir(cwd, { withFileTypes: true, recursive: true })) {\n if (!dirent.isFile()) continue;\n\n const relativePath = path.relative(cwd, path.join(dirent.parentPath, dirent.name));\n filePathSet.add(relativePath);\n\n if (!needsSourceCode) continue;\n const languageDefinition = findLanguageDefinitionByPath(dirent.name);\n if (!languageDefinition) continue;\n\n const text = await fs.promises.readFile(path.join(dirent.parentPath, dirent.name), 'utf8');\n const isBinary = text.includes('\\uFFFD');\n if (isBinary) continue;\n\n sourceCodeWithoutCommentFiles.push({\n path: relativePath,\n data: languageDefinition.grammer ? removeCommentsInSourceCode(languageDefinition.grammer, text) : text,\n });\n }\n\n if (problemMarkdownFrontMatterLike.requiredSubmissionFilePaths) {\n const missingFilePaths = problemMarkdownFrontMatterLike.requiredSubmissionFilePaths\n .filter((p) => !filePathSet.has(p))\n .toSorted();\n\n if (missingFilePaths.length > 0) {\n return {\n decisionCode: DecisionCode.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,\n feedbackMarkdown: `ファイルが不足しています。\n次のファイルを追加してから再度提出してください。\n\n${missingFilePaths.map((p) => `- \\`${p}\\``).join('\\n')}\n`,\n };\n }\n }\n\n const violations: ForbiddenRuleViolation[] = [];\n\n for (const rule of problemMarkdownFrontMatterLike.forbiddenRegExpsInCode ?? []) {\n const { pattern, message } = normalizeCodeRule(rule);\n const re = new RegExp(pattern, 'g');\n const matches = sourceCodeWithoutCommentFiles.flatMap((file) =>\n [...file.data.matchAll(re)].map((match) => ({ path: file.path, match: match[0] }))\n );\n if (matches.length > 0) violations.push({ label: message ?? `禁止パターン \\`${re.toString()}\\``, matches });\n }\n for (const rule of problemMarkdownFrontMatterLike.forbiddenTextsInCode ?? []) {\n const { pattern, message } = normalizeCodeRule(rule);\n const matches = sourceCodeWithoutCommentFiles\n .filter((file) => file.data.includes(pattern))\n .map((file) => ({ path: file.path, match: pattern }));\n if (matches.length > 0) violations.push({ label: message ?? `禁止文字列 \\`${pattern}\\``, matches });\n }\n\n if (violations.length > 0) {\n return {\n decisionCode: DecisionCode.FORBIDDEN_PATTERNS_IN_CODE_ERROR,\n feedbackMarkdown: `ソースコード中に禁止された文字列が含まれています。\nソースコードを修正してから再度提出してください。\n\n${violations.map(formatForbiddenRuleViolation).join('\\n')}\n`,\n };\n }\n\n const missingRequiredLabels: string[] = [];\n\n for (const rule of problemMarkdownFrontMatterLike.requiredRegExpsInCode ?? []) {\n const { pattern, message } = normalizeCodeRule(rule);\n const re = new RegExp(pattern);\n const isFound = sourceCodeWithoutCommentFiles.some((f) => re.test(f.data));\n if (!isFound) missingRequiredLabels.push(message ?? `\\`${re.toString()}\\``);\n }\n\n if (missingRequiredLabels.length > 0) {\n return {\n decisionCode: DecisionCode.REQUIRED_PATTERNS_IN_CODE_ERROR,\n feedbackMarkdown: `ソースコード中に必要な文字列が含まれていません。\nソースコードを修正してから再度提出してください。\n\n${missingRequiredLabels.map((label) => `- ${label}`).join('\\n')}\n`,\n };\n }\n\n return;\n}\n\nfunction formatForbiddenRuleViolation(violation: ForbiddenRuleViolation): string {\n const locations = [...new Set(violation.matches.map(({ path, match }) => ` - \\`${path}\\`: \\`${match}\\``))];\n return `- ${violation.label}\\n${locations.join('\\n')}`;\n}\n"],"mappings":"8TAiBA,eAAsB,EACpB,EACA,EAIgF,CAChF,IAAM,EACJ,CAAC,CAAC,EAA+B,wBAAwB,QACzD,CAAC,CAAC,EAA+B,sBAAsB,QACvD,CAAC,CAAC,EAA+B,uBAAuB,OAC1D,GAAI,CAAC,GAAmB,CAAC,EAA+B,6BAA6B,OAAQ,OAE7F,IAAM,EAAc,IAAI,IAClB,EAAkE,CAAC,EAEzE,IAAK,IAAM,KAAU,MAAM,EAAG,SAAS,QAAQ,EAAK,CAAE,cAAe,GAAM,UAAW,EAAK,CAAC,EAAG,CAC7F,GAAI,CAAC,EAAO,OAAO,EAAG,SAEtB,IAAM,EAAe,EAAK,SAAS,EAAK,EAAK,KAAK,EAAO,WAAY,EAAO,IAAI,CAAC,EAGjF,GAFA,EAAY,IAAI,CAAY,EAExB,CAAC,EAAiB,SACtB,IAAM,EAAqB,EAA6B,EAAO,IAAI,EACnE,GAAI,CAAC,EAAoB,SAEzB,IAAM,EAAO,MAAM,EAAG,SAAS,SAAS,EAAK,KAAK,EAAO,WAAY,EAAO,IAAI,EAAG,MAAM,EACxE,EAAK,SAAS,GACpB,GAEX,EAA8B,KAAK,CACjC,KAAM,EACN,KAAM,EAAmB,QAAU,EAA2B,EAAmB,QAAS,CAAI,EAAI,CACpG,CAAC,CACH,CAEA,GAAI,EAA+B,4BAA6B,CAC9D,IAAM,EAAmB,EAA+B,4BACrD,OAAQ,GAAM,CAAC,EAAY,IAAI,CAAC,CAAC,CAAC,CAClC,SAAS,EAEZ,GAAI,EAAiB,OAAS,EAC5B,MAAO,CACL,aAAc,EAAa,uCAC3B,iBAAkB;;;EAGxB,EAAiB,IAAK,GAAM,OAAO,EAAE,GAAG,CAAC,CAAC,KAAK;CAAI,EAAE;CAEjD,CAEJ,CAEA,IAAM,EAAuC,CAAC,EAE9C,IAAK,IAAM,KAAQ,EAA+B,wBAA0B,CAAC,EAAG,CAC9E,GAAM,CAAE,UAAS,WAAY,EAAkB,CAAI,EAC7C,EAAK,IAAI,OAAO,EAAS,GAAG,EAC5B,EAAU,EAA8B,QAAS,GACrD,CAAC,GAAG,EAAK,KAAK,SAAS,CAAE,CAAC,CAAC,CAAC,IAAK,IAAW,CAAE,KAAM,EAAK,KAAM,MAAO,EAAM,EAAG,EAAE,CACnF,EACI,EAAQ,OAAS,GAAG,EAAW,KAAK,CAAE,MAAO,GAAW,YAAY,EAAG,SAAS,EAAE,IAAK,SAAQ,CAAC,CACtG,CACA,IAAK,IAAM,KAAQ,EAA+B,sBAAwB,CAAC,EAAG,CAC5E,GAAM,CAAE,UAAS,WAAY,EAAkB,CAAI,EAC7C,EAAU,EACb,OAAQ,GAAS,EAAK,KAAK,SAAS,CAAO,CAAC,CAAC,CAC7C,IAAK,IAAU,CAAE,KAAM,EAAK,KAAM,MAAO,CAAQ,EAAE,EAClD,EAAQ,OAAS,GAAG,EAAW,KAAK,CAAE,MAAO,GAAW,WAAW,EAAQ,IAAK,SAAQ,CAAC,CAC/F,CAEA,GAAI,EAAW,OAAS,EACtB,MAAO,CACL,aAAc,EAAa,iCAC3B,iBAAkB;;;EAGtB,EAAW,IAAI,CAA4B,CAAC,CAAC,KAAK;CAAI,EAAE;CAEtD,EAGF,IAAM,EAAkC,CAAC,EAEzC,IAAK,IAAM,KAAQ,EAA+B,uBAAyB,CAAC,EAAG,CAC7E,GAAM,CAAE,UAAS,WAAY,EAAkB,CAAI,EAC7C,EAAK,IAAI,OAAO,CAAO,EACb,EAA8B,KAAM,GAAM,EAAG,KAAK,EAAE,IAAI,CAC7D,GAAG,EAAsB,KAAK,GAAW,KAAK,EAAG,SAAS,EAAE,GAAG,CAC5E,CAEA,GAAI,EAAsB,OAAS,EACjC,MAAO,CACL,aAAc,EAAa,gCAC3B,iBAAkB;;;EAGtB,EAAsB,IAAK,GAAU,KAAK,GAAO,CAAC,CAAC,KAAK;CAAI,EAAE;CAE5D,CAIJ,CAEA,SAAS,EAA6B,EAA2C,CAC/E,IAAM,EAAY,CAAC,GAAG,IAAI,IAAI,EAAU,QAAQ,KAAK,CAAE,OAAM,WAAY,SAAS,EAAK,QAAQ,EAAM,GAAG,CAAC,CAAC,EAC1G,MAAO,KAAK,EAAU,MAAM,IAAI,EAAU,KAAK;CAAI,GACrD"}
1
+ {"version":3,"file":"judgeByStaticAnalysis.js","names":[],"sources":["../../src/helpers/judgeByStaticAnalysis.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { DecisionCode } from '../types/decisionCode.js';\nimport type { ProblemMarkdownFrontMatter } from '../types/problem.js';\nimport { normalizeCodeRule } from '../types/problem.js';\nimport type { TestCaseResult } from '../types/testCaseResult.js';\n\nimport { findLanguageDefinitionByPath } from './findLanguageDefinitionByPath.js';\nimport { removeCommentsInSourceCode } from './removeCommentsInSourceCode.js';\n\ninterface ForbiddenRuleViolation {\n /** Learner-facing description of the rule; the raw pattern when the problem declares no message. */\n label: string;\n matches: { path: string; match: string }[];\n}\n\nexport async function judgeByStaticAnalysis(\n cwd: string,\n problemMarkdownFrontMatterLike: Pick<\n ProblemMarkdownFrontMatter,\n 'forbiddenRegExpsInCode' | 'forbiddenTextsInCode' | 'requiredRegExpsInCode' | 'requiredSubmissionFilePaths'\n >\n): Promise<Pick<TestCaseResult, 'decisionCode' | 'feedbackMarkdown'> | undefined> {\n const needsSourceCode =\n !!problemMarkdownFrontMatterLike.forbiddenRegExpsInCode?.length ||\n !!problemMarkdownFrontMatterLike.forbiddenTextsInCode?.length ||\n !!problemMarkdownFrontMatterLike.requiredRegExpsInCode?.length;\n if (!needsSourceCode && !problemMarkdownFrontMatterLike.requiredSubmissionFilePaths?.length) return;\n\n const filePathSet = new Set<string>();\n const sourceCodeWithoutCommentFiles: { path: string; data: string }[] = [];\n\n for (const dirent of await fs.promises.readdir(cwd, { withFileTypes: true, recursive: true })) {\n if (!dirent.isFile()) continue;\n\n const relativePath = path.relative(cwd, path.join(dirent.parentPath, dirent.name));\n filePathSet.add(relativePath);\n\n if (!needsSourceCode) continue;\n const languageDefinition = findLanguageDefinitionByPath(dirent.name);\n if (!languageDefinition) continue;\n\n const text = await fs.promises.readFile(path.join(dirent.parentPath, dirent.name), 'utf8');\n const isBinary = text.includes('\\uFFFD');\n if (isBinary) continue;\n\n sourceCodeWithoutCommentFiles.push({\n path: relativePath,\n data: languageDefinition.grammer ? removeCommentsInSourceCode(languageDefinition.grammer, text) : text,\n });\n }\n\n if (problemMarkdownFrontMatterLike.requiredSubmissionFilePaths) {\n const missingFilePaths = problemMarkdownFrontMatterLike.requiredSubmissionFilePaths\n .filter((p) => !filePathSet.has(p))\n .toSorted();\n\n if (missingFilePaths.length > 0) {\n return {\n decisionCode: DecisionCode.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,\n feedbackMarkdown: `ファイルが不足しています。\n次のファイルを追加してから再度提出してください。\n\n${missingFilePaths.map((p) => `- \\`${p}\\``).join('\\n')}\n`,\n };\n }\n }\n\n const violations: ForbiddenRuleViolation[] = [];\n\n for (const rule of problemMarkdownFrontMatterLike.forbiddenRegExpsInCode ?? []) {\n const { pattern, message } = normalizeCodeRule(rule);\n const re = new RegExp(pattern, 'g');\n const matches = sourceCodeWithoutCommentFiles.flatMap((file) =>\n [...file.data.matchAll(re)].map((match) => ({ path: file.path, match: match[0] }))\n );\n if (matches.length > 0) violations.push({ label: message ?? `禁止パターン \\`${re.toString()}\\``, matches });\n }\n for (const rule of problemMarkdownFrontMatterLike.forbiddenTextsInCode ?? []) {\n const { pattern, message } = normalizeCodeRule(rule);\n const matches = sourceCodeWithoutCommentFiles\n .filter((file) => file.data.includes(pattern))\n .map((file) => ({ path: file.path, match: pattern }));\n if (matches.length > 0) violations.push({ label: message ?? `禁止文字列 \\`${pattern}\\``, matches });\n }\n\n if (violations.length > 0) {\n return {\n decisionCode: DecisionCode.FORBIDDEN_PATTERNS_IN_CODE_ERROR,\n feedbackMarkdown: `ソースコード中に禁止された文字列が含まれています。\nソースコードを修正してから再度提出してください。\n\n${violations.map(formatForbiddenRuleViolation).join('\\n')}\n`,\n };\n }\n\n const missingRequiredLabels: string[] = [];\n\n for (const rule of problemMarkdownFrontMatterLike.requiredRegExpsInCode ?? []) {\n const { pattern, message } = normalizeCodeRule(rule);\n const re = new RegExp(pattern);\n const isFound = sourceCodeWithoutCommentFiles.some((f) => re.test(f.data));\n if (!isFound) missingRequiredLabels.push(message ?? `\\`${re.toString()}\\``);\n }\n\n if (missingRequiredLabels.length > 0) {\n return {\n decisionCode: DecisionCode.REQUIRED_PATTERNS_IN_CODE_ERROR,\n feedbackMarkdown: `ソースコード中に必要な文字列が含まれていません。\nソースコードを修正してから再度提出してください。\n\n${missingRequiredLabels.map((label) => `- ${label}`).join('\\n')}\n`,\n };\n }\n\n return;\n}\n\nfunction formatForbiddenRuleViolation(violation: ForbiddenRuleViolation): string {\n const locations = [...new Set(violation.matches.map(({ path, match }) => ` - \\`${path}\\`: \\`${match}\\``))];\n return `- ${violation.label}\\n${locations.join('\\n')}`;\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- "use strict";const e=require("../_virtual/_rolldown/runtime.cjs");let t=require("node:fs");t=e.__toESM(t,1);let n=require("node:path");n=e.__toESM(n,1);const r=`_shared`;async function i(e){if(!t.default.existsSync(e))return[];let i=new Set,a=new Map,o=new Map,s=new Map,c=new Map;for(let r of await t.default.promises.readdir(e,{withFileTypes:!0}))if(r.isFile()){let{ext:e,name:s}=n.default.parse(r.name);if(e!==`.in`&&e!==`.out`)continue;let c=await t.default.promises.readFile(n.default.join(r.parentPath,r.name),`utf8`);i.add(s),e===`.in`&&a.set(s,c),e===`.out`&&o.set(s,c)}else if(r.isDirectory()){let{ext:e,name:t}=n.default.parse(r.name);if(e!==`.fin`&&e!==`.fout`)continue;i.add(t),e===`.fin`&&s.set(t,n.default.join(r.parentPath,r.name)),e===`.fout`&&c.set(t,n.default.join(r.parentPath,r.name))}let l=[...i].filter(e=>e!==r).toSorted().map(e=>({id:e,input:a.get(e),output:o.get(e),fileInputPath:s.get(e),fileOutputPath:c.get(e)}));return i.has(r)&&(l.shared={fileInputPath:s.get(r)}),l}exports.readTestCases=i;
1
+ "use strict";const e=require("../_virtual/_rolldown/runtime.cjs");let t=require("node:fs");t=e.__toESM(t,1);let n=require("node:path");n=e.__toESM(n,1);const r=`_shared`;async function i(e){if(!t.default.existsSync(e))return[];let i=/* @__PURE__ */ new Set,a=/* @__PURE__ */ new Map,o=/* @__PURE__ */ new Map,s=/* @__PURE__ */ new Map,c=/* @__PURE__ */ new Map;for(let r of await t.default.promises.readdir(e,{withFileTypes:!0}))if(r.isFile()){let{ext:e,name:s}=n.default.parse(r.name);if(e!==`.in`&&e!==`.out`)continue;let c=await t.default.promises.readFile(n.default.join(r.parentPath,r.name),`utf8`);i.add(s),e===`.in`&&a.set(s,c),e===`.out`&&o.set(s,c)}else if(r.isDirectory()){let{ext:e,name:t}=n.default.parse(r.name);if(e!==`.fin`&&e!==`.fout`)continue;i.add(t),e===`.fin`&&s.set(t,n.default.join(r.parentPath,r.name)),e===`.fout`&&c.set(t,n.default.join(r.parentPath,r.name))}let l=[...i].filter(e=>e!==r).toSorted().map(e=>({id:e,input:a.get(e),output:o.get(e),fileInputPath:s.get(e),fileOutputPath:c.get(e)}));return i.has(r)&&(l.shared={fileInputPath:s.get(r)}),l}exports.readTestCases=i;
2
2
  //# sourceMappingURL=readTestCases.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"readTestCases.cjs","names":["fs","path"],"sources":["../../src/helpers/readTestCases.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\n\nconst SHARED_TEST_CASE_NAME = '_shared';\n\n/**\n * Read the test cases of a `test_cases/` directory. A test case id is the name of every\n * `<id>.in` / `<id>.out` file and `<id>.fin/` / `<id>.fout/` directory; any of them may be absent,\n * and what an absent part means is up to the harness (the presets document their rules).\n * Other entries (e.g. `.json` configuration read by a custom `judge.ts`) are ignored.\n */\nexport async function readTestCases(directory: string): Promise<\n { id: string; input?: string; output?: string; fileInputPath?: string; fileOutputPath?: string }[] & {\n shared?: { fileInputPath?: string };\n }\n> {\n if (!fs.existsSync(directory)) return [];\n\n const idSet = new Set<string>();\n const idToInput = new Map<string, string>();\n const idToOutput = new Map<string, string>();\n const idToFileInputPath = new Map<string, string>();\n const idToFileOutputPath = new Map<string, string>();\n\n for (const dirent of await fs.promises.readdir(directory, { withFileTypes: true })) {\n if (dirent.isFile()) {\n const { ext, name } = path.parse(dirent.name);\n if (ext !== '.in' && ext !== '.out') continue;\n\n const text = await fs.promises.readFile(path.join(dirent.parentPath, dirent.name), 'utf8');\n\n idSet.add(name);\n if (ext === '.in') idToInput.set(name, text);\n if (ext === '.out') idToOutput.set(name, text);\n } else if (dirent.isDirectory()) {\n const { ext, name } = path.parse(dirent.name);\n if (ext !== '.fin' && ext !== '.fout') continue;\n\n idSet.add(name);\n if (ext === '.fin') idToFileInputPath.set(name, path.join(dirent.parentPath, dirent.name));\n if (ext === '.fout') idToFileOutputPath.set(name, path.join(dirent.parentPath, dirent.name));\n }\n }\n\n const testCases: Awaited<ReturnType<typeof readTestCases>> = [...idSet]\n .filter((id) => id !== SHARED_TEST_CASE_NAME)\n .toSorted()\n .map((id) => ({\n id,\n input: idToInput.get(id),\n output: idToOutput.get(id),\n fileInputPath: idToFileInputPath.get(id),\n fileOutputPath: idToFileOutputPath.get(id),\n }));\n\n if (idSet.has(SHARED_TEST_CASE_NAME)) {\n testCases.shared = { fileInputPath: idToFileInputPath.get(SHARED_TEST_CASE_NAME) };\n }\n\n return testCases;\n}\n"],"mappings":"wJAGA,MAAM,EAAwB,UAQ9B,eAAsB,EAAc,EAIlC,CACA,GAAI,CAACA,EAAAA,QAAG,WAAW,CAAS,EAAG,MAAO,CAAC,EAEvC,IAAM,EAAQ,IAAI,IACZ,EAAY,IAAI,IAChB,EAAa,IAAI,IACjB,EAAoB,IAAI,IACxB,EAAqB,IAAI,IAE/B,IAAK,IAAM,KAAU,MAAMA,EAAAA,QAAG,SAAS,QAAQ,EAAW,CAAE,cAAe,EAAK,CAAC,EAC/E,GAAI,EAAO,OAAO,EAAG,CACnB,GAAM,CAAE,MAAK,QAASC,EAAAA,QAAK,MAAM,EAAO,IAAI,EAC5C,GAAI,IAAQ,OAAS,IAAQ,OAAQ,SAErC,IAAM,EAAO,MAAMD,EAAAA,QAAG,SAAS,SAASC,EAAAA,QAAK,KAAK,EAAO,WAAY,EAAO,IAAI,EAAG,MAAM,EAEzF,EAAM,IAAI,CAAI,EACV,IAAQ,OAAO,EAAU,IAAI,EAAM,CAAI,EACvC,IAAQ,QAAQ,EAAW,IAAI,EAAM,CAAI,CAC/C,MAAO,GAAI,EAAO,YAAY,EAAG,CAC/B,GAAM,CAAE,MAAK,QAASA,EAAAA,QAAK,MAAM,EAAO,IAAI,EAC5C,GAAI,IAAQ,QAAU,IAAQ,QAAS,SAEvC,EAAM,IAAI,CAAI,EACV,IAAQ,QAAQ,EAAkB,IAAI,EAAMA,EAAAA,QAAK,KAAK,EAAO,WAAY,EAAO,IAAI,CAAC,EACrF,IAAQ,SAAS,EAAmB,IAAI,EAAMA,EAAAA,QAAK,KAAK,EAAO,WAAY,EAAO,IAAI,CAAC,CAC7F,CAGF,IAAM,EAAuD,CAAC,GAAG,CAAK,CAAC,CACpE,OAAQ,GAAO,IAAO,CAAqB,CAAC,CAC5C,SAAS,CAAC,CACV,IAAK,IAAQ,CACZ,KACA,MAAO,EAAU,IAAI,CAAE,EACvB,OAAQ,EAAW,IAAI,CAAE,EACzB,cAAe,EAAkB,IAAI,CAAE,EACvC,eAAgB,EAAmB,IAAI,CAAE,CAC3C,EAAE,EAMJ,OAJI,EAAM,IAAI,CAAqB,IACjC,EAAU,OAAS,CAAE,cAAe,EAAkB,IAAI,CAAqB,CAAE,GAG5E,CACT"}
1
+ {"version":3,"file":"readTestCases.cjs","names":["fs","path"],"sources":["../../src/helpers/readTestCases.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\n\nconst SHARED_TEST_CASE_NAME = '_shared';\n\n/**\n * Read the test cases of a `test_cases/` directory. A test case id is the name of every\n * `<id>.in` / `<id>.out` file and `<id>.fin/` / `<id>.fout/` directory; any of them may be absent,\n * and what an absent part means is up to the harness (the presets document their rules).\n * Other entries (e.g. `.json` configuration read by a custom `judge.ts`) are ignored.\n */\nexport async function readTestCases(directory: string): Promise<\n { id: string; input?: string; output?: string; fileInputPath?: string; fileOutputPath?: string }[] & {\n shared?: { fileInputPath?: string };\n }\n> {\n if (!fs.existsSync(directory)) return [];\n\n const idSet = new Set<string>();\n const idToInput = new Map<string, string>();\n const idToOutput = new Map<string, string>();\n const idToFileInputPath = new Map<string, string>();\n const idToFileOutputPath = new Map<string, string>();\n\n for (const dirent of await fs.promises.readdir(directory, { withFileTypes: true })) {\n if (dirent.isFile()) {\n const { ext, name } = path.parse(dirent.name);\n if (ext !== '.in' && ext !== '.out') continue;\n\n const text = await fs.promises.readFile(path.join(dirent.parentPath, dirent.name), 'utf8');\n\n idSet.add(name);\n if (ext === '.in') idToInput.set(name, text);\n if (ext === '.out') idToOutput.set(name, text);\n } else if (dirent.isDirectory()) {\n const { ext, name } = path.parse(dirent.name);\n if (ext !== '.fin' && ext !== '.fout') continue;\n\n idSet.add(name);\n if (ext === '.fin') idToFileInputPath.set(name, path.join(dirent.parentPath, dirent.name));\n if (ext === '.fout') idToFileOutputPath.set(name, path.join(dirent.parentPath, dirent.name));\n }\n }\n\n const testCases: Awaited<ReturnType<typeof readTestCases>> = [...idSet]\n .filter((id) => id !== SHARED_TEST_CASE_NAME)\n .toSorted()\n .map((id) => ({\n id,\n input: idToInput.get(id),\n output: idToOutput.get(id),\n fileInputPath: idToFileInputPath.get(id),\n fileOutputPath: idToFileOutputPath.get(id),\n }));\n\n if (idSet.has(SHARED_TEST_CASE_NAME)) {\n testCases.shared = { fileInputPath: idToFileInputPath.get(SHARED_TEST_CASE_NAME) };\n }\n\n return testCases;\n}\n"],"mappings":"wJAGA,MAAM,EAAwB,UAQ9B,eAAsB,EAAc,EAIlC,CACA,GAAI,CAACA,EAAAA,QAAG,WAAW,CAAS,EAAG,MAAO,CAAC,EAEvC,IAAM,kBAAQ,IAAI,IACZ,kBAAY,IAAI,IAChB,kBAAa,IAAI,IACjB,kBAAoB,IAAI,IACxB,kBAAqB,IAAI,IAE/B,IAAK,IAAM,KAAU,MAAMA,EAAAA,QAAG,SAAS,QAAQ,EAAW,CAAE,cAAe,EAAK,CAAC,EAC/E,GAAI,EAAO,OAAO,EAAG,CACnB,GAAM,CAAE,MAAK,QAASC,EAAAA,QAAK,MAAM,EAAO,IAAI,EAC5C,GAAI,IAAQ,OAAS,IAAQ,OAAQ,SAErC,IAAM,EAAO,MAAMD,EAAAA,QAAG,SAAS,SAASC,EAAAA,QAAK,KAAK,EAAO,WAAY,EAAO,IAAI,EAAG,MAAM,EAEzF,EAAM,IAAI,CAAI,EACV,IAAQ,OAAO,EAAU,IAAI,EAAM,CAAI,EACvC,IAAQ,QAAQ,EAAW,IAAI,EAAM,CAAI,CAC/C,MAAO,GAAI,EAAO,YAAY,EAAG,CAC/B,GAAM,CAAE,MAAK,QAASA,EAAAA,QAAK,MAAM,EAAO,IAAI,EAC5C,GAAI,IAAQ,QAAU,IAAQ,QAAS,SAEvC,EAAM,IAAI,CAAI,EACV,IAAQ,QAAQ,EAAkB,IAAI,EAAMA,EAAAA,QAAK,KAAK,EAAO,WAAY,EAAO,IAAI,CAAC,EACrF,IAAQ,SAAS,EAAmB,IAAI,EAAMA,EAAAA,QAAK,KAAK,EAAO,WAAY,EAAO,IAAI,CAAC,CAC7F,CAGF,IAAM,EAAuD,CAAC,GAAG,CAAK,CAAC,CACpE,OAAQ,GAAO,IAAO,CAAqB,CAAC,CAC5C,SAAS,CAAC,CACV,IAAK,IAAQ,CACZ,KACA,MAAO,EAAU,IAAI,CAAE,EACvB,OAAQ,EAAW,IAAI,CAAE,EACzB,cAAe,EAAkB,IAAI,CAAE,EACvC,eAAgB,EAAmB,IAAI,CAAE,CAC3C,EAAE,EAMJ,OAJI,EAAM,IAAI,CAAqB,IACjC,EAAU,OAAS,CAAE,cAAe,EAAkB,IAAI,CAAqB,CAAE,GAG5E,CACT"}
@@ -1,2 +1,2 @@
1
- import e from"node:fs";import t from"node:path";const n=`_shared`;async function r(r){if(!e.existsSync(r))return[];let i=new Set,a=new Map,o=new Map,s=new Map,c=new Map;for(let n of await e.promises.readdir(r,{withFileTypes:!0}))if(n.isFile()){let{ext:r,name:s}=t.parse(n.name);if(r!==`.in`&&r!==`.out`)continue;let c=await e.promises.readFile(t.join(n.parentPath,n.name),`utf8`);i.add(s),r===`.in`&&a.set(s,c),r===`.out`&&o.set(s,c)}else if(n.isDirectory()){let{ext:e,name:r}=t.parse(n.name);if(e!==`.fin`&&e!==`.fout`)continue;i.add(r),e===`.fin`&&s.set(r,t.join(n.parentPath,n.name)),e===`.fout`&&c.set(r,t.join(n.parentPath,n.name))}let l=[...i].filter(e=>e!==n).toSorted().map(e=>({id:e,input:a.get(e),output:o.get(e),fileInputPath:s.get(e),fileOutputPath:c.get(e)}));return i.has(n)&&(l.shared={fileInputPath:s.get(n)}),l}export{r as readTestCases};
1
+ import e from"node:fs";import t from"node:path";const n=`_shared`;async function r(r){if(!e.existsSync(r))return[];let i=/* @__PURE__ */ new Set,a=/* @__PURE__ */ new Map,o=/* @__PURE__ */ new Map,s=/* @__PURE__ */ new Map,c=/* @__PURE__ */ new Map;for(let n of await e.promises.readdir(r,{withFileTypes:!0}))if(n.isFile()){let{ext:r,name:s}=t.parse(n.name);if(r!==`.in`&&r!==`.out`)continue;let c=await e.promises.readFile(t.join(n.parentPath,n.name),`utf8`);i.add(s),r===`.in`&&a.set(s,c),r===`.out`&&o.set(s,c)}else if(n.isDirectory()){let{ext:e,name:r}=t.parse(n.name);if(e!==`.fin`&&e!==`.fout`)continue;i.add(r),e===`.fin`&&s.set(r,t.join(n.parentPath,n.name)),e===`.fout`&&c.set(r,t.join(n.parentPath,n.name))}let l=[...i].filter(e=>e!==n).toSorted().map(e=>({id:e,input:a.get(e),output:o.get(e),fileInputPath:s.get(e),fileOutputPath:c.get(e)}));return i.has(n)&&(l.shared={fileInputPath:s.get(n)}),l}export{r as readTestCases};
2
2
  //# sourceMappingURL=readTestCases.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"readTestCases.js","names":[],"sources":["../../src/helpers/readTestCases.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\n\nconst SHARED_TEST_CASE_NAME = '_shared';\n\n/**\n * Read the test cases of a `test_cases/` directory. A test case id is the name of every\n * `<id>.in` / `<id>.out` file and `<id>.fin/` / `<id>.fout/` directory; any of them may be absent,\n * and what an absent part means is up to the harness (the presets document their rules).\n * Other entries (e.g. `.json` configuration read by a custom `judge.ts`) are ignored.\n */\nexport async function readTestCases(directory: string): Promise<\n { id: string; input?: string; output?: string; fileInputPath?: string; fileOutputPath?: string }[] & {\n shared?: { fileInputPath?: string };\n }\n> {\n if (!fs.existsSync(directory)) return [];\n\n const idSet = new Set<string>();\n const idToInput = new Map<string, string>();\n const idToOutput = new Map<string, string>();\n const idToFileInputPath = new Map<string, string>();\n const idToFileOutputPath = new Map<string, string>();\n\n for (const dirent of await fs.promises.readdir(directory, { withFileTypes: true })) {\n if (dirent.isFile()) {\n const { ext, name } = path.parse(dirent.name);\n if (ext !== '.in' && ext !== '.out') continue;\n\n const text = await fs.promises.readFile(path.join(dirent.parentPath, dirent.name), 'utf8');\n\n idSet.add(name);\n if (ext === '.in') idToInput.set(name, text);\n if (ext === '.out') idToOutput.set(name, text);\n } else if (dirent.isDirectory()) {\n const { ext, name } = path.parse(dirent.name);\n if (ext !== '.fin' && ext !== '.fout') continue;\n\n idSet.add(name);\n if (ext === '.fin') idToFileInputPath.set(name, path.join(dirent.parentPath, dirent.name));\n if (ext === '.fout') idToFileOutputPath.set(name, path.join(dirent.parentPath, dirent.name));\n }\n }\n\n const testCases: Awaited<ReturnType<typeof readTestCases>> = [...idSet]\n .filter((id) => id !== SHARED_TEST_CASE_NAME)\n .toSorted()\n .map((id) => ({\n id,\n input: idToInput.get(id),\n output: idToOutput.get(id),\n fileInputPath: idToFileInputPath.get(id),\n fileOutputPath: idToFileOutputPath.get(id),\n }));\n\n if (idSet.has(SHARED_TEST_CASE_NAME)) {\n testCases.shared = { fileInputPath: idToFileInputPath.get(SHARED_TEST_CASE_NAME) };\n }\n\n return testCases;\n}\n"],"mappings":"gDAGA,MAAM,EAAwB,UAQ9B,eAAsB,EAAc,EAIlC,CACA,GAAI,CAAC,EAAG,WAAW,CAAS,EAAG,MAAO,CAAC,EAEvC,IAAM,EAAQ,IAAI,IACZ,EAAY,IAAI,IAChB,EAAa,IAAI,IACjB,EAAoB,IAAI,IACxB,EAAqB,IAAI,IAE/B,IAAK,IAAM,KAAU,MAAM,EAAG,SAAS,QAAQ,EAAW,CAAE,cAAe,EAAK,CAAC,EAC/E,GAAI,EAAO,OAAO,EAAG,CACnB,GAAM,CAAE,MAAK,QAAS,EAAK,MAAM,EAAO,IAAI,EAC5C,GAAI,IAAQ,OAAS,IAAQ,OAAQ,SAErC,IAAM,EAAO,MAAM,EAAG,SAAS,SAAS,EAAK,KAAK,EAAO,WAAY,EAAO,IAAI,EAAG,MAAM,EAEzF,EAAM,IAAI,CAAI,EACV,IAAQ,OAAO,EAAU,IAAI,EAAM,CAAI,EACvC,IAAQ,QAAQ,EAAW,IAAI,EAAM,CAAI,CAC/C,MAAO,GAAI,EAAO,YAAY,EAAG,CAC/B,GAAM,CAAE,MAAK,QAAS,EAAK,MAAM,EAAO,IAAI,EAC5C,GAAI,IAAQ,QAAU,IAAQ,QAAS,SAEvC,EAAM,IAAI,CAAI,EACV,IAAQ,QAAQ,EAAkB,IAAI,EAAM,EAAK,KAAK,EAAO,WAAY,EAAO,IAAI,CAAC,EACrF,IAAQ,SAAS,EAAmB,IAAI,EAAM,EAAK,KAAK,EAAO,WAAY,EAAO,IAAI,CAAC,CAC7F,CAGF,IAAM,EAAuD,CAAC,GAAG,CAAK,CAAC,CACpE,OAAQ,GAAO,IAAO,CAAqB,CAAC,CAC5C,SAAS,CAAC,CACV,IAAK,IAAQ,CACZ,KACA,MAAO,EAAU,IAAI,CAAE,EACvB,OAAQ,EAAW,IAAI,CAAE,EACzB,cAAe,EAAkB,IAAI,CAAE,EACvC,eAAgB,EAAmB,IAAI,CAAE,CAC3C,EAAE,EAMJ,OAJI,EAAM,IAAI,CAAqB,IACjC,EAAU,OAAS,CAAE,cAAe,EAAkB,IAAI,CAAqB,CAAE,GAG5E,CACT"}
1
+ {"version":3,"file":"readTestCases.js","names":[],"sources":["../../src/helpers/readTestCases.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\n\nconst SHARED_TEST_CASE_NAME = '_shared';\n\n/**\n * Read the test cases of a `test_cases/` directory. A test case id is the name of every\n * `<id>.in` / `<id>.out` file and `<id>.fin/` / `<id>.fout/` directory; any of them may be absent,\n * and what an absent part means is up to the harness (the presets document their rules).\n * Other entries (e.g. `.json` configuration read by a custom `judge.ts`) are ignored.\n */\nexport async function readTestCases(directory: string): Promise<\n { id: string; input?: string; output?: string; fileInputPath?: string; fileOutputPath?: string }[] & {\n shared?: { fileInputPath?: string };\n }\n> {\n if (!fs.existsSync(directory)) return [];\n\n const idSet = new Set<string>();\n const idToInput = new Map<string, string>();\n const idToOutput = new Map<string, string>();\n const idToFileInputPath = new Map<string, string>();\n const idToFileOutputPath = new Map<string, string>();\n\n for (const dirent of await fs.promises.readdir(directory, { withFileTypes: true })) {\n if (dirent.isFile()) {\n const { ext, name } = path.parse(dirent.name);\n if (ext !== '.in' && ext !== '.out') continue;\n\n const text = await fs.promises.readFile(path.join(dirent.parentPath, dirent.name), 'utf8');\n\n idSet.add(name);\n if (ext === '.in') idToInput.set(name, text);\n if (ext === '.out') idToOutput.set(name, text);\n } else if (dirent.isDirectory()) {\n const { ext, name } = path.parse(dirent.name);\n if (ext !== '.fin' && ext !== '.fout') continue;\n\n idSet.add(name);\n if (ext === '.fin') idToFileInputPath.set(name, path.join(dirent.parentPath, dirent.name));\n if (ext === '.fout') idToFileOutputPath.set(name, path.join(dirent.parentPath, dirent.name));\n }\n }\n\n const testCases: Awaited<ReturnType<typeof readTestCases>> = [...idSet]\n .filter((id) => id !== SHARED_TEST_CASE_NAME)\n .toSorted()\n .map((id) => ({\n id,\n input: idToInput.get(id),\n output: idToOutput.get(id),\n fileInputPath: idToFileInputPath.get(id),\n fileOutputPath: idToFileOutputPath.get(id),\n }));\n\n if (idSet.has(SHARED_TEST_CASE_NAME)) {\n testCases.shared = { fileInputPath: idToFileInputPath.get(SHARED_TEST_CASE_NAME) };\n }\n\n return testCases;\n}\n"],"mappings":"gDAGA,MAAM,EAAwB,UAQ9B,eAAsB,EAAc,EAIlC,CACA,GAAI,CAAC,EAAG,WAAW,CAAS,EAAG,MAAO,CAAC,EAEvC,IAAM,kBAAQ,IAAI,IACZ,kBAAY,IAAI,IAChB,kBAAa,IAAI,IACjB,kBAAoB,IAAI,IACxB,kBAAqB,IAAI,IAE/B,IAAK,IAAM,KAAU,MAAM,EAAG,SAAS,QAAQ,EAAW,CAAE,cAAe,EAAK,CAAC,EAC/E,GAAI,EAAO,OAAO,EAAG,CACnB,GAAM,CAAE,MAAK,QAAS,EAAK,MAAM,EAAO,IAAI,EAC5C,GAAI,IAAQ,OAAS,IAAQ,OAAQ,SAErC,IAAM,EAAO,MAAM,EAAG,SAAS,SAAS,EAAK,KAAK,EAAO,WAAY,EAAO,IAAI,EAAG,MAAM,EAEzF,EAAM,IAAI,CAAI,EACV,IAAQ,OAAO,EAAU,IAAI,EAAM,CAAI,EACvC,IAAQ,QAAQ,EAAW,IAAI,EAAM,CAAI,CAC/C,MAAO,GAAI,EAAO,YAAY,EAAG,CAC/B,GAAM,CAAE,MAAK,QAAS,EAAK,MAAM,EAAO,IAAI,EAC5C,GAAI,IAAQ,QAAU,IAAQ,QAAS,SAEvC,EAAM,IAAI,CAAI,EACV,IAAQ,QAAQ,EAAkB,IAAI,EAAM,EAAK,KAAK,EAAO,WAAY,EAAO,IAAI,CAAC,EACrF,IAAQ,SAAS,EAAmB,IAAI,EAAM,EAAK,KAAK,EAAO,WAAY,EAAO,IAAI,CAAC,CAC7F,CAGF,IAAM,EAAuD,CAAC,GAAG,CAAK,CAAC,CACpE,OAAQ,GAAO,IAAO,CAAqB,CAAC,CAC5C,SAAS,CAAC,CACV,IAAK,IAAQ,CACZ,KACA,MAAO,EAAU,IAAI,CAAE,EACvB,OAAQ,EAAW,IAAI,CAAE,EACzB,cAAe,EAAkB,IAAI,CAAE,EACvC,eAAgB,EAAmB,IAAI,CAAE,CAC3C,EAAE,EAMJ,OAJI,EAAM,IAAI,CAAqB,IACjC,EAAU,OAAS,CAAE,cAAe,EAAkB,IAAI,CAAqB,CAAE,GAG5E,CACT"}
@@ -1,2 +1,2 @@
1
- "use strict";const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("./spawnWithLimits.cjs"),n=require("./temporaryProblemDirCopy.cjs");let r=require("node:path");r=e.__toESM(r,1);let i=require("node:fs/promises");i=e.__toESM(i,1);let a=require("node:os");a=e.__toESM(a,1);const o={bun:[`package.json`,`bun.lock`,`bun.lockb`],cargo:[`Cargo.toml`,`Cargo.lock`],go:[`go.mod`,`go.sum`],gradle:[`build.gradle`,`build.gradle.kts`,`settings.gradle`,`settings.gradle.kts`,`gradle.properties`,`gradle.lockfile`,`buildscript-gradle.lockfile`,`gradle`,`gradlew`,`gradlew.bat`],maven:[`pom.xml`,`.mvn`,`mvnw`,`mvnw.cmd`],npm:[`package.json`,`package-lock.json`],pnpm:[`package.json`,`pnpm-lock.yaml`,`pnpm-workspace.yaml`],ruby:[`Gemfile`,`Gemfile.lock`,`.ruby-version`],uv:[`pyproject.toml`,`uv.lock`],yarn:[`package.json`,`yarn.lock`,`.yarnrc`,`.yarnrc.yml`,`.yarn`]},s={bun:f,cargo:p,go:m,gradle:h,maven:g,npm:_,pnpm:v,ruby:y,uv:b,yarn:x};async function c(e){let o=await i.default.mkdtemp(r.default.join(a.default.tmpdir(),e.tempDirPrefix??`exercode-`));try{await i.default.cp(e.cwd,o,{recursive:!0}),await E({packageManager:e.packageManager,projectDir:e.projectDir,runDir:o,projectFilePaths:e.projectFilePaths});let n=e.env?{...process.env,...e.env}:process.env,r=e.prepareDependencies===!1?void 0:await u(e.packageManager,o),a=typeof e.command==`function`?e.command({runDir:o}):e.command,s=e.outputLimitBytes??52428800,c;if(r&&(c=await t.spawnWithLimits(r,{cwd:o,env:n,outputLimitBytes:s,stdin:``,timeLimitSeconds:e.timeLimitSeconds}),d(c)))return l({options:e,result:c});let f=e.timeLimitSeconds-(c?.timeSeconds??0);if(f<=0)return{stdin:e.stdin??``,stdout:c?.stdout??``,stderr:c?.stderr??``,status:0,timeSeconds:e.timeLimitSeconds+.001,cpuTimeSeconds:c?.cpuTimeSeconds??0,memoryBytes:c?.memoryBytes??0,timedOut:!0,signal:c?.signal,outputLimitExceeded:!1};let p=await t.spawnWithLimits(a,{cwd:o,env:n,outputLimitBytes:s,stdin:e.stdin??``,timeLimitSeconds:f});return l(c?{options:e,result:{...p,timeSeconds:c.timeSeconds+p.timeSeconds,cpuTimeSeconds:c.cpuTimeSeconds+p.cpuTimeSeconds,memoryBytes:Math.max(c.memoryBytes,p.memoryBytes)}}:{options:e,result:p})}finally{await n.forciblyRemoveDirectory(o)}}function l(e){return{stdin:e.options.stdin??``,stdout:e.result.stdout,stderr:e.result.stderr,status:e.result.timedOut||e.result.outputLimitExceeded?0:e.result.status,timeSeconds:e.result.timedOut?e.options.timeLimitSeconds+.001:e.result.timeSeconds,cpuTimeSeconds:e.result.cpuTimeSeconds,memoryBytes:e.result.memoryBytes,timedOut:e.result.timedOut,signal:e.result.signal,outputLimitExceeded:e.result.outputLimitExceeded}}function u(e,t){return s[e](t)}function d(e){return e.status!==0||e.timedOut||e.outputLimitExceeded}async function f(e){if(await w(r.default.join(e,`package.json`)))return await C(e,[`bun.lock`,`bun.lockb`])?[`bun`,`install`,`--frozen-lockfile`,`--silent`]:[`bun`,`install`,`--silent`]}async function p(e){if(await w(r.default.join(e,`Cargo.toml`)))return await w(r.default.join(e,`Cargo.lock`))?[`cargo`,`fetch`,`--locked`]:[`cargo`,`fetch`]}async function m(e){if(await w(r.default.join(e,`go.mod`)))return[`go`,`mod`,`download`]}async function h(e){if(!await C(e,[`build.gradle`,`build.gradle.kts`,`settings.gradle`,`settings.gradle.kts`,`gradlew`,`gradlew.bat`]))return;let t=[`--no-daemon`,`--quiet`,`dependencies`];return process.platform===`win32`?await w(r.default.join(e,`gradlew.bat`))?[`cmd.exe`,`/c`,`gradlew.bat`,...t]:[`gradle`,...t]:await w(r.default.join(e,`gradlew`))?[`sh`,`./gradlew`,...t]:[`gradle`,...t]}async function g(e){if(!await w(r.default.join(e,`pom.xml`)))return;let t=[`-q`,`dependency:go-offline`];return process.platform===`win32`?await w(r.default.join(e,`mvnw.cmd`))?[`cmd.exe`,`/c`,`mvnw.cmd`,...t]:[`mvn`,...t]:await w(r.default.join(e,`mvnw`))?[`sh`,`./mvnw`,...t]:[`mvn`,...t]}async function _(e){if(await w(r.default.join(e,`package.json`)))return await w(r.default.join(e,`package-lock.json`))?[`npm`,`ci`,`--silent`]:[`npm`,`install`,`--silent`]}async function v(e){if(await w(r.default.join(e,`package.json`)))return await w(r.default.join(e,`pnpm-lock.yaml`))?[`pnpm`,`install`,`--frozen-lockfile`,`--silent`]:[`pnpm`,`install`,`--silent`]}async function y(e){if(await w(r.default.join(e,`Gemfile`)))return await w(r.default.join(e,`Gemfile.lock`))?[`bundle`,`install`,`--frozen`,`--quiet`]:[`bundle`,`install`,`--quiet`]}async function b(){}async function x(e){if(!await w(r.default.join(e,`package.json`)))return;let t=await S(e),n=await w(r.default.join(e,`yarn.lock`));return t?n?[`yarn`,`install`,`--immutable`]:[`yarn`,`install`]:n?[`yarn`,`install`,`--frozen-lockfile`,`--silent`]:[`yarn`,`install`,`--silent`]}async function S(e){if(await w(r.default.join(e,`.yarnrc.yml`)))return!0;let t=await T(r.default.join(e,`package.json`)),n=typeof t.packageManager==`string`?t.packageManager:void 0,i=/^yarn@(\d+)/.exec(n??``)?.[1];return i!==void 0&&Number(i)>=2}async function C(e,t){for(let n of t)if(await w(r.default.join(e,n)))return!0;return!1}async function w(e){try{return await i.default.access(e),!0}catch(e){if((typeof e==`object`&&e&&`code`in e?e.code:void 0)!==`ENOENT`)throw e;return!1}}async function T(e){try{let t=JSON.parse(await i.default.readFile(e,`utf8`));return typeof t==`object`&&t&&!Array.isArray(t)?t:{}}catch(e){if(e instanceof SyntaxError||(typeof e==`object`&&e&&`code`in e?e.code:void 0)===`ENOENT`)return{};throw e}}async function E(e){for(let t of e.projectFilePaths??o[e.packageManager])await D(r.default.join(e.projectDir,t),r.default.join(e.runDir,t))}async function D(e,t){try{await i.default.lstat(e),await i.default.mkdir(r.default.dirname(t),{recursive:!0}),await i.default.cp(e,t,{force:!0,recursive:!0})}catch(e){if((typeof e==`object`&&e&&`code`in e?e.code:void 0)!==`ENOENT`)throw e}}exports.copyPackageManagerProjectFiles=E,exports.runCommandInTemporaryPackageManagerProject=c;
1
+ "use strict";const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("./spawnWithLimits.cjs"),n=require("./temporaryProblemDirCopy.cjs");let r=require("node:path");r=e.__toESM(r,1);let i=require("node:fs/promises");i=e.__toESM(i,1);let a=require("node:os");a=e.__toESM(a,1);let o=require("@willbooster/shared-lib");const s={bun:[`package.json`,`bun.lock`,`bun.lockb`],cargo:[`Cargo.toml`,`Cargo.lock`],go:[`go.mod`,`go.sum`],gradle:[`build.gradle`,`build.gradle.kts`,`settings.gradle`,`settings.gradle.kts`,`gradle.properties`,`gradle.lockfile`,`buildscript-gradle.lockfile`,`gradle`,`gradlew`,`gradlew.bat`],maven:[`pom.xml`,`.mvn`,`mvnw`,`mvnw.cmd`],npm:[`package.json`,`package-lock.json`],pnpm:[`package.json`,`pnpm-lock.yaml`,`pnpm-workspace.yaml`],ruby:[`Gemfile`,`Gemfile.lock`,`.ruby-version`],uv:[`pyproject.toml`,`uv.lock`],yarn:[`package.json`,`yarn.lock`,`.yarnrc`,`.yarnrc.yml`,`.yarn`]},c={bun:p,cargo:m,go:h,gradle:g,maven:_,npm:v,pnpm:y,ruby:b,uv:x,yarn:S};async function l(e){let o=await i.default.mkdtemp(r.default.join(a.default.tmpdir(),e.tempDirPrefix??`exercode-`));try{await i.default.cp(e.cwd,o,{recursive:!0}),await D({packageManager:e.packageManager,projectDir:e.projectDir,runDir:o,projectFilePaths:e.projectFilePaths});let n=e.env?{...process.env,...e.env}:process.env,r=e.prepareDependencies===!1?void 0:await d(e.packageManager,o),a=typeof e.command==`function`?e.command({runDir:o}):e.command,s=e.outputLimitBytes??52428800,c;if(r&&(c=await t.spawnWithLimits(r,{cwd:o,env:n,outputLimitBytes:s,stdin:``,timeLimitSeconds:e.timeLimitSeconds}),f(c)))return u({options:e,result:c});let l=e.timeLimitSeconds-(c?.timeSeconds??0);if(l<=0)return{stdin:e.stdin??``,stdout:c?.stdout??``,stderr:c?.stderr??``,status:0,timeSeconds:e.timeLimitSeconds+.001,cpuTimeSeconds:c?.cpuTimeSeconds??0,memoryBytes:c?.memoryBytes??0,timedOut:!0,signal:c?.signal,outputLimitExceeded:!1};let p=await t.spawnWithLimits(a,{cwd:o,env:n,outputLimitBytes:s,stdin:e.stdin??``,timeLimitSeconds:l});return u(c?{options:e,result:{...p,timeSeconds:c.timeSeconds+p.timeSeconds,cpuTimeSeconds:c.cpuTimeSeconds+p.cpuTimeSeconds,memoryBytes:Math.max(c.memoryBytes,p.memoryBytes)}}:{options:e,result:p})}finally{await n.forciblyRemoveDirectory(o)}}function u(e){return{stdin:e.options.stdin??``,stdout:e.result.stdout,stderr:e.result.stderr,status:e.result.timedOut||e.result.outputLimitExceeded?0:e.result.status,timeSeconds:e.result.timedOut?e.options.timeLimitSeconds+.001:e.result.timeSeconds,cpuTimeSeconds:e.result.cpuTimeSeconds,memoryBytes:e.result.memoryBytes,timedOut:e.result.timedOut,signal:e.result.signal,outputLimitExceeded:e.result.outputLimitExceeded}}function d(e,t){return c[e](t)}function f(e){return e.status!==0||e.timedOut||e.outputLimitExceeded}async function p(e){if(await T(r.default.join(e,`package.json`)))return await w(e,[`bun.lock`,`bun.lockb`])?[`bun`,`install`,`--frozen-lockfile`,`--silent`]:[`bun`,`install`,`--silent`]}async function m(e){if(await T(r.default.join(e,`Cargo.toml`)))return await T(r.default.join(e,`Cargo.lock`))?[`cargo`,`fetch`,`--locked`]:[`cargo`,`fetch`]}async function h(e){if(await T(r.default.join(e,`go.mod`)))return[`go`,`mod`,`download`]}async function g(e){if(!await w(e,[`build.gradle`,`build.gradle.kts`,`settings.gradle`,`settings.gradle.kts`,`gradlew`,`gradlew.bat`]))return;let t=[`--no-daemon`,`--quiet`,`dependencies`];return process.platform===`win32`?await T(r.default.join(e,`gradlew.bat`))?[`cmd.exe`,`/c`,`gradlew.bat`,...t]:[`gradle`,...t]:await T(r.default.join(e,`gradlew`))?[`sh`,`./gradlew`,...t]:[`gradle`,...t]}async function _(e){if(!await T(r.default.join(e,`pom.xml`)))return;let t=[`-q`,`dependency:go-offline`];return process.platform===`win32`?await T(r.default.join(e,`mvnw.cmd`))?[`cmd.exe`,`/c`,`mvnw.cmd`,...t]:[`mvn`,...t]:await T(r.default.join(e,`mvnw`))?[`sh`,`./mvnw`,...t]:[`mvn`,...t]}async function v(e){if(await T(r.default.join(e,`package.json`)))return await T(r.default.join(e,`package-lock.json`))?[`npm`,`ci`,`--silent`]:[`npm`,`install`,`--silent`]}async function y(e){if(await T(r.default.join(e,`package.json`)))return await T(r.default.join(e,`pnpm-lock.yaml`))?[`pnpm`,`install`,`--frozen-lockfile`,`--silent`]:[`pnpm`,`install`,`--silent`]}async function b(e){if(await T(r.default.join(e,`Gemfile`)))return await T(r.default.join(e,`Gemfile.lock`))?[`bundle`,`install`,`--frozen`,`--quiet`]:[`bundle`,`install`,`--quiet`]}async function x(){}async function S(e){if(!await T(r.default.join(e,`package.json`)))return;let t=await C(e),n=await T(r.default.join(e,`yarn.lock`));return t?n?[`yarn`,`install`,`--immutable`]:[`yarn`,`install`]:n?[`yarn`,`install`,`--frozen-lockfile`,`--silent`]:[`yarn`,`install`,`--silent`]}async function C(e){if(await T(r.default.join(e,`.yarnrc.yml`)))return!0;let t=await E(r.default.join(e,`package.json`)),n=typeof t.packageManager==`string`?t.packageManager:void 0,i=/^yarn@(\d+)/.exec(n??``)?.[1];return i!==void 0&&Number(i)>=2}async function w(e,t){for(let n of t)if(await T(r.default.join(e,n)))return!0;return!1}async function T(e){try{return await i.default.access(e),!0}catch(e){if(!(0,o.hasErrorCode)(e,`ENOENT`))throw e;return!1}}async function E(e){try{let t=JSON.parse(await i.default.readFile(e,`utf8`));return(0,o.isRecord)(t)?t:{}}catch(e){if(e instanceof SyntaxError||(0,o.hasErrorCode)(e,`ENOENT`))return{};throw e}}async function D(e){for(let t of e.projectFilePaths??s[e.packageManager])await O(r.default.join(e.projectDir,t),r.default.join(e.runDir,t))}async function O(e,t){try{await i.default.lstat(e),await i.default.mkdir(r.default.dirname(t),{recursive:!0}),await i.default.cp(e,t,{force:!0,recursive:!0})}catch(e){if(!(0,o.hasErrorCode)(e,`ENOENT`))throw e}}exports.copyPackageManagerProjectFiles=D,exports.runCommandInTemporaryPackageManagerProject=l;
2
2
  //# sourceMappingURL=runCommandInTemporaryPackageManagerProject.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"runCommandInTemporaryPackageManagerProject.cjs","names":["fs","path","os","spawnWithLimits","forciblyRemoveDirectory"],"sources":["../../src/helpers/runCommandInTemporaryPackageManagerProject.ts"],"sourcesContent":["import fs from 'node:fs/promises';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport { spawnWithLimits, type SpawnWithLimitsResult } from './spawnWithLimits.js';\nimport { forciblyRemoveDirectory } from './temporaryProblemDirCopy.js';\n\nexport type PackageManager = 'bun' | 'cargo' | 'go' | 'gradle' | 'maven' | 'npm' | 'pnpm' | 'ruby' | 'uv' | 'yarn';\ntype PackageManagerInstallCommand = readonly [string, ...string[]];\n\nexport interface PackageManagerCommandRunResult {\n stdin: string;\n stdout: string;\n stderr: string;\n status: number | undefined;\n timeSeconds: number;\n /** See `SpawnWithLimitsResult.cpuTimeSeconds`; the install's CPU time counts too. */\n cpuTimeSeconds: number;\n memoryBytes: number;\n timedOut: boolean;\n signal: NodeJS.Signals | undefined;\n outputLimitExceeded: boolean;\n}\n\nexport interface RunCommandInTemporaryPackageManagerProjectOptions {\n cwd: string;\n projectDir: string;\n packageManager: PackageManager;\n command: readonly [string, ...string[]] | ((context: { runDir: string }) => readonly [string, ...string[]]);\n /**\n * Set to false when `command` prepares the dependencies it needs.\n * Defaults to true.\n */\n prepareDependencies?: boolean;\n stdin?: string;\n env?: NodeJS.ProcessEnv;\n timeLimitSeconds: number;\n outputLimitBytes?: number;\n tempDirPrefix?: string;\n projectFilePaths?: readonly string[];\n}\n\nconst packageManagerProjectFilePaths = {\n bun: ['package.json', 'bun.lock', 'bun.lockb'],\n cargo: ['Cargo.toml', 'Cargo.lock'],\n go: ['go.mod', 'go.sum'],\n gradle: [\n 'build.gradle',\n 'build.gradle.kts',\n 'settings.gradle',\n 'settings.gradle.kts',\n 'gradle.properties',\n 'gradle.lockfile',\n 'buildscript-gradle.lockfile',\n 'gradle',\n 'gradlew',\n 'gradlew.bat',\n ],\n maven: ['pom.xml', '.mvn', 'mvnw', 'mvnw.cmd'],\n npm: ['package.json', 'package-lock.json'],\n pnpm: ['package.json', 'pnpm-lock.yaml', 'pnpm-workspace.yaml'],\n ruby: ['Gemfile', 'Gemfile.lock', '.ruby-version'],\n uv: ['pyproject.toml', 'uv.lock'],\n yarn: ['package.json', 'yarn.lock', '.yarnrc', '.yarnrc.yml', '.yarn'],\n} as const satisfies Record<PackageManager, readonly string[]>;\n\nconst packageManagerInstallCommandResolvers = {\n bun: resolveBunInstallCommand,\n cargo: resolveCargoInstallCommand,\n go: resolveGoInstallCommand,\n gradle: resolveGradleInstallCommand,\n maven: resolveMavenInstallCommand,\n npm: resolveNpmInstallCommand,\n pnpm: resolvePnpmInstallCommand,\n ruby: resolveRubyInstallCommand,\n uv: resolveUvInstallCommand,\n yarn: resolveYarnInstallCommand,\n} as const satisfies Record<PackageManager, (runDir: string) => Promise<PackageManagerInstallCommand | undefined>>;\n\nconst defaultOutputLimitBytes = 50 * 1024 * 1024;\n\n/**\n * Copies a submission directory to a temporary directory, overlays package\n * manager project files from the problem directory, prepares dependencies,\n * runs a command, and then removes the temporary directory.\n */\nexport async function runCommandInTemporaryPackageManagerProject(\n options: RunCommandInTemporaryPackageManagerProjectOptions\n): Promise<PackageManagerCommandRunResult> {\n const runDir = await fs.mkdtemp(path.join(os.tmpdir(), options.tempDirPrefix ?? 'exercode-'));\n try {\n await fs.cp(options.cwd, runDir, { recursive: true });\n await copyPackageManagerProjectFiles({\n packageManager: options.packageManager,\n projectDir: options.projectDir,\n runDir,\n projectFilePaths: options.projectFilePaths,\n });\n\n const env = options.env ? { ...process.env, ...options.env } : process.env;\n const installCommand =\n options.prepareDependencies === false ? undefined : await resolveInstallCommand(options.packageManager, runDir);\n const command = typeof options.command === 'function' ? options.command({ runDir }) : options.command;\n const outputLimitBytes = options.outputLimitBytes ?? defaultOutputLimitBytes;\n let installResult: SpawnWithLimitsResult | undefined;\n\n if (installCommand) {\n installResult = await spawnWithLimits(installCommand, {\n cwd: runDir,\n env,\n outputLimitBytes,\n stdin: '',\n timeLimitSeconds: options.timeLimitSeconds,\n });\n if (isFailedSpawnResult(installResult)) {\n return toPackageManagerCommandRunResult({\n options,\n result: installResult,\n });\n }\n }\n\n // The install's measured time excludes the grace spent releasing pipes after it exited.\n const remainingTimeLimitSeconds = options.timeLimitSeconds - (installResult?.timeSeconds ?? 0);\n if (remainingTimeLimitSeconds <= 0) {\n return {\n stdin: options.stdin ?? '',\n stdout: installResult?.stdout ?? '',\n stderr: installResult?.stderr ?? '',\n status: 0,\n timeSeconds: options.timeLimitSeconds + 1e-3,\n cpuTimeSeconds: installResult?.cpuTimeSeconds ?? 0,\n memoryBytes: installResult?.memoryBytes ?? 0,\n timedOut: true,\n signal: installResult?.signal,\n outputLimitExceeded: false,\n };\n }\n\n const result = await spawnWithLimits(command, {\n cwd: runDir,\n env,\n outputLimitBytes,\n stdin: options.stdin ?? '',\n timeLimitSeconds: remainingTimeLimitSeconds,\n });\n\n if (installResult) {\n return toPackageManagerCommandRunResult({\n options,\n result: {\n ...result,\n timeSeconds: installResult.timeSeconds + result.timeSeconds,\n cpuTimeSeconds: installResult.cpuTimeSeconds + result.cpuTimeSeconds,\n memoryBytes: Math.max(installResult.memoryBytes, result.memoryBytes),\n },\n });\n }\n\n return toPackageManagerCommandRunResult({ options, result });\n } finally {\n // The command may have left permission-locked entries (e.g. a mode-000 directory).\n await forciblyRemoveDirectory(runDir);\n }\n}\n\nfunction toPackageManagerCommandRunResult(context: {\n options: RunCommandInTemporaryPackageManagerProjectOptions;\n result: SpawnWithLimitsResult;\n}): PackageManagerCommandRunResult {\n return {\n stdin: context.options.stdin ?? '',\n stdout: context.result.stdout,\n stderr: context.result.stderr,\n status: context.result.timedOut || context.result.outputLimitExceeded ? 0 : context.result.status,\n timeSeconds: context.result.timedOut ? context.options.timeLimitSeconds + 1e-3 : context.result.timeSeconds,\n cpuTimeSeconds: context.result.cpuTimeSeconds,\n memoryBytes: context.result.memoryBytes,\n timedOut: context.result.timedOut,\n signal: context.result.signal,\n outputLimitExceeded: context.result.outputLimitExceeded,\n };\n}\n\nfunction resolveInstallCommand(\n packageManager: PackageManager,\n runDir: string\n): Promise<PackageManagerInstallCommand | undefined> {\n return packageManagerInstallCommandResolvers[packageManager](runDir);\n}\n\nfunction isFailedSpawnResult(result: SpawnWithLimitsResult): boolean {\n return result.status !== 0 || result.timedOut || result.outputLimitExceeded;\n}\n\nasync function resolveBunInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'package.json')))) return undefined;\n // Bun supports --silent and it keeps successful preparation output out of judge output buffers.\n return (await hasAnyPath(runDir, ['bun.lock', 'bun.lockb']))\n ? ['bun', 'install', '--frozen-lockfile', '--silent']\n : ['bun', 'install', '--silent'];\n}\n\nasync function resolveCargoInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'Cargo.toml')))) return undefined;\n return (await pathExists(path.join(runDir, 'Cargo.lock'))) ? ['cargo', 'fetch', '--locked'] : ['cargo', 'fetch'];\n}\n\nasync function resolveGoInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'go.mod')))) return undefined;\n return ['go', 'mod', 'download'];\n}\n\nasync function resolveGradleInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (\n !(await hasAnyPath(runDir, [\n 'build.gradle',\n 'build.gradle.kts',\n 'settings.gradle',\n 'settings.gradle.kts',\n 'gradlew',\n 'gradlew.bat',\n ]))\n )\n return undefined;\n const args = ['--no-daemon', '--quiet', 'dependencies'] as const;\n if (process.platform === 'win32') {\n return (await pathExists(path.join(runDir, 'gradlew.bat')))\n ? ['cmd.exe', '/c', 'gradlew.bat', ...args]\n : ['gradle', ...args];\n }\n return (await pathExists(path.join(runDir, 'gradlew'))) ? ['sh', './gradlew', ...args] : ['gradle', ...args];\n}\n\nasync function resolveMavenInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'pom.xml')))) return undefined;\n const args = ['-q', 'dependency:go-offline'] as const;\n if (process.platform === 'win32') {\n return (await pathExists(path.join(runDir, 'mvnw.cmd')))\n ? ['cmd.exe', '/c', 'mvnw.cmd', ...args]\n : ['mvn', ...args];\n }\n return (await pathExists(path.join(runDir, 'mvnw'))) ? ['sh', './mvnw', ...args] : ['mvn', ...args];\n}\n\nasync function resolveNpmInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'package.json')))) return undefined;\n return (await pathExists(path.join(runDir, 'package-lock.json')))\n ? ['npm', 'ci', '--silent']\n : ['npm', 'install', '--silent'];\n}\n\nasync function resolvePnpmInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'package.json')))) return undefined;\n return (await pathExists(path.join(runDir, 'pnpm-lock.yaml')))\n ? ['pnpm', 'install', '--frozen-lockfile', '--silent']\n : ['pnpm', 'install', '--silent'];\n}\n\nasync function resolveRubyInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'Gemfile')))) return undefined;\n return (await pathExists(path.join(runDir, 'Gemfile.lock')))\n ? ['bundle', 'install', '--frozen', '--quiet']\n : ['bundle', 'install', '--quiet'];\n}\n\nasync function resolveUvInstallCommand(): Promise<undefined> {\n return undefined;\n}\n\nasync function resolveYarnInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'package.json')))) return undefined;\n const isBerry = await isYarnBerryProject(runDir);\n const hasLockfile = await pathExists(path.join(runDir, 'yarn.lock'));\n if (isBerry) return hasLockfile ? ['yarn', 'install', '--immutable'] : ['yarn', 'install'];\n return hasLockfile ? ['yarn', 'install', '--frozen-lockfile', '--silent'] : ['yarn', 'install', '--silent'];\n}\n\nasync function isYarnBerryProject(runDir: string): Promise<boolean> {\n if (await pathExists(path.join(runDir, '.yarnrc.yml'))) return true;\n\n const packageJson = await readJson(path.join(runDir, 'package.json'));\n const packageManager = typeof packageJson.packageManager === 'string' ? packageJson.packageManager : undefined;\n const yarnMajorVersion = /^yarn@(\\d+)/.exec(packageManager ?? '')?.[1];\n return yarnMajorVersion !== undefined && Number(yarnMajorVersion) >= 2;\n}\n\nasync function hasAnyPath(directoryPath: string, relativePaths: readonly string[]): Promise<boolean> {\n for (const relativePath of relativePaths) {\n if (await pathExists(path.join(directoryPath, relativePath))) return true;\n }\n return false;\n}\n\nasync function pathExists(filePath: string): Promise<boolean> {\n try {\n await fs.access(filePath);\n return true;\n } catch (error) {\n const code =\n typeof error === 'object' && error !== null && 'code' in error ? (error as { code: unknown }).code : undefined;\n if (code !== 'ENOENT') throw error;\n return false;\n }\n}\n\nasync function readJson(filePath: string): Promise<Record<string, unknown>> {\n try {\n const parsed = JSON.parse(await fs.readFile(filePath, 'utf8')) as unknown;\n return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : {};\n } catch (error) {\n if (error instanceof SyntaxError) return {};\n const code =\n typeof error === 'object' && error !== null && 'code' in error ? (error as { code: unknown }).code : undefined;\n if (code === 'ENOENT') return {};\n throw error;\n }\n}\n\nexport async function copyPackageManagerProjectFiles(options: {\n packageManager: PackageManager;\n projectDir: string;\n runDir: string;\n projectFilePaths?: readonly string[];\n}): Promise<void> {\n for (const projectFilePath of options.projectFilePaths ?? packageManagerProjectFilePaths[options.packageManager]) {\n await copyPathIfExists(path.join(options.projectDir, projectFilePath), path.join(options.runDir, projectFilePath));\n }\n}\n\nasync function copyPathIfExists(sourcePath: string, destinationPath: string): Promise<void> {\n try {\n // Before touching the destination: creating the parent levels must not happen for a project\n // file the problem does not even ship.\n await fs.lstat(sourcePath);\n await fs.mkdir(path.dirname(destinationPath), { recursive: true });\n await fs.cp(sourcePath, destinationPath, { force: true, recursive: true });\n } catch (error) {\n const code =\n typeof error === 'object' && error !== null && 'code' in error ? (error as { code: unknown }).code : undefined;\n if (code !== 'ENOENT') throw error;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"runCommandInTemporaryPackageManagerProject.cjs","names":["fs","path","os","spawnWithLimits","forciblyRemoveDirectory","hasErrorCode","isRecord"],"sources":["../../src/helpers/runCommandInTemporaryPackageManagerProject.ts"],"sourcesContent":["import fs from 'node:fs/promises';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { hasErrorCode, isRecord } from '@willbooster/shared-lib';\n\nimport { spawnWithLimits, type SpawnWithLimitsResult } from './spawnWithLimits.js';\nimport { forciblyRemoveDirectory } from './temporaryProblemDirCopy.js';\n\nexport type PackageManager = 'bun' | 'cargo' | 'go' | 'gradle' | 'maven' | 'npm' | 'pnpm' | 'ruby' | 'uv' | 'yarn';\ntype PackageManagerInstallCommand = readonly [string, ...string[]];\n\nexport interface PackageManagerCommandRunResult {\n stdin: string;\n stdout: string;\n stderr: string;\n status: number | undefined;\n timeSeconds: number;\n /** See `SpawnWithLimitsResult.cpuTimeSeconds`; the install's CPU time counts too. */\n cpuTimeSeconds: number;\n memoryBytes: number;\n timedOut: boolean;\n signal: NodeJS.Signals | undefined;\n outputLimitExceeded: boolean;\n}\n\nexport interface RunCommandInTemporaryPackageManagerProjectOptions {\n cwd: string;\n projectDir: string;\n packageManager: PackageManager;\n command: readonly [string, ...string[]] | ((context: { runDir: string }) => readonly [string, ...string[]]);\n /**\n * Set to false when `command` prepares the dependencies it needs.\n * Defaults to true.\n */\n prepareDependencies?: boolean;\n stdin?: string;\n env?: NodeJS.ProcessEnv;\n timeLimitSeconds: number;\n outputLimitBytes?: number;\n tempDirPrefix?: string;\n projectFilePaths?: readonly string[];\n}\n\nconst packageManagerProjectFilePaths = {\n bun: ['package.json', 'bun.lock', 'bun.lockb'],\n cargo: ['Cargo.toml', 'Cargo.lock'],\n go: ['go.mod', 'go.sum'],\n gradle: [\n 'build.gradle',\n 'build.gradle.kts',\n 'settings.gradle',\n 'settings.gradle.kts',\n 'gradle.properties',\n 'gradle.lockfile',\n 'buildscript-gradle.lockfile',\n 'gradle',\n 'gradlew',\n 'gradlew.bat',\n ],\n maven: ['pom.xml', '.mvn', 'mvnw', 'mvnw.cmd'],\n npm: ['package.json', 'package-lock.json'],\n pnpm: ['package.json', 'pnpm-lock.yaml', 'pnpm-workspace.yaml'],\n ruby: ['Gemfile', 'Gemfile.lock', '.ruby-version'],\n uv: ['pyproject.toml', 'uv.lock'],\n yarn: ['package.json', 'yarn.lock', '.yarnrc', '.yarnrc.yml', '.yarn'],\n} as const satisfies Record<PackageManager, readonly string[]>;\n\nconst packageManagerInstallCommandResolvers = {\n bun: resolveBunInstallCommand,\n cargo: resolveCargoInstallCommand,\n go: resolveGoInstallCommand,\n gradle: resolveGradleInstallCommand,\n maven: resolveMavenInstallCommand,\n npm: resolveNpmInstallCommand,\n pnpm: resolvePnpmInstallCommand,\n ruby: resolveRubyInstallCommand,\n uv: resolveUvInstallCommand,\n yarn: resolveYarnInstallCommand,\n} as const satisfies Record<PackageManager, (runDir: string) => Promise<PackageManagerInstallCommand | undefined>>;\n\nconst defaultOutputLimitBytes = 50 * 1024 * 1024;\n\n/**\n * Copies a submission directory to a temporary directory, overlays package\n * manager project files from the problem directory, prepares dependencies,\n * runs a command, and then removes the temporary directory.\n */\nexport async function runCommandInTemporaryPackageManagerProject(\n options: RunCommandInTemporaryPackageManagerProjectOptions\n): Promise<PackageManagerCommandRunResult> {\n const runDir = await fs.mkdtemp(path.join(os.tmpdir(), options.tempDirPrefix ?? 'exercode-'));\n try {\n await fs.cp(options.cwd, runDir, { recursive: true });\n await copyPackageManagerProjectFiles({\n packageManager: options.packageManager,\n projectDir: options.projectDir,\n runDir,\n projectFilePaths: options.projectFilePaths,\n });\n\n const env = options.env ? { ...process.env, ...options.env } : process.env;\n const installCommand =\n options.prepareDependencies === false ? undefined : await resolveInstallCommand(options.packageManager, runDir);\n const command = typeof options.command === 'function' ? options.command({ runDir }) : options.command;\n const outputLimitBytes = options.outputLimitBytes ?? defaultOutputLimitBytes;\n let installResult: SpawnWithLimitsResult | undefined;\n\n if (installCommand) {\n installResult = await spawnWithLimits(installCommand, {\n cwd: runDir,\n env,\n outputLimitBytes,\n stdin: '',\n timeLimitSeconds: options.timeLimitSeconds,\n });\n if (isFailedSpawnResult(installResult)) {\n return toPackageManagerCommandRunResult({\n options,\n result: installResult,\n });\n }\n }\n\n // The install's measured time excludes the grace spent releasing pipes after it exited.\n const remainingTimeLimitSeconds = options.timeLimitSeconds - (installResult?.timeSeconds ?? 0);\n if (remainingTimeLimitSeconds <= 0) {\n return {\n stdin: options.stdin ?? '',\n stdout: installResult?.stdout ?? '',\n stderr: installResult?.stderr ?? '',\n status: 0,\n timeSeconds: options.timeLimitSeconds + 1e-3,\n cpuTimeSeconds: installResult?.cpuTimeSeconds ?? 0,\n memoryBytes: installResult?.memoryBytes ?? 0,\n timedOut: true,\n signal: installResult?.signal,\n outputLimitExceeded: false,\n };\n }\n\n const result = await spawnWithLimits(command, {\n cwd: runDir,\n env,\n outputLimitBytes,\n stdin: options.stdin ?? '',\n timeLimitSeconds: remainingTimeLimitSeconds,\n });\n\n if (installResult) {\n return toPackageManagerCommandRunResult({\n options,\n result: {\n ...result,\n timeSeconds: installResult.timeSeconds + result.timeSeconds,\n cpuTimeSeconds: installResult.cpuTimeSeconds + result.cpuTimeSeconds,\n memoryBytes: Math.max(installResult.memoryBytes, result.memoryBytes),\n },\n });\n }\n\n return toPackageManagerCommandRunResult({ options, result });\n } finally {\n // The command may have left permission-locked entries (e.g. a mode-000 directory).\n await forciblyRemoveDirectory(runDir);\n }\n}\n\nfunction toPackageManagerCommandRunResult(context: {\n options: RunCommandInTemporaryPackageManagerProjectOptions;\n result: SpawnWithLimitsResult;\n}): PackageManagerCommandRunResult {\n return {\n stdin: context.options.stdin ?? '',\n stdout: context.result.stdout,\n stderr: context.result.stderr,\n status: context.result.timedOut || context.result.outputLimitExceeded ? 0 : context.result.status,\n timeSeconds: context.result.timedOut ? context.options.timeLimitSeconds + 1e-3 : context.result.timeSeconds,\n cpuTimeSeconds: context.result.cpuTimeSeconds,\n memoryBytes: context.result.memoryBytes,\n timedOut: context.result.timedOut,\n signal: context.result.signal,\n outputLimitExceeded: context.result.outputLimitExceeded,\n };\n}\n\nfunction resolveInstallCommand(\n packageManager: PackageManager,\n runDir: string\n): Promise<PackageManagerInstallCommand | undefined> {\n return packageManagerInstallCommandResolvers[packageManager](runDir);\n}\n\nfunction isFailedSpawnResult(result: SpawnWithLimitsResult): boolean {\n return result.status !== 0 || result.timedOut || result.outputLimitExceeded;\n}\n\nasync function resolveBunInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'package.json')))) return undefined;\n // Bun supports --silent and it keeps successful preparation output out of judge output buffers.\n return (await hasAnyPath(runDir, ['bun.lock', 'bun.lockb']))\n ? ['bun', 'install', '--frozen-lockfile', '--silent']\n : ['bun', 'install', '--silent'];\n}\n\nasync function resolveCargoInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'Cargo.toml')))) return undefined;\n return (await pathExists(path.join(runDir, 'Cargo.lock'))) ? ['cargo', 'fetch', '--locked'] : ['cargo', 'fetch'];\n}\n\nasync function resolveGoInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'go.mod')))) return undefined;\n return ['go', 'mod', 'download'];\n}\n\nasync function resolveGradleInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (\n !(await hasAnyPath(runDir, [\n 'build.gradle',\n 'build.gradle.kts',\n 'settings.gradle',\n 'settings.gradle.kts',\n 'gradlew',\n 'gradlew.bat',\n ]))\n )\n return undefined;\n const args = ['--no-daemon', '--quiet', 'dependencies'] as const;\n if (process.platform === 'win32') {\n return (await pathExists(path.join(runDir, 'gradlew.bat')))\n ? ['cmd.exe', '/c', 'gradlew.bat', ...args]\n : ['gradle', ...args];\n }\n return (await pathExists(path.join(runDir, 'gradlew'))) ? ['sh', './gradlew', ...args] : ['gradle', ...args];\n}\n\nasync function resolveMavenInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'pom.xml')))) return undefined;\n const args = ['-q', 'dependency:go-offline'] as const;\n if (process.platform === 'win32') {\n return (await pathExists(path.join(runDir, 'mvnw.cmd')))\n ? ['cmd.exe', '/c', 'mvnw.cmd', ...args]\n : ['mvn', ...args];\n }\n return (await pathExists(path.join(runDir, 'mvnw'))) ? ['sh', './mvnw', ...args] : ['mvn', ...args];\n}\n\nasync function resolveNpmInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'package.json')))) return undefined;\n return (await pathExists(path.join(runDir, 'package-lock.json')))\n ? ['npm', 'ci', '--silent']\n : ['npm', 'install', '--silent'];\n}\n\nasync function resolvePnpmInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'package.json')))) return undefined;\n return (await pathExists(path.join(runDir, 'pnpm-lock.yaml')))\n ? ['pnpm', 'install', '--frozen-lockfile', '--silent']\n : ['pnpm', 'install', '--silent'];\n}\n\nasync function resolveRubyInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'Gemfile')))) return undefined;\n return (await pathExists(path.join(runDir, 'Gemfile.lock')))\n ? ['bundle', 'install', '--frozen', '--quiet']\n : ['bundle', 'install', '--quiet'];\n}\n\nasync function resolveUvInstallCommand(): Promise<undefined> {\n return undefined;\n}\n\nasync function resolveYarnInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'package.json')))) return undefined;\n const isBerry = await isYarnBerryProject(runDir);\n const hasLockfile = await pathExists(path.join(runDir, 'yarn.lock'));\n if (isBerry) return hasLockfile ? ['yarn', 'install', '--immutable'] : ['yarn', 'install'];\n return hasLockfile ? ['yarn', 'install', '--frozen-lockfile', '--silent'] : ['yarn', 'install', '--silent'];\n}\n\nasync function isYarnBerryProject(runDir: string): Promise<boolean> {\n if (await pathExists(path.join(runDir, '.yarnrc.yml'))) return true;\n\n const packageJson = await readJson(path.join(runDir, 'package.json'));\n const packageManager = typeof packageJson.packageManager === 'string' ? packageJson.packageManager : undefined;\n const yarnMajorVersion = /^yarn@(\\d+)/.exec(packageManager ?? '')?.[1];\n return yarnMajorVersion !== undefined && Number(yarnMajorVersion) >= 2;\n}\n\nasync function hasAnyPath(directoryPath: string, relativePaths: readonly string[]): Promise<boolean> {\n for (const relativePath of relativePaths) {\n if (await pathExists(path.join(directoryPath, relativePath))) return true;\n }\n return false;\n}\n\nasync function pathExists(filePath: string): Promise<boolean> {\n try {\n await fs.access(filePath);\n return true;\n } catch (error) {\n if (!hasErrorCode(error, 'ENOENT')) throw error;\n return false;\n }\n}\n\nasync function readJson(filePath: string): Promise<Record<string, unknown>> {\n try {\n const parsed = JSON.parse(await fs.readFile(filePath, 'utf8')) as unknown;\n return isRecord(parsed) ? parsed : {};\n } catch (error) {\n if (error instanceof SyntaxError) return {};\n if (hasErrorCode(error, 'ENOENT')) return {};\n throw error;\n }\n}\n\nexport async function copyPackageManagerProjectFiles(options: {\n packageManager: PackageManager;\n projectDir: string;\n runDir: string;\n projectFilePaths?: readonly string[];\n}): Promise<void> {\n for (const projectFilePath of options.projectFilePaths ?? packageManagerProjectFilePaths[options.packageManager]) {\n await copyPathIfExists(path.join(options.projectDir, projectFilePath), path.join(options.runDir, projectFilePath));\n }\n}\n\nasync function copyPathIfExists(sourcePath: string, destinationPath: string): Promise<void> {\n try {\n // Before touching the destination: creating the parent levels must not happen for a project\n // file the problem does not even ship.\n await fs.lstat(sourcePath);\n await fs.mkdir(path.dirname(destinationPath), { recursive: true });\n await fs.cp(sourcePath, destinationPath, { force: true, recursive: true });\n } catch (error) {\n if (!hasErrorCode(error, 'ENOENT')) throw error;\n }\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{spawnWithLimits as e}from"./spawnWithLimits.js";import{forciblyRemoveDirectory as t}from"./temporaryProblemDirCopy.js";import n from"node:path";import r from"node:fs/promises";import i from"node:os";const a={bun:[`package.json`,`bun.lock`,`bun.lockb`],cargo:[`Cargo.toml`,`Cargo.lock`],go:[`go.mod`,`go.sum`],gradle:[`build.gradle`,`build.gradle.kts`,`settings.gradle`,`settings.gradle.kts`,`gradle.properties`,`gradle.lockfile`,`buildscript-gradle.lockfile`,`gradle`,`gradlew`,`gradlew.bat`],maven:[`pom.xml`,`.mvn`,`mvnw`,`mvnw.cmd`],npm:[`package.json`,`package-lock.json`],pnpm:[`package.json`,`pnpm-lock.yaml`,`pnpm-workspace.yaml`],ruby:[`Gemfile`,`Gemfile.lock`,`.ruby-version`],uv:[`pyproject.toml`,`uv.lock`],yarn:[`package.json`,`yarn.lock`,`.yarnrc`,`.yarnrc.yml`,`.yarn`]},o={bun:d,cargo:f,go:p,gradle:m,maven:h,npm:g,pnpm:_,ruby:v,uv:y,yarn:b};async function s(a){let o=await r.mkdtemp(n.join(i.tmpdir(),a.tempDirPrefix??`exercode-`));try{await r.cp(a.cwd,o,{recursive:!0}),await T({packageManager:a.packageManager,projectDir:a.projectDir,runDir:o,projectFilePaths:a.projectFilePaths});let t=a.env?{...process.env,...a.env}:process.env,n=a.prepareDependencies===!1?void 0:await l(a.packageManager,o),i=typeof a.command==`function`?a.command({runDir:o}):a.command,s=a.outputLimitBytes??52428800,d;if(n&&(d=await e(n,{cwd:o,env:t,outputLimitBytes:s,stdin:``,timeLimitSeconds:a.timeLimitSeconds}),u(d)))return c({options:a,result:d});let f=a.timeLimitSeconds-(d?.timeSeconds??0);if(f<=0)return{stdin:a.stdin??``,stdout:d?.stdout??``,stderr:d?.stderr??``,status:0,timeSeconds:a.timeLimitSeconds+.001,cpuTimeSeconds:d?.cpuTimeSeconds??0,memoryBytes:d?.memoryBytes??0,timedOut:!0,signal:d?.signal,outputLimitExceeded:!1};let p=await e(i,{cwd:o,env:t,outputLimitBytes:s,stdin:a.stdin??``,timeLimitSeconds:f});return c(d?{options:a,result:{...p,timeSeconds:d.timeSeconds+p.timeSeconds,cpuTimeSeconds:d.cpuTimeSeconds+p.cpuTimeSeconds,memoryBytes:Math.max(d.memoryBytes,p.memoryBytes)}}:{options:a,result:p})}finally{await t(o)}}function c(e){return{stdin:e.options.stdin??``,stdout:e.result.stdout,stderr:e.result.stderr,status:e.result.timedOut||e.result.outputLimitExceeded?0:e.result.status,timeSeconds:e.result.timedOut?e.options.timeLimitSeconds+.001:e.result.timeSeconds,cpuTimeSeconds:e.result.cpuTimeSeconds,memoryBytes:e.result.memoryBytes,timedOut:e.result.timedOut,signal:e.result.signal,outputLimitExceeded:e.result.outputLimitExceeded}}function l(e,t){return o[e](t)}function u(e){return e.status!==0||e.timedOut||e.outputLimitExceeded}async function d(e){if(await C(n.join(e,`package.json`)))return await S(e,[`bun.lock`,`bun.lockb`])?[`bun`,`install`,`--frozen-lockfile`,`--silent`]:[`bun`,`install`,`--silent`]}async function f(e){if(await C(n.join(e,`Cargo.toml`)))return await C(n.join(e,`Cargo.lock`))?[`cargo`,`fetch`,`--locked`]:[`cargo`,`fetch`]}async function p(e){if(await C(n.join(e,`go.mod`)))return[`go`,`mod`,`download`]}async function m(e){if(!await S(e,[`build.gradle`,`build.gradle.kts`,`settings.gradle`,`settings.gradle.kts`,`gradlew`,`gradlew.bat`]))return;let t=[`--no-daemon`,`--quiet`,`dependencies`];return process.platform===`win32`?await C(n.join(e,`gradlew.bat`))?[`cmd.exe`,`/c`,`gradlew.bat`,...t]:[`gradle`,...t]:await C(n.join(e,`gradlew`))?[`sh`,`./gradlew`,...t]:[`gradle`,...t]}async function h(e){if(!await C(n.join(e,`pom.xml`)))return;let t=[`-q`,`dependency:go-offline`];return process.platform===`win32`?await C(n.join(e,`mvnw.cmd`))?[`cmd.exe`,`/c`,`mvnw.cmd`,...t]:[`mvn`,...t]:await C(n.join(e,`mvnw`))?[`sh`,`./mvnw`,...t]:[`mvn`,...t]}async function g(e){if(await C(n.join(e,`package.json`)))return await C(n.join(e,`package-lock.json`))?[`npm`,`ci`,`--silent`]:[`npm`,`install`,`--silent`]}async function _(e){if(await C(n.join(e,`package.json`)))return await C(n.join(e,`pnpm-lock.yaml`))?[`pnpm`,`install`,`--frozen-lockfile`,`--silent`]:[`pnpm`,`install`,`--silent`]}async function v(e){if(await C(n.join(e,`Gemfile`)))return await C(n.join(e,`Gemfile.lock`))?[`bundle`,`install`,`--frozen`,`--quiet`]:[`bundle`,`install`,`--quiet`]}async function y(){}async function b(e){if(!await C(n.join(e,`package.json`)))return;let t=await x(e),r=await C(n.join(e,`yarn.lock`));return t?r?[`yarn`,`install`,`--immutable`]:[`yarn`,`install`]:r?[`yarn`,`install`,`--frozen-lockfile`,`--silent`]:[`yarn`,`install`,`--silent`]}async function x(e){if(await C(n.join(e,`.yarnrc.yml`)))return!0;let t=await w(n.join(e,`package.json`)),r=typeof t.packageManager==`string`?t.packageManager:void 0,i=/^yarn@(\d+)/.exec(r??``)?.[1];return i!==void 0&&Number(i)>=2}async function S(e,t){for(let r of t)if(await C(n.join(e,r)))return!0;return!1}async function C(e){try{return await r.access(e),!0}catch(e){if((typeof e==`object`&&e&&`code`in e?e.code:void 0)!==`ENOENT`)throw e;return!1}}async function w(e){try{let t=JSON.parse(await r.readFile(e,`utf8`));return typeof t==`object`&&t&&!Array.isArray(t)?t:{}}catch(e){if(e instanceof SyntaxError||(typeof e==`object`&&e&&`code`in e?e.code:void 0)===`ENOENT`)return{};throw e}}async function T(e){for(let t of e.projectFilePaths??a[e.packageManager])await E(n.join(e.projectDir,t),n.join(e.runDir,t))}async function E(e,t){try{await r.lstat(e),await r.mkdir(n.dirname(t),{recursive:!0}),await r.cp(e,t,{force:!0,recursive:!0})}catch(e){if((typeof e==`object`&&e&&`code`in e?e.code:void 0)!==`ENOENT`)throw e}}export{T as copyPackageManagerProjectFiles,s as runCommandInTemporaryPackageManagerProject};
1
+ import{spawnWithLimits as e}from"./spawnWithLimits.js";import{forciblyRemoveDirectory as t}from"./temporaryProblemDirCopy.js";import n from"node:path";import r from"node:fs/promises";import i from"node:os";import{hasErrorCode as a,isRecord as o}from"@willbooster/shared-lib";const s={bun:[`package.json`,`bun.lock`,`bun.lockb`],cargo:[`Cargo.toml`,`Cargo.lock`],go:[`go.mod`,`go.sum`],gradle:[`build.gradle`,`build.gradle.kts`,`settings.gradle`,`settings.gradle.kts`,`gradle.properties`,`gradle.lockfile`,`buildscript-gradle.lockfile`,`gradle`,`gradlew`,`gradlew.bat`],maven:[`pom.xml`,`.mvn`,`mvnw`,`mvnw.cmd`],npm:[`package.json`,`package-lock.json`],pnpm:[`package.json`,`pnpm-lock.yaml`,`pnpm-workspace.yaml`],ruby:[`Gemfile`,`Gemfile.lock`,`.ruby-version`],uv:[`pyproject.toml`,`uv.lock`],yarn:[`package.json`,`yarn.lock`,`.yarnrc`,`.yarnrc.yml`,`.yarn`]},c={bun:p,cargo:m,go:h,gradle:g,maven:_,npm:v,pnpm:y,ruby:b,uv:x,yarn:S};async function l(a){let o=await r.mkdtemp(n.join(i.tmpdir(),a.tempDirPrefix??`exercode-`));try{await r.cp(a.cwd,o,{recursive:!0}),await D({packageManager:a.packageManager,projectDir:a.projectDir,runDir:o,projectFilePaths:a.projectFilePaths});let t=a.env?{...process.env,...a.env}:process.env,n=a.prepareDependencies===!1?void 0:await d(a.packageManager,o),i=typeof a.command==`function`?a.command({runDir:o}):a.command,s=a.outputLimitBytes??52428800,c;if(n&&(c=await e(n,{cwd:o,env:t,outputLimitBytes:s,stdin:``,timeLimitSeconds:a.timeLimitSeconds}),f(c)))return u({options:a,result:c});let l=a.timeLimitSeconds-(c?.timeSeconds??0);if(l<=0)return{stdin:a.stdin??``,stdout:c?.stdout??``,stderr:c?.stderr??``,status:0,timeSeconds:a.timeLimitSeconds+.001,cpuTimeSeconds:c?.cpuTimeSeconds??0,memoryBytes:c?.memoryBytes??0,timedOut:!0,signal:c?.signal,outputLimitExceeded:!1};let p=await e(i,{cwd:o,env:t,outputLimitBytes:s,stdin:a.stdin??``,timeLimitSeconds:l});return u(c?{options:a,result:{...p,timeSeconds:c.timeSeconds+p.timeSeconds,cpuTimeSeconds:c.cpuTimeSeconds+p.cpuTimeSeconds,memoryBytes:Math.max(c.memoryBytes,p.memoryBytes)}}:{options:a,result:p})}finally{await t(o)}}function u(e){return{stdin:e.options.stdin??``,stdout:e.result.stdout,stderr:e.result.stderr,status:e.result.timedOut||e.result.outputLimitExceeded?0:e.result.status,timeSeconds:e.result.timedOut?e.options.timeLimitSeconds+.001:e.result.timeSeconds,cpuTimeSeconds:e.result.cpuTimeSeconds,memoryBytes:e.result.memoryBytes,timedOut:e.result.timedOut,signal:e.result.signal,outputLimitExceeded:e.result.outputLimitExceeded}}function d(e,t){return c[e](t)}function f(e){return e.status!==0||e.timedOut||e.outputLimitExceeded}async function p(e){if(await T(n.join(e,`package.json`)))return await w(e,[`bun.lock`,`bun.lockb`])?[`bun`,`install`,`--frozen-lockfile`,`--silent`]:[`bun`,`install`,`--silent`]}async function m(e){if(await T(n.join(e,`Cargo.toml`)))return await T(n.join(e,`Cargo.lock`))?[`cargo`,`fetch`,`--locked`]:[`cargo`,`fetch`]}async function h(e){if(await T(n.join(e,`go.mod`)))return[`go`,`mod`,`download`]}async function g(e){if(!await w(e,[`build.gradle`,`build.gradle.kts`,`settings.gradle`,`settings.gradle.kts`,`gradlew`,`gradlew.bat`]))return;let t=[`--no-daemon`,`--quiet`,`dependencies`];return process.platform===`win32`?await T(n.join(e,`gradlew.bat`))?[`cmd.exe`,`/c`,`gradlew.bat`,...t]:[`gradle`,...t]:await T(n.join(e,`gradlew`))?[`sh`,`./gradlew`,...t]:[`gradle`,...t]}async function _(e){if(!await T(n.join(e,`pom.xml`)))return;let t=[`-q`,`dependency:go-offline`];return process.platform===`win32`?await T(n.join(e,`mvnw.cmd`))?[`cmd.exe`,`/c`,`mvnw.cmd`,...t]:[`mvn`,...t]:await T(n.join(e,`mvnw`))?[`sh`,`./mvnw`,...t]:[`mvn`,...t]}async function v(e){if(await T(n.join(e,`package.json`)))return await T(n.join(e,`package-lock.json`))?[`npm`,`ci`,`--silent`]:[`npm`,`install`,`--silent`]}async function y(e){if(await T(n.join(e,`package.json`)))return await T(n.join(e,`pnpm-lock.yaml`))?[`pnpm`,`install`,`--frozen-lockfile`,`--silent`]:[`pnpm`,`install`,`--silent`]}async function b(e){if(await T(n.join(e,`Gemfile`)))return await T(n.join(e,`Gemfile.lock`))?[`bundle`,`install`,`--frozen`,`--quiet`]:[`bundle`,`install`,`--quiet`]}async function x(){}async function S(e){if(!await T(n.join(e,`package.json`)))return;let t=await C(e),r=await T(n.join(e,`yarn.lock`));return t?r?[`yarn`,`install`,`--immutable`]:[`yarn`,`install`]:r?[`yarn`,`install`,`--frozen-lockfile`,`--silent`]:[`yarn`,`install`,`--silent`]}async function C(e){if(await T(n.join(e,`.yarnrc.yml`)))return!0;let t=await E(n.join(e,`package.json`)),r=typeof t.packageManager==`string`?t.packageManager:void 0,i=/^yarn@(\d+)/.exec(r??``)?.[1];return i!==void 0&&Number(i)>=2}async function w(e,t){for(let r of t)if(await T(n.join(e,r)))return!0;return!1}async function T(e){try{return await r.access(e),!0}catch(e){if(!a(e,`ENOENT`))throw e;return!1}}async function E(e){try{let t=JSON.parse(await r.readFile(e,`utf8`));return o(t)?t:{}}catch(e){if(e instanceof SyntaxError||a(e,`ENOENT`))return{};throw e}}async function D(e){for(let t of e.projectFilePaths??s[e.packageManager])await O(n.join(e.projectDir,t),n.join(e.runDir,t))}async function O(e,t){try{await r.lstat(e),await r.mkdir(n.dirname(t),{recursive:!0}),await r.cp(e,t,{force:!0,recursive:!0})}catch(e){if(!a(e,`ENOENT`))throw e}}export{D as copyPackageManagerProjectFiles,l as runCommandInTemporaryPackageManagerProject};
2
2
  //# sourceMappingURL=runCommandInTemporaryPackageManagerProject.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"runCommandInTemporaryPackageManagerProject.js","names":[],"sources":["../../src/helpers/runCommandInTemporaryPackageManagerProject.ts"],"sourcesContent":["import fs from 'node:fs/promises';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport { spawnWithLimits, type SpawnWithLimitsResult } from './spawnWithLimits.js';\nimport { forciblyRemoveDirectory } from './temporaryProblemDirCopy.js';\n\nexport type PackageManager = 'bun' | 'cargo' | 'go' | 'gradle' | 'maven' | 'npm' | 'pnpm' | 'ruby' | 'uv' | 'yarn';\ntype PackageManagerInstallCommand = readonly [string, ...string[]];\n\nexport interface PackageManagerCommandRunResult {\n stdin: string;\n stdout: string;\n stderr: string;\n status: number | undefined;\n timeSeconds: number;\n /** See `SpawnWithLimitsResult.cpuTimeSeconds`; the install's CPU time counts too. */\n cpuTimeSeconds: number;\n memoryBytes: number;\n timedOut: boolean;\n signal: NodeJS.Signals | undefined;\n outputLimitExceeded: boolean;\n}\n\nexport interface RunCommandInTemporaryPackageManagerProjectOptions {\n cwd: string;\n projectDir: string;\n packageManager: PackageManager;\n command: readonly [string, ...string[]] | ((context: { runDir: string }) => readonly [string, ...string[]]);\n /**\n * Set to false when `command` prepares the dependencies it needs.\n * Defaults to true.\n */\n prepareDependencies?: boolean;\n stdin?: string;\n env?: NodeJS.ProcessEnv;\n timeLimitSeconds: number;\n outputLimitBytes?: number;\n tempDirPrefix?: string;\n projectFilePaths?: readonly string[];\n}\n\nconst packageManagerProjectFilePaths = {\n bun: ['package.json', 'bun.lock', 'bun.lockb'],\n cargo: ['Cargo.toml', 'Cargo.lock'],\n go: ['go.mod', 'go.sum'],\n gradle: [\n 'build.gradle',\n 'build.gradle.kts',\n 'settings.gradle',\n 'settings.gradle.kts',\n 'gradle.properties',\n 'gradle.lockfile',\n 'buildscript-gradle.lockfile',\n 'gradle',\n 'gradlew',\n 'gradlew.bat',\n ],\n maven: ['pom.xml', '.mvn', 'mvnw', 'mvnw.cmd'],\n npm: ['package.json', 'package-lock.json'],\n pnpm: ['package.json', 'pnpm-lock.yaml', 'pnpm-workspace.yaml'],\n ruby: ['Gemfile', 'Gemfile.lock', '.ruby-version'],\n uv: ['pyproject.toml', 'uv.lock'],\n yarn: ['package.json', 'yarn.lock', '.yarnrc', '.yarnrc.yml', '.yarn'],\n} as const satisfies Record<PackageManager, readonly string[]>;\n\nconst packageManagerInstallCommandResolvers = {\n bun: resolveBunInstallCommand,\n cargo: resolveCargoInstallCommand,\n go: resolveGoInstallCommand,\n gradle: resolveGradleInstallCommand,\n maven: resolveMavenInstallCommand,\n npm: resolveNpmInstallCommand,\n pnpm: resolvePnpmInstallCommand,\n ruby: resolveRubyInstallCommand,\n uv: resolveUvInstallCommand,\n yarn: resolveYarnInstallCommand,\n} as const satisfies Record<PackageManager, (runDir: string) => Promise<PackageManagerInstallCommand | undefined>>;\n\nconst defaultOutputLimitBytes = 50 * 1024 * 1024;\n\n/**\n * Copies a submission directory to a temporary directory, overlays package\n * manager project files from the problem directory, prepares dependencies,\n * runs a command, and then removes the temporary directory.\n */\nexport async function runCommandInTemporaryPackageManagerProject(\n options: RunCommandInTemporaryPackageManagerProjectOptions\n): Promise<PackageManagerCommandRunResult> {\n const runDir = await fs.mkdtemp(path.join(os.tmpdir(), options.tempDirPrefix ?? 'exercode-'));\n try {\n await fs.cp(options.cwd, runDir, { recursive: true });\n await copyPackageManagerProjectFiles({\n packageManager: options.packageManager,\n projectDir: options.projectDir,\n runDir,\n projectFilePaths: options.projectFilePaths,\n });\n\n const env = options.env ? { ...process.env, ...options.env } : process.env;\n const installCommand =\n options.prepareDependencies === false ? undefined : await resolveInstallCommand(options.packageManager, runDir);\n const command = typeof options.command === 'function' ? options.command({ runDir }) : options.command;\n const outputLimitBytes = options.outputLimitBytes ?? defaultOutputLimitBytes;\n let installResult: SpawnWithLimitsResult | undefined;\n\n if (installCommand) {\n installResult = await spawnWithLimits(installCommand, {\n cwd: runDir,\n env,\n outputLimitBytes,\n stdin: '',\n timeLimitSeconds: options.timeLimitSeconds,\n });\n if (isFailedSpawnResult(installResult)) {\n return toPackageManagerCommandRunResult({\n options,\n result: installResult,\n });\n }\n }\n\n // The install's measured time excludes the grace spent releasing pipes after it exited.\n const remainingTimeLimitSeconds = options.timeLimitSeconds - (installResult?.timeSeconds ?? 0);\n if (remainingTimeLimitSeconds <= 0) {\n return {\n stdin: options.stdin ?? '',\n stdout: installResult?.stdout ?? '',\n stderr: installResult?.stderr ?? '',\n status: 0,\n timeSeconds: options.timeLimitSeconds + 1e-3,\n cpuTimeSeconds: installResult?.cpuTimeSeconds ?? 0,\n memoryBytes: installResult?.memoryBytes ?? 0,\n timedOut: true,\n signal: installResult?.signal,\n outputLimitExceeded: false,\n };\n }\n\n const result = await spawnWithLimits(command, {\n cwd: runDir,\n env,\n outputLimitBytes,\n stdin: options.stdin ?? '',\n timeLimitSeconds: remainingTimeLimitSeconds,\n });\n\n if (installResult) {\n return toPackageManagerCommandRunResult({\n options,\n result: {\n ...result,\n timeSeconds: installResult.timeSeconds + result.timeSeconds,\n cpuTimeSeconds: installResult.cpuTimeSeconds + result.cpuTimeSeconds,\n memoryBytes: Math.max(installResult.memoryBytes, result.memoryBytes),\n },\n });\n }\n\n return toPackageManagerCommandRunResult({ options, result });\n } finally {\n // The command may have left permission-locked entries (e.g. a mode-000 directory).\n await forciblyRemoveDirectory(runDir);\n }\n}\n\nfunction toPackageManagerCommandRunResult(context: {\n options: RunCommandInTemporaryPackageManagerProjectOptions;\n result: SpawnWithLimitsResult;\n}): PackageManagerCommandRunResult {\n return {\n stdin: context.options.stdin ?? '',\n stdout: context.result.stdout,\n stderr: context.result.stderr,\n status: context.result.timedOut || context.result.outputLimitExceeded ? 0 : context.result.status,\n timeSeconds: context.result.timedOut ? context.options.timeLimitSeconds + 1e-3 : context.result.timeSeconds,\n cpuTimeSeconds: context.result.cpuTimeSeconds,\n memoryBytes: context.result.memoryBytes,\n timedOut: context.result.timedOut,\n signal: context.result.signal,\n outputLimitExceeded: context.result.outputLimitExceeded,\n };\n}\n\nfunction resolveInstallCommand(\n packageManager: PackageManager,\n runDir: string\n): Promise<PackageManagerInstallCommand | undefined> {\n return packageManagerInstallCommandResolvers[packageManager](runDir);\n}\n\nfunction isFailedSpawnResult(result: SpawnWithLimitsResult): boolean {\n return result.status !== 0 || result.timedOut || result.outputLimitExceeded;\n}\n\nasync function resolveBunInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'package.json')))) return undefined;\n // Bun supports --silent and it keeps successful preparation output out of judge output buffers.\n return (await hasAnyPath(runDir, ['bun.lock', 'bun.lockb']))\n ? ['bun', 'install', '--frozen-lockfile', '--silent']\n : ['bun', 'install', '--silent'];\n}\n\nasync function resolveCargoInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'Cargo.toml')))) return undefined;\n return (await pathExists(path.join(runDir, 'Cargo.lock'))) ? ['cargo', 'fetch', '--locked'] : ['cargo', 'fetch'];\n}\n\nasync function resolveGoInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'go.mod')))) return undefined;\n return ['go', 'mod', 'download'];\n}\n\nasync function resolveGradleInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (\n !(await hasAnyPath(runDir, [\n 'build.gradle',\n 'build.gradle.kts',\n 'settings.gradle',\n 'settings.gradle.kts',\n 'gradlew',\n 'gradlew.bat',\n ]))\n )\n return undefined;\n const args = ['--no-daemon', '--quiet', 'dependencies'] as const;\n if (process.platform === 'win32') {\n return (await pathExists(path.join(runDir, 'gradlew.bat')))\n ? ['cmd.exe', '/c', 'gradlew.bat', ...args]\n : ['gradle', ...args];\n }\n return (await pathExists(path.join(runDir, 'gradlew'))) ? ['sh', './gradlew', ...args] : ['gradle', ...args];\n}\n\nasync function resolveMavenInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'pom.xml')))) return undefined;\n const args = ['-q', 'dependency:go-offline'] as const;\n if (process.platform === 'win32') {\n return (await pathExists(path.join(runDir, 'mvnw.cmd')))\n ? ['cmd.exe', '/c', 'mvnw.cmd', ...args]\n : ['mvn', ...args];\n }\n return (await pathExists(path.join(runDir, 'mvnw'))) ? ['sh', './mvnw', ...args] : ['mvn', ...args];\n}\n\nasync function resolveNpmInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'package.json')))) return undefined;\n return (await pathExists(path.join(runDir, 'package-lock.json')))\n ? ['npm', 'ci', '--silent']\n : ['npm', 'install', '--silent'];\n}\n\nasync function resolvePnpmInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'package.json')))) return undefined;\n return (await pathExists(path.join(runDir, 'pnpm-lock.yaml')))\n ? ['pnpm', 'install', '--frozen-lockfile', '--silent']\n : ['pnpm', 'install', '--silent'];\n}\n\nasync function resolveRubyInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'Gemfile')))) return undefined;\n return (await pathExists(path.join(runDir, 'Gemfile.lock')))\n ? ['bundle', 'install', '--frozen', '--quiet']\n : ['bundle', 'install', '--quiet'];\n}\n\nasync function resolveUvInstallCommand(): Promise<undefined> {\n return undefined;\n}\n\nasync function resolveYarnInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'package.json')))) return undefined;\n const isBerry = await isYarnBerryProject(runDir);\n const hasLockfile = await pathExists(path.join(runDir, 'yarn.lock'));\n if (isBerry) return hasLockfile ? ['yarn', 'install', '--immutable'] : ['yarn', 'install'];\n return hasLockfile ? ['yarn', 'install', '--frozen-lockfile', '--silent'] : ['yarn', 'install', '--silent'];\n}\n\nasync function isYarnBerryProject(runDir: string): Promise<boolean> {\n if (await pathExists(path.join(runDir, '.yarnrc.yml'))) return true;\n\n const packageJson = await readJson(path.join(runDir, 'package.json'));\n const packageManager = typeof packageJson.packageManager === 'string' ? packageJson.packageManager : undefined;\n const yarnMajorVersion = /^yarn@(\\d+)/.exec(packageManager ?? '')?.[1];\n return yarnMajorVersion !== undefined && Number(yarnMajorVersion) >= 2;\n}\n\nasync function hasAnyPath(directoryPath: string, relativePaths: readonly string[]): Promise<boolean> {\n for (const relativePath of relativePaths) {\n if (await pathExists(path.join(directoryPath, relativePath))) return true;\n }\n return false;\n}\n\nasync function pathExists(filePath: string): Promise<boolean> {\n try {\n await fs.access(filePath);\n return true;\n } catch (error) {\n const code =\n typeof error === 'object' && error !== null && 'code' in error ? (error as { code: unknown }).code : undefined;\n if (code !== 'ENOENT') throw error;\n return false;\n }\n}\n\nasync function readJson(filePath: string): Promise<Record<string, unknown>> {\n try {\n const parsed = JSON.parse(await fs.readFile(filePath, 'utf8')) as unknown;\n return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : {};\n } catch (error) {\n if (error instanceof SyntaxError) return {};\n const code =\n typeof error === 'object' && error !== null && 'code' in error ? (error as { code: unknown }).code : undefined;\n if (code === 'ENOENT') return {};\n throw error;\n }\n}\n\nexport async function copyPackageManagerProjectFiles(options: {\n packageManager: PackageManager;\n projectDir: string;\n runDir: string;\n projectFilePaths?: readonly string[];\n}): Promise<void> {\n for (const projectFilePath of options.projectFilePaths ?? packageManagerProjectFilePaths[options.packageManager]) {\n await copyPathIfExists(path.join(options.projectDir, projectFilePath), path.join(options.runDir, projectFilePath));\n }\n}\n\nasync function copyPathIfExists(sourcePath: string, destinationPath: string): Promise<void> {\n try {\n // Before touching the destination: creating the parent levels must not happen for a project\n // file the problem does not even ship.\n await fs.lstat(sourcePath);\n await fs.mkdir(path.dirname(destinationPath), { recursive: true });\n await fs.cp(sourcePath, destinationPath, { force: true, recursive: true });\n } catch (error) {\n const code =\n typeof error === 'object' && error !== null && 'code' in error ? (error as { code: unknown }).code : undefined;\n if (code !== 'ENOENT') throw error;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"runCommandInTemporaryPackageManagerProject.js","names":[],"sources":["../../src/helpers/runCommandInTemporaryPackageManagerProject.ts"],"sourcesContent":["import fs from 'node:fs/promises';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { hasErrorCode, isRecord } from '@willbooster/shared-lib';\n\nimport { spawnWithLimits, type SpawnWithLimitsResult } from './spawnWithLimits.js';\nimport { forciblyRemoveDirectory } from './temporaryProblemDirCopy.js';\n\nexport type PackageManager = 'bun' | 'cargo' | 'go' | 'gradle' | 'maven' | 'npm' | 'pnpm' | 'ruby' | 'uv' | 'yarn';\ntype PackageManagerInstallCommand = readonly [string, ...string[]];\n\nexport interface PackageManagerCommandRunResult {\n stdin: string;\n stdout: string;\n stderr: string;\n status: number | undefined;\n timeSeconds: number;\n /** See `SpawnWithLimitsResult.cpuTimeSeconds`; the install's CPU time counts too. */\n cpuTimeSeconds: number;\n memoryBytes: number;\n timedOut: boolean;\n signal: NodeJS.Signals | undefined;\n outputLimitExceeded: boolean;\n}\n\nexport interface RunCommandInTemporaryPackageManagerProjectOptions {\n cwd: string;\n projectDir: string;\n packageManager: PackageManager;\n command: readonly [string, ...string[]] | ((context: { runDir: string }) => readonly [string, ...string[]]);\n /**\n * Set to false when `command` prepares the dependencies it needs.\n * Defaults to true.\n */\n prepareDependencies?: boolean;\n stdin?: string;\n env?: NodeJS.ProcessEnv;\n timeLimitSeconds: number;\n outputLimitBytes?: number;\n tempDirPrefix?: string;\n projectFilePaths?: readonly string[];\n}\n\nconst packageManagerProjectFilePaths = {\n bun: ['package.json', 'bun.lock', 'bun.lockb'],\n cargo: ['Cargo.toml', 'Cargo.lock'],\n go: ['go.mod', 'go.sum'],\n gradle: [\n 'build.gradle',\n 'build.gradle.kts',\n 'settings.gradle',\n 'settings.gradle.kts',\n 'gradle.properties',\n 'gradle.lockfile',\n 'buildscript-gradle.lockfile',\n 'gradle',\n 'gradlew',\n 'gradlew.bat',\n ],\n maven: ['pom.xml', '.mvn', 'mvnw', 'mvnw.cmd'],\n npm: ['package.json', 'package-lock.json'],\n pnpm: ['package.json', 'pnpm-lock.yaml', 'pnpm-workspace.yaml'],\n ruby: ['Gemfile', 'Gemfile.lock', '.ruby-version'],\n uv: ['pyproject.toml', 'uv.lock'],\n yarn: ['package.json', 'yarn.lock', '.yarnrc', '.yarnrc.yml', '.yarn'],\n} as const satisfies Record<PackageManager, readonly string[]>;\n\nconst packageManagerInstallCommandResolvers = {\n bun: resolveBunInstallCommand,\n cargo: resolveCargoInstallCommand,\n go: resolveGoInstallCommand,\n gradle: resolveGradleInstallCommand,\n maven: resolveMavenInstallCommand,\n npm: resolveNpmInstallCommand,\n pnpm: resolvePnpmInstallCommand,\n ruby: resolveRubyInstallCommand,\n uv: resolveUvInstallCommand,\n yarn: resolveYarnInstallCommand,\n} as const satisfies Record<PackageManager, (runDir: string) => Promise<PackageManagerInstallCommand | undefined>>;\n\nconst defaultOutputLimitBytes = 50 * 1024 * 1024;\n\n/**\n * Copies a submission directory to a temporary directory, overlays package\n * manager project files from the problem directory, prepares dependencies,\n * runs a command, and then removes the temporary directory.\n */\nexport async function runCommandInTemporaryPackageManagerProject(\n options: RunCommandInTemporaryPackageManagerProjectOptions\n): Promise<PackageManagerCommandRunResult> {\n const runDir = await fs.mkdtemp(path.join(os.tmpdir(), options.tempDirPrefix ?? 'exercode-'));\n try {\n await fs.cp(options.cwd, runDir, { recursive: true });\n await copyPackageManagerProjectFiles({\n packageManager: options.packageManager,\n projectDir: options.projectDir,\n runDir,\n projectFilePaths: options.projectFilePaths,\n });\n\n const env = options.env ? { ...process.env, ...options.env } : process.env;\n const installCommand =\n options.prepareDependencies === false ? undefined : await resolveInstallCommand(options.packageManager, runDir);\n const command = typeof options.command === 'function' ? options.command({ runDir }) : options.command;\n const outputLimitBytes = options.outputLimitBytes ?? defaultOutputLimitBytes;\n let installResult: SpawnWithLimitsResult | undefined;\n\n if (installCommand) {\n installResult = await spawnWithLimits(installCommand, {\n cwd: runDir,\n env,\n outputLimitBytes,\n stdin: '',\n timeLimitSeconds: options.timeLimitSeconds,\n });\n if (isFailedSpawnResult(installResult)) {\n return toPackageManagerCommandRunResult({\n options,\n result: installResult,\n });\n }\n }\n\n // The install's measured time excludes the grace spent releasing pipes after it exited.\n const remainingTimeLimitSeconds = options.timeLimitSeconds - (installResult?.timeSeconds ?? 0);\n if (remainingTimeLimitSeconds <= 0) {\n return {\n stdin: options.stdin ?? '',\n stdout: installResult?.stdout ?? '',\n stderr: installResult?.stderr ?? '',\n status: 0,\n timeSeconds: options.timeLimitSeconds + 1e-3,\n cpuTimeSeconds: installResult?.cpuTimeSeconds ?? 0,\n memoryBytes: installResult?.memoryBytes ?? 0,\n timedOut: true,\n signal: installResult?.signal,\n outputLimitExceeded: false,\n };\n }\n\n const result = await spawnWithLimits(command, {\n cwd: runDir,\n env,\n outputLimitBytes,\n stdin: options.stdin ?? '',\n timeLimitSeconds: remainingTimeLimitSeconds,\n });\n\n if (installResult) {\n return toPackageManagerCommandRunResult({\n options,\n result: {\n ...result,\n timeSeconds: installResult.timeSeconds + result.timeSeconds,\n cpuTimeSeconds: installResult.cpuTimeSeconds + result.cpuTimeSeconds,\n memoryBytes: Math.max(installResult.memoryBytes, result.memoryBytes),\n },\n });\n }\n\n return toPackageManagerCommandRunResult({ options, result });\n } finally {\n // The command may have left permission-locked entries (e.g. a mode-000 directory).\n await forciblyRemoveDirectory(runDir);\n }\n}\n\nfunction toPackageManagerCommandRunResult(context: {\n options: RunCommandInTemporaryPackageManagerProjectOptions;\n result: SpawnWithLimitsResult;\n}): PackageManagerCommandRunResult {\n return {\n stdin: context.options.stdin ?? '',\n stdout: context.result.stdout,\n stderr: context.result.stderr,\n status: context.result.timedOut || context.result.outputLimitExceeded ? 0 : context.result.status,\n timeSeconds: context.result.timedOut ? context.options.timeLimitSeconds + 1e-3 : context.result.timeSeconds,\n cpuTimeSeconds: context.result.cpuTimeSeconds,\n memoryBytes: context.result.memoryBytes,\n timedOut: context.result.timedOut,\n signal: context.result.signal,\n outputLimitExceeded: context.result.outputLimitExceeded,\n };\n}\n\nfunction resolveInstallCommand(\n packageManager: PackageManager,\n runDir: string\n): Promise<PackageManagerInstallCommand | undefined> {\n return packageManagerInstallCommandResolvers[packageManager](runDir);\n}\n\nfunction isFailedSpawnResult(result: SpawnWithLimitsResult): boolean {\n return result.status !== 0 || result.timedOut || result.outputLimitExceeded;\n}\n\nasync function resolveBunInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'package.json')))) return undefined;\n // Bun supports --silent and it keeps successful preparation output out of judge output buffers.\n return (await hasAnyPath(runDir, ['bun.lock', 'bun.lockb']))\n ? ['bun', 'install', '--frozen-lockfile', '--silent']\n : ['bun', 'install', '--silent'];\n}\n\nasync function resolveCargoInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'Cargo.toml')))) return undefined;\n return (await pathExists(path.join(runDir, 'Cargo.lock'))) ? ['cargo', 'fetch', '--locked'] : ['cargo', 'fetch'];\n}\n\nasync function resolveGoInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'go.mod')))) return undefined;\n return ['go', 'mod', 'download'];\n}\n\nasync function resolveGradleInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (\n !(await hasAnyPath(runDir, [\n 'build.gradle',\n 'build.gradle.kts',\n 'settings.gradle',\n 'settings.gradle.kts',\n 'gradlew',\n 'gradlew.bat',\n ]))\n )\n return undefined;\n const args = ['--no-daemon', '--quiet', 'dependencies'] as const;\n if (process.platform === 'win32') {\n return (await pathExists(path.join(runDir, 'gradlew.bat')))\n ? ['cmd.exe', '/c', 'gradlew.bat', ...args]\n : ['gradle', ...args];\n }\n return (await pathExists(path.join(runDir, 'gradlew'))) ? ['sh', './gradlew', ...args] : ['gradle', ...args];\n}\n\nasync function resolveMavenInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'pom.xml')))) return undefined;\n const args = ['-q', 'dependency:go-offline'] as const;\n if (process.platform === 'win32') {\n return (await pathExists(path.join(runDir, 'mvnw.cmd')))\n ? ['cmd.exe', '/c', 'mvnw.cmd', ...args]\n : ['mvn', ...args];\n }\n return (await pathExists(path.join(runDir, 'mvnw'))) ? ['sh', './mvnw', ...args] : ['mvn', ...args];\n}\n\nasync function resolveNpmInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'package.json')))) return undefined;\n return (await pathExists(path.join(runDir, 'package-lock.json')))\n ? ['npm', 'ci', '--silent']\n : ['npm', 'install', '--silent'];\n}\n\nasync function resolvePnpmInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'package.json')))) return undefined;\n return (await pathExists(path.join(runDir, 'pnpm-lock.yaml')))\n ? ['pnpm', 'install', '--frozen-lockfile', '--silent']\n : ['pnpm', 'install', '--silent'];\n}\n\nasync function resolveRubyInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'Gemfile')))) return undefined;\n return (await pathExists(path.join(runDir, 'Gemfile.lock')))\n ? ['bundle', 'install', '--frozen', '--quiet']\n : ['bundle', 'install', '--quiet'];\n}\n\nasync function resolveUvInstallCommand(): Promise<undefined> {\n return undefined;\n}\n\nasync function resolveYarnInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'package.json')))) return undefined;\n const isBerry = await isYarnBerryProject(runDir);\n const hasLockfile = await pathExists(path.join(runDir, 'yarn.lock'));\n if (isBerry) return hasLockfile ? ['yarn', 'install', '--immutable'] : ['yarn', 'install'];\n return hasLockfile ? ['yarn', 'install', '--frozen-lockfile', '--silent'] : ['yarn', 'install', '--silent'];\n}\n\nasync function isYarnBerryProject(runDir: string): Promise<boolean> {\n if (await pathExists(path.join(runDir, '.yarnrc.yml'))) return true;\n\n const packageJson = await readJson(path.join(runDir, 'package.json'));\n const packageManager = typeof packageJson.packageManager === 'string' ? packageJson.packageManager : undefined;\n const yarnMajorVersion = /^yarn@(\\d+)/.exec(packageManager ?? '')?.[1];\n return yarnMajorVersion !== undefined && Number(yarnMajorVersion) >= 2;\n}\n\nasync function hasAnyPath(directoryPath: string, relativePaths: readonly string[]): Promise<boolean> {\n for (const relativePath of relativePaths) {\n if (await pathExists(path.join(directoryPath, relativePath))) return true;\n }\n return false;\n}\n\nasync function pathExists(filePath: string): Promise<boolean> {\n try {\n await fs.access(filePath);\n return true;\n } catch (error) {\n if (!hasErrorCode(error, 'ENOENT')) throw error;\n return false;\n }\n}\n\nasync function readJson(filePath: string): Promise<Record<string, unknown>> {\n try {\n const parsed = JSON.parse(await fs.readFile(filePath, 'utf8')) as unknown;\n return isRecord(parsed) ? parsed : {};\n } catch (error) {\n if (error instanceof SyntaxError) return {};\n if (hasErrorCode(error, 'ENOENT')) return {};\n throw error;\n }\n}\n\nexport async function copyPackageManagerProjectFiles(options: {\n packageManager: PackageManager;\n projectDir: string;\n runDir: string;\n projectFilePaths?: readonly string[];\n}): Promise<void> {\n for (const projectFilePath of options.projectFilePaths ?? packageManagerProjectFilePaths[options.packageManager]) {\n await copyPathIfExists(path.join(options.projectDir, projectFilePath), path.join(options.runDir, projectFilePath));\n }\n}\n\nasync function copyPathIfExists(sourcePath: string, destinationPath: string): Promise<void> {\n try {\n // Before touching the destination: creating the parent levels must not happen for a project\n // file the problem does not even ship.\n await fs.lstat(sourcePath);\n await fs.mkdir(path.dirname(destinationPath), { recursive: true });\n await fs.cp(sourcePath, destinationPath, { force: true, recursive: true });\n } catch (error) {\n if (!hasErrorCode(error, 'ENOENT')) throw error;\n }\n}\n"],"mappings":"mRA2CA,MAAM,EAAiC,CACrC,IAAK,CAAC,eAAgB,WAAY,WAAW,EAC7C,MAAO,CAAC,aAAc,YAAY,EAClC,GAAI,CAAC,SAAU,QAAQ,EACvB,OAAQ,CACN,eACA,mBACA,kBACA,sBACA,oBACA,kBACA,8BACA,SACA,UACA,aACF,EACA,MAAO,CAAC,UAAW,OAAQ,OAAQ,UAAU,EAC7C,IAAK,CAAC,eAAgB,mBAAmB,EACzC,KAAM,CAAC,eAAgB,iBAAkB,qBAAqB,EAC9D,KAAM,CAAC,UAAW,eAAgB,eAAe,EACjD,GAAI,CAAC,iBAAkB,SAAS,EAChC,KAAM,CAAC,eAAgB,YAAa,UAAW,cAAe,OAAO,CACvE,EAEM,EAAwC,CAC5C,IAAK,EACL,MAAO,EACP,GAAI,EACJ,OAAQ,EACR,MAAO,EACP,IAAK,EACL,KAAM,EACN,KAAM,EACN,GAAI,EACJ,KAAM,CACR,EASA,eAAsB,EACpB,EACyC,CACzC,IAAM,EAAS,MAAM,EAAG,QAAQ,EAAK,KAAK,EAAG,OAAO,EAAG,EAAQ,eAAiB,WAAW,CAAC,EAC5F,GAAI,CACF,MAAM,EAAG,GAAG,EAAQ,IAAK,EAAQ,CAAE,UAAW,EAAK,CAAC,EACpD,MAAM,EAA+B,CACnC,eAAgB,EAAQ,eACxB,WAAY,EAAQ,WACpB,SACA,iBAAkB,EAAQ,gBAC5B,CAAC,EAED,IAAM,EAAM,EAAQ,IAAM,CAAE,GAAG,QAAQ,IAAK,GAAG,EAAQ,GAAI,EAAI,QAAQ,IACjE,EACJ,EAAQ,sBAAwB,GAAQ,IAAA,GAAY,MAAM,EAAsB,EAAQ,eAAgB,CAAM,EAC1G,EAAU,OAAO,EAAQ,SAAY,WAAa,EAAQ,QAAQ,CAAE,QAAO,CAAC,EAAI,EAAQ,QACxF,EAAmB,EAAQ,kBAAoB,SACjD,EAEJ,GAAI,IACF,EAAgB,MAAM,EAAgB,EAAgB,CACpD,IAAK,EACL,MACA,mBACA,MAAO,GACP,iBAAkB,EAAQ,gBAC5B,CAAC,EACG,EAAoB,CAAa,GACnC,OAAO,EAAiC,CACtC,UACA,OAAQ,CACV,CAAC,EAKL,IAAM,EAA4B,EAAQ,kBAAoB,GAAe,aAAe,GAC5F,GAAI,GAA6B,EAC/B,MAAO,CACL,MAAO,EAAQ,OAAS,GACxB,OAAQ,GAAe,QAAU,GACjC,OAAQ,GAAe,QAAU,GACjC,OAAQ,EACR,YAAa,EAAQ,iBAAmB,KACxC,eAAgB,GAAe,gBAAkB,EACjD,YAAa,GAAe,aAAe,EAC3C,SAAU,GACV,OAAQ,GAAe,OACvB,oBAAqB,EACvB,EAGF,IAAM,EAAS,MAAM,EAAgB,EAAS,CAC5C,IAAK,EACL,MACA,mBACA,MAAO,EAAQ,OAAS,GACxB,iBAAkB,CACpB,CAAC,EAcD,OAXS,EADL,EACsC,CACtC,UACA,OAAQ,CACN,GAAG,EACH,YAAa,EAAc,YAAc,EAAO,YAChD,eAAgB,EAAc,eAAiB,EAAO,eACtD,YAAa,KAAK,IAAI,EAAc,YAAa,EAAO,WAAW,CACrE,CACF,EAGsC,CAAE,UAAS,QAAO,CAHvD,CAIL,QAAU,CAER,MAAM,EAAwB,CAAM,CACtC,CACF,CAEA,SAAS,EAAiC,EAGP,CACjC,MAAO,CACL,MAAO,EAAQ,QAAQ,OAAS,GAChC,OAAQ,EAAQ,OAAO,OACvB,OAAQ,EAAQ,OAAO,OACvB,OAAQ,EAAQ,OAAO,UAAY,EAAQ,OAAO,oBAAsB,EAAI,EAAQ,OAAO,OAC3F,YAAa,EAAQ,OAAO,SAAW,EAAQ,QAAQ,iBAAmB,KAAO,EAAQ,OAAO,YAChG,eAAgB,EAAQ,OAAO,eAC/B,YAAa,EAAQ,OAAO,YAC5B,SAAU,EAAQ,OAAO,SACzB,OAAQ,EAAQ,OAAO,OACvB,oBAAqB,EAAQ,OAAO,mBACtC,CACF,CAEA,SAAS,EACP,EACA,EACmD,CACnD,OAAO,EAAsC,EAAe,CAAC,CAAM,CACrE,CAEA,SAAS,EAAoB,EAAwC,CACnE,OAAO,EAAO,SAAW,GAAK,EAAO,UAAY,EAAO,mBAC1D,CAEA,eAAe,EAAyB,EAAmE,CACnG,SAAM,EAAW,EAAK,KAAK,EAAQ,cAAc,CAAC,EAExD,OAAQ,MAAM,EAAW,EAAQ,CAAC,WAAY,WAAW,CAAC,EACtD,CAAC,MAAO,UAAW,oBAAqB,UAAU,EAClD,CAAC,MAAO,UAAW,UAAU,CACnC,CAEA,eAAe,EAA2B,EAAmE,CACrG,SAAM,EAAW,EAAK,KAAK,EAAQ,YAAY,CAAC,EACtD,OAAQ,MAAM,EAAW,EAAK,KAAK,EAAQ,YAAY,CAAC,EAAK,CAAC,QAAS,QAAS,UAAU,EAAI,CAAC,QAAS,OAAO,CACjH,CAEA,eAAe,EAAwB,EAAmE,CAClG,SAAM,EAAW,EAAK,KAAK,EAAQ,QAAQ,CAAC,EAClD,MAAO,CAAC,KAAM,MAAO,UAAU,CACjC,CAEA,eAAe,EAA4B,EAAmE,CAC5G,GACE,CAAE,MAAM,EAAW,EAAQ,CACzB,eACA,mBACA,kBACA,sBACA,UACA,aACF,CAAC,EAED,OACF,IAAM,EAAO,CAAC,cAAe,UAAW,cAAc,EAMtD,OALI,QAAQ,WAAa,QACf,MAAM,EAAW,EAAK,KAAK,EAAQ,aAAa,CAAC,EACrD,CAAC,UAAW,KAAM,cAAe,GAAG,CAAI,EACxC,CAAC,SAAU,GAAG,CAAI,EAEhB,MAAM,EAAW,EAAK,KAAK,EAAQ,SAAS,CAAC,EAAK,CAAC,KAAM,YAAa,GAAG,CAAI,EAAI,CAAC,SAAU,GAAG,CAAI,CAC7G,CAEA,eAAe,EAA2B,EAAmE,CAC3G,GAAI,CAAE,MAAM,EAAW,EAAK,KAAK,EAAQ,SAAS,CAAC,EAAI,OACvD,IAAM,EAAO,CAAC,KAAM,uBAAuB,EAM3C,OALI,QAAQ,WAAa,QACf,MAAM,EAAW,EAAK,KAAK,EAAQ,UAAU,CAAC,EAClD,CAAC,UAAW,KAAM,WAAY,GAAG,CAAI,EACrC,CAAC,MAAO,GAAG,CAAI,EAEb,MAAM,EAAW,EAAK,KAAK,EAAQ,MAAM,CAAC,EAAK,CAAC,KAAM,SAAU,GAAG,CAAI,EAAI,CAAC,MAAO,GAAG,CAAI,CACpG,CAEA,eAAe,EAAyB,EAAmE,CACnG,SAAM,EAAW,EAAK,KAAK,EAAQ,cAAc,CAAC,EACxD,OAAQ,MAAM,EAAW,EAAK,KAAK,EAAQ,mBAAmB,CAAC,EAC3D,CAAC,MAAO,KAAM,UAAU,EACxB,CAAC,MAAO,UAAW,UAAU,CACnC,CAEA,eAAe,EAA0B,EAAmE,CACpG,SAAM,EAAW,EAAK,KAAK,EAAQ,cAAc,CAAC,EACxD,OAAQ,MAAM,EAAW,EAAK,KAAK,EAAQ,gBAAgB,CAAC,EACxD,CAAC,OAAQ,UAAW,oBAAqB,UAAU,EACnD,CAAC,OAAQ,UAAW,UAAU,CACpC,CAEA,eAAe,EAA0B,EAAmE,CACpG,SAAM,EAAW,EAAK,KAAK,EAAQ,SAAS,CAAC,EACnD,OAAQ,MAAM,EAAW,EAAK,KAAK,EAAQ,cAAc,CAAC,EACtD,CAAC,SAAU,UAAW,WAAY,SAAS,EAC3C,CAAC,SAAU,UAAW,SAAS,CACrC,CAEA,eAAe,GAA8C,CAE7D,CAEA,eAAe,EAA0B,EAAmE,CAC1G,GAAI,CAAE,MAAM,EAAW,EAAK,KAAK,EAAQ,cAAc,CAAC,EAAI,OAC5D,IAAM,EAAU,MAAM,EAAmB,CAAM,EACzC,EAAc,MAAM,EAAW,EAAK,KAAK,EAAQ,WAAW,CAAC,EAEnE,OADI,EAAgB,EAAc,CAAC,OAAQ,UAAW,aAAa,EAAI,CAAC,OAAQ,SAAS,EAClF,EAAc,CAAC,OAAQ,UAAW,oBAAqB,UAAU,EAAI,CAAC,OAAQ,UAAW,UAAU,CAC5G,CAEA,eAAe,EAAmB,EAAkC,CAClE,GAAI,MAAM,EAAW,EAAK,KAAK,EAAQ,aAAa,CAAC,EAAG,MAAO,GAE/D,IAAM,EAAc,MAAM,EAAS,EAAK,KAAK,EAAQ,cAAc,CAAC,EAC9D,EAAiB,OAAO,EAAY,gBAAmB,SAAW,EAAY,eAAiB,IAAA,GAC/F,EAAmB,cAAc,KAAK,GAAkB,EAAE,CAAC,GAAG,GACpE,OAAO,IAAqB,IAAA,IAAa,OAAO,CAAgB,GAAK,CACvE,CAEA,eAAe,EAAW,EAAuB,EAAoD,CACnG,IAAK,IAAM,KAAgB,EACzB,GAAI,MAAM,EAAW,EAAK,KAAK,EAAe,CAAY,CAAC,EAAG,MAAO,GAEvE,MAAO,EACT,CAEA,eAAe,EAAW,EAAoC,CAC5D,GAAI,CAEF,OADA,MAAM,EAAG,OAAO,CAAQ,EACjB,EACT,OAAS,EAAO,CACd,GAAI,CAAC,EAAa,EAAO,QAAQ,EAAG,MAAM,EAC1C,MAAO,EACT,CACF,CAEA,eAAe,EAAS,EAAoD,CAC1E,GAAI,CACF,IAAM,EAAS,KAAK,MAAM,MAAM,EAAG,SAAS,EAAU,MAAM,CAAC,EAC7D,OAAO,EAAS,CAAM,EAAI,EAAS,CAAC,CACtC,OAAS,EAAO,CAEd,GADI,aAAiB,aACjB,EAAa,EAAO,QAAQ,EAAG,MAAO,CAAC,EAC3C,MAAM,CACR,CACF,CAEA,eAAsB,EAA+B,EAKnC,CAChB,IAAK,IAAM,KAAmB,EAAQ,kBAAoB,EAA+B,EAAQ,gBAC/F,MAAM,EAAiB,EAAK,KAAK,EAAQ,WAAY,CAAe,EAAG,EAAK,KAAK,EAAQ,OAAQ,CAAe,CAAC,CAErH,CAEA,eAAe,EAAiB,EAAoB,EAAwC,CAC1F,GAAI,CAGF,MAAM,EAAG,MAAM,CAAU,EACzB,MAAM,EAAG,MAAM,EAAK,QAAQ,CAAe,EAAG,CAAE,UAAW,EAAK,CAAC,EACjE,MAAM,EAAG,GAAG,EAAY,EAAiB,CAAE,MAAO,GAAM,UAAW,EAAK,CAAC,CAC3E,OAAS,EAAO,CACd,GAAI,CAAC,EAAa,EAAO,QAAQ,EAAG,MAAM,CAC5C,CACF"}
@@ -1,2 +1,2 @@
1
- "use strict";const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("./temporaryProblemDirCopy.cjs");let n=require("node:child_process");n=e.__toESM(n,1);const r=new Set;let i=!1;function a(e,t){return o(),new Promise(i=>{let a=n.default.spawn(process.execPath,e,{cwd:t.cwd,detached:process.platform!==`win32`,env:t.tempRoot?{...t.env,TMPDIR:t.tempRoot}:t.env,stdio:[`ignore`,`pipe`,`pipe`]}),o=a.pid===void 0?void 0:{pid:a.pid,tempRoot:t.tempRoot};o&&r.add(o);let c=[],l=[],u=0,d,f=!1,p=!1,m=e=>{if(d!==void 0)return!1;d=e,a.stdout?.destroy(),a.stderr?.destroy();try{a.pid===void 0?a.kill(`SIGKILL`):s(a.pid)}catch{a.kill(`SIGKILL`)}return!0},h=setTimeout(()=>{f=m(`timed out after ${t.timeoutMs/1e3} seconds`)},t.timeoutMs),g=(e,n)=>{if(u+=e.byteLength,u>t.maxOutputBytes){m(`the harness printed more than ${t.maxOutputBytes/1024/1024} MB of output`);return}n.push(e)};a.stdout?.on(`data`,e=>g(e,c)),a.stderr?.on(`data`,e=>g(e,l));let _=(e,t,n)=>{p||(p=!0,clearTimeout(h),o&&r.delete(o),i({stdout:Buffer.concat(c).toString(`utf8`),stderr:Buffer.concat(l).toString(`utf8`),exitCode:e,signal:t,failureReason:d??(n?`failed to run the harness: ${n.message}`:void 0),timedOut:f}))};a.on(`error`,e=>_(void 0,void 0,e)),a.on(`close`,(e,t)=>_(e??void 0,t??void 0))})}function o(){if(!i){i=!0;for(let e of[`SIGINT`,`SIGTERM`])process.once(e,()=>{for(let e of r){try{s(e.pid)}catch{}e.tempRoot&&t.forciblyRemoveDirectorySync(e.tempRoot)}process.kill(process.pid,e)})}}function s(e){process.platform===`win32`?n.default.spawnSync(`taskkill`,[`/PID`,String(e),`/T`,`/F`],{stdio:`ignore`}):process.kill(-e,`SIGKILL`)}exports.runHarnessProcess=a;
1
+ "use strict";const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("./temporaryProblemDirCopy.cjs");let n=require("node:child_process");n=e.__toESM(n,1);const r=/* @__PURE__ */ new Set;let i=!1;function a(e,t){return o(),new Promise(i=>{let a=n.default.spawn(process.execPath,e,{cwd:t.cwd,detached:process.platform!==`win32`,env:t.tempRoot?{...t.env,TMPDIR:t.tempRoot}:t.env,stdio:[`ignore`,`pipe`,`pipe`]}),o=a.pid===void 0?void 0:{pid:a.pid,tempRoot:t.tempRoot};o&&r.add(o);let c=[],l=[],u=0,d,f=!1,p=!1,m=e=>{if(d!==void 0)return!1;d=e,a.stdout?.destroy(),a.stderr?.destroy();try{a.pid===void 0?a.kill(`SIGKILL`):s(a.pid)}catch{a.kill(`SIGKILL`)}return!0},h=setTimeout(()=>{f=m(`timed out after ${t.timeoutMs/1e3} seconds`)},t.timeoutMs),g=(e,n)=>{if(u+=e.byteLength,u>t.maxOutputBytes){m(`the harness printed more than ${t.maxOutputBytes/1024/1024} MB of output`);return}n.push(e)};a.stdout?.on(`data`,e=>g(e,c)),a.stderr?.on(`data`,e=>g(e,l));let _=(e,t,n)=>{p||(p=!0,clearTimeout(h),o&&r.delete(o),i({stdout:Buffer.concat(c).toString(`utf8`),stderr:Buffer.concat(l).toString(`utf8`),exitCode:e,signal:t,failureReason:d??(n?`failed to run the harness: ${n.message}`:void 0),timedOut:f}))};a.on(`error`,e=>_(void 0,void 0,e)),a.on(`close`,(e,t)=>_(e??void 0,t??void 0))})}function o(){if(!i){i=!0;for(let e of[`SIGINT`,`SIGTERM`])process.once(e,()=>{for(let e of r){try{s(e.pid)}catch{}e.tempRoot&&t.forciblyRemoveDirectorySync(e.tempRoot)}process.kill(process.pid,e)})}}function s(e){process.platform===`win32`?n.default.spawnSync(`taskkill`,[`/PID`,String(e),`/T`,`/F`],{stdio:`ignore`}):process.kill(-e,`SIGKILL`)}exports.runHarnessProcess=a;
2
2
  //# sourceMappingURL=runHarnessProcess.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"runHarnessProcess.cjs","names":["child_process"],"sources":["../../src/helpers/runHarnessProcess.ts"],"sourcesContent":["import child_process from 'node:child_process';\n\nimport { forciblyRemoveDirectorySync } from './temporaryProblemDirCopy.js';\n\nexport interface HarnessProcessOptions {\n cwd: string;\n env: NodeJS.ProcessEnv;\n timeoutMs: number;\n maxOutputBytes: number;\n /** A temporary copy to remove when the check itself is interrupted by a signal. */\n tempRoot?: string;\n}\n\nexport interface HarnessProcessResult {\n stdout: string;\n stderr: string;\n exitCode: number | undefined;\n /** The signal that terminated the harness when it did not exit on its own. */\n signal: NodeJS.Signals | undefined;\n /** Set when the run was killed by this helper (timeout or output cap) or could not be spawned. */\n failureReason: string | undefined;\n timedOut: boolean;\n}\n\ninterface LiveHarnessRun {\n pid: number;\n tempRoot: string | undefined;\n}\n\n// Detached harness groups no longer receive the terminal's SIGINT, so an interrupted run must\n// tear them down (and remove their temp copies) itself before exiting. The handlers stay installed\n// for the process lifetime: once the last run has settled they only iterate an empty set and re-raise\n// the signal, so removing them would change nothing observable.\nconst liveHarnessRuns = new Set<LiveHarnessRun>();\nlet signalHandlersInstalled = false;\n\n/**\n * Run a judge harness with `process.execPath` in its own process group, killing the whole group on\n * timeout or when the output cap is exceeded. The submissions a harness judges through\n * `spawnWithLimits` run in sessions of their own, out of that kill's reach; each wrapper's process\n * group is bounded by its own GNU `timeout` instead, while a descendant that moved to yet another\n * session may survive without stalling the harness.\n */\nexport function runHarnessProcess(\n commandArgs: readonly string[],\n options: HarnessProcessOptions\n): Promise<HarnessProcessResult> {\n installSignalHandlers();\n return new Promise((resolve) => {\n // process.execPath keeps the harness on the same bun executable regardless of PATH.\n const child = child_process.spawn(process.execPath, commandArgs, {\n cwd: options.cwd,\n detached: process.platform !== 'win32',\n // The parent removes this root even when SIGKILL prevents child cleanup.\n env: options.tempRoot ? { ...options.env, TMPDIR: options.tempRoot } : options.env,\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n const liveRun: LiveHarnessRun | undefined =\n child.pid === undefined ? undefined : { pid: child.pid, tempRoot: options.tempRoot };\n if (liveRun) liveHarnessRuns.add(liveRun);\n const stdoutChunks: Buffer[] = [];\n const stderrChunks: Buffer[] = [];\n let totalOutputBytes = 0;\n let failureReason: string | undefined;\n let timedOut = false;\n let settled = false;\n\n // Returns false when an earlier kill already recorded its reason, so callers can tell whether theirs won.\n const killProcessGroup = (reason: string): boolean => {\n if (failureReason !== undefined) return false;\n failureReason = reason;\n // Stop buffering immediately: OS pipe buffers can keep emitting data after the kill, and\n // destroyed streams also let `close` fire even if a stray grandchild inherited the pipes.\n child.stdout?.destroy();\n child.stderr?.destroy();\n try {\n if (child.pid === undefined) {\n child.kill('SIGKILL');\n } else {\n killHarnessTree(child.pid);\n }\n } catch {\n child.kill('SIGKILL');\n }\n return true;\n };\n const timeoutId = setTimeout(() => {\n timedOut = killProcessGroup(`timed out after ${options.timeoutMs / 1000} seconds`);\n }, options.timeoutMs);\n\n const appendOutput = (chunk: Buffer, chunks: Buffer[]): void => {\n totalOutputBytes += chunk.byteLength;\n if (totalOutputBytes > options.maxOutputBytes) {\n killProcessGroup(`the harness printed more than ${options.maxOutputBytes / 1024 / 1024} MB of output`);\n return;\n }\n chunks.push(chunk);\n };\n child.stdout?.on('data', (chunk: Buffer) => appendOutput(chunk, stdoutChunks));\n child.stderr?.on('data', (chunk: Buffer) => appendOutput(chunk, stderrChunks));\n\n const settle = (exitCode: number | undefined, signal: NodeJS.Signals | undefined, spawnError?: Error): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timeoutId);\n if (liveRun) liveHarnessRuns.delete(liveRun);\n resolve({\n stdout: Buffer.concat(stdoutChunks).toString('utf8'),\n stderr: Buffer.concat(stderrChunks).toString('utf8'),\n exitCode,\n signal,\n failureReason: failureReason ?? (spawnError ? `failed to run the harness: ${spawnError.message}` : undefined),\n timedOut,\n });\n };\n child.on('error', (error) => settle(undefined, undefined, error));\n child.on('close', (exitCode, signal) => settle(exitCode ?? undefined, signal ?? undefined));\n });\n}\n\nfunction installSignalHandlers(): void {\n if (signalHandlersInstalled) return;\n signalHandlersInstalled = true;\n for (const signal of ['SIGINT', 'SIGTERM'] as const) {\n process.once(signal, () => {\n for (const liveRun of liveHarnessRuns) {\n try {\n killHarnessTree(liveRun.pid);\n } catch {\n // The tree already exited.\n }\n // Best-effort cleanup while exiting; judged code may have left permission-locked entries.\n if (liveRun.tempRoot) forciblyRemoveDirectorySync(liveRun.tempRoot);\n }\n process.kill(process.pid, signal);\n });\n }\n}\n\n// On Windows the harness is not detached (process groups are unavailable), so killing only the\n// direct child would leave submission grandchildren running; taskkill terminates the whole tree.\nfunction killHarnessTree(pid: number): void {\n if (process.platform === 'win32') {\n child_process.spawnSync('taskkill', ['/PID', String(pid), '/T', '/F'], { stdio: 'ignore' });\n } else {\n process.kill(-pid, 'SIGKILL');\n }\n}\n"],"mappings":"kKAiCA,MAAM,EAAkB,IAAI,IAC5B,IAAI,EAA0B,GAS9B,SAAgB,EACd,EACA,EAC+B,CAE/B,OADA,EAAsB,EACf,IAAI,QAAS,GAAY,CAE9B,IAAM,EAAQA,EAAAA,QAAc,MAAM,QAAQ,SAAU,EAAa,CAC/D,IAAK,EAAQ,IACb,SAAU,QAAQ,WAAa,QAE/B,IAAK,EAAQ,SAAW,CAAE,GAAG,EAAQ,IAAK,OAAQ,EAAQ,QAAS,EAAI,EAAQ,IAC/E,MAAO,CAAC,SAAU,OAAQ,MAAM,CAClC,CAAC,EACK,EACJ,EAAM,MAAQ,IAAA,GAAY,IAAA,GAAY,CAAE,IAAK,EAAM,IAAK,SAAU,EAAQ,QAAS,EACjF,GAAS,EAAgB,IAAI,CAAO,EACxC,IAAM,EAAyB,CAAC,EAC1B,EAAyB,CAAC,EAC5B,EAAmB,EACnB,EACA,EAAW,GACX,EAAU,GAGR,EAAoB,GAA4B,CACpD,GAAI,IAAkB,IAAA,GAAW,MAAO,GACxC,EAAgB,EAGhB,EAAM,QAAQ,QAAQ,EACtB,EAAM,QAAQ,QAAQ,EACtB,GAAI,CACE,EAAM,MAAQ,IAAA,GAChB,EAAM,KAAK,SAAS,EAEpB,EAAgB,EAAM,GAAG,CAE7B,MAAQ,CACN,EAAM,KAAK,SAAS,CACtB,CACA,MAAO,EACT,EACM,EAAY,eAAiB,CACjC,EAAW,EAAiB,mBAAmB,EAAQ,UAAY,IAAK,SAAS,CACnF,EAAG,EAAQ,SAAS,EAEd,GAAgB,EAAe,IAA2B,CAE9D,GADA,GAAoB,EAAM,WACtB,EAAmB,EAAQ,eAAgB,CAC7C,EAAiB,iCAAiC,EAAQ,eAAiB,KAAO,KAAK,cAAc,EACrG,MACF,CACA,EAAO,KAAK,CAAK,CACnB,EACA,EAAM,QAAQ,GAAG,OAAS,GAAkB,EAAa,EAAO,CAAY,CAAC,EAC7E,EAAM,QAAQ,GAAG,OAAS,GAAkB,EAAa,EAAO,CAAY,CAAC,EAE7E,IAAM,GAAU,EAA8B,EAAoC,IAA6B,CACzG,IACJ,EAAU,GACV,aAAa,CAAS,EAClB,GAAS,EAAgB,OAAO,CAAO,EAC3C,EAAQ,CACN,OAAQ,OAAO,OAAO,CAAY,CAAC,CAAC,SAAS,MAAM,EACnD,OAAQ,OAAO,OAAO,CAAY,CAAC,CAAC,SAAS,MAAM,EACnD,WACA,SACA,cAAe,IAAkB,EAAa,8BAA8B,EAAW,UAAY,IAAA,IACnG,UACF,CAAC,EACH,EACA,EAAM,GAAG,QAAU,GAAU,EAAO,IAAA,GAAW,IAAA,GAAW,CAAK,CAAC,EAChE,EAAM,GAAG,SAAU,EAAU,IAAW,EAAO,GAAY,IAAA,GAAW,GAAU,IAAA,EAAS,CAAC,CAC5F,CAAC,CACH,CAEA,SAAS,GAA8B,CACjC,MACJ,GAA0B,GAC1B,IAAK,IAAM,IAAU,CAAC,SAAU,SAAS,EACvC,QAAQ,KAAK,MAAc,CACzB,IAAK,IAAM,KAAW,EAAiB,CACrC,GAAI,CACF,EAAgB,EAAQ,GAAG,CAC7B,MAAQ,CAER,CAEI,EAAQ,UAAU,EAAA,4BAA4B,EAAQ,QAAQ,CACpE,CACA,QAAQ,KAAK,QAAQ,IAAK,CAAM,CAClC,CAAC,CAbuB,CAe5B,CAIA,SAAS,EAAgB,EAAmB,CACtC,QAAQ,WAAa,QACvB,EAAA,QAAc,UAAU,WAAY,CAAC,OAAQ,OAAO,CAAG,EAAG,KAAM,IAAI,EAAG,CAAE,MAAO,QAAS,CAAC,EAE1F,QAAQ,KAAK,CAAC,EAAK,SAAS,CAEhC"}
1
+ {"version":3,"file":"runHarnessProcess.cjs","names":["child_process"],"sources":["../../src/helpers/runHarnessProcess.ts"],"sourcesContent":["import child_process from 'node:child_process';\n\nimport { forciblyRemoveDirectorySync } from './temporaryProblemDirCopy.js';\n\nexport interface HarnessProcessOptions {\n cwd: string;\n env: NodeJS.ProcessEnv;\n timeoutMs: number;\n maxOutputBytes: number;\n /** A temporary copy to remove when the check itself is interrupted by a signal. */\n tempRoot?: string;\n}\n\nexport interface HarnessProcessResult {\n stdout: string;\n stderr: string;\n exitCode: number | undefined;\n /** The signal that terminated the harness when it did not exit on its own. */\n signal: NodeJS.Signals | undefined;\n /** Set when the run was killed by this helper (timeout or output cap) or could not be spawned. */\n failureReason: string | undefined;\n timedOut: boolean;\n}\n\ninterface LiveHarnessRun {\n pid: number;\n tempRoot: string | undefined;\n}\n\n// Detached harness groups no longer receive the terminal's SIGINT, so an interrupted run must\n// tear them down (and remove their temp copies) itself before exiting. The handlers stay installed\n// for the process lifetime: once the last run has settled they only iterate an empty set and re-raise\n// the signal, so removing them would change nothing observable.\nconst liveHarnessRuns = new Set<LiveHarnessRun>();\nlet signalHandlersInstalled = false;\n\n/**\n * Run a judge harness with `process.execPath` in its own process group, killing the whole group on\n * timeout or when the output cap is exceeded. The submissions a harness judges through\n * `spawnWithLimits` run in sessions of their own, out of that kill's reach; each wrapper's process\n * group is bounded by its own GNU `timeout` instead, while a descendant that moved to yet another\n * session may survive without stalling the harness.\n */\nexport function runHarnessProcess(\n commandArgs: readonly string[],\n options: HarnessProcessOptions\n): Promise<HarnessProcessResult> {\n installSignalHandlers();\n return new Promise((resolve) => {\n // process.execPath keeps the harness on the same bun executable regardless of PATH.\n const child = child_process.spawn(process.execPath, commandArgs, {\n cwd: options.cwd,\n detached: process.platform !== 'win32',\n // The parent removes this root even when SIGKILL prevents child cleanup.\n env: options.tempRoot ? { ...options.env, TMPDIR: options.tempRoot } : options.env,\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n const liveRun: LiveHarnessRun | undefined =\n child.pid === undefined ? undefined : { pid: child.pid, tempRoot: options.tempRoot };\n if (liveRun) liveHarnessRuns.add(liveRun);\n const stdoutChunks: Buffer[] = [];\n const stderrChunks: Buffer[] = [];\n let totalOutputBytes = 0;\n let failureReason: string | undefined;\n let timedOut = false;\n let settled = false;\n\n // Returns false when an earlier kill already recorded its reason, so callers can tell whether theirs won.\n const killProcessGroup = (reason: string): boolean => {\n if (failureReason !== undefined) return false;\n failureReason = reason;\n // Stop buffering immediately: OS pipe buffers can keep emitting data after the kill, and\n // destroyed streams also let `close` fire even if a stray grandchild inherited the pipes.\n child.stdout?.destroy();\n child.stderr?.destroy();\n try {\n if (child.pid === undefined) {\n child.kill('SIGKILL');\n } else {\n killHarnessTree(child.pid);\n }\n } catch {\n child.kill('SIGKILL');\n }\n return true;\n };\n const timeoutId = setTimeout(() => {\n timedOut = killProcessGroup(`timed out after ${options.timeoutMs / 1000} seconds`);\n }, options.timeoutMs);\n\n const appendOutput = (chunk: Buffer, chunks: Buffer[]): void => {\n totalOutputBytes += chunk.byteLength;\n if (totalOutputBytes > options.maxOutputBytes) {\n killProcessGroup(`the harness printed more than ${options.maxOutputBytes / 1024 / 1024} MB of output`);\n return;\n }\n chunks.push(chunk);\n };\n child.stdout?.on('data', (chunk: Buffer) => appendOutput(chunk, stdoutChunks));\n child.stderr?.on('data', (chunk: Buffer) => appendOutput(chunk, stderrChunks));\n\n const settle = (exitCode: number | undefined, signal: NodeJS.Signals | undefined, spawnError?: Error): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timeoutId);\n if (liveRun) liveHarnessRuns.delete(liveRun);\n resolve({\n stdout: Buffer.concat(stdoutChunks).toString('utf8'),\n stderr: Buffer.concat(stderrChunks).toString('utf8'),\n exitCode,\n signal,\n failureReason: failureReason ?? (spawnError ? `failed to run the harness: ${spawnError.message}` : undefined),\n timedOut,\n });\n };\n child.on('error', (error) => settle(undefined, undefined, error));\n child.on('close', (exitCode, signal) => settle(exitCode ?? undefined, signal ?? undefined));\n });\n}\n\nfunction installSignalHandlers(): void {\n if (signalHandlersInstalled) return;\n signalHandlersInstalled = true;\n for (const signal of ['SIGINT', 'SIGTERM'] as const) {\n process.once(signal, () => {\n for (const liveRun of liveHarnessRuns) {\n try {\n killHarnessTree(liveRun.pid);\n } catch {\n // The tree already exited.\n }\n // Best-effort cleanup while exiting; judged code may have left permission-locked entries.\n if (liveRun.tempRoot) forciblyRemoveDirectorySync(liveRun.tempRoot);\n }\n process.kill(process.pid, signal);\n });\n }\n}\n\n// On Windows the harness is not detached (process groups are unavailable), so killing only the\n// direct child would leave submission grandchildren running; taskkill terminates the whole tree.\nfunction killHarnessTree(pid: number): void {\n if (process.platform === 'win32') {\n child_process.spawnSync('taskkill', ['/PID', String(pid), '/T', '/F'], { stdio: 'ignore' });\n } else {\n process.kill(-pid, 'SIGKILL');\n }\n}\n"],"mappings":"kKAiCA,MAAM,kBAAkB,IAAI,IAC5B,IAAI,EAA0B,GAS9B,SAAgB,EACd,EACA,EAC+B,CAE/B,OADA,EAAsB,EACf,IAAI,QAAS,GAAY,CAE9B,IAAM,EAAQA,EAAAA,QAAc,MAAM,QAAQ,SAAU,EAAa,CAC/D,IAAK,EAAQ,IACb,SAAU,QAAQ,WAAa,QAE/B,IAAK,EAAQ,SAAW,CAAE,GAAG,EAAQ,IAAK,OAAQ,EAAQ,QAAS,EAAI,EAAQ,IAC/E,MAAO,CAAC,SAAU,OAAQ,MAAM,CAClC,CAAC,EACK,EACJ,EAAM,MAAQ,IAAA,GAAY,IAAA,GAAY,CAAE,IAAK,EAAM,IAAK,SAAU,EAAQ,QAAS,EACjF,GAAS,EAAgB,IAAI,CAAO,EACxC,IAAM,EAAyB,CAAC,EAC1B,EAAyB,CAAC,EAC5B,EAAmB,EACnB,EACA,EAAW,GACX,EAAU,GAGR,EAAoB,GAA4B,CACpD,GAAI,IAAkB,IAAA,GAAW,MAAO,GACxC,EAAgB,EAGhB,EAAM,QAAQ,QAAQ,EACtB,EAAM,QAAQ,QAAQ,EACtB,GAAI,CACE,EAAM,MAAQ,IAAA,GAChB,EAAM,KAAK,SAAS,EAEpB,EAAgB,EAAM,GAAG,CAE7B,MAAQ,CACN,EAAM,KAAK,SAAS,CACtB,CACA,MAAO,EACT,EACM,EAAY,eAAiB,CACjC,EAAW,EAAiB,mBAAmB,EAAQ,UAAY,IAAK,SAAS,CACnF,EAAG,EAAQ,SAAS,EAEd,GAAgB,EAAe,IAA2B,CAE9D,GADA,GAAoB,EAAM,WACtB,EAAmB,EAAQ,eAAgB,CAC7C,EAAiB,iCAAiC,EAAQ,eAAiB,KAAO,KAAK,cAAc,EACrG,MACF,CACA,EAAO,KAAK,CAAK,CACnB,EACA,EAAM,QAAQ,GAAG,OAAS,GAAkB,EAAa,EAAO,CAAY,CAAC,EAC7E,EAAM,QAAQ,GAAG,OAAS,GAAkB,EAAa,EAAO,CAAY,CAAC,EAE7E,IAAM,GAAU,EAA8B,EAAoC,IAA6B,CACzG,IACJ,EAAU,GACV,aAAa,CAAS,EAClB,GAAS,EAAgB,OAAO,CAAO,EAC3C,EAAQ,CACN,OAAQ,OAAO,OAAO,CAAY,CAAC,CAAC,SAAS,MAAM,EACnD,OAAQ,OAAO,OAAO,CAAY,CAAC,CAAC,SAAS,MAAM,EACnD,WACA,SACA,cAAe,IAAkB,EAAa,8BAA8B,EAAW,UAAY,IAAA,IACnG,UACF,CAAC,EACH,EACA,EAAM,GAAG,QAAU,GAAU,EAAO,IAAA,GAAW,IAAA,GAAW,CAAK,CAAC,EAChE,EAAM,GAAG,SAAU,EAAU,IAAW,EAAO,GAAY,IAAA,GAAW,GAAU,IAAA,EAAS,CAAC,CAC5F,CAAC,CACH,CAEA,SAAS,GAA8B,CACjC,MACJ,GAA0B,GAC1B,IAAK,IAAM,IAAU,CAAC,SAAU,SAAS,EACvC,QAAQ,KAAK,MAAc,CACzB,IAAK,IAAM,KAAW,EAAiB,CACrC,GAAI,CACF,EAAgB,EAAQ,GAAG,CAC7B,MAAQ,CAER,CAEI,EAAQ,UAAU,EAAA,4BAA4B,EAAQ,QAAQ,CACpE,CACA,QAAQ,KAAK,QAAQ,IAAK,CAAM,CAClC,CAAC,CAbuB,CAe5B,CAIA,SAAS,EAAgB,EAAmB,CACtC,QAAQ,WAAa,QACvB,EAAA,QAAc,UAAU,WAAY,CAAC,OAAQ,OAAO,CAAG,EAAG,KAAM,IAAI,EAAG,CAAE,MAAO,QAAS,CAAC,EAE1F,QAAQ,KAAK,CAAC,EAAK,SAAS,CAEhC"}
@@ -1,2 +1,2 @@
1
- import{forciblyRemoveDirectorySync as e}from"./temporaryProblemDirCopy.js";import t from"node:child_process";const n=new Set;let r=!1;function i(e,r){return a(),new Promise(i=>{let a=t.spawn(process.execPath,e,{cwd:r.cwd,detached:process.platform!==`win32`,env:r.tempRoot?{...r.env,TMPDIR:r.tempRoot}:r.env,stdio:[`ignore`,`pipe`,`pipe`]}),s=a.pid===void 0?void 0:{pid:a.pid,tempRoot:r.tempRoot};s&&n.add(s);let c=[],l=[],u=0,d,f=!1,p=!1,m=e=>{if(d!==void 0)return!1;d=e,a.stdout?.destroy(),a.stderr?.destroy();try{a.pid===void 0?a.kill(`SIGKILL`):o(a.pid)}catch{a.kill(`SIGKILL`)}return!0},h=setTimeout(()=>{f=m(`timed out after ${r.timeoutMs/1e3} seconds`)},r.timeoutMs),g=(e,t)=>{if(u+=e.byteLength,u>r.maxOutputBytes){m(`the harness printed more than ${r.maxOutputBytes/1024/1024} MB of output`);return}t.push(e)};a.stdout?.on(`data`,e=>g(e,c)),a.stderr?.on(`data`,e=>g(e,l));let _=(e,t,r)=>{p||(p=!0,clearTimeout(h),s&&n.delete(s),i({stdout:Buffer.concat(c).toString(`utf8`),stderr:Buffer.concat(l).toString(`utf8`),exitCode:e,signal:t,failureReason:d??(r?`failed to run the harness: ${r.message}`:void 0),timedOut:f}))};a.on(`error`,e=>_(void 0,void 0,e)),a.on(`close`,(e,t)=>_(e??void 0,t??void 0))})}function a(){if(!r){r=!0;for(let t of[`SIGINT`,`SIGTERM`])process.once(t,()=>{for(let t of n){try{o(t.pid)}catch{}t.tempRoot&&e(t.tempRoot)}process.kill(process.pid,t)})}}function o(e){process.platform===`win32`?t.spawnSync(`taskkill`,[`/PID`,String(e),`/T`,`/F`],{stdio:`ignore`}):process.kill(-e,`SIGKILL`)}export{i as runHarnessProcess};
1
+ import{forciblyRemoveDirectorySync as e}from"./temporaryProblemDirCopy.js";import t from"node:child_process";const n=/* @__PURE__ */ new Set;let r=!1;function i(e,r){return a(),new Promise(i=>{let a=t.spawn(process.execPath,e,{cwd:r.cwd,detached:process.platform!==`win32`,env:r.tempRoot?{...r.env,TMPDIR:r.tempRoot}:r.env,stdio:[`ignore`,`pipe`,`pipe`]}),s=a.pid===void 0?void 0:{pid:a.pid,tempRoot:r.tempRoot};s&&n.add(s);let c=[],l=[],u=0,d,f=!1,p=!1,m=e=>{if(d!==void 0)return!1;d=e,a.stdout?.destroy(),a.stderr?.destroy();try{a.pid===void 0?a.kill(`SIGKILL`):o(a.pid)}catch{a.kill(`SIGKILL`)}return!0},h=setTimeout(()=>{f=m(`timed out after ${r.timeoutMs/1e3} seconds`)},r.timeoutMs),g=(e,t)=>{if(u+=e.byteLength,u>r.maxOutputBytes){m(`the harness printed more than ${r.maxOutputBytes/1024/1024} MB of output`);return}t.push(e)};a.stdout?.on(`data`,e=>g(e,c)),a.stderr?.on(`data`,e=>g(e,l));let _=(e,t,r)=>{p||(p=!0,clearTimeout(h),s&&n.delete(s),i({stdout:Buffer.concat(c).toString(`utf8`),stderr:Buffer.concat(l).toString(`utf8`),exitCode:e,signal:t,failureReason:d??(r?`failed to run the harness: ${r.message}`:void 0),timedOut:f}))};a.on(`error`,e=>_(void 0,void 0,e)),a.on(`close`,(e,t)=>_(e??void 0,t??void 0))})}function a(){if(!r){r=!0;for(let t of[`SIGINT`,`SIGTERM`])process.once(t,()=>{for(let t of n){try{o(t.pid)}catch{}t.tempRoot&&e(t.tempRoot)}process.kill(process.pid,t)})}}function o(e){process.platform===`win32`?t.spawnSync(`taskkill`,[`/PID`,String(e),`/T`,`/F`],{stdio:`ignore`}):process.kill(-e,`SIGKILL`)}export{i as runHarnessProcess};
2
2
  //# sourceMappingURL=runHarnessProcess.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"runHarnessProcess.js","names":["child_process"],"sources":["../../src/helpers/runHarnessProcess.ts"],"sourcesContent":["import child_process from 'node:child_process';\n\nimport { forciblyRemoveDirectorySync } from './temporaryProblemDirCopy.js';\n\nexport interface HarnessProcessOptions {\n cwd: string;\n env: NodeJS.ProcessEnv;\n timeoutMs: number;\n maxOutputBytes: number;\n /** A temporary copy to remove when the check itself is interrupted by a signal. */\n tempRoot?: string;\n}\n\nexport interface HarnessProcessResult {\n stdout: string;\n stderr: string;\n exitCode: number | undefined;\n /** The signal that terminated the harness when it did not exit on its own. */\n signal: NodeJS.Signals | undefined;\n /** Set when the run was killed by this helper (timeout or output cap) or could not be spawned. */\n failureReason: string | undefined;\n timedOut: boolean;\n}\n\ninterface LiveHarnessRun {\n pid: number;\n tempRoot: string | undefined;\n}\n\n// Detached harness groups no longer receive the terminal's SIGINT, so an interrupted run must\n// tear them down (and remove their temp copies) itself before exiting. The handlers stay installed\n// for the process lifetime: once the last run has settled they only iterate an empty set and re-raise\n// the signal, so removing them would change nothing observable.\nconst liveHarnessRuns = new Set<LiveHarnessRun>();\nlet signalHandlersInstalled = false;\n\n/**\n * Run a judge harness with `process.execPath` in its own process group, killing the whole group on\n * timeout or when the output cap is exceeded. The submissions a harness judges through\n * `spawnWithLimits` run in sessions of their own, out of that kill's reach; each wrapper's process\n * group is bounded by its own GNU `timeout` instead, while a descendant that moved to yet another\n * session may survive without stalling the harness.\n */\nexport function runHarnessProcess(\n commandArgs: readonly string[],\n options: HarnessProcessOptions\n): Promise<HarnessProcessResult> {\n installSignalHandlers();\n return new Promise((resolve) => {\n // process.execPath keeps the harness on the same bun executable regardless of PATH.\n const child = child_process.spawn(process.execPath, commandArgs, {\n cwd: options.cwd,\n detached: process.platform !== 'win32',\n // The parent removes this root even when SIGKILL prevents child cleanup.\n env: options.tempRoot ? { ...options.env, TMPDIR: options.tempRoot } : options.env,\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n const liveRun: LiveHarnessRun | undefined =\n child.pid === undefined ? undefined : { pid: child.pid, tempRoot: options.tempRoot };\n if (liveRun) liveHarnessRuns.add(liveRun);\n const stdoutChunks: Buffer[] = [];\n const stderrChunks: Buffer[] = [];\n let totalOutputBytes = 0;\n let failureReason: string | undefined;\n let timedOut = false;\n let settled = false;\n\n // Returns false when an earlier kill already recorded its reason, so callers can tell whether theirs won.\n const killProcessGroup = (reason: string): boolean => {\n if (failureReason !== undefined) return false;\n failureReason = reason;\n // Stop buffering immediately: OS pipe buffers can keep emitting data after the kill, and\n // destroyed streams also let `close` fire even if a stray grandchild inherited the pipes.\n child.stdout?.destroy();\n child.stderr?.destroy();\n try {\n if (child.pid === undefined) {\n child.kill('SIGKILL');\n } else {\n killHarnessTree(child.pid);\n }\n } catch {\n child.kill('SIGKILL');\n }\n return true;\n };\n const timeoutId = setTimeout(() => {\n timedOut = killProcessGroup(`timed out after ${options.timeoutMs / 1000} seconds`);\n }, options.timeoutMs);\n\n const appendOutput = (chunk: Buffer, chunks: Buffer[]): void => {\n totalOutputBytes += chunk.byteLength;\n if (totalOutputBytes > options.maxOutputBytes) {\n killProcessGroup(`the harness printed more than ${options.maxOutputBytes / 1024 / 1024} MB of output`);\n return;\n }\n chunks.push(chunk);\n };\n child.stdout?.on('data', (chunk: Buffer) => appendOutput(chunk, stdoutChunks));\n child.stderr?.on('data', (chunk: Buffer) => appendOutput(chunk, stderrChunks));\n\n const settle = (exitCode: number | undefined, signal: NodeJS.Signals | undefined, spawnError?: Error): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timeoutId);\n if (liveRun) liveHarnessRuns.delete(liveRun);\n resolve({\n stdout: Buffer.concat(stdoutChunks).toString('utf8'),\n stderr: Buffer.concat(stderrChunks).toString('utf8'),\n exitCode,\n signal,\n failureReason: failureReason ?? (spawnError ? `failed to run the harness: ${spawnError.message}` : undefined),\n timedOut,\n });\n };\n child.on('error', (error) => settle(undefined, undefined, error));\n child.on('close', (exitCode, signal) => settle(exitCode ?? undefined, signal ?? undefined));\n });\n}\n\nfunction installSignalHandlers(): void {\n if (signalHandlersInstalled) return;\n signalHandlersInstalled = true;\n for (const signal of ['SIGINT', 'SIGTERM'] as const) {\n process.once(signal, () => {\n for (const liveRun of liveHarnessRuns) {\n try {\n killHarnessTree(liveRun.pid);\n } catch {\n // The tree already exited.\n }\n // Best-effort cleanup while exiting; judged code may have left permission-locked entries.\n if (liveRun.tempRoot) forciblyRemoveDirectorySync(liveRun.tempRoot);\n }\n process.kill(process.pid, signal);\n });\n }\n}\n\n// On Windows the harness is not detached (process groups are unavailable), so killing only the\n// direct child would leave submission grandchildren running; taskkill terminates the whole tree.\nfunction killHarnessTree(pid: number): void {\n if (process.platform === 'win32') {\n child_process.spawnSync('taskkill', ['/PID', String(pid), '/T', '/F'], { stdio: 'ignore' });\n } else {\n process.kill(-pid, 'SIGKILL');\n }\n}\n"],"mappings":"6GAiCA,MAAM,EAAkB,IAAI,IAC5B,IAAI,EAA0B,GAS9B,SAAgB,EACd,EACA,EAC+B,CAE/B,OADA,EAAsB,EACf,IAAI,QAAS,GAAY,CAE9B,IAAM,EAAQA,EAAc,MAAM,QAAQ,SAAU,EAAa,CAC/D,IAAK,EAAQ,IACb,SAAU,QAAQ,WAAa,QAE/B,IAAK,EAAQ,SAAW,CAAE,GAAG,EAAQ,IAAK,OAAQ,EAAQ,QAAS,EAAI,EAAQ,IAC/E,MAAO,CAAC,SAAU,OAAQ,MAAM,CAClC,CAAC,EACK,EACJ,EAAM,MAAQ,IAAA,GAAY,IAAA,GAAY,CAAE,IAAK,EAAM,IAAK,SAAU,EAAQ,QAAS,EACjF,GAAS,EAAgB,IAAI,CAAO,EACxC,IAAM,EAAyB,CAAC,EAC1B,EAAyB,CAAC,EAC5B,EAAmB,EACnB,EACA,EAAW,GACX,EAAU,GAGR,EAAoB,GAA4B,CACpD,GAAI,IAAkB,IAAA,GAAW,MAAO,GACxC,EAAgB,EAGhB,EAAM,QAAQ,QAAQ,EACtB,EAAM,QAAQ,QAAQ,EACtB,GAAI,CACE,EAAM,MAAQ,IAAA,GAChB,EAAM,KAAK,SAAS,EAEpB,EAAgB,EAAM,GAAG,CAE7B,MAAQ,CACN,EAAM,KAAK,SAAS,CACtB,CACA,MAAO,EACT,EACM,EAAY,eAAiB,CACjC,EAAW,EAAiB,mBAAmB,EAAQ,UAAY,IAAK,SAAS,CACnF,EAAG,EAAQ,SAAS,EAEd,GAAgB,EAAe,IAA2B,CAE9D,GADA,GAAoB,EAAM,WACtB,EAAmB,EAAQ,eAAgB,CAC7C,EAAiB,iCAAiC,EAAQ,eAAiB,KAAO,KAAK,cAAc,EACrG,MACF,CACA,EAAO,KAAK,CAAK,CACnB,EACA,EAAM,QAAQ,GAAG,OAAS,GAAkB,EAAa,EAAO,CAAY,CAAC,EAC7E,EAAM,QAAQ,GAAG,OAAS,GAAkB,EAAa,EAAO,CAAY,CAAC,EAE7E,IAAM,GAAU,EAA8B,EAAoC,IAA6B,CACzG,IACJ,EAAU,GACV,aAAa,CAAS,EAClB,GAAS,EAAgB,OAAO,CAAO,EAC3C,EAAQ,CACN,OAAQ,OAAO,OAAO,CAAY,CAAC,CAAC,SAAS,MAAM,EACnD,OAAQ,OAAO,OAAO,CAAY,CAAC,CAAC,SAAS,MAAM,EACnD,WACA,SACA,cAAe,IAAkB,EAAa,8BAA8B,EAAW,UAAY,IAAA,IACnG,UACF,CAAC,EACH,EACA,EAAM,GAAG,QAAU,GAAU,EAAO,IAAA,GAAW,IAAA,GAAW,CAAK,CAAC,EAChE,EAAM,GAAG,SAAU,EAAU,IAAW,EAAO,GAAY,IAAA,GAAW,GAAU,IAAA,EAAS,CAAC,CAC5F,CAAC,CACH,CAEA,SAAS,GAA8B,CACjC,MACJ,GAA0B,GAC1B,IAAK,IAAM,IAAU,CAAC,SAAU,SAAS,EACvC,QAAQ,KAAK,MAAc,CACzB,IAAK,IAAM,KAAW,EAAiB,CACrC,GAAI,CACF,EAAgB,EAAQ,GAAG,CAC7B,MAAQ,CAER,CAEI,EAAQ,UAAU,EAA4B,EAAQ,QAAQ,CACpE,CACA,QAAQ,KAAK,QAAQ,IAAK,CAAM,CAClC,CAAC,CAbuB,CAe5B,CAIA,SAAS,EAAgB,EAAmB,CACtC,QAAQ,WAAa,QACvB,EAAc,UAAU,WAAY,CAAC,OAAQ,OAAO,CAAG,EAAG,KAAM,IAAI,EAAG,CAAE,MAAO,QAAS,CAAC,EAE1F,QAAQ,KAAK,CAAC,EAAK,SAAS,CAEhC"}
1
+ {"version":3,"file":"runHarnessProcess.js","names":["child_process"],"sources":["../../src/helpers/runHarnessProcess.ts"],"sourcesContent":["import child_process from 'node:child_process';\n\nimport { forciblyRemoveDirectorySync } from './temporaryProblemDirCopy.js';\n\nexport interface HarnessProcessOptions {\n cwd: string;\n env: NodeJS.ProcessEnv;\n timeoutMs: number;\n maxOutputBytes: number;\n /** A temporary copy to remove when the check itself is interrupted by a signal. */\n tempRoot?: string;\n}\n\nexport interface HarnessProcessResult {\n stdout: string;\n stderr: string;\n exitCode: number | undefined;\n /** The signal that terminated the harness when it did not exit on its own. */\n signal: NodeJS.Signals | undefined;\n /** Set when the run was killed by this helper (timeout or output cap) or could not be spawned. */\n failureReason: string | undefined;\n timedOut: boolean;\n}\n\ninterface LiveHarnessRun {\n pid: number;\n tempRoot: string | undefined;\n}\n\n// Detached harness groups no longer receive the terminal's SIGINT, so an interrupted run must\n// tear them down (and remove their temp copies) itself before exiting. The handlers stay installed\n// for the process lifetime: once the last run has settled they only iterate an empty set and re-raise\n// the signal, so removing them would change nothing observable.\nconst liveHarnessRuns = new Set<LiveHarnessRun>();\nlet signalHandlersInstalled = false;\n\n/**\n * Run a judge harness with `process.execPath` in its own process group, killing the whole group on\n * timeout or when the output cap is exceeded. The submissions a harness judges through\n * `spawnWithLimits` run in sessions of their own, out of that kill's reach; each wrapper's process\n * group is bounded by its own GNU `timeout` instead, while a descendant that moved to yet another\n * session may survive without stalling the harness.\n */\nexport function runHarnessProcess(\n commandArgs: readonly string[],\n options: HarnessProcessOptions\n): Promise<HarnessProcessResult> {\n installSignalHandlers();\n return new Promise((resolve) => {\n // process.execPath keeps the harness on the same bun executable regardless of PATH.\n const child = child_process.spawn(process.execPath, commandArgs, {\n cwd: options.cwd,\n detached: process.platform !== 'win32',\n // The parent removes this root even when SIGKILL prevents child cleanup.\n env: options.tempRoot ? { ...options.env, TMPDIR: options.tempRoot } : options.env,\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n const liveRun: LiveHarnessRun | undefined =\n child.pid === undefined ? undefined : { pid: child.pid, tempRoot: options.tempRoot };\n if (liveRun) liveHarnessRuns.add(liveRun);\n const stdoutChunks: Buffer[] = [];\n const stderrChunks: Buffer[] = [];\n let totalOutputBytes = 0;\n let failureReason: string | undefined;\n let timedOut = false;\n let settled = false;\n\n // Returns false when an earlier kill already recorded its reason, so callers can tell whether theirs won.\n const killProcessGroup = (reason: string): boolean => {\n if (failureReason !== undefined) return false;\n failureReason = reason;\n // Stop buffering immediately: OS pipe buffers can keep emitting data after the kill, and\n // destroyed streams also let `close` fire even if a stray grandchild inherited the pipes.\n child.stdout?.destroy();\n child.stderr?.destroy();\n try {\n if (child.pid === undefined) {\n child.kill('SIGKILL');\n } else {\n killHarnessTree(child.pid);\n }\n } catch {\n child.kill('SIGKILL');\n }\n return true;\n };\n const timeoutId = setTimeout(() => {\n timedOut = killProcessGroup(`timed out after ${options.timeoutMs / 1000} seconds`);\n }, options.timeoutMs);\n\n const appendOutput = (chunk: Buffer, chunks: Buffer[]): void => {\n totalOutputBytes += chunk.byteLength;\n if (totalOutputBytes > options.maxOutputBytes) {\n killProcessGroup(`the harness printed more than ${options.maxOutputBytes / 1024 / 1024} MB of output`);\n return;\n }\n chunks.push(chunk);\n };\n child.stdout?.on('data', (chunk: Buffer) => appendOutput(chunk, stdoutChunks));\n child.stderr?.on('data', (chunk: Buffer) => appendOutput(chunk, stderrChunks));\n\n const settle = (exitCode: number | undefined, signal: NodeJS.Signals | undefined, spawnError?: Error): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timeoutId);\n if (liveRun) liveHarnessRuns.delete(liveRun);\n resolve({\n stdout: Buffer.concat(stdoutChunks).toString('utf8'),\n stderr: Buffer.concat(stderrChunks).toString('utf8'),\n exitCode,\n signal,\n failureReason: failureReason ?? (spawnError ? `failed to run the harness: ${spawnError.message}` : undefined),\n timedOut,\n });\n };\n child.on('error', (error) => settle(undefined, undefined, error));\n child.on('close', (exitCode, signal) => settle(exitCode ?? undefined, signal ?? undefined));\n });\n}\n\nfunction installSignalHandlers(): void {\n if (signalHandlersInstalled) return;\n signalHandlersInstalled = true;\n for (const signal of ['SIGINT', 'SIGTERM'] as const) {\n process.once(signal, () => {\n for (const liveRun of liveHarnessRuns) {\n try {\n killHarnessTree(liveRun.pid);\n } catch {\n // The tree already exited.\n }\n // Best-effort cleanup while exiting; judged code may have left permission-locked entries.\n if (liveRun.tempRoot) forciblyRemoveDirectorySync(liveRun.tempRoot);\n }\n process.kill(process.pid, signal);\n });\n }\n}\n\n// On Windows the harness is not detached (process groups are unavailable), so killing only the\n// direct child would leave submission grandchildren running; taskkill terminates the whole tree.\nfunction killHarnessTree(pid: number): void {\n if (process.platform === 'win32') {\n child_process.spawnSync('taskkill', ['/PID', String(pid), '/T', '/F'], { stdio: 'ignore' });\n } else {\n process.kill(-pid, 'SIGKILL');\n }\n}\n"],"mappings":"6GAiCA,MAAM,kBAAkB,IAAI,IAC5B,IAAI,EAA0B,GAS9B,SAAgB,EACd,EACA,EAC+B,CAE/B,OADA,EAAsB,EACf,IAAI,QAAS,GAAY,CAE9B,IAAM,EAAQA,EAAc,MAAM,QAAQ,SAAU,EAAa,CAC/D,IAAK,EAAQ,IACb,SAAU,QAAQ,WAAa,QAE/B,IAAK,EAAQ,SAAW,CAAE,GAAG,EAAQ,IAAK,OAAQ,EAAQ,QAAS,EAAI,EAAQ,IAC/E,MAAO,CAAC,SAAU,OAAQ,MAAM,CAClC,CAAC,EACK,EACJ,EAAM,MAAQ,IAAA,GAAY,IAAA,GAAY,CAAE,IAAK,EAAM,IAAK,SAAU,EAAQ,QAAS,EACjF,GAAS,EAAgB,IAAI,CAAO,EACxC,IAAM,EAAyB,CAAC,EAC1B,EAAyB,CAAC,EAC5B,EAAmB,EACnB,EACA,EAAW,GACX,EAAU,GAGR,EAAoB,GAA4B,CACpD,GAAI,IAAkB,IAAA,GAAW,MAAO,GACxC,EAAgB,EAGhB,EAAM,QAAQ,QAAQ,EACtB,EAAM,QAAQ,QAAQ,EACtB,GAAI,CACE,EAAM,MAAQ,IAAA,GAChB,EAAM,KAAK,SAAS,EAEpB,EAAgB,EAAM,GAAG,CAE7B,MAAQ,CACN,EAAM,KAAK,SAAS,CACtB,CACA,MAAO,EACT,EACM,EAAY,eAAiB,CACjC,EAAW,EAAiB,mBAAmB,EAAQ,UAAY,IAAK,SAAS,CACnF,EAAG,EAAQ,SAAS,EAEd,GAAgB,EAAe,IAA2B,CAE9D,GADA,GAAoB,EAAM,WACtB,EAAmB,EAAQ,eAAgB,CAC7C,EAAiB,iCAAiC,EAAQ,eAAiB,KAAO,KAAK,cAAc,EACrG,MACF,CACA,EAAO,KAAK,CAAK,CACnB,EACA,EAAM,QAAQ,GAAG,OAAS,GAAkB,EAAa,EAAO,CAAY,CAAC,EAC7E,EAAM,QAAQ,GAAG,OAAS,GAAkB,EAAa,EAAO,CAAY,CAAC,EAE7E,IAAM,GAAU,EAA8B,EAAoC,IAA6B,CACzG,IACJ,EAAU,GACV,aAAa,CAAS,EAClB,GAAS,EAAgB,OAAO,CAAO,EAC3C,EAAQ,CACN,OAAQ,OAAO,OAAO,CAAY,CAAC,CAAC,SAAS,MAAM,EACnD,OAAQ,OAAO,OAAO,CAAY,CAAC,CAAC,SAAS,MAAM,EACnD,WACA,SACA,cAAe,IAAkB,EAAa,8BAA8B,EAAW,UAAY,IAAA,IACnG,UACF,CAAC,EACH,EACA,EAAM,GAAG,QAAU,GAAU,EAAO,IAAA,GAAW,IAAA,GAAW,CAAK,CAAC,EAChE,EAAM,GAAG,SAAU,EAAU,IAAW,EAAO,GAAY,IAAA,GAAW,GAAU,IAAA,EAAS,CAAC,CAC5F,CAAC,CACH,CAEA,SAAS,GAA8B,CACjC,MACJ,GAA0B,GAC1B,IAAK,IAAM,IAAU,CAAC,SAAU,SAAS,EACvC,QAAQ,KAAK,MAAc,CACzB,IAAK,IAAM,KAAW,EAAiB,CACrC,GAAI,CACF,EAAgB,EAAQ,GAAG,CAC7B,MAAQ,CAER,CAEI,EAAQ,UAAU,EAA4B,EAAQ,QAAQ,CACpE,CACA,QAAQ,KAAK,QAAQ,IAAK,CAAM,CAClC,CAAC,CAbuB,CAe5B,CAIA,SAAS,EAAgB,EAAmB,CACtC,QAAQ,WAAa,QACvB,EAAc,UAAU,WAAY,CAAC,OAAQ,OAAO,CAAG,EAAG,KAAM,IAAI,EAAG,CAAE,MAAO,QAAS,CAAC,EAE1F,QAAQ,KAAK,CAAC,EAAK,SAAS,CAEhC"}
@@ -1,2 +1,2 @@
1
- "use strict";const e=require("../_virtual/_rolldown/runtime.cjs");let t=require("node:path");t=e.__toESM(t,1);let n=require("node:fs/promises");n=e.__toESM(n,1);let r=require("node:os");r=e.__toESM(r,1);let i=require("node:child_process");i=e.__toESM(i,1);const a=1e3,o=_(),s=o!==void 0,c=new Set;process.once(`exit`,()=>{for(let e of c)d(e,`SIGKILL`)});async function l(e,t){let n=o===void 0?void 0:await f(`exercode-time-`);try{let r=process.platform!==`win32`,s=r?[`timeout`,`--foreground`,`-k`,`1`,String(Math.max(t.timeLimitSeconds,.001)),...e]:e,l=o===void 0?s:[...o,`--output=/dev/fd/3`,...s],f=r?t.timeLimitSeconds*1e3+2*a:t.timeLimitSeconds*1e3,h=performance.now(),g=i.default.spawn(l[0],l.slice(1),{cwd:t.cwd,detached:r,env:t.env,stdio:n===void 0?[`pipe`,`pipe`,`pipe`]:[`pipe`,`pipe`,`pipe`,n.fd]});c.add(g);let _=[],v=[],y=0,b=!1,x=!1,S=0,C,w=(e,n)=>{if(y>=t.outputLimitBytes){n.byteLength>0&&(x=!0,d(g,`SIGKILL`));return}let r=t.outputLimitBytes-y,i=n.byteLength>r?n.subarray(0,r):n;e.push(i),y+=i.byteLength,n.byteLength>r&&(x=!0,d(g,`SIGKILL`))};g.stdout.on(`data`,e=>w(_,e)),g.stderr.on(`data`,e=>w(v,e));let T=setTimeout(()=>{b=!0,d(g,`SIGTERM`)},f),E=setTimeout(()=>{b&&d(g,`SIGKILL`)},f+a);E.unref();let{status:D,signal:O}=await new Promise((e,n)=>{let r=!1,i,o,s=(t,a)=>{if(!r){if(r=!0,clearTimeout(o),i){n(i);return}e({status:t??void 0,signal:a??void 0})}},c=e=>{if(!r){if(g.pid===void 0){C=e.message,s(void 0,void 0);return}i=e,d(g,`SIGKILL`)}};g.on(`error`,c),g.stdin.on(`error`,e=>{e.code!==`EPIPE`&&c(e)}),g.on(`exit`,(e,t)=>{clearTimeout(T),clearTimeout(E),S=(performance.now()-h)/1e3,d(g,`SIGKILL`),o=setTimeout(()=>{g.stdout.destroy(),g.stderr.destroy(),s(e,t)},a)}),g.on(`close`,s),g.stdin.end(t.stdin)}).finally(()=>{clearTimeout(T),clearTimeout(E),c.delete(g)}),k=n===void 0?void 0:m(await p(n,4096));return{stdout:Buffer.concat(_).toString(),stderr:C??Buffer.concat(v).toString(),status:D,signal:O,timeSeconds:k?.timeSeconds||S,cpuTimeSeconds:k?.cpuTimeSeconds??0,memoryBytes:k?.memoryBytes??0,timeCommandMessage:k?.message,timedOut:b||u(D,S,t.timeLimitSeconds),outputLimitExceeded:x}}finally{await n?.close()}}function u(e,t,n){return e===124?t>=n:e===137&&t>=n+a/1e3}function d(e,t){if(e.pid!==void 0)try{if(process.platform===`win32`){e.kill(t);return}process.kill(-e.pid,t)}catch(e){if(!g(e,`ESRCH`)&&!g(e,`EPERM`))throw e}}async function f(e){let i=await n.default.mkdtemp(t.default.join(r.default.tmpdir(),e));try{return await n.default.open(t.default.join(i,`record`),`wx+`,384)}finally{await n.default.rm(i,{recursive:!0,force:!0})}}async function p(e,t){let{size:n}=await e.stat(),r=Math.min(n,t),{buffer:i}=await e.read(Buffer.alloc(r),0,r,n-r);return i.toString()}function m(e){let t=/(?:^|\n)(\d+(?:[.,]\d+)?) (\d+(?:[.,]\d+)?) (\d+(?:[.,]\d+)?) (\d+)\s*$/.exec(e);if(t)return{timeSeconds:h(t[1]),cpuTimeSeconds:h(t[2])+h(t[3]),memoryBytes:Number(t[4])*1024,message:e.slice(0,t.index).trim()||void 0}}function h(e){return Number(e.replace(`,`,`.`))}function g(e,t){return typeof e==`object`&&!!e&&`code`in e&&e.code===t}function _(){let e=r.default.platform()===`darwin`?`gtime`:`/usr/bin/time`,t=i.default.spawnSync(e,[`--version`],{stdio:`ignore`});if(!(t.error||t.status!==0))return[e,`--format`,`%e %U %S %M`]}exports.isTimeCommandAvailable=s,exports.spawnWithLimits=l;
1
+ "use strict";const e=require("../_virtual/_rolldown/runtime.cjs");let t=require("node:path");t=e.__toESM(t,1);let n=require("node:fs/promises");n=e.__toESM(n,1);let r=require("node:os");r=e.__toESM(r,1);let i=require("@willbooster/shared-lib"),a=require("node:child_process");a=e.__toESM(a,1);const o=1e3,s=_(),c=s!==void 0,l=/* @__PURE__ */ new Set;process.once(`exit`,()=>{for(let e of l)f(e,`SIGKILL`)});async function u(e,t){let n=s===void 0?void 0:await p(`exercode-time-`);try{let r=process.platform!==`win32`,i=r?[`timeout`,`--foreground`,`-k`,`1`,String(Math.max(t.timeLimitSeconds,.001)),...e]:e,c=s===void 0?i:[...s,`--output=/dev/fd/3`,...i],u=r?t.timeLimitSeconds*1e3+2*o:t.timeLimitSeconds*1e3,p=performance.now(),g=a.default.spawn(c[0],c.slice(1),{cwd:t.cwd,detached:r,env:t.env,stdio:n===void 0?[`pipe`,`pipe`,`pipe`]:[`pipe`,`pipe`,`pipe`,n.fd]});l.add(g);let _=[],v=[],y=0,b=!1,x=!1,S=0,C,w=(e,n)=>{if(y>=t.outputLimitBytes){n.byteLength>0&&(x=!0,f(g,`SIGKILL`));return}let r=t.outputLimitBytes-y,i=n.byteLength>r?n.subarray(0,r):n;e.push(i),y+=i.byteLength,n.byteLength>r&&(x=!0,f(g,`SIGKILL`))};g.stdout.on(`data`,e=>w(_,e)),g.stderr.on(`data`,e=>w(v,e));let T=setTimeout(()=>{b=!0,f(g,`SIGTERM`)},u),E=setTimeout(()=>{b&&f(g,`SIGKILL`)},u+o);E.unref();let{status:D,signal:O}=await new Promise((e,n)=>{let r=!1,i,a,s=(t,o)=>{if(!r){if(r=!0,clearTimeout(a),i){n(i);return}e({status:t??void 0,signal:o??void 0})}},c=e=>{if(!r){if(g.pid===void 0){C=e.message,s(void 0,void 0);return}i=e,f(g,`SIGKILL`)}};g.on(`error`,c),g.stdin.on(`error`,e=>{e.code!==`EPIPE`&&c(e)}),g.on(`exit`,(e,t)=>{clearTimeout(T),clearTimeout(E),S=(performance.now()-p)/1e3,f(g,`SIGKILL`),a=setTimeout(()=>{g.stdout.destroy(),g.stderr.destroy(),s(e,t)},o)}),g.on(`close`,s),g.stdin.end(t.stdin)}).finally(()=>{clearTimeout(T),clearTimeout(E),l.delete(g)}),k=n===void 0?void 0:h(await m(n,4096));return{stdout:Buffer.concat(_).toString(),stderr:C??Buffer.concat(v).toString(),status:D,signal:O,timeSeconds:k?.timeSeconds||S,cpuTimeSeconds:k?.cpuTimeSeconds??0,memoryBytes:k?.memoryBytes??0,timeCommandMessage:k?.message,timedOut:b||d(D,S,t.timeLimitSeconds),outputLimitExceeded:x}}finally{await n?.close()}}function d(e,t,n){return e===124?t>=n:e===137&&t>=n+o/1e3}function f(e,t){if(e.pid!==void 0)try{if(process.platform===`win32`){e.kill(t);return}process.kill(-e.pid,t)}catch(e){if(!(0,i.hasErrorCode)(e,`ESRCH`)&&!(0,i.hasErrorCode)(e,`EPERM`))throw e}}async function p(e){let i=await n.default.mkdtemp(t.default.join(r.default.tmpdir(),e));try{return await n.default.open(t.default.join(i,`record`),`wx+`,384)}finally{await n.default.rm(i,{recursive:!0,force:!0})}}async function m(e,t){let{size:n}=await e.stat(),r=Math.min(n,t),{buffer:i}=await e.read(Buffer.alloc(r),0,r,n-r);return i.toString()}function h(e){let t=/(?:^|\n)(\d+(?:[.,]\d+)?) (\d+(?:[.,]\d+)?) (\d+(?:[.,]\d+)?) (\d+)\s*$/.exec(e);if(t)return{timeSeconds:g(t[1]),cpuTimeSeconds:g(t[2])+g(t[3]),memoryBytes:Number(t[4])*1024,message:e.slice(0,t.index).trim()||void 0}}function g(e){return Number(e.replace(`,`,`.`))}function _(){let e=r.default.platform()===`darwin`?`gtime`:`/usr/bin/time`,t=a.default.spawnSync(e,[`--version`],{stdio:`ignore`});if(!(t.error||t.status!==0))return[e,`--format`,`%e %U %S %M`]}exports.isTimeCommandAvailable=c,exports.spawnWithLimits=u;
2
2
  //# sourceMappingURL=spawnWithLimits.cjs.map