[
  {
    "path": ".github/workflows/deploy.yml",
    "content": "name: Deploy to GitHub Pages\r\n\r\non:\r\n  push:\r\n    branches:\r\n      - main\r\n\r\njobs:\r\n  build-and-deploy:\r\n    runs-on: ubuntu-latest\r\n\r\n    steps:\r\n      - name: Checkout repository\r\n        uses: actions/checkout@v4\r\n\r\n      - name: Setup Node.js\r\n        uses: actions/setup-node@v4\r\n        with:\r\n          node-version: '22'\r\n\r\n      - name: Install dependencies\r\n        run: npm install\r\n\r\n      - name: Build project\r\n        run: npm run build\r\n\r\n      - name: Prepare deployment directory\r\n        run: |\r\n          mkdir pages\r\n          cp -r dist pages/\r\n          cp -r test pages/\r\n          cp -r css pages/\r\n          cp index.html pages/\r\n          cp logo.svg pages/\r\n\r\n      - name: Deploy to GitHub Pages\r\n        uses: peaceiris/actions-gh-pages@v4\r\n        with:\r\n          github_token: ${{ secrets.GITHUB_TOKEN }}\r\n          publish_dir: ./pages"
  },
  {
    "path": ".github/workflows/npm-publish.yml",
    "content": "# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created\n# For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages\n\nname: Node.js Package\n\non:\n  # release:\n  #   types: [create]\n  push:\n    branches:\n      - main\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: actions/setup-node@v4\n        with:\n          node-version: 24\n      - run: npm install\n      - run: npm run build\n      - run: npm test\n\n  publish-npm:\n    needs: test\n    runs-on: ubuntu-latest\n    permissions:\n      id-token: write\n      contents: read\n    steps:\n      - uses: actions/checkout@v4\n      - uses: actions/setup-node@v4\n        with:\n          node-version: 24\n          registry-url: https://registry.npmjs.org/\n      - run: npm install\n      - run: npm run build      \n      - run: npm publish --access public --provenance\n\n"
  },
  {
    "path": ".github/workflows/shields.yml",
    "content": "name: Shield badges\n\non:\n  push:\n    branches: [main]\n\njobs:\n  bundle-size:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n\n      - uses: actions/setup-node@v4\n        with:\n          node-version: 24\n\n      - run: npm install\n      - run: npm run build\n\n      - name: Measure bundle size (gzip)\n        id: size\n        run: |\n          BYTES=$(gzip -9 -c dist/umd/Sandbox.min.js | wc -c)\n          echo \"bytes=$BYTES\" >> $GITHUB_OUTPUT\n\n      - name: Format size\n        id: fmt\n        run: |\n          python3 -c \"\n          b = ${{ steps.size.outputs.bytes }}\n          if b >= 1024*1024:\n              s = f'{b/1024/1024:.1f} MB'\n          elif b >= 1024:\n              s = f'{b/1024:.1f} kB'\n          else:\n              s = f'{b} B'\n          print('size=' + s)\n          \" >> $GITHUB_OUTPUT\n\n      - name: Update Gist badge\n        uses: schneegans/dynamic-badges-action@v1.7.0\n        with:\n          auth: ${{ secrets.GIST_TOKEN }}\n          gistID: dd4a46f4c2fff1c43d4f2e8fb4b52862\n          filename: bundle-size.json\n          label: minified (gzip)\n          message: ${{ steps.fmt.outputs.size }}\n          color: blue\n\n  tests:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n\n      - uses: actions/setup-node@v4\n        with:\n          node-version: 24\n\n      - run: npm install\n\n      - name: Run tests with JSON reporter\n        id: test\n        run: |\n          NODE_OPTIONS='--no-warnings=ExperimentalWarning' npx jest --json --outputFile=jest-results.json || true\n\n      - name: Extract test counts\n        id: counts\n        run: |\n          python3 -c \"\n          import json\n          with open('jest-results.json') as f:\n              data = json.load(f)\n          passed = data['numPassedTests']\n          total = data['numTotalTests']\n          print(f'passed={passed}')\n          print(f'total={total}')\n          print(f'message={passed}/{total}')\n          color = '#439e2e' if passed == total else 'red'\n          print(f'color={color}')\n          \" >> $GITHUB_OUTPUT\n\n      - name: Update Gist badge\n        uses: schneegans/dynamic-badges-action@v1.7.0\n        with:\n          auth: ${{ secrets.GIST_TOKEN }}\n          gistID: dd4a46f4c2fff1c43d4f2e8fb4b52862\n          filename: tests.json\n          label: passing tests\n          message: ${{ steps.counts.outputs.message }}\n          color: ${{ steps.counts.outputs.color }}\n\n  sandbox-badge:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n\n      - uses: actions/setup-node@v4\n        with:\n          node-version: 24\n\n      - name: Prepare sandbox badge\n        id: sandbox_badge\n        run: |\n          # collapse SVG to a single line\n          svg_inline=\"$(tr -d '\\n' < ./logo.svg)\"\n          echo \"message=Sandbox protected\" >> \"$GITHUB_OUTPUT\"\n          echo \"color=#202830\" >> \"$GITHUB_OUTPUT\"\n          echo \"label=\" >> \"$GITHUB_OUTPUT\"\n          echo \"logoSvg=${svg_inline}\" >> \"$GITHUB_OUTPUT\"\n\n      - name: Update Gist badge\n        uses: schneegans/dynamic-badges-action@v1.7.0\n        with:\n          auth: ${{ secrets.GIST_TOKEN }}\n          gistID: dd4a46f4c2fff1c43d4f2e8fb4b52862\n          filename: sandbox-protected.json\n          label: ${{ steps.sandbox_badge.outputs.label }}\n          message: ${{ steps.sandbox_badge.outputs.message }}\n          color: ${{ steps.sandbox_badge.outputs.color }}\n          logoSvg: ${{ steps.sandbox_badge.outputs.logoSvg }}"
  },
  {
    "path": ".github/workflows/test.yml",
    "content": "name: Run Tests\r\n\r\non:\r\n  push:\r\n    branches:\r\n      - main\r\n  pull_request:\r\n    branches:\r\n      - '**'   # Optional: also run on PRs\r\n\r\njobs:\r\n  test:\r\n    runs-on: ubuntu-latest\r\n\r\n    steps:\r\n      - name: Checkout code\r\n        uses: actions/checkout@v4\r\n\r\n      - name: Setup Node.js\r\n        uses: actions/setup-node@v4\r\n        with:\r\n          node-version: '22'\r\n\r\n      - name: Install dependencies\r\n        run: npm install\r\n\r\n      - name: Run lint\r\n        run: npm run lint\r\n\r\n      - name: Run tests\r\n        run: npm test\r\n\r\n      - name: Upload coverage to Codecov\r\n        uses: codecov/codecov-action@v4\r\n        with:\r\n          files: ./coverage/lcov.info\r\n          fail_ci_if_error: false\r\n          token: ${{ secrets.CODECOV_TOKEN }}\r\n\r\n  performance:\r\n    runs-on: ubuntu-latest\r\n\r\n    steps:\r\n      - name: Checkout code\r\n        uses: actions/checkout@v4\r\n\r\n      - name: Setup Node.js\r\n        uses: actions/setup-node@v4\r\n        with:\r\n          node-version: '22'\r\n\r\n      - name: Install dependencies\r\n        run: npm install\r\n\r\n      - name: Build\r\n        run: npm run build\r\n\r\n      - name: Run performance tests\r\n        run: npm run test:perf"
  },
  {
    "path": ".gitignore",
    "content": "node_modules/\r\ndist/\r\nbuild/\r\ncoverage/"
  },
  {
    "path": ".husky/pre-commit",
    "content": "npx lint-staged\n"
  },
  {
    "path": ".npmignore",
    "content": "node_modules/\n.github/\n.gitignore\n.git/\n.vscode/\n.husky\n.claude\njs/\ntest/\ncss/\nsrc/\nindex.html\ntsconfig.json\nrollup.config.mjs\n.eslintrc.json\n.prettierrc\njest.config.js\ntsconfig.jest.json\npackage-lock.json\n"
  },
  {
    "path": ".prettierignore",
    "content": "# Dependencies\nnode_modules\n\n# Build outputs\ndist\nbuild\ncoverage\n\n# Minified files\n*.min.js\n\n# Configuration files\nrollup.config.mjs\njest.config.js\n\n# Test files that should be ignored\ntest/eval/jquery.min.js\n"
  },
  {
    "path": ".prettierrc",
    "content": "{\n  \"printWidth\": 100,\n  \"singleQuote\": true\n}\n"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n  \"editor.defaultFormatter\": \"esbenp.prettier-vscode\",\n  \"editor.formatOnSave\": true\n}\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2019 nyariv\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "[![GitHub](https://img.shields.io/github/license/nyariv/SandboxJS)](https://github.com/nyariv/SandboxJS/blob/main/LICENSE) ![npm](https://img.shields.io/npm/v/@nyariv/sandboxjs) ![Bundle size](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/nyariv/dd4a46f4c2fff1c43d4f2e8fb4b52862/raw/bundle-size.json)\n [![GitHub issues](https://img.shields.io/github/issues-raw/nyariv/SandboxJS)](https://github.com/nyariv/SandboxJS/issues) [![codecov](https://codecov.io/gh/nyariv/SandboxJS/branch/main/graph/badge.svg)](https://codecov.io/gh/nyariv/SandboxJS) [![passing tests](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/nyariv/dd4a46f4c2fff1c43d4f2e8fb4b52862/raw/tests.json)](https://nyariv.github.io/SandboxJS/) [![Sandbox protected](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/nyariv/dd4a46f4c2fff1c43d4f2e8fb4b52862/raw/sandbox-protected.json)](https://github.com/nyariv/SandboxJS)\n---\n<table><tr><td width=\"100\">\n\n![sanboxjs logo](./logo.svg)\n\n</td><td width=\"400\">\n<h1>SandboxJS</h1>\n<i>Safe eval runtime</i>\n</td></tr></table>\n\n---\n\n\n\n\nThis is a javascript sandboxing library. When embedding any kind of js code inside your app (either web or nodejs based) you are essentially giving access to the entire kingdom, hoping there is no malicious code in a dependency such as with supply chain attacks. For securing code, sandboxing is needed.\n\n>  a \"sandbox\" is a security mechanism for separating running programs, usually in an effort to mitigate system failures or software vulnerabilities from spreading. It is often used to execute untested or untrusted programs or code, possibly from unverified or untrusted third parties, suppliers, users or websites, without risking harm to the host machine or operating system. - Wikipedia\n\nThere are many vulnerable modules available on the global scope of a js environment, and unfortunately it is way to easy to get access to that if 3rd party code is allowed to be included. The main way is through the `eval` or `Function` globals because they execute code in the global context. Trying to block access to some global components through proxies or limiting scope variables is fruitless because of one main issue: _every function inherits the `Function` prototype, and can invoke `eval` by calling its constructor_, essentially making `eval` at most two properties away from anything in js.\n\nExample:\n```javascript\n[].filter.constructor(\"alert('jailbreak')\")()\n```\n\nTo make matters worse, it is extremely difficult to blacklist functions because code is easily obfuscated. For example, it is possible to execute anything using only `(`, `)`, `[`, `]`, `!`, and `+`. ([source](http://www.jsfuck.com/))\n\n```javascript\n[+!+[]]+[] // This evaluates to the number one, go a head type that in console\n```\n\n**SandboxJS** solves this problem by parsing js code and executing it though its own js runtime, while in the process checking every single prototype function that is being called. This allows whitelisting anything and everything, regardless of obfuscation.\n\nThis means that you can potentially give different libraries different permissions, such as allowing `fetch()` for one library, or allowing access to the `Node` prototype for another, depending what the library requires and nothing more, and any objects that are gotten from the sandbox will remain sandboxed when used outside of it.\n\nAdditionaly, `eval` and `Function` are sandboxed as well, and can be used recursively safely, which is why they are considered safe globals in SandboxJS.\n\nThere is an `audit` method that will return all the accessed functions and prototypes during runtime if you need to know what permissions to give a certain library.\n\nSince parsing and executing are separated, execution with SandboxJS can be sometimes even faster than `eval`, allowing to prepare the execution code ahead of time.\n\n## Installation\n\n```\nnpm install @nyariv/sandboxjs\n```\n\n## Usage\n\nThe following is the bare minimum of code for using SandboxJS. This assumes safe whitelisted defaults.\n\n```javascript\nconst code = `return myTest;`;\nconst scope = { myTest: \"hello world\" };\nconst sandbox = new Sandbox();\nconst exec = sandbox.compile(code);\nconst result = exec(scope).run(); // result: \"hello world\"\n```\n\nIt is possible to defined multiple scopes in case you are reusing scopes with multiple layers.\n\n```javascript\nconst sandbox = new Sandbox();\n\nconst scopeA = {a: 1};\nconst scopeB = {b: 2};\nconst scopeC = {c: 3};\n\nconst code = `a = 4; let d = 5; let b = 6`;\nconst exec = sandbox.compile(code);\nexec(scopeA, scopeB, scopeC).run();\n\nconsole.log(scopeA); // {a: 4}\nconsole.log(scopeB); // {b: 2}\nconsole.log(scopeC); // {c: 3, d: 5, b: 6}\n```\n\nYou can set your own whilelisted prototypes and global properties like so (`alert` and `Node` are added to whitelist in the following code):\n\n```javascript\nconst prototypeWhitelist = Sandbox.SAFE_PROTOTYPES;\nprototypeWhitelist.set(Node, new Set());\n\nconst globals = {...Sandbox.SAFE_GLOBALS, alert};\n\nconst sandbox = new Sandbox({globals, prototypeWhitelist});\n```\n\nYou can audit a piece of code, which will permit all globals and prototypes but will return a json with accessed globals and prototypes over time.\n\n```javascript\nconst code = `console.log(\"test\")`;\nconsole.log(Sandbox.audit(code));\n```\n\n## Safe Globals\n\n- `globalThis`\n- `Function`\n- `eval`\n- `setTimeout` - excluded by default\n- `setInterval` - excluded by default\n- `clearTimeout` - excluded by default\n- `clearInterval` - excluded by default\n- `console`\n- `isFinite`\n- `isNaN`\n- `parseFloat`\n- `parseInt`\n- `decodeURI`\n- `decodeURIComponent`\n- `encodeURI`\n- `encodeURIComponent`\n- `escape`\n- `unescape`\n- `Boolean`\n- `Number`\n- `BigInt`\n- `String`\n- `Object`\n- `Array`\n- `Symbol`\n- `Error`\n- `EvalError`\n- `RangeError`\n- `ReferenceError`\n- `SyntaxError`\n- `TypeError`\n- `URIError`\n- `Int8Array`\n- `Uint8Array`\n- `Uint8ClampedArray`\n- `Int16Array`\n- `Uint16Array`\n- `Int32Array`\n- `Uint32Array`\n- `Float32Array`\n- `Float64Array`\n- `Map`\n- `Set`\n- `WeakMap`\n- `WeakSet`\n- `Promise`\n- `Intl`\n- `JSON`\n- `Math`\n\n# Safe Prototypes\n\n- `SandboxGlobal`\n- `Function`\n- `Boolean`\n- `Object`\n- `Number`\n- `BigInt`\n- `String`\n- `Date`\n- `RegExp`\n- `Error`\n- `Array`\n- `Int8Array`\n- `Uint8Array`\n- `Uint8ClampedArray`\n- `Int16Array`\n- `Uint16Array`\n- `Int32Array`\n- `Uint32Array`\n- `Float32Array`\n- `Float64Array`\n- `Map`\n- `Set`\n- `WeakMap`\n- `WeakSet`\n- `Promise`\n\n## Goals\n\n|Feature|Status|\n|---|---|\n|Prototype access protection|done|\n|Globals access protection|done|\n|Prototype proxying|done|\n|Single line sandboxing|done|\n|Multi line sandboxing|done|\n|Functions support|done|\n|Audit prototype and globals access|done|\n|Code blocks (try/catch, ifs, and loops)|done|\n|Async/await|done|\n|Execution time protection|done|\n|Extensibility|done|\n|Full ECMAScript support|90%|\n|Script source and import sandboxing|Won't fix - handled by 3rd party|\n|DOM ownership and inherited permissions|See [scope-js](https://github.com/nyariv/scope-js)|\n|Tests|done|\n\n📋 **[ECMAScript Feature Implementation Status](TODO.md)** - See which JavaScript features are supported and tested (~90% of core ES5-ES2018 features)"
  },
  {
    "path": "TODO.md",
    "content": "# SandboxJS - ECMAScript Feature Status\n\n[![codecov](https://codecov.io/gh/nyariv/SandboxJS/branch/main/graph/badge.svg)](https://codecov.io/gh/nyariv/SandboxJS)\n\nThis document describes the current implementation status of ECMAScript features in SandboxJS.\n\n**Test Coverage**: 1598 total tests | Code Coverage: ~97% statement coverage, ~92% branch coverage\n\n---\n\n## 🐛 Known Bugs & Limitations\n\nThe following limitations have been identified during testing:\n\n1. **Unicode identifier escapes** - `\\uXXXX` escape sequences in variable names are not supported\n\n---\n\n## ✅ Supported Features\n\nSandboxJS supports the following ECMAScript features with comprehensive test coverage:\n\n### Arithmetic Operators\n\n- ✅ **Addition** - `1+1` → `2`\n- ✅ **Subtraction** - `1 * 2 + 3 * (4 + 5) * 6` → `164`\n- ✅ **Multiplication** - `1 * 2` → `2`\n- ✅ **Division** - `1+2*4/5-6+7/8 % 9+10-11-12/13*14` → `-16.448...`\n- ✅ **Modulus** - `test2 %= 1` → `0`\n- ✅ **Exponentiation** - `2 ** 3` → `8`, `3 ** 2 ** 2` → `81`\n- ✅ **Exponentiation assignment** - `test2 **= 0` → `1`\n- ✅ **Unary plus** - `+'1'` → `1`\n- ✅ **Unary minus** - `-'1'` → `-1`\n\n### Logical Operators\n\n- ✅ **Logical AND** - `true && true || false` → `true`\n- ✅ **Logical OR** - `test2 || false` → `3`\n- ✅ **Logical NOT** - `!test2` → `false`, `!!test2` → `true`\n- ✅ **Nullish coalescing** - `null ?? 'default'` → `'default'`, `0 ?? 'default'` → `0`\n\n### Comparison Operators\n\n- ✅ **Equality** - `test2 == '3'` → `true`\n- ✅ **Strict equality** - `test2 === '3'` → `false`\n- ✅ **Inequality** - `test2 != '3'` → `false`\n- ✅ **Strict inequality** - `test2 !== '3'` → `true`\n- ✅ **Less than** - `test2 < 3` → `false`\n- ✅ **Greater than** - `test2 > 3` → `false`\n- ✅ **Less than or equal** - `test2 <= 3` → `true`\n- ✅ **Greater than or equal** - `test2 >= 3` → `true`\n\n### Bitwise Operators\n\n- ✅ **Bitwise AND** - `test2 & 1` → `1`\n- ✅ **Bitwise OR** - `test2 | 4` → `7`\n- ✅ **Bitwise NOT** - `~test2` → `-2`\n- ✅ **Bitwise XOR** - `test2 ^= 1` → `1`\n- ✅ **Left shift** - Tested in complex expressions\n- ✅ **Right shift** - Tested in complex expressions\n- ✅ **Unsigned right shift** - Tested in complex expressions\n- ✅ **Left shift assignment** - `let x = 5; x <<= 1` → `10`\n- ✅ **Right shift assignment** - `let x = 8; x >>= 1` → `4`\n- ✅ **Unsigned right shift assignment** - `let x = 8; x >>>= 2` → `2`\n- ✅ **XOR assignment** - `test2 ^= 1` → `1`\n- ✅ **AND assignment** - `test2 &= 3` → `1`\n- ✅ **OR assignment** - `test2 |= 2` → `3`\n\n### Assignment Operators\n\n- ✅ **Simple assignment** - `test2 = 1` → `1`\n- ✅ **Addition assignment** - `test2 += 1` → `2`\n- ✅ **Subtraction assignment** - `test2 -= 1` → `1`\n- ✅ **Multiplication assignment** - `test2 *= 2` → `2`\n- ✅ **Division assignment** - `test2 /= 2` → `1`\n- ✅ **Exponentiation assignment** - `test2 **= 0` → `1`\n- ✅ **Modulus assignment** - `test2 %= 1` → `0`\n- ✅ **XOR assignment** - `test2 ^= 1` → `1`\n- ✅ **AND assignment** - `test2 &= 3` → `1`\n- ✅ **OR assignment** - `test2 |= 2` → `3`\n- ✅ **Logical AND assignment (&&=)** - `let x = 10; x &&= 5` → `5`\n- ✅ **Logical OR assignment (||=)** - `let x = 0; x ||= 5` → `5`\n- ✅ **Nullish coalescing assignment (??=)** - `let x = null; x ??= 5` → `5`\n- ✅ **Post-increment** - `test2++` → `1`\n- ✅ **Pre-increment** - `++test2` → `3`\n\n### Other Operators\n\n- ✅ **Conditional (ternary)** - `test[test2] ? true : false ? 'not ok' : 'ok'` → `'ok'`\n- ✅ **Optional chaining** - `!({}).a?.a` → `true`, `({}).a?.toString()` → `undefined`\n- ✅ **Comma operator** - `1,2` → `2`\n- ✅ **typeof** - `typeof '1'` → `'string'`, `typeof x === 'undefined'` → `true`\n- ✅ **instanceof** - `{} instanceof Object` → `true`\n- ✅ **in operator** - `'a' in {a: 1}` → `true`\n- ✅ **delete operator** - `delete 1` → `true`, `let a = {b: 1}; return delete a.b` → `true`\n- ✅ **void operator** - `void 2 == '2'` → `false`\n- ✅ **new operator** - `new Date(0).toISOString()` → `'1970-01-01T00:00:00.000Z'`\n\n### Data Types\n\n- ✅ **Numbers** - `2.2204460492503130808472633361816E-16` → Scientific notation\n- ✅ **BigInt** - `(1n + 0x1n).toString()` → `'2'`\n- ✅ **Binary literals** - `0b1010` → `10`, `0B1111` → `15`, `0b1010n` → `'10'` (BigInt), `0b1_000` → `8` (with separators)\n- ✅ **Octal literals** - `0o17` → `15`, `0O77` → `63`, `0o17n` → `'15'` (BigInt), `0o7_777` → `4095` (with separators)\n- ✅ **Strings** - `\"test2\"` → `'test2'`\n- ✅ **Template literals** - `` `test2 is ${`also ${test2}`}` `` → `'test2 is also 1'`\n- ✅ **Tagged template functions** - ``tag`hello ${\"world\"}` `` → function receives string parts and interpolated values\n- ✅ **Escape sequences** - `\"\\\\\"` → `'\\\\'`, `\"\\\\xd9\"` → `'Ù'`, `\"\\\\n\"` → `'\\n'`\n- ✅ **Boolean** - `true`, `false`\n- ✅ **null** - `null ?? 'default'` → `'default'`\n- ✅ **undefined** - `typeof x === 'undefined'` → `true`\n- ✅ **Arrays** - `[test2, 2]` → `[1, 2]`\n- ✅ **Objects** - `{\"aa\": test[0](), b: test2 * 3}` → `{ \"aa\": 1, \"b\": 3 }`\n- ✅ **Regular expressions** - `/a/.test('a')` → `true`, `/a/i.test('A')` → `true`\n\n### Objects & Arrays\n\n- ✅ **Object literals** - `{a: 1, b: 2}` → `{ a: 1, b: 2 }`\n- ✅ **Array literals** - `[1, 2]` → `[1, 2]`\n- ✅ **Property access (dot)** - `a.b.c` → `2`\n- ✅ **Property access (bracket)** - `a['b']['c']` → `2`\n- ✅ **Computed property names** - `{\"aa\": test[0]()}` → `{ \"aa\": 1 }`\n- ✅ **Object spread** - `{a: 1, ...{b: 2, c: {d: test2,}}, e: 5}` → Full object\n- ✅ **Array spread** - `[1, ...[2, [test2, 4]], 5]` → `[1, 2, [3, 4], 5]`\n- ✅ **Object method shorthand** - `let y = {a: 1, b(x) {return this.a + x}}; return y.b(2)` → `3`\n\n### Functions\n\n- ✅ **Function declarations** - `function f(a) { return a + 1 } return f(2);` → `3`\n- ✅ **Function expressions** - `(function () { return 1 })()` → `1`\n- ✅ **Arrow functions (single param)** - `(a => a + 1)(1)` → `2`\n- ✅ **Arrow functions (multiple params)** - `((a) => {return a + 1})(1)` → `2`\n- ✅ **Arrow functions (expression body)** - `(a => a + 1)(1)` → `2`\n- ✅ **Arrow functions (block body)** - `(() => {return 1})()` → `1`\n- ✅ **Async arrow functions** - `(async () => 1)()` → `1`\n- ✅ **Async function expressions** - `(async () => await 1)()` → `1`\n- ✅ **Rest parameters** - `[0,1].filter((...args) => args[1])` → `[1]`\n- ✅ **Parameter default values** - `function fn(a = 1) { return a; }` → `1`\n- ✅ **Spread in function calls** - `Math.pow(...[2, 2])` → `4`\n- ✅ **Constructor functions** - `function LinkedListNode(e){this.value=e,this.next=null}` with `new`\n- ✅ **Recursive functions** - Linked list reverse example\n\n#### Destructuring\n- ✅ **Array destructuring** - `const [a, b] = [1, 2]`\n- ✅ **Object destructuring** - `const {a, b} = {a: 1, b: 2}`\n- ✅ **Nested destructuring** - `const {a: {b}} = {a: {b: 42}}`\n- ✅ **Destructuring with defaults** - `const {a = 1} = {}`\n- ✅ **Custom variable names (renaming)** - `const {a: myA} = {a: 1}`\n- ✅ **Destructuring in function parameters** - `function fn({a, b}) { }`\n- ✅ **Rest in destructuring** - `const [a, ...rest] = [1, 2, 3]`, `const {a, ...rest} = obj`\n- ✅ **Computed property names in destructuring** - `const {[key]: val} = obj`\n- ✅ **Destructuring in for-of/for-in loops** - `for (const [a, b] of arr) { }`\n- ✅ **Destructuring in function parameters with defaults** - `function fn({a = 1, b = 2} = {}) { }`\n\n### Control Flow\n\n#### Conditionals\n- ✅ **if statement** - `if (true) { return true; } else return false` → `true`\n- ✅ **else statement** - `if (false) { return true; } else return false` → `false`\n- ✅ **if/else chains** - `if (false) return true; else if (false) {return true} else return false` → `false`\n- ✅ **Nested if statements** - Complex nested if/else with 9 levels\n- ✅ **Inline ternary** - `true ? 1 : 2` → `1`\n\n#### Loops\n- ✅ **for loop** - `let x; for(let i = 0; i < 2; i++){ x = i }; return x;` → `1`\n- ✅ **while loop** - `let x = 2; while(--x){ }; return x;` → `0`\n- ✅ **do-while loop** - `let x = 1; do {x++} while(x < 1); return x;` → `2`\n- ✅ **for-of loop** - `for(let i of [1,2]){ return i };` → `1`\n- ✅ **for-in loop** - `for(let i in [1,2]){ return i };` → `'0'`\n- ✅ **break statement** - `for(let i = 0; i < 2; i++){ x = i; break; }` → Exits early\n- ✅ **continue statement** - `for (let i = 0; i < 5; i++) { if (i === 2) continue; sum += i; }` → Skips iteration\n\n#### Switch\n- ✅ **switch statement** - `switch(1) {case 1: b = 2; break; case 2: b = 3; default: b = 4}; return b` → `2`\n- ✅ **case clauses** - Multiple case tests\n- ✅ **default clause** - `switch(3) {case 1: b = 2; break; case 2: b = 3; default: b = 4}; return b` → `4`\n- ✅ **Fall-through behavior** - `switch(1) {case 1:b = 2; case 2: b = 3; default: b = 4}; return b` → `4`\n\n#### Error Handling\n- ✅ **try/catch** - `try {a.x.a} catch {return 1}; return 2` → `1`\n- ✅ **try/catch with exception variable** - `try { throw new Error('msg'); } catch(e) { return e.message; }` → `'msg'`\n- ✅ **finally block** - `try { return 1; } finally { x = 2; }` → Finally executes before return\n- ✅ **finally overrides return** - `try { return 1; } finally { return 2; }` → `2`\n- ✅ **finally overrides error** - `try { throw Error('a'); } finally { throw Error('b'); }` → Error: 'b'\n- ✅ **throw statement** - `throw new Error('test')` → Error with message\n\n#### Other\n- ✅ **Code blocks** - `{let j = 1; i += j;}` → Block scope\n- ✅ **this binding** - `let y = {a: 1, b(x) {return this.a + x}}` → Method context\n- ✅ **Closures** - `const a = () => {return 1}; const b = () => {return 2}; return (() => a() + b())()` → `3`\n\n### Variables\n\n- ✅ **var declaration** - `var i = 1; return i + 1` → `2`\n- ✅ **let declaration** - `let j = 1; return j + 1` → `2`\n- ✅ **const declaration** - `const k = 1; return k + 1` → `2`\n- ✅ **const immutability** - `const l = 1; return l = 2` → Error\n\n### Async/Await\n\n- ✅ **async functions** - `(async () => 1)()` → Promise resolves to `1`\n- ✅ **await keyword** - `(async () => await 1)()` → `1`\n- ✅ **await with promises** - `(async () => await (async () => 1)())()` → `1`\n- ✅ **async with variables** - `let p = (async () => 1)(); return (async () => 'i = ' + await p)()` → `'i = 1'`\n- ✅ **Async arrow functions** - `let i = 0; (async () => i += 1)(); return i;` → `1`\n- ✅ **for-await-of loops** - `for await (const item of asyncIterable) { }`\n\n#### Generators (ES6)\n- ✅ **Generator functions (function*)** - `function* gen() { yield 1; }`\n- ✅ **yield keyword**\n- ✅ **`yield` as expression value** - `const x = yield 1`\n- ✅ **yield* delegation**\n- ✅ **Async generators** - `async function* gen() { yield 1; }`\n- ✅ **Iterator `.return()`/`.throw()`** - Protocol methods for early termination and error injection\n- ✅ **`next(value)` injection** - Sending values back into a paused generator\n\n### Other Built-in Objects\n\n- ✅ **WeakMap** - All methods work: `set()`, `get()`, `has()`, `delete()` (6 tests)\n- ✅ **WeakSet** - All methods work: `add()`, `has()`, `delete()`\n\n### Comments\n\n- ✅ **Single-line comments** - `1 // 2` → `1`\n- ✅ **Multi-line comments** - `/* 2 */ 1` → `1`\n\n### Operator Precedence\n\nComprehensive operator precedence testing has been implemented with 35 tests covering:\n- NOT (!) with comparison operators\n- Logical NOT with AND/OR\n- Comparison operator chaining\n- Bitwise vs logical operators\n- Bitwise shift with arithmetic\n- Mixed bitwise operators (correct precedence: shift > & > ^ > |)\n- Exponentiation (right-associative)\n- typeof, delete, void with various operators\n- Optional chaining and nullish coalescing\n- Increment/decrement with arithmetic\n- Multiple unary operators\n- Comma operator in expressions\n\n---\n\n## ❌ Not Supported Features\n\nThe following ECMAScript features are not currently supported in SandboxJS:\n\n### HIGH PRIORITY\n\n#### Classes (ES6)\n- ❌ **class declarations**\n- ❌ **extends keyword (inheritance)**\n- ❌ **super keyword**\n- ❌ **Static methods**\n- ❌ **Class fields (public)**\n- ❌ **Private fields (#field)**\n- ❌ **Private methods**\n- ❌ **Static class fields**\n- ❌ **Static initialization blocks**\n\n#### Object Features\n- ❌ **Getters in object literals** - `{get prop() { return 1; }}`\n- ❌ **Setters in object literals** - `{set prop(v) { this.val = v; }}`\n\n### LOW PRIORITY\n\n#### Modules\nModule features are not supported by design as SandboxJS is intended for sandboxed code execution:\n- ❌ **import statements**\n- ❌ **export statements**\n- ❌ **Dynamic import()**\n- ❌ **import.meta**\n\n---\n\n## 🔒 Security-Related Restrictions (Intentionally Blocked)\n\nThese features are intentionally blocked for security reasons:\n\n- 🔒 Direct access to global scope\n- 🔒 Access to `__proto__` (prototype pollution prevention)\n- 🔒 Global object pollution\n- 🔒 Prototype method overriding\n- 🔒 Access to non-whitelisted globals\n- 🔒 Access to non-whitelisted prototype methods\n- 🔒 `with` statement\n- 🔒 `arguments` object (security risk, use rest parameters `...args` instead)\n- 🔒 Execution beyond quota limits\n\n---\n\n## 📝 Notes\n\n- **Priority Levels**:\n  - **HIGH**: Common patterns used frequently in production code\n  - **MEDIUM**: Less common but still important for completeness\n  - **LOW**: Edge cases and advanced features with limited use\n- **Implementation Focus**: SandboxJS focuses on core ES5-ES2018 features with strong security controls\n- **Performance**: Advanced meta-programming features are omitted to maintain sandbox safety\n"
  },
  {
    "path": "css/style.css",
    "content": ":root {\n  --bg: #0f1117;\n  --surface: #1a1d27;\n  --surface2: #22263a;\n  --border: #2e3248;\n  --accent: #4a9eff;\n  --accent2: #ffe94d;\n  --text: #e4e8f0;\n  --muted: #8892aa;\n  --pass: #34d399;\n  --fail: #f87171;\n  --header-h: 130px;\n  --sidebar-w: 260px;\n  --font-bump: 0px;\n  font-size: 15px;\n}\n\n*, *::before, *::after {\n  box-sizing: border-box;\n  margin: 0;\n  padding: 0;\n}\n\nbody {\n  background: var(--bg);\n  color: var(--text);\n  font-family: 'Inter', 'Segoe UI', system-ui, sans-serif;\n  font-size: calc(1rem + var(--font-bump));\n  min-height: 100vh;\n}\n\nbody.modal-open {\n  overflow: hidden;\n}\n\n/* ── Header ──────────────────────────────────────────────── */\n.site-header {\n  position: sticky;\n  top: 0;\n  z-index: 100;\n  background: var(--surface);\n  border-bottom: 1px solid var(--border);\n  padding: 14px 24px 10px;\n  backdrop-filter: blur(10px);\n}\n\n.header-inner {\n  display: flex;\n  align-items: center;\n  gap: 20px;\n  flex-wrap: wrap;\n}\n\n.brand {\n  display: flex;\n  align-items: center;\n  gap: 14px;\n  flex: 1 1 auto;\n}\n\n.logo {\n  width: 48px;\n  height: 48px;\n  flex-shrink: 0;\n}\n\n.brand-text h1 {\n  font-size: calc(1.4rem + var(--font-bump));\n  font-weight: 700;\n  letter-spacing: -0.3px;\n  color: var(--text);\n}\n\n.tagline {\n  font-size: calc(0.75rem + var(--font-bump));\n  color: var(--muted);\n  letter-spacing: 0.5px;\n  text-transform: uppercase;\n}\n\n.controls {\n  display: flex;\n  align-items: center;\n  gap: 14px;\n  flex-shrink: 0;\n}\n\n/* toggle */\n.toggle-label {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  cursor: pointer;\n  font-size: calc(0.85rem + var(--font-bump));\n  color: var(--muted);\n  user-select: none;\n}\n.toggle-label input { display: none; }\n.toggle-track {\n  position: relative;\n  width: 34px;\n  height: 18px;\n  background: var(--border);\n  border-radius: 9px;\n  transition: background 0.2s;\n}\n.toggle-thumb {\n  position: absolute;\n  top: 3px;\n  left: 3px;\n  width: 12px;\n  height: 12px;\n  background: var(--muted);\n  border-radius: 50%;\n  transition: left 0.2s, background 0.2s;\n}\n.toggle-label input:checked + .toggle-track,\n.settings-toggle-label input:checked + .toggle-track { background: var(--accent); }\n.toggle-label input:checked + .toggle-track .toggle-thumb,\n.settings-toggle-label input:checked + .toggle-track .toggle-thumb {\n  left: 19px;\n  background: #fff;\n}\n\n.select-wrap select {\n  background: var(--surface2);\n  color: var(--text);\n  border: 1px solid var(--border);\n  border-radius: 6px;\n  padding: 5px 10px;\n  font-size: calc(0.85rem + var(--font-bump));\n  cursor: pointer;\n  outline: none;\n  appearance: none;\n  padding-right: 28px;\n  background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%238892aa'/%3E%3C/svg%3E\");\n  background-repeat: no-repeat;\n  background-position: right 8px center;\n}\n\n.run-btn {\n  background: var(--accent);\n  color: #fff;\n  border: none;\n  border-radius: 6px;\n  padding: 6px 16px;\n  font-size: calc(0.85rem + var(--font-bump));\n  font-weight: 600;\n  cursor: pointer;\n  transition: opacity 0.15s;\n}\n.run-btn:hover { opacity: 0.85; }\n\n.secondary-btn,\n.icon-btn {\n  background: var(--surface2);\n  color: var(--text);\n  border: 1px solid var(--border);\n  border-radius: 6px;\n  padding: 6px 16px;\n  font-size: calc(0.85rem + var(--font-bump));\n  font-weight: 600;\n  cursor: pointer;\n  transition: border-color 0.15s, background 0.15s, opacity 0.15s;\n}\n\n.secondary-btn:hover,\n.icon-btn:hover {\n  border-color: var(--accent);\n  background: color-mix(in srgb, var(--accent) 12%, var(--surface2));\n}\n\n.icon-btn {\n  padding-inline: 12px;\n}\n\n.description {\n  margin-top: 8px;\n  font-size: calc(0.78rem + var(--font-bump));\n  color: var(--muted);\n  line-height: 1.5;\n  max-width: 900px;\n}\n.description code {\n  background: var(--surface2);\n  border: 1px solid var(--border);\n  border-radius: 3px;\n  padding: 1px 4px;\n  font-family: 'Fira Code', 'Cascadia Code', monospace;\n  font-size: calc(0.75rem + var(--font-bump));\n  color: var(--accent);\n}\n\n/* ── Layout ──────────────────────────────────────────────── */\n.page-body {\n  display: flex;\n  gap: 0;\n  min-height: calc(100vh - var(--header-h));\n}\n\n/* ── Sidebar ─────────────────────────────────────────────── */\n.sidebar {\n  width: var(--sidebar-w);\n  flex-shrink: 0;\n  padding: 20px 16px;\n  border-right: 1px solid var(--border);\n  position: sticky;\n  top: var(--header-h);\n  height: calc(100vh - var(--header-h));\n  overflow-y: auto;\n  display: flex;\n  flex-direction: column;\n  gap: 20px;\n}\n\n@media (min-width: 721px) {\n  .sidebar {\n    padding-top: 0;\n  }\n}\n\n.card-title {\n  font-size: calc(0.7rem + var(--font-bump));\n  font-weight: 600;\n  letter-spacing: 1px;\n  text-transform: uppercase;\n  color: var(--muted);\n  margin-bottom: 10px;\n}\n\n/* perf card */\n.perf-card {\n  background: var(--surface);\n  border: 1px solid var(--border);\n  border-radius: 10px;\n  padding: 14px;\n}\n\n#times {\n  width: 100%;\n  border-collapse: collapse;\n}\n\n#times th, #times td {\n  padding: 4px 6px;\n  font-size: calc(0.78rem + var(--font-bump));\n  border: none;\n  color: var(--text);\n}\n\n#times th { color: var(--muted); font-weight: 500; }\n#times td:not(:first-child) {\n  text-align: right;\n  font-variant-numeric: tabular-nums;\n  font-family: 'Fira Code', monospace;\n  font-size: calc(0.75rem + var(--font-bump));\n}\n#times tr { border-bottom: 1px solid var(--border); }\n#times tr:last-child { border-bottom: none; }\n\n/* category nav */\n.category-nav {\n  flex: 1;\n}\n#category-list {\n  list-style: none;\n  display: flex;\n  flex-direction: column;\n  gap: 2px;\n}\n#category-list li .category-link {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  width: 100%;\n  padding: 6px 10px;\n  border-radius: 6px;\n  border: none;\n  background: transparent;\n  text-align: left;\n  color: var(--muted);\n  font-size: calc(0.82rem + var(--font-bump));\n  transition: background 0.15s, color 0.15s;\n  cursor: pointer;\n}\n#category-list li .category-link:hover {\n  background: var(--surface2);\n  color: var(--text);\n}\n#category-list li .category-link.active {\n  background: color-mix(in srgb, var(--accent) 15%, transparent);\n  color: var(--accent);\n}\n.cat-badge {\n  font-size: calc(0.7rem + var(--font-bump));\n  background: var(--surface2);\n  border-radius: 10px;\n  padding: 1px 7px;\n  color: var(--muted);\n  font-variant-numeric: tabular-nums;\n}\n.cat-pass { color: var(--pass); }\n.cat-fail { color: var(--fail); }\n\n/* ── Main ────────────────────────────────────────────────── */\n.tests-main {\n  flex: 1;\n  padding: 20px 24px;\n  min-width: 0;\n}\n\n/* summary */\n.summary-bar {\n  display: flex;\n  gap: 16px;\n  margin-bottom: 20px;\n  flex-wrap: wrap;\n}\n.summary-chip {\n  background: var(--surface);\n  border: 1px solid var(--border);\n  border-radius: 8px;\n  padding: 8px 16px;\n  display: flex;\n  flex-direction: column;\n  gap: 2px;\n}\n.summary-chip .chip-val {\n  font-size: calc(1.4rem + var(--font-bump));\n  font-weight: 700;\n  font-variant-numeric: tabular-nums;\n}\n.summary-chip .chip-label {\n  font-size: calc(0.72rem + var(--font-bump));\n  color: var(--muted);\n  text-transform: uppercase;\n  letter-spacing: 0.5px;\n}\n.chip-pass .chip-val { color: var(--pass); }\n.chip-fail .chip-val { color: var(--fail); }\n\n/* sections */\n.test-section {\n  margin-bottom: 32px;\n}\n\n.section-header {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  margin-bottom: 10px;\n  cursor: pointer;\n  user-select: none;\n}\n\n.section-title {\n  font-size: 1.6rem;\n  font-weight: 600;\n  color: var(--text);\n}\n\n.section-count {\n  font-size: calc(0.75rem + var(--font-bump));\n  color: var(--muted);\n  background: var(--surface2);\n  border: 1px solid var(--border);\n  border-radius: 12px;\n  padding: 1px 9px;\n}\n\n.section-toggle {\n  margin-left: auto;\n  font-size: calc(0.7rem + var(--font-bump));\n  color: var(--muted);\n  transition: transform 0.2s;\n}\n.test-section.collapsed .section-toggle { transform: rotate(-90deg); }\n.test-section.collapsed .section-body { display: none; }\n\n.section-pass-rate {\n  font-size: calc(0.72rem + var(--font-bump));\n  padding: 2px 8px;\n  border-radius: 10px;\n  font-weight: 600;\n}\n.rate-good { background: color-mix(in srgb, var(--pass) 15%, transparent); color: var(--pass); }\n.rate-bad  { background: color-mix(in srgb, var(--fail) 15%, transparent); color: var(--fail); }\n\n/* table */\n.section-body {\n  border: 1px solid var(--border);\n  border-radius: 10px;\n  overflow: hidden;\n}\n\ntable.test-table {\n  width: 100%;\n  border-collapse: collapse;\n  table-layout: fixed;\n}\n\ntable.test-table col.col-code { width: 46%; }\ntable.test-table col.col-eval { width: 22%; }\ntable.test-table col.col-sandbox { width: 22%; }\ntable.test-table col.col-verdict { width: 10%; }\n\ntable.test-table thead th {\n  background: var(--surface2);\n  color: var(--muted);\n  font-size: calc(0.72rem + var(--font-bump));\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: 0.5px;\n  padding: 8px 12px;\n  text-align: left;\n  border-bottom: 1px solid var(--border);\n}\n\ntable.test-table tbody tr {\n  border-bottom: 1px solid var(--border);\n  transition: background 0.1s;\n}\ntable.test-table tbody tr:last-child { border-bottom: none; }\ntable.test-table tbody tr:hover { background: var(--surface2); }\n\ntable.test-table td {\n  padding: 7px 12px;\n  font-size: calc(0.82rem + var(--font-bump));\n  vertical-align: middle;\n}\n\ntd.td-code {\n  font-family: 'Fira Code', 'Cascadia Code', monospace;\n  font-size: calc(0.78rem + var(--font-bump));\n  color: var(--accent);\n  max-width: 340px;\n}\n\n.td-code-wrap {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  min-width: 0;\n}\n\n.td-code-text {\n  white-space: nowrap;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  flex: 1;\n}\n\n.runner-link {\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  width: 2.3rem;\n  height: 2.3rem;\n  flex-shrink: 0;\n  border-radius: 999px;\n  border: 1px solid var(--border);\n  background: linear-gradient(180deg, color-mix(in srgb, var(--surface2) 92%, white 8%), var(--surface2));\n  color: var(--accent);\n  text-decoration: none;\n  box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.05);\n  transition: transform 0.15s, background 0.15s, border-color 0.15s, color 0.15s, box-shadow 0.15s;\n}\n\n.runner-link-icon {\n  width: 1.45rem;\n  height: 1.45rem;\n}\n\n.runner-link:hover {\n  background: color-mix(in srgb, var(--accent) 12%, var(--surface2));\n  border-color: var(--accent);\n  color: #fff;\n  transform: translateY(-1px);\n  box-shadow: 0 6px 14px rgb(0 0 0 / 0.18);\n}\n\n.runner-link:focus-visible {\n  outline: none;\n  border-color: var(--accent);\n  box-shadow: 0 0 0 3px rgb(74 158 255 / 0.18);\n}\n\ntd.td-result {\n  font-family: 'Fira Code', 'Cascadia Code', monospace;\n  font-size: calc(0.75rem + var(--font-bump));\n  color: var(--text);\n  max-width: 180px;\n  white-space: nowrap;\n  overflow: hidden;\n  text-overflow: ellipsis;\n}\n\ntd.td-verdict {\n  font-weight: 700;\n  font-size: calc(0.78rem + var(--font-bump));\n  white-space: nowrap;\n  text-align: left;\n}\n\n.positive { color: var(--pass); }\n.negative { color: var(--fail); }\n\n.error {\n  cursor: help;\n  color: var(--fail);\n}\n\n.hidden {\n  display: none;\n}\n\n.modal-shell {\n  position: fixed;\n  inset: 0;\n  z-index: 300;\n}\n\n.modal-backdrop {\n  position: absolute;\n  inset: 0;\n  background: rgb(8 10 16 / 0.72);\n  backdrop-filter: blur(10px);\n}\n\n.sandbox-modal {\n  position: relative;\n  width: min(900px, calc(100vw - 32px));\n  max-height: calc(100vh - 48px);\n  margin: 24px auto;\n  background: linear-gradient(180deg, rgb(31 36 54 / 0.98), rgb(20 24 36 / 0.98));\n  border: 1px solid color-mix(in srgb, var(--accent) 22%, var(--border));\n  border-radius: 18px;\n  box-shadow: 0 30px 80px rgb(0 0 0 / 0.45);\n  overflow: hidden;\n  display: flex;\n  flex-direction: column;\n}\n\n.sandbox-modal-header {\n  display: flex;\n  align-items: flex-start;\n  justify-content: space-between;\n  gap: 16px;\n  padding: 20px 22px 16px;\n  border-bottom: 1px solid var(--border);\n}\n\n.sandbox-modal-header h2 {\n  font-size: calc(1.1rem + var(--font-bump));\n  margin-bottom: 4px;\n}\n\n.sandbox-modal-header p {\n  font-size: calc(0.82rem + var(--font-bump));\n  color: var(--muted);\n  line-height: 1.5;\n}\n\n.sandbox-modal-body {\n  padding: 20px 22px 22px;\n  overflow: auto;\n  display: grid;\n  gap: 18px;\n}\n\n.sandbox-field {\n  display: grid;\n  gap: 10px;\n}\n\n.sandbox-field-header {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n}\n\n.sandbox-field-label {\n  font-size: calc(0.76rem + var(--font-bump));\n  font-weight: 600;\n  letter-spacing: 0.8px;\n  text-transform: uppercase;\n  color: var(--muted);\n  flex: 1;\n}\n\n.settings-gear-btn {\n  position: relative;\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  width: 2rem;\n  height: 2rem;\n  border-radius: 6px;\n  border: 1px solid var(--border);\n  background: transparent;\n  color: var(--muted);\n  cursor: pointer;\n  transition: color 0.15s, border-color 0.15s, background 0.15s;\n  flex-shrink: 0;\n}\n\n.settings-gear-btn svg {\n  width: 1.1rem;\n  height: 1.1rem;\n}\n\n.settings-gear-btn:hover,\n.settings-gear-btn.active {\n  color: var(--accent);\n  border-color: var(--accent);\n  background: color-mix(in srgb, var(--accent) 10%, transparent);\n}\n\n.settings-badge {\n  position: absolute;\n  top: -4px;\n  right: -4px;\n  min-width: 14px;\n  height: 14px;\n  background: var(--accent);\n  color: #fff;\n  font-size: 0.6rem;\n  font-weight: 700;\n  border-radius: 7px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  padding: 0 3px;\n  line-height: 1;\n}\n.settings-badge.hidden {\n  display: none;\n}\n\n.sandbox-settings-panel {\n  background: rgb(10 13 22 / 0.82);\n  border: 1px solid var(--border);\n  border-radius: 12px;\n  overflow: hidden;\n}\n\n.settings-panel-inner {\n  padding: 14px 16px;\n  display: grid;\n  gap: 14px;\n}\n\n.settings-section {\n  display: grid;\n  gap: 8px;\n}\n\n.settings-section-label {\n  font-size: calc(0.7rem + var(--font-bump));\n  font-weight: 600;\n  letter-spacing: 0.8px;\n  text-transform: uppercase;\n  color: var(--muted);\n}\n\n.settings-grid {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 10px 20px;\n}\n\n.settings-toggle-label {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  cursor: pointer;\n  font-size: calc(0.82rem + var(--font-bump));\n  color: var(--text);\n  user-select: none;\n  font-family: 'Fira Code', 'Cascadia Code', monospace;\n}\n.settings-toggle-label input { display: none; }\n\n.settings-field-row {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  flex-wrap: wrap;\n}\n\n.settings-input {\n  background: var(--surface2);\n  border: 1px solid var(--border);\n  border-radius: 6px;\n  color: var(--text);\n  font-family: 'Fira Code', 'Cascadia Code', monospace;\n  font-size: calc(0.82rem + var(--font-bump));\n  padding: 5px 10px;\n  width: 160px;\n  outline: none;\n  transition: border-color 0.15s;\n  appearance: textfield;\n  -moz-appearance: textfield;\n}\n.settings-input::-webkit-outer-spin-button,\n.settings-input::-webkit-inner-spin-button { -webkit-appearance: none; }\n.settings-input:focus { border-color: var(--accent); }\n.settings-input.settings-field-error { border-color: var(--fail); }\n\n.settings-textarea {\n  background: var(--surface2);\n  border: 1px solid var(--border);\n  border-radius: 6px;\n  color: var(--text);\n  font-family: 'Fira Code', 'Cascadia Code', monospace;\n  font-size: calc(0.82rem + var(--font-bump));\n  padding: 7px 10px;\n  width: 100%;\n  resize: vertical;\n  outline: none;\n  transition: border-color 0.15s;\n  line-height: 1.5;\n}\n.settings-textarea:focus { border-color: var(--accent); }\n.settings-textarea.settings-field-error { border-color: var(--fail); }\n\n.settings-field-hint {\n  font-size: calc(0.72rem + var(--font-bump));\n  color: var(--muted);\n}\n\n.settings-reset-btn {\n  align-self: start;\n  background: transparent;\n  color: var(--muted);\n  border: 1px solid var(--border);\n  border-radius: 6px;\n  padding: 4px 12px;\n  font-size: calc(0.78rem + var(--font-bump));\n  cursor: pointer;\n  transition: color 0.15s, border-color 0.15s;\n}\n.settings-reset-btn:hover {\n  color: var(--fail);\n  border-color: var(--fail);\n}\n\n#sandbox-editor {\n  border: 1px solid var(--border);\n  border-radius: 12px;\n  overflow: hidden;\n  transition: border-color 0.15s, box-shadow 0.15s;\n}\n\n#sandbox-editor:focus-within {\n  border-color: var(--accent);\n  box-shadow: 0 0 0 3px rgb(74 158 255 / 0.18);\n}\n\n#sandbox-editor .cm-editor {\n  background: rgb(12 15 24 / 0.92);\n}\n\n#sandbox-editor .cm-scroller {\n  min-height: 240px;\n}\n\n.sandbox-actions {\n  display: flex;\n  align-items: center;\n  gap: 12px;\n  flex-wrap: wrap;\n}\n\n.sandbox-ticks {\n  font-size: calc(0.78rem + var(--font-bump));\n  color: var(--muted);\n  font-family: 'Fira Code', 'Cascadia Code', monospace;\n  white-space: nowrap;\n}\n\n.sandbox-ticks::before {\n  content: 'execution ticks: ';\n  opacity: 0.55;\n}\n\n.sandbox-status {\n  margin-right: auto;\n  font-size: calc(0.78rem + var(--font-bump));\n  font-weight: 700;\n  letter-spacing: 0.4px;\n  text-transform: uppercase;\n  color: var(--muted);\n}\n\n.sandbox-status.status-running {\n  color: var(--accent2);\n}\n\n.sandbox-status.status-success {\n  color: var(--pass);\n}\n\n.sandbox-status.status-error {\n  color: var(--fail);\n}\n\n.sandbox-status.status-halted {\n  color: #f97316;\n}\n\n.sandbox-notice {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  border-radius: 12px;\n  padding: 12px 14px;\n  border: 1px solid var(--border);\n  font-size: calc(0.82rem + var(--font-bump));\n  line-height: 1.45;\n}\n\n.sandbox-notice strong {\n  font-size: calc(1rem + var(--font-bump));\n}\n\n.sandbox-notice-safe {\n  background: color-mix(in srgb, var(--pass) 14%, rgb(10 13 22 / 0.78));\n  border-color: color-mix(in srgb, var(--pass) 35%, var(--border));\n  color: var(--pass);\n}\n\n.sandbox-notice-critical {\n  background: color-mix(in srgb, var(--fail) 15%, rgb(10 13 22 / 0.78));\n  border-color: color-mix(in srgb, var(--fail) 40%, var(--border));\n  color: #ffb2b2;\n}\n\n.sandbox-output-grid {\n  display: grid;\n  grid-template-columns: repeat(2, minmax(0, 1fr));\n  gap: 16px;\n}\n\n.output-card {\n  background: rgb(10 13 22 / 0.78);\n  border: 1px solid var(--border);\n  border-radius: 12px;\n  overflow: hidden;\n}\n\n.output-card h3 {\n  font-size: calc(0.78rem + var(--font-bump));\n  letter-spacing: 0.6px;\n  text-transform: uppercase;\n  color: var(--muted);\n  padding: 12px 14px;\n  border-bottom: 1px solid var(--border);\n}\n\n.output-card pre {\n  min-height: 100px;\n  padding: 14px;\n  white-space: pre-wrap;\n  word-break: break-word;\n  font-family: 'Fira Code', 'Cascadia Code', monospace;\n  font-size: calc(0.8rem + var(--font-bump));\n  line-height: 1.55;\n  color: var(--text);\n}\n\n@media (max-width: 900px) {\n  .sandbox-output-grid {\n    grid-template-columns: 1fr;\n  }\n}\n\n@media (max-width: 720px) {\n  .controls {\n    width: 100%;\n    flex-wrap: wrap;\n  }\n\n  .page-body {\n    flex-direction: column;\n  }\n\n  .sidebar {\n    position: static;\n    width: 100%;\n    height: auto;\n    border-right: none;\n    border-bottom: 1px solid var(--border);\n  }\n\n  .tests-main {\n    padding-inline: 16px;\n  }\n\n  .sandbox-modal {\n    width: calc(100vw - 20px);\n    max-height: calc(100vh - 20px);\n    margin: 10px auto;\n  }\n\n  .sandbox-modal-header,\n  .sandbox-actions {\n    flex-direction: column;\n    align-items: stretch;\n  }\n\n  .sandbox-status {\n    margin-right: 0;\n  }\n}\n\n/* scrollbar */\n::-webkit-scrollbar { width: 6px; height: 6px; }\n::-webkit-scrollbar-track { background: transparent; }\n::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }\n::-webkit-scrollbar-thumb:hover { background: var(--muted); }\n"
  },
  {
    "path": "eslint.config.js",
    "content": "const eslint = require('@eslint/js');\nconst tseslint = require('@typescript-eslint/eslint-plugin');\nconst tsparser = require('@typescript-eslint/parser');\nconst globals = require('globals');\nconst prettierConfig = require('eslint-config-prettier');\n\nmodule.exports = [\n  // Global ignores\n  {\n    ignores: [\n      'node_modules/**',\n      'dist/**',\n      'build/**',\n      'test/**',\n      'coverage/**',\n      '*.min.js',\n      'test/eval/jquery.min.js',\n      'rollup.config.mjs',\n      'jest.config.js',\n      'eslint.config.js'\n    ]\n  },\n  // Base config for all TypeScript files\n  {\n    files: ['**/*.ts'],\n    languageOptions: {\n      parser: tsparser,\n      parserOptions: {\n        ecmaVersion: 'latest',\n        sourceType: 'module',\n        project: './tsconfig.json'\n      },\n      globals: {\n        ...globals.browser,\n        ...globals.node,\n        ...globals.es2021\n      }\n    },\n    plugins: {\n      '@typescript-eslint': tseslint\n    },\n    rules: {\n      ...eslint.configs.recommended.rules,\n      ...tseslint.configs.recommended.rules,\n      ...prettierConfig.rules,\n      '@typescript-eslint/no-explicit-any': 'warn',\n      '@typescript-eslint/no-empty-object-type': 'warn',\n      '@typescript-eslint/no-unused-expressions': 'warn',\n      '@typescript-eslint/no-unsafe-function-type': 'off',\n      'no-fallthrough': 'off',\n      '@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^lastLastLastLastPart' }],\n      '@typescript-eslint/no-unsafe-assignment': 'off',\n      '@typescript-eslint/no-this-alias': 'off'\n    }\n  },\n  // Scripts that are part of the test tsconfig\n  {\n    files: ['scripts/export-tests.ts'],\n    languageOptions: {\n      parser: tsparser,\n      parserOptions: {\n        ecmaVersion: 'latest',\n        sourceType: 'module',\n        project: './test/tsconfig.json'\n      },\n      globals: {\n        ...globals.node,\n        ...globals.es2021\n      }\n    },\n    plugins: {\n      '@typescript-eslint': tseslint\n    },\n    rules: {\n      ...eslint.configs.recommended.rules,\n      ...tseslint.configs.recommended.rules,\n      ...prettierConfig.rules,\n      '@typescript-eslint/no-explicit-any': 'warn',\n      '@typescript-eslint/no-empty-object-type': 'warn',\n      '@typescript-eslint/no-unused-expressions': 'warn',\n      '@typescript-eslint/no-unsafe-function-type': 'off',\n      'no-fallthrough': 'off',\n      '@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^lastLastLastLastPart' }],\n      '@typescript-eslint/no-unsafe-assignment': 'off',\n    }\n  }\n];\n"
  },
  {
    "path": "index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>SandboxJS — Test Suite</title>\n  <link rel=\"shortcut icon\" href=\"logo.svg\" type=\"image/x-icon\">\n  <script src=\"https://cdn.jsdelivr.net/npm/lodash@4.17.20/lodash.min.js\"></script>\n  <script src=\"test/eval/script.js\" type=\"module\"></script>\n  <link rel=\"stylesheet\" href=\"css/style.css\">\n</head>\n<body>\n  <header class=\"site-header\">\n    <div class=\"header-inner\">\n      <div class=\"brand\">\n        <img src=\"logo.svg\" alt=\"SandboxJS logo\" class=\"logo\">\n        <div class=\"brand-text\">\n          <h1>SandboxJS</h1>\n          <span class=\"tagline\">Safe JavaScript eval runtime</span>\n        </div>\n      </div>\n      <div class=\"controls\">\n        <label class=\"toggle-label\">\n          <input id=\"jit-parsing\" type=\"checkbox\" checked>\n          <span class=\"toggle-track\"><span class=\"toggle-thumb\"></span></span>\n          JIT Parsing\n        </label>\n        <div class=\"select-wrap\">\n          <select id=\"runtime-type\">\n            <option value=\"sync\" selected>Sync</option>\n            <option value=\"async\">Async</option>\n          </select>\n        </div>\n        <button id=\"open-sandbox-modal-btn\" class=\"secondary-btn\">Sandbox Runner</button>\n        <button id=\"run-btn\" class=\"run-btn\">Run Tests</button>\n      </div>\n    </div>\n    <p class=\"description\">\n      Tests compare SandboxJS against a proxied <code>eval</code>. A global <code>bypassed</code> flag is set to <code>true</code> if a test escapes the sandbox — highlighted in red. Blocked bypasses show as <em>Error</em>.\n    </p>\n  </header>\n\n  <div class=\"page-body\">\n    <aside class=\"sidebar\">\n      <div class=\"perf-card\">\n        <h2 class=\"card-title\">Performance</h2>\n        <table id=\"times\">\n          <tbody></tbody>\n        </table>\n      </div>\n      <nav class=\"category-nav\" id=\"category-nav\">\n        <h2 class=\"card-title\">Sections</h2>\n        <ul id=\"category-list\"></ul>\n      </nav>\n    </aside>\n\n    <main class=\"tests-main\" id=\"tests-main\">\n      <div class=\"summary-bar\" id=\"summary-bar\"></div>\n      <!-- sections injected here by script.js -->\n    </main>\n  </div>\n\n  <div id=\"sandbox-modal\" class=\"modal-shell hidden\" aria-hidden=\"true\">\n    <div class=\"modal-backdrop\" data-close-modal=\"true\"></div>\n    <div class=\"sandbox-modal\" role=\"dialog\" aria-modal=\"true\" aria-labelledby=\"sandbox-modal-title\">\n      <div class=\"sandbox-modal-header\">\n        <div>\n          <h2 id=\"sandbox-modal-title\">Sandbox Runner</h2>\n          <p>Write JavaScript, keep it in the URL hash, and run it only when you press the button.</p>\n        </div>\n        <button id=\"sandbox-close-btn\" class=\"icon-btn\" type=\"button\" aria-label=\"Close sandbox runner\">Close</button>\n      </div>\n\n      <div class=\"sandbox-modal-body\">\n        <div class=\"sandbox-field\">\n          <div class=\"sandbox-field-header\">\n            <span class=\"sandbox-field-label\">JavaScript Input</span>\n            <button id=\"sandbox-settings-btn\" class=\"settings-gear-btn\" type=\"button\" aria-label=\"Sandbox settings\" title=\"Sandbox settings\">\n              <svg viewBox=\"0 0 20 20\" aria-hidden=\"true\" focusable=\"false\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n                <circle cx=\"10\" cy=\"10\" r=\"2.8\"/>\n                <path d=\"M10 2.5v1.2M10 16.3v1.2M2.5 10h1.2M16.3 10h1.2M4.6 4.6l.85.85M14.55 14.55l.85.85M4.6 15.4l.85-.85M14.55 5.45l.85-.85\"/>\n              </svg>\n              <span id=\"sandbox-settings-badge\" class=\"settings-badge hidden\"></span>\n            </button>\n          </div>\n          <div id=\"sandbox-settings-panel\" class=\"sandbox-settings-panel hidden\">\n            <div class=\"settings-panel-inner\">\n              <div class=\"settings-section\">\n                <span class=\"settings-section-label\">Options</span>\n                <div class=\"settings-grid\">\n                  <label class=\"settings-toggle-label\">\n                    <input type=\"checkbox\" id=\"setting-forbid-calls\">\n                    <span class=\"toggle-track\"><span class=\"toggle-thumb\"></span></span>\n                    forbidFunctionCalls\n                  </label>\n                  <label class=\"settings-toggle-label\">\n                    <input type=\"checkbox\" id=\"setting-forbid-creation\">\n                    <span class=\"toggle-track\"><span class=\"toggle-thumb\"></span></span>\n                    forbidFunctionCreation\n                  </label>\n                  <label class=\"settings-toggle-label\">\n                    <input type=\"checkbox\" id=\"setting-halt-on-error\">\n                    <span class=\"toggle-track\"><span class=\"toggle-thumb\"></span></span>\n                    haltOnSandboxError\n                  </label>\n                  <label class=\"settings-toggle-label\">\n                    <input type=\"checkbox\" id=\"setting-async\">\n                    <span class=\"toggle-track\"><span class=\"toggle-thumb\"></span></span>\n                    async\n                  </label>\n                </div>\n              </div>\n              <div class=\"settings-section\">\n                <span class=\"settings-section-label\">Execution Quota</span>\n                <div class=\"settings-field-row\">\n                  <input type=\"number\" id=\"setting-quota\" class=\"settings-input\" placeholder=\"unlimited\" min=\"0\" step=\"1000\">\n                  <span class=\"settings-field-hint\">ticks (blank = unlimited)</span>\n                </div>\n              </div>\n              <div class=\"settings-section\">\n                <span class=\"settings-section-label\">Scope / State</span>\n                <div class=\"settings-field-row\">\n                  <textarea id=\"setting-scope\" class=\"settings-textarea\" placeholder='{ \"myVar\": 42 }' rows=\"3\" spellcheck=\"false\"></textarea>\n                </div>\n                <span class=\"settings-field-hint\">JSON object merged into sandbox scope</span>\n              </div>\n              <button id=\"sandbox-settings-reset-btn\" class=\"settings-reset-btn\" type=\"button\">Reset to defaults</button>\n            </div>\n          </div>\n          <div id=\"sandbox-editor\"></div>\n        </div>\n\n        <div class=\"sandbox-actions\">\n          <span id=\"sandbox-status\" class=\"sandbox-status\">Idle</span>\n          <span id=\"sandbox-ticks-output\" class=\"sandbox-ticks\">—</span>\n          <button id=\"sandbox-clear-btn\" class=\"secondary-btn\" type=\"button\">Clear</button>\n          <button id=\"sandbox-exec-btn\" class=\"run-btn\" type=\"button\">Run In Sandbox</button>\n        </div>\n\n        <div id=\"sandbox-bypass-notice\" class=\"sandbox-notice sandbox-notice-safe\">\n          <strong id=\"sandbox-bypass-icon\" aria-hidden=\"true\">✓</strong>\n          <span id=\"sandbox-bypass-text\">`globalThis.bypassed` is falsy.</span>\n        </div>\n\n        <div class=\"sandbox-output-grid\">\n          <section class=\"output-card\">\n            <h3>Console</h3>\n            <pre id=\"sandbox-console-output\">No logs yet.</pre>\n          </section>\n          <section class=\"output-card\">\n            <h3>Return Value</h3>\n            <pre id=\"sandbox-return-output\">Run code to inspect the result.</pre>\n          </section>\n        </div>\n      </div>\n    </div>\n  </div>\n</body>\n</html>\n"
  },
  {
    "path": "jest.config.js",
    "content": "/**\n * For a detailed explanation regarding each configuration property, visit:\n * https://jestjs.io/docs/configuration\n */\n\n/** @type {import('jest').Config} */\nexport default {\n  // All imported modules in your tests should be mocked automatically\n  // automock: false,\n\n  // Stop running tests after `n` failures\n  // bail: 0,\n\n  // The directory where Jest should store its cached dependency information\n  // cacheDirectory: \"C:\\\\Users\\\\user\\\\AppData\\\\Local\\\\Temp\\\\jest\",\n\n  // Automatically clear mock calls, instances, contexts and results before every test\n  // clearMocks: false,\n\n  // Indicates whether the coverage information should be collected while executing the test\n  collectCoverage: true,\n\n  // An array of glob patterns indicating a set of files for which coverage information should be collected\n  // collectCoverageFrom: undefined,\n\n  // The directory where Jest should output its coverage files\n  coverageDirectory: \"coverage\",\n\n  // An array of regexp pattern strings used to skip coverage collection\n  coveragePathIgnorePatterns: [\n    \"/node_modules/\",\n    \"/test/eval/testCases/.*\\\\.data\\\\.ts$\",\n    \"/test/eval/testCases/types\\\\.ts$\",\n    \"/test/eval/testCases/test-utils\\\\.ts$\",\n    \"/test/eval/testCases/index\\\\.ts$\",\n    \"/test/eval/export-tests\\\\.ts$\",\n    \"/test/eval/tests\\\\.json$\"\n  ],\n\n  // Indicates which provider should be used to instrument code for coverage\n  coverageProvider: \"v8\",\n\n  // A list of reporter names that Jest uses when writing coverage reports\n  // coverageReporters: [\n  //   \"json\",\n  //   \"text\",\n  //   \"lcov\",\n  //   \"clover\"\n  // ],\n\n  // An object that configures minimum threshold enforcement for coverage results\n  // coverageThreshold: undefined,\n\n  // A path to a custom dependency extractor\n  // dependencyExtractor: undefined,\n\n  // Make calling deprecated APIs throw helpful error messages\n  // errorOnDeprecated: false,\n\n  // The default configuration for fake timers\n  // fakeTimers: {\n  //   \"enableGlobally\": false\n  // },\n\n  // Force coverage collection from ignored files using an array of glob patterns\n  // forceCoverageMatch: [],\n\n  // A path to a module which exports an async function that is triggered once before all test suites\n  // globalSetup: undefined,\n\n  // A path to a module which exports an async function that is triggered once after all test suites\n  // globalTeardown: undefined,\n\n  // A set of global variables that need to be available in all test environments\n  // globals: {},\n\n  // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.\n  maxWorkers: '50%',\n\n  // An array of directory names to be searched recursively up from the requiring module's location\n  // moduleDirectories: [\n  //   \"node_modules\"\n  // ],\n\n  // An array of file extensions your modules use\n  moduleFileExtensions: [\n    \"js\",\n    \"mjs\",\n    \"cjs\",\n    \"jsx\",\n    \"ts\",\n    \"tsx\",\n    \"json\",\n    \"node\"\n  ],\n\n  // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module\n  moduleNameMapper: {\n    '^(\\\\.{1,2}/.*)\\\\.js$': '$1', // fixes path issues\n  },\n\n  // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader\n  // modulePathIgnorePatterns: [],\n\n  // Activates notifications for test results\n  // notify: false,\n\n  // An enum that specifies notification mode. Requires { notify: true }\n  // notifyMode: \"failure-change\",\n\n  // A preset that is used as a base for Jest's configuration\n  preset: 'ts-jest/presets/default-esm',\n\n  // Run tests from one or more projects\n  // projects: undefined,\n\n  // Use this configuration option to add custom reporters to Jest\n  // reporters: undefined,\n\n  // Automatically reset mock state before every test\n  // resetMocks: false,\n\n  // Reset the module registry before running each individual test\n  // resetModules: false,\n\n  // A path to a custom resolver\n  // resolver: undefined,\n\n  // Automatically restore mock state and implementation before every test\n  // restoreMocks: false,\n\n  // The root directory that Jest should scan for tests and modules within\n  // rootDir: undefined,\n\n  // A list of paths to directories that Jest should use to search for files in\n  // roots: [\n  //   \"<rootDir>\"\n  // ],\n\n  // Allows you to use a custom runner instead of Jest's default test runner\n  // runner: \"jest-runner\",\n\n  // The paths to modules that run some code to configure or set up the testing environment before each test\n  // setupFiles: [],\n\n  // A list of paths to modules that run some code to configure or set up the testing framework before each test\n  // setupFilesAfterEnv: [],\n\n  // The number of seconds after which a test is considered as slow and reported as such in the results.\n  // slowTestThreshold: 5,\n\n  // A list of paths to snapshot serializer modules Jest should use for snapshot testing\n  // snapshotSerializers: [],\n\n  // The test environment that will be used for testing\n  testEnvironment: 'node',\n\n  // Options that will be passed to the testEnvironment\n  // testEnvironmentOptions: {},\n\n  // Adds a location field to test results\n  // testLocationInResults: false,\n\n  // The glob patterns Jest uses to detect test files\n  // testMatch: [\n  //   \"**/__tests__/**/*.[jt]s?(x)\",\n  //   \"**/?(*.)+(spec|test).[tj]s?(x)\"\n  // ],\n\n  // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped\n  // testPathIgnorePatterns: [\n  //   \"\\\\\\\\node_modules\\\\\\\\\"\n  // ],\n\n  // The regexp pattern or array of patterns that Jest uses to detect test files\n  // testRegex: [],\n\n  // This option allows the use of a custom results processor\n  // testResultsProcessor: undefined,\n\n  // This option allows use of a custom test runner\n  // testRunner: \"jest-circus/runner\",\n\n  // A map from regular expressions to paths to transformers\n  transform: {\n    '^.+\\.tsx?$': ['ts-jest', { \n      useESM: true, \n      tsconfig: 'tsconfig.jest.json',\n    }],\n  },\n\n  // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation\n  // transformIgnorePatterns: [\n  //   \"\\\\\\\\node_modules\\\\\\\\\",\n  //   \"\\\\.pnp\\\\.[^\\\\\\\\]+$\"\n  // ],\n\n  // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them\n  // unmockedModulePathPatterns: undefined,\n\n  // Indicates whether each individual test should be reported during the run\n  // verbose: undefined,\n\n  // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode\n  // watchPathIgnorePatterns: [],\n\n  // Whether to use watchman for file crawling\n  // watchman: true,\n};\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"@nyariv/sandboxjs\",\n  \"version\": \"0.9.6\",\n  \"description\": \"Javascript sandboxing library.\",\n  \"main\": \"dist/cjs/Sandbox.js\",\n  \"module\": \"./dist/esm/Sandbox.js\",\n  \"browser\": \"./dist/umd/Sandbox.min.js\",\n  \"types\": \"./dist/esm/Sandbox.d.ts\",\n  \"exports\": {\n    \".\": {\n      \"types\": \"./dist/esm/Sandbox.d.ts\",\n      \"import\": \"./dist/esm/Sandbox.js\",\n      \"require\": \"./dist/cjs/Sandbox.js\",\n      \"default\": \"./dist/esm/Sandbox.js\"\n    },\n    \"./SandboxExec\": {\n      \"types\": \"./dist/esm/SandboxExec.d.ts\",\n      \"import\": \"./dist/esm/SandboxExec.js\",\n      \"require\": \"./dist/cjs/SandboxExec.js\",\n      \"default\": \"./dist/esm/SandboxExec.js\"\n    },\n    \"./package.json\": \"./package.json\"\n  },\n  \"scripts\": {\n    \"test\": \"NODE_OPTIONS='--no-warnings=ExperimentalWarning' jest\",\n    \"test:perf\": \"NODE_OPTIONS='--no-warnings=ExperimentalWarning' node --expose-gc test/performance.mjs\",\n    \"build\": \"node scripts/build.mjs\",\n    \"lint\": \"prettier --check \\\"**/*.+(ts|json)\\\" && eslint --ext .ts .\",\n    \"lint:fix\": \"prettier --write \\\"**/*.+(ts|json)\\\" && eslint --ext .ts --fix .\",\n    \"patch\": \"npm version patch\",\n    \"prepare\": \"husky\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/nyariv/SandboxJS.git\"\n  },\n  \"author\": \"\",\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/nyariv/SandboxJS/issues\"\n  },\n  \"homepage\": \"https://github.com/nyariv/SandboxJS#readme\",\n  \"devDependencies\": {\n    \"@types/jest\": \"^30.0.0\",\n    \"@typescript-eslint/eslint-plugin\": \"^8.53.0\",\n    \"@typescript-eslint/parser\": \"^8.53.0\",\n    \"chalk\": \"^5.6.2\",\n    \"cli-table3\": \"^0.6.5\",\n    \"eslint\": \"^9.39.2\",\n    \"eslint-config-prettier\": \"^10.1.8\",\n    \"husky\": \"^9.1.7\",\n    \"jest\": \"^30.2.0\",\n    \"lint-staged\": \"^16.2.7\",\n    \"node-fetch\": \"^3.3.2\",\n    \"prettier\": \"^3.8.0\",\n    \"terser\": \"^5.46.1\",\n    \"tinybench\": \"^6.0.0\",\n    \"ts-jest\": \"^29.4.6\",\n    \"tslib\": \"^2.8.1\",\n    \"typescript\": \"^5.9.3\",\n    \"vite\": \"^8.0.8\",\n    \"vite-plugin-dts\": \"^4.5.4\"\n  },\n  \"lint-staged\": {\n    \"*.ts\": [\n      \"prettier --write\",\n      \"eslint --fix\"\n    ],\n    \"*.json\": [\n      \"prettier --write\"\n    ]\n  }\n}\n"
  },
  {
    "path": "scripts/build.mjs",
    "content": "import { resolve, dirname } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { rmSync, writeFileSync } from 'node:fs'\nimport { execSync } from 'node:child_process'\nimport { build } from 'vite'\nimport dts from 'vite-plugin-dts'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\nconst root = resolve(__dirname, '..')\n\ntry {\n  execSync('npx tsc --noEmit', { cwd: root, stdio: 'inherit' })\n} catch {\n  process.exit(1)\n}\n\nrmSync(resolve(root, 'dist'), { recursive: true, force: true })\n\nconst entries = {\n  Sandbox: resolve(root, 'src/Sandbox.ts'),\n  SandboxExec: resolve(root, 'src/SandboxExec.ts'),\n}\n\nconst minifyOptions = {\n  minify: 'terser',\n  terserOptions: {\n    keep_fnames: /^Sandbox(Symbol|(Async)?(Generator)?(Function|Global)?)$/,\n  },\n}\n\nfunction writeModuleTypeManifest(outDir, type) {\n  writeFileSync(resolve(root, outDir, 'package.json'), `${JSON.stringify({ type }, null, 2)}\\n`)\n}\n\n// 1. CJS build → dist/node/\nawait build({\n  root,\n  build: {\n    outDir: 'dist/cjs',\n    minify: false,\n    lib: {\n      entry: entries,\n      formats: ['cjs'],\n    },\n    rollupOptions: {\n      output: {\n        preserveModules: true,\n        entryFileNames: '[name].js',\n        exports: 'named',\n      },\n    },\n  },\n  plugins: [\n    dts({\n      outDir: 'dist/cjs',\n      include: ['src'],\n      tsconfigPath: resolve(root, 'tsconfig.json'),\n    }),\n  ],\n})\n\nwriteModuleTypeManifest('dist/cjs', 'commonjs')\n\n// 2. UMD build → dist/Sandbox.umd.js\nawait build({\n  root,\n  build: {\n    outDir: 'dist/umd',\n    emptyOutDir: false,\n    sourcemap: true,\n    lib: {\n      entry: resolve(root, 'src/Sandbox.ts'),\n      name: 'Sandbox',\n      formats: ['umd'],\n      fileName: () => 'Sandbox.min.js',\n    },\n    rollupOptions: {\n      output: { exports: 'named' },\n    },\n    ...minifyOptions,\n  },\n})\n\n// 3. UMD build → dist/SandboxExec.umd.js\nawait build({\n  root,\n  build: {\n    outDir: 'dist/umd',\n    emptyOutDir: false,\n    sourcemap: true,\n    lib: {\n      entry: resolve(root, 'src/SandboxExec.ts'),\n      name: 'SandboxExec',\n      formats: ['umd'],\n      fileName: () => 'SandboxExec.min.js',\n    },\n    rollupOptions: {\n      output: { exports: 'named' },\n    },\n    ...minifyOptions,\n  },\n})\n\n// 4. ESM build → dist/esm/\nawait build({\n  root,\n  build: {\n    outDir: 'dist/esm',\n    minify: false,\n    sourcemap: true,\n    lib: {\n      entry: entries,\n      formats: ['es'],\n    },\n    rollupOptions: {\n      output: {\n        preserveModules: true,\n        entryFileNames: '[name].js',\n        exports: 'named',\n      },\n    },\n  },\n  plugins: [\n    dts({\n      outDir: 'dist/esm',\n      include: ['src'],\n      tsconfigPath: resolve(root, 'tsconfig.json'),\n    }),\n  ],\n})\n\nwriteModuleTypeManifest('dist/esm', 'module')\n"
  },
  {
    "path": "scripts/export-tests.ts",
    "content": "import * as fs from 'fs';\nimport * as path from 'path';\nimport { fileURLToPath } from 'url';\nimport { TestCase } from '../test/eval/testCases/types';\nimport * as allTestModules from '../test/eval/testCases/index';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n// Collect all tests from imported modules\nasync function extractTests() {\n  const allTests: TestCase[] = [];\n\n  // Get all test arrays from the imported modules\n  for (const [moduleName, tests] of Object.entries(allTestModules)) {\n    if (Array.isArray(tests)) {\n      allTests.push(...tests);\n      console.log(`Extracted ${tests.length} tests from ${moduleName}`);\n    }\n  }\n\n  // Group tests by category, preserving order within each category\n  const testsByCategory: Record<string, TestCase[]> = {};\n  const categoryOrder: string[] = [];\n\n  allTests.forEach((test) => {\n    const category = test.category;\n    if (!testsByCategory[category]) {\n      testsByCategory[category] = [];\n      categoryOrder.push(category);\n    }\n    testsByCategory[category].push(test);\n  });\n\n  // Build final array: Data Types first, Security second, rest in original order\n  const sortedTests: TestCase[] = [];\n\n  // Add Data Types first\n  if (testsByCategory['Data Types']) {\n    sortedTests.push(...testsByCategory['Data Types']);\n  }\n\n  // Add Security second\n  if (testsByCategory['Security']) {\n    sortedTests.push(...testsByCategory['Security']);\n  }\n\n  // Add all other categories in the order they appeared\n  categoryOrder.forEach((category) => {\n    if (category !== 'Data Types' && category !== 'Security') {\n      sortedTests.push(...testsByCategory[category]);\n    }\n  });\n\n  // Write to tests.json\n  const outputPath = path.join(__dirname, '../test/eval', 'tests.json');\n  const jsonContent = JSON.stringify(sortedTests, null, 2);\n  // Ensure consistent LF line endings\n  const normalizedContent = jsonContent.replace(/\\r\\n/g, '\\n').replace(/\\r/g, '\\n');\n  fs.writeFileSync(outputPath, normalizedContent + '\\n', 'utf8');\n\n  console.log(`\\nTotal tests: ${sortedTests.length}`);\n  console.log(`Exported to: ${outputPath}`);\n\n  // Show category distribution\n  const categories: Record<string, number> = {};\n  sortedTests.forEach((test) => {\n    categories[test.category] = (categories[test.category] || 0) + 1;\n  });\n\n  console.log('\\nTests per category:');\n  Object.entries(categories).forEach(([cat, count]) => {\n    console.log(`  ${cat}: ${count}`);\n  });\n}\n\nextractTests().catch((err) => {\n  console.error('Error:', err);\n  process.exit(1);\n});\n"
  },
  {
    "path": "src/Sandbox.ts",
    "content": "import { AsyncFunction, createExecContext, SandboxCapabilityError, sanitizeScopes } from './utils';\nimport type { IExecContext, IOptionParams, IScope } from './utils';\nimport { createEvalContext } from './eval';\nimport { ExecReturn } from './executor';\nimport parse from './parser';\nimport SandboxExec from './SandboxExec';\nexport { ParseError } from './parser';\nexport {\n  LocalScope,\n  SandboxExecutionTreeError,\n  SandboxCapabilityError,\n  SandboxAccessError,\n  SandboxExecutionQuotaExceededError,\n  SandboxError,\n  delaySynchronousResult,\n} from './utils';\n\nexport type * from './utils';\nexport type * from './parser';\nexport type * from './executor';\nexport type * from './eval';\n\nexport class Sandbox extends SandboxExec {\n  constructor(options?: IOptionParams) {\n    super(options, createEvalContext());\n  }\n\n  static audit<T>(code: string, scopes: IScope[] = []): ExecReturn<T> {\n    const globals: Record<string, unknown> = {};\n    for (const i of Object.getOwnPropertyNames(globalThis) as [keyof typeof globalThis]) {\n      globals[i] = globalThis[i];\n    }\n    const sandbox = new SandboxExec({\n      globals,\n      audit: true,\n    });\n    return sandbox.executeTree(\n      createExecContext(\n        sandbox,\n        parse(code, true, false, sandbox.context.options.maxParserRecursionDepth),\n        createEvalContext(),\n      ),\n      scopes,\n    );\n  }\n\n  static parse(code: string) {\n    return parse(code, true);\n  }\n\n  get Function() {\n    const context = createExecContext(\n      this,\n      {\n        tree: [],\n        constants: {\n          strings: [],\n          eager: true,\n          literals: [],\n          maxDepth: this.context.options.maxParserRecursionDepth,\n          regexes: [],\n        },\n      },\n      this.evalContext,\n    );\n    return context.evals.get(Function)!;\n  }\n\n  get AsyncFunction() {\n    const context = createExecContext(\n      this,\n      {\n        tree: [],\n        constants: {\n          strings: [],\n          eager: true,\n          literals: [],\n          maxDepth: this.context.options.maxParserRecursionDepth,\n          regexes: [],\n        },\n      },\n      this.evalContext,\n    );\n    return context.evals.get(AsyncFunction)!;\n  }\n\n  get eval() {\n    const context = createExecContext(\n      this,\n      {\n        tree: [],\n        constants: {\n          strings: [],\n          eager: true,\n          literals: [],\n          maxDepth: this.context.options.maxParserRecursionDepth,\n          regexes: [],\n        },\n      },\n      this.evalContext,\n    );\n    return context.evals.get(eval)!;\n  }\n\n  compile<T>(\n    code: string,\n    optimize = false,\n  ): (...scopes: IScope[]) => { context: IExecContext; run: () => T } {\n    if (this.context.options.nonBlocking)\n      throw new SandboxCapabilityError(\n        'Non-blocking mode is enabled, use Sandbox.compileAsync() instead.',\n      );\n    const parsed = parse(code, optimize, false, this.context.options.maxParserRecursionDepth);\n    const context = createExecContext(this, parsed, this.evalContext);\n    const exec = (...scopes: IScope[]) => {\n      sanitizeScopes(scopes, context);\n      return { context, run: () => this.executeTree<T>(context, [...scopes]).result };\n    };\n    return exec;\n  }\n\n  compileAsync<T>(\n    code: string,\n    optimize = false,\n  ): (...scopes: IScope[]) => { context: IExecContext; run: () => Promise<T> } {\n    const parsed = parse(code, optimize, false, this.context.options.maxParserRecursionDepth);\n    const context = createExecContext(this, parsed, this.evalContext);\n    const exec = (...scopes: IScope[]) => {\n      sanitizeScopes(scopes, context);\n      return {\n        context,\n        run: () => this.executeTreeAsync<T>(context, [...scopes]).then((ret) => ret.result),\n      };\n    };\n    return exec;\n  }\n\n  compileExpression<T>(\n    code: string,\n    optimize = false,\n  ): (...scopes: IScope[]) => { context: IExecContext; run: () => T } {\n    const parsed = parse(code, optimize, true, this.context.options.maxParserRecursionDepth);\n    const context = createExecContext(this, parsed, this.evalContext);\n    const exec = (...scopes: IScope[]) => {\n      sanitizeScopes(scopes, context);\n      return { context, run: () => this.executeTree<T>(context, [...scopes]).result };\n    };\n    return exec;\n  }\n\n  compileExpressionAsync<T>(\n    code: string,\n    optimize = false,\n  ): (...scopes: IScope[]) => { context: IExecContext; run: () => Promise<T> } {\n    const parsed = parse(code, optimize, true, this.context.options.maxParserRecursionDepth);\n    const context = createExecContext(this, parsed, this.evalContext);\n    const exec = (...scopes: IScope[]) => {\n      return {\n        context,\n        run: () => this.executeTreeAsync<T>(context, [...scopes]).then((ret) => ret.result),\n      };\n    };\n    return exec;\n  }\n}\n\nexport default Sandbox;\n"
  },
  {
    "path": "src/SandboxExec.ts",
    "content": "import type { IEvalContext } from './eval';\nimport { Change, ExecReturn, executeTree, executeTreeAsync } from './executor';\nimport { createContext, SandboxExecutionQuotaExceededError } from './utils';\nimport type {\n  IContext,\n  IExecContext,\n  IGlobals,\n  IOptionParams,\n  IOptions,\n  IScope,\n  ISymbolWhitelist,\n  SubscriptionSubject,\n  HaltContext,\n} from './utils';\n\nfunction subscribeSet(\n  obj: object,\n  name: string,\n  callback: (modification: Change) => void,\n  context: {\n    setSubscriptions: WeakMap<\n      SubscriptionSubject,\n      Map<string, Set<(modification: Change) => void>>\n    >;\n    changeSubscriptions: WeakMap<SubscriptionSubject, Set<(modification: Change) => void>>;\n  },\n): { unsubscribe: () => void } {\n  const names =\n    context.setSubscriptions.get(obj) || new Map<string, Set<(modification: Change) => void>>();\n  context.setSubscriptions.set(obj, names);\n  const callbacks = names.get(name) || new Set();\n  names.set(name, callbacks);\n  callbacks.add(callback);\n  let changeCbs: Set<(modification: Change) => void>;\n  const val = (obj as any)[name] as unknown;\n  if (val instanceof Object) {\n    changeCbs = context.changeSubscriptions.get(val) || new Set();\n    changeCbs.add(callback);\n    context.changeSubscriptions.set(val, changeCbs);\n  }\n  return {\n    unsubscribe: () => {\n      callbacks.delete(callback);\n      changeCbs?.delete(callback);\n    },\n  };\n}\n\nexport class SandboxExec {\n  public readonly context: IContext;\n  public readonly setSubscriptions: WeakMap<\n    SubscriptionSubject,\n    Map<string, Set<(modification: Change) => void>>\n  > = new WeakMap();\n  public readonly changeSubscriptions: WeakMap<\n    SubscriptionSubject,\n    Set<(modification: Change) => void>\n  > = new WeakMap();\n  public readonly sandboxFunctions: WeakMap<Function, IExecContext> = new WeakMap();\n  private haltSubscriptions: Set<(context: HaltContext) => void> = new Set();\n  private resumeSubscriptions: Set<() => void> = new Set();\n  public halted = false;\n  timeoutHandleCounter = 0;\n  public readonly setTimeoutHandles = new Map<\n    number,\n    {\n      handle: number;\n      haltsub: { unsubscribe: () => void };\n      contsub: { unsubscribe: () => void };\n    }\n  >();\n  public readonly setIntervalHandles = new Map<\n    number,\n    {\n      handle: number;\n      haltsub: { unsubscribe: () => void };\n      contsub: { unsubscribe: () => void };\n    }\n  >();\n  constructor(\n    options?: IOptionParams,\n    public evalContext?: IEvalContext,\n  ) {\n    const opt: IOptions = Object.assign(\n      {\n        audit: false,\n        forbidFunctionCalls: false,\n        forbidFunctionCreation: false,\n        globals: SandboxExec.SAFE_GLOBALS,\n        symbolWhitelist: SandboxExec.SAFE_SYMBOLS,\n        prototypeWhitelist: SandboxExec.SAFE_PROTOTYPES,\n        maxParserRecursionDepth: 256,\n        nonBlocking: false,\n        functionReplacements: new Map<\n          Function,\n          (ctx: IExecContext, builtInReplacement?: Function) => Function\n        >(),\n      },\n      options || {},\n    );\n    this.context = createContext(this, opt);\n  }\n\n  static get SAFE_GLOBALS(): IGlobals {\n    return {\n      globalThis,\n      Function,\n      eval,\n      console: {\n        debug: console.debug,\n        error: console.error,\n        info: console.info,\n        log: console.log,\n        table: console.table,\n        warn: console.warn,\n      },\n      isFinite,\n      isNaN,\n      parseFloat,\n      parseInt,\n      decodeURI,\n      decodeURIComponent,\n      encodeURI,\n      encodeURIComponent,\n      escape,\n      unescape,\n      Boolean,\n      Number,\n      BigInt,\n      String,\n      Object,\n      Array,\n      Symbol,\n      Error,\n      EvalError,\n      RangeError,\n      ReferenceError,\n      SyntaxError,\n      TypeError,\n      URIError,\n      Int8Array,\n      Uint8Array,\n      Uint8ClampedArray,\n      Int16Array,\n      Uint16Array,\n      Int32Array,\n      Uint32Array,\n      Float32Array,\n      Float64Array,\n      Map,\n      Set,\n      WeakMap,\n      WeakSet,\n      Promise,\n      Intl,\n      JSON,\n      Math,\n      Date,\n      RegExp,\n    };\n  }\n\n  static get SAFE_SYMBOLS(): ISymbolWhitelist {\n    const safeSymbols: ISymbolWhitelist = {};\n    for (const key of [\n      'asyncIterator',\n      'iterator',\n      'match',\n      'matchAll',\n      'replace',\n      'search',\n      'split',\n    ]) {\n      const value = (Symbol as unknown as Record<string, symbol | undefined>)[key];\n      if (typeof value === 'symbol') {\n        safeSymbols[key] = value;\n      }\n    }\n    return safeSymbols;\n  }\n\n  static get SAFE_PROTOTYPES(): Map<any, Set<string>> {\n    const protos = [\n      Function,\n      Boolean,\n      Number,\n      BigInt,\n      String,\n      Date,\n      Error,\n      Array,\n      Int8Array,\n      Uint8Array,\n      Uint8ClampedArray,\n      Int16Array,\n      Uint16Array,\n      Int32Array,\n      Uint32Array,\n      Float32Array,\n      Float64Array,\n      Map,\n      Set,\n      WeakMap,\n      WeakSet,\n      Promise,\n      Symbol,\n      Date,\n      RegExp,\n    ];\n    const map = new Map<any, Set<string>>();\n    protos.forEach((proto) => {\n      map.set(proto, new Set());\n    });\n    map.set(\n      Object,\n      new Set([\n        'constructor',\n        'name',\n        'entries',\n        'fromEntries',\n        'getOwnPropertyNames',\n        'is',\n        'keys',\n        'hasOwnProperty',\n        'isPrototypeOf',\n        'propertyIsEnumerable',\n        'toLocaleString',\n        'toString',\n        'valueOf',\n        'values',\n      ]),\n    );\n    return map;\n  }\n\n  subscribeGet(\n    callback: (obj: SubscriptionSubject, name: string) => void,\n    context: IExecContext,\n  ): { unsubscribe: () => void } {\n    context.getSubscriptions.add(callback);\n    return { unsubscribe: () => context.getSubscriptions.delete(callback) };\n  }\n\n  subscribeSet(\n    obj: object,\n    name: string,\n    callback: (modification: Change) => void,\n    context: SandboxExec | IExecContext,\n  ): { unsubscribe: () => void } {\n    return subscribeSet(obj, name, callback, context);\n  }\n\n  subscribeSetGlobal(\n    obj: SubscriptionSubject,\n    name: string,\n    callback: (modification: Change) => void,\n  ): { unsubscribe: () => void } {\n    return subscribeSet(obj, name, callback, this);\n  }\n\n  subscribeHalt(cb: (context: HaltContext) => void) {\n    this.haltSubscriptions.add(cb);\n    return {\n      unsubscribe: () => {\n        this.haltSubscriptions.delete(cb);\n      },\n    };\n  }\n  subscribeResume(cb: () => void) {\n    this.resumeSubscriptions.add(cb);\n    return {\n      unsubscribe: () => {\n        this.resumeSubscriptions.delete(cb);\n      },\n    };\n  }\n\n  haltExecution(haltContext: HaltContext = { type: 'manual' }) {\n    if (this.halted) return;\n    this.halted = true;\n    for (const cb of this.haltSubscriptions) {\n      cb(haltContext);\n    }\n  }\n\n  resumeExecution() {\n    if (!this.halted) return;\n    if (\n      this.context.ticks.tickLimit !== undefined &&\n      this.context.ticks.ticks >= this.context.ticks.tickLimit\n    ) {\n      throw new SandboxExecutionQuotaExceededError('Cannot resume execution: tick limit exceeded');\n    }\n    this.halted = false;\n    for (const cb of this.resumeSubscriptions) {\n      cb();\n    }\n  }\n\n  getContext(fn: (...args: any[]) => any) {\n    return this.sandboxFunctions.get(fn);\n  }\n\n  executeTree<T>(context: IExecContext, scopes: IScope[] = []): ExecReturn<T> {\n    return executeTree(context.ctx.ticks, context, context.tree, scopes, undefined, false);\n  }\n\n  executeTreeAsync<T>(context: IExecContext, scopes: IScope[] = []): Promise<ExecReturn<T>> {\n    return executeTreeAsync(context.ctx.ticks, context, context.tree, scopes, undefined, false);\n  }\n}\n\nexport default SandboxExec;\n"
  },
  {
    "path": "src/eval/index.ts",
    "content": "import {\n  createAsyncGeneratorFunction,\n  createFunction,\n  createFunctionAsync,\n  createGeneratorFunction,\n} from '../executor';\nimport parse, { lispifyFunction } from '../parser';\nimport type { Lisp } from '../parser';\nimport { getSandboxSymbolCtor, LispType } from '../utils';\nimport type { IExecContext } from '../utils';\n\nexport interface IEvalContext {\n  sandboxFunction: typeof sandboxFunction;\n  sandboxAsyncFunction: typeof sandboxAsyncFunction;\n  sandboxGeneratorFunction: typeof sandboxGeneratorFunction;\n  sandboxAsyncGeneratorFunction: typeof sandboxAsyncGeneratorFunction;\n  sandboxedSymbol: typeof sandboxedSymbol;\n  sandboxedEval: (func: SandboxFunction, context: IExecContext) => SandboxEval;\n  sandboxedSetTimeout: typeof sandboxedSetTimeout;\n  sandboxedSetInterval: typeof sandboxedSetInterval;\n  sandboxedClearTimeout: typeof sandboxedClearTimeout;\n  sandboxedClearInterval: typeof sandboxedClearInterval;\n  lispifyFunction: typeof lispifyFunction;\n}\nexport type SandboxFunction = (code: string, ...args: string[]) => () => unknown;\nexport type SandboxEval = (code: string) => unknown;\nexport type SandboxSetTimeout = (\n  handler: TimerHandler,\n  timeout?: number,\n  ...args: unknown[]\n) => any;\nexport type SandboxSetInterval = (\n  handler: TimerHandler,\n  timeout?: number,\n  ...args: unknown[]\n) => any;\nexport type SandboxClearTimeout = (handle: number) => void;\nexport type SandboxClearInterval = (handle: number) => void;\n\nexport function createEvalContext(): IEvalContext {\n  return {\n    sandboxFunction,\n    sandboxAsyncFunction,\n    sandboxGeneratorFunction,\n    sandboxAsyncGeneratorFunction,\n    sandboxedSymbol,\n    sandboxedEval,\n    sandboxedSetTimeout,\n    sandboxedSetInterval,\n    sandboxedClearTimeout,\n    sandboxedClearInterval,\n    lispifyFunction,\n  };\n}\n\nexport function sandboxedSymbol(context: IExecContext) {\n  return getSandboxSymbolCtor(context.ctx.sandboxSymbols);\n}\n\nfunction SB() {}\nexport function sandboxFunction(context: IExecContext): SandboxFunction {\n  SandboxFunction.prototype = SB.prototype;\n  return SandboxFunction;\n  function SandboxFunction(...params: string[]) {\n    const code = params.pop() || '';\n    const parsed = parse(code, false, false, context.ctx.options.maxParserRecursionDepth);\n    return createFunction(\n      params,\n      parsed.tree,\n      context.ctx.ticks,\n      {\n        ...context,\n        constants: parsed.constants,\n        tree: parsed.tree,\n      },\n      undefined,\n      'anonymous',\n    );\n  }\n}\n\nexport type SandboxAsyncFunction = (code: string, ...args: string[]) => () => Promise<unknown>;\nfunction SAF() {}\nexport function sandboxAsyncFunction(context: IExecContext): SandboxAsyncFunction {\n  SandboxAsyncFunction.prototype = SAF.prototype;\n  return SandboxAsyncFunction;\n  function SandboxAsyncFunction(...params: string[]) {\n    const code = params.pop() || '';\n    const parsed = parse(code, false, false, context.ctx.options.maxParserRecursionDepth);\n    return createFunctionAsync(\n      params,\n      parsed.tree,\n      context.ctx.ticks,\n      {\n        ...context,\n        constants: parsed.constants,\n        tree: parsed.tree,\n      },\n      undefined,\n      'anonymous',\n    );\n  }\n}\n\nexport type SandboxGeneratorFunction = (\n  code: string,\n  ...args: string[]\n) => () => Iterator<unknown> & Iterable<unknown>;\nfunction SGF() {}\nexport function sandboxGeneratorFunction(context: IExecContext): SandboxGeneratorFunction {\n  SandboxGeneratorFunction.prototype = SGF.prototype;\n  return SandboxGeneratorFunction;\n  function SandboxGeneratorFunction(...params: string[]) {\n    const code = params.pop() || '';\n    const parsed = parse(code, false, false, context.ctx.options.maxParserRecursionDepth);\n    return createGeneratorFunction(\n      params,\n      parsed.tree,\n      context.ctx.ticks,\n      {\n        ...context,\n        constants: parsed.constants,\n        tree: parsed.tree,\n      },\n      undefined,\n      'anonymous',\n    );\n  }\n}\n\nexport type SandboxAsyncGeneratorFunction = (\n  code: string,\n  ...args: string[]\n) => () => AsyncGenerator<unknown, unknown, unknown>;\nfunction SAGF() {}\nexport function sandboxAsyncGeneratorFunction(\n  context: IExecContext,\n): SandboxAsyncGeneratorFunction {\n  SandboxAsyncGeneratorFunction.prototype = SAGF.prototype;\n  return SandboxAsyncGeneratorFunction;\n  function SandboxAsyncGeneratorFunction(...params: string[]) {\n    const code = params.pop() || '';\n    const parsed = parse(code, false, false, context.ctx.options.maxParserRecursionDepth);\n    return createAsyncGeneratorFunction(\n      params,\n      parsed.tree,\n      context.ctx.ticks,\n      {\n        ...context,\n        constants: parsed.constants,\n        tree: parsed.tree,\n      },\n      undefined,\n      'anonymous',\n    );\n  }\n}\n\nfunction SE() {}\nexport function sandboxedEval(func: SandboxFunction, context: IExecContext): SandboxEval {\n  sandboxEval.prototype = SE.prototype;\n  return sandboxEval;\n  function sandboxEval(code: string) {\n    // Parse the code and wrap last statement in return for completion value\n    const parsed = parse(code, false, false, context.ctx.options.maxParserRecursionDepth);\n    const tree = wrapLastStatementInReturn(parsed.tree);\n    // Create and execute function with modified tree\n    return createFunction(\n      [],\n      tree,\n      context.ctx.ticks,\n      {\n        ...context,\n        constants: parsed.constants,\n        tree,\n      },\n      undefined,\n      'anonymous',\n    )();\n  }\n}\n\nfunction wrapLastStatementInReturn(tree: Lisp[]): Lisp[] {\n  if (tree.length === 0) return tree;\n  const newTree = [...tree];\n  const lastIndex = newTree.length - 1;\n  const lastStmt = newTree[lastIndex];\n\n  // Only wrap if it's not already a return or throw\n  if (Array.isArray(lastStmt) && lastStmt.length >= 1) {\n    const op = lastStmt[0];\n\n    // Don't wrap Return (8) or Throw (47) - they already control flow\n    if (op === LispType.Return || op === LispType.Throw) {\n      return newTree;\n    }\n\n    // List of statement types that should have undefined completion value\n    // These match JavaScript semantics where declarations and control structures\n    // don't produce a completion value\n    const statementTypes = [\n      LispType.Let, // 3\n      LispType.Const, // 4\n      LispType.Var, // 35\n      LispType.Function, // 38\n      LispType.If, // 14\n      LispType.Loop, // 39\n      LispType.Try, // 40\n      LispType.Switch, // 41\n      LispType.InternalBlock, // 43\n      LispType.Expression, // 44\n    ];\n\n    // If the last statement is a declaration or control structure,\n    // don't wrap it (it will naturally return undefined)\n    if (statementTypes.includes(op)) {\n      return newTree;\n    }\n\n    // For all other types (expressions, operators, etc.),\n    // wrap in return to capture the completion value\n    newTree[lastIndex] = [LispType.Return, LispType.None, lastStmt];\n  }\n\n  return newTree;\n}\n\nfunction sST() {}\nexport function sandboxedSetTimeout(\n  func: SandboxFunction,\n  context: IExecContext,\n): SandboxSetTimeout {\n  sandboxSetTimeout.prototype = sST.prototype;\n  return sandboxSetTimeout;\n  function sandboxSetTimeout(handler: TimerHandler, timeout?: number, ...args: unknown[]) {\n    const sandbox = context.ctx.sandbox;\n    const exec = (...a: any[]) => {\n      const h = typeof handler === 'string' ? func(handler) : handler;\n      haltsub.unsubscribe();\n      contsub.unsubscribe();\n      sandbox.setTimeoutHandles.delete(sandBoxhandle);\n      return h(...a);\n    };\n\n    const sandBoxhandle = ++sandbox.timeoutHandleCounter;\n\n    let start = Date.now();\n    let handle: number = setTimeout(exec, timeout, ...args);\n\n    let elapsed = 0;\n    const haltsub = sandbox.subscribeHalt(() => {\n      elapsed = Date.now() - start + elapsed;\n      clearTimeout(handle);\n    });\n    const contsub = sandbox.subscribeResume(() => {\n      start = Date.now();\n      const remaining = Math.floor((timeout || 0) - elapsed);\n      handle = setTimeout(exec, remaining, ...args);\n      sandbox.setTimeoutHandles.set(sandBoxhandle, {\n        handle,\n        haltsub,\n        contsub,\n      });\n    });\n    sandbox.setTimeoutHandles.set(sandBoxhandle, {\n      handle,\n      haltsub,\n      contsub,\n    });\n    return sandBoxhandle;\n  }\n}\n\nfunction sCT() {}\nexport function sandboxedClearTimeout(context: IExecContext): SandboxClearTimeout {\n  sandboxClearTimeout.prototype = sCT.prototype;\n  return sandboxClearTimeout;\n  function sandboxClearTimeout(handle: number) {\n    const sandbox = context.ctx.sandbox;\n    const timeoutHandle = sandbox.setTimeoutHandles.get(handle);\n    if (timeoutHandle) {\n      clearTimeout(timeoutHandle.handle);\n      timeoutHandle.haltsub.unsubscribe();\n      timeoutHandle.contsub.unsubscribe();\n      sandbox.setTimeoutHandles.delete(handle);\n    }\n  }\n}\nfunction sCI() {}\nexport function sandboxedClearInterval(context: IExecContext): SandboxClearInterval {\n  sandboxClearInterval.prototype = sCI.prototype;\n  return sandboxClearInterval;\n  function sandboxClearInterval(handle: number) {\n    const sandbox = context.ctx.sandbox;\n    const intervalHandle = sandbox.setIntervalHandles.get(handle);\n    if (intervalHandle) {\n      clearInterval(intervalHandle.handle);\n      clearTimeout(intervalHandle.handle);\n      intervalHandle.haltsub.unsubscribe();\n      intervalHandle.contsub.unsubscribe();\n      sandbox.setIntervalHandles.delete(handle);\n    }\n  }\n}\n\nfunction sSI() {}\nexport function sandboxedSetInterval(\n  func: SandboxFunction,\n  context: IExecContext,\n): SandboxSetInterval {\n  sandboxSetInterval.prototype = sSI.prototype;\n  return sandboxSetInterval;\n  function sandboxSetInterval(\n    handler: TimerHandler,\n    timeout: number | undefined,\n    ...args: unknown[]\n  ) {\n    const sandbox = context.ctx.sandbox;\n    const h = typeof handler === 'string' ? func(handler) : handler;\n    const exec = (...a: any[]) => {\n      start = Date.now();\n      elapsed = 0;\n      return h(...a);\n    };\n\n    const sandBoxhandle = ++sandbox.timeoutHandleCounter;\n\n    let start = Date.now();\n    let handle: number = setInterval(exec, timeout, ...args);\n\n    let elapsed = 0;\n    const haltsub = sandbox.subscribeHalt(() => {\n      elapsed = Date.now() - start + elapsed;\n      clearInterval(handle);\n      clearTimeout(handle);\n    });\n    const contsub = sandbox.subscribeResume(() => {\n      start = Date.now();\n      handle = setTimeout(\n        () => {\n          start = Date.now();\n          elapsed = 0;\n          handle = setInterval(exec, timeout, ...args);\n          handlObj.handle = handle;\n          exec(...args);\n        },\n        Math.floor((timeout || 0) - elapsed),\n        ...args,\n      );\n      handlObj.handle = handle;\n    });\n\n    const handlObj = {\n      handle,\n      haltsub,\n      contsub,\n    };\n    sandbox.setIntervalHandles.set(sandBoxhandle, handlObj);\n    return sandBoxhandle;\n  }\n}\n"
  },
  {
    "path": "src/executor/executorUtils.ts",
    "content": "import type { LispItem, Lisp, StatementLabel } from '../parser';\nimport {\n  hasOwnProperty,\n  isLisp,\n  LispType,\n  LocalScope,\n  Prop,\n  SandboxExecutionQuotaExceededError,\n  SandboxError,\n  SandboxExecutionTreeError,\n  Scope,\n  GeneratorFunction,\n  AsyncGeneratorFunction,\n  SandboxCapabilityError,\n  SandboxAccessError,\n  NON_BLOCKING_THRESHOLD,\n  sanitizeProp,\n  Unknown,\n} from '../utils';\nimport { IAuditReport, IExecContext, IScope, optional, Ticks } from '../utils';\n\nexport type Done<T = any> = (err?: any, res?: T | typeof optional) => void;\n\nexport type ControlFlowAction = 'break' | 'continue';\n\nexport interface ControlFlowSignal {\n  type: ControlFlowAction;\n  label?: string;\n}\n\ninterface ControlFlowTarget {\n  label?: string;\n  acceptsBreak: boolean;\n  acceptsContinue: boolean;\n  acceptsUnlabeledBreak: boolean;\n  acceptsUnlabeledContinue: boolean;\n}\n\nexport type ControlFlowTargets = readonly ControlFlowTarget[] | undefined;\n\nexport class ExecReturn<T> {\n  constructor(\n    public auditReport: IAuditReport | undefined,\n    public result: T,\n    public returned: boolean,\n    public controlFlow?: ControlFlowSignal,\n  ) {}\n\n  get breakLoop() {\n    return this.controlFlow?.type === 'break';\n  }\n\n  get continueLoop() {\n    return this.controlFlow?.type === 'continue';\n  }\n}\n\nexport interface IChange {\n  type: string;\n}\n\nexport interface ICreate extends IChange {\n  type: 'create';\n  prop: number | string;\n}\n\nexport interface IReplace extends IChange {\n  type: 'replace';\n}\n\nexport interface IDelete extends IChange {\n  type: 'delete';\n  prop: number | string;\n}\n\nexport interface IReverse extends IChange {\n  type: 'reverse';\n}\n\nexport interface ISort extends IChange {\n  type: 'sort';\n}\n\nexport interface IPush extends IChange {\n  type: 'push';\n  added: unknown[];\n}\n\nexport interface IPop extends IChange {\n  type: 'pop';\n  removed: unknown[];\n}\n\nexport interface IShift extends IChange {\n  type: 'shift';\n  removed: unknown[];\n}\n\nexport interface IUnShift extends IChange {\n  type: 'unshift';\n  added: unknown[];\n}\n\nexport interface ISplice extends IChange {\n  type: 'splice';\n  startIndex: number;\n  deleteCount: number;\n  added: unknown[];\n  removed: unknown[];\n}\n\nexport interface ICopyWithin extends IChange {\n  type: 'copyWithin';\n  startIndex: number;\n  endIndex: number;\n  added: unknown[];\n  removed: unknown[];\n}\n\nexport type Change =\n  | ICreate\n  | IReplace\n  | IDelete\n  | IReverse\n  | ISort\n  | IPush\n  | IPop\n  | IUnShift\n  | IShift\n  | ISplice\n  | ICopyWithin;\n\nconst emptyControlFlowTargets: readonly ControlFlowTarget[] = [];\n\nexport function normalizeStatementLabel(label: StatementLabel | undefined) {\n  return label === undefined || label === LispType.None ? undefined : label;\n}\n\nexport function normalizeStatementLabels(label: LispItem | StatementLabel | undefined) {\n  if (label === undefined || label === LispType.None) return [] as string[];\n  if (Array.isArray(label) && !isLisp(label)) {\n    return label.filter((item): item is string => typeof item === 'string');\n  }\n  return [label as string];\n}\n\nexport function createLoopTarget(label?: string, acceptsUnlabeled = true): ControlFlowTarget {\n  return {\n    label,\n    acceptsBreak: true,\n    acceptsContinue: true,\n    acceptsUnlabeledBreak: acceptsUnlabeled,\n    acceptsUnlabeledContinue: acceptsUnlabeled,\n  };\n}\n\nexport function createSwitchTarget(label?: string): ControlFlowTarget {\n  return {\n    label,\n    acceptsBreak: true,\n    acceptsContinue: false,\n    acceptsUnlabeledBreak: true,\n    acceptsUnlabeledContinue: false,\n  };\n}\n\nexport function createLabeledStatementTarget(label?: string): ControlFlowTarget | undefined {\n  if (!label) return undefined;\n  return {\n    label,\n    acceptsBreak: true,\n    acceptsContinue: false,\n    acceptsUnlabeledBreak: false,\n    acceptsUnlabeledContinue: false,\n  };\n}\n\nexport function addControlFlowTarget(\n  controlFlowTargets: ControlFlowTargets,\n  target?: ControlFlowTarget,\n): ControlFlowTargets {\n  if (!target) return controlFlowTargets;\n  return [...(controlFlowTargets || emptyControlFlowTargets), target];\n}\n\nexport function addControlFlowTargets(\n  controlFlowTargets: ControlFlowTargets,\n  targets: ControlFlowTarget[],\n): ControlFlowTargets {\n  return targets.reduce(\n    (currentTargets, target) => addControlFlowTarget(currentTargets, target),\n    controlFlowTargets,\n  );\n}\n\nexport function matchesControlFlowTarget(signal: ControlFlowSignal, target: ControlFlowTarget) {\n  if (signal.type === 'continue') {\n    if (!target.acceptsContinue) return false;\n    return signal.label ? target.label === signal.label : target.acceptsUnlabeledContinue;\n  }\n  if (!target.acceptsBreak) return false;\n  return signal.label ? target.label === signal.label : target.acceptsUnlabeledBreak;\n}\n\nexport function findControlFlowTarget(\n  controlFlowTargets: ControlFlowTargets,\n  type: ControlFlowAction,\n  label?: string,\n) {\n  if (!controlFlowTargets) return undefined;\n  for (let i = controlFlowTargets.length - 1; i >= 0; i--) {\n    const target = controlFlowTargets[i];\n    if (label) {\n      if (target.label !== label) continue;\n      if (type === 'continue' ? target.acceptsContinue : target.acceptsBreak) {\n        return target;\n      }\n      return null;\n    }\n    if (type === 'continue' ? target.acceptsUnlabeledContinue : target.acceptsUnlabeledBreak) {\n      return target;\n    }\n  }\n  return undefined;\n}\n\nfunction generateArgs(argNames: string[], args: unknown[]) {\n  const vars: Record<string, unknown> = {};\n  argNames.forEach((arg, i) => {\n    if (arg.startsWith('...')) {\n      vars[arg.substring(3)] = args.slice(i);\n    } else {\n      vars[arg] = args[i];\n    }\n  });\n  return vars;\n}\n\nexport function createFunction(\n  argNames: string[],\n  parsed: Lisp[],\n  ticks: Ticks,\n  context: IExecContext,\n  scope?: Scope,\n  name?: string,\n  internal = false,\n) {\n  if (context.ctx.options.forbidFunctionCreation) {\n    throw new SandboxCapabilityError('Function creation is forbidden');\n  }\n  let func;\n  if (name === undefined) {\n    func = (...args: unknown[]) => {\n      const vars = generateArgs(argNames, args);\n      const res = executeTree(\n        ticks,\n        context,\n        parsed,\n        scope === undefined ? [] : [new Scope(scope, vars)],\n        undefined,\n        internal,\n      );\n      return res.result;\n    };\n  } else {\n    func = function sandboxedObject(this: Unknown, ...args: unknown[]) {\n      const vars = generateArgs(argNames, args);\n      const res = executeTree(\n        ticks,\n        context,\n        parsed,\n        scope === undefined ? [] : [new Scope(scope, vars, this)],\n        undefined,\n        internal,\n      );\n      return res.result;\n    };\n  }\n  context.registerSandboxFunction(func);\n  context.ctx.sandboxedFunctions.add(func);\n  return func;\n}\n\nexport function createFunctionAsync(\n  argNames: string[],\n  parsed: Lisp[],\n  ticks: Ticks,\n  context: IExecContext,\n  scope?: Scope,\n  name?: string,\n  internal = false,\n) {\n  if (context.ctx.options.forbidFunctionCreation) {\n    throw new SandboxCapabilityError('Function creation is forbidden');\n  }\n  if (!context.ctx.prototypeWhitelist?.has(Promise.prototype)) {\n    throw new SandboxCapabilityError('Async/await not permitted');\n  }\n  let func;\n  if (name === undefined) {\n    func = async (...args: unknown[]) => {\n      const vars = generateArgs(argNames, args);\n      const res = await executeTreeAsync(\n        ticks,\n        context,\n        parsed,\n        scope === undefined ? [] : [new Scope(scope, vars)],\n        undefined,\n        internal,\n      );\n      return res.result;\n    };\n  } else {\n    func = async function sandboxedObject(this: Unknown, ...args: unknown[]) {\n      const vars = generateArgs(argNames, args);\n      const res = await executeTreeAsync(\n        ticks,\n        context,\n        parsed,\n        scope === undefined ? [] : [new Scope(scope, vars, this)],\n        undefined,\n        internal,\n      );\n      return res.result;\n    };\n  }\n  context.registerSandboxFunction(func);\n  context.ctx.sandboxedFunctions.add(func);\n  return func;\n}\n\n// Sentinel class used to communicate yield values from the executor back to the generator.\nexport class YieldValue {\n  constructor(\n    public value: unknown,\n    public delegate: boolean,\n  ) {}\n}\n\n// Unique sentinel thrown by captureYieldFn in executeGenBody's default case when a new\n// synchronous yield is encountered. Propagates through the call stack back to the restart loop.\nconst syncYieldPauseSentinel = Symbol('syncYieldPause');\n\nfunction asIterableIterator(value: unknown): Iterator<unknown> & Iterable<unknown> {\n  const iterator =\n    (value as { [Symbol.iterator]?: () => Iterator<unknown> })?.[Symbol.iterator]?.() ??\n    (value as Iterator<unknown>);\n\n  if (!iterator || typeof iterator.next !== 'function') {\n    throw new TypeError('yield* target is not iterable');\n  }\n\n  if (typeof (iterator as Iterator<unknown> & Iterable<unknown>)[Symbol.iterator] === 'function') {\n    return iterator as Iterator<unknown> & Iterable<unknown>;\n  }\n\n  return {\n    next: iterator.next.bind(iterator),\n    throw: iterator.throw?.bind(iterator),\n    return: iterator.return?.bind(iterator),\n    [Symbol.iterator]() {\n      return this;\n    },\n  };\n}\n\nfunction asAsyncIterableIterator(value: unknown): AsyncIterator<unknown> & AsyncIterable<unknown> {\n  const asyncIterator = (value as { [Symbol.asyncIterator]?: () => AsyncIterator<unknown> })?.[\n    Symbol.asyncIterator\n  ]?.();\n\n  if (asyncIterator) {\n    return {\n      next: asyncIterator.next.bind(asyncIterator),\n      throw: asyncIterator.throw?.bind(asyncIterator),\n      return: asyncIterator.return?.bind(asyncIterator),\n      [Symbol.asyncIterator]() {\n        return this;\n      },\n    };\n  }\n\n  const iterator = asIterableIterator(value);\n  return {\n    async next(nextValue?: unknown) {\n      return iterator.next(nextValue);\n    },\n    async throw(err?: unknown) {\n      if (typeof iterator.throw === 'function') {\n        return iterator.throw(err);\n      }\n      throw err;\n    },\n    async return(valueToReturn?: unknown) {\n      if (typeof iterator.return === 'function') {\n        return iterator.return(valueToReturn);\n      }\n      return { value: valueToReturn, done: true };\n    },\n    [Symbol.asyncIterator]() {\n      return this;\n    },\n  };\n}\n\n// executeGenBody: a native generator that lazily executes a generator function body.\n// It handles compound control-flow nodes (statements, if, loop, try, yield) with yield*\n// recursion, and falls back to the existing execSync for all leaf expressions.\n// Only used by createGeneratorFunction — nothing else in the executor is changed.\nfunction* executeGenBody(\n  ticks: Ticks,\n  tree: Lisp | Lisp[],\n  scope: Scope,\n  context: IExecContext,\n  statementLabels: ControlFlowTargets,\n  internal: boolean,\n): Generator<unknown, ExecReturn<unknown> | unknown, unknown> {\n  // ── Statement list ──────────────────────────────────────────────────────────\n  if (!isLisp(tree as Lisp) && Array.isArray(tree)) {\n    const stmts = tree as Lisp[];\n    if (stmts.length === 0 || (stmts[0] as unknown) === LispType.None) {\n      return new ExecReturn(context.ctx.auditReport, undefined, false);\n    }\n    for (const stmt of stmts) {\n      const res = (yield* executeGenBody(\n        ticks,\n        stmt,\n        scope,\n        context,\n        statementLabels,\n        internal,\n      )) as ExecReturn<unknown>;\n      if (res instanceof ExecReturn && (res.returned || res.controlFlow)) return res;\n      // Mirror _executeWithDoneSync: wrap the result of a return statement\n      if (isLisp(stmt) && (stmt as Lisp)[0] === LispType.Return) {\n        return new ExecReturn(context.ctx.auditReport, res.result, true);\n      }\n    }\n    return new ExecReturn(context.ctx.auditReport, undefined, false);\n  }\n\n  const [op, a, b] = tree as Lisp;\n\n  switch (op) {\n    // ── yield expr ────────────────────────────────────────────────────────────\n    case LispType.Yield: {\n      const valResult = (yield* executeGenBody(\n        ticks,\n        a as Lisp,\n        scope,\n        context,\n        statementLabels,\n        internal,\n      )) as ExecReturn<unknown>;\n      const sanitized = sanitizeProp(valResult.result, context);\n      const injected: unknown = yield sanitized; // ← real pause point\n      return new ExecReturn(context.ctx.auditReport, injected, false);\n    }\n\n    // ── yield* expr ───────────────────────────────────────────────────────────\n    case LispType.YieldDelegate: {\n      const iterResult = (yield* executeGenBody(\n        ticks,\n        a as Lisp,\n        scope,\n        context,\n        statementLabels,\n        internal,\n      )) as ExecReturn<unknown>;\n      const delegatee = sanitizeProp(iterResult.result, context);\n      const result: unknown = yield* asIterableIterator(delegatee);\n      return new ExecReturn(context.ctx.auditReport, result, false);\n    }\n\n    // ── if / else ─────────────────────────────────────────────────────────────\n    // LispType.If is NOT in unexecTypes — its `a` is the raw condition Lisp,\n    // `b` is the raw IfCase node that evaluates to an If object with .t/.f.\n    case LispType.If: {\n      const condResult = (yield* executeGenBody(\n        ticks,\n        a as Lisp,\n        scope,\n        context,\n        statementLabels,\n        internal,\n      )) as ExecReturn<unknown>;\n      const ifCase = syncDone((d) =>\n        execSync(ticks, b as Lisp, scope, context, d, statementLabels, internal, undefined),\n      ).result as If;\n      const branch = sanitizeProp(condResult.result, context) ? ifCase.t : ifCase.f;\n      if (branch) {\n        return (yield* executeGenBody(\n          ticks,\n          branch,\n          scope,\n          context,\n          statementLabels,\n          internal,\n        )) as ExecReturn<unknown>;\n      }\n      return new ExecReturn(context.ctx.auditReport, undefined, false);\n    }\n\n    // ── loops (while / for / for-of / for-in / do-while) ─────────────────────\n    // Mirror the sync path of the existing Loop handler (executor.ts ~1421-1475)\n    // but replace `executeTree(b, ...)` with `yield*`.\n    case LispType.Loop: {\n      const [\n        checkFirst,\n        startInternal,\n        getIterator,\n        startStep,\n        step,\n        condition,\n        beforeStep,\n        isForAwait,\n        label,\n      ] = a as Lisp[];\n      if ((isForAwait as unknown as LispType) === LispType.True) {\n        throw new SyntaxError('for-await-of loops are only allowed inside async functions');\n      }\n      const loopStatementTargets = [\n        ...normalizeStatementLabels(label).map((loopLabel) => createLoopTarget(loopLabel, false)),\n        createLoopTarget(),\n      ];\n      const loopTargets = addControlFlowTargets(statementLabels, loopStatementTargets);\n      const loopScope = new Scope(scope, {});\n      const internalVars: Record<string, unknown> = { $$obj: undefined };\n      const interalScope = new Scope(loopScope, internalVars);\n\n      syncDone((d) =>\n        execSync(ticks, startStep, loopScope, context, d, undefined, internal, undefined),\n      );\n      internalVars['$$obj'] = syncDone((d) =>\n        execSync(ticks, getIterator, loopScope, context, d, undefined, internal, undefined),\n      ).result;\n      syncDone((d) =>\n        execSync(ticks, startInternal, interalScope, context, d, undefined, internal, undefined),\n      );\n\n      let loop: unknown = true;\n      if (checkFirst) {\n        loop = syncDone((d) =>\n          execSync(ticks, condition, interalScope, context, d, undefined, internal, undefined),\n        ).result;\n      }\n\n      while (loop) {\n        const iterScope = new Scope(interalScope, {});\n        syncDone((d) =>\n          execSync(ticks, beforeStep, iterScope, context, d, undefined, internal, undefined),\n        );\n\n        const res = (yield* executeGenBody(\n          ticks,\n          b as Lisp[],\n          iterScope,\n          context,\n          loopTargets,\n          internal,\n        )) as ExecReturn<unknown>;\n\n        if (res.returned) return res;\n        if (res.controlFlow) {\n          if (!loopStatementTargets.some((t) => matchesControlFlowTarget(res.controlFlow!, t))) {\n            return res; // break/continue targeting an outer labeled loop\n          }\n          if (res.breakLoop) break;\n          // continueLoop: fall through to step + condition check\n        }\n        syncDone((d) =>\n          execSync(ticks, step, interalScope, context, d, undefined, internal, undefined),\n        );\n        loop = syncDone((d) =>\n          execSync(ticks, condition, interalScope, context, d, undefined, internal, undefined),\n        ).result;\n      }\n      return new ExecReturn(context.ctx.auditReport, undefined, false);\n    }\n\n    // ── try / catch / finally ─────────────────────────────────────────────────\n    // Using real native try/catch/finally gives us correct gen.throw() and\n    // gen.return() semantics for free (the native generator machinery handles them).\n    case LispType.Try: {\n      const [exception, catchBody, finallyBody] = b as [string, Lisp[], Lisp[]];\n      let result!: ExecReturn<unknown>;\n      let finalOverride: ExecReturn<unknown> | undefined;\n      try {\n        result = (yield* executeGenBody(\n          ticks,\n          a as Lisp[],\n          scope,\n          context,\n          statementLabels,\n          internal,\n        )) as ExecReturn<unknown>;\n      } catch (e) {\n        if (exception && catchBody?.length > 0) {\n          const catchScope = new Scope(scope, { [exception]: e });\n          result = (yield* executeGenBody(\n            ticks,\n            catchBody,\n            catchScope,\n            context,\n            statementLabels,\n            internal,\n          )) as ExecReturn<unknown>;\n        } else {\n          throw e;\n        }\n      } finally {\n        if (finallyBody?.length > 0) {\n          const fr = (yield* executeGenBody(\n            ticks,\n            finallyBody,\n            scope,\n            context,\n            statementLabels,\n            internal,\n          )) as ExecReturn<unknown>;\n          // finally control flow (return/break/continue) overrides everything\n          if (fr.returned || fr.controlFlow) {\n            finalOverride = fr;\n          }\n        }\n      }\n      if (finalOverride) return finalOverride;\n      return result;\n    }\n\n    // ── labeled statement ─────────────────────────────────────────────────────\n    case LispType.Labeled: {\n      const target = createLabeledStatementTarget(normalizeStatementLabel(a as StatementLabel));\n      const newTargets = addControlFlowTargets(statementLabels, target ? [target] : []);\n      const res = (yield* executeGenBody(\n        ticks,\n        b as Lisp,\n        scope,\n        context,\n        newTargets,\n        internal,\n      )) as ExecReturn<unknown>;\n      if (res.controlFlow && target && matchesControlFlowTarget(res.controlFlow, target)) {\n        return new ExecReturn(context.ctx.auditReport, res.result, false);\n      }\n      return res;\n    }\n\n    // ── everything else ───────────────────────────────────────────────────────\n    // Arithmetic, property access, function calls, assignments, etc. are\n    // delegated to execSync. However, a yield expression may be nested inside\n    // a non-unexecType node (e.g. `const x = yield 1`). In that case the\n    // existing sync yield handler throws syncYieldPauseSentinel. We restart\n    // execSync from scratch on each such pause, skipping already-completed\n    // yields by immediately calling their continuation with the stored result.\n    default: {\n      let completedYields = 0;\n      const yieldResults: unknown[] = [];\n      while (true) {\n        let currentYieldIdx = 0;\n        let capturedValue: unknown = undefined;\n        let capturedDelegate = false;\n        let yielded = false;\n        const captureYieldFn = (yv: YieldValue, continueDone?: Done) => {\n          if (currentYieldIdx < completedYields) {\n            // This yield already happened in a prior iteration; fast-forward.\n            continueDone!(undefined, yieldResults[currentYieldIdx]);\n            currentYieldIdx++;\n            return;\n          }\n          // New yield: capture the value and pause.\n          capturedValue = yv.value;\n          capturedDelegate = yv.delegate;\n          yielded = true;\n          currentYieldIdx++;\n          throw syncYieldPauseSentinel;\n        };\n        try {\n          const result = syncDone((d) =>\n            execSync(\n              ticks,\n              tree as Lisp,\n              scope,\n              context,\n              d,\n              statementLabels,\n              internal,\n              captureYieldFn,\n            ),\n          ).result;\n          if (result instanceof ExecReturn) return result;\n          return new ExecReturn(context.ctx.auditReport, result, false);\n        } catch (e) {\n          if (!yielded || e !== syncYieldPauseSentinel) throw e;\n          const resumedValue: unknown = capturedDelegate\n            ? yield* asIterableIterator(capturedValue)\n            : yield capturedValue;\n          yieldResults.push(resumedValue);\n          completedYields++;\n        }\n      }\n    }\n  }\n}\n\nexport function createGeneratorFunction(\n  argNames: string[],\n  parsed: Lisp[],\n  ticks: Ticks,\n  context: IExecContext,\n  scope?: Scope,\n  name?: string,\n  internal = false,\n) {\n  if (context.ctx.options.forbidFunctionCreation) {\n    throw new SandboxCapabilityError('Function creation is forbidden');\n  }\n  const makeGen = (thisArg: Unknown, args: unknown[]) => {\n    const vars = generateArgs(argNames, args);\n    const genScope =\n      scope === undefined ? new Scope(null, vars, thisArg) : new Scope(scope, vars, thisArg);\n\n    const executionGen = executeGenBody(ticks, parsed, genScope, context, undefined, internal);\n    let isDone = false;\n\n    function drive(action: () => IteratorResult<unknown>): IteratorResult<unknown> {\n      if (isDone) return { value: undefined, done: true };\n      try {\n        const r = action();\n        if (r.done) {\n          isDone = true;\n          return {\n            value: r.value instanceof ExecReturn ? r.value.result : r.value,\n            done: true,\n          };\n        }\n        return { value: r.value, done: false };\n      } catch (e) {\n        isDone = true;\n        throw e;\n      }\n    }\n\n    const iterator: Iterator<unknown> & Iterable<unknown> = {\n      next(value?: unknown) {\n        return drive(() => executionGen.next(value));\n      },\n      return(value?: unknown) {\n        return drive(() => executionGen.return(value));\n      },\n      throw(err?: unknown) {\n        return drive(() => executionGen.throw(err));\n      },\n      [Symbol.iterator]() {\n        return this;\n      },\n    };\n    return iterator;\n  };\n  const func = function sandboxedObject(this: Unknown, ...args: unknown[]) {\n    return makeGen(this, args);\n  };\n  Object.setPrototypeOf(func, GeneratorFunction.prototype);\n  context.registerSandboxFunction(func);\n  context.ctx.sandboxedFunctions.add(func);\n  return func;\n}\n\nexport function createAsyncGeneratorFunction(\n  argNames: string[],\n  parsed: Lisp[],\n  ticks: Ticks,\n  context: IExecContext,\n  scope?: Scope,\n  name?: string,\n  internal = false,\n) {\n  if (context.ctx.options.forbidFunctionCreation) {\n    throw new SandboxCapabilityError('Function creation is forbidden');\n  }\n  if (!context.ctx.prototypeWhitelist?.has(Promise.prototype)) {\n    throw new SandboxCapabilityError('Async/await not permitted');\n  }\n  const makeGen = (thisArg: Unknown, args: unknown[]) => {\n    const vars = generateArgs(argNames, args);\n    const genScope =\n      scope === undefined ? [new Scope(null, vars, thisArg)] : [new Scope(scope, vars, thisArg)];\n    return (async function* sandboxedAsyncGenerator(): AsyncGenerator<unknown, unknown, unknown> {\n      const yieldQueue: Array<{ yieldValue: YieldValue; continueDone?: Done }> = [];\n      let resolveYield: (() => void) | null = null;\n      const yieldFn = (yv: YieldValue, continueDone?: Done) => {\n        yieldQueue.push({ yieldValue: yv, continueDone });\n        if (resolveYield) {\n          resolveYield();\n          resolveYield = null;\n        }\n      };\n      const bodyPromise = executeTreeAsync(\n        ticks,\n        context,\n        parsed,\n        genScope,\n        undefined,\n        internal,\n        yieldFn,\n      );\n      let bodyDone = false;\n      let bodyResult: ExecReturn<unknown> | undefined;\n      let bodyError: unknown;\n      bodyPromise.then(\n        (r) => {\n          bodyDone = true;\n          bodyResult = r;\n          resolveYield?.();\n        },\n        (e) => {\n          bodyDone = true;\n          bodyError = e;\n          resolveYield?.();\n        },\n      );\n      while (true) {\n        if (yieldQueue.length === 0 && !bodyDone) {\n          await new Promise<void>((res) => {\n            resolveYield = res;\n          });\n        }\n        while (yieldQueue.length > 0) {\n          const { yieldValue, continueDone } = yieldQueue.shift()!;\n          try {\n            const resumedValue = yieldValue.delegate\n              ? yield* asAsyncIterableIterator(yieldValue.value)\n              : yield yieldValue.value;\n            continueDone?.(undefined, resumedValue);\n          } catch (err) {\n            continueDone?.(err);\n          }\n        }\n        if (bodyDone) break;\n      }\n      if (bodyError !== undefined) throw bodyError;\n      return bodyResult?.result;\n    })();\n  };\n  const func = function sandboxedObject(this: Unknown, ...args: unknown[]) {\n    return makeGen(this, args) as unknown as AsyncGenerator;\n  };\n  Object.setPrototypeOf(func, AsyncGeneratorFunction.prototype);\n  context.registerSandboxFunction(func);\n  context.ctx.sandboxedFunctions.add(func);\n  return func;\n}\n\nexport function assignCheck(obj: Prop, context: IExecContext, op = 'assign') {\n  if (obj.context === undefined) {\n    throw new ReferenceError(`Cannot ${op} value to undefined.`);\n  }\n  if (obj.isConst) {\n    throw new TypeError(`Assignment to constant variable.`);\n  }\n  if (obj.isGlobal) {\n    throw new SandboxAccessError(\n      `Cannot ${op} property '${obj.prop.toString()}' of a global object`,\n    );\n  }\n  if (obj.context === null) {\n    throw new TypeError('Cannot set properties of null');\n  }\n  if (\n    typeof (obj.context as any)[obj.prop] === 'function' &&\n    !hasOwnProperty(obj.context, obj.prop)\n  ) {\n    throw new SandboxAccessError(\n      `Override prototype property '${obj.prop.toString()}' not allowed`,\n    );\n  }\n  if (op === 'delete') {\n    if (hasOwnProperty(obj.context, obj.prop)) {\n      context.changeSubscriptions\n        .get(obj.context)\n        ?.forEach((cb) => cb({ type: 'delete', prop: obj.prop.toString() }));\n      context.changeSubscriptionsGlobal\n        .get(obj.context)\n        ?.forEach((cb) => cb({ type: 'delete', prop: obj.prop.toString() }));\n    }\n  } else if (hasOwnProperty(obj.context, obj.prop)) {\n    context.setSubscriptions\n      .get(obj.context)\n      ?.get(obj.prop.toString())\n      ?.forEach((cb) =>\n        cb({\n          type: 'replace',\n        }),\n      );\n    context.setSubscriptionsGlobal\n      .get(obj.context)\n      ?.get(obj.prop.toString())\n      ?.forEach((cb) =>\n        cb({\n          type: 'replace',\n        }),\n      );\n  } else {\n    context.changeSubscriptions\n      .get(obj.context)\n      ?.forEach((cb) => cb({ type: 'create', prop: obj.prop.toString() }));\n    context.changeSubscriptionsGlobal\n      .get(obj.context)\n      ?.forEach((cb) => cb({ type: 'create', prop: obj.prop.toString() }));\n  }\n}\nexport const arrayChange = new Set([\n  [].push,\n  [].pop,\n  [].shift,\n  [].unshift,\n  [].splice,\n  [].reverse,\n  [].sort,\n  [].copyWithin,\n]);\n\nexport class KeyVal {\n  constructor(\n    public key: PropertyKey | SpreadObject,\n    public val: unknown,\n  ) {}\n}\n\nexport class SpreadObject {\n  constructor(public item: { [key: string]: unknown }) {}\n}\n\nexport class SpreadArray {\n  constructor(public item: unknown[]) {}\n}\n\nexport class ArrayHole {}\n\nexport class If {\n  constructor(\n    public t: Lisp,\n    public f: Lisp,\n    public label?: string,\n  ) {}\n}\n\nexport const literalRegex = /(\\$\\$)*(\\$)?\\${(\\d+)}/g;\n\nexport { ops, addOps } from './opsRegistry';\nimport { ops, Execution } from './opsRegistry';\n\nexport const prorptyKeyTypes = ['string', 'number', 'symbol'];\n\nexport function isPropertyKey(val: unknown): val is PropertyKey {\n  return prorptyKeyTypes.includes(typeof val);\n}\n\nexport function hasPossibleProperties(val: unknown): val is {} {\n  return val !== null && val !== undefined;\n}\n\nimport './ops/index';\n\nexport function execMany(\n  ticks: Ticks,\n  exec: Execution,\n  tree: Lisp[],\n  done: Done,\n  scope: Scope,\n  context: IExecContext,\n  statementLabels: ControlFlowTargets,\n  internal: boolean,\n  generatorYield: ((yv: YieldValue, done?: Done) => void) | undefined,\n) {\n  if (exec === execSync) {\n    _execManySync(ticks, tree, done, scope, context, statementLabels, internal, generatorYield);\n  } else {\n    _execManyAsync(\n      ticks,\n      tree,\n      done,\n      scope,\n      context,\n      statementLabels,\n      internal,\n      generatorYield,\n    ).catch(done);\n  }\n}\n\nfunction _execManySync(\n  ticks: Ticks,\n  tree: Lisp[],\n  done: Done,\n  scope: Scope,\n  context: IExecContext,\n  statementLabels: ControlFlowTargets,\n  internal: boolean,\n  generatorYield: ((yv: YieldValue, done?: Done) => void) | undefined,\n) {\n  const ret: any[] = [];\n  for (let i = 0; i < tree.length; i++) {\n    let res = syncDone((d) =>\n      execSync(ticks, tree[i], scope, context, d, statementLabels, internal, generatorYield),\n    ).result;\n    if (res instanceof ExecReturn && (res.returned || res.breakLoop || res.continueLoop)) {\n      done(undefined, res);\n      return;\n    }\n    if (isLisp(tree[i]) && tree[i][0] === LispType.Return) {\n      done(undefined, new ExecReturn(context.ctx.auditReport, res, true));\n      return;\n    }\n    ret.push(res);\n  }\n  done(undefined, ret);\n}\n\nasync function _execManyAsync(\n  ticks: Ticks,\n  tree: Lisp[],\n  done: Done,\n  scope: Scope,\n  context: IExecContext,\n  statementLabels: ControlFlowTargets,\n  internal: boolean,\n  generatorYield: ((yv: YieldValue, done?: Done) => void) | undefined,\n) {\n  const ret: any[] = [];\n  for (let i = 0; i < tree.length; i++) {\n    let res;\n    try {\n      let ad: AsyncDoneRet;\n      res =\n        (ad = asyncDone((d) =>\n          execAsync(ticks, tree[i], scope, context, d, statementLabels, internal, generatorYield),\n        )).isInstant === true\n          ? ad.instant\n          : (await ad.p).result;\n    } catch (e) {\n      done(e);\n      return;\n    }\n    if (res instanceof ExecReturn && (res.returned || res.breakLoop || res.continueLoop)) {\n      done(undefined, res);\n      return;\n    }\n    if (isLisp(tree[i]) && tree[i][0] === LispType.Return) {\n      done(undefined, new ExecReturn(context.ctx.auditReport, res, true));\n      return;\n    }\n    ret.push(res);\n  }\n  done(undefined, ret);\n}\n\nexport interface AsyncDoneRet {\n  isInstant: boolean;\n  instant: any;\n  p: Promise<{ result: any }>;\n}\n\nexport function asyncDone(callback: (done: Done) => void): AsyncDoneRet {\n  let isInstant = false;\n  let instant: unknown;\n  const p = new Promise<any>((resolve, reject) => {\n    callback((...args: unknown[]) => {\n      if (args.length === 1) reject(args[0]);\n      else {\n        isInstant = true;\n        instant = args[1];\n        resolve({ result: args[1] });\n      }\n    });\n  });\n  return {\n    isInstant,\n    instant,\n    p,\n  };\n}\n\nexport function syncDone(callback: (done: Done) => void): { result: any } {\n  let result;\n  let err: { error: unknown } | undefined;\n  callback((...args: unknown[]) => {\n    err = args.length === 1 ? { error: args[0] } : undefined;\n    result = args[1];\n  });\n  if (err) throw err.error;\n  return { result };\n}\n\nexport async function execAsync<T = any>(\n  ticks: Ticks,\n  tree: LispItem,\n  scope: Scope,\n  context: IExecContext,\n  doneOriginal: Done<T>,\n  statementLabels: ControlFlowTargets,\n  internal: boolean,\n  generatorYield: ((yv: YieldValue, done?: Done) => void) | undefined,\n): Promise<void> {\n  let done: Done<T> = doneOriginal;\n  const p = new Promise<void>((resolve) => {\n    done = (...args: unknown[]) => {\n      doneOriginal(...args);\n      resolve();\n    };\n  });\n  if (\n    !_execNoneRecurse(\n      ticks,\n      tree,\n      scope,\n      context,\n      done,\n      true,\n      statementLabels,\n      internal,\n      generatorYield,\n    ) &&\n    isLisp(tree)\n  ) {\n    let op = tree[0];\n    let obj;\n    try {\n      let ad: AsyncDoneRet;\n      obj =\n        (ad = asyncDone((d) =>\n          execAsync(ticks, tree[1], scope, context, d, statementLabels, internal, generatorYield),\n        )).isInstant === true\n          ? ad.instant\n          : (await ad.p).result;\n    } catch (e) {\n      done(e);\n      return;\n    }\n    let a = obj;\n    try {\n      a = obj instanceof Prop ? obj.get(context) : obj;\n    } catch (e) {\n      done(e);\n      return;\n    }\n    if (op === LispType.PropOptional || op === LispType.CallOptional) {\n      if (a === undefined || a === null) {\n        done(undefined, optional);\n        return;\n      }\n      op = op === LispType.PropOptional ? LispType.Prop : LispType.Call;\n    }\n    if (a === optional) {\n      if (op === LispType.Prop || op === LispType.Call) {\n        done(undefined, a);\n        return;\n      } else {\n        a = undefined;\n      }\n    }\n    // Short-circuit for nullish coalescing: if a is not null/undefined, return a without evaluating b\n    if (op === LispType.NullishCoalescing && a !== undefined && a !== null) {\n      done(undefined, a);\n      return;\n    }\n    let bobj;\n    try {\n      let ad: AsyncDoneRet;\n      bobj =\n        (ad = asyncDone((d) =>\n          execAsync(ticks, tree[2], scope, context, d, statementLabels, internal, generatorYield),\n        )).isInstant === true\n          ? ad.instant\n          : (await ad.p).result;\n    } catch (e) {\n      done(e);\n      return;\n    }\n    let b = bobj;\n    try {\n      b = bobj instanceof Prop ? bobj.get(context) : bobj;\n    } catch (e) {\n      done(e);\n      return;\n    }\n    if (b === optional) {\n      b = undefined;\n    }\n    performOp({\n      op,\n      exec: execAsync,\n      done,\n      ticks,\n      a,\n      b,\n      obj,\n      context,\n      scope,\n      bobj,\n      statementLabels,\n      internal,\n      generatorYield,\n      tree,\n    });\n  }\n  await p;\n}\n\nexport function execSync<T = any>(\n  ticks: Ticks,\n  tree: LispItem,\n  scope: Scope,\n  context: IExecContext,\n  done: Done<T>,\n  statementLabels: ControlFlowTargets,\n  internal: boolean,\n  generatorYield: ((yv: YieldValue, done?: Done) => void) | undefined,\n) {\n  if (\n    !_execNoneRecurse(\n      ticks,\n      tree,\n      scope,\n      context,\n      done,\n      false,\n      statementLabels,\n      internal,\n      generatorYield,\n    ) &&\n    isLisp(tree)\n  ) {\n    let op = tree[0];\n    let obj = syncDone((d) =>\n      execSync(ticks, tree[1], scope, context, d, statementLabels, internal, generatorYield),\n    ).result;\n    let a = obj instanceof Prop ? obj.get(context) : obj;\n    if (op === LispType.PropOptional || op === LispType.CallOptional) {\n      if (a === undefined || a === null) {\n        done(undefined, optional);\n        return;\n      }\n      op = op === LispType.PropOptional ? LispType.Prop : LispType.Call;\n    }\n    if (a === optional) {\n      if (op === LispType.Prop || op === LispType.Call) {\n        done(undefined, a);\n        return;\n      } else {\n        a = undefined;\n      }\n    }\n    // Short-circuit for nullish coalescing: if a is not null/undefined, return a without evaluating b\n    if (op === LispType.NullishCoalescing && a !== undefined && a !== null) {\n      done(undefined, a);\n      return;\n    }\n    let bobj = syncDone((d) =>\n      execSync(ticks, tree[2], scope, context, d, statementLabels, internal, generatorYield),\n    ).result;\n    let b = bobj instanceof Prop ? bobj.get(context) : bobj;\n    if (b === optional) {\n      b = undefined;\n    }\n    performOp({\n      op,\n      exec: execSync,\n      done,\n      ticks,\n      a,\n      b,\n      obj,\n      context,\n      scope,\n      bobj,\n      statementLabels,\n      internal,\n      generatorYield,\n      tree,\n    });\n  }\n}\n\ntype OpsCallbackParams<a, b, obj, bobj> = {\n  op: LispType;\n  exec: Execution;\n  a: a;\n  b: b;\n  obj: obj;\n  bobj: bobj;\n  ticks: Ticks;\n  tree: LispItem;\n  scope: Scope;\n  context: IExecContext;\n  done: Done;\n  statementLabels: ControlFlowTargets;\n  internal: boolean;\n  generatorYield: ((yv: YieldValue, done?: Done) => void) | undefined;\n};\n\nexport function checkHaltExpectedTicks(\n  params: OpsCallbackParams<any, any, any, any>,\n  expectTicks = 0n,\n): boolean {\n  const sandbox = params.context.ctx.sandbox;\n  const { ticks, scope, context } = params;\n  if (sandbox.halted) {\n    const sub = sandbox.subscribeResume(() => {\n      sub.unsubscribe();\n      performOp(params, false);\n    });\n    return true;\n  } else if (ticks.tickLimit !== undefined && ticks.tickLimit <= ticks.ticks + expectTicks) {\n    const error = new SandboxExecutionQuotaExceededError('Execution quota exceeded');\n    if (context.ctx.options.haltOnSandboxError) {\n      const sub = sandbox.subscribeResume(() => {\n        sub.unsubscribe();\n        performOp(params);\n      });\n      sandbox.haltExecution({\n        type: 'error',\n        error,\n        ticks,\n        scope,\n        context,\n      });\n    } else {\n      params.done(error);\n    }\n    return true;\n  } else if (ticks.nextYield && ticks.ticks > ticks.nextYield) {\n    const sub = sandbox.subscribeResume(() => {\n      sub.unsubscribe();\n      performOp(params, false);\n    });\n    ticks.nextYield += NON_BLOCKING_THRESHOLD;\n    sandbox.haltExecution({ type: 'yield' });\n    setTimeout(() => sandbox.resumeExecution());\n    return true;\n  }\n  ticks.ticks += expectTicks;\n  return false;\n}\n\nfunction performOp(params: OpsCallbackParams<any, any, any, any>, count = true) {\n  const { done, op, ticks, context, scope } = params;\n  if (count) {\n    ticks.ticks++;\n  }\n  const sandbox = context.ctx.sandbox;\n\n  try {\n    if (checkHaltExpectedTicks(params)) {\n      return;\n    }\n    const o = ops.get(op);\n    if (o === undefined) {\n      done(new SandboxExecutionTreeError('Unknown operator: ' + op));\n      return;\n    }\n    o(params);\n  } catch (err) {\n    if (context.ctx.options.haltOnSandboxError && err instanceof SandboxError) {\n      const sub = sandbox.subscribeResume(() => {\n        sub.unsubscribe();\n        done(err);\n      });\n      sandbox.haltExecution({\n        type: 'error',\n        error: err,\n        ticks,\n        scope,\n        context,\n      });\n    } else {\n      done(err);\n    }\n  }\n}\n\nconst unexecTypes = new Set([\n  LispType.ArrowFunction,\n  LispType.Function,\n  LispType.InlineFunction,\n  LispType.Loop,\n  LispType.Try,\n  LispType.Switch,\n  LispType.IfCase,\n  LispType.InlineIfCase,\n  LispType.Labeled,\n  LispType.Typeof,\n]);\n\nfunction _execNoneRecurse<T = any>(\n  ticks: Ticks,\n  tree: LispItem,\n  scope: Scope,\n  context: IExecContext,\n  done: Done<T>,\n  isAsync: boolean,\n  statementLabels: ControlFlowTargets,\n  internal: boolean,\n  generatorYield: ((yv: YieldValue, done?: Done) => void) | undefined,\n): boolean {\n  const exec = isAsync ? execAsync : execSync;\n  if (tree instanceof Prop) {\n    done(undefined, tree.get(context));\n  } else if (tree === optional) {\n    done();\n  } else if (Array.isArray(tree) && !isLisp(tree)) {\n    if (tree[0] === LispType.None) {\n      done();\n    } else {\n      execMany(\n        ticks,\n        exec,\n        tree as Lisp[],\n        done,\n        scope,\n        context,\n        statementLabels,\n        internal,\n        generatorYield,\n      );\n    }\n  } else if (!isLisp(tree)) {\n    done(undefined, tree);\n  } else if (tree[0] === LispType.Block) {\n    execMany(\n      ticks,\n      exec,\n      tree[1] as Lisp[],\n      done,\n      new Scope(scope),\n      context,\n      statementLabels,\n      internal,\n      generatorYield,\n    );\n  } else if (tree[0] === LispType.InternalBlock) {\n    execMany(\n      ticks,\n      exec,\n      tree[1] as Lisp[],\n      done,\n      scope,\n      context,\n      statementLabels,\n      true,\n      generatorYield,\n    );\n  } else if (tree[0] === LispType.Await) {\n    if (!isAsync) {\n      done(new SyntaxError(\"Illegal use of 'await', must be inside async function\"));\n    } else if (context.ctx.prototypeWhitelist?.has(Promise.prototype)) {\n      execAsync(\n        ticks,\n        tree[1],\n        scope,\n        context,\n        async (...args: unknown[]) => {\n          if (args.length === 1) done(args[0]);\n          else\n            try {\n              done(undefined, (await sanitizeProp(args[1], context)) as any);\n            } catch (err) {\n              done(err);\n            }\n        },\n        statementLabels,\n        internal,\n        generatorYield,\n      ).catch(done);\n    } else {\n      done(new SandboxCapabilityError('Async/await is not permitted'));\n    }\n  } else if (tree[0] === LispType.Yield || tree[0] === LispType.YieldDelegate) {\n    const yieldFn = generatorYield;\n    if (!yieldFn) {\n      done(new SyntaxError(\"Illegal use of 'yield', must be inside a generator function\"));\n      return true;\n    }\n    const isDelegate = tree[0] === LispType.YieldDelegate;\n    if (isAsync) {\n      execAsync(\n        ticks,\n        tree[1],\n        scope,\n        context,\n        async (...args: unknown[]) => {\n          if (args.length === 1) {\n            done(args[0]);\n            return;\n          }\n          try {\n            const val = await sanitizeProp(args[1], context);\n            yieldFn(new YieldValue(val, isDelegate), done);\n          } catch (err) {\n            done(err);\n          }\n        },\n        statementLabels,\n        internal,\n        generatorYield,\n      ).catch(done);\n    } else {\n      try {\n        const val = syncDone((d) =>\n          execSync(ticks, tree[1], scope, context, d, statementLabels, internal, generatorYield),\n        ).result;\n        const sanitized = sanitizeProp(val, context);\n        // Pass `done` as second arg so the yieldFn can call it with the injected value.\n        // For capture-mode yieldFns (executeGenBody default case), this enables the restart loop.\n        // For plain yieldFns (eager mode), done? is ignored and done() is called below.\n        yieldFn(new YieldValue(sanitized, isDelegate), done);\n        // If yieldFn did not call done (it threw syncYieldPauseSentinel instead), we fall through\n        // to the catch which re-throws the sentinel. Otherwise yieldFn called done itself.\n      } catch (err) {\n        if (err === syncYieldPauseSentinel) throw err; // propagate pause up to restart loop\n        done(err);\n      }\n    }\n  } else if (unexecTypes.has(tree[0])) {\n    performOp({\n      op: tree[0],\n      exec,\n      done,\n      ticks,\n      a: tree[1],\n      b: tree[2],\n      obj: tree,\n      tree,\n      context,\n      scope,\n      bobj: undefined,\n      statementLabels,\n      internal,\n      generatorYield,\n    });\n  } else {\n    return false;\n  }\n  return true;\n}\nexport function executeTree<T>(\n  ticks: Ticks,\n  context: IExecContext,\n  executionTree: Lisp[],\n  scopes: IScope[] = [],\n  statementLabels: ControlFlowTargets,\n  internal: boolean,\n  generatorYield?: ((yv: YieldValue, done?: Done) => void) | undefined,\n): ExecReturn<T> {\n  return syncDone((done) =>\n    executeTreeWithDone(\n      execSync,\n      done,\n      ticks,\n      context,\n      executionTree,\n      scopes,\n      statementLabels,\n      internal,\n      generatorYield,\n    ),\n  ).result;\n}\n\nexport async function executeTreeAsync<T>(\n  ticks: Ticks,\n  context: IExecContext,\n  executionTree: Lisp[],\n  scopes: IScope[] = [],\n  statementLabels: ControlFlowTargets,\n  internal: boolean,\n  generatorYield?: ((yv: YieldValue, done?: Done) => void) | undefined,\n): Promise<ExecReturn<T>> {\n  let ad: AsyncDoneRet;\n  return (ad = asyncDone((done) =>\n    executeTreeWithDone(\n      execAsync,\n      done,\n      ticks,\n      context,\n      executionTree,\n      scopes,\n      statementLabels,\n      internal,\n      generatorYield,\n    ),\n  )).isInstant === true\n    ? ad.instant\n    : (await ad.p).result;\n}\n\nexport function executeTreeWithDone(\n  exec: Execution,\n  done: Done,\n  ticks: Ticks,\n  context: IExecContext,\n  executionTree: Lisp[],\n  scopes: IScope[] = [],\n  statementLabels: ControlFlowTargets,\n  internal: boolean,\n  generatorYield?: ((yv: YieldValue, done?: Done) => void) | undefined,\n) {\n  if (!executionTree) {\n    done();\n    return;\n  }\n  if (!(executionTree instanceof Array)) {\n    throw new SyntaxError('Bad execution tree');\n  }\n  let scope = context.ctx.globalScope;\n  let s;\n  while ((s = scopes.shift())) {\n    if (typeof s !== 'object') continue;\n    if (s instanceof Scope) {\n      scope = s;\n    } else {\n      scope = new Scope(scope, s, s instanceof LocalScope ? undefined : null);\n    }\n  }\n  if (context.ctx.options.audit && !context.ctx.auditReport) {\n    context.ctx.auditReport = {\n      globalsAccess: new Set(),\n      prototypeAccess: {},\n    };\n  }\n  if (exec === execSync) {\n    _executeWithDoneSync(\n      done,\n      ticks,\n      context,\n      executionTree,\n      scope,\n      statementLabels,\n      internal,\n      generatorYield,\n    );\n  } else {\n    _executeWithDoneAsync(\n      done,\n      ticks,\n      context,\n      executionTree,\n      scope,\n      statementLabels,\n      internal,\n      generatorYield,\n    ).catch(done);\n  }\n}\n\nfunction _executeWithDoneSync(\n  done: Done,\n  ticks: Ticks,\n  context: IExecContext,\n  executionTree: Lisp[],\n  scope: Scope,\n  statementLabels: ControlFlowTargets,\n  internal: boolean,\n  generatorYield: ((yv: YieldValue, done?: Done) => void) | undefined,\n) {\n  if (!(executionTree instanceof Array)) throw new SyntaxError('Bad execution tree');\n  let i = 0;\n  for (i = 0; i < executionTree.length; i++) {\n    let res: unknown;\n    let err: { error: unknown } | undefined;\n    const current = executionTree[i];\n    try {\n      execSync(\n        ticks,\n        current,\n        scope,\n        context,\n        (...args: unknown[]) => {\n          if (args.length === 1) err = { error: args[0] };\n          else res = args[1];\n        },\n        statementLabels,\n        internal,\n        generatorYield,\n      );\n    } catch (e) {\n      err = { error: e };\n    }\n    if (err) {\n      done(err.error);\n      return;\n    }\n    if (res instanceof ExecReturn) {\n      done(undefined, res);\n      return;\n    }\n    if (isLisp(current) && current[0] === LispType.Return) {\n      done(undefined, new ExecReturn(context.ctx.auditReport, res, true));\n      return;\n    }\n  }\n  done(undefined, new ExecReturn(context.ctx.auditReport, undefined, false));\n}\n\nasync function _executeWithDoneAsync(\n  done: Done,\n  ticks: Ticks,\n  context: IExecContext,\n  executionTree: Lisp[],\n  scope: Scope,\n  statementLabels: ControlFlowTargets,\n  internal: boolean,\n  generatorYield: ((yv: YieldValue, done?: Done) => void) | undefined,\n) {\n  if (!(executionTree instanceof Array)) throw new SyntaxError('Bad execution tree');\n  let i = 0;\n  for (i = 0; i < executionTree.length; i++) {\n    let res: unknown;\n    let err: { error: unknown } | undefined;\n    const current = executionTree[i];\n    try {\n      await execAsync(\n        ticks,\n        current,\n        scope,\n        context,\n        (...args: unknown[]) => {\n          if (args.length === 1) err = { error: args[0] };\n          else res = args[1];\n        },\n        statementLabels,\n        internal,\n        generatorYield,\n      );\n    } catch (e) {\n      err = { error: e };\n    }\n    if (err) {\n      done(err.error);\n      return;\n    }\n    if (res instanceof ExecReturn) {\n      done(undefined, res);\n      return;\n    }\n    if (isLisp(current) && current[0] === LispType.Return) {\n      done(undefined, new ExecReturn(context.ctx.auditReport, res, true));\n      return;\n    }\n  }\n  done(undefined, new ExecReturn(context.ctx.auditReport, undefined, false));\n}\n"
  },
  {
    "path": "src/executor/index.ts",
    "content": "export * from './executorUtils';\n"
  },
  {
    "path": "src/executor/ops/assignment.ts",
    "content": "import { addOps, assignCheck, checkHaltExpectedTicks } from '../executorUtils';\nimport { LispType, Prop } from '../../utils';\n\naddOps<unknown, unknown, Prop<any>, Prop<any>>(LispType.Assign, (params) => {\n  const { done, b, obj, context, scope, bobj, internal } = params;\n  assignCheck(obj, context);\n  obj.isGlobal = bobj?.isGlobal || false;\n  if (obj.isVariable) {\n    const s = scope.getWhereValScope(obj.prop as string, obj.prop === 'this', internal);\n    if (s === null) {\n      throw new ReferenceError(`Cannot assign to undeclared variable '${obj.prop.toString()}'`);\n    }\n    s.set(obj.prop as string, b, internal);\n    if (obj.isGlobal) {\n      s.globals[obj.prop.toString()] = true;\n    } else {\n      delete s.globals[obj.prop.toString()];\n    }\n    done(undefined, b);\n    return;\n  }\n  if (obj.prop === 'length' && Array.isArray(obj.context) && typeof b === 'number') {\n    const delta = BigInt(Math.abs(b - obj.context.length));\n    if (delta > 0n && checkHaltExpectedTicks(params, delta)) return;\n  }\n  done(undefined, (obj.context[obj.prop] = b));\n});\n\naddOps<unknown, unknown, Prop<any>>(LispType.AddEquals, (params) => {\n  const { done, b, obj, context } = params;\n  assignCheck(obj, context);\n  const result = (obj.context[obj.prop] as any) + (b as any);\n  if (typeof result === 'string' && checkHaltExpectedTicks(params, BigInt(result.length))) return;\n  done(undefined, (obj.context[obj.prop] = result));\n});\n\naddOps<unknown, number, Prop<any>>(LispType.SubractEquals, ({ done, b, obj, context }) => {\n  assignCheck(obj, context);\n  done(undefined, (obj.context[obj.prop] -= b));\n});\n\naddOps<unknown, number, Prop<any>>(LispType.DivideEquals, ({ done, b, obj, context }) => {\n  assignCheck(obj, context);\n  done(undefined, (obj.context[obj.prop] /= b));\n});\n\naddOps<unknown, number, Prop<any>>(LispType.MultiplyEquals, ({ done, b, obj, context }) => {\n  assignCheck(obj, context);\n  done(undefined, (obj.context[obj.prop] *= b));\n});\n\naddOps<unknown, number, Prop<any>>(LispType.PowerEquals, ({ done, b, obj, context }) => {\n  assignCheck(obj, context);\n  done(undefined, (obj.context[obj.prop] **= b));\n});\n\naddOps<unknown, number, Prop<any>>(LispType.ModulusEquals, ({ done, b, obj, context }) => {\n  assignCheck(obj, context);\n  done(undefined, (obj.context[obj.prop] %= b));\n});\n\naddOps<unknown, number, Prop<any>>(LispType.BitNegateEquals, ({ done, b, obj, context }) => {\n  assignCheck(obj, context);\n  done(undefined, (obj.context[obj.prop] ^= b));\n});\n\naddOps<unknown, number, Prop<any>>(LispType.BitAndEquals, ({ done, b, obj, context }) => {\n  assignCheck(obj, context);\n  done(undefined, (obj.context[obj.prop] &= b));\n});\n\naddOps<unknown, number, Prop<any>>(LispType.BitOrEquals, ({ done, b, obj, context }) => {\n  assignCheck(obj, context);\n  done(undefined, (obj.context[obj.prop] |= b));\n});\n\naddOps<unknown, number, Prop<any>>(LispType.ShiftLeftEquals, ({ done, b, obj, context }) => {\n  assignCheck(obj, context);\n  done(undefined, (obj.context[obj.prop] <<= b));\n});\n\naddOps<unknown, number, Prop<any>>(LispType.ShiftRightEquals, ({ done, b, obj, context }) => {\n  assignCheck(obj, context);\n  done(undefined, (obj.context[obj.prop] >>= b));\n});\n\naddOps<unknown, number, Prop<any>>(\n  LispType.UnsignedShiftRightEquals,\n  ({ done, b, obj, context }) => {\n    assignCheck(obj, context);\n    done(undefined, (obj.context[obj.prop] >>>= b));\n  },\n);\n\naddOps<unknown, unknown, Prop<any>>(LispType.AndEquals, ({ done, b, obj, context }) => {\n  assignCheck(obj, context);\n  done(undefined, (obj.context[obj.prop] &&= b));\n});\n\naddOps<unknown, unknown, Prop<any>>(LispType.OrEquals, ({ done, b, obj, context }) => {\n  assignCheck(obj, context);\n  done(undefined, (obj.context[obj.prop] ||= b));\n});\n\naddOps<unknown, unknown, Prop<any>>(\n  LispType.NullishCoalescingEquals,\n  ({ done, b, obj, context }) => {\n    assignCheck(obj, context);\n    done(undefined, (obj.context[obj.prop] ??= b));\n  },\n);\n"
  },
  {
    "path": "src/executor/ops/call.ts",
    "content": "import { addOps, arrayChange, Change, checkHaltExpectedTicks, SpreadArray } from '../executorUtils';\nimport {\n  checkTicksAndThrow,\n  typedArrayProtos as _typedArrayProtos,\n} from '../../utils/functionReplacements';\nimport type { Lisp } from '../../parser';\nimport {\n  DelayedSynchronousResult,\n  getReplacementReceiver,\n  LispType,\n  SandboxAccessError,\n  SandboxCapabilityError,\n  sanitizeProp,\n} from '../../utils';\n\naddOps<unknown, Lisp[], any>(LispType.Call, (params) => {\n  const { done, a, b, obj, context } = params;\n  if (context.ctx.options.forbidFunctionCalls)\n    throw new SandboxCapabilityError('Function invocations are not allowed');\n  if (typeof a !== 'function') {\n    throw new TypeError(\n      `${typeof obj?.prop === 'symbol' ? 'Symbol' : obj?.prop} is not a function`,\n    );\n  }\n  const vals = new Array<unknown>(b.length);\n  let valsLen = 0;\n  for (let i = 0; i < b.length; i++) {\n    const item = b[i];\n    if (item instanceof SpreadArray) {\n      const expanded = Array.isArray(item.item) ? item.item : [...(item.item as Iterable<unknown>)];\n      if (checkHaltExpectedTicks(params, BigInt(expanded.length))) return;\n      for (let j = 0; j < expanded.length; j++)\n        vals[valsLen++] = sanitizeProp(expanded[j], context);\n    } else {\n      vals[valsLen++] = sanitizeProp(item, context);\n    }\n  }\n  vals.length = valsLen;\n\n  if (a === String) {\n    const result = String(vals[0]);\n    checkTicksAndThrow(context, BigInt(result.length));\n    done(undefined, result);\n    return;\n  }\n\n  if (typeof obj === 'function') {\n    // Direct function call (not a method): obj is the function itself\n    const evl = context.evals.get(obj);\n    const receiver = getReplacementReceiver(obj);\n    let ret = evl\n      ? evl(obj, ...vals)\n      : receiver === undefined\n        ? obj(...vals)\n        : obj.call(receiver, ...vals);\n    ret = sanitizeProp(ret, context);\n    if (ret !== null && typeof ret === 'object' && ret instanceof DelayedSynchronousResult) {\n      Promise.resolve(ret.result).then(\n        (res) => done(undefined, res),\n        (err) => done(err),\n      );\n    } else {\n      done(undefined, ret);\n    }\n    return;\n  }\n\n  // Method call: obj is a Prop. `a` is already the replacement (from Prop.get()).\n  // The original function is still accessible via obj.context[obj.prop] for subscription checks.\n  const originalFn: unknown = obj.context[obj.prop];\n\n  if (originalFn === JSON.stringify && context.getSubscriptions.size) {\n    const cache = new WeakSet<any>();\n    let ticks = 0n;\n    const recurse = (x: unknown) => {\n      if (!x || !(typeof x === 'object') || cache.has(x)) return;\n      cache.add(x);\n      const keys = Object.keys(x) as (keyof typeof x)[];\n      ticks += BigInt(keys.length);\n      for (const y of keys) {\n        context.getSubscriptions.forEach((cb) => cb(x, y));\n        recurse(x[y]);\n      }\n    };\n    recurse(vals[0]);\n    checkTicksAndThrow(context, ticks);\n  }\n\n  if (\n    obj.context instanceof Array &&\n    arrayChange.has(originalFn as any) &&\n    (context.changeSubscriptions.get(obj.context) ||\n      context.changeSubscriptionsGlobal.get(obj.context))\n  ) {\n    let change: Change = undefined!;\n    let changed = false;\n    if (obj.prop === 'push') {\n      change = { type: 'push', added: vals };\n      changed = !!vals.length;\n    } else if (obj.prop === 'pop') {\n      change = { type: 'pop', removed: obj.context.slice(-1) };\n      changed = !!change.removed.length;\n    } else if (obj.prop === 'shift') {\n      change = { type: 'shift', removed: obj.context.slice(0, 1) };\n      changed = !!change.removed.length;\n    } else if (obj.prop === 'unshift') {\n      change = { type: 'unshift', added: vals };\n      changed = !!vals.length;\n    } else if (obj.prop === 'splice') {\n      change = {\n        type: 'splice',\n        startIndex: vals[0] as number,\n        deleteCount: vals[1] === undefined ? obj.context.length : vals[1],\n        added: vals.slice(2),\n        removed: obj.context.slice(\n          vals[0],\n          vals[1] === undefined ? undefined : (vals[0] as number) + (vals[1] as number),\n        ),\n      };\n      changed = !!change.added.length || !!change.removed.length;\n    } else if (obj.prop === 'reverse' || obj.prop === 'sort') {\n      change = { type: obj.prop };\n      changed = !!obj.context.length;\n    } else if (obj.prop === 'copyWithin') {\n      const len =\n        vals[2] === undefined\n          ? obj.context.length - (vals[1] as number)\n          : Math.min(obj.context.length, (vals[2] as number) - (vals[1] as number));\n      change = {\n        type: 'copyWithin',\n        startIndex: vals[0] as number,\n        endIndex: (vals[0] as number) + len,\n        added: obj.context.slice(vals[1] as number, (vals[1] as number) + len),\n        removed: obj.context.slice(vals[0] as number, (vals[0] as number) + len),\n      };\n      changed = !!change.added.length || !!change.removed.length;\n    }\n    if (changed) {\n      const subs = context.changeSubscriptions.get(obj.context);\n      if (subs !== undefined) for (const cb of subs) cb(change);\n      const subsG = context.changeSubscriptionsGlobal.get(obj.context);\n      if (subsG !== undefined) for (const cb of subsG) cb(change);\n    }\n  }\n\n  // Trigger get-subscriptions, then call via `a` (which may be a replacement from evals).\n  // Sandboxed wrappers for globals (Function, eval, etc.) must be called without `this`;\n  // tick-checking replacements must be called with `this`.\n  obj.get(context);\n  const evl = context.evals.get(originalFn as Function);\n  const receiver = getReplacementReceiver(originalFn as Function);\n  const thisArg = obj.isVariable && receiver !== undefined ? receiver : obj.context;\n  let ret = evl ? evl.call(thisArg, ...vals) : (a as Function).call(thisArg, ...vals);\n  ret = sanitizeProp(ret, context);\n  if (ret !== null && typeof ret === 'object' && ret instanceof DelayedSynchronousResult) {\n    Promise.resolve(ret.result).then(\n      (res) => done(undefined, res),\n      (err) => done(err),\n    );\n  } else {\n    done(undefined, ret);\n  }\n});\n\naddOps<new (...args: unknown[]) => void, unknown[]>(LispType.New, (params) => {\n  const { done, a, b, context } = params;\n  if (!context.ctx.globalsWhitelist.has(a) && !context.ctx.sandboxedFunctions.has(a)) {\n    throw new SandboxAccessError(`Object construction not allowed: ${a.constructor.name}`);\n  }\n  const vals = b.map((item) => sanitizeProp(item, context));\n  const replacement = context.evals.get(a);\n  if (replacement) {\n    const ret = new (replacement as new (...args: unknown[]) => unknown)(...vals);\n    done(undefined, ret);\n    return;\n  }\n  const expectedTicks = getNewTicks(a, vals);\n  if (expectedTicks > 0n && checkHaltExpectedTicks(params, expectedTicks)) return;\n  const ret = new a(...vals);\n  done(undefined, ret);\n});\n\nfunction getNewTicks(ctor: Function, args: unknown[]): bigint {\n  // new Array(n) or new TypedArray(n) — allocates n elements\n  if (ctor === Array) {\n    const n = args[0];\n    if (typeof n === 'number' && args.length === 1) return BigInt(n);\n    return BigInt(args.length);\n  }\n  if (_typedArrayProtos.has(Object.getPrototypeOf(ctor.prototype))) {\n    const n = args[0];\n    if (typeof n === 'number') return BigInt(n);\n    if (Array.isArray(n) || ArrayBuffer.isView(n)) return BigInt((n as ArrayLike<unknown>).length);\n    return 0n;\n  }\n  // new Map(iterable) or new Set(iterable) — O(n) of iterable length\n  if (ctor === Map || ctor === Set) {\n    const iterable = args[0];\n    if (Array.isArray(iterable)) return BigInt(iterable.length);\n    return 0n;\n  }\n  // new String(s) or new RegExp(pattern) — O(n) of string length\n  if (ctor === String || ctor === RegExp) {\n    const s = args[0];\n    if (typeof s === 'string') return BigInt(s.length);\n    return 0n;\n  }\n  return 0n;\n}\n"
  },
  {
    "path": "src/executor/ops/comparison.ts",
    "content": "import { addOps, checkHaltExpectedTicks } from '../executorUtils';\nimport { LispType } from '../../utils';\n\naddOps<number, number>(LispType.LargerThan, ({ done, a, b }) => done(undefined, a > b));\n\naddOps<number, number>(LispType.SmallerThan, ({ done, a, b }) => done(undefined, a < b));\n\naddOps<number, number>(LispType.LargerEqualThan, ({ done, a, b }) => done(undefined, a >= b));\n\naddOps<number, number>(LispType.SmallerEqualThan, ({ done, a, b }) => done(undefined, a <= b));\n\naddOps<number, number>(LispType.Equal, ({ done, a, b }) => done(undefined, a == b));\n\naddOps<number, number>(LispType.StrictEqual, ({ done, a, b }) => done(undefined, a === b));\n\naddOps<number, number>(LispType.NotEqual, ({ done, a, b }) => done(undefined, a != b));\n\naddOps<number, number>(LispType.StrictNotEqual, ({ done, a, b }) => done(undefined, a !== b));\n\naddOps<number, number>(LispType.And, ({ done, a, b }) => done(undefined, a && b));\n\naddOps<number, number>(LispType.Or, ({ done, a, b }) => done(undefined, a || b));\n\naddOps<number, number>(LispType.NullishCoalescing, ({ done, a, b }) => done(undefined, a ?? b));\n\naddOps<number, number>(LispType.BitAnd, ({ done, a, b }) => done(undefined, a & b));\n\naddOps<number, number>(LispType.BitOr, ({ done, a, b }) => done(undefined, a | b));\n\naddOps<number, number>(LispType.Plus, (params) => {\n  const { done, a, b } = params;\n  const result = (a as any) + (b as any);\n  if (typeof result === 'string' && checkHaltExpectedTicks(params, BigInt(result.length))) return;\n  done(undefined, result);\n});\n\naddOps<number, number>(LispType.Minus, ({ done, a, b }) => done(undefined, a - b));\n\naddOps<number, number>(LispType.Divide, ({ done, a, b }) => done(undefined, a / b));\n\naddOps<number, number>(LispType.Power, ({ done, a, b }) => done(undefined, a ** b));\n\naddOps<number, number>(LispType.BitNegate, ({ done, a, b }) => done(undefined, a ^ b));\n\naddOps<number, number>(LispType.Multiply, ({ done, a, b }) => done(undefined, a * b));\n\naddOps<number, number>(LispType.Modulus, ({ done, a, b }) => done(undefined, a % b));\n\naddOps<number, number>(LispType.BitShiftLeft, ({ done, a, b }) => done(undefined, a << b));\n\naddOps<number, number>(LispType.BitShiftRight, ({ done, a, b }) => done(undefined, a >> b));\n\naddOps<number, number>(LispType.BitUnsignedShiftRight, ({ done, a, b }) =>\n  done(undefined, a >>> b),\n);\n\naddOps<unknown, { new (): unknown }>(LispType.Instanceof, ({ done, a, b }) =>\n  done(undefined, a instanceof b),\n);\n\naddOps<string, {}>(LispType.In, ({ done, a, b }) => done(undefined, a in b));\n"
  },
  {
    "path": "src/executor/ops/control.ts",
    "content": "import {\n  addOps,\n  execAsync,\n  execSync,\n  asyncDone,\n  executeTreeWithDone,\n  normalizeStatementLabels,\n  addControlFlowTargets,\n  createLoopTarget,\n  createSwitchTarget,\n  createLabeledStatementTarget,\n  matchesControlFlowTarget,\n  If,\n  ExecReturn,\n  executeTreeAsync,\n  syncDone,\n  executeTree,\n  normalizeStatementLabel,\n  addControlFlowTarget,\n} from '../executorUtils';\nimport type { AsyncDoneRet } from '../executorUtils';\nimport type { Lisp, LispItem, StatementLabel, SwitchCase } from '../../parser';\nimport { LispType, Scope, SandboxError, sanitizeProp } from '../../utils';\n\naddOps<Lisp[], Lisp[]>(\n  LispType.Loop,\n  ({ exec, done, ticks, a, b, context, scope, statementLabels, internal, generatorYield }) => {\n    const [\n      checkFirst,\n      startInternal,\n      getIterator,\n      startStep,\n      step,\n      condition,\n      beforeStep,\n      isForAwait,\n      label,\n    ] = a;\n    const loopStatementTargets = [\n      ...normalizeStatementLabels(label).map((loopLabel) => createLoopTarget(loopLabel, false)),\n      createLoopTarget(),\n    ];\n    const loopTargets = addControlFlowTargets(statementLabels, loopStatementTargets);\n    if ((isForAwait as unknown as LispType) === LispType.True && exec !== execAsync) {\n      done(new SyntaxError('for-await-of loops are only allowed inside async functions'));\n      return;\n    }\n    let loop = true;\n    const loopScope = new Scope(scope, {});\n    const internalVars: Record<string, unknown> = {\n      $$obj: undefined,\n    };\n    const interalScope = new Scope(loopScope, internalVars);\n    if (exec === execAsync) {\n      (async () => {\n        let ad: AsyncDoneRet;\n        ad = asyncDone((d) =>\n          exec(ticks, startStep, loopScope, context, d, undefined, internal, generatorYield),\n        );\n        internalVars['$$obj'] =\n          (ad = asyncDone((d) =>\n            exec(ticks, getIterator, loopScope, context, d, undefined, internal, generatorYield),\n          )).isInstant === true\n            ? ad.instant\n            : (await ad.p).result;\n        // for-await-of: override $$obj with the correct async iterator\n        if ((isForAwait as unknown as LispType) === LispType.True) {\n          const obj = internalVars['$$obj'] as any;\n          internalVars['$$obj'] = obj[Symbol.asyncIterator]\n            ? obj[Symbol.asyncIterator]()\n            : obj[Symbol.iterator]\n              ? obj[Symbol.iterator]()\n              : obj;\n        }\n        ad = asyncDone((d) =>\n          exec(ticks, startInternal, interalScope, context, d, undefined, internal, generatorYield),\n        );\n        // for-await-of: await the $$next promise after startInternal sets it\n        if ((isForAwait as unknown as LispType) === LispType.True) {\n          internalVars['$$next'] = await internalVars['$$next'];\n        }\n        if (checkFirst)\n          loop =\n            (ad = asyncDone((d) =>\n              exec(ticks, condition, interalScope, context, d, undefined, internal, generatorYield),\n            )).isInstant === true\n              ? ad.instant\n              : (await ad.p).result;\n        while (loop) {\n          const innerLoopVars = {};\n          const iterScope = new Scope(interalScope, innerLoopVars);\n          ad = asyncDone((d) =>\n            exec(ticks, beforeStep, iterScope, context, d, undefined, internal, generatorYield),\n          );\n          ad.isInstant === true ? ad.instant : (await ad.p).result;\n          const res = await executeTreeAsync(\n            ticks,\n            context,\n            b,\n            [iterScope],\n            loopTargets,\n            internal,\n            generatorYield,\n          );\n          if (res instanceof ExecReturn && res.returned) {\n            done(undefined, res);\n            return;\n          }\n          if (res instanceof ExecReturn && res.controlFlow) {\n            if (\n              !loopStatementTargets.some((target) =>\n                matchesControlFlowTarget(res.controlFlow!, target),\n              )\n            ) {\n              done(undefined, res);\n              return;\n            }\n            if (res.breakLoop) {\n              break;\n            }\n          }\n          ad = asyncDone((d) =>\n            exec(ticks, step, interalScope, context, d, undefined, internal, generatorYield),\n          );\n          // for-await-of: await the $$next promise after step updates it\n          if ((isForAwait as unknown as LispType) === LispType.True) {\n            internalVars['$$next'] = await internalVars['$$next'];\n          }\n          loop =\n            (ad = asyncDone((d) =>\n              exec(ticks, condition, interalScope, context, d, undefined, internal, generatorYield),\n            )).isInstant === true\n              ? ad.instant\n              : (await ad.p).result;\n        }\n        done();\n      })().catch(done);\n    } else {\n      syncDone((d) =>\n        exec(ticks, startStep, loopScope, context, d, undefined, internal, generatorYield),\n      );\n      internalVars['$$obj'] = syncDone((d) =>\n        exec(ticks, getIterator, loopScope, context, d, undefined, internal, generatorYield),\n      ).result;\n      syncDone((d) =>\n        exec(ticks, startInternal, interalScope, context, d, undefined, internal, generatorYield),\n      );\n      if (checkFirst)\n        loop = syncDone((d) =>\n          exec(ticks, condition, interalScope, context, d, undefined, internal, generatorYield),\n        ).result;\n      while (loop) {\n        const innerLoopVars = {};\n        const iterScope = new Scope(interalScope, innerLoopVars);\n        syncDone((d) =>\n          exec(ticks, beforeStep, iterScope, context, d, undefined, internal, generatorYield),\n        );\n        const res = executeTree(\n          ticks,\n          context,\n          b,\n          [iterScope],\n          loopTargets,\n          internal,\n          generatorYield,\n        );\n        if (res instanceof ExecReturn && res.returned) {\n          done(undefined, res);\n          return;\n        }\n        if (res instanceof ExecReturn && res.controlFlow) {\n          if (\n            !loopStatementTargets.some((target) =>\n              matchesControlFlowTarget(res.controlFlow!, target),\n            )\n          ) {\n            done(undefined, res);\n            return;\n          }\n          if (res.breakLoop) {\n            break;\n          }\n        }\n        syncDone((d) =>\n          exec(ticks, step, interalScope, context, d, undefined, internal, generatorYield),\n        );\n        loop = syncDone((d) =>\n          exec(ticks, condition, interalScope, context, d, undefined, internal, generatorYield),\n        ).result;\n      }\n      done();\n    }\n  },\n);\n\naddOps<LispItem, If>(\n  LispType.If,\n  ({ exec, done, ticks, a, b, context, scope, statementLabels, internal, generatorYield }) => {\n    exec(\n      ticks,\n      sanitizeProp(a, context) ? b.t : b.f,\n      scope,\n      context,\n      done,\n      statementLabels,\n      internal,\n      generatorYield,\n    );\n  },\n);\n\naddOps<LispItem, If>(\n  LispType.InlineIf,\n  ({ exec, done, ticks, a, b, context, scope, internal, generatorYield }) => {\n    exec(\n      ticks,\n      sanitizeProp(a, context) ? b.t : b.f,\n      scope,\n      context,\n      done,\n      undefined,\n      internal,\n      generatorYield,\n    );\n  },\n);\n\naddOps<Lisp, Lisp>(LispType.InlineIfCase, ({ done, a, b }) => done(undefined, new If(a, b)));\n\naddOps<Lisp, Lisp>(LispType.IfCase, ({ done, a, b }) => done(undefined, new If(a, b)));\n\naddOps<StatementLabel, Lisp>(\n  LispType.Labeled,\n  ({ exec, done, ticks, a, b, context, scope, statementLabels, internal, generatorYield }) => {\n    const target = createLabeledStatementTarget(normalizeStatementLabel(a));\n    exec(\n      ticks,\n      b,\n      scope,\n      context,\n      (...args: unknown[]) => {\n        if (args.length === 1) {\n          done(args[0]);\n          return;\n        }\n        const res = args[1];\n        if (res instanceof ExecReturn && res.controlFlow && target) {\n          if (matchesControlFlowTarget(res.controlFlow, target)) {\n            done();\n            return;\n          }\n        }\n        done(undefined, res as any);\n      },\n      addControlFlowTarget(statementLabels, target),\n      internal,\n      generatorYield,\n    );\n  },\n);\n\naddOps<LispItem, SwitchCase[]>(\n  LispType.Switch,\n  ({ exec, done, ticks, a, b, context, scope, statementLabels, internal, generatorYield }) => {\n    const switchTarget = createSwitchTarget();\n    const switchTargets = addControlFlowTarget(statementLabels, switchTarget);\n    exec(\n      ticks,\n      a,\n      scope,\n      context,\n      (...args: unknown[]) => {\n        if (args.length === 1) {\n          done(args[0]);\n          return;\n        }\n        let toTest = args[1];\n        toTest = sanitizeProp(toTest, context);\n        if (exec === execSync) {\n          let res: ExecReturn<unknown>;\n          let isTrue = false;\n          for (const caseItem of b) {\n            if (\n              isTrue ||\n              (isTrue =\n                !caseItem[1] ||\n                toTest ===\n                  sanitizeProp(\n                    syncDone((d) =>\n                      exec(\n                        ticks,\n                        caseItem[1],\n                        scope,\n                        context,\n                        d,\n                        undefined,\n                        internal,\n                        generatorYield,\n                      ),\n                    ).result,\n                    context,\n                  ))\n            ) {\n              if (!caseItem[2]) continue;\n              res = executeTree(\n                ticks,\n                context,\n                caseItem[2],\n                [scope],\n                switchTargets,\n                internal,\n                generatorYield,\n              );\n              if (res.controlFlow) {\n                if (!matchesControlFlowTarget(res.controlFlow, switchTarget)) {\n                  done(undefined, res);\n                  return;\n                }\n                if (res.breakLoop) break;\n              }\n              if (res.returned) {\n                done(undefined, res);\n                return;\n              }\n              if (!caseItem[1]) {\n                // default case\n                break;\n              }\n            }\n          }\n          done();\n        } else {\n          (async () => {\n            let res: ExecReturn<unknown>;\n            let isTrue = false;\n            for (const caseItem of b) {\n              let ad: AsyncDoneRet;\n              if (\n                isTrue ||\n                (isTrue =\n                  !caseItem[1] ||\n                  toTest ===\n                    sanitizeProp(\n                      (ad = asyncDone((d) =>\n                        exec(\n                          ticks,\n                          caseItem[1],\n                          scope,\n                          context,\n                          d,\n                          undefined,\n                          internal,\n                          generatorYield,\n                        ),\n                      )).isInstant === true\n                        ? ad.instant\n                        : (await ad.p).result,\n                      context,\n                    ))\n              ) {\n                if (!caseItem[2]) continue;\n                res = await executeTreeAsync(\n                  ticks,\n                  context,\n                  caseItem[2],\n                  [scope],\n                  switchTargets,\n                  internal,\n                  generatorYield,\n                );\n                if (res.controlFlow) {\n                  if (!matchesControlFlowTarget(res.controlFlow, switchTarget)) {\n                    done(undefined, res);\n                    return;\n                  }\n                  if (res.breakLoop) break;\n                }\n                if (res.returned) {\n                  done(undefined, res);\n                  return;\n                }\n                if (!caseItem[1]) {\n                  // default case\n                  break;\n                }\n              }\n            }\n            done();\n          })().catch(done);\n        }\n      },\n      undefined,\n      internal,\n      generatorYield,\n    );\n  },\n);\n\naddOps<Lisp[], [string, Lisp[], Lisp[]]>(\n  LispType.Try,\n  ({ exec, done, ticks, a, b, context, scope, statementLabels, internal, generatorYield }) => {\n    const [exception, catchBody, finallyBody] = b;\n\n    // Execute try block\n    executeTreeWithDone(\n      exec,\n      (...tryArgs: unknown[]) => {\n        const tryHadError = tryArgs.length === 1;\n        const tryError = tryHadError ? tryArgs[0] : undefined;\n        const tryResult = !tryHadError && tryArgs.length > 1 ? tryArgs[1] : undefined;\n\n        // Handler to execute finally and complete\n        const executeFinallyAndComplete = (hadError: boolean, errorOrResult: unknown) => {\n          if (finallyBody && finallyBody.length > 0) {\n            // Execute finally block\n            executeTreeWithDone(\n              exec,\n              (...finallyArgs: unknown[]) => {\n                const finallyHadError = finallyArgs.length === 1;\n                const finallyResult =\n                  !finallyHadError && finallyArgs.length > 1 ? finallyArgs[1] : undefined;\n\n                // If finally throws an error, it overrides everything\n                if (finallyHadError) {\n                  done(finallyArgs[0]);\n                  return;\n                }\n\n                // If finally has a control flow statement (return/break/continue), it overrides everything\n                if (\n                  finallyResult instanceof ExecReturn &&\n                  (finallyResult.returned || finallyResult.breakLoop || finallyResult.continueLoop)\n                ) {\n                  done(undefined, finallyResult);\n                  return;\n                }\n\n                // Otherwise, return the original try/catch result/error\n                if (hadError) {\n                  done(errorOrResult);\n                } else if (errorOrResult instanceof ExecReturn) {\n                  // If try/catch returned or has some other control flow, pass that through\n                  if (\n                    errorOrResult.returned ||\n                    errorOrResult.breakLoop ||\n                    errorOrResult.continueLoop\n                  ) {\n                    done(undefined, errorOrResult);\n                  } else {\n                    // Normal completion - don't return a value\n                    done();\n                  }\n                } else {\n                  // Try/catch completed normally, just signal completion with no return value\n                  done();\n                }\n              },\n              ticks,\n              context,\n              finallyBody,\n              [new Scope(scope, {})],\n              statementLabels,\n              internal,\n              generatorYield,\n            );\n          } else {\n            // No finally block, just return result/error\n            if (hadError) {\n              done(errorOrResult);\n            } else if (errorOrResult instanceof ExecReturn) {\n              // If try/catch returned or has some other control flow, pass that through\n              if (errorOrResult.returned || errorOrResult.breakLoop || errorOrResult.continueLoop) {\n                done(undefined, errorOrResult);\n              } else {\n                // Normal completion - don't return a value\n                done();\n              }\n            } else {\n              done();\n            }\n          }\n        };\n\n        // SandboxErrors bypass both catch and finally — propagate immediately.\n        if (tryHadError && tryError instanceof SandboxError) {\n          done(tryError);\n          return;\n        }\n\n        // If try had an error and there's a catch block, execute catch.\n        if (tryHadError && catchBody && catchBody.length > 0) {\n          const sc: Record<string, unknown> = {};\n          if (exception) sc[exception] = tryError;\n\n          executeTreeWithDone(\n            exec,\n            (...catchArgs: unknown[]) => {\n              const catchHadError = catchArgs.length === 1;\n              const catchErrorOrResult = catchHadError\n                ? catchArgs[0]\n                : catchArgs.length > 1\n                  ? catchArgs[1]\n                  : undefined;\n\n              // Execute finally with catch result\n              executeFinallyAndComplete(catchHadError, catchErrorOrResult);\n            },\n            ticks,\n            context,\n            catchBody,\n            [new Scope(scope, sc)],\n            statementLabels,\n            internal,\n            generatorYield,\n          );\n        } else {\n          // No catch or no error, execute finally with try result\n          executeFinallyAndComplete(tryHadError, tryHadError ? tryError : tryResult);\n        }\n      },\n      ticks,\n      context,\n      a,\n      [new Scope(scope)],\n      statementLabels,\n      internal,\n      generatorYield,\n    );\n  },\n);\n\naddOps<unknown[]>(LispType.Expression, ({ done, a }) => done(undefined, a.pop()));\n"
  },
  {
    "path": "src/executor/ops/functions.ts",
    "content": "import {\n  addOps,\n  createAsyncGeneratorFunction,\n  createFunction,\n  createFunctionAsync,\n  createGeneratorFunction,\n} from '../executorUtils';\nimport type { Lisp } from '../../parser';\nimport { LispType, SandboxCapabilityError, CodeString, Scope, VarType } from '../../utils';\n\naddOps<string[], Lisp[], Lisp>(\n  LispType.ArrowFunction,\n  ({ done, ticks, a, b, obj, context, scope, internal }) => {\n    if (typeof obj[2] === 'string' || obj[2] instanceof CodeString) {\n      if (context.allowJit && context.evalContext) {\n        obj[2] = b = context.evalContext.lispifyFunction(new CodeString(obj[2]), context.constants);\n      } else {\n        throw new SandboxCapabilityError('Unevaluated code detected, JIT not allowed');\n      }\n    }\n    const argNames = a.slice(1);\n    if (a[0]) {\n      done(undefined, createFunctionAsync(argNames, b, ticks, context, scope, undefined, internal));\n    } else {\n      done(undefined, createFunction(argNames, b, ticks, context, scope, undefined, internal));\n    }\n  },\n);\n\naddOps<(string | LispType)[], Lisp[], Lisp>(\n  LispType.Function,\n  ({ done, ticks, a, b, obj, context, scope, internal }) => {\n    if (typeof obj[2] === 'string' || obj[2] instanceof CodeString) {\n      if (context.allowJit && context.evalContext) {\n        obj[2] = b = context.evalContext.lispifyFunction(\n          new CodeString(obj[2]),\n          context.constants,\n          false,\n          {\n            generatorDepth: a[1] === LispType.True ? 1 : 0,\n            asyncDepth: a[0] === LispType.True ? 1 : 0,\n            lispDepth: 0,\n          },\n        );\n      } else {\n        throw new SandboxCapabilityError('Unevaluated code detected, JIT not allowed');\n      }\n    }\n    const isAsync = a[0];\n    const isGenerator = a[1];\n    const name = a[2] as string;\n    const argNames = a.slice(3) as string[];\n    let func;\n    if (isAsync === LispType.True && isGenerator === LispType.True) {\n      func = createAsyncGeneratorFunction(argNames, b, ticks, context, scope, name, internal);\n    } else if (isGenerator === LispType.True) {\n      func = createGeneratorFunction(argNames, b, ticks, context, scope, name, internal);\n    } else if (isAsync === LispType.True) {\n      func = createFunctionAsync(argNames, b, ticks, context, scope, name, internal);\n    } else {\n      func = createFunction(argNames, b, ticks, context, scope, name, internal);\n    }\n    if (name) {\n      scope.declare(name, VarType.var, func, false, internal);\n    }\n    done(undefined, func);\n  },\n);\n\naddOps<(string | LispType)[], Lisp[], Lisp>(\n  LispType.InlineFunction,\n  ({ done, ticks, a, b, obj, context, scope, internal }) => {\n    if (typeof obj[2] === 'string' || obj[2] instanceof CodeString) {\n      if (context.allowJit && context.evalContext) {\n        obj[2] = b = context.evalContext.lispifyFunction(\n          new CodeString(obj[2]),\n          context.constants,\n          false,\n          {\n            generatorDepth: a[1] === LispType.True ? 1 : 0,\n            asyncDepth: a[0] === LispType.True ? 1 : 0,\n            lispDepth: 0,\n          },\n        );\n      } else {\n        throw new SandboxCapabilityError('Unevaluated code detected, JIT not allowed');\n      }\n    }\n    const isAsync = a[0];\n    const isGenerator = a[1];\n    const name = a[2] as string;\n    const argNames = a.slice(3) as string[];\n    if (name) {\n      scope = new Scope(scope, {});\n    }\n    let func;\n    if (isAsync === LispType.True && isGenerator === LispType.True) {\n      func = createAsyncGeneratorFunction(argNames, b, ticks, context, scope, name, internal);\n    } else if (isGenerator === LispType.True) {\n      func = createGeneratorFunction(argNames, b, ticks, context, scope, name, internal);\n    } else if (isAsync === LispType.True) {\n      func = createFunctionAsync(argNames, b, ticks, context, scope, name, internal);\n    } else {\n      func = createFunction(argNames, b, ticks, context, scope, name, internal);\n    }\n    if (name) {\n      scope.declare(name, VarType.let, func, false, internal);\n    }\n    done(undefined, func);\n  },\n);\n"
  },
  {
    "path": "src/executor/ops/index.ts",
    "content": "import './prop';\nimport './call';\nimport './object';\nimport './literals';\nimport './unary';\nimport './assignment';\nimport './comparison';\nimport './variables';\nimport './misc';\nimport './functions';\nimport './control';\n"
  },
  {
    "path": "src/executor/ops/literals.ts",
    "content": "import { addOps, literalRegex, checkHaltExpectedTicks } from '../executorUtils';\nimport type { Lisp, IRegEx } from '../../parser';\nimport { LispType, SandboxCapabilityError, sanitizeProp } from '../../utils';\n\naddOps<unknown, string>(LispType.Number, ({ done, b }) =>\n  done(undefined, Number(b.replace(/_/g, ''))),\n);\n\naddOps<unknown, string>(LispType.BigInt, ({ done, b }) =>\n  done(undefined, BigInt(b.replace(/_/g, ''))),\n);\n\naddOps<unknown, string>(LispType.RegexIndex, ({ done, b, context }) => {\n  const reg: IRegEx = context.constants.regexes[parseInt(b)];\n  if (!context.ctx.globalsWhitelist.has(RegExp)) {\n    throw new SandboxCapabilityError('Regex not permitted');\n  } else {\n    const RegExpCtor =\n      (context.evals.get(RegExp) as\n        | (new (pattern: string, flags?: string) => unknown)\n        | undefined) ?? RegExp;\n    done(undefined, new RegExpCtor(reg.regex, reg.flags));\n  }\n});\n\naddOps<unknown, string>(LispType.LiteralIndex, (params) => {\n  const { exec, done, ticks, b, context, scope, internal, generatorYield } = params;\n  const item = context.constants.literals[parseInt(b)];\n  const [, name, js] = item;\n  const found: Lisp[] = [];\n  let f: RegExpExecArray | null;\n  const resnums: string[] = [];\n  while ((f = literalRegex.exec(name))) {\n    if (!f[2]) {\n      found.push(js[parseInt(f[3], 10)]);\n      resnums.push(f[3]);\n    }\n  }\n\n  exec<unknown[]>(\n    ticks,\n    found,\n    scope,\n    context,\n    (...args: unknown[]) => {\n      const reses: Record<string, unknown> = {};\n      if (args.length === 1) {\n        done(args[0]);\n        return;\n      }\n      const processed = args[1];\n      for (const i of Object.keys(processed!) as (keyof typeof processed)[]) {\n        const num = resnums[i];\n        reses[num] = processed![i];\n      }\n      const result = name.replace(/(\\\\\\\\)*(\\\\)?\\${(\\d+)}/g, (match, $$, $, num) => {\n        if ($) return match;\n        const res = reses[num];\n        return ($$ ? $$ : '') + `${sanitizeProp(res, context)}`;\n      });\n      if (checkHaltExpectedTicks(params, BigInt(result.length))) return;\n      done(undefined, result);\n    },\n    undefined,\n    internal,\n    generatorYield,\n  );\n});\n"
  },
  {
    "path": "src/executor/ops/misc.ts",
    "content": "import {\n  addOps,\n  findControlFlowTarget,\n  ExecReturn,\n  normalizeStatementLabel,\n} from '../executorUtils';\nimport type { ControlFlowAction } from '../executorUtils';\nimport type { LispItem, StatementLabel } from '../../parser';\nimport { LispType, Prop, VarType, SandboxCapabilityError } from '../../utils';\n\naddOps<string, unknown, unknown, Prop>(\n  LispType.Internal,\n  ({ done, a, b, scope, bobj, internal }) => {\n    if (!internal) {\n      throw new SandboxCapabilityError('Internal variables are not accessible');\n    }\n    done(undefined, scope.declare(a, VarType.internal, b, bobj?.isGlobal || false, internal));\n  },\n);\n\naddOps<LispItem, StatementLabel>(\n  LispType.LoopAction,\n  ({ done, a, b, context, statementLabels }) => {\n    const label = normalizeStatementLabel(b);\n    const target = findControlFlowTarget(statementLabels, a as ControlFlowAction, label);\n    if (target === null) {\n      throw new TypeError('Illegal continue statement');\n    }\n    if (!target) {\n      throw new TypeError(label ? `Undefined label '${label}'` : 'Illegal ' + a + ' statement');\n    }\n    done(\n      undefined,\n      new ExecReturn(context.ctx.auditReport, undefined, false, {\n        type: a as ControlFlowAction,\n        label,\n      }),\n    );\n  },\n);\n\naddOps(LispType.Throw, ({ done, b }) => {\n  done(b);\n});\n\naddOps(LispType.None, ({ done }) => done());\n"
  },
  {
    "path": "src/executor/ops/object.ts",
    "content": "import {\n  addOps,\n  checkHaltExpectedTicks,\n  SpreadArray,\n  SpreadObject,\n  KeyVal,\n  ArrayHole,\n} from '../executorUtils';\nimport type { Lisp, LispItem } from '../../parser';\nimport { LispType, sanitizeProp } from '../../utils';\n\naddOps<unknown, KeyVal[]>(LispType.CreateObject, (params) => {\n  const { done, b } = params;\n  let res = {} as any;\n  for (const item of b) {\n    if (item.key instanceof SpreadObject) {\n      const keys = Object.keys(item.key.item);\n      if (checkHaltExpectedTicks(params, BigInt(keys.length))) return;\n      res = { ...res, ...item.key.item };\n    } else {\n      res[item.key] = item.val;\n    }\n  }\n  done(undefined, res);\n});\n\naddOps<PropertyKey, LispItem>(LispType.KeyVal, ({ done, a, b }) =>\n  done(undefined, new KeyVal(a, b)),\n);\n\naddOps<unknown, Lisp[]>(LispType.CreateArray, (params) => {\n  const { done, b, context } = params;\n  const items: unknown[] = [];\n  for (const item of b) {\n    if (item instanceof SpreadArray) {\n      const expanded = Array.isArray(item.item) ? item.item : [...(item.item as Iterable<unknown>)];\n      if (checkHaltExpectedTicks(params, BigInt(expanded.length))) return;\n      for (const v of expanded) items.push(sanitizeProp(v, context));\n    } else if (item instanceof ArrayHole) {\n      items.length++;\n    } else {\n      items.push(sanitizeProp(item, context));\n    }\n  }\n  done(undefined, items);\n});\n\naddOps(LispType.Hole, ({ done }) => done(undefined, new ArrayHole()));\n\naddOps<unknown, unknown>(LispType.Group, ({ done, b }) => done(undefined, b));\n\naddOps<unknown, string>(LispType.GlobalSymbol, ({ done, b }) => {\n  switch (b) {\n    case 'true':\n      return done(undefined, true);\n    case 'false':\n      return done(undefined, false);\n    case 'null':\n      return done(undefined, null);\n    case 'undefined':\n      return done(undefined, undefined);\n    case 'NaN':\n      return done(undefined, NaN);\n    case 'Infinity':\n      return done(undefined, Infinity);\n  }\n  done(new Error('Unknown symbol: ' + b));\n});\n\naddOps<unknown, unknown[]>(LispType.SpreadArray, ({ done, b }) => {\n  done(undefined, new SpreadArray(b));\n});\n\naddOps<unknown, Record<string, unknown>>(LispType.SpreadObject, ({ done, b }) => {\n  done(undefined, new SpreadObject(b));\n});\n"
  },
  {
    "path": "src/executor/ops/prop.ts",
    "content": "import { addOps, hasPossibleProperties, isPropertyKey } from '../executorUtils';\nimport {\n  LispType,\n  Prop,\n  SandboxAccessError,\n  resolveSandboxProp,\n  hasOwnProperty,\n} from '../../utils';\n\naddOps<unknown, PropertyKey>(LispType.Prop, ({ done, a, b, obj, context, scope, internal }) => {\n  if (a === null) {\n    throw new TypeError(`Cannot read properties of null (reading '${b?.toString()}')`);\n  }\n\n  if (!isPropertyKey(b)) {\n    b = `${b}`;\n  }\n\n  if (a === undefined && obj === undefined && typeof b === 'string') {\n    // is variable access\n    const prop = scope.get(b, internal);\n    if (prop.context === undefined) {\n      throw new ReferenceError(`${b} is not defined`);\n    }\n    if (prop.context === context.ctx.sandboxGlobal) {\n      if (context.ctx.options.audit) {\n        context.ctx.auditReport?.globalsAccess.add(b);\n      }\n    }\n    const val = (prop.context as any)[prop.prop];\n    const p = resolveSandboxProp(val, context, prop) || prop;\n\n    done(undefined, p);\n    return;\n  } else if (a === undefined) {\n    throw new TypeError(`Cannot read properties of undefined (reading '${b.toString()}')`);\n  }\n\n  if (!hasPossibleProperties(a)) {\n    done(undefined, new Prop(undefined, b));\n    return;\n  }\n\n  const prototypeAccess = typeof a === 'function' || !hasOwnProperty(a, b);\n\n  if (context.ctx.options.audit && prototypeAccess) {\n    let prot: {} = Object.getPrototypeOf(a);\n    do {\n      if (hasOwnProperty(prot, b)) {\n        if (\n          context.ctx.auditReport &&\n          !context.ctx.auditReport.prototypeAccess[prot.constructor.name]\n        ) {\n          context.ctx.auditReport.prototypeAccess[prot.constructor.name] = new Set();\n        }\n        context.ctx.auditReport?.prototypeAccess[prot.constructor.name].add(b);\n      }\n    } while ((prot = Object.getPrototypeOf(prot)));\n  }\n\n  if (prototypeAccess) {\n    if (typeof a === 'function') {\n      if (hasOwnProperty(a, b)) {\n        const whitelist = context.ctx.prototypeWhitelist.get(a.prototype);\n        if (\n          !(whitelist && (!whitelist.size || whitelist.has(b))) &&\n          !context.ctx.sandboxedFunctions.has(a)\n        ) {\n          throw new SandboxAccessError(\n            `Static method or property access not permitted: ${a.name}.${b.toString()}`,\n          );\n        }\n      }\n    }\n\n    let prot: {} = a;\n    while ((prot = Object.getPrototypeOf(prot))) {\n      if (hasOwnProperty(prot, b) || b === '__proto__') {\n        const whitelist = context.ctx.prototypeWhitelist.get(prot);\n        if (\n          (whitelist && (!whitelist.size || whitelist.has(b))) ||\n          context.ctx.sandboxedFunctions.has(prot.constructor)\n        ) {\n          break;\n        }\n        if (b === '__proto__') {\n          throw new SandboxAccessError(`Access to prototype of global object is not permitted`);\n        }\n        throw new SandboxAccessError(\n          `Method or property access not permitted: ${prot.constructor.name}.${b.toString()}`,\n        );\n      }\n    }\n  }\n\n  if (typeof a === 'function') {\n    if (b === 'prototype' && !context.ctx.sandboxedFunctions.has(a)) {\n      throw new SandboxAccessError(`Access to prototype of global object is not permitted`);\n    }\n    if (['caller', 'callee', 'arguments'].includes(b as string)) {\n      throw new SandboxAccessError(`Access to '${b as string}' property is not permitted`);\n    }\n  }\n  const val = a[b as keyof typeof a] as unknown;\n\n  if (b === '__proto__' && !context.ctx.sandboxedFunctions.has(val?.constructor as any)) {\n    throw new SandboxAccessError(`Access to prototype of global object is not permitted`);\n  }\n\n  const p = resolveSandboxProp(val, context, new Prop(a, b, false, false));\n  if (p) {\n    done(undefined, p);\n    return;\n  }\n\n  const isSandboxGlobal = a === context.ctx.sandboxGlobal;\n  const g =\n    (!isSandboxGlobal && obj instanceof Prop && obj.isGlobal) ||\n    (typeof a === 'function' && !context.ctx.sandboxedFunctions.has(a)) ||\n    context.ctx.globalsWhitelist.has(a) ||\n    (isSandboxGlobal &&\n      typeof b === 'string' &&\n      hasOwnProperty(context.ctx.globalScope.globals, b));\n\n  done(undefined, new Prop(a, b, false, g, false));\n});\n\naddOps<unknown, string>(LispType.StringIndex, ({ done, b, context }) =>\n  done(undefined, context.constants.strings[parseInt(b)]),\n);\n"
  },
  {
    "path": "src/executor/ops/unary.ts",
    "content": "import { addOps, assignCheck } from '../executorUtils';\nimport type { LispItem } from '../../parser';\nimport { LispType, Prop, sanitizeProp } from '../../utils';\n\naddOps<unknown, unknown>(LispType.Not, ({ done, b }) => done(undefined, !b));\n\naddOps<unknown, number>(LispType.Inverse, ({ done, b }) => done(undefined, ~b));\n\naddOps<unknown, unknown, Prop<any>>(LispType.IncrementBefore, ({ done, obj, context }) => {\n  assignCheck(obj, context);\n  done(undefined, ++obj.context[obj.prop]);\n});\n\naddOps<unknown, unknown, Prop<any>>(LispType.IncrementAfter, ({ done, obj, context }) => {\n  assignCheck(obj, context);\n  done(undefined, obj.context[obj.prop]++);\n});\n\naddOps<unknown, unknown, Prop<any>>(LispType.DecrementBefore, ({ done, obj, context }) => {\n  assignCheck(obj, context);\n  done(undefined, --obj.context[obj.prop]);\n});\n\naddOps<unknown, unknown, Prop<any>>(LispType.DecrementAfter, ({ done, obj, context }) => {\n  assignCheck(obj, context);\n  done(undefined, obj.context[obj.prop]--);\n});\n\naddOps<number, number>(LispType.Positive, ({ done, b }) => done(undefined, +b));\n\naddOps<number, number>(LispType.Negative, ({ done, b }) => done(undefined, -b));\n\naddOps<unknown, LispItem>(\n  LispType.Typeof,\n  ({ exec, done, ticks, b, context, scope, internal, generatorYield }) => {\n    exec(\n      ticks,\n      b,\n      scope,\n      context,\n      (e, prop) => {\n        done(undefined, typeof sanitizeProp(prop, context));\n      },\n      undefined,\n      internal,\n      generatorYield,\n    );\n  },\n);\n\naddOps<unknown, unknown>(LispType.Delete, ({ done, context, bobj }) => {\n  if (!(bobj instanceof Prop)) {\n    done(undefined, true);\n    return;\n  }\n  assignCheck(bobj, context, 'delete');\n  if (bobj.isVariable) {\n    done(undefined, false);\n    return;\n  }\n  done(undefined, delete (bobj.context as any)?.[bobj.prop]);\n});\n\naddOps(LispType.Void, ({ done }) => {\n  done();\n});\n"
  },
  {
    "path": "src/executor/ops/variables.ts",
    "content": "import { addOps } from '../executorUtils';\nimport { LispType, Prop, VarType } from '../../utils';\n\naddOps(LispType.Return, ({ done, b }) => done(undefined, b));\n\naddOps<string, unknown, unknown, Prop>(LispType.Var, ({ done, a, b, scope, bobj, internal }) => {\n  done(undefined, scope.declare(a, VarType.var, b, bobj?.isGlobal || false, internal));\n});\n\naddOps<string, unknown, unknown, Prop>(LispType.Let, ({ done, a, b, scope, bobj, internal }) => {\n  done(undefined, scope.declare(a, VarType.let, b, bobj?.isGlobal || false, internal));\n});\n\naddOps<string, unknown, unknown, Prop>(LispType.Const, ({ done, a, b, scope, bobj, internal }) => {\n  done(undefined, scope.declare(a, VarType.const, b, bobj?.isGlobal || false, internal));\n});\n"
  },
  {
    "path": "src/executor/opsRegistry.ts",
    "content": "/**\n * Ops registry — kept separate from executorUtils.ts so that ops/*.ts files\n * can import addOps without creating a circular initialization problem.\n *\n * executorUtils.ts imports from here; ops/*.ts imports from here too.\n * No imports from executorUtils.ts allowed in this file.\n */\nimport { LispType } from '../utils';\nimport type { LispItem } from '../parser';\nimport type { IExecContext, Ticks } from '../utils';\nimport type { Scope } from '../utils';\nimport type { YieldValue, Done } from './executorUtils';\n\nexport type ControlFlowAction = 'break' | 'continue';\n\nexport interface ControlFlowTarget {\n  label?: string;\n  acceptsBreak: boolean;\n  acceptsContinue: boolean;\n  acceptsUnlabeledBreak: boolean;\n  acceptsUnlabeledContinue: boolean;\n}\n\nexport type ControlFlowTargets = readonly ControlFlowTarget[] | undefined;\n\nexport type Execution = <T = any>(\n  ticks: Ticks,\n  tree: LispItem,\n  scope: Scope,\n  context: IExecContext,\n  done: Done<T>,\n  statementLabels: ControlFlowTargets,\n  internal: boolean,\n  generatorYield: ((yv: YieldValue, done?: Done) => void) | undefined,\n) => void;\n\nexport type OpsCallbackParams<a, b, obj, bobj> = {\n  op: LispType;\n  exec: Execution;\n  a: a;\n  b: b;\n  obj: obj;\n  bobj: bobj;\n  ticks: Ticks;\n  tree: LispItem;\n  scope: Scope;\n  context: IExecContext;\n  done: Done;\n  statementLabels: ControlFlowTargets;\n  internal: boolean;\n  generatorYield: ((yv: YieldValue, done?: Done) => void) | undefined;\n};\n\ntype OpCallback<a, b, obj, bobj> = (params: OpsCallbackParams<a, b, obj, bobj>) => void;\n\nexport const ops = new Map<LispType, OpCallback<any, any, any, any>>();\n\nexport function addOps<a = unknown, b = unknown, obj = unknown, bobj = unknown>(\n  type: LispType,\n  cb: OpCallback<a, b, obj, bobj>,\n) {\n  ops.set(type, cb);\n}\n"
  },
  {
    "path": "src/parser/index.ts",
    "content": "export * from './lisp';\nexport * from './parserUtils';\nexport { default } from './parserUtils';\n"
  },
  {
    "path": "src/parser/lisp.ts",
    "content": "import type { CodeString, LispType } from '../utils';\n\nexport type DefineLisp<\n  op extends LispType,\n  a extends LispItem | LispItem,\n  b extends LispItem | LispItem,\n> = [op, a, b];\n\nexport type ExtractLispOp<L> = L extends DefineLisp<infer i, any, any> ? i : never;\nexport type ExtractLispA<L> = L extends DefineLisp<any, infer i, any> ? i : never;\nexport type ExtractLispB<L> = L extends DefineLisp<any, any, infer i> ? i : never;\n\nexport type LispItemSingle = LispType.None | LispType.True | string | Lisp;\nexport type LispItem = LispItemSingle | LispItemSingle[];\nexport type Lisp = [LispType, LispItem, LispItem];\n\nexport type Literal = DefineLisp<LispType.Literal, string, Lisp[]> & { tempJsStrings?: string[] };\nexport type IfLisp = DefineLisp<LispType.If, Lisp, IfCase>;\nexport type InlineIf = DefineLisp<LispType.InlineIf, Lisp, InlineIfCase>;\nexport type StatementLabel = string | LispType.None;\nexport type IfCase = DefineLisp<LispType.IfCase, Lisp[], Lisp[]>;\nexport type InlineIfCase = DefineLisp<LispType.InlineIfCase, Lisp, Lisp>;\nexport type Labeled = DefineLisp<LispType.Labeled, StatementLabel, Lisp>;\nexport type KeyValLisp = DefineLisp<LispType.KeyVal, string | Lisp, Lisp>;\nexport type SpreadObjectLisp = DefineLisp<LispType.SpreadObject, LispType.None, Lisp>;\nexport type SpreadArrayLisp = DefineLisp<LispType.SpreadArray, LispType.None, Lisp>;\nexport type ArrayProp = DefineLisp<LispType.ArrayProp, Lisp, Lisp>;\nexport type PropLisp = DefineLisp<LispType.Prop, Lisp, string | Lisp>;\nexport type PropOptional = DefineLisp<LispType.PropOptional, Lisp, Lisp[]>;\nexport type Call = DefineLisp<LispType.Call, Lisp, Lisp[]>;\nexport type CallOptional = DefineLisp<LispType.CallOptional, Lisp, Lisp[]>;\nexport type CreateArray = DefineLisp<LispType.CreateArray, Lisp, Lisp[]>;\nexport type CreateObject = DefineLisp<LispType.CreateObject, Lisp, Lisp[]>;\nexport type Group = DefineLisp<LispType.Group, Lisp, Lisp[]>;\nexport type Inverse = DefineLisp<LispType.Inverse, Lisp, Lisp>;\nexport type Not = DefineLisp<LispType.Not, Lisp, Lisp>;\nexport type Negative = DefineLisp<LispType.Negative, Lisp, Lisp>;\nexport type Positive = DefineLisp<LispType.Positive, Lisp, Lisp>;\nexport type Typeof = DefineLisp<LispType.Typeof, Lisp, Lisp>;\nexport type Delete = DefineLisp<LispType.Delete, Lisp, Lisp>;\nexport type IncrementBefore = DefineLisp<LispType.IncrementBefore, Lisp, LispType.None>;\nexport type IncrementAfter = DefineLisp<LispType.IncrementAfter, Lisp, LispType.None>;\nexport type DecrementBefore = DefineLisp<LispType.DecrementBefore, Lisp, LispType.None>;\nexport type DecrementAfter = DefineLisp<LispType.DecrementAfter, Lisp, LispType.None>;\n\nexport type And = DefineLisp<LispType.And, Lisp, Lisp>;\nexport type Or = DefineLisp<LispType.Or, Lisp, Lisp>;\nexport type NullishCoalescing = DefineLisp<LispType.NullishCoalescing, Lisp, Lisp>;\nexport type Instanceof = DefineLisp<LispType.Instanceof, Lisp, Lisp>;\nexport type In = DefineLisp<LispType.In, Lisp, Lisp>;\nexport type Assigns = DefineLisp<LispType.Assign, Lisp, Lisp>;\nexport type SubractEquals = DefineLisp<LispType.SubractEquals, Lisp, Lisp>;\nexport type AddEquals = DefineLisp<LispType.AddEquals, Lisp, Lisp>;\nexport type DivideEquals = DefineLisp<LispType.DivideEquals, Lisp, Lisp>;\nexport type PowerEquals = DefineLisp<LispType.PowerEquals, Lisp, Lisp>;\nexport type MultiplyEquals = DefineLisp<LispType.MultiplyEquals, Lisp, Lisp>;\nexport type ModulusEquals = DefineLisp<LispType.ModulusEquals, Lisp, Lisp>;\nexport type BitNegateEquals = DefineLisp<LispType.BitNegateEquals, Lisp, Lisp>;\nexport type BitAndEquals = DefineLisp<LispType.BitAndEquals, Lisp, Lisp>;\nexport type BitOrEquals = DefineLisp<LispType.BitOrEquals, Lisp, Lisp>;\nexport type UnsignedShiftRightEquals = DefineLisp<LispType.UnsignedShiftRightEquals, Lisp, Lisp>;\nexport type ShiftLeftEquals = DefineLisp<LispType.ShiftLeftEquals, Lisp, Lisp>;\nexport type ShiftRightEquals = DefineLisp<LispType.ShiftRightEquals, Lisp, Lisp>;\nexport type AndEquals = DefineLisp<LispType.AndEquals, Lisp, Lisp>;\nexport type OrEquals = DefineLisp<LispType.OrEquals, Lisp, Lisp>;\nexport type NullishCoalescingEquals = DefineLisp<LispType.NullishCoalescingEquals, Lisp, Lisp>;\n\nexport type BitAnd = DefineLisp<LispType.BitAnd, Lisp, Lisp>;\nexport type BitOr = DefineLisp<LispType.BitOr, Lisp, Lisp>;\nexport type BitNegate = DefineLisp<LispType.BitNegate, Lisp, Lisp>;\nexport type BitShiftLeft = DefineLisp<LispType.BitShiftLeft, Lisp, Lisp>;\nexport type BitShiftRight = DefineLisp<LispType.BitShiftRight, Lisp, Lisp>;\nexport type BitUnsignedShiftRight = DefineLisp<LispType.BitUnsignedShiftRight, Lisp, Lisp>;\nexport type SmallerEqualThan = DefineLisp<LispType.SmallerEqualThan, Lisp, Lisp>;\nexport type LargerEqualThan = DefineLisp<LispType.LargerEqualThan, Lisp, Lisp>;\nexport type SmallerThan = DefineLisp<LispType.SmallerThan, Lisp, Lisp>;\nexport type LargerThan = DefineLisp<LispType.LargerThan, Lisp, Lisp>;\nexport type StrictNotEqual = DefineLisp<LispType.StrictNotEqual, Lisp, Lisp>;\nexport type NotEqual = DefineLisp<LispType.NotEqual, Lisp, Lisp>;\nexport type StrictEqual = DefineLisp<LispType.StrictEqual, Lisp, Lisp>;\nexport type Equal = DefineLisp<LispType.Equal, Lisp, Lisp>;\nexport type Plus = DefineLisp<LispType.Plus, Lisp, Lisp>;\nexport type Minus = DefineLisp<LispType.Minus, Lisp, Lisp>;\nexport type Divide = DefineLisp<LispType.Divide, Lisp, Lisp>;\nexport type Power = DefineLisp<LispType.Power, Lisp, Lisp>;\nexport type Multiply = DefineLisp<LispType.Multiply, Lisp, Lisp>;\nexport type Modulus = DefineLisp<LispType.Modulus, Lisp, Lisp>;\n\nexport type InternalCode = DefineLisp<LispType.InternalBlock, Lisp[], LispType.None>;\nexport type Block = DefineLisp<LispType.Block, Lisp[], LispType.None>;\nexport type Expression = DefineLisp<LispType.Expression, Lisp[], LispType.None>;\nexport type Return = DefineLisp<LispType.Return, LispType.None, Lisp>;\nexport type Throw = DefineLisp<LispType.Throw, LispType.None, Lisp>;\nexport type Switch = DefineLisp<LispType.Switch, Lisp, SwitchCase[]>;\nexport type SwitchCase = DefineLisp<LispType.SwitchCase, LispType.None | Lisp, Lisp[]>;\nexport type Var = DefineLisp<LispType.Var, string, Lisp | LispType.None>;\nexport type Let = DefineLisp<LispType.Let, string, Lisp | LispType.None>;\nexport type Const = DefineLisp<LispType.Const, string, Lisp | LispType.None>;\nexport type Internal = DefineLisp<LispType.Internal, string, Lisp | LispType.None>;\n\nexport type Number = DefineLisp<LispType.Number, LispType.None, string>;\nexport type BigInt = DefineLisp<LispType.BigInt, LispType.None, string>;\nexport type GlobalSymbol = DefineLisp<LispType.GlobalSymbol, LispType.None, string>;\nexport type LiteralIndex = DefineLisp<LispType.LiteralIndex, LispType.None, string>;\nexport type StringIndex = DefineLisp<LispType.StringIndex, LispType.None, string>;\nexport type RegexIndex = DefineLisp<LispType.RegexIndex, LispType.None, string>;\n\nexport type Function = DefineLisp<\n  LispType.Function,\n  (string | LispType.None | LispType.True)[],\n  string | Lisp[]\n>;\nexport type InlineFunction = DefineLisp<LispType.InlineFunction, string[], string | Lisp[]>;\nexport type ArrowFunction = DefineLisp<LispType.ArrowFunction, string[], string | Lisp[]>;\nexport type Loop = DefineLisp<LispType.Loop, LispItem, Lisp[]>;\nexport type LoopAction = DefineLisp<LispType.LoopAction, string, StatementLabel>;\nexport type Try = DefineLisp<LispType.Try, Lisp[], LispItem>;\n\nexport type Void = DefineLisp<LispType.Void, Lisp, LispType.None>;\nexport type Await = DefineLisp<LispType.Await, Lisp, LispType.None>;\nexport type Yield = DefineLisp<LispType.Yield, Lisp, LispType.None>;\nexport type YieldDelegate = DefineLisp<LispType.YieldDelegate, Lisp, LispType.None>;\nexport type New = DefineLisp<LispType.New, Lisp, Lisp[]>;\nexport type None = DefineLisp<LispType.None, LispType.None, LispType.None>;\n\nexport type LispFamily =\n  | Literal\n  | IfLisp\n  | InlineIf\n  | IfCase\n  | InlineIfCase\n  | Labeled\n  | KeyValLisp\n  | SpreadObjectLisp\n  | SpreadArrayLisp\n  | ArrayProp\n  | PropLisp\n  | PropOptional\n  | Call\n  | CallOptional\n  | CreateArray\n  | CreateObject\n  | Group\n  | Inverse\n  | Not\n  | Negative\n  | Positive\n  | Typeof\n  | Delete\n  | IncrementBefore\n  | IncrementAfter\n  | DecrementBefore\n  | DecrementAfter\n  | And\n  | Or\n  | NullishCoalescing\n  | Instanceof\n  | In\n  | Assigns\n  | SubractEquals\n  | AddEquals\n  | DivideEquals\n  | PowerEquals\n  | MultiplyEquals\n  | ModulusEquals\n  | BitNegateEquals\n  | BitAndEquals\n  | BitOrEquals\n  | UnsignedShiftRightEquals\n  | ShiftLeftEquals\n  | ShiftRightEquals\n  | AndEquals\n  | OrEquals\n  | NullishCoalescingEquals\n  | BitAnd\n  | BitOr\n  | BitNegate\n  | BitShiftLeft\n  | BitShiftRight\n  | BitUnsignedShiftRight\n  | SmallerEqualThan\n  | LargerEqualThan\n  | SmallerThan\n  | LargerThan\n  | StrictNotEqual\n  | NotEqual\n  | StrictEqual\n  | Equal\n  | Plus\n  | Minus\n  | Divide\n  | Power\n  | Multiply\n  | Modulus\n  | InternalCode\n  | Expression\n  | Return\n  | Throw\n  | Switch\n  | SwitchCase\n  | Var\n  | Let\n  | Const\n  | Number\n  | BigInt\n  | GlobalSymbol\n  | LiteralIndex\n  | StringIndex\n  | RegexIndex\n  | Function\n  | InlineFunction\n  | ArrowFunction\n  | Loop\n  | LoopAction\n  | Try\n  | Void\n  | Await\n  | Yield\n  | YieldDelegate\n  | New\n  | Block\n  | Internal\n  | None;\n\nexport interface IRegEx {\n  regex: string;\n  flags: string;\n  length: number;\n}\n\nexport interface IConstants {\n  strings: string[];\n  literals: Literal[];\n  regexes: IRegEx[];\n  eager: boolean;\n  maxDepth: number;\n}\n\nexport interface IExecutionTree {\n  tree: Lisp[];\n  constants: IConstants;\n}\n\nexport interface LispDepthCtx {\n  generatorDepth: number;\n  asyncDepth: number;\n  lispDepth: number;\n}\n\nexport interface LispCallbackCtx extends LispDepthCtx {\n  constants: IConstants;\n  type: string;\n  part: CodeString;\n  res: string[];\n  expect: string;\n  lispTree: Lisp;\n}\n\nexport type LispCallback<T> = (ctx: LispCallbackCtx & { type: T }) => any;\n"
  },
  {
    "path": "src/parser/lispTypes/conditionals.ts",
    "content": "import { CodeString, LispType, SandboxCapabilityError } from '../../utils';\nimport type { IConstants, IfLisp, IfCase, Lisp, Switch, SwitchCase } from '../lisp';\nimport type { RegisterLispTypesDeps } from './shared';\n\nexport function registerConditionalLispTypes({\n  createLisp,\n  emptyString,\n  extractStatementLabels,\n  insertSemicolons,\n  lispifyBlock,\n  lispifyExpr,\n  restOfExp,\n  semiColon,\n  setLispType,\n  wrapLabeledStatement,\n}: RegisterLispTypesDeps) {\n  const inlineIfElse = /^:/;\n  const elseIf = /^else(?![\\w$])/;\n  const ifElse = /^if(?![\\w$])/;\n\n  function extractIfElse(constants: IConstants, part: CodeString, depth = 0) {\n    if (depth > constants.maxDepth) {\n      throw new SandboxCapabilityError('Maximum expression depth exceeded');\n    }\n    let count = 0;\n    let found = part.substring(0, 0);\n    let foundElse = emptyString;\n    let foundTrue: CodeString | undefined;\n    let first = true;\n    let elseReg: RegExpExecArray | null;\n    let details: any = {};\n    while (\n      (found = restOfExp(\n        constants,\n        part.substring(found.end - part.start),\n        [elseIf, ifElse, semiColon],\n        undefined,\n        undefined,\n        undefined,\n        details,\n      )).length ||\n      first\n    ) {\n      first = false;\n      const f = part.substring(found.end - part.start).toString();\n\n      if (f.startsWith('if')) {\n        found.end++;\n        count++;\n      } else if (f.startsWith('else')) {\n        foundTrue = part.substring(0, found.end - part.start);\n        found.end++;\n        count--;\n        if (!count) {\n          found.end--;\n        }\n      } else if ((elseReg = /^;?\\s*else(?![\\w$])/.exec(f))) {\n        foundTrue = part.substring(0, found.end - part.start);\n        found.end += elseReg[0].length - 1;\n        count--;\n        if (!count) {\n          found.end -= elseReg[0].length - 1;\n        }\n      } else {\n        foundTrue = foundElse.length ? foundTrue : part.substring(0, found.end - part.start);\n        break;\n      }\n      if (!count) {\n        const ie = extractIfElse(\n          constants,\n          part.substring(found.end - part.start + (/^;?\\s*else(?![\\w$])/.exec(f)?.[0].length || 0)),\n          depth + 1,\n        );\n        foundElse = ie.all;\n        break;\n      }\n      details = {};\n    }\n    foundTrue = foundTrue || part.substring(0, found.end - part.start);\n    return {\n      all: part.substring(0, Math.max(foundTrue.end, foundElse.end) - part.start),\n      true: foundTrue,\n      false: foundElse,\n    };\n  }\n\n  setLispType(['if'] as const, (ctx) => {\n    const { constants, part, res } = ctx;\n    const labels = extractStatementLabels(res[1]);\n    let condition = restOfExp(constants, part.substring(res[0].length), [], '(');\n    const ie = extractIfElse(constants, part.substring(res[1].length));\n    const startTrue = res[0].length - res[1].length + condition.length + 1;\n\n    let trueBlock = ie.true.substring(startTrue);\n    let elseBlock = ie.false;\n\n    condition = condition.trim();\n    trueBlock = trueBlock.trim();\n    elseBlock = elseBlock.trim();\n\n    if (!trueBlock.length || /^else(?![\\w$])/.test(trueBlock.toString())) {\n      throw new SyntaxError('Unexpected token');\n    }\n    if (trueBlock.char(0) === '{') trueBlock = trueBlock.slice(1, -1);\n    if (elseBlock.char(0) === '{') elseBlock = elseBlock.slice(1, -1);\n    ctx.lispTree = wrapLabeledStatement(\n      labels,\n      createLisp<IfLisp>({\n        op: LispType.If,\n        a: lispifyExpr(constants, condition, undefined, ctx),\n        b: createLisp<IfCase>({\n          op: LispType.IfCase,\n          a: lispifyBlock(trueBlock, constants, false, ctx),\n          b: lispifyBlock(elseBlock, constants, false, ctx),\n        }),\n      }),\n    ) as Lisp;\n  });\n\n  setLispType(['switch'] as const, (ctx) => {\n    const { constants, part, res } = ctx;\n    const labels = extractStatementLabels(res[1]);\n    const test = restOfExp(constants, part.substring(res[0].length), [], '(');\n    let start = part.toString().indexOf('{', res[0].length + test.length + 1);\n    if (start === -1) throw new SyntaxError('Invalid switch');\n    let statement = insertSemicolons(\n      constants,\n      restOfExp(constants, part.substring(start + 1), [], '{'),\n    );\n    let caseFound: RegExpExecArray | null;\n    const caseTest = /^\\s*(case\\s|default)\\s*/;\n    const caseNoTestReg = /^\\s*case\\s*:/;\n    const cases: SwitchCase[] = [];\n    let defaultFound = false;\n    while (\n      (caseFound = caseTest.exec(statement.toString())) ||\n      caseNoTestReg.test(statement.toString())\n    ) {\n      if (!caseFound) {\n        throw new SyntaxError('Unexpected end of expression');\n      }\n      if (caseFound[1] === 'default') {\n        if (defaultFound) throw new SyntaxError('Only one default switch case allowed');\n        defaultFound = true;\n      }\n      const cond = restOfExp(constants, statement.substring(caseFound[0].length), [/^:/]);\n      if (caseFound[1] !== 'default' && !cond.trimStart().length) {\n        throw new SyntaxError('Unexpected end of expression');\n      }\n      let found = emptyString;\n      let i = (start = caseFound[0].length + cond.length + 1);\n      const bracketFound = /^\\s*\\{/.exec(statement.substring(i).toString());\n      let exprs: Lisp[] = [];\n      if (bracketFound) {\n        i += bracketFound[0].length;\n        found = restOfExp(constants, statement.substring(i), [], '{');\n        i += found.length + 1;\n        exprs = lispifyBlock(found, constants, false, ctx);\n      } else {\n        const notEmpty = restOfExp(constants, statement.substring(i), [caseTest]);\n        if (!notEmpty.trim().length) {\n          exprs = [];\n          i += notEmpty.length;\n        } else {\n          while ((found = restOfExp(constants, statement.substring(i), [semiColon])).length) {\n            i += found.length + (statement.char(i + found.length) === ';' ? 1 : 0);\n            if (caseTest.test(statement.substring(i).toString())) {\n              break;\n            }\n          }\n          exprs = lispifyBlock(\n            statement.substring(start, found.end - statement.start),\n            constants,\n            false,\n            ctx,\n          );\n        }\n      }\n      statement = statement.substring(i);\n      cases.push(\n        createLisp<SwitchCase>({\n          op: LispType.SwitchCase,\n          a:\n            caseFound[1] === 'default'\n              ? LispType.None\n              : lispifyExpr(constants, cond, undefined, ctx),\n          b: exprs,\n        }),\n      );\n    }\n    ctx.lispTree = wrapLabeledStatement(\n      labels,\n      createLisp<Switch>({\n        op: LispType.Switch,\n        a: lispifyExpr(constants, test, undefined, ctx),\n        b: cases,\n      }),\n    ) as Lisp;\n  });\n\n  void inlineIfElse;\n}\n"
  },
  {
    "path": "src/parser/lispTypes/control.ts",
    "content": "import { CodeString, LispType } from '../../utils';\nimport type {\n  Block,\n  InternalCode,\n  Lisp,\n  LispItem,\n  Loop,\n  LoopAction,\n  StatementLabel,\n  Try,\n} from '../lisp';\nimport type { RegisterLispTypesDeps } from './shared';\n\nexport function registerControlLispTypes({\n  ParseError,\n  createLisp,\n  emptyString,\n  expandDestructure,\n  extractStatementLabels,\n  insertSemicolons,\n  lispify,\n  lispifyBlock,\n  lispifyExpr,\n  lispifyReturnExpr,\n  restOfExp,\n  semiColon,\n  setLispType,\n  startingExecpted,\n  wrapLabeledStatement,\n}: RegisterLispTypesDeps) {\n  const iteratorRegex =\n    /^((let|var|const|internal)\\s+)?\\s*([a-zA-Z$_][a-zA-Z\\d$_]*)\\s+(in|of)(?![\\w$])/;\n  const iteratorDestructureRegex = /^((let|var|const|internal)\\s+)\\s*([[{])/;\n\n  const ofStart2 = lispify(\n    { maxDepth: 10 } as any,\n    new CodeString('let $$iterator = $$obj[Symbol.iterator]()'),\n    ['initialize'],\n  );\n  const ofStart3 = lispify(\n    { maxDepth: 10 } as any,\n    new CodeString('let $$next = $$iterator.next()'),\n    ['initialize'],\n  );\n  const ofCondition = lispify({ maxDepth: 10 } as any, new CodeString('return !$$next.done'), [\n    'initialize',\n  ]);\n  const ofStep = lispify({ maxDepth: 10 } as any, new CodeString('$$next = $$iterator.next()'));\n  const asyncOfStart2 = lispify({ maxDepth: 10 } as any, new CodeString('let $$iterator = $$obj'), [\n    'initialize',\n  ]);\n  const asyncOfStart3 = lispify(\n    { maxDepth: 10 } as any,\n    new CodeString('let $$next = $$iterator.next()'),\n    ['initialize'],\n  );\n  const asyncOfStep = lispify(\n    { maxDepth: 10 } as any,\n    new CodeString('$$next = $$iterator.next()'),\n  );\n  const inStart2 = lispify(\n    { maxDepth: 10 } as any,\n    new CodeString('let $$keys = Object.keys($$obj)'),\n    ['initialize'],\n  );\n  const inStart3 = lispify({ maxDepth: 10 } as any, new CodeString('let $$keyIndex = 0'), [\n    'initialize',\n  ]);\n  const inStep = lispify({ maxDepth: 10 } as any, new CodeString('$$keyIndex++'));\n  const inCondition = lispify(\n    { maxDepth: 10 } as any,\n    new CodeString('return $$keyIndex < $$keys.length'),\n    ['initialize'],\n  );\n\n  setLispType(['for', 'do', 'while'] as const, (ctx) => {\n    const { constants, type, part, res } = ctx;\n    const labels = extractStatementLabels(res[1]);\n    let i = 0;\n    let startStep: LispItem = LispType.True;\n    let startInternal: Lisp[] = [];\n    let getIterator: Lisp | LispType.None = LispType.None;\n    let beforeStep: LispItem = LispType.None;\n    let checkFirst = LispType.True;\n    let condition: LispItem;\n    let step: LispItem = LispType.True;\n    let body: CodeString;\n    const isForAwait: LispItem = type === 'for' && res[2] ? LispType.True : LispType.None;\n    switch (type) {\n      case 'while': {\n        i = part.toString().indexOf('(') + 1;\n        const extract = restOfExp(constants, part.substring(i), [], '(');\n        if (!extract.trimStart().length) {\n          throw new SyntaxError('Unexpected end of expression');\n        }\n        condition = lispifyReturnExpr(constants, extract);\n        body = restOfExp(constants, part.substring(i + extract.length + 1)).trim();\n        if (body.char(0) === '{') body = body.slice(1, -1);\n        break;\n      }\n      case 'for': {\n        i = part.toString().indexOf('(') + 1;\n        const args: CodeString[] = [];\n        let extract2 = emptyString;\n        for (let k = 0; k < 3; k++) {\n          extract2 = restOfExp(constants, part.substring(i), [/^[;)]/]);\n          args.push(extract2.trim());\n          i += extract2.length + 1;\n          if (part.char(i - 1) === ')') break;\n        }\n        let iterator: RegExpExecArray | null;\n        let iteratorDestructure: RegExpExecArray | null;\n        if (args.length === 1 && (iterator = iteratorRegex.exec(args[0].toString()))) {\n          const iterableExpr = args[0].substring(iterator[0].length);\n          if (!iterableExpr.trimStart().length) {\n            throw new SyntaxError('Unexpected end of expression');\n          }\n          if (iterator[4] === 'of') {\n            getIterator = lispifyReturnExpr(constants, iterableExpr);\n            startInternal = isForAwait ? [asyncOfStart2, asyncOfStart3] : [ofStart2, ofStart3];\n            condition = ofCondition;\n            step = isForAwait ? asyncOfStep : ofStep;\n            beforeStep = lispify(\n              constants,\n              new CodeString((iterator[1] || 'let ') + iterator[3] + ' = $$next.value'),\n              ['initialize'],\n            );\n          } else {\n            if (isForAwait) {\n              throw new SyntaxError(\"Unexpected token 'in'\");\n            }\n            getIterator = lispifyReturnExpr(constants, iterableExpr);\n            startInternal = [inStart2, inStart3];\n            step = inStep;\n            condition = inCondition;\n            beforeStep = lispify(\n              constants,\n              new CodeString((iterator[1] || 'let ') + iterator[3] + ' = $$keys[$$keyIndex]'),\n              ['initialize'],\n            );\n          }\n        } else if (\n          args.length === 1 &&\n          (iteratorDestructure = iteratorDestructureRegex.exec(args[0].toString()))\n        ) {\n          const keyword = iteratorDestructure[1].trim();\n          const openBracket = iteratorDestructure[3];\n          const closeBracket = openBracket === '[' ? ']' : '}';\n          const keywordPrefixLen = iteratorDestructure[0].length - 1;\n          const patternContent = restOfExp(\n            constants,\n            args[0].substring(keywordPrefixLen + 1),\n            [],\n            openBracket,\n          );\n          const patternStr = openBracket + patternContent.toString() + closeBracket;\n          const afterClose = args[0]\n            .substring(keywordPrefixLen + 1 + patternContent.length + 1)\n            .trimStart();\n          const inOfMatch = /^(in|of)(?![\\w$])\\s*/.exec(afterClose.toString());\n          if (!inOfMatch) throw new SyntaxError('Invalid for loop definition');\n          const inOf = inOfMatch[1];\n          const iterExpr = afterClose.substring(inOfMatch[0].length);\n          if (!iterExpr.trimStart().length) {\n            throw new SyntaxError('Unexpected end of expression');\n          }\n          const tempVarName = '$$_fv';\n          if (inOf === 'of') {\n            getIterator = lispifyReturnExpr(constants, iterExpr);\n            startInternal = isForAwait ? [asyncOfStart2, asyncOfStart3] : [ofStart2, ofStart3];\n            condition = ofCondition;\n            step = isForAwait ? asyncOfStep : ofStep;\n            const expandedCode = expandDestructure(keyword, patternStr, tempVarName);\n            const stmts = lispifyBlock(new CodeString(expandedCode), constants, false, ctx);\n            beforeStep = createLisp<InternalCode>({\n              op: LispType.InternalBlock,\n              a: [\n                lispify(constants, new CodeString(`${keyword} ${tempVarName} = $$next.value`), [\n                  'initialize',\n                ]),\n                ...stmts,\n              ],\n              b: LispType.None,\n            });\n          } else {\n            getIterator = lispifyReturnExpr(constants, iterExpr);\n            startInternal = [inStart2, inStart3];\n            step = inStep;\n            condition = inCondition;\n            const expandedCode = expandDestructure(keyword, patternStr, tempVarName);\n            const stmts = lispifyBlock(new CodeString(expandedCode), constants, false, ctx);\n            beforeStep = createLisp<InternalCode>({\n              op: LispType.InternalBlock,\n              a: [\n                lispify(\n                  constants,\n                  new CodeString(`${keyword} ${tempVarName} = $$keys[$$keyIndex]`),\n                  ['initialize'],\n                ),\n                ...stmts,\n              ],\n              b: LispType.None,\n            });\n          }\n        } else if (args.length === 3) {\n          const [startArg, conditionArg, stepArg] = args;\n          if (startArg.length) {\n            startStep = lispifyExpr(constants, startArg, startingExecpted, ctx);\n          }\n          condition = conditionArg.length\n            ? lispifyReturnExpr(constants, conditionArg)\n            : LispType.True;\n          if (stepArg.length) {\n            step = lispifyExpr(constants, stepArg, undefined, ctx);\n          }\n        } else {\n          throw new SyntaxError('Invalid for loop definition');\n        }\n        body = restOfExp(constants, part.substring(i)).trim();\n        if (body.char(0) === '{') body = body.slice(1, -1);\n        break;\n      }\n      case 'do': {\n        checkFirst = LispType.None;\n        const isBlock = !!res[2];\n        body = restOfExp(constants, part.substring(res[0].length), isBlock ? [/^\\}/] : [semiColon]);\n        condition = lispifyReturnExpr(\n          constants,\n          restOfExp(\n            constants,\n            part.substring(part.toString().indexOf('(', res[0].length + body.length) + 1),\n            [],\n            '(',\n          ),\n        );\n        break;\n      }\n    }\n    const a = [\n      checkFirst,\n      startInternal,\n      getIterator,\n      startStep,\n      step,\n      condition,\n      beforeStep,\n      isForAwait,\n      labels,\n    ] as LispItem;\n    ctx.lispTree = createLisp<Loop>({\n      op: LispType.Loop,\n      a,\n      b: lispifyBlock(body, constants, false, ctx),\n    });\n  });\n\n  setLispType(['block'] as const, (ctx) => {\n    const { constants, part, res } = ctx;\n    const labels = extractStatementLabels(res[1]);\n    ctx.lispTree = wrapLabeledStatement(\n      labels,\n      createLisp<Block>({\n        op: LispType.Block,\n        a: lispifyBlock(\n          restOfExp(constants, part.substring(res[0].length), [], '{'),\n          constants,\n          false,\n          ctx,\n        ),\n        b: LispType.None,\n      }),\n    ) as Lisp;\n  });\n\n  setLispType(['loopAction'] as const, (ctx) => {\n    const { part, res } = ctx;\n    const remaining = part.substring(res[0].length).trimStart();\n    if (remaining.length && !res[2]) {\n      throw new SyntaxError(`Unexpected token '${remaining.char(0)}'`);\n    }\n    ctx.lispTree = createLisp<LoopAction>({\n      op: LispType.LoopAction,\n      a: res[1],\n      b: (res[2] || LispType.None) as StatementLabel,\n    });\n  });\n\n  const catchReg = /^\\s*(catch\\s*(\\(\\s*([a-zA-Z$_][a-zA-Z\\d$_]*)\\s*\\))?|finally)\\s*\\{/;\n  const catchEmptyBindingReg = /^\\s*catch\\s*\\(\\s*\\)/;\n  const catchReservedBindingReg =\n    /^\\s*catch\\s*\\(\\s*(break|case|catch|continue|debugger|default|delete|do|else|finally|for|function|if|in|instanceof|new|return|switch|this|throw|try|typeof|var|void|while|with|class|const|enum|export|extends|implements|import|interface|let|package|private|protected|public|static|super|yield|await)\\s*\\)/;\n  const finallyKeywordReg = /^\\s*finally\\b/;\n\n  setLispType(['try'] as const, (ctx) => {\n    const { constants, part, res } = ctx;\n    const body = restOfExp(constants, part.substring(res[0].length), [], '{');\n    const afterBody = part.substring(res[0].length + body.length + 1).toString();\n\n    if (catchEmptyBindingReg.test(afterBody)) {\n      throw new ParseError(\"Unexpected token ')'\", part.toString());\n    }\n\n    const reservedMatch = catchReservedBindingReg.exec(afterBody);\n    if (reservedMatch) {\n      throw new ParseError(`Unexpected token '${reservedMatch[1]}'`, part.toString());\n    }\n\n    const finallyMatch = finallyKeywordReg.exec(afterBody);\n    if (finallyMatch && !/^\\s*\\{/.test(afterBody.substring(finallyMatch[0].length))) {\n      throw new ParseError('Unexpected token', part.toString());\n    }\n\n    let catchRes = catchReg.exec(afterBody);\n    let finallyBody;\n    let exception = '';\n    let catchBody;\n    let offset = 0;\n\n    if (!catchRes) {\n      throw new ParseError('Missing catch or finally after try', part.toString());\n    }\n\n    if (catchRes[1].startsWith('catch')) {\n      catchRes = catchReg.exec(part.substring(res[0].length + body.length + 1).toString());\n      exception = catchRes![3] || '';\n      catchBody = restOfExp(\n        constants,\n        part.substring(res[0].length + body.length + 1 + catchRes![0].length),\n        [],\n        '{',\n      );\n      offset = res[0].length + body.length + 1 + catchRes![0].length + catchBody.length + 1;\n      if (\n        (catchRes = catchReg.exec(part.substring(offset).toString())) &&\n        catchRes[1].startsWith('finally')\n      ) {\n        finallyBody = restOfExp(constants, part.substring(offset + catchRes[0].length), [], '{');\n      }\n    } else {\n      finallyBody = restOfExp(\n        constants,\n        part.substring(res[0].length + body.length + 1 + catchRes![0].length),\n        [],\n        '{',\n      );\n    }\n    const b = [\n      exception,\n      lispifyBlock(insertSemicolons(constants, catchBody || emptyString), constants, false, ctx),\n      lispifyBlock(insertSemicolons(constants, finallyBody || emptyString), constants, false, ctx),\n    ] as LispItem;\n    ctx.lispTree = wrapLabeledStatement(\n      extractStatementLabels(res[1]),\n      createLisp<Try>({\n        op: LispType.Try,\n        a: lispifyBlock(insertSemicolons(constants, body), constants, false, ctx),\n        b,\n      }),\n    ) as Lisp;\n  });\n}\n"
  },
  {
    "path": "src/parser/lispTypes/declarations.ts",
    "content": "import { CodeString, LispType, reservedWords } from '../../utils';\nimport type {\n  ArrowFunction,\n  Const,\n  Function as FunctionLisp,\n  InlineFunction,\n  Internal,\n  InternalCode,\n  Let,\n  Var,\n} from '../lisp';\nimport type { RegisterLispTypesDeps } from './shared';\n\nexport function registerDeclarationLispTypes({\n  createLisp,\n  expectTypes,\n  expandDestructure,\n  expandFunctionParamDestructure,\n  lispify,\n  lispifyBlock,\n  lispifyFunction,\n  restOfExp,\n  semiColon,\n  setLispType,\n  splitByCommasDestructure,\n}: RegisterLispTypesDeps) {\n  setLispType(['initializeDestructure'] as const, (ctx) => {\n    const { constants, part, res } = ctx;\n    const keyword = res[1] as string;\n    const openBracket = res[2] as string;\n    const closeBracket = openBracket === '[' ? ']' : '}';\n\n    const patternContent = restOfExp(constants, part.substring(res[0].length), [], openBracket);\n    const patternStr = openBracket + patternContent.toString() + closeBracket;\n\n    const afterClose = part.substring(res[0].length + patternContent.length + 1).trimStart();\n    if (!afterClose.length || afterClose.char(0) !== '=') {\n      throw new SyntaxError('Destructuring declaration requires an initializer');\n    }\n\n    const rhsCode = restOfExp(constants, afterClose.substring(1).trimStart(), [semiColon]);\n    const rhsStr = rhsCode.toString();\n    const expandedCode = expandDestructure(keyword, patternStr, rhsStr);\n    const stmts = lispifyBlock(new CodeString(expandedCode), constants, false, ctx);\n\n    ctx.lispTree = createLisp<InternalCode>({\n      op: LispType.InternalBlock,\n      a: stmts,\n      b: LispType.None,\n    });\n  });\n\n  setLispType(['initialize'] as const, (ctx) => {\n    const { constants, part, res, expect } = ctx;\n    const lt =\n      res[1] === 'var'\n        ? LispType.Var\n        : res[1] === 'let'\n          ? LispType.Let\n          : res[1] === 'const'\n            ? LispType.Const\n            : LispType.Internal;\n    if (!res[3]) {\n      ctx.lispTree = lispify(\n        constants,\n        part.substring(res[0].length),\n        expectTypes[expect].next,\n        createLisp<Var | Let | Const | Internal>({\n          op: lt,\n          a: res[2],\n          b: LispType.None,\n        }),\n        false,\n        ctx,\n      );\n    } else {\n      const initExpr = part.substring(res[0].length);\n      if (!initExpr.trimStart().length) {\n        throw new SyntaxError('Unexpected end of expression');\n      }\n      ctx.lispTree = createLisp<Var | Let | Const | Internal>({\n        op: lt,\n        a: res[2],\n        b: lispify(constants, initExpr, expectTypes[expect].next, undefined, false, ctx),\n      });\n    }\n  });\n\n  setLispType(\n    ['function', 'inlineFunction', 'arrowFunction', 'arrowFunctionSingle'] as const,\n    (ctx) => {\n      const { constants, type, part, res, expect, generatorDepth, asyncDepth } = ctx;\n      const isArrow = type !== 'function' && type !== 'inlineFunction';\n      const isReturn = isArrow && !res[res.length - 1];\n      const isGenerator =\n        !isArrow && res[2] && res[2].trimStart().startsWith('*') ? LispType.True : LispType.None;\n      const isAsync = res[1] ? LispType.True : LispType.None;\n      let rawArgStr: string;\n      let bodyOffset: number;\n      if (type === 'function' || type === 'inlineFunction') {\n        const argsCode = restOfExp(constants, part.substring(res[0].length), [], '(');\n        rawArgStr = argsCode.toString().trim();\n        bodyOffset = res[0].length + argsCode.length + 1;\n        const afterParen = part.substring(bodyOffset).trimStart();\n        bodyOffset = part.length - afterParen.length + 1;\n      } else {\n        rawArgStr = res[2] ?? '';\n        bodyOffset = 0;\n      }\n      const rawArgs: string[] = rawArgStr\n        ? splitByCommasDestructure(rawArgStr)\n            .map((a) => a.trim())\n            .filter(Boolean)\n        : [];\n      const args: string[] = [...rawArgs];\n      if (!isArrow) {\n        args.unshift((res[3] || '').trimStart());\n      }\n      let ended = false;\n      args.forEach((arg: string) => {\n        if (ended) throw new SyntaxError('Rest parameter must be last formal parameter');\n        if (arg.startsWith('...')) ended = true;\n      });\n      const f = restOfExp(\n        constants,\n        isArrow ? part.substring(res[0].length) : part.substring(bodyOffset),\n        !isReturn ? [/^}/] : [/^[,)}\\]]/, semiColon],\n      );\n      let funcBody = isReturn ? 'return ' + f : f.toString();\n      const funcArgs = isArrow ? args : args.slice(1);\n      const expanded = expandFunctionParamDestructure(funcArgs, funcBody);\n      const finalArgs = isArrow ? expanded.args : [args[0], ...expanded.args];\n      funcBody = expanded.body;\n      finalArgs.forEach((arg: string) => {\n        if (reservedWords.has(arg.replace(/^\\.\\.\\./, ''))) {\n          throw new SyntaxError(`Unexpected token '${arg}'`);\n        }\n      });\n      const afterFunc = isArrow ? res[0].length + f.length + 1 : bodyOffset + f.length + 1;\n      ctx.lispTree = lispify(\n        constants,\n        part.substring(afterFunc),\n        expectTypes[expect].next,\n        createLisp<FunctionLisp | InlineFunction | ArrowFunction>({\n          op: isArrow\n            ? LispType.ArrowFunction\n            : type === 'function'\n              ? LispType.Function\n              : LispType.InlineFunction,\n          a: isArrow ? [isAsync, ...finalArgs] : [isAsync, isGenerator, ...finalArgs],\n          b: constants.eager\n            ? lispifyFunction(\n                new CodeString(funcBody),\n                constants,\n                false,\n                isArrow\n                  ? { generatorDepth: 0, asyncDepth: 0, lispDepth: 0 }\n                  : {\n                      generatorDepth: isGenerator === LispType.True ? generatorDepth + 1 : 0,\n                      asyncDepth: isAsync === LispType.True ? asyncDepth + 1 : 0,\n                      lispDepth: 0,\n                    },\n              )\n            : funcBody,\n        }),\n        false,\n        ctx,\n      );\n    },\n  );\n}\n"
  },
  {
    "path": "src/parser/lispTypes/index.ts",
    "content": "import type { RegisterLispTypesDeps } from './shared';\nimport { registerConditionalLispTypes } from './conditionals';\nimport { registerControlLispTypes } from './control';\nimport { registerDeclarationLispTypes } from './declarations';\nimport { registerOperatorLispTypes } from './operators';\nimport { registerStructureLispTypes } from './structures';\nimport { registerValueLispTypes } from './values';\n\nexport function registerLispTypes(deps: RegisterLispTypesDeps) {\n  registerStructureLispTypes(deps);\n  registerOperatorLispTypes(deps);\n  registerConditionalLispTypes(deps);\n  registerValueLispTypes(deps);\n  registerDeclarationLispTypes(deps);\n  registerControlLispTypes(deps);\n}\n\nexport type { RegisterLispTypesDeps } from './shared';\n"
  },
  {
    "path": "src/parser/lispTypes/operators.ts",
    "content": "import { CodeString, isLisp, LispType } from '../../utils';\nimport type {\n  AddEquals,\n  And,\n  AndEquals,\n  Assigns,\n  BigInt as BigIntLisp,\n  BitAnd,\n  BitAndEquals,\n  BitNegate,\n  BitNegateEquals,\n  BitOr,\n  BitOrEquals,\n  BitShiftLeft,\n  BitShiftRight,\n  BitUnsignedShiftRight,\n  Call,\n  CreateArray,\n  DecrementAfter,\n  DecrementBefore,\n  Delete,\n  Divide,\n  DivideEquals,\n  Equal,\n  GlobalSymbol,\n  In,\n  IncrementAfter,\n  IncrementBefore,\n  InlineIf,\n  InlineIfCase,\n  Instanceof,\n  InternalCode,\n  Inverse,\n  LargerEqualThan,\n  LargerThan,\n  Lisp,\n  Minus,\n  Modulus,\n  ModulusEquals,\n  Multiply,\n  MultiplyEquals,\n  Negative,\n  Not,\n  NotEqual,\n  NullishCoalescing,\n  NullishCoalescingEquals,\n  Number as NumberLisp,\n  Or,\n  OrEquals,\n  Plus,\n  Positive,\n  Power,\n  PowerEquals,\n  ShiftLeftEquals,\n  ShiftRightEquals,\n  SmallerEqualThan,\n  SmallerThan,\n  StrictEqual,\n  StrictNotEqual,\n  StringIndex,\n  SubractEquals,\n  Typeof,\n  UnsignedShiftRightEquals,\n} from '../lisp';\nimport type { RegisterLispTypesDeps } from './shared';\n\nexport function registerOperatorLispTypes({\n  createLisp,\n  expandDestructure,\n  expectTypes,\n  getDestructurePatternSource,\n  lispifyBlock,\n  restOfExp,\n  lispify,\n  lispifyExpr,\n  setLispType,\n}: RegisterLispTypesDeps) {\n  const inlineIfElse = /^:/;\n\n  const modifierTypes = {\n    inverse: LispType.Inverse,\n    not: LispType.Not,\n    positive: LispType.Positive,\n    negative: LispType.Negative,\n    typeof: LispType.Typeof,\n    delete: LispType.Delete,\n  } as const;\n\n  setLispType(['inverse', 'not', 'negative', 'positive', 'typeof', 'delete'] as const, (ctx) => {\n    const { constants, type, part, res, expect } = ctx;\n    const extract = restOfExp(constants, part.substring(res[0].length), [/^([^\\s.?\\w$]|\\?[^.])/]);\n    const remainingAfterOperand = part.substring(extract.length + res[0].length);\n    const remainingStr = remainingAfterOperand.trim().toString();\n    if (remainingStr.startsWith('**')) {\n      throw new SyntaxError(\n        'Unary operator used immediately before exponentiation expression. Parenthesis must be used to disambiguate operator precedence',\n      );\n    }\n\n    ctx.lispTree = lispify(\n      constants,\n      part.substring(extract.length + res[0].length),\n      restOfExp.next,\n      createLisp<Inverse | Not | Negative | Positive | Typeof | Delete>({\n        op: modifierTypes[type],\n        a: ctx.lispTree,\n        b: lispify(constants, extract, expectTypes[expect].next),\n      }),\n      false,\n      ctx,\n    );\n  });\n\n  setLispType(['taggedTemplate'] as const, (ctx) => {\n    const { constants, part, res, expect } = ctx;\n    const literalIndex = res[1];\n    const literal = constants.literals[parseInt(literalIndex)];\n    const [, templateStr, jsExprs] = literal;\n    const stringParts: string[] = [];\n    const expressions: Lisp[] = [];\n    let currentStr = '';\n    let i = 0;\n\n    while (i < templateStr.length) {\n      if (templateStr.substring(i, i + 2) === '${') {\n        let j = i + 2;\n        let exprIndex = '';\n        let isValidPlaceholder = false;\n        while (j < templateStr.length && templateStr[j] !== '}') {\n          exprIndex += templateStr[j];\n          j++;\n        }\n        if (j < templateStr.length && templateStr[j] === '}' && /^\\d+$/.test(exprIndex)) {\n          isValidPlaceholder = true;\n        }\n\n        if (isValidPlaceholder) {\n          stringParts.push(currentStr);\n          currentStr = '';\n          expressions.push(jsExprs[parseInt(exprIndex)]);\n          i = j + 1;\n        } else {\n          currentStr += templateStr[i];\n          i++;\n        }\n      } else {\n        currentStr += templateStr[i];\n        i++;\n      }\n    }\n    stringParts.push(currentStr);\n\n    const stringsArray = stringParts.map((str) =>\n      createLisp<StringIndex>({\n        op: LispType.StringIndex,\n        a: LispType.None,\n        b: String(constants.strings.push(str) - 1),\n      }),\n    );\n\n    const stringsArrayLisp = createLisp<CreateArray>({\n      op: LispType.CreateArray,\n      a: createLisp({\n        op: LispType.None,\n        a: LispType.None,\n        b: LispType.None,\n      }),\n      b: stringsArray,\n    });\n\n    ctx.lispTree = lispify(\n      constants,\n      part.substring(res[0].length),\n      expectTypes[expect].next,\n      createLisp<Call>({\n        op: LispType.Call,\n        a: ctx.lispTree,\n        b: [stringsArrayLisp, ...expressions],\n      }),\n      false,\n      ctx,\n    );\n  });\n\n  const incrementTypes = {\n    '++$': LispType.IncrementBefore,\n    '--$': LispType.DecrementBefore,\n    '$++': LispType.IncrementAfter,\n    '$--': LispType.DecrementAfter,\n  } as any;\n\n  setLispType(['incrementerBefore'] as const, (ctx) => {\n    const { constants, part, res } = ctx;\n    const extract = restOfExp(constants, part.substring(2), [/^[^\\s.\\w$]/]);\n    ctx.lispTree = lispify(\n      constants,\n      part.substring(extract.length + 2),\n      restOfExp.next,\n      createLisp<IncrementBefore | DecrementBefore>({\n        op: incrementTypes[res[0] + '$'],\n        a: lispify(constants, extract, expectTypes.incrementerBefore.next),\n        b: LispType.None,\n      }),\n      false,\n      ctx,\n    );\n  });\n\n  setLispType(['incrementerAfter'] as const, (ctx) => {\n    const { constants, part, res, expect } = ctx;\n    if (\n      ctx.lispTree[0] === LispType.Number ||\n      ctx.lispTree[0] === LispType.BigInt ||\n      ctx.lispTree[0] === LispType.GlobalSymbol ||\n      ctx.lispTree[0] === LispType.StringIndex ||\n      ctx.lispTree[0] === LispType.LiteralIndex ||\n      ctx.lispTree[0] === LispType.RegexIndex\n    ) {\n      throw new SyntaxError('Invalid left-hand side expression in postfix operation');\n    }\n    ctx.lispTree = lispify(\n      constants,\n      part.substring(res[0].length),\n      expectTypes[expect].next,\n      createLisp<IncrementAfter | DecrementAfter>({\n        op: incrementTypes['$' + res[0]],\n        a: ctx.lispTree,\n        b: LispType.None,\n      }),\n      false,\n      ctx,\n    );\n  });\n\n  const adderTypes = {\n    '&&': LispType.And,\n    '||': LispType.Or,\n    '??': LispType.NullishCoalescing,\n    instanceof: LispType.Instanceof,\n    in: LispType.In,\n    '=': LispType.Assign,\n    '-=': LispType.SubractEquals,\n    '+=': LispType.AddEquals,\n    '/=': LispType.DivideEquals,\n    '**=': LispType.PowerEquals,\n    '*=': LispType.MultiplyEquals,\n    '%=': LispType.ModulusEquals,\n    '^=': LispType.BitNegateEquals,\n    '&=': LispType.BitAndEquals,\n    '|=': LispType.BitOrEquals,\n    '>>>=': LispType.UnsignedShiftRightEquals,\n    '<<=': LispType.ShiftLeftEquals,\n    '>>=': LispType.ShiftRightEquals,\n    '&&=': LispType.AndEquals,\n    '||=': LispType.OrEquals,\n    '??=': LispType.NullishCoalescingEquals,\n  } as any;\n\n  setLispType(['assign', 'assignModify', 'nullishCoalescing'] as const, (ctx) => {\n    const { constants, type, part, res } = ctx;\n    if (\n      type !== 'nullishCoalescing' &&\n      isLisp(ctx.lispTree) &&\n      (ctx.lispTree[0] === LispType.PropOptional || ctx.lispTree[0] === LispType.CallOptional)\n    ) {\n      throw new SyntaxError('Invalid left-hand side in assignment');\n    }\n    if (res[0] === '=') {\n      const patternStr = getDestructurePatternSource(ctx.lispTree);\n      if (patternStr) {\n        const rhsStr = part.substring(res[0].length).toString();\n        const tempName = `$$_da${Math.random().toString(36).slice(2)}`;\n        const expandedCode = `internal ${tempName} = (${rhsStr}); ${expandDestructure('', patternStr, tempName)}; ${tempName}`;\n        ctx.lispTree = createLisp<InternalCode>({\n          op: LispType.InternalBlock,\n          a: lispifyBlock(new CodeString(expandedCode), constants, false, ctx),\n          b: LispType.None,\n        });\n        return;\n      }\n    }\n    ctx.lispTree = createLisp<\n      | NullishCoalescing\n      | Assigns\n      | SubractEquals\n      | AddEquals\n      | DivideEquals\n      | PowerEquals\n      | MultiplyEquals\n      | ModulusEquals\n      | BitNegateEquals\n      | BitAndEquals\n      | BitOrEquals\n      | UnsignedShiftRightEquals\n      | ShiftLeftEquals\n      | ShiftRightEquals\n      | AndEquals\n      | OrEquals\n      | NullishCoalescingEquals\n    >({\n      op: adderTypes[res[0]],\n      a: ctx.lispTree,\n      b: lispify(\n        constants,\n        part.substring(res[0].length),\n        expectTypes.assignment.next,\n        undefined,\n        false,\n        { ...ctx, lispDepth: ctx.lispDepth + 1 },\n      ),\n    });\n  });\n\n  const opTypes = {\n    '&': LispType.BitAnd,\n    '|': LispType.BitOr,\n    '^': LispType.BitNegate,\n    '<<': LispType.BitShiftLeft,\n    '>>': LispType.BitShiftRight,\n    '>>>': LispType.BitUnsignedShiftRight,\n    '<=': LispType.SmallerEqualThan,\n    '>=': LispType.LargerEqualThan,\n    '<': LispType.SmallerThan,\n    '>': LispType.LargerThan,\n    '!==': LispType.StrictNotEqual,\n    '!=': LispType.NotEqual,\n    '===': LispType.StrictEqual,\n    '==': LispType.Equal,\n    '+': LispType.Plus,\n    '-': LispType.Minus,\n    '/': LispType.Divide,\n    '**': LispType.Power,\n    '*': LispType.Multiply,\n    '%': LispType.Modulus,\n    '&&': LispType.And,\n    '||': LispType.Or,\n    instanceof: LispType.Instanceof,\n    in: LispType.In,\n  } as any;\n\n  setLispType(\n    [\n      'power',\n      'opHigh',\n      'op',\n      'comparitor',\n      'bitwiseShift',\n      'bitwiseAnd',\n      'bitwiseXor',\n      'bitwiseOr',\n      'boolOpAnd',\n      'boolOpOr',\n    ] as const,\n    (ctx) => {\n      const { constants, type, part, res } = ctx;\n      const next = [expectTypes.inlineIf.types.inlineIf, inlineIfElse];\n      switch (type) {\n        case 'power':\n          break;\n        case 'opHigh':\n          next.push(expectTypes.splitter.types.opHigh);\n        case 'op':\n          next.push(expectTypes.splitter.types.op);\n        case 'comparitor':\n          next.push(expectTypes.splitter.types.comparitor);\n        case 'bitwiseShift':\n          next.push(expectTypes.splitter.types.bitwiseShift);\n        case 'bitwiseAnd':\n          next.push(expectTypes.splitter.types.bitwiseAnd);\n        case 'bitwiseXor':\n          next.push(expectTypes.splitter.types.bitwiseXor);\n        case 'bitwiseOr':\n          next.push(expectTypes.splitter.types.bitwiseOr);\n        case 'boolOpAnd':\n          next.push(expectTypes.splitter.types.boolOpAnd);\n        case 'boolOpOr':\n          next.push(expectTypes.splitter.types.boolOpOr);\n      }\n      const extract = restOfExp(constants, part.substring(res[0].length), next);\n      ctx.lispTree = lispify(\n        constants,\n        part.substring(extract.length + res[0].length),\n        restOfExp.next,\n        createLisp<\n          | BitAnd\n          | BitOr\n          | BitNegate\n          | BitShiftLeft\n          | BitShiftRight\n          | BitUnsignedShiftRight\n          | SmallerEqualThan\n          | LargerEqualThan\n          | SmallerThan\n          | LargerThan\n          | StrictNotEqual\n          | NotEqual\n          | StrictEqual\n          | Equal\n          | Plus\n          | Minus\n          | Divide\n          | Power\n          | Multiply\n          | Modulus\n          | And\n          | Or\n          | Instanceof\n          | In\n        >({\n          op: opTypes[res[0]],\n          a: ctx.lispTree,\n          b: lispify(constants, extract, expectTypes.splitter.next),\n        }),\n        false,\n        ctx,\n      );\n    },\n  );\n\n  setLispType(['inlineIf'] as const, (ctx) => {\n    const { constants, part, res } = ctx;\n    let found = false;\n    const extract = part.substring(0, 0);\n    let quoteCount = 1;\n    while (!found && extract.length < part.length) {\n      extract.end = restOfExp(constants, part.substring(extract.length + 1), [\n        expectTypes.inlineIf.types.inlineIf,\n        inlineIfElse,\n      ]).end;\n      if (part.char(extract.length) === '?') {\n        quoteCount++;\n      } else {\n        quoteCount--;\n      }\n      if (!quoteCount) {\n        found = true;\n      }\n    }\n    extract.start = part.start + 1;\n    const falseExpr = part.substring(res[0].length + extract.length + 1);\n    if (!falseExpr.trimStart().length) {\n      throw new SyntaxError('Unexpected end of expression');\n    }\n    ctx.lispTree = createLisp<InlineIf>({\n      op: LispType.InlineIf,\n      a: ctx.lispTree,\n      b: createLisp<InlineIfCase>({\n        op: LispType.InlineIfCase,\n        a: lispifyExpr(constants, extract, undefined, { ...ctx, lispDepth: ctx.lispDepth + 1 }),\n        b: lispifyExpr(constants, falseExpr, undefined, { ...ctx, lispDepth: ctx.lispDepth + 1 }),\n      }),\n    });\n  });\n}\n"
  },
  {
    "path": "src/parser/lispTypes/shared.ts",
    "content": "import type { CodeString } from '../../utils';\nimport type {\n  ExtractLispA,\n  ExtractLispB,\n  ExtractLispOp,\n  IConstants,\n  Lisp,\n  LispCallback,\n  LispDepthCtx,\n  LispItem,\n  None,\n  Return,\n} from '../lisp';\n\nexport interface RegisterLispTypesDeps {\n  NullLisp: None;\n  ParseError: new (message: string, code: string) => Error;\n  createLisp: <L extends Lisp>(obj: {\n    op: ExtractLispOp<L>;\n    a: ExtractLispA<L>;\n    b: ExtractLispB<L>;\n  }) => L;\n  emptyString: CodeString;\n  expectTypes: Record<string, { types: Record<string, RegExp>; next: string[] }>;\n  expandDestructure: (keyword: string, patternStr: string, rhsStr: string) => string;\n  expandFunctionParamDestructure: (\n    args: string[],\n    funcBody: string,\n  ) => { args: string[]; body: string };\n  extractStatementLabels: (prefix?: string) => string[];\n  findPatternEndIdx: (s: string) => number;\n  getDestructurePatternSource: (tree: LispItem) => string | null;\n  insertSemicolons: (constants: IConstants, str: CodeString) => CodeString;\n  lispify: (\n    constants: IConstants,\n    part: CodeString,\n    expected?: readonly string[],\n    lispTree?: Lisp,\n    topLevel?: boolean,\n    depthCtx?: LispDepthCtx,\n  ) => Lisp;\n  lispifyBlock: (\n    str: CodeString,\n    constants: IConstants,\n    expression?: boolean,\n    depthCtx?: LispDepthCtx,\n  ) => Lisp[];\n  lispifyExpr: (\n    constants: IConstants,\n    str: CodeString,\n    expected?: readonly string[],\n    depthCtx?: LispDepthCtx,\n  ) => Lisp;\n  lispifyFunction: (\n    str: CodeString,\n    constants: IConstants,\n    expression?: boolean,\n    depthCtx?: LispDepthCtx,\n  ) => Lisp[];\n  lispifyReturnExpr: (constants: IConstants, str: CodeString) => Return;\n  restOfExp: ((\n    constants: IConstants,\n    part: CodeString,\n    tests?: RegExp[],\n    quote?: string,\n    firstOpening?: string,\n    closingsTests?: RegExp[],\n    details?: any,\n    depth?: number,\n  ) => CodeString) & { next: string[] };\n  semiColon: RegExp;\n  setLispType: <T extends readonly string[]>(types: T, fn: LispCallback<T[number]>) => void;\n  splitByCommasDestructure: (s: string) => string[];\n  startingExecpted: readonly string[];\n  wrapLabeledStatement: <T extends Lisp>(labels: string[], statement: T) => Lisp;\n}\n"
  },
  {
    "path": "src/parser/lispTypes/structures.ts",
    "content": "import { CodeString, isLisp, LispType, reservedWords } from '../../utils';\nimport type {\n  ArrayProp,\n  Call,\n  CallOptional,\n  CreateArray,\n  CreateObject,\n  Group,\n  InternalCode,\n  KeyValLisp,\n  Lisp,\n  PropLisp,\n  PropOptional,\n  SpreadArrayLisp,\n  SpreadObjectLisp,\n} from '../lisp';\nimport type { RegisterLispTypesDeps } from './shared';\n\nexport function registerStructureLispTypes({\n  NullLisp,\n  createLisp,\n  emptyString,\n  expectTypes,\n  expandDestructure,\n  findPatternEndIdx,\n  lispify,\n  lispifyBlock,\n  lispifyExpr,\n  restOfExp,\n  setLispType,\n}: RegisterLispTypesDeps) {\n  type LispWithSource = Lisp & { source?: string };\n\n  const closingsCreate: { [type: string]: RegExp } = {\n    createArray: /^\\]/,\n    createObject: /^\\}/,\n    group: /^\\)/,\n    arrayProp: /^\\]/,\n    call: /^\\)/,\n  };\n\n  const typesCreate = {\n    createArray: LispType.CreateArray,\n    createObject: LispType.CreateObject,\n    group: LispType.Group,\n    arrayProp: LispType.ArrayProp,\n    call: LispType.Call,\n    prop: LispType.Prop,\n    '?prop': LispType.PropOptional,\n    '?call': LispType.CallOptional,\n  } as any;\n\n  setLispType(['createArray', 'createObject', 'group', 'arrayProp', 'call'] as const, (ctx) => {\n    const { constants, type, part, res, expect } = ctx;\n    let extract = emptyString;\n    const arg: CodeString[] = [];\n    const argIsHole: boolean[] = [];\n    let end = false;\n    let i = res[0].length;\n    const start = i;\n    while (i < part.length && !end) {\n      extract = restOfExp(constants, part.substring(i), [closingsCreate[type], /^,/]);\n      i += extract.length;\n      if (extract.trim().length) {\n        arg.push(extract);\n        argIsHole.push(false);\n      } else if (type === 'createArray' && part.char(i) === ',') {\n        arg.push(extract);\n        argIsHole.push(true);\n      }\n      if (part.char(i) !== ',') {\n        end = true;\n      } else {\n        if (!extract.trim().length && type === 'call') {\n          throw new SyntaxError('Unexpected end of expression');\n        }\n        if (!extract.trim().length && type === 'createObject') {\n          throw new SyntaxError('Unexpected token ,');\n        }\n        i++;\n      }\n    }\n    const next =\n      type === 'createArray' || type === 'createObject' || type === 'group'\n        ? ['value', 'modifier', 'prop', 'incrementerBefore', 'assignment', 'expEnd']\n        : ['value', 'modifier', 'prop', 'incrementerBefore', 'expEnd'];\n    let l: Lisp | Lisp[];\n\n    let funcFound: RegExpExecArray | null;\n    switch (type) {\n      case 'group': {\n        const groupContent = part.substring(start, i).trim();\n        const groupContentStr = groupContent.toString();\n        if (groupContentStr.startsWith('{') || groupContentStr.startsWith('[')) {\n          const patternEnd = findPatternEndIdx(groupContentStr);\n          const patternStr = groupContentStr.slice(0, patternEnd).trim();\n          const afterPattern = groupContentStr.slice(patternEnd).trimStart();\n          if (afterPattern.startsWith('=')) {\n            const rhsStr = afterPattern.slice(1).trim();\n            const tempName = `$$_da${Math.random().toString(36).slice(2)}`;\n            const expandedCode = `internal ${tempName} = (${rhsStr}); ${expandDestructure('', patternStr, tempName)}; ${tempName}`;\n            l = createLisp<InternalCode>({\n              op: LispType.InternalBlock,\n              a: lispifyBlock(new CodeString(expandedCode), constants, false, ctx),\n              b: LispType.None,\n            });\n            break;\n          }\n        }\n      }\n      case 'arrayProp': {\n        const arrayPropExpr = part.substring(start, i);\n        if (!arrayPropExpr.trimStart().length) {\n          throw new SyntaxError('Unexpected end of expression');\n        }\n        l = lispifyExpr(constants, arrayPropExpr, undefined, {\n          ...ctx,\n          lispDepth: ctx.lispDepth + 1,\n        });\n        break;\n      }\n      case 'call':\n        l = arg.map((e) => lispify(constants, e, [...next, 'spreadArray'], undefined, false, ctx));\n        break;\n      case 'createArray':\n        l = arg.map((e, idx) =>\n          argIsHole[idx]\n            ? createLisp({ op: LispType.Hole, a: LispType.None, b: LispType.None })\n            : lispify(constants, e, [...next, 'spreadArray'], undefined, false, ctx),\n        );\n        break;\n      case 'createObject':\n        l = arg.map((str) => {\n          str = str.trimStart();\n          let value: Lisp;\n          let key: string | Lisp = '';\n          if (str.char(0) === '[') {\n            const innerExpr = restOfExp(constants, str.substring(1), [], '[');\n            const afterBracket = str.substring(1 + innerExpr.length + 1).trimStart();\n            key = lispify(constants, innerExpr, next);\n            if (afterBracket.length > 0 && afterBracket.char(0) === ':') {\n              value = lispify(constants, afterBracket.substring(1));\n            } else if (afterBracket.length > 0 && afterBracket.char(0) === '(') {\n              value = lispify(constants, new CodeString('function' + afterBracket.toString()));\n            } else {\n              throw new SyntaxError('Unexpected token in computed property');\n            }\n            return createLisp<KeyValLisp>({\n              op: LispType.KeyVal,\n              a: key,\n              b: value,\n            });\n          }\n          funcFound = expectTypes.expFunction.types.function.exec('function ' + str);\n          if (funcFound) {\n            key = funcFound[3].trimStart();\n            value = lispify(\n              constants,\n              new CodeString('function ' + str.toString().replace(key, '')),\n            );\n          } else {\n            const extract = restOfExp(constants, str, [/^:/]);\n            key = lispify(constants, extract, [...next, 'spreadObject']) as PropLisp;\n\n            if (isLisp(key) && key[0] === LispType.SpreadObject) {\n              value = NullLisp;\n            } else {\n              if (key[0] === LispType.Prop) {\n                key = (key as PropLisp)[2];\n              }\n\n              if (str.length > extract.length && str.char(extract.length) === ':') {\n                value = lispify(constants, str.substring(extract.length + 1));\n              } else {\n                value = lispify(constants, extract, next);\n              }\n            }\n          }\n          return createLisp<KeyValLisp>({\n            op: LispType.KeyVal,\n            a: key,\n            b: value,\n          });\n        });\n        break;\n    }\n    const lisptype = (\n      type === 'arrayProp'\n        ? res[1]\n          ? LispType.PropOptional\n          : LispType.Prop\n        : type === 'call'\n          ? res[1]\n            ? LispType.CallOptional\n            : LispType.Call\n          : typesCreate[type]\n    ) as (typeof typesCreate)[keyof typeof typesCreate];\n    const currentTree = createLisp<\n      ArrayProp | PropLisp | Call | CreateObject | CreateArray | Group | PropOptional | CallOptional\n    >({\n      op: lisptype,\n      a: ctx.lispTree,\n      b: l,\n    }) as LispWithSource;\n    if (\n      lisptype === LispType.Group ||\n      lisptype === LispType.CreateArray ||\n      lisptype === LispType.CreateObject\n    ) {\n      currentTree.source = part.substring(start, i).toString();\n    }\n    ctx.lispTree = lispify(\n      constants,\n      part.substring(i + 1),\n      expectTypes[expect].next,\n      currentTree,\n      false,\n      ctx,\n    );\n  });\n\n  setLispType(['dot', 'prop'] as const, (ctx) => {\n    const { constants, type, part, res, expect } = ctx;\n    let prop = res[0];\n    let index = res[0].length;\n    let op = 'prop';\n    if (type === 'dot') {\n      if (res[1]) {\n        op = '?prop';\n      }\n      const matches = part.substring(res[0].length).toString().match(expectTypes.prop.types.prop);\n      if (matches && matches.length) {\n        prop = matches[0];\n        index = prop.length + res[0].length;\n      } else {\n        throw new SyntaxError('Hanging dot');\n      }\n    } else if (reservedWords.has(prop) && prop !== 'this') {\n      throw new SyntaxError(`Unexpected token '${prop}'`);\n    }\n    ctx.lispTree = lispify(\n      constants,\n      part.substring(index),\n      expectTypes[expect].next,\n      createLisp<PropLisp | PropOptional>({\n        op: typesCreate[op],\n        a: ctx.lispTree,\n        b: prop,\n      }),\n      false,\n      ctx,\n    );\n  });\n\n  setLispType(['spreadArray', 'spreadObject'] as const, (ctx) => {\n    const { constants, type, part, res, expect } = ctx;\n    ctx.lispTree = createLisp<SpreadArrayLisp | SpreadObjectLisp>({\n      op: type === 'spreadArray' ? LispType.SpreadArray : LispType.SpreadObject,\n      a: LispType.None,\n      b: lispify(constants, part.substring(res[0].length), expectTypes[expect].next),\n    });\n  });\n}\n"
  },
  {
    "path": "src/parser/lispTypes/values.ts",
    "content": "import { LispType } from '../../utils';\nimport type {\n  Await,\n  BigInt as BigIntLisp,\n  GlobalSymbol,\n  LiteralIndex,\n  Number as NumberLisp,\n  RegexIndex,\n  Return,\n  StringIndex,\n  Throw,\n  Void,\n  Yield,\n  YieldDelegate,\n} from '../lisp';\nimport type { RegisterLispTypesDeps } from './shared';\n\nexport function registerValueLispTypes({\n  createLisp,\n  expectTypes,\n  lispify,\n  lispifyExpr,\n  restOfExp,\n  semiColon,\n  setLispType,\n}: RegisterLispTypesDeps) {\n  setLispType(['return', 'throw'] as const, (ctx) => {\n    const { constants, type, part, res } = ctx;\n    const expr = part.substring(res[0].length);\n    if (type === 'throw' && !expr.trimStart().length) {\n      throw new SyntaxError('Unexpected end of expression');\n    }\n    ctx.lispTree = createLisp<Return | Throw>({\n      op: type === 'return' ? LispType.Return : LispType.Throw,\n      a: LispType.None,\n      b: lispifyExpr(constants, expr, undefined, ctx),\n    });\n  });\n\n  setLispType(['number', 'boolean', 'null', 'und', 'NaN', 'Infinity'] as const, (ctx) => {\n    const { constants, type, part, res, expect } = ctx;\n    ctx.lispTree = lispify(\n      constants,\n      part.substring(res[0].length),\n      expectTypes[expect].next,\n      createLisp<NumberLisp | BigIntLisp | GlobalSymbol>({\n        op:\n          type === 'number' ? (res[12] ? LispType.BigInt : LispType.Number) : LispType.GlobalSymbol,\n        a: LispType.None,\n        b: res[12] ? res[1] : res[0],\n      }),\n      false,\n      ctx,\n    );\n  });\n\n  setLispType(['string', 'literal', 'regex'] as const, (ctx) => {\n    const { constants, type, part, res, expect } = ctx;\n    ctx.lispTree = lispify(\n      constants,\n      part.substring(res[0].length),\n      expectTypes[expect].next,\n      createLisp<StringIndex | LiteralIndex | RegexIndex>({\n        op:\n          type === 'string'\n            ? LispType.StringIndex\n            : type === 'literal'\n              ? LispType.LiteralIndex\n              : LispType.RegexIndex,\n        a: LispType.None,\n        b: res[1],\n      }),\n      false,\n      ctx,\n    );\n  });\n\n  setLispType(['void', 'await'] as const, (ctx) => {\n    const { constants, type, part, res, expect } = ctx;\n    const extract = restOfExp(constants, part.substring(res[0].length), [/^([^\\s.?\\w$]|\\?[^.])/]);\n    if (!extract.trimStart().length) {\n      throw new SyntaxError('Unexpected end of expression');\n    }\n    ctx.lispTree = lispify(\n      constants,\n      part.substring(res[0].length + extract.length),\n      expectTypes[expect].next,\n      createLisp<Void | Await>({\n        op: type === 'void' ? LispType.Void : LispType.Await,\n        a: lispify(constants, extract),\n        b: LispType.None,\n      }),\n      false,\n      ctx,\n    );\n  });\n\n  setLispType(['yield'] as const, (ctx) => {\n    const { constants, part, res, expect, generatorDepth } = ctx;\n    if (generatorDepth === 0) {\n      throw new SyntaxError('Unexpected token');\n    }\n    const isDelegate = res[0].trimEnd().endsWith('*');\n    const extract = restOfExp(constants, part.substring(res[0].length), [/^([^\\s.?\\w$]|\\?[^.])/]);\n    if (isDelegate && !extract.trimStart().length) {\n      throw new SyntaxError('Unexpected end of expression');\n    }\n    ctx.lispTree = lispify(\n      constants,\n      part.substring(res[0].length + extract.length),\n      expectTypes[expect].next,\n      createLisp<Yield | YieldDelegate>({\n        op: isDelegate ? LispType.YieldDelegate : LispType.Yield,\n        a: lispify(constants, extract),\n        b: LispType.None,\n      }),\n      false,\n      ctx,\n    );\n  });\n\n  setLispType(['new'] as const, (ctx) => {\n    const { constants, part, res } = ctx;\n    let i = res[0].length;\n    const obj = restOfExp(constants, part.substring(i), [], undefined, '(');\n    if (!obj.trimStart().length) {\n      throw new SyntaxError('Unexpected end of expression');\n    }\n    i += obj.length + 1;\n    const args = [];\n    if (part.char(i - 1) === '(') {\n      const argsString = restOfExp(constants, part.substring(i), [], '(');\n      i += argsString.length + 1;\n      let found;\n      let j = 0;\n      while ((found = restOfExp(constants, argsString.substring(j), [/^,/])).length) {\n        j += found.length + 1;\n        args.push(found.trim());\n      }\n    }\n    ctx.lispTree = lispify(\n      constants,\n      part.substring(i),\n      expectTypes.expEdge.next,\n      createLisp({\n        op: LispType.New,\n        a: lispify(constants, obj, expectTypes.initialize.next),\n        b: args.map((arg) => lispify(constants, arg, expectTypes.initialize.next)),\n      }),\n      false,\n      ctx,\n    );\n  });\n\n  void semiColon;\n}\n"
  },
  {
    "path": "src/parser/parserUtils.ts",
    "content": "import { unraw } from '../utils/unraw';\nimport { CodeString, isLisp, LispType, SandboxCapabilityError } from '../utils';\nimport { registerLispTypes } from './lispTypes';\nimport type {\n  Expression,\n  ExtractLispA,\n  ExtractLispB,\n  ExtractLispOp,\n  IConstants,\n  IExecutionTree,\n  IRegEx,\n  InternalCode,\n  Labeled,\n  Lisp,\n  LispCallback,\n  LispCallbackCtx,\n  LispDepthCtx,\n  LispFamily,\n  LispItem,\n  LispItemSingle,\n  Literal,\n  None,\n  Return,\n} from './lisp';\n\ntype LispWithSource = Lisp & { source?: string };\n\nfunction createLisp<L extends Lisp>(obj: {\n  op: ExtractLispOp<L>;\n  a: ExtractLispA<L>;\n  b: ExtractLispB<L>;\n}) {\n  return [obj.op, obj.a, obj.b] as L;\n}\n\nconst NullLisp = createLisp<None>({ op: LispType.None, a: LispType.None, b: LispType.None });\n\nconst statementLabelRegex = /([a-zA-Z$_][\\w$]*)\\s*:/g;\n\nfunction extractStatementLabels(prefix = '') {\n  return [...prefix.matchAll(statementLabelRegex)].map((match) => match[1]);\n}\n\nfunction wrapLabeledStatement<T extends Lisp>(labels: string[], statement: T): Lisp {\n  return labels.reduceRight(\n    (current, label) =>\n      createLisp<Labeled>({\n        op: LispType.Labeled,\n        a: label,\n        b: current,\n      }),\n    statement as Lisp,\n  );\n}\n\nconst lispTypes: Map<string, LispCallback<string>> = new Map();\n\nexport class ParseError extends Error {\n  constructor(\n    message: string,\n    public code: string,\n  ) {\n    super(message + ': ' + code.substring(0, 40));\n  }\n}\n\nlet lastType: CodeString | string;\nlet lastPart: CodeString | string;\nlet lastLastPart: CodeString | string;\nlet lastLastLastPart: CodeString | string;\nlet lastLastLastLastPart: CodeString | string;\n\nconst inlineIfElse = /^:/;\nconst elseIf = /^else(?![\\w$])/;\nconst ifElse = /^if(?![\\w$])/;\nconst space = /^\\s/;\n\nexport const expectTypes = {\n  splitter: {\n    types: {\n      power: /^(\\*\\*)(?!=)/,\n      opHigh: /^(\\/|\\*(?!\\*)|%)(?!=)/,\n      op: /^(\\+(?!(\\+))|-(?!(-)))(?!=)/,\n      comparitor: /^(<=|>=|<(?!<)|>(?!>)|!==|!=(?!=)|===|==)/,\n      bitwiseShift: /^(<<|>>(?!>)|>>>)(?!=)/,\n      bitwiseAnd: /^(&(?!&))(?!=)/,\n      bitwiseXor: /^(\\^)(?!=)/,\n      bitwiseOr: /^(\\|(?!\\|))(?!=)/,\n      boolOpAnd: /^(&&)(?!=)/,\n      boolOpOr: /^(\\|\\|(?!=)|instanceof(?![\\w$])|in(?![\\w$]))/,\n      nullishCoalescing: /^\\?\\?(?!=)/,\n    },\n    next: ['modifier', 'value', 'prop', 'incrementerBefore'],\n  },\n  inlineIf: {\n    types: {\n      inlineIf: /^\\?(?!\\.(?!\\d))/,\n    },\n    next: ['expEnd'],\n  },\n  assignment: {\n    types: {\n      assignModify: /^(-=|\\+=|\\/=|\\*\\*=|\\*=|%=|\\^=|&=|\\|=|>>>=|>>=|<<=|&&=|\\|\\|=|\\?\\?=)/,\n      assign: /^(=)(?!=)/,\n    },\n    next: ['modifier', 'value', 'prop', 'incrementerBefore'],\n  },\n  incrementerBefore: {\n    types: { incrementerBefore: /^(\\+\\+|--)/ },\n    next: ['prop'],\n  },\n  expEdge: {\n    types: {\n      call: /^(\\?\\.)?[(]/,\n      incrementerAfter: /^(\\+\\+|--)/,\n      taggedTemplate: /^`(\\d+)`/,\n    },\n    next: ['splitter', 'assignment', 'expEdge', 'dot', 'inlineIf', 'expEnd'],\n  },\n  modifier: {\n    types: {\n      not: /^!/,\n      inverse: /^~/,\n      negative: /^-(?!-)/,\n      positive: /^\\+(?!\\+)/,\n      typeof: /^typeof(?![\\w$])/,\n      delete: /^delete(?![\\w$])/,\n    },\n    next: ['modifier', 'value', 'prop', 'incrementerBefore'],\n  },\n  dot: {\n    types: {\n      arrayProp: /^(\\?\\.)?\\[/,\n      dot: /^(\\?)?\\.(?=\\s*[a-zA-Z$_])/,\n    },\n    next: ['splitter', 'assignment', 'expEdge', 'dot', 'inlineIf', 'expEnd'],\n  },\n  prop: {\n    types: {\n      prop: /^[a-zA-Z$_][a-zA-Z\\d$_]*/,\n    },\n    next: ['splitter', 'assignment', 'expEdge', 'dot', 'inlineIf', 'expEnd'],\n  },\n  value: {\n    types: {\n      createObject: /^\\{/,\n      createArray: /^\\[/,\n      number:\n        /^(0b[01]+(_[01]+)*|0o[0-7]+(_[0-7]+)*|0x[\\da-f]+(_[\\da-f]+)*|(\\d+(_\\d+)*(\\.\\d+(_\\d+)*)?|\\.\\d+(_\\d+)*))(e[+-]?\\d+(_\\d+)*)?(n)?(?!\\d)/i,\n      string: /^\"(\\d+)\"/,\n      literal: /^`(\\d+)`/,\n      regex: /^\\/(\\d+)\\/r(?![\\w$])/,\n      boolean: /^(true|false)(?![\\w$])/,\n      null: /^null(?![\\w$])/,\n      und: /^undefined(?![\\w$])/,\n      arrowFunctionSingle: /^(async\\s+)?([a-zA-Z$_][a-zA-Z\\d$_]*)\\s*=>\\s*({)?/,\n      arrowFunction: /^(async\\s*)?\\(\\s*([^)(]*?)\\s*\\)\\s*=>\\s*({)?/,\n      inlineFunction: /^(async\\s+)?function(\\*\\s*|\\s*)([a-zA-Z$_][a-zA-Z\\d$_]*)?\\s*\\(\\s*/,\n      yield: /^yield\\*(?![\\w$])\\s*|^yield(?![\\w$])\\s*/,\n      group: /^\\(/,\n      NaN: /^NaN(?![\\w$])/,\n      Infinity: /^Infinity(?![\\w$])/,\n      void: /^void(?![\\w$])\\s*/,\n      await: /^await(?![\\w$])\\s*/,\n      new: /^new(?![\\w$])\\s*/,\n    },\n    next: ['splitter', 'expEdge', 'dot', 'inlineIf', 'expEnd'],\n  },\n  initialize: {\n    types: {\n      initializeDestructure: /^(var|let|const|internal)\\s+([{[])/,\n      initialize: /^(var|let|const|internal)\\s+([a-zA-Z$_][a-zA-Z\\d$_]*)\\s*(=)?/,\n      return: /^return(?![\\w$])/,\n      throw: /^throw(?![\\w$])\\s*/,\n    },\n    next: ['modifier', 'value', 'prop', 'incrementerBefore', 'expEnd'],\n  },\n  spreadObject: {\n    types: {\n      spreadObject: /^\\.\\.\\./,\n    },\n    next: ['value', 'prop'],\n  },\n  spreadArray: {\n    types: {\n      spreadArray: /^\\.\\.\\./,\n    },\n    next: ['value', 'prop'],\n  },\n  expEnd: { types: {}, next: [] },\n  expFunction: {\n    types: {\n      function: /^(async\\s+)?function(\\*\\s*|\\s+)([a-zA-Z$_][a-zA-Z\\d$_]*)\\s*\\(\\s*/,\n    },\n    next: ['expEdge', 'expEnd'],\n  },\n  expSingle: {\n    types: {\n      for: /^((?:[a-zA-Z$_][\\w$]*\\s*:\\s*)*)\\s*for(\\s+await)?\\s*\\(/,\n      do: /^((?:[a-zA-Z$_][\\w$]*\\s*:\\s*)*)\\s*do(?![\\w$])\\s*(\\{)?/,\n      while: /^((?:[a-zA-Z$_][\\w$]*\\s*:\\s*)*)\\s*while\\s*\\(/,\n      loopAction: /^(break|continue)(?![\\w$])\\s*([a-zA-Z$_][\\w$]*)?/,\n      if: /^((?:[a-zA-Z$_][\\w$]*\\s*:\\s*)*)\\s*if\\s*\\(/,\n      try: /^((?:[a-zA-Z$_][\\w$]*\\s*:\\s*)*)\\s*try\\s*{/,\n      block: /^((?:[a-zA-Z$_][\\w$]*\\s*:\\s*)*)\\s*{/,\n      switch: /^((?:[a-zA-Z$_][\\w$]*\\s*:\\s*)*)\\s*switch\\s*\\(/,\n    },\n    next: ['expEnd'],\n  },\n} as Record<string, { types: Record<string, RegExp>; next: string[] }>;\n\nconst closings = {\n  '(': ')',\n  '[': ']',\n  '{': '}',\n  \"'\": \"'\",\n  '\"': '\"',\n  '`': '`',\n} as Record<string, string>;\n\nexport function testMultiple(str: string, tests: RegExp[]) {\n  let found: RegExpExecArray | null = null;\n  for (let i = 0; i < tests.length; i++) {\n    const test = tests[i];\n    found = test.exec(str);\n    if (found) break;\n  }\n  return found;\n}\n\nconst emptyString = new CodeString('');\n\nconst okFirstChars = /^[+\\-~ !]/;\nconst aNumber = expectTypes.value.types.number;\nconst wordReg = /^((if|for|else|while|do|function)(?![\\w$])|[\\w$]+)/;\nconst semiColon = /^;/;\nconst insertedSemicolons: WeakMap<{ str: string }, Array<number>> = new WeakMap();\nconst quoteCache: WeakMap<{ str: string }, Map<number, number>> = new WeakMap();\nexport interface restDetails {\n  oneliner?: boolean;\n  words?: string[];\n  lastWord?: string;\n  lastAnyWord?: string;\n  regRes?: RegExpExecArray;\n  bodyContentAfterKeyword?: boolean;\n}\nexport function restOfExp(\n  constants: IConstants,\n  part: CodeString,\n  tests?: RegExp[],\n  quote?: string,\n  firstOpening?: string,\n  closingsTests?: RegExp[],\n  details: restDetails = {},\n  depth = 0,\n): CodeString {\n  if (!part.length) {\n    return part;\n  }\n  if (depth > constants.maxDepth) {\n    throw new SandboxCapabilityError('Maximum expression depth exceeded');\n  }\n  details.words = details.words || [];\n  let isStart = true;\n  tests = tests || [];\n  const hasSemiTest = tests.includes(semiColon);\n  if (hasSemiTest) {\n    tests = tests.filter((a) => a !== semiColon);\n  }\n  const insertedSemis = insertedSemicolons.get(part.ref) || [];\n  const cache = quoteCache.get(part.ref) || new Map<number, number>();\n  quoteCache.set(part.ref, cache);\n  if (quote && cache.has(part.start - 1)) {\n    return part.substring(0, cache.get(part.start - 1)! - part.start);\n  }\n  let escape = false;\n  let done = false;\n  let lastChar = '';\n  let isOneLiner = false;\n  let i;\n  let lastInertedSemi = false;\n  let seenKeyword = false;\n  let skipNextWord = false;\n  for (i = 0; i < part.length && !done; i++) {\n    let char = part.char(i)!;\n    if (quote === '\"' || quote === \"'\" || quote === '`') {\n      if (quote === '`' && char === '$' && part.char(i + 1) === '{' && !escape) {\n        const skip = restOfExp(\n          constants,\n          part.substring(i + 2),\n          [],\n          '{',\n          undefined,\n          undefined,\n          {},\n          depth + 1,\n        );\n        i += skip.length + 2;\n      } else if (char === quote && !escape) {\n        return part.substring(0, i);\n      }\n      escape = !escape && char === '\\\\';\n    } else if (closings[char]) {\n      if (!lastInertedSemi && insertedSemis[i + part.start]) {\n        lastInertedSemi = true;\n        if (hasSemiTest) {\n          break;\n        }\n        i--;\n        lastChar = ';';\n        continue;\n      }\n      if (isOneLiner && char === '{') {\n        isOneLiner = false;\n      }\n      if (char === firstOpening) {\n        done = true;\n        break;\n      } else {\n        const skip = restOfExp(\n          constants,\n          part.substring(i + 1),\n          [],\n          char,\n          undefined,\n          undefined,\n          {},\n          depth + 1,\n        );\n        cache.set(skip.start - 1, skip.end);\n        i += skip.length + 1;\n        isStart = false;\n        if (closingsTests) {\n          const sub = part.substring(i);\n          let found: RegExpExecArray | null;\n          if ((found = testMultiple(sub.toString(), closingsTests))) {\n            details.regRes = found;\n            done = true;\n          }\n        }\n      }\n    } else if (!quote) {\n      let sub = part.substring(i).toString();\n      let foundWord: RegExpExecArray | null;\n      let foundNumber: RegExpExecArray | null;\n      if (closingsTests) {\n        let found: RegExpExecArray | null;\n        if ((found = testMultiple(sub, closingsTests))) {\n          details.regRes = found;\n          i++;\n          done = true;\n          break;\n        }\n      }\n      if ((foundNumber = aNumber.exec(sub))) {\n        i += foundNumber[0].length - 1;\n        sub = part.substring(i).toString();\n        if (closingsTests) {\n          let found: RegExpExecArray | null;\n          if ((found = testMultiple(sub, closingsTests))) {\n            details.regRes = found;\n            i++;\n            done = true;\n            break;\n          }\n        }\n      } else if (lastChar != char) {\n        let found: [string] | RegExpExecArray | null = null;\n        if (char === ';' || (insertedSemis[i + part.start] && !isStart && !lastInertedSemi)) {\n          if (hasSemiTest) {\n            found = [';'];\n          } else if (insertedSemis[i + part.start]) {\n            lastInertedSemi = true;\n            i--;\n            lastChar = ';';\n            continue;\n          }\n          char = sub = ';';\n        } else {\n          lastInertedSemi = false;\n        }\n        if (!found) {\n          found = testMultiple(sub, tests);\n        }\n        if (found) {\n          done = true;\n        }\n        if (!done && (foundWord = wordReg.exec(sub))) {\n          isOneLiner = true;\n          if (foundWord[2]) {\n            seenKeyword = true;\n            skipNextWord = true;\n          } else if (seenKeyword) {\n            if (skipNextWord) {\n              skipNextWord = false;\n            } else {\n              details.bodyContentAfterKeyword = true;\n            }\n          }\n          if (foundWord[0].length > 1) {\n            details.words.push(foundWord[1]);\n            details.lastAnyWord = foundWord[1];\n            if (foundWord[2]) {\n              details.lastWord = foundWord[2];\n            }\n          }\n          if (foundWord[0].length > 2) {\n            i += foundWord[0].length - 2;\n          }\n        }\n      }\n      if (isStart) {\n        if (okFirstChars.test(sub)) {\n          done = false;\n        } else {\n          isStart = false;\n        }\n      }\n      if (done) break;\n    } else if (char === closings[quote]) {\n      return part.substring(0, i);\n    }\n    lastChar = char;\n  }\n  if (quote) {\n    throw new SyntaxError(\"Unclosed '\" + quote + \"'\");\n  }\n  if (details) {\n    details.oneliner = isOneLiner;\n  }\n  return part.substring(0, i);\n}\nrestOfExp.next = ['splitter', 'expEnd', 'inlineIf'];\n\nconst startingExecpted = [\n  'initialize',\n  'expSingle',\n  'expFunction',\n  'value',\n  'modifier',\n  'prop',\n  'incrementerBefore',\n  'expEnd',\n];\n\nexport const setLispType = <T extends readonly string[]>(types: T, fn: LispCallback<T[number]>) => {\n  types.forEach((type) => {\n    lispTypes.set(type, fn);\n  });\n};\n\nregisterLispTypes({\n  NullLisp,\n  ParseError,\n  createLisp,\n  emptyString,\n  expectTypes,\n  expandDestructure,\n  expandFunctionParamDestructure,\n  extractStatementLabels,\n  findPatternEndIdx,\n  getDestructurePatternSource,\n  insertSemicolons,\n  lispify,\n  lispifyBlock,\n  lispifyExpr,\n  lispifyFunction,\n  lispifyReturnExpr,\n  restOfExp,\n  semiColon,\n  setLispType,\n  splitByCommasDestructure,\n  startingExecpted,\n  wrapLabeledStatement,\n});\n\nfunction splitByCommasDestructure(s: string): string[] {\n  const parts: string[] = [];\n  let depth = 0;\n  let cur = '';\n  for (let i = 0; i < s.length; i++) {\n    const c = s[i];\n    if (c === '[' || c === '{' || c === '(') depth++;\n    else if (c === ']' || c === '}' || c === ')') depth--;\n    if (c === ',' && depth === 0) {\n      parts.push(cur);\n      cur = '';\n    } else {\n      cur += c;\n    }\n  }\n  parts.push(cur);\n  return parts;\n}\n\nfunction findFirstAtTopLevel(s: string, ch: string): number {\n  let depth = 0;\n  for (let i = 0; i < s.length; i++) {\n    const c = s[i];\n    if (c === '[' || c === '{' || c === '(') depth++;\n    else if (c === ']' || c === '}' || c === ')') depth--;\n    if (c === ch && depth === 0) return i;\n  }\n  return -1;\n}\n\nfunction findPatternEndIdx(s: string): number {\n  let depth = 0;\n  for (let i = 0; i < s.length; i++) {\n    const c = s[i];\n    if (c === '[' || c === '{') depth++;\n    else if (c === ']' || c === '}') {\n      depth--;\n      if (depth === 0) return i + 1;\n    }\n  }\n  return s.length;\n}\n\nconst validIdentifier = /^[a-zA-Z$_][a-zA-Z\\d$_]*$/;\nfunction assertIdentifier(name: string): string {\n  if (!validIdentifier.test(name)) throw new SyntaxError(`Invalid destructuring target: '${name}'`);\n  return name;\n}\n\nfunction expandDestructure(keyword: string, patternStr: string, rhsStr: string): string {\n  const stmts: string[] = [];\n\n  function genTemp(): string {\n    return `$$_d${Math.random().toString(36).slice(2)}`;\n  }\n\n  function processPattern(pattern: string, src: string) {\n    pattern = pattern.trim();\n    if (pattern.startsWith('[')) {\n      processArrayPattern(pattern.slice(1, -1), src);\n    } else if (pattern.startsWith('{')) {\n      processObjectPattern(pattern.slice(1, -1), src);\n    }\n  }\n\n  function processArrayPattern(content: string, src: string) {\n    const elements = splitByCommasDestructure(content);\n    for (let i = 0; i < elements.length; i++) {\n      const elem = elements[i].trim();\n      if (!elem) continue;\n\n      if (elem.startsWith('...')) {\n        const rest = elem.slice(3).trim();\n        if (rest.startsWith('[') || rest.startsWith('{')) {\n          const t = genTemp();\n          stmts.push(`internal ${t} = ${src}.slice(${i})`);\n          processPattern(rest, t);\n        } else {\n          stmts.push(`${keyword} ${assertIdentifier(rest)} = ${src}.slice(${i})`);\n        }\n        break;\n      }\n\n      const eqIdx = findFirstAtTopLevel(elem, '=');\n      const target = eqIdx !== -1 ? elem.slice(0, eqIdx).trim() : elem.trim();\n      const defaultVal = eqIdx !== -1 ? elem.slice(eqIdx + 1).trim() : undefined;\n\n      if (target.startsWith('[') || target.startsWith('{')) {\n        const t = genTemp();\n        stmts.push(\n          defaultVal !== undefined\n            ? `internal ${t} = ${src}[${i}] !== undefined ? ${src}[${i}] : (${defaultVal})`\n            : `internal ${t} = ${src}[${i}]`,\n        );\n        processPattern(target, t);\n      } else {\n        stmts.push(\n          defaultVal !== undefined\n            ? `${keyword} ${assertIdentifier(target)} = ${src}[${i}] !== undefined ? ${src}[${i}] : (${defaultVal})`\n            : `${keyword} ${assertIdentifier(target)} = ${src}[${i}]`,\n        );\n      }\n    }\n  }\n\n  function processObjectPattern(content: string, src: string) {\n    const props = splitByCommasDestructure(content);\n    const usedKeys: string[] = [];\n\n    for (const prop of props) {\n      const p = prop.trim();\n      if (!p) continue;\n\n      if (p.startsWith('...')) {\n        // Check that rest is the last element\n        const restIdx = props.indexOf(prop);\n        const hasMore = props.slice(restIdx + 1).some((pp) => pp.trim().length > 0);\n        if (hasMore) {\n          throw new SyntaxError('Rest element must be last element');\n        }\n        const rest = p.slice(3).trim();\n        const exclTemp = genTemp();\n        const keyTemp = genTemp();\n        const resTemp = genTemp();\n        const exclEntries = usedKeys.map((k) => `${assertIdentifier(k)}:1`).join(',');\n        stmts.push(`internal ${exclTemp} = {${exclEntries}}`);\n        stmts.push(`internal ${resTemp} = {}`);\n        stmts.push(\n          `for (internal ${keyTemp} in ${src}) { if (!(${keyTemp} in ${exclTemp})) { ${resTemp}[${keyTemp}] = ${src}[${keyTemp}] } }`,\n        );\n        if (rest.startsWith('[') || rest.startsWith('{')) {\n          processPattern(rest, resTemp);\n        } else {\n          stmts.push(`${keyword} ${assertIdentifier(rest)} = ${resTemp}`);\n        }\n        break;\n      }\n\n      if (p.startsWith('[')) {\n        // Computed property name: [expr]: target\n        let closeBracket = -1;\n        let depth = 1;\n        for (let ci = 1; ci < p.length; ci++) {\n          if (p[ci] === '[') depth++;\n          else if (p[ci] === ']') {\n            depth--;\n            if (depth === 0) {\n              closeBracket = ci;\n              break;\n            }\n          }\n        }\n        if (closeBracket !== -1) {\n          const computedExpr = p.slice(1, closeBracket);\n          const after = p.slice(closeBracket + 1).trim();\n          if (after.startsWith(':')) {\n            const valueStr = after.slice(1).trim();\n            const accessor = `${src}[${computedExpr}]`;\n            const eqIdx = findFirstAtTopLevel(valueStr, '=');\n            const tgt =\n              eqIdx !== -1 && !valueStr.slice(0, eqIdx).trim().match(/^[[{]/)\n                ? valueStr.slice(0, eqIdx).trim()\n                : valueStr.trim();\n            const defVal =\n              eqIdx !== -1 && !valueStr.slice(0, eqIdx).trim().match(/^[[{]/)\n                ? valueStr.slice(eqIdx + 1).trim()\n                : undefined;\n            if (tgt.startsWith('[') || tgt.startsWith('{')) {\n              const t = genTemp();\n              stmts.push(\n                defVal !== undefined\n                  ? `internal ${t} = ${accessor} !== undefined ? ${accessor} : (${defVal})`\n                  : `internal ${t} = ${accessor}`,\n              );\n              processPattern(tgt, t);\n            } else {\n              stmts.push(\n                defVal !== undefined\n                  ? `${keyword} ${assertIdentifier(tgt)} = ${accessor} !== undefined ? ${accessor} : (${defVal})`\n                  : `${keyword} ${assertIdentifier(tgt)} = ${accessor}`,\n              );\n            }\n          }\n        }\n        continue;\n      }\n\n      const colonIdx = findFirstAtTopLevel(p, ':');\n      if (colonIdx !== -1) {\n        const key = assertIdentifier(p.slice(0, colonIdx).trim());\n        const valueStr = p.slice(colonIdx + 1).trim();\n        usedKeys.push(key);\n        const accessor = `${src}.${key}`;\n        const eqIdx = findFirstAtTopLevel(valueStr, '=');\n        const beforeEq = eqIdx !== -1 ? valueStr.slice(0, eqIdx).trim() : valueStr.trim();\n        const isNestedPattern = beforeEq.startsWith('[') || beforeEq.startsWith('{');\n        const tgt = isNestedPattern ? valueStr.trim() : beforeEq;\n        const defVal =\n          !isNestedPattern && eqIdx !== -1 ? valueStr.slice(eqIdx + 1).trim() : undefined;\n\n        if (tgt.startsWith('[') || tgt.startsWith('{')) {\n          const patEnd = findPatternEndIdx(tgt);\n          const finalPattern = tgt.slice(0, patEnd);\n          const afterPat = tgt.slice(patEnd).trim();\n          const nestedDef = afterPat.startsWith('=') ? afterPat.slice(1).trim() : undefined;\n          const t = genTemp();\n          stmts.push(\n            nestedDef !== undefined\n              ? `internal ${t} = ${accessor} !== undefined ? ${accessor} : (${nestedDef})`\n              : `internal ${t} = ${accessor}`,\n          );\n          processPattern(finalPattern, t);\n        } else {\n          stmts.push(\n            defVal !== undefined\n              ? `${keyword} ${assertIdentifier(tgt)} = ${accessor} !== undefined ? ${accessor} : (${defVal})`\n              : `${keyword} ${assertIdentifier(tgt)} = ${accessor}`,\n          );\n        }\n        continue;\n      }\n\n      const eqIdx = findFirstAtTopLevel(p, '=');\n      if (eqIdx !== -1) {\n        const name = assertIdentifier(p.slice(0, eqIdx).trim());\n        const defaultVal = p.slice(eqIdx + 1).trim();\n        usedKeys.push(name);\n        stmts.push(\n          `${keyword} ${name} = ${src}.${name} !== undefined ? ${src}.${name} : (${defaultVal})`,\n        );\n      } else {\n        assertIdentifier(p);\n        usedKeys.push(p);\n        stmts.push(`${keyword} ${p} = ${src}.${p}`);\n      }\n    }\n  }\n\n  const rootTemp = genTemp();\n  stmts.unshift(`var ${rootTemp} = (${rhsStr})`);\n  processPattern(patternStr, rootTemp);\n  return stmts.join('; ');\n}\n\nfunction getDestructurePatternSource(tree: LispItem): string | null {\n  if (!isLisp(tree)) return null;\n  const source = (tree as LispWithSource).source?.trim();\n  if (source && (source.startsWith('[') || source.startsWith('{'))) {\n    return source;\n  }\n  if (tree[0] === LispType.Group) {\n    return getDestructurePatternSource(tree[2]);\n  }\n  return null;\n}\n\nfunction expandFunctionParamDestructure(\n  args: string[],\n  funcBody: string,\n): { args: string[]; body: string } {\n  const injected: string[] = [];\n  const newArgs = args.map((arg, i) => {\n    const a = arg.trim();\n    if (a.startsWith('[') || a.startsWith('{')) {\n      const tempName = `$$_p${i}`;\n      // Check for a parameter default: {pattern} = defaultVal or [pattern] = defaultVal\n      const patEnd = findPatternEndIdx(a);\n      const patternOnly = a.slice(0, patEnd);\n      const afterPat = a.slice(patEnd).trim();\n      if (afterPat.startsWith('=')) {\n        const defaultVal = afterPat.slice(1).trim();\n        // Use temp var that applies the default, then destructure from it\n        injected.push(\n          expandDestructure(\n            'const',\n            patternOnly,\n            `${tempName} !== undefined ? ${tempName} : (${defaultVal})`,\n          ),\n        );\n      } else {\n        injected.push(expandDestructure('const', patternOnly, tempName));\n      }\n      return tempName;\n    }\n    // Handle simple default: a = defaultVal or ...rest (no default)\n    const eqIdx = findFirstAtTopLevel(a, '=');\n    if (eqIdx !== -1 && !a.startsWith('...')) {\n      const paramName = a.slice(0, eqIdx).trim();\n      const defaultVal = a.slice(eqIdx + 1).trim();\n      injected.push(`if (${paramName} === undefined) ${paramName} = (${defaultVal})`);\n      return paramName;\n    }\n    return a;\n  });\n  if (injected.length === 0) return { args: newArgs, body: funcBody };\n  return { args: newArgs, body: injected.join('; ') + '; ' + funcBody };\n}\n\nfunction lispify(\n  constants: IConstants,\n  part: CodeString,\n  expected?: readonly string[],\n  lispTree?: Lisp,\n  topLevel = false,\n  depthCtx: LispDepthCtx = { generatorDepth: 0, asyncDepth: 0, lispDepth: 0 },\n): Lisp {\n  if (depthCtx.lispDepth > constants.maxDepth) {\n    throw new SandboxCapabilityError('Maximum expression depth exceeded');\n  }\n  const { generatorDepth, asyncDepth, lispDepth } = depthCtx;\n  lispTree = lispTree || NullLisp;\n  expected = expected || expectTypes.initialize.next;\n  if (part === undefined) return lispTree;\n\n  part = part.trimStart();\n  const str = part.toString();\n  if (!part.length && !expected.includes('expEnd')) {\n    throw new SyntaxError('Unexpected end of expression');\n  }\n  if (!part.length) return lispTree;\n\n  const ctx: LispCallbackCtx = {\n    constants,\n    type: '',\n    part,\n    res: [],\n    expect: '',\n    lispTree,\n    generatorDepth,\n    asyncDepth,\n    lispDepth,\n  };\n\n  let res: any;\n  for (const expect of expected) {\n    if (expect === 'expEnd') {\n      continue;\n    }\n    for (const type in expectTypes[expect].types) {\n      if (type === 'expEnd') {\n        continue;\n      }\n      if ((res = expectTypes[expect].types[type].exec(str))) {\n        lastType = type;\n        lastLastLastLastPart = lastLastLastPart;\n        lastLastLastPart = lastLastPart;\n        lastLastPart = lastPart;\n        lastPart = part;\n        ctx.type = type;\n        ctx.part = part;\n        ctx.res = res;\n        ctx.expect = expect;\n        try {\n          lispTypes.get(type)?.(ctx as LispCallbackCtx & { type: string });\n        } catch (e) {\n          if (topLevel && e instanceof SyntaxError) {\n            throw new ParseError(e.message, str);\n          }\n          throw e;\n        }\n        break;\n      }\n    }\n    if (res) break;\n  }\n\n  if (!res && part.length) {\n    if (topLevel) {\n      throw new ParseError(`Unexpected token after ${lastType}: ${part.char(0)}`, str);\n    }\n    throw new SyntaxError(`Unexpected token after ${lastType}: ${part.char(0)}`);\n  }\n  return ctx.lispTree;\n}\n\nconst startingExpectedWithoutSingle = startingExecpted.filter((r) => r !== 'expSingle');\n\nfunction lispifyExpr(\n  constants: IConstants,\n  str: CodeString,\n  expected?: readonly string[],\n  depthCtx: LispDepthCtx = { generatorDepth: 0, asyncDepth: 0, lispDepth: 0 },\n): Lisp {\n  if (depthCtx.lispDepth > constants.maxDepth) {\n    throw new SandboxCapabilityError('Maximum expression depth exceeded');\n  }\n  if (!str.trimStart().length) return NullLisp;\n  const subExpressions: CodeString[] = [];\n  let sub: CodeString;\n  let pos = 0;\n  expected = expected || expectTypes.initialize.next;\n  if (expected.includes('expSingle')) {\n    if (testMultiple(str.toString(), Object.values(expectTypes.expSingle.types))) {\n      return lispify(constants, str, ['expSingle'], undefined, true, depthCtx);\n    }\n  }\n  if (expected === startingExecpted) expected = startingExpectedWithoutSingle;\n  while ((sub = restOfExp(constants, str.substring(pos), [/^,/])).length) {\n    subExpressions.push(sub.trimStart());\n    pos += sub.length + 1;\n  }\n  if (subExpressions.length === 1) {\n    return lispify(constants, str, expected, undefined, true, depthCtx);\n  }\n  if (expected.includes('initialize')) {\n    const defined = expectTypes.initialize.types.initialize.exec(subExpressions[0].toString());\n    if (defined) {\n      return createLisp<InternalCode>({\n        op: LispType.InternalBlock,\n        a: subExpressions.map((str, i) =>\n          lispify(\n            constants,\n            i ? new CodeString(defined![1] + ' ' + str) : str,\n            ['initialize'],\n            undefined,\n            true,\n            depthCtx,\n          ),\n        ),\n        b: LispType.None,\n      });\n    } else if (expectTypes.initialize.types.return.exec(subExpressions[0].toString())) {\n      return lispify(constants, str, expected, undefined, true, depthCtx);\n    }\n  }\n  const exprs = subExpressions.map((str) =>\n    lispify(constants, str, expected, undefined, true, depthCtx),\n  );\n  return createLisp<Expression>({ op: LispType.Expression, a: exprs, b: LispType.None });\n}\n\nexport function lispifyReturnExpr(constants: IConstants, str: CodeString) {\n  return createLisp<Return>({\n    op: LispType.Return,\n    a: LispType.None,\n    b: lispifyExpr(constants, str),\n  });\n}\n\nexport function lispifyBlock(\n  str: CodeString,\n  constants: IConstants,\n  expression = false,\n  depthCtx: LispDepthCtx = { generatorDepth: 0, asyncDepth: 0, lispDepth: 0 },\n): Lisp[] {\n  str = insertSemicolons(constants, str);\n  if (!str.trim().length) return [];\n  const parts: CodeString[] = [];\n  let part: CodeString;\n  let pos = 0;\n  let start = 0;\n  let details: restDetails = {};\n  let skipped = false;\n  let isInserted = false;\n  while (\n    (part = restOfExp(\n      constants,\n      str.substring(pos),\n      [semiColon],\n      undefined,\n      undefined,\n      undefined,\n      details,\n    )).length\n  ) {\n    isInserted = !!(str.char(pos + part.length) && str.char(pos + part.length) !== ';');\n    pos += part.length + (isInserted ? 0 : 1);\n    if (/^\\s*else(?![\\w$])/.test(str.substring(pos).toString())) {\n      skipped = true;\n    } else if (\n      details['words']?.includes('do') &&\n      /^\\s*while(?![\\w$])/.test(str.substring(pos).toString())\n    ) {\n      skipped = true;\n    } else {\n      skipped = false;\n      parts.push(str.substring(start, pos - (isInserted ? 0 : 1)));\n      start = pos;\n    }\n    details = {};\n    if (expression) break;\n  }\n  if (skipped) {\n    parts.push(str.substring(start, pos - (isInserted ? 0 : 1)));\n  }\n  return parts\n    .map((str) => str.trimStart())\n    .filter((str) => str.length)\n    .map((str) => {\n      return lispifyExpr(constants, str.trimStart(), startingExecpted, depthCtx);\n    });\n}\n\nexport function lispifyFunction(\n  str: CodeString,\n  constants: IConstants,\n  expression = false,\n  depthCtx: LispDepthCtx = { generatorDepth: 0, asyncDepth: 0, lispDepth: 0 },\n): Lisp[] {\n  if (!str.trim().length) return [];\n  const tree = lispifyBlock(str, constants, expression, depthCtx);\n  hoist(tree);\n  return tree;\n}\n\nfunction hoist(item: LispItem, res: Lisp[] = []): boolean {\n  if (isLisp(item)) {\n    if (!isLisp<LispFamily>(item)) return false;\n    const [op, a, b] = item;\n    if (\n      op === LispType.Labeled ||\n      op === LispType.Try ||\n      op === LispType.If ||\n      op === LispType.Loop ||\n      op === LispType.Switch\n    ) {\n      hoist(a, res);\n      hoist(b, res);\n    } else if (op === LispType.Var) {\n      res.push(createLisp({ op: LispType.Var, a: a, b: LispType.None }));\n    } else if (op === LispType.Function && a[2]) {\n      res.push(item);\n      return true;\n    }\n  } else if (Array.isArray(item)) {\n    const rep: LispItemSingle[] = [];\n    for (const it of item) {\n      if (!hoist(it, res)) {\n        rep.push(it);\n      }\n    }\n    if (rep.length !== item.length) {\n      item.length = 0;\n      item.push(...res, ...rep);\n    }\n  }\n  return false;\n}\n\nconst closingsNoInsertion = /^(\\})\\s*(catch|finally|else|while|instanceof)(?![\\w$])/;\n//  \\w|)|] \\n \\w = 2                                  // \\} \\w|\\{ = 5\nconst colonsRegex = /^((([\\w$\\])\"'`]|\\+\\+|--)\\s*\\r?\\n\\s*([\\w$+\\-!~]))|(\\}\\s*[\\w$!~+\\-{(\"'`]))/;\n\n// if () \\w \\n; \\w              == \\w \\n \\w    | last === if             a\n// if () { }; \\w                == \\} ^else    | last === if             b\n// if () \\w \\n; else \\n \\w \\n;  == \\w \\n \\w    | last === else           a\n// if () {} else {}; \\w         == \\} \\w       | last === else           b\n// while () \\n \\w \\n; \\w        == \\w \\n \\w    | last === while          a\n// while () { }; \\w             == \\} \\w       | last === while          b\n// do \\w \\n; while (); \\w       == \\w \\n while | last === do             a\n// do { } while (); \\w          == \\) \\w       | last === while          c\n// try {} catch () {}; \\w       == \\} \\w       | last === catch|finally  b\n// \\w \\n; \\w                    == \\w \\n \\w    | last === none           a\n// cb() \\n \\w                   == \\) \\n \\w    | last === none           a\n// obj[a] \\n \\w                 == \\] \\n \\w    | last === none           a\n// {} {}                        == \\} \\{       | last === none           b\n\nexport function insertSemicolons(constants: IConstants, str: CodeString): CodeString {\n  let rest = str;\n  let sub = emptyString;\n  let details: restDetails = {};\n  let pendingDoWhile = false;\n  const inserted = insertedSemicolons.get(str.ref) || new Array(str.ref.str.length);\n  while (\n    (sub = restOfExp(constants, rest, [], undefined, undefined, [colonsRegex], details)).length\n  ) {\n    let valid = false;\n    let part = sub;\n    let edge = sub.length;\n    if (details.regRes) {\n      valid = true;\n      const [, , a, , , b] = details.regRes;\n      edge = details.regRes[3] === '++' || details.regRes[3] === '--' ? sub.length + 1 : sub.length;\n      part = rest.substring(0, edge);\n      if (b) {\n        const res = closingsNoInsertion.exec(rest.substring(sub.length - 1).toString());\n        if (res) {\n          if (res[2] === 'while') {\n            if (details.lastWord === 'do') {\n              valid = false;\n              pendingDoWhile = true;\n            } else {\n              valid = true;\n            }\n          } else {\n            valid = false;\n          }\n        } else if (\n          details.lastWord === 'function' &&\n          details.regRes[5][0] === '}' &&\n          details.regRes[5].slice(-1) === '('\n        ) {\n          valid = false;\n        }\n      } else if (a) {\n        if (pendingDoWhile && details.lastWord === 'while') {\n          valid = true;\n          pendingDoWhile = false;\n        } else if (\n          details.lastWord === 'if' ||\n          details.lastWord === 'while' ||\n          details.lastWord === 'for' ||\n          details.lastWord === 'else'\n        ) {\n          valid = !!details.bodyContentAfterKeyword;\n        }\n      }\n    }\n    if (valid) {\n      inserted[part.end] = true;\n    }\n    rest = rest.substring(edge);\n    details = {};\n  }\n  insertedSemicolons.set(str.ref, inserted);\n  return str;\n}\n\nexport function checkRegex(str: string): IRegEx | null {\n  let i = 1;\n  let escape = false;\n  let done = false;\n  let cancel = false;\n  while (i < str.length && !done && !cancel) {\n    done = str[i] === '/' && !escape;\n    escape = str[i] === '\\\\' && !escape;\n    cancel = str[i] === '\\n';\n    i++;\n  }\n  const after = str.substring(i);\n  cancel = cancel || !done || /^\\s*\\d/.test(after);\n  if (cancel) return null;\n  const flags = /^[a-z]*/.exec(after);\n  if (/^\\s+[\\w$]/.test(str.substring(i + flags![0].length))) {\n    return null;\n  }\n  const regexPattern = str.substring(1, i - 1);\n  const regexFlags = (flags && flags[0]) || '';\n  try {\n    new RegExp(regexPattern, regexFlags);\n  } catch (e) {\n    if (e instanceof SyntaxError) throw e;\n  }\n  return {\n    regex: regexPattern,\n    flags: regexFlags,\n    length: i + ((flags && flags[0].length) || 0),\n  };\n}\n\nconst notDivide = /(typeof|delete|instanceof|return|in|of|throw|new|void|do|if)$/;\nconst possibleDivide = /^([\\w$\\])]|\\+\\+|--)[\\s/]/;\nexport function extractConstants(\n  constants: IConstants,\n  str: string,\n  currentEnclosure = '',\n  depth = 0,\n): { str: string; length: number } {\n  if (depth > constants.maxDepth) {\n    throw new SandboxCapabilityError('Maximum expression depth exceeded');\n  }\n  let quote;\n  let extract: (string | number)[] = [];\n  let escape = false;\n  let regexFound: IRegEx | null;\n  let comment = '';\n  let commentStart = -1;\n  let currJs: string[] = [];\n  let char = '';\n  const strRes: (string | number)[] = [];\n  const enclosures: string[] = [];\n  let isPossibleDivide: RegExpExecArray | null = null;\n  let i = 0;\n  for (i = 0; i < str.length; i++) {\n    char = str[i];\n    if (comment) {\n      if (char === comment) {\n        if (comment === '*' && str[i + 1] === '/') {\n          comment = '';\n          i++;\n        } else if (comment === '\\n') {\n          comment = '';\n          strRes.push('\\n');\n        }\n      }\n    } else {\n      if (escape) {\n        escape = false;\n        extract.push(char);\n        continue;\n      }\n\n      if (quote) {\n        if (quote === '`' && char === '$' && str[i + 1] === '{') {\n          const skip = extractConstants(constants, str.substring(i + 2), '{', depth + 1);\n          if (!skip.str.trim().length) {\n            throw new SyntaxError('Unexpected end of expression');\n          }\n          currJs.push(skip.str);\n          extract.push('${', currJs.length - 1, `}`);\n          i += skip.length + 2;\n        } else if (quote === char) {\n          if (quote === '`') {\n            const li = createLisp<Literal>({\n              op: LispType.Literal,\n              a: unraw(extract.join('')),\n              b: [],\n            });\n            li.tempJsStrings = currJs;\n            constants.literals.push(li);\n            strRes.push(`\\``, constants.literals.length - 1, `\\``);\n          } else {\n            constants.strings.push(unraw(extract.join('')));\n            strRes.push(`\"`, constants.strings.length - 1, `\"`);\n          }\n          quote = null;\n          extract = [];\n        } else {\n          extract.push(char);\n        }\n      } else {\n        if (char === \"'\" || char === '\"' || char === '`') {\n          currJs = [];\n          quote = char;\n        } else if (closings[currentEnclosure] === char && !enclosures.length) {\n          return { str: strRes.join(''), length: i };\n        } else if (closings[char]) {\n          enclosures.push(char);\n          strRes.push(char);\n        } else if (closings[enclosures[enclosures.length - 1]] === char) {\n          enclosures.pop();\n          strRes.push(char);\n        } else if (char === '/' && (str[i + 1] === '*' || str[i + 1] === '/')) {\n          comment = str[i + 1] === '*' ? '*' : '\\n';\n          commentStart = i;\n        } else if (\n          char === '/' &&\n          !isPossibleDivide &&\n          (regexFound = checkRegex(str.substring(i)))\n        ) {\n          constants.regexes.push(regexFound);\n          strRes.push(`/`, constants.regexes.length - 1, `/r`);\n          i += regexFound.length - 1;\n        } else {\n          strRes.push(char);\n        }\n\n        if (!isPossibleDivide || !space.test(char)) {\n          if ((isPossibleDivide = possibleDivide.exec(str.substring(i)))) {\n            if (notDivide.test(str.substring(0, i + isPossibleDivide[1].length))) {\n              isPossibleDivide = null;\n            }\n          }\n        }\n      }\n      escape = !!(quote && char === '\\\\');\n    }\n  }\n\n  if (quote) {\n    throw new SyntaxError(`Unclosed '${quote}'`);\n  }\n  if (comment) {\n    if (comment === '*') {\n      throw new SyntaxError(`Unclosed comment '/*': ${str.substring(commentStart)}`);\n    }\n  }\n  return { str: strRes.join(''), length: i };\n}\n\nexport default function parse(\n  code: string,\n  eager = false,\n  expression = false,\n  maxParserRecursionDepth = 256,\n): IExecutionTree {\n  if (typeof code !== 'string') throw new ParseError(`Cannot parse ${code}`, String(code));\n  let str = ' ' + code;\n  const constants: IConstants = {\n    strings: [],\n    literals: [],\n    regexes: [],\n    eager,\n    maxDepth: maxParserRecursionDepth,\n  };\n  str = extractConstants(constants, str).str;\n\n  for (const l of constants.literals) {\n    l[2] = l.tempJsStrings!.map((js: string) => lispifyExpr(constants, new CodeString(js)));\n    delete l.tempJsStrings;\n  }\n  return { tree: lispifyFunction(new CodeString(str), constants, expression), constants };\n}\n"
  },
  {
    "path": "src/utils/CodeString.ts",
    "content": "export class CodeString {\n  start: number;\n  end: number;\n  ref: { str: string };\n  constructor(str: string | CodeString) {\n    this.ref = { str: '' };\n    if (str instanceof CodeString) {\n      this.ref = str.ref;\n      this.start = str.start;\n      this.end = str.end;\n    } else {\n      this.ref.str = str;\n      this.start = 0;\n      this.end = str.length;\n    }\n  }\n\n  substring(start: number, end?: number): CodeString {\n    if (!this.length) return this;\n    start = this.start + start;\n    if (start < 0) {\n      start = 0;\n    }\n    if (start > this.end) {\n      start = this.end;\n    }\n    end = end === undefined ? this.end : this.start + end;\n    if (end < 0) {\n      end = 0;\n    }\n    if (end > this.end) {\n      end = this.end;\n    }\n    const code = new CodeString(this);\n    code.start = start;\n    code.end = end;\n    return code;\n  }\n\n  get length() {\n    const len = this.end - this.start;\n    return len < 0 ? 0 : len;\n  }\n\n  char(i: number) {\n    if (this.start === this.end) return undefined;\n    return this.ref.str[this.start + i];\n  }\n\n  toString() {\n    return this.ref.str.substring(this.start, this.end);\n  }\n\n  trimStart() {\n    const found = /^\\s+/.exec(this.toString());\n    const code = new CodeString(this);\n    if (found) {\n      code.start += found[0].length;\n    }\n    return code;\n  }\n\n  slice(start: number, end?: number) {\n    if (start < 0) {\n      start = this.end - this.start + start;\n    }\n    if (start < 0) {\n      start = 0;\n    }\n    if (end === undefined) {\n      end = this.end - this.start;\n    }\n\n    if (end < 0) {\n      end = this.end - this.start + end;\n    }\n    if (end < 0) {\n      end = 0;\n    }\n    return this.substring(start, end);\n  }\n\n  trim() {\n    const code = this.trimStart();\n    const found = /\\s+$/.exec(code.toString());\n    if (found) {\n      code.end -= found[0].length;\n    }\n    return code;\n  }\n\n  valueOf() {\n    return this.toString();\n  }\n}\n"
  },
  {
    "path": "src/utils/ExecContext.ts",
    "content": "import type { IEvalContext } from '../eval';\nimport type { Change } from '../executor';\nimport { DEFAULT_FUNCTION_REPLACEMENTS } from './functionReplacements';\nimport type { IConstants, IExecutionTree, Lisp, LispItem } from '../parser';\nimport type SandboxExec from '../SandboxExec';\nimport {\n  AsyncFunction,\n  GeneratorFunction,\n  AsyncGeneratorFunction,\n  NON_BLOCKING_THRESHOLD,\n  LispType,\n  type IContext,\n  type IExecContext,\n  type IOptions,\n  type ISymbolWhitelist,\n  type ISandboxGlobal,\n  type SandboxSymbolContext,\n  type SubscriptionSubject,\n} from './types';\nimport { Scope } from './Scope';\nimport { hasOwnProperty } from './Prop';\n\nexport class ExecContext implements IExecContext {\n  constructor(\n    public ctx: IContext,\n    public constants: IConstants,\n    public tree: Lisp[],\n    public getSubscriptions: Set<(obj: SubscriptionSubject, name: string) => void>,\n    public setSubscriptions: WeakMap<\n      SubscriptionSubject,\n      Map<string, Set<(modification: Change) => void>>\n    >,\n    public changeSubscriptions: WeakMap<SubscriptionSubject, Set<(modification: Change) => void>>,\n    public setSubscriptionsGlobal: WeakMap<\n      SubscriptionSubject,\n      Map<string, Set<(modification: Change) => void>>\n    >,\n    public changeSubscriptionsGlobal: WeakMap<\n      SubscriptionSubject,\n      Set<(modification: Change) => void>\n    >,\n    public evals: Map<any, any>,\n    public registerSandboxFunction: (fn: (...args: any[]) => any) => void,\n    public allowJit: boolean,\n    public evalContext?: IEvalContext,\n  ) {}\n}\n\nfunction createSandboxSymbolContext(symbolWhitelist: ISymbolWhitelist): SandboxSymbolContext {\n  return {\n    registry: new Map<string, symbol>(),\n    reverseRegistry: new Map<symbol, string>(),\n    whitelist: { ...symbolWhitelist },\n  };\n}\n\nconst RESERVED_SYMBOL_PROPERTIES = new Set(['length', 'name', 'prototype', 'for', 'keyFor']);\n\nfunction copyWhitelistedSymbols(target: Function, symbolWhitelist: ISymbolWhitelist) {\n  for (const [key, value] of Object.entries(symbolWhitelist)) {\n    if (RESERVED_SYMBOL_PROPERTIES.has(key)) continue;\n    const descriptor = Object.getOwnPropertyDescriptor(Symbol, key);\n    if (descriptor) {\n      Object.defineProperty(target, key, descriptor);\n    }\n  }\n}\n\nexport function getSandboxSymbolCtor(symbols: SandboxSymbolContext) {\n  if (symbols.ctor) {\n    return symbols.ctor;\n  }\n\n  function SandboxSymbol(this: unknown, description?: unknown) {\n    if (new.target) {\n      throw new TypeError('Symbol is not a constructor');\n    }\n    return Symbol(description === undefined ? undefined : String(description));\n  }\n\n  copyWhitelistedSymbols(SandboxSymbol, symbols.whitelist);\n  Object.defineProperties(SandboxSymbol, {\n    prototype: {\n      value: Symbol.prototype,\n      enumerable: false,\n      configurable: false,\n      writable: false,\n    },\n    for: {\n      value(key: unknown) {\n        const stringKey = String(key);\n        let symbol = symbols.registry.get(stringKey);\n        if (!symbol) {\n          symbol = Symbol(stringKey);\n          symbols.registry.set(stringKey, symbol);\n          symbols.reverseRegistry.set(symbol, stringKey);\n        }\n        return symbol;\n      },\n      enumerable: false,\n      configurable: true,\n      writable: true,\n    },\n    keyFor: {\n      value(symbol: unknown) {\n        return typeof symbol === 'symbol' ? symbols.reverseRegistry.get(symbol) : undefined;\n      },\n      enumerable: false,\n      configurable: true,\n      writable: true,\n    },\n  });\n\n  symbols.ctor = SandboxSymbol;\n  return SandboxSymbol;\n}\n\nfunction SandboxGlobal() {}\ninterface SandboxGlobalConstructor {\n  new (): ISandboxGlobal;\n}\nexport function sandboxedGlobal(globals: ISandboxGlobal): SandboxGlobalConstructor {\n  SG.prototype = SandboxGlobal.prototype;\n  return SG as unknown as SandboxGlobalConstructor;\n  function SG(this: ISandboxGlobal) {\n    for (const i in globals) {\n      this[i] = globals[i];\n    }\n  }\n}\n\nexport function createContext(sandbox: SandboxExec, options: IOptions): IContext {\n  const sandboxSymbols = createSandboxSymbolContext(options.symbolWhitelist);\n  const SandboxGlobal = sandboxedGlobal(options.globals);\n  const sandboxGlobal = new SandboxGlobal();\n  const context: IContext = {\n    sandbox: sandbox,\n    globalsWhitelist: new Set(Object.values(options.globals)),\n    prototypeWhitelist: new Map([...options.prototypeWhitelist].map((a) => [a[0].prototype, a[1]])),\n    sandboxSymbols,\n    options,\n    globalScope: new Scope(null, sandboxGlobal, sandboxGlobal),\n    sandboxGlobal,\n    ticks: {\n      ticks: 0n,\n      tickLimit: options.executionQuota,\n      nextYield: options.nonBlocking ? NON_BLOCKING_THRESHOLD : undefined,\n    },\n    sandboxedFunctions: new WeakSet<Function>(),\n  };\n  context.prototypeWhitelist.set(Object.getPrototypeOf(sandboxGlobal), new Set());\n  context.prototypeWhitelist.set(Object.getPrototypeOf([][Symbol.iterator]()) as object, new Set());\n  // Whitelist Generator and AsyncGenerator prototype chains\n  const genProto = Object.getPrototypeOf((function* () {})());\n  context.prototypeWhitelist.set(Object.getPrototypeOf(genProto), new Set());\n  const asyncGenProto = Object.getPrototypeOf((async function* () {})());\n  context.prototypeWhitelist.set(Object.getPrototypeOf(asyncGenProto), new Set());\n  return context;\n}\n\nexport function createExecContext(\n  sandbox: {\n    readonly setSubscriptions: WeakMap<\n      SubscriptionSubject,\n      Map<string, Set<(modification: Change) => void>>\n    >;\n    readonly changeSubscriptions: WeakMap<SubscriptionSubject, Set<(modification: Change) => void>>;\n    readonly sandboxFunctions: WeakMap<(...args: any[]) => any, IExecContext>;\n    readonly context: IContext;\n  },\n  executionTree: IExecutionTree,\n  evalContext?: IEvalContext,\n): IExecContext {\n  const evals = new Map();\n  const execContext: IExecContext = new ExecContext(\n    sandbox.context,\n    executionTree.constants,\n    executionTree.tree,\n    new Set<(obj: SubscriptionSubject, name: string) => void>(),\n    new WeakMap<SubscriptionSubject, Map<string, Set<(modification: Change) => void>>>(),\n    new WeakMap<SubscriptionSubject, Set<(modification: Change) => void>>(),\n    sandbox.setSubscriptions,\n    sandbox.changeSubscriptions,\n    evals,\n    (fn: any) => sandbox.sandboxFunctions.set(fn, execContext),\n    !!evalContext,\n    evalContext,\n  );\n  if (evalContext) {\n    const func = evalContext.sandboxFunction(execContext);\n    const asyncFunc = evalContext.sandboxAsyncFunction(execContext);\n    const genFunc = evalContext.sandboxGeneratorFunction(execContext);\n    const asyncGenFunc = evalContext.sandboxAsyncGeneratorFunction(execContext);\n    const sandboxSymbol = evalContext.sandboxedSymbol(execContext);\n    evals.set(Function, func);\n    evals.set(AsyncFunction, asyncFunc);\n    evals.set(GeneratorFunction, genFunc);\n    evals.set(AsyncGeneratorFunction, asyncGenFunc);\n    evals.set(Symbol, sandboxSymbol);\n    evals.set(eval, evalContext.sandboxedEval(func, execContext));\n    evals.set(setTimeout, evalContext.sandboxedSetTimeout(func, execContext));\n    evals.set(setInterval, evalContext.sandboxedSetInterval(func, execContext));\n    evals.set(clearTimeout, evalContext.sandboxedClearTimeout(execContext));\n    evals.set(clearInterval, evalContext.sandboxedClearInterval(execContext));\n\n    for (const [original, factory] of DEFAULT_FUNCTION_REPLACEMENTS) {\n      evals.set(original, factory(execContext));\n    }\n\n    for (const [original, factory] of sandbox.context.options.functionReplacements) {\n      evals.set(original, factory(execContext, evals.get(original)));\n    }\n\n    const ptwl = sandbox.context.prototypeWhitelist;\n\n    for (const [key, value] of evals) {\n      if (!ptwl.has(key.prototype)) {\n        ptwl.set(key.prototype, new Set());\n      }\n      if (!ptwl.has(value.prototype)) {\n        ptwl.set(value.prototype, ptwl.get(key.prototype) || new Set());\n      }\n      if (sandbox.context.globalsWhitelist.has(key)) {\n        sandbox.context.globalsWhitelist.add(value);\n      }\n      if (hasOwnProperty(sandbox.context.sandboxGlobal, key.name)) {\n        sandbox.context.sandboxGlobal[key.name] = value;\n      }\n    }\n    if (sandbox.context.sandboxGlobal.globalThis) {\n      sandbox.context.sandboxGlobal.globalThis = sandbox.context.sandboxGlobal;\n    }\n  }\n  return execContext;\n}\n\nexport function isLisp<Type extends Lisp = Lisp>(item: LispItem | LispItem): item is Type {\n  return (\n    Array.isArray(item) &&\n    typeof item[0] === 'number' &&\n    item[0] !== LispType.None &&\n    item[0] !== LispType.True\n  );\n}\n"
  },
  {
    "path": "src/utils/Prop.ts",
    "content": "import type { IExecContext } from './types';\nimport { THIS_DEPENDENT_FUNCTION_REPLACEMENTS } from './functionReplacements';\n\nconst boundFunctionCache = new WeakMap<Function, WeakMap<object, Function>>();\nconst replacementReceiver = new WeakMap<Function, object>();\n\nexport class Prop<T = unknown> {\n  constructor(\n    public context: T,\n    public prop: PropertyKey,\n    public isConst = false,\n    public isGlobal = false,\n    public isVariable = false,\n    public isInternal = false,\n  ) {}\n\n  get<T = unknown>(context: IExecContext): T {\n    const ctx = this.context;\n    if (ctx === undefined) throw new ReferenceError(`${this.prop.toString()} is not defined`);\n    if (ctx === null)\n      throw new TypeError(`Cannot read properties of null, (reading '${this.prop.toString()}')`);\n    context.getSubscriptions.forEach((cb) => cb(ctx, this.prop.toString()));\n    const val = (ctx as any)[this.prop];\n    return getReplacementValue(val, context, ctx) as T;\n  }\n}\n\nexport function hasOwnProperty(obj: unknown, prop: PropertyKey): boolean {\n  return Object.prototype.hasOwnProperty.call(obj, prop);\n}\n\nexport function getReplacementReceiver(fn: Function) {\n  return replacementReceiver.get(fn);\n}\n\nexport function resolveSandboxProp(val: unknown, context: IExecContext, prop?: Prop) {\n  if (!val) return;\n  if (val instanceof Prop) {\n    if (!prop) {\n      prop = val;\n    }\n    val = val.get(context);\n  }\n  const p = prop?.prop || 'prop';\n  if (val === globalThis) {\n    return new Prop(\n      {\n        [p]: context.ctx.sandboxGlobal,\n      },\n      p,\n      prop?.isConst || false,\n      false,\n      prop?.isVariable || false,\n    );\n  }\n  if (prop && !prop.isVariable) {\n    return;\n  }\n  const replacement = getReplacementValue(val, context, prop?.context);\n  if (replacement !== val) {\n    return new Prop(\n      { [p]: replacement },\n      p,\n      prop?.isConst || false,\n      prop?.isGlobal || false,\n      prop?.isVariable || false,\n    );\n  }\n}\n\nfunction getReplacementValue(val: unknown, context: IExecContext, bindContext?: unknown) {\n  if (typeof val !== 'function') {\n    return val;\n  }\n  const replacement = context.evals.get(val as Function);\n  if (replacement === undefined) {\n    return val;\n  }\n  if (!shouldBindReplacement(val as Function, bindContext, context)) {\n    return replacement;\n  }\n  return bindReplacement(replacement, bindContext as object, val as Function, context);\n}\n\nfunction shouldBindReplacement(original: Function, bindContext: unknown, context: IExecContext) {\n  return (\n    THIS_DEPENDENT_FUNCTION_REPLACEMENTS.has(original) &&\n    bindContext !== null &&\n    (typeof bindContext === 'object' || typeof bindContext === 'function') &&\n    bindContext !== context.ctx.sandboxGlobal &&\n    !context.ctx.globalsWhitelist.has(bindContext)\n  );\n}\n\nfunction bindReplacement(\n  replacement: Function,\n  bindContext: object,\n  original: Function,\n  context: IExecContext,\n) {\n  let cache = boundFunctionCache.get(replacement);\n  if (!cache) {\n    cache = new WeakMap<object, Function>();\n    boundFunctionCache.set(replacement, cache);\n  }\n\n  let bound = cache.get(bindContext);\n  if (bound) {\n    return bound;\n  }\n\n  bound = function (this: unknown, ...args: unknown[]) {\n    return replacement.apply(this, args);\n  };\n\n  redefineFunctionMetadata(bound, original);\n  context.ctx.sandboxedFunctions.add(bound);\n  replacementReceiver.set(bound, bindContext);\n  cache.set(bindContext, bound);\n  return bound;\n}\n\nfunction redefineFunctionMetadata(\n  target: Function,\n  source: Function,\n  overrides: Partial<Record<'name' | 'length', string | number>> = {},\n) {\n  for (const key of ['name', 'length'] as const) {\n    const descriptor = Object.getOwnPropertyDescriptor(source, key);\n    if (descriptor?.configurable) {\n      Object.defineProperty(target, key, {\n        ...descriptor,\n        value: overrides[key] ?? source[key],\n      });\n    }\n  }\n}\n"
  },
  {
    "path": "src/utils/Scope.ts",
    "content": "import { reservedWords, VarType } from './types';\nimport { Prop, resolveSandboxProp, hasOwnProperty } from './Prop';\nimport { SandboxError } from './errors';\nimport type { IExecContext, IScope } from './types';\n\nfunction keysOnly(obj: unknown): Record<string, true> {\n  const ret: Record<string, true> = Object.assign({}, obj);\n  for (const key in ret) {\n    ret[key] = true;\n  }\n  return ret;\n}\n\nexport type Unknown = undefined | null | Record<string | number, unknown>;\n\nexport class Scope {\n  parent: Scope | null;\n  const: { [key: string]: true } = {};\n  let: { [key: string]: true } = {};\n  var: { [key: string]: true } = {};\n  internal: { [key: string]: true } = {};\n  globals: { [key: string]: true };\n  allVars: { [key: string]: unknown } & object;\n  internalVars: { [key: string]: unknown } = {};\n  functionThis?: Unknown;\n  constructor(parent: Scope | null, vars = {}, functionThis?: Unknown) {\n    const isFuncScope = functionThis !== undefined || parent === null;\n    this.parent = parent;\n    this.allVars = vars;\n    this.let = isFuncScope ? this.let : keysOnly(vars);\n    this.var = isFuncScope ? keysOnly(vars) : this.var;\n    this.globals = parent === null ? keysOnly(vars) : {};\n    this.functionThis = functionThis;\n  }\n\n  get(key: string, internal: boolean): Prop {\n    const isThis = key === 'this';\n    const scope = this.getWhereValScope(key, isThis, internal);\n    if (scope && isThis) {\n      return new Prop({ this: scope.functionThis }, key, false, false, true);\n    }\n    if (!scope) {\n      return new Prop(undefined, key);\n    }\n    if (internal && scope.internalVars[key]) {\n      return new Prop(scope.internalVars, key, false, false, true, true);\n    }\n    return new Prop(\n      scope.allVars,\n      key,\n      hasOwnProperty(scope.const, key),\n      hasOwnProperty(scope.globals, key),\n      true,\n    );\n  }\n\n  set(key: string, val: unknown, internal: boolean) {\n    if (key === 'this') throw new SyntaxError('\"this\" cannot be assigned');\n    if (reservedWords.has(key)) throw new SyntaxError(\"Unexepected token '\" + key + \"'\");\n    const prop = this.get(key, internal);\n    if (prop.context === undefined) {\n      throw new ReferenceError(`Variable '${key}' was not declared.`);\n    }\n    if (prop.context === null) {\n      throw new TypeError(`Cannot set properties of null, (setting '${key}')`);\n    }\n    if (prop.isConst) {\n      throw new TypeError(`Assignment to constant variable`);\n    }\n    if (prop.isGlobal) {\n      throw new SandboxError(`Cannot override global variable '${key}'`);\n    }\n    (prop.context as any)[prop.prop] = val;\n    return prop;\n  }\n\n  getWhereValScope(key: string, isThis: boolean, internal: boolean): Scope | null {\n    let scope: Scope = this;\n    if (isThis) {\n      do {\n        if (scope.functionThis !== undefined) return scope;\n        scope = scope.parent!;\n      } while (scope !== null);\n      return null;\n    }\n    do {\n      if (\n        internal &&\n        key in scope.internalVars &&\n        !(key in {} && !hasOwnProperty(scope.internalVars, key))\n      ) {\n        return scope;\n      }\n      if (key in scope.allVars && !(key in {} && !hasOwnProperty(scope.allVars, key))) {\n        return scope;\n      }\n      scope = scope.parent!;\n    } while (scope !== null);\n    return null;\n  }\n\n  getWhereVarScope(key: string, localScope: boolean, internal: boolean): Scope {\n    let scope: Scope = this;\n    do {\n      if (\n        internal &&\n        key in scope.internalVars &&\n        !(key in {} && !hasOwnProperty(scope.internalVars, key))\n      ) {\n        return scope;\n      }\n      if (key in scope.allVars && !(key in {} && !hasOwnProperty(scope.allVars, key))) {\n        return scope;\n      }\n      if (scope.parent === null || localScope || scope.functionThis !== undefined) {\n        return scope;\n      }\n      scope = scope.parent!;\n    } while (scope !== null);\n    return scope;\n  }\n\n  declare(key: string, type: VarType, value: unknown, isGlobal: boolean, internal: boolean): Prop {\n    if (key === 'this') throw new SyntaxError('\"this\" cannot be declared');\n    if (reservedWords.has(key)) throw new SyntaxError(\"Unexepected token '\" + key + \"'\");\n    const existingScope = this.getWhereVarScope(key, type !== VarType.var, internal);\n    if (type === VarType.var) {\n      if (existingScope.var[key]) {\n        existingScope.allVars[key] = value;\n        if (!isGlobal) {\n          delete existingScope.globals[key];\n        } else {\n          existingScope.globals[key] = true;\n        }\n        return new Prop(existingScope.allVars, key, false, existingScope.globals[key], true);\n      } else if (key in existingScope.allVars) {\n        throw new SyntaxError(`Identifier '${key}' has already been declared`);\n      }\n    }\n    if (key in existingScope.allVars || key in existingScope.internalVars) {\n      throw new SyntaxError(`Identifier '${key}' has already been declared`);\n    }\n\n    if (isGlobal) {\n      existingScope.globals[key] = true;\n    }\n    existingScope[type][key] = true;\n    if (type === VarType.internal) {\n      existingScope.internalVars[key] = value;\n    } else {\n      existingScope.allVars[key] = value;\n    }\n\n    return new Prop(\n      type === VarType.internal ? this.internalVars : this.allVars,\n      key,\n      type === VarType.const,\n      isGlobal,\n      true,\n      type === VarType.internal,\n    );\n  }\n}\n\nexport class FunctionScope implements IScope {}\n\nexport class LocalScope implements IScope {}\n\nexport const optional = {};\n\nexport class DelayedSynchronousResult {\n  readonly result: unknown;\n  constructor(cb: () => unknown) {\n    this.result = cb();\n  }\n}\n\nexport function delaySynchronousResult(cb: () => Promise<unknown>) {\n  return new DelayedSynchronousResult(cb);\n}\n\nexport function sanitizeProp(\n  value: unknown,\n  context: IExecContext,\n  cache = new WeakSet<object>(),\n): unknown {\n  if (value === null || (typeof value !== 'object' && typeof value !== 'function')) return value;\n\n  value = resolveSandboxProp(value, context) || value;\n\n  if (value instanceof Prop) {\n    value = value.get(context);\n  }\n\n  if (value === optional) {\n    return undefined;\n  }\n\n  return value;\n}\n\nexport function sanitizeScope(scope: IScope, context: IExecContext, cache = new WeakSet<object>()) {\n  if (cache.has(scope)) return;\n  cache.add(scope);\n  for (const key in scope) {\n    const val = scope[key];\n    if (val !== null && typeof val === 'object') {\n      sanitizeScope(val, context, cache);\n    }\n    scope[key] = sanitizeProp(val, context);\n  }\n}\n\nexport function sanitizeScopes(\n  scopes: IScope[],\n  context: IExecContext,\n  cache = new WeakSet<object>(),\n) {\n  for (const scope of scopes) {\n    sanitizeScope(scope, context, cache);\n  }\n}\n"
  },
  {
    "path": "src/utils/errors.ts",
    "content": "import { IExecContext, IScope } from './types';\n\nexport class SandboxError extends Error {}\n\nexport class SandboxExecutionQuotaExceededError extends SandboxError {}\n\nexport class SandboxExecutionTreeError extends SandboxError {}\n\nexport class SandboxCapabilityError extends SandboxError {}\n\nexport class SandboxAccessError extends SandboxError {}\n"
  },
  {
    "path": "src/utils/functionReplacements.ts",
    "content": "import type { IContext, IExecContext } from './types';\nimport { SandboxExecutionQuotaExceededError } from './errors';\n\n/**\n * Checks if adding `expectTicks` would exceed the tick limit, and throws SandboxExecutionQuotaExceededError\n * (which bypasses user try/catch) if so. Otherwise increments the tick counter.\n */\nexport function checkTicksAndThrow(ctx: IExecContext, expectTicks: bigint): void {\n  const { ticks } = ctx.ctx;\n  if (ticks.tickLimit !== undefined && ticks.tickLimit <= ticks.ticks + expectTicks) {\n    throw new SandboxExecutionQuotaExceededError('Execution quota exceeded');\n  }\n  ticks.ticks += expectTicks;\n}\n\n// ---------------------------------------------------------------------------\n// TypedArray shared prototype detection (mirrors call.ts)\n// ---------------------------------------------------------------------------\n\nconst _typedArrayCtors = [\n  Int8Array,\n  Uint8Array,\n  Uint8ClampedArray,\n  Int16Array,\n  Uint16Array,\n  Int32Array,\n  Uint32Array,\n  Float32Array,\n  Float64Array,\n];\nexport const typedArrayProtos = new Set(\n  _typedArrayCtors.map((T) => Object.getPrototypeOf(T.prototype) as any),\n);\n\nfunction isTypedArray(obj: unknown): obj is { length: number } {\n  return (\n    ArrayBuffer.isView(obj) &&\n    !(obj instanceof DataView) &&\n    typedArrayProtos.has(Object.getPrototypeOf(Object.getPrototypeOf(obj)))\n  );\n}\n\n// ---------------------------------------------------------------------------\n// Helpers to build replacements\n// ---------------------------------------------------------------------------\n\ntype Factory = (ctx: IExecContext) => Function;\n\nfunction makeReplacement(\n  original: Function,\n  getTicks: (thisArg: unknown, args: unknown[]) => bigint,\n): Factory {\n  return (ctx: IExecContext) =>\n    function (this: unknown, ...args: unknown[]) {\n      checkTicksAndThrow(ctx, getTicks(this, args));\n      return (original as any).apply(this, args);\n    };\n}\n\n// ---------------------------------------------------------------------------\n// Array replacements\n// ---------------------------------------------------------------------------\n\nconst arr: any[] = [];\nconst arrProto = Array.prototype as any;\n\nfunction arrayTicks(\n  complexity: 'one' | 'n' | 'nlogn' | 'arrs',\n  original: Function,\n): (thisArg: unknown, args: unknown[]) => bigint {\n  return (thisArg, args) => {\n    if (!Array.isArray(thisArg)) return 0n;\n    const n = BigInt(thisArg.length);\n    switch (complexity) {\n      case 'one':\n        return 1n;\n      case 'n':\n        return n;\n      case 'nlogn':\n        return thisArg.length <= 1 ? 1n : n * BigInt(Math.round(Math.log2(thisArg.length)));\n      case 'arrs': {\n        let ticks = 0n;\n        const maxDepth = original === arr.flat ? (typeof args[0] === 'number' ? args[0] : 1) : 1;\n        const recurse = (a: unknown[], depth = 0) => {\n          ticks += BigInt(a.length);\n          if (depth >= maxDepth) return;\n          for (const item of a) {\n            if (Array.isArray(item)) recurse(item, depth + 1);\n          }\n        };\n        recurse(thisArg);\n        return ticks;\n      }\n    }\n  };\n}\n\nconst arrayReplacementDefs: [Function, 'one' | 'n' | 'nlogn' | 'arrs'][] = [\n  // O(1)\n  [arr.push, 'one'],\n  [arr.pop, 'one'],\n  [arrProto.at, 'one'],\n  // O(n)\n  [arr.fill, 'n'],\n  [arr.includes, 'n'],\n  [arr.indexOf, 'n'],\n  [arr.lastIndexOf, 'n'],\n  [arr.find, 'n'],\n  [arr.findIndex, 'n'],\n  [arrProto.findLast, 'n'],\n  [arrProto.findLastIndex, 'n'],\n  [arr.forEach, 'n'],\n  [arr.map, 'n'],\n  [arr.filter, 'n'],\n  [arr.reduce, 'n'],\n  [arr.reduceRight, 'n'],\n  [arr.every, 'n'],\n  [arr.some, 'n'],\n  [arr.join, 'n'],\n  [arr.reverse, 'n'],\n  [arr.shift, 'n'],\n  [arr.unshift, 'n'],\n  [arr.splice, 'n'],\n  [arr.slice, 'n'],\n  [arr.copyWithin, 'n'],\n  [arr.entries, 'n'],\n  [arr.keys, 'n'],\n  [arr.values, 'n'],\n  [arrProto.toReversed, 'n'],\n  [arrProto.toSpliced, 'n'],\n  [arrProto.with, 'n'],\n  [arr.toString, 'n'],\n  [arr.toLocaleString, 'n'],\n  // O(n log n)\n  [arr.sort, 'nlogn'],\n  [arrProto.toSorted, 'nlogn'],\n  // O(n) across arrays\n  [arr.flat, 'arrs'],\n  [arr.flatMap, 'arrs'],\n  [arr.concat, 'arrs'],\n];\n\n// ---------------------------------------------------------------------------\n// String replacements\n// ---------------------------------------------------------------------------\n\nconst str = '';\nconst strProto = String.prototype as any;\n\nfunction stringTicks(complexity: 'one' | 'n'): (thisArg: unknown, _args: unknown[]) => bigint {\n  return (thisArg) => {\n    if (typeof thisArg !== 'string') return 0n;\n    return complexity === 'one' ? 1n : BigInt(thisArg.length);\n  };\n}\n\nconst stringReplacementDefs: [Function, 'one' | 'n'][] = [\n  // O(1)\n  [str.charAt, 'one'],\n  [str.charCodeAt, 'one'],\n  [str.codePointAt, 'one'],\n  [strProto.at, 'one'],\n  // O(n)\n  [str.indexOf, 'n'],\n  [str.lastIndexOf, 'n'],\n  [str.includes, 'n'],\n  [str.startsWith, 'n'],\n  [str.endsWith, 'n'],\n  [str.slice, 'n'],\n  [str.substring, 'n'],\n  [str.padStart, 'n'],\n  [str.padEnd, 'n'],\n  [str.repeat, 'n'],\n  [str.split, 'n'],\n  [str.replace, 'n'],\n  [strProto.replaceAll, 'n'],\n  [str.match, 'n'],\n  [str.matchAll, 'n'],\n  [str.search, 'n'],\n  [str.trim, 'n'],\n  [str.trimStart, 'n'],\n  [str.trimEnd, 'n'],\n  [str.toLowerCase, 'n'],\n  [str.toUpperCase, 'n'],\n  [str.toLocaleLowerCase, 'n'],\n  [str.toLocaleUpperCase, 'n'],\n  [str.normalize, 'n'],\n  [str.concat, 'n'],\n  [str.toString, 'n'],\n  [str.valueOf, 'n'],\n];\n\n// ---------------------------------------------------------------------------\n// Map replacements\n// ---------------------------------------------------------------------------\n\nconst _map = new Map<never, never>();\n\nconst mapReplacementDefs: [Function, 'one' | 'n'][] = [\n  [_map.get, 'one'],\n  [_map.set, 'one'],\n  [_map.has, 'one'],\n  [_map.delete, 'one'],\n  [_map.keys, 'n'],\n  [_map.values, 'n'],\n  [_map.entries, 'n'],\n  [_map.forEach, 'n'],\n  [_map.clear, 'n'],\n];\n\nfunction mapTicks(complexity: 'one' | 'n'): (thisArg: unknown, _args: unknown[]) => bigint {\n  return (thisArg) => {\n    if (!(thisArg instanceof Map)) return 0n;\n    return complexity === 'one' ? 1n : BigInt(thisArg.size);\n  };\n}\n\n// ---------------------------------------------------------------------------\n// Set replacements\n// ---------------------------------------------------------------------------\n\nconst _set = new Set<never>();\n\nconst setReplacementDefs: [Function, 'one' | 'n'][] = [\n  [_set.add, 'one'],\n  [_set.has, 'one'],\n  [_set.delete, 'one'],\n  [_set.values, 'n'],\n  [_set.keys, 'n'],\n  [_set.entries, 'n'],\n  [_set.forEach, 'n'],\n  [_set.clear, 'n'],\n];\n\nfunction setTicks(complexity: 'one' | 'n'): (thisArg: unknown, _args: unknown[]) => bigint {\n  return (thisArg) => {\n    if (!(thisArg instanceof Set)) return 0n;\n    return complexity === 'one' ? 1n : BigInt(thisArg.size);\n  };\n}\n\n// ---------------------------------------------------------------------------\n// TypedArray replacements\n// ---------------------------------------------------------------------------\n\nconst typedArrayReplacementDefs: [Function, 'one' | 'n' | 'nlogn'][] = [];\nfor (const proto of typedArrayProtos) {\n  if (proto.at) typedArrayReplacementDefs.push([proto.at, 'one']);\n  if (proto.set) typedArrayReplacementDefs.push([proto.set, 'one']);\n  const nMethods = [\n    'fill',\n    'find',\n    'findIndex',\n    'findLast',\n    'findLastIndex',\n    'includes',\n    'indexOf',\n    'lastIndexOf',\n    'forEach',\n    'map',\n    'filter',\n    'reduce',\n    'reduceRight',\n    'every',\n    'some',\n    'join',\n    'reverse',\n    'slice',\n    'subarray',\n    'copyWithin',\n    'entries',\n    'keys',\n    'values',\n    'toReversed',\n    'with',\n    'toString',\n    'toLocaleString',\n  ] as const;\n  for (const m of nMethods) {\n    if (proto[m]) typedArrayReplacementDefs.push([proto[m], 'n']);\n  }\n  if (proto.sort) typedArrayReplacementDefs.push([proto.sort, 'nlogn']);\n  if (proto.toSorted) typedArrayReplacementDefs.push([proto.toSorted, 'nlogn']);\n}\n\nfunction typedArrayTicks(\n  complexity: 'one' | 'n' | 'nlogn',\n): (thisArg: unknown, _args: unknown[]) => bigint {\n  return (thisArg) => {\n    if (!isTypedArray(thisArg)) return 0n;\n    const n = BigInt(thisArg.length);\n    switch (complexity) {\n      case 'one':\n        return 1n;\n      case 'n':\n        return n;\n      case 'nlogn':\n        return (thisArg as any).length <= 1\n          ? 1n\n          : n * BigInt(Math.round(Math.log2((thisArg as any).length)));\n    }\n  };\n}\n\n// ---------------------------------------------------------------------------\n// Math replacements\n// ---------------------------------------------------------------------------\n\nconst mathReplacementDefs: [Function][] = [[Math.max], [Math.min], [Math.hypot]];\n\n// ---------------------------------------------------------------------------\n// JSON replacements\n// ---------------------------------------------------------------------------\n\n// (JSON.stringify with subscription traversal is handled separately in LispType.Call)\n\n// ---------------------------------------------------------------------------\n// RegExp replacements\n// ---------------------------------------------------------------------------\n\nconst _re = /x/;\n\nconst regexpReplacementDefs: Function[] = [\n  _re.exec,\n  _re.test,\n  (_re as any)[Symbol.match],\n  (_re as any)[Symbol.matchAll],\n  (_re as any)[Symbol.replace],\n  (_re as any)[Symbol.search],\n  (_re as any)[Symbol.split],\n];\n\n// ---------------------------------------------------------------------------\n// Promise replacements\n// ---------------------------------------------------------------------------\n\nconst promiseReplacementDefs: Function[] = [\n  Promise.all,\n  Promise.allSettled,\n  Promise.race,\n  ...(typeof (Promise as any).any === 'function' ? [(Promise as any).any] : []),\n];\n\n// ---------------------------------------------------------------------------\n// Object replacements\n// ---------------------------------------------------------------------------\n\nconst objectReplacementDefs: [Function, 'one' | 'n'][] = [\n  [Object.prototype.hasOwnProperty, 'one'],\n  [Object.prototype.propertyIsEnumerable, 'one'],\n  [Object.prototype.isPrototypeOf, 'one'],\n  [Object.create, 'one'],\n  [Object.getPrototypeOf, 'one'],\n  [Object.setPrototypeOf, 'one'],\n  [Object.is, 'one'],\n  [Object.defineProperty, 'one'],\n  [Object.getOwnPropertyDescriptor, 'one'],\n  [Object.isExtensible, 'one'],\n  [Object.preventExtensions, 'one'],\n  [Object.keys, 'n'],\n  [Object.values, 'n'],\n  [Object.entries, 'n'],\n  [Object.assign, 'n'],\n  [Object.fromEntries, 'n'],\n  [Object.getOwnPropertyNames, 'n'],\n  [Object.getOwnPropertySymbols, 'n'],\n  [Object.getOwnPropertyDescriptors, 'n'],\n  [Object.freeze, 'n'],\n  [Object.seal, 'n'],\n  [Object.isFrozen, 'n'],\n  [Object.isSealed, 'n'],\n];\n\nfunction objectTicks(\n  complexity: 'one' | 'n',\n  isStatic: boolean,\n): (thisArg: unknown, args: unknown[]) => bigint {\n  return (thisArg, args) => {\n    if (complexity === 'one') return 1n;\n    const target = isStatic ? args[0] : thisArg;\n    if (target !== null && typeof target === 'object')\n      return BigInt(Object.keys(target as object).length);\n    return 1n;\n  };\n}\n\n// Static Object methods whose tick count is based on args[0] key count\nconst staticObjectMethods = new Set<Function>([\n  Object.keys,\n  Object.values,\n  Object.entries,\n  Object.assign,\n  Object.fromEntries,\n  Object.getOwnPropertyNames,\n  Object.getOwnPropertySymbols,\n  Object.getOwnPropertyDescriptors,\n  Object.freeze,\n  Object.seal,\n  Object.isFrozen,\n  Object.isSealed,\n]);\n\n// ---------------------------------------------------------------------------\n// Array.from / Array.fromAsync\n// ---------------------------------------------------------------------------\n\n// ---------------------------------------------------------------------------\n// Build the default replacements map\n// ---------------------------------------------------------------------------\n\nexport const DEFAULT_FUNCTION_REPLACEMENTS = new Map<Function, Factory>();\nexport const THIS_DEPENDENT_FUNCTION_REPLACEMENTS = new Set<Function>();\n\n// Array\nfor (const [original, complexity] of arrayReplacementDefs) {\n  if (!original) continue;\n  DEFAULT_FUNCTION_REPLACEMENTS.set(\n    original,\n    makeReplacement(original, arrayTicks(complexity, original)),\n  );\n  THIS_DEPENDENT_FUNCTION_REPLACEMENTS.add(original);\n}\n\n// String\nfor (const [original, complexity] of stringReplacementDefs) {\n  if (!original) continue;\n  DEFAULT_FUNCTION_REPLACEMENTS.set(original, makeReplacement(original, stringTicks(complexity)));\n  THIS_DEPENDENT_FUNCTION_REPLACEMENTS.add(original);\n}\n\n// Map\nfor (const [original, complexity] of mapReplacementDefs) {\n  if (!original) continue;\n  DEFAULT_FUNCTION_REPLACEMENTS.set(original, makeReplacement(original, mapTicks(complexity)));\n  THIS_DEPENDENT_FUNCTION_REPLACEMENTS.add(original);\n}\n\n// Set\nfor (const [original, complexity] of setReplacementDefs) {\n  if (!original) continue;\n  DEFAULT_FUNCTION_REPLACEMENTS.set(original, makeReplacement(original, setTicks(complexity)));\n  THIS_DEPENDENT_FUNCTION_REPLACEMENTS.add(original);\n}\n\n// TypedArray\nfor (const [original, complexity] of typedArrayReplacementDefs) {\n  if (!original) continue;\n  DEFAULT_FUNCTION_REPLACEMENTS.set(\n    original,\n    makeReplacement(original, typedArrayTicks(complexity)),\n  );\n  THIS_DEPENDENT_FUNCTION_REPLACEMENTS.add(original);\n}\n\n// Math — O(n) on arg count\nfor (const [original] of mathReplacementDefs) {\n  if (!original) continue;\n  DEFAULT_FUNCTION_REPLACEMENTS.set(\n    original,\n    makeReplacement(original, (_thisArg, args) => BigInt(args.length)),\n  );\n}\n\n// JSON — O(n) on string/object size\nfor (const original of [JSON.parse, JSON.stringify]) {\n  DEFAULT_FUNCTION_REPLACEMENTS.set(\n    original,\n    makeReplacement(original, (_thisArg, args) => {\n      const target = args[0];\n      if (typeof target === 'string') return BigInt(target.length);\n      if (target !== null && typeof target === 'object')\n        return BigInt(Object.keys(target as object).length);\n      return 1n;\n    }),\n  );\n}\n\n// RegExp — O(n) on input string length\nfor (const original of regexpReplacementDefs) {\n  if (!original) continue;\n  DEFAULT_FUNCTION_REPLACEMENTS.set(\n    original,\n    makeReplacement(original, (_thisArg, args) => {\n      const input = args[0];\n      return typeof input === 'string' ? BigInt(input.length) : 1n;\n    }),\n  );\n  THIS_DEPENDENT_FUNCTION_REPLACEMENTS.add(original);\n}\n\n// Promise.all/allSettled/race/any — O(n) on iterable length\nfor (const original of promiseReplacementDefs) {\n  if (!original) continue;\n  DEFAULT_FUNCTION_REPLACEMENTS.set(\n    original,\n    makeReplacement(original, (_thisArg, args) => {\n      const iterable = args[0];\n      return Array.isArray(iterable) ? BigInt(iterable.length) : 0n;\n    }),\n  );\n}\n\n// Object static & instance methods\nfor (const [original, complexity] of objectReplacementDefs) {\n  if (!original) continue;\n  const isStatic = staticObjectMethods.has(original);\n  DEFAULT_FUNCTION_REPLACEMENTS.set(\n    original,\n    makeReplacement(original, objectTicks(complexity, isStatic)),\n  );\n  if (!isStatic) {\n    THIS_DEPENDENT_FUNCTION_REPLACEMENTS.add(original);\n  }\n}\n\n// Array.from — O(n) on source length\nif (Array.from) {\n  DEFAULT_FUNCTION_REPLACEMENTS.set(\n    Array.from,\n    makeReplacement(Array.from, (_thisArg, args) => {\n      const source = args[0];\n      if (source != null && typeof (source as any).length === 'number')\n        return BigInt((source as any).length);\n      return 0n;\n    }),\n  );\n}\n\n// Array.fromAsync — O(n) on source length (if available)\nif (typeof (Array as any).fromAsync === 'function') {\n  const fromAsync = (Array as any).fromAsync as Function;\n  DEFAULT_FUNCTION_REPLACEMENTS.set(\n    fromAsync,\n    makeReplacement(fromAsync, (_thisArg, args) => {\n      const source = args[0];\n      if (source != null && typeof (source as any).length === 'number')\n        return BigInt((source as any).length);\n      return 0n;\n    }),\n  );\n}\n"
  },
  {
    "path": "src/utils/index.ts",
    "content": "export * from './unraw';\nexport * from './errors';\nexport * from './CodeString';\nexport * from './types';\nexport * from './Prop';\nexport * from './Scope';\nexport * from './ExecContext';\n"
  },
  {
    "path": "src/utils/types.ts",
    "content": "// Reusable AsyncFunction constructor references\nexport const AsyncFunction: Function = Object.getPrototypeOf(async function () {}).constructor;\nexport const GeneratorFunction: Function = Object.getPrototypeOf(function* () {}).constructor;\nexport const AsyncGeneratorFunction: Function = Object.getPrototypeOf(\n  async function* () {},\n).constructor;\n\nimport type { IEvalContext } from '../eval';\nimport type { Change } from '../executor';\nimport type { IExecutionTree } from '../parser';\nimport type SandboxExec from '../SandboxExec';\nimport type { Scope } from './Scope';\n\nexport interface IOptionParams {\n  audit?: boolean;\n  forbidFunctionCalls?: boolean;\n  forbidFunctionCreation?: boolean;\n  prototypeWhitelist?: Map<Function, Set<string>>;\n  globals?: IGlobals;\n  symbolWhitelist?: ISymbolWhitelist;\n  executionQuota?: bigint;\n  nonBlocking?: boolean;\n  haltOnSandboxError?: boolean;\n  maxParserRecursionDepth?: number;\n  /**\n   * Additional function replacements to merge with the built-in tick-checking replacements.\n   * Maps a native function to a factory that receives the IContext and returns the replacement.\n   * When sandboxed code accesses a property that returns a mapped function, the factory is\n   * called once per context and the result is cached and returned instead.\n   */\n  functionReplacements?: Map<Function, (ctx: IContext) => Function>;\n}\n\nexport interface IOptions {\n  audit: boolean;\n  forbidFunctionCalls: boolean;\n  forbidFunctionCreation: boolean;\n  prototypeWhitelist: Map<Function, Set<PropertyKey>>;\n  globals: IGlobals;\n  symbolWhitelist: ISymbolWhitelist;\n  executionQuota?: bigint;\n  haltOnSandboxError?: boolean;\n  maxParserRecursionDepth: number;\n  nonBlocking: boolean;\n  functionReplacements: Map<\n    Function,\n    (ctx: IExecContext, builtInReplacement?: Function) => Function\n  >;\n}\n\nexport interface IContext {\n  sandbox: SandboxExec;\n  globalScope: Scope;\n  sandboxGlobal: ISandboxGlobal;\n  globalsWhitelist: Set<any>;\n  prototypeWhitelist: Map<any, Set<PropertyKey>>;\n  sandboxedFunctions: WeakSet<Function>;\n  sandboxSymbols: SandboxSymbolContext;\n  options: IOptions;\n  auditReport?: IAuditReport;\n  ticks: Ticks;\n}\n\nexport interface IAuditReport {\n  globalsAccess: Set<unknown>;\n  prototypeAccess: { [name: string]: Set<PropertyKey> };\n}\n\nexport interface Ticks {\n  ticks: bigint;\n  tickLimit?: bigint;\n  nextYield?: bigint;\n}\n\nexport type SubscriptionSubject = object;\n\nexport type HaltContext =\n  | {\n      type: 'error';\n      error: Error;\n      ticks: Ticks;\n      scope: Scope;\n      context: IExecContext;\n    }\n  | {\n      type: 'manual';\n      error?: never;\n      ticks?: never;\n      scope?: never;\n      context?: never;\n    }\n  | {\n      type: 'yield';\n      error?: never;\n      ticks?: never;\n      scope?: never;\n      context?: never;\n    };\n\nexport interface IExecContext extends IExecutionTree {\n  ctx: IContext;\n  getSubscriptions: Set<(obj: SubscriptionSubject, name: string) => void>;\n  setSubscriptions: WeakMap<SubscriptionSubject, Map<string, Set<(modification: Change) => void>>>;\n  changeSubscriptions: WeakMap<SubscriptionSubject, Set<(modification: Change) => void>>;\n  setSubscriptionsGlobal: WeakMap<\n    SubscriptionSubject,\n    Map<string, Set<(modification: Change) => void>>\n  >;\n  changeSubscriptionsGlobal: WeakMap<SubscriptionSubject, Set<(modification: Change) => void>>;\n  registerSandboxFunction: (fn: (...args: any[]) => any) => void;\n  evals: Map<Function, Function>;\n  allowJit: boolean;\n  evalContext?: IEvalContext;\n}\n\nexport interface ISandboxGlobal {\n  [key: string]: unknown;\n}\n\nexport interface ISymbolWhitelist {\n  [key: string]: symbol;\n}\n\nexport interface SandboxSymbolContext {\n  ctor?: Function;\n  registry: Map<string, symbol>;\n  reverseRegistry: Map<symbol, string>;\n  whitelist: ISymbolWhitelist;\n}\n\nexport type IGlobals = ISandboxGlobal;\n\nexport interface IScope {\n  [key: string]: any;\n}\n\nexport const NON_BLOCKING_THRESHOLD = 50_000n;\n\nexport const reservedWords = new Set([\n  'await',\n  'break',\n  'case',\n  'catch',\n  'class',\n  'const',\n  'continue',\n  'debugger',\n  'default',\n  'delete',\n  'do',\n  'else',\n  'enum',\n  'export',\n  'extends',\n  'false',\n  'finally',\n  'for',\n  'function',\n  'if',\n  'implements',\n  'import',\n  'in',\n  'instanceof',\n  'let',\n  'new',\n  'null',\n  'return',\n  'super',\n  'switch',\n  'this',\n  'throw',\n  'true',\n  'try',\n  'typeof',\n  'var',\n  'void',\n  'while',\n  'with',\n]);\n\nexport const enum VarType {\n  let = 'let',\n  const = 'const',\n  var = 'var',\n  internal = 'internal',\n}\n\nexport const enum LispType {\n  None,\n  Prop,\n  StringIndex,\n  Let,\n  Const,\n  Call,\n  KeyVal,\n  Number,\n  Return,\n  Assign,\n  InlineFunction,\n  ArrowFunction,\n  CreateArray,\n  If,\n  IfCase,\n  InlineIf,\n  InlineIfCase,\n  SpreadObject,\n  SpreadArray,\n  ArrayProp,\n  PropOptional,\n  CallOptional,\n  CreateObject,\n  Group,\n  Not,\n  IncrementBefore,\n  IncrementAfter,\n  DecrementBefore,\n  DecrementAfter,\n  And,\n  Or,\n  StrictNotEqual,\n  StrictEqual,\n  Plus,\n  Var,\n  GlobalSymbol,\n  Literal,\n  Function,\n  Loop,\n  Try,\n  Switch,\n  SwitchCase,\n  InternalBlock,\n  Expression,\n  Await,\n  New,\n  Throw,\n  Minus,\n  Divide,\n  Power,\n  Multiply,\n  Modulus,\n  Equal,\n  NotEqual,\n  SmallerEqualThan,\n  LargerEqualThan,\n  SmallerThan,\n  LargerThan,\n  Negative,\n  Positive,\n  Typeof,\n  Delete,\n  Instanceof,\n  In,\n  Inverse,\n  SubractEquals,\n  AddEquals,\n  DivideEquals,\n  PowerEquals,\n  MultiplyEquals,\n  ModulusEquals,\n  BitNegateEquals,\n  BitAndEquals,\n  BitOrEquals,\n  UnsignedShiftRightEquals,\n  ShiftRightEquals,\n  ShiftLeftEquals,\n  BitAnd,\n  BitOr,\n  BitNegate,\n  BitShiftLeft,\n  BitShiftRight,\n  BitUnsignedShiftRight,\n  BigInt,\n  LiteralIndex,\n  RegexIndex,\n  LoopAction,\n  Void,\n  True,\n  NullishCoalescing,\n  AndEquals,\n  OrEquals,\n  NullishCoalescingEquals,\n  Block,\n  Labeled,\n  Internal,\n  Yield,\n  YieldDelegate,\n  Hole,\n\n  LispEnumSize,\n}\n"
  },
  {
    "path": "src/utils/unraw.ts",
    "content": "/**\n * Parse a string as a base-16 number. This is more strict than `parseInt` as it\n * will not allow any other characters, including (for example) \"+\", \"-\", and\n * \".\".\n * @param hex A string containing a hexadecimal number.\n * @returns The parsed integer, or `NaN` if the string is not a valid hex\n * number.\n */\nfunction parseHexToInt(hex: string): number {\n  const isOnlyHexChars = !hex.match(/[^a-f0-9]/i);\n  return isOnlyHexChars ? parseInt(hex, 16) : NaN;\n}\n\n/**\n * Check the validity and length of a hexadecimal code and optionally enforces\n * a specific number of hex digits.\n * @param hex The string to validate and parse.\n * @param errorName The name of the error message to throw a `SyntaxError` with\n * if `hex` is invalid. This is used to index `errorMessages`.\n * @param enforcedLength If provided, will throw an error if `hex` is not\n * exactly this many characters.\n * @returns The parsed hex number as a normal number.\n * @throws {SyntaxError} If the code is not valid.\n */\nfunction validateAndParseHex(hex: string, errorName: string, enforcedLength?: number): number {\n  const parsedHex = parseHexToInt(hex);\n  if (Number.isNaN(parsedHex) || (enforcedLength !== undefined && enforcedLength !== hex.length)) {\n    throw new SyntaxError(errorName + ': ' + hex);\n  }\n  return parsedHex;\n}\n\n/**\n * Parse a two-digit hexadecimal character escape code.\n * @param code The two-digit hexadecimal number that represents the character to\n * output.\n * @returns The single character represented by the code.\n * @throws {SyntaxError} If the code is not valid hex or is not the right\n * length.\n */\nfunction parseHexadecimalCode(code: string): string {\n  const parsedCode = validateAndParseHex(code, 'Malformed Hexadecimal', 2);\n  return String.fromCharCode(parsedCode);\n}\n\n/**\n * Parse a four-digit Unicode character escape code.\n * @param code The four-digit unicode number that represents the character to\n * output.\n * @param surrogateCode Optional four-digit unicode surrogate that represents\n * the other half of the character to output.\n * @returns The single character represented by the code.\n * @throws {SyntaxError} If the codes are not valid hex or are not the right\n * length.\n */\nfunction parseUnicodeCode(code: string, surrogateCode?: string): string {\n  const parsedCode = validateAndParseHex(code, 'Malformed Unicode', 4);\n\n  if (surrogateCode !== undefined) {\n    const parsedSurrogateCode = validateAndParseHex(surrogateCode, 'Malformed Unicode', 4);\n    return String.fromCharCode(parsedCode, parsedSurrogateCode);\n  }\n\n  return String.fromCharCode(parsedCode);\n}\n\n/**\n * Test if the text is surrounded by curly braces (`{}`).\n * @param text Text to check.\n * @returns `true` if the text is in the form `{*}`.\n */\nfunction isCurlyBraced(text: string): boolean {\n  return text.charAt(0) === '{' && text.charAt(text.length - 1) === '}';\n}\n\n/**\n * Parse a Unicode code point character escape code.\n * @param codePoint A unicode escape code point, including the surrounding curly\n * braces.\n * @returns The single character represented by the code.\n * @throws {SyntaxError} If the code is not valid hex or does not have the\n * surrounding curly braces.\n */\nfunction parseUnicodeCodePointCode(codePoint: string): string {\n  if (!isCurlyBraced(codePoint)) {\n    throw new SyntaxError('Malformed Unicode: +' + codePoint);\n  }\n  const withoutBraces = codePoint.slice(1, -1);\n  const parsedCode = validateAndParseHex(withoutBraces, 'Malformed Unicode');\n\n  try {\n    return String.fromCodePoint(parsedCode);\n  } catch (err) {\n    throw err instanceof RangeError ? new SyntaxError('Code Point Limit:' + parsedCode) : err;\n  }\n}\n\n/**\n * Map of unescaped letters to their corresponding special JS escape characters.\n * Intentionally does not include characters that map to themselves like \"\\'\".\n */\nconst singleCharacterEscapes = new Map<string, string>([\n  ['b', '\\b'],\n  ['f', '\\f'],\n  ['n', '\\n'],\n  ['r', '\\r'],\n  ['t', '\\t'],\n  ['v', '\\v'],\n  ['0', '\\0'],\n]);\n\n/**\n * Parse a single character escape sequence and return the matching character.\n * If none is matched, defaults to `code`.\n * @param code A single character code.\n */\nfunction parseSingleCharacterCode(code: string): string {\n  return singleCharacterEscapes.get(code) || code;\n}\n\n/**\n * Matches every escape sequence possible, including invalid ones.\n *\n * All capture groups (described below) are unique (only one will match), except\n * for 4, which can only potentially match if 3 does.\n *\n * **Capture Groups:**\n * 0. A single backslash\n * 1. Hexadecimal code\n * 2. Unicode code point code with surrounding curly braces\n * 3. Unicode escape code with surrogate\n * 4. Surrogate code\n * 5. Unicode escape code without surrogate\n * 6. Octal code _NOTE: includes \"0\"._\n * 7. A single character (will never be \\, x, u, or 0-3)\n */\nconst escapeMatch =\n  /\\\\(?:(\\\\)|x([\\s\\S]{0,2})|u(\\{[^}]*\\}?)|u([\\s\\S]{4})\\\\u([^{][\\s\\S]{0,3})|u([\\s\\S]{0,4})|([0-3]?[0-7]{1,2})|([\\s\\S])|$)/g;\n\n/**\n * Replace raw escape character strings with their escape characters.\n * @param raw A string where escape characters are represented as raw string\n * values like `\\'` rather than `'`.\n * @param allowOctals If `true`, will process the now-deprecated octal escape\n * sequences (ie, `\\111`).\n * @returns The processed string, with escape characters replaced by their\n * respective actual Unicode characters.\n */\nexport function unraw(raw: string): string {\n  return raw.replace(\n    escapeMatch,\n    function (\n      _,\n      backslash?: string,\n      hex?: string,\n      codePoint?: string,\n      unicodeWithSurrogate?: string,\n      surrogate?: string,\n      unicode?: string,\n      octal?: string,\n      singleCharacter?: string,\n    ): string {\n      // Compare groups to undefined because empty strings mean different errors\n      // Otherwise, `\\u` would fail the same as `\\` which is wrong.\n      if (backslash !== undefined) {\n        return '\\\\';\n      }\n      if (hex !== undefined) {\n        return parseHexadecimalCode(hex);\n      }\n      if (codePoint !== undefined) {\n        return parseUnicodeCodePointCode(codePoint);\n      }\n      if (unicodeWithSurrogate !== undefined) {\n        return parseUnicodeCode(unicodeWithSurrogate, surrogate);\n      }\n      if (unicode !== undefined) {\n        return parseUnicodeCode(unicode);\n      }\n      if (octal === '0') {\n        return '\\0';\n      }\n      if (octal !== undefined) {\n        throw new SyntaxError('Octal Deprecation: ' + octal);\n      }\n      if (singleCharacter !== undefined) {\n        return parseSingleCharacterCode(singleCharacter);\n      }\n      throw new SyntaxError('End of string');\n    },\n  );\n}\nexport default unraw;\n"
  },
  {
    "path": "test/audit.spec.ts",
    "content": "import Sandbox from '../src/Sandbox.js';\n\ndescribe('Sandbox.audit Tests', () => {\n  describe('Basic functionality', () => {\n    it('should execute simple code and return result', () => {\n      const result = Sandbox.audit<number>('return 2 + 3');\n      expect(result.result).toBe(5);\n    });\n\n    it('should execute code with variables from scope', () => {\n      const scope = { x: 10, y: 20 };\n      const result = Sandbox.audit<number>('return x + y', [scope]);\n      expect(result.result).toBe(30);\n    });\n\n    it('should handle function definitions', () => {\n      const code = `\n        function add(a, b) {\n          return a + b;\n        }\n        return add(5, 7);\n      `;\n      const result = Sandbox.audit<number>(code);\n      expect(result.result).toBe(12);\n    });\n\n    it('should handle arrow functions', () => {\n      const code = 'return ((x, y) => x * y)(3, 4)';\n      const result = Sandbox.audit<number>(code);\n      expect(result.result).toBe(12);\n    });\n\n    it('should handle arrays and objects', () => {\n      const code = 'return [1, 2, 3].map(x => x * 2)';\n      const result = Sandbox.audit<number[]>(code);\n      expect(result.result).toEqual([2, 4, 6]);\n    });\n\n    it('should handle object creation', () => {\n      const code = 'return { a: 1, b: 2, c: 3 }';\n      const result = Sandbox.audit<{ a: number; b: number; c: number }>(code);\n      expect(result.result).toEqual({ a: 1, b: 2, c: 3 });\n    });\n  });\n\n  describe('Audit mode behavior', () => {\n    it('should allow access to global objects', () => {\n      const code = 'return typeof Math';\n      const result = Sandbox.audit<string>(code);\n      expect(result.result).toBe('object');\n    });\n\n    it('should allow access to Date', () => {\n      const code = 'return new Date(0).toISOString()';\n      const result = Sandbox.audit<string>(code);\n      expect(result.result).toBe('1970-01-01T00:00:00.000Z');\n    });\n\n    it('should allow access to JSON', () => {\n      const code = 'return JSON.stringify({test: 1})';\n      const result = Sandbox.audit<string>(code);\n      expect(result.result).toBe('{\"test\":1}');\n    });\n\n    it('should provide access to console methods', () => {\n      const code = 'return typeof console.log';\n      const result = Sandbox.audit<string>(code);\n      expect(result.result).toBe('function');\n    });\n  });\n\n  describe('With multiple scopes', () => {\n    it('should handle multiple scopes in order', () => {\n      const scope1 = { a: 1 };\n      const scope2 = { b: 2 };\n      const scope3 = { c: 3 };\n      const result = Sandbox.audit<number>('return a + b + c', [scope1, scope2, scope3]);\n      expect(result.result).toBe(6);\n    });\n\n    it('should handle scope shadowing', () => {\n      const scope1 = { x: 10 };\n      const scope2 = { x: 20 };\n      const result = Sandbox.audit<number>('return x', [scope1, scope2]);\n      // Later scopes shadow earlier ones\n      expect(result.result).toBe(20);\n    });\n  });\n\n  describe('Error handling', () => {\n    it('should throw runtime errors', () => {\n      const code = 'throw new Error(\"test error\")';\n      expect(() => {\n        Sandbox.audit(code);\n      }).toThrow('test error');\n    });\n\n    it('should throw on undefined variable access', () => {\n      const code = 'return nonExistentVar';\n      expect(() => {\n        Sandbox.audit(code);\n      }).toThrow(ReferenceError);\n    });\n\n    it('should throw type errors', () => {\n      const code = 'return null.property';\n      expect(() => {\n        Sandbox.audit(code);\n      }).toThrow(TypeError);\n    });\n  });\n\n  describe('Complex operations', () => {\n    it('should handle loops', () => {\n      const code = `\n        let sum = 0;\n        for (let i = 1; i <= 5; i++) {\n          sum += i;\n        }\n        return sum;\n      `;\n      const result = Sandbox.audit<number>(code);\n      expect(result.result).toBe(15);\n    });\n\n    it('should handle nested functions', () => {\n      const code = `\n        function outer(x) {\n          function inner(y) {\n            return x + y;\n          }\n          return inner(5);\n        }\n        return outer(10);\n      `;\n      const result = Sandbox.audit<number>(code);\n      expect(result.result).toBe(15);\n    });\n\n    it('should handle closures', () => {\n      const code = `\n        function makeCounter() {\n          let count = 0;\n          return function() {\n            return ++count;\n          };\n        }\n        const counter = makeCounter();\n        counter();\n        counter();\n        return counter();\n      `;\n      const result = Sandbox.audit<number>(code);\n      expect(result.result).toBe(3);\n    });\n\n    it('should handle template literals', () => {\n      const code = `\n        const name = \"World\";\n        return \\`Hello, \\${name}!\\`;\n      `;\n      const result = Sandbox.audit<string>(code);\n      expect(result.result).toBe('Hello, World!');\n    });\n  });\n});\n"
  },
  {
    "path": "test/compileRerun.spec.ts",
    "content": "import Sandbox from '../src/Sandbox.js';\n\ndescribe('Compiled code rerun', () => {\n  it('should correctly rerun compiled code with named function declarations', () => {\n    const sandbox = new Sandbox();\n    const exec = sandbox.compile<number>('function double(x) { return x * 2; } return double(21);');\n    expect(exec({}).run()).toBe(42);\n    expect(exec({}).run()).toBe(42);\n  });\n\n  it('should correctly rerun compiled code with recursive functions', () => {\n    const sandbox = new Sandbox();\n    const exec = sandbox.compile<number>(`\n      function fact(n) { return n <= 1 ? 1 : n * fact(n - 1); }\n      return fact(5);\n    `);\n    expect(exec({}).run()).toBe(120);\n    expect(exec({}).run()).toBe(120);\n  });\n\n  it('should correctly rerun compiled code with constructor functions', () => {\n    const sandbox = new Sandbox();\n    const exec = sandbox.compile<number>(`\n      function Point(x, y) { this.x = x; this.y = y; }\n      const p = new Point(3, 4);\n      return p.x + p.y;\n    `);\n    expect(exec({}).run()).toBe(7);\n    expect(exec({}).run()).toBe(7);\n  });\n\n  it('should correctly rerun compiled code with multiple function declarations', () => {\n    const sandbox = new Sandbox();\n    const exec = sandbox.compile<number>(`\n      function LinkedListNode(value) {\n        this.value = value;\n        this.next = null;\n      }\n      function reverse(head) {\n        if (!head || !head.next) return head;\n        let tmp = reverse(head.next);\n        head.next.next = head;\n        head.next = undefined;\n        return tmp;\n      }\n      const root = new LinkedListNode(0);\n      let current = root;\n      for (let i = 1; i < 5; i++) {\n        const node = new LinkedListNode(i);\n        current.next = node;\n        current = node;\n      }\n      return reverse(root).value;\n    `);\n    expect(exec({}).run()).toBe(4);\n    expect(exec({}).run()).toBe(4);\n  });\n\n  it('should correctly rerun compiled async code with named function declarations', async () => {\n    const sandbox = new Sandbox();\n    const exec = sandbox.compileAsync<number>(`\n      async function add(a, b) { return a + b; }\n      return add(1, 2);\n    `);\n    expect(await exec({}).run()).toBe(3);\n    expect(await exec({}).run()).toBe(3);\n  });\n\n  it('should correctly rerun compiled code with arrow functions', () => {\n    const sandbox = new Sandbox();\n    const exec = sandbox.compile<number>('const fn = (x) => x + 1; return fn(5);');\n    expect(exec({}).run()).toBe(6);\n    expect(exec({}).run()).toBe(6);\n  });\n\n  it('should correctly rerun compiled code with async arrow functions', async () => {\n    const sandbox = new Sandbox();\n    const exec = sandbox.compileAsync<number>('const fn = async (x) => x + 1; return fn(5);');\n    expect(await exec({}).run()).toBe(6);\n    expect(await exec({}).run()).toBe(6);\n  });\n});\n"
  },
  {
    "path": "test/delaySynchronousResult.spec.ts",
    "content": "import Sandbox, { delaySynchronousResult } from '../src/Sandbox.js';\nconst wait = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms));\n\ndescribe('delaySynchronousResult', () => {\n  it('delivers a value from a setTimeout without async/await in sandbox code', async () => {\n    const fetchValue = () =>\n      delaySynchronousResult(async () => {\n        await wait(10);\n        return 42;\n      });\n    const sandbox = new Sandbox();\n    const scope = { fetchValue };\n\n    const result = await sandbox.compileAsync<number>('return fetchValue();')(scope).run();\n    expect(result).toBe(42);\n  });\n\n  it('sandbox code does not need await to get the delayed result', async () => {\n    const results: number[] = [];\n    const loadItem = (n: number) =>\n      delaySynchronousResult(async () => {\n        await wait(10);\n        return n * 10;\n      });\n    const sandbox = new Sandbox();\n    const scope = { loadItem, results };\n\n    // Note: no await in the sandbox code — delaySynchronousResult handles it\n    await sandbox\n      .compileAsync(\n        `\n        const a = loadItem(1);\n        const b = loadItem(2);\n        results.push(a, b);\n      `,\n      )(scope)\n      .run();\n\n    expect(results).toEqual([10, 20]);\n  });\n\n  it('sequences delayed setTimeout calls in order without await', async () => {\n    const order: string[] = [];\n    const step = (label: string) =>\n      delaySynchronousResult(async () => {\n        await wait(10);\n        order.push(label);\n        return label;\n      });\n    const sandbox = new Sandbox();\n    const scope = { step, order };\n\n    const ret = await sandbox\n      .compileAsync(\n        `\n        return step('a') + step('b') + step('c');\n      `,\n      )(scope)\n      .run();\n\n    expect(order).toEqual(['a', 'b', 'c']);\n    expect(ret).toEqual('abc');\n  });\n\n  it('propagates a setTimeout rejection without await in sandbox code', async () => {\n    const failAfterDelay = () =>\n      delaySynchronousResult(async () => {\n        await wait(10);\n        throw new Error('timeout error');\n      });\n    const sandbox = new Sandbox();\n    const scope = { failAfterDelay };\n\n    await expect(\n      sandbox.compileAsync<never>('return failAfterDelay();')(scope).run(),\n    ).rejects.toThrow('timeout error');\n  });\n\n  it('can catch a delayed setTimeout rejection inside sandbox code', async () => {\n    const failAfterDelay = () =>\n      delaySynchronousResult(async () => {\n        await wait(10);\n        throw new Error('oops');\n      });\n    const sandbox = new Sandbox();\n    const scope = { failAfterDelay, msg: '' };\n\n    await sandbox\n      .compileAsync(\n        `\n        try {\n          failAfterDelay();\n        } catch (e) {\n          msg = e.message;\n        }\n      `,\n      )(scope)\n      .run();\n\n    expect(scope.msg).toBe('oops');\n  });\n\n  it('works via a method on an object', async () => {\n    const api = {\n      fetch: (val: number) =>\n        delaySynchronousResult(async () => {\n          await wait(10);\n          return val;\n        }),\n    };\n    const sandbox = new Sandbox();\n    const scope = { api };\n\n    const result = await sandbox.compileAsync<number>('return api.fetch(99);')(scope).run();\n    expect(result).toBe(99);\n  });\n});\n"
  },
  {
    "path": "test/eval/README.md",
    "content": "# Test Files Organization\n\nThis directory contains organized test files for the SandboxJS evaluation engine.\n\n## Structure\n\n- **testCases/** - Directory containing all test-related files:\n  - `*.spec.ts` - Category-specific test spec files (Jest tests)\n  - `*.data.ts` - Test data arrays (exported and imported by spec files)\n  - `index.ts` - Central export that re-exports all test data\n  - `types.ts` - TypeScript interface definitions for test cases\n  - `test-utils.ts` - Reusable `run()` and `getState()` functions\n- **export-tests.ts** - Script to consolidate all tests into tests.json\n- **tests.json** - All tests exported to a single JSON file\n\n## Test Categories\n\nTests are organized into the following categories:\n- Data Types\n- Security\n- Arithmetic Operators\n- Assignment Operators\n- Bitwise Operators\n- Comments\n- Comparison Operators\n- Complex Expressions\n- Conditionals\n- Error Handling\n- Functions\n- Logical Operators\n- Loops\n- Objects & Arrays\n- Operator Precedence\n- Other Operators\n- Switch\n- Template Literals\n\n## Exporting Tests to JSON\n\nTo regenerate the `tests.json` file from the individual test files:\n\n```bash\ncd test/eval\nnpx tsx export-tests.ts\n```\n\nThis script:\n1. Imports all test arrays from `testCases/index.ts`\n2. Consolidates them into a single array\n4. Orders the tests with:\n3. Orders the tests with:\n   - **Data Types** tests first\n   - **Security** tests second\n   - All other categories in the order they appear\n4. Preserves the original order of tests within each category\n5\n### Output\n\nThe script will display:\n- Number of tests extracted from each file\n- Total test count\n- Tests per category distribution\n\nExample output:\n```\nExtracted 36 tests from data-types.spec.ts\nExtracted 39 tests from security.spec.ts\n...\nTotal tests: 340\nExported to: /Users/nyariv/workspace/SandboxJS/test/eval/tests.json\n```\n\n## Running Tests\n\nRun all tests:\n```bash\nnpm test\n```\n\nRun specific category tests:\n```bash\nnpm test -- test/eval/testCases/data-types.spec.ts\nnpm test -- test/eval/testCases/security.spec.ts\n```\n"
  },
  {
    "path": "test/eval/reveseLinkedList.js",
    "content": "function LinkedListNode(value) {\n  this.value = value;\n  this.next = null;\n}\n\nfunction reverse(head) {\n  let node = head,\n    previous,\n    tmp;\n\n  while (node) {\n    // save next before we overwrite node.next!\n    tmp = node.next;\n\n    // reverse pointer\n    node.next = previous;\n\n    // step forward in the list\n    previous = node;\n    node = tmp;\n  }\n\n  return previous;\n}\n\nfunction reverse(head) {\n  if (!head || !head.next) {\n    return head;\n  }\n  let tmp = reverse(head.next);\n  head.next.next = head;\n  head.next = undefined;\n  return tmp;\n}\n"
  },
  {
    "path": "test/eval/script.js",
    "content": "// @ts-nocheck\nimport Sandbox, { LocalScope, SandboxExecutionQuotaExceededError } from '../../dist/esm/Sandbox.js';\nimport { EditorState } from 'https://esm.sh/@codemirror/state@6';\nimport { EditorView, keymap, lineNumbers, highlightActiveLine, highlightActiveLineGutter, drawSelection } from 'https://esm.sh/@codemirror/view@6';\nimport { defaultKeymap, history, historyKeymap, indentWithTab } from 'https://esm.sh/@codemirror/commands@6';\nimport { javascript } from 'https://esm.sh/@codemirror/lang-javascript@6';\nimport { oneDark } from 'https://esm.sh/@codemirror/theme-one-dark@6';\nimport { bracketMatching, indentOnInput, syntaxHighlighting, defaultHighlightStyle } from 'https://esm.sh/@codemirror/language@6';\nimport { closeBrackets, closeBracketsKeymap } from 'https://esm.sh/@codemirror/autocomplete@6';\nwindow['Sandbox'] = Sandbox;\n\nconst testsPromise = fetch('test/eval/tests.json').then((res) => res.json());\nconst SANDBOX_HASH_KEY = 'sandbox';\nconst SANDBOX_SETTINGS_HASH_KEY = 'settings';\n\nconst runtimeTypeEl = document.getElementById('runtime-type');\nconst jitParsingEl = document.getElementById('jit-parsing');\nconst runBtnEl = document.getElementById('run-btn');\nconst openSandboxModalBtnEl = document.getElementById('open-sandbox-modal-btn');\nconst sandboxModalEl = document.getElementById('sandbox-modal');\nconst sandboxEditorEl = document.getElementById('sandbox-editor');\nconst sandboxConsoleOutputEl = document.getElementById('sandbox-console-output');\nconst sandboxReturnOutputEl = document.getElementById('sandbox-return-output');\nconst sandboxStatusEl = document.getElementById('sandbox-status');\nconst sandboxBypassNoticeEl = document.getElementById('sandbox-bypass-notice');\nconst sandboxBypassIconEl = document.getElementById('sandbox-bypass-icon');\nconst sandboxBypassTextEl = document.getElementById('sandbox-bypass-text');\nconst sandboxTicksOutputEl = document.getElementById('sandbox-ticks-output');\nconst sandboxExecBtnEl = document.getElementById('sandbox-exec-btn');\nconst sandboxCloseBtnEl = document.getElementById('sandbox-close-btn');\nconst sandboxClearBtnEl = document.getElementById('sandbox-clear-btn');\nconst sandboxSettingsBtnEl = document.getElementById('sandbox-settings-btn');\nconst sandboxSettingsPanelEl = document.getElementById('sandbox-settings-panel');\nconst sandboxSettingsBadgeEl = document.getElementById('sandbox-settings-badge');\nconst sandboxSettingsResetBtnEl = document.getElementById('sandbox-settings-reset-btn');\nconst settingForbidCallsEl = document.getElementById('setting-forbid-calls');\nconst settingForbidCreationEl = document.getElementById('setting-forbid-creation');\nconst settingHaltOnErrorEl = document.getElementById('setting-halt-on-error');\nconst settingQuotaEl = document.getElementById('setting-quota');\nconst settingScopeEl = document.getElementById('setting-scope');\nconst settingAsyncEl = document.getElementById('setting-async');\n\n// ── CodeMirror editor ────────────────────────────────────────\nconst editorView = new EditorView({\n  state: EditorState.create({\n    doc: '',\n    extensions: [\n      history(),\n      lineNumbers(),\n      highlightActiveLine(),\n      highlightActiveLineGutter(),\n      drawSelection(),\n      indentOnInput(),\n      bracketMatching(),\n      closeBrackets(),\n      syntaxHighlighting(defaultHighlightStyle, { fallback: true }),\n      keymap.of([{ key: 'Mod-Enter', run: () => { runSandboxCode(); return true; } }, ...closeBracketsKeymap, ...defaultKeymap, ...historyKeymap, indentWithTab]),\n      javascript(),\n      oneDark,\n      EditorView.updateListener.of((update) => {\n        if (update.docChanged) {\n          setSandboxHashCode(editorView.state.doc.toString());\n          setBypassNotice(false);\n          setSandboxStatus('Idle');\n        }\n      }),\n      EditorView.theme({\n        '&': { borderRadius: '12px', overflow: 'hidden' },\n        '.cm-scroller': { minHeight: '240px', fontFamily: \"'Fira Code', 'Cascadia Code', monospace\", fontSize: '0.84rem', lineHeight: '1.6' },\n        '.cm-focused': { outline: 'none' },\n      }),\n    ],\n  }),\n  parent: sandboxEditorEl,\n});\n\nconst getEditorValue = () => editorView.state.doc.toString();\nconst setEditorValue = (value) => {\n  editorView.dispatch({\n    changes: { from: 0, to: editorView.state.doc.length, insert: value },\n  });\n};\n// ─────────────────────────────────────────────────────────────\n\n// ── Settings ─────────────────────────────────────────────────\nconst getSandboxSettings = () => {\n  const quotaRaw = settingQuotaEl.value.trim();\n  let executionQuota;\n  if (quotaRaw) {\n    const n = Number(quotaRaw);\n    executionQuota = Number.isInteger(n) && n >= 0 ? BigInt(n) : undefined;\n  }\n  return {\n    forbidFunctionCalls: settingForbidCallsEl.checked,\n    forbidFunctionCreation: settingForbidCreationEl.checked,\n    haltOnSandboxError: settingHaltOnErrorEl.checked,\n    async: settingAsyncEl.checked,\n    executionQuota,\n    scopeJson: settingScopeEl.value.trim(),\n  };\n};\n\nconst validateSettings = () => {\n  const errors = [];\n  const quotaRaw = settingQuotaEl.value.trim();\n  if (quotaRaw) {\n    const n = Number(quotaRaw);\n    if (!Number.isInteger(n) || n < 0) {\n      errors.push('Execution quota must be a non-negative integer.');\n    }\n    setFieldError(settingQuotaEl, !Number.isInteger(n) || n < 0);\n  } else {\n    setFieldError(settingQuotaEl, false);\n  }\n  const scopeRaw = settingScopeEl.value.trim();\n  if (scopeRaw) {\n    let parsed;\n    try { parsed = JSON.parse(scopeRaw); } catch { parsed = null; }\n    const bad = parsed === null || typeof parsed !== 'object' || Array.isArray(parsed);\n    if (bad) errors.push('Scope must be a valid JSON object (e.g. {\"x\": 1}).');\n    setFieldError(settingScopeEl, bad);\n  } else {\n    setFieldError(settingScopeEl, false);\n  }\n  return errors;\n};\n\nconst setFieldError = (el, hasError) => {\n  el.classList.toggle('settings-field-error', hasError);\n};\n\nconst countNonDefaultSettings = () => {\n  let n = 0;\n  if (settingForbidCallsEl.checked) n++;\n  if (settingForbidCreationEl.checked) n++;\n  if (settingHaltOnErrorEl.checked) n++;\n  if (settingAsyncEl.checked) n++;\n  if (settingQuotaEl.value.trim()) n++;\n  if (settingScopeEl.value.trim()) n++;\n  return n;\n};\n\nconst updateSettingsBadge = () => {\n  const n = countNonDefaultSettings();\n  if (n > 0) {\n    sandboxSettingsBadgeEl.textContent = n;\n    sandboxSettingsBadgeEl.classList.remove('hidden');\n  } else {\n    sandboxSettingsBadgeEl.classList.add('hidden');\n  }\n};\n\nconst resetSettings = () => {\n  settingForbidCallsEl.checked = false;\n  settingForbidCreationEl.checked = false;\n  settingHaltOnErrorEl.checked = false;\n  settingAsyncEl.checked = false;\n  settingQuotaEl.value = '';\n  settingScopeEl.value = '';\n  setFieldError(settingQuotaEl, false);\n  setFieldError(settingScopeEl, false);\n  updateSettingsHashAndBadge();\n};\n\nconst applySettingsFromObject = (settings) => {\n  settingForbidCallsEl.checked = Boolean(settings.forbidFunctionCalls);\n  settingForbidCreationEl.checked = Boolean(settings.forbidFunctionCreation);\n  settingHaltOnErrorEl.checked = Boolean(settings.haltOnSandboxError);\n  settingAsyncEl.checked = Boolean(settings.async);\n  settingQuotaEl.value = settings.executionQuota != null ? String(settings.executionQuota) : '';\n  settingScopeEl.value = settings.scopeJson || '';\n  setFieldError(settingQuotaEl, false);\n  setFieldError(settingScopeEl, false);\n  updateSettingsBadge();\n};\n\nconst parseScopeJson = (json) => {\n  if (!json) return {};\n  try { return JSON.parse(json); } catch { return {}; }\n};\n\nconst updateSettingsHashAndBadge = () => {\n  updateSettingsBadge();\n  const code = getEditorValue();\n  setSandboxHashCode(code);\n};\n\n[settingForbidCallsEl, settingForbidCreationEl, settingHaltOnErrorEl, settingAsyncEl].forEach(el => {\n  el.addEventListener('change', updateSettingsHashAndBadge);\n});\nsettingQuotaEl.addEventListener('input', () => { updateSettingsHashAndBadge(); validateSettings(); });\nsettingScopeEl.addEventListener('input', () => { updateSettingsHashAndBadge(); validateSettings(); });\nsettingScopeEl.addEventListener('keydown', (e) => {\n  const el = settingScopeEl;\n  const start = el.selectionStart;\n  const end = el.selectionEnd;\n  const val = el.value;\n\n  const pairs = { '{': '}', '[': ']', '\"': '\"' };\n  const closers = new Set(['}', ']', '\"']);\n\n  if (e.key in pairs) {\n    const close = pairs[e.key];\n    // For quotes: skip if next char is already a closing quote\n    if (e.key === '\"' && val[start] === '\"') {\n      e.preventDefault();\n      el.setSelectionRange(start + 1, start + 1);\n      return;\n    }\n    e.preventDefault();\n    const selected = val.slice(start, end);\n    const insert = e.key + selected + close;\n    el.value = val.slice(0, start) + insert + val.slice(end);\n    el.setSelectionRange(start + 1, start + 1);\n    updateSettingsHashAndBadge();\n    validateSettings();\n    return;\n  }\n\n  if (e.key === 'Backspace' && start === end) {\n    const before = val[start - 1];\n    const after = val[start];\n    if (before in pairs && pairs[before] === after) {\n      e.preventDefault();\n      el.value = val.slice(0, start - 1) + val.slice(start + 1);\n      el.setSelectionRange(start - 1, start - 1);\n      updateSettingsHashAndBadge();\n      validateSettings();\n      return;\n    }\n  }\n\n  // Skip over a closing char if already present\n  if (closers.has(e.key) && val[start] === e.key) {\n    e.preventDefault();\n    el.setSelectionRange(start + 1, start + 1);\n    return;\n  }\n\n  if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) return;\n  if (e.key === 'Enter') {\n    e.preventDefault();\n    // Smart indent: match current line's indentation, add extra level inside { or [\n    const lineStart = val.lastIndexOf('\\n', start - 1) + 1;\n    const currentLine = val.slice(lineStart, start);\n    const indent = currentLine.match(/^(\\s*)/)[1];\n    const charBefore = val[start - 1];\n    const charAfter = val[end];\n    let insert, cursorOffset;\n    if ((charBefore === '{' && charAfter === '}') || (charBefore === '[' && charAfter === ']')) {\n      const newIndent = indent + '  ';\n      insert = '\\n' + newIndent + '\\n' + indent;\n      cursorOffset = 1 + newIndent.length;\n    } else {\n      insert = '\\n' + indent;\n      cursorOffset = insert.length;\n    }\n    el.value = val.slice(0, start) + insert + val.slice(end);\n    el.setSelectionRange(start + cursorOffset, start + cursorOffset);\n    updateSettingsHashAndBadge();\n    validateSettings();\n  }\n});\n\nsandboxSettingsBtnEl.addEventListener('click', () => {\n  const isOpen = !sandboxSettingsPanelEl.classList.contains('hidden');\n  sandboxSettingsPanelEl.classList.toggle('hidden', isOpen);\n  sandboxSettingsBtnEl.classList.toggle('active', !isOpen);\n});\n\nsandboxSettingsResetBtnEl.addEventListener('click', resetSettings);\n// ─────────────────────────────────────────────────────────────\n\nconst clonePrototypeWhitelist = () => {\n  const prototypeWhitelist = new Map();\n  for (const [key, value] of Sandbox.SAFE_PROTOTYPES.entries()) {\n    prototypeWhitelist.set(key, new Set(value));\n  }\n  return prototypeWhitelist;\n};\n\nconst freeze = Object.freeze;\nconst Function = globalThis.Function;\n\nconst createSandboxInstance = (extraGlobals = {}, sandboxOptions = {}) => {\n  const prototypeWhitelist = clonePrototypeWhitelist();\n  const globals = { ...Sandbox.SAFE_GLOBALS, setTimeout, ...extraGlobals };\n  const functionReplacements = new Map();\n  functionReplacements.set([].filter, () => [].filter)\n  const sandbox = new Sandbox({ prototypeWhitelist, globals, functionReplacements, ...sandboxOptions });\n  return { sandbox };\n};\n\nconst encodeBase64Url = (value) => {\n  const bytes = new TextEncoder().encode(value);\n  let binary = '';\n  for (const byte of bytes) binary += String.fromCharCode(byte);\n  return btoa(binary).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/g, '');\n};\n\nconst decodeBase64Url = (value) => {\n  const normalized = value.replace(/-/g, '+').replace(/_/g, '/');\n  const padding = normalized.length % 4 === 0 ? '' : '='.repeat(4 - (normalized.length % 4));\n  const binary = atob(normalized + padding);\n  const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));\n  return new TextDecoder().decode(bytes);\n};\n\nconst getHashParams = () => new URLSearchParams(window.location.hash.replace(/^#/, ''));\n\nconst getSandboxHashCode = () => {\n  const encoded = getHashParams().get(SANDBOX_HASH_KEY);\n  if (!encoded) return '';\n  try {\n    return decodeBase64Url(encoded);\n  } catch {\n    return '';\n  }\n};\n\nconst serializeSettings = (settings) => {\n  const obj = {};\n  if (settings.forbidFunctionCalls) obj.forbidFunctionCalls = true;\n  if (settings.forbidFunctionCreation) obj.forbidFunctionCreation = true;\n  if (settings.haltOnSandboxError) obj.haltOnSandboxError = true;\n  if (settings.async) obj.async = true;\n  if (settings.executionQuota != null) obj.executionQuota = String(settings.executionQuota);\n  if (settings.scopeJson) obj.scopeJson = settings.scopeJson;\n  return Object.keys(obj).length ? JSON.stringify(obj) : null;\n};\n\nconst deserializeSettings = (str) => {\n  try {\n    const obj = JSON.parse(str);\n    return {\n      forbidFunctionCalls: Boolean(obj.forbidFunctionCalls),\n      forbidFunctionCreation: Boolean(obj.forbidFunctionCreation),\n      haltOnSandboxError: Boolean(obj.haltOnSandboxError),\n      async: Boolean(obj.async),\n      executionQuota: obj.executionQuota != null ? BigInt(obj.executionQuota) : undefined,\n      scopeJson: obj.scopeJson || '',\n    };\n  } catch { return null; }\n};\n\nconst getSandboxHashSettings = () => {\n  const encoded = getHashParams().get(SANDBOX_SETTINGS_HASH_KEY);\n  if (!encoded) return null;\n  try { return deserializeSettings(decodeBase64Url(encoded)); } catch { return null; }\n};\n\nconst setSandboxHashCode = (value) => {\n  const params = getHashParams();\n  if (value) params.set(SANDBOX_HASH_KEY, encodeBase64Url(value));\n  else params.delete(SANDBOX_HASH_KEY);\n  const settingsStr = serializeSettings(getSandboxSettings());\n  if (settingsStr) params.set(SANDBOX_SETTINGS_HASH_KEY, encodeBase64Url(settingsStr));\n  else params.delete(SANDBOX_SETTINGS_HASH_KEY);\n  const nextHash = params.toString();\n  const nextUrl = `${window.location.pathname}${window.location.search}${nextHash ? `#${nextHash}` : ''}`;\n  window.history.replaceState(null, '', nextUrl);\n};\n\nconst buildSandboxHashHref = (value, settings = null) => {\n  const params = new URLSearchParams();\n  if (value) params.set(SANDBOX_HASH_KEY, encodeBase64Url(value));\n  if (settings) {\n    const str = serializeSettings(settings);\n    if (str) params.set(SANDBOX_SETTINGS_HASH_KEY, encodeBase64Url(str));\n  }\n  return `${window.location.pathname}${window.location.search}#${params.toString()}`;\n};\n\nconst getRunnableCode = (code) => {\n  const needsReturn = !code.includes(';') && !code.startsWith('throw');\n  return `${needsReturn ? 'return ' : ''}${code}`;\n};\n\nconst resetSandboxOutput = () => {\n  sandboxConsoleOutputEl.textContent = 'No logs yet.';\n  sandboxReturnOutputEl.textContent = 'Run code to inspect the result.';\n  sandboxTicksOutputEl.textContent = '—';\n};\n\nconst setBypassNotice = (isBypassed) => {\n  sandboxBypassNoticeEl.classList.remove('sandbox-notice-safe', 'sandbox-notice-critical');\n  if (isBypassed) {\n    sandboxBypassNoticeEl.classList.add('sandbox-notice-critical');\n    sandboxBypassIconEl.textContent = '!';\n    sandboxBypassTextEl.textContent = '`globalThis.bypassed` is truthy. Critical notice: the sandbox protections were bypassed.';\n    return;\n  }\n\n  sandboxBypassNoticeEl.classList.add('sandbox-notice-safe');\n  sandboxBypassIconEl.textContent = '✓';\n  sandboxBypassTextEl.textContent = '`globalThis.bypassed` is falsy.';\n};\n\nconst setSandboxStatus = (text, type = '') => {\n  sandboxStatusEl.textContent = text;\n  sandboxStatusEl.classList.remove('status-running', 'status-success', 'status-error', 'status-halted');\n  if (type) sandboxStatusEl.classList.add(`status-${type}`);\n};\n\nconst openSandboxModal = () => {\n  sandboxModalEl.classList.remove('hidden');\n  sandboxModalEl.setAttribute('aria-hidden', 'false');\n  document.body.classList.add('modal-open');\n  editorView.focus();\n};\n\nconst openSandboxModalWithCode = (code, settings = null) => {\n  setEditorValue(code);\n  resetSandboxOutput();\n  setBypassNotice(false);\n  setSandboxStatus('Idle');\n  if (settings) applySettingsFromObject(settings);\n  setSandboxHashCode(code);\n  openSandboxModal();\n};\n\nconst closeSandboxModal = () => {\n  sandboxModalEl.classList.add('hidden');\n  sandboxModalEl.setAttribute('aria-hidden', 'true');\n  document.body.classList.remove('modal-open');\n  const params = getHashParams();\n  params.delete(SANDBOX_HASH_KEY);\n  params.delete(SANDBOX_SETTINGS_HASH_KEY);\n  const nextHash = params.toString();\n  const nextUrl = `${window.location.pathname}${window.location.search}${nextHash ? `#${nextHash}` : ''}`;\n  window.history.replaceState(null, '', nextUrl);\n};\n\nconst formatValue = (value) => {\n  if (typeof value === 'string') return value;\n  if (typeof value === 'undefined') return 'undefined';\n  if (typeof value === 'function') return value.toString();\n  if (typeof value === 'symbol') return value.toString();\n  if (typeof value === 'bigint') return `${value}n`;\n  if (value instanceof Error) return `${value.name}: ${value.message}`;\n\n  const seen = new WeakSet();\n  try {\n    return JSON.stringify(value, (_key, item) => {\n      if (typeof item === 'bigint') return `${item}n`;\n      if (typeof item === 'function') return item.toString();\n      if (typeof item === 'symbol') return item.toString();\n      if (!item || typeof item !== 'object') return item;\n      if (seen.has(item)) return '[Circular]';\n      seen.add(item);\n      return item;\n    }, 2);\n  } catch {\n    return String(value);\n  }\n};\n\nconst createUserRunner = (sandbox, code, isAsync, optimize) => {\n  return isAsync ? sandbox.compileAsync(code, optimize) : sandbox.compile(code, optimize);\n};\n\nconst createSandboxRunnerScope = (extraScope = {}) => {\n  const scope = {\n    type: 'Sandbox',\n    test: [(a, b) => 1],\n    test2: 1,\n    a: { b: { c: 2 } },\n    ...extraScope,\n  };\n  Object.setPrototypeOf(scope, LocalScope.prototype);\n  return scope;\n};\n\nconst runSandboxCode = async () => {\n  const code = getEditorValue();\n  if (!code.trim()) {\n    resetSandboxOutput();\n    setSandboxStatus('Idle');\n    return;\n  }\n\n  const settingsErrors = validateSettings();\n  if (settingsErrors.length) {\n    setSandboxStatus('Invalid settings', 'error');\n    sandboxConsoleOutputEl.textContent = settingsErrors.join('\\n');\n    sandboxReturnOutputEl.textContent = '—';\n    sandboxSettingsPanelEl.classList.remove('hidden');\n    sandboxSettingsBtnEl.classList.add('active');\n    return;\n  }\n\n  setSandboxHashCode(code);\n  setSandboxStatus('Running', 'running');\n  sandboxConsoleOutputEl.textContent = 'Running...';\n  sandboxReturnOutputEl.textContent = 'Running...';\n  setBypassNotice(false);\n  globalThis.bypassed = false;\n\n  const logs = [];\n  const pushLog = (level, args) => {\n    const prefix = level === 'log' ? '' : `[${level}] `;\n    logs.push(prefix + args.map((arg) => formatValue(arg)).join(' '));\n  };\n\n  const sandboxConsole = {\n    log: (...args) => pushLog('log', args),\n    info: (...args) => pushLog('info', args),\n    warn: (...args) => pushLog('warn', args),\n    error: (...args) => pushLog('error', args),\n    debug: (...args) => pushLog('debug', args),\n  };\n\n  const settings = getSandboxSettings();\n  const sandboxOptions = {};\n  if (settings.forbidFunctionCalls) sandboxOptions.forbidFunctionCalls = true;\n  if (settings.forbidFunctionCreation) sandboxOptions.forbidFunctionCreation = true;\n  if (settings.haltOnSandboxError) sandboxOptions.haltOnSandboxError = true;\n  if (settings.executionQuota != null) sandboxOptions.executionQuota = settings.executionQuota;\n\n  const { sandbox } = createSandboxInstance({}, sandboxOptions);\n  window.sandbox = sandbox;\n\n  sandbox.subscribeHalt((haltContext) => {\n    const ticks = runner?.context?.ctx?.ticks?.ticks;\n    sandboxConsoleOutputEl.textContent = logs.length ? logs.join('\\n') : 'No logs.';\n    sandboxTicksOutputEl.textContent = ticks != null ? ticks.toLocaleString() : '—';\n    setBypassNotice(Boolean(globalThis.bypassed));\n    if (haltContext.type === 'error') {\n      const isQuota = haltContext.error instanceof SandboxExecutionQuotaExceededError;\n      const label = 'Halted';\n      const reason = haltContext.error?.message || String(haltContext.error);\n      sandboxReturnOutputEl.textContent = reason;\n      setSandboxStatus(`${label}: ${reason}`, 'halted');\n    } else {\n      sandboxReturnOutputEl.textContent = haltContext.type;\n      setSandboxStatus(`Halted: ${haltContext.type}`, 'halted');\n    }\n  });\n\n  const extraScope = parseScopeJson(settings.scopeJson);\n  const scope = createSandboxRunnerScope({ console: sandboxConsole, cheat: () => {\n    globalThis.bypassed = true;\n    sandboxConsole.error(\"cheat() was called!\")\n  }, ...extraScope });\n\n  let runner;\n  try {\n    const execFn = createUserRunner(\n      sandbox,\n      code,\n      settings.async || runtimeTypeEl.value === 'async',\n      jitParsingEl.checked,\n    );\n    runner = execFn(scope, new LocalScope());\n    const result = await runner.run();\n    if (sandbox.halted) return;\n    sandboxConsoleOutputEl.textContent = logs.length ? logs.join('\\n') : 'No logs.';\n    sandboxReturnOutputEl.textContent = formatValue(result);\n    sandboxTicksOutputEl.textContent = runner.context.ctx.ticks.ticks.toLocaleString();\n    setBypassNotice(Boolean(globalThis.bypassed));\n    setSandboxStatus('Success', 'success');\n  } catch (error) {\n    if (sandbox.halted) return;\n    console.error(error);\n    sandboxConsoleOutputEl.textContent = logs.length ? logs.join('\\n') : 'No logs.';\n    sandboxReturnOutputEl.textContent = formatValue(error);\n    sandboxTicksOutputEl.textContent = runner ? String(runner.context.ctx.ticks.ticks.toLocaleString()) : '—';\n    setBypassNotice(Boolean(globalThis.bypassed));\n    setSandboxStatus('Error', 'error');\n  }\n};\n\nconst exec = async () => {\n  const tests = await testsPromise;\n  delete Object.anything;\n  delete {}.constructor.anything1;\n\n  window.bypassed = false;\n  const isAsync = runtimeTypeEl.value === 'async';\n  const jit = jitParsingEl.checked;\n  const { sandbox } = createSandboxInstance();\n  window.sandbox = sandbox;\n\n  class TestError {\n    constructor(error) { this.error = error; }\n  }\n\n  const validate = (value, compare) => {\n    if (compare === 'error') return value instanceof TestError;\n    if (typeof compare === 'string' && compare.startsWith('/') && compare.endsWith('/')) {\n      const reg = new RegExp(compare.substring(1, compare.length - 1));\n      return Boolean(value?.error?.message) && reg.test(value.error.message);\n    }\n    if (compare === null) return compare === value;\n    if (compare === 'NaN') return isNaN(value) && typeof value === 'number';\n    if (typeof compare !== 'object') return value === compare;\n    let res = value?.length === compare?.length;\n    for (let i in compare) res = res && validate(value?.[i], compare[i]);\n    return res;\n  };\n\n  // Group tests by category\n  const categories = [];\n  const categoryMap = new Map();\n  for (const test of tests) {\n    const cat = test.category || 'Uncategorized';\n    if (!categoryMap.has(cat)) {\n      categoryMap.set(cat, []);\n      categories.push(cat);\n    }\n    categoryMap.get(cat).push(test);\n  }\n\n  // Clear main\n  const main = document.getElementById('tests-main');\n  const summaryBar = document.getElementById('summary-bar');\n  main.querySelectorAll('.test-section').forEach(el => el.remove());\n  summaryBar.innerHTML = '';\n\n  // Build category nav\n  const catList = document.getElementById('category-list');\n  catList.innerHTML = '';\n\n  // Perf counters\n  let totalCompileNative = 0, totalCompileSandbox = 0;\n  let totalExecuteNative = 0, totalExecuteSandbox = 0;\n  let grandTotal = 0, grandPass = 0;\n\n  // Results per section (fill in after running)\n  const sectionResults = new Map();\n\n  // Run all tests and build DOM\n  for (const cat of categories) {\n    const catTests = categoryMap.get(cat);\n    const section = document.createElement('div');\n    section.className = 'test-section';\n    section.id = 'section-' + cat.replace(/[^a-z0-9]/gi, '-').toLowerCase();\n\n    const headerEl = document.createElement('div');\n    headerEl.className = 'section-header';\n\n    const titleEl = document.createElement('span');\n    titleEl.className = 'section-title';\n    titleEl.textContent = cat;\n\n    const countEl = document.createElement('span');\n    countEl.className = 'section-count';\n    countEl.textContent = catTests.length;\n\n    const rateEl = document.createElement('span');\n    rateEl.className = 'section-pass-rate';\n\n    const toggleEl = document.createElement('span');\n    toggleEl.className = 'section-toggle';\n    toggleEl.textContent = '▾';\n\n    headerEl.append(titleEl, countEl, rateEl, toggleEl);\n    headerEl.addEventListener('click', () => {\n      section.classList.toggle('collapsed');\n    });\n\n    const body = document.createElement('div');\n    body.className = 'section-body';\n\n    const table = document.createElement('table');\n    table.className = 'test-table';\n    table.innerHTML = `<colgroup>\n      <col class=\"col-code\">\n      <col class=\"col-eval\">\n      <col class=\"col-sandbox\">\n      <col class=\"col-verdict\">\n    </colgroup><thead><tr>\n      <th>Code</th>\n      <th>eval</th>\n      <th>Sandbox.js</th>\n      <th>Result</th>\n    </tr></thead>`;\n    const tbody = document.createElement('tbody');\n    table.appendChild(tbody);\n    body.appendChild(table);\n    section.append(headerEl, body);\n    main.appendChild(section);\n\n    let catPass = 0;\n    for (const test of catTests) {\n      let sandbox = createSandboxInstance().sandbox;\n      const state = {\n        type: 'eval', test: [(a, b) => 1], test2: 1,\n        a: { b: { c: 2 } }, Object, Math, Date, Array, undefined, NaN, Error\n      };\n      const state2 = createSandboxRunnerScope();\n      sandbox.context.ticks.ticks = 0n;\n      bypassed = false;\n\n      const tr = document.createElement('tr');\n      const runnableCode = getRunnableCode(test.code);\n\n      // Sandbox.js column\n      const sbResultTd = document.createElement('td');\n      sbResultTd.className = 'td-result';\n      let emsg = '';\n      let time = performance.now();\n      let ret;\n      try {\n        const fn = isAsync ? sandbox.compileAsync(runnableCode) : sandbox.compile(runnableCode);\n        totalCompileSandbox += performance.now() - time;\n        time = performance.now();\n        ret = await fn(state2, new LocalScope()).run();\n        totalExecuteSandbox += performance.now() - time;\n      } catch (e) {\n        emsg = e?.message;\n        sbResultTd.classList.add('error');\n        ret = new TestError(e);\n      }\n      let res;\n      try {\n        res = ret instanceof TestError ? ret : await ret;\n      } catch (e) {\n        emsg = e?.message;\n        sbResultTd.classList.add('error');\n        res = new TestError(e);\n      }\n      sbResultTd.title = emsg;\n      sbResultTd.textContent = bypassed\n        ? 'bypassed'\n        : res instanceof TestError ? 'Error'\n        : isNaN(res) && typeof res === 'number' ? 'NaN'\n        : JSON.stringify(res) + (ret instanceof Promise ? ' (Promise)' : '');\n\n      // Verdict column\n      const verdictTd = document.createElement('td');\n      verdictTd.className = 'td-verdict';\n      const valid = validate(res, test.safeExpect);\n      const passed = valid && !bypassed;\n      if (passed) catPass++;\n      verdictTd.textContent = bypassed ? 'BYPASSED' : valid ? 'PASS' : 'FAIL';\n      verdictTd.classList.add(passed ? 'positive' : 'negative');\n\n      // eval column\n      const evalTd = document.createElement('td');\n      evalTd.className = 'td-result';\n      let evalEmsg = '';\n      const evall = () => {\n        if (isAsync) {\n          return new Function('sandbox', `return (async () => {with (sandbox) {\\n${runnableCode}\\n}})()`);\n        }\n        return new Function('sandbox', `with (sandbox) {\\n${runnableCode}\\n}`);\n      };\n      const proxy = new Proxy(state, {\n        has(target, key, context) {\n          if (key in state) return Reflect.has(target, key, context);\n          if (key === 'x') return false;\n          throw new Error('Not allowed: ' + key);\n        },\n      });\n      bypassed = false;\n      time = performance.now();\n      try {\n        const fn = evall();\n        totalCompileNative += performance.now() - time;\n        time = performance.now();\n        const evalRet = test.code.includes('__proto__') ? undefined : await fn(proxy);\n        totalExecuteNative += performance.now() - time;\n        const evalRes = await evalRet;\n        evalTd.textContent = bypassed ? 'bypassed'\n          : isNaN(evalRet) && typeof evalRes === 'number' ? 'NaN'\n          : JSON.stringify(evalRes) + (evalRet instanceof Promise ? ' (Promise)' : '');\n      } catch (e) {\n        evalEmsg = e?.message;\n        evalTd.classList.add('error');\n        evalTd.textContent = 'Error';\n      }\n      evalTd.title = evalEmsg;\n      evalTd.classList.toggle('negative', bypassed);\n\n      // Code column\n      const codeTd = document.createElement('td');\n      codeTd.className = 'td-code';\n      codeTd.title = test.code;\n      const codeWrap = document.createElement('div');\n      codeWrap.className = 'td-code-wrap';\n      const codeText = document.createElement('span');\n      codeText.className = 'td-code-text';\n      codeText.textContent = test.code.length > 60 ? test.code.substring(0, 60) + '…' : test.code;\n      const runnerLink = document.createElement('a');\n      runnerLink.className = 'runner-link';\n      const testSettings = { forbidFunctionCalls: false, forbidFunctionCreation: false, haltOnSandboxError: false, async: isAsync, executionQuota: undefined, scopeJson: '' };\n      runnerLink.href = buildSandboxHashHref(runnableCode, testSettings);\n      runnerLink.title = 'Open this test in the Sandbox Runner';\n      runnerLink.setAttribute('aria-label', 'Open this test in the Sandbox Runner');\n      runnerLink.innerHTML = `\n        <svg class=\"runner-link-icon\" viewBox=\"0 0 20 20\" aria-hidden=\"true\" focusable=\"false\">\n          <path d=\"M6.7 6.1 3.6 10l3.1 3.9\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.8\"/>\n          <path d=\"M13.3 6.1 16.4 10l-3.1 3.9\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.8\"/>\n          <path d=\"M11.2 5.1 8.8 14.9\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.8\"/>\n        </svg>`;\n      runnerLink.addEventListener('click', (event) => {\n        if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey || event.button !== 0) return;\n        event.preventDefault();\n        openSandboxModalWithCode(runnableCode, testSettings);\n      });\n      codeWrap.append(codeText, runnerLink);\n      codeTd.appendChild(codeWrap);\n\n      tr.append(codeTd, evalTd, sbResultTd, verdictTd);\n      tbody.appendChild(tr);\n\n      Object.freeze = freeze;\n      globalThis.Function = Function;\n    }\n\n    grandTotal += catTests.length;\n    grandPass += catPass;\n\n    // Update pass-rate badge\n    const pct = Math.round((catPass / catTests.length) * 100);\n    rateEl.textContent = `${catPass}/${catTests.length}`;\n    rateEl.classList.add(pct === 100 ? 'rate-good' : 'rate-bad');\n\n    sectionResults.set(cat, { pass: catPass, total: catTests.length });\n\n    // Category nav item\n    const li = document.createElement('li');\n    const button = document.createElement('button');\n    button.type = 'button';\n    button.className = 'category-link';\n    button.dataset.sectionId = section.id;\n    button.innerHTML = `<span>${cat}</span><span class=\"cat-badge ${catPass === catTests.length ? 'cat-pass' : 'cat-fail'}\">${catPass}/${catTests.length}</span>`;\n    button.addEventListener('click', () => {\n      const header = document.querySelector('.site-header');\n      const offset = header ? header.offsetHeight : 0;\n      const top = section.getBoundingClientRect().top + window.scrollY - offset;\n      window.scrollTo({ top, behavior: 'smooth' });\n    });\n    li.appendChild(button);\n    catList.appendChild(li);\n  }\n\n  // Summary chips\n  const failCount = grandTotal - grandPass;\n  summaryBar.innerHTML = `\n    <div class=\"summary-chip\">\n      <span class=\"chip-val\">${grandTotal}</span>\n      <span class=\"chip-label\">Total Tests</span>\n    </div>\n    <div class=\"summary-chip chip-pass\">\n      <span class=\"chip-val\">${grandPass}</span>\n      <span class=\"chip-label\">Passing</span>\n    </div>\n    <div class=\"summary-chip chip-fail\">\n      <span class=\"chip-val\">${failCount}</span>\n      <span class=\"chip-label\">Failing</span>\n    </div>\n    <div class=\"summary-chip\">\n      <span class=\"chip-val\">${Math.round((grandPass / grandTotal) * 100)}%</span>\n      <span class=\"chip-label\">Pass Rate</span>\n    </div>\n  `;\n\n  // Performance table\n  renderPerf(totalCompileNative, totalCompileSandbox, totalExecuteNative, totalExecuteSandbox);\n\n  // Lodash benchmark (background)\n  ;(async () => {\n    const code = await (await fetch('https://cdn.jsdelivr.net/npm/lodash@4.17.20/lodash.min.js')).text();\n    let start = performance.now();\n    let error = '';\n    try {\n      new Sandbox().compile(code, !jit);\n    } catch (e) {\n      error = e.message;\n    }\n    const slodash = performance.now() - start;\n    start = performance.now();\n    new Function(code);\n    const elodash = performance.now() - start;\n\n    const timesBody = document.querySelector('#times tbody');\n    const tr = document.createElement('tr');\n    const td1 = document.createElement('td'); td1.textContent = 'Lodash compile';\n    const td2 = document.createElement('td'); td2.textContent = Math.round(elodash * 10) / 10 + 'ms';\n    const td3 = document.createElement('td');\n    td3.title = error;\n    td3.textContent = Math.round(slodash * 10) / 10 + 'ms';\n    tr.append(td1, td2, td3);\n    timesBody.appendChild(tr);\n  })();\n};\n\nfunction renderPerf(cn, cs, en, es) {\n  const body = document.querySelector('#times tbody');\n  body.innerHTML = '';\n  const rows = [\n    ['', 'eval', 'Sandbox'],\n    ['Compile', Math.round(cn * 10) / 10 + 'ms', Math.round(cs * 10) / 10 + 'ms'],\n    ['Execute', Math.round(en * 10) / 10 + 'ms', Math.round(es * 10) / 10 + 'ms'],\n    ['Total',   Math.round((cn + en) * 10) / 10 + 'ms', Math.round((cs + es) * 10) / 10 + 'ms'],\n  ];\n  for (const [label, evalVal, sbVal] of rows) {\n    const tr = document.createElement('tr');\n    const makeCell = (text, tag = 'td') => {\n      const el = document.createElement(tag);\n      el.textContent = text;\n      return el;\n    };\n    tr.append(makeCell(label, 'th'), makeCell(evalVal), makeCell(sbVal));\n    body.appendChild(tr);\n  }\n}\n\n// Active section highlight in nav\nconst observer = new IntersectionObserver((entries) => {\n  for (const entry of entries) {\n    const id = entry.target.id;\n    const link = document.querySelector(`#category-list .category-link[data-section-id=\"${id}\"]`);\n    if (link) link.classList.toggle('active', entry.isIntersecting);\n  }\n}, { threshold: 0.1 });\n\nconst observeSections = () => {\n  observer.disconnect();\n  document.querySelectorAll('.test-section').forEach(s => observer.observe(s));\n};\n\nexec().then(observeSections);\nrunBtnEl.addEventListener('click', () => exec().then(observeSections));\nruntimeTypeEl.addEventListener('change', () => exec().then(observeSections));\njitParsingEl.addEventListener('change', () => exec().then(observeSections));\n\nopenSandboxModalBtnEl.addEventListener('click', openSandboxModal);\nsandboxExecBtnEl.addEventListener('click', runSandboxCode);\nsandboxCloseBtnEl.addEventListener('click', closeSandboxModal);\nsandboxClearBtnEl.addEventListener('click', () => {\n  resetSettings();\n  setEditorValue('');\n  resetSandboxOutput();\n  setBypassNotice(false);\n  setSandboxStatus('Idle');\n});\nsandboxModalEl.addEventListener('click', (event) => {\n  if (event.target.dataset.closeModal === 'true') closeSandboxModal();\n});\nwindow.addEventListener('keydown', (event) => {\n  if (event.key === 'Escape' && !sandboxModalEl.classList.contains('hidden')) closeSandboxModal();\n});\nwindow.addEventListener('hashchange', () => {\n  const code = getSandboxHashCode();\n  if (!code) return;\n  const settings = getSandboxHashSettings();\n  if (settings) applySettingsFromObject(settings);\n  setEditorValue(code);\n  openSandboxModal();\n  setSandboxStatus('Idle');\n});\n\nconst initialSandboxCode = getSandboxHashCode();\nif (initialSandboxCode) {\n  const initialSettings = getSandboxHashSettings();\n  if (initialSettings) applySettingsFromObject(initialSettings);\n  setEditorValue(initialSandboxCode);\n  openSandboxModal();\n}\n\nsetBypassNotice(false);\n"
  },
  {
    "path": "test/eval/testCases/arithmetic-operators.data.ts",
    "content": "'use strict';\nimport { TestCase } from './types.js';\n\nexport const tests: TestCase[] = [\n  {\n    code: '1+1',\n    evalExpect: 2,\n    safeExpect: 2,\n    category: 'Arithmetic Operators',\n  },\n  {\n    code: '1 * 2 + 3 * (4 + 5) * 6',\n    evalExpect: 164,\n    safeExpect: 164,\n    category: 'Arithmetic Operators',\n  },\n  {\n    code: '(test2 * (2 + 3 * (4 + 5))) * 6',\n    evalExpect: 174,\n    safeExpect: 174,\n    category: 'Arithmetic Operators',\n  },\n  {\n    code: '1+2*4/5-6+7/8 % 9+10-11-12/13*14',\n    evalExpect: -16.448076923076925,\n    safeExpect: -16.448076923076925,\n    category: 'Arithmetic Operators',\n  },\n  {\n    code: 'test2 **= 0',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Arithmetic Operators',\n  },\n  {\n    code: '2 ** 3',\n    evalExpect: 8,\n    safeExpect: 8,\n    category: 'Arithmetic Operators',\n  },\n  {\n    code: '10 ** 0',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Arithmetic Operators',\n  },\n  {\n    code: '2 ** 10',\n    evalExpect: 1024,\n    safeExpect: 1024,\n    category: 'Arithmetic Operators',\n  },\n  {\n    code: '3 ** 2 ** 2',\n    evalExpect: 81,\n    safeExpect: 81,\n    category: 'Arithmetic Operators',\n  },\n  {\n    code: 'test2 %= 1',\n    evalExpect: 0,\n    safeExpect: 0,\n    category: 'Arithmetic Operators',\n  },\n  {\n    code: \"+'1'\",\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Arithmetic Operators',\n  },\n  {\n    code: \"-'1'\",\n    evalExpect: -1,\n    safeExpect: -1,\n    category: 'Arithmetic Operators',\n  },\n  {\n    code: 'var i = 1; return i + 1',\n    evalExpect: 'error',\n    safeExpect: 2,\n    category: 'Arithmetic Operators',\n  },\n  {\n    code: 'let j = 1; return j + 1',\n    evalExpect: 'error',\n    safeExpect: 2,\n    category: 'Arithmetic Operators',\n  },\n  {\n    code: 'const k = 1; return k + 1',\n    evalExpect: 'error',\n    safeExpect: 2,\n    category: 'Arithmetic Operators',\n  },\n  {\n    code: 'null?.a + 5',\n    evalExpect: 'NaN',\n    safeExpect: 'NaN',\n    category: 'Arithmetic Operators',\n  },\n  {\n    code: '({}).a ?? 10 + 5',\n    evalExpect: 15,\n    safeExpect: 15,\n    category: 'Arithmetic Operators',\n  },\n  {\n    code: 'let x = 5; return x++ + 2',\n    evalExpect: 7,\n    safeExpect: 7,\n    category: 'Arithmetic Operators',\n  },\n  {\n    code: 'let y = 5; return ++y + 2',\n    evalExpect: 8,\n    safeExpect: 8,\n    category: 'Arithmetic Operators',\n  },\n  {\n    code: '+-5',\n    evalExpect: -5,\n    safeExpect: -5,\n    category: 'Arithmetic Operators',\n  },\n  {\n    code: '~-1',\n    evalExpect: 0,\n    safeExpect: 0,\n    category: 'Arithmetic Operators',\n  },\n  {\n    code: '(1, 2) + (3, 4)',\n    evalExpect: 6,\n    safeExpect: 6,\n    category: 'Arithmetic Operators',\n  },\n  {\n    code: 'let z = 5; return --z * 2',\n    evalExpect: 8,\n    safeExpect: 8,\n    category: 'Arithmetic Operators',\n  },\n  {\n    code: 'let z = 5; return z-- * 2',\n    evalExpect: 10,\n    safeExpect: 10,\n    category: 'Arithmetic Operators',\n  },\n  {\n    code: '1 + 2 + 3 + 4',\n    evalExpect: 10,\n    safeExpect: 10,\n    category: 'Arithmetic Operators',\n  },\n  {\n    code: '10 - 5 - 2',\n    evalExpect: 3,\n    safeExpect: 3,\n    category: 'Arithmetic Operators',\n  },\n  {\n    code: '2 * 3 * 4',\n    evalExpect: 24,\n    safeExpect: 24,\n    category: 'Arithmetic Operators',\n  },\n  {\n    code: '100 / 5 / 2',\n    evalExpect: 10,\n    safeExpect: 10,\n    category: 'Arithmetic Operators',\n  },\n  {\n    code: '17 % 5 % 2',\n    evalExpect: 0,\n    safeExpect: 0,\n    category: 'Arithmetic Operators',\n  },\n  {\n    code: 'let a = 1, b = 2, c = 3; return a + b + c',\n    evalExpect: 6,\n    safeExpect: 6,\n    category: 'Arithmetic Operators',\n  },\n  {\n    code: 'const a = 1, b = 2; return a * b',\n    evalExpect: 2,\n    safeExpect: 2,\n    category: 'Arithmetic Operators',\n  },\n  {\n    code: '(false ? 1 : 2) + (true ? 3 : 4)',\n    evalExpect: 5,\n    safeExpect: 5,\n    category: 'Arithmetic Operators',\n  },\n  {\n    code: 'let x = 5; x++; return x',\n    evalExpect: 6,\n    safeExpect: 6,\n    category: 'Arithmetic Operators',\n  },\n  {\n    code: 'let x = 5; return ++x',\n    evalExpect: 6,\n    safeExpect: 6,\n    category: 'Arithmetic Operators',\n  },\n  {\n    code: 'let x = 5; x--; return x',\n    evalExpect: 4,\n    safeExpect: 4,\n    category: 'Arithmetic Operators',\n  },\n  {\n    code: 'let x = 5; return --x',\n    evalExpect: 4,\n    safeExpect: 4,\n    category: 'Arithmetic Operators',\n  },\n];\n"
  },
  {
    "path": "test/eval/testCases/arithmetic-operators.spec.ts",
    "content": "'use strict';\nimport { run, getState } from './test-utils.js';\nimport { tests } from './arithmetic-operators.data.js';\n\ndescribe('Arithmetic Operators Tests', () => {\n  describe('Sync', () => {\n    tests.forEach((test) => {\n      let state = getState();\n      it(test.code.substring(0, 100), async () => {\n        await run(test, state, false);\n      });\n    });\n  });\n\n  describe('Async', () => {\n    tests.forEach((test) => {\n      let state = getState();\n      it(test.code.substring(0, 100), async () => {\n        await run(test, state, true);\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/eval/testCases/assignment-operators.data.ts",
    "content": "'use strict';\nimport { TestCase } from './types.js';\n\nexport const tests: TestCase[] = [\n  {\n    code: 'test.b = test2 - test[0]() - test[0]()',\n    evalExpect: -1,\n    safeExpect: -1,\n    category: 'Assignment Operators',\n  },\n  {\n    code: 'test2++',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Assignment Operators',\n  },\n  {\n    code: '++test2',\n    evalExpect: 2,\n    safeExpect: 2,\n    category: 'Assignment Operators',\n  },\n  {\n    code: 'test2 = 1',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Assignment Operators',\n  },\n  {\n    code: 'test2 += 1',\n    evalExpect: 2,\n    safeExpect: 2,\n    category: 'Assignment Operators',\n  },\n  {\n    code: 'test2 -= 1',\n    evalExpect: 0,\n    safeExpect: 0,\n    category: 'Assignment Operators',\n  },\n  {\n    code: 'test2 *= 2',\n    evalExpect: 2,\n    safeExpect: 2,\n    category: 'Assignment Operators',\n  },\n  {\n    code: 'test2 /= 2',\n    evalExpect: 0.5,\n    safeExpect: 0.5,\n    category: 'Assignment Operators',\n  },\n  {\n    code: 'let a = 1; let a = 2; return a',\n    evalExpect: 'error',\n    safeExpect: \"/Identifier 'a' has already been declared/\",\n    category: 'Assignment Operators',\n  },\n  {\n    code: 'var z = 1; var z = 2; return z',\n    evalExpect: 2,\n    safeExpect: 2,\n    category: 'Assignment Operators',\n  },\n  {\n    code: 'let y = 1; var y = 2; return y',\n    evalExpect: 'error',\n    safeExpect: \"/Identifier 'y' has already been declared/\",\n    category: 'Assignment Operators',\n  },\n  {\n    code: \"var z = globalThis; let y = globalThis; return z.constructor.name === 'SandboxGlobal' && z === y\",\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Assignment Operators',\n  },\n  {\n    code: 'var v = 1;  var v = Object; return v.name',\n    evalExpect: 'Object',\n    safeExpect: 'Object',\n    category: 'Assignment Operators',\n  },\n  {\n    code: 'let a = 1; {let a = 2}; return a',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Assignment Operators',\n  },\n  {\n    code: 'this = 1',\n    evalExpect: 'error',\n    safeExpect: '/\"this\" cannot be assigned/',\n    category: 'Assignment Operators',\n  },\n  {\n    code: 'const l = 1; return l = 2',\n    evalExpect: 'error',\n    safeExpect: '/Assignment to constant variable/',\n    category: 'Assignment Operators',\n  },\n  {\n    code: 'delete 1',\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Assignment Operators',\n  },\n  {\n    code: 'let a = {b: 1}; return delete a.b',\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Assignment Operators',\n  },\n  {\n    code: 'let b = {a: 1}; return delete b',\n    evalExpect: false,\n    safeExpect: false,\n    category: 'Assignment Operators',\n  },\n  {\n    code: 'let x = 5; x <<= 1; return x',\n    evalExpect: 10,\n    safeExpect: 10,\n    category: 'Assignment Operators',\n  },\n  {\n    code: 'let x = 8; x >>= 1; return x',\n    evalExpect: 4,\n    safeExpect: 4,\n    category: 'Assignment Operators',\n  },\n  {\n    code: 'let obj = {a: 1}; delete obj.a; return obj.a === undefined',\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Assignment Operators',\n  },\n  {\n    code: 'delete {a: 1}.a',\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Assignment Operators',\n  },\n  {\n    code: 'let x = 10; x <<= 2; return x;',\n    evalExpect: 40,\n    safeExpect: 40,\n    category: 'Assignment Operators',\n  },\n  {\n    code: 'let x = 16; x >>= 2; return x;',\n    evalExpect: 4,\n    safeExpect: 4,\n    category: 'Assignment Operators',\n  },\n  {\n    code: '(1 == 1) == true',\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Assignment Operators',\n  },\n  {\n    code: 'let x = (1, 2, 3); return x',\n    evalExpect: 3,\n    safeExpect: 3,\n    category: 'Assignment Operators',\n  },\n  {\n    code: 'let x = 0; x = x || 5; return x',\n    evalExpect: 5,\n    safeExpect: 5,\n    category: 'Assignment Operators',\n  },\n  {\n    code: 'let x = 10; x = x && 5; return x',\n    evalExpect: 5,\n    safeExpect: 5,\n    category: 'Assignment Operators',\n  },\n  {\n    code: 'let x = 10; x &&= 5; return x',\n    evalExpect: 5,\n    safeExpect: 5,\n    category: 'Assignment Operators',\n  },\n  {\n    code: 'let x = 0; x &&= 5; return x',\n    evalExpect: 0,\n    safeExpect: 0,\n    category: 'Assignment Operators',\n  },\n  {\n    code: 'let x = false; x &&= true; return x',\n    evalExpect: false,\n    safeExpect: false,\n    category: 'Assignment Operators',\n  },\n  {\n    code: 'let x = 0; x ||= 5; return x',\n    evalExpect: 5,\n    safeExpect: 5,\n    category: 'Assignment Operators',\n  },\n  {\n    code: 'let x = 10; x ||= 5; return x',\n    evalExpect: 10,\n    safeExpect: 10,\n    category: 'Assignment Operators',\n  },\n  {\n    code: 'let x = false; x ||= \"default\"; return x',\n    evalExpect: 'default',\n    safeExpect: 'default',\n    category: 'Assignment Operators',\n  },\n  {\n    code: 'let x = null; x ??= 5; return x',\n    evalExpect: 5,\n    safeExpect: 5,\n    category: 'Assignment Operators',\n  },\n  {\n    code: 'let x = undefined; x ??= 10; return x',\n    evalExpect: 10,\n    safeExpect: 10,\n    category: 'Assignment Operators',\n  },\n  {\n    code: 'let x = 0; x ??= 5; return x',\n    evalExpect: 0,\n    safeExpect: 0,\n    category: 'Assignment Operators',\n  },\n  {\n    code: 'let x = \"\"; x ??= \"default\"; return x',\n    evalExpect: '',\n    safeExpect: '',\n    category: 'Assignment Operators',\n  },\n];\n"
  },
  {
    "path": "test/eval/testCases/assignment-operators.spec.ts",
    "content": "'use strict';\nimport { run, getState } from './test-utils.js';\nimport { tests } from './assignment-operators.data.js';\n\ndescribe('Assignment Operators Tests', () => {\n  describe('Sync', () => {\n    tests.forEach((test) => {\n      let state = getState();\n      it(test.code.substring(0, 100), async () => {\n        await run(test, state, false);\n      });\n    });\n  });\n\n  describe('Async', () => {\n    tests.forEach((test) => {\n      let state = getState();\n      it(test.code.substring(0, 100), async () => {\n        await run(test, state, true);\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/eval/testCases/bitwise-operators.data.ts",
    "content": "'use strict';\nimport { TestCase } from './types.js';\n\nexport const tests: TestCase[] = [\n  {\n    code: '~test2',\n    evalExpect: -2,\n    safeExpect: -2,\n    category: 'Bitwise Operators',\n  },\n  {\n    code: 'let test3 = 8; test3 >>>= 2; return test3',\n    evalExpect: 2,\n    safeExpect: 2,\n    category: 'Bitwise Operators',\n  },\n  {\n    code: 'let test4 = -8; test4 >>>= 2; return test4',\n    evalExpect: 1073741822,\n    safeExpect: 1073741822,\n    category: 'Bitwise Operators',\n  },\n  {\n    code: 'let test5 = 16; test5 >>>= 1; return test5',\n    evalExpect: 8,\n    safeExpect: 8,\n    category: 'Bitwise Operators',\n  },\n  {\n    code: 'let test6 = -1; test6 >>>= 0; return test6',\n    evalExpect: 4294967295,\n    safeExpect: 4294967295,\n    category: 'Bitwise Operators',\n  },\n  {\n    code: 'test2 ^= 1',\n    evalExpect: 0,\n    safeExpect: 0,\n    category: 'Bitwise Operators',\n  },\n  {\n    code: 'test2 &= 3',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Bitwise Operators',\n  },\n  {\n    code: 'test2 |= 2',\n    evalExpect: 3,\n    safeExpect: 3,\n    category: 'Bitwise Operators',\n  },\n  {\n    code: 'test2 & 1',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Bitwise Operators',\n  },\n  {\n    code: 'test2 | 4',\n    evalExpect: 5,\n    safeExpect: 5,\n    category: 'Bitwise Operators',\n  },\n  {\n    code: '8 >> 1 >> 1',\n    evalExpect: 2,\n    safeExpect: 2,\n    category: 'Bitwise Operators',\n  },\n  {\n    code: '1 << 2 << 1',\n    evalExpect: 8,\n    safeExpect: 8,\n    category: 'Bitwise Operators',\n  },\n  {\n    code: '16 >>> 2 >> 1',\n    evalExpect: 2,\n    safeExpect: 2,\n    category: 'Bitwise Operators',\n  },\n  {\n    code: '1 << 1 << 1',\n    evalExpect: 4,\n    safeExpect: 4,\n    category: 'Bitwise Operators',\n  },\n  {\n    code: '16 >> 1 >> 1',\n    evalExpect: 4,\n    safeExpect: 4,\n    category: 'Bitwise Operators',\n  },\n  {\n    code: '5 & 7 & 3',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Bitwise Operators',\n  },\n  {\n    code: '1 | 2 | 4',\n    evalExpect: 7,\n    safeExpect: 7,\n    category: 'Bitwise Operators',\n  },\n  {\n    code: '15 ^ 10 ^ 5',\n    evalExpect: 0,\n    safeExpect: 0,\n    category: 'Bitwise Operators',\n  },\n  {\n    code: '~5',\n    evalExpect: -6,\n    safeExpect: -6,\n    category: 'Bitwise Operators',\n  },\n];\n"
  },
  {
    "path": "test/eval/testCases/bitwise-operators.spec.ts",
    "content": "'use strict';\nimport { run, getState } from './test-utils.js';\nimport { tests } from './bitwise-operators.data.js';\n\ndescribe('Bitwise Operators Tests', () => {\n  describe('Sync', () => {\n    tests.forEach((test) => {\n      let state = getState();\n      it(test.code.substring(0, 100), async () => {\n        await run(test, state, false);\n      });\n    });\n  });\n\n  describe('Async', () => {\n    tests.forEach((test) => {\n      let state = getState();\n      it(test.code.substring(0, 100), async () => {\n        await run(test, state, true);\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/eval/testCases/comments.data.ts",
    "content": "'use strict';\nimport { TestCase } from './types.js';\n\nexport const tests: TestCase[] = [\n  {\n    code: '/* 2 */ 1',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Comments',\n  },\n  {\n    code: '1 // 2',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Comments',\n  },\n  {\n    code: '/* 2 */ (() => /* 3 */ 1)() // 4',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Comments',\n  },\n  {\n    code: '/* never closed',\n    evalExpect: 'error',\n    safeExpect: \"/Unclosed comment '\\\\/\\\\*/\",\n    category: 'Comments',\n  },\n];\n"
  },
  {
    "path": "test/eval/testCases/comments.spec.ts",
    "content": "'use strict';\nimport { run, getState } from './test-utils.js';\nimport { tests } from './comments.data.js';\n\ndescribe('Comments Tests', () => {\n  describe('Sync', () => {\n    tests.forEach((test) => {\n      let state = getState();\n      it(test.code.substring(0, 100), async () => {\n        await run(test, state, false);\n      });\n    });\n  });\n\n  describe('Async', () => {\n    tests.forEach((test) => {\n      let state = getState();\n      it(test.code.substring(0, 100), async () => {\n        await run(test, state, true);\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/eval/testCases/comparison-operators.data.ts",
    "content": "'use strict';\nimport { TestCase } from './types.js';\n\nexport const tests: TestCase[] = [\n  {\n    code: \"test2 == '1'\",\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Comparison Operators',\n  },\n  {\n    code: \"test2 === '1'\",\n    evalExpect: false,\n    safeExpect: false,\n    category: 'Comparison Operators',\n  },\n  {\n    code: \"test2 != '1'\",\n    evalExpect: false,\n    safeExpect: false,\n    category: 'Comparison Operators',\n  },\n  {\n    code: \"test2 !== '1'\",\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Comparison Operators',\n  },\n  {\n    code: 'test2 < 1',\n    evalExpect: false,\n    safeExpect: false,\n    category: 'Comparison Operators',\n  },\n  {\n    code: 'test2 > 1',\n    evalExpect: false,\n    safeExpect: false,\n    category: 'Comparison Operators',\n  },\n  {\n    code: 'test2 >= 1',\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Comparison Operators',\n  },\n  {\n    code: 'test2 <= 1',\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Comparison Operators',\n  },\n];\n"
  },
  {
    "path": "test/eval/testCases/comparison-operators.spec.ts",
    "content": "'use strict';\nimport { run, getState } from './test-utils.js';\nimport { tests } from './comparison-operators.data.js';\n\ndescribe('Comparison Operators Tests', () => {\n  describe('Sync', () => {\n    tests.forEach((test) => {\n      let state = getState();\n      it(test.code.substring(0, 100), async () => {\n        await run(test, state, false);\n      });\n    });\n  });\n\n  describe('Async', () => {\n    tests.forEach((test) => {\n      let state = getState();\n      it(test.code.substring(0, 100), async () => {\n        await run(test, state, true);\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/eval/testCases/complex-expressions.data.ts",
    "content": "'use strict';\nimport { TestCase } from './types.js';\n\nexport const tests: TestCase[] = [\n  {\n    code: 'let a = null; let b = [a?.a]; return b[0] === undefined && b.length',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Complex Expressions',\n  },\n  {\n    code: '{a: 1, ...{b: 2, c: {d: test2,}}, e: 5}',\n    evalExpect: {\n      a: 1,\n      b: 2,\n      c: {\n        d: 1,\n      },\n      e: 5,\n    },\n    safeExpect: {\n      a: 1,\n      b: 2,\n      c: {\n        d: 1,\n      },\n      e: 5,\n    },\n    category: 'Complex Expressions',\n  },\n  {\n    code: '{a: 1,b: 2, /*,*/}',\n    evalExpect: {\n      a: 1,\n      b: 2,\n    },\n    safeExpect: {\n      a: 1,\n      b: 2,\n    },\n    category: 'Complex Expressions',\n  },\n  {\n    code: 'test2 = 1,(() => 2)(),test2',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Complex Expressions',\n  },\n  {\n    code: 'const a = () => {return 1}; const b = () => {return 2}; return (() => a() + b())()',\n    evalExpect: 3,\n    safeExpect: 3,\n    category: 'Complex Expressions',\n  },\n  {\n    code: '({}).a?.toString()',\n    category: 'Complex Expressions',\n  },\n  {\n    code: '({}).a?.toSring() + ({}).b?.toString()',\n    evalExpect: 'NaN',\n    safeExpect: 'NaN',\n    category: 'Complex Expressions',\n  },\n  {\n    code: \"({})['b']?.toString() === undefined\",\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Complex Expressions',\n  },\n  {\n    code: '({}).c?.()() ? 1 : 2',\n    evalExpect: 2,\n    safeExpect: 2,\n    category: 'Complex Expressions',\n  },\n  {\n    code: 'function plus(n,u){return n+u};function minus(n,u){return n-u};var added=plus(1,10);return minus(added,5);',\n    evalExpect: 6,\n    safeExpect: 6,\n    category: 'Complex Expressions',\n  },\n  {\n    code: 'function LinkedListNode(e){this.value=e,this.next=null}function reverse(e){let n,t,r=e;for(;r;)t=r.next,r.next=n,n=r,r=t;return n}function reverse(e){if(!e||!e.next)return e;let n=reverse(e.next);return e.next.next=e,e.next=null,n} let l1 = new LinkedListNode(1); l1.next = new LinkedListNode(2); return reverse(l1);',\n    evalExpect: {\n      value: 2,\n      next: {\n        value: 1,\n        next: null,\n      },\n    },\n    safeExpect: {\n      value: 2,\n      next: {\n        value: 1,\n        next: null,\n      },\n    },\n    category: 'Complex Expressions',\n  },\n  {\n    code: 'const f = function factorial(n) { return n <= 1 ? 1 : n * factorial(n - 1); }; return f(5)',\n    evalExpect: 120,\n    safeExpect: 120,\n    category: 'Complex Expressions',\n  },\n  {\n    code: '({a: 10}).a?.toString() + 5',\n    evalExpect: '105',\n    safeExpect: '105',\n    category: 'Complex Expressions',\n  },\n  {\n    code: 'let arr = [1, 2, 3]; return arr?.[1]',\n    evalExpect: 2,\n    safeExpect: 2,\n    category: 'Complex Expressions',\n  },\n  {\n    code: 'let obj = {fn: () => 42}; return obj?.fn?.()',\n    evalExpect: 42,\n    safeExpect: 42,\n    category: 'Complex Expressions',\n  },\n];\n"
  },
  {
    "path": "test/eval/testCases/complex-expressions.spec.ts",
    "content": "'use strict';\nimport { run, getState } from './test-utils.js';\nimport { tests } from './complex-expressions.data.js';\n\ndescribe('Complex Expressions Tests', () => {\n  describe('Sync', () => {\n    tests.forEach((test) => {\n      let state = getState();\n      it(test.code.substring(0, 100), async () => {\n        await run(test, state, false);\n      });\n    });\n  });\n\n  describe('Async', () => {\n    tests.forEach((test) => {\n      let state = getState();\n      it(test.code.substring(0, 100), async () => {\n        await run(test, state, true);\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/eval/testCases/conditionals.data.ts",
    "content": "'use strict';\nimport { TestCase } from './types.js';\n\nexport const tests: TestCase[] = [\n  {\n    code: \"test[test2] ? true : false ? 'not ok' : 'ok'\",\n    evalExpect: 'ok',\n    safeExpect: 'ok',\n    category: 'Conditionals',\n  },\n  {\n    code: 'let ok = true; false ? ok = false : ok; return ok',\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Conditionals',\n  },\n  {\n    code: 'if (true) { return true; } else return false',\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Conditionals',\n  },\n  {\n    code: 'let a = false; if (a) { return false; } else return true',\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Conditionals',\n  },\n  {\n    code: 'let a = null; if (a?.a) { return false; } else return true',\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Conditionals',\n  },\n  {\n    code: 'if (false) return true; else if (false) {return true} else return false',\n    evalExpect: false,\n    safeExpect: false,\n    category: 'Conditionals',\n  },\n  {\n    code: 'if (false) { return true; } else return false',\n    evalExpect: false,\n    safeExpect: false,\n    category: 'Conditionals',\n  },\n  {\n    code: 'if (false) return true; return false',\n    evalExpect: false,\n    safeExpect: false,\n    category: 'Conditionals',\n  },\n  {\n    code: 'if (true) {\\n  if (false)\\n    if (true)\\n      if (false)\\n        return 1\\n      else if (true)\\n        return 2\\n      else\\n        return 3\\n    else\\n      return 4\\n  else if (true)\\n    if (false)\\n      return 5\\n    else if (true)\\n      return 6\\n    else\\n      return 7\\n  else\\n    return 8\\n} else if (true)\\n  return 9;',\n    evalExpect: 6,\n    safeExpect: 6,\n    category: 'Conditionals',\n  },\n];\n"
  },
  {
    "path": "test/eval/testCases/conditionals.spec.ts",
    "content": "'use strict';\nimport { run, getState } from './test-utils.js';\nimport { tests } from './conditionals.data.js';\n\ndescribe('Conditionals Tests', () => {\n  describe('Sync', () => {\n    tests.forEach((test) => {\n      let state = getState();\n      it(test.code.substring(0, 100), async () => {\n        await run(test, state, false);\n      });\n    });\n  });\n\n  describe('Async', () => {\n    tests.forEach((test) => {\n      let state = getState();\n      it(test.code.substring(0, 100), async () => {\n        await run(test, state, true);\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/eval/testCases/data-types.data.ts",
    "content": "'use strict';\nimport { TestCase } from './types.js';\n\nexport const tests: TestCase[] = [\n  {\n    code: '`${type}`',\n    evalExpect: 'eval',\n    safeExpect: 'Sandbox',\n    category: 'Data Types',\n  },\n  {\n    code: 'test2',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Data Types',\n  },\n  {\n    code: '2.2204460492503130808472633361816E-16',\n    evalExpect: 2.220446049250313e-16,\n    safeExpect: 2.220446049250313e-16,\n    category: 'Data Types',\n  },\n  {\n    code: '\"test2\"',\n    evalExpect: 'test2',\n    safeExpect: 'test2',\n    category: 'Data Types',\n  },\n  {\n    code: '`test2 is ${`also ${test2}`}`',\n    evalExpect: 'test2 is also 1',\n    safeExpect: 'test2 is also 1',\n    category: 'Data Types',\n  },\n  {\n    code: '\"\\\\\\\\\"',\n    evalExpect: '\\\\',\n    safeExpect: '\\\\',\n    category: 'Data Types',\n  },\n  {\n    code: \"`\\\\\\\\$$\\\\${${`\\\\\\\\\\\\`${'ok'}`}\\\\\\\\}`\",\n    evalExpect: '\\\\$$${\\\\`ok\\\\}',\n    safeExpect: '\\\\$$${\\\\`ok\\\\}',\n    category: 'Data Types',\n  },\n  {\n    code: '[\"\\\\\\\\\", \"\\\\xd9\", \"\\\\n\", \"\\\\r\", \"\\\\u2028\", \"\\\\u2029\"]',\n    evalExpect: ['\\\\', '\\u00d9', '\\n', '\\r', '\\u2028', '\\u2029'],\n    safeExpect: ['\\\\', '\\u00d9', '\\n', '\\r', '\\u2028', '\\u2029'],\n    category: 'Data Types',\n  },\n  {\n    code: \"/a/.test('a')\",\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Data Types',\n  },\n  {\n    code: \"/a/i.test('A')\",\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Data Types',\n  },\n  {\n    code: \"let reg = /a/g; reg.exec('aaa'); return reg.exec('aaa').index\",\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Data Types',\n  },\n  {\n    code: '(1n + 0x1n).toString()',\n    evalExpect: '2',\n    safeExpect: '2',\n    category: 'Data Types',\n  },\n  {\n    code: '0b1010',\n    evalExpect: 10,\n    safeExpect: 10,\n    category: 'Data Types',\n  },\n  {\n    code: '0B1111',\n    evalExpect: 15,\n    safeExpect: 15,\n    category: 'Data Types',\n  },\n  {\n    code: '0b1010n.toString()',\n    evalExpect: '10',\n    safeExpect: '10',\n    category: 'Data Types',\n  },\n  {\n    code: '0b1_000',\n    evalExpect: 8,\n    safeExpect: 8,\n    category: 'Data Types',\n  },\n  {\n    code: '1_000',\n    evalExpect: 1000,\n    safeExpect: 1000,\n    category: 'Data Types',\n  },\n  {\n    code: '0b0',\n    evalExpect: 0,\n    safeExpect: 0,\n    category: 'Data Types',\n  },\n  {\n    code: '0o17',\n    evalExpect: 15,\n    safeExpect: 15,\n    category: 'Data Types',\n  },\n  {\n    code: '0O77',\n    evalExpect: 63,\n    safeExpect: 63,\n    category: 'Data Types',\n  },\n  {\n    code: '0o17n.toString()',\n    evalExpect: '15',\n    safeExpect: '15',\n    category: 'Data Types',\n  },\n  {\n    code: '0o7_777',\n    evalExpect: 4095,\n    safeExpect: 4095,\n    category: 'Data Types',\n  },\n  {\n    code: '0o0',\n    evalExpect: 0,\n    safeExpect: 0,\n    category: 'Data Types',\n  },\n  {\n    code: '0b1010 + 0o17',\n    evalExpect: 25,\n    safeExpect: 25,\n    category: 'Data Types',\n  },\n  {\n    code: '(0b1010n + 0o17n).toString()',\n    evalExpect: '25',\n    safeExpect: '25',\n    category: 'Data Types',\n  },\n  {\n    code: \"String(BigInt('12345'))\",\n    evalExpect: '12345',\n    safeExpect: '12345',\n    category: 'Data Types',\n  },\n  {\n    code: '(123_456_789n).toString()',\n    evalExpect: '123456789',\n    safeExpect: '123456789',\n    category: 'Data Types',\n  },\n  {\n    code: \"/test/gi.test('test')\",\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Data Types',\n  },\n  {\n    code: \"/[a-z]+/i.test('Hello')\",\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Data Types',\n  },\n  {\n    code: 'NaN',\n    evalExpect: 'NaN',\n    safeExpect: 'NaN',\n    category: 'Data Types',\n  },\n  {\n    code: \"typeof Infinity === 'number'\",\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Data Types',\n  },\n  {\n    code: \"typeof undefined === 'undefined'\",\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Data Types',\n  },\n  {\n    code: 'null',\n    evalExpect: null,\n    safeExpect: null,\n    category: 'Data Types',\n  },\n  {\n    code: 'true',\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Data Types',\n  },\n  {\n    code: 'false',\n    evalExpect: false,\n    safeExpect: false,\n    category: 'Data Types',\n  },\n  {\n    code: 'undefined === undefined',\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Data Types',\n  },\n];\n"
  },
  {
    "path": "test/eval/testCases/data-types.spec.ts",
    "content": "'use strict';\nimport { run, getState } from './test-utils.js';\nimport { tests } from './data-types.data.js';\n\ndescribe('Data Types Tests', () => {\n  describe('Sync', () => {\n    tests.forEach((test) => {\n      let state = getState();\n      it(test.code.substring(0, 100), async () => {\n        await run(test, state, false);\n      });\n    });\n  });\n\n  describe('Async', () => {\n    tests.forEach((test) => {\n      let state = getState();\n      it(test.code.substring(0, 100), async () => {\n        await run(test, state, true);\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/eval/testCases/defaults.data.ts",
    "content": "'use strict';\nimport { TestCase } from './types.js';\n\nexport const tests: TestCase[] = [\n  // Basic single default\n  {\n    code: 'function fn(a = 1) { return a }; return fn()',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Defaults',\n  },\n  {\n    code: 'function fn(a = 1) { return a }; return fn(5)',\n    evalExpect: 5,\n    safeExpect: 5,\n    category: 'Defaults',\n  },\n  // Multiple defaults\n  {\n    code: 'function fn(a = 1, b = 2) { return a + b }; return fn()',\n    evalExpect: 3,\n    safeExpect: 3,\n    category: 'Defaults',\n  },\n  {\n    code: 'function fn(a = 1, b = 2) { return a + b }; return fn(10)',\n    evalExpect: 12,\n    safeExpect: 12,\n    category: 'Defaults',\n  },\n  {\n    code: 'function fn(a = 1, b = 2) { return a + b }; return fn(10, 20)',\n    evalExpect: 30,\n    safeExpect: 30,\n    category: 'Defaults',\n  },\n  // Mixed required and default\n  {\n    code: 'function fn(a, b = 2) { return a + b }; return fn(10)',\n    evalExpect: 12,\n    safeExpect: 12,\n    category: 'Defaults',\n  },\n  {\n    code: 'function fn(a, b = 2) { return a + b }; return fn(10, 20)',\n    evalExpect: 30,\n    safeExpect: 30,\n    category: 'Defaults',\n  },\n  // Default with expression\n  {\n    code: 'function fn(a = 1 + 2) { return a }; return fn()',\n    evalExpect: 3,\n    safeExpect: 3,\n    category: 'Defaults',\n  },\n  // Default referencing earlier param\n  {\n    code: 'function fn(a = 1, b = a + 1) { return b }; return fn()',\n    evalExpect: 2,\n    safeExpect: 2,\n    category: 'Defaults',\n  },\n  // Arrow function with defaults\n  {\n    code: 'const fn = (a = 10) => a; return fn()',\n    evalExpect: 10,\n    safeExpect: 10,\n    category: 'Defaults',\n  },\n  {\n    code: 'const fn = (a = 10) => a; return fn(5)',\n    evalExpect: 5,\n    safeExpect: 5,\n    category: 'Defaults',\n  },\n  {\n    code: 'const fn = (a = 1, b = 2) => a + b; return fn()',\n    evalExpect: 3,\n    safeExpect: 3,\n    category: 'Defaults',\n  },\n  // Default with null (only undefined triggers default)\n  {\n    code: 'function fn(a = 99) { return a }; return fn(null)',\n    evalExpect: null,\n    safeExpect: null,\n    category: 'Defaults',\n  },\n  // Default with rest params\n  {\n    code: 'function fn(a = 1, ...rest) { return a + rest.length }; return fn()',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Defaults',\n  },\n  {\n    code: 'function fn(a = 1, ...rest) { return a + rest.length }; return fn(10, 20, 30)',\n    evalExpect: 12,\n    safeExpect: 12,\n    category: 'Defaults',\n  },\n  // Default with string value\n  {\n    code: 'function fn(name = \"world\") { return \"hello \" + name }; return fn()',\n    evalExpect: 'hello world',\n    safeExpect: 'hello world',\n    category: 'Defaults',\n  },\n  {\n    code: 'function fn(name = \"world\") { return \"hello \" + name }; return fn(\"there\")',\n    evalExpect: 'hello there',\n    safeExpect: 'hello there',\n    category: 'Defaults',\n  },\n  // Default is a function call\n  {\n    code: 'function make() { return 42 }; function fn(a = make()) { return a }; return fn()',\n    evalExpect: 42,\n    safeExpect: 42,\n    category: 'Defaults',\n  },\n  // Default is only evaluated when needed\n  {\n    code: 'let calls = 0; function make() { calls++; return 1 }; function fn(a = make()) { return a }; fn(99); return calls',\n    evalExpect: 0,\n    safeExpect: 0,\n    category: 'Defaults',\n  },\n  // Default evaluated each call\n  {\n    code: 'let n = 0; function next() { return ++n }; function fn(a = next()) { return a }; fn(); fn(); return fn()',\n    evalExpect: 3,\n    safeExpect: 3,\n    category: 'Defaults',\n  },\n  // Default references earlier param (already tested b = a+1, now more complex)\n  {\n    code: 'function fn(a = 2, b = a * 3, c = a + b) { return c }; return fn()',\n    evalExpect: 8,\n    safeExpect: 8,\n    category: 'Defaults',\n  },\n  // Default with destructuring param\n  {\n    code: 'function fn({a = 1, b = 2} = {}) { return a + b }; return fn()',\n    evalExpect: 3,\n    safeExpect: 3,\n    category: 'Defaults',\n  },\n  {\n    code: 'function fn({a = 1, b = 2} = {}) { return a + b }; return fn({a: 10})',\n    evalExpect: 12,\n    safeExpect: 12,\n    category: 'Defaults',\n  },\n  {\n    code: 'function fn([a = 10, b = 20] = []) { return a + b }; return fn()',\n    evalExpect: 30,\n    safeExpect: 30,\n    category: 'Defaults',\n  },\n  {\n    code: 'function fn([a = 10, b = 20] = []) { return a + b }; return fn([1])',\n    evalExpect: 21,\n    safeExpect: 21,\n    category: 'Defaults',\n  },\n  // Default in method\n  {\n    code: 'const obj = { fn(a = 7) { return a } }; return obj.fn()',\n    evalExpect: 7,\n    safeExpect: 7,\n    category: 'Defaults',\n  },\n  // Default in recursive function\n  {\n    code: 'function sum(n, acc = 0) { if (n <= 0) return acc; return sum(n - 1, acc + n) }; return sum(5)',\n    evalExpect: 15,\n    safeExpect: 15,\n    category: 'Defaults',\n  },\n  // Passing undefined explicitly triggers default\n  {\n    code: 'function fn(a = 5, b = 6) { return a + b }; return fn(undefined, 10)',\n    evalExpect: 15,\n    safeExpect: 15,\n    category: 'Defaults',\n  },\n  // Default with array literal\n  {\n    code: 'function fn(a = [1, 2, 3]) { return a.length }; return fn()',\n    evalExpect: 3,\n    safeExpect: 3,\n    category: 'Defaults',\n  },\n  // Default with object literal\n  {\n    code: 'function fn(opts = { x: 10, y: 20 }) { return opts.x + opts.y }; return fn()',\n    evalExpect: 30,\n    safeExpect: 30,\n    category: 'Defaults',\n  },\n  // Async function with defaults\n  {\n    code: 'async function fn(a = 1, b = 2) { return a + b }; return fn()',\n    evalExpect: 3,\n    safeExpect: 3,\n    category: 'Defaults',\n  },\n  // Arrow with destructuring default\n  {\n    code: 'const fn = ({x = 1, y = 2} = {}) => x + y; return fn()',\n    evalExpect: 3,\n    safeExpect: 3,\n    category: 'Defaults',\n  },\n  {\n    code: 'const fn = ({x = 1, y = 2} = {}) => x + y; return fn({x: 5})',\n    evalExpect: 7,\n    safeExpect: 7,\n    category: 'Defaults',\n  },\n];\n"
  },
  {
    "path": "test/eval/testCases/defaults.spec.ts",
    "content": "'use strict';\nimport { run, getState } from './test-utils.js';\nimport { tests } from './defaults.data.js';\n\ndescribe('Parameter Default Values Tests', () => {\n  describe('Sync', () => {\n    tests.forEach((test) => {\n      let state = getState();\n      it(test.code.substring(0, 100), async () => {\n        await run(test, state, false);\n      });\n    });\n  });\n\n  describe('Async', () => {\n    tests.forEach((test) => {\n      let state = getState();\n      it(test.code.substring(0, 100), async () => {\n        await run(test, state, true);\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/eval/testCases/destructuring.data.ts",
    "content": "'use strict';\nimport { TestCase } from './types.js';\n\nexport const tests: TestCase[] = [\n  // Array destructuring - basic\n  {\n    code: 'const [a, b] = [1, 2]; return a',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Destructuring',\n  },\n  {\n    code: 'const [a, b] = [1, 2]; return b',\n    evalExpect: 2,\n    safeExpect: 2,\n    category: 'Destructuring',\n  },\n  {\n    code: 'const [a, b, c] = [1, 2, 3]; return a + b + c',\n    evalExpect: 6,\n    safeExpect: 6,\n    category: 'Destructuring',\n  },\n  // Array destructuring - skipping elements\n  {\n    code: 'const [, b] = [1, 2]; return b',\n    evalExpect: 2,\n    safeExpect: 2,\n    category: 'Destructuring',\n  },\n  {\n    code: 'const [a, , c] = [1, 2, 3]; return a + c',\n    evalExpect: 4,\n    safeExpect: 4,\n    category: 'Destructuring',\n  },\n  // Array destructuring - defaults\n  {\n    code: 'const [a = 10] = []; return a',\n    evalExpect: 10,\n    safeExpect: 10,\n    category: 'Destructuring',\n  },\n  {\n    code: 'const [a = 10] = [5]; return a',\n    evalExpect: 5,\n    safeExpect: 5,\n    category: 'Destructuring',\n  },\n  {\n    code: 'const [a = 1, b = 2] = [10]; return a + b',\n    evalExpect: 12,\n    safeExpect: 12,\n    category: 'Destructuring',\n  },\n  // Array destructuring - rest\n  {\n    code: 'const [a, ...rest] = [1, 2, 3]; return rest',\n    evalExpect: [2, 3],\n    safeExpect: [2, 3],\n    category: 'Destructuring',\n  },\n  {\n    code: 'const [a, b, ...rest] = [1, 2, 3, 4, 5]; return rest.length',\n    evalExpect: 3,\n    safeExpect: 3,\n    category: 'Destructuring',\n  },\n  {\n    code: 'const [...all] = [1, 2, 3]; return all',\n    evalExpect: [1, 2, 3],\n    safeExpect: [1, 2, 3],\n    category: 'Destructuring',\n  },\n  {\n    code: 'const res = []; const a = [1,2,3]; for (let i = 0; i < a.length; i++) { const [...[a]] = a; res.push(a); } return res',\n    evalExpect: [1, 1, 1],\n    safeExpect: [1, 1, 1],\n    category: 'Destructuring',\n  },\n  // Object destructuring - basic\n  {\n    code: 'const {a} = {a: 1}; return a',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Destructuring',\n  },\n  {\n    code: 'const {a, b} = {a: 1, b: 2}; return a + b',\n    evalExpect: 3,\n    safeExpect: 3,\n    category: 'Destructuring',\n  },\n  // Object destructuring - renaming (custom variable names)\n  {\n    code: 'const {a: myA} = {a: 1}; return myA',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Destructuring',\n  },\n  {\n    code: 'const {a: x, b: y} = {a: 10, b: 20}; return x + y',\n    evalExpect: 30,\n    safeExpect: 30,\n    category: 'Destructuring',\n  },\n  {\n    code: 'const {firstName: first, lastName: last} = {firstName: \"John\", lastName: \"Doe\"}; return first + \" \" + last',\n    evalExpect: 'John Doe',\n    safeExpect: 'John Doe',\n    category: 'Destructuring',\n  },\n  // Object destructuring - defaults\n  {\n    code: 'const {a = 5} = {}; return a',\n    evalExpect: 5,\n    safeExpect: 5,\n    category: 'Destructuring',\n  },\n  {\n    code: 'const {a = 5} = {a: 10}; return a',\n    evalExpect: 10,\n    safeExpect: 10,\n    category: 'Destructuring',\n  },\n  {\n    code: 'const {a = 1, b = 2} = {a: 10}; return a + b',\n    evalExpect: 12,\n    safeExpect: 12,\n    category: 'Destructuring',\n  },\n  // Object destructuring - rename with default\n  {\n    code: 'const {a: x = 99} = {}; return x',\n    evalExpect: 99,\n    safeExpect: 99,\n    category: 'Destructuring',\n  },\n  {\n    code: 'const {a: x = 99} = {a: 1}; return x',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Destructuring',\n  },\n  // Object destructuring - rest\n  {\n    code: 'const {a, ...rest} = {a: 1, b: 2, c: 3}; return rest.b + rest.c',\n    evalExpect: 5,\n    safeExpect: 5,\n    category: 'Destructuring',\n  },\n  {\n    code: 'const {a, b, ...rest} = {a: 1, b: 2, c: 3, d: 4}; return Object.keys(rest).length',\n    evalExpect: 2,\n    safeExpect: 2,\n    category: 'Destructuring',\n  },\n  {\n    code: 'const {...all} = {a: 1, b: 2}; return all.a + all.b',\n    evalExpect: 3,\n    safeExpect: 3,\n    category: 'Destructuring',\n  },\n  // Nested destructuring - arrays\n  {\n    code: 'const [a, [b, c]] = [1, [2, 3]]; return a + b + c',\n    evalExpect: 6,\n    safeExpect: 6,\n    category: 'Destructuring',\n  },\n  {\n    code: 'const [[a]] = [[1]]; return a',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Destructuring',\n  },\n  // Nested destructuring - objects\n  {\n    code: 'const {a: {b}} = {a: {b: 42}}; return b',\n    evalExpect: 42,\n    safeExpect: 42,\n    category: 'Destructuring',\n  },\n  {\n    code: 'const {a: {b, c}} = {a: {b: 1, c: 2}}; return b + c',\n    evalExpect: 3,\n    safeExpect: 3,\n    category: 'Destructuring',\n  },\n  // Mixed nested\n  {\n    code: 'const {a: [x, y]} = {a: [1, 2]}; return x + y',\n    evalExpect: 3,\n    safeExpect: 3,\n    category: 'Destructuring',\n  },\n  {\n    code: 'const [a, {b, c}] = [1, {b: 2, c: 3}]; return a + b + c',\n    evalExpect: 6,\n    safeExpect: 6,\n    category: 'Destructuring',\n  },\n  // Nested with defaults\n  {\n    code: 'const {a: {b = 10} = {}} = {}; return b',\n    evalExpect: 10,\n    safeExpect: 10,\n    category: 'Destructuring',\n  },\n  {\n    code: 'const [a = 1, [b = 2] = []] = []; return a + b',\n    evalExpect: 3,\n    safeExpect: 3,\n    category: 'Destructuring',\n  },\n  // var and let\n  {\n    code: 'var [p, q] = [1, 2]; return p + q',\n    evalExpect: 3,\n    safeExpect: 3,\n    category: 'Destructuring',\n  },\n  {\n    code: 'let {x, y} = {x: 10, y: 20}; return x + y',\n    evalExpect: 30,\n    safeExpect: 30,\n    category: 'Destructuring',\n  },\n  // Computed property names in destructuring\n  {\n    code: 'const key = \"a\"; const {[key]: val} = {a: 42}; return val',\n    evalExpect: 42,\n    safeExpect: 42,\n    category: 'Destructuring',\n  },\n  {\n    code: 'const {[\"a\" + \"b\"]: v} = {ab: 42}; return v',\n    evalExpect: 42,\n    safeExpect: 42,\n    category: 'Destructuring',\n  },\n  {\n    code: 'const k = \"a\"; const {[k]: v = 10} = {}; return v',\n    evalExpect: 10,\n    safeExpect: 10,\n    category: 'Destructuring',\n  },\n  {\n    code: 'const a = \"x\", b = \"y\"; const {[a]: v1, [b]: v2} = {x: 1, y: 2}; return v1 + v2',\n    evalExpect: 3,\n    safeExpect: 3,\n    category: 'Destructuring',\n  },\n  {\n    code: 'function f({[\"a\"]: v}) { return v }; return f({a: 42})',\n    evalExpect: 42,\n    safeExpect: 42,\n    category: 'Destructuring',\n  },\n  // Destructuring in function parameters - array\n  {\n    code: 'function fn([a, b]) { return a + b }; return fn([1, 2])',\n    evalExpect: 3,\n    safeExpect: 3,\n    category: 'Destructuring',\n  },\n  {\n    code: 'const fn = ([a, b]) => a + b; return fn([10, 20])',\n    evalExpect: 30,\n    safeExpect: 30,\n    category: 'Destructuring',\n  },\n  // Destructuring in function parameters - object\n  {\n    code: 'function fn({a, b}) { return a + b }; return fn({a: 1, b: 2})',\n    evalExpect: 3,\n    safeExpect: 3,\n    category: 'Destructuring',\n  },\n  {\n    code: 'const fn = ({a, b}) => a + b; return fn({a: 5, b: 6})',\n    evalExpect: 11,\n    safeExpect: 11,\n    category: 'Destructuring',\n  },\n  // Destructuring in function parameters - with rename\n  {\n    code: 'function fn({a: x, b: y}) { return x + y }; return fn({a: 3, b: 4})',\n    evalExpect: 7,\n    safeExpect: 7,\n    category: 'Destructuring',\n  },\n  // Destructuring in function parameters - with defaults\n  {\n    code: 'function fn({a = 10, b = 20} = {}) { return a + b }; return fn({})',\n    evalExpect: 30,\n    safeExpect: 30,\n    category: 'Destructuring',\n  },\n  {\n    code: 'function fn({a = 10, b = 20} = {}) { return a + b }; return fn({a: 1})',\n    evalExpect: 21,\n    safeExpect: 21,\n    category: 'Destructuring',\n  },\n  // Destructuring in function parameters - mixed\n  {\n    code: 'function fn({a}, [b, c]) { return a + b + c }; return fn({a: 1}, [2, 3])',\n    evalExpect: 6,\n    safeExpect: 6,\n    category: 'Destructuring',\n  },\n  // Destructuring in function parameters - rest\n  {\n    code: 'function fn([first, ...rest]) { return rest.length }; return fn([1, 2, 3, 4])',\n    evalExpect: 3,\n    safeExpect: 3,\n    category: 'Destructuring',\n  },\n  // RHS evaluated once\n  {\n    code: 'let count = 0; function makeArr() { count++; return [1, 2]; } const [a, b] = makeArr(); return count',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Destructuring',\n  },\n  // In loops\n  {\n    code: 'let sum = 0; for (const [a, b] of [[1, 2], [3, 4]]) { sum += a + b; } return sum',\n    evalExpect: 10,\n    safeExpect: 10,\n    category: 'Destructuring',\n  },\n  {\n    code: 'const pairs = []; for (const [first, second] in {ab: 1, cd: 2}) { pairs.push(first + second); } return pairs',\n    evalExpect: ['ab', 'cd'],\n    safeExpect: ['ab', 'cd'],\n    category: 'Destructuring',\n  },\n  {\n    code: 'let total = 0; for (const {a, b: {c}} of [{a: 1, b: {c: 2}}, {a: 3, b: {c: 4}}]) { total += a + c; } return total',\n    evalExpect: 10,\n    safeExpect: 10,\n    category: 'Destructuring',\n  },\n  {\n    code: 'const [value];',\n    evalExpect: 'error',\n    safeExpect: '/Destructuring declaration requires an initializer/',\n    category: 'Destructuring',\n  },\n  // Shorthand properties with destructuring\n  {\n    code: 'const obj = {x: 1, y: 2}; const {x, y} = obj; return x + y',\n    evalExpect: 3,\n    safeExpect: 3,\n    category: 'Destructuring',\n  },\n];\n"
  },
  {
    "path": "test/eval/testCases/destructuring.spec.ts",
    "content": "'use strict';\nimport { run, getState } from './test-utils.js';\nimport { tests } from './destructuring.data.js';\n\ndescribe('Destructuring Tests', () => {\n  describe('Sync', () => {\n    tests.forEach((test) => {\n      let state = getState();\n      it(test.code.substring(0, 100), async () => {\n        await run(test, state, false);\n      });\n    });\n  });\n\n  describe('Async', () => {\n    tests.forEach((test) => {\n      let state = getState();\n      it(test.code.substring(0, 100), async () => {\n        await run(test, state, true);\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/eval/testCases/error-handling.data.ts",
    "content": "'use strict';\nimport { TestCase } from './types.js';\n\nexport const tests: TestCase[] = [\n  {\n    code: \"throw new Error('test')\",\n    evalExpect: 'error',\n    safeExpect: '/test/',\n    category: 'Error Handling',\n  },\n  {\n    code: 'throw undefined',\n    evalExpect: 'error',\n    safeExpect: 'error',\n    category: 'Error Handling',\n  },\n  {\n    code: 'try {a.x.a} catch {return 1}; return 2',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Error Handling',\n  },\n  {\n    code: \"try { throw new Error('test'); } catch { return 'caught'; }\",\n    evalExpect: 'caught',\n    safeExpect: 'caught',\n    category: 'Error Handling',\n  },\n  // Finally block tests\n  {\n    code: 'let x = 0; try { x = 1; } finally { x = 2; } return x;',\n    evalExpect: 2,\n    safeExpect: 2,\n    category: 'Error Handling',\n  },\n  {\n    code: 'let x = 0; try { throw new Error(); } catch(e) { x = 1; } finally { x = 2; } return x;',\n    evalExpect: 2,\n    safeExpect: 2,\n    category: 'Error Handling',\n  },\n  {\n    code: 'let x = 0; try { x = 1; } finally { x += 10; } return x;',\n    evalExpect: 11,\n    safeExpect: 11,\n    category: 'Error Handling',\n  },\n  {\n    code: 'let x = 0; try { throw new Error(\"test\"); } catch(e) { x = 5; } finally { x *= 2; } return x;',\n    evalExpect: 10,\n    safeExpect: 10,\n    category: 'Error Handling',\n  },\n  {\n    code: 'try { return 1; } finally { return 2; }',\n    evalExpect: 2,\n    safeExpect: 2,\n    category: 'Error Handling',\n  },\n  {\n    code: 'let x = 0; try { return 10; } finally { x = 5; } return x;',\n    evalExpect: 10,\n    safeExpect: 10,\n    category: 'Error Handling',\n  },\n  {\n    code: 'try { throw new Error(\"first\"); } finally { throw new Error(\"second\"); }',\n    evalExpect: 'error',\n    safeExpect: '/second/',\n    category: 'Error Handling',\n  },\n  {\n    code: 'try { throw new Error(\"test\"); } catch(e) { return e.message; } finally { let y = 1; }',\n    evalExpect: 'test',\n    safeExpect: 'test',\n    category: 'Error Handling',\n  },\n  {\n    code: 'let result = \"\"; try { result += \"a\"; } catch(e) { result += \"b\"; } finally { result += \"c\"; } return result;',\n    evalExpect: 'ac',\n    safeExpect: 'ac',\n    category: 'Error Handling',\n  },\n  {\n    code: 'let result = \"\"; try { throw new Error(); } catch(e) { result += \"b\"; } finally { result += \"c\"; } return result;',\n    evalExpect: 'bc',\n    safeExpect: 'bc',\n    category: 'Error Handling',\n  },\n  {\n    code: 'function testFn1() { try { return \"try\"; } finally { } } return testFn1();',\n    evalExpect: 'try',\n    safeExpect: 'try',\n    category: 'Error Handling',\n  },\n  {\n    code: 'function testFn2() { try { throw new Error(\"err\"); } catch(e) { return \"catch\"; } finally { } } return testFn2();',\n    evalExpect: 'catch',\n    safeExpect: 'catch',\n    category: 'Error Handling',\n  },\n  {\n    code: 'let x = 0; try { } finally { x = 1; } return x;',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Error Handling',\n  },\n  {\n    code: 'missing = 1;',\n    evalExpect: 'error',\n    safeExpect: '/missing is not defined/',\n    category: 'Error Handling',\n  },\n  {\n    code: 'Math = 1;',\n    evalExpect: 'error',\n    safeExpect: \"/Cannot assign property 'Math' of a global object/\",\n    category: 'Error Handling',\n  },\n  {\n    code: 'const answer = 1; answer = 2;',\n    evalExpect: 'error',\n    safeExpect: '/Assignment to constant variable\\\\./',\n    category: 'Error Handling',\n  },\n  {\n    code: '1 2',\n    evalExpect: 'error',\n    safeExpect: '/Unexpected token after .*: 2/',\n    category: 'Error Handling',\n  },\n  {\n    code: 'const x = 0; ({ x } = { x: 1 });',\n    evalExpect: 'error',\n    safeExpect: '/Assignment to constant variable/',\n    category: 'Error Handling',\n  },\n  {\n    code: '({ missing } = { missing: 1 });',\n    evalExpect: 'error',\n    safeExpect: '/missing is not defined/',\n    category: 'Error Handling',\n  },\n  {\n    code: '{a: 1} = 1',\n    evalExpect: 'error',\n    safeExpect: '/Unexpected token/',\n    category: 'Error Handling',\n  },\n  {\n    code: 'missing: for (;;) { break nope; }',\n    evalExpect: 'error',\n    safeExpect: \"/Undefined label 'nope'/\",\n    category: 'Error Handling',\n  },\n  {\n    code: 'outer: { continue outer; }',\n    evalExpect: 'error',\n    safeExpect: '/Illegal continue statement/',\n    category: 'Error Handling',\n  },\n  {\n    code: 'outer: inner: { continue inner; }',\n    evalExpect: 'error',\n    safeExpect: '/Illegal continue statement/',\n    category: 'Error Handling',\n  },\n  {\n    code: 'outer: switch (1) { case 1: continue outer; }',\n    evalExpect: 'error',\n    safeExpect: '/Illegal continue statement/',\n    category: 'Error Handling',\n  },\n  {\n    code: 'outer: if (true) { continue outer; }',\n    evalExpect: 'error',\n    safeExpect: '/Illegal continue statement/',\n    category: 'Error Handling',\n  },\n  {\n    code: 'outer: try { continue outer; } finally {}',\n    evalExpect: 'error',\n    safeExpect: '/Illegal continue statement/',\n    category: 'Error Handling',\n  },\n  {\n    code: 'outer: { } break outer;',\n    evalExpect: 'error',\n    safeExpect: \"/Undefined label 'outer'/\",\n    category: 'Error Handling',\n  },\n  {\n    code: 'outer: for (;;) { try { break outer; } finally { throw new Error(\"stop\"); } }',\n    evalExpect: 'error',\n    safeExpect: '/stop/',\n    category: 'Error Handling',\n  },\n  {\n    code: 'nonExistentVar',\n    evalExpect: 'error',\n    safeExpect: '/nonExistentVar is not defined/',\n    category: 'Error Handling',\n  },\n];\n"
  },
  {
    "path": "test/eval/testCases/error-handling.spec.ts",
    "content": "'use strict';\nimport { run, getState } from './test-utils.js';\nimport { tests } from './error-handling.data.js';\n\ndescribe('Error Handling Tests', () => {\n  describe('Sync', () => {\n    tests.forEach((test) => {\n      let state = getState();\n      it(test.code.substring(0, 100), async () => {\n        await run(test, state, false);\n      });\n    });\n  });\n\n  describe('Async', () => {\n    tests.forEach((test) => {\n      let state = getState();\n      it(test.code.substring(0, 100), async () => {\n        await run(test, state, true);\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/eval/testCases/function-replacements.data.ts",
    "content": "'use strict';\nimport { TestCase } from './types.js';\n\nexport const tests: TestCase[] = [\n  {\n    code: \"const list = []; const push = list.push; push(1); push(2); return list.join(',')\",\n    evalExpect: '1,2',\n    safeExpect: '1,2',\n    category: 'Function Replacements',\n  },\n  {\n    code: 'const list = []; const push = list.push; return push.name',\n    evalExpect: 'push',\n    safeExpect: 'push',\n    category: 'Function Replacements',\n  },\n  {\n    code: \"const list = []; const other = []; const push = list.push; const rebound = push.bind(other); rebound(3); return [list.join(','), other.join(','), rebound.name].join('|')\",\n    evalExpect: '|3|bound push',\n    safeExpect: '|3|bound push',\n    category: 'Function Replacements',\n  },\n  {\n    code: \"const list = []; const other = []; const push = list.push; const rebound = push.bind(other, 3, 4); rebound(); return [list.join(','), other.join(','), rebound.name, rebound.length].join('|')\",\n    evalExpect: '|3,4|bound push|0',\n    safeExpect: '|3,4|bound push|0',\n    category: 'Function Replacements',\n  },\n  {\n    code: \"const list = []; const other = []; const third = []; const push = list.push; const rebound = push.bind(other, 3); const reboundAgain = rebound.bind(third, 4); reboundAgain(5); return [list.join(','), other.join(','), third.join(','), reboundAgain.name].join('|')\",\n    evalExpect: '|3,4,5||bound bound push',\n    safeExpect: '|3,4,5||bound bound push',\n    category: 'Function Replacements',\n  },\n];\n"
  },
  {
    "path": "test/eval/testCases/function-replacements.spec.ts",
    "content": "'use strict';\nimport { run, getState } from './test-utils.js';\nimport { tests } from './function-replacements.data.js';\n\ndescribe('Function Replacements Tests', () => {\n  describe('Sync', () => {\n    tests.forEach((test) => {\n      const state = getState();\n      it(test.code.substring(0, 100), async () => {\n        await run(test, state, false);\n      });\n    });\n  });\n\n  describe('Async', () => {\n    tests.forEach((test) => {\n      const state = getState();\n      it(test.code.substring(0, 100), async () => {\n        await run(test, state, true);\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/eval/testCases/functions.data.ts",
    "content": "'use strict';\nimport { TestCase } from './types.js';\n\nexport const tests: TestCase[] = [\n  {\n    code: '((a) => {return a + 1})(1)',\n    evalExpect: 2,\n    safeExpect: 2,\n    category: 'Functions',\n  },\n  {\n    code: \"(() => '1' + (() => '22')())()\",\n    evalExpect: '122',\n    safeExpect: '122',\n    category: 'Functions',\n  },\n  {\n    code: '(a => a + 1)(1)',\n    evalExpect: 2,\n    safeExpect: 2,\n    category: 'Functions',\n  },\n  {\n    code: 'function f(a) { return a + 1 } return f(2);',\n    evalExpect: 3,\n    safeExpect: 3,\n    category: 'Functions',\n  },\n  {\n    code: '(function () { return 1 })()',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Functions',\n  },\n  {\n    code: 'let list = [0, 1]; return list.sort((a, b) => (a < b) ? 1 : -1)',\n    evalExpect: [1, 0],\n    safeExpect: [1, 0],\n    category: 'Functions',\n  },\n  {\n    code: 'let y = {a: 1, b(x) {return this.a + x}}; return y.b(2)',\n    evalExpect: 3,\n    safeExpect: 3,\n    category: 'Functions',\n  },\n  {\n    code: \"let y = {a: '2', b() {return this.a = '1'}}; y.b(); return y.a\",\n    evalExpect: '1',\n    safeExpect: '1',\n    category: 'Functions',\n  },\n  {\n    code: '[0,1].filter((...args) => args[1])',\n    evalExpect: [1],\n    safeExpect: [1],\n    category: 'Functions',\n  },\n  {\n    code: 'Math.pow(...[2, 2])',\n    evalExpect: 4,\n    safeExpect: 4,\n    category: 'Functions',\n  },\n  {\n    code: 'return f(); function f() { return 1; }',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Functions',\n  },\n  {\n    code: 'return add(2, 3); function add(a, b) { return a + b; }',\n    evalExpect: 5,\n    safeExpect: 5,\n    category: 'Functions',\n  },\n  {\n    code: 'let x = f(); function f() { return 42; } return x;',\n    evalExpect: 42,\n    safeExpect: 42,\n    category: 'Functions',\n  },\n  {\n    code: 'const fn = ({ a: for }) => for; return fn({ a: 1 });',\n    evalExpect: 'error',\n    safeExpect: \"/Unexpected token 'for'/\",\n    category: 'Functions',\n  },\n  {\n    code: 'function fn(for) { return for; } return fn(1);',\n    evalExpect: 'error',\n    safeExpect: \"/Unexpected token 'for'/\",\n    category: 'Functions',\n  },\n  // Trailing commas in function parameters\n  {\n    code: 'function fn(a, b,) { return a + b } return fn(1, 2);',\n    evalExpect: 3,\n    safeExpect: 3,\n    category: 'Functions',\n  },\n  // Trailing commas in function call\n  {\n    code: 'function fn(a, b) { return a + b } return fn(1, 2,);',\n    evalExpect: 3,\n    safeExpect: 3,\n    category: 'Functions',\n  },\n  // Trailing commas in arrow function parameters\n  {\n    code: '((a, b,) => a + b)(1, 2)',\n    evalExpect: 3,\n    safeExpect: 3,\n    category: 'Functions',\n  },\n];\n"
  },
  {
    "path": "test/eval/testCases/functions.spec.ts",
    "content": "'use strict';\nimport { run, getState } from './test-utils.js';\nimport { tests } from './functions.data.js';\n\ndescribe('Functions Tests', () => {\n  describe('Sync', () => {\n    tests.forEach((test) => {\n      let state = getState();\n      it(test.code.substring(0, 100), async () => {\n        await run(test, state, false);\n      });\n    });\n  });\n\n  describe('Async', () => {\n    tests.forEach((test) => {\n      let state = getState();\n      it(test.code.substring(0, 100), async () => {\n        await run(test, state, true);\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/eval/testCases/generators.data.ts",
    "content": "'use strict';\nimport { TestCase } from './types.js';\n\nexport const tests: TestCase[] = [\n  // Basic generator\n  {\n    code: 'function* gen() { yield 1; yield 2; yield 3; } const g = gen(); return [g.next().value, g.next().value, g.next().value]',\n    evalExpect: [1, 2, 3],\n    safeExpect: [1, 2, 3],\n    category: 'Generators',\n  },\n  // Generator done state\n  {\n    code: 'function* gen() { yield 1; } const g = gen(); g.next(); return g.next().done',\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Generators',\n  },\n  // Generator return value\n  {\n    code: 'function* gen() { yield 1; return 42; } const g = gen(); g.next(); return g.next().value',\n    evalExpect: 42,\n    safeExpect: 42,\n    category: 'Generators',\n  },\n  // for-of with generator\n  {\n    code: 'function* gen() { yield 1; yield 2; yield 3; } const arr = []; for (const x of gen()) { arr.push(x); } return arr',\n    evalExpect: [1, 2, 3],\n    safeExpect: [1, 2, 3],\n    category: 'Generators',\n  },\n  // Spread with generator\n  {\n    code: 'function* gen() { yield 1; yield 2; yield 3; } return [...gen()]',\n    evalExpect: [1, 2, 3],\n    safeExpect: [1, 2, 3],\n    category: 'Generators',\n  },\n  // yield* delegation\n  {\n    code: 'function* inner() { yield 2; yield 3; } function* outer() { yield 1; yield* inner(); yield 4; } return [...outer()]',\n    evalExpect: [1, 2, 3, 4],\n    safeExpect: [1, 2, 3, 4],\n    category: 'Generators',\n  },\n  // yield* with array\n  {\n    code: 'function* gen() { yield 1; yield* [2, 3]; yield 4; } return [...gen()]',\n    evalExpect: [1, 2, 3, 4],\n    safeExpect: [1, 2, 3, 4],\n    category: 'Generators',\n  },\n  // yield* invalid target throws\n  {\n    code: 'function* gen() { yield* 1; } return [...gen()]',\n    evalExpect: 'error',\n    safeExpect: '/not iterable/',\n    category: 'Generators',\n  },\n  // Inline generator function expression\n  {\n    code: 'const gen = function* () { yield 1; yield 2; }; return [...gen()]',\n    evalExpect: [1, 2],\n    safeExpect: [1, 2],\n    category: 'Generators',\n  },\n  // Anonymous generator IIFE\n  {\n    code: 'return (function* () { yield 1; yield 2; })().next().value',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Generators',\n  },\n  // Generator with arguments\n  {\n    code: 'function* range(start, end) { for (let i = start; i < end; i++) yield i; } return [...range(0, 4)]',\n    evalExpect: [0, 1, 2, 3],\n    safeExpect: [0, 1, 2, 3],\n    category: 'Generators',\n  },\n  // Generator with return in middle\n  {\n    code: 'function* gen() { yield 1; return; yield 2; } return [...gen()]',\n    evalExpect: [1],\n    safeExpect: [1],\n    category: 'Generators',\n  },\n  // yield outside generator throws\n  {\n    code: 'function f() { yield 1; } f()',\n    evalExpect: 'error',\n    safeExpect: 'error',\n    category: 'Generators',\n  },\n  // yield inside nested regular function inside generator throws\n  {\n    code: 'function* gen() { function inner() { yield 1; } inner(); } [...gen()]',\n    evalExpect: 'error',\n    safeExpect: 'error',\n    category: 'Generators',\n  },\n  // yield inside arrow function inside generator throws\n  {\n    code: 'function* gen() { const f = () => { yield 1; }; f(); } [...gen()]',\n    evalExpect: 'error',\n    safeExpect: 'error',\n    category: 'Generators',\n  },\n  // yield inside method of object literal inside generator throws\n  {\n    code: 'function* gen() { const obj = { m() { yield 1; } }; obj.m(); } [...gen()]',\n    evalExpect: 'error',\n    safeExpect: 'error',\n    category: 'Generators',\n  },\n  // Generator with conditional yields\n  {\n    code: 'function* gen(n) { if (n > 0) { yield 1; yield 2; } else { yield -1; } } return [...gen(1)]',\n    evalExpect: [1, 2],\n    safeExpect: [1, 2],\n    category: 'Generators',\n  },\n  {\n    code: 'function* gen(n) { if (n > 0) { yield 1; yield 2; } else { yield -1; } } return [...gen(-1)]',\n    evalExpect: [-1],\n    safeExpect: [-1],\n    category: 'Generators',\n  },\n  // Generator with if and no else branch\n  {\n    code: 'function* gen(flag) { if (flag) { yield 1; } } return [...gen(false)]',\n    evalExpect: [],\n    safeExpect: [],\n    category: 'Generators',\n  },\n  // Generator with if and no else branch\n  {\n    code: 'function* gen() { if (false) { yield 1; } yield 2; } return [...gen()]',\n    evalExpect: [2],\n    safeExpect: [2],\n    category: 'Generators',\n  },\n  // Generator with try/finally\n  {\n    code: 'const log = []; function* gen() { try { yield 1; yield 2; } finally { log.push(\"done\"); } }; [...gen()]; return log',\n    evalExpect: ['done'],\n    safeExpect: ['done'],\n    category: 'Generators',\n  },\n  // Generator with try/catch (no throw, catch block not entered)\n  {\n    code: 'const log = []; function* gen() { try { yield 1; } catch(e) { log.push(\"caught\"); } yield 2; }; [...gen()]; return log',\n    evalExpect: [],\n    safeExpect: [],\n    category: 'Generators',\n  },\n  // Generator with destructuring in for-of\n  {\n    code: 'function* pairs() { yield [1, \"a\"]; yield [2, \"b\"]; yield [3, \"c\"]; } const arr = []; for (const [n, s] of pairs()) { arr.push(s + n); } return arr',\n    evalExpect: ['a1', 'b2', 'c3'],\n    safeExpect: ['a1', 'b2', 'c3'],\n    category: 'Generators',\n  },\n  // Multiple yield* delegations in sequence\n  {\n    code: 'function* genA() { yield 1; yield 2; } function* genB() { yield 3; yield 4; } function* genC() { yield* genA(); yield* genB(); yield 5; } return [...genC()]',\n    evalExpect: [1, 2, 3, 4, 5],\n    safeExpect: [1, 2, 3, 4, 5],\n    category: 'Generators',\n  },\n  // Nested yield* delegation\n  {\n    code: 'function* genX() { yield 1; } function* genY() { yield* genX(); yield 2; } function* genZ() { yield* genY(); yield 3; } return [...genZ()]',\n    evalExpect: [1, 2, 3],\n    safeExpect: [1, 2, 3],\n    category: 'Generators',\n  },\n  // Generator with while loop\n  {\n    code: 'function* countdown(n) { while (n > 0) { yield n; n = n - 1; } } return [...countdown(3)]',\n    evalExpect: [3, 2, 1],\n    safeExpect: [3, 2, 1],\n    category: 'Generators',\n  },\n  // Generator return() ends iteration\n  {\n    code: 'function* gen() { yield 1; yield 2; yield 3; } const g = gen(); g.next(); const r = g.return(42); return [r.value, r.done, g.next().done]',\n    evalExpect: [42, true, true],\n    safeExpect: [42, true, true],\n    category: 'Generators',\n  },\n  // Generator throw() propagates error\n  {\n    code: 'function* gen() { yield 1; yield 2; } const g = gen(); try { g.throw(new Error(\"boom\")); } catch(e) { return e.message }',\n    evalExpect: 'boom',\n    safeExpect: 'boom',\n    category: 'Generators',\n  },\n  // Generator throw() after partial consumption\n  {\n    code: 'function* gen() { yield 1; yield 2; } const g = gen(); g.next(); try { g.throw(new Error(\"x\")); } catch(e) { return e.message }',\n    evalExpect: 'x',\n    safeExpect: 'x',\n    category: 'Generators',\n  },\n  // next(value) injection — yield as expression\n  {\n    code: 'function* gen() { const x = yield 1; const y = yield 2; return x + y; } const g = gen(); g.next(); g.next(10); return g.next(20).value',\n    evalExpect: 30,\n    safeExpect: 30,\n    category: 'Generators',\n  },\n  // yield expression returns undefined when no value injected\n  {\n    code: 'function* gen() { const x = yield 1; return x; } const g = gen(); g.next(); return g.next().value',\n    evalExpect: undefined,\n    safeExpect: undefined,\n    category: 'Generators',\n  },\n  // Generator return() runs finally block\n  {\n    code: 'const log = []; function* gen() { try { yield 1; } finally { log.push(\"F\"); } } const g = gen(); g.next(); g.return(99); return log',\n    evalExpect: ['F'],\n    safeExpect: ['F'],\n    category: 'Generators',\n  },\n  // Generator throw() triggers catch block\n  {\n    code: 'function* gen() { try { yield 1; } catch(e) { yield e.message; } } const g = gen(); g.next(); return g.throw(new Error(\"boom\")).value',\n    evalExpect: 'boom',\n    safeExpect: 'boom',\n    category: 'Generators',\n  },\n  // yield inside for loop — correct iteration count\n  {\n    code: 'function* range(n) { for (let i = 0; i < n; i++) yield i; } return [...range(4)]',\n    evalExpect: [0, 1, 2, 3],\n    safeExpect: [0, 1, 2, 3],\n    category: 'Generators',\n  },\n  // yield* delegation with next(value) forwarding\n  {\n    code: 'function* inner() { const x = yield 1; return x * 2; } function* outer() { const r = yield* inner(); yield r; } const g = outer(); g.next(); const r = g.next(5); return [r.value, r.done, g.next().done]',\n    evalExpect: [10, false, true],\n    safeExpect: [10, false, true],\n    category: 'Generators',\n  },\n  // yield* wraps iterators that do not expose Symbol.iterator themselves\n  {\n    code: 'const iterable = {}; iterable[Symbol.iterator] = function() { return { step: 0, next(value) { if (this.step++ === 0) return { value: 1, done: false }; return { value: value * 2, done: true }; } }; }; function* gen() { return yield* iterable; } const g = gen(); const a = g.next(); const b = g.next(7); return [[a.value, a.done], [b.value, b.done]]',\n    evalExpect: [\n      [1, false],\n      [14, true],\n    ],\n    safeExpect: [\n      [1, false],\n      [14, true],\n    ],\n    category: 'Generators',\n  },\n  // yield* forwards return() into wrapped iterators\n  {\n    code: 'const log = []; const iterable = {}; iterable[Symbol.iterator] = function() { return { next() { return { value: 1, done: false }; }, return(value) { log.push(value); return { value: value + 1, done: true }; } }; }; function* gen() { yield* iterable; } const g = gen(); g.next(); const r = g.return(9); return [r.value, r.done, log]',\n    evalExpect: [10, true, [9]],\n    safeExpect: [10, true, [9]],\n    category: 'Generators',\n  },\n  // yield* forwards throw() into wrapped iterators\n  {\n    code: 'const log = []; const iterable = {}; iterable[Symbol.iterator] = function() { return { next() { return { value: 1, done: false }; }, throw(err) { log.push(err.message); return { value: \"caught\", done: true }; } }; }; function* gen() { return yield* iterable; } const g = gen(); g.next(); const r = g.throw(new Error(\"boom\")); return [r.value, r.done, log]',\n    evalExpect: ['caught', true, ['boom']],\n    safeExpect: ['caught', true, ['boom']],\n    category: 'Generators',\n  },\n  // Generator loop with continue and break\n  {\n    code: 'function* gen() { for (let i = 0; i < 5; i++) { if (i === 1) continue; if (i === 3) break; yield i; } } return [...gen()]',\n    evalExpect: [0, 2],\n    safeExpect: [0, 2],\n    category: 'Generators',\n  },\n  // Generator labeled break exits outer loop\n  {\n    code: 'function* gen() { outer: for (let i = 0; i < 3; i++) { for (let j = 0; j < 3; j++) { if (i === 1 && j === 1) break outer; yield `${i}${j}`; } } } return [...gen()]',\n    evalExpect: ['00', '01', '02', '10'],\n    safeExpect: ['00', '01', '02', '10'],\n    category: 'Generators',\n  },\n  // Generator labeled statement handles break target\n  {\n    code: 'function* gen() { outer: { yield 1; break outer; yield 2; } yield 3; } return [...gen()]',\n    evalExpect: [1, 3],\n    safeExpect: [1, 3],\n    category: 'Generators',\n  },\n  // Generator catches internal throws\n  {\n    code: 'function* gen() { try { throw new Error(\"boom\"); } catch (e) { yield e.message; } } return [...gen()]',\n    evalExpect: ['boom'],\n    safeExpect: ['boom'],\n    category: 'Generators',\n  },\n  // Generator rethrows internal throws when no catch is present\n  {\n    code: 'function* gen() { try { throw new Error(\"boom\"); } finally { } } const g = gen(); try { g.next(); } catch (e) { return e.message; }',\n    evalExpect: 'boom',\n    safeExpect: 'boom',\n    category: 'Generators',\n  },\n  // Generator labeled statement falls through when not broken\n  {\n    code: 'function* gen() { outer: { yield 1; } return 2; } const g = gen(); return [g.next().value, g.next().value]',\n    evalExpect: [1, 2],\n    safeExpect: [1, 2],\n    category: 'Generators',\n  },\n  // for-await-of inside sync generator throws\n  {\n    code: 'function* gen() { for await (const x of [1, 2]) { yield x; } } return [...gen()]',\n    evalExpect: 'error',\n    safeExpect: 'error',\n    category: 'Generators',\n  },\n];\n\n// These tests require async compilation (use of await / for-await-of at top level).\nexport const asyncTests: TestCase[] = [\n  // Async generator\n  {\n    code: 'async function* gen() { yield 1; yield 2; } const g = gen(); return [(await g.next()).value, (await g.next()).value]',\n    evalExpect: [1, 2],\n    safeExpect: [1, 2],\n    category: 'Generators',\n  },\n  // Async generator with for-await-of\n  {\n    code: 'async function* gen() { yield 1; yield 2; yield 3; } const arr = []; for await (const x of gen()) { arr.push(x); } return arr',\n    evalExpect: [1, 2, 3],\n    safeExpect: [1, 2, 3],\n    category: 'Generators',\n  },\n  // Async inline generator function expression\n  {\n    code: 'const gen = async function* () { yield 1; yield 2; }; const g = gen(); return [(await g.next()).value, (await g.next()).value]',\n    evalExpect: [1, 2],\n    safeExpect: [1, 2],\n    category: 'Generators',\n  },\n  // Anonymous async generator IIFE\n  {\n    code: 'return (await (async function* () { yield 1; yield 2; })().next()).value',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Generators',\n  },\n  // Async generator next(value) injection\n  {\n    code: 'async function* gen() { const x = yield 1; const y = yield 2; return x + y; } const g = gen(); await g.next(); await g.next(10); return (await g.next(20)).value',\n    evalExpect: 30,\n    safeExpect: 30,\n    category: 'Generators',\n  },\n  // Async generator yield* delegation with next(value) forwarding\n  {\n    code: 'async function* inner() { const x = yield 1; return x * 2; } async function* outer() { const r = yield* inner(); yield r; } const g = outer(); await g.next(); const r = await g.next(5); return [r.value, r.done, (await g.next()).done]',\n    evalExpect: [10, false, true],\n    safeExpect: [10, false, true],\n    category: 'Generators',\n  },\n  // Async yield* invalid target throws\n  {\n    code: 'async function* gen() { yield* 1; } const g = gen(); await g.next()',\n    evalExpect: 'error',\n    safeExpect: '/not iterable/',\n    category: 'Generators',\n  },\n  // Async yield* can delegate to sync iterables\n  {\n    code: 'const iterable = {}; iterable[Symbol.iterator] = function() { return { step: 0, next(value) { if (this.step++ === 0) return { value: 1, done: false }; return { value: value * 2, done: true }; } }; }; async function* gen() { return yield* iterable; } const g = gen(); const a = await g.next(); const b = await g.next(7); return [[a.value, a.done], [b.value, b.done]]',\n    evalExpect: [\n      [1, false],\n      [14, true],\n    ],\n    safeExpect: [\n      [1, false],\n      [14, true],\n    ],\n    category: 'Generators',\n  },\n  // Async yield* forwards return() into sync iterators\n  {\n    code: 'const log = []; const iterable = {}; iterable[Symbol.iterator] = function() { return { next() { return { value: 1, done: false }; }, return(value) { log.push(value); return { value: value + 1, done: true }; } }; }; async function* gen() { yield* iterable; } const g = gen(); await g.next(); const r = await g.return(9); return [r.value, r.done, log]',\n    evalExpect: [10, true, [9]],\n    safeExpect: [10, true, [9]],\n    category: 'Generators',\n  },\n  // Async yield* forwards throw() into sync iterators\n  {\n    code: 'const log = []; const iterable = {}; iterable[Symbol.iterator] = function() { return { next() { return { value: 1, done: false }; }, throw(err) { log.push(err.message); return { value: \"caught\", done: true }; } }; }; async function* gen() { return yield* iterable; } const g = gen(); await g.next(); const r = await g.throw(new Error(\"boom\")); return [r.value, r.done, log]',\n    evalExpect: ['caught', true, ['boom']],\n    safeExpect: ['caught', true, ['boom']],\n    category: 'Generators',\n  },\n  // Async yield* return() works even when delegate lacks return()\n  {\n    code: 'const iterable = {}; iterable[Symbol.iterator] = function() { return { next() { return { value: 1, done: false }; } }; }; async function* gen() { yield* iterable; } const g = gen(); await g.next(); const r = await g.return(9); return [r.value, r.done]',\n    evalExpect: [9, true],\n    safeExpect: [9, true],\n    category: 'Generators',\n  },\n  // Async yield* throw() rethrows when delegate lacks throw()\n  {\n    code: 'const iterable = {}; iterable[Symbol.iterator] = function() { return { next() { return { value: 1, done: false }; } }; }; async function* gen() { try { return yield* iterable; } catch (e) { return e.message; } } const g = gen(); await g.next(); const r = await g.throw(new Error(\"boom\")); return [r.value, r.done]',\n    evalExpect: ['boom', true],\n    safeExpect: ['boom', true],\n    category: 'Generators',\n  },\n  // Async generator surfaces body errors after resuming\n  {\n    code: 'async function* gen() { yield 1; throw new Error(\"boom\"); } const g = gen(); await g.next(); try { await g.next(); } catch (e) { return e.message; }',\n    evalExpect: 'boom',\n    safeExpect: 'boom',\n    category: 'Generators',\n  },\n];\n"
  },
  {
    "path": "test/eval/testCases/generators.spec.ts",
    "content": "'use strict';\nimport { run, getState } from './test-utils.js';\nimport { tests, asyncTests } from './generators.data.js';\n\ndescribe('Generator Tests', () => {\n  describe('Sync', () => {\n    tests.forEach((test) => {\n      let state = getState();\n      it(test.code.substring(0, 100), async () => {\n        await run(test, state, false);\n      });\n    });\n  });\n\n  describe('Async', () => {\n    [...tests, ...asyncTests].forEach((test) => {\n      let state = getState();\n      it(test.code.substring(0, 100), async () => {\n        await run(test, state, true);\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/eval/testCases/index.ts",
    "content": "'use strict';\n\n// Import and re-export all test arrays\nexport { tests as arithmeticOperators } from './arithmetic-operators.data.js';\nexport { tests as assignmentOperators } from './assignment-operators.data.js';\nexport { tests as bitwiseOperators } from './bitwise-operators.data.js';\nexport { tests as comments } from './comments.data.js';\nexport { tests as comparisonOperators } from './comparison-operators.data.js';\nexport { tests as complexExpressions } from './complex-expressions.data.js';\nexport { tests as conditionals } from './conditionals.data.js';\nexport { tests as dataTypes } from './data-types.data.js';\nexport { tests as syntaxErrors } from './syntax-errors.data.js';\nexport { tests as errorHandling } from './error-handling.data.js';\nexport { tests as functionReplacements } from './function-replacements.data.js';\nexport { tests as functions } from './functions.data.js';\nexport { tests as logicalOperators } from './logical-operators.data.js';\nexport { tests as loops } from './loops.data.js';\nexport { tests as objectsAndArrays } from './objects-and-arrays.data.js';\nexport { tests as operatorPrecedence } from './operator-precedence.data.js';\nexport { tests as otherOperators } from './other-operators.data.js';\nexport { tests as security } from './security.data.js';\nexport { tests as switch_ } from './switch.data.js';\nexport { tests as templateLiterals } from './template-literals.data.js';\n"
  },
  {
    "path": "test/eval/testCases/logical-operators.data.ts",
    "content": "'use strict';\nimport { TestCase } from './types.js';\n\nexport const tests: TestCase[] = [\n  {\n    code: '!test2',\n    evalExpect: false,\n    safeExpect: false,\n    category: 'Logical Operators',\n  },\n  {\n    code: '!!test2',\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Logical Operators',\n  },\n  {\n    code: '!({}).a?.a',\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Logical Operators',\n  },\n  {\n    code: '!({a: {a: 1}}).a?.a',\n    evalExpect: false,\n    safeExpect: false,\n    category: 'Logical Operators',\n  },\n  {\n    code: '!({a: {a: 0}}).a?.a',\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Logical Operators',\n  },\n  {\n    code: '!({}).a ? true : false',\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Logical Operators',\n  },\n  {\n    code: 'test2 && true',\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Logical Operators',\n  },\n  {\n    code: 'test2 || false',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Logical Operators',\n  },\n  {\n    code: \"null ?? 'default'\",\n    evalExpect: 'default',\n    safeExpect: 'default',\n    category: 'Logical Operators',\n  },\n  {\n    code: \"undefined ?? 'default'\",\n    evalExpect: 'default',\n    safeExpect: 'default',\n    category: 'Logical Operators',\n  },\n  {\n    code: \"0 ?? 'default'\",\n    evalExpect: 0,\n    safeExpect: 0,\n    category: 'Logical Operators',\n  },\n  {\n    code: \"'' ?? 'default'\",\n    evalExpect: '',\n    safeExpect: '',\n    category: 'Logical Operators',\n  },\n  {\n    code: \"false ?? 'default'\",\n    evalExpect: false,\n    safeExpect: false,\n    category: 'Logical Operators',\n  },\n  {\n    code: \"NaN ?? 'default'\",\n    evalExpect: 'NaN',\n    safeExpect: 'NaN',\n    category: 'Logical Operators',\n  },\n  {\n    code: \"null ?? null ?? 'default'\",\n    evalExpect: 'default',\n    safeExpect: 'default',\n    category: 'Logical Operators',\n  },\n  {\n    code: '1 ?? 2 ?? 3',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Logical Operators',\n  },\n  {\n    code: 'null ?? 2 ?? 3',\n    evalExpect: 2,\n    safeExpect: 2,\n    category: 'Logical Operators',\n  },\n  {\n    code: \"({a: 1}).a ?? 'default'\",\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Logical Operators',\n  },\n  {\n    code: \"({}).a ?? 'default'\",\n    evalExpect: 'default',\n    safeExpect: 'default',\n    category: 'Logical Operators',\n  },\n  {\n    code: \"({}).a?.b ?? 'default'\",\n    evalExpect: 'default',\n    safeExpect: 'default',\n    category: 'Logical Operators',\n  },\n  {\n    code: \"null?.a ?? 'default'\",\n    evalExpect: 'default',\n    safeExpect: 'default',\n    category: 'Logical Operators',\n  },\n  {\n    code: \"({a: {b: 1}}).a?.b ?? 'default'\",\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Logical Operators',\n  },\n  {\n    code: \"({a: null}).a?.b ?? 'default'\",\n    evalExpect: 'default',\n    safeExpect: 'default',\n    category: 'Logical Operators',\n  },\n  {\n    code: 'null ?? 1 || 2',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Logical Operators',\n  },\n  {\n    code: '0 || null ?? 1',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Logical Operators',\n  },\n  {\n    code: \"(null ?? false) || 'fallback'\",\n    evalExpect: 'fallback',\n    safeExpect: 'fallback',\n    category: 'Logical Operators',\n  },\n  {\n    code: \"null ?? 0 || 'fallback'\",\n    evalExpect: 'error',\n    safeExpect: 'fallback',\n    category: 'Logical Operators',\n  },\n  {\n    code: \"true && null ?? 'default'\",\n    evalExpect: 'error',\n    safeExpect: 'default',\n    category: 'Logical Operators',\n  },\n  {\n    code: 'let x = 0; (1 ?? (x = 1)); return x',\n    evalExpect: 0,\n    safeExpect: 0,\n    category: 'Logical Operators',\n  },\n  {\n    code: 'let x = 0; (null ?? (x = 1)); return x',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Logical Operators',\n  },\n  {\n    code: 'let x = 0; (undefined ?? (x = 1)); return x',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Logical Operators',\n  },\n  {\n    code: 'let x = 0; (0 ?? (x = 1)); return x',\n    evalExpect: 0,\n    safeExpect: 0,\n    category: 'Logical Operators',\n  },\n  {\n    code: \"let x = 0; ('' ?? (x = 1)); return x\",\n    evalExpect: 0,\n    safeExpect: 0,\n    category: 'Logical Operators',\n  },\n  {\n    code: 'let x = 0; (false ?? (x = 1)); return x',\n    evalExpect: 0,\n    safeExpect: 0,\n    category: 'Logical Operators',\n  },\n  {\n    code: \"({a: null ?? 'default'}).a\",\n    evalExpect: 'default',\n    safeExpect: 'default',\n    category: 'Logical Operators',\n  },\n  {\n    code: '[null ?? 1, 2 ?? 3][0]',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Logical Operators',\n  },\n  {\n    code: '[null ?? 1, undefined ?? 3][1]',\n    evalExpect: 3,\n    safeExpect: 3,\n    category: 'Logical Operators',\n  },\n  {\n    code: \"let outputs = {}; return (outputs['classify']?.intent ?? null)\",\n    evalExpect: null,\n    safeExpect: null,\n    category: 'Logical Operators',\n  },\n  {\n    code: \"let outputs = {classify: {intent: 'greeting'}}; return (outputs['classify']?.intent ?? null)\",\n    evalExpect: 'greeting',\n    safeExpect: 'greeting',\n    category: 'Logical Operators',\n  },\n  {\n    code: 'null ?? 5 ? \"yes\" : \"no\"',\n    evalExpect: 'yes',\n    safeExpect: 'yes',\n    category: 'Logical Operators',\n  },\n  {\n    code: 'null ?? 0 ? \"yes\" : \"no\"',\n    evalExpect: 'no',\n    safeExpect: 'no',\n    category: 'Logical Operators',\n  },\n  {\n    code: '!~0',\n    evalExpect: false,\n    safeExpect: false,\n    category: 'Logical Operators',\n  },\n  {\n    code: 'true && true && true',\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Logical Operators',\n  },\n  {\n    code: 'false || false || true',\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Logical Operators',\n  },\n  {\n    code: \"null ?? null ?? null ?? 'default'\",\n    evalExpect: 'default',\n    safeExpect: 'default',\n    category: 'Logical Operators',\n  },\n  {\n    code: '1 || 2 && 3',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Logical Operators',\n  },\n  {\n    code: '0 && 1 || 2',\n    evalExpect: 2,\n    safeExpect: 2,\n    category: 'Logical Operators',\n  },\n  {\n    code: 'true || false && false',\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Logical Operators',\n  },\n  {\n    code: '!true',\n    evalExpect: false,\n    safeExpect: false,\n    category: 'Logical Operators',\n  },\n  {\n    code: '!false',\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Logical Operators',\n  },\n];\n"
  },
  {
    "path": "test/eval/testCases/logical-operators.spec.ts",
    "content": "'use strict';\nimport { run, getState } from './test-utils.js';\nimport { tests } from './logical-operators.data.js';\n\ndescribe('Logical Operators Tests', () => {\n  describe('Sync', () => {\n    tests.forEach((test) => {\n      let state = getState();\n      it(test.code.substring(0, 100), async () => {\n        await run(test, state, false);\n      });\n    });\n  });\n\n  describe('Async', () => {\n    tests.forEach((test) => {\n      let state = getState();\n      it(test.code.substring(0, 100), async () => {\n        await run(test, state, true);\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/eval/testCases/loops.data.ts",
    "content": "'use strict';\nimport { TestCase } from './types.js';\n\nexport const tests: TestCase[] = [\n  {\n    code: 'let x; for(let i = 0; i < 2; i++){ x = i }; return x;',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Loops',\n  },\n  {\n    code: 'let x; for(let i = 0; i < 2; i++){ x = i; break; }; return x;',\n    evalExpect: 0,\n    safeExpect: 0,\n    category: 'Loops',\n  },\n  {\n    code: 'let x; for(let i = 0; i < 2; i++){ x = i; continue; x++ }; return x;',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Loops',\n  },\n  {\n    code: 'break;',\n    evalExpect: 'error',\n    safeExpect: 'error',\n    category: 'Loops',\n  },\n  {\n    code: 'continue;',\n    evalExpect: 'error',\n    safeExpect: 'error',\n    category: 'Loops',\n  },\n  {\n    code: 'let sum = 0; for (let i = 0; i < 5; i++) { if (i === 2) continue; sum += i; }; return sum;',\n    evalExpect: 8,\n    safeExpect: 8,\n    category: 'Loops',\n  },\n  {\n    code: 'let sum = 0; for (let i = 0; i < 10; i++) { if (i === 3) break; sum += i; }; return sum;',\n    evalExpect: 3,\n    safeExpect: 3,\n    category: 'Loops',\n  },\n  {\n    code: 'let sum = 0; for (let i = 0; i < 5; i++) { if (i > 0) { if (i === 2) continue; } sum += i; }; return sum;',\n    evalExpect: 8,\n    safeExpect: 8,\n    category: 'Loops',\n  },\n  {\n    code: 'let sum = 0; for (let i = 0; i < 5; i++) { if (i === 2) { continue; } sum += i; }; return sum;',\n    evalExpect: 8,\n    safeExpect: 8,\n    category: 'Loops',\n  },\n  {\n    code: 'let x = 0; while (x < 5) { x++; if (x === 3) continue; if (x === 4) break; }; return x;',\n    evalExpect: 4,\n    safeExpect: 4,\n    category: 'Loops',\n  },\n  {\n    code: 'let sum = 0; for (let i = 0; i < 5; i++) { sum += i === 2 ? continue : i; }; return sum;',\n    evalExpect: 'error',\n    safeExpect: 'error',\n    category: 'Loops',\n  },\n  {\n    code: 'let x = 2; while(--x){ }; return x;',\n    evalExpect: 0,\n    safeExpect: 0,\n    category: 'Loops',\n  },\n  {\n    code: 'let x = 1; do {x++} while(x < 1); return x;',\n    evalExpect: 2,\n    safeExpect: 2,\n    category: 'Loops',\n  },\n  {\n    code: 'for(let i of [1,2]){ return i };',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Loops',\n  },\n  {\n    code: 'let arr = [1,2]; for(let i of arr){ return i };',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Loops',\n  },\n  {\n    code: 'for(let i in [1,2]){ return i };',\n    evalExpect: '0',\n    safeExpect: '0',\n    category: 'Loops',\n  },\n  {\n    code: 'let i = 1; {let j = 1; i += j;}; return i',\n    evalExpect: 2,\n    safeExpect: 2,\n    category: 'Loops',\n  },\n  {\n    code: 'let c = 0; for (let i = 0; i < 10; i++) {c++} return c',\n    evalExpect: 10,\n    safeExpect: 10,\n    category: 'Loops',\n  },\n  {\n    code: \"outer: for(let i = 0; i < 3; i++) { for(let j = 0; j < 3; j++) { if(i === 1 && j === 1) break outer; } } return 'done'\",\n    evalExpect: 'done',\n    safeExpect: 'done',\n    category: 'Loops',\n  },\n  {\n    code: 'let hits = \"\"; outer: for (let i = 0; i < 3; i++) { for (let j = 0; j < 3; j++) { hits += `${i}${j}|`; continue outer; } } return hits;',\n    evalExpect: '00|10|20|',\n    safeExpect: '00|10|20|',\n    category: 'Loops',\n  },\n  {\n    code: 'let out = \"\"; outer: { out += \"a\"; break outer; out += \"b\"; } return out;',\n    evalExpect: 'a',\n    safeExpect: 'a',\n    category: 'Loops',\n  },\n  {\n    code: 'let out = \"\"; outer: switch (1) { case 1: out = \"ok\"; break outer; default: out = \"bad\"; } return out;',\n    evalExpect: 'ok',\n    safeExpect: 'ok',\n    category: 'Loops',\n  },\n  {\n    code: 'let out = \"\"; outer: inner: for (let i = 0; i < 3; i++) { out += i; if (i < 2) continue outer; out += \"!\"; } return out;',\n    evalExpect: '012!',\n    safeExpect: '012!',\n    category: 'Loops',\n  },\n  {\n    code: 'let out = \"\"; outer: inner: for (let i = 0; i < 3; i++) { out += i; if (i < 2) continue inner; out += \"!\"; } return out;',\n    evalExpect: '012!',\n    safeExpect: '012!',\n    category: 'Loops',\n  },\n  {\n    code: 'let out = \"\"; first: second: { out += \"a\"; break first; out += \"b\"; } return out;',\n    evalExpect: 'a',\n    safeExpect: 'a',\n    category: 'Loops',\n  },\n  {\n    code: 'let out = \"\"; first: second: switch (1) { case 1: out += \"a\"; break second; default: out += \"b\"; } return out;',\n    evalExpect: 'a',\n    safeExpect: 'a',\n    category: 'Loops',\n  },\n  {\n    code: 'let out = \"\"; outer: if (true) { out += \"a\"; break outer; out += \"b\"; } return out;',\n    evalExpect: 'a',\n    safeExpect: 'a',\n    category: 'Loops',\n  },\n  {\n    code: 'let out = \"\"; outer: try { out += \"a\"; break outer; } finally { out += \"f\"; } return out;',\n    evalExpect: 'af',\n    safeExpect: 'af',\n    category: 'Loops',\n  },\n  {\n    code: 'let out = \"\"; outer: while (true) { out += \"a\"; break outer; } return out;',\n    evalExpect: 'a',\n    safeExpect: 'a',\n    category: 'Loops',\n  },\n  {\n    code: 'let out = \"\"; outer: do { out += \"a\"; continue outer; out += \"b\"; } while (false); return out;',\n    evalExpect: 'a',\n    safeExpect: 'a',\n    category: 'Loops',\n  },\n  {\n    code: 'let out = \"\"; outer: for (const value of [1, 2, 3]) { out += value; if (value === 2) break outer; } return out;',\n    evalExpect: '12',\n    safeExpect: '12',\n    category: 'Loops',\n  },\n  {\n    code: 'let out = \"\"; outer: for (const key in { a: 1, b: 2, c: 3 }) { out += key; if (key === \"b\") break outer; } return out;',\n    evalExpect: 'ab',\n    safeExpect: 'ab',\n    category: 'Loops',\n  },\n  {\n    code: 'let hits = \"\"; outer: for (let i = 0; i < 3; i++) { switch (i) { case 1: hits += \"s\"; continue outer; default: hits += i; } hits += \"|\"; } return hits;',\n    evalExpect: '0|s2|',\n    safeExpect: '0|s2|',\n    category: 'Loops',\n  },\n  {\n    code: 'let hits = \"\"; outer: for (let i = 0; i < 3; i++) { inner: for (let j = 0; j < 3; j++) { if (j === 1) break inner; if (i === 2) break outer; hits += `${i}${j}|`; } } return hits;',\n    evalExpect: '00|10|',\n    safeExpect: '00|10|',\n    category: 'Loops',\n  },\n  {\n    code: 'let log = \"\"; outer: { try { log += \"a\"; break outer; } finally { log += \"f\"; } log += \"x\"; } return log;',\n    evalExpect: 'af',\n    safeExpect: 'af',\n    category: 'Loops',\n  },\n  {\n    code: 'let log = \"\"; outer: for (let i = 0; i < 3; i++) { for (let j = 0; j < 3; j++) { try { log += `${i}${j}|`; if (j === 1) break; } finally { if (j === 1) continue outer; } log += \"x\"; } log += \"y\"; } return log;',\n    evalExpect: '00|x01|10|x11|20|x21|',\n    safeExpect: '00|x01|10|x11|20|x21|',\n    category: 'Loops',\n  },\n  {\n    code: 'let log = \"\"; outer: for (let i = 0; i < 3; i++) { try { switch (i) { case 2: log += \"b\"; break outer; default: log += i; } } finally { log += \"f\"; } log += \"x\"; } return log;',\n    evalExpect: '0fx1fxbf',\n    safeExpect: '0fx1fxbf',\n    category: 'Loops',\n  },\n  {\n    code: \"for(let i = 0, j = 10; i < 3; i++, j--) { } return 'done'\",\n    evalExpect: 'done',\n    safeExpect: 'done',\n    category: 'Loops',\n  },\n  {\n    code: 'let i = 0; for (; i < 3;) { i++; } return i;',\n    evalExpect: 3,\n    safeExpect: 3,\n    category: 'Loops',\n  },\n  {\n    code: 'let i = 0; for (;;) { i++; if (i === 3) break; } return i;',\n    evalExpect: 3,\n    safeExpect: 3,\n    category: 'Loops',\n  },\n  {\n    code: 'let total = 0; for (; total < 3; total++) {} return total;',\n    evalExpect: 3,\n    safeExpect: 3,\n    category: 'Loops',\n  },\n  {\n    code: 'let x = 0; do x++; while (x < 3); return x;',\n    evalExpect: 3,\n    safeExpect: 3,\n    category: 'Loops',\n  },\n];\n"
  },
  {
    "path": "test/eval/testCases/loops.spec.ts",
    "content": "'use strict';\nimport { run, getState } from './test-utils.js';\nimport { tests } from './loops.data.js';\n\ndescribe('Loops Tests', () => {\n  describe('Sync', () => {\n    tests.forEach((test) => {\n      let state = getState();\n      it(test.code.substring(0, 100), async () => {\n        await run(test, state, false);\n      });\n    });\n  });\n\n  describe('Async', () => {\n    tests.forEach((test) => {\n      let state = getState();\n      it(test.code.substring(0, 100), async () => {\n        await run(test, state, true);\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/eval/testCases/objects-and-arrays.data.ts",
    "content": "'use strict';\nimport { TestCase } from './types.js';\n\nexport const tests: TestCase[] = [\n  {\n    code: 'a.b.c',\n    evalExpect: 2,\n    safeExpect: 2,\n    category: 'Objects & Arrays',\n  },\n  {\n    code: '[test2, 2]',\n    evalExpect: [1, 2],\n    safeExpect: [1, 2],\n    category: 'Objects & Arrays',\n  },\n  {\n    code: '{\"aa\": test[0](), b: test2 * 3}',\n    evalExpect: {\n      aa: 1,\n      b: 3,\n    },\n    safeExpect: {\n      aa: 1,\n      b: 3,\n    },\n    category: 'Objects & Arrays',\n  },\n  {\n    code: '{\"\\\\\\\\\":\"\\\\\\\\\"}',\n    evalExpect: {\n      '\\\\': '\\\\',\n    },\n    safeExpect: {\n      '\\\\': '\\\\',\n    },\n    category: 'Objects & Arrays',\n  },\n  {\n    code: 'Object.keys({a:1})',\n    evalExpect: ['a'],\n    safeExpect: ['a'],\n    category: 'Objects & Arrays',\n  },\n  {\n    code: '[1, ...[2, [test2, 4]], 5]',\n    evalExpect: [1, 2, [1, 4], 5],\n    safeExpect: [1, 2, [1, 4], 5],\n    category: 'Objects & Arrays',\n  },\n  {\n    code: 'const obj1 = {a: 1, b: 2}; const obj2 = {c: 3, ...obj1}; return obj2.a',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Objects & Arrays',\n  },\n  {\n    code: 'const obj1 = {a: 1, b: 2}; const obj2 = {...obj1, b: 5}; return obj2.b',\n    evalExpect: 5,\n    safeExpect: 5,\n    category: 'Objects & Arrays',\n  },\n  {\n    code: 'const obj1 = {x: 10}; const obj2 = {y: 20}; const obj3 = {...obj1, ...obj2}; return obj3.x + obj3.y',\n    evalExpect: 30,\n    safeExpect: 30,\n    category: 'Objects & Arrays',\n  },\n  {\n    code: 'const arr1 = [1, 2, 3]; const arr2 = [...arr1, 4, 5]; return arr2.length',\n    evalExpect: 5,\n    safeExpect: 5,\n    category: 'Objects & Arrays',\n  },\n  {\n    code: 'const arr1 = [1, 2]; const arr2 = [3, 4]; return [...arr1, ...arr2]',\n    evalExpect: [1, 2, 3, 4],\n    safeExpect: [1, 2, 3, 4],\n    category: 'Objects & Arrays',\n  },\n  {\n    code: 'const a = [1]; const b = [2]; const c = [3]; return [...a, ...b, ...c]',\n    evalExpect: [1, 2, 3],\n    safeExpect: [1, 2, 3],\n    category: 'Objects & Arrays',\n  },\n  {\n    code: 'const data = []; return {data}',\n    evalExpect: { data: [] },\n    safeExpect: { data: [] },\n    category: 'Objects & Arrays',\n  },\n  {\n    code: 'const x = 1; const y = 2; return {x, y}',\n    evalExpect: { x: 1, y: 2 },\n    safeExpect: { x: 1, y: 2 },\n    category: 'Objects & Arrays',\n  },\n  {\n    code: 'const name = \"Alice\"; const age = 30; return {name, age, city: \"NYC\"}',\n    evalExpect: { name: 'Alice', age: 30, city: 'NYC' },\n    safeExpect: { name: 'Alice', age: 30, city: 'NYC' },\n    category: 'Objects & Arrays',\n  },\n  {\n    code: 'const a = 1; const obj = {a, b: a + 1}; return obj.a + obj.b',\n    evalExpect: 3,\n    safeExpect: 3,\n    category: 'Objects & Arrays',\n  },\n  // Computed property names - basic\n  {\n    code: '({[\"a\" + \"b\"]: 1})',\n    evalExpect: { ab: 1 },\n    safeExpect: { ab: 1 },\n    category: 'Objects & Arrays',\n  },\n  {\n    code: 'let k = \"x\"; return ({[k]: 42})',\n    evalExpect: { x: 42 },\n    safeExpect: { x: 42 },\n    category: 'Objects & Arrays',\n  },\n  {\n    code: '({[1 + 2]: \"three\"})',\n    evalExpect: { 3: 'three' },\n    safeExpect: { 3: 'three' },\n    category: 'Objects & Arrays',\n  },\n  {\n    code: 'let n = \"world\"; return ({[`hello_${n}`]: 1})',\n    evalExpect: { hello_world: 1 },\n    safeExpect: { hello_world: 1 },\n    category: 'Objects & Arrays',\n  },\n  {\n    code: 'function k() { return \"key\" }; return ({[k()]: \"val\"})',\n    evalExpect: { key: 'val' },\n    safeExpect: { key: 'val' },\n    category: 'Objects & Arrays',\n  },\n  {\n    code: 'let t = true; return ({[t ? \"a\" : \"b\"]: 1})',\n    evalExpect: { a: 1 },\n    safeExpect: { a: 1 },\n    category: 'Objects & Arrays',\n  },\n  // Computed property names - multiple and mixed\n  {\n    code: 'let a = \"x\", b = \"y\"; return ({[a]: 1, [b]: 2})',\n    evalExpect: { x: 1, y: 2 },\n    safeExpect: { x: 1, y: 2 },\n    category: 'Objects & Arrays',\n  },\n  {\n    code: '({a: 1, [\"b\"]: 2, c: 3})',\n    evalExpect: { a: 1, b: 2, c: 3 },\n    safeExpect: { a: 1, b: 2, c: 3 },\n    category: 'Objects & Arrays',\n  },\n  {\n    code: '({a: 1, [\"a\"]: 2})',\n    evalExpect: { a: 2 },\n    safeExpect: { a: 2 },\n    category: 'Objects & Arrays',\n  },\n  {\n    code: 'let arr = [\"key\"]; return ({[arr[0]]: \"val\"})',\n    evalExpect: { key: 'val' },\n    safeExpect: { key: 'val' },\n    category: 'Objects & Arrays',\n  },\n  // Computed property names - edge cases\n  {\n    code: 'let c = 0; function k() { c++; return \"a\" } let o = {[k()]: 1}; return c',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Objects & Arrays',\n  },\n  {\n    code: '({[\"\"]: 1})',\n    evalExpect: { '': 1 },\n    safeExpect: { '': 1 },\n    category: 'Objects & Arrays',\n  },\n  {\n    code: '({[0]: \"zero\"})',\n    evalExpect: { 0: 'zero' },\n    safeExpect: { 0: 'zero' },\n    category: 'Objects & Arrays',\n  },\n  {\n    code: '({[true]: 1})',\n    evalExpect: { true: 1 },\n    safeExpect: { true: 1 },\n    category: 'Objects & Arrays',\n  },\n  {\n    code: '({[null]: 1})',\n    evalExpect: { null: 1 },\n    safeExpect: { null: 1 },\n    category: 'Objects & Arrays',\n  },\n  {\n    code: '({[undefined]: 1})',\n    evalExpect: { undefined: 1 },\n    safeExpect: { undefined: 1 },\n    category: 'Objects & Arrays',\n  },\n  // Computed property names - with spread\n  {\n    code: 'let rest = {b: 2}; return ({[\"a\"]: 1, ...rest})',\n    evalExpect: { a: 1, b: 2 },\n    safeExpect: { a: 1, b: 2 },\n    category: 'Objects & Arrays',\n  },\n  {\n    code: 'let rest = {a: 1}; return ({...rest, [\"b\"]: 2})',\n    evalExpect: { a: 1, b: 2 },\n    safeExpect: { a: 1, b: 2 },\n    category: 'Objects & Arrays',\n  },\n  {\n    code: 'let rest = {a: 1}; return ({...rest, [\"a\"]: 2})',\n    evalExpect: { a: 2 },\n    safeExpect: { a: 2 },\n    category: 'Objects & Arrays',\n  },\n  {\n    code: 'let o = {k: \"deep\"}; return ({[o.k]: 1})',\n    evalExpect: { deep: 1 },\n    safeExpect: { deep: 1 },\n    category: 'Objects & Arrays',\n  },\n  {\n    code: '({[(0, \"a\")]: 1})',\n    evalExpect: { a: 1 },\n    safeExpect: { a: 1 },\n    category: 'Objects & Arrays',\n  },\n  // Computed method names\n  {\n    code: 'let m = \"greet\"; let o = {[m]() { return \"hi\" }}; return o.greet()',\n    evalExpect: 'hi',\n    safeExpect: 'hi',\n    category: 'Objects & Arrays',\n  },\n  {\n    code: 'let o = {[\"say\"]() { return \"hello\" }}; return o.say()',\n    evalExpect: 'hello',\n    safeExpect: 'hello',\n    category: 'Objects & Arrays',\n  },\n  {\n    code: 'let o = {[\"a\" + \"b\"]() { return 1 }}; return o.ab()',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Objects & Arrays',\n  },\n  {\n    code: 'let o = {x: 10, [\"getX\"]() { return this.x }}; return o.getX()',\n    evalExpect: 10,\n    safeExpect: 10,\n    category: 'Objects & Arrays',\n  },\n  {\n    code: '[1,2,3,].length',\n    evalExpect: 3,\n    safeExpect: 3,\n    category: 'Objects & Arrays',\n  },\n  {\n    code: 'Object.keys({a:1,b:2,}).length',\n    evalExpect: 2,\n    safeExpect: 2,\n    category: 'Objects & Arrays',\n  },\n  {\n    code: '[,].length',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Objects & Arrays',\n  },\n  {\n    code: '0 in [,]',\n    evalExpect: false,\n    safeExpect: false,\n    category: 'Objects & Arrays',\n  },\n  {\n    code: '[,,].length',\n    evalExpect: 2,\n    safeExpect: 2,\n    category: 'Objects & Arrays',\n  },\n  {\n    code: '[1,,].length',\n    evalExpect: 2,\n    safeExpect: 2,\n    category: 'Objects & Arrays',\n  },\n  {\n    code: '1 in [1,,]',\n    evalExpect: false,\n    safeExpect: false,\n    category: 'Objects & Arrays',\n  },\n  {\n    code: '[1,,2].length',\n    evalExpect: 3,\n    safeExpect: 3,\n    category: 'Objects & Arrays',\n  },\n  {\n    code: '1 in [1,,2]',\n    evalExpect: false,\n    safeExpect: false,\n    category: 'Objects & Arrays',\n  },\n  {\n    code: '[1,,2][2]',\n    evalExpect: 2,\n    safeExpect: 2,\n    category: 'Objects & Arrays',\n  },\n];\n"
  },
  {
    "path": "test/eval/testCases/objects-and-arrays.spec.ts",
    "content": "'use strict';\nimport { run, getState } from './test-utils.js';\nimport { tests } from './objects-and-arrays.data.js';\n\ndescribe('Objects And Arrays Tests', () => {\n  describe('Sync', () => {\n    tests.forEach((test) => {\n      let state = getState();\n      it(test.code.substring(0, 100), async () => {\n        await run(test, state, false);\n      });\n    });\n  });\n\n  describe('Async', () => {\n    tests.forEach((test) => {\n      let state = getState();\n      it(test.code.substring(0, 100), async () => {\n        await run(test, state, true);\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/eval/testCases/operator-precedence.data.ts",
    "content": "'use strict';\nimport { TestCase } from './types.js';\n\nexport const tests: TestCase[] = [\n  {\n    code: \"test2 !== '1' && false\",\n    evalExpect: false,\n    safeExpect: false,\n    category: 'Operator Precedence',\n  },\n  {\n    code: 'true && true || false',\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Operator Precedence',\n  },\n  {\n    code: 'true || true && false',\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Operator Precedence',\n  },\n  {\n    code: '1 && 2 == 1',\n    evalExpect: false,\n    safeExpect: false,\n    category: 'Operator Precedence',\n  },\n  {\n    code: '1 + 2 === 1 + 2 === 1 && 2',\n    evalExpect: false,\n    safeExpect: false,\n    category: 'Operator Precedence',\n  },\n  {\n    code: 'true === 1 && 2',\n    evalExpect: false,\n    safeExpect: false,\n    category: 'Operator Precedence',\n  },\n  {\n    code: '-1 < 1 && 2 > 1',\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Operator Precedence',\n  },\n  {\n    code: '!5 > 3',\n    evalExpect: false,\n    safeExpect: false,\n    category: 'Operator Precedence',\n  },\n  {\n    code: '!5 < 3',\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Operator Precedence',\n  },\n  {\n    code: '!0 === true',\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Operator Precedence',\n  },\n  {\n    code: '!false && true',\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Operator Precedence',\n  },\n  {\n    code: '!true || true',\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Operator Precedence',\n  },\n  {\n    code: '!false && false || true',\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Operator Precedence',\n  },\n  {\n    code: '5 > 3 > 1',\n    evalExpect: false,\n    safeExpect: false,\n    category: 'Operator Precedence',\n  },\n  {\n    code: '1 < 2 < 3',\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Operator Precedence',\n  },\n  {\n    code: '5 > 3 === true',\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Operator Precedence',\n  },\n  {\n    code: '1 | 2 && 3',\n    evalExpect: 3,\n    safeExpect: 3,\n    category: 'Operator Precedence',\n  },\n  {\n    code: 'true && 1 | 2',\n    evalExpect: 3,\n    safeExpect: 3,\n    category: 'Operator Precedence',\n  },\n  {\n    code: '4 & 5 || 0',\n    evalExpect: 4,\n    safeExpect: 4,\n    category: 'Operator Precedence',\n  },\n  {\n    code: '2 + 3 << 1',\n    evalExpect: 10,\n    safeExpect: 10,\n    category: 'Operator Precedence',\n  },\n  {\n    code: '8 >> 1 + 1',\n    evalExpect: 2,\n    safeExpect: 2,\n    category: 'Operator Precedence',\n  },\n  {\n    code: '1 << 2 * 2',\n    evalExpect: 16,\n    safeExpect: 16,\n    category: 'Operator Precedence',\n  },\n  {\n    code: '5 & 3 | 2',\n    evalExpect: 3,\n    safeExpect: 3,\n    category: 'Operator Precedence',\n  },\n  {\n    code: '8 | 4 & 2',\n    evalExpect: 8,\n    safeExpect: 8,\n    category: 'Operator Precedence',\n  },\n  {\n    code: '15 ^ 3 & 7',\n    evalExpect: 12,\n    safeExpect: 12,\n    category: 'Operator Precedence',\n  },\n  // Exponentiation precedence tests\n  {\n    code: '2 * 3 ** 2',\n    evalExpect: 18,\n    safeExpect: 18,\n    category: 'Operator Precedence',\n  },\n  {\n    code: '10 / 2 ** 3',\n    evalExpect: 1.25,\n    safeExpect: 1.25,\n    category: 'Operator Precedence',\n  },\n  {\n    code: '2 + 3 ** 2',\n    evalExpect: 11,\n    safeExpect: 11,\n    category: 'Operator Precedence',\n  },\n  {\n    code: '2 * 3 * 2 ** 3',\n    evalExpect: 48,\n    safeExpect: 48,\n    category: 'Operator Precedence',\n  },\n  {\n    code: '-2 ** 2',\n    evalExpect: 'error',\n    safeExpect: 'error',\n    category: 'Operator Precedence',\n  },\n  // Exponentiation right-associativity tests\n  {\n    code: '2 ** 3 ** 2',\n    evalExpect: 512,\n    safeExpect: 512,\n    category: 'Operator Precedence',\n  },\n  {\n    code: '2 ** 2 ** 3',\n    evalExpect: 256,\n    safeExpect: 256,\n    category: 'Operator Precedence',\n  },\n  {\n    code: '3 ** 2 ** 2',\n    evalExpect: 81,\n    safeExpect: 81,\n    category: 'Operator Precedence',\n  },\n  {\n    code: '4 / 2 ** 2 * 3',\n    evalExpect: 3,\n    safeExpect: 3,\n    category: 'Operator Precedence',\n  },\n  {\n    code: '100 - 10 ** 2',\n    evalExpect: 0,\n    safeExpect: 0,\n    category: 'Operator Precedence',\n  },\n];\n"
  },
  {
    "path": "test/eval/testCases/operator-precedence.spec.ts",
    "content": "'use strict';\nimport { run, getState } from './test-utils.js';\nimport { tests } from './operator-precedence.data.js';\n\ndescribe('Operator Precedence Tests', () => {\n  describe('Sync', () => {\n    tests.forEach((test) => {\n      let state = getState();\n      it(test.code.substring(0, 100), async () => {\n        await run(test, state, false);\n      });\n    });\n  });\n\n  describe('Async', () => {\n    tests.forEach((test) => {\n      let state = getState();\n      it(test.code.substring(0, 100), async () => {\n        await run(test, state, true);\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/eval/testCases/other-operators.data.ts",
    "content": "'use strict';\nimport { TestCase } from './types.js';\n\nexport const tests: TestCase[] = [\n  {\n    code: \"typeof '1'\",\n    evalExpect: 'string',\n    safeExpect: 'string',\n    category: 'Other Operators',\n  },\n  {\n    code: \"typeof z === 'undefined'\",\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Other Operators',\n  },\n  {\n    code: '{} instanceof Object',\n    evalExpect: 'error',\n    safeExpect: true,\n    category: 'Other Operators',\n  },\n  {\n    code: '{} instanceof undefined',\n    evalExpect: 'error',\n    safeExpect: 'error',\n    category: 'Other Operators',\n  },\n  {\n    code: \"'a' in {a: 1}\",\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Other Operators',\n  },\n  {\n    code: '1,2',\n    evalExpect: 2,\n    safeExpect: 2,\n    category: 'Other Operators',\n  },\n  {\n    code: \"void 2 == '2'\",\n    evalExpect: false,\n    safeExpect: false,\n    category: 'Other Operators',\n  },\n  {\n    code: \"void (2 == '2')\",\n    category: 'Other Operators',\n  },\n  {\n    code: 'new Date(0).toISOString()',\n    evalExpect: '1970-01-01T00:00:00.000Z',\n    safeExpect: '1970-01-01T00:00:00.000Z',\n    category: 'Other Operators',\n  },\n  {\n    code: 'function E(a) { this.scope = a.context }; return new E(isNaN).scope?.Function?.name',\n    category: 'Other Operators',\n  },\n  {\n    code: 'typeof 5 + \"2\"',\n    evalExpect: 'number2',\n    safeExpect: 'number2',\n    category: 'Other Operators',\n  },\n  {\n    code: 'typeof (5 + 2)',\n    evalExpect: 'number',\n    safeExpect: 'number',\n    category: 'Other Operators',\n  },\n  {\n    code: 'typeof 5 === \"number\"',\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Other Operators',\n  },\n  {\n    code: 'void 0 === undefined',\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Other Operators',\n  },\n  {\n    code: 'void 5 + 10',\n    evalExpect: 'NaN',\n    safeExpect: 'NaN',\n    category: 'Other Operators',\n  },\n  {\n    code: 'typeof void 0',\n    evalExpect: 'undefined',\n    safeExpect: 'undefined',\n    category: 'Other Operators',\n  },\n  {\n    code: 'null?.[0]',\n    category: 'Other Operators',\n  },\n  {\n    code: 'null?.fn?.()',\n    category: 'Other Operators',\n  },\n];\n"
  },
  {
    "path": "test/eval/testCases/other-operators.spec.ts",
    "content": "'use strict';\nimport { run, getState } from './test-utils.js';\nimport { tests } from './other-operators.data.js';\n\ndescribe('Other Operators Tests', () => {\n  describe('Sync', () => {\n    tests.forEach((test) => {\n      let state = getState();\n      it(test.code.substring(0, 100), async () => {\n        await run(test, state, false);\n      });\n    });\n  });\n\n  describe('Async', () => {\n    tests.forEach((test) => {\n      let state = getState();\n      it(test.code.substring(0, 100), async () => {\n        await run(test, state, true);\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/eval/testCases/security.data.ts",
    "content": "import { TestCase } from './types';\n\nexport const tests: TestCase[] = [\n  {\n    code: 'globalThis.constructor.name',\n    evalExpect: 'Window',\n    safeExpect: 'SandboxGlobal',\n    category: 'Security',\n  },\n  {\n    code: 'this.constructor.name',\n    evalExpect: 'Window',\n    safeExpect: 'SandboxGlobal',\n    category: 'Security',\n  },\n  {\n    code: 'eval.name',\n    evalExpect: 'eval',\n    safeExpect: 'sandboxEval',\n    category: 'Security',\n  },\n  {\n    code: 'return eval(\"1+1; 2+2;\")',\n    evalExpect: 4,\n    safeExpect: 4,\n    category: 'Security',\n  },\n  {\n    code: 'Function.name',\n    evalExpect: 'Function',\n    safeExpect: 'SandboxFunction',\n    category: 'Security',\n  },\n  {\n    code: 'bypassed=1',\n    evalExpect: 'error',\n    safeExpect: '/bypassed is not defined/',\n    category: 'Security',\n  },\n  {\n    code: '`${`${bypassed=1}`}`',\n    evalExpect: 'error',\n    safeExpect: '/bypassed is not defined/',\n    category: 'Security',\n  },\n  {\n    code: '[].filter.constructor(\"return \\'ok\\'\")()',\n    evalExpect: 'ok',\n    safeExpect: 'ok',\n    category: 'Security',\n  },\n  {\n    code: \"[].filter.constructor('return bypassed')()\",\n    evalExpect: false,\n    safeExpect: '/bypassed is not defined/',\n    category: 'Security',\n  },\n  {\n    code: \"[].filter.constructor('return bypassed=1')()\",\n    evalExpect: 'bypassed',\n    safeExpect: '/bypassed is not defined/',\n    category: 'Security',\n  },\n  {\n    code: \"[].filter.constructor.apply(null,['return bypassed=1'])()\",\n    evalExpect: 'bypassed',\n    safeExpect: '/bypassed is not defined/',\n    category: 'Security',\n  },\n  {\n    code: \"[].filter.constructor('return this.bypassed=1')()\",\n    evalExpect: 'bypassed',\n    safeExpect: 1,\n    category: 'Security',\n  },\n  {\n    code: \"[].filter.constructor('return this.constructor.name')()\",\n    evalExpect: 'Window',\n    safeExpect: 'SandboxGlobal',\n    category: 'Security',\n  },\n  {\n    code: \"[].filter.constructor.call(this,'bypassed = 1')()\",\n    evalExpect: 'bypassed',\n    safeExpect: '/bypassed is not defined/',\n    category: 'Security',\n  },\n  {\n    code: '[].filter.constructor(\\'return eval(\"bypassed = 1\")\\')()',\n    evalExpect: 'bypassed',\n    safeExpect: '/bypassed is not defined/',\n    category: 'Security',\n  },\n  {\n    code: '[+!+[]]+[]',\n    evalExpect: '1',\n    safeExpect: '1',\n    category: 'Security',\n  },\n  {\n    code: '[][(![]+[])[+!+[]]+(!![]+[])[+[]]][([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]](([][(!![]+[])[!+[]+!+[]+!+[]]+([][[]]+[])[+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(!![]+[])[!+[]+!+[]+!+[]]+(![]+[])[!+[]+!+[]+!+[]]]()+[])[!+[]+!+[]]+(+[![]]+[+(+!+[]+(!+[]+[])[!+[]+!+[]+!+[]]+[+!+[]]+[+[]]+[+[]]+[+[]])])[+!+[]+[+[]]]+(+(!+[]+!+[]+[+!+[]]+[+!+[]]))[(!![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([]+[])[([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]][([][[]]+[])[+!+[]]+(![]+[])[+!+[]]+((+[])[([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[+!+[]+[+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]]](!+[]+!+[]+!+[]+[+!+[]])[+!+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+([][[]]+[])[!+[]+!+[]]+([]+[])[(![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(!![]+[])[+[]]+([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]()[+!+[]+[+!+[]]]+[+!+[]])()',\n    evalExpect: 'bypassed',\n    safeExpect: '/bypassed is not defined/',\n    ignoreTime: true,\n    category: 'Security',\n  },\n  {\n    code: 'Object.entries(this).find(a => a[0] === \"Function\")?.at(1)(\"bypassed=1\")()',\n    evalExpect: 'bypassed',\n    safeExpect: '/bypassed is not defined/',\n    category: 'Security',\n  },\n  {\n    code: 'Object.values(this).find(a => {if(a?.name?.endsWith(\"Function\")) return a.call(\"\", \"return bypassed = 1\")()})',\n    evalExpect: 'bypassed',\n    safeExpect: '/bypassed is not defined/',\n    category: 'Security',\n  },\n  {\n    code: '(async () => (await (async () => Function(\\'return Object.values(this).at(Object.values(this).findIndex(a => {if(a?.name?.endsWith(\"Function\")) return true}))\\')())()).name)()',\n    evalExpect: 'Function',\n    safeExpect: 'SandboxFunction',\n    category: 'Security',\n  },\n  {\n    code: '[].constructor.constructor.constructor.name',\n    evalExpect: 'Function',\n    safeExpect: 'SandboxFunction',\n    category: 'Security',\n  },\n  {\n    code: '(()=>{}).constructor.name',\n    evalExpect: 'Function',\n    safeExpect: 'SandboxFunction',\n    category: 'Security',\n  },\n  {\n    code: '(async ()=>{}).constructor.name',\n    evalExpect: 'AsyncFunction',\n    safeExpect: 'SandboxAsyncFunction',\n    category: 'Security',\n  },\n  {\n    code: '(function*(){}).constructor.name',\n    evalExpect: 'GeneratorFunction',\n    safeExpect: 'SandboxGeneratorFunction',\n    category: 'Security',\n  },\n  {\n    code: '(async function*(){}).constructor.name',\n    evalExpect: 'AsyncGeneratorFunction',\n    safeExpect: 'SandboxAsyncGeneratorFunction',\n    category: 'Security',\n  },\n  {\n    code: \"(async ()=>{}).constructor('return this')().then((a) => a.constructor.name)\",\n    evalExpect: 'Window',\n    safeExpect: 'SandboxGlobal',\n    category: 'Security',\n  },\n  {\n    code: '[].anything = 1',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Security',\n  },\n  {\n    code: '[].filter = 1',\n    evalExpect: 1,\n    safeExpect: \"/Override prototype property 'filter' not allowed/\",\n    category: 'Security',\n  },\n  {\n    code: '[].constructor.prototype.flatMap = 1',\n    evalExpect: 1,\n    safeExpect: '/Access to prototype of global object is not permitted/',\n    category: 'Security',\n  },\n  {\n    code: '[].__proto__.flatMap = 1',\n    evalExpect: 1,\n    safeExpect: '/Access to prototype of global object is not permitted/',\n    category: 'Security',\n  },\n  {\n    code: 'let p; p = [].constructor.prototype; p.flatMap = 2; return [].flatMap;',\n    evalExpect: 2,\n    safeExpect: '/Access to prototype of global object is not permitted/',\n    category: 'Security',\n  },\n  {\n    code: 'Object.anything = 1',\n    evalExpect: 'error',\n    safeExpect: \"/Cannot assign property 'anything' of a global object/\",\n    category: 'Security',\n  },\n  {\n    code: 'Object.freeze = 1',\n    evalExpect: 'error',\n    safeExpect: '/Static method or property access not permitted/',\n    category: 'Security',\n  },\n  {\n    code: '{}.constructor.anything1 = 1',\n    evalExpect: 1,\n    safeExpect: \"/Cannot assign property 'anything1' of a global object/\",\n    category: 'Security',\n  },\n  {\n    code: '{}.constructor.freeze = 1',\n    evalExpect: 1,\n    safeExpect: '/Static method or property access not permitted/',\n    category: 'Security',\n  },\n  {\n    code: '{}.constructor.constructor.anything = 1',\n    evalExpect: 1,\n    safeExpect: \"/Cannot assign property 'anything' of a global object/\",\n    category: 'Security',\n  },\n  {\n    code: '(() => {}).anything = 1',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Security',\n  },\n  {\n    code: 'Object.name',\n    evalExpect: 'error',\n    safeExpect: 'Object',\n    category: 'Security',\n  },\n  {\n    code: \"Object.assign(Object, {}) || 'ok'\",\n    evalExpect: 'error',\n    safeExpect: '/Static method or property access not permitted/',\n    category: 'Security',\n  },\n  {\n    code: \"({}).__defineGetter__('a', () => 1 ) || 'ok'\",\n    evalExpect: 'ok',\n    safeExpect: '/Method or property access not permitted/',\n    category: 'Security',\n  },\n  {\n    code: \"(() => {}).__defineGetter__('a', () => 1 ) || 'ok'\",\n    evalExpect: 'ok',\n    safeExpect: '/Method or property access not permitted/',\n    category: 'Security',\n  },\n  {\n    code: \"({}).toString.__defineGetter__('a', () => 1 ) || 'ok'\",\n    evalExpect: 'ok',\n    safeExpect: '/Method or property access not permitted/',\n    category: 'Security',\n  },\n  {\n    code: `const arr=[Array.prototype]; arr[0].polluted = 1; return [].polluted;`,\n    evalExpect: 1,\n    safeExpect: '/Access to prototype of global object is not permitted/',\n    category: 'Security',\n  },\n  {\n    code: `(async () => Array.prototype)().then((a) => a.polluted = 'pwned').then(() => [].polluted)`,\n    evalExpect: 'ok',\n    safeExpect: '/Access to prototype of global object is not permitted/',\n    category: 'Security',\n  },\n  {\n    code: `function x() {}; x.prototype.permitted = true; return new x().permitted;`,\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Security',\n  },\n  {\n    code: `function x() {}; const y = new x(); y.__proto__.permitted = true; return y.permitted;`,\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Security',\n  },\n  {\n    code: `function x() {}; const y = new x(); y.__proto__.__proto__.forbidden = true; return y.forbidden;`,\n    evalExpect: true,\n    safeExpect: '/Access to prototype of global object is not permitted/',\n    category: 'Security',\n  },\n  {\n    code: `hasOwnProperty = 1; return ({}).hasOwnProperty`,\n    evalExpect: 'error',\n    safeExpect: '/hasOwnProperty is not defined/',\n    category: 'Security',\n  },\n  {\n    code: `Object.values(this).includes(Function)`,\n    evalExpect: true,\n    safeExpect: true,\n    category: 'Security',\n  },\n  {\n    code: `Object.values(this).at(1)('bypassed=1')()`,\n    evalExpect: 'bypassed',\n    safeExpect: '/bypassed is not defined/',\n    category: 'Security',\n  },\n  {\n    code: `\nconst p = (async function () {})();\n({\n  \"finally\": p.finally,\n  ...Object.fromEntries([['then', ...Object.values(this).slice(1)]]),\n}).finally('bypassed=1')();\n`,\n    evalExpect: 'bypassed',\n    safeExpect: '/bypassed is not defined/',\n    category: 'Security',\n  },\n  {\n    code: `const a = Function; a.anything = 1; return a.anything;`,\n    evalExpect: 1,\n    safeExpect: \"/Cannot assign property 'anything' of a global object/\",\n    category: 'Security',\n  },\n  {\n    code: 'this.Function = 1',\n    evalExpect: 1,\n    safeExpect: \"/Cannot assign property 'Function' of a global object/\",\n    category: 'Security',\n  },\n  {\n    code: `\nconst callOp = (function fn() { return fn.caller; })();\n\nfunction makeContext(capture = () => {}) {\n  return { ctx: { options: 0 }, evals: { get: capture } };\n}\n\nfunction leakStatic(obj, prop) {\n  let leaked;\n  callOp({\n    done() {},\n    a() {},\n    b: [],\n    obj: { context: obj, prop, get() {} },\n    context: makeContext((fn) => (leaked = fn, () => 1))\n  });\n  return leaked;\n}\n\nfunction callDirect(fn, args) {\n  let value;\n  callOp({\n    done(_, result) { value = result; },\n    a() {},\n    b: args,\n    obj: fn,\n    context: makeContext()\n  });\n  return value;\n}\n\ncallDirect(leakStatic(Object, 'defineProperty'), [\n  leakStatic,\n  'call',\n  callDirect(leakStatic(Object, 'getOwnPropertyDescriptor'), [\n    callDirect(leakStatic(Object, 'getPrototypeOf'), [() => 0]),\n    'constructor'\n  ])\n]);\n\nlet hostFn;\ncallOp({\n  done(_, result) { hostFn = result; },\n  a: leakStatic,\n  b: [],\n  obj: {\n    context: 'return globalThis.bypassed = 1',\n    get() {}\n  },\n  context: makeContext()\n});\n\nreturn hostFn();\n`,\n    evalExpect: 'error',\n    safeExpect: \"/Access to 'caller' property is not permitted/\",\n    category: 'Security',\n  },\n];\n"
  },
  {
    "path": "test/eval/testCases/security.spec.ts",
    "content": "'use strict';\nimport { run, getState } from './test-utils.js';\nimport { tests } from './security.data.js';\n\ndescribe('Security Tests', () => {\n  describe('Sync', () => {\n    tests.forEach((test) => {\n      let state = getState();\n      it(test.code.substring(0, 100), async () => {\n        await run(test, state, false);\n      });\n    });\n  });\n\n  describe('Async', () => {\n    tests.forEach((test) => {\n      let state = getState();\n      it(test.code.substring(0, 100), async () => {\n        await run(test, state, true);\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/eval/testCases/switch.data.ts",
    "content": "'use strict';\nimport { TestCase } from './types.js';\n\nexport const tests: TestCase[] = [\n  {\n    code: 'let a = 1; let b = 2; switch(1) {case a: b = 1; case b: return b; default: return 0;}',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Switch',\n  },\n  {\n    code: 'let b = 1; switch(1) {case 1: b = 2; break; case 2: b = 3; default: b = 4}; return b',\n    evalExpect: 2,\n    safeExpect: 2,\n    category: 'Switch',\n  },\n  {\n    code: 'let b = 1; switch(3) {case 1: b = 2; break; case 2: b = 3; default: b = 4}; return b',\n    evalExpect: 4,\n    safeExpect: 4,\n    category: 'Switch',\n  },\n  {\n    code: 'let b = 1; switch(1) {case 1:b = 2; case 2: b = 3; default: b = 4}; return b',\n    evalExpect: 4,\n    safeExpect: 4,\n    category: 'Switch',\n  },\n  {\n    code: 'let a = 1; switch(a) {case 1: return 2}; return 1',\n    evalExpect: 2,\n    safeExpect: 2,\n    category: 'Switch',\n  },\n  {\n    code: 'switch (1) { case 1: { return 42; } default: return 0; }',\n    evalExpect: 42,\n    safeExpect: 42,\n    category: 'Switch',\n  },\n  {\n    code: 'switch (1) { case 1: case 2: return 7; default: return 0; }',\n    evalExpect: 7,\n    safeExpect: 7,\n    category: 'Switch',\n  },\n];\n"
  },
  {
    "path": "test/eval/testCases/switch.spec.ts",
    "content": "'use strict';\nimport { run, getState } from './test-utils.js';\nimport { tests } from './switch.data.js';\n\ndescribe('Switch Tests', () => {\n  describe('Sync', () => {\n    tests.forEach((test) => {\n      let state = getState();\n      it(test.code.substring(0, 100), async () => {\n        await run(test, state, false);\n      });\n    });\n  });\n\n  describe('Async', () => {\n    tests.forEach((test) => {\n      let state = getState();\n      it(test.code.substring(0, 100), async () => {\n        await run(test, state, true);\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/eval/testCases/syntax-errors.data.ts",
    "content": "'use strict';\nimport { TestCase } from './types.js';\n\nfunction toEvalTest(code: string, safeExpect: string): TestCase {\n  return {\n    code: `return eval(${JSON.stringify(code)});`,\n    evalExpect: 'error',\n    safeExpect,\n    category: 'Syntax Errors',\n  };\n}\n\nexport const tests: TestCase[] = [\n  // Entry and delimiter guards\n  toEvalTest(\"const value = 'oops\", \"/Unclosed '\\\\''/\"),\n  toEvalTest('const value = \"oops', '/Unclosed \\'\"/'),\n  toEvalTest('const value = `oops', \"/Unclosed '`'/\"),\n  toEvalTest('const value = 1; /* open comment', \"/Unclosed comment '\\\\/\\\\*'/\"),\n\n  // Expression and property guards\n  toEvalTest('const value =', '/Unexpected end of expression/'),\n  toEvalTest('const value = [1 2];', '/Unexpected token/'),\n  toEvalTest('const value = { a: 1 b: 2 };', '/Unexpected token/'),\n  toEvalTest('fn(, value);', '/Unexpected end of expression/'),\n  toEvalTest('const obj = { [key] };', '/Unexpected token in computed property/'),\n  toEvalTest(\n    'return -2 ** 3;',\n    '/Unary operator used immediately before exponentiation expression/',\n  ),\n  toEvalTest('value.', '/Unexpected token after prop: \\\\./'),\n  toEvalTest('const value = for;', \"/Unexpected token 'for'/\"),\n  toEvalTest('return 1 + ;', '/Unexpected end of expression/'),\n  toEvalTest('return 1,;', '/Unexpected token/'),\n  toEvalTest('return value ? truthy :;', '/Unexpected end of expression/'),\n  toEvalTest('({}).a?.;', '/Unexpected token/'),\n  toEvalTest('({}).a?.[;', \"/Unclosed '\\\\['/\"),\n  toEvalTest('a?.b = 1;', '/Invalid left-hand side in assignment/'),\n  toEvalTest('new Map?.();', '/optional chain|Unexpected/'),\n  toEvalTest('a[];', '/Unexpected end of expression/'),\n  toEvalTest('a[;', \"/Unclosed '\\\\['/\"),\n  toEvalTest('return (1 + 2;', \"/Unclosed '\\\\('/\"),\n  toEvalTest('const value = [1, 2;', \"/Unclosed '\\\\['/\"),\n  toEvalTest('const value = { a: 1;', \"/Unclosed '\\\\{'/\"),\n  toEvalTest('fn(value;', \"/Unclosed '\\\\('/\"),\n\n  // Statement-form guards\n  toEvalTest('switch (value) case 1: break;', '/Invalid switch/'),\n  toEvalTest(\n    'switch (value) { default: break; default: break; }',\n    '/Only one default switch case allowed/',\n  ),\n  toEvalTest('function fn(for) { return for; }', \"/Unexpected token 'for'/\"),\n  toEvalTest('async function fn(await) {}', \"/Unexpected token 'await'/\"),\n  toEvalTest('const [value];', '/Destructuring declaration requires an initializer/'),\n  toEvalTest('const [value + 1] = arr;', '/Invalid destructuring target/'),\n  toEvalTest('const { a: value + 1 } = obj;', '/Invalid destructuring target/'),\n  toEvalTest(\n    'function fn(...rest, last) { return rest; }',\n    '/Rest parameter must be last formal parameter/',\n  ),\n  toEvalTest('(first, ...rest, last) => first', '/Rest parameter must be last formal parameter/'),\n  toEvalTest('for (const [a, b] from pairs) {}', '/Invalid for loop definition/'),\n  toEvalTest('for (let i = 0; i < 2) {}', '/Invalid for loop definition/'),\n  toEvalTest('if (value) else return 1;', '/Unexpected token/'),\n  toEvalTest('while () {}', '/Unexpected end of expression/'),\n  toEvalTest('do { value++; }', \"/Unclosed '\\\\('/\"),\n  toEvalTest('try {} catch () {}', \"/Unexpected token '\\\\)'/\"),\n  toEvalTest('try { value++; }', '/Missing catch or finally after try/'),\n  toEvalTest('try {} catch (for) {}', \"/Unexpected token 'for'/\"),\n  toEvalTest('try {} finally', '/Unexpected token/'),\n  toEvalTest('throw ;', '/Unexpected end of expression/'),\n  toEvalTest('new ;', '/Unexpected end of expression/'),\n  toEvalTest('typeof ;', '/Unexpected end of expression/'),\n  toEvalTest('delete ;', '/Unexpected end of expression/'),\n  toEvalTest('void ;', '/Unexpected end of expression/'),\n  toEvalTest('\"a\" in ;', '/Unexpected end of expression/'),\n  toEvalTest('value instanceof ;', '/Unexpected end of expression/'),\n  toEvalTest('const value = [...];', '/Unexpected end of expression/'),\n  toEvalTest('const value = {...};', '/Unexpected end of expression/'),\n  toEvalTest('fn(...);', '/Unexpected end of expression/'),\n  toEvalTest('while (true) { break 1; }', \"/Unexpected token '1'/\"),\n  toEvalTest('while (true) { continue 1; }', \"/Unexpected token '1'/\"),\n\n  // Known parser gaps\n  toEvalTest('for (const { a } of) {}', '/Unexpected end of expression/'),\n  toEvalTest('const { ...rest, value } = obj;', '/Rest element must be last element/'),\n  toEvalTest(\n    'function fn(...[rest], last) { return last; }',\n    '/Rest parameter must be last formal parameter/',\n  ),\n  toEvalTest('yield 1;', '/Unexpected token/'),\n  toEvalTest('function* gen(){ yield* ; }', '/Unexpected end of expression/'),\n  toEvalTest('async function fn(){ await ; }', '/Unexpected end of expression/'),\n  toEvalTest('async function* gen(){ yield* ; }', '/Unexpected end of expression/'),\n  toEvalTest('switch (x) { case: break; }', '/Unexpected end of expression/'),\n  toEvalTest('switch (x) { case 1 break; }', '/switch|case|Unexpected/'),\n  toEvalTest('1++;', '/Invalid left-hand side expression in postfix operation/'),\n  toEvalTest('--1;', '/left-hand side|Unexpected/'),\n  toEvalTest('async function run() { for await (const key in obj) {} }', \"/Unexpected token 'in'/\"),\n  toEvalTest(\n    'async function run(){ for await (const item of) {} }',\n    '/Unexpected end of expression/',\n  ),\n  toEvalTest('const re = /(/;', '/Invalid regular expression/'),\n  toEvalTest('const re = /a/zz;', '/Invalid flags/'),\n  toEvalTest('tag`${}`;', '/Unexpected end of expression/'),\n  toEvalTest('tag`${value`;', '/Unclosed/'),\n  toEvalTest('({a:1,,b:2})', '/Unexpected token ,/'),\n];\n"
  },
  {
    "path": "test/eval/testCases/syntax-errors.spec.ts",
    "content": "'use strict';\n\nimport { run, getState } from './test-utils.js';\nimport { tests } from './syntax-errors.data.js';\n\ndescribe('Eval Syntax Errors Tests', () => {\n  describe('Sync', () => {\n    tests.forEach((test) => {\n      let state = getState();\n      it(test.code.substring(0, 100), async () => {\n        await run(test, state, false);\n      });\n    });\n  });\n\n  describe('Async', () => {\n    tests.forEach((test) => {\n      let state = getState();\n      it(test.code.substring(0, 100), async () => {\n        await run(test, state, true);\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/eval/testCases/template-literals.data.ts",
    "content": "'use strict';\nimport { TestCase } from './types.js';\n\nexport const tests: TestCase[] = [\n  {\n    code: 'const a = 1; const b = 2; return `${a} + ${b} = ${a+b}`',\n    evalExpect: '1 + 2 = 3',\n    safeExpect: '1 + 2 = 3',\n    category: 'Template Literals',\n  },\n  {\n    code: \"const name = 'world'; return `hello ${name}`\",\n    evalExpect: 'hello world',\n    safeExpect: 'hello world',\n    category: 'Template Literals',\n  },\n  {\n    code: 'function tag(strings, ...values) { return strings[0] + values[0] + strings[1]; } return tag`hello ${\"world\"}`',\n    evalExpect: 'hello world',\n    safeExpect: 'hello world',\n    category: 'Template Literals',\n  },\n  {\n    code: 'function tag(strings, ...values) { return values.reduce((acc, val, i) => acc + val + strings[i + 1], strings[0]); } return tag`a${1}b${2}c${3}d`',\n    evalExpect: 'a1b2c3d',\n    safeExpect: 'a1b2c3d',\n    category: 'Template Literals',\n  },\n  {\n    code: 'const tagging = () => tag; function tag(strings, ...values) { return values.reduce((acc, val, i) => acc + val + strings[i + 1], strings[0]); } return tagging()`a${1}b${2}c${3}d`',\n    evalExpect: 'a1b2c3d',\n    safeExpect: 'a1b2c3d',\n    category: 'Template Literals',\n  },\n  {\n    code: 'function tag(strings) { return strings[0]; } return tag`static template`',\n    evalExpect: 'static template',\n    safeExpect: 'static template',\n    category: 'Template Literals',\n  },\n  {\n    code: 'const tag = (strings, ...values) => strings.length; return tag`test`',\n    evalExpect: 1,\n    safeExpect: 1,\n    category: 'Template Literals',\n  },\n  {\n    code: 'const multiply = (strings, a, b) => a * b; return multiply`${2} * ${3}`',\n    evalExpect: 6,\n    safeExpect: 6,\n    category: 'Template Literals',\n  },\n  {\n    code: 'const tag = (s, ...v) => v.length; return tag`${1}${2}${3}`',\n    evalExpect: 3,\n    safeExpect: 3,\n    category: 'Template Literals',\n  },\n  {\n    code: 'const obj = { tag(s, v) { return v * 2; } }; return obj.tag`${5}`',\n    evalExpect: 10,\n    safeExpect: 10,\n    category: 'Template Literals',\n  },\n  {\n    code: 'const tag = s => s.join(\"-\"); return tag`a${\"b\"}c`',\n    evalExpect: 'a-c',\n    safeExpect: 'a-c',\n    category: 'Template Literals',\n  },\n  {\n    code: 'const inner = \"world\"; return `hello ${`${inner.toUpperCase()}!`}`',\n    evalExpect: 'hello WORLD!',\n    safeExpect: 'hello WORLD!',\n    category: 'Template Literals',\n  },\n  {\n    code: 'const values = [1, 2]; return `${values.map(v => `${v * 2}`).join(\",\")}`',\n    evalExpect: '2,4',\n    safeExpect: '2,4',\n    category: 'Template Literals',\n  },\n];\n"
  },
  {
    "path": "test/eval/testCases/template-literals.spec.ts",
    "content": "'use strict';\nimport { run, getState } from './test-utils.js';\nimport { tests } from './template-literals.data.js';\n\ndescribe('Template Literals Tests', () => {\n  describe('Sync', () => {\n    tests.forEach((test) => {\n      let state = getState();\n      it(test.code.substring(0, 100), async () => {\n        await run(test, state, false);\n      });\n    });\n  });\n\n  describe('Async', () => {\n    tests.forEach((test) => {\n      let state = getState();\n      it(test.code.substring(0, 100), async () => {\n        await run(test, state, true);\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/eval/testCases/test-utils.ts",
    "content": "'use strict';\nimport Sandbox, { LocalScope } from '../../../src/Sandbox.js';\nimport { TestCase } from './types.js';\n\ndeclare global {\n  var bypassed: boolean;\n}\n\nclass TestError {\n  constructor(public error: Error | null | undefined) {}\n}\n\nexport async function run(test: TestCase, state: any, isAsync: boolean) {\n  globalThis.bypassed = false;\n  let ret;\n  const sandbox = new Sandbox();\n  try {\n    const c = `${test.code.includes(';') || test.code.startsWith('throw') ? '' : 'return '}${test.code}`;\n    let fn = isAsync ? sandbox.compileAsync(c, true) : sandbox.compile(c, true);\n    ret = await fn(state, new LocalScope()).run();\n  } catch (e: any) {\n    ret = new TestError(e);\n  }\n  let res;\n\n  try {\n    res = await ret;\n  } catch (e: any) {\n    res = new TestError(e);\n  }\n\n  expect(globalThis.bypassed).toBe(false);\n\n  if (test.safeExpect === 'error') {\n    expect(res).toBeInstanceOf(TestError);\n  } else if (typeof test.safeExpect === 'string' && test.safeExpect.startsWith('/')) {\n    const regex = new RegExp(test.safeExpect.slice(1, -1));\n    expect(res).toBeInstanceOf(TestError);\n    expect((res as TestError).error?.message).toMatch(regex);\n  } else if (test.safeExpect === 'NaN') {\n    expect(res).toBeNaN();\n  } else {\n    expect(res).toEqual(test.safeExpect);\n  }\n}\n\nexport function getState() {\n  const a = {\n    type: 'Sandbox',\n    test: [\n      (a: any, b: any) => {\n        return 1;\n      },\n    ],\n    test2: 1,\n    a: { b: { c: 2 } },\n  };\n\n  Object.setPrototypeOf(a, LocalScope.prototype);\n\n  return a;\n}\n"
  },
  {
    "path": "test/eval/testCases/types.ts",
    "content": "export interface TestCase {\n  code: string;\n  evalExpect?: unknown;\n  safeExpect?: unknown;\n  category: string;\n  ignoreTime?: boolean;\n}\n"
  },
  {
    "path": "test/eval/tests.json",
    "content": "[\n  {\n    \"code\": \"`${type}`\",\n    \"evalExpect\": \"eval\",\n    \"safeExpect\": \"Sandbox\",\n    \"category\": \"Data Types\"\n  },\n  {\n    \"code\": \"test2\",\n    \"evalExpect\": 1,\n    \"safeExpect\": 1,\n    \"category\": \"Data Types\"\n  },\n  {\n    \"code\": \"2.2204460492503130808472633361816E-16\",\n    \"evalExpect\": 2.220446049250313e-16,\n    \"safeExpect\": 2.220446049250313e-16,\n    \"category\": \"Data Types\"\n  },\n  {\n    \"code\": \"\\\"test2\\\"\",\n    \"evalExpect\": \"test2\",\n    \"safeExpect\": \"test2\",\n    \"category\": \"Data Types\"\n  },\n  {\n    \"code\": \"`test2 is ${`also ${test2}`}`\",\n    \"evalExpect\": \"test2 is also 1\",\n    \"safeExpect\": \"test2 is also 1\",\n    \"category\": \"Data Types\"\n  },\n  {\n    \"code\": \"\\\"\\\\\\\\\\\"\",\n    \"evalExpect\": \"\\\\\",\n    \"safeExpect\": \"\\\\\",\n    \"category\": \"Data Types\"\n  },\n  {\n    \"code\": \"`\\\\\\\\$$\\\\${${`\\\\\\\\\\\\`${'ok'}`}\\\\\\\\}`\",\n    \"evalExpect\": \"\\\\$$${\\\\`ok\\\\}\",\n    \"safeExpect\": \"\\\\$$${\\\\`ok\\\\}\",\n    \"category\": \"Data Types\"\n  },\n  {\n    \"code\": \"[\\\"\\\\\\\\\\\", \\\"\\\\xd9\\\", \\\"\\\\n\\\", \\\"\\\\r\\\", \\\"\\\\u2028\\\", \\\"\\\\u2029\\\"]\",\n    \"evalExpect\": [\"\\\\\", \"Ù\", \"\\n\", \"\\r\", \" \", \" \"],\n    \"safeExpect\": [\"\\\\\", \"Ù\", \"\\n\", \"\\r\", \" \", \" \"],\n    \"category\": \"Data Types\"\n  },\n  {\n    \"code\": \"/a/.test('a')\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Data Types\"\n  },\n  {\n    \"code\": \"/a/i.test('A')\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Data Types\"\n  },\n  {\n    \"code\": \"let reg = /a/g; reg.exec('aaa'); return reg.exec('aaa').index\",\n    \"evalExpect\": 1,\n    \"safeExpect\": 1,\n    \"category\": \"Data Types\"\n  },\n  {\n    \"code\": \"(1n + 0x1n).toString()\",\n    \"evalExpect\": \"2\",\n    \"safeExpect\": \"2\",\n    \"category\": \"Data Types\"\n  },\n  {\n    \"code\": \"0b1010\",\n    \"evalExpect\": 10,\n    \"safeExpect\": 10,\n    \"category\": \"Data Types\"\n  },\n  {\n    \"code\": \"0B1111\",\n    \"evalExpect\": 15,\n    \"safeExpect\": 15,\n    \"category\": \"Data Types\"\n  },\n  {\n    \"code\": \"0b1010n.toString()\",\n    \"evalExpect\": \"10\",\n    \"safeExpect\": \"10\",\n    \"category\": \"Data Types\"\n  },\n  {\n    \"code\": \"0b1_000\",\n    \"evalExpect\": 8,\n    \"safeExpect\": 8,\n    \"category\": \"Data Types\"\n  },\n  {\n    \"code\": \"1_000\",\n    \"evalExpect\": 1000,\n    \"safeExpect\": 1000,\n    \"category\": \"Data Types\"\n  },\n  {\n    \"code\": \"0b0\",\n    \"evalExpect\": 0,\n    \"safeExpect\": 0,\n    \"category\": \"Data Types\"\n  },\n  {\n    \"code\": \"0o17\",\n    \"evalExpect\": 15,\n    \"safeExpect\": 15,\n    \"category\": \"Data Types\"\n  },\n  {\n    \"code\": \"0O77\",\n    \"evalExpect\": 63,\n    \"safeExpect\": 63,\n    \"category\": \"Data Types\"\n  },\n  {\n    \"code\": \"0o17n.toString()\",\n    \"evalExpect\": \"15\",\n    \"safeExpect\": \"15\",\n    \"category\": \"Data Types\"\n  },\n  {\n    \"code\": \"0o7_777\",\n    \"evalExpect\": 4095,\n    \"safeExpect\": 4095,\n    \"category\": \"Data Types\"\n  },\n  {\n    \"code\": \"0o0\",\n    \"evalExpect\": 0,\n    \"safeExpect\": 0,\n    \"category\": \"Data Types\"\n  },\n  {\n    \"code\": \"0b1010 + 0o17\",\n    \"evalExpect\": 25,\n    \"safeExpect\": 25,\n    \"category\": \"Data Types\"\n  },\n  {\n    \"code\": \"(0b1010n + 0o17n).toString()\",\n    \"evalExpect\": \"25\",\n    \"safeExpect\": \"25\",\n    \"category\": \"Data Types\"\n  },\n  {\n    \"code\": \"String(BigInt('12345'))\",\n    \"evalExpect\": \"12345\",\n    \"safeExpect\": \"12345\",\n    \"category\": \"Data Types\"\n  },\n  {\n    \"code\": \"(123_456_789n).toString()\",\n    \"evalExpect\": \"123456789\",\n    \"safeExpect\": \"123456789\",\n    \"category\": \"Data Types\"\n  },\n  {\n    \"code\": \"/test/gi.test('test')\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Data Types\"\n  },\n  {\n    \"code\": \"/[a-z]+/i.test('Hello')\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Data Types\"\n  },\n  {\n    \"code\": \"NaN\",\n    \"evalExpect\": \"NaN\",\n    \"safeExpect\": \"NaN\",\n    \"category\": \"Data Types\"\n  },\n  {\n    \"code\": \"typeof Infinity === 'number'\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Data Types\"\n  },\n  {\n    \"code\": \"typeof undefined === 'undefined'\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Data Types\"\n  },\n  {\n    \"code\": \"null\",\n    \"evalExpect\": null,\n    \"safeExpect\": null,\n    \"category\": \"Data Types\"\n  },\n  {\n    \"code\": \"true\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Data Types\"\n  },\n  {\n    \"code\": \"false\",\n    \"evalExpect\": false,\n    \"safeExpect\": false,\n    \"category\": \"Data Types\"\n  },\n  {\n    \"code\": \"undefined === undefined\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Data Types\"\n  },\n  {\n    \"code\": \"globalThis.constructor.name\",\n    \"evalExpect\": \"Window\",\n    \"safeExpect\": \"SandboxGlobal\",\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"this.constructor.name\",\n    \"evalExpect\": \"Window\",\n    \"safeExpect\": \"SandboxGlobal\",\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"eval.name\",\n    \"evalExpect\": \"eval\",\n    \"safeExpect\": \"sandboxEval\",\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"return eval(\\\"1+1; 2+2;\\\")\",\n    \"evalExpect\": 4,\n    \"safeExpect\": 4,\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"Function.name\",\n    \"evalExpect\": \"Function\",\n    \"safeExpect\": \"SandboxFunction\",\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"bypassed=1\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/bypassed is not defined/\",\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"`${`${bypassed=1}`}`\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/bypassed is not defined/\",\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"[].filter.constructor(\\\"return 'ok'\\\")()\",\n    \"evalExpect\": \"ok\",\n    \"safeExpect\": \"ok\",\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"[].filter.constructor('return bypassed')()\",\n    \"evalExpect\": false,\n    \"safeExpect\": \"/bypassed is not defined/\",\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"[].filter.constructor('return bypassed=1')()\",\n    \"evalExpect\": \"bypassed\",\n    \"safeExpect\": \"/bypassed is not defined/\",\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"[].filter.constructor.apply(null,['return bypassed=1'])()\",\n    \"evalExpect\": \"bypassed\",\n    \"safeExpect\": \"/bypassed is not defined/\",\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"[].filter.constructor('return this.bypassed=1')()\",\n    \"evalExpect\": \"bypassed\",\n    \"safeExpect\": 1,\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"[].filter.constructor('return this.constructor.name')()\",\n    \"evalExpect\": \"Window\",\n    \"safeExpect\": \"SandboxGlobal\",\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"[].filter.constructor.call(this,'bypassed = 1')()\",\n    \"evalExpect\": \"bypassed\",\n    \"safeExpect\": \"/bypassed is not defined/\",\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"[].filter.constructor('return eval(\\\"bypassed = 1\\\")')()\",\n    \"evalExpect\": \"bypassed\",\n    \"safeExpect\": \"/bypassed is not defined/\",\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"[+!+[]]+[]\",\n    \"evalExpect\": \"1\",\n    \"safeExpect\": \"1\",\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"[][(![]+[])[+!+[]]+(!![]+[])[+[]]][([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]](([][(!![]+[])[!+[]+!+[]+!+[]]+([][[]]+[])[+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(!![]+[])[!+[]+!+[]+!+[]]+(![]+[])[!+[]+!+[]+!+[]]]()+[])[!+[]+!+[]]+(+[![]]+[+(+!+[]+(!+[]+[])[!+[]+!+[]+!+[]]+[+!+[]]+[+[]]+[+[]]+[+[]])])[+!+[]+[+[]]]+(+(!+[]+!+[]+[+!+[]]+[+!+[]]))[(!![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([]+[])[([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]][([][[]]+[])[+!+[]]+(![]+[])[+!+[]]+((+[])[([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[+!+[]+[+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]]](!+[]+!+[]+!+[]+[+!+[]])[+!+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+([][[]]+[])[!+[]+!+[]]+([]+[])[(![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(!![]+[])[+[]]+([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]()[+!+[]+[+!+[]]]+[+!+[]])()\",\n    \"evalExpect\": \"bypassed\",\n    \"safeExpect\": \"/bypassed is not defined/\",\n    \"ignoreTime\": true,\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"Object.entries(this).find(a => a[0] === \\\"Function\\\")?.at(1)(\\\"bypassed=1\\\")()\",\n    \"evalExpect\": \"bypassed\",\n    \"safeExpect\": \"/bypassed is not defined/\",\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"Object.values(this).find(a => {if(a?.name?.endsWith(\\\"Function\\\")) return a.call(\\\"\\\", \\\"return bypassed = 1\\\")()})\",\n    \"evalExpect\": \"bypassed\",\n    \"safeExpect\": \"/bypassed is not defined/\",\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"(async () => (await (async () => Function('return Object.values(this).at(Object.values(this).findIndex(a => {if(a?.name?.endsWith(\\\"Function\\\")) return true}))')())()).name)()\",\n    \"evalExpect\": \"Function\",\n    \"safeExpect\": \"SandboxFunction\",\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"[].constructor.constructor.constructor.name\",\n    \"evalExpect\": \"Function\",\n    \"safeExpect\": \"SandboxFunction\",\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"(()=>{}).constructor.name\",\n    \"evalExpect\": \"Function\",\n    \"safeExpect\": \"SandboxFunction\",\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"(async ()=>{}).constructor.name\",\n    \"evalExpect\": \"AsyncFunction\",\n    \"safeExpect\": \"SandboxAsyncFunction\",\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"(function*(){}).constructor.name\",\n    \"evalExpect\": \"GeneratorFunction\",\n    \"safeExpect\": \"SandboxGeneratorFunction\",\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"(async function*(){}).constructor.name\",\n    \"evalExpect\": \"AsyncGeneratorFunction\",\n    \"safeExpect\": \"SandboxAsyncGeneratorFunction\",\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"(async ()=>{}).constructor('return this')().then((a) => a.constructor.name)\",\n    \"evalExpect\": \"Window\",\n    \"safeExpect\": \"SandboxGlobal\",\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"[].anything = 1\",\n    \"evalExpect\": 1,\n    \"safeExpect\": 1,\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"[].filter = 1\",\n    \"evalExpect\": 1,\n    \"safeExpect\": \"/Override prototype property 'filter' not allowed/\",\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"[].constructor.prototype.flatMap = 1\",\n    \"evalExpect\": 1,\n    \"safeExpect\": \"/Access to prototype of global object is not permitted/\",\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"[].__proto__.flatMap = 1\",\n    \"evalExpect\": 1,\n    \"safeExpect\": \"/Access to prototype of global object is not permitted/\",\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"let p; p = [].constructor.prototype; p.flatMap = 2; return [].flatMap;\",\n    \"evalExpect\": 2,\n    \"safeExpect\": \"/Access to prototype of global object is not permitted/\",\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"Object.anything = 1\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Cannot assign property 'anything' of a global object/\",\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"Object.freeze = 1\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Static method or property access not permitted/\",\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"{}.constructor.anything1 = 1\",\n    \"evalExpect\": 1,\n    \"safeExpect\": \"/Cannot assign property 'anything1' of a global object/\",\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"{}.constructor.freeze = 1\",\n    \"evalExpect\": 1,\n    \"safeExpect\": \"/Static method or property access not permitted/\",\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"{}.constructor.constructor.anything = 1\",\n    \"evalExpect\": 1,\n    \"safeExpect\": \"/Cannot assign property 'anything' of a global object/\",\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"(() => {}).anything = 1\",\n    \"evalExpect\": 1,\n    \"safeExpect\": 1,\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"Object.name\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"Object\",\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"Object.assign(Object, {}) || 'ok'\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Static method or property access not permitted/\",\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"({}).__defineGetter__('a', () => 1 ) || 'ok'\",\n    \"evalExpect\": \"ok\",\n    \"safeExpect\": \"/Method or property access not permitted/\",\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"(() => {}).__defineGetter__('a', () => 1 ) || 'ok'\",\n    \"evalExpect\": \"ok\",\n    \"safeExpect\": \"/Method or property access not permitted/\",\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"({}).toString.__defineGetter__('a', () => 1 ) || 'ok'\",\n    \"evalExpect\": \"ok\",\n    \"safeExpect\": \"/Method or property access not permitted/\",\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"const arr=[Array.prototype]; arr[0].polluted = 1; return [].polluted;\",\n    \"evalExpect\": 1,\n    \"safeExpect\": \"/Access to prototype of global object is not permitted/\",\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"(async () => Array.prototype)().then((a) => a.polluted = 'pwned').then(() => [].polluted)\",\n    \"evalExpect\": \"ok\",\n    \"safeExpect\": \"/Access to prototype of global object is not permitted/\",\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"function x() {}; x.prototype.permitted = true; return new x().permitted;\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"function x() {}; const y = new x(); y.__proto__.permitted = true; return y.permitted;\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"function x() {}; const y = new x(); y.__proto__.__proto__.forbidden = true; return y.forbidden;\",\n    \"evalExpect\": true,\n    \"safeExpect\": \"/Access to prototype of global object is not permitted/\",\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"hasOwnProperty = 1; return ({}).hasOwnProperty\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/hasOwnProperty is not defined/\",\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"Object.values(this).includes(Function)\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"Object.values(this).at(1)('bypassed=1')()\",\n    \"evalExpect\": \"bypassed\",\n    \"safeExpect\": \"/bypassed is not defined/\",\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"\\nconst p = (async function () {})();\\n({\\n  \\\"finally\\\": p.finally,\\n  ...Object.fromEntries([['then', ...Object.values(this).slice(1)]]),\\n}).finally('bypassed=1')();\\n\",\n    \"evalExpect\": \"bypassed\",\n    \"safeExpect\": \"/bypassed is not defined/\",\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"const a = Function; a.anything = 1; return a.anything;\",\n    \"evalExpect\": 1,\n    \"safeExpect\": \"/Cannot assign property 'anything' of a global object/\",\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"this.Function = 1\",\n    \"evalExpect\": 1,\n    \"safeExpect\": \"/Cannot assign property 'Function' of a global object/\",\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"\\nconst callOp = (function fn() { return fn.caller; })();\\n\\nfunction makeContext(capture = () => {}) {\\n  return { ctx: { options: 0 }, evals: { get: capture } };\\n}\\n\\nfunction leakStatic(obj, prop) {\\n  let leaked;\\n  callOp({\\n    done() {},\\n    a() {},\\n    b: [],\\n    obj: { context: obj, prop, get() {} },\\n    context: makeContext((fn) => (leaked = fn, () => 1))\\n  });\\n  return leaked;\\n}\\n\\nfunction callDirect(fn, args) {\\n  let value;\\n  callOp({\\n    done(_, result) { value = result; },\\n    a() {},\\n    b: args,\\n    obj: fn,\\n    context: makeContext()\\n  });\\n  return value;\\n}\\n\\ncallDirect(leakStatic(Object, 'defineProperty'), [\\n  leakStatic,\\n  'call',\\n  callDirect(leakStatic(Object, 'getOwnPropertyDescriptor'), [\\n    callDirect(leakStatic(Object, 'getPrototypeOf'), [() => 0]),\\n    'constructor'\\n  ])\\n]);\\n\\nlet hostFn;\\ncallOp({\\n  done(_, result) { hostFn = result; },\\n  a: leakStatic,\\n  b: [],\\n  obj: {\\n    context: 'return globalThis.bypassed = 1',\\n    get() {}\\n  },\\n  context: makeContext()\\n});\\n\\nreturn hostFn();\\n\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Access to 'caller' property is not permitted/\",\n    \"category\": \"Security\"\n  },\n  {\n    \"code\": \"1+1\",\n    \"evalExpect\": 2,\n    \"safeExpect\": 2,\n    \"category\": \"Arithmetic Operators\"\n  },\n  {\n    \"code\": \"1 * 2 + 3 * (4 + 5) * 6\",\n    \"evalExpect\": 164,\n    \"safeExpect\": 164,\n    \"category\": \"Arithmetic Operators\"\n  },\n  {\n    \"code\": \"(test2 * (2 + 3 * (4 + 5))) * 6\",\n    \"evalExpect\": 174,\n    \"safeExpect\": 174,\n    \"category\": \"Arithmetic Operators\"\n  },\n  {\n    \"code\": \"1+2*4/5-6+7/8 % 9+10-11-12/13*14\",\n    \"evalExpect\": -16.448076923076925,\n    \"safeExpect\": -16.448076923076925,\n    \"category\": \"Arithmetic Operators\"\n  },\n  {\n    \"code\": \"test2 **= 0\",\n    \"evalExpect\": 1,\n    \"safeExpect\": 1,\n    \"category\": \"Arithmetic Operators\"\n  },\n  {\n    \"code\": \"2 ** 3\",\n    \"evalExpect\": 8,\n    \"safeExpect\": 8,\n    \"category\": \"Arithmetic Operators\"\n  },\n  {\n    \"code\": \"10 ** 0\",\n    \"evalExpect\": 1,\n    \"safeExpect\": 1,\n    \"category\": \"Arithmetic Operators\"\n  },\n  {\n    \"code\": \"2 ** 10\",\n    \"evalExpect\": 1024,\n    \"safeExpect\": 1024,\n    \"category\": \"Arithmetic Operators\"\n  },\n  {\n    \"code\": \"3 ** 2 ** 2\",\n    \"evalExpect\": 81,\n    \"safeExpect\": 81,\n    \"category\": \"Arithmetic Operators\"\n  },\n  {\n    \"code\": \"test2 %= 1\",\n    \"evalExpect\": 0,\n    \"safeExpect\": 0,\n    \"category\": \"Arithmetic Operators\"\n  },\n  {\n    \"code\": \"+'1'\",\n    \"evalExpect\": 1,\n    \"safeExpect\": 1,\n    \"category\": \"Arithmetic Operators\"\n  },\n  {\n    \"code\": \"-'1'\",\n    \"evalExpect\": -1,\n    \"safeExpect\": -1,\n    \"category\": \"Arithmetic Operators\"\n  },\n  {\n    \"code\": \"var i = 1; return i + 1\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": 2,\n    \"category\": \"Arithmetic Operators\"\n  },\n  {\n    \"code\": \"let j = 1; return j + 1\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": 2,\n    \"category\": \"Arithmetic Operators\"\n  },\n  {\n    \"code\": \"const k = 1; return k + 1\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": 2,\n    \"category\": \"Arithmetic Operators\"\n  },\n  {\n    \"code\": \"null?.a + 5\",\n    \"evalExpect\": \"NaN\",\n    \"safeExpect\": \"NaN\",\n    \"category\": \"Arithmetic Operators\"\n  },\n  {\n    \"code\": \"({}).a ?? 10 + 5\",\n    \"evalExpect\": 15,\n    \"safeExpect\": 15,\n    \"category\": \"Arithmetic Operators\"\n  },\n  {\n    \"code\": \"let x = 5; return x++ + 2\",\n    \"evalExpect\": 7,\n    \"safeExpect\": 7,\n    \"category\": \"Arithmetic Operators\"\n  },\n  {\n    \"code\": \"let y = 5; return ++y + 2\",\n    \"evalExpect\": 8,\n    \"safeExpect\": 8,\n    \"category\": \"Arithmetic Operators\"\n  },\n  {\n    \"code\": \"+-5\",\n    \"evalExpect\": -5,\n    \"safeExpect\": -5,\n    \"category\": \"Arithmetic Operators\"\n  },\n  {\n    \"code\": \"~-1\",\n    \"evalExpect\": 0,\n    \"safeExpect\": 0,\n    \"category\": \"Arithmetic Operators\"\n  },\n  {\n    \"code\": \"(1, 2) + (3, 4)\",\n    \"evalExpect\": 6,\n    \"safeExpect\": 6,\n    \"category\": \"Arithmetic Operators\"\n  },\n  {\n    \"code\": \"let z = 5; return --z * 2\",\n    \"evalExpect\": 8,\n    \"safeExpect\": 8,\n    \"category\": \"Arithmetic Operators\"\n  },\n  {\n    \"code\": \"let z = 5; return z-- * 2\",\n    \"evalExpect\": 10,\n    \"safeExpect\": 10,\n    \"category\": \"Arithmetic Operators\"\n  },\n  {\n    \"code\": \"1 + 2 + 3 + 4\",\n    \"evalExpect\": 10,\n    \"safeExpect\": 10,\n    \"category\": \"Arithmetic Operators\"\n  },\n  {\n    \"code\": \"10 - 5 - 2\",\n    \"evalExpect\": 3,\n    \"safeExpect\": 3,\n    \"category\": \"Arithmetic Operators\"\n  },\n  {\n    \"code\": \"2 * 3 * 4\",\n    \"evalExpect\": 24,\n    \"safeExpect\": 24,\n    \"category\": \"Arithmetic Operators\"\n  },\n  {\n    \"code\": \"100 / 5 / 2\",\n    \"evalExpect\": 10,\n    \"safeExpect\": 10,\n    \"category\": \"Arithmetic Operators\"\n  },\n  {\n    \"code\": \"17 % 5 % 2\",\n    \"evalExpect\": 0,\n    \"safeExpect\": 0,\n    \"category\": \"Arithmetic Operators\"\n  },\n  {\n    \"code\": \"let a = 1, b = 2, c = 3; return a + b + c\",\n    \"evalExpect\": 6,\n    \"safeExpect\": 6,\n    \"category\": \"Arithmetic Operators\"\n  },\n  {\n    \"code\": \"const a = 1, b = 2; return a * b\",\n    \"evalExpect\": 2,\n    \"safeExpect\": 2,\n    \"category\": \"Arithmetic Operators\"\n  },\n  {\n    \"code\": \"(false ? 1 : 2) + (true ? 3 : 4)\",\n    \"evalExpect\": 5,\n    \"safeExpect\": 5,\n    \"category\": \"Arithmetic Operators\"\n  },\n  {\n    \"code\": \"let x = 5; x++; return x\",\n    \"evalExpect\": 6,\n    \"safeExpect\": 6,\n    \"category\": \"Arithmetic Operators\"\n  },\n  {\n    \"code\": \"let x = 5; return ++x\",\n    \"evalExpect\": 6,\n    \"safeExpect\": 6,\n    \"category\": \"Arithmetic Operators\"\n  },\n  {\n    \"code\": \"let x = 5; x--; return x\",\n    \"evalExpect\": 4,\n    \"safeExpect\": 4,\n    \"category\": \"Arithmetic Operators\"\n  },\n  {\n    \"code\": \"let x = 5; return --x\",\n    \"evalExpect\": 4,\n    \"safeExpect\": 4,\n    \"category\": \"Arithmetic Operators\"\n  },\n  {\n    \"code\": \"test.b = test2 - test[0]() - test[0]()\",\n    \"evalExpect\": -1,\n    \"safeExpect\": -1,\n    \"category\": \"Assignment Operators\"\n  },\n  {\n    \"code\": \"test2++\",\n    \"evalExpect\": 1,\n    \"safeExpect\": 1,\n    \"category\": \"Assignment Operators\"\n  },\n  {\n    \"code\": \"++test2\",\n    \"evalExpect\": 2,\n    \"safeExpect\": 2,\n    \"category\": \"Assignment Operators\"\n  },\n  {\n    \"code\": \"test2 = 1\",\n    \"evalExpect\": 1,\n    \"safeExpect\": 1,\n    \"category\": \"Assignment Operators\"\n  },\n  {\n    \"code\": \"test2 += 1\",\n    \"evalExpect\": 2,\n    \"safeExpect\": 2,\n    \"category\": \"Assignment Operators\"\n  },\n  {\n    \"code\": \"test2 -= 1\",\n    \"evalExpect\": 0,\n    \"safeExpect\": 0,\n    \"category\": \"Assignment Operators\"\n  },\n  {\n    \"code\": \"test2 *= 2\",\n    \"evalExpect\": 2,\n    \"safeExpect\": 2,\n    \"category\": \"Assignment Operators\"\n  },\n  {\n    \"code\": \"test2 /= 2\",\n    \"evalExpect\": 0.5,\n    \"safeExpect\": 0.5,\n    \"category\": \"Assignment Operators\"\n  },\n  {\n    \"code\": \"let a = 1; let a = 2; return a\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Identifier 'a' has already been declared/\",\n    \"category\": \"Assignment Operators\"\n  },\n  {\n    \"code\": \"var z = 1; var z = 2; return z\",\n    \"evalExpect\": 2,\n    \"safeExpect\": 2,\n    \"category\": \"Assignment Operators\"\n  },\n  {\n    \"code\": \"let y = 1; var y = 2; return y\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Identifier 'y' has already been declared/\",\n    \"category\": \"Assignment Operators\"\n  },\n  {\n    \"code\": \"var z = globalThis; let y = globalThis; return z.constructor.name === 'SandboxGlobal' && z === y\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Assignment Operators\"\n  },\n  {\n    \"code\": \"var v = 1;  var v = Object; return v.name\",\n    \"evalExpect\": \"Object\",\n    \"safeExpect\": \"Object\",\n    \"category\": \"Assignment Operators\"\n  },\n  {\n    \"code\": \"let a = 1; {let a = 2}; return a\",\n    \"evalExpect\": 1,\n    \"safeExpect\": 1,\n    \"category\": \"Assignment Operators\"\n  },\n  {\n    \"code\": \"this = 1\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/\\\"this\\\" cannot be assigned/\",\n    \"category\": \"Assignment Operators\"\n  },\n  {\n    \"code\": \"const l = 1; return l = 2\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Assignment to constant variable/\",\n    \"category\": \"Assignment Operators\"\n  },\n  {\n    \"code\": \"delete 1\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Assignment Operators\"\n  },\n  {\n    \"code\": \"let a = {b: 1}; return delete a.b\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Assignment Operators\"\n  },\n  {\n    \"code\": \"let b = {a: 1}; return delete b\",\n    \"evalExpect\": false,\n    \"safeExpect\": false,\n    \"category\": \"Assignment Operators\"\n  },\n  {\n    \"code\": \"let x = 5; x <<= 1; return x\",\n    \"evalExpect\": 10,\n    \"safeExpect\": 10,\n    \"category\": \"Assignment Operators\"\n  },\n  {\n    \"code\": \"let x = 8; x >>= 1; return x\",\n    \"evalExpect\": 4,\n    \"safeExpect\": 4,\n    \"category\": \"Assignment Operators\"\n  },\n  {\n    \"code\": \"let obj = {a: 1}; delete obj.a; return obj.a === undefined\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Assignment Operators\"\n  },\n  {\n    \"code\": \"delete {a: 1}.a\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Assignment Operators\"\n  },\n  {\n    \"code\": \"let x = 10; x <<= 2; return x;\",\n    \"evalExpect\": 40,\n    \"safeExpect\": 40,\n    \"category\": \"Assignment Operators\"\n  },\n  {\n    \"code\": \"let x = 16; x >>= 2; return x;\",\n    \"evalExpect\": 4,\n    \"safeExpect\": 4,\n    \"category\": \"Assignment Operators\"\n  },\n  {\n    \"code\": \"(1 == 1) == true\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Assignment Operators\"\n  },\n  {\n    \"code\": \"let x = (1, 2, 3); return x\",\n    \"evalExpect\": 3,\n    \"safeExpect\": 3,\n    \"category\": \"Assignment Operators\"\n  },\n  {\n    \"code\": \"let x = 0; x = x || 5; return x\",\n    \"evalExpect\": 5,\n    \"safeExpect\": 5,\n    \"category\": \"Assignment Operators\"\n  },\n  {\n    \"code\": \"let x = 10; x = x && 5; return x\",\n    \"evalExpect\": 5,\n    \"safeExpect\": 5,\n    \"category\": \"Assignment Operators\"\n  },\n  {\n    \"code\": \"let x = 10; x &&= 5; return x\",\n    \"evalExpect\": 5,\n    \"safeExpect\": 5,\n    \"category\": \"Assignment Operators\"\n  },\n  {\n    \"code\": \"let x = 0; x &&= 5; return x\",\n    \"evalExpect\": 0,\n    \"safeExpect\": 0,\n    \"category\": \"Assignment Operators\"\n  },\n  {\n    \"code\": \"let x = false; x &&= true; return x\",\n    \"evalExpect\": false,\n    \"safeExpect\": false,\n    \"category\": \"Assignment Operators\"\n  },\n  {\n    \"code\": \"let x = 0; x ||= 5; return x\",\n    \"evalExpect\": 5,\n    \"safeExpect\": 5,\n    \"category\": \"Assignment Operators\"\n  },\n  {\n    \"code\": \"let x = 10; x ||= 5; return x\",\n    \"evalExpect\": 10,\n    \"safeExpect\": 10,\n    \"category\": \"Assignment Operators\"\n  },\n  {\n    \"code\": \"let x = false; x ||= \\\"default\\\"; return x\",\n    \"evalExpect\": \"default\",\n    \"safeExpect\": \"default\",\n    \"category\": \"Assignment Operators\"\n  },\n  {\n    \"code\": \"let x = null; x ??= 5; return x\",\n    \"evalExpect\": 5,\n    \"safeExpect\": 5,\n    \"category\": \"Assignment Operators\"\n  },\n  {\n    \"code\": \"let x = undefined; x ??= 10; return x\",\n    \"evalExpect\": 10,\n    \"safeExpect\": 10,\n    \"category\": \"Assignment Operators\"\n  },\n  {\n    \"code\": \"let x = 0; x ??= 5; return x\",\n    \"evalExpect\": 0,\n    \"safeExpect\": 0,\n    \"category\": \"Assignment Operators\"\n  },\n  {\n    \"code\": \"let x = \\\"\\\"; x ??= \\\"default\\\"; return x\",\n    \"evalExpect\": \"\",\n    \"safeExpect\": \"\",\n    \"category\": \"Assignment Operators\"\n  },\n  {\n    \"code\": \"~test2\",\n    \"evalExpect\": -2,\n    \"safeExpect\": -2,\n    \"category\": \"Bitwise Operators\"\n  },\n  {\n    \"code\": \"let test3 = 8; test3 >>>= 2; return test3\",\n    \"evalExpect\": 2,\n    \"safeExpect\": 2,\n    \"category\": \"Bitwise Operators\"\n  },\n  {\n    \"code\": \"let test4 = -8; test4 >>>= 2; return test4\",\n    \"evalExpect\": 1073741822,\n    \"safeExpect\": 1073741822,\n    \"category\": \"Bitwise Operators\"\n  },\n  {\n    \"code\": \"let test5 = 16; test5 >>>= 1; return test5\",\n    \"evalExpect\": 8,\n    \"safeExpect\": 8,\n    \"category\": \"Bitwise Operators\"\n  },\n  {\n    \"code\": \"let test6 = -1; test6 >>>= 0; return test6\",\n    \"evalExpect\": 4294967295,\n    \"safeExpect\": 4294967295,\n    \"category\": \"Bitwise Operators\"\n  },\n  {\n    \"code\": \"test2 ^= 1\",\n    \"evalExpect\": 0,\n    \"safeExpect\": 0,\n    \"category\": \"Bitwise Operators\"\n  },\n  {\n    \"code\": \"test2 &= 3\",\n    \"evalExpect\": 1,\n    \"safeExpect\": 1,\n    \"category\": \"Bitwise Operators\"\n  },\n  {\n    \"code\": \"test2 |= 2\",\n    \"evalExpect\": 3,\n    \"safeExpect\": 3,\n    \"category\": \"Bitwise Operators\"\n  },\n  {\n    \"code\": \"test2 & 1\",\n    \"evalExpect\": 1,\n    \"safeExpect\": 1,\n    \"category\": \"Bitwise Operators\"\n  },\n  {\n    \"code\": \"test2 | 4\",\n    \"evalExpect\": 5,\n    \"safeExpect\": 5,\n    \"category\": \"Bitwise Operators\"\n  },\n  {\n    \"code\": \"8 >> 1 >> 1\",\n    \"evalExpect\": 2,\n    \"safeExpect\": 2,\n    \"category\": \"Bitwise Operators\"\n  },\n  {\n    \"code\": \"1 << 2 << 1\",\n    \"evalExpect\": 8,\n    \"safeExpect\": 8,\n    \"category\": \"Bitwise Operators\"\n  },\n  {\n    \"code\": \"16 >>> 2 >> 1\",\n    \"evalExpect\": 2,\n    \"safeExpect\": 2,\n    \"category\": \"Bitwise Operators\"\n  },\n  {\n    \"code\": \"1 << 1 << 1\",\n    \"evalExpect\": 4,\n    \"safeExpect\": 4,\n    \"category\": \"Bitwise Operators\"\n  },\n  {\n    \"code\": \"16 >> 1 >> 1\",\n    \"evalExpect\": 4,\n    \"safeExpect\": 4,\n    \"category\": \"Bitwise Operators\"\n  },\n  {\n    \"code\": \"5 & 7 & 3\",\n    \"evalExpect\": 1,\n    \"safeExpect\": 1,\n    \"category\": \"Bitwise Operators\"\n  },\n  {\n    \"code\": \"1 | 2 | 4\",\n    \"evalExpect\": 7,\n    \"safeExpect\": 7,\n    \"category\": \"Bitwise Operators\"\n  },\n  {\n    \"code\": \"15 ^ 10 ^ 5\",\n    \"evalExpect\": 0,\n    \"safeExpect\": 0,\n    \"category\": \"Bitwise Operators\"\n  },\n  {\n    \"code\": \"~5\",\n    \"evalExpect\": -6,\n    \"safeExpect\": -6,\n    \"category\": \"Bitwise Operators\"\n  },\n  {\n    \"code\": \"/* 2 */ 1\",\n    \"evalExpect\": 1,\n    \"safeExpect\": 1,\n    \"category\": \"Comments\"\n  },\n  {\n    \"code\": \"1 // 2\",\n    \"evalExpect\": 1,\n    \"safeExpect\": 1,\n    \"category\": \"Comments\"\n  },\n  {\n    \"code\": \"/* 2 */ (() => /* 3 */ 1)() // 4\",\n    \"evalExpect\": 1,\n    \"safeExpect\": 1,\n    \"category\": \"Comments\"\n  },\n  {\n    \"code\": \"/* never closed\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unclosed comment '\\\\/\\\\*/\",\n    \"category\": \"Comments\"\n  },\n  {\n    \"code\": \"test2 == '1'\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Comparison Operators\"\n  },\n  {\n    \"code\": \"test2 === '1'\",\n    \"evalExpect\": false,\n    \"safeExpect\": false,\n    \"category\": \"Comparison Operators\"\n  },\n  {\n    \"code\": \"test2 != '1'\",\n    \"evalExpect\": false,\n    \"safeExpect\": false,\n    \"category\": \"Comparison Operators\"\n  },\n  {\n    \"code\": \"test2 !== '1'\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Comparison Operators\"\n  },\n  {\n    \"code\": \"test2 < 1\",\n    \"evalExpect\": false,\n    \"safeExpect\": false,\n    \"category\": \"Comparison Operators\"\n  },\n  {\n    \"code\": \"test2 > 1\",\n    \"evalExpect\": false,\n    \"safeExpect\": false,\n    \"category\": \"Comparison Operators\"\n  },\n  {\n    \"code\": \"test2 >= 1\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Comparison Operators\"\n  },\n  {\n    \"code\": \"test2 <= 1\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Comparison Operators\"\n  },\n  {\n    \"code\": \"let a = null; let b = [a?.a]; return b[0] === undefined && b.length\",\n    \"evalExpect\": 1,\n    \"safeExpect\": 1,\n    \"category\": \"Complex Expressions\"\n  },\n  {\n    \"code\": \"{a: 1, ...{b: 2, c: {d: test2,}}, e: 5}\",\n    \"evalExpect\": {\n      \"a\": 1,\n      \"b\": 2,\n      \"c\": {\n        \"d\": 1\n      },\n      \"e\": 5\n    },\n    \"safeExpect\": {\n      \"a\": 1,\n      \"b\": 2,\n      \"c\": {\n        \"d\": 1\n      },\n      \"e\": 5\n    },\n    \"category\": \"Complex Expressions\"\n  },\n  {\n    \"code\": \"{a: 1,b: 2, /*,*/}\",\n    \"evalExpect\": {\n      \"a\": 1,\n      \"b\": 2\n    },\n    \"safeExpect\": {\n      \"a\": 1,\n      \"b\": 2\n    },\n    \"category\": \"Complex Expressions\"\n  },\n  {\n    \"code\": \"test2 = 1,(() => 2)(),test2\",\n    \"evalExpect\": 1,\n    \"safeExpect\": 1,\n    \"category\": \"Complex Expressions\"\n  },\n  {\n    \"code\": \"const a = () => {return 1}; const b = () => {return 2}; return (() => a() + b())()\",\n    \"evalExpect\": 3,\n    \"safeExpect\": 3,\n    \"category\": \"Complex Expressions\"\n  },\n  {\n    \"code\": \"({}).a?.toString()\",\n    \"category\": \"Complex Expressions\"\n  },\n  {\n    \"code\": \"({}).a?.toSring() + ({}).b?.toString()\",\n    \"evalExpect\": \"NaN\",\n    \"safeExpect\": \"NaN\",\n    \"category\": \"Complex Expressions\"\n  },\n  {\n    \"code\": \"({})['b']?.toString() === undefined\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Complex Expressions\"\n  },\n  {\n    \"code\": \"({}).c?.()() ? 1 : 2\",\n    \"evalExpect\": 2,\n    \"safeExpect\": 2,\n    \"category\": \"Complex Expressions\"\n  },\n  {\n    \"code\": \"function plus(n,u){return n+u};function minus(n,u){return n-u};var added=plus(1,10);return minus(added,5);\",\n    \"evalExpect\": 6,\n    \"safeExpect\": 6,\n    \"category\": \"Complex Expressions\"\n  },\n  {\n    \"code\": \"function LinkedListNode(e){this.value=e,this.next=null}function reverse(e){let n,t,r=e;for(;r;)t=r.next,r.next=n,n=r,r=t;return n}function reverse(e){if(!e||!e.next)return e;let n=reverse(e.next);return e.next.next=e,e.next=null,n} let l1 = new LinkedListNode(1); l1.next = new LinkedListNode(2); return reverse(l1);\",\n    \"evalExpect\": {\n      \"value\": 2,\n      \"next\": {\n        \"value\": 1,\n        \"next\": null\n      }\n    },\n    \"safeExpect\": {\n      \"value\": 2,\n      \"next\": {\n        \"value\": 1,\n        \"next\": null\n      }\n    },\n    \"category\": \"Complex Expressions\"\n  },\n  {\n    \"code\": \"const f = function factorial(n) { return n <= 1 ? 1 : n * factorial(n - 1); }; return f(5)\",\n    \"evalExpect\": 120,\n    \"safeExpect\": 120,\n    \"category\": \"Complex Expressions\"\n  },\n  {\n    \"code\": \"({a: 10}).a?.toString() + 5\",\n    \"evalExpect\": \"105\",\n    \"safeExpect\": \"105\",\n    \"category\": \"Complex Expressions\"\n  },\n  {\n    \"code\": \"let arr = [1, 2, 3]; return arr?.[1]\",\n    \"evalExpect\": 2,\n    \"safeExpect\": 2,\n    \"category\": \"Complex Expressions\"\n  },\n  {\n    \"code\": \"let obj = {fn: () => 42}; return obj?.fn?.()\",\n    \"evalExpect\": 42,\n    \"safeExpect\": 42,\n    \"category\": \"Complex Expressions\"\n  },\n  {\n    \"code\": \"test[test2] ? true : false ? 'not ok' : 'ok'\",\n    \"evalExpect\": \"ok\",\n    \"safeExpect\": \"ok\",\n    \"category\": \"Conditionals\"\n  },\n  {\n    \"code\": \"let ok = true; false ? ok = false : ok; return ok\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Conditionals\"\n  },\n  {\n    \"code\": \"if (true) { return true; } else return false\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Conditionals\"\n  },\n  {\n    \"code\": \"let a = false; if (a) { return false; } else return true\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Conditionals\"\n  },\n  {\n    \"code\": \"let a = null; if (a?.a) { return false; } else return true\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Conditionals\"\n  },\n  {\n    \"code\": \"if (false) return true; else if (false) {return true} else return false\",\n    \"evalExpect\": false,\n    \"safeExpect\": false,\n    \"category\": \"Conditionals\"\n  },\n  {\n    \"code\": \"if (false) { return true; } else return false\",\n    \"evalExpect\": false,\n    \"safeExpect\": false,\n    \"category\": \"Conditionals\"\n  },\n  {\n    \"code\": \"if (false) return true; return false\",\n    \"evalExpect\": false,\n    \"safeExpect\": false,\n    \"category\": \"Conditionals\"\n  },\n  {\n    \"code\": \"if (true) {\\n  if (false)\\n    if (true)\\n      if (false)\\n        return 1\\n      else if (true)\\n        return 2\\n      else\\n        return 3\\n    else\\n      return 4\\n  else if (true)\\n    if (false)\\n      return 5\\n    else if (true)\\n      return 6\\n    else\\n      return 7\\n  else\\n    return 8\\n} else if (true)\\n  return 9;\",\n    \"evalExpect\": 6,\n    \"safeExpect\": 6,\n    \"category\": \"Conditionals\"\n  },\n  {\n    \"code\": \"throw new Error('test')\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/test/\",\n    \"category\": \"Error Handling\"\n  },\n  {\n    \"code\": \"throw undefined\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"error\",\n    \"category\": \"Error Handling\"\n  },\n  {\n    \"code\": \"try {a.x.a} catch {return 1}; return 2\",\n    \"evalExpect\": 1,\n    \"safeExpect\": 1,\n    \"category\": \"Error Handling\"\n  },\n  {\n    \"code\": \"try { throw new Error('test'); } catch { return 'caught'; }\",\n    \"evalExpect\": \"caught\",\n    \"safeExpect\": \"caught\",\n    \"category\": \"Error Handling\"\n  },\n  {\n    \"code\": \"let x = 0; try { x = 1; } finally { x = 2; } return x;\",\n    \"evalExpect\": 2,\n    \"safeExpect\": 2,\n    \"category\": \"Error Handling\"\n  },\n  {\n    \"code\": \"let x = 0; try { throw new Error(); } catch(e) { x = 1; } finally { x = 2; } return x;\",\n    \"evalExpect\": 2,\n    \"safeExpect\": 2,\n    \"category\": \"Error Handling\"\n  },\n  {\n    \"code\": \"let x = 0; try { x = 1; } finally { x += 10; } return x;\",\n    \"evalExpect\": 11,\n    \"safeExpect\": 11,\n    \"category\": \"Error Handling\"\n  },\n  {\n    \"code\": \"let x = 0; try { throw new Error(\\\"test\\\"); } catch(e) { x = 5; } finally { x *= 2; } return x;\",\n    \"evalExpect\": 10,\n    \"safeExpect\": 10,\n    \"category\": \"Error Handling\"\n  },\n  {\n    \"code\": \"try { return 1; } finally { return 2; }\",\n    \"evalExpect\": 2,\n    \"safeExpect\": 2,\n    \"category\": \"Error Handling\"\n  },\n  {\n    \"code\": \"let x = 0; try { return 10; } finally { x = 5; } return x;\",\n    \"evalExpect\": 10,\n    \"safeExpect\": 10,\n    \"category\": \"Error Handling\"\n  },\n  {\n    \"code\": \"try { throw new Error(\\\"first\\\"); } finally { throw new Error(\\\"second\\\"); }\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/second/\",\n    \"category\": \"Error Handling\"\n  },\n  {\n    \"code\": \"try { throw new Error(\\\"test\\\"); } catch(e) { return e.message; } finally { let y = 1; }\",\n    \"evalExpect\": \"test\",\n    \"safeExpect\": \"test\",\n    \"category\": \"Error Handling\"\n  },\n  {\n    \"code\": \"let result = \\\"\\\"; try { result += \\\"a\\\"; } catch(e) { result += \\\"b\\\"; } finally { result += \\\"c\\\"; } return result;\",\n    \"evalExpect\": \"ac\",\n    \"safeExpect\": \"ac\",\n    \"category\": \"Error Handling\"\n  },\n  {\n    \"code\": \"let result = \\\"\\\"; try { throw new Error(); } catch(e) { result += \\\"b\\\"; } finally { result += \\\"c\\\"; } return result;\",\n    \"evalExpect\": \"bc\",\n    \"safeExpect\": \"bc\",\n    \"category\": \"Error Handling\"\n  },\n  {\n    \"code\": \"function testFn1() { try { return \\\"try\\\"; } finally { } } return testFn1();\",\n    \"evalExpect\": \"try\",\n    \"safeExpect\": \"try\",\n    \"category\": \"Error Handling\"\n  },\n  {\n    \"code\": \"function testFn2() { try { throw new Error(\\\"err\\\"); } catch(e) { return \\\"catch\\\"; } finally { } } return testFn2();\",\n    \"evalExpect\": \"catch\",\n    \"safeExpect\": \"catch\",\n    \"category\": \"Error Handling\"\n  },\n  {\n    \"code\": \"let x = 0; try { } finally { x = 1; } return x;\",\n    \"evalExpect\": 1,\n    \"safeExpect\": 1,\n    \"category\": \"Error Handling\"\n  },\n  {\n    \"code\": \"missing = 1;\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/missing is not defined/\",\n    \"category\": \"Error Handling\"\n  },\n  {\n    \"code\": \"Math = 1;\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Cannot assign property 'Math' of a global object/\",\n    \"category\": \"Error Handling\"\n  },\n  {\n    \"code\": \"const answer = 1; answer = 2;\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Assignment to constant variable\\\\./\",\n    \"category\": \"Error Handling\"\n  },\n  {\n    \"code\": \"1 2\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unexpected token after .*: 2/\",\n    \"category\": \"Error Handling\"\n  },\n  {\n    \"code\": \"const x = 0; ({ x } = { x: 1 });\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Assignment to constant variable/\",\n    \"category\": \"Error Handling\"\n  },\n  {\n    \"code\": \"({ missing } = { missing: 1 });\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/missing is not defined/\",\n    \"category\": \"Error Handling\"\n  },\n  {\n    \"code\": \"{a: 1} = 1\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unexpected token/\",\n    \"category\": \"Error Handling\"\n  },\n  {\n    \"code\": \"missing: for (;;) { break nope; }\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Undefined label 'nope'/\",\n    \"category\": \"Error Handling\"\n  },\n  {\n    \"code\": \"outer: { continue outer; }\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Illegal continue statement/\",\n    \"category\": \"Error Handling\"\n  },\n  {\n    \"code\": \"outer: inner: { continue inner; }\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Illegal continue statement/\",\n    \"category\": \"Error Handling\"\n  },\n  {\n    \"code\": \"outer: switch (1) { case 1: continue outer; }\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Illegal continue statement/\",\n    \"category\": \"Error Handling\"\n  },\n  {\n    \"code\": \"outer: if (true) { continue outer; }\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Illegal continue statement/\",\n    \"category\": \"Error Handling\"\n  },\n  {\n    \"code\": \"outer: try { continue outer; } finally {}\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Illegal continue statement/\",\n    \"category\": \"Error Handling\"\n  },\n  {\n    \"code\": \"outer: { } break outer;\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Undefined label 'outer'/\",\n    \"category\": \"Error Handling\"\n  },\n  {\n    \"code\": \"outer: for (;;) { try { break outer; } finally { throw new Error(\\\"stop\\\"); } }\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/stop/\",\n    \"category\": \"Error Handling\"\n  },\n  {\n    \"code\": \"nonExistentVar\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/nonExistentVar is not defined/\",\n    \"category\": \"Error Handling\"\n  },\n  {\n    \"code\": \"const list = []; const push = list.push; push(1); push(2); return list.join(',')\",\n    \"evalExpect\": \"1,2\",\n    \"safeExpect\": \"1,2\",\n    \"category\": \"Function Replacements\"\n  },\n  {\n    \"code\": \"const list = []; const push = list.push; return push.name\",\n    \"evalExpect\": \"push\",\n    \"safeExpect\": \"push\",\n    \"category\": \"Function Replacements\"\n  },\n  {\n    \"code\": \"const list = []; const other = []; const push = list.push; const rebound = push.bind(other); rebound(3); return [list.join(','), other.join(','), rebound.name].join('|')\",\n    \"evalExpect\": \"|3|bound push\",\n    \"safeExpect\": \"|3|bound push\",\n    \"category\": \"Function Replacements\"\n  },\n  {\n    \"code\": \"const list = []; const other = []; const push = list.push; const rebound = push.bind(other, 3, 4); rebound(); return [list.join(','), other.join(','), rebound.name, rebound.length].join('|')\",\n    \"evalExpect\": \"|3,4|bound push|0\",\n    \"safeExpect\": \"|3,4|bound push|0\",\n    \"category\": \"Function Replacements\"\n  },\n  {\n    \"code\": \"const list = []; const other = []; const third = []; const push = list.push; const rebound = push.bind(other, 3); const reboundAgain = rebound.bind(third, 4); reboundAgain(5); return [list.join(','), other.join(','), third.join(','), reboundAgain.name].join('|')\",\n    \"evalExpect\": \"|3,4,5||bound bound push\",\n    \"safeExpect\": \"|3,4,5||bound bound push\",\n    \"category\": \"Function Replacements\"\n  },\n  {\n    \"code\": \"((a) => {return a + 1})(1)\",\n    \"evalExpect\": 2,\n    \"safeExpect\": 2,\n    \"category\": \"Functions\"\n  },\n  {\n    \"code\": \"(() => '1' + (() => '22')())()\",\n    \"evalExpect\": \"122\",\n    \"safeExpect\": \"122\",\n    \"category\": \"Functions\"\n  },\n  {\n    \"code\": \"(a => a + 1)(1)\",\n    \"evalExpect\": 2,\n    \"safeExpect\": 2,\n    \"category\": \"Functions\"\n  },\n  {\n    \"code\": \"function f(a) { return a + 1 } return f(2);\",\n    \"evalExpect\": 3,\n    \"safeExpect\": 3,\n    \"category\": \"Functions\"\n  },\n  {\n    \"code\": \"(function () { return 1 })()\",\n    \"evalExpect\": 1,\n    \"safeExpect\": 1,\n    \"category\": \"Functions\"\n  },\n  {\n    \"code\": \"let list = [0, 1]; return list.sort((a, b) => (a < b) ? 1 : -1)\",\n    \"evalExpect\": [1, 0],\n    \"safeExpect\": [1, 0],\n    \"category\": \"Functions\"\n  },\n  {\n    \"code\": \"let y = {a: 1, b(x) {return this.a + x}}; return y.b(2)\",\n    \"evalExpect\": 3,\n    \"safeExpect\": 3,\n    \"category\": \"Functions\"\n  },\n  {\n    \"code\": \"let y = {a: '2', b() {return this.a = '1'}}; y.b(); return y.a\",\n    \"evalExpect\": \"1\",\n    \"safeExpect\": \"1\",\n    \"category\": \"Functions\"\n  },\n  {\n    \"code\": \"[0,1].filter((...args) => args[1])\",\n    \"evalExpect\": [1],\n    \"safeExpect\": [1],\n    \"category\": \"Functions\"\n  },\n  {\n    \"code\": \"Math.pow(...[2, 2])\",\n    \"evalExpect\": 4,\n    \"safeExpect\": 4,\n    \"category\": \"Functions\"\n  },\n  {\n    \"code\": \"return f(); function f() { return 1; }\",\n    \"evalExpect\": 1,\n    \"safeExpect\": 1,\n    \"category\": \"Functions\"\n  },\n  {\n    \"code\": \"return add(2, 3); function add(a, b) { return a + b; }\",\n    \"evalExpect\": 5,\n    \"safeExpect\": 5,\n    \"category\": \"Functions\"\n  },\n  {\n    \"code\": \"let x = f(); function f() { return 42; } return x;\",\n    \"evalExpect\": 42,\n    \"safeExpect\": 42,\n    \"category\": \"Functions\"\n  },\n  {\n    \"code\": \"const fn = ({ a: for }) => for; return fn({ a: 1 });\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unexpected token 'for'/\",\n    \"category\": \"Functions\"\n  },\n  {\n    \"code\": \"function fn(for) { return for; } return fn(1);\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unexpected token 'for'/\",\n    \"category\": \"Functions\"\n  },\n  {\n    \"code\": \"function fn(a, b,) { return a + b } return fn(1, 2);\",\n    \"evalExpect\": 3,\n    \"safeExpect\": 3,\n    \"category\": \"Functions\"\n  },\n  {\n    \"code\": \"function fn(a, b) { return a + b } return fn(1, 2,);\",\n    \"evalExpect\": 3,\n    \"safeExpect\": 3,\n    \"category\": \"Functions\"\n  },\n  {\n    \"code\": \"((a, b,) => a + b)(1, 2)\",\n    \"evalExpect\": 3,\n    \"safeExpect\": 3,\n    \"category\": \"Functions\"\n  },\n  {\n    \"code\": \"!test2\",\n    \"evalExpect\": false,\n    \"safeExpect\": false,\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"!!test2\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"!({}).a?.a\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"!({a: {a: 1}}).a?.a\",\n    \"evalExpect\": false,\n    \"safeExpect\": false,\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"!({a: {a: 0}}).a?.a\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"!({}).a ? true : false\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"test2 && true\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"test2 || false\",\n    \"evalExpect\": 1,\n    \"safeExpect\": 1,\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"null ?? 'default'\",\n    \"evalExpect\": \"default\",\n    \"safeExpect\": \"default\",\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"undefined ?? 'default'\",\n    \"evalExpect\": \"default\",\n    \"safeExpect\": \"default\",\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"0 ?? 'default'\",\n    \"evalExpect\": 0,\n    \"safeExpect\": 0,\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"'' ?? 'default'\",\n    \"evalExpect\": \"\",\n    \"safeExpect\": \"\",\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"false ?? 'default'\",\n    \"evalExpect\": false,\n    \"safeExpect\": false,\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"NaN ?? 'default'\",\n    \"evalExpect\": \"NaN\",\n    \"safeExpect\": \"NaN\",\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"null ?? null ?? 'default'\",\n    \"evalExpect\": \"default\",\n    \"safeExpect\": \"default\",\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"1 ?? 2 ?? 3\",\n    \"evalExpect\": 1,\n    \"safeExpect\": 1,\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"null ?? 2 ?? 3\",\n    \"evalExpect\": 2,\n    \"safeExpect\": 2,\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"({a: 1}).a ?? 'default'\",\n    \"evalExpect\": 1,\n    \"safeExpect\": 1,\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"({}).a ?? 'default'\",\n    \"evalExpect\": \"default\",\n    \"safeExpect\": \"default\",\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"({}).a?.b ?? 'default'\",\n    \"evalExpect\": \"default\",\n    \"safeExpect\": \"default\",\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"null?.a ?? 'default'\",\n    \"evalExpect\": \"default\",\n    \"safeExpect\": \"default\",\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"({a: {b: 1}}).a?.b ?? 'default'\",\n    \"evalExpect\": 1,\n    \"safeExpect\": 1,\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"({a: null}).a?.b ?? 'default'\",\n    \"evalExpect\": \"default\",\n    \"safeExpect\": \"default\",\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"null ?? 1 || 2\",\n    \"evalExpect\": 1,\n    \"safeExpect\": 1,\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"0 || null ?? 1\",\n    \"evalExpect\": 1,\n    \"safeExpect\": 1,\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"(null ?? false) || 'fallback'\",\n    \"evalExpect\": \"fallback\",\n    \"safeExpect\": \"fallback\",\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"null ?? 0 || 'fallback'\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"fallback\",\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"true && null ?? 'default'\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"default\",\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"let x = 0; (1 ?? (x = 1)); return x\",\n    \"evalExpect\": 0,\n    \"safeExpect\": 0,\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"let x = 0; (null ?? (x = 1)); return x\",\n    \"evalExpect\": 1,\n    \"safeExpect\": 1,\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"let x = 0; (undefined ?? (x = 1)); return x\",\n    \"evalExpect\": 1,\n    \"safeExpect\": 1,\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"let x = 0; (0 ?? (x = 1)); return x\",\n    \"evalExpect\": 0,\n    \"safeExpect\": 0,\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"let x = 0; ('' ?? (x = 1)); return x\",\n    \"evalExpect\": 0,\n    \"safeExpect\": 0,\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"let x = 0; (false ?? (x = 1)); return x\",\n    \"evalExpect\": 0,\n    \"safeExpect\": 0,\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"({a: null ?? 'default'}).a\",\n    \"evalExpect\": \"default\",\n    \"safeExpect\": \"default\",\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"[null ?? 1, 2 ?? 3][0]\",\n    \"evalExpect\": 1,\n    \"safeExpect\": 1,\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"[null ?? 1, undefined ?? 3][1]\",\n    \"evalExpect\": 3,\n    \"safeExpect\": 3,\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"let outputs = {}; return (outputs['classify']?.intent ?? null)\",\n    \"evalExpect\": null,\n    \"safeExpect\": null,\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"let outputs = {classify: {intent: 'greeting'}}; return (outputs['classify']?.intent ?? null)\",\n    \"evalExpect\": \"greeting\",\n    \"safeExpect\": \"greeting\",\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"null ?? 5 ? \\\"yes\\\" : \\\"no\\\"\",\n    \"evalExpect\": \"yes\",\n    \"safeExpect\": \"yes\",\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"null ?? 0 ? \\\"yes\\\" : \\\"no\\\"\",\n    \"evalExpect\": \"no\",\n    \"safeExpect\": \"no\",\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"!~0\",\n    \"evalExpect\": false,\n    \"safeExpect\": false,\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"true && true && true\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"false || false || true\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"null ?? null ?? null ?? 'default'\",\n    \"evalExpect\": \"default\",\n    \"safeExpect\": \"default\",\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"1 || 2 && 3\",\n    \"evalExpect\": 1,\n    \"safeExpect\": 1,\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"0 && 1 || 2\",\n    \"evalExpect\": 2,\n    \"safeExpect\": 2,\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"true || false && false\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"!true\",\n    \"evalExpect\": false,\n    \"safeExpect\": false,\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"!false\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Logical Operators\"\n  },\n  {\n    \"code\": \"let x; for(let i = 0; i < 2; i++){ x = i }; return x;\",\n    \"evalExpect\": 1,\n    \"safeExpect\": 1,\n    \"category\": \"Loops\"\n  },\n  {\n    \"code\": \"let x; for(let i = 0; i < 2; i++){ x = i; break; }; return x;\",\n    \"evalExpect\": 0,\n    \"safeExpect\": 0,\n    \"category\": \"Loops\"\n  },\n  {\n    \"code\": \"let x; for(let i = 0; i < 2; i++){ x = i; continue; x++ }; return x;\",\n    \"evalExpect\": 1,\n    \"safeExpect\": 1,\n    \"category\": \"Loops\"\n  },\n  {\n    \"code\": \"break;\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"error\",\n    \"category\": \"Loops\"\n  },\n  {\n    \"code\": \"continue;\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"error\",\n    \"category\": \"Loops\"\n  },\n  {\n    \"code\": \"let sum = 0; for (let i = 0; i < 5; i++) { if (i === 2) continue; sum += i; }; return sum;\",\n    \"evalExpect\": 8,\n    \"safeExpect\": 8,\n    \"category\": \"Loops\"\n  },\n  {\n    \"code\": \"let sum = 0; for (let i = 0; i < 10; i++) { if (i === 3) break; sum += i; }; return sum;\",\n    \"evalExpect\": 3,\n    \"safeExpect\": 3,\n    \"category\": \"Loops\"\n  },\n  {\n    \"code\": \"let sum = 0; for (let i = 0; i < 5; i++) { if (i > 0) { if (i === 2) continue; } sum += i; }; return sum;\",\n    \"evalExpect\": 8,\n    \"safeExpect\": 8,\n    \"category\": \"Loops\"\n  },\n  {\n    \"code\": \"let sum = 0; for (let i = 0; i < 5; i++) { if (i === 2) { continue; } sum += i; }; return sum;\",\n    \"evalExpect\": 8,\n    \"safeExpect\": 8,\n    \"category\": \"Loops\"\n  },\n  {\n    \"code\": \"let x = 0; while (x < 5) { x++; if (x === 3) continue; if (x === 4) break; }; return x;\",\n    \"evalExpect\": 4,\n    \"safeExpect\": 4,\n    \"category\": \"Loops\"\n  },\n  {\n    \"code\": \"let sum = 0; for (let i = 0; i < 5; i++) { sum += i === 2 ? continue : i; }; return sum;\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"error\",\n    \"category\": \"Loops\"\n  },\n  {\n    \"code\": \"let x = 2; while(--x){ }; return x;\",\n    \"evalExpect\": 0,\n    \"safeExpect\": 0,\n    \"category\": \"Loops\"\n  },\n  {\n    \"code\": \"let x = 1; do {x++} while(x < 1); return x;\",\n    \"evalExpect\": 2,\n    \"safeExpect\": 2,\n    \"category\": \"Loops\"\n  },\n  {\n    \"code\": \"for(let i of [1,2]){ return i };\",\n    \"evalExpect\": 1,\n    \"safeExpect\": 1,\n    \"category\": \"Loops\"\n  },\n  {\n    \"code\": \"let arr = [1,2]; for(let i of arr){ return i };\",\n    \"evalExpect\": 1,\n    \"safeExpect\": 1,\n    \"category\": \"Loops\"\n  },\n  {\n    \"code\": \"for(let i in [1,2]){ return i };\",\n    \"evalExpect\": \"0\",\n    \"safeExpect\": \"0\",\n    \"category\": \"Loops\"\n  },\n  {\n    \"code\": \"let i = 1; {let j = 1; i += j;}; return i\",\n    \"evalExpect\": 2,\n    \"safeExpect\": 2,\n    \"category\": \"Loops\"\n  },\n  {\n    \"code\": \"let c = 0; for (let i = 0; i < 10; i++) {c++} return c\",\n    \"evalExpect\": 10,\n    \"safeExpect\": 10,\n    \"category\": \"Loops\"\n  },\n  {\n    \"code\": \"outer: for(let i = 0; i < 3; i++) { for(let j = 0; j < 3; j++) { if(i === 1 && j === 1) break outer; } } return 'done'\",\n    \"evalExpect\": \"done\",\n    \"safeExpect\": \"done\",\n    \"category\": \"Loops\"\n  },\n  {\n    \"code\": \"let hits = \\\"\\\"; outer: for (let i = 0; i < 3; i++) { for (let j = 0; j < 3; j++) { hits += `${i}${j}|`; continue outer; } } return hits;\",\n    \"evalExpect\": \"00|10|20|\",\n    \"safeExpect\": \"00|10|20|\",\n    \"category\": \"Loops\"\n  },\n  {\n    \"code\": \"let out = \\\"\\\"; outer: { out += \\\"a\\\"; break outer; out += \\\"b\\\"; } return out;\",\n    \"evalExpect\": \"a\",\n    \"safeExpect\": \"a\",\n    \"category\": \"Loops\"\n  },\n  {\n    \"code\": \"let out = \\\"\\\"; outer: switch (1) { case 1: out = \\\"ok\\\"; break outer; default: out = \\\"bad\\\"; } return out;\",\n    \"evalExpect\": \"ok\",\n    \"safeExpect\": \"ok\",\n    \"category\": \"Loops\"\n  },\n  {\n    \"code\": \"let out = \\\"\\\"; outer: inner: for (let i = 0; i < 3; i++) { out += i; if (i < 2) continue outer; out += \\\"!\\\"; } return out;\",\n    \"evalExpect\": \"012!\",\n    \"safeExpect\": \"012!\",\n    \"category\": \"Loops\"\n  },\n  {\n    \"code\": \"let out = \\\"\\\"; outer: inner: for (let i = 0; i < 3; i++) { out += i; if (i < 2) continue inner; out += \\\"!\\\"; } return out;\",\n    \"evalExpect\": \"012!\",\n    \"safeExpect\": \"012!\",\n    \"category\": \"Loops\"\n  },\n  {\n    \"code\": \"let out = \\\"\\\"; first: second: { out += \\\"a\\\"; break first; out += \\\"b\\\"; } return out;\",\n    \"evalExpect\": \"a\",\n    \"safeExpect\": \"a\",\n    \"category\": \"Loops\"\n  },\n  {\n    \"code\": \"let out = \\\"\\\"; first: second: switch (1) { case 1: out += \\\"a\\\"; break second; default: out += \\\"b\\\"; } return out;\",\n    \"evalExpect\": \"a\",\n    \"safeExpect\": \"a\",\n    \"category\": \"Loops\"\n  },\n  {\n    \"code\": \"let out = \\\"\\\"; outer: if (true) { out += \\\"a\\\"; break outer; out += \\\"b\\\"; } return out;\",\n    \"evalExpect\": \"a\",\n    \"safeExpect\": \"a\",\n    \"category\": \"Loops\"\n  },\n  {\n    \"code\": \"let out = \\\"\\\"; outer: try { out += \\\"a\\\"; break outer; } finally { out += \\\"f\\\"; } return out;\",\n    \"evalExpect\": \"af\",\n    \"safeExpect\": \"af\",\n    \"category\": \"Loops\"\n  },\n  {\n    \"code\": \"let out = \\\"\\\"; outer: while (true) { out += \\\"a\\\"; break outer; } return out;\",\n    \"evalExpect\": \"a\",\n    \"safeExpect\": \"a\",\n    \"category\": \"Loops\"\n  },\n  {\n    \"code\": \"let out = \\\"\\\"; outer: do { out += \\\"a\\\"; continue outer; out += \\\"b\\\"; } while (false); return out;\",\n    \"evalExpect\": \"a\",\n    \"safeExpect\": \"a\",\n    \"category\": \"Loops\"\n  },\n  {\n    \"code\": \"let out = \\\"\\\"; outer: for (const value of [1, 2, 3]) { out += value; if (value === 2) break outer; } return out;\",\n    \"evalExpect\": \"12\",\n    \"safeExpect\": \"12\",\n    \"category\": \"Loops\"\n  },\n  {\n    \"code\": \"let out = \\\"\\\"; outer: for (const key in { a: 1, b: 2, c: 3 }) { out += key; if (key === \\\"b\\\") break outer; } return out;\",\n    \"evalExpect\": \"ab\",\n    \"safeExpect\": \"ab\",\n    \"category\": \"Loops\"\n  },\n  {\n    \"code\": \"let hits = \\\"\\\"; outer: for (let i = 0; i < 3; i++) { switch (i) { case 1: hits += \\\"s\\\"; continue outer; default: hits += i; } hits += \\\"|\\\"; } return hits;\",\n    \"evalExpect\": \"0|s2|\",\n    \"safeExpect\": \"0|s2|\",\n    \"category\": \"Loops\"\n  },\n  {\n    \"code\": \"let hits = \\\"\\\"; outer: for (let i = 0; i < 3; i++) { inner: for (let j = 0; j < 3; j++) { if (j === 1) break inner; if (i === 2) break outer; hits += `${i}${j}|`; } } return hits;\",\n    \"evalExpect\": \"00|10|\",\n    \"safeExpect\": \"00|10|\",\n    \"category\": \"Loops\"\n  },\n  {\n    \"code\": \"let log = \\\"\\\"; outer: { try { log += \\\"a\\\"; break outer; } finally { log += \\\"f\\\"; } log += \\\"x\\\"; } return log;\",\n    \"evalExpect\": \"af\",\n    \"safeExpect\": \"af\",\n    \"category\": \"Loops\"\n  },\n  {\n    \"code\": \"let log = \\\"\\\"; outer: for (let i = 0; i < 3; i++) { for (let j = 0; j < 3; j++) { try { log += `${i}${j}|`; if (j === 1) break; } finally { if (j === 1) continue outer; } log += \\\"x\\\"; } log += \\\"y\\\"; } return log;\",\n    \"evalExpect\": \"00|x01|10|x11|20|x21|\",\n    \"safeExpect\": \"00|x01|10|x11|20|x21|\",\n    \"category\": \"Loops\"\n  },\n  {\n    \"code\": \"let log = \\\"\\\"; outer: for (let i = 0; i < 3; i++) { try { switch (i) { case 2: log += \\\"b\\\"; break outer; default: log += i; } } finally { log += \\\"f\\\"; } log += \\\"x\\\"; } return log;\",\n    \"evalExpect\": \"0fx1fxbf\",\n    \"safeExpect\": \"0fx1fxbf\",\n    \"category\": \"Loops\"\n  },\n  {\n    \"code\": \"for(let i = 0, j = 10; i < 3; i++, j--) { } return 'done'\",\n    \"evalExpect\": \"done\",\n    \"safeExpect\": \"done\",\n    \"category\": \"Loops\"\n  },\n  {\n    \"code\": \"let i = 0; for (; i < 3;) { i++; } return i;\",\n    \"evalExpect\": 3,\n    \"safeExpect\": 3,\n    \"category\": \"Loops\"\n  },\n  {\n    \"code\": \"let i = 0; for (;;) { i++; if (i === 3) break; } return i;\",\n    \"evalExpect\": 3,\n    \"safeExpect\": 3,\n    \"category\": \"Loops\"\n  },\n  {\n    \"code\": \"let total = 0; for (; total < 3; total++) {} return total;\",\n    \"evalExpect\": 3,\n    \"safeExpect\": 3,\n    \"category\": \"Loops\"\n  },\n  {\n    \"code\": \"let x = 0; do x++; while (x < 3); return x;\",\n    \"evalExpect\": 3,\n    \"safeExpect\": 3,\n    \"category\": \"Loops\"\n  },\n  {\n    \"code\": \"a.b.c\",\n    \"evalExpect\": 2,\n    \"safeExpect\": 2,\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"[test2, 2]\",\n    \"evalExpect\": [1, 2],\n    \"safeExpect\": [1, 2],\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"{\\\"aa\\\": test[0](), b: test2 * 3}\",\n    \"evalExpect\": {\n      \"aa\": 1,\n      \"b\": 3\n    },\n    \"safeExpect\": {\n      \"aa\": 1,\n      \"b\": 3\n    },\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"{\\\"\\\\\\\\\\\":\\\"\\\\\\\\\\\"}\",\n    \"evalExpect\": {\n      \"\\\\\": \"\\\\\"\n    },\n    \"safeExpect\": {\n      \"\\\\\": \"\\\\\"\n    },\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"Object.keys({a:1})\",\n    \"evalExpect\": [\"a\"],\n    \"safeExpect\": [\"a\"],\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"[1, ...[2, [test2, 4]], 5]\",\n    \"evalExpect\": [1, 2, [1, 4], 5],\n    \"safeExpect\": [1, 2, [1, 4], 5],\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"const obj1 = {a: 1, b: 2}; const obj2 = {c: 3, ...obj1}; return obj2.a\",\n    \"evalExpect\": 1,\n    \"safeExpect\": 1,\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"const obj1 = {a: 1, b: 2}; const obj2 = {...obj1, b: 5}; return obj2.b\",\n    \"evalExpect\": 5,\n    \"safeExpect\": 5,\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"const obj1 = {x: 10}; const obj2 = {y: 20}; const obj3 = {...obj1, ...obj2}; return obj3.x + obj3.y\",\n    \"evalExpect\": 30,\n    \"safeExpect\": 30,\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"const arr1 = [1, 2, 3]; const arr2 = [...arr1, 4, 5]; return arr2.length\",\n    \"evalExpect\": 5,\n    \"safeExpect\": 5,\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"const arr1 = [1, 2]; const arr2 = [3, 4]; return [...arr1, ...arr2]\",\n    \"evalExpect\": [1, 2, 3, 4],\n    \"safeExpect\": [1, 2, 3, 4],\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"const a = [1]; const b = [2]; const c = [3]; return [...a, ...b, ...c]\",\n    \"evalExpect\": [1, 2, 3],\n    \"safeExpect\": [1, 2, 3],\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"const data = []; return {data}\",\n    \"evalExpect\": {\n      \"data\": []\n    },\n    \"safeExpect\": {\n      \"data\": []\n    },\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"const x = 1; const y = 2; return {x, y}\",\n    \"evalExpect\": {\n      \"x\": 1,\n      \"y\": 2\n    },\n    \"safeExpect\": {\n      \"x\": 1,\n      \"y\": 2\n    },\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"const name = \\\"Alice\\\"; const age = 30; return {name, age, city: \\\"NYC\\\"}\",\n    \"evalExpect\": {\n      \"name\": \"Alice\",\n      \"age\": 30,\n      \"city\": \"NYC\"\n    },\n    \"safeExpect\": {\n      \"name\": \"Alice\",\n      \"age\": 30,\n      \"city\": \"NYC\"\n    },\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"const a = 1; const obj = {a, b: a + 1}; return obj.a + obj.b\",\n    \"evalExpect\": 3,\n    \"safeExpect\": 3,\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"({[\\\"a\\\" + \\\"b\\\"]: 1})\",\n    \"evalExpect\": {\n      \"ab\": 1\n    },\n    \"safeExpect\": {\n      \"ab\": 1\n    },\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"let k = \\\"x\\\"; return ({[k]: 42})\",\n    \"evalExpect\": {\n      \"x\": 42\n    },\n    \"safeExpect\": {\n      \"x\": 42\n    },\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"({[1 + 2]: \\\"three\\\"})\",\n    \"evalExpect\": {\n      \"3\": \"three\"\n    },\n    \"safeExpect\": {\n      \"3\": \"three\"\n    },\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"let n = \\\"world\\\"; return ({[`hello_${n}`]: 1})\",\n    \"evalExpect\": {\n      \"hello_world\": 1\n    },\n    \"safeExpect\": {\n      \"hello_world\": 1\n    },\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"function k() { return \\\"key\\\" }; return ({[k()]: \\\"val\\\"})\",\n    \"evalExpect\": {\n      \"key\": \"val\"\n    },\n    \"safeExpect\": {\n      \"key\": \"val\"\n    },\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"let t = true; return ({[t ? \\\"a\\\" : \\\"b\\\"]: 1})\",\n    \"evalExpect\": {\n      \"a\": 1\n    },\n    \"safeExpect\": {\n      \"a\": 1\n    },\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"let a = \\\"x\\\", b = \\\"y\\\"; return ({[a]: 1, [b]: 2})\",\n    \"evalExpect\": {\n      \"x\": 1,\n      \"y\": 2\n    },\n    \"safeExpect\": {\n      \"x\": 1,\n      \"y\": 2\n    },\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"({a: 1, [\\\"b\\\"]: 2, c: 3})\",\n    \"evalExpect\": {\n      \"a\": 1,\n      \"b\": 2,\n      \"c\": 3\n    },\n    \"safeExpect\": {\n      \"a\": 1,\n      \"b\": 2,\n      \"c\": 3\n    },\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"({a: 1, [\\\"a\\\"]: 2})\",\n    \"evalExpect\": {\n      \"a\": 2\n    },\n    \"safeExpect\": {\n      \"a\": 2\n    },\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"let arr = [\\\"key\\\"]; return ({[arr[0]]: \\\"val\\\"})\",\n    \"evalExpect\": {\n      \"key\": \"val\"\n    },\n    \"safeExpect\": {\n      \"key\": \"val\"\n    },\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"let c = 0; function k() { c++; return \\\"a\\\" } let o = {[k()]: 1}; return c\",\n    \"evalExpect\": 1,\n    \"safeExpect\": 1,\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"({[\\\"\\\"]: 1})\",\n    \"evalExpect\": {\n      \"\": 1\n    },\n    \"safeExpect\": {\n      \"\": 1\n    },\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"({[0]: \\\"zero\\\"})\",\n    \"evalExpect\": {\n      \"0\": \"zero\"\n    },\n    \"safeExpect\": {\n      \"0\": \"zero\"\n    },\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"({[true]: 1})\",\n    \"evalExpect\": {\n      \"true\": 1\n    },\n    \"safeExpect\": {\n      \"true\": 1\n    },\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"({[null]: 1})\",\n    \"evalExpect\": {\n      \"null\": 1\n    },\n    \"safeExpect\": {\n      \"null\": 1\n    },\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"({[undefined]: 1})\",\n    \"evalExpect\": {\n      \"undefined\": 1\n    },\n    \"safeExpect\": {\n      \"undefined\": 1\n    },\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"let rest = {b: 2}; return ({[\\\"a\\\"]: 1, ...rest})\",\n    \"evalExpect\": {\n      \"a\": 1,\n      \"b\": 2\n    },\n    \"safeExpect\": {\n      \"a\": 1,\n      \"b\": 2\n    },\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"let rest = {a: 1}; return ({...rest, [\\\"b\\\"]: 2})\",\n    \"evalExpect\": {\n      \"a\": 1,\n      \"b\": 2\n    },\n    \"safeExpect\": {\n      \"a\": 1,\n      \"b\": 2\n    },\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"let rest = {a: 1}; return ({...rest, [\\\"a\\\"]: 2})\",\n    \"evalExpect\": {\n      \"a\": 2\n    },\n    \"safeExpect\": {\n      \"a\": 2\n    },\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"let o = {k: \\\"deep\\\"}; return ({[o.k]: 1})\",\n    \"evalExpect\": {\n      \"deep\": 1\n    },\n    \"safeExpect\": {\n      \"deep\": 1\n    },\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"({[(0, \\\"a\\\")]: 1})\",\n    \"evalExpect\": {\n      \"a\": 1\n    },\n    \"safeExpect\": {\n      \"a\": 1\n    },\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"let m = \\\"greet\\\"; let o = {[m]() { return \\\"hi\\\" }}; return o.greet()\",\n    \"evalExpect\": \"hi\",\n    \"safeExpect\": \"hi\",\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"let o = {[\\\"say\\\"]() { return \\\"hello\\\" }}; return o.say()\",\n    \"evalExpect\": \"hello\",\n    \"safeExpect\": \"hello\",\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"let o = {[\\\"a\\\" + \\\"b\\\"]() { return 1 }}; return o.ab()\",\n    \"evalExpect\": 1,\n    \"safeExpect\": 1,\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"let o = {x: 10, [\\\"getX\\\"]() { return this.x }}; return o.getX()\",\n    \"evalExpect\": 10,\n    \"safeExpect\": 10,\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"[1,2,3,].length\",\n    \"evalExpect\": 3,\n    \"safeExpect\": 3,\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"Object.keys({a:1,b:2,}).length\",\n    \"evalExpect\": 2,\n    \"safeExpect\": 2,\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"[,].length\",\n    \"evalExpect\": 1,\n    \"safeExpect\": 1,\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"0 in [,]\",\n    \"evalExpect\": false,\n    \"safeExpect\": false,\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"[,,].length\",\n    \"evalExpect\": 2,\n    \"safeExpect\": 2,\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"[1,,].length\",\n    \"evalExpect\": 2,\n    \"safeExpect\": 2,\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"1 in [1,,]\",\n    \"evalExpect\": false,\n    \"safeExpect\": false,\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"[1,,2].length\",\n    \"evalExpect\": 3,\n    \"safeExpect\": 3,\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"1 in [1,,2]\",\n    \"evalExpect\": false,\n    \"safeExpect\": false,\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"[1,,2][2]\",\n    \"evalExpect\": 2,\n    \"safeExpect\": 2,\n    \"category\": \"Objects & Arrays\"\n  },\n  {\n    \"code\": \"test2 !== '1' && false\",\n    \"evalExpect\": false,\n    \"safeExpect\": false,\n    \"category\": \"Operator Precedence\"\n  },\n  {\n    \"code\": \"true && true || false\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Operator Precedence\"\n  },\n  {\n    \"code\": \"true || true && false\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Operator Precedence\"\n  },\n  {\n    \"code\": \"1 && 2 == 1\",\n    \"evalExpect\": false,\n    \"safeExpect\": false,\n    \"category\": \"Operator Precedence\"\n  },\n  {\n    \"code\": \"1 + 2 === 1 + 2 === 1 && 2\",\n    \"evalExpect\": false,\n    \"safeExpect\": false,\n    \"category\": \"Operator Precedence\"\n  },\n  {\n    \"code\": \"true === 1 && 2\",\n    \"evalExpect\": false,\n    \"safeExpect\": false,\n    \"category\": \"Operator Precedence\"\n  },\n  {\n    \"code\": \"-1 < 1 && 2 > 1\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Operator Precedence\"\n  },\n  {\n    \"code\": \"!5 > 3\",\n    \"evalExpect\": false,\n    \"safeExpect\": false,\n    \"category\": \"Operator Precedence\"\n  },\n  {\n    \"code\": \"!5 < 3\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Operator Precedence\"\n  },\n  {\n    \"code\": \"!0 === true\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Operator Precedence\"\n  },\n  {\n    \"code\": \"!false && true\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Operator Precedence\"\n  },\n  {\n    \"code\": \"!true || true\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Operator Precedence\"\n  },\n  {\n    \"code\": \"!false && false || true\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Operator Precedence\"\n  },\n  {\n    \"code\": \"5 > 3 > 1\",\n    \"evalExpect\": false,\n    \"safeExpect\": false,\n    \"category\": \"Operator Precedence\"\n  },\n  {\n    \"code\": \"1 < 2 < 3\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Operator Precedence\"\n  },\n  {\n    \"code\": \"5 > 3 === true\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Operator Precedence\"\n  },\n  {\n    \"code\": \"1 | 2 && 3\",\n    \"evalExpect\": 3,\n    \"safeExpect\": 3,\n    \"category\": \"Operator Precedence\"\n  },\n  {\n    \"code\": \"true && 1 | 2\",\n    \"evalExpect\": 3,\n    \"safeExpect\": 3,\n    \"category\": \"Operator Precedence\"\n  },\n  {\n    \"code\": \"4 & 5 || 0\",\n    \"evalExpect\": 4,\n    \"safeExpect\": 4,\n    \"category\": \"Operator Precedence\"\n  },\n  {\n    \"code\": \"2 + 3 << 1\",\n    \"evalExpect\": 10,\n    \"safeExpect\": 10,\n    \"category\": \"Operator Precedence\"\n  },\n  {\n    \"code\": \"8 >> 1 + 1\",\n    \"evalExpect\": 2,\n    \"safeExpect\": 2,\n    \"category\": \"Operator Precedence\"\n  },\n  {\n    \"code\": \"1 << 2 * 2\",\n    \"evalExpect\": 16,\n    \"safeExpect\": 16,\n    \"category\": \"Operator Precedence\"\n  },\n  {\n    \"code\": \"5 & 3 | 2\",\n    \"evalExpect\": 3,\n    \"safeExpect\": 3,\n    \"category\": \"Operator Precedence\"\n  },\n  {\n    \"code\": \"8 | 4 & 2\",\n    \"evalExpect\": 8,\n    \"safeExpect\": 8,\n    \"category\": \"Operator Precedence\"\n  },\n  {\n    \"code\": \"15 ^ 3 & 7\",\n    \"evalExpect\": 12,\n    \"safeExpect\": 12,\n    \"category\": \"Operator Precedence\"\n  },\n  {\n    \"code\": \"2 * 3 ** 2\",\n    \"evalExpect\": 18,\n    \"safeExpect\": 18,\n    \"category\": \"Operator Precedence\"\n  },\n  {\n    \"code\": \"10 / 2 ** 3\",\n    \"evalExpect\": 1.25,\n    \"safeExpect\": 1.25,\n    \"category\": \"Operator Precedence\"\n  },\n  {\n    \"code\": \"2 + 3 ** 2\",\n    \"evalExpect\": 11,\n    \"safeExpect\": 11,\n    \"category\": \"Operator Precedence\"\n  },\n  {\n    \"code\": \"2 * 3 * 2 ** 3\",\n    \"evalExpect\": 48,\n    \"safeExpect\": 48,\n    \"category\": \"Operator Precedence\"\n  },\n  {\n    \"code\": \"-2 ** 2\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"error\",\n    \"category\": \"Operator Precedence\"\n  },\n  {\n    \"code\": \"2 ** 3 ** 2\",\n    \"evalExpect\": 512,\n    \"safeExpect\": 512,\n    \"category\": \"Operator Precedence\"\n  },\n  {\n    \"code\": \"2 ** 2 ** 3\",\n    \"evalExpect\": 256,\n    \"safeExpect\": 256,\n    \"category\": \"Operator Precedence\"\n  },\n  {\n    \"code\": \"3 ** 2 ** 2\",\n    \"evalExpect\": 81,\n    \"safeExpect\": 81,\n    \"category\": \"Operator Precedence\"\n  },\n  {\n    \"code\": \"4 / 2 ** 2 * 3\",\n    \"evalExpect\": 3,\n    \"safeExpect\": 3,\n    \"category\": \"Operator Precedence\"\n  },\n  {\n    \"code\": \"100 - 10 ** 2\",\n    \"evalExpect\": 0,\n    \"safeExpect\": 0,\n    \"category\": \"Operator Precedence\"\n  },\n  {\n    \"code\": \"typeof '1'\",\n    \"evalExpect\": \"string\",\n    \"safeExpect\": \"string\",\n    \"category\": \"Other Operators\"\n  },\n  {\n    \"code\": \"typeof z === 'undefined'\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Other Operators\"\n  },\n  {\n    \"code\": \"{} instanceof Object\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": true,\n    \"category\": \"Other Operators\"\n  },\n  {\n    \"code\": \"{} instanceof undefined\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"error\",\n    \"category\": \"Other Operators\"\n  },\n  {\n    \"code\": \"'a' in {a: 1}\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Other Operators\"\n  },\n  {\n    \"code\": \"1,2\",\n    \"evalExpect\": 2,\n    \"safeExpect\": 2,\n    \"category\": \"Other Operators\"\n  },\n  {\n    \"code\": \"void 2 == '2'\",\n    \"evalExpect\": false,\n    \"safeExpect\": false,\n    \"category\": \"Other Operators\"\n  },\n  {\n    \"code\": \"void (2 == '2')\",\n    \"category\": \"Other Operators\"\n  },\n  {\n    \"code\": \"new Date(0).toISOString()\",\n    \"evalExpect\": \"1970-01-01T00:00:00.000Z\",\n    \"safeExpect\": \"1970-01-01T00:00:00.000Z\",\n    \"category\": \"Other Operators\"\n  },\n  {\n    \"code\": \"function E(a) { this.scope = a.context }; return new E(isNaN).scope?.Function?.name\",\n    \"category\": \"Other Operators\"\n  },\n  {\n    \"code\": \"typeof 5 + \\\"2\\\"\",\n    \"evalExpect\": \"number2\",\n    \"safeExpect\": \"number2\",\n    \"category\": \"Other Operators\"\n  },\n  {\n    \"code\": \"typeof (5 + 2)\",\n    \"evalExpect\": \"number\",\n    \"safeExpect\": \"number\",\n    \"category\": \"Other Operators\"\n  },\n  {\n    \"code\": \"typeof 5 === \\\"number\\\"\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Other Operators\"\n  },\n  {\n    \"code\": \"void 0 === undefined\",\n    \"evalExpect\": true,\n    \"safeExpect\": true,\n    \"category\": \"Other Operators\"\n  },\n  {\n    \"code\": \"void 5 + 10\",\n    \"evalExpect\": \"NaN\",\n    \"safeExpect\": \"NaN\",\n    \"category\": \"Other Operators\"\n  },\n  {\n    \"code\": \"typeof void 0\",\n    \"evalExpect\": \"undefined\",\n    \"safeExpect\": \"undefined\",\n    \"category\": \"Other Operators\"\n  },\n  {\n    \"code\": \"null?.[0]\",\n    \"category\": \"Other Operators\"\n  },\n  {\n    \"code\": \"null?.fn?.()\",\n    \"category\": \"Other Operators\"\n  },\n  {\n    \"code\": \"let a = 1; let b = 2; switch(1) {case a: b = 1; case b: return b; default: return 0;}\",\n    \"evalExpect\": 1,\n    \"safeExpect\": 1,\n    \"category\": \"Switch\"\n  },\n  {\n    \"code\": \"let b = 1; switch(1) {case 1: b = 2; break; case 2: b = 3; default: b = 4}; return b\",\n    \"evalExpect\": 2,\n    \"safeExpect\": 2,\n    \"category\": \"Switch\"\n  },\n  {\n    \"code\": \"let b = 1; switch(3) {case 1: b = 2; break; case 2: b = 3; default: b = 4}; return b\",\n    \"evalExpect\": 4,\n    \"safeExpect\": 4,\n    \"category\": \"Switch\"\n  },\n  {\n    \"code\": \"let b = 1; switch(1) {case 1:b = 2; case 2: b = 3; default: b = 4}; return b\",\n    \"evalExpect\": 4,\n    \"safeExpect\": 4,\n    \"category\": \"Switch\"\n  },\n  {\n    \"code\": \"let a = 1; switch(a) {case 1: return 2}; return 1\",\n    \"evalExpect\": 2,\n    \"safeExpect\": 2,\n    \"category\": \"Switch\"\n  },\n  {\n    \"code\": \"switch (1) { case 1: { return 42; } default: return 0; }\",\n    \"evalExpect\": 42,\n    \"safeExpect\": 42,\n    \"category\": \"Switch\"\n  },\n  {\n    \"code\": \"switch (1) { case 1: case 2: return 7; default: return 0; }\",\n    \"evalExpect\": 7,\n    \"safeExpect\": 7,\n    \"category\": \"Switch\"\n  },\n  {\n    \"code\": \"return eval(\\\"const value = 'oops\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unclosed '\\\\''/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"const value = \\\\\\\"oops\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unclosed '\\\"/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"const value = `oops\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unclosed '`'/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"const value = 1; /* open comment\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unclosed comment '\\\\/\\\\*'/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"const value =\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unexpected end of expression/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"const value = [1 2];\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unexpected token/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"const value = { a: 1 b: 2 };\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unexpected token/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"fn(, value);\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unexpected end of expression/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"const obj = { [key] };\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unexpected token in computed property/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"return -2 ** 3;\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unary operator used immediately before exponentiation expression/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"value.\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unexpected token after prop: \\\\./\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"const value = for;\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unexpected token 'for'/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"return 1 + ;\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unexpected end of expression/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"return 1,;\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unexpected token/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"return value ? truthy :;\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unexpected end of expression/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"({}).a?.;\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unexpected token/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"({}).a?.[;\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unclosed '\\\\['/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"a?.b = 1;\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Invalid left-hand side in assignment/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"new Map?.();\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/optional chain|Unexpected/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"a[];\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unexpected end of expression/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"a[;\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unclosed '\\\\['/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"return (1 + 2;\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unclosed '\\\\('/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"const value = [1, 2;\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unclosed '\\\\['/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"const value = { a: 1;\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unclosed '\\\\{'/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"fn(value;\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unclosed '\\\\('/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"switch (value) case 1: break;\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Invalid switch/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"switch (value) { default: break; default: break; }\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Only one default switch case allowed/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"function fn(for) { return for; }\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unexpected token 'for'/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"async function fn(await) {}\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unexpected token 'await'/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"const [value];\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Destructuring declaration requires an initializer/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"const [value + 1] = arr;\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Invalid destructuring target/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"const { a: value + 1 } = obj;\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Invalid destructuring target/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"function fn(...rest, last) { return rest; }\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Rest parameter must be last formal parameter/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"(first, ...rest, last) => first\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Rest parameter must be last formal parameter/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"for (const [a, b] from pairs) {}\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Invalid for loop definition/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"for (let i = 0; i < 2) {}\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Invalid for loop definition/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"if (value) else return 1;\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unexpected token/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"while () {}\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unexpected end of expression/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"do { value++; }\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unclosed '\\\\('/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"try {} catch () {}\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unexpected token '\\\\)'/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"try { value++; }\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Missing catch or finally after try/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"try {} catch (for) {}\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unexpected token 'for'/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"try {} finally\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unexpected token/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"throw ;\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unexpected end of expression/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"new ;\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unexpected end of expression/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"typeof ;\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unexpected end of expression/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"delete ;\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unexpected end of expression/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"void ;\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unexpected end of expression/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"\\\\\\\"a\\\\\\\" in ;\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unexpected end of expression/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"value instanceof ;\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unexpected end of expression/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"const value = [...];\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unexpected end of expression/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"const value = {...};\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unexpected end of expression/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"fn(...);\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unexpected end of expression/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"while (true) { break 1; }\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unexpected token '1'/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"while (true) { continue 1; }\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unexpected token '1'/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"for (const { a } of) {}\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unexpected end of expression/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"const { ...rest, value } = obj;\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Rest element must be last element/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"function fn(...[rest], last) { return last; }\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Rest parameter must be last formal parameter/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"yield 1;\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unexpected token/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"function* gen(){ yield* ; }\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unexpected end of expression/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"async function fn(){ await ; }\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unexpected end of expression/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"async function* gen(){ yield* ; }\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unexpected end of expression/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"switch (x) { case: break; }\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unexpected end of expression/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"switch (x) { case 1 break; }\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/switch|case|Unexpected/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"1++;\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Invalid left-hand side expression in postfix operation/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"--1;\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/left-hand side|Unexpected/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"async function run() { for await (const key in obj) {} }\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unexpected token 'in'/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"async function run(){ for await (const item of) {} }\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unexpected end of expression/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"const re = /(/;\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Invalid regular expression/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"const re = /a/zz;\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Invalid flags/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"tag`${}`;\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unexpected end of expression/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"tag`${value`;\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unclosed/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"return eval(\\\"({a:1,,b:2})\\\");\",\n    \"evalExpect\": \"error\",\n    \"safeExpect\": \"/Unexpected token ,/\",\n    \"category\": \"Syntax Errors\"\n  },\n  {\n    \"code\": \"const a = 1; const b = 2; return `${a} + ${b} = ${a+b}`\",\n    \"evalExpect\": \"1 + 2 = 3\",\n    \"safeExpect\": \"1 + 2 = 3\",\n    \"category\": \"Template Literals\"\n  },\n  {\n    \"code\": \"const name = 'world'; return `hello ${name}`\",\n    \"evalExpect\": \"hello world\",\n    \"safeExpect\": \"hello world\",\n    \"category\": \"Template Literals\"\n  },\n  {\n    \"code\": \"function tag(strings, ...values) { return strings[0] + values[0] + strings[1]; } return tag`hello ${\\\"world\\\"}`\",\n    \"evalExpect\": \"hello world\",\n    \"safeExpect\": \"hello world\",\n    \"category\": \"Template Literals\"\n  },\n  {\n    \"code\": \"function tag(strings, ...values) { return values.reduce((acc, val, i) => acc + val + strings[i + 1], strings[0]); } return tag`a${1}b${2}c${3}d`\",\n    \"evalExpect\": \"a1b2c3d\",\n    \"safeExpect\": \"a1b2c3d\",\n    \"category\": \"Template Literals\"\n  },\n  {\n    \"code\": \"const tagging = () => tag; function tag(strings, ...values) { return values.reduce((acc, val, i) => acc + val + strings[i + 1], strings[0]); } return tagging()`a${1}b${2}c${3}d`\",\n    \"evalExpect\": \"a1b2c3d\",\n    \"safeExpect\": \"a1b2c3d\",\n    \"category\": \"Template Literals\"\n  },\n  {\n    \"code\": \"function tag(strings) { return strings[0]; } return tag`static template`\",\n    \"evalExpect\": \"static template\",\n    \"safeExpect\": \"static template\",\n    \"category\": \"Template Literals\"\n  },\n  {\n    \"code\": \"const tag = (strings, ...values) => strings.length; return tag`test`\",\n    \"evalExpect\": 1,\n    \"safeExpect\": 1,\n    \"category\": \"Template Literals\"\n  },\n  {\n    \"code\": \"const multiply = (strings, a, b) => a * b; return multiply`${2} * ${3}`\",\n    \"evalExpect\": 6,\n    \"safeExpect\": 6,\n    \"category\": \"Template Literals\"\n  },\n  {\n    \"code\": \"const tag = (s, ...v) => v.length; return tag`${1}${2}${3}`\",\n    \"evalExpect\": 3,\n    \"safeExpect\": 3,\n    \"category\": \"Template Literals\"\n  },\n  {\n    \"code\": \"const obj = { tag(s, v) { return v * 2; } }; return obj.tag`${5}`\",\n    \"evalExpect\": 10,\n    \"safeExpect\": 10,\n    \"category\": \"Template Literals\"\n  },\n  {\n    \"code\": \"const tag = s => s.join(\\\"-\\\"); return tag`a${\\\"b\\\"}c`\",\n    \"evalExpect\": \"a-c\",\n    \"safeExpect\": \"a-c\",\n    \"category\": \"Template Literals\"\n  },\n  {\n    \"code\": \"const inner = \\\"world\\\"; return `hello ${`${inner.toUpperCase()}!`}`\",\n    \"evalExpect\": \"hello WORLD!\",\n    \"safeExpect\": \"hello WORLD!\",\n    \"category\": \"Template Literals\"\n  },\n  {\n    \"code\": \"const values = [1, 2]; return `${values.map(v => `${v * 2}`).join(\\\",\\\")}`\",\n    \"evalExpect\": \"2,4\",\n    \"safeExpect\": \"2,4\",\n    \"category\": \"Template Literals\"\n  }\n]\n"
  },
  {
    "path": "test/evalCompletionValue.spec.ts",
    "content": "import Sandbox from '../src/Sandbox.js';\n\ndescribe('eval() Completion Value Tests', () => {\n  let sandbox: Sandbox;\n\n  beforeEach(() => {\n    sandbox = new Sandbox();\n  });\n\n  describe('Declaration statements should return undefined', () => {\n    it('should return undefined for var declaration', () => {\n      const code = 'return eval(\"var x = 5;\");';\n      const fn = sandbox.compile(code);\n      const result = fn().run();\n      expect(result).toBeUndefined();\n    });\n\n    it('should return undefined for let declaration', () => {\n      const code = 'return eval(\"let x = 5;\");';\n      const fn = sandbox.compile(code);\n      const result = fn().run();\n      expect(result).toBeUndefined();\n    });\n\n    it('should return undefined for const declaration', () => {\n      const code = 'return eval(\"const x = 5;\");';\n      const fn = sandbox.compile(code);\n      const result = fn().run();\n      expect(result).toBeUndefined();\n    });\n\n    it('should return undefined for function declaration', () => {\n      const code = 'return eval(\"function foo() { return 42; }\");';\n      const fn = sandbox.compile(code);\n      const result = fn().run();\n      expect(result).toBeUndefined();\n    });\n\n    // Note: Class declarations are not supported by the parser yet\n    // it('should return undefined for class declaration', () => {\n    //   const code = 'return eval(\"class Foo {}\");';\n    //   const fn = sandbox.compile(code);\n    //   const result = fn().run();\n    //   expect(result).toBeUndefined();\n    // });\n  });\n\n  describe('Expression statements should return the value', () => {\n    it('should return value for numeric expression', () => {\n      const code = 'return eval(\"5 + 3\");';\n      const fn = sandbox.compile(code);\n      const result = fn().run();\n      expect(result).toBe(8);\n    });\n\n    it('should return value for string expression', () => {\n      const code = 'return eval(\"\\\\\"hello\\\\\" + \\\\\" world\\\\\"\");';\n      const fn = sandbox.compile(code);\n      const result = fn().run();\n      expect(result).toBe('hello world');\n    });\n\n    it('should return value for assignment expression', () => {\n      const code = 'return eval(\"var x; x = 10\");';\n      const fn = sandbox.compile(code);\n      const result = fn().run();\n      expect(result).toBe(10);\n    });\n\n    it('should return value for function call', () => {\n      const code = 'return eval(\"Math.max(1, 2, 3)\");';\n      const fn = sandbox.compile(code);\n      const scope = { Math };\n      const result = fn(scope).run();\n      expect(result).toBe(3);\n    });\n\n    it('should return value for object literal', () => {\n      const code = 'return eval(\"({ a: 1, b: 2 })\");';\n      const fn = sandbox.compile(code);\n      const result = fn().run();\n      expect(result).toEqual({ a: 1, b: 2 });\n    });\n\n    it('should return value for array literal', () => {\n      const code = 'return eval(\"[1, 2, 3]\");';\n      const fn = sandbox.compile(code);\n      const result = fn().run();\n      expect(result).toEqual([1, 2, 3]);\n    });\n  });\n\n  describe('Control flow statements should return undefined', () => {\n    it('should return undefined for if statement', () => {\n      const code = 'return eval(\"if (true) { 5; }\");';\n      const fn = sandbox.compile(code);\n      const result = fn().run();\n      expect(result).toBeUndefined();\n    });\n\n    it('should return undefined for for loop', () => {\n      const code = 'return eval(\"for (let i = 0; i < 3; i++) {}\");';\n      const fn = sandbox.compile(code);\n      const result = fn().run();\n      expect(result).toBeUndefined();\n    });\n\n    it('should return undefined for while loop', () => {\n      const code = 'return eval(\"var i = 0; while (i < 3) { i++; }\");';\n      const fn = sandbox.compile(code);\n      const result = fn().run();\n      expect(result).toBeUndefined();\n    });\n\n    it('should return undefined for switch statement', () => {\n      const code = 'return eval(\"switch (1) { case 1: break; }\");';\n      const fn = sandbox.compile(code);\n      const result = fn().run();\n      expect(result).toBeUndefined();\n    });\n\n    it('should return undefined for try-catch', () => {\n      const code = 'return eval(\"try { 5; } catch (e) {}\");';\n      const fn = sandbox.compile(code);\n      const result = fn().run();\n      expect(result).toBeUndefined();\n    });\n  });\n\n  describe('Empty statements should return undefined', () => {\n    it('should return undefined for empty statement', () => {\n      const code = 'return eval(\";\");';\n      const fn = sandbox.compile(code);\n      const result = fn().run();\n      expect(result).toBeUndefined();\n    });\n\n    it('should return undefined for empty code', () => {\n      const code = 'return eval(\"\");';\n      const fn = sandbox.compile(code);\n      const result = fn().run();\n      expect(result).toBeUndefined();\n    });\n  });\n\n  describe('Mixed statements - last statement determines completion value', () => {\n    it('should return value when last is expression after declaration', () => {\n      const code = 'return eval(\"let x = 5; x + 3\");';\n      const fn = sandbox.compile(code);\n      const result = fn().run();\n      expect(result).toBe(8);\n    });\n\n    it('should return undefined when last is declaration after expression', () => {\n      const code = 'return eval(\"5 + 3; let x = 10\");';\n      const fn = sandbox.compile(code);\n      const result = fn().run();\n      expect(result).toBeUndefined();\n    });\n\n    it('should return value from expression after control flow', () => {\n      const code = 'return eval(\"if (true) {} 42\");';\n      const fn = sandbox.compile(code);\n      const result = fn().run();\n      expect(result).toBe(42);\n    });\n  });\n\n  describe('Return and throw should work as-is', () => {\n    it('should return value for explicit return', () => {\n      const code = 'return eval(\"return 42\");';\n      const fn = sandbox.compile(code);\n      const result = fn().run();\n      expect(result).toBe(42);\n    });\n\n    it('should throw for explicit throw', () => {\n      const code = 'return eval(\"throw new Error(\\\\\"test\\\\\")\");';\n      const fn = sandbox.compile(code);\n      expect(() => fn().run()).toThrow('test');\n    });\n\n    it('should not double-wrap explicit return', () => {\n      const code = 'return eval(\"return { value: 123 }\");';\n      const fn = sandbox.compile(code);\n      const result = fn().run();\n      expect(result).toEqual({ value: 123 });\n    });\n  });\n\n  describe('Should not leak internal Prop objects', () => {\n    it('should not return Prop object for let declaration', () => {\n      const code = 'return eval(\"let x = 5\");';\n      const fn = sandbox.compile(code);\n      const result = fn().run();\n\n      // Should be undefined, not a Prop object\n      expect(result).toBeUndefined();\n\n      // Verify it's not an object with internal properties\n      expect(typeof result).not.toBe('object');\n    });\n\n    it('should not return Prop object for const declaration', () => {\n      const code = 'return eval(\"const x = 5\");';\n      const fn = sandbox.compile(code);\n      const result = fn().run();\n\n      expect(result).toBeUndefined();\n      expect(typeof result).not.toBe('object');\n    });\n\n    it('should not return Prop object for var declaration', () => {\n      const code = 'return eval(\"var x = 5\");';\n      const fn = sandbox.compile(code);\n      const result = fn().run();\n\n      expect(result).toBeUndefined();\n      expect(typeof result).not.toBe('object');\n    });\n  });\n\n  describe('Matches native JavaScript eval behavior', () => {\n    it('should match eval() for declarations', () => {\n      const nativeResult = eval('let x = 5; undefined;'); // eval returns undefined for declarations\n      const code = 'return eval(\"let x = 5\");';\n      const fn = sandbox.compile(code);\n      const sandboxResult = fn().run();\n\n      expect(sandboxResult).toBe(nativeResult);\n    });\n\n    it('should match eval() for expressions', () => {\n      const nativeResult = eval('5 + 3');\n      const code = 'return eval(\"5 + 3\");';\n      const fn = sandbox.compile(code);\n      const sandboxResult = fn().run();\n\n      expect(sandboxResult).toBe(nativeResult);\n    });\n\n    it('should match eval() for assignment', () => {\n      let x: number;\n      const nativeResult = eval('x = 10');\n      const code = 'return eval(\"var x; x = 10\");';\n      const fn = sandbox.compile(code);\n      const sandboxResult = fn().run();\n\n      expect(sandboxResult).toBe(nativeResult);\n    });\n  });\n});\n"
  },
  {
    "path": "test/expression.spec.ts",
    "content": "import Sandbox from '../src/Sandbox.js';\n\ndescribe('Expression Compilation Tests', () => {\n  describe('compileExpression - only first statement/expression', () => {\n    it('should only compile first statement, ignoring rest', () => {\n      const sandbox = new Sandbox();\n      let result = 0;\n      const code = 'result = 1; result = 2';\n      const fn = sandbox.compileExpression(code);\n      const scope = { result };\n      fn(scope).run();\n      // Only first statement executes\n      expect(scope.result).toBe(1);\n    });\n\n    it('should only compile first expression with semicolons', () => {\n      const sandbox = new Sandbox();\n      let a = 0;\n      let b = 0;\n      const code = 'a = 5; b = 10';\n      const fn = sandbox.compileExpression(code);\n      const scope = { a, b };\n      fn(scope).run();\n      expect(scope.a).toBe(5);\n      expect(scope.b).toBe(0); // Second statement not executed\n    });\n\n    it('should compile single expression', () => {\n      const sandbox = new Sandbox();\n      let x = 0;\n      const code = 'x = 42';\n      const fn = sandbox.compileExpression(code);\n      const scope = { x };\n      fn(scope).run();\n      expect(scope.x).toBe(42);\n    });\n\n    it('should handle ternary operator', () => {\n      const sandbox = new Sandbox();\n      let result = '';\n      const code = 'result = (value > 5 ? \"big\" : \"small\")';\n      const fn = sandbox.compileExpression(code);\n      const scope1 = { result, value: 10 };\n      const scope2 = { result: '', value: 3 };\n      fn(scope1).run();\n      fn(scope2).run();\n      expect(scope1.result).toBe('big');\n      expect(scope2.result).toBe('small');\n    });\n\n    it('should execute function call in expression', () => {\n      const sandbox = new Sandbox();\n      let result = 0;\n      const fn = sandbox.compileExpression('result = callback()');\n      const scope = {\n        result,\n        callback: () => 99,\n      };\n      fn(scope).run();\n      expect(scope.result).toBe(99);\n    });\n  });\n\n  describe('compileExpression - allows functions with statements', () => {\n    it('should allow IIFE with multiple statements', () => {\n      const sandbox = new Sandbox();\n      let result = 0;\n      const code = 'result = (() => { let a = 1; let b = 2; return a + b; })()';\n      const fn = sandbox.compileExpression(code);\n      const scope = { result };\n      fn(scope).run();\n      expect(scope.result).toBe(3);\n    });\n\n    it('should allow arrow function with multiple statements', () => {\n      const sandbox = new Sandbox();\n      let result = 0;\n      const code = 'result = ((x) => { const y = x * 2; const z = y + 3; return z; })(5)';\n      const fn = sandbox.compileExpression(code);\n      const scope = { result };\n      fn(scope).run();\n      expect(scope.result).toBe(13);\n    });\n\n    it('should allow function expression with statements', () => {\n      const sandbox = new Sandbox();\n      let result = 0;\n      const code = 'result = (function() { let x = 10; let y = 20; return x + y; })()';\n      const fn = sandbox.compileExpression(code);\n      const scope = { result };\n      fn(scope).run();\n      expect(scope.result).toBe(30);\n    });\n\n    it('should allow nested functions with loops', () => {\n      const sandbox = new Sandbox();\n      let result = 0;\n      const code = `\n        result = ((a) => {\n          const helper = (b) => {\n            let sum = 0;\n            for (let i = 0; i < b; i++) {\n              sum += i;\n            }\n            return sum;\n          };\n          return helper(a);\n        })(5)\n      `;\n      const fn = sandbox.compileExpression(code);\n      const scope = { result };\n      fn(scope).run();\n      expect(scope.result).toBe(10); // 0+1+2+3+4\n    });\n\n    it('should allow functions with if statements', () => {\n      const sandbox = new Sandbox();\n      let result = '';\n      const code = `\n        result = ((x) => {\n          if (x > 10) {\n            return 'greater';\n          } else {\n            return 'less';\n          }\n        })(15)\n      `;\n      const fn = sandbox.compileExpression(code);\n      const scope = { result };\n      fn(scope).run();\n      expect(scope.result).toBe('greater');\n    });\n\n    it('should allow functions with while loops', () => {\n      const sandbox = new Sandbox();\n      let result = 0;\n      const code = `\n        result = (() => {\n          let i = 0;\n          let sum = 0;\n          while (i < 5) {\n            sum += i;\n            i++;\n          }\n          return sum;\n        })()\n      `;\n      const fn = sandbox.compileExpression(code);\n      const scope = { result };\n      fn(scope).run();\n      expect(scope.result).toBe(10); // 0+1+2+3+4\n    });\n\n    it('should allow functions with try-catch', () => {\n      const sandbox = new Sandbox();\n      let result = 0;\n      const code = `\n        result = (() => {\n          try {\n            let x = 10;\n            return x * 2;\n          } catch (e) {\n            return 0;\n          }\n        })()\n      `;\n      const fn = sandbox.compileExpression(code);\n      const scope = { result };\n      fn(scope).run();\n      expect(scope.result).toBe(20);\n    });\n\n    it('should allow functions with switch statements', () => {\n      const sandbox = new Sandbox();\n      let result = '';\n      const code = `\n        result = ((x) => {\n          let res;\n          switch (x) {\n            case 1:\n              res = 'one';\n              break;\n            case 2:\n              res = 'two';\n              break;\n            default:\n              res = 'other';\n          }\n          return res;\n        })(2)\n      `;\n      const fn = sandbox.compileExpression(code);\n      const scope = { result };\n      fn(scope).run();\n      expect(scope.result).toBe('two');\n    });\n\n    it('should allow functions with variable declarations', () => {\n      const sandbox = new Sandbox();\n      let result = 0;\n      const code = `\n        result = (() => {\n          let x = 5;\n          const y = 10;\n          var z = 15;\n          return x + y + z;\n        })()\n      `;\n      const fn = sandbox.compileExpression(code);\n      const scope = { result };\n      fn(scope).run();\n      expect(scope.result).toBe(30);\n    });\n\n    it('should allow functions with for loops', () => {\n      const sandbox = new Sandbox();\n      let result = 0;\n      const code = `\n        result = (() => {\n          let total = 0;\n          for (let i = 1; i <= 5; i++) {\n            total += i;\n          }\n          return total;\n        })()\n      `;\n      const fn = sandbox.compileExpression(code);\n      const scope = { result };\n      fn(scope).run();\n      expect(scope.result).toBe(15); // 1+2+3+4+5\n    });\n  });\n\n  describe('compileExpressionAsync - only first statement/expression', () => {\n    it('should only compile first async statement', async () => {\n      const sandbox = new Sandbox();\n      let result = 0;\n      const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));\n      const code = 'result = await delay(10).then(() => 1); result = 2';\n      const fn = sandbox.compileExpressionAsync(code);\n      const scope = { result, delay };\n      await fn(scope).run();\n      // Only first statement executes\n      expect(scope.result).toBe(1);\n    });\n\n    it('should compile single await expression', async () => {\n      const sandbox = new Sandbox();\n      let result = 0;\n      const code = 'result = await Promise.resolve(42)';\n      const fn = sandbox.compileExpressionAsync(code);\n      const scope = { result };\n      await fn(scope).run();\n      expect(scope.result).toBe(42);\n    });\n  });\n\n  describe('compileExpressionAsync - allows async functions with statements', () => {\n    it('should allow async IIFE with multiple statements', async () => {\n      const sandbox = new Sandbox();\n      let result = 0;\n      const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));\n      const code = `\n        result = await (async () => {\n          let a = 1;\n          await delay(10);\n          let b = 2;\n          return a + b;\n        })()\n      `;\n      const fn = sandbox.compileExpressionAsync(code);\n      const scope = { result, delay };\n      await fn(scope).run();\n      expect(scope.result).toBe(3);\n    });\n\n    it('should allow async function with for-of loop', async () => {\n      const sandbox = new Sandbox();\n      let result = 0;\n      const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));\n      const code = `\n        result = await (async (arr) => {\n          let sum = 0;\n          for (const item of arr) {\n            await delay(5);\n            sum += item;\n          }\n          return sum;\n        })([1, 2, 3, 4, 5])\n      `;\n      const fn = sandbox.compileExpressionAsync(code);\n      const scope = { result, delay };\n      await fn(scope).run();\n      expect(scope.result).toBe(15);\n    });\n\n    it('should allow async function with try-catch', async () => {\n      const sandbox = new Sandbox();\n      let result = 0;\n      const code = `\n        result = await (async () => {\n          try {\n            const x = await Promise.resolve(10);\n            return x * 2;\n          } catch (e) {\n            return 0;\n          }\n        })()\n      `;\n      const fn = sandbox.compileExpressionAsync(code);\n      const scope = { result };\n      await fn(scope).run();\n      expect(scope.result).toBe(20);\n    });\n\n    it('should allow Promise.all', async () => {\n      const sandbox = new Sandbox();\n      let result: number[] = [];\n      const delay = (ms: number, val: number) =>\n        new Promise<number>((resolve) => setTimeout(() => resolve(val), ms));\n      const code = 'result = await Promise.all([delay(10, 1), delay(10, 2), delay(10, 3)])';\n      const fn = sandbox.compileExpressionAsync(code);\n      const scope = { result, delay };\n      await fn(scope).run();\n      expect(scope.result).toEqual([1, 2, 3]);\n    });\n\n    it('should allow async arrow function with conditionals', async () => {\n      const sandbox = new Sandbox();\n      let result = '';\n      const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));\n      const code = `\n        result = await (async (x) => {\n          await delay(5);\n          if (x > 10) {\n            return 'big';\n          } else if (x > 5) {\n            return 'medium';\n          } else {\n            return 'small';\n          }\n        })(7)\n      `;\n      const fn = sandbox.compileExpressionAsync(code);\n      const scope = { result, delay };\n      await fn(scope).run();\n      expect(scope.result).toBe('medium');\n    });\n  });\n});\n"
  },
  {
    "path": "test/parse.spec.ts",
    "content": "import { ParseError } from '../src/parser';\nimport Sandbox from '../src/Sandbox';\n\ndescribe('Sandbox.parse() tests', () => {\n  it('Sandbox.parse()', () => {\n    const code = 'return 1 + 2;';\n    const ast = Sandbox.parse(code);\n    expect(ast).toBeDefined();\n  });\n\n  it('Sandbox.parse() with syntax error', () => {\n    const code = 'return 1 + ;';\n    expect(() => {\n      Sandbox.parse(code);\n    }).toThrow(ParseError);\n  });\n});\n"
  },
  {
    "path": "test/performance.mjs",
    "content": "import { Sandbox } from '../dist/esm/Sandbox.js'\nimport { Bench } from 'tinybench';\nimport Table from 'cli-table3';\nimport chalk from 'chalk';\n\n\nconst bench = new Bench({ name: 'simple benchmark', time: 2000 });\n\nconst sandbox = new Sandbox();\n\nconst code = `\nlet a = [1];\nlet count = 1000;\nwhile(count--) {\n  a.push(0);\n}\nreturn a;\n`;\n\nconst codeComplex = `\nfunction LinkedListNode(value) {\n  this.value = value;\n  this.next = null;\n}\n\nfunction reverse(head) {\n  let node = head,\n    previous,\n    tmp;\n\n  while (node) {\n    // save next before we overwrite node.next!\n    tmp = node.next;\n\n    // reverse pointer\n    node.next = previous;\n\n    // step forward in the list\n    previous = node;\n    node = tmp;\n  }\n\n  return previous;\n}\n\nfunction reverse(head) {\n  if (!head || !head.next) {\n    return head;\n  }\n  let tmp = reverse(head.next);\n  head.next.next = head;\n  head.next = undefined;\n  return tmp;\n}\n\nconst root = new LinkedListNode(0);\nlet current = root;\nfor (let i = 1; i < 100; i++) {\n  const node = new LinkedListNode(i);\n  current.next = node;\n  current = node;\n}\n\nreverse(root);\n`;\n\nconst ciMultiplier = process.env.CI ? 1.3 : 1;\n\nconst tests = [\n  {\n    name: '1k loop',\n    code,\n    max: Math.round(2.950 * ciMultiplier * 1000)/1000\n  },\n  {\n    name: 'reverse linked list',\n    code: codeComplex,\n    max: Math.round(1.750 * ciMultiplier * 1000)/1000\n  }\n];\n\n// const exec = sandbox.compile(codeComplex);\n// exec({}).run();\n\n\nconst passes = (bool) => bool ? chalk.green('PASS') : chalk.red('FAIL');\nconst tpColor = (max, tp) => {\n  const ratio = max / tp;\n  return ratio < 1 ? chalk.red(tp) : ratio < 1.1 ? chalk.yellow(tp) : ratio < 1.3 ? chalk.green(tp) : chalk.blue(tp);\n}\n\n(async () => {\n  for (const test of tests) {\n    const exec = sandbox.compile(test.code);\n    bench.add(test.name, () => exec({}).run());\n    // const func = new Function(test.code);\n    // bench.add(test.name, ()=>func()) \n  }\n  const start = performance.now();\n  await bench.run();\n  const duration = Math.round((performance.now() - start)/100)/10;\n\n  const output = bench.results.map((res, i) => {\n    const test = tests[i];\n    if (res.state !== 'completed') return [bench.tasks[i].name, '-', '-', '-', '-'];\n    const tp = Math.round(res.latency.p50*10000)/10000;\n    const exec = sandbox.compile(test.code)({});\n    exec.context.ctx.ticks.ticks = 0n;\n    exec.run();\n    return Object.values({\n      name: bench.tasks[i].name,\n      latency: tpColor(test.max, tp),\n      maximum: tests[i].max.toString(),\n      ticks: exec.context.ctx.ticks.ticks,\n      passes: passes(test.max > tp)\n    })\n  })\n  \n\n  const table = new Table({\n    head: ['name', 'latency (ms)', 'fail at (ms)', 'ticks', 'result'],\n    style: {\n      head: []\n    }\n  })\n  // console.log(bench.results)\n  table.push(...output);\n  // console.table(bench.table());\n  console.log(table.toString());\n  console.log(`ran for ${duration}s`);\n\n  let fail = false;\n  for (const i in bench.results) {\n    const res = bench.results[i];\n    const test = tests[i];\n    if (res.state === 'errored') {\n      console.error(chalk.red(`ERROR: ${test.name} - ${res.error}`));\n      fail = true;\n    }\n    if (res.state !== 'completed') continue;\n    \n    if (res.latency.p50 > test.max / .99) {\n      console.error(chalk.red(`FAILED: ${test.name} (${(res.latency.p50 / test.max * 100).toFixed(2)}%)`));\n      fail = true;\n    }\n  }\n  if (fail) process.exit(1);\n})();\n"
  },
  {
    "path": "test/sandboxErrorCatch.spec.ts",
    "content": "import Sandbox from '../src/Sandbox.js';\n\ndescribe('SandboxError catch behavior', () => {\n  describe('SandboxError is not catchable by sandboxed code', () => {\n    it('sandboxed try/catch cannot catch a SandboxError — it propagates out', () => {\n      const sandbox = new Sandbox({ forbidFunctionCalls: true });\n      const scope: Record<string, unknown> = { catchRan: false };\n      const fn = sandbox.compile(`\n        try {\n          Math.abs(-1);\n        } catch (e) {\n          catchRan = true;\n        }\n      `);\n      expect(() => fn(scope).run()).toThrow('Function invocations are not allowed');\n      expect(scope['catchRan']).toBe(false);\n    });\n\n    it('sandboxed try/catch cannot catch a function-creation SandboxError', () => {\n      const sandbox = new Sandbox({ forbidFunctionCreation: true });\n      const scope: Record<string, unknown> = { catchRan: false };\n      const fn = sandbox.compile(`\n        try {\n          (() => 1)();\n        } catch (e) {\n          catchRan = true;\n        }\n      `);\n      expect(() => fn(scope).run()).toThrow('Function creation is forbidden');\n      expect(scope['catchRan']).toBe(false);\n    });\n\n    it('sandboxed try/catch cannot catch a regex SandboxError', () => {\n      const sandbox = new Sandbox({ globals: {} });\n      const scope: Record<string, unknown> = { catchRan: false };\n      const fn = sandbox.compile(`\n        try {\n          /test/;\n        } catch (e) {\n          catchRan = true;\n        }\n      `);\n      expect(() => fn(scope).run()).toThrow('Regex not permitted');\n      expect(scope['catchRan']).toBe(false);\n    });\n\n    it('finally does not run when a SandboxError occurs', () => {\n      const sandbox = new Sandbox({ forbidFunctionCreation: true });\n      const scope: Record<string, unknown> = { finallyRan: false };\n      const fn = sandbox.compile(`\n        try {\n          (() => 1)();\n        } catch (e) {\n          // should not run\n        } finally {\n          finallyRan = true;\n        }\n      `);\n      expect(() => fn(scope).run()).toThrow('Function creation is forbidden');\n      expect(scope['finallyRan']).toBe(false);\n    });\n\n    it('finally does not run without a catch block when SandboxError occurs', () => {\n      const sandbox = new Sandbox({ forbidFunctionCreation: true });\n      const scope: Record<string, unknown> = { finallyRan: false };\n      const fn = sandbox.compile(`\n        try {\n          (() => 1)();\n        } finally {\n          finallyRan = true;\n        }\n      `);\n      expect(() => fn(scope).run()).toThrow('Function creation is forbidden');\n      expect(scope['finallyRan']).toBe(false);\n    });\n\n    it('nested try/catch: inner catch does not intercept SandboxError, outer also cannot', () => {\n      const sandbox = new Sandbox({ forbidFunctionCalls: true });\n      const scope: Record<string, unknown> = { innerCaught: false, outerCaught: false };\n      const fn = sandbox.compile(`\n        try {\n          try {\n            Math.abs(-1);\n          } catch (inner) {\n            innerCaught = true;\n          }\n        } catch (outer) {\n          outerCaught = true;\n        }\n      `);\n      expect(() => fn(scope).run()).toThrow('Function invocations are not allowed');\n      expect(scope['innerCaught']).toBe(false);\n      expect(scope['outerCaught']).toBe(false);\n    });\n\n    it('non-SandboxError can still be caught normally', () => {\n      const sandbox = new Sandbox();\n      const scope: Record<string, unknown> = { caught: false };\n      const fn = sandbox.compile(`\n        try {\n          throw new Error('regular error');\n        } catch (e) {\n          caught = true;\n        }\n      `);\n      fn(scope).run();\n      expect(scope['caught']).toBe(true);\n    });\n\n    it('execution after a caught non-SandboxError continues normally', () => {\n      const sandbox = new Sandbox();\n      const fn = sandbox.compile(`\n        let x = 0;\n        try {\n          throw new Error('oops');\n        } catch (e) {\n          x = 1;\n        }\n        return x + 10;\n      `);\n      expect(fn({}).run()).toBe(11);\n    });\n  });\n\n  describe('when haltOnSandboxError is true', () => {\n    it('halt fires when SandboxError escapes (no try/catch)', () => {\n      const sandbox = new Sandbox({ forbidFunctionCalls: true, haltOnSandboxError: true });\n      let haltFired = false;\n      let haltError: Error | undefined;\n\n      sandbox.subscribeHalt((args) => {\n        haltFired = true;\n        haltError = args?.error;\n      });\n\n      sandbox.compile('Math.abs(-1)')({}).run();\n\n      expect(haltFired).toBe(true);\n      expect(haltError?.message).toBe('Function invocations are not allowed');\n    });\n\n    it('halt fires when SandboxError escapes a try/catch (catch does not run)', () => {\n      const sandbox = new Sandbox({ forbidFunctionCalls: true, haltOnSandboxError: true });\n      let haltFired = false;\n      const scope: Record<string, unknown> = { catchRan: false };\n\n      sandbox.subscribeHalt(() => {\n        haltFired = true;\n      });\n\n      const fn = sandbox.compile(`\n        try {\n          Math.abs(-1);\n        } catch (e) {\n          catchRan = true;\n        }\n      `);\n      fn(scope).run();\n\n      expect(haltFired).toBe(true);\n      expect(scope['catchRan']).toBe(false);\n    });\n\n    it('halt fires when SandboxError occurs inside a try/finally', () => {\n      const sandbox = new Sandbox({ forbidFunctionCreation: true, haltOnSandboxError: true });\n      let haltFired = false;\n\n      sandbox.subscribeHalt(() => {\n        haltFired = true;\n      });\n\n      sandbox\n        .compile(\n          `\n        try {\n          (() => 1)();\n        } finally {\n          // finally does not prevent halt\n        }\n      `,\n        )({})\n        .run();\n\n      expect(haltFired).toBe(true);\n    });\n\n    it('non-SandboxError can still be caught — halt does not fire', () => {\n      const sandbox = new Sandbox({ haltOnSandboxError: true });\n      let haltFired = false;\n      const scope: Record<string, unknown> = { caught: false };\n\n      sandbox.subscribeHalt(() => {\n        haltFired = true;\n      });\n\n      sandbox\n        .compile(\n          `\n        try {\n          throw new Error('regular error');\n        } catch (e) {\n          caught = true;\n        }\n      `,\n        )(scope)\n        .run();\n\n      expect(haltFired).toBe(false);\n      expect(scope['caught']).toBe(true);\n    });\n  });\n});\n"
  },
  {
    "path": "test/sandboxRestrictions.spec.ts",
    "content": "import Sandbox from '../src/Sandbox.js';\n\ndescribe('Executor Edge Cases', () => {\n  describe('Function creation restrictions', () => {\n    it('should throw when function creation is forbidden', () => {\n      const sandbox = new Sandbox({ forbidFunctionCreation: true });\n      const fn = sandbox.compile('(function() { return 1; })()');\n      expect(() => {\n        fn({}).run();\n      }).toThrow('Function creation is forbidden');\n    });\n\n    it('should throw when arrow function creation is forbidden', () => {\n      const sandbox = new Sandbox({ forbidFunctionCreation: true });\n      const fn = sandbox.compile('(() => 1)()');\n      expect(() => {\n        fn({}).run();\n      }).toThrow('Function creation is forbidden');\n    });\n\n    it('should throw when async function creation is forbidden', () => {\n      const sandbox = new Sandbox({ forbidFunctionCreation: true });\n      const fn = sandbox.compile('(async function() { return 1; })');\n      expect(() => {\n        fn({}).run();\n      }).toThrow('Function creation is forbidden');\n    });\n\n    it('should throw when async arrow function creation is forbidden', () => {\n      const sandbox = new Sandbox({ forbidFunctionCreation: true });\n      const fn = sandbox.compile('(async () => 1)');\n      expect(() => {\n        fn({}).run();\n      }).toThrow('Function creation is forbidden');\n    });\n\n    it('should throw when generator creation is forbidden', () => {\n      const sandbox = new Sandbox({ forbidFunctionCreation: true });\n      const fn = sandbox.compile('(function* () { yield 1; })().next().value');\n      expect(() => {\n        fn({}).run();\n      }).toThrow('Function creation is forbidden');\n    });\n\n    it('should throw when async generator creation is forbidden', () => {\n      const sandbox = new Sandbox({ forbidFunctionCreation: true });\n      const fn = sandbox.compile('(async function* () { yield 1; })()');\n      expect(() => {\n        fn({}).run();\n      }).toThrow('Function creation is forbidden');\n    });\n\n    it('should throw when async generators are not permitted', () => {\n      const sandbox = new Sandbox({ prototypeWhitelist: new Map() });\n      const fn = sandbox.compile('(async function* () { yield 1; })()');\n      expect(() => {\n        fn({}).run();\n      }).toThrow('Async/await not permitted');\n    });\n  });\n\n  describe('Function call restrictions', () => {\n    it('should throw when function calls are forbidden', () => {\n      const sandbox = new Sandbox({ forbidFunctionCalls: true });\n      const fn = sandbox.compile('Math.abs(-1)');\n      expect(() => {\n        fn({}).run();\n      }).toThrow('Function invocations are not allowed');\n    });\n  });\n\n  describe('Regex restrictions', () => {\n    it('should throw when regex is not permitted', () => {\n      const sandbox = new Sandbox({ globals: {} });\n      const fn = sandbox.compile('/test/');\n      expect(() => {\n        fn({}).run();\n      }).toThrow('Regex not permitted');\n    });\n  });\n\n  describe('haltOnSandboxError option', () => {\n    it('should halt execution when SandboxError is thrown and haltOnSandboxError is true', () => {\n      const sandbox = new Sandbox({\n        forbidFunctionCalls: true,\n        haltOnSandboxError: true,\n      });\n      let haltCalled = false;\n      let haltError: Error | undefined;\n\n      sandbox.subscribeHalt((args) => {\n        haltCalled = true;\n        haltError = args?.error;\n      });\n\n      const fn = sandbox.compile('Math.abs(-1)');\n      const { run } = fn({});\n\n      // Should not throw immediately, but halt instead\n      run();\n\n      expect(haltCalled).toBe(true);\n      expect(haltError).toBeDefined();\n      expect(haltError?.message).toBe('Function invocations are not allowed');\n    });\n\n    it('should throw immediately when SandboxError is thrown and haltOnSandboxError is false', () => {\n      const sandbox = new Sandbox({\n        forbidFunctionCalls: true,\n        haltOnSandboxError: false,\n      });\n      let haltCalled = false;\n\n      sandbox.subscribeHalt(() => {\n        haltCalled = true;\n      });\n\n      const fn = sandbox.compile('Math.abs(-1)');\n\n      expect(() => {\n        fn({}).run();\n      }).toThrow('Function invocations are not allowed');\n\n      expect(haltCalled).toBe(false);\n    });\n\n    it('should throw immediately when SandboxError is thrown and haltOnSandboxError is undefined', () => {\n      const sandbox = new Sandbox({ forbidFunctionCalls: true });\n      const fn = sandbox.compile('Math.abs(-1)');\n\n      expect(() => {\n        fn({}).run();\n      }).toThrow('Function invocations are not allowed');\n    });\n\n    it('should resume and propagate error after halt', (done) => {\n      const sandbox = new Sandbox({\n        forbidFunctionCalls: true,\n        haltOnSandboxError: true,\n      });\n      let haltCalled = false;\n      let errorReceived: Error | undefined;\n\n      sandbox.subscribeHalt((args) => {\n        haltCalled = true;\n        errorReceived = args?.error;\n\n        // Resume after a short delay\n        setTimeout(() => {\n          sandbox.resumeExecution();\n        }, 50);\n      });\n\n      const fn = sandbox.compile('Math.abs(-1)');\n      const { run } = fn({});\n\n      // Run - execution will halt\n      run();\n\n      // Wait for resume to happen\n      setTimeout(() => {\n        expect(haltCalled).toBe(true);\n        expect(errorReceived).toBeDefined();\n        expect(errorReceived?.message).toBe('Function invocations are not allowed');\n        done();\n      }, 100);\n    });\n\n    it('should halt on function creation error when haltOnSandboxError is true', () => {\n      const sandbox = new Sandbox({\n        forbidFunctionCreation: true,\n        haltOnSandboxError: true,\n      });\n      let haltCalled = false;\n      let haltError: Error | undefined;\n\n      sandbox.subscribeHalt((args) => {\n        haltCalled = true;\n        haltError = args?.error;\n      });\n\n      const fn = sandbox.compile('(() => 1)()');\n      const { run } = fn({});\n      run();\n\n      expect(haltCalled).toBe(true);\n      expect(haltError).toBeDefined();\n      expect(haltError?.message).toBe('Function creation is forbidden');\n    });\n\n    it('should halt on regex error when haltOnSandboxError is true', () => {\n      const sandbox = new Sandbox({\n        globals: {},\n        haltOnSandboxError: true,\n      });\n      let haltCalled = false;\n      let haltError: Error | undefined;\n\n      sandbox.subscribeHalt((args) => {\n        haltCalled = true;\n        haltError = args?.error;\n      });\n\n      const fn = sandbox.compile('/test/');\n      const { run } = fn({});\n      run();\n\n      expect(haltCalled).toBe(true);\n      expect(haltError).toBeDefined();\n      expect(haltError?.message).toBe('Regex not permitted');\n    });\n\n    it('should not halt on non-SandboxError exceptions', () => {\n      const sandbox = new Sandbox({ haltOnSandboxError: true });\n      let haltCalled = false;\n\n      sandbox.subscribeHalt(() => {\n        haltCalled = true;\n      });\n\n      const fn = sandbox.compile('throw new Error(\"Regular error\")');\n\n      expect(() => {\n        fn({}).run();\n      }).toThrow('Regular error');\n\n      expect(haltCalled).toBe(false);\n    });\n\n    it('should provide context information when halting', () => {\n      const sandbox = new Sandbox({\n        forbidFunctionCalls: true,\n        haltOnSandboxError: true,\n      });\n      let haltContext: any;\n\n      sandbox.subscribeHalt((args) => {\n        haltContext = args;\n      });\n\n      const fn = sandbox.compile('Math.abs(-1)');\n      const { run } = fn({});\n      run();\n\n      expect(haltContext).toBeDefined();\n      expect(haltContext.error).toBeDefined();\n      expect(haltContext.ticks).toBeDefined();\n      expect(haltContext.scope).toBeDefined();\n      expect(haltContext.context).toBeDefined();\n    });\n  });\n\n  describe('maxParserRecursionDepth option', () => {\n    it('should not throw with default maxParserRecursionDepth and 256 nesting', () => {\n      const sandbox = new Sandbox();\n      const deeplyNested = 'return' + '('.repeat(256) + '1' + ')'.repeat(256);\n      const fn = sandbox.compile(deeplyNested);\n      expect(fn({}).run()).toBe(1);\n    });\n\n    it('should throw when expression nesting exceeds maxParserRecursionDepth', () => {\n      const sandbox = new Sandbox({ maxParserRecursionDepth: 5 });\n      const deeplyNested = '('.repeat(10) + '1' + ')'.repeat(10);\n      expect(() => {\n        sandbox.compile(deeplyNested);\n      }).toThrow('Maximum expression depth exceeded');\n    });\n\n    it('should not throw when expression nesting is within maxParserRecursionDepth', () => {\n      const sandbox = new Sandbox({ maxParserRecursionDepth: 20 });\n      const nested = 'return (((1 + 2)))';\n      const fn = sandbox.compile(nested);\n      expect(fn({}).run()).toBe(3);\n    });\n\n    it('should throw on deeply nested template literals when depth is low', () => {\n      const sandbox = new Sandbox({ maxParserRecursionDepth: 2 });\n      expect(() => {\n        sandbox.compile('`${ `${ `${ 1 }` }` }`');\n      }).toThrow('Maximum expression depth exceeded');\n    });\n\n    it('should parse deeply nested template literals within depth limit', () => {\n      const sandbox = new Sandbox({ maxParserRecursionDepth: 4 });\n      const fn = sandbox.compile('return `${ `${ 1 }` }`');\n      expect(fn({}).run()).toBe('1');\n    });\n\n    it('should apply depth limit when using compileExpression', () => {\n      const sandbox = new Sandbox({ maxParserRecursionDepth: 5 });\n      const deeplyNested = '('.repeat(10) + '1' + ')'.repeat(10);\n      expect(() => {\n        sandbox.compileExpression(deeplyNested);\n      }).toThrow('Maximum expression depth exceeded');\n    });\n\n    it('should apply depth limit when using compileExpressionAsync', () => {\n      const sandbox = new Sandbox({ maxParserRecursionDepth: 5 });\n      const deeplyNested = '('.repeat(10) + '1' + ')'.repeat(10);\n      expect(() => {\n        sandbox.compileExpressionAsync(deeplyNested);\n      }).toThrow('Maximum expression depth exceeded');\n    });\n\n    it('should apply depth limit when using compileAsync', () => {\n      const sandbox = new Sandbox({ maxParserRecursionDepth: 5 });\n      const deeplyNested = '('.repeat(10) + '1' + ')'.repeat(10);\n      expect(() => {\n        sandbox.compileAsync(deeplyNested);\n      }).toThrow('Maximum expression depth exceeded');\n    });\n\n    it('should apply depth limit when calling Function constructor', () => {\n      const sandbox = new Sandbox({ maxParserRecursionDepth: 5 });\n      expect(() => {\n        sandbox\n          .compile('new Function(\"return \" + \"(\".repeat(10) + \"1\" + \")\".repeat(10))()')({})\n          .run();\n      }).toThrow('Maximum expression depth exceeded');\n    });\n\n    it('should throw on deeply nested ternary expressions', () => {\n      const sandbox = new Sandbox({ maxParserRecursionDepth: 5 });\n      const deepTernary = 'true ? '.repeat(10) + '1' + ' : 0'.repeat(10);\n      expect(() => {\n        sandbox.compile(deepTernary);\n      }).toThrow('Maximum expression depth exceeded');\n    });\n\n    it('should not throw on ternary within depth limit', () => {\n      const sandbox = new Sandbox();\n      const fn = sandbox.compile('return true ? true ? 1 : 2 : 3');\n      expect(fn({}).run()).toBe(1);\n    });\n\n    it('should throw on deeply chained right-associative assignments', () => {\n      const sandbox = new Sandbox({ maxParserRecursionDepth: 5 });\n      const deepAssign = 'let x; ' + 'x = '.repeat(10) + '1';\n      expect(() => {\n        sandbox.compile(deepAssign);\n      }).toThrow('Maximum expression depth exceeded');\n    });\n\n    it('should not throw on assignment chain within depth limit', () => {\n      const sandbox = new Sandbox();\n      const fn = sandbox.compile('let a, b, c; a = b = c = 5; return a');\n      expect(fn({}).run()).toBe(5);\n    });\n\n    it('should throw on deeply chained else-if', () => {\n      const sandbox = new Sandbox({ maxParserRecursionDepth: 5 });\n      let code = 'if (false) { 1 }';\n      for (let i = 0; i < 10; i++) code += ' else if (false) { ' + i + ' }';\n      code += ' else { 42 }';\n      expect(() => {\n        sandbox.compile(code);\n      }).toThrow('Maximum expression depth exceeded');\n    });\n\n    it('should not throw on else-if chain within depth limit', () => {\n      const sandbox = new Sandbox();\n      const fn = sandbox.compile(\n        'if (false) { return 1 } else if (false) { return 2 } else { return 3 }',\n      );\n      expect(fn({}).run()).toBe(3);\n    });\n  });\n});\n"
  },
  {
    "path": "test/semicolonInsertion.spec.ts",
    "content": "import Sandbox from '../src/Sandbox.js';\n\ndescribe('Semicolon Insertion Tests', () => {\n  beforeEach(() => {});\n\n  it('should handle one-line if statement without braces', () => {\n    const logs: any[] = [];\n    const mockConsole = { log: (...args: any[]) => logs.push(...args) };\n    const sandbox = new Sandbox({\n      globals: {\n        console: mockConsole,\n      },\n    });\n    const script = `\nconsole.log(1)\n\nif (false) throw new Error('test')\n\nconsole.log(2)\n\nconsole.log(3)\n    `;\n    const jsExecutor = sandbox.compile(script);\n\n    jsExecutor({}).run();\n\n    expect(logs).toEqual([1, 2, 3]);\n  });\n\n  it('should handle do-while loop correctly', () => {\n    const logs: any[] = [];\n    const mockConsole = { log: (...args: any[]) => logs.push(...args) };\n    const sandbox = new Sandbox({\n      globals: {\n        console: mockConsole,\n      },\n    });\n    const script = `\nconsole.log(1)\n\ndo {\n  console.log(2)\n\n  break\n} while (true)\n\nconsole.log(3)\n\nconsole.log(4)\n    `;\n\n    const jsExecutor = sandbox.compile(script);\n\n    jsExecutor({}).run();\n\n    expect(logs).toEqual([1, 2, 3, 4]);\n  });\n\n  it('should handle one-line while statement without braces', () => {\n    const script = `\nconsole.log(1)\n\nlet x = 0\nwhile (x < 1) x++\n\nconsole.log(2)\n\nconsole.log(3)\n    `;\n\n    const logs: any[] = [];\n    const mockConsole = { log: (...args: any[]) => logs.push(...args) };\n    const sandbox = new Sandbox({\n      globals: {\n        console: mockConsole,\n      },\n    });\n    const jsExecutor = sandbox.compile(script);\n    jsExecutor({}).run();\n\n    expect(logs).toEqual([1, 2, 3]);\n  });\n\n  it('should handle one-line for statement without braces', () => {\n    const script = `\nconsole.log(1)\n\nfor (let i = 0; i < 1; i++) console.log('loop')\n\nconsole.log(2)\n\nconsole.log(3)\n    `;\n\n    const logs: any[] = [];\n    const mockConsole = { log: (...args: any[]) => logs.push(...args) };\n    const sandbox = new Sandbox({\n      globals: {\n        console: mockConsole,\n      },\n    });\n\n    const jsExecutor = sandbox.compile(script);\n\n    jsExecutor({}).run();\n\n    expect(logs).toEqual([1, 'loop', 2, 3]);\n  });\n\n  it('should handle nested if statements without braces', () => {\n    const logs: any[] = [];\n    const mockConsole = { log: (...args: any[]) => logs.push(...args) };\n    const sandbox = new Sandbox({\n      globals: {\n        console: mockConsole,\n      },\n    });\n    const script = `\nconsole.log(1)\n\nif (true) \n  if (true) console.log(2)\n\nconsole.log(3)\n\nif (false) console.log('skip')\n\nconsole.log(4)\n    `;\n    const jsExecutor = sandbox.compile(script);\n    jsExecutor({}).run();\n\n    expect(logs).toEqual([1, 2, 3, 4]);\n  });\n\n  it('should handle functions with control flow statements', () => {\n    const logs: any[] = [];\n    const mockConsole = { log: (...args: any[]) => logs.push(...args) };\n    const sandbox = new Sandbox({\n      globals: {\n        console: mockConsole,\n      },\n    });\n    const script = `\nconsole.log(1)\n\nfunction test() {\n  if (true) console.log(2)\n  \n  console.log(3)\n  \n  for (let i = 0; i < 2; i++) console.log(4)\n  \n  console.log(5)\n}\n\ntest()\n\nconsole.log(6)\n    `;\n    const jsExecutor = sandbox.compile(script);\n    jsExecutor({}).run();\n\n    expect(logs).toEqual([1, 2, 3, 4, 4, 5, 6]);\n  });\n\n  it('should handle switch with control flow in cases', () => {\n    const logs: any[] = [];\n    const mockConsole = { log: (...args: any[]) => logs.push(...args) };\n    const sandbox = new Sandbox({\n      globals: {\n        console: mockConsole,\n      },\n    });\n    const script = `\nconsole.log(1)\n\nlet x = 2\n\nswitch (x) {\n  case 1:\n    if (true) console.log('a')\n    console.log('b')\n    break\n  case 2:\n    console.log(2)\n    \n    if (true) console.log(3)\n    \n    console.log(4)\n    break\n}\n\nconsole.log(5)\n    `;\n    const jsExecutor = sandbox.compile(script);\n    jsExecutor({}).run();\n\n    expect(logs).toEqual([1, 2, 3, 4, 5]);\n  });\n\n  it('should handle multiple loops with single-line bodies', () => {\n    const logs: any[] = [];\n    const mockConsole = { log: (...args: any[]) => logs.push(...args) };\n    const sandbox = new Sandbox({\n      globals: {\n        console: mockConsole,\n      },\n    });\n    const script = `\nconsole.log(1)\n\nfor (let i = 0; i < 2; i++) console.log(2)\n\nconsole.log(3)\n\nlet j = 0\nwhile (j < 2) j++\n\nconsole.log(4)\n\ndo {\n  console.log(5)\n  break\n} while (true)\n\nconsole.log(6)\n    `;\n    const jsExecutor = sandbox.compile(script);\n    jsExecutor({}).run();\n\n    expect(logs).toEqual([1, 2, 2, 3, 4, 5, 6]);\n  });\n\n  it('should handle complex nested structures', () => {\n    const logs: any[] = [];\n    const mockConsole = { log: (...args: any[]) => logs.push(...args) };\n    const sandbox = new Sandbox({\n      globals: {\n        console: mockConsole,\n      },\n    });\n    const script = `\nconsole.log(1)\n\nif (true) {\n  console.log(2)\n  \n  for (let i = 0; i < 2; i++) {\n    if (i === 1) console.log(3)\n  }\n  \n  console.log(4)\n}\n\nconsole.log(5)\n\nfunction nested() {\n  if (false) return\n  \n  console.log(6)\n  \n  do {\n    console.log(7)\n    break\n  } while (true)\n  \n  console.log(8)\n}\n\nnested()\n\nconsole.log(9)\n    `;\n    const jsExecutor = sandbox.compile(script);\n    jsExecutor({}).run();\n\n    expect(logs).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9]);\n  });\n\n  it('should handle if-else chains without braces', () => {\n    const logs: any[] = [];\n    const mockConsole = { log: (...args: any[]) => logs.push(...args) };\n    const sandbox = new Sandbox({\n      globals: {\n        console: mockConsole,\n      },\n    });\n    const script = `\nconsole.log(1)\n\nlet x = 2\n\nif (x === 1) console.log('a')\nelse if (x === 2) console.log(2)\nelse console.log('c')\n\nconsole.log(3)\n\nif (x === 3) console.log('d')\nelse if (x === 4) console.log('e')\n\nconsole.log(4)\n    `;\n    const jsExecutor = sandbox.compile(script);\n    jsExecutor({}).run();\n\n    expect(logs).toEqual([1, 2, 3, 4]);\n  });\n\n  it('should handle try-catch with control flow', () => {\n    const logs: any[] = [];\n    const mockConsole = { log: (...args: any[]) => logs.push(...args) };\n    const sandbox = new Sandbox({\n      globals: {\n        console: mockConsole,\n      },\n    });\n    const script = `\nconsole.log(1)\n\ntry {\n  console.log(2)\n  \n  if (false) throw new Error('test')\n  \n  console.log(3)\n} catch (e) {\n  console.log('error')\n}\n\nconsole.log(4)\n\ntry {\n  if (true) throw new Error('test2')\n  \n  console.log('skip')\n} catch (e) {\n  console.log(5)\n}\n\nconsole.log(6)\n    `;\n    const jsExecutor = sandbox.compile(script);\n    jsExecutor({}).run();\n\n    expect(logs).toEqual([1, 2, 3, 4, 5, 6]);\n  });\n\n  it('should handle arrow functions with control flow', () => {\n    const logs: any[] = [];\n    const mockConsole = { log: (...args: any[]) => logs.push(...args) };\n    const sandbox = new Sandbox({\n      globals: {\n        console: mockConsole,\n      },\n    });\n    const script = `\nconsole.log(1)\n\nconst fn = () => {\n  if (true) console.log(2)\n  \n  console.log(3)\n}\n\nfn()\n\nconsole.log(4)\n\nconst arr = [1, 2]\narr.forEach(x => {\n  if (x === 2) console.log(5)\n})\n\nconsole.log(6)\n    `;\n    const jsExecutor = sandbox.compile(script);\n    jsExecutor({}).run();\n\n    expect(logs).toEqual([1, 2, 3, 4, 5, 6]);\n  });\n\n  it('should handle deeply nested control structures', () => {\n    const logs: any[] = [];\n    const mockConsole = { log: (...args: any[]) => logs.push(...args) };\n    const sandbox = new Sandbox({\n      globals: {\n        console: mockConsole,\n      },\n    });\n    const script = `\nconsole.log(1)\n\nfor (let i = 0; i < 2; i++) {\n  if (i === 0) {\n    console.log(2)\n    \n    while (false) console.log('skip')\n    \n    console.log(3)\n  } else {\n    for (let j = 0; j < 2; j++) {\n      if (j === 1) console.log(4)\n    }\n    \n    console.log(5)\n  }\n}\n\nconsole.log(6)\n\ndo {\n  if (true) {\n    console.log(7)\n    \n    if (false) console.log('skip')\n    \n    console.log(8)\n  }\n  break\n} while (true)\n\nconsole.log(9)\n    `;\n    const jsExecutor = sandbox.compile(script);\n    jsExecutor({}).run();\n\n    expect(logs).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9]);\n  });\n\n  test('should handle multiple do-while loops in sequence', () => {\n    const code = `\n      let x = 0\n      do {\n        x++\n      } while (x < 3)\n      \n      do {\n        x++\n      } while (x < 6)\n      \n      return x\n    `;\n    const sandbox = new Sandbox();\n    const result = sandbox.compile(code);\n    expect(result({}).run()).toBe(6);\n  });\n\n  test('should handle nested do-while loops', () => {\n    const code = `\n      let x = 0, y = 0\n      do {\n        y = 0;\n        do {\n          y++\n        } while (y < 2)\n        x++\n      } while (x < 3)\n      return x + y\n    `;\n    const sandbox = new Sandbox();\n    const result = sandbox.compile(code);\n    expect(result({}).run()).toBe(5);\n  });\n\n  test('should handle do-while with complex preceding code', () => {\n    const code = `\n      function test() {\n        let x = 0\n        if (true) {\n          x = 5\n        }\n        return x\n      }\n      let y = 0;\n      do {\n        y++\n      } while (y < 2)\n      return test() + y\n    `;\n    const sandbox = new Sandbox();\n    const result = sandbox.compile(code);\n    expect(result({}).run()).toBe(7);\n  });\n\n  test('should handle do-while inside if without braces', () => {\n    const code = `\n      let count = 0\n      if (true)\n        do {\n          count++\n        } while (count < 3)\n      return count\n    `;\n    const sandbox = new Sandbox();\n    const result = sandbox.compile(code);\n    expect(result({}).run()).toBe(3);\n  });\n\n  test('should handle multiple control structures with mixed brace styles', () => {\n    const code = `\n      let result = 0\n      if (true) {\n        result = 1\n      }\n      \n      if (result > 0)\n        result = 2\n      else\n        result = 0\n      \n      while (result < 5)\n        result++\n      \n      do {\n        result++\n      } while (result < 10)\n      \n      return result\n    `;\n    const sandbox = new Sandbox();\n    const result = sandbox.compile(code);\n    expect(result({}).run()).toBe(10);\n  });\n\n  test('should handle large script with functions and callbacks', () => {\n    const code = `\n      // Utility functions at the top\n      function add(a, b) {\n        return a + b\n      }\n      \n      function multiply(x, y) {\n        let result = 0\n        let count = 0\n        do {\n          result += x\n          count++\n        } while (count < y)\n        return result\n      }\n      \n      // Main processing function with nested structures\n      function processData(data) {\n        let total = 0\n        let index = 0\n        \n        // Nested callback\n        const callback = function(item) {\n          if (item > 0)\n            return item * 2\n          else\n            return 0\n        }\n        \n        // Do-while inside function\n        do {\n          if (data[index]) {\n            total += callback(data[index])\n          }\n          index++\n        } while (index < data.length)\n        \n        return total\n      }\n      \n      // Another function with complex control flow\n      function calculate(n) {\n        let sum = 0\n        let i = 0\n        \n        while (i < n) {\n          if (i % 2 === 0)\n            sum += i\n          else\n            sum += i * 2\n          i++\n        }\n        \n        // Do-while at the end\n        do {\n          sum++\n        } while (sum < 20)\n        \n        return sum\n      }\n      \n      // Nested closures\n      function outer(x) {\n        return function inner(y) {\n          let result = x + y\n          \n          if (result > 10)\n            result = 10\n          else if (result > 5)\n            result = 5\n          else\n            result = 0\n          \n          return result\n        }\n      }\n      \n      // Execute everything\n      const data = [1, 2, 3, 4, 5]\n      let finalResult = 0\n      \n      finalResult += processData(data)\n      finalResult += calculate(5)\n      finalResult += outer(3)(4)\n      finalResult += multiply(3, 4)\n      \n      return finalResult\n    `;\n    const sandbox = new Sandbox();\n    const result = sandbox.compile(code);\n    // processData: (1*2 + 2*2 + 3*2 + 4*2 + 5*2) = 30\n    // calculate: (0 + 2 + 4 + 2*1 + 2*3) = 14, then incremented to 20\n    // outer(3)(4): 7 > 5, so returns 5\n    // multiply(3, 4): 12\n    // Total: 30 + 20 + 5 + 12 = 67\n    expect(result({}).run()).toBe(67);\n  });\n\n  test('should handle deeply nested callbacks with do-while', () => {\n    const code = `\n      function outer() {\n        let x = 0\n        \n        const middle = function() {\n          let y = 0\n          \n          const inner = function() {\n            let z = 0\n            do {\n              z++\n            } while (z < 2)\n            return z\n          }\n          \n          do {\n            y += inner()\n          } while (y < 5)\n          \n          return y\n        }\n        \n        do {\n          x += middle()\n        } while (x < 10)\n        \n        return x\n      }\n      \n      return outer()\n    `;\n    const sandbox = new Sandbox();\n    const result = sandbox.compile(code);\n    expect(result({}).run()).toBe(12);\n  });\n\n  test('should handle mixed control structures across function boundaries', () => {\n    const code = `\n      let globalCount = 0\n      \n      // Top-level if-else\n      if (globalCount < 10)\n        globalCount = 10\n      \n      // Function declaration after control structure\n      function looper() {\n        return 3\n      }\n      \n      return globalCount + looper()\n    `;\n    const sandbox = new Sandbox();\n    const result = sandbox.compile(code);\n    expect(result({}).run()).toBe(13);\n  });\n});\n"
  },
  {
    "path": "test/subscriptions.spec.ts",
    "content": "import Sandbox from '../src/Sandbox.js';\n\ndescribe('Subscription Tests', () => {\n  describe('subscribeGet', () => {\n    it('should track property access', () => {\n      const sandbox = new Sandbox();\n      const accesses: Array<{ obj: any; name: string }> = [];\n\n      const code = `\n        const value = obj.property;\n        return value;\n      `;\n\n      const fn = sandbox.compile(code);\n      const scope = { obj: { property: 42 } };\n      const { context, run } = fn(scope);\n\n      sandbox.subscribeGet((obj, name) => {\n        accesses.push({ obj, name });\n      }, context);\n\n      const result = run();\n\n      expect(result).toBe(42);\n      expect(accesses.length).toBeGreaterThan(0);\n      expect(accesses.some((a) => a.name === 'property')).toBe(true);\n    });\n\n    it('should track multiple property accesses', () => {\n      const sandbox = new Sandbox();\n      const accesses: string[] = [];\n\n      const code = `\n        const a = obj.x;\n        const b = obj.y;\n        const c = obj.z;\n        return a + b + c;\n      `;\n\n      const fn = sandbox.compile(code);\n      const scope = { obj: { x: 1, y: 2, z: 3 } };\n      const { context, run } = fn(scope);\n\n      sandbox.subscribeGet((obj, name) => {\n        if (typeof name === 'string') {\n          accesses.push(name);\n        }\n      }, context);\n\n      const result = run();\n\n      expect(result).toBe(6);\n      expect(accesses.filter((a) => a === 'x').length).toBeGreaterThan(0);\n      expect(accesses.filter((a) => a === 'y').length).toBeGreaterThan(0);\n      expect(accesses.filter((a) => a === 'z').length).toBeGreaterThan(0);\n    });\n\n    it('should allow unsubscribing from get events', () => {\n      const sandbox = new Sandbox();\n      let accessCount = 0;\n\n      const code = `\n        return obj.value;\n      `;\n\n      const fn = sandbox.compile(code);\n      const scope = { obj: { value: 10 } };\n      const { context, run } = fn(scope);\n\n      const subscription = sandbox.subscribeGet((obj, name) => {\n        if (name === 'value') accessCount++;\n      }, context);\n\n      run();\n      const countAfterFirstRun = accessCount;\n\n      subscription.unsubscribe();\n\n      // Run again in same context\n      run();\n\n      expect(countAfterFirstRun).toBeGreaterThan(0);\n      expect(accessCount).toBe(countAfterFirstRun); // Should not increase after unsubscribe\n    });\n\n    it('should track nested property access', () => {\n      const sandbox = new Sandbox();\n      const accesses: string[] = [];\n\n      const code = `\n        return obj.nested.deep.value;\n      `;\n\n      const fn = sandbox.compile(code);\n      const scope = { obj: { nested: { deep: { value: 99 } } } };\n      const { context, run } = fn(scope);\n\n      sandbox.subscribeGet((obj, name) => {\n        if (typeof name === 'string') {\n          accesses.push(name);\n        }\n      }, context);\n\n      const result = run();\n\n      expect(result).toBe(99);\n      expect(accesses).toContain('nested');\n      expect(accesses).toContain('deep');\n      expect(accesses).toContain('value');\n    });\n\n    it('should track nested property access performed by JSON.stringify', () => {\n      const sandbox = new Sandbox();\n      const accesses: string[] = [];\n      const scope = {\n        payload: {\n          top: 1,\n          nested: {\n            inner: 2,\n          },\n        },\n      };\n\n      const fn = sandbox.compile('return JSON.stringify(payload)');\n      const { context, run } = fn(scope);\n\n      sandbox.subscribeGet((obj, name) => {\n        if (typeof name === 'string') {\n          accesses.push(name);\n        }\n      }, context);\n\n      const result = run();\n\n      expect(result).toBe('{\"top\":1,\"nested\":{\"inner\":2}}');\n      expect(accesses).toContain('top');\n      expect(accesses).toContain('nested');\n      expect(accesses).toContain('inner');\n    });\n  });\n\n  describe('subscribeSet', () => {\n    it('should track property modification', () => {\n      const sandbox = new Sandbox();\n      const modifications: string[] = [];\n\n      const code = `\n        obj.property = 100;\n        return obj.property;\n      `;\n\n      const fn = sandbox.compile(code);\n      const scope = { obj: { property: 42 } };\n      const { context, run } = fn(scope);\n\n      sandbox.subscribeSet(\n        scope.obj,\n        'property',\n        (change) => {\n          modifications.push(change.type);\n        },\n        context,\n      );\n\n      const result = run();\n\n      expect(result).toBe(100);\n      expect(scope.obj.property).toBe(100);\n      expect(modifications.length).toBe(1);\n      expect(modifications[0]).toBe('replace');\n    });\n\n    it('should track multiple modifications to same property', () => {\n      const sandbox = new Sandbox();\n      const modifications: string[] = [];\n\n      const code = `\n        obj.value = 1;\n        obj.value = 2;\n        obj.value = 3;\n        return obj.value;\n      `;\n\n      const fn = sandbox.compile(code);\n      const scope = { obj: { value: 0 } };\n      const { context, run } = fn(scope);\n\n      sandbox.subscribeSet(\n        scope.obj,\n        'value',\n        (change) => {\n          modifications.push(change.type);\n        },\n        context,\n      );\n\n      run();\n\n      expect(modifications).toEqual(['replace', 'replace', 'replace']);\n    });\n\n    it('should only track specified property', () => {\n      const sandbox = new Sandbox();\n      let xModifications = 0;\n      let yModifications = 0;\n\n      const code = `\n        obj.x = 10;\n        obj.y = 20;\n        obj.x = 30;\n      `;\n\n      const fn = sandbox.compile(code);\n      const scope = { obj: { x: 0, y: 0 } };\n      const { context, run } = fn(scope);\n\n      sandbox.subscribeSet(\n        scope.obj,\n        'x',\n        () => {\n          xModifications++;\n        },\n        context,\n      );\n\n      sandbox.subscribeSet(\n        scope.obj,\n        'y',\n        () => {\n          yModifications++;\n        },\n        context,\n      );\n\n      run();\n\n      expect(xModifications).toBe(2);\n      expect(yModifications).toBe(1);\n    });\n\n    it('should allow unsubscribing from set events', () => {\n      const sandbox = new Sandbox();\n      let modificationCount = 0;\n\n      const code = `\n        obj.value = 100;\n      `;\n\n      const fn = sandbox.compile(code);\n      const scope = { obj: { value: 0 } };\n      const { context, run } = fn(scope);\n\n      const subscription = sandbox.subscribeSet(\n        scope.obj,\n        'value',\n        () => {\n          modificationCount++;\n        },\n        context,\n      );\n\n      run();\n      expect(modificationCount).toBe(1);\n\n      subscription.unsubscribe();\n\n      const { context: context2, run: run2 } = fn(scope);\n      run2();\n\n      expect(modificationCount).toBe(1); // Should not increase after unsubscribe\n    });\n\n    it('should track changes to nested objects', () => {\n      const sandbox = new Sandbox();\n      let modificationCount = 0;\n\n      const code = `\n        obj.nested.value = 42;\n        return obj.nested.value;\n      `;\n\n      const fn = sandbox.compile(code);\n      const scope = { obj: { nested: { value: 0 } } };\n      const { context, run } = fn(scope);\n\n      sandbox.subscribeSet(\n        scope.obj.nested,\n        'value',\n        (change) => {\n          modificationCount++;\n        },\n        context,\n      );\n\n      run();\n\n      expect(modificationCount).toBe(1);\n      expect(scope.obj.nested.value).toBe(42);\n    });\n\n    it('should handle increment operations', () => {\n      const sandbox = new Sandbox();\n      let modificationCount = 0;\n\n      const code = `\n        obj.counter++;\n        obj.counter++;\n        obj.counter++;\n        return obj.counter;\n      `;\n\n      const fn = sandbox.compile(code);\n      const scope = { obj: { counter: 0 } };\n      const { context, run } = fn(scope);\n\n      sandbox.subscribeSet(\n        scope.obj,\n        'counter',\n        (change) => {\n          modificationCount++;\n        },\n        context,\n      );\n\n      const result = run();\n\n      expect(result).toBe(3);\n      expect(modificationCount).toBe(3);\n    });\n\n    it('should handle compound assignments', () => {\n      const sandbox = new Sandbox();\n      let modificationCount = 0;\n\n      const code = `\n        obj.value += 10;\n        obj.value *= 2;\n        obj.value -= 5;\n        return obj.value;\n      `;\n\n      const fn = sandbox.compile(code);\n      const scope = { obj: { value: 5 } };\n      const { context, run } = fn(scope);\n\n      sandbox.subscribeSet(\n        scope.obj,\n        'value',\n        (change) => {\n          modificationCount++;\n        },\n        context,\n      );\n\n      run();\n\n      expect(scope.obj.value).toBe(25);\n      expect(modificationCount).toBe(3);\n    });\n  });\n\n  describe('subscribeSetGlobal', () => {\n    it('should track global property modifications', () => {\n      const sandbox = new Sandbox();\n      let modificationCount = 0;\n\n      const code = `\n        obj.value = 999;\n      `;\n\n      const fn = sandbox.compile(code);\n      const scope = { obj: { value: 0 } };\n\n      sandbox.subscribeSetGlobal(scope.obj, 'value', (change) => {\n        modificationCount++;\n      });\n\n      fn(scope).run();\n\n      expect(modificationCount).toBe(1);\n      expect(scope.obj.value).toBe(999);\n    });\n\n    it('should persist across multiple executions', () => {\n      const sandbox = new Sandbox();\n      let modificationCount = 0;\n\n      const code = `\n        obj.value++;\n      `;\n\n      const fn = sandbox.compile(code);\n      const scope = { obj: { value: 0 } };\n\n      sandbox.subscribeSetGlobal(scope.obj, 'value', () => {\n        modificationCount++;\n      });\n\n      fn(scope).run();\n      fn(scope).run();\n      fn(scope).run();\n\n      expect(modificationCount).toBe(3);\n    });\n\n    it('should allow unsubscribing from global set events', () => {\n      const sandbox = new Sandbox();\n      let modificationCount = 0;\n\n      const code = `\n        obj.value = 5;\n      `;\n\n      const fn = sandbox.compile(code);\n      const scope = { obj: { value: 0 } };\n\n      const subscription = sandbox.subscribeSetGlobal(scope.obj, 'value', () => {\n        modificationCount++;\n      });\n\n      fn(scope).run();\n      expect(modificationCount).toBe(1);\n\n      subscription.unsubscribe();\n      fn(scope).run();\n\n      expect(modificationCount).toBe(1); // Should not increase\n    });\n\n    it('should track modifications to different objects', () => {\n      const sandbox = new Sandbox();\n      let obj1ModCount = 0;\n      let obj2ModCount = 0;\n\n      const code = `\n        obj1.value = 100;\n        obj2.value = 200;\n      `;\n\n      const fn = sandbox.compile(code);\n      const scope = {\n        obj1: { value: 0 },\n        obj2: { value: 0 },\n      };\n\n      sandbox.subscribeSetGlobal(scope.obj1, 'value', (change) => {\n        obj1ModCount++;\n      });\n\n      sandbox.subscribeSetGlobal(scope.obj2, 'value', (change) => {\n        obj2ModCount++;\n      });\n\n      fn(scope).run();\n\n      expect(obj1ModCount).toBe(1);\n      expect(obj2ModCount).toBe(1);\n      expect(scope.obj1.value).toBe(100);\n      expect(scope.obj2.value).toBe(200);\n    });\n  });\n\n  describe('Multiple subscribers', () => {\n    it('should notify multiple subscribers for same property', () => {\n      const sandbox = new Sandbox();\n      let subscriber1Called = 0;\n      let subscriber2Called = 0;\n      let subscriber3Called = 0;\n\n      const code = `\n        obj.value = 42;\n      `;\n\n      const fn = sandbox.compile(code);\n      const scope = { obj: { value: 0 } };\n      const { context, run } = fn(scope);\n\n      sandbox.subscribeSet(\n        scope.obj,\n        'value',\n        () => {\n          subscriber1Called++;\n        },\n        context,\n      );\n\n      sandbox.subscribeSet(\n        scope.obj,\n        'value',\n        () => {\n          subscriber2Called++;\n        },\n        context,\n      );\n\n      sandbox.subscribeSet(\n        scope.obj,\n        'value',\n        () => {\n          subscriber3Called++;\n        },\n        context,\n      );\n\n      run();\n\n      expect(subscriber1Called).toBe(1);\n      expect(subscriber2Called).toBe(1);\n      expect(subscriber3Called).toBe(1);\n    });\n\n    it('should independently unsubscribe multiple subscribers', () => {\n      const sandbox = new Sandbox();\n      let subscriber1Called = 0;\n      let subscriber2Called = 0;\n\n      const code = `\n        obj.value = 10;\n      `;\n\n      const fn = sandbox.compile(code);\n      const scope = { obj: { value: 0 } };\n      const { context, run } = fn(scope);\n\n      const sub1 = sandbox.subscribeSet(\n        scope.obj,\n        'value',\n        () => {\n          subscriber1Called++;\n        },\n        context,\n      );\n\n      const sub2 = sandbox.subscribeSet(\n        scope.obj,\n        'value',\n        () => {\n          subscriber2Called++;\n        },\n        context,\n      );\n\n      run();\n      expect(subscriber1Called).toBe(1);\n      expect(subscriber2Called).toBe(1);\n\n      sub1.unsubscribe();\n\n      // Set a different value to trigger subscription again\n      scope.obj.value = 0; // Reset\n      run();\n\n      expect(subscriber1Called).toBe(1); // Should not increase\n      expect(subscriber2Called).toBe(2); // Should increase\n    });\n  });\n\n  describe('Change notification with object replacement', () => {\n    it('should track when property value is an object that gets replaced', () => {\n      const sandbox = new Sandbox();\n      let modificationCount = 0;\n\n      const code = `\n        obj.nested = { x: 1, y: 2 };\n        obj.nested = { x: 3, y: 4 };\n      `;\n\n      const fn = sandbox.compile(code);\n      const scope = { obj: { nested: { x: 0, y: 0 } } };\n      const { context, run } = fn(scope);\n\n      sandbox.subscribeSet(\n        scope.obj,\n        'nested',\n        (change) => {\n          modificationCount++;\n        },\n        context,\n      );\n\n      run();\n\n      expect(modificationCount).toBe(2);\n      expect(scope.obj.nested).toEqual({ x: 3, y: 4 });\n    });\n\n    it('should track array modifications', () => {\n      const sandbox = new Sandbox();\n      let modificationCount = 0;\n\n      const code = `\n        obj.items = [1, 2, 3];\n        obj.items = [4, 5, 6];\n      `;\n\n      const fn = sandbox.compile(code);\n      const scope = { obj: { items: [] as number[] } };\n      const { context, run } = fn(scope);\n\n      sandbox.subscribeSet(\n        scope.obj,\n        'items',\n        (change) => {\n          modificationCount++;\n        },\n        context,\n      );\n\n      run();\n\n      expect(modificationCount).toBe(2);\n      expect(scope.obj.items).toEqual([4, 5, 6]);\n    });\n  });\n\n  describe('Array mutation subscriptions', () => {\n    it('should track array push operations', () => {\n      const sandbox = new Sandbox();\n      const changes: any[] = [];\n\n      const code = `\n        arr.push(4);\n        arr.push(5, 6);\n      `;\n\n      const fn = sandbox.compile(code);\n      const scope = { arr: [1, 2, 3] };\n      const { context, run } = fn(scope);\n\n      sandbox.subscribeSet(\n        scope,\n        'arr',\n        (change) => {\n          changes.push(change);\n        },\n        context,\n      );\n\n      run();\n\n      expect(changes.length).toBe(2);\n      expect(changes[0].type).toBe('push');\n      expect(changes[0].added).toEqual([4]);\n      expect(changes[1].type).toBe('push');\n      expect(changes[1].added).toEqual([5, 6]);\n      expect(scope.arr).toEqual([1, 2, 3, 4, 5, 6]);\n    });\n\n    it('should track array pop operations', () => {\n      const sandbox = new Sandbox();\n      const changes: any[] = [];\n\n      const code = `\n        arr.pop();\n        arr.pop();\n      `;\n\n      const fn = sandbox.compile(code);\n      const scope = { arr: [1, 2, 3, 4, 5] };\n      const { context, run } = fn(scope);\n\n      sandbox.subscribeSet(\n        scope,\n        'arr',\n        (change) => {\n          changes.push(change);\n        },\n        context,\n      );\n\n      run();\n\n      expect(changes.length).toBe(2);\n      expect(changes[0].type).toBe('pop');\n      expect(changes[0].removed).toEqual([5]);\n      expect(changes[1].type).toBe('pop');\n      expect(changes[1].removed).toEqual([4]);\n      expect(scope.arr).toEqual([1, 2, 3]);\n    });\n\n    it('should track array shift operations', () => {\n      const sandbox = new Sandbox();\n      const changes: any[] = [];\n\n      const code = `\n        arr.shift();\n      `;\n\n      const fn = sandbox.compile(code);\n      const scope = { arr: [1, 2, 3] };\n      const { context, run } = fn(scope);\n\n      sandbox.subscribeSet(\n        scope,\n        'arr',\n        (change) => {\n          changes.push(change);\n        },\n        context,\n      );\n\n      run();\n\n      expect(changes.length).toBe(1);\n      expect(changes[0].type).toBe('shift');\n      expect(changes[0].removed).toEqual([1]);\n      expect(scope.arr).toEqual([2, 3]);\n    });\n\n    it('should track array unshift operations', () => {\n      const sandbox = new Sandbox();\n      const changes: any[] = [];\n\n      const code = `\n        arr.unshift(0);\n        arr.unshift(-2, -1);\n      `;\n\n      const fn = sandbox.compile(code);\n      const scope = { arr: [1, 2, 3] };\n      const { context, run } = fn(scope);\n\n      sandbox.subscribeSet(\n        scope,\n        'arr',\n        (change) => {\n          changes.push(change);\n        },\n        context,\n      );\n\n      run();\n\n      expect(changes.length).toBe(2);\n      expect(changes[0].type).toBe('unshift');\n      expect(changes[0].added).toEqual([0]);\n      expect(changes[1].type).toBe('unshift');\n      expect(changes[1].added).toEqual([-2, -1]);\n      expect(scope.arr).toEqual([-2, -1, 0, 1, 2, 3]);\n    });\n\n    it('should track array splice operations', () => {\n      const sandbox = new Sandbox();\n      const changes: any[] = [];\n\n      const code = `\n        arr.splice(1, 2, 'a', 'b');\n      `;\n\n      const fn = sandbox.compile(code);\n      const scope = { arr: [1, 2, 3, 4, 5] };\n      const { context, run } = fn(scope);\n\n      sandbox.subscribeSet(\n        scope,\n        'arr',\n        (change) => {\n          changes.push(change);\n        },\n        context,\n      );\n\n      run();\n\n      expect(changes.length).toBe(1);\n      expect(changes[0].type).toBe('splice');\n      expect(changes[0].startIndex).toBe(1);\n      expect(changes[0].deleteCount).toBe(2);\n      expect(changes[0].added).toEqual(['a', 'b']);\n      expect(changes[0].removed).toEqual([2, 3]);\n      expect(scope.arr).toEqual([1, 'a', 'b', 4, 5]);\n    });\n\n    it('should track array reverse operations', () => {\n      const sandbox = new Sandbox();\n      const changes: any[] = [];\n\n      const code = `\n        arr.reverse();\n      `;\n\n      const fn = sandbox.compile(code);\n      const scope = { arr: [1, 2, 3, 4, 5] };\n      const { context, run } = fn(scope);\n\n      sandbox.subscribeSet(\n        scope,\n        'arr',\n        (change) => {\n          changes.push(change);\n        },\n        context,\n      );\n\n      run();\n\n      expect(changes.length).toBe(1);\n      expect(changes[0].type).toBe('reverse');\n      expect(scope.arr).toEqual([5, 4, 3, 2, 1]);\n    });\n\n    it('should track array sort operations', () => {\n      const sandbox = new Sandbox();\n      const changes: any[] = [];\n\n      const code = `\n        arr.sort((a, b) => b - a);\n      `;\n\n      const fn = sandbox.compile(code);\n      const scope = { arr: [3, 1, 4, 1, 5, 9] };\n      const { context, run } = fn(scope);\n\n      sandbox.subscribeSet(\n        scope,\n        'arr',\n        (change) => {\n          changes.push(change);\n        },\n        context,\n      );\n\n      run();\n\n      expect(changes.length).toBe(1);\n      expect(changes[0].type).toBe('sort');\n      expect(scope.arr).toEqual([9, 5, 4, 3, 1, 1]);\n    });\n\n    it('should track array copyWithin operations', () => {\n      const sandbox = new Sandbox();\n      const changes: any[] = [];\n\n      const code = `\n        arr.copyWithin(0, 3, 5);\n      `;\n\n      const fn = sandbox.compile(code);\n      const scope = { arr: [1, 2, 3, 4, 5] };\n      const { context, run } = fn(scope);\n\n      sandbox.subscribeSet(\n        scope,\n        'arr',\n        (change) => {\n          changes.push(change);\n        },\n        context,\n      );\n\n      run();\n\n      expect(changes.length).toBe(1);\n      expect(changes[0].type).toBe('copyWithin');\n      expect(changes[0].startIndex).toBe(0);\n      expect(changes[0].endIndex).toBe(2);\n      expect(changes[0].added).toEqual([4, 5]);\n      expect(changes[0].removed).toEqual([1, 2]);\n      expect(scope.arr).toEqual([4, 5, 3, 4, 5]);\n    });\n\n    it('should not trigger change when push adds no elements', () => {\n      const sandbox = new Sandbox();\n      const changes: any[] = [];\n\n      const code = `\n        arr.push();\n      `;\n\n      const fn = sandbox.compile(code);\n      const scope = { arr: [1, 2, 3] };\n      const { context, run } = fn(scope);\n\n      sandbox.subscribeSet(\n        scope,\n        'arr',\n        (change) => {\n          changes.push(change);\n        },\n        context,\n      );\n\n      run();\n\n      expect(changes.length).toBe(0);\n      expect(scope.arr).toEqual([1, 2, 3]);\n    });\n\n    it('should not trigger change when pop on empty array', () => {\n      const sandbox = new Sandbox();\n      const changes: any[] = [];\n\n      const code = `\n        arr.pop();\n      `;\n\n      const fn = sandbox.compile(code);\n      const scope = { arr: [] as number[] };\n      const { context, run } = fn(scope);\n\n      sandbox.subscribeSet(\n        scope,\n        'arr',\n        (change) => {\n          changes.push(change);\n        },\n        context,\n      );\n\n      run();\n\n      expect(changes.length).toBe(0);\n      expect(scope.arr).toEqual([]);\n    });\n\n    it('should not trigger change when reverse on empty array', () => {\n      const sandbox = new Sandbox();\n      const changes: any[] = [];\n\n      const code = `\n        arr.reverse();\n      `;\n\n      const fn = sandbox.compile(code);\n      const scope = { arr: [] as number[] };\n      const { context, run } = fn(scope);\n\n      sandbox.subscribeSet(\n        scope,\n        'arr',\n        (change) => {\n          changes.push(change);\n        },\n        context,\n      );\n\n      run();\n\n      expect(changes.length).toBe(0);\n      expect(scope.arr).toEqual([]);\n    });\n  });\n});\n"
  },
  {
    "path": "test/symbol.spec.ts",
    "content": "'use strict';\nimport Sandbox, { LocalScope } from '../src/Sandbox.js';\n\nfunction createScope(vars: Record<string, unknown> = {}) {\n  Object.setPrototypeOf(vars, LocalScope.prototype);\n  return vars;\n}\n\ndescribe('SandboxSymbol', () => {\n  it('keeps Symbol.for state local to the sandbox', () => {\n    const sandbox = new Sandbox();\n    const hostShared = Symbol.for('shared');\n\n    const result = sandbox\n      .compile(\n        `\n        const local = Symbol('secret');\n        const shared = Symbol.for('shared');\n        return {\n          localType: typeof local,\n          shared,\n          sameWithinSandbox: shared === Symbol.for('shared'),\n          sandboxKey: Symbol.keyFor(shared),\n          hostKeyInsideSandbox: Symbol.keyFor(hostShared),\n          equalsHostShared: shared === hostShared,\n        };\n      `,\n        true,\n      )(createScope({ hostShared }))\n      .run() as {\n      localType: string;\n      shared: symbol;\n      sameWithinSandbox: boolean;\n      sandboxKey: string;\n      hostKeyInsideSandbox: string | undefined;\n      equalsHostShared: boolean;\n    };\n\n    expect(result.localType).toBe('symbol');\n    expect(result.sameWithinSandbox).toBe(true);\n    expect(result.sandboxKey).toBe('shared');\n    expect(result.hostKeyInsideSandbox).toBeUndefined();\n    expect(result.equalsHostShared).toBe(false);\n    expect(Symbol.keyFor(result.shared)).toBeUndefined();\n    expect(Symbol.keyFor(hostShared)).toBe('shared');\n  });\n\n  it('keeps well-known symbols native for iterator support', () => {\n    const sandbox = new Sandbox();\n    const result = sandbox\n      .compile(\n        `\n        const iterable = {\n          [Symbol.iterator]: function* () {\n            yield 7;\n          },\n        };\n        return [Symbol.iterator === hostIterator, [...iterable][0]];\n      `,\n        true,\n      )(createScope({ hostIterator: Symbol.iterator }))\n      .run();\n\n    expect(result).toEqual([true, 7]);\n  });\n\n  it('exposes the default safe symbol allowlist', () => {\n    const sandbox = new Sandbox();\n    const result = sandbox\n      .compile(\n        `\n        return [\n          Symbol.iterator === hostIterator,\n          Symbol.asyncIterator === hostAsyncIterator,\n          Symbol.custom,\n        ];\n      `,\n        true,\n      )(createScope({ hostIterator: Symbol.iterator, hostAsyncIterator: Symbol.asyncIterator }))\n      .run();\n\n    expect(result).toEqual([true, true, undefined]);\n  });\n\n  it('allows custom whitelisted symbol statics', () => {\n    const custom = Symbol.for('custom');\n    const sandbox = new Sandbox({\n      symbolWhitelist: {\n        ...Sandbox.SAFE_SYMBOLS,\n        custom,\n      },\n    });\n    const result = sandbox\n      .compile(\n        `\n        const sym = Symbol.for('custom');\n        return [\n          sym === hostCustom,\n          sym === Symbol.for('custom'),\n          typeof sym\n        ];\n      `,\n        true,\n      )(createScope({ hostCustom: custom }))\n      .run();\n\n    expect(result).toEqual([false, true, 'symbol']);\n  });\n\n  it('preserves sandbox symbol keys across spread and fromEntries', () => {\n    const sandbox = new Sandbox();\n    const result = sandbox\n      .compile(\n        `\n        const local = Symbol('local');\n        const viaSpread = { ...{ [local]: 3, a: 1 } };\n        const shared = Symbol.for('shared');\n        const viaEntries = Object.fromEntries([[shared, 5]]);\n        return [\n          viaSpread[local],\n          viaSpread.hasOwnProperty(local),\n          Object.keys(viaSpread),\n          viaEntries[shared],\n          viaEntries.hasOwnProperty(shared),\n          Symbol.keyFor(shared),\n        ];\n      `,\n        true,\n      )()\n      .run();\n\n    expect(result).toEqual([3, true, ['a'], 5, true, 'shared']);\n  });\n});\n"
  },
  {
    "path": "test/taggedTemplateEscaping.spec.ts",
    "content": "import Sandbox from '../src/Sandbox.js';\n\ndescribe('Tagged Template Escaping Tests', () => {\n  let sandbox: Sandbox;\n\n  beforeEach(() => {\n    sandbox = new Sandbox();\n  });\n\n  it('should handle escaped dollar signs in tagged templates', () => {\n    const code = `\n      function tag(strings, ...values) {\n        let result = strings[0];\n        for (let i = 0; i < values.length; i++) {\n          result += values[i] + strings[i + 1];\n        }\n        return result;\n      }\n      const x = 5;\n      return tag\\`value: \\\\\\${x}\\`;\n    `;\n    const fn = sandbox.compile(code);\n    const result = fn().run();\n    // The escaped \\${ should be treated as literal text \"${x}\", not interpolation\n    expect(result).toBe('value: ${x}');\n  });\n\n  it('should handle normal interpolations in tagged templates', () => {\n    const code = `\n      function tag(strings, ...values) {\n        return strings[0] + values[0] + strings[1];\n      }\n      const x = 42;\n      return tag\\`value: \\${x}!\\`;\n    `;\n    const fn = sandbox.compile(code);\n    const result = fn().run();\n    expect(result).toBe('value: 42!');\n  });\n\n  it('should handle multiple escaped sequences', () => {\n    const code = `\n      function tag(strings, ...values) {\n        return strings.join('|');\n      }\n      return tag\\`start\\\\\\${foo}\\\\\\${bar}end\\`;\n    `;\n    const fn = sandbox.compile(code);\n    const result = fn().run();\n    expect(result).toBe('start${foo}${bar}end');\n  });\n\n  it('should handle mixed escaped and real interpolations', () => {\n    const code = `\n      function tag(strings, ...values) {\n        let result = '';\n        for (let i = 0; i < strings.length; i++) {\n          result += strings[i];\n          if (i < values.length) {\n            result += values[i];\n          }\n        }\n        return result;\n      }\n      const x = 10;\n      return tag\\`value: \\${x}, escaped: \\\\\\${y}\\`;\n    `;\n    const fn = sandbox.compile(code);\n    const result = fn().run();\n    expect(result).toBe('value: 10, escaped: ${y}');\n  });\n\n  it('should handle malformed placeholders without hanging', () => {\n    const code = `\n      function tag(strings) {\n        return strings[0];\n      }\n      return tag\\`text with \\\\\\${ no closing brace\\`;\n    `;\n    const fn = sandbox.compile(code);\n    const result = fn().run();\n    // Should treat the escaped ${ as literal text since it's not a valid placeholder\n    expect(result).toContain('${');\n  });\n\n  it('should handle non-numeric placeholder content', () => {\n    const code = `\n      function tag(strings) {\n        return strings[0];\n      }\n      // The escaped sequence should be treated as literal text\n      return tag\\`start\\`;\n    `;\n    const fn = sandbox.compile(code);\n    const result = fn().run();\n    expect(result).toBe('start');\n  });\n\n  it('should correctly parse adjacent interpolations', () => {\n    const code = `\n      function tag(strings, ...values) {\n        return values.join(',');\n      }\n      const a = 1, b = 2;\n      return tag\\`\\${a}\\${b}\\`;\n    `;\n    const fn = sandbox.compile(code);\n    const result = fn().run();\n    expect(result).toBe('1,2');\n  });\n\n  it('should handle escaped backslash before interpolation', () => {\n    const code = `\n      function tag(strings, ...values) {\n        return strings[0] + values[0] + strings[1];\n      }\n      const x = 5;\n      return tag\\`\\\\\\\\\\${x}\\`;\n    `;\n    const fn = sandbox.compile(code);\n    const result = fn().run();\n    // \\\\ becomes \\ and ${x} is interpolated\n    expect(result).toBe('\\\\5');\n  });\n});\n"
  },
  {
    "path": "test/ticks/sandboxArrayTicks.spec.ts",
    "content": "import Sandbox from '../../src/Sandbox.js';\n\ndescribe('array ticks', () => {\n  function haltsWithQuota(\n    code: string,\n    quota: bigint,\n    scope: Record<string, unknown> = {},\n  ): boolean {\n    const sandbox = new Sandbox({ executionQuota: quota, haltOnSandboxError: true });\n    let halted = false;\n    sandbox.subscribeHalt(() => {\n      halted = true;\n    });\n    sandbox.compile(code)(scope).run();\n    return halted;\n  }\n\n  describe('flat', () => {\n    it('does not halt on a small flat array within quota', () => {\n      expect(haltsWithQuota('return arr.flat()', 100n, { arr: [1, 2, 3] })).toBe(false);\n    });\n\n    it('halts when flat on large array exceeds quota', () => {\n      const arr = Array.from({ length: 200 }, (_, i) => i);\n      expect(haltsWithQuota('return arr.flat()', 10n, { arr })).toBe(true);\n    });\n\n    it('halts on deeply nested array with depth=2 but not with depth=1', () => {\n      const arr = [Array.from({ length: 100 }, (_, i) => i)];\n      const nested = [arr];\n      expect(haltsWithQuota('return arr.flat(1)', 10n, { arr: nested })).toBe(false);\n      expect(haltsWithQuota('return arr.flat(2)', 10n, { arr: nested })).toBe(true);\n    });\n\n    it('respects explicit depth argument — depth=0 does not recurse', () => {\n      const arr = [Array.from({ length: 100 }, (_, i) => i)];\n      expect(haltsWithQuota('return arr.flat(0)', 10n, { arr })).toBe(false);\n    });\n  });\n\n  describe('flatMap', () => {\n    it('does not halt on a small array within quota', () => {\n      expect(haltsWithQuota('return arr.flatMap(x => x)', 100n, { arr: [[1], [2], [3]] })).toBe(\n        false,\n      );\n    });\n\n    it('halts when flatMap on large array exceeds quota', () => {\n      const arr = Array.from({ length: 200 }, () => [1, 2, 3]);\n      expect(haltsWithQuota('return arr.flatMap(x => x)', 10n, { arr })).toBe(true);\n    });\n\n    it('only counts one level deep (does not recurse into deeply nested arrays)', () => {\n      const arr = [Array.from({ length: 100 }, (_, i) => i)];\n      const nested = [arr];\n      expect(haltsWithQuota('return arr.flatMap(x => x)', 10n, { arr: nested })).toBe(false);\n    });\n  });\n\n  describe('concat', () => {\n    it('does not halt when concatenating small arrays within quota', () => {\n      expect(haltsWithQuota('return a.concat(b)', 100n, { a: [1, 2], b: [3, 4, 5] })).toBe(false);\n    });\n\n    it('halts when concat on large arrays exceeds quota', () => {\n      const a = Array(100).fill(0);\n      const b = Array(100).fill(0);\n      expect(haltsWithQuota('return a.concat(b)', 10n, { a, b })).toBe(true);\n    });\n  });\n\n  describe('O(1) — pop, at', () => {\n    it('pop does not halt on a large array (always 1 tick)', () => {\n      const arr = Array(200).fill(0);\n      expect(haltsWithQuota('arr.pop()', 50n, { arr })).toBe(false);\n    });\n\n    it('at does not halt on a large array (always 1 tick)', () => {\n      const arr = Array(200).fill(0);\n      expect(haltsWithQuota('return arr.at(0)', 50n, { arr })).toBe(false);\n    });\n\n    it('O(1) does not halt at quota that halts O(n) map on the same array', () => {\n      const arr = Array(200).fill(0);\n      expect(haltsWithQuota('return arr.map(x => x)', 50n, { arr })).toBe(true);\n      expect(haltsWithQuota('arr.at(1)', 50n, { arr })).toBe(false);\n    });\n  });\n\n  describe('O(n) — map, filter, forEach, reduce, find, indexOf, etc.', () => {\n    it('does not halt on a small array within quota', () => {\n      expect(haltsWithQuota('return arr.map(x => x)', 100n, { arr: [1, 2, 3] })).toBe(false);\n    });\n\n    it('halts on a large array when quota < array length', () => {\n      const arr = Array(200).fill(1);\n      expect(haltsWithQuota('return arr.map(x => x)', 10n, { arr })).toBe(true);\n    });\n\n    it('filter halts on a large array', () => {\n      const arr = Array(200).fill(1);\n      expect(haltsWithQuota('return arr.filter(x => x)', 10n, { arr })).toBe(true);\n    });\n\n    it('forEach halts on a large array', () => {\n      const arr = Array(200).fill(1);\n      expect(haltsWithQuota('arr.forEach(x => x)', 10n, { arr })).toBe(true);\n    });\n\n    it('reduce halts on a large array', () => {\n      const arr = Array(200).fill(1);\n      expect(haltsWithQuota('return arr.reduce((a, x) => a + x, 0)', 10n, { arr })).toBe(true);\n    });\n\n    it('find halts on a large array', () => {\n      const arr = Array(200).fill(1);\n      expect(haltsWithQuota('return arr.find(x => x === 2)', 10n, { arr })).toBe(true);\n    });\n\n    it('indexOf halts on a large array', () => {\n      const arr = Array(200).fill(1);\n      expect(haltsWithQuota('return arr.indexOf(2)', 10n, { arr })).toBe(true);\n    });\n\n    it('includes halts on a large array', () => {\n      const arr = Array(200).fill(1);\n      expect(haltsWithQuota('return arr.includes(2)', 10n, { arr })).toBe(true);\n    });\n\n    it('slice halts on a large array', () => {\n      const arr = Array(200).fill(1);\n      expect(haltsWithQuota('return arr.slice()', 10n, { arr })).toBe(true);\n    });\n\n    it('join halts on a large array', () => {\n      const arr = Array(200).fill('a');\n      expect(haltsWithQuota('return arr.join(\",\")', 10n, { arr })).toBe(true);\n    });\n  });\n\n  describe('O(n log n) — sort, toSorted', () => {\n    it('sort halts at a lower quota than an equivalent O(n) op on the same array', () => {\n      const arr = Array.from({ length: 200 }, (_, i) => 200 - i);\n      expect(haltsWithQuota('return arr.indexOf(-1)', 500n, { arr })).toBe(false);\n      expect(haltsWithQuota('return arr.sort()', 500n, { arr: [...arr] })).toBe(true);\n    });\n\n    it('does not halt on a small array within quota', () => {\n      expect(haltsWithQuota('return arr.sort()', 100n, { arr: [3, 1, 2] })).toBe(false);\n    });\n\n    it('toSorted halts on a large array', () => {\n      const arr = Array.from({ length: 200 }, (_, i) => 200 - i);\n      expect(haltsWithQuota('return arr.toSorted()', 500n, { arr })).toBe(true);\n    });\n  });\n\n  describe('Array.from', () => {\n    it('does not halt on a small array-like within quota', () => {\n      expect(haltsWithQuota('return Array.from(src)', 100n, { src: [1, 2, 3] })).toBe(false);\n    });\n\n    it('halts when source length exceeds quota', () => {\n      const src = Array(200).fill(1);\n      expect(haltsWithQuota('return Array.from(src)', 10n, { src })).toBe(true);\n    });\n\n    it('halts when using Array.from with a mapFn on a large source', () => {\n      const src = Array(200).fill(1);\n      expect(haltsWithQuota('return Array.from(src, x => x * 2)', 10n, { src })).toBe(true);\n    });\n\n    it('works with array-like objects with a length property', () => {\n      const src = { length: 200, 0: 'a', 1: 'b' };\n      expect(haltsWithQuota('return Array.from(src)', 10n, { src })).toBe(true);\n    });\n\n    it('does not halt when source has no length (e.g. Set)', () => {\n      expect(haltsWithQuota('return Array.from(src)', 10n, { src: new Set([1, 2, 3]) })).toBe(\n        false,\n      );\n    });\n  });\n\n  describe('arr.length =', () => {\n    it('does not halt when shrinking within quota', () => {\n      const arr = Array(5).fill(0);\n      expect(haltsWithQuota('arr.length = 2', 100n, { arr })).toBe(false);\n    });\n\n    it('halts when expanding length far exceeds quota', () => {\n      const arr: number[] = [];\n      expect(haltsWithQuota('arr.length = 200', 10n, { arr })).toBe(true);\n    });\n\n    it('halts when shrinking by a large delta that exceeds quota', () => {\n      const arr = Array(200).fill(0);\n      expect(haltsWithQuota('arr.length = 0', 10n, { arr })).toBe(true);\n    });\n\n    it('does not charge ticks when length is unchanged', () => {\n      const arr = Array(5).fill(0);\n      expect(haltsWithQuota('arr.length = 5', 5n, { arr })).toBe(false);\n    });\n  });\n});\n"
  },
  {
    "path": "test/ticks/sandboxCollectionTicks.spec.ts",
    "content": "import Sandbox from '../../src/Sandbox.js';\n\ndescribe('Map ticks', () => {\n  function haltsWithQuota(\n    code: string,\n    quota: bigint,\n    scope: Record<string, unknown> = {},\n  ): boolean {\n    const sandbox = new Sandbox({ executionQuota: quota, haltOnSandboxError: true });\n    let halted = false;\n    sandbox.subscribeHalt(() => {\n      halted = true;\n    });\n    sandbox.compile(code)(scope).run();\n    return halted;\n  }\n\n  describe('O(1) — get, set, has, delete', () => {\n    it('get does not halt on a large Map (always 1 tick)', () => {\n      const m = new Map(Array.from({ length: 200 }, (_, i) => [i, i]));\n      expect(haltsWithQuota('return m.get(0)', 50n, { m })).toBe(false);\n    });\n\n    it('set does not halt on a large Map (always 1 tick)', () => {\n      const m = new Map(Array.from({ length: 200 }, (_, i) => [i, i]));\n      expect(haltsWithQuota('m.set(999, 999)', 50n, { m })).toBe(false);\n    });\n\n    it('has does not halt on a large Map (always 1 tick)', () => {\n      const m = new Map(Array.from({ length: 200 }, (_, i) => [i, i]));\n      expect(haltsWithQuota('return m.has(0)', 50n, { m })).toBe(false);\n    });\n\n    it('delete does not halt on a large Map (always 1 tick)', () => {\n      const m = new Map(Array.from({ length: 200 }, (_, i) => [i, i]));\n      expect(haltsWithQuota('m.delete(0)', 50n, { m })).toBe(false);\n    });\n\n    it('O(1) does not halt at quota that halts O(n) forEach on same Map', () => {\n      const m = new Map(Array.from({ length: 200 }, (_, i) => [i, i]));\n      expect(haltsWithQuota('m.forEach(() => {})', 50n, { m })).toBe(true);\n      expect(haltsWithQuota('return m.get(0)', 50n, { m })).toBe(false);\n    });\n  });\n\n  describe('O(n) — keys, values, entries, forEach, clear', () => {\n    it('does not halt on a small Map within quota', () => {\n      const m = new Map([\n        ['a', 1],\n        ['b', 2],\n      ]);\n      expect(haltsWithQuota('return [...m.keys()]', 100n, { m })).toBe(false);\n    });\n\n    it('forEach halts on a large Map', () => {\n      const m = new Map(Array.from({ length: 200 }, (_, i) => [i, i]));\n      expect(haltsWithQuota('m.forEach(() => {})', 10n, { m })).toBe(true);\n    });\n\n    it('keys halts on a large Map', () => {\n      const m = new Map(Array.from({ length: 200 }, (_, i) => [i, i]));\n      expect(haltsWithQuota('return [...m.keys()]', 10n, { m })).toBe(true);\n    });\n\n    it('values halts on a large Map', () => {\n      const m = new Map(Array.from({ length: 200 }, (_, i) => [i, i]));\n      expect(haltsWithQuota('return [...m.values()]', 10n, { m })).toBe(true);\n    });\n\n    it('entries halts on a large Map', () => {\n      const m = new Map(Array.from({ length: 200 }, (_, i) => [i, i]));\n      expect(haltsWithQuota('return [...m.entries()]', 10n, { m })).toBe(true);\n    });\n\n    it('clear halts on a large Map', () => {\n      const m = new Map(Array.from({ length: 200 }, (_, i) => [i, i]));\n      expect(haltsWithQuota('m.clear()', 10n, { m })).toBe(true);\n    });\n  });\n\n  describe('null edge cases', () => {\n    it('Map.get on null map throws TypeError', () => {\n      expect(() => new Sandbox().compile('return m.get(0)')({ m: null }).run()).toThrow(TypeError);\n    });\n  });\n});\n\ndescribe('Set ticks', () => {\n  function haltsWithQuota(\n    code: string,\n    quota: bigint,\n    scope: Record<string, unknown> = {},\n  ): boolean {\n    const sandbox = new Sandbox({ executionQuota: quota, haltOnSandboxError: true });\n    let halted = false;\n    sandbox.subscribeHalt(() => {\n      halted = true;\n    });\n    sandbox.compile(code)(scope).run();\n    return halted;\n  }\n\n  describe('O(1) — add, has, delete', () => {\n    it('add does not halt on a large Set (always 1 tick)', () => {\n      const s = new Set(Array.from({ length: 200 }, (_, i) => i));\n      expect(haltsWithQuota('s.add(999)', 50n, { s })).toBe(false);\n    });\n\n    it('has does not halt on a large Set (always 1 tick)', () => {\n      const s = new Set(Array.from({ length: 200 }, (_, i) => i));\n      expect(haltsWithQuota('return s.has(0)', 50n, { s })).toBe(false);\n    });\n\n    it('delete does not halt on a large Set (always 1 tick)', () => {\n      const s = new Set(Array.from({ length: 200 }, (_, i) => i));\n      expect(haltsWithQuota('s.delete(0)', 50n, { s })).toBe(false);\n    });\n\n    it('O(1) does not halt at quota that halts O(n) forEach on same Set', () => {\n      const s = new Set(Array.from({ length: 200 }, (_, i) => i));\n      expect(haltsWithQuota('s.forEach(() => {})', 50n, { s })).toBe(true);\n      expect(haltsWithQuota('s.has(0)', 50n, { s })).toBe(false);\n    });\n  });\n\n  describe('O(n) — values, keys, entries, forEach, clear', () => {\n    it('does not halt on a small Set within quota', () => {\n      const s = new Set([1, 2, 3]);\n      expect(haltsWithQuota('return [...s.values()]', 100n, { s })).toBe(false);\n    });\n\n    it('forEach halts on a large Set', () => {\n      const s = new Set(Array.from({ length: 200 }, (_, i) => i));\n      expect(haltsWithQuota('s.forEach(() => {})', 10n, { s })).toBe(true);\n    });\n\n    it('values halts on a large Set', () => {\n      const s = new Set(Array.from({ length: 200 }, (_, i) => i));\n      expect(haltsWithQuota('return [...s.values()]', 10n, { s })).toBe(true);\n    });\n\n    it('keys halts on a large Set', () => {\n      const s = new Set(Array.from({ length: 200 }, (_, i) => i));\n      expect(haltsWithQuota('return [...s.keys()]', 10n, { s })).toBe(true);\n    });\n\n    it('entries halts on a large Set', () => {\n      const s = new Set(Array.from({ length: 200 }, (_, i) => i));\n      expect(haltsWithQuota('return [...s.entries()]', 10n, { s })).toBe(true);\n    });\n\n    it('clear halts on a large Set', () => {\n      const s = new Set(Array.from({ length: 200 }, (_, i) => i));\n      expect(haltsWithQuota('s.clear()', 10n, { s })).toBe(true);\n    });\n  });\n\n  describe('null edge cases', () => {\n    it('Set.has on null set throws TypeError', () => {\n      expect(() => new Sandbox().compile('return s.has(0)')({ s: null }).run()).toThrow(TypeError);\n    });\n  });\n});\n\ndescribe('TypedArray ticks', () => {\n  // TypedArrays need explicit whitelisting.\n  // The shared %TypedArray%.prototype(s) must be registered via fake constructors\n  // because prototypeWhitelist keys are constructors (mapped to .prototype internally).\n  // In some environments (e.g., JSDOM) Uint8Array has a patched prototype chain,\n  // so collect all unique %TypedArray% protos across the typed array types.\n  const typedArrayCtors = [\n    Int8Array,\n    Uint8Array,\n    Uint8ClampedArray,\n    Int16Array,\n    Uint16Array,\n    Int32Array,\n    Uint32Array,\n    Float32Array,\n    Float64Array,\n  ];\n  const uniqueTypedArrayProtos = new Set(\n    typedArrayCtors.map((T) => Object.getPrototypeOf(T.prototype)),\n  );\n  const prototypeWhitelist = new Map(Sandbox.SAFE_PROTOTYPES);\n  for (const proto of uniqueTypedArrayProtos) {\n    function FakeTypedArrayCtor() {}\n    FakeTypedArrayCtor.prototype = proto;\n    prototypeWhitelist.set(FakeTypedArrayCtor as any, new Set());\n  }\n  for (const T of typedArrayCtors) {\n    prototypeWhitelist.set(T, new Set());\n  }\n\n  function haltsWithQuota(\n    code: string,\n    quota: bigint,\n    scope: Record<string, unknown> = {},\n  ): boolean {\n    const sandbox = new Sandbox({\n      executionQuota: quota,\n      haltOnSandboxError: true,\n      prototypeWhitelist,\n    });\n    let halted = false;\n    sandbox.subscribeHalt(() => {\n      halted = true;\n    });\n    sandbox.compile(code)(scope).run();\n    return halted;\n  }\n\n  it('map on large TypedArray halts', () => {\n    const ta = new Int32Array(200);\n    expect(haltsWithQuota('return ta.map(x => x)', 10n, { ta })).toBe(true);\n  });\n\n  it('indexOf on large TypedArray halts', () => {\n    const ta = new Float64Array(200);\n    expect(haltsWithQuota('return ta.indexOf(-1)', 10n, { ta })).toBe(true);\n  });\n\n  it('sort on large TypedArray halts at lower quota than indexOf (O(n log n))', () => {\n    const ta = new Int32Array(Array.from({ length: 200 }, (_, i) => 200 - i));\n    expect(haltsWithQuota('return ta.indexOf(-1)', 500n, { ta })).toBe(false);\n    expect(haltsWithQuota('return ta.sort()', 500n, { ta: ta.slice() })).toBe(true);\n  });\n\n  it('at on large TypedArray does not halt (O(1))', () => {\n    const ta = new Uint8Array(200);\n    expect(haltsWithQuota('return ta.at(0)', 50n, { ta })).toBe(false);\n  });\n\n  it('does not halt on small TypedArray within quota', () => {\n    const ta = new Int32Array([1, 2, 3]);\n    expect(haltsWithQuota('return ta.map(x => x)', 100n, { ta })).toBe(false);\n  });\n});\n"
  },
  {
    "path": "test/ticks/sandboxNativeTicks.spec.ts",
    "content": "import Sandbox from '../../src/Sandbox.js';\n\ndescribe('Math ticks', () => {\n  function haltsWithQuota(\n    code: string,\n    quota: bigint,\n    scope: Record<string, unknown> = {},\n  ): boolean {\n    const sandbox = new Sandbox({ executionQuota: quota, haltOnSandboxError: true });\n    let halted = false;\n    sandbox.subscribeHalt(() => {\n      halted = true;\n    });\n    sandbox.compile(code)(scope).run();\n    return halted;\n  }\n\n  it('Math.max halts when called with many arguments', () => {\n    const args = Array.from({ length: 200 }, (_, i) => i).join(',');\n    expect(haltsWithQuota(`return Math.max(${args})`, 10n)).toBe(true);\n  });\n\n  it('Math.min halts when called with many arguments', () => {\n    const args = Array.from({ length: 200 }, (_, i) => i).join(',');\n    expect(haltsWithQuota(`return Math.min(${args})`, 10n)).toBe(true);\n  });\n\n  it('Math.max does not halt with few arguments', () => {\n    expect(haltsWithQuota('return Math.max(1, 2, 3)', 100n)).toBe(false);\n  });\n\n  it('Math.hypot halts when called with many arguments', () => {\n    const args = Array.from({ length: 200 }, (_, i) => i).join(',');\n    expect(haltsWithQuota(`return Math.hypot(${args})`, 10n)).toBe(true);\n  });\n});\n\ndescribe('JSON ticks', () => {\n  function haltsWithQuota(\n    code: string,\n    quota: bigint,\n    scope: Record<string, unknown> = {},\n  ): boolean {\n    const sandbox = new Sandbox({ executionQuota: quota, haltOnSandboxError: true });\n    let halted = false;\n    sandbox.subscribeHalt(() => {\n      halted = true;\n    });\n    sandbox.compile(code)(scope).run();\n    return halted;\n  }\n\n  it('JSON.parse halts on a long string', () => {\n    const json = JSON.stringify(Array(200).fill(1));\n    expect(haltsWithQuota('return JSON.parse(json)', 10n, { json })).toBe(true);\n  });\n\n  it('JSON.parse does not halt on a short string', () => {\n    expect(haltsWithQuota('return JSON.parse(json)', 100n, { json: '[1,2,3]' })).toBe(false);\n  });\n\n  it('JSON.stringify halts on a large object', () => {\n    const obj = Object.fromEntries(Array.from({ length: 200 }, (_, i) => [`k${i}`, i]));\n    expect(haltsWithQuota('return JSON.stringify(obj)', 10n, { obj })).toBe(true);\n  });\n});\n\ndescribe('RegExp ticks', () => {\n  function haltsWithQuota(\n    code: string,\n    quota: bigint,\n    scope: Record<string, unknown> = {},\n  ): boolean {\n    const sandbox = new Sandbox({ executionQuota: quota, haltOnSandboxError: true });\n    let halted = false;\n    sandbox.subscribeHalt(() => {\n      halted = true;\n    });\n    sandbox.compile(code)(scope).run();\n    return halted;\n  }\n\n  it('exec halts on a long input string', () => {\n    const input = 'a'.repeat(200);\n    expect(haltsWithQuota('return re.exec(input)', 10n, { re: /z/, input })).toBe(true);\n  });\n\n  it('test halts on a long input string', () => {\n    const input = 'a'.repeat(200);\n    expect(haltsWithQuota('return re.test(input)', 10n, { re: /z/, input })).toBe(true);\n  });\n\n  it('does not halt on a short input string', () => {\n    expect(haltsWithQuota('return re.test(input)', 100n, { re: /z/, input: 'hello' })).toBe(false);\n  });\n});\n\ndescribe('Promise ticks', () => {\n  function haltsWithQuota(\n    code: string,\n    quota: bigint,\n    scope: Record<string, unknown> = {},\n  ): boolean {\n    const sandbox = new Sandbox({ executionQuota: quota, haltOnSandboxError: true });\n    let halted = false;\n    sandbox.subscribeHalt(() => {\n      halted = true;\n    });\n    sandbox.compile(code)(scope).run();\n    return halted;\n  }\n\n  it('Promise.all halts on a large array of promises', () => {\n    const promises = Array.from({ length: 200 }, () => Promise.resolve(1));\n    expect(haltsWithQuota('return Promise.all(promises)', 10n, { promises })).toBe(true);\n  });\n\n  it('Promise.allSettled halts on a large array', () => {\n    const promises = Array.from({ length: 200 }, () => Promise.resolve(1));\n    expect(haltsWithQuota('return Promise.allSettled(promises)', 10n, { promises })).toBe(true);\n  });\n\n  it('Promise.race halts on a large array', () => {\n    const promises = Array.from({ length: 200 }, () => Promise.resolve(1));\n    expect(haltsWithQuota('return Promise.race(promises)', 10n, { promises })).toBe(true);\n  });\n\n  it('does not halt on a small array of promises', () => {\n    const promises = [Promise.resolve(1), Promise.resolve(2)];\n    expect(haltsWithQuota('return Promise.all(promises)', 100n, { promises })).toBe(false);\n  });\n});\n\ndescribe('new constructor ticks', () => {\n  function haltsWithQuota(\n    code: string,\n    quota: bigint,\n    scope: Record<string, unknown> = {},\n  ): boolean {\n    const sandbox = new Sandbox({ executionQuota: quota, haltOnSandboxError: true });\n    let halted = false;\n    sandbox.subscribeHalt(() => {\n      halted = true;\n    });\n    sandbox.compile(code)(scope).run();\n    return halted;\n  }\n\n  describe('new Array', () => {\n    it('new Array(n) halts when n is large', () => {\n      expect(haltsWithQuota('return new Array(200)', 10n)).toBe(true);\n    });\n\n    it('new Array(n) does not halt when n is small', () => {\n      expect(haltsWithQuota('return new Array(3)', 20n)).toBe(false);\n    });\n\n    it('new Array(...items) charges args.length ticks', () => {\n      expect(haltsWithQuota('return new Array(\"a\",\"b\",\"c\")', 10n)).toBe(false);\n      const items = Array.from({ length: 200 }, (_, i) => i).join(',');\n      expect(haltsWithQuota(`return new Array(${items})`, 10n)).toBe(true);\n    });\n  });\n\n  describe('new Map / new Set', () => {\n    it('new Map(iterable) halts when iterable is large', () => {\n      const entries = Array.from({ length: 200 }, (_, i): [number, number] => [i, i]);\n      expect(haltsWithQuota('return new Map(entries)', 10n, { entries })).toBe(true);\n    });\n\n    it('new Map(iterable) does not halt when iterable is small', () => {\n      expect(haltsWithQuota('return new Map(entries)', 20n, { entries: [[1, 1]] })).toBe(false);\n    });\n\n    it('new Set(iterable) halts when iterable is large', () => {\n      const items = Array.from({ length: 200 }, (_, i) => i);\n      expect(haltsWithQuota('return new Set(items)', 10n, { items })).toBe(true);\n    });\n\n    it('new Set(iterable) does not halt when iterable is small', () => {\n      expect(haltsWithQuota('return new Set(items)', 20n, { items: [1, 2, 3] })).toBe(false);\n    });\n  });\n\n  describe('new TypedArray', () => {\n    it('new Int32Array(n) halts when n is large', () => {\n      expect(haltsWithQuota('return new Int32Array(200)', 10n)).toBe(true);\n    });\n\n    it('new Int32Array(n) does not halt when n is small', () => {\n      expect(haltsWithQuota('return new Int32Array(3)', 20n)).toBe(false);\n    });\n\n    it('new Float64Array(n) halts when n is large', () => {\n      expect(haltsWithQuota('return new Float64Array(200)', 10n)).toBe(true);\n    });\n\n    it('new TypedArray(array) charges source length ticks', () => {\n      const src = Array.from({ length: 200 }, (_, i) => i);\n      expect(haltsWithQuota('return new Int32Array(src)', 10n, { src })).toBe(true);\n      expect(haltsWithQuota('return new Int32Array(src)', 210n, { src })).toBe(false);\n    });\n  });\n});\n"
  },
  {
    "path": "test/ticks/sandboxObjectTicks.spec.ts",
    "content": "import Sandbox from '../../src/Sandbox.js';\n\ndescribe('object ticks', () => {\n  function haltsWithQuota(\n    code: string,\n    quota: bigint,\n    scope: Record<string, unknown> = {},\n  ): boolean {\n    const sandbox = new Sandbox({ executionQuota: quota, haltOnSandboxError: true });\n    let halted = false;\n    sandbox.subscribeHalt(() => {\n      halted = true;\n    });\n    sandbox.compile(code)(scope).run();\n    return halted;\n  }\n\n  describe('O(1) — Object.is, hasOwnProperty, isPrototypeOf', () => {\n    it('Object.is does not halt on a large object (always 1 tick)', () => {\n      const obj = Object.fromEntries(Array.from({ length: 200 }, (_, i) => [`k${i}`, i]));\n      expect(haltsWithQuota('return Object.is(obj, obj)', 50n, { obj })).toBe(false);\n    });\n\n    it('hasOwnProperty does not halt on an object with many keys (always 1 tick)', () => {\n      const obj = Object.fromEntries(Array.from({ length: 200 }, (_, i) => [`k${i}`, i]));\n      expect(haltsWithQuota('return obj.hasOwnProperty(\"k0\")', 50n, { obj })).toBe(false);\n    });\n\n    it('O(1) does not halt at quota that halts O(n) on same object', () => {\n      const obj = Object.fromEntries(Array.from({ length: 200 }, (_, i) => [`k${i}`, i]));\n      expect(haltsWithQuota('return Object.keys(obj)', 50n, { obj })).toBe(true);\n      expect(haltsWithQuota('return obj.hasOwnProperty(\"k0\")', 50n, { obj })).toBe(false);\n    });\n  });\n\n  describe('O(n) static — Object.keys, Object.values, Object.entries, etc.', () => {\n    it('does not halt on a small object within quota', () => {\n      expect(haltsWithQuota('return Object.keys(obj)', 100n, { obj: { a: 1, b: 2 } })).toBe(false);\n    });\n\n    it('Object.keys halts on an object with many keys', () => {\n      const obj = Object.fromEntries(Array.from({ length: 200 }, (_, i) => [`k${i}`, i]));\n      expect(haltsWithQuota('return Object.keys(obj)', 10n, { obj })).toBe(true);\n    });\n\n    it('Object.values halts on an object with many keys', () => {\n      const obj = Object.fromEntries(Array.from({ length: 200 }, (_, i) => [`k${i}`, i]));\n      expect(haltsWithQuota('return Object.values(obj)', 10n, { obj })).toBe(true);\n    });\n\n    it('Object.entries halts on an object with many keys', () => {\n      const obj = Object.fromEntries(Array.from({ length: 200 }, (_, i) => [`k${i}`, i]));\n      expect(haltsWithQuota('return Object.entries(obj)', 10n, { obj })).toBe(true);\n    });\n\n    it('Object.getOwnPropertyNames halts on an object with many keys', () => {\n      const obj = Object.fromEntries(Array.from({ length: 200 }, (_, i) => [`k${i}`, i]));\n      expect(haltsWithQuota('return Object.getOwnPropertyNames(obj)', 10n, { obj })).toBe(true);\n    });\n  });\n});\n\ndescribe('JSON.stringify ticks', () => {\n  function haltsWithQuota(\n    code: string,\n    quota: bigint,\n    scope: Record<string, unknown> = {},\n  ): boolean {\n    const sandbox = new Sandbox({ executionQuota: quota, haltOnSandboxError: true });\n    let halted = false;\n    sandbox.subscribeHalt(() => {\n      halted = true;\n    });\n    const fn = sandbox.compile(code);\n    const { context, run } = fn(scope);\n    sandbox.subscribeGet(() => {}, context);\n    run();\n    return halted;\n  }\n\n  it('does not halt on a small object within quota', () => {\n    expect(haltsWithQuota('return JSON.stringify(obj)', 100n, { obj: { a: 1 } })).toBe(false);\n  });\n\n  it('halts when stringifying an object with many keys', () => {\n    const obj = Object.fromEntries(Array.from({ length: 200 }, (_, i) => [`k${i}`, i]));\n    expect(haltsWithQuota('return JSON.stringify(obj)', 10n, { obj })).toBe(true);\n  });\n\n  it('halts on deeply nested objects (counts keys at all levels)', () => {\n    let obj: Record<string, unknown> = {};\n    let cur = obj;\n    for (let i = 0; i < 50; i++) {\n      cur.next = {};\n      cur = cur.next as any;\n    }\n    expect(haltsWithQuota('return JSON.stringify(obj)', 10n, { obj })).toBe(true);\n  });\n\n  it('does not halt for a small object at generous quota', () => {\n    const obj = { a: 1, b: 2, c: 3 };\n    expect(haltsWithQuota('return JSON.stringify(obj)', 100n, { obj })).toBe(false);\n  });\n\n  it('fires tick check even without subscribeGet (getTicks handles JSON directly)', () => {\n    const sandbox = new Sandbox({ executionQuota: 10n, haltOnSandboxError: true });\n    let halted = false;\n    sandbox.subscribeHalt(() => {\n      halted = true;\n    });\n    const obj = Object.fromEntries(Array.from({ length: 200 }, (_, i) => [`k${i}`, i]));\n    sandbox.compile('return JSON.stringify(obj)')({ obj }).run();\n    expect(halted).toBe(true);\n  });\n});\n\ndescribe('null obj edge cases', () => {\n  function run(code: string, scope: Record<string, unknown> = {}) {\n    return new Sandbox().compile(code)(scope).run();\n  }\n\n  it('calling a method on null throws TypeError', () => {\n    expect(() => run('return null.toString()')).toThrow(TypeError);\n  });\n\n  it('JSON.stringify(null) returns \"null\" without crashing', () => {\n    expect(run('return JSON.stringify(null)')).toBe('null');\n  });\n\n  it('JSON.stringify(null) does not halt even with subscribeGet active', () => {\n    const sandbox = new Sandbox({ executionQuota: 100n, haltOnSandboxError: true });\n    let halted = false;\n    sandbox.subscribeHalt(() => {\n      halted = true;\n    });\n    const fn = sandbox.compile('return JSON.stringify(null)');\n    const { context, run: r } = fn({});\n    sandbox.subscribeGet(() => {}, context);\n    const result = r();\n    expect(halted).toBe(false);\n    expect(result).toBe('null');\n  });\n\n  it('Array.from(null) throws TypeError', () => {\n    expect(() => run('return Array.from(null)')).toThrow(TypeError);\n  });\n\n  it('Array.from(null) does not crash the tick calculation', () => {\n    const sandbox = new Sandbox({ executionQuota: 1000n, haltOnSandboxError: true });\n    let halted = false;\n    sandbox.subscribeHalt(() => {\n      halted = true;\n    });\n    expect(() => sandbox.compile('return Array.from(null)')({}).run()).toThrow(TypeError);\n    expect(halted).toBe(false);\n  });\n\n  it('Object.keys(null) throws TypeError', () => {\n    expect(() => run('return Object.keys(null)')).toThrow(TypeError);\n  });\n\n  it('arr.length = null does not charge ticks (non-number assignment)', () => {\n    const sandbox = new Sandbox({ executionQuota: 5n, haltOnSandboxError: true });\n    let halted = false;\n    sandbox.subscribeHalt(() => {\n      halted = true;\n    });\n    const arr: number[] = [1, 2, 3];\n    sandbox.compile('arr.length = null')({ arr }).run();\n    expect(halted).toBe(false);\n  });\n});\n"
  },
  {
    "path": "test/ticks/sandboxSpreadTicks.spec.ts",
    "content": "import Sandbox from '../../src/Sandbox.js';\n\ndescribe('spread ticks', () => {\n  function haltsWithQuota(\n    code: string,\n    quota: bigint,\n    scope: Record<string, unknown> = {},\n  ): boolean {\n    const sandbox = new Sandbox({ executionQuota: quota, haltOnSandboxError: true });\n    let halted = false;\n    sandbox.subscribeHalt(() => {\n      halted = true;\n    });\n    sandbox.compile(code)(scope).run();\n    return halted;\n  }\n\n  it('spread into array literal halts on large array', () => {\n    const arr = Array.from({ length: 200 }, (_, i) => i);\n    expect(haltsWithQuota('return [...arr]', 10n, { arr })).toBe(true);\n  });\n\n  it('spread into array literal does not halt on small array', () => {\n    const arr = [1, 2, 3];\n    expect(haltsWithQuota('return [...arr]', 50n, { arr })).toBe(false);\n  });\n\n  it('spread into function call halts on large array', () => {\n    const arr = Array.from({ length: 200 }, (_, i) => i);\n    expect(haltsWithQuota('return Math.max(...arr)', 10n, { arr })).toBe(true);\n  });\n\n  it('spread into function call does not halt on small array', () => {\n    const arr = [1, 2, 3];\n    expect(haltsWithQuota('return Math.max(...arr)', 50n, { arr })).toBe(false);\n  });\n\n  it('object spread halts on large object', () => {\n    const obj = Object.fromEntries(Array.from({ length: 200 }, (_, i) => [`k${i}`, i]));\n    expect(haltsWithQuota('return {...obj}', 10n, { obj })).toBe(true);\n  });\n\n  it('object spread does not halt on small object', () => {\n    const obj = { a: 1, b: 2, c: 3 };\n    expect(haltsWithQuota('return {...obj}', 50n, { obj })).toBe(false);\n  });\n\n  it('spread of generator halts on large generator', () => {\n    expect(\n      haltsWithQuota(\n        'function* gen() { for (let i = 0; i < 200; i++) yield i; } return [...gen()]',\n        10n,\n      ),\n    ).toBe(true);\n  });\n\n  it('spread of generator does not halt on small generator', () => {\n    expect(\n      haltsWithQuota('function* gen() { yield 1; yield 2; yield 3; } return [...gen()]', 50n),\n    ).toBe(false);\n  });\n\n  it('multiple array spreads accumulate ticks', () => {\n    const a = [1, 2, 3];\n    const b = [4, 5, 6];\n    expect(haltsWithQuota('return [...a]', 20n, { a })).toBe(false);\n    expect(haltsWithQuota('return [...a, ...b]', 10n, { a, b })).toBe(true);\n  });\n\n  it('spread into user-defined function halts on large array', () => {\n    const big = Array.from({ length: 200 }, (_, i) => i);\n    expect(\n      haltsWithQuota('function f(...args) { return args.length } return f(...big)', 10n, { big }),\n    ).toBe(true);\n  });\n\n  it('spread into user-defined function does not halt on small array', () => {\n    const arr = [1, 2, 3];\n    expect(\n      haltsWithQuota('function f(...args) { return args.length } return f(...arr)', 20n, { arr }),\n    ).toBe(false);\n  });\n\n  it('multiple object spreads accumulate ticks', () => {\n    const o1 = { a: 1, b: 2 };\n    const o2 = { c: 3, d: 4 };\n    expect(haltsWithQuota('return {...o1}', 20n, { o1 })).toBe(false);\n    expect(haltsWithQuota('return {...o1, ...o2}', 10n, { o1, o2 })).toBe(true);\n  });\n\n  it('spread combined with non-spread args does not double-count', () => {\n    const arr = [1, 2, 3];\n    expect(haltsWithQuota('return [0, ...arr, 4]', 20n, { arr })).toBe(false);\n  });\n});\n"
  },
  {
    "path": "test/ticks/sandboxStringTicks.spec.ts",
    "content": "import Sandbox from '../../src/Sandbox.js';\n\ndescribe('string method ticks', () => {\n  function haltsWithQuota(\n    code: string,\n    quota: bigint,\n    scope: Record<string, unknown> = {},\n  ): boolean {\n    const sandbox = new Sandbox({ executionQuota: quota, haltOnSandboxError: true });\n    let halted = false;\n    sandbox.subscribeHalt(() => {\n      halted = true;\n    });\n    sandbox.compile(code)(scope).run();\n    return halted;\n  }\n\n  describe('O(1) — charAt, charCodeAt, codePointAt, at', () => {\n    it('charAt does not halt on a long string (always 1 tick)', () => {\n      const s = 'a'.repeat(200);\n      expect(haltsWithQuota('return s.charAt(0)', 50n, { s })).toBe(false);\n    });\n\n    it('charCodeAt does not halt on a long string (always 1 tick)', () => {\n      const s = 'a'.repeat(200);\n      expect(haltsWithQuota('return s.charCodeAt(0)', 50n, { s })).toBe(false);\n    });\n\n    it('at does not halt on a long string (always 1 tick)', () => {\n      const s = 'a'.repeat(200);\n      expect(haltsWithQuota('return s.at(0)', 50n, { s })).toBe(false);\n    });\n\n    it('O(1) does not halt at quota that halts O(n) on same string', () => {\n      const s = 'a'.repeat(200);\n      expect(haltsWithQuota('return s.indexOf(\"z\")', 50n, { s })).toBe(true);\n      expect(haltsWithQuota('return s.charAt(0)', 50n, { s })).toBe(false);\n    });\n  });\n\n  describe('O(n) — indexOf, includes, slice, split, replace, trim, etc.', () => {\n    it('does not halt on a short string within quota', () => {\n      expect(haltsWithQuota('return s.indexOf(\"z\")', 100n, { s: 'hello' })).toBe(false);\n    });\n\n    it('indexOf halts on a long string exceeding quota', () => {\n      const s = 'a'.repeat(200);\n      expect(haltsWithQuota('return s.indexOf(\"z\")', 10n, { s })).toBe(true);\n    });\n\n    it('includes halts on a long string', () => {\n      const s = 'a'.repeat(200);\n      expect(haltsWithQuota('return s.includes(\"z\")', 10n, { s })).toBe(true);\n    });\n\n    it('slice halts on a long string', () => {\n      const s = 'a'.repeat(200);\n      expect(haltsWithQuota('return s.slice(0)', 10n, { s })).toBe(true);\n    });\n\n    it('split halts on a long string', () => {\n      const s = 'a'.repeat(200);\n      expect(haltsWithQuota('return s.split(\"\")', 10n, { s })).toBe(true);\n    });\n\n    it('replace halts on a long string', () => {\n      const s = 'a'.repeat(200);\n      expect(haltsWithQuota('return s.replace(\"b\", \"c\")', 10n, { s })).toBe(true);\n    });\n\n    it('toLowerCase halts on a long string', () => {\n      const s = 'A'.repeat(200);\n      expect(haltsWithQuota('return s.toLowerCase()', 10n, { s })).toBe(true);\n    });\n\n    it('trim halts on a long string', () => {\n      const s = ' '.repeat(200);\n      expect(haltsWithQuota('return s.trim()', 10n, { s })).toBe(true);\n    });\n  });\n});\n\ndescribe('string concatenation ticks (+ operator)', () => {\n  function haltsWithQuota(\n    code: string,\n    quota: bigint,\n    scope: Record<string, unknown> = {},\n  ): boolean {\n    const sandbox = new Sandbox({ executionQuota: quota, haltOnSandboxError: true });\n    let halted = false;\n    sandbox.subscribeHalt(() => {\n      halted = true;\n    });\n    sandbox.compile(code)(scope).run();\n    return halted;\n  }\n\n  it('string + string halts when result is long', () => {\n    const a = 'x'.repeat(100);\n    const b = 'y'.repeat(100);\n    expect(haltsWithQuota('return a + b', 10n, { a, b })).toBe(true);\n  });\n\n  it('string + string does not halt when result is short', () => {\n    expect(haltsWithQuota('return a + b', 100n, { a: 'hi', b: ' there' })).toBe(false);\n  });\n\n  it('string + number halts when result is long', () => {\n    const a = 'x'.repeat(200);\n    expect(haltsWithQuota('return a + 42', 10n, { a })).toBe(true);\n  });\n\n  it('number + string halts when result is long', () => {\n    const b = 'x'.repeat(200);\n    expect(haltsWithQuota('return 42 + b', 10n, { b })).toBe(true);\n  });\n\n  it('number + number does not charge string ticks', () => {\n    expect(haltsWithQuota('return 1 + 2', 5n)).toBe(false);\n  });\n\n  it('chained concatenation accumulates ticks', () => {\n    const s = 'x'.repeat(50);\n    expect(haltsWithQuota('return s + s + s + s', 10n, { s })).toBe(true);\n  });\n\n  it('empty string + empty string does not halt', () => {\n    expect(haltsWithQuota('return \"\" + \"\"', 50n)).toBe(false);\n  });\n\n  it('string literal + string literal halts when combined result is long', () => {\n    const a = 'a'.repeat(100);\n    const b = 'b'.repeat(100);\n    expect(haltsWithQuota('return a + b', 10n, { a, b })).toBe(true);\n  });\n});\n\ndescribe('string += ticks (AddEquals operator)', () => {\n  function haltsWithQuota(\n    code: string,\n    quota: bigint,\n    scope: Record<string, unknown> = {},\n  ): boolean {\n    const sandbox = new Sandbox({ executionQuota: quota, haltOnSandboxError: true });\n    let halted = false;\n    sandbox.subscribeHalt(() => {\n      halted = true;\n    });\n    sandbox.compile(code)(scope).run();\n    return halted;\n  }\n\n  it('+= on string halts when result is long', () => {\n    const s = 'x'.repeat(100);\n    expect(haltsWithQuota('let r = s; r += s; return r', 10n, { s })).toBe(true);\n  });\n\n  it('+= on string does not halt when result is short', () => {\n    expect(haltsWithQuota('let r = \"hi\"; r += \" there\"; return r', 100n)).toBe(false);\n  });\n\n  it('+= on number does not charge string ticks', () => {\n    expect(haltsWithQuota('let n = 1; n += 2; return n', 50n)).toBe(false);\n  });\n\n  it('+= string appended multiple times accumulates ticks', () => {\n    const s = 'x'.repeat(40);\n    // first += produces 80-char string, second produces 120-char — should halt early\n    expect(haltsWithQuota('let r = s; r += s; r += s; return r', 10n, { s })).toBe(true);\n  });\n\n  it('+= number to string halts when result is long', () => {\n    const s = 'x'.repeat(200);\n    expect(haltsWithQuota('let r = s; r += 1; return r', 10n, { s })).toBe(true);\n  });\n\n  it('+= string to number coerces and halts when result is long', () => {\n    const s = 'x'.repeat(200);\n    expect(haltsWithQuota('let r = 1; r += s; return r', 10n, { s })).toBe(true);\n  });\n});\n\ndescribe('template literal ticks', () => {\n  function haltsWithQuota(\n    code: string,\n    quota: bigint,\n    scope: Record<string, unknown> = {},\n  ): boolean {\n    const sandbox = new Sandbox({ executionQuota: quota, haltOnSandboxError: true });\n    let halted = false;\n    sandbox.subscribeHalt(() => {\n      halted = true;\n    });\n    sandbox.compile(code)(scope).run();\n    return halted;\n  }\n\n  it('template literal halts when result is long', () => {\n    const s = 'x'.repeat(200);\n    expect(haltsWithQuota('return `prefix_${s}`', 10n, { s })).toBe(true);\n  });\n\n  it('template literal does not halt when result is short', () => {\n    expect(haltsWithQuota('return `hello ${\"world\"}`', 100n)).toBe(false);\n  });\n\n  it('template literal with large interpolated value halts', () => {\n    const val = 'a'.repeat(200);\n    expect(haltsWithQuota('return `${val}`', 10n, { val })).toBe(true);\n  });\n\n  it('template literal with no interpolation does not halt for short string', () => {\n    expect(haltsWithQuota('return `hello world`', 100n)).toBe(false);\n  });\n\n  it('template literal with multiple interpolations halts when total is long', () => {\n    const a = 'x'.repeat(100);\n    const b = 'y'.repeat(100);\n    expect(haltsWithQuota('return `${a}${b}`', 10n, { a, b })).toBe(true);\n  });\n\n  it('template literal with multiple interpolations does not halt when total is short', () => {\n    expect(haltsWithQuota('return `${\"hi\"} ${\"there\"}`', 100n)).toBe(false);\n  });\n\n  it('template literal with number interpolation does not halt when short', () => {\n    expect(haltsWithQuota('return `value: ${42}`', 100n)).toBe(false);\n  });\n});\n\ndescribe('String() cast ticks', () => {\n  function haltsWithQuota(\n    code: string,\n    quota: bigint,\n    scope: Record<string, unknown> = {},\n  ): boolean {\n    const sandbox = new Sandbox({ executionQuota: quota, haltOnSandboxError: true });\n    let halted = false;\n    sandbox.subscribeHalt(() => {\n      halted = true;\n    });\n    sandbox.compile(code)(scope).run();\n    return halted;\n  }\n\n  it('String(s) halts when s is a long string', () => {\n    const s = 'x'.repeat(200);\n    expect(haltsWithQuota('return String(s)', 10n, { s })).toBe(true);\n  });\n\n  it('String(s) does not halt when s is a short string', () => {\n    expect(haltsWithQuota('return String(s)', 100n, { s: 'hello' })).toBe(false);\n  });\n\n  it('String(n) does not halt for small number', () => {\n    expect(haltsWithQuota('return String(42)', 50n)).toBe(false);\n  });\n\n  it('String(true) does not halt (result is 4 chars)', () => {\n    expect(haltsWithQuota('return String(true)', 50n)).toBe(false);\n  });\n\n  it('String(null) does not halt (result is 4 chars)', () => {\n    expect(haltsWithQuota('return String(null)', 50n)).toBe(false);\n  });\n\n  it('String(undefined) does not halt (result is 9 chars)', () => {\n    expect(haltsWithQuota('return String(undefined)', 50n)).toBe(false);\n  });\n\n  it('String(obj) with object whose toString returns long string halts', () => {\n    const obj = { toString: () => 'x'.repeat(200) };\n    expect(haltsWithQuota('return String(obj)', 10n, { obj })).toBe(true);\n  });\n});\n"
  },
  {
    "path": "test/ticksQuotaHalt.spec.ts",
    "content": "import Sandbox from '../src/Sandbox';\nimport { SandboxExecutionQuotaExceededError } from '../src/utils';\n\ndescribe('ticks quota halt', () => {\n  it('should trigger halt subscription when tick quota is exceeded', async () => {\n    const sandbox = new Sandbox({\n      haltOnSandboxError: true,\n      executionQuota: BigInt(100),\n    });\n    let haltCalled = false;\n\n    sandbox.subscribeHalt((params) => {\n      if (params.type === 'error' && params.error instanceof SandboxExecutionQuotaExceededError) {\n        haltCalled = true;\n      }\n    });\n\n    const code = `\n      let str = '';\n      for (let i = 0; i < 1000; i++) {\n        str += 'a';\n      }\n    `;\n    const fn = sandbox.compile(code);\n    const { run } = fn();\n\n    run();\n\n    expect(haltCalled).toBe(true);\n  });\n\n  it('should allow resuming after tick quota halt', async () => {\n    const sandbox = new Sandbox({\n      haltOnSandboxError: true,\n      executionQuota: BigInt(100),\n    });\n    let haltCalled = false;\n    sandbox.subscribeHalt((params) => {\n      if (params.type === 'error' && params.error instanceof SandboxExecutionQuotaExceededError) {\n        haltCalled = true;\n        params.ticks.tickLimit = params.ticks.ticks + BigInt(10000); // Increase tick limit to allow resuming\n        sandbox.resumeExecution();\n      }\n    });\n\n    const code = `\n      let str = '';\n      for (let i = 0; i < 100; i++) {\n        str += 'a';\n      }\n      return str.length;\n    `;\n    const fn = sandbox.compileAsync(code);\n    const { run } = fn();\n\n    const len = await run();\n\n    expect(haltCalled).toBe(true);\n    expect(len).toBe(100);\n  });\n\n  it('should halt sandbox A when execution quota is exceeded', (done) => {\n    const globals = { ...Sandbox.SAFE_GLOBALS, setTimeout, clearTimeout };\n    const prototypeWhitelist = Sandbox.SAFE_PROTOTYPES;\n\n    const sandboxA = new Sandbox({\n      globals,\n      prototypeWhitelist,\n      executionQuota: 50n,\n      haltOnSandboxError: true,\n    });\n    let haltedA = false;\n    sandboxA.subscribeHalt(() => {\n      haltedA = true;\n    });\n\n    const sandboxB = new Sandbox({ globals, prototypeWhitelist });\n\n    // Sandbox A schedules a heavy string handler\n    sandboxA\n      .compile(\n        'setTimeout(\"let x=0; for (let i=0;i<200;i++){ x += i } globalThis.doneA = true;\", 0);',\n      )()\n      .run();\n\n    // Run sandbox B before A's timer fires\n    sandboxB.compile('1+1')().run();\n\n    setTimeout(() => {\n      expect(haltedA).toBe(true);\n      expect(sandboxA.context.sandboxGlobal.doneA).toBeUndefined();\n      done();\n    }, 50);\n  });\n\n  it('should allow resuming after a quota halt in the middle of sync generator iteration', async () => {\n    const sandbox = new Sandbox({\n      haltOnSandboxError: true,\n    });\n    const progress: number[] = [];\n    let haltCalled = false;\n\n    const fn = sandbox.compileAsync(`\n      function* gen(limit) {\n        for (let i = 0; i < limit; i++) {\n          let spin = 0;\n          for (let j = 0; j < 40; j++) {\n            spin += j;\n          }\n          yield i + spin * 0;\n        }\n      }\n\n      for (const value of gen(5)) {\n        progress.push(value);\n        if (progress.length === 2) {\n          armQuota();\n        }\n      }\n\n      return progress.slice();\n    `);\n\n    const { context, run } = fn({\n      progress,\n      armQuota: () => {\n        context.ctx.ticks.tickLimit = context.ctx.ticks.ticks + 30n;\n      },\n    });\n\n    sandbox.subscribeHalt((params) => {\n      if (params.type === 'error' && params.error instanceof SandboxExecutionQuotaExceededError) {\n        haltCalled = true;\n        expect(progress).toEqual([0, 1]);\n        params.ticks.tickLimit = params.ticks.ticks + 10000n;\n        sandbox.resumeExecution();\n      }\n    });\n\n    const values = await run();\n\n    expect(haltCalled).toBe(true);\n    expect(values).toEqual([0, 1, 2, 3, 4]);\n    expect(progress).toEqual([0, 1, 2, 3, 4]);\n  });\n\n  it('should allow resuming async generator next() after a quota halt', async () => {\n    const sandbox = new Sandbox({\n      haltOnSandboxError: true,\n    });\n    let haltCalled = false;\n\n    const fn = sandbox.compile(`\n      async function* gen(limit) {\n        for (let i = 0; i < limit; i++) {\n          let spin = 0;\n          for (let j = 0; j < 40; j++) {\n            spin += j;\n          }\n          yield i + spin * 0;\n        }\n      }\n\n      return gen(3);\n    `);\n\n    const { context, run } = fn();\n    const gen = run() as AsyncGenerator<number>;\n\n    expect(await gen.next()).toEqual({ value: 0, done: false });\n\n    sandbox.subscribeHalt((params) => {\n      if (params.type === 'error' && params.error instanceof SandboxExecutionQuotaExceededError) {\n        haltCalled = true;\n        params.ticks.tickLimit = params.ticks.ticks + 10000n;\n        sandbox.resumeExecution();\n      }\n    });\n\n    context.ctx.ticks.tickLimit = context.ctx.ticks.ticks + 30n;\n\n    expect(await gen.next()).toEqual({ value: 1, done: false });\n    expect(await gen.next()).toEqual({ value: 2, done: false });\n    expect(await gen.next()).toEqual({ value: undefined, done: true });\n    expect(haltCalled).toBe(true);\n  });\n\n  it('should automatically halt and resume in nonBlocking mode', async () => {\n    const sandbox = new Sandbox({\n      nonBlocking: true,\n    });\n    let haltCount = 0;\n    let resumeCount = 0;\n\n    sandbox.subscribeHalt(() => {\n      haltCount++;\n    });\n    sandbox.subscribeResume(() => {\n      resumeCount++;\n    });\n\n    const fn = sandbox.compileAsync(`\n      for (let i = 0; i < 20000; i++) {\n        total += 1;\n      }\n      return total;\n    `);\n\n    const scope = { total: 0 };\n\n    const { run } = fn(scope);\n\n    const res = run();\n\n    expect(scope.total).toBeLessThan(20000);\n    expect(scope.total).toBeGreaterThan(0);\n\n    const result = await res;\n\n    expect(result).toBe(20000);\n    expect(haltCount).toBeGreaterThan(0);\n    expect(resumeCount).toBeGreaterThan(0);\n    expect(resumeCount).toBe(haltCount);\n  });\n\n  it('should automatically halt and resume in the middle of generator iteration in nonBlocking mode', async () => {\n    const sandbox = new Sandbox({\n      nonBlocking: true,\n    });\n    const progress: number[] = [];\n    let haltCount = 0;\n    let resumeCount = 0;\n\n    sandbox.subscribeHalt(() => {\n      haltCount++;\n    });\n    sandbox.subscribeResume(() => {\n      resumeCount++;\n    });\n\n    const fn = sandbox.compileAsync<number[]>(`\n      function* gen(limit) {\n        for (let i = 0; i < limit; i++) {\n          let spin = 0;\n          for (let j = 0; j < 60; j++) {\n            spin += j;\n          }\n          yield i + spin * 0;\n        }\n      }\n\n      for (const value of gen(300)) {\n        progress.push(value);\n      }\n\n      return progress.slice();\n    `);\n\n    const { run } = fn({ progress });\n    const values = await run();\n\n    expect(values).toHaveLength(300);\n    expect(values[0]).toBe(0);\n    expect(values[299]).toBe(299);\n    expect(progress).toEqual(values);\n    expect(haltCount).toBeGreaterThan(0);\n    expect(resumeCount).toBeGreaterThan(0);\n    expect(resumeCount).toBe(haltCount);\n  });\n\n  it('should throw when executionQuota is exceeded without haltOnSandboxError', () => {\n    const sandbox = new Sandbox({\n      executionQuota: BigInt(100),\n    });\n\n    const code = `\n      let str = '';\n      for (let i = 0; i < 1000; i++) {\n        str += 'a';\n      }\n    `;\n    const fn = sandbox.compile(code);\n    const { run } = fn();\n\n    expect(() => run()).toThrow(SandboxExecutionQuotaExceededError);\n  });\n});\n"
  },
  {
    "path": "test/timers.spec.ts",
    "content": "import Sandbox from '../src/Sandbox.js';\n\ndescribe('Timer Tests', () => {\n  describe('setTimeout', () => {\n    it('should execute setTimeout callback', (done) => {\n      const sandbox = new Sandbox({\n        globals: {\n          setTimeout,\n        },\n      });\n      let result = 0;\n      const code = `\n        setTimeout(() => {\n          result = 42;\n        }, 10);\n      `;\n      const fn = sandbox.compile(code);\n      const scope = { result };\n      const { context, run } = fn(scope);\n      run();\n\n      setTimeout(() => {\n        expect(scope.result).toBe(42);\n        done();\n      }, 50);\n    });\n\n    it('should clear setTimeout with clearTimeout', (done) => {\n      const sandbox = new Sandbox({\n        globals: {\n          setTimeout,\n          clearTimeout,\n        },\n      });\n      let result = 0;\n      const code = `\n        const timeoutId = setTimeout(() => {\n          result = 42;\n        }, 10);\n        clearTimeout(timeoutId);\n      `;\n      const fn = sandbox.compile(code);\n      const scope = { result };\n      const { context, run } = fn(scope);\n      run();\n\n      setTimeout(() => {\n        expect(scope.result).toBe(0);\n        done();\n      }, 50);\n    });\n  });\n\n  describe('setInterval', () => {\n    it('should execute setInterval callback multiple times', (done) => {\n      const sandbox = new Sandbox({\n        globals: {\n          setInterval,\n          clearInterval,\n        },\n      });\n      let count = 0;\n      const code = `\n        const intervalId = setInterval(() => {\n          count++;\n          if (count >= 3) {\n            clearInterval(intervalId);\n          }\n        }, 10);\n      `;\n      const fn = sandbox.compile(code);\n      const scope = { count };\n      const { context, run } = fn(scope);\n      run();\n\n      setTimeout(() => {\n        expect(scope.count).toBeGreaterThanOrEqual(3);\n        done();\n      }, 100);\n    });\n\n    it('should clear setInterval with clearInterval', (done) => {\n      const sandbox = new Sandbox({\n        globals: {\n          setInterval,\n          clearInterval,\n        },\n      });\n      let count = 0;\n      const code = `\n        const intervalId = setInterval(() => {\n          count++;\n        }, 10);\n        clearInterval(intervalId);\n      `;\n      const fn = sandbox.compile(code);\n      const scope = { count };\n      const { context, run } = fn(scope);\n      run();\n\n      setTimeout(() => {\n        expect(scope.count).toBe(0);\n        done();\n      }, 50);\n    });\n  });\n\n  describe('setTimeout/setInterval with string arguments', () => {\n    it('should execute setTimeout with string code argument', (done) => {\n      const sandbox = new Sandbox({\n        globals: {\n          setTimeout,\n          clearTimeout,\n        },\n      });\n      let completed = false;\n      const code = `\n        // String code executes in isolated scope\n        setTimeout('1 + 1', 10);\n        setTimeout(() => {\n          completed = true;\n        }, 30);\n      `;\n      const fn = sandbox.compile(code);\n      const scope = { completed };\n      const { context, run } = fn(scope);\n      run();\n\n      setTimeout(() => {\n        expect(scope.completed).toBe(true);\n        done();\n      }, 50);\n    });\n\n    it('should execute setInterval with string code argument', (done) => {\n      const sandbox = new Sandbox({\n        globals: {\n          globalThis,\n          setTimeout,\n          setInterval,\n          clearInterval,\n        },\n      });\n      let completed = false;\n      const code = `\n        globalThis.completed = completed;\n        const intervalId = setInterval('this.completed = true', 10);\n        setTimeout(() => {\n          clearInterval(intervalId);\n          completed = globalThis.completed;\n        }, 55);\n      `;\n      const fn = sandbox.compile(code);\n      const scope = { completed };\n      const { context, run } = fn(scope);\n      run();\n\n      setTimeout(() => {\n        expect(scope.completed).toBe(true);\n        done();\n      }, 100);\n    });\n\n    it('should clear setTimeout with string code before execution', (done) => {\n      const sandbox = new Sandbox({\n        globals: {\n          globalThis,\n          setTimeout,\n          clearTimeout,\n        },\n      });\n      let completed = false;\n      const code = `\n        const timeoutId = setTimeout('this.completed = 1 + 1', 10);\n        clearTimeout(timeoutId);\n        completed = globalThis.completed || false;\n      `;\n      const fn = sandbox.compile(code);\n      const scope = { completed };\n      const { context, run } = fn(scope);\n      run();\n\n      setTimeout(() => {\n        expect(scope.completed).toBe(false);\n        done();\n      }, 50);\n    });\n\n    it('should execute string code with multiple statements', (done) => {\n      const sandbox = new Sandbox({\n        globals: {\n          globalThis,\n          setTimeout,\n          clearTimeout,\n        },\n      });\n      let completed = false;\n      const code = `\n        globalThis.completed = completed;\n        setTimeout('let x = 5; let y = 10; this.completed = x + y', 10);\n        setTimeout(() => {\n          completed = globalThis.completed;\n        }, 30);\n      `;\n      const fn = sandbox.compile(code);\n      const scope = { completed };\n      const { context, run } = fn(scope);\n      run();\n\n      setTimeout(() => {\n        expect(scope.completed).toEqual(15);\n        done();\n      }, 50);\n    });\n\n    it('should execute string code with expressions', (done) => {\n      const sandbox = new Sandbox({\n        globals: {\n          setTimeout,\n          clearTimeout,\n        },\n      });\n      let completed = false;\n      const code = `\n        setTimeout('2 + 3', 10);\n        setTimeout(() => {\n          completed = true;\n        }, 30);\n      `;\n      const fn = sandbox.compile(code);\n      const scope = { completed };\n      const { context, run } = fn(scope);\n      run();\n\n      setTimeout(() => {\n        expect(scope.completed).toBe(true);\n        done();\n      }, 50);\n    });\n\n    it('should handle empty string code', (done) => {\n      const sandbox = new Sandbox({\n        globals: {\n          setTimeout,\n          clearTimeout,\n        },\n      });\n      let completed = false;\n      const code = `\n        setTimeout('', 10);\n        setTimeout(() => {\n          completed = true;\n        }, 30);\n      `;\n      const fn = sandbox.compile(code);\n      const scope = { completed };\n      const { context, run } = fn(scope);\n      run();\n\n      setTimeout(() => {\n        expect(scope.completed).toBe(true);\n        done();\n      }, 50);\n    });\n  });\n});\n"
  },
  {
    "path": "test/timersAsync.spec.ts",
    "content": "import Sandbox from '../src/Sandbox.js';\n\ndescribe('Async Timer Tests', () => {\n  describe('setTimeout with async execution', () => {\n    it('should execute async setTimeout callback', async () => {\n      const sandbox = new Sandbox();\n      let result = 0;\n\n      // Provide a delay function to the sandbox\n      const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));\n\n      const code = `\n        await delay(50);\n        result = 42;\n      `;\n      const fn = sandbox.compileAsync(code);\n      const scope = { result, delay };\n      const { context, run } = fn(scope);\n\n      await run();\n      expect(scope.result).toBe(42);\n    });\n\n    it('should handle multiple async delays', async () => {\n      const sandbox = new Sandbox();\n      let results: number[] = [];\n\n      const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));\n\n      const code = `\n        results.push(1);\n        await delay(30);\n        results.push(2);\n        await delay(30);\n        results.push(3);\n      `;\n      const fn = sandbox.compileAsync(code);\n      const scope = { results, delay };\n      const { context, run } = fn(scope);\n\n      await run();\n      expect(scope.results).toEqual([1, 2, 3]);\n    });\n\n    it('should handle async setTimeout in sandbox', async () => {\n      const sandbox = new Sandbox({\n        globals: { setTimeout },\n      });\n      let result = 0;\n\n      const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));\n\n      const code = `\n        setTimeout(async () => {\n          await delay(30);\n          result = 42;\n        }, 20);\n      `;\n      const fn = sandbox.compileAsync(code);\n      const scope = { result, delay };\n      const { context, run } = fn(scope);\n\n      await run();\n\n      // Wait for the async callback to complete\n      await new Promise((resolve) => setTimeout(resolve, 100));\n      expect(scope.result).toBe(42);\n    });\n  });\n});\n"
  },
  {
    "path": "test/timersAsyncHalt.spec.ts",
    "content": "import Sandbox from '../src/Sandbox.js';\n\ndescribe('Async Halt and Resume Tests', () => {\n  describe('async execution with halt/resume', () => {\n    it('should halt async execution and resume', async () => {\n      const sandbox = new Sandbox();\n      let result = 0;\n      let executionCompleted = false;\n\n      const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));\n\n      const code = `\n        result = 1;\n        await delay(50);\n        result = 2;\n        await delay(50);\n        result = 3;\n      `;\n      const fn = sandbox.compileAsync(code);\n      const scope = { result, delay };\n      const { context, run } = fn(scope);\n\n      // Start execution but don't wait\n      const promise = run().then(() => {\n        executionCompleted = true;\n      });\n\n      // Wait a bit for first assignment\n      await new Promise((resolve) => setTimeout(resolve, 30));\n      expect(scope.result).toBe(1);\n\n      // Halt execution\n      sandbox.haltExecution();\n\n      // Wait and verify execution is halted\n      await new Promise((resolve) => setTimeout(resolve, 100));\n      expect(scope.result).toBe(1); // Should still be 1\n      expect(executionCompleted).toBe(false);\n\n      // Resume execution\n      sandbox.resumeExecution();\n\n      // Wait for completion\n      await promise;\n      expect(scope.result).toBe(3);\n      expect(executionCompleted).toBe(true);\n    }, 10000);\n\n    it('should handle halt subscription during async execution', async () => {\n      const sandbox = new Sandbox();\n      let haltCalled = false;\n\n      sandbox.subscribeHalt(() => {\n        haltCalled = true;\n      });\n\n      const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));\n\n      const code = `\n        await delay(50);\n      `;\n      const fn = sandbox.compileAsync(code);\n      const scope = { delay };\n      const { context, run } = fn(scope);\n\n      // Start execution\n      const promise = run();\n\n      // Wait a bit then halt\n      await new Promise((resolve) => setTimeout(resolve, 30));\n      sandbox.haltExecution();\n\n      expect(haltCalled).toBe(true);\n\n      // Resume to allow cleanup\n      sandbox.resumeExecution();\n      await promise;\n    });\n\n    it('should handle resume subscription during async execution', async () => {\n      const sandbox = new Sandbox();\n      let resumeCalled = false;\n\n      sandbox.subscribeResume(() => {\n        resumeCalled = true;\n      });\n\n      const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));\n\n      const code = `\n        await delay(50);\n      `;\n      const fn = sandbox.compileAsync(code);\n      const scope = { delay };\n      const { context, run } = fn(scope);\n\n      // Start execution\n      const promise = run();\n\n      // Wait a bit then halt\n      await new Promise((resolve) => setTimeout(resolve, 30));\n      sandbox.haltExecution();\n\n      // Resume\n      await new Promise((resolve) => setTimeout(resolve, 20));\n      sandbox.resumeExecution();\n\n      expect(resumeCalled).toBe(true);\n\n      // Wait for completion\n      await promise;\n    });\n\n    it('should pause execution on tick limit and resume', async () => {\n      const sandbox = new Sandbox({ haltOnSandboxError: true });\n      let result = 0;\n\n      const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));\n\n      const code = `\n        for (let i = 0; i < 100; i++) {\n          result = i;\n        }\n        await delay(10);\n        result = 999;\n      `;\n      const fn = sandbox.compileAsync(code);\n      const scope = { result, delay };\n      const { context, run } = fn(scope);\n\n      // Set a tick limit\n      context.ctx.ticks.tickLimit = 100n;\n\n      let haltCalled = 0;\n      sandbox.subscribeHalt(() => {\n        haltCalled++;\n      });\n\n      // Start execution - should halt due to tick limit\n      const promise = run();\n\n      // Wait for halt\n      await new Promise((resolve) => setTimeout(resolve, 100));\n      expect(haltCalled).toBe(1);\n\n      // Resume execution\n      context.ctx.ticks.tickLimit = undefined;\n      sandbox.resumeExecution();\n\n      // Wait for completion\n      await promise;\n      expect(scope.result).toBe(999);\n    });\n  });\n});\n"
  },
  {
    "path": "test/timersHalt.spec.ts",
    "content": "import Sandbox from '../src/Sandbox.js';\n\ndescribe('Timer Halt and Resume Tests', () => {\n  describe('setTimeout with halt/resume', () => {\n    it('should pause setTimeout when halted and resume when continued', (done) => {\n      const sandbox = new Sandbox({\n        globals: {\n          setTimeout,\n        },\n      });\n      let result = 0;\n      const code = `\n        setTimeout(() => {\n          result = 42;\n        }, 50);\n      `;\n      const fn = sandbox.compile(code);\n      const scope = { result };\n      const { context, run } = fn(scope);\n      run();\n\n      // Halt immediately\n      sandbox.haltExecution();\n\n      // Check that callback didn't execute during halt\n      setTimeout(() => {\n        expect(scope.result).toBe(0);\n\n        // Resume execution\n        sandbox.resumeExecution();\n\n        // Check that callback executes after resume\n        setTimeout(() => {\n          expect(scope.result).toBe(42);\n          done();\n        }, 100);\n      }, 100);\n    });\n\n    it('should trigger halt subscription when halted', (done) => {\n      const sandbox = new Sandbox({\n        globals: {\n          setTimeout,\n        },\n      });\n      let haltCalled = false;\n\n      sandbox.subscribeHalt(() => {\n        haltCalled = true;\n      });\n\n      const code = `\n        setTimeout(() => {}, 100);\n      `;\n      const fn = sandbox.compile(code);\n      const { context, run } = fn();\n      run();\n\n      sandbox.haltExecution();\n\n      setTimeout(() => {\n        expect(haltCalled).toBe(true);\n        done();\n      }, 50);\n    });\n\n    it('should trigger resume subscription when resumed', (done) => {\n      const sandbox = new Sandbox({\n        globals: {\n          setTimeout,\n        },\n      });\n      let resumeCalled = false;\n\n      sandbox.subscribeResume(() => {\n        resumeCalled = true;\n      });\n\n      const code = `\n        setTimeout(() => {}, 100);\n      `;\n      const fn = sandbox.compile(code);\n      const { context, run } = fn();\n      run();\n\n      sandbox.haltExecution();\n\n      setTimeout(() => {\n        sandbox.resumeExecution();\n        expect(resumeCalled).toBe(true);\n        done();\n      }, 50);\n    });\n  });\n\n  describe('setInterval with halt/resume', () => {\n    it('should pause setInterval when halted and resume when continued', (done) => {\n      const sandbox = new Sandbox({\n        globals: {\n          setInterval,\n          clearInterval,\n        },\n      });\n      let count = 0;\n      const code = `\n        const intervalId = setInterval(() => {\n          count++;\n          if (count >= 5) {\n            clearInterval(intervalId);\n          }\n        }, 20);\n      `;\n      const fn = sandbox.compile(code);\n      const scope = { count };\n      const { context, run } = fn(scope);\n      run();\n\n      // Let it run a bit\n      setTimeout(() => {\n        const countBeforeHalt = scope.count;\n        sandbox.haltExecution();\n\n        // Wait during halt\n        setTimeout(() => {\n          // Count should not increase during halt\n          expect(scope.count).toBe(countBeforeHalt);\n\n          // Resume execution\n          sandbox.resumeExecution();\n\n          // Wait for interval to complete\n          setTimeout(() => {\n            expect(scope.count).toBeGreaterThanOrEqual(5);\n            done();\n          }, 200);\n        }, 100);\n      }, 50);\n    });\n  });\n});\n"
  },
  {
    "path": "test/tryFinallyControlFlow.spec.ts",
    "content": "import Sandbox from '../src/Sandbox.js';\n\ndescribe('Try/Finally Control Flow Tests', () => {\n  let sandbox: Sandbox;\n\n  beforeEach(() => {\n    sandbox = new Sandbox();\n  });\n\n  describe('Finally with return overrides try/catch', () => {\n    it('should return from finally instead of try', () => {\n      const code = `\n        function test() {\n          try {\n            return 1;\n          } finally {\n            return 2;\n          }\n        }\n        return test();\n      `;\n      const result = sandbox.compile(code)().run();\n      expect(result).toBe(2);\n    });\n\n    it('should return from finally instead of catch', () => {\n      const code = `\n        function test() {\n          try {\n            throw new Error('test');\n          } catch (e) {\n            return 1;\n          } finally {\n            return 2;\n          }\n        }\n        return test();\n      `;\n      const result = sandbox.compile(code)().run();\n      expect(result).toBe(2);\n    });\n  });\n\n  describe('Finally with break overrides try/catch', () => {\n    it('should break from finally instead of continuing try loop', () => {\n      const code = `\n        let result = '';\n        for (let i = 0; i < 5; i++) {\n          try {\n            result += i;\n            if (i === 2) continue;\n          } finally {\n            if (i === 2) break;\n          }\n        }\n        return result;\n      `;\n      const result = sandbox.compile(code)().run();\n      // Should be '012' - breaks at i=2 in finally, ignoring try's continue\n      expect(result).toBe('012');\n    });\n\n    it('should break from finally overriding try return in loop', () => {\n      const code = `\n        function test() {\n          let result = '';\n          for (let i = 0; i < 5; i++) {\n            try {\n              result += i;\n              if (i === 2) return 'early';\n            } finally {\n              if (i === 2) break;\n            }\n          }\n          return result;\n        }\n        return test();\n      `;\n      const result = sandbox.compile(code)().run();\n      // Finally's break overrides try's return, so function returns '012' not 'early'\n      expect(result).toBe('012');\n    });\n\n    it('should break from finally overriding catch', () => {\n      const code = `\n        let result = '';\n        for (let i = 0; i < 5; i++) {\n          try {\n            if (i === 2) throw new Error('test');\n            result += i;\n          } catch (e) {\n            result += 'E';\n          } finally {\n            if (i === 2) break;\n          }\n        }\n        return result;\n      `;\n      const result = sandbox.compile(code)().run();\n      // Should be '01E' - breaks at i=2 in finally after catch executes\n      expect(result).toBe('01E');\n    });\n  });\n\n  describe('Finally with continue overrides try/catch', () => {\n    it('should continue from finally instead of breaking from try', () => {\n      const code = `\n        let result = '';\n        for (let i = 0; i < 5; i++) {\n          try {\n            result += i;\n            if (i === 2) break;\n          } finally {\n            if (i === 2) continue;\n          }\n        }\n        return result;\n      `;\n      const result = sandbox.compile(code)().run();\n      // Should be '01234' - finally's continue overrides try's break\n      expect(result).toBe('01234');\n    });\n\n    it('should continue from finally overriding try return in loop', () => {\n      const code = `\n        function test() {\n          let result = '';\n          for (let i = 0; i < 5; i++) {\n            try {\n              result += i;\n              if (i === 2) return 'early';\n            } finally {\n              if (i === 2) continue;\n            }\n          }\n          return result;\n        }\n        return test();\n      `;\n      const result = sandbox.compile(code)().run();\n      // Finally's continue overrides try's return, continues loop\n      expect(result).toBe('01234');\n    });\n\n    it('should continue from finally overriding catch', () => {\n      const code = `\n        let result = '';\n        for (let i = 0; i < 5; i++) {\n          try {\n            if (i === 2) throw new Error('test');\n            result += i;\n          } catch (e) {\n            result += 'E';\n            break;\n          } finally {\n            if (i === 2) continue;\n          }\n        }\n        return result;\n      `;\n      const result = sandbox.compile(code)().run();\n      // Should be '01E34' - finally's continue overrides catch's break\n      expect(result).toBe('01E34');\n    });\n  });\n\n  describe('Normal finally behavior (no control flow in finally)', () => {\n    it('should allow try return to pass through when finally has no control flow', () => {\n      const code = `\n        function test() {\n          try {\n            return 1;\n          } finally {\n            let x = 2; // No control flow\n          }\n        }\n        return test();\n      `;\n      const result = sandbox.compile(code)().run();\n      expect(result).toBe(1);\n    });\n\n    it('should allow try break to pass through', () => {\n      const code = `\n        let result = '';\n        for (let i = 0; i < 5; i++) {\n          try {\n            result += i;\n            if (i === 2) break;\n          } finally {\n            result += '-';\n          }\n        }\n        return result;\n      `;\n      const result = sandbox.compile(code)().run();\n      expect(result).toBe('0-1-2-');\n    });\n\n    it('should allow try continue to pass through', () => {\n      const code = `\n        let result = '';\n        for (let i = 0; i < 5; i++) {\n          try {\n            result += i;\n            if (i === 2) continue;\n            result += 'X';\n          } finally {\n            result += '-';\n          }\n        }\n        return result;\n      `;\n      const result = sandbox.compile(code)().run();\n      expect(result).toBe('0X-1X-2-3X-4X-');\n    });\n  });\n\n  describe('Finally with throw still overrides', () => {\n    it('should throw from finally overriding try return', () => {\n      const code = `\n        function test() {\n          try {\n            return 1;\n          } finally {\n            throw new Error('finally error');\n          }\n        }\n        try {\n          return test();\n        } catch (e) {\n          return e.message;\n        }\n      `;\n      const result = sandbox.compile(code)().run();\n      expect(result).toBe('finally error');\n    });\n\n    it('should throw from finally overriding catch return', () => {\n      const code = `\n        function test() {\n          try {\n            throw new Error('try error');\n          } catch (e) {\n            return 'caught';\n          } finally {\n            throw new Error('finally error');\n          }\n        }\n        try {\n          return test();\n        } catch (e) {\n          return e.message;\n        }\n      `;\n      const result = sandbox.compile(code)().run();\n      expect(result).toBe('finally error');\n    });\n  });\n\n  describe('Nested try/finally', () => {\n    it('should handle nested finally with break', () => {\n      const code = `\n        let result = '';\n        for (let i = 0; i < 3; i++) {\n          try {\n            try {\n              result += i;\n            } finally {\n              result += 'A';\n              if (i === 1) break;\n            }\n          } finally {\n            result += 'B';\n          }\n        }\n        return result;\n      `;\n      const result = sandbox.compile(code)().run();\n      expect(result).toBe('0AB1AB');\n    });\n  });\n});\n"
  },
  {
    "path": "test/tsconfig.json",
    "content": "{\n  \"extends\": \"../tsconfig.json\",\n  \"compilerOptions\": {\n    \"types\": [\"jest\", \"node\"],\n    \"resolveJsonModule\": true,\n    \"rootDir\": \"..\",\n    \"allowSyntheticDefaultImports\": true\n  },\n  \"files\": [],\n  \"include\": [\"**/*.ts\", \"../scripts/export-tests.ts\"]\n}\n"
  },
  {
    "path": "test/unraw.spec.ts",
    "content": "import { unraw } from '../src/utils/unraw.js';\n\ndescribe('unraw Tests', () => {\n  describe('Basic escape sequences', () => {\n    it('should handle single character escapes', () => {\n      expect(unraw('\\\\n')).toBe('\\n');\n      expect(unraw('\\\\r')).toBe('\\r');\n      expect(unraw('\\\\t')).toBe('\\t');\n      expect(unraw('\\\\b')).toBe('\\b');\n      expect(unraw('\\\\f')).toBe('\\f');\n      expect(unraw('\\\\v')).toBe('\\v');\n    });\n\n    it('should handle backslash escape', () => {\n      expect(unraw('\\\\\\\\')).toBe('\\\\');\n    });\n\n    it('should handle quote escapes', () => {\n      expect(unraw(\"\\\\\\'\")).toBe(\"'\");\n      expect(unraw('\\\\\"')).toBe('\"');\n    });\n  });\n\n  describe('Hexadecimal escape sequences', () => {\n    it('should handle valid hexadecimal escapes', () => {\n      expect(unraw('\\\\x41')).toBe('A');\n      expect(unraw('\\\\x42')).toBe('B');\n      expect(unraw('\\\\x30')).toBe('0');\n    });\n\n    it('should throw error for invalid hexadecimal escapes', () => {\n      expect(() => unraw('\\\\xGG')).toThrow(SyntaxError);\n      expect(() => unraw('\\\\xGG')).toThrow('Malformed Hexadecimal');\n    });\n\n    it('should throw error for wrong length hexadecimal escapes', () => {\n      expect(() => unraw('\\\\x1')).toThrow(SyntaxError);\n      expect(() => unraw('\\\\x1')).toThrow('Malformed Hexadecimal');\n    });\n  });\n\n  describe('Unicode escape sequences', () => {\n    it('should handle valid 4-digit unicode escapes', () => {\n      expect(unraw('\\\\u0041')).toBe('A');\n      expect(unraw('\\\\u4E2D')).toBe('中');\n    });\n\n    it('should throw error for invalid unicode escapes', () => {\n      expect(() => unraw('\\\\uGGGG')).toThrow(SyntaxError);\n      expect(() => unraw('\\\\uGGGG')).toThrow('Malformed Unicode');\n    });\n\n    it('should throw error for wrong length unicode escapes', () => {\n      expect(() => unraw('\\\\u041')).toThrow(SyntaxError);\n      expect(() => unraw('\\\\u041')).toThrow('Malformed Unicode');\n    });\n\n    it('should handle unicode with surrogate pairs', () => {\n      // Test surrogate pair for emoji\n      expect(unraw('\\\\uD83D\\\\uDE00')).toBe('😀');\n      expect(unraw('\\\\uD834\\\\uDD1E')).toBe('𝄞'); // Musical symbol\n    });\n  });\n\n  describe('Unicode code point escape sequences', () => {\n    it('should handle valid unicode code point escapes', () => {\n      expect(unraw('\\\\u{41}')).toBe('A');\n      expect(unraw('\\\\u{1F600}')).toBe('😀');\n      expect(unraw('\\\\u{4E2D}')).toBe('中');\n    });\n\n    it('should throw error for malformed unicode code points (missing braces)', () => {\n      expect(() => unraw('\\\\u{41')).toThrow(SyntaxError);\n      expect(() => unraw('\\\\u{41')).toThrow('Malformed Unicode');\n      expect(() => unraw('\\\\u41}')).toThrow(SyntaxError);\n    });\n\n    it('should throw error for invalid hex in unicode code points', () => {\n      expect(() => unraw('\\\\u{GGGG}')).toThrow(SyntaxError);\n      expect(() => unraw('\\\\u{GGGG}')).toThrow('Malformed Unicode');\n    });\n\n    it('should throw error for code points beyond limit', () => {\n      // Test a code point beyond the valid range (> 0x10FFFF)\n      expect(() => unraw('\\\\u{110000}')).toThrow(SyntaxError);\n      expect(() => unraw('\\\\u{110000}')).toThrow('Code Point Limit');\n    });\n  });\n\n  describe('Octal escape sequences', () => {\n    it('should handle null character \\\\0', () => {\n      expect(unraw('\\\\0')).toBe('\\0');\n    });\n\n    it('should throw error for deprecated octal escapes', () => {\n      expect(() => unraw('\\\\1')).toThrow(SyntaxError);\n      expect(() => unraw('\\\\1')).toThrow('Octal Deprecation');\n      expect(() => unraw('\\\\77')).toThrow(SyntaxError);\n      expect(() => unraw('\\\\77')).toThrow('Octal Deprecation');\n      expect(() => unraw('\\\\123')).toThrow(SyntaxError);\n      expect(() => unraw('\\\\123')).toThrow('Octal Deprecation');\n    });\n  });\n\n  describe('End of string handling', () => {\n    it('should throw error for incomplete escape at end of string', () => {\n      expect(() => unraw('\\\\')).toThrow(SyntaxError);\n      expect(() => unraw('\\\\')).toThrow('End of string');\n    });\n  });\n\n  describe('Complex strings', () => {\n    it('should handle mixed escape sequences', () => {\n      expect(unraw('Hello\\\\nWorld\\\\t!')).toBe('Hello\\nWorld\\t!');\n      expect(unraw('\\\\x41\\\\u0042')).toBe('AB');\n    });\n\n    it('should handle strings with no escapes', () => {\n      expect(unraw('Hello World')).toBe('Hello World');\n    });\n\n    it('should handle empty string', () => {\n      expect(unraw('')).toBe('');\n    });\n  });\n});\n"
  },
  {
    "path": "tsconfig.jest.json",
    "content": "{\n  \"compilerOptions\": {\n    \"module\": \"ES2020\",\n    \"target\": \"ES2020\",\n    \"rootDir\": \"src/\",\n    \"baseUrl\": \".\",\n    \"strict\": true,\n    \"moduleResolution\": \"node\",\n    \"resolveJsonModule\": true,\n    \"esModuleInterop\": true,\n    \"noImplicitAny\": false,\n    \"isolatedModules\": true\n  },\n  \"files\": [\"src/SandboxExec.ts\"],\n  \"include\": [\"src/**/*\"],\n  \"exclude\": []\n}\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"module\": \"ES2020\",\n    \"target\": \"ES2020\",\n    \"moduleResolution\": \"node\",\n    \"rootDir\": \"src/\",\n    \"strict\": true\n  },\n  \"files\": [\"src/SandboxExec.ts\"],\n  \"include\": [\"src/**/*\"],\n  \"exclude\": []\n}\n"
  }
]