feat: add ESLint with error-handling rules
Add ESLint v9.35+ with typescript-eslint recommended config and
error-handling rules: preserve-caught-error (enforces { cause } when
re-throwing), no-unused-vars with caughtErrors:all, and
eslint-plugin-no-catch-all (warns on catch blocks that don't rethrow).
Fix existing violations: add error cause to container-runtime rethrow,
prefix unused vars with underscore, remove unused imports.
https://claude.ai/code/session_01JPjzhBp9PR5LtfLWVDrYrH
This commit is contained in:
32
eslint.config.js
Normal file
32
eslint.config.js
Normal file
@@ -0,0 +1,32 @@
|
||||
import globals from 'globals'
|
||||
import pluginJs from '@eslint/js'
|
||||
import tseslint from 'typescript-eslint'
|
||||
import noCatchAll from 'eslint-plugin-no-catch-all'
|
||||
|
||||
export default [
|
||||
{ ignores: ['node_modules/', 'dist/', 'container/', 'groups/'] },
|
||||
{ files: ['src/**/*.{js,ts}'] },
|
||||
{ languageOptions: { globals: globals.node } },
|
||||
pluginJs.configs.recommended,
|
||||
...tseslint.configs.recommended,
|
||||
{
|
||||
plugins: { 'no-catch-all': noCatchAll },
|
||||
rules: {
|
||||
'preserve-caught-error': ['error', { requireCatchParameter: true }],
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'error',
|
||||
{
|
||||
args: 'all',
|
||||
argsIgnorePattern: '^_',
|
||||
caughtErrors: 'all',
|
||||
caughtErrorsIgnorePattern: '^_',
|
||||
destructuredArrayIgnorePattern: '^_',
|
||||
varsIgnorePattern: '^_',
|
||||
ignoreRestSiblings: true,
|
||||
},
|
||||
],
|
||||
'no-catch-all/no-catch-all': 'warn',
|
||||
'@typescript-eslint/no-explicit-any': 'warn',
|
||||
},
|
||||
},
|
||||
]
|
||||
1376
package-lock.json
generated
1376
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -15,6 +15,8 @@
|
||||
"prepare": "husky",
|
||||
"setup": "tsx setup/index.ts",
|
||||
"auth": "tsx src/whatsapp-auth.ts",
|
||||
"lint": "eslint src/",
|
||||
"lint:fix": "eslint src/ --fix",
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest"
|
||||
},
|
||||
@@ -27,13 +29,18 @@
|
||||
"zod": "^4.3.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.35.0",
|
||||
"@types/better-sqlite3": "^7.6.12",
|
||||
"@types/node": "^22.10.0",
|
||||
"@vitest/coverage-v8": "^4.0.18",
|
||||
"eslint": "^9.35.0",
|
||||
"eslint-plugin-no-catch-all": "^1.1.0",
|
||||
"globals": "^15.12.0",
|
||||
"husky": "^9.1.7",
|
||||
"prettier": "^3.8.1",
|
||||
"tsx": "^4.19.0",
|
||||
"typescript": "^5.7.0",
|
||||
"typescript-eslint": "^8.35.0",
|
||||
"vitest": "^4.0.18"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import { describe, it, expect } from 'vitest';
|
||||
|
||||
import {
|
||||
registerChannel,
|
||||
|
||||
@@ -698,7 +698,7 @@ export function writeGroupsSnapshot(
|
||||
groupFolder: string,
|
||||
isMain: boolean,
|
||||
groups: AvailableGroup[],
|
||||
registeredJids: Set<string>,
|
||||
_registeredJids: Set<string>,
|
||||
): void {
|
||||
const groupIpcDir = resolveGroupIpcPath(groupFolder);
|
||||
fs.mkdirSync(groupIpcDir, { recursive: true });
|
||||
|
||||
@@ -96,7 +96,7 @@ export function ensureContainerRuntimeRunning(): void {
|
||||
console.error(
|
||||
'╚════════════════════════════════════════════════════════════════╝\n',
|
||||
);
|
||||
throw new Error('Container runtime is required but failed to start');
|
||||
throw new Error('Container runtime is required but failed to start', { cause: err });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ describe('GroupQueue', () => {
|
||||
let concurrentCount = 0;
|
||||
let maxConcurrent = 0;
|
||||
|
||||
const processMessages = vi.fn(async (groupJid: string) => {
|
||||
const processMessages = vi.fn(async (_groupJid: string) => {
|
||||
concurrentCount++;
|
||||
maxConcurrent = Math.max(maxConcurrent, concurrentCount);
|
||||
// Simulate async work
|
||||
@@ -69,7 +69,7 @@ describe('GroupQueue', () => {
|
||||
let maxActive = 0;
|
||||
const completionCallbacks: Array<() => void> = [];
|
||||
|
||||
const processMessages = vi.fn(async (groupJid: string) => {
|
||||
const processMessages = vi.fn(async (_groupJid: string) => {
|
||||
activeCount++;
|
||||
maxActive = Math.max(maxActive, activeCount);
|
||||
await new Promise<void>((resolve) => completionCallbacks.push(resolve));
|
||||
@@ -104,7 +104,7 @@ describe('GroupQueue', () => {
|
||||
const executionOrder: string[] = [];
|
||||
let resolveFirst: () => void;
|
||||
|
||||
const processMessages = vi.fn(async (groupJid: string) => {
|
||||
const processMessages = vi.fn(async (_groupJid: string) => {
|
||||
if (executionOrder.length === 0) {
|
||||
// First call: block until we release it
|
||||
await new Promise<void>((resolve) => {
|
||||
|
||||
@@ -351,7 +351,7 @@ export class GroupQueue {
|
||||
// via idle timeout or container timeout. The --rm flag cleans them up on exit.
|
||||
// This prevents WhatsApp reconnection restarts from killing working agents.
|
||||
const activeContainers: string[] = [];
|
||||
for (const [jid, state] of this.groups) {
|
||||
for (const [_jid, state] of this.groups) {
|
||||
if (state.process && !state.process.killed && state.containerName) {
|
||||
activeContainers.push(state.containerName);
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ import {
|
||||
getAllTasks,
|
||||
getMessagesSince,
|
||||
getNewMessages,
|
||||
getRegisteredGroup,
|
||||
getRouterState,
|
||||
initDatabase,
|
||||
setRegisteredGroup,
|
||||
|
||||
@@ -37,7 +37,7 @@ describe('remote-control', () => {
|
||||
let readFileSyncSpy: ReturnType<typeof vi.spyOn>;
|
||||
let writeFileSyncSpy: ReturnType<typeof vi.spyOn>;
|
||||
let unlinkSyncSpy: ReturnType<typeof vi.spyOn>;
|
||||
let mkdirSyncSpy: ReturnType<typeof vi.spyOn>;
|
||||
let _mkdirSyncSpy: ReturnType<typeof vi.spyOn>;
|
||||
let openSyncSpy: ReturnType<typeof vi.spyOn>;
|
||||
let closeSyncSpy: ReturnType<typeof vi.spyOn>;
|
||||
|
||||
@@ -50,7 +50,7 @@ describe('remote-control', () => {
|
||||
stdoutFileContent = '';
|
||||
|
||||
// Default fs mocks
|
||||
mkdirSyncSpy = vi
|
||||
_mkdirSyncSpy = vi
|
||||
.spyOn(fs, 'mkdirSync')
|
||||
.mockImplementation(() => undefined as any);
|
||||
writeFileSyncSpy = vi
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
|
||||
import { _initTestDatabase, getAllChats, storeChatMetadata } from './db.js';
|
||||
import { _initTestDatabase, storeChatMetadata } from './db.js';
|
||||
import { getAvailableGroups, _setRegisteredGroups } from './index.js';
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import fs from 'fs';
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
||||
|
||||
import {
|
||||
isSenderAllowed,
|
||||
|
||||
Reference in New Issue
Block a user