Address AI review findings: - Bug: restore 30 s periodic eviction via setInterval so queries that are never repeated don't accumulate forever (add setInterval/ clearInterval to ESLint globals to allow it) - Bug: fix .finally() race – use identity check before deleting the in-flight key so a waiting request's replacement promise is never evicted by the original promise's cleanup handler - Warning: replace `new URL(c.req.url).search` with a string-split approach that cannot throw on relative URLs - Warning: add MAX_CACHE_ENTRIES (500) cap with LRU-oldest eviction to bound memory growth from callers with many unique variable sets - Warning: prefix cache key with c.req.path so /graphql and / can never produce cross-route cache collisions Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
102 lines
3 KiB
JavaScript
102 lines
3 KiB
JavaScript
import eslint from '@eslint/js';
|
|
import tseslint from '@typescript-eslint/eslint-plugin';
|
|
import tsparser from '@typescript-eslint/parser';
|
|
import eslintConfigPrettier from 'eslint-config-prettier';
|
|
|
|
export default [
|
|
eslint.configs.recommended,
|
|
{
|
|
files: ['**/*.ts'],
|
|
languageOptions: {
|
|
parser: tsparser,
|
|
parserOptions: {
|
|
project: './tsconfig.json',
|
|
tsconfigRootDir: import.meta.dirname,
|
|
},
|
|
globals: {
|
|
process: 'readonly',
|
|
console: 'readonly',
|
|
Context: 'readonly',
|
|
setInterval: 'readonly',
|
|
clearInterval: 'readonly',
|
|
},
|
|
},
|
|
plugins: {
|
|
'@typescript-eslint': tseslint,
|
|
},
|
|
rules: {
|
|
// TypeScript
|
|
'@typescript-eslint/no-explicit-any': 'error',
|
|
'@typescript-eslint/no-unused-vars': [
|
|
'error',
|
|
{
|
|
argsIgnorePattern: '^_',
|
|
varsIgnorePattern: '^_',
|
|
},
|
|
],
|
|
'@typescript-eslint/naming-convention': [
|
|
'error',
|
|
{
|
|
selector: 'function',
|
|
format: ['camelCase'],
|
|
},
|
|
{
|
|
selector: 'variable',
|
|
format: ['camelCase', 'UPPER_CASE'],
|
|
},
|
|
{
|
|
selector: 'typeLike',
|
|
format: ['PascalCase'],
|
|
},
|
|
],
|
|
|
|
// Style
|
|
'prefer-const': 'error',
|
|
indent: ['error', 2, { SwitchCase: 1 }],
|
|
'max-len': [
|
|
'error',
|
|
{
|
|
code: 140,
|
|
ignoreStrings: true,
|
|
ignoreTemplateLiterals: true,
|
|
ignoreUrls: true,
|
|
},
|
|
],
|
|
|
|
// Console
|
|
'no-console': 'error',
|
|
|
|
// Complexity (off)
|
|
complexity: 'off',
|
|
'max-depth': 'off',
|
|
'max-nested-callbacks': 'off',
|
|
'max-params': 'off',
|
|
'max-statements': 'off',
|
|
},
|
|
},
|
|
{
|
|
name: 'arch/ponder-bounded-queries',
|
|
rules: {
|
|
'no-restricted-syntax': [
|
|
'error',
|
|
{
|
|
selector: "CallExpression[callee.property.name='findMany']:not(:has(Property[key.name='limit']))",
|
|
message:
|
|
"Ponder findMany() called without a limit parameter. Unbounded queries will grow without limit as the chain is indexed — never use findMany() without a limit. Always specify `limit` in the options object. Use the ring buffer pattern from docs/ARCHITECTURE.md.",
|
|
},
|
|
{
|
|
selector: "CallExpression[callee.property.name='waitForTimeout']",
|
|
message:
|
|
'[BANNED] waitForTimeout is a fixed delay. → Subscribe to events instead (eth_newFilter for on-chain, waitForSelector/waitForURL for DOM). → Polling with timeout is acceptable only if no event source exists. → See AGENTS.md #Engineering Principles.',
|
|
},
|
|
{
|
|
selector:
|
|
"NewExpression[callee.name='Promise'] > ArrowFunctionExpression CallExpression[callee.name='setTimeout']",
|
|
message:
|
|
'[BANNED] Promise+setTimeout sleep pattern. → Use event subscription or polling with timeout instead. → See AGENTS.md #Engineering Principles.',
|
|
},
|
|
],
|
|
},
|
|
},
|
|
eslintConfigPrettier,
|
|
];
|