@exercode/problem-utils 2.3.1 → 2.4.0

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 (76) hide show
  1. package/README.md +28 -0
  2. package/dist/cli/checkAllProblems.cjs +1 -1
  3. package/dist/cli/checkAllProblems.js +1 -1
  4. package/dist/cli/runSingleHarness.cjs +1 -1
  5. package/dist/cli/runSingleHarness.js +1 -1
  6. package/dist/helpers/cleanWorkingDirectory.cjs +1 -1
  7. package/dist/helpers/cleanWorkingDirectory.js +1 -1
  8. package/dist/helpers/compareExpectedOutputFiles.cjs +2 -0
  9. package/dist/helpers/compareExpectedOutputFiles.cjs.map +1 -0
  10. package/dist/helpers/compareExpectedOutputFiles.d.ts +45 -0
  11. package/dist/helpers/compareExpectedOutputFiles.js +2 -0
  12. package/dist/helpers/compareExpectedOutputFiles.js.map +1 -0
  13. package/dist/helpers/compareStdoutAsSpaceSeparatedTokens.cjs +1 -1
  14. package/dist/helpers/compareStdoutAsSpaceSeparatedTokens.cjs.map +1 -1
  15. package/dist/helpers/compareStdoutAsSpaceSeparatedTokens.js +1 -1
  16. package/dist/helpers/compareStdoutAsSpaceSeparatedTokens.js.map +1 -1
  17. package/dist/helpers/defaultStdioHarness.cjs +1 -1
  18. package/dist/helpers/defaultStdioHarness.js +1 -1
  19. package/dist/helpers/findModelAnswerDirs.cjs +1 -1
  20. package/dist/helpers/findModelAnswerDirs.js +1 -1
  21. package/dist/helpers/judgeByStaticAnalysis.cjs +1 -1
  22. package/dist/helpers/judgeByStaticAnalysis.js +1 -1
  23. package/dist/helpers/readOutputFiles.cjs +1 -1
  24. package/dist/helpers/readOutputFiles.js +1 -1
  25. package/dist/helpers/readProblemMarkdownFrontMatter.cjs +1 -1
  26. package/dist/helpers/readProblemMarkdownFrontMatter.js +1 -1
  27. package/dist/helpers/readTestCases.cjs +1 -1
  28. package/dist/helpers/readTestCases.cjs.map +1 -1
  29. package/dist/helpers/readTestCases.d.ts +6 -0
  30. package/dist/helpers/readTestCases.js +1 -1
  31. package/dist/helpers/readTestCases.js.map +1 -1
  32. package/dist/helpers/runCommandInTemporaryPackageManagerProject.cjs +1 -1
  33. package/dist/helpers/runCommandInTemporaryPackageManagerProject.js +1 -1
  34. package/dist/helpers/runCommandInTemporaryPackageManagerProject.js.map +1 -1
  35. package/dist/helpers/safeFs.cjs +1 -1
  36. package/dist/helpers/safeFs.js +1 -1
  37. package/dist/helpers/sandboxUser.cjs +2 -2
  38. package/dist/helpers/sandboxUser.cjs.map +1 -1
  39. package/dist/helpers/sandboxUser.d.ts +5 -0
  40. package/dist/helpers/sandboxUser.js +2 -2
  41. package/dist/helpers/sandboxUser.js.map +1 -1
  42. package/dist/helpers/startHttpServer.cjs +1 -1
  43. package/dist/helpers/startHttpServer.js +1 -1
  44. package/dist/helpers/temporaryProblemDirCopy.cjs +1 -1
  45. package/dist/helpers/temporaryProblemDirCopy.cjs.map +1 -1
  46. package/dist/helpers/temporaryProblemDirCopy.d.ts +4 -1
  47. package/dist/helpers/temporaryProblemDirCopy.js +1 -1
  48. package/dist/helpers/temporaryProblemDirCopy.js.map +1 -1
  49. package/dist/index.cjs +1 -1
  50. package/dist/index.d.ts +3 -0
  51. package/dist/index.js +1 -1
  52. package/dist/presets/command.cjs +1 -1
  53. package/dist/presets/command.cjs.map +1 -1
  54. package/dist/presets/command.d.ts +35 -3
  55. package/dist/presets/command.js +1 -1
  56. package/dist/presets/command.js.map +1 -1
  57. package/dist/presets/evaluation.cjs +3 -3
  58. package/dist/presets/evaluation.js +3 -3
  59. package/dist/presets/guiCommand.cjs +3 -3
  60. package/dist/presets/guiCommand.cjs.map +1 -1
  61. package/dist/presets/guiCommand.d.ts +14 -2
  62. package/dist/presets/guiCommand.js +3 -3
  63. package/dist/presets/guiCommand.js.map +1 -1
  64. package/dist/presets/llm.cjs +1 -1
  65. package/dist/presets/llm.cjs.map +1 -1
  66. package/dist/presets/llm.d.ts +12 -10
  67. package/dist/presets/llm.js +1 -1
  68. package/dist/presets/llm.js.map +1 -1
  69. package/dist/presets/stdio.cjs +1 -1
  70. package/dist/presets/stdio.cjs.map +1 -1
  71. package/dist/presets/stdio.d.ts +3 -1
  72. package/dist/presets/stdio.js +1 -1
  73. package/dist/presets/stdio.js.map +1 -1
  74. package/dist/types/language.cjs +2 -2
  75. package/dist/types/language.js +2 -2
  76. package/package.json +1 -1
@@ -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:child_process");n=e.__toESM(n,1);let r=require("node:fs");r=e.__toESM(r,1);let i=require("node:os");i=e.__toESM(i,1);async function a(e){let n=await r.default.promises.mkdtemp(t.default.join(i.default.tmpdir(),`problem-utils-isolation_`));try{let i=t.default.resolve(e),a=t.default.join(n,f(i));return await r.default.promises.mkdir(t.default.dirname(a),{recursive:!0}),await r.default.promises.cp(i,a,{recursive:!0,filter:l,verbatimSymlinks:!0}),await d(n,i),{tempRoot:n,copiedProblemDir:a}}catch(e){throw await o(n),e}}async function o(e){try{return await r.default.promises.rm(e,{recursive:!0,force:!0}),!0}catch{c(e);try{return await r.default.promises.rm(e,{recursive:!0,force:!0}),!0}catch{return!1}}}function s(e){try{return r.default.rmSync(e,{recursive:!0,force:!0}),!0}catch{c(e);try{return r.default.rmSync(e,{recursive:!0,force:!0}),!0}catch{return!1}}}function c(e){process.platform!==`win32`&&n.default.spawnSync(`chmod`,[`-R`,`u+rwX`,e])}async function l(e){if(!u(e))return!1;if((await r.default.promises.lstat(e)).isSymbolicLink()&&t.default.isAbsolute(await r.default.promises.readlink(e)))throw Error(`${e} is an absolute symlink, which would escape the temporary copy; use a relative symlink`);return!0}function u(e){let n=t.default.basename(e);return n!==`node_modules`&&n!==`.git`}async function d(e,n){let i=t.default.resolve(n);for(;;){let n=t.default.join(i,`node_modules`);if(r.default.existsSync(n)){let a=t.default.join(e,f(i),`node_modules`);try{await r.default.promises.symlink(n,a,process.platform===`win32`?`junction`:`dir`)}catch{}}let a=t.default.dirname(i);if(a===i)break;i=a}}function f(e){let{root:n}=t.default.parse(e);return t.default.relative(n,e)}exports.copyProblemDirToTemporaryRoot=a,exports.forciblyRemoveDirectory=o,exports.forciblyRemoveDirectorySync=s;
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);let r=require("node:child_process");r=e.__toESM(r,1);let i=require("node:os");i=e.__toESM(i,1);async function a(e,r={}){let a=await t.default.promises.mkdtemp(n.default.join(i.default.tmpdir(),`problem-utils-isolation_`));r.onTempRootCreated?.(a);try{let r=n.default.resolve(e),i=n.default.join(a,f(r));return await t.default.promises.mkdir(n.default.dirname(i),{recursive:!0}),await t.default.promises.cp(r,i,{recursive:!0,filter:l,verbatimSymlinks:!0}),await d(a,r),{tempRoot:a,copiedProblemDir:i}}catch(e){throw await o(a),e}}async function o(e){try{return await t.default.promises.rm(e,{recursive:!0,force:!0}),!0}catch{c(e);try{return await t.default.promises.rm(e,{recursive:!0,force:!0}),!0}catch{return!1}}}function s(e){try{return t.default.rmSync(e,{recursive:!0,force:!0}),!0}catch{c(e);try{return t.default.rmSync(e,{recursive:!0,force:!0}),!0}catch{return!1}}}function c(e){process.platform!==`win32`&&r.default.spawnSync(`chmod`,[`-R`,`u+rwX`,e])}async function l(e){if(!u(e))return!1;if((await t.default.promises.lstat(e)).isSymbolicLink()&&n.default.isAbsolute(await t.default.promises.readlink(e)))throw Error(`${e} is an absolute symlink, which would escape the temporary copy; use a relative symlink`);return!0}function u(e){let t=n.default.basename(e);return t!==`node_modules`&&t!==`.git`}async function d(e,r){let i=n.default.resolve(r);for(;;){let r=n.default.join(i,`node_modules`);if(t.default.existsSync(r)){let a=n.default.join(e,f(i),`node_modules`);try{await t.default.promises.symlink(r,a,process.platform===`win32`?`junction`:`dir`)}catch{}}let a=n.default.dirname(i);if(a===i)break;i=a}}function f(e){let{root:t}=n.default.parse(e);return n.default.relative(t,e)}exports.copyProblemDirToTemporaryRoot=a,exports.forciblyRemoveDirectory=o,exports.forciblyRemoveDirectorySync=s;
2
2
  //# sourceMappingURL=temporaryProblemDirCopy.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"temporaryProblemDirCopy.cjs","names":["fs","path","os"],"sources":["../../src/helpers/temporaryProblemDirCopy.ts"],"sourcesContent":["import child_process from 'node:child_process';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\n/**\n * Copy a problem directory into a temporary root, symlinking every ancestor `node_modules` so the\n * copied harness still resolves its imports. Callers must remove the returned `tempRoot`.\n */\nexport async function copyProblemDirToTemporaryRoot(\n problemDir: string\n): Promise<{ tempRoot: string; copiedProblemDir: string }> {\n const tempRoot = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'problem-utils-isolation_'));\n try {\n const absoluteProblemDir = path.resolve(problemDir);\n const copiedProblemDir = path.join(tempRoot, toTempRelativePath(absoluteProblemDir));\n await fs.promises.mkdir(path.dirname(copiedProblemDir), { recursive: true });\n await fs.promises.cp(absoluteProblemDir, copiedProblemDir, {\n recursive: true,\n filter: rejectAbsoluteSymlinks,\n // Keep relative symlinks relative: the default rewrites them to absolute paths into the\n // source tree, so judging the copy could write through them into the checked repository.\n verbatimSymlinks: true,\n });\n await symlinkAllAncestorNodeModules(tempRoot, absoluteProblemDir);\n return { tempRoot, copiedProblemDir };\n } catch (error) {\n await forciblyRemoveDirectory(tempRoot);\n throw error;\n }\n}\n\n/**\n * Remove a temporary directory even when judged code left permission-locked entries in it (e.g. a\n * mode-000 directory makes a plain `fs.rm` fail with EACCES). Returns whether removal succeeded.\n */\nexport async function forciblyRemoveDirectory(dir: string): Promise<boolean> {\n try {\n await fs.promises.rm(dir, { recursive: true, force: true });\n return true;\n } catch {\n unlockPermissions(dir);\n try {\n await fs.promises.rm(dir, { recursive: true, force: true });\n return true;\n } catch {\n return false;\n }\n }\n}\n\n/** Synchronous variant of {@link forciblyRemoveDirectory} for signal handlers. */\nexport function forciblyRemoveDirectorySync(dir: string): boolean {\n try {\n fs.rmSync(dir, { recursive: true, force: true });\n return true;\n } catch {\n unlockPermissions(dir);\n try {\n fs.rmSync(dir, { recursive: true, force: true });\n return true;\n } catch {\n return false;\n }\n }\n}\n\n// chmod does not exist on Windows, where POSIX-mode locks cannot occur anyway. spawnSync reports\n// spawn failures via its return value instead of throwing, so no try/catch is needed.\nfunction unlockPermissions(dir: string): void {\n if (process.platform !== 'win32') child_process.spawnSync('chmod', ['-R', 'u+rwX', dir]);\n}\n\n// An absolute symlink is copied verbatim, so judging the copy could write through it into the\n// original tree; reject it instead of silently breaking the isolation guarantee.\nasync function rejectAbsoluteSymlinks(src: string): Promise<boolean> {\n if (!isCopiedProblemPath(src)) return false;\n const stats = await fs.promises.lstat(src);\n if (stats.isSymbolicLink() && path.isAbsolute(await fs.promises.readlink(src))) {\n throw new Error(`${src} is an absolute symlink, which would escape the temporary copy; use a relative symlink`);\n }\n return true;\n}\n\nfunction isCopiedProblemPath(src: string): boolean {\n const name = path.basename(src);\n return name !== 'node_modules' && name !== '.git';\n}\n\nasync function symlinkAllAncestorNodeModules(tempRoot: string, problemDir: string): Promise<void> {\n let currentDir = path.resolve(problemDir);\n while (true) {\n const nodeModulesPath = path.join(currentDir, 'node_modules');\n if (fs.existsSync(nodeModulesPath)) {\n const targetSymlinkPath = path.join(tempRoot, toTempRelativePath(currentDir), 'node_modules');\n try {\n await fs.promises.symlink(\n nodeModulesPath,\n targetSymlinkPath,\n process.platform === 'win32' ? 'junction' : 'dir'\n );\n } catch {\n // Package resolution is best-effort; the isolation check still reports a clear spawn failure if imports break.\n }\n }\n\n const parentDir = path.dirname(currentDir);\n if (parentDir === currentDir) break;\n currentDir = parentDir;\n }\n}\n\nfunction toTempRelativePath(absolutePath: string): string {\n const { root } = path.parse(absolutePath);\n return path.relative(root, absolutePath);\n}\n"],"mappings":"uPASA,eAAsB,EACpB,EACyD,CACzD,IAAM,EAAW,MAAMA,EAAAA,QAAG,SAAS,QAAQC,EAAAA,QAAK,KAAKC,EAAAA,QAAG,OAAO,EAAG,0BAA0B,CAAC,EAC7F,GAAI,CACF,IAAM,EAAqBD,EAAAA,QAAK,QAAQ,CAAU,EAC5C,EAAmBA,EAAAA,QAAK,KAAK,EAAU,EAAmB,CAAkB,CAAC,EAUnF,OATA,MAAMD,EAAAA,QAAG,SAAS,MAAMC,EAAAA,QAAK,QAAQ,CAAgB,EAAG,CAAE,UAAW,EAAK,CAAC,EAC3E,MAAMD,EAAAA,QAAG,SAAS,GAAG,EAAoB,EAAkB,CACzD,UAAW,GACX,OAAQ,EAGR,iBAAkB,EACpB,CAAC,EACD,MAAM,EAA8B,EAAU,CAAkB,EACzD,CAAE,WAAU,kBAAiB,CACtC,OAAS,EAAO,CAEd,MADA,MAAM,EAAwB,CAAQ,EAChC,CACR,CACF,CAMA,eAAsB,EAAwB,EAA+B,CAC3E,GAAI,CAEF,OADA,MAAMA,EAAAA,QAAG,SAAS,GAAG,EAAK,CAAE,UAAW,GAAM,MAAO,EAAK,CAAC,EACnD,EACT,MAAQ,CACN,EAAkB,CAAG,EACrB,GAAI,CAEF,OADA,MAAMA,EAAAA,QAAG,SAAS,GAAG,EAAK,CAAE,UAAW,GAAM,MAAO,EAAK,CAAC,EACnD,EACT,MAAQ,CACN,MAAO,EACT,CACF,CACF,CAGA,SAAgB,EAA4B,EAAsB,CAChE,GAAI,CAEF,OADA,EAAA,QAAG,OAAO,EAAK,CAAE,UAAW,GAAM,MAAO,EAAK,CAAC,EACxC,EACT,MAAQ,CACN,EAAkB,CAAG,EACrB,GAAI,CAEF,OADA,EAAA,QAAG,OAAO,EAAK,CAAE,UAAW,GAAM,MAAO,EAAK,CAAC,EACxC,EACT,MAAQ,CACN,MAAO,EACT,CACF,CACF,CAIA,SAAS,EAAkB,EAAmB,CACxC,QAAQ,WAAa,SAAS,EAAA,QAAc,UAAU,QAAS,CAAC,KAAM,QAAS,CAAG,CAAC,CACzF,CAIA,eAAe,EAAuB,EAA+B,CACnE,GAAI,CAAC,EAAoB,CAAG,EAAG,MAAO,GAEtC,IAAI,MADgBA,EAAAA,QAAG,SAAS,MAAM,CAAG,EAAA,CAC/B,eAAe,GAAKC,EAAAA,QAAK,WAAW,MAAMD,EAAAA,QAAG,SAAS,SAAS,CAAG,CAAC,EAC3E,MAAU,MAAM,GAAG,EAAI,uFAAuF,EAEhH,MAAO,EACT,CAEA,SAAS,EAAoB,EAAsB,CACjD,IAAM,EAAOC,EAAAA,QAAK,SAAS,CAAG,EAC9B,OAAO,IAAS,gBAAkB,IAAS,MAC7C,CAEA,eAAe,EAA8B,EAAkB,EAAmC,CAChG,IAAI,EAAaA,EAAAA,QAAK,QAAQ,CAAU,EACxC,OAAa,CACX,IAAM,EAAkBA,EAAAA,QAAK,KAAK,EAAY,cAAc,EAC5D,GAAID,EAAAA,QAAG,WAAW,CAAe,EAAG,CAClC,IAAM,EAAoBC,EAAAA,QAAK,KAAK,EAAU,EAAmB,CAAU,EAAG,cAAc,EAC5F,GAAI,CACF,MAAMD,EAAAA,QAAG,SAAS,QAChB,EACA,EACA,QAAQ,WAAa,QAAU,WAAa,KAC9C,CACF,MAAQ,CAER,CACF,CAEA,IAAM,EAAYC,EAAAA,QAAK,QAAQ,CAAU,EACzC,GAAI,IAAc,EAAY,MAC9B,EAAa,CACf,CACF,CAEA,SAAS,EAAmB,EAA8B,CACxD,GAAM,CAAE,QAASA,EAAAA,QAAK,MAAM,CAAY,EACxC,OAAOA,EAAAA,QAAK,SAAS,EAAM,CAAY,CACzC"}
1
+ {"version":3,"file":"temporaryProblemDirCopy.cjs","names":["fs","path","os"],"sources":["../../src/helpers/temporaryProblemDirCopy.ts"],"sourcesContent":["import child_process from 'node:child_process';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\n/**\n * Copy a problem directory into a temporary root, symlinking every ancestor `node_modules` so the\n * copied harness still resolves its imports. Callers must remove the returned `tempRoot`.\n */\nexport async function copyProblemDirToTemporaryRoot(\n problemDir: string,\n options: {\n /** Called as soon as the root exists, so a caller's signal cleanup can cover the copy phase too. */\n onTempRootCreated?: (tempRoot: string) => void;\n } = {}\n): Promise<{ tempRoot: string; copiedProblemDir: string }> {\n const tempRoot = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'problem-utils-isolation_'));\n options.onTempRootCreated?.(tempRoot);\n try {\n const absoluteProblemDir = path.resolve(problemDir);\n const copiedProblemDir = path.join(tempRoot, toTempRelativePath(absoluteProblemDir));\n await fs.promises.mkdir(path.dirname(copiedProblemDir), { recursive: true });\n await fs.promises.cp(absoluteProblemDir, copiedProblemDir, {\n recursive: true,\n filter: rejectAbsoluteSymlinks,\n // Keep relative symlinks relative: the default rewrites them to absolute paths into the\n // source tree, so judging the copy could write through them into the checked repository.\n verbatimSymlinks: true,\n });\n await symlinkAllAncestorNodeModules(tempRoot, absoluteProblemDir);\n return { tempRoot, copiedProblemDir };\n } catch (error) {\n await forciblyRemoveDirectory(tempRoot);\n throw error;\n }\n}\n\n/**\n * Remove a temporary directory even when judged code left permission-locked entries in it (e.g. a\n * mode-000 directory makes a plain `fs.rm` fail with EACCES). Returns whether removal succeeded.\n */\nexport async function forciblyRemoveDirectory(dir: string): Promise<boolean> {\n try {\n await fs.promises.rm(dir, { recursive: true, force: true });\n return true;\n } catch {\n unlockPermissions(dir);\n try {\n await fs.promises.rm(dir, { recursive: true, force: true });\n return true;\n } catch {\n return false;\n }\n }\n}\n\n/** Synchronous variant of {@link forciblyRemoveDirectory} for signal handlers. */\nexport function forciblyRemoveDirectorySync(dir: string): boolean {\n try {\n fs.rmSync(dir, { recursive: true, force: true });\n return true;\n } catch {\n unlockPermissions(dir);\n try {\n fs.rmSync(dir, { recursive: true, force: true });\n return true;\n } catch {\n return false;\n }\n }\n}\n\n// chmod does not exist on Windows, where POSIX-mode locks cannot occur anyway. spawnSync reports\n// spawn failures via its return value instead of throwing, so no try/catch is needed.\nfunction unlockPermissions(dir: string): void {\n if (process.platform !== 'win32') child_process.spawnSync('chmod', ['-R', 'u+rwX', dir]);\n}\n\n// An absolute symlink is copied verbatim, so judging the copy could write through it into the\n// original tree; reject it instead of silently breaking the isolation guarantee.\nasync function rejectAbsoluteSymlinks(src: string): Promise<boolean> {\n if (!isCopiedProblemPath(src)) return false;\n const stats = await fs.promises.lstat(src);\n if (stats.isSymbolicLink() && path.isAbsolute(await fs.promises.readlink(src))) {\n throw new Error(`${src} is an absolute symlink, which would escape the temporary copy; use a relative symlink`);\n }\n return true;\n}\n\nfunction isCopiedProblemPath(src: string): boolean {\n const name = path.basename(src);\n return name !== 'node_modules' && name !== '.git';\n}\n\nasync function symlinkAllAncestorNodeModules(tempRoot: string, problemDir: string): Promise<void> {\n let currentDir = path.resolve(problemDir);\n while (true) {\n const nodeModulesPath = path.join(currentDir, 'node_modules');\n if (fs.existsSync(nodeModulesPath)) {\n const targetSymlinkPath = path.join(tempRoot, toTempRelativePath(currentDir), 'node_modules');\n try {\n await fs.promises.symlink(\n nodeModulesPath,\n targetSymlinkPath,\n process.platform === 'win32' ? 'junction' : 'dir'\n );\n } catch {\n // Package resolution is best-effort; the isolation check still reports a clear spawn failure if imports break.\n }\n }\n\n const parentDir = path.dirname(currentDir);\n if (parentDir === currentDir) break;\n currentDir = parentDir;\n }\n}\n\nfunction toTempRelativePath(absolutePath: string): string {\n const { root } = path.parse(absolutePath);\n return path.relative(root, absolutePath);\n}\n"],"mappings":"uPASA,eAAsB,EACpB,EACA,EAGI,CAAC,EACoD,CACzD,IAAM,EAAW,MAAMA,EAAAA,QAAG,SAAS,QAAQC,EAAAA,QAAK,KAAKC,EAAAA,QAAG,OAAO,EAAG,0BAA0B,CAAC,EAC7F,EAAQ,oBAAoB,CAAQ,EACpC,GAAI,CACF,IAAM,EAAqBD,EAAAA,QAAK,QAAQ,CAAU,EAC5C,EAAmBA,EAAAA,QAAK,KAAK,EAAU,EAAmB,CAAkB,CAAC,EAUnF,OATA,MAAMD,EAAAA,QAAG,SAAS,MAAMC,EAAAA,QAAK,QAAQ,CAAgB,EAAG,CAAE,UAAW,EAAK,CAAC,EAC3E,MAAMD,EAAAA,QAAG,SAAS,GAAG,EAAoB,EAAkB,CACzD,UAAW,GACX,OAAQ,EAGR,iBAAkB,EACpB,CAAC,EACD,MAAM,EAA8B,EAAU,CAAkB,EACzD,CAAE,WAAU,kBAAiB,CACtC,OAAS,EAAO,CAEd,MADA,MAAM,EAAwB,CAAQ,EAChC,CACR,CACF,CAMA,eAAsB,EAAwB,EAA+B,CAC3E,GAAI,CAEF,OADA,MAAMA,EAAAA,QAAG,SAAS,GAAG,EAAK,CAAE,UAAW,GAAM,MAAO,EAAK,CAAC,EACnD,EACT,MAAQ,CACN,EAAkB,CAAG,EACrB,GAAI,CAEF,OADA,MAAMA,EAAAA,QAAG,SAAS,GAAG,EAAK,CAAE,UAAW,GAAM,MAAO,EAAK,CAAC,EACnD,EACT,MAAQ,CACN,MAAO,EACT,CACF,CACF,CAGA,SAAgB,EAA4B,EAAsB,CAChE,GAAI,CAEF,OADA,EAAA,QAAG,OAAO,EAAK,CAAE,UAAW,GAAM,MAAO,EAAK,CAAC,EACxC,EACT,MAAQ,CACN,EAAkB,CAAG,EACrB,GAAI,CAEF,OADA,EAAA,QAAG,OAAO,EAAK,CAAE,UAAW,GAAM,MAAO,EAAK,CAAC,EACxC,EACT,MAAQ,CACN,MAAO,EACT,CACF,CACF,CAIA,SAAS,EAAkB,EAAmB,CACxC,QAAQ,WAAa,SAAS,EAAA,QAAc,UAAU,QAAS,CAAC,KAAM,QAAS,CAAG,CAAC,CACzF,CAIA,eAAe,EAAuB,EAA+B,CACnE,GAAI,CAAC,EAAoB,CAAG,EAAG,MAAO,GAEtC,IAAI,MADgBA,EAAAA,QAAG,SAAS,MAAM,CAAG,EAAA,CAC/B,eAAe,GAAKC,EAAAA,QAAK,WAAW,MAAMD,EAAAA,QAAG,SAAS,SAAS,CAAG,CAAC,EAC3E,MAAU,MAAM,GAAG,EAAI,uFAAuF,EAEhH,MAAO,EACT,CAEA,SAAS,EAAoB,EAAsB,CACjD,IAAM,EAAOC,EAAAA,QAAK,SAAS,CAAG,EAC9B,OAAO,IAAS,gBAAkB,IAAS,MAC7C,CAEA,eAAe,EAA8B,EAAkB,EAAmC,CAChG,IAAI,EAAaA,EAAAA,QAAK,QAAQ,CAAU,EACxC,OAAa,CACX,IAAM,EAAkBA,EAAAA,QAAK,KAAK,EAAY,cAAc,EAC5D,GAAID,EAAAA,QAAG,WAAW,CAAe,EAAG,CAClC,IAAM,EAAoBC,EAAAA,QAAK,KAAK,EAAU,EAAmB,CAAU,EAAG,cAAc,EAC5F,GAAI,CACF,MAAMD,EAAAA,QAAG,SAAS,QAChB,EACA,EACA,QAAQ,WAAa,QAAU,WAAa,KAC9C,CACF,MAAQ,CAER,CACF,CAEA,IAAM,EAAYC,EAAAA,QAAK,QAAQ,CAAU,EACzC,GAAI,IAAc,EAAY,MAC9B,EAAa,CACf,CACF,CAEA,SAAS,EAAmB,EAA8B,CACxD,GAAM,CAAE,QAASA,EAAAA,QAAK,MAAM,CAAY,EACxC,OAAOA,EAAAA,QAAK,SAAS,EAAM,CAAY,CACzC"}
@@ -2,7 +2,10 @@
2
2
  * Copy a problem directory into a temporary root, symlinking every ancestor `node_modules` so the
3
3
  * copied harness still resolves its imports. Callers must remove the returned `tempRoot`.
4
4
  */
5
- export declare function copyProblemDirToTemporaryRoot(problemDir: string): Promise<{
5
+ export declare function copyProblemDirToTemporaryRoot(problemDir: string, options?: {
6
+ /** Called as soon as the root exists, so a caller's signal cleanup can cover the copy phase too. */
7
+ onTempRootCreated?: (tempRoot: string) => void;
8
+ }): Promise<{
6
9
  tempRoot: string;
7
10
  copiedProblemDir: string;
8
11
  }>;
@@ -1,2 +1,2 @@
1
- import e from"node:path";import t from"node:child_process";import n from"node:fs";import r from"node:os";async function i(t){let i=await n.promises.mkdtemp(e.join(r.tmpdir(),`problem-utils-isolation_`));try{let r=e.resolve(t),a=e.join(i,d(r));return await n.promises.mkdir(e.dirname(a),{recursive:!0}),await n.promises.cp(r,a,{recursive:!0,filter:c,verbatimSymlinks:!0}),await u(i,r),{tempRoot:i,copiedProblemDir:a}}catch(e){throw await a(i),e}}async function a(e){try{return await n.promises.rm(e,{recursive:!0,force:!0}),!0}catch{s(e);try{return await n.promises.rm(e,{recursive:!0,force:!0}),!0}catch{return!1}}}function o(e){try{return n.rmSync(e,{recursive:!0,force:!0}),!0}catch{s(e);try{return n.rmSync(e,{recursive:!0,force:!0}),!0}catch{return!1}}}function s(e){process.platform!==`win32`&&t.spawnSync(`chmod`,[`-R`,`u+rwX`,e])}async function c(t){if(!l(t))return!1;if((await n.promises.lstat(t)).isSymbolicLink()&&e.isAbsolute(await n.promises.readlink(t)))throw Error(`${t} is an absolute symlink, which would escape the temporary copy; use a relative symlink`);return!0}function l(t){let n=e.basename(t);return n!==`node_modules`&&n!==`.git`}async function u(t,r){let i=e.resolve(r);for(;;){let r=e.join(i,`node_modules`);if(n.existsSync(r)){let a=e.join(t,d(i),`node_modules`);try{await n.promises.symlink(r,a,process.platform===`win32`?`junction`:`dir`)}catch{}}let a=e.dirname(i);if(a===i)break;i=a}}function d(t){let{root:n}=e.parse(t);return e.relative(n,t)}export{i as copyProblemDirToTemporaryRoot,a as forciblyRemoveDirectory,o as forciblyRemoveDirectorySync};
1
+ import e from"node:fs";import t from"node:path";import n from"node:child_process";import r from"node:os";async function i(n,i={}){let o=await e.promises.mkdtemp(t.join(r.tmpdir(),`problem-utils-isolation_`));i.onTempRootCreated?.(o);try{let r=t.resolve(n),i=t.join(o,d(r));return await e.promises.mkdir(t.dirname(i),{recursive:!0}),await e.promises.cp(r,i,{recursive:!0,filter:c,verbatimSymlinks:!0}),await u(o,r),{tempRoot:o,copiedProblemDir:i}}catch(e){throw await a(o),e}}async function a(t){try{return await e.promises.rm(t,{recursive:!0,force:!0}),!0}catch{s(t);try{return await e.promises.rm(t,{recursive:!0,force:!0}),!0}catch{return!1}}}function o(t){try{return e.rmSync(t,{recursive:!0,force:!0}),!0}catch{s(t);try{return e.rmSync(t,{recursive:!0,force:!0}),!0}catch{return!1}}}function s(e){process.platform!==`win32`&&n.spawnSync(`chmod`,[`-R`,`u+rwX`,e])}async function c(n){if(!l(n))return!1;if((await e.promises.lstat(n)).isSymbolicLink()&&t.isAbsolute(await e.promises.readlink(n)))throw Error(`${n} is an absolute symlink, which would escape the temporary copy; use a relative symlink`);return!0}function l(e){let n=t.basename(e);return n!==`node_modules`&&n!==`.git`}async function u(n,r){let i=t.resolve(r);for(;;){let r=t.join(i,`node_modules`);if(e.existsSync(r)){let a=t.join(n,d(i),`node_modules`);try{await e.promises.symlink(r,a,process.platform===`win32`?`junction`:`dir`)}catch{}}let a=t.dirname(i);if(a===i)break;i=a}}function d(e){let{root:n}=t.parse(e);return t.relative(n,e)}export{i as copyProblemDirToTemporaryRoot,a as forciblyRemoveDirectory,o as forciblyRemoveDirectorySync};
2
2
  //# sourceMappingURL=temporaryProblemDirCopy.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"temporaryProblemDirCopy.js","names":[],"sources":["../../src/helpers/temporaryProblemDirCopy.ts"],"sourcesContent":["import child_process from 'node:child_process';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\n/**\n * Copy a problem directory into a temporary root, symlinking every ancestor `node_modules` so the\n * copied harness still resolves its imports. Callers must remove the returned `tempRoot`.\n */\nexport async function copyProblemDirToTemporaryRoot(\n problemDir: string\n): Promise<{ tempRoot: string; copiedProblemDir: string }> {\n const tempRoot = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'problem-utils-isolation_'));\n try {\n const absoluteProblemDir = path.resolve(problemDir);\n const copiedProblemDir = path.join(tempRoot, toTempRelativePath(absoluteProblemDir));\n await fs.promises.mkdir(path.dirname(copiedProblemDir), { recursive: true });\n await fs.promises.cp(absoluteProblemDir, copiedProblemDir, {\n recursive: true,\n filter: rejectAbsoluteSymlinks,\n // Keep relative symlinks relative: the default rewrites them to absolute paths into the\n // source tree, so judging the copy could write through them into the checked repository.\n verbatimSymlinks: true,\n });\n await symlinkAllAncestorNodeModules(tempRoot, absoluteProblemDir);\n return { tempRoot, copiedProblemDir };\n } catch (error) {\n await forciblyRemoveDirectory(tempRoot);\n throw error;\n }\n}\n\n/**\n * Remove a temporary directory even when judged code left permission-locked entries in it (e.g. a\n * mode-000 directory makes a plain `fs.rm` fail with EACCES). Returns whether removal succeeded.\n */\nexport async function forciblyRemoveDirectory(dir: string): Promise<boolean> {\n try {\n await fs.promises.rm(dir, { recursive: true, force: true });\n return true;\n } catch {\n unlockPermissions(dir);\n try {\n await fs.promises.rm(dir, { recursive: true, force: true });\n return true;\n } catch {\n return false;\n }\n }\n}\n\n/** Synchronous variant of {@link forciblyRemoveDirectory} for signal handlers. */\nexport function forciblyRemoveDirectorySync(dir: string): boolean {\n try {\n fs.rmSync(dir, { recursive: true, force: true });\n return true;\n } catch {\n unlockPermissions(dir);\n try {\n fs.rmSync(dir, { recursive: true, force: true });\n return true;\n } catch {\n return false;\n }\n }\n}\n\n// chmod does not exist on Windows, where POSIX-mode locks cannot occur anyway. spawnSync reports\n// spawn failures via its return value instead of throwing, so no try/catch is needed.\nfunction unlockPermissions(dir: string): void {\n if (process.platform !== 'win32') child_process.spawnSync('chmod', ['-R', 'u+rwX', dir]);\n}\n\n// An absolute symlink is copied verbatim, so judging the copy could write through it into the\n// original tree; reject it instead of silently breaking the isolation guarantee.\nasync function rejectAbsoluteSymlinks(src: string): Promise<boolean> {\n if (!isCopiedProblemPath(src)) return false;\n const stats = await fs.promises.lstat(src);\n if (stats.isSymbolicLink() && path.isAbsolute(await fs.promises.readlink(src))) {\n throw new Error(`${src} is an absolute symlink, which would escape the temporary copy; use a relative symlink`);\n }\n return true;\n}\n\nfunction isCopiedProblemPath(src: string): boolean {\n const name = path.basename(src);\n return name !== 'node_modules' && name !== '.git';\n}\n\nasync function symlinkAllAncestorNodeModules(tempRoot: string, problemDir: string): Promise<void> {\n let currentDir = path.resolve(problemDir);\n while (true) {\n const nodeModulesPath = path.join(currentDir, 'node_modules');\n if (fs.existsSync(nodeModulesPath)) {\n const targetSymlinkPath = path.join(tempRoot, toTempRelativePath(currentDir), 'node_modules');\n try {\n await fs.promises.symlink(\n nodeModulesPath,\n targetSymlinkPath,\n process.platform === 'win32' ? 'junction' : 'dir'\n );\n } catch {\n // Package resolution is best-effort; the isolation check still reports a clear spawn failure if imports break.\n }\n }\n\n const parentDir = path.dirname(currentDir);\n if (parentDir === currentDir) break;\n currentDir = parentDir;\n }\n}\n\nfunction toTempRelativePath(absolutePath: string): string {\n const { root } = path.parse(absolutePath);\n return path.relative(root, absolutePath);\n}\n"],"mappings":"yGASA,eAAsB,EACpB,EACyD,CACzD,IAAM,EAAW,MAAM,EAAG,SAAS,QAAQ,EAAK,KAAK,EAAG,OAAO,EAAG,0BAA0B,CAAC,EAC7F,GAAI,CACF,IAAM,EAAqB,EAAK,QAAQ,CAAU,EAC5C,EAAmB,EAAK,KAAK,EAAU,EAAmB,CAAkB,CAAC,EAUnF,OATA,MAAM,EAAG,SAAS,MAAM,EAAK,QAAQ,CAAgB,EAAG,CAAE,UAAW,EAAK,CAAC,EAC3E,MAAM,EAAG,SAAS,GAAG,EAAoB,EAAkB,CACzD,UAAW,GACX,OAAQ,EAGR,iBAAkB,EACpB,CAAC,EACD,MAAM,EAA8B,EAAU,CAAkB,EACzD,CAAE,WAAU,kBAAiB,CACtC,OAAS,EAAO,CAEd,MADA,MAAM,EAAwB,CAAQ,EAChC,CACR,CACF,CAMA,eAAsB,EAAwB,EAA+B,CAC3E,GAAI,CAEF,OADA,MAAM,EAAG,SAAS,GAAG,EAAK,CAAE,UAAW,GAAM,MAAO,EAAK,CAAC,EACnD,EACT,MAAQ,CACN,EAAkB,CAAG,EACrB,GAAI,CAEF,OADA,MAAM,EAAG,SAAS,GAAG,EAAK,CAAE,UAAW,GAAM,MAAO,EAAK,CAAC,EACnD,EACT,MAAQ,CACN,MAAO,EACT,CACF,CACF,CAGA,SAAgB,EAA4B,EAAsB,CAChE,GAAI,CAEF,OADA,EAAG,OAAO,EAAK,CAAE,UAAW,GAAM,MAAO,EAAK,CAAC,EACxC,EACT,MAAQ,CACN,EAAkB,CAAG,EACrB,GAAI,CAEF,OADA,EAAG,OAAO,EAAK,CAAE,UAAW,GAAM,MAAO,EAAK,CAAC,EACxC,EACT,MAAQ,CACN,MAAO,EACT,CACF,CACF,CAIA,SAAS,EAAkB,EAAmB,CACxC,QAAQ,WAAa,SAAS,EAAc,UAAU,QAAS,CAAC,KAAM,QAAS,CAAG,CAAC,CACzF,CAIA,eAAe,EAAuB,EAA+B,CACnE,GAAI,CAAC,EAAoB,CAAG,EAAG,MAAO,GAEtC,IAAI,MADgB,EAAG,SAAS,MAAM,CAAG,EAAA,CAC/B,eAAe,GAAK,EAAK,WAAW,MAAM,EAAG,SAAS,SAAS,CAAG,CAAC,EAC3E,MAAU,MAAM,GAAG,EAAI,uFAAuF,EAEhH,MAAO,EACT,CAEA,SAAS,EAAoB,EAAsB,CACjD,IAAM,EAAO,EAAK,SAAS,CAAG,EAC9B,OAAO,IAAS,gBAAkB,IAAS,MAC7C,CAEA,eAAe,EAA8B,EAAkB,EAAmC,CAChG,IAAI,EAAa,EAAK,QAAQ,CAAU,EACxC,OAAa,CACX,IAAM,EAAkB,EAAK,KAAK,EAAY,cAAc,EAC5D,GAAI,EAAG,WAAW,CAAe,EAAG,CAClC,IAAM,EAAoB,EAAK,KAAK,EAAU,EAAmB,CAAU,EAAG,cAAc,EAC5F,GAAI,CACF,MAAM,EAAG,SAAS,QAChB,EACA,EACA,QAAQ,WAAa,QAAU,WAAa,KAC9C,CACF,MAAQ,CAER,CACF,CAEA,IAAM,EAAY,EAAK,QAAQ,CAAU,EACzC,GAAI,IAAc,EAAY,MAC9B,EAAa,CACf,CACF,CAEA,SAAS,EAAmB,EAA8B,CACxD,GAAM,CAAE,QAAS,EAAK,MAAM,CAAY,EACxC,OAAO,EAAK,SAAS,EAAM,CAAY,CACzC"}
1
+ {"version":3,"file":"temporaryProblemDirCopy.js","names":[],"sources":["../../src/helpers/temporaryProblemDirCopy.ts"],"sourcesContent":["import child_process from 'node:child_process';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\n/**\n * Copy a problem directory into a temporary root, symlinking every ancestor `node_modules` so the\n * copied harness still resolves its imports. Callers must remove the returned `tempRoot`.\n */\nexport async function copyProblemDirToTemporaryRoot(\n problemDir: string,\n options: {\n /** Called as soon as the root exists, so a caller's signal cleanup can cover the copy phase too. */\n onTempRootCreated?: (tempRoot: string) => void;\n } = {}\n): Promise<{ tempRoot: string; copiedProblemDir: string }> {\n const tempRoot = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'problem-utils-isolation_'));\n options.onTempRootCreated?.(tempRoot);\n try {\n const absoluteProblemDir = path.resolve(problemDir);\n const copiedProblemDir = path.join(tempRoot, toTempRelativePath(absoluteProblemDir));\n await fs.promises.mkdir(path.dirname(copiedProblemDir), { recursive: true });\n await fs.promises.cp(absoluteProblemDir, copiedProblemDir, {\n recursive: true,\n filter: rejectAbsoluteSymlinks,\n // Keep relative symlinks relative: the default rewrites them to absolute paths into the\n // source tree, so judging the copy could write through them into the checked repository.\n verbatimSymlinks: true,\n });\n await symlinkAllAncestorNodeModules(tempRoot, absoluteProblemDir);\n return { tempRoot, copiedProblemDir };\n } catch (error) {\n await forciblyRemoveDirectory(tempRoot);\n throw error;\n }\n}\n\n/**\n * Remove a temporary directory even when judged code left permission-locked entries in it (e.g. a\n * mode-000 directory makes a plain `fs.rm` fail with EACCES). Returns whether removal succeeded.\n */\nexport async function forciblyRemoveDirectory(dir: string): Promise<boolean> {\n try {\n await fs.promises.rm(dir, { recursive: true, force: true });\n return true;\n } catch {\n unlockPermissions(dir);\n try {\n await fs.promises.rm(dir, { recursive: true, force: true });\n return true;\n } catch {\n return false;\n }\n }\n}\n\n/** Synchronous variant of {@link forciblyRemoveDirectory} for signal handlers. */\nexport function forciblyRemoveDirectorySync(dir: string): boolean {\n try {\n fs.rmSync(dir, { recursive: true, force: true });\n return true;\n } catch {\n unlockPermissions(dir);\n try {\n fs.rmSync(dir, { recursive: true, force: true });\n return true;\n } catch {\n return false;\n }\n }\n}\n\n// chmod does not exist on Windows, where POSIX-mode locks cannot occur anyway. spawnSync reports\n// spawn failures via its return value instead of throwing, so no try/catch is needed.\nfunction unlockPermissions(dir: string): void {\n if (process.platform !== 'win32') child_process.spawnSync('chmod', ['-R', 'u+rwX', dir]);\n}\n\n// An absolute symlink is copied verbatim, so judging the copy could write through it into the\n// original tree; reject it instead of silently breaking the isolation guarantee.\nasync function rejectAbsoluteSymlinks(src: string): Promise<boolean> {\n if (!isCopiedProblemPath(src)) return false;\n const stats = await fs.promises.lstat(src);\n if (stats.isSymbolicLink() && path.isAbsolute(await fs.promises.readlink(src))) {\n throw new Error(`${src} is an absolute symlink, which would escape the temporary copy; use a relative symlink`);\n }\n return true;\n}\n\nfunction isCopiedProblemPath(src: string): boolean {\n const name = path.basename(src);\n return name !== 'node_modules' && name !== '.git';\n}\n\nasync function symlinkAllAncestorNodeModules(tempRoot: string, problemDir: string): Promise<void> {\n let currentDir = path.resolve(problemDir);\n while (true) {\n const nodeModulesPath = path.join(currentDir, 'node_modules');\n if (fs.existsSync(nodeModulesPath)) {\n const targetSymlinkPath = path.join(tempRoot, toTempRelativePath(currentDir), 'node_modules');\n try {\n await fs.promises.symlink(\n nodeModulesPath,\n targetSymlinkPath,\n process.platform === 'win32' ? 'junction' : 'dir'\n );\n } catch {\n // Package resolution is best-effort; the isolation check still reports a clear spawn failure if imports break.\n }\n }\n\n const parentDir = path.dirname(currentDir);\n if (parentDir === currentDir) break;\n currentDir = parentDir;\n }\n}\n\nfunction toTempRelativePath(absolutePath: string): string {\n const { root } = path.parse(absolutePath);\n return path.relative(root, absolutePath);\n}\n"],"mappings":"yGASA,eAAsB,EACpB,EACA,EAGI,CAAC,EACoD,CACzD,IAAM,EAAW,MAAM,EAAG,SAAS,QAAQ,EAAK,KAAK,EAAG,OAAO,EAAG,0BAA0B,CAAC,EAC7F,EAAQ,oBAAoB,CAAQ,EACpC,GAAI,CACF,IAAM,EAAqB,EAAK,QAAQ,CAAU,EAC5C,EAAmB,EAAK,KAAK,EAAU,EAAmB,CAAkB,CAAC,EAUnF,OATA,MAAM,EAAG,SAAS,MAAM,EAAK,QAAQ,CAAgB,EAAG,CAAE,UAAW,EAAK,CAAC,EAC3E,MAAM,EAAG,SAAS,GAAG,EAAoB,EAAkB,CACzD,UAAW,GACX,OAAQ,EAGR,iBAAkB,EACpB,CAAC,EACD,MAAM,EAA8B,EAAU,CAAkB,EACzD,CAAE,WAAU,kBAAiB,CACtC,OAAS,EAAO,CAEd,MADA,MAAM,EAAwB,CAAQ,EAChC,CACR,CACF,CAMA,eAAsB,EAAwB,EAA+B,CAC3E,GAAI,CAEF,OADA,MAAM,EAAG,SAAS,GAAG,EAAK,CAAE,UAAW,GAAM,MAAO,EAAK,CAAC,EACnD,EACT,MAAQ,CACN,EAAkB,CAAG,EACrB,GAAI,CAEF,OADA,MAAM,EAAG,SAAS,GAAG,EAAK,CAAE,UAAW,GAAM,MAAO,EAAK,CAAC,EACnD,EACT,MAAQ,CACN,MAAO,EACT,CACF,CACF,CAGA,SAAgB,EAA4B,EAAsB,CAChE,GAAI,CAEF,OADA,EAAG,OAAO,EAAK,CAAE,UAAW,GAAM,MAAO,EAAK,CAAC,EACxC,EACT,MAAQ,CACN,EAAkB,CAAG,EACrB,GAAI,CAEF,OADA,EAAG,OAAO,EAAK,CAAE,UAAW,GAAM,MAAO,EAAK,CAAC,EACxC,EACT,MAAQ,CACN,MAAO,EACT,CACF,CACF,CAIA,SAAS,EAAkB,EAAmB,CACxC,QAAQ,WAAa,SAAS,EAAc,UAAU,QAAS,CAAC,KAAM,QAAS,CAAG,CAAC,CACzF,CAIA,eAAe,EAAuB,EAA+B,CACnE,GAAI,CAAC,EAAoB,CAAG,EAAG,MAAO,GAEtC,IAAI,MADgB,EAAG,SAAS,MAAM,CAAG,EAAA,CAC/B,eAAe,GAAK,EAAK,WAAW,MAAM,EAAG,SAAS,SAAS,CAAG,CAAC,EAC3E,MAAU,MAAM,GAAG,EAAI,uFAAuF,EAEhH,MAAO,EACT,CAEA,SAAS,EAAoB,EAAsB,CACjD,IAAM,EAAO,EAAK,SAAS,CAAG,EAC9B,OAAO,IAAS,gBAAkB,IAAS,MAC7C,CAEA,eAAe,EAA8B,EAAkB,EAAmC,CAChG,IAAI,EAAa,EAAK,QAAQ,CAAU,EACxC,OAAa,CACX,IAAM,EAAkB,EAAK,KAAK,EAAY,cAAc,EAC5D,GAAI,EAAG,WAAW,CAAe,EAAG,CAClC,IAAM,EAAoB,EAAK,KAAK,EAAU,EAAmB,CAAU,EAAG,cAAc,EAC5F,GAAI,CACF,MAAM,EAAG,SAAS,QAChB,EACA,EACA,QAAQ,WAAa,QAAU,WAAa,KAC9C,CACF,MAAQ,CAER,CACF,CAEA,IAAM,EAAY,EAAK,QAAQ,CAAU,EACzC,GAAI,IAAc,EAAY,MAC9B,EAAa,CACf,CACF,CAEA,SAAS,EAAmB,EAA8B,CACxD,GAAM,CAAE,QAAS,EAAK,MAAM,CAAY,EACxC,OAAO,EAAK,SAAS,EAAM,CAAY,CACzC"}
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./helpers/defaultStdioHarness.cjs"),t=require("./helpers/parseArgs.cjs"),n=require("./helpers/parseCsv.cjs"),r=require("./types/testCaseResult.cjs"),i=require("./helpers/printTestCaseResult.cjs"),a=require("./helpers/removeCommentsInSourceCode.cjs"),o=require("./helpers/sandboxUser.cjs"),s=require("./helpers/safeFs.cjs"),c=require("./helpers/runCommandInTemporaryPackageManagerProject.cjs"),l=require("./helpers/runPythonInTemporaryUvProject.cjs"),u=require("./helpers/sourceCodeGrammars.cjs"),d=require("./helpers/startHttpServer.cjs"),f=require("./types/decisionCode.cjs"),p=require("./types/problem.cjs");exports.CsvParseError=n.CsvParseError,exports.DecisionCode=f.DecisionCode,exports.HARNESS_FILE_PRESETS=e.HARNESS_FILE_PRESETS,exports.SANDBOX_ENV_PREFIX=o.SANDBOX_ENV_PREFIX,exports.SANDBOX_USER_ENV_NAME=o.SANDBOX_USER_ENV_NAME,exports.SANDBOX_WATCHDOG_GRACE_SECONDS=o.SANDBOX_WATCHDOG_GRACE_SECONDS,exports.TEST_CASE_RESULT_PREFIX=r.TEST_CASE_RESULT_PREFIX,exports.TIMEOUT_COMMAND=o.TIMEOUT_COMMAND,exports.codeRuleSchema=p.codeRuleSchema,exports.copyPackageManagerProjectFiles=c.copyPackageManagerProjectFiles,exports.copyWithoutFollowingSymlinks=s.copyWithoutFollowingSymlinks,exports.createDirectoryWithoutFollowingSymlinks=s.createDirectoryWithoutFollowingSymlinks,exports.encodeFileForTestCaseResult=i.encodeFileForTestCaseResult,exports.findDefaultStdioHarnessFiles=e.findDefaultStdioHarnessFiles,exports.forceRemove=o.forceRemove,exports.getSandboxUserEnvOverrides=o.getSandboxUserEnvOverrides,exports.getTrustedHelperEnv=o.getTrustedHelperEnv,exports.isContainedPath=s.isContainedPath,exports.isDefaultStdioHarnessSource=e.isDefaultStdioHarnessSource,exports.isSandboxWrappedCommand=o.isSandboxWrappedCommand,exports.killSandboxUserProcesses=o.killSandboxUserProcesses,exports.languageIdToSourceCodeGrammar=u.languageIdToSourceCodeGrammar,exports.makeAccessibleToSandboxUser=o.makeAccessibleToSandboxUser,exports.normalizeCodeRule=p.normalizeCodeRule,exports.parseArgs=t.parseArgs,exports.parseCsv=n.parseCsv,exports.parseCsvRecords=n.parseCsvRecords,exports.prependInsideSandboxWrapper=o.prependInsideSandboxWrapper,exports.printTestCaseResult=i.printTestCaseResult,exports.problemMarkdownFrontMatterSchema=p.problemMarkdownFrontMatterSchema,exports.relaxPermissionsAsSandboxUser=o.relaxPermissionsAsSandboxUser,exports.removeCommentsAndStringsInSourceCode=a.removeCommentsAndStringsInSourceCode,exports.removeCommentsInSourceCode=a.removeCommentsInSourceCode,exports.runCommandInTemporaryPackageManagerProject=c.runCommandInTemporaryPackageManagerProject,exports.runPythonInTemporaryUvProject=l.runPythonInTemporaryUvProject,exports.sandboxUserName=o.sandboxUserName,exports.startHttpServer=d.startHttpServer,exports.startSandboxTimeoutWatchdog=o.startSandboxTimeoutWatchdog,exports.testCaseResultSchema=r.testCaseResultSchema,exports.wrapCommandWithSandboxUser=o.wrapCommandWithSandboxUser,exports.writeFileWithoutFollowingSymlinks=s.writeFileWithoutFollowingSymlinks;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./helpers/compareStdoutAsSpaceSeparatedTokens.cjs"),t=require("./types/testCaseResult.cjs"),n=require("./helpers/printTestCaseResult.cjs"),r=require("./helpers/sandboxUser.cjs"),i=require("./helpers/safeFs.cjs"),a=require("./helpers/compareExpectedOutputFiles.cjs"),o=require("./helpers/defaultStdioHarness.cjs"),s=require("./helpers/parseArgs.cjs"),c=require("./helpers/parseCsv.cjs"),l=require("./helpers/readTestCases.cjs"),u=require("./helpers/removeCommentsInSourceCode.cjs"),d=require("./helpers/runCommandInTemporaryPackageManagerProject.cjs"),f=require("./helpers/runPythonInTemporaryUvProject.cjs"),p=require("./helpers/sourceCodeGrammars.cjs"),m=require("./helpers/startHttpServer.cjs"),h=require("./types/decisionCode.cjs"),g=require("./types/problem.cjs");exports.CsvParseError=c.CsvParseError,exports.DecisionCode=h.DecisionCode,exports.HARNESS_FILE_PRESETS=o.HARNESS_FILE_PRESETS,exports.MAX_COMPARED_FILE_BYTES=a.MAX_COMPARED_FILE_BYTES,exports.MAX_REPORTED_FILE_BYTES=a.MAX_REPORTED_FILE_BYTES,exports.SANDBOX_ENV_PREFIX=r.SANDBOX_ENV_PREFIX,exports.SANDBOX_USER_ENV_NAME=r.SANDBOX_USER_ENV_NAME,exports.SANDBOX_WATCHDOG_GRACE_SECONDS=r.SANDBOX_WATCHDOG_GRACE_SECONDS,exports.TEST_CASE_RESULT_PREFIX=t.TEST_CASE_RESULT_PREFIX,exports.TIMEOUT_COMMAND=r.TIMEOUT_COMMAND,exports.codeRuleSchema=g.codeRuleSchema,exports.compareExpectedOutputFiles=a.compareExpectedOutputFiles,exports.compareStdoutAsSpaceSeparatedTokens=e.compareStdoutAsSpaceSeparatedTokens,exports.copyPackageManagerProjectFiles=d.copyPackageManagerProjectFiles,exports.copyWithoutFollowingSymlinks=i.copyWithoutFollowingSymlinks,exports.createDirectoryWithoutFollowingSymlinks=i.createDirectoryWithoutFollowingSymlinks,exports.encodeFileForTestCaseResult=n.encodeFileForTestCaseResult,exports.findDefaultStdioHarnessFiles=o.findDefaultStdioHarnessFiles,exports.forceRemove=r.forceRemove,exports.getSandboxUserEnvOverrides=r.getSandboxUserEnvOverrides,exports.getTrustedHelperEnv=r.getTrustedHelperEnv,exports.isContainedPath=i.isContainedPath,exports.isDefaultStdioHarnessSource=o.isDefaultStdioHarnessSource,exports.isSandboxWrappedCommand=r.isSandboxWrappedCommand,exports.judgeAgainstExpectations=a.judgeAgainstExpectations,exports.killSandboxUserProcesses=r.killSandboxUserProcesses,exports.languageIdToSourceCodeGrammar=p.languageIdToSourceCodeGrammar,exports.makeAccessibleToSandboxUser=r.makeAccessibleToSandboxUser,exports.makeTraversableBySandboxUser=r.makeTraversableBySandboxUser,exports.mergeComparisonOutputFiles=a.mergeComparisonOutputFiles,exports.normalizeCodeRule=g.normalizeCodeRule,exports.parseArgs=s.parseArgs,exports.parseCsv=c.parseCsv,exports.parseCsvRecords=c.parseCsvRecords,exports.prependInsideSandboxWrapper=r.prependInsideSandboxWrapper,exports.printTestCaseResult=n.printTestCaseResult,exports.problemMarkdownFrontMatterSchema=g.problemMarkdownFrontMatterSchema,exports.readTestCases=l.readTestCases,exports.relaxPermissionsAsSandboxUser=r.relaxPermissionsAsSandboxUser,exports.removeCommentsAndStringsInSourceCode=u.removeCommentsAndStringsInSourceCode,exports.removeCommentsInSourceCode=u.removeCommentsInSourceCode,exports.runCommandInTemporaryPackageManagerProject=d.runCommandInTemporaryPackageManagerProject,exports.runPythonInTemporaryUvProject=f.runPythonInTemporaryUvProject,exports.sandboxUserName=r.sandboxUserName,exports.startHttpServer=m.startHttpServer,exports.startSandboxTimeoutWatchdog=r.startSandboxTimeoutWatchdog,exports.testCaseResultSchema=t.testCaseResultSchema,exports.wrapCommandWithSandboxUser=r.wrapCommandWithSandboxUser,exports.writeFileWithoutFollowingSymlinks=i.writeFileWithoutFollowingSymlinks;
package/dist/index.d.ts CHANGED
@@ -1,7 +1,10 @@
1
+ export * from './helpers/compareExpectedOutputFiles.js';
2
+ export * from './helpers/compareStdoutAsSpaceSeparatedTokens.js';
1
3
  export * from './helpers/defaultStdioHarness.js';
2
4
  export * from './helpers/parseArgs.js';
3
5
  export * from './helpers/parseCsv.js';
4
6
  export * from './helpers/printTestCaseResult.js';
7
+ export * from './helpers/readTestCases.js';
5
8
  export * from './helpers/removeCommentsInSourceCode.js';
6
9
  export * from './helpers/runCommandInTemporaryPackageManagerProject.js';
7
10
  export * from './helpers/runPythonInTemporaryUvProject.js';
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{HARNESS_FILE_PRESETS as e,findDefaultStdioHarnessFiles as t,isDefaultStdioHarnessSource as n}from"./helpers/defaultStdioHarness.js";import{parseArgs as r}from"./helpers/parseArgs.js";import{CsvParseError as i,parseCsv as a,parseCsvRecords as o}from"./helpers/parseCsv.js";import{TEST_CASE_RESULT_PREFIX as s,testCaseResultSchema as c}from"./types/testCaseResult.js";import{encodeFileForTestCaseResult as l,printTestCaseResult as u}from"./helpers/printTestCaseResult.js";import{removeCommentsAndStringsInSourceCode as d,removeCommentsInSourceCode as f}from"./helpers/removeCommentsInSourceCode.js";import{SANDBOX_ENV_PREFIX as p,SANDBOX_USER_ENV_NAME as m,SANDBOX_WATCHDOG_GRACE_SECONDS as h,TIMEOUT_COMMAND as g,forceRemove as _,getSandboxUserEnvOverrides as v,getTrustedHelperEnv as y,isSandboxWrappedCommand as b,killSandboxUserProcesses as x,makeAccessibleToSandboxUser as S,prependInsideSandboxWrapper as C,relaxPermissionsAsSandboxUser as w,sandboxUserName as T,startSandboxTimeoutWatchdog as E,wrapCommandWithSandboxUser as D}from"./helpers/sandboxUser.js";import{copyWithoutFollowingSymlinks as O,createDirectoryWithoutFollowingSymlinks as k,isContainedPath as A,writeFileWithoutFollowingSymlinks as j}from"./helpers/safeFs.js";import{copyPackageManagerProjectFiles as M,runCommandInTemporaryPackageManagerProject as N}from"./helpers/runCommandInTemporaryPackageManagerProject.js";import{runPythonInTemporaryUvProject as P}from"./helpers/runPythonInTemporaryUvProject.js";import{languageIdToSourceCodeGrammar as F}from"./helpers/sourceCodeGrammars.js";import{startHttpServer as I}from"./helpers/startHttpServer.js";import{DecisionCode as L}from"./types/decisionCode.js";import{codeRuleSchema as R,normalizeCodeRule as z,problemMarkdownFrontMatterSchema as B}from"./types/problem.js";export{i as CsvParseError,L as DecisionCode,e as HARNESS_FILE_PRESETS,p as SANDBOX_ENV_PREFIX,m as SANDBOX_USER_ENV_NAME,h as SANDBOX_WATCHDOG_GRACE_SECONDS,s as TEST_CASE_RESULT_PREFIX,g as TIMEOUT_COMMAND,R as codeRuleSchema,M as copyPackageManagerProjectFiles,O as copyWithoutFollowingSymlinks,k as createDirectoryWithoutFollowingSymlinks,l as encodeFileForTestCaseResult,t as findDefaultStdioHarnessFiles,_ as forceRemove,v as getSandboxUserEnvOverrides,y as getTrustedHelperEnv,A as isContainedPath,n as isDefaultStdioHarnessSource,b as isSandboxWrappedCommand,x as killSandboxUserProcesses,F as languageIdToSourceCodeGrammar,S as makeAccessibleToSandboxUser,z as normalizeCodeRule,r as parseArgs,a as parseCsv,o as parseCsvRecords,C as prependInsideSandboxWrapper,u as printTestCaseResult,B as problemMarkdownFrontMatterSchema,w as relaxPermissionsAsSandboxUser,d as removeCommentsAndStringsInSourceCode,f as removeCommentsInSourceCode,N as runCommandInTemporaryPackageManagerProject,P as runPythonInTemporaryUvProject,T as sandboxUserName,I as startHttpServer,E as startSandboxTimeoutWatchdog,c as testCaseResultSchema,D as wrapCommandWithSandboxUser,j as writeFileWithoutFollowingSymlinks};
1
+ import{compareStdoutAsSpaceSeparatedTokens as e}from"./helpers/compareStdoutAsSpaceSeparatedTokens.js";import{TEST_CASE_RESULT_PREFIX as t,testCaseResultSchema as n}from"./types/testCaseResult.js";import{encodeFileForTestCaseResult as r,printTestCaseResult as i}from"./helpers/printTestCaseResult.js";import{SANDBOX_ENV_PREFIX as a,SANDBOX_USER_ENV_NAME as o,SANDBOX_WATCHDOG_GRACE_SECONDS as s,TIMEOUT_COMMAND as c,forceRemove as l,getSandboxUserEnvOverrides as u,getTrustedHelperEnv as d,isSandboxWrappedCommand as f,killSandboxUserProcesses as p,makeAccessibleToSandboxUser as m,makeTraversableBySandboxUser as h,prependInsideSandboxWrapper as g,relaxPermissionsAsSandboxUser as _,sandboxUserName as v,startSandboxTimeoutWatchdog as y,wrapCommandWithSandboxUser as b}from"./helpers/sandboxUser.js";import{copyWithoutFollowingSymlinks as x,createDirectoryWithoutFollowingSymlinks as S,isContainedPath as C,writeFileWithoutFollowingSymlinks as w}from"./helpers/safeFs.js";import{MAX_COMPARED_FILE_BYTES as T,MAX_REPORTED_FILE_BYTES as E,compareExpectedOutputFiles as D,judgeAgainstExpectations as O,mergeComparisonOutputFiles as k}from"./helpers/compareExpectedOutputFiles.js";import{HARNESS_FILE_PRESETS as A,findDefaultStdioHarnessFiles as j,isDefaultStdioHarnessSource as M}from"./helpers/defaultStdioHarness.js";import{parseArgs as N}from"./helpers/parseArgs.js";import{CsvParseError as P,parseCsv as F,parseCsvRecords as I}from"./helpers/parseCsv.js";import{readTestCases as L}from"./helpers/readTestCases.js";import{removeCommentsAndStringsInSourceCode as R,removeCommentsInSourceCode as z}from"./helpers/removeCommentsInSourceCode.js";import{copyPackageManagerProjectFiles as B,runCommandInTemporaryPackageManagerProject as V}from"./helpers/runCommandInTemporaryPackageManagerProject.js";import{runPythonInTemporaryUvProject as H}from"./helpers/runPythonInTemporaryUvProject.js";import{languageIdToSourceCodeGrammar as U}from"./helpers/sourceCodeGrammars.js";import{startHttpServer as W}from"./helpers/startHttpServer.js";import{DecisionCode as G}from"./types/decisionCode.js";import{codeRuleSchema as K,normalizeCodeRule as q,problemMarkdownFrontMatterSchema as J}from"./types/problem.js";export{P as CsvParseError,G as DecisionCode,A as HARNESS_FILE_PRESETS,T as MAX_COMPARED_FILE_BYTES,E as MAX_REPORTED_FILE_BYTES,a as SANDBOX_ENV_PREFIX,o as SANDBOX_USER_ENV_NAME,s as SANDBOX_WATCHDOG_GRACE_SECONDS,t as TEST_CASE_RESULT_PREFIX,c as TIMEOUT_COMMAND,K as codeRuleSchema,D as compareExpectedOutputFiles,e as compareStdoutAsSpaceSeparatedTokens,B as copyPackageManagerProjectFiles,x as copyWithoutFollowingSymlinks,S as createDirectoryWithoutFollowingSymlinks,r as encodeFileForTestCaseResult,j as findDefaultStdioHarnessFiles,l as forceRemove,u as getSandboxUserEnvOverrides,d as getTrustedHelperEnv,C as isContainedPath,M as isDefaultStdioHarnessSource,f as isSandboxWrappedCommand,O as judgeAgainstExpectations,p as killSandboxUserProcesses,U as languageIdToSourceCodeGrammar,m as makeAccessibleToSandboxUser,h as makeTraversableBySandboxUser,k as mergeComparisonOutputFiles,q as normalizeCodeRule,N as parseArgs,F as parseCsv,I as parseCsvRecords,g as prependInsideSandboxWrapper,i as printTestCaseResult,J as problemMarkdownFrontMatterSchema,L as readTestCases,_ as relaxPermissionsAsSandboxUser,R as removeCommentsAndStringsInSourceCode,z as removeCommentsInSourceCode,V as runCommandInTemporaryPackageManagerProject,H as runPythonInTemporaryUvProject,v as sandboxUserName,W as startHttpServer,y as startSandboxTimeoutWatchdog,n as testCaseResultSchema,b as wrapCommandWithSandboxUser,w as writeFileWithoutFollowingSymlinks};
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("../helpers/parseArgs.cjs"),n=require("../helpers/printTestCaseResult.cjs"),r=require("../helpers/sandboxUser.cjs"),i=require("../types/decisionCode.cjs"),a=require("../helpers/cleanWorkingDirectory.cjs"),o=require("../helpers/copyTestCaseFileInput.cjs"),s=require("../helpers/findEntryPointFile.cjs"),c=require("../helpers/findLanguageDefinitionByPath.cjs"),l=require("../helpers/judgeByStaticAnalysis.cjs"),u=require("../helpers/readOutputFiles.cjs"),d=require("../helpers/runCustomRunner.cjs"),f=require("../helpers/runIsolationCheckInDebugMode.cjs"),p=require("../helpers/readProblemMarkdownFrontMatter.cjs"),m=require("../helpers/readTestCases.cjs"),h=require("../helpers/resolveCwds.cjs"),g=require("../helpers/spawnSyncWithTimeout.cjs");let _=require("node:path");_=e.__toESM(_,1);let v=require("zod");const y=v.z.object({language:v.z.union([v.z.string(),v.z.array(v.z.string())]).optional()});async function b(e,n){let r=t.parseArgs(process.argv),i=y.parse(r.params),{cwds:a,isDebugMode:o}=await h.resolveCwds(e,r.cwd),s=await p.readProblemMarkdownFrontMatter(e),c=await(n.readTestCases??E)(e),l={buildTimeoutSeconds:n.limits?.buildTimeoutSeconds??10,maxOutputLength:n.limits?.maxOutputLength??5e4},u=typeof s.timeLimitMs==`number`?s.timeLimitMs/1e3:n.runTimeoutSeconds??2,d={problemMarkdownFrontMatter:s,testCases:c,limits:l,timeLimitSeconds:u};if(o){let t=(l.buildTimeoutSeconds+c.length*u)*1e3;if(!await f.passesIsolationCheckInDebugMode(e,a,i,{expectedMaxDurationMs:t})){process.exitCode=1;return}}for(let t of a){o&&h.printDebugCwdBanner(e,t);let r=await x(t.cwd,i,d,n);o&&!S(t,r)&&(process.exitCode=1,h.printDebugExpectationFailureBanner(e,t))}}async function x(e,t,f,p){r.makeAccessibleToSandboxUser(e);let{problemMarkdownFrontMatter:m,testCases:h,limits:g,timeLimitSeconds:_}=f,v=h[0]?.id??`prebuild`,y=await l.judgeByStaticAnalysis(e,m);if(y)return n.printTestCaseResult({testCaseId:v,...y}),{allAccepted:!1};let b=await s.findEntryPointFile(e,t.language);if(!b)return n.printTestCaseResult({testCaseId:v,decisionCode:i.DecisionCode.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,stderr:`main file not found${t.language?`: language: ${t.language}`:``}`}),{allAccepted:!1};let x=c.findLanguageDefinitionByPath(b);if(!x)return n.printTestCaseResult({testCaseId:v,decisionCode:i.DecisionCode.WRONG_ANSWER,stderr:`unsupported language`}),{allAccepted:!1};let S={...process.env,CI:``,FORCE_COLOR:`0`},T=b;if(p.buildSubmission!==!1){if(x.prebuild)try{await x.prebuild(e);let n=await s.findEntryPointFile(e,t.language);n&&(T=n)}catch(e){return n.printTestCaseResult({testCaseId:v,decisionCode:i.DecisionCode.BUILD_ERROR,stderr:e instanceof Error?e.message:String(e)}),{allAccepted:!1}}let r=x.buildCommand?.(T);if(r){let t=C(r,{cwd:e,env:S,testCaseId:v,limits:g});if(t)return n.printTestCaseResult(t),{allAccepted:!1}}}let E=await a.snapshotWorkingDirectory(e);if(h.length===0)return n.printTestCaseResult({testCaseId:`default`,decisionCode:i.DecisionCode.ACCEPTED}),{allAccepted:!0};let k=h.shared?.fileInputPath;for(let t of h){k&&await o.copyTestCaseFileInput(k,e),t.fileInputPath&&await o.copyTestCaseFileInput(t.fileInputPath,e);let s=x.command(T),c=t.input??``,l;try{p.resolveInput&&(c=await p.resolveInput({testCase:t,cwd:e,env:S})),l=p.runCommand?await d.runCustomRunner(()=>p.runCommand?.({testCase:t,command:r.wrapCommandWithSandboxUser(s),stdin:c,cwd:e,env:{...S,...r.getSandboxUserEnvOverrides(S)},timeLimitSeconds:_})):D(s,{stdin:c,cwd:e,env:S,timeLimitSeconds:_})}catch(r){return n.printTestCaseResult({testCaseId:t.id,decisionCode:i.DecisionCode.RUNTIME_ERROR,stdin:c,stderr:w(r)}),await a.cleanWorkingDirectory(e,E),{allAccepted:!1}}let f=await u.readOutputFiles(e,m.requiredOutputFilePaths??[]),h={timeLimitSeconds:_,outputLimitLength:g.maxOutputLength,problemMarkdownFrontMatter:{memoryLimitByte:m.memoryLimitByte,requiredOutputFilePaths:m.requiredOutputFilePaths}},v=O({runResult:l,outputFiles:f,context:h}),y=v;if(v.decisionCode===i.DecisionCode.ACCEPTED)try{let e=await p.test?.({testCase:t,runResult:l,outputFiles:f,context:h});e&&(y={decisionCode:e.decisionCode??v.decisionCode,feedbackMarkdown:e.feedbackMarkdown,stderr:e.stderr})}catch(e){y={decisionCode:i.DecisionCode.RUNTIME_ERROR,stderr:w(e)}}if(n.printTestCaseResult({testCaseId:t.id,...y,exitStatus:l.status,stdin:l.stdin,stdout:l.stdout.slice(0,g.maxOutputLength)||void 0,stderr:(y.stderr??l.stderr).slice(0,g.maxOutputLength)||void 0,timeSeconds:l.timeSeconds,memoryBytes:l.memoryBytes,outputFiles:f.length>0?f:void 0}),await a.cleanWorkingDirectory(e,E),y.decisionCode!==i.DecisionCode.ACCEPTED)return{allAccepted:!1}}return{allAccepted:!0}}function S(e,t){return t.allAccepted===(e.expectedResult===`accepted`)}function C(e,t){let n=g.spawnSyncWithTimeout(e[0],e.slice(1),{cwd:t.cwd,encoding:`utf8`,env:t.env},t.limits.buildTimeoutSeconds),r=n.status??void 0;if(n.status!==0)return{testCaseId:t.testCaseId,decisionCode:i.DecisionCode.BUILD_ERROR,exitStatus:r,stdout:n.stdout.slice(0,t.limits.maxOutputLength),stderr:n.stderr.slice(0,t.limits.maxOutputLength),timeSeconds:n.timeSeconds,memoryBytes:n.memoryBytes};if(n.timeSeconds>t.limits.buildTimeoutSeconds)return{testCaseId:t.testCaseId,decisionCode:i.DecisionCode.BUILD_TIME_LIMIT_EXCEEDED,exitStatus:r,stdout:n.stdout.slice(0,t.limits.maxOutputLength),stderr:n.stderr.slice(0,t.limits.maxOutputLength),timeSeconds:n.timeSeconds,memoryBytes:n.memoryBytes};if(n.stdout.length>t.limits.maxOutputLength||n.stderr.length>t.limits.maxOutputLength)return{testCaseId:t.testCaseId,decisionCode:i.DecisionCode.BUILD_OUTPUT_SIZE_LIMIT_EXCEEDED,exitStatus:r,stdout:n.stdout.slice(0,t.limits.maxOutputLength),stderr:n.stderr.slice(0,t.limits.maxOutputLength),timeSeconds:n.timeSeconds,memoryBytes:n.memoryBytes}}function w(e){return e instanceof Error?e.message:String(e)}function T(e){return{id:e.id,input:e.input,fileInputPath:e.fileInputPath}}async function E(e){let t=await m.readTestCases(_.default.join(e,`test_cases`)),n=t.map(e=>T(e));return t.shared?.fileInputPath?Object.assign(n,{shared:{fileInputPath:t.shared.fileInputPath}}):n}function D(e,t){let n=g.spawnSyncWithTimeout(e[0],e.slice(1),{cwd:t.cwd,encoding:`utf8`,input:t.stdin,env:t.env},t.timeLimitSeconds);return{stdin:t.stdin,stdout:n.stdout,stderr:n.stderr,status:n.status??void 0,timeSeconds:n.timeSeconds,memoryBytes:n.memoryBytes}}function O(e){if(e.runResult.status!==0)return{decisionCode:i.DecisionCode.RUNTIME_ERROR,stderr:e.runResult.stderr};if(e.runResult.timeSeconds>e.context.timeLimitSeconds)return{decisionCode:i.DecisionCode.TIME_LIMIT_EXCEEDED,stderr:e.runResult.stderr};if(e.runResult.memoryBytes>(e.context.problemMarkdownFrontMatter.memoryLimitByte??1/0))return{decisionCode:i.DecisionCode.MEMORY_LIMIT_EXCEEDED,stderr:e.runResult.stderr};if(e.runResult.outputLimitExceeded||e.runResult.stdout.length>e.context.outputLimitLength||e.runResult.stderr.length>e.context.outputLimitLength)return{decisionCode:i.DecisionCode.OUTPUT_SIZE_LIMIT_EXCEEDED,stderr:e.runResult.stderr};let t=e.context.problemMarkdownFrontMatter.requiredOutputFilePaths?.length??0;return e.outputFiles.length<t?{decisionCode:i.DecisionCode.MISSING_REQUIRED_OUTPUT_FILE_ERROR}:{decisionCode:i.DecisionCode.ACCEPTED}}exports.commandJudgePreset=b;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("../helpers/printTestCaseResult.cjs"),n=require("../helpers/sandboxUser.cjs"),r=require("../helpers/readOutputFiles.cjs"),i=require("../helpers/compareExpectedOutputFiles.cjs"),a=require("../helpers/parseArgs.cjs"),o=require("../helpers/readTestCases.cjs"),s=require("../types/decisionCode.cjs"),c=require("../helpers/cleanWorkingDirectory.cjs"),l=require("../helpers/copyTestCaseFileInput.cjs"),u=require("../helpers/findEntryPointFile.cjs"),d=require("../helpers/findLanguageDefinitionByPath.cjs"),f=require("../helpers/judgeByStaticAnalysis.cjs"),p=require("../helpers/runCustomRunner.cjs"),m=require("../helpers/runIsolationCheckInDebugMode.cjs"),h=require("../helpers/readProblemMarkdownFrontMatter.cjs"),g=require("../helpers/resolveCwds.cjs"),_=require("../helpers/spawnSyncWithTimeout.cjs");let v=require("node:path");v=e.__toESM(v,1);let y=require("zod");const b=y.z.object({language:y.z.union([y.z.string(),y.z.array(y.z.string())]).optional()});async function x(e,t={}){let n=a.parseArgs(process.argv),r=b.parse(n.params),{cwds:i,isDebugMode:o}=await g.resolveCwds(e,n.cwd),s=await h.readProblemMarkdownFrontMatter(e),c=await(t.readTestCases??E)(e),l={buildTimeoutSeconds:t.limits?.buildTimeoutSeconds??10,maxOutputLength:t.limits?.maxOutputLength??5e4},u=typeof s.timeLimitMs==`number`?s.timeLimitMs/1e3:t.runTimeoutSeconds??2,d={problemMarkdownFrontMatter:s,testCases:c,limits:l,timeLimitSeconds:u};if(o){let t=(l.buildTimeoutSeconds+c.length*u)*1e3;if(!await m.passesIsolationCheckInDebugMode(e,i,r,{expectedMaxDurationMs:t})){process.exitCode=1;return}}for(let n of i){o&&g.printDebugCwdBanner(e,n);let i=await S(n.cwd,r,d,t);o&&!C(n,i)&&(process.exitCode=1,g.printDebugExpectationFailureBanner(e,n))}}async function S(e,i,a,o){n.makeAccessibleToSandboxUser(e);let{problemMarkdownFrontMatter:m,testCases:h,limits:g,timeLimitSeconds:_}=a,v=h[0]?.id??`prebuild`,y=await f.judgeByStaticAnalysis(e,m);if(y)return t.printTestCaseResult({testCaseId:v,...y}),{allAccepted:!1};let b=await u.findEntryPointFile(e,i.language);if(!b)return t.printTestCaseResult({testCaseId:v,decisionCode:s.DecisionCode.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,stderr:`main file not found${i.language?`: language: ${i.language}`:``}`}),{allAccepted:!1};let x=d.findLanguageDefinitionByPath(b);if(!x)return t.printTestCaseResult({testCaseId:v,decisionCode:s.DecisionCode.WRONG_ANSWER,stderr:`unsupported language`}),{allAccepted:!1};let S={...process.env,CI:``,FORCE_COLOR:`0`},C=b;if(o.buildSubmission!==!1){if(x.prebuild)try{await x.prebuild(e);let t=await u.findEntryPointFile(e,i.language);t&&(C=t)}catch(e){return t.printTestCaseResult({testCaseId:v,decisionCode:s.DecisionCode.BUILD_ERROR,stderr:e instanceof Error?e.message:String(e)}),{allAccepted:!1}}let n=x.buildCommand?.(C);if(n){let r=w(n,{cwd:e,env:S,testCaseId:v,limits:g});if(r)return t.printTestCaseResult(r),{allAccepted:!1}}}let E=await c.snapshotWorkingDirectory(e);if(h.length===0)return t.printTestCaseResult({testCaseId:`default`,decisionCode:s.DecisionCode.ACCEPTED}),{allAccepted:!0};let A=h.shared?.fileInputPath;for(let i of h){A&&await l.copyTestCaseFileInput(A,e),i.fileInputPath&&await l.copyTestCaseFileInput(i.fileInputPath,e);let a=x.command(C),u=i.input??``,d;try{o.resolveInput&&(u=await o.resolveInput({testCase:i,cwd:e,env:S})),d=o.runCommand?await p.runCustomRunner(()=>o.runCommand?.({testCase:i,command:n.wrapCommandWithSandboxUser(a),stdin:u,cwd:e,env:{...S,...n.getSandboxUserEnvOverrides(S)},timeLimitSeconds:_})):O(a,{stdin:u,cwd:e,env:S,timeLimitSeconds:_})}catch(n){return t.printTestCaseResult({testCaseId:i.id,decisionCode:s.DecisionCode.RUNTIME_ERROR,stdin:u,stderr:T(n)}),await c.cleanWorkingDirectory(e,E),{allAccepted:!1}}let f=await r.readOutputFiles(e,m.requiredOutputFilePaths??[]),h={timeLimitSeconds:_,outputLimitLength:g.maxOutputLength,problemMarkdownFrontMatter:{memoryLimitByte:m.memoryLimitByte,requiredOutputFilePaths:m.requiredOutputFilePaths}},v=k({runResult:d,outputFiles:f,context:h}),y=v;if(v.decisionCode===s.DecisionCode.ACCEPTED)try{let t=o.test?await o.test({testCase:i,runResult:d,outputFiles:f,cwd:e,context:h}):await D({testCase:i,runResult:d,outputFiles:f,cwd:e});t&&(y={decisionCode:t.decisionCode??v.decisionCode,feedbackMarkdown:t.feedbackMarkdown,stderr:t.stderr})}catch(e){y={decisionCode:s.DecisionCode.RUNTIME_ERROR,stderr:T(e)}}if(t.printTestCaseResult({testCaseId:i.id,...y,exitStatus:d.status,stdin:d.stdin,stdout:d.stdout.slice(0,g.maxOutputLength)||void 0,stderr:(y.stderr??d.stderr).slice(0,g.maxOutputLength)||void 0,timeSeconds:d.timeSeconds,memoryBytes:d.memoryBytes,outputFiles:f.length>0?f:void 0}),await c.cleanWorkingDirectory(e,E),y.decisionCode!==s.DecisionCode.ACCEPTED)return{allAccepted:!1}}return{allAccepted:!0}}function C(e,t){return t.allAccepted===(e.expectedResult===`accepted`)}function w(e,t){let n=_.spawnSyncWithTimeout(e[0],e.slice(1),{cwd:t.cwd,encoding:`utf8`,env:t.env},t.limits.buildTimeoutSeconds),r=n.status??void 0;if(n.status!==0)return{testCaseId:t.testCaseId,decisionCode:s.DecisionCode.BUILD_ERROR,exitStatus:r,stdout:n.stdout.slice(0,t.limits.maxOutputLength),stderr:n.stderr.slice(0,t.limits.maxOutputLength),timeSeconds:n.timeSeconds,memoryBytes:n.memoryBytes};if(n.timeSeconds>t.limits.buildTimeoutSeconds)return{testCaseId:t.testCaseId,decisionCode:s.DecisionCode.BUILD_TIME_LIMIT_EXCEEDED,exitStatus:r,stdout:n.stdout.slice(0,t.limits.maxOutputLength),stderr:n.stderr.slice(0,t.limits.maxOutputLength),timeSeconds:n.timeSeconds,memoryBytes:n.memoryBytes};if(n.stdout.length>t.limits.maxOutputLength||n.stderr.length>t.limits.maxOutputLength)return{testCaseId:t.testCaseId,decisionCode:s.DecisionCode.BUILD_OUTPUT_SIZE_LIMIT_EXCEEDED,exitStatus:r,stdout:n.stdout.slice(0,t.limits.maxOutputLength),stderr:n.stderr.slice(0,t.limits.maxOutputLength),timeSeconds:n.timeSeconds,memoryBytes:n.memoryBytes}}function T(e){return e instanceof Error?e.message:String(e)}async function E(e){return await o.readTestCases(v.default.join(e,`test_cases`))}async function D(e){let{testCase:t,runResult:n,outputFiles:r,cwd:a}=e,o=await i.judgeAgainstExpectations({stdout:n.stdout,expectedStdout:typeof t.output==`string`?t.output:void 0,fileOutputPath:typeof t.fileOutputPath==`string`?t.fileOutputPath:void 0,cwd:a,outputFiles:r});return r.splice(0,r.length,...o.outputFiles),o.matches?{}:{decisionCode:s.DecisionCode.WRONG_ANSWER}}function O(e,t){let n=_.spawnSyncWithTimeout(e[0],e.slice(1),{cwd:t.cwd,encoding:`utf8`,input:t.stdin,env:t.env},t.timeLimitSeconds);return{stdin:t.stdin,stdout:n.stdout,stderr:n.stderr,status:n.status??void 0,timeSeconds:n.timeSeconds,memoryBytes:n.memoryBytes}}function k(e){if(e.runResult.status!==0)return{decisionCode:s.DecisionCode.RUNTIME_ERROR,stderr:e.runResult.stderr};if(e.runResult.timeSeconds>e.context.timeLimitSeconds)return{decisionCode:s.DecisionCode.TIME_LIMIT_EXCEEDED,stderr:e.runResult.stderr};if(e.runResult.memoryBytes>(e.context.problemMarkdownFrontMatter.memoryLimitByte??1/0))return{decisionCode:s.DecisionCode.MEMORY_LIMIT_EXCEEDED,stderr:e.runResult.stderr};if(e.runResult.outputLimitExceeded||e.runResult.stdout.length>e.context.outputLimitLength||e.runResult.stderr.length>e.context.outputLimitLength)return{decisionCode:s.DecisionCode.OUTPUT_SIZE_LIMIT_EXCEEDED,stderr:e.runResult.stderr};let t=e.context.problemMarkdownFrontMatter.requiredOutputFilePaths?.length??0;return e.outputFiles.length<t?{decisionCode:s.DecisionCode.MISSING_REQUIRED_OUTPUT_FILE_ERROR}:{decisionCode:s.DecisionCode.ACCEPTED}}exports.commandJudgePreset=x;
2
2
  //# sourceMappingURL=command.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"command.cjs","names":["z","parseArgs","resolveCwds","readProblemMarkdownFrontMatter","passesIsolationCheckInDebugMode","judgeByStaticAnalysis","findEntryPointFile","DecisionCode","findLanguageDefinitionByPath","snapshotWorkingDirectory","copyTestCaseFileInput","runCustomRunner","wrapCommandWithSandboxUser","getSandboxUserEnvOverrides","cleanWorkingDirectory","readOutputFiles","spawnSyncWithTimeout","readFileTestCases","path"],"sources":["../../src/presets/command.ts"],"sourcesContent":["import path from 'node:path';\n\nimport { z } from 'zod';\n\nimport { cleanWorkingDirectory, snapshotWorkingDirectory } from '../helpers/cleanWorkingDirectory.js';\nimport { copyTestCaseFileInput } from '../helpers/copyTestCaseFileInput.js';\nimport { findEntryPointFile } from '../helpers/findEntryPointFile.js';\nimport { findLanguageDefinitionByPath } from '../helpers/findLanguageDefinitionByPath.js';\nimport { judgeByStaticAnalysis } from '../helpers/judgeByStaticAnalysis.js';\nimport { parseArgs } from '../helpers/parseArgs.js';\nimport { printTestCaseResult } from '../helpers/printTestCaseResult.js';\nimport { readOutputFiles } from '../helpers/readOutputFiles.js';\nimport { runCustomRunner } from '../helpers/runCustomRunner.js';\nimport { passesIsolationCheckInDebugMode } from '../helpers/runIsolationCheckInDebugMode.js';\nimport {\n getSandboxUserEnvOverrides,\n makeAccessibleToSandboxUser,\n wrapCommandWithSandboxUser,\n} from '../helpers/sandboxUser.js';\nimport { readProblemMarkdownFrontMatter } from '../helpers/readProblemMarkdownFrontMatter.js';\nimport { readTestCases as readFileTestCases } from '../helpers/readTestCases.js';\nimport {\n printDebugCwdBanner,\n printDebugExpectationFailureBanner,\n resolveCwds,\n type ResolvedCwd,\n} from '../helpers/resolveCwds.js';\nimport { spawnSyncWithTimeout } from '../helpers/spawnSyncWithTimeout.js';\nimport { DecisionCode } from '../types/decisionCode.js';\nimport type { ProblemMarkdownFrontMatter } from '../types/problem.js';\nimport type { TestCaseResult } from '../types/testCaseResult.js';\n\nconst BUILD_TIMEOUT_SECONDS = 10;\nconst JUDGE_DEFAULT_TIMEOUT_SECONDS = 2;\nconst MAX_STDOUT_LENGTH = 50_000;\n\nconst judgeParamsSchema = z.object({\n language: z.union([z.string(), z.array(z.string())]).optional(),\n});\n\ntype JudgeParams = z.infer<typeof judgeParamsSchema>;\n\ninterface BaseCommandTestCase {\n id: string;\n input?: string;\n fileInputPath?: string;\n}\n\ntype CommandJudgeCaseResult = Pick<TestCaseResult, 'decisionCode' | 'feedbackMarkdown' | 'stderr'>;\n\nexport interface CommandRunResult {\n stdin: string;\n stdout: string;\n stderr: string;\n status: number | undefined;\n timeSeconds: number;\n memoryBytes: number;\n outputLimitExceeded?: boolean;\n}\n\ninterface CommandJudgeContext {\n timeLimitSeconds: number;\n outputLimitLength: number;\n problemMarkdownFrontMatter: Pick<ProblemMarkdownFrontMatter, 'memoryLimitByte' | 'requiredOutputFilePaths'>;\n}\n\nexport interface CommandJudgeLimits {\n buildTimeoutSeconds: number;\n maxOutputLength: number;\n}\n\ninterface ResolvedCommandProblem<TTestCase extends BaseCommandTestCase> {\n problemMarkdownFrontMatter: ProblemMarkdownFrontMatter;\n testCases: readonly TTestCase[];\n limits: CommandJudgeLimits;\n timeLimitSeconds: number;\n}\n\nexport interface CommandJudgePresetOptions<\n TTestCase extends BaseCommandTestCase = BaseCommandTestCase,\n TRunResult extends CommandRunResult = CommandRunResult,\n> {\n /**\n * Set to false when the custom runner builds the submitted source itself.\n * Defaults to true.\n */\n buildSubmission?: boolean;\n limits?: CommandJudgeLimits;\n runTimeoutSeconds?: number;\n readTestCases?: (problemDir: string) => Promise<readonly TTestCase[]>;\n /**\n * Runs as the trusted harness user with the submission's `cwd`. Fixture files it creates there\n * must be written with `createDirectoryWithoutFollowingSymlinks`/`writeFileWithoutFollowingSymlinks`\n * (both exported): a submission can plant a symlink at a fixture path, and a plain `fs.writeFile`\n * would follow it into a file only the harness can write.\n */\n resolveInput?: (context: { testCase: TTestCase; cwd: string; env: NodeJS.ProcessEnv }) => Promise<string> | string;\n runCommand?: (context: {\n testCase: TTestCase;\n /**\n * The command to run. Under `EXERCODE_SANDBOX_USER` delegation it is already wrapped so it\n * executes as the sandbox user; spawn it as given (with the supplied `env`) instead of\n * reconstructing it, or the submission runs as the trusted harness user.\n *\n * Its direct child is then a root-owned `sudo` whose descendants belong to the sandbox user, so\n * the handler cannot signal them: enforce `timeLimitSeconds` with `startSandboxTimeoutWatchdog`\n * and `killSandboxUserProcesses` (both exported) rather than `child.kill()` or an outer\n * `timeout`. The preset terminates leftover sandbox processes after the handler returns.\n */\n command: readonly [string, ...string[]];\n stdin: string;\n cwd: string;\n env: NodeJS.ProcessEnv;\n timeLimitSeconds: number;\n }) => Promise<TRunResult> | TRunResult;\n test?: (context: {\n testCase: TTestCase;\n runResult: TRunResult;\n outputFiles: NonNullable<TestCaseResult['outputFiles']>;\n context: CommandJudgeContext;\n }) => Promise<Partial<CommandJudgeCaseResult>> | Partial<CommandJudgeCaseResult> | undefined;\n}\n\n/**\n * A preset function for judging by executable command.\n *\n * Keep problem-specific logic in `resolveInput` and `test`.\n *\n * @example\n * Create `judge.ts`:\n * ```ts\n * import { commandJudgePreset } from '@exercode/problem-utils/presets/command';\n * import { DecisionCode } from '@exercode/problem-utils';\n *\n * await commandJudgePreset(import.meta.dirname, {\n * readTestCases: async () => [\n * { id: '01', input: '1 2' },\n * ],\n * test: ({ runResult }) => {\n * return runResult.stdout.trim() === '3'\n * ? { decisionCode: DecisionCode.ACCEPTED }\n * : { decisionCode: DecisionCode.WRONG_ANSWER };\n * },\n * });\n * ```\n *\n * Run with the required parameters:\n * ```bash\n * bun judge.ts model_answers/python '{ \"language\": \"python\" }'\n * ```\n *\n * Run without a cwd argument to judge each `<problemDir>/model_answers/*` directory\n * for debugging. A prominent banner is printed for each run.\n * ```bash\n * bun judge.ts\n * ```\n */\nexport async function commandJudgePreset<\n TTestCase extends BaseCommandTestCase = BaseCommandTestCase,\n TRunResult extends CommandRunResult = CommandRunResult,\n>(problemDir: string, options: CommandJudgePresetOptions<TTestCase, TRunResult>): Promise<void> {\n const args = parseArgs(process.argv);\n const params = judgeParamsSchema.parse(args.params);\n\n const { cwds, isDebugMode } = await resolveCwds(problemDir, args.cwd);\n const problemMarkdownFrontMatter = await readProblemMarkdownFrontMatter(problemDir);\n const testCases = await (options.readTestCases ?? readCommandTestCases)(problemDir);\n const limits = {\n buildTimeoutSeconds: options.limits?.buildTimeoutSeconds ?? BUILD_TIMEOUT_SECONDS,\n maxOutputLength: options.limits?.maxOutputLength ?? MAX_STDOUT_LENGTH,\n };\n const timeLimitSeconds =\n typeof problemMarkdownFrontMatter.timeLimitMs === 'number'\n ? problemMarkdownFrontMatter.timeLimitMs / 1000\n : (options.runTimeoutSeconds ?? JUDGE_DEFAULT_TIMEOUT_SECONDS);\n const problem: ResolvedCommandProblem<TTestCase> = {\n problemMarkdownFrontMatter,\n testCases,\n limits,\n timeLimitSeconds,\n };\n\n if (isDebugMode) {\n const expectedMaxDurationMs = (limits.buildTimeoutSeconds + testCases.length * timeLimitSeconds) * 1000;\n if (!(await passesIsolationCheckInDebugMode(problemDir, cwds, params, { expectedMaxDurationMs }))) {\n process.exitCode = 1;\n return;\n }\n }\n\n for (const resolvedCwd of cwds) {\n if (isDebugMode) printDebugCwdBanner(problemDir, resolvedCwd);\n const result = await runCommandJudgeForCwd<TTestCase, TRunResult>(resolvedCwd.cwd, params, problem, options);\n if (isDebugMode && !matchesExpectedResult(resolvedCwd, result)) {\n process.exitCode = 1;\n printDebugExpectationFailureBanner(problemDir, resolvedCwd);\n }\n }\n}\n\nasync function runCommandJudgeForCwd<\n TTestCase extends BaseCommandTestCase,\n TRunResult extends CommandRunResult = CommandRunResult,\n>(\n cwd: string,\n params: JudgeParams,\n problem: ResolvedCommandProblem<TTestCase>,\n options: CommandJudgePresetOptions<TTestCase, TRunResult>\n): Promise<{ allAccepted: boolean }> {\n // The sandboxed submission must read its sources and write build/run outputs in its directory.\n makeAccessibleToSandboxUser(cwd);\n\n const { problemMarkdownFrontMatter, testCases, limits, timeLimitSeconds } = problem;\n const prebuildTestCaseId = testCases[0]?.id ?? 'prebuild';\n\n const staticAnalysisResult = await judgeByStaticAnalysis(cwd, problemMarkdownFrontMatter);\n if (staticAnalysisResult) {\n printTestCaseResult({ testCaseId: prebuildTestCaseId, ...staticAnalysisResult });\n return { allAccepted: false };\n }\n\n const originalMainFilePath = await findEntryPointFile(cwd, params.language);\n if (!originalMainFilePath) {\n printTestCaseResult({\n testCaseId: prebuildTestCaseId,\n decisionCode: DecisionCode.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,\n stderr: `main file not found${params.language ? `: language: ${params.language}` : ''}`,\n });\n return { allAccepted: false };\n }\n\n const languageDefinition = findLanguageDefinitionByPath(originalMainFilePath);\n if (!languageDefinition) {\n printTestCaseResult({\n testCaseId: prebuildTestCaseId,\n decisionCode: DecisionCode.WRONG_ANSWER,\n stderr: 'unsupported language',\n });\n return { allAccepted: false };\n }\n\n // `CI` changes affects Chainlit. `FORCE_COLOR` affects Bun.\n const env = { ...process.env, CI: '', FORCE_COLOR: '0' };\n\n let mainFilePath = originalMainFilePath;\n if (options.buildSubmission !== false) {\n if (languageDefinition.prebuild) {\n try {\n await languageDefinition.prebuild(cwd);\n const prebuiltMainFilePath = await findEntryPointFile(cwd, params.language);\n if (prebuiltMainFilePath) mainFilePath = prebuiltMainFilePath;\n } catch (error) {\n printTestCaseResult({\n testCaseId: prebuildTestCaseId,\n decisionCode: DecisionCode.BUILD_ERROR,\n stderr: error instanceof Error ? error.message : String(error),\n });\n return { allAccepted: false };\n }\n }\n\n const buildCommand = languageDefinition.buildCommand?.(mainFilePath);\n if (buildCommand) {\n const buildResult = runBuild(buildCommand, {\n cwd,\n env,\n testCaseId: prebuildTestCaseId,\n limits,\n });\n if (buildResult) {\n printTestCaseResult(buildResult);\n return { allAccepted: false };\n }\n }\n }\n\n const cwdSnapshot = await snapshotWorkingDirectory(cwd);\n\n if (testCases.length === 0) {\n printTestCaseResult({ testCaseId: 'default', decisionCode: DecisionCode.ACCEPTED });\n return { allAccepted: true };\n }\n\n const sharedFileInputPath = (testCases as { shared?: { fileInputPath?: string } }).shared?.fileInputPath;\n\n for (const testCase of testCases) {\n if (sharedFileInputPath) await copyTestCaseFileInput(sharedFileInputPath, cwd);\n if (testCase.fileInputPath) await copyTestCaseFileInput(testCase.fileInputPath, cwd);\n\n const command = languageDefinition.command(mainFilePath);\n let stdin = testCase.input ?? '';\n let runResult: TRunResult;\n try {\n if (options.resolveInput) {\n stdin = await options.resolveInput({ testCase, cwd, env });\n }\n\n runResult = options.runCommand\n ? await runCustomRunner(\n () =>\n // Hand custom runners a command that is already sandbox-wrapped, and the matching\n // environment: they spawn it themselves, so an unwrapped command would run the\n // submission as the trusted harness user and defeat the delegation boundary.\n options.runCommand?.({\n testCase,\n command: wrapCommandWithSandboxUser(command),\n stdin,\n cwd,\n env: { ...env, ...getSandboxUserEnvOverrides(env) },\n timeLimitSeconds,\n }) as Promise<TRunResult>\n )\n : (runCommand(command, {\n stdin,\n cwd,\n env,\n timeLimitSeconds,\n }) as TRunResult);\n } catch (error) {\n printTestCaseResult({\n testCaseId: testCase.id,\n decisionCode: DecisionCode.RUNTIME_ERROR,\n stdin,\n stderr: errorToMessage(error),\n });\n await cleanWorkingDirectory(cwd, cwdSnapshot);\n return { allAccepted: false };\n }\n\n const outputFiles = await readOutputFiles(cwd, problemMarkdownFrontMatter.requiredOutputFilePaths ?? []);\n const judgeContext: CommandJudgeContext = {\n timeLimitSeconds,\n outputLimitLength: limits.maxOutputLength,\n problemMarkdownFrontMatter: {\n memoryLimitByte: problemMarkdownFrontMatter.memoryLimitByte,\n requiredOutputFilePaths: problemMarkdownFrontMatter.requiredOutputFilePaths,\n },\n };\n const baseJudgeResult = evaluateByLimits({ runResult, outputFiles, context: judgeContext });\n let judgeResult = baseJudgeResult;\n if (baseJudgeResult.decisionCode === DecisionCode.ACCEPTED) {\n try {\n const extendedJudgeResult = await options.test?.({ testCase, runResult, outputFiles, context: judgeContext });\n if (extendedJudgeResult) {\n judgeResult = {\n decisionCode: extendedJudgeResult.decisionCode ?? baseJudgeResult.decisionCode,\n feedbackMarkdown: extendedJudgeResult.feedbackMarkdown,\n stderr: extendedJudgeResult.stderr,\n };\n }\n } catch (error) {\n judgeResult = {\n decisionCode: DecisionCode.RUNTIME_ERROR,\n stderr: errorToMessage(error),\n };\n }\n }\n\n printTestCaseResult({\n testCaseId: testCase.id,\n ...judgeResult,\n exitStatus: runResult.status,\n stdin: runResult.stdin,\n stdout: runResult.stdout.slice(0, limits.maxOutputLength) || undefined,\n stderr: (judgeResult.stderr ?? runResult.stderr).slice(0, limits.maxOutputLength) || undefined,\n timeSeconds: runResult.timeSeconds,\n memoryBytes: runResult.memoryBytes,\n outputFiles: outputFiles.length > 0 ? outputFiles : undefined,\n });\n\n await cleanWorkingDirectory(cwd, cwdSnapshot);\n if (judgeResult.decisionCode !== DecisionCode.ACCEPTED) return { allAccepted: false };\n }\n\n return { allAccepted: true };\n}\n\nfunction matchesExpectedResult(resolvedCwd: ResolvedCwd, result: { allAccepted: boolean }): boolean {\n return result.allAccepted === (resolvedCwd.expectedResult === 'accepted');\n}\n\nfunction runBuild(\n buildCommand: readonly [string, ...string[]],\n context: { cwd: string; env: NodeJS.ProcessEnv; testCaseId: string; limits: CommandJudgeLimits }\n): (Omit<TestCaseResult, 'testCaseId'> & { testCaseId: string }) | undefined {\n const spawnResult = spawnSyncWithTimeout(\n buildCommand[0],\n buildCommand.slice(1),\n { cwd: context.cwd, encoding: 'utf8', env: context.env },\n context.limits.buildTimeoutSeconds\n );\n const exitStatus = spawnResult.status ?? undefined;\n\n if (spawnResult.status !== 0) {\n return {\n testCaseId: context.testCaseId,\n decisionCode: DecisionCode.BUILD_ERROR,\n exitStatus,\n stdout: spawnResult.stdout.slice(0, context.limits.maxOutputLength),\n stderr: spawnResult.stderr.slice(0, context.limits.maxOutputLength),\n timeSeconds: spawnResult.timeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n };\n }\n\n if (spawnResult.timeSeconds > context.limits.buildTimeoutSeconds) {\n return {\n testCaseId: context.testCaseId,\n decisionCode: DecisionCode.BUILD_TIME_LIMIT_EXCEEDED,\n exitStatus,\n stdout: spawnResult.stdout.slice(0, context.limits.maxOutputLength),\n stderr: spawnResult.stderr.slice(0, context.limits.maxOutputLength),\n timeSeconds: spawnResult.timeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n };\n }\n\n if (\n spawnResult.stdout.length > context.limits.maxOutputLength ||\n spawnResult.stderr.length > context.limits.maxOutputLength\n ) {\n return {\n testCaseId: context.testCaseId,\n decisionCode: DecisionCode.BUILD_OUTPUT_SIZE_LIMIT_EXCEEDED,\n exitStatus,\n stdout: spawnResult.stdout.slice(0, context.limits.maxOutputLength),\n stderr: spawnResult.stderr.slice(0, context.limits.maxOutputLength),\n timeSeconds: spawnResult.timeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n };\n }\n\n return;\n}\n\nfunction errorToMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction toCommandTestCase(value: {\n id: string;\n input?: string;\n fileInputPath?: string;\n fileOutputPath?: string;\n output?: string;\n}): BaseCommandTestCase {\n return { id: value.id, input: value.input, fileInputPath: value.fileInputPath };\n}\n\nasync function readCommandTestCases<TTestCase extends BaseCommandTestCase = BaseCommandTestCase>(\n problemDir: string\n): Promise<readonly TTestCase[]> {\n const fileTestCases = await readFileTestCases(path.join(problemDir, 'test_cases'));\n const commandTestCases = fileTestCases.map((testCase) => toCommandTestCase(testCase) as TTestCase);\n if (fileTestCases.shared?.fileInputPath) {\n return Object.assign(commandTestCases, { shared: { fileInputPath: fileTestCases.shared.fileInputPath } });\n }\n return commandTestCases;\n}\n\nfunction runCommand(\n command: readonly [string, ...string[]],\n context: {\n stdin: string;\n cwd: string;\n env: NodeJS.ProcessEnv;\n timeLimitSeconds: number;\n }\n): CommandRunResult {\n const spawnResult = spawnSyncWithTimeout(\n command[0],\n command.slice(1),\n { cwd: context.cwd, encoding: 'utf8', input: context.stdin, env: context.env },\n context.timeLimitSeconds\n );\n\n return {\n stdin: context.stdin,\n stdout: spawnResult.stdout,\n stderr: spawnResult.stderr,\n status: spawnResult.status ?? undefined,\n timeSeconds: spawnResult.timeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n };\n}\n\nfunction evaluateByLimits(context: {\n runResult: CommandRunResult;\n outputFiles: NonNullable<TestCaseResult['outputFiles']>;\n context: CommandJudgeContext;\n}): CommandJudgeCaseResult {\n if (context.runResult.status !== 0) {\n return { decisionCode: DecisionCode.RUNTIME_ERROR, stderr: context.runResult.stderr };\n }\n\n if (context.runResult.timeSeconds > context.context.timeLimitSeconds) {\n return { decisionCode: DecisionCode.TIME_LIMIT_EXCEEDED, stderr: context.runResult.stderr };\n }\n\n if (\n context.runResult.memoryBytes >\n (context.context.problemMarkdownFrontMatter.memoryLimitByte ?? Number.POSITIVE_INFINITY)\n ) {\n return { decisionCode: DecisionCode.MEMORY_LIMIT_EXCEEDED, stderr: context.runResult.stderr };\n }\n\n if (\n context.runResult.outputLimitExceeded ||\n context.runResult.stdout.length > context.context.outputLimitLength ||\n context.runResult.stderr.length > context.context.outputLimitLength\n ) {\n return { decisionCode: DecisionCode.OUTPUT_SIZE_LIMIT_EXCEEDED, stderr: context.runResult.stderr };\n }\n\n const requiredOutputFileCount = context.context.problemMarkdownFrontMatter.requiredOutputFilePaths?.length ?? 0;\n if (context.outputFiles.length < requiredOutputFileCount) {\n return { decisionCode: DecisionCode.MISSING_REQUIRED_OUTPUT_FILE_ERROR };\n }\n\n return { decisionCode: DecisionCode.ACCEPTED };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"command.cjs","names":["z","parseArgs","resolveCwds","readProblemMarkdownFrontMatter","passesIsolationCheckInDebugMode","judgeByStaticAnalysis","findEntryPointFile","DecisionCode","findLanguageDefinitionByPath","snapshotWorkingDirectory","copyTestCaseFileInput","runCustomRunner","wrapCommandWithSandboxUser","getSandboxUserEnvOverrides","cleanWorkingDirectory","readOutputFiles","spawnSyncWithTimeout","readFileTestCases","path","judgeAgainstExpectations"],"sources":["../../src/presets/command.ts"],"sourcesContent":["import path from 'node:path';\n\nimport { z } from 'zod';\n\nimport { cleanWorkingDirectory, snapshotWorkingDirectory } from '../helpers/cleanWorkingDirectory.js';\nimport { judgeAgainstExpectations } from '../helpers/compareExpectedOutputFiles.js';\nimport { copyTestCaseFileInput } from '../helpers/copyTestCaseFileInput.js';\nimport { findEntryPointFile } from '../helpers/findEntryPointFile.js';\nimport { findLanguageDefinitionByPath } from '../helpers/findLanguageDefinitionByPath.js';\nimport { judgeByStaticAnalysis } from '../helpers/judgeByStaticAnalysis.js';\nimport { parseArgs } from '../helpers/parseArgs.js';\nimport { printTestCaseResult } from '../helpers/printTestCaseResult.js';\nimport { readOutputFiles } from '../helpers/readOutputFiles.js';\nimport { runCustomRunner } from '../helpers/runCustomRunner.js';\nimport { passesIsolationCheckInDebugMode } from '../helpers/runIsolationCheckInDebugMode.js';\nimport {\n getSandboxUserEnvOverrides,\n makeAccessibleToSandboxUser,\n wrapCommandWithSandboxUser,\n} from '../helpers/sandboxUser.js';\nimport { readProblemMarkdownFrontMatter } from '../helpers/readProblemMarkdownFrontMatter.js';\nimport { readTestCases as readFileTestCases } from '../helpers/readTestCases.js';\nimport {\n printDebugCwdBanner,\n printDebugExpectationFailureBanner,\n resolveCwds,\n type ResolvedCwd,\n} from '../helpers/resolveCwds.js';\nimport { spawnSyncWithTimeout } from '../helpers/spawnSyncWithTimeout.js';\nimport { DecisionCode } from '../types/decisionCode.js';\nimport type { ProblemMarkdownFrontMatter } from '../types/problem.js';\nimport type { TestCaseResult } from '../types/testCaseResult.js';\n\nconst BUILD_TIMEOUT_SECONDS = 10;\nconst JUDGE_DEFAULT_TIMEOUT_SECONDS = 2;\nconst MAX_STDOUT_LENGTH = 50_000;\n\nconst judgeParamsSchema = z.object({\n language: z.union([z.string(), z.array(z.string())]).optional(),\n});\n\ntype JudgeParams = z.infer<typeof judgeParamsSchema>;\n\n/** What every command test case needs; a custom `readTestCases` may add any fields of its own. */\ninterface BaseCommandTestCase {\n id: string;\n /** Standard input (`test_cases/<id>.in`). */\n input?: string;\n /** Directory copied into the working directory before the run (`test_cases/<id>.fin/`). */\n fileInputPath?: string;\n}\n\n/** A test case read from `test_cases/` by the default reader. */\nexport interface CommandTestCase extends BaseCommandTestCase {\n /** Expected standard output (`test_cases/<id>.out`). */\n output?: string;\n /** Directory of expected output files compared after the run (`test_cases/<id>.fout/`). */\n fileOutputPath?: string;\n}\n\ntype CommandJudgeCaseResult = Pick<TestCaseResult, 'decisionCode' | 'feedbackMarkdown' | 'stderr'>;\n\nexport interface CommandRunResult {\n stdin: string;\n stdout: string;\n stderr: string;\n status: number | undefined;\n timeSeconds: number;\n memoryBytes: number;\n outputLimitExceeded?: boolean;\n}\n\ninterface CommandJudgeContext {\n timeLimitSeconds: number;\n outputLimitLength: number;\n problemMarkdownFrontMatter: Pick<ProblemMarkdownFrontMatter, 'memoryLimitByte' | 'requiredOutputFilePaths'>;\n}\n\nexport interface CommandJudgeLimits {\n buildTimeoutSeconds: number;\n maxOutputLength: number;\n}\n\ninterface ResolvedCommandProblem<TTestCase extends BaseCommandTestCase> {\n problemMarkdownFrontMatter: ProblemMarkdownFrontMatter;\n testCases: readonly TTestCase[];\n limits: CommandJudgeLimits;\n timeLimitSeconds: number;\n}\n\nexport interface CommandJudgePresetOptions<\n TTestCase extends BaseCommandTestCase = CommandTestCase,\n TRunResult extends CommandRunResult = CommandRunResult,\n> {\n /**\n * Set to false when the custom runner builds the submitted source itself.\n * Defaults to true.\n */\n buildSubmission?: boolean;\n limits?: CommandJudgeLimits;\n /** The per-case time limit used only when `problem.md` declares no `timeLimitMs`. */\n runTimeoutSeconds?: number;\n readTestCases?: (problemDir: string) => Promise<readonly TTestCase[]>;\n /**\n * Runs as the trusted harness user with the submission's `cwd`. Fixture files it creates there\n * must be written with `createDirectoryWithoutFollowingSymlinks`/`writeFileWithoutFollowingSymlinks`\n * (both exported): a submission can plant a symlink at a fixture path, and a plain `fs.writeFile`\n * would follow it into a file only the harness can write.\n */\n resolveInput?: (context: { testCase: TTestCase; cwd: string; env: NodeJS.ProcessEnv }) => Promise<string> | string;\n runCommand?: (context: {\n testCase: TTestCase;\n /**\n * The command to run. Under `EXERCODE_SANDBOX_USER` delegation it is already wrapped so it\n * executes as the sandbox user; spawn it as given (with the supplied `env`) instead of\n * reconstructing it, or the submission runs as the trusted harness user.\n *\n * Its direct child is then a root-owned `sudo` whose descendants belong to the sandbox user, so\n * the handler cannot signal them: enforce `timeLimitSeconds` with `startSandboxTimeoutWatchdog`\n * and `killSandboxUserProcesses` (both exported) rather than `child.kill()` or an outer\n * `timeout`. The preset terminates leftover sandbox processes after the handler returns.\n */\n command: readonly [string, ...string[]];\n stdin: string;\n cwd: string;\n env: NodeJS.ProcessEnv;\n timeLimitSeconds: number;\n }) => Promise<TRunResult> | TRunResult;\n /**\n * Decides the verdict of a run that passed the limit checks. It replaces the default comparison\n * of `testCase.output` (space-separated tokens) and `testCase.fileOutputPath` (see\n * `compareStdoutAsSpaceSeparatedTokens` and `compareExpectedOutputFiles`, both exported), so\n * call those yourself when the test case ships `.out` / `.fout` expectations to keep.\n * `outputFiles` is the array printed with the result and may be edited in place.\n */\n test?: (context: {\n testCase: TTestCase;\n runResult: TRunResult;\n outputFiles: NonNullable<TestCaseResult['outputFiles']>;\n /** The submission's working directory, e.g. for `compareExpectedOutputFiles(cwd, testCase.fileOutputPath)`. */\n cwd: string;\n context: CommandJudgeContext;\n }) => Promise<Partial<CommandJudgeCaseResult>> | Partial<CommandJudgeCaseResult> | undefined;\n}\n\n/**\n * A preset function for judging by executable command.\n *\n * Without options, test cases come from `test_cases/` (`.in`, `.out`, `.fin/`, `.fout/`) and a run\n * within the limits is accepted when its stdout matches `.out` and its files match `.fout/`; a\n * case without either expectation only has to run within the limits.\n * Keep problem-specific logic in `resolveInput` and `test`.\n *\n * @example\n * Create `judge.ts` that judges `test_cases/` like the default stdio harness, with the command\n * preset's debug mode:\n * ```ts\n * import { commandJudgePreset } from '@exercode/problem-utils/presets/command';\n *\n * await commandJudgePreset(import.meta.dirname);\n * ```\n *\n * @example\n * Create `judge.ts` with an own verdict:\n * ```ts\n * import { commandJudgePreset } from '@exercode/problem-utils/presets/command';\n * import { DecisionCode } from '@exercode/problem-utils';\n *\n * await commandJudgePreset(import.meta.dirname, {\n * readTestCases: async () => [\n * { id: '01', input: '1 2' },\n * ],\n * test: ({ runResult }) => {\n * return runResult.stdout.trim() === '3'\n * ? { decisionCode: DecisionCode.ACCEPTED }\n * : { decisionCode: DecisionCode.WRONG_ANSWER };\n * },\n * });\n * ```\n *\n * Run with the required parameters:\n * ```bash\n * bun judge.ts model_answers/python '{ \"language\": \"python\" }'\n * ```\n *\n * Run without a cwd argument to judge each `<problemDir>/model_answers/*` directory\n * for debugging. A prominent banner is printed for each run.\n * ```bash\n * bun judge.ts\n * ```\n */\nexport async function commandJudgePreset<\n TTestCase extends BaseCommandTestCase = CommandTestCase,\n TRunResult extends CommandRunResult = CommandRunResult,\n>(problemDir: string, options: CommandJudgePresetOptions<TTestCase, TRunResult> = {}): Promise<void> {\n const args = parseArgs(process.argv);\n const params = judgeParamsSchema.parse(args.params);\n\n const { cwds, isDebugMode } = await resolveCwds(problemDir, args.cwd);\n const problemMarkdownFrontMatter = await readProblemMarkdownFrontMatter(problemDir);\n const testCases = await (options.readTestCases ?? readCommandTestCases)(problemDir);\n const limits = {\n buildTimeoutSeconds: options.limits?.buildTimeoutSeconds ?? BUILD_TIMEOUT_SECONDS,\n maxOutputLength: options.limits?.maxOutputLength ?? MAX_STDOUT_LENGTH,\n };\n const timeLimitSeconds =\n typeof problemMarkdownFrontMatter.timeLimitMs === 'number'\n ? problemMarkdownFrontMatter.timeLimitMs / 1000\n : (options.runTimeoutSeconds ?? JUDGE_DEFAULT_TIMEOUT_SECONDS);\n const problem: ResolvedCommandProblem<TTestCase> = {\n problemMarkdownFrontMatter,\n testCases,\n limits,\n timeLimitSeconds,\n };\n\n if (isDebugMode) {\n const expectedMaxDurationMs = (limits.buildTimeoutSeconds + testCases.length * timeLimitSeconds) * 1000;\n if (!(await passesIsolationCheckInDebugMode(problemDir, cwds, params, { expectedMaxDurationMs }))) {\n process.exitCode = 1;\n return;\n }\n }\n\n for (const resolvedCwd of cwds) {\n if (isDebugMode) printDebugCwdBanner(problemDir, resolvedCwd);\n const result = await runCommandJudgeForCwd<TTestCase, TRunResult>(resolvedCwd.cwd, params, problem, options);\n if (isDebugMode && !matchesExpectedResult(resolvedCwd, result)) {\n process.exitCode = 1;\n printDebugExpectationFailureBanner(problemDir, resolvedCwd);\n }\n }\n}\n\nasync function runCommandJudgeForCwd<\n TTestCase extends BaseCommandTestCase,\n TRunResult extends CommandRunResult = CommandRunResult,\n>(\n cwd: string,\n params: JudgeParams,\n problem: ResolvedCommandProblem<TTestCase>,\n options: CommandJudgePresetOptions<TTestCase, TRunResult>\n): Promise<{ allAccepted: boolean }> {\n // The sandboxed submission must read its sources and write build/run outputs in its directory.\n makeAccessibleToSandboxUser(cwd);\n\n const { problemMarkdownFrontMatter, testCases, limits, timeLimitSeconds } = problem;\n const prebuildTestCaseId = testCases[0]?.id ?? 'prebuild';\n\n const staticAnalysisResult = await judgeByStaticAnalysis(cwd, problemMarkdownFrontMatter);\n if (staticAnalysisResult) {\n printTestCaseResult({ testCaseId: prebuildTestCaseId, ...staticAnalysisResult });\n return { allAccepted: false };\n }\n\n const originalMainFilePath = await findEntryPointFile(cwd, params.language);\n if (!originalMainFilePath) {\n printTestCaseResult({\n testCaseId: prebuildTestCaseId,\n decisionCode: DecisionCode.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,\n stderr: `main file not found${params.language ? `: language: ${params.language}` : ''}`,\n });\n return { allAccepted: false };\n }\n\n const languageDefinition = findLanguageDefinitionByPath(originalMainFilePath);\n if (!languageDefinition) {\n printTestCaseResult({\n testCaseId: prebuildTestCaseId,\n decisionCode: DecisionCode.WRONG_ANSWER,\n stderr: 'unsupported language',\n });\n return { allAccepted: false };\n }\n\n // `CI` changes affects Chainlit. `FORCE_COLOR` affects Bun.\n const env = { ...process.env, CI: '', FORCE_COLOR: '0' };\n\n let mainFilePath = originalMainFilePath;\n if (options.buildSubmission !== false) {\n if (languageDefinition.prebuild) {\n try {\n await languageDefinition.prebuild(cwd);\n const prebuiltMainFilePath = await findEntryPointFile(cwd, params.language);\n if (prebuiltMainFilePath) mainFilePath = prebuiltMainFilePath;\n } catch (error) {\n printTestCaseResult({\n testCaseId: prebuildTestCaseId,\n decisionCode: DecisionCode.BUILD_ERROR,\n stderr: error instanceof Error ? error.message : String(error),\n });\n return { allAccepted: false };\n }\n }\n\n const buildCommand = languageDefinition.buildCommand?.(mainFilePath);\n if (buildCommand) {\n const buildResult = runBuild(buildCommand, {\n cwd,\n env,\n testCaseId: prebuildTestCaseId,\n limits,\n });\n if (buildResult) {\n printTestCaseResult(buildResult);\n return { allAccepted: false };\n }\n }\n }\n\n const cwdSnapshot = await snapshotWorkingDirectory(cwd);\n\n if (testCases.length === 0) {\n printTestCaseResult({ testCaseId: 'default', decisionCode: DecisionCode.ACCEPTED });\n return { allAccepted: true };\n }\n\n const sharedFileInputPath = (testCases as { shared?: { fileInputPath?: string } }).shared?.fileInputPath;\n\n for (const testCase of testCases) {\n if (sharedFileInputPath) await copyTestCaseFileInput(sharedFileInputPath, cwd);\n if (testCase.fileInputPath) await copyTestCaseFileInput(testCase.fileInputPath, cwd);\n\n const command = languageDefinition.command(mainFilePath);\n let stdin = testCase.input ?? '';\n let runResult: TRunResult;\n try {\n if (options.resolveInput) {\n stdin = await options.resolveInput({ testCase, cwd, env });\n }\n\n runResult = options.runCommand\n ? await runCustomRunner(\n () =>\n // Hand custom runners a command that is already sandbox-wrapped, and the matching\n // environment: they spawn it themselves, so an unwrapped command would run the\n // submission as the trusted harness user and defeat the delegation boundary.\n options.runCommand?.({\n testCase,\n command: wrapCommandWithSandboxUser(command),\n stdin,\n cwd,\n env: { ...env, ...getSandboxUserEnvOverrides(env) },\n timeLimitSeconds,\n }) as Promise<TRunResult>\n )\n : (runCommand(command, {\n stdin,\n cwd,\n env,\n timeLimitSeconds,\n }) as TRunResult);\n } catch (error) {\n printTestCaseResult({\n testCaseId: testCase.id,\n decisionCode: DecisionCode.RUNTIME_ERROR,\n stdin,\n stderr: errorToMessage(error),\n });\n await cleanWorkingDirectory(cwd, cwdSnapshot);\n return { allAccepted: false };\n }\n\n const outputFiles = await readOutputFiles(cwd, problemMarkdownFrontMatter.requiredOutputFilePaths ?? []);\n const judgeContext: CommandJudgeContext = {\n timeLimitSeconds,\n outputLimitLength: limits.maxOutputLength,\n problemMarkdownFrontMatter: {\n memoryLimitByte: problemMarkdownFrontMatter.memoryLimitByte,\n requiredOutputFilePaths: problemMarkdownFrontMatter.requiredOutputFilePaths,\n },\n };\n const baseJudgeResult = evaluateByLimits({ runResult, outputFiles, context: judgeContext });\n let judgeResult = baseJudgeResult;\n if (baseJudgeResult.decisionCode === DecisionCode.ACCEPTED) {\n try {\n const extendedJudgeResult = options.test\n ? await options.test({ testCase, runResult, outputFiles, cwd, context: judgeContext })\n : await compareWithExpectedOutputs({ testCase, runResult, outputFiles, cwd });\n if (extendedJudgeResult) {\n judgeResult = {\n decisionCode: extendedJudgeResult.decisionCode ?? baseJudgeResult.decisionCode,\n feedbackMarkdown: extendedJudgeResult.feedbackMarkdown,\n stderr: extendedJudgeResult.stderr,\n };\n }\n } catch (error) {\n judgeResult = {\n decisionCode: DecisionCode.RUNTIME_ERROR,\n stderr: errorToMessage(error),\n };\n }\n }\n\n printTestCaseResult({\n testCaseId: testCase.id,\n ...judgeResult,\n exitStatus: runResult.status,\n stdin: runResult.stdin,\n stdout: runResult.stdout.slice(0, limits.maxOutputLength) || undefined,\n stderr: (judgeResult.stderr ?? runResult.stderr).slice(0, limits.maxOutputLength) || undefined,\n timeSeconds: runResult.timeSeconds,\n memoryBytes: runResult.memoryBytes,\n outputFiles: outputFiles.length > 0 ? outputFiles : undefined,\n });\n\n await cleanWorkingDirectory(cwd, cwdSnapshot);\n if (judgeResult.decisionCode !== DecisionCode.ACCEPTED) return { allAccepted: false };\n }\n\n return { allAccepted: true };\n}\n\nfunction matchesExpectedResult(resolvedCwd: ResolvedCwd, result: { allAccepted: boolean }): boolean {\n return result.allAccepted === (resolvedCwd.expectedResult === 'accepted');\n}\n\nfunction runBuild(\n buildCommand: readonly [string, ...string[]],\n context: { cwd: string; env: NodeJS.ProcessEnv; testCaseId: string; limits: CommandJudgeLimits }\n): (Omit<TestCaseResult, 'testCaseId'> & { testCaseId: string }) | undefined {\n const spawnResult = spawnSyncWithTimeout(\n buildCommand[0],\n buildCommand.slice(1),\n { cwd: context.cwd, encoding: 'utf8', env: context.env },\n context.limits.buildTimeoutSeconds\n );\n const exitStatus = spawnResult.status ?? undefined;\n\n if (spawnResult.status !== 0) {\n return {\n testCaseId: context.testCaseId,\n decisionCode: DecisionCode.BUILD_ERROR,\n exitStatus,\n stdout: spawnResult.stdout.slice(0, context.limits.maxOutputLength),\n stderr: spawnResult.stderr.slice(0, context.limits.maxOutputLength),\n timeSeconds: spawnResult.timeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n };\n }\n\n if (spawnResult.timeSeconds > context.limits.buildTimeoutSeconds) {\n return {\n testCaseId: context.testCaseId,\n decisionCode: DecisionCode.BUILD_TIME_LIMIT_EXCEEDED,\n exitStatus,\n stdout: spawnResult.stdout.slice(0, context.limits.maxOutputLength),\n stderr: spawnResult.stderr.slice(0, context.limits.maxOutputLength),\n timeSeconds: spawnResult.timeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n };\n }\n\n if (\n spawnResult.stdout.length > context.limits.maxOutputLength ||\n spawnResult.stderr.length > context.limits.maxOutputLength\n ) {\n return {\n testCaseId: context.testCaseId,\n decisionCode: DecisionCode.BUILD_OUTPUT_SIZE_LIMIT_EXCEEDED,\n exitStatus,\n stdout: spawnResult.stdout.slice(0, context.limits.maxOutputLength),\n stderr: spawnResult.stderr.slice(0, context.limits.maxOutputLength),\n timeSeconds: spawnResult.timeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n };\n }\n\n return;\n}\n\nfunction errorToMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nasync function readCommandTestCases<TTestCase extends BaseCommandTestCase = CommandTestCase>(\n problemDir: string\n): Promise<readonly TTestCase[]> {\n // The default reader yields the base shape; a narrower TTestCase must come from `options.readTestCases`.\n return (await readFileTestCases(path.join(problemDir, 'test_cases'))) as unknown as readonly TTestCase[];\n}\n\n/** The default verdict: a case exposing a string `output` / `fileOutputPath` is judged against it. */\nasync function compareWithExpectedOutputs(context: {\n testCase: BaseCommandTestCase & Partial<CommandTestCase>;\n runResult: CommandRunResult;\n outputFiles: NonNullable<TestCaseResult['outputFiles']>;\n cwd: string;\n}): Promise<Partial<CommandJudgeCaseResult>> {\n const { testCase, runResult, outputFiles, cwd } = context;\n const judgement = await judgeAgainstExpectations({\n stdout: runResult.stdout,\n expectedStdout: typeof testCase.output === 'string' ? testCase.output : undefined,\n fileOutputPath: typeof testCase.fileOutputPath === 'string' ? testCase.fileOutputPath : undefined,\n cwd,\n outputFiles,\n });\n outputFiles.splice(0, outputFiles.length, ...judgement.outputFiles);\n return judgement.matches ? {} : { decisionCode: DecisionCode.WRONG_ANSWER };\n}\n\nfunction runCommand(\n command: readonly [string, ...string[]],\n context: {\n stdin: string;\n cwd: string;\n env: NodeJS.ProcessEnv;\n timeLimitSeconds: number;\n }\n): CommandRunResult {\n const spawnResult = spawnSyncWithTimeout(\n command[0],\n command.slice(1),\n { cwd: context.cwd, encoding: 'utf8', input: context.stdin, env: context.env },\n context.timeLimitSeconds\n );\n\n return {\n stdin: context.stdin,\n stdout: spawnResult.stdout,\n stderr: spawnResult.stderr,\n status: spawnResult.status ?? undefined,\n timeSeconds: spawnResult.timeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n };\n}\n\nfunction evaluateByLimits(context: {\n runResult: CommandRunResult;\n outputFiles: NonNullable<TestCaseResult['outputFiles']>;\n context: CommandJudgeContext;\n}): CommandJudgeCaseResult {\n if (context.runResult.status !== 0) {\n return { decisionCode: DecisionCode.RUNTIME_ERROR, stderr: context.runResult.stderr };\n }\n\n if (context.runResult.timeSeconds > context.context.timeLimitSeconds) {\n return { decisionCode: DecisionCode.TIME_LIMIT_EXCEEDED, stderr: context.runResult.stderr };\n }\n\n if (\n context.runResult.memoryBytes >\n (context.context.problemMarkdownFrontMatter.memoryLimitByte ?? Number.POSITIVE_INFINITY)\n ) {\n return { decisionCode: DecisionCode.MEMORY_LIMIT_EXCEEDED, stderr: context.runResult.stderr };\n }\n\n if (\n context.runResult.outputLimitExceeded ||\n context.runResult.stdout.length > context.context.outputLimitLength ||\n context.runResult.stderr.length > context.context.outputLimitLength\n ) {\n return { decisionCode: DecisionCode.OUTPUT_SIZE_LIMIT_EXCEEDED, stderr: context.runResult.stderr };\n }\n\n const requiredOutputFileCount = context.context.problemMarkdownFrontMatter.requiredOutputFilePaths?.length ?? 0;\n if (context.outputFiles.length < requiredOutputFileCount) {\n return { decisionCode: DecisionCode.MISSING_REQUIRED_OUTPUT_FILE_ERROR };\n }\n\n return { decisionCode: DecisionCode.ACCEPTED };\n}\n"],"mappings":"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"}
@@ -1,10 +1,20 @@
1
1
  import type { ProblemMarkdownFrontMatter } from '../types/problem.js';
2
2
  import type { TestCaseResult } from '../types/testCaseResult.js';
3
+ /** What every command test case needs; a custom `readTestCases` may add any fields of its own. */
3
4
  interface BaseCommandTestCase {
4
5
  id: string;
6
+ /** Standard input (`test_cases/<id>.in`). */
5
7
  input?: string;
8
+ /** Directory copied into the working directory before the run (`test_cases/<id>.fin/`). */
6
9
  fileInputPath?: string;
7
10
  }
11
+ /** A test case read from `test_cases/` by the default reader. */
12
+ export interface CommandTestCase extends BaseCommandTestCase {
13
+ /** Expected standard output (`test_cases/<id>.out`). */
14
+ output?: string;
15
+ /** Directory of expected output files compared after the run (`test_cases/<id>.fout/`). */
16
+ fileOutputPath?: string;
17
+ }
8
18
  type CommandJudgeCaseResult = Pick<TestCaseResult, 'decisionCode' | 'feedbackMarkdown' | 'stderr'>;
9
19
  export interface CommandRunResult {
10
20
  stdin: string;
@@ -24,13 +34,14 @@ export interface CommandJudgeLimits {
24
34
  buildTimeoutSeconds: number;
25
35
  maxOutputLength: number;
26
36
  }
27
- export interface CommandJudgePresetOptions<TTestCase extends BaseCommandTestCase = BaseCommandTestCase, TRunResult extends CommandRunResult = CommandRunResult> {
37
+ export interface CommandJudgePresetOptions<TTestCase extends BaseCommandTestCase = CommandTestCase, TRunResult extends CommandRunResult = CommandRunResult> {
28
38
  /**
29
39
  * Set to false when the custom runner builds the submitted source itself.
30
40
  * Defaults to true.
31
41
  */
32
42
  buildSubmission?: boolean;
33
43
  limits?: CommandJudgeLimits;
44
+ /** The per-case time limit used only when `problem.md` declares no `timeLimitMs`. */
34
45
  runTimeoutSeconds?: number;
35
46
  readTestCases?: (problemDir: string) => Promise<readonly TTestCase[]>;
36
47
  /**
@@ -62,20 +73,41 @@ export interface CommandJudgePresetOptions<TTestCase extends BaseCommandTestCase
62
73
  env: NodeJS.ProcessEnv;
63
74
  timeLimitSeconds: number;
64
75
  }) => Promise<TRunResult> | TRunResult;
76
+ /**
77
+ * Decides the verdict of a run that passed the limit checks. It replaces the default comparison
78
+ * of `testCase.output` (space-separated tokens) and `testCase.fileOutputPath` (see
79
+ * `compareStdoutAsSpaceSeparatedTokens` and `compareExpectedOutputFiles`, both exported), so
80
+ * call those yourself when the test case ships `.out` / `.fout` expectations to keep.
81
+ * `outputFiles` is the array printed with the result and may be edited in place.
82
+ */
65
83
  test?: (context: {
66
84
  testCase: TTestCase;
67
85
  runResult: TRunResult;
68
86
  outputFiles: NonNullable<TestCaseResult['outputFiles']>;
87
+ /** The submission's working directory, e.g. for `compareExpectedOutputFiles(cwd, testCase.fileOutputPath)`. */
88
+ cwd: string;
69
89
  context: CommandJudgeContext;
70
90
  }) => Promise<Partial<CommandJudgeCaseResult>> | Partial<CommandJudgeCaseResult> | undefined;
71
91
  }
72
92
  /**
73
93
  * A preset function for judging by executable command.
74
94
  *
95
+ * Without options, test cases come from `test_cases/` (`.in`, `.out`, `.fin/`, `.fout/`) and a run
96
+ * within the limits is accepted when its stdout matches `.out` and its files match `.fout/`; a
97
+ * case without either expectation only has to run within the limits.
75
98
  * Keep problem-specific logic in `resolveInput` and `test`.
76
99
  *
77
100
  * @example
78
- * Create `judge.ts`:
101
+ * Create `judge.ts` that judges `test_cases/` like the default stdio harness, with the command
102
+ * preset's debug mode:
103
+ * ```ts
104
+ * import { commandJudgePreset } from '@exercode/problem-utils/presets/command';
105
+ *
106
+ * await commandJudgePreset(import.meta.dirname);
107
+ * ```
108
+ *
109
+ * @example
110
+ * Create `judge.ts` with an own verdict:
79
111
  * ```ts
80
112
  * import { commandJudgePreset } from '@exercode/problem-utils/presets/command';
81
113
  * import { DecisionCode } from '@exercode/problem-utils';
@@ -103,5 +135,5 @@ export interface CommandJudgePresetOptions<TTestCase extends BaseCommandTestCase
103
135
  * bun judge.ts
104
136
  * ```
105
137
  */
106
- export declare function commandJudgePreset<TTestCase extends BaseCommandTestCase = BaseCommandTestCase, TRunResult extends CommandRunResult = CommandRunResult>(problemDir: string, options: CommandJudgePresetOptions<TTestCase, TRunResult>): Promise<void>;
138
+ export declare function commandJudgePreset<TTestCase extends BaseCommandTestCase = CommandTestCase, TRunResult extends CommandRunResult = CommandRunResult>(problemDir: string, options?: CommandJudgePresetOptions<TTestCase, TRunResult>): Promise<void>;
107
139
  export {};
@@ -1,2 +1,2 @@
1
- import{parseArgs as e}from"../helpers/parseArgs.js";import{printTestCaseResult as t}from"../helpers/printTestCaseResult.js";import{getSandboxUserEnvOverrides as n,makeAccessibleToSandboxUser as r,wrapCommandWithSandboxUser as i}from"../helpers/sandboxUser.js";import{DecisionCode as a}from"../types/decisionCode.js";import{cleanWorkingDirectory as o,snapshotWorkingDirectory as s}from"../helpers/cleanWorkingDirectory.js";import{copyTestCaseFileInput as c}from"../helpers/copyTestCaseFileInput.js";import{findEntryPointFile as l}from"../helpers/findEntryPointFile.js";import{findLanguageDefinitionByPath as u}from"../helpers/findLanguageDefinitionByPath.js";import{judgeByStaticAnalysis as d}from"../helpers/judgeByStaticAnalysis.js";import{readOutputFiles as f}from"../helpers/readOutputFiles.js";import{runCustomRunner as p}from"../helpers/runCustomRunner.js";import{passesIsolationCheckInDebugMode as m}from"../helpers/runIsolationCheckInDebugMode.js";import{readProblemMarkdownFrontMatter as h}from"../helpers/readProblemMarkdownFrontMatter.js";import{readTestCases as g}from"../helpers/readTestCases.js";import{printDebugCwdBanner as _,printDebugExpectationFailureBanner as v,resolveCwds as y}from"../helpers/resolveCwds.js";import{spawnSyncWithTimeout as b}from"../helpers/spawnSyncWithTimeout.js";import x from"node:path";import{z as S}from"zod";const C=S.object({language:S.union([S.string(),S.array(S.string())]).optional()});async function w(t,n){let r=e(process.argv),i=C.parse(r.params),{cwds:a,isDebugMode:o}=await y(t,r.cwd),s=await h(t),c=await(n.readTestCases??A)(t),l={buildTimeoutSeconds:n.limits?.buildTimeoutSeconds??10,maxOutputLength:n.limits?.maxOutputLength??5e4},u=typeof s.timeLimitMs==`number`?s.timeLimitMs/1e3:n.runTimeoutSeconds??2,d={problemMarkdownFrontMatter:s,testCases:c,limits:l,timeLimitSeconds:u};if(o){let e=(l.buildTimeoutSeconds+c.length*u)*1e3;if(!await m(t,a,i,{expectedMaxDurationMs:e})){process.exitCode=1;return}}for(let e of a){o&&_(t,e);let r=await T(e.cwd,i,d,n);o&&!E(e,r)&&(process.exitCode=1,v(t,e))}}async function T(e,m,h,g){r(e);let{problemMarkdownFrontMatter:_,testCases:v,limits:y,timeLimitSeconds:b}=h,x=v[0]?.id??`prebuild`,S=await d(e,_);if(S)return t({testCaseId:x,...S}),{allAccepted:!1};let C=await l(e,m.language);if(!C)return t({testCaseId:x,decisionCode:a.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,stderr:`main file not found${m.language?`: language: ${m.language}`:``}`}),{allAccepted:!1};let w=u(C);if(!w)return t({testCaseId:x,decisionCode:a.WRONG_ANSWER,stderr:`unsupported language`}),{allAccepted:!1};let T={...process.env,CI:``,FORCE_COLOR:`0`},E=C;if(g.buildSubmission!==!1){if(w.prebuild)try{await w.prebuild(e);let t=await l(e,m.language);t&&(E=t)}catch(e){return t({testCaseId:x,decisionCode:a.BUILD_ERROR,stderr:e instanceof Error?e.message:String(e)}),{allAccepted:!1}}let n=w.buildCommand?.(E);if(n){let r=D(n,{cwd:e,env:T,testCaseId:x,limits:y});if(r)return t(r),{allAccepted:!1}}}let k=await s(e);if(v.length===0)return t({testCaseId:`default`,decisionCode:a.ACCEPTED}),{allAccepted:!0};let A=v.shared?.fileInputPath;for(let r of v){A&&await c(A,e),r.fileInputPath&&await c(r.fileInputPath,e);let s=w.command(E),l=r.input??``,u;try{g.resolveInput&&(l=await g.resolveInput({testCase:r,cwd:e,env:T})),u=g.runCommand?await p(()=>g.runCommand?.({testCase:r,command:i(s),stdin:l,cwd:e,env:{...T,...n(T)},timeLimitSeconds:b})):j(s,{stdin:l,cwd:e,env:T,timeLimitSeconds:b})}catch(n){return t({testCaseId:r.id,decisionCode:a.RUNTIME_ERROR,stdin:l,stderr:O(n)}),await o(e,k),{allAccepted:!1}}let d=await f(e,_.requiredOutputFilePaths??[]),m={timeLimitSeconds:b,outputLimitLength:y.maxOutputLength,problemMarkdownFrontMatter:{memoryLimitByte:_.memoryLimitByte,requiredOutputFilePaths:_.requiredOutputFilePaths}},h=M({runResult:u,outputFiles:d,context:m}),v=h;if(h.decisionCode===a.ACCEPTED)try{let e=await g.test?.({testCase:r,runResult:u,outputFiles:d,context:m});e&&(v={decisionCode:e.decisionCode??h.decisionCode,feedbackMarkdown:e.feedbackMarkdown,stderr:e.stderr})}catch(e){v={decisionCode:a.RUNTIME_ERROR,stderr:O(e)}}if(t({testCaseId:r.id,...v,exitStatus:u.status,stdin:u.stdin,stdout:u.stdout.slice(0,y.maxOutputLength)||void 0,stderr:(v.stderr??u.stderr).slice(0,y.maxOutputLength)||void 0,timeSeconds:u.timeSeconds,memoryBytes:u.memoryBytes,outputFiles:d.length>0?d:void 0}),await o(e,k),v.decisionCode!==a.ACCEPTED)return{allAccepted:!1}}return{allAccepted:!0}}function E(e,t){return t.allAccepted===(e.expectedResult===`accepted`)}function D(e,t){let n=b(e[0],e.slice(1),{cwd:t.cwd,encoding:`utf8`,env:t.env},t.limits.buildTimeoutSeconds),r=n.status??void 0;if(n.status!==0)return{testCaseId:t.testCaseId,decisionCode:a.BUILD_ERROR,exitStatus:r,stdout:n.stdout.slice(0,t.limits.maxOutputLength),stderr:n.stderr.slice(0,t.limits.maxOutputLength),timeSeconds:n.timeSeconds,memoryBytes:n.memoryBytes};if(n.timeSeconds>t.limits.buildTimeoutSeconds)return{testCaseId:t.testCaseId,decisionCode:a.BUILD_TIME_LIMIT_EXCEEDED,exitStatus:r,stdout:n.stdout.slice(0,t.limits.maxOutputLength),stderr:n.stderr.slice(0,t.limits.maxOutputLength),timeSeconds:n.timeSeconds,memoryBytes:n.memoryBytes};if(n.stdout.length>t.limits.maxOutputLength||n.stderr.length>t.limits.maxOutputLength)return{testCaseId:t.testCaseId,decisionCode:a.BUILD_OUTPUT_SIZE_LIMIT_EXCEEDED,exitStatus:r,stdout:n.stdout.slice(0,t.limits.maxOutputLength),stderr:n.stderr.slice(0,t.limits.maxOutputLength),timeSeconds:n.timeSeconds,memoryBytes:n.memoryBytes}}function O(e){return e instanceof Error?e.message:String(e)}function k(e){return{id:e.id,input:e.input,fileInputPath:e.fileInputPath}}async function A(e){let t=await g(x.join(e,`test_cases`)),n=t.map(e=>k(e));return t.shared?.fileInputPath?Object.assign(n,{shared:{fileInputPath:t.shared.fileInputPath}}):n}function j(e,t){let n=b(e[0],e.slice(1),{cwd:t.cwd,encoding:`utf8`,input:t.stdin,env:t.env},t.timeLimitSeconds);return{stdin:t.stdin,stdout:n.stdout,stderr:n.stderr,status:n.status??void 0,timeSeconds:n.timeSeconds,memoryBytes:n.memoryBytes}}function M(e){if(e.runResult.status!==0)return{decisionCode:a.RUNTIME_ERROR,stderr:e.runResult.stderr};if(e.runResult.timeSeconds>e.context.timeLimitSeconds)return{decisionCode:a.TIME_LIMIT_EXCEEDED,stderr:e.runResult.stderr};if(e.runResult.memoryBytes>(e.context.problemMarkdownFrontMatter.memoryLimitByte??1/0))return{decisionCode:a.MEMORY_LIMIT_EXCEEDED,stderr:e.runResult.stderr};if(e.runResult.outputLimitExceeded||e.runResult.stdout.length>e.context.outputLimitLength||e.runResult.stderr.length>e.context.outputLimitLength)return{decisionCode:a.OUTPUT_SIZE_LIMIT_EXCEEDED,stderr:e.runResult.stderr};let t=e.context.problemMarkdownFrontMatter.requiredOutputFilePaths?.length??0;return e.outputFiles.length<t?{decisionCode:a.MISSING_REQUIRED_OUTPUT_FILE_ERROR}:{decisionCode:a.ACCEPTED}}export{w as commandJudgePreset};
1
+ import{printTestCaseResult as e}from"../helpers/printTestCaseResult.js";import{getSandboxUserEnvOverrides as t,makeAccessibleToSandboxUser as n,wrapCommandWithSandboxUser as r}from"../helpers/sandboxUser.js";import{readOutputFiles as i}from"../helpers/readOutputFiles.js";import{judgeAgainstExpectations as a}from"../helpers/compareExpectedOutputFiles.js";import{parseArgs as o}from"../helpers/parseArgs.js";import{readTestCases as s}from"../helpers/readTestCases.js";import{DecisionCode as c}from"../types/decisionCode.js";import{cleanWorkingDirectory as l,snapshotWorkingDirectory as u}from"../helpers/cleanWorkingDirectory.js";import{copyTestCaseFileInput as d}from"../helpers/copyTestCaseFileInput.js";import{findEntryPointFile as f}from"../helpers/findEntryPointFile.js";import{findLanguageDefinitionByPath as p}from"../helpers/findLanguageDefinitionByPath.js";import{judgeByStaticAnalysis as m}from"../helpers/judgeByStaticAnalysis.js";import{runCustomRunner as h}from"../helpers/runCustomRunner.js";import{passesIsolationCheckInDebugMode as g}from"../helpers/runIsolationCheckInDebugMode.js";import{readProblemMarkdownFrontMatter as _}from"../helpers/readProblemMarkdownFrontMatter.js";import{printDebugCwdBanner as v,printDebugExpectationFailureBanner as y,resolveCwds as b}from"../helpers/resolveCwds.js";import{spawnSyncWithTimeout as x}from"../helpers/spawnSyncWithTimeout.js";import S from"node:path";import{z as C}from"zod";const w=C.object({language:C.union([C.string(),C.array(C.string())]).optional()});async function T(e,t={}){let n=o(process.argv),r=w.parse(n.params),{cwds:i,isDebugMode:a}=await b(e,n.cwd),s=await _(e),c=await(t.readTestCases??A)(e),l={buildTimeoutSeconds:t.limits?.buildTimeoutSeconds??10,maxOutputLength:t.limits?.maxOutputLength??5e4},u=typeof s.timeLimitMs==`number`?s.timeLimitMs/1e3:t.runTimeoutSeconds??2,d={problemMarkdownFrontMatter:s,testCases:c,limits:l,timeLimitSeconds:u};if(a){let t=(l.buildTimeoutSeconds+c.length*u)*1e3;if(!await g(e,i,r,{expectedMaxDurationMs:t})){process.exitCode=1;return}}for(let n of i){a&&v(e,n);let i=await E(n.cwd,r,d,t);a&&!D(n,i)&&(process.exitCode=1,y(e,n))}}async function E(a,o,s,g){n(a);let{problemMarkdownFrontMatter:_,testCases:v,limits:y,timeLimitSeconds:b}=s,x=v[0]?.id??`prebuild`,S=await m(a,_);if(S)return e({testCaseId:x,...S}),{allAccepted:!1};let C=await f(a,o.language);if(!C)return e({testCaseId:x,decisionCode:c.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,stderr:`main file not found${o.language?`: language: ${o.language}`:``}`}),{allAccepted:!1};let w=p(C);if(!w)return e({testCaseId:x,decisionCode:c.WRONG_ANSWER,stderr:`unsupported language`}),{allAccepted:!1};let T={...process.env,CI:``,FORCE_COLOR:`0`},E=C;if(g.buildSubmission!==!1){if(w.prebuild)try{await w.prebuild(a);let e=await f(a,o.language);e&&(E=e)}catch(t){return e({testCaseId:x,decisionCode:c.BUILD_ERROR,stderr:t instanceof Error?t.message:String(t)}),{allAccepted:!1}}let t=w.buildCommand?.(E);if(t){let n=O(t,{cwd:a,env:T,testCaseId:x,limits:y});if(n)return e(n),{allAccepted:!1}}}let D=await u(a);if(v.length===0)return e({testCaseId:`default`,decisionCode:c.ACCEPTED}),{allAccepted:!0};let A=v.shared?.fileInputPath;for(let n of v){A&&await d(A,a),n.fileInputPath&&await d(n.fileInputPath,a);let o=w.command(E),s=n.input??``,u;try{g.resolveInput&&(s=await g.resolveInput({testCase:n,cwd:a,env:T})),u=g.runCommand?await h(()=>g.runCommand?.({testCase:n,command:r(o),stdin:s,cwd:a,env:{...T,...t(T)},timeLimitSeconds:b})):M(o,{stdin:s,cwd:a,env:T,timeLimitSeconds:b})}catch(t){return e({testCaseId:n.id,decisionCode:c.RUNTIME_ERROR,stdin:s,stderr:k(t)}),await l(a,D),{allAccepted:!1}}let f=await i(a,_.requiredOutputFilePaths??[]),p={timeLimitSeconds:b,outputLimitLength:y.maxOutputLength,problemMarkdownFrontMatter:{memoryLimitByte:_.memoryLimitByte,requiredOutputFilePaths:_.requiredOutputFilePaths}},m=N({runResult:u,outputFiles:f,context:p}),v=m;if(m.decisionCode===c.ACCEPTED)try{let e=g.test?await g.test({testCase:n,runResult:u,outputFiles:f,cwd:a,context:p}):await j({testCase:n,runResult:u,outputFiles:f,cwd:a});e&&(v={decisionCode:e.decisionCode??m.decisionCode,feedbackMarkdown:e.feedbackMarkdown,stderr:e.stderr})}catch(e){v={decisionCode:c.RUNTIME_ERROR,stderr:k(e)}}if(e({testCaseId:n.id,...v,exitStatus:u.status,stdin:u.stdin,stdout:u.stdout.slice(0,y.maxOutputLength)||void 0,stderr:(v.stderr??u.stderr).slice(0,y.maxOutputLength)||void 0,timeSeconds:u.timeSeconds,memoryBytes:u.memoryBytes,outputFiles:f.length>0?f:void 0}),await l(a,D),v.decisionCode!==c.ACCEPTED)return{allAccepted:!1}}return{allAccepted:!0}}function D(e,t){return t.allAccepted===(e.expectedResult===`accepted`)}function O(e,t){let n=x(e[0],e.slice(1),{cwd:t.cwd,encoding:`utf8`,env:t.env},t.limits.buildTimeoutSeconds),r=n.status??void 0;if(n.status!==0)return{testCaseId:t.testCaseId,decisionCode:c.BUILD_ERROR,exitStatus:r,stdout:n.stdout.slice(0,t.limits.maxOutputLength),stderr:n.stderr.slice(0,t.limits.maxOutputLength),timeSeconds:n.timeSeconds,memoryBytes:n.memoryBytes};if(n.timeSeconds>t.limits.buildTimeoutSeconds)return{testCaseId:t.testCaseId,decisionCode:c.BUILD_TIME_LIMIT_EXCEEDED,exitStatus:r,stdout:n.stdout.slice(0,t.limits.maxOutputLength),stderr:n.stderr.slice(0,t.limits.maxOutputLength),timeSeconds:n.timeSeconds,memoryBytes:n.memoryBytes};if(n.stdout.length>t.limits.maxOutputLength||n.stderr.length>t.limits.maxOutputLength)return{testCaseId:t.testCaseId,decisionCode:c.BUILD_OUTPUT_SIZE_LIMIT_EXCEEDED,exitStatus:r,stdout:n.stdout.slice(0,t.limits.maxOutputLength),stderr:n.stderr.slice(0,t.limits.maxOutputLength),timeSeconds:n.timeSeconds,memoryBytes:n.memoryBytes}}function k(e){return e instanceof Error?e.message:String(e)}async function A(e){return await s(S.join(e,`test_cases`))}async function j(e){let{testCase:t,runResult:n,outputFiles:r,cwd:i}=e,o=await a({stdout:n.stdout,expectedStdout:typeof t.output==`string`?t.output:void 0,fileOutputPath:typeof t.fileOutputPath==`string`?t.fileOutputPath:void 0,cwd:i,outputFiles:r});return r.splice(0,r.length,...o.outputFiles),o.matches?{}:{decisionCode:c.WRONG_ANSWER}}function M(e,t){let n=x(e[0],e.slice(1),{cwd:t.cwd,encoding:`utf8`,input:t.stdin,env:t.env},t.timeLimitSeconds);return{stdin:t.stdin,stdout:n.stdout,stderr:n.stderr,status:n.status??void 0,timeSeconds:n.timeSeconds,memoryBytes:n.memoryBytes}}function N(e){if(e.runResult.status!==0)return{decisionCode:c.RUNTIME_ERROR,stderr:e.runResult.stderr};if(e.runResult.timeSeconds>e.context.timeLimitSeconds)return{decisionCode:c.TIME_LIMIT_EXCEEDED,stderr:e.runResult.stderr};if(e.runResult.memoryBytes>(e.context.problemMarkdownFrontMatter.memoryLimitByte??1/0))return{decisionCode:c.MEMORY_LIMIT_EXCEEDED,stderr:e.runResult.stderr};if(e.runResult.outputLimitExceeded||e.runResult.stdout.length>e.context.outputLimitLength||e.runResult.stderr.length>e.context.outputLimitLength)return{decisionCode:c.OUTPUT_SIZE_LIMIT_EXCEEDED,stderr:e.runResult.stderr};let t=e.context.problemMarkdownFrontMatter.requiredOutputFilePaths?.length??0;return e.outputFiles.length<t?{decisionCode:c.MISSING_REQUIRED_OUTPUT_FILE_ERROR}:{decisionCode:c.ACCEPTED}}export{T as commandJudgePreset};
2
2
  //# sourceMappingURL=command.js.map