{"_id":"@donrx/scratch-judge","_rev":"6-92f4df03d1e6b4027b433a9101c72b61","name":"@donrx/scratch-judge","dist-tags":{"latest":"1.0.0"},"versions":{"0.1.0":{"name":"@donrx/scratch-judge","version":"0.1.0","keywords":["scratch","judge"],"author":{"name":"donrx"},"license":"MIT","_id":"@donrx/scratch-judge@0.1.0","maintainers":[{"name":"donrx","email":"triple.khongkhong@gmail.com"}],"homepage":"https://github.com/donrx/scratch-judge#readme","bugs":{"url":"https://github.com/donrx/scratch-judge/issues"},"dist":{"shasum":"448920a8f5247c546f60f6cf5ea738352ddab9ea","tarball":"https://registry.npmjs.org/@donrx/scratch-judge/-/scratch-judge-0.1.0.tgz","fileCount":3,"integrity":"sha512-/p6SPRVawMdq5kEFgShYOK+YPraHXIFgp6pKRgs40J2Yx434m+vV8L7Lb4YAoZYlCDrPC756DG1KEOWjn25DXQ==","signatures":[{"sig":"MEUCIQD5bkRloRigBqgzjbhH3b9yyq3agJuK7PDk6rrqP9Re5QIgaRUt9+ALIVgDxiP66xVY+U4I2r7hIaIDo+g0VmRWuog=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":7331},"main":"src/index.js","type":"module","engines":{"node":">=16"},"exports":"./src/index.js","gitHead":"24be707c6a12e70c8d89eb0c52b628f3a9575e4d","scripts":{"test":"node test/test.js"},"_npmUser":{"name":"donrx","email":"triple.khongkhong@gmail.com"},"repository":{"url":"git+https://github.com/donrx/scratch-judge.git","type":"git"},"_npmVersion":"11.12.1","description":"A fast and secure judge engine for Scratch projects","directories":{},"_nodeVersion":"25.9.0","dependencies":{"scratch-vm":"github:TurboWarp/scratch-vm"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/scratch-judge_0.1.0_1776755439445_0.5646854916613133","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@donrx/scratch-judge","version":"0.1.1","keywords":["scratch","judge"],"author":{"name":"donrx"},"license":"MIT","_id":"@donrx/scratch-judge@0.1.1","maintainers":[{"name":"donrx","email":"triple.khongkhong@gmail.com"}],"homepage":"https://github.com/donrx/scratch-judge#readme","bugs":{"url":"https://github.com/donrx/scratch-judge/issues"},"dist":{"shasum":"b76c398979f8313ec412ea5b3a08fb680c512793","tarball":"https://registry.npmjs.org/@donrx/scratch-judge/-/scratch-judge-0.1.1.tgz","fileCount":3,"integrity":"sha512-8E+f02/Hf2TnRLhboVrzP0loyeGUifUeUH4faoHNJ8K6lFIGeKnV4Ev2fLmhWD8SQvJuQC5Zb4ULs5uCDhkX2g==","signatures":[{"sig":"MEUCIQDCT+paOVwV4cAbAh8Zteo70VF+JTE3XV4SJWmvZk0lwQIgWM1ENymxl2cMBgmdvVjW+gWkpWKllxHLJyJTw1i9GgQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":7500},"main":"src/index.js","type":"module","engines":{"node":">=16"},"exports":"./src/index.js","gitHead":"93d5bf4c8633a23eba200cbf28936eb3312a78e5","scripts":{"test":"node test/test.js"},"_npmUser":{"name":"donrx","email":"triple.khongkhong@gmail.com"},"repository":{"url":"git+https://github.com/donrx/scratch-judge.git","type":"git"},"_npmVersion":"11.12.1","description":"A fast and secure judge engine for Scratch projects","directories":{},"_nodeVersion":"25.9.0","dependencies":{"scratch-vm":"github:TurboWarp/scratch-vm"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/scratch-judge_0.1.1_1776768545880_0.8575981147503813","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@donrx/scratch-judge","version":"0.1.2","keywords":["scratch","judge"],"author":{"name":"donrx"},"license":"MIT","_id":"@donrx/scratch-judge@0.1.2","maintainers":[{"name":"donrx","email":"triple.khongkhong@gmail.com"}],"homepage":"https://github.com/donrx/scratch-judge#readme","bugs":{"url":"https://github.com/donrx/scratch-judge/issues"},"dist":{"shasum":"161f8dd0f6638c3cf611e7841938541f2711f891","tarball":"https://registry.npmjs.org/@donrx/scratch-judge/-/scratch-judge-0.1.2.tgz","fileCount":3,"integrity":"sha512-wJSeBjMJFeY9fCrtu7dIK8n84VFTkahfsi1feLvfIDZJ5KjExqW/MML8EYnQ3ms+yV3EqfO30CP1izEeC8ezXA==","signatures":[{"sig":"MEQCICRiP3VKxaBXEq2wU8QlD2jUsCb+UEi2O8ruYYtN3yIsAiBu23qLHWXQLScKb1YM46Q9WD66J77SBsM9w9eY/l8EkA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":7734},"main":"src/index.js","engines":{"node":">=16"},"exports":"./src/index.js","gitHead":"9848c26fa2c4d1ac6c4a2ba2b4b882cfe088722f","scripts":{"test":"jest"},"_npmUser":{"name":"donrx","email":"triple.khongkhong@gmail.com"},"repository":{"url":"git+https://github.com/donrx/scratch-judge.git","type":"git"},"_npmVersion":"11.12.1","description":"A fast and secure judge engine for Scratch projects","directories":{},"_nodeVersion":"25.9.0","dependencies":{"scratch-vm":"github:TurboWarp/scratch-vm"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^30.3.0","@jest/globals":"^30.3.0"},"_npmOperationalInternal":{"tmp":"tmp/scratch-judge_0.1.2_1776780525168_0.035566046786769334","host":"s3://npm-registry-packages-npm-production"}},"0.1.3":{"name":"@donrx/scratch-judge","version":"0.1.3","keywords":["scratch","judge"],"author":{"name":"donrx"},"license":"MIT","_id":"@donrx/scratch-judge@0.1.3","maintainers":[{"name":"donrx","email":"triple.khongkhong@gmail.com"}],"homepage":"https://github.com/donrx/scratch-judge#readme","bugs":{"url":"https://github.com/donrx/scratch-judge/issues"},"dist":{"shasum":"b4fe3682c7608ef3f62d9dbe49767f9158bbd313","tarball":"https://registry.npmjs.org/@donrx/scratch-judge/-/scratch-judge-0.1.3.tgz","fileCount":3,"integrity":"sha512-GNjtf9w5incZ8ILOW6fTn+nNjkgj1IH44kEjgaRrBftKLxfoMUilEgXr+wVZ2KJN+PmvkFDYIgiU7Us7v9Tweg==","signatures":[{"sig":"MEQCIFwTnUgsd1wUqmu0jWW2efA7fpvZzsvwzhmwWRDv+EUAAiBninSrnEy8WV89Mk0JYZDAIALF86QLOLbEr8+A8M5u/Q==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":7763},"main":"src/index.js","types":"src/index.d.ts","engines":{"node":">=16"},"exports":"./src/index.js","gitHead":"abcce3fbe96d53fdec4b9811fc3b502ddd7b766b","scripts":{"test":"jest"},"_npmUser":{"name":"donrx","email":"triple.khongkhong@gmail.com"},"repository":{"url":"git+https://github.com/donrx/scratch-judge.git","type":"git"},"_npmVersion":"11.12.1","description":"A fast and secure judge engine for Scratch projects","directories":{},"_nodeVersion":"25.9.0","dependencies":{"scratch-vm":"github:TurboWarp/scratch-vm"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^30.3.0","@jest/globals":"^30.3.0"},"_npmOperationalInternal":{"tmp":"tmp/scratch-judge_0.1.3_1776780788328_0.4268149033837594","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@donrx/scratch-judge","version":"0.2.0","keywords":["scratch","judge"],"author":{"name":"donrx"},"license":"MIT","_id":"@donrx/scratch-judge@0.2.0","maintainers":[{"name":"donrx","email":"triple.khongkhong@gmail.com"}],"homepage":"https://github.com/donrx/scratch-judge#readme","bugs":{"url":"https://github.com/donrx/scratch-judge/issues"},"dist":{"shasum":"ad48a3e2bd080257b449f84e5597d4e9d96488d4","tarball":"https://registry.npmjs.org/@donrx/scratch-judge/-/scratch-judge-0.2.0.tgz","fileCount":3,"integrity":"sha512-ISqjlQK9frag9F6tymyZMw9R7nBIfWzHF5S1b3tsBIw99aQ85/TzZt+J9+pul+qvBYxbZtoS9eEvV1PJA5JjTw==","signatures":[{"sig":"MEYCIQC44jhEbHmHToz7HhmOeUsqez5Py5gPDGTQxUUPdlsmkgIhAPbqWFnGz4AlCJNiZ+BFaQwoqAeLZ1HT5wrS7nwfAOU6","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":6293},"main":"src/index.js","types":"src/index.d.ts","engines":{"node":">=16"},"exports":"./src/index.js","gitHead":"f5522711a57d5b8c89c7fa1bc1023900edef71b0","scripts":{"test":"jest"},"_npmUser":{"name":"donrx","email":"triple.khongkhong@gmail.com"},"repository":{"url":"git+https://github.com/donrx/scratch-judge.git","type":"git"},"_npmVersion":"11.12.1","description":"A fast and secure judge engine for Scratch projects","directories":{},"_nodeVersion":"25.9.0","dependencies":{"scratch-vm":"github:TurboWarp/scratch-vm"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^30.3.0","@jest/globals":"^30.3.0"},"_npmOperationalInternal":{"tmp":"tmp/scratch-judge_0.2.0_1776786208101_0.42389408997273725","host":"s3://npm-registry-packages-npm-production"}},"1.0.0":{"name":"@donrx/scratch-judge","version":"1.0.0","description":"A fast and secure judge engine for Scratch projects","main":"src/index.js","types":"src/index.d.ts","exports":"./src/index.js","type":"commonjs","scripts":{"test":"jest","build":"docker build -t scratch-judge-runner .","bat":"npm run build && npm test"},"keywords":["scratch","judge"],"author":{"name":"donrx"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/donrx/scratch-judge.git"},"engines":{"node":">=16"},"dependencies":{"isolated-vm":"^6.1.2","n":"^10.2.0","scratch-vm":"github:TurboWarp/scratch-vm","segfault-handler":"github:ddopson/node-segfault-handler"},"devDependencies":{"@jest/globals":"^30.3.0","jest":"^30.3.0"},"gitHead":"4e4d2c6a00deac20648bdd17b07890665909a025","_id":"@donrx/scratch-judge@1.0.0","bugs":{"url":"https://github.com/donrx/scratch-judge/issues"},"homepage":"https://github.com/donrx/scratch-judge#readme","_nodeVersion":"25.9.0","_npmVersion":"11.13.0","dist":{"integrity":"sha512-BJTiodMAoi4ltgGyV2vAzn3vs6BdjP4xVhf1bpFqZaiOWdy1/5O91M2V4RC6jmLGpupGtYEmCiFVqKisf/4Q9A==","shasum":"908c745860efa323fe492cad36ed089d9a38ee6f","tarball":"https://registry.npmjs.org/@donrx/scratch-judge/-/scratch-judge-1.0.0.tgz","fileCount":3,"unpackedSize":9190,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCP9I6bzuOz8tnF1Xq2oNUBT8IaptzamN04YuecWS3g/gIhAOKmAhN6C3C60FNXB5cl/HX5qwu7ztNw0h2KyR4+tPNm"}]},"_npmUser":{"name":"donrx","email":"triple.khongkhong@gmail.com"},"directories":{},"maintainers":[{"name":"donrx","email":"triple.khongkhong@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/scratch-judge_1.0.0_1777198137728_0.7862519988099572"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-21T07:10:39.370Z","modified":"2026-04-26T10:08:57.983Z","0.1.0":"2026-04-21T07:10:39.607Z","0.1.1":"2026-04-21T10:49:06.010Z","0.1.2":"2026-04-21T14:08:45.297Z","0.1.3":"2026-04-21T14:13:08.531Z","0.2.0":"2026-04-21T15:43:28.259Z","1.0.0":"2026-04-26T10:08:57.864Z"},"bugs":{"url":"https://github.com/donrx/scratch-judge/issues"},"author":{"name":"donrx"},"license":"MIT","homepage":"https://github.com/donrx/scratch-judge#readme","keywords":["scratch","judge"],"repository":{"type":"git","url":"git+https://github.com/donrx/scratch-judge.git"},"description":"A fast and secure judge engine for Scratch projects","maintainers":[{"name":"donrx","email":"triple.khongkhong@gmail.com"}],"readme":"# scratch-judge\n\nA fast and secure judge engine for Scratch projects. Runs `.sb3` programs inside an isolated Docker container with no network access, capped memory, and a configurable timeout.\n\n## Requirements\n\n- [Node.js](https://nodejs.org/) ≥ 16\n- [Docker](https://www.docker.com/) (required for sandboxed execution)\n\n## Installation\n\nInstall as a dependency:\n\n```bash\nnpm install @donrx/scratch-judge\n```\n\nClone for development:\n\n```bash\ngit clone https://github.com/donrx/scratch-judge.git\ncd scratch-judge\nnpm install\n```\n\n## Docker setup\n\nBefore calling `judge()`, build the runner image once:\n\n```bash\ndocker build -t scratch-judge-runner .\n```\n\nThe image is a slim Node 20 container that executes the Scratch VM in a read-only, network-isolated environment.\n\n## Usage\n\n```js\nimport { judge } from '@donrx/scratch-judge';\nimport fs from 'fs';\n\nconst program = fs.readFileSync('project.sb3');\n\nconst tests = {\n  timeout: 5000,\n  tests: [\n    {\n      input:  { list: ['1', '2', '3'], live: ['hello'] },\n      output: { list: ['6'],           live: 'world'   }\n    }\n  ]\n};\n\nconst result = await judge(program, tests);\nconsole.log(result.avgScore);   // 0–100\nconsole.log(result.judgement);  // per-test breakdown\n```\n\n## API\n\n### `judge(program, json, checker?)`\n\nRuns a Scratch project against a set of test cases and returns a score.\n\n| Parameter | Type | Description |\n|-----------|------|-------------|\n| `program` | `Buffer` \\| `ArrayBuffer` | The `.sb3` project file contents |\n| `json` | `JudgeConfig` | Test configuration (see below) |\n| `checker` | `Checker` _(optional)_ | Custom checker function (see below) |\n\n**Returns:** `Promise<JudgeResult>`\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `avgScore` | `number` | Average score across all test cases (0–100) |\n| `judgement` | `Array<{ score: number, reason: string }>` | Per-test result |\n\n---\n\n### Test configuration (`JudgeConfig`)\n\n```js\n{\n  timeout: 5000,   // Max ms per test before the VM is killed\n  tests: [\n    {\n      input: {\n        list: [],    // string[] — values pre-loaded into the INPUT list variable\n        live: []     // string[] — answers fed to Ask/Answer prompts, in order\n      },\n      output: {\n        list: [],    // string[] — expected OUTPUT list variable contents\n        live: ''     // string  — expected last Say message\n      }\n    }\n  ]\n}\n```\n\nThe Scratch project must expose two stage list variables named **`INPUT`** and **`OUTPUT`**. If a test case uses neither (both input and expected output are empty lists with no live I/O), the test is skipped gracefully.\n\n---\n\n### Custom checker\n\nBy default, scratch-judge requires an exact match on both the `OUTPUT` list and the last `Say` message. You can supply your own scoring logic:\n\n```js\nconst checker = (input, output, expected) => {\n  // input    – the test's input object  { list: string[], live: string[] }\n  // output   – what the program produced { list: string[], live: string }\n  // expected – the expected output        { list: string[], live: string }\n\n  const correct =\n    output.live === expected.live &&\n    output.list.join() === expected.list.join();\n\n  return {\n    score:  correct ? 100 : 0,\n    reason: correct ? 'Correct answer' : 'Wrong answer'\n  };\n};\n\nconst result = await judge(program, tests, checker);\n```\n\nThe checker must return `{ score: number, reason: string }` where `score` is 0–100.\n\n---\n\n## Scratch project requirements\n\n| Variable name | Type | Purpose |\n|---------------|------|---------|\n| `INPUT` | List (stage) | Test input values read by the project |\n| `OUTPUT` | List (stage) | Values written by the project as output |\n\n- The project reads live input via **Ask & Answer** blocks. Answers are supplied from `input.live` in order.\n- The last value passed to a **Say** block is captured as `output.live`.\n- If the project runs longer than `json.timeout` milliseconds, the test is scored 0 with reason `'Out of time'`.\n\n---\n\n## Sandbox\n\nEach judging call spins up a fresh Docker container with the following constraints:\n\n| Constraint | Value |\n|------------|-------|\n| Network | none |\n| Memory | 512 MB |\n| CPUs | 1 |\n| Filesystem | read-only + `/tmp` tmpfs |\n| Container lifetime | one run, auto-removed |\n\nThe host only mounts the `.sb3` file into the container; the rest of your filesystem is untouched.\n\n---\n\n## TypeScript\n\nType declarations are included:\n\n```ts\nimport { judge, JudgeConfig, JudgeResult, Checker } from '@donrx/scratch-judge';\n```\n\n---\n\n## Development\n\nRun the test suite (requires Docker and the built image):\n\n```bash\nnpm test\n```\n\nTests are written with [Jest](https://jestjs.io/) and live in `test/judge.test.js`. Fixture `.sb3` and `.json` files are in `test/fixtures/`.\n\n---\n\n## License\n\nMIT","readmeFilename":"README.md"}