diff --git a/eslint.config.js b/eslint.config.js index fcddc32..467953a 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,5 +1,8 @@ import eslint from '@eslint/js'; +import vitest from '@vitest/eslint-plugin'; +import eslintComments from 'eslint-plugin-eslint-comments'; import importPlugin from 'eslint-plugin-import'; +import noBarrelFiles from 'eslint-plugin-no-barrel-files'; import unusedImports from 'eslint-plugin-unused-imports'; import tseslint from 'typescript-eslint'; @@ -11,6 +14,8 @@ export default tseslint.config( plugins: { 'unused-imports': unusedImports, import: importPlugin, + 'no-barrel-files': noBarrelFiles, + 'eslint-comments': eslintComments, }, languageOptions: { parserOptions: { @@ -36,7 +41,43 @@ export default tseslint.config( ], '@typescript-eslint/no-floating-promises': 'error', '@typescript-eslint/explicit-function-return-type': 'off', - '@typescript-eslint/no-empty-object-type': 'off', + '@typescript-eslint/no-empty-object-type': 'error', + '@typescript-eslint/no-require-imports': 'error', + 'no-barrel-files/no-barrel-files': 'error', + 'import/no-default-export': 'error', + 'import/named': 'error', + 'import/no-extraneous-dependencies': ['error', { devDependencies: true }], + 'eslint-comments/no-unused-disable': 'error', + 'no-void': ['error', { allowAsStatement: true }], + 'default-case': 'error', + 'no-constant-condition': ['error', { checkLoops: false }], + 'no-param-reassign': ['error', { props: false }], + 'no-promise-executor-return': 'error', + 'prefer-promise-reject-errors': 'error', + 'prefer-arrow-callback': ['error', { allowNamedFunctions: true }], + 'no-restricted-syntax': [ + 'error', + { + selector: 'ForInStatement', + message: + 'for..in loops iterate over the entire prototype chain, which is virtually never what you want. Use Object.{keys,values,entries}, and iterate over the resulting array.', + }, + { + selector: 'LabeledStatement', + message: + 'Labels are a form of GOTO; using them makes code confusing and hard to maintain and understand.', + }, + { + selector: 'WithStatement', + message: + '`with` is disallowed in strict mode because it makes code impossible to predict and optimize.', + }, + { + selector: 'ExportAllDeclaration', + message: + "Export all doesn't work well if imported in ESM due to how they are transpiled, and they can also lead to unexpected exposure of internal methods.", + }, + ], 'unused-imports/no-unused-imports': 'error', 'import/order': [ 'error', @@ -61,15 +102,24 @@ export default tseslint.config( }, ], 'import/no-cycle': 'error', + 'import/extensions': ['error', 'ignorePackages', { js: 'always', ts: 'never' }], 'no-console': 'error', }, }, { files: ['tests/**/*.ts'], + plugins: { + vitest, + }, rules: { '@typescript-eslint/no-explicit-any': 'off', '@typescript-eslint/consistent-type-assertions': 'off', 'no-console': 'off', + 'vitest/expect-expect': [ + 'error', + { assertFunctionNames: ['expect', 'expect*', 'assert*'] }, + ], + 'vitest/valid-expect': ['error', { maxArgs: 2 }], }, }, { @@ -79,6 +129,18 @@ export default tseslint.config( 'no-console': 'off', }, }, + { + // Dedicated public-API barrels: their sole purpose is re-exporting the + // package surface, so the no-barrel-files rule does not apply. + files: [ + 'src/index.ts', + 'src/schemas/index.ts', + 'src/daemon/index.ts', + ], + rules: { + 'no-barrel-files/no-barrel-files': 'off', + }, + }, { ignores: ['dist/', 'node_modules/', '*.config.*'], } diff --git a/examples/hook-execution.ts b/examples/hook-execution.ts index 8de8a01..de54a7f 100644 --- a/examples/hook-execution.ts +++ b/examples/hook-execution.ts @@ -55,6 +55,9 @@ async function main(): Promise { case DroidMessageType.Result: console.log('\n\n--- Turn complete ---'); break; + + default: + break; } } } finally { diff --git a/examples/session-stream.ts b/examples/session-stream.ts index b476693..55836e2 100644 --- a/examples/session-stream.ts +++ b/examples/session-stream.ts @@ -44,6 +44,9 @@ async function main(): Promise { ); } break; + + default: + break; } } } finally { diff --git a/package-lock.json b/package-lock.json index c5ac216..935d4e1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,9 +20,12 @@ "@types/uuid": "^10.0.0", "@types/ws": "^8.18.1", "@vitest/coverage-v8": "^3.1.0", + "@vitest/eslint-plugin": "^1.6.18", "eslint": "^9.24.0", "eslint-import-resolver-typescript": "^4.4.4", + "eslint-plugin-eslint-comments": "^3.2.0", "eslint-plugin-import": "^2.32.0", + "eslint-plugin-no-barrel-files": "^1.3.1", "eslint-plugin-unused-imports": "^4.4.1", "prettier": "^3.8.1", "tsup": "^8.4.0", @@ -1964,6 +1967,230 @@ } } }, + "node_modules/@vitest/eslint-plugin": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/@vitest/eslint-plugin/-/eslint-plugin-1.6.18.tgz", + "integrity": "sha512-J6U4X0jH3NwTuYouvrJn6I8ypTOU+GhKEjyVwpoPnDuc23usa/xi/R0caWLBbNp3xLy3/rL1YkuJuneTMVV4Mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "^8.58.0", + "@typescript-eslint/utils": "^8.58.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "*", + "eslint": ">=8.57.0", + "typescript": ">=5.0.0", + "vitest": "*" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + }, + "typescript": { + "optional": true + }, + "vitest": { + "optional": true + } + } + }, + "node_modules/@vitest/eslint-plugin/node_modules/@typescript-eslint/project-service": { + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.60.0.tgz", + "integrity": "sha512-aZu74NNKJeUWqCjDddzdiKaS82dgYgV/vmf+Ui3ZdZejmgfXR/q+pRumgobnQ2cCJTgGTWp4ypiwsuofFubavg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.60.0", + "@typescript-eslint/types": "^8.60.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@vitest/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.60.0.tgz", + "integrity": "sha512-pFzqhllJMs+jghLQWzV00ds39xLzuyqPSev5pd8f4Ir0rtKR3ZLUB4/4dhjOFighWb9larvtfJvqL+4yKDI3Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.60.0", + "@typescript-eslint/visitor-keys": "8.60.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vitest/eslint-plugin/node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.60.0.tgz", + "integrity": "sha512-BZPR3RGYlAXnly6ymAxfkVn5rCbZzQNou0rxv3GfWZ8cTQp+hhVd73khbGLAd8k1TlAPLISH337M+tAgAnaJDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@vitest/eslint-plugin/node_modules/@typescript-eslint/types": { + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.60.0.tgz", + "integrity": "sha512-AsE7x2XaAK+CVbeih0Fvbn+r1qHxtpLDJ3XUuFcIinT318T90yHMJC+Zgv+jUuDjQQd06HKwxnDu6sz1IcTilA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vitest/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.60.0.tgz", + "integrity": "sha512-3AcZNBGMClm6CXDyo8kYvVGT/sx29sS0oBsIb9oZI2gunA4Vm2M3YHzRLPvsUBBsl+yB5FPtltq7gGH0iTlp9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.60.0", + "@typescript-eslint/tsconfig-utils": "8.60.0", + "@typescript-eslint/types": "8.60.0", + "@typescript-eslint/visitor-keys": "8.60.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@vitest/eslint-plugin/node_modules/@typescript-eslint/utils": { + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.60.0.tgz", + "integrity": "sha512-HtXuPfrHTyBDkameWpl+vJb1Uevu2tznAyahM1Oc4AENidCLTPiZDWIo4GfcxNdC/RcfGcadzzkqbRG87dUrQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.60.0", + "@typescript-eslint/types": "8.60.0", + "@typescript-eslint/typescript-estree": "8.60.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@vitest/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.60.0.tgz", + "integrity": "sha512-9WI52t8ZGLVGrPMBet25yAftqY/n95+zmoUUtJBBQTKDSKUu7OsPTroT2op7U9JatkoRccL0YkWDNMFfC4Sjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.60.0", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vitest/eslint-plugin/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@vitest/eslint-plugin/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@vitest/eslint-plugin/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@vitest/eslint-plugin/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@vitest/expect": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", @@ -3277,6 +3504,36 @@ "ms": "^2.1.1" } }, + "node_modules/eslint-plugin-eslint-comments": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-3.2.0.tgz", + "integrity": "sha512-0jkOl0hfojIHHmEHgmNdqv4fmh7300NdpA9FFpF7zaoLvB/QeXOGNLIo86oAveJFrfB1p05kC8hpEMHM8DwWVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.5", + "ignore": "^5.0.5" + }, + "engines": { + "node": ">=6.5.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=4.19.1" + } + }, + "node_modules/eslint-plugin-eslint-comments/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/eslint-plugin-import": { "version": "2.32.0", "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", @@ -3331,6 +3588,212 @@ "semver": "bin/semver.js" } }, + "node_modules/eslint-plugin-no-barrel-files": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-no-barrel-files/-/eslint-plugin-no-barrel-files-1.3.1.tgz", + "integrity": "sha512-y7OX5kyH7PMNRFhLF6SmM4JapxvaxExrgWPndPNTzilpO5uBqybuN480g3E8TTxT3OLOOhQDynmcJ0dnipIyNA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@typescript-eslint/utils": "^8.58.1" + }, + "peerDependencies": { + "eslint": "^8.0.0 || ^9.0.0 || ^10.0.0" + } + }, + "node_modules/eslint-plugin-no-barrel-files/node_modules/@typescript-eslint/project-service": { + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.60.0.tgz", + "integrity": "sha512-aZu74NNKJeUWqCjDddzdiKaS82dgYgV/vmf+Ui3ZdZejmgfXR/q+pRumgobnQ2cCJTgGTWp4ypiwsuofFubavg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.60.0", + "@typescript-eslint/types": "^8.60.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/eslint-plugin-no-barrel-files/node_modules/@typescript-eslint/scope-manager": { + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.60.0.tgz", + "integrity": "sha512-pFzqhllJMs+jghLQWzV00ds39xLzuyqPSev5pd8f4Ir0rtKR3ZLUB4/4dhjOFighWb9larvtfJvqL+4yKDI3Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.60.0", + "@typescript-eslint/visitor-keys": "8.60.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-plugin-no-barrel-files/node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.60.0.tgz", + "integrity": "sha512-BZPR3RGYlAXnly6ymAxfkVn5rCbZzQNou0rxv3GfWZ8cTQp+hhVd73khbGLAd8k1TlAPLISH337M+tAgAnaJDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/eslint-plugin-no-barrel-files/node_modules/@typescript-eslint/types": { + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.60.0.tgz", + "integrity": "sha512-AsE7x2XaAK+CVbeih0Fvbn+r1qHxtpLDJ3XUuFcIinT318T90yHMJC+Zgv+jUuDjQQd06HKwxnDu6sz1IcTilA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-plugin-no-barrel-files/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.60.0.tgz", + "integrity": "sha512-3AcZNBGMClm6CXDyo8kYvVGT/sx29sS0oBsIb9oZI2gunA4Vm2M3YHzRLPvsUBBsl+yB5FPtltq7gGH0iTlp9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.60.0", + "@typescript-eslint/tsconfig-utils": "8.60.0", + "@typescript-eslint/types": "8.60.0", + "@typescript-eslint/visitor-keys": "8.60.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/eslint-plugin-no-barrel-files/node_modules/@typescript-eslint/utils": { + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.60.0.tgz", + "integrity": "sha512-HtXuPfrHTyBDkameWpl+vJb1Uevu2tznAyahM1Oc4AENidCLTPiZDWIo4GfcxNdC/RcfGcadzzkqbRG87dUrQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.60.0", + "@typescript-eslint/types": "8.60.0", + "@typescript-eslint/typescript-estree": "8.60.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/eslint-plugin-no-barrel-files/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.60.0.tgz", + "integrity": "sha512-9WI52t8ZGLVGrPMBet25yAftqY/n95+zmoUUtJBBQTKDSKUu7OsPTroT2op7U9JatkoRccL0YkWDNMFfC4Sjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.60.0", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-plugin-no-barrel-files/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/eslint-plugin-no-barrel-files/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/eslint-plugin-no-barrel-files/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-no-barrel-files/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/eslint-plugin-unused-imports": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-4.4.1.tgz", diff --git a/package.json b/package.json index 666b494..74e67a2 100644 --- a/package.json +++ b/package.json @@ -66,9 +66,12 @@ "@types/uuid": "^10.0.0", "@types/ws": "^8.18.1", "@vitest/coverage-v8": "^3.1.0", + "@vitest/eslint-plugin": "^1.6.18", "eslint": "^9.24.0", "eslint-import-resolver-typescript": "^4.4.4", + "eslint-plugin-eslint-comments": "^3.2.0", "eslint-plugin-import": "^2.32.0", + "eslint-plugin-no-barrel-files": "^1.3.1", "eslint-plugin-unused-imports": "^4.4.1", "prettier": "^3.8.1", "tsup": "^8.4.0", diff --git a/src/api-types.ts b/src/api-types.ts index 246c2f1..ec4e6c6 100644 --- a/src/api-types.ts +++ b/src/api-types.ts @@ -84,9 +84,9 @@ export const ComputerListResponseSchema = z.object({ export type ComputerListResponse = z.infer; -export interface ListComputersOptions extends FactoryApiOptions {} +export type ListComputersOptions = FactoryApiOptions; -export interface GetComputerOptions extends ComputerApiOptions {} +export type GetComputerOptions = ComputerApiOptions; export interface CreateComputerOptions extends FactoryApiOptions { name: string; @@ -108,11 +108,11 @@ export interface UpdateComputerOptions extends ComputerApiOptions { hostId?: string; } -export interface DeleteComputerOptions extends ComputerApiOptions {} +export type DeleteComputerOptions = ComputerApiOptions; -export interface RestartComputerOptions extends ComputerApiOptions {} +export type RestartComputerOptions = ComputerApiOptions; -export interface RefreshComputerOptions extends ComputerApiOptions {} +export type RefreshComputerOptions = ComputerApiOptions; export const RefreshComputerResponseSchema = z.object({ configured: z.number().int(), @@ -144,7 +144,7 @@ export interface GetComputerMetricsOptions extends ComputerApiOptions { start?: string; } -export interface RetryInstallDepsOptions extends ComputerApiOptions {} +export type RetryInstallDepsOptions = ComputerApiOptions; export const RemoteSessionSchema = z.object({ sessionId: z.string(), diff --git a/src/constants.ts b/src/constants.ts index 4e529a0..03a06c5 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,4 +1,5 @@ /** Re-exported for convenience. */ +// eslint-disable-next-line no-barrel-files/no-barrel-files export { DEFAULT_REQUEST_TIMEOUT, SESSION_INIT_TIMEOUT, diff --git a/src/daemon/connection.ts b/src/daemon/connection.ts index c11fb74..db41ab1 100644 --- a/src/daemon/connection.ts +++ b/src/daemon/connection.ts @@ -495,7 +495,9 @@ export async function connectDaemon( // Best-effort cleanup between retries } if (attempt < maxRetries) { - await new Promise((resolve) => setTimeout(resolve, 2_000)); + await new Promise((resolve) => { + setTimeout(resolve, 2_000); + }); } } } diff --git a/src/daemon/local.ts b/src/daemon/local.ts index 7da05bd..2b87b2b 100644 --- a/src/daemon/local.ts +++ b/src/daemon/local.ts @@ -138,7 +138,9 @@ async function waitForDaemonReady( settle('timeout'); return; } - await new Promise((r) => setTimeout(r, STARTUP_POLL_INTERVAL_MS)); + await new Promise((r) => { + setTimeout(r, STARTUP_POLL_INTERVAL_MS); + }); } }; diff --git a/src/daemon/transport.ts b/src/daemon/transport.ts index 10bbb58..f529bf3 100644 --- a/src/daemon/transport.ts +++ b/src/daemon/transport.ts @@ -60,7 +60,9 @@ export class WebSocketTransport implements DroidClientTransport { this.initialRetryDelayMs * 2 ** attempt, this.maxRetryDelayMs ); - await new Promise((resolve) => setTimeout(resolve, delay)); + await new Promise((resolve) => { + setTimeout(resolve, delay); + }); } } } diff --git a/src/hooks.ts b/src/hooks.ts index 7ff76c0..4f92b76 100644 --- a/src/hooks.ts +++ b/src/hooks.ts @@ -1,5 +1,6 @@ import type { DroidHookEvent } from './schemas/hooks.js'; +// eslint-disable-next-line no-barrel-files/no-barrel-files export type { DroidHookEvent, DroidHookOutput } from './schemas/hooks.js'; export type DroidPermissionMode = diff --git a/src/index.ts b/src/index.ts index 3b9fc54..f34e8fe 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,10 +1,20 @@ -export * from './schemas/index.js'; - export { SDK_TAG, SDK_VERSION } from './constants.js'; -export * from './errors.js'; +export { + ConnectionError, + ProtocolError, + SessionError, + SessionNotFoundError, + TimeoutError, + ProcessExitError, +} from './errors.js'; -export * from './types.js'; +export type { + MessageCallback, + ErrorCallback, + DroidClientTransport, + ProcessTransportOptions, +} from './types.js'; export { ProcessTransport } from './transport.js'; export { dispatchNotification, ProtocolEngine } from './protocol.js'; @@ -180,3 +190,573 @@ export type { SendOptions, WebSocketTransportOptions, } from './daemon/index.js'; + +// Protocol schemas, types, enums, and constants. +export { + AddMcpServerRequestParamsSchema, + AddMcpServerRequestSchema, + AddMcpServerResponseSchema, + AddMcpServerResultSchema, + AddUserMessageRequestParamsSchema, + AddUserMessageRequestSchema, + AddUserMessageResponseSchema, + AddUserMessageResultSchema, + ApplyPatchToolConfirmationDetailsSchema, + AskUserCollectedAnswerSchema, + AskUserConfirmationDetailsSchema, + AskUserQuestionSchema, + AskUserRequestParamsSchema, + AskUserRequestSchema, + AskUserResponseSchema, + AskUserResultSchema, + AssistantTextCompleteNotificationSchema, + AssistantTextDeltaNotificationSchema, + AuthenticateMcpServerRequestParamsSchema, + AuthenticateMcpServerRequestSchema, + AuthenticateMcpServerResponseSchema, + AuthenticateMcpServerResultSchema, + AutonomyLevel, + AutonomyMode, + AvailableModelConfigSchema, + Base64ImageSourceSchema, + BaseNotificationSchema, + BaseRequestSchema, + BaseResponseFailureSchema, + BaseResponseSuccessSchema, + COMPACTION_TIMEOUT, + CancelMcpAuthRequestParamsSchema, + CancelMcpAuthRequestSchema, + CancelMcpAuthResponseSchema, + CancelMcpAuthResultSchema, + ClearMcpAuthRequestParamsSchema, + ClearMcpAuthRequestSchema, + ClearMcpAuthResponseSchema, + ClearMcpAuthResultSchema, + CliRequestOrNotificationSchema, + ClientRequestSchema, + CloseSessionRequestParamsSchema, + CloseSessionRequestSchema, + CloseSessionResponseSchema, + CloseSessionResultSchema, + CompactSessionRequestParamsSchema, + CompactSessionRequestSchema, + CompactSessionResponseSchema, + CompactSessionResultSchema, + ContentBlockSchema, + ContextBreakdownCategorySchema, + ContextBreakdownResultSchema, + ContextStatsAccuracy, + ContextStatsSchema, + CreateMessageNotificationSchema, + CreateToolConfirmationDetailsSchema, + DEFAULT_REQUEST_TIMEOUT, + DecompSessionType, + DiscoveredIssueSchema, + DismissalRecordSchema, + DismissalType, + DocumentBlockSchema, + DocumentSourceSchema, + DroidClientMethod, + DroidErrorType, + DroidHookEventSchema, + DroidHookExecutionResultSchema, + DroidHookOutputSchema, + DroidHookSpecificOutputSchema, + DroidInteractionMode, + DroidServerMethod, + DroidWorkingState, + DroidWorkingStateChangedNotificationSchema, + EditToolConfirmationDetailsSchema, + EmptyResultSchema, + ErrorDetailSchema, + ErrorNotificationSchema, + ExecToolInfoSchema, + ExecuteRewindRequestParamsSchema, + ExecuteRewindRequestSchema, + ExecuteRewindResponseSchema, + ExecuteRewindResultSchema, + ExecuteToolConfirmationDetailsSchema, + ExitSpecModeConfirmationDetailsSchema, + FACTORY_CLIENT_HEADER, + FACTORY_CLIENT_VERSION, + FACTORY_PROTOCOL_VERSION, + FactoryDroidMessageRole, + FactoryDroidMessageSchema, + FeatureStatus, + FeatureSuccessState, + FeatureSuccessStateSchema, + ForkSessionRequestParamsSchema, + ForkSessionRequestSchema, + ForkSessionResponseSchema, + ForkSessionResultSchema, + GetContextStatsRequestParamsSchema, + GetContextStatsRequestSchema, + GetContextStatsResponseSchema, + GetContextStatsResultSchema, + GetRewindInfoRequestParamsSchema, + GetRewindInfoRequestSchema, + GetRewindInfoResponseSchema, + GetRewindInfoResultSchema, + GitRepoInfoSchema, + HandoffItemsDismissedEntrySchema, + HandoffSchema, + HookCommandSchema, + HookExecutionCompletedNotificationSchema, + HookExecutionStartedNotificationSchema, + HookResultSchema, + HttpHeaderSchema, + HttpMcpConfigSchema, + ImageBlockSchema, + InitializeSessionRequestParamsSchema, + InitializeSessionRequestSchema, + InitializeSessionResponseSchema, + InitializeSessionResultSchema, + InteractiveCheckSchema, + InterruptSessionRequestParamsSchema, + InterruptSessionRequestSchema, + InterruptSessionResponseSchema, + InterruptSessionResultSchema, + IssueSeverity, + JSONRPC_VERSION, + JsonArraySchema, + JsonObjectSchema, + JsonRpcEnvelopeSchema, + JsonRpcErrorCode, + JsonRpcErrorSchema, + JsonRpcMessageSchema, + JsonRpcMessageType, + JsonRpcNotificationSchema, + JsonRpcRequestSchema, + JsonRpcResponseFailureSchema, + JsonRpcResponseSchema, + JsonRpcResponseSuccessSchema, + JsonValueSchema, + KillWorkerSessionRequestParamsSchema, + KillWorkerSessionRequestSchema, + KillWorkerSessionResponseSchema, + KillWorkerSessionResultSchema, + LEGACY_FACTORY_API_VERSION, + ListMcpRegistryRequestParamsSchema, + ListMcpRegistryRequestSchema, + ListMcpRegistryResponseSchema, + ListMcpRegistryResultSchema, + ListMcpServersRequestParamsSchema, + ListMcpServersRequestSchema, + ListMcpServersResponseSchema, + ListMcpServersResultSchema, + ListMcpToolsRequestParamsSchema, + ListMcpToolsRequestSchema, + ListMcpToolsResponseSchema, + ListMcpToolsResultSchema, + ListSkillsRequestParamsSchema, + ListSkillsRequestSchema, + ListSkillsResponseSchema, + ListSkillsResultSchema, + ListToolsRequestParamsSchema, + ListToolsRequestSchema, + ListToolsResponseSchema, + ListToolsResultSchema, + LoadSessionRequestParamsSchema, + LoadSessionRequestSchema, + LoadSessionResponseSchema, + LoadSessionResultSchema, + MCP_AUTH_TIMEOUT, + McpAuthCompletedNotificationSchema, + McpAuthOutcome, + McpAuthRequiredNotificationSchema, + McpHttpServerConfigFieldsSchema, + McpRegistryServerSchema, + McpServerConfigSchema, + McpServerStatus, + McpServerStatusInfoSchema, + McpServerType, + McpSseServerConfigFieldsSchema, + McpStatus, + McpStatusChangedNotificationSchema, + McpStatusSummarySchema, + McpStdioServerConfigFieldsSchema, + McpToolConfirmationDetailsSchema, + McpToolInfoSchema, + McpToolInputSchemaSchema, + MilestoneValidationTriggeredEntrySchema, + MissionAcceptedEntrySchema, + MissionFeatureSchema, + MissionFeaturesChangedNotificationSchema, + MissionHeartbeatNotificationSchema, + MissionPausedEntrySchema, + MissionProgressEntryNotificationSchema, + MissionResumedEntrySchema, + MissionRunStartedEntrySchema, + MissionSnapshotSchema, + MissionState, + MissionStateChangedNotificationSchema, + MissionWorkerCompletedNotificationSchema, + MissionWorkerStartedNotificationSchema, + ModelProvider, + OutputFormatSchema, + OutputFormatType, + PermissionResolvedNotificationSchema, + ProgressLogEntrySchema, + ProgressLogEntryType, + ProposeMissionConfirmationDetailsSchema, + REWIND_TIMEOUT, + ReasoningEffort, + RedactedThinkingBlockSchema, + RemoveMcpServerRequestParamsSchema, + RemoveMcpServerRequestSchema, + RemoveMcpServerResponseSchema, + RemoveMcpServerResultSchema, + RenameSessionRequestParamsSchema, + RenameSessionRequestSchema, + RenameSessionResponseSchema, + RenameSessionResultSchema, + RequestPermissionRequestParamsSchema, + RequestPermissionRequestSchema, + RequestPermissionResponseSchema, + RequestPermissionResultSchema, + RewindEvictedFileSchema, + RewindFileCreationSchema, + RewindFileSnapshotSchema, + SESSION_INIT_TIMEOUT, + ServerRequestHandlerType, + SessionMetadataSchema, + SessionNotificationParamsSchema, + SessionNotificationPayloadSchema, + SessionNotificationSchema, + SessionNotificationSchemaList, + SessionNotificationType, + SessionSettingsFileSchema, + SessionSettingsSchema, + SessionSourceSchema, + SessionStartEventSchema, + SessionTagSchema, + SessionTitleUpdatedNotificationSchema, + SessionTokenUsageChangedNotificationSchema, + SettingsLevel, + SettingsUpdatedNotificationSchema, + SettingsUpdatedPayloadSchema, + SkillDeviationSchema, + SkillFeedbackSchema, + SkillInfoSchema, + SkillLocation, + SkillResourceSchema, + SseMcpConfigSchema, + StartMissionRunConfirmationDetailsSchema, + StdioMcpConfigSchema, + StructuredOutputErrorSchema, + StructuredOutputNotificationSchema, + SubmitBugReportRequestParamsSchema, + SubmitBugReportRequestSchema, + SubmitBugReportResponseSchema, + SubmitBugReportResultSchema, + SubmitMcpAuthCodeRequestParamsSchema, + SubmitMcpAuthCodeRequestSchema, + SubmitMcpAuthCodeResponseSchema, + SubmitMcpAuthCodeResultSchema, + SuccessResultSchema, + TestCaseSchema, + TestFileSchema, + TestsSchema, + TextBlockSchema, + ThinkingBlockSchema, + ThinkingTextCompleteNotificationSchema, + ThinkingTextDeltaNotificationSchema, + ToggleMcpServerRequestParamsSchema, + ToggleMcpServerRequestSchema, + ToggleMcpServerResponseSchema, + ToggleMcpServerResultSchema, + ToggleMcpToolRequestParamsSchema, + ToggleMcpToolRequestSchema, + ToggleMcpToolResponseSchema, + ToggleMcpToolResultSchema, + TokenUsageSchema, + ToolCallNotificationSchema, + ToolConfirmationDetailsSchema, + ToolConfirmationInfoSchema, + ToolConfirmationListItemSchema, + ToolConfirmationOutcome, + ToolConfirmationType, + ToolProgressUpdateNotificationSchema, + ToolProgressUpdateSchema, + ToolResultBlockSchema, + ToolResultNotificationSchema, + ToolSelectionOverridesSchema, + ToolUseBlockSchema, + ToolUseSchema, + TraceContextMetaSchema, + UpdateSessionSettingsRequestParamsSchema, + UpdateSessionSettingsRequestSchema, + UpdateSessionSettingsResponseSchema, + UpdateSessionSettingsResultSchema, + VerificationCommandSchema, + VerificationSchema, + WorkerCompletedEntrySchema, + WorkerFailedEntrySchema, + WorkerPausedEntrySchema, + WorkerSelectedFeatureEntrySchema, + WorkerStartedEntrySchema, + WorkerStateInfoSchema, + createResponseSchema, +} from './schemas/index.js'; + +export type { + AddMcpServerRequest, + AddMcpServerRequestParams, + AddMcpServerResponse, + AddMcpServerResult, + AddUserMessageRequest, + AddUserMessageRequestParams, + AddUserMessageResponse, + AddUserMessageResult, + ApplyPatchToolConfirmationDetails, + AskUserCollectedAnswer, + AskUserConfirmationDetails, + AskUserQuestion, + AskUserRequest, + AskUserRequestParams, + AskUserResponse, + AskUserResult, + AssistantTextCompleteNotification, + AssistantTextDeltaNotification, + AuthenticateMcpServerRequest, + AuthenticateMcpServerRequestParams, + AuthenticateMcpServerResponse, + AuthenticateMcpServerResult, + AvailableModelConfig, + Base64ImageSource, + BaseNotification, + BaseRequest, + BaseResponseFailure, + BaseResponseSuccess, + CancelMcpAuthRequest, + CancelMcpAuthRequestParams, + CancelMcpAuthResponse, + CancelMcpAuthResult, + ClearMcpAuthRequest, + ClearMcpAuthRequestParams, + ClearMcpAuthResponse, + ClearMcpAuthResult, + CliRequestOrNotification, + ClientRequest, + CloseSessionRequest, + CloseSessionRequestParams, + CloseSessionResponse, + CloseSessionResult, + CompactSessionRequest, + CompactSessionRequestParams, + CompactSessionResponse, + CompactSessionResult, + ContentBlock, + ContextBreakdownResult, + ContextStats, + CreateMessageNotification, + CreateToolConfirmationDetails, + DiscoveredIssue, + DismissalRecord, + DocumentBlock, + DocumentSource, + DroidHookExecutionResult, + DroidWorkingStateChangedNotification, + EditToolConfirmationDetails, + EmptyResult, + ErrorDetail, + ErrorNotification, + ExecToolInfo, + ExecuteRewindRequest, + ExecuteRewindRequestParams, + ExecuteRewindResponse, + ExecuteRewindResult, + ExecuteToolConfirmationDetails, + ExitSpecModeConfirmationDetails, + FactoryDroidMessage, + ForkSessionRequest, + ForkSessionRequestParams, + ForkSessionResponse, + ForkSessionResult, + GetContextStatsRequest, + GetContextStatsRequestParams, + GetContextStatsResponse, + GetContextStatsResult, + GetRewindInfoRequest, + GetRewindInfoRequestParams, + GetRewindInfoResponse, + GetRewindInfoResult, + GitRepoInfo, + Handoff, + HandoffItemsDismissedEntry, + HookCommand, + HookExecutionCompletedNotification, + HookExecutionStartedNotification, + HookResult, + HttpHeader, + HttpMcpConfig, + ImageBlock, + InitializeSessionRequest, + InitializeSessionRequestParams, + InitializeSessionResponse, + InitializeSessionResult, + InteractiveCheck, + InterruptSessionRequest, + InterruptSessionRequestParams, + InterruptSessionResponse, + InterruptSessionResult, + JsonArray, + JsonObject, + JsonPrimitive, + JsonRpcEnvelope, + JsonRpcError, + JsonRpcMessage, + JsonRpcNotification, + JsonRpcRequest, + JsonRpcResponse, + JsonRpcResponseFailure, + JsonRpcResponseSuccess, + JsonValue, + KillWorkerSessionRequest, + KillWorkerSessionRequestParams, + KillWorkerSessionResponse, + KillWorkerSessionResult, + ListMcpRegistryRequest, + ListMcpRegistryRequestParams, + ListMcpRegistryResponse, + ListMcpRegistryResult, + ListMcpServersRequest, + ListMcpServersRequestParams, + ListMcpServersResponse, + ListMcpServersResult, + ListMcpToolsRequest, + ListMcpToolsRequestParams, + ListMcpToolsResponse, + ListMcpToolsResult, + ListSessionsOptions, + ListSkillsRequest, + ListSkillsRequestParams, + ListSkillsResponse, + ListSkillsResult, + ListToolsRequest, + ListToolsRequestParams, + ListToolsResponse, + ListToolsResult, + LoadSessionRequest, + LoadSessionRequestParams, + LoadSessionResponse, + LoadSessionResult, + McpAuthCompletedNotification, + McpAuthRequiredNotification, + McpHttpServerConfigFields, + McpRegistryServer, + McpServerConfig, + McpServerStatusInfo, + McpSseServerConfigFields, + McpStatusChangedNotification, + McpStatusSummary, + McpStdioServerConfigFields, + McpToolConfirmationDetails, + McpToolInfo, + McpToolInputSchema, + MilestoneValidationTriggeredEntry, + MissionAcceptedEntry, + MissionFeature, + MissionFeaturesChangedNotification, + MissionHeartbeatNotification, + MissionPausedEntry, + MissionProgressEntryNotification, + MissionResumedEntry, + MissionRunStartedEntry, + MissionSnapshot, + MissionStateChangedNotification, + MissionWorkerCompletedNotification, + MissionWorkerStartedNotification, + OutputFormat, + PermissionResolvedNotification, + ProgressLogEntry, + ProposeMissionConfirmationDetails, + RedactedThinkingBlock, + RemoveMcpServerRequest, + RemoveMcpServerRequestParams, + RemoveMcpServerResponse, + RemoveMcpServerResult, + RenameSessionRequest, + RenameSessionRequestParams, + RenameSessionResponse, + RenameSessionResult, + RequestPermissionHandlerResult, + RequestPermissionRequest, + RequestPermissionRequestParams, + RequestPermissionResponse, + RequestPermissionResult, + RequestPermissionSelection, + RewindEvictedFile, + RewindFileCreation, + RewindFileSnapshot, + SessionMetadata, + SessionNotification, + SessionNotificationParams, + SessionNotificationPayload, + SessionSettings, + SessionSettingsFile, + SessionSource, + SessionStartEvent, + SessionTag, + SessionTitleUpdatedNotification, + SessionTokenUsageChangedNotification, + SettingsUpdatedNotification, + SettingsUpdatedPayload, + SkillDeviation, + SkillFeedback, + SkillInfo, + SkillResource, + SseMcpConfig, + StartMissionRunConfirmationDetails, + StdioMcpConfig, + StructuredOutputError, + StructuredOutputNotification, + SubmitBugReportRequest, + SubmitBugReportRequestParams, + SubmitBugReportResponse, + SubmitBugReportResult, + SubmitMcpAuthCodeRequest, + SubmitMcpAuthCodeRequestParams, + SubmitMcpAuthCodeResponse, + SubmitMcpAuthCodeResult, + SuccessResult, + TestCase, + TestFile, + Tests, + TextBlock, + ThinkingBlock, + ThinkingTextCompleteNotification, + ThinkingTextDeltaNotification, + ToggleMcpServerRequest, + ToggleMcpServerRequestParams, + ToggleMcpServerResponse, + ToggleMcpServerResult, + ToggleMcpToolRequest, + ToggleMcpToolRequestParams, + ToggleMcpToolResponse, + ToggleMcpToolResult, + TokenUsage, + ToolCallNotification, + ToolConfirmationDetails, + ToolConfirmationInfo, + ToolConfirmationListItem, + ToolProgressUpdate, + ToolProgressUpdateNotification, + ToolResultBlock, + ToolResultNotification, + ToolSelectionOverrides, + ToolUseBlock, + TraceContextMeta, + UpdateSessionSettingsRequest, + UpdateSessionSettingsRequestParams, + UpdateSessionSettingsResponse, + UpdateSessionSettingsResult, + Verification, + VerificationCommand, + WorkerCompletedEntry, + WorkerFailedEntry, + WorkerPausedEntry, + WorkerSelectedFeatureEntry, + WorkerStartedEntry, + WorkerStateInfo, +} from './schemas/index.js'; diff --git a/src/protocol.ts b/src/protocol.ts index f5e43fb..3ba17c5 100644 --- a/src/protocol.ts +++ b/src/protocol.ts @@ -288,6 +288,8 @@ export class ProtocolEngine { case JsonRpcMessageType.Request: void this._handleServerRequest(msg.method, msg.id, msg.params); break; + default: + break; } } diff --git a/src/protocol/index.ts b/src/protocol/index.ts deleted file mode 100644 index e8ff37d..0000000 --- a/src/protocol/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -// Barrel for the new protocol source-of-truth modules. -// These files mirror the private factory-mono-alpha protocol exactly and are -// intentionally self-contained (zod-only). NOT yet wired into the public -// package API (src/index.ts) — this is the first migration step. - -export * from './constants.js'; -export * from './enums.js'; -export * from './json-rpc.js'; -export * from './host.js'; -export * from './session.js'; -export * from './messages.js'; -export * from './loop.js'; -export * from './selectable-list-item.js'; diff --git a/src/session.ts b/src/session.ts index 23d7876..f0956e5 100644 --- a/src/session.ts +++ b/src/session.ts @@ -76,6 +76,7 @@ export interface ResumeSessionOptions extends Pick< mcpServers?: DroidMcpServerConfig[]; } +// eslint-disable-next-line no-barrel-files/no-barrel-files export type { MessageOptions } from './helpers.js'; /** Create instances via {@link createSession} or {@link resumeSession}. */ @@ -313,7 +314,9 @@ export async function createSession( cleanupInitAbortSignal(); cleanupInitAbortSignal = () => {}; session.setAbortSignalCleanup( - wireAbortSignal(options.abortSignal, () => void session.close()) + wireAbortSignal(options.abortSignal, () => { + void session.close(); + }) ); return session; @@ -355,7 +358,9 @@ export async function resumeSession( const session = new DroidSession(client, sessionId, loadResult); session.addCleanup(sdkMcpServers.cleanup); session.setAbortSignalCleanup( - wireAbortSignal(options.abortSignal, () => void session.close()) + wireAbortSignal(options.abortSignal, () => { + void session.close(); + }) ); return session; diff --git a/src/stream.ts b/src/stream.ts index 60d31bc..046bcab 100644 --- a/src/stream.ts +++ b/src/stream.ts @@ -632,6 +632,7 @@ export class StreamStateTracker { additional: InternalDroidMessage[]; } { const additional: InternalDroidMessage[] = []; + let enriched: InternalDroidMessage = message; if (message.type === 'tool_use') { this.toolNameMap.set(message.toolUseId, message.toolName); @@ -647,7 +648,7 @@ export class StreamStateTracker { // Enrich tool_result with toolName from prior tool_use if (message.type === DroidMessageType.ToolResult) { - message = { ...message, toolName: this.getToolName(message.toolUseId) }; + enriched = { ...message, toolName: this.getToolName(message.toolUseId) }; } if (message.type === DroidMessageType.AssistantTextDelta) { @@ -687,10 +688,10 @@ export class StreamStateTracker { } if (message.type !== DroidMessageType.WorkingStateChanged) { - this.trackEmittedMessage(message); + this.trackEmittedMessage(enriched); } - return { message, additional }; + return { message: enriched, additional }; } private createResultMessage(): DroidResultMessage { diff --git a/tests/daemon/client.test.ts b/tests/daemon/client.test.ts index 0b083f5..b6876a1 100644 --- a/tests/daemon/client.test.ts +++ b/tests/daemon/client.test.ts @@ -300,7 +300,9 @@ describe('DaemonClient', () => { }, }); - await new Promise((r) => setTimeout(r, 10)); + await new Promise((r) => { + setTimeout(r, 10); + }); expect(notifications.length).toBeGreaterThan(0); }); @@ -321,7 +323,9 @@ describe('DaemonClient', () => { }, }); - await new Promise((r) => setTimeout(r, 10)); + await new Promise((r) => { + setTimeout(r, 10); + }); expect(notifications).toHaveLength(0); }); @@ -346,7 +350,9 @@ describe('DaemonClient', () => { }, }); - await new Promise((r) => setTimeout(r, 10)); + await new Promise((r) => { + setTimeout(r, 10); + }); expect(a.length).toBeGreaterThan(0); expect(b.length).toBeGreaterThan(0); }); @@ -354,7 +360,7 @@ describe('DaemonClient', () => { it('idempotent unsubscribe is safe', () => { const unsub = client.onNotification(() => {}); unsub(); - unsub(); // Should not throw + expect(() => unsub()).not.toThrow(); }); }); @@ -386,7 +392,9 @@ describe('DaemonClient', () => { ); transport.injectMessage(permRequest); - await new Promise((r) => setTimeout(r, 50)); + await new Promise((r) => { + setTimeout(r, 50); + }); const response = transport.sentMessages.find( (m) => m['id'] === 'perm-1' && m['type'] === 'response' @@ -430,7 +438,9 @@ describe('DaemonClient', () => { ); transport.injectMessage(permRequest); - await new Promise((r) => setTimeout(r, 50)); + await new Promise((r) => { + setTimeout(r, 50); + }); const response = transport.sentMessages.find( (m) => m['id'] === 'perm-2' && m['type'] === 'response' @@ -446,7 +456,9 @@ describe('DaemonClient', () => { await initializeClient(transport, client, 'perm-sess'); client.setPermissionHandler(async () => { - await new Promise((r) => setTimeout(r, 5)); + await new Promise((r) => { + setTimeout(r, 5); + }); return ToolConfirmationOutcome.ProceedOnce; }); @@ -471,7 +483,9 @@ describe('DaemonClient', () => { ); transport.injectMessage(permRequest); - await new Promise((r) => setTimeout(r, 100)); + await new Promise((r) => { + setTimeout(r, 100); + }); const response = transport.sentMessages.find( (m) => m['id'] === 'perm-3' && m['type'] === 'response' @@ -498,7 +512,9 @@ describe('DaemonClient', () => { }); transport.injectMessage(askRequest); - await new Promise((r) => setTimeout(r, 50)); + await new Promise((r) => { + setTimeout(r, 50); + }); const response = transport.sentMessages.find( (m) => m['id'] === 'ask-1' && m['type'] === 'response' @@ -530,7 +546,9 @@ describe('DaemonClient', () => { }); transport.injectMessage(askRequest); - await new Promise((r) => setTimeout(r, 50)); + await new Promise((r) => { + setTimeout(r, 50); + }); const response = transport.sentMessages.find( (m) => m['id'] === 'ask-2' && m['type'] === 'response' @@ -961,7 +979,7 @@ describe('DaemonClient', () => { describe('close()', () => { it('is idempotent', async () => { await client.close(); - await client.close(); // Should not throw + await expect(client.close()).resolves.toBeUndefined(); }); it('clears notification listeners', async () => { @@ -980,7 +998,9 @@ describe('DaemonClient', () => { } catch { // Transport may be closed } - await new Promise((r) => setTimeout(r, 10)); + await new Promise((r) => { + setTimeout(r, 10); + }); expect(notifications).toHaveLength(0); }); diff --git a/tests/daemon/connection-lifecycle.test.ts b/tests/daemon/connection-lifecycle.test.ts index 45e88a3..125cb0f 100644 --- a/tests/daemon/connection-lifecycle.test.ts +++ b/tests/daemon/connection-lifecycle.test.ts @@ -162,7 +162,9 @@ describe('DaemonConnection — lifecycle', () => { }, }); - await new Promise((r) => setTimeout(r, 50)); + await new Promise((r) => { + setTimeout(r, 50); + }); expect(handlerCalled).toBe(true); await session.close(); @@ -216,7 +218,9 @@ describe('DaemonConnection — lifecycle', () => { }, }); - await new Promise((r) => setTimeout(r, 50)); + await new Promise((r) => { + setTimeout(r, 50); + }); expect(handlerCalled).toBe(true); await session.close(); @@ -309,7 +313,7 @@ describe('DaemonConnection — lifecycle', () => { const session = await connection.createSession({ cwd: '/test' }); // Verifies the cleanup callback path executes without throwing - await session.close(); + await expect(session.close()).resolves.toBeUndefined(); }); }); @@ -408,7 +412,9 @@ describe('DaemonConnection — lifecycle', () => { }, }); - await new Promise((r) => setTimeout(r, 50)); + await new Promise((r) => { + setTimeout(r, 50); + }); expect(permCalled).toBe(true); await session.close(); @@ -479,7 +485,7 @@ describe('DaemonConnection — lifecycle', () => { describe('close', () => { it('is idempotent', async () => { await connection.close(); - await connection.close(); // Should not throw + await expect(connection.close()).resolves.toBeUndefined(); }); it('makes subsequent createSession throw', async () => { diff --git a/tests/daemon/doc-snippets-test.ts b/tests/daemon/doc-snippets-test.ts index cbd0165..82c6c4a 100644 --- a/tests/daemon/doc-snippets-test.ts +++ b/tests/daemon/doc-snippets-test.ts @@ -20,12 +20,12 @@ async function test( try { await Promise.race([ fn(), - new Promise((_, reject) => + new Promise((_, reject) => { setTimeout( () => reject(new Error(`Timed out after ${timeoutMs}ms`)), timeoutMs - ) - ), + ); + }), ]); const ms = Date.now() - start; console.log(` ${PASS} ${name} (${ms}ms)`); @@ -148,6 +148,8 @@ async function main(): Promise { ` Done in ${msg.durationMs}ms, turns: ${msg.numTurns}` ); break; + default: + break; } } assert(seen.has('result'), 'should see result message'); @@ -196,7 +198,9 @@ async function main(): Promise { console.log(' send() returned (fire-and-forget)'); // Wait briefly so the daemon doesn't get confused by immediate close - await new Promise((r) => setTimeout(r, 500)); + await new Promise((r) => { + setTimeout(r, 500); + }); await session.close(); await connection.close(); }); diff --git a/tests/daemon/multiplexer.test.ts b/tests/daemon/multiplexer.test.ts index b88aa86..125a8eb 100644 --- a/tests/daemon/multiplexer.test.ts +++ b/tests/daemon/multiplexer.test.ts @@ -134,7 +134,9 @@ describe('SharedTransportMultiplexer (via DaemonConnection)', () => { }, }); - await new Promise((r) => setTimeout(r, 20)); + await new Promise((r) => { + setTimeout(r, 20); + }); // Each session should only see its own notifications expect(notifs1.length).toBe(1); @@ -176,7 +178,9 @@ describe('SharedTransportMultiplexer (via DaemonConnection)', () => { params: { isDroidCLIInPath: true }, }); - await new Promise((r) => setTimeout(r, 10)); + await new Promise((r) => { + setTimeout(r, 10); + }); expect(notifs.length).toBeGreaterThanOrEqual(0); // May or may not reach session depending on method routing await session.close(); }); @@ -200,12 +204,15 @@ describe('SharedTransportMultiplexer (via DaemonConnection)', () => { }); const session = await connection.createSession({ cwd: '/test' }); + expect(session.sessionId).toBe('s-err'); // Inject a transport error — should propagate transport.injectError(new Error('WebSocket closed unexpectedly')); // Session should now be in an error state - await new Promise((r) => setTimeout(r, 10)); + await new Promise((r) => { + setTimeout(r, 10); + }); // Cleanup try { @@ -252,7 +259,9 @@ describe('SharedTransportMultiplexer (via DaemonConnection)', () => { }, }); - await new Promise((r) => setTimeout(r, 10)); + await new Promise((r) => { + setTimeout(r, 10); + }); // After close, no new notifications should be received expect(notifs).toHaveLength(0); }); @@ -310,7 +319,9 @@ describe('SharedTransportMultiplexer (via DaemonConnection)', () => { }, }); - await new Promise((r) => setTimeout(r, 50)); + await new Promise((r) => { + setTimeout(r, 50); + }); // A response should have been sent back const response = transport.sentMessages.find( diff --git a/tests/daemon/session-advanced.test.ts b/tests/daemon/session-advanced.test.ts index 7f714f9..1c4bf8c 100644 --- a/tests/daemon/session-advanced.test.ts +++ b/tests/daemon/session-advanced.test.ts @@ -90,7 +90,9 @@ describe('DaemonSession — advanced scenarios', () => { } })(); - await new Promise((r) => setTimeout(r, 10)); + await new Promise((r) => { + setTimeout(r, 10); + }); // Abort after the stream has started controller.abort(new Error('User cancelled')); @@ -127,7 +129,9 @@ describe('DaemonSession — advanced scenarios', () => { } })(); - await new Promise((r) => setTimeout(r, 10)); + await new Promise((r) => { + setTimeout(r, 10); + }); controller.abort('timeout'); await streamPromise; @@ -151,7 +155,9 @@ describe('DaemonSession — advanced scenarios', () => { } })(); - await new Promise((r) => setTimeout(r, 10)); + await new Promise((r) => { + setTimeout(r, 10); + }); controller.abort(); await streamPromise; @@ -173,7 +179,9 @@ describe('DaemonSession — advanced scenarios', () => { } })(); - await new Promise((r) => setTimeout(r, 10)); + await new Promise((r) => { + setTimeout(r, 10); + }); controller.abort(new Error('stop')); await streamPromise; @@ -197,7 +205,9 @@ describe('DaemonSession — advanced scenarios', () => { } })(); - await new Promise((r) => setTimeout(r, 10)); + await new Promise((r) => { + setTimeout(r, 10); + }); sendDefaultStreamSequence(transport, { deltas: ['Chunk1', ' Chunk2'] }); await streamPromise; @@ -219,7 +229,9 @@ describe('DaemonSession — advanced scenarios', () => { } })(); - await new Promise((r) => setTimeout(r, 10)); + await new Promise((r) => { + setTimeout(r, 10); + }); sendDefaultStreamSequence(transport); await streamPromise; @@ -246,7 +258,9 @@ describe('DaemonSession — advanced scenarios', () => { } })(); - await new Promise((r) => setTimeout(r, 10)); + await new Promise((r) => { + setTimeout(r, 10); + }); sendDefaultStreamSequence(transport); await streamPromise; @@ -266,36 +280,53 @@ describe('DaemonSession — advanced scenarios', () => { } })(); - await new Promise((r) => setTimeout(r, 10)); + await new Promise((r) => { + setTimeout(r, 10); + }); sendDefaultStreamSequence(transport); await streamPromise; // After stream completes, session should still be usable + const secondStreamMessages: DroidStreamEvent[] = []; const streamPromise2 = (async () => { - for await (const _msg of session.stream('test2')) { - // consume + for await (const msg of session.stream('test2')) { + secondStreamMessages.push(msg); } })(); - await new Promise((r) => setTimeout(r, 10)); + await new Promise((r) => { + setTimeout(r, 10); + }); sendDefaultStreamSequence(transport); await streamPromise2; + + expect(secondStreamMessages.length).toBeGreaterThan(0); }); it('unsubscribes notification handler after stream completes', async () => { + const messages: DroidStreamEvent[] = []; const streamPromise = (async () => { - for await (const _msg of session.stream('test')) { - // consume + for await (const msg of session.stream('test')) { + messages.push(msg); } })(); - await new Promise((r) => setTimeout(r, 10)); + await new Promise((r) => { + setTimeout(r, 10); + }); sendDefaultStreamSequence(transport); await streamPromise; - // Late notifications should not cause issues + const countAfterStream = messages.length; + expect(countAfterStream).toBeGreaterThan(0); + + // Late notifications should not reach the unsubscribed handler sendDefaultStreamSequence(transport); - await new Promise((r) => setTimeout(r, 10)); + await new Promise((r) => { + setTimeout(r, 10); + }); + + expect(messages.length).toBe(countAfterStream); }); }); @@ -311,7 +342,9 @@ describe('DaemonSession — advanced scenarios', () => { streamDone = true; })(); - await new Promise((r) => setTimeout(r, 10)); + await new Promise((r) => { + setTimeout(r, 10); + }); // Close while streaming await session.close(); @@ -330,7 +363,9 @@ describe('DaemonSession — advanced scenarios', () => { messages1.push(msg); } })(); - await new Promise((r) => setTimeout(r, 10)); + await new Promise((r) => { + setTimeout(r, 10); + }); sendDefaultStreamSequence(transport, { deltas: ['Response 1'] }); await stream1; @@ -341,7 +376,9 @@ describe('DaemonSession — advanced scenarios', () => { messages2.push(msg); } })(); - await new Promise((r) => setTimeout(r, 10)); + await new Promise((r) => { + setTimeout(r, 10); + }); sendDefaultStreamSequence(transport, { deltas: ['Response 2'] }); await stream2; @@ -415,7 +452,9 @@ describe('DaemonSession — advanced scenarios', () => { }, }); - await new Promise((r) => setTimeout(r, 10)); + await new Promise((r) => { + setTimeout(r, 10); + }); expect(notifications.length).toBeGreaterThan(0); }); }); @@ -433,7 +472,9 @@ describe('DaemonSession — advanced scenarios', () => { } })(); - await new Promise((r) => setTimeout(r, 10)); + await new Promise((r) => { + setTimeout(r, 10); + }); sendDefaultStreamSequence(transport); await streamPromise; diff --git a/tests/daemon/session.test.ts b/tests/daemon/session.test.ts index e2090f8..1838dd1 100644 --- a/tests/daemon/session.test.ts +++ b/tests/daemon/session.test.ts @@ -112,9 +112,14 @@ describe('DaemonSession', () => { it('does not subscribe to notifications after send', async () => { await session.send('Quick task.'); + const addUserMessageSent = transport.sentMessages.find( + (m) => m['method'] === 'daemon.add_user_message' + ); + expect(addUserMessageSent).toBeDefined(); + // Injecting a notification after send should not cause any issues // (send() does not subscribe to notifications) - sendDefaultStreamSequence(transport); + expect(() => sendDefaultStreamSequence(transport)).not.toThrow(); }); it('throws when session is closed', async () => { @@ -133,7 +138,9 @@ describe('DaemonSession', () => { })(); // Wait a tick for the auto-responder to handle addUserMessage - await new Promise((r) => setTimeout(r, 10)); + await new Promise((r) => { + setTimeout(r, 10); + }); // Use the standard test helper to inject a full stream sequence sendDefaultStreamSequence(transport); @@ -167,7 +174,7 @@ describe('DaemonSession', () => { describe('close()', () => { it('is idempotent', async () => { await session.close(); - await session.close(); // Should not throw + await expect(session.close()).resolves.toBeUndefined(); }); it('signals done to active bridges', async () => { @@ -179,11 +186,13 @@ describe('DaemonSession', () => { })(); // Wait for auto-responder to handle addUserMessage - await new Promise((r) => setTimeout(r, 10)); + await new Promise((r) => { + setTimeout(r, 10); + }); // Close the session — should terminate the stream await session.close(); - await streamPromise; + await expect(streamPromise).resolves.toBeUndefined(); }); it('invokes cleanup callbacks when session is closed', async () => { @@ -215,7 +224,9 @@ describe('DaemonSession', () => { it('awaits async cleanup callbacks', async () => { let cleaned = false; session.addCleanup(async () => { - await new Promise((r) => setTimeout(r, 10)); + await new Promise((r) => { + setTimeout(r, 10); + }); cleaned = true; }); diff --git a/tests/daemon/stress-test-suite.ts b/tests/daemon/stress-test-suite.ts index 571ff1b..ef83877 100644 --- a/tests/daemon/stress-test-suite.ts +++ b/tests/daemon/stress-test-suite.ts @@ -65,12 +65,12 @@ async function test( try { await Promise.race([ fn(), - new Promise((_, reject) => + new Promise((_, reject) => { setTimeout( () => reject(new Error(`Timeout after ${timeoutMs}ms`)), timeoutMs - ) - ), + ); + }), ]); const dur = Date.now() - start; results.push({ diff --git a/tests/daemon/transport.test.ts b/tests/daemon/transport.test.ts index b194e68..0f87973 100644 --- a/tests/daemon/transport.test.ts +++ b/tests/daemon/transport.test.ts @@ -55,13 +55,13 @@ describe('WebSocketTransport', () => { describe('close()', () => { it('is safe to call when not connected', async () => { const transport = new WebSocketTransport(); - await transport.close(); // Should not throw + await expect(transport.close()).resolves.toBeUndefined(); }); it('is idempotent', async () => { const transport = new WebSocketTransport(); - await transport.close(); - await transport.close(); // Should not throw + await expect(transport.close()).resolves.toBeUndefined(); + await expect(transport.close()).resolves.toBeUndefined(); }); }); @@ -69,15 +69,13 @@ describe('WebSocketTransport', () => { it('accepts message handler', () => { const transport = new WebSocketTransport(); const handler = vi.fn(); - transport.onMessage(handler); - // No error means it works + expect(() => transport.onMessage(handler)).not.toThrow(); }); it('accepts error handler', () => { const transport = new WebSocketTransport(); const handler = vi.fn(); - transport.onError(handler); - // No error means it works + expect(() => transport.onError(handler)).not.toThrow(); }); }); }); diff --git a/tests/integration.test.ts b/tests/integration.test.ts index 4c65405..a4931f0 100644 --- a/tests/integration.test.ts +++ b/tests/integration.test.ts @@ -1863,7 +1863,9 @@ describe('Settings update notification flow (VAL-CROSS-007)', () => { await session.updateSettings({ modelId: 'upgraded-model' } as never); - await new Promise((r) => setTimeout(r, 20)); + await new Promise((r) => { + setTimeout(r, 20); + }); const sentUpdateSettings = transport.sentMessages.find( (m) => diff --git a/tests/protocol.test.ts b/tests/protocol.test.ts index 891f2a2..33aa4f4 100644 --- a/tests/protocol.test.ts +++ b/tests/protocol.test.ts @@ -725,9 +725,12 @@ describe('ProtocolEngine', () => { }) ); - await new Promise((resolve) => setTimeout(resolve, 10)); + await new Promise((resolve) => { + setTimeout(resolve, 10); + }); - await engine2.close(); + // The broken transport.send must not surface as an unhandled rejection. + await expect(engine2.close()).resolves.toBeUndefined(); }); }); @@ -1021,7 +1024,9 @@ describe('ProtocolEngine', () => { }); // Give time for any async dispatch - await new Promise((r) => setTimeout(r, 50)); + await new Promise((r) => { + setTimeout(r, 50); + }); expect(permHandler).not.toHaveBeenCalled(); expect(askHandler).not.toHaveBeenCalled(); diff --git a/tests/session.test.ts b/tests/session.test.ts index 913bf7b..6e2a965 100644 --- a/tests/session.test.ts +++ b/tests/session.test.ts @@ -695,7 +695,7 @@ describe('DroidSession', () => { await session.close(); await session.close(); - await session.close(); + await expect(session.close()).resolves.toBeUndefined(); }); it('requests graceful close so file hooks can receive SessionEnd', async () => { @@ -1372,7 +1372,9 @@ describe('DroidSession', () => { }) ); - await new Promise((resolve) => setTimeout(resolve, 10)); + await new Promise((resolve) => { + setTimeout(resolve, 10); + }); expect(notifications.length).toBe(1); @@ -1384,7 +1386,9 @@ describe('DroidSession', () => { }) ); - await new Promise((resolve) => setTimeout(resolve, 10)); + await new Promise((resolve) => { + setTimeout(resolve, 10); + }); expect(notifications.length).toBe(1); await session.close(); @@ -1980,7 +1984,9 @@ describe('DroidSession', () => { controller.abort(); - await new Promise((resolve) => setTimeout(resolve, 10)); + await new Promise((resolve) => { + setTimeout(resolve, 10); + }); expect(transport.isConnected).toBe(false); await expectStreamToThrow(session, 'test'); @@ -2001,7 +2007,9 @@ describe('DroidSession', () => { abortSignal: controller.signal, }); - await new Promise((resolve) => setTimeout(resolve, 10)); + await new Promise((resolve) => { + setTimeout(resolve, 10); + }); expect(transport.isConnected).toBe(false); await expectStreamToThrow(session, 'test'); @@ -2027,7 +2035,9 @@ describe('DroidSession', () => { controller.abort(); - await new Promise((resolve) => setTimeout(resolve, 10)); + await new Promise((resolve) => { + setTimeout(resolve, 10); + }); expect(transport.isConnected).toBe(false); await expectStreamToThrow(session, 'test'); @@ -2048,7 +2058,9 @@ describe('DroidSession', () => { abortSignal: controller.signal, }); - await new Promise((resolve) => setTimeout(resolve, 10)); + await new Promise((resolve) => { + setTimeout(resolve, 10); + }); expect(transport.isConnected).toBe(false); await expectStreamToThrow(session, 'test'); diff --git a/tests/transport.test.ts b/tests/transport.test.ts index 06745be..da0db45 100644 --- a/tests/transport.test.ts +++ b/tests/transport.test.ts @@ -471,7 +471,9 @@ describe('ProcessTransport', () => { transport.send({ first: true }); - await new Promise((resolve) => setTimeout(resolve, 200)); + await new Promise((resolve) => { + setTimeout(resolve, 200); + }); try { transport.send({ second: true }); @@ -540,7 +542,9 @@ describe('ProcessTransport', () => { await transport.connect!(); - await new Promise((resolve) => setTimeout(resolve, 300)); + await new Promise((resolve) => { + setTimeout(resolve, 300); + }); expect(messages.length).toBeGreaterThanOrEqual(1); expect(messages[0]).toEqual({ early: true });