diff --git a/CHANGELOG.md b/CHANGELOG.md index 08703a26..2e4a2fe7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,11 @@ - Deleted stray artifacts from the working tree: `autotask-node-2.0.0.tgz`, `npm-debug-403.log`, and the empty `logs/` directory. +### Fixed + +- **Cleared the 17 real ESLint errors (`preserve-caught-error`, `no-useless-assignment`) that a bumped `eslint`/`@typescript-eslint/eslint-plugin` devDependency newly surfaces on pre-existing code, blocking dependabot dev-dependency PRs (#233 and any future bump hitting the same two rules).** 8 `preserve-caught-error` sites now attach `{ cause: }` to the re-thrown `Error`. 8 `no-useless-assignment` sites were genuine dead stores (a `let` initializer or intermediate assignment never read before being overwritten or falling out of scope) and had the dead assignment removed. One site (`test/base.test.ts`, a `WeakRef` GC test) is an intentional false positive — nulling the variable has a real side effect (dropping the last strong reference so `global.gc()` can collect it) even though the linter can't see a subsequent read — kept via a scoped `eslint-disable-next-line` with an inline explanation rather than removed. +- Added `ES2022.Error` to `tsconfig.json`'s `lib` array (alongside the existing `ES2021`) — the two-argument `Error(message, { cause })` constructor form needs it for type-checking; `target` is unchanged (still `ES2020`), so output syntax is unaffected. + ### Security - **Closed GHSA-r292-9mhp-454m (node-tar uncontrolled recursion, high, stack-overflow DoS via crafted long-path tar).** Bundled `tar` (pulled in transitively via the `npm` CLI devDependency, itself pulled in via `@semantic-release/npm`) was at 7.5.19/7.5.20, just short of the 7.5.21 patch. A plain `npm audit fix` (no `--force`) re-resolved `npm` to 11.19.1 (bundled tar 7.5.22) within the existing declared range — lockfile-only change, no `package.json`/direct-dependency edits, no semver-major bump. `npm audit`'s own suggested remediation path (`semantic-release@24.2.9`, flagged `isSemVerMajor`) was a red herring — the currently-resolved `semantic-release` (25.0.3, newer than that suggestion) was never at risk and is unchanged by this fix. diff --git a/examples/shared/auth/autotask-auth.ts b/examples/shared/auth/autotask-auth.ts index 024716d5..9ea5d51d 100644 --- a/examples/shared/auth/autotask-auth.ts +++ b/examples/shared/auth/autotask-auth.ts @@ -48,7 +48,7 @@ export class AutotaskAuthManager { logger.info('Autotask connection initialized successfully'); } catch (error) { logger.error('Failed to initialize Autotask connection:', error); - throw new Error(`Autotask authentication failed: ${error instanceof Error ? error.message : 'Unknown error'}`); + throw new Error(`Autotask authentication failed: ${error instanceof Error ? error.message : 'Unknown error'}`, { cause: error }); } } diff --git a/src/caching/TTLManager.ts b/src/caching/TTLManager.ts index bb0ba4d8..14031fb5 100644 --- a/src/caching/TTLManager.ts +++ b/src/caching/TTLManager.ts @@ -324,7 +324,7 @@ export class TTLManager { hour >= this.businessHours.startHour && hour < this.businessHours.endHour; - let ttlMultiplier = 1; + let ttlMultiplier: number; let confidence = 0.7; let reason: string; diff --git a/src/caching/stores/FileCacheStore.ts b/src/caching/stores/FileCacheStore.ts index acaac9c9..a2388f8f 100644 --- a/src/caching/stores/FileCacheStore.ts +++ b/src/caching/stores/FileCacheStore.ts @@ -852,7 +852,7 @@ export class FileCacheStore implements ICacheStore { */ private async performCleanup(): Promise { const now = Date.now(); - let deletedCount = 0; + let deletedCount: number; const expiredKeys: string[] = []; // Find expired keys diff --git a/src/migration/cli/MigrationCLI.ts b/src/migration/cli/MigrationCLI.ts index 552a7164..ae5c9a65 100644 --- a/src/migration/cli/MigrationCLI.ts +++ b/src/migration/cli/MigrationCLI.ts @@ -387,7 +387,7 @@ async function loadConfig(): Promise { const configContent = await fs.readFile(configPath, 'utf8'); return JSON.parse(configContent); } catch (error) { - throw new Error(`Failed to load configuration from ${configPath}. Run 'autotask-migrate init' first.`); + throw new Error(`Failed to load configuration from ${configPath}. Run 'autotask-migrate init' first.`, { cause: error }); } } diff --git a/src/performance/monitoring/ConnectionPoolMonitor.ts b/src/performance/monitoring/ConnectionPoolMonitor.ts index 55a4448c..797cee03 100644 --- a/src/performance/monitoring/ConnectionPoolMonitor.ts +++ b/src/performance/monitoring/ConnectionPoolMonitor.ts @@ -302,8 +302,8 @@ export class ConnectionPoolMonitor extends EventEmitter { // In a real implementation, this would query actual HTTP agents // For now, we'll simulate metrics based on recorded events - let totalConnections = 0; - let activeConnections = 0; + let totalConnections: number; + let activeConnections: number; let connectionsCreated = 0; let connectionsDestroyed = 0; let totalAcquisitionTime = 0; diff --git a/src/relationships/loading/SmartLoadingEngine.ts b/src/relationships/loading/SmartLoadingEngine.ts index de74e9e4..1dba4828 100644 --- a/src/relationships/loading/SmartLoadingEngine.ts +++ b/src/relationships/loading/SmartLoadingEngine.ts @@ -539,7 +539,7 @@ export class SmartLoadingEngine { } // Execute query based on relationship type - let relatedData: any[] = []; + let relatedData: any[]; try { switch (relationship.relationshipType) { @@ -562,7 +562,7 @@ export class SmartLoadingEngine { } } catch (error) { - throw new Error(`Failed to load ${relationshipName}: ${(error as Error).message}`); + throw new Error(`Failed to load ${relationshipName}: ${(error as Error).message}`, { cause: error }); } return relatedData; diff --git a/src/tools/client-generator.ts b/src/tools/client-generator.ts index 8c2c2a20..9753c841 100644 --- a/src/tools/client-generator.ts +++ b/src/tools/client-generator.ts @@ -64,7 +64,7 @@ class ClientGenerator { const data = fs.readFileSync(this.dataFile, 'utf8'); return JSON.parse(data) as EntitiesData; } catch (error) { - throw new Error(`Failed to load entity data: ${error}`); + throw new Error(`Failed to load entity data: ${error}`, { cause: error }); } } diff --git a/src/tools/entity-generator.ts b/src/tools/entity-generator.ts index b2422913..76961238 100644 --- a/src/tools/entity-generator.ts +++ b/src/tools/entity-generator.ts @@ -55,7 +55,7 @@ class EntityGenerator { const data = fs.readFileSync(this.dataFile, 'utf8'); return JSON.parse(data) as EntitiesData; } catch (error) { - throw new Error(`Failed to load entity data: ${error}`); + throw new Error(`Failed to load entity data: ${error}`, { cause: error }); } } diff --git a/src/validation/index.ts b/src/validation/index.ts index 8f0190b2..ffecf14b 100644 --- a/src/validation/index.ts +++ b/src/validation/index.ts @@ -466,7 +466,8 @@ export class ValidationDecorators { } } catch (error) { throw new Error( - `Validation error: ${error instanceof Error ? error.message : String(error)}` + `Validation error: ${error instanceof Error ? error.message : String(error)}`, + { cause: error } ); } } diff --git a/src/validation/security/SecurityValidator.ts b/src/validation/security/SecurityValidator.ts index b1f073c7..c10f184e 100644 --- a/src/validation/security/SecurityValidator.ts +++ b/src/validation/security/SecurityValidator.ts @@ -729,7 +729,7 @@ export class SecurityValidator { return 'ENC:' + CryptoJS.AES.encrypt(value, this.encryptionKey).toString(); } catch (error) { this.logger.error('Encryption error:', error); - throw new Error('Failed to encrypt value'); + throw new Error('Failed to encrypt value', { cause: error }); } } diff --git a/src/webhooks/reliability/EventStore.ts b/src/webhooks/reliability/EventStore.ts index 6fb05932..dfa16661 100644 --- a/src/webhooks/reliability/EventStore.ts +++ b/src/webhooks/reliability/EventStore.ts @@ -321,7 +321,7 @@ export class EventStore extends EventEmitter implements IEventStore { public async getByFilter(filter: EventStoreFilter): Promise { const startTime = Date.now(); - let eventIds: Set = new Set(); + let eventIds: Set; try { // Use index for efficient filtering if available @@ -402,7 +402,6 @@ export class EventStore extends EventEmitter implements IEventStore { candidateIds = firstFilter ? dateIds : this.intersect(candidateIds, dateIds); - firstFilter = false; } return candidateIds; diff --git a/test/base.test.ts b/test/base.test.ts index 982863a3..3f1ebfc4 100644 --- a/test/base.test.ts +++ b/test/base.test.ts @@ -759,7 +759,10 @@ describe('BaseEntity - Comprehensive Tests', () => { await testEntity.testExecuteRequest(requestFn, '/test', 'GET'); - requestFn = null; // Remove our reference + // Intentional dead-looking store: drops the last strong reference so + // `global.gc()` below can actually collect it for the WeakRef check. + // eslint-disable-next-line no-useless-assignment + requestFn = null; // Force garbage collection if available (for testing environments) if (global.gc) { diff --git a/test/integration/framework/TestEnvironment.ts b/test/integration/framework/TestEnvironment.ts index d977b2e3..8aeb7c06 100644 --- a/test/integration/framework/TestEnvironment.ts +++ b/test/integration/framework/TestEnvironment.ts @@ -135,7 +135,7 @@ export class TestEnvironment { // Test basic connectivity with a simple list operation await this.client.version.list({ pageSize: 1 }); } catch (error) { - throw new Error(`Connection validation failed: ${error}`); + throw new Error(`Connection validation failed: ${error}`, { cause: error }); } } diff --git a/test/integration/setup-enhanced.ts b/test/integration/setup-enhanced.ts index 3edb157f..3ca237fb 100644 --- a/test/integration/setup-enhanced.ts +++ b/test/integration/setup-enhanced.ts @@ -58,7 +58,7 @@ export default async function globalSetup(): Promise { console.log('✅ API connection successful'); } catch (error) { console.error('❌ API connection failed:', error); - throw new Error('API connection validation failed'); + throw new Error('API connection validation failed', { cause: error }); } // Store global instances diff --git a/tsconfig.json b/tsconfig.json index a37c2922..10bf4425 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,8 @@ "compilerOptions": { "target": "ES2020", "lib": [ - "ES2021" + "ES2021", + "ES2022.Error" ], "module": "CommonJS", "moduleResolution": "Node",