@exercode/problem-utils 1.16.9 → 1.16.11

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.
package/README.md CHANGED
@@ -2,5 +2,6 @@
2
2
 
3
3
  [![Test](https://github.com/WillBooster/exercode-problem-utils/actions/workflows/test.yml/badge.svg)](https://github.com/WillBooster/exercode-problem-utils/actions/workflows/test.yml)
4
4
  [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)
5
+ [![wbfy](https://img.shields.io/badge/wbfy-4.3.9-1e90ff.svg)](https://github.com/WillBooster/shared/tree/main/packages/wbfy)
5
6
 
6
7
  :100: A set of utilities for judging programs on Exercode (https://exercode.willbooster.com/).
@@ -1,2 +1,2 @@
1
- "use strict";const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("../types/testCaseResult.cjs"),n=require("../types/decisionCode.cjs"),r=require("./printDebugBanner.cjs");let i=require("node:child_process");i=e.__toESM(i,1);let a=require("node:os");a=e.__toESM(a,1);let o=require("node:path");o=e.__toESM(o,1);let s=require("node:fs");s=e.__toESM(s,1);async function c(e,t,n){let c;try{c=await s.default.promises.mkdtemp(o.default.join(a.default.tmpdir(),`problem-utils-isolation_`));let g=o.default.resolve(e),_=o.default.join(c,p(g));await s.default.promises.mkdir(o.default.dirname(_),{recursive:!0}),await s.default.promises.cp(g,_,{recursive:!0,filter:l}),await f(c,g);let v=o.default.relative(g,o.default.resolve(t.cwd)),y=o.default.join(_,v),b=m(g);if(b.startsWith(`..`)||o.default.isAbsolute(b))return r.printDebugBanner([`[DEBUG MODE] isolated problem directory check skipped`,``,`The invoked judge script is located outside the problem directory.`,`Script path: ${b}`]),{passed:!0};let x=process.execArgv.filter(u),S=JSON.stringify(d(n)?n:{}),C=i.default.spawnSync(process.execPath,[...x,b,y,S],{cwd:_,encoding:`utf8`,env:process.env,timeout:3e4}),w=C.stdout??``,T=C.stderr??``;return C.status===0&&h(w)?(r.printDebugBanner([`[DEBUG MODE] isolated problem directory check passed`,``,`Copied problem dir : ${_}`,`Checked cwd : ${v}`]),{passed:!0}):(r.printDebugBanner([`[DEBUG MODE] isolated problem directory check failed`,``,`The judge did not complete successfully after copying only the problem directory to a temporary location.`,`Make sure judge.ts imports only files included in the problem directory.`,``,`Copied problem dir : ${_}`,`Checked cwd : ${v}`,`Exit status : ${C.status??C.signal??`unknown`}`,`Spawn error : ${C.error?.message??`<none>`}`,``,`stdout:`,w.trimEnd()||`<empty>`,``,`stderr:`,T.trimEnd()||`<empty>`]),{passed:!1})}catch(e){return r.printDebugBanner([`[DEBUG MODE] isolated problem directory check failed due to an unexpected error`,``,e instanceof Error?e.message:String(e)]),{passed:!1}}finally{if(c)try{await s.default.promises.rm(c,{recursive:!0,force:!0})}catch{}}}function l(e){let t=o.default.basename(e);return t!==`node_modules`&&t!==`.git`}function u(e){return!e.startsWith(`--inspect`)&&!e.startsWith(`--watch`)&&!e.startsWith(`--hot`)}function d(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}async function f(e,t){let n=o.default.resolve(t);for(;;){let t=o.default.join(n,`node_modules`);if(s.default.existsSync(t)){let r=o.default.join(e,p(n),`node_modules`);try{await s.default.promises.symlink(t,r,process.platform===`win32`?`junction`:`dir`)}catch{}}let r=o.default.dirname(n);if(r===n)break;n=r}}function p(e){let{root:t}=o.default.parse(e);return o.default.relative(t,e)}function m(e){let t=process.argv[1];if(!t)return`.${o.default.sep}judge.ts`;let n=o.default.relative(e,o.default.resolve(t));return o.default.isAbsolute(n)||n.startsWith(`.`)?n:`.${o.default.sep}${n}`}function h(e){let r=e.split(/\r?\n/).filter(e=>e.startsWith(t.TEST_CASE_RESULT_PREFIX));return r.length===0?!1:r.every(e=>{try{let r=t.testCaseResultSchema.safeParse(JSON.parse(e.slice(t.TEST_CASE_RESULT_PREFIX.length)));return r.success&&r.data.decisionCode===n.DecisionCode.ACCEPTED}catch{return!1}})}exports.checkProblemDirIsolation=c;
1
+ "use strict";const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("../types/testCaseResult.cjs"),n=require("../types/decisionCode.cjs"),r=require("./printDebugBanner.cjs");let i=require("node:child_process");i=e.__toESM(i,1);let a=require("node:os");a=e.__toESM(a,1);let o=require("node:path");o=e.__toESM(o,1);let s=require("node:fs");s=e.__toESM(s,1);async function c(e,t,n){let c;try{c=await s.default.promises.mkdtemp(o.default.join(a.default.tmpdir(),`problem-utils-isolation_`));let g=o.default.resolve(e),_=o.default.join(c,p(g));await s.default.promises.mkdir(o.default.dirname(_),{recursive:!0}),await s.default.promises.cp(g,_,{recursive:!0,filter:l}),await f(c,g);let v=o.default.relative(g,o.default.resolve(t.cwd)),y=o.default.join(_,v),b=m(g);if(b.startsWith(`..`)||o.default.isAbsolute(b))return r.printDebugBanner([`[DEBUG MODE] isolated problem directory check skipped`,``,`The invoked judge script is located outside the problem directory.`,`Script path: ${b}`]),{passed:!0};let x=process.execArgv.filter(u),S=JSON.stringify(d(n)?n:{}),C=i.default.spawnSync(process.execPath,[...x,b,y,S],{cwd:_,encoding:`utf8`,env:process.env,timeout:3e4}),w=C.stdout??``,T=C.stderr??``;return C.status===0&&h(w)?(r.printDebugBanner([`[DEBUG MODE] isolated problem directory check passed`,``,`Copied problem dir : ${_}`,`Checked cwd : ${v}`]),{passed:!0}):(r.printDebugBanner([`[DEBUG MODE] isolated problem directory check failed`,``,`The judge did not complete successfully after copying only the problem directory to a temporary location.`,`Make sure judge.ts imports only files included in the problem directory.`,``,`Copied problem dir : ${_}`,`Checked cwd : ${v}`,`Exit status : ${C.status??C.signal??`unknown`}`,`Spawn error : ${C.error?.message??`<none>`}`,``,`stdout:`,w.trimEnd()||`<empty>`,``,`stderr:`,T.trimEnd()||`<empty>`]),{passed:!1})}catch(e){return r.printDebugBanner([`[DEBUG MODE] isolated problem directory check failed due to an unexpected error`,``,e instanceof Error?e.message:String(e)]),{passed:!1}}finally{if(c)try{await s.default.promises.rm(c,{recursive:!0,force:!0})}catch{}}}function l(e){let t=o.default.basename(e);return t!==`node_modules`&&t!==`.git`}function u(e){return!e.startsWith(`--inspect`)&&!e.startsWith(`--watch`)&&!e.startsWith(`--hot`)}function d(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}async function f(e,t){let n=o.default.resolve(t);for(;;){let t=o.default.join(n,`node_modules`);if(s.default.existsSync(t)){let r=o.default.join(e,p(n),`node_modules`);try{await s.default.promises.symlink(t,r,process.platform===`win32`?`junction`:`dir`)}catch{}}let r=o.default.dirname(n);if(r===n)break;n=r}}function p(e){let{root:t}=o.default.parse(e);return o.default.relative(t,e)}function m(e){let t=process.argv[1];if(!t)return`.${o.default.sep}judge.ts`;let n=o.default.relative(e,o.default.resolve(t));return o.default.isAbsolute(n)||n.startsWith(`.`)?n:`.${o.default.sep}${n}`}function h(e){let r=e.split(/\r?\n/).filter(e=>e.startsWith(t.TEST_CASE_RESULT_PREFIX));return r.length!==0&&r.every(e=>{try{let r=t.testCaseResultSchema.safeParse(JSON.parse(e.slice(t.TEST_CASE_RESULT_PREFIX.length)));return r.success&&r.data.decisionCode===n.DecisionCode.ACCEPTED}catch{return!1}})}exports.checkProblemDirIsolation=c;
2
2
  //# sourceMappingURL=checkProblemDirIsolation.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"checkProblemDirIsolation.cjs","names":["fs","path","os","child_process","TEST_CASE_RESULT_PREFIX","testCaseResultSchema","DecisionCode"],"sources":["../../src/helpers/checkProblemDirIsolation.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\nimport { DecisionCode } from '../types/decisionCode.js';\nimport { TEST_CASE_RESULT_PREFIX, testCaseResultSchema } from '../types/testCaseResult.js';\n\nimport { printDebugBanner } from './printDebugBanner.js';\nimport type { ResolvedCwd } from './resolveCwds.js';\n\nconst ISOLATION_CHECK_TIMEOUT_MS = 30_000;\n\nexport interface ProblemDirIsolationCheckResult {\n passed: boolean;\n}\n\n/**\n * Check that a judge can run after only the problem directory is copied elsewhere.\n */\nexport async function checkProblemDirIsolation(\n problemDir: string,\n resolvedCwd: ResolvedCwd,\n params: unknown\n): Promise<ProblemDirIsolationCheckResult> {\n let tempRoot: string | undefined;\n try {\n tempRoot = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'problem-utils-isolation_'));\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: isCopiedProblemPath,\n });\n await symlinkAllAncestorNodeModules(tempRoot, absoluteProblemDir);\n\n const relativeCwd = path.relative(absoluteProblemDir, path.resolve(resolvedCwd.cwd));\n const copiedCwd = path.join(copiedProblemDir, relativeCwd);\n const scriptPath = getInvokedScriptPath(absoluteProblemDir);\n if (scriptPath.startsWith('..') || path.isAbsolute(scriptPath)) {\n printDebugBanner([\n '[DEBUG MODE] isolated problem directory check skipped',\n '',\n 'The invoked judge script is located outside the problem directory.',\n `Script path: ${scriptPath}`,\n ]);\n return { passed: true };\n }\n const execArgv = process.execArgv.filter(isIsolationExecArg);\n const paramsJson = JSON.stringify(isJudgeParamsObject(params) ? params : {});\n const spawnResult = child_process.spawnSync(process.execPath, [...execArgv, scriptPath, copiedCwd, paramsJson], {\n cwd: copiedProblemDir,\n encoding: 'utf8',\n env: process.env,\n timeout: ISOLATION_CHECK_TIMEOUT_MS,\n });\n const stdout = spawnResult.stdout ?? '';\n const stderr = spawnResult.stderr ?? '';\n\n if (spawnResult.status === 0 && isAcceptedJudgeOutput(stdout)) {\n printDebugBanner([\n '[DEBUG MODE] isolated problem directory check passed',\n '',\n `Copied problem dir : ${copiedProblemDir}`,\n `Checked cwd : ${relativeCwd}`,\n ]);\n return { passed: true };\n }\n\n printDebugBanner([\n '[DEBUG MODE] isolated problem directory check failed',\n '',\n 'The judge did not complete successfully after copying only the problem directory to a temporary location.',\n 'Make sure judge.ts imports only files included in the problem directory.',\n '',\n `Copied problem dir : ${copiedProblemDir}`,\n `Checked cwd : ${relativeCwd}`,\n `Exit status : ${spawnResult.status ?? spawnResult.signal ?? 'unknown'}`,\n `Spawn error : ${spawnResult.error?.message ?? '<none>'}`,\n '',\n 'stdout:',\n stdout.trimEnd() || '<empty>',\n '',\n 'stderr:',\n stderr.trimEnd() || '<empty>',\n ]);\n return { passed: false };\n } catch (error) {\n printDebugBanner([\n '[DEBUG MODE] isolated problem directory check failed due to an unexpected error',\n '',\n error instanceof Error ? error.message : String(error),\n ]);\n return { passed: false };\n } finally {\n if (tempRoot) {\n try {\n await fs.promises.rm(tempRoot, { recursive: true, force: true });\n } catch {\n // Cleanup errors should not mask the primary isolation check result.\n }\n }\n }\n}\n\nfunction isCopiedProblemPath(src: string): boolean {\n const name = path.basename(src);\n return name !== 'node_modules' && name !== '.git';\n}\n\nfunction isIsolationExecArg(arg: string): boolean {\n return !arg.startsWith('--inspect') && !arg.startsWith('--watch') && !arg.startsWith('--hot');\n}\n\nfunction isJudgeParamsObject(params: unknown): params is object {\n return params !== undefined && params !== null && typeof params === 'object' && !Array.isArray(params);\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\nfunction getInvokedScriptPath(problemDir: string): string {\n const scriptPath = process.argv[1];\n if (!scriptPath) return `.${path.sep}judge.ts`;\n const relativeScriptPath = path.relative(problemDir, path.resolve(scriptPath));\n if (path.isAbsolute(relativeScriptPath)) return relativeScriptPath;\n return relativeScriptPath.startsWith('.') ? relativeScriptPath : `.${path.sep}${relativeScriptPath}`;\n}\n\nfunction isAcceptedJudgeOutput(stdout: string): boolean {\n const resultLines = stdout.split(/\\r?\\n/).filter((line) => line.startsWith(TEST_CASE_RESULT_PREFIX));\n if (resultLines.length === 0) return false;\n\n return resultLines.every((line) => {\n try {\n const parsedResult = testCaseResultSchema.safeParse(JSON.parse(line.slice(TEST_CASE_RESULT_PREFIX.length)));\n return parsedResult.success && parsedResult.data.decisionCode === DecisionCode.ACCEPTED;\n } catch {\n return false;\n }\n });\n}\n"],"mappings":"2WAoBA,eAAsB,EACpB,EACA,EACA,EACyC,CACzC,IAAI,EACJ,GAAI,CACF,EAAW,MAAMA,EAAAA,QAAG,SAAS,QAAQC,EAAAA,QAAK,KAAKC,EAAAA,QAAG,OAAO,EAAG,0BAA0B,CAAC,EACvF,IAAM,EAAqBD,EAAAA,QAAK,QAAQ,CAAU,EAC5C,EAAmBA,EAAAA,QAAK,KAAK,EAAU,EAAmB,CAAkB,CAAC,EACnF,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,CACV,CAAC,EACD,MAAM,EAA8B,EAAU,CAAkB,EAEhE,IAAM,EAAcC,EAAAA,QAAK,SAAS,EAAoBA,EAAAA,QAAK,QAAQ,EAAY,GAAG,CAAC,EAC7E,EAAYA,EAAAA,QAAK,KAAK,EAAkB,CAAW,EACnD,EAAa,EAAqB,CAAkB,EAC1D,GAAI,EAAW,WAAW,IAAI,GAAKA,EAAAA,QAAK,WAAW,CAAU,EAO3D,OANA,EAAA,iBAAiB,CACf,wDACA,GACA,qEACA,gBAAgB,GAClB,CAAC,EACM,CAAE,OAAQ,EAAK,EAExB,IAAM,EAAW,QAAQ,SAAS,OAAO,CAAkB,EACrD,EAAa,KAAK,UAAU,EAAoB,CAAM,EAAI,EAAS,CAAC,CAAC,EACrE,EAAcE,EAAAA,QAAc,UAAU,QAAQ,SAAU,CAAC,GAAG,EAAU,EAAY,EAAW,CAAU,EAAG,CAC9G,IAAK,EACL,SAAU,OACV,IAAK,QAAQ,IACb,QAAS,GACX,CAAC,EACK,EAAS,EAAY,QAAU,GAC/B,EAAS,EAAY,QAAU,GA6BrC,OA3BI,EAAY,SAAW,GAAK,EAAsB,CAAM,GAC1D,EAAA,iBAAiB,CACf,uDACA,GACA,wBAAwB,IACxB,wBAAwB,GAC1B,CAAC,EACM,CAAE,OAAQ,EAAK,IAGxB,EAAA,iBAAiB,CACf,uDACA,GACA,4GACA,2EACA,GACA,wBAAwB,IACxB,wBAAwB,IACxB,wBAAwB,EAAY,QAAU,EAAY,QAAU,YACpE,wBAAwB,EAAY,OAAO,SAAW,WACtD,GACA,UACA,EAAO,QAAQ,GAAK,UACpB,GACA,UACA,EAAO,QAAQ,GAAK,SACtB,CAAC,EACM,CAAE,OAAQ,EAAM,EACzB,OAAS,EAAO,CAMd,OALA,EAAA,iBAAiB,CACf,kFACA,GACA,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,CACvD,CAAC,EACM,CAAE,OAAQ,EAAM,CACzB,QAAU,CACR,GAAI,EACF,GAAI,CACF,MAAMH,EAAAA,QAAG,SAAS,GAAG,EAAU,CAAE,UAAW,GAAM,MAAO,EAAK,CAAC,CACjE,MAAQ,CAER,CAEJ,CACF,CAEA,SAAS,EAAoB,EAAsB,CACjD,IAAM,EAAOC,EAAAA,QAAK,SAAS,CAAG,EAC9B,OAAO,IAAS,gBAAkB,IAAS,MAC7C,CAEA,SAAS,EAAmB,EAAsB,CAChD,MAAO,CAAC,EAAI,WAAW,WAAW,GAAK,CAAC,EAAI,WAAW,SAAS,GAAK,CAAC,EAAI,WAAW,OAAO,CAC9F,CAEA,SAAS,EAAoB,EAAmC,CAC9D,OAAkD,OAAO,GAAW,YAA7D,GAAyE,CAAC,MAAM,QAAQ,CAAM,CACvG,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,CAEA,SAAS,EAAqB,EAA4B,CACxD,IAAM,EAAa,QAAQ,KAAK,GAChC,GAAI,CAAC,EAAY,MAAO,IAAIA,EAAAA,QAAK,IAAI,UACrC,IAAM,EAAqBA,EAAAA,QAAK,SAAS,EAAYA,EAAAA,QAAK,QAAQ,CAAU,CAAC,EAE7E,OADIA,EAAAA,QAAK,WAAW,CAAkB,GAC/B,EAAmB,WAAW,GAAG,EADQ,EACiB,IAAIA,EAAAA,QAAK,MAAM,GAClF,CAEA,SAAS,EAAsB,EAAyB,CACtD,IAAM,EAAc,EAAO,MAAM,OAAO,CAAC,CAAC,OAAQ,GAAS,EAAK,WAAWG,EAAAA,uBAAuB,CAAC,EAGnG,OAFI,EAAY,SAAW,EAAU,GAE9B,EAAY,MAAO,GAAS,CACjC,GAAI,CACF,IAAM,EAAeC,EAAAA,qBAAqB,UAAU,KAAK,MAAM,EAAK,MAAMD,EAAAA,wBAAwB,MAAM,CAAC,CAAC,EAC1G,OAAO,EAAa,SAAW,EAAa,KAAK,eAAiBE,EAAAA,aAAa,QACjF,MAAQ,CACN,MAAO,EACT,CACF,CAAC,CACH"}
1
+ {"version":3,"file":"checkProblemDirIsolation.cjs","names":["fs","path","os","child_process","TEST_CASE_RESULT_PREFIX","testCaseResultSchema","DecisionCode"],"sources":["../../src/helpers/checkProblemDirIsolation.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\nimport { DecisionCode } from '../types/decisionCode.js';\nimport { TEST_CASE_RESULT_PREFIX, testCaseResultSchema } from '../types/testCaseResult.js';\n\nimport { printDebugBanner } from './printDebugBanner.js';\nimport type { ResolvedCwd } from './resolveCwds.js';\n\nconst ISOLATION_CHECK_TIMEOUT_MS = 30_000;\n\nexport interface ProblemDirIsolationCheckResult {\n passed: boolean;\n}\n\n/**\n * Check that a judge can run after only the problem directory is copied elsewhere.\n */\nexport async function checkProblemDirIsolation(\n problemDir: string,\n resolvedCwd: ResolvedCwd,\n params: unknown\n): Promise<ProblemDirIsolationCheckResult> {\n let tempRoot: string | undefined;\n try {\n tempRoot = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'problem-utils-isolation_'));\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: isCopiedProblemPath,\n });\n await symlinkAllAncestorNodeModules(tempRoot, absoluteProblemDir);\n\n const relativeCwd = path.relative(absoluteProblemDir, path.resolve(resolvedCwd.cwd));\n const copiedCwd = path.join(copiedProblemDir, relativeCwd);\n const scriptPath = getInvokedScriptPath(absoluteProblemDir);\n if (scriptPath.startsWith('..') || path.isAbsolute(scriptPath)) {\n printDebugBanner([\n '[DEBUG MODE] isolated problem directory check skipped',\n '',\n 'The invoked judge script is located outside the problem directory.',\n `Script path: ${scriptPath}`,\n ]);\n return { passed: true };\n }\n const execArgv = process.execArgv.filter(isIsolationExecArg);\n const paramsJson = JSON.stringify(isJudgeParamsObject(params) ? params : {});\n const spawnResult = child_process.spawnSync(process.execPath, [...execArgv, scriptPath, copiedCwd, paramsJson], {\n cwd: copiedProblemDir,\n encoding: 'utf8',\n env: process.env,\n timeout: ISOLATION_CHECK_TIMEOUT_MS,\n });\n const stdout = spawnResult.stdout ?? '';\n const stderr = spawnResult.stderr ?? '';\n\n if (spawnResult.status === 0 && isAcceptedJudgeOutput(stdout)) {\n printDebugBanner([\n '[DEBUG MODE] isolated problem directory check passed',\n '',\n `Copied problem dir : ${copiedProblemDir}`,\n `Checked cwd : ${relativeCwd}`,\n ]);\n return { passed: true };\n }\n\n printDebugBanner([\n '[DEBUG MODE] isolated problem directory check failed',\n '',\n 'The judge did not complete successfully after copying only the problem directory to a temporary location.',\n 'Make sure judge.ts imports only files included in the problem directory.',\n '',\n `Copied problem dir : ${copiedProblemDir}`,\n `Checked cwd : ${relativeCwd}`,\n `Exit status : ${spawnResult.status ?? spawnResult.signal ?? 'unknown'}`,\n `Spawn error : ${spawnResult.error?.message ?? '<none>'}`,\n '',\n 'stdout:',\n stdout.trimEnd() || '<empty>',\n '',\n 'stderr:',\n stderr.trimEnd() || '<empty>',\n ]);\n return { passed: false };\n } catch (error) {\n printDebugBanner([\n '[DEBUG MODE] isolated problem directory check failed due to an unexpected error',\n '',\n error instanceof Error ? error.message : String(error),\n ]);\n return { passed: false };\n } finally {\n if (tempRoot) {\n try {\n await fs.promises.rm(tempRoot, { recursive: true, force: true });\n } catch {\n // Cleanup errors should not mask the primary isolation check result.\n }\n }\n }\n}\n\nfunction isCopiedProblemPath(src: string): boolean {\n const name = path.basename(src);\n return name !== 'node_modules' && name !== '.git';\n}\n\nfunction isIsolationExecArg(arg: string): boolean {\n return !arg.startsWith('--inspect') && !arg.startsWith('--watch') && !arg.startsWith('--hot');\n}\n\nfunction isJudgeParamsObject(params: unknown): params is object {\n return params !== undefined && params !== null && typeof params === 'object' && !Array.isArray(params);\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\nfunction getInvokedScriptPath(problemDir: string): string {\n const scriptPath = process.argv[1];\n if (!scriptPath) return `.${path.sep}judge.ts`;\n const relativeScriptPath = path.relative(problemDir, path.resolve(scriptPath));\n if (path.isAbsolute(relativeScriptPath)) return relativeScriptPath;\n return relativeScriptPath.startsWith('.') ? relativeScriptPath : `.${path.sep}${relativeScriptPath}`;\n}\n\nfunction isAcceptedJudgeOutput(stdout: string): boolean {\n const resultLines = stdout.split(/\\r?\\n/).filter((line) => line.startsWith(TEST_CASE_RESULT_PREFIX));\n if (resultLines.length === 0) return false;\n\n return resultLines.every((line) => {\n try {\n const parsedResult = testCaseResultSchema.safeParse(JSON.parse(line.slice(TEST_CASE_RESULT_PREFIX.length)));\n return parsedResult.success && parsedResult.data.decisionCode === DecisionCode.ACCEPTED;\n } catch {\n return false;\n }\n });\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{TEST_CASE_RESULT_PREFIX as e,testCaseResultSchema as t}from"../types/testCaseResult.js";import{DecisionCode as n}from"../types/decisionCode.js";import{printDebugBanner as r}from"./printDebugBanner.js";import i from"node:child_process";import a from"node:os";import o from"node:path";import s from"node:fs";async function c(e,t,n){let c;try{c=await s.promises.mkdtemp(o.join(a.tmpdir(),`problem-utils-isolation_`));let g=o.resolve(e),_=o.join(c,p(g));await s.promises.mkdir(o.dirname(_),{recursive:!0}),await s.promises.cp(g,_,{recursive:!0,filter:l}),await f(c,g);let v=o.relative(g,o.resolve(t.cwd)),y=o.join(_,v),b=m(g);if(b.startsWith(`..`)||o.isAbsolute(b))return r([`[DEBUG MODE] isolated problem directory check skipped`,``,`The invoked judge script is located outside the problem directory.`,`Script path: ${b}`]),{passed:!0};let x=process.execArgv.filter(u),S=JSON.stringify(d(n)?n:{}),C=i.spawnSync(process.execPath,[...x,b,y,S],{cwd:_,encoding:`utf8`,env:process.env,timeout:3e4}),w=C.stdout??``,T=C.stderr??``;return C.status===0&&h(w)?(r([`[DEBUG MODE] isolated problem directory check passed`,``,`Copied problem dir : ${_}`,`Checked cwd : ${v}`]),{passed:!0}):(r([`[DEBUG MODE] isolated problem directory check failed`,``,`The judge did not complete successfully after copying only the problem directory to a temporary location.`,`Make sure judge.ts imports only files included in the problem directory.`,``,`Copied problem dir : ${_}`,`Checked cwd : ${v}`,`Exit status : ${C.status??C.signal??`unknown`}`,`Spawn error : ${C.error?.message??`<none>`}`,``,`stdout:`,w.trimEnd()||`<empty>`,``,`stderr:`,T.trimEnd()||`<empty>`]),{passed:!1})}catch(e){return r([`[DEBUG MODE] isolated problem directory check failed due to an unexpected error`,``,e instanceof Error?e.message:String(e)]),{passed:!1}}finally{if(c)try{await s.promises.rm(c,{recursive:!0,force:!0})}catch{}}}function l(e){let t=o.basename(e);return t!==`node_modules`&&t!==`.git`}function u(e){return!e.startsWith(`--inspect`)&&!e.startsWith(`--watch`)&&!e.startsWith(`--hot`)}function d(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}async function f(e,t){let n=o.resolve(t);for(;;){let t=o.join(n,`node_modules`);if(s.existsSync(t)){let r=o.join(e,p(n),`node_modules`);try{await s.promises.symlink(t,r,process.platform===`win32`?`junction`:`dir`)}catch{}}let r=o.dirname(n);if(r===n)break;n=r}}function p(e){let{root:t}=o.parse(e);return o.relative(t,e)}function m(e){let t=process.argv[1];if(!t)return`.${o.sep}judge.ts`;let n=o.relative(e,o.resolve(t));return o.isAbsolute(n)||n.startsWith(`.`)?n:`.${o.sep}${n}`}function h(r){let i=r.split(/\r?\n/).filter(t=>t.startsWith(e));return i.length===0?!1:i.every(r=>{try{let i=t.safeParse(JSON.parse(r.slice(e.length)));return i.success&&i.data.decisionCode===n.ACCEPTED}catch{return!1}})}export{c as checkProblemDirIsolation};
1
+ import{TEST_CASE_RESULT_PREFIX as e,testCaseResultSchema as t}from"../types/testCaseResult.js";import{DecisionCode as n}from"../types/decisionCode.js";import{printDebugBanner as r}from"./printDebugBanner.js";import i from"node:child_process";import a from"node:os";import o from"node:path";import s from"node:fs";async function c(e,t,n){let c;try{c=await s.promises.mkdtemp(o.join(a.tmpdir(),`problem-utils-isolation_`));let g=o.resolve(e),_=o.join(c,p(g));await s.promises.mkdir(o.dirname(_),{recursive:!0}),await s.promises.cp(g,_,{recursive:!0,filter:l}),await f(c,g);let v=o.relative(g,o.resolve(t.cwd)),y=o.join(_,v),b=m(g);if(b.startsWith(`..`)||o.isAbsolute(b))return r([`[DEBUG MODE] isolated problem directory check skipped`,``,`The invoked judge script is located outside the problem directory.`,`Script path: ${b}`]),{passed:!0};let x=process.execArgv.filter(u),S=JSON.stringify(d(n)?n:{}),C=i.spawnSync(process.execPath,[...x,b,y,S],{cwd:_,encoding:`utf8`,env:process.env,timeout:3e4}),w=C.stdout??``,T=C.stderr??``;return C.status===0&&h(w)?(r([`[DEBUG MODE] isolated problem directory check passed`,``,`Copied problem dir : ${_}`,`Checked cwd : ${v}`]),{passed:!0}):(r([`[DEBUG MODE] isolated problem directory check failed`,``,`The judge did not complete successfully after copying only the problem directory to a temporary location.`,`Make sure judge.ts imports only files included in the problem directory.`,``,`Copied problem dir : ${_}`,`Checked cwd : ${v}`,`Exit status : ${C.status??C.signal??`unknown`}`,`Spawn error : ${C.error?.message??`<none>`}`,``,`stdout:`,w.trimEnd()||`<empty>`,``,`stderr:`,T.trimEnd()||`<empty>`]),{passed:!1})}catch(e){return r([`[DEBUG MODE] isolated problem directory check failed due to an unexpected error`,``,e instanceof Error?e.message:String(e)]),{passed:!1}}finally{if(c)try{await s.promises.rm(c,{recursive:!0,force:!0})}catch{}}}function l(e){let t=o.basename(e);return t!==`node_modules`&&t!==`.git`}function u(e){return!e.startsWith(`--inspect`)&&!e.startsWith(`--watch`)&&!e.startsWith(`--hot`)}function d(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}async function f(e,t){let n=o.resolve(t);for(;;){let t=o.join(n,`node_modules`);if(s.existsSync(t)){let r=o.join(e,p(n),`node_modules`);try{await s.promises.symlink(t,r,process.platform===`win32`?`junction`:`dir`)}catch{}}let r=o.dirname(n);if(r===n)break;n=r}}function p(e){let{root:t}=o.parse(e);return o.relative(t,e)}function m(e){let t=process.argv[1];if(!t)return`.${o.sep}judge.ts`;let n=o.relative(e,o.resolve(t));return o.isAbsolute(n)||n.startsWith(`.`)?n:`.${o.sep}${n}`}function h(r){let i=r.split(/\r?\n/).filter(t=>t.startsWith(e));return i.length!==0&&i.every(r=>{try{let i=t.safeParse(JSON.parse(r.slice(e.length)));return i.success&&i.data.decisionCode===n.ACCEPTED}catch{return!1}})}export{c as checkProblemDirIsolation};
2
2
  //# sourceMappingURL=checkProblemDirIsolation.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"checkProblemDirIsolation.js","names":["child_process"],"sources":["../../src/helpers/checkProblemDirIsolation.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\nimport { DecisionCode } from '../types/decisionCode.js';\nimport { TEST_CASE_RESULT_PREFIX, testCaseResultSchema } from '../types/testCaseResult.js';\n\nimport { printDebugBanner } from './printDebugBanner.js';\nimport type { ResolvedCwd } from './resolveCwds.js';\n\nconst ISOLATION_CHECK_TIMEOUT_MS = 30_000;\n\nexport interface ProblemDirIsolationCheckResult {\n passed: boolean;\n}\n\n/**\n * Check that a judge can run after only the problem directory is copied elsewhere.\n */\nexport async function checkProblemDirIsolation(\n problemDir: string,\n resolvedCwd: ResolvedCwd,\n params: unknown\n): Promise<ProblemDirIsolationCheckResult> {\n let tempRoot: string | undefined;\n try {\n tempRoot = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'problem-utils-isolation_'));\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: isCopiedProblemPath,\n });\n await symlinkAllAncestorNodeModules(tempRoot, absoluteProblemDir);\n\n const relativeCwd = path.relative(absoluteProblemDir, path.resolve(resolvedCwd.cwd));\n const copiedCwd = path.join(copiedProblemDir, relativeCwd);\n const scriptPath = getInvokedScriptPath(absoluteProblemDir);\n if (scriptPath.startsWith('..') || path.isAbsolute(scriptPath)) {\n printDebugBanner([\n '[DEBUG MODE] isolated problem directory check skipped',\n '',\n 'The invoked judge script is located outside the problem directory.',\n `Script path: ${scriptPath}`,\n ]);\n return { passed: true };\n }\n const execArgv = process.execArgv.filter(isIsolationExecArg);\n const paramsJson = JSON.stringify(isJudgeParamsObject(params) ? params : {});\n const spawnResult = child_process.spawnSync(process.execPath, [...execArgv, scriptPath, copiedCwd, paramsJson], {\n cwd: copiedProblemDir,\n encoding: 'utf8',\n env: process.env,\n timeout: ISOLATION_CHECK_TIMEOUT_MS,\n });\n const stdout = spawnResult.stdout ?? '';\n const stderr = spawnResult.stderr ?? '';\n\n if (spawnResult.status === 0 && isAcceptedJudgeOutput(stdout)) {\n printDebugBanner([\n '[DEBUG MODE] isolated problem directory check passed',\n '',\n `Copied problem dir : ${copiedProblemDir}`,\n `Checked cwd : ${relativeCwd}`,\n ]);\n return { passed: true };\n }\n\n printDebugBanner([\n '[DEBUG MODE] isolated problem directory check failed',\n '',\n 'The judge did not complete successfully after copying only the problem directory to a temporary location.',\n 'Make sure judge.ts imports only files included in the problem directory.',\n '',\n `Copied problem dir : ${copiedProblemDir}`,\n `Checked cwd : ${relativeCwd}`,\n `Exit status : ${spawnResult.status ?? spawnResult.signal ?? 'unknown'}`,\n `Spawn error : ${spawnResult.error?.message ?? '<none>'}`,\n '',\n 'stdout:',\n stdout.trimEnd() || '<empty>',\n '',\n 'stderr:',\n stderr.trimEnd() || '<empty>',\n ]);\n return { passed: false };\n } catch (error) {\n printDebugBanner([\n '[DEBUG MODE] isolated problem directory check failed due to an unexpected error',\n '',\n error instanceof Error ? error.message : String(error),\n ]);\n return { passed: false };\n } finally {\n if (tempRoot) {\n try {\n await fs.promises.rm(tempRoot, { recursive: true, force: true });\n } catch {\n // Cleanup errors should not mask the primary isolation check result.\n }\n }\n }\n}\n\nfunction isCopiedProblemPath(src: string): boolean {\n const name = path.basename(src);\n return name !== 'node_modules' && name !== '.git';\n}\n\nfunction isIsolationExecArg(arg: string): boolean {\n return !arg.startsWith('--inspect') && !arg.startsWith('--watch') && !arg.startsWith('--hot');\n}\n\nfunction isJudgeParamsObject(params: unknown): params is object {\n return params !== undefined && params !== null && typeof params === 'object' && !Array.isArray(params);\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\nfunction getInvokedScriptPath(problemDir: string): string {\n const scriptPath = process.argv[1];\n if (!scriptPath) return `.${path.sep}judge.ts`;\n const relativeScriptPath = path.relative(problemDir, path.resolve(scriptPath));\n if (path.isAbsolute(relativeScriptPath)) return relativeScriptPath;\n return relativeScriptPath.startsWith('.') ? relativeScriptPath : `.${path.sep}${relativeScriptPath}`;\n}\n\nfunction isAcceptedJudgeOutput(stdout: string): boolean {\n const resultLines = stdout.split(/\\r?\\n/).filter((line) => line.startsWith(TEST_CASE_RESULT_PREFIX));\n if (resultLines.length === 0) return false;\n\n return resultLines.every((line) => {\n try {\n const parsedResult = testCaseResultSchema.safeParse(JSON.parse(line.slice(TEST_CASE_RESULT_PREFIX.length)));\n return parsedResult.success && parsedResult.data.decisionCode === DecisionCode.ACCEPTED;\n } catch {\n return false;\n }\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"checkProblemDirIsolation.js","names":["child_process"],"sources":["../../src/helpers/checkProblemDirIsolation.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\nimport { DecisionCode } from '../types/decisionCode.js';\nimport { TEST_CASE_RESULT_PREFIX, testCaseResultSchema } from '../types/testCaseResult.js';\n\nimport { printDebugBanner } from './printDebugBanner.js';\nimport type { ResolvedCwd } from './resolveCwds.js';\n\nconst ISOLATION_CHECK_TIMEOUT_MS = 30_000;\n\nexport interface ProblemDirIsolationCheckResult {\n passed: boolean;\n}\n\n/**\n * Check that a judge can run after only the problem directory is copied elsewhere.\n */\nexport async function checkProblemDirIsolation(\n problemDir: string,\n resolvedCwd: ResolvedCwd,\n params: unknown\n): Promise<ProblemDirIsolationCheckResult> {\n let tempRoot: string | undefined;\n try {\n tempRoot = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'problem-utils-isolation_'));\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: isCopiedProblemPath,\n });\n await symlinkAllAncestorNodeModules(tempRoot, absoluteProblemDir);\n\n const relativeCwd = path.relative(absoluteProblemDir, path.resolve(resolvedCwd.cwd));\n const copiedCwd = path.join(copiedProblemDir, relativeCwd);\n const scriptPath = getInvokedScriptPath(absoluteProblemDir);\n if (scriptPath.startsWith('..') || path.isAbsolute(scriptPath)) {\n printDebugBanner([\n '[DEBUG MODE] isolated problem directory check skipped',\n '',\n 'The invoked judge script is located outside the problem directory.',\n `Script path: ${scriptPath}`,\n ]);\n return { passed: true };\n }\n const execArgv = process.execArgv.filter(isIsolationExecArg);\n const paramsJson = JSON.stringify(isJudgeParamsObject(params) ? params : {});\n const spawnResult = child_process.spawnSync(process.execPath, [...execArgv, scriptPath, copiedCwd, paramsJson], {\n cwd: copiedProblemDir,\n encoding: 'utf8',\n env: process.env,\n timeout: ISOLATION_CHECK_TIMEOUT_MS,\n });\n const stdout = spawnResult.stdout ?? '';\n const stderr = spawnResult.stderr ?? '';\n\n if (spawnResult.status === 0 && isAcceptedJudgeOutput(stdout)) {\n printDebugBanner([\n '[DEBUG MODE] isolated problem directory check passed',\n '',\n `Copied problem dir : ${copiedProblemDir}`,\n `Checked cwd : ${relativeCwd}`,\n ]);\n return { passed: true };\n }\n\n printDebugBanner([\n '[DEBUG MODE] isolated problem directory check failed',\n '',\n 'The judge did not complete successfully after copying only the problem directory to a temporary location.',\n 'Make sure judge.ts imports only files included in the problem directory.',\n '',\n `Copied problem dir : ${copiedProblemDir}`,\n `Checked cwd : ${relativeCwd}`,\n `Exit status : ${spawnResult.status ?? spawnResult.signal ?? 'unknown'}`,\n `Spawn error : ${spawnResult.error?.message ?? '<none>'}`,\n '',\n 'stdout:',\n stdout.trimEnd() || '<empty>',\n '',\n 'stderr:',\n stderr.trimEnd() || '<empty>',\n ]);\n return { passed: false };\n } catch (error) {\n printDebugBanner([\n '[DEBUG MODE] isolated problem directory check failed due to an unexpected error',\n '',\n error instanceof Error ? error.message : String(error),\n ]);\n return { passed: false };\n } finally {\n if (tempRoot) {\n try {\n await fs.promises.rm(tempRoot, { recursive: true, force: true });\n } catch {\n // Cleanup errors should not mask the primary isolation check result.\n }\n }\n }\n}\n\nfunction isCopiedProblemPath(src: string): boolean {\n const name = path.basename(src);\n return name !== 'node_modules' && name !== '.git';\n}\n\nfunction isIsolationExecArg(arg: string): boolean {\n return !arg.startsWith('--inspect') && !arg.startsWith('--watch') && !arg.startsWith('--hot');\n}\n\nfunction isJudgeParamsObject(params: unknown): params is object {\n return params !== undefined && params !== null && typeof params === 'object' && !Array.isArray(params);\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\nfunction getInvokedScriptPath(problemDir: string): string {\n const scriptPath = process.argv[1];\n if (!scriptPath) return `.${path.sep}judge.ts`;\n const relativeScriptPath = path.relative(problemDir, path.resolve(scriptPath));\n if (path.isAbsolute(relativeScriptPath)) return relativeScriptPath;\n return relativeScriptPath.startsWith('.') ? relativeScriptPath : `.${path.sep}${relativeScriptPath}`;\n}\n\nfunction isAcceptedJudgeOutput(stdout: string): boolean {\n const resultLines = stdout.split(/\\r?\\n/).filter((line) => line.startsWith(TEST_CASE_RESULT_PREFIX));\n if (resultLines.length === 0) return false;\n\n return resultLines.every((line) => {\n try {\n const parsedResult = testCaseResultSchema.safeParse(JSON.parse(line.slice(TEST_CASE_RESULT_PREFIX.length)));\n return parsedResult.success && parsedResult.data.decisionCode === DecisionCode.ACCEPTED;\n } catch {\n return false;\n }\n });\n}\n"],"mappings":"yTAoBA,eAAsB,EACpB,EACA,EACA,EACyC,CACzC,IAAI,EACJ,GAAI,CACF,EAAW,MAAM,EAAG,SAAS,QAAQ,EAAK,KAAK,EAAG,OAAO,EAAG,0BAA0B,CAAC,EACvF,IAAM,EAAqB,EAAK,QAAQ,CAAU,EAC5C,EAAmB,EAAK,KAAK,EAAU,EAAmB,CAAkB,CAAC,EACnF,MAAM,EAAG,SAAS,MAAM,EAAK,QAAQ,CAAgB,EAAG,CAAE,UAAW,EAAK,CAAC,EAC3E,MAAM,EAAG,SAAS,GAAG,EAAoB,EAAkB,CACzD,UAAW,GACX,OAAQ,CACV,CAAC,EACD,MAAM,EAA8B,EAAU,CAAkB,EAEhE,IAAM,EAAc,EAAK,SAAS,EAAoB,EAAK,QAAQ,EAAY,GAAG,CAAC,EAC7E,EAAY,EAAK,KAAK,EAAkB,CAAW,EACnD,EAAa,EAAqB,CAAkB,EAC1D,GAAI,EAAW,WAAW,IAAI,GAAK,EAAK,WAAW,CAAU,EAO3D,OANA,EAAiB,CACf,wDACA,GACA,qEACA,gBAAgB,GAClB,CAAC,EACM,CAAE,OAAQ,EAAK,EAExB,IAAM,EAAW,QAAQ,SAAS,OAAO,CAAkB,EACrD,EAAa,KAAK,UAAU,EAAoB,CAAM,EAAI,EAAS,CAAC,CAAC,EACrE,EAAcA,EAAc,UAAU,QAAQ,SAAU,CAAC,GAAG,EAAU,EAAY,EAAW,CAAU,EAAG,CAC9G,IAAK,EACL,SAAU,OACV,IAAK,QAAQ,IACb,QAAS,GACX,CAAC,EACK,EAAS,EAAY,QAAU,GAC/B,EAAS,EAAY,QAAU,GA6BrC,OA3BI,EAAY,SAAW,GAAK,EAAsB,CAAM,GAC1D,EAAiB,CACf,uDACA,GACA,wBAAwB,IACxB,wBAAwB,GAC1B,CAAC,EACM,CAAE,OAAQ,EAAK,IAGxB,EAAiB,CACf,uDACA,GACA,4GACA,2EACA,GACA,wBAAwB,IACxB,wBAAwB,IACxB,wBAAwB,EAAY,QAAU,EAAY,QAAU,YACpE,wBAAwB,EAAY,OAAO,SAAW,WACtD,GACA,UACA,EAAO,QAAQ,GAAK,UACpB,GACA,UACA,EAAO,QAAQ,GAAK,SACtB,CAAC,EACM,CAAE,OAAQ,EAAM,EACzB,OAAS,EAAO,CAMd,OALA,EAAiB,CACf,kFACA,GACA,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,CACvD,CAAC,EACM,CAAE,OAAQ,EAAM,CACzB,QAAU,CACR,GAAI,EACF,GAAI,CACF,MAAM,EAAG,SAAS,GAAG,EAAU,CAAE,UAAW,GAAM,MAAO,EAAK,CAAC,CACjE,MAAQ,CAER,CAEJ,CACF,CAEA,SAAS,EAAoB,EAAsB,CACjD,IAAM,EAAO,EAAK,SAAS,CAAG,EAC9B,OAAO,IAAS,gBAAkB,IAAS,MAC7C,CAEA,SAAS,EAAmB,EAAsB,CAChD,MAAO,CAAC,EAAI,WAAW,WAAW,GAAK,CAAC,EAAI,WAAW,SAAS,GAAK,CAAC,EAAI,WAAW,OAAO,CAC9F,CAEA,SAAS,EAAoB,EAAmC,CAC9D,OAAkD,OAAO,GAAW,YAA7D,GAAyE,CAAC,MAAM,QAAQ,CAAM,CACvG,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,CAEA,SAAS,EAAqB,EAA4B,CACxD,IAAM,EAAa,QAAQ,KAAK,GAChC,GAAI,CAAC,EAAY,MAAO,IAAI,EAAK,IAAI,UACrC,IAAM,EAAqB,EAAK,SAAS,EAAY,EAAK,QAAQ,CAAU,CAAC,EAE7E,OADI,EAAK,WAAW,CAAkB,GAC/B,EAAmB,WAAW,GAAG,EADQ,EACiB,IAAI,EAAK,MAAM,GAClF,CAEA,SAAS,EAAsB,EAAyB,CACtD,IAAM,EAAc,EAAO,MAAM,OAAO,CAAC,CAAC,OAAQ,GAAS,EAAK,WAAW,CAAuB,CAAC,EAGnG,OAFI,EAAY,SAAW,GAEpB,EAAY,MAAO,GAAS,CACjC,GAAI,CACF,IAAM,EAAe,EAAqB,UAAU,KAAK,MAAM,EAAK,MAAM,EAAwB,MAAM,CAAC,CAAC,EAC1G,OAAO,EAAa,SAAW,EAAa,KAAK,eAAiB,EAAa,QACjF,MAAQ,CACN,MAAO,EACT,CACF,CAAC,CACH"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exercode/problem-utils",
3
- "version": "1.16.9",
3
+ "version": "1.16.11",
4
4
  "description": ":100: A set of utilities for judging programs on Exercode (https://exercode.willbooster.com/).",
5
5
  "keywords": [
6
6
  "exercode",
@@ -34,19 +34,17 @@
34
34
  ],
35
35
  "scripts": {
36
36
  "build": "build-ts lib --input src/index.ts src/presets/*.ts",
37
- "cleanup": "yarn format && yarn lint-fix",
38
- "format": "sort-package-json && yarn format-code && yarn prettify",
39
- "format-code": "oxfmt --write --no-error-on-unmatched-pattern . '!**/package.json'",
40
- "lint": "oxlint --type-aware --type-check --no-error-on-unmatched-pattern .",
41
- "lint-fix": "yarn lint --fix",
37
+ "cleanup": "bun wb lint --fix --format",
38
+ "format": "bun wb lint --format",
39
+ "lint": "bun wb lint",
40
+ "lint-fix": "bun wb lint --fix",
42
41
  "prepare": "lefthook install || true",
43
- "prettify": "prettier --cache --no-error-on-unmatched-pattern --write \"**/{.*/,}*.{java}\" \"!**/test{-,/}fixtures/**\" || true",
44
- "start": "build-ts run src/index.ts",
45
- "test": "rm -fr temp && WB_ENV=test wb dotenv -- yarn vitest test",
46
- "test/ci-setup": "yarn build && bun install --cwd example",
47
- "typecheck": "wb typecheck",
48
- "verify": "wb verify",
49
- "verify-full": "wb verify --full"
42
+ "start": "bun src/index.ts",
43
+ "test": "bun wb test",
44
+ "test/ci-setup": "bun run build && bun install --cwd example",
45
+ "typecheck": "bun wb typecheck",
46
+ "verify": "bun wb verify",
47
+ "verify-full": "bun wb verify --full"
50
48
  },
51
49
  "prettier": "@willbooster/prettier-config",
52
50
  "dependencies": {
@@ -54,34 +52,35 @@
54
52
  "@ai-sdk/google": "3.0.91",
55
53
  "@ai-sdk/openai": "3.0.84",
56
54
  "@ai-sdk/xai": "3.0.106",
57
- "ai": "6.0.224",
55
+ "ai": "7.0.31",
58
56
  "front-matter": "4.0.2",
59
57
  "zod": "4.4.3"
60
58
  },
61
59
  "devDependencies": {
60
+ "@tsconfig/bun": "1.0.10",
62
61
  "@tsconfig/node-lts": "24.0.0",
63
62
  "@tsconfig/node-ts": "23.6.4",
64
63
  "@tsconfig/node24": "24.0.4",
64
+ "@types/bun": "1.3.14",
65
65
  "@types/node": "24.13.3",
66
66
  "@willbooster/oxfmt-config": "1.2.2",
67
67
  "@willbooster/oxlint-config": "1.4.8",
68
68
  "@willbooster/prettier-config": "10.4.0",
69
- "@willbooster/wb": "13.28.8",
70
- "build-ts": "17.1.34",
71
- "conventional-changelog-conventionalcommits": "9.3.1",
69
+ "@willbooster/wb": "15.3.0",
70
+ "build-ts": "19.0.0",
71
+ "conventional-changelog-conventionalcommits": "10.2.1",
72
72
  "lefthook": "2.1.10",
73
73
  "oxfmt": "0.59.0",
74
74
  "oxlint": "1.74.0",
75
- "oxlint-tsgolint": "0.24.0",
75
+ "oxlint-tsgolint": "7.0.2001",
76
76
  "prettier": "3.9.5",
77
77
  "prettier-plugin-java": "2.10.2",
78
- "puppeteer": "24.43.1",
78
+ "puppeteer": "25.3.0",
79
79
  "semantic-release": "25.0.7",
80
80
  "sort-package-json": "4.0.0",
81
81
  "typescript": "7.0.2",
82
82
  "vitest": "4.1.10"
83
83
  },
84
- "packageManager": "yarn@4.17.1",
85
84
  "engines": {
86
85
  "node": ">=24"
87
86
  },