| name | javascript-strict |
| description | JavaScript (Node.js) strictness, clean code, and security rules. Use when writing, reviewing, or refactoring plain JavaScript (non-TypeScript) Node.js code. Covers const-first variable declarations, async/await patterns, class vs function patterns, JSDoc documentation, error handling, hot-path performance, CommonJS module patterns, and vulnerability prevention. Derived from production Node.js services.
|
JavaScript Strict Standard
Rules extracted from production Node.js services (non-TypeScript).
CRITICAL: Variable Declarations
JS-01: const by default, let when necessary, never var
var count = 0;
var items = [];
const VALID_MODES = ['bypass', 'browser', 'capsolver'];
let activeCount = 0;
let currentDelay = baseDelay;
No var anywhere.
JS-02: Destructure at declaration
const name = config.name;
const port = config.port;
const mode = config.mode;
const { name, port, mode } = config;
const { mode = 'both', delay = 1000 } = options;
CRITICAL: Error Handling
JS-03: Never catch and swallow errors
try { await save(); } catch (e) {}
try { await save(); } catch (e) { console.log(e); }
try {
await save();
} catch (err) {
console.error(`[Monitor] ${this.monitorId} Save failed:`, err.message);
await this.notifyError('save_failure', err.message);
}
JS-04: Prefix error logs with context
console.error(err.message);
console.error(`[Monitor] ${this.monitorId} Error:`, err.message);
console.error(`[TokenBank] ${this.baseHost} Persist failed:`, err.message);
console.warn(`[DEPRECATED] MONITOR_BYPASS is deprecated, use TMPT_MODE`);
Format: [Module] ${identifier} Action: message
JS-05: Avoid recursive retry without limits
async getTmpt() {
try {
const token = await this.tmptBank.getTmpt();
if (!token) { await sleep(1000); return this.getTmpt(); }
return token;
} catch {
await sleep(1000);
return this.getTmpt();
}
}
async getTmpt(retries = 10) {
for (let i = 0; i < retries; i++) {
try {
const token = await this.tmptBank.getTmpt();
if (token) return token;
} catch (err) {
console.error(`[Monitor] getTmpt attempt ${i + 1}/${retries}:`, err.message);
}
await sleep(1000 * Math.min(i + 1, 5));
}
throw new Error(`Failed to get TMPT after ${retries} attempts`);
}
CRITICAL: Async Patterns
JS-06: async/await over raw Promises
function fetchData() {
return fetch(url).then(r => r.json()).then(data => process(data)).catch(handleError);
}
async function fetchData() {
const response = await fetch(url);
const data = await response.json();
return process(data);
}
JS-07: Promise.race for timeouts
function withTimeout(promise, ms, message = 'Timeout') {
let timeoutId;
const timeoutPromise = new Promise((_, reject) => {
timeoutId = setTimeout(() => reject(new Error(message)), ms);
});
return Promise.race([promise, timeoutPromise])
.finally(() => clearTimeout(timeoutId));
}
const data = await withTimeout(fetch(url), 5000, 'Request timed out');
Always .finally() to clear the timeout: prevents dangling timers.
JS-08: Async file I/O only
fs.writeFileSync(path, JSON.stringify(data, null, 2));
await fs.promises.writeFile(path, JSON.stringify(data, null, 2));
const json = JSON.stringify(data, null, 2);
const hash = crypto.createHash('md5').update(json).digest('hex');
if (hash !== this._lastHash) {
await fs.promises.writeFile(path, json);
this._lastHash = hash;
}
HIGH: Module Patterns
JS-09: CommonJS with clear exports
class Monitor { ... }
module.exports = Monitor;
module.exports = {
addMonitor: async (data) => { ... },
getMonitor: async (id) => { ... },
updateMonitor: async (id, updates) => { ... },
};
const { sleep, withTimeout, shouldRun } = require('./utils');
module.exports = { sleep, withTimeout, shouldRun };
JS-10: No circular dependencies
If A requires B and B requires A, extract shared logic into C.
HIGH: Class Patterns
JS-11: Classes for stateful objects, functions for utilities
class Monitor {
constructor(monitorData, tmptBank, mode = 'both') {
this.monitorId = monitorData.id;
this.tmptBank = tmptBank;
this.mode = mode;
this.stopped = false;
this.delay = 100;
this.notifiedListings = new Set();
}
async start() { ... }
async stop() { this.stopped = true; }
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function formatProxy(line) {
const [host, port, user, pass] = line.split(':');
return `http://${user}:${pass}@${host}:${port}`;
}
HIGH: Documentation (JSDoc)
JS-12: JSDoc for all public methods
async makeRequest(config) { ... }
Essential for non-TypeScript codebases: provides IDE autocomplete and documentation.
MEDIUM: Performance Rules
JS-13: Set for membership, Map for lookups
const newIds = currentIds.filter(x => !savedIds.includes(x));
const savedSet = new Set(savedIds);
const newIds = currentIds.filter(x => !savedSet.has(x));
JS-14: Single-pass algorithms in hot paths
const available = tokens.filter(t => !t.expired);
available.sort((a, b) => a.lastUsed - b.lastUsed);
const best = available[0];
let best = null;
for (const t of tokens) {
if (t.expired) continue;
if (!best || t.lastUsed < best.lastUsed) best = t;
}
JS-15: Cache expensive computations
function getProxy() {
const proxies = source.split(/\r?\n/);
return proxies[Math.floor(Math.random() * proxies.length)].split(':');
}
class ProxyPool {
constructor(source) {
this.urls = source.split(/\r?\n/).filter(Boolean).map(formatProxy);
}
random() { return this.urls[Math.floor(Math.random() * this.urls.length)]; }
}
JS-16: Index Maps for frequent lookups
function findToken(value) {
return this.tokens.find(t => t.value === value);
}
this.tokenIndex = new Map(tokens.map(t => [t.value, t]));
function findToken(value) {
return this.tokenIndex.get(value);
}
Security Rules
JS-17: Never eval() or Function()
eval(userInput);
new Function(userInput)();
JSON.parse(jsonString);
JS-18: Never innerHTML with user data
element.innerHTML = `<p>${userInput}</p>`;
element.textContent = userInput;
JS-19: Environment variables for secrets
const API_KEY = 'sk-abc123...';
require('dotenv').config();
const API_KEY = process.env.API_KEY;
if (!API_KEY) throw new Error('API_KEY required');
JS-20: Mask secrets in logs
console.log(`Token: ${token}`);
console.log(`Token: ${token.slice(0, 8)}...`);
JS-21: Be cautious with NODE_TLS_REJECT_UNAUTHORIZED
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
const agent = new https.Agent({ rejectUnauthorized: false });
JS-22: AbortController for cancellable fetch and timeouts
const data = await fetch(url).then(r => r.json());
async function fetchJSON(url, { signal, timeoutMs = 5000 } = {}) {
const ctrl = new AbortController();
const timer = setTimeout(() => ctrl.abort(new Error('timeout')), timeoutMs);
signal?.addEventListener('abort', () => ctrl.abort(signal.reason));
try {
const res = await fetch(url, { signal: ctrl.signal });
return await res.json();
} finally {
clearTimeout(timer);
}
}
Replaces the Promise.race timeout pattern in modern Node.
JS-23: crypto.randomBytes for tokens, never Math.random
const token = Math.random().toString(36).slice(2);
const { randomBytes } = require('node:crypto');
const token = randomBytes(32).toString('hex');
Same rule for session IDs, password reset tokens, nonces, and salts.
Runtime Targets
- Node.js 24 LTS "Krypton" is the current Active LTS, supported through April 2028. Latest patch line is 24.15.x. New projects target Node 24.
- Release schedule change (announced Oct 2026 cycle): one major release per year (April), every release becomes LTS after the October promotion. Plan upgrades around April + October windows.
- Native TypeScript stripping is unflagged default since Node 23.6, available throughout 24.x.
--experimental-transform-types still required for enums/namespaces (which erasableSyntaxOnly in TS bans anyway).
JS-24a: Use stdlib over third-party where Node has shipped equivalents
| Replace | With (Node 22+) |
|---|
dotenv | node --env-file=.env, or process.loadEnvFile() |
node-fetch, axios (for simple cases) | global fetch, Request, Response, Headers |
ws (server) | global WebSocket (client) / node:ws patterns |
mocha, jest (for libs without React) | node:test + node --test |
glob | fs.glob / fs.globSync |
better-sqlite3 (for simple use) | node:sqlite |
Smaller node_modules, less supply-chain surface, fewer compatibility bugs across runtimes.
JS-24b: --permission for sandboxed processes
node --permission \
--allow-fs-read=/app \
--allow-fs-write=/app/tmp \
--allow-net=api.example.com \
server.js
Restricts what filesystem paths and network targets the process can touch. Useful for plugin runners, untrusted code, or hardening prod entrypoints. Still flagged experimental in Node 24, treat as defense-in-depth.
JS-25: Stream large I/O, do not buffer
const data = await fs.promises.readFile('huge.csv');
process(data);
const { pipeline } = require('node:stream/promises');
const { createReadStream, createWriteStream } = require('node:fs');
await pipeline(
createReadStream('huge.csv'),
parseCsv(),
transform(),
createWriteStream('out.json'),
);
Anything over a few MB or coming from the network should stream.
Vulnerability Checklist