@sarj/eslint-plugin 2.16.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/rules/enforce-file-structure.ts","../src/rules/_paths.ts","../src/rules/no-client-side-data-fetching.ts","../src/rules/no-comment-cruft.ts","../src/rules/_comments.ts","../src/rules/no-enum.ts","../src/rules/no-insecure-random-id.ts","../src/rules/no-json-stringify-error.ts","../src/rules/no-log-only-catch.ts","../src/rules/_logging.ts","../src/rules/no-raw-env.ts","../src/rules/no-sentinel-return-on-catch.ts","../src/rules/no-sequential-await.ts","../src/rules/no-string-concat-in-loop.ts","../src/rules/no-unnecessary-use-client.ts","../src/rules/prefer-discriminated-union.ts","../src/rules/prefer-schema-for-api-payload.ts","../src/rules/prefer-semantic-colors.ts","../src/rules/_tailwind.ts","../src/rules/prefer-server-actions.ts","../src/rules/prefer-shadcn.ts","../src/rules/require-assert-never.ts","../src/rules/require-zod-form-validation.ts","../src/rules/_zod.ts","../src/rules/zod-naming-convention.ts","../src/rules/no-cors-wildcard-with-credentials.ts","../src/rules/no-silent-promise-catch.ts","../src/rules/require-fetch-timeout.ts","../src/rules/require-schema-validate-search.ts","../src/rules/no-fat-try-blocks.ts","../src/rules/no-secret-in-log.ts","../src/rules/_secret_names.ts","../src/rules/no-unsafe-cast.ts","../src/rules/prefer-string-literal-union.ts","../src/rules/single-public-export.ts","../src/rules/no-offset-pagination.ts","../src/rules/_sql.ts","../src/rules/no-positional-tuple-return.ts","../src/rules/no-repeated-string-literal.ts","../src/rules/no-select-star.ts","../src/rules/no-sleep-in-test-body.ts","../src/rules/prefer-constant-time-secret-compare.ts","../src/rules/store-insert-requires-on-conflict.ts","../src/rules/no-dynamic-sql.ts","../src/rules/no-raw-fetch-outside-clients.ts","../src/rules/no-storage-in-stateless-modules.ts","../src/rules/no-zod-native-enum.ts","../src/rules/prefer-module-level-constant.ts","../src/rules/jsdoc-restates-signature.ts","../src/rules/no-restated-comment.ts","../src/rules/trailing-value-narration.ts","../src/rules/no-tautological-expect.ts","../src/rules/require-interface-for-injected-service.ts","../src/rules/prefer-non-nullable-collection.ts","../src/rules/primary-export-file-name.ts","../src/rules/no-implicit-attribute-access.ts"],"sourcesContent":["import enforceFileStructure from \"./rules/enforce-file-structure.js\";\nimport noClientSideDataFetching from \"./rules/no-client-side-data-fetching.js\";\nimport noCommentCruft from \"./rules/no-comment-cruft.js\";\nimport noEnum from \"./rules/no-enum.js\";\nimport noInsecureRandomId from \"./rules/no-insecure-random-id.js\";\nimport noJsonStringifyError from \"./rules/no-json-stringify-error.js\";\nimport noLogOnlyCatch from \"./rules/no-log-only-catch.js\";\nimport noRawEnv from \"./rules/no-raw-env.js\";\nimport noSentinelReturnOnCatch from \"./rules/no-sentinel-return-on-catch.js\";\nimport noSequentialAwait from \"./rules/no-sequential-await.js\";\nimport noStringConcatInLoop from \"./rules/no-string-concat-in-loop.js\";\nimport noUnnecessaryUseClient from \"./rules/no-unnecessary-use-client.js\";\nimport preferDiscriminatedUnion from \"./rules/prefer-discriminated-union.js\";\nimport preferSchemaForApiPayload from \"./rules/prefer-schema-for-api-payload.js\";\nimport preferSemanticColors from \"./rules/prefer-semantic-colors.js\";\nimport preferServerActions from \"./rules/prefer-server-actions.js\";\nimport preferShadcn from \"./rules/prefer-shadcn.js\";\nimport requireAssertNever from \"./rules/require-assert-never.js\";\nimport requireZodFormValidation from \"./rules/require-zod-form-validation.js\";\nimport zodNamingConvention from \"./rules/zod-naming-convention.js\";\nimport noCorsWildcardWithCredentials from \"./rules/no-cors-wildcard-with-credentials.js\";\nimport noSilentPromiseCatch from \"./rules/no-silent-promise-catch.js\";\nimport requireFetchTimeout from \"./rules/require-fetch-timeout.js\";\nimport requireSchemaValidateSearch from \"./rules/require-schema-validate-search.js\";\nimport noFatTryBlocks from \"./rules/no-fat-try-blocks.js\";\nimport noSecretInLog from \"./rules/no-secret-in-log.js\";\nimport noUnsafeCast from \"./rules/no-unsafe-cast.js\";\nimport preferStringLiteralUnion from \"./rules/prefer-string-literal-union.js\";\nimport singlePublicExport from \"./rules/single-public-export.js\";\nimport noOffsetPagination from \"./rules/no-offset-pagination.js\";\nimport noPositionalTupleReturn from \"./rules/no-positional-tuple-return.js\";\nimport noRepeatedStringLiteral from \"./rules/no-repeated-string-literal.js\";\nimport noSelectStar from \"./rules/no-select-star.js\";\nimport noSleepInTestBody from \"./rules/no-sleep-in-test-body.js\";\nimport preferConstantTimeSecretCompare from \"./rules/prefer-constant-time-secret-compare.js\";\nimport storeInsertRequiresOnConflict from \"./rules/store-insert-requires-on-conflict.js\";\nimport noDynamicSql from \"./rules/no-dynamic-sql.js\";\nimport noRawFetchOutsideClients from \"./rules/no-raw-fetch-outside-clients.js\";\nimport noStorageInStatelessModules from \"./rules/no-storage-in-stateless-modules.js\";\nimport noZodNativeEnum from \"./rules/no-zod-native-enum.js\";\nimport preferModuleLevelConstant from \"./rules/prefer-module-level-constant.js\";\nimport jsdocRestatesSignature from \"./rules/jsdoc-restates-signature.js\";\nimport noRestatedComment from \"./rules/no-restated-comment.js\";\nimport trailingValueNarration from \"./rules/trailing-value-narration.js\";\nimport noTautologicalExpect from \"./rules/no-tautological-expect.js\";\nimport requireInterfaceForInjectedService from \"./rules/require-interface-for-injected-service.js\";\nimport preferNonNullableCollection from \"./rules/prefer-non-nullable-collection.js\";\nimport primaryExportFileName from \"./rules/primary-export-file-name.js\";\nimport noImplicitAttributeAccess from \"./rules/no-implicit-attribute-access.js\";\n\nconst rules = {\n \"enforce-file-structure\": enforceFileStructure,\n \"no-client-side-data-fetching\": noClientSideDataFetching,\n \"no-comment-cruft\": noCommentCruft,\n \"no-enum\": noEnum,\n \"no-insecure-random-id\": noInsecureRandomId,\n \"no-json-stringify-error\": noJsonStringifyError,\n \"no-log-only-catch\": noLogOnlyCatch,\n \"no-raw-env\": noRawEnv,\n \"no-sentinel-return-on-catch\": noSentinelReturnOnCatch,\n \"no-sequential-await\": noSequentialAwait,\n \"no-string-concat-in-loop\": noStringConcatInLoop,\n \"no-unnecessary-use-client\": noUnnecessaryUseClient,\n \"prefer-discriminated-union\": preferDiscriminatedUnion,\n \"prefer-schema-for-api-payload\": preferSchemaForApiPayload,\n \"prefer-semantic-colors\": preferSemanticColors,\n \"prefer-server-actions\": preferServerActions,\n \"prefer-shadcn\": preferShadcn,\n \"require-assert-never\": requireAssertNever,\n \"require-zod-form-validation\": requireZodFormValidation,\n \"zod-naming-convention\": zodNamingConvention,\n \"no-cors-wildcard-with-credentials\": noCorsWildcardWithCredentials,\n \"no-fat-try-blocks\": noFatTryBlocks,\n \"no-secret-in-log\": noSecretInLog,\n \"no-unsafe-cast\": noUnsafeCast,\n \"prefer-string-literal-union\": preferStringLiteralUnion,\n \"single-public-export\": singlePublicExport,\n \"primary-export-file-name\": primaryExportFileName,\n \"no-silent-promise-catch\": noSilentPromiseCatch,\n \"require-fetch-timeout\": requireFetchTimeout,\n \"require-schema-validate-search\": requireSchemaValidateSearch,\n \"no-offset-pagination\": noOffsetPagination,\n \"no-positional-tuple-return\": noPositionalTupleReturn,\n \"no-repeated-string-literal\": noRepeatedStringLiteral,\n \"no-select-star\": noSelectStar,\n \"no-sleep-in-test-body\": noSleepInTestBody,\n \"prefer-constant-time-secret-compare\": preferConstantTimeSecretCompare,\n \"store-insert-requires-on-conflict\": storeInsertRequiresOnConflict,\n \"no-dynamic-sql\": noDynamicSql,\n \"no-raw-fetch-outside-clients\": noRawFetchOutsideClients,\n \"no-storage-in-stateless-modules\": noStorageInStatelessModules,\n \"no-zod-native-enum\": noZodNativeEnum,\n \"prefer-module-level-constant\": preferModuleLevelConstant,\n \"jsdoc-restates-signature\": jsdocRestatesSignature,\n \"no-restated-comment\": noRestatedComment,\n \"trailing-value-narration\": trailingValueNarration,\n \"no-tautological-expect\": noTautologicalExpect,\n \"require-interface-for-injected-service\": requireInterfaceForInjectedService,\n \"prefer-non-nullable-collection\": preferNonNullableCollection,\n \"no-implicit-attribute-access\": noImplicitAttributeAccess,\n};\n\nconst plugin = {\n meta: {\n name: \"@sarj/eslint-plugin\",\n version: \"2.16.0\",\n },\n rules,\n configs: {\n recommended: {\n plugins: [\"@sarj\"],\n rules: {\n \"@sarj/zod-naming-convention\": \"warn\",\n \"@sarj/require-assert-never\": \"error\",\n \"@sarj/require-zod-form-validation\": \"error\",\n \"@sarj/enforce-file-structure\": \"warn\",\n \"@sarj/no-client-side-data-fetching\": \"warn\",\n \"@sarj/prefer-server-actions\": \"warn\",\n \"@sarj/no-unnecessary-use-client\": \"warn\",\n \"@sarj/prefer-schema-for-api-payload\": \"warn\",\n // Distilled from sarj-audit skills — warn in recommended, error in strict.\n \"@sarj/no-sequential-await\": \"warn\",\n \"@sarj/no-sentinel-return-on-catch\": \"warn\",\n \"@sarj/no-log-only-catch\": \"warn\",\n \"@sarj/no-insecure-random-id\": \"warn\",\n \"@sarj/no-json-stringify-error\": \"warn\",\n \"@sarj/no-string-concat-in-loop\": \"warn\",\n \"@sarj/prefer-discriminated-union\": \"warn\",\n \"@sarj/no-comment-cruft\": \"warn\",\n // Frontend / styling — distilled from frontend PR-review mining.\n \"@sarj/prefer-semantic-colors\": [\n \"warn\",\n { requireSemanticTokens: true },\n ],\n // Ported from sarj-python-lint (SARJ), corpus-validated FP~0.\n \"@sarj/no-fat-try-blocks\": \"warn\",\n \"@sarj/no-cors-wildcard-with-credentials\": \"warn\",\n \"@sarj/no-secret-in-log\": \"warn\",\n \"@sarj/no-unsafe-cast\": \"warn\",\n \"@sarj/single-public-export\": \"warn\",\n \"@sarj/primary-export-file-name\": \"warn\",\n \"@sarj/prefer-string-literal-union\": \"warn\",\n // Mined from 2y of PR review feedback + 5-repo code-smell audit (2026-07).\n \"@sarj/require-fetch-timeout\": \"warn\",\n \"@sarj/no-silent-promise-catch\": \"warn\",\n \"@sarj/require-schema-validate-search\": \"warn\",\n // Second SARJ port wave — the TS/Python parity gap. Each targets a\n // defect class seen in production Workers code: timing-leaky secret\n // compares, non-idempotent store writes under queue redelivery,\n // O(N) pagination, implicit row contracts, flaky timed tests.\n \"@sarj/prefer-constant-time-secret-compare\": \"error\",\n \"@sarj/store-insert-requires-on-conflict\": \"warn\",\n \"@sarj/no-offset-pagination\": \"warn\",\n \"@sarj/no-select-star\": \"warn\",\n \"@sarj/no-sleep-in-test-body\": \"warn\",\n \"@sarj/no-repeated-string-literal\": \"warn\",\n \"@sarj/no-positional-tuple-return\": \"warn\",\n // Injection guard — low FP, applies to any repo touching SQL.\n \"@sarj/no-dynamic-sql\": \"warn\",\n // Mined from two years of PR review (SARJ-928). Schema-layer sibling of\n // `no-enum`; autofixable for inline string-literal objects.\n \"@sarj/no-zod-native-enum\": \"warn\",\n // Mined from two years of PR review — the single most frequent uncovered\n // theme (~37 PRs). Measured 17 hits / 1085 real TS files, all true\n // positives, so it is safe to run everywhere.\n \"@sarj/prefer-module-level-constant\": \"warn\",\n // Anti-comment-verbosity family (2026-07), from a 37,918-comment,\n // nine-repo measurement study. Each is a deletion-class finding, so each\n // was validated against pydantic / trio / attrs as well as the maintained\n // repos: `no-restated-comment` 0 hits in bulbul and 4 in the three famous\n // corpora combined; `trailing-value-narration` 18 hits, 18 true\n // positives; `jsdoc-restates-signature` 36 hits, 0 measured false\n // positives, and it offers a suggestion rather than a `--fix` because a\n // wrong deletion is silent information loss.\n \"@sarj/no-restated-comment\": \"warn\",\n \"@sarj/jsdoc-restates-signature\": \"warn\",\n \"@sarj/trailing-value-narration\": \"warn\",\n // The TS half of SARJ057 (2026-07). Python has caught the\n // assertion-FREE test since 0.15.0 (SARJ043) and had no TS\n // counterpart, which is how `expect(true).toBe(true); // placeholder`\n // survived in internal-automations: the file HAS an assertion.\n // Measured across 5,819 .ts/.tsx files (1,003 of them test files) in\n // six internal repos plus got / hono / swr / trpc: 3 hits, 3 true\n // positives, 0 false positives.\n \"@sarj/no-tautological-expect\": \"warn\",\n // Substitutability: an exported service class with injected\n // collaborators and no interface above it can only be tested by\n // mocking. 11-repo sweep: 229 exported classes, 82% already carry a\n // port, 29 fire, 28 of them true positives.\n \"@sarj/require-interface-for-injected-service\": \"warn\",\n \"@sarj/prefer-non-nullable-collection\": \"warn\",\n \"@sarj/no-implicit-attribute-access\": \"warn\",\n },\n },\n strict: {\n plugins: [\"@sarj\"],\n rules: {\n \"@sarj/zod-naming-convention\": \"error\",\n \"@sarj/require-assert-never\": \"error\",\n \"@sarj/require-zod-form-validation\": \"error\",\n \"@sarj/enforce-file-structure\": \"error\",\n \"@sarj/no-raw-env\": \"error\",\n \"@sarj/prefer-shadcn\": \"error\",\n \"@sarj/no-enum\": \"error\",\n \"@sarj/no-client-side-data-fetching\": \"error\",\n \"@sarj/prefer-server-actions\": \"error\",\n \"@sarj/no-unnecessary-use-client\": \"error\",\n \"@sarj/prefer-schema-for-api-payload\": \"error\",\n // Distilled from sarj-audit skills.\n \"@sarj/no-sequential-await\": \"error\",\n \"@sarj/no-sentinel-return-on-catch\": \"error\",\n \"@sarj/no-log-only-catch\": \"error\",\n \"@sarj/no-insecure-random-id\": \"error\",\n \"@sarj/no-json-stringify-error\": \"error\",\n \"@sarj/no-string-concat-in-loop\": \"error\",\n \"@sarj/prefer-discriminated-union\": \"error\",\n \"@sarj/no-comment-cruft\": \"error\",\n // Frontend / styling — distilled from frontend PR-review mining. Stylistic,\n // no autofix → warn (rollout should prove the FP rate before raising it).\n \"@sarj/prefer-semantic-colors\": [\n \"error\",\n { requireSemanticTokens: true },\n ],\n // Ported from sarj-python-lint (SARJ), corpus-validated FP~0.\n \"@sarj/no-fat-try-blocks\": \"error\",\n \"@sarj/no-cors-wildcard-with-credentials\": \"error\",\n \"@sarj/no-secret-in-log\": \"error\",\n \"@sarj/no-unsafe-cast\": \"error\",\n \"@sarj/single-public-export\": \"error\",\n \"@sarj/primary-export-file-name\": \"error\",\n // Promoted to error 2026-07-25 — strict means strict (user directive).\n \"@sarj/prefer-string-literal-union\": \"error\",\n // Mined from 2y of PR review feedback + 5-repo code-smell audit (2026-07).\n \"@sarj/require-fetch-timeout\": \"error\",\n \"@sarj/no-silent-promise-catch\": \"error\",\n \"@sarj/require-schema-validate-search\": \"error\",\n // Second SARJ port wave — the TS/Python parity gap.\n \"@sarj/prefer-constant-time-secret-compare\": \"error\",\n \"@sarj/store-insert-requires-on-conflict\": \"error\",\n \"@sarj/no-offset-pagination\": \"error\",\n \"@sarj/no-select-star\": \"error\",\n \"@sarj/no-sleep-in-test-body\": \"error\",\n \"@sarj/no-repeated-string-literal\": \"error\",\n // API-shape advice rather than a runtime defect — a corpus sweep found its\n // only hits are parser `[value, cursor]` returns, which are conventional.\n // Warn even in strict until a rollout justifies more.\n \"@sarj/no-positional-tuple-return\": \"error\",\n \"@sarj/no-dynamic-sql\": \"error\",\n // Architectural: both need per-repo config to be meaningful, so they\n // are strict-only. `no-storage-in-stateless-modules` is a no-op until\n // its `modules` option names the directories a team declared stateless;\n // `no-raw-fetch-outside-clients` defaults to the `clients/` convention\n // and takes an `allow` list for repos that lay their client layer out\n // differently.\n \"@sarj/no-raw-fetch-outside-clients\": \"error\",\n \"@sarj/no-storage-in-stateless-modules\": \"error\",\n // Mined from two years of PR review (SARJ-928).\n \"@sarj/no-zod-native-enum\": \"error\",\n \"@sarj/prefer-module-level-constant\": \"error\",\n // Anti-comment-verbosity family (2026-07) — see the `recommended` block\n // for the measured hit counts and false-positive rates.\n \"@sarj/no-restated-comment\": \"error\",\n \"@sarj/jsdoc-restates-signature\": \"error\",\n \"@sarj/trailing-value-narration\": \"error\",\n // TS half of SARJ057 — see the `recommended` block for the measurement.\n \"@sarj/no-tautological-expect\": \"error\",\n // Substitutability: the TS sibling of the Python `prefer-real-store-in-tests`\n // / `prefer-library-fake` wave. The convention already exists in the\n // corpus (175 `implements` clauses vs 29 hits), so strict enforces it.\n \"@sarj/require-interface-for-injected-service\": \"error\",\n \"@sarj/prefer-non-nullable-collection\": \"error\",\n \"@sarj/no-implicit-attribute-access\": \"error\",\n },\n },\n },\n};\n\nexport default plugin;\nexport { rules };\n","/**\n * @fileoverview Require imports to come first, then allow step-down ordering for\n * the rest of the file, and require a `use server` directive to be the first\n * statement.\n *\n * Reporting is per-DEFECT, not per-import: see the `inMisplacedRun` note in\n * `create` for the corpus measurement behind that.\n */\n\nimport { ESLintUtils, type TSESTree, AST_NODE_TYPES } from \"@typescript-eslint/utils\";\n\nimport { isGeneratedFile, isTestFile } from \"./_paths.js\";\n\ntype MessageIds = \"importsFirst\" | \"useServerDirective\";\ntype Options = readonly [];\n\ntype StatementKind = \"import\" | \"reexport\" | \"body\";\n\n/**\n * Classify a top-level statement by WHAT it introduces, not by the presence of\n * the `export` keyword.\n *\n * - `import ...` → \"import\"\n * - `export ... from`, `export * from`, → \"reexport\"\n * `export { a, b }` (local names)\n * - everything else, INCLUDING every → \"body\"\n * exported declaration/function\n *\n * Bucketing exported statements as \"body\" is the whole point: an exported\n * `interface`/`type`/`enum`/`class`/value-`const` is a declaration and an\n * exported `function` (or `export default <fn>`) is a function — both live in\n * the same body as their non-exported equivalents. That lets the dominant\n * step-down layout (public API first, private helpers below) pass instead of\n * forcing every `export`-prefixed statement into a terminal \"exports\" section.\n *\n * Re-exports are their own neutral group: a generated `_namespaces` barrel that\n * interleaves `import * as X` / `export { X }` must not be flagged, so\n * re-exports never trigger and are allowed anywhere in the file.\n */\nconst classifyStatement = (\n statement: TSESTree.ProgramStatement,\n): StatementKind => {\n switch (statement.type) {\n case AST_NODE_TYPES.ImportDeclaration:\n return \"import\";\n case AST_NODE_TYPES.ExportAllDeclaration:\n return \"reexport\";\n case AST_NODE_TYPES.ExportNamedDeclaration:\n // `export { a } from './x'` / `export { a, b }` re-export names without\n // declaring anything; only `export <decl>` introduces a body statement.\n return statement.declaration === null ? \"reexport\" : \"body\";\n default:\n return \"body\";\n }\n};\n\nconst isStringDirective = (statement: TSESTree.ProgramStatement): boolean =>\n statement.type === AST_NODE_TYPES.ExpressionStatement &&\n statement.expression.type === AST_NODE_TYPES.Literal &&\n typeof statement.expression.value === \"string\" &&\n statement.expression.value.startsWith(\"use \");\n\nconst isUseServerDirective = (\n statement: TSESTree.ProgramStatement,\n): boolean => {\n if (statement.type !== AST_NODE_TYPES.ExpressionStatement) return false;\n const expr = statement.expression;\n if (expr.type !== AST_NODE_TYPES.Literal) return false;\n return expr.value === \"use server\";\n};\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"enforce-file-structure\",\n meta: {\n type: \"suggestion\",\n docs: {\n description:\n \"Require `import` statements to come first, then allow step-down ordering (public API first, private helpers below) for the rest of the file. Exported statements are classified by WHAT they export — an exported interface is a declaration, an exported function is a function — so a public exported function followed by a private helper, or an exported interface among declarations, is allowed. Re-exports (`export { … } from`, `export *`, `export { … }`) are a neutral group, so generated namespace barrels pass. When a module contains a `use server` directive, it must be the first statement in the file.\",\n },\n schema: [],\n messages: {\n importsFirst:\n \"File structure violation: import statements must come before other declarations\",\n useServerDirective:\n \"A 'use server' directive must be the first statement in the file\",\n },\n },\n defaultOptions: [],\n create(context) {\n if (isTestFile(context.filename) || isGeneratedFile(context.filename, context.sourceCode.text)) {\n return {};\n }\n\n return {\n Program(node: TSESTree.Program): void {\n const body = node.body;\n\n const misplacedUseServer = body.find(\n (statement, index) => index > 0 && isUseServerDirective(statement),\n );\n if (misplacedUseServer !== undefined) {\n context.report({\n node: misplacedUseServer,\n messageId: \"useServerDirective\",\n });\n }\n\n let seenBody = false;\n // One misplacement is one defect. A single interleaved statement pushes\n // every later import \"after the body\", and reporting each of them turns\n // one `const nodeRequire = createRequire(...)` between two import blocks\n // into 18 messages — measured at\n // react-router/packages/react-router-dev/vite/plugin.ts:41, which alone\n // produced 18 of the 33 corpus hits. Report the head of each contiguous\n // run of misplaced imports instead, so a file with two separate\n // interleavings still gets two messages.\n let inMisplacedRun = false;\n\n for (const statement of body) {\n if (isStringDirective(statement)) continue;\n\n switch (classifyStatement(statement)) {\n case \"reexport\":\n continue;\n case \"body\":\n seenBody = true;\n inMisplacedRun = false;\n continue;\n case \"import\":\n if (seenBody && !inMisplacedRun) {\n inMisplacedRun = true;\n context.report({\n node: statement,\n messageId: \"importsFirst\",\n });\n }\n continue;\n }\n }\n },\n };\n },\n});\n","/**\n * @fileoverview Shared file-path predicates for rules that are scoped by file\n * kind rather than by syntax — a timing-attack surface only exists in\n * production auth code, fixture SQL in a test legitimately breaks the\n * store-write conventions, and a dev script legitimately skips production\n * hardening. Answering \"is this a test file?\" / \"is this a script?\" in exactly\n * one place (mirroring the Python `_paths.py` extraction) is what stops the\n * per-rule regexes from drifting apart again.\n */\n\nconst SCRIPT_FILE_RE = /([\\\\/]scripts[\\\\/])|(\\.mjs$)/;\nconst STORY_FILE_RE = /\\.stories\\.[cm]?[jt]sx?$/i;\nconst GENERATED_FILE_RE =\n /([\\\\/](?:generated|openapi-gen|graphql[\\\\/]types)[\\\\/])|(\\.gen\\.[cm]?[jt]sx?$)|(\\.generated\\.[cm]?[jt]sx?$)|(\\.d\\.[cm]?ts$)|(\\.types\\.[cm]?ts$)/;\nconst GENERATED_MARKER_RE =\n /(?:@generated\\b|generated (?:with|by)|generated (?:graphql )?types|do not edit(?: directly| manually)?)/i;\n\n/**\n * True for a test file: a `*.test.*` / `*.spec.*` basename, the `-test` /\n * `_test` suffix forms of the same, or anything under a `test/` / `tests/` /\n * `__tests__/` / `__mocks__/` / `fixtures/` directory.\n *\n * The `-test.ts` / `_test.ts` suffixes are recognised because the dot form\n * alone is not the universal convention, and missing them silently un-exempts a\n * whole repo's test tree. Measured while converging `no-repeated-string-literal`\n * with its Python twin (2026-07): react-router names every suite `*-test.ts`, and\n * ALL 5 of the rule's hits over the 2,186-file third-party corpus were in two of\n * them — `react-router/packages/react-router-dev/vite/remove-exports-test.ts`\n * (4 hits) and `route-chunks-test.ts` (1). Each is a source-transform fixture\n * (`\"export const keptExport_1 = () => {};…\"`) deliberately repeated so the\n * before/after assertions stay comparable — the exact category the test\n * exemption exists for. Python's `_paths.is_test_path` has always accepted\n * `*_test.py`; this closes the same gap on the TS side.\n */\nexport function isTestFile(filename: string): boolean {\n const normalized = filename.replaceAll(\"\\\\\", \"/\");\n const base = normalized.slice(normalized.lastIndexOf(\"/\") + 1);\n if (/[.\\-_](test|spec|e2e)\\.[cm]?[jt]sx?$/.test(base) || /\\.integration\\.[cm]?[jt]sx?$/.test(base)) {\n return true;\n }\n return /(^|\\/)(tests?|__tests__|__mocks__|fixtures|e2e|integration)\\//.test(normalized);\n}\n\nexport function isStoryFile(filename: string): boolean {\n return STORY_FILE_RE.test(filename);\n}\n\nexport function isGeneratedFile(filename: string, sourceText = \"\"): boolean {\n return GENERATED_FILE_RE.test(filename.replaceAll(\"\\\\\", \"/\")) || GENERATED_MARKER_RE.test(sourceText.slice(0, 2048));\n}\n\n/**\n * True for one-off tooling files: anything under a `scripts/` directory or a\n * `*.mjs` file. Dev scripts run interactively and die with the terminal, so\n * production-hardening rules (e.g. fetch timeouts) do not apply there.\n */\nexport function isScriptFile(filename: string): boolean {\n return SCRIPT_FILE_RE.test(filename);\n}\n","/**\n * @fileoverview Disallow data fetching inside `useEffect` / `useLayoutEffect`.\n *\n * Anti-pattern:\n *\n * useEffect(() => { fetch('/data').then(setData); }, []);\n *\n * Causes a client-side waterfall (render → effect → fetch → re-render),\n * forfeits server-side caching, and produces layout shift. In Next.js App\n * Router, prefer:\n * - a React Server Component that fetches at render time, or\n * - a Server Action invoked from a form / onClick handler, or\n * - client-side caching libraries like SWR or React Query.\n *\n * Detection covers:\n * - `fetch(...)` (default + when explicit `method: \"GET\"`)\n * - `axios.<method>(...)` for actual HTTP verbs only:\n * get / post / put / delete / patch / request / head / options\n * (i.e. NOT `axios.create` / `axios.defaults`)\n * - `ky.<method>(...)` / `superagent.<method>(...)` (same verbs)\n * - bare `axios(...)` / `ky(...)` calls (when treated as a GET)\n *\n * Analytics / telemetry endpoints whose URL has a whole path segment of\n * `track` / `log` / `ping` / `event` / ... are intentionally exempt because\n * they aren't render-blocking data fetches. (Matched per-segment, so\n * `/api/login`, `/blog`, `/api/events`, `/catalog`, `/api/shipping` are NOT\n * exempt.)\n *\n * References:\n * - https://nextjs.org/docs/app/building-your-application/data-fetching\n * - https://react.dev/reference/react/useEffect#fetching-data-with-effects\n */\n\nimport {\n AST_NODE_TYPES,\n ESLintUtils,\n type TSESTree,\n} from \"@typescript-eslint/utils\";\n\ntype MessageIds = \"noClientFetch\";\ntype Options = readonly [];\n\nconst FETCH_LIBS: ReadonlySet<string> = new Set([\"axios\", \"ky\", \"superagent\"]);\n\n// Real HTTP verbs only. Explicitly excludes `create`, `defaults`,\n// `interceptors`, `isAxiosError`, etc.\nconst HTTP_METHOD_NAMES: ReadonlySet<string> = new Set([\n \"get\",\n \"post\",\n \"put\",\n \"delete\",\n \"patch\",\n \"request\",\n \"head\",\n \"options\",\n]);\n\n// Matched against whole path SEGMENTS (split on `/` and `.`), never as raw\n// substrings — otherwise `/api/login` (\"log\"), `/blog` (\"log\"), `/api/events`\n// (\"event\"), `/catalog` (\"log\"), and `/api/shipping` (\"ping\") would be wrongly\n// exempted.\nconst ANALYTICS_SEGMENTS: ReadonlySet<string> = new Set([\n \"analytics\",\n \"telemetry\",\n \"track\",\n \"log\",\n \"ping\",\n \"beacon\",\n \"metrics\",\n \"event\",\n]);\n\nfunction isEffectHookCall(node: TSESTree.CallExpression): boolean {\n const callee = node.callee;\n\n // useEffect(...) / useLayoutEffect(...)\n if (callee.type === AST_NODE_TYPES.Identifier) {\n return callee.name === \"useEffect\" || callee.name === \"useLayoutEffect\";\n }\n\n // React.useEffect(...) / React.useLayoutEffect(...)\n if (\n callee.type === AST_NODE_TYPES.MemberExpression &&\n !callee.computed &&\n callee.object.type === AST_NODE_TYPES.Identifier &&\n callee.object.name === \"React\" &&\n callee.property.type === AST_NODE_TYPES.Identifier\n ) {\n return (\n callee.property.name === \"useEffect\" ||\n callee.property.name === \"useLayoutEffect\"\n );\n }\n\n return false;\n}\n\n/**\n * Reads the `method` property of an options object passed to `fetch` / `axios`.\n * Returns the uppercased method name, or `null` if not statically determinable.\n */\nfunction readMethodProperty(\n optionsArg: TSESTree.Node | undefined,\n): string | null {\n if (!optionsArg || optionsArg.type !== AST_NODE_TYPES.ObjectExpression) {\n return null;\n }\n for (const prop of optionsArg.properties) {\n if (prop.type !== AST_NODE_TYPES.Property) continue;\n if (prop.computed) continue;\n const key = prop.key;\n const matchesMethodKey =\n (key.type === AST_NODE_TYPES.Identifier && key.name === \"method\") ||\n (key.type === AST_NODE_TYPES.Literal && key.value === \"method\");\n if (!matchesMethodKey) continue;\n if (\n prop.value.type === AST_NODE_TYPES.Literal &&\n typeof prop.value.value === \"string\"\n ) {\n return prop.value.value.toUpperCase();\n }\n return null;\n }\n return null;\n}\n\nfunction isFetchCall(node: TSESTree.CallExpression): boolean {\n const callee = node.callee;\n\n // fetch(url, options?)\n if (\n callee.type === AST_NODE_TYPES.Identifier &&\n callee.name === \"fetch\"\n ) {\n const method = readMethodProperty(node.arguments[1]);\n if (method !== null && method !== \"GET\") {\n return false;\n }\n return true;\n }\n\n // axios.get(...), ky.post(...), superagent.delete(...), ...\n if (\n callee.type === AST_NODE_TYPES.MemberExpression &&\n !callee.computed &&\n callee.object.type === AST_NODE_TYPES.Identifier &&\n FETCH_LIBS.has(callee.object.name) &&\n callee.property.type === AST_NODE_TYPES.Identifier\n ) {\n // Only flag actual HTTP method calls — NOT `axios.create`, `axios.defaults`,\n // `axios.interceptors`, `axios.isAxiosError`, etc.\n return HTTP_METHOD_NAMES.has(callee.property.name);\n }\n\n // axios(config) / ky(config) — treat as request unless method is explicitly non-GET.\n if (\n callee.type === AST_NODE_TYPES.Identifier &&\n (callee.name === \"axios\" || callee.name === \"ky\")\n ) {\n const firstArg = node.arguments[0];\n const secondArg = node.arguments[1];\n let configArg: TSESTree.Node | undefined;\n if (firstArg?.type === AST_NODE_TYPES.ObjectExpression) {\n configArg = firstArg;\n } else if (secondArg?.type === AST_NODE_TYPES.ObjectExpression) {\n configArg = secondArg;\n }\n const method = readMethodProperty(configArg);\n if (method !== null && method !== \"GET\") {\n return false;\n }\n return true;\n }\n\n return false;\n}\n\nfunction extractUrlString(node: TSESTree.CallExpression): string {\n const firstArg = node.arguments[0];\n if (!firstArg) return \"\";\n\n if (\n firstArg.type === AST_NODE_TYPES.Literal &&\n typeof firstArg.value === \"string\"\n ) {\n return firstArg.value;\n }\n if (firstArg.type === AST_NODE_TYPES.TemplateLiteral) {\n return firstArg.quasis.map((q) => q.value.cooked).join(\"\");\n }\n if (firstArg.type === AST_NODE_TYPES.Identifier) {\n return firstArg.name;\n }\n return \"\";\n}\n\nfunction isAnalyticsCall(node: TSESTree.CallExpression): boolean {\n const url = extractUrlString(node).toLowerCase();\n if (url === \"\") return false;\n // Split into path segments and file-extension parts; exempt only when a\n // WHOLE segment is a known analytics keyword.\n return url\n .split(/[/.]/)\n .some((segment) => ANALYTICS_SEGMENTS.has(segment));\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-client-side-data-fetching\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Disallow data fetching inside `useEffect` / `useLayoutEffect`; prefer React Server Components, Server Actions, or a client-side cache (SWR / React Query).\",\n },\n schema: [],\n messages: {\n noClientFetch:\n \"Avoid direct data fetching inside useEffect / useLayoutEffect. This causes waterfalls and layout shifts. Prefer React Server Components, Server Actions, or client-side caching libraries like SWR or React Query.\",\n },\n },\n defaultOptions: [],\n create(context) {\n let effectDepth = 0;\n return {\n CallExpression(node: TSESTree.CallExpression): void {\n if (isEffectHookCall(node)) {\n effectDepth += 1;\n return;\n }\n if (effectDepth === 0) return;\n if (!isFetchCall(node)) return;\n if (isAnalyticsCall(node)) return;\n context.report({ node, messageId: \"noClientFetch\" });\n },\n \"CallExpression:exit\"(node: TSESTree.CallExpression): void {\n if (isEffectHookCall(node)) {\n effectDepth -= 1;\n }\n },\n };\n },\n});\n","/**\n * @fileoverview Flag comment cruft — commented-out code, section banners,\n * leading file-header comment preambles, and redundant narration. Code carries\n * the *what*; comments are reserved for the *why*. JSDoc (`/** ... *\\/`) is\n * never flagged, and directive comments (`eslint-`, `@ts-`, `prettier-`,\n * `biome-`, `c8`, `<reference`, `TODO`, `FIXME`) are ignored.\n *\n * `redundantNarration` covers three shapes: step markers (\"First, …\", \"Step 2:\"),\n * self-admitted meta-commentary (\"for now\", \"temporary hack\"), and a comment\n * that restates the statement directly below it (`// increment the counter`\n * above `counter += 1`). The third shape is heavily guarded — see\n * `restatesNextLine` — because the first attempt at it (PR #98) corroborated by\n * substring and produced 933 hits at a ~60% false-positive rate. Measured on\n * 7,159 local TS/TSX files: 42 hits, 2 of them wrong (a comment heading a block\n * whose first statement happened to carry every word), and ZERO hits in the\n * maintained repos — it is a preventive ratchet with no migration cost.\n *\n * `fileHeaderPreamble` requires the preamble to contain NO prose sentence. The\n * original \"4+ consecutive `//` lines before the first code\" test penalised\n * syntax rather than content: on a real 42k-LOC codebase, 11 of 15 hits were the\n * repo's BEST documentation — module headers explaining a stateless idempotency\n * substrate, one citing RFC 9562 §5.7 — which is precisely the \"brief doc\n * comment for the why\" this rule's own message asks for. Exactly one hit was\n * genuine (an ASCII banner, already covered by `sectionBanner`). What survives\n * is the content-free preamble: a stack of bare labels/fragments with nothing\n * explained. A prose header should be a JSDoc block for tooling reasons, but\n * that is a formatting preference, not cruft, and this rule does not litigate it.\n *\n * A 2026-07 sweep of 2,186 real TypeScript files (zod, TanStack Query,\n * react-router, swr, zustand) produced 821 hits and turned up four false-positive\n * classes, each now guarded at the point that produced it — see\n * `DIAGRAM_ARROW_RE` (ASCII timelines read as banners), `ENUMERATED_ITEM_RE` (a\n * numbered walkthrough read as a content-free preamble), `isInsideCommentRun` (a\n * phrase inside a prose paragraph read as a one-line narration label), and\n * `hasIllustrationLeadInAbove` (a code sample read as commented-out code because\n * its `Example:` heading was more than one line up). The bulk of the remaining\n * hits are real: zod alone carries hundreds of lines of genuinely commented-out\n * code (e.g. zod/packages/zod/src/v4/core/checks.ts:1231-1244, a whole disabled\n * `$ZodCheckTrim` implementation), and those still fire.\n *\n * A 2026-07 corpus run of the SHIPPED rule turned up two live false positives,\n * both fixed here: `REGION_RE` matched the bare word `region`, so prose opening\n * with it read as a folding marker (six sites, e.g. `// Region centroids for\n * map_pan.`), and `for now` fired on a ticket-bearing scoping note — a comment\n * naming where the decision is recorded is doing the one thing code cannot, so\n * protected-class signal S1 now exempts narration at RUN granularity. The same\n * pass added `sarj-noqa` to `DIRECTIVE_RE` (this repo's own suppression syntax\n * was missing, so a suppression comment could itself be flagged) and four\n * detectors that are all ZERO-hit on bulbul, the repo that runs this rule at\n * `error`: bare section labels, the `Helper function to …` opener, `Let's\n * <verb>`, Unicode box-drawing banners, and an ISOLATED numbered/`Phase N:`\n * marker. JSX-expression comments stay categorically exempt: `{/* Step 1:\n * Select Patient *\\/}` mirrors the literal step labels a wizard renders.\n */\n\nimport { AST_NODE_TYPES, ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\nimport { hasExternalReference, restatableStatementBelow, restatesStatementHead } from \"./_comments.js\";\nimport { isGeneratedFile } from \"./_paths.js\";\n\ntype MessageIds =\n | \"commentedOutCode\"\n | \"sectionBanner\"\n | \"fileHeaderPreamble\"\n | \"redundantNarration\"\n | \"untrackedTodo\";\ntype Options = readonly [];\n\nconst LEADING_PREAMBLE_MIN = 4;\n\n// Step-narration lead-ins (\"First, …\", \"Then, …\", \"Finally, …\", \"Step 2:\"). A\n// trailing comma/colon is required so English adverbs (\"finally the invariant\n// holds\") aren't mistaken for an enumeration marker.\nconst STEP_NARRATION_RE =\n /^(?:first(?:ly)?|second(?:ly)?|third(?:ly)?|then|next|after(?:wards| that)?|finally|lastly|now)\\s*[,:]\\s*\\S/i;\n\n// Self-admitted meta-commentary — the \"why later\", not the why. `TODO`/`FIXME`/\n// `HACK`/`XXX` are handled as directives (kept, with an owner, per convention).\nconst META_COMMENTARY_RE =\n /\\b(?:for now|keeping (?:it|this) simple|could be (?:refactored|improved|cleaned up|simplified)|refactor(?:ed|ing)? (?:later|this)|not sure (?:if|whether|why|how)|quick[- ](?:and[- ]dirty|fix)|(?:a |bit of a )?hacky|is a hack|temporary (?:solution|workaround|fix|hack)|revisit (?:this|later|below)|clean (?:this|it) up|not ideal|placeholder for now)\\b/i;\n\n\n// `sarj-noqa` is this repo's own suppression syntax (see `rule_base.py`). It was\n// missing here, so `// sarj-noqa: … — <reason>` on its own line was read as\n// prose and could itself be flagged.\nconst DIRECTIVE_RE =\n /^(eslint\\b|eslint-|sarj-noqa\\b|@ts-|prettier-ignore|prettier\\b|biome-|c8\\b|v8\\b|istanbul\\b|@type\\b|@vite|webpack|<reference|<amd|global\\b|noinspection|hack\\b|xxx\\b)/i;\n\nconst LICENSE_RE =\n /copyright|licen[cs]ed?|spdx|permission is hereby granted|all rights reserved/i;\n\n// An enumerated step in a numbered/bulleted plan: `1. get all tags`, `- read the\n// manifest`. A stack of these is an algorithm walkthrough — the \"why/how\" the\n// rule's own message asks for — even though no item ends in a full stop, which\n// is why `isProse` misses it.\nconst ENUMERATED_ITEM_RE = /^(?:\\d+[.):]|[-*•])\\s+\\S/;\nconst ENUMERATED_ITEM_MIN_WORDS = 3;\nconst ENUMERATED_PREAMBLE_MIN_ITEMS = 2;\n\nconst BANNER_FULL_RE = /^[\\s\\-=*#~_+.]{4,}$/;\n// `={4,}` not `={3,}`: `===` is TS strict-equality and appears in prose comments.\n// `[\\u2500-\\u257f]` is the Unicode box-drawing block — `────────` is the same\n// section separator as `--------`, and 34 of them were sitting in the corpus\n// under a check that only knew ASCII.\nconst BANNER_RUN_RE = /={4,}|-{4,}|#{4,}|\\*{4,}|~{4,}|[\\u2500-\\u257f]{4,}/;\n\n// A VS Code / Visual Studio folding marker: `//#region`, `// region helpers`,\n// `// endregion`. The title must be short and unpunctuated. Matching the bare\n// word alone flagged running prose that merely opens with it — six sites across\n// the corpus, the clearest being\n// demo-gateway/demos/momah-furas-anas/pipeline/matching.py:159 (\"region, sector\n// AND facility_type are HARD constraints when the investor names them — …\") and\n// its five TypeScript siblings. A marker *names* a region; a sentence discusses\n// one, and a sentence has punctuation (a full stop included — `// Region\n// centroids for map_pan.` is prose) and more than a handful of words.\nconst REGION_MARKER_RE = /^#?(?:end)?region\\b(.*)$/i;\nconst REGION_TITLE_RE = /^[\\s:\\-\\u2013\\u2014]*\\w[\\w \\-/&+]*$/;\nconst REGION_TITLE_MAX_WORDS = 5;\n\nfunction isRegionMarker(text: string): boolean {\n const match = REGION_MARKER_RE.exec(text);\n if (match === null) return false;\n const title = (match[1] ?? \"\").trim();\n if (title.length === 0) return true;\n if (!REGION_TITLE_RE.test(title)) return false;\n return title.split(/\\s+/).length <= REGION_TITLE_MAX_WORDS;\n}\n\n// A bare one-word signpost naming a region of the file (`// Types`, `// Main`,\n// `// Helpers`). It is a table of contents for a file that should have been\n// split, and it goes stale silently. 22 corpus hits, 12 of 12 sampled were true\n// positives. Closed vocabulary on purpose: a one-word comment outside this list\n// is far more likely to be a genuine label for a value.\nconst SECTION_LABEL_WORDS: ReadonlySet<string> = new Set([\n \"actions\", \"components\", \"config\", \"configuration\", \"constant\", \"constants\",\n \"enums\", \"exports\", \"fixtures\", \"getters\", \"globals\", \"handler\", \"handlers\",\n \"helper\", \"helpers\", \"hook\", \"hooks\", \"imports\", \"interfaces\", \"main\",\n \"mocks\", \"models\", \"mutations\", \"props\", \"queries\", \"reducers\", \"routes\",\n \"schemas\", \"selectors\", \"setters\", \"setup\", \"state\", \"styles\", \"teardown\",\n \"type\", \"types\", \"util\", \"utilities\", \"utils\",\n]);\nconst SECTION_LABEL_RE = /^([A-Za-z]+)\\s*:?\\s*$/;\n\nfunction isSectionLabel(text: string): boolean {\n const match = SECTION_LABEL_RE.exec(text);\n return match !== null && SECTION_LABEL_WORDS.has((match[1] ?? \"\").toLowerCase());\n}\n\n// \"Helper function to check if a path is active\" — the opener announces the\n// *category* of the thing below (which its declaration already states) and then\n// restates its name. 6 corpus hits, 6 true positives.\nconst HELPER_OPENER_RE = /^(?:a\\s+)?helper\\s+(?:function|method|component|hook|class|type|util(?:ity)?)\\b/i;\n\n// \"Let's not await the promise\" — the first-person-plural walkthrough voice.\n// Gated on the narration verb list because the third-person `lets` is a\n// different word doing real work: \"lets a same-day re-run find the message it\n// already posted\" explains a mechanism and must not be touched.\nconst LETS_RE =\n /^let'?s\\s+(?:not\\s+|just\\s+|now\\s+|first\\s+)?(?:add|append|assign|await|build|calculate|call|check|clear|close|compute|convert|copy|count|create|declare|decrement|define|delete|extract|fetch|filter|find|format|generate|get|handle|increment|init|initialise|initialize|insert|iterate|join|load|log|loop|map|merge|open|parse|print|process|push|read|remove|render|reset|return|save|send|set|setup|sort|split|start|stop|store|update|validate|wrap|write)(?:s|es|ed|ing)?\\b/i;\n\n// Enumeration markers that narrate a sequence: `// 1. Load the config`,\n// `// Phase 2: reconcile`. Flagged only when the file carries exactly one — a\n// *run* of them is a documented algorithm walkthrough, which is the kind of\n// comment this rule exists to protect. JSX-expression comments are exempt\n// wholesale (see `isStandalone`): `{/* Step 1: Select Patient */}` mirrors the\n// literal step labels of a UI wizard.\nconst ENUMERATION_RE = /^(?:\\d+[.)]\\s+\\S|(?:phase|step)\\s+\\d+\\b)/i;\n\n// Dummy translational comments: ultra-short comments that just restate the code.\nconst DUMMY_TRANSLATION_RE = /^(?:increment|return|returns|get|gets|set\\b(?! up\\b)|sets\\b(?! up\\b)|function to|method to)\\b/i;\n\n// An ASCII sequence-diagram arrow. A long rule of dashes that ENDS IN AN ARROW\n// HEAD is drawing a timeline, not separating sections: `req------->res` is the\n// clearest documentation of a race condition anyone has written, and deleting it\n// loses information no function extraction can recover. Measured on 2,186 real\n// TypeScript files (zod / TanStack Query / react-router / swr / zustand): 8 of\n// the 83 section-banner hits were one such diagram, swr/src/index/use-swr.ts:524\n// through :549, explaining request/mutation interleaving. A real banner\n// (`// ---------- Checks ----------`,\n// react-router/scripts/pr.ts:157) has no arrow head and still fires.\nconst DIAGRAM_ARROW_RE = /[-=~]{2,}>|<[-=~]{2,}/;\n\nconst CODE_KEYWORD_RE =\n /^(import |export |const |let |var |function\\b|class |interface |type \\w|enum |return\\b|throw |await |async |if\\s*\\(|for\\s*\\(|while\\s*\\(|switch\\s*\\(|new |console\\.)/;\nconst CODE_TAIL_RE = /[;{}()]\\s*$|=>\\s*$|,\\s*$/;\n// LHS must be a real identifier (not a number literal — `0=Monday` in prose is\n// not an assignment) and `=` must not be `==`/`===`/`=>` (comparison/arrow).\n// The assignment branch additionally requires a code-tail — the line must end\n// with `;`, `)`, `}` or `]` — so plain prose like `count = number of items`\n// (which has no code-tail) is not mistaken for commented-out code.\nconst CALL_OR_ASSIGN_RE =\n /^[A-Za-z_$][\\w.$[\\]]*\\s*(?:=(?![=>])|\\+=|-=|\\*=)\\s*\\S.*[;)}\\]]\\s*$|^[A-Za-z_$][\\w.$]*\\([^)]*\\)\\s*;?\\s*$/;\n\n// Placeholders that only appear in grammar productions / desugaring examples,\n// never in real code: `%sent%`, `[opt]`, a standalone `<FunctionBody>`, `…` / `...`.\nconst PSEUDOCODE_RE = /%\\w+%|\\[opt\\]|(?:^|\\s)<[A-Za-z]\\w*>|…|\\.\\.\\./;\n\n// A triple-slash `///` directive keeps its third `/` after ESLint strips the\n// leading `//`, so strip 1–2 leading slashes (not exactly two) for `<reference`.\nfunction stripCommentMarker(line: string): string {\n return line.replace(/^\\s*\\/{1,2}/, \"\").replace(/^\\s*\\*+/, \"\").trim();\n}\n\nfunction isDirective(text: string): boolean {\n return DIRECTIVE_RE.test(text.trim());\n}\n\nfunction isBanner(text: string): boolean {\n const t = text.trim();\n if (!t) return false;\n if (BANNER_FULL_RE.test(t) || isRegionMarker(t)) return true;\n return BANNER_RUN_RE.test(t) && !DIAGRAM_ARROW_RE.test(t);\n}\n\nfunction looksLikeCode(text: string): boolean {\n const t = text.trim();\n if (!t) return false;\n if (CODE_KEYWORD_RE.test(t) && CODE_TAIL_RE.test(t)) return true;\n return CALL_OR_ASSIGN_RE.test(t);\n}\n\nfunction hasPseudocode(text: string): boolean {\n return PSEUDOCODE_RE.test(text);\n}\n\n/** An explanatory item of a numbered/bulleted walkthrough. */\nfunction isEnumeratedProseItem(text: string): boolean {\n const t = text.trim();\n return ENUMERATED_ITEM_RE.test(t) && t.split(/\\s+/).length >= ENUMERATED_ITEM_MIN_WORDS;\n}\n\n// A prose lead-in preceding a code-shaped line marks that line as an\n// illustration (`// For example:`, a grammar production `FunctionExpression:`),\n// not commented-out code.\nfunction isProse(text: string): boolean {\n const t = text.trim();\n if (!t) return false;\n if (t.endsWith(\":\")) return true;\n if (\n /[.!?]$/.test(t) &&\n /\\s/.test(t) &&\n /[a-z]/.test(t) &&\n !looksLikeCode(t) &&\n t.split(/\\s+/).length >= 3\n ) {\n return true;\n }\n return false;\n}\n\n// --- \"restates the next line\" ---------------------------------------------\n// A comment that opens with a narration verb and whose every remaining content\n// word already names what the statement below it computes says nothing the code\n// does not (`// increment the counter` / `counter += 1`). Three guards keep the\n// inference sound, because *coincidental* token overlap is the failure mode that\n// sank the first attempt at this shape (PR #98: `service` matching\n// `locationService` gave a ~60% false-positive rate):\n//\n// 1. Total corroboration — one unmatched word (`// guard the race from\n// PLT-812`) means the comment carries something the code does not, so it\n// stays.\n// 2. The comment must sit on top of a single-line, value-producing *statement*.\n// A comment above a block, a declaration, a type member or an object-literal\n// entry (`// store state` above `interface SessionState {`) labels a region\n// of code; the words it shares with the first line of that region are\n// incidental.\n// 3. Only the head of the statement counts — everything up to the first `(` —\n// so the comment must restate what the statement *computes* (its target and\n// its callee), not merely something it passes as an argument.\n\n// A causal connective. `for now` inside a sentence that also states WHY is a\n// justification, not an admission — `// Needed for now since router.fetch is not\n// async until v7` (react-router/.../__tests__/router/lazy-discovery-test.ts:2412,\n// and :2505) is the reason the sleep exists, which is exactly what the rule wants\n// a comment to carry. A bare `// quick fix for now` still fires.\nconst JUSTIFICATION_RE =\n /\\b(?:because|since|until|due to|so that|otherwise|which is why|in order to|to avoid|to work around|to prevent)\\b/i;\n\n/**\n * Whether a single-line comment merely narrates the code rather than explaining\n * the *why*. Three deterministic shapes: step narration (\"First, …\"), self-\n * admitted meta-commentary (\"keeping it simple\"), and a restatement of the very\n * next line (`// increment the counter` above `counter += 1`).\n *\n * `standalone` is false when the comment is one line of a contiguous `//` block.\n * The step and meta shapes are single-line tells; inside a paragraph they match a\n * clause of running prose rather than a label. Measured on 2,186 real TypeScript\n * files (zod / TanStack Query / react-router / swr / zustand), 9 of 42 narration\n * hits were exactly that — e.g. react-router/integration/bug-report-test.ts:26\n * (\"First, make sure to install dependencies and build React Router. From the\n * root of / the project, run this:\"), a six-line contributor instruction, and\n * react-router/packages/react-router/lib/dom/ssr/routes.tsx:663. A restatement of\n * the next line is still checked in a block, since it is corroborated against the\n * code rather than against a phrase.\n */\nfunction isRedundantNarration(\n body: string,\n statementBelow: string | null,\n standalone: boolean,\n isolatedEnumeration: boolean,\n nested: boolean,\n): boolean {\n const t = body.trim();\n if (!t || looksLikeCode(t) || hasPseudocode(t)) return false;\n if (standalone) {\n if (STEP_NARRATION_RE.test(t)) return true;\n if (META_COMMENTARY_RE.test(t) && !JUSTIFICATION_RE.test(t)) return true;\n if (HELPER_OPENER_RE.test(t) || LETS_RE.test(t)) return true;\n \n const words = t.split(/\\s+/);\n if (words.length <= 4 && DUMMY_TRANSLATION_RE.test(t) && !/[():=]/.test(t)) {\n const lowerT = t.toLowerCase();\n const rationaleWords = [\"when\", \"because\", \"if\", \"so that\", \"due to\", \"for\", \"instead of\", \"to prevent\", \"to avoid\", \"only\"];\n if (!rationaleWords.some(w => lowerT.includes(w))) {\n if (words.length > 1) return true;\n }\n }\n\n if (!nested && isSectionLabel(t)) return true;\n if (isolatedEnumeration && ENUMERATION_RE.test(t)) return true;\n }\n return restatesStatementHead(t, statementBelow);\n}\n\n// Statement-position containers. A comment whose innermost enclosing node is\n// anything else sits INSIDE an expression — an array, an object literal, a call\n// argument list — where a one-word label groups the elements beneath it rather\n// than signposting the file. `# config` inside pydantic's `__all__` is the\n// Python twin of this; both readings produce the same comment and only the\n// nesting tells them apart.\nconst STATEMENT_CONTAINERS: ReadonlySet<string> = new Set([\n AST_NODE_TYPES.Program,\n AST_NODE_TYPES.BlockStatement,\n AST_NODE_TYPES.ClassBody,\n AST_NODE_TYPES.StaticBlock,\n AST_NODE_TYPES.SwitchCase,\n AST_NODE_TYPES.TSModuleBlock,\n AST_NODE_TYPES.TSInterfaceBody,\n]);\n\n/**\n * True when the comment at `index` belongs to a contiguous `//` run in which\n * some line cites a ticket, URL, RFC or issue number (protected-class signal\n * S1, applied at run granularity).\n *\n * A scoping note puts its owner at the end — bulbul's Zoho-canary comment ends\n * \"EN-only for now — add an AR variant once AR audio exists (PROD-249)\" — so\n * judging the last line alone read \"for now\" as an unowned admission. A comment\n * that names where the decision is recorded is doing the one thing code cannot.\n */\nfunction runCitesAReference(comments: readonly TSESTree.Comment[], index: number): boolean {\n for (let i = index; i >= 0; i--) {\n if (i < index && !areAdjacentLineComments(comments[i], comments[i + 1])) break;\n if (hasExternalReference(stripCommentMarker(comments[i]?.value ?? \"\"))) return true;\n }\n for (let i = index + 1; i < comments.length; i++) {\n if (!areAdjacentLineComments(comments[i - 1], comments[i])) break;\n if (hasExternalReference(stripCommentMarker(comments[i]?.value ?? \"\"))) return true;\n }\n return false;\n}\n\n/** True when `a` and `b` are `//` comments on consecutive lines. */\nfunction areAdjacentLineComments(\n a: TSESTree.Comment | undefined,\n b: TSESTree.Comment | undefined,\n): boolean {\n return (\n a !== undefined &&\n b !== undefined &&\n a.type === \"Line\" &&\n b.type === \"Line\" &&\n b.loc.start.line === a.loc.end.line + 1\n );\n}\n\n/**\n * True when the comment at `index` is one line of a contiguous `//` block rather\n * than a lone annotation.\n */\nfunction isInsideCommentRun(comments: readonly TSESTree.Comment[], index: number): boolean {\n const comment = comments[index];\n return (\n areAdjacentLineComments(comments[index - 1], comment) ||\n areAdjacentLineComments(comment, comments[index + 1])\n );\n}\n\n/** How far back the illustration lead-in scan walks within one `//` block. */\nconst LEAD_IN_SCAN_LIMIT = 24;\n\n/**\n * True when a prose lead-in (`// Example:`, `// For example:`, a grammar\n * production head) appears earlier in the SAME contiguous `//` block. The\n * existing check only looked at the immediately preceding comment, so a code\n * illustration more than one line below its own heading was read as\n * commented-out code — measured at\n * react-router/packages/react-router/lib/hooks.tsx:791, where `// function\n * Blog() {` sits nine lines under its `// Example:` heading inside one 17-line\n * block (2 hits there of the 695 commented-out-code hits over 2,186 files).\n */\nfunction hasIllustrationLeadInAbove(\n comments: readonly TSESTree.Comment[],\n index: number,\n): boolean {\n for (let i = index - 1; i >= 0 && index - i <= LEAD_IN_SCAN_LIMIT; i--) {\n if (!areAdjacentLineComments(comments[i], comments[i + 1])) return false;\n const body = stripCommentMarker(comments[i]?.value ?? \"\");\n if (body.length > 0 && body.endsWith(\":\")) return true;\n }\n return false;\n}\n\nfunction hasCommentedOutCode(\n texts: readonly string[],\n precedingProse: boolean,\n): boolean {\n for (let i = 0; i < texts.length; i++) {\n const line = texts[i];\n if (line === undefined || !looksLikeCode(line) || hasPseudocode(line)) {\n continue;\n }\n const prev = i > 0 ? texts[i - 1] : undefined;\n if (prev !== undefined ? isProse(prev) : precedingProse) continue;\n return true;\n }\n return false;\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-comment-cruft\",\n meta: {\n type: \"suggestion\",\n docs: {\n description:\n \"Flag commented-out code, section-banner comments, and leading file-header comment preambles.\",\n },\n schema: [],\n messages: {\n commentedOutCode:\n \"Commented-out code — delete it; git history remembers.\",\n sectionBanner:\n \"Section-banner / region comment — structure code with functions, not ASCII rules.\",\n fileHeaderPreamble:\n \"File-header comment preamble — use a brief doc comment for the why, not a block of `//` lines.\",\n redundantNarration:\n \"Comment narrates the code — delete it or say *why*, not *what*. Code is self-documenting.\",\n untrackedTodo:\n \"Untracked TODO/FIXME marker — add an issue ticket or context link.\",\n },\n },\n defaultOptions: [],\n create(context) {\n if (isGeneratedFile(context.filename, context.sourceCode.text)) {\n return {};\n }\n\n const sourceCode = context.sourceCode;\n\n function isStandalone(comment: TSESTree.Comment): boolean {\n const before = sourceCode.getTokenBefore(comment, {\n includeComments: false,\n });\n return !before || before.loc.end.line < comment.loc.start.line;\n }\n\n function isJsDoc(comment: TSESTree.Comment): boolean {\n return comment.type === \"Block\" && /^\\*/.test(comment.value);\n }\n\n function reportLeadingPreamble(\n comments: readonly TSESTree.Comment[],\n firstCodeLine: number,\n ): void {\n const leading: TSESTree.Comment[] = [];\n let prevLine: number | null = null;\n for (const comment of comments) {\n if (comment.type !== \"Line\") break;\n if (comment.loc.start.line >= firstCodeLine) break;\n if (!isStandalone(comment)) break;\n const body = stripCommentMarker(comment.value);\n if (isDirective(body) || body.startsWith(\"!\")) continue;\n if (prevLine !== null && comment.loc.start.line !== prevLine + 1) break;\n leading.push(comment);\n prevLine = comment.loc.start.line;\n }\n const first = leading[0];\n if (first === undefined || leading.length < LEADING_PREAMBLE_MIN) return;\n const bodies = leading.map((c) => stripCommentMarker(c.value));\n if (bodies.some((body) => LICENSE_RE.test(body))) return;\n // A preamble carrying at least one prose sentence is documentation — the\n // \"why\" the rule wants — regardless of which comment syntax carries it.\n if (bodies.some((body) => isProse(body))) return;\n // Same for a numbered/bulleted walkthrough. The single corpus hit for this\n // message across 2,186 real TypeScript files was\n // react-router/scripts/release-comments.ts:1, a six-step description of\n // what the script does (\"1. get all tags sorted by creation date\", …) —\n // documentation, not a stack of content-free labels.\n if (bodies.filter(isEnumeratedProseItem).length >= ENUMERATED_PREAMBLE_MIN_ITEMS) {\n return;\n }\n context.report({ node: first, messageId: \"fileHeaderPreamble\" });\n }\n\n return {\n Program(): void {\n const comments = sourceCode.getAllComments();\n const firstCodeLine =\n sourceCode.ast.tokens[0]?.loc.start.line ?? Number.MAX_SAFE_INTEGER;\n const enumerated = comments.filter(\n (c) => c.type === \"Line\" && ENUMERATION_RE.test(stripCommentMarker(c.value)),\n );\n\n for (let i = 0; i < comments.length; i++) {\n const comment = comments[i];\n if (comment === undefined) continue;\n if (isJsDoc(comment) || !isStandalone(comment)) continue;\n if (LICENSE_RE.test(comment.value)) continue;\n const texts = comment.value\n .split(\"\\n\")\n .map(stripCommentMarker)\n .filter((l) => l.length > 0 && !isDirective(l));\n\n const firstText = texts[0];\n if (firstText !== undefined && /^(?:todo|fixme)\\b/i.test(firstText)) {\n if (!runCitesAReference(comments, i)) {\n context.report({\n node: comment,\n messageId: \"untrackedTodo\",\n });\n }\n continue;\n }\n\n if (texts.some(isBanner)) {\n context.report({ node: comment, messageId: \"sectionBanner\" });\n continue;\n }\n const prev = comments[i - 1];\n const precedingProse =\n prev !== undefined &&\n prev.type === \"Line\" &&\n prev.loc.end.line === comment.loc.start.line - 1 &&\n isProse(stripCommentMarker(prev.value));\n if (\n hasCommentedOutCode(texts, precedingProse) &&\n !hasIllustrationLeadInAbove(comments, i)\n ) {\n context.report({ node: comment, messageId: \"commentedOutCode\" });\n continue;\n }\n // Narration only for single-line comments (a multi-line block is\n // usually a real doc).\n if (comment.type === \"Line\" && texts.length === 1) {\n const body = texts[0];\n const statement = restatableStatementBelow(comment, sourceCode);\n const standalone = !isInsideCommentRun(comments, i);\n const container = sourceCode.getNodeByRangeIndex(comment.range[0]);\n const nested = container !== null && !STATEMENT_CONTAINERS.has(container.type);\n if (\n body !== undefined &&\n !runCitesAReference(comments, i) &&\n isRedundantNarration(body, statement, standalone, enumerated.length === 1, nested)\n ) {\n context.report({ node: comment, messageId: \"redundantNarration\" });\n }\n }\n }\n\n reportLeadingPreamble(comments, firstCodeLine);\n },\n };\n },\n});\n","/**\n * @fileoverview Shared comment analysis for the comment-hygiene rules\n * (`no-comment-cruft`, `no-restated-comment`, `jsdoc-restates-signature`,\n * `trailing-value-narration`) — the TypeScript twin of the Python\n * `rules/_comments.py`, kept signal-for-signal identical so the two languages\n * cannot drift on what counts as a comment worth keeping.\n *\n * **The protected class.** Nine deterministic signals that mark a comment as\n * carrying something the code cannot: an external reference, a version pin, a\n * number with a unit, a causal connective, a negation of the obvious, an\n * upstream-quirk word, a concurrency/invariant term, security reasoning, or a\n * vendor proper noun with *ascribed behaviour*. Measured over a 37,918-comment\n * corpus from nine repos: the nine protect 40/40 hand-picked best comments and\n * leak ~1% of the hand-classified cruft list.\n *\n * The class is an **EXEMPTION FLOOR, never a test**. `isProtected` returning\n * false says nothing about a comment — across pydantic / trio / attrs it matches\n * only 18-35% of the comments a human called valuable. Every use is of the form\n * \"if protected, do not flag\". Inverting it into \"unprotected, therefore delete\"\n * would condemn two thirds of the best comments in those libraries.\n */\n\nimport { AST_NODE_TYPES, type TSESTree } from \"@typescript-eslint/utils\";\n\n// S1 — external reference: URL, ticket key, RFC/PEP/CVE, bare issue number, or\n// an email/handle domain. Ticket keys allow letters after the first digit\n// (`PLATFORM-1YC`) and exclude the encoding acronyms sharing the shape.\nconst REF_RE =\n /https?:\\/\\/|\\bRFC[- ]?\\d+|\\bPEP[- ]?\\d+|\\bCVE-\\d{4}|\\b(?!UTF-|SHA-|ISO-|AES-|CRC-|MD-|PCM-|EOF-|API-|BASE-)[A-Z][A-Z0-9]{1,9}-\\d[A-Z0-9]{0,5}\\b|(?<![&\\w])#\\d{2,6}\\b|@[a-z][\\w.-]*\\.(?:us|com|ai|io|net|org|dev)\\b/;\n\n// S2 — version pin or comparison.\nconst VERSION_RE =\n /(?:>=|<=|==|<|>)\\s*v?\\d+\\.\\d+|\\bv\\d+\\.\\d+|\\b(?:since|until|as of)\\s+(?:v?\\d+\\.\\d+|Python\\s*\\d)/i;\n\n// S3 — a number carrying a unit, or an HTTP status code.\nconst UNITS_RE =\n /[~<>]?\\d+(?:\\.\\d+)?\\s?(?:ms|s\\b|sec\\b|seconds?\\b|min\\b|minutes?\\b|hours?\\b|days?\\b|KB|MB|MiB|GiB|kHz|Hz|bytes?\\b|bit\\b|-bit\\b|%|px\\b|rps\\b|qps\\b)|\\b[1-5]xx\\b|\\b(?:301|302|304|307|308|400|401|403|404|405|409|410|412|422|425|429|500|501|502|503|504)\\b/;\n\n// S4 — a causal connective tying behaviour to a consequence.\nconst CAUSAL_RE =\n /\\b(?:because|otherwise|so that|or else|would (?:break|fail|race|deadlock|leak|clobber|loop|crash|page|stall)|breaks?\\b|so we don'?t|to avoid\\b|caused\\b|causes\\b|gets? clobbered|keeps? (?:us|it|them) from|doesn'?t\\b.{0,24}\\b(?:page|fire|break|leak|loop)|eat into|would otherwise|trade-?offs?\\b)\\b/i;\n\n// S5 — negation of the obvious, or a flagged deliberate deviation.\nconst NEGATION_RE =\n /\\b(?:must not|must never|do(?:es)? not\\b|don'?t\\b.{0,30}\\b(?:leak|log|cache|retry|block|steal|wipe)|never\\b|deliberately|intentionally|counterintuitiv|NOT\\b)|(?<!based )\\bon purpose\\b|\\(not\\s|\\binstead of\\b|\\brather than\\b/;\n\n// S6 — upstream/vendor quirk, workaround provenance, or an external contract.\nconst UPSTREAM_RE =\n /\\b(?:upstream|workaround|quirk|backport|vendored|regression|fixed upstream|requires?\\b|convention\\b|rate.?limit|deprecat|opts? in(?:to)?\\b|raises?\\b.{0,60}\\b(?:when|if|unless)\\b)/i;\n\n// S7 — concurrency, ordering, or invariant vocabulary.\nconst INVARIANT_RE =\n /\\b(?:invariant|idempotent|race\\b|deadlock|re-?entran|atomic|thread-?safe|signal-?safe|lexicographic(?:al(?:ly)?)?|monotonic|must (?:run|be|happen|come|stay|hit|converge|configure)|before any\\b|lost the (?:claim )?race)\\b/i;\n\n// S8 — security reasoning.\nconst SECURITY_RE =\n /\\b(?:timing attack|constant-?time|replay|PII\\b|redact|secret|injection|spoof|fail-?closed|fail-?open|auth bypass|early-?exit timing)\\b/i;\n\n// S9 — a vendor proper noun with *ascribed behaviour*. A vendor name as the mere\n// object of a narration verb (\"Create the prompt for Gemini\") is NOT protected;\n// that distinction is what holds the leak rate at ~1%.\nconst VENDOR_RE =\n /\\b(?:GitHub|Slack|Twilio|LiveKit|Kamailio|Groq|OpenAI|Anthropic|Cloudflare|FastAPI|Starlette|Sentry|Zoho|Salla|Ashby|Linear|BigQuery|Postgres|Neon|Drizzle|Vertex|Gemini|Firestore|Stripe|Next\\.js|React Compiler|pydantic|ruff|loguru|Lexical|Farasa|Orpheus|Whisper|schemathesis)(?:'s\\b|\\s+(?:requires?|returns?|expects?|allows?|rejects?|accepts?|sends?|caps?|limits?|wraps?|silently|outputs?|stores?|treats?|doesn'?t|does not|won'?t|can'?t|only|models)\\b)/;\n\nconst PROTECTED_SIGNALS: readonly RegExp[] = [\n REF_RE,\n VERSION_RE,\n UNITS_RE,\n CAUSAL_RE,\n NEGATION_RE,\n UPSTREAM_RE,\n INVARIANT_RE,\n SECURITY_RE,\n VENDOR_RE,\n];\n\n/**\n * True when a comment carries any of the nine protected-class signals.\n *\n * EXEMPTION FLOOR ONLY — a false result is not evidence the comment is\n * worthless. See the file overview.\n */\nexport function isProtected(body: string): boolean {\n return PROTECTED_SIGNALS.some((signal) => signal.test(body));\n}\n\n/**\n * True when a comment cites a ticket, URL, RFC/PEP/CVE, or issue number\n * (signal S1 alone). Naming where a decision is recorded is the one thing code\n * cannot do, and it separates an owned scoping note (\"EN-only for now — AR needs\n * audio (PROD-249)\") from an unowned admission (\"hacky, fix later\").\n */\nexport function hasExternalReference(body: string): boolean {\n return REF_RE.test(body);\n}\n\n// --- tokenisation shared by the restatement detectors ----------------------\n\n/**\n * Words that say nothing about *which* code a comment describes. Kept in step\n * with `_comments.py`: shrinking the list costs recall, growing it costs\n * precision by discounting a genuinely novel word.\n */\nexport const STOPWORDS: ReadonlySet<string> = new Set(\n `a an the this that these those it its their his her our your my\n is are was were be been being am do does did done doing has have had having\n will would shall should can could may might must\n and or but nor so yet not no none\n to of for in on at by with from into onto out up down over under about\n as if then than when where which who whom whose what how why while\n we you they i he she them him us me\n also just only even still already again there here\n all any each every some\n via per etc eg ie vs\n need needs needed want wants make makes making let lets\n please note see above below`.split(/\\s+/),\n);\n\nconst CAMEL_PART_RE = /[A-Z]+(?=[A-Z][a-z])|[A-Z]?[a-z]+|[A-Z]+|\\d+/g;\nconst WORD_RE = /[A-Za-z_$][\\w$]*|\\d+/g;\n\n/** Split `snake_case` / `camelCase` / `SCREAMING_CASE` into lowercase parts. */\nexport function splitIdentifier(token: string): string[] {\n const parts: string[] = [];\n for (const chunk of token.split(/[_$]/)) {\n for (const match of chunk.match(CAMEL_PART_RE) ?? []) {\n parts.push(match.toLowerCase());\n }\n }\n return parts;\n}\n\n/**\n * Fold the common English inflections so `updates` / `updating` match `update`.\n *\n * The trailing-`e` strip is what makes the fold *symmetric*: without it\n * `creates` / `creating` reduce to `creat` while `create` stays `create`, and\n * the two never match — the shape that most often made a restatement look\n * novel. Deliberately crude otherwise; every extra conflation is a chance to\n * call a novel word a restatement.\n */\nexport function stem(word: string): string {\n let base = word;\n for (const suffix of [\"ing\", \"ied\", \"ies\", \"ers\", \"er\", \"ed\", \"es\", \"s\"]) {\n if (word.endsWith(suffix) && word.length - suffix.length >= 3) {\n base = word.slice(0, word.length - suffix.length);\n if (suffix === \"ied\" || suffix === \"ies\") return `${base}y`;\n break;\n }\n }\n return base.endsWith(\"e\") && base.length - 1 >= 3 ? base.slice(0, -1) : base;\n}\n\n/** Split prose into lowercase content words, dropping stopwords. */\nexport function contentTokens(text: string): string[] {\n const tokens: string[] = [];\n for (const match of text.match(WORD_RE) ?? []) {\n tokens.push(...splitIdentifier(match));\n }\n return tokens.filter((token) => !STOPWORDS.has(token));\n}\n\n/** Every identifier part appearing in a slice of source. */\nexport function codeTokens(text: string): Set<string> {\n const tokens = new Set<string>();\n for (const match of text.match(WORD_RE) ?? []) {\n for (const part of splitIdentifier(match)) tokens.add(part);\n }\n return tokens;\n}\n\n/**\n * True when every content token of a comment already appears in the code.\n *\n * Exact or stemmed match only. Prefix matching is deliberately absent: it is\n * what sank the first attempt at this shape (PR #98), where `service` matched\n * `locationService` and drove the false-positive rate to ~60%.\n */\nexport function restates(commentTokens: readonly string[], code: ReadonlySet<string>): boolean {\n const stems = new Set<string>();\n for (const token of code) stems.add(stem(token));\n return commentTokens.every((token) => code.has(token) || stems.has(stem(token)));\n}\n\n// --- the verb-opener restatement shape (shared with `no-comment-cruft`) ------\n\nconst NARRATION_MAX_WORDS = 6;\nconst NARRATION_MIN_CONTENT = 1;\nconst TOKEN_PLURAL_MIN = 4;\n\n// Statements that compute something a comment could be restating. A block, a\n// class/function/type declaration, an `if`, a `for` — anything whose body the\n// comment could be labelling instead — is deliberately absent.\nconst RESTATABLE_STATEMENTS: ReadonlySet<string> = new Set([\n AST_NODE_TYPES.ExpressionStatement,\n AST_NODE_TYPES.ReturnStatement,\n AST_NODE_TYPES.ThrowStatement,\n AST_NODE_TYPES.VariableDeclaration,\n]);\n\n// Verbs that describe the mechanics of the next statement. A comment opening\n// with anything else (a noun, \"because\", \"we\", a ticket id) is not narration.\nconst NARRATION_VERB_RE =\n /^(?:add|append|assign|build|calculate|call|check|clear|close|compute|convert|copy|count|create|declare|decrement|define|delete|extract|fetch|filter|find|format|generate|get|handle|increment|init|initialise|initialize|insert|iterate|join|load|log|loop|make|map|merge|open|parse|print|process|push|read|remove|render|reset|return|save|send|set|setup|sort|split|start|stop|store|update|validate|wrap|write)(?:s|es|d|ed|ing)?$/i;\n\n// Words that carry no information about *which* code the comment describes, so\n// they are not required to appear in the line below.\nconst NARRATION_STOPWORDS: ReadonlySet<string> = new Set([\n \"a\", \"all\", \"an\", \"and\", \"any\", \"are\", \"as\", \"at\", \"back\", \"be\", \"both\", \"by\",\n \"each\", \"for\", \"from\", \"here\", \"if\", \"in\", \"into\", \"is\", \"it\", \"its\", \"just\",\n \"new\", \"of\", \"on\", \"one\", \"onto\", \"or\", \"our\", \"out\", \"over\", \"so\", \"that\",\n \"the\", \"then\", \"this\", \"to\", \"up\", \"us\", \"we\", \"when\", \"with\",\n]);\n\n/** Lowercase, with a single plural `s` folded away so `users` matches `user`. */\nexport function normalizeToken(word: string): string {\n const lower = word.toLowerCase();\n return lower.length > TOKEN_PLURAL_MIN && lower.endsWith(\"s\") && !lower.endsWith(\"ss\")\n ? lower.slice(0, -1)\n : lower;\n}\n\n/** Every identifier in a slice of source, plus its parts, with plurals folded. */\nfunction headTokens(source: string): ReadonlySet<string> {\n const tokens = new Set<string>();\n for (const identifier of source.match(/[A-Za-z_$][\\w$]*/g) ?? []) {\n tokens.add(normalizeToken(identifier));\n for (const part of identifier.split(/[_$]+|(?<=[a-z0-9])(?=[A-Z])/)) {\n if (part.length > 0) tokens.add(normalizeToken(part));\n }\n }\n return tokens;\n}\n\n/** The slice of ESLint's `SourceCode` these shapes read. */\nexport interface StatementReader {\n getTokenAfter: (\n node: TSESTree.Comment,\n options: { includeComments: boolean },\n ) => TSESTree.Token | null;\n getNodeByRangeIndex: (index: number) => TSESTree.Node | null;\n getText: (node: TSESTree.Node) => string;\n}\n\n/** A declaration with nothing but a zero/empty seed computes nothing to restate. */\nfunction isTrivialInitializer(node: TSESTree.VariableDeclaration): boolean {\n return node.declarations.every((declarator) => {\n const init = declarator.init;\n if (init == null || init.type === AST_NODE_TYPES.Literal) return true;\n return (\n (init.type === AST_NODE_TYPES.ArrayExpression && init.elements.length === 0) ||\n (init.type === AST_NODE_TYPES.ObjectExpression && init.properties.length === 0)\n );\n });\n}\n\n/**\n * The source of the single-line statement a comment sits directly above, or null\n * when what follows is a block, a declaration, a type member or anything else\n * the comment could be *labelling* rather than restating.\n */\nexport function restatableStatementBelow(comment: TSESTree.Comment, sourceCode: StatementReader): string | null {\n const token = sourceCode.getTokenAfter(comment, { includeComments: false });\n if (token === null || token.loc.start.line !== comment.loc.end.line + 1) return null;\n for (\n let node: TSESTree.Node | undefined | null = sourceCode.getNodeByRangeIndex(token.range[0]);\n node != null && node.type !== AST_NODE_TYPES.Program;\n node = node.parent\n ) {\n if (!RESTATABLE_STATEMENTS.has(node.type)) continue;\n if (node.loc.start.line !== token.loc.start.line || node.loc.end.line !== node.loc.start.line) {\n return null;\n }\n if (node.type === AST_NODE_TYPES.VariableDeclaration && isTrivialInitializer(node)) {\n return null;\n }\n return sourceCode.getText(node);\n }\n return null;\n}\n\n/**\n * True when a short verb-led comment adds nothing to the statement beneath it:\n * every content word after the opening verb already appears in that statement's\n * head — its assignment target or its callee.\n */\nexport function restatesStatementHead(body: string, statement: string | null): boolean {\n if (statement === null) return false;\n const words = body.match(/[A-Za-z][\\w$]*/g) ?? [];\n const opener = words[0];\n if (opener === undefined || words.length > NARRATION_MAX_WORDS) return false;\n if (!NARRATION_VERB_RE.test(opener)) return false;\n const content = words\n .slice(1)\n .map(normalizeToken)\n .filter((word) => !NARRATION_STOPWORDS.has(word));\n if (content.length < NARRATION_MIN_CONTENT) return false;\n const head = statement.split(\"(\")[0] ?? statement;\n const code = headTokens(head);\n return content.every((word) => code.has(word));\n}\n","/**\n * @fileoverview Disallow TypeScript `enum` declarations. Use string-literal\n * union types (e.g. `type Status = \"active\" | \"inactive\"`) or `as const`\n * objects instead — enums generate runtime code, have unintuitive numeric\n * defaults, and don't tree-shake cleanly.\n *\n * Generated files can opt out either by living under a path matched by\n * `ignoreFiles` (default: `**\\/generated/**`, `**\\/*.gen.ts`, `**\\/*.generated.ts`)\n * or by including a `@generated` marker comment near the top of the file.\n */\n\nimport { ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\nimport { isGeneratedFile } from \"./_paths.js\";\n\ntype MessageIds = \"noEnum\";\ntype Options = readonly [\n {\n ignoreFiles?: readonly string[];\n }?,\n];\n\nconst DEFAULT_IGNORE_PATTERNS: readonly RegExp[] = [\n /[\\\\/]generated[\\\\/]/,\n /[\\\\/]openapi-gen[\\\\/]/,\n /\\.gen\\.tsx?$/,\n /\\.generated\\.tsx?$/,\n];\n\nfunction matchesAnyPattern(\n filename: string,\n patterns: readonly string[],\n): boolean {\n for (const pattern of patterns) {\n // Convert minimatch-ish globs to regex: ** -> .*, * -> [^/\\\\]*\n const regexSource = pattern\n .replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\")\n .replace(/\\*\\*/g, \"::DOUBLESTAR::\")\n .replace(/\\*/g, \"[^/\\\\\\\\]*\")\n .replace(/::DOUBLESTAR::/g, \".*\");\n if (new RegExp(`^${regexSource}$`).test(filename)) {\n return true;\n }\n }\n return false;\n}\n\nfunction hasGeneratedMarker(sourceText: string): boolean {\n // Look only in the first 1KB to keep this cheap.\n const head = sourceText.slice(0, 1024);\n return /@generated\\b/.test(head);\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-enum\",\n meta: {\n type: \"suggestion\",\n docs: {\n description:\n \"Disallow TypeScript `enum`; use string-literal unions or `as const` objects instead.\",\n },\n schema: [\n {\n type: \"object\",\n additionalProperties: false,\n properties: {\n ignoreFiles: {\n type: \"array\",\n items: { type: \"string\" },\n },\n },\n },\n ],\n messages: {\n noEnum:\n 'Enums are discouraged. Use a string-literal union (e.g. `type Status = \"active\" | \"inactive\"`) or an `as const` object instead.',\n },\n },\n defaultOptions: [{}],\n create(context, [optionsArg]) {\n const options = optionsArg ?? {};\n const ignoreFiles = options.ignoreFiles ?? [];\n const filename = context.filename;\n const sourceText = context.sourceCode.getText();\n\n const isIgnoredByDefault = DEFAULT_IGNORE_PATTERNS.some((re) =>\n re.test(filename),\n );\n const isIgnoredByOption =\n ignoreFiles.length > 0 && matchesAnyPattern(filename, ignoreFiles);\n const isGenerated = hasGeneratedMarker(sourceText) || isGeneratedFile(filename, sourceText);\n\n if (isIgnoredByDefault || isIgnoredByOption || isGenerated) {\n return {};\n }\n\n return {\n TSEnumDeclaration(node: TSESTree.TSEnumDeclaration): void {\n context.report({\n node,\n messageId: \"noEnum\",\n });\n },\n };\n },\n});\n","/**\n * @fileoverview Disallow using `Math.random()` to generate identifiers,\n * tokens, keys, or other security-sensitive values. `Math.random()` is not\n * cryptographically secure and is predictable — using it for tokens/secrets\n * can lead to collisions and trivially guessable values. Prefer\n * `crypto.randomUUID()` or `crypto.getRandomValues(...)` instead.\n *\n * Precision matters here: the rule runs at `error` in the `strict` config, so a\n * false positive blocks a real PR. Large open-source sweeps (VS Code, NestJS,\n * Next.js) showed the bare `id`/`key`/`session` substring heuristic firing on a\n * flood of NON-security correlation/ephemeral ids — temp-file suffixes, HMR\n * session numbers, dev request/execution/trace ids, in-process RPC handles.\n * None of those are security tokens. So the rule now requires a STRONG security\n * signal, and actively exempts names that read as ephemeral/correlation ids or\n * random values concatenated into a filename/path/DOM id.\n *\n * The rule flags a `Math.random()` call when, and only when:\n * 1. Its enclosing binding/property NAME carries a strong security signal\n * (`token`, `secret`, `apiKey`, `csrf`, `password`, `nonce`, `salt`,\n * `uuid`, `authId`) — even a `sessionToken` counts because of `token`; or\n * 2. Its result is fed into a `.toString(36)` chain (the classic insecure\n * random-id idiom, e.g. `Math.random().toString(36).slice(2)`) AND the\n * value is not an exempt ephemeral/correlation id or path fragment.\n *\n * It does NOT flag when the enclosing name signals a non-security\n * correlation/ephemeral id (`temp`, `tmp`, `cache`, `correlation`, `request`,\n * `req`, `trace`, `execution`, `dev`, `hmr`, `mock`, `test`, `perf`, `marker`),\n * nor when the random value is concatenated into a filename/path/DOM id. A\n * strong security name still wins over these exemptions.\n *\n * Bare `Math.random()` used for non-identifier purposes (jitter, sampling,\n * rolls, etc.) is NOT flagged, and neither is the bare `id`/`key`/`session`\n * substring on its own — we err toward suppressing ambiguous correlation ids.\n *\n * Test files are out of scope entirely. A fixture generator is not a token\n * mint, and the `.toString(36)` idiom is how tests cheaply produce distinct\n * keys. Measured on 2,186 real TypeScript files (zod / TanStack Query /\n * react-router / swr / zustand): 3 of 6 hits were exactly that —\n * zod/packages/zod/src/v3/tests/Mocker.ts:13 (a mock-data string generator) and\n * two in\n * react-router/packages/react-router/__tests__/vendor/turbo-stream-test.ts:215\n * (random map keys for a serialization round-trip). None is a credential.\n *\n * KNOWN GAP (false-negative): an arithmetic expression between `Math.random()`\n * and `.toString(36)` breaks the member-chain walk, e.g.\n * `(Math.random() * 1e9).toString(36)`. The intervening `BinaryExpression`\n * means `Math.random()` is no longer the object end of the `.toString` chain,\n * so trigger 2 does not fire. Such code is only caught if its binding/property\n * name looks security-like (trigger 1). See the documented test case.\n */\n\nimport { ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\nimport { isTestFile } from \"./_paths.js\";\n\ntype MessageIds = \"insecureRandomId\";\ntype Options = readonly [];\n\nconst STRONG_SECURITY_PATTERN =\n /token|secret|csrf|password|passwd|apikey|api[-_]?key|nonce|salt|uuid|authid/i;\n\nconst NON_SECURITY_ID_PATTERN =\n /temp|tmp|cache|correlation|request|req|trace|execution|dev|hmr|mock|test|perf|marker/i;\n\n/** Matches a static string fragment that reads as a path or DOM-id context. */\nconst PATH_OR_DOM_MARKER = /[\\\\/#]|\\.[A-Za-z0-9]/;\n\n/**\n * Returns true if `node` is a `Math.random()` CallExpression.\n */\nfunction isMathRandomCall(node: TSESTree.Node): node is TSESTree.CallExpression {\n if (node.type !== \"CallExpression\") {\n return false;\n }\n const callee = node.callee;\n if (callee.type !== \"MemberExpression\" || callee.computed) {\n return false;\n }\n const { object, property } = callee;\n return (\n object.type === \"Identifier\" &&\n object.name === \"Math\" &&\n property.type === \"Identifier\" &&\n property.name === \"random\"\n );\n}\n\n/**\n * Climbs the member/call chain in which `node` is the object/callee end, and\n * returns the outermost value node of that chain. For\n * `Math.random().toString(36).slice(2)` this returns the `.slice(2)` call —\n * i.e. the value that actually gets assigned/concatenated.\n */\nfunction climbValueChain(node: TSESTree.Node): TSESTree.Node {\n let current: TSESTree.Node = node;\n let parent = current.parent;\n\n while (parent) {\n if (\n parent.type === \"MemberExpression\" &&\n parent.object === current &&\n !parent.computed\n ) {\n current = parent;\n parent = current.parent;\n continue;\n }\n if (parent.type === \"CallExpression\" && parent.callee === current) {\n current = parent;\n parent = current.parent;\n continue;\n }\n break;\n }\n\n return current;\n}\n\n/**\n * Returns true if `node` (a `Math.random()` call) is the base of a member\n * chain that calls `.toString(36)` somewhere above it, e.g.\n * `Math.random().toString(36)` or `Math.random().toString(36).slice(2)`.\n */\nfunction isPartOfToString36Chain(node: TSESTree.Node): boolean {\n let current: TSESTree.Node = node;\n let parent = current.parent;\n\n while (parent) {\n if (\n parent.type === \"MemberExpression\" &&\n parent.object === current &&\n !parent.computed &&\n parent.property.type === \"Identifier\" &&\n parent.property.name === \"toString\"\n ) {\n const grandparent = parent.parent;\n if (\n grandparent &&\n grandparent.type === \"CallExpression\" &&\n grandparent.callee === parent\n ) {\n const firstArg = grandparent.arguments[0];\n if (firstArg && firstArg.type === \"Literal\" && firstArg.value === 36) {\n return true;\n }\n }\n }\n\n if (parent.type === \"MemberExpression\" && parent.object === current) {\n current = parent;\n parent = current.parent;\n continue;\n }\n if (parent.type === \"CallExpression\" && parent.callee === current) {\n current = parent;\n parent = current.parent;\n continue;\n }\n break;\n }\n\n return false;\n}\n\n/**\n * Walks up from `node` to find the name of the nearest enclosing binding\n * (VariableDeclarator id) or property (Property / PropertyDefinition key),\n * and returns it. Passes through string concatenation and template literals so\n * that `const tempPath = base + Math.random()...` resolves to `tempPath`.\n * Returns `undefined` if no such name is found before leaving the enclosing\n * initializer/value context.\n */\nfunction findEnclosingName(node: TSESTree.Node): string | undefined {\n let current: TSESTree.Node = node;\n let parent = current.parent;\n\n while (parent) {\n if (parent.type === \"VariableDeclarator\" && parent.init === current) {\n if (parent.id.type === \"Identifier\") {\n return parent.id.name;\n }\n return undefined;\n }\n\n if (parent.type === \"Property\" && parent.value === current) {\n const key = parent.key;\n if (!parent.computed && key.type === \"Identifier\") {\n return key.name;\n }\n if (key.type === \"Literal\" && typeof key.value === \"string\") {\n return key.value;\n }\n return undefined;\n }\n\n if (parent.type === \"PropertyDefinition\" && parent.value === current) {\n const key = parent.key;\n if (!parent.computed && key.type === \"Identifier\") {\n return key.name;\n }\n if (key.type === \"Literal\" && typeof key.value === \"string\") {\n return key.value;\n }\n return undefined;\n }\n\n if (\n parent.type === \"FunctionDeclaration\" ||\n parent.type === \"FunctionExpression\" ||\n parent.type === \"ArrowFunctionExpression\" ||\n parent.type === \"BlockStatement\" ||\n parent.type === \"ReturnStatement\" ||\n parent.type === \"ExpressionStatement\"\n ) {\n return undefined;\n }\n\n current = parent;\n parent = current.parent;\n }\n\n return undefined;\n}\n\n/**\n * Collects the static string fragments of a `+` concatenation / template\n * literal subtree into `out`.\n */\nfunction collectStaticStringParts(\n node: TSESTree.Node,\n out: string[],\n): void {\n if (node.type === \"Literal\" && typeof node.value === \"string\") {\n out.push(node.value);\n return;\n }\n if (node.type === \"TemplateLiteral\") {\n for (const quasi of node.quasis) {\n out.push(quasi.value.cooked ?? quasi.value.raw);\n }\n return;\n }\n if (node.type === \"BinaryExpression\" && node.operator === \"+\") {\n collectStaticStringParts(node.left, out);\n collectStaticStringParts(node.right, out);\n }\n}\n\n/**\n * Returns true if the random value is concatenated into a string whose static\n * parts read as a filename/path/DOM id (contain a slash, backslash, `#`, or a\n * `.ext`-style fragment).\n */\nfunction isConcatenatedIntoPathOrDomId(node: TSESTree.Node): boolean {\n const valueNode = climbValueChain(node);\n\n let current: TSESTree.Node = valueNode;\n let parent = current.parent;\n let top: TSESTree.Node | undefined;\n\n while (parent) {\n if (\n parent.type === \"BinaryExpression\" &&\n parent.operator === \"+\" &&\n (parent.left === current || parent.right === current)\n ) {\n top = parent;\n current = parent;\n parent = current.parent;\n continue;\n }\n if (parent.type === \"TemplateLiteral\") {\n top = parent;\n current = parent;\n parent = current.parent;\n continue;\n }\n break;\n }\n\n if (!top) {\n return false;\n }\n\n const parts: string[] = [];\n collectStaticStringParts(top, parts);\n return parts.some((part) => PATH_OR_DOM_MARKER.test(part));\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-insecure-random-id\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Disallow using `Math.random()` to generate identifiers, tokens, or secrets; use `crypto.randomUUID()` or `crypto.getRandomValues(...)` instead.\",\n },\n schema: [],\n messages: {\n insecureRandomId:\n \"`Math.random()` is not cryptographically secure and is predictable; do not use it to generate IDs, tokens, or secrets. Use `crypto.randomUUID()` or `crypto.getRandomValues(...)` instead.\",\n },\n },\n defaultOptions: [],\n create(context) {\n if (isTestFile(context.filename)) {\n return {};\n }\n return {\n CallExpression(node: TSESTree.CallExpression): void {\n if (!isMathRandomCall(node)) {\n return;\n }\n\n const name = findEnclosingName(node);\n\n // A strong security name always fires — even a `sessionToken` or a name\n // that also happens to contain a correlation-id word.\n if (name !== undefined && STRONG_SECURITY_PATTERN.test(name)) {\n context.report({ node, messageId: \"insecureRandomId\" });\n return;\n }\n\n // Ephemeral / correlation ids (temp files, HMR sessions, dev request\n // ids, ...) are not security-sensitive — suppress, including the\n // `.toString(36)` idiom used to shape them.\n if (name !== undefined && NON_SECURITY_ID_PATTERN.test(name)) {\n return;\n }\n\n // A random value concatenated into a filename/path/DOM id is not a\n // security token.\n if (isConcatenatedIntoPathOrDomId(node)) {\n return;\n }\n\n // The classic insecure random-id idiom.\n if (isPartOfToString36Chain(node)) {\n context.report({ node, messageId: \"insecureRandomId\" });\n }\n },\n };\n },\n});\n","/**\n * @fileoverview Disallow `JSON.stringify(err)` on a (heuristically detected)\n * Error value. `JSON.stringify` on an Error produces `\"{}\"` because the\n * `message` and `stack` properties are non-enumerable, silently throwing away\n * the very information you were trying to log.\n *\n * This is a purely syntactic rule (no type information). It flags\n * `JSON.stringify(x)` when the first argument is either:\n * 1. an Identifier that is the binding of an enclosing `catch (x)` clause, or\n * matches the conventional error-name pattern /^(e|err|error|ex|exc)$/i, OR\n * 2. a member expression denoting an error value (`err.cause`, `this.lastError`) —\n * an error-suggesting property name, or an error-named base whose property is\n * not a plain string accessor (`.message` / `.stack` / `.name`).\n *\n * It suppresses the report inside the non-error branch of an `x instanceof Error`\n * guard (`x instanceof Error ? x : JSON.stringify(x)`), where stringifying the\n * non-Error fallback is exactly correct.\n *\n * Object literals and arbitrary identifiers (`JSON.stringify(user)`) are not flagged.\n *\n * Rule (2) originally assumed that any property of an error-named base is itself\n * an error. It is not: the error objects real frameworks throw carry a plain,\n * fully-enumerable DATA payload alongside the non-enumerable `message`/`stack`.\n * A sweep of 2,186 real TypeScript files (zod / TanStack Query / react-router /\n * swr / zustand) hit exactly that at\n * react-router/playground/rsc-vite/src/routes/root/root.client.tsx:42 —\n * `JSON.stringify(error.data)` on a react-router `ErrorResponse`, where `.data`\n * is the loader's own JSON body and stringifying it is the correct thing to do.\n * `PAYLOAD_PROPS` lists those accessors (`data`, `status`, `issues`, ...)\n * alongside `SAFE_STRING_PROPS`; `err.cause` and friends still fire because the\n * property name itself names an error.\n */\n\nimport { ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\nimport type { Scope, SourceCode } from \"@typescript-eslint/utils/ts-eslint\";\n\ntype MessageIds = \"noJsonStringifyError\";\ntype Options = readonly [];\n\nconst ERROR_NAME_PATTERN = /^(e|err|error|ex|exc)$/i;\n\n/** Property names whose value is itself an error object (unsafe to stringify). */\nconst ERROR_PROP_PATTERN = /^(cause|lastError|error|err|exception|originalError|innerError)$/i;\n\n/** Property names whose value is a plain string — the recommended escape hatch. */\nconst SAFE_STRING_PROPS: ReadonlySet<string> = new Set([\"message\", \"stack\", \"name\"]);\n\n/**\n * Accessors that hold an error's *data* payload rather than a nested error.\n * These are ordinary enumerable JSON, so `JSON.stringify` on them is correct —\n * react-router's `ErrorResponse.data`, zod's `ZodError.issues`, an HTTP client's\n * `.status` / `.response.body`.\n */\nconst PAYLOAD_PROPS: ReadonlySet<string> = new Set([\n \"data\",\n \"status\",\n \"statuscode\",\n \"statustext\",\n \"code\",\n \"issues\",\n \"details\",\n \"body\",\n \"payload\",\n \"response\",\n \"info\",\n \"meta\",\n \"metadata\",\n \"context\",\n]);\n\n/**\n * Walk up the scope chain looking for a `catch` clause whose binding matches\n * `name`. We rely on the scope analysis provided by the parser rather than\n * type information, keeping the rule type-free.\n */\nfunction isCatchBinding(scope: Scope.Scope, name: string): boolean {\n let current: Scope.Scope | null = scope;\n while (current) {\n const variable = current.set.get(name);\n if (variable) {\n for (const def of variable.defs) {\n if (def.type === \"CatchClause\") {\n return true;\n }\n }\n }\n current = current.upper;\n }\n return false;\n}\n\n/**\n * True if a member-expression argument (`err.cause`, `this.lastError`) denotes an\n * Error value: an error-suggesting property name, or an error-suggesting base whose\n * property is not a known string accessor (`.message` / `.stack` / `.name`).\n */\nfunction memberSuggestsError(\n member: TSESTree.MemberExpression,\n scope: Scope.Scope,\n): boolean {\n const propName =\n !member.computed && member.property.type === \"Identifier\" ? member.property.name : null;\n\n if (propName !== null && ERROR_PROP_PATTERN.test(propName)) {\n return true;\n }\n\n const base = member.object;\n const baseSuggestsError =\n base.type === \"Identifier\" &&\n (ERROR_NAME_PATTERN.test(base.name) || isCatchBinding(scope, base.name));\n if (baseSuggestsError) {\n if (propName === null) {\n return true;\n }\n const lowered = propName.toLowerCase();\n return !SAFE_STRING_PROPS.has(lowered) && !PAYLOAD_PROPS.has(lowered);\n }\n\n return false;\n}\n\n/** The subject `x` of an `x instanceof Error` test, or null. */\nfunction instanceofErrorSubject(\n test: TSESTree.Expression,\n): TSESTree.Expression | null {\n if (\n test.type === \"BinaryExpression\" &&\n test.operator === \"instanceof\" &&\n test.right.type === \"Identifier\" &&\n test.right.name === \"Error\"\n ) {\n return test.left;\n }\n return null;\n}\n\n/** Names of a user-defined type-guard predicate: `isErrorLike`, `isError`, `hasErrorShape`. */\nconst TYPE_GUARD_PATTERN = /^(is|has)[A-Z]/;\n\n/** The narrowed subject `x` of a user-defined type-guard call `isFoo(x)`, or null. */\nfunction typeGuardSubject(\n test: TSESTree.Expression,\n): TSESTree.Expression | null {\n const arg = test.type === \"CallExpression\" ? test.arguments[0] : undefined;\n if (\n test.type === \"CallExpression\" &&\n test.callee.type === \"Identifier\" &&\n TYPE_GUARD_PATTERN.test(test.callee.name) &&\n test.arguments.length === 1 &&\n arg !== undefined &&\n arg.type !== \"SpreadElement\"\n ) {\n return arg;\n }\n return null;\n}\n\n/**\n * The subject `x` of a positive error-narrowing test — `x instanceof Error` or a\n * user-defined guard `isErrorLike(x)`. In both, the value IS the error in the\n * truthy branch, so `JSON.stringify(x)` belongs in the falsy (alternate) branch.\n */\nfunction positiveErrorSubject(\n test: TSESTree.Expression,\n): TSESTree.Expression | null {\n return instanceofErrorSubject(test) ?? typeGuardSubject(test);\n}\n\n/** The subject `x` of a negated error-narrowing test — `!(x instanceof Error)` / `!isErrorLike(x)`. */\nfunction negatedInstanceofErrorSubject(\n test: TSESTree.Expression,\n): TSESTree.Expression | null {\n if (test.type === \"UnaryExpression\" && test.operator === \"!\") {\n return positiveErrorSubject(test.argument);\n }\n return null;\n}\n\n/** Whether a branch statement unconditionally exits (its last statement returns/throws). */\nfunction branchTerminates(branch: TSESTree.Statement): boolean {\n const body = branch.type === \"BlockStatement\" ? branch.body : [branch];\n const last = body[body.length - 1];\n return (\n last !== undefined &&\n (last.type === \"ReturnStatement\" || last.type === \"ThrowStatement\")\n );\n}\n\n/**\n * True if an earlier guard `if (isErrorLike(arg)) return …` (or `instanceof Error`)\n * in an enclosing block narrows `argExpr` away from the error case before `node`,\n * so by the time `JSON.stringify(arg)` runs the value is the non-Error fallback.\n */\nfunction isNarrowedByEarlyReturn(\n node: TSESTree.Node,\n argExpr: TSESTree.Expression,\n sourceCode: Readonly<SourceCode>,\n): boolean {\n const argText = sourceCode.getText(argExpr);\n let current: TSESTree.Node | undefined = node.parent;\n while (current) {\n if (current.type === \"BlockStatement\" || current.type === \"Program\") {\n for (const stmt of current.body) {\n if (stmt.range[0] >= node.range[0]) {\n break;\n }\n if (\n stmt.type === \"IfStatement\" &&\n stmt.alternate === null &&\n branchTerminates(stmt.consequent)\n ) {\n const subject = positiveErrorSubject(stmt.test);\n if (subject && sourceCode.getText(subject) === argText) {\n return true;\n }\n }\n }\n }\n current = current.parent;\n }\n return false;\n}\n\nfunction nodeWithin(node: TSESTree.Node, container: TSESTree.Node | null): boolean {\n return (\n container !== null &&\n node.range[0] >= container.range[0] &&\n node.range[1] <= container.range[1]\n );\n}\n\n/**\n * True if `node` sits in the NON-error branch of an `argExpr instanceof Error`\n * guard — the branch where `argExpr` is provably not an Error, so stringifying it\n * is the correct fallback. Handles both the ternary and `if`/`else` shapes,\n * including the negated `!(x instanceof Error)` form.\n */\nfunction isGuardedByInstanceofError(\n node: TSESTree.Node,\n argExpr: TSESTree.Expression,\n sourceCode: Readonly<SourceCode>,\n): boolean {\n const argText = sourceCode.getText(argExpr);\n const sameSubject = (subject: TSESTree.Expression): boolean =>\n sourceCode.getText(subject) === argText;\n\n let current: TSESTree.Node | undefined = node.parent;\n while (current) {\n if (current.type === \"ConditionalExpression\") {\n const subject = positiveErrorSubject(current.test);\n if (subject && sameSubject(subject) && nodeWithin(node, current.alternate)) {\n return true;\n }\n const negated = negatedInstanceofErrorSubject(current.test);\n if (negated && sameSubject(negated) && nodeWithin(node, current.consequent)) {\n return true;\n }\n } else if (current.type === \"IfStatement\") {\n const subject = positiveErrorSubject(current.test);\n if (subject && sameSubject(subject) && nodeWithin(node, current.alternate)) {\n return true;\n }\n const negated = negatedInstanceofErrorSubject(current.test);\n if (negated && sameSubject(negated) && nodeWithin(node, current.consequent)) {\n return true;\n }\n }\n current = current.parent;\n }\n return false;\n}\n\nfunction isJsonStringify(callee: TSESTree.Expression): boolean {\n return (\n callee.type === \"MemberExpression\" &&\n !callee.computed &&\n callee.object.type === \"Identifier\" &&\n callee.object.name === \"JSON\" &&\n callee.property.type === \"Identifier\" &&\n callee.property.name === \"stringify\"\n );\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-json-stringify-error\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Disallow `JSON.stringify` on an Error value; it yields `{}` because `message`/`stack` are non-enumerable.\",\n },\n schema: [],\n messages: {\n noJsonStringifyError:\n \"`JSON.stringify` on an Error yields `{}` because `message`/`stack` are non-enumerable. Log `err.message` / `err.stack`, or use a proper error serializer.\",\n },\n },\n defaultOptions: [],\n create(context) {\n return {\n CallExpression(node: TSESTree.CallExpression): void {\n if (!isJsonStringify(node.callee)) {\n return;\n }\n\n const firstArg = node.arguments[0];\n if (!firstArg) {\n return;\n }\n\n const scope = context.sourceCode.getScope(firstArg);\n\n let suggestsError: boolean;\n if (firstArg.type === \"Identifier\") {\n suggestsError =\n ERROR_NAME_PATTERN.test(firstArg.name) || isCatchBinding(scope, firstArg.name);\n } else if (firstArg.type === \"MemberExpression\") {\n suggestsError = memberSuggestsError(firstArg, scope);\n } else {\n return;\n }\n\n if (!suggestsError) {\n return;\n }\n\n if (\n isGuardedByInstanceofError(node, firstArg, context.sourceCode) ||\n isNarrowedByEarlyReturn(node, firstArg, context.sourceCode)\n ) {\n return;\n }\n\n context.report({\n node,\n messageId: \"noJsonStringifyError\",\n });\n },\n };\n },\n});\n","/**\n * @fileoverview Disallow `catch` clauses that only log (via `console.*` or a\n * logger receiver such as `logger.warn(...)` / `Log.error(...)`) and then\n * swallow the error. A catch that logs and falls through — with no `throw`, no\n * `return`, and no real recovery — hides failures: the program keeps running in\n * a broken state while the only signal is a log line that is easy to miss.\n * Either rethrow the error or handle it for real.\n *\n * This rule is deliberately conservative and fires in exactly two shapes:\n * - `noLogOnlyCatch`: the catch body is non-empty and *every* statement is a\n * logging call (`console.*` or a call on a logger-named receiver). Any other\n * statement (a `throw`, a `return`, a fallback assignment, a non-logging\n * call, etc.) means the catch is doing something and is left alone.\n * - `emptyCatch`: the catch body is genuinely empty AND carries no comment.\n * A comment-only catch (`catch { /* ignore, safe because … *\\/ }`) is treated\n * as an intentional, documented ignore and is exempt.\n *\n * The documented-ignore exemption applies to BOTH message ids. A catch that logs\n * and explains why the failure is survivable is the same deliberate decision as\n * a comment-only empty catch, and the rule's own message (\"handle it for real\")\n * has nothing to add to it. Measured on 2,186 real TypeScript files (zod /\n * TanStack Query / react-router / swr / zustand): of 10 log-only hits, the one\n * carrying a written rationale was\n * react-router/packages/react-router-dev/vite/styles.ts:104 —\n * `catch { console.warn(...); // this can happen with dynamically imported\n * modules … }` — and it is precisely the case the rule should not litigate. A\n * bare `catch (e) { console.error(e); }` with no comment still fires (9 hits,\n * e.g. react-router/packages/react-router/lib/dom/ssr/fog-of-war.ts:209).\n *\n * A previous version fired the \"logging then swallowing\" message on empty and\n * comment-only catches that contained no logging call at all — the vast majority\n * of real-world hits — which was factually wrong. The two distinct message ids\n * keep each diagnostic accurate.\n *\n * Scope: this rule owns the `CatchClause` (try/catch) form ONLY. The promise\n * form — `.catch(() => {})`, `.catch(() => null)`, and every other handler that\n * provably does nothing — is owned entirely by `no-silent-promise-catch`, whose\n * detection is a strict superset of what this rule used to do there. Two rules\n * firing on one `.catch()` meant two messages and two suppression comments for\n * a single defect, so the promise path was removed from here.\n *\n * Test files opt out by default (filenames containing `.test.`, `.spec.`, or a\n * `__tests__/` path segment) since swallow-and-log is common and acceptable in\n * test scaffolding.\n *\n * A logging call is recognised by the shared `_logging` matcher: a log method on\n * a logger receiver, plus any project-declared free logging function named in\n * the `logFunctions` option (`logEvent(\"x\", { err })`). Structured loggers are\n * usually free functions, so without that option a catch that only calls one was\n * silently under-reported here — declaring it makes the shape visible.\n */\n\nimport { ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\nimport {\n createLogMatcher,\n LOGGING_OPTION_PROPERTIES,\n type LoggingOptions,\n} from \"./_logging.js\";\n\ntype MessageIds = \"noLogOnlyCatch\" | \"emptyCatch\";\ntype Options = readonly [LoggingOptions?];\n\nconst DEFAULT_IGNORE_PATTERNS: readonly RegExp[] = [\n /\\.test\\./,\n /\\.spec\\./,\n /[\\\\/]__tests__[\\\\/]/,\n];\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-log-only-catch\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Disallow `catch` clauses that only log (or silently do nothing) and then swallow the error; rethrow or handle it instead.\",\n },\n schema: [\n {\n type: \"object\",\n additionalProperties: false,\n properties: { ...LOGGING_OPTION_PROPERTIES },\n },\n ],\n messages: {\n noLogOnlyCatch:\n \"Logging then swallowing the error hides failures. Rethrow the error or handle it for real.\",\n emptyCatch:\n \"Empty catch silently swallows the error. Rethrow it, handle it, or add a comment explaining why it is safe to ignore.\",\n },\n },\n defaultOptions: [{}],\n create(context, [loggingOptions]) {\n const matcher = createLogMatcher(loggingOptions);\n const filename = context.filename;\n\n /** True when a statement is exactly a bare logging call, e.g. `console.error(err);`. */\n function isLoggingCallStatement(statement: TSESTree.Statement): boolean {\n if (statement.type !== \"ExpressionStatement\") {\n return false;\n }\n return matcher.isLoggingCall(statement.expression);\n }\n\n const isIgnoredByDefault = DEFAULT_IGNORE_PATTERNS.some((re) =>\n re.test(filename),\n );\n\n if (isIgnoredByDefault) {\n return {};\n }\n\n return {\n CatchClause(node: TSESTree.CatchClause): void {\n const statements = node.body.body;\n\n // A comment inside the block documents an intentional ignore — for a\n // silent swallow and for a log-and-continue alike.\n const isDocumented =\n context.sourceCode.getCommentsInside(node.body).length > 0;\n\n if (statements.length === 0) {\n if (isDocumented) {\n return;\n }\n context.report({ node, messageId: \"emptyCatch\" });\n return;\n }\n\n if (isDocumented) {\n return;\n }\n\n const everyStatementIsLogging = statements.every((statement) =>\n isLoggingCallStatement(statement),\n );\n\n if (everyStatementIsLogging) {\n context.report({ node, messageId: \"noLogOnlyCatch\" });\n }\n },\n };\n },\n});\n","/**\n * @fileoverview Shared helpers for recognising logging / error-reporting calls.\n * Used by `no-log-only-catch`, `no-sentinel-return-on-catch` and\n * `no-secret-in-log` so all three rules agree on what counts as \"this call\n * writes to a log sink\" before deciding a catch silently swallows an error or a\n * secret leaks.\n *\n * Two shapes are recognised:\n *\n * - **Receiver-shaped** — a log METHOD (`debug`/`info`/`warn`/`error`/…) on a\n * logger RECEIVER: `console.error(...)`, `logger.warn(...)`,\n * `this.logger.info(...)`, and builder/factory chains\n * (`logger.bind({...}).info(...)`, `logging.getLogger(n).info(...)`).\n * - **Free-function-shaped** — a project-declared logging function, e.g.\n * `logEvent(\"pr_scan.failed\", { repo, error })`. Structured loggers that are\n * plain functions taking a meta object are the dominant real-world shape and\n * have no logger receiver at all, so they are invisible to the receiver\n * heuristic. Projects declare theirs via the shared `logFunctions` rule\n * option; `loggerNames` extends the receiver set the same way.\n *\n * Both options are OFF by default (empty), so default behaviour is unchanged.\n * Declaring them makes the catch rules stop reporting a correctly-logged\n * degraded return AND makes `no-secret-in-log` start inspecting those calls —\n * the second effect closes a real hole, since `logEvent(\"auth\", { botToken })`\n * was previously never examined.\n */\n\nimport { type TSESTree } from \"@typescript-eslint/utils\";\n\nexport const LOG_METHODS: ReadonlySet<string> = new Set([\n \"debug\",\n \"info\",\n \"warn\",\n \"warning\",\n \"error\",\n \"exception\",\n \"critical\",\n \"trace\",\n \"log\",\n \"fatal\",\n \"success\",\n]);\n\nexport const LOGGER_NAMES: ReadonlySet<string> = new Set([\n \"logger\",\n \"log\",\n \"logging\",\n \"loguru\",\n \"console\",\n \"_logger\",\n \"_log\",\n]);\n\n/** Factory methods that RETURN a logger (`logging.getLogger(name).info(...)`). */\nexport const LOGGER_FACTORIES: ReadonlySet<string> = new Set([\n \"getlogger\",\n \"get_logger\",\n]);\n\n/**\n * Names of free functions / methods that report an error to a handler or sink,\n * e.g. `onUnexpectedError`, `reportError`, `captureException`, `logError`. Kept\n * broad on purpose: pairing this with \"takes the caught binding\" is what makes\n * it precise.\n */\nexport const REPORT_NAME_RE = /error|report|capture|log|trace|warn/i;\n\n/** Shared `loggerNames` / `logFunctions` option shape. */\nexport interface LoggingOptions {\n readonly loggerNames?: readonly string[];\n readonly logFunctions?: readonly string[];\n}\n\n/**\n * JSON-schema properties for the shared logging options. Spread into a rule's\n * `schema[0].properties` so every rule that consults `_logging` accepts the same\n * declaration and a project configures its logger exactly once.\n */\nexport const LOGGING_OPTION_PROPERTIES = {\n loggerNames: { type: \"array\", items: { type: \"string\" } },\n logFunctions: { type: \"array\", items: { type: \"string\" } },\n} as const;\n\n/** The static callee name of a call (free function or method), or null. */\nexport function calleeName(callee: TSESTree.Node): string | null {\n if (callee.type === \"Identifier\") {\n return callee.name;\n }\n if (\n callee.type === \"MemberExpression\" &&\n !callee.computed &&\n callee.property.type === \"Identifier\"\n ) {\n return callee.property.name;\n }\n return null;\n}\n\nexport interface LogMatcher {\n /** Is `expr` a logger receiver (bare name, member chain, or factory call)? */\n isLoggerReceiver(expr: TSESTree.Expression | TSESTree.PrivateIdentifier): boolean;\n /** Is `expr` a project-declared free logging function call (`logEvent(...)`)? */\n isLogFunctionCall(expr: TSESTree.Node): boolean;\n /** Is `expr` a logging call of either shape? */\n isLoggingCall(expr: TSESTree.Node): boolean;\n}\n\n/**\n * Builds the log-recognition predicates for one rule invocation, folding in the\n * project's declared `loggerNames` / `logFunctions`. Call once per `create()`.\n */\nexport function createLogMatcher(options: LoggingOptions = {}): LogMatcher {\n const loggerNames: ReadonlySet<string> = new Set([\n ...LOGGER_NAMES,\n ...(options.loggerNames ?? []).map((name) => name.toLowerCase()),\n ]);\n const logFunctions: ReadonlySet<string> = new Set(options.logFunctions ?? []);\n\n function isLoggerReceiver(\n expr: TSESTree.Expression | TSESTree.PrivateIdentifier,\n ): boolean {\n switch (expr.type) {\n case \"Identifier\":\n return loggerNames.has(expr.name.toLowerCase());\n case \"MemberExpression\": {\n const { property, object } = expr;\n if (!expr.computed && property.type === \"Identifier\") {\n const lowered = property.name.toLowerCase();\n if (loggerNames.has(lowered) || LOGGER_FACTORIES.has(lowered)) {\n return true;\n }\n }\n return isLoggerReceiver(object);\n }\n case \"CallExpression\": {\n const callee = expr.callee;\n if (\n callee.type === \"MemberExpression\" &&\n !callee.computed &&\n callee.property.type === \"Identifier\" &&\n LOGGER_FACTORIES.has(callee.property.name.toLowerCase())\n ) {\n return true;\n }\n if (callee.type !== \"Super\") {\n return isLoggerReceiver(callee);\n }\n return false;\n }\n default:\n return false;\n }\n }\n\n function isLogFunctionCall(expr: TSESTree.Node): boolean {\n if (expr.type !== \"CallExpression\" || logFunctions.size === 0) {\n return false;\n }\n const name = calleeName(expr.callee);\n return name !== null && logFunctions.has(name);\n }\n\n function isLoggingCall(expr: TSESTree.Node): boolean {\n if (expr.type !== \"CallExpression\") {\n return false;\n }\n if (isLogFunctionCall(expr)) {\n return true;\n }\n const callee = expr.callee;\n if (\n callee.type !== \"MemberExpression\" ||\n callee.computed ||\n callee.property.type !== \"Identifier\"\n ) {\n return false;\n }\n if (!LOG_METHODS.has(callee.property.name.toLowerCase())) {\n return false;\n }\n return isLoggerReceiver(callee.object);\n }\n\n return { isLoggerReceiver, isLogFunctionCall, isLoggingCall };\n}\n","/**\n * @fileoverview Disallow direct `process.env` access. Force all env reads\n * through a Zod-validated env module so configuration is typed and validated\n * at startup.\n *\n * The rule is about READING A CONFIGURATION VALUE in application code, and a\n * sweep of 2,186 real TypeScript files (zod / TanStack Query / react-router /\n * swr / zustand) produced 143 hits of which ~90% were not that. Four exemptions,\n * each measured against that corpus:\n *\n * - **Write target.** `process.env.X = \"yes\"` sets a variable for a child\n * process; there is no value being read, so the validated-env module has\n * nothing to say about it. 20 hits, e.g.\n * react-router/packages/react-router-dev/vite/plugins/prerender.ts:250 and\n * react-router/packages/react-router-dev/vite/plugin.ts:2451.\n * - **Whole-environment pass-through.** `{ ...process.env, NO_COLOR: \"1\" }`\n * forwards the inherited environment to a spawned process — again no\n * configuration value is read. 9 hits, e.g.\n * react-router/integration/helpers/create-fixture.ts:45.\n * - **Test files** (shared `isTestFile`). A test that drives a CLI has to set\n * and read the raw environment; there is no app env module in scope. 50\n * hits, e.g.\n * react-router/packages/create-react-router/__tests__/create-react-router-test.ts:1052.\n * - **Scripts and build/test config** (shared `isScriptFile`, plus a\n * `*.config.*` basename). A `vitest.config.mts` / `playwright.config.ts`\n * configures the *build*, runs before the app exists, and cannot import the\n * app's validated env. 47 hits, e.g. swr/test/e2e/playwright.config.ts:16\n * and zod/scripts/check-versions.ts:5.\n *\n * What survives is the shape the rule was written for: application code reading\n * a deployment setting, e.g. zod/packages/docs/loaders/stars.ts:1\n * (`const GITHUB_TOKEN = process.env.GITHUB_TOKEN!`).\n */\n\nimport { ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\nimport { isScriptFile, isTestFile } from \"./_paths.js\";\n\ntype MessageIds = \"noRawEnv\";\ntype Options = readonly [];\n\n/** Build / test / tooling config: `vite.config.ts`, `vitest.config.mts`, `playwright.config.ts`. */\nconst CONFIG_FILE_RE = /(^|[\\\\/])[\\w.-]+\\.config\\.[cm]?[jt]sx?$/;\n\n/**\n * Modules that define the validated env boundary have to read raw env exactly\n * once before handing values to Zod. Keep this path-scoped so ordinary\n * `config.ts`/settings helpers still use the boundary instead of becoming one.\n */\nconst ENV_BOUNDARY_FILE_RE =\n /(^|[\\\\/])(?:env|client-env|server-env|client-settings|server-settings)\\.[cm]?[jt]sx?$/;\n\nfunction isValidatedEnvBoundary(filename: string, sourceText: string): boolean {\n return (\n ENV_BOUNDARY_FILE_RE.test(filename.replaceAll(\"\\\\\", \"/\")) &&\n /\\bz\\.object\\s*\\(/.test(sourceText) &&\n /\\.parse\\s*\\(/.test(sourceText)\n );\n}\n\n/** True for the `process.env` member node (dotted or as the base of `process.env[key]`). */\nfunction isProcessEnv(node: TSESTree.MemberExpression): boolean {\n return (\n !node.computed &&\n node.object.type === \"Identifier\" &&\n node.object.name === \"process\" &&\n node.property.type === \"Identifier\" &&\n node.property.name === \"env\"\n );\n}\n\n/** True for the `import.meta.env` member node (dotted or as the base of `import.meta.env[key]`). */\nfunction isImportMetaEnv(node: TSESTree.MemberExpression): boolean {\n return (\n !node.computed &&\n node.property.type === \"Identifier\" &&\n node.property.name === \"env\" &&\n node.object.type === \"MetaProperty\" &&\n node.object.meta.name === \"import\" &&\n node.object.property.name === \"meta\"\n );\n}\n\n// Build-time constants that bundlers (webpack/Vite) statically replace — there is\n// no runtime env value to route through the validated layer, so they are exempt.\nconst BUILD_TIME_CONSTANTS: ReadonlySet<string> = new Set([\n \"NODE_ENV\",\n \"MODE\",\n \"DEV\",\n \"PROD\",\n \"SSR\",\n]);\n\n/** True when `node` is the base of a build-time-constant access like `process.env.NODE_ENV`. */\nfunction isBuildTimeConstantAccess(node: TSESTree.MemberExpression): boolean {\n const parent = node.parent;\n return (\n parent.type === \"MemberExpression\" &&\n parent.object === node &&\n !parent.computed &&\n parent.property.type === \"Identifier\" &&\n BUILD_TIME_CONSTANTS.has(parent.property.name)\n );\n}\n\n/**\n * True when the env access is the TARGET of an assignment (`process.env.X = v`,\n * `process.env.X ??= v`, `delete process.env.X`) rather than a read. Writing a\n * variable for a child process reads no configuration value.\n */\nfunction isWriteTarget(node: TSESTree.MemberExpression): boolean {\n const access = node.parent.type === \"MemberExpression\" && node.parent.object === node ? node.parent : node;\n const parent = access.parent;\n if (parent.type === \"AssignmentExpression\") {\n return parent.left === access;\n }\n if (parent.type === \"UnaryExpression\") {\n return parent.operator === \"delete\";\n }\n return false;\n}\n\n/**\n * True when the WHOLE environment is spread into an object literal —\n * `{ ...process.env, NO_COLOR: \"1\" }`. That forwards the inherited environment\n * to a spawned process; it is not a read of any particular setting.\n */\nfunction isWholeEnvSpread(node: TSESTree.MemberExpression): boolean {\n const parent = node.parent;\n return parent.type === \"SpreadElement\" && parent.argument === node;\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-raw-env\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Disallow direct `process.env` access; use a Zod-validated env module instead.\",\n },\n schema: [],\n messages: {\n noRawEnv:\n \"Do not read from `process.env` directly. Import the Zod-validated env module instead so values are typed and validated at startup.\",\n },\n },\n defaultOptions: [],\n create(context) {\n const filename = context.filename;\n if (\n isTestFile(filename) ||\n isScriptFile(filename) ||\n CONFIG_FILE_RE.test(filename.replaceAll(\"\\\\\", \"/\")) ||\n isValidatedEnvBoundary(filename, context.sourceCode.text)\n ) {\n return {};\n }\n return {\n MemberExpression(node: TSESTree.MemberExpression): void {\n if (\n (isProcessEnv(node) || isImportMetaEnv(node)) &&\n !isBuildTimeConstantAccess(node) &&\n !isWriteTarget(node) &&\n !isWholeEnvSpread(node)\n ) {\n context.report({\n node,\n messageId: \"noRawEnv\",\n });\n }\n },\n };\n },\n});\n","/**\n * @fileoverview Disallow silently swallowing an error in a `catch` clause by\n * returning a \"sentinel\" empty value as the final/only statement.\n *\n * A `catch` block whose last statement is `return null` / `return undefined` /\n * `return false` / `return []` / `return {}` (and which never `throw`s) discards\n * the caught error entirely. Downstream callers can't distinguish a genuine\n * empty result from a failure, which is a frequent source of silent data loss\n * and broken idempotency decisions.\n *\n * This rule is deliberately conservative — it prefers false negatives over\n * false positives. It does NOT flag:\n * - catch blocks that `throw`/rethrow anywhere in their body,\n * - returns of a computed/meaningful value (calls, identifiers, member\n * expressions, non-empty literals),\n * - `return 0` / `return \"\"`, which are often legitimate results,\n * - catches that LOG or REPORT the caught error before the sentinel return —\n * a `console.*`/logger call, a project-declared free logging function (the\n * shared `logFunctions` option), or an error-reporting call that mentions\n * the caught binding ANYWHERE in its arguments. The binding search walks the\n * whole argument subtree, because structured loggers take a meta object:\n * `logEvent(\"x\", { error: err instanceof Error ? err.message : String(err) })`\n * reports the error just as surely as `report(err)` does. Here the sentinel\n * is a deliberate degraded return, not a silent swallow.\n * - the typed-optional / safe-parse / predicate shape: the try body returns an\n * expression whose subtree contains a parse-style call that throws on bad\n * input (`JSON.parse(x)`, `new URL(s).protocol === \"https:\"`, or a lone\n * `await request.json()`), or the enclosing function is a predicate — either\n * declared (`: boolean`) or NAMED as one (`isDirectory`, `fileExists`) — or\n * returns the same sentinel kind on a normal path, including through a\n * ternary (`return valid ? value : false`). Here the sentinel is the\n * declared contract.\n *\n * The predicate exemption is deliberately limited to a `boolean` result. A\n * declared `T | null` / `T | undefined` return does NOT exempt: hiding a\n * failure behind a nullable accessor is the exact true positive this rule\n * exists for, and exempting it would gut the rule.\n *\n * The last two clauses came out of a 2026-07 corpus sweep (2220 files across\n * zod / TanStack Query / react-router / swr / zustand) in which the rule fired\n * 10 times and every hit was a false positive — see `PREDICATE_NAME_RE` and\n * `returnedSentinelKinds` for the per-family evidence.\n *\n * Scope: this rule owns the `CatchClause` (try/catch) form ONLY. The same\n * swallow written in promise form — `await load().catch(() => [])` — is owned\n * entirely by `no-silent-promise-catch`, whose detection (any handler that\n * provably does nothing: bare literal, empty collection, empty block, or a\n * block returning one of those) is a strict superset of the empty-collection\n * check this rule used to run there. Two rules firing on one `.catch()` meant\n * two messages and two suppression comments for a single defect, so the\n * promise path was removed from here.\n */\n\nimport {\n ESLintUtils,\n type TSESTree,\n AST_NODE_TYPES,\n} from \"@typescript-eslint/utils\";\n\nimport {\n createLogMatcher,\n calleeName,\n LOGGING_OPTION_PROPERTIES,\n type LoggingOptions,\n REPORT_NAME_RE,\n} from \"./_logging.js\";\nimport { isGeneratedFile } from \"./_paths.js\";\n\ntype MessageIds = \"noSentinelReturn\";\ntype Options = readonly [LoggingOptions?];\n\ntype SentinelKind = \"nullish\" | \"boolean\" | \"array\" | \"object\" | \"string\";\n\n/** The sentinel \"kind\" of a returned expression, or null if not a sentinel. */\nfunction sentinelKind(arg: TSESTree.Expression | null): SentinelKind | null {\n if (arg === null) {\n return null;\n }\n if (arg.type === AST_NODE_TYPES.Literal) {\n if (arg.value === null) {\n return \"nullish\";\n }\n if (typeof arg.value === \"boolean\") {\n return \"boolean\";\n }\n if (typeof arg.value === \"string\") {\n return \"string\";\n }\n return null;\n }\n if (arg.type === AST_NODE_TYPES.Identifier && arg.name === \"undefined\") {\n return \"nullish\";\n }\n if (arg.type === AST_NODE_TYPES.ArrayExpression) {\n return \"array\";\n }\n if (arg.type === AST_NODE_TYPES.ObjectExpression) {\n return \"object\";\n }\n return null;\n}\n\n/** Whether a returned expression is one of the swallowing sentinels we flag. */\nfunction isSentinelArgument(arg: TSESTree.Expression | null): boolean {\n if (arg === null) {\n return false;\n }\n if (arg.type === AST_NODE_TYPES.Literal && arg.value === null) {\n return true;\n }\n if (arg.type === AST_NODE_TYPES.Literal && arg.value === false) {\n return true;\n }\n if (arg.type === AST_NODE_TYPES.Identifier && arg.name === \"undefined\") {\n return true;\n }\n if (arg.type === AST_NODE_TYPES.ArrayExpression && arg.elements.length === 0) {\n return true;\n }\n if (\n arg.type === AST_NODE_TYPES.ObjectExpression &&\n arg.properties.length === 0\n ) {\n return true;\n }\n return false;\n}\n\nfunction isFunctionNode(node: TSESTree.Node): boolean {\n return (\n node.type === AST_NODE_TYPES.FunctionDeclaration ||\n node.type === AST_NODE_TYPES.FunctionExpression ||\n node.type === AST_NODE_TYPES.ArrowFunctionExpression\n );\n}\n\nfunction isNode(value: unknown): value is TSESTree.Node {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as { type?: unknown }).type === \"string\"\n );\n}\n\n/**\n * Walk `node`'s subtree applying `visit`, but do not descend into nested\n * function scopes — their statements don't run synchronously for the current\n * catch. Stops early once `visit` returns true.\n */\nfunction walkWithinScope(\n node: TSESTree.Node,\n visit: (current: TSESTree.Node) => boolean,\n): boolean {\n let found = false;\n\n const recurse = (current: TSESTree.Node): void => {\n if (found) {\n return;\n }\n if (visit(current)) {\n found = true;\n return;\n }\n if (isFunctionNode(current)) {\n return;\n }\n for (const key of Object.keys(current)) {\n if (key === \"parent\") {\n continue;\n }\n const value = (current as unknown as Record<string, unknown>)[key];\n if (Array.isArray(value)) {\n for (const child of value) {\n if (isNode(child)) {\n recurse(child);\n }\n }\n } else if (isNode(value)) {\n recurse(value);\n }\n }\n };\n\n recurse(node);\n return found;\n}\n\n/** Does this subtree throw (ignoring nested function scopes)? */\nfunction containsThrow(node: TSESTree.Node): boolean {\n return walkWithinScope(\n node,\n (current) => current.type === AST_NODE_TYPES.ThrowStatement,\n );\n}\n\n/** True when a parameter pattern binds `name` (plain, default, rest, or destructured). */\nfunction bindsName(param: TSESTree.Node, name: string): boolean {\n switch (param.type) {\n case AST_NODE_TYPES.Identifier:\n return param.name === name;\n case AST_NODE_TYPES.AssignmentPattern:\n return bindsName(param.left, name);\n case AST_NODE_TYPES.RestElement:\n return bindsName(param.argument, name);\n case AST_NODE_TYPES.ArrayPattern:\n return param.elements.some(\n (element) => element !== null && bindsName(element, name),\n );\n case AST_NODE_TYPES.ObjectPattern:\n return param.properties.some((property) =>\n property.type === AST_NODE_TYPES.RestElement\n ? bindsName(property.argument, name)\n : bindsName(property.value, name),\n );\n default:\n return false;\n }\n}\n\n/**\n * Does `node`'s subtree READ the identifier `name`?\n *\n * \"Read\" means a VALUE position. A bare name match is not enough, and getting\n * that wrong silently guts the rule. Walking the whole argument subtree (so a\n * structured logger's meta object counts as reporting the error) cost 8 of 18\n * true positives when measured against the previous release, because all of\n * these matched on name alone:\n *\n * logCounter({ error: 1 }) // object KEY — a counter, not a log\n * captureMetric({ tags: { e: 1 } }) // nested key\n * reportStatus(response.err) // member PROPERTY of another object\n * logAll(items.map((error) => error.id)) // a param that merely SHADOWS it\n *\n * Each of those swallows the error while looking \"logged\". Non-computed property\n * keys and non-computed member properties are therefore skipped, and a nested\n * function that rebinds the name is not descended into.\n */\nfunction subtreeReadsName(node: TSESTree.Node, name: string): boolean {\n let found = false;\n\n /** True when this function rebinds `name`, so its body reads a different value. */\n const shadowsName = (fn: TSESTree.Node): boolean =>\n isFunctionNode(fn) &&\n (fn as unknown as { params: TSESTree.Parameter[] }).params.some((param) =>\n bindsName(param, name),\n );\n\n const recurse = (current: TSESTree.Node): void => {\n if (found) {\n return;\n }\n if (current.type === AST_NODE_TYPES.Identifier && current.name === name) {\n found = true;\n return;\n }\n if (shadowsName(current)) {\n return;\n }\n for (const key of Object.keys(current)) {\n if (key === \"parent\") {\n continue;\n }\n // `{ error: 1 }` — the key names a field; it does not read the binding.\n if (\n key === \"key\" &&\n current.type === AST_NODE_TYPES.Property &&\n !current.computed\n ) {\n continue;\n }\n // `response.err` — the property names a field on some other object.\n if (\n key === \"property\" &&\n current.type === AST_NODE_TYPES.MemberExpression &&\n !current.computed\n ) {\n continue;\n }\n const value = (current as unknown as Record<string, unknown>)[key];\n if (Array.isArray(value)) {\n for (const child of value) {\n if (isNode(child)) {\n recurse(child);\n }\n }\n } else if (isNode(value)) {\n recurse(value);\n }\n }\n };\n\n recurse(node);\n return found;\n}\n\n/**\n * Does the caught-error binding appear ANYWHERE in a call's arguments? The whole\n * argument subtree is searched, not just top-level positional args: a structured\n * logger takes the error nested in a meta object or behind a conditional\n * (`logEvent(\"x\", { error: err instanceof Error ? err.message : String(err) })`),\n * and that reports the error exactly as `report(err)` does.\n */\nfunction argsIncludeBinding(\n args: readonly TSESTree.CallExpressionArgument[],\n caughtName: string | null,\n): boolean {\n if (caughtName === null) {\n return false;\n }\n return args.some((arg) => subtreeReadsName(arg, caughtName));\n}\n\n/** The try block guarded by this catch. */\nfunction tryBlockOf(catchNode: TSESTree.CatchClause): TSESTree.BlockStatement {\n return catchNode.parent.block;\n}\n\n/** Constructors that throw on malformed input. */\nconst SAFE_PARSE_CONSTRUCTORS: ReadonlySet<string> = new Set([\n \"RegExp\",\n \"URL\",\n \"URLPattern\",\n]);\n\n/**\n * A parse-style call that throws on bad input: `JSON.parse(x)`, `YAML.parse(x)`,\n * `new RegExp(x)`, `new URL(x)`.\n */\nfunction isParseShapedNode(node: TSESTree.Node): boolean {\n if (\n node.type === AST_NODE_TYPES.CallExpression &&\n node.callee.type === AST_NODE_TYPES.MemberExpression &&\n !node.callee.computed &&\n node.callee.property.type === AST_NODE_TYPES.Identifier\n ) {\n return node.callee.property.name === \"parse\";\n }\n if (\n node.type === AST_NODE_TYPES.NewExpression &&\n node.callee.type === AST_NODE_TYPES.Identifier\n ) {\n return SAFE_PARSE_CONSTRUCTORS.has(node.callee.name);\n }\n return false;\n}\n\n/** Body-decoding methods of a `Request` / `Response` — they throw on bad input. */\nconst BODY_DECODE_METHODS: ReadonlySet<string> = new Set([\n \"json\",\n \"text\",\n \"arrayBuffer\",\n]);\n\nfunction isBodyDecodeNode(node: TSESTree.Node): boolean {\n return (\n node.type === AST_NODE_TYPES.CallExpression &&\n node.callee.type === AST_NODE_TYPES.MemberExpression &&\n !node.callee.computed &&\n node.callee.property.type === AST_NODE_TYPES.Identifier &&\n BODY_DECODE_METHODS.has(node.callee.property.name)\n );\n}\n\n/**\n * True when a return statement's argument SUBTREE contains a match. The whole\n * subtree is searched rather than the top node alone, because the parse is\n * usually consumed in place: `return new URL(s).protocol === \"https:\"` is a\n * `BinaryExpression`, not a `NewExpression`, and `return await request.json()`\n * is an `AwaitExpression`. Nested function scopes are not searched — a parse\n * inside a callback does not run when the `try` does.\n */\nfunction returnsMatching(\n stmt: TSESTree.Node,\n predicate: (node: TSESTree.Node) => boolean,\n): boolean {\n return (\n stmt.type === AST_NODE_TYPES.ReturnStatement &&\n stmt.argument !== null &&\n walkWithinScope(stmt.argument, predicate)\n );\n}\n\n/** The declared return type annotation of the nearest enclosing function, or null. */\nfunction enclosingReturnTypeNode(\n node: TSESTree.Node,\n): TSESTree.TypeNode | null {\n let current: TSESTree.Node | undefined | null = node.parent;\n while (current !== undefined && current !== null) {\n if (isFunctionNode(current) && \"returnType\" in current) {\n return current.returnType?.typeAnnotation ?? null;\n }\n current = current.parent;\n }\n return null;\n}\n\n/**\n * Function names that declare a boolean contract as clearly as a `: boolean`\n * annotation does. TypeScript INFERS the return type of\n * `async function isDirectory(d: string) { try { … } catch { return false } }`,\n * so demanding the annotation made the predicate exemption depend on a style\n * choice rather than on the contract.\n *\n * Corpus evidence (2220 files across zod / TanStack Query / react-router / swr /\n * zustand, 2026-07): 7 of the rule's 10 hits were unannotated predicates —\n * `react-router/packages/create-react-router/utils.ts:196-202` (`directoryExists`),\n * `:205-211` (`fileExists`), and\n * `react-router/packages/react-router/lib/rsc/server.rsc.ts:1501-1507`\n * (`isClientReference`). `false` is the ANSWER those functions exist to give;\n * there is no richer value a caller could have received.\n */\nconst PREDICATE_NAME_RE = /^(is|has|can|should|must|does|did|was|were|are)[A-Z]/;\nconst PREDICATE_SUFFIX_RE = /(Exists?|Available|Enabled|Disabled)$/;\n\n/** The name of the nearest enclosing function, or null for an anonymous one. */\nfunction enclosingFunctionName(node: TSESTree.Node): string | null {\n let current: TSESTree.Node | undefined | null = node.parent;\n while (current !== undefined && current !== null) {\n if (isFunctionNode(current)) {\n if (\n \"id\" in current &&\n isNode(current.id) &&\n current.id.type === AST_NODE_TYPES.Identifier\n ) {\n return current.id.name;\n }\n const parent = current.parent;\n if (\n parent?.type === AST_NODE_TYPES.VariableDeclarator &&\n parent.id.type === AST_NODE_TYPES.Identifier\n ) {\n return parent.id.name;\n }\n return null;\n }\n current = current.parent;\n }\n return null;\n}\n\n/**\n * True when the enclosing function's NAME declares it a predicate and the catch\n * returns a boolean. Same reasoning as `isDeclaredBooleanPredicate`, applied to\n * the (very common) unannotated spelling.\n */\nfunction isNamedBooleanPredicate(\n catchNode: TSESTree.CatchClause,\n kind: SentinelKind,\n): boolean {\n if (kind !== \"boolean\") {\n return false;\n }\n const name = enclosingFunctionName(catchNode);\n return (\n name !== null &&\n (PREDICATE_NAME_RE.test(name) || PREDICATE_SUFFIX_RE.test(name))\n );\n}\n\n/**\n * True when the enclosing function is a DECLARED predicate — annotated\n * `: boolean` (or `: Promise<boolean>`) — and the catch returns `false`. A\n * boolean return type cannot carry error information at all, so \"return a typed\n * Result\" is not advice that applies; the sentinel IS the whole contract. Only\n * `boolean` qualifies: a declared `T | null` return is the shape this rule\n * exists to flag and must keep firing.\n */\nfunction isDeclaredBooleanPredicate(\n catchNode: TSESTree.CatchClause,\n kind: SentinelKind,\n): boolean {\n if (kind !== \"boolean\") {\n return false;\n }\n let declared = enclosingReturnTypeNode(catchNode);\n if (\n declared?.type === AST_NODE_TYPES.TSTypeReference &&\n declared.typeName.type === AST_NODE_TYPES.Identifier &&\n declared.typeName.name === \"Promise\"\n ) {\n declared = declared.typeArguments?.params[0] ?? null;\n }\n return declared?.type === AST_NODE_TYPES.TSBooleanKeyword;\n}\n\n/**\n * Does the try body return a safe-parse-style expression?\n *\n * `JSON.parse` / `new RegExp` / `new URL` count from anywhere in the try body —\n * a read-then-parse (`const t = read(p); return JSON.parse(t)`) is still the\n * safe-parse contract. A body-decoding method (`.json()`) counts ONLY when it is\n * the try's sole statement, so `try { return await request.json() }` is exempt\n * while `try { const res = await fetch(url); return await res.json() }` is not:\n * there the catch also swallows the network failure, which is the true positive.\n */\nfunction tryReturnsSafeParse(catchNode: TSESTree.CatchClause): boolean {\n const tryBlock = tryBlockOf(catchNode);\n if (\n walkWithinScope(tryBlock, (current) =>\n returnsMatching(current, isParseShapedNode),\n )\n ) {\n return true;\n }\n const only = tryBlock.body.length === 1 ? tryBlock.body[0] : undefined;\n return only !== undefined && returnsMatching(only, isBodyDecodeNode);\n}\n\n/** The nearest enclosing function body, or null. */\nfunction enclosingFunctionBody(\n node: TSESTree.Node,\n): TSESTree.BlockStatement | null {\n let current: TSESTree.Node | undefined | null = node.parent;\n while (current !== undefined && current !== null) {\n if (\n isFunctionNode(current) &&\n \"body\" in current &&\n isNode(current.body) &&\n current.body.type === AST_NODE_TYPES.BlockStatement\n ) {\n return current.body;\n }\n current = current.parent;\n }\n return null;\n}\n\n/**\n * True when the enclosing function returns the same sentinel kind on a normal\n * (non-catch) path — a boolean predicate, a `T | undefined` accessor, etc. —\n * so the catch sentinel is the declared contract, not a swallow. Returns inside\n * the flagged catch itself are excluded.\n */\nfunction functionReturnsSameSentinelKindElsewhere(\n catchNode: TSESTree.CatchClause,\n kind: SentinelKind,\n): boolean {\n const functionBody = enclosingFunctionBody(catchNode);\n if (functionBody === null) {\n return false;\n }\n return walkWithinScope(functionBody, (current) => {\n if (current.type !== AST_NODE_TYPES.ReturnStatement) {\n return false;\n }\n if (isWithin(current, catchNode.body)) {\n return false;\n }\n return returnedSentinelKinds(current.argument).has(kind);\n });\n}\n\n/**\n * The sentinel kinds a returned expression can evaluate to. A bare sentinel\n * yields one; a ternary or a `??` / `||` fallback yields the kinds of its\n * branches, because `return valid ? value : false` puts `false` on a NORMAL\n * path just as plainly as `return false` does.\n *\n * Corpus evidence (2026-07 sweep): the remaining 3 of the rule's 10 hits were\n * this spelling —\n * `react-router/packages/react-router/lib/server-runtime/crypto.ts:30`\n * (`return valid ? value : false;` on the happy path, then `return false` in the\n * catch for an unparseable signature) and\n * `react-router/scripts/utils/git.ts:12` (`return typeof parsed.version ===\n * \"string\" ? parsed.version : null`). In both, the catch sentinel is the value\n * the function already documents for \"no result\", so it hides nothing.\n */\nfunction returnedSentinelKinds(\n arg: TSESTree.Expression | null,\n): ReadonlySet<SentinelKind> {\n const kinds = new Set<SentinelKind>();\n if (arg === null) {\n return kinds;\n }\n const direct = sentinelKind(arg);\n if (direct !== null) {\n kinds.add(direct);\n return kinds;\n }\n if (arg.type === AST_NODE_TYPES.ConditionalExpression) {\n for (const branch of [arg.consequent, arg.alternate]) {\n for (const nested of returnedSentinelKinds(branch)) {\n kinds.add(nested);\n }\n }\n } else if (\n arg.type === AST_NODE_TYPES.LogicalExpression &&\n (arg.operator === \"??\" || arg.operator === \"||\")\n ) {\n for (const nested of returnedSentinelKinds(arg.right)) {\n kinds.add(nested);\n }\n }\n return kinds;\n}\n\n/** Is `node` inside `ancestor`'s subtree? */\nfunction isWithin(node: TSESTree.Node, ancestor: TSESTree.Node): boolean {\n let current: TSESTree.Node | undefined | null = node;\n while (current !== undefined && current !== null) {\n if (current === ancestor) {\n return true;\n }\n current = current.parent;\n }\n return false;\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-sentinel-return-on-catch\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Disallow swallowing a caught error by returning an empty sentinel (`null`, `undefined`, `false`, `[]`, `{}`) as the final statement of a `catch` block, unless the error is logged/reported or the sentinel is the declared safe-parse/predicate contract.\",\n },\n schema: [\n {\n type: \"object\",\n additionalProperties: false,\n properties: { ...LOGGING_OPTION_PROPERTIES },\n },\n ],\n messages: {\n noSentinelReturn:\n \"This `catch` block swallows the error by returning an empty sentinel without logging it. Rethrow it, log/report it, or return a typed Result.\",\n },\n },\n defaultOptions: [{}],\n create(context, [loggingOptions]) {\n if (isGeneratedFile(context.filename, context.sourceCode.text)) {\n return {};\n }\n\n const matcher = createLogMatcher(loggingOptions);\n\n /**\n * Does the catch body log or report the caught error before the sentinel\n * return? Either a logging call (logger receiver or a declared\n * `logFunctions` free function), or an error-reporting call whose name\n * matches `REPORT_NAME_RE` and whose arguments mention the caught binding.\n */\n function logsOrReportsError(\n catchBody: TSESTree.BlockStatement,\n caughtName: string | null,\n ): boolean {\n return walkWithinScope(catchBody, (current) => {\n if (current.type !== AST_NODE_TYPES.CallExpression) {\n return false;\n }\n if (matcher.isLoggingCall(current)) {\n return true;\n }\n const name = calleeName(current.callee);\n return (\n name !== null &&\n REPORT_NAME_RE.test(name) &&\n argsIncludeBinding(current.arguments, caughtName)\n );\n });\n }\n\n return {\n CatchClause(node: TSESTree.CatchClause): void {\n const body = node.body.body;\n if (body.length === 0) {\n return;\n }\n\n const last = body[body.length - 1];\n if (last === undefined || last.type !== AST_NODE_TYPES.ReturnStatement) {\n return;\n }\n\n if (!isSentinelArgument(last.argument)) {\n return;\n }\n\n if (containsThrow(node.body)) {\n return;\n }\n\n const caughtName =\n node.param?.type === AST_NODE_TYPES.Identifier\n ? node.param.name\n : null;\n\n if (logsOrReportsError(node.body, caughtName)) {\n return;\n }\n\n if (tryReturnsSafeParse(node)) {\n return;\n }\n\n const kind = sentinelKind(last.argument);\n if (kind !== null && isNamedBooleanPredicate(node, kind)) {\n return;\n }\n\n if (kind !== null && isDeclaredBooleanPredicate(node, kind)) {\n return;\n }\n\n if (\n kind !== null &&\n functionReturnsSameSentinelKindElsewhere(node, kind)\n ) {\n return;\n }\n\n context.report({\n node: last,\n messageId: \"noSentinelReturn\",\n });\n },\n };\n },\n});\n","/**\n * @fileoverview Disallow `await` expressions located directly inside the body\n * of a `for` / `for-of` / `for-in` / `while` loop (or an async `.forEach` /\n * `.map` / `.filter` callback) within the same function scope. Awaiting serially\n * inside a loop serializes I/O that is often better expressed as\n * `await Promise.all(xs.map(async (x) => ...))`, letting the operations run\n * concurrently.\n *\n * This rule is intentionally conservative — it prefers a false negative over a\n * false positive:\n * - `for await...of` is NOT flagged (async iteration is the correct tool).\n * - awaits inside a function/arrow defined within the loop are NOT flagged\n * (they belong to a different function scope).\n * - awaits not inside any loop are NOT flagged.\n *\n * Real-world sweeps (VS Code, NestJS, Next.js) surfaced whole classes of loops\n * where serial awaits are REQUIRED and `Promise.all` would change semantics.\n * Those are suppressed here:\n * (a) the loop short-circuits or retries — its body (this scope) contains a\n * `return` / `break` / `continue`;\n * (b) the awaited value threads through the iteration — `x = await f(x)`,\n * `content = await p(content)` (the assignment target is read inside the\n * awaited expression);\n * (c) the await is a timer yield — `await new Promise((r) => setTimeout(r))`;\n * (d) the iterable's name/derivation signals ordering — `*Sorted*`,\n * `.reverse()`, lifecycle `hooks`, pipeline `stages`, etc.\n * (e) a `while` / `do-while` whose CONTINUATION depends on state the body\n * computes — cursor pagination:\n * `do { const page = await list(cursor); cursor = page.nextCursor } while (cursor)`.\n * The next request's input is the previous response's output, so the\n * iteration count is unknowable up front and there is nothing to collect\n * into a `Promise.all`. Guard (b) misses this because the cursor is\n * assigned from a property of the awaited value, not from the await\n * itself; guard (a) misses it because the `while` test IS the exit.\n * Deliberately narrow: only a plain `=` assignment to a test-read\n * variable counts, so a hand-rolled counter loop (`while (i < n) { i++;\n * await g(i) }`) still fires.\n * The genuinely parallelizable shape — independent awaits whose results are\n * collected or discarded without a cross-iteration dependency or early exit —\n * still fires.\n *\n * At most one report is emitted per offending loop / callback.\n *\n * CORPUS SWEEP (2220 files across zod / TanStack Query / react-router / swr /\n * zustand, 2026-07): 64 raw hits. Three further suppressions came out of it:\n *\n * (f) TEST AND BENCHMARK FILES — 45 hits. A loop in a test is a table-driven\n * case sweep or a teardown, and its iterations share one global fixture,\n * so serial IS the contract.\n * `react-router/packages/react-router/__tests__/dom/special-characters-test.tsx:312`\n * (`for (let charDef of specialChars) { await testParamValues(...) }`, 10\n * hits in that file alone) mounts and unmounts a React tree into the same\n * JSDOM container per case; `Promise.all` would interleave them into one\n * container and the suite would simply be wrong. Teardown has the same\n * shape —\n * `react-router/packages/create-react-router/__tests__/create-react-router-test.ts:77`\n * (`for (let dir of tempDirs) await rm(dir, ...)`). A BENCHMARK is the\n * purest case: `zod/packages/bench/object-async.ts:6`\n * (`for (const _ of DATA) await zod3.parseAsync(_)`) exists precisely to\n * measure one operation at a time; concurrency would destroy the number\n * it is trying to produce.\n * (g) `await Promise.resolve()` / `await Promise.reject()` — a microtask\n * flush used to let queued work settle, the microtask twin of the\n * `await new Promise(setTimeout)` already covered by (c). There is no I/O\n * to parallelize. `query/packages/lit-query/src/tests/infinite-and-options.test.ts:207`\n * (`for (let i = 0; i < 5; i += 1) { host.update(); await Promise.resolve() }`).\n * (h) `preset` / `plugin` / `extension` added to the ordered-iterable hints —\n * a preset or plugin list is a lifecycle chain exactly like the `hook` and\n * `middleware` entries already there, and each entry may observe what the\n * previous one wrote. `react-router/packages/react-router-dev/config/config.ts:776`\n * (`for (let preset of reactRouterUserConfig.presets ?? []) await\n * preset.reactRouterConfigResolved?.({ reactRouterConfig })`) threads one\n * mutable `reactRouterConfig` through every preset in order.\n */\n\nimport { ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\nimport { isTestFile } from \"./_paths.js\";\n\ntype MessageIds = \"noSequentialAwait\";\ntype Options = readonly [];\n\n/**\n * Array iteration methods whose async callback with an `await` inside is the\n * classic serial-await footgun (`.forEach` ignores the returned promise\n * entirely; a discarded `.map` / `.filter` floats its promises).\n */\nconst ARRAY_ITERATION_METHODS = new Set<string>([\"forEach\", \"map\", \"filter\"]);\n\n/**\n * Text on the iterable (its identifier name or derivation) that signals an\n * ORDERED sequence whose elements must be processed one-after-another —\n * lifecycle hooks, sorted / reversed lists, pipeline stages. Awaiting serially\n * over such a sequence is the contract, so the report is suppressed. Broad by\n * design: the rule prefers a false negative to a false positive.\n */\nconst SEQUENTIAL_ITERABLE_HINT =\n /sort|reverse|ordered|sequence|hook|middleware|pipeline|preset|plugin|extension|\\bstage|\\bstep|\\bphase|migration|chain|buffer|stream|teleport|chunk|\\bqueue|drain/i;\n\n/**\n * Guard (f): a benchmark file. A benchmark loop measures ONE operation at a\n * time; running the iterations concurrently would destroy the measurement, so\n * the serial await is the entire point.\n */\nconst BENCH_FILE_RE = /(^|[\\\\/])bench(marks?)?[\\\\/]|\\.bench\\.[cm]?[jt]sx?$/i;\n\n/**\n * Node types that introduce a new function scope. We never descend across one\n * of these when looking for awaits belonging to a loop, because an `await`\n * inside a nested function/arrow is awaited by *that* function, not by the loop.\n */\nfunction isFunctionLike(node: TSESTree.Node): boolean {\n return (\n node.type === \"FunctionDeclaration\" ||\n node.type === \"FunctionExpression\" ||\n node.type === \"ArrowFunctionExpression\"\n );\n}\n\ntype LoopNode =\n | TSESTree.ForStatement\n | TSESTree.ForOfStatement\n | TSESTree.ForInStatement\n | TSESTree.WhileStatement\n | TSESTree.DoWhileStatement;\n\n/**\n * Statement node types that introduce a nested loop. When scanning a loop's\n * body we must NOT descend into another loop — that inner loop's awaits belong\n * to it and are reported when the inner loop is visited. This keeps reporting\n * at one-per-loop and avoids double-flagging an outer loop for an await that\n * only appears inside a nested loop.\n */\nfunction isLoop(node: TSESTree.Node): boolean {\n return (\n node.type === \"ForStatement\" ||\n node.type === \"ForOfStatement\" ||\n node.type === \"ForInStatement\" ||\n node.type === \"WhileStatement\" ||\n node.type === \"DoWhileStatement\"\n );\n}\n\nfunction isNode(value: unknown): value is TSESTree.Node {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as { type?: unknown }).type === \"string\"\n );\n}\n\n/**\n * Visits `root` and its descendants, calling `visit` on each, without crossing\n * into a nested function scope or descending into a nested loop — the same\n * ownership boundaries the rule uses to decide which awaits belong to a loop.\n */\nfunction visitScope(\n root: TSESTree.Node,\n visit: (node: TSESTree.Node) => void,\n): void {\n visit(root);\n for (const key of Object.keys(root) as (keyof TSESTree.Node)[]) {\n if (key === \"parent\") {\n continue;\n }\n const value = root[key];\n const children = Array.isArray(value) ? value : [value];\n for (const child of children) {\n if (isNode(child) && !isFunctionLike(child) && !isLoop(child)) {\n visitScope(child, visit);\n }\n }\n }\n}\n\nfunction collectAwaits(root: TSESTree.Node): TSESTree.AwaitExpression[] {\n const awaits: TSESTree.AwaitExpression[] = [];\n visitScope(root, (node) => {\n if (node.type === \"AwaitExpression\") {\n awaits.push(node);\n }\n });\n return awaits;\n}\n\n/**\n * Guard (i): the loop body ASSERTS. A loop containing `expect(...)` (or\n * `assert(...)`) is sweeping a table of cases, and its iterations share one\n * fixture — a browser page, a rendered tree, a temp directory. Running them\n * concurrently interleaves the assertions so a failure names the wrong case,\n * and for a single-page driver it is not even possible.\n *\n * This is the syntactic form of guard (f): react-router spells its suites\n * `integration/vite-css-test.ts`, which no path predicate matching `*.test.ts`\n * can see. Corpus evidence (2026-07): 17 of the 31 hits surviving guard (f) were\n * exactly this — `react-router/integration/vite-server-bundles-test.ts:282`\n * (`for (let path of _404s) { let response = await page.goto(...);\n * expect(response?.status()).toBe(404) }`, one Playwright page that cannot\n * navigate to two URLs at once) and\n * `react-router/integration/sri-test.ts:121`.\n */\nconst ASSERTION_CALLEE_RE = /^(expect|assert|should|invariant)$/;\n\n/**\n * Guard (a): the scope contains a `return` / `break` / `continue`, marking a\n * retry / short-circuit / early-exit loop whose serial awaits are intentional.\n */\nfunction hasEarlyExit(root: TSESTree.Node): boolean {\n let found = false;\n visitScope(root, (node) => {\n if (\n node.type === \"ReturnStatement\" ||\n node.type === \"BreakStatement\" ||\n node.type === \"ContinueStatement\"\n ) {\n found = true;\n }\n });\n return found;\n}\n\n/**\n * Guard (c): `await new Promise(...)` — a deliberate event-loop / timer yield,\n * not parallelizable I/O.\n */\nconst TIMER_HELPER_RE = /^(sleep|timeout|delay|wait|pause|tick)$/i;\n\nfunction calleeName(callee: TSESTree.Node): string | null {\n if (callee.type === \"Identifier\") return callee.name;\n if (\n callee.type === \"MemberExpression\" &&\n !callee.computed &&\n callee.property.type === \"Identifier\"\n ) {\n return callee.property.name;\n }\n return null;\n}\n\nfunction isTimerYield(node: TSESTree.AwaitExpression): boolean {\n const arg = node.argument;\n // `await new Promise((r) => setTimeout(r))` — the canonical event-loop yield.\n if (\n arg.type === \"NewExpression\" &&\n arg.callee.type === \"Identifier\" &&\n arg.callee.name === \"Promise\"\n ) {\n return true;\n }\n // `await sleep(ms)` / `await delay()` / `await this.wait()` — a poll/throttle\n // yield in a `while` loop, deliberately serial.\n if (arg.type === \"CallExpression\") {\n // Guard (g): `await Promise.resolve()` / `await Promise.reject()` is a\n // microtask flush — the microtask twin of the `new Promise(setTimeout)`\n // yield above. There is no I/O in it to run concurrently.\n if (\n arg.callee.type === \"MemberExpression\" &&\n !arg.callee.computed &&\n arg.callee.object.type === \"Identifier\" &&\n arg.callee.object.name === \"Promise\" &&\n arg.callee.property.type === \"Identifier\" &&\n (arg.callee.property.name === \"resolve\" ||\n arg.callee.property.name === \"reject\")\n ) {\n return true;\n }\n const name = calleeName(arg.callee);\n return name !== null && TIMER_HELPER_RE.test(name);\n }\n return false;\n}\n\nconst QUEUE_DRAIN_METHODS = /^(shift|pop|dequeue|next|poll)$/;\n\n/**\n * Guard: `await queue.shift()` / `await this.stack.pop()` — draining a mutable\n * receiver one element at a time. Each call consumes the shared queue, so the\n * iterations are order-dependent and cannot be parallelized.\n */\nfunction isQueueDrain(node: TSESTree.AwaitExpression): boolean {\n const arg = node.argument;\n return (\n arg.type === \"CallExpression\" &&\n arg.callee.type === \"MemberExpression\" &&\n !arg.callee.computed &&\n arg.callee.property.type === \"Identifier\" &&\n QUEUE_DRAIN_METHODS.test(arg.callee.property.name)\n );\n}\n\n/** Guard (i): does this scope contain an assertion call? */\nfunction hasAssertion(root: TSESTree.Node): boolean {\n let found = false;\n visitScope(root, (node) => {\n if (node.type !== \"CallExpression\") {\n return;\n }\n let callee: TSESTree.Node = node.callee;\n // Unwrap `expect(x).resolves`, `assert.ok`, `chai.expect` — the base name.\n while (callee.type === \"MemberExpression\") {\n callee = callee.object;\n }\n if (callee.type === \"CallExpression\") {\n callee = callee.callee;\n }\n if (callee.type === \"Identifier\" && ASSERTION_CALLEE_RE.test(callee.name)) {\n found = true;\n }\n });\n return found;\n}\n\nfunction referencesName(root: TSESTree.Node, name: string): boolean {\n let found = false;\n visitScope(root, (node) => {\n if (node.type === \"Identifier\" && node.name === name) {\n found = true;\n }\n });\n return found;\n}\n\n/**\n * Guard (b): the awaited value is threaded into the next iteration — the await\n * is the RHS of `target = await ...` / `const target = await ...` and `target`\n * is read inside the awaited expression (`x = await f(x)`,\n * `content = await p(content)`). Parallelizing would break the data dependency.\n */\nfunction isThreadedAccumulator(node: TSESTree.AwaitExpression): boolean {\n const parent = node.parent;\n let target: string | null = null;\n if (\n parent.type === \"AssignmentExpression\" &&\n parent.operator === \"=\" &&\n parent.right === node &&\n parent.left.type === \"Identifier\"\n ) {\n target = parent.left.name;\n } else if (\n parent.type === \"VariableDeclarator\" &&\n parent.init === node &&\n parent.id.type === \"Identifier\"\n ) {\n target = parent.id.name;\n }\n if (target === null) {\n return false;\n }\n return referencesName(node.argument, target);\n}\n\n/** Every identifier name read by a loop's test expression. */\nfunction namesReadBy(test: TSESTree.Node): Set<string> {\n const names = new Set<string>();\n visitScope(test, (node) => {\n if (node.type === \"Identifier\") {\n names.add(node.name);\n }\n });\n return names;\n}\n\n/**\n * Guard (e): a `while` / `do-while` whose continuation condition is recomputed\n * by its own body — the cursor-pagination shape. True when the body plainly\n * assigns (`=`) to a variable the test reads. `++` / `+=` are excluded so a\n * hand-rolled counter loop, which IS parallelizable, keeps firing.\n */\nfunction testStateIsAssignedInBody(\n test: TSESTree.Node,\n body: TSESTree.Node,\n): boolean {\n const testNames = namesReadBy(test);\n if (testNames.size === 0) {\n return false;\n }\n let found = false;\n visitScope(body, (node) => {\n if (\n node.type === \"AssignmentExpression\" &&\n node.operator === \"=\" &&\n node.left.type === \"Identifier\" &&\n testNames.has(node.left.name)\n ) {\n found = true;\n }\n });\n return found;\n}\n\n/**\n * Decides whether a set of awaits owned by a loop/callback is worth reporting.\n * Suppressed when the loop short-circuits (a) or iterates an ordered sequence\n * (d); otherwise reported if AT LEAST ONE await is genuinely parallelizable —\n * i.e. not a timer yield (c) and not a threaded accumulator (b).\n */\nfunction shouldReport(\n awaits: readonly TSESTree.AwaitExpression[],\n earlyExit: boolean,\n iterableText: string | null,\n asserts = false,\n): boolean {\n if (awaits.length === 0) {\n return false;\n }\n if (earlyExit) {\n return false;\n }\n if (asserts) {\n return false;\n }\n if (iterableText !== null && SEQUENTIAL_ITERABLE_HINT.test(iterableText)) {\n return false;\n }\n return awaits.some(\n (node) =>\n !isTimerYield(node) &&\n !isThreadedAccumulator(node) &&\n !isQueueDrain(node),\n );\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-sequential-await\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Disallow serial `await` inside a loop; use `await Promise.all(...)` to run the operations concurrently.\",\n },\n schema: [],\n messages: {\n noSequentialAwait:\n \"Avoid `await` inside a loop — it serializes I/O. Collect the promises and `await Promise.all(xs.map(async (x) => ...))` instead.\",\n },\n },\n defaultOptions: [],\n create(context) {\n // Guard (f): fixtures and benchmarks serialize on purpose — see @fileoverview.\n if (isTestFile(context.filename) || BENCH_FILE_RE.test(context.filename)) {\n return {};\n }\n\n /**\n * The parts of a loop where an await \"belonging to this loop\" can live: the\n * body, plus the C-style `for` init/test/update or the for-of/for-in\n * iterable or the while/do-while test. A part that is itself a nested loop\n * is owned by that loop and checked when it is visited.\n */\n function loopParts(node: LoopNode): (TSESTree.Node | null)[] {\n // Only parts that run PER ITERATION can serialize awaits. The C-`for`\n // init and the for-of/for-in iterable are evaluated ONCE, so an `await`\n // there (`for (const x of await Promise.allSettled(...))`) is a single\n // await, not a loop-serialized one — exclude them.\n if (node.type === \"ForStatement\") {\n return [node.body, node.test, node.update];\n }\n if (node.type === \"ForOfStatement\" || node.type === \"ForInStatement\") {\n return [node.body];\n }\n return [node.body, node.test];\n }\n\n function iterableTextOf(node: LoopNode): string | null {\n if (node.type === \"ForOfStatement\" || node.type === \"ForInStatement\") {\n return context.sourceCode.getText(node.right);\n }\n return null;\n }\n\n function checkLoop(node: LoopNode): void {\n if (\n (node.type === \"WhileStatement\" || node.type === \"DoWhileStatement\") &&\n testStateIsAssignedInBody(node.test, node.body)\n ) {\n return;\n }\n\n const awaits: TSESTree.AwaitExpression[] = [];\n let earlyExit = false;\n let asserts = false;\n for (const part of loopParts(node)) {\n if (part === null || isLoop(part)) {\n continue;\n }\n awaits.push(...collectAwaits(part));\n if (!earlyExit && hasEarlyExit(part)) {\n earlyExit = true;\n }\n if (!asserts && hasAssertion(part)) {\n asserts = true;\n }\n }\n if (shouldReport(awaits, earlyExit, iterableTextOf(node), asserts)) {\n context.report({ node, messageId: \"noSequentialAwait\" });\n }\n }\n\n return {\n ForStatement: checkLoop,\n ForInStatement: checkLoop,\n WhileStatement: checkLoop,\n DoWhileStatement: checkLoop,\n ForOfStatement(node: TSESTree.ForOfStatement): void {\n // `for await...of` is correct async iteration — never flag it.\n if (node.await) {\n return;\n }\n checkLoop(node);\n },\n CallExpression(node: TSESTree.CallExpression): void {\n const callee = node.callee;\n if (callee.type !== \"MemberExpression\" || callee.computed) {\n return;\n }\n if (\n callee.property.type !== \"Identifier\" ||\n !ARRAY_ITERATION_METHODS.has(callee.property.name)\n ) {\n return;\n }\n const callback = node.arguments[0];\n if (\n callback === undefined ||\n !isFunctionLike(callback) ||\n !(\"async\" in callback && callback.async)\n ) {\n return;\n }\n // A discarded `.map` / `.filter` (statement position) floats its\n // promises just like `.forEach`; when its result is kept\n // (`Promise.all(...)`, an assignment, a `return`) the awaits are\n // consumed correctly, so only flag `.forEach` or a bare-statement call.\n if (\n callee.property.name !== \"forEach\" &&\n node.parent.type !== \"ExpressionStatement\"\n ) {\n return;\n }\n const awaits = collectAwaits(callback.body);\n const earlyExit = hasEarlyExit(callback.body);\n const iterableText = context.sourceCode.getText(callee.object);\n if (\n shouldReport(awaits, earlyExit, iterableText, hasAssertion(callback.body))\n ) {\n context.report({ node, messageId: \"noSequentialAwait\" });\n }\n },\n };\n },\n});\n","/**\n * @fileoverview Disallow string accumulation via `+=` inside a loop, which is\n * the classic O(n^2) string-building antipattern: each `+=` rebuilds the whole\n * string. Push the parts onto an array and `arr.join(\"\")` after the loop\n * instead.\n *\n * This is a purely SYNTACTIC rule — it uses scope analysis (not the type\n * service) to confirm the left-hand side was declared with a string-literal\n * initializer (`let s = \"\"`, `= \"...\"`, or a template literal). It is\n * deliberately conservative: when the initializer type cannot be determined\n * (no initializer, a non-literal expression, a parameter, etc.) the `+=` is\n * NOT flagged. This mirrors the Python rule SARJ002.\n *\n * Both the compound `s += x` and the equivalent longhand `s = s + x` (a plain\n * `=` assignment whose RHS is a `+` `BinaryExpression` with the target as one\n * operand) are detected — they have identical O(n^2) behavior. A plain\n * `s = x + y` (target absent from the RHS) is NOT flagged.\n *\n * AT MOST ONE REPORT PER (accumulator, loop). A single loop that appends in\n * several branches is ONE defect with ONE fix — replace the accumulator with an\n * array of parts — so N reports on it are N-1 copies of the same finding, and\n * silencing it costs N disable comments. Corpus sweep (2220 files across zod /\n * TanStack Query / react-router / swr / zustand, 2026-07): 62 raw reports\n * collapsed to 21 distinct defects. `react-router/packages/react-router-fs-routes/flatRoutes.ts`\n * alone produced 23 reports; `react-router/packages/react-router/lib/server-runtime/cookies.ts:221-231`\n * produced 6, all of them the one percent-encoder loop appending `result` from\n * four branches. This matches the one-report-per-loop policy `no-sequential-await`\n * already follows.\n *\n * EXEMPTION — AN ACCUMULATOR DECLARED INSIDE THE LOOP BODY (bulbul PR #4111).\n * The O(n^2) claim requires the accumulator to survive across iterations. A\n * `let s = \"...\"` declared *inside* the body is rebound to a fresh string every\n * pass, so the `+=` runs a bounded number of times on a string that is discarded\n * at the end of the iteration — there is no quadratic growth for `join` to\n * remove, and the parts are typically already being collected into an array.\n * Evidence:\n * `typescript/packages/app/src/lib/lexical/plugins/utils/serializes-state-to-text.ts:16`,\n * where `let sectionText = \\`## ${…}\\`` is declared in the body, appended to at\n * most once behind an `if (body)`, and then pushed onto `textParts` for a\n * `textParts.join()` after the loop. The disable there reads \"single conditional\n * append to a per-iteration string; result is collected via textParts.join below,\n * not O(n²)\" — the rule had nothing to offer.\n *\n * The Python twin SARJ002 (`inefficient_string_concat_in_loop`) has drawn this\n * same line since it shipped: \"A target that is freshly (re)bound earlier in the\n * same loop body … is loop-local: it starts empty each iteration, so the growth\n * is bounded, not cross-iteration accumulation.\" The two rules now agree.\n */\n\nimport { ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\nimport type { Scope } from \"@typescript-eslint/utils/ts-eslint\";\n\nimport { isGeneratedFile } from \"./_paths.js\";\n\ntype MessageIds = \"noStringConcatInLoop\";\ntype Options = readonly [];\n\nconst LOOP_NODE_TYPES = new Set<string>([\n \"ForStatement\",\n \"ForOfStatement\",\n \"ForInStatement\",\n \"WhileStatement\",\n \"DoWhileStatement\",\n]);\n\n/**\n * Returns true if the given expression node is a string-producing literal:\n * a string `Literal` (`\"\"`, `\"...\"`, `'...'`) or a `TemplateLiteral`.\n */\nfunction isStringLiteralInit(node: TSESTree.Expression | null): boolean {\n if (node === null) {\n return false;\n }\n if (node.type === \"TemplateLiteral\") {\n return true;\n }\n if (node.type === \"Literal\") {\n return typeof node.value === \"string\";\n }\n return false;\n}\n\n/**\n * Walk the chain of enclosing scopes to find the variable definition for the\n * given identifier name. Returns the resolved `Variable`, or `undefined` if it\n * cannot be found (e.g. an undeclared global or an out-of-scope reference).\n */\nfunction findVariable(\n scope: Scope.Scope,\n name: string,\n): Scope.Variable | undefined {\n let current: Scope.Scope | null = scope;\n while (current !== null) {\n const variable = current.variables.find((v) => v.name === name);\n if (variable !== undefined) {\n return variable;\n }\n current = current.upper;\n }\n return undefined;\n}\n\n/**\n * Returns true if the variable was declared with a string-literal initializer.\n * Conservative: if the variable has no single string-initialized declarator\n * (no init, non-literal init, multiple conflicting declarators), returns false.\n */\nfunction isStringInitializedVariable(variable: Scope.Variable): boolean {\n // A variable can technically have multiple declarators (e.g. via `var`\n // hoisting / redeclaration). Only treat it as string-initialized when there\n // is exactly one declarator and it has a string-literal initializer.\n if (variable.defs.length !== 1) {\n return false;\n }\n const def = variable.defs[0];\n if (def === undefined || def.type !== \"Variable\") {\n // Parameters, function names, imports, etc. — type unknown, don't flag.\n return false;\n }\n const declarator = def.node;\n if (declarator.type !== \"VariableDeclarator\") {\n return false;\n }\n return isStringLiteralInit(declarator.init);\n}\n\n/**\n * Returns true if `target` appears as a direct operand of a `+` expression\n * (following left-associative `+` chains, e.g. `s + a + b`). Used to recognize\n * the longhand `s = s + <...>` reassignment, which has the same O(n^2) cost as\n * `s += <...>`. A non-`+` operand (`foo.s`, `s.slice()`, `x + y`) does not match.\n */\nfunction isConcatOperand(\n node: TSESTree.Expression | TSESTree.PrivateIdentifier,\n target: string,\n): boolean {\n if (node.type === \"Identifier\") {\n return node.name === target;\n }\n if (node.type === \"BinaryExpression\" && node.operator === \"+\") {\n return (\n isConcatOperand(node.left, target) || isConcatOperand(node.right, target)\n );\n }\n return false;\n}\n\n/**\n * Returns true if `rhs` is a `+` `BinaryExpression` in which `target` appears as\n * an operand — the reassignment shape `s = s + x` / `s = x + s` / `s = s + a + b`.\n */\nfunction isConcatOntoTarget(\n rhs: TSESTree.Expression,\n target: string,\n): boolean {\n if (rhs.type !== \"BinaryExpression\" || rhs.operator !== \"+\") {\n return false;\n }\n return isConcatOperand(rhs.left, target) || isConcatOperand(rhs.right, target);\n}\n\n/**\n * Returns true when the accumulator's declaration lives inside `loop`'s BODY, so\n * a fresh string is bound on every iteration.\n *\n * `let s = \"\"` before the loop is the cross-iteration accumulator this rule\n * targets. `let s = \"\"` inside the body is not: the `+=` runs a bounded number of\n * times against a string that is discarded at the end of the pass, and there is\n * no quadratic growth to remove — the parts are usually already collected into an\n * array afterwards. Compared by source range, which is exact for a declaration\n * that is lexically nested in the body.\n */\nfunction isDeclaredInsideLoop(\n variable: Scope.Variable,\n loop: TSESTree.Node,\n): boolean {\n const def = variable.defs[0];\n if (def === undefined) {\n return false;\n }\n const body = (\n loop as\n | TSESTree.ForStatement\n | TSESTree.ForOfStatement\n | TSESTree.ForInStatement\n | TSESTree.WhileStatement\n | TSESTree.DoWhileStatement\n ).body;\n const [declStart, declEnd] = def.node.range;\n const [bodyStart, bodyEnd] = body.range;\n return declStart >= bodyStart && declEnd <= bodyEnd;\n}\n\n/**\n * Returns true if `node` is contained within the body of a loop statement.\n * Walks ancestors and, for each loop, ensures the node is inside the loop's\n * BODY (not its test/init/update clauses, which run a bounded number of times\n * relative to the body and aren't the antipattern we target).\n */\nfunction enclosingLoop(node: TSESTree.Node): TSESTree.Node | null {\n let child: TSESTree.Node = node;\n let parent = node.parent;\n while (parent !== undefined && parent !== null) {\n if (LOOP_NODE_TYPES.has(parent.type)) {\n // `body` is the property that holds the looped statements for every\n // loop variant we care about.\n const loop = parent as\n | TSESTree.ForStatement\n | TSESTree.ForOfStatement\n | TSESTree.ForInStatement\n | TSESTree.WhileStatement\n | TSESTree.DoWhileStatement;\n if (loop.body === child) {\n return loop;\n }\n }\n child = parent;\n parent = parent.parent;\n }\n return null;\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-string-concat-in-loop\",\n meta: {\n type: \"suggestion\",\n docs: {\n description:\n \"Disallow O(n^2) string building via `+=` on a string variable inside a loop; push parts to an array and `join` instead.\",\n },\n schema: [],\n messages: {\n noStringConcatInLoop:\n \"Avoid building a string with `+=` inside a loop — this is O(n^2). Push the parts onto an array and use `arr.join(\\\"\\\")` after the loop.\",\n },\n },\n defaultOptions: [],\n create(context) {\n if (isGeneratedFile(context.filename, context.sourceCode.text)) {\n return {};\n }\n\n // One defect per (accumulator, loop) — see @fileoverview. Keyed on the loop\n // node so sibling loops over the same variable each keep their report.\n const reported = new WeakMap<TSESTree.Node, Set<string>>();\n\n return {\n AssignmentExpression(node: TSESTree.AssignmentExpression): void {\n // The LHS must be a plain variable reference.\n if (node.left.type !== \"Identifier\") {\n return;\n }\n // Accept both the compound `s += x` and the longhand `s = s + x`; any\n // other operator/shape (`s = x + y`, `s -= x`, ...) is not accumulation.\n const isAccumulation =\n node.operator === \"+=\" ||\n (node.operator === \"=\" &&\n isConcatOntoTarget(node.right, node.left.name));\n if (!isAccumulation) {\n return;\n }\n // Must occur inside a loop body, else it's a one-shot append.\n const loop = enclosingLoop(node);\n if (loop === null) {\n return;\n }\n\n const scope = context.sourceCode.getScope(node);\n const variable = findVariable(scope, node.left.name);\n // Conservative: can't resolve the declaration -> don't flag.\n if (variable === undefined) {\n return;\n }\n // Only flag when we can confirm the LHS was string-initialized; a\n // numeric initializer (or anything non-string) is intentionally\n // excluded to avoid false positives.\n if (!isStringInitializedVariable(variable)) {\n return;\n }\n // The O(n^2) claim requires the accumulator to SURVIVE across iterations.\n // One declared inside the loop body is a fresh string every pass, so its\n // length is bounded by one iteration's work and `join` cannot replace it.\n // See @fileoverview for the PR #4111 evidence.\n if (isDeclaredInsideLoop(variable, loop)) {\n return;\n }\n\n let seen = reported.get(loop);\n if (seen === undefined) {\n seen = new Set<string>();\n reported.set(loop, seen);\n }\n if (seen.has(node.left.name)) {\n return;\n }\n seen.add(node.left.name);\n\n context.report({\n node,\n messageId: \"noStringConcatInLoop\",\n });\n },\n };\n },\n});\n","/**\n * @fileoverview Flag `'use client'` files with no hooks or event handlers.\n *\n * If a file is marked `'use client'` but contains no hook calls\n * (`useState`/`useEffect`/etc.), no JSX event handlers (`onClick`,\n * `onChange`, etc.), no browser globals, no client-only imports, and no\n * other client-side indicators (classes, re-exports), the directive is\n * likely unnecessary and the file could be a React Server Component —\n * improving cold-start, bundle size, and SEO.\n *\n * False-positive watch: components that only use client-side context\n * (e.g. theme providers) without hooks or events still need `'use client'`.\n *\n * TWO MORE CLIENT INDICATORS, ADDED AFTER A CORPUS SWEEP (2220 files across\n * zod / TanStack Query / react-router / swr / zustand, 2026-07 — 12 hits, all\n * false positives):\n *\n * 1. **Rendering a component imported from a THIRD-PARTY package.** React\n * documents `'use client'` at the top of a wrapper as the way to mark a\n * dependency's components as client components, and this rule cannot see\n * into `node_modules` to know whether the dependency needs it.\n * `CLIENT_ONLY_PACKAGES_REGEX` was a hand-maintained approximation of the\n * same idea and is necessarily incomplete — it did not know about\n * `fumadocs-ui` (`zod/packages/docs/components/tabs.tsx:1`, which renders\n * `<Primitive.Tabs>`), about `swr` itself\n * (`swr/examples/suspense-global/global-swr-config.tsx:1`, a `<SWRConfig>`\n * provider), or about `next/image` and `lucide-react`\n * (`zod/packages/docs/components/themed-image.tsx:1`,\n * `.../heading.tsx:1`). A component imported by a RELATIVE path lives in\n * the same repo, is linted by this same rule, and still fires — so the\n * narrowing costs nothing where the rule can actually see the answer.\n * 2. **Aliasing an import into a public export** —\n * `import * as Devtools from './ReactQueryDevtools';\n * export const ReactQueryDevtools = … Devtools.ReactQueryDevtools`\n * (`query/packages/react-query-devtools/src/index.ts:1`, and `production.ts`).\n * That is a re-export written the long way, and `export … from` was already\n * treated as an indicator; the two spellings now agree.\n *\n * ONE MORE INDICATOR FROM bulbul PR #4111:\n *\n * 3. **Importing `next/dynamic`.** In the App Router, `dynamic(…, { ssr: false })`\n * is a hard BUILD ERROR inside a Server Component — Next.js rejects it with\n * \"`ssr: false` is not allowed with `next/dynamic` in Server Components\".\n * A lazy-wrapper module therefore has NO legal form without the directive,\n * so the rule was demanding something the framework forbids and the only\n * available response was a disable comment. These wrappers also look\n * maximally \"unnecessary\" to the old predicate: one `dynamic()` call, no\n * hooks, no handlers, no browser globals.\n * `typescript/packages/app/src/app/dashboard/call-volume-chart-lazy.tsx:1`\n * (deferring the recharts bundle off the server dashboard page) and\n * `typescript/packages/app/src/components/rich-text-editor/rich-text-editor-lazy.tsx:1`\n * (deferring the Lexical bundle off /agents and /admin/global-prompts).\n *\n * HONEST SCOPE: both of those files also happen to be covered by indicator\n * 2, since each EXPORTS a const whose initializer reads the imported\n * `dynamic`. Indicator 3 is therefore defense-in-depth, not the thing that\n * currently silences them: it is what covers the same wrapper when the lazy\n * component is module-internal (`const Editor = dynamic(…); export function\n * Page() { return <Editor />; }`), where indicator 2 does not reach and the\n * framework constraint is identical. The test suite pins exactly that shape.\n *\n * All FOUR of the repo's `no-unnecessary-use-client` disables are consequently\n * stale rather than live false positives — including\n * `app/src/app/scenarios/organization-selector-wrapper.tsx:1` and its batch-calls\n * twin, which pass a hook adapter as a function prop and are already exempt via\n * indicator 2. The valid-case suite pins all of them so a future narrowing of\n * indicator 2 cannot silently reintroduce the reports.\n *\n * References:\n * - https://nextjs.org/docs/app/building-your-application/rendering/client-components\n * - https://react.dev/reference/rsc/use-client (wrapping third-party components)\n * - https://nextjs.org/docs/app/api-reference/functions/dynamic (`ssr: false`)\n */\n\nimport {\n AST_NODE_TYPES,\n ESLintUtils,\n type TSESTree,\n} from \"@typescript-eslint/utils\";\nimport type { RuleContext, Scope } from \"@typescript-eslint/utils/ts-eslint\";\n\ntype MessageIds = \"unnecessaryUseClient\";\ntype Options = readonly [];\n\nconst HOOK_REGEX = /^use([A-Z]|$)/;\nconst EVENT_PROP_REGEX = /^on[A-Z]/;\nconst ERROR_FILE_REGEX = /\\b(?:global-)?error\\.[jt]sx?$/;\n\nconst BROWSER_GLOBALS: ReadonlySet<string> = new Set([\n \"window\",\n \"document\",\n \"navigator\",\n \"localStorage\",\n \"sessionStorage\",\n \"location\",\n \"history\",\n \"screen\",\n \"requestAnimationFrame\",\n \"cancelAnimationFrame\",\n \"CustomEvent\",\n \"Event\",\n \"MouseEvent\",\n \"KeyboardEvent\",\n \"TouchEvent\",\n]);\n\n/**\n * Modules whose very import forces a client boundary, independent of what the\n * file then does with them. `next/dynamic` is the case that matters: in the App\n * Router, `dynamic(..., { ssr: false })` is a BUILD ERROR in a Server Component\n * (\"`ssr: false` is not allowed with `next/dynamic` in Server Components\"), so a\n * lazy-wrapper module has no legal form without `'use client'`. See @fileoverview.\n */\nconst CLIENT_REQUIRED_MODULES: ReadonlySet<string> = new Set([\"next/dynamic\"]);\n\nconst CLIENT_ONLY_PACKAGES_REGEX =\n /^(?:@radix-ui\\/|framer-motion|react-dom|react-day-picker|@floating-ui\\/|react-select|react-toastify|react-hook-form|recharts|react-dropzone|react-slick|react-swipeable|react-resizable|react-draggable|react-beautiful-dnd|@hello-pangea\\/dnd|react-virtualized|react-window|@tanstack\\/react-table|@tanstack\\/react-query|react-redux|recoil|jotai|zustand|@tippyjs\\/react|react-color|react-datepicker|next-themes|react-helmet|react-helmet-async|styled-components|@emotion\\/)/;\n\ntype Ctx = Readonly<RuleContext<MessageIds, Options>>;\n\n/**\n * True for a package specifier — anything that is not a relative path or one of\n * the usual in-repo alias prefixes. A relative/aliased import points at code in\n * this repo, which this same rule already lints, so the \"cannot see into the\n * dependency\" argument does not apply to it.\n */\nconst isBareSpecifier = (source: string): boolean =>\n !source.startsWith(\".\") && !source.startsWith(\"/\") && !source.startsWith(\"@/\") && !source.startsWith(\"~\");\n\n/** The leftmost identifier of a JSX element name: `Primitive.Tabs` -> `Primitive`. */\nconst jsxRootName = (name: TSESTree.JSXTagNameExpression): string => {\n let current: TSESTree.JSXTagNameExpression = name;\n while (current.type === AST_NODE_TYPES.JSXMemberExpression) {\n current = current.object;\n }\n return current.type === AST_NODE_TYPES.JSXIdentifier ? current.name : \"\";\n};\n\n/** True when any identifier in `node`'s subtree names an imported binding. */\nconst subtreeReadsImportedBinding = (\n node: TSESTree.Node,\n imported: ReadonlySet<string>,\n): boolean => {\n if (node.type === AST_NODE_TYPES.Identifier) {\n return imported.has(node.name);\n }\n for (const key of Object.keys(node) as (keyof TSESTree.Node)[]) {\n if (key === \"parent\") continue;\n const value = node[key];\n for (const child of (Array.isArray(value) ? value : [value]) as unknown[]) {\n if (\n child !== null &&\n typeof child === \"object\" &&\n typeof (child as { type?: unknown }).type === \"string\" &&\n subtreeReadsImportedBinding(child as TSESTree.Node, imported)\n ) {\n return true;\n }\n }\n }\n return false;\n};\n\nconst isUseClientDirective = (\n node: TSESTree.Statement,\n): node is TSESTree.ExpressionStatement => {\n return (\n node.type === AST_NODE_TYPES.ExpressionStatement &&\n node.expression.type === AST_NODE_TYPES.Literal &&\n node.expression.value === \"use client\"\n );\n};\n\nconst isGlobalReference = (\n node: TSESTree.Identifier,\n context: Ctx,\n): boolean => {\n if (!BROWSER_GLOBALS.has(node.name)) return false;\n\n const parent = node.parent;\n if (parent !== undefined) {\n // `obj.window` — `window` is a property name, not a global reference.\n if (\n parent.type === AST_NODE_TYPES.MemberExpression &&\n parent.property === node &&\n !parent.computed\n ) {\n return false;\n }\n // `{ window: ... }` — property key, not a global reference.\n if (\n parent.type === AST_NODE_TYPES.Property &&\n parent.key === node &&\n !parent.computed\n ) {\n return false;\n }\n // Type annotations / type-only positions are not runtime references.\n if (parent.type.startsWith(\"TS\")) {\n return false;\n }\n }\n\n // If there's a local binding for this name anywhere up the chain, it's not\n // a reference to the browser global.\n let scope: Scope.Scope | null = context.sourceCode.getScope(node);\n while (scope !== null) {\n const variable = scope.set.get(node.name);\n if (variable !== undefined && variable.defs.length > 0) {\n return false;\n }\n scope = scope.upper;\n }\n\n return true;\n};\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-unnecessary-use-client\",\n meta: {\n type: \"suggestion\",\n docs: {\n description:\n \"Flag `'use client'` files with no hooks or event handlers — they could be RSC.\",\n },\n schema: [],\n messages: {\n unnecessaryUseClient:\n \"'use client' directive but no hooks (use*), JSX event handlers (on*), browser globals, or client-only imports found. Consider removing the directive and serving as a React Server Component.\",\n },\n },\n defaultOptions: [],\n create(context) {\n const filename = context.filename;\n if (ERROR_FILE_REGEX.test(filename)) {\n return {};\n }\n\n let directiveNode: TSESTree.ExpressionStatement | null = null;\n let hasClientIndicator = false;\n /** Every local name bound by an import, whatever the module. */\n const importedLocals = new Set<string>();\n /** Locals bound from a BARE (third-party) specifier — see @fileoverview. */\n const externalLocals = new Set<string>();\n\n const markIfHookOrContext = (\n callee: TSESTree.CallExpression[\"callee\"],\n ): void => {\n if (callee.type === AST_NODE_TYPES.Identifier) {\n if (HOOK_REGEX.test(callee.name) || callee.name === \"createContext\") {\n hasClientIndicator = true;\n }\n return;\n }\n if (\n callee.type === AST_NODE_TYPES.MemberExpression &&\n callee.property.type === AST_NODE_TYPES.Identifier\n ) {\n const name = callee.property.name;\n if (HOOK_REGEX.test(name) || name === \"createContext\") {\n hasClientIndicator = true;\n }\n }\n };\n\n return {\n Program(node): void {\n for (const stmt of node.body) {\n // Directives must be the first statements; once we see a non-\n // ExpressionStatement, stop scanning.\n if (stmt.type !== AST_NODE_TYPES.ExpressionStatement) break;\n if (isUseClientDirective(stmt)) {\n directiveNode = stmt;\n break;\n }\n }\n },\n CallExpression(node): void {\n // The `Program` visitor (entered first) has already determined whether\n // this file has a `'use client'` directive. If it doesn't, the result\n // can't change — skip all of the per-node indicator work, including the\n // hot scope-resolution in the `Identifier` visitor below.\n if (directiveNode === null) return;\n markIfHookOrContext(node.callee);\n },\n JSXAttribute(node): void {\n if (directiveNode === null) return;\n if (\n node.name.type === AST_NODE_TYPES.JSXIdentifier &&\n EVENT_PROP_REGEX.test(node.name.name)\n ) {\n hasClientIndicator = true;\n }\n },\n ImportDeclaration(node): void {\n if (directiveNode === null) return;\n if (typeof node.source.value !== \"string\") return;\n const source = node.source.value;\n if (\n CLIENT_ONLY_PACKAGES_REGEX.test(source) ||\n CLIENT_REQUIRED_MODULES.has(source)\n ) {\n hasClientIndicator = true;\n }\n for (const specifier of node.specifiers) {\n importedLocals.add(specifier.local.name);\n if (isBareSpecifier(source)) {\n externalLocals.add(specifier.local.name);\n }\n }\n },\n JSXOpeningElement(node): void {\n if (directiveNode === null) return;\n // Indicator 1: rendering a third-party component — this rule cannot see\n // whether the dependency itself needs a client boundary.\n if (externalLocals.has(jsxRootName(node.name))) {\n hasClientIndicator = true;\n }\n },\n ExportNamedDeclaration(node): void {\n if (directiveNode === null) return;\n if (node.source !== null) {\n hasClientIndicator = true;\n return;\n }\n // Indicator 2: `export const X = SomeImport.X` — a re-export written\n // the long way, so it must agree with the `export … from` branch above.\n if (\n node.declaration !== null &&\n subtreeReadsImportedBinding(node.declaration, importedLocals)\n ) {\n hasClientIndicator = true;\n }\n },\n ExportAllDeclaration(node): void {\n if (directiveNode === null) return;\n if (node.source !== null) {\n hasClientIndicator = true;\n }\n },\n ClassDeclaration(): void {\n if (directiveNode === null) return;\n hasClientIndicator = true;\n },\n ClassExpression(): void {\n if (directiveNode === null) return;\n hasClientIndicator = true;\n },\n Identifier(node): void {\n if (directiveNode === null) return;\n if (isGlobalReference(node, context)) {\n hasClientIndicator = true;\n }\n },\n \"Program:exit\"(): void {\n if (directiveNode !== null && !hasClientIndicator) {\n context.report({\n node: directiveNode,\n messageId: \"unnecessaryUseClient\",\n });\n }\n },\n };\n },\n});\n","/**\n * @fileoverview Flag object types that model mutually-exclusive states with a\n * boolean status flag plus many optional members. A shape like\n * `{ success: boolean; data?: T; error?: E; code?: number }` encodes \"success\n * vs. error\" implicitly and lets illegal states (e.g. `success: true` with an\n * `error`) be representable.\n *\n * Such shapes should be modelled as a discriminated union, e.g.\n * `{ ok: true; data: T } | { ok: false; error: E }`, so the compiler enforces\n * that exactly one branch's fields are present.\n *\n * This is the TypeScript mirror of the Python rule SARJ005.\n *\n * A PAYLOAD IS REQUIRED. The defect is that a payload can be present in the\n * wrong branch (`success: true` carrying an `error`), so the shape must have at\n * least one optional member that is NOT itself a boolean. An all-boolean record\n * is a FLAG SET, not a state machine: every combination of its members is legal,\n * so there is no illegal state to make unrepresentable and a discriminated union\n * would be strictly worse.\n *\n * Corpus evidence (2220 files across zod / TanStack Query / react-router / swr /\n * zustand, 2026-07): the rule fired exactly once, and it was this false\n * positive — `swr/src/_internal/types.ts:1191`,\n * `interface StateDependencies { data?: boolean; error?: boolean;\n * isValidating?: boolean; isLoading?: boolean }`. That is SWR's dependency\n * TRACKER: each flag records \"did the render read this field?\", and all sixteen\n * combinations are meaningful.\n */\n\nimport { ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\nimport { AST_NODE_TYPES } from \"@typescript-eslint/utils\";\n\ntype MessageIds = \"preferDiscriminatedUnion\";\ntype Options = readonly [];\n\n/**\n * Boolean-typed member names that read as a success/error status discriminant.\n */\nconst STATUS_MEMBER_NAMES: ReadonlySet<string> = new Set([\n \"success\",\n \"ok\",\n \"error\",\n \"failed\",\n \"isError\",\n]);\n\nconst MIN_OPTIONAL_MEMBERS = 2;\n\n/**\n * Returns the property key name for a member if it is a plain identifier or\n * string-literal property signature, otherwise `null`.\n */\nfunction getMemberName(member: TSESTree.TypeElement): string | null {\n if (member.type !== AST_NODE_TYPES.TSPropertySignature) {\n return null;\n }\n const { key } = member;\n if (key.type === AST_NODE_TYPES.Identifier) {\n return key.name;\n }\n if (key.type === AST_NODE_TYPES.Literal && typeof key.value === \"string\") {\n return key.value;\n }\n return null;\n}\n\n/**\n * Whether a property signature is annotated with `boolean`.\n */\nfunction isBooleanTyped(member: TSESTree.TSPropertySignature): boolean {\n return (\n member.typeAnnotation?.typeAnnotation.type ===\n AST_NODE_TYPES.TSBooleanKeyword\n );\n}\n\n/**\n * Returns true when the object type literal has BOTH a boolean-typed status\n * member AND at least `MIN_OPTIONAL_MEMBERS` optional members.\n */\nfunction looksLikeMutuallyExclusiveState(\n typeLiteral: TSESTree.TSTypeLiteral,\n): boolean {\n let hasStatusBoolean = false;\n let optionalCount = 0;\n let optionalPayloadCount = 0;\n\n for (const member of typeLiteral.members) {\n if (member.type !== AST_NODE_TYPES.TSPropertySignature) {\n continue;\n }\n\n if (member.optional) {\n optionalCount += 1;\n // A non-boolean optional is a PAYLOAD — the thing that can end up in the\n // wrong branch. An all-boolean record is a flag set; see @fileoverview.\n if (!isBooleanTyped(member)) {\n optionalPayloadCount += 1;\n }\n }\n\n const name = getMemberName(member);\n if (\n name !== null &&\n STATUS_MEMBER_NAMES.has(name) &&\n isBooleanTyped(member)\n ) {\n hasStatusBoolean = true;\n }\n }\n\n return (\n hasStatusBoolean &&\n optionalCount >= MIN_OPTIONAL_MEMBERS &&\n optionalPayloadCount >= 1\n );\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"prefer-discriminated-union\",\n meta: {\n type: \"suggestion\",\n docs: {\n description:\n \"Flag object types with a boolean status flag and many optionals; model them as a discriminated union instead.\",\n },\n schema: [],\n messages: {\n preferDiscriminatedUnion:\n \"This object type uses a boolean status flag alongside several optional fields, which lets illegal states be representable. Model it as a `z.discriminatedUnion` / discriminated union (e.g. `{ ok: true; data: T } | { ok: false; error: E }`) to make illegal states unrepresentable.\",\n },\n },\n defaultOptions: [],\n create(context) {\n function checkTypeLiteral(\n typeLiteral: TSESTree.TSTypeLiteral,\n reportNode: TSESTree.Node,\n ): void {\n if (looksLikeMutuallyExclusiveState(typeLiteral)) {\n context.report({\n node: reportNode,\n messageId: \"preferDiscriminatedUnion\",\n });\n }\n }\n\n return {\n TSInterfaceDeclaration(\n node: TSESTree.TSInterfaceDeclaration,\n ): void {\n // An interface body is structurally an object type literal; reuse the\n // same membership analysis by treating its `body.body` as members.\n const synthetic: TSESTree.TSTypeLiteral = {\n ...node.body,\n type: AST_NODE_TYPES.TSTypeLiteral,\n members: node.body.body,\n } as TSESTree.TSTypeLiteral;\n checkTypeLiteral(synthetic, node);\n },\n \"TSTypeAliasDeclaration > TSTypeLiteral\"(\n node: TSESTree.TSTypeLiteral,\n ): void {\n checkTypeLiteral(node, node.parent);\n },\n };\n },\n});\n","/**\n * @fileoverview Don't access `response.json()` / `JSON.parse()` fields without a\n * Zod parse first.\n *\n * Pattern flagged:\n * const data = await response.json();\n * doSomething(data.foo); // <-- unvalidated property access\n *\n * const body = JSON.parse(raw);\n * doSomething(body.foo); // <-- same `any` leak, different source\n *\n * Encouraged:\n * const data = MySchema.parse(await response.json());\n * doSomething(data.foo); // typed + validated\n *\n * const raw: unknown = JSON.parse(text); // never flagged: nothing read off it\n * const data = MySchema.parse(raw);\n *\n * Heuristic:\n * - Track variables initialized to `await someCall.json()` or `JSON.parse(x)`\n * using ESLint's scope manager.\n * - Untrack if reassigned to anything other than another raw payload source.\n * - Untrack when passed to a user-defined type-guard predicate — a call whose\n * callee name matches `/^is[A-Z]/`, or any call used in an `if`/`?:` test\n * position (`if (guard(body)) { … body.foo … }`). Hand-written guards validate\n * the payload just as a Zod `.parse()` does.\n * - Flag MemberExpression reads and destructuring off tracked variables.\n * - `.parse()` / `.safeParse()` chained directly on the json call are legit\n * and never produce a tracked binding in the first place.\n *\n * NOT FLAGGED (corpus sweep, 2220 files across zod / TanStack Query /\n * react-router / swr / zustand, 2026-07 — 86 raw hits, 50 of them these):\n * - **Test files**, 46 hits. A test parses a payload it produced itself and\n * immediately asserts on it:\n * `react-router/integration/request-test.ts:120-121`\n * (`loaderData = JSON.parse(await page.locator(\"#loader-data\").innerHTML());\n * expect(loaderData.method).toEqual(\"GET\")`). Routing that through a schema\n * would assert the schema instead of the subject, and the assertion IS the\n * validation.\n * - **Reads inside an assertion** (`expect(payload.method)`) — see\n * `isInsideAssertion`. This catches hyphen-named suites such as\n * `react-router/integration/request-test.ts` that no path predicate sees.\n * - **JSON read off local disk**, 4 hits — see `isLocalFileRead`.\n *\n * References:\n * - https://zod.dev/?id=parse\n * - https://www.totaltypescript.com/parse-don-t-validate\n */\n\nimport {\n AST_NODE_TYPES,\n ESLintUtils,\n type TSESTree,\n} from \"@typescript-eslint/utils\";\nimport type { RuleContext, Scope } from \"@typescript-eslint/utils/ts-eslint\";\n\nimport { isGeneratedFile, isTestFile } from \"./_paths.js\";\n\ntype MessageIds = \"unparsedJsonAccess\";\ntype Options = readonly [];\n\ntype Ctx = Readonly<RuleContext<MessageIds, Options>>;\n\n/**\n * Peel TypeScript wrapper nodes that don't affect the underlying value\n * (`as Foo`, `<Foo>x`, `x!`, `x satisfies Foo`, parentheses, optional chain\n * wrappers). Returns the inner expression we actually care about.\n */\nconst unwrap = (\n node: TSESTree.Node | null | undefined,\n): TSESTree.Node | null => {\n let current: TSESTree.Node | null | undefined = node;\n while (current !== null && current !== undefined) {\n if (\n current.type === AST_NODE_TYPES.TSAsExpression ||\n current.type === AST_NODE_TYPES.TSTypeAssertion ||\n current.type === AST_NODE_TYPES.TSNonNullExpression ||\n current.type === AST_NODE_TYPES.TSSatisfiesExpression\n ) {\n current = current.expression;\n } else if (current.type === AST_NODE_TYPES.ChainExpression) {\n current = current.expression;\n } else {\n break;\n }\n }\n return current ?? null;\n};\n\n/**\n * Returns true if the expression is (optionally awaited) a raw payload source:\n * `<x>.json()` (a fetch/Request body) or `JSON.parse(<x>)`.\n *\n * Both hand back `any`, and the failure mode is the same: `payload.user.id`\n * type-checks against a shape the peer never sent, and surfaces at runtime as\n * `undefined` several frames away. Note this only matters at the point a FIELD\n * is read — `const raw: unknown = JSON.parse(body)` is the recommended shape and\n * is never reported, because nothing is accessed off it.\n */\nconst isRawPayloadSource = (\n node: TSESTree.Node | null | undefined,\n): boolean => {\n let current = unwrap(node);\n if (current === null) return false;\n if (current.type === AST_NODE_TYPES.AwaitExpression) {\n current = unwrap(current.argument);\n }\n if (current === null || current.type !== AST_NODE_TYPES.CallExpression) {\n return false;\n }\n const callee = unwrap(current.callee);\n if (callee === null || callee.type !== AST_NODE_TYPES.MemberExpression) {\n return false;\n }\n const property = unwrap(callee.property);\n if (property === null || property.type !== AST_NODE_TYPES.Identifier) {\n return false;\n }\n if (property.name === \"json\") {\n return true;\n }\n // `JSON.parse(...)` specifically — not any `.parse()`, which is usually the\n // schema validation we are asking for.\n const object = unwrap(callee.object);\n return (\n property.name === \"parse\" &&\n object !== null &&\n object.type === AST_NODE_TYPES.Identifier &&\n object.name === \"JSON\" &&\n // ...but not `JSON.parse(readFileSync(p, \"utf8\"))` — see isLocalFileRead.\n !isLocalFileRead(current.arguments[0])\n );\n};\n\n/** Filesystem readers whose result is repo-local text, not a peer's payload. */\nconst FILE_READ_RE = /^(readFile|readFileSync|readJson|readJsonSync|readJSON)$/;\n\n/**\n * True when the expression tree contains a filesystem read, i.e. the JSON came\n * off local disk rather than off the wire.\n *\n * The rule's premise is that the value is \"unvalidated and attacker-controlled\".\n * `JSON.parse(readFileSync(\"package.json\", \"utf8\"))` is neither: the bytes ship\n * with the repo, nobody else can write them, and a Zod schema over a file the\n * build already depends on adds a second place to update.\n *\n * Corpus evidence (2220 files across zod / TanStack Query / react-router / swr /\n * zustand, 2026-07): `zod/scripts/check-versions.ts:13-14`\n * (`const packageJson = JSON.parse(readFileSync(packageJsonPath, \"utf8\"));\n * const packageJsonVersion = packageJson.version as string;` — and the very next\n * line is a `typeof` check), `zod/scripts/check-semver.ts:10-11`, and\n * `zod/packages/docs/app/llms-full.txt/route.ts:13-17`\n * (`JSON.parse(await fs.readFile(metaPath, \"utf-8\"))` over the docs' own\n * `meta.json`). A `response.json()` — the actual trust boundary — is unaffected.\n */\nconst isLocalFileRead = (node: TSESTree.Node | null | undefined): boolean => {\n let found = false;\n const visit = (current: TSESTree.Node | null | undefined): void => {\n if (found || current === null || current === undefined) return;\n if (current.type === AST_NODE_TYPES.CallExpression) {\n const callee = unwrap(current.callee);\n const name =\n callee?.type === AST_NODE_TYPES.Identifier\n ? callee.name\n : callee?.type === AST_NODE_TYPES.MemberExpression &&\n !callee.computed &&\n callee.property.type === AST_NODE_TYPES.Identifier\n ? callee.property.name\n : null;\n if (name !== null && FILE_READ_RE.test(name)) {\n found = true;\n return;\n }\n }\n for (const key of Object.keys(current) as (keyof TSESTree.Node)[]) {\n if (key === \"parent\") continue;\n const value = current[key];\n for (const child of (Array.isArray(value) ? value : [value]) as unknown[]) {\n if (\n child !== null &&\n typeof child === \"object\" &&\n typeof (child as { type?: unknown }).type === \"string\"\n ) {\n visit(child as TSESTree.Node);\n }\n }\n }\n };\n visit(node);\n return found;\n};\n\n/** Assertion helpers whose argument is being checked, not consumed. */\nconst ASSERTION_CALLEE_RE = /^(expect|assert|should|invariant)$/;\n\n/**\n * True when the node sits inside an assertion call, e.g.\n * `expect(loaderData.method).toEqual(\"GET\")`.\n *\n * The rule's premise is that the field is READ and trusted. Inside an assertion\n * it is neither: the assertion states what the value must be, which is the same\n * check a schema would perform, and a schema parse would move the failure away\n * from the assertion that explains it.\n *\n * Corpus evidence (2220 files across zod / TanStack Query / react-router / swr /\n * zustand, 2026-07): after the test-file exemption, 15 of the 45 remaining hits\n * were this — react-router names its Playwright suites `integration/request-test.ts`\n * (hyphen), which no `*.test.ts` path predicate can recognise, so the shape\n * check is what catches them.\n * `react-router/integration/request-test.ts:120-121`:\n * `loaderData = JSON.parse(await page.locator(\"#loader-data\").innerHTML());\n * expect(loaderData.method).toEqual(\"GET\");`\n */\nconst isInsideAssertion = (node: TSESTree.Node): boolean => {\n for (\n let current: TSESTree.Node | undefined | null = node.parent;\n current !== undefined && current !== null;\n current = current.parent\n ) {\n if (current.type !== AST_NODE_TYPES.CallExpression) continue;\n let callee: TSESTree.Node = current.callee;\n while (callee.type === AST_NODE_TYPES.MemberExpression) {\n callee = callee.object;\n }\n if (callee.type === AST_NODE_TYPES.CallExpression) {\n callee = callee.callee;\n }\n if (\n callee.type === AST_NODE_TYPES.Identifier &&\n ASSERTION_CALLEE_RE.test(callee.name)\n ) {\n return true;\n }\n }\n return false;\n};\n\nconst findVariable = (\n scope: Scope.Scope | null,\n name: string,\n): Scope.Variable | null => {\n let current: Scope.Scope | null = scope;\n while (current !== null) {\n const variable = current.set.get(name);\n if (variable !== undefined) return variable;\n current = current.upper;\n }\n return null;\n};\n\n/** User-defined type-guard predicate names, e.g. `isProtectedResourceMetadata`. */\nconst GUARD_NAME_RE = /^is[A-Z]/;\n\n/**\n * True when a call sits in a boolean-test position (`if`/`while`/`for`/`?:`),\n * seen through `!`, `&&`/`||`, and optional-chaining wrappers — i.e. it narrows.\n */\nconst isGuardTestPosition = (node: TSESTree.Node): boolean => {\n let current: TSESTree.Node = node;\n let parent: TSESTree.Node | null | undefined = current.parent;\n while (parent !== undefined && parent !== null) {\n switch (parent.type) {\n case AST_NODE_TYPES.UnaryExpression:\n case AST_NODE_TYPES.LogicalExpression:\n case AST_NODE_TYPES.ChainExpression:\n current = parent;\n parent = parent.parent;\n continue;\n case AST_NODE_TYPES.IfStatement:\n case AST_NODE_TYPES.ConditionalExpression:\n case AST_NODE_TYPES.WhileStatement:\n case AST_NODE_TYPES.DoWhileStatement:\n case AST_NODE_TYPES.ForStatement:\n return parent.test === current;\n default:\n return false;\n }\n }\n return false;\n};\n\nconst isUnvalidatedVariableRef = (\n node: TSESTree.Node | null | undefined,\n scope: Scope.Scope,\n tracked: ReadonlySet<Scope.Variable>,\n): boolean => {\n const unwrapped = unwrap(node);\n if (unwrapped === null || unwrapped.type !== AST_NODE_TYPES.Identifier) {\n return false;\n }\n const variable = findVariable(scope, unwrapped.name);\n return variable !== null && tracked.has(variable);\n};\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"prefer-schema-for-api-payload\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Require Zod (or similar) schema validation on `response.json()` / `JSON.parse()` results before property access.\",\n },\n schema: [],\n messages: {\n unparsedJsonAccess:\n \"Property access on an unvalidated payload (`response.json()` / `JSON.parse()`) without a schema parse. Pipe through `XSchema.parse(...)` (Zod) before reading fields.\",\n },\n },\n defaultOptions: [],\n create(context: Ctx) {\n // A fixture parses what it just produced and asserts on it; see @fileoverview.\n if (isTestFile(context.filename) || isGeneratedFile(context.filename, context.sourceCode.text)) {\n return {};\n }\n\n const unvalidatedVariables = new Set<Scope.Variable>();\n\n const trackInitializer = (\n declarator: TSESTree.VariableDeclarator,\n ): void => {\n if (!isRawPayloadSource(declarator.init)) return;\n const declaredVars = context.sourceCode.getDeclaredVariables(declarator);\n const variable = declaredVars[0];\n if (variable !== undefined) {\n unvalidatedVariables.add(variable);\n }\n };\n\n return {\n VariableDeclarator(node): void {\n const scope = context.sourceCode.getScope(node);\n\n if (node.id.type === AST_NODE_TYPES.Identifier) {\n trackInitializer(node);\n return;\n }\n\n if (\n node.id.type === AST_NODE_TYPES.ObjectPattern ||\n node.id.type === AST_NODE_TYPES.ArrayPattern\n ) {\n if (isRawPayloadSource(node.init)) {\n context.report({ node: node.id, messageId: \"unparsedJsonAccess\" });\n return;\n }\n if (\n isUnvalidatedVariableRef(node.init, scope, unvalidatedVariables)\n ) {\n context.report({ node: node.id, messageId: \"unparsedJsonAccess\" });\n }\n }\n },\n AssignmentExpression(node): void {\n const scope = context.sourceCode.getScope(node);\n\n if (node.left.type === AST_NODE_TYPES.Identifier) {\n const variable = findVariable(scope, node.left.name);\n if (variable === null) return;\n if (isRawPayloadSource(node.right)) {\n unvalidatedVariables.add(variable);\n } else {\n // Reassigned to a parse call or something else: drop tracking.\n unvalidatedVariables.delete(variable);\n }\n return;\n }\n\n if (\n node.left.type === AST_NODE_TYPES.ObjectPattern ||\n node.left.type === AST_NODE_TYPES.ArrayPattern\n ) {\n if (isRawPayloadSource(node.right)) {\n context.report({\n node: node.left,\n messageId: \"unparsedJsonAccess\",\n });\n return;\n }\n if (\n isUnvalidatedVariableRef(node.right, scope, unvalidatedVariables)\n ) {\n context.report({\n node: node.left,\n messageId: \"unparsedJsonAccess\",\n });\n }\n }\n },\n CallExpression(node): void {\n if (node.callee.type !== AST_NODE_TYPES.Identifier) return;\n if (!GUARD_NAME_RE.test(node.callee.name) && !isGuardTestPosition(node)) {\n return;\n }\n const scope = context.sourceCode.getScope(node);\n for (const arg of node.arguments) {\n if (arg.type === AST_NODE_TYPES.SpreadElement) continue;\n const unwrapped = unwrap(arg);\n if (unwrapped === null || unwrapped.type !== AST_NODE_TYPES.Identifier) {\n continue;\n }\n const variable = findVariable(scope, unwrapped.name);\n if (variable !== null) unvalidatedVariables.delete(variable);\n }\n },\n MemberExpression(node): void {\n // The read is inside an assertion — the assertion IS the validation.\n if (isInsideAssertion(node)) return;\n const scope = context.sourceCode.getScope(node);\n const obj = unwrap(node.object);\n\n if (isRawPayloadSource(obj)) {\n // Direct `.foo` access on `(await r.json()).foo` is always bad,\n // unless the parent call is a `.parse()`/`.safeParse()` — in which\n // case it's a validation, not an unvalidated read.\n const parent = node.parent;\n if (\n parent.type === AST_NODE_TYPES.CallExpression &&\n parent.callee === node &&\n node.property.type === AST_NODE_TYPES.Identifier &&\n (node.property.name === \"parse\" ||\n node.property.name === \"safeParse\")\n ) {\n return;\n }\n context.report({ node, messageId: \"unparsedJsonAccess\" });\n return;\n }\n\n if (\n obj !== null &&\n obj.type === AST_NODE_TYPES.Identifier &&\n isUnvalidatedVariableRef(obj, scope, unvalidatedVariables)\n ) {\n context.report({ node, messageId: \"unparsedJsonAccess\" });\n const variable = findVariable(scope, obj.name);\n if (variable !== null) {\n unvalidatedVariables.delete(variable);\n }\n }\n },\n };\n },\n});\n","/**\n * @fileoverview Enforce design-system semantic color tokens over raw Tailwind\n * palette classes and hardcoded color values.\n *\n * Scoped to genuine className positions to avoid false positives on non-class\n * strings (Tailwind `safelist`, `toHaveClass(...)` test assertions, prose, color\n * maps): JSX `className`, the args of `cn()`/`clsx()`/`cva()`/`tv()`/`cx()`/\n * `twMerge()` (recursing into cva variant objects), and `*class*`-named\n * variables/object properties. Plus inline color literals on JSX `style`/`fill`/\n * `stroke`.\n *\n * Flags:\n * - raw palette classes: `text-red-500`, `bg-slate-200/50`\n * - arbitrary color values: `bg-[#fff]`, `text-[rgb(...)]`, `ring-[oklch(...)]`\n * - inline color literals: `style={{ color: \"#111827\" }}`, `fill=\"#000\"`\n *\n * Allowed: semantic tokens (`bg-primary`, `text-muted-foreground`, `bg-chart-1`),\n * `white`/`black` (the `bg-black/50` overlay idiom rarely has a token), `var(--…)`,\n * `currentColor`, and non-color arbitraries (`w-[437px]`, `grid-cols-[auto_1fr]`).\n *\n * SVG drawing data is exempt on `fill`/`stroke`/`color` attributes: any value inside\n * a `<mask>`/`<clipPath>`/`<defs>`/`<pattern>`/`<linearGradient>`/`<radialGradient>`\n * (masking breaks without literal `#fff`/`#000`), the neutral literals\n * (`#fff`/`#000`/`transparent`/`none`/`currentColor`/`inherit`), and `*.stories.*`\n * files (Storybook fixtures) never fire. Real component styling — `className` and\n * inline `style={{ … }}` objects — still fires on hardcoded colors.\n *\n * No autofix — use a semantic token, or for charts / standalone pages / 3rd-party\n * config add `// eslint-disable-next-line @sarj/prefer-semantic-colors -- <reason>`.\n */\n\nimport { AST_NODE_TYPES, ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\nimport { existsSync, readFileSync } from \"fs\";\nimport { dirname, join, parse } from \"path\";\n\nimport { classTokens, tailwindBase } from \"./_tailwind.js\";\n\ntype MessageIds = \"rawPalette\" | \"arbitraryColor\" | \"inlineColor\";\ntype Options = readonly [\n {\n requireSemanticTokens?: boolean;\n }?,\n];\n\nconst COLOR_PREFIXES =\n \"text|bg|border(?:-[trblxyse])?|ring(?:-offset)?|fill|stroke|from|via|to|divide|decoration|placeholder|accent|caret|shadow|outline\";\nconst PALETTE =\n \"red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose|slate|gray|zinc|neutral|stone\";\nconst COLOR_FN = \"rgba?|hsla?|hwb|oklch|oklab|lab|lch|color\";\n\nconst RAW_PALETTE_RE = new RegExp(`^(?:${COLOR_PREFIXES})-(?:${PALETTE})-\\\\d{2,3}(?:/\\\\d{1,3})?$`);\nconst ARBITRARY_COLOR_RE = new RegExp(\n `^(?:${COLOR_PREFIXES})-\\\\[(?:#[0-9a-fA-F]{3,8}|(?:${COLOR_FN})\\\\([^\\\\]]*\\\\))\\\\]$`,\n \"i\",\n);\n\n/** Call expressions whose string args are className fragments. */\nconst CLASS_FNS = new Set<string>([\"cn\", \"clsx\", \"cva\", \"tv\", \"cx\", \"twMerge\", \"classnames\", \"classNames\"]);\nconst CLASS_NAME_RE = /class/i;\n\n/** CSS color-bearing properties, in their JSX (camelCase) and SVG-attribute forms. */\nconst STYLE_COLOR_PROPS = new Set<string>([\n \"color\",\n \"background\",\n \"backgroundColor\",\n \"borderColor\",\n \"borderTopColor\",\n \"borderRightColor\",\n \"borderBottomColor\",\n \"borderLeftColor\",\n \"outlineColor\",\n \"caretColor\",\n \"textDecorationColor\",\n \"columnRuleColor\",\n \"fill\",\n \"stroke\",\n \"stopColor\",\n \"floodColor\",\n \"lightingColor\",\n]);\nconst RAW_COLOR_VALUE_RE = new RegExp(`#[0-9a-fA-F]{3,8}\\\\b|\\\\b(?:${COLOR_FN})\\\\s*\\\\(`, \"i\");\n\nconst STORIES_FILE_RE = /\\.stories\\.[cm]?[jt]sx?$/i;\nconst SEMANTIC_TOKEN_RE = /--(?:background|foreground|primary|secondary|muted|accent|destructive|border|card|popover)\\b|(?:bg|text|border)-(?:background|foreground|primary|secondary|muted|accent|destructive|border|card|popover)\\b/;\nconst DETECTION_FILES = [\n \"components.json\",\n \"tailwind.config.js\",\n \"tailwind.config.cjs\",\n \"tailwind.config.mjs\",\n \"tailwind.config.ts\",\n \"app/globals.css\",\n \"src/app/globals.css\",\n \"src/index.css\",\n \"src/styles/globals.css\",\n \"styles/globals.css\",\n];\nconst semanticTokenCache = new Map<string, boolean>();\n\n/** SVG container elements whose children carry structural (not UI-token) colors. */\nconst SVG_DEFS_CONTAINERS = new Set<string>([\n \"mask\",\n \"clipPath\",\n \"defs\",\n \"pattern\",\n \"linearGradient\",\n \"radialGradient\",\n]);\n\n/** Neutral fill/stroke literals that are SVG drawing data, never a UI token. */\nconst SVG_EXEMPT_COLOR_VALUES = new Set<string>([\n \"#fff\",\n \"#ffffff\",\n \"#000\",\n \"#000000\",\n \"transparent\",\n \"none\",\n \"currentcolor\",\n \"inherit\",\n]);\n\n// A `fill`/`stroke` literal anywhere inside an `<svg>` subtree is drawing data\n// (icon/illustration artwork), not a reusable UI token — the color is inherent to\n// the graphic. Exempt any descendant of `<svg>` (which subsumes the defs\n// containers `<mask>`/`<clipPath>`/`<defs>`/`<pattern>`/gradients).\nfunction jsxElementName(node: TSESTree.JSXElement): string | null {\n const name = node.openingElement.name;\n if (name.type === AST_NODE_TYPES.JSXIdentifier) return name.name;\n if (name.type === AST_NODE_TYPES.JSXMemberExpression && name.property.type === AST_NODE_TYPES.JSXIdentifier) {\n return name.property.name;\n }\n return null;\n}\n\nfunction isSvgLikeElementName(name: string): boolean {\n return name === \"svg\" || SVG_DEFS_CONTAINERS.has(name) || /svg$/i.test(name);\n}\n\nconst isInsideSvg = (node: TSESTree.Node): boolean => {\n let current: TSESTree.Node | null | undefined = node.parent;\n while (current !== undefined && current !== null) {\n if (current.type === AST_NODE_TYPES.JSXElement) {\n const name = jsxElementName(current);\n if (name !== null && isSvgLikeElementName(name)) return true;\n }\n current = current.parent;\n }\n return false;\n};\n\nconst isInsideIconFactoryPath = (node: TSESTree.Node): boolean => {\n let current: TSESTree.Node | null | undefined = node.parent;\n while (current !== undefined && current !== null) {\n if (\n current.type === AST_NODE_TYPES.Property &&\n propName(current.key) === \"path\" &&\n current.parent.type === AST_NODE_TYPES.ObjectExpression &&\n current.parent.parent.type === AST_NODE_TYPES.CallExpression &&\n current.parent.parent.callee.type === AST_NODE_TYPES.Identifier &&\n current.parent.parent.callee.name === \"createIcon\"\n ) {\n return true;\n }\n current = current.parent;\n }\n return false;\n};\n\nconst hasSemanticTokenSystem = (filename: string): boolean => {\n let dir = dirname(filename);\n const root = parse(dir).root;\n for (let depth = 0; depth < 8; depth += 1) {\n const cached = semanticTokenCache.get(dir);\n if (cached !== undefined) return cached;\n\n let found = false;\n for (const rel of DETECTION_FILES) {\n const candidate = join(dir, rel);\n if (!existsSync(candidate)) continue;\n if (rel === \"components.json\") {\n found = true;\n break;\n }\n try {\n if (SEMANTIC_TOKEN_RE.test(readFileSync(candidate, \"utf8\"))) {\n found = true;\n break;\n }\n } catch {\n // Ignore unreadable config files; absence of evidence means no report.\n }\n }\n semanticTokenCache.set(dir, found);\n if (found || dir === root) return found;\n dir = dirname(dir);\n }\n return false;\n};\n\nconst propName = (key: TSESTree.Property[\"key\"]): string | null => {\n if (key.type === AST_NODE_TYPES.Identifier) return key.name;\n if (key.type === AST_NODE_TYPES.Literal && typeof key.value === \"string\") return key.value;\n return null;\n};\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"prefer-semantic-colors\",\n meta: {\n type: \"suggestion\",\n docs: {\n description:\n \"Enforce design-system semantic color tokens (bg-primary, text-destructive, …) over raw Tailwind palette classes (text-red-500), arbitrary color values (bg-[#fff]), and inline color literals.\",\n },\n schema: [\n {\n type: \"object\",\n additionalProperties: false,\n properties: {\n requireSemanticTokens: { type: \"boolean\" },\n },\n },\n ],\n messages: {\n rawPalette:\n \"Raw palette class '{{class}}' — use a semantic token (e.g. text-foreground, bg-primary, text-destructive, bg-muted).\",\n arbitraryColor:\n \"Hardcoded color '{{class}}' — use a semantic token, or var(--…). For charts/brand add an eslint-disable with a reason.\",\n inlineColor:\n \"Hardcoded color '{{value}}' — use a semantic token / CSS variable. For charts/standalone pages add an eslint-disable with a reason.\",\n },\n },\n defaultOptions: [{}],\n create(context, [options]) {\n if (STORIES_FILE_RE.test(context.filename)) return {};\n if (options?.requireSemanticTokens === true && !hasSemanticTokenSystem(context.filename)) {\n return {};\n }\n\n const reportClasses = (value: string, node: TSESTree.Node): void => {\n for (const token of classTokens(value)) {\n const base = tailwindBase(token);\n if (RAW_PALETTE_RE.test(base)) {\n context.report({ node, messageId: \"rawPalette\", data: { class: token } });\n } else if (ARBITRARY_COLOR_RE.test(base)) {\n context.report({ node, messageId: \"arbitraryColor\", data: { class: token } });\n }\n }\n };\n\n // Walk a node that holds className fragments: strings, templates, arrays, cva\n // variant objects, and conditionals. CallExpressions are handled separately, so\n // they're not recursed here (avoids double-reporting cn()/cva() args).\n const checkClassNode = (node: TSESTree.Node | null): void => {\n if (node === null) return;\n switch (node.type) {\n case AST_NODE_TYPES.Literal:\n if (typeof node.value === \"string\") reportClasses(node.value, node);\n break;\n case AST_NODE_TYPES.TemplateLiteral:\n for (const quasi of node.quasis) reportClasses(quasi.value.cooked ?? \"\", quasi);\n break;\n case AST_NODE_TYPES.ArrayExpression:\n for (const element of node.elements) {\n if (element !== null && element.type !== AST_NODE_TYPES.SpreadElement) checkClassNode(element);\n }\n break;\n case AST_NODE_TYPES.ObjectExpression:\n for (const property of node.properties) {\n if (property.type === AST_NODE_TYPES.Property) checkClassNode(property.value);\n }\n break;\n case AST_NODE_TYPES.ConditionalExpression:\n checkClassNode(node.consequent);\n checkClassNode(node.alternate);\n break;\n case AST_NODE_TYPES.LogicalExpression:\n checkClassNode(node.right);\n break;\n default:\n break;\n }\n };\n\n const checkColorValueNode = (node: TSESTree.Node): void => {\n if (\n node.type === AST_NODE_TYPES.Literal &&\n typeof node.value === \"string\" &&\n RAW_COLOR_VALUE_RE.test(node.value)\n ) {\n context.report({ node, messageId: \"inlineColor\", data: { value: node.value } });\n }\n };\n\n return {\n \"JSXAttribute[name.name='className']\"(node: TSESTree.JSXAttribute): void {\n if (node.value === null) return;\n if (node.value.type === AST_NODE_TYPES.Literal) checkClassNode(node.value);\n else if (node.value.type === AST_NODE_TYPES.JSXExpressionContainer) {\n if (node.value.expression.type !== AST_NODE_TYPES.JSXEmptyExpression) {\n checkClassNode(node.value.expression);\n }\n }\n },\n CallExpression(node: TSESTree.CallExpression): void {\n if (node.callee.type === AST_NODE_TYPES.Identifier && CLASS_FNS.has(node.callee.name)) {\n for (const arg of node.arguments) {\n if (arg.type !== AST_NODE_TYPES.SpreadElement) checkClassNode(arg);\n }\n }\n },\n VariableDeclarator(node: TSESTree.VariableDeclarator): void {\n if (node.id.type === AST_NODE_TYPES.Identifier && CLASS_NAME_RE.test(node.id.name)) {\n checkClassNode(node.init);\n }\n },\n Property(node: TSESTree.Property): void {\n const name = propName(node.key);\n if (name !== null && CLASS_NAME_RE.test(name)) checkClassNode(node.value);\n },\n // SVG presentation attributes: <path fill=\"#7c3aed\" stroke=\"#7c3aed\" />.\n // Neutral drawing literals and anything inside an SVG defs container are\n // structural, not UI tokens, so they never fire.\n \"JSXAttribute[name.name=/^(fill|stroke|color)$/]\"(node: TSESTree.JSXAttribute): void {\n if (node.value?.type !== AST_NODE_TYPES.Literal) return;\n if (\n typeof node.value.value === \"string\" &&\n SVG_EXEMPT_COLOR_VALUES.has(node.value.value.toLowerCase())\n ) {\n return;\n }\n if (isInsideSvg(node) || isInsideIconFactoryPath(node)) return;\n checkColorValueNode(node.value);\n },\n // Inline style objects: style={{ color: \"#111827\", backgroundColor: \"#fff\" }}\n \"JSXAttribute[name.name='style'] ObjectExpression > Property\"(node: TSESTree.Property): void {\n const name = propName(node.key);\n if (name !== null && STYLE_COLOR_PROPS.has(name)) checkColorValueNode(node.value);\n },\n };\n },\n});\n","/**\n * @fileoverview Shared helpers for the Tailwind-className rules. className values\n * are reachable as plain string `Literal`s (attribute values, `cn()`/`clsx()`/\n * `cva()`/`tv()` args, and className-holding constants) and as the static quasis of\n * `TemplateLiteral`s — so the rules visit both node types and run these helpers.\n */\n\n/**\n * Strip Tailwind variant prefixes (`hover:`, `dark:`, `focus-visible:`, …) and a\n * leading `!` important marker, leaving the bare utility (`bg-red-500`). Variants are\n * `[a-z0-9-]+:` runs at the start; bracketed arbitrary values never start a token, so\n * a `:` inside `[url(http://…)]` is not mistaken for a variant separator.\n */\nexport const tailwindBase = (token: string): string =>\n token.replace(/^(?:[a-z0-9-]+:)+/i, \"\").replace(/^!/, \"\");\n\n/** Split a className string into its non-empty class tokens. */\nexport const classTokens = (value: string): readonly string[] =>\n value.split(/\\s+/).filter(Boolean);\n","/**\n * @fileoverview Prefer Next.js Server Actions over /api/* mutations.\n *\n * Flags mutations against internal `/api/*` URLs (POST/PUT/DELETE/PATCH) via\n * `fetch`, axios-style helpers, or direct axios/request calls. GET requests\n * and external URLs are ignored. Tests, scripts, and Next.js route handlers\n * are skipped because Server Actions don't apply there.\n *\n * The member branch (`api.post('/api/x')`) intentionally skips calls that pass\n * a function argument (e.g. `router.post('/api/x', handler)`) so Express-style\n * route *definitions* aren't mistaken for client-side mutations.\n *\n * NON-REACT FRAMEWORKS ARE SKIPPED. \"Use a Server Action\" is not advice an\n * Angular, Vue, Svelte or Solid module can act on — the feature does not exist\n * outside React/Next. Corpus sweep (2220 files across zod / TanStack Query /\n * react-router / swr / zustand, 2026-07): 9 hits, of which 3 were Angular\n * services —\n * `query/examples/angular/auto-refetching/src/app/services/tasks.service.ts:38`\n * (`lastValueFrom(this.#http.post('/api/tasks', task))`, an `HttpClient`\n * injection) — and 4 were jscodeshift input/output fixtures under\n * `__testfixtures__/`, which are text a codemod transforms rather than code that\n * runs. Both are now skipped; the 2 genuine React hits still fire.\n *\n * References:\n * - https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations\n */\n\nimport { ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\nimport type { RuleContext, Scope } from \"@typescript-eslint/utils/ts-eslint\";\n\ntype MessageIds = \"preferServerAction\";\ntype Options = readonly [];\n\nconst MUTATION_METHODS = new Set([\"POST\", \"PUT\", \"DELETE\", \"PATCH\"]);\nconst AXIOS_MUTATION_METHODS = new Set([\"post\", \"put\", \"delete\", \"patch\"]);\n\nconst SKIP_FILE_REGEX =\n /(?:\\.test\\.[jt]sx?$|\\.spec\\.[jt]sx?$|-(?:test|spec)\\.[jt]sx?$|\\/tests?\\/|\\/__tests__\\/|\\/__testfixtures__\\/|\\/scripts?\\/|\\/app\\/api\\/.*\\/route\\.[jt]sx?$|\\/pages\\/api\\/)/;\n\n/**\n * Import sources that prove the module belongs to a framework where Server\n * Actions do not exist. See @fileoverview for the corpus evidence.\n */\nconst NON_REACT_FRAMEWORK_RE =\n /^(?:@angular\\/|@nestjs\\/|vue$|vue\\/|svelte$|svelte\\/|solid-js$|solid-js\\/|@ember\\/|rxjs$|rxjs\\/)/;\n\ntype Ctx = Readonly<RuleContext<MessageIds, Options>>;\n\nfunction getScope(\n context: Ctx,\n node: TSESTree.Node,\n): Scope.Scope {\n return context.sourceCode.getScope(node);\n}\n\n/**\n * Walk up the scope chain looking for a variable declared with a single\n * initializer; if found, return the initializer expression so we can analyze\n * it. Falls back to the original node when resolution is not possible.\n */\nfunction resolveNode(\n node: TSESTree.Node | null | undefined,\n context: Ctx,\n): TSESTree.Node | null {\n if (!node) return null;\n if (node.type !== \"Identifier\") return node;\n\n let scope: Scope.Scope | null = getScope(context, node);\n while (scope) {\n const variable = scope.set.get(node.name);\n if (variable && variable.defs.length === 1) {\n const def = variable.defs[0];\n if (def && def.type === \"Variable\") {\n const declarator = def.node;\n if (\n declarator.type === \"VariableDeclarator\" &&\n declarator.init\n ) {\n return declarator.init;\n }\n }\n }\n scope = scope.upper;\n }\n return node;\n}\n\nfunction isApiUrl(\n node: TSESTree.Node | null | undefined,\n context: Ctx,\n): boolean {\n const resolved = resolveNode(node, context);\n if (!resolved) return false;\n\n if (resolved.type === \"Literal\" && typeof resolved.value === \"string\") {\n return resolved.value.startsWith(\"/api/\");\n }\n if (resolved.type === \"TemplateLiteral\") {\n const firstQuasi = resolved.quasis[0];\n const cooked = firstQuasi?.value.cooked;\n return typeof cooked === \"string\" && cooked.startsWith(\"/api/\");\n }\n if (resolved.type === \"BinaryExpression\" && resolved.operator === \"+\") {\n return isApiUrl(resolved.left, context);\n }\n return false;\n}\n\nfunction isMutationMethod(\n node: TSESTree.Node | null | undefined,\n context: Ctx,\n): boolean {\n const resolved = resolveNode(node, context);\n if (!resolved) return false;\n\n if (resolved.type === \"Literal\" && typeof resolved.value === \"string\") {\n return MUTATION_METHODS.has(resolved.value.toUpperCase());\n }\n\n if (\n resolved.type === \"TemplateLiteral\" &&\n resolved.expressions.length === 0\n ) {\n const val = resolved.quasis.map((q) => q.value.cooked).join(\"\");\n return MUTATION_METHODS.has(val.toUpperCase());\n }\n\n if (resolved.type === \"ConditionalExpression\") {\n return (\n isMutationMethod(resolved.consequent, context) ||\n isMutationMethod(resolved.alternate, context)\n );\n }\n\n if (resolved.type === \"LogicalExpression\" && resolved.operator === \"||\") {\n return (\n isMutationMethod(resolved.left, context) ||\n isMutationMethod(resolved.right, context)\n );\n }\n\n return false;\n}\n\nfunction getPropertyNode(\n objNode: TSESTree.Node | null | undefined,\n propName: string,\n): TSESTree.Node | null {\n if (!objNode || objNode.type !== \"ObjectExpression\") return null;\n for (const prop of objNode.properties) {\n if (prop.type !== \"Property\") continue;\n let keyName: string | null = null;\n if (prop.key.type === \"Identifier\" && !prop.computed) {\n keyName = prop.key.name;\n } else if (\n prop.key.type === \"Literal\" &&\n typeof prop.key.value === \"string\"\n ) {\n keyName = prop.key.value;\n }\n if (keyName === propName) {\n // Skip destructuring patterns — they're not valid as config values.\n if (\n prop.value.type === \"AssignmentPattern\" ||\n prop.value.type === \"ArrayPattern\" ||\n prop.value.type === \"ObjectPattern\"\n ) {\n return null;\n }\n return prop.value;\n }\n }\n return null;\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"prefer-server-actions\",\n meta: {\n type: \"suggestion\",\n docs: {\n description: \"Prefer Next.js Server Actions over /api/* mutations.\",\n },\n schema: [],\n messages: {\n preferServerAction:\n \"Mutation against /api/* — prefer a Next.js Server Action for type-safety and to avoid the JSON round-trip.\",\n },\n },\n defaultOptions: [],\n create(context) {\n const filename = context.filename;\n if (SKIP_FILE_REGEX.test(filename)) {\n return {};\n }\n\n // Set by the ImportDeclaration visitor. Imports are hoisted to the top of a\n // module, so every one is seen before the first CallExpression below.\n let isNonReactFramework = false;\n\n return {\n ImportDeclaration(node) {\n if (\n typeof node.source.value === \"string\" &&\n NON_REACT_FRAMEWORK_RE.test(node.source.value)\n ) {\n isNonReactFramework = true;\n }\n },\n CallExpression(node) {\n if (isNonReactFramework) return;\n let isMutation = false;\n\n // 1. Standard fetch('/api/orders', { method: 'POST' })\n if (\n node.callee.type === \"Identifier\" &&\n node.callee.name === \"fetch\"\n ) {\n const urlArg = node.arguments[0];\n if (urlArg && urlArg.type !== \"SpreadElement\" && isApiUrl(urlArg, context)) {\n const initArg = node.arguments[1];\n if (initArg && initArg.type !== \"SpreadElement\") {\n const resolvedInit = resolveNode(initArg, context);\n const methodNode = getPropertyNode(resolvedInit, \"method\");\n if (methodNode && isMutationMethod(methodNode, context)) {\n isMutation = true;\n }\n }\n }\n }\n // 2. Custom wrappers or Axios: api.post('/api/orders') or axios.put('/api/orders')\n else if (\n node.callee.type === \"MemberExpression\" &&\n node.callee.property.type === \"Identifier\" &&\n !node.callee.computed\n ) {\n const methodName = node.callee.property.name.toLowerCase();\n if (AXIOS_MUTATION_METHODS.has(methodName)) {\n const urlArg = node.arguments[0];\n // Skip Express-style route definitions like\n // `router.post('/api/x', handler)`: a function argument means this\n // is registering a handler, not issuing a client mutation.\n const hasHandlerArg = node.arguments.some(\n (arg) =>\n arg.type === \"ArrowFunctionExpression\" ||\n arg.type === \"FunctionExpression\",\n );\n if (\n urlArg &&\n urlArg.type !== \"SpreadElement\" &&\n !hasHandlerArg &&\n isApiUrl(urlArg, context)\n ) {\n isMutation = true;\n }\n }\n }\n // 3. Direct axios/request call: axios({ method: 'post', url: '/api/orders' })\n else if (\n node.callee.type === \"Identifier\" &&\n (node.callee.name === \"axios\" || node.callee.name === \"request\")\n ) {\n const firstArg = node.arguments[0];\n if (firstArg && firstArg.type !== \"SpreadElement\") {\n const configArg = resolveNode(firstArg, context);\n if (configArg && configArg.type === \"ObjectExpression\") {\n const urlNode = getPropertyNode(configArg, \"url\");\n const methodNode = getPropertyNode(configArg, \"method\");\n if (\n urlNode &&\n isApiUrl(urlNode, context) &&\n methodNode &&\n isMutationMethod(methodNode, context)\n ) {\n isMutation = true;\n }\n }\n }\n }\n\n if (isMutation) {\n context.report({ node, messageId: \"preferServerAction\" });\n }\n },\n };\n },\n});\n","/**\n * @fileoverview Prefer shadcn/ui form primitives over their native HTML\n * counterparts. Limited to form/dialog elements — `<button>` and `<table>`\n * have been removed from the forbid list because they produced 100% false\n * positives during bulbul validation (icon buttons, layout tables).\n *\n * `<input>` is resolved by its `type`: a bare `<input type=\"checkbox\">` maps to\n * `<Checkbox>`, `radio` → `<RadioGroup>`, `range` → `<Slider>`, and the text-like\n * types (or no type) → `<Input>`. `type=\"hidden\"` and native file upload controls\n * are skipped (no shadcn primitive), and a dynamic `type={…}` falls back to the\n * generic `<Input>` rather than asserting a wrong primitive.\n *\n * TEST FILES ARE EXEMPT. Corpus sweep (2220 files across zod / TanStack Query /\n * react-router / swr / zustand, 2026-07): 84 raw hits, 53 of them in a single\n * react-router suite. JSX inside a test is not a user interface — it is the\n * smallest possible DOM that makes an assertion reachable.\n * `react-router/packages/react-router/__tests__/dom/data-browser-router-test.tsx:1415`\n * (`<input name=\"test\" value=\"value\" />` inside a `<Form method=\"post\">`, so the\n * test can assert what the router submitted) is representative: swapping in\n * `<Input>` would add a dependency, change nothing observable, and make the\n * fixture harder to read. The remaining hits, in real screens under\n * `examples/`, still fire.\n */\n\nimport {\n AST_NODE_TYPES,\n ESLintUtils,\n type TSESTree,\n} from \"@typescript-eslint/utils\";\n\nimport { isStoryFile, isTestFile } from \"./_paths.js\";\n\ntype MessageIds = \"preferShadcn\";\ntype Options = readonly [];\n\nconst REPLACEMENTS: Readonly<Record<string, string>> = {\n select: \"Select\",\n textarea: \"Textarea\",\n dialog: \"Dialog\",\n};\n\n/** `<input type>` → shadcn primitive. Absent types (and text-likes) fall to Input. */\nconst INPUT_TYPE_REPLACEMENTS: Readonly<Record<string, string>> = {\n checkbox: \"Checkbox\",\n radio: \"RadioGroup\",\n range: \"Slider\",\n};\n\n/** `<input type>` values with no shadcn equivalent — never reported. */\nconst SKIPPED_INPUT_TYPES = new Set<string>([\"file\", \"hidden\"]);\n\nconst kebabCase = (component: string): string =>\n component.replace(/([a-z0-9])([A-Z])/g, \"$1-$2\").toLowerCase();\n\nconst literalTypeAttr = (\n node: TSESTree.JSXOpeningElement,\n): { readonly kind: \"literal\"; readonly value: string } | { readonly kind: \"dynamic\" } | null => {\n for (const attribute of node.attributes) {\n if (\n attribute.type !== AST_NODE_TYPES.JSXAttribute ||\n attribute.name.type !== AST_NODE_TYPES.JSXIdentifier ||\n attribute.name.name !== \"type\"\n ) {\n continue;\n }\n if (attribute.value?.type === AST_NODE_TYPES.Literal && typeof attribute.value.value === \"string\") {\n return { kind: \"literal\", value: attribute.value.value.toLowerCase() };\n }\n return { kind: \"dynamic\" };\n }\n return null;\n};\n\nconst hasFileAcceptAttr = (node: TSESTree.JSXOpeningElement): boolean =>\n node.attributes.some(\n (attribute) =>\n attribute.type === AST_NODE_TYPES.JSXAttribute &&\n attribute.name.type === AST_NODE_TYPES.JSXIdentifier &&\n attribute.name.name === \"accept\",\n );\n\n/** Resolve an `<input>` to its shadcn primitive, or null to skip it entirely. */\nconst resolveInputReplacement = (node: TSESTree.JSXOpeningElement): string | null => {\n const typeAttr = literalTypeAttr(node);\n if (hasFileAcceptAttr(node)) return null;\n if (typeAttr === null || typeAttr.kind === \"dynamic\") return \"Input\";\n if (SKIPPED_INPUT_TYPES.has(typeAttr.value)) return null;\n return INPUT_TYPE_REPLACEMENTS[typeAttr.value] ?? \"Input\";\n};\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"prefer-shadcn\",\n meta: {\n type: \"suggestion\",\n docs: {\n description:\n \"Prefer shadcn/ui form primitives over native `<input>`, `<select>`, `<textarea>`, and `<dialog>` elements.\",\n },\n schema: [],\n messages: {\n preferShadcn:\n \"Use the shadcn <{{replacement}}> component from @/components/ui/{{lowercase}} instead of native <{{element}}>.\",\n },\n },\n defaultOptions: [],\n create(context) {\n if (isTestFile(context.filename) || isStoryFile(context.filename)) {\n return {};\n }\n\n return {\n JSXOpeningElement(node: TSESTree.JSXOpeningElement): void {\n // Only lowercase JSXIdentifier names represent native HTML elements.\n // Member expressions (`Foo.Bar`) and namespaces (`svg:path`) are skipped.\n if (node.name.type !== \"JSXIdentifier\") {\n return;\n }\n\n const elementName = node.name.name;\n const replacement =\n elementName === \"input\" ? resolveInputReplacement(node) : REPLACEMENTS[elementName];\n\n if (replacement === undefined || replacement === null) {\n return;\n }\n\n context.report({\n node,\n messageId: \"preferShadcn\",\n data: {\n element: elementName,\n replacement,\n lowercase: kebabCase(replacement),\n },\n });\n },\n };\n },\n});\n","import {\n ESLintUtils,\n type TSESLint,\n type TSESTree,\n AST_NODE_TYPES,\n} from \"@typescript-eslint/utils\";\n\ntype MessageIds = \"missingAssertNever\";\ntype Options = readonly [];\n\n/**\n * Matches a call to `assertNever(...)` — either the bare identifier form\n * (`assertNever(x)`) or a namespaced member form (`utils.assertNever(x)`).\n */\nconst isAssertNeverCall = (expression: TSESTree.Expression): boolean => {\n if (expression.type !== AST_NODE_TYPES.CallExpression) return false;\n const callee = expression.callee;\n if (callee.type === AST_NODE_TYPES.Identifier) {\n return callee.name === \"assertNever\";\n }\n if (\n callee.type === AST_NODE_TYPES.MemberExpression &&\n !callee.computed &&\n callee.property.type === AST_NODE_TYPES.Identifier\n ) {\n return callee.property.name === \"assertNever\";\n }\n return false;\n};\n\nconst statementContainsAssertNever = (\n statement: TSESTree.Statement,\n): boolean => {\n if (statement.type === AST_NODE_TYPES.ExpressionStatement) {\n return isAssertNeverCall(statement.expression);\n }\n if (statement.type === AST_NODE_TYPES.ThrowStatement) {\n return isAssertNeverCall(statement.argument);\n }\n if (statement.type === AST_NODE_TYPES.ReturnStatement) {\n return (\n statement.argument !== null && isAssertNeverCall(statement.argument)\n );\n }\n // Recurse into block-scoped default bodies like `default: { ... }`\n if (statement.type === AST_NODE_TYPES.BlockStatement) {\n return statement.body.some(statementContainsAssertNever);\n }\n return false;\n};\n\n/**\n * Returns true if the statement performs some runtime work. An empty statement\n * or an empty block does nothing; everything else (`return`, `throw`, `break`,\n * a function call, an `if`, ...) is treated as legitimate runtime handling of\n * the default case.\n */\nconst isRuntimeHandlingStatement = (statement: TSESTree.Statement): boolean => {\n if (statement.type === AST_NODE_TYPES.EmptyStatement) return false;\n if (statement.type === AST_NODE_TYPES.BlockStatement) {\n return statement.body.some(isRuntimeHandlingStatement);\n }\n return true;\n};\n\n/**\n * A `default` clause with an empty body that is followed by another `case`\n * falls through into that case, which does the real work (e.g.\n * `default: case Single: handle();`). There is nothing to assert here.\n */\nconst isFallthroughDefault = (\n node: TSESTree.SwitchStatement,\n defaultIndex: number,\n): boolean => {\n const defaultCase = node.cases[defaultIndex];\n return (\n defaultCase !== undefined &&\n defaultCase.consequent.length === 0 &&\n defaultIndex < node.cases.length - 1\n );\n};\n\n/**\n * A `default` clause is a deliberate, documented no-op when its body is empty\n * (or an empty block) but carries a comment — e.g. `default: // do nothing`.\n * We can't distinguish a config-string switch from an exhaustive union switch\n * without type info, so a written-out intent to do nothing is honoured;\n * injecting `assertNever` there would throw at runtime. A bare empty default\n * with no comment is still flagged.\n */\nconst isCommentOnlyNoopDefault = (\n defaultCase: TSESTree.SwitchCase,\n sourceCode: Readonly<TSESLint.SourceCode>,\n): boolean => {\n if (defaultCase.consequent.length === 0) {\n const defaultToken = sourceCode.getFirstToken(defaultCase);\n const colonToken = defaultToken\n ? sourceCode.getTokenAfter(defaultToken)\n : null;\n return (\n colonToken !== null && sourceCode.getCommentsAfter(colonToken).length > 0\n );\n }\n const only = defaultCase.consequent[0];\n if (\n only !== undefined &&\n defaultCase.consequent.length === 1 &&\n only.type === AST_NODE_TYPES.BlockStatement &&\n only.body.length === 0\n ) {\n return sourceCode.getCommentsInside(only).length > 0;\n }\n return false;\n};\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"require-assert-never\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Require an exhaustive-style switch whose `default` case does no runtime work to call `assertNever(_)` so that discriminated unions are exhaustively checked at compile time. Switches with a legitimate runtime default (a reducer's `return state`, an HTTP-status `return fallback()`, a `break`, a `throw`, etc.) are left alone.\",\n },\n schema: [],\n messages: {\n missingAssertNever:\n \"Empty switch `default` case — add runtime handling or call `assertNever()` so the discriminated union is exhaustively checked at compile time.\",\n },\n },\n defaultOptions: [],\n create(context) {\n return {\n SwitchStatement(node: TSESTree.SwitchStatement): void {\n const defaultIndex = node.cases.findIndex(\n (caseNode) => caseNode.test === null,\n );\n // No `default` at all is fine — we don't demand exhaustiveness of every\n // switch, only of those that opted into a (no-op) default.\n if (defaultIndex === -1) return;\n const defaultCase = node.cases[defaultIndex];\n if (defaultCase === undefined) return;\n\n // An explicit `assertNever(...)` is the canonical exhaustiveness check.\n if (defaultCase.consequent.some(statementContainsAssertNever)) return;\n\n // A default that does real runtime work (return a fallback, break,\n // throw, log, ...) is legitimate — don't demand assertNever there.\n if (defaultCase.consequent.some(isRuntimeHandlingStatement)) return;\n\n // A fallthrough default hands control to a following case, which does\n // the work — there is nothing to assert.\n if (isFallthroughDefault(node, defaultIndex)) return;\n\n // A deliberate, comment-documented no-op default is honoured; we can't\n // prove the discriminant is a union without type info, and injecting\n // assertNever would throw at runtime on a config-string switch.\n if (isCommentOnlyNoopDefault(defaultCase, context.sourceCode)) return;\n\n // Otherwise the default is a bare, undocumented no-op: flag it.\n context.report({\n node: defaultCase,\n messageId: \"missingAssertNever\",\n });\n },\n };\n },\n});\n","/**\n * @fileoverview Require Zod validation on values read out of a `FormData`.\n * `formData.get(k)` returns `FormDataEntryValue | null` — an unvalidated,\n * attacker-controlled value. Pipe it through a schema before use.\n *\n * Validation is recognised two ways:\n * - INLINE: the `.get(...)` call sits inside a Zod `.parse(...)` /\n * `.safeParse(...)` — found by walking up the parent chain.\n * - VIA A BINDING: the value is bound and validated one or more statements\n * later, which is how a real handler reads. Tracked through the scope\n * manager (the same approach `prefer-schema-for-api-payload` uses for\n * `response.json()`), so this is not reported:\n * const tokenRaw = formData.get(\"t\");\n * const parsed = ZForm.safeParse({ t: typeof tokenRaw === \"string\" ? tokenRaw : undefined });\n *\n * A binding narrowed by `instanceof File` / `instanceof Blob` is also exempt: a\n * Zod schema has nothing useful to say about a `File`, and `instanceof` IS the\n * validation for that branch.\n *\n * A Zod receiver is recognised by name — `Schema`-suffixed (`userSchema`), the\n * `Z<Capital>` house form (`ZUser`), or the bare `z` builder — matching\n * `zod-naming-convention`, which accepts both conventions.\n *\n * TEST FILES ARE EXEMPT. Corpus sweep (2220 files across zod / TanStack Query /\n * react-router / swr / zustand, 2026-07): 42 raw hits, 40 of them in\n * react-router suites and every one an assertion rather than a trust boundary.\n * `react-router/packages/react-router/__tests__/dom/data-browser-router-test.tsx:4183`\n * is the shape — `let formData = await actionSpy.mock.calls[0][0].request.formData();\n * expect(formData.get(\"a\")).toBe(\"1\")`. The FormData was built by the test two\n * lines earlier; there is no attacker, and validating it with a schema would\n * assert the schema instead of the router. The rule's premise — \"this value is\n * attacker-controlled\" — is false by construction in a fixture.\n */\n\nimport { ESLintUtils, type TSESTree, AST_NODE_TYPES } from \"@typescript-eslint/utils\";\nimport type { RuleContext, Scope } from \"@typescript-eslint/utils/ts-eslint\";\n\nimport { isTestFile } from \"./_paths.js\";\nimport { ZOD_SCHEMA_NAME_RE } from \"./_zod.js\";\n\ntype MessageIds = \"missingZodValidation\";\ntype Options = readonly [];\n\ntype Ctx = Readonly<RuleContext<MessageIds, Options>>;\n\n/**\n * Walk down a (possibly chained) receiver expression and decide whether it\n * originates from something that looks like a Zod schema — the bare `z` builder\n * (`z.object({...}).parse(...)`), a `Schema`-suffixed identifier\n * (`userSchema.parse(...)`), or a `Z`-prefixed identifier (`ZUser.parse(...)`).\n * Non-Zod receivers like `JSON` / `Date` are intentionally rejected.\n */\nconst looksLikeZodSchema = (node: TSESTree.Node): boolean => {\n let current: TSESTree.Node = node;\n while (true) {\n if (current.type === AST_NODE_TYPES.Identifier) {\n return current.name === \"z\" || ZOD_SCHEMA_NAME_RE.test(current.name);\n }\n if (current.type === AST_NODE_TYPES.CallExpression) {\n current = current.callee;\n continue;\n }\n if (current.type === AST_NODE_TYPES.MemberExpression) {\n current = current.object;\n continue;\n }\n return false;\n }\n};\n\n/**\n * Matches a Zod validation call: `<ZodSchema>.parse(...)` or\n * `<ZodSchema>.safeParse(...)`. Keys off the *receiver* looking like a Zod\n * schema rather than the method name alone, so `JSON.parse(...)` /\n * `Date.parse(...)` are NOT treated as validation.\n */\nconst isZodParseCall = (node: TSESTree.Node): boolean => {\n if (node.type !== AST_NODE_TYPES.CallExpression) return false;\n const callee = node.callee;\n if (callee.type !== AST_NODE_TYPES.MemberExpression) return false;\n if (callee.computed) return false;\n if (callee.property.type !== AST_NODE_TYPES.Identifier) return false;\n const method = callee.property.name;\n if (method !== \"parse\" && method !== \"safeParse\") return false;\n return looksLikeZodSchema(callee.object);\n};\n\n/**\n * Matches an (optionally awaited) `<x>.formData()` call — the canonical way a\n * `FormData` object is obtained from a `Request` / `Response`.\n */\nconst isFormDataMethodCall = (node: TSESTree.Node): boolean => {\n let current: TSESTree.Node = node;\n if (current.type === AST_NODE_TYPES.AwaitExpression) {\n current = current.argument;\n }\n if (current.type !== AST_NODE_TYPES.CallExpression) return false;\n const callee = current.callee;\n return (\n callee.type === AST_NODE_TYPES.MemberExpression &&\n !callee.computed &&\n callee.property.type === AST_NODE_TYPES.Identifier &&\n callee.property.name === \"formData\"\n );\n};\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"require-zod-form-validation\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Require Zod validation (`Schema.parse(...)` / `Schema.safeParse(...)`) when reading values out of a `FormData` object.\",\n },\n schema: [],\n messages: {\n missingZodValidation:\n \"FormData parsing must use Zod schema validation (e.g., Schema.parse() / Schema.safeParse())\",\n },\n },\n defaultOptions: [],\n create(context: Ctx) {\n if (isTestFile(context.filename)) {\n return {};\n }\n\n // A receiver is a FormData source if its name reads like form data, or if\n // it is a binding initialized from a `.formData()` call.\n const isFormSourceIdentifier = (node: TSESTree.Node): boolean => {\n if (node.type !== AST_NODE_TYPES.Identifier) return false;\n if (/formdata/i.test(node.name)) return true;\n\n let scope: Scope.Scope | null = context.sourceCode.getScope(node);\n while (scope !== null) {\n const variable = scope.set.get(node.name);\n if (variable !== undefined && variable.defs.length === 1) {\n const def = variable.defs[0];\n if (\n def !== undefined &&\n def.type === \"Variable\" &&\n def.node.type === AST_NODE_TYPES.VariableDeclarator &&\n def.node.init !== null\n ) {\n return isFormDataMethodCall(def.node.init);\n }\n return false;\n }\n scope = scope.upper;\n }\n return false;\n };\n\n const isFormDataGetCall = (node: TSESTree.CallExpression): boolean => {\n const callee = node.callee;\n if (callee.type !== AST_NODE_TYPES.MemberExpression) return false;\n if (\n callee.property.type !== AST_NODE_TYPES.Identifier ||\n callee.property.name !== \"get\"\n ) {\n return false;\n }\n return isFormSourceIdentifier(callee.object);\n };\n\n // Walk up from `node` looking for a surrounding Zod `.parse(...)` /\n // `.safeParse(...)` call. `.parent` is `null` at the Program root, so we\n // must guard for both null and undefined.\n const hasZodParseAncestor = (node: TSESTree.Node): boolean => {\n let parent: TSESTree.Node | null | undefined = node.parent;\n while (parent !== null && parent !== undefined) {\n if (isZodParseCall(parent)) return true;\n parent = parent.parent;\n }\n return false;\n };\n\n // `x instanceof File` / `x instanceof Blob` — a Zod schema adds nothing over\n // the `instanceof` narrowing for a binary upload.\n const isInstanceofNarrowing = (node: TSESTree.Node): boolean => {\n const parent: TSESTree.Node | null | undefined = node.parent;\n return (\n parent !== null &&\n parent !== undefined &&\n parent.type === AST_NODE_TYPES.BinaryExpression &&\n parent.operator === \"instanceof\" &&\n parent.left === node\n );\n };\n\n /** The identifier this `.get(...)` call is bound to, or null. */\n const boundDeclarator = (\n node: TSESTree.CallExpression,\n ): TSESTree.VariableDeclarator | null => {\n const parent = node.parent;\n if (\n parent.type === AST_NODE_TYPES.VariableDeclarator &&\n parent.init === node &&\n parent.id.type === AST_NODE_TYPES.Identifier\n ) {\n return parent;\n }\n return null;\n };\n\n /** Is any read of this binding validated (Zod parse, or `instanceof File`)? */\n const bindingIsValidated = (\n declarator: TSESTree.VariableDeclarator,\n ): boolean => {\n const variable = context.sourceCode.getDeclaredVariables(declarator)[0];\n if (variable === undefined) return false;\n return variable.references.some(\n (ref) =>\n hasZodParseAncestor(ref.identifier) ||\n isInstanceofNarrowing(ref.identifier),\n );\n };\n\n return {\n CallExpression(node: TSESTree.CallExpression): void {\n if (!isFormDataGetCall(node)) return;\n\n if (hasZodParseAncestor(node) || isInstanceofNarrowing(node)) return;\n\n const declarator = boundDeclarator(node);\n if (declarator !== null && bindingIsValidated(declarator)) return;\n\n context.report({\n node,\n messageId: \"missingZodValidation\",\n });\n },\n };\n },\n});\n","/**\n * @fileoverview The single source of truth for \"does this name read as a Zod\n * schema?\". Shared by `zod-naming-convention` (which ENFORCES a convention) and\n * `require-zod-form-validation` (which RECOGNISES a schema receiver), because\n * the two disagreeing is a bug: a plugin that accepts `SubmitFormSchema` as a\n * validator in one rule must not call the same symbol non-conforming in another.\n *\n * Two conventions are recognised, and both are correct:\n * - PREFIX (`ZUser`) — lets a schema and its inferred type share a base name\n * (`type User = z.infer<typeof ZUser>`) without collision.\n * - SUFFIX (`userSchema`, `SubmitFormDataSchema`) — the dominant convention in\n * the wider Zod ecosystem and in most existing codebases.\n */\n\n/** The `Z<Capital>` prefix convention: `ZUser`, `ZSubmitForm`. */\nexport const ZOD_PREFIX_RE = /^Z[A-Z]/;\n\n/** The `XxxSchema` suffix convention: `userSchema`, `SubmitFormDataSchema`. */\nexport const ZOD_SUFFIX_RE = /Schema$/;\n\n/** Either accepted convention. */\nexport const ZOD_SCHEMA_NAME_RE = /Schema$|^Z[A-Z]/;\n","/**\n * @fileoverview Enforce a consistent naming convention for Zod schemas, so a\n * schema is recognisable at its use site without chasing the declaration.\n *\n * BOTH conventions are accepted by default (`convention: \"either\"`):\n * - `Z`-prefix (`ZUser = z.object({...})`) — lets a schema and its inferred\n * type share a base name (`type User = z.infer<typeof ZUser>`).\n * - `Schema`-suffix (`userSchema`, `SubmitFormDataSchema`) — the dominant\n * convention in the wider Zod ecosystem and in most existing codebases.\n *\n * Defaulting to prefix-only was wrong on both counts. It contradicted the rest\n * of the plugin — `require-zod-form-validation` accepts `/Schema$|^Z[A-Z]/` as\n * \"this is a Zod schema\" — and on a real 42k-LOC codebase that uniformly uses\n * the suffix it declared 220 symbols non-conforming with no defect behind any of\n * them. Both regexes now come from `_zod.ts` so the two rules cannot drift\n * apart again.\n *\n * A team that wants exactly one form sets `convention: \"prefix\"` or\n * `convention: \"suffix\"`; the point of the rule is consistency, and either\n * choice delivers it.\n *\n * FALSE POSITIVES FOUND BY A 2220-FILE CORPUS SWEEP (zod / TanStack Query /\n * react-router / swr / zustand, 2026-07). The rule fired 1816 times, ALL of\n * them in the `zod` repo and none of them a defect. Three guards remove them:\n *\n * (a) TEST FILES — 1783 / 1816 hits. A schema declared inside a test is a\n * throwaway fixture named `a`, `b`, `t1`, read three lines below its\n * declaration; there is no cross-module use site for the convention to\n * serve. `zod/packages/zod/src/v4/classic/tests/index.test.ts:782`\n * (`const a = z.lazy(() => z.string())`) is representative — that file\n * alone contributed 70 reports.\n * (b) TERMINAL CALLS THAT DO NOT RETURN A SCHEMA — ~280 hits. The rule keyed\n * off \"the callee chain starts at `z`\", which is also true of\n * `z.string().safeParse(x)` (a RESULT), `z.toJSONSchema(s)` (a plain JSON\n * object), `z.registry()` (a registry), `codec.encode(v)` / `.decode(v)`\n * (a converted VALUE) and `z.function(...).implement(fn)` (a function).\n * Demanding a `Schema` suffix on any of those is simply wrong.\n * `zod/packages/zod/src/v3/tests/record.test.ts:166`\n * (`const result1 = z.record(z.any()).parse({ foo: undefined })`).\n * (c) NAMES THAT ALREADY SAY \"SCHEMA\" — 421 hits. `ZOD_SUFFIX_RE` is anchored\n * AND case-sensitive, so `schema`, `schema1` and `numberSchemaOptional`\n * were all declared non-conforming despite being unmistakable at a\n * glance. `schema` alone accounts for 371 reports, e.g.\n * `zod/packages/treeshake/zod-string.ts:3`. This widens only the ACCEPT\n * side, so it cannot make `require-zod-form-validation` — which uses the\n * shared `_zod.ts` recognisers to accept a receiver — reject anything it\n * previously accepted.\n */\n\nimport { ESLintUtils, type TSESTree, AST_NODE_TYPES } from \"@typescript-eslint/utils\";\n\nimport { isGeneratedFile, isTestFile } from \"./_paths.js\";\nimport { ZOD_PREFIX_RE, ZOD_SCHEMA_NAME_RE, ZOD_SUFFIX_RE } from \"./_zod.js\";\n\ntype MessageIds = \"zPrefix\" | \"schemaSuffix\" | \"zodSchemaName\";\ntype Convention = \"prefix\" | \"suffix\" | \"either\";\ntype Options = readonly [\n {\n convention?: Convention;\n }?,\n];\n\nconst CONVENTIONS: Record<\n Convention,\n { readonly test: RegExp; readonly messageId: MessageIds }\n> = {\n prefix: { test: ZOD_PREFIX_RE, messageId: \"zPrefix\" },\n suffix: { test: ZOD_SUFFIX_RE, messageId: \"schemaSuffix\" },\n either: { test: ZOD_SCHEMA_NAME_RE, messageId: \"zodSchemaName\" },\n};\n\n/**\n * Guard (c): the binding already reads as a schema. `ZOD_SUFFIX_RE` is anchored\n * and case-sensitive by design (it is also the *recogniser* other rules use), so\n * it rejects `schema`, `schema1` and `numberSchemaOptional`. Those need no\n * rename — the whole point of the convention is that a reader spots a schema at\n * the use site, and every one of them does. Accepting them here only widens what\n * this rule tolerates; it never makes the shared recogniser stricter.\n */\nconst CONTAINS_SCHEMA_RE = /schema/i;\n\n/**\n * Guard (b): terminal methods on a `z.…` chain whose result is NOT a schema.\n * `.parse`/`.safeParse` return a parsed value or a result envelope, the codec\n * methods return the converted value, `z.toJSONSchema` returns a plain JSON\n * object, `z.registry()` returns a registry, and `.implement()` returns a\n * function. Naming any of them `xSchema` would be a lie.\n */\nconst NON_SCHEMA_TERMINALS: ReadonlySet<string> = new Set([\n \"parse\",\n \"parseAsync\",\n \"safeParse\",\n \"safeParseAsync\",\n \"encode\",\n \"decode\",\n \"encodeAsync\",\n \"decodeAsync\",\n \"safeEncode\",\n \"safeDecode\",\n \"safeEncodeAsync\",\n \"safeDecodeAsync\",\n \"toJSONSchema\",\n \"registry\",\n \"implement\",\n]);\n\n/** The method name a call chain ends on, e.g. `safeParse` for `z.string().safeParse(x)`. */\nconst terminalMethodName = (callee: TSESTree.MemberExpression): string | null =>\n !callee.computed && callee.property.type === AST_NODE_TYPES.Identifier\n ? callee.property.name\n : null;\n\n/**\n * Walks down a (possibly chained) callee like `z.object().extend().refine()` and\n * returns `true` if the chain originates from a bare `z` identifier — i.e. the\n * outermost MemberExpression on the chain has `z` as its receiver.\n */\nconst calleeChainStartsWithZ = (node: TSESTree.Node): boolean => {\n let current: TSESTree.Node = node;\n\n while (current.type === AST_NODE_TYPES.MemberExpression) {\n const receiver: TSESTree.Node = current.object;\n if (receiver.type === AST_NODE_TYPES.Identifier && receiver.name === \"z\") {\n return true;\n }\n if (receiver.type === AST_NODE_TYPES.CallExpression) {\n current = receiver.callee;\n continue;\n }\n return false;\n }\n\n return false;\n};\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"zod-naming-convention\",\n meta: {\n type: \"suggestion\",\n docs: {\n description:\n \"Enforce a consistent Zod schema naming convention — a `Z` prefix (`ZUser`) or a `Schema` suffix (`userSchema`); both are accepted by default.\",\n },\n schema: [\n {\n type: \"object\",\n additionalProperties: false,\n properties: {\n convention: {\n type: \"string\",\n enum: [\"prefix\", \"suffix\", \"either\"],\n },\n },\n },\n ],\n messages: {\n zPrefix: \"Zod schema names should start with Z (e.g. `ZUser`)\",\n schemaSuffix: \"Zod schema names should end with Schema (e.g. `userSchema`)\",\n zodSchemaName:\n \"Zod schema names should start with Z (`ZUser`) or end with Schema (`userSchema`)\",\n },\n },\n defaultOptions: [{}],\n create(context, [optionsArg]) {\n const convention = optionsArg?.convention ?? \"either\";\n const { test, messageId } = CONVENTIONS[convention];\n // Guard (c) relaxes the SUFFIX spelling, so it must not apply to a team that\n // explicitly asked for prefix-only — there `userschema` really is off-convention.\n const acceptsSchemaWord = convention !== \"prefix\";\n\n // Guard (a): a schema declared in a test is a local fixture, not an API.\n if (isTestFile(context.filename) || isGeneratedFile(context.filename, context.sourceCode.text)) {\n return {};\n }\n\n return {\n VariableDeclarator(node: TSESTree.VariableDeclarator): void {\n const init = node.init;\n if (init === null || init === undefined) return;\n if (init.type !== AST_NODE_TYPES.CallExpression) return;\n\n const callee = init.callee;\n if (callee.type !== AST_NODE_TYPES.MemberExpression) return;\n\n if (!calleeChainStartsWithZ(callee)) return;\n\n // Guard (b): the chain ends on a call that yields a value/result, not a schema.\n const terminal = terminalMethodName(callee);\n if (terminal !== null && NON_SCHEMA_TERMINALS.has(terminal)) return;\n\n if (node.id.type !== AST_NODE_TYPES.Identifier) return;\n if (test.test(node.id.name)) return;\n // Guard (c): the name already reads as a schema in any casing.\n if (acceptsSchemaWord && CONTAINS_SCHEMA_RE.test(node.id.name)) return;\n\n context.report({\n node: node.id,\n messageId,\n });\n },\n };\n },\n});\n","/**\n * @fileoverview TS port of Python SARJ028\n * (`no-cors-wildcard-with-credentials`). Flags CORS configuration that reflects\n * ANY origin (`\"*\"`) while ALSO allowing credentials. The browser treats\n * `Access-Control-Allow-Origin: *` together with\n * `Access-Control-Allow-Credentials: true` as a directive to reflect the\n * request's Origin and expose authenticated (cookie/session) responses — which\n * lets any website read them cross-origin. That is a credential-theft surface.\n *\n * Two shapes are detected, and BOTH the wildcard origin and credentials=true\n * must co-occur before the rule fires (a `\"*\"` origin without credentials, or\n * credentials with a specific origin, is safe and is NOT reported):\n *\n * 1. A `cors(...)` / `new Cors(...)` call whose options `ObjectExpression`\n * has `credentials: true` AND an `origin` property whose value subtree\n * contains a `\"*\"` string literal anywhere — the bare `\"*\"`, the `[\"*\"]`\n * array, or a `flag ? origins : \"*\"` conditional branch. Reported at the\n * call.\n *\n * 2. Manual header setting where, within the SAME function (or module) scope,\n * `Access-Control-Allow-Origin` is set to `\"*\"` AND\n * `Access-Control-Allow-Credentials` is set to `\"true\"` — via\n * `res.setHeader(...)`, `headers.set(...)` / `.append(...)` (covers\n * `NextResponse` header objects), or a single object literal\n * `{ \"Access-Control-Allow-Origin\": \"*\", \"Access-Control-Allow-Credentials\": \"true\" }`.\n * Header-name matching is case-insensitive. The object-literal form is\n * reported at the object; the split `setHeader`/`set` form is reported at\n * the wildcard-origin call.\n *\n * References:\n * - https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#credentialed_requests_and_wildcards\n */\n\nimport { ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\ntype MessageIds = \"corsWildcardWithCredentials\";\ntype Options = readonly [];\n\nconst ACAO_HEADER = \"access-control-allow-origin\";\nconst ACAC_HEADER = \"access-control-allow-credentials\";\nconst HEADER_SET_METHODS = new Set([\"setheader\", \"set\", \"append\"]);\n\n/**\n * True only for the boolean literal `true` (not `1`, not a truthy expression).\n */\nfunction isTrueLiteral(node: TSESTree.Node): boolean {\n return node.type === \"Literal\" && node.value === true;\n}\n\n/**\n * True if `node` is a string literal whose value equals `\"true\"`\n * (case-insensitive), or the boolean literal `true`. Header values are strings,\n * but some frameworks coerce a boolean, so both are accepted.\n */\nfunction isCredentialsTrueValue(node: TSESTree.Node): boolean {\n if (node.type === \"Literal\") {\n if (node.value === true) {\n return true;\n }\n if (typeof node.value === \"string\") {\n return node.value.trim().toLowerCase() === \"true\";\n }\n }\n return false;\n}\n\n/**\n * True if `node` is the string literal `\"*\"`.\n */\nfunction isStarLiteral(node: TSESTree.Node): boolean {\n return node.type === \"Literal\" && node.value === \"*\";\n}\n\n/**\n * True if a `\"*\"` string literal appears anywhere in `node`'s subtree. Walking\n * the whole subtree catches `\"*\"`, `[\"*\"]`, and the `flag ? origins : \"*\"`\n * conditional branch. A dynamic `origin: someVar` has no `\"*\"` literal, so it\n * does not fire.\n */\nfunction subtreeContainsStarLiteral(node: TSESTree.Node): boolean {\n if (isStarLiteral(node)) {\n return true;\n }\n for (const key of Object.keys(node)) {\n if (key === \"parent\" || key === \"loc\" || key === \"range\") {\n continue;\n }\n const value = (node as unknown as Record<string, unknown>)[key];\n if (Array.isArray(value)) {\n for (const child of value) {\n if (isNode(child) && subtreeContainsStarLiteral(child)) {\n return true;\n }\n }\n } else if (isNode(value) && subtreeContainsStarLiteral(value)) {\n return true;\n }\n }\n return false;\n}\n\nfunction isNode(value: unknown): value is TSESTree.Node {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as { type?: unknown }).type === \"string\"\n );\n}\n\n/**\n * Returns the (non-computed) string name of a property key, or `undefined`.\n */\nfunction propertyKeyName(prop: TSESTree.Property): string | undefined {\n if (prop.computed) {\n return undefined;\n }\n const key = prop.key;\n if (key.type === \"Identifier\") {\n return key.name;\n }\n if (key.type === \"Literal\" && typeof key.value === \"string\") {\n return key.value;\n }\n return undefined;\n}\n\n/**\n * Extracts the callee's terminal identifier name for a call/new expression\n * (`cors` for `cors(...)` and `app.cors(...)`, `Cors` for `new Cors(...)`).\n */\nfunction calleeName(\n node: TSESTree.CallExpression | TSESTree.NewExpression,\n): string | undefined {\n const callee = node.callee;\n if (callee.type === \"Identifier\") {\n return callee.name;\n }\n if (\n callee.type === \"MemberExpression\" &&\n !callee.computed &&\n callee.property.type === \"Identifier\"\n ) {\n return callee.property.name;\n }\n return undefined;\n}\n\n/**\n * Detects the `cors({ origin: \"*\", credentials: true })` shape. Returns true\n * when the callee is `cors` / `Cors` and the first ObjectExpression argument\n * has `credentials: true` AND an `origin` whose subtree contains `\"*\"`.\n */\nfunction isCorsWildcardCredentialsCall(\n node: TSESTree.CallExpression | TSESTree.NewExpression,\n): boolean {\n const name = calleeName(node);\n if (name === undefined || name.toLowerCase() !== \"cors\") {\n return false;\n }\n const options = node.arguments.find(\n (arg): arg is TSESTree.ObjectExpression => arg.type === \"ObjectExpression\",\n );\n if (options === undefined) {\n return false;\n }\n let hasCredentials = false;\n let hasWildcardOrigin = false;\n for (const prop of options.properties) {\n if (prop.type !== \"Property\") {\n continue;\n }\n const key = propertyKeyName(prop);\n if (key === \"credentials\" && isTrueLiteral(prop.value)) {\n hasCredentials = true;\n } else if (key === \"origin\" && subtreeContainsStarLiteral(prop.value)) {\n hasWildcardOrigin = true;\n }\n }\n return hasCredentials && hasWildcardOrigin;\n}\n\n/**\n * True if the ObjectExpression is a header map setting BOTH\n * `Access-Control-Allow-Origin: \"*\"` and\n * `Access-Control-Allow-Credentials: \"true\"` (case-insensitive keys).\n */\nfunction isWildcardCredentialsHeaderObject(\n node: TSESTree.ObjectExpression,\n): boolean {\n let wildcardOrigin = false;\n let credentialsTrue = false;\n for (const prop of node.properties) {\n if (prop.type !== \"Property\") {\n continue;\n }\n const key = propertyKeyName(prop);\n if (key === undefined) {\n continue;\n }\n const header = key.toLowerCase();\n if (header === ACAO_HEADER && isStarLiteral(prop.value)) {\n wildcardOrigin = true;\n } else if (header === ACAC_HEADER && isCredentialsTrueValue(prop.value)) {\n credentialsTrue = true;\n }\n }\n return wildcardOrigin && credentialsTrue;\n}\n\ntype HeaderSetKind = \"origin\" | \"credentials\";\n\n/**\n * Classifies a `x.setHeader(name, value)` / `x.set(name, value)` /\n * `x.append(name, value)` call as an ACAO-wildcard set, an ACAC-true set, or\n * neither.\n */\nfunction classifyHeaderSetCall(\n node: TSESTree.CallExpression,\n): HeaderSetKind | undefined {\n const callee = node.callee;\n if (\n callee.type !== \"MemberExpression\" ||\n callee.computed ||\n callee.property.type !== \"Identifier\" ||\n !HEADER_SET_METHODS.has(callee.property.name.toLowerCase())\n ) {\n return undefined;\n }\n const [nameArg, valueArg] = node.arguments;\n if (\n nameArg === undefined ||\n valueArg === undefined ||\n nameArg.type !== \"Literal\" ||\n typeof nameArg.value !== \"string\"\n ) {\n return undefined;\n }\n const header = nameArg.value.toLowerCase();\n if (header === ACAO_HEADER && isStarLiteral(valueArg)) {\n return \"origin\";\n }\n if (header === ACAC_HEADER && isCredentialsTrueValue(valueArg)) {\n return \"credentials\";\n }\n return undefined;\n}\n\n/**\n * Nearest enclosing function node, or `undefined` for module scope. Used to\n * group split `setHeader`/`set` header assignments so a wildcard origin and a\n * credentials=true set only pair up when they live in the same scope.\n */\nfunction enclosingScope(node: TSESTree.Node): TSESTree.Node | undefined {\n let current: TSESTree.Node | undefined = node.parent;\n while (current) {\n if (\n current.type === \"FunctionDeclaration\" ||\n current.type === \"FunctionExpression\" ||\n current.type === \"ArrowFunctionExpression\"\n ) {\n return current;\n }\n current = current.parent;\n }\n return undefined;\n}\n\ninterface ScopeHeaderSets {\n originNodes: TSESTree.CallExpression[];\n credentialsNodes: TSESTree.CallExpression[];\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-cors-wildcard-with-credentials\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n 'Disallow CORS that reflects any Origin (`\"*\"`) while allowing credentials; any site could then read authenticated responses. Enumerate explicit trusted origins instead.',\n },\n schema: [],\n messages: {\n corsWildcardWithCredentials:\n 'CORS reflects any Origin (`\"*\"`) while allowing credentials — any site can read authenticated responses. Enumerate explicit trusted origins instead of using `\"*\"` with credentials.',\n },\n },\n defaultOptions: [],\n create(context) {\n const scopeHeaderSets = new Map<TSESTree.Node | \"module\", ScopeHeaderSets>();\n\n function recordHeaderSet(\n node: TSESTree.CallExpression,\n kind: HeaderSetKind,\n ): void {\n const key = enclosingScope(node) ?? \"module\";\n let entry = scopeHeaderSets.get(key);\n if (entry === undefined) {\n entry = { originNodes: [], credentialsNodes: [] };\n scopeHeaderSets.set(key, entry);\n }\n if (kind === \"origin\") {\n entry.originNodes.push(node);\n } else {\n entry.credentialsNodes.push(node);\n }\n }\n\n return {\n NewExpression(node: TSESTree.NewExpression): void {\n if (isCorsWildcardCredentialsCall(node)) {\n context.report({ node, messageId: \"corsWildcardWithCredentials\" });\n }\n },\n CallExpression(node: TSESTree.CallExpression): void {\n if (isCorsWildcardCredentialsCall(node)) {\n context.report({ node, messageId: \"corsWildcardWithCredentials\" });\n return;\n }\n const kind = classifyHeaderSetCall(node);\n if (kind !== undefined) {\n recordHeaderSet(node, kind);\n }\n },\n ObjectExpression(node: TSESTree.ObjectExpression): void {\n if (isWildcardCredentialsHeaderObject(node)) {\n context.report({ node, messageId: \"corsWildcardWithCredentials\" });\n }\n },\n \"Program:exit\"(): void {\n for (const { originNodes, credentialsNodes } of scopeHeaderSets.values()) {\n if (originNodes.length > 0 && credentialsNodes.length > 0) {\n for (const node of originNodes) {\n context.report({\n node,\n messageId: \"corsWildcardWithCredentials\",\n });\n }\n }\n }\n },\n };\n },\n});\n","/**\n * @fileoverview Flag `.catch()` handlers that silently swallow the rejection:\n * `promise.catch(() => null)` and friends. These are the promise-chain twin of\n * the empty-`catch`-block anti-pattern already covered by the try/catch rules —\n * the failure vanishes with no log, no metric, no rethrow, and the caller\n * receives a sentinel it usually cannot distinguish from a real value.\n *\n * Only handlers that provably do nothing are flagged: an arrow/function whose\n * entire body is a bare literal (`null`, `undefined`, a number, a string, a\n * boolean), an empty object/array literal, an empty block, or a block that\n * only `return`s one of those. A handler that references its error parameter\n * or calls anything (logging, metrics, rethrow) is never flagged.\n *\n * Two corpus-driven exemptions (5-repo sweep, 2026-07: 11/35 raw hits — 31% —\n * were these deliberate idioms):\n * - `res.json().catch(() => ({}))` / `res.text().catch(() => '')` — the\n * body-parse-fallback idiom while composing error diagnostics; the parse\n * failure itself is not the signal being handled.\n * - Test files (`.test.` / `.spec.` / `__tests__/`), where silencing a\n * promise is routine unhandled-rejection suppression.\n *\n * A SECOND SWEEP (2220 files across zod / TanStack Query / react-router / swr /\n * zustand, 2026-07) produced 16 hits, of which 11 were false positives in three\n * families. All three are now exempt:\n *\n * - **Documented on purpose.** The handler body (or the line above / beside\n * the call) carries a comment explaining the suppression. The rule's\n * complaint is that the decision is invisible; a comment is exactly the\n * thing that makes it visible, and the author has already answered.\n * `query/packages/query-core/src/thenable.ts:54`\n * (`thenable.catch(() => { /* prevent unhandled rejection errors *\\/ })`) and\n * `react-router/packages/react-router/lib/router/router.ts:6052-6053`\n * (`// Prevent unhandled rejection errors - handled inside of \\`callLoadOrAction\\``\n * directly above `lazyRoutePromise.catch(() => {})`) are the shape: the\n * promise is ALSO consumed by the real handler, and this `.catch` exists only\n * to stop the runtime's unhandled-rejection warning. 7 of the 16 hits.\n * - **The chain continues.** `p.catch(() => null).then(…)` — the sentinel is\n * consumed by the very next link, which IS the recovery, so no caller ever\n * receives an indistinguishable value.\n * `react-router/integration/helpers/playwright-fixture.ts:318`.\n * - **Teardown calls.** `.cancel()` / `.close()` / `.abort()` / `.destroy()` /\n * `.dispose()` / `.unlock()` reject when the resource is already gone, which\n * is the outcome the caller wanted; the rejection is not the signal, exactly\n * as for the `res.json()` fallback above.\n * `react-router/packages/react-router/lib/rsc/html-stream/server.ts:80`\n * (`await rscReader.cancel(reason).catch(() => {})`, inside the stream's own\n * abort path). 3 of the 16 hits.\n */\n\nimport { AST_NODE_TYPES, ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\nimport { isTestFile } from \"./_paths.js\";\n\ntype MessageIds = \"silentCatch\";\ntype Options = readonly [];\n\n/** True for `x.json()` / `x.text()` — the receiver of a body-parse-fallback catch. */\nfunction isBodyParseCall(node: TSESTree.Expression): boolean {\n return (\n node.type === AST_NODE_TYPES.CallExpression &&\n node.arguments.length === 0 &&\n node.callee.type === AST_NODE_TYPES.MemberExpression &&\n !node.callee.computed &&\n node.callee.property.type === AST_NODE_TYPES.Identifier &&\n (node.callee.property.name === \"json\" ||\n node.callee.property.name === \"text\")\n );\n}\n\n/**\n * Teardown methods that reject when the resource is already gone — which is the\n * state the caller was asking for. The rejection carries no signal, so silencing\n * it is correct, the same reasoning as the `res.json()` fallback above.\n */\nconst TEARDOWN_METHODS: ReadonlySet<string> = new Set([\n \"cancel\",\n \"close\",\n \"abort\",\n \"destroy\",\n \"dispose\",\n \"release\",\n \"unlock\",\n \"disconnect\",\n]);\n\n/**\n * Tooling directives are machinery, not an explanation of the swallow. An\n * `eslint-disable` already suppresses the report through the normal channel, so\n * counting it as documentation would turn every directive into an unused one.\n */\nconst DIRECTIVE_COMMENT_RE =\n /^\\s*(eslint-|@ts-|prettier-ignore|biome-ignore|c8 |v8 |istanbul )/;\n\nconst isExplanatory = (comment: { value: string }): boolean =>\n !DIRECTIVE_COMMENT_RE.test(comment.value);\n\n/** True for `reader.cancel(reason)` / `stream.close()` — a teardown receiver. */\nfunction isTeardownCall(node: TSESTree.Expression): boolean {\n return (\n node.type === AST_NODE_TYPES.CallExpression &&\n node.callee.type === AST_NODE_TYPES.MemberExpression &&\n !node.callee.computed &&\n node.callee.property.type === AST_NODE_TYPES.Identifier &&\n TEARDOWN_METHODS.has(node.callee.property.name)\n );\n}\n\n/** True for expressions that provably discard the error: bare literals,\n * `undefined`, empty object/array literals. */\nfunction isSilentExpression(node: TSESTree.Expression): boolean {\n switch (node.type) {\n case AST_NODE_TYPES.Literal:\n // null / number / string / boolean literals (regex literals excluded —\n // nobody writes `.catch(() => /x/)` and they are not sentinel values).\n return !(\"regex\" in node);\n case AST_NODE_TYPES.Identifier:\n return node.name === \"undefined\";\n case AST_NODE_TYPES.UnaryExpression:\n // `void 0` — the other spelling of undefined.\n return (\n node.operator === \"void\" &&\n node.argument.type === AST_NODE_TYPES.Literal\n );\n case AST_NODE_TYPES.ObjectExpression:\n return node.properties.length === 0;\n case AST_NODE_TYPES.ArrayExpression:\n return node.elements.length === 0;\n case AST_NODE_TYPES.TSAsExpression:\n // `.catch(() => null as Foo | null)` is still silent.\n return isSilentExpression(node.expression);\n default:\n return false;\n }\n}\n\n/** True when the handler's whole body provably does nothing with the error. */\nfunction isSilentHandler(\n handler: TSESTree.ArrowFunctionExpression | TSESTree.FunctionExpression,\n): boolean {\n const body = handler.body;\n\n if (body.type !== AST_NODE_TYPES.BlockStatement) {\n // Arrow expression body: `.catch(() => null)`\n return isSilentExpression(body);\n }\n\n if (body.body.length === 0) {\n // `.catch(() => {})` / `.catch(function () {})`\n return true;\n }\n\n if (body.body.length === 1) {\n const only = body.body[0];\n if (only !== undefined && only.type === AST_NODE_TYPES.ReturnStatement) {\n // `.catch(() => { return null; })`\n return only.argument === null || isSilentExpression(only.argument);\n }\n }\n\n return false;\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-silent-promise-catch\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Disallow `.catch()` handlers that silently swallow the rejection (e.g. `.catch(() => null)`); log, rethrow, or handle the error.\",\n },\n schema: [],\n messages: {\n silentCatch:\n \"This `.catch()` swallows the rejection without logging, rethrowing, or handling it — failures become invisible and callers get an indistinguishable sentinel. Log the error (and only then map to a fallback), or let it propagate.\",\n },\n },\n defaultOptions: [],\n create(context) {\n if (isTestFile(context.filename)) {\n return {};\n }\n\n /**\n * True when the suppression is DOCUMENTED: a comment inside the handler\n * body, trailing the call on the same line, or on the line(s) directly above\n * the statement the call belongs to. The rule's complaint is that the\n * decision to discard the error is invisible — a comment is precisely what\n * makes it visible, so there is nothing left to report.\n */\n const hasExplanatoryComment = (\n call: TSESTree.CallExpression,\n handler: TSESTree.ArrowFunctionExpression | TSESTree.FunctionExpression,\n ): boolean => {\n const sourceCode = context.sourceCode;\n if (sourceCode.getCommentsInside(handler).some(isExplanatory)) {\n return true;\n }\n // Walk out to the enclosing statement so a comment above or beside\n // `lazyRoutePromise.catch(() => {});` is found rather than one sitting\n // between the receiver and `.catch`.\n let statement: TSESTree.Node = call;\n while (\n statement.parent !== undefined &&\n statement.parent !== null &&\n !statement.type.endsWith(\"Statement\") &&\n statement.type !== AST_NODE_TYPES.VariableDeclaration\n ) {\n statement = statement.parent;\n }\n if (sourceCode.getCommentsBefore(statement).some(isExplanatory)) {\n return true;\n }\n return sourceCode\n .getCommentsAfter(statement)\n .some(\n (c) => isExplanatory(c) && c.loc.start.line === statement.loc.end.line,\n );\n };\n\n return {\n CallExpression(node: TSESTree.CallExpression): void {\n if (\n node.callee.type !== AST_NODE_TYPES.MemberExpression ||\n node.callee.computed ||\n node.callee.property.type !== AST_NODE_TYPES.Identifier ||\n node.callee.property.name !== \"catch\"\n ) {\n return;\n }\n\n if (isBodyParseCall(node.callee.object)) {\n return;\n }\n\n if (isTeardownCall(node.callee.object)) {\n return;\n }\n\n // `p.catch(() => null).then(...)` — the next link consumes the fallback,\n // so it is a recovery step, not a value handed back to an outside caller.\n if (\n node.parent.type === AST_NODE_TYPES.MemberExpression &&\n node.parent.object === node\n ) {\n return;\n }\n\n if (node.arguments.length !== 1) {\n return;\n }\n const handler = node.arguments[0];\n if (\n handler === undefined ||\n (handler.type !== AST_NODE_TYPES.ArrowFunctionExpression &&\n handler.type !== AST_NODE_TYPES.FunctionExpression)\n ) {\n return;\n }\n\n if (hasExplanatoryComment(node, handler)) {\n return;\n }\n\n if (isSilentHandler(handler)) {\n // Anchor on the CallExpression, not the handler: when the handler\n // sits on a later line than the call (multi-line `.catch(`), a\n // handler-anchored report escapes an `eslint-disable-next-line`\n // above the call AND marks that directive as unused.\n context.report({ node, messageId: \"silentCatch\" });\n }\n },\n };\n },\n});\n","/**\n * @fileoverview Require an abort `signal` on global `fetch()` calls. A fetch\n * without a timeout hangs forever when the upstream stalls, tying up the\n * request (or worker) with it. Pass `AbortSignal.timeout(ms)` — or a signal\n * from an `AbortController` — in the init object.\n *\n * Scope is deliberately narrow to keep false positives near zero:\n * - Only calls that resolve to the *global* `fetch` are checked: bare\n * `fetch(...)` (unless a local binding — import, parameter, variable —\n * shadows it) plus the explicit global spellings `globalThis.fetch(...)`,\n * `window.fetch(...)`, and `self.fetch(...)`. Other member calls like\n * Cloudflare Workers service bindings (`env.MY_SERVICE.fetch(...)`) or\n * custom clients (`client.fetch(...)`) are skipped.\n * - A call is flagged only when the init argument is absent, or is an\n * object literal that provably lacks `signal` (no spread, no `signal`\n * key). Anything dynamic (an identifier, a call result, a spread) is\n * assumed to carry a signal.\n * - Single-argument calls whose argument is not a string/template literal\n * are skipped: `fetch(request)` / `fetch(c.req.raw.clone())` are proxy\n * passthroughs (Workers idiom) where the inbound Request governs the\n * lifetime and attaching a fresh signal is impossible or wrong.\n * - Test files and one-off tooling (`scripts/**`, `*.mjs`) are skipped —\n * dev scripts die with the terminal, so hang-hardening is noise there.\n * - Codemod fixtures under `__testfixtures__/` are skipped. Corpus sweep\n * (2220 files across zod / TanStack Query / react-router / swr / zustand,\n * 2026-07): 8 of 79 hits came from a single jscodeshift input/output pair,\n * `query/packages/query-codemods/src/v5/remove-overloads/__testfixtures__/bug-reports.input.tsx`\n * and its `.output.tsx` twin. Those files are the BEFORE and AFTER text a\n * codemod test diffs; editing them to add a signal would break the test they\n * exist to drive, and the code never runs. The shared `isTestFile` predicate\n * knows `fixtures/` but not jscodeshift's `__testfixtures__/` spelling.\n *\n * Wrapper modules that centralize timeout handling can be exempted via the\n * `allowIn` glob-pattern option.\n */\n\nimport {\n AST_NODE_TYPES,\n ASTUtils,\n ESLintUtils,\n type TSESTree,\n} from \"@typescript-eslint/utils\";\n\nimport { isScriptFile, isTestFile } from \"./_paths.js\";\n\n/** jscodeshift's fixture directory — input/output text, not code that runs. */\nconst CODEMOD_FIXTURE_RE = /[\\\\/]__testfixtures__[\\\\/]/;\n\ntype MessageIds = \"missingSignal\";\ntype Options = readonly [\n {\n allowIn?: readonly string[];\n }?,\n];\n\n/** The explicit-global receivers of `<obj>.fetch(...)`. */\nconst GLOBAL_OBJECTS: ReadonlySet<string> = new Set([\n \"globalThis\",\n \"window\",\n \"self\",\n]);\n\nfunction matchesAnyPattern(\n filename: string,\n patterns: readonly string[],\n): boolean {\n for (const pattern of patterns) {\n // Convert minimatch-ish globs to regex: ** -> .*, * -> [^/\\\\]*\n const regexSource = pattern\n .replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\")\n .replace(/\\*\\*/g, \"::DOUBLESTAR::\")\n .replace(/\\*/g, \"[^/\\\\\\\\]*\")\n .replace(/::DOUBLESTAR::/g, \".*\");\n if (new RegExp(`^${regexSource}$`).test(filename)) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * True when the init argument provably lacks an abort signal: it is an object\n * literal with no `signal` property and no spread element. Any other shape\n * (identifier, call, spread, conditional, ...) may carry a signal, so it is\n * treated as safe.\n */\nfunction initProvablyLacksSignal(init: TSESTree.CallExpressionArgument): boolean {\n if (init.type !== AST_NODE_TYPES.ObjectExpression) {\n return false;\n }\n for (const prop of init.properties) {\n if (prop.type === AST_NODE_TYPES.SpreadElement) {\n return false;\n }\n if (\n (prop.key.type === AST_NODE_TYPES.Identifier &&\n prop.key.name === \"signal\") ||\n (prop.key.type === AST_NODE_TYPES.Literal && prop.key.value === \"signal\")\n ) {\n return false;\n }\n // A computed key could be \"signal\" at runtime; assume it is.\n if (prop.computed) {\n return false;\n }\n }\n return true;\n}\n\n/** True for a string or template literal — a URL spelled inline. */\nfunction isStringish(node: TSESTree.CallExpressionArgument): boolean {\n return (\n (node.type === AST_NODE_TYPES.Literal && typeof node.value === \"string\") ||\n node.type === AST_NODE_TYPES.TemplateLiteral\n );\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"require-fetch-timeout\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Require an abort `signal` (e.g. `AbortSignal.timeout(ms)`) on global `fetch()` calls so stalled upstreams cannot hang the caller forever.\",\n },\n schema: [\n {\n type: \"object\",\n additionalProperties: false,\n properties: {\n allowIn: {\n description:\n \"Glob patterns for wrapper modules exempt from the rule. Matched against the ABSOLUTE file path, so anchor with a `**/` prefix (e.g. `**/http-client.ts`).\",\n type: \"array\",\n items: { type: \"string\" },\n },\n },\n },\n ],\n messages: {\n missingSignal:\n \"This `fetch()` has no abort `signal` — a stalled upstream will hang it forever. Pass `{ signal: AbortSignal.timeout(ms) }` or a signal from an AbortController.\",\n },\n },\n defaultOptions: [{}],\n create(context, [optionsArg]) {\n if (\n isTestFile(context.filename) ||\n isScriptFile(context.filename) ||\n CODEMOD_FIXTURE_RE.test(context.filename)\n ) {\n return {};\n }\n const allowIn = optionsArg?.allowIn ?? [];\n if (allowIn.length > 0 && matchesAnyPattern(context.filename, allowIn)) {\n return {};\n }\n\n /** True when `identifier` resolves to the global (no local binding shadows it). */\n function resolvesToGlobal(identifier: TSESTree.Identifier): boolean {\n const scope = context.sourceCode.getScope(identifier);\n const variable = ASTUtils.findVariable(scope, identifier.name);\n // Unresolved → implicit global. Resolved with zero defs → declared\n // global (languageOptions.globals). Any def is a local shadow.\n return variable === null || variable.defs.length === 0;\n }\n\n /** True for `fetch(...)` / `globalThis.fetch(...)` / `window.fetch(...)` /\n * `self.fetch(...)` where the receiver resolves to the global scope. */\n function isGlobalFetchCall(callee: TSESTree.Expression): boolean {\n if (callee.type === AST_NODE_TYPES.Identifier) {\n return callee.name === \"fetch\" && resolvesToGlobal(callee);\n }\n return (\n callee.type === AST_NODE_TYPES.MemberExpression &&\n !callee.computed &&\n callee.property.type === AST_NODE_TYPES.Identifier &&\n callee.property.name === \"fetch\" &&\n callee.object.type === AST_NODE_TYPES.Identifier &&\n GLOBAL_OBJECTS.has(callee.object.name) &&\n resolvesToGlobal(callee.object)\n );\n }\n\n return {\n CallExpression(node: TSESTree.CallExpression): void {\n if (!isGlobalFetchCall(node.callee)) {\n return;\n }\n\n // Proxy passthrough: a lone non-string argument is a Request (or\n // equivalent) being forwarded — the inbound request owns the\n // lifetime, and a fresh signal cannot be attached without an init.\n const [first, init] = node.arguments;\n if (node.arguments.length === 1 && first !== undefined && !isStringish(first)) {\n return;\n }\n\n if (init === undefined || initProvablyLacksSignal(init)) {\n context.report({ node, messageId: \"missingSignal\" });\n }\n },\n };\n },\n});\n","/**\n * @fileoverview Flag TanStack Router `validateSearch` implementations that\n * hand-roll \"validation\" with `as` casts. A cast asserts a shape without\n * checking it, so malformed query params flow into the app typed as clean\n * data. Use a schema validator (e.g. `zodValidator(searchSchema)` from\n * `@tanstack/zod-adapter`, or `searchSchema.parse`) so the search params are\n * actually validated at runtime.\n *\n * Deliberately narrow: only fires on a property literally named\n * `validateSearch` whose value is a function containing at least one cast —\n * `as` form or angle-bracket `<T>x` form (`as const` exempt: it narrows\n * rather than lies). Two exemptions:\n * - Casts that feed a validator are fine: an argument (transitively) of a\n * call whose callee property is `parse` / `safeParse` / `decode` — e.g.\n * the rule's own recommended remedy\n * `validateSearch: (s) => schema.parse(s as Record<string, unknown>)` —\n * is validated at runtime regardless of the cast.\n * - Test files, where route stubs are fixtures rather than real routes.\n */\n\nimport { AST_NODE_TYPES, ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\nimport { isTestFile } from \"./_paths.js\";\n\ntype MessageIds = \"castInValidateSearch\";\ntype Options = readonly [];\n\n/** Callee property names whose calls validate their input at runtime. */\nconst VALIDATOR_METHODS: ReadonlySet<string> = new Set([\n \"parse\",\n \"safeParse\",\n \"decode\",\n]);\n\nfunction isConstTypeAnnotation(typeAnnotation: TSESTree.TypeNode): boolean {\n return (\n typeAnnotation.type === AST_NODE_TYPES.TSTypeReference &&\n typeAnnotation.typeName.type === AST_NODE_TYPES.Identifier &&\n typeAnnotation.typeName.name === \"const\"\n );\n}\n\n/** True for a call whose callee property is a runtime validator (`schema.parse(...)`). */\nfunction isValidatorCall(node: TSESTree.CallExpression): boolean {\n return (\n node.callee.type === AST_NODE_TYPES.MemberExpression &&\n !node.callee.computed &&\n node.callee.property.type === AST_NODE_TYPES.Identifier &&\n VALIDATOR_METHODS.has(node.callee.property.name)\n );\n}\n\n/**\n * Depth-first search for the first offending cast under `node`: a non-`const`\n * TSAsExpression or TSTypeAssertion that is not (transitively) an argument of\n * a validator call. `insideValidatorArg` is threaded through the descent so a\n * cast feeding `schema.parse(...)` is exempt however deeply it nests.\n */\nfunction findCastExpression(\n node: TSESTree.Node,\n insideValidatorArg: boolean,\n): TSESTree.TSAsExpression | TSESTree.TSTypeAssertion | null {\n if (\n (node.type === AST_NODE_TYPES.TSAsExpression ||\n node.type === AST_NODE_TYPES.TSTypeAssertion) &&\n !isConstTypeAnnotation(node.typeAnnotation) &&\n !insideValidatorArg\n ) {\n return node;\n }\n\n if (\n node.type === AST_NODE_TYPES.CallExpression &&\n isValidatorCall(node)\n ) {\n // The callee itself is still in scope; only the arguments are validated.\n const inCallee = findCastExpression(node.callee, insideValidatorArg);\n if (inCallee !== null) {\n return inCallee;\n }\n for (const arg of node.arguments) {\n const found = findCastExpression(arg, true);\n if (found !== null) {\n return found;\n }\n }\n return null;\n }\n\n for (const key of Object.keys(node)) {\n if (key === \"parent\") {\n continue;\n }\n const value = (node as unknown as Record<string, unknown>)[key];\n const children = Array.isArray(value) ? value : [value];\n for (const child of children) {\n if (\n child !== null &&\n typeof child === \"object\" &&\n \"type\" in child &&\n typeof (child as { type: unknown }).type === \"string\"\n ) {\n const found = findCastExpression(\n child as TSESTree.Node,\n insideValidatorArg,\n );\n if (found !== null) {\n return found;\n }\n }\n }\n }\n return null;\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"require-schema-validate-search\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Disallow `as` casts inside hand-rolled `validateSearch` functions; use a schema validator (e.g. zodValidator) so search params are validated at runtime.\",\n },\n schema: [],\n messages: {\n castInValidateSearch:\n \"This `validateSearch` asserts the search-param shape with `as` instead of validating it — malformed query params flow through typed as clean data. Use a schema validator (e.g. `zodValidator(searchSchema)` or `searchSchema.parse`) instead of casting.\",\n },\n },\n defaultOptions: [],\n create(context) {\n if (isTestFile(context.filename)) {\n return {};\n }\n\n return {\n Property(node: TSESTree.Property): void {\n const isValidateSearchKey =\n (!node.computed &&\n node.key.type === AST_NODE_TYPES.Identifier &&\n node.key.name === \"validateSearch\") ||\n (node.key.type === AST_NODE_TYPES.Literal &&\n node.key.value === \"validateSearch\");\n if (!isValidateSearchKey) {\n return;\n }\n\n if (\n node.value.type !== AST_NODE_TYPES.ArrowFunctionExpression &&\n node.value.type !== AST_NODE_TYPES.FunctionExpression\n ) {\n return;\n }\n\n const cast = findCastExpression(node.value.body, false);\n if (cast !== null) {\n context.report({ node: cast, messageId: \"castInValidateSearch\" });\n }\n },\n };\n },\n});\n","/**\n * @fileoverview Disallow `try` blocks whose body contains more than three\n * top-level statements that can throw (TS port of Python's SARJ007).\n *\n * A fat `try` body obscures which statement is actually expected to throw and\n * widens the blast radius of the `catch` handler: unrelated failures get caught\n * (and often swallowed or mis-reported) by a handler written for a different\n * operation. Keep the `try` skinny — isolate the throwing statement(s) and move\n * the non-throwing setup and follow-up work outside.\n *\n * Only top-level statements that can *throw* are counted. What counts, and the\n * guards that keep the count aligned with intent (tuned against ~5.6k real TS\n * files to drive false positives to ~zero):\n *\n * - An `await` always counts — awaiting a promise is the canonical throwing\n * operation in async TS. A statement with an `await` in its same-scope\n * subtree counts.\n * - A synchronous call / `new` whose value is *used* (assigned, returned,\n * branched on, or passed as an argument) counts — e.g. `const x = parse(s)`,\n * `return build(x)`, `if (!validate(x))`.\n * - A bare fire-and-forget call statement with no `await` does NOT count. In\n * idiomatic TS these are side effects — React state setters (`setOpen(false)`),\n * toasts (`toast.error(...)`), `router.refresh()`, logging, optional\n * callbacks (`onSuccess?.()`). They are the post-success UI work that\n * naturally trails the one awaited action; counting them flagged nearly\n * every event handler. The exemption is applied STRUCTURALLY, recursing\n * through blocks and `if`/`else` branches, so wrapping the same call in a\n * guard (`if (!res.ok) { logEvent(\"http_error\", { path }); return null; }`)\n * does not resurrect it. A guard's TEST is still examined — a call whose\n * result is branched on (`if (!validate(x))`) still counts.\n * - Pure, non-throwing array / string / `Map` / `Object` / `Math` / `JSON`\n * helpers (`.map`, `.filter`, `.push`, `.get`, `.join`, `Object.keys`, ...)\n * do NOT count — they are data plumbing, not the operation being guarded.\n * - Calls inside a nested function / arrow body do not run when the `try`\n * executes, so they are not counted (same-scope walk).\n *\n * Two structural exemptions match the Python rule:\n *\n * - A `finally` clause is a deliberate cleanup contract that couples the body\n * to the handler — exempt.\n * - A `catch` handler guaranteed to re-throw (its body's last statement is a\n * `throw`) makes the wide body uniform error-context wrapping, not an\n * over-broad swallow — exempt.\n */\n\nimport {\n ESLintUtils,\n type TSESTree,\n AST_NODE_TYPES,\n} from \"@typescript-eslint/utils\";\n\nimport { isGeneratedFile } from \"./_paths.js\";\n\ntype MessageIds = \"fatTryBlock\";\ntype Options = readonly [];\n\nconst MAX_TRY_BODY_STATEMENTS = 3;\n\nconst NESTED_FUNCTION_TYPES = new Set<AST_NODE_TYPES>([\n AST_NODE_TYPES.FunctionDeclaration,\n AST_NODE_TYPES.FunctionExpression,\n AST_NODE_TYPES.ArrowFunctionExpression,\n]);\n\n/** Non-throwing member methods — array / string / Map / Set data plumbing. */\nconst PURE_METHODS = new Set<string>([\n \"map\", \"filter\", \"forEach\", \"reduce\", \"reduceRight\", \"find\", \"findIndex\",\n \"findLast\", \"findLastIndex\", \"some\", \"every\", \"push\", \"pop\", \"shift\",\n \"unshift\", \"slice\", \"splice\", \"concat\", \"flat\", \"flatMap\", \"join\", \"reverse\",\n \"sort\", \"fill\", \"includes\", \"indexOf\", \"lastIndexOf\", \"at\", \"keys\", \"values\",\n \"entries\", \"has\", \"get\", \"set\", \"add\", \"delete\", \"clear\", \"toString\",\n \"toLocaleString\", \"valueOf\", \"charAt\", \"charCodeAt\", \"codePointAt\", \"split\",\n \"padStart\", \"padEnd\", \"repeat\", \"trim\", \"trimStart\", \"trimEnd\", \"toUpperCase\",\n \"toLowerCase\", \"toFixed\", \"toPrecision\", \"startsWith\", \"endsWith\",\n]);\n\n/** Non-throwing global namespaces called as `X.method(...)`. */\nconst PURE_NAMESPACES = new Set<string>([\n \"Object\", \"Array\", \"Math\", \"JSON\", \"Number\", \"String\", \"Boolean\", \"console\",\n]);\n\n/** Constructors that do not throw on construction. */\nconst PURE_CONSTRUCTORS = new Set<string>([\n \"Map\", \"Set\", \"WeakMap\", \"WeakSet\", \"Date\", \"Error\", \"TypeError\",\n \"RangeError\", \"Array\", \"Object\", \"Headers\", \"URLSearchParams\", \"FormData\",\n \"TextEncoder\", \"TextDecoder\", \"Blob\", \"ReadableStream\", \"WritableStream\",\n \"TransformStream\", \"Response\", \"AbortController\",\n]);\n\nfunction isNode(value: unknown): value is TSESTree.Node {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as { type?: unknown }).type === \"string\"\n );\n}\n\n/** A call whose value is a known pure, non-throwing helper. */\nfunction isPureCall(node: TSESTree.CallExpression): boolean {\n const callee = node.callee;\n if (callee.type !== AST_NODE_TYPES.MemberExpression) {\n return false;\n }\n const property = callee.property;\n if (property.type !== AST_NODE_TYPES.Identifier) {\n return false;\n }\n if (\n callee.object.type === AST_NODE_TYPES.Identifier &&\n PURE_NAMESPACES.has(callee.object.name)\n ) {\n return true;\n }\n return PURE_METHODS.has(property.name);\n}\n\nfunction isPureNew(node: TSESTree.NewExpression): boolean {\n return (\n node.callee.type === AST_NODE_TYPES.Identifier &&\n PURE_CONSTRUCTORS.has(node.callee.name)\n );\n}\n\n/**\n * Walk `stmt`'s same-scope subtree (not descending into nested function/arrow\n * bodies) until `predicate` matches a node.\n */\nfunction subtreeMatches(\n stmt: TSESTree.Node,\n predicate: (node: TSESTree.Node) => boolean,\n): boolean {\n let found = false;\n\n const visit = (current: TSESTree.Node): void => {\n if (found) {\n return;\n }\n if (predicate(current)) {\n found = true;\n return;\n }\n for (const key of Object.keys(current)) {\n if (key === \"parent\") {\n continue;\n }\n if (NESTED_FUNCTION_TYPES.has(current.type) && key === \"body\") {\n continue;\n }\n const value = (current as unknown as Record<string, unknown>)[key];\n if (Array.isArray(value)) {\n for (const child of value) {\n if (isNode(child)) {\n visit(child);\n }\n }\n } else if (isNode(value)) {\n visit(value);\n }\n if (found) {\n return;\n }\n }\n };\n\n visit(stmt);\n return found;\n}\n\nconst hasAwait = (node: TSESTree.Node): boolean =>\n subtreeMatches(node, (n) => n.type === AST_NODE_TYPES.AwaitExpression);\n\nconst hasThrowingCallOrNew = (node: TSESTree.Node): boolean =>\n subtreeMatches(\n node,\n (n) =>\n (n.type === AST_NODE_TYPES.CallExpression && !isPureCall(n)) ||\n (n.type === AST_NODE_TYPES.NewExpression && !isPureNew(n)),\n );\n\n/** Unwrap `await` / optional-chain / non-null wrappers to the core expression. */\nfunction unwrap(expr: TSESTree.Expression): TSESTree.Expression {\n let current = expr;\n while (\n current.type === AST_NODE_TYPES.ChainExpression ||\n current.type === AST_NODE_TYPES.TSNonNullExpression\n ) {\n current = current.expression;\n }\n return current;\n}\n\n/** A bare fire-and-forget call statement — `toast(\"done\");`, `logEvent(...);`. */\nfunction isBareCallStatement(stmt: TSESTree.Statement): boolean {\n return (\n stmt.type === AST_NODE_TYPES.ExpressionStatement &&\n unwrap(stmt.expression).type === AST_NODE_TYPES.CallExpression\n );\n}\n\n/**\n * Whether a top-level try-body statement can plausibly throw when the `try`\n * runs. See the file overview for the guards; the key ones are: `await` always\n * counts, and a bare fire-and-forget call statement (no `await`) does not.\n *\n * Blocks and `if`/`else` branches recurse so the bare-call exemption survives a\n * guard — a fire-and-forget call is no more throwing for sitting inside an\n * `if`. The guard's own TEST is still checked for a throwing call, keeping\n * `if (!validate(x))` counted. A compound statement still contributes at most\n * ONE to the caller's count; this predicate only decides whether it counts.\n */\nfunction canThrow(stmt: TSESTree.Statement): boolean {\n if (hasAwait(stmt)) {\n return true;\n }\n if (isBareCallStatement(stmt)) {\n return false;\n }\n if (stmt.type === AST_NODE_TYPES.BlockStatement) {\n return stmt.body.some(canThrow);\n }\n if (stmt.type === AST_NODE_TYPES.IfStatement) {\n return (\n hasThrowingCallOrNew(stmt.test) ||\n canThrow(stmt.consequent) ||\n (stmt.alternate !== null && canThrow(stmt.alternate))\n );\n }\n return hasThrowingCallOrNew(stmt);\n}\n\n/**\n * Conservative: is the `catch` handler guaranteed to re-throw? True when a\n * handler is present and its body's last statement is a `throw`.\n */\nfunction handlerRethrows(handler: TSESTree.CatchClause | null): boolean {\n if (handler === null) {\n return false;\n }\n const body = handler.body.body;\n const last = body[body.length - 1];\n return last !== undefined && last.type === AST_NODE_TYPES.ThrowStatement;\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-fat-try-blocks\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Disallow `try` blocks with more than three top-level statements that can throw — isolate the throwing statement and move non-throwing work outside.\",\n },\n schema: [],\n messages: {\n fatTryBlock:\n \"This `try` block has {{count}} statements that can throw (max {{max}}). Isolate the throwing statement(s); move non-throwing work outside the `try`.\",\n },\n },\n defaultOptions: [],\n create(context) {\n if (isGeneratedFile(context.filename, context.sourceCode.text)) {\n return {};\n }\n\n const sourceCode = context.sourceCode;\n\n return {\n TryStatement(node: TSESTree.TryStatement): void {\n if (node.finalizer !== null) {\n return;\n }\n if (handlerRethrows(node.handler)) {\n return;\n }\n\n const count = node.block.body.filter(canThrow).length;\n if (count <= MAX_TRY_BODY_STATEMENTS) {\n return;\n }\n\n const tryKeyword = sourceCode.getFirstToken(node);\n context.report({\n node: tryKeyword ?? node,\n messageId: \"fatTryBlock\",\n data: { count, max: MAX_TRY_BODY_STATEMENTS },\n });\n },\n };\n },\n});\n","/**\n * @fileoverview TS port of SARJ012 (`no-secret-in-log`). Passing a secret value\n * (token, password, api key, jwt, credential, signature, ...) to a logging call\n * leaks it into log sinks — files, stdout, log aggregators — where it persists\n * far beyond its intended lifetime and is readable by anyone with log access.\n * Prefer redaction (`tokenPrefix: token.slice(0, 6)`) or omission.\n *\n * We fire on a logging call (`logger.info(...)`, `log.error(...)`, loguru/bind\n * builder chains, etc.) that passes a secret-named value either as a property of\n * an object argument (`logger.error(\"msg\", { token, apiKey })`) or as a bare\n * secret-named positional identifier (`logger.info(\"x\", password)`).\n *\n * Log recognition is shared with the catch rules via `_logging`, so the\n * `logFunctions` option applies here too. That matters: a structured logger is\n * usually a free function with no logger receiver, and `logEvent(\"slack.auth\",\n * { botToken })` was previously never even examined by this rule. Declaring the\n * project's logger closes that hole.\n *\n * The secret-name predicate matches a secret word only as a WHOLE token (after\n * snake_case / camelCase splitting) and disqualifies identifiers whose trailing\n * token is a counter / row-id / flag marker (`tokenCount`, `apiKeyId`,\n * `passwordEnabled`) or whose LEADING word is a boolean predicate (`hasSecret`,\n * `is_token`), so metadata *about* a secret is not mistaken for the secret\n * itself. Both guards live in the shared `_secret_names` predicate. Redaction\n * markers (prefix/mask/hash/redact/tag) are exempt on top of that.\n *\n * ## The raw-blob arm (`noRawBodyInLog`)\n *\n * A second, separately-messaged arm covers what the name-based arm structurally\n * cannot: a whole request/response **blob** — `logEvent(\"ashby.response\",\n * { status, body })`, `console.log(res.body)`. No property of that object is\n * secret-*named*; the object itself is the leak. Bodies are candidate PII and\n * routinely echo credentials back (an auth response containing the token it just\n * minted, a webhook payload carrying its own signing header), and a log sink has\n * no retention policy for either. The advice differs from the secret arm's —\n * \"redact or omit the blob\", not \"this named field is a credential\" — so it gets\n * its own messageId.\n *\n * This arm is deliberately name-driven and narrow. **Only these words, matched as\n * the identifier's TRAILING camel/snake word, count as a raw blob**: `body`,\n * `bodies`, `payload`, `payloads`, `params` (so `rawBody`, `requestBody`,\n * `responsePayload`, `webhookPayload`, `searchParams` all qualify), plus the\n * single whole identifier `formData` (whose camel split ends in the far too\n * generic `data`). Generic containers — `data`, `input`, `args`, `event`,\n * `result`, `record`, `req`, `res` — are NOT blob words: they name everything, so\n * firing on them would make the rule noise and get it switched off.\n *\n * A leak is reported only when the logged VALUE is the blob verbatim: a shorthand\n * property (`{ body }`), a bare identifier (`{ meta: body }`, `logEvent(\"x\",\n * payload)`), or a non-computed member access whose PROPERTY is blob-named\n * (`{ body: res.body }`, `console.log(res.body)`). Judging the value rather than\n * the key is what keeps `{ payload: body.id }` silent.\n *\n * Deliberately NOT reported:\n * - **A narrowed field** — `{ id: body.id }`, `{ bodyLength: body.length }`. The\n * member property, not the object, decides; picking a field is the fix.\n * - **Anything passed through a call** — `redact(body)`, `sanitize(payload)`,\n * `pick(body, [\"id\"])`, `JSON.stringify(body).slice(0, 200)`,\n * `summarizeIssues(body)`. Summarising is the behaviour we want, and no name\n * list can enumerate every project's summariser, so the *shape* is the exemption.\n * - **A string literal or template** — `{ body: \"ok\" }`, `` { body: `n=${n}` } ``.\n * Already a rendered, author-chosen string.\n * - **Redaction / derivation markers in the name** — `redactedBody`,\n * `sanitizedPayload`, `truncatedBody`, `bodyHash`, `bodyPreview`, `safeBody`.\n * - **Boolean flags** — `hasBody`, `isPayload`: a leading predicate word.\n * - **Object spread** — `{ ...body }`. Real, but no observed instances; left out\n * rather than shipped unmeasured.\n * - **Test files** (`_paths.isTestFile`) — a body there is a fixture the author\n * wrote, not production PII. The secret arm is deliberately NOT exempted this\n * way: its behaviour is unchanged by this port.\n *\n * Both arms sit behind the same `_logging` gate, so `logFunctions` /\n * `loggerNames` govern the blob arm identically — `logEvent(\"x\", { body })` is\n * invisible until the project declares `logEvent`, exactly like `{ botToken }`.\n *\n * References:\n * - https://owasp.org/www-community/vulnerabilities/Information_exposure_through_log_files\n */\n\nimport { ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\nimport {\n createLogMatcher,\n LOGGING_OPTION_PROPERTIES,\n type LoggingOptions,\n} from \"./_logging.js\";\nimport { isTestFile } from \"./_paths.js\";\nimport {\n FLAG_PREFIXES,\n INNOCUOUS_WORDS,\n isSecretName,\n leadingWord,\n tokenize,\n} from \"./_secret_names.js\";\n\ntype MessageIds = \"noSecretInLog\" | \"noRawBodyInLog\";\ntype Options = readonly [LoggingOptions?];\n\n/**\n * The shared metadata set plus the log-specific extras. Logging a secret's\n * label, expiry, ARN, URL, scope, or the DI component that holds it is metadata\n * ABOUT the credential, never the credential bytes, so it must not fire here —\n * `prefer-constant-time-secret-compare` uses the narrower shared set.\n */\nconst LOG_INNOCUOUS_WORDS: ReadonlySet<string> = new Set([\n ...INNOCUOUS_WORDS,\n \"name\",\n \"names\",\n \"label\",\n \"labels\",\n \"title\",\n \"expiry\",\n \"expiration\",\n \"expires\",\n \"ttl\",\n \"version\",\n \"versions\",\n \"policy\",\n \"rotation\",\n \"arn\",\n \"path\",\n \"paths\",\n \"issuer\",\n \"audience\",\n \"strength\",\n \"manager\",\n \"service\",\n \"services\",\n \"repository\",\n \"provider\",\n \"providers\",\n \"store\",\n \"factory\",\n \"handler\",\n \"controller\",\n \"bucket\",\n \"url\",\n \"uri\",\n \"endpoint\",\n \"endpoints\",\n \"scope\",\n \"scopes\",\n \"event\",\n \"events\",\n \"format\",\n \"at\",\n \"len\",\n \"length\",\n]);\n\nconst REDACTION_RE = /prefix|suffix|redact|mask|hash|hint|_len|length/i;\nconst WHOLE_TOKEN_REDACTION_MARKERS: ReadonlySet<string> = new Set([\"tag\"]);\n\n/** True if the name names a raw secret and is not a redacted derivative. */\nfunction isSecretKeyword(name: string): boolean {\n if (REDACTION_RE.test(name)) {\n return false;\n }\n if (tokenize(name).some((tok) => WHOLE_TOKEN_REDACTION_MARKERS.has(tok))) {\n return false;\n }\n return isSecretName(name, LOG_INNOCUOUS_WORDS);\n}\n\n/**\n * True if `prop`'s value is the raw secret rather than a redacted/derived form.\n * Shorthand (`{ token }`), a bare identifier (`{ apiKey: theKey }`), or a plain\n * member access (`{ apiKey: config.apiKey }`) all carry the secret verbatim. A\n * call (`token.slice(0, 6)`, `mask(token)`), template literal, ternary, concat,\n * or literal placeholder (`\"***\"`) is already redacted — logging it is safe.\n */\nfunction isRawSecretValue(prop: TSESTree.Property): boolean {\n if (prop.shorthand) {\n return true;\n }\n return prop.value.type === \"Identifier\" || prop.value.type === \"MemberExpression\";\n}\n\n/**\n * Trailing camel/snake words that name an un-redacted request/response blob. Kept\n * to an enumerated five: these are the containers whose *whole point* is \"the\n * bytes the client sent / the server returned\". Generic container words (`data`,\n * `input`, `args`, `event`, `result`, `payloadless` domain objects) are excluded\n * on purpose — see the `@fileoverview`.\n */\nconst RAW_BLOB_WORDS: ReadonlySet<string> = new Set([\n \"body\",\n \"bodies\",\n \"payload\",\n \"payloads\",\n \"params\",\n]);\n\n/**\n * Whole identifiers whose camelCase split ends in a word too generic to enumerate\n * (`formData` -> `data`) but which name a blob unambiguously on their own.\n */\nconst RAW_BLOB_IDENTIFIERS: ReadonlySet<string> = new Set([\"formdata\"]);\n\n/**\n * Substrings that mark a blob name as an already-derived, safe-to-log form:\n * `redactedBody`, `sanitizedPayload`, `truncatedBody`, `bodyPreview`.\n */\nconst BLOB_REDACTION_RE = /redact|sanit|scrub|mask|truncat|anonym|filtered|preview|summar/i;\n\n/**\n * Derivation markers that are only safe when matched as a WHOLE token — `safe` as\n * a substring would wrongly exempt `unsafeBody`.\n */\nconst BLOB_REDACTION_TOKENS: ReadonlySet<string> = new Set([\n \"safe\",\n \"clean\",\n \"shape\",\n \"keys\",\n \"public\",\n]);\n\n/** True if the name names a raw request/response blob and is not a derived form. */\nfunction isRawBlobName(name: string): boolean {\n if (REDACTION_RE.test(name) || BLOB_REDACTION_RE.test(name)) {\n return false;\n }\n const tokens = tokenize(name);\n if (tokens.some((tok) => BLOB_REDACTION_TOKENS.has(tok))) {\n return false;\n }\n // Same leading boolean-predicate words the secret arm uses: `hasBody` answers\n // \"is there one?\", it is not the blob.\n const first = leadingWord(name);\n if (first !== undefined && FLAG_PREFIXES.has(first)) {\n return false;\n }\n if (RAW_BLOB_IDENTIFIERS.has(name.toLowerCase())) {\n return true;\n }\n const last = tokens.at(-1);\n return last !== undefined && RAW_BLOB_WORDS.has(last);\n}\n\n/**\n * The blob name a logged value carries VERBATIM, or null when it does not carry\n * one. A bare identifier is the whole object; a non-computed member access is\n * judged by its PROPERTY, so `res.body` is the blob but `body.id` is a picked\n * field. Every other shape — call, template, literal, ternary, spread — has\n * already been through the author's hands and is left alone.\n */\nfunction rawBlobValueName(value: TSESTree.Node): string | null {\n if (value.type === \"Identifier\") {\n return isRawBlobName(value.name) ? value.name : null;\n }\n if (\n value.type === \"MemberExpression\" &&\n !value.computed &&\n value.property.type === \"Identifier\"\n ) {\n return isRawBlobName(value.property.name) ? value.property.name : null;\n }\n return null;\n}\n\n/** The static string name of an object-property key, or null when not statically named. */\nfunction propertyKeyName(prop: TSESTree.Property): string | null {\n if (prop.computed) {\n return null;\n }\n if (prop.key.type === \"Identifier\") {\n return prop.key.name;\n }\n if (prop.key.type === \"Literal\" && typeof prop.key.value === \"string\") {\n return prop.key.value;\n }\n return null;\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-secret-in-log\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Disallow passing a secret-named value or a raw request/response blob to a logging call; both leak to log sinks. Redact or omit.\",\n },\n schema: [\n {\n type: \"object\",\n additionalProperties: false,\n properties: { ...LOGGING_OPTION_PROPERTIES },\n },\n ],\n messages: {\n noSecretInLog:\n \"Secret `{{name}}` passed to a logging call leaks it to log sinks. Redact (e.g. `{{name}}Prefix: {{name}}.slice(0, 6)`) or omit it.\",\n noRawBodyInLog:\n \"Raw `{{name}}` passed to a logging call. Request/response blobs carry PII and often echo credentials back, and log sinks have no retention policy. Log a derived value instead (a status, `{{name}}.id`, a length, a truncated issue list) or pass it through a redactor (`redact({{name}})`).\",\n },\n },\n defaultOptions: [{}],\n create(context, [loggingOptions]) {\n const matcher = createLogMatcher(loggingOptions);\n // Bodies in a test file are fixtures the author wrote, not production PII.\n const blobArmApplies = !isTestFile(context.filename);\n\n /** Reports a bare positional argument that IS the secret. Did it fire? */\n function reportSecretArgument(arg: TSESTree.Node): boolean {\n const name =\n arg.type === \"Identifier\"\n ? arg.name\n : arg.type === \"MemberExpression\" &&\n !arg.computed &&\n arg.property.type === \"Identifier\"\n ? arg.property.name\n : null;\n if (name === null || !isSecretKeyword(name)) {\n return false;\n }\n context.report({ node: arg, messageId: \"noSecretInLog\", data: { name } });\n return true;\n }\n\n /** Reports a meta-object property whose key names a secret it carries raw. Did it fire? */\n function reportSecretProperty(prop: TSESTree.Property): boolean {\n const keyName = propertyKeyName(prop);\n if (keyName === null || !isSecretKeyword(keyName) || !isRawSecretValue(prop)) {\n return false;\n }\n context.report({ node: prop, messageId: \"noSecretInLog\", data: { name: keyName } });\n return true;\n }\n\n /** Reports `node` when `value` carries an un-redacted request/response blob. */\n function reportRawBlob(node: TSESTree.Node, value: TSESTree.Node): void {\n if (!blobArmApplies) {\n return;\n }\n const name = rawBlobValueName(value);\n if (name !== null) {\n context.report({ node, messageId: \"noRawBodyInLog\", data: { name } });\n }\n }\n\n return {\n CallExpression(node: TSESTree.CallExpression): void {\n if (!matcher.isLoggingCall(node)) {\n return;\n }\n\n for (const arg of node.arguments) {\n if (arg.type === \"ObjectExpression\") {\n for (const prop of arg.properties) {\n if (prop.type !== \"Property\") {\n continue;\n }\n if (!reportSecretProperty(prop)) {\n reportRawBlob(prop, prop.value);\n }\n }\n continue;\n }\n if (!reportSecretArgument(arg)) {\n reportRawBlob(arg, arg);\n }\n }\n },\n };\n },\n});\n","/**\n * @fileoverview Shared predicate for deciding whether an identifier names secret\n * material. TS port of Python's `_secret_names.py`, used by `no-secret-in-log`\n * (SARJ012) and `prefer-constant-time-secret-compare` (SARJ011) so the two rules\n * never diverge on what counts as a secret.\n *\n * A naive implementation matches a secret word as a bare *substring*, which\n * misfires on a large false-positive class observed in real audits:\n *\n * - LLM usage counters that merely embed `token`: `tokenCount`, `promptTokens`,\n * `completionTokens`, `totalTokens`, `maxTokens`, `tokenize`, `tokenizer`,\n * `tokenBudget`.\n * - Row-id / handle names: `apiKeyId`, `*KeyId` — the id of a key row, not the\n * key material.\n * - Boolean feature / presence / state flags: `passwordEnabled`, `tokenPresent`,\n * `passwordSet`, `passwordConfigured` — a boolean answering \"is it there / was\n * it set\", not the credential itself. A `type` discriminator is the same:\n * `tokenType` is `\"Bearer\"`, `credentialType` is a class name.\n * - Innocent words embedding a secret word: `secretary` (embeds `secret`),\n * `keyboardEvent` (embeds `key`).\n *\n * Two rules fix this:\n *\n * 1. Match a secret word only as a WHOLE token (after snake_case / camelCase\n * splitting), never a substring. This alone clears `tokenize`, `tokenizer`,\n * `secretary`, and every *pluralized* `tokens` counter (plural `tokens` is not\n * the singular secret word `token`).\n * 2. Disqualify an identifier whose TRAILING token is a counter / row-id / flag\n * marker (`count`, `budget`, `id`, `enabled`, ...) even when a secret word is\n * also present — this clears `tokenCount`, `apiKeyId`, `passwordEnabled`,\n * while still catching a credential that merely leads with such a word\n * (`validToken`, `presentToken` are secrets, not flags).\n *\n * `isAuthSecretName` narrows further for SARJ011: a *timing-attack* surface is\n * only an authenticator whose bytes gate access, so category discriminators,\n * boolean flags, and integrity-only content hashes are stripped there while\n * `no-secret-in-log` keeps its broader reach.\n */\n\nexport const SECRET_WORDS: ReadonlySet<string> = new Set([\n \"token\",\n \"secret\",\n \"password\",\n \"passwd\",\n \"jwt\",\n \"secrets\",\n \"passwords\",\n \"credential\",\n \"credentials\",\n \"authorization\",\n \"signature\",\n \"hmac\",\n \"digest\",\n \"hash\",\n \"apikey\",\n \"bearer\",\n]);\n\n/**\n * Tokens that mark a counter, row-id, feature flag, or boolean presence/state\n * marker. As the TRAILING token they mean the identifier is metadata *about* a\n * secret, not the secret itself, so it is not a leak / timing surface even when a\n * secret word is also present: `tokenPresent`, `passwordSet`, and\n * `passwordConfigured` are booleans, not credentials. Leading such a word does\n * not disqualify — `validToken` / `presentToken` are credentials.\n */\nexport const INNOCUOUS_WORDS: ReadonlySet<string> = new Set([\n \"count\",\n \"counts\",\n \"budget\",\n \"limit\",\n \"limits\",\n \"id\",\n \"ids\",\n \"enabled\",\n \"disabled\",\n \"flag\",\n \"flags\",\n \"present\",\n \"set\",\n \"unset\",\n \"configured\",\n \"missing\",\n \"required\",\n \"valid\",\n \"invalid\",\n \"exists\",\n \"type\",\n \"types\",\n]);\n\n/**\n * Trailing token that makes the identifier metadata *about* a secret (its\n * category / handle / label), not the credential: `tokenType`, `tokenName`,\n * `sessionId`, `credentialKind`. `type`/`id` are already dropped by the shared\n * innocuous set; `name`/`kind` are added here because logging them can still\n * matter (SARJ012) but they are never a timing surface.\n */\nconst DESCRIPTOR_WORDS: ReadonlySet<string> = new Set([\n \"type\",\n \"types\",\n \"name\",\n \"names\",\n \"id\",\n \"ids\",\n \"kind\",\n \"kinds\",\n]);\n\n/**\n * A `type`/`kind` token anywhere marks an enum/category discriminator, not a\n * credential: `TOKEN_TYPE_SYSTEM`, `credentialType`, `grantKind`.\n */\nconst CATEGORY_WORDS: ReadonlySet<string> = new Set([\"type\", \"types\", \"kind\", \"kinds\"]);\n\n/**\n * A leading boolean-predicate word marks a flag, not the credential itself:\n * `isToken`, `hasSecret`, `has_secret`, `isTokenStrategy`. Consulted by the\n * SHARED `isSecretName`, so every consumer gets it: this is the exact mirror of\n * the trailing innocuous-word check that already lives there — a boolean\n * answering \"does a secret exist?\" leaks nothing whether the marker leads or\n * trails, and word ORDER should not decide whether a name counts as a\n * credential. Matched as a whole leading WORD via `leadingWord`, never as a\n * character prefix, so `hash_secret` (`hash` != `has`), `issuer_token`\n * (`issuer` != `is`) and `canary_token` (`canary` != `can`) keep firing.\n */\nexport const FLAG_PREFIXES: ReadonlySet<string> = new Set([\n \"is\",\n \"has\",\n \"was\",\n \"are\",\n \"can\",\n \"should\",\n]);\n\n/**\n * Words that make an identifier a secret *only* via an integrity/content hash\n * (`contentHash`, `metadataHash`, `rowHash`) rather than an authenticator. A name\n * that ALSO carries one of these keeps firing (`passwordHash`, `tokenHash`,\n * `computedHmac`, `signature`): those gate access, a plain digest of content does\n * not.\n */\nconst AUTH_WORDS: ReadonlySet<string> = new Set([\n \"token\",\n \"secret\",\n \"secrets\",\n \"password\",\n \"passwd\",\n \"passwords\",\n \"jwt\",\n \"credential\",\n \"credentials\",\n \"authorization\",\n \"signature\",\n \"hmac\",\n \"apikey\",\n \"bearer\",\n]);\n\n/**\n * camelCase / PascalCase / ALLCAPS / digit-run splitter, applied to each\n * snake/kebab segment. `APIKey` -> [\"API\", \"Key\"], `authToken` -> [\"auth\", \"Token\"].\n */\nconst CAMEL_RE = /[A-Z]+(?=[A-Z][a-z])|[A-Z]?[a-z]+|[A-Z]+|\\d+/g;\nconst SEGMENT_RE = /[^A-Za-z0-9]+/;\n\n/**\n * Ordered lowercase tokens from snake_case + camelCase decomposition. Also\n * yields each whole snake/kebab segment lowercased, so a pathological mixed-case\n * single word like `ToKeN` (which camel-splitting shreds into `to`/`ke`/`n`)\n * still surfaces its intended `token` form.\n */\nexport function tokenize(identifier: string): string[] {\n const tokens: string[] = [];\n for (const segment of identifier.split(SEGMENT_RE)) {\n if (!segment) {\n continue;\n }\n tokens.push(segment.toLowerCase());\n for (const part of segment.match(CAMEL_RE) ?? []) {\n tokens.push(part.toLowerCase());\n }\n }\n return tokens;\n}\n\n/**\n * The first *word* of an identifier, from camelCase splitting.\n *\n * `tokenize` deliberately emits each whole snake/kebab segment before its camel\n * parts, so `tokens[0]` for the camelCase `hasSecret` is the useless `hassecret`\n * rather than `has`. Reading `tokens[0]` directly — as the Python original's\n * local check did — therefore never matched a camelCase flag at all, and Python's\n * shared predicate had no leading check whatsoever; both were false positives,\n * fixed on both sides. The leading-word check has to split the first segment\n * explicitly or every `isToken` / `hasSecret` flag is mistaken for a credential.\n */\nexport function leadingWord(identifier: string): string | undefined {\n for (const segment of identifier.split(SEGMENT_RE)) {\n if (segment) {\n return (segment.match(CAMEL_RE) ?? [segment])[0]?.toLowerCase();\n }\n }\n return undefined;\n}\n\n/** True if an `api` token is immediately followed by a `key` token (`api_key`). */\nexport function hasApiKey(tokens: readonly string[]): boolean {\n for (let i = 0; i + 1 < tokens.length; i++) {\n if (tokens[i] === \"api\" && tokens[i + 1] === \"key\") {\n return true;\n }\n }\n return false;\n}\n\n/**\n * True if `identifier` names raw secret material (a credential, not metadata).\n * `innocuous` defaults to the shared metadata set; callers that need a wider\n * exemption list (e.g. `no-secret-in-log`) pass their own superset.\n *\n * Two symmetric metadata guards run before the secret-word scan: a TRAILING\n * innocuous word (`tokenCount`, `apiKeyId`) and a LEADING boolean-predicate word\n * (`hasSecret`, `is_token`). Both describe a name that is *about* a credential\n * rather than being one.\n */\nexport function isSecretName(\n identifier: string,\n innocuous: ReadonlySet<string> = INNOCUOUS_WORDS,\n): boolean {\n const tokens = tokenize(identifier);\n const last = tokens.at(-1);\n if (last !== undefined && innocuous.has(last)) {\n return false;\n }\n const first = leadingWord(identifier);\n if (first !== undefined && FLAG_PREFIXES.has(first)) {\n return false;\n }\n if (tokens.some((tok) => SECRET_WORDS.has(tok))) {\n return true;\n }\n return hasApiKey(tokens);\n}\n\n/**\n * True if `identifier` names an *authenticator* — an access-gating secret whose\n * bytes an attacker could recover by timing a byte-wise comparison.\n *\n * Narrows `isSecretName` for SARJ011: strips category/handle descriptors and\n * `type`/`kind` discriminators, neither of which is a timing-attack surface even\n * though logging them may still matter. Leading boolean-predicate flags are no\n * longer stripped here — `isSecretName` now rejects them for every consumer, so\n * repeating the check would be dead code.\n */\nexport function isAuthSecretName(identifier: string): boolean {\n if (!isSecretName(identifier)) {\n return false;\n }\n const tokens = tokenize(identifier);\n const last = tokens.at(-1);\n if (last !== undefined && DESCRIPTOR_WORDS.has(last)) {\n return false;\n }\n if (tokens.some((tok) => CATEGORY_WORDS.has(tok))) {\n return false;\n }\n if (tokens.some((tok) => AUTH_WORDS.has(tok))) {\n return true;\n }\n return hasApiKey(tokens);\n}\n","/**\n * @fileoverview Flag type-safety escape hatches that fully disable checking:\n * 1. `x as any` (and the `<any>x` assertion form) — silences every downstream\n * error on the value.\n * 2. `x as unknown as T` double-casts — assert through `unknown`/`any` to reach\n * an unrelated type, bypassing the compiler's structural check.\n *\n * `as const` is exempt — it narrows rather than widens and is the prescribed\n * pattern for literal inference.\n *\n * TEST FILES ARE EXEMPT. Corpus sweep (2220 files across zod / TanStack Query /\n * react-router / swr / zustand, 2026-07): 947 raw hits, 357 of them inside test\n * files, and the test-file ones are a different act entirely. A test asserts on\n * behaviour, not on types: it hands the subject a deliberately partial or\n * deliberately invalid value to drive a branch that a well-typed caller could\n * never reach, and `as any` is how you spell that.\n * `query/packages/preact-query/src/__tests__/HydrationBoundary.test.tsx:401`\n * (`<HydrationBoundary state={{} as any}>`, exercising the empty-state path) and\n * `zustand/tests/devtools.test.tsx` (71 hits, all mock wiring) are\n * representative. \"Model the shape\" is not advice a fixture can act on, so every\n * one of those reports is noise the author must suppress. The 590 hits in\n * production sources still fire.\n */\n\nimport { ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\nimport { AST_NODE_TYPES } from \"@typescript-eslint/utils\";\n\nimport { isGeneratedFile, isTestFile } from \"./_paths.js\";\n\ntype MessageIds = \"asAny\" | \"doubleCast\";\n\nfunction isAnyAnnotation(node: TSESTree.TypeNode): boolean {\n return node.type === AST_NODE_TYPES.TSAnyKeyword;\n}\n\nfunction isConstAssertion(typeAnnotation: TSESTree.TypeNode): boolean {\n return (\n typeAnnotation.type === AST_NODE_TYPES.TSTypeReference &&\n typeAnnotation.typeName.type === AST_NODE_TYPES.Identifier &&\n typeAnnotation.typeName.name === \"const\"\n );\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<[], MessageIds>({\n name: \"no-unsafe-cast\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Disallow `as any`/`<any>` casts and `as unknown as T` double-casts, which fully disable type checking.\",\n },\n schema: [],\n messages: {\n asAny:\n \"`as any` disables type checking on this value. Narrow with a type guard, model the shape, or cast to a specific type instead.\",\n doubleCast:\n \"`as unknown as T` double-cast bypasses the compiler's structural check. Prefer a runtime validator (e.g. a zod schema) or an accurate type at the boundary.\",\n },\n },\n defaultOptions: [],\n create(context) {\n if (isTestFile(context.filename) || isGeneratedFile(context.filename, context.sourceCode.text)) {\n return {};\n }\n\n function checkAssertion(\n node: TSESTree.TSAsExpression | TSESTree.TSTypeAssertion,\n ): void {\n if (isConstAssertion(node.typeAnnotation)) {\n return;\n }\n\n if (isAnyAnnotation(node.typeAnnotation)) {\n context.report({ node, messageId: \"asAny\" });\n return;\n }\n\n const inner = node.expression;\n if (\n inner.type === AST_NODE_TYPES.TSAsExpression ||\n inner.type === AST_NODE_TYPES.TSTypeAssertion\n ) {\n context.report({ node, messageId: \"doubleCast\" });\n }\n }\n\n return {\n TSAsExpression: checkAssertion,\n TSTypeAssertion: checkAssertion,\n };\n },\n});\n","/**\n * @fileoverview Flag raw `string` used where a closed enumeration is clearly\n * intended, and comparison clusters against a fixed set of string literals.\n * The prescribed replacement is a string-literal union type\n * (`type Status = \"active\" | \"inactive\"`) — NOT a `StrEnum`/`enum`, since the\n * companion `no-enum` rule bans TypeScript enums.\n *\n * This is the TypeScript analog of the Python rule SARJ006 (prefer-str-enum).\n * It fires on two shapes:\n *\n * 1. **Choice-like field** — a `TSPropertySignature` (interface / type literal)\n * or class `PropertyDefinition` whose key's last word is one of the\n * high-precision CHOICE tokens (`status`, `state`, `kind`, `role`,\n * `priority`, `severity`, `direction`, `tier`, `stage`, `type`, `mode`,\n * `level`) and whose type annotation is the bare `string` keyword. Because\n * open-set API DTO fields (`status: string` from an untyped backend) are the\n * dominant false positive, a bare field fires ONLY when CORROBORATED by a\n * sibling string-literal-union member in the SAME interface / class / object\n * type. A file-wide comparison cluster on the field's name is deliberately\n * NOT used to corroborate: it flags unrelated same-named fields (DB-row casts\n * like `as Array<{ status: string }>`, passthrough DTOs). The closed-set fact\n * is still surfaced — as a `comparisonCluster` diagnostic at the comparison\n * site, which is the actionable location.\n *\n * 2. **Comparison cluster** — within one function scope, the same identifier or\n * member expression compared (`===` / `!==` / `==` / `!=`, or a `switch`)\n * against 2+ distinct short lowercase string literals (each matching\n * `^[a-z][a-z0-9_-]{0,30}$`). One diagnostic per cluster. This shape is\n * type-aware: it fires ONLY when the compared operand's resolved type is the\n * general `string` type. An operand already typed as a string-literal union\n * (`CallStatus = \"a\" | \"b\"`, a discriminated-union tag) is the target state\n * and is suppressed — a syntactic rule can't see through a named/imported\n * union, so without type information this shape is inert (the choice-field\n * shape still runs).\n *\n * A cluster inside the very function that PRODUCES the union is suppressed\n * too: when the enclosing function's declared return type is a string-literal\n * union containing every compared literal, the `string` parameter is the\n * narrowing boundary and MUST stay `string` —\n * `function parse(s: string): Status { return s === \"a\" || s === \"b\" ? s : null }`\n * is the fix, not the violation.\n *\n * `Literal`-union types (`type X = \"a\" | \"b\"`) are the target state and never\n * fire. Generated files (`*.gen.ts`, `**\\/generated/**`, `*.d.ts`, or a\n * `@generated` marker) opt out. Vendor-owned OPEN sets that merely look closed\n * (a Resend `bounceType`, a Slack `event.subtype` — dozens of values, more added\n * over time) can be listed in the `ignoreFields` option; narrowing to a union we\n * don't control would be wrong, not better.\n */\n\nimport {\n ESLintUtils,\n type TSESTree,\n type ParserServicesWithTypeInformation,\n AST_NODE_TYPES,\n} from \"@typescript-eslint/utils\";\nimport * as ts from \"typescript\";\n\ntype MessageIds = \"bareChoiceField\" | \"comparisonCluster\";\ntype Options = readonly [\n {\n ignoreFields?: readonly string[];\n }?,\n];\n\ntype FunctionNode =\n | TSESTree.FunctionDeclaration\n | TSESTree.FunctionExpression\n | TSESTree.ArrowFunctionExpression;\n\nconst CHOICE_TOKENS: ReadonlySet<string> = new Set([\n \"status\",\n \"state\",\n \"kind\",\n \"role\",\n \"priority\",\n \"severity\",\n \"direction\",\n \"tier\",\n \"stage\",\n \"type\",\n \"mode\",\n \"level\",\n]);\n\nconst LOWER_TOKEN_RE = /^[a-z][a-z0-9_-]{0,30}$/;\nconst MIN_CLUSTER_SIZE = 2;\n\nconst BOOLEANISH: ReadonlySet<string> = new Set([\"true\", \"false\"]);\n\n/**\n * An enum-shaped token worth a string-literal union: a short lowercase word of\n * 2+ chars that isn't a boolean-string. Single characters (`'a'`), file\n * paths/URLs/i18n keys (contain `/`, `:`, `.`), and `'true'`/`'false'` are NOT\n * closed-enum members — comparing against them is a flag/path/boolean guard.\n */\nfunction isEnumToken(lit: string): boolean {\n return LOWER_TOKEN_RE.test(lit) && lit.length >= 2 && !BOOLEANISH.has(lit);\n}\n\nconst IGNORE_PATTERNS: readonly RegExp[] = [\n /[\\\\/]generated[\\\\/]/,\n /\\.gen\\.tsx?$/,\n /\\.generated\\.tsx?$/,\n /\\.d\\.ts$/,\n];\n\nfunction isIgnoredFile(filename: string, sourceText: string): boolean {\n if (IGNORE_PATTERNS.some((re) => re.test(filename))) {\n return true;\n }\n return /@generated\\b/.test(sourceText.slice(0, 1024));\n}\n\n/**\n * The trailing word of a camelCase / snake_case identifier, lowercased.\n * `callStatus` -> `status`, `user_role` -> `role`, `estate` -> `estate`.\n */\nfunction lastWord(name: string): string {\n const words = name\n .replace(/([a-z0-9])([A-Z])/g, \"$1 $2\")\n .split(/[_\\s]+/)\n .filter((w) => w.length > 0);\n const last = words[words.length - 1] ?? name;\n return last.toLowerCase();\n}\n\nfunction isChoiceLikeName(name: string): boolean {\n return CHOICE_TOKENS.has(lastWord(name));\n}\n\nfunction keyName(\n key: TSESTree.PropertyDefinition[\"key\"] | TSESTree.PropertyName,\n): string | null {\n if (key.type === AST_NODE_TYPES.Identifier) {\n return key.name;\n }\n if (key.type === AST_NODE_TYPES.Literal && typeof key.value === \"string\") {\n return key.value;\n }\n return null;\n}\n\nfunction isStringLiteralMember(t: TSESTree.TypeNode): boolean {\n return (\n t.type === AST_NODE_TYPES.TSLiteralType &&\n t.literal.type === AST_NODE_TYPES.Literal &&\n typeof t.literal.value === \"string\"\n );\n}\n\n/** Whether a type node is a union of 2+ string-literal types. */\nfunction isStringLiteralUnion(node: TSESTree.TypeNode | undefined): boolean {\n if (node?.type !== AST_NODE_TYPES.TSUnionType) {\n return false;\n }\n return node.types.filter(isStringLiteralMember).length >= MIN_CLUSTER_SIZE;\n}\n\n/**\n * Whether a resolved TS type includes the general `string` type — as opposed to\n * being (a union of) string-literal types. `CallStatus = \"a\" | \"b\"` has no\n * general-string member and is already the target state; a raw `string` (or\n * `string | undefined`) does, and a comparison cluster on it should become a\n * union. Requires type information; the comparison-cluster path is inert\n * without it.\n */\nfunction typeHasRawString(type: ts.Type): boolean {\n const parts = type.isUnion() ? type.types : [type];\n return parts.some((t) => (t.flags & ts.TypeFlags.String) !== 0);\n}\n\nfunction isExternalSourceFile(sf: ts.SourceFile | undefined): boolean {\n if (sf === undefined) {\n return false;\n }\n return sf.isDeclarationFile || sf.fileName.includes(\"/node_modules/\");\n}\n\nfunction symbolIsExternallyDeclared(sym: ts.Symbol | undefined): boolean {\n return (\n sym?.declarations?.some((d) => isExternalSourceFile(d.getSourceFile())) ??\n false\n );\n}\n\n/**\n * The iterable expression a destructuring binding element draws its value from,\n * e.g. `Object.entries(x)` for `key` in `for (const [key] of Object.entries(x))`\n * or the RHS of `const { a } = rhs`.\n */\nfunction bindingSourceExpression(\n decl: ts.BindingElement,\n): ts.Expression | undefined {\n let node: ts.Node = decl.parent;\n while (\n !ts.isForOfStatement(node) &&\n !(ts.isVariableDeclaration(node) && node.initializer !== undefined)\n ) {\n if (node.parent === undefined) {\n return undefined;\n }\n node = node.parent;\n }\n return ts.isForOfStatement(node) ? node.expression : node.initializer;\n}\n\n/** A stable key for a plain identifier or non-computed member chain, else null. */\nfunction refKey(node: TSESTree.Node): string | null {\n if (node.type === AST_NODE_TYPES.Identifier) {\n return node.name;\n }\n if (node.type === AST_NODE_TYPES.MemberExpression && !node.computed) {\n const inner = refKey(node.object);\n if (inner === null || node.property.type !== AST_NODE_TYPES.Identifier) {\n return null;\n }\n return `${inner}.${node.property.name}`;\n }\n return null;\n}\n\nfunction strLiteral(node: TSESTree.Node): string | null {\n if (node.type === AST_NODE_TYPES.Literal && typeof node.value === \"string\") {\n return node.value;\n }\n return null;\n}\n\ninterface ClusterEntry {\n node: TSESTree.Node;\n literals: Set<string>;\n allTokens: boolean;\n}\n\ninterface Scope {\n clusters: Map<string, ClusterEntry>;\n /** The function this scope belongs to; null at file scope. */\n fn: FunctionNode | null;\n}\n\ninterface CollectedProperty {\n name: string;\n container: TSESTree.Node;\n node: TSESTree.Node;\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"prefer-string-literal-union\",\n meta: {\n type: \"suggestion\",\n docs: {\n description:\n \"Flag raw `string` choice fields and string-literal comparison clusters; prefer a string-literal union type.\",\n },\n schema: [\n {\n type: \"object\",\n additionalProperties: false,\n properties: {\n ignoreFields: {\n type: \"array\",\n items: { type: \"string\" },\n },\n },\n },\n ],\n messages: {\n bareChoiceField:\n '`{{name}}: string` looks like a choice field — prefer a string-literal union type (e.g. `type X = \"a\" | \"b\"`). Enums are banned by `no-enum`; use a union.',\n comparisonCluster:\n '`{{key}}` is compared against a closed set of string literals — define a string-literal union type (e.g. `type X = \"a\" | \"b\"`).',\n },\n },\n defaultOptions: [{}],\n create(context, [optionsArg]) {\n const filename = context.filename;\n const sourceText = context.sourceCode.getText();\n if (isIgnoredFile(filename, sourceText)) {\n return {};\n }\n\n const ignoredFields = new Set(\n (optionsArg?.ignoreFields ?? []).map((name) => name.toLowerCase()),\n );\n\n let services: ParserServicesWithTypeInformation | null;\n try {\n services = ESLintUtils.getParserServices(context);\n } catch {\n services = null;\n }\n\n const scopeStack: Scope[] = [];\n const validClusters: TSESTree.Node[] = [];\n const bareChoiceProps: CollectedProperty[] = [];\n const containersWithUnion = new Set<TSESTree.Node>();\n\n function operandIsRawString(node: TSESTree.Node): boolean {\n if (services === null) {\n return false;\n }\n return typeHasRawString(services.getTypeAtLocation(node));\n }\n\n /**\n * Whether the compared value provably originates from a type we don't own —\n * a property of a `.d.ts` / node_modules type (DOM `getComputedStyle().overflowY`,\n * `Task.status` from another package), the return of such a method\n * (`URLSearchParams.get()`, `str.toLowerCase()`), or a destructuring of one\n * (`Object.entries()` keys). A string-literal union is not an available fix\n * for these, so the cluster shape must not flag them.\n */\n function originIsExternal(node: ts.Node | undefined, depth: number): boolean {\n if (node === undefined || services === null || depth > 6) {\n return false;\n }\n const checker = services.program.getTypeChecker();\n if (\n ts.isParenthesizedExpression(node) ||\n ts.isNonNullExpression(node) ||\n ts.isAsExpression(node)\n ) {\n return originIsExternal(node.expression, depth + 1);\n }\n if (ts.isPropertyAccessExpression(node)) {\n return symbolIsExternallyDeclared(checker.getSymbolAtLocation(node.name));\n }\n if (ts.isCallExpression(node)) {\n return originIsExternal(node.expression, depth + 1);\n }\n if (ts.isIdentifier(node)) {\n const decl = checker.getSymbolAtLocation(node)?.valueDeclaration;\n if (decl === undefined) {\n return false;\n }\n if (ts.isVariableDeclaration(decl) && decl.initializer !== undefined) {\n return originIsExternal(decl.initializer, depth + 1);\n }\n if (ts.isBindingElement(decl)) {\n return originIsExternal(bindingSourceExpression(decl), depth + 1);\n }\n }\n return false;\n }\n\n function operandIsFlaggable(node: TSESTree.Node): boolean {\n return (\n operandIsRawString(node) &&\n !originIsExternal(services?.esTreeNodeToTSNodeMap.get(node), 0)\n );\n }\n\n /**\n * The string-literal members of a function's DECLARED return type, resolved\n * through named aliases and `Promise<...>`. Null when the function has no\n * annotation, type information is unavailable, or the type is not a\n * string-literal union. Computed lazily — only for a function that actually\n * accumulated a cluster — since it costs a type resolution.\n */\n function declaredReturnLiterals(fn: FunctionNode): ReadonlySet<string> | null {\n const annotation = fn.returnType?.typeAnnotation;\n if (annotation === undefined || services === null) {\n return null;\n }\n const tsNode = services.esTreeNodeToTSNodeMap.get(annotation);\n if (!ts.isTypeNode(tsNode)) {\n return null;\n }\n const checker = services.program.getTypeChecker();\n const declared = checker.getTypeFromTypeNode(tsNode);\n const type = checker.getAwaitedType(declared) ?? declared;\n const literals = new Set<string>();\n for (const part of type.isUnion() ? type.types : [type]) {\n if (part.isStringLiteral()) {\n literals.add(part.value);\n }\n }\n return literals.size >= MIN_CLUSTER_SIZE ? literals : null;\n }\n\n function pushScope(node: FunctionNode): void {\n scopeStack.push({ clusters: new Map(), fn: node });\n }\n\n function popScope(): void {\n const scope = scopeStack.pop();\n if (scope === undefined) {\n return;\n }\n const candidates = [...scope.clusters.values()].filter(\n (entry) => entry.allTokens && entry.literals.size >= MIN_CLUSTER_SIZE,\n );\n if (candidates.length === 0) {\n return;\n }\n const returnLiterals =\n scope.fn === null ? null : declaredReturnLiterals(scope.fn);\n for (const entry of candidates) {\n // The enclosing function declares the very union being compared against:\n // this is the narrowing boundary that produces it, so the `string`\n // operand is required, not a defect.\n if (\n returnLiterals !== null &&\n [...entry.literals].every((lit) => returnLiterals.has(lit))\n ) {\n continue;\n }\n validClusters.push(entry.node);\n }\n }\n\n function accumulate(\n key: string,\n literals: string[],\n node: TSESTree.Node,\n ): void {\n const scope = scopeStack[scopeStack.length - 1];\n if (scope === undefined) {\n return;\n }\n const allTokens = literals.every((lit) => isEnumToken(lit));\n const existing = scope.clusters.get(key);\n if (existing === undefined) {\n scope.clusters.set(key, {\n node,\n literals: new Set(literals),\n allTokens,\n });\n return;\n }\n for (const lit of literals) {\n existing.literals.add(lit);\n }\n existing.allTokens = existing.allTokens && allTokens;\n }\n\n function collectProperty(\n key: TSESTree.PropertyDefinition[\"key\"] | TSESTree.PropertyName,\n typeNode: TSESTree.TypeNode | undefined,\n container: TSESTree.Node,\n node: TSESTree.Node,\n ): void {\n if (isStringLiteralUnion(typeNode)) {\n containersWithUnion.add(container);\n return;\n }\n if (typeNode?.type !== AST_NODE_TYPES.TSStringKeyword) {\n return;\n }\n const name = keyName(key);\n if (\n name === null ||\n !isChoiceLikeName(name) ||\n ignoredFields.has(name.toLowerCase())\n ) {\n return;\n }\n bareChoiceProps.push({ name, container, node });\n }\n\n return {\n FunctionDeclaration: pushScope,\n \"FunctionDeclaration:exit\": popScope,\n FunctionExpression: pushScope,\n \"FunctionExpression:exit\": popScope,\n ArrowFunctionExpression: pushScope,\n \"ArrowFunctionExpression:exit\": popScope,\n\n BinaryExpression(node: TSESTree.BinaryExpression): void {\n if (\n node.operator !== \"===\" &&\n node.operator !== \"!==\" &&\n node.operator !== \"==\" &&\n node.operator !== \"!=\"\n ) {\n return;\n }\n const leftKey = refKey(node.left);\n const rightLit = strLiteral(node.right);\n const rightKey = refKey(node.right);\n const leftLit = strLiteral(node.left);\n if (leftKey !== null && rightLit !== null) {\n if (operandIsFlaggable(node.left)) {\n accumulate(leftKey, [rightLit], node);\n }\n } else if (rightKey !== null && leftLit !== null) {\n if (operandIsFlaggable(node.right)) {\n accumulate(rightKey, [leftLit], node);\n }\n }\n },\n\n SwitchStatement(node: TSESTree.SwitchStatement): void {\n const key = refKey(node.discriminant);\n if (key === null || !operandIsFlaggable(node.discriminant)) {\n return;\n }\n const literals: string[] = [];\n for (const c of node.cases) {\n if (c.test !== null) {\n const lit = strLiteral(c.test);\n if (lit !== null) {\n literals.push(lit);\n }\n }\n }\n if (literals.length > 0) {\n accumulate(key, literals, node);\n }\n },\n\n TSPropertySignature(node: TSESTree.TSPropertySignature): void {\n collectProperty(\n node.key,\n node.typeAnnotation?.typeAnnotation,\n node.parent,\n node,\n );\n },\n\n PropertyDefinition(node: TSESTree.PropertyDefinition): void {\n collectProperty(\n node.key,\n node.typeAnnotation?.typeAnnotation,\n node.parent,\n node,\n );\n },\n\n \"Program:exit\"(): void {\n for (const clusterNode of validClusters) {\n context.report({\n node: clusterNode,\n messageId: \"comparisonCluster\",\n data: { key: refKeyText(clusterNode) },\n });\n }\n for (const prop of bareChoiceProps) {\n if (containersWithUnion.has(prop.container)) {\n context.report({\n node: prop.node,\n messageId: \"bareChoiceField\",\n data: { name: prop.name },\n });\n }\n }\n },\n };\n\n function refKeyText(node: TSESTree.Node): string {\n if (node.type === AST_NODE_TYPES.BinaryExpression) {\n return refKey(node.left) ?? refKey(node.right) ?? \"value\";\n }\n if (node.type === AST_NODE_TYPES.SwitchStatement) {\n return refKey(node.discriminant) ?? \"value\";\n }\n return \"value\";\n }\n },\n});\n","/**\n * @fileoverview Flag a junk-drawer module stem that has a single public export.\n * TS port of Python SARJ022. Fires ONLY when BOTH hold:\n *\n * (a) the file's basename stem is a generic \"junk-drawer\" name that describes\n * no responsibility (`utils`, `helpers`, `types`, `models`, ...), AND\n * (b) the module has exactly one public export, and that export is a named\n * function / class / function-const, so the rename target is unambiguous.\n *\n * When both hold, the sole export's name is the information-rich replacement for\n * the meaningless stem (`utils.ts` exporting `snakeCaseText` -> `snake-case-text.ts`).\n *\n * A junk-drawer stem carries no domain to lose, so replacing it with the export\n * name is strictly an improvement; an informative stem (`pagination.ts`) names a\n * domain broader than its one current export and is deliberately never flagged.\n *\n * CORPUS SWEEP (2220 files, zod / TanStack Query / react-router / swr /\n * zustand, 2026-07): 1 hit, and it was a false positive —\n * `react-router/packages/react-router/__tests__/server-runtime/utils.ts:20`\n * exports one `mockServerBuild` helper. The basename-only `TEST_FILE_RE` does\n * not see `__tests__/`, so test-support modules escaped the existing test\n * exemption; a suite's shared helper module is `utils.ts` by convention and\n * renaming it buys nothing. The check now also consults the shared `isTestFile`\n * path predicate, which understands directories.\n */\n\nimport { ESLintUtils, type TSESTree, AST_NODE_TYPES } from \"@typescript-eslint/utils\";\n\nimport { isTestFile } from \"./_paths.js\";\n\ntype MessageIds = \"renameJunkDrawer\";\ntype Options = readonly [];\n\n// Generic module stems that describe no responsibility. `index` is deliberately\n// excluded: barrel files legitimately re-export many symbols under that name.\nconst JUNK_DRAWER_STEMS = new Set([\n \"util\",\n \"utils\",\n \"helper\",\n \"helpers\",\n \"common\",\n \"constant\",\n \"constants\",\n \"type\",\n \"types\",\n \"model\",\n \"models\",\n \"shared\",\n \"misc\",\n]);\n\n// Exports whose name is an idiomatic ecosystem convention that lives in a\n// junk-drawer bucket by design — flagging them fights the convention and the\n// bucket is expected to grow. `cn` is the shadcn/ui tailwind-merge className\n// helper that scaffolds into `lib/utils.ts`; renaming to `cn.ts` breaks every\n// `import { cn } from \"@/lib/utils\"`.\nconst CONVENTIONAL_BUCKET_EXPORTS = new Set([\"cn\"]);\n\n// Multi-word acronyms whose accepted kebab-case is a single token rather than a\n// letter-by-letter split (`OAuth` -> `oauth`, not `o-auth`).\nconst ACRONYM_OVERRIDES: ReadonlyArray<readonly [RegExp, string]> = [\n [/OAuth/g, \"Oauth\"],\n [/GraphQL/g, \"Graphql\"],\n [/gRPC/g, \"Grpc\"],\n];\n\n// Split on camelCase boundaries while keeping runs of capitals (acronyms)\n// together: `HTTPServer` -> `HTTP` + `Server`, `JWTHandler` -> `JWT` + `Handler`.\nconst CAMEL_BOUNDARY_RE = /(?<=[a-z0-9])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])/g;\n\nconst TEST_FILE_RE = /\\.(test|spec)\\.[cm]?[jt]sx?$/i;\nconst SCRIPT_EXT_RE = /\\.[cm]?[jt]sx?$/i;\n\ninterface ExportSummary {\n readonly names: number;\n readonly hasReExport: boolean;\n readonly candidate: { name: string; node: TSESTree.Node } | null;\n}\n\nconst basename = (filename: string): string =>\n filename.split(/[/\\\\]/).pop() ?? filename;\n\nconst stemOf = (base: string): string => base.replace(SCRIPT_EXT_RE, \"\");\n\nconst kebabCase = (name: string): string => {\n let normalized = name;\n for (const [pattern, replacement] of ACRONYM_OVERRIDES) {\n normalized = normalized.replace(pattern, replacement);\n }\n return normalized.replace(CAMEL_BOUNDARY_RE, \"-\").toLowerCase();\n};\n\nconst isFunctionExpression = (node: TSESTree.Expression | null): boolean =>\n node !== null &&\n (node.type === AST_NODE_TYPES.ArrowFunctionExpression ||\n node.type === AST_NODE_TYPES.FunctionExpression);\n\n// A `const foo = () => {}` is a rename candidate; a value const (`const MAX = 5`)\n// is not — renaming a file after a bare constant loses more than it gains.\nconst functionConstName = (\n decl: TSESTree.VariableDeclaration,\n): string | null => {\n if (decl.declarations.length !== 1) return null;\n const [declarator] = decl.declarations;\n if (declarator === undefined) return null;\n if (declarator.id.type !== AST_NODE_TYPES.Identifier) return null;\n if (!isFunctionExpression(declarator.init)) return null;\n return declarator.id.name;\n};\n\nconst summarizeExports = (body: readonly TSESTree.ProgramStatement[]): ExportSummary => {\n let names = 0;\n let hasReExport = false;\n let candidate: { name: string; node: TSESTree.Node } | null = null;\n\n const addCandidate = (name: string, node: TSESTree.Node): void => {\n names += 1;\n candidate = { name, node };\n };\n\n for (const statement of body) {\n switch (statement.type) {\n case AST_NODE_TYPES.ExportAllDeclaration:\n hasReExport = true;\n break;\n case AST_NODE_TYPES.ExportDefaultDeclaration: {\n names += 1;\n const decl = statement.declaration;\n if (\n decl.type === AST_NODE_TYPES.FunctionDeclaration &&\n decl.id !== null\n ) {\n candidate = { name: decl.id.name, node: statement };\n } else if (\n decl.type === AST_NODE_TYPES.ClassDeclaration &&\n decl.id !== null\n ) {\n candidate = { name: decl.id.name, node: statement };\n }\n break;\n }\n case AST_NODE_TYPES.ExportNamedDeclaration: {\n if (statement.source !== null) {\n hasReExport = true;\n break;\n }\n const decl = statement.declaration;\n if (decl === null) {\n names += statement.specifiers.length;\n break;\n }\n switch (decl.type) {\n case AST_NODE_TYPES.FunctionDeclaration:\n if (decl.id !== null) addCandidate(decl.id.name, statement);\n else names += 1;\n break;\n case AST_NODE_TYPES.ClassDeclaration:\n if (decl.id !== null) addCandidate(decl.id.name, statement);\n else names += 1;\n break;\n case AST_NODE_TYPES.VariableDeclaration: {\n const fnName = functionConstName(decl);\n if (fnName !== null && decl.declarations.length === 1) {\n addCandidate(fnName, statement);\n } else {\n names += decl.declarations.length;\n }\n break;\n }\n default:\n names += 1;\n }\n break;\n }\n default:\n break;\n }\n }\n\n return { names, hasReExport, candidate };\n};\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"single-public-export\",\n meta: {\n type: \"suggestion\",\n docs: {\n description:\n \"A junk-drawer module stem (`utils`, `helpers`, `types`, ...) with a single public function/class/const export should be renamed after that export.\",\n },\n schema: [],\n messages: {\n renameJunkDrawer:\n \"Module stem `{{stem}}` is a generic junk-drawer name; its sole public export is `{{name}}` — rename the file to `{{expected}}.ts` to describe its responsibility.\",\n },\n },\n defaultOptions: [],\n create(context) {\n const base = basename(context.filename);\n\n if (base.endsWith(\".d.ts\")) return {};\n if (TEST_FILE_RE.test(base)) return {};\n // A test-support module is conventionally `__tests__/utils.ts`; the suite\n // imports it as \"the helpers for these tests\" and the stem is meaningful in\n // that context. `TEST_FILE_RE` only sees the basename, so it misses these.\n if (isTestFile(context.filename)) return {};\n\n const stem = stemOf(base);\n if (!JUNK_DRAWER_STEMS.has(stem.toLowerCase())) return {};\n\n return {\n Program(node: TSESTree.Program): void {\n const { names, hasReExport, candidate } = summarizeExports(node.body);\n if (hasReExport) return;\n if (names !== 1 || candidate === null) return;\n if (CONVENTIONAL_BUCKET_EXPORTS.has(candidate.name)) return;\n\n const expected = kebabCase(candidate.name);\n if (stem === expected) return;\n\n context.report({\n node: candidate.node,\n messageId: \"renameJunkDrawer\",\n data: { stem, name: candidate.name, expected },\n });\n },\n };\n },\n});\n","/**\n * @fileoverview TS port of SARJ025 (`no-offset-pagination`). `LIMIT n OFFSET m`\n * makes the database scan and discard every one of the `m` skipped rows before\n * returning the page, so page N costs O(N): deep pages get linearly slower until\n * they blow the request budget. Worse, under concurrent inserts the offset window\n * shifts between requests, so a row can be returned twice or skipped entirely —\n * a paginated backfill silently misses records. Keyset / cursor pagination\n * (`WHERE id > ? ORDER BY id LIMIT n`) is O(page) and stable.\n *\n * The rule reads statically-resolvable SQL strings (literals, template literals,\n * `+` concatenations, `[...].join(\" \")` fragment arrays), neutralizes string\n * values and `--` / comment bodies first, and flags an `OFFSET` keyword\n * immediately followed by a value/param token — `?` or `?1` (SQLite/D1), `:name`,\n * `@name`, `$1`, or a bare digit. A `${...}` substitution has already become `?`\n * by then, so an interpolated offset is caught too.\n *\n * Requiring the value token is what keeps false positives at zero: it excludes\n * the English word (\"offset out of range\", \"no base offset\"), an `'offset'`\n * string value or object key, and array-index constructs like BigQuery's\n * `UNNEST(...) WITH OFFSET AS col`, none of which put a value after the keyword.\n *\n * // flagged\n * `SELECT id, status FROM runs ORDER BY created_at LIMIT ? OFFSET ?`\n *\n * // preferred\n * `SELECT id, status FROM runs WHERE id > ? ORDER BY id LIMIT ?`\n *\n * Test files are out of scope — a bounded fixture query is not a production\n * pagination path.\n *\n * CONVERGED WITH SARJ025 AND SARJ107 (2026-07). The concept is implemented three\n * times — here, in `packages/python/src/sarj_python_lint/rules/\n * no_offset_pagination.py` (SARJ025) and in\n * `packages/sql/src/sarj_sql_lint/rules/no_limit_offset.py` (SARJ107). This rule\n * and SARJ025 both required the value token and both documented why; SARJ107 was\n * a bare `\\bOFFSET\\b` and fired on `ALTER TABLE t ADD COLUMN offset INTEGER`,\n * and SARJ025's parameter set omitted `?`, so the `LIMIT ? OFFSET ?` spelling\n * this file uses as its own headline example was a silent false negative in\n * Python. Both are fixed, and all three now share ONE parameter alternation —\n * the union of the markers each dialect uses (`%s`, `%(name)s`, `?`, `?1`,\n * `:name`, `@name`, `$1`, digit) — so a marker added for one language cannot go\n * missing in another. `%s` is inert in TypeScript today; it is present so the\n * three patterns are literally identical and a diff between them is a bug.\n * Corpus delta here: 0 findings before and after over 748 first-party TS/TSX\n * files (bulbul `typescript/` + `sdks/typescript`, noura-fe).\n */\n\nimport { ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\nimport { isTestFile } from \"./_paths.js\";\nimport { createSqlListener } from \"./_sql.js\";\n\ntype MessageIds = \"noOffsetPagination\";\ntype Options = readonly [];\n\n/**\n * `OFFSET` followed by a value/param token — the real pagination construct.\n * The parameter alternatives are the UNION of every marker the three packages\n * see, and are kept identical in SARJ025 and SARJ107 — see the header.\n */\nconst OFFSET_PAGINATION = /\\bOFFSET\\s+(?:%s|%\\(\\w+\\)s|\\?\\d*|:\\w+|@\\w+|\\$\\d+|\\d+)/i;\n\n/** Cheap substring gate; noise-stripping can only ever remove keywords, never add them. */\nconst OFFSET_GATE = /offset/i;\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-offset-pagination\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Disallow OFFSET pagination in embedded SQL; it is O(N) per page and drops or repeats rows under concurrent writes. Use a keyset cursor.\",\n },\n schema: [],\n messages: {\n noOffsetPagination:\n \"OFFSET pagination scans and discards every skipped row (O(N) per page) and shifts under concurrent inserts, so rows get repeated or missed. Use a keyset cursor: `WHERE id > ? ORDER BY id LIMIT ?`.\",\n },\n },\n defaultOptions: [],\n create(context) {\n if (isTestFile(context.filename) || !OFFSET_GATE.test(context.sourceCode.text)) {\n return {};\n }\n return createSqlListener((sql: string, node: TSESTree.Node): void => {\n if (!OFFSET_PAGINATION.test(sql)) {\n return;\n }\n context.report({ node, messageId: \"noOffsetPagination\" });\n });\n },\n});\n","/**\n * @fileoverview Shared SQL-literal helpers for the SQL-aware store-lint rules.\n * TS port of Python's `rules/_sql.py`, adapted to where SQL actually lives in a\n * TypeScript / Cloudflare Workers codebase: D1 `db.prepare(`...`)` template\n * literals, `sql` tagged templates, `+`-concatenated fragments, and\n * `[...].join(\" \")` arrays of one-line fragments.\n *\n * These rules scan raw SQL for keywords (`INSERT`, `ON CONFLICT`, `OFFSET`,\n * `*`, ...). Scanning the raw text conflates SQL *code* with SQL string-literal\n * *values*: `WHERE p = 'on conflict'` holds no upsert clause, a `--` inside a\n * quoted value is not a comment, and `'*'` inside a value is not a projection\n * star. `stripSqlNoise` neutralizes both classes of noise before any keyword\n * scan — that single step is what keeps the false-positive rate of these rules\n * near zero.\n *\n * Template-literal substitutions (`${cols}`, `${values}`) are replaced with the\n * SQLite/D1 parameter marker `?` rather than dropped, so `LIMIT ? OFFSET ${n}`\n * still reads as a parameterised pagination clause and an interpolated\n * `VALUES ${rows}` still reads as an insert write.\n */\n\nimport { AST_NODE_TYPES, type TSESLint, type TSESTree } from \"@typescript-eslint/utils\";\n\n/**\n * Blank out SQL string-literal contents and comment bodies.\n *\n * A single left-to-right scan, so precedence between strings and comments is\n * correct: a `--` or quote inside a string literal is protected (masked as\n * string data, never read as a comment), and a quote inside a comment is\n * ignored. Every masked character becomes a space except newlines, which are\n * preserved so the text keeps its shape. Doubled quotes (`''` / `\"\"`) are SQL's\n * in-string escape and keep the scanner inside the literal.\n */\nexport function stripSqlNoise(text: string): string {\n const out = [...text];\n const n = text.length;\n let i = 0;\n while (i < n) {\n const ch = text[i];\n if (ch === \"'\" || ch === '\"') {\n out[i] = \" \";\n i += 1;\n while (i < n) {\n const c = text[i];\n if (c === ch) {\n if (i + 1 < n && text[i + 1] === ch) {\n out[i] = \" \";\n out[i + 1] = \" \";\n i += 2;\n continue;\n }\n out[i] = \" \";\n i += 1;\n break;\n }\n if (c !== \"\\n\") {\n out[i] = \" \";\n }\n i += 1;\n }\n continue;\n }\n if (ch === \"-\" && text[i + 1] === \"-\") {\n while (i < n && text[i] !== \"\\n\") {\n out[i] = \" \";\n i += 1;\n }\n continue;\n }\n if (ch === \"/\" && text[i + 1] === \"*\") {\n out[i] = \" \";\n out[i + 1] = \" \";\n i += 2;\n while (i < n && !(text[i] === \"*\" && text[i + 1] === \"/\")) {\n if (text[i] !== \"\\n\") {\n out[i] = \" \";\n }\n i += 1;\n }\n if (i < n) {\n out[i] = \" \";\n out[i + 1] = \" \";\n i += 2;\n }\n continue;\n }\n i += 1;\n }\n return out.join(\"\");\n}\n\n/** The parameter marker a `${...}` substitution is replaced with before scanning. */\nconst SUBSTITUTION_MARKER = \"?\";\n\n/**\n * Reconstruct the SQL text of a string-bearing node, or null when the node does\n * not statically resolve to one. Handles the four shapes real TS SQL takes:\n * a plain string literal, a template literal (substitutions become `?`), a\n * `+`-concatenation of those, and an array of fragments destined for `.join()`.\n */\nexport function sqlTextOf(node: TSESTree.Node): string | null {\n switch (node.type) {\n case AST_NODE_TYPES.Literal:\n return typeof node.value === \"string\" ? node.value : null;\n case AST_NODE_TYPES.TemplateLiteral:\n return node.quasis.map((q) => q.value.cooked ?? q.value.raw).join(SUBSTITUTION_MARKER);\n case AST_NODE_TYPES.TaggedTemplateExpression:\n return sqlTextOf(node.quasi);\n case AST_NODE_TYPES.BinaryExpression: {\n if (node.operator !== \"+\") {\n return null;\n }\n const left = sqlTextOf(node.left);\n const right = sqlTextOf(node.right);\n return left !== null && right !== null ? left + right : null;\n }\n case AST_NODE_TYPES.ArrayExpression: {\n const parts: string[] = [];\n for (const element of node.elements) {\n if (element === null) {\n return null;\n }\n const part = sqlTextOf(element);\n if (part === null) {\n return null;\n }\n parts.push(part);\n }\n return parts.length > 0 ? parts.join(\" \") : null;\n }\n default:\n return null;\n }\n}\n\n/**\n * True when `node` is an array whose fragments are glued together by `.join(...)`\n * — the `['INSERT INTO t (...)', 'VALUES (?)', 'ON CONFLICT ...'].join(' ')`\n * shape. Only then may the elements be read as one statement; an unrelated array\n * of strings is not SQL.\n */\nfunction isJoinedFragmentArray(node: TSESTree.ArrayExpression): boolean {\n const parent = node.parent;\n return (\n parent?.type === AST_NODE_TYPES.MemberExpression &&\n parent.object === node &&\n !parent.computed &&\n parent.property.type === AST_NODE_TYPES.Identifier &&\n parent.property.name === \"join\" &&\n parent.parent?.type === AST_NODE_TYPES.CallExpression\n );\n}\n\n/** Every string-bearing descendant that a composite node has already absorbed. */\nfunction markConsumed(node: TSESTree.Node, consumed: WeakSet<TSESTree.Node>): void {\n consumed.add(node);\n for (const key of Object.keys(node)) {\n if (key === \"parent\") {\n continue;\n }\n const value = (node as unknown as Record<string, unknown>)[key];\n for (const child of Array.isArray(value) ? value : [value]) {\n if (child !== null && typeof child === \"object\" && \"type\" in child) {\n markConsumed(child as TSESTree.Node, consumed);\n }\n }\n }\n}\n\n/**\n * Build the ESLint visitor that hands each *whole* SQL statement to `handler`\n * exactly once, along with the node to report on.\n *\n * ESLint traverses parents before children, so a composite node (a\n * `+`-concatenation or a joined fragment array) is seen first and marks its\n * string descendants consumed — the fragments are never re-reported on their own.\n */\nexport function createSqlListener(\n handler: (sql: string, node: TSESTree.Node) => void,\n): TSESLint.RuleListener {\n const consumed = new WeakSet<TSESTree.Node>();\n\n const visit = (node: TSESTree.Node): void => {\n if (consumed.has(node)) {\n return;\n }\n const text = sqlTextOf(node);\n if (text === null) {\n return;\n }\n markConsumed(node, consumed);\n handler(stripSqlNoise(text), node);\n };\n\n return {\n BinaryExpression: (node: TSESTree.BinaryExpression): void => {\n visit(node);\n },\n ArrayExpression: (node: TSESTree.ArrayExpression): void => {\n if (isJoinedFragmentArray(node)) {\n visit(node);\n }\n },\n TemplateLiteral: (node: TSESTree.TemplateLiteral): void => {\n visit(node);\n },\n Literal: (node: TSESTree.Literal): void => {\n visit(node);\n },\n };\n}\n","/**\n * @fileoverview TS port of SARJ026 (`prefer-namedtuple-over-tuple-return`). A\n * multi-field value returned across a module boundary should be an object with\n * named fields, not a positional tuple the caller has to unpack by position.\n *\n * `export async function fetchDoc(): Promise<[string, Headers, string | null]>`\n * forces every call site to remember which slot is which, and the names live\n * only in the destructuring at the call site — so two call sites can and do\n * disagree. Returning `{ body, headers, contentType }` names each field once, at\n * the definition, and a wrong-field access becomes a type error instead of a\n * silently wrong value.\n *\n * Fires on an *exported* function (or exported class method) whose declared\n * return type is a tuple of two or more elements — directly or wrapped in\n * `Promise<...>`. Exported means either the inline keyword (`export function\n * split`) or a detached specifier for the same module-scope binding (`export {\n * split }`, `export { split as s }`, `export default split`, `export = split`);\n * a re-export from another module and a type-only export do not count.\n *\n * The permitted tuple uses are deliberately NOT flagged, matching the Python\n * rule's tuning plus the TS-specific idioms:\n *\n * - **Homogeneous tuples** — `[number, number]`, `[Date, Date]`: a pair of the\n * same thing (a range, a coordinate), not distinct fields.\n * - **Variadic tuples** — `[string, ...number[]]`: an immutable sequence, not a\n * fixed record.\n * - **Labeled tuple members** — `[status: number, body: string]`: TypeScript\n * surfaces those names at the call site, which is the whole point of the rule.\n * - **Discriminated tags** — `[\"ok\", T]` with a literal first element: the tuple\n * IS the discriminated union.\n * - **React hooks** — any `use*` function. `[value, setValue]` is the\n * established contract of the entire hooks ecosystem; renaming at the call\n * site is the intended affordance, not a hazard.\n * - **Accessor/mutator pairs** — a 2-tuple with a function type in either slot\n * (`[T, (next: T) => void]`, `[() => void, Promise<void>]`). A function slot\n * is a capability handle, not a data field. That is the\n * same `[value, setValue]` contract without the `use` prefix, and every\n * reactive library outside React ships it: measured on 2,186 real TypeScript\n * files (zod / TanStack Query / react-router / swr / zustand), 2 of the 5 hits\n * were this shape —\n * query/packages/svelte-query/src/containers.svelte.ts:31 returning\n * `[T, (newValue: T) => void]` and\n * query/packages/query-persist-client-core/src/persist.ts:162 returning\n * `[() => void, Promise<void>]` (unsubscribe handle + completion). Neither has\n * \"fields\" to name; boxing them in an object would fight the ecosystem.\n * - Single-element tuples, unannotated returns, and non-exported helpers, whose\n * two or three call sites live in the same file as the definition.\n */\n\nimport { AST_NODE_TYPES, ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\ntype MessageIds = \"noPositionalTupleReturn\";\ntype Options = readonly [];\n\nconst MIN_ELEMENTS = 2;\n\n/** The pair length an accessor/mutator tuple has. */\nconst ACCESSOR_PAIR_LENGTH = 2;\n\n/** Type wrappers whose single type argument is the value actually returned. */\nconst AWAITABLE_TYPES: ReadonlySet<string> = new Set([\"Promise\", \"PromiseLike\", \"Awaited\"]);\n\n/** The tuple type a return annotation resolves to, unwrapping `Promise<...>`. */\nfunction tupleReturnType(node: TSESTree.TypeNode): TSESTree.TSTupleType | null {\n if (node.type === AST_NODE_TYPES.TSTupleType) {\n return node;\n }\n if (\n node.type === AST_NODE_TYPES.TSTypeReference &&\n node.typeName.type === AST_NODE_TYPES.Identifier &&\n AWAITABLE_TYPES.has(node.typeName.name)\n ) {\n const argument = node.typeArguments?.params[0];\n return argument === undefined ? null : tupleReturnType(argument);\n }\n return null;\n}\n\n/** Source text with all whitespace runs collapsed, for structural comparison. */\nfunction normalizedText(sourceCode: { getText: (node: TSESTree.Node) => string }, node: TSESTree.Node): string {\n return sourceCode.getText(node).replaceAll(/\\s+/g, \" \").trim();\n}\n\n/**\n * True when the tuple is one of the permitted shapes: variadic, labeled, tagged\n * by a leading literal type, or structurally homogeneous.\n */\nfunction isPermittedTuple(\n tuple: TSESTree.TSTupleType,\n sourceCode: { getText: (node: TSESTree.Node) => string },\n): boolean {\n const elements = tuple.elementTypes;\n if (elements.length < MIN_ELEMENTS) {\n return true;\n }\n if (elements.some((element) => element.type === AST_NODE_TYPES.TSRestType)) {\n return true;\n }\n if (elements.some((element) => element.type === AST_NODE_TYPES.TSNamedTupleMember)) {\n return true;\n }\n if (elements[0]?.type === AST_NODE_TYPES.TSLiteralType) {\n return true;\n }\n if (\n elements.length === ACCESSOR_PAIR_LENGTH &&\n elements.some((element) => element.type === AST_NODE_TYPES.TSFunctionType)\n ) {\n return true;\n }\n const texts = new Set(elements.map((element) => normalizedText(sourceCode, element)));\n return texts.size === 1;\n}\n\n/** The declared name of a function-ish node, or null for an anonymous one. */\nfunction functionName(node: TSESTree.Node): string | null {\n if (node.type === AST_NODE_TYPES.FunctionDeclaration) {\n return node.id?.name ?? null;\n }\n const parent = node.parent;\n if (parent?.type === AST_NODE_TYPES.VariableDeclarator && parent.id.type === AST_NODE_TYPES.Identifier) {\n return parent.id.name;\n }\n if (\n (parent?.type === AST_NODE_TYPES.MethodDefinition ||\n parent?.type === AST_NODE_TYPES.PropertyDefinition ||\n parent?.type === AST_NODE_TYPES.Property) &&\n parent.key.type === AST_NODE_TYPES.Identifier\n ) {\n return parent.key.name;\n }\n return null;\n}\n\n/**\n * True when an ancestor statement carries the `export` keyword inline —\n * `export function f`, `export const f =`, `export default function f`.\n */\nfunction isInlineExported(node: TSESTree.Node): boolean {\n for (let current: TSESTree.Node | undefined | null = node; current != null; current = current.parent) {\n const parent = current.parent;\n if (\n parent?.type === AST_NODE_TYPES.ExportNamedDeclaration ||\n parent?.type === AST_NODE_TYPES.ExportDefaultDeclaration\n ) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * The module-scope binding the function lives under — `split` for a top-level\n * `function split`, `Repo` for a method of a top-level `class Repo`. That is the\n * name an `export { … }` specifier elsewhere in the file would refer to. Null\n * when the function is not anchored to a named top-level declaration.\n */\nfunction moduleScopeBindingName(node: TSESTree.Node): string | null {\n let current: TSESTree.Node = node;\n let child: TSESTree.Node = node;\n while (current.parent != null && current.parent.type !== AST_NODE_TYPES.Program) {\n child = current;\n current = current.parent;\n }\n if (current.parent?.type !== AST_NODE_TYPES.Program) {\n return null;\n }\n if (\n current.type === AST_NODE_TYPES.FunctionDeclaration ||\n current.type === AST_NODE_TYPES.ClassDeclaration\n ) {\n return current.id?.name ?? null;\n }\n if (current.type === AST_NODE_TYPES.VariableDeclaration) {\n if (child.type !== AST_NODE_TYPES.VariableDeclarator || child.id.type !== AST_NODE_TYPES.Identifier) {\n return null;\n }\n return child.id.name;\n }\n return null;\n}\n\n/**\n * Local names this module puts on its public surface through a *detached* export\n * statement: `export { split }`, `export { split as s }`, `export default split`,\n * `export = split`. A re-export (`export { x } from \"./other\"`) names a binding\n * owned by another module, and a type-only export (`export type { x }`) does not\n * expose the value, so neither counts.\n */\nfunction specifierExportedNames(program: TSESTree.Program): ReadonlySet<string> {\n const names = new Set<string>();\n for (const statement of program.body) {\n if (\n statement.type === AST_NODE_TYPES.ExportNamedDeclaration &&\n statement.declaration == null &&\n statement.source == null &&\n statement.exportKind !== \"type\"\n ) {\n for (const specifier of statement.specifiers) {\n if (specifier.exportKind !== \"type\" && specifier.local.type === AST_NODE_TYPES.Identifier) {\n names.add(specifier.local.name);\n }\n }\n continue;\n }\n if (\n statement.type === AST_NODE_TYPES.ExportDefaultDeclaration &&\n statement.declaration.type === AST_NODE_TYPES.Identifier\n ) {\n names.add(statement.declaration.name);\n continue;\n }\n if (\n statement.type === AST_NODE_TYPES.TSExportAssignment &&\n statement.expression.type === AST_NODE_TYPES.Identifier\n ) {\n names.add(statement.expression.name);\n }\n }\n return names;\n}\n\n/**\n * True when the function is reachable from outside the module — the boundary the\n * rule is about. Either the declaration is exported inline, or its module-scope\n * binding is exported later through an export specifier.\n */\nfunction isExported(node: TSESTree.Node, specifierExports: ReadonlySet<string>): boolean {\n if (isInlineExported(node)) {\n return true;\n }\n if (specifierExports.size === 0) {\n return false;\n }\n const binding = moduleScopeBindingName(node);\n return binding !== null && specifierExports.has(binding);\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-positional-tuple-return\",\n meta: {\n type: \"suggestion\",\n docs: {\n description:\n \"Disallow returning a positional tuple of distinct fields from an exported function; return a named object so call sites cannot mismatch slots.\",\n },\n schema: [],\n messages: {\n noPositionalTupleReturn:\n \"Exported `{{name}}` returns a positional {{count}}-tuple, so every call site re-invents the field names and can disagree. Return a named object (or label the tuple members) instead.\",\n },\n },\n defaultOptions: [],\n create(context) {\n const specifierExports = specifierExportedNames(context.sourceCode.ast);\n const check = (\n node:\n | TSESTree.FunctionDeclaration\n | TSESTree.FunctionExpression\n | TSESTree.ArrowFunctionExpression,\n ): void => {\n const annotation = node.returnType?.typeAnnotation;\n if (annotation === undefined) {\n return;\n }\n const tuple = tupleReturnType(annotation);\n if (tuple === null || isPermittedTuple(tuple, context.sourceCode)) {\n return;\n }\n const name = functionName(node);\n if (name === null || name.startsWith(\"_\") || /^use[A-Z]/.test(name)) {\n return;\n }\n if (!isExported(node, specifierExports)) {\n return;\n }\n context.report({\n node: tuple,\n messageId: \"noPositionalTupleReturn\",\n data: { name, count: String(tuple.elementTypes.length) },\n });\n };\n return {\n FunctionDeclaration: check,\n FunctionExpression: check,\n ArrowFunctionExpression: check,\n };\n },\n});\n","/**\n * @fileoverview TS port of SARJ024 (`no-repeated-string-literal`). The same long,\n * *structured* string repeated across two or more functions of a module is a real\n * maintenance hazard: when one copy is edited the others silently drift. A column\n * list that gains a column in the read query but not in the upsert, or a prompt\n * template updated in one branch only, fails at runtime and nowhere else.\n *\n * COUNT THRESHOLD — CONVERGED WITH SARJ024 (2026-07). This rule and the Python\n * twin had drifted to two different definitions of \"repeated\": TS demanded three\n * total occurrences, Python only two distinct enclosing functions. A literal used\n * exactly twice therefore fired in `.py` and was clean in `.ts`, which is a\n * defect — the same code judged differently by file extension. Both were measured\n * and TS moved to Python's rule; **\"two distinct functions\" is the only count\n * threshold.** Evidence:\n * - Requiring three occurrences would drop 15 of the 18 findings over the\n * first-party Python corpora (bulbul `python/` + `sdks/python`, noura-be) and\n * the django/fastapi/celery FP controls, and the dropped ones are true\n * positives of exactly the shape this rule exists for:\n * `noura-be/.../modules/onboarding/store.py:631` repeats one\n * `SELECT stage FROM onboarding_profiles WHERE onboarding_token = %s FOR\n * UPDATE` verbatim between `submit_financial_info_atomic` and\n * `submit_legal_info_atomic`, and `.../modules/card/store.py:287` repeats a\n * 10-column `SELECT … JOIN card_types …` between two methods. Two copies is\n * where column-list drift *begins*; the third was arbitrary.\n * - Dropping the gate costs the TS side nothing. Over 748 first-party TS/TSX\n * files (bulbul `typescript/` + `sdks/typescript`, noura-fe) the rule yields\n * 0 findings at BOTH thresholds. Over the 2,186-file third-party corpus\n * (zod / TanStack Query / react-router / swr / zustand) it yields 0 at three\n * and 5 at two — and all 5 were react-router `*-test.ts` fixtures that the\n * test-file exemption should already have covered but did not, because\n * `_paths.isTestFile` only knew the `.test.ts` spelling. That gap is fixed at\n * its source, so the corpus delta of this change is 0 → 0 everywhere.\n * Precision here is carried by the *structured* filter below, not by counting.\n *\n * The rule is deliberately narrow — it fires only where cross-site drift is a\n * genuine bug, never on coincidentally-equal prose. Three filters combine:\n *\n * 1. **Structured only.** A literal qualifies only if it carries structural\n * signal that makes coincidental equality near-impossible: it contains a\n * newline (multi-line SQL / prompt templates), it matches an *uppercase* SQL\n * keyword (`SELECT`, `FROM`, `ON CONFLICT`, ...) — matched case-sensitively so\n * English prose does not trip it, only real SQL does — or it is a bare\n * snake_case / dotted identifier, i.e. a constraint / index / column-list name\n * reused across statements. Plain user-facing messages and log lines carry\n * none of these, so two same-text-different-intent messages are never coupled\n * into one shared constant.\n * 2. **Cross-function only.** Occurrences must span at least two distinct\n * enclosing functions. Two uses inside one function are edited together and\n * hoisting them buys no drift protection — that is pure locality loss. This\n * is the only count threshold; see the note above.\n * 3. **Exclusions.** Template literals *with* substitutions (the f-string\n * analogue — each fragment is half a sentence, not a reusable value), import\n * and `require` sources, JSX attribute values (a repeated Tailwind class list\n * is `no-duplicate-class-names` territory, not a drift bug), and test files,\n * where fixtures legitimately repeat literal payloads.\n *\n * A substitution-free template literal IS included: in TypeScript that is just a\n * multi-line string, and it is exactly where embedded SQL lives — but only when\n * it is UNTAGGED (see below).\n *\n * Every occurrence after the first is reported, so a deliberate duplicate can be\n * disabled on its own line.\n *\n * CORPUS SWEEP (2220 files across zod / TanStack Query / react-router / swr /\n * zustand, 2026-07): 96 raw hits, 100% of them the quasi of a TAGGED template\n * and every one a false positive. Two families:\n * - 94 in react-router's Playwright suites, where `js`…`` is the SOURCE TEXT of\n * a scratch app the test writes to disk\n * (`react-router/integration/single-fetch-test.ts:1482` repeats a 3-line\n * `app/routes/target.tsx` fixture). Hoisting a shared constant would move the\n * fixture away from the assertion that explains it — and the fixtures are\n * MEANT to be near-identical; that is what makes the assertions comparable.\n * - 2 in `query/packages/query-devtools/src/Devtools.tsx:398`, where ``css`…` ``\n * is a goober style block whose \"duplicate\" is a style rule, i.e. exactly the\n * styling-string category `isScaffolding` already excludes for JSX attributes.\n * A tagged template is an INVOCATION, not a string value: the tag decides what\n * the text means, so \"these two strings must not drift\" is not a claim the rule\n * can make about it. Tagged templates are therefore excluded. An untagged\n * multi-line SQL/prompt literal — the shape the rule exists for — still fires.\n */\n\nimport { AST_NODE_TYPES, ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\nimport { isTestFile } from \"./_paths.js\";\n\ntype MessageIds = \"noRepeatedStringLiteral\";\ntype Options = readonly [];\n\nconst MIN_LENGTH = 40;\nconst MIN_DISTINCT_SCOPES = 2;\nconst PREVIEW_LENGTH = 40;\n\n/** Case-sensitive on purpose: uppercase keywords mean SQL, lowercase means prose. */\nconst SQL_KEYWORD_RE =\n /\\b(SELECT|INSERT|UPDATE|DELETE|FROM|WHERE|JOIN|VALUES|ON CONFLICT|RETURNING|GROUP BY|ORDER BY)\\b/;\nconst IDENTIFIER_RE = /^[a-z_][a-z0-9_.]*$/;\n\nconst FUNCTION_TYPES: ReadonlySet<AST_NODE_TYPES> = new Set([\n AST_NODE_TYPES.FunctionDeclaration,\n AST_NODE_TYPES.FunctionExpression,\n AST_NODE_TYPES.ArrowFunctionExpression,\n]);\n\n/** True when the literal carries structure that rules out coincidental equality. */\nfunction isStructured(value: string): boolean {\n return value.includes(\"\\n\") || SQL_KEYWORD_RE.test(value) || IDENTIFIER_RE.test(value);\n}\n\nfunction preview(value: string): string {\n const oneLine = value.replaceAll(\"\\n\", \" \").trim();\n return oneLine.length <= PREVIEW_LENGTH ? oneLine : `${oneLine.slice(0, PREVIEW_LENGTH)}...`;\n}\n\n/** The enclosing function node, or null when the literal sits at module/class scope. */\nfunction enclosingFunction(node: TSESTree.Node): TSESTree.Node | null {\n for (let current = node.parent; current != null; current = current.parent) {\n if (FUNCTION_TYPES.has(current.type)) {\n return current;\n }\n }\n return null;\n}\n\n/**\n * True when the literal is scaffolding rather than a reusable value: an\n * import/`require` source (repeating a module path is the point) or a JSX\n * attribute value (styling strings, handled by the styling rules).\n */\nfunction isScaffolding(node: TSESTree.Node): boolean {\n const parent = node.parent;\n if (parent === undefined) {\n return true;\n }\n return (\n parent.type === AST_NODE_TYPES.ImportDeclaration ||\n parent.type === AST_NODE_TYPES.ExportNamedDeclaration ||\n parent.type === AST_NODE_TYPES.ExportAllDeclaration ||\n parent.type === AST_NODE_TYPES.TSImportType ||\n parent.type === AST_NODE_TYPES.JSXAttribute ||\n parent.type === AST_NODE_TYPES.TSLiteralType\n );\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-repeated-string-literal\",\n meta: {\n type: \"suggestion\",\n docs: {\n description:\n \"Disallow a long structured string literal repeated across functions; the copies drift when one is edited. Extract a module-level constant.\",\n },\n schema: [],\n messages: {\n noRepeatedStringLiteral:\n 'Structured string literal \"{{preview}}\" is repeated across functions (first use on line {{line}}) — extract a module-level constant so the copies cannot drift.',\n },\n },\n defaultOptions: [],\n create(context) {\n if (isTestFile(context.filename)) {\n return {};\n }\n const occurrences = new Map<string, TSESTree.Node[]>();\n const scopes = new WeakMap<TSESTree.Node, TSESTree.Node | null>();\n\n const record = (value: string, node: TSESTree.Node): void => {\n if (value.length < MIN_LENGTH || !isStructured(value) || isScaffolding(node)) {\n return;\n }\n const existing = occurrences.get(value);\n if (existing === undefined) {\n occurrences.set(value, [node]);\n } else {\n existing.push(node);\n }\n scopes.set(node, enclosingFunction(node));\n };\n\n return {\n Literal(node: TSESTree.Literal): void {\n if (typeof node.value === \"string\") {\n record(node.value, node);\n }\n },\n TemplateLiteral(node: TSESTree.TemplateLiteral): void {\n // A tagged template (`js`…``, `css`…``, `sql`…``) is a call, not a\n // string value — the tag, not this rule, decides what the text means.\n if (node.parent.type === AST_NODE_TYPES.TaggedTemplateExpression) {\n return;\n }\n const [only] = node.quasis;\n if (node.expressions.length === 0 && only !== undefined) {\n record(only.value.cooked ?? only.value.raw, node);\n }\n },\n \"Program:exit\": (): void => {\n for (const [value, nodes] of occurrences) {\n const distinctScopes = new Set(\n nodes.map((node) => scopes.get(node)).filter((scope) => scope != null),\n );\n if (distinctScopes.size < MIN_DISTINCT_SCOPES) {\n continue;\n }\n const [first, ...repeats] = nodes;\n if (first === undefined) {\n continue;\n }\n for (const node of repeats) {\n context.report({\n node,\n messageId: \"noRepeatedStringLiteral\",\n data: { preview: preview(value), line: String(first.loc.start.line) },\n });\n }\n }\n },\n };\n },\n});\n","/**\n * @fileoverview TS port of SARJ021 (`no-select-star`). `SELECT *` over-fetches\n * every column (including large JSON/text blobs the caller never reads) and,\n * more importantly, makes the row shape implicit: the moment a column is added,\n * renamed, or reordered, the zod schema that parses the row starts failing — or\n * worse, silently accepts extra keys — and nothing in the query says which\n * columns the code actually depends on. Naming the columns makes the dependency\n * explicit and pins the row contract at the query.\n *\n * The rule reads statically-resolvable SQL strings (literals, template literals,\n * `+` concatenations, `[...].join(\" \")` fragment arrays), neutralizes string\n * values and `--` / comment bodies first (so a `'*'` value is never mistaken for\n * a star), and flags a query — a string with a real `SELECT ... FROM` shape —\n * whose projection list holds a `*` in any position: bare (`SELECT *`,\n * `SELECT id, *`) or qualified (`c.*`, `main.runs.*`).\n *\n * Deliberately NOT flagged, matching the Python rule's tuning:\n * - `COUNT(*)` and other aggregate stars — the star is a function argument, not\n * a projection.\n * - `a * b` arithmetic — an operand follows the star.\n * - `EXISTS (SELECT * ...)` — the projection is unused by definition.\n * - Prose that merely contains the words \"select\" and \"from\".\n * - Test files, where a `SELECT *` assertion over a fixture table is fine.\n */\n\nimport { ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\nimport { isTestFile } from \"./_paths.js\";\nimport { createSqlListener } from \"./_sql.js\";\n\ntype MessageIds = \"noSelectStar\";\ntype Options = readonly [];\n\n/** A real SQL query shape, so prose containing the bare word \"from\" isn't matched. */\nconst QUERY_SHAPE = /\\bSELECT\\b[\\s\\S]*?\\bFROM\\b/i;\nconst SELECT_KEYWORD = /\\bSELECT\\b/gi;\nconst FROM_KEYWORD = /^FROM\\b/i;\nconst EXISTS_BEFORE = /\\bEXISTS\\s*\\(\\s*$/i;\n/** A `word.` immediately preceding a `*` marks a qualified star (`c.*`, `main.runs.*`). */\nconst QUALIFIED_PREFIX = /\\w\\.$/;\n\n/** Cheap substring gate; a file with no star and no SELECT can never produce a finding. */\nconst SELECT_GATE = /select/i;\n\n/**\n * True when the `*` at `pos` is a column-projection star.\n *\n * A projection star expands columns: bare (`SELECT *`, `id, *`) or qualified\n * (`c.*`). It is NOT a `COUNT(*)` argument (parenthesised on both sides) nor an\n * `a * b` multiply (an operand follows rather than a terminator).\n */\nfunction isProjectionStar(sql: string, pos: number): boolean {\n if (QUALIFIED_PREFIX.test(sql.slice(0, pos))) {\n return true;\n }\n let before = pos - 1;\n while (before >= 0 && /\\s/.test(sql[before] ?? \"\")) {\n before -= 1;\n }\n let after = pos + 1;\n while (after < sql.length && /\\s/.test(sql[after] ?? \"\")) {\n after += 1;\n }\n const beforeChar = before >= 0 ? (sql[before] ?? \"\") : \"\";\n const afterChar = after < sql.length ? (sql[after] ?? \"\") : \"\";\n const terminates =\n afterChar === \"\" || afterChar === \",\" || afterChar === \")\" || FROM_KEYWORD.test(sql.slice(after));\n if (!terminates) {\n return false;\n }\n return !(beforeChar === \"(\" && afterChar === \")\");\n}\n\n/** True when the query projects a star that is not inside an `EXISTS (...)` subquery. */\nfunction hasRealSelectStar(sql: string): boolean {\n const selects = [...sql.matchAll(SELECT_KEYWORD)].map((m) => m.index);\n for (let pos = 0; pos < sql.length; pos++) {\n if (sql[pos] !== \"*\" || !isProjectionStar(sql, pos)) {\n continue;\n }\n const owning = selects.filter((start) => start < pos).at(-1);\n if (owning !== undefined && !EXISTS_BEFORE.test(sql.slice(0, owning))) {\n return true;\n }\n }\n return false;\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-select-star\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Disallow SELECT * in embedded SQL; it over-fetches and leaves the row contract implicit, so a schema change breaks row parsing silently.\",\n },\n schema: [],\n messages: {\n noSelectStar:\n \"`SELECT *` over-fetches and leaves the row shape implicit — a new or reordered column silently changes what this query returns. List the columns explicitly.\",\n },\n },\n defaultOptions: [],\n create(context) {\n if (isTestFile(context.filename) || !SELECT_GATE.test(context.sourceCode.text)) {\n return {};\n }\n return createSqlListener((sql: string, node: TSESTree.Node): void => {\n if (!QUERY_SHAPE.test(sql) || !hasRealSelectStar(sql)) {\n return;\n }\n context.report({ node, messageId: \"noSelectStar\" });\n });\n },\n});\n","/**\n * @fileoverview TS port of SARJ031 (`no-sleep-in-test-body`). A real timed sleep\n * placed directly in a test body — `await new Promise((r) => setTimeout(r, 50))`\n * or `await sleep(200)` — is the canonical flaky-test pattern. The delay is a\n * guess about how long some other work takes: too short and the test fails on a\n * loaded CI runner, too long and every run pays for it. Either way the test is\n * asserting on wall-clock time rather than on the signal it actually cares about.\n *\n * The fix is to synchronize on the signal: await the promise the code returns,\n * await the flushed microtask queue, or drive time deterministically with\n * `vi.useFakeTimers()` + `await vi.advanceTimersByTimeAsync(ms)`, which makes the\n * elapsed time exact and the test instant.\n *\n * Fires only on the exact shape:\n *\n * - `new Promise(...)` whose executor body is a `setTimeout(resolve, <n>)`, or a\n * call to a bare `sleep`/`delay`/`wait`/`pause` helper, where\n * - the delay is a **nonzero numeric literal** — `setTimeout(r, 0)` is a\n * macrotask yield used to flush the event loop, not a timing guess, and a\n * non-literal `sleep(configuredDelay)` is a deliberate parameterised wait, and\n * - the **nearest enclosing function is the callback of an `it`/`test`** (any\n * `.only`/`.skip`/`.each` variant) or of a `beforeEach`/`afterEach` hook.\n *\n * The nearest-enclosing-function gate is the critical false-positive guard, and\n * it is ported deliberately: a sleep inside a nested helper or fake declared\n * within the test (`const slowFetch = async () => { await sleep(50); ... }`) is\n * *simulating* latency in order to exercise a timeout or cancellation path. That\n * is the intended use of a delay in a test, not a flaky synchronization, and it\n * must not fire. Because the check keys off the nearest enclosing function, such\n * a helper is excluded automatically. The `new Promise` executor arrow is not\n * treated as an enclosing function — it is part of the sleep idiom itself.\n *\n * Applies only in test files.\n */\n\nimport { AST_NODE_TYPES, ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\nimport { isTestFile } from \"./_paths.js\";\n\ntype MessageIds = \"noSleepInTestBody\";\ntype Options = readonly [];\n\n/** Free-function sleep helpers. A `sleep(50)` reads as a sleep whatever its module. */\nconst SLEEP_HELPERS: ReadonlySet<string> = new Set([\"sleep\", \"delay\", \"wait\", \"pause\"]);\n\n/** Test-case and per-test-hook callers whose callback body is \"the test body\". */\nconst TEST_CALLERS: ReadonlySet<string> = new Set([\n \"it\",\n \"test\",\n \"beforeEach\",\n \"afterEach\",\n]);\n\nconst FUNCTION_TYPES: ReadonlySet<AST_NODE_TYPES> = new Set([\n AST_NODE_TYPES.FunctionDeclaration,\n AST_NODE_TYPES.FunctionExpression,\n AST_NODE_TYPES.ArrowFunctionExpression,\n]);\n\n/** True for a nonzero numeric literal — the timing guess, as opposed to a `0` yield. */\nfunction isNonzeroNumericLiteral(node: TSESTree.Node | undefined): boolean {\n return node?.type === AST_NODE_TYPES.Literal && typeof node.value === \"number\" && node.value !== 0;\n}\n\n/** True when `node` is `setTimeout(<anything>, <nonzero literal>)`. */\nfunction isTimedSetTimeout(node: TSESTree.Node): boolean {\n return (\n node.type === AST_NODE_TYPES.CallExpression &&\n node.callee.type === AST_NODE_TYPES.Identifier &&\n node.callee.name === \"setTimeout\" &&\n node.arguments.length >= 2 &&\n isNonzeroNumericLiteral(node.arguments[1])\n );\n}\n\n/**\n * True when `node` is `new Promise((r) => setTimeout(r, n))` — including the\n * block-bodied `{ setTimeout(r, n); }` spelling.\n */\nfunction isPromiseSleep(node: TSESTree.NewExpression): boolean {\n if (node.callee.type !== AST_NODE_TYPES.Identifier || node.callee.name !== \"Promise\") {\n return false;\n }\n const executor = node.arguments[0];\n if (\n executor?.type !== AST_NODE_TYPES.ArrowFunctionExpression &&\n executor?.type !== AST_NODE_TYPES.FunctionExpression\n ) {\n return false;\n }\n const body = executor.body;\n if (body.type !== AST_NODE_TYPES.BlockStatement) {\n return isTimedSetTimeout(body);\n }\n return body.body.some(\n (stmt) => stmt.type === AST_NODE_TYPES.ExpressionStatement && isTimedSetTimeout(stmt.expression),\n );\n}\n\n/** True when `node` is `sleep(n)` / `delay(n)` with a nonzero numeric literal. */\nfunction isHelperSleep(node: TSESTree.CallExpression): boolean {\n return (\n node.callee.type === AST_NODE_TYPES.Identifier &&\n SLEEP_HELPERS.has(node.callee.name) &&\n node.arguments.length >= 1 &&\n isNonzeroNumericLiteral(node.arguments[0])\n );\n}\n\n/** The nearest enclosing function of `node`, skipping the `new Promise` executor. */\nfunction nearestEnclosingFunction(node: TSESTree.Node): TSESTree.Node | null {\n for (let current = node.parent; current != null; current = current.parent) {\n if (!FUNCTION_TYPES.has(current.type)) {\n continue;\n }\n const grandparent = current.parent;\n const isPromiseExecutor =\n grandparent?.type === AST_NODE_TYPES.NewExpression && isPromiseSleep(grandparent);\n if (!isPromiseExecutor) {\n return current;\n }\n }\n return null;\n}\n\n/** The base callee name of a call, unwrapping `.only` / `.skip` / `.each` chains. */\nfunction testCallerName(callee: TSESTree.Node): string | null {\n if (callee.type === AST_NODE_TYPES.Identifier) {\n return callee.name;\n }\n if (callee.type === AST_NODE_TYPES.MemberExpression) {\n return testCallerName(callee.object);\n }\n if (callee.type === AST_NODE_TYPES.CallExpression) {\n return testCallerName(callee.callee);\n }\n if (callee.type === AST_NODE_TYPES.TaggedTemplateExpression) {\n return testCallerName(callee.tag);\n }\n return null;\n}\n\n/** True when `fn` is the callback argument of an `it`/`test`/per-test-hook call. */\nfunction isTestBody(fn: TSESTree.Node): boolean {\n const call = fn.parent;\n if (\n call?.type !== AST_NODE_TYPES.CallExpression ||\n !call.arguments.some((argument) => argument === fn)\n ) {\n return false;\n }\n const name = testCallerName(call.callee);\n return name !== null && TEST_CALLERS.has(name);\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-sleep-in-test-body\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Disallow a fixed timed sleep directly in a test body; it flakes under CI load. Synchronize on the signal or use fake timers.\",\n },\n schema: [],\n messages: {\n noSleepInTestBody:\n \"A fixed sleep in a test body asserts on wall-clock time and flakes under CI load. Await the promise the code returns, or drive time with `vi.useFakeTimers()` + `await vi.advanceTimersByTimeAsync(ms)`.\",\n },\n },\n defaultOptions: [],\n create(context) {\n if (!isTestFile(context.filename)) {\n return {};\n }\n const report = (node: TSESTree.Node): void => {\n const enclosing = nearestEnclosingFunction(node);\n if (enclosing === null || !isTestBody(enclosing)) {\n return;\n }\n context.report({ node, messageId: \"noSleepInTestBody\" });\n };\n return {\n NewExpression(node: TSESTree.NewExpression): void {\n if (isPromiseSleep(node)) {\n report(node);\n }\n },\n CallExpression(node: TSESTree.CallExpression): void {\n if (isHelperSleep(node)) {\n report(node);\n }\n },\n };\n },\n});\n","/**\n * @fileoverview TS port of SARJ011 (`prefer-constant-time-secret-compare`).\n * Comparing secrets (bearer tokens, signatures, HMACs, password hashes, API\n * keys) with `===`/`!==`/`==`/`!=` is timing-attack-prone: the engine's string\n * comparison short-circuits on the first differing byte, so the time it takes to\n * return `false` leaks how many leading bytes the attacker got right. Over enough\n * requests that is enough to recover the secret byte by byte.\n *\n * On Cloudflare Workers the correct primitive is `crypto.subtle.timingSafeEqual`\n * (over equal-length `ArrayBuffer`s — SHA-256 both sides first so the lengths\n * match and no length is leaked), or a hand-rolled XOR-accumulate loop over the\n * digests. Node exposes `crypto.timingSafeEqual`. `===` is never the answer.\n *\n * The secret-name predicate is shared with `no-secret-in-log` and then narrowed\n * (`isAuthSecretName`): a timing-attack surface is only an *authenticator* whose\n * bytes gate access, so the following are deliberately NOT flagged even though\n * they are secret-shaped by name:\n *\n * - Category / handle metadata: `tokenType === \"Bearer\"`, `tokenName`,\n * `credentialKind`, `apiKeyId` — a discriminator or row id, not the credential.\n * - Boolean flags: `isTokenValid`, `hasSecret` — a decision, not the bytes.\n * - Integrity-only content hashes: `contentHash === previousHash` — a change\n * detector, not an authenticator. `passwordHash` / `tokenHash` still fire\n * because those DO gate access.\n * - Presence checks and literal sentinels: `token === null`, `secret !== undefined`,\n * `token.length === 0`, `tokenType === \"bearer\"`, `x === TOKEN_TYPE_SYSTEM` —\n * comparing against a compile-time constant or a null check reveals nothing\n * about a runtime secret.\n * - Marker values whose camelCase name merely ENDS in a secret word:\n * `queryFn === skipToken`. A `skipToken` is a unique `Symbol` compared by\n * identity, so there are no bytes to walk and no timing to leak. This was the\n * rule's entire false-positive surface on the 2026-07 corpus sweep — 10 hits\n * out of 10, all `skipToken`; see `SENTINEL_PREFIX_RE`.\n * - Anything in a test file: no attacker measures a test's clock, and fixture\n * assertions (`expect(res.apiKey === \"known\")`) are not an auth path.\n *\n * References:\n * - https://developers.cloudflare.com/workers/runtime-apis/web-crypto/#timingsafeequal\n * - https://codahale.com/a-lesson-in-timing-attacks/\n */\n\nimport { AST_NODE_TYPES, ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\nimport { isAuthSecretName } from \"./_secret_names.js\";\nimport { isTestFile } from \"./_paths.js\";\n\ntype MessageIds = \"preferConstantTimeSecretCompare\";\ntype Options = readonly [];\n\nconst EQUALITY_OPERATORS: ReadonlySet<string> = new Set([\"===\", \"!==\", \"==\", \"!=\"]);\n\n/**\n * Identifiers that are compile-time sentinels rather than runtime values.\n * `undefined`/`NaN` make the comparison a presence check; an ALL-CAPS name is a\n * named constant or enum member (`TOKEN_TYPE_SYSTEM`), and timing a compare\n * against a value the attacker already knows leaks nothing.\n */\nconst SENTINEL_IDENTIFIERS: ReadonlySet<string> = new Set([\"undefined\", \"NaN\"]);\n\n/**\n * Words that mark a secret-SHAPED name as a placeholder rather than a live\n * credential: `TOKEN_SENTINEL`, `EMPTY_SECRET`, `PLACEHOLDER_API_KEY`. Comparing\n * against one of these is a presence check, not an authentication check, so it\n * stays excluded even though the secret-name heuristics match it.\n */\nconst SENTINEL_WORDS = /(^|_)(SENTINEL|EMPTY|NONE|NULL|UNSET|MISSING|PLACEHOLDER|DUMMY|FAKE|EXAMPLE)(_|$)/;\n\n/**\n * The camelCase spelling of the same idea: a marker VALUE whose name happens to\n * end in a secret-shaped word. A library exports `skipToken` as a unique\n * `Symbol` and callers test identity against it — there are no bytes to compare\n * and nothing an attacker could learn from the timing.\n *\n * Corpus evidence (2220 files across zod / TanStack Query / react-router / swr /\n * zustand, 2026-07): the rule fired 10 times and ALL TEN were\n * `options.queryFn === skipToken`, e.g.\n * `query/packages/query-core/src/queryClient.ts:604`. The list is deliberately\n * limited to modifiers that can only introduce a marker; `default`/`invalid`\n * are excluded because `defaultApiKey` really can hold a live credential.\n */\nconst SENTINEL_PREFIX_RE =\n /^(skip|sentinel|empty|none|missing|unset|placeholder|dummy|fake|example|noop)[A-Z]/;\nconst AST_NODE_TYPE_RE = /^(?:TS|JSX)?[A-Z][A-Za-z]*(?:Signature|Keyword|Expression|Declaration|Element|Literal|Identifier)$/;\n\n/**\n * True when every cased character is upper-case and at least one letter exists —\n * AND the name is not itself secret-shaped.\n *\n * The ALL-CAPS carve-out exists for public named constants (`TOKEN_TYPE_SYSTEM`).\n * But environment secrets are conventionally SCREAMING_SNAKE too\n * (`ADMIN_TOKEN`, `ASHBY_API_KEY`, `SLACK_SIGNING_SECRET`), so an unqualified\n * ALL-CAPS bail-out made this rule blind to essentially every real secret\n * compare: `env.ADMIN_TOKEN === supplied` was silent while the camelCase\n * `env.adminToken === supplied` fired. That is the exact comparison this rule\n * exists to catch. Defer to the shared secret-name heuristics first.\n */\nfunction isConstantReference(identifier: string): boolean {\n if (AST_NODE_TYPE_RE.test(identifier)) return true;\n if (isAuthSecretName(identifier) && !SENTINEL_WORDS.test(identifier)) return false;\n return identifier === identifier.toUpperCase() && /[A-Za-z]/.test(identifier);\n}\n\n/**\n * True when this operand makes the comparison a non-timing-attack surface:\n * any literal (string / number / boolean / null / regex), a template literal\n * with no substitutions, `undefined`/`NaN`, or an ALL-CAPS constant reference.\n */\nfunction isExcludedOperand(node: TSESTree.Node): boolean {\n switch (node.type) {\n case AST_NODE_TYPES.Literal:\n return true;\n case AST_NODE_TYPES.TemplateLiteral:\n return node.expressions.length === 0;\n case AST_NODE_TYPES.Identifier:\n return (\n SENTINEL_IDENTIFIERS.has(node.name) ||\n SENTINEL_PREFIX_RE.test(node.name) ||\n isConstantReference(node.name)\n );\n case AST_NODE_TYPES.MemberExpression:\n return (\n !node.computed &&\n node.property.type === AST_NODE_TYPES.Identifier &&\n (SENTINEL_PREFIX_RE.test(node.property.name) ||\n isConstantReference(node.property.name))\n );\n default:\n return false;\n }\n}\n\n/** The identifier a plain operand denotes, or null for anything else. */\nfunction operandName(node: TSESTree.Node): string | null {\n if (node.type === AST_NODE_TYPES.Identifier) {\n return node.name;\n }\n if (\n node.type === AST_NODE_TYPES.MemberExpression &&\n !node.computed &&\n node.property.type === AST_NODE_TYPES.Identifier\n ) {\n return node.property.name;\n }\n return null;\n}\n\n/**\n * True when the operand names an authenticator worth a constant-time compare.\n *\n * A template literal counts when it interpolates one: `header === \\`Bearer\n * ${adminToken}\\`` is the canonical Workers auth check and is exactly as\n * timing-leaky as comparing the bare token, since the constant prefix only makes\n * the first bytes free.\n */\nfunction isSecretOperand(node: TSESTree.Node): boolean {\n if (node.type === AST_NODE_TYPES.TemplateLiteral) {\n return node.expressions.some((expression) => isSecretOperand(expression));\n }\n const name = operandName(node);\n return name !== null && isAuthSecretName(name);\n}\n\n/** The secret identifier this comparison exposes, for the diagnostic message. */\nfunction secretNameOf(node: TSESTree.Node): string | null {\n if (node.type === AST_NODE_TYPES.TemplateLiteral) {\n for (const expression of node.expressions) {\n const nested = secretNameOf(expression);\n if (nested !== null) {\n return nested;\n }\n }\n return null;\n }\n return operandName(node);\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"prefer-constant-time-secret-compare\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Disallow `===`/`!==` on a secret-like value; short-circuiting comparison leaks the secret through timing. Use a constant-time compare.\",\n },\n schema: [],\n messages: {\n preferConstantTimeSecretCompare:\n \"`{{operator}}` on secret `{{name}}` short-circuits on the first differing byte and leaks it through timing. Compare constant-time instead (`crypto.subtle.timingSafeEqual` over equal-length SHA-256 digests).\",\n },\n },\n defaultOptions: [],\n create(context) {\n if (isTestFile(context.filename)) {\n return {};\n }\n return {\n BinaryExpression(node: TSESTree.BinaryExpression): void {\n if (!EQUALITY_OPERATORS.has(node.operator)) {\n return;\n }\n const { left, right } = node;\n if (isExcludedOperand(left) || isExcludedOperand(right)) {\n return;\n }\n const secret = [left, right].find((operand) => isSecretOperand(operand));\n if (secret === undefined) {\n return;\n }\n context.report({\n node,\n messageId: \"preferConstantTimeSecretCompare\",\n data: { operator: node.operator, name: secretNameOf(secret) ?? \"\" },\n });\n },\n };\n },\n});\n","/**\n * @fileoverview TS port of SARJ018 (`store-insert-requires-on-conflict`).\n * An embedded `INSERT INTO ... VALUES/SELECT` with no conflict clause must\n * become an idempotent upsert.\n *\n * Store write paths run under retries, races, and replays — and on Cloudflare\n * Workers that is the *normal* case, not the exception: a cron trigger can fire\n * twice, a Queue message is redelivered on any handler throw, and a `waitUntil`\n * task can be retried after the response is already sent. A bare `INSERT` under\n * redelivery either duplicates rows (a second Slack post, a second email, a\n * double-counted referral) or throws on a unique-constraint violation, which\n * fails the handler, which triggers another redelivery. Every store write should\n * be `INSERT ... ON CONFLICT ... DO UPDATE` / `DO NOTHING` so replay is a no-op.\n *\n * The rule reads every statically-resolvable SQL string in the file — plain\n * literals, template literals (`${...}` becomes a `?` parameter marker), `+`\n * concatenations, and `[...].join(\" \")` fragment arrays — and flags one that\n * contains a genuine `INSERT INTO ... VALUES` / `... SELECT` write with no\n * conflict handling. SQL string-literal values and `--` / comment bodies are\n * neutralized first, so an `ON CONFLICT` living inside a quoted value never\n * excuses a bare insert, a `--` inside a value never eats a real clause, and\n * commented-out keywords neither trigger nor excuse a finding.\n *\n * Deliberately NOT flagged:\n * - SQLite/D1's own idempotent insert forms: `INSERT OR IGNORE INTO ...` and\n * `INSERT OR REPLACE INTO ...` already survive replay.\n * - MySQL's `ON DUPLICATE KEY UPDATE`, the same contract under another name.\n * - Pure reads, DDL, and `RETURNING`-only tails.\n * - Test files. A fresh in-memory D1 has nothing to conflict with, so fixture\n * seeding legitimately uses a bare `INSERT`; flagging it would train people to\n * ignore the rule.\n *\n * For a deliberate append-only write (an event/audit log where duplicates are\n * the point) disable with an inline `eslint-disable-next-line` and a reason.\n */\n\nimport { ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\nimport { isTestFile } from \"./_paths.js\";\nimport { createSqlListener } from \"./_sql.js\";\n\ntype MessageIds = \"storeInsertRequiresOnConflict\";\ntype Options = readonly [];\n\n/**\n * A real insert *write*: the keyword, a table identifier, an optional column\n * list, then `VALUES` / `SELECT` / `DEFAULT VALUES` with nothing in between.\n * Requiring that exact adjacency is what keeps English prose out — \"failed to\n * insert into the queue: values were rejected\" has words between the table and\n * the verb, so it does not match. The optional `OR <action>` clause is SQLite's\n * conflict resolution; it is matched here so `INSERT OR IGNORE INTO` is\n * recognised as an insert at all, then excused below.\n */\nconst INSERT_WRITE =\n /\\bINSERT\\s+(?:OR\\s+\\w+\\s+)?INTO\\s+[\\w.\"'`?$:@-]+\\s*(?:\\([^)]*\\)\\s*)?(?:VALUES|SELECT|DEFAULT\\s+VALUES)\\b/i;\n\n/** Conflict handling that makes the write replay-safe. */\nconst CONFLICT_HANDLED = /\\bON\\s+CONFLICT\\b|\\bON\\s+DUPLICATE\\s+KEY\\b|\\bINSERT\\s+OR\\s+(?:IGNORE|REPLACE)\\b/i;\n\n/**\n * Cheap substring gate. Noise-stripping only ever blanks characters to spaces,\n * so it can never introduce a keyword the raw text lacks — a file with no\n * `insert` at all can never produce a finding, and most files in a repo sweep\n * are not SQL-bearing.\n */\nconst INSERT_GATE = /insert/i;\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"store-insert-requires-on-conflict\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Require an embedded SQL INSERT to carry ON CONFLICT; store writes replay under cron re-runs and queue redelivery and must be idempotent upserts.\",\n },\n schema: [],\n messages: {\n storeInsertRequiresOnConflict:\n \"This INSERT is not replay-safe: a cron re-run or queue redelivery duplicates the row (or fails the handler on a unique-constraint violation). Add `ON CONFLICT (...) DO UPDATE` / `DO NOTHING` (or `INSERT OR IGNORE`).\",\n },\n },\n defaultOptions: [],\n create(context) {\n if (isTestFile(context.filename) || !INSERT_GATE.test(context.sourceCode.text)) {\n return {};\n }\n return createSqlListener((sql: string, node: TSESTree.Node): void => {\n if (!INSERT_WRITE.test(sql) || CONFLICT_HANDLED.test(sql)) {\n return;\n }\n context.report({ node, messageId: \"storeInsertRequiresOnConflict\" });\n });\n },\n});\n","/**\n * @fileoverview Disallow building a SQL statement out of runtime values.\n *\n * `db.prepare(sql)` takes a STATIC statement plus `?` / `$1` placeholders bound\n * through `.bind(...)`. Interpolating a value into the statement text bypasses\n * the binding layer completely: that is SQL injection, and it also defeats the\n * driver's prepared-statement cache, because every distinct value produces a\n * distinct statement to compile. The shape is identical across Cloudflare D1,\n * better-sqlite3 and node-postgres, so the rule is driver-agnostic.\n *\n * WHAT IT CATCHES\n * db.prepare(`select * from users where id = '${userId}'`)\n * db.prepare(\"select * from users where id = '\" + userId + \"'\")\n * db.exec(`delete from sessions where token = '${token}'`)\n *\n * NOT FLAGGED\n * - `${CONSTANT_CASE}` fragments. A module-level constant — the column-list\n * constants several repos keep (`${CANDIDATE_COLS}`, `${TABLES.USERS}`) —\n * is a compile-time value, not user input. Anything starting lowercase is\n * treated as runtime data.\n * - A template literal with no interpolations at all.\n * - Tagged templates (`` sql`select ... ${id}` ``). A tag function receives the\n * static strings and the values separately and is the parameterising\n * mechanism, not a bypass of it.\n * - `.prepare()` on something that is not a database. Requiring an\n * interpolated runtime value keeps this rare in practice.\n * - A statement text that contains no SQL data-statement keyword. `exec` and\n * `query` are not SQL-specific names: a 5-repo sweep of real TypeScript\n * (zod / TanStack Query / react-router / swr / zustand, 2,186 files) found\n * 4/4 hits were `child_process.exec` building a SHELL command line —\n * an `open <url>` at\n * react-router/integration/helpers/playwright-fixture.ts:230 and an\n * `npm view <pkg>@<version> version` at\n * react-router/scripts/changes/publish.ts:111. Requiring the statement to\n * actually read as SQL (`SELECT` / `INSERT INTO` / `UPDATE` / `DELETE FROM`\n * / DDL / `PRAGMA` / a CTE head) takes that class to zero without touching\n * any real injection: an interpolated statement with no SQL verb in it was\n * never a SQL statement.\n *\n * CONFIGURATION\n * `methods` is the list of statement-taking method names to inspect. Extend it\n * for a driver that names things differently:\n *\n * \"@sarj/no-dynamic-sql\": [\"error\", { \"methods\": [\"prepare\", \"exec\", \"raw\"] }]\n *\n * Supplying `methods` REPLACES the defaults.\n */\n\nimport { AST_NODE_TYPES, ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\nimport { stripSqlNoise } from \"./_sql.js\";\n\ntype MessageIds = \"dynamicSql\";\n\nexport interface RuleOptions {\n /** Statement-taking method names to inspect. Replaces the defaults. */\n readonly methods?: readonly string[];\n}\n\ntype Options = readonly [RuleOptions?];\n\nconst DEFAULT_METHODS: readonly string[] = [\"prepare\", \"exec\", \"query\"];\n\n/** A module-level constant fragment: `CANDIDATE_COLS`, `TABLE_NAME`. */\nconst CONSTANT_CASE_RE = /^[A-Z][A-Z0-9_]*$/;\n\n/**\n * True when an interpolated expression is a compile-time SQL fragment rather\n * than runtime data: a CONSTANT_CASE identifier, a member access whose final\n * property is CONSTANT_CASE (`TABLES.USERS`), or a string literal.\n */\nfunction isStaticFragment(expression: TSESTree.Expression): boolean {\n if (expression.type === AST_NODE_TYPES.Identifier) {\n return CONSTANT_CASE_RE.test(expression.name);\n }\n if (\n expression.type === AST_NODE_TYPES.MemberExpression &&\n !expression.computed &&\n expression.property.type === AST_NODE_TYPES.Identifier\n ) {\n return CONSTANT_CASE_RE.test(expression.property.name);\n }\n if (expression.type === AST_NODE_TYPES.Literal) {\n return typeof expression.value === \"string\";\n }\n return false;\n}\n\n/** The interpolated runtime expressions in a template literal. */\nfunction runtimeInterpolations(\n template: TSESTree.TemplateLiteral,\n): TSESTree.Expression[] {\n return template.expressions.filter(\n (expression) => !isStaticFragment(expression),\n );\n}\n\n/**\n * Flatten a `+` chain into its operands. `\"a\" + b + \"c\"` yields three nodes so\n * each can be judged independently.\n */\nfunction concatOperands(node: TSESTree.Expression): TSESTree.Expression[] {\n if (node.type === AST_NODE_TYPES.BinaryExpression && node.operator === \"+\") {\n return [...concatOperands(node.left), ...concatOperands(node.right)];\n }\n return [node];\n}\n\n/**\n * The runtime operands of a string-concatenation chain, or an empty array when\n * the node is not a concatenation that mixes a literal with a runtime value.\n * Requiring at least one string literal is what distinguishes statement\n * building from ordinary arithmetic.\n */\nfunction runtimeConcatOperands(node: TSESTree.Node): TSESTree.Expression[] {\n if (node.type !== AST_NODE_TYPES.BinaryExpression || node.operator !== \"+\") {\n return [];\n }\n const operands = concatOperands(node);\n const hasStringLiteral = operands.some(\n (operand) =>\n operand.type === AST_NODE_TYPES.Literal &&\n typeof operand.value === \"string\",\n );\n if (!hasStringLiteral) {\n return [];\n }\n return operands.filter(\n (operand) =>\n operand.type !== AST_NODE_TYPES.Literal && !isStaticFragment(operand),\n );\n}\n\n/**\n * A SQL data/DDL statement keyword. `exec` and `query` are generic method names\n * — `child_process.exec` shares them — so the statement text itself has to prove\n * it is SQL before an interpolation into it can be an injection.\n */\nconst SQL_STATEMENT_RE =\n /\\b(?:select\\s|insert\\s+into\\b|insert\\s+or\\b|update\\s+\\w|delete\\s+from\\b|replace\\s+into\\b|merge\\s+into\\b|upsert\\s+into\\b|create\\s+(?:temp(?:orary)?\\s+)?(?:table|index|view|trigger|schema|database)\\b|alter\\s+table\\b|drop\\s+(?:table|index|view|trigger)\\b|truncate\\s+table\\b|pragma\\s+\\w|with\\s+\\w+\\s+as\\s*\\(|from\\s+\\w+\\s+where\\b)/i;\n\n/** The marker a non-static operand contributes to the reconstructed statement text. */\nconst RUNTIME_MARKER = \" ? \";\n\n/**\n * The statically known text of a statement argument, with every runtime operand\n * replaced by a placeholder. Enough to answer \"is this SQL?\" for both the\n * template-literal and the `+`-concatenation shape.\n */\nfunction staticStatementText(node: TSESTree.Node): string {\n if (node.type === AST_NODE_TYPES.TemplateLiteral) {\n return node.quasis.map((quasi) => quasi.value.cooked ?? quasi.value.raw).join(RUNTIME_MARKER);\n }\n if (node.type === AST_NODE_TYPES.Literal) {\n return typeof node.value === \"string\" ? node.value : RUNTIME_MARKER;\n }\n if (node.type === AST_NODE_TYPES.BinaryExpression && node.operator === \"+\") {\n return staticStatementText(node.left) + staticStatementText(node.right);\n }\n return RUNTIME_MARKER;\n}\n\n/** True when the statement argument reads as SQL rather than as a shell command line. */\nfunction looksLikeSql(node: TSESTree.Node): boolean {\n return SQL_STATEMENT_RE.test(stripSqlNoise(staticStatementText(node)));\n}\n\n/** The inspected method name of `receiver.method(...)`, or null. */\nfunction statementMethodName(\n node: TSESTree.CallExpression,\n methods: ReadonlySet<string>,\n): string | null {\n const callee = node.callee;\n if (\n callee.type !== AST_NODE_TYPES.MemberExpression ||\n callee.computed ||\n callee.property.type !== AST_NODE_TYPES.Identifier\n ) {\n return null;\n }\n const name = callee.property.name;\n return methods.has(name) ? name : null;\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-dynamic-sql\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Disallow interpolating or concatenating a runtime value into a SQL statement passed to `prepare`/`exec`/`query`; use a placeholder and bind the value.\",\n },\n schema: [\n {\n type: \"object\",\n properties: {\n methods: {\n type: \"array\",\n items: { type: \"string\" },\n description:\n \"Statement-taking method names to inspect. Replaces the defaults.\",\n },\n },\n additionalProperties: false,\n },\n ],\n messages: {\n dynamicSql:\n \"Runtime value built into a SQL statement passed to `{{method}}()`. Use a `?` placeholder and pass the value through `.bind(...)` so the driver parameterises it.\",\n },\n },\n defaultOptions: [{}],\n create(context, [options]) {\n const methods = new Set(options?.methods ?? DEFAULT_METHODS);\n\n return {\n CallExpression(node: TSESTree.CallExpression): void {\n const method = statementMethodName(node, methods);\n if (method === null) {\n return;\n }\n\n const statement = node.arguments[0];\n if (statement === undefined || !looksLikeSql(statement)) {\n return;\n }\n\n const offenders =\n statement.type === AST_NODE_TYPES.TemplateLiteral\n ? runtimeInterpolations(statement)\n : runtimeConcatOperands(statement);\n\n for (const offender of offenders) {\n context.report({\n node: offender,\n messageId: \"dynamicSql\",\n data: { method },\n });\n }\n },\n };\n },\n});\n","/**\n * @fileoverview Keep outbound HTTP behind a client module.\n *\n * A bare `fetch()` in a route handler, server action or component opts out of\n * whatever the codebase's client layer provides — retry/backoff, timeouts,\n * status handling, auth headers, structured log breadcrumbs. It is also the\n * shape that cannot be stubbed in a test without monkey-patching global\n * `fetch`, so the call site quietly becomes untestable.\n *\n * WHAT IT CATCHES\n * fetch(url) // bare global\n * globalThis.fetch(url) // explicit global receiver\n * window.fetch(url)\n *\n * NOT FLAGGED\n * - Files whose path matches one of the `allow` patterns. The defaults cover\n * the conventions we have seen in practice — a `clients/` directory, a\n * `*-client.ts` module, an `http-client.*` wrapper, an `api/` directory or\n * `api.ts` / `*-api.ts` module — plus test files and codemod fixtures.\n * - A method named `fetch` on some other receiver (`cache.fetch(k)`,\n * `queryClient.fetch()`): only the global is HTTP.\n * - A pre-signed upload/download URL transfer (`fetch(uploadUrl, ...)`,\n * `fetch(file.downloadUrl)`). Those URLs are one-off storage handoffs,\n * not calls to a first-party service API that belongs behind a client\n * wrapper.\n * - `new Request(...)` / `axios(...)` and friends. This rule is about the\n * global `fetch`, not about every HTTP library.\n *\n * CONFIGURATION\n * `allow` is a list of regular-expression sources matched against the absolute\n * filename, so a repo that keeps its client layer somewhere else can say so\n * rather than sprinkling disable comments:\n *\n * \"@sarj/no-raw-fetch-outside-clients\": [\"error\", {\n * \"allow\": [\"[\\\\\\\\/]lib[\\\\\\\\/]api[\\\\\\\\/]\", \"-gateway\\\\\\\\.ts$\"]\n * }]\n *\n * Supplying `allow` REPLACES the defaults, so include the test patterns if you\n * still want test files exempt.\n *\n * CORPUS SWEEP (2220 files, zod / TanStack Query / react-router / swr /\n * zustand, 2026-07): 96 raw hits. The defaults missed three path conventions\n * that ARE the client layer, so the reports landed on the very modules the rule\n * wants the `fetch` to live in:\n * - `api.ts` / `api/` / `*-api.ts` — 15 hits, e.g.\n * `query/examples/react/star-wars/src/api.ts:7`, a module that exists solely\n * to own `getFilm`/`getPerson` HTTP calls. `api` is the same convention\n * family as `clients/`, just the other common spelling.\n * - Hyphenated test basenames (`*-test.ts` / `*-spec.ts`) — the default list\n * only knew the dotted `*.test.ts` form, so react-router's entire suite\n * (which names files `single-fetch-test.ts`) was unprotected.\n * - `__testfixtures__/` — 8 hits from jscodeshift fixtures such as\n * `query/packages/query-codemods/src/v5/remove-overloads/__testfixtures__/bug-reports.input.tsx`,\n * which are input/output text for a codemod, not code that runs.\n */\n\nimport { ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\ntype MessageIds = \"rawFetch\";\n\nexport interface RuleOptions {\n /** Regular-expression sources matched against the filename. */\n readonly allow?: readonly string[];\n}\n\ntype Options = readonly [RuleOptions?];\n\n/**\n * Path shapes that legitimately own outbound HTTP, plus test files. Written as\n * regex sources (not globs) so they can express both path separators.\n */\nconst DEFAULT_ALLOW: readonly string[] = [\n \"[\\\\\\\\/]clients?[\\\\\\\\/]\",\n \"-client\\\\.[cm]?[jt]sx?$\",\n \"[\\\\\\\\/]http-client\\\\.[cm]?[jt]sx?$\",\n // The `api` spelling of the same client-layer convention: an `api/` directory,\n // a bare `api.ts`, or a `*-api.ts` / `*.api.ts` module.\n \"[\\\\\\\\/]api[\\\\\\\\/]\",\n \"[\\\\\\\\/]api\\\\.[cm]?[jt]sx?$\",\n \"[-.]api\\\\.[cm]?[jt]sx?$\",\n \"\\\\.test\\\\.\",\n \"\\\\.spec\\\\.\",\n // `*.test-d.ts` type tests, and react-router's `single-fetch-test.ts` spelling.\n \"\\\\.(test|spec)-d\\\\.[cm]?[jt]sx?$\",\n \"-(test|spec)\\\\.[cm]?[jt]sx?$\",\n \"[\\\\\\\\/]__tests__[\\\\\\\\/]\",\n \"[\\\\\\\\/]__mocks__[\\\\\\\\/]\",\n \"[\\\\\\\\/]tests?[\\\\\\\\/]\",\n // jscodeshift input/output fixtures — text a codemod transforms, not code.\n \"[\\\\\\\\/]__testfixtures__[\\\\\\\\/]\",\n];\n\n/** Receivers on which `.fetch()` is the global, not some cache/query API. */\nconst GLOBAL_RECEIVERS: ReadonlySet<string> = new Set([\n \"globalThis\",\n \"window\",\n \"self\",\n]);\n\nconst PRESIGNED_URL_NAME_RE = /(?:pre-?signed|signed|upload|download)Url$/i;\n\n/** True when `node` is a call to the global `fetch`. */\nfunction isGlobalFetchCall(node: TSESTree.CallExpression): boolean {\n const callee = node.callee;\n\n if (callee.type === \"Identifier\") {\n return callee.name === \"fetch\";\n }\n\n if (\n callee.type === \"MemberExpression\" &&\n !callee.computed &&\n callee.property.type === \"Identifier\" &&\n callee.property.name === \"fetch\" &&\n callee.object.type === \"Identifier\"\n ) {\n return GLOBAL_RECEIVERS.has(callee.object.name);\n }\n\n return false;\n}\n\nfunction identifierLikeName(node: TSESTree.Node): string | null {\n if (node.type === \"Identifier\") {\n return node.name;\n }\n if (\n node.type === \"MemberExpression\" &&\n !node.computed &&\n node.property.type === \"Identifier\"\n ) {\n return node.property.name;\n }\n return null;\n}\n\nfunction isPresignedUrlTransfer(node: TSESTree.CallExpression): boolean {\n const first = node.arguments[0];\n if (first === undefined) {\n return false;\n }\n const name = identifierLikeName(first);\n return name !== null && PRESIGNED_URL_NAME_RE.test(name);\n}\n\n/**\n * Compile the configured patterns once per file. An invalid pattern is skipped\n * rather than thrown: a bad `allow` entry should not take the whole lint run\n * down, and the rule failing closed (still reporting) is the safe direction.\n */\nfunction compile(patterns: readonly string[]): RegExp[] {\n const compiled: RegExp[] = [];\n for (const pattern of patterns) {\n try {\n compiled.push(new RegExp(pattern));\n } catch {\n // Skip the malformed entry; the remaining patterns still apply.\n }\n }\n return compiled;\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-raw-fetch-outside-clients\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Disallow calling the global `fetch` outside the client layer; route outbound HTTP through a client module that owns retry, timeout and status handling.\",\n },\n schema: [\n {\n type: \"object\",\n properties: {\n allow: {\n type: \"array\",\n items: { type: \"string\" },\n description:\n \"Regular-expression sources matched against the filename. Replaces the defaults.\",\n },\n },\n additionalProperties: false,\n },\n ],\n messages: {\n rawFetch:\n \"Raw `fetch()` outside a client module. Route the call through a client (e.g. `clients/*-client.ts`) so it inherits retry, timeout and status handling and stays stubbable in tests.\",\n },\n },\n defaultOptions: [{}],\n create(context, [options]) {\n const patterns = options?.allow ?? DEFAULT_ALLOW;\n const allowed = compile(patterns);\n const filename = context.filename;\n\n if (allowed.some((re) => re.test(filename))) {\n return {};\n }\n\n return {\n CallExpression(node: TSESTree.CallExpression): void {\n if (isPresignedUrlTransfer(node)) {\n return;\n }\n if (isGlobalFetchCall(node)) {\n context.report({ node, messageId: \"rawFetch\" });\n }\n },\n };\n },\n});\n","/**\n * @fileoverview Keep modules that are stateless by design free of private\n * storage.\n *\n * Some features are stateless deliberately: they derive everything they need\n * from reads against the systems of record (Slack, Linear, GitHub, a CRM) plus\n * markers in the artefacts they themselves produced. The reason is operational\n * — such a feature can be re-run and back-filled freely, whereas a private\n * table or key/value namespace immediately diverges from what a human can\n * actually see and audit in the system of record. Adding a store to one of\n * these modules silently deletes that property, and nothing else in the\n * toolchain notices.\n *\n * WHAT IT CATCHES, inside the configured modules only\n * db.prepare(sql) // a SQL statement\n * kv.put(key, value) // a key/value write\n * kv.getWithMetadata(key) // a key/value read\n *\n * OPT-IN BY DESIGN\n * `modules` defaults to an EMPTY list, which makes the rule a no-op. That is\n * deliberate: the method names alone (`put`, `prepare`) carry no type\n * information, so the rule is only meaningful — and only quiet enough to live\n * in a shared preset — when it is pointed at the specific directories a team\n * has declared stateless.\n *\n * \"@sarj/no-storage-in-stateless-modules\": [\"error\", {\n * \"modules\": [\"[\\\\\\\\/]engineer-digest[\\\\\\\\/]\", \"[\\\\\\\\/]digest[\\\\\\\\/]\"]\n * }]\n *\n * `modules` entries are regular-expression sources matched against the absolute\n * filename. `methods` overrides the storage method names if a driver names\n * things differently.\n *\n * NOT FLAGGED\n * - Anything outside the configured modules.\n * - `.put()` with fewer than two arguments — a one-argument `put` is more\n * often a builder or queue helper than a key/value write.\n *\n * If a feature genuinely cannot be expressed statelessly, that is a design\n * conversation, not a disable comment.\n */\n\nimport { AST_NODE_TYPES, ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\ntype MessageIds = \"storageInStatelessModule\";\n\nexport interface RuleOptions {\n /** Regex sources matched against the filename. Empty means the rule is off. */\n readonly modules?: readonly string[];\n /** Storage method names to flag. Replaces the defaults. */\n readonly methods?: readonly string[];\n}\n\ntype Options = readonly [RuleOptions?];\n\nconst DEFAULT_METHODS: readonly string[] = [\n \"prepare\",\n \"put\",\n \"getWithMetadata\",\n];\n\n/**\n * Minimum argument count before a method call counts as storage access. `put`\n * needs a key AND a value; a single-argument `put` is usually something else.\n * Methods absent from this map need one argument.\n */\nconst MIN_ARGUMENTS: ReadonlyMap<string, number> = new Map([[\"put\", 2]]);\n\n/** Compile regex sources, skipping malformed entries rather than throwing. */\nfunction compile(patterns: readonly string[]): RegExp[] {\n const compiled: RegExp[] = [];\n for (const pattern of patterns) {\n try {\n compiled.push(new RegExp(pattern));\n } catch {\n // Skip the malformed entry; the remaining patterns still apply.\n }\n }\n return compiled;\n}\n\n/** The flagged storage method name of `receiver.method(...)`, or null. */\nfunction storageMethodName(\n node: TSESTree.CallExpression,\n methods: ReadonlySet<string>,\n): string | null {\n const callee = node.callee;\n if (\n callee.type !== AST_NODE_TYPES.MemberExpression ||\n callee.computed ||\n callee.property.type !== AST_NODE_TYPES.Identifier\n ) {\n return null;\n }\n const name = callee.property.name;\n if (!methods.has(name)) {\n return null;\n }\n if (node.arguments.length < (MIN_ARGUMENTS.get(name) ?? 1)) {\n return null;\n }\n return name;\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-storage-in-stateless-modules\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Disallow SQL or key/value access inside modules a team has declared stateless; derive state from the systems of record instead. No-op until `modules` is configured.\",\n },\n schema: [\n {\n type: \"object\",\n properties: {\n modules: {\n type: \"array\",\n items: { type: \"string\" },\n description:\n \"Regex sources matched against the filename. Empty (the default) disables the rule.\",\n },\n methods: {\n type: \"array\",\n items: { type: \"string\" },\n description: \"Storage method names to flag. Replaces the defaults.\",\n },\n },\n additionalProperties: false,\n },\n ],\n messages: {\n storageInStatelessModule:\n \"`{{method}}()` reaches for private storage inside a module declared stateless. Derive the state from a read against the system of record, or from a marker in the artefact this feature already produces.\",\n },\n },\n defaultOptions: [{}],\n create(context, [options]) {\n const modules = options?.modules ?? [];\n if (modules.length === 0) {\n return {};\n }\n\n const scoped = compile(modules);\n if (!scoped.some((re) => re.test(context.filename))) {\n return {};\n }\n\n const methods = new Set(options?.methods ?? DEFAULT_METHODS);\n\n return {\n CallExpression(node: TSESTree.CallExpression): void {\n const method = storageMethodName(node, methods);\n if (method !== null) {\n context.report({\n node,\n messageId: \"storageInStatelessModule\",\n data: { method },\n });\n }\n },\n };\n },\n});\n","/**\n * @fileoverview Disallow `z.nativeEnum(...)` in zod schemas; use\n * `z.enum([\"a\", \"b\"])` and derive the type with `z.infer<typeof Schema>`.\n *\n * Rationale — this is the schema-layer sibling of the `no-enum` rule. Zod's\n * `nativeEnum` exists for exactly one purpose: to wrap a TypeScript `enum`,\n * which `no-enum` already bans (enums emit runtime code, have unintuitive\n * numeric defaults, and don't tree-shake). Reaching for `nativeEnum` is\n * therefore either (a) importing a banned construct through the back door, or\n * (b) wrapping an `as const` object, in which case `z.enum` over the value list\n * is the shorter, better-inferring form. Either way the target state is the\n * same string-literal union `no-enum` prescribes, so the two rules point at one\n * destination.\n *\n * Two shapes fire:\n *\n * 1. **`z.nativeEnum(x)`** — always. Autofixable ONLY when the argument is an\n * inline object literal (optionally `as const`) whose every value is a\n * string literal: that set is fully known at the call site, so the rewrite\n * to `z.enum([...])` is mechanical and value-preserving. A numeric member\n * (`{ A: 1 }`) is NOT fixed — `z.enum` accepts strings only, so rewriting\n * would silently change the accepted input. A spread, computed key, shorthand\n * property, method, or an empty object is not fixed either (the member set\n * isn't statically known, and `z.enum([])` is not a valid schema). An\n * identifier argument (`z.nativeEnum(Fruits)`) is reported without a fix:\n * inlining the values at the call site would duplicate the literal set that\n * the named object exists to own, so the correct edit is a human one.\n *\n * 2. **`z.enum(SomeTsEnum)`** — zod v4 lets `z.enum` take a TS enum directly,\n * which is `nativeEnum` under a friendlier name and re-opens the same hole.\n * Detected two ways: lexical resolution of the identifier to a\n * `TSEnumDeclaration` in the same file, and — when type information is\n * available — the resolved symbol carrying an enum flag, which also catches\n * an enum imported from another module. `z.enum(COLORS)` where `COLORS` is a\n * `readonly string[]` / `as const` array is the prescribed pattern and never\n * fires.\n *\n * False positives handled: a bare `nativeEnum(...)` call fires only when the\n * name is imported from a zod module, so a same-named local helper is not\n * flagged. Generated files (`*.gen.ts`, `**\\/generated/**`, `*.d.ts`, or a\n * `@generated` marker) opt out, matching `no-enum` — codegen from an OpenAPI\n * spec legitimately emits enums and the schemas that wrap them.\n *\n * TEST FILES ARE EXEMPT. Corpus sweep (2220 files across zod / TanStack Query /\n * react-router / swr / zustand, 2026-07): 32 raw hits, 32 of them in test files\n * and 100% false positives. A test that covers `z.nativeEnum` has to CALL\n * `z.nativeEnum` — `zod/packages/zod/src/v3/tests/nativeEnum.test.ts:12`\n * (`const fruitEnum = z.nativeEnum(Fruits)`) is not importing a banned construct\n * through the back door, it is the coverage for the construct. The same applies\n * to any consumer pinning the migration behaviour of a legacy enum schema, and\n * it mirrors the exemption `no-enum` already grants generated code: the rule\n * targets the DECISION to model a domain with an enum, and a fixture makes no\n * such decision.\n */\n\nimport {\n ESLintUtils,\n type TSESLint,\n type TSESTree,\n type ParserServicesWithTypeInformation,\n AST_NODE_TYPES,\n} from \"@typescript-eslint/utils\";\nimport * as ts from \"typescript\";\n\nimport { isTestFile } from \"./_paths.js\";\n\ntype MessageIds = \"nativeEnum\" | \"enumOfTsEnum\";\ntype Options = readonly [];\n\nconst IGNORE_PATTERNS: readonly RegExp[] = [\n /[\\\\/]generated[\\\\/]/,\n /\\.gen\\.tsx?$/,\n /\\.generated\\.tsx?$/,\n /\\.d\\.ts$/,\n];\n\nfunction isIgnoredFile(filename: string, sourceText: string): boolean {\n if (IGNORE_PATTERNS.some((re) => re.test(filename))) {\n return true;\n }\n return /@generated\\b/.test(sourceText.slice(0, 1024));\n}\n\n/** `zod`, `zod/v4`, `zod/mini`, `@hono/zod-openapi`, `@/lib/zod`, ... */\nfunction isZodModule(source: string): boolean {\n return /(^|[/@-])zod([/-]|$)/.test(source);\n}\n\n/** Unwraps `x as const` / `x satisfies T` / `(x)` down to the inner expression. */\nfunction unwrap(node: TSESTree.Expression): TSESTree.Expression {\n if (\n node.type === AST_NODE_TYPES.TSAsExpression ||\n node.type === AST_NODE_TYPES.TSSatisfiesExpression\n ) {\n return unwrap(node.expression);\n }\n return node;\n}\n\n/**\n * The string values of an object literal whose every property is a plain\n * `key: \"literal\"` pair, as raw source text (so quote style survives the fix).\n * Returns null when any member is a spread, a computed key, a method, a\n * shorthand, or a non-string value — those sets are not statically rewritable.\n */\nfunction stringValueTexts(\n node: TSESTree.ObjectExpression,\n sourceCode: Readonly<TSESLint.SourceCode>,\n): string[] | null {\n const texts: string[] = [];\n for (const prop of node.properties) {\n if (prop.type !== AST_NODE_TYPES.Property) {\n return null;\n }\n if (prop.computed || prop.shorthand || prop.method || prop.kind !== \"init\") {\n return null;\n }\n const value = prop.value;\n if (\n value.type !== AST_NODE_TYPES.Literal ||\n typeof value.value !== \"string\"\n ) {\n return null;\n }\n const text = sourceCode.getText(value);\n if (!texts.includes(text)) {\n texts.push(text);\n }\n }\n return texts.length > 0 ? texts : null;\n}\n\n/** Resolves an identifier to a `TSEnumDeclaration` declared in this file. */\nfunction resolvesToLocalEnum(\n node: TSESTree.Identifier,\n scope: TSESLint.Scope.Scope,\n): boolean {\n let current: TSESLint.Scope.Scope | null = scope;\n while (current !== null) {\n const variable = current.variables.find((v) => v.name === node.name);\n if (variable !== undefined) {\n return variable.defs.some(\n (def) => def.node.type === AST_NODE_TYPES.TSEnumDeclaration,\n );\n }\n current = current.upper;\n }\n return false;\n}\n\nconst ENUM_SYMBOL_FLAGS =\n ts.SymbolFlags.RegularEnum | ts.SymbolFlags.ConstEnum | ts.SymbolFlags.Enum;\n\n/**\n * Whether the identifier's resolved symbol is a TypeScript enum, following\n * import aliases. Catches `import { Status } from \"./types\"; z.enum(Status)`,\n * which lexical resolution alone cannot see.\n */\nfunction resolvesToImportedEnum(\n node: TSESTree.Identifier,\n services: ParserServicesWithTypeInformation,\n): boolean {\n const checker = services.program.getTypeChecker();\n const tsNode = services.esTreeNodeToTSNodeMap.get(node);\n let symbol = checker.getSymbolAtLocation(tsNode);\n if (symbol === undefined) {\n return false;\n }\n if ((symbol.flags & ts.SymbolFlags.Alias) !== 0) {\n symbol = checker.getAliasedSymbol(symbol);\n }\n return (symbol.flags & ENUM_SYMBOL_FLAGS) !== 0;\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-zod-native-enum\",\n meta: {\n type: \"suggestion\",\n fixable: \"code\",\n docs: {\n description:\n \"Disallow `z.nativeEnum()` (and `z.enum()` over a TypeScript enum); use `z.enum([\\\"a\\\", \\\"b\\\"])` with a string-literal union instead.\",\n },\n schema: [],\n messages: {\n nativeEnum:\n '`z.nativeEnum()` exists to wrap a TypeScript `enum`, which `no-enum` bans. Use `z.enum([\"a\", \"b\"])` and derive the union with `z.infer<typeof Schema>`.',\n enumOfTsEnum:\n '`z.enum()` is being passed the TypeScript enum `{{name}}`, which `no-enum` bans. Pass a string-literal array instead: `z.enum([\"a\", \"b\"])`.',\n },\n },\n defaultOptions: [],\n create(context) {\n const sourceCode = context.sourceCode;\n if (isIgnoredFile(context.filename, sourceCode.getText())) {\n return {};\n }\n // A test that covers `z.nativeEnum` must call it; see @fileoverview.\n if (isTestFile(context.filename)) {\n return {};\n }\n\n let services: ParserServicesWithTypeInformation | null;\n try {\n services = ESLintUtils.getParserServices(context);\n } catch {\n services = null;\n }\n\n /** Local names imported from a zod module, e.g. `nativeEnum`, `enum_`. */\n const zodImportedNames = new Map<string, string>();\n\n function isZodMemberCall(node: TSESTree.CallExpression, api: string): boolean {\n const callee = node.callee;\n if (\n callee.type === AST_NODE_TYPES.MemberExpression &&\n !callee.computed &&\n callee.property.type === AST_NODE_TYPES.Identifier\n ) {\n return callee.property.name === api;\n }\n if (callee.type === AST_NODE_TYPES.Identifier) {\n return zodImportedNames.get(callee.name) === api;\n }\n return false;\n }\n\n function buildFix(\n node: TSESTree.CallExpression,\n ): TSESLint.ReportFixFunction | null {\n // A bare `nativeEnum(...)` imported from zod cannot be renamed in place —\n // `enum` is a reserved word, so there is no bare callee to rewrite to.\n // Rewriting only the argument would leave a `nativeEnum` call taking an\n // array, so this shape is report-only.\n const callee = node.callee;\n if (\n callee.type !== AST_NODE_TYPES.MemberExpression ||\n callee.property.type !== AST_NODE_TYPES.Identifier\n ) {\n return null;\n }\n const arg = node.arguments[0];\n if (\n arg === undefined ||\n node.arguments.length !== 1 ||\n arg.type === AST_NODE_TYPES.SpreadElement\n ) {\n return null;\n }\n const inner = unwrap(arg);\n if (inner.type !== AST_NODE_TYPES.ObjectExpression) {\n return null;\n }\n const values = stringValueTexts(inner, sourceCode);\n if (values === null) {\n return null;\n }\n const property = callee.property;\n const replacementArg = `[${values.join(\", \")}]`;\n return (fixer) => [\n fixer.replaceText(property, \"enum\"),\n fixer.replaceText(arg, replacementArg),\n ];\n }\n\n return {\n ImportDeclaration(node: TSESTree.ImportDeclaration): void {\n if (!isZodModule(node.source.value)) {\n return;\n }\n for (const spec of node.specifiers) {\n if (\n spec.type === AST_NODE_TYPES.ImportSpecifier &&\n spec.imported.type === AST_NODE_TYPES.Identifier\n ) {\n zodImportedNames.set(spec.local.name, spec.imported.name);\n }\n }\n },\n\n CallExpression(node: TSESTree.CallExpression): void {\n if (isZodMemberCall(node, \"nativeEnum\")) {\n const fix = buildFix(node);\n context.report({\n node,\n messageId: \"nativeEnum\",\n ...(fix === null ? {} : { fix }),\n });\n return;\n }\n\n if (!isZodMemberCall(node, \"enum\")) {\n return;\n }\n const arg = node.arguments[0];\n if (arg === undefined || arg.type !== AST_NODE_TYPES.Identifier) {\n return;\n }\n const isEnum =\n resolvesToLocalEnum(arg, sourceCode.getScope(arg)) ||\n (services !== null && resolvesToImportedEnum(arg, services));\n if (isEnum) {\n context.report({\n node,\n messageId: \"enumOfTsEnum\",\n data: { name: arg.name },\n });\n }\n },\n };\n },\n});\n","/**\n * @fileoverview Flag a literal-only constant collection (or regex) declared\n * INSIDE a function body that should be hoisted to module scope.\n *\n * Rationale — this is the single most frequent recurring review comment in the\n * mined PR corpus (~37 distinct PRs, TypeScript and Python alike): a lookup\n * table, allow-list, `Set` membership test or validation regex written at the\n * top of a function, rebuilt on every single call. Three costs, in increasing\n * order of severity:\n *\n * 1. Allocation — every call re-walks the literal and re-allocates the array /\n * object / Set / Map. In a hot path or a React render this is pure waste.\n * 2. Identity churn — a fresh object every call means a fresh reference every\n * render, which silently defeats `useMemo`/`useEffect` dependency arrays and\n * `React.memo` on anything the value is threaded into.\n * 3. Discoverability — a domain constant buried in a function body is invisible\n * to the next reader and cannot be exported, tested, or reused, so it gets\n * duplicated in the next function that needs it. That is the defect class:\n * the duplicated copies drift apart.\n *\n * This rule is deliberately NOT a duplicate of `unicorn/consistent-function-scoping`,\n * which is enabled at error in the shipped strict config. That rule reports\n * nested FUNCTION declarations that reference nothing from their enclosing\n * scope; its entire visitor surface is the function node types and it never\n * looks at a `VariableDeclarator` whose init is an array, object, `new Set`,\n * `new Map` or a regex. The two rules are disjoint by construction.\n *\n * What fires — a `const` binding inside a function whose initializer is one of:\n *\n * - an array literal, an object literal, `new Set([...])`, `new Map([...])`,\n * or `Object.freeze(...)` around any of those, with at least `minElements`\n * entries and where EVERY leaf is a literal; or\n * - a regular-expression literal without the `g`/`y` flags.\n *\n * \"Every leaf is a literal\" means string/number/boolean/null, an unsigned or\n * negated numeric literal, a template literal with no interpolation, or a\n * nested array/object of the same — no identifiers, no calls, no member\n * expressions, no spreads, no computed non-literal keys, no shorthand\n * properties. That single condition is what makes the hoist provably safe and\n * is also what eliminates the largest false-positive class outright.\n *\n * False positives handled explicitly:\n *\n * - **Closes over a parameter or an outer binding.** `new Map([[userId, 1]])`\n * or `[prefix + \"-a\"]` contains an identifier / a computed value, so it is\n * not literal-only and never fires. Hoisting those would be a compile error\n * or a behaviour change, so the literal-only gate is load-bearing, not a\n * stylistic preference.\n * - **Deliberately fresh per call because someone mutates it.** Every\n * reference to the binding must be a provably non-mutating read: a member\n * read, a call to a known non-mutating method, `for…of` iteration, or a\n * spread into a NEW literal or argument list (which copies). A call to\n * `.push`/`.set`/`.add`/`.sort`/`.splice`/…, an index or property\n * assignment, a `delete`, or an `X++` bails immediately.\n * - **Escaping value the CALLER may mutate.** Returning the binding, passing\n * it bare as an argument, aliasing it into another variable, or embedding it\n * in a returned object/array all bail. Once the value leaves the function\n * this rule cannot see what happens to it, and a shared hoisted array that a\n * caller mutates is a genuine cross-call data-corruption bug — exactly the\n * kind of defect a lint rule must not introduce. Reads from an escaping\n * CLOSURE are fine and still fire: a nested arrow that only reads the\n * constant is unaffected by hoisting.\n * - **Tiny literals where hoisting hurts readability.** A one- or two-element\n * literal reads better next to its use, so `minElements` (default 3) sets\n * the floor and can be raised per repo.\n * - **Stateful regexes.** A `/…/g` or `/…/y` regex carries `lastIndex` across\n * calls to `.test()`/`.exec()`. Hoisting one changes behaviour — the second\n * call resumes mid-string — so global and sticky regexes are never reported\n * even though they are the most expensive to recompile.\n * - **Test files.** Fixture tables inside a test body are local by design and\n * hoisting them separates the data from the assertion that explains it.\n * Exempt by default via `ignoreTestFiles`.\n * - **Generated files** (`*.gen.ts`, `**\\/generated/**`, `*.d.ts`, or a\n * `@generated` marker) opt out, matching `no-enum` and `no-zod-native-enum`.\n *\n * There is no autofix. Hoisting has to pick an insertion point and may collide\n * with an existing module-scope name; both are judgement calls that belong to a\n * human, and a wrong automated hoist is worse than the warning.\n */\n\nimport {\n ESLintUtils,\n type TSESTree,\n AST_NODE_TYPES,\n} from \"@typescript-eslint/utils\";\n\ntype MessageIds = \"hoistCollection\" | \"hoistRegex\";\n\ntype Options = readonly [\n {\n minElements?: number;\n checkRegex?: boolean;\n ignoreTestFiles?: boolean;\n }?,\n];\n\nconst DEFAULT_MIN_ELEMENTS = 3;\n\n/** How deep a nested literal may go before we stop trying to prove it static. */\nconst MAX_LITERAL_DEPTH = 4;\n\nconst IGNORE_PATTERNS: readonly RegExp[] = [\n /[\\\\/]generated[\\\\/]/,\n /\\.gen\\.tsx?$/,\n /\\.generated\\.tsx?$/,\n /\\.d\\.ts$/,\n];\n\nconst TEST_FILE_PATTERNS: readonly RegExp[] = [\n /\\.(?:test|spec)\\.[cm]?[jt]sx?$/,\n /[\\\\/]__tests__[\\\\/]/,\n /[\\\\/]__mocks__[\\\\/]/,\n /[\\\\/]tests?[\\\\/]/,\n /\\.stories\\.[cm]?[jt]sx?$/,\n];\n\n/**\n * Methods that mutate the receiver. A call to any of these on the binding is\n * proof the value is meant to be rebuilt per call.\n */\nconst MUTATING_METHODS: ReadonlySet<string> = new Set([\n // Array\n \"push\",\n \"pop\",\n \"shift\",\n \"unshift\",\n \"splice\",\n \"sort\",\n \"reverse\",\n \"fill\",\n \"copyWithin\",\n // Set / Map\n \"add\",\n \"set\",\n \"delete\",\n \"clear\",\n // Object-ish escape hatches\n \"assign\",\n]);\n\nconst FUNCTION_TYPES: ReadonlySet<AST_NODE_TYPES> = new Set([\n AST_NODE_TYPES.FunctionDeclaration,\n AST_NODE_TYPES.FunctionExpression,\n AST_NODE_TYPES.ArrowFunctionExpression,\n]);\n\nconst COLLECTION_CONSTRUCTORS: ReadonlySet<string> = new Set([\"Set\", \"Map\"]);\n\nfunction isIgnoredFile(filename: string, sourceText: string): boolean {\n if (IGNORE_PATTERNS.some((re) => re.test(filename))) {\n return true;\n }\n return /@generated\\b/.test(sourceText.slice(0, 1024));\n}\n\nfunction isTestFile(filename: string): boolean {\n return TEST_FILE_PATTERNS.some((re) => re.test(filename));\n}\n\n/** Unwraps `x as const` / `x satisfies T` down to the inner expression. */\nfunction unwrap(node: TSESTree.Node): TSESTree.Node {\n if (\n node.type === AST_NODE_TYPES.TSAsExpression ||\n node.type === AST_NODE_TYPES.TSSatisfiesExpression ||\n node.type === AST_NODE_TYPES.TSNonNullExpression\n ) {\n return unwrap(node.expression);\n }\n return node;\n}\n\nfunction isRegexLiteral(node: TSESTree.Node): node is TSESTree.RegExpLiteral {\n return (\n node.type === AST_NODE_TYPES.Literal &&\n \"regex\" in node &&\n node.regex !== undefined\n );\n}\n\n/**\n * Whether the expression is built entirely out of literals — no identifier, no\n * call, no member access, no interpolation, no spread. This is the gate that\n * makes hoisting provably safe: a literal-only value cannot capture a\n * parameter, cannot observe call-time state, and cannot have side effects.\n */\nfunction isLiteralOnly(node: TSESTree.Node, depth: number): boolean {\n if (depth > MAX_LITERAL_DEPTH) {\n return false;\n }\n const inner = unwrap(node);\n switch (inner.type) {\n case AST_NODE_TYPES.Literal: {\n return true;\n }\n case AST_NODE_TYPES.TemplateLiteral: {\n return inner.expressions.length === 0;\n }\n case AST_NODE_TYPES.UnaryExpression: {\n return (\n (inner.operator === \"-\" || inner.operator === \"+\") &&\n inner.argument.type === AST_NODE_TYPES.Literal &&\n typeof inner.argument.value === \"number\"\n );\n }\n case AST_NODE_TYPES.ArrayExpression: {\n return inner.elements.every(\n (el) =>\n el !== null &&\n el.type !== AST_NODE_TYPES.SpreadElement &&\n isLiteralOnly(el, depth + 1),\n );\n }\n case AST_NODE_TYPES.ObjectExpression: {\n return inner.properties.every((prop) => {\n if (prop.type !== AST_NODE_TYPES.Property) {\n return false;\n }\n if (prop.shorthand || prop.method || prop.kind !== \"init\") {\n return false;\n }\n if (prop.computed && prop.key.type !== AST_NODE_TYPES.Literal) {\n return false;\n }\n return isLiteralOnly(prop.value, depth + 1);\n });\n }\n default: {\n return false;\n }\n }\n}\n\n/** `Object.freeze(x)` → `x`; anything else is returned untouched. */\nfunction unwrapObjectFreeze(node: TSESTree.Node): TSESTree.Node {\n const inner = unwrap(node);\n if (\n inner.type === AST_NODE_TYPES.CallExpression &&\n inner.callee.type === AST_NODE_TYPES.MemberExpression &&\n !inner.callee.computed &&\n inner.callee.object.type === AST_NODE_TYPES.Identifier &&\n inner.callee.object.name === \"Object\" &&\n inner.callee.property.type === AST_NODE_TYPES.Identifier &&\n inner.callee.property.name === \"freeze\" &&\n inner.arguments.length === 1 &&\n inner.arguments[0] !== undefined &&\n inner.arguments[0].type !== AST_NODE_TYPES.SpreadElement\n ) {\n return unwrap(inner.arguments[0]);\n }\n return inner;\n}\n\ntype Candidate =\n | { kind: \"array\" | \"object\" | \"Set\" | \"Map\"; size: number }\n | { kind: \"regex\"; size: number };\n\n/**\n * Classifies the initializer, or returns null when it is not a hoistable\n * literal-only collection / regex.\n */\nfunction classify(init: TSESTree.Node, checkRegex: boolean): Candidate | null {\n const node = unwrapObjectFreeze(init);\n\n if (isRegexLiteral(node)) {\n if (!checkRegex) {\n return null;\n }\n // `g` and `y` carry `lastIndex` between calls — hoisting changes behaviour.\n if (/[gy]/.test(node.regex.flags)) {\n return null;\n }\n return { kind: \"regex\", size: 1 };\n }\n\n if (node.type === AST_NODE_TYPES.ArrayExpression) {\n return isLiteralOnly(node, 0)\n ? { kind: \"array\", size: node.elements.length }\n : null;\n }\n\n if (node.type === AST_NODE_TYPES.ObjectExpression) {\n return isLiteralOnly(node, 0)\n ? { kind: \"object\", size: node.properties.length }\n : null;\n }\n\n if (\n node.type === AST_NODE_TYPES.NewExpression &&\n node.callee.type === AST_NODE_TYPES.Identifier &&\n COLLECTION_CONSTRUCTORS.has(node.callee.name)\n ) {\n const arg = node.arguments[0];\n if (\n node.arguments.length !== 1 ||\n arg === undefined ||\n arg.type === AST_NODE_TYPES.SpreadElement\n ) {\n return null;\n }\n const entries = unwrap(arg);\n if (entries.type !== AST_NODE_TYPES.ArrayExpression) {\n return null;\n }\n return isLiteralOnly(entries, 0)\n ? { kind: node.callee.name === \"Set\" ? \"Set\" : \"Map\", size: entries.elements.length }\n : null;\n }\n\n return null;\n}\n\n/** The nearest enclosing function, or null when the node is at module scope. */\nfunction enclosingFunction(node: TSESTree.Node): TSESTree.Node | null {\n let current: TSESTree.Node | undefined | null = node.parent;\n while (current !== undefined && current !== null) {\n if (FUNCTION_TYPES.has(current.type)) {\n return current;\n }\n current = current.parent;\n }\n return null;\n}\n\n/**\n * Built-ins that read their argument and return a fresh value without keeping\n * or mutating the original, so passing the binding to them is still a read.\n */\nconst NON_RETAINING_BUILTINS: ReadonlyMap<string, ReadonlySet<string>> = new Map(\n [\n [\n \"Object\",\n new Set([\"keys\", \"values\", \"entries\", \"freeze\", \"fromEntries\", \"assign\"]),\n ],\n [\"Array\", new Set([\"from\", \"isArray\"])],\n [\"JSON\", new Set([\"stringify\"])],\n ],\n);\n\nfunction isNonRetainingBuiltinCall(\n node: TSESTree.CallExpression,\n argument: TSESTree.Identifier,\n): boolean {\n const callee = node.callee;\n if (\n callee.type === AST_NODE_TYPES.Identifier &&\n callee.name === \"structuredClone\"\n ) {\n return true;\n }\n if (\n callee.type !== AST_NODE_TYPES.MemberExpression ||\n callee.computed ||\n callee.object.type !== AST_NODE_TYPES.Identifier ||\n callee.property.type !== AST_NODE_TYPES.Identifier\n ) {\n return false;\n }\n const members = NON_RETAINING_BUILTINS.get(callee.object.name);\n if (members === undefined || !members.has(callee.property.name)) {\n return false;\n }\n // `Object.assign(X, src)` mutates its FIRST argument; only later positions\n // (sources) are reads.\n if (callee.object.name === \"Object\" && callee.property.name === \"assign\") {\n return node.arguments[0] !== argument;\n }\n return true;\n}\n\n/**\n * Whether this single reference to the binding is a provably non-mutating,\n * non-escaping read. Anything not on the whitelist is treated as unsafe, so an\n * unrecognised usage suppresses the report rather than risking a bad hoist.\n */\nfunction isSafeRead(identifier: TSESTree.Identifier): boolean {\n const parent = identifier.parent;\n\n if (parent.type === AST_NODE_TYPES.MemberExpression) {\n if (parent.object !== identifier) {\n // `foo[X]` — the binding is used as a key, which is a plain read.\n return true;\n }\n const grandparent = parent.parent;\n // `X.a = 1`, `X[0] = 1`, `X.a += 1`\n if (\n grandparent.type === AST_NODE_TYPES.AssignmentExpression &&\n grandparent.left === parent\n ) {\n return false;\n }\n // `X.a++`\n if (grandparent.type === AST_NODE_TYPES.UpdateExpression) {\n return false;\n }\n // `delete X.a`\n if (\n grandparent.type === AST_NODE_TYPES.UnaryExpression &&\n grandparent.operator === \"delete\"\n ) {\n return false;\n }\n // `X.push(...)`, `X.sort()`, ...\n if (\n !parent.computed &&\n parent.property.type === AST_NODE_TYPES.Identifier &&\n MUTATING_METHODS.has(parent.property.name) &&\n grandparent.type === AST_NODE_TYPES.CallExpression &&\n grandparent.callee === parent\n ) {\n return false;\n }\n // Everything else through a member expression — `X.length`, `X.includes(v)`,\n // `X[i]`, `X.get(k)`, even `arr.map(X.has)` — reads a property of the\n // binding without handing the binding itself out, so it cannot mutate it.\n return true;\n }\n\n // `for (const x of X)` — iteration is a read.\n if (\n parent.type === AST_NODE_TYPES.ForOfStatement &&\n parent.right === identifier\n ) {\n return true;\n }\n\n // `[...X]`, `{...X}`, `f(...X)` — every spread copies, so the original is\n // never handed out and hoisting stays safe.\n if (parent.type === AST_NODE_TYPES.SpreadElement) {\n return true;\n }\n\n // `\"a\" in X`, `X instanceof Y`, `X === undefined`\n if (parent.type === AST_NODE_TYPES.BinaryExpression) {\n return true;\n }\n\n // A bare argument to a built-in that is known not to retain or mutate its\n // input: `Object.entries(X)`, `Array.from(X)`, `JSON.stringify(X)`. Any other\n // call receiving the binding bare is an escape.\n if (\n parent.type === AST_NODE_TYPES.CallExpression &&\n parent.arguments.includes(identifier) &&\n isNonRetainingBuiltinCall(parent, identifier)\n ) {\n return true;\n }\n\n // `typeof X`, `!X`\n if (\n parent.type === AST_NODE_TYPES.UnaryExpression &&\n parent.operator !== \"delete\"\n ) {\n return true;\n }\n\n return false;\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"prefer-module-level-constant\",\n meta: {\n type: \"suggestion\",\n docs: {\n description:\n \"Hoist literal-only constant collections and regexes out of function bodies to module scope so they are allocated once.\",\n },\n schema: [\n {\n type: \"object\",\n additionalProperties: false,\n properties: {\n minElements: { type: \"number\", minimum: 1 },\n checkRegex: { type: \"boolean\" },\n ignoreTestFiles: { type: \"boolean\" },\n },\n },\n ],\n messages: {\n hoistCollection:\n \"`{{name}}` is a literal-only {{kind}} rebuilt on every call. Hoist it to module scope so it is allocated once and can be reused, exported, and tested.\",\n hoistRegex:\n \"`{{name}}` is a constant regex recompiled on every call. Hoist it to module scope.\",\n },\n },\n defaultOptions: [{}],\n create(context, [optionsArg]) {\n const options = optionsArg ?? {};\n const minElements = options.minElements ?? DEFAULT_MIN_ELEMENTS;\n const checkRegex = options.checkRegex ?? true;\n const ignoreTestFiles = options.ignoreTestFiles ?? true;\n\n const sourceCode = context.sourceCode;\n const filename = context.filename;\n\n if (isIgnoredFile(filename, sourceCode.getText())) {\n return {};\n }\n if (ignoreTestFiles && isTestFile(filename)) {\n return {};\n }\n\n function allReferencesAreSafeReads(\n declarator: TSESTree.VariableDeclarator,\n ): boolean {\n const variables = sourceCode.getDeclaredVariables(declarator);\n const variable = variables[0];\n if (variable === undefined) {\n return false;\n }\n for (const reference of variable.references) {\n // The initializer write itself is not a usage.\n if (reference.init === true) {\n continue;\n }\n if (reference.isWrite()) {\n return false;\n }\n // A `JSXIdentifier` reference means the constant is rendered as a JSX\n // tag name — not something this rule reasons about, so bail.\n if (reference.identifier.type !== AST_NODE_TYPES.Identifier) {\n return false;\n }\n if (!isSafeRead(reference.identifier)) {\n return false;\n }\n }\n return true;\n }\n\n return {\n VariableDeclarator(node: TSESTree.VariableDeclarator): void {\n const declaration = node.parent;\n if (\n declaration.type !== AST_NODE_TYPES.VariableDeclaration ||\n declaration.kind !== \"const\" ||\n declaration.declare === true\n ) {\n return;\n }\n if (node.id.type !== AST_NODE_TYPES.Identifier || node.init === null) {\n return;\n }\n if (enclosingFunction(node) === null) {\n return;\n }\n\n const candidate = classify(node.init, checkRegex);\n if (candidate === null) {\n return;\n }\n if (candidate.kind !== \"regex\" && candidate.size < minElements) {\n return;\n }\n if (!allReferencesAreSafeReads(node)) {\n return;\n }\n\n context.report({\n node: node.id,\n messageId:\n candidate.kind === \"regex\" ? \"hoistRegex\" : \"hoistCollection\",\n data: { name: node.id.name, kind: candidate.kind },\n });\n },\n };\n },\n});\n","/**\n * @fileoverview Flag a JSDoc block whose every word is already in the signature\n * it documents.\n *\n * /** Get the user by id. *\\/\n * export function getUserById(id: string): User { … }\n *\n * The block costs three lines and a review, survives every rename that matters,\n * and tells the reader nothing. Delete it, or say what the caller cannot read\n * off the name: what it throws, what it assumes, why it exists.\n *\n * **The generated-file sniff is mandatory, not a nicety.** In the 817-block\n * hand-written JSDoc corpus, 87% of the raw hits for this shape came from\n * OpenAPI codegen output, where every `@param id The id.` is emitted by a\n * template and rewritten on the next `openapi-generator` run. Editing them is\n * work that gets reverted; `isGeneratedFile` (path AND header marker) is what\n * takes the count from \"hundreds, mostly noise\" to a readable handful.\n *\n * **Never flagged**: a block carrying a value tag (`@deprecated`, `@see`,\n * `@example`, `@throws`, `@template`, `@remarks`, `@since`, …) — those are the\n * content the signature cannot hold; a block carrying any tag this rule does not\n * model, because it cannot judge what it cannot read; a `@param` or `@returns`\n * description that adds a word of its own; and anything in the nine-signal\n * protected class from `_comments`.\n *\n * **Autofix is deliberately a SUGGESTION, never `--fix`.** Deleting a doc block\n * in bulk is silent information loss if the judgement is wrong once; a\n * suggestion makes a human accept each one.\n *\n * **Measured.** 40 hits across the maintained repos (noura-be 11, demo-gateway\n * 29, bulbul / portal / automations 0) and 5 across zod / swr / TanStack Query\n * (zustand 0). All 45 were read; the only debatable one is zod's\n * `/** The input data *\\/` on `readonly input: unknown`, which is a published\n * API doc that nonetheless says nothing the field does not. Everything else is\n * `/** Logout function *\\/` on `useLogout`, `/** Base64 encode a string *\\/` on\n * `base64Encode(str)`, `/** A list row. *\\/` on `ListRow`.\n */\n\nimport { AST_NODE_TYPES, ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\nimport { isProtected, splitIdentifier, stem } from \"./_comments.js\";\nimport { isGeneratedFile } from \"./_paths.js\";\n\ntype MessageIds = \"restatesSignature\" | \"deleteBlock\";\ntype Options = readonly [];\n\n// Tags that carry something the signature cannot. Any of them and the block stays.\nconst VALUE_TAGS: ReadonlySet<string> = new Set([\n \"alpha\", \"author\", \"beta\", \"category\", \"copyright\", \"default\", \"defaultvalue\",\n \"deprecated\", \"example\", \"experimental\", \"fileoverview\", \"fixme\", \"group\",\n \"inheritdoc\", \"internal\", \"license\", \"link\", \"module\", \"override\",\n \"packagedocumentation\", \"remarks\", \"see\", \"since\", \"template\", \"throws\",\n \"todo\", \"typeparam\",\n]);\n\n// Tags this rule models and can therefore judge. Anything else means the block\n// is doing something the rule does not understand, so it is left alone.\nconst MODELLED_TAGS: ReadonlySet<string> = new Set([\n \"arg\", \"argument\", \"async\", \"description\", \"param\", \"return\", \"returns\",\n]);\n\nconst PARAM_TAGS: ReadonlySet<string> = new Set([\"arg\", \"argument\", \"param\"]);\nconst RETURN_TAGS: ReadonlySet<string> = new Set([\"return\", \"returns\"]);\n\nconst DIRECTIVE_RE = /^\\s*(?:eslint\\b|eslint-|@ts-|prettier|biome-|c8\\b|v8\\b|istanbul\\b|@vite|webpack|@jsx|@jest-environment|@vitest-environment|#__)/i;\n\n// Prose filler that says nothing about *which* thing is being described. Wider\n// than the comment tokenizer's list because JSDoc conventionally repeats the\n// vocabulary of the type system itself (\"the callback prop\", \"an optional\n// string\") without that being a claim about this declaration.\nconst STOPWORDS: ReadonlySet<string> = new Set(\n `the a an of to for in on with and or as at by is are was be been being\n this that it its if whether when where which what will would can could should\n must may into from over about not no does do done has have had used use uses\n using given provided specified current new existing all any each per via based\n function method component hook class instance object value values data item\n items element callback handler prop props param parameter argument arg return\n returns returning result optional required default true false null undefined\n string number boolean array list promise`.split(/\\s+/),\n);\n\nconst WORD_RE = /[A-Za-z]+/g;\n\ninterface JsDocTag {\n readonly name: string;\n readonly text: string;\n}\n\n/** Split a JSDoc block into its free description and its tags. */\nfunction parseJsDoc(value: string): { description: string; tags: JsDocTag[] } {\n const lines = value.replace(/^\\*/, \"\").split(\"\\n\").map((line) => line.replace(/^\\s*\\*?\\s?/, \"\"));\n const description: string[] = [];\n const tags: { name: string; text: string }[] = [];\n let current: { name: string; text: string } | null = null;\n for (const line of lines) {\n const match = /^\\s*@(\\w[\\w-]*)\\s*(.*)$/.exec(line);\n if (match) {\n current = { name: (match[1] ?? \"\").toLowerCase(), text: match[2] ?? \"\" };\n tags.push(current);\n } else if (current !== null) {\n current.text += ` ${line.trim()}`;\n } else {\n description.push(line);\n }\n }\n return { description: description.join(\"\\n\").trim(), tags };\n}\n\n/** Every content word of `text`, lowercased, with filler dropped. */\nfunction proseTokens(text: string): string[] {\n return (text.match(WORD_RE) ?? [])\n .map((word) => word.toLowerCase())\n .filter((word) => word.length > 1 && !STOPWORDS.has(word));\n}\n\n/** True when every content word of `text` already appears in `known`. */\nfunction covered(text: string, known: ReadonlySet<string>): boolean {\n const stems = new Set<string>();\n for (const token of known) stems.add(stem(token));\n return proseTokens(text).every((word) => known.has(word) || stems.has(stem(word)));\n}\n\n/** The declared name and parameter names of the node a JSDoc block sits above. */\nfunction declarationNames(node: TSESTree.Node): { name: string; params: string[] } | null {\n switch (node.type) {\n // `export function f()` — the JSDoc sits above the `export`, so the token\n // after it resolves to the wrapper, not to the thing being documented.\n case AST_NODE_TYPES.ExportNamedDeclaration:\n case AST_NODE_TYPES.ExportDefaultDeclaration:\n return node.declaration == null ? null : declarationNames(node.declaration);\n case AST_NODE_TYPES.FunctionDeclaration:\n case AST_NODE_TYPES.TSDeclareFunction:\n return node.id === null ? null : { name: node.id.name, params: paramNames(node.params) };\n case AST_NODE_TYPES.ClassDeclaration:\n case AST_NODE_TYPES.TSInterfaceDeclaration:\n case AST_NODE_TYPES.TSTypeAliasDeclaration:\n case AST_NODE_TYPES.TSEnumDeclaration:\n return node.id === null ? null : { name: node.id.name, params: [] };\n case AST_NODE_TYPES.VariableDeclaration: {\n const declarator = node.declarations[0];\n if (declarator === undefined || declarator.id.type !== AST_NODE_TYPES.Identifier) return null;\n const init = declarator.init;\n const params =\n init != null &&\n (init.type === AST_NODE_TYPES.ArrowFunctionExpression ||\n init.type === AST_NODE_TYPES.FunctionExpression)\n ? paramNames(init.params)\n : [];\n return { name: declarator.id.name, params };\n }\n case AST_NODE_TYPES.MethodDefinition:\n case AST_NODE_TYPES.PropertyDefinition:\n case AST_NODE_TYPES.TSMethodSignature:\n case AST_NODE_TYPES.TSPropertySignature: {\n if (node.key.type !== AST_NODE_TYPES.Identifier) return null;\n const params =\n node.type === AST_NODE_TYPES.MethodDefinition\n ? paramNames(node.value.params)\n : node.type === AST_NODE_TYPES.TSMethodSignature\n ? paramNames(node.params)\n : [];\n return { name: node.key.name, params };\n }\n default:\n return null;\n }\n}\n\nfunction paramNames(params: readonly TSESTree.Parameter[]): string[] {\n const names: string[] = [];\n for (const param of params) {\n const target = param.type === AST_NODE_TYPES.AssignmentPattern ? param.left : param;\n if (target.type === AST_NODE_TYPES.Identifier) names.push(target.name);\n else if (target.type === AST_NODE_TYPES.TSParameterProperty) continue;\n }\n return names;\n}\n\nfunction tokensOf(names: readonly string[]): Set<string> {\n const tokens = new Set<string>();\n for (const name of names) for (const part of splitIdentifier(name)) tokens.add(part);\n return tokens;\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"jsdoc-restates-signature\",\n meta: {\n type: \"suggestion\",\n hasSuggestions: true,\n docs: {\n description:\n \"Flag a JSDoc block whose description and tags only re-spell the signature they document.\",\n },\n schema: [],\n messages: {\n restatesSignature:\n \"JSDoc only re-spells the signature — delete it, or say what the caller cannot read off the name (what it throws, what it assumes, why it exists).\",\n deleteBlock: \"Delete the JSDoc block.\",\n },\n },\n defaultOptions: [],\n create(context) {\n if (isGeneratedFile(context.filename, context.sourceCode.text)) {\n return {};\n }\n const sourceCode = context.sourceCode;\n\n return {\n Program(): void {\n for (const comment of sourceCode.getAllComments()) {\n if (comment.type !== \"Block\" || !comment.value.startsWith(\"*\")) continue;\n const { description, tags } = parseJsDoc(comment.value);\n if (DIRECTIVE_RE.test(description)) continue;\n const tagNames = new Set(tags.map((tag) => tag.name));\n if ([...tagNames].some((name) => VALUE_TAGS.has(name))) continue;\n if ([...tagNames].some((name) => !MODELLED_TAGS.has(name))) continue;\n if (isProtected(description)) continue;\n\n const token = sourceCode.getTokenAfter(comment, { includeComments: false });\n if (token === null || token.loc.start.line !== comment.loc.end.line + 1) continue;\n let node = sourceCode.getNodeByRangeIndex(token.range[0]);\n let declaration: { name: string; params: string[] } | null = null;\n while (node != null && node.type !== AST_NODE_TYPES.Program) {\n declaration = declarationNames(node);\n if (declaration !== null) break;\n node = node.parent ?? null;\n }\n if (declaration === null) continue;\n\n const paramTags = tags.filter((tag) => PARAM_TAGS.has(tag.name));\n const returnTags = tags.filter((tag) => RETURN_TAGS.has(tag.name));\n if (description.length === 0 && paramTags.length === 0 && returnTags.length === 0) {\n continue;\n }\n\n const nameTokens = tokensOf([declaration.name]);\n const paramTokens = tokensOf(declaration.params);\n const known = new Set([...nameTokens, ...paramTokens]);\n\n let addsNothing = covered(description, known);\n for (const tag of paramTags) {\n const text = tag.text.replace(/^\\{[^}]*\\}\\s*/, \"\");\n const match = /^\\[?([A-Za-z_$][\\w.$]*)\\]?\\s*-?\\s*([\\s\\S]*)$/.exec(text);\n if (match === null) {\n addsNothing = false;\n break;\n }\n const own = new Set([...splitIdentifier(match[1]?.split(\".\").pop() ?? \"\"), ...nameTokens]);\n if (!covered(match[2] ?? \"\", own)) {\n addsNothing = false;\n break;\n }\n for (const part of splitIdentifier(match[1]?.split(\".\").pop() ?? \"\")) known.add(part);\n }\n if (addsNothing) {\n for (const tag of returnTags) {\n if (!covered(tag.text.replace(/^\\{[^}]*\\}\\s*/, \"\"), known)) {\n addsNothing = false;\n break;\n }\n }\n }\n if (!addsNothing) continue;\n\n context.report({\n node: comment,\n messageId: \"restatesSignature\",\n suggest: [\n {\n messageId: \"deleteBlock\",\n fix: (fixer) => fixer.removeRange([comment.range[0], token.range[0]]),\n },\n ],\n });\n }\n },\n };\n },\n});\n","/**\n * @fileoverview Flag a one-line comment that only re-spells the statement\n * beneath it — the TypeScript twin of Python's SARJ049.\n *\n * // Login schemas\n * export const ZLoginResponseSchema = z.object({ … });\n *\n * Every content word of the comment is already an identifier on the line below.\n * It cannot go out of date usefully, only silently, and a reader who scans it\n * learns nothing the code did not already say. Delete it, or replace it with the\n * *why*.\n *\n * **Division of labour with `no-comment-cruft`.** That rule already reports the\n * VERB-LED shape (`// increment the counter` above `counter += 1`), corroborated\n * against the statement *head*. This rule defers to it — `restatesStatementHead`\n * is imported and used as an exemption — so a comment is never reported twice.\n * What is left for this rule is the noun-phrase label whose every word appears\n * anywhere on the line: `// VB Environment badge`, `// OTP schemas`,\n * `// Language enum`.\n *\n * **What makes it safe.** The first attempt at this shape (PR #98) corroborated\n * by substring — `service` matched `locationService` — and produced 933 hits at\n * a ~60% false-positive rate. Coincidental token overlap is the failure mode, so\n * every guard below is load-bearing: zero information (EVERY content token must\n * appear, exact or stemmed, never by prefix); at least two content tokens (one\n * word labels a thing, it does not restate a statement); a single-line comment\n * (a `//` run is a paragraph); a single-line, value-producing statement (a\n * comment above a block labels a region); the statement must invoke something\n * (a comment above a plain data declaration is a group label — that one shape\n * was every false positive left in the Python corpus sweep); and the whole\n * nine-signal protected class from `_comments` is exempt.\n *\n * **Measured.** 1 hit across the maintained repos (bulbul 0, noura-be 0,\n * portal 0, automations 0, demo-gateway 1) and 8 across zod / swr / TanStack\n * Query, of which one — zod's `// no issues with confirmPassword or password`\n * over `return payload.issues.every(…)` — was the last false positive and is\n * now guarded by `NEGATION_WORD_RE`. The single-line-statement requirement is\n * what keeps the sarj count at zero: noura-be's `// VB Environment badge` sits\n * over a multi-line `const renderVBEnvironmentBadge = () => (`, i.e. it labels\n * a REGION, so this rule leaves it to `no-comment-cruft`'s section-label check.\n * That makes it a preventive ratchet on TypeScript with essentially no\n * migration cost, unlike its Python twin (SARJ049, 29 hits in noura-be).\n */\n\nimport { AST_NODE_TYPES, ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\nimport {\n codeTokens,\n contentTokens,\n isProtected,\n restatableStatementBelow,\n restates,\n restatesStatementHead,\n} from \"./_comments.js\";\nimport { isGeneratedFile } from \"./_paths.js\";\n\ntype MessageIds = \"restatesLineBelow\";\ntype Options = readonly [];\n\nconst MAX_WORDS = 8;\nconst MIN_CONTENT_TOKENS = 2;\n\nconst DIRECTIVE_RE =\n /^(eslint\\b|eslint-|sarj-noqa\\b|@ts-|prettier-ignore|prettier\\b|biome-|c8\\b|v8\\b|istanbul\\b|@type\\b|@vite|webpack|<reference|<amd|global\\b|noinspection|todo\\b|fixme\\b|hack\\b|xxx\\b)/i;\n\n// Commented-out code and section banners belong to `no-comment-cruft`.\nconst CODEY_RE = /^[\\w.$[\\]'\"]+\\s*[:=]\\s*\\S|^[\\w.$]+\\s*\\(|^(?:return|throw|await|import|export|const|let|var)\\b.*[=()[\\]{}]/;\nconst BANNERISH_RE = /[=\\-─-╿*#~_.]{3,}|^[A-Z0-9 _:-]+$/;\n\n// Four shapes that pass the zero-information test and were wrong every time in\n// the famous-corpus sweep: modality (`can`, `should`, `must` state a\n// possibility or an obligation, which no arrangement of identifiers can say), a\n// colon-terminated lead-in (it announces what follows rather than describing the\n// line under it), inline emphasis (someone who wrote `*not*` or backticked an\n// identifier was making a point about it), and a bare negation — `no`/`not`/\n// `never` are stopwords for the tokenizer, so a comment stating a NEGATIVE\n// property passes against the positive spelling below it (`// no issues with\n// confirmPassword or password` over `return payload.issues.every(…)`,\n// zod/packages/zod/src/v4/classic/tests/refine.test.ts:546). Saying what is\n// absent is the one thing the code's own words cannot.\nconst MODALITY_RE = /\\b(?:can|could|should|shall|may|might|must|will|would|cannot)\\b/i;\nconst LEAD_IN_RE = /:$/;\nconst EMPHASIS_RE = /\\*\\w[^*]*\\*|`[^`]+`/;\nconst NEGATION_WORD_RE = /\\b(?:no|not|never|neither|nor|without|none|non)\\b/i;\n\n// The statement must *do* something. A comment above a plain data declaration\n// labels the datum, and that shape is a series of group labels rather than\n// narration — it was every false positive left in the Python corpus sweep.\nconst ACTION_STMT_RE = /[\\w.$\\])]\\s*\\(|^\\s*(?:return|throw|await|yield)\\b/;\n\nconst NON_ASCII_LETTER_RE = /[^\\p{ASCII}\\p{N}\\p{P}\\p{Z}]/u;\n\n/** True when `a` and `b` are `//` comments on consecutive lines. */\nfunction areAdjacentLineComments(\n a: TSESTree.Comment | undefined,\n b: TSESTree.Comment | undefined,\n): boolean {\n return (\n a !== undefined &&\n b !== undefined &&\n a.type === \"Line\" &&\n b.type === \"Line\" &&\n b.loc.start.line === a.loc.end.line + 1\n );\n}\n\n/**\n * True when the statement below the comment heads a run of siblings of the same\n * kind — `// register secrets` over eight `registerSecret(…)` calls. The label\n * provides the grouping, and deleting it loses that.\n */\nfunction headsSiblingRun(node: TSESTree.Node): boolean {\n const parent = node.parent;\n if (parent === undefined) return false;\n const body: readonly TSESTree.Node[] | undefined =\n \"body\" in parent && Array.isArray(parent.body)\n ? (parent.body as readonly TSESTree.Node[])\n : undefined;\n if (body === undefined) return false;\n const index = body.indexOf(node);\n const next = index >= 0 ? body[index + 1] : undefined;\n return next !== undefined && next.type === node.type;\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-restated-comment\",\n meta: {\n type: \"suggestion\",\n docs: {\n description:\n \"Flag a single-line comment whose every word already appears on the statement below it.\",\n },\n schema: [],\n messages: {\n restatesLineBelow:\n \"Comment restates the statement below it — delete it, or replace it with the *why*; the code already carries the *what*.\",\n },\n },\n defaultOptions: [],\n create(context) {\n if (isGeneratedFile(context.filename, context.sourceCode.text)) {\n return {};\n }\n const sourceCode = context.sourceCode;\n const lines = sourceCode.lines;\n\n function isStandalone(comment: TSESTree.Comment): boolean {\n const before = sourceCode.getTokenBefore(comment, { includeComments: false });\n return !before || before.loc.end.line < comment.loc.start.line;\n }\n\n function labelsASiblingRun(comment: TSESTree.Comment): boolean {\n const token = sourceCode.getTokenAfter(comment, { includeComments: false });\n if (token === null) return false;\n // Walk out to the STATEMENT, not merely to the first node starting on that\n // line. Stopping early looked at the innermost `Identifier`, whose parent\n // is an expression rather than a block, so the sibling test silently never\n // ran — zod's `assignability.test.ts` alone contributed 89 hits, a table\n // of one-line `z.string() satisfies z.core.$ZodString;` assertions each\n // labelled with the type it checks.\n for (\n let node: TSESTree.Node | undefined | null = sourceCode.getNodeByRangeIndex(token.range[0]);\n node != null && node.type !== AST_NODE_TYPES.Program;\n node = node.parent\n ) {\n if (headsSiblingRun(node)) return true;\n }\n return false;\n }\n\n return {\n Program(): void {\n const comments = sourceCode.getAllComments();\n for (let i = 0; i < comments.length; i++) {\n const comment = comments[i];\n if (comment === undefined || comment.type !== \"Line\") continue;\n if (!isStandalone(comment)) continue;\n if (\n areAdjacentLineComments(comments[i - 1], comment) ||\n areAdjacentLineComments(comment, comments[i + 1])\n ) {\n continue; // one line of a paragraph, not a label for the next statement\n }\n const body = comment.value.replace(/^\\/*/, \"\").trim();\n if (body.length === 0 || body.endsWith(\"?\")) continue;\n if (DIRECTIVE_RE.test(body) || CODEY_RE.test(body) || BANNERISH_RE.test(body)) continue;\n if (NON_ASCII_LETTER_RE.test(body) || isProtected(body)) continue;\n if (MODALITY_RE.test(body) || LEAD_IN_RE.test(body) || EMPHASIS_RE.test(body)) continue;\n if (NEGATION_WORD_RE.test(body)) continue;\n if (body.split(/\\s+/).length > MAX_WORDS) continue;\n\n const tokens = contentTokens(body);\n if (tokens.length < MIN_CONTENT_TOKENS) continue;\n\n const statement = restatableStatementBelow(comment, sourceCode);\n if (statement === null) continue;\n if (restatesStatementHead(body, statement)) continue; // `no-comment-cruft` owns it\n\n const line = lines[comment.loc.start.line] ?? \"\";\n if (!ACTION_STMT_RE.test(line)) continue;\n\n if (labelsASiblingRun(comment)) continue;\n\n if (restates(tokens, codeTokens(line))) {\n context.report({ node: comment, messageId: \"restatesLineBelow\" });\n }\n }\n },\n };\n },\n});\n","/**\n * @fileoverview Flag a trailing comment that spells out a literal already on the\n * line — the TypeScript twin of Python's SARJ051.\n *\n * staleTime: 5 * 60 * 1000, // 5 minutes\n *\n * The comment adds one thing — the unit — and the line already carries every\n * number in it. Put the unit in the *name* (`STALE_TIME_MS`, a `Duration`\n * helper) and the fact travels with the value: it survives a copy-paste, it is\n * visible at every call site, and it cannot drift when someone edits the\n * arithmetic and forgets the comment. That drift is the whole risk — a wrong\n * unit comment is worse than none.\n *\n * **The test is deliberately narrow.** Every one of these must hold: the code\n * before the comment contains a numeric literal; the comment contains at least\n * one number and EVERY number it contains appears verbatim in that code; and\n * every non-numeric word is either a unit word or already an identifier on the\n * line. So `// 5 minutes` over `5 * 60 * 1000` fires, while `// ~3.5 days` over\n * `300000` — a conversion the reader cannot do in their head — does not, and\n * neither does `// doubles per attempt, capped by the gateway`. A comment\n * carrying a ticket or URL is exempt (protected-class signal S1).\n *\n * **Measured.** 18 hits across the nine-repo corpus, 18 of 18 true positives.\n * They cluster hard: `noura-be/typescript/dashboard-fe/src/hooks/use-analytics.ts`\n * alone holds 12 `staleTime` lines, `lib/query-client.ts` two more, and\n * `automations/apps/sarj-ai-worker/src/config.ts:3` carries\n * `export const COOKIE_MAX_AGE_SECONDS = 60 * 60 * 24 * 90; // 90 days`.\n */\n\nimport { ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\nimport { codeTokens, hasExternalReference, stem } from \"./_comments.js\";\nimport { isGeneratedFile } from \"./_paths.js\";\n\ntype MessageIds = \"narratesValue\";\ntype Options = readonly [];\n\n// A number that is not part of an identifier or a dotted member path.\nconst NUMBER_RE = /(?<![\\w.])(\\d+(?:\\.\\d+)?)(?![\\w.])/g;\nconst WORD_RE = /[A-Za-z]+(?:'[a-z]+)?|\\d+(?:\\.\\d+)?/g;\n\n// Words that name the unit rather than the quantity — the one thing the code\n// does not say, and the reason the fix is a *name*, not a deletion.\nconst UNIT_WORDS: ReadonlySet<string> = new Set([\n \"bytes\", \"characters\", \"chars\", \"day\", \"days\", \"gb\", \"hour\", \"hours\", \"hr\",\n \"hrs\", \"hz\", \"items\", \"k\", \"kb\", \"khz\", \"m\", \"mb\", \"milliseconds\", \"min\",\n \"mins\", \"minute\", \"minutes\", \"ms\", \"pct\", \"percent\", \"px\", \"retries\", \"rows\",\n \"s\", \"sec\", \"second\", \"seconds\", \"secs\", \"times\", \"tokens\",\n]);\n\nconst STOPWORDS: ReadonlySet<string> = new Set([\n \"a\", \"an\", \"and\", \"are\", \"as\", \"at\", \"be\", \"by\", \"for\", \"in\", \"is\", \"it\",\n \"of\", \"on\", \"or\", \"that\", \"the\", \"this\", \"we\", \"with\",\n]);\n\nconst DIRECTIVE_RE =\n /^\\s*(?:eslint\\b|eslint-|sarj-noqa\\b|@ts-|prettier|biome-|c8\\b|v8\\b|istanbul\\b|todo\\b|fixme\\b|hack\\b|xxx\\b)/i;\n\nfunction numbersIn(text: string): Set<string> {\n return new Set(text.match(NUMBER_RE) ?? []);\n}\n\nfunction narratesValue(body: string, code: string): boolean {\n if (body.length === 0 || DIRECTIVE_RE.test(body) || hasExternalReference(body)) return false;\n const codeNumbers = numbersIn(code);\n if (codeNumbers.size === 0) return false;\n const words = (body.match(WORD_RE) ?? []).map((word) => word.toLowerCase());\n if (words.length === 0) return false;\n const commentNumbers = numbersIn(body);\n if (commentNumbers.size === 0) return false;\n for (const number of commentNumbers) {\n if (!codeNumbers.has(number)) return false;\n }\n const identifiers = codeTokens(code);\n const stems = new Set<string>();\n for (const token of identifiers) stems.add(stem(token));\n return words.every(\n (word) =>\n STOPWORDS.has(word) ||\n UNIT_WORDS.has(word) ||\n commentNumbers.has(word) ||\n identifiers.has(word) ||\n stems.has(stem(word)),\n );\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"trailing-value-narration\",\n meta: {\n type: \"suggestion\",\n docs: {\n description:\n \"Flag a trailing comment whose every word and number is already on the line it annotates.\",\n },\n schema: [],\n messages: {\n narratesValue:\n \"Trailing comment restates the literal on this line — put the unit in the name (STALE_TIME_MS) so it cannot drift.\",\n },\n },\n defaultOptions: [],\n create(context) {\n if (isGeneratedFile(context.filename, context.sourceCode.text)) {\n return {};\n }\n const sourceCode = context.sourceCode;\n\n function isTrailing(comment: TSESTree.Comment): boolean {\n const before = sourceCode.getTokenBefore(comment, { includeComments: false });\n return before !== null && before.loc.end.line === comment.loc.start.line;\n }\n\n return {\n Program(): void {\n for (const comment of sourceCode.getAllComments()) {\n if (!isTrailing(comment)) continue;\n const line = sourceCode.lines[comment.loc.start.line - 1] ?? \"\";\n const code = line.slice(0, comment.loc.start.column);\n const body = comment.value.replace(/^\\*+/, \"\").replace(/\\*+$/, \"\").trim();\n if (narratesValue(body, code)) {\n context.report({ node: comment, messageId: \"narratesValue\" });\n }\n }\n },\n };\n },\n});\n","/**\n * @fileoverview TS side of SARJ057 (`no-tautological-expect`). An `expect(...)`\n * whose operands are all literals has already decided its outcome before the\n * test runs: `expect(true).toBe(true)` passes if you delete the entire module\n * under test. It is not a weak assertion, it is a *non*-assertion.\n *\n * This is the placeholder that never got replaced. Every hit found in the\n * internal sweep is one:\n *\n * - `apps/internal-automations/test/customer-bridge-disambiguation-handler.test.ts:37`\n * — `expect(true).toBe(true); // placeholder`, in a suite whose name promises\n * it disambiguates customer bridges,\n * - `packages/precedent-node/src/tests/dummy.test.ts:5` — `expect(true).toBe(true)`,\n * - `packages/precedent-iso/src/tests/dummy.test.ts:4` — `expect(1).toEqual(1)`.\n *\n * The Python side (SARJ043 `zero-assertion-test`) has caught the assertion-free\n * version of this since 0.15.0 and has no TypeScript counterpart, which is\n * precisely why the `automations` placeholder went uncaught for as long as it\n * did: the file *has* an assertion, so nothing was looking at it.\n *\n * Fires on exactly two shapes:\n *\n * 1. `expect(<literal>).toBe|toEqual|toStrictEqual(<textually identical literal>)`;\n * 2. `expect(<literal>).<zero-argument matcher>()` — `toBeDefined`,\n * `toBeTruthy`, `toBeNull`, `toBeUndefined`, `toBeFalsy`, `toBeNaN`. With a\n * literal receiver the answer is fixed at parse time either way.\n *\n * **The narrowness is the rule.** The obvious generalisation — \"flag a\n * comparison of a thing with itself\" — was measured across five repositories\n * and is ~95% false positives. `expect(hash([o])).toEqual(hash([o]))` is a\n * *determinism* test; `expect(memo(x)).toBe(memo(x))` is a *memoization* test;\n * `expect(a).toEqual(a)` on a value with a custom equality is a *reflexivity*\n * test. All three can genuinely fail, and all three are correct code. So an\n * operand that is an identifier, a member expression or a call is never enough:\n * both sides must be literals, and textually identical ones.\n *\n * Deliberately NOT flagged:\n *\n * - any modified chain — `expect(x).not.toBe(...)`, `.resolves`, `.rejects`.\n * The matcher must hang directly off the `expect(...)` call, which keeps the\n * rule to the shape it can reason about;\n * - a literal compared with a *different* literal (`expect(1).toBe(2)`) — that\n * assertion always fails, which is loud on the first run rather than silent;\n * - a spread element anywhere in an array/object literal (`expect([...xs])`),\n * whose contents come from a runtime value;\n * - a template literal with interpolations, for the same reason;\n * - anything outside a test file.\n *\n * Measured before shipping: 3 hits across 5,819 `.ts`/`.tsx` files (1,003 of\n * them test files, where the rule is active) — bulbul, noura-be, kpi-hub, ai,\n * automations and demo-gateway plus got / hono / swr / trpc. 3 true positives,\n * 0 false positives; all three are the placeholders listed above.\n */\n\nimport { AST_NODE_TYPES, ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\nimport { isTestFile } from \"./_paths.js\";\n\ntype MessageIds = \"tautologicalComparison\" | \"tautologicalMatcher\";\ntype Options = readonly [];\n\n/** Matchers that take the expected value as their single argument. */\nconst EQUALITY_MATCHERS: ReadonlySet<string> = new Set([\"toBe\", \"toEqual\", \"toStrictEqual\"]);\n\n/** Matchers that take no argument, so the receiver alone fixes the outcome. */\nconst ZERO_ARG_MATCHERS: ReadonlySet<string> = new Set([\n \"toBeDefined\",\n \"toBeUndefined\",\n \"toBeNull\",\n \"toBeTruthy\",\n \"toBeFalsy\",\n \"toBeNaN\",\n]);\n\n/** Enough of the operand to identify it in the message without pasting a screenful. */\nconst OPERAND_PREVIEW_CHARS = 40;\n\n/** Sign prefixes: `-1` is a unary expression, not a literal, but it is constant. */\nconst NUMERIC_SIGNS: ReadonlySet<string> = new Set([\"-\", \"+\"]);\n\n/**\n * True when `node` is written entirely out of literals — no identifier, member\n * expression, call or spread anywhere inside it. That exclusion is the whole\n * false-positive guard: a value the code produced is what makes an assertion an\n * assertion.\n */\nfunction isLiteral(node: TSESTree.Node): boolean {\n switch (node.type) {\n case AST_NODE_TYPES.Literal:\n return true;\n case AST_NODE_TYPES.TemplateLiteral:\n return node.expressions.length === 0;\n case AST_NODE_TYPES.UnaryExpression:\n return NUMERIC_SIGNS.has(node.operator) && isLiteral(node.argument);\n case AST_NODE_TYPES.ArrayExpression:\n return node.elements.every((element) => element !== null && isLiteral(element));\n case AST_NODE_TYPES.ObjectExpression:\n return node.properties.every(\n (property) =>\n property.type === AST_NODE_TYPES.Property &&\n !property.computed &&\n isLiteral(property.value),\n );\n default:\n return false;\n }\n}\n\n/** The `expect(<single argument>)` call a matcher hangs directly off, if any. */\nfunction expectOperand(callee: TSESTree.MemberExpression): TSESTree.Node | null {\n const receiver = callee.object;\n if (\n receiver.type !== AST_NODE_TYPES.CallExpression ||\n receiver.callee.type !== AST_NODE_TYPES.Identifier ||\n receiver.callee.name !== \"expect\" ||\n receiver.arguments.length !== 1\n ) {\n return null;\n }\n return receiver.arguments[0] ?? null;\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-tautological-expect\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Disallow an assertion whose operands are all literals; its outcome is fixed before the code runs, so it can never fail.\",\n },\n schema: [],\n messages: {\n tautologicalComparison:\n \"`expect({{operand}}).{{matcher}}({{operand}})` compares a literal with an identical literal — it passes even if the code under test is deleted. Assert on a value the code produced, or delete the test.\",\n tautologicalMatcher:\n \"`expect({{operand}}).{{matcher}}()` asserts on a literal, so its outcome is fixed before the code runs. Assert on a value the code produced, or delete the test.\",\n },\n },\n defaultOptions: [],\n create(context) {\n if (!isTestFile(context.filename)) {\n return {};\n }\n /** The operand as written, collapsed to one line and elided for the message. */\n const preview = (node: TSESTree.Node): string => {\n const text = context.sourceCode.getText(node).replaceAll(/\\s+/gu, \" \");\n return text.length > OPERAND_PREVIEW_CHARS\n ? `${text.slice(0, OPERAND_PREVIEW_CHARS)}…`\n : text;\n };\n return {\n CallExpression(node: TSESTree.CallExpression): void {\n const callee = node.callee;\n if (callee.type !== AST_NODE_TYPES.MemberExpression || callee.computed) {\n return;\n }\n if (callee.property.type !== AST_NODE_TYPES.Identifier) {\n return;\n }\n const matcher = callee.property.name;\n const operand = expectOperand(callee);\n if (operand === null || !isLiteral(operand)) {\n return;\n }\n if (ZERO_ARG_MATCHERS.has(matcher) && node.arguments.length === 0) {\n context.report({\n node,\n messageId: \"tautologicalMatcher\",\n data: { operand: preview(operand), matcher },\n });\n return;\n }\n const expected = node.arguments[0];\n if (\n !EQUALITY_MATCHERS.has(matcher) ||\n node.arguments.length !== 1 ||\n expected === undefined ||\n !isLiteral(expected)\n ) {\n return;\n }\n // Textual identity, not structural: `expect(1).toBe(1.0)` is a\n // deliberate statement about representation and is left alone.\n if (context.sourceCode.getText(operand) !== context.sourceCode.getText(expected)) {\n return;\n }\n context.report({\n node,\n messageId: \"tautologicalComparison\",\n data: { operand: preview(operand), matcher },\n });\n },\n };\n },\n});\n","/**\n * @fileoverview An exported concrete class that takes injected collaborators and\n * has no interface above it is not substitutable. Every consumer must import and\n * depend on the concrete class, so the only way to test a consumer is to mock the\n * class — the exact failure the Python side of this standard attacks with\n * `prefer-real-store-in-tests` / `prefer-library-fake`. An interface (a port),\n * even a two-method one, lets a consumer's test pass a real alternative\n * implementation instead of a mock.\n *\n * The rule fires on an EXPORTED, non-abstract class declaration that\n * (a) has no `implements` clause and does not `extend` anything,\n * (b) stores >=1 constructor parameter whose type is a bare type reference\n * (not a primitive, literal, inline object type, union, or function type)\n * and is not a config/options/callback bag, and\n * (c) declares >=1 public instance method (a class with only a constructor,\n * fields, or getters is a value object / DTO, not a service).\n *\n * CORPUS SWEEP (2026-07, re-measured over 18 repos): bulbul/typescript,\n * automations, portal, demo-gateway, sarj-demos, noura-be, kpi-hub, najm,\n * kashta, summer, hala, ai-canvas-health, farwa, tamr, money2020, banking-demo,\n * pericles, digital-bank. 286 exported class declarations in non-test /\n * non-generated files; 216 already carry an `implements` clause, extend a base\n * class, or are themselves abstract. 76% of the population is ALREADY\n * compliant, which is what makes this a lint rule rather than a design\n * proposal. 30 fire after the two guards below (35 before them).\n *\n * The earlier sweep read 82% of 229 because it covered only the first twelve\n * repos; the six added here are where both false-positive families lived, and\n * both were invisible to it. Adoption varies far more than the aggregate\n * suggests: portal (62/62), bulbul (32/32), pericles (12/12), hala and\n * demo-gateway are at 100%; tamr 86%, summer 83%, farwa 80%, najm 71%, kashta\n * 56%, ai-canvas-health / money2020 / banking-demo 50%, automations 46% (32 of\n * 69), digital-bank 29%, noura-be 25%. The rule is a ratchet for the low half,\n * not a description of the high half.\n *\n * The convention being enforced is `interface`, not `abstract class`, and the\n * corpus is unambiguous about it: 175 `implements` clauses against exactly ONE\n * hand-written abstract class in 7,912 files\n * (hala/typescript/packages/app/src/app/sifi/lib/question-generator.ts:222).\n * (A raw grep finds ~220 more `abstract class` tokens in demo-gateway and\n * sarj-demos; every one is an ambient Cloudflare declaration inside\n * `worker-configuration.d.ts`, excluded here as generated.) Three impl-naming\n * conventions coexist — `HttpCallService implements CallService` (bulbul),\n * `AshbyClient implements IAshbyClient` (automations, portal, najm), and\n * `DocumentParserImpl` / `DocumentParser` (summer) — so the message names no\n * interface for you.\n *\n * FIRING DISTRIBUTION, and why the hits are outliers rather than a house style\n * (before -> after the two guards below):\n * automations 69 exported classes, 20 -> 20 hits\n * najm 17 exported classes, 3 -> 3 hits\n * summer 24 exported classes, 3 -> 2 hits\n * farwa 20 exported classes, 3 -> 2 hits\n * kashta 9 exported classes, 2 -> 2 hits\n * ai-canvas-health 2 exported classes, 1 -> 1 hit\n * tamr 14 exported classes, 1 -> 0 hits\n * money2020 2 exported classes, 1 -> 0 hits\n * banking-demo 2 exported classes, 1 -> 0 hits\n * bulbul / portal / pericles / hala / demo-gateway / sarj-demos / kpi-hub /\n * noura-be / digital-bank: 0 hits\n *\n * THE FALSE-POSITIVE RATE WAS 14%, NOT 0-3.4%. The earlier claim rested on a\n * sweep that never reached farwa, tamr, money2020 or banking-demo, and both\n * false-positive families live only there (plus one instance each already in\n * summer and, for the transport family, nowhere the old sweep looked). Of 35\n * raw hits, 5 were false: three copies of one express router template and two\n * copies of one `ky` wrapper. The two guards below remove exactly those 5 and\n * cost 0 true positives — verified hit-by-hit across all 18 repos, 35 -> 30.\n *\n * The origin case is\n * automations/apps/internal-automations/src/talent/name-healer/service.ts:28\n * (commit 5c5830aa, PR #241): `class NameHealerService` holds `svc: Services`,\n * exposes one `run` method, and implements nothing. Its own test\n * (test/talent-name-healer-service.test.ts:57) has to build the collaborator\n * with `{ ... } as unknown as Services` — the cast-mock this rule is trying to\n * make unnecessary. Its sibling in the same directory tree,\n * talent/referral-tracker/service.ts:129, is the same shape done right:\n * `class ReferralTrackerService implements IReferralTrackerService`.\n *\n * Two further confirmations that the rule finds real drift rather than taste:\n *\n * - najm/src/services/service-factory.ts types 8 of its 11 fields as `I*`\n * interfaces and 3 as concrete classes. The rule fires on exactly those\n * three (`VoiceCallService`, `UserService`, `AgentService`) — the repo's own\n * composition root already documents which classes are missing a port.\n * - summer/typescript/packages/credit/src/app/api/document-parser.ts:116\n * declares `interface DocumentParser` three lines above\n * `class DocumentParserImpl`, which never says `implements`. The port exists\n * and the impl is silently free to drift from it.\n *\n * NAMED FALSE POSITIVES that shaped the guards (each measured, not imagined):\n * - automations/packages/shared/src/http-client.ts:25 `JsonHttpClient`\n * takes `options: JsonHttpClientOptions`, whose three fields are all\n * primitives. A config bag is not a collaborator seam -> config-ish type\n * suffixes and parameter names are excluded. Cost: 1 of 32 raw hits.\n * - noura-be/.../AssistantSDK/src/voice/domain/LiveKitManager.ts:42 takes\n * `callbacks: LiveKitCallbacks`, an OUTBOUND observer bag, not an inbound\n * dependency; there is nothing to substitute. -> `Callbacks` suffix and\n * `callbacks` parameter name excluded. Cost: 1 of 32.\n * - najm/src/services/service-factory.ts:15 `ServiceFactory` receives a `db`\n * and `new`s eleven services onto its own fields. That is a composition\n * root: it is where concrete types are supposed to be named, and a port\n * above it protects nobody. -> a constructor that BUILDS more fields than it\n * RECEIVES collaborators is exempt. Cost: 1 of 32.\n *\n * The first cut of that guard exempted any constructor containing a `new`\n * at all, and the corpus immediately punished it: automations'\n * `talent/referral-message-handler.ts:5` and `talent/referral-reaction-handler.ts:5`\n * each take `svc: Services` AND build one internal `ReferralProcessor`, and\n * both silently stopped firing. Constructing a helper is not being a wiring\n * class; the ratio is what separates the two.\n *\n * - FRAMEWORK ROUTERS. summer/typescript/.../eight-job-runner/src/router.ts:11,\n * farwa/typescript/packages/farwa-job-runner/src/router.ts:11 and\n * tamr/typescript/packages/tamr-job-runner/src/router.ts:7 are three copies\n * of one template: `MainRouter(taskStore, taskExecutor)` whose `init()`\n * returns an `express.Router()`. The server's bootstrap mounts it; nothing\n * injects it, so there is no consumer to hand a port to. -> a class whose\n * body manufactures a router (`express.Router()` / `Router()`) or handles\n * the framework's own namespaced request/response types\n * (`express.Request`) is exempt. The DOM `Request`/`Response` globals a\n * Workers `fetch` handler names are unqualified and deliberately do not\n * match. Cost: 3 of 35 raw hits, all three false positives, 0 true\n * positives lost.\n *\n * - TRANSPORT WRAPPERS. money2020/src/lib/http-client.ts:34 and\n * banking-demo/src/lib/http-client.ts:39 are two copies of\n * `Money2020HttpClient(private readonly client: KyInstance)`. These are\n * false by this standard's own stated reasoning: the Python twin excludes\n * `*Client` precisely because an ABC over a class whose collaborator is\n * somebody else's HTTP transport substitutes nothing — a consumer's test\n * fakes the transport (`respx`, `nock`, `msw`), not the wrapper. -> a class\n * is exempt when its ONLY collaborator is a third-party transport\n * (`KyInstance`, `AxiosInstance`, `Session`), it is named `*Client`, AND no\n * interface in the same file shares its stem. All three arms are load\n * bearing. The `*Client` arm is what keeps kashta's\n * `.../precedent-node/src/services/call-service.ts:50` `HttpCallService`\n * and summer's `.../credit/src/app/api/document-parser.ts:116`\n * `DocumentParserImpl` firing — both are `AxiosInstance`-only, and both\n * wrap the transport in a domain operation their consumers can substitute.\n * The same-file-interface arm has zero measured cost and is kept as a\n * precaution: a `FooClient` beside an `IFooClient` it never `implements` is\n * exactly the drift this rule exists for, and the corpus cannot prove that\n * shape's absence elsewhere. Cost: 2 of 35 raw hits, both false positives,\n * 0 true positives lost.\n *\n * GUARDS WITH ZERO MEASURED COST, kept as precautions because the corpus is\n * first-party and cannot prove their absence elsewhere: decorated classes\n * (NestJS-style `@Injectable()` providers are instantiated by class token, so\n * the class IS the contract — 0 decorated classes in the corpus), and a\n * logger/clock-only dependency (weak evidence of a real seam — 0 such classes).\n *\n * GUARDS THAT NEED NO CODE, because `extends` already covers them: React class\n * components (`extends React.Component`), Lexical/editor nodes\n * (`extends TextNode`, `extends DecoratorNode`), error classes\n * (`extends Error`), Durable Objects, Workflows and custom elements. Every one\n * of the 4 exported classes in `.tsx` files across the corpus is such a case\n * (bulbul .../nodes/variable-node.tsx:36, .../lexical/nodes/section-node.tsx:219,\n * demo-gateway + sarj-demos .../PhoneMockupBoundary.tsx:13). Extending a\n * framework base class is itself a form of port.\n *\n * THE ONE-METHOD THRESHOLD is the load-bearing difference from the Python\n * sibling `require_port_for_service` (SARJ063), which demands >=2 public methods\n * and a `*Service`/`*Store`/`*Client` name. Measured here: 22 of the 29 hits\n * expose exactly ONE public method (the distribution is 22x1, 1x2, 1x5, 1x6,\n * 1x8, 2x9, 1x12), so a >=2 threshold would discard 76% of them — including the\n * origin case, whose sole method is `run`. A service-family NAME filter would\n * cost as much: `ReferralProcessor`, `InteractiveActionHandler`,\n * `DocumentParserImpl`, `MainRouter` and every `*Handler` in automations carry\n * no such suffix. TypeScript's `export` keyword plus a stored collaborator is\n * the proxy for \"someone depends on this\" that Python has to approximate with\n * naming, so this rule leans on those two instead.\n *\n * DELIBERATE FALSE NEGATIVES, each measured at 0 occurrences in the corpus and\n * therefore not worth the AST cost: a destructuring constructor\n * (`constructor({ a, b }: Deps)`) and a class whose public methods are all arrow\n * properties (`handle = async () => {}`) rather than method definitions.\n */\n\nimport { AST_NODE_TYPES, ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\nimport { isGeneratedFile, isScriptFile, isStoryFile, isTestFile } from \"./_paths.js\";\n\ntype MessageIds = \"requireInterface\";\ntype Options = readonly [];\n\n/**\n * Type-name suffixes that mark a constructor parameter as configuration rather\n * than a collaborator. Deliberately short: every entry here is a silent false\n * negative for anyone who names a real dependency that way, so it lists only\n * what the corpus proved (`*Options`, `*Callbacks`) plus the immediate synonyms.\n * `Handler` / `Listener` / `Store` / `Client` are pointedly ABSENT — those are\n * exactly the collaborators the rule exists to notice.\n */\nconst CONFIGISH_TYPE_RE =\n /(?:Options|Opts|Config|Configuration|Settings|Params|Props|Args|Env|Environment|Callbacks|Flags)$/;\n\n/**\n * Parameter names that carry the same signal from the other side, plus the two\n * ambient-capability names the brief called out as weak evidence of a seam: a\n * class whose ONLY dependency is a logger or a clock is not the substitutability\n * problem this rule is about.\n */\nconst CONFIGISH_NAME_RE =\n /^(?:options|opts|config|configuration|settings|params|props|args|env|environment|callbacks|flags|logger|log|clock)$/i;\n\n/**\n * Third-party HTTP transports. A class whose ONLY stored collaborator is one of\n * these is a thin wrapper over somebody else's wire client: the Python side of\n * this standard excludes `*Client` for exactly this reason — an ABC over a class\n * whose collaborator is somebody else's HTTP transport substitutes nothing, and\n * a consumer's test fakes the transport (`respx`, `nock`, `msw`), not the wrapper.\n */\nconst HTTP_TRANSPORT_TYPE_RE = /^(?:KyInstance|AxiosInstance|Session)$/;\n\n/** Only the `*Client` naming carries the guard; see the transport-wrapper note in the header. */\nconst TRANSPORT_WRAPPER_NAME_RE = /Client$/;\n\n/** `express.Router()` / `Router()` — the router-factory call that marks HTTP wiring. */\nconst ROUTER_FACTORY_NAME = \"Router\";\n\n/** Namespaced framework HTTP types (`express.Request`), never the DOM globals of the same name. */\nconst FRAMEWORK_HTTP_TYPES: ReadonlySet<string> = new Set([\"Request\", \"Response\", \"NextFunction\"]);\n\ninterface Collaborator {\n readonly name: string;\n /** Rightmost segment of the type name — what the config-ish guards test. */\n readonly typeName: string;\n /** Full source spelling, including any namespace qualifier, for the message. */\n readonly display: string;\n}\n\n/** The class's own declaration is what `export`s it; anonymous defaults have no name to report. */\nconst isExportedClass = (node: TSESTree.ClassDeclaration): boolean =>\n node.parent.type === AST_NODE_TYPES.ExportNamedDeclaration ||\n node.parent.type === AST_NODE_TYPES.ExportDefaultDeclaration;\n\n/** `ashby.Client` -> `\"ashby.Client\"`; a nested qualifier is flattened the same way. */\nconst qualifiedName = (name: TSESTree.EntityName): string =>\n name.type === AST_NODE_TYPES.Identifier\n ? name.name\n : name.type === AST_NODE_TYPES.TSQualifiedName\n ? `${qualifiedName(name.left)}.${name.right.name}`\n : \"\";\n\nconst typeReferenceName = (annotated: TSESTree.Parameter): Collaborator | null => {\n let target: TSESTree.Node = annotated;\n if (target.type === AST_NODE_TYPES.TSParameterProperty) target = target.parameter;\n if (target.type === AST_NODE_TYPES.AssignmentPattern) target = target.left;\n if (target.type !== AST_NODE_TYPES.Identifier) return null;\n\n const annotation = target.typeAnnotation?.typeAnnotation;\n // A bare type reference is the only shape that names a nominal collaborator.\n // An inline `{ a: string }`, a union, an array, or a function type is data or\n // a callback, never something a consumer would want to swap wholesale.\n if (annotation === undefined || annotation.type !== AST_NODE_TYPES.TSTypeReference) {\n return null;\n }\n const { typeName } = annotation;\n const rightmost =\n typeName.type === AST_NODE_TYPES.Identifier\n ? typeName.name\n : typeName.type === AST_NODE_TYPES.TSQualifiedName\n ? typeName.right.name\n : null;\n if (rightmost === null) return null;\n return { name: target.name, typeName: rightmost, display: qualifiedName(typeName) };\n};\n\ninterface ConstructorFacts {\n /** Parameters assigned to an instance field, either as a parameter property or in the body. */\n readonly collaborators: readonly Collaborator[];\n /** How many instance fields the constructor fills with a `new` expression. */\n readonly constructedFields: number;\n}\n\nconst readConstructor = (ctor: TSESTree.MethodDefinition): ConstructorFacts => {\n const body = ctor.value.body;\n const storedFrom = new Set<string>();\n let constructedFields = 0;\n\n if (body !== null && body !== undefined) {\n for (const statement of body.body) {\n if (statement.type !== AST_NODE_TYPES.ExpressionStatement) continue;\n const expression = statement.expression;\n if (\n expression.type !== AST_NODE_TYPES.AssignmentExpression ||\n expression.operator !== \"=\" ||\n expression.left.type !== AST_NODE_TYPES.MemberExpression ||\n expression.left.object.type !== AST_NODE_TYPES.ThisExpression\n ) {\n continue;\n }\n const source = expression.right;\n if (source.type === AST_NODE_TYPES.NewExpression) {\n constructedFields += 1;\n } else if (source.type === AST_NODE_TYPES.Identifier) {\n // `this.svc = svc`\n storedFrom.add(source.name);\n } else if (\n source.type === AST_NODE_TYPES.MemberExpression &&\n source.object.type === AST_NODE_TYPES.Identifier\n ) {\n // `this.slack = deps.slack` — a dependency bag spread onto fields.\n storedFrom.add(source.object.name);\n }\n }\n }\n\n const collaborators: Collaborator[] = [];\n for (const parameter of ctor.value.params) {\n const reference = typeReferenceName(parameter);\n if (reference === null) continue;\n const stored =\n parameter.type === AST_NODE_TYPES.TSParameterProperty || storedFrom.has(reference.name);\n if (!stored) continue;\n if (CONFIGISH_TYPE_RE.test(reference.typeName)) continue;\n if (CONFIGISH_NAME_RE.test(reference.name)) continue;\n collaborators.push(reference);\n }\n\n return { collaborators, constructedFields };\n};\n\n/** Walk every descendant node, `parent` links excluded, until `found` returns true. */\nconst subtreeHas = (root: TSESTree.Node, found: (node: TSESTree.Node) => boolean): boolean => {\n let hit = false;\n const visit = (current: TSESTree.Node): void => {\n if (hit) return;\n if (found(current)) {\n hit = true;\n return;\n }\n for (const key of Object.keys(current) as (keyof TSESTree.Node)[]) {\n if (key === \"parent\") continue;\n const value = current[key];\n for (const child of (Array.isArray(value) ? value : [value]) as unknown[]) {\n if (\n child !== null &&\n typeof child === \"object\" &&\n typeof (child as { type?: unknown }).type === \"string\"\n ) {\n visit(child as TSESTree.Node);\n }\n }\n }\n };\n visit(root);\n return hit;\n};\n\n/**\n * Report whether the class body is HTTP framework wiring: it manufactures a\n * router (`express.Router()`), or it handles the framework's own namespaced\n * request/response objects. Such a class is mounted by the server's bootstrap,\n * never injected into anything, so there is no consumer to give a port to.\n */\nconst isFrameworkWiring = (body: TSESTree.ClassBody): boolean =>\n subtreeHas(body, (node) => {\n if (node.type === AST_NODE_TYPES.CallExpression) {\n const { callee } = node;\n if (callee.type === AST_NODE_TYPES.Identifier) return callee.name === ROUTER_FACTORY_NAME;\n return (\n callee.type === AST_NODE_TYPES.MemberExpression &&\n !callee.computed &&\n callee.property.type === AST_NODE_TYPES.Identifier &&\n callee.property.name === ROUTER_FACTORY_NAME\n );\n }\n // `express.Request` — qualified on purpose, so the DOM `Request`/`Response`\n // globals a Workers `fetch` handler names do not read as express wiring.\n return (\n node.type === AST_NODE_TYPES.TSTypeReference &&\n node.typeName.type === AST_NODE_TYPES.TSQualifiedName &&\n FRAMEWORK_HTTP_TYPES.has(node.typeName.right.name)\n );\n });\n\n/** `IFooClient` / `FooClientImpl` both reduce to `FooClient`, the three impl conventions the corpus uses. */\nconst stem = (name: string): string => name.replace(/^I(?=[A-Z])/, \"\").replace(/Impl$/, \"\");\n\n/** Interface declarations at the top level of this module, exported or not. */\nconst fileInterfaceNames = (program: TSESTree.Program): string[] => {\n const names: string[] = [];\n for (const statement of program.body) {\n const declaration =\n statement.type === AST_NODE_TYPES.ExportNamedDeclaration ? statement.declaration : statement;\n if (declaration?.type === AST_NODE_TYPES.TSInterfaceDeclaration) names.push(declaration.id.name);\n }\n return names;\n};\n\n/**\n * Report whether the class is a thin wrapper over somebody else's HTTP transport.\n *\n * Three conditions, all necessary. The class receives exactly ONE collaborator\n * and that collaborator is a third-party transport, so there is no domain seam\n * hiding behind it. It is named `*Client`, which is the arm that keeps kashta's\n * `HttpCallService` and summer's `DocumentParserImpl` — both `AxiosInstance`-only\n * — firing, because those wrap a transport in a *domain* operation and their\n * consumers do have something to substitute. And no interface in the same file\n * shares its stem, because an in-file port the class silently fails to\n * `implements` is the drift this rule exists to catch.\n */\nconst isTransportWrapper = (\n className: string,\n collaborators: readonly Collaborator[],\n program: TSESTree.Program,\n): boolean => {\n const [only] = collaborators;\n if (collaborators.length !== 1 || only === undefined) return false;\n if (!HTTP_TRANSPORT_TYPE_RE.test(only.typeName)) return false;\n if (!TRANSPORT_WRAPPER_NAME_RE.test(className)) return false;\n const target = stem(className);\n return !fileInterfaceNames(program).some((name) => {\n const other = stem(name);\n return target.endsWith(other) || other.endsWith(target);\n });\n};\n\n/**\n * Public instance methods only. `kind === \"method\"` excludes getters and setters\n * on purpose: a class that exposes only accessors is a value object, and the\n * constructor is not a seam worth porting.\n */\nconst publicMethodNames = (body: TSESTree.ClassBody): string[] => {\n const names: string[] = [];\n for (const member of body.body) {\n if (member.type !== AST_NODE_TYPES.MethodDefinition) continue;\n if (member.kind !== \"method\" || member.static) continue;\n if (member.accessibility === \"private\" || member.accessibility === \"protected\") continue;\n if (member.key.type === AST_NODE_TYPES.PrivateIdentifier) continue;\n if (member.key.type === AST_NODE_TYPES.Identifier) names.push(member.key.name);\n else names.push(\"…\");\n }\n return names;\n};\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"require-interface-for-injected-service\",\n meta: {\n type: \"suggestion\",\n docs: {\n description:\n \"An exported service class with constructor-injected collaborators must implement an interface, so consumers depend on a port they can substitute instead of mocking the class.\",\n },\n schema: [],\n messages: {\n requireInterface:\n \"`{{name}}` stores injected collaborator(s) ({{deps}}) but implements no interface, so every consumer depends on this concrete class and can only be tested by mocking it. Declare an interface with its public method signature(s) ({{methods}}) and `class {{name}} implements <Interface>`.\",\n },\n },\n defaultOptions: [],\n create(context) {\n const { filename } = context;\n if (isTestFile(filename) || isStoryFile(filename) || isScriptFile(filename)) return {};\n if (isGeneratedFile(filename, context.sourceCode.getText())) return {};\n\n return {\n ClassDeclaration(node: TSESTree.ClassDeclaration): void {\n if (node.id === null) return;\n if (!isExportedClass(node)) return;\n // The port itself must never fire, and a class that extends anything —\n // an abstract base, a framework class, `Error` — already has one.\n if (node.abstract === true) return;\n if (node.superClass !== null) return;\n if (node.implements.length > 0) return;\n // A framework that instantiates by class token (NestJS `@Injectable()`)\n // makes the class its own contract.\n if (node.decorators.length > 0) return;\n\n const ctor = node.body.body.find(\n (member): member is TSESTree.MethodDefinition =>\n member.type === AST_NODE_TYPES.MethodDefinition && member.kind === \"constructor\",\n );\n if (ctor === undefined) return;\n\n const { collaborators, constructedFields } = readConstructor(ctor);\n if (collaborators.length === 0) return;\n // A composition root BUILDS more than it RECEIVES — that is where\n // concrete types are supposed to be named, and a port above it protects\n // nobody. Constructing one internal helper while receiving a real\n // collaborator is an ordinary service, not a wiring class, so the test\n // is a ratio rather than the mere presence of a `new`.\n if (constructedFields > collaborators.length) return;\n // HTTP wiring: a router factory is mounted by the bootstrap, not injected.\n if (isFrameworkWiring(node.body)) return;\n // A lone third-party transport is not a seam a port could protect.\n if (isTransportWrapper(node.id.name, collaborators, context.sourceCode.ast)) return;\n\n const methods = publicMethodNames(node.body);\n if (methods.length === 0) return;\n\n context.report({\n node: node.id,\n messageId: \"requireInterface\",\n data: {\n name: node.id.name,\n deps: collaborators.map((c) => `${c.name}: ${c.display}`).join(\", \"),\n methods: methods.join(\", \"),\n },\n });\n },\n };\n },\n});\n","/**\n * @fileoverview Prefer non-null arrays in declared TypeScript data shapes.\n *\n * An array type such as `OrganizationId[] | null` gives an empty collection two\n * representations: nullish and `[]`. Requiring an array removes null checks\n * from the call chain and makes the project-wide contract explicit.\n *\n * The rule checks declared data shapes: interface/class properties, properties\n * in object type aliases, and direct array type aliases. An optional property\n * (`items?: T[]`) does not fire unless its written type also explicitly includes\n * `null` or `undefined`: omission is API input syntax, not a nullable collection\n * value. Mixed scalar-or-array unions do not fire because they model more than\n * an empty collection. Function-local annotations, tests, and generated\n * declarations are exempt.\n *\n * This is an opinionated application convention, not a TypeScript type-system\n * fact. When null is a meaningful third state (for example React Router uses\n * `matches: Match[] | null` to distinguish \"no match\" from an empty match set),\n * retain the union with an inline ESLint disable and its reason.\n *\n * Corpus sweep (2026-07-27): FastAPI, Pydantic, SQLModel, Zod, and React Router;\n * 2,901 Python/TypeScript files total. The final TypeScript rule reported 13\n * explicit nullable-array declarations. Every match had the advertised AST\n * shape; optional-only properties, tests, generated files, and vendor code\n * produced no reports.\n *\n */\n\nimport { AST_NODE_TYPES, ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\nimport { isGeneratedFile, isTestFile } from \"./_paths.js\";\n\ntype MessageIds = \"preferNonNullableCollection\";\ntype Options = readonly [];\n\nconst ARRAY_TYPE_NAMES: ReadonlySet<string> = new Set([\"Array\", \"ReadonlyArray\"]);\n\nfunction propertyName(node: TSESTree.TSPropertySignature | TSESTree.PropertyDefinition): string {\n const key = node.key;\n if (key.type === AST_NODE_TYPES.Identifier) return key.name;\n if (key.type === AST_NODE_TYPES.Literal) return String(key.value);\n return \"collection\";\n}\n\nfunction isArrayType(node: TSESTree.TypeNode): boolean {\n if (node.type === AST_NODE_TYPES.TSArrayType) return true;\n return (\n node.type === AST_NODE_TYPES.TSTypeReference &&\n node.typeName.type === AST_NODE_TYPES.Identifier &&\n ARRAY_TYPE_NAMES.has(node.typeName.name)\n );\n}\n\nfunction isNullishType(node: TSESTree.TypeNode): boolean {\n return node.type === AST_NODE_TYPES.TSNullKeyword || node.type === AST_NODE_TYPES.TSUndefinedKeyword;\n}\n\nfunction isNullableArrayOnly(node: TSESTree.TSUnionType): boolean {\n const values = node.types.filter((member) => !isNullishType(member));\n return values.length > 0 && values.length < node.types.length && values.every(isArrayType);\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) => `https://github.com/sarj-ai/standards/tree/main/packages/typescript#${name}`,\n)<Options, MessageIds>({\n name: \"prefer-non-nullable-collection\",\n meta: {\n type: \"suggestion\",\n docs: {\n description:\n \"Require array types to use an empty array instead of explicit nullish unions with two equivalent empty states.\",\n },\n schema: [],\n messages: {\n preferNonNullableCollection:\n \"`{{name}}` is a nullable array, so nullish and `[]` represent the same empty collection. Use a non-null array and default omitted values to `[]`.\",\n },\n },\n defaultOptions: [],\n create(context) {\n const normalizedFilename = context.filename.replaceAll(\"\\\\\", \"/\");\n if (\n isTestFile(context.filename) ||\n isGeneratedFile(context.filename, context.sourceCode.text) ||\n /\\/(?:vendor|vendored)\\//u.test(normalizedFilename)\n ) {\n return {};\n }\n\n function checkOptionalProperty(\n node: TSESTree.TSPropertySignature | TSESTree.PropertyDefinition,\n ): void {\n const annotation = node.typeAnnotation?.typeAnnotation;\n if (annotation === undefined) return;\n if (annotation.type !== AST_NODE_TYPES.TSUnionType || !isNullableArrayOnly(annotation)) {\n return;\n }\n context.report({\n node,\n messageId: \"preferNonNullableCollection\",\n data: { name: propertyName(node) },\n });\n }\n\n return {\n TSPropertySignature: checkOptionalProperty,\n PropertyDefinition: checkOptionalProperty,\n TSTypeAliasDeclaration(node): void {\n if (node.typeAnnotation.type !== AST_NODE_TYPES.TSUnionType) return;\n if (!isNullableArrayOnly(node.typeAnnotation)) return;\n context.report({\n node,\n messageId: \"preferNonNullableCollection\",\n data: { name: node.id.name },\n });\n },\n };\n },\n});\n","/**\n * @fileoverview Flag a module whose filename stem does not match its primary export.\n *\n * Semantic File Naming Rule:\n * When a TypeScript/TSX module has a single primary export (a sole exported\n * function, class, or React component, or a default export), its filename stem\n * should semantically reflect that export's name in kebab-case.\n *\n * Examples:\n * - File `user-helpers.tsx` exporting sole `UserProfileCard` -> rename to `user-profile-card.tsx`.\n * - File `account-stuff.ts` exporting sole `AccountService` -> rename to `account-service.ts`.\n * - File `auth-hook.ts` exporting sole `useAuthSession` -> rename to `use-auth-session.ts`.\n *\n * Exemptions (Corpus-validated against noura-be, bulbul, and top TS repos):\n * - Barrel re-export files (`index.ts`, `index.tsx`).\n * - Declaration files (`*.d.ts`).\n * - Test files (`*.test.ts`, `*.spec.ts`, `isTestFile`).\n * - Framework route files (`page.tsx`, `layout.tsx`, `route.ts`, `$id.tsx`, `[id].tsx`, `__root.tsx`).\n * - Standard framework convention stems (`config`, `constants`, `errors`, `types`).\n * - Entrypoint functions (`main`, `run`, `cli`, `setup`, `teardown`, `execute`).\n * - Conventional exports (`cn`).\n */\n\nimport { AST_NODE_TYPES, ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\nimport { isGeneratedFile, isTestFile } from \"./_paths.js\";\n\ntype MessageIds = \"primaryExportMismatch\";\ntype Options = readonly [];\n\nconst CONVENTIONAL_BUCKET_EXPORTS = new Set([\"cn\"]);\nconst GENERIC_ENTRYPOINTS = new Set([\"main\", \"run\", \"cli\", \"setup\", \"teardown\", \"execute\"]);\n\nconst ACRONYM_OVERRIDES: ReadonlyArray<readonly [RegExp, string]> = [\n [/OAuth/g, \"Oauth\"],\n [/GraphQL/g, \"Graphql\"],\n [/gRPC/g, \"Grpc\"],\n];\n\nconst CAMEL_BOUNDARY_RE = /(?<=[a-z0-9])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])/g;\n\nconst basename = (filename: string): string =>\n filename.split(/[/\\\\]/).pop() ?? filename;\n\nconst stemOf = (base: string): string =>\n base.replace(/\\.(test|spec|config|d|stories)\\.[cm]?[jt]sx?$/i, \"\").replace(/\\.[cm]?[jt]sx?$/i, \"\");\n\nconst kebabCase = (name: string): string => {\n let normalized = name;\n for (const [pattern, replacement] of ACRONYM_OVERRIDES) {\n normalized = normalized.replace(pattern, replacement);\n }\n return normalized.replace(CAMEL_BOUNDARY_RE, \"-\").toLowerCase();\n};\n\nconst isFunctionOrClass = (node: TSESTree.Node): boolean =>\n node.type === AST_NODE_TYPES.ArrowFunctionExpression ||\n node.type === AST_NODE_TYPES.FunctionExpression;\n\ninterface PrimaryExportInfo {\n readonly name: string;\n readonly node: TSESTree.Node;\n readonly count: number;\n readonly isDefault: boolean;\n}\n\nconst findPrimaryExport = (body: readonly TSESTree.ProgramStatement[]): PrimaryExportInfo | null => {\n let exportCount = 0;\n let primaryCandidate: { name: string; node: TSESTree.Node; isDefault: boolean } | null = null;\n\n for (const statement of body) {\n if (statement.type === AST_NODE_TYPES.ExportAllDeclaration) {\n return null; // Barrel file\n }\n\n if (statement.type === AST_NODE_TYPES.ExportDefaultDeclaration) {\n exportCount += 1;\n const decl = statement.declaration;\n if (\n (decl.type === AST_NODE_TYPES.FunctionDeclaration || decl.type === AST_NODE_TYPES.ClassDeclaration) &&\n decl.id !== null\n ) {\n primaryCandidate = { name: decl.id.name, node: statement, isDefault: true };\n } else if (decl.type === AST_NODE_TYPES.Identifier) {\n primaryCandidate = { name: decl.name, node: statement, isDefault: true };\n }\n } else if (statement.type === AST_NODE_TYPES.ExportNamedDeclaration) {\n if (statement.source !== null) {\n return null; // Re-export\n }\n const decl = statement.declaration;\n if (decl === null) {\n exportCount += statement.specifiers.length;\n if (statement.specifiers.length === 1 && primaryCandidate === null) {\n const spec = statement.specifiers[0];\n if (spec && spec.exported.type === AST_NODE_TYPES.Identifier) {\n primaryCandidate = { name: spec.exported.name, node: statement, isDefault: false };\n }\n }\n } else if (decl.type === AST_NODE_TYPES.FunctionDeclaration || decl.type === AST_NODE_TYPES.ClassDeclaration) {\n if (decl.id !== null) {\n exportCount += 1;\n if (primaryCandidate === null) {\n primaryCandidate = { name: decl.id.name, node: statement, isDefault: false };\n }\n }\n } else if (decl.type === AST_NODE_TYPES.VariableDeclaration) {\n for (const declarator of decl.declarations) {\n if (declarator.id.type === AST_NODE_TYPES.Identifier) {\n exportCount += 1;\n if (primaryCandidate === null && declarator.init && isFunctionOrClass(declarator.init)) {\n primaryCandidate = { name: declarator.id.name, node: statement, isDefault: false };\n }\n }\n }\n } else if (decl.type === AST_NODE_TYPES.TSTypeAliasDeclaration || decl.type === AST_NODE_TYPES.TSInterfaceDeclaration) {\n exportCount += 1;\n if (primaryCandidate === null) {\n primaryCandidate = { name: decl.id.name, node: statement, isDefault: false };\n }\n }\n }\n }\n\n if (primaryCandidate === null || exportCount > 2) {\n return null;\n }\n\n return { ...primaryCandidate, count: exportCount };\n};\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"primary-export-file-name\",\n meta: {\n type: \"suggestion\",\n docs: {\n description:\n \"Enforce semantic naming alignment: a file with a single primary export must be named after that export.\",\n },\n schema: [],\n messages: {\n primaryExportMismatch:\n \"Module stem '{{stem}}' does not match its primary export '{{name}}' — rename the file to '{{expected}}{{ext}}' to describe its responsibility.\",\n },\n },\n defaultOptions: [],\n create(context) {\n const base = basename(context.filename);\n\n if (base.endsWith(\".d.ts\") || base.startsWith(\"index.\") || base.startsWith(\"_\")) return {};\n if (isTestFile(context.filename) || isGeneratedFile(context.filename, context.sourceCode.text)) return {};\n\n const stem = stemOf(base);\n // Skip framework routes like page.tsx, layout.tsx, route.ts, $id.tsx, [id].tsx and convention stems\n if (\n stem === \"page\" ||\n stem === \"layout\" ||\n stem === \"loading\" ||\n stem === \"error\" ||\n stem === \"not-found\" ||\n stem === \"route\" ||\n stem === \"__root\" ||\n stem === \"config\" ||\n stem === \"constants\" ||\n stem === \"types\" ||\n stem.startsWith(\"$\") ||\n stem.startsWith(\"[\")\n ) {\n return {};\n }\n\n return {\n Program(node: TSESTree.Program): void {\n const primary = findPrimaryExport(node.body);\n if (primary === null) return;\n if (CONVENTIONAL_BUCKET_EXPORTS.has(primary.name) || GENERIC_ENTRYPOINTS.has(primary.name)) return;\n\n const expectedStem = kebabCase(primary.name);\n if (stem === expectedStem) return;\n\n // Ensure extension is preserved (.tsx if component/JSX or .ts)\n const ext = base.endsWith(\".tsx\") ? \".tsx\" : \".ts\";\n\n context.report({\n node: primary.node,\n messageId: \"primaryExportMismatch\",\n data: { stem, name: primary.name, expected: expectedStem, ext },\n });\n },\n };\n },\n});\n","import { AST_NODE_TYPES, ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\ntype MessageIds = \"noImplicitAttributeAccess\";\n\nconst EXCLUDED_BASES = new Set([\n \"environ\", \"headers\", \"cookies\", \"session\", \"redis\", \"cache\", \n \"state\", \"config\", \"env\", \"process\", \"localStorage\", \"sessionStorage\"\n]);\n\nfunction getBaseName(node: TSESTree.Node): string | null {\n if (node.type === AST_NODE_TYPES.Identifier) {\n return node.name;\n }\n if (node.type === AST_NODE_TYPES.MemberExpression && node.property.type === AST_NODE_TYPES.Identifier) {\n return node.property.name;\n }\n return null;\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<[], MessageIds>({\n name: \"no-implicit-attribute-access\",\n meta: {\n type: \"problem\",\n docs: {\n description: \"Disallow imperative dictionary access with string literals; use declarative Zod schemas.\",\n },\n schema: [],\n messages: {\n noImplicitAttributeAccess:\n \"Imperative lookup for '{{key}}' — if the key is known, parse the object declaratively with a Zod schema instead.\",\n },\n },\n defaultOptions: [],\n create(context) {\n return {\n MemberExpression(node: TSESTree.MemberExpression): void {\n if (!node.computed) {\n return;\n }\n \n // We are looking for something like: foo[\"price\"]\n if (node.property.type === AST_NODE_TYPES.Literal && typeof node.property.value === \"string\") {\n const baseName = getBaseName(node.object);\n if (!baseName || !EXCLUDED_BASES.has(baseName)) {\n context.report({\n node,\n messageId: \"noImplicitAttributeAccess\",\n data: { key: node.property.value },\n });\n }\n }\n },\n CallExpression(node: TSESTree.CallExpression): void {\n const callee = node.callee;\n if (callee.type === AST_NODE_TYPES.MemberExpression) {\n const method = callee.property.type === AST_NODE_TYPES.Identifier ? callee.property.name : null;\n if (method === \"get\") {\n const arg = node.arguments[0];\n if (arg && arg.type === AST_NODE_TYPES.Literal && typeof arg.value === \"string\") {\n const baseName = getBaseName(callee.object);\n if (!baseName || !EXCLUDED_BASES.has(baseName)) {\n context.report({\n node,\n messageId: \"noImplicitAttributeAccess\",\n data: { key: arg.value },\n });\n }\n }\n }\n }\n }\n };\n },\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACSA,mBAA2D;;;ACC3D,IAAM,iBAAiB;AACvB,IAAM,gBAAgB;AACtB,IAAM,oBACJ;AACF,IAAM,sBACJ;AAmBK,SAAS,WAAW,UAA2B;AACpD,QAAM,aAAa,SAAS,WAAW,MAAM,GAAG;AAChD,QAAM,OAAO,WAAW,MAAM,WAAW,YAAY,GAAG,IAAI,CAAC;AAC7D,MAAI,uCAAuC,KAAK,IAAI,KAAK,+BAA+B,KAAK,IAAI,GAAG;AAClG,WAAO;AAAA,EACT;AACA,SAAO,gEAAgE,KAAK,UAAU;AACxF;AAEO,SAAS,YAAY,UAA2B;AACrD,SAAO,cAAc,KAAK,QAAQ;AACpC;AAEO,SAAS,gBAAgB,UAAkB,aAAa,IAAa;AAC1E,SAAO,kBAAkB,KAAK,SAAS,WAAW,MAAM,GAAG,CAAC,KAAK,oBAAoB,KAAK,WAAW,MAAM,GAAG,IAAI,CAAC;AACrH;AAOO,SAAS,aAAa,UAA2B;AACtD,SAAO,eAAe,KAAK,QAAQ;AACrC;;;ADnBA,IAAM,oBAAoB,CACxB,cACkB;AAClB,UAAQ,UAAU,MAAM;AAAA,IACtB,KAAK,4BAAe;AAClB,aAAO;AAAA,IACT,KAAK,4BAAe;AAClB,aAAO;AAAA,IACT,KAAK,4BAAe;AAGlB,aAAO,UAAU,gBAAgB,OAAO,aAAa;AAAA,IACvD;AACE,aAAO;AAAA,EACX;AACF;AAEA,IAAM,oBAAoB,CAAC,cACzB,UAAU,SAAS,4BAAe,uBAClC,UAAU,WAAW,SAAS,4BAAe,WAC7C,OAAO,UAAU,WAAW,UAAU,YACtC,UAAU,WAAW,MAAM,WAAW,MAAM;AAE9C,IAAM,uBAAuB,CAC3B,cACY;AACZ,MAAI,UAAU,SAAS,4BAAe,oBAAqB,QAAO;AAClE,QAAM,OAAO,UAAU;AACvB,MAAI,KAAK,SAAS,4BAAe,QAAS,QAAO;AACjD,SAAO,KAAK,UAAU;AACxB;AAEA,IAAO,iCAAQ,yBAAY;AAAA,EACzB,CAAC,SACC,8EAA8E,IAAI;AACtF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,UAAU;AAAA,MACR,cACE;AAAA,MACF,oBACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC;AAAA,EACjB,OAAO,SAAS;AACd,QAAI,WAAW,QAAQ,QAAQ,KAAK,gBAAgB,QAAQ,UAAU,QAAQ,WAAW,IAAI,GAAG;AAC9F,aAAO,CAAC;AAAA,IACV;AAEA,WAAO;AAAA,MACL,QAAQ,MAA8B;AACpC,cAAM,OAAO,KAAK;AAElB,cAAM,qBAAqB,KAAK;AAAA,UAC9B,CAAC,WAAW,UAAU,QAAQ,KAAK,qBAAqB,SAAS;AAAA,QACnE;AACA,YAAI,uBAAuB,QAAW;AACpC,kBAAQ,OAAO;AAAA,YACb,MAAM;AAAA,YACN,WAAW;AAAA,UACb,CAAC;AAAA,QACH;AAEA,YAAI,WAAW;AASf,YAAI,iBAAiB;AAErB,mBAAW,aAAa,MAAM;AAC5B,cAAI,kBAAkB,SAAS,EAAG;AAElC,kBAAQ,kBAAkB,SAAS,GAAG;AAAA,YACpC,KAAK;AACH;AAAA,YACF,KAAK;AACH,yBAAW;AACX,+BAAiB;AACjB;AAAA,YACF,KAAK;AACH,kBAAI,YAAY,CAAC,gBAAgB;AAC/B,iCAAiB;AACjB,wBAAQ,OAAO;AAAA,kBACb,MAAM;AAAA,kBACN,WAAW;AAAA,gBACb,CAAC;AAAA,cACH;AACA;AAAA,UACJ;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;AEhHD,IAAAA,gBAIO;AAKP,IAAM,aAAkC,oBAAI,IAAI,CAAC,SAAS,MAAM,YAAY,CAAC;AAI7E,IAAM,oBAAyC,oBAAI,IAAI;AAAA,EACrD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAMD,IAAM,qBAA0C,oBAAI,IAAI;AAAA,EACtD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,SAAS,iBAAiB,MAAwC;AAChE,QAAM,SAAS,KAAK;AAGpB,MAAI,OAAO,SAAS,6BAAe,YAAY;AAC7C,WAAO,OAAO,SAAS,eAAe,OAAO,SAAS;AAAA,EACxD;AAGA,MACE,OAAO,SAAS,6BAAe,oBAC/B,CAAC,OAAO,YACR,OAAO,OAAO,SAAS,6BAAe,cACtC,OAAO,OAAO,SAAS,WACvB,OAAO,SAAS,SAAS,6BAAe,YACxC;AACA,WACE,OAAO,SAAS,SAAS,eACzB,OAAO,SAAS,SAAS;AAAA,EAE7B;AAEA,SAAO;AACT;AAMA,SAAS,mBACP,YACe;AACf,MAAI,CAAC,cAAc,WAAW,SAAS,6BAAe,kBAAkB;AACtE,WAAO;AAAA,EACT;AACA,aAAW,QAAQ,WAAW,YAAY;AACxC,QAAI,KAAK,SAAS,6BAAe,SAAU;AAC3C,QAAI,KAAK,SAAU;AACnB,UAAM,MAAM,KAAK;AACjB,UAAM,mBACH,IAAI,SAAS,6BAAe,cAAc,IAAI,SAAS,YACvD,IAAI,SAAS,6BAAe,WAAW,IAAI,UAAU;AACxD,QAAI,CAAC,iBAAkB;AACvB,QACE,KAAK,MAAM,SAAS,6BAAe,WACnC,OAAO,KAAK,MAAM,UAAU,UAC5B;AACA,aAAO,KAAK,MAAM,MAAM,YAAY;AAAA,IACtC;AACA,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,SAAS,YAAY,MAAwC;AAC3D,QAAM,SAAS,KAAK;AAGpB,MACE,OAAO,SAAS,6BAAe,cAC/B,OAAO,SAAS,SAChB;AACA,UAAM,SAAS,mBAAmB,KAAK,UAAU,CAAC,CAAC;AACnD,QAAI,WAAW,QAAQ,WAAW,OAAO;AACvC,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AAGA,MACE,OAAO,SAAS,6BAAe,oBAC/B,CAAC,OAAO,YACR,OAAO,OAAO,SAAS,6BAAe,cACtC,WAAW,IAAI,OAAO,OAAO,IAAI,KACjC,OAAO,SAAS,SAAS,6BAAe,YACxC;AAGA,WAAO,kBAAkB,IAAI,OAAO,SAAS,IAAI;AAAA,EACnD;AAGA,MACE,OAAO,SAAS,6BAAe,eAC9B,OAAO,SAAS,WAAW,OAAO,SAAS,OAC5C;AACA,UAAM,WAAW,KAAK,UAAU,CAAC;AACjC,UAAM,YAAY,KAAK,UAAU,CAAC;AAClC,QAAI;AACJ,QAAI,UAAU,SAAS,6BAAe,kBAAkB;AACtD,kBAAY;AAAA,IACd,WAAW,WAAW,SAAS,6BAAe,kBAAkB;AAC9D,kBAAY;AAAA,IACd;AACA,UAAM,SAAS,mBAAmB,SAAS;AAC3C,QAAI,WAAW,QAAQ,WAAW,OAAO;AACvC,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEA,SAAS,iBAAiB,MAAuC;AAC/D,QAAM,WAAW,KAAK,UAAU,CAAC;AACjC,MAAI,CAAC,SAAU,QAAO;AAEtB,MACE,SAAS,SAAS,6BAAe,WACjC,OAAO,SAAS,UAAU,UAC1B;AACA,WAAO,SAAS;AAAA,EAClB;AACA,MAAI,SAAS,SAAS,6BAAe,iBAAiB;AACpD,WAAO,SAAS,OAAO,IAAI,CAAC,MAAM,EAAE,MAAM,MAAM,EAAE,KAAK,EAAE;AAAA,EAC3D;AACA,MAAI,SAAS,SAAS,6BAAe,YAAY;AAC/C,WAAO,SAAS;AAAA,EAClB;AACA,SAAO;AACT;AAEA,SAAS,gBAAgB,MAAwC;AAC/D,QAAM,MAAM,iBAAiB,IAAI,EAAE,YAAY;AAC/C,MAAI,QAAQ,GAAI,QAAO;AAGvB,SAAO,IACJ,MAAM,MAAM,EACZ,KAAK,CAAC,YAAY,mBAAmB,IAAI,OAAO,CAAC;AACtD;AAEA,IAAO,uCAAQ,0BAAY;AAAA,EACzB,CAAC,SACC,8EAA8E,IAAI;AACtF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,UAAU;AAAA,MACR,eACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC;AAAA,EACjB,OAAO,SAAS;AACd,QAAI,cAAc;AAClB,WAAO;AAAA,MACL,eAAe,MAAqC;AAClD,YAAI,iBAAiB,IAAI,GAAG;AAC1B,yBAAe;AACf;AAAA,QACF;AACA,YAAI,gBAAgB,EAAG;AACvB,YAAI,CAAC,YAAY,IAAI,EAAG;AACxB,YAAI,gBAAgB,IAAI,EAAG;AAC3B,gBAAQ,OAAO,EAAE,MAAM,WAAW,gBAAgB,CAAC;AAAA,MACrD;AAAA,MACA,sBAAsB,MAAqC;AACzD,YAAI,iBAAiB,IAAI,GAAG;AAC1B,yBAAe;AAAA,QACjB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;AC7LD,IAAAC,gBAA2D;;;ACjC3D,IAAAC,gBAA8C;AAK9C,IAAM,SACJ;AAGF,IAAM,aACJ;AAGF,IAAM,WACJ;AAGF,IAAM,YACJ;AAGF,IAAM,cACJ;AAGF,IAAM,cACJ;AAGF,IAAM,eACJ;AAGF,IAAM,cACJ;AAKF,IAAM,YACJ;AAEF,IAAM,oBAAuC;AAAA,EAC3C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAQO,SAAS,YAAY,MAAuB;AACjD,SAAO,kBAAkB,KAAK,CAAC,WAAW,OAAO,KAAK,IAAI,CAAC;AAC7D;AAQO,SAAS,qBAAqB,MAAuB;AAC1D,SAAO,OAAO,KAAK,IAAI;AACzB;AASO,IAAM,YAAiC,IAAI;AAAA,EAChD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,gCAW8B,MAAM,KAAK;AAC3C;AAEA,IAAM,gBAAgB;AACtB,IAAM,UAAU;AAGT,SAAS,gBAAgB,OAAyB;AACvD,QAAM,QAAkB,CAAC;AACzB,aAAW,SAAS,MAAM,MAAM,MAAM,GAAG;AACvC,eAAW,SAAS,MAAM,MAAM,aAAa,KAAK,CAAC,GAAG;AACpD,YAAM,KAAK,MAAM,YAAY,CAAC;AAAA,IAChC;AAAA,EACF;AACA,SAAO;AACT;AAWO,SAAS,KAAK,MAAsB;AACzC,MAAI,OAAO;AACX,aAAW,UAAU,CAAC,OAAO,OAAO,OAAO,OAAO,MAAM,MAAM,MAAM,GAAG,GAAG;AACxE,QAAI,KAAK,SAAS,MAAM,KAAK,KAAK,SAAS,OAAO,UAAU,GAAG;AAC7D,aAAO,KAAK,MAAM,GAAG,KAAK,SAAS,OAAO,MAAM;AAChD,UAAI,WAAW,SAAS,WAAW,MAAO,QAAO,GAAG,IAAI;AACxD;AAAA,IACF;AAAA,EACF;AACA,SAAO,KAAK,SAAS,GAAG,KAAK,KAAK,SAAS,KAAK,IAAI,KAAK,MAAM,GAAG,EAAE,IAAI;AAC1E;AAGO,SAAS,cAAc,MAAwB;AACpD,QAAM,SAAmB,CAAC;AAC1B,aAAW,SAAS,KAAK,MAAM,OAAO,KAAK,CAAC,GAAG;AAC7C,WAAO,KAAK,GAAG,gBAAgB,KAAK,CAAC;AAAA,EACvC;AACA,SAAO,OAAO,OAAO,CAAC,UAAU,CAAC,UAAU,IAAI,KAAK,CAAC;AACvD;AAGO,SAAS,WAAW,MAA2B;AACpD,QAAM,SAAS,oBAAI,IAAY;AAC/B,aAAW,SAAS,KAAK,MAAM,OAAO,KAAK,CAAC,GAAG;AAC7C,eAAW,QAAQ,gBAAgB,KAAK,EAAG,QAAO,IAAI,IAAI;AAAA,EAC5D;AACA,SAAO;AACT;AASO,SAAS,SAAS,eAAkC,MAAoC;AAC7F,QAAM,QAAQ,oBAAI,IAAY;AAC9B,aAAW,SAAS,KAAM,OAAM,IAAI,KAAK,KAAK,CAAC;AAC/C,SAAO,cAAc,MAAM,CAAC,UAAU,KAAK,IAAI,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,CAAC,CAAC;AACjF;AAIA,IAAM,sBAAsB;AAC5B,IAAM,wBAAwB;AAC9B,IAAM,mBAAmB;AAKzB,IAAM,wBAA6C,oBAAI,IAAI;AAAA,EACzD,6BAAe;AAAA,EACf,6BAAe;AAAA,EACf,6BAAe;AAAA,EACf,6BAAe;AACjB,CAAC;AAID,IAAM,oBACJ;AAIF,IAAM,sBAA2C,oBAAI,IAAI;AAAA,EACvD;AAAA,EAAK;AAAA,EAAO;AAAA,EAAM;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAM;AAAA,EAAM;AAAA,EAAQ;AAAA,EAAM;AAAA,EAAQ;AAAA,EACzE;AAAA,EAAQ;AAAA,EAAO;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAM;AAAA,EAAM;AAAA,EAAQ;AAAA,EAAM;AAAA,EAAM;AAAA,EAAO;AAAA,EACtE;AAAA,EAAO;AAAA,EAAM;AAAA,EAAM;AAAA,EAAO;AAAA,EAAQ;AAAA,EAAM;AAAA,EAAO;AAAA,EAAO;AAAA,EAAQ;AAAA,EAAM;AAAA,EACpE;AAAA,EAAO;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAQ;AACzD,CAAC;AAGM,SAAS,eAAe,MAAsB;AACnD,QAAM,QAAQ,KAAK,YAAY;AAC/B,SAAO,MAAM,SAAS,oBAAoB,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,SAAS,IAAI,IACjF,MAAM,MAAM,GAAG,EAAE,IACjB;AACN;AAGA,SAAS,WAAW,QAAqC;AACvD,QAAM,SAAS,oBAAI,IAAY;AAC/B,aAAW,cAAc,OAAO,MAAM,mBAAmB,KAAK,CAAC,GAAG;AAChE,WAAO,IAAI,eAAe,UAAU,CAAC;AACrC,eAAW,QAAQ,WAAW,MAAM,8BAA8B,GAAG;AACnE,UAAI,KAAK,SAAS,EAAG,QAAO,IAAI,eAAe,IAAI,CAAC;AAAA,IACtD;AAAA,EACF;AACA,SAAO;AACT;AAaA,SAAS,qBAAqB,MAA6C;AACzE,SAAO,KAAK,aAAa,MAAM,CAAC,eAAe;AAC7C,UAAM,OAAO,WAAW;AACxB,QAAI,QAAQ,QAAQ,KAAK,SAAS,6BAAe,QAAS,QAAO;AACjE,WACG,KAAK,SAAS,6BAAe,mBAAmB,KAAK,SAAS,WAAW,KACzE,KAAK,SAAS,6BAAe,oBAAoB,KAAK,WAAW,WAAW;AAAA,EAEjF,CAAC;AACH;AAOO,SAAS,yBAAyB,SAA2B,YAA4C;AAC9G,QAAM,QAAQ,WAAW,cAAc,SAAS,EAAE,iBAAiB,MAAM,CAAC;AAC1E,MAAI,UAAU,QAAQ,MAAM,IAAI,MAAM,SAAS,QAAQ,IAAI,IAAI,OAAO,EAAG,QAAO;AAChF,WACM,OAAyC,WAAW,oBAAoB,MAAM,MAAM,CAAC,CAAC,GAC1F,QAAQ,QAAQ,KAAK,SAAS,6BAAe,SAC7C,OAAO,KAAK,QACZ;AACA,QAAI,CAAC,sBAAsB,IAAI,KAAK,IAAI,EAAG;AAC3C,QAAI,KAAK,IAAI,MAAM,SAAS,MAAM,IAAI,MAAM,QAAQ,KAAK,IAAI,IAAI,SAAS,KAAK,IAAI,MAAM,MAAM;AAC7F,aAAO;AAAA,IACT;AACA,QAAI,KAAK,SAAS,6BAAe,uBAAuB,qBAAqB,IAAI,GAAG;AAClF,aAAO;AAAA,IACT;AACA,WAAO,WAAW,QAAQ,IAAI;AAAA,EAChC;AACA,SAAO;AACT;AAOO,SAAS,sBAAsB,MAAc,WAAmC;AACrF,MAAI,cAAc,KAAM,QAAO;AAC/B,QAAM,QAAQ,KAAK,MAAM,iBAAiB,KAAK,CAAC;AAChD,QAAM,SAAS,MAAM,CAAC;AACtB,MAAI,WAAW,UAAa,MAAM,SAAS,oBAAqB,QAAO;AACvE,MAAI,CAAC,kBAAkB,KAAK,MAAM,EAAG,QAAO;AAC5C,QAAM,UAAU,MACb,MAAM,CAAC,EACP,IAAI,cAAc,EAClB,OAAO,CAAC,SAAS,CAAC,oBAAoB,IAAI,IAAI,CAAC;AAClD,MAAI,QAAQ,SAAS,sBAAuB,QAAO;AACnD,QAAM,OAAO,UAAU,MAAM,GAAG,EAAE,CAAC,KAAK;AACxC,QAAM,OAAO,WAAW,IAAI;AAC5B,SAAO,QAAQ,MAAM,CAAC,SAAS,KAAK,IAAI,IAAI,CAAC;AAC/C;;;ADxOA,IAAM,uBAAuB;AAK7B,IAAM,oBACJ;AAIF,IAAM,qBACJ;AAMF,IAAM,eACJ;AAEF,IAAM,aACJ;AAMF,IAAM,qBAAqB;AAC3B,IAAM,4BAA4B;AAClC,IAAM,gCAAgC;AAEtC,IAAM,iBAAiB;AAKvB,IAAM,gBAAgB;AAWtB,IAAM,mBAAmB;AACzB,IAAM,kBAAkB;AACxB,IAAM,yBAAyB;AAE/B,SAAS,eAAe,MAAuB;AAC7C,QAAM,QAAQ,iBAAiB,KAAK,IAAI;AACxC,MAAI,UAAU,KAAM,QAAO;AAC3B,QAAM,SAAS,MAAM,CAAC,KAAK,IAAI,KAAK;AACpC,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,MAAI,CAAC,gBAAgB,KAAK,KAAK,EAAG,QAAO;AACzC,SAAO,MAAM,MAAM,KAAK,EAAE,UAAU;AACtC;AAOA,IAAM,sBAA2C,oBAAI,IAAI;AAAA,EACvD;AAAA,EAAW;AAAA,EAAc;AAAA,EAAU;AAAA,EAAiB;AAAA,EAAY;AAAA,EAChE;AAAA,EAAS;AAAA,EAAW;AAAA,EAAY;AAAA,EAAW;AAAA,EAAW;AAAA,EAAW;AAAA,EACjE;AAAA,EAAU;AAAA,EAAW;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAW;AAAA,EAAc;AAAA,EAC/D;AAAA,EAAS;AAAA,EAAU;AAAA,EAAa;AAAA,EAAS;AAAA,EAAW;AAAA,EAAY;AAAA,EAChE;AAAA,EAAW;AAAA,EAAa;AAAA,EAAW;AAAA,EAAS;AAAA,EAAS;AAAA,EAAU;AAAA,EAC/D;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAQ;AAAA,EAAa;AACxC,CAAC;AACD,IAAM,mBAAmB;AAEzB,SAAS,eAAe,MAAuB;AAC7C,QAAM,QAAQ,iBAAiB,KAAK,IAAI;AACxC,SAAO,UAAU,QAAQ,oBAAoB,KAAK,MAAM,CAAC,KAAK,IAAI,YAAY,CAAC;AACjF;AAKA,IAAM,mBAAmB;AAMzB,IAAM,UACJ;AAQF,IAAM,iBAAiB;AAGvB,IAAM,uBAAuB;AAW7B,IAAM,mBAAmB;AAEzB,IAAM,kBACJ;AACF,IAAM,eAAe;AAMrB,IAAM,oBACJ;AAIF,IAAM,gBAAgB;AAItB,SAAS,mBAAmB,MAAsB;AAChD,SAAO,KAAK,QAAQ,eAAe,EAAE,EAAE,QAAQ,WAAW,EAAE,EAAE,KAAK;AACrE;AAEA,SAAS,YAAY,MAAuB;AAC1C,SAAO,aAAa,KAAK,KAAK,KAAK,CAAC;AACtC;AAEA,SAAS,SAAS,MAAuB;AACvC,QAAM,IAAI,KAAK,KAAK;AACpB,MAAI,CAAC,EAAG,QAAO;AACf,MAAI,eAAe,KAAK,CAAC,KAAK,eAAe,CAAC,EAAG,QAAO;AACxD,SAAO,cAAc,KAAK,CAAC,KAAK,CAAC,iBAAiB,KAAK,CAAC;AAC1D;AAEA,SAAS,cAAc,MAAuB;AAC5C,QAAM,IAAI,KAAK,KAAK;AACpB,MAAI,CAAC,EAAG,QAAO;AACf,MAAI,gBAAgB,KAAK,CAAC,KAAK,aAAa,KAAK,CAAC,EAAG,QAAO;AAC5D,SAAO,kBAAkB,KAAK,CAAC;AACjC;AAEA,SAAS,cAAc,MAAuB;AAC5C,SAAO,cAAc,KAAK,IAAI;AAChC;AAGA,SAAS,sBAAsB,MAAuB;AACpD,QAAM,IAAI,KAAK,KAAK;AACpB,SAAO,mBAAmB,KAAK,CAAC,KAAK,EAAE,MAAM,KAAK,EAAE,UAAU;AAChE;AAKA,SAAS,QAAQ,MAAuB;AACtC,QAAM,IAAI,KAAK,KAAK;AACpB,MAAI,CAAC,EAAG,QAAO;AACf,MAAI,EAAE,SAAS,GAAG,EAAG,QAAO;AAC5B,MACE,SAAS,KAAK,CAAC,KACf,KAAK,KAAK,CAAC,KACX,QAAQ,KAAK,CAAC,KACd,CAAC,cAAc,CAAC,KAChB,EAAE,MAAM,KAAK,EAAE,UAAU,GACzB;AACA,WAAO;AAAA,EACT;AACA,SAAO;AACT;AA2BA,IAAM,mBACJ;AAmBF,SAAS,qBACP,MACA,gBACA,YACA,qBACA,QACS;AACT,QAAM,IAAI,KAAK,KAAK;AACpB,MAAI,CAAC,KAAK,cAAc,CAAC,KAAK,cAAc,CAAC,EAAG,QAAO;AACvD,MAAI,YAAY;AACd,QAAI,kBAAkB,KAAK,CAAC,EAAG,QAAO;AACtC,QAAI,mBAAmB,KAAK,CAAC,KAAK,CAAC,iBAAiB,KAAK,CAAC,EAAG,QAAO;AACpE,QAAI,iBAAiB,KAAK,CAAC,KAAK,QAAQ,KAAK,CAAC,EAAG,QAAO;AAExD,UAAM,QAAQ,EAAE,MAAM,KAAK;AAC3B,QAAI,MAAM,UAAU,KAAK,qBAAqB,KAAK,CAAC,KAAK,CAAC,SAAS,KAAK,CAAC,GAAG;AAC1E,YAAM,SAAS,EAAE,YAAY;AAC7B,YAAM,iBAAiB,CAAC,QAAQ,WAAW,MAAM,WAAW,UAAU,OAAO,cAAc,cAAc,YAAY,MAAM;AAC3H,UAAI,CAAC,eAAe,KAAK,OAAK,OAAO,SAAS,CAAC,CAAC,GAAG;AACjD,YAAI,MAAM,SAAS,EAAG,QAAO;AAAA,MAC/B;AAAA,IACF;AAEA,QAAI,CAAC,UAAU,eAAe,CAAC,EAAG,QAAO;AACzC,QAAI,uBAAuB,eAAe,KAAK,CAAC,EAAG,QAAO;AAAA,EAC5D;AACA,SAAO,sBAAsB,GAAG,cAAc;AAChD;AAQA,IAAM,uBAA4C,oBAAI,IAAI;AAAA,EACxD,6BAAe;AAAA,EACf,6BAAe;AAAA,EACf,6BAAe;AAAA,EACf,6BAAe;AAAA,EACf,6BAAe;AAAA,EACf,6BAAe;AAAA,EACf,6BAAe;AACjB,CAAC;AAYD,SAAS,mBAAmB,UAAuC,OAAwB;AACzF,WAAS,IAAI,OAAO,KAAK,GAAG,KAAK;AAC/B,QAAI,IAAI,SAAS,CAAC,wBAAwB,SAAS,CAAC,GAAG,SAAS,IAAI,CAAC,CAAC,EAAG;AACzE,QAAI,qBAAqB,mBAAmB,SAAS,CAAC,GAAG,SAAS,EAAE,CAAC,EAAG,QAAO;AAAA,EACjF;AACA,WAAS,IAAI,QAAQ,GAAG,IAAI,SAAS,QAAQ,KAAK;AAChD,QAAI,CAAC,wBAAwB,SAAS,IAAI,CAAC,GAAG,SAAS,CAAC,CAAC,EAAG;AAC5D,QAAI,qBAAqB,mBAAmB,SAAS,CAAC,GAAG,SAAS,EAAE,CAAC,EAAG,QAAO;AAAA,EACjF;AACA,SAAO;AACT;AAGA,SAAS,wBACP,GACA,GACS;AACT,SACE,MAAM,UACN,MAAM,UACN,EAAE,SAAS,UACX,EAAE,SAAS,UACX,EAAE,IAAI,MAAM,SAAS,EAAE,IAAI,IAAI,OAAO;AAE1C;AAMA,SAAS,mBAAmB,UAAuC,OAAwB;AACzF,QAAM,UAAU,SAAS,KAAK;AAC9B,SACE,wBAAwB,SAAS,QAAQ,CAAC,GAAG,OAAO,KACpD,wBAAwB,SAAS,SAAS,QAAQ,CAAC,CAAC;AAExD;AAGA,IAAM,qBAAqB;AAY3B,SAAS,2BACP,UACA,OACS;AACT,WAAS,IAAI,QAAQ,GAAG,KAAK,KAAK,QAAQ,KAAK,oBAAoB,KAAK;AACtE,QAAI,CAAC,wBAAwB,SAAS,CAAC,GAAG,SAAS,IAAI,CAAC,CAAC,EAAG,QAAO;AACnE,UAAM,OAAO,mBAAmB,SAAS,CAAC,GAAG,SAAS,EAAE;AACxD,QAAI,KAAK,SAAS,KAAK,KAAK,SAAS,GAAG,EAAG,QAAO;AAAA,EACpD;AACA,SAAO;AACT;AAEA,SAAS,oBACP,OACA,gBACS;AACT,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,UAAM,OAAO,MAAM,CAAC;AACpB,QAAI,SAAS,UAAa,CAAC,cAAc,IAAI,KAAK,cAAc,IAAI,GAAG;AACrE;AAAA,IACF;AACA,UAAM,OAAO,IAAI,IAAI,MAAM,IAAI,CAAC,IAAI;AACpC,QAAI,SAAS,SAAY,QAAQ,IAAI,IAAI,eAAgB;AACzD,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,IAAO,2BAAQ,0BAAY;AAAA,EACzB,CAAC,SACC,gFAAgF,IAAI;AACxF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,UAAU;AAAA,MACR,kBACE;AAAA,MACF,eACE;AAAA,MACF,oBACE;AAAA,MACF,oBACE;AAAA,MACF,eACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC;AAAA,EACjB,OAAO,SAAS;AACd,QAAI,gBAAgB,QAAQ,UAAU,QAAQ,WAAW,IAAI,GAAG;AAC9D,aAAO,CAAC;AAAA,IACV;AAEA,UAAM,aAAa,QAAQ;AAE3B,aAAS,aAAa,SAAoC;AACxD,YAAM,SAAS,WAAW,eAAe,SAAS;AAAA,QAChD,iBAAiB;AAAA,MACnB,CAAC;AACD,aAAO,CAAC,UAAU,OAAO,IAAI,IAAI,OAAO,QAAQ,IAAI,MAAM;AAAA,IAC5D;AAEA,aAAS,QAAQ,SAAoC;AACnD,aAAO,QAAQ,SAAS,WAAW,MAAM,KAAK,QAAQ,KAAK;AAAA,IAC7D;AAEA,aAAS,sBACP,UACA,eACM;AACN,YAAM,UAA8B,CAAC;AACrC,UAAI,WAA0B;AAC9B,iBAAW,WAAW,UAAU;AAC9B,YAAI,QAAQ,SAAS,OAAQ;AAC7B,YAAI,QAAQ,IAAI,MAAM,QAAQ,cAAe;AAC7C,YAAI,CAAC,aAAa,OAAO,EAAG;AAC5B,cAAM,OAAO,mBAAmB,QAAQ,KAAK;AAC7C,YAAI,YAAY,IAAI,KAAK,KAAK,WAAW,GAAG,EAAG;AAC/C,YAAI,aAAa,QAAQ,QAAQ,IAAI,MAAM,SAAS,WAAW,EAAG;AAClE,gBAAQ,KAAK,OAAO;AACpB,mBAAW,QAAQ,IAAI,MAAM;AAAA,MAC/B;AACA,YAAM,QAAQ,QAAQ,CAAC;AACvB,UAAI,UAAU,UAAa,QAAQ,SAAS,qBAAsB;AAClE,YAAM,SAAS,QAAQ,IAAI,CAAC,MAAM,mBAAmB,EAAE,KAAK,CAAC;AAC7D,UAAI,OAAO,KAAK,CAAC,SAAS,WAAW,KAAK,IAAI,CAAC,EAAG;AAGlD,UAAI,OAAO,KAAK,CAAC,SAAS,QAAQ,IAAI,CAAC,EAAG;AAM1C,UAAI,OAAO,OAAO,qBAAqB,EAAE,UAAU,+BAA+B;AAChF;AAAA,MACF;AACA,cAAQ,OAAO,EAAE,MAAM,OAAO,WAAW,qBAAqB,CAAC;AAAA,IACjE;AAEA,WAAO;AAAA,MACL,UAAgB;AACd,cAAM,WAAW,WAAW,eAAe;AAC3C,cAAM,gBACJ,WAAW,IAAI,OAAO,CAAC,GAAG,IAAI,MAAM,QAAQ,OAAO;AACrD,cAAM,aAAa,SAAS;AAAA,UAC1B,CAAC,MAAM,EAAE,SAAS,UAAU,eAAe,KAAK,mBAAmB,EAAE,KAAK,CAAC;AAAA,QAC7E;AAEA,iBAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACxC,gBAAM,UAAU,SAAS,CAAC;AAC1B,cAAI,YAAY,OAAW;AAC3B,cAAI,QAAQ,OAAO,KAAK,CAAC,aAAa,OAAO,EAAG;AAChD,cAAI,WAAW,KAAK,QAAQ,KAAK,EAAG;AACpC,gBAAM,QAAQ,QAAQ,MACnB,MAAM,IAAI,EACV,IAAI,kBAAkB,EACtB,OAAO,CAAC,MAAM,EAAE,SAAS,KAAK,CAAC,YAAY,CAAC,CAAC;AAEhD,gBAAM,YAAY,MAAM,CAAC;AACzB,cAAI,cAAc,UAAa,qBAAqB,KAAK,SAAS,GAAG;AACnE,gBAAI,CAAC,mBAAmB,UAAU,CAAC,GAAG;AACpC,sBAAQ,OAAO;AAAA,gBACb,MAAM;AAAA,gBACN,WAAW;AAAA,cACb,CAAC;AAAA,YACH;AACA;AAAA,UACF;AAEA,cAAI,MAAM,KAAK,QAAQ,GAAG;AACxB,oBAAQ,OAAO,EAAE,MAAM,SAAS,WAAW,gBAAgB,CAAC;AAC5D;AAAA,UACF;AACA,gBAAM,OAAO,SAAS,IAAI,CAAC;AAC3B,gBAAM,iBACJ,SAAS,UACT,KAAK,SAAS,UACd,KAAK,IAAI,IAAI,SAAS,QAAQ,IAAI,MAAM,OAAO,KAC/C,QAAQ,mBAAmB,KAAK,KAAK,CAAC;AACxC,cACE,oBAAoB,OAAO,cAAc,KACzC,CAAC,2BAA2B,UAAU,CAAC,GACvC;AACA,oBAAQ,OAAO,EAAE,MAAM,SAAS,WAAW,mBAAmB,CAAC;AAC/D;AAAA,UACF;AAGA,cAAI,QAAQ,SAAS,UAAU,MAAM,WAAW,GAAG;AACjD,kBAAM,OAAO,MAAM,CAAC;AACpB,kBAAM,YAAY,yBAAyB,SAAS,UAAU;AAC9D,kBAAM,aAAa,CAAC,mBAAmB,UAAU,CAAC;AAClD,kBAAM,YAAY,WAAW,oBAAoB,QAAQ,MAAM,CAAC,CAAC;AACjE,kBAAM,SAAS,cAAc,QAAQ,CAAC,qBAAqB,IAAI,UAAU,IAAI;AAC7E,gBACE,SAAS,UACT,CAAC,mBAAmB,UAAU,CAAC,KAC/B,qBAAqB,MAAM,WAAW,YAAY,WAAW,WAAW,GAAG,MAAM,GACjF;AACA,sBAAQ,OAAO,EAAE,MAAM,SAAS,WAAW,qBAAqB,CAAC;AAAA,YACnE;AAAA,UACF;AAAA,QACF;AAEA,8BAAsB,UAAU,aAAa;AAAA,MAC/C;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;AErjBD,IAAAC,gBAA2C;AAW3C,IAAM,0BAA6C;AAAA,EACjD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,SAAS,kBACP,UACA,UACS;AACT,aAAW,WAAW,UAAU;AAE9B,UAAM,cAAc,QACjB,QAAQ,qBAAqB,MAAM,EACnC,QAAQ,SAAS,gBAAgB,EACjC,QAAQ,OAAO,WAAW,EAC1B,QAAQ,mBAAmB,IAAI;AAClC,QAAI,IAAI,OAAO,IAAI,WAAW,GAAG,EAAE,KAAK,QAAQ,GAAG;AACjD,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,mBAAmB,YAA6B;AAEvD,QAAM,OAAO,WAAW,MAAM,GAAG,IAAI;AACrC,SAAO,eAAe,KAAK,IAAI;AACjC;AAEA,IAAO,kBAAQ,0BAAY;AAAA,EACzB,CAAC,SACC,8EAA8E,IAAI;AACtF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ;AAAA,MACN;AAAA,QACE,MAAM;AAAA,QACN,sBAAsB;AAAA,QACtB,YAAY;AAAA,UACV,aAAa;AAAA,YACX,MAAM;AAAA,YACN,OAAO,EAAE,MAAM,SAAS;AAAA,UAC1B;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,IACA,UAAU;AAAA,MACR,QACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC,CAAC,CAAC;AAAA,EACnB,OAAO,SAAS,CAAC,UAAU,GAAG;AAC5B,UAAM,UAAU,cAAc,CAAC;AAC/B,UAAM,cAAc,QAAQ,eAAe,CAAC;AAC5C,UAAM,WAAW,QAAQ;AACzB,UAAM,aAAa,QAAQ,WAAW,QAAQ;AAE9C,UAAM,qBAAqB,wBAAwB;AAAA,MAAK,CAAC,OACvD,GAAG,KAAK,QAAQ;AAAA,IAClB;AACA,UAAM,oBACJ,YAAY,SAAS,KAAK,kBAAkB,UAAU,WAAW;AACnE,UAAM,cAAc,mBAAmB,UAAU,KAAK,gBAAgB,UAAU,UAAU;AAE1F,QAAI,sBAAsB,qBAAqB,aAAa;AAC1D,aAAO,CAAC;AAAA,IACV;AAEA,WAAO;AAAA,MACL,kBAAkB,MAAwC;AACxD,gBAAQ,OAAO;AAAA,UACb;AAAA,UACA,WAAW;AAAA,QACb,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;ACzDD,IAAAC,gBAA2C;AAO3C,IAAM,0BACJ;AAEF,IAAM,0BACJ;AAGF,IAAM,qBAAqB;AAK3B,SAAS,iBAAiB,MAAsD;AAC9E,MAAI,KAAK,SAAS,kBAAkB;AAClC,WAAO;AAAA,EACT;AACA,QAAM,SAAS,KAAK;AACpB,MAAI,OAAO,SAAS,sBAAsB,OAAO,UAAU;AACzD,WAAO;AAAA,EACT;AACA,QAAM,EAAE,QAAQ,SAAS,IAAI;AAC7B,SACE,OAAO,SAAS,gBAChB,OAAO,SAAS,UAChB,SAAS,SAAS,gBAClB,SAAS,SAAS;AAEtB;AAQA,SAAS,gBAAgB,MAAoC;AAC3D,MAAI,UAAyB;AAC7B,MAAI,SAAS,QAAQ;AAErB,SAAO,QAAQ;AACb,QACE,OAAO,SAAS,sBAChB,OAAO,WAAW,WAClB,CAAC,OAAO,UACR;AACA,gBAAU;AACV,eAAS,QAAQ;AACjB;AAAA,IACF;AACA,QAAI,OAAO,SAAS,oBAAoB,OAAO,WAAW,SAAS;AACjE,gBAAU;AACV,eAAS,QAAQ;AACjB;AAAA,IACF;AACA;AAAA,EACF;AAEA,SAAO;AACT;AAOA,SAAS,wBAAwB,MAA8B;AAC7D,MAAI,UAAyB;AAC7B,MAAI,SAAS,QAAQ;AAErB,SAAO,QAAQ;AACb,QACE,OAAO,SAAS,sBAChB,OAAO,WAAW,WAClB,CAAC,OAAO,YACR,OAAO,SAAS,SAAS,gBACzB,OAAO,SAAS,SAAS,YACzB;AACA,YAAM,cAAc,OAAO;AAC3B,UACE,eACA,YAAY,SAAS,oBACrB,YAAY,WAAW,QACvB;AACA,cAAM,WAAW,YAAY,UAAU,CAAC;AACxC,YAAI,YAAY,SAAS,SAAS,aAAa,SAAS,UAAU,IAAI;AACpE,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF;AAEA,QAAI,OAAO,SAAS,sBAAsB,OAAO,WAAW,SAAS;AACnE,gBAAU;AACV,eAAS,QAAQ;AACjB;AAAA,IACF;AACA,QAAI,OAAO,SAAS,oBAAoB,OAAO,WAAW,SAAS;AACjE,gBAAU;AACV,eAAS,QAAQ;AACjB;AAAA,IACF;AACA;AAAA,EACF;AAEA,SAAO;AACT;AAUA,SAAS,kBAAkB,MAAyC;AAClE,MAAI,UAAyB;AAC7B,MAAI,SAAS,QAAQ;AAErB,SAAO,QAAQ;AACb,QAAI,OAAO,SAAS,wBAAwB,OAAO,SAAS,SAAS;AACnE,UAAI,OAAO,GAAG,SAAS,cAAc;AACnC,eAAO,OAAO,GAAG;AAAA,MACnB;AACA,aAAO;AAAA,IACT;AAEA,QAAI,OAAO,SAAS,cAAc,OAAO,UAAU,SAAS;AAC1D,YAAM,MAAM,OAAO;AACnB,UAAI,CAAC,OAAO,YAAY,IAAI,SAAS,cAAc;AACjD,eAAO,IAAI;AAAA,MACb;AACA,UAAI,IAAI,SAAS,aAAa,OAAO,IAAI,UAAU,UAAU;AAC3D,eAAO,IAAI;AAAA,MACb;AACA,aAAO;AAAA,IACT;AAEA,QAAI,OAAO,SAAS,wBAAwB,OAAO,UAAU,SAAS;AACpE,YAAM,MAAM,OAAO;AACnB,UAAI,CAAC,OAAO,YAAY,IAAI,SAAS,cAAc;AACjD,eAAO,IAAI;AAAA,MACb;AACA,UAAI,IAAI,SAAS,aAAa,OAAO,IAAI,UAAU,UAAU;AAC3D,eAAO,IAAI;AAAA,MACb;AACA,aAAO;AAAA,IACT;AAEA,QACE,OAAO,SAAS,yBAChB,OAAO,SAAS,wBAChB,OAAO,SAAS,6BAChB,OAAO,SAAS,oBAChB,OAAO,SAAS,qBAChB,OAAO,SAAS,uBAChB;AACA,aAAO;AAAA,IACT;AAEA,cAAU;AACV,aAAS,QAAQ;AAAA,EACnB;AAEA,SAAO;AACT;AAMA,SAAS,yBACP,MACA,KACM;AACN,MAAI,KAAK,SAAS,aAAa,OAAO,KAAK,UAAU,UAAU;AAC7D,QAAI,KAAK,KAAK,KAAK;AACnB;AAAA,EACF;AACA,MAAI,KAAK,SAAS,mBAAmB;AACnC,eAAW,SAAS,KAAK,QAAQ;AAC/B,UAAI,KAAK,MAAM,MAAM,UAAU,MAAM,MAAM,GAAG;AAAA,IAChD;AACA;AAAA,EACF;AACA,MAAI,KAAK,SAAS,sBAAsB,KAAK,aAAa,KAAK;AAC7D,6BAAyB,KAAK,MAAM,GAAG;AACvC,6BAAyB,KAAK,OAAO,GAAG;AAAA,EAC1C;AACF;AAOA,SAAS,8BAA8B,MAA8B;AACnE,QAAM,YAAY,gBAAgB,IAAI;AAEtC,MAAI,UAAyB;AAC7B,MAAI,SAAS,QAAQ;AACrB,MAAI;AAEJ,SAAO,QAAQ;AACb,QACE,OAAO,SAAS,sBAChB,OAAO,aAAa,QACnB,OAAO,SAAS,WAAW,OAAO,UAAU,UAC7C;AACA,YAAM;AACN,gBAAU;AACV,eAAS,QAAQ;AACjB;AAAA,IACF;AACA,QAAI,OAAO,SAAS,mBAAmB;AACrC,YAAM;AACN,gBAAU;AACV,eAAS,QAAQ;AACjB;AAAA,IACF;AACA;AAAA,EACF;AAEA,MAAI,CAAC,KAAK;AACR,WAAO;AAAA,EACT;AAEA,QAAM,QAAkB,CAAC;AACzB,2BAAyB,KAAK,KAAK;AACnC,SAAO,MAAM,KAAK,CAAC,SAAS,mBAAmB,KAAK,IAAI,CAAC;AAC3D;AAEA,IAAO,gCAAQ,0BAAY;AAAA,EACzB,CAAC,SACC,8EAA8E,IAAI;AACtF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,UAAU;AAAA,MACR,kBACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC;AAAA,EACjB,OAAO,SAAS;AACd,QAAI,WAAW,QAAQ,QAAQ,GAAG;AAChC,aAAO,CAAC;AAAA,IACV;AACA,WAAO;AAAA,MACL,eAAe,MAAqC;AAClD,YAAI,CAAC,iBAAiB,IAAI,GAAG;AAC3B;AAAA,QACF;AAEA,cAAM,OAAO,kBAAkB,IAAI;AAInC,YAAI,SAAS,UAAa,wBAAwB,KAAK,IAAI,GAAG;AAC5D,kBAAQ,OAAO,EAAE,MAAM,WAAW,mBAAmB,CAAC;AACtD;AAAA,QACF;AAKA,YAAI,SAAS,UAAa,wBAAwB,KAAK,IAAI,GAAG;AAC5D;AAAA,QACF;AAIA,YAAI,8BAA8B,IAAI,GAAG;AACvC;AAAA,QACF;AAGA,YAAI,wBAAwB,IAAI,GAAG;AACjC,kBAAQ,OAAO,EAAE,MAAM,WAAW,mBAAmB,CAAC;AAAA,QACxD;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;ACzTD,IAAAC,gBAA2C;AAM3C,IAAM,qBAAqB;AAG3B,IAAM,qBAAqB;AAG3B,IAAM,oBAAyC,oBAAI,IAAI,CAAC,WAAW,SAAS,MAAM,CAAC;AAQnF,IAAM,gBAAqC,oBAAI,IAAI;AAAA,EACjD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAOD,SAAS,eAAe,OAAoB,MAAuB;AACjE,MAAI,UAA8B;AAClC,SAAO,SAAS;AACd,UAAM,WAAW,QAAQ,IAAI,IAAI,IAAI;AACrC,QAAI,UAAU;AACZ,iBAAW,OAAO,SAAS,MAAM;AAC/B,YAAI,IAAI,SAAS,eAAe;AAC9B,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF;AACA,cAAU,QAAQ;AAAA,EACpB;AACA,SAAO;AACT;AAOA,SAAS,oBACP,QACA,OACS;AACT,QAAMC,YACJ,CAAC,OAAO,YAAY,OAAO,SAAS,SAAS,eAAe,OAAO,SAAS,OAAO;AAErF,MAAIA,cAAa,QAAQ,mBAAmB,KAAKA,SAAQ,GAAG;AAC1D,WAAO;AAAA,EACT;AAEA,QAAM,OAAO,OAAO;AACpB,QAAM,oBACJ,KAAK,SAAS,iBACb,mBAAmB,KAAK,KAAK,IAAI,KAAK,eAAe,OAAO,KAAK,IAAI;AACxE,MAAI,mBAAmB;AACrB,QAAIA,cAAa,MAAM;AACrB,aAAO;AAAA,IACT;AACA,UAAM,UAAUA,UAAS,YAAY;AACrC,WAAO,CAAC,kBAAkB,IAAI,OAAO,KAAK,CAAC,cAAc,IAAI,OAAO;AAAA,EACtE;AAEA,SAAO;AACT;AAGA,SAAS,uBACP,MAC4B;AAC5B,MACE,KAAK,SAAS,sBACd,KAAK,aAAa,gBAClB,KAAK,MAAM,SAAS,gBACpB,KAAK,MAAM,SAAS,SACpB;AACA,WAAO,KAAK;AAAA,EACd;AACA,SAAO;AACT;AAGA,IAAM,qBAAqB;AAG3B,SAAS,iBACP,MAC4B;AAC5B,QAAM,MAAM,KAAK,SAAS,mBAAmB,KAAK,UAAU,CAAC,IAAI;AACjE,MACE,KAAK,SAAS,oBACd,KAAK,OAAO,SAAS,gBACrB,mBAAmB,KAAK,KAAK,OAAO,IAAI,KACxC,KAAK,UAAU,WAAW,KAC1B,QAAQ,UACR,IAAI,SAAS,iBACb;AACA,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAOA,SAAS,qBACP,MAC4B;AAC5B,SAAO,uBAAuB,IAAI,KAAK,iBAAiB,IAAI;AAC9D;AAGA,SAAS,8BACP,MAC4B;AAC5B,MAAI,KAAK,SAAS,qBAAqB,KAAK,aAAa,KAAK;AAC5D,WAAO,qBAAqB,KAAK,QAAQ;AAAA,EAC3C;AACA,SAAO;AACT;AAGA,SAAS,iBAAiB,QAAqC;AAC7D,QAAM,OAAO,OAAO,SAAS,mBAAmB,OAAO,OAAO,CAAC,MAAM;AACrE,QAAM,OAAO,KAAK,KAAK,SAAS,CAAC;AACjC,SACE,SAAS,WACR,KAAK,SAAS,qBAAqB,KAAK,SAAS;AAEtD;AAOA,SAAS,wBACP,MACA,SACA,YACS;AACT,QAAM,UAAU,WAAW,QAAQ,OAAO;AAC1C,MAAI,UAAqC,KAAK;AAC9C,SAAO,SAAS;AACd,QAAI,QAAQ,SAAS,oBAAoB,QAAQ,SAAS,WAAW;AACnE,iBAAW,QAAQ,QAAQ,MAAM;AAC/B,YAAI,KAAK,MAAM,CAAC,KAAK,KAAK,MAAM,CAAC,GAAG;AAClC;AAAA,QACF;AACA,YACE,KAAK,SAAS,iBACd,KAAK,cAAc,QACnB,iBAAiB,KAAK,UAAU,GAChC;AACA,gBAAM,UAAU,qBAAqB,KAAK,IAAI;AAC9C,cAAI,WAAW,WAAW,QAAQ,OAAO,MAAM,SAAS;AACtD,mBAAO;AAAA,UACT;AAAA,QACF;AAAA,MACF;AAAA,IACF;AACA,cAAU,QAAQ;AAAA,EACpB;AACA,SAAO;AACT;AAEA,SAAS,WAAW,MAAqB,WAA0C;AACjF,SACE,cAAc,QACd,KAAK,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,KAClC,KAAK,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC;AAEtC;AAQA,SAAS,2BACP,MACA,SACA,YACS;AACT,QAAM,UAAU,WAAW,QAAQ,OAAO;AAC1C,QAAM,cAAc,CAAC,YACnB,WAAW,QAAQ,OAAO,MAAM;AAElC,MAAI,UAAqC,KAAK;AAC9C,SAAO,SAAS;AACd,QAAI,QAAQ,SAAS,yBAAyB;AAC5C,YAAM,UAAU,qBAAqB,QAAQ,IAAI;AACjD,UAAI,WAAW,YAAY,OAAO,KAAK,WAAW,MAAM,QAAQ,SAAS,GAAG;AAC1E,eAAO;AAAA,MACT;AACA,YAAM,UAAU,8BAA8B,QAAQ,IAAI;AAC1D,UAAI,WAAW,YAAY,OAAO,KAAK,WAAW,MAAM,QAAQ,UAAU,GAAG;AAC3E,eAAO;AAAA,MACT;AAAA,IACF,WAAW,QAAQ,SAAS,eAAe;AACzC,YAAM,UAAU,qBAAqB,QAAQ,IAAI;AACjD,UAAI,WAAW,YAAY,OAAO,KAAK,WAAW,MAAM,QAAQ,SAAS,GAAG;AAC1E,eAAO;AAAA,MACT;AACA,YAAM,UAAU,8BAA8B,QAAQ,IAAI;AAC1D,UAAI,WAAW,YAAY,OAAO,KAAK,WAAW,MAAM,QAAQ,UAAU,GAAG;AAC3E,eAAO;AAAA,MACT;AAAA,IACF;AACA,cAAU,QAAQ;AAAA,EACpB;AACA,SAAO;AACT;AAEA,SAAS,gBAAgB,QAAsC;AAC7D,SACE,OAAO,SAAS,sBAChB,CAAC,OAAO,YACR,OAAO,OAAO,SAAS,gBACvB,OAAO,OAAO,SAAS,UACvB,OAAO,SAAS,SAAS,gBACzB,OAAO,SAAS,SAAS;AAE7B;AAEA,IAAO,kCAAQ,0BAAY;AAAA,EACzB,CAAC,SACC,8EAA8E,IAAI;AACtF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,UAAU;AAAA,MACR,sBACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC;AAAA,EACjB,OAAO,SAAS;AACd,WAAO;AAAA,MACL,eAAe,MAAqC;AAClD,YAAI,CAAC,gBAAgB,KAAK,MAAM,GAAG;AACjC;AAAA,QACF;AAEA,cAAM,WAAW,KAAK,UAAU,CAAC;AACjC,YAAI,CAAC,UAAU;AACb;AAAA,QACF;AAEA,cAAM,QAAQ,QAAQ,WAAW,SAAS,QAAQ;AAElD,YAAI;AACJ,YAAI,SAAS,SAAS,cAAc;AAClC,0BACE,mBAAmB,KAAK,SAAS,IAAI,KAAK,eAAe,OAAO,SAAS,IAAI;AAAA,QACjF,WAAW,SAAS,SAAS,oBAAoB;AAC/C,0BAAgB,oBAAoB,UAAU,KAAK;AAAA,QACrD,OAAO;AACL;AAAA,QACF;AAEA,YAAI,CAAC,eAAe;AAClB;AAAA,QACF;AAEA,YACE,2BAA2B,MAAM,UAAU,QAAQ,UAAU,KAC7D,wBAAwB,MAAM,UAAU,QAAQ,UAAU,GAC1D;AACA;AAAA,QACF;AAEA,gBAAQ,OAAO;AAAA,UACb;AAAA,UACA,WAAW;AAAA,QACb,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;ACpSD,IAAAC,gBAA2C;;;ACzB3C,IAAAC,gBAA8B;AAEvB,IAAM,cAAmC,oBAAI,IAAI;AAAA,EACtD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAEM,IAAM,eAAoC,oBAAI,IAAI;AAAA,EACvD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGM,IAAM,mBAAwC,oBAAI,IAAI;AAAA,EAC3D;AAAA,EACA;AACF,CAAC;AAQM,IAAM,iBAAiB;AAavB,IAAM,4BAA4B;AAAA,EACvC,aAAa,EAAE,MAAM,SAAS,OAAO,EAAE,MAAM,SAAS,EAAE;AAAA,EACxD,cAAc,EAAE,MAAM,SAAS,OAAO,EAAE,MAAM,SAAS,EAAE;AAC3D;AAGO,SAAS,WAAW,QAAsC;AAC/D,MAAI,OAAO,SAAS,cAAc;AAChC,WAAO,OAAO;AAAA,EAChB;AACA,MACE,OAAO,SAAS,sBAChB,CAAC,OAAO,YACR,OAAO,SAAS,SAAS,cACzB;AACA,WAAO,OAAO,SAAS;AAAA,EACzB;AACA,SAAO;AACT;AAeO,SAAS,iBAAiB,UAA0B,CAAC,GAAe;AACzE,QAAM,cAAmC,oBAAI,IAAI;AAAA,IAC/C,GAAG;AAAA,IACH,IAAI,QAAQ,eAAe,CAAC,GAAG,IAAI,CAAC,SAAS,KAAK,YAAY,CAAC;AAAA,EACjE,CAAC;AACD,QAAM,eAAoC,IAAI,IAAI,QAAQ,gBAAgB,CAAC,CAAC;AAE5E,WAAS,iBACP,MACS;AACT,YAAQ,KAAK,MAAM;AAAA,MACjB,KAAK;AACH,eAAO,YAAY,IAAI,KAAK,KAAK,YAAY,CAAC;AAAA,MAChD,KAAK,oBAAoB;AACvB,cAAM,EAAE,UAAU,OAAO,IAAI;AAC7B,YAAI,CAAC,KAAK,YAAY,SAAS,SAAS,cAAc;AACpD,gBAAM,UAAU,SAAS,KAAK,YAAY;AAC1C,cAAI,YAAY,IAAI,OAAO,KAAK,iBAAiB,IAAI,OAAO,GAAG;AAC7D,mBAAO;AAAA,UACT;AAAA,QACF;AACA,eAAO,iBAAiB,MAAM;AAAA,MAChC;AAAA,MACA,KAAK,kBAAkB;AACrB,cAAM,SAAS,KAAK;AACpB,YACE,OAAO,SAAS,sBAChB,CAAC,OAAO,YACR,OAAO,SAAS,SAAS,gBACzB,iBAAiB,IAAI,OAAO,SAAS,KAAK,YAAY,CAAC,GACvD;AACA,iBAAO;AAAA,QACT;AACA,YAAI,OAAO,SAAS,SAAS;AAC3B,iBAAO,iBAAiB,MAAM;AAAA,QAChC;AACA,eAAO;AAAA,MACT;AAAA,MACA;AACE,eAAO;AAAA,IACX;AAAA,EACF;AAEA,WAAS,kBAAkB,MAA8B;AACvD,QAAI,KAAK,SAAS,oBAAoB,aAAa,SAAS,GAAG;AAC7D,aAAO;AAAA,IACT;AACA,UAAM,OAAO,WAAW,KAAK,MAAM;AACnC,WAAO,SAAS,QAAQ,aAAa,IAAI,IAAI;AAAA,EAC/C;AAEA,WAAS,cAAc,MAA8B;AACnD,QAAI,KAAK,SAAS,kBAAkB;AAClC,aAAO;AAAA,IACT;AACA,QAAI,kBAAkB,IAAI,GAAG;AAC3B,aAAO;AAAA,IACT;AACA,UAAM,SAAS,KAAK;AACpB,QACE,OAAO,SAAS,sBAChB,OAAO,YACP,OAAO,SAAS,SAAS,cACzB;AACA,aAAO;AAAA,IACT;AACA,QAAI,CAAC,YAAY,IAAI,OAAO,SAAS,KAAK,YAAY,CAAC,GAAG;AACxD,aAAO;AAAA,IACT;AACA,WAAO,iBAAiB,OAAO,MAAM;AAAA,EACvC;AAEA,SAAO,EAAE,kBAAkB,mBAAmB,cAAc;AAC9D;;;ADzHA,IAAMC,2BAA6C;AAAA,EACjD;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAO,4BAAQ,0BAAY;AAAA,EACzB,CAAC,SACC,8EAA8E,IAAI;AACtF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ;AAAA,MACN;AAAA,QACE,MAAM;AAAA,QACN,sBAAsB;AAAA,QACtB,YAAY,EAAE,GAAG,0BAA0B;AAAA,MAC7C;AAAA,IACF;AAAA,IACA,UAAU;AAAA,MACR,gBACE;AAAA,MACF,YACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC,CAAC,CAAC;AAAA,EACnB,OAAO,SAAS,CAAC,cAAc,GAAG;AAChC,UAAM,UAAU,iBAAiB,cAAc;AAC/C,UAAM,WAAW,QAAQ;AAGzB,aAAS,uBAAuB,WAAwC;AACtE,UAAI,UAAU,SAAS,uBAAuB;AAC5C,eAAO;AAAA,MACT;AACA,aAAO,QAAQ,cAAc,UAAU,UAAU;AAAA,IACnD;AAEA,UAAM,qBAAqBA,yBAAwB;AAAA,MAAK,CAAC,OACvD,GAAG,KAAK,QAAQ;AAAA,IAClB;AAEA,QAAI,oBAAoB;AACtB,aAAO,CAAC;AAAA,IACV;AAEA,WAAO;AAAA,MACL,YAAY,MAAkC;AAC5C,cAAM,aAAa,KAAK,KAAK;AAI7B,cAAM,eACJ,QAAQ,WAAW,kBAAkB,KAAK,IAAI,EAAE,SAAS;AAE3D,YAAI,WAAW,WAAW,GAAG;AAC3B,cAAI,cAAc;AAChB;AAAA,UACF;AACA,kBAAQ,OAAO,EAAE,MAAM,WAAW,aAAa,CAAC;AAChD;AAAA,QACF;AAEA,YAAI,cAAc;AAChB;AAAA,QACF;AAEA,cAAM,0BAA0B,WAAW;AAAA,UAAM,CAAC,cAChD,uBAAuB,SAAS;AAAA,QAClC;AAEA,YAAI,yBAAyB;AAC3B,kBAAQ,OAAO,EAAE,MAAM,WAAW,iBAAiB,CAAC;AAAA,QACtD;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;AEhHD,IAAAC,iBAA2C;AAQ3C,IAAM,iBAAiB;AAOvB,IAAM,uBACJ;AAEF,SAAS,uBAAuB,UAAkB,YAA6B;AAC7E,SACE,qBAAqB,KAAK,SAAS,WAAW,MAAM,GAAG,CAAC,KACxD,mBAAmB,KAAK,UAAU,KAClC,eAAe,KAAK,UAAU;AAElC;AAGA,SAAS,aAAa,MAA0C;AAC9D,SACE,CAAC,KAAK,YACN,KAAK,OAAO,SAAS,gBACrB,KAAK,OAAO,SAAS,aACrB,KAAK,SAAS,SAAS,gBACvB,KAAK,SAAS,SAAS;AAE3B;AAGA,SAAS,gBAAgB,MAA0C;AACjE,SACE,CAAC,KAAK,YACN,KAAK,SAAS,SAAS,gBACvB,KAAK,SAAS,SAAS,SACvB,KAAK,OAAO,SAAS,kBACrB,KAAK,OAAO,KAAK,SAAS,YAC1B,KAAK,OAAO,SAAS,SAAS;AAElC;AAIA,IAAM,uBAA4C,oBAAI,IAAI;AAAA,EACxD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGD,SAAS,0BAA0B,MAA0C;AAC3E,QAAM,SAAS,KAAK;AACpB,SACE,OAAO,SAAS,sBAChB,OAAO,WAAW,QAClB,CAAC,OAAO,YACR,OAAO,SAAS,SAAS,gBACzB,qBAAqB,IAAI,OAAO,SAAS,IAAI;AAEjD;AAOA,SAAS,cAAc,MAA0C;AAC/D,QAAM,SAAS,KAAK,OAAO,SAAS,sBAAsB,KAAK,OAAO,WAAW,OAAO,KAAK,SAAS;AACtG,QAAM,SAAS,OAAO;AACtB,MAAI,OAAO,SAAS,wBAAwB;AAC1C,WAAO,OAAO,SAAS;AAAA,EACzB;AACA,MAAI,OAAO,SAAS,mBAAmB;AACrC,WAAO,OAAO,aAAa;AAAA,EAC7B;AACA,SAAO;AACT;AAOA,SAAS,iBAAiB,MAA0C;AAClE,QAAM,SAAS,KAAK;AACpB,SAAO,OAAO,SAAS,mBAAmB,OAAO,aAAa;AAChE;AAEA,IAAO,qBAAQ,2BAAY;AAAA,EACzB,CAAC,SACC,8EAA8E,IAAI;AACtF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,UAAU;AAAA,MACR,UACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC;AAAA,EACjB,OAAO,SAAS;AACd,UAAM,WAAW,QAAQ;AACzB,QACE,WAAW,QAAQ,KACnB,aAAa,QAAQ,KACrB,eAAe,KAAK,SAAS,WAAW,MAAM,GAAG,CAAC,KAClD,uBAAuB,UAAU,QAAQ,WAAW,IAAI,GACxD;AACA,aAAO,CAAC;AAAA,IACV;AACA,WAAO;AAAA,MACL,iBAAiB,MAAuC;AACtD,aACG,aAAa,IAAI,KAAK,gBAAgB,IAAI,MAC3C,CAAC,0BAA0B,IAAI,KAC/B,CAAC,cAAc,IAAI,KACnB,CAAC,iBAAiB,IAAI,GACtB;AACA,kBAAQ,OAAO;AAAA,YACb;AAAA,YACA,WAAW;AAAA,UACb,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;AC3HD,IAAAC,iBAIO;AAiBP,SAAS,aAAa,KAAsD;AAC1E,MAAI,QAAQ,MAAM;AAChB,WAAO;AAAA,EACT;AACA,MAAI,IAAI,SAAS,8BAAe,SAAS;AACvC,QAAI,IAAI,UAAU,MAAM;AACtB,aAAO;AAAA,IACT;AACA,QAAI,OAAO,IAAI,UAAU,WAAW;AAClC,aAAO;AAAA,IACT;AACA,QAAI,OAAO,IAAI,UAAU,UAAU;AACjC,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AACA,MAAI,IAAI,SAAS,8BAAe,cAAc,IAAI,SAAS,aAAa;AACtE,WAAO;AAAA,EACT;AACA,MAAI,IAAI,SAAS,8BAAe,iBAAiB;AAC/C,WAAO;AAAA,EACT;AACA,MAAI,IAAI,SAAS,8BAAe,kBAAkB;AAChD,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAGA,SAAS,mBAAmB,KAA0C;AACpE,MAAI,QAAQ,MAAM;AAChB,WAAO;AAAA,EACT;AACA,MAAI,IAAI,SAAS,8BAAe,WAAW,IAAI,UAAU,MAAM;AAC7D,WAAO;AAAA,EACT;AACA,MAAI,IAAI,SAAS,8BAAe,WAAW,IAAI,UAAU,OAAO;AAC9D,WAAO;AAAA,EACT;AACA,MAAI,IAAI,SAAS,8BAAe,cAAc,IAAI,SAAS,aAAa;AACtE,WAAO;AAAA,EACT;AACA,MAAI,IAAI,SAAS,8BAAe,mBAAmB,IAAI,SAAS,WAAW,GAAG;AAC5E,WAAO;AAAA,EACT;AACA,MACE,IAAI,SAAS,8BAAe,oBAC5B,IAAI,WAAW,WAAW,GAC1B;AACA,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,SAAS,eAAe,MAA8B;AACpD,SACE,KAAK,SAAS,8BAAe,uBAC7B,KAAK,SAAS,8BAAe,sBAC7B,KAAK,SAAS,8BAAe;AAEjC;AAEA,SAAS,OAAO,OAAwC;AACtD,SACE,OAAO,UAAU,YACjB,UAAU,QACV,OAAQ,MAA6B,SAAS;AAElD;AAOA,SAAS,gBACP,MACA,OACS;AACT,MAAI,QAAQ;AAEZ,QAAM,UAAU,CAAC,YAAiC;AAChD,QAAI,OAAO;AACT;AAAA,IACF;AACA,QAAI,MAAM,OAAO,GAAG;AAClB,cAAQ;AACR;AAAA,IACF;AACA,QAAI,eAAe,OAAO,GAAG;AAC3B;AAAA,IACF;AACA,eAAW,OAAO,OAAO,KAAK,OAAO,GAAG;AACtC,UAAI,QAAQ,UAAU;AACpB;AAAA,MACF;AACA,YAAM,QAAS,QAA+C,GAAG;AACjE,UAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,mBAAW,SAAS,OAAO;AACzB,cAAI,OAAO,KAAK,GAAG;AACjB,oBAAQ,KAAK;AAAA,UACf;AAAA,QACF;AAAA,MACF,WAAW,OAAO,KAAK,GAAG;AACxB,gBAAQ,KAAK;AAAA,MACf;AAAA,IACF;AAAA,EACF;AAEA,UAAQ,IAAI;AACZ,SAAO;AACT;AAGA,SAAS,cAAc,MAA8B;AACnD,SAAO;AAAA,IACL;AAAA,IACA,CAAC,YAAY,QAAQ,SAAS,8BAAe;AAAA,EAC/C;AACF;AAGA,SAAS,UAAU,OAAsB,MAAuB;AAC9D,UAAQ,MAAM,MAAM;AAAA,IAClB,KAAK,8BAAe;AAClB,aAAO,MAAM,SAAS;AAAA,IACxB,KAAK,8BAAe;AAClB,aAAO,UAAU,MAAM,MAAM,IAAI;AAAA,IACnC,KAAK,8BAAe;AAClB,aAAO,UAAU,MAAM,UAAU,IAAI;AAAA,IACvC,KAAK,8BAAe;AAClB,aAAO,MAAM,SAAS;AAAA,QACpB,CAAC,YAAY,YAAY,QAAQ,UAAU,SAAS,IAAI;AAAA,MAC1D;AAAA,IACF,KAAK,8BAAe;AAClB,aAAO,MAAM,WAAW;AAAA,QAAK,CAAC,aAC5B,SAAS,SAAS,8BAAe,cAC7B,UAAU,SAAS,UAAU,IAAI,IACjC,UAAU,SAAS,OAAO,IAAI;AAAA,MACpC;AAAA,IACF;AACE,aAAO;AAAA,EACX;AACF;AAoBA,SAAS,iBAAiB,MAAqB,MAAuB;AACpE,MAAI,QAAQ;AAGZ,QAAM,cAAc,CAAC,OACnB,eAAe,EAAE,KAChB,GAAmD,OAAO;AAAA,IAAK,CAAC,UAC/D,UAAU,OAAO,IAAI;AAAA,EACvB;AAEF,QAAM,UAAU,CAAC,YAAiC;AAChD,QAAI,OAAO;AACT;AAAA,IACF;AACA,QAAI,QAAQ,SAAS,8BAAe,cAAc,QAAQ,SAAS,MAAM;AACvE,cAAQ;AACR;AAAA,IACF;AACA,QAAI,YAAY,OAAO,GAAG;AACxB;AAAA,IACF;AACA,eAAW,OAAO,OAAO,KAAK,OAAO,GAAG;AACtC,UAAI,QAAQ,UAAU;AACpB;AAAA,MACF;AAEA,UACE,QAAQ,SACR,QAAQ,SAAS,8BAAe,YAChC,CAAC,QAAQ,UACT;AACA;AAAA,MACF;AAEA,UACE,QAAQ,cACR,QAAQ,SAAS,8BAAe,oBAChC,CAAC,QAAQ,UACT;AACA;AAAA,MACF;AACA,YAAM,QAAS,QAA+C,GAAG;AACjE,UAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,mBAAW,SAAS,OAAO;AACzB,cAAI,OAAO,KAAK,GAAG;AACjB,oBAAQ,KAAK;AAAA,UACf;AAAA,QACF;AAAA,MACF,WAAW,OAAO,KAAK,GAAG;AACxB,gBAAQ,KAAK;AAAA,MACf;AAAA,IACF;AAAA,EACF;AAEA,UAAQ,IAAI;AACZ,SAAO;AACT;AASA,SAAS,mBACP,MACA,YACS;AACT,MAAI,eAAe,MAAM;AACvB,WAAO;AAAA,EACT;AACA,SAAO,KAAK,KAAK,CAAC,QAAQ,iBAAiB,KAAK,UAAU,CAAC;AAC7D;AAGA,SAAS,WAAW,WAA0D;AAC5E,SAAO,UAAU,OAAO;AAC1B;AAGA,IAAM,0BAA+C,oBAAI,IAAI;AAAA,EAC3D;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAMD,SAAS,kBAAkB,MAA8B;AACvD,MACE,KAAK,SAAS,8BAAe,kBAC7B,KAAK,OAAO,SAAS,8BAAe,oBACpC,CAAC,KAAK,OAAO,YACb,KAAK,OAAO,SAAS,SAAS,8BAAe,YAC7C;AACA,WAAO,KAAK,OAAO,SAAS,SAAS;AAAA,EACvC;AACA,MACE,KAAK,SAAS,8BAAe,iBAC7B,KAAK,OAAO,SAAS,8BAAe,YACpC;AACA,WAAO,wBAAwB,IAAI,KAAK,OAAO,IAAI;AAAA,EACrD;AACA,SAAO;AACT;AAGA,IAAM,sBAA2C,oBAAI,IAAI;AAAA,EACvD;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,SAAS,iBAAiB,MAA8B;AACtD,SACE,KAAK,SAAS,8BAAe,kBAC7B,KAAK,OAAO,SAAS,8BAAe,oBACpC,CAAC,KAAK,OAAO,YACb,KAAK,OAAO,SAAS,SAAS,8BAAe,cAC7C,oBAAoB,IAAI,KAAK,OAAO,SAAS,IAAI;AAErD;AAUA,SAAS,gBACP,MACA,WACS;AACT,SACE,KAAK,SAAS,8BAAe,mBAC7B,KAAK,aAAa,QAClB,gBAAgB,KAAK,UAAU,SAAS;AAE5C;AAGA,SAAS,wBACP,MAC0B;AAC1B,MAAI,UAA4C,KAAK;AACrD,SAAO,YAAY,UAAa,YAAY,MAAM;AAChD,QAAI,eAAe,OAAO,KAAK,gBAAgB,SAAS;AACtD,aAAO,QAAQ,YAAY,kBAAkB;AAAA,IAC/C;AACA,cAAU,QAAQ;AAAA,EACpB;AACA,SAAO;AACT;AAiBA,IAAM,oBAAoB;AAC1B,IAAM,sBAAsB;AAG5B,SAAS,sBAAsB,MAAoC;AACjE,MAAI,UAA4C,KAAK;AACrD,SAAO,YAAY,UAAa,YAAY,MAAM;AAChD,QAAI,eAAe,OAAO,GAAG;AAC3B,UACE,QAAQ,WACR,OAAO,QAAQ,EAAE,KACjB,QAAQ,GAAG,SAAS,8BAAe,YACnC;AACA,eAAO,QAAQ,GAAG;AAAA,MACpB;AACA,YAAM,SAAS,QAAQ;AACvB,UACE,QAAQ,SAAS,8BAAe,sBAChC,OAAO,GAAG,SAAS,8BAAe,YAClC;AACA,eAAO,OAAO,GAAG;AAAA,MACnB;AACA,aAAO;AAAA,IACT;AACA,cAAU,QAAQ;AAAA,EACpB;AACA,SAAO;AACT;AAOA,SAAS,wBACP,WACA,MACS;AACT,MAAI,SAAS,WAAW;AACtB,WAAO;AAAA,EACT;AACA,QAAM,OAAO,sBAAsB,SAAS;AAC5C,SACE,SAAS,SACR,kBAAkB,KAAK,IAAI,KAAK,oBAAoB,KAAK,IAAI;AAElE;AAUA,SAAS,2BACP,WACA,MACS;AACT,MAAI,SAAS,WAAW;AACtB,WAAO;AAAA,EACT;AACA,MAAI,WAAW,wBAAwB,SAAS;AAChD,MACE,UAAU,SAAS,8BAAe,mBAClC,SAAS,SAAS,SAAS,8BAAe,cAC1C,SAAS,SAAS,SAAS,WAC3B;AACA,eAAW,SAAS,eAAe,OAAO,CAAC,KAAK;AAAA,EAClD;AACA,SAAO,UAAU,SAAS,8BAAe;AAC3C;AAYA,SAAS,oBAAoB,WAA0C;AACrE,QAAM,WAAW,WAAW,SAAS;AACrC,MACE;AAAA,IAAgB;AAAA,IAAU,CAAC,YACzB,gBAAgB,SAAS,iBAAiB;AAAA,EAC5C,GACA;AACA,WAAO;AAAA,EACT;AACA,QAAM,OAAO,SAAS,KAAK,WAAW,IAAI,SAAS,KAAK,CAAC,IAAI;AAC7D,SAAO,SAAS,UAAa,gBAAgB,MAAM,gBAAgB;AACrE;AAGA,SAAS,sBACP,MACgC;AAChC,MAAI,UAA4C,KAAK;AACrD,SAAO,YAAY,UAAa,YAAY,MAAM;AAChD,QACE,eAAe,OAAO,KACtB,UAAU,WACV,OAAO,QAAQ,IAAI,KACnB,QAAQ,KAAK,SAAS,8BAAe,gBACrC;AACA,aAAO,QAAQ;AAAA,IACjB;AACA,cAAU,QAAQ;AAAA,EACpB;AACA,SAAO;AACT;AAQA,SAAS,yCACP,WACA,MACS;AACT,QAAM,eAAe,sBAAsB,SAAS;AACpD,MAAI,iBAAiB,MAAM;AACzB,WAAO;AAAA,EACT;AACA,SAAO,gBAAgB,cAAc,CAAC,YAAY;AAChD,QAAI,QAAQ,SAAS,8BAAe,iBAAiB;AACnD,aAAO;AAAA,IACT;AACA,QAAI,SAAS,SAAS,UAAU,IAAI,GAAG;AACrC,aAAO;AAAA,IACT;AACA,WAAO,sBAAsB,QAAQ,QAAQ,EAAE,IAAI,IAAI;AAAA,EACzD,CAAC;AACH;AAiBA,SAAS,sBACP,KAC2B;AAC3B,QAAM,QAAQ,oBAAI,IAAkB;AACpC,MAAI,QAAQ,MAAM;AAChB,WAAO;AAAA,EACT;AACA,QAAM,SAAS,aAAa,GAAG;AAC/B,MAAI,WAAW,MAAM;AACnB,UAAM,IAAI,MAAM;AAChB,WAAO;AAAA,EACT;AACA,MAAI,IAAI,SAAS,8BAAe,uBAAuB;AACrD,eAAW,UAAU,CAAC,IAAI,YAAY,IAAI,SAAS,GAAG;AACpD,iBAAW,UAAU,sBAAsB,MAAM,GAAG;AAClD,cAAM,IAAI,MAAM;AAAA,MAClB;AAAA,IACF;AAAA,EACF,WACE,IAAI,SAAS,8BAAe,sBAC3B,IAAI,aAAa,QAAQ,IAAI,aAAa,OAC3C;AACA,eAAW,UAAU,sBAAsB,IAAI,KAAK,GAAG;AACrD,YAAM,IAAI,MAAM;AAAA,IAClB;AAAA,EACF;AACA,SAAO;AACT;AAGA,SAAS,SAAS,MAAqB,UAAkC;AACvE,MAAI,UAA4C;AAChD,SAAO,YAAY,UAAa,YAAY,MAAM;AAChD,QAAI,YAAY,UAAU;AACxB,aAAO;AAAA,IACT;AACA,cAAU,QAAQ;AAAA,EACpB;AACA,SAAO;AACT;AAEA,IAAO,sCAAQ,2BAAY;AAAA,EACzB,CAAC,SACC,8EAA8E,IAAI;AACtF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ;AAAA,MACN;AAAA,QACE,MAAM;AAAA,QACN,sBAAsB;AAAA,QACtB,YAAY,EAAE,GAAG,0BAA0B;AAAA,MAC7C;AAAA,IACF;AAAA,IACA,UAAU;AAAA,MACR,kBACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC,CAAC,CAAC;AAAA,EACnB,OAAO,SAAS,CAAC,cAAc,GAAG;AAChC,QAAI,gBAAgB,QAAQ,UAAU,QAAQ,WAAW,IAAI,GAAG;AAC9D,aAAO,CAAC;AAAA,IACV;AAEA,UAAM,UAAU,iBAAiB,cAAc;AAQ/C,aAAS,mBACP,WACA,YACS;AACT,aAAO,gBAAgB,WAAW,CAAC,YAAY;AAC7C,YAAI,QAAQ,SAAS,8BAAe,gBAAgB;AAClD,iBAAO;AAAA,QACT;AACA,YAAI,QAAQ,cAAc,OAAO,GAAG;AAClC,iBAAO;AAAA,QACT;AACA,cAAM,OAAO,WAAW,QAAQ,MAAM;AACtC,eACE,SAAS,QACT,eAAe,KAAK,IAAI,KACxB,mBAAmB,QAAQ,WAAW,UAAU;AAAA,MAEpD,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,MACL,YAAY,MAAkC;AAC5C,cAAM,OAAO,KAAK,KAAK;AACvB,YAAI,KAAK,WAAW,GAAG;AACrB;AAAA,QACF;AAEA,cAAM,OAAO,KAAK,KAAK,SAAS,CAAC;AACjC,YAAI,SAAS,UAAa,KAAK,SAAS,8BAAe,iBAAiB;AACtE;AAAA,QACF;AAEA,YAAI,CAAC,mBAAmB,KAAK,QAAQ,GAAG;AACtC;AAAA,QACF;AAEA,YAAI,cAAc,KAAK,IAAI,GAAG;AAC5B;AAAA,QACF;AAEA,cAAM,aACJ,KAAK,OAAO,SAAS,8BAAe,aAChC,KAAK,MAAM,OACX;AAEN,YAAI,mBAAmB,KAAK,MAAM,UAAU,GAAG;AAC7C;AAAA,QACF;AAEA,YAAI,oBAAoB,IAAI,GAAG;AAC7B;AAAA,QACF;AAEA,cAAM,OAAO,aAAa,KAAK,QAAQ;AACvC,YAAI,SAAS,QAAQ,wBAAwB,MAAM,IAAI,GAAG;AACxD;AAAA,QACF;AAEA,YAAI,SAAS,QAAQ,2BAA2B,MAAM,IAAI,GAAG;AAC3D;AAAA,QACF;AAEA,YACE,SAAS,QACT,yCAAyC,MAAM,IAAI,GACnD;AACA;AAAA,QACF;AAEA,gBAAQ,OAAO;AAAA,UACb,MAAM;AAAA,UACN,WAAW;AAAA,QACb,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;ACtoBD,IAAAC,iBAA2C;AAY3C,IAAM,0BAA0B,oBAAI,IAAY,CAAC,WAAW,OAAO,QAAQ,CAAC;AAS5E,IAAM,2BACJ;AAOF,IAAM,gBAAgB;AAOtB,SAAS,eAAe,MAA8B;AACpD,SACE,KAAK,SAAS,yBACd,KAAK,SAAS,wBACd,KAAK,SAAS;AAElB;AAgBA,SAAS,OAAO,MAA8B;AAC5C,SACE,KAAK,SAAS,kBACd,KAAK,SAAS,oBACd,KAAK,SAAS,oBACd,KAAK,SAAS,oBACd,KAAK,SAAS;AAElB;AAEA,SAASC,QAAO,OAAwC;AACtD,SACE,OAAO,UAAU,YACjB,UAAU,QACV,OAAQ,MAA6B,SAAS;AAElD;AAOA,SAAS,WACP,MACA,OACM;AACN,QAAM,IAAI;AACV,aAAW,OAAO,OAAO,KAAK,IAAI,GAA8B;AAC9D,QAAI,QAAQ,UAAU;AACpB;AAAA,IACF;AACA,UAAM,QAAQ,KAAK,GAAG;AACtB,UAAM,WAAW,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;AACtD,eAAW,SAAS,UAAU;AAC5B,UAAIA,QAAO,KAAK,KAAK,CAAC,eAAe,KAAK,KAAK,CAAC,OAAO,KAAK,GAAG;AAC7D,mBAAW,OAAO,KAAK;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,cAAc,MAAiD;AACtE,QAAM,SAAqC,CAAC;AAC5C,aAAW,MAAM,CAAC,SAAS;AACzB,QAAI,KAAK,SAAS,mBAAmB;AACnC,aAAO,KAAK,IAAI;AAAA,IAClB;AAAA,EACF,CAAC;AACD,SAAO;AACT;AAkBA,IAAM,sBAAsB;AAM5B,SAAS,aAAa,MAA8B;AAClD,MAAI,QAAQ;AACZ,aAAW,MAAM,CAAC,SAAS;AACzB,QACE,KAAK,SAAS,qBACd,KAAK,SAAS,oBACd,KAAK,SAAS,qBACd;AACA,cAAQ;AAAA,IACV;AAAA,EACF,CAAC;AACD,SAAO;AACT;AAMA,IAAM,kBAAkB;AAExB,SAASC,YAAW,QAAsC;AACxD,MAAI,OAAO,SAAS,aAAc,QAAO,OAAO;AAChD,MACE,OAAO,SAAS,sBAChB,CAAC,OAAO,YACR,OAAO,SAAS,SAAS,cACzB;AACA,WAAO,OAAO,SAAS;AAAA,EACzB;AACA,SAAO;AACT;AAEA,SAAS,aAAa,MAAyC;AAC7D,QAAM,MAAM,KAAK;AAEjB,MACE,IAAI,SAAS,mBACb,IAAI,OAAO,SAAS,gBACpB,IAAI,OAAO,SAAS,WACpB;AACA,WAAO;AAAA,EACT;AAGA,MAAI,IAAI,SAAS,kBAAkB;AAIjC,QACE,IAAI,OAAO,SAAS,sBACpB,CAAC,IAAI,OAAO,YACZ,IAAI,OAAO,OAAO,SAAS,gBAC3B,IAAI,OAAO,OAAO,SAAS,aAC3B,IAAI,OAAO,SAAS,SAAS,iBAC5B,IAAI,OAAO,SAAS,SAAS,aAC5B,IAAI,OAAO,SAAS,SAAS,WAC/B;AACA,aAAO;AAAA,IACT;AACA,UAAM,OAAOA,YAAW,IAAI,MAAM;AAClC,WAAO,SAAS,QAAQ,gBAAgB,KAAK,IAAI;AAAA,EACnD;AACA,SAAO;AACT;AAEA,IAAM,sBAAsB;AAO5B,SAAS,aAAa,MAAyC;AAC7D,QAAM,MAAM,KAAK;AACjB,SACE,IAAI,SAAS,oBACb,IAAI,OAAO,SAAS,sBACpB,CAAC,IAAI,OAAO,YACZ,IAAI,OAAO,SAAS,SAAS,gBAC7B,oBAAoB,KAAK,IAAI,OAAO,SAAS,IAAI;AAErD;AAGA,SAAS,aAAa,MAA8B;AAClD,MAAI,QAAQ;AACZ,aAAW,MAAM,CAAC,SAAS;AACzB,QAAI,KAAK,SAAS,kBAAkB;AAClC;AAAA,IACF;AACA,QAAI,SAAwB,KAAK;AAEjC,WAAO,OAAO,SAAS,oBAAoB;AACzC,eAAS,OAAO;AAAA,IAClB;AACA,QAAI,OAAO,SAAS,kBAAkB;AACpC,eAAS,OAAO;AAAA,IAClB;AACA,QAAI,OAAO,SAAS,gBAAgB,oBAAoB,KAAK,OAAO,IAAI,GAAG;AACzE,cAAQ;AAAA,IACV;AAAA,EACF,CAAC;AACD,SAAO;AACT;AAEA,SAAS,eAAe,MAAqB,MAAuB;AAClE,MAAI,QAAQ;AACZ,aAAW,MAAM,CAAC,SAAS;AACzB,QAAI,KAAK,SAAS,gBAAgB,KAAK,SAAS,MAAM;AACpD,cAAQ;AAAA,IACV;AAAA,EACF,CAAC;AACD,SAAO;AACT;AAQA,SAAS,sBAAsB,MAAyC;AACtE,QAAM,SAAS,KAAK;AACpB,MAAI,SAAwB;AAC5B,MACE,OAAO,SAAS,0BAChB,OAAO,aAAa,OACpB,OAAO,UAAU,QACjB,OAAO,KAAK,SAAS,cACrB;AACA,aAAS,OAAO,KAAK;AAAA,EACvB,WACE,OAAO,SAAS,wBAChB,OAAO,SAAS,QAChB,OAAO,GAAG,SAAS,cACnB;AACA,aAAS,OAAO,GAAG;AAAA,EACrB;AACA,MAAI,WAAW,MAAM;AACnB,WAAO;AAAA,EACT;AACA,SAAO,eAAe,KAAK,UAAU,MAAM;AAC7C;AAGA,SAAS,YAAY,MAAkC;AACrD,QAAM,QAAQ,oBAAI,IAAY;AAC9B,aAAW,MAAM,CAAC,SAAS;AACzB,QAAI,KAAK,SAAS,cAAc;AAC9B,YAAM,IAAI,KAAK,IAAI;AAAA,IACrB;AAAA,EACF,CAAC;AACD,SAAO;AACT;AAQA,SAAS,0BACP,MACA,MACS;AACT,QAAM,YAAY,YAAY,IAAI;AAClC,MAAI,UAAU,SAAS,GAAG;AACxB,WAAO;AAAA,EACT;AACA,MAAI,QAAQ;AACZ,aAAW,MAAM,CAAC,SAAS;AACzB,QACE,KAAK,SAAS,0BACd,KAAK,aAAa,OAClB,KAAK,KAAK,SAAS,gBACnB,UAAU,IAAI,KAAK,KAAK,IAAI,GAC5B;AACA,cAAQ;AAAA,IACV;AAAA,EACF,CAAC;AACD,SAAO;AACT;AAQA,SAAS,aACP,QACA,WACA,cACA,UAAU,OACD;AACT,MAAI,OAAO,WAAW,GAAG;AACvB,WAAO;AAAA,EACT;AACA,MAAI,WAAW;AACb,WAAO;AAAA,EACT;AACA,MAAI,SAAS;AACX,WAAO;AAAA,EACT;AACA,MAAI,iBAAiB,QAAQ,yBAAyB,KAAK,YAAY,GAAG;AACxE,WAAO;AAAA,EACT;AACA,SAAO,OAAO;AAAA,IACZ,CAAC,SACC,CAAC,aAAa,IAAI,KAClB,CAAC,sBAAsB,IAAI,KAC3B,CAAC,aAAa,IAAI;AAAA,EACtB;AACF;AAEA,IAAO,8BAAQ,2BAAY;AAAA,EACzB,CAAC,SACC,8EAA8E,IAAI;AACtF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,UAAU;AAAA,MACR,mBACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC;AAAA,EACjB,OAAO,SAAS;AAEd,QAAI,WAAW,QAAQ,QAAQ,KAAK,cAAc,KAAK,QAAQ,QAAQ,GAAG;AACxE,aAAO,CAAC;AAAA,IACV;AAQA,aAAS,UAAU,MAA0C;AAK3D,UAAI,KAAK,SAAS,gBAAgB;AAChC,eAAO,CAAC,KAAK,MAAM,KAAK,MAAM,KAAK,MAAM;AAAA,MAC3C;AACA,UAAI,KAAK,SAAS,oBAAoB,KAAK,SAAS,kBAAkB;AACpE,eAAO,CAAC,KAAK,IAAI;AAAA,MACnB;AACA,aAAO,CAAC,KAAK,MAAM,KAAK,IAAI;AAAA,IAC9B;AAEA,aAAS,eAAe,MAA+B;AACrD,UAAI,KAAK,SAAS,oBAAoB,KAAK,SAAS,kBAAkB;AACpE,eAAO,QAAQ,WAAW,QAAQ,KAAK,KAAK;AAAA,MAC9C;AACA,aAAO;AAAA,IACT;AAEA,aAAS,UAAU,MAAsB;AACvC,WACG,KAAK,SAAS,oBAAoB,KAAK,SAAS,uBACjD,0BAA0B,KAAK,MAAM,KAAK,IAAI,GAC9C;AACA;AAAA,MACF;AAEA,YAAM,SAAqC,CAAC;AAC5C,UAAI,YAAY;AAChB,UAAI,UAAU;AACd,iBAAW,QAAQ,UAAU,IAAI,GAAG;AAClC,YAAI,SAAS,QAAQ,OAAO,IAAI,GAAG;AACjC;AAAA,QACF;AACA,eAAO,KAAK,GAAG,cAAc,IAAI,CAAC;AAClC,YAAI,CAAC,aAAa,aAAa,IAAI,GAAG;AACpC,sBAAY;AAAA,QACd;AACA,YAAI,CAAC,WAAW,aAAa,IAAI,GAAG;AAClC,oBAAU;AAAA,QACZ;AAAA,MACF;AACA,UAAI,aAAa,QAAQ,WAAW,eAAe,IAAI,GAAG,OAAO,GAAG;AAClE,gBAAQ,OAAO,EAAE,MAAM,WAAW,oBAAoB,CAAC;AAAA,MACzD;AAAA,IACF;AAEA,WAAO;AAAA,MACL,cAAc;AAAA,MACd,gBAAgB;AAAA,MAChB,gBAAgB;AAAA,MAChB,kBAAkB;AAAA,MAClB,eAAe,MAAqC;AAElD,YAAI,KAAK,OAAO;AACd;AAAA,QACF;AACA,kBAAU,IAAI;AAAA,MAChB;AAAA,MACA,eAAe,MAAqC;AAClD,cAAM,SAAS,KAAK;AACpB,YAAI,OAAO,SAAS,sBAAsB,OAAO,UAAU;AACzD;AAAA,QACF;AACA,YACE,OAAO,SAAS,SAAS,gBACzB,CAAC,wBAAwB,IAAI,OAAO,SAAS,IAAI,GACjD;AACA;AAAA,QACF;AACA,cAAM,WAAW,KAAK,UAAU,CAAC;AACjC,YACE,aAAa,UACb,CAAC,eAAe,QAAQ,KACxB,EAAE,WAAW,YAAY,SAAS,QAClC;AACA;AAAA,QACF;AAKA,YACE,OAAO,SAAS,SAAS,aACzB,KAAK,OAAO,SAAS,uBACrB;AACA;AAAA,QACF;AACA,cAAM,SAAS,cAAc,SAAS,IAAI;AAC1C,cAAM,YAAY,aAAa,SAAS,IAAI;AAC5C,cAAM,eAAe,QAAQ,WAAW,QAAQ,OAAO,MAAM;AAC7D,YACE,aAAa,QAAQ,WAAW,cAAc,aAAa,SAAS,IAAI,CAAC,GACzE;AACA,kBAAQ,OAAO,EAAE,MAAM,WAAW,oBAAoB,CAAC;AAAA,QACzD;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;ACxfD,IAAAC,iBAA2C;AAQ3C,IAAM,kBAAkB,oBAAI,IAAY;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAMD,SAAS,oBAAoB,MAA2C;AACtE,MAAI,SAAS,MAAM;AACjB,WAAO;AAAA,EACT;AACA,MAAI,KAAK,SAAS,mBAAmB;AACnC,WAAO;AAAA,EACT;AACA,MAAI,KAAK,SAAS,WAAW;AAC3B,WAAO,OAAO,KAAK,UAAU;AAAA,EAC/B;AACA,SAAO;AACT;AAOA,SAAS,aACP,OACA,MAC4B;AAC5B,MAAI,UAA8B;AAClC,SAAO,YAAY,MAAM;AACvB,UAAM,WAAW,QAAQ,UAAU,KAAK,CAAC,MAAM,EAAE,SAAS,IAAI;AAC9D,QAAI,aAAa,QAAW;AAC1B,aAAO;AAAA,IACT;AACA,cAAU,QAAQ;AAAA,EACpB;AACA,SAAO;AACT;AAOA,SAAS,4BAA4B,UAAmC;AAItE,MAAI,SAAS,KAAK,WAAW,GAAG;AAC9B,WAAO;AAAA,EACT;AACA,QAAM,MAAM,SAAS,KAAK,CAAC;AAC3B,MAAI,QAAQ,UAAa,IAAI,SAAS,YAAY;AAEhD,WAAO;AAAA,EACT;AACA,QAAM,aAAa,IAAI;AACvB,MAAI,WAAW,SAAS,sBAAsB;AAC5C,WAAO;AAAA,EACT;AACA,SAAO,oBAAoB,WAAW,IAAI;AAC5C;AAQA,SAAS,gBACP,MACA,QACS;AACT,MAAI,KAAK,SAAS,cAAc;AAC9B,WAAO,KAAK,SAAS;AAAA,EACvB;AACA,MAAI,KAAK,SAAS,sBAAsB,KAAK,aAAa,KAAK;AAC7D,WACE,gBAAgB,KAAK,MAAM,MAAM,KAAK,gBAAgB,KAAK,OAAO,MAAM;AAAA,EAE5E;AACA,SAAO;AACT;AAMA,SAAS,mBACP,KACA,QACS;AACT,MAAI,IAAI,SAAS,sBAAsB,IAAI,aAAa,KAAK;AAC3D,WAAO;AAAA,EACT;AACA,SAAO,gBAAgB,IAAI,MAAM,MAAM,KAAK,gBAAgB,IAAI,OAAO,MAAM;AAC/E;AAaA,SAAS,qBACP,UACA,MACS;AACT,QAAM,MAAM,SAAS,KAAK,CAAC;AAC3B,MAAI,QAAQ,QAAW;AACrB,WAAO;AAAA,EACT;AACA,QAAM,OACJ,KAMA;AACF,QAAM,CAAC,WAAW,OAAO,IAAI,IAAI,KAAK;AACtC,QAAM,CAAC,WAAW,OAAO,IAAI,KAAK;AAClC,SAAO,aAAa,aAAa,WAAW;AAC9C;AAQA,SAAS,cAAc,MAA2C;AAChE,MAAI,QAAuB;AAC3B,MAAI,SAAS,KAAK;AAClB,SAAO,WAAW,UAAa,WAAW,MAAM;AAC9C,QAAI,gBAAgB,IAAI,OAAO,IAAI,GAAG;AAGpC,YAAM,OAAO;AAMb,UAAI,KAAK,SAAS,OAAO;AACvB,eAAO;AAAA,MACT;AAAA,IACF;AACA,YAAQ;AACR,aAAS,OAAO;AAAA,EAClB;AACA,SAAO;AACT;AAEA,IAAO,mCAAQ,2BAAY;AAAA,EACzB,CAAC,SACC,8EAA8E,IAAI;AACtF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,UAAU;AAAA,MACR,sBACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC;AAAA,EACjB,OAAO,SAAS;AACd,QAAI,gBAAgB,QAAQ,UAAU,QAAQ,WAAW,IAAI,GAAG;AAC9D,aAAO,CAAC;AAAA,IACV;AAIA,UAAM,WAAW,oBAAI,QAAoC;AAEzD,WAAO;AAAA,MACL,qBAAqB,MAA2C;AAE9D,YAAI,KAAK,KAAK,SAAS,cAAc;AACnC;AAAA,QACF;AAGA,cAAM,iBACJ,KAAK,aAAa,QACjB,KAAK,aAAa,OACjB,mBAAmB,KAAK,OAAO,KAAK,KAAK,IAAI;AACjD,YAAI,CAAC,gBAAgB;AACnB;AAAA,QACF;AAEA,cAAM,OAAO,cAAc,IAAI;AAC/B,YAAI,SAAS,MAAM;AACjB;AAAA,QACF;AAEA,cAAM,QAAQ,QAAQ,WAAW,SAAS,IAAI;AAC9C,cAAM,WAAW,aAAa,OAAO,KAAK,KAAK,IAAI;AAEnD,YAAI,aAAa,QAAW;AAC1B;AAAA,QACF;AAIA,YAAI,CAAC,4BAA4B,QAAQ,GAAG;AAC1C;AAAA,QACF;AAKA,YAAI,qBAAqB,UAAU,IAAI,GAAG;AACxC;AAAA,QACF;AAEA,YAAI,OAAO,SAAS,IAAI,IAAI;AAC5B,YAAI,SAAS,QAAW;AACtB,iBAAO,oBAAI,IAAY;AACvB,mBAAS,IAAI,MAAM,IAAI;AAAA,QACzB;AACA,YAAI,KAAK,IAAI,KAAK,KAAK,IAAI,GAAG;AAC5B;AAAA,QACF;AACA,aAAK,IAAI,KAAK,KAAK,IAAI;AAEvB,gBAAQ,OAAO;AAAA,UACb;AAAA,UACA,WAAW;AAAA,QACb,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;ACzOD,IAAAC,iBAIO;AAMP,IAAM,aAAa;AACnB,IAAM,mBAAmB;AACzB,IAAM,mBAAmB;AAEzB,IAAM,kBAAuC,oBAAI,IAAI;AAAA,EACnD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AASD,IAAM,0BAA+C,oBAAI,IAAI,CAAC,cAAc,CAAC;AAE7E,IAAM,6BACJ;AAUF,IAAM,kBAAkB,CAAC,WACvB,CAAC,OAAO,WAAW,GAAG,KAAK,CAAC,OAAO,WAAW,GAAG,KAAK,CAAC,OAAO,WAAW,IAAI,KAAK,CAAC,OAAO,WAAW,GAAG;AAG1G,IAAM,cAAc,CAAC,SAAgD;AACnE,MAAI,UAAyC;AAC7C,SAAO,QAAQ,SAAS,8BAAe,qBAAqB;AAC1D,cAAU,QAAQ;AAAA,EACpB;AACA,SAAO,QAAQ,SAAS,8BAAe,gBAAgB,QAAQ,OAAO;AACxE;AAGA,IAAM,8BAA8B,CAClC,MACA,aACY;AACZ,MAAI,KAAK,SAAS,8BAAe,YAAY;AAC3C,WAAO,SAAS,IAAI,KAAK,IAAI;AAAA,EAC/B;AACA,aAAW,OAAO,OAAO,KAAK,IAAI,GAA8B;AAC9D,QAAI,QAAQ,SAAU;AACtB,UAAM,QAAQ,KAAK,GAAG;AACtB,eAAW,SAAU,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK,GAAiB;AACzE,UACE,UAAU,QACV,OAAO,UAAU,YACjB,OAAQ,MAA6B,SAAS,YAC9C,4BAA4B,OAAwB,QAAQ,GAC5D;AACA,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAEA,IAAM,uBAAuB,CAC3B,SACyC;AACzC,SACE,KAAK,SAAS,8BAAe,uBAC7B,KAAK,WAAW,SAAS,8BAAe,WACxC,KAAK,WAAW,UAAU;AAE9B;AAEA,IAAM,oBAAoB,CACxB,MACA,YACY;AACZ,MAAI,CAAC,gBAAgB,IAAI,KAAK,IAAI,EAAG,QAAO;AAE5C,QAAM,SAAS,KAAK;AACpB,MAAI,WAAW,QAAW;AAExB,QACE,OAAO,SAAS,8BAAe,oBAC/B,OAAO,aAAa,QACpB,CAAC,OAAO,UACR;AACA,aAAO;AAAA,IACT;AAEA,QACE,OAAO,SAAS,8BAAe,YAC/B,OAAO,QAAQ,QACf,CAAC,OAAO,UACR;AACA,aAAO;AAAA,IACT;AAEA,QAAI,OAAO,KAAK,WAAW,IAAI,GAAG;AAChC,aAAO;AAAA,IACT;AAAA,EACF;AAIA,MAAI,QAA4B,QAAQ,WAAW,SAAS,IAAI;AAChE,SAAO,UAAU,MAAM;AACrB,UAAM,WAAW,MAAM,IAAI,IAAI,KAAK,IAAI;AACxC,QAAI,aAAa,UAAa,SAAS,KAAK,SAAS,GAAG;AACtD,aAAO;AAAA,IACT;AACA,YAAQ,MAAM;AAAA,EAChB;AAEA,SAAO;AACT;AAEA,IAAO,oCAAQ,2BAAY;AAAA,EACzB,CAAC,SACC,8EAA8E,IAAI;AACtF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,UAAU;AAAA,MACR,sBACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC;AAAA,EACjB,OAAO,SAAS;AACd,UAAM,WAAW,QAAQ;AACzB,QAAI,iBAAiB,KAAK,QAAQ,GAAG;AACnC,aAAO,CAAC;AAAA,IACV;AAEA,QAAI,gBAAqD;AACzD,QAAI,qBAAqB;AAEzB,UAAM,iBAAiB,oBAAI,IAAY;AAEvC,UAAM,iBAAiB,oBAAI,IAAY;AAEvC,UAAM,sBAAsB,CAC1B,WACS;AACT,UAAI,OAAO,SAAS,8BAAe,YAAY;AAC7C,YAAI,WAAW,KAAK,OAAO,IAAI,KAAK,OAAO,SAAS,iBAAiB;AACnE,+BAAqB;AAAA,QACvB;AACA;AAAA,MACF;AACA,UACE,OAAO,SAAS,8BAAe,oBAC/B,OAAO,SAAS,SAAS,8BAAe,YACxC;AACA,cAAM,OAAO,OAAO,SAAS;AAC7B,YAAI,WAAW,KAAK,IAAI,KAAK,SAAS,iBAAiB;AACrD,+BAAqB;AAAA,QACvB;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,MACL,QAAQ,MAAY;AAClB,mBAAW,QAAQ,KAAK,MAAM;AAG5B,cAAI,KAAK,SAAS,8BAAe,oBAAqB;AACtD,cAAI,qBAAqB,IAAI,GAAG;AAC9B,4BAAgB;AAChB;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,MACA,eAAe,MAAY;AAKzB,YAAI,kBAAkB,KAAM;AAC5B,4BAAoB,KAAK,MAAM;AAAA,MACjC;AAAA,MACA,aAAa,MAAY;AACvB,YAAI,kBAAkB,KAAM;AAC5B,YACE,KAAK,KAAK,SAAS,8BAAe,iBAClC,iBAAiB,KAAK,KAAK,KAAK,IAAI,GACpC;AACA,+BAAqB;AAAA,QACvB;AAAA,MACF;AAAA,MACA,kBAAkB,MAAY;AAC5B,YAAI,kBAAkB,KAAM;AAC5B,YAAI,OAAO,KAAK,OAAO,UAAU,SAAU;AAC3C,cAAM,SAAS,KAAK,OAAO;AAC3B,YACE,2BAA2B,KAAK,MAAM,KACtC,wBAAwB,IAAI,MAAM,GAClC;AACA,+BAAqB;AAAA,QACvB;AACA,mBAAW,aAAa,KAAK,YAAY;AACvC,yBAAe,IAAI,UAAU,MAAM,IAAI;AACvC,cAAI,gBAAgB,MAAM,GAAG;AAC3B,2BAAe,IAAI,UAAU,MAAM,IAAI;AAAA,UACzC;AAAA,QACF;AAAA,MACF;AAAA,MACA,kBAAkB,MAAY;AAC5B,YAAI,kBAAkB,KAAM;AAG5B,YAAI,eAAe,IAAI,YAAY,KAAK,IAAI,CAAC,GAAG;AAC9C,+BAAqB;AAAA,QACvB;AAAA,MACF;AAAA,MACA,uBAAuB,MAAY;AACjC,YAAI,kBAAkB,KAAM;AAC5B,YAAI,KAAK,WAAW,MAAM;AACxB,+BAAqB;AACrB;AAAA,QACF;AAGA,YACE,KAAK,gBAAgB,QACrB,4BAA4B,KAAK,aAAa,cAAc,GAC5D;AACA,+BAAqB;AAAA,QACvB;AAAA,MACF;AAAA,MACA,qBAAqB,MAAY;AAC/B,YAAI,kBAAkB,KAAM;AAC5B,YAAI,KAAK,WAAW,MAAM;AACxB,+BAAqB;AAAA,QACvB;AAAA,MACF;AAAA,MACA,mBAAyB;AACvB,YAAI,kBAAkB,KAAM;AAC5B,6BAAqB;AAAA,MACvB;AAAA,MACA,kBAAwB;AACtB,YAAI,kBAAkB,KAAM;AAC5B,6BAAqB;AAAA,MACvB;AAAA,MACA,WAAW,MAAY;AACrB,YAAI,kBAAkB,KAAM;AAC5B,YAAI,kBAAkB,MAAM,OAAO,GAAG;AACpC,+BAAqB;AAAA,QACvB;AAAA,MACF;AAAA,MACA,iBAAuB;AACrB,YAAI,kBAAkB,QAAQ,CAAC,oBAAoB;AACjD,kBAAQ,OAAO;AAAA,YACb,MAAM;AAAA,YACN,WAAW;AAAA,UACb,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;AClVD,IAAAC,iBAA2C;AAC3C,IAAAA,iBAA+B;AAQ/B,IAAM,sBAA2C,oBAAI,IAAI;AAAA,EACvD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,IAAM,uBAAuB;AAM7B,SAAS,cAAc,QAA6C;AAClE,MAAI,OAAO,SAAS,8BAAe,qBAAqB;AACtD,WAAO;AAAA,EACT;AACA,QAAM,EAAE,IAAI,IAAI;AAChB,MAAI,IAAI,SAAS,8BAAe,YAAY;AAC1C,WAAO,IAAI;AAAA,EACb;AACA,MAAI,IAAI,SAAS,8BAAe,WAAW,OAAO,IAAI,UAAU,UAAU;AACxE,WAAO,IAAI;AAAA,EACb;AACA,SAAO;AACT;AAKA,SAAS,eAAe,QAA+C;AACrE,SACE,OAAO,gBAAgB,eAAe,SACtC,8BAAe;AAEnB;AAMA,SAAS,gCACP,aACS;AACT,MAAI,mBAAmB;AACvB,MAAI,gBAAgB;AACpB,MAAI,uBAAuB;AAE3B,aAAW,UAAU,YAAY,SAAS;AACxC,QAAI,OAAO,SAAS,8BAAe,qBAAqB;AACtD;AAAA,IACF;AAEA,QAAI,OAAO,UAAU;AACnB,uBAAiB;AAGjB,UAAI,CAAC,eAAe,MAAM,GAAG;AAC3B,gCAAwB;AAAA,MAC1B;AAAA,IACF;AAEA,UAAM,OAAO,cAAc,MAAM;AACjC,QACE,SAAS,QACT,oBAAoB,IAAI,IAAI,KAC5B,eAAe,MAAM,GACrB;AACA,yBAAmB;AAAA,IACrB;AAAA,EACF;AAEA,SACE,oBACA,iBAAiB,wBACjB,wBAAwB;AAE5B;AAEA,IAAO,qCAAQ,2BAAY;AAAA,EACzB,CAAC,SACC,8EAA8E,IAAI;AACtF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,UAAU;AAAA,MACR,0BACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC;AAAA,EACjB,OAAO,SAAS;AACd,aAAS,iBACP,aACA,YACM;AACN,UAAI,gCAAgC,WAAW,GAAG;AAChD,gBAAQ,OAAO;AAAA,UACb,MAAM;AAAA,UACN,WAAW;AAAA,QACb,CAAC;AAAA,MACH;AAAA,IACF;AAEA,WAAO;AAAA,MACL,uBACE,MACM;AAGN,cAAM,YAAoC;AAAA,UACxC,GAAG,KAAK;AAAA,UACR,MAAM,8BAAe;AAAA,UACrB,SAAS,KAAK,KAAK;AAAA,QACrB;AACA,yBAAiB,WAAW,IAAI;AAAA,MAClC;AAAA,MACA,yCACE,MACM;AACN,yBAAiB,MAAM,KAAK,MAAM;AAAA,MACpC;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;ACxHD,IAAAC,iBAIO;AAeP,IAAM,SAAS,CACb,SACyB;AACzB,MAAI,UAA4C;AAChD,SAAO,YAAY,QAAQ,YAAY,QAAW;AAChD,QACE,QAAQ,SAAS,8BAAe,kBAChC,QAAQ,SAAS,8BAAe,mBAChC,QAAQ,SAAS,8BAAe,uBAChC,QAAQ,SAAS,8BAAe,uBAChC;AACA,gBAAU,QAAQ;AAAA,IACpB,WAAW,QAAQ,SAAS,8BAAe,iBAAiB;AAC1D,gBAAU,QAAQ;AAAA,IACpB,OAAO;AACL;AAAA,IACF;AAAA,EACF;AACA,SAAO,WAAW;AACpB;AAYA,IAAM,qBAAqB,CACzB,SACY;AACZ,MAAI,UAAU,OAAO,IAAI;AACzB,MAAI,YAAY,KAAM,QAAO;AAC7B,MAAI,QAAQ,SAAS,8BAAe,iBAAiB;AACnD,cAAU,OAAO,QAAQ,QAAQ;AAAA,EACnC;AACA,MAAI,YAAY,QAAQ,QAAQ,SAAS,8BAAe,gBAAgB;AACtE,WAAO;AAAA,EACT;AACA,QAAM,SAAS,OAAO,QAAQ,MAAM;AACpC,MAAI,WAAW,QAAQ,OAAO,SAAS,8BAAe,kBAAkB;AACtE,WAAO;AAAA,EACT;AACA,QAAM,WAAW,OAAO,OAAO,QAAQ;AACvC,MAAI,aAAa,QAAQ,SAAS,SAAS,8BAAe,YAAY;AACpE,WAAO;AAAA,EACT;AACA,MAAI,SAAS,SAAS,QAAQ;AAC5B,WAAO;AAAA,EACT;AAGA,QAAM,SAAS,OAAO,OAAO,MAAM;AACnC,SACE,SAAS,SAAS,WAClB,WAAW,QACX,OAAO,SAAS,8BAAe,cAC/B,OAAO,SAAS;AAAA,EAEhB,CAAC,gBAAgB,QAAQ,UAAU,CAAC,CAAC;AAEzC;AAGA,IAAM,eAAe;AAoBrB,IAAM,kBAAkB,CAAC,SAAoD;AAC3E,MAAI,QAAQ;AACZ,QAAM,QAAQ,CAAC,YAAoD;AACjE,QAAI,SAAS,YAAY,QAAQ,YAAY,OAAW;AACxD,QAAI,QAAQ,SAAS,8BAAe,gBAAgB;AAClD,YAAM,SAAS,OAAO,QAAQ,MAAM;AACpC,YAAM,OACJ,QAAQ,SAAS,8BAAe,aAC5B,OAAO,OACP,QAAQ,SAAS,8BAAe,oBAC9B,CAAC,OAAO,YACR,OAAO,SAAS,SAAS,8BAAe,aACxC,OAAO,SAAS,OAChB;AACR,UAAI,SAAS,QAAQ,aAAa,KAAK,IAAI,GAAG;AAC5C,gBAAQ;AACR;AAAA,MACF;AAAA,IACF;AACA,eAAW,OAAO,OAAO,KAAK,OAAO,GAA8B;AACjE,UAAI,QAAQ,SAAU;AACtB,YAAM,QAAQ,QAAQ,GAAG;AACzB,iBAAW,SAAU,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK,GAAiB;AACzE,YACE,UAAU,QACV,OAAO,UAAU,YACjB,OAAQ,MAA6B,SAAS,UAC9C;AACA,gBAAM,KAAsB;AAAA,QAC9B;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,QAAM,IAAI;AACV,SAAO;AACT;AAGA,IAAMC,uBAAsB;AAoB5B,IAAM,oBAAoB,CAAC,SAAiC;AAC1D,WACM,UAA4C,KAAK,QACrD,YAAY,UAAa,YAAY,MACrC,UAAU,QAAQ,QAClB;AACA,QAAI,QAAQ,SAAS,8BAAe,eAAgB;AACpD,QAAI,SAAwB,QAAQ;AACpC,WAAO,OAAO,SAAS,8BAAe,kBAAkB;AACtD,eAAS,OAAO;AAAA,IAClB;AACA,QAAI,OAAO,SAAS,8BAAe,gBAAgB;AACjD,eAAS,OAAO;AAAA,IAClB;AACA,QACE,OAAO,SAAS,8BAAe,cAC/BA,qBAAoB,KAAK,OAAO,IAAI,GACpC;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAEA,IAAMC,gBAAe,CACnB,OACA,SAC0B;AAC1B,MAAI,UAA8B;AAClC,SAAO,YAAY,MAAM;AACvB,UAAM,WAAW,QAAQ,IAAI,IAAI,IAAI;AACrC,QAAI,aAAa,OAAW,QAAO;AACnC,cAAU,QAAQ;AAAA,EACpB;AACA,SAAO;AACT;AAGA,IAAM,gBAAgB;AAMtB,IAAM,sBAAsB,CAAC,SAAiC;AAC5D,MAAI,UAAyB;AAC7B,MAAI,SAA2C,QAAQ;AACvD,SAAO,WAAW,UAAa,WAAW,MAAM;AAC9C,YAAQ,OAAO,MAAM;AAAA,MACnB,KAAK,8BAAe;AAAA,MACpB,KAAK,8BAAe;AAAA,MACpB,KAAK,8BAAe;AAClB,kBAAU;AACV,iBAAS,OAAO;AAChB;AAAA,MACF,KAAK,8BAAe;AAAA,MACpB,KAAK,8BAAe;AAAA,MACpB,KAAK,8BAAe;AAAA,MACpB,KAAK,8BAAe;AAAA,MACpB,KAAK,8BAAe;AAClB,eAAO,OAAO,SAAS;AAAA,MACzB;AACE,eAAO;AAAA,IACX;AAAA,EACF;AACA,SAAO;AACT;AAEA,IAAM,2BAA2B,CAC/B,MACA,OACA,YACY;AACZ,QAAM,YAAY,OAAO,IAAI;AAC7B,MAAI,cAAc,QAAQ,UAAU,SAAS,8BAAe,YAAY;AACtE,WAAO;AAAA,EACT;AACA,QAAM,WAAWA,cAAa,OAAO,UAAU,IAAI;AACnD,SAAO,aAAa,QAAQ,QAAQ,IAAI,QAAQ;AAClD;AAEA,IAAO,wCAAQ,2BAAY;AAAA,EACzB,CAAC,SACC,8EAA8E,IAAI;AACtF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,UAAU;AAAA,MACR,oBACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC;AAAA,EACjB,OAAO,SAAc;AAEnB,QAAI,WAAW,QAAQ,QAAQ,KAAK,gBAAgB,QAAQ,UAAU,QAAQ,WAAW,IAAI,GAAG;AAC9F,aAAO,CAAC;AAAA,IACV;AAEA,UAAM,uBAAuB,oBAAI,IAAoB;AAErD,UAAM,mBAAmB,CACvB,eACS;AACT,UAAI,CAAC,mBAAmB,WAAW,IAAI,EAAG;AAC1C,YAAM,eAAe,QAAQ,WAAW,qBAAqB,UAAU;AACvE,YAAM,WAAW,aAAa,CAAC;AAC/B,UAAI,aAAa,QAAW;AAC1B,6BAAqB,IAAI,QAAQ;AAAA,MACnC;AAAA,IACF;AAEA,WAAO;AAAA,MACL,mBAAmB,MAAY;AAC7B,cAAM,QAAQ,QAAQ,WAAW,SAAS,IAAI;AAE9C,YAAI,KAAK,GAAG,SAAS,8BAAe,YAAY;AAC9C,2BAAiB,IAAI;AACrB;AAAA,QACF;AAEA,YACE,KAAK,GAAG,SAAS,8BAAe,iBAChC,KAAK,GAAG,SAAS,8BAAe,cAChC;AACA,cAAI,mBAAmB,KAAK,IAAI,GAAG;AACjC,oBAAQ,OAAO,EAAE,MAAM,KAAK,IAAI,WAAW,qBAAqB,CAAC;AACjE;AAAA,UACF;AACA,cACE,yBAAyB,KAAK,MAAM,OAAO,oBAAoB,GAC/D;AACA,oBAAQ,OAAO,EAAE,MAAM,KAAK,IAAI,WAAW,qBAAqB,CAAC;AAAA,UACnE;AAAA,QACF;AAAA,MACF;AAAA,MACA,qBAAqB,MAAY;AAC/B,cAAM,QAAQ,QAAQ,WAAW,SAAS,IAAI;AAE9C,YAAI,KAAK,KAAK,SAAS,8BAAe,YAAY;AAChD,gBAAM,WAAWA,cAAa,OAAO,KAAK,KAAK,IAAI;AACnD,cAAI,aAAa,KAAM;AACvB,cAAI,mBAAmB,KAAK,KAAK,GAAG;AAClC,iCAAqB,IAAI,QAAQ;AAAA,UACnC,OAAO;AAEL,iCAAqB,OAAO,QAAQ;AAAA,UACtC;AACA;AAAA,QACF;AAEA,YACE,KAAK,KAAK,SAAS,8BAAe,iBAClC,KAAK,KAAK,SAAS,8BAAe,cAClC;AACA,cAAI,mBAAmB,KAAK,KAAK,GAAG;AAClC,oBAAQ,OAAO;AAAA,cACb,MAAM,KAAK;AAAA,cACX,WAAW;AAAA,YACb,CAAC;AACD;AAAA,UACF;AACA,cACE,yBAAyB,KAAK,OAAO,OAAO,oBAAoB,GAChE;AACA,oBAAQ,OAAO;AAAA,cACb,MAAM,KAAK;AAAA,cACX,WAAW;AAAA,YACb,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAAA,MACA,eAAe,MAAY;AACzB,YAAI,KAAK,OAAO,SAAS,8BAAe,WAAY;AACpD,YAAI,CAAC,cAAc,KAAK,KAAK,OAAO,IAAI,KAAK,CAAC,oBAAoB,IAAI,GAAG;AACvE;AAAA,QACF;AACA,cAAM,QAAQ,QAAQ,WAAW,SAAS,IAAI;AAC9C,mBAAW,OAAO,KAAK,WAAW;AAChC,cAAI,IAAI,SAAS,8BAAe,cAAe;AAC/C,gBAAM,YAAY,OAAO,GAAG;AAC5B,cAAI,cAAc,QAAQ,UAAU,SAAS,8BAAe,YAAY;AACtE;AAAA,UACF;AACA,gBAAM,WAAWA,cAAa,OAAO,UAAU,IAAI;AACnD,cAAI,aAAa,KAAM,sBAAqB,OAAO,QAAQ;AAAA,QAC7D;AAAA,MACF;AAAA,MACA,iBAAiB,MAAY;AAE3B,YAAI,kBAAkB,IAAI,EAAG;AAC7B,cAAM,QAAQ,QAAQ,WAAW,SAAS,IAAI;AAC9C,cAAM,MAAM,OAAO,KAAK,MAAM;AAE9B,YAAI,mBAAmB,GAAG,GAAG;AAI3B,gBAAM,SAAS,KAAK;AACpB,cACE,OAAO,SAAS,8BAAe,kBAC/B,OAAO,WAAW,QAClB,KAAK,SAAS,SAAS,8BAAe,eACrC,KAAK,SAAS,SAAS,WACtB,KAAK,SAAS,SAAS,cACzB;AACA;AAAA,UACF;AACA,kBAAQ,OAAO,EAAE,MAAM,WAAW,qBAAqB,CAAC;AACxD;AAAA,QACF;AAEA,YACE,QAAQ,QACR,IAAI,SAAS,8BAAe,cAC5B,yBAAyB,KAAK,OAAO,oBAAoB,GACzD;AACA,kBAAQ,OAAO,EAAE,MAAM,WAAW,qBAAqB,CAAC;AACxD,gBAAM,WAAWA,cAAa,OAAO,IAAI,IAAI;AAC7C,cAAI,aAAa,MAAM;AACrB,iCAAqB,OAAO,QAAQ;AAAA,UACtC;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;AC9ZD,IAAAC,iBAA2D;AAC3D,gBAAyC;AACzC,kBAAqC;;;ACpB9B,IAAM,eAAe,CAAC,UAC3B,MAAM,QAAQ,sBAAsB,EAAE,EAAE,QAAQ,MAAM,EAAE;AAGnD,IAAM,cAAc,CAAC,UAC1B,MAAM,MAAM,KAAK,EAAE,OAAO,OAAO;;;AD0BnC,IAAM,iBACJ;AACF,IAAM,UACJ;AACF,IAAM,WAAW;AAEjB,IAAM,iBAAiB,IAAI,OAAO,OAAO,cAAc,QAAQ,OAAO,2BAA2B;AACjG,IAAM,qBAAqB,IAAI;AAAA,EAC7B,OAAO,cAAc,gCAAgC,QAAQ;AAAA,EAC7D;AACF;AAGA,IAAM,YAAY,oBAAI,IAAY,CAAC,MAAM,QAAQ,OAAO,MAAM,MAAM,WAAW,cAAc,YAAY,CAAC;AAC1G,IAAM,gBAAgB;AAGtB,IAAM,oBAAoB,oBAAI,IAAY;AAAA,EACxC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AACD,IAAM,qBAAqB,IAAI,OAAO,8BAA8B,QAAQ,YAAY,GAAG;AAE3F,IAAM,kBAAkB;AACxB,IAAM,oBAAoB;AAC1B,IAAM,kBAAkB;AAAA,EACtB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AACA,IAAM,qBAAqB,oBAAI,IAAqB;AAGpD,IAAM,sBAAsB,oBAAI,IAAY;AAAA,EAC1C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGD,IAAM,0BAA0B,oBAAI,IAAY;AAAA,EAC9C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAMD,SAAS,eAAe,MAA0C;AAChE,QAAM,OAAO,KAAK,eAAe;AACjC,MAAI,KAAK,SAAS,8BAAe,cAAe,QAAO,KAAK;AAC5D,MAAI,KAAK,SAAS,8BAAe,uBAAuB,KAAK,SAAS,SAAS,8BAAe,eAAe;AAC3G,WAAO,KAAK,SAAS;AAAA,EACvB;AACA,SAAO;AACT;AAEA,SAAS,qBAAqB,MAAuB;AACnD,SAAO,SAAS,SAAS,oBAAoB,IAAI,IAAI,KAAK,QAAQ,KAAK,IAAI;AAC7E;AAEA,IAAM,cAAc,CAAC,SAAiC;AACpD,MAAI,UAA4C,KAAK;AACrD,SAAO,YAAY,UAAa,YAAY,MAAM;AAChD,QAAI,QAAQ,SAAS,8BAAe,YAAY;AAC9C,YAAM,OAAO,eAAe,OAAO;AACnC,UAAI,SAAS,QAAQ,qBAAqB,IAAI,EAAG,QAAO;AAAA,IAC1D;AACA,cAAU,QAAQ;AAAA,EACpB;AACA,SAAO;AACT;AAEA,IAAM,0BAA0B,CAAC,SAAiC;AAChE,MAAI,UAA4C,KAAK;AACrD,SAAO,YAAY,UAAa,YAAY,MAAM;AAChD,QACE,QAAQ,SAAS,8BAAe,YAChC,SAAS,QAAQ,GAAG,MAAM,UAC1B,QAAQ,OAAO,SAAS,8BAAe,oBACvC,QAAQ,OAAO,OAAO,SAAS,8BAAe,kBAC9C,QAAQ,OAAO,OAAO,OAAO,SAAS,8BAAe,cACrD,QAAQ,OAAO,OAAO,OAAO,SAAS,cACtC;AACA,aAAO;AAAA,IACT;AACA,cAAU,QAAQ;AAAA,EACpB;AACA,SAAO;AACT;AAEA,IAAM,yBAAyB,CAAC,aAA8B;AAC5D,MAAI,UAAM,qBAAQ,QAAQ;AAC1B,QAAM,WAAO,mBAAM,GAAG,EAAE;AACxB,WAAS,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG;AACzC,UAAM,SAAS,mBAAmB,IAAI,GAAG;AACzC,QAAI,WAAW,OAAW,QAAO;AAEjC,QAAI,QAAQ;AACZ,eAAW,OAAO,iBAAiB;AACjC,YAAM,gBAAY,kBAAK,KAAK,GAAG;AAC/B,UAAI,KAAC,sBAAW,SAAS,EAAG;AAC5B,UAAI,QAAQ,mBAAmB;AAC7B,gBAAQ;AACR;AAAA,MACF;AACA,UAAI;AACF,YAAI,kBAAkB,SAAK,wBAAa,WAAW,MAAM,CAAC,GAAG;AAC3D,kBAAQ;AACR;AAAA,QACF;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF;AACA,uBAAmB,IAAI,KAAK,KAAK;AACjC,QAAI,SAAS,QAAQ,KAAM,QAAO;AAClC,cAAM,qBAAQ,GAAG;AAAA,EACnB;AACA,SAAO;AACT;AAEA,IAAM,WAAW,CAAC,QAAiD;AACjE,MAAI,IAAI,SAAS,8BAAe,WAAY,QAAO,IAAI;AACvD,MAAI,IAAI,SAAS,8BAAe,WAAW,OAAO,IAAI,UAAU,SAAU,QAAO,IAAI;AACrF,SAAO;AACT;AAEA,IAAO,iCAAQ,2BAAY;AAAA,EACzB,CAAC,SACC,gFAAgF,IAAI;AACxF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ;AAAA,MACN;AAAA,QACE,MAAM;AAAA,QACN,sBAAsB;AAAA,QACtB,YAAY;AAAA,UACV,uBAAuB,EAAE,MAAM,UAAU;AAAA,QAC3C;AAAA,MACF;AAAA,IACF;AAAA,IACA,UAAU;AAAA,MACR,YACE;AAAA,MACF,gBACE;AAAA,MACF,aACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC,CAAC,CAAC;AAAA,EACnB,OAAO,SAAS,CAAC,OAAO,GAAG;AACzB,QAAI,gBAAgB,KAAK,QAAQ,QAAQ,EAAG,QAAO,CAAC;AACpD,QAAI,SAAS,0BAA0B,QAAQ,CAAC,uBAAuB,QAAQ,QAAQ,GAAG;AACxF,aAAO,CAAC;AAAA,IACV;AAEA,UAAM,gBAAgB,CAAC,OAAe,SAA8B;AAClE,iBAAW,SAAS,YAAY,KAAK,GAAG;AACtC,cAAM,OAAO,aAAa,KAAK;AAC/B,YAAI,eAAe,KAAK,IAAI,GAAG;AAC7B,kBAAQ,OAAO,EAAE,MAAM,WAAW,cAAc,MAAM,EAAE,OAAO,MAAM,EAAE,CAAC;AAAA,QAC1E,WAAW,mBAAmB,KAAK,IAAI,GAAG;AACxC,kBAAQ,OAAO,EAAE,MAAM,WAAW,kBAAkB,MAAM,EAAE,OAAO,MAAM,EAAE,CAAC;AAAA,QAC9E;AAAA,MACF;AAAA,IACF;AAKA,UAAM,iBAAiB,CAAC,SAAqC;AAC3D,UAAI,SAAS,KAAM;AACnB,cAAQ,KAAK,MAAM;AAAA,QACjB,KAAK,8BAAe;AAClB,cAAI,OAAO,KAAK,UAAU,SAAU,eAAc,KAAK,OAAO,IAAI;AAClE;AAAA,QACF,KAAK,8BAAe;AAClB,qBAAW,SAAS,KAAK,OAAQ,eAAc,MAAM,MAAM,UAAU,IAAI,KAAK;AAC9E;AAAA,QACF,KAAK,8BAAe;AAClB,qBAAW,WAAW,KAAK,UAAU;AACnC,gBAAI,YAAY,QAAQ,QAAQ,SAAS,8BAAe,cAAe,gBAAe,OAAO;AAAA,UAC/F;AACA;AAAA,QACF,KAAK,8BAAe;AAClB,qBAAW,YAAY,KAAK,YAAY;AACtC,gBAAI,SAAS,SAAS,8BAAe,SAAU,gBAAe,SAAS,KAAK;AAAA,UAC9E;AACA;AAAA,QACF,KAAK,8BAAe;AAClB,yBAAe,KAAK,UAAU;AAC9B,yBAAe,KAAK,SAAS;AAC7B;AAAA,QACF,KAAK,8BAAe;AAClB,yBAAe,KAAK,KAAK;AACzB;AAAA,QACF;AACE;AAAA,MACJ;AAAA,IACF;AAEA,UAAM,sBAAsB,CAAC,SAA8B;AACzD,UACE,KAAK,SAAS,8BAAe,WAC7B,OAAO,KAAK,UAAU,YACtB,mBAAmB,KAAK,KAAK,KAAK,GAClC;AACA,gBAAQ,OAAO,EAAE,MAAM,WAAW,eAAe,MAAM,EAAE,OAAO,KAAK,MAAM,EAAE,CAAC;AAAA,MAChF;AAAA,IACF;AAEA,WAAO;AAAA,MACL,sCAAsC,MAAmC;AACvE,YAAI,KAAK,UAAU,KAAM;AACzB,YAAI,KAAK,MAAM,SAAS,8BAAe,QAAS,gBAAe,KAAK,KAAK;AAAA,iBAChE,KAAK,MAAM,SAAS,8BAAe,wBAAwB;AAClE,cAAI,KAAK,MAAM,WAAW,SAAS,8BAAe,oBAAoB;AACpE,2BAAe,KAAK,MAAM,UAAU;AAAA,UACtC;AAAA,QACF;AAAA,MACF;AAAA,MACA,eAAe,MAAqC;AAClD,YAAI,KAAK,OAAO,SAAS,8BAAe,cAAc,UAAU,IAAI,KAAK,OAAO,IAAI,GAAG;AACrF,qBAAW,OAAO,KAAK,WAAW;AAChC,gBAAI,IAAI,SAAS,8BAAe,cAAe,gBAAe,GAAG;AAAA,UACnE;AAAA,QACF;AAAA,MACF;AAAA,MACA,mBAAmB,MAAyC;AAC1D,YAAI,KAAK,GAAG,SAAS,8BAAe,cAAc,cAAc,KAAK,KAAK,GAAG,IAAI,GAAG;AAClF,yBAAe,KAAK,IAAI;AAAA,QAC1B;AAAA,MACF;AAAA,MACA,SAAS,MAA+B;AACtC,cAAM,OAAO,SAAS,KAAK,GAAG;AAC9B,YAAI,SAAS,QAAQ,cAAc,KAAK,IAAI,EAAG,gBAAe,KAAK,KAAK;AAAA,MAC1E;AAAA;AAAA;AAAA;AAAA,MAIA,kDAAkD,MAAmC;AACnF,YAAI,KAAK,OAAO,SAAS,8BAAe,QAAS;AACjD,YACE,OAAO,KAAK,MAAM,UAAU,YAC5B,wBAAwB,IAAI,KAAK,MAAM,MAAM,YAAY,CAAC,GAC1D;AACA;AAAA,QACF;AACA,YAAI,YAAY,IAAI,KAAK,wBAAwB,IAAI,EAAG;AACxD,4BAAoB,KAAK,KAAK;AAAA,MAChC;AAAA;AAAA,MAEA,8DAA8D,MAA+B;AAC3F,cAAM,OAAO,SAAS,KAAK,GAAG;AAC9B,YAAI,SAAS,QAAQ,kBAAkB,IAAI,IAAI,EAAG,qBAAoB,KAAK,KAAK;AAAA,MAClF;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;AE3TD,IAAAC,iBAA2C;AAM3C,IAAM,mBAAmB,oBAAI,IAAI,CAAC,QAAQ,OAAO,UAAU,OAAO,CAAC;AACnE,IAAM,yBAAyB,oBAAI,IAAI,CAAC,QAAQ,OAAO,UAAU,OAAO,CAAC;AAEzE,IAAM,kBACJ;AAMF,IAAM,yBACJ;AAIF,SAAS,SACP,SACA,MACa;AACb,SAAO,QAAQ,WAAW,SAAS,IAAI;AACzC;AAOA,SAAS,YACP,MACA,SACsB;AACtB,MAAI,CAAC,KAAM,QAAO;AAClB,MAAI,KAAK,SAAS,aAAc,QAAO;AAEvC,MAAI,QAA4B,SAAS,SAAS,IAAI;AACtD,SAAO,OAAO;AACZ,UAAM,WAAW,MAAM,IAAI,IAAI,KAAK,IAAI;AACxC,QAAI,YAAY,SAAS,KAAK,WAAW,GAAG;AAC1C,YAAM,MAAM,SAAS,KAAK,CAAC;AAC3B,UAAI,OAAO,IAAI,SAAS,YAAY;AAClC,cAAM,aAAa,IAAI;AACvB,YACE,WAAW,SAAS,wBACpB,WAAW,MACX;AACA,iBAAO,WAAW;AAAA,QACpB;AAAA,MACF;AAAA,IACF;AACA,YAAQ,MAAM;AAAA,EAChB;AACA,SAAO;AACT;AAEA,SAAS,SACP,MACA,SACS;AACT,QAAM,WAAW,YAAY,MAAM,OAAO;AAC1C,MAAI,CAAC,SAAU,QAAO;AAEtB,MAAI,SAAS,SAAS,aAAa,OAAO,SAAS,UAAU,UAAU;AACrE,WAAO,SAAS,MAAM,WAAW,OAAO;AAAA,EAC1C;AACA,MAAI,SAAS,SAAS,mBAAmB;AACvC,UAAM,aAAa,SAAS,OAAO,CAAC;AACpC,UAAM,SAAS,YAAY,MAAM;AACjC,WAAO,OAAO,WAAW,YAAY,OAAO,WAAW,OAAO;AAAA,EAChE;AACA,MAAI,SAAS,SAAS,sBAAsB,SAAS,aAAa,KAAK;AACrE,WAAO,SAAS,SAAS,MAAM,OAAO;AAAA,EACxC;AACA,SAAO;AACT;AAEA,SAAS,iBACP,MACA,SACS;AACT,QAAM,WAAW,YAAY,MAAM,OAAO;AAC1C,MAAI,CAAC,SAAU,QAAO;AAEtB,MAAI,SAAS,SAAS,aAAa,OAAO,SAAS,UAAU,UAAU;AACrE,WAAO,iBAAiB,IAAI,SAAS,MAAM,YAAY,CAAC;AAAA,EAC1D;AAEA,MACE,SAAS,SAAS,qBAClB,SAAS,YAAY,WAAW,GAChC;AACA,UAAM,MAAM,SAAS,OAAO,IAAI,CAAC,MAAM,EAAE,MAAM,MAAM,EAAE,KAAK,EAAE;AAC9D,WAAO,iBAAiB,IAAI,IAAI,YAAY,CAAC;AAAA,EAC/C;AAEA,MAAI,SAAS,SAAS,yBAAyB;AAC7C,WACE,iBAAiB,SAAS,YAAY,OAAO,KAC7C,iBAAiB,SAAS,WAAW,OAAO;AAAA,EAEhD;AAEA,MAAI,SAAS,SAAS,uBAAuB,SAAS,aAAa,MAAM;AACvE,WACE,iBAAiB,SAAS,MAAM,OAAO,KACvC,iBAAiB,SAAS,OAAO,OAAO;AAAA,EAE5C;AAEA,SAAO;AACT;AAEA,SAAS,gBACP,SACAC,WACsB;AACtB,MAAI,CAAC,WAAW,QAAQ,SAAS,mBAAoB,QAAO;AAC5D,aAAW,QAAQ,QAAQ,YAAY;AACrC,QAAI,KAAK,SAAS,WAAY;AAC9B,QAAIC,WAAyB;AAC7B,QAAI,KAAK,IAAI,SAAS,gBAAgB,CAAC,KAAK,UAAU;AACpD,MAAAA,WAAU,KAAK,IAAI;AAAA,IACrB,WACE,KAAK,IAAI,SAAS,aAClB,OAAO,KAAK,IAAI,UAAU,UAC1B;AACA,MAAAA,WAAU,KAAK,IAAI;AAAA,IACrB;AACA,QAAIA,aAAYD,WAAU;AAExB,UACE,KAAK,MAAM,SAAS,uBACpB,KAAK,MAAM,SAAS,kBACpB,KAAK,MAAM,SAAS,iBACpB;AACA,eAAO;AAAA,MACT;AACA,aAAO,KAAK;AAAA,IACd;AAAA,EACF;AACA,SAAO;AACT;AAEA,IAAO,gCAAQ,2BAAY;AAAA,EACzB,CAAC,SACC,8EAA8E,IAAI;AACtF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aAAa;AAAA,IACf;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,UAAU;AAAA,MACR,oBACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC;AAAA,EACjB,OAAO,SAAS;AACd,UAAM,WAAW,QAAQ;AACzB,QAAI,gBAAgB,KAAK,QAAQ,GAAG;AAClC,aAAO,CAAC;AAAA,IACV;AAIA,QAAI,sBAAsB;AAE1B,WAAO;AAAA,MACL,kBAAkB,MAAM;AACtB,YACE,OAAO,KAAK,OAAO,UAAU,YAC7B,uBAAuB,KAAK,KAAK,OAAO,KAAK,GAC7C;AACA,gCAAsB;AAAA,QACxB;AAAA,MACF;AAAA,MACA,eAAe,MAAM;AACnB,YAAI,oBAAqB;AACzB,YAAI,aAAa;AAGjB,YACE,KAAK,OAAO,SAAS,gBACrB,KAAK,OAAO,SAAS,SACrB;AACA,gBAAM,SAAS,KAAK,UAAU,CAAC;AAC/B,cAAI,UAAU,OAAO,SAAS,mBAAmB,SAAS,QAAQ,OAAO,GAAG;AAC1E,kBAAM,UAAU,KAAK,UAAU,CAAC;AAChC,gBAAI,WAAW,QAAQ,SAAS,iBAAiB;AAC/C,oBAAM,eAAe,YAAY,SAAS,OAAO;AACjD,oBAAM,aAAa,gBAAgB,cAAc,QAAQ;AACzD,kBAAI,cAAc,iBAAiB,YAAY,OAAO,GAAG;AACvD,6BAAa;AAAA,cACf;AAAA,YACF;AAAA,UACF;AAAA,QACF,WAGE,KAAK,OAAO,SAAS,sBACrB,KAAK,OAAO,SAAS,SAAS,gBAC9B,CAAC,KAAK,OAAO,UACb;AACA,gBAAM,aAAa,KAAK,OAAO,SAAS,KAAK,YAAY;AACzD,cAAI,uBAAuB,IAAI,UAAU,GAAG;AAC1C,kBAAM,SAAS,KAAK,UAAU,CAAC;AAI/B,kBAAM,gBAAgB,KAAK,UAAU;AAAA,cACnC,CAAC,QACC,IAAI,SAAS,6BACb,IAAI,SAAS;AAAA,YACjB;AACA,gBACE,UACA,OAAO,SAAS,mBAChB,CAAC,iBACD,SAAS,QAAQ,OAAO,GACxB;AACA,2BAAa;AAAA,YACf;AAAA,UACF;AAAA,QACF,WAGE,KAAK,OAAO,SAAS,iBACpB,KAAK,OAAO,SAAS,WAAW,KAAK,OAAO,SAAS,YACtD;AACA,gBAAM,WAAW,KAAK,UAAU,CAAC;AACjC,cAAI,YAAY,SAAS,SAAS,iBAAiB;AACjD,kBAAM,YAAY,YAAY,UAAU,OAAO;AAC/C,gBAAI,aAAa,UAAU,SAAS,oBAAoB;AACtD,oBAAM,UAAU,gBAAgB,WAAW,KAAK;AAChD,oBAAM,aAAa,gBAAgB,WAAW,QAAQ;AACtD,kBACE,WACA,SAAS,SAAS,OAAO,KACzB,cACA,iBAAiB,YAAY,OAAO,GACpC;AACA,6BAAa;AAAA,cACf;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAEA,YAAI,YAAY;AACd,kBAAQ,OAAO,EAAE,MAAM,WAAW,qBAAqB,CAAC;AAAA,QAC1D;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;ACxQD,IAAAE,iBAIO;AAOP,IAAM,eAAiD;AAAA,EACrD,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,QAAQ;AACV;AAGA,IAAM,0BAA4D;AAAA,EAChE,UAAU;AAAA,EACV,OAAO;AAAA,EACP,OAAO;AACT;AAGA,IAAM,sBAAsB,oBAAI,IAAY,CAAC,QAAQ,QAAQ,CAAC;AAE9D,IAAM,YAAY,CAAC,cACjB,UAAU,QAAQ,sBAAsB,OAAO,EAAE,YAAY;AAE/D,IAAM,kBAAkB,CACtB,SAC+F;AAC/F,aAAW,aAAa,KAAK,YAAY;AACvC,QACE,UAAU,SAAS,8BAAe,gBAClC,UAAU,KAAK,SAAS,8BAAe,iBACvC,UAAU,KAAK,SAAS,QACxB;AACA;AAAA,IACF;AACA,QAAI,UAAU,OAAO,SAAS,8BAAe,WAAW,OAAO,UAAU,MAAM,UAAU,UAAU;AACjG,aAAO,EAAE,MAAM,WAAW,OAAO,UAAU,MAAM,MAAM,YAAY,EAAE;AAAA,IACvE;AACA,WAAO,EAAE,MAAM,UAAU;AAAA,EAC3B;AACA,SAAO;AACT;AAEA,IAAM,oBAAoB,CAAC,SACzB,KAAK,WAAW;AAAA,EACd,CAAC,cACC,UAAU,SAAS,8BAAe,gBAClC,UAAU,KAAK,SAAS,8BAAe,iBACvC,UAAU,KAAK,SAAS;AAC5B;AAGF,IAAM,0BAA0B,CAAC,SAAoD;AACnF,QAAM,WAAW,gBAAgB,IAAI;AACrC,MAAI,kBAAkB,IAAI,EAAG,QAAO;AACpC,MAAI,aAAa,QAAQ,SAAS,SAAS,UAAW,QAAO;AAC7D,MAAI,oBAAoB,IAAI,SAAS,KAAK,EAAG,QAAO;AACpD,SAAO,wBAAwB,SAAS,KAAK,KAAK;AACpD;AAEA,IAAO,wBAAQ,2BAAY;AAAA,EACzB,CAAC,SACC,8EAA8E,IAAI;AACtF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,UAAU;AAAA,MACR,cACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC;AAAA,EACjB,OAAO,SAAS;AACd,QAAI,WAAW,QAAQ,QAAQ,KAAK,YAAY,QAAQ,QAAQ,GAAG;AACjE,aAAO,CAAC;AAAA,IACV;AAEA,WAAO;AAAA,MACL,kBAAkB,MAAwC;AAGxD,YAAI,KAAK,KAAK,SAAS,iBAAiB;AACtC;AAAA,QACF;AAEA,cAAM,cAAc,KAAK,KAAK;AAC9B,cAAM,cACJ,gBAAgB,UAAU,wBAAwB,IAAI,IAAI,aAAa,WAAW;AAEpF,YAAI,gBAAgB,UAAa,gBAAgB,MAAM;AACrD;AAAA,QACF;AAEA,gBAAQ,OAAO;AAAA,UACb;AAAA,UACA,WAAW;AAAA,UACX,MAAM;AAAA,YACJ,SAAS;AAAA,YACT;AAAA,YACA,WAAW,UAAU,WAAW;AAAA,UAClC;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;AC7ID,IAAAC,iBAKO;AASP,IAAM,oBAAoB,CAAC,eAA6C;AACtE,MAAI,WAAW,SAAS,8BAAe,eAAgB,QAAO;AAC9D,QAAM,SAAS,WAAW;AAC1B,MAAI,OAAO,SAAS,8BAAe,YAAY;AAC7C,WAAO,OAAO,SAAS;AAAA,EACzB;AACA,MACE,OAAO,SAAS,8BAAe,oBAC/B,CAAC,OAAO,YACR,OAAO,SAAS,SAAS,8BAAe,YACxC;AACA,WAAO,OAAO,SAAS,SAAS;AAAA,EAClC;AACA,SAAO;AACT;AAEA,IAAM,+BAA+B,CACnC,cACY;AACZ,MAAI,UAAU,SAAS,8BAAe,qBAAqB;AACzD,WAAO,kBAAkB,UAAU,UAAU;AAAA,EAC/C;AACA,MAAI,UAAU,SAAS,8BAAe,gBAAgB;AACpD,WAAO,kBAAkB,UAAU,QAAQ;AAAA,EAC7C;AACA,MAAI,UAAU,SAAS,8BAAe,iBAAiB;AACrD,WACE,UAAU,aAAa,QAAQ,kBAAkB,UAAU,QAAQ;AAAA,EAEvE;AAEA,MAAI,UAAU,SAAS,8BAAe,gBAAgB;AACpD,WAAO,UAAU,KAAK,KAAK,4BAA4B;AAAA,EACzD;AACA,SAAO;AACT;AAQA,IAAM,6BAA6B,CAAC,cAA2C;AAC7E,MAAI,UAAU,SAAS,8BAAe,eAAgB,QAAO;AAC7D,MAAI,UAAU,SAAS,8BAAe,gBAAgB;AACpD,WAAO,UAAU,KAAK,KAAK,0BAA0B;AAAA,EACvD;AACA,SAAO;AACT;AAOA,IAAM,uBAAuB,CAC3B,MACA,iBACY;AACZ,QAAM,cAAc,KAAK,MAAM,YAAY;AAC3C,SACE,gBAAgB,UAChB,YAAY,WAAW,WAAW,KAClC,eAAe,KAAK,MAAM,SAAS;AAEvC;AAUA,IAAM,2BAA2B,CAC/B,aACA,eACY;AACZ,MAAI,YAAY,WAAW,WAAW,GAAG;AACvC,UAAM,eAAe,WAAW,cAAc,WAAW;AACzD,UAAM,aAAa,eACf,WAAW,cAAc,YAAY,IACrC;AACJ,WACE,eAAe,QAAQ,WAAW,iBAAiB,UAAU,EAAE,SAAS;AAAA,EAE5E;AACA,QAAM,OAAO,YAAY,WAAW,CAAC;AACrC,MACE,SAAS,UACT,YAAY,WAAW,WAAW,KAClC,KAAK,SAAS,8BAAe,kBAC7B,KAAK,KAAK,WAAW,GACrB;AACA,WAAO,WAAW,kBAAkB,IAAI,EAAE,SAAS;AAAA,EACrD;AACA,SAAO;AACT;AAEA,IAAO,+BAAQ,2BAAY;AAAA,EACzB,CAAC,SACC,8EAA8E,IAAI;AACtF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,UAAU;AAAA,MACR,oBACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC;AAAA,EACjB,OAAO,SAAS;AACd,WAAO;AAAA,MACL,gBAAgB,MAAsC;AACpD,cAAM,eAAe,KAAK,MAAM;AAAA,UAC9B,CAAC,aAAa,SAAS,SAAS;AAAA,QAClC;AAGA,YAAI,iBAAiB,GAAI;AACzB,cAAM,cAAc,KAAK,MAAM,YAAY;AAC3C,YAAI,gBAAgB,OAAW;AAG/B,YAAI,YAAY,WAAW,KAAK,4BAA4B,EAAG;AAI/D,YAAI,YAAY,WAAW,KAAK,0BAA0B,EAAG;AAI7D,YAAI,qBAAqB,MAAM,YAAY,EAAG;AAK9C,YAAI,yBAAyB,aAAa,QAAQ,UAAU,EAAG;AAG/D,gBAAQ,OAAO;AAAA,UACb,MAAM;AAAA,UACN,WAAW;AAAA,QACb,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;ACvID,IAAAC,iBAA2D;;;ACnBpD,IAAM,gBAAgB;AAGtB,IAAM,gBAAgB;AAGtB,IAAM,qBAAqB;;;AD+BlC,IAAM,qBAAqB,CAAC,SAAiC;AAC3D,MAAI,UAAyB;AAC7B,SAAO,MAAM;AACX,QAAI,QAAQ,SAAS,8BAAe,YAAY;AAC9C,aAAO,QAAQ,SAAS,OAAO,mBAAmB,KAAK,QAAQ,IAAI;AAAA,IACrE;AACA,QAAI,QAAQ,SAAS,8BAAe,gBAAgB;AAClD,gBAAU,QAAQ;AAClB;AAAA,IACF;AACA,QAAI,QAAQ,SAAS,8BAAe,kBAAkB;AACpD,gBAAU,QAAQ;AAClB;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACF;AAQA,IAAM,iBAAiB,CAAC,SAAiC;AACvD,MAAI,KAAK,SAAS,8BAAe,eAAgB,QAAO;AACxD,QAAM,SAAS,KAAK;AACpB,MAAI,OAAO,SAAS,8BAAe,iBAAkB,QAAO;AAC5D,MAAI,OAAO,SAAU,QAAO;AAC5B,MAAI,OAAO,SAAS,SAAS,8BAAe,WAAY,QAAO;AAC/D,QAAM,SAAS,OAAO,SAAS;AAC/B,MAAI,WAAW,WAAW,WAAW,YAAa,QAAO;AACzD,SAAO,mBAAmB,OAAO,MAAM;AACzC;AAMA,IAAM,uBAAuB,CAAC,SAAiC;AAC7D,MAAI,UAAyB;AAC7B,MAAI,QAAQ,SAAS,8BAAe,iBAAiB;AACnD,cAAU,QAAQ;AAAA,EACpB;AACA,MAAI,QAAQ,SAAS,8BAAe,eAAgB,QAAO;AAC3D,QAAM,SAAS,QAAQ;AACvB,SACE,OAAO,SAAS,8BAAe,oBAC/B,CAAC,OAAO,YACR,OAAO,SAAS,SAAS,8BAAe,cACxC,OAAO,SAAS,SAAS;AAE7B;AAEA,IAAO,sCAAQ,2BAAY;AAAA,EACzB,CAAC,SACC,8EAA8E,IAAI;AACtF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,UAAU;AAAA,MACR,sBACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC;AAAA,EACjB,OAAO,SAAc;AACnB,QAAI,WAAW,QAAQ,QAAQ,GAAG;AAChC,aAAO,CAAC;AAAA,IACV;AAIA,UAAM,yBAAyB,CAAC,SAAiC;AAC/D,UAAI,KAAK,SAAS,8BAAe,WAAY,QAAO;AACpD,UAAI,YAAY,KAAK,KAAK,IAAI,EAAG,QAAO;AAExC,UAAI,QAA4B,QAAQ,WAAW,SAAS,IAAI;AAChE,aAAO,UAAU,MAAM;AACrB,cAAM,WAAW,MAAM,IAAI,IAAI,KAAK,IAAI;AACxC,YAAI,aAAa,UAAa,SAAS,KAAK,WAAW,GAAG;AACxD,gBAAM,MAAM,SAAS,KAAK,CAAC;AAC3B,cACE,QAAQ,UACR,IAAI,SAAS,cACb,IAAI,KAAK,SAAS,8BAAe,sBACjC,IAAI,KAAK,SAAS,MAClB;AACA,mBAAO,qBAAqB,IAAI,KAAK,IAAI;AAAA,UAC3C;AACA,iBAAO;AAAA,QACT;AACA,gBAAQ,MAAM;AAAA,MAChB;AACA,aAAO;AAAA,IACT;AAEA,UAAM,oBAAoB,CAAC,SAA2C;AACpE,YAAM,SAAS,KAAK;AACpB,UAAI,OAAO,SAAS,8BAAe,iBAAkB,QAAO;AAC5D,UACE,OAAO,SAAS,SAAS,8BAAe,cACxC,OAAO,SAAS,SAAS,OACzB;AACA,eAAO;AAAA,MACT;AACA,aAAO,uBAAuB,OAAO,MAAM;AAAA,IAC7C;AAKA,UAAM,sBAAsB,CAAC,SAAiC;AAC5D,UAAI,SAA2C,KAAK;AACpD,aAAO,WAAW,QAAQ,WAAW,QAAW;AAC9C,YAAI,eAAe,MAAM,EAAG,QAAO;AACnC,iBAAS,OAAO;AAAA,MAClB;AACA,aAAO;AAAA,IACT;AAIA,UAAM,wBAAwB,CAAC,SAAiC;AAC9D,YAAM,SAA2C,KAAK;AACtD,aACE,WAAW,QACX,WAAW,UACX,OAAO,SAAS,8BAAe,oBAC/B,OAAO,aAAa,gBACpB,OAAO,SAAS;AAAA,IAEpB;AAGA,UAAM,kBAAkB,CACtB,SACuC;AACvC,YAAM,SAAS,KAAK;AACpB,UACE,OAAO,SAAS,8BAAe,sBAC/B,OAAO,SAAS,QAChB,OAAO,GAAG,SAAS,8BAAe,YAClC;AACA,eAAO;AAAA,MACT;AACA,aAAO;AAAA,IACT;AAGA,UAAM,qBAAqB,CACzB,eACY;AACZ,YAAM,WAAW,QAAQ,WAAW,qBAAqB,UAAU,EAAE,CAAC;AACtE,UAAI,aAAa,OAAW,QAAO;AACnC,aAAO,SAAS,WAAW;AAAA,QACzB,CAAC,QACC,oBAAoB,IAAI,UAAU,KAClC,sBAAsB,IAAI,UAAU;AAAA,MACxC;AAAA,IACF;AAEA,WAAO;AAAA,MACL,eAAe,MAAqC;AAClD,YAAI,CAAC,kBAAkB,IAAI,EAAG;AAE9B,YAAI,oBAAoB,IAAI,KAAK,sBAAsB,IAAI,EAAG;AAE9D,cAAM,aAAa,gBAAgB,IAAI;AACvC,YAAI,eAAe,QAAQ,mBAAmB,UAAU,EAAG;AAE3D,gBAAQ,OAAO;AAAA,UACb;AAAA,UACA,WAAW;AAAA,QACb,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;AE3LD,IAAAC,iBAA2D;AAa3D,IAAM,cAGF;AAAA,EACF,QAAQ,EAAE,MAAM,eAAe,WAAW,UAAU;AAAA,EACpD,QAAQ,EAAE,MAAM,eAAe,WAAW,eAAe;AAAA,EACzD,QAAQ,EAAE,MAAM,oBAAoB,WAAW,gBAAgB;AACjE;AAUA,IAAM,qBAAqB;AAS3B,IAAM,uBAA4C,oBAAI,IAAI;AAAA,EACxD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGD,IAAM,qBAAqB,CAAC,WAC1B,CAAC,OAAO,YAAY,OAAO,SAAS,SAAS,8BAAe,aACxD,OAAO,SAAS,OAChB;AAON,IAAM,yBAAyB,CAAC,SAAiC;AAC/D,MAAI,UAAyB;AAE7B,SAAO,QAAQ,SAAS,8BAAe,kBAAkB;AACvD,UAAM,WAA0B,QAAQ;AACxC,QAAI,SAAS,SAAS,8BAAe,cAAc,SAAS,SAAS,KAAK;AACxE,aAAO;AAAA,IACT;AACA,QAAI,SAAS,SAAS,8BAAe,gBAAgB;AACnD,gBAAU,SAAS;AACnB;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEA,IAAO,gCAAQ,2BAAY;AAAA,EACzB,CAAC,SACC,8EAA8E,IAAI;AACtF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ;AAAA,MACN;AAAA,QACE,MAAM;AAAA,QACN,sBAAsB;AAAA,QACtB,YAAY;AAAA,UACV,YAAY;AAAA,YACV,MAAM;AAAA,YACN,MAAM,CAAC,UAAU,UAAU,QAAQ;AAAA,UACrC;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,IACA,UAAU;AAAA,MACR,SAAS;AAAA,MACT,cAAc;AAAA,MACd,eACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC,CAAC,CAAC;AAAA,EACnB,OAAO,SAAS,CAAC,UAAU,GAAG;AAC5B,UAAM,aAAa,YAAY,cAAc;AAC7C,UAAM,EAAE,MAAM,UAAU,IAAI,YAAY,UAAU;AAGlD,UAAM,oBAAoB,eAAe;AAGzC,QAAI,WAAW,QAAQ,QAAQ,KAAK,gBAAgB,QAAQ,UAAU,QAAQ,WAAW,IAAI,GAAG;AAC9F,aAAO,CAAC;AAAA,IACV;AAEA,WAAO;AAAA,MACL,mBAAmB,MAAyC;AAC1D,cAAM,OAAO,KAAK;AAClB,YAAI,SAAS,QAAQ,SAAS,OAAW;AACzC,YAAI,KAAK,SAAS,8BAAe,eAAgB;AAEjD,cAAM,SAAS,KAAK;AACpB,YAAI,OAAO,SAAS,8BAAe,iBAAkB;AAErD,YAAI,CAAC,uBAAuB,MAAM,EAAG;AAGrC,cAAM,WAAW,mBAAmB,MAAM;AAC1C,YAAI,aAAa,QAAQ,qBAAqB,IAAI,QAAQ,EAAG;AAE7D,YAAI,KAAK,GAAG,SAAS,8BAAe,WAAY;AAChD,YAAI,KAAK,KAAK,KAAK,GAAG,IAAI,EAAG;AAE7B,YAAI,qBAAqB,mBAAmB,KAAK,KAAK,GAAG,IAAI,EAAG;AAEhE,gBAAQ,OAAO;AAAA,UACb,MAAM,KAAK;AAAA,UACX;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;AC5KD,IAAAC,iBAA2C;AAK3C,IAAM,cAAc;AACpB,IAAM,cAAc;AACpB,IAAM,qBAAqB,oBAAI,IAAI,CAAC,aAAa,OAAO,QAAQ,CAAC;AAKjE,SAAS,cAAc,MAA8B;AACnD,SAAO,KAAK,SAAS,aAAa,KAAK,UAAU;AACnD;AAOA,SAAS,uBAAuB,MAA8B;AAC5D,MAAI,KAAK,SAAS,WAAW;AAC3B,QAAI,KAAK,UAAU,MAAM;AACvB,aAAO;AAAA,IACT;AACA,QAAI,OAAO,KAAK,UAAU,UAAU;AAClC,aAAO,KAAK,MAAM,KAAK,EAAE,YAAY,MAAM;AAAA,IAC7C;AAAA,EACF;AACA,SAAO;AACT;AAKA,SAAS,cAAc,MAA8B;AACnD,SAAO,KAAK,SAAS,aAAa,KAAK,UAAU;AACnD;AAQA,SAAS,2BAA2B,MAA8B;AAChE,MAAI,cAAc,IAAI,GAAG;AACvB,WAAO;AAAA,EACT;AACA,aAAW,OAAO,OAAO,KAAK,IAAI,GAAG;AACnC,QAAI,QAAQ,YAAY,QAAQ,SAAS,QAAQ,SAAS;AACxD;AAAA,IACF;AACA,UAAM,QAAS,KAA4C,GAAG;AAC9D,QAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,iBAAW,SAAS,OAAO;AACzB,YAAIC,QAAO,KAAK,KAAK,2BAA2B,KAAK,GAAG;AACtD,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF,WAAWA,QAAO,KAAK,KAAK,2BAA2B,KAAK,GAAG;AAC7D,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAASA,QAAO,OAAwC;AACtD,SACE,OAAO,UAAU,YACjB,UAAU,QACV,OAAQ,MAA6B,SAAS;AAElD;AAKA,SAAS,gBAAgB,MAA6C;AACpE,MAAI,KAAK,UAAU;AACjB,WAAO;AAAA,EACT;AACA,QAAM,MAAM,KAAK;AACjB,MAAI,IAAI,SAAS,cAAc;AAC7B,WAAO,IAAI;AAAA,EACb;AACA,MAAI,IAAI,SAAS,aAAa,OAAO,IAAI,UAAU,UAAU;AAC3D,WAAO,IAAI;AAAA,EACb;AACA,SAAO;AACT;AAMA,SAASC,YACP,MACoB;AACpB,QAAM,SAAS,KAAK;AACpB,MAAI,OAAO,SAAS,cAAc;AAChC,WAAO,OAAO;AAAA,EAChB;AACA,MACE,OAAO,SAAS,sBAChB,CAAC,OAAO,YACR,OAAO,SAAS,SAAS,cACzB;AACA,WAAO,OAAO,SAAS;AAAA,EACzB;AACA,SAAO;AACT;AAOA,SAAS,8BACP,MACS;AACT,QAAM,OAAOA,YAAW,IAAI;AAC5B,MAAI,SAAS,UAAa,KAAK,YAAY,MAAM,QAAQ;AACvD,WAAO;AAAA,EACT;AACA,QAAM,UAAU,KAAK,UAAU;AAAA,IAC7B,CAAC,QAA0C,IAAI,SAAS;AAAA,EAC1D;AACA,MAAI,YAAY,QAAW;AACzB,WAAO;AAAA,EACT;AACA,MAAI,iBAAiB;AACrB,MAAI,oBAAoB;AACxB,aAAW,QAAQ,QAAQ,YAAY;AACrC,QAAI,KAAK,SAAS,YAAY;AAC5B;AAAA,IACF;AACA,UAAM,MAAM,gBAAgB,IAAI;AAChC,QAAI,QAAQ,iBAAiB,cAAc,KAAK,KAAK,GAAG;AACtD,uBAAiB;AAAA,IACnB,WAAW,QAAQ,YAAY,2BAA2B,KAAK,KAAK,GAAG;AACrE,0BAAoB;AAAA,IACtB;AAAA,EACF;AACA,SAAO,kBAAkB;AAC3B;AAOA,SAAS,kCACP,MACS;AACT,MAAI,iBAAiB;AACrB,MAAI,kBAAkB;AACtB,aAAW,QAAQ,KAAK,YAAY;AAClC,QAAI,KAAK,SAAS,YAAY;AAC5B;AAAA,IACF;AACA,UAAM,MAAM,gBAAgB,IAAI;AAChC,QAAI,QAAQ,QAAW;AACrB;AAAA,IACF;AACA,UAAM,SAAS,IAAI,YAAY;AAC/B,QAAI,WAAW,eAAe,cAAc,KAAK,KAAK,GAAG;AACvD,uBAAiB;AAAA,IACnB,WAAW,WAAW,eAAe,uBAAuB,KAAK,KAAK,GAAG;AACvE,wBAAkB;AAAA,IACpB;AAAA,EACF;AACA,SAAO,kBAAkB;AAC3B;AASA,SAAS,sBACP,MAC2B;AAC3B,QAAM,SAAS,KAAK;AACpB,MACE,OAAO,SAAS,sBAChB,OAAO,YACP,OAAO,SAAS,SAAS,gBACzB,CAAC,mBAAmB,IAAI,OAAO,SAAS,KAAK,YAAY,CAAC,GAC1D;AACA,WAAO;AAAA,EACT;AACA,QAAM,CAAC,SAAS,QAAQ,IAAI,KAAK;AACjC,MACE,YAAY,UACZ,aAAa,UACb,QAAQ,SAAS,aACjB,OAAO,QAAQ,UAAU,UACzB;AACA,WAAO;AAAA,EACT;AACA,QAAM,SAAS,QAAQ,MAAM,YAAY;AACzC,MAAI,WAAW,eAAe,cAAc,QAAQ,GAAG;AACrD,WAAO;AAAA,EACT;AACA,MAAI,WAAW,eAAe,uBAAuB,QAAQ,GAAG;AAC9D,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAOA,SAAS,eAAe,MAAgD;AACtE,MAAI,UAAqC,KAAK;AAC9C,SAAO,SAAS;AACd,QACE,QAAQ,SAAS,yBACjB,QAAQ,SAAS,wBACjB,QAAQ,SAAS,2BACjB;AACA,aAAO;AAAA,IACT;AACA,cAAU,QAAQ;AAAA,EACpB;AACA,SAAO;AACT;AAOA,IAAO,4CAAQ,2BAAY;AAAA,EACzB,CAAC,SACC,8EAA8E,IAAI;AACtF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,UAAU;AAAA,MACR,6BACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC;AAAA,EACjB,OAAO,SAAS;AACd,UAAM,kBAAkB,oBAAI,IAA+C;AAE3E,aAAS,gBACP,MACA,MACM;AACN,YAAM,MAAM,eAAe,IAAI,KAAK;AACpC,UAAI,QAAQ,gBAAgB,IAAI,GAAG;AACnC,UAAI,UAAU,QAAW;AACvB,gBAAQ,EAAE,aAAa,CAAC,GAAG,kBAAkB,CAAC,EAAE;AAChD,wBAAgB,IAAI,KAAK,KAAK;AAAA,MAChC;AACA,UAAI,SAAS,UAAU;AACrB,cAAM,YAAY,KAAK,IAAI;AAAA,MAC7B,OAAO;AACL,cAAM,iBAAiB,KAAK,IAAI;AAAA,MAClC;AAAA,IACF;AAEA,WAAO;AAAA,MACL,cAAc,MAAoC;AAChD,YAAI,8BAA8B,IAAI,GAAG;AACvC,kBAAQ,OAAO,EAAE,MAAM,WAAW,8BAA8B,CAAC;AAAA,QACnE;AAAA,MACF;AAAA,MACA,eAAe,MAAqC;AAClD,YAAI,8BAA8B,IAAI,GAAG;AACvC,kBAAQ,OAAO,EAAE,MAAM,WAAW,8BAA8B,CAAC;AACjE;AAAA,QACF;AACA,cAAM,OAAO,sBAAsB,IAAI;AACvC,YAAI,SAAS,QAAW;AACtB,0BAAgB,MAAM,IAAI;AAAA,QAC5B;AAAA,MACF;AAAA,MACA,iBAAiB,MAAuC;AACtD,YAAI,kCAAkC,IAAI,GAAG;AAC3C,kBAAQ,OAAO,EAAE,MAAM,WAAW,8BAA8B,CAAC;AAAA,QACnE;AAAA,MACF;AAAA,MACA,iBAAuB;AACrB,mBAAW,EAAE,aAAa,iBAAiB,KAAK,gBAAgB,OAAO,GAAG;AACxE,cAAI,YAAY,SAAS,KAAK,iBAAiB,SAAS,GAAG;AACzD,uBAAW,QAAQ,aAAa;AAC9B,sBAAQ,OAAO;AAAA,gBACb;AAAA,gBACA,WAAW;AAAA,cACb,CAAC;AAAA,YACH;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;ACxSD,IAAAC,iBAA2D;AAQ3D,SAAS,gBAAgB,MAAoC;AAC3D,SACE,KAAK,SAAS,8BAAe,kBAC7B,KAAK,UAAU,WAAW,KAC1B,KAAK,OAAO,SAAS,8BAAe,oBACpC,CAAC,KAAK,OAAO,YACb,KAAK,OAAO,SAAS,SAAS,8BAAe,eAC5C,KAAK,OAAO,SAAS,SAAS,UAC7B,KAAK,OAAO,SAAS,SAAS;AAEpC;AAOA,IAAM,mBAAwC,oBAAI,IAAI;AAAA,EACpD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAOD,IAAM,uBACJ;AAEF,IAAM,gBAAgB,CAAC,YACrB,CAAC,qBAAqB,KAAK,QAAQ,KAAK;AAG1C,SAAS,eAAe,MAAoC;AAC1D,SACE,KAAK,SAAS,8BAAe,kBAC7B,KAAK,OAAO,SAAS,8BAAe,oBACpC,CAAC,KAAK,OAAO,YACb,KAAK,OAAO,SAAS,SAAS,8BAAe,cAC7C,iBAAiB,IAAI,KAAK,OAAO,SAAS,IAAI;AAElD;AAIA,SAAS,mBAAmB,MAAoC;AAC9D,UAAQ,KAAK,MAAM;AAAA,IACjB,KAAK,8BAAe;AAGlB,aAAO,EAAE,WAAW;AAAA,IACtB,KAAK,8BAAe;AAClB,aAAO,KAAK,SAAS;AAAA,IACvB,KAAK,8BAAe;AAElB,aACE,KAAK,aAAa,UAClB,KAAK,SAAS,SAAS,8BAAe;AAAA,IAE1C,KAAK,8BAAe;AAClB,aAAO,KAAK,WAAW,WAAW;AAAA,IACpC,KAAK,8BAAe;AAClB,aAAO,KAAK,SAAS,WAAW;AAAA,IAClC,KAAK,8BAAe;AAElB,aAAO,mBAAmB,KAAK,UAAU;AAAA,IAC3C;AACE,aAAO;AAAA,EACX;AACF;AAGA,SAAS,gBACP,SACS;AACT,QAAM,OAAO,QAAQ;AAErB,MAAI,KAAK,SAAS,8BAAe,gBAAgB;AAE/C,WAAO,mBAAmB,IAAI;AAAA,EAChC;AAEA,MAAI,KAAK,KAAK,WAAW,GAAG;AAE1B,WAAO;AAAA,EACT;AAEA,MAAI,KAAK,KAAK,WAAW,GAAG;AAC1B,UAAM,OAAO,KAAK,KAAK,CAAC;AACxB,QAAI,SAAS,UAAa,KAAK,SAAS,8BAAe,iBAAiB;AAEtE,aAAO,KAAK,aAAa,QAAQ,mBAAmB,KAAK,QAAQ;AAAA,IACnE;AAAA,EACF;AAEA,SAAO;AACT;AAEA,IAAO,kCAAQ,2BAAY;AAAA,EACzB,CAAC,SACC,8EAA8E,IAAI;AACtF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,UAAU;AAAA,MACR,aACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC;AAAA,EACjB,OAAO,SAAS;AACd,QAAI,WAAW,QAAQ,QAAQ,GAAG;AAChC,aAAO,CAAC;AAAA,IACV;AASA,UAAM,wBAAwB,CAC5B,MACA,YACY;AACZ,YAAM,aAAa,QAAQ;AAC3B,UAAI,WAAW,kBAAkB,OAAO,EAAE,KAAK,aAAa,GAAG;AAC7D,eAAO;AAAA,MACT;AAIA,UAAI,YAA2B;AAC/B,aACE,UAAU,WAAW,UACrB,UAAU,WAAW,QACrB,CAAC,UAAU,KAAK,SAAS,WAAW,KACpC,UAAU,SAAS,8BAAe,qBAClC;AACA,oBAAY,UAAU;AAAA,MACxB;AACA,UAAI,WAAW,kBAAkB,SAAS,EAAE,KAAK,aAAa,GAAG;AAC/D,eAAO;AAAA,MACT;AACA,aAAO,WACJ,iBAAiB,SAAS,EAC1B;AAAA,QACC,CAAC,MAAM,cAAc,CAAC,KAAK,EAAE,IAAI,MAAM,SAAS,UAAU,IAAI,IAAI;AAAA,MACpE;AAAA,IACJ;AAEA,WAAO;AAAA,MACL,eAAe,MAAqC;AAClD,YACE,KAAK,OAAO,SAAS,8BAAe,oBACpC,KAAK,OAAO,YACZ,KAAK,OAAO,SAAS,SAAS,8BAAe,cAC7C,KAAK,OAAO,SAAS,SAAS,SAC9B;AACA;AAAA,QACF;AAEA,YAAI,gBAAgB,KAAK,OAAO,MAAM,GAAG;AACvC;AAAA,QACF;AAEA,YAAI,eAAe,KAAK,OAAO,MAAM,GAAG;AACtC;AAAA,QACF;AAIA,YACE,KAAK,OAAO,SAAS,8BAAe,oBACpC,KAAK,OAAO,WAAW,MACvB;AACA;AAAA,QACF;AAEA,YAAI,KAAK,UAAU,WAAW,GAAG;AAC/B;AAAA,QACF;AACA,cAAM,UAAU,KAAK,UAAU,CAAC;AAChC,YACE,YAAY,UACX,QAAQ,SAAS,8BAAe,2BAC/B,QAAQ,SAAS,8BAAe,oBAClC;AACA;AAAA,QACF;AAEA,YAAI,sBAAsB,MAAM,OAAO,GAAG;AACxC;AAAA,QACF;AAEA,YAAI,gBAAgB,OAAO,GAAG;AAK5B,kBAAQ,OAAO,EAAE,MAAM,WAAW,cAAc,CAAC;AAAA,QACnD;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;AChPD,IAAAC,iBAKO;AAKP,IAAM,qBAAqB;AAU3B,IAAM,iBAAsC,oBAAI,IAAI;AAAA,EAClD;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,SAASC,mBACP,UACA,UACS;AACT,aAAW,WAAW,UAAU;AAE9B,UAAM,cAAc,QACjB,QAAQ,qBAAqB,MAAM,EACnC,QAAQ,SAAS,gBAAgB,EACjC,QAAQ,OAAO,WAAW,EAC1B,QAAQ,mBAAmB,IAAI;AAClC,QAAI,IAAI,OAAO,IAAI,WAAW,GAAG,EAAE,KAAK,QAAQ,GAAG;AACjD,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAQA,SAAS,wBAAwB,MAAgD;AAC/E,MAAI,KAAK,SAAS,8BAAe,kBAAkB;AACjD,WAAO;AAAA,EACT;AACA,aAAW,QAAQ,KAAK,YAAY;AAClC,QAAI,KAAK,SAAS,8BAAe,eAAe;AAC9C,aAAO;AAAA,IACT;AACA,QACG,KAAK,IAAI,SAAS,8BAAe,cAChC,KAAK,IAAI,SAAS,YACnB,KAAK,IAAI,SAAS,8BAAe,WAAW,KAAK,IAAI,UAAU,UAChE;AACA,aAAO;AAAA,IACT;AAEA,QAAI,KAAK,UAAU;AACjB,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAGA,SAAS,YAAY,MAAgD;AACnE,SACG,KAAK,SAAS,8BAAe,WAAW,OAAO,KAAK,UAAU,YAC/D,KAAK,SAAS,8BAAe;AAEjC;AAEA,IAAO,gCAAQ,2BAAY;AAAA,EACzB,CAAC,SACC,8EAA8E,IAAI;AACtF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ;AAAA,MACN;AAAA,QACE,MAAM;AAAA,QACN,sBAAsB;AAAA,QACtB,YAAY;AAAA,UACV,SAAS;AAAA,YACP,aACE;AAAA,YACF,MAAM;AAAA,YACN,OAAO,EAAE,MAAM,SAAS;AAAA,UAC1B;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,IACA,UAAU;AAAA,MACR,eACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC,CAAC,CAAC;AAAA,EACnB,OAAO,SAAS,CAAC,UAAU,GAAG;AAC5B,QACE,WAAW,QAAQ,QAAQ,KAC3B,aAAa,QAAQ,QAAQ,KAC7B,mBAAmB,KAAK,QAAQ,QAAQ,GACxC;AACA,aAAO,CAAC;AAAA,IACV;AACA,UAAM,UAAU,YAAY,WAAW,CAAC;AACxC,QAAI,QAAQ,SAAS,KAAKA,mBAAkB,QAAQ,UAAU,OAAO,GAAG;AACtE,aAAO,CAAC;AAAA,IACV;AAGA,aAAS,iBAAiB,YAA0C;AAClE,YAAM,QAAQ,QAAQ,WAAW,SAAS,UAAU;AACpD,YAAM,WAAW,wBAAS,aAAa,OAAO,WAAW,IAAI;AAG7D,aAAO,aAAa,QAAQ,SAAS,KAAK,WAAW;AAAA,IACvD;AAIA,aAASC,mBAAkB,QAAsC;AAC/D,UAAI,OAAO,SAAS,8BAAe,YAAY;AAC7C,eAAO,OAAO,SAAS,WAAW,iBAAiB,MAAM;AAAA,MAC3D;AACA,aACE,OAAO,SAAS,8BAAe,oBAC/B,CAAC,OAAO,YACR,OAAO,SAAS,SAAS,8BAAe,cACxC,OAAO,SAAS,SAAS,WACzB,OAAO,OAAO,SAAS,8BAAe,cACtC,eAAe,IAAI,OAAO,OAAO,IAAI,KACrC,iBAAiB,OAAO,MAAM;AAAA,IAElC;AAEA,WAAO;AAAA,MACL,eAAe,MAAqC;AAClD,YAAI,CAACA,mBAAkB,KAAK,MAAM,GAAG;AACnC;AAAA,QACF;AAKA,cAAM,CAAC,OAAO,IAAI,IAAI,KAAK;AAC3B,YAAI,KAAK,UAAU,WAAW,KAAK,UAAU,UAAa,CAAC,YAAY,KAAK,GAAG;AAC7E;AAAA,QACF;AAEA,YAAI,SAAS,UAAa,wBAAwB,IAAI,GAAG;AACvD,kBAAQ,OAAO,EAAE,MAAM,WAAW,gBAAgB,CAAC;AAAA,QACrD;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;AC3LD,IAAAC,iBAA2D;AAQ3D,IAAM,oBAAyC,oBAAI,IAAI;AAAA,EACrD;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,SAAS,sBAAsB,gBAA4C;AACzE,SACE,eAAe,SAAS,8BAAe,mBACvC,eAAe,SAAS,SAAS,8BAAe,cAChD,eAAe,SAAS,SAAS;AAErC;AAGA,SAAS,gBAAgB,MAAwC;AAC/D,SACE,KAAK,OAAO,SAAS,8BAAe,oBACpC,CAAC,KAAK,OAAO,YACb,KAAK,OAAO,SAAS,SAAS,8BAAe,cAC7C,kBAAkB,IAAI,KAAK,OAAO,SAAS,IAAI;AAEnD;AAQA,SAAS,mBACP,MACA,oBAC2D;AAC3D,OACG,KAAK,SAAS,8BAAe,kBAC5B,KAAK,SAAS,8BAAe,oBAC/B,CAAC,sBAAsB,KAAK,cAAc,KAC1C,CAAC,oBACD;AACA,WAAO;AAAA,EACT;AAEA,MACE,KAAK,SAAS,8BAAe,kBAC7B,gBAAgB,IAAI,GACpB;AAEA,UAAM,WAAW,mBAAmB,KAAK,QAAQ,kBAAkB;AACnE,QAAI,aAAa,MAAM;AACrB,aAAO;AAAA,IACT;AACA,eAAW,OAAO,KAAK,WAAW;AAChC,YAAM,QAAQ,mBAAmB,KAAK,IAAI;AAC1C,UAAI,UAAU,MAAM;AAClB,eAAO;AAAA,MACT;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAEA,aAAW,OAAO,OAAO,KAAK,IAAI,GAAG;AACnC,QAAI,QAAQ,UAAU;AACpB;AAAA,IACF;AACA,UAAM,QAAS,KAA4C,GAAG;AAC9D,UAAM,WAAW,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;AACtD,eAAW,SAAS,UAAU;AAC5B,UACE,UAAU,QACV,OAAO,UAAU,YACjB,UAAU,SACV,OAAQ,MAA4B,SAAS,UAC7C;AACA,cAAM,QAAQ;AAAA,UACZ;AAAA,UACA;AAAA,QACF;AACA,YAAI,UAAU,MAAM;AAClB,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAEA,IAAO,yCAAQ,2BAAY;AAAA,EACzB,CAAC,SACC,8EAA8E,IAAI;AACtF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,UAAU;AAAA,MACR,sBACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC;AAAA,EACjB,OAAO,SAAS;AACd,QAAI,WAAW,QAAQ,QAAQ,GAAG;AAChC,aAAO,CAAC;AAAA,IACV;AAEA,WAAO;AAAA,MACL,SAAS,MAA+B;AACtC,cAAM,sBACH,CAAC,KAAK,YACL,KAAK,IAAI,SAAS,8BAAe,cACjC,KAAK,IAAI,SAAS,oBACnB,KAAK,IAAI,SAAS,8BAAe,WAChC,KAAK,IAAI,UAAU;AACvB,YAAI,CAAC,qBAAqB;AACxB;AAAA,QACF;AAEA,YACE,KAAK,MAAM,SAAS,8BAAe,2BACnC,KAAK,MAAM,SAAS,8BAAe,oBACnC;AACA;AAAA,QACF;AAEA,cAAM,OAAO,mBAAmB,KAAK,MAAM,MAAM,KAAK;AACtD,YAAI,SAAS,MAAM;AACjB,kBAAQ,OAAO,EAAE,MAAM,MAAM,WAAW,uBAAuB,CAAC;AAAA,QAClE;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;ACvHD,IAAAC,iBAIO;AAOP,IAAM,0BAA0B;AAEhC,IAAM,wBAAwB,oBAAI,IAAoB;AAAA,EACpD,8BAAe;AAAA,EACf,8BAAe;AAAA,EACf,8BAAe;AACjB,CAAC;AAGD,IAAM,eAAe,oBAAI,IAAY;AAAA,EACnC;AAAA,EAAO;AAAA,EAAU;AAAA,EAAW;AAAA,EAAU;AAAA,EAAe;AAAA,EAAQ;AAAA,EAC7D;AAAA,EAAY;AAAA,EAAiB;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAQ;AAAA,EAAO;AAAA,EAC7D;AAAA,EAAW;AAAA,EAAS;AAAA,EAAU;AAAA,EAAU;AAAA,EAAQ;AAAA,EAAW;AAAA,EAAQ;AAAA,EACnE;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAY;AAAA,EAAW;AAAA,EAAe;AAAA,EAAM;AAAA,EAAQ;AAAA,EACpE;AAAA,EAAW;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAU;AAAA,EAAS;AAAA,EAC1D;AAAA,EAAkB;AAAA,EAAW;AAAA,EAAU;AAAA,EAAc;AAAA,EAAe;AAAA,EACpE;AAAA,EAAY;AAAA,EAAU;AAAA,EAAU;AAAA,EAAQ;AAAA,EAAa;AAAA,EAAW;AAAA,EAChE;AAAA,EAAe;AAAA,EAAW;AAAA,EAAe;AAAA,EAAc;AACzD,CAAC;AAGD,IAAM,kBAAkB,oBAAI,IAAY;AAAA,EACtC;AAAA,EAAU;AAAA,EAAS;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAU;AAAA,EAAU;AAAA,EAAW;AACpE,CAAC;AAGD,IAAM,oBAAoB,oBAAI,IAAY;AAAA,EACxC;AAAA,EAAO;AAAA,EAAO;AAAA,EAAW;AAAA,EAAW;AAAA,EAAQ;AAAA,EAAS;AAAA,EACrD;AAAA,EAAc;AAAA,EAAS;AAAA,EAAU;AAAA,EAAW;AAAA,EAAmB;AAAA,EAC/D;AAAA,EAAe;AAAA,EAAe;AAAA,EAAQ;AAAA,EAAkB;AAAA,EACxD;AAAA,EAAmB;AAAA,EAAY;AACjC,CAAC;AAED,SAASC,QAAO,OAAwC;AACtD,SACE,OAAO,UAAU,YACjB,UAAU,QACV,OAAQ,MAA6B,SAAS;AAElD;AAGA,SAAS,WAAW,MAAwC;AAC1D,QAAM,SAAS,KAAK;AACpB,MAAI,OAAO,SAAS,8BAAe,kBAAkB;AACnD,WAAO;AAAA,EACT;AACA,QAAM,WAAW,OAAO;AACxB,MAAI,SAAS,SAAS,8BAAe,YAAY;AAC/C,WAAO;AAAA,EACT;AACA,MACE,OAAO,OAAO,SAAS,8BAAe,cACtC,gBAAgB,IAAI,OAAO,OAAO,IAAI,GACtC;AACA,WAAO;AAAA,EACT;AACA,SAAO,aAAa,IAAI,SAAS,IAAI;AACvC;AAEA,SAAS,UAAU,MAAuC;AACxD,SACE,KAAK,OAAO,SAAS,8BAAe,cACpC,kBAAkB,IAAI,KAAK,OAAO,IAAI;AAE1C;AAMA,SAAS,eACP,MACA,WACS;AACT,MAAI,QAAQ;AAEZ,QAAM,QAAQ,CAAC,YAAiC;AAC9C,QAAI,OAAO;AACT;AAAA,IACF;AACA,QAAI,UAAU,OAAO,GAAG;AACtB,cAAQ;AACR;AAAA,IACF;AACA,eAAW,OAAO,OAAO,KAAK,OAAO,GAAG;AACtC,UAAI,QAAQ,UAAU;AACpB;AAAA,MACF;AACA,UAAI,sBAAsB,IAAI,QAAQ,IAAI,KAAK,QAAQ,QAAQ;AAC7D;AAAA,MACF;AACA,YAAM,QAAS,QAA+C,GAAG;AACjE,UAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,mBAAW,SAAS,OAAO;AACzB,cAAIA,QAAO,KAAK,GAAG;AACjB,kBAAM,KAAK;AAAA,UACb;AAAA,QACF;AAAA,MACF,WAAWA,QAAO,KAAK,GAAG;AACxB,cAAM,KAAK;AAAA,MACb;AACA,UAAI,OAAO;AACT;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,QAAM,IAAI;AACV,SAAO;AACT;AAEA,IAAM,WAAW,CAAC,SAChB,eAAe,MAAM,CAAC,MAAM,EAAE,SAAS,8BAAe,eAAe;AAEvE,IAAM,uBAAuB,CAAC,SAC5B;AAAA,EACE;AAAA,EACA,CAAC,MACE,EAAE,SAAS,8BAAe,kBAAkB,CAAC,WAAW,CAAC,KACzD,EAAE,SAAS,8BAAe,iBAAiB,CAAC,UAAU,CAAC;AAC5D;AAGF,SAASC,QAAO,MAAgD;AAC9D,MAAI,UAAU;AACd,SACE,QAAQ,SAAS,8BAAe,mBAChC,QAAQ,SAAS,8BAAe,qBAChC;AACA,cAAU,QAAQ;AAAA,EACpB;AACA,SAAO;AACT;AAGA,SAAS,oBAAoB,MAAmC;AAC9D,SACE,KAAK,SAAS,8BAAe,uBAC7BA,QAAO,KAAK,UAAU,EAAE,SAAS,8BAAe;AAEpD;AAaA,SAAS,SAAS,MAAmC;AACnD,MAAI,SAAS,IAAI,GAAG;AAClB,WAAO;AAAA,EACT;AACA,MAAI,oBAAoB,IAAI,GAAG;AAC7B,WAAO;AAAA,EACT;AACA,MAAI,KAAK,SAAS,8BAAe,gBAAgB;AAC/C,WAAO,KAAK,KAAK,KAAK,QAAQ;AAAA,EAChC;AACA,MAAI,KAAK,SAAS,8BAAe,aAAa;AAC5C,WACE,qBAAqB,KAAK,IAAI,KAC9B,SAAS,KAAK,UAAU,KACvB,KAAK,cAAc,QAAQ,SAAS,KAAK,SAAS;AAAA,EAEvD;AACA,SAAO,qBAAqB,IAAI;AAClC;AAMA,SAAS,gBAAgB,SAA+C;AACtE,MAAI,YAAY,MAAM;AACpB,WAAO;AAAA,EACT;AACA,QAAM,OAAO,QAAQ,KAAK;AAC1B,QAAM,OAAO,KAAK,KAAK,SAAS,CAAC;AACjC,SAAO,SAAS,UAAa,KAAK,SAAS,8BAAe;AAC5D;AAEA,IAAO,4BAAQ,2BAAY;AAAA,EACzB,CAAC,SACC,gFAAgF,IAAI;AACxF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,UAAU;AAAA,MACR,aACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC;AAAA,EACjB,OAAO,SAAS;AACd,QAAI,gBAAgB,QAAQ,UAAU,QAAQ,WAAW,IAAI,GAAG;AAC9D,aAAO,CAAC;AAAA,IACV;AAEA,UAAM,aAAa,QAAQ;AAE3B,WAAO;AAAA,MACL,aAAa,MAAmC;AAC9C,YAAI,KAAK,cAAc,MAAM;AAC3B;AAAA,QACF;AACA,YAAI,gBAAgB,KAAK,OAAO,GAAG;AACjC;AAAA,QACF;AAEA,cAAM,QAAQ,KAAK,MAAM,KAAK,OAAO,QAAQ,EAAE;AAC/C,YAAI,SAAS,yBAAyB;AACpC;AAAA,QACF;AAEA,cAAM,aAAa,WAAW,cAAc,IAAI;AAChD,gBAAQ,OAAO;AAAA,UACb,MAAM,cAAc;AAAA,UACpB,WAAW;AAAA,UACX,MAAM,EAAE,OAAO,KAAK,wBAAwB;AAAA,QAC9C,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;ACpND,IAAAC,iBAA2C;;;ACxCpC,IAAM,eAAoC,oBAAI,IAAI;AAAA,EACvD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAUM,IAAM,kBAAuC,oBAAI,IAAI;AAAA,EAC1D;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AASD,IAAM,mBAAwC,oBAAI,IAAI;AAAA,EACpD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAMD,IAAM,iBAAsC,oBAAI,IAAI,CAAC,QAAQ,SAAS,QAAQ,OAAO,CAAC;AAa/E,IAAM,gBAAqC,oBAAI,IAAI;AAAA,EACxD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AASD,IAAM,aAAkC,oBAAI,IAAI;AAAA,EAC9C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAMD,IAAM,WAAW;AACjB,IAAM,aAAa;AAQZ,SAAS,SAAS,YAA8B;AACrD,QAAM,SAAmB,CAAC;AAC1B,aAAW,WAAW,WAAW,MAAM,UAAU,GAAG;AAClD,QAAI,CAAC,SAAS;AACZ;AAAA,IACF;AACA,WAAO,KAAK,QAAQ,YAAY,CAAC;AACjC,eAAW,QAAQ,QAAQ,MAAM,QAAQ,KAAK,CAAC,GAAG;AAChD,aAAO,KAAK,KAAK,YAAY,CAAC;AAAA,IAChC;AAAA,EACF;AACA,SAAO;AACT;AAaO,SAAS,YAAY,YAAwC;AAClE,aAAW,WAAW,WAAW,MAAM,UAAU,GAAG;AAClD,QAAI,SAAS;AACX,cAAQ,QAAQ,MAAM,QAAQ,KAAK,CAAC,OAAO,GAAG,CAAC,GAAG,YAAY;AAAA,IAChE;AAAA,EACF;AACA,SAAO;AACT;AAGO,SAAS,UAAU,QAAoC;AAC5D,WAAS,IAAI,GAAG,IAAI,IAAI,OAAO,QAAQ,KAAK;AAC1C,QAAI,OAAO,CAAC,MAAM,SAAS,OAAO,IAAI,CAAC,MAAM,OAAO;AAClD,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAYO,SAAS,aACd,YACA,YAAiC,iBACxB;AACT,QAAM,SAAS,SAAS,UAAU;AAClC,QAAM,OAAO,OAAO,GAAG,EAAE;AACzB,MAAI,SAAS,UAAa,UAAU,IAAI,IAAI,GAAG;AAC7C,WAAO;AAAA,EACT;AACA,QAAM,QAAQ,YAAY,UAAU;AACpC,MAAI,UAAU,UAAa,cAAc,IAAI,KAAK,GAAG;AACnD,WAAO;AAAA,EACT;AACA,MAAI,OAAO,KAAK,CAAC,QAAQ,aAAa,IAAI,GAAG,CAAC,GAAG;AAC/C,WAAO;AAAA,EACT;AACA,SAAO,UAAU,MAAM;AACzB;AAYO,SAAS,iBAAiB,YAA6B;AAC5D,MAAI,CAAC,aAAa,UAAU,GAAG;AAC7B,WAAO;AAAA,EACT;AACA,QAAM,SAAS,SAAS,UAAU;AAClC,QAAM,OAAO,OAAO,GAAG,EAAE;AACzB,MAAI,SAAS,UAAa,iBAAiB,IAAI,IAAI,GAAG;AACpD,WAAO;AAAA,EACT;AACA,MAAI,OAAO,KAAK,CAAC,QAAQ,eAAe,IAAI,GAAG,CAAC,GAAG;AACjD,WAAO;AAAA,EACT;AACA,MAAI,OAAO,KAAK,CAAC,QAAQ,WAAW,IAAI,GAAG,CAAC,GAAG;AAC7C,WAAO;AAAA,EACT;AACA,SAAO,UAAU,MAAM;AACzB;;;ADvKA,IAAM,sBAA2C,oBAAI,IAAI;AAAA,EACvD,GAAG;AAAA,EACH;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,IAAM,eAAe;AACrB,IAAM,gCAAqD,oBAAI,IAAI,CAAC,KAAK,CAAC;AAG1E,SAAS,gBAAgB,MAAuB;AAC9C,MAAI,aAAa,KAAK,IAAI,GAAG;AAC3B,WAAO;AAAA,EACT;AACA,MAAI,SAAS,IAAI,EAAE,KAAK,CAAC,QAAQ,8BAA8B,IAAI,GAAG,CAAC,GAAG;AACxE,WAAO;AAAA,EACT;AACA,SAAO,aAAa,MAAM,mBAAmB;AAC/C;AASA,SAAS,iBAAiB,MAAkC;AAC1D,MAAI,KAAK,WAAW;AAClB,WAAO;AAAA,EACT;AACA,SAAO,KAAK,MAAM,SAAS,gBAAgB,KAAK,MAAM,SAAS;AACjE;AASA,IAAM,iBAAsC,oBAAI,IAAI;AAAA,EAClD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAMD,IAAM,uBAA4C,oBAAI,IAAI,CAAC,UAAU,CAAC;AAMtE,IAAM,oBAAoB;AAM1B,IAAM,wBAA6C,oBAAI,IAAI;AAAA,EACzD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGD,SAAS,cAAc,MAAuB;AAC5C,MAAI,aAAa,KAAK,IAAI,KAAK,kBAAkB,KAAK,IAAI,GAAG;AAC3D,WAAO;AAAA,EACT;AACA,QAAM,SAAS,SAAS,IAAI;AAC5B,MAAI,OAAO,KAAK,CAAC,QAAQ,sBAAsB,IAAI,GAAG,CAAC,GAAG;AACxD,WAAO;AAAA,EACT;AAGA,QAAM,QAAQ,YAAY,IAAI;AAC9B,MAAI,UAAU,UAAa,cAAc,IAAI,KAAK,GAAG;AACnD,WAAO;AAAA,EACT;AACA,MAAI,qBAAqB,IAAI,KAAK,YAAY,CAAC,GAAG;AAChD,WAAO;AAAA,EACT;AACA,QAAM,OAAO,OAAO,GAAG,EAAE;AACzB,SAAO,SAAS,UAAa,eAAe,IAAI,IAAI;AACtD;AASA,SAAS,iBAAiB,OAAqC;AAC7D,MAAI,MAAM,SAAS,cAAc;AAC/B,WAAO,cAAc,MAAM,IAAI,IAAI,MAAM,OAAO;AAAA,EAClD;AACA,MACE,MAAM,SAAS,sBACf,CAAC,MAAM,YACP,MAAM,SAAS,SAAS,cACxB;AACA,WAAO,cAAc,MAAM,SAAS,IAAI,IAAI,MAAM,SAAS,OAAO;AAAA,EACpE;AACA,SAAO;AACT;AAGA,SAASC,iBAAgB,MAAwC;AAC/D,MAAI,KAAK,UAAU;AACjB,WAAO;AAAA,EACT;AACA,MAAI,KAAK,IAAI,SAAS,cAAc;AAClC,WAAO,KAAK,IAAI;AAAA,EAClB;AACA,MAAI,KAAK,IAAI,SAAS,aAAa,OAAO,KAAK,IAAI,UAAU,UAAU;AACrE,WAAO,KAAK,IAAI;AAAA,EAClB;AACA,SAAO;AACT;AAEA,IAAO,2BAAQ,2BAAY;AAAA,EACzB,CAAC,SACC,gFAAgF,IAAI;AACxF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ;AAAA,MACN;AAAA,QACE,MAAM;AAAA,QACN,sBAAsB;AAAA,QACtB,YAAY,EAAE,GAAG,0BAA0B;AAAA,MAC7C;AAAA,IACF;AAAA,IACA,UAAU;AAAA,MACR,eACE;AAAA,MACF,gBACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC,CAAC,CAAC;AAAA,EACnB,OAAO,SAAS,CAAC,cAAc,GAAG;AAChC,UAAM,UAAU,iBAAiB,cAAc;AAE/C,UAAM,iBAAiB,CAAC,WAAW,QAAQ,QAAQ;AAGnD,aAAS,qBAAqB,KAA6B;AACzD,YAAM,OACJ,IAAI,SAAS,eACT,IAAI,OACJ,IAAI,SAAS,sBACX,CAAC,IAAI,YACL,IAAI,SAAS,SAAS,eACtB,IAAI,SAAS,OACb;AACR,UAAI,SAAS,QAAQ,CAAC,gBAAgB,IAAI,GAAG;AAC3C,eAAO;AAAA,MACT;AACA,cAAQ,OAAO,EAAE,MAAM,KAAK,WAAW,iBAAiB,MAAM,EAAE,KAAK,EAAE,CAAC;AACxE,aAAO;AAAA,IACT;AAGA,aAAS,qBAAqB,MAAkC;AAC9D,YAAMC,WAAUD,iBAAgB,IAAI;AACpC,UAAIC,aAAY,QAAQ,CAAC,gBAAgBA,QAAO,KAAK,CAAC,iBAAiB,IAAI,GAAG;AAC5E,eAAO;AAAA,MACT;AACA,cAAQ,OAAO,EAAE,MAAM,MAAM,WAAW,iBAAiB,MAAM,EAAE,MAAMA,SAAQ,EAAE,CAAC;AAClF,aAAO;AAAA,IACT;AAGA,aAAS,cAAc,MAAqB,OAA4B;AACtE,UAAI,CAAC,gBAAgB;AACnB;AAAA,MACF;AACA,YAAM,OAAO,iBAAiB,KAAK;AACnC,UAAI,SAAS,MAAM;AACjB,gBAAQ,OAAO,EAAE,MAAM,WAAW,kBAAkB,MAAM,EAAE,KAAK,EAAE,CAAC;AAAA,MACtE;AAAA,IACF;AAEA,WAAO;AAAA,MACL,eAAe,MAAqC;AAClD,YAAI,CAAC,QAAQ,cAAc,IAAI,GAAG;AAChC;AAAA,QACF;AAEA,mBAAW,OAAO,KAAK,WAAW;AAChC,cAAI,IAAI,SAAS,oBAAoB;AACnC,uBAAW,QAAQ,IAAI,YAAY;AACjC,kBAAI,KAAK,SAAS,YAAY;AAC5B;AAAA,cACF;AACA,kBAAI,CAAC,qBAAqB,IAAI,GAAG;AAC/B,8BAAc,MAAM,KAAK,KAAK;AAAA,cAChC;AAAA,YACF;AACA;AAAA,UACF;AACA,cAAI,CAAC,qBAAqB,GAAG,GAAG;AAC9B,0BAAc,KAAK,GAAG;AAAA,UACxB;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;AExVD,IAAAC,iBAA2C;AAC3C,IAAAA,iBAA+B;AAM/B,SAAS,gBAAgB,MAAkC;AACzD,SAAO,KAAK,SAAS,8BAAe;AACtC;AAEA,SAAS,iBAAiB,gBAA4C;AACpE,SACE,eAAe,SAAS,8BAAe,mBACvC,eAAe,SAAS,SAAS,8BAAe,cAChD,eAAe,SAAS,SAAS;AAErC;AAEA,IAAO,yBAAQ,2BAAY;AAAA,EACzB,CAAC,SACC,8EAA8E,IAAI;AACtF,EAAkB;AAAA,EAChB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,UAAU;AAAA,MACR,OACE;AAAA,MACF,YACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC;AAAA,EACjB,OAAO,SAAS;AACd,QAAI,WAAW,QAAQ,QAAQ,KAAK,gBAAgB,QAAQ,UAAU,QAAQ,WAAW,IAAI,GAAG;AAC9F,aAAO,CAAC;AAAA,IACV;AAEA,aAAS,eACP,MACM;AACN,UAAI,iBAAiB,KAAK,cAAc,GAAG;AACzC;AAAA,MACF;AAEA,UAAI,gBAAgB,KAAK,cAAc,GAAG;AACxC,gBAAQ,OAAO,EAAE,MAAM,WAAW,QAAQ,CAAC;AAC3C;AAAA,MACF;AAEA,YAAM,QAAQ,KAAK;AACnB,UACE,MAAM,SAAS,8BAAe,kBAC9B,MAAM,SAAS,8BAAe,iBAC9B;AACA,gBAAQ,OAAO,EAAE,MAAM,WAAW,aAAa,CAAC;AAAA,MAClD;AAAA,IACF;AAEA,WAAO;AAAA,MACL,gBAAgB;AAAA,MAChB,iBAAiB;AAAA,IACnB;AAAA,EACF;AACF,CAAC;;;AC5CD,IAAAC,iBAKO;AACP,SAAoB;AAcpB,IAAM,gBAAqC,oBAAI,IAAI;AAAA,EACjD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,IAAM,iBAAiB;AACvB,IAAM,mBAAmB;AAEzB,IAAM,aAAkC,oBAAI,IAAI,CAAC,QAAQ,OAAO,CAAC;AAQjE,SAAS,YAAY,KAAsB;AACzC,SAAO,eAAe,KAAK,GAAG,KAAK,IAAI,UAAU,KAAK,CAAC,WAAW,IAAI,GAAG;AAC3E;AAEA,IAAM,kBAAqC;AAAA,EACzC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,SAAS,cAAc,UAAkB,YAA6B;AACpE,MAAI,gBAAgB,KAAK,CAAC,OAAO,GAAG,KAAK,QAAQ,CAAC,GAAG;AACnD,WAAO;AAAA,EACT;AACA,SAAO,eAAe,KAAK,WAAW,MAAM,GAAG,IAAI,CAAC;AACtD;AAMA,SAAS,SAAS,MAAsB;AACtC,QAAM,QAAQ,KACX,QAAQ,sBAAsB,OAAO,EACrC,MAAM,QAAQ,EACd,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC;AAC7B,QAAM,OAAO,MAAM,MAAM,SAAS,CAAC,KAAK;AACxC,SAAO,KAAK,YAAY;AAC1B;AAEA,SAAS,iBAAiB,MAAuB;AAC/C,SAAO,cAAc,IAAI,SAAS,IAAI,CAAC;AACzC;AAEA,SAAS,QACP,KACe;AACf,MAAI,IAAI,SAAS,8BAAe,YAAY;AAC1C,WAAO,IAAI;AAAA,EACb;AACA,MAAI,IAAI,SAAS,8BAAe,WAAW,OAAO,IAAI,UAAU,UAAU;AACxE,WAAO,IAAI;AAAA,EACb;AACA,SAAO;AACT;AAEA,SAAS,sBAAsB,GAA+B;AAC5D,SACE,EAAE,SAAS,8BAAe,iBAC1B,EAAE,QAAQ,SAAS,8BAAe,WAClC,OAAO,EAAE,QAAQ,UAAU;AAE/B;AAGA,SAAS,qBAAqB,MAA8C;AAC1E,MAAI,MAAM,SAAS,8BAAe,aAAa;AAC7C,WAAO;AAAA,EACT;AACA,SAAO,KAAK,MAAM,OAAO,qBAAqB,EAAE,UAAU;AAC5D;AAUA,SAAS,iBAAiB,MAAwB;AAChD,QAAM,QAAQ,KAAK,QAAQ,IAAI,KAAK,QAAQ,CAAC,IAAI;AACjD,SAAO,MAAM,KAAK,CAAC,OAAO,EAAE,QAAW,aAAU,YAAY,CAAC;AAChE;AAEA,SAAS,qBAAqB,IAAwC;AACpE,MAAI,OAAO,QAAW;AACpB,WAAO;AAAA,EACT;AACA,SAAO,GAAG,qBAAqB,GAAG,SAAS,SAAS,gBAAgB;AACtE;AAEA,SAAS,2BAA2B,KAAqC;AACvE,SACE,KAAK,cAAc,KAAK,CAAC,MAAM,qBAAqB,EAAE,cAAc,CAAC,CAAC,KACtE;AAEJ;AAOA,SAAS,wBACP,MAC2B;AAC3B,MAAI,OAAgB,KAAK;AACzB,SACE,CAAI,oBAAiB,IAAI,KACzB,EAAK,yBAAsB,IAAI,KAAK,KAAK,gBAAgB,SACzD;AACA,QAAI,KAAK,WAAW,QAAW;AAC7B,aAAO;AAAA,IACT;AACA,WAAO,KAAK;AAAA,EACd;AACA,SAAU,oBAAiB,IAAI,IAAI,KAAK,aAAa,KAAK;AAC5D;AAGA,SAAS,OAAO,MAAoC;AAClD,MAAI,KAAK,SAAS,8BAAe,YAAY;AAC3C,WAAO,KAAK;AAAA,EACd;AACA,MAAI,KAAK,SAAS,8BAAe,oBAAoB,CAAC,KAAK,UAAU;AACnE,UAAM,QAAQ,OAAO,KAAK,MAAM;AAChC,QAAI,UAAU,QAAQ,KAAK,SAAS,SAAS,8BAAe,YAAY;AACtE,aAAO;AAAA,IACT;AACA,WAAO,GAAG,KAAK,IAAI,KAAK,SAAS,IAAI;AAAA,EACvC;AACA,SAAO;AACT;AAEA,SAAS,WAAW,MAAoC;AACtD,MAAI,KAAK,SAAS,8BAAe,WAAW,OAAO,KAAK,UAAU,UAAU;AAC1E,WAAO,KAAK;AAAA,EACd;AACA,SAAO;AACT;AAoBA,IAAO,sCAAQ,2BAAY;AAAA,EACzB,CAAC,SACC,8EAA8E,IAAI;AACtF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ;AAAA,MACN;AAAA,QACE,MAAM;AAAA,QACN,sBAAsB;AAAA,QACtB,YAAY;AAAA,UACV,cAAc;AAAA,YACZ,MAAM;AAAA,YACN,OAAO,EAAE,MAAM,SAAS;AAAA,UAC1B;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,IACA,UAAU;AAAA,MACR,iBACE;AAAA,MACF,mBACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC,CAAC,CAAC;AAAA,EACnB,OAAO,SAAS,CAAC,UAAU,GAAG;AAC5B,UAAM,WAAW,QAAQ;AACzB,UAAM,aAAa,QAAQ,WAAW,QAAQ;AAC9C,QAAI,cAAc,UAAU,UAAU,GAAG;AACvC,aAAO,CAAC;AAAA,IACV;AAEA,UAAM,gBAAgB,IAAI;AAAA,OACvB,YAAY,gBAAgB,CAAC,GAAG,IAAI,CAAC,SAAS,KAAK,YAAY,CAAC;AAAA,IACnE;AAEA,QAAI;AACJ,QAAI;AACF,iBAAW,2BAAY,kBAAkB,OAAO;AAAA,IAClD,QAAQ;AACN,iBAAW;AAAA,IACb;AAEA,UAAM,aAAsB,CAAC;AAC7B,UAAM,gBAAiC,CAAC;AACxC,UAAM,kBAAuC,CAAC;AAC9C,UAAM,sBAAsB,oBAAI,IAAmB;AAEnD,aAAS,mBAAmB,MAA8B;AACxD,UAAI,aAAa,MAAM;AACrB,eAAO;AAAA,MACT;AACA,aAAO,iBAAiB,SAAS,kBAAkB,IAAI,CAAC;AAAA,IAC1D;AAUA,aAAS,iBAAiB,MAA2B,OAAwB;AAC3E,UAAI,SAAS,UAAa,aAAa,QAAQ,QAAQ,GAAG;AACxD,eAAO;AAAA,MACT;AACA,YAAM,UAAU,SAAS,QAAQ,eAAe;AAChD,UACK,6BAA0B,IAAI,KAC9B,uBAAoB,IAAI,KACxB,kBAAe,IAAI,GACtB;AACA,eAAO,iBAAiB,KAAK,YAAY,QAAQ,CAAC;AAAA,MACpD;AACA,UAAO,8BAA2B,IAAI,GAAG;AACvC,eAAO,2BAA2B,QAAQ,oBAAoB,KAAK,IAAI,CAAC;AAAA,MAC1E;AACA,UAAO,oBAAiB,IAAI,GAAG;AAC7B,eAAO,iBAAiB,KAAK,YAAY,QAAQ,CAAC;AAAA,MACpD;AACA,UAAO,gBAAa,IAAI,GAAG;AACzB,cAAM,OAAO,QAAQ,oBAAoB,IAAI,GAAG;AAChD,YAAI,SAAS,QAAW;AACtB,iBAAO;AAAA,QACT;AACA,YAAO,yBAAsB,IAAI,KAAK,KAAK,gBAAgB,QAAW;AACpE,iBAAO,iBAAiB,KAAK,aAAa,QAAQ,CAAC;AAAA,QACrD;AACA,YAAO,oBAAiB,IAAI,GAAG;AAC7B,iBAAO,iBAAiB,wBAAwB,IAAI,GAAG,QAAQ,CAAC;AAAA,QAClE;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAEA,aAAS,mBAAmB,MAA8B;AACxD,aACE,mBAAmB,IAAI,KACvB,CAAC,iBAAiB,UAAU,sBAAsB,IAAI,IAAI,GAAG,CAAC;AAAA,IAElE;AASA,aAAS,uBAAuB,IAA8C;AAC5E,YAAM,aAAa,GAAG,YAAY;AAClC,UAAI,eAAe,UAAa,aAAa,MAAM;AACjD,eAAO;AAAA,MACT;AACA,YAAM,SAAS,SAAS,sBAAsB,IAAI,UAAU;AAC5D,UAAI,CAAI,cAAW,MAAM,GAAG;AAC1B,eAAO;AAAA,MACT;AACA,YAAM,UAAU,SAAS,QAAQ,eAAe;AAChD,YAAM,WAAW,QAAQ,oBAAoB,MAAM;AACnD,YAAM,OAAO,QAAQ,eAAe,QAAQ,KAAK;AACjD,YAAM,WAAW,oBAAI,IAAY;AACjC,iBAAW,QAAQ,KAAK,QAAQ,IAAI,KAAK,QAAQ,CAAC,IAAI,GAAG;AACvD,YAAI,KAAK,gBAAgB,GAAG;AAC1B,mBAAS,IAAI,KAAK,KAAK;AAAA,QACzB;AAAA,MACF;AACA,aAAO,SAAS,QAAQ,mBAAmB,WAAW;AAAA,IACxD;AAEA,aAAS,UAAU,MAA0B;AAC3C,iBAAW,KAAK,EAAE,UAAU,oBAAI,IAAI,GAAG,IAAI,KAAK,CAAC;AAAA,IACnD;AAEA,aAAS,WAAiB;AACxB,YAAM,QAAQ,WAAW,IAAI;AAC7B,UAAI,UAAU,QAAW;AACvB;AAAA,MACF;AACA,YAAM,aAAa,CAAC,GAAG,MAAM,SAAS,OAAO,CAAC,EAAE;AAAA,QAC9C,CAAC,UAAU,MAAM,aAAa,MAAM,SAAS,QAAQ;AAAA,MACvD;AACA,UAAI,WAAW,WAAW,GAAG;AAC3B;AAAA,MACF;AACA,YAAM,iBACJ,MAAM,OAAO,OAAO,OAAO,uBAAuB,MAAM,EAAE;AAC5D,iBAAW,SAAS,YAAY;AAI9B,YACE,mBAAmB,QACnB,CAAC,GAAG,MAAM,QAAQ,EAAE,MAAM,CAAC,QAAQ,eAAe,IAAI,GAAG,CAAC,GAC1D;AACA;AAAA,QACF;AACA,sBAAc,KAAK,MAAM,IAAI;AAAA,MAC/B;AAAA,IACF;AAEA,aAAS,WACP,KACA,UACA,MACM;AACN,YAAM,QAAQ,WAAW,WAAW,SAAS,CAAC;AAC9C,UAAI,UAAU,QAAW;AACvB;AAAA,MACF;AACA,YAAM,YAAY,SAAS,MAAM,CAAC,QAAQ,YAAY,GAAG,CAAC;AAC1D,YAAM,WAAW,MAAM,SAAS,IAAI,GAAG;AACvC,UAAI,aAAa,QAAW;AAC1B,cAAM,SAAS,IAAI,KAAK;AAAA,UACtB;AAAA,UACA,UAAU,IAAI,IAAI,QAAQ;AAAA,UAC1B;AAAA,QACF,CAAC;AACD;AAAA,MACF;AACA,iBAAW,OAAO,UAAU;AAC1B,iBAAS,SAAS,IAAI,GAAG;AAAA,MAC3B;AACA,eAAS,YAAY,SAAS,aAAa;AAAA,IAC7C;AAEA,aAAS,gBACP,KACA,UACA,WACA,MACM;AACN,UAAI,qBAAqB,QAAQ,GAAG;AAClC,4BAAoB,IAAI,SAAS;AACjC;AAAA,MACF;AACA,UAAI,UAAU,SAAS,8BAAe,iBAAiB;AACrD;AAAA,MACF;AACA,YAAM,OAAO,QAAQ,GAAG;AACxB,UACE,SAAS,QACT,CAAC,iBAAiB,IAAI,KACtB,cAAc,IAAI,KAAK,YAAY,CAAC,GACpC;AACA;AAAA,MACF;AACA,sBAAgB,KAAK,EAAE,MAAM,WAAW,KAAK,CAAC;AAAA,IAChD;AAEA,WAAO;AAAA,MACL,qBAAqB;AAAA,MACrB,4BAA4B;AAAA,MAC5B,oBAAoB;AAAA,MACpB,2BAA2B;AAAA,MAC3B,yBAAyB;AAAA,MACzB,gCAAgC;AAAA,MAEhC,iBAAiB,MAAuC;AACtD,YACE,KAAK,aAAa,SAClB,KAAK,aAAa,SAClB,KAAK,aAAa,QAClB,KAAK,aAAa,MAClB;AACA;AAAA,QACF;AACA,cAAM,UAAU,OAAO,KAAK,IAAI;AAChC,cAAM,WAAW,WAAW,KAAK,KAAK;AACtC,cAAM,WAAW,OAAO,KAAK,KAAK;AAClC,cAAM,UAAU,WAAW,KAAK,IAAI;AACpC,YAAI,YAAY,QAAQ,aAAa,MAAM;AACzC,cAAI,mBAAmB,KAAK,IAAI,GAAG;AACjC,uBAAW,SAAS,CAAC,QAAQ,GAAG,IAAI;AAAA,UACtC;AAAA,QACF,WAAW,aAAa,QAAQ,YAAY,MAAM;AAChD,cAAI,mBAAmB,KAAK,KAAK,GAAG;AAClC,uBAAW,UAAU,CAAC,OAAO,GAAG,IAAI;AAAA,UACtC;AAAA,QACF;AAAA,MACF;AAAA,MAEA,gBAAgB,MAAsC;AACpD,cAAM,MAAM,OAAO,KAAK,YAAY;AACpC,YAAI,QAAQ,QAAQ,CAAC,mBAAmB,KAAK,YAAY,GAAG;AAC1D;AAAA,QACF;AACA,cAAM,WAAqB,CAAC;AAC5B,mBAAW,KAAK,KAAK,OAAO;AAC1B,cAAI,EAAE,SAAS,MAAM;AACnB,kBAAM,MAAM,WAAW,EAAE,IAAI;AAC7B,gBAAI,QAAQ,MAAM;AAChB,uBAAS,KAAK,GAAG;AAAA,YACnB;AAAA,UACF;AAAA,QACF;AACA,YAAI,SAAS,SAAS,GAAG;AACvB,qBAAW,KAAK,UAAU,IAAI;AAAA,QAChC;AAAA,MACF;AAAA,MAEA,oBAAoB,MAA0C;AAC5D;AAAA,UACE,KAAK;AAAA,UACL,KAAK,gBAAgB;AAAA,UACrB,KAAK;AAAA,UACL;AAAA,QACF;AAAA,MACF;AAAA,MAEA,mBAAmB,MAAyC;AAC1D;AAAA,UACE,KAAK;AAAA,UACL,KAAK,gBAAgB;AAAA,UACrB,KAAK;AAAA,UACL;AAAA,QACF;AAAA,MACF;AAAA,MAEA,iBAAuB;AACrB,mBAAW,eAAe,eAAe;AACvC,kBAAQ,OAAO;AAAA,YACb,MAAM;AAAA,YACN,WAAW;AAAA,YACX,MAAM,EAAE,KAAK,WAAW,WAAW,EAAE;AAAA,UACvC,CAAC;AAAA,QACH;AACA,mBAAW,QAAQ,iBAAiB;AAClC,cAAI,oBAAoB,IAAI,KAAK,SAAS,GAAG;AAC3C,oBAAQ,OAAO;AAAA,cACb,MAAM,KAAK;AAAA,cACX,WAAW;AAAA,cACX,MAAM,EAAE,MAAM,KAAK,KAAK;AAAA,YAC1B,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,aAAS,WAAW,MAA6B;AAC/C,UAAI,KAAK,SAAS,8BAAe,kBAAkB;AACjD,eAAO,OAAO,KAAK,IAAI,KAAK,OAAO,KAAK,KAAK,KAAK;AAAA,MACpD;AACA,UAAI,KAAK,SAAS,8BAAe,iBAAiB;AAChD,eAAO,OAAO,KAAK,YAAY,KAAK;AAAA,MACtC;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACF,CAAC;;;ACzhBD,IAAAC,iBAA2D;AAS3D,IAAM,oBAAoB,oBAAI,IAAI;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAOD,IAAM,8BAA8B,oBAAI,IAAI,CAAC,IAAI,CAAC;AAIlD,IAAM,oBAA8D;AAAA,EAClE,CAAC,UAAU,OAAO;AAAA,EAClB,CAAC,YAAY,SAAS;AAAA,EACtB,CAAC,SAAS,MAAM;AAClB;AAIA,IAAM,oBAAoB;AAE1B,IAAM,eAAe;AACrB,IAAM,gBAAgB;AAQtB,IAAM,WAAW,CAAC,aAChB,SAAS,MAAM,OAAO,EAAE,IAAI,KAAK;AAEnC,IAAM,SAAS,CAAC,SAAyB,KAAK,QAAQ,eAAe,EAAE;AAEvE,IAAMC,aAAY,CAAC,SAAyB;AAC1C,MAAI,aAAa;AACjB,aAAW,CAAC,SAAS,WAAW,KAAK,mBAAmB;AACtD,iBAAa,WAAW,QAAQ,SAAS,WAAW;AAAA,EACtD;AACA,SAAO,WAAW,QAAQ,mBAAmB,GAAG,EAAE,YAAY;AAChE;AAEA,IAAM,uBAAuB,CAAC,SAC5B,SAAS,SACR,KAAK,SAAS,8BAAe,2BAC5B,KAAK,SAAS,8BAAe;AAIjC,IAAM,oBAAoB,CACxB,SACkB;AAClB,MAAI,KAAK,aAAa,WAAW,EAAG,QAAO;AAC3C,QAAM,CAAC,UAAU,IAAI,KAAK;AAC1B,MAAI,eAAe,OAAW,QAAO;AACrC,MAAI,WAAW,GAAG,SAAS,8BAAe,WAAY,QAAO;AAC7D,MAAI,CAAC,qBAAqB,WAAW,IAAI,EAAG,QAAO;AACnD,SAAO,WAAW,GAAG;AACvB;AAEA,IAAM,mBAAmB,CAAC,SAA8D;AACtF,MAAI,QAAQ;AACZ,MAAI,cAAc;AAClB,MAAI,YAA0D;AAE9D,QAAM,eAAe,CAAC,MAAc,SAA8B;AAChE,aAAS;AACT,gBAAY,EAAE,MAAM,KAAK;AAAA,EAC3B;AAEA,aAAW,aAAa,MAAM;AAC5B,YAAQ,UAAU,MAAM;AAAA,MACtB,KAAK,8BAAe;AAClB,sBAAc;AACd;AAAA,MACF,KAAK,8BAAe,0BAA0B;AAC5C,iBAAS;AACT,cAAM,OAAO,UAAU;AACvB,YACE,KAAK,SAAS,8BAAe,uBAC7B,KAAK,OAAO,MACZ;AACA,sBAAY,EAAE,MAAM,KAAK,GAAG,MAAM,MAAM,UAAU;AAAA,QACpD,WACE,KAAK,SAAS,8BAAe,oBAC7B,KAAK,OAAO,MACZ;AACA,sBAAY,EAAE,MAAM,KAAK,GAAG,MAAM,MAAM,UAAU;AAAA,QACpD;AACA;AAAA,MACF;AAAA,MACA,KAAK,8BAAe,wBAAwB;AAC1C,YAAI,UAAU,WAAW,MAAM;AAC7B,wBAAc;AACd;AAAA,QACF;AACA,cAAM,OAAO,UAAU;AACvB,YAAI,SAAS,MAAM;AACjB,mBAAS,UAAU,WAAW;AAC9B;AAAA,QACF;AACA,gBAAQ,KAAK,MAAM;AAAA,UACjB,KAAK,8BAAe;AAClB,gBAAI,KAAK,OAAO,KAAM,cAAa,KAAK,GAAG,MAAM,SAAS;AAAA,gBACrD,UAAS;AACd;AAAA,UACF,KAAK,8BAAe;AAClB,gBAAI,KAAK,OAAO,KAAM,cAAa,KAAK,GAAG,MAAM,SAAS;AAAA,gBACrD,UAAS;AACd;AAAA,UACF,KAAK,8BAAe,qBAAqB;AACvC,kBAAM,SAAS,kBAAkB,IAAI;AACrC,gBAAI,WAAW,QAAQ,KAAK,aAAa,WAAW,GAAG;AACrD,2BAAa,QAAQ,SAAS;AAAA,YAChC,OAAO;AACL,uBAAS,KAAK,aAAa;AAAA,YAC7B;AACA;AAAA,UACF;AAAA,UACA;AACE,qBAAS;AAAA,QACb;AACA;AAAA,MACF;AAAA,MACA;AACE;AAAA,IACJ;AAAA,EACF;AAEA,SAAO,EAAE,OAAO,aAAa,UAAU;AACzC;AAEA,IAAO,+BAAQ,2BAAY;AAAA,EACzB,CAAC,SACC,gFAAgF,IAAI;AACxF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,UAAU;AAAA,MACR,kBACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC;AAAA,EACjB,OAAO,SAAS;AACd,UAAM,OAAO,SAAS,QAAQ,QAAQ;AAEtC,QAAI,KAAK,SAAS,OAAO,EAAG,QAAO,CAAC;AACpC,QAAI,aAAa,KAAK,IAAI,EAAG,QAAO,CAAC;AAIrC,QAAI,WAAW,QAAQ,QAAQ,EAAG,QAAO,CAAC;AAE1C,UAAMC,QAAO,OAAO,IAAI;AACxB,QAAI,CAAC,kBAAkB,IAAIA,MAAK,YAAY,CAAC,EAAG,QAAO,CAAC;AAExD,WAAO;AAAA,MACL,QAAQ,MAA8B;AACpC,cAAM,EAAE,OAAO,aAAa,UAAU,IAAI,iBAAiB,KAAK,IAAI;AACpE,YAAI,YAAa;AACjB,YAAI,UAAU,KAAK,cAAc,KAAM;AACvC,YAAI,4BAA4B,IAAI,UAAU,IAAI,EAAG;AAErD,cAAM,WAAWD,WAAU,UAAU,IAAI;AACzC,YAAIC,UAAS,SAAU;AAEvB,gBAAQ,OAAO;AAAA,UACb,MAAM,UAAU;AAAA,UAChB,WAAW;AAAA,UACX,MAAM,EAAE,MAAAA,OAAM,MAAM,UAAU,MAAM,SAAS;AAAA,QAC/C,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;ACxLD,IAAAC,iBAA2C;;;AC1B3C,IAAAC,iBAA6D;AAYtD,SAAS,cAAc,MAAsB;AAClD,QAAM,MAAM,CAAC,GAAG,IAAI;AACpB,QAAM,IAAI,KAAK;AACf,MAAI,IAAI;AACR,SAAO,IAAI,GAAG;AACZ,UAAM,KAAK,KAAK,CAAC;AACjB,QAAI,OAAO,OAAO,OAAO,KAAK;AAC5B,UAAI,CAAC,IAAI;AACT,WAAK;AACL,aAAO,IAAI,GAAG;AACZ,cAAM,IAAI,KAAK,CAAC;AAChB,YAAI,MAAM,IAAI;AACZ,cAAI,IAAI,IAAI,KAAK,KAAK,IAAI,CAAC,MAAM,IAAI;AACnC,gBAAI,CAAC,IAAI;AACT,gBAAI,IAAI,CAAC,IAAI;AACb,iBAAK;AACL;AAAA,UACF;AACA,cAAI,CAAC,IAAI;AACT,eAAK;AACL;AAAA,QACF;AACA,YAAI,MAAM,MAAM;AACd,cAAI,CAAC,IAAI;AAAA,QACX;AACA,aAAK;AAAA,MACP;AACA;AAAA,IACF;AACA,QAAI,OAAO,OAAO,KAAK,IAAI,CAAC,MAAM,KAAK;AACrC,aAAO,IAAI,KAAK,KAAK,CAAC,MAAM,MAAM;AAChC,YAAI,CAAC,IAAI;AACT,aAAK;AAAA,MACP;AACA;AAAA,IACF;AACA,QAAI,OAAO,OAAO,KAAK,IAAI,CAAC,MAAM,KAAK;AACrC,UAAI,CAAC,IAAI;AACT,UAAI,IAAI,CAAC,IAAI;AACb,WAAK;AACL,aAAO,IAAI,KAAK,EAAE,KAAK,CAAC,MAAM,OAAO,KAAK,IAAI,CAAC,MAAM,MAAM;AACzD,YAAI,KAAK,CAAC,MAAM,MAAM;AACpB,cAAI,CAAC,IAAI;AAAA,QACX;AACA,aAAK;AAAA,MACP;AACA,UAAI,IAAI,GAAG;AACT,YAAI,CAAC,IAAI;AACT,YAAI,IAAI,CAAC,IAAI;AACb,aAAK;AAAA,MACP;AACA;AAAA,IACF;AACA,SAAK;AAAA,EACP;AACA,SAAO,IAAI,KAAK,EAAE;AACpB;AAGA,IAAM,sBAAsB;AAQrB,SAAS,UAAU,MAAoC;AAC5D,UAAQ,KAAK,MAAM;AAAA,IACjB,KAAK,8BAAe;AAClB,aAAO,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ;AAAA,IACvD,KAAK,8BAAe;AAClB,aAAO,KAAK,OAAO,IAAI,CAAC,MAAM,EAAE,MAAM,UAAU,EAAE,MAAM,GAAG,EAAE,KAAK,mBAAmB;AAAA,IACvF,KAAK,8BAAe;AAClB,aAAO,UAAU,KAAK,KAAK;AAAA,IAC7B,KAAK,8BAAe,kBAAkB;AACpC,UAAI,KAAK,aAAa,KAAK;AACzB,eAAO;AAAA,MACT;AACA,YAAM,OAAO,UAAU,KAAK,IAAI;AAChC,YAAM,QAAQ,UAAU,KAAK,KAAK;AAClC,aAAO,SAAS,QAAQ,UAAU,OAAO,OAAO,QAAQ;AAAA,IAC1D;AAAA,IACA,KAAK,8BAAe,iBAAiB;AACnC,YAAM,QAAkB,CAAC;AACzB,iBAAW,WAAW,KAAK,UAAU;AACnC,YAAI,YAAY,MAAM;AACpB,iBAAO;AAAA,QACT;AACA,cAAM,OAAO,UAAU,OAAO;AAC9B,YAAI,SAAS,MAAM;AACjB,iBAAO;AAAA,QACT;AACA,cAAM,KAAK,IAAI;AAAA,MACjB;AACA,aAAO,MAAM,SAAS,IAAI,MAAM,KAAK,GAAG,IAAI;AAAA,IAC9C;AAAA,IACA;AACE,aAAO;AAAA,EACX;AACF;AAQA,SAAS,sBAAsB,MAAyC;AACtE,QAAM,SAAS,KAAK;AACpB,SACE,QAAQ,SAAS,8BAAe,oBAChC,OAAO,WAAW,QAClB,CAAC,OAAO,YACR,OAAO,SAAS,SAAS,8BAAe,cACxC,OAAO,SAAS,SAAS,UACzB,OAAO,QAAQ,SAAS,8BAAe;AAE3C;AAGA,SAAS,aAAa,MAAqB,UAAwC;AACjF,WAAS,IAAI,IAAI;AACjB,aAAW,OAAO,OAAO,KAAK,IAAI,GAAG;AACnC,QAAI,QAAQ,UAAU;AACpB;AAAA,IACF;AACA,UAAM,QAAS,KAA4C,GAAG;AAC9D,eAAW,SAAS,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK,GAAG;AAC1D,UAAI,UAAU,QAAQ,OAAO,UAAU,YAAY,UAAU,OAAO;AAClE,qBAAa,OAAwB,QAAQ;AAAA,MAC/C;AAAA,IACF;AAAA,EACF;AACF;AAUO,SAAS,kBACd,SACuB;AACvB,QAAM,WAAW,oBAAI,QAAuB;AAE5C,QAAM,QAAQ,CAAC,SAA8B;AAC3C,QAAI,SAAS,IAAI,IAAI,GAAG;AACtB;AAAA,IACF;AACA,UAAM,OAAO,UAAU,IAAI;AAC3B,QAAI,SAAS,MAAM;AACjB;AAAA,IACF;AACA,iBAAa,MAAM,QAAQ;AAC3B,YAAQ,cAAc,IAAI,GAAG,IAAI;AAAA,EACnC;AAEA,SAAO;AAAA,IACL,kBAAkB,CAAC,SAA0C;AAC3D,YAAM,IAAI;AAAA,IACZ;AAAA,IACA,iBAAiB,CAAC,SAAyC;AACzD,UAAI,sBAAsB,IAAI,GAAG;AAC/B,cAAM,IAAI;AAAA,MACZ;AAAA,IACF;AAAA,IACA,iBAAiB,CAAC,SAAyC;AACzD,YAAM,IAAI;AAAA,IACZ;AAAA,IACA,SAAS,CAAC,SAAiC;AACzC,YAAM,IAAI;AAAA,IACZ;AAAA,EACF;AACF;;;ADtJA,IAAM,oBAAoB;AAG1B,IAAM,cAAc;AAEpB,IAAO,+BAAQ,2BAAY;AAAA,EACzB,CAAC,SACC,gFAAgF,IAAI;AACxF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,UAAU;AAAA,MACR,oBACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC;AAAA,EACjB,OAAO,SAAS;AACd,QAAI,WAAW,QAAQ,QAAQ,KAAK,CAAC,YAAY,KAAK,QAAQ,WAAW,IAAI,GAAG;AAC9E,aAAO,CAAC;AAAA,IACV;AACA,WAAO,kBAAkB,CAAC,KAAa,SAA8B;AACnE,UAAI,CAAC,kBAAkB,KAAK,GAAG,GAAG;AAChC;AAAA,MACF;AACA,cAAQ,OAAO,EAAE,MAAM,WAAW,qBAAqB,CAAC;AAAA,IAC1D,CAAC;AAAA,EACH;AACF,CAAC;;;AE7CD,IAAAC,iBAA2D;AAK3D,IAAM,eAAe;AAGrB,IAAM,uBAAuB;AAG7B,IAAM,kBAAuC,oBAAI,IAAI,CAAC,WAAW,eAAe,SAAS,CAAC;AAG1F,SAAS,gBAAgB,MAAsD;AAC7E,MAAI,KAAK,SAAS,8BAAe,aAAa;AAC5C,WAAO;AAAA,EACT;AACA,MACE,KAAK,SAAS,8BAAe,mBAC7B,KAAK,SAAS,SAAS,8BAAe,cACtC,gBAAgB,IAAI,KAAK,SAAS,IAAI,GACtC;AACA,UAAM,WAAW,KAAK,eAAe,OAAO,CAAC;AAC7C,WAAO,aAAa,SAAY,OAAO,gBAAgB,QAAQ;AAAA,EACjE;AACA,SAAO;AACT;AAGA,SAAS,eAAe,YAA0D,MAA6B;AAC7G,SAAO,WAAW,QAAQ,IAAI,EAAE,WAAW,QAAQ,GAAG,EAAE,KAAK;AAC/D;AAMA,SAAS,iBACP,OACA,YACS;AACT,QAAM,WAAW,MAAM;AACvB,MAAI,SAAS,SAAS,cAAc;AAClC,WAAO;AAAA,EACT;AACA,MAAI,SAAS,KAAK,CAAC,YAAY,QAAQ,SAAS,8BAAe,UAAU,GAAG;AAC1E,WAAO;AAAA,EACT;AACA,MAAI,SAAS,KAAK,CAAC,YAAY,QAAQ,SAAS,8BAAe,kBAAkB,GAAG;AAClF,WAAO;AAAA,EACT;AACA,MAAI,SAAS,CAAC,GAAG,SAAS,8BAAe,eAAe;AACtD,WAAO;AAAA,EACT;AACA,MACE,SAAS,WAAW,wBACpB,SAAS,KAAK,CAAC,YAAY,QAAQ,SAAS,8BAAe,cAAc,GACzE;AACA,WAAO;AAAA,EACT;AACA,QAAM,QAAQ,IAAI,IAAI,SAAS,IAAI,CAAC,YAAY,eAAe,YAAY,OAAO,CAAC,CAAC;AACpF,SAAO,MAAM,SAAS;AACxB;AAGA,SAAS,aAAa,MAAoC;AACxD,MAAI,KAAK,SAAS,8BAAe,qBAAqB;AACpD,WAAO,KAAK,IAAI,QAAQ;AAAA,EAC1B;AACA,QAAM,SAAS,KAAK;AACpB,MAAI,QAAQ,SAAS,8BAAe,sBAAsB,OAAO,GAAG,SAAS,8BAAe,YAAY;AACtG,WAAO,OAAO,GAAG;AAAA,EACnB;AACA,OACG,QAAQ,SAAS,8BAAe,oBAC/B,QAAQ,SAAS,8BAAe,sBAChC,QAAQ,SAAS,8BAAe,aAClC,OAAO,IAAI,SAAS,8BAAe,YACnC;AACA,WAAO,OAAO,IAAI;AAAA,EACpB;AACA,SAAO;AACT;AAMA,SAAS,iBAAiB,MAA8B;AACtD,WAAS,UAA4C,MAAM,WAAW,MAAM,UAAU,QAAQ,QAAQ;AACpG,UAAM,SAAS,QAAQ;AACvB,QACE,QAAQ,SAAS,8BAAe,0BAChC,QAAQ,SAAS,8BAAe,0BAChC;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAQA,SAAS,uBAAuB,MAAoC;AAClE,MAAI,UAAyB;AAC7B,MAAI,QAAuB;AAC3B,SAAO,QAAQ,UAAU,QAAQ,QAAQ,OAAO,SAAS,8BAAe,SAAS;AAC/E,YAAQ;AACR,cAAU,QAAQ;AAAA,EACpB;AACA,MAAI,QAAQ,QAAQ,SAAS,8BAAe,SAAS;AACnD,WAAO;AAAA,EACT;AACA,MACE,QAAQ,SAAS,8BAAe,uBAChC,QAAQ,SAAS,8BAAe,kBAChC;AACA,WAAO,QAAQ,IAAI,QAAQ;AAAA,EAC7B;AACA,MAAI,QAAQ,SAAS,8BAAe,qBAAqB;AACvD,QAAI,MAAM,SAAS,8BAAe,sBAAsB,MAAM,GAAG,SAAS,8BAAe,YAAY;AACnG,aAAO;AAAA,IACT;AACA,WAAO,MAAM,GAAG;AAAA,EAClB;AACA,SAAO;AACT;AASA,SAAS,uBAAuB,SAAgD;AAC9E,QAAM,QAAQ,oBAAI,IAAY;AAC9B,aAAW,aAAa,QAAQ,MAAM;AACpC,QACE,UAAU,SAAS,8BAAe,0BAClC,UAAU,eAAe,QACzB,UAAU,UAAU,QACpB,UAAU,eAAe,QACzB;AACA,iBAAW,aAAa,UAAU,YAAY;AAC5C,YAAI,UAAU,eAAe,UAAU,UAAU,MAAM,SAAS,8BAAe,YAAY;AACzF,gBAAM,IAAI,UAAU,MAAM,IAAI;AAAA,QAChC;AAAA,MACF;AACA;AAAA,IACF;AACA,QACE,UAAU,SAAS,8BAAe,4BAClC,UAAU,YAAY,SAAS,8BAAe,YAC9C;AACA,YAAM,IAAI,UAAU,YAAY,IAAI;AACpC;AAAA,IACF;AACA,QACE,UAAU,SAAS,8BAAe,sBAClC,UAAU,WAAW,SAAS,8BAAe,YAC7C;AACA,YAAM,IAAI,UAAU,WAAW,IAAI;AAAA,IACrC;AAAA,EACF;AACA,SAAO;AACT;AAOA,SAAS,WAAW,MAAqB,kBAAgD;AACvF,MAAI,iBAAiB,IAAI,GAAG;AAC1B,WAAO;AAAA,EACT;AACA,MAAI,iBAAiB,SAAS,GAAG;AAC/B,WAAO;AAAA,EACT;AACA,QAAM,UAAU,uBAAuB,IAAI;AAC3C,SAAO,YAAY,QAAQ,iBAAiB,IAAI,OAAO;AACzD;AAEA,IAAO,qCAAQ,2BAAY;AAAA,EACzB,CAAC,SACC,gFAAgF,IAAI;AACxF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,UAAU;AAAA,MACR,yBACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC;AAAA,EACjB,OAAO,SAAS;AACd,UAAM,mBAAmB,uBAAuB,QAAQ,WAAW,GAAG;AACtE,UAAM,QAAQ,CACZ,SAIS;AACT,YAAM,aAAa,KAAK,YAAY;AACpC,UAAI,eAAe,QAAW;AAC5B;AAAA,MACF;AACA,YAAM,QAAQ,gBAAgB,UAAU;AACxC,UAAI,UAAU,QAAQ,iBAAiB,OAAO,QAAQ,UAAU,GAAG;AACjE;AAAA,MACF;AACA,YAAM,OAAO,aAAa,IAAI;AAC9B,UAAI,SAAS,QAAQ,KAAK,WAAW,GAAG,KAAK,YAAY,KAAK,IAAI,GAAG;AACnE;AAAA,MACF;AACA,UAAI,CAAC,WAAW,MAAM,gBAAgB,GAAG;AACvC;AAAA,MACF;AACA,cAAQ,OAAO;AAAA,QACb,MAAM;AAAA,QACN,WAAW;AAAA,QACX,MAAM,EAAE,MAAM,OAAO,OAAO,MAAM,aAAa,MAAM,EAAE;AAAA,MACzD,CAAC;AAAA,IACH;AACA,WAAO;AAAA,MACL,qBAAqB;AAAA,MACrB,oBAAoB;AAAA,MACpB,yBAAyB;AAAA,IAC3B;AAAA,EACF;AACF,CAAC;;;AClND,IAAAC,iBAA2D;AAO3D,IAAM,aAAa;AACnB,IAAM,sBAAsB;AAC5B,IAAM,iBAAiB;AAGvB,IAAM,iBACJ;AACF,IAAM,gBAAgB;AAEtB,IAAM,iBAA8C,oBAAI,IAAI;AAAA,EAC1D,8BAAe;AAAA,EACf,8BAAe;AAAA,EACf,8BAAe;AACjB,CAAC;AAGD,SAAS,aAAa,OAAwB;AAC5C,SAAO,MAAM,SAAS,IAAI,KAAK,eAAe,KAAK,KAAK,KAAK,cAAc,KAAK,KAAK;AACvF;AAEA,SAAS,QAAQ,OAAuB;AACtC,QAAM,UAAU,MAAM,WAAW,MAAM,GAAG,EAAE,KAAK;AACjD,SAAO,QAAQ,UAAU,iBAAiB,UAAU,GAAG,QAAQ,MAAM,GAAG,cAAc,CAAC;AACzF;AAGA,SAAS,kBAAkB,MAA2C;AACpE,WAAS,UAAU,KAAK,QAAQ,WAAW,MAAM,UAAU,QAAQ,QAAQ;AACzE,QAAI,eAAe,IAAI,QAAQ,IAAI,GAAG;AACpC,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAOA,SAAS,cAAc,MAA8B;AACnD,QAAM,SAAS,KAAK;AACpB,MAAI,WAAW,QAAW;AACxB,WAAO;AAAA,EACT;AACA,SACE,OAAO,SAAS,8BAAe,qBAC/B,OAAO,SAAS,8BAAe,0BAC/B,OAAO,SAAS,8BAAe,wBAC/B,OAAO,SAAS,8BAAe,gBAC/B,OAAO,SAAS,8BAAe,gBAC/B,OAAO,SAAS,8BAAe;AAEnC;AAEA,IAAO,qCAAQ,2BAAY;AAAA,EACzB,CAAC,SACC,gFAAgF,IAAI;AACxF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,UAAU;AAAA,MACR,yBACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC;AAAA,EACjB,OAAO,SAAS;AACd,QAAI,WAAW,QAAQ,QAAQ,GAAG;AAChC,aAAO,CAAC;AAAA,IACV;AACA,UAAM,cAAc,oBAAI,IAA6B;AACrD,UAAM,SAAS,oBAAI,QAA6C;AAEhE,UAAM,SAAS,CAAC,OAAe,SAA8B;AAC3D,UAAI,MAAM,SAAS,cAAc,CAAC,aAAa,KAAK,KAAK,cAAc,IAAI,GAAG;AAC5E;AAAA,MACF;AACA,YAAM,WAAW,YAAY,IAAI,KAAK;AACtC,UAAI,aAAa,QAAW;AAC1B,oBAAY,IAAI,OAAO,CAAC,IAAI,CAAC;AAAA,MAC/B,OAAO;AACL,iBAAS,KAAK,IAAI;AAAA,MACpB;AACA,aAAO,IAAI,MAAM,kBAAkB,IAAI,CAAC;AAAA,IAC1C;AAEA,WAAO;AAAA,MACL,QAAQ,MAA8B;AACpC,YAAI,OAAO,KAAK,UAAU,UAAU;AAClC,iBAAO,KAAK,OAAO,IAAI;AAAA,QACzB;AAAA,MACF;AAAA,MACA,gBAAgB,MAAsC;AAGpD,YAAI,KAAK,OAAO,SAAS,8BAAe,0BAA0B;AAChE;AAAA,QACF;AACA,cAAM,CAAC,IAAI,IAAI,KAAK;AACpB,YAAI,KAAK,YAAY,WAAW,KAAK,SAAS,QAAW;AACvD,iBAAO,KAAK,MAAM,UAAU,KAAK,MAAM,KAAK,IAAI;AAAA,QAClD;AAAA,MACF;AAAA,MACA,gBAAgB,MAAY;AAC1B,mBAAW,CAAC,OAAO,KAAK,KAAK,aAAa;AACxC,gBAAM,iBAAiB,IAAI;AAAA,YACzB,MAAM,IAAI,CAAC,SAAS,OAAO,IAAI,IAAI,CAAC,EAAE,OAAO,CAAC,UAAU,SAAS,IAAI;AAAA,UACvE;AACA,cAAI,eAAe,OAAO,qBAAqB;AAC7C;AAAA,UACF;AACA,gBAAM,CAAC,OAAO,GAAG,OAAO,IAAI;AAC5B,cAAI,UAAU,QAAW;AACvB;AAAA,UACF;AACA,qBAAW,QAAQ,SAAS;AAC1B,oBAAQ,OAAO;AAAA,cACb;AAAA,cACA,WAAW;AAAA,cACX,MAAM,EAAE,SAAS,QAAQ,KAAK,GAAG,MAAM,OAAO,MAAM,IAAI,MAAM,IAAI,EAAE;AAAA,YACtE,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;ACpMD,IAAAC,iBAA2C;AAS3C,IAAM,cAAc;AACpB,IAAM,iBAAiB;AACvB,IAAM,eAAe;AACrB,IAAM,gBAAgB;AAEtB,IAAM,mBAAmB;AAGzB,IAAM,cAAc;AASpB,SAAS,iBAAiB,KAAa,KAAsB;AAC3D,MAAI,iBAAiB,KAAK,IAAI,MAAM,GAAG,GAAG,CAAC,GAAG;AAC5C,WAAO;AAAA,EACT;AACA,MAAI,SAAS,MAAM;AACnB,SAAO,UAAU,KAAK,KAAK,KAAK,IAAI,MAAM,KAAK,EAAE,GAAG;AAClD,cAAU;AAAA,EACZ;AACA,MAAI,QAAQ,MAAM;AAClB,SAAO,QAAQ,IAAI,UAAU,KAAK,KAAK,IAAI,KAAK,KAAK,EAAE,GAAG;AACxD,aAAS;AAAA,EACX;AACA,QAAM,aAAa,UAAU,IAAK,IAAI,MAAM,KAAK,KAAM;AACvD,QAAM,YAAY,QAAQ,IAAI,SAAU,IAAI,KAAK,KAAK,KAAM;AAC5D,QAAM,aACJ,cAAc,MAAM,cAAc,OAAO,cAAc,OAAO,aAAa,KAAK,IAAI,MAAM,KAAK,CAAC;AAClG,MAAI,CAAC,YAAY;AACf,WAAO;AAAA,EACT;AACA,SAAO,EAAE,eAAe,OAAO,cAAc;AAC/C;AAGA,SAAS,kBAAkB,KAAsB;AAC/C,QAAM,UAAU,CAAC,GAAG,IAAI,SAAS,cAAc,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK;AACpE,WAAS,MAAM,GAAG,MAAM,IAAI,QAAQ,OAAO;AACzC,QAAI,IAAI,GAAG,MAAM,OAAO,CAAC,iBAAiB,KAAK,GAAG,GAAG;AACnD;AAAA,IACF;AACA,UAAM,SAAS,QAAQ,OAAO,CAAC,UAAU,QAAQ,GAAG,EAAE,GAAG,EAAE;AAC3D,QAAI,WAAW,UAAa,CAAC,cAAc,KAAK,IAAI,MAAM,GAAG,MAAM,CAAC,GAAG;AACrE,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAEA,IAAO,yBAAQ,2BAAY;AAAA,EACzB,CAAC,SACC,gFAAgF,IAAI;AACxF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,UAAU;AAAA,MACR,cACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC;AAAA,EACjB,OAAO,SAAS;AACd,QAAI,WAAW,QAAQ,QAAQ,KAAK,CAAC,YAAY,KAAK,QAAQ,WAAW,IAAI,GAAG;AAC9E,aAAO,CAAC;AAAA,IACV;AACA,WAAO,kBAAkB,CAAC,KAAa,SAA8B;AACnE,UAAI,CAAC,YAAY,KAAK,GAAG,KAAK,CAAC,kBAAkB,GAAG,GAAG;AACrD;AAAA,MACF;AACA,cAAQ,OAAO,EAAE,MAAM,WAAW,eAAe,CAAC;AAAA,IACpD,CAAC;AAAA,EACH;AACF,CAAC;;;AClFD,IAAAC,iBAA2D;AAQ3D,IAAM,gBAAqC,oBAAI,IAAI,CAAC,SAAS,SAAS,QAAQ,OAAO,CAAC;AAGtF,IAAM,eAAoC,oBAAI,IAAI;AAAA,EAChD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,IAAMC,kBAA8C,oBAAI,IAAI;AAAA,EAC1D,8BAAe;AAAA,EACf,8BAAe;AAAA,EACf,8BAAe;AACjB,CAAC;AAGD,SAAS,wBAAwB,MAA0C;AACzE,SAAO,MAAM,SAAS,8BAAe,WAAW,OAAO,KAAK,UAAU,YAAY,KAAK,UAAU;AACnG;AAGA,SAAS,kBAAkB,MAA8B;AACvD,SACE,KAAK,SAAS,8BAAe,kBAC7B,KAAK,OAAO,SAAS,8BAAe,cACpC,KAAK,OAAO,SAAS,gBACrB,KAAK,UAAU,UAAU,KACzB,wBAAwB,KAAK,UAAU,CAAC,CAAC;AAE7C;AAMA,SAAS,eAAe,MAAuC;AAC7D,MAAI,KAAK,OAAO,SAAS,8BAAe,cAAc,KAAK,OAAO,SAAS,WAAW;AACpF,WAAO;AAAA,EACT;AACA,QAAM,WAAW,KAAK,UAAU,CAAC;AACjC,MACE,UAAU,SAAS,8BAAe,2BAClC,UAAU,SAAS,8BAAe,oBAClC;AACA,WAAO;AAAA,EACT;AACA,QAAM,OAAO,SAAS;AACtB,MAAI,KAAK,SAAS,8BAAe,gBAAgB;AAC/C,WAAO,kBAAkB,IAAI;AAAA,EAC/B;AACA,SAAO,KAAK,KAAK;AAAA,IACf,CAAC,SAAS,KAAK,SAAS,8BAAe,uBAAuB,kBAAkB,KAAK,UAAU;AAAA,EACjG;AACF;AAGA,SAAS,cAAc,MAAwC;AAC7D,SACE,KAAK,OAAO,SAAS,8BAAe,cACpC,cAAc,IAAI,KAAK,OAAO,IAAI,KAClC,KAAK,UAAU,UAAU,KACzB,wBAAwB,KAAK,UAAU,CAAC,CAAC;AAE7C;AAGA,SAAS,yBAAyB,MAA2C;AAC3E,WAAS,UAAU,KAAK,QAAQ,WAAW,MAAM,UAAU,QAAQ,QAAQ;AACzE,QAAI,CAACA,gBAAe,IAAI,QAAQ,IAAI,GAAG;AACrC;AAAA,IACF;AACA,UAAM,cAAc,QAAQ;AAC5B,UAAM,oBACJ,aAAa,SAAS,8BAAe,iBAAiB,eAAe,WAAW;AAClF,QAAI,CAAC,mBAAmB;AACtB,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAGA,SAAS,eAAe,QAAsC;AAC5D,MAAI,OAAO,SAAS,8BAAe,YAAY;AAC7C,WAAO,OAAO;AAAA,EAChB;AACA,MAAI,OAAO,SAAS,8BAAe,kBAAkB;AACnD,WAAO,eAAe,OAAO,MAAM;AAAA,EACrC;AACA,MAAI,OAAO,SAAS,8BAAe,gBAAgB;AACjD,WAAO,eAAe,OAAO,MAAM;AAAA,EACrC;AACA,MAAI,OAAO,SAAS,8BAAe,0BAA0B;AAC3D,WAAO,eAAe,OAAO,GAAG;AAAA,EAClC;AACA,SAAO;AACT;AAGA,SAAS,WAAW,IAA4B;AAC9C,QAAM,OAAO,GAAG;AAChB,MACE,MAAM,SAAS,8BAAe,kBAC9B,CAAC,KAAK,UAAU,KAAK,CAAC,aAAa,aAAa,EAAE,GAClD;AACA,WAAO;AAAA,EACT;AACA,QAAM,OAAO,eAAe,KAAK,MAAM;AACvC,SAAO,SAAS,QAAQ,aAAa,IAAI,IAAI;AAC/C;AAEA,IAAO,gCAAQ,2BAAY;AAAA,EACzB,CAAC,SACC,gFAAgF,IAAI;AACxF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,UAAU;AAAA,MACR,mBACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC;AAAA,EACjB,OAAO,SAAS;AACd,QAAI,CAAC,WAAW,QAAQ,QAAQ,GAAG;AACjC,aAAO,CAAC;AAAA,IACV;AACA,UAAM,SAAS,CAAC,SAA8B;AAC5C,YAAM,YAAY,yBAAyB,IAAI;AAC/C,UAAI,cAAc,QAAQ,CAAC,WAAW,SAAS,GAAG;AAChD;AAAA,MACF;AACA,cAAQ,OAAO,EAAE,MAAM,WAAW,oBAAoB,CAAC;AAAA,IACzD;AACA,WAAO;AAAA,MACL,cAAc,MAAoC;AAChD,YAAI,eAAe,IAAI,GAAG;AACxB,iBAAO,IAAI;AAAA,QACb;AAAA,MACF;AAAA,MACA,eAAe,MAAqC;AAClD,YAAI,cAAc,IAAI,GAAG;AACvB,iBAAO,IAAI;AAAA,QACb;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;AC5JD,IAAAC,iBAA2D;AAQ3D,IAAM,qBAA0C,oBAAI,IAAI,CAAC,OAAO,OAAO,MAAM,IAAI,CAAC;AAQlF,IAAM,uBAA4C,oBAAI,IAAI,CAAC,aAAa,KAAK,CAAC;AAQ9E,IAAM,iBAAiB;AAevB,IAAM,qBACJ;AACF,IAAM,mBAAmB;AAczB,SAAS,oBAAoB,YAA6B;AACxD,MAAI,iBAAiB,KAAK,UAAU,EAAG,QAAO;AAC9C,MAAI,iBAAiB,UAAU,KAAK,CAAC,eAAe,KAAK,UAAU,EAAG,QAAO;AAC7E,SAAO,eAAe,WAAW,YAAY,KAAK,WAAW,KAAK,UAAU;AAC9E;AAOA,SAAS,kBAAkB,MAA8B;AACvD,UAAQ,KAAK,MAAM;AAAA,IACjB,KAAK,8BAAe;AAClB,aAAO;AAAA,IACT,KAAK,8BAAe;AAClB,aAAO,KAAK,YAAY,WAAW;AAAA,IACrC,KAAK,8BAAe;AAClB,aACE,qBAAqB,IAAI,KAAK,IAAI,KAClC,mBAAmB,KAAK,KAAK,IAAI,KACjC,oBAAoB,KAAK,IAAI;AAAA,IAEjC,KAAK,8BAAe;AAClB,aACE,CAAC,KAAK,YACN,KAAK,SAAS,SAAS,8BAAe,eACrC,mBAAmB,KAAK,KAAK,SAAS,IAAI,KACzC,oBAAoB,KAAK,SAAS,IAAI;AAAA,IAE5C;AACE,aAAO;AAAA,EACX;AACF;AAGA,SAAS,YAAY,MAAoC;AACvD,MAAI,KAAK,SAAS,8BAAe,YAAY;AAC3C,WAAO,KAAK;AAAA,EACd;AACA,MACE,KAAK,SAAS,8BAAe,oBAC7B,CAAC,KAAK,YACN,KAAK,SAAS,SAAS,8BAAe,YACtC;AACA,WAAO,KAAK,SAAS;AAAA,EACvB;AACA,SAAO;AACT;AAUA,SAAS,gBAAgB,MAA8B;AACrD,MAAI,KAAK,SAAS,8BAAe,iBAAiB;AAChD,WAAO,KAAK,YAAY,KAAK,CAAC,eAAe,gBAAgB,UAAU,CAAC;AAAA,EAC1E;AACA,QAAM,OAAO,YAAY,IAAI;AAC7B,SAAO,SAAS,QAAQ,iBAAiB,IAAI;AAC/C;AAGA,SAAS,aAAa,MAAoC;AACxD,MAAI,KAAK,SAAS,8BAAe,iBAAiB;AAChD,eAAW,cAAc,KAAK,aAAa;AACzC,YAAM,SAAS,aAAa,UAAU;AACtC,UAAI,WAAW,MAAM;AACnB,eAAO;AAAA,MACT;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACA,SAAO,YAAY,IAAI;AACzB;AAEA,IAAO,8CAAQ,2BAAY;AAAA,EACzB,CAAC,SACC,gFAAgF,IAAI;AACxF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,UAAU;AAAA,MACR,iCACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC;AAAA,EACjB,OAAO,SAAS;AACd,QAAI,WAAW,QAAQ,QAAQ,GAAG;AAChC,aAAO,CAAC;AAAA,IACV;AACA,WAAO;AAAA,MACL,iBAAiB,MAAuC;AACtD,YAAI,CAAC,mBAAmB,IAAI,KAAK,QAAQ,GAAG;AAC1C;AAAA,QACF;AACA,cAAM,EAAE,MAAM,MAAM,IAAI;AACxB,YAAI,kBAAkB,IAAI,KAAK,kBAAkB,KAAK,GAAG;AACvD;AAAA,QACF;AACA,cAAM,SAAS,CAAC,MAAM,KAAK,EAAE,KAAK,CAAC,YAAY,gBAAgB,OAAO,CAAC;AACvE,YAAI,WAAW,QAAW;AACxB;AAAA,QACF;AACA,gBAAQ,OAAO;AAAA,UACb;AAAA,UACA,WAAW;AAAA,UACX,MAAM,EAAE,UAAU,KAAK,UAAU,MAAM,aAAa,MAAM,KAAK,GAAG;AAAA,QACpE,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;ACvLD,IAAAC,iBAA2C;AAiB3C,IAAM,eACJ;AAGF,IAAM,mBAAmB;AAQzB,IAAM,cAAc;AAEpB,IAAO,4CAAQ,2BAAY;AAAA,EACzB,CAAC,SACC,gFAAgF,IAAI;AACxF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,UAAU;AAAA,MACR,+BACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC;AAAA,EACjB,OAAO,SAAS;AACd,QAAI,WAAW,QAAQ,QAAQ,KAAK,CAAC,YAAY,KAAK,QAAQ,WAAW,IAAI,GAAG;AAC9E,aAAO,CAAC;AAAA,IACV;AACA,WAAO,kBAAkB,CAAC,KAAa,SAA8B;AACnE,UAAI,CAAC,aAAa,KAAK,GAAG,KAAK,iBAAiB,KAAK,GAAG,GAAG;AACzD;AAAA,MACF;AACA,cAAQ,OAAO,EAAE,MAAM,WAAW,gCAAgC,CAAC;AAAA,IACrE,CAAC;AAAA,EACH;AACF,CAAC;;;AChDD,IAAAC,iBAA2D;AAa3D,IAAM,kBAAqC,CAAC,WAAW,QAAQ,OAAO;AAGtE,IAAM,mBAAmB;AAOzB,SAAS,iBAAiB,YAA0C;AAClE,MAAI,WAAW,SAAS,8BAAe,YAAY;AACjD,WAAO,iBAAiB,KAAK,WAAW,IAAI;AAAA,EAC9C;AACA,MACE,WAAW,SAAS,8BAAe,oBACnC,CAAC,WAAW,YACZ,WAAW,SAAS,SAAS,8BAAe,YAC5C;AACA,WAAO,iBAAiB,KAAK,WAAW,SAAS,IAAI;AAAA,EACvD;AACA,MAAI,WAAW,SAAS,8BAAe,SAAS;AAC9C,WAAO,OAAO,WAAW,UAAU;AAAA,EACrC;AACA,SAAO;AACT;AAGA,SAAS,sBACP,UACuB;AACvB,SAAO,SAAS,YAAY;AAAA,IAC1B,CAAC,eAAe,CAAC,iBAAiB,UAAU;AAAA,EAC9C;AACF;AAMA,SAAS,eAAe,MAAkD;AACxE,MAAI,KAAK,SAAS,8BAAe,oBAAoB,KAAK,aAAa,KAAK;AAC1E,WAAO,CAAC,GAAG,eAAe,KAAK,IAAI,GAAG,GAAG,eAAe,KAAK,KAAK,CAAC;AAAA,EACrE;AACA,SAAO,CAAC,IAAI;AACd;AAQA,SAAS,sBAAsB,MAA4C;AACzE,MAAI,KAAK,SAAS,8BAAe,oBAAoB,KAAK,aAAa,KAAK;AAC1E,WAAO,CAAC;AAAA,EACV;AACA,QAAM,WAAW,eAAe,IAAI;AACpC,QAAM,mBAAmB,SAAS;AAAA,IAChC,CAAC,YACC,QAAQ,SAAS,8BAAe,WAChC,OAAO,QAAQ,UAAU;AAAA,EAC7B;AACA,MAAI,CAAC,kBAAkB;AACrB,WAAO,CAAC;AAAA,EACV;AACA,SAAO,SAAS;AAAA,IACd,CAAC,YACC,QAAQ,SAAS,8BAAe,WAAW,CAAC,iBAAiB,OAAO;AAAA,EACxE;AACF;AAOA,IAAM,mBACJ;AAGF,IAAM,iBAAiB;AAOvB,SAAS,oBAAoB,MAA6B;AACxD,MAAI,KAAK,SAAS,8BAAe,iBAAiB;AAChD,WAAO,KAAK,OAAO,IAAI,CAAC,UAAU,MAAM,MAAM,UAAU,MAAM,MAAM,GAAG,EAAE,KAAK,cAAc;AAAA,EAC9F;AACA,MAAI,KAAK,SAAS,8BAAe,SAAS;AACxC,WAAO,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ;AAAA,EACvD;AACA,MAAI,KAAK,SAAS,8BAAe,oBAAoB,KAAK,aAAa,KAAK;AAC1E,WAAO,oBAAoB,KAAK,IAAI,IAAI,oBAAoB,KAAK,KAAK;AAAA,EACxE;AACA,SAAO;AACT;AAGA,SAAS,aAAa,MAA8B;AAClD,SAAO,iBAAiB,KAAK,cAAc,oBAAoB,IAAI,CAAC,CAAC;AACvE;AAGA,SAAS,oBACP,MACA,SACe;AACf,QAAM,SAAS,KAAK;AACpB,MACE,OAAO,SAAS,8BAAe,oBAC/B,OAAO,YACP,OAAO,SAAS,SAAS,8BAAe,YACxC;AACA,WAAO;AAAA,EACT;AACA,QAAM,OAAO,OAAO,SAAS;AAC7B,SAAO,QAAQ,IAAI,IAAI,IAAI,OAAO;AACpC;AAEA,IAAO,yBAAQ,2BAAY;AAAA,EACzB,CAAC,SACC,gFAAgF,IAAI;AACxF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ;AAAA,MACN;AAAA,QACE,MAAM;AAAA,QACN,YAAY;AAAA,UACV,SAAS;AAAA,YACP,MAAM;AAAA,YACN,OAAO,EAAE,MAAM,SAAS;AAAA,YACxB,aACE;AAAA,UACJ;AAAA,QACF;AAAA,QACA,sBAAsB;AAAA,MACxB;AAAA,IACF;AAAA,IACA,UAAU;AAAA,MACR,YACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC,CAAC,CAAC;AAAA,EACnB,OAAO,SAAS,CAAC,OAAO,GAAG;AACzB,UAAM,UAAU,IAAI,IAAI,SAAS,WAAW,eAAe;AAE3D,WAAO;AAAA,MACL,eAAe,MAAqC;AAClD,cAAM,SAAS,oBAAoB,MAAM,OAAO;AAChD,YAAI,WAAW,MAAM;AACnB;AAAA,QACF;AAEA,cAAM,YAAY,KAAK,UAAU,CAAC;AAClC,YAAI,cAAc,UAAa,CAAC,aAAa,SAAS,GAAG;AACvD;AAAA,QACF;AAEA,cAAM,YACJ,UAAU,SAAS,8BAAe,kBAC9B,sBAAsB,SAAS,IAC/B,sBAAsB,SAAS;AAErC,mBAAW,YAAY,WAAW;AAChC,kBAAQ,OAAO;AAAA,YACb,MAAM;AAAA,YACN,WAAW;AAAA,YACX,MAAM,EAAE,OAAO;AAAA,UACjB,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;AC7LD,IAAAC,iBAA2C;AAe3C,IAAM,gBAAmC;AAAA,EACvC;AAAA,EACA;AAAA,EACA;AAAA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AACF;AAGA,IAAM,mBAAwC,oBAAI,IAAI;AAAA,EACpD;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,IAAM,wBAAwB;AAG9B,SAAS,kBAAkB,MAAwC;AACjE,QAAM,SAAS,KAAK;AAEpB,MAAI,OAAO,SAAS,cAAc;AAChC,WAAO,OAAO,SAAS;AAAA,EACzB;AAEA,MACE,OAAO,SAAS,sBAChB,CAAC,OAAO,YACR,OAAO,SAAS,SAAS,gBACzB,OAAO,SAAS,SAAS,WACzB,OAAO,OAAO,SAAS,cACvB;AACA,WAAO,iBAAiB,IAAI,OAAO,OAAO,IAAI;AAAA,EAChD;AAEA,SAAO;AACT;AAEA,SAAS,mBAAmB,MAAoC;AAC9D,MAAI,KAAK,SAAS,cAAc;AAC9B,WAAO,KAAK;AAAA,EACd;AACA,MACE,KAAK,SAAS,sBACd,CAAC,KAAK,YACN,KAAK,SAAS,SAAS,cACvB;AACA,WAAO,KAAK,SAAS;AAAA,EACvB;AACA,SAAO;AACT;AAEA,SAAS,uBAAuB,MAAwC;AACtE,QAAM,QAAQ,KAAK,UAAU,CAAC;AAC9B,MAAI,UAAU,QAAW;AACvB,WAAO;AAAA,EACT;AACA,QAAM,OAAO,mBAAmB,KAAK;AACrC,SAAO,SAAS,QAAQ,sBAAsB,KAAK,IAAI;AACzD;AAOA,SAAS,QAAQ,UAAuC;AACtD,QAAM,WAAqB,CAAC;AAC5B,aAAW,WAAW,UAAU;AAC9B,QAAI;AACF,eAAS,KAAK,IAAI,OAAO,OAAO,CAAC;AAAA,IACnC,QAAQ;AAAA,IAER;AAAA,EACF;AACA,SAAO;AACT;AAEA,IAAO,uCAAQ,2BAAY;AAAA,EACzB,CAAC,SACC,gFAAgF,IAAI;AACxF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ;AAAA,MACN;AAAA,QACE,MAAM;AAAA,QACN,YAAY;AAAA,UACV,OAAO;AAAA,YACL,MAAM;AAAA,YACN,OAAO,EAAE,MAAM,SAAS;AAAA,YACxB,aACE;AAAA,UACJ;AAAA,QACF;AAAA,QACA,sBAAsB;AAAA,MACxB;AAAA,IACF;AAAA,IACA,UAAU;AAAA,MACR,UACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC,CAAC,CAAC;AAAA,EACnB,OAAO,SAAS,CAAC,OAAO,GAAG;AACzB,UAAM,WAAW,SAAS,SAAS;AACnC,UAAM,UAAU,QAAQ,QAAQ;AAChC,UAAM,WAAW,QAAQ;AAEzB,QAAI,QAAQ,KAAK,CAAC,OAAO,GAAG,KAAK,QAAQ,CAAC,GAAG;AAC3C,aAAO,CAAC;AAAA,IACV;AAEA,WAAO;AAAA,MACL,eAAe,MAAqC;AAClD,YAAI,uBAAuB,IAAI,GAAG;AAChC;AAAA,QACF;AACA,YAAI,kBAAkB,IAAI,GAAG;AAC3B,kBAAQ,OAAO,EAAE,MAAM,WAAW,WAAW,CAAC;AAAA,QAChD;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;AC3KD,IAAAC,iBAA2D;AAa3D,IAAMC,mBAAqC;AAAA,EACzC;AAAA,EACA;AAAA,EACA;AACF;AAOA,IAAM,gBAA6C,oBAAI,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;AAGvE,SAASC,SAAQ,UAAuC;AACtD,QAAM,WAAqB,CAAC;AAC5B,aAAW,WAAW,UAAU;AAC9B,QAAI;AACF,eAAS,KAAK,IAAI,OAAO,OAAO,CAAC;AAAA,IACnC,QAAQ;AAAA,IAER;AAAA,EACF;AACA,SAAO;AACT;AAGA,SAAS,kBACP,MACA,SACe;AACf,QAAM,SAAS,KAAK;AACpB,MACE,OAAO,SAAS,8BAAe,oBAC/B,OAAO,YACP,OAAO,SAAS,SAAS,8BAAe,YACxC;AACA,WAAO;AAAA,EACT;AACA,QAAM,OAAO,OAAO,SAAS;AAC7B,MAAI,CAAC,QAAQ,IAAI,IAAI,GAAG;AACtB,WAAO;AAAA,EACT;AACA,MAAI,KAAK,UAAU,UAAU,cAAc,IAAI,IAAI,KAAK,IAAI;AAC1D,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,IAAO,0CAAQ,2BAAY;AAAA,EACzB,CAAC,SACC,gFAAgF,IAAI;AACxF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ;AAAA,MACN;AAAA,QACE,MAAM;AAAA,QACN,YAAY;AAAA,UACV,SAAS;AAAA,YACP,MAAM;AAAA,YACN,OAAO,EAAE,MAAM,SAAS;AAAA,YACxB,aACE;AAAA,UACJ;AAAA,UACA,SAAS;AAAA,YACP,MAAM;AAAA,YACN,OAAO,EAAE,MAAM,SAAS;AAAA,YACxB,aAAa;AAAA,UACf;AAAA,QACF;AAAA,QACA,sBAAsB;AAAA,MACxB;AAAA,IACF;AAAA,IACA,UAAU;AAAA,MACR,0BACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC,CAAC,CAAC;AAAA,EACnB,OAAO,SAAS,CAAC,OAAO,GAAG;AACzB,UAAM,UAAU,SAAS,WAAW,CAAC;AACrC,QAAI,QAAQ,WAAW,GAAG;AACxB,aAAO,CAAC;AAAA,IACV;AAEA,UAAM,SAASA,SAAQ,OAAO;AAC9B,QAAI,CAAC,OAAO,KAAK,CAAC,OAAO,GAAG,KAAK,QAAQ,QAAQ,CAAC,GAAG;AACnD,aAAO,CAAC;AAAA,IACV;AAEA,UAAM,UAAU,IAAI,IAAI,SAAS,WAAWD,gBAAe;AAE3D,WAAO;AAAA,MACL,eAAe,MAAqC;AAClD,cAAM,SAAS,kBAAkB,MAAM,OAAO;AAC9C,YAAI,WAAW,MAAM;AACnB,kBAAQ,OAAO;AAAA,YACb;AAAA,YACA,WAAW;AAAA,YACX,MAAM,EAAE,OAAO;AAAA,UACjB,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;AC/GD,IAAAE,iBAMO;AACP,IAAAC,MAAoB;AAOpB,IAAMC,mBAAqC;AAAA,EACzC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,SAASC,eAAc,UAAkB,YAA6B;AACpE,MAAID,iBAAgB,KAAK,CAAC,OAAO,GAAG,KAAK,QAAQ,CAAC,GAAG;AACnD,WAAO;AAAA,EACT;AACA,SAAO,eAAe,KAAK,WAAW,MAAM,GAAG,IAAI,CAAC;AACtD;AAGA,SAAS,YAAY,QAAyB;AAC5C,SAAO,uBAAuB,KAAK,MAAM;AAC3C;AAGA,SAASE,QAAO,MAAgD;AAC9D,MACE,KAAK,SAAS,8BAAe,kBAC7B,KAAK,SAAS,8BAAe,uBAC7B;AACA,WAAOA,QAAO,KAAK,UAAU;AAAA,EAC/B;AACA,SAAO;AACT;AAQA,SAAS,iBACP,MACA,YACiB;AACjB,QAAM,QAAkB,CAAC;AACzB,aAAW,QAAQ,KAAK,YAAY;AAClC,QAAI,KAAK,SAAS,8BAAe,UAAU;AACzC,aAAO;AAAA,IACT;AACA,QAAI,KAAK,YAAY,KAAK,aAAa,KAAK,UAAU,KAAK,SAAS,QAAQ;AAC1E,aAAO;AAAA,IACT;AACA,UAAM,QAAQ,KAAK;AACnB,QACE,MAAM,SAAS,8BAAe,WAC9B,OAAO,MAAM,UAAU,UACvB;AACA,aAAO;AAAA,IACT;AACA,UAAM,OAAO,WAAW,QAAQ,KAAK;AACrC,QAAI,CAAC,MAAM,SAAS,IAAI,GAAG;AACzB,YAAM,KAAK,IAAI;AAAA,IACjB;AAAA,EACF;AACA,SAAO,MAAM,SAAS,IAAI,QAAQ;AACpC;AAGA,SAAS,oBACP,MACA,OACS;AACT,MAAI,UAAuC;AAC3C,SAAO,YAAY,MAAM;AACvB,UAAM,WAAW,QAAQ,UAAU,KAAK,CAAC,MAAM,EAAE,SAAS,KAAK,IAAI;AACnE,QAAI,aAAa,QAAW;AAC1B,aAAO,SAAS,KAAK;AAAA,QACnB,CAAC,QAAQ,IAAI,KAAK,SAAS,8BAAe;AAAA,MAC5C;AAAA,IACF;AACA,cAAU,QAAQ;AAAA,EACpB;AACA,SAAO;AACT;AAEA,IAAM,oBACD,gBAAY,cAAiB,gBAAY,YAAe,gBAAY;AAOzE,SAAS,uBACP,MACA,UACS;AACT,QAAM,UAAU,SAAS,QAAQ,eAAe;AAChD,QAAM,SAAS,SAAS,sBAAsB,IAAI,IAAI;AACtD,MAAI,SAAS,QAAQ,oBAAoB,MAAM;AAC/C,MAAI,WAAW,QAAW;AACxB,WAAO;AAAA,EACT;AACA,OAAK,OAAO,QAAW,gBAAY,WAAW,GAAG;AAC/C,aAAS,QAAQ,iBAAiB,MAAM;AAAA,EAC1C;AACA,UAAQ,OAAO,QAAQ,uBAAuB;AAChD;AAEA,IAAO,6BAAQ,2BAAY;AAAA,EACzB,CAAC,SACC,8EAA8E,IAAI;AACtF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,SAAS;AAAA,IACT,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,UAAU;AAAA,MACR,YACE;AAAA,MACF,cACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC;AAAA,EACjB,OAAO,SAAS;AACd,UAAM,aAAa,QAAQ;AAC3B,QAAID,eAAc,QAAQ,UAAU,WAAW,QAAQ,CAAC,GAAG;AACzD,aAAO,CAAC;AAAA,IACV;AAEA,QAAI,WAAW,QAAQ,QAAQ,GAAG;AAChC,aAAO,CAAC;AAAA,IACV;AAEA,QAAI;AACJ,QAAI;AACF,iBAAW,2BAAY,kBAAkB,OAAO;AAAA,IAClD,QAAQ;AACN,iBAAW;AAAA,IACb;AAGA,UAAM,mBAAmB,oBAAI,IAAoB;AAEjD,aAAS,gBAAgB,MAA+B,KAAsB;AAC5E,YAAM,SAAS,KAAK;AACpB,UACE,OAAO,SAAS,8BAAe,oBAC/B,CAAC,OAAO,YACR,OAAO,SAAS,SAAS,8BAAe,YACxC;AACA,eAAO,OAAO,SAAS,SAAS;AAAA,MAClC;AACA,UAAI,OAAO,SAAS,8BAAe,YAAY;AAC7C,eAAO,iBAAiB,IAAI,OAAO,IAAI,MAAM;AAAA,MAC/C;AACA,aAAO;AAAA,IACT;AAEA,aAAS,SACP,MACmC;AAKnC,YAAM,SAAS,KAAK;AACpB,UACE,OAAO,SAAS,8BAAe,oBAC/B,OAAO,SAAS,SAAS,8BAAe,YACxC;AACA,eAAO;AAAA,MACT;AACA,YAAM,MAAM,KAAK,UAAU,CAAC;AAC5B,UACE,QAAQ,UACR,KAAK,UAAU,WAAW,KAC1B,IAAI,SAAS,8BAAe,eAC5B;AACA,eAAO;AAAA,MACT;AACA,YAAM,QAAQC,QAAO,GAAG;AACxB,UAAI,MAAM,SAAS,8BAAe,kBAAkB;AAClD,eAAO;AAAA,MACT;AACA,YAAM,SAAS,iBAAiB,OAAO,UAAU;AACjD,UAAI,WAAW,MAAM;AACnB,eAAO;AAAA,MACT;AACA,YAAM,WAAW,OAAO;AACxB,YAAM,iBAAiB,IAAI,OAAO,KAAK,IAAI,CAAC;AAC5C,aAAO,CAAC,UAAU;AAAA,QAChB,MAAM,YAAY,UAAU,MAAM;AAAA,QAClC,MAAM,YAAY,KAAK,cAAc;AAAA,MACvC;AAAA,IACF;AAEA,WAAO;AAAA,MACL,kBAAkB,MAAwC;AACxD,YAAI,CAAC,YAAY,KAAK,OAAO,KAAK,GAAG;AACnC;AAAA,QACF;AACA,mBAAW,QAAQ,KAAK,YAAY;AAClC,cACE,KAAK,SAAS,8BAAe,mBAC7B,KAAK,SAAS,SAAS,8BAAe,YACtC;AACA,6BAAiB,IAAI,KAAK,MAAM,MAAM,KAAK,SAAS,IAAI;AAAA,UAC1D;AAAA,QACF;AAAA,MACF;AAAA,MAEA,eAAe,MAAqC;AAClD,YAAI,gBAAgB,MAAM,YAAY,GAAG;AACvC,gBAAM,MAAM,SAAS,IAAI;AACzB,kBAAQ,OAAO;AAAA,YACb;AAAA,YACA,WAAW;AAAA,YACX,GAAI,QAAQ,OAAO,CAAC,IAAI,EAAE,IAAI;AAAA,UAChC,CAAC;AACD;AAAA,QACF;AAEA,YAAI,CAAC,gBAAgB,MAAM,MAAM,GAAG;AAClC;AAAA,QACF;AACA,cAAM,MAAM,KAAK,UAAU,CAAC;AAC5B,YAAI,QAAQ,UAAa,IAAI,SAAS,8BAAe,YAAY;AAC/D;AAAA,QACF;AACA,cAAM,SACJ,oBAAoB,KAAK,WAAW,SAAS,GAAG,CAAC,KAChD,aAAa,QAAQ,uBAAuB,KAAK,QAAQ;AAC5D,YAAI,QAAQ;AACV,kBAAQ,OAAO;AAAA,YACb;AAAA,YACA,WAAW;AAAA,YACX,MAAM,EAAE,MAAM,IAAI,KAAK;AAAA,UACzB,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;AC1OD,IAAAC,iBAIO;AAYP,IAAM,uBAAuB;AAG7B,IAAM,oBAAoB;AAE1B,IAAMC,mBAAqC;AAAA,EACzC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,qBAAwC;AAAA,EAC5C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAMA,IAAM,mBAAwC,oBAAI,IAAI;AAAA;AAAA,EAEpD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AACF,CAAC;AAED,IAAMC,kBAA8C,oBAAI,IAAI;AAAA,EAC1D,8BAAe;AAAA,EACf,8BAAe;AAAA,EACf,8BAAe;AACjB,CAAC;AAED,IAAM,0BAA+C,oBAAI,IAAI,CAAC,OAAO,KAAK,CAAC;AAE3E,SAASC,eAAc,UAAkB,YAA6B;AACpE,MAAIF,iBAAgB,KAAK,CAAC,OAAO,GAAG,KAAK,QAAQ,CAAC,GAAG;AACnD,WAAO;AAAA,EACT;AACA,SAAO,eAAe,KAAK,WAAW,MAAM,GAAG,IAAI,CAAC;AACtD;AAEA,SAASG,YAAW,UAA2B;AAC7C,SAAO,mBAAmB,KAAK,CAAC,OAAO,GAAG,KAAK,QAAQ,CAAC;AAC1D;AAGA,SAASC,QAAO,MAAoC;AAClD,MACE,KAAK,SAAS,8BAAe,kBAC7B,KAAK,SAAS,8BAAe,yBAC7B,KAAK,SAAS,8BAAe,qBAC7B;AACA,WAAOA,QAAO,KAAK,UAAU;AAAA,EAC/B;AACA,SAAO;AACT;AAEA,SAAS,eAAe,MAAqD;AAC3E,SACE,KAAK,SAAS,8BAAe,WAC7B,WAAW,QACX,KAAK,UAAU;AAEnB;AAQA,SAAS,cAAc,MAAqB,OAAwB;AAClE,MAAI,QAAQ,mBAAmB;AAC7B,WAAO;AAAA,EACT;AACA,QAAM,QAAQA,QAAO,IAAI;AACzB,UAAQ,MAAM,MAAM;AAAA,IAClB,KAAK,8BAAe,SAAS;AAC3B,aAAO;AAAA,IACT;AAAA,IACA,KAAK,8BAAe,iBAAiB;AACnC,aAAO,MAAM,YAAY,WAAW;AAAA,IACtC;AAAA,IACA,KAAK,8BAAe,iBAAiB;AACnC,cACG,MAAM,aAAa,OAAO,MAAM,aAAa,QAC9C,MAAM,SAAS,SAAS,8BAAe,WACvC,OAAO,MAAM,SAAS,UAAU;AAAA,IAEpC;AAAA,IACA,KAAK,8BAAe,iBAAiB;AACnC,aAAO,MAAM,SAAS;AAAA,QACpB,CAAC,OACC,OAAO,QACP,GAAG,SAAS,8BAAe,iBAC3B,cAAc,IAAI,QAAQ,CAAC;AAAA,MAC/B;AAAA,IACF;AAAA,IACA,KAAK,8BAAe,kBAAkB;AACpC,aAAO,MAAM,WAAW,MAAM,CAAC,SAAS;AACtC,YAAI,KAAK,SAAS,8BAAe,UAAU;AACzC,iBAAO;AAAA,QACT;AACA,YAAI,KAAK,aAAa,KAAK,UAAU,KAAK,SAAS,QAAQ;AACzD,iBAAO;AAAA,QACT;AACA,YAAI,KAAK,YAAY,KAAK,IAAI,SAAS,8BAAe,SAAS;AAC7D,iBAAO;AAAA,QACT;AACA,eAAO,cAAc,KAAK,OAAO,QAAQ,CAAC;AAAA,MAC5C,CAAC;AAAA,IACH;AAAA,IACA,SAAS;AACP,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAGA,SAAS,mBAAmB,MAAoC;AAC9D,QAAM,QAAQA,QAAO,IAAI;AACzB,MACE,MAAM,SAAS,8BAAe,kBAC9B,MAAM,OAAO,SAAS,8BAAe,oBACrC,CAAC,MAAM,OAAO,YACd,MAAM,OAAO,OAAO,SAAS,8BAAe,cAC5C,MAAM,OAAO,OAAO,SAAS,YAC7B,MAAM,OAAO,SAAS,SAAS,8BAAe,cAC9C,MAAM,OAAO,SAAS,SAAS,YAC/B,MAAM,UAAU,WAAW,KAC3B,MAAM,UAAU,CAAC,MAAM,UACvB,MAAM,UAAU,CAAC,EAAE,SAAS,8BAAe,eAC3C;AACA,WAAOA,QAAO,MAAM,UAAU,CAAC,CAAC;AAAA,EAClC;AACA,SAAO;AACT;AAUA,SAAS,SAAS,MAAqB,YAAuC;AAC5E,QAAM,OAAO,mBAAmB,IAAI;AAEpC,MAAI,eAAe,IAAI,GAAG;AACxB,QAAI,CAAC,YAAY;AACf,aAAO;AAAA,IACT;AAEA,QAAI,OAAO,KAAK,KAAK,MAAM,KAAK,GAAG;AACjC,aAAO;AAAA,IACT;AACA,WAAO,EAAE,MAAM,SAAS,MAAM,EAAE;AAAA,EAClC;AAEA,MAAI,KAAK,SAAS,8BAAe,iBAAiB;AAChD,WAAO,cAAc,MAAM,CAAC,IACxB,EAAE,MAAM,SAAS,MAAM,KAAK,SAAS,OAAO,IAC5C;AAAA,EACN;AAEA,MAAI,KAAK,SAAS,8BAAe,kBAAkB;AACjD,WAAO,cAAc,MAAM,CAAC,IACxB,EAAE,MAAM,UAAU,MAAM,KAAK,WAAW,OAAO,IAC/C;AAAA,EACN;AAEA,MACE,KAAK,SAAS,8BAAe,iBAC7B,KAAK,OAAO,SAAS,8BAAe,cACpC,wBAAwB,IAAI,KAAK,OAAO,IAAI,GAC5C;AACA,UAAM,MAAM,KAAK,UAAU,CAAC;AAC5B,QACE,KAAK,UAAU,WAAW,KAC1B,QAAQ,UACR,IAAI,SAAS,8BAAe,eAC5B;AACA,aAAO;AAAA,IACT;AACA,UAAM,UAAUA,QAAO,GAAG;AAC1B,QAAI,QAAQ,SAAS,8BAAe,iBAAiB;AACnD,aAAO;AAAA,IACT;AACA,WAAO,cAAc,SAAS,CAAC,IAC3B,EAAE,MAAM,KAAK,OAAO,SAAS,QAAQ,QAAQ,OAAO,MAAM,QAAQ,SAAS,OAAO,IAClF;AAAA,EACN;AAEA,SAAO;AACT;AAGA,SAASC,mBAAkB,MAA2C;AACpE,MAAI,UAA4C,KAAK;AACrD,SAAO,YAAY,UAAa,YAAY,MAAM;AAChD,QAAIJ,gBAAe,IAAI,QAAQ,IAAI,GAAG;AACpC,aAAO;AAAA,IACT;AACA,cAAU,QAAQ;AAAA,EACpB;AACA,SAAO;AACT;AAMA,IAAM,yBAAmE,oBAAI;AAAA,EAC3E;AAAA,IACE;AAAA,MACE;AAAA,MACA,oBAAI,IAAI,CAAC,QAAQ,UAAU,WAAW,UAAU,eAAe,QAAQ,CAAC;AAAA,IAC1E;AAAA,IACA,CAAC,SAAS,oBAAI,IAAI,CAAC,QAAQ,SAAS,CAAC,CAAC;AAAA,IACtC,CAAC,QAAQ,oBAAI,IAAI,CAAC,WAAW,CAAC,CAAC;AAAA,EACjC;AACF;AAEA,SAAS,0BACP,MACA,UACS;AACT,QAAM,SAAS,KAAK;AACpB,MACE,OAAO,SAAS,8BAAe,cAC/B,OAAO,SAAS,mBAChB;AACA,WAAO;AAAA,EACT;AACA,MACE,OAAO,SAAS,8BAAe,oBAC/B,OAAO,YACP,OAAO,OAAO,SAAS,8BAAe,cACtC,OAAO,SAAS,SAAS,8BAAe,YACxC;AACA,WAAO;AAAA,EACT;AACA,QAAM,UAAU,uBAAuB,IAAI,OAAO,OAAO,IAAI;AAC7D,MAAI,YAAY,UAAa,CAAC,QAAQ,IAAI,OAAO,SAAS,IAAI,GAAG;AAC/D,WAAO;AAAA,EACT;AAGA,MAAI,OAAO,OAAO,SAAS,YAAY,OAAO,SAAS,SAAS,UAAU;AACxE,WAAO,KAAK,UAAU,CAAC,MAAM;AAAA,EAC/B;AACA,SAAO;AACT;AAOA,SAAS,WAAW,YAA0C;AAC5D,QAAM,SAAS,WAAW;AAE1B,MAAI,OAAO,SAAS,8BAAe,kBAAkB;AACnD,QAAI,OAAO,WAAW,YAAY;AAEhC,aAAO;AAAA,IACT;AACA,UAAM,cAAc,OAAO;AAE3B,QACE,YAAY,SAAS,8BAAe,wBACpC,YAAY,SAAS,QACrB;AACA,aAAO;AAAA,IACT;AAEA,QAAI,YAAY,SAAS,8BAAe,kBAAkB;AACxD,aAAO;AAAA,IACT;AAEA,QACE,YAAY,SAAS,8BAAe,mBACpC,YAAY,aAAa,UACzB;AACA,aAAO;AAAA,IACT;AAEA,QACE,CAAC,OAAO,YACR,OAAO,SAAS,SAAS,8BAAe,cACxC,iBAAiB,IAAI,OAAO,SAAS,IAAI,KACzC,YAAY,SAAS,8BAAe,kBACpC,YAAY,WAAW,QACvB;AACA,aAAO;AAAA,IACT;AAIA,WAAO;AAAA,EACT;AAGA,MACE,OAAO,SAAS,8BAAe,kBAC/B,OAAO,UAAU,YACjB;AACA,WAAO;AAAA,EACT;AAIA,MAAI,OAAO,SAAS,8BAAe,eAAe;AAChD,WAAO;AAAA,EACT;AAGA,MAAI,OAAO,SAAS,8BAAe,kBAAkB;AACnD,WAAO;AAAA,EACT;AAKA,MACE,OAAO,SAAS,8BAAe,kBAC/B,OAAO,UAAU,SAAS,UAAU,KACpC,0BAA0B,QAAQ,UAAU,GAC5C;AACA,WAAO;AAAA,EACT;AAGA,MACE,OAAO,SAAS,8BAAe,mBAC/B,OAAO,aAAa,UACpB;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEA,IAAO,uCAAQ,2BAAY;AAAA,EACzB,CAAC,SACC,8EAA8E,IAAI;AACtF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ;AAAA,MACN;AAAA,QACE,MAAM;AAAA,QACN,sBAAsB;AAAA,QACtB,YAAY;AAAA,UACV,aAAa,EAAE,MAAM,UAAU,SAAS,EAAE;AAAA,UAC1C,YAAY,EAAE,MAAM,UAAU;AAAA,UAC9B,iBAAiB,EAAE,MAAM,UAAU;AAAA,QACrC;AAAA,MACF;AAAA,IACF;AAAA,IACA,UAAU;AAAA,MACR,iBACE;AAAA,MACF,YACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC,CAAC,CAAC;AAAA,EACnB,OAAO,SAAS,CAAC,UAAU,GAAG;AAC5B,UAAM,UAAU,cAAc,CAAC;AAC/B,UAAM,cAAc,QAAQ,eAAe;AAC3C,UAAM,aAAa,QAAQ,cAAc;AACzC,UAAM,kBAAkB,QAAQ,mBAAmB;AAEnD,UAAM,aAAa,QAAQ;AAC3B,UAAM,WAAW,QAAQ;AAEzB,QAAIC,eAAc,UAAU,WAAW,QAAQ,CAAC,GAAG;AACjD,aAAO,CAAC;AAAA,IACV;AACA,QAAI,mBAAmBC,YAAW,QAAQ,GAAG;AAC3C,aAAO,CAAC;AAAA,IACV;AAEA,aAAS,0BACP,YACS;AACT,YAAM,YAAY,WAAW,qBAAqB,UAAU;AAC5D,YAAM,WAAW,UAAU,CAAC;AAC5B,UAAI,aAAa,QAAW;AAC1B,eAAO;AAAA,MACT;AACA,iBAAW,aAAa,SAAS,YAAY;AAE3C,YAAI,UAAU,SAAS,MAAM;AAC3B;AAAA,QACF;AACA,YAAI,UAAU,QAAQ,GAAG;AACvB,iBAAO;AAAA,QACT;AAGA,YAAI,UAAU,WAAW,SAAS,8BAAe,YAAY;AAC3D,iBAAO;AAAA,QACT;AACA,YAAI,CAAC,WAAW,UAAU,UAAU,GAAG;AACrC,iBAAO;AAAA,QACT;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,MACL,mBAAmB,MAAyC;AAC1D,cAAM,cAAc,KAAK;AACzB,YACE,YAAY,SAAS,8BAAe,uBACpC,YAAY,SAAS,WACrB,YAAY,YAAY,MACxB;AACA;AAAA,QACF;AACA,YAAI,KAAK,GAAG,SAAS,8BAAe,cAAc,KAAK,SAAS,MAAM;AACpE;AAAA,QACF;AACA,YAAIE,mBAAkB,IAAI,MAAM,MAAM;AACpC;AAAA,QACF;AAEA,cAAM,YAAY,SAAS,KAAK,MAAM,UAAU;AAChD,YAAI,cAAc,MAAM;AACtB;AAAA,QACF;AACA,YAAI,UAAU,SAAS,WAAW,UAAU,OAAO,aAAa;AAC9D;AAAA,QACF;AACA,YAAI,CAAC,0BAA0B,IAAI,GAAG;AACpC;AAAA,QACF;AAEA,gBAAQ,OAAO;AAAA,UACb,MAAM,KAAK;AAAA,UACX,WACE,UAAU,SAAS,UAAU,eAAe;AAAA,UAC9C,MAAM,EAAE,MAAM,KAAK,GAAG,MAAM,MAAM,UAAU,KAAK;AAAA,QACnD,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;ACnhBD,IAAAC,iBAA2D;AAS3D,IAAM,aAAkC,oBAAI,IAAI;AAAA,EAC9C;AAAA,EAAS;AAAA,EAAU;AAAA,EAAQ;AAAA,EAAY;AAAA,EAAa;AAAA,EAAW;AAAA,EAC/D;AAAA,EAAc;AAAA,EAAW;AAAA,EAAgB;AAAA,EAAgB;AAAA,EAAS;AAAA,EAClE;AAAA,EAAc;AAAA,EAAY;AAAA,EAAW;AAAA,EAAQ;AAAA,EAAU;AAAA,EACvD;AAAA,EAAwB;AAAA,EAAW;AAAA,EAAO;AAAA,EAAS;AAAA,EAAY;AAAA,EAC/D;AAAA,EAAQ;AACV,CAAC;AAID,IAAM,gBAAqC,oBAAI,IAAI;AAAA,EACjD;AAAA,EAAO;AAAA,EAAY;AAAA,EAAS;AAAA,EAAe;AAAA,EAAS;AAAA,EAAU;AAChE,CAAC;AAED,IAAM,aAAkC,oBAAI,IAAI,CAAC,OAAO,YAAY,OAAO,CAAC;AAC5E,IAAM,cAAmC,oBAAI,IAAI,CAAC,UAAU,SAAS,CAAC;AAEtE,IAAMC,gBAAe;AAMrB,IAAMC,aAAiC,IAAI;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,6CAO2C,MAAM,KAAK;AACxD;AAEA,IAAMC,WAAU;AAQhB,SAAS,WAAW,OAA0D;AAC5E,QAAM,QAAQ,MAAM,QAAQ,OAAO,EAAE,EAAE,MAAM,IAAI,EAAE,IAAI,CAAC,SAAS,KAAK,QAAQ,cAAc,EAAE,CAAC;AAC/F,QAAM,cAAwB,CAAC;AAC/B,QAAM,OAAyC,CAAC;AAChD,MAAI,UAAiD;AACrD,aAAW,QAAQ,OAAO;AACxB,UAAM,QAAQ,0BAA0B,KAAK,IAAI;AACjD,QAAI,OAAO;AACT,gBAAU,EAAE,OAAO,MAAM,CAAC,KAAK,IAAI,YAAY,GAAG,MAAM,MAAM,CAAC,KAAK,GAAG;AACvE,WAAK,KAAK,OAAO;AAAA,IACnB,WAAW,YAAY,MAAM;AAC3B,cAAQ,QAAQ,IAAI,KAAK,KAAK,CAAC;AAAA,IACjC,OAAO;AACL,kBAAY,KAAK,IAAI;AAAA,IACvB;AAAA,EACF;AACA,SAAO,EAAE,aAAa,YAAY,KAAK,IAAI,EAAE,KAAK,GAAG,KAAK;AAC5D;AAGA,SAAS,YAAY,MAAwB;AAC3C,UAAQ,KAAK,MAAMA,QAAO,KAAK,CAAC,GAC7B,IAAI,CAAC,SAAS,KAAK,YAAY,CAAC,EAChC,OAAO,CAAC,SAAS,KAAK,SAAS,KAAK,CAACD,WAAU,IAAI,IAAI,CAAC;AAC7D;AAGA,SAAS,QAAQ,MAAc,OAAqC;AAClE,QAAM,QAAQ,oBAAI,IAAY;AAC9B,aAAW,SAAS,MAAO,OAAM,IAAI,KAAK,KAAK,CAAC;AAChD,SAAO,YAAY,IAAI,EAAE,MAAM,CAAC,SAAS,MAAM,IAAI,IAAI,KAAK,MAAM,IAAI,KAAK,IAAI,CAAC,CAAC;AACnF;AAGA,SAAS,iBAAiB,MAAgE;AACxF,UAAQ,KAAK,MAAM;AAAA;AAAA;AAAA,IAGjB,KAAK,8BAAe;AAAA,IACpB,KAAK,8BAAe;AAClB,aAAO,KAAK,eAAe,OAAO,OAAO,iBAAiB,KAAK,WAAW;AAAA,IAC5E,KAAK,8BAAe;AAAA,IACpB,KAAK,8BAAe;AAClB,aAAO,KAAK,OAAO,OAAO,OAAO,EAAE,MAAM,KAAK,GAAG,MAAM,QAAQ,WAAW,KAAK,MAAM,EAAE;AAAA,IACzF,KAAK,8BAAe;AAAA,IACpB,KAAK,8BAAe;AAAA,IACpB,KAAK,8BAAe;AAAA,IACpB,KAAK,8BAAe;AAClB,aAAO,KAAK,OAAO,OAAO,OAAO,EAAE,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,EAAE;AAAA,IACpE,KAAK,8BAAe,qBAAqB;AACvC,YAAM,aAAa,KAAK,aAAa,CAAC;AACtC,UAAI,eAAe,UAAa,WAAW,GAAG,SAAS,8BAAe,WAAY,QAAO;AACzF,YAAM,OAAO,WAAW;AACxB,YAAM,SACJ,QAAQ,SACP,KAAK,SAAS,8BAAe,2BAC5B,KAAK,SAAS,8BAAe,sBAC3B,WAAW,KAAK,MAAM,IACtB,CAAC;AACP,aAAO,EAAE,MAAM,WAAW,GAAG,MAAM,OAAO;AAAA,IAC5C;AAAA,IACA,KAAK,8BAAe;AAAA,IACpB,KAAK,8BAAe;AAAA,IACpB,KAAK,8BAAe;AAAA,IACpB,KAAK,8BAAe,qBAAqB;AACvC,UAAI,KAAK,IAAI,SAAS,8BAAe,WAAY,QAAO;AACxD,YAAM,SACJ,KAAK,SAAS,8BAAe,mBACzB,WAAW,KAAK,MAAM,MAAM,IAC5B,KAAK,SAAS,8BAAe,oBAC3B,WAAW,KAAK,MAAM,IACtB,CAAC;AACT,aAAO,EAAE,MAAM,KAAK,IAAI,MAAM,OAAO;AAAA,IACvC;AAAA,IACA;AACE,aAAO;AAAA,EACX;AACF;AAEA,SAAS,WAAW,QAAiD;AACnE,QAAM,QAAkB,CAAC;AACzB,aAAW,SAAS,QAAQ;AAC1B,UAAM,SAAS,MAAM,SAAS,8BAAe,oBAAoB,MAAM,OAAO;AAC9E,QAAI,OAAO,SAAS,8BAAe,WAAY,OAAM,KAAK,OAAO,IAAI;AAAA,aAC5D,OAAO,SAAS,8BAAe,oBAAqB;AAAA,EAC/D;AACA,SAAO;AACT;AAEA,SAAS,SAAS,OAAuC;AACvD,QAAM,SAAS,oBAAI,IAAY;AAC/B,aAAW,QAAQ,MAAO,YAAW,QAAQ,gBAAgB,IAAI,EAAG,QAAO,IAAI,IAAI;AACnF,SAAO;AACT;AAEA,IAAO,mCAAQ,2BAAY;AAAA,EACzB,CAAC,SACC,gFAAgF,IAAI;AACxF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,gBAAgB;AAAA,IAChB,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,UAAU;AAAA,MACR,mBACE;AAAA,MACF,aAAa;AAAA,IACf;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC;AAAA,EACjB,OAAO,SAAS;AACd,QAAI,gBAAgB,QAAQ,UAAU,QAAQ,WAAW,IAAI,GAAG;AAC9D,aAAO,CAAC;AAAA,IACV;AACA,UAAM,aAAa,QAAQ;AAE3B,WAAO;AAAA,MACL,UAAgB;AACd,mBAAW,WAAW,WAAW,eAAe,GAAG;AACjD,cAAI,QAAQ,SAAS,WAAW,CAAC,QAAQ,MAAM,WAAW,GAAG,EAAG;AAChE,gBAAM,EAAE,aAAa,KAAK,IAAI,WAAW,QAAQ,KAAK;AACtD,cAAID,cAAa,KAAK,WAAW,EAAG;AACpC,gBAAM,WAAW,IAAI,IAAI,KAAK,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC;AACpD,cAAI,CAAC,GAAG,QAAQ,EAAE,KAAK,CAAC,SAAS,WAAW,IAAI,IAAI,CAAC,EAAG;AACxD,cAAI,CAAC,GAAG,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,IAAI,IAAI,CAAC,EAAG;AAC5D,cAAI,YAAY,WAAW,EAAG;AAE9B,gBAAM,QAAQ,WAAW,cAAc,SAAS,EAAE,iBAAiB,MAAM,CAAC;AAC1E,cAAI,UAAU,QAAQ,MAAM,IAAI,MAAM,SAAS,QAAQ,IAAI,IAAI,OAAO,EAAG;AACzE,cAAI,OAAO,WAAW,oBAAoB,MAAM,MAAM,CAAC,CAAC;AACxD,cAAI,cAAyD;AAC7D,iBAAO,QAAQ,QAAQ,KAAK,SAAS,8BAAe,SAAS;AAC3D,0BAAc,iBAAiB,IAAI;AACnC,gBAAI,gBAAgB,KAAM;AAC1B,mBAAO,KAAK,UAAU;AAAA,UACxB;AACA,cAAI,gBAAgB,KAAM;AAE1B,gBAAM,YAAY,KAAK,OAAO,CAAC,QAAQ,WAAW,IAAI,IAAI,IAAI,CAAC;AAC/D,gBAAM,aAAa,KAAK,OAAO,CAAC,QAAQ,YAAY,IAAI,IAAI,IAAI,CAAC;AACjE,cAAI,YAAY,WAAW,KAAK,UAAU,WAAW,KAAK,WAAW,WAAW,GAAG;AACjF;AAAA,UACF;AAEA,gBAAM,aAAa,SAAS,CAAC,YAAY,IAAI,CAAC;AAC9C,gBAAM,cAAc,SAAS,YAAY,MAAM;AAC/C,gBAAM,QAAQ,oBAAI,IAAI,CAAC,GAAG,YAAY,GAAG,WAAW,CAAC;AAErD,cAAI,cAAc,QAAQ,aAAa,KAAK;AAC5C,qBAAW,OAAO,WAAW;AAC3B,kBAAM,OAAO,IAAI,KAAK,QAAQ,iBAAiB,EAAE;AACjD,kBAAM,QAAQ,+CAA+C,KAAK,IAAI;AACtE,gBAAI,UAAU,MAAM;AAClB,4BAAc;AACd;AAAA,YACF;AACA,kBAAM,MAAM,oBAAI,IAAI,CAAC,GAAG,gBAAgB,MAAM,CAAC,GAAG,MAAM,GAAG,EAAE,IAAI,KAAK,EAAE,GAAG,GAAG,UAAU,CAAC;AACzF,gBAAI,CAAC,QAAQ,MAAM,CAAC,KAAK,IAAI,GAAG,GAAG;AACjC,4BAAc;AACd;AAAA,YACF;AACA,uBAAW,QAAQ,gBAAgB,MAAM,CAAC,GAAG,MAAM,GAAG,EAAE,IAAI,KAAK,EAAE,EAAG,OAAM,IAAI,IAAI;AAAA,UACtF;AACA,cAAI,aAAa;AACf,uBAAW,OAAO,YAAY;AAC5B,kBAAI,CAAC,QAAQ,IAAI,KAAK,QAAQ,iBAAiB,EAAE,GAAG,KAAK,GAAG;AAC1D,8BAAc;AACd;AAAA,cACF;AAAA,YACF;AAAA,UACF;AACA,cAAI,CAAC,YAAa;AAElB,kBAAQ,OAAO;AAAA,YACb,MAAM;AAAA,YACN,WAAW;AAAA,YACX,SAAS;AAAA,cACP;AAAA,gBACE,WAAW;AAAA,gBACX,KAAK,CAAC,UAAU,MAAM,YAAY,CAAC,QAAQ,MAAM,CAAC,GAAG,MAAM,MAAM,CAAC,CAAC,CAAC;AAAA,cACtE;AAAA,YACF;AAAA,UACF,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;AC7OD,IAAAG,iBAA2D;AAe3D,IAAM,YAAY;AAClB,IAAM,qBAAqB;AAE3B,IAAMC,gBACJ;AAGF,IAAM,WAAW;AACjB,IAAM,eAAe;AAarB,IAAM,cAAc;AACpB,IAAM,aAAa;AACnB,IAAM,cAAc;AACpB,IAAM,mBAAmB;AAKzB,IAAM,iBAAiB;AAEvB,IAAM,sBAAsB;AAG5B,SAASC,yBACP,GACA,GACS;AACT,SACE,MAAM,UACN,MAAM,UACN,EAAE,SAAS,UACX,EAAE,SAAS,UACX,EAAE,IAAI,MAAM,SAAS,EAAE,IAAI,IAAI,OAAO;AAE1C;AAOA,SAAS,gBAAgB,MAA8B;AACrD,QAAM,SAAS,KAAK;AACpB,MAAI,WAAW,OAAW,QAAO;AACjC,QAAM,OACJ,UAAU,UAAU,MAAM,QAAQ,OAAO,IAAI,IACxC,OAAO,OACR;AACN,MAAI,SAAS,OAAW,QAAO;AAC/B,QAAM,QAAQ,KAAK,QAAQ,IAAI;AAC/B,QAAM,OAAO,SAAS,IAAI,KAAK,QAAQ,CAAC,IAAI;AAC5C,SAAO,SAAS,UAAa,KAAK,SAAS,KAAK;AAClD;AAEA,IAAO,8BAAQ,2BAAY;AAAA,EACzB,CAAC,SACC,gFAAgF,IAAI;AACxF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,UAAU;AAAA,MACR,mBACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC;AAAA,EACjB,OAAO,SAAS;AACd,QAAI,gBAAgB,QAAQ,UAAU,QAAQ,WAAW,IAAI,GAAG;AAC9D,aAAO,CAAC;AAAA,IACV;AACA,UAAM,aAAa,QAAQ;AAC3B,UAAM,QAAQ,WAAW;AAEzB,aAAS,aAAa,SAAoC;AACxD,YAAM,SAAS,WAAW,eAAe,SAAS,EAAE,iBAAiB,MAAM,CAAC;AAC5E,aAAO,CAAC,UAAU,OAAO,IAAI,IAAI,OAAO,QAAQ,IAAI,MAAM;AAAA,IAC5D;AAEA,aAAS,kBAAkB,SAAoC;AAC7D,YAAM,QAAQ,WAAW,cAAc,SAAS,EAAE,iBAAiB,MAAM,CAAC;AAC1E,UAAI,UAAU,KAAM,QAAO;AAO3B,eACM,OAAyC,WAAW,oBAAoB,MAAM,MAAM,CAAC,CAAC,GAC1F,QAAQ,QAAQ,KAAK,SAAS,8BAAe,SAC7C,OAAO,KAAK,QACZ;AACA,YAAI,gBAAgB,IAAI,EAAG,QAAO;AAAA,MACpC;AACA,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,MACL,UAAgB;AACd,cAAM,WAAW,WAAW,eAAe;AAC3C,iBAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACxC,gBAAM,UAAU,SAAS,CAAC;AAC1B,cAAI,YAAY,UAAa,QAAQ,SAAS,OAAQ;AACtD,cAAI,CAAC,aAAa,OAAO,EAAG;AAC5B,cACEA,yBAAwB,SAAS,IAAI,CAAC,GAAG,OAAO,KAChDA,yBAAwB,SAAS,SAAS,IAAI,CAAC,CAAC,GAChD;AACA;AAAA,UACF;AACA,gBAAM,OAAO,QAAQ,MAAM,QAAQ,QAAQ,EAAE,EAAE,KAAK;AACpD,cAAI,KAAK,WAAW,KAAK,KAAK,SAAS,GAAG,EAAG;AAC7C,cAAID,cAAa,KAAK,IAAI,KAAK,SAAS,KAAK,IAAI,KAAK,aAAa,KAAK,IAAI,EAAG;AAC/E,cAAI,oBAAoB,KAAK,IAAI,KAAK,YAAY,IAAI,EAAG;AACzD,cAAI,YAAY,KAAK,IAAI,KAAK,WAAW,KAAK,IAAI,KAAK,YAAY,KAAK,IAAI,EAAG;AAC/E,cAAI,iBAAiB,KAAK,IAAI,EAAG;AACjC,cAAI,KAAK,MAAM,KAAK,EAAE,SAAS,UAAW;AAE1C,gBAAM,SAAS,cAAc,IAAI;AACjC,cAAI,OAAO,SAAS,mBAAoB;AAExC,gBAAM,YAAY,yBAAyB,SAAS,UAAU;AAC9D,cAAI,cAAc,KAAM;AACxB,cAAI,sBAAsB,MAAM,SAAS,EAAG;AAE5C,gBAAM,OAAO,MAAM,QAAQ,IAAI,MAAM,IAAI,KAAK;AAC9C,cAAI,CAAC,eAAe,KAAK,IAAI,EAAG;AAEhC,cAAI,kBAAkB,OAAO,EAAG;AAEhC,cAAI,SAAS,QAAQ,WAAW,IAAI,CAAC,GAAG;AACtC,oBAAQ,OAAO,EAAE,MAAM,SAAS,WAAW,oBAAoB,CAAC;AAAA,UAClE;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;ACxLD,IAAAE,iBAA2C;AAS3C,IAAM,YAAY;AAClB,IAAMC,WAAU;AAIhB,IAAM,aAAkC,oBAAI,IAAI;AAAA,EAC9C;AAAA,EAAS;AAAA,EAAc;AAAA,EAAS;AAAA,EAAO;AAAA,EAAQ;AAAA,EAAM;AAAA,EAAQ;AAAA,EAAS;AAAA,EACtE;AAAA,EAAO;AAAA,EAAM;AAAA,EAAS;AAAA,EAAK;AAAA,EAAM;AAAA,EAAO;AAAA,EAAK;AAAA,EAAM;AAAA,EAAgB;AAAA,EACnE;AAAA,EAAQ;AAAA,EAAU;AAAA,EAAW;AAAA,EAAM;AAAA,EAAO;AAAA,EAAW;AAAA,EAAM;AAAA,EAAW;AAAA,EACtE;AAAA,EAAK;AAAA,EAAO;AAAA,EAAU;AAAA,EAAW;AAAA,EAAQ;AAAA,EAAS;AACpD,CAAC;AAED,IAAMC,aAAiC,oBAAI,IAAI;AAAA,EAC7C;AAAA,EAAK;AAAA,EAAM;AAAA,EAAO;AAAA,EAAO;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAO;AAAA,EAAM;AAAA,EAAM;AAAA,EACpE;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAQ;AAAA,EAAO;AAAA,EAAQ;AAAA,EAAM;AACjD,CAAC;AAED,IAAMC,gBACJ;AAEF,SAAS,UAAU,MAA2B;AAC5C,SAAO,IAAI,IAAI,KAAK,MAAM,SAAS,KAAK,CAAC,CAAC;AAC5C;AAEA,SAAS,cAAc,MAAc,MAAuB;AAC1D,MAAI,KAAK,WAAW,KAAKA,cAAa,KAAK,IAAI,KAAK,qBAAqB,IAAI,EAAG,QAAO;AACvF,QAAM,cAAc,UAAU,IAAI;AAClC,MAAI,YAAY,SAAS,EAAG,QAAO;AACnC,QAAM,SAAS,KAAK,MAAMF,QAAO,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,KAAK,YAAY,CAAC;AAC1E,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,QAAM,iBAAiB,UAAU,IAAI;AACrC,MAAI,eAAe,SAAS,EAAG,QAAO;AACtC,aAAW,UAAU,gBAAgB;AACnC,QAAI,CAAC,YAAY,IAAI,MAAM,EAAG,QAAO;AAAA,EACvC;AACA,QAAM,cAAc,WAAW,IAAI;AACnC,QAAM,QAAQ,oBAAI,IAAY;AAC9B,aAAW,SAAS,YAAa,OAAM,IAAI,KAAK,KAAK,CAAC;AACtD,SAAO,MAAM;AAAA,IACX,CAAC,SACCC,WAAU,IAAI,IAAI,KAClB,WAAW,IAAI,IAAI,KACnB,eAAe,IAAI,IAAI,KACvB,YAAY,IAAI,IAAI,KACpB,MAAM,IAAI,KAAK,IAAI,CAAC;AAAA,EACxB;AACF;AAEA,IAAO,mCAAQ,2BAAY;AAAA,EACzB,CAAC,SACC,gFAAgF,IAAI;AACxF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,UAAU;AAAA,MACR,eACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC;AAAA,EACjB,OAAO,SAAS;AACd,QAAI,gBAAgB,QAAQ,UAAU,QAAQ,WAAW,IAAI,GAAG;AAC9D,aAAO,CAAC;AAAA,IACV;AACA,UAAM,aAAa,QAAQ;AAE3B,aAAS,WAAW,SAAoC;AACtD,YAAM,SAAS,WAAW,eAAe,SAAS,EAAE,iBAAiB,MAAM,CAAC;AAC5E,aAAO,WAAW,QAAQ,OAAO,IAAI,IAAI,SAAS,QAAQ,IAAI,MAAM;AAAA,IACtE;AAEA,WAAO;AAAA,MACL,UAAgB;AACd,mBAAW,WAAW,WAAW,eAAe,GAAG;AACjD,cAAI,CAAC,WAAW,OAAO,EAAG;AAC1B,gBAAM,OAAO,WAAW,MAAM,QAAQ,IAAI,MAAM,OAAO,CAAC,KAAK;AAC7D,gBAAM,OAAO,KAAK,MAAM,GAAG,QAAQ,IAAI,MAAM,MAAM;AACnD,gBAAM,OAAO,QAAQ,MAAM,QAAQ,QAAQ,EAAE,EAAE,QAAQ,QAAQ,EAAE,EAAE,KAAK;AACxE,cAAI,cAAc,MAAM,IAAI,GAAG;AAC7B,oBAAQ,OAAO,EAAE,MAAM,SAAS,WAAW,gBAAgB,CAAC;AAAA,UAC9D;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;AC3ED,IAAAE,iBAA2D;AAQ3D,IAAM,oBAAyC,oBAAI,IAAI,CAAC,QAAQ,WAAW,eAAe,CAAC;AAG3F,IAAM,oBAAyC,oBAAI,IAAI;AAAA,EACrD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGD,IAAM,wBAAwB;AAG9B,IAAM,gBAAqC,oBAAI,IAAI,CAAC,KAAK,GAAG,CAAC;AAQ7D,SAAS,UAAU,MAA8B;AAC/C,UAAQ,KAAK,MAAM;AAAA,IACjB,KAAK,8BAAe;AAClB,aAAO;AAAA,IACT,KAAK,8BAAe;AAClB,aAAO,KAAK,YAAY,WAAW;AAAA,IACrC,KAAK,8BAAe;AAClB,aAAO,cAAc,IAAI,KAAK,QAAQ,KAAK,UAAU,KAAK,QAAQ;AAAA,IACpE,KAAK,8BAAe;AAClB,aAAO,KAAK,SAAS,MAAM,CAAC,YAAY,YAAY,QAAQ,UAAU,OAAO,CAAC;AAAA,IAChF,KAAK,8BAAe;AAClB,aAAO,KAAK,WAAW;AAAA,QACrB,CAAC,aACC,SAAS,SAAS,8BAAe,YACjC,CAAC,SAAS,YACV,UAAU,SAAS,KAAK;AAAA,MAC5B;AAAA,IACF;AACE,aAAO;AAAA,EACX;AACF;AAGA,SAAS,cAAc,QAAyD;AAC9E,QAAM,WAAW,OAAO;AACxB,MACE,SAAS,SAAS,8BAAe,kBACjC,SAAS,OAAO,SAAS,8BAAe,cACxC,SAAS,OAAO,SAAS,YACzB,SAAS,UAAU,WAAW,GAC9B;AACA,WAAO;AAAA,EACT;AACA,SAAO,SAAS,UAAU,CAAC,KAAK;AAClC;AAEA,IAAO,iCAAQ,2BAAY;AAAA,EACzB,CAAC,SACC,gFAAgF,IAAI;AACxF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,UAAU;AAAA,MACR,wBACE;AAAA,MACF,qBACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC;AAAA,EACjB,OAAO,SAAS;AACd,QAAI,CAAC,WAAW,QAAQ,QAAQ,GAAG;AACjC,aAAO,CAAC;AAAA,IACV;AAEA,UAAMC,WAAU,CAAC,SAAgC;AAC/C,YAAM,OAAO,QAAQ,WAAW,QAAQ,IAAI,EAAE,WAAW,SAAS,GAAG;AACrE,aAAO,KAAK,SAAS,wBACjB,GAAG,KAAK,MAAM,GAAG,qBAAqB,CAAC,WACvC;AAAA,IACN;AACA,WAAO;AAAA,MACL,eAAe,MAAqC;AAClD,cAAM,SAAS,KAAK;AACpB,YAAI,OAAO,SAAS,8BAAe,oBAAoB,OAAO,UAAU;AACtE;AAAA,QACF;AACA,YAAI,OAAO,SAAS,SAAS,8BAAe,YAAY;AACtD;AAAA,QACF;AACA,cAAM,UAAU,OAAO,SAAS;AAChC,cAAM,UAAU,cAAc,MAAM;AACpC,YAAI,YAAY,QAAQ,CAAC,UAAU,OAAO,GAAG;AAC3C;AAAA,QACF;AACA,YAAI,kBAAkB,IAAI,OAAO,KAAK,KAAK,UAAU,WAAW,GAAG;AACjE,kBAAQ,OAAO;AAAA,YACb;AAAA,YACA,WAAW;AAAA,YACX,MAAM,EAAE,SAASA,SAAQ,OAAO,GAAG,QAAQ;AAAA,UAC7C,CAAC;AACD;AAAA,QACF;AACA,cAAM,WAAW,KAAK,UAAU,CAAC;AACjC,YACE,CAAC,kBAAkB,IAAI,OAAO,KAC9B,KAAK,UAAU,WAAW,KAC1B,aAAa,UACb,CAAC,UAAU,QAAQ,GACnB;AACA;AAAA,QACF;AAGA,YAAI,QAAQ,WAAW,QAAQ,OAAO,MAAM,QAAQ,WAAW,QAAQ,QAAQ,GAAG;AAChF;AAAA,QACF;AACA,gBAAQ,OAAO;AAAA,UACb;AAAA,UACA,WAAW;AAAA,UACX,MAAM,EAAE,SAASA,SAAQ,OAAO,GAAG,QAAQ;AAAA,QAC7C,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;AClBD,IAAAC,iBAA2D;AAe3D,IAAM,oBACJ;AAQF,IAAM,oBACJ;AASF,IAAM,yBAAyB;AAG/B,IAAM,4BAA4B;AAGlC,IAAM,sBAAsB;AAG5B,IAAM,uBAA4C,oBAAI,IAAI,CAAC,WAAW,YAAY,cAAc,CAAC;AAWjG,IAAM,kBAAkB,CAAC,SACvB,KAAK,OAAO,SAAS,8BAAe,0BACpC,KAAK,OAAO,SAAS,8BAAe;AAGtC,IAAM,gBAAgB,CAAC,SACrB,KAAK,SAAS,8BAAe,aACzB,KAAK,OACL,KAAK,SAAS,8BAAe,kBAC3B,GAAG,cAAc,KAAK,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,KAC9C;AAER,IAAM,oBAAoB,CAAC,cAAuD;AAChF,MAAI,SAAwB;AAC5B,MAAI,OAAO,SAAS,8BAAe,oBAAqB,UAAS,OAAO;AACxE,MAAI,OAAO,SAAS,8BAAe,kBAAmB,UAAS,OAAO;AACtE,MAAI,OAAO,SAAS,8BAAe,WAAY,QAAO;AAEtD,QAAM,aAAa,OAAO,gBAAgB;AAI1C,MAAI,eAAe,UAAa,WAAW,SAAS,8BAAe,iBAAiB;AAClF,WAAO;AAAA,EACT;AACA,QAAM,EAAE,SAAS,IAAI;AACrB,QAAM,YACJ,SAAS,SAAS,8BAAe,aAC7B,SAAS,OACT,SAAS,SAAS,8BAAe,kBAC/B,SAAS,MAAM,OACf;AACR,MAAI,cAAc,KAAM,QAAO;AAC/B,SAAO,EAAE,MAAM,OAAO,MAAM,UAAU,WAAW,SAAS,cAAc,QAAQ,EAAE;AACpF;AASA,IAAM,kBAAkB,CAAC,SAAsD;AAC7E,QAAM,OAAO,KAAK,MAAM;AACxB,QAAM,aAAa,oBAAI,IAAY;AACnC,MAAI,oBAAoB;AAExB,MAAI,SAAS,QAAQ,SAAS,QAAW;AACvC,eAAW,aAAa,KAAK,MAAM;AACjC,UAAI,UAAU,SAAS,8BAAe,oBAAqB;AAC3D,YAAM,aAAa,UAAU;AAC7B,UACE,WAAW,SAAS,8BAAe,wBACnC,WAAW,aAAa,OACxB,WAAW,KAAK,SAAS,8BAAe,oBACxC,WAAW,KAAK,OAAO,SAAS,8BAAe,gBAC/C;AACA;AAAA,MACF;AACA,YAAM,SAAS,WAAW;AAC1B,UAAI,OAAO,SAAS,8BAAe,eAAe;AAChD,6BAAqB;AAAA,MACvB,WAAW,OAAO,SAAS,8BAAe,YAAY;AAEpD,mBAAW,IAAI,OAAO,IAAI;AAAA,MAC5B,WACE,OAAO,SAAS,8BAAe,oBAC/B,OAAO,OAAO,SAAS,8BAAe,YACtC;AAEA,mBAAW,IAAI,OAAO,OAAO,IAAI;AAAA,MACnC;AAAA,IACF;AAAA,EACF;AAEA,QAAM,gBAAgC,CAAC;AACvC,aAAW,aAAa,KAAK,MAAM,QAAQ;AACzC,UAAM,YAAY,kBAAkB,SAAS;AAC7C,QAAI,cAAc,KAAM;AACxB,UAAM,SACJ,UAAU,SAAS,8BAAe,uBAAuB,WAAW,IAAI,UAAU,IAAI;AACxF,QAAI,CAAC,OAAQ;AACb,QAAI,kBAAkB,KAAK,UAAU,QAAQ,EAAG;AAChD,QAAI,kBAAkB,KAAK,UAAU,IAAI,EAAG;AAC5C,kBAAc,KAAK,SAAS;AAAA,EAC9B;AAEA,SAAO,EAAE,eAAe,kBAAkB;AAC5C;AAGA,IAAM,aAAa,CAAC,MAAqB,UAAqD;AAC5F,MAAI,MAAM;AACV,QAAM,QAAQ,CAAC,YAAiC;AAC9C,QAAI,IAAK;AACT,QAAI,MAAM,OAAO,GAAG;AAClB,YAAM;AACN;AAAA,IACF;AACA,eAAW,OAAO,OAAO,KAAK,OAAO,GAA8B;AACjE,UAAI,QAAQ,SAAU;AACtB,YAAM,QAAQ,QAAQ,GAAG;AACzB,iBAAW,SAAU,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK,GAAiB;AACzE,YACE,UAAU,QACV,OAAO,UAAU,YACjB,OAAQ,MAA6B,SAAS,UAC9C;AACA,gBAAM,KAAsB;AAAA,QAC9B;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,QAAM,IAAI;AACV,SAAO;AACT;AAQA,IAAM,oBAAoB,CAAC,SACzB,WAAW,MAAM,CAAC,SAAS;AACzB,MAAI,KAAK,SAAS,8BAAe,gBAAgB;AAC/C,UAAM,EAAE,OAAO,IAAI;AACnB,QAAI,OAAO,SAAS,8BAAe,WAAY,QAAO,OAAO,SAAS;AACtE,WACE,OAAO,SAAS,8BAAe,oBAC/B,CAAC,OAAO,YACR,OAAO,SAAS,SAAS,8BAAe,cACxC,OAAO,SAAS,SAAS;AAAA,EAE7B;AAGA,SACE,KAAK,SAAS,8BAAe,mBAC7B,KAAK,SAAS,SAAS,8BAAe,mBACtC,qBAAqB,IAAI,KAAK,SAAS,MAAM,IAAI;AAErD,CAAC;AAGH,IAAMC,QAAO,CAAC,SAAyB,KAAK,QAAQ,eAAe,EAAE,EAAE,QAAQ,SAAS,EAAE;AAG1F,IAAM,qBAAqB,CAAC,YAAwC;AAClE,QAAM,QAAkB,CAAC;AACzB,aAAW,aAAa,QAAQ,MAAM;AACpC,UAAM,cACJ,UAAU,SAAS,8BAAe,yBAAyB,UAAU,cAAc;AACrF,QAAI,aAAa,SAAS,8BAAe,uBAAwB,OAAM,KAAK,YAAY,GAAG,IAAI;AAAA,EACjG;AACA,SAAO;AACT;AAcA,IAAM,qBAAqB,CACzB,WACA,eACA,YACY;AACZ,QAAM,CAAC,IAAI,IAAI;AACf,MAAI,cAAc,WAAW,KAAK,SAAS,OAAW,QAAO;AAC7D,MAAI,CAAC,uBAAuB,KAAK,KAAK,QAAQ,EAAG,QAAO;AACxD,MAAI,CAAC,0BAA0B,KAAK,SAAS,EAAG,QAAO;AACvD,QAAM,SAASA,MAAK,SAAS;AAC7B,SAAO,CAAC,mBAAmB,OAAO,EAAE,KAAK,CAAC,SAAS;AACjD,UAAM,QAAQA,MAAK,IAAI;AACvB,WAAO,OAAO,SAAS,KAAK,KAAK,MAAM,SAAS,MAAM;AAAA,EACxD,CAAC;AACH;AAOA,IAAM,oBAAoB,CAAC,SAAuC;AAChE,QAAM,QAAkB,CAAC;AACzB,aAAW,UAAU,KAAK,MAAM;AAC9B,QAAI,OAAO,SAAS,8BAAe,iBAAkB;AACrD,QAAI,OAAO,SAAS,YAAY,OAAO,OAAQ;AAC/C,QAAI,OAAO,kBAAkB,aAAa,OAAO,kBAAkB,YAAa;AAChF,QAAI,OAAO,IAAI,SAAS,8BAAe,kBAAmB;AAC1D,QAAI,OAAO,IAAI,SAAS,8BAAe,WAAY,OAAM,KAAK,OAAO,IAAI,IAAI;AAAA,QACxE,OAAM,KAAK,QAAG;AAAA,EACrB;AACA,SAAO;AACT;AAEA,IAAO,iDAAQ,2BAAY;AAAA,EACzB,CAAC,SACC,gFAAgF,IAAI;AACxF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,UAAU;AAAA,MACR,kBACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC;AAAA,EACjB,OAAO,SAAS;AACd,UAAM,EAAE,SAAS,IAAI;AACrB,QAAI,WAAW,QAAQ,KAAK,YAAY,QAAQ,KAAK,aAAa,QAAQ,EAAG,QAAO,CAAC;AACrF,QAAI,gBAAgB,UAAU,QAAQ,WAAW,QAAQ,CAAC,EAAG,QAAO,CAAC;AAErE,WAAO;AAAA,MACL,iBAAiB,MAAuC;AACtD,YAAI,KAAK,OAAO,KAAM;AACtB,YAAI,CAAC,gBAAgB,IAAI,EAAG;AAG5B,YAAI,KAAK,aAAa,KAAM;AAC5B,YAAI,KAAK,eAAe,KAAM;AAC9B,YAAI,KAAK,WAAW,SAAS,EAAG;AAGhC,YAAI,KAAK,WAAW,SAAS,EAAG;AAEhC,cAAM,OAAO,KAAK,KAAK,KAAK;AAAA,UAC1B,CAAC,WACC,OAAO,SAAS,8BAAe,oBAAoB,OAAO,SAAS;AAAA,QACvE;AACA,YAAI,SAAS,OAAW;AAExB,cAAM,EAAE,eAAe,kBAAkB,IAAI,gBAAgB,IAAI;AACjE,YAAI,cAAc,WAAW,EAAG;AAMhC,YAAI,oBAAoB,cAAc,OAAQ;AAE9C,YAAI,kBAAkB,KAAK,IAAI,EAAG;AAElC,YAAI,mBAAmB,KAAK,GAAG,MAAM,eAAe,QAAQ,WAAW,GAAG,EAAG;AAE7E,cAAM,UAAU,kBAAkB,KAAK,IAAI;AAC3C,YAAI,QAAQ,WAAW,EAAG;AAE1B,gBAAQ,OAAO;AAAA,UACb,MAAM,KAAK;AAAA,UACX,WAAW;AAAA,UACX,MAAM;AAAA,YACJ,MAAM,KAAK,GAAG;AAAA,YACd,MAAM,cAAc,IAAI,CAAC,MAAM,GAAG,EAAE,IAAI,KAAK,EAAE,OAAO,EAAE,EAAE,KAAK,IAAI;AAAA,YACnE,SAAS,QAAQ,KAAK,IAAI;AAAA,UAC5B;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;ACheD,IAAAC,iBAA2D;AAO3D,IAAM,mBAAwC,oBAAI,IAAI,CAAC,SAAS,eAAe,CAAC;AAEhF,SAAS,aAAa,MAA0E;AAC9F,QAAM,MAAM,KAAK;AACjB,MAAI,IAAI,SAAS,8BAAe,WAAY,QAAO,IAAI;AACvD,MAAI,IAAI,SAAS,8BAAe,QAAS,QAAO,OAAO,IAAI,KAAK;AAChE,SAAO;AACT;AAEA,SAAS,YAAY,MAAkC;AACrD,MAAI,KAAK,SAAS,8BAAe,YAAa,QAAO;AACrD,SACE,KAAK,SAAS,8BAAe,mBAC7B,KAAK,SAAS,SAAS,8BAAe,cACtC,iBAAiB,IAAI,KAAK,SAAS,IAAI;AAE3C;AAEA,SAAS,cAAc,MAAkC;AACvD,SAAO,KAAK,SAAS,8BAAe,iBAAiB,KAAK,SAAS,8BAAe;AACpF;AAEA,SAAS,oBAAoB,MAAqC;AAChE,QAAM,SAAS,KAAK,MAAM,OAAO,CAAC,WAAW,CAAC,cAAc,MAAM,CAAC;AACnE,SAAO,OAAO,SAAS,KAAK,OAAO,SAAS,KAAK,MAAM,UAAU,OAAO,MAAM,WAAW;AAC3F;AAEA,IAAO,yCAAQ,2BAAY;AAAA,EACzB,CAAC,SAAS,sEAAsE,IAAI;AACtF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,UAAU;AAAA,MACR,6BACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC;AAAA,EACjB,OAAO,SAAS;AACd,UAAM,qBAAqB,QAAQ,SAAS,WAAW,MAAM,GAAG;AAChE,QACE,WAAW,QAAQ,QAAQ,KAC3B,gBAAgB,QAAQ,UAAU,QAAQ,WAAW,IAAI,KACzD,2BAA2B,KAAK,kBAAkB,GAClD;AACA,aAAO,CAAC;AAAA,IACV;AAEA,aAAS,sBACP,MACM;AACN,YAAM,aAAa,KAAK,gBAAgB;AACxC,UAAI,eAAe,OAAW;AAC9B,UAAI,WAAW,SAAS,8BAAe,eAAe,CAAC,oBAAoB,UAAU,GAAG;AACtF;AAAA,MACF;AACA,cAAQ,OAAO;AAAA,QACb;AAAA,QACA,WAAW;AAAA,QACX,MAAM,EAAE,MAAM,aAAa,IAAI,EAAE;AAAA,MACnC,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,MACL,qBAAqB;AAAA,MACrB,oBAAoB;AAAA,MACpB,uBAAuB,MAAY;AACjC,YAAI,KAAK,eAAe,SAAS,8BAAe,YAAa;AAC7D,YAAI,CAAC,oBAAoB,KAAK,cAAc,EAAG;AAC/C,gBAAQ,OAAO;AAAA,UACb;AAAA,UACA,WAAW;AAAA,UACX,MAAM,EAAE,MAAM,KAAK,GAAG,KAAK;AAAA,QAC7B,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;AC/FD,IAAAC,iBAA2D;AAM3D,IAAMC,+BAA8B,oBAAI,IAAI,CAAC,IAAI,CAAC;AAClD,IAAM,sBAAsB,oBAAI,IAAI,CAAC,QAAQ,OAAO,OAAO,SAAS,YAAY,SAAS,CAAC;AAE1F,IAAMC,qBAA8D;AAAA,EAClE,CAAC,UAAU,OAAO;AAAA,EAClB,CAAC,YAAY,SAAS;AAAA,EACtB,CAAC,SAAS,MAAM;AAClB;AAEA,IAAMC,qBAAoB;AAE1B,IAAMC,YAAW,CAAC,aAChB,SAAS,MAAM,OAAO,EAAE,IAAI,KAAK;AAEnC,IAAMC,UAAS,CAAC,SACd,KAAK,QAAQ,kDAAkD,EAAE,EAAE,QAAQ,oBAAoB,EAAE;AAEnG,IAAMC,aAAY,CAAC,SAAyB;AAC1C,MAAI,aAAa;AACjB,aAAW,CAAC,SAAS,WAAW,KAAKJ,oBAAmB;AACtD,iBAAa,WAAW,QAAQ,SAAS,WAAW;AAAA,EACtD;AACA,SAAO,WAAW,QAAQC,oBAAmB,GAAG,EAAE,YAAY;AAChE;AAEA,IAAM,oBAAoB,CAAC,SACzB,KAAK,SAAS,8BAAe,2BAC7B,KAAK,SAAS,8BAAe;AAS/B,IAAM,oBAAoB,CAAC,SAAyE;AAClG,MAAI,cAAc;AAClB,MAAI,mBAAqF;AAEzF,aAAW,aAAa,MAAM;AAC5B,QAAI,UAAU,SAAS,8BAAe,sBAAsB;AAC1D,aAAO;AAAA,IACT;AAEA,QAAI,UAAU,SAAS,8BAAe,0BAA0B;AAC9D,qBAAe;AACf,YAAM,OAAO,UAAU;AACvB,WACG,KAAK,SAAS,8BAAe,uBAAuB,KAAK,SAAS,8BAAe,qBAClF,KAAK,OAAO,MACZ;AACA,2BAAmB,EAAE,MAAM,KAAK,GAAG,MAAM,MAAM,WAAW,WAAW,KAAK;AAAA,MAC5E,WAAW,KAAK,SAAS,8BAAe,YAAY;AAClD,2BAAmB,EAAE,MAAM,KAAK,MAAM,MAAM,WAAW,WAAW,KAAK;AAAA,MACzE;AAAA,IACF,WAAW,UAAU,SAAS,8BAAe,wBAAwB;AACnE,UAAI,UAAU,WAAW,MAAM;AAC7B,eAAO;AAAA,MACT;AACA,YAAM,OAAO,UAAU;AACvB,UAAI,SAAS,MAAM;AACjB,uBAAe,UAAU,WAAW;AACpC,YAAI,UAAU,WAAW,WAAW,KAAK,qBAAqB,MAAM;AAClE,gBAAM,OAAO,UAAU,WAAW,CAAC;AACnC,cAAI,QAAQ,KAAK,SAAS,SAAS,8BAAe,YAAY;AAC5D,+BAAmB,EAAE,MAAM,KAAK,SAAS,MAAM,MAAM,WAAW,WAAW,MAAM;AAAA,UACnF;AAAA,QACF;AAAA,MACF,WAAW,KAAK,SAAS,8BAAe,uBAAuB,KAAK,SAAS,8BAAe,kBAAkB;AAC5G,YAAI,KAAK,OAAO,MAAM;AACpB,yBAAe;AACf,cAAI,qBAAqB,MAAM;AAC7B,+BAAmB,EAAE,MAAM,KAAK,GAAG,MAAM,MAAM,WAAW,WAAW,MAAM;AAAA,UAC7E;AAAA,QACF;AAAA,MACF,WAAW,KAAK,SAAS,8BAAe,qBAAqB;AAC3D,mBAAW,cAAc,KAAK,cAAc;AAC1C,cAAI,WAAW,GAAG,SAAS,8BAAe,YAAY;AACpD,2BAAe;AACf,gBAAI,qBAAqB,QAAQ,WAAW,QAAQ,kBAAkB,WAAW,IAAI,GAAG;AACtF,iCAAmB,EAAE,MAAM,WAAW,GAAG,MAAM,MAAM,WAAW,WAAW,MAAM;AAAA,YACnF;AAAA,UACF;AAAA,QACF;AAAA,MACF,WAAW,KAAK,SAAS,8BAAe,0BAA0B,KAAK,SAAS,8BAAe,wBAAwB;AACrH,uBAAe;AACf,YAAI,qBAAqB,MAAM;AAC7B,6BAAmB,EAAE,MAAM,KAAK,GAAG,MAAM,MAAM,WAAW,WAAW,MAAM;AAAA,QAC7E;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,MAAI,qBAAqB,QAAQ,cAAc,GAAG;AAChD,WAAO;AAAA,EACT;AAEA,SAAO,EAAE,GAAG,kBAAkB,OAAO,YAAY;AACnD;AAEA,IAAO,mCAAQ,2BAAY;AAAA,EACzB,CAAC,SACC,gFAAgF,IAAI;AACxF,EAAuB;AAAA,EACrB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,IACJ;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,UAAU;AAAA,MACR,uBACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC;AAAA,EACjB,OAAO,SAAS;AACd,UAAM,OAAOC,UAAS,QAAQ,QAAQ;AAEtC,QAAI,KAAK,SAAS,OAAO,KAAK,KAAK,WAAW,QAAQ,KAAK,KAAK,WAAW,GAAG,EAAG,QAAO,CAAC;AACzF,QAAI,WAAW,QAAQ,QAAQ,KAAK,gBAAgB,QAAQ,UAAU,QAAQ,WAAW,IAAI,EAAG,QAAO,CAAC;AAExG,UAAMG,QAAOF,QAAO,IAAI;AAExB,QACEE,UAAS,UACTA,UAAS,YACTA,UAAS,aACTA,UAAS,WACTA,UAAS,eACTA,UAAS,WACTA,UAAS,YACTA,UAAS,YACTA,UAAS,eACTA,UAAS,WACTA,MAAK,WAAW,GAAG,KACnBA,MAAK,WAAW,GAAG,GACnB;AACA,aAAO,CAAC;AAAA,IACV;AAEA,WAAO;AAAA,MACL,QAAQ,MAA8B;AACpC,cAAM,UAAU,kBAAkB,KAAK,IAAI;AAC3C,YAAI,YAAY,KAAM;AACtB,YAAIN,6BAA4B,IAAI,QAAQ,IAAI,KAAK,oBAAoB,IAAI,QAAQ,IAAI,EAAG;AAE5F,cAAM,eAAeK,WAAU,QAAQ,IAAI;AAC3C,YAAIC,UAAS,aAAc;AAG3B,cAAM,MAAM,KAAK,SAAS,MAAM,IAAI,SAAS;AAE7C,gBAAQ,OAAO;AAAA,UACb,MAAM,QAAQ;AAAA,UACd,WAAW;AAAA,UACX,MAAM,EAAE,MAAAA,OAAM,MAAM,QAAQ,MAAM,UAAU,cAAc,IAAI;AAAA,QAChE,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;ACjMD,IAAAC,iBAA2D;AAI3D,IAAM,iBAAiB,oBAAI,IAAI;AAAA,EAC7B;AAAA,EAAW;AAAA,EAAW;AAAA,EAAW;AAAA,EAAW;AAAA,EAAS;AAAA,EACrD;AAAA,EAAS;AAAA,EAAU;AAAA,EAAO;AAAA,EAAW;AAAA,EAAgB;AACvD,CAAC;AAED,SAAS,YAAY,MAAoC;AACvD,MAAI,KAAK,SAAS,8BAAe,YAAY;AAC3C,WAAO,KAAK;AAAA,EACd;AACA,MAAI,KAAK,SAAS,8BAAe,oBAAoB,KAAK,SAAS,SAAS,8BAAe,YAAY;AACrG,WAAO,KAAK,SAAS;AAAA,EACvB;AACA,SAAO;AACT;AAEA,IAAO,uCAAQ,2BAAY;AAAA,EACzB,CAAC,SACC,gFAAgF,IAAI;AACxF,EAAkB;AAAA,EAChB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aAAa;AAAA,IACf;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,UAAU;AAAA,MACR,2BACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,gBAAgB,CAAC;AAAA,EACjB,OAAO,SAAS;AACd,WAAO;AAAA,MACL,iBAAiB,MAAuC;AACtD,YAAI,CAAC,KAAK,UAAU;AAChB;AAAA,QACJ;AAGA,YAAI,KAAK,SAAS,SAAS,8BAAe,WAAW,OAAO,KAAK,SAAS,UAAU,UAAU;AAC1F,gBAAM,WAAW,YAAY,KAAK,MAAM;AACxC,cAAI,CAAC,YAAY,CAAC,eAAe,IAAI,QAAQ,GAAG;AAC5C,oBAAQ,OAAO;AAAA,cACX;AAAA,cACA,WAAW;AAAA,cACX,MAAM,EAAE,KAAK,KAAK,SAAS,MAAM;AAAA,YACrC,CAAC;AAAA,UACL;AAAA,QACJ;AAAA,MACF;AAAA,MACA,eAAe,MAAqC;AAClD,cAAM,SAAS,KAAK;AACpB,YAAI,OAAO,SAAS,8BAAe,kBAAkB;AACjD,gBAAM,SAAS,OAAO,SAAS,SAAS,8BAAe,aAAa,OAAO,SAAS,OAAO;AAC3F,cAAI,WAAW,OAAO;AAClB,kBAAM,MAAM,KAAK,UAAU,CAAC;AAC5B,gBAAI,OAAO,IAAI,SAAS,8BAAe,WAAW,OAAO,IAAI,UAAU,UAAU;AAC7E,oBAAM,WAAW,YAAY,OAAO,MAAM;AAC1C,kBAAI,CAAC,YAAY,CAAC,eAAe,IAAI,QAAQ,GAAG;AAC5C,wBAAQ,OAAO;AAAA,kBACX;AAAA,kBACA,WAAW;AAAA,kBACX,MAAM,EAAE,KAAK,IAAI,MAAM;AAAA,gBAC3B,CAAC;AAAA,cACL;AAAA,YACJ;AAAA,UACJ;AAAA,QACJ;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;AxD1BD,IAAM,QAAQ;AAAA,EACZ,0BAA0B;AAAA,EAC1B,gCAAgC;AAAA,EAChC,oBAAoB;AAAA,EACpB,WAAW;AAAA,EACX,yBAAyB;AAAA,EACzB,2BAA2B;AAAA,EAC3B,qBAAqB;AAAA,EACrB,cAAc;AAAA,EACd,+BAA+B;AAAA,EAC/B,uBAAuB;AAAA,EACvB,4BAA4B;AAAA,EAC5B,6BAA6B;AAAA,EAC7B,8BAA8B;AAAA,EAC9B,iCAAiC;AAAA,EACjC,0BAA0B;AAAA,EAC1B,yBAAyB;AAAA,EACzB,iBAAiB;AAAA,EACjB,wBAAwB;AAAA,EACxB,+BAA+B;AAAA,EAC/B,yBAAyB;AAAA,EACzB,qCAAqC;AAAA,EACrC,qBAAqB;AAAA,EACrB,oBAAoB;AAAA,EACpB,kBAAkB;AAAA,EAClB,+BAA+B;AAAA,EAC/B,wBAAwB;AAAA,EACxB,4BAA4B;AAAA,EAC5B,2BAA2B;AAAA,EAC3B,yBAAyB;AAAA,EACzB,kCAAkC;AAAA,EAClC,wBAAwB;AAAA,EACxB,8BAA8B;AAAA,EAC9B,8BAA8B;AAAA,EAC9B,kBAAkB;AAAA,EAClB,yBAAyB;AAAA,EACzB,uCAAuC;AAAA,EACvC,qCAAqC;AAAA,EACrC,kBAAkB;AAAA,EAClB,gCAAgC;AAAA,EAChC,mCAAmC;AAAA,EACnC,sBAAsB;AAAA,EACtB,gCAAgC;AAAA,EAChC,4BAA4B;AAAA,EAC5B,uBAAuB;AAAA,EACvB,4BAA4B;AAAA,EAC5B,0BAA0B;AAAA,EAC1B,0CAA0C;AAAA,EAC1C,kCAAkC;AAAA,EAClC,gCAAgC;AAClC;AAEA,IAAM,SAAS;AAAA,EACb,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,SAAS;AAAA,EACX;AAAA,EACA;AAAA,EACA,SAAS;AAAA,IACP,aAAa;AAAA,MACX,SAAS,CAAC,OAAO;AAAA,MACjB,OAAO;AAAA,QACL,+BAA+B;AAAA,QAC/B,8BAA8B;AAAA,QAC9B,qCAAqC;AAAA,QACrC,gCAAgC;AAAA,QAChC,sCAAsC;AAAA,QACtC,+BAA+B;AAAA,QAC/B,mCAAmC;AAAA,QACnC,uCAAuC;AAAA;AAAA,QAEvC,6BAA6B;AAAA,QAC7B,qCAAqC;AAAA,QACrC,2BAA2B;AAAA,QAC3B,+BAA+B;AAAA,QAC/B,iCAAiC;AAAA,QACjC,kCAAkC;AAAA,QAClC,oCAAoC;AAAA,QACpC,0BAA0B;AAAA;AAAA,QAE1B,gCAAgC;AAAA,UAC9B;AAAA,UACA,EAAE,uBAAuB,KAAK;AAAA,QAChC;AAAA;AAAA,QAEA,2BAA2B;AAAA,QAC3B,2CAA2C;AAAA,QAC3C,0BAA0B;AAAA,QAC1B,wBAAwB;AAAA,QACxB,8BAA8B;AAAA,QAC9B,kCAAkC;AAAA,QAClC,qCAAqC;AAAA;AAAA,QAErC,+BAA+B;AAAA,QAC/B,iCAAiC;AAAA,QACjC,wCAAwC;AAAA;AAAA;AAAA;AAAA;AAAA,QAKxC,6CAA6C;AAAA,QAC7C,2CAA2C;AAAA,QAC3C,8BAA8B;AAAA,QAC9B,wBAAwB;AAAA,QACxB,+BAA+B;AAAA,QAC/B,oCAAoC;AAAA,QACpC,oCAAoC;AAAA;AAAA,QAEpC,wBAAwB;AAAA;AAAA;AAAA,QAGxB,4BAA4B;AAAA;AAAA;AAAA;AAAA,QAI5B,sCAAsC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAStC,6BAA6B;AAAA,QAC7B,kCAAkC;AAAA,QAClC,kCAAkC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAQlC,gCAAgC;AAAA;AAAA;AAAA;AAAA;AAAA,QAKhC,gDAAgD;AAAA,QAChD,wCAAwC;AAAA,QACxC,sCAAsC;AAAA,MACxC;AAAA,IACF;AAAA,IACA,QAAQ;AAAA,MACN,SAAS,CAAC,OAAO;AAAA,MACjB,OAAO;AAAA,QACL,+BAA+B;AAAA,QAC/B,8BAA8B;AAAA,QAC9B,qCAAqC;AAAA,QACrC,gCAAgC;AAAA,QAChC,oBAAoB;AAAA,QACpB,uBAAuB;AAAA,QACvB,iBAAiB;AAAA,QACjB,sCAAsC;AAAA,QACtC,+BAA+B;AAAA,QAC/B,mCAAmC;AAAA,QACnC,uCAAuC;AAAA;AAAA,QAEvC,6BAA6B;AAAA,QAC7B,qCAAqC;AAAA,QACrC,2BAA2B;AAAA,QAC3B,+BAA+B;AAAA,QAC/B,iCAAiC;AAAA,QACjC,kCAAkC;AAAA,QAClC,oCAAoC;AAAA,QACpC,0BAA0B;AAAA;AAAA;AAAA,QAG1B,gCAAgC;AAAA,UAC9B;AAAA,UACA,EAAE,uBAAuB,KAAK;AAAA,QAChC;AAAA;AAAA,QAEA,2BAA2B;AAAA,QAC3B,2CAA2C;AAAA,QAC3C,0BAA0B;AAAA,QAC1B,wBAAwB;AAAA,QACxB,8BAA8B;AAAA,QAC9B,kCAAkC;AAAA;AAAA,QAElC,qCAAqC;AAAA;AAAA,QAErC,+BAA+B;AAAA,QAC/B,iCAAiC;AAAA,QACjC,wCAAwC;AAAA;AAAA,QAExC,6CAA6C;AAAA,QAC7C,2CAA2C;AAAA,QAC3C,8BAA8B;AAAA,QAC9B,wBAAwB;AAAA,QACxB,+BAA+B;AAAA,QAC/B,oCAAoC;AAAA;AAAA;AAAA;AAAA,QAIpC,oCAAoC;AAAA,QACpC,wBAAwB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAOxB,sCAAsC;AAAA,QACtC,yCAAyC;AAAA;AAAA,QAEzC,4BAA4B;AAAA,QAC5B,sCAAsC;AAAA;AAAA;AAAA,QAGtC,6BAA6B;AAAA,QAC7B,kCAAkC;AAAA,QAClC,kCAAkC;AAAA;AAAA,QAElC,gCAAgC;AAAA;AAAA;AAAA;AAAA,QAIhC,gDAAgD;AAAA,QAChD,wCAAwC;AAAA,QACxC,sCAAsC;AAAA,MACxC;AAAA,IACF;AAAA,EACF;AACF;AAEA,IAAO,gBAAQ;","names":["import_utils","import_utils","import_utils","import_utils","import_utils","import_utils","propName","import_utils","import_utils","DEFAULT_IGNORE_PATTERNS","import_utils","import_utils","import_utils","isNode","calleeName","import_utils","import_utils","import_utils","import_utils","ASSERTION_CALLEE_RE","findVariable","import_utils","import_utils","propName","keyName","import_utils","import_utils","import_utils","import_utils","import_utils","isNode","calleeName","import_utils","import_utils","matchesAnyPattern","isGlobalFetchCall","import_utils","import_utils","isNode","unwrap","import_utils","propertyKeyName","keyName","import_utils","import_utils","import_utils","kebabCase","stem","import_utils","import_utils","import_utils","import_utils","import_utils","import_utils","FUNCTION_TYPES","import_utils","import_utils","import_utils","import_utils","import_utils","DEFAULT_METHODS","compile","import_utils","ts","IGNORE_PATTERNS","isIgnoredFile","unwrap","import_utils","IGNORE_PATTERNS","FUNCTION_TYPES","isIgnoredFile","isTestFile","unwrap","enclosingFunction","import_utils","DIRECTIVE_RE","STOPWORDS","WORD_RE","import_utils","DIRECTIVE_RE","areAdjacentLineComments","import_utils","WORD_RE","STOPWORDS","DIRECTIVE_RE","import_utils","preview","import_utils","stem","import_utils","import_utils","CONVENTIONAL_BUCKET_EXPORTS","ACRONYM_OVERRIDES","CAMEL_BOUNDARY_RE","basename","stemOf","kebabCase","stem","import_utils"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/rules/ban-loose-type-guards-in-tests.ts","../src/rules/_paths.ts","../src/rules/enforce-file-structure.ts","../src/rules/no-client-side-data-fetching.ts","../src/rules/no-comment-cruft.ts","../src/rules/_comments.ts","../src/rules/no-enum.ts","../src/rules/no-insecure-random-id.ts","../src/rules/no-json-stringify-error.ts","../src/rules/no-log-only-catch.ts","../src/rules/_logging.ts","../src/rules/no-raw-env.ts","../src/rules/no-sentinel-return-on-catch.ts","../src/rules/no-string-concat-in-loop.ts","../src/rules/no-unnecessary-use-client.ts","../src/rules/prefer-discriminated-union.ts","../src/rules/prefer-schema-for-api-payload.ts","../src/rules/prefer-semantic-colors.ts","../src/rules/_tailwind.ts","../src/rules/prefer-server-actions.ts","../src/rules/require-assert-never.ts","../src/rules/require-zod-form-validation.ts","../src/rules/_zod.ts","../src/rules/zod-naming-convention.ts","../src/rules/no-cors-wildcard-with-credentials.ts","../src/rules/no-silent-promise-catch.ts","../src/rules/require-fetch-timeout.ts","../src/rules/no-fat-try-blocks.ts","../src/rules/no-secret-in-log.ts","../src/rules/_secret_names.ts","../src/rules/no-unsafe-mock-casting.ts","../src/rules/prefer-string-literal-union.ts","../src/rules/prefer-zod-enum.ts","../src/rules/no-offset-pagination.ts","../src/rules/_sql.ts","../src/rules/no-positional-tuple-return.ts","../src/rules/no-repeated-string-literal.ts","../src/rules/no-select-star.ts","../src/rules/no-sleep-in-test-body.ts","../src/rules/no-conditional-in-test.ts","../src/rules/prefer-constant-time-secret-compare.ts","../src/rules/store-insert-requires-on-conflict.ts","../src/rules/no-dynamic-sql.ts","../src/rules/no-raw-fetch-outside-clients.ts","../src/rules/no-storage-in-stateless-modules.ts","../src/rules/no-zod-native-enum.ts","../src/rules/prefer-module-level-constant.ts","../src/rules/jsdoc-restates-signature.ts","../src/rules/no-restated-comment.ts","../src/rules/trailing-value-narration.ts","../src/rules/no-tautological-expect.ts","../src/rules/require-interface-for-injected-service.ts","../src/rules/prefer-non-nullable-collection.ts","../src/rules/primary-export-file-name.ts","../src/rules/no-implicit-attribute-access.ts","../src/rules/strict-test-assertions.ts","../src/rules/no-async-callback-in-waitfor.ts","../src/rules/prefer-setup-file-mocks.ts"],"sourcesContent":["import banLooseTypeGuardsInTests from \"./rules/ban-loose-type-guards-in-tests.js\";\nimport enforceFileStructure from \"./rules/enforce-file-structure.js\";\nimport noClientSideDataFetching from \"./rules/no-client-side-data-fetching.js\";\nimport noCommentCruft from \"./rules/no-comment-cruft.js\";\nimport noEnum from \"./rules/no-enum.js\";\nimport noInsecureRandomId from \"./rules/no-insecure-random-id.js\";\nimport noJsonStringifyError from \"./rules/no-json-stringify-error.js\";\nimport noLogOnlyCatch from \"./rules/no-log-only-catch.js\";\nimport noRawEnv from \"./rules/no-raw-env.js\";\nimport noSentinelReturnOnCatch from \"./rules/no-sentinel-return-on-catch.js\";\nimport noStringConcatInLoop from \"./rules/no-string-concat-in-loop.js\";\nimport noUnnecessaryUseClient from \"./rules/no-unnecessary-use-client.js\";\nimport preferDiscriminatedUnion from \"./rules/prefer-discriminated-union.js\";\nimport preferSchemaForApiPayload from \"./rules/prefer-schema-for-api-payload.js\";\nimport preferSemanticColors from \"./rules/prefer-semantic-colors.js\";\nimport preferServerActions from \"./rules/prefer-server-actions.js\";\nimport requireAssertNever from \"./rules/require-assert-never.js\";\nimport requireZodFormValidation from \"./rules/require-zod-form-validation.js\";\nimport zodNamingConvention from \"./rules/zod-naming-convention.js\";\nimport noCorsWildcardWithCredentials from \"./rules/no-cors-wildcard-with-credentials.js\";\nimport noSilentPromiseCatch from \"./rules/no-silent-promise-catch.js\";\nimport requireFetchTimeout from \"./rules/require-fetch-timeout.js\";\nimport noFatTryBlocks from \"./rules/no-fat-try-blocks.js\";\nimport noSecretInLog from \"./rules/no-secret-in-log.js\";\nimport noUnsafeMockCasting from \"./rules/no-unsafe-mock-casting.js\";\nimport preferStringLiteralUnion from \"./rules/prefer-string-literal-union.js\";\nimport preferZodEnum from \"./rules/prefer-zod-enum.js\";\nimport noOffsetPagination from \"./rules/no-offset-pagination.js\";\nimport noPositionalTupleReturn from \"./rules/no-positional-tuple-return.js\";\nimport noRepeatedStringLiteral from \"./rules/no-repeated-string-literal.js\";\nimport noSelectStar from \"./rules/no-select-star.js\";\nimport noSleepInTestBody from \"./rules/no-sleep-in-test-body.js\";\nimport noConditionalInTest from \"./rules/no-conditional-in-test.js\";\nimport preferConstantTimeSecretCompare from \"./rules/prefer-constant-time-secret-compare.js\";\nimport storeInsertRequiresOnConflict from \"./rules/store-insert-requires-on-conflict.js\";\nimport noDynamicSql from \"./rules/no-dynamic-sql.js\";\nimport noRawFetchOutsideClients from \"./rules/no-raw-fetch-outside-clients.js\";\nimport noStorageInStatelessModules from \"./rules/no-storage-in-stateless-modules.js\";\nimport noZodNativeEnum from \"./rules/no-zod-native-enum.js\";\nimport preferModuleLevelConstant from \"./rules/prefer-module-level-constant.js\";\nimport jsdocRestatesSignature from \"./rules/jsdoc-restates-signature.js\";\nimport noRestatedComment from \"./rules/no-restated-comment.js\";\nimport trailingValueNarration from \"./rules/trailing-value-narration.js\";\nimport noTautologicalExpect from \"./rules/no-tautological-expect.js\";\nimport requireInterfaceForInjectedService from \"./rules/require-interface-for-injected-service.js\";\nimport preferNonNullableCollection from \"./rules/prefer-non-nullable-collection.js\";\nimport primaryExportFileName from \"./rules/primary-export-file-name.js\";\nimport noImplicitAttributeAccess from \"./rules/no-implicit-attribute-access.js\";\nimport strictTestAssertions from \"./rules/strict-test-assertions.js\";\nimport noAsyncCallbackInWaitFor from \"./rules/no-async-callback-in-waitfor.js\";\n\nimport preferSetupFileMocks from \"./rules/prefer-setup-file-mocks.js\";\n\nconst rules = {\n \"ban-loose-type-guards-in-tests\": banLooseTypeGuardsInTests,\n \"enforce-file-structure\": enforceFileStructure,\n \"no-client-side-data-fetching\": noClientSideDataFetching,\n \"no-comment-cruft\": noCommentCruft,\n \"no-enum\": noEnum,\n \"no-insecure-random-id\": noInsecureRandomId,\n \"no-json-stringify-error\": noJsonStringifyError,\n \"no-log-only-catch\": noLogOnlyCatch,\n \"no-raw-env\": noRawEnv,\n \"no-sentinel-return-on-catch\": noSentinelReturnOnCatch,\n \"no-string-concat-in-loop\": noStringConcatInLoop,\n \"no-unnecessary-use-client\": noUnnecessaryUseClient,\n \"prefer-discriminated-union\": preferDiscriminatedUnion,\n \"prefer-schema-for-api-payload\": preferSchemaForApiPayload,\n \"prefer-semantic-colors\": preferSemanticColors,\n \"prefer-server-actions\": preferServerActions,\n \"require-assert-never\": requireAssertNever,\n \"require-zod-form-validation\": requireZodFormValidation,\n \"zod-naming-convention\": zodNamingConvention,\n \"no-cors-wildcard-with-credentials\": noCorsWildcardWithCredentials,\n \"no-fat-try-blocks\": noFatTryBlocks,\n \"no-secret-in-log\": noSecretInLog,\n \"no-unsafe-mock-casting\": noUnsafeMockCasting,\n \"prefer-string-literal-union\": preferStringLiteralUnion,\n \"prefer-zod-enum\": preferZodEnum,\n \"primary-export-file-name\": primaryExportFileName,\n \"no-silent-promise-catch\": noSilentPromiseCatch,\n \"require-fetch-timeout\": requireFetchTimeout,\n \"no-offset-pagination\": noOffsetPagination,\n \"no-positional-tuple-return\": noPositionalTupleReturn,\n \"no-repeated-string-literal\": noRepeatedStringLiteral,\n \"no-select-star\": noSelectStar,\n \"no-sleep-in-test-body\": noSleepInTestBody,\n \"no-conditional-in-test\": noConditionalInTest,\n \"prefer-constant-time-secret-compare\": preferConstantTimeSecretCompare,\n \"store-insert-requires-on-conflict\": storeInsertRequiresOnConflict,\n \"no-dynamic-sql\": noDynamicSql,\n \"no-raw-fetch-outside-clients\": noRawFetchOutsideClients,\n \"no-storage-in-stateless-modules\": noStorageInStatelessModules,\n \"no-zod-native-enum\": noZodNativeEnum,\n \"prefer-module-level-constant\": preferModuleLevelConstant,\n \"jsdoc-restates-signature\": jsdocRestatesSignature,\n \"no-restated-comment\": noRestatedComment,\n \"trailing-value-narration\": trailingValueNarration,\n \"no-tautological-expect\": noTautologicalExpect,\n \"require-interface-for-injected-service\": requireInterfaceForInjectedService,\n \"strict-test-assertions\": strictTestAssertions,\n \"prefer-non-nullable-collection\": preferNonNullableCollection,\n \"no-implicit-attribute-access\": noImplicitAttributeAccess,\n \"no-async-callback-in-waitfor\": noAsyncCallbackInWaitFor,\n \"prefer-setup-file-mocks\": preferSetupFileMocks,\n};\n\nconst plugin = {\n meta: {\n name: \"@sarj/eslint-plugin\",\n version: \"3.0.0\",\n },\n rules,\n configs: {\n recommended: {\n plugins: [\"@sarj\"],\n rules: {\n \"@sarj/zod-naming-convention\": \"warn\",\n \"@sarj/require-assert-never\": \"error\",\n \"@sarj/require-zod-form-validation\": \"error\",\n \"@sarj/ban-loose-type-guards-in-tests\": \"error\",\n \"@sarj/enforce-file-structure\": \"warn\",\n \"@sarj/no-client-side-data-fetching\": \"warn\",\n \"@sarj/prefer-server-actions\": \"warn\",\n \"@sarj/no-unnecessary-use-client\": \"warn\",\n \"@sarj/prefer-schema-for-api-payload\": \"warn\",\n // Distilled from sarj-audit skills — warn in recommended, error in strict.\n \"@sarj/no-sentinel-return-on-catch\": \"warn\",\n \"@sarj/no-log-only-catch\": \"warn\",\n \"@sarj/no-insecure-random-id\": \"warn\",\n \"@sarj/no-json-stringify-error\": \"warn\",\n \"@sarj/no-string-concat-in-loop\": \"warn\",\n \"@sarj/prefer-discriminated-union\": \"warn\",\n \"@sarj/no-comment-cruft\": \"warn\",\n // Frontend / styling — distilled from frontend PR-review mining.\n \"@sarj/prefer-semantic-colors\": [\n \"warn\",\n { requireSemanticTokens: true },\n ],\n // Ported from sarj-python-lint (SARJ), corpus-validated FP~0.\n \"@sarj/no-fat-try-blocks\": \"warn\",\n \"@sarj/no-cors-wildcard-with-credentials\": \"warn\",\n \"@sarj/no-secret-in-log\": \"warn\",\n \"@sarj/no-unsafe-mock-casting\": \"warn\",\n \"@sarj/primary-export-file-name\": \"warn\",\n \"@sarj/prefer-string-literal-union\": \"warn\",\n \"@sarj/prefer-zod-enum\": \"warn\",\n // Mined from 2y of PR review feedback + 5-repo code-smell audit (2026-07).\n \"@sarj/require-fetch-timeout\": \"warn\",\n \"@sarj/no-silent-promise-catch\": \"warn\",\n // Second SARJ port wave — the TS/Python parity gap. Each targets a\n // defect class seen in production Workers code: timing-leaky secret\n // compares, non-idempotent store writes under queue redelivery,\n // O(N) pagination, implicit row contracts, flaky timed tests.\n \"@sarj/prefer-constant-time-secret-compare\": \"error\",\n \"@sarj/store-insert-requires-on-conflict\": \"warn\",\n \"@sarj/no-offset-pagination\": \"warn\",\n \"@sarj/no-select-star\": \"warn\",\n \"@sarj/no-sleep-in-test-body\": \"warn\",\n \"@sarj/no-conditional-in-test\": \"warn\",\n \"@sarj/no-repeated-string-literal\": \"warn\",\n \"@sarj/no-positional-tuple-return\": \"warn\",\n // Injection guard — low FP, applies to any repo touching SQL.\n \"@sarj/no-dynamic-sql\": \"warn\",\n // Mined from two years of PR review (SARJ-928). Schema-layer sibling of\n // `no-enum`; autofixable for inline string-literal objects.\n \"@sarj/no-zod-native-enum\": \"warn\",\n // Mined from two years of PR review — the single most frequent uncovered\n // theme (~37 PRs). Measured 17 hits / 1085 real TS files, all true\n // positives, so it is safe to run everywhere.\n \"@sarj/prefer-module-level-constant\": \"warn\",\n // Anti-comment-verbosity family (2026-07), from a 37,918-comment,\n // nine-repo measurement study. Each is a deletion-class finding, so each\n // was validated against pydantic / trio / attrs as well as the maintained\n // repos: `no-restated-comment` 0 hits in bulbul and 4 in the three famous\n // corpora combined; `trailing-value-narration` 18 hits, 18 true\n // positives; `jsdoc-restates-signature` 36 hits, 0 measured false\n // positives, and it offers a suggestion rather than a `--fix` because a\n // wrong deletion is silent information loss.\n \"@sarj/no-restated-comment\": \"warn\",\n \"@sarj/jsdoc-restates-signature\": \"warn\",\n \"@sarj/trailing-value-narration\": \"warn\",\n // The TS half of SARJ057 (2026-07). Python has caught the\n // assertion-FREE test since 0.15.0 (SARJ043) and had no TS\n // counterpart, which is how `expect(true).toBe(true); // placeholder`\n // survived in internal-automations: the file HAS an assertion.\n // Measured across 5,819 .ts/.tsx files (1,003 of them test files) in\n // six internal repos plus got / hono / swr / trpc: 3 hits, 3 true\n // positives, 0 false positives.\n \"@sarj/no-tautological-expect\": \"warn\",\n // Substitutability: an exported service class with injected\n // collaborators and no interface above it can only be tested by\n // mocking. 11-repo sweep: 229 exported classes, 82% already carry a\n // port, 29 fire, 28 of them true positives.\n \"@sarj/require-interface-for-injected-service\": \"warn\",\n \"@sarj/prefer-non-nullable-collection\": \"warn\",\n \"@sarj/no-implicit-attribute-access\": \"warn\",\n \"@sarj/no-async-callback-in-waitfor\": \"warn\",\n \"@sarj/prefer-setup-file-mocks\": \"warn\",\n },\n },\n strict: {\n plugins: [\"@sarj\"],\n rules: {\n \"@sarj/zod-naming-convention\": \"error\",\n \"@sarj/require-assert-never\": \"error\",\n \"@sarj/require-zod-form-validation\": \"error\",\n \"@sarj/ban-loose-type-guards-in-tests\": \"error\",\n \"@sarj/enforce-file-structure\": \"error\",\n \"@sarj/no-raw-env\": \"error\",\n \"@sarj/no-enum\": \"error\",\n \"@sarj/no-client-side-data-fetching\": \"error\",\n \"@sarj/prefer-server-actions\": \"error\",\n \"@sarj/no-unnecessary-use-client\": \"error\",\n \"@sarj/prefer-schema-for-api-payload\": \"error\",\n // Distilled from sarj-audit skills.\n \"@sarj/no-sentinel-return-on-catch\": \"error\",\n \"@sarj/no-log-only-catch\": \"error\",\n \"@sarj/no-insecure-random-id\": \"error\",\n \"@sarj/no-json-stringify-error\": \"error\",\n \"@sarj/no-string-concat-in-loop\": \"error\",\n \"@sarj/prefer-discriminated-union\": \"error\",\n \"@sarj/no-comment-cruft\": \"error\",\n // Frontend / styling — distilled from frontend PR-review mining. Stylistic,\n // no autofix → warn (rollout should prove the FP rate before raising it).\n \"@sarj/prefer-semantic-colors\": [\n \"error\",\n { requireSemanticTokens: true },\n ],\n // Ported from sarj-python-lint (SARJ), corpus-validated FP~0.\n \"@sarj/no-fat-try-blocks\": \"error\",\n \"@sarj/no-cors-wildcard-with-credentials\": \"error\",\n \"@sarj/no-secret-in-log\": \"error\",\n \"@sarj/no-unsafe-mock-casting\": \"error\",\n \"@sarj/primary-export-file-name\": \"error\",\n // Promoted to error 2026-07-25 — strict means strict (user directive).\n \"@sarj/prefer-string-literal-union\": \"error\",\n \"@sarj/prefer-zod-enum\": \"error\",\n // Mined from 2y of PR review feedback + 5-repo code-smell audit (2026-07).\n \"@sarj/require-fetch-timeout\": \"error\",\n \"@sarj/no-silent-promise-catch\": \"error\",\n // Second SARJ port wave — the TS/Python parity gap.\n \"@sarj/prefer-constant-time-secret-compare\": \"error\",\n \"@sarj/store-insert-requires-on-conflict\": \"error\",\n \"@sarj/no-offset-pagination\": \"error\",\n \"@sarj/no-select-star\": \"error\",\n \"@sarj/no-sleep-in-test-body\": \"error\",\n \"@sarj/no-conditional-in-test\": \"error\",\n \"@sarj/no-repeated-string-literal\": \"error\",\n // API-shape advice rather than a runtime defect — a corpus sweep found its\n // only hits are parser `[value, cursor]` returns, which are conventional.\n // Warn even in strict until a rollout justifies more.\n \"@sarj/no-positional-tuple-return\": \"error\",\n \"@sarj/no-dynamic-sql\": \"error\",\n // Architectural: both need per-repo config to be meaningful, so they\n // are strict-only. `no-storage-in-stateless-modules` is a no-op until\n // its `modules` option names the directories a team declared stateless;\n // `no-raw-fetch-outside-clients` defaults to the `clients/` convention\n // and takes an `allow` list for repos that lay their client layer out\n // differently.\n \"@sarj/no-raw-fetch-outside-clients\": \"error\",\n \"@sarj/no-storage-in-stateless-modules\": \"error\",\n // Mined from two years of PR review (SARJ-928).\n \"@sarj/no-zod-native-enum\": \"error\",\n \"@sarj/prefer-module-level-constant\": \"error\",\n // Anti-comment-verbosity family (2026-07) — see the `recommended` block\n // for the measured hit counts and false-positive rates.\n \"@sarj/no-restated-comment\": \"error\",\n \"@sarj/jsdoc-restates-signature\": \"error\",\n \"@sarj/trailing-value-narration\": \"error\",\n // TS half of SARJ057 — see the `recommended` block for the measurement.\n \"@sarj/no-tautological-expect\": \"error\",\n // Substitutability: the TS sibling of the Python `prefer-real-store-in-tests`\n // / `prefer-library-fake` wave. The convention already exists in the\n // corpus (175 `implements` clauses vs 29 hits), so strict enforces it.\n \"@sarj/require-interface-for-injected-service\": \"error\",\n \"@sarj/prefer-non-nullable-collection\": \"error\",\n \"@sarj/no-implicit-attribute-access\": \"error\",\n \"@sarj/no-async-callback-in-waitfor\": \"error\",\n \"@sarj/prefer-setup-file-mocks\": \"error\",\n },\n },\n },\n};\n\nexport default plugin;\nexport { rules };\n","import { ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\nimport { isTestFile } from \"./_paths.js\";\n\ntype MessageIds = \"banLooseTypeGuardsInTests\";\ntype Options = readonly [];\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"ban-loose-type-guards-in-tests\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Disallow loose type guards (`typeof` and `in`) in test files. Enforce Zod schemas or strict matchers for type validation.\",\n },\n schema: [],\n messages: {\n banLooseTypeGuardsInTests:\n \"Do not use `typeof` or `in` for type validation in tests. Use Zod schemas or strict assertion matchers instead.\",\n },\n },\n defaultOptions: [],\n create(context) {\n if (!isTestFile(context.filename)) {\n return {};\n }\n\n return {\n UnaryExpression(node: TSESTree.UnaryExpression): void {\n if (node.operator === \"typeof\") {\n context.report({ node, messageId: \"banLooseTypeGuardsInTests\" });\n }\n },\n BinaryExpression(node: TSESTree.BinaryExpression): void {\n if (node.operator === \"in\") {\n context.report({ node, messageId: \"banLooseTypeGuardsInTests\" });\n }\n },\n };\n },\n});\n","/**\n * @fileoverview Shared file-path predicates for rules that are scoped by file\n * kind rather than by syntax — a timing-attack surface only exists in\n * production auth code, fixture SQL in a test legitimately breaks the\n * store-write conventions, and a dev script legitimately skips production\n * hardening. Answering \"is this a test file?\" / \"is this a script?\" in exactly\n * one place (mirroring the Python `_paths.py` extraction) is what stops the\n * per-rule regexes from drifting apart again.\n */\n\nconst SCRIPT_FILE_RE = /([\\\\/]scripts[\\\\/])|(\\.mjs$)/;\nconst STORY_FILE_RE = /\\.stories\\.[cm]?[jt]sx?$/i;\nconst GENERATED_FILE_RE =\n /([\\\\/](?:generated|openapi-gen|graphql[\\\\/]types)[\\\\/])|(\\.gen\\.[cm]?[jt]sx?$)|(\\.generated\\.[cm]?[jt]sx?$)|(\\.d\\.[cm]?ts$)|(\\.types\\.[cm]?ts$)/;\nconst GENERATED_MARKER_RE =\n /(?:@generated\\b|generated (?:with|by)|generated (?:graphql )?types|do not edit(?: directly| manually)?)/i;\n\n/**\n * True for a test file: a `*.test.*` / `*.spec.*` basename, the `-test` /\n * `_test` suffix forms of the same, or anything under a `test/` / `tests/` /\n * `__tests__/` / `__mocks__/` / `fixtures/` directory.\n *\n * The `-test.ts` / `_test.ts` suffixes are recognised because the dot form\n * alone is not the universal convention, and missing them silently un-exempts a\n * whole repo's test tree. Measured while converging `no-repeated-string-literal`\n * with its Python twin (2026-07): react-router names every suite `*-test.ts`, and\n * ALL 5 of the rule's hits over the 2,186-file third-party corpus were in two of\n * them — `react-router/packages/react-router-dev/vite/remove-exports-test.ts`\n * (4 hits) and `route-chunks-test.ts` (1). Each is a source-transform fixture\n * (`\"export const keptExport_1 = () => {};…\"`) deliberately repeated so the\n * before/after assertions stay comparable — the exact category the test\n * exemption exists for. Python's `_paths.is_test_path` has always accepted\n * `*_test.py`; this closes the same gap on the TS side.\n */\nexport function isTestFile(filename: string): boolean {\n const normalized = filename.replaceAll(\"\\\\\", \"/\");\n const base = normalized.slice(normalized.lastIndexOf(\"/\") + 1);\n if (/[.\\-_](test|spec|e2e)\\.[cm]?[jt]sx?$/.test(base) || /\\.integration\\.[cm]?[jt]sx?$/.test(base)) {\n return true;\n }\n return /(^|\\/)(tests?|__tests__|__mocks__|fixtures|e2e|integration)\\//.test(normalized);\n}\n\nexport function isStoryFile(filename: string): boolean {\n return STORY_FILE_RE.test(filename);\n}\n\nexport function isGeneratedFile(filename: string, sourceText = \"\"): boolean {\n return GENERATED_FILE_RE.test(filename.replaceAll(\"\\\\\", \"/\")) || GENERATED_MARKER_RE.test(sourceText.slice(0, 2048));\n}\n\n/**\n * True for one-off tooling files: anything under a `scripts/` directory or a\n * `*.mjs` file. Dev scripts run interactively and die with the terminal, so\n * production-hardening rules (e.g. fetch timeouts) do not apply there.\n */\nexport function isScriptFile(filename: string): boolean {\n return SCRIPT_FILE_RE.test(filename);\n}\n","/**\n * @fileoverview Require imports to come first, then allow step-down ordering for\n * the rest of the file, and require a `use server` directive to be the first\n * statement.\n *\n * Reporting is per-DEFECT, not per-import: see the `inMisplacedRun` note in\n * `create` for the corpus measurement behind that.\n */\n\nimport { ESLintUtils, type TSESTree, AST_NODE_TYPES } from \"@typescript-eslint/utils\";\n\nimport { isGeneratedFile, isTestFile } from \"./_paths.js\";\n\ntype MessageIds = \"importsFirst\" | \"useServerDirective\";\ntype Options = readonly [];\n\ntype StatementKind = \"import\" | \"reexport\" | \"body\";\n\n/**\n * Classify a top-level statement by WHAT it introduces, not by the presence of\n * the `export` keyword.\n *\n * - `import ...` → \"import\"\n * - `export ... from`, `export * from`, → \"reexport\"\n * `export { a, b }` (local names)\n * - everything else, INCLUDING every → \"body\"\n * exported declaration/function\n *\n * Bucketing exported statements as \"body\" is the whole point: an exported\n * `interface`/`type`/`enum`/`class`/value-`const` is a declaration and an\n * exported `function` (or `export default <fn>`) is a function — both live in\n * the same body as their non-exported equivalents. That lets the dominant\n * step-down layout (public API first, private helpers below) pass instead of\n * forcing every `export`-prefixed statement into a terminal \"exports\" section.\n *\n * Re-exports are their own neutral group: a generated `_namespaces` barrel that\n * interleaves `import * as X` / `export { X }` must not be flagged, so\n * re-exports never trigger and are allowed anywhere in the file.\n */\nconst classifyStatement = (\n statement: TSESTree.ProgramStatement,\n): StatementKind => {\n switch (statement.type) {\n case AST_NODE_TYPES.ImportDeclaration:\n return \"import\";\n case AST_NODE_TYPES.ExportAllDeclaration:\n return \"reexport\";\n case AST_NODE_TYPES.ExportNamedDeclaration:\n // `export { a } from './x'` / `export { a, b }` re-export names without\n // declaring anything; only `export <decl>` introduces a body statement.\n return statement.declaration === null ? \"reexport\" : \"body\";\n default:\n return \"body\";\n }\n};\n\nconst isStringDirective = (statement: TSESTree.ProgramStatement): boolean =>\n statement.type === AST_NODE_TYPES.ExpressionStatement &&\n statement.expression.type === AST_NODE_TYPES.Literal &&\n typeof statement.expression.value === \"string\" &&\n statement.expression.value.startsWith(\"use \");\n\nconst isUseServerDirective = (\n statement: TSESTree.ProgramStatement,\n): boolean => {\n if (statement.type !== AST_NODE_TYPES.ExpressionStatement) return false;\n const expr = statement.expression;\n if (expr.type !== AST_NODE_TYPES.Literal) return false;\n return expr.value === \"use server\";\n};\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"enforce-file-structure\",\n meta: {\n type: \"suggestion\",\n docs: {\n description:\n \"Require `import` statements to come first, then allow step-down ordering (public API first, private helpers below) for the rest of the file. Exported statements are classified by WHAT they export — an exported interface is a declaration, an exported function is a function — so a public exported function followed by a private helper, or an exported interface among declarations, is allowed. Re-exports (`export { … } from`, `export *`, `export { … }`) are a neutral group, so generated namespace barrels pass. When a module contains a `use server` directive, it must be the first statement in the file.\",\n },\n schema: [],\n messages: {\n importsFirst:\n \"File structure violation: import statements must come before other declarations\",\n useServerDirective:\n \"A 'use server' directive must be the first statement in the file\",\n },\n },\n defaultOptions: [],\n create(context) {\n if (isTestFile(context.filename) || isGeneratedFile(context.filename, context.sourceCode.text)) {\n return {};\n }\n\n return {\n Program(node: TSESTree.Program): void {\n const body = node.body;\n\n const misplacedUseServer = body.find(\n (statement, index) => index > 0 && isUseServerDirective(statement),\n );\n if (misplacedUseServer !== undefined) {\n context.report({\n node: misplacedUseServer,\n messageId: \"useServerDirective\",\n });\n }\n\n let seenBody = false;\n // One misplacement is one defect. A single interleaved statement pushes\n // every later import \"after the body\", and reporting each of them turns\n // one `const nodeRequire = createRequire(...)` between two import blocks\n // into 18 messages — measured at\n // react-router/packages/react-router-dev/vite/plugin.ts:41, which alone\n // produced 18 of the 33 corpus hits. Report the head of each contiguous\n // run of misplaced imports instead, so a file with two separate\n // interleavings still gets two messages.\n let inMisplacedRun = false;\n\n for (const statement of body) {\n if (isStringDirective(statement)) continue;\n\n switch (classifyStatement(statement)) {\n case \"reexport\":\n continue;\n case \"body\":\n seenBody = true;\n inMisplacedRun = false;\n continue;\n case \"import\":\n if (seenBody && !inMisplacedRun) {\n inMisplacedRun = true;\n context.report({\n node: statement,\n messageId: \"importsFirst\",\n });\n }\n continue;\n }\n }\n },\n };\n },\n});\n","/**\n * @fileoverview Disallow data fetching inside `useEffect` / `useLayoutEffect`.\n *\n * Anti-pattern:\n *\n * useEffect(() => { fetch('/data').then(setData); }, []);\n *\n * Causes a client-side waterfall (render → effect → fetch → re-render),\n * forfeits server-side caching, and produces layout shift. In Next.js App\n * Router, prefer:\n * - a React Server Component that fetches at render time, or\n * - a Server Action invoked from a form / onClick handler, or\n * - client-side caching libraries like SWR or React Query.\n *\n * Detection covers:\n * - `fetch(...)` (default + when explicit `method: \"GET\"`)\n * - `axios.<method>(...)` for actual HTTP verbs only:\n * get / post / put / delete / patch / request / head / options\n * (i.e. NOT `axios.create` / `axios.defaults`)\n * - `ky.<method>(...)` / `superagent.<method>(...)` (same verbs)\n * - bare `axios(...)` / `ky(...)` calls (when treated as a GET)\n *\n * Analytics / telemetry endpoints whose URL has a whole path segment of\n * `track` / `log` / `ping` / `event` / ... are intentionally exempt because\n * they aren't render-blocking data fetches. (Matched per-segment, so\n * `/api/login`, `/blog`, `/api/events`, `/catalog`, `/api/shipping` are NOT\n * exempt.)\n *\n * References:\n * - https://nextjs.org/docs/app/building-your-application/data-fetching\n * - https://react.dev/reference/react/useEffect#fetching-data-with-effects\n */\n\nimport {\n AST_NODE_TYPES,\n ESLintUtils,\n type TSESTree,\n} from \"@typescript-eslint/utils\";\n\ntype MessageIds = \"noClientFetch\";\ntype Options = readonly [];\n\nconst FETCH_LIBS: ReadonlySet<string> = new Set([\"axios\", \"ky\", \"superagent\"]);\n\n// Real HTTP verbs only. Explicitly excludes `create`, `defaults`,\n// `interceptors`, `isAxiosError`, etc.\nconst HTTP_METHOD_NAMES: ReadonlySet<string> = new Set([\n \"get\",\n \"post\",\n \"put\",\n \"delete\",\n \"patch\",\n \"request\",\n \"head\",\n \"options\",\n]);\n\n// Matched against whole path SEGMENTS (split on `/` and `.`), never as raw\n// substrings — otherwise `/api/login` (\"log\"), `/blog` (\"log\"), `/api/events`\n// (\"event\"), `/catalog` (\"log\"), and `/api/shipping` (\"ping\") would be wrongly\n// exempted.\nconst ANALYTICS_SEGMENTS: ReadonlySet<string> = new Set([\n \"analytics\",\n \"telemetry\",\n \"track\",\n \"log\",\n \"ping\",\n \"beacon\",\n \"metrics\",\n \"event\",\n]);\n\nfunction isEffectHookCall(node: TSESTree.CallExpression): boolean {\n const callee = node.callee;\n\n // useEffect(...) / useLayoutEffect(...)\n if (callee.type === AST_NODE_TYPES.Identifier) {\n return callee.name === \"useEffect\" || callee.name === \"useLayoutEffect\";\n }\n\n // React.useEffect(...) / React.useLayoutEffect(...)\n if (\n callee.type === AST_NODE_TYPES.MemberExpression &&\n !callee.computed &&\n callee.object.type === AST_NODE_TYPES.Identifier &&\n callee.object.name === \"React\" &&\n callee.property.type === AST_NODE_TYPES.Identifier\n ) {\n return (\n callee.property.name === \"useEffect\" ||\n callee.property.name === \"useLayoutEffect\"\n );\n }\n\n return false;\n}\n\n/**\n * Reads the `method` property of an options object passed to `fetch` / `axios`.\n * Returns the uppercased method name, or `null` if not statically determinable.\n */\nfunction readMethodProperty(\n optionsArg: TSESTree.Node | undefined,\n): string | null {\n if (!optionsArg || optionsArg.type !== AST_NODE_TYPES.ObjectExpression) {\n return null;\n }\n for (const prop of optionsArg.properties) {\n if (prop.type !== AST_NODE_TYPES.Property) continue;\n if (prop.computed) continue;\n const key = prop.key;\n const matchesMethodKey =\n (key.type === AST_NODE_TYPES.Identifier && key.name === \"method\") ||\n (key.type === AST_NODE_TYPES.Literal && key.value === \"method\");\n if (!matchesMethodKey) continue;\n if (\n prop.value.type === AST_NODE_TYPES.Literal &&\n typeof prop.value.value === \"string\"\n ) {\n return prop.value.value.toUpperCase();\n }\n return null;\n }\n return null;\n}\n\nfunction isFetchCall(node: TSESTree.CallExpression): boolean {\n const callee = node.callee;\n\n // fetch(url, options?)\n if (\n callee.type === AST_NODE_TYPES.Identifier &&\n callee.name === \"fetch\"\n ) {\n const method = readMethodProperty(node.arguments[1]);\n if (method !== null && method !== \"GET\") {\n return false;\n }\n return true;\n }\n\n // axios.get(...), ky.post(...), superagent.delete(...), ...\n if (\n callee.type === AST_NODE_TYPES.MemberExpression &&\n !callee.computed &&\n callee.object.type === AST_NODE_TYPES.Identifier &&\n FETCH_LIBS.has(callee.object.name) &&\n callee.property.type === AST_NODE_TYPES.Identifier\n ) {\n // Only flag actual HTTP method calls — NOT `axios.create`, `axios.defaults`,\n // `axios.interceptors`, `axios.isAxiosError`, etc.\n return HTTP_METHOD_NAMES.has(callee.property.name);\n }\n\n // axios(config) / ky(config) — treat as request unless method is explicitly non-GET.\n if (\n callee.type === AST_NODE_TYPES.Identifier &&\n (callee.name === \"axios\" || callee.name === \"ky\")\n ) {\n const firstArg = node.arguments[0];\n const secondArg = node.arguments[1];\n let configArg: TSESTree.Node | undefined;\n if (firstArg?.type === AST_NODE_TYPES.ObjectExpression) {\n configArg = firstArg;\n } else if (secondArg?.type === AST_NODE_TYPES.ObjectExpression) {\n configArg = secondArg;\n }\n const method = readMethodProperty(configArg);\n if (method !== null && method !== \"GET\") {\n return false;\n }\n return true;\n }\n\n return false;\n}\n\nfunction extractUrlString(node: TSESTree.CallExpression): string {\n const firstArg = node.arguments[0];\n if (!firstArg) return \"\";\n\n if (\n firstArg.type === AST_NODE_TYPES.Literal &&\n typeof firstArg.value === \"string\"\n ) {\n return firstArg.value;\n }\n if (firstArg.type === AST_NODE_TYPES.TemplateLiteral) {\n return firstArg.quasis.map((q) => q.value.cooked).join(\"\");\n }\n if (firstArg.type === AST_NODE_TYPES.Identifier) {\n return firstArg.name;\n }\n return \"\";\n}\n\nfunction isAnalyticsCall(node: TSESTree.CallExpression): boolean {\n const url = extractUrlString(node).toLowerCase();\n if (url === \"\") return false;\n // Split into path segments and file-extension parts; exempt only when a\n // WHOLE segment is a known analytics keyword.\n return url\n .split(/[/.]/)\n .some((segment) => ANALYTICS_SEGMENTS.has(segment));\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-client-side-data-fetching\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Disallow data fetching inside `useEffect` / `useLayoutEffect`; prefer React Server Components, Server Actions, or a client-side cache (SWR / React Query).\",\n },\n schema: [],\n messages: {\n noClientFetch:\n \"Avoid direct data fetching inside useEffect / useLayoutEffect. This causes waterfalls and layout shifts. Prefer React Server Components, Server Actions, or client-side caching libraries like SWR or React Query.\",\n },\n },\n defaultOptions: [],\n create(context) {\n let effectDepth = 0;\n return {\n CallExpression(node: TSESTree.CallExpression): void {\n if (isEffectHookCall(node)) {\n effectDepth += 1;\n return;\n }\n if (effectDepth === 0) return;\n if (!isFetchCall(node)) return;\n if (isAnalyticsCall(node)) return;\n context.report({ node, messageId: \"noClientFetch\" });\n },\n \"CallExpression:exit\"(node: TSESTree.CallExpression): void {\n if (isEffectHookCall(node)) {\n effectDepth -= 1;\n }\n },\n };\n },\n});\n","/**\n * @fileoverview Flag comment cruft — commented-out code, section banners,\n * leading file-header comment preambles, and redundant narration. Code carries\n * the *what*; comments are reserved for the *why*. JSDoc (`/** ... *\\/`) is\n * never flagged, and directive comments (`eslint-`, `@ts-`, `prettier-`,\n * `biome-`, `c8`, `<reference`, `TODO`, `FIXME`) are ignored.\n *\n * `redundantNarration` covers three shapes: step markers (\"First, …\", \"Step 2:\"),\n * self-admitted meta-commentary (\"for now\", \"temporary hack\"), and a comment\n * that restates the statement directly below it (`// increment the counter`\n * above `counter += 1`). The third shape is heavily guarded — see\n * `restatesNextLine` — because the first attempt at it (PR #98) corroborated by\n * substring and produced 933 hits at a ~60% false-positive rate. Measured on\n * 7,159 local TS/TSX files: 42 hits, 2 of them wrong (a comment heading a block\n * whose first statement happened to carry every word), and ZERO hits in the\n * maintained repos — it is a preventive ratchet with no migration cost.\n *\n * `fileHeaderPreamble` requires the preamble to contain NO prose sentence. The\n * original \"4+ consecutive `//` lines before the first code\" test penalised\n * syntax rather than content: on a real 42k-LOC codebase, 11 of 15 hits were the\n * repo's BEST documentation — module headers explaining a stateless idempotency\n * substrate, one citing RFC 9562 §5.7 — which is precisely the \"brief doc\n * comment for the why\" this rule's own message asks for. Exactly one hit was\n * genuine (an ASCII banner, already covered by `sectionBanner`). What survives\n * is the content-free preamble: a stack of bare labels/fragments with nothing\n * explained. A prose header should be a JSDoc block for tooling reasons, but\n * that is a formatting preference, not cruft, and this rule does not litigate it.\n *\n * A 2026-07 sweep of 2,186 real TypeScript files (zod, TanStack Query,\n * react-router, swr, zustand) produced 821 hits and turned up four false-positive\n * classes, each now guarded at the point that produced it — see\n * `DIAGRAM_ARROW_RE` (ASCII timelines read as banners), `ENUMERATED_ITEM_RE` (a\n * numbered walkthrough read as a content-free preamble), `isInsideCommentRun` (a\n * phrase inside a prose paragraph read as a one-line narration label), and\n * `hasIllustrationLeadInAbove` (a code sample read as commented-out code because\n * its `Example:` heading was more than one line up). The bulk of the remaining\n * hits are real: zod alone carries hundreds of lines of genuinely commented-out\n * code (e.g. zod/packages/zod/src/v4/core/checks.ts:1231-1244, a whole disabled\n * `$ZodCheckTrim` implementation), and those still fire.\n *\n * A 2026-07 corpus run of the SHIPPED rule turned up two live false positives,\n * both fixed here: `REGION_RE` matched the bare word `region`, so prose opening\n * with it read as a folding marker (six sites, e.g. `// Region centroids for\n * map_pan.`), and `for now` fired on a ticket-bearing scoping note — a comment\n * naming where the decision is recorded is doing the one thing code cannot, so\n * protected-class signal S1 now exempts narration at RUN granularity. The same\n * pass added `sarj-noqa` to `DIRECTIVE_RE` (this repo's own suppression syntax\n * was missing, so a suppression comment could itself be flagged) and four\n * detectors that are all ZERO-hit on bulbul, the repo that runs this rule at\n * `error`: bare section labels, the `Helper function to …` opener, `Let's\n * <verb>`, Unicode box-drawing banners, and an ISOLATED numbered/`Phase N:`\n * marker. JSX-expression comments stay categorically exempt: `{/* Step 1:\n * Select Patient *\\/}` mirrors the literal step labels a wizard renders.\n */\n\nimport { AST_NODE_TYPES, ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\nimport { hasExternalReference, restatableStatementBelow, restatesStatementHead } from \"./_comments.js\";\nimport { isGeneratedFile } from \"./_paths.js\";\n\ntype MessageIds =\n | \"commentedOutCode\"\n | \"sectionBanner\"\n | \"fileHeaderPreamble\"\n | \"redundantNarration\"\n | \"untrackedTodo\";\ntype Options = readonly [];\n\nconst LEADING_PREAMBLE_MIN = 4;\n\n// Step-narration lead-ins (\"First, …\", \"Then, …\", \"Finally, …\", \"Step 2:\"). A\n// trailing comma/colon is required so English adverbs (\"finally the invariant\n// holds\") aren't mistaken for an enumeration marker.\nconst STEP_NARRATION_RE =\n /^(?:first(?:ly)?|second(?:ly)?|third(?:ly)?|then|next|after(?:wards| that)?|finally|lastly|now)\\s*[,:]\\s*\\S/i;\n\n// Self-admitted meta-commentary — the \"why later\", not the why. `TODO`/`FIXME`/\n// `HACK`/`XXX` are handled as directives (kept, with an owner, per convention).\nconst META_COMMENTARY_RE =\n /\\b(?:for now|keeping (?:it|this) simple|could be (?:refactored|improved|cleaned up|simplified)|refactor(?:ed|ing)? (?:later|this)|not sure (?:if|whether|why|how)|quick[- ](?:and[- ]dirty|fix)|(?:a |bit of a )?hacky|is a hack|temporary (?:solution|workaround|fix|hack)|revisit (?:this|later|below)|clean (?:this|it) up|not ideal|placeholder for now)\\b/i;\n\n\n// `sarj-noqa` is this repo's own suppression syntax (see `rule_base.py`). It was\n// missing here, so `// sarj-noqa: … — <reason>` on its own line was read as\n// prose and could itself be flagged.\nconst DIRECTIVE_RE =\n /^(eslint\\b|eslint-|sarj-noqa\\b|@ts-|prettier-ignore|prettier\\b|biome-|c8\\b|v8\\b|istanbul\\b|@type\\b|@vite|webpack|<reference|<amd|global\\b|noinspection|hack\\b|xxx\\b)/i;\n\nconst LICENSE_RE =\n /copyright|licen[cs]ed?|spdx|permission is hereby granted|all rights reserved/i;\n\n// An enumerated step in a numbered/bulleted plan: `1. get all tags`, `- read the\n// manifest`. A stack of these is an algorithm walkthrough — the \"why/how\" the\n// rule's own message asks for — even though no item ends in a full stop, which\n// is why `isProse` misses it.\nconst ENUMERATED_ITEM_RE = /^(?:\\d+[.):]|[-*•])\\s+\\S/;\nconst ENUMERATED_ITEM_MIN_WORDS = 3;\nconst ENUMERATED_PREAMBLE_MIN_ITEMS = 2;\n\nconst BANNER_FULL_RE = /^[\\s\\-=*#~_+.]{4,}$/;\n// `={4,}` not `={3,}`: `===` is TS strict-equality and appears in prose comments.\n// `[\\u2500-\\u257f]` is the Unicode box-drawing block — `────────` is the same\n// section separator as `--------`, and 34 of them were sitting in the corpus\n// under a check that only knew ASCII.\nconst BANNER_RUN_RE = /={4,}|-{4,}|#{4,}|\\*{4,}|~{4,}|[\\u2500-\\u257f]{4,}/;\n\n// A VS Code / Visual Studio folding marker: `//#region`, `// region helpers`,\n// `// endregion`. The title must be short and unpunctuated. Matching the bare\n// word alone flagged running prose that merely opens with it — six sites across\n// the corpus, the clearest being\n// demo-gateway/demos/momah-furas-anas/pipeline/matching.py:159 (\"region, sector\n// AND facility_type are HARD constraints when the investor names them — …\") and\n// its five TypeScript siblings. A marker *names* a region; a sentence discusses\n// one, and a sentence has punctuation (a full stop included — `// Region\n// centroids for map_pan.` is prose) and more than a handful of words.\nconst REGION_MARKER_RE = /^#?(?:end)?region\\b(.*)$/i;\nconst REGION_TITLE_RE = /^[\\s:\\-\\u2013\\u2014]*\\w[\\w \\-/&+]*$/;\nconst REGION_TITLE_MAX_WORDS = 5;\n\nfunction isRegionMarker(text: string): boolean {\n const match = REGION_MARKER_RE.exec(text);\n if (match === null) return false;\n const title = (match[1] ?? \"\").trim();\n if (title.length === 0) return true;\n if (!REGION_TITLE_RE.test(title)) return false;\n return title.split(/\\s+/).length <= REGION_TITLE_MAX_WORDS;\n}\n\n// A bare one-word signpost naming a region of the file (`// Types`, `// Main`,\n// `// Helpers`). It is a table of contents for a file that should have been\n// split, and it goes stale silently. 22 corpus hits, 12 of 12 sampled were true\n// positives. Closed vocabulary on purpose: a one-word comment outside this list\n// is far more likely to be a genuine label for a value.\nconst SECTION_LABEL_WORDS: ReadonlySet<string> = new Set([\n \"actions\", \"components\", \"config\", \"configuration\", \"constant\", \"constants\",\n \"enums\", \"exports\", \"fixtures\", \"getters\", \"globals\", \"handler\", \"handlers\",\n \"helper\", \"helpers\", \"hook\", \"hooks\", \"imports\", \"interfaces\", \"main\",\n \"mocks\", \"models\", \"mutations\", \"props\", \"queries\", \"reducers\", \"routes\",\n \"schemas\", \"selectors\", \"setters\", \"setup\", \"state\", \"styles\", \"teardown\",\n \"type\", \"types\", \"util\", \"utilities\", \"utils\",\n]);\nconst SECTION_LABEL_RE = /^([A-Za-z]+)\\s*:?\\s*$/;\n\nfunction isSectionLabel(text: string): boolean {\n const match = SECTION_LABEL_RE.exec(text);\n return match !== null && SECTION_LABEL_WORDS.has((match[1] ?? \"\").toLowerCase());\n}\n\n// \"Helper function to check if a path is active\" — the opener announces the\n// *category* of the thing below (which its declaration already states) and then\n// restates its name. 6 corpus hits, 6 true positives.\nconst HELPER_OPENER_RE = /^(?:a\\s+)?helper\\s+(?:function|method|component|hook|class|type|util(?:ity)?)\\b/i;\n\n// \"Let's not await the promise\" — the first-person-plural walkthrough voice.\n// Gated on the narration verb list because the third-person `lets` is a\n// different word doing real work: \"lets a same-day re-run find the message it\n// already posted\" explains a mechanism and must not be touched.\nconst LETS_RE =\n /^let'?s\\s+(?:not\\s+|just\\s+|now\\s+|first\\s+)?(?:add|append|assign|await|build|calculate|call|check|clear|close|compute|convert|copy|count|create|declare|decrement|define|delete|extract|fetch|filter|find|format|generate|get|handle|increment|init|initialise|initialize|insert|iterate|join|load|log|loop|map|merge|open|parse|print|process|push|read|remove|render|reset|return|save|send|set|setup|sort|split|start|stop|store|update|validate|wrap|write)(?:s|es|ed|ing)?\\b/i;\n\n// Enumeration markers that narrate a sequence: `// 1. Load the config`,\n// `// Phase 2: reconcile`. Flagged only when the file carries exactly one — a\n// *run* of them is a documented algorithm walkthrough, which is the kind of\n// comment this rule exists to protect. JSX-expression comments are exempt\n// wholesale (see `isStandalone`): `{/* Step 1: Select Patient */}` mirrors the\n// literal step labels of a UI wizard.\nconst ENUMERATION_RE = /^(?:\\d+[.)]\\s+\\S|(?:phase|step)\\s+\\d+\\b)/i;\n\n// Dummy translational comments: ultra-short comments that just restate the code.\nconst DUMMY_TRANSLATION_RE = /^(?:increment|return|returns|get|gets|set\\b(?! up\\b)|sets\\b(?! up\\b)|function to|method to)\\b/i;\n\n// An ASCII sequence-diagram arrow. A long rule of dashes that ENDS IN AN ARROW\n// HEAD is drawing a timeline, not separating sections: `req------->res` is the\n// clearest documentation of a race condition anyone has written, and deleting it\n// loses information no function extraction can recover. Measured on 2,186 real\n// TypeScript files (zod / TanStack Query / react-router / swr / zustand): 8 of\n// the 83 section-banner hits were one such diagram, swr/src/index/use-swr.ts:524\n// through :549, explaining request/mutation interleaving. A real banner\n// (`// ---------- Checks ----------`,\n// react-router/scripts/pr.ts:157) has no arrow head and still fires.\nconst DIAGRAM_ARROW_RE = /[-=~]{2,}>|<[-=~]{2,}/;\n\nconst CODE_KEYWORD_RE =\n /^(import |export |const |let |var |function\\b|class |interface |type \\w|enum |return\\b|throw |await |async |if\\s*\\(|for\\s*\\(|while\\s*\\(|switch\\s*\\(|new |console\\.)/;\nconst CODE_TAIL_RE = /[;{}()]\\s*$|=>\\s*$|,\\s*$/;\n// LHS must be a real identifier (not a number literal — `0=Monday` in prose is\n// not an assignment) and `=` must not be `==`/`===`/`=>` (comparison/arrow).\n// The assignment branch additionally requires a code-tail — the line must end\n// with `;`, `)`, `}` or `]` — so plain prose like `count = number of items`\n// (which has no code-tail) is not mistaken for commented-out code.\nconst CALL_OR_ASSIGN_RE =\n /^[A-Za-z_$][\\w.$[\\]]*\\s*(?:=(?![=>])|\\+=|-=|\\*=)\\s*\\S.*[;)}\\]]\\s*$|^[A-Za-z_$][\\w.$]*\\([^)]*\\)\\s*;?\\s*$/;\n\n// Placeholders that only appear in grammar productions / desugaring examples,\n// never in real code: `%sent%`, `[opt]`, a standalone `<FunctionBody>`, `…` / `...`.\nconst PSEUDOCODE_RE = /%\\w+%|\\[opt\\]|(?:^|\\s)<[A-Za-z]\\w*>|…|\\.\\.\\./;\n\n// A triple-slash `///` directive keeps its third `/` after ESLint strips the\n// leading `//`, so strip 1–2 leading slashes (not exactly two) for `<reference`.\nfunction stripCommentMarker(line: string): string {\n return line.replace(/^\\s*\\/{1,2}/, \"\").replace(/^\\s*\\*+/, \"\").trim();\n}\n\nfunction isDirective(text: string): boolean {\n return DIRECTIVE_RE.test(text.trim());\n}\n\nfunction isBanner(text: string): boolean {\n const t = text.trim();\n if (!t) return false;\n if (BANNER_FULL_RE.test(t) || isRegionMarker(t)) return true;\n return BANNER_RUN_RE.test(t) && !DIAGRAM_ARROW_RE.test(t);\n}\n\nfunction looksLikeCode(text: string): boolean {\n const t = text.trim();\n if (!t) return false;\n if (CODE_KEYWORD_RE.test(t) && CODE_TAIL_RE.test(t)) return true;\n return CALL_OR_ASSIGN_RE.test(t);\n}\n\nfunction hasPseudocode(text: string): boolean {\n return PSEUDOCODE_RE.test(text);\n}\n\n/** An explanatory item of a numbered/bulleted walkthrough. */\nfunction isEnumeratedProseItem(text: string): boolean {\n const t = text.trim();\n return ENUMERATED_ITEM_RE.test(t) && t.split(/\\s+/).length >= ENUMERATED_ITEM_MIN_WORDS;\n}\n\n// A prose lead-in preceding a code-shaped line marks that line as an\n// illustration (`// For example:`, a grammar production `FunctionExpression:`),\n// not commented-out code.\nfunction isProse(text: string): boolean {\n const t = text.trim();\n if (!t) return false;\n if (t.endsWith(\":\")) return true;\n if (\n /[.!?]$/.test(t) &&\n /\\s/.test(t) &&\n /[a-z]/.test(t) &&\n !looksLikeCode(t) &&\n t.split(/\\s+/).length >= 3\n ) {\n return true;\n }\n return false;\n}\n\n// --- \"restates the next line\" ---------------------------------------------\n// A comment that opens with a narration verb and whose every remaining content\n// word already names what the statement below it computes says nothing the code\n// does not (`// increment the counter` / `counter += 1`). Three guards keep the\n// inference sound, because *coincidental* token overlap is the failure mode that\n// sank the first attempt at this shape (PR #98: `service` matching\n// `locationService` gave a ~60% false-positive rate):\n//\n// 1. Total corroboration — one unmatched word (`// guard the race from\n// PLT-812`) means the comment carries something the code does not, so it\n// stays.\n// 2. The comment must sit on top of a single-line, value-producing *statement*.\n// A comment above a block, a declaration, a type member or an object-literal\n// entry (`// store state` above `interface SessionState {`) labels a region\n// of code; the words it shares with the first line of that region are\n// incidental.\n// 3. Only the head of the statement counts — everything up to the first `(` —\n// so the comment must restate what the statement *computes* (its target and\n// its callee), not merely something it passes as an argument.\n\n// A causal connective. `for now` inside a sentence that also states WHY is a\n// justification, not an admission — `// Needed for now since router.fetch is not\n// async until v7` (react-router/.../__tests__/router/lazy-discovery-test.ts:2412,\n// and :2505) is the reason the sleep exists, which is exactly what the rule wants\n// a comment to carry. A bare `// quick fix for now` still fires.\nconst JUSTIFICATION_RE =\n /\\b(?:because|since|until|due to|so that|otherwise|which is why|in order to|to avoid|to work around|to prevent)\\b/i;\n\n/**\n * Whether a single-line comment merely narrates the code rather than explaining\n * the *why*. Three deterministic shapes: step narration (\"First, …\"), self-\n * admitted meta-commentary (\"keeping it simple\"), and a restatement of the very\n * next line (`// increment the counter` above `counter += 1`).\n *\n * `standalone` is false when the comment is one line of a contiguous `//` block.\n * The step and meta shapes are single-line tells; inside a paragraph they match a\n * clause of running prose rather than a label. Measured on 2,186 real TypeScript\n * files (zod / TanStack Query / react-router / swr / zustand), 9 of 42 narration\n * hits were exactly that — e.g. react-router/integration/bug-report-test.ts:26\n * (\"First, make sure to install dependencies and build React Router. From the\n * root of / the project, run this:\"), a six-line contributor instruction, and\n * react-router/packages/react-router/lib/dom/ssr/routes.tsx:663. A restatement of\n * the next line is still checked in a block, since it is corroborated against the\n * code rather than against a phrase.\n */\nfunction isRedundantNarration(\n body: string,\n statementBelow: string | null,\n standalone: boolean,\n isolatedEnumeration: boolean,\n nested: boolean,\n): boolean {\n const t = body.trim();\n if (!t || looksLikeCode(t) || hasPseudocode(t)) return false;\n if (standalone) {\n if (STEP_NARRATION_RE.test(t)) return true;\n if (META_COMMENTARY_RE.test(t) && !JUSTIFICATION_RE.test(t)) return true;\n if (HELPER_OPENER_RE.test(t) || LETS_RE.test(t)) return true;\n \n const words = t.split(/\\s+/);\n if (words.length <= 4 && DUMMY_TRANSLATION_RE.test(t) && !/[():=]/.test(t)) {\n const lowerT = t.toLowerCase();\n const rationaleWords = [\"when\", \"because\", \"if\", \"so that\", \"due to\", \"for\", \"instead of\", \"to prevent\", \"to avoid\", \"only\"];\n if (!rationaleWords.some(w => lowerT.includes(w))) {\n if (words.length > 1) return true;\n }\n }\n\n if (!nested && isSectionLabel(t)) return true;\n if (isolatedEnumeration && ENUMERATION_RE.test(t)) return true;\n }\n return restatesStatementHead(t, statementBelow);\n}\n\n// Statement-position containers. A comment whose innermost enclosing node is\n// anything else sits INSIDE an expression — an array, an object literal, a call\n// argument list — where a one-word label groups the elements beneath it rather\n// than signposting the file. `# config` inside pydantic's `__all__` is the\n// Python twin of this; both readings produce the same comment and only the\n// nesting tells them apart.\nconst STATEMENT_CONTAINERS: ReadonlySet<string> = new Set([\n AST_NODE_TYPES.Program,\n AST_NODE_TYPES.BlockStatement,\n AST_NODE_TYPES.ClassBody,\n AST_NODE_TYPES.StaticBlock,\n AST_NODE_TYPES.SwitchCase,\n AST_NODE_TYPES.TSModuleBlock,\n AST_NODE_TYPES.TSInterfaceBody,\n]);\n\n/**\n * True when the comment at `index` belongs to a contiguous `//` run in which\n * some line cites a ticket, URL, RFC or issue number (protected-class signal\n * S1, applied at run granularity).\n *\n * A scoping note puts its owner at the end — bulbul's Zoho-canary comment ends\n * \"EN-only for now — add an AR variant once AR audio exists (PROD-249)\" — so\n * judging the last line alone read \"for now\" as an unowned admission. A comment\n * that names where the decision is recorded is doing the one thing code cannot.\n */\nfunction runCitesAReference(comments: readonly TSESTree.Comment[], index: number): boolean {\n for (let i = index; i >= 0; i--) {\n if (i < index && !areAdjacentLineComments(comments[i], comments[i + 1])) break;\n if (hasExternalReference(stripCommentMarker(comments[i]?.value ?? \"\"))) return true;\n }\n for (let i = index + 1; i < comments.length; i++) {\n if (!areAdjacentLineComments(comments[i - 1], comments[i])) break;\n if (hasExternalReference(stripCommentMarker(comments[i]?.value ?? \"\"))) return true;\n }\n return false;\n}\n\n/** True when `a` and `b` are `//` comments on consecutive lines. */\nfunction areAdjacentLineComments(\n a: TSESTree.Comment | undefined,\n b: TSESTree.Comment | undefined,\n): boolean {\n return (\n a !== undefined &&\n b !== undefined &&\n a.type === \"Line\" &&\n b.type === \"Line\" &&\n b.loc.start.line === a.loc.end.line + 1\n );\n}\n\n/**\n * True when the comment at `index` is one line of a contiguous `//` block rather\n * than a lone annotation.\n */\nfunction isInsideCommentRun(comments: readonly TSESTree.Comment[], index: number): boolean {\n const comment = comments[index];\n return (\n areAdjacentLineComments(comments[index - 1], comment) ||\n areAdjacentLineComments(comment, comments[index + 1])\n );\n}\n\n/** How far back the illustration lead-in scan walks within one `//` block. */\nconst LEAD_IN_SCAN_LIMIT = 24;\n\n/**\n * True when a prose lead-in (`// Example:`, `// For example:`, a grammar\n * production head) appears earlier in the SAME contiguous `//` block. The\n * existing check only looked at the immediately preceding comment, so a code\n * illustration more than one line below its own heading was read as\n * commented-out code — measured at\n * react-router/packages/react-router/lib/hooks.tsx:791, where `// function\n * Blog() {` sits nine lines under its `// Example:` heading inside one 17-line\n * block (2 hits there of the 695 commented-out-code hits over 2,186 files).\n */\nfunction hasIllustrationLeadInAbove(\n comments: readonly TSESTree.Comment[],\n index: number,\n): boolean {\n for (let i = index - 1; i >= 0 && index - i <= LEAD_IN_SCAN_LIMIT; i--) {\n if (!areAdjacentLineComments(comments[i], comments[i + 1])) return false;\n const body = stripCommentMarker(comments[i]?.value ?? \"\");\n if (body.length > 0 && body.endsWith(\":\")) return true;\n }\n return false;\n}\n\nfunction hasCommentedOutCode(\n texts: readonly string[],\n precedingProse: boolean,\n): boolean {\n for (let i = 0; i < texts.length; i++) {\n const line = texts[i];\n if (line === undefined || !looksLikeCode(line) || hasPseudocode(line)) {\n continue;\n }\n const prev = i > 0 ? texts[i - 1] : undefined;\n if (prev !== undefined ? isProse(prev) : precedingProse) continue;\n return true;\n }\n return false;\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-comment-cruft\",\n meta: {\n type: \"suggestion\",\n docs: {\n description:\n \"Flag commented-out code, section-banner comments, and leading file-header comment preambles.\",\n },\n schema: [],\n messages: {\n commentedOutCode:\n \"Commented-out code — delete it; git history remembers.\",\n sectionBanner:\n \"Section-banner / region comment — structure code with functions, not ASCII rules.\",\n fileHeaderPreamble:\n \"File-header comment preamble — use a brief doc comment for the why, not a block of `//` lines.\",\n redundantNarration:\n \"Comment narrates the code — delete it or say *why*, not *what*. Code is self-documenting.\",\n untrackedTodo:\n \"Untracked TODO/FIXME marker — add an issue ticket or context link.\",\n },\n },\n defaultOptions: [],\n create(context) {\n if (isGeneratedFile(context.filename, context.sourceCode.text)) {\n return {};\n }\n\n const sourceCode = context.sourceCode;\n\n function isStandalone(comment: TSESTree.Comment): boolean {\n const before = sourceCode.getTokenBefore(comment, {\n includeComments: false,\n });\n return !before || before.loc.end.line < comment.loc.start.line;\n }\n\n function isJsDoc(comment: TSESTree.Comment): boolean {\n return comment.type === \"Block\" && /^\\*/.test(comment.value);\n }\n\n function reportLeadingPreamble(\n comments: readonly TSESTree.Comment[],\n firstCodeLine: number,\n ): void {\n const leading: TSESTree.Comment[] = [];\n let prevLine: number | null = null;\n for (const comment of comments) {\n if (comment.type !== \"Line\") break;\n if (comment.loc.start.line >= firstCodeLine) break;\n if (!isStandalone(comment)) break;\n const body = stripCommentMarker(comment.value);\n if (isDirective(body) || body.startsWith(\"!\")) continue;\n if (prevLine !== null && comment.loc.start.line !== prevLine + 1) break;\n leading.push(comment);\n prevLine = comment.loc.start.line;\n }\n const first = leading[0];\n if (first === undefined || leading.length < LEADING_PREAMBLE_MIN) return;\n const bodies = leading.map((c) => stripCommentMarker(c.value));\n if (bodies.some((body) => LICENSE_RE.test(body))) return;\n // A preamble carrying at least one prose sentence is documentation — the\n // \"why\" the rule wants — regardless of which comment syntax carries it.\n if (bodies.some((body) => isProse(body))) return;\n // Same for a numbered/bulleted walkthrough. The single corpus hit for this\n // message across 2,186 real TypeScript files was\n // react-router/scripts/release-comments.ts:1, a six-step description of\n // what the script does (\"1. get all tags sorted by creation date\", …) —\n // documentation, not a stack of content-free labels.\n if (bodies.filter(isEnumeratedProseItem).length >= ENUMERATED_PREAMBLE_MIN_ITEMS) {\n return;\n }\n context.report({ node: first, messageId: \"fileHeaderPreamble\" });\n }\n\n return {\n Program(): void {\n const comments = sourceCode.getAllComments();\n const firstCodeLine =\n sourceCode.ast.tokens[0]?.loc.start.line ?? Number.MAX_SAFE_INTEGER;\n const enumerated = comments.filter(\n (c) => c.type === \"Line\" && ENUMERATION_RE.test(stripCommentMarker(c.value)),\n );\n\n for (let i = 0; i < comments.length; i++) {\n const comment = comments[i];\n if (comment === undefined) continue;\n if (isJsDoc(comment) || !isStandalone(comment)) continue;\n if (LICENSE_RE.test(comment.value)) continue;\n const texts = comment.value\n .split(\"\\n\")\n .map(stripCommentMarker)\n .filter((l) => l.length > 0 && !isDirective(l));\n\n const firstText = texts[0];\n if (firstText !== undefined && /^(?:todo|fixme)\\b/i.test(firstText)) {\n if (!runCitesAReference(comments, i)) {\n context.report({\n node: comment,\n messageId: \"untrackedTodo\",\n });\n }\n continue;\n }\n\n if (texts.some(isBanner)) {\n context.report({ node: comment, messageId: \"sectionBanner\" });\n continue;\n }\n const prev = comments[i - 1];\n const precedingProse =\n prev !== undefined &&\n prev.type === \"Line\" &&\n prev.loc.end.line === comment.loc.start.line - 1 &&\n isProse(stripCommentMarker(prev.value));\n if (\n hasCommentedOutCode(texts, precedingProse) &&\n !hasIllustrationLeadInAbove(comments, i)\n ) {\n context.report({ node: comment, messageId: \"commentedOutCode\" });\n continue;\n }\n // Narration only for single-line comments (a multi-line block is\n // usually a real doc).\n if (comment.type === \"Line\" && texts.length === 1) {\n const body = texts[0];\n const statement = restatableStatementBelow(comment, sourceCode);\n const standalone = !isInsideCommentRun(comments, i);\n const container = sourceCode.getNodeByRangeIndex(comment.range[0]);\n const nested = container !== null && !STATEMENT_CONTAINERS.has(container.type);\n if (\n body !== undefined &&\n !runCitesAReference(comments, i) &&\n isRedundantNarration(body, statement, standalone, enumerated.length === 1, nested)\n ) {\n context.report({ node: comment, messageId: \"redundantNarration\" });\n }\n }\n }\n\n reportLeadingPreamble(comments, firstCodeLine);\n },\n };\n },\n});\n","/**\n * @fileoverview Shared comment analysis for the comment-hygiene rules\n * (`no-comment-cruft`, `no-restated-comment`, `jsdoc-restates-signature`,\n * `trailing-value-narration`) — the TypeScript twin of the Python\n * `rules/_comments.py`, kept signal-for-signal identical so the two languages\n * cannot drift on what counts as a comment worth keeping.\n *\n * **The protected class.** Nine deterministic signals that mark a comment as\n * carrying something the code cannot: an external reference, a version pin, a\n * number with a unit, a causal connective, a negation of the obvious, an\n * upstream-quirk word, a concurrency/invariant term, security reasoning, or a\n * vendor proper noun with *ascribed behaviour*. Measured over a 37,918-comment\n * corpus from nine repos: the nine protect 40/40 hand-picked best comments and\n * leak ~1% of the hand-classified cruft list.\n *\n * The class is an **EXEMPTION FLOOR, never a test**. `isProtected` returning\n * false says nothing about a comment — across pydantic / trio / attrs it matches\n * only 18-35% of the comments a human called valuable. Every use is of the form\n * \"if protected, do not flag\". Inverting it into \"unprotected, therefore delete\"\n * would condemn two thirds of the best comments in those libraries.\n */\n\nimport { AST_NODE_TYPES, type TSESTree } from \"@typescript-eslint/utils\";\n\n// S1 — external reference: URL, ticket key, RFC/PEP/CVE, bare issue number, or\n// an email/handle domain. Ticket keys allow letters after the first digit\n// (`PLATFORM-1YC`) and exclude the encoding acronyms sharing the shape.\nconst REF_RE =\n /https?:\\/\\/|\\bRFC[- ]?\\d+|\\bPEP[- ]?\\d+|\\bCVE-\\d{4}|\\b(?!UTF-|SHA-|ISO-|AES-|CRC-|MD-|PCM-|EOF-|API-|BASE-)[A-Z][A-Z0-9]{1,9}-\\d[A-Z0-9]{0,5}\\b|(?<![&\\w])#\\d{2,6}\\b|@[a-z][\\w.-]*\\.(?:us|com|ai|io|net|org|dev)\\b/;\n\n// S2 — version pin or comparison.\nconst VERSION_RE =\n /(?:>=|<=|==|<|>)\\s*v?\\d+\\.\\d+|\\bv\\d+\\.\\d+|\\b(?:since|until|as of)\\s+(?:v?\\d+\\.\\d+|Python\\s*\\d)/i;\n\n// S3 — a number carrying a unit, or an HTTP status code.\nconst UNITS_RE =\n /[~<>]?\\d+(?:\\.\\d+)?\\s?(?:ms|s\\b|sec\\b|seconds?\\b|min\\b|minutes?\\b|hours?\\b|days?\\b|KB|MB|MiB|GiB|kHz|Hz|bytes?\\b|bit\\b|-bit\\b|%|px\\b|rps\\b|qps\\b)|\\b[1-5]xx\\b|\\b(?:301|302|304|307|308|400|401|403|404|405|409|410|412|422|425|429|500|501|502|503|504)\\b/;\n\n// S4 — a causal connective tying behaviour to a consequence.\nconst CAUSAL_RE =\n /\\b(?:because|otherwise|so that|or else|would (?:break|fail|race|deadlock|leak|clobber|loop|crash|page|stall)|breaks?\\b|so we don'?t|to avoid\\b|caused\\b|causes\\b|gets? clobbered|keeps? (?:us|it|them) from|doesn'?t\\b.{0,24}\\b(?:page|fire|break|leak|loop)|eat into|would otherwise|trade-?offs?\\b)\\b/i;\n\n// S5 — negation of the obvious, or a flagged deliberate deviation.\nconst NEGATION_RE =\n /\\b(?:must not|must never|do(?:es)? not\\b|don'?t\\b.{0,30}\\b(?:leak|log|cache|retry|block|steal|wipe)|never\\b|deliberately|intentionally|counterintuitiv|NOT\\b)|(?<!based )\\bon purpose\\b|\\(not\\s|\\binstead of\\b|\\brather than\\b/;\n\n// S6 — upstream/vendor quirk, workaround provenance, or an external contract.\nconst UPSTREAM_RE =\n /\\b(?:upstream|workaround|quirk|backport|vendored|regression|fixed upstream|requires?\\b|convention\\b|rate.?limit|deprecat|opts? in(?:to)?\\b|raises?\\b.{0,60}\\b(?:when|if|unless)\\b)/i;\n\n// S7 — concurrency, ordering, or invariant vocabulary.\nconst INVARIANT_RE =\n /\\b(?:invariant|idempotent|race\\b|deadlock|re-?entran|atomic|thread-?safe|signal-?safe|lexicographic(?:al(?:ly)?)?|monotonic|must (?:run|be|happen|come|stay|hit|converge|configure)|before any\\b|lost the (?:claim )?race)\\b/i;\n\n// S8 — security reasoning.\nconst SECURITY_RE =\n /\\b(?:timing attack|constant-?time|replay|PII\\b|redact|secret|injection|spoof|fail-?closed|fail-?open|auth bypass|early-?exit timing)\\b/i;\n\n// S9 — a vendor proper noun with *ascribed behaviour*. A vendor name as the mere\n// object of a narration verb (\"Create the prompt for Gemini\") is NOT protected;\n// that distinction is what holds the leak rate at ~1%.\nconst VENDOR_RE =\n /\\b(?:GitHub|Slack|Twilio|LiveKit|Kamailio|Groq|OpenAI|Anthropic|Cloudflare|FastAPI|Starlette|Sentry|Zoho|Salla|Ashby|Linear|BigQuery|Postgres|Neon|Drizzle|Vertex|Gemini|Firestore|Stripe|Next\\.js|React Compiler|pydantic|ruff|loguru|Lexical|Farasa|Orpheus|Whisper|schemathesis)(?:'s\\b|\\s+(?:requires?|returns?|expects?|allows?|rejects?|accepts?|sends?|caps?|limits?|wraps?|silently|outputs?|stores?|treats?|doesn'?t|does not|won'?t|can'?t|only|models)\\b)/;\n\nconst PROTECTED_SIGNALS: readonly RegExp[] = [\n REF_RE,\n VERSION_RE,\n UNITS_RE,\n CAUSAL_RE,\n NEGATION_RE,\n UPSTREAM_RE,\n INVARIANT_RE,\n SECURITY_RE,\n VENDOR_RE,\n];\n\n/**\n * True when a comment carries any of the nine protected-class signals.\n *\n * EXEMPTION FLOOR ONLY — a false result is not evidence the comment is\n * worthless. See the file overview.\n */\nexport function isProtected(body: string): boolean {\n return PROTECTED_SIGNALS.some((signal) => signal.test(body));\n}\n\n/**\n * True when a comment cites a ticket, URL, RFC/PEP/CVE, or issue number\n * (signal S1 alone). Naming where a decision is recorded is the one thing code\n * cannot do, and it separates an owned scoping note (\"EN-only for now — AR needs\n * audio (PROD-249)\") from an unowned admission (\"hacky, fix later\").\n */\nexport function hasExternalReference(body: string): boolean {\n return REF_RE.test(body);\n}\n\n// --- tokenisation shared by the restatement detectors ----------------------\n\n/**\n * Words that say nothing about *which* code a comment describes. Kept in step\n * with `_comments.py`: shrinking the list costs recall, growing it costs\n * precision by discounting a genuinely novel word.\n */\nexport const STOPWORDS: ReadonlySet<string> = new Set(\n `a an the this that these those it its their his her our your my\n is are was were be been being am do does did done doing has have had having\n will would shall should can could may might must\n and or but nor so yet not no none\n to of for in on at by with from into onto out up down over under about\n as if then than when where which who whom whose what how why while\n we you they i he she them him us me\n also just only even still already again there here\n all any each every some\n via per etc eg ie vs\n need needs needed want wants make makes making let lets\n please note see above below`.split(/\\s+/),\n);\n\nconst CAMEL_PART_RE = /[A-Z]+(?=[A-Z][a-z])|[A-Z]?[a-z]+|[A-Z]+|\\d+/g;\nconst WORD_RE = /[A-Za-z_$][\\w$]*|\\d+/g;\n\n/** Split `snake_case` / `camelCase` / `SCREAMING_CASE` into lowercase parts. */\nexport function splitIdentifier(token: string): string[] {\n const parts: string[] = [];\n for (const chunk of token.split(/[_$]/)) {\n for (const match of chunk.match(CAMEL_PART_RE) ?? []) {\n parts.push(match.toLowerCase());\n }\n }\n return parts;\n}\n\n/**\n * Fold the common English inflections so `updates` / `updating` match `update`.\n *\n * The trailing-`e` strip is what makes the fold *symmetric*: without it\n * `creates` / `creating` reduce to `creat` while `create` stays `create`, and\n * the two never match — the shape that most often made a restatement look\n * novel. Deliberately crude otherwise; every extra conflation is a chance to\n * call a novel word a restatement.\n */\nexport function stem(word: string): string {\n let base = word;\n for (const suffix of [\"ing\", \"ied\", \"ies\", \"ers\", \"er\", \"ed\", \"es\", \"s\"]) {\n if (word.endsWith(suffix) && word.length - suffix.length >= 3) {\n base = word.slice(0, word.length - suffix.length);\n if (suffix === \"ied\" || suffix === \"ies\") return `${base}y`;\n break;\n }\n }\n return base.endsWith(\"e\") && base.length - 1 >= 3 ? base.slice(0, -1) : base;\n}\n\n/** Split prose into lowercase content words, dropping stopwords. */\nexport function contentTokens(text: string): string[] {\n const tokens: string[] = [];\n for (const match of text.match(WORD_RE) ?? []) {\n tokens.push(...splitIdentifier(match));\n }\n return tokens.filter((token) => !STOPWORDS.has(token));\n}\n\n/** Every identifier part appearing in a slice of source. */\nexport function codeTokens(text: string): Set<string> {\n const tokens = new Set<string>();\n for (const match of text.match(WORD_RE) ?? []) {\n for (const part of splitIdentifier(match)) tokens.add(part);\n }\n return tokens;\n}\n\n/**\n * True when every content token of a comment already appears in the code.\n *\n * Exact or stemmed match only. Prefix matching is deliberately absent: it is\n * what sank the first attempt at this shape (PR #98), where `service` matched\n * `locationService` and drove the false-positive rate to ~60%.\n */\nexport function restates(commentTokens: readonly string[], code: ReadonlySet<string>): boolean {\n const stems = new Set<string>();\n for (const token of code) stems.add(stem(token));\n return commentTokens.every((token) => code.has(token) || stems.has(stem(token)));\n}\n\n// --- the verb-opener restatement shape (shared with `no-comment-cruft`) ------\n\nconst NARRATION_MAX_WORDS = 6;\nconst NARRATION_MIN_CONTENT = 1;\nconst TOKEN_PLURAL_MIN = 4;\n\n// Statements that compute something a comment could be restating. A block, a\n// class/function/type declaration, an `if`, a `for` — anything whose body the\n// comment could be labelling instead — is deliberately absent.\nconst RESTATABLE_STATEMENTS: ReadonlySet<string> = new Set([\n AST_NODE_TYPES.ExpressionStatement,\n AST_NODE_TYPES.ReturnStatement,\n AST_NODE_TYPES.ThrowStatement,\n AST_NODE_TYPES.VariableDeclaration,\n]);\n\n// Verbs that describe the mechanics of the next statement. A comment opening\n// with anything else (a noun, \"because\", \"we\", a ticket id) is not narration.\nconst NARRATION_VERB_RE =\n /^(?:add|append|assign|build|calculate|call|check|clear|close|compute|convert|copy|count|create|declare|decrement|define|delete|extract|fetch|filter|find|format|generate|get|handle|increment|init|initialise|initialize|insert|iterate|join|load|log|loop|make|map|merge|open|parse|print|process|push|read|remove|render|reset|return|save|send|set|setup|sort|split|start|stop|store|update|validate|wrap|write)(?:s|es|d|ed|ing)?$/i;\n\n// Words that carry no information about *which* code the comment describes, so\n// they are not required to appear in the line below.\nconst NARRATION_STOPWORDS: ReadonlySet<string> = new Set([\n \"a\", \"all\", \"an\", \"and\", \"any\", \"are\", \"as\", \"at\", \"back\", \"be\", \"both\", \"by\",\n \"each\", \"for\", \"from\", \"here\", \"if\", \"in\", \"into\", \"is\", \"it\", \"its\", \"just\",\n \"new\", \"of\", \"on\", \"one\", \"onto\", \"or\", \"our\", \"out\", \"over\", \"so\", \"that\",\n \"the\", \"then\", \"this\", \"to\", \"up\", \"us\", \"we\", \"when\", \"with\",\n]);\n\n/** Lowercase, with a single plural `s` folded away so `users` matches `user`. */\nexport function normalizeToken(word: string): string {\n const lower = word.toLowerCase();\n return lower.length > TOKEN_PLURAL_MIN && lower.endsWith(\"s\") && !lower.endsWith(\"ss\")\n ? lower.slice(0, -1)\n : lower;\n}\n\n/** Every identifier in a slice of source, plus its parts, with plurals folded. */\nfunction headTokens(source: string): ReadonlySet<string> {\n const tokens = new Set<string>();\n for (const identifier of source.match(/[A-Za-z_$][\\w$]*/g) ?? []) {\n tokens.add(normalizeToken(identifier));\n for (const part of identifier.split(/[_$]+|(?<=[a-z0-9])(?=[A-Z])/)) {\n if (part.length > 0) tokens.add(normalizeToken(part));\n }\n }\n return tokens;\n}\n\n/** The slice of ESLint's `SourceCode` these shapes read. */\nexport interface StatementReader {\n getTokenAfter: (\n node: TSESTree.Comment,\n options: { includeComments: boolean },\n ) => TSESTree.Token | null;\n getNodeByRangeIndex: (index: number) => TSESTree.Node | null;\n getText: (node: TSESTree.Node) => string;\n}\n\n/** A declaration with nothing but a zero/empty seed computes nothing to restate. */\nfunction isTrivialInitializer(node: TSESTree.VariableDeclaration): boolean {\n return node.declarations.every((declarator) => {\n const init = declarator.init;\n if (init == null || init.type === AST_NODE_TYPES.Literal) return true;\n return (\n (init.type === AST_NODE_TYPES.ArrayExpression && init.elements.length === 0) ||\n (init.type === AST_NODE_TYPES.ObjectExpression && init.properties.length === 0)\n );\n });\n}\n\n/**\n * The source of the single-line statement a comment sits directly above, or null\n * when what follows is a block, a declaration, a type member or anything else\n * the comment could be *labelling* rather than restating.\n */\nexport function restatableStatementBelow(comment: TSESTree.Comment, sourceCode: StatementReader): string | null {\n const token = sourceCode.getTokenAfter(comment, { includeComments: false });\n if (token === null || token.loc.start.line !== comment.loc.end.line + 1) return null;\n for (\n let node: TSESTree.Node | undefined | null = sourceCode.getNodeByRangeIndex(token.range[0]);\n node != null && node.type !== AST_NODE_TYPES.Program;\n node = node.parent\n ) {\n if (!RESTATABLE_STATEMENTS.has(node.type)) continue;\n if (node.loc.start.line !== token.loc.start.line || node.loc.end.line !== node.loc.start.line) {\n return null;\n }\n if (node.type === AST_NODE_TYPES.VariableDeclaration && isTrivialInitializer(node)) {\n return null;\n }\n return sourceCode.getText(node);\n }\n return null;\n}\n\n/**\n * True when a short verb-led comment adds nothing to the statement beneath it:\n * every content word after the opening verb already appears in that statement's\n * head — its assignment target or its callee.\n */\nexport function restatesStatementHead(body: string, statement: string | null): boolean {\n if (statement === null) return false;\n const words = body.match(/[A-Za-z][\\w$]*/g) ?? [];\n const opener = words[0];\n if (opener === undefined || words.length > NARRATION_MAX_WORDS) return false;\n if (!NARRATION_VERB_RE.test(opener)) return false;\n const content = words\n .slice(1)\n .map(normalizeToken)\n .filter((word) => !NARRATION_STOPWORDS.has(word));\n if (content.length < NARRATION_MIN_CONTENT) return false;\n const head = statement.split(\"(\")[0] ?? statement;\n const code = headTokens(head);\n return content.every((word) => code.has(word));\n}\n","/**\n * @fileoverview Disallow TypeScript `enum` declarations. Use string-literal\n * union types (e.g. `type Status = \"active\" | \"inactive\"`) or `as const`\n * objects instead — enums generate runtime code, have unintuitive numeric\n * defaults, and don't tree-shake cleanly.\n *\n * Generated files can opt out either by living under a path matched by\n * `ignoreFiles` (default: `**\\/generated/**`, `**\\/*.gen.ts`, `**\\/*.generated.ts`)\n * or by including a `@generated` marker comment near the top of the file.\n */\n\nimport { ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\nimport { isGeneratedFile } from \"./_paths.js\";\n\ntype MessageIds = \"noEnum\";\ntype Options = readonly [\n {\n ignoreFiles?: readonly string[];\n }?,\n];\n\nconst DEFAULT_IGNORE_PATTERNS: readonly RegExp[] = [\n /[\\\\/]generated[\\\\/]/,\n /[\\\\/]openapi-gen[\\\\/]/,\n /\\.gen\\.tsx?$/,\n /\\.generated\\.tsx?$/,\n];\n\nfunction matchesAnyPattern(\n filename: string,\n patterns: readonly string[],\n): boolean {\n for (const pattern of patterns) {\n // Convert minimatch-ish globs to regex: ** -> .*, * -> [^/\\\\]*\n const regexSource = pattern\n .replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\")\n .replace(/\\*\\*/g, \"::DOUBLESTAR::\")\n .replace(/\\*/g, \"[^/\\\\\\\\]*\")\n .replace(/::DOUBLESTAR::/g, \".*\");\n if (new RegExp(`^${regexSource}$`).test(filename)) {\n return true;\n }\n }\n return false;\n}\n\nfunction hasGeneratedMarker(sourceText: string): boolean {\n // Look only in the first 1KB to keep this cheap.\n const head = sourceText.slice(0, 1024);\n return /@generated\\b/.test(head);\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-enum\",\n meta: {\n type: \"suggestion\",\n docs: {\n description:\n \"Disallow TypeScript `enum`; use string-literal unions or `as const` objects instead.\",\n },\n schema: [\n {\n type: \"object\",\n additionalProperties: false,\n properties: {\n ignoreFiles: {\n type: \"array\",\n items: { type: \"string\" },\n },\n },\n },\n ],\n messages: {\n noEnum:\n 'Enums are discouraged. Use a string-literal union (e.g. `type Status = \"active\" | \"inactive\"`) or an `as const` object instead.',\n },\n },\n defaultOptions: [{}],\n create(context, [optionsArg]) {\n const options = optionsArg ?? {};\n const ignoreFiles = options.ignoreFiles ?? [];\n const filename = context.filename;\n const sourceText = context.sourceCode.getText();\n\n const isIgnoredByDefault = DEFAULT_IGNORE_PATTERNS.some((re) =>\n re.test(filename),\n );\n const isIgnoredByOption =\n ignoreFiles.length > 0 && matchesAnyPattern(filename, ignoreFiles);\n const isGenerated = hasGeneratedMarker(sourceText) || isGeneratedFile(filename, sourceText);\n\n if (isIgnoredByDefault || isIgnoredByOption || isGenerated) {\n return {};\n }\n\n return {\n TSEnumDeclaration(node: TSESTree.TSEnumDeclaration): void {\n context.report({\n node,\n messageId: \"noEnum\",\n });\n },\n };\n },\n});\n","/**\n * @fileoverview Disallow using `Math.random()` to generate identifiers,\n * tokens, keys, or other security-sensitive values. `Math.random()` is not\n * cryptographically secure and is predictable — using it for tokens/secrets\n * can lead to collisions and trivially guessable values. Prefer\n * `crypto.randomUUID()` or `crypto.getRandomValues(...)` instead.\n *\n * Precision matters here: the rule runs at `error` in the `strict` config, so a\n * false positive blocks a real PR. Large open-source sweeps (VS Code, NestJS,\n * Next.js) showed the bare `id`/`key`/`session` substring heuristic firing on a\n * flood of NON-security correlation/ephemeral ids — temp-file suffixes, HMR\n * session numbers, dev request/execution/trace ids, in-process RPC handles.\n * None of those are security tokens. So the rule now requires a STRONG security\n * signal, and actively exempts names that read as ephemeral/correlation ids or\n * random values concatenated into a filename/path/DOM id.\n *\n * The rule flags a `Math.random()` call when, and only when:\n * 1. Its enclosing binding/property NAME carries a strong security signal\n * (`token`, `secret`, `apiKey`, `csrf`, `password`, `nonce`, `salt`,\n * `uuid`, `authId`) — even a `sessionToken` counts because of `token`; or\n * 2. Its result is fed into a `.toString(36)` chain (the classic insecure\n * random-id idiom, e.g. `Math.random().toString(36).slice(2)`) AND the\n * value is not an exempt ephemeral/correlation id or path fragment.\n *\n * It does NOT flag when the enclosing name signals a non-security\n * correlation/ephemeral id (`temp`, `tmp`, `cache`, `correlation`, `request`,\n * `req`, `trace`, `execution`, `dev`, `hmr`, `mock`, `test`, `perf`, `marker`),\n * nor when the random value is concatenated into a filename/path/DOM id. A\n * strong security name still wins over these exemptions.\n *\n * Bare `Math.random()` used for non-identifier purposes (jitter, sampling,\n * rolls, etc.) is NOT flagged, and neither is the bare `id`/`key`/`session`\n * substring on its own — we err toward suppressing ambiguous correlation ids.\n *\n * Test files are out of scope entirely. A fixture generator is not a token\n * mint, and the `.toString(36)` idiom is how tests cheaply produce distinct\n * keys. Measured on 2,186 real TypeScript files (zod / TanStack Query /\n * react-router / swr / zustand): 3 of 6 hits were exactly that —\n * zod/packages/zod/src/v3/tests/Mocker.ts:13 (a mock-data string generator) and\n * two in\n * react-router/packages/react-router/__tests__/vendor/turbo-stream-test.ts:215\n * (random map keys for a serialization round-trip). None is a credential.\n *\n * KNOWN GAP (false-negative): an arithmetic expression between `Math.random()`\n * and `.toString(36)` breaks the member-chain walk, e.g.\n * `(Math.random() * 1e9).toString(36)`. The intervening `BinaryExpression`\n * means `Math.random()` is no longer the object end of the `.toString` chain,\n * so trigger 2 does not fire. Such code is only caught if its binding/property\n * name looks security-like (trigger 1). See the documented test case.\n */\n\nimport { ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\nimport { isTestFile } from \"./_paths.js\";\n\ntype MessageIds = \"insecureRandomId\";\ntype Options = readonly [];\n\nconst STRONG_SECURITY_PATTERN =\n /token|secret|csrf|password|passwd|apikey|api[-_]?key|nonce|salt|uuid|authid/i;\n\nconst NON_SECURITY_ID_PATTERN =\n /temp|tmp|cache|correlation|request|req|trace|execution|dev|hmr|mock|test|perf|marker/i;\n\n/** Matches a static string fragment that reads as a path or DOM-id context. */\nconst PATH_OR_DOM_MARKER = /[\\\\/#]|\\.[A-Za-z0-9]/;\n\n/**\n * Returns true if `node` is a `Math.random()` CallExpression.\n */\nfunction isMathRandomCall(node: TSESTree.Node): node is TSESTree.CallExpression {\n if (node.type !== \"CallExpression\") {\n return false;\n }\n const callee = node.callee;\n if (callee.type !== \"MemberExpression\" || callee.computed) {\n return false;\n }\n const { object, property } = callee;\n return (\n object.type === \"Identifier\" &&\n object.name === \"Math\" &&\n property.type === \"Identifier\" &&\n property.name === \"random\"\n );\n}\n\n/**\n * Climbs the member/call chain in which `node` is the object/callee end, and\n * returns the outermost value node of that chain. For\n * `Math.random().toString(36).slice(2)` this returns the `.slice(2)` call —\n * i.e. the value that actually gets assigned/concatenated.\n */\nfunction climbValueChain(node: TSESTree.Node): TSESTree.Node {\n let current: TSESTree.Node = node;\n let parent = current.parent;\n\n while (parent) {\n if (\n parent.type === \"MemberExpression\" &&\n parent.object === current &&\n !parent.computed\n ) {\n current = parent;\n parent = current.parent;\n continue;\n }\n if (parent.type === \"CallExpression\" && parent.callee === current) {\n current = parent;\n parent = current.parent;\n continue;\n }\n break;\n }\n\n return current;\n}\n\n/**\n * Returns true if `node` (a `Math.random()` call) is the base of a member\n * chain that calls `.toString(36)` somewhere above it, e.g.\n * `Math.random().toString(36)` or `Math.random().toString(36).slice(2)`.\n */\nfunction isPartOfToString36Chain(node: TSESTree.Node): boolean {\n let current: TSESTree.Node = node;\n let parent = current.parent;\n\n while (parent) {\n if (\n parent.type === \"MemberExpression\" &&\n parent.object === current &&\n !parent.computed &&\n parent.property.type === \"Identifier\" &&\n parent.property.name === \"toString\"\n ) {\n const grandparent = parent.parent;\n if (\n grandparent &&\n grandparent.type === \"CallExpression\" &&\n grandparent.callee === parent\n ) {\n const firstArg = grandparent.arguments[0];\n if (firstArg && firstArg.type === \"Literal\" && firstArg.value === 36) {\n return true;\n }\n }\n }\n\n if (parent.type === \"MemberExpression\" && parent.object === current) {\n current = parent;\n parent = current.parent;\n continue;\n }\n if (parent.type === \"CallExpression\" && parent.callee === current) {\n current = parent;\n parent = current.parent;\n continue;\n }\n break;\n }\n\n return false;\n}\n\n/**\n * Walks up from `node` to find the name of the nearest enclosing binding\n * (VariableDeclarator id) or property (Property / PropertyDefinition key),\n * and returns it. Passes through string concatenation and template literals so\n * that `const tempPath = base + Math.random()...` resolves to `tempPath`.\n * Returns `undefined` if no such name is found before leaving the enclosing\n * initializer/value context.\n */\nfunction findEnclosingName(node: TSESTree.Node): string | undefined {\n let current: TSESTree.Node = node;\n let parent = current.parent;\n\n while (parent) {\n if (parent.type === \"VariableDeclarator\" && parent.init === current) {\n if (parent.id.type === \"Identifier\") {\n return parent.id.name;\n }\n return undefined;\n }\n\n if (parent.type === \"Property\" && parent.value === current) {\n const key = parent.key;\n if (!parent.computed && key.type === \"Identifier\") {\n return key.name;\n }\n if (key.type === \"Literal\" && typeof key.value === \"string\") {\n return key.value;\n }\n return undefined;\n }\n\n if (parent.type === \"PropertyDefinition\" && parent.value === current) {\n const key = parent.key;\n if (!parent.computed && key.type === \"Identifier\") {\n return key.name;\n }\n if (key.type === \"Literal\" && typeof key.value === \"string\") {\n return key.value;\n }\n return undefined;\n }\n\n if (\n parent.type === \"FunctionDeclaration\" ||\n parent.type === \"FunctionExpression\" ||\n parent.type === \"ArrowFunctionExpression\" ||\n parent.type === \"BlockStatement\" ||\n parent.type === \"ReturnStatement\" ||\n parent.type === \"ExpressionStatement\"\n ) {\n return undefined;\n }\n\n current = parent;\n parent = current.parent;\n }\n\n return undefined;\n}\n\n/**\n * Collects the static string fragments of a `+` concatenation / template\n * literal subtree into `out`.\n */\nfunction collectStaticStringParts(\n node: TSESTree.Node,\n out: string[],\n): void {\n if (node.type === \"Literal\" && typeof node.value === \"string\") {\n out.push(node.value);\n return;\n }\n if (node.type === \"TemplateLiteral\") {\n for (const quasi of node.quasis) {\n out.push(quasi.value.cooked ?? quasi.value.raw);\n }\n return;\n }\n if (node.type === \"BinaryExpression\" && node.operator === \"+\") {\n collectStaticStringParts(node.left, out);\n collectStaticStringParts(node.right, out);\n }\n}\n\n/**\n * Returns true if the random value is concatenated into a string whose static\n * parts read as a filename/path/DOM id (contain a slash, backslash, `#`, or a\n * `.ext`-style fragment).\n */\nfunction isConcatenatedIntoPathOrDomId(node: TSESTree.Node): boolean {\n const valueNode = climbValueChain(node);\n\n let current: TSESTree.Node = valueNode;\n let parent = current.parent;\n let top: TSESTree.Node | undefined;\n\n while (parent) {\n if (\n parent.type === \"BinaryExpression\" &&\n parent.operator === \"+\" &&\n (parent.left === current || parent.right === current)\n ) {\n top = parent;\n current = parent;\n parent = current.parent;\n continue;\n }\n if (parent.type === \"TemplateLiteral\") {\n top = parent;\n current = parent;\n parent = current.parent;\n continue;\n }\n break;\n }\n\n if (!top) {\n return false;\n }\n\n const parts: string[] = [];\n collectStaticStringParts(top, parts);\n return parts.some((part) => PATH_OR_DOM_MARKER.test(part));\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-insecure-random-id\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Disallow using `Math.random()` to generate identifiers, tokens, or secrets; use `crypto.randomUUID()` or `crypto.getRandomValues(...)` instead.\",\n },\n schema: [],\n messages: {\n insecureRandomId:\n \"`Math.random()` is not cryptographically secure and is predictable; do not use it to generate IDs, tokens, or secrets. Use `crypto.randomUUID()` or `crypto.getRandomValues(...)` instead.\",\n },\n },\n defaultOptions: [],\n create(context) {\n if (isTestFile(context.filename)) {\n return {};\n }\n return {\n CallExpression(node: TSESTree.CallExpression): void {\n if (!isMathRandomCall(node)) {\n return;\n }\n\n const name = findEnclosingName(node);\n\n // A strong security name always fires — even a `sessionToken` or a name\n // that also happens to contain a correlation-id word.\n if (name !== undefined && STRONG_SECURITY_PATTERN.test(name)) {\n context.report({ node, messageId: \"insecureRandomId\" });\n return;\n }\n\n // Ephemeral / correlation ids (temp files, HMR sessions, dev request\n // ids, ...) are not security-sensitive — suppress, including the\n // `.toString(36)` idiom used to shape them.\n if (name !== undefined && NON_SECURITY_ID_PATTERN.test(name)) {\n return;\n }\n\n // A random value concatenated into a filename/path/DOM id is not a\n // security token.\n if (isConcatenatedIntoPathOrDomId(node)) {\n return;\n }\n\n // The classic insecure random-id idiom.\n if (isPartOfToString36Chain(node)) {\n context.report({ node, messageId: \"insecureRandomId\" });\n }\n },\n };\n },\n});\n","/**\n * @fileoverview Disallow `JSON.stringify(err)` on a (heuristically detected)\n * Error value. `JSON.stringify` on an Error produces `\"{}\"` because the\n * `message` and `stack` properties are non-enumerable, silently throwing away\n * the very information you were trying to log.\n *\n * This is a purely syntactic rule (no type information). It flags\n * `JSON.stringify(x)` when the first argument is either:\n * 1. an Identifier that is the binding of an enclosing `catch (x)` clause, or\n * matches the conventional error-name pattern /^(e|err|error|ex|exc)$/i, OR\n * 2. a member expression denoting an error value (`err.cause`, `this.lastError`) —\n * an error-suggesting property name, or an error-named base whose property is\n * not a plain string accessor (`.message` / `.stack` / `.name`).\n *\n * It suppresses the report inside the non-error branch of an `x instanceof Error`\n * guard (`x instanceof Error ? x : JSON.stringify(x)`), where stringifying the\n * non-Error fallback is exactly correct.\n *\n * Object literals and arbitrary identifiers (`JSON.stringify(user)`) are not flagged.\n *\n * Rule (2) originally assumed that any property of an error-named base is itself\n * an error. It is not: the error objects real frameworks throw carry a plain,\n * fully-enumerable DATA payload alongside the non-enumerable `message`/`stack`.\n * A sweep of 2,186 real TypeScript files (zod / TanStack Query / react-router /\n * swr / zustand) hit exactly that at\n * react-router/playground/rsc-vite/src/routes/root/root.client.tsx:42 —\n * `JSON.stringify(error.data)` on a react-router `ErrorResponse`, where `.data`\n * is the loader's own JSON body and stringifying it is the correct thing to do.\n * `PAYLOAD_PROPS` lists those accessors (`data`, `status`, `issues`, ...)\n * alongside `SAFE_STRING_PROPS`; `err.cause` and friends still fire because the\n * property name itself names an error.\n */\n\nimport { ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\nimport type { Scope, SourceCode } from \"@typescript-eslint/utils/ts-eslint\";\n\ntype MessageIds = \"noJsonStringifyError\";\ntype Options = readonly [];\n\nconst ERROR_NAME_PATTERN = /^(e|err|error|ex|exc)$/i;\n\n/** Property names whose value is itself an error object (unsafe to stringify). */\nconst ERROR_PROP_PATTERN = /^(cause|lastError|error|err|exception|originalError|innerError)$/i;\n\n/** Property names whose value is a plain string — the recommended escape hatch. */\nconst SAFE_STRING_PROPS: ReadonlySet<string> = new Set([\"message\", \"stack\", \"name\"]);\n\n/**\n * Accessors that hold an error's *data* payload rather than a nested error.\n * These are ordinary enumerable JSON, so `JSON.stringify` on them is correct —\n * react-router's `ErrorResponse.data`, zod's `ZodError.issues`, an HTTP client's\n * `.status` / `.response.body`.\n */\nconst PAYLOAD_PROPS: ReadonlySet<string> = new Set([\n \"data\",\n \"status\",\n \"statuscode\",\n \"statustext\",\n \"code\",\n \"issues\",\n \"details\",\n \"body\",\n \"payload\",\n \"response\",\n \"info\",\n \"meta\",\n \"metadata\",\n \"context\",\n]);\n\n/**\n * Walk up the scope chain looking for a `catch` clause whose binding matches\n * `name`. We rely on the scope analysis provided by the parser rather than\n * type information, keeping the rule type-free.\n */\nfunction isCatchBinding(scope: Scope.Scope, name: string): boolean {\n let current: Scope.Scope | null = scope;\n while (current) {\n const variable = current.set.get(name);\n if (variable) {\n for (const def of variable.defs) {\n if (def.type === \"CatchClause\") {\n return true;\n }\n }\n }\n current = current.upper;\n }\n return false;\n}\n\n/**\n * True if a member-expression argument (`err.cause`, `this.lastError`) denotes an\n * Error value: an error-suggesting property name, or an error-suggesting base whose\n * property is not a known string accessor (`.message` / `.stack` / `.name`).\n */\nfunction memberSuggestsError(\n member: TSESTree.MemberExpression,\n scope: Scope.Scope,\n): boolean {\n const propName =\n !member.computed && member.property.type === \"Identifier\" ? member.property.name : null;\n\n if (propName !== null && ERROR_PROP_PATTERN.test(propName)) {\n return true;\n }\n\n const base = member.object;\n const baseSuggestsError =\n base.type === \"Identifier\" &&\n (ERROR_NAME_PATTERN.test(base.name) || isCatchBinding(scope, base.name));\n if (baseSuggestsError) {\n if (propName === null) {\n return true;\n }\n const lowered = propName.toLowerCase();\n return !SAFE_STRING_PROPS.has(lowered) && !PAYLOAD_PROPS.has(lowered);\n }\n\n return false;\n}\n\n/** The subject `x` of an `x instanceof Error` test, or null. */\nfunction instanceofErrorSubject(\n test: TSESTree.Expression,\n): TSESTree.Expression | null {\n if (\n test.type === \"BinaryExpression\" &&\n test.operator === \"instanceof\" &&\n test.right.type === \"Identifier\" &&\n test.right.name === \"Error\"\n ) {\n return test.left;\n }\n return null;\n}\n\n/** Names of a user-defined type-guard predicate: `isErrorLike`, `isError`, `hasErrorShape`. */\nconst TYPE_GUARD_PATTERN = /^(is|has)[A-Z]/;\n\n/** The narrowed subject `x` of a user-defined type-guard call `isFoo(x)`, or null. */\nfunction typeGuardSubject(\n test: TSESTree.Expression,\n): TSESTree.Expression | null {\n const arg = test.type === \"CallExpression\" ? test.arguments[0] : undefined;\n if (\n test.type === \"CallExpression\" &&\n test.callee.type === \"Identifier\" &&\n TYPE_GUARD_PATTERN.test(test.callee.name) &&\n test.arguments.length === 1 &&\n arg !== undefined &&\n arg.type !== \"SpreadElement\"\n ) {\n return arg;\n }\n return null;\n}\n\n/**\n * The subject `x` of a positive error-narrowing test — `x instanceof Error` or a\n * user-defined guard `isErrorLike(x)`. In both, the value IS the error in the\n * truthy branch, so `JSON.stringify(x)` belongs in the falsy (alternate) branch.\n */\nfunction positiveErrorSubject(\n test: TSESTree.Expression,\n): TSESTree.Expression | null {\n return instanceofErrorSubject(test) ?? typeGuardSubject(test);\n}\n\n/** The subject `x` of a negated error-narrowing test — `!(x instanceof Error)` / `!isErrorLike(x)`. */\nfunction negatedInstanceofErrorSubject(\n test: TSESTree.Expression,\n): TSESTree.Expression | null {\n if (test.type === \"UnaryExpression\" && test.operator === \"!\") {\n return positiveErrorSubject(test.argument);\n }\n return null;\n}\n\n/** Whether a branch statement unconditionally exits (its last statement returns/throws). */\nfunction branchTerminates(branch: TSESTree.Statement): boolean {\n const body = branch.type === \"BlockStatement\" ? branch.body : [branch];\n const last = body[body.length - 1];\n return (\n last !== undefined &&\n (last.type === \"ReturnStatement\" || last.type === \"ThrowStatement\")\n );\n}\n\n/**\n * True if an earlier guard `if (isErrorLike(arg)) return …` (or `instanceof Error`)\n * in an enclosing block narrows `argExpr` away from the error case before `node`,\n * so by the time `JSON.stringify(arg)` runs the value is the non-Error fallback.\n */\nfunction isNarrowedByEarlyReturn(\n node: TSESTree.Node,\n argExpr: TSESTree.Expression,\n sourceCode: Readonly<SourceCode>,\n): boolean {\n const argText = sourceCode.getText(argExpr);\n let current: TSESTree.Node | undefined = node.parent;\n while (current) {\n if (current.type === \"BlockStatement\" || current.type === \"Program\") {\n for (const stmt of current.body) {\n if (stmt.range[0] >= node.range[0]) {\n break;\n }\n if (\n stmt.type === \"IfStatement\" &&\n stmt.alternate === null &&\n branchTerminates(stmt.consequent)\n ) {\n const subject = positiveErrorSubject(stmt.test);\n if (subject && sourceCode.getText(subject) === argText) {\n return true;\n }\n }\n }\n }\n current = current.parent;\n }\n return false;\n}\n\nfunction nodeWithin(node: TSESTree.Node, container: TSESTree.Node | null): boolean {\n return (\n container !== null &&\n node.range[0] >= container.range[0] &&\n node.range[1] <= container.range[1]\n );\n}\n\n/**\n * True if `node` sits in the NON-error branch of an `argExpr instanceof Error`\n * guard — the branch where `argExpr` is provably not an Error, so stringifying it\n * is the correct fallback. Handles both the ternary and `if`/`else` shapes,\n * including the negated `!(x instanceof Error)` form.\n */\nfunction isGuardedByInstanceofError(\n node: TSESTree.Node,\n argExpr: TSESTree.Expression,\n sourceCode: Readonly<SourceCode>,\n): boolean {\n const argText = sourceCode.getText(argExpr);\n const sameSubject = (subject: TSESTree.Expression): boolean =>\n sourceCode.getText(subject) === argText;\n\n let current: TSESTree.Node | undefined = node.parent;\n while (current) {\n if (current.type === \"ConditionalExpression\") {\n const subject = positiveErrorSubject(current.test);\n if (subject && sameSubject(subject) && nodeWithin(node, current.alternate)) {\n return true;\n }\n const negated = negatedInstanceofErrorSubject(current.test);\n if (negated && sameSubject(negated) && nodeWithin(node, current.consequent)) {\n return true;\n }\n } else if (current.type === \"IfStatement\") {\n const subject = positiveErrorSubject(current.test);\n if (subject && sameSubject(subject) && nodeWithin(node, current.alternate)) {\n return true;\n }\n const negated = negatedInstanceofErrorSubject(current.test);\n if (negated && sameSubject(negated) && nodeWithin(node, current.consequent)) {\n return true;\n }\n }\n current = current.parent;\n }\n return false;\n}\n\nfunction isJsonStringify(callee: TSESTree.Expression): boolean {\n return (\n callee.type === \"MemberExpression\" &&\n !callee.computed &&\n callee.object.type === \"Identifier\" &&\n callee.object.name === \"JSON\" &&\n callee.property.type === \"Identifier\" &&\n callee.property.name === \"stringify\"\n );\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-json-stringify-error\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Disallow `JSON.stringify` on an Error value; it yields `{}` because `message`/`stack` are non-enumerable.\",\n },\n schema: [],\n messages: {\n noJsonStringifyError:\n \"`JSON.stringify` on an Error yields `{}` because `message`/`stack` are non-enumerable. Log `err.message` / `err.stack`, or use a proper error serializer.\",\n },\n },\n defaultOptions: [],\n create(context) {\n return {\n CallExpression(node: TSESTree.CallExpression): void {\n if (!isJsonStringify(node.callee)) {\n return;\n }\n\n const firstArg = node.arguments[0];\n if (!firstArg) {\n return;\n }\n\n const scope = context.sourceCode.getScope(firstArg);\n\n let suggestsError: boolean;\n if (firstArg.type === \"Identifier\") {\n suggestsError =\n ERROR_NAME_PATTERN.test(firstArg.name) || isCatchBinding(scope, firstArg.name);\n } else if (firstArg.type === \"MemberExpression\") {\n suggestsError = memberSuggestsError(firstArg, scope);\n } else {\n return;\n }\n\n if (!suggestsError) {\n return;\n }\n\n if (\n isGuardedByInstanceofError(node, firstArg, context.sourceCode) ||\n isNarrowedByEarlyReturn(node, firstArg, context.sourceCode)\n ) {\n return;\n }\n\n context.report({\n node,\n messageId: \"noJsonStringifyError\",\n });\n },\n };\n },\n});\n","/**\n * @fileoverview Disallow `catch` clauses that only log (via `console.*` or a\n * logger receiver such as `logger.warn(...)` / `Log.error(...)`) and then\n * swallow the error. A catch that logs and falls through — with no `throw`, no\n * `return`, and no real recovery — hides failures: the program keeps running in\n * a broken state while the only signal is a log line that is easy to miss.\n * Either rethrow the error or handle it for real.\n *\n * This rule is deliberately conservative and fires in exactly two shapes:\n * - `noLogOnlyCatch`: the catch body is non-empty and *every* statement is a\n * logging call (`console.*` or a call on a logger-named receiver). Any other\n * statement (a `throw`, a `return`, a fallback assignment, a non-logging\n * call, etc.) means the catch is doing something and is left alone.\n * - `emptyCatch`: the catch body is genuinely empty AND carries no comment.\n * A comment-only catch (`catch { /* ignore, safe because … *\\/ }`) is treated\n * as an intentional, documented ignore and is exempt.\n *\n * The documented-ignore exemption applies to BOTH message ids. A catch that logs\n * and explains why the failure is survivable is the same deliberate decision as\n * a comment-only empty catch, and the rule's own message (\"handle it for real\")\n * has nothing to add to it. Measured on 2,186 real TypeScript files (zod /\n * TanStack Query / react-router / swr / zustand): of 10 log-only hits, the one\n * carrying a written rationale was\n * react-router/packages/react-router-dev/vite/styles.ts:104 —\n * `catch { console.warn(...); // this can happen with dynamically imported\n * modules … }` — and it is precisely the case the rule should not litigate. A\n * bare `catch (e) { console.error(e); }` with no comment still fires (9 hits,\n * e.g. react-router/packages/react-router/lib/dom/ssr/fog-of-war.ts:209).\n *\n * A previous version fired the \"logging then swallowing\" message on empty and\n * comment-only catches that contained no logging call at all — the vast majority\n * of real-world hits — which was factually wrong. The two distinct message ids\n * keep each diagnostic accurate.\n *\n * Scope: this rule owns the `CatchClause` (try/catch) form ONLY. The promise\n * form — `.catch(() => {})`, `.catch(() => null)`, and every other handler that\n * provably does nothing — is owned entirely by `no-silent-promise-catch`, whose\n * detection is a strict superset of what this rule used to do there. Two rules\n * firing on one `.catch()` meant two messages and two suppression comments for\n * a single defect, so the promise path was removed from here.\n *\n * Test files opt out by default (filenames containing `.test.`, `.spec.`, or a\n * `__tests__/` path segment) since swallow-and-log is common and acceptable in\n * test scaffolding.\n *\n * A logging call is recognised by the shared `_logging` matcher: a log method on\n * a logger receiver, plus any project-declared free logging function named in\n * the `logFunctions` option (`logEvent(\"x\", { err })`). Structured loggers are\n * usually free functions, so without that option a catch that only calls one was\n * silently under-reported here — declaring it makes the shape visible.\n */\n\nimport { ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\nimport {\n createLogMatcher,\n LOGGING_OPTION_PROPERTIES,\n type LoggingOptions,\n} from \"./_logging.js\";\n\ntype MessageIds = \"noLogOnlyCatch\" | \"emptyCatch\";\ntype Options = readonly [LoggingOptions?];\n\nconst DEFAULT_IGNORE_PATTERNS: readonly RegExp[] = [\n /\\.test\\./,\n /\\.spec\\./,\n /[\\\\/]__tests__[\\\\/]/,\n];\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-log-only-catch\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Disallow `catch` clauses that only log (or silently do nothing) and then swallow the error; rethrow or handle it instead.\",\n },\n schema: [\n {\n type: \"object\",\n additionalProperties: false,\n properties: { ...LOGGING_OPTION_PROPERTIES },\n },\n ],\n messages: {\n noLogOnlyCatch:\n \"Logging then swallowing the error hides failures. Rethrow the error or handle it for real.\",\n emptyCatch:\n \"Empty catch silently swallows the error. Rethrow it, handle it, or add a comment explaining why it is safe to ignore.\",\n },\n },\n defaultOptions: [{}],\n create(context, [loggingOptions]) {\n const matcher = createLogMatcher(loggingOptions);\n const filename = context.filename;\n\n /** True when a statement is exactly a bare logging call, e.g. `console.error(err);`. */\n function isLoggingCallStatement(statement: TSESTree.Statement): boolean {\n if (statement.type !== \"ExpressionStatement\") {\n return false;\n }\n return matcher.isLoggingCall(statement.expression);\n }\n\n const isIgnoredByDefault = DEFAULT_IGNORE_PATTERNS.some((re) =>\n re.test(filename),\n );\n\n if (isIgnoredByDefault) {\n return {};\n }\n\n return {\n CatchClause(node: TSESTree.CatchClause): void {\n const statements = node.body.body;\n\n // A comment inside the block documents an intentional ignore — for a\n // silent swallow and for a log-and-continue alike.\n const isDocumented =\n context.sourceCode.getCommentsInside(node.body).length > 0;\n\n if (statements.length === 0) {\n if (isDocumented) {\n return;\n }\n context.report({ node, messageId: \"emptyCatch\" });\n return;\n }\n\n if (isDocumented) {\n return;\n }\n\n const everyStatementIsLogging = statements.every((statement) =>\n isLoggingCallStatement(statement),\n );\n\n if (everyStatementIsLogging) {\n context.report({ node, messageId: \"noLogOnlyCatch\" });\n }\n },\n };\n },\n});\n","/**\n * @fileoverview Shared helpers for recognising logging / error-reporting calls.\n * Used by `no-log-only-catch`, `no-sentinel-return-on-catch` and\n * `no-secret-in-log` so all three rules agree on what counts as \"this call\n * writes to a log sink\" before deciding a catch silently swallows an error or a\n * secret leaks.\n *\n * Two shapes are recognised:\n *\n * - **Receiver-shaped** — a log METHOD (`debug`/`info`/`warn`/`error`/…) on a\n * logger RECEIVER: `console.error(...)`, `logger.warn(...)`,\n * `this.logger.info(...)`, and builder/factory chains\n * (`logger.bind({...}).info(...)`, `logging.getLogger(n).info(...)`).\n * - **Free-function-shaped** — a project-declared logging function, e.g.\n * `logEvent(\"pr_scan.failed\", { repo, error })`. Structured loggers that are\n * plain functions taking a meta object are the dominant real-world shape and\n * have no logger receiver at all, so they are invisible to the receiver\n * heuristic. Projects declare theirs via the shared `logFunctions` rule\n * option; `loggerNames` extends the receiver set the same way.\n *\n * Both options are OFF by default (empty), so default behaviour is unchanged.\n * Declaring them makes the catch rules stop reporting a correctly-logged\n * degraded return AND makes `no-secret-in-log` start inspecting those calls —\n * the second effect closes a real hole, since `logEvent(\"auth\", { botToken })`\n * was previously never examined.\n */\n\nimport { type TSESTree } from \"@typescript-eslint/utils\";\n\nexport const LOG_METHODS: ReadonlySet<string> = new Set([\n \"debug\",\n \"info\",\n \"warn\",\n \"warning\",\n \"error\",\n \"exception\",\n \"critical\",\n \"trace\",\n \"log\",\n \"fatal\",\n \"success\",\n]);\n\nexport const LOGGER_NAMES: ReadonlySet<string> = new Set([\n \"logger\",\n \"log\",\n \"logging\",\n \"loguru\",\n \"console\",\n \"_logger\",\n \"_log\",\n]);\n\n/** Factory methods that RETURN a logger (`logging.getLogger(name).info(...)`). */\nexport const LOGGER_FACTORIES: ReadonlySet<string> = new Set([\n \"getlogger\",\n \"get_logger\",\n]);\n\n/**\n * Names of free functions / methods that report an error to a handler or sink,\n * e.g. `onUnexpectedError`, `reportError`, `captureException`, `logError`. Kept\n * broad on purpose: pairing this with \"takes the caught binding\" is what makes\n * it precise.\n */\nexport const REPORT_NAME_RE = /error|report|capture|log|trace|warn/i;\n\n/** Shared `loggerNames` / `logFunctions` option shape. */\nexport interface LoggingOptions {\n readonly loggerNames?: readonly string[];\n readonly logFunctions?: readonly string[];\n}\n\n/**\n * JSON-schema properties for the shared logging options. Spread into a rule's\n * `schema[0].properties` so every rule that consults `_logging` accepts the same\n * declaration and a project configures its logger exactly once.\n */\nexport const LOGGING_OPTION_PROPERTIES = {\n loggerNames: { type: \"array\", items: { type: \"string\" } },\n logFunctions: { type: \"array\", items: { type: \"string\" } },\n} as const;\n\n/** The static callee name of a call (free function or method), or null. */\nexport function calleeName(callee: TSESTree.Node): string | null {\n if (callee.type === \"Identifier\") {\n return callee.name;\n }\n if (\n callee.type === \"MemberExpression\" &&\n !callee.computed &&\n callee.property.type === \"Identifier\"\n ) {\n return callee.property.name;\n }\n return null;\n}\n\nexport interface LogMatcher {\n /** Is `expr` a logger receiver (bare name, member chain, or factory call)? */\n isLoggerReceiver(expr: TSESTree.Expression | TSESTree.PrivateIdentifier): boolean;\n /** Is `expr` a project-declared free logging function call (`logEvent(...)`)? */\n isLogFunctionCall(expr: TSESTree.Node): boolean;\n /** Is `expr` a logging call of either shape? */\n isLoggingCall(expr: TSESTree.Node): boolean;\n}\n\n/**\n * Builds the log-recognition predicates for one rule invocation, folding in the\n * project's declared `loggerNames` / `logFunctions`. Call once per `create()`.\n */\nexport function createLogMatcher(options: LoggingOptions = {}): LogMatcher {\n const loggerNames: ReadonlySet<string> = new Set([\n ...LOGGER_NAMES,\n ...(options.loggerNames ?? []).map((name) => name.toLowerCase()),\n ]);\n const logFunctions: ReadonlySet<string> = new Set(options.logFunctions ?? []);\n\n function isLoggerReceiver(\n expr: TSESTree.Expression | TSESTree.PrivateIdentifier,\n ): boolean {\n switch (expr.type) {\n case \"Identifier\":\n return loggerNames.has(expr.name.toLowerCase());\n case \"MemberExpression\": {\n const { property, object } = expr;\n if (!expr.computed && property.type === \"Identifier\") {\n const lowered = property.name.toLowerCase();\n if (loggerNames.has(lowered) || LOGGER_FACTORIES.has(lowered)) {\n return true;\n }\n }\n return isLoggerReceiver(object);\n }\n case \"CallExpression\": {\n const callee = expr.callee;\n if (\n callee.type === \"MemberExpression\" &&\n !callee.computed &&\n callee.property.type === \"Identifier\" &&\n LOGGER_FACTORIES.has(callee.property.name.toLowerCase())\n ) {\n return true;\n }\n if (callee.type !== \"Super\") {\n return isLoggerReceiver(callee);\n }\n return false;\n }\n default:\n return false;\n }\n }\n\n function isLogFunctionCall(expr: TSESTree.Node): boolean {\n if (expr.type !== \"CallExpression\" || logFunctions.size === 0) {\n return false;\n }\n const name = calleeName(expr.callee);\n return name !== null && logFunctions.has(name);\n }\n\n function isLoggingCall(expr: TSESTree.Node): boolean {\n if (expr.type !== \"CallExpression\") {\n return false;\n }\n if (isLogFunctionCall(expr)) {\n return true;\n }\n const callee = expr.callee;\n if (\n callee.type !== \"MemberExpression\" ||\n callee.computed ||\n callee.property.type !== \"Identifier\"\n ) {\n return false;\n }\n if (!LOG_METHODS.has(callee.property.name.toLowerCase())) {\n return false;\n }\n return isLoggerReceiver(callee.object);\n }\n\n return { isLoggerReceiver, isLogFunctionCall, isLoggingCall };\n}\n","/**\n * @fileoverview Disallow direct `process.env` access. Force all env reads\n * through a Zod-validated env module so configuration is typed and validated\n * at startup.\n *\n * The rule is about READING A CONFIGURATION VALUE in application code, and a\n * sweep of 2,186 real TypeScript files (zod / TanStack Query / react-router /\n * swr / zustand) produced 143 hits of which ~90% were not that. Four exemptions,\n * each measured against that corpus:\n *\n * - **Write target.** `process.env.X = \"yes\"` sets a variable for a child\n * process; there is no value being read, so the validated-env module has\n * nothing to say about it. 20 hits, e.g.\n * react-router/packages/react-router-dev/vite/plugins/prerender.ts:250 and\n * react-router/packages/react-router-dev/vite/plugin.ts:2451.\n * - **Whole-environment pass-through.** `{ ...process.env, NO_COLOR: \"1\" }`\n * forwards the inherited environment to a spawned process — again no\n * configuration value is read. 9 hits, e.g.\n * react-router/integration/helpers/create-fixture.ts:45.\n * - **Test files** (shared `isTestFile`). A test that drives a CLI has to set\n * and read the raw environment; there is no app env module in scope. 50\n * hits, e.g.\n * react-router/packages/create-react-router/__tests__/create-react-router-test.ts:1052.\n * - **Scripts and build/test config** (shared `isScriptFile`, plus a\n * `*.config.*` basename). A `vitest.config.mts` / `playwright.config.ts`\n * configures the *build*, runs before the app exists, and cannot import the\n * app's validated env. 47 hits, e.g. swr/test/e2e/playwright.config.ts:16\n * and zod/scripts/check-versions.ts:5.\n *\n * What survives is the shape the rule was written for: application code reading\n * a deployment setting, e.g. zod/packages/docs/loaders/stars.ts:1\n * (`const GITHUB_TOKEN = process.env.GITHUB_TOKEN!`).\n */\n\nimport { ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\nimport { isScriptFile, isTestFile } from \"./_paths.js\";\n\ntype MessageIds = \"noRawEnv\";\ntype Options = readonly [];\n\n/** Build / test / tooling config: `vite.config.ts`, `vitest.config.mts`, `playwright.config.ts`. */\nconst CONFIG_FILE_RE = /(^|[\\\\/])[\\w.-]+\\.config\\.[cm]?[jt]sx?$/;\n\n/**\n * Modules that define the validated env boundary have to read raw env exactly\n * once before handing values to Zod. Keep this path-scoped so ordinary\n * `config.ts`/settings helpers still use the boundary instead of becoming one.\n */\nconst ENV_BOUNDARY_FILE_RE =\n /(^|[\\\\/])(?:env|client-env|server-env|client-settings|server-settings)\\.[cm]?[jt]sx?$/;\n\nfunction isValidatedEnvBoundary(filename: string, sourceText: string): boolean {\n return (\n ENV_BOUNDARY_FILE_RE.test(filename.replaceAll(\"\\\\\", \"/\")) &&\n /\\bz\\.object\\s*\\(/.test(sourceText) &&\n /\\.parse\\s*\\(/.test(sourceText)\n );\n}\n\n/** True for the `process.env` member node (dotted or as the base of `process.env[key]`). */\nfunction isProcessEnv(node: TSESTree.MemberExpression): boolean {\n return (\n !node.computed &&\n node.object.type === \"Identifier\" &&\n node.object.name === \"process\" &&\n node.property.type === \"Identifier\" &&\n node.property.name === \"env\"\n );\n}\n\n/** True for the `import.meta.env` member node (dotted or as the base of `import.meta.env[key]`). */\nfunction isImportMetaEnv(node: TSESTree.MemberExpression): boolean {\n return (\n !node.computed &&\n node.property.type === \"Identifier\" &&\n node.property.name === \"env\" &&\n node.object.type === \"MetaProperty\" &&\n node.object.meta.name === \"import\" &&\n node.object.property.name === \"meta\"\n );\n}\n\n// Build-time constants that bundlers (webpack/Vite) statically replace — there is\n// no runtime env value to route through the validated layer, so they are exempt.\nconst BUILD_TIME_CONSTANTS: ReadonlySet<string> = new Set([\n \"NODE_ENV\",\n \"MODE\",\n \"DEV\",\n \"PROD\",\n \"SSR\",\n]);\n\n/** True when `node` is the base of a build-time-constant access like `process.env.NODE_ENV`. */\nfunction isBuildTimeConstantAccess(node: TSESTree.MemberExpression): boolean {\n const parent = node.parent;\n return (\n parent.type === \"MemberExpression\" &&\n parent.object === node &&\n !parent.computed &&\n parent.property.type === \"Identifier\" &&\n BUILD_TIME_CONSTANTS.has(parent.property.name)\n );\n}\n\n/**\n * True when the env access is the TARGET of an assignment (`process.env.X = v`,\n * `process.env.X ??= v`, `delete process.env.X`) rather than a read. Writing a\n * variable for a child process reads no configuration value.\n */\nfunction isWriteTarget(node: TSESTree.MemberExpression): boolean {\n const access = node.parent.type === \"MemberExpression\" && node.parent.object === node ? node.parent : node;\n const parent = access.parent;\n if (parent.type === \"AssignmentExpression\") {\n return parent.left === access;\n }\n if (parent.type === \"UnaryExpression\") {\n return parent.operator === \"delete\";\n }\n return false;\n}\n\n/**\n * True when the WHOLE environment is spread into an object literal —\n * `{ ...process.env, NO_COLOR: \"1\" }`. That forwards the inherited environment\n * to a spawned process; it is not a read of any particular setting.\n */\nfunction isWholeEnvSpread(node: TSESTree.MemberExpression): boolean {\n const parent = node.parent;\n return parent.type === \"SpreadElement\" && parent.argument === node;\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-raw-env\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Disallow direct `process.env` access; use a Zod-validated env module instead.\",\n },\n schema: [],\n messages: {\n noRawEnv:\n \"Do not read from `process.env` directly. Import the Zod-validated env module instead so values are typed and validated at startup.\",\n },\n },\n defaultOptions: [],\n create(context) {\n const filename = context.filename;\n if (\n isTestFile(filename) ||\n isScriptFile(filename) ||\n CONFIG_FILE_RE.test(filename.replaceAll(\"\\\\\", \"/\")) ||\n isValidatedEnvBoundary(filename, context.sourceCode.text)\n ) {\n return {};\n }\n return {\n MemberExpression(node: TSESTree.MemberExpression): void {\n if (\n (isProcessEnv(node) || isImportMetaEnv(node)) &&\n !isBuildTimeConstantAccess(node) &&\n !isWriteTarget(node) &&\n !isWholeEnvSpread(node)\n ) {\n context.report({\n node,\n messageId: \"noRawEnv\",\n });\n }\n },\n };\n },\n});\n","/**\n * @fileoverview Disallow silently swallowing an error in a `catch` clause by\n * returning a \"sentinel\" empty value as the final/only statement.\n *\n * A `catch` block whose last statement is `return null` / `return undefined` /\n * `return false` / `return []` / `return {}` (and which never `throw`s) discards\n * the caught error entirely. Downstream callers can't distinguish a genuine\n * empty result from a failure, which is a frequent source of silent data loss\n * and broken idempotency decisions.\n *\n * This rule is deliberately conservative — it prefers false negatives over\n * false positives. It does NOT flag:\n * - catch blocks that `throw`/rethrow anywhere in their body,\n * - returns of a computed/meaningful value (calls, identifiers, member\n * expressions, non-empty literals),\n * - `return 0` / `return \"\"`, which are often legitimate results,\n * - catches that LOG or REPORT the caught error before the sentinel return —\n * a `console.*`/logger call, a project-declared free logging function (the\n * shared `logFunctions` option), or an error-reporting call that mentions\n * the caught binding ANYWHERE in its arguments. The binding search walks the\n * whole argument subtree, because structured loggers take a meta object:\n * `logEvent(\"x\", { error: err instanceof Error ? err.message : String(err) })`\n * reports the error just as surely as `report(err)` does. Here the sentinel\n * is a deliberate degraded return, not a silent swallow.\n * - the typed-optional / safe-parse / predicate shape: the try body returns an\n * expression whose subtree contains a parse-style call that throws on bad\n * input (`JSON.parse(x)`, `new URL(s).protocol === \"https:\"`, or a lone\n * `await request.json()`), or the enclosing function is a predicate — either\n * declared (`: boolean`) or NAMED as one (`isDirectory`, `fileExists`) — or\n * returns the same sentinel kind on a normal path, including through a\n * ternary (`return valid ? value : false`). Here the sentinel is the\n * declared contract.\n *\n * The predicate exemption is deliberately limited to a `boolean` result. A\n * declared `T | null` / `T | undefined` return does NOT exempt: hiding a\n * failure behind a nullable accessor is the exact true positive this rule\n * exists for, and exempting it would gut the rule.\n *\n * The last two clauses came out of a 2026-07 corpus sweep (2220 files across\n * zod / TanStack Query / react-router / swr / zustand) in which the rule fired\n * 10 times and every hit was a false positive — see `PREDICATE_NAME_RE` and\n * `returnedSentinelKinds` for the per-family evidence.\n *\n * Scope: this rule owns the `CatchClause` (try/catch) form ONLY. The same\n * swallow written in promise form — `await load().catch(() => [])` — is owned\n * entirely by `no-silent-promise-catch`, whose detection (any handler that\n * provably does nothing: bare literal, empty collection, empty block, or a\n * block returning one of those) is a strict superset of the empty-collection\n * check this rule used to run there. Two rules firing on one `.catch()` meant\n * two messages and two suppression comments for a single defect, so the\n * promise path was removed from here.\n */\n\nimport {\n ESLintUtils,\n type TSESTree,\n AST_NODE_TYPES,\n} from \"@typescript-eslint/utils\";\n\nimport {\n createLogMatcher,\n calleeName,\n LOGGING_OPTION_PROPERTIES,\n type LoggingOptions,\n REPORT_NAME_RE,\n} from \"./_logging.js\";\nimport { isGeneratedFile } from \"./_paths.js\";\n\ntype MessageIds = \"noSentinelReturn\";\ntype Options = readonly [LoggingOptions?];\n\ntype SentinelKind = \"nullish\" | \"boolean\" | \"array\" | \"object\" | \"string\";\n\n/** The sentinel \"kind\" of a returned expression, or null if not a sentinel. */\nfunction sentinelKind(arg: TSESTree.Expression | null): SentinelKind | null {\n if (arg === null) {\n return null;\n }\n if (arg.type === AST_NODE_TYPES.Literal) {\n if (arg.value === null) {\n return \"nullish\";\n }\n if (typeof arg.value === \"boolean\") {\n return \"boolean\";\n }\n if (typeof arg.value === \"string\") {\n return \"string\";\n }\n return null;\n }\n if (arg.type === AST_NODE_TYPES.Identifier && arg.name === \"undefined\") {\n return \"nullish\";\n }\n if (arg.type === AST_NODE_TYPES.ArrayExpression) {\n return \"array\";\n }\n if (arg.type === AST_NODE_TYPES.ObjectExpression) {\n return \"object\";\n }\n return null;\n}\n\n/** Whether a returned expression is one of the swallowing sentinels we flag. */\nfunction isSentinelArgument(arg: TSESTree.Expression | null): boolean {\n if (arg === null) {\n return false;\n }\n if (arg.type === AST_NODE_TYPES.Literal && arg.value === null) {\n return true;\n }\n if (arg.type === AST_NODE_TYPES.Literal && arg.value === false) {\n return true;\n }\n if (arg.type === AST_NODE_TYPES.Identifier && arg.name === \"undefined\") {\n return true;\n }\n if (arg.type === AST_NODE_TYPES.ArrayExpression && arg.elements.length === 0) {\n return true;\n }\n if (\n arg.type === AST_NODE_TYPES.ObjectExpression &&\n arg.properties.length === 0\n ) {\n return true;\n }\n return false;\n}\n\nfunction isFunctionNode(node: TSESTree.Node): boolean {\n return (\n node.type === AST_NODE_TYPES.FunctionDeclaration ||\n node.type === AST_NODE_TYPES.FunctionExpression ||\n node.type === AST_NODE_TYPES.ArrowFunctionExpression\n );\n}\n\nfunction isNode(value: unknown): value is TSESTree.Node {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as { type?: unknown }).type === \"string\"\n );\n}\n\n/**\n * Walk `node`'s subtree applying `visit`, but do not descend into nested\n * function scopes — their statements don't run synchronously for the current\n * catch. Stops early once `visit` returns true.\n */\nfunction walkWithinScope(\n node: TSESTree.Node,\n visit: (current: TSESTree.Node) => boolean,\n): boolean {\n let found = false;\n\n const recurse = (current: TSESTree.Node): void => {\n if (found) {\n return;\n }\n if (visit(current)) {\n found = true;\n return;\n }\n if (isFunctionNode(current)) {\n return;\n }\n for (const key of Object.keys(current)) {\n if (key === \"parent\") {\n continue;\n }\n const value = (current as unknown as Record<string, unknown>)[key];\n if (Array.isArray(value)) {\n for (const child of value) {\n if (isNode(child)) {\n recurse(child);\n }\n }\n } else if (isNode(value)) {\n recurse(value);\n }\n }\n };\n\n recurse(node);\n return found;\n}\n\n/** Does this subtree throw (ignoring nested function scopes)? */\nfunction containsThrow(node: TSESTree.Node): boolean {\n return walkWithinScope(\n node,\n (current) => current.type === AST_NODE_TYPES.ThrowStatement,\n );\n}\n\n/** True when a parameter pattern binds `name` (plain, default, rest, or destructured). */\nfunction bindsName(param: TSESTree.Node, name: string): boolean {\n switch (param.type) {\n case AST_NODE_TYPES.Identifier:\n return param.name === name;\n case AST_NODE_TYPES.AssignmentPattern:\n return bindsName(param.left, name);\n case AST_NODE_TYPES.RestElement:\n return bindsName(param.argument, name);\n case AST_NODE_TYPES.ArrayPattern:\n return param.elements.some(\n (element) => element !== null && bindsName(element, name),\n );\n case AST_NODE_TYPES.ObjectPattern:\n return param.properties.some((property) =>\n property.type === AST_NODE_TYPES.RestElement\n ? bindsName(property.argument, name)\n : bindsName(property.value, name),\n );\n default:\n return false;\n }\n}\n\n/**\n * Does `node`'s subtree READ the identifier `name`?\n *\n * \"Read\" means a VALUE position. A bare name match is not enough, and getting\n * that wrong silently guts the rule. Walking the whole argument subtree (so a\n * structured logger's meta object counts as reporting the error) cost 8 of 18\n * true positives when measured against the previous release, because all of\n * these matched on name alone:\n *\n * logCounter({ error: 1 }) // object KEY — a counter, not a log\n * captureMetric({ tags: { e: 1 } }) // nested key\n * reportStatus(response.err) // member PROPERTY of another object\n * logAll(items.map((error) => error.id)) // a param that merely SHADOWS it\n *\n * Each of those swallows the error while looking \"logged\". Non-computed property\n * keys and non-computed member properties are therefore skipped, and a nested\n * function that rebinds the name is not descended into.\n */\nfunction subtreeReadsName(node: TSESTree.Node, name: string): boolean {\n let found = false;\n\n /** True when this function rebinds `name`, so its body reads a different value. */\n const shadowsName = (fn: TSESTree.Node): boolean =>\n isFunctionNode(fn) &&\n (fn as unknown as { params: TSESTree.Parameter[] }).params.some((param) =>\n bindsName(param, name),\n );\n\n const recurse = (current: TSESTree.Node): void => {\n if (found) {\n return;\n }\n if (current.type === AST_NODE_TYPES.Identifier && current.name === name) {\n found = true;\n return;\n }\n if (shadowsName(current)) {\n return;\n }\n for (const key of Object.keys(current)) {\n if (key === \"parent\") {\n continue;\n }\n // `{ error: 1 }` — the key names a field; it does not read the binding.\n if (\n key === \"key\" &&\n current.type === AST_NODE_TYPES.Property &&\n !current.computed\n ) {\n continue;\n }\n // `response.err` — the property names a field on some other object.\n if (\n key === \"property\" &&\n current.type === AST_NODE_TYPES.MemberExpression &&\n !current.computed\n ) {\n continue;\n }\n const value = (current as unknown as Record<string, unknown>)[key];\n if (Array.isArray(value)) {\n for (const child of value) {\n if (isNode(child)) {\n recurse(child);\n }\n }\n } else if (isNode(value)) {\n recurse(value);\n }\n }\n };\n\n recurse(node);\n return found;\n}\n\n/**\n * Does the caught-error binding appear ANYWHERE in a call's arguments? The whole\n * argument subtree is searched, not just top-level positional args: a structured\n * logger takes the error nested in a meta object or behind a conditional\n * (`logEvent(\"x\", { error: err instanceof Error ? err.message : String(err) })`),\n * and that reports the error exactly as `report(err)` does.\n */\nfunction argsIncludeBinding(\n args: readonly TSESTree.CallExpressionArgument[],\n caughtName: string | null,\n): boolean {\n if (caughtName === null) {\n return false;\n }\n return args.some((arg) => subtreeReadsName(arg, caughtName));\n}\n\n/** The try block guarded by this catch. */\nfunction tryBlockOf(catchNode: TSESTree.CatchClause): TSESTree.BlockStatement {\n return catchNode.parent.block;\n}\n\n/** Constructors that throw on malformed input. */\nconst SAFE_PARSE_CONSTRUCTORS: ReadonlySet<string> = new Set([\n \"RegExp\",\n \"URL\",\n \"URLPattern\",\n]);\n\n/**\n * A parse-style call that throws on bad input: `JSON.parse(x)`, `YAML.parse(x)`,\n * `new RegExp(x)`, `new URL(x)`.\n */\nfunction isParseShapedNode(node: TSESTree.Node): boolean {\n if (\n node.type === AST_NODE_TYPES.CallExpression &&\n node.callee.type === AST_NODE_TYPES.MemberExpression &&\n !node.callee.computed &&\n node.callee.property.type === AST_NODE_TYPES.Identifier\n ) {\n return node.callee.property.name === \"parse\";\n }\n if (\n node.type === AST_NODE_TYPES.NewExpression &&\n node.callee.type === AST_NODE_TYPES.Identifier\n ) {\n return SAFE_PARSE_CONSTRUCTORS.has(node.callee.name);\n }\n return false;\n}\n\n/** Body-decoding methods of a `Request` / `Response` — they throw on bad input. */\nconst BODY_DECODE_METHODS: ReadonlySet<string> = new Set([\n \"json\",\n \"text\",\n \"arrayBuffer\",\n]);\n\nfunction isBodyDecodeNode(node: TSESTree.Node): boolean {\n return (\n node.type === AST_NODE_TYPES.CallExpression &&\n node.callee.type === AST_NODE_TYPES.MemberExpression &&\n !node.callee.computed &&\n node.callee.property.type === AST_NODE_TYPES.Identifier &&\n BODY_DECODE_METHODS.has(node.callee.property.name)\n );\n}\n\n/**\n * True when a return statement's argument SUBTREE contains a match. The whole\n * subtree is searched rather than the top node alone, because the parse is\n * usually consumed in place: `return new URL(s).protocol === \"https:\"` is a\n * `BinaryExpression`, not a `NewExpression`, and `return await request.json()`\n * is an `AwaitExpression`. Nested function scopes are not searched — a parse\n * inside a callback does not run when the `try` does.\n */\nfunction returnsMatching(\n stmt: TSESTree.Node,\n predicate: (node: TSESTree.Node) => boolean,\n): boolean {\n return (\n stmt.type === AST_NODE_TYPES.ReturnStatement &&\n stmt.argument !== null &&\n walkWithinScope(stmt.argument, predicate)\n );\n}\n\n/** The declared return type annotation of the nearest enclosing function, or null. */\nfunction enclosingReturnTypeNode(\n node: TSESTree.Node,\n): TSESTree.TypeNode | null {\n let current: TSESTree.Node | undefined | null = node.parent;\n while (current !== undefined && current !== null) {\n if (isFunctionNode(current) && \"returnType\" in current) {\n return current.returnType?.typeAnnotation ?? null;\n }\n current = current.parent;\n }\n return null;\n}\n\n/**\n * Function names that declare a boolean contract as clearly as a `: boolean`\n * annotation does. TypeScript INFERS the return type of\n * `async function isDirectory(d: string) { try { … } catch { return false } }`,\n * so demanding the annotation made the predicate exemption depend on a style\n * choice rather than on the contract.\n *\n * Corpus evidence (2220 files across zod / TanStack Query / react-router / swr /\n * zustand, 2026-07): 7 of the rule's 10 hits were unannotated predicates —\n * `react-router/packages/create-react-router/utils.ts:196-202` (`directoryExists`),\n * `:205-211` (`fileExists`), and\n * `react-router/packages/react-router/lib/rsc/server.rsc.ts:1501-1507`\n * (`isClientReference`). `false` is the ANSWER those functions exist to give;\n * there is no richer value a caller could have received.\n */\nconst PREDICATE_NAME_RE = /^(is|has|can|should|must|does|did|was|were|are)[A-Z]/;\nconst PREDICATE_SUFFIX_RE = /(Exists?|Available|Enabled|Disabled)$/;\n\n/** The name of the nearest enclosing function, or null for an anonymous one. */\nfunction enclosingFunctionName(node: TSESTree.Node): string | null {\n let current: TSESTree.Node | undefined | null = node.parent;\n while (current !== undefined && current !== null) {\n if (isFunctionNode(current)) {\n if (\n \"id\" in current &&\n isNode(current.id) &&\n current.id.type === AST_NODE_TYPES.Identifier\n ) {\n return current.id.name;\n }\n const parent = current.parent;\n if (\n parent?.type === AST_NODE_TYPES.VariableDeclarator &&\n parent.id.type === AST_NODE_TYPES.Identifier\n ) {\n return parent.id.name;\n }\n return null;\n }\n current = current.parent;\n }\n return null;\n}\n\n/**\n * True when the enclosing function's NAME declares it a predicate and the catch\n * returns a boolean. Same reasoning as `isDeclaredBooleanPredicate`, applied to\n * the (very common) unannotated spelling.\n */\nfunction isNamedBooleanPredicate(\n catchNode: TSESTree.CatchClause,\n kind: SentinelKind,\n): boolean {\n if (kind !== \"boolean\") {\n return false;\n }\n const name = enclosingFunctionName(catchNode);\n return (\n name !== null &&\n (PREDICATE_NAME_RE.test(name) || PREDICATE_SUFFIX_RE.test(name))\n );\n}\n\n/**\n * True when the enclosing function is a DECLARED predicate — annotated\n * `: boolean` (or `: Promise<boolean>`) — and the catch returns `false`. A\n * boolean return type cannot carry error information at all, so \"return a typed\n * Result\" is not advice that applies; the sentinel IS the whole contract. Only\n * `boolean` qualifies: a declared `T | null` return is the shape this rule\n * exists to flag and must keep firing.\n */\nfunction isDeclaredBooleanPredicate(\n catchNode: TSESTree.CatchClause,\n kind: SentinelKind,\n): boolean {\n if (kind !== \"boolean\") {\n return false;\n }\n let declared = enclosingReturnTypeNode(catchNode);\n if (\n declared?.type === AST_NODE_TYPES.TSTypeReference &&\n declared.typeName.type === AST_NODE_TYPES.Identifier &&\n declared.typeName.name === \"Promise\"\n ) {\n declared = declared.typeArguments?.params[0] ?? null;\n }\n return declared?.type === AST_NODE_TYPES.TSBooleanKeyword;\n}\n\n/**\n * Does the try body return a safe-parse-style expression?\n *\n * `JSON.parse` / `new RegExp` / `new URL` count from anywhere in the try body —\n * a read-then-parse (`const t = read(p); return JSON.parse(t)`) is still the\n * safe-parse contract. A body-decoding method (`.json()`) counts ONLY when it is\n * the try's sole statement, so `try { return await request.json() }` is exempt\n * while `try { const res = await fetch(url); return await res.json() }` is not:\n * there the catch also swallows the network failure, which is the true positive.\n */\nfunction tryReturnsSafeParse(catchNode: TSESTree.CatchClause): boolean {\n const tryBlock = tryBlockOf(catchNode);\n if (\n walkWithinScope(tryBlock, (current) =>\n returnsMatching(current, isParseShapedNode),\n )\n ) {\n return true;\n }\n const only = tryBlock.body.length === 1 ? tryBlock.body[0] : undefined;\n return only !== undefined && returnsMatching(only, isBodyDecodeNode);\n}\n\n/** The nearest enclosing function body, or null. */\nfunction enclosingFunctionBody(\n node: TSESTree.Node,\n): TSESTree.BlockStatement | null {\n let current: TSESTree.Node | undefined | null = node.parent;\n while (current !== undefined && current !== null) {\n if (\n isFunctionNode(current) &&\n \"body\" in current &&\n isNode(current.body) &&\n current.body.type === AST_NODE_TYPES.BlockStatement\n ) {\n return current.body;\n }\n current = current.parent;\n }\n return null;\n}\n\n/**\n * True when the enclosing function returns the same sentinel kind on a normal\n * (non-catch) path — a boolean predicate, a `T | undefined` accessor, etc. —\n * so the catch sentinel is the declared contract, not a swallow. Returns inside\n * the flagged catch itself are excluded.\n */\nfunction functionReturnsSameSentinelKindElsewhere(\n catchNode: TSESTree.CatchClause,\n kind: SentinelKind,\n): boolean {\n const functionBody = enclosingFunctionBody(catchNode);\n if (functionBody === null) {\n return false;\n }\n return walkWithinScope(functionBody, (current) => {\n if (current.type !== AST_NODE_TYPES.ReturnStatement) {\n return false;\n }\n if (isWithin(current, catchNode.body)) {\n return false;\n }\n return returnedSentinelKinds(current.argument).has(kind);\n });\n}\n\n/**\n * The sentinel kinds a returned expression can evaluate to. A bare sentinel\n * yields one; a ternary or a `??` / `||` fallback yields the kinds of its\n * branches, because `return valid ? value : false` puts `false` on a NORMAL\n * path just as plainly as `return false` does.\n *\n * Corpus evidence (2026-07 sweep): the remaining 3 of the rule's 10 hits were\n * this spelling —\n * `react-router/packages/react-router/lib/server-runtime/crypto.ts:30`\n * (`return valid ? value : false;` on the happy path, then `return false` in the\n * catch for an unparseable signature) and\n * `react-router/scripts/utils/git.ts:12` (`return typeof parsed.version ===\n * \"string\" ? parsed.version : null`). In both, the catch sentinel is the value\n * the function already documents for \"no result\", so it hides nothing.\n */\nfunction returnedSentinelKinds(\n arg: TSESTree.Expression | null,\n): ReadonlySet<SentinelKind> {\n const kinds = new Set<SentinelKind>();\n if (arg === null) {\n return kinds;\n }\n const direct = sentinelKind(arg);\n if (direct !== null) {\n kinds.add(direct);\n return kinds;\n }\n if (arg.type === AST_NODE_TYPES.ConditionalExpression) {\n for (const branch of [arg.consequent, arg.alternate]) {\n for (const nested of returnedSentinelKinds(branch)) {\n kinds.add(nested);\n }\n }\n } else if (\n arg.type === AST_NODE_TYPES.LogicalExpression &&\n (arg.operator === \"??\" || arg.operator === \"||\")\n ) {\n for (const nested of returnedSentinelKinds(arg.right)) {\n kinds.add(nested);\n }\n }\n return kinds;\n}\n\n/** Is `node` inside `ancestor`'s subtree? */\nfunction isWithin(node: TSESTree.Node, ancestor: TSESTree.Node): boolean {\n let current: TSESTree.Node | undefined | null = node;\n while (current !== undefined && current !== null) {\n if (current === ancestor) {\n return true;\n }\n current = current.parent;\n }\n return false;\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-sentinel-return-on-catch\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Disallow swallowing a caught error by returning an empty sentinel (`null`, `undefined`, `false`, `[]`, `{}`) as the final statement of a `catch` block, unless the error is logged/reported or the sentinel is the declared safe-parse/predicate contract.\",\n },\n schema: [\n {\n type: \"object\",\n additionalProperties: false,\n properties: { ...LOGGING_OPTION_PROPERTIES },\n },\n ],\n messages: {\n noSentinelReturn:\n \"This `catch` block swallows the error by returning an empty sentinel without logging it. Rethrow it, log/report it, or return a typed Result.\",\n },\n },\n defaultOptions: [{}],\n create(context, [loggingOptions]) {\n if (isGeneratedFile(context.filename, context.sourceCode.text)) {\n return {};\n }\n\n const matcher = createLogMatcher(loggingOptions);\n\n /**\n * Does the catch body log or report the caught error before the sentinel\n * return? Either a logging call (logger receiver or a declared\n * `logFunctions` free function), or an error-reporting call whose name\n * matches `REPORT_NAME_RE` and whose arguments mention the caught binding.\n */\n function logsOrReportsError(\n catchBody: TSESTree.BlockStatement,\n caughtName: string | null,\n ): boolean {\n return walkWithinScope(catchBody, (current) => {\n if (current.type !== AST_NODE_TYPES.CallExpression) {\n return false;\n }\n if (matcher.isLoggingCall(current)) {\n return true;\n }\n const name = calleeName(current.callee);\n return (\n name !== null &&\n REPORT_NAME_RE.test(name) &&\n argsIncludeBinding(current.arguments, caughtName)\n );\n });\n }\n\n return {\n CatchClause(node: TSESTree.CatchClause): void {\n const body = node.body.body;\n if (body.length === 0) {\n return;\n }\n\n const last = body[body.length - 1];\n if (last === undefined || last.type !== AST_NODE_TYPES.ReturnStatement) {\n return;\n }\n\n if (!isSentinelArgument(last.argument)) {\n return;\n }\n\n if (containsThrow(node.body)) {\n return;\n }\n\n const caughtName =\n node.param?.type === AST_NODE_TYPES.Identifier\n ? node.param.name\n : null;\n\n if (logsOrReportsError(node.body, caughtName)) {\n return;\n }\n\n if (tryReturnsSafeParse(node)) {\n return;\n }\n\n const kind = sentinelKind(last.argument);\n if (kind !== null && isNamedBooleanPredicate(node, kind)) {\n return;\n }\n\n if (kind !== null && isDeclaredBooleanPredicate(node, kind)) {\n return;\n }\n\n if (\n kind !== null &&\n functionReturnsSameSentinelKindElsewhere(node, kind)\n ) {\n return;\n }\n\n context.report({\n node: last,\n messageId: \"noSentinelReturn\",\n });\n },\n };\n },\n});\n","/**\n * @fileoverview Disallow string accumulation via `+=` inside a loop, which is\n * the classic O(n^2) string-building antipattern: each `+=` rebuilds the whole\n * string. Push the parts onto an array and `arr.join(\"\")` after the loop\n * instead.\n *\n * This is a purely SYNTACTIC rule — it uses scope analysis (not the type\n * service) to confirm the left-hand side was declared with a string-literal\n * initializer (`let s = \"\"`, `= \"...\"`, or a template literal). It is\n * deliberately conservative: when the initializer type cannot be determined\n * (no initializer, a non-literal expression, a parameter, etc.) the `+=` is\n * NOT flagged. This mirrors the Python rule SARJ002.\n *\n * Both the compound `s += x` and the equivalent longhand `s = s + x` (a plain\n * `=` assignment whose RHS is a `+` `BinaryExpression` with the target as one\n * operand) are detected — they have identical O(n^2) behavior. A plain\n * `s = x + y` (target absent from the RHS) is NOT flagged.\n *\n * AT MOST ONE REPORT PER (accumulator, loop). A single loop that appends in\n * several branches is ONE defect with ONE fix — replace the accumulator with an\n * array of parts — so N reports on it are N-1 copies of the same finding, and\n * silencing it costs N disable comments. Corpus sweep (2220 files across zod /\n * TanStack Query / react-router / swr / zustand, 2026-07): 62 raw reports\n * collapsed to 21 distinct defects. `react-router/packages/react-router-fs-routes/flatRoutes.ts`\n * alone produced 23 reports; `react-router/packages/react-router/lib/server-runtime/cookies.ts:221-231`\n * produced 6, all of them the one percent-encoder loop appending `result` from\n * four branches. This matches the one-report-per-loop policy `no-sequential-await`\n * already follows.\n *\n * EXEMPTION — AN ACCUMULATOR DECLARED INSIDE THE LOOP BODY (bulbul PR #4111).\n * The O(n^2) claim requires the accumulator to survive across iterations. A\n * `let s = \"...\"` declared *inside* the body is rebound to a fresh string every\n * pass, so the `+=` runs a bounded number of times on a string that is discarded\n * at the end of the iteration — there is no quadratic growth for `join` to\n * remove, and the parts are typically already being collected into an array.\n * Evidence:\n * `typescript/packages/app/src/lib/lexical/plugins/utils/serializes-state-to-text.ts:16`,\n * where `let sectionText = \\`## ${…}\\`` is declared in the body, appended to at\n * most once behind an `if (body)`, and then pushed onto `textParts` for a\n * `textParts.join()` after the loop. The disable there reads \"single conditional\n * append to a per-iteration string; result is collected via textParts.join below,\n * not O(n²)\" — the rule had nothing to offer.\n *\n * The Python twin SARJ002 (`inefficient_string_concat_in_loop`) has drawn this\n * same line since it shipped: \"A target that is freshly (re)bound earlier in the\n * same loop body … is loop-local: it starts empty each iteration, so the growth\n * is bounded, not cross-iteration accumulation.\" The two rules now agree.\n */\n\nimport { ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\nimport type { Scope } from \"@typescript-eslint/utils/ts-eslint\";\n\nimport { isGeneratedFile } from \"./_paths.js\";\n\ntype MessageIds = \"noStringConcatInLoop\";\ntype Options = readonly [];\n\nconst LOOP_NODE_TYPES = new Set<string>([\n \"ForStatement\",\n \"ForOfStatement\",\n \"ForInStatement\",\n \"WhileStatement\",\n \"DoWhileStatement\",\n]);\n\n/**\n * Returns true if the given expression node is a string-producing literal:\n * a string `Literal` (`\"\"`, `\"...\"`, `'...'`) or a `TemplateLiteral`.\n */\nfunction isStringLiteralInit(node: TSESTree.Expression | null): boolean {\n if (node === null) {\n return false;\n }\n if (node.type === \"TemplateLiteral\") {\n return true;\n }\n if (node.type === \"Literal\") {\n return typeof node.value === \"string\";\n }\n return false;\n}\n\n/**\n * Walk the chain of enclosing scopes to find the variable definition for the\n * given identifier name. Returns the resolved `Variable`, or `undefined` if it\n * cannot be found (e.g. an undeclared global or an out-of-scope reference).\n */\nfunction findVariable(\n scope: Scope.Scope,\n name: string,\n): Scope.Variable | undefined {\n let current: Scope.Scope | null = scope;\n while (current !== null) {\n const variable = current.variables.find((v) => v.name === name);\n if (variable !== undefined) {\n return variable;\n }\n current = current.upper;\n }\n return undefined;\n}\n\n/**\n * Returns true if the variable was declared with a string-literal initializer.\n * Conservative: if the variable has no single string-initialized declarator\n * (no init, non-literal init, multiple conflicting declarators), returns false.\n */\nfunction isStringInitializedVariable(variable: Scope.Variable): boolean {\n // A variable can technically have multiple declarators (e.g. via `var`\n // hoisting / redeclaration). Only treat it as string-initialized when there\n // is exactly one declarator and it has a string-literal initializer.\n if (variable.defs.length !== 1) {\n return false;\n }\n const def = variable.defs[0];\n if (def === undefined || def.type !== \"Variable\") {\n // Parameters, function names, imports, etc. — type unknown, don't flag.\n return false;\n }\n const declarator = def.node;\n if (declarator.type !== \"VariableDeclarator\") {\n return false;\n }\n return isStringLiteralInit(declarator.init);\n}\n\n/**\n * Returns true if `target` appears as a direct operand of a `+` expression\n * (following left-associative `+` chains, e.g. `s + a + b`). Used to recognize\n * the longhand `s = s + <...>` reassignment, which has the same O(n^2) cost as\n * `s += <...>`. A non-`+` operand (`foo.s`, `s.slice()`, `x + y`) does not match.\n */\nfunction isConcatOperand(\n node: TSESTree.Expression | TSESTree.PrivateIdentifier,\n target: string,\n): boolean {\n if (node.type === \"Identifier\") {\n return node.name === target;\n }\n if (node.type === \"BinaryExpression\" && node.operator === \"+\") {\n return (\n isConcatOperand(node.left, target) || isConcatOperand(node.right, target)\n );\n }\n return false;\n}\n\n/**\n * Returns true if `rhs` is a `+` `BinaryExpression` in which `target` appears as\n * an operand — the reassignment shape `s = s + x` / `s = x + s` / `s = s + a + b`.\n */\nfunction isConcatOntoTarget(\n rhs: TSESTree.Expression,\n target: string,\n): boolean {\n if (rhs.type !== \"BinaryExpression\" || rhs.operator !== \"+\") {\n return false;\n }\n return isConcatOperand(rhs.left, target) || isConcatOperand(rhs.right, target);\n}\n\n/**\n * Returns true when the accumulator's declaration lives inside `loop`'s BODY, so\n * a fresh string is bound on every iteration.\n *\n * `let s = \"\"` before the loop is the cross-iteration accumulator this rule\n * targets. `let s = \"\"` inside the body is not: the `+=` runs a bounded number of\n * times against a string that is discarded at the end of the pass, and there is\n * no quadratic growth to remove — the parts are usually already collected into an\n * array afterwards. Compared by source range, which is exact for a declaration\n * that is lexically nested in the body.\n */\nfunction isDeclaredInsideLoop(\n variable: Scope.Variable,\n loop: TSESTree.Node,\n): boolean {\n const def = variable.defs[0];\n if (def === undefined) {\n return false;\n }\n const body = (\n loop as\n | TSESTree.ForStatement\n | TSESTree.ForOfStatement\n | TSESTree.ForInStatement\n | TSESTree.WhileStatement\n | TSESTree.DoWhileStatement\n ).body;\n const [declStart, declEnd] = def.node.range;\n const [bodyStart, bodyEnd] = body.range;\n return declStart >= bodyStart && declEnd <= bodyEnd;\n}\n\n/**\n * Returns true if `node` is contained within the body of a loop statement.\n * Walks ancestors and, for each loop, ensures the node is inside the loop's\n * BODY (not its test/init/update clauses, which run a bounded number of times\n * relative to the body and aren't the antipattern we target).\n */\nfunction enclosingLoop(node: TSESTree.Node): TSESTree.Node | null {\n let child: TSESTree.Node = node;\n let parent = node.parent;\n while (parent !== undefined && parent !== null) {\n if (LOOP_NODE_TYPES.has(parent.type)) {\n // `body` is the property that holds the looped statements for every\n // loop variant we care about.\n const loop = parent as\n | TSESTree.ForStatement\n | TSESTree.ForOfStatement\n | TSESTree.ForInStatement\n | TSESTree.WhileStatement\n | TSESTree.DoWhileStatement;\n if (loop.body === child) {\n return loop;\n }\n }\n child = parent;\n parent = parent.parent;\n }\n return null;\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-string-concat-in-loop\",\n meta: {\n type: \"suggestion\",\n docs: {\n description:\n \"Disallow O(n^2) string building via `+=` on a string variable inside a loop; push parts to an array and `join` instead.\",\n },\n schema: [],\n messages: {\n noStringConcatInLoop:\n \"Avoid building a string with `+=` inside a loop — this is O(n^2). Push the parts onto an array and use `arr.join(\\\"\\\")` after the loop.\",\n },\n },\n defaultOptions: [],\n create(context) {\n if (isGeneratedFile(context.filename, context.sourceCode.text)) {\n return {};\n }\n\n // One defect per (accumulator, loop) — see @fileoverview. Keyed on the loop\n // node so sibling loops over the same variable each keep their report.\n const reported = new WeakMap<TSESTree.Node, Set<string>>();\n\n return {\n AssignmentExpression(node: TSESTree.AssignmentExpression): void {\n // The LHS must be a plain variable reference.\n if (node.left.type !== \"Identifier\") {\n return;\n }\n // Accept both the compound `s += x` and the longhand `s = s + x`; any\n // other operator/shape (`s = x + y`, `s -= x`, ...) is not accumulation.\n const isAccumulation =\n node.operator === \"+=\" ||\n (node.operator === \"=\" &&\n isConcatOntoTarget(node.right, node.left.name));\n if (!isAccumulation) {\n return;\n }\n // Must occur inside a loop body, else it's a one-shot append.\n const loop = enclosingLoop(node);\n if (loop === null) {\n return;\n }\n\n const scope = context.sourceCode.getScope(node);\n const variable = findVariable(scope, node.left.name);\n // Conservative: can't resolve the declaration -> don't flag.\n if (variable === undefined) {\n return;\n }\n // Only flag when we can confirm the LHS was string-initialized; a\n // numeric initializer (or anything non-string) is intentionally\n // excluded to avoid false positives.\n if (!isStringInitializedVariable(variable)) {\n return;\n }\n // The O(n^2) claim requires the accumulator to SURVIVE across iterations.\n // One declared inside the loop body is a fresh string every pass, so its\n // length is bounded by one iteration's work and `join` cannot replace it.\n // See @fileoverview for the PR #4111 evidence.\n if (isDeclaredInsideLoop(variable, loop)) {\n return;\n }\n\n let seen = reported.get(loop);\n if (seen === undefined) {\n seen = new Set<string>();\n reported.set(loop, seen);\n }\n if (seen.has(node.left.name)) {\n return;\n }\n seen.add(node.left.name);\n\n context.report({\n node,\n messageId: \"noStringConcatInLoop\",\n });\n },\n };\n },\n});\n","/**\n * @fileoverview Flag `'use client'` files with no hooks or event handlers.\n *\n * If a file is marked `'use client'` but contains no hook calls\n * (`useState`/`useEffect`/etc.), no JSX event handlers (`onClick`,\n * `onChange`, etc.), no browser globals, no client-only imports, and no\n * other client-side indicators (classes, re-exports), the directive is\n * likely unnecessary and the file could be a React Server Component —\n * improving cold-start, bundle size, and SEO.\n *\n * False-positive watch: components that only use client-side context\n * (e.g. theme providers) without hooks or events still need `'use client'`.\n *\n * TWO MORE CLIENT INDICATORS, ADDED AFTER A CORPUS SWEEP (2220 files across\n * zod / TanStack Query / react-router / swr / zustand, 2026-07 — 12 hits, all\n * false positives):\n *\n * 1. **Rendering a component imported from a THIRD-PARTY package.** React\n * documents `'use client'` at the top of a wrapper as the way to mark a\n * dependency's components as client components, and this rule cannot see\n * into `node_modules` to know whether the dependency needs it.\n * `CLIENT_ONLY_PACKAGES_REGEX` was a hand-maintained approximation of the\n * same idea and is necessarily incomplete — it did not know about\n * `fumadocs-ui` (`zod/packages/docs/components/tabs.tsx:1`, which renders\n * `<Primitive.Tabs>`), about `swr` itself\n * (`swr/examples/suspense-global/global-swr-config.tsx:1`, a `<SWRConfig>`\n * provider), or about `next/image` and `lucide-react`\n * (`zod/packages/docs/components/themed-image.tsx:1`,\n * `.../heading.tsx:1`). A component imported by a RELATIVE path lives in\n * the same repo, is linted by this same rule, and still fires — so the\n * narrowing costs nothing where the rule can actually see the answer.\n * 2. **Aliasing an import into a public export** —\n * `import * as Devtools from './ReactQueryDevtools';\n * export const ReactQueryDevtools = … Devtools.ReactQueryDevtools`\n * (`query/packages/react-query-devtools/src/index.ts:1`, and `production.ts`).\n * That is a re-export written the long way, and `export … from` was already\n * treated as an indicator; the two spellings now agree.\n *\n * ONE MORE INDICATOR FROM bulbul PR #4111:\n *\n * 3. **Importing `next/dynamic`.** In the App Router, `dynamic(…, { ssr: false })`\n * is a hard BUILD ERROR inside a Server Component — Next.js rejects it with\n * \"`ssr: false` is not allowed with `next/dynamic` in Server Components\".\n * A lazy-wrapper module therefore has NO legal form without the directive,\n * so the rule was demanding something the framework forbids and the only\n * available response was a disable comment. These wrappers also look\n * maximally \"unnecessary\" to the old predicate: one `dynamic()` call, no\n * hooks, no handlers, no browser globals.\n * `typescript/packages/app/src/app/dashboard/call-volume-chart-lazy.tsx:1`\n * (deferring the recharts bundle off the server dashboard page) and\n * `typescript/packages/app/src/components/rich-text-editor/rich-text-editor-lazy.tsx:1`\n * (deferring the Lexical bundle off /agents and /admin/global-prompts).\n *\n * HONEST SCOPE: both of those files also happen to be covered by indicator\n * 2, since each EXPORTS a const whose initializer reads the imported\n * `dynamic`. Indicator 3 is therefore defense-in-depth, not the thing that\n * currently silences them: it is what covers the same wrapper when the lazy\n * component is module-internal (`const Editor = dynamic(…); export function\n * Page() { return <Editor />; }`), where indicator 2 does not reach and the\n * framework constraint is identical. The test suite pins exactly that shape.\n *\n * All FOUR of the repo's `no-unnecessary-use-client` disables are consequently\n * stale rather than live false positives — including\n * `app/src/app/scenarios/organization-selector-wrapper.tsx:1` and its batch-calls\n * twin, which pass a hook adapter as a function prop and are already exempt via\n * indicator 2. The valid-case suite pins all of them so a future narrowing of\n * indicator 2 cannot silently reintroduce the reports.\n *\n * References:\n * - https://nextjs.org/docs/app/building-your-application/rendering/client-components\n * - https://react.dev/reference/rsc/use-client (wrapping third-party components)\n * - https://nextjs.org/docs/app/api-reference/functions/dynamic (`ssr: false`)\n */\n\nimport {\n AST_NODE_TYPES,\n ESLintUtils,\n type TSESTree,\n} from \"@typescript-eslint/utils\";\nimport type { RuleContext, Scope } from \"@typescript-eslint/utils/ts-eslint\";\n\ntype MessageIds = \"unnecessaryUseClient\";\ntype Options = readonly [];\n\nconst HOOK_REGEX = /^use([A-Z]|$)/;\nconst EVENT_PROP_REGEX = /^on[A-Z]/;\nconst ERROR_FILE_REGEX = /\\b(?:global-)?error\\.[jt]sx?$/;\n\nconst BROWSER_GLOBALS: ReadonlySet<string> = new Set([\n \"window\",\n \"document\",\n \"navigator\",\n \"localStorage\",\n \"sessionStorage\",\n \"location\",\n \"history\",\n \"screen\",\n \"requestAnimationFrame\",\n \"cancelAnimationFrame\",\n \"CustomEvent\",\n \"Event\",\n \"MouseEvent\",\n \"KeyboardEvent\",\n \"TouchEvent\",\n]);\n\n/**\n * Modules whose very import forces a client boundary, independent of what the\n * file then does with them. `next/dynamic` is the case that matters: in the App\n * Router, `dynamic(..., { ssr: false })` is a BUILD ERROR in a Server Component\n * (\"`ssr: false` is not allowed with `next/dynamic` in Server Components\"), so a\n * lazy-wrapper module has no legal form without `'use client'`. See @fileoverview.\n */\nconst CLIENT_REQUIRED_MODULES: ReadonlySet<string> = new Set([\"next/dynamic\"]);\n\nconst CLIENT_ONLY_PACKAGES_REGEX =\n /^(?:@radix-ui\\/|framer-motion|react-dom|react-day-picker|@floating-ui\\/|react-select|react-toastify|react-hook-form|recharts|react-dropzone|react-slick|react-swipeable|react-resizable|react-draggable|react-beautiful-dnd|@hello-pangea\\/dnd|react-virtualized|react-window|@tanstack\\/react-table|@tanstack\\/react-query|react-redux|recoil|jotai|zustand|@tippyjs\\/react|react-color|react-datepicker|next-themes|react-helmet|react-helmet-async|styled-components|@emotion\\/)/;\n\ntype Ctx = Readonly<RuleContext<MessageIds, Options>>;\n\n/**\n * True for a package specifier — anything that is not a relative path or one of\n * the usual in-repo alias prefixes. A relative/aliased import points at code in\n * this repo, which this same rule already lints, so the \"cannot see into the\n * dependency\" argument does not apply to it.\n */\nconst isBareSpecifier = (source: string): boolean =>\n !source.startsWith(\".\") && !source.startsWith(\"/\") && !source.startsWith(\"@/\") && !source.startsWith(\"~\");\n\n/** The leftmost identifier of a JSX element name: `Primitive.Tabs` -> `Primitive`. */\nconst jsxRootName = (name: TSESTree.JSXTagNameExpression): string => {\n let current: TSESTree.JSXTagNameExpression = name;\n while (current.type === AST_NODE_TYPES.JSXMemberExpression) {\n current = current.object;\n }\n return current.type === AST_NODE_TYPES.JSXIdentifier ? current.name : \"\";\n};\n\n/** True when any identifier in `node`'s subtree names an imported binding. */\nconst subtreeReadsImportedBinding = (\n node: TSESTree.Node,\n imported: ReadonlySet<string>,\n): boolean => {\n if (node.type === AST_NODE_TYPES.Identifier) {\n return imported.has(node.name);\n }\n for (const key of Object.keys(node) as (keyof TSESTree.Node)[]) {\n if (key === \"parent\") continue;\n const value = node[key];\n for (const child of (Array.isArray(value) ? value : [value]) as unknown[]) {\n if (\n child !== null &&\n typeof child === \"object\" &&\n typeof (child as { type?: unknown }).type === \"string\" &&\n subtreeReadsImportedBinding(child as TSESTree.Node, imported)\n ) {\n return true;\n }\n }\n }\n return false;\n};\n\nconst isUseClientDirective = (\n node: TSESTree.Statement,\n): node is TSESTree.ExpressionStatement => {\n return (\n node.type === AST_NODE_TYPES.ExpressionStatement &&\n node.expression.type === AST_NODE_TYPES.Literal &&\n node.expression.value === \"use client\"\n );\n};\n\nconst isGlobalReference = (\n node: TSESTree.Identifier,\n context: Ctx,\n): boolean => {\n if (!BROWSER_GLOBALS.has(node.name)) return false;\n\n const parent = node.parent;\n if (parent !== undefined) {\n // `obj.window` — `window` is a property name, not a global reference.\n if (\n parent.type === AST_NODE_TYPES.MemberExpression &&\n parent.property === node &&\n !parent.computed\n ) {\n return false;\n }\n // `{ window: ... }` — property key, not a global reference.\n if (\n parent.type === AST_NODE_TYPES.Property &&\n parent.key === node &&\n !parent.computed\n ) {\n return false;\n }\n // Type annotations / type-only positions are not runtime references.\n if (parent.type.startsWith(\"TS\")) {\n return false;\n }\n }\n\n // If there's a local binding for this name anywhere up the chain, it's not\n // a reference to the browser global.\n let scope: Scope.Scope | null = context.sourceCode.getScope(node);\n while (scope !== null) {\n const variable = scope.set.get(node.name);\n if (variable !== undefined && variable.defs.length > 0) {\n return false;\n }\n scope = scope.upper;\n }\n\n return true;\n};\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-unnecessary-use-client\",\n meta: {\n type: \"suggestion\",\n docs: {\n description:\n \"Flag `'use client'` files with no hooks or event handlers — they could be RSC.\",\n },\n schema: [],\n messages: {\n unnecessaryUseClient:\n \"'use client' directive but no hooks (use*), JSX event handlers (on*), browser globals, or client-only imports found. Consider removing the directive and serving as a React Server Component.\",\n },\n },\n defaultOptions: [],\n create(context) {\n const filename = context.filename;\n if (ERROR_FILE_REGEX.test(filename)) {\n return {};\n }\n\n let directiveNode: TSESTree.ExpressionStatement | null = null;\n let hasClientIndicator = false;\n /** Every local name bound by an import, whatever the module. */\n const importedLocals = new Set<string>();\n /** Locals bound from a BARE (third-party) specifier — see @fileoverview. */\n const externalLocals = new Set<string>();\n\n const markIfHookOrContext = (\n callee: TSESTree.CallExpression[\"callee\"],\n ): void => {\n if (callee.type === AST_NODE_TYPES.Identifier) {\n if (HOOK_REGEX.test(callee.name) || callee.name === \"createContext\") {\n hasClientIndicator = true;\n }\n return;\n }\n if (\n callee.type === AST_NODE_TYPES.MemberExpression &&\n callee.property.type === AST_NODE_TYPES.Identifier\n ) {\n const name = callee.property.name;\n if (HOOK_REGEX.test(name) || name === \"createContext\") {\n hasClientIndicator = true;\n }\n }\n };\n\n return {\n Program(node): void {\n for (const stmt of node.body) {\n // Directives must be the first statements; once we see a non-\n // ExpressionStatement, stop scanning.\n if (stmt.type !== AST_NODE_TYPES.ExpressionStatement) break;\n if (isUseClientDirective(stmt)) {\n directiveNode = stmt;\n break;\n }\n }\n },\n CallExpression(node): void {\n // The `Program` visitor (entered first) has already determined whether\n // this file has a `'use client'` directive. If it doesn't, the result\n // can't change — skip all of the per-node indicator work, including the\n // hot scope-resolution in the `Identifier` visitor below.\n if (directiveNode === null) return;\n markIfHookOrContext(node.callee);\n },\n JSXAttribute(node): void {\n if (directiveNode === null) return;\n if (\n node.name.type === AST_NODE_TYPES.JSXIdentifier &&\n EVENT_PROP_REGEX.test(node.name.name)\n ) {\n hasClientIndicator = true;\n }\n },\n ImportDeclaration(node): void {\n if (directiveNode === null) return;\n if (typeof node.source.value !== \"string\") return;\n const source = node.source.value;\n if (\n CLIENT_ONLY_PACKAGES_REGEX.test(source) ||\n CLIENT_REQUIRED_MODULES.has(source)\n ) {\n hasClientIndicator = true;\n }\n for (const specifier of node.specifiers) {\n importedLocals.add(specifier.local.name);\n if (isBareSpecifier(source)) {\n externalLocals.add(specifier.local.name);\n }\n }\n },\n JSXOpeningElement(node): void {\n if (directiveNode === null) return;\n // Indicator 1: rendering a third-party component — this rule cannot see\n // whether the dependency itself needs a client boundary.\n if (externalLocals.has(jsxRootName(node.name))) {\n hasClientIndicator = true;\n }\n },\n ExportNamedDeclaration(node): void {\n if (directiveNode === null) return;\n if (node.source !== null) {\n hasClientIndicator = true;\n return;\n }\n // Indicator 2: `export const X = SomeImport.X` — a re-export written\n // the long way, so it must agree with the `export … from` branch above.\n if (\n node.declaration !== null &&\n subtreeReadsImportedBinding(node.declaration, importedLocals)\n ) {\n hasClientIndicator = true;\n }\n },\n ExportAllDeclaration(node): void {\n if (directiveNode === null) return;\n if (node.source !== null) {\n hasClientIndicator = true;\n }\n },\n ClassDeclaration(): void {\n if (directiveNode === null) return;\n hasClientIndicator = true;\n },\n ClassExpression(): void {\n if (directiveNode === null) return;\n hasClientIndicator = true;\n },\n Identifier(node): void {\n if (directiveNode === null) return;\n if (isGlobalReference(node, context)) {\n hasClientIndicator = true;\n }\n },\n \"Program:exit\"(): void {\n if (directiveNode !== null && !hasClientIndicator) {\n context.report({\n node: directiveNode,\n messageId: \"unnecessaryUseClient\",\n });\n }\n },\n };\n },\n});\n","/**\n * @fileoverview Flag object types that model mutually-exclusive states with a\n * boolean status flag plus many optional members. A shape like\n * `{ success: boolean; data?: T; error?: E; code?: number }` encodes \"success\n * vs. error\" implicitly and lets illegal states (e.g. `success: true` with an\n * `error`) be representable.\n *\n * Such shapes should be modelled as a discriminated union, e.g.\n * `{ ok: true; data: T } | { ok: false; error: E }`, so the compiler enforces\n * that exactly one branch's fields are present.\n *\n * This is the TypeScript mirror of the Python rule SARJ005.\n *\n * A PAYLOAD IS REQUIRED. The defect is that a payload can be present in the\n * wrong branch (`success: true` carrying an `error`), so the shape must have at\n * least one optional member that is NOT itself a boolean. An all-boolean record\n * is a FLAG SET, not a state machine: every combination of its members is legal,\n * so there is no illegal state to make unrepresentable and a discriminated union\n * would be strictly worse.\n *\n * Corpus evidence (2220 files across zod / TanStack Query / react-router / swr /\n * zustand, 2026-07): the rule fired exactly once, and it was this false\n * positive — `swr/src/_internal/types.ts:1191`,\n * `interface StateDependencies { data?: boolean; error?: boolean;\n * isValidating?: boolean; isLoading?: boolean }`. That is SWR's dependency\n * TRACKER: each flag records \"did the render read this field?\", and all sixteen\n * combinations are meaningful.\n */\n\nimport { ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\nimport { AST_NODE_TYPES } from \"@typescript-eslint/utils\";\n\ntype MessageIds = \"preferDiscriminatedUnion\";\ntype Options = readonly [];\n\n/**\n * Boolean-typed member names that read as a success/error status discriminant.\n */\nconst STATUS_MEMBER_NAMES: ReadonlySet<string> = new Set([\n \"success\",\n \"ok\",\n \"error\",\n \"failed\",\n \"isError\",\n]);\n\nconst MIN_OPTIONAL_MEMBERS = 2;\n\n/**\n * Returns the property key name for a member if it is a plain identifier or\n * string-literal property signature, otherwise `null`.\n */\nfunction getMemberName(member: TSESTree.TypeElement): string | null {\n if (member.type !== AST_NODE_TYPES.TSPropertySignature) {\n return null;\n }\n const { key } = member;\n if (key.type === AST_NODE_TYPES.Identifier) {\n return key.name;\n }\n if (key.type === AST_NODE_TYPES.Literal && typeof key.value === \"string\") {\n return key.value;\n }\n return null;\n}\n\n/**\n * Whether a property signature is annotated with `boolean`.\n */\nfunction isBooleanTyped(member: TSESTree.TSPropertySignature): boolean {\n return (\n member.typeAnnotation?.typeAnnotation.type ===\n AST_NODE_TYPES.TSBooleanKeyword\n );\n}\n\n/**\n * Returns true when the object type literal has BOTH a boolean-typed status\n * member AND at least `MIN_OPTIONAL_MEMBERS` optional members.\n */\nfunction looksLikeMutuallyExclusiveState(\n typeLiteral: TSESTree.TSTypeLiteral,\n): boolean {\n let hasStatusBoolean = false;\n let optionalCount = 0;\n let optionalPayloadCount = 0;\n\n for (const member of typeLiteral.members) {\n if (member.type !== AST_NODE_TYPES.TSPropertySignature) {\n continue;\n }\n\n if (member.optional) {\n optionalCount += 1;\n // A non-boolean optional is a PAYLOAD — the thing that can end up in the\n // wrong branch. An all-boolean record is a flag set; see @fileoverview.\n if (!isBooleanTyped(member)) {\n optionalPayloadCount += 1;\n }\n }\n\n const name = getMemberName(member);\n if (\n name !== null &&\n STATUS_MEMBER_NAMES.has(name) &&\n isBooleanTyped(member)\n ) {\n hasStatusBoolean = true;\n }\n }\n\n return (\n hasStatusBoolean &&\n optionalCount >= MIN_OPTIONAL_MEMBERS &&\n optionalPayloadCount >= 1\n );\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"prefer-discriminated-union\",\n meta: {\n type: \"suggestion\",\n docs: {\n description:\n \"Flag object types with a boolean status flag and many optionals; model them as a discriminated union instead.\",\n },\n schema: [],\n messages: {\n preferDiscriminatedUnion:\n \"This object type uses a boolean status flag alongside several optional fields, which lets illegal states be representable. Model it as a `z.discriminatedUnion` / discriminated union (e.g. `{ ok: true; data: T } | { ok: false; error: E }`) to make illegal states unrepresentable.\",\n },\n },\n defaultOptions: [],\n create(context) {\n function checkTypeLiteral(\n typeLiteral: TSESTree.TSTypeLiteral,\n reportNode: TSESTree.Node,\n ): void {\n if (looksLikeMutuallyExclusiveState(typeLiteral)) {\n context.report({\n node: reportNode,\n messageId: \"preferDiscriminatedUnion\",\n });\n }\n }\n\n return {\n TSInterfaceDeclaration(\n node: TSESTree.TSInterfaceDeclaration,\n ): void {\n // An interface body is structurally an object type literal; reuse the\n // same membership analysis by treating its `body.body` as members.\n const synthetic: TSESTree.TSTypeLiteral = {\n ...node.body,\n type: AST_NODE_TYPES.TSTypeLiteral,\n members: node.body.body,\n } as TSESTree.TSTypeLiteral;\n checkTypeLiteral(synthetic, node);\n },\n \"TSTypeAliasDeclaration > TSTypeLiteral\"(\n node: TSESTree.TSTypeLiteral,\n ): void {\n checkTypeLiteral(node, node.parent);\n },\n };\n },\n});\n","/**\n * @fileoverview Don't access `response.json()` / `JSON.parse()` fields without a\n * Zod parse first.\n *\n * Pattern flagged:\n * const data = await response.json();\n * doSomething(data.foo); // <-- unvalidated property access\n *\n * const body = JSON.parse(raw);\n * doSomething(body.foo); // <-- same `any` leak, different source\n *\n * Encouraged:\n * const data = MySchema.parse(await response.json());\n * doSomething(data.foo); // typed + validated\n *\n * const raw: unknown = JSON.parse(text); // never flagged: nothing read off it\n * const data = MySchema.parse(raw);\n *\n * Heuristic:\n * - Track variables initialized to `await someCall.json()` or `JSON.parse(x)`\n * using ESLint's scope manager.\n * - Untrack if reassigned to anything other than another raw payload source.\n * - Untrack when passed to a user-defined type-guard predicate — a call whose\n * callee name matches `/^is[A-Z]/`, or any call used in an `if`/`?:` test\n * position (`if (guard(body)) { … body.foo … }`). Hand-written guards validate\n * the payload just as a Zod `.parse()` does.\n * - Flag MemberExpression reads and destructuring off tracked variables.\n * - `.parse()` / `.safeParse()` chained directly on the json call are legit\n * and never produce a tracked binding in the first place.\n *\n * NOT FLAGGED (corpus sweep, 2220 files across zod / TanStack Query /\n * react-router / swr / zustand, 2026-07 — 86 raw hits, 50 of them these):\n * - **Test files**, 46 hits. A test parses a payload it produced itself and\n * immediately asserts on it:\n * `react-router/integration/request-test.ts:120-121`\n * (`loaderData = JSON.parse(await page.locator(\"#loader-data\").innerHTML());\n * expect(loaderData.method).toEqual(\"GET\")`). Routing that through a schema\n * would assert the schema instead of the subject, and the assertion IS the\n * validation.\n * - **Reads inside an assertion** (`expect(payload.method)`) — see\n * `isInsideAssertion`. This catches hyphen-named suites such as\n * `react-router/integration/request-test.ts` that no path predicate sees.\n * - **JSON read off local disk**, 4 hits — see `isLocalFileRead`.\n *\n * References:\n * - https://zod.dev/?id=parse\n * - https://www.totaltypescript.com/parse-don-t-validate\n */\n\nimport {\n AST_NODE_TYPES,\n ESLintUtils,\n type TSESTree,\n} from \"@typescript-eslint/utils\";\nimport type { RuleContext, Scope } from \"@typescript-eslint/utils/ts-eslint\";\n\nimport { isGeneratedFile, isTestFile } from \"./_paths.js\";\n\ntype MessageIds = \"unparsedJsonAccess\";\ntype Options = readonly [];\n\ntype Ctx = Readonly<RuleContext<MessageIds, Options>>;\n\n/**\n * Peel TypeScript wrapper nodes that don't affect the underlying value\n * (`as Foo`, `<Foo>x`, `x!`, `x satisfies Foo`, parentheses, optional chain\n * wrappers). Returns the inner expression we actually care about.\n */\nconst unwrap = (\n node: TSESTree.Node | null | undefined,\n): TSESTree.Node | null => {\n let current: TSESTree.Node | null | undefined = node;\n while (current !== null && current !== undefined) {\n if (\n current.type === AST_NODE_TYPES.TSAsExpression ||\n current.type === AST_NODE_TYPES.TSTypeAssertion ||\n current.type === AST_NODE_TYPES.TSNonNullExpression ||\n current.type === AST_NODE_TYPES.TSSatisfiesExpression\n ) {\n current = current.expression;\n } else if (current.type === AST_NODE_TYPES.ChainExpression) {\n current = current.expression;\n } else {\n break;\n }\n }\n return current ?? null;\n};\n\n/**\n * Returns true if the expression is (optionally awaited) a raw payload source:\n * `<x>.json()` (a fetch/Request body) or `JSON.parse(<x>)`.\n *\n * Both hand back `any`, and the failure mode is the same: `payload.user.id`\n * type-checks against a shape the peer never sent, and surfaces at runtime as\n * `undefined` several frames away. Note this only matters at the point a FIELD\n * is read — `const raw: unknown = JSON.parse(body)` is the recommended shape and\n * is never reported, because nothing is accessed off it.\n */\nconst isRawPayloadSource = (\n node: TSESTree.Node | null | undefined,\n): boolean => {\n let current = unwrap(node);\n if (current === null) return false;\n if (current.type === AST_NODE_TYPES.AwaitExpression) {\n current = unwrap(current.argument);\n }\n if (current === null || current.type !== AST_NODE_TYPES.CallExpression) {\n return false;\n }\n const callee = unwrap(current.callee);\n if (callee === null || callee.type !== AST_NODE_TYPES.MemberExpression) {\n return false;\n }\n const property = unwrap(callee.property);\n if (property === null || property.type !== AST_NODE_TYPES.Identifier) {\n return false;\n }\n if (property.name === \"json\") {\n return true;\n }\n // `JSON.parse(...)` specifically — not any `.parse()`, which is usually the\n // schema validation we are asking for.\n const object = unwrap(callee.object);\n return (\n property.name === \"parse\" &&\n object !== null &&\n object.type === AST_NODE_TYPES.Identifier &&\n object.name === \"JSON\" &&\n // ...but not `JSON.parse(readFileSync(p, \"utf8\"))` — see isLocalFileRead.\n !isLocalFileRead(current.arguments[0])\n );\n};\n\n/** Filesystem readers whose result is repo-local text, not a peer's payload. */\nconst FILE_READ_RE = /^(readFile|readFileSync|readJson|readJsonSync|readJSON)$/;\n\n/**\n * True when the expression tree contains a filesystem read, i.e. the JSON came\n * off local disk rather than off the wire.\n *\n * The rule's premise is that the value is \"unvalidated and attacker-controlled\".\n * `JSON.parse(readFileSync(\"package.json\", \"utf8\"))` is neither: the bytes ship\n * with the repo, nobody else can write them, and a Zod schema over a file the\n * build already depends on adds a second place to update.\n *\n * Corpus evidence (2220 files across zod / TanStack Query / react-router / swr /\n * zustand, 2026-07): `zod/scripts/check-versions.ts:13-14`\n * (`const packageJson = JSON.parse(readFileSync(packageJsonPath, \"utf8\"));\n * const packageJsonVersion = packageJson.version as string;` — and the very next\n * line is a `typeof` check), `zod/scripts/check-semver.ts:10-11`, and\n * `zod/packages/docs/app/llms-full.txt/route.ts:13-17`\n * (`JSON.parse(await fs.readFile(metaPath, \"utf-8\"))` over the docs' own\n * `meta.json`). A `response.json()` — the actual trust boundary — is unaffected.\n */\nconst isLocalFileRead = (node: TSESTree.Node | null | undefined): boolean => {\n let found = false;\n const visit = (current: TSESTree.Node | null | undefined): void => {\n if (found || current === null || current === undefined) return;\n if (current.type === AST_NODE_TYPES.CallExpression) {\n const callee = unwrap(current.callee);\n const name =\n callee?.type === AST_NODE_TYPES.Identifier\n ? callee.name\n : callee?.type === AST_NODE_TYPES.MemberExpression &&\n !callee.computed &&\n callee.property.type === AST_NODE_TYPES.Identifier\n ? callee.property.name\n : null;\n if (name !== null && FILE_READ_RE.test(name)) {\n found = true;\n return;\n }\n }\n for (const key of Object.keys(current) as (keyof TSESTree.Node)[]) {\n if (key === \"parent\") continue;\n const value = current[key];\n for (const child of (Array.isArray(value) ? value : [value]) as unknown[]) {\n if (\n child !== null &&\n typeof child === \"object\" &&\n typeof (child as { type?: unknown }).type === \"string\"\n ) {\n visit(child as TSESTree.Node);\n }\n }\n }\n };\n visit(node);\n return found;\n};\n\n/** Assertion helpers whose argument is being checked, not consumed. */\nconst ASSERTION_CALLEE_RE = /^(expect|assert|should|invariant)$/;\n\n/**\n * True when the node sits inside an assertion call, e.g.\n * `expect(loaderData.method).toEqual(\"GET\")`.\n *\n * The rule's premise is that the field is READ and trusted. Inside an assertion\n * it is neither: the assertion states what the value must be, which is the same\n * check a schema would perform, and a schema parse would move the failure away\n * from the assertion that explains it.\n *\n * Corpus evidence (2220 files across zod / TanStack Query / react-router / swr /\n * zustand, 2026-07): after the test-file exemption, 15 of the 45 remaining hits\n * were this — react-router names its Playwright suites `integration/request-test.ts`\n * (hyphen), which no `*.test.ts` path predicate can recognise, so the shape\n * check is what catches them.\n * `react-router/integration/request-test.ts:120-121`:\n * `loaderData = JSON.parse(await page.locator(\"#loader-data\").innerHTML());\n * expect(loaderData.method).toEqual(\"GET\");`\n */\nconst isInsideAssertion = (node: TSESTree.Node): boolean => {\n for (\n let current: TSESTree.Node | undefined | null = node.parent;\n current !== undefined && current !== null;\n current = current.parent\n ) {\n if (current.type !== AST_NODE_TYPES.CallExpression) continue;\n let callee: TSESTree.Node = current.callee;\n while (callee.type === AST_NODE_TYPES.MemberExpression) {\n callee = callee.object;\n }\n if (callee.type === AST_NODE_TYPES.CallExpression) {\n callee = callee.callee;\n }\n if (\n callee.type === AST_NODE_TYPES.Identifier &&\n ASSERTION_CALLEE_RE.test(callee.name)\n ) {\n return true;\n }\n }\n return false;\n};\n\nconst findVariable = (\n scope: Scope.Scope | null,\n name: string,\n): Scope.Variable | null => {\n let current: Scope.Scope | null = scope;\n while (current !== null) {\n const variable = current.set.get(name);\n if (variable !== undefined) return variable;\n current = current.upper;\n }\n return null;\n};\n\n/** User-defined type-guard predicate names, e.g. `isProtectedResourceMetadata`. */\nconst GUARD_NAME_RE = /^is[A-Z]/;\n\n/**\n * True when a call sits in a boolean-test position (`if`/`while`/`for`/`?:`),\n * seen through `!`, `&&`/`||`, and optional-chaining wrappers — i.e. it narrows.\n */\nconst isGuardTestPosition = (node: TSESTree.Node): boolean => {\n let current: TSESTree.Node = node;\n let parent: TSESTree.Node | null | undefined = current.parent;\n while (parent !== undefined && parent !== null) {\n switch (parent.type) {\n case AST_NODE_TYPES.UnaryExpression:\n case AST_NODE_TYPES.LogicalExpression:\n case AST_NODE_TYPES.ChainExpression:\n current = parent;\n parent = parent.parent;\n continue;\n case AST_NODE_TYPES.IfStatement:\n case AST_NODE_TYPES.ConditionalExpression:\n case AST_NODE_TYPES.WhileStatement:\n case AST_NODE_TYPES.DoWhileStatement:\n case AST_NODE_TYPES.ForStatement:\n return parent.test === current;\n default:\n return false;\n }\n }\n return false;\n};\n\nconst isUnvalidatedVariableRef = (\n node: TSESTree.Node | null | undefined,\n scope: Scope.Scope,\n tracked: ReadonlySet<Scope.Variable>,\n): boolean => {\n const unwrapped = unwrap(node);\n if (unwrapped === null || unwrapped.type !== AST_NODE_TYPES.Identifier) {\n return false;\n }\n const variable = findVariable(scope, unwrapped.name);\n return variable !== null && tracked.has(variable);\n};\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"prefer-schema-for-api-payload\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Require Zod (or similar) schema validation on `response.json()` / `JSON.parse()` results before property access.\",\n },\n schema: [],\n messages: {\n unparsedJsonAccess:\n \"Property access on an unvalidated payload (`response.json()` / `JSON.parse()`) without a schema parse. Pipe through `XSchema.parse(...)` (Zod) before reading fields.\",\n },\n },\n defaultOptions: [],\n create(context: Ctx) {\n // A fixture parses what it just produced and asserts on it; see @fileoverview.\n if (isTestFile(context.filename) || isGeneratedFile(context.filename, context.sourceCode.text)) {\n return {};\n }\n\n const unvalidatedVariables = new Set<Scope.Variable>();\n\n const trackInitializer = (\n declarator: TSESTree.VariableDeclarator,\n ): void => {\n if (!isRawPayloadSource(declarator.init)) return;\n const declaredVars = context.sourceCode.getDeclaredVariables(declarator);\n const variable = declaredVars[0];\n if (variable !== undefined) {\n unvalidatedVariables.add(variable);\n }\n };\n\n return {\n VariableDeclarator(node): void {\n const scope = context.sourceCode.getScope(node);\n\n if (node.id.type === AST_NODE_TYPES.Identifier) {\n trackInitializer(node);\n return;\n }\n\n if (\n node.id.type === AST_NODE_TYPES.ObjectPattern ||\n node.id.type === AST_NODE_TYPES.ArrayPattern\n ) {\n if (isRawPayloadSource(node.init)) {\n context.report({ node: node.id, messageId: \"unparsedJsonAccess\" });\n return;\n }\n if (\n isUnvalidatedVariableRef(node.init, scope, unvalidatedVariables)\n ) {\n context.report({ node: node.id, messageId: \"unparsedJsonAccess\" });\n }\n }\n },\n AssignmentExpression(node): void {\n const scope = context.sourceCode.getScope(node);\n\n if (node.left.type === AST_NODE_TYPES.Identifier) {\n const variable = findVariable(scope, node.left.name);\n if (variable === null) return;\n if (isRawPayloadSource(node.right)) {\n unvalidatedVariables.add(variable);\n } else {\n // Reassigned to a parse call or something else: drop tracking.\n unvalidatedVariables.delete(variable);\n }\n return;\n }\n\n if (\n node.left.type === AST_NODE_TYPES.ObjectPattern ||\n node.left.type === AST_NODE_TYPES.ArrayPattern\n ) {\n if (isRawPayloadSource(node.right)) {\n context.report({\n node: node.left,\n messageId: \"unparsedJsonAccess\",\n });\n return;\n }\n if (\n isUnvalidatedVariableRef(node.right, scope, unvalidatedVariables)\n ) {\n context.report({\n node: node.left,\n messageId: \"unparsedJsonAccess\",\n });\n }\n }\n },\n CallExpression(node): void {\n if (node.callee.type !== AST_NODE_TYPES.Identifier) return;\n if (!GUARD_NAME_RE.test(node.callee.name) && !isGuardTestPosition(node)) {\n return;\n }\n const scope = context.sourceCode.getScope(node);\n for (const arg of node.arguments) {\n if (arg.type === AST_NODE_TYPES.SpreadElement) continue;\n const unwrapped = unwrap(arg);\n if (unwrapped === null || unwrapped.type !== AST_NODE_TYPES.Identifier) {\n continue;\n }\n const variable = findVariable(scope, unwrapped.name);\n if (variable !== null) unvalidatedVariables.delete(variable);\n }\n },\n MemberExpression(node): void {\n // The read is inside an assertion — the assertion IS the validation.\n if (isInsideAssertion(node)) return;\n const scope = context.sourceCode.getScope(node);\n const obj = unwrap(node.object);\n\n if (isRawPayloadSource(obj)) {\n // Direct `.foo` access on `(await r.json()).foo` is always bad,\n // unless the parent call is a `.parse()`/`.safeParse()` — in which\n // case it's a validation, not an unvalidated read.\n const parent = node.parent;\n if (\n parent.type === AST_NODE_TYPES.CallExpression &&\n parent.callee === node &&\n node.property.type === AST_NODE_TYPES.Identifier &&\n (node.property.name === \"parse\" ||\n node.property.name === \"safeParse\")\n ) {\n return;\n }\n context.report({ node, messageId: \"unparsedJsonAccess\" });\n return;\n }\n\n if (\n obj !== null &&\n obj.type === AST_NODE_TYPES.Identifier &&\n isUnvalidatedVariableRef(obj, scope, unvalidatedVariables)\n ) {\n context.report({ node, messageId: \"unparsedJsonAccess\" });\n const variable = findVariable(scope, obj.name);\n if (variable !== null) {\n unvalidatedVariables.delete(variable);\n }\n }\n },\n };\n },\n});\n","/**\n * @fileoverview Enforce design-system semantic color tokens over raw Tailwind\n * palette classes and hardcoded color values.\n *\n * Scoped to genuine className positions to avoid false positives on non-class\n * strings (Tailwind `safelist`, `toHaveClass(...)` test assertions, prose, color\n * maps): JSX `className`, the args of `cn()`/`clsx()`/`cva()`/`tv()`/`cx()`/\n * `twMerge()` (recursing into cva variant objects), and `*class*`-named\n * variables/object properties. Plus inline color literals on JSX `style`/`fill`/\n * `stroke`.\n *\n * Flags:\n * - raw palette classes: `text-red-500`, `bg-slate-200/50`\n * - arbitrary color values: `bg-[#fff]`, `text-[rgb(...)]`, `ring-[oklch(...)]`\n * - inline color literals: `style={{ color: \"#111827\" }}`, `fill=\"#000\"`\n *\n * Allowed: semantic tokens (`bg-primary`, `text-muted-foreground`, `bg-chart-1`),\n * `white`/`black` (the `bg-black/50` overlay idiom rarely has a token), `var(--…)`,\n * `currentColor`, and non-color arbitraries (`w-[437px]`, `grid-cols-[auto_1fr]`).\n *\n * SVG drawing data is exempt on `fill`/`stroke`/`color` attributes: any value inside\n * a `<mask>`/`<clipPath>`/`<defs>`/`<pattern>`/`<linearGradient>`/`<radialGradient>`\n * (masking breaks without literal `#fff`/`#000`), the neutral literals\n * (`#fff`/`#000`/`transparent`/`none`/`currentColor`/`inherit`), and `*.stories.*`\n * files (Storybook fixtures) never fire. Real component styling — `className` and\n * inline `style={{ … }}` objects — still fires on hardcoded colors.\n *\n * No autofix — use a semantic token, or for charts / standalone pages / 3rd-party\n * config add `// eslint-disable-next-line @sarj/prefer-semantic-colors -- <reason>`.\n */\n\nimport { AST_NODE_TYPES, ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\nimport { existsSync, readFileSync } from \"fs\";\nimport { dirname, join, parse } from \"path\";\n\nimport { classTokens, tailwindBase } from \"./_tailwind.js\";\n\ntype MessageIds = \"rawPalette\" | \"arbitraryColor\" | \"inlineColor\";\ntype Options = readonly [\n {\n requireSemanticTokens?: boolean;\n }?,\n];\n\nconst COLOR_PREFIXES =\n \"text|bg|border(?:-[trblxyse])?|ring(?:-offset)?|fill|stroke|from|via|to|divide|decoration|placeholder|accent|caret|shadow|outline\";\nconst PALETTE =\n \"red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose|slate|gray|zinc|neutral|stone\";\nconst COLOR_FN = \"rgba?|hsla?|hwb|oklch|oklab|lab|lch|color\";\n\nconst RAW_PALETTE_RE = new RegExp(`^(?:${COLOR_PREFIXES})-(?:${PALETTE})-\\\\d{2,3}(?:/\\\\d{1,3})?$`);\nconst ARBITRARY_COLOR_RE = new RegExp(\n `^(?:${COLOR_PREFIXES})-\\\\[(?:#[0-9a-fA-F]{3,8}|(?:${COLOR_FN})\\\\([^\\\\]]*\\\\))\\\\]$`,\n \"i\",\n);\n\n/** Call expressions whose string args are className fragments. */\nconst CLASS_FNS = new Set<string>([\"cn\", \"clsx\", \"cva\", \"tv\", \"cx\", \"twMerge\", \"classnames\", \"classNames\"]);\nconst CLASS_NAME_RE = /class/i;\n\n/** CSS color-bearing properties, in their JSX (camelCase) and SVG-attribute forms. */\nconst STYLE_COLOR_PROPS = new Set<string>([\n \"color\",\n \"background\",\n \"backgroundColor\",\n \"borderColor\",\n \"borderTopColor\",\n \"borderRightColor\",\n \"borderBottomColor\",\n \"borderLeftColor\",\n \"outlineColor\",\n \"caretColor\",\n \"textDecorationColor\",\n \"columnRuleColor\",\n \"fill\",\n \"stroke\",\n \"stopColor\",\n \"floodColor\",\n \"lightingColor\",\n]);\nconst RAW_COLOR_VALUE_RE = new RegExp(`#[0-9a-fA-F]{3,8}\\\\b|\\\\b(?:${COLOR_FN})\\\\s*\\\\(`, \"i\");\n\nconst STORIES_FILE_RE = /\\.stories\\.[cm]?[jt]sx?$/i;\nconst SEMANTIC_TOKEN_RE = /--(?:background|foreground|primary|secondary|muted|accent|destructive|border|card|popover)\\b|(?:bg|text|border)-(?:background|foreground|primary|secondary|muted|accent|destructive|border|card|popover)\\b/;\nconst DETECTION_FILES = [\n \"components.json\",\n \"tailwind.config.js\",\n \"tailwind.config.cjs\",\n \"tailwind.config.mjs\",\n \"tailwind.config.ts\",\n \"app/globals.css\",\n \"src/app/globals.css\",\n \"src/index.css\",\n \"src/styles/globals.css\",\n \"styles/globals.css\",\n];\n/** How far up the tree to look for a design-token marker before giving up. */\nconst MAX_UPWARD_DEPTH = 8;\n\nconst semanticTokenCache = new Map<string, boolean>();\n\n/** SVG container elements whose children carry structural (not UI-token) colors. */\nconst SVG_DEFS_CONTAINERS = new Set<string>([\n \"mask\",\n \"clipPath\",\n \"defs\",\n \"pattern\",\n \"linearGradient\",\n \"radialGradient\",\n]);\n\n/** Neutral fill/stroke literals that are SVG drawing data, never a UI token. */\nconst SVG_EXEMPT_COLOR_VALUES = new Set<string>([\n \"#fff\",\n \"#ffffff\",\n \"#000\",\n \"#000000\",\n \"transparent\",\n \"none\",\n \"currentcolor\",\n \"inherit\",\n]);\n\n// A `fill`/`stroke` literal anywhere inside an `<svg>` subtree is drawing data\n// (icon/illustration artwork), not a reusable UI token — the color is inherent to\n// the graphic. Exempt any descendant of `<svg>` (which subsumes the defs\n// containers `<mask>`/`<clipPath>`/`<defs>`/`<pattern>`/gradients).\nfunction jsxElementName(node: TSESTree.JSXElement): string | null {\n const name = node.openingElement.name;\n if (name.type === AST_NODE_TYPES.JSXIdentifier) return name.name;\n if (name.type === AST_NODE_TYPES.JSXMemberExpression && name.property.type === AST_NODE_TYPES.JSXIdentifier) {\n return name.property.name;\n }\n return null;\n}\n\nfunction isSvgLikeElementName(name: string): boolean {\n return name === \"svg\" || SVG_DEFS_CONTAINERS.has(name) || /svg$/i.test(name);\n}\n\nconst isInsideSvg = (node: TSESTree.Node): boolean => {\n let current: TSESTree.Node | null | undefined = node.parent;\n while (current !== undefined && current !== null) {\n if (current.type === AST_NODE_TYPES.JSXElement) {\n const name = jsxElementName(current);\n if (name !== null && isSvgLikeElementName(name)) return true;\n }\n current = current.parent;\n }\n return false;\n};\n\nconst isInsideIconFactoryPath = (node: TSESTree.Node): boolean => {\n let current: TSESTree.Node | null | undefined = node.parent;\n while (current !== undefined && current !== null) {\n if (\n current.type === AST_NODE_TYPES.Property &&\n propName(current.key) === \"path\" &&\n current.parent.type === AST_NODE_TYPES.ObjectExpression &&\n current.parent.parent.type === AST_NODE_TYPES.CallExpression &&\n current.parent.parent.callee.type === AST_NODE_TYPES.Identifier &&\n current.parent.parent.callee.name === \"createIcon\"\n ) {\n return true;\n }\n current = current.parent;\n }\n return false;\n};\n\n/**\n * Does a design-token system exist at or above `filename`'s directory?\n *\n * The cache stores the RESOLVED answer for every directory visited on the way\n * up, not each directory's own local result. That distinction is the entire bug\n * this replaced: the previous version wrote `cache[dir] = found` per\n * intermediate directory, where `found` meant \"no marker AT this dir\" — but the\n * question being cached is \"is there a marker at or ABOVE this dir\". So the\n * first file linted wrote `cache[\"…/src\"] = false`, and every later file under\n * `src/**` short-circuited on it and never walked up to the `components.json`\n * at the repo root.\n *\n * The effect was order-dependent and total: linting one file reported normally,\n * while linting a glob containing that same file reported nothing, because some\n * other file poisoned the cache first. Measured across 6,774 files of real\n * TypeScript this rule produced ZERO findings — silently disabled everywhere\n * despite shipping as \"error\". It is the only rule in the plugin with a\n * module-level directory cache, which is why nothing else showed the symptom.\n *\n * Caching the resolved answer for all visited directories is sound: every\n * visited directory sits at or below the one where the marker was found, so the\n * marker is at-or-above each of them too.\n */\nconst hasSemanticTokenSystem = (filename: string): boolean => {\n let dir = dirname(filename);\n const root = parse(dir).root;\n const visited: string[] = [];\n let answer: boolean | undefined;\n\n for (let depth = 0; depth < MAX_UPWARD_DEPTH; depth += 1) {\n const cached = semanticTokenCache.get(dir);\n if (cached !== undefined) {\n answer = cached;\n break;\n }\n visited.push(dir);\n\n let found = false;\n for (const rel of DETECTION_FILES) {\n const candidate = join(dir, rel);\n if (!existsSync(candidate)) continue;\n if (rel === \"components.json\") {\n found = true;\n break;\n }\n try {\n if (SEMANTIC_TOKEN_RE.test(readFileSync(candidate, \"utf8\"))) {\n found = true;\n break;\n }\n } catch {\n // Ignore unreadable config files; absence of evidence means no report.\n }\n }\n if (found) {\n answer = true;\n break;\n }\n if (dir === root) {\n answer = false;\n break;\n }\n dir = dirname(dir);\n }\n\n // `answer` is undefined only when the depth budget ran out before reaching a\n // marker or the filesystem root. That is inconclusive, not \"no\" — a directory\n // higher up could still hold one, reachable within budget from a shallower\n // file. Memoizing it would make the wrong answer permanent for the process,\n // so those directories are deliberately left uncached.\n if (answer === undefined) return false;\n for (const seen of visited) semanticTokenCache.set(seen, answer);\n return answer;\n};\n\nconst propName = (key: TSESTree.Property[\"key\"]): string | null => {\n if (key.type === AST_NODE_TYPES.Identifier) return key.name;\n if (key.type === AST_NODE_TYPES.Literal && typeof key.value === \"string\") return key.value;\n return null;\n};\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"prefer-semantic-colors\",\n meta: {\n type: \"suggestion\",\n docs: {\n description:\n \"Enforce design-system semantic color tokens (bg-primary, text-destructive, …) over raw Tailwind palette classes (text-red-500), arbitrary color values (bg-[#fff]), and inline color literals.\",\n },\n schema: [\n {\n type: \"object\",\n additionalProperties: false,\n properties: {\n requireSemanticTokens: { type: \"boolean\" },\n },\n },\n ],\n messages: {\n rawPalette:\n \"Raw palette class '{{class}}' — use a semantic token (e.g. text-foreground, bg-primary, text-destructive, bg-muted).\",\n arbitraryColor:\n \"Hardcoded color '{{class}}' — use a semantic token, or var(--…). For charts/brand add an eslint-disable with a reason.\",\n inlineColor:\n \"Hardcoded color '{{value}}' — use a semantic token / CSS variable. For charts/standalone pages add an eslint-disable with a reason.\",\n },\n },\n defaultOptions: [{}],\n create(context, [options]) {\n if (STORIES_FILE_RE.test(context.filename)) return {};\n if (options?.requireSemanticTokens === true && !hasSemanticTokenSystem(context.filename)) {\n return {};\n }\n\n const reportClasses = (value: string, node: TSESTree.Node): void => {\n for (const token of classTokens(value)) {\n const base = tailwindBase(token);\n if (RAW_PALETTE_RE.test(base)) {\n context.report({ node, messageId: \"rawPalette\", data: { class: token } });\n } else if (ARBITRARY_COLOR_RE.test(base)) {\n context.report({ node, messageId: \"arbitraryColor\", data: { class: token } });\n }\n }\n };\n\n // Walk a node that holds className fragments: strings, templates, arrays, cva\n // variant objects, and conditionals. CallExpressions are handled separately, so\n // they're not recursed here (avoids double-reporting cn()/cva() args).\n const checkClassNode = (node: TSESTree.Node | null): void => {\n if (node === null) return;\n switch (node.type) {\n case AST_NODE_TYPES.Literal:\n if (typeof node.value === \"string\") reportClasses(node.value, node);\n break;\n case AST_NODE_TYPES.TemplateLiteral:\n for (const quasi of node.quasis) reportClasses(quasi.value.cooked ?? \"\", quasi);\n break;\n case AST_NODE_TYPES.ArrayExpression:\n for (const element of node.elements) {\n if (element !== null && element.type !== AST_NODE_TYPES.SpreadElement) checkClassNode(element);\n }\n break;\n case AST_NODE_TYPES.ObjectExpression:\n for (const property of node.properties) {\n if (property.type === AST_NODE_TYPES.Property) checkClassNode(property.value);\n }\n break;\n case AST_NODE_TYPES.ConditionalExpression:\n checkClassNode(node.consequent);\n checkClassNode(node.alternate);\n break;\n case AST_NODE_TYPES.LogicalExpression:\n checkClassNode(node.right);\n break;\n default:\n break;\n }\n };\n\n const checkColorValueNode = (node: TSESTree.Node): void => {\n if (\n node.type === AST_NODE_TYPES.Literal &&\n typeof node.value === \"string\" &&\n RAW_COLOR_VALUE_RE.test(node.value)\n ) {\n context.report({ node, messageId: \"inlineColor\", data: { value: node.value } });\n }\n };\n\n return {\n \"JSXAttribute[name.name='className']\"(node: TSESTree.JSXAttribute): void {\n if (node.value === null) return;\n if (node.value.type === AST_NODE_TYPES.Literal) checkClassNode(node.value);\n else if (node.value.type === AST_NODE_TYPES.JSXExpressionContainer) {\n if (node.value.expression.type !== AST_NODE_TYPES.JSXEmptyExpression) {\n checkClassNode(node.value.expression);\n }\n }\n },\n CallExpression(node: TSESTree.CallExpression): void {\n if (node.callee.type === AST_NODE_TYPES.Identifier && CLASS_FNS.has(node.callee.name)) {\n for (const arg of node.arguments) {\n if (arg.type !== AST_NODE_TYPES.SpreadElement) checkClassNode(arg);\n }\n }\n },\n VariableDeclarator(node: TSESTree.VariableDeclarator): void {\n if (node.id.type === AST_NODE_TYPES.Identifier && CLASS_NAME_RE.test(node.id.name)) {\n checkClassNode(node.init);\n }\n },\n Property(node: TSESTree.Property): void {\n const name = propName(node.key);\n if (name !== null && CLASS_NAME_RE.test(name)) checkClassNode(node.value);\n },\n // SVG presentation attributes: <path fill=\"#7c3aed\" stroke=\"#7c3aed\" />.\n // Neutral drawing literals and anything inside an SVG defs container are\n // structural, not UI tokens, so they never fire.\n \"JSXAttribute[name.name=/^(fill|stroke|color)$/]\"(node: TSESTree.JSXAttribute): void {\n if (node.value?.type !== AST_NODE_TYPES.Literal) return;\n if (\n typeof node.value.value === \"string\" &&\n SVG_EXEMPT_COLOR_VALUES.has(node.value.value.toLowerCase())\n ) {\n return;\n }\n if (isInsideSvg(node) || isInsideIconFactoryPath(node)) return;\n checkColorValueNode(node.value);\n },\n // Inline style objects: style={{ color: \"#111827\", backgroundColor: \"#fff\" }}\n \"JSXAttribute[name.name='style'] ObjectExpression > Property\"(node: TSESTree.Property): void {\n const name = propName(node.key);\n if (name !== null && STYLE_COLOR_PROPS.has(name)) checkColorValueNode(node.value);\n },\n };\n },\n});\n","/**\n * @fileoverview Shared helpers for the Tailwind-className rules. className values\n * are reachable as plain string `Literal`s (attribute values, `cn()`/`clsx()`/\n * `cva()`/`tv()` args, and className-holding constants) and as the static quasis of\n * `TemplateLiteral`s — so the rules visit both node types and run these helpers.\n */\n\n/**\n * Strip Tailwind variant prefixes (`hover:`, `dark:`, `focus-visible:`, …) and a\n * leading `!` important marker, leaving the bare utility (`bg-red-500`). Variants are\n * `[a-z0-9-]+:` runs at the start; bracketed arbitrary values never start a token, so\n * a `:` inside `[url(http://…)]` is not mistaken for a variant separator.\n */\nexport const tailwindBase = (token: string): string =>\n token.replace(/^(?:[a-z0-9-]+:)+/i, \"\").replace(/^!/, \"\");\n\n/** Split a className string into its non-empty class tokens. */\nexport const classTokens = (value: string): readonly string[] =>\n value.split(/\\s+/).filter(Boolean);\n","/**\n * @fileoverview Prefer Next.js Server Actions over /api/* mutations.\n *\n * Flags mutations against internal `/api/*` URLs (POST/PUT/DELETE/PATCH) via\n * `fetch`, axios-style helpers, or direct axios/request calls. GET requests\n * and external URLs are ignored. Tests, scripts, and Next.js route handlers\n * are skipped because Server Actions don't apply there.\n *\n * The member branch (`api.post('/api/x')`) intentionally skips calls that pass\n * a function argument (e.g. `router.post('/api/x', handler)`) so Express-style\n * route *definitions* aren't mistaken for client-side mutations.\n *\n * NON-REACT FRAMEWORKS ARE SKIPPED. \"Use a Server Action\" is not advice an\n * Angular, Vue, Svelte or Solid module can act on — the feature does not exist\n * outside React/Next. Corpus sweep (2220 files across zod / TanStack Query /\n * react-router / swr / zustand, 2026-07): 9 hits, of which 3 were Angular\n * services —\n * `query/examples/angular/auto-refetching/src/app/services/tasks.service.ts:38`\n * (`lastValueFrom(this.#http.post('/api/tasks', task))`, an `HttpClient`\n * injection) — and 4 were jscodeshift input/output fixtures under\n * `__testfixtures__/`, which are text a codemod transforms rather than code that\n * runs. Both are now skipped; the 2 genuine React hits still fire.\n *\n * References:\n * - https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations\n */\n\nimport { ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\nimport type { RuleContext, Scope } from \"@typescript-eslint/utils/ts-eslint\";\n\ntype MessageIds = \"preferServerAction\";\ntype Options = readonly [];\n\nconst MUTATION_METHODS = new Set([\"POST\", \"PUT\", \"DELETE\", \"PATCH\"]);\nconst AXIOS_MUTATION_METHODS = new Set([\"post\", \"put\", \"delete\", \"patch\"]);\n\nconst SKIP_FILE_REGEX =\n /(?:\\.test\\.[jt]sx?$|\\.spec\\.[jt]sx?$|-(?:test|spec)\\.[jt]sx?$|\\/tests?\\/|\\/__tests__\\/|\\/__testfixtures__\\/|\\/scripts?\\/|\\/app\\/api\\/.*\\/route\\.[jt]sx?$|\\/pages\\/api\\/)/;\n\n/**\n * Import sources that prove the module belongs to a framework where Server\n * Actions do not exist. See @fileoverview for the corpus evidence.\n */\nconst NON_REACT_FRAMEWORK_RE =\n /^(?:@angular\\/|@nestjs\\/|vue$|vue\\/|svelte$|svelte\\/|solid-js$|solid-js\\/|@ember\\/|rxjs$|rxjs\\/)/;\n\ntype Ctx = Readonly<RuleContext<MessageIds, Options>>;\n\nfunction getScope(\n context: Ctx,\n node: TSESTree.Node,\n): Scope.Scope {\n return context.sourceCode.getScope(node);\n}\n\n/**\n * Walk up the scope chain looking for a variable declared with a single\n * initializer; if found, return the initializer expression so we can analyze\n * it. Falls back to the original node when resolution is not possible.\n */\nfunction resolveNode(\n node: TSESTree.Node | null | undefined,\n context: Ctx,\n): TSESTree.Node | null {\n if (!node) return null;\n if (node.type !== \"Identifier\") return node;\n\n let scope: Scope.Scope | null = getScope(context, node);\n while (scope) {\n const variable = scope.set.get(node.name);\n if (variable && variable.defs.length === 1) {\n const def = variable.defs[0];\n if (def && def.type === \"Variable\") {\n const declarator = def.node;\n if (\n declarator.type === \"VariableDeclarator\" &&\n declarator.init\n ) {\n return declarator.init;\n }\n }\n }\n scope = scope.upper;\n }\n return node;\n}\n\nfunction isApiUrl(\n node: TSESTree.Node | null | undefined,\n context: Ctx,\n): boolean {\n const resolved = resolveNode(node, context);\n if (!resolved) return false;\n\n if (resolved.type === \"Literal\" && typeof resolved.value === \"string\") {\n return resolved.value.startsWith(\"/api/\");\n }\n if (resolved.type === \"TemplateLiteral\") {\n const firstQuasi = resolved.quasis[0];\n const cooked = firstQuasi?.value.cooked;\n return typeof cooked === \"string\" && cooked.startsWith(\"/api/\");\n }\n if (resolved.type === \"BinaryExpression\" && resolved.operator === \"+\") {\n return isApiUrl(resolved.left, context);\n }\n return false;\n}\n\nfunction isMutationMethod(\n node: TSESTree.Node | null | undefined,\n context: Ctx,\n): boolean {\n const resolved = resolveNode(node, context);\n if (!resolved) return false;\n\n if (resolved.type === \"Literal\" && typeof resolved.value === \"string\") {\n return MUTATION_METHODS.has(resolved.value.toUpperCase());\n }\n\n if (\n resolved.type === \"TemplateLiteral\" &&\n resolved.expressions.length === 0\n ) {\n const val = resolved.quasis.map((q) => q.value.cooked).join(\"\");\n return MUTATION_METHODS.has(val.toUpperCase());\n }\n\n if (resolved.type === \"ConditionalExpression\") {\n return (\n isMutationMethod(resolved.consequent, context) ||\n isMutationMethod(resolved.alternate, context)\n );\n }\n\n if (resolved.type === \"LogicalExpression\" && resolved.operator === \"||\") {\n return (\n isMutationMethod(resolved.left, context) ||\n isMutationMethod(resolved.right, context)\n );\n }\n\n return false;\n}\n\nfunction getPropertyNode(\n objNode: TSESTree.Node | null | undefined,\n propName: string,\n): TSESTree.Node | null {\n if (!objNode || objNode.type !== \"ObjectExpression\") return null;\n for (const prop of objNode.properties) {\n if (prop.type !== \"Property\") continue;\n let keyName: string | null = null;\n if (prop.key.type === \"Identifier\" && !prop.computed) {\n keyName = prop.key.name;\n } else if (\n prop.key.type === \"Literal\" &&\n typeof prop.key.value === \"string\"\n ) {\n keyName = prop.key.value;\n }\n if (keyName === propName) {\n // Skip destructuring patterns — they're not valid as config values.\n if (\n prop.value.type === \"AssignmentPattern\" ||\n prop.value.type === \"ArrayPattern\" ||\n prop.value.type === \"ObjectPattern\"\n ) {\n return null;\n }\n return prop.value;\n }\n }\n return null;\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"prefer-server-actions\",\n meta: {\n type: \"suggestion\",\n docs: {\n description: \"Prefer Next.js Server Actions over /api/* mutations.\",\n },\n schema: [],\n messages: {\n preferServerAction:\n \"Mutation against /api/* — prefer a Next.js Server Action for type-safety and to avoid the JSON round-trip.\",\n },\n },\n defaultOptions: [],\n create(context) {\n const filename = context.filename;\n if (SKIP_FILE_REGEX.test(filename)) {\n return {};\n }\n\n // Set by the ImportDeclaration visitor. Imports are hoisted to the top of a\n // module, so every one is seen before the first CallExpression below.\n let isNonReactFramework = false;\n\n return {\n ImportDeclaration(node) {\n if (\n typeof node.source.value === \"string\" &&\n NON_REACT_FRAMEWORK_RE.test(node.source.value)\n ) {\n isNonReactFramework = true;\n }\n },\n CallExpression(node) {\n if (isNonReactFramework) return;\n let isMutation = false;\n\n // 1. Standard fetch('/api/orders', { method: 'POST' })\n if (\n node.callee.type === \"Identifier\" &&\n node.callee.name === \"fetch\"\n ) {\n const urlArg = node.arguments[0];\n if (urlArg && urlArg.type !== \"SpreadElement\" && isApiUrl(urlArg, context)) {\n const initArg = node.arguments[1];\n if (initArg && initArg.type !== \"SpreadElement\") {\n const resolvedInit = resolveNode(initArg, context);\n const methodNode = getPropertyNode(resolvedInit, \"method\");\n if (methodNode && isMutationMethod(methodNode, context)) {\n isMutation = true;\n }\n }\n }\n }\n // 2. Custom wrappers or Axios: api.post('/api/orders') or axios.put('/api/orders')\n else if (\n node.callee.type === \"MemberExpression\" &&\n node.callee.property.type === \"Identifier\" &&\n !node.callee.computed\n ) {\n const methodName = node.callee.property.name.toLowerCase();\n if (AXIOS_MUTATION_METHODS.has(methodName)) {\n const urlArg = node.arguments[0];\n // Skip Express-style route definitions like\n // `router.post('/api/x', handler)`: a function argument means this\n // is registering a handler, not issuing a client mutation.\n const hasHandlerArg = node.arguments.some(\n (arg) =>\n arg.type === \"ArrowFunctionExpression\" ||\n arg.type === \"FunctionExpression\",\n );\n if (\n urlArg &&\n urlArg.type !== \"SpreadElement\" &&\n !hasHandlerArg &&\n isApiUrl(urlArg, context)\n ) {\n isMutation = true;\n }\n }\n }\n // 3. Direct axios/request call: axios({ method: 'post', url: '/api/orders' })\n else if (\n node.callee.type === \"Identifier\" &&\n (node.callee.name === \"axios\" || node.callee.name === \"request\")\n ) {\n const firstArg = node.arguments[0];\n if (firstArg && firstArg.type !== \"SpreadElement\") {\n const configArg = resolveNode(firstArg, context);\n if (configArg && configArg.type === \"ObjectExpression\") {\n const urlNode = getPropertyNode(configArg, \"url\");\n const methodNode = getPropertyNode(configArg, \"method\");\n if (\n urlNode &&\n isApiUrl(urlNode, context) &&\n methodNode &&\n isMutationMethod(methodNode, context)\n ) {\n isMutation = true;\n }\n }\n }\n }\n\n if (isMutation) {\n context.report({ node, messageId: \"preferServerAction\" });\n }\n },\n };\n },\n});\n","import {\n ESLintUtils,\n type TSESLint,\n type TSESTree,\n AST_NODE_TYPES,\n} from \"@typescript-eslint/utils\";\n\ntype MessageIds = \"missingAssertNever\";\ntype Options = readonly [];\n\n/**\n * Matches a call to `assertNever(...)` — either the bare identifier form\n * (`assertNever(x)`) or a namespaced member form (`utils.assertNever(x)`).\n */\nconst isAssertNeverCall = (expression: TSESTree.Expression): boolean => {\n if (expression.type !== AST_NODE_TYPES.CallExpression) return false;\n const callee = expression.callee;\n if (callee.type === AST_NODE_TYPES.Identifier) {\n return callee.name === \"assertNever\";\n }\n if (\n callee.type === AST_NODE_TYPES.MemberExpression &&\n !callee.computed &&\n callee.property.type === AST_NODE_TYPES.Identifier\n ) {\n return callee.property.name === \"assertNever\";\n }\n return false;\n};\n\nconst statementContainsAssertNever = (\n statement: TSESTree.Statement,\n): boolean => {\n if (statement.type === AST_NODE_TYPES.ExpressionStatement) {\n return isAssertNeverCall(statement.expression);\n }\n if (statement.type === AST_NODE_TYPES.ThrowStatement) {\n return isAssertNeverCall(statement.argument);\n }\n if (statement.type === AST_NODE_TYPES.ReturnStatement) {\n return (\n statement.argument !== null && isAssertNeverCall(statement.argument)\n );\n }\n // Recurse into block-scoped default bodies like `default: { ... }`\n if (statement.type === AST_NODE_TYPES.BlockStatement) {\n return statement.body.some(statementContainsAssertNever);\n }\n return false;\n};\n\n/**\n * Returns true if the statement performs some runtime work. An empty statement\n * or an empty block does nothing; everything else (`return`, `throw`, `break`,\n * a function call, an `if`, ...) is treated as legitimate runtime handling of\n * the default case.\n */\nconst isRuntimeHandlingStatement = (statement: TSESTree.Statement): boolean => {\n if (statement.type === AST_NODE_TYPES.EmptyStatement) return false;\n if (statement.type === AST_NODE_TYPES.BlockStatement) {\n return statement.body.some(isRuntimeHandlingStatement);\n }\n return true;\n};\n\n/**\n * A `default` clause with an empty body that is followed by another `case`\n * falls through into that case, which does the real work (e.g.\n * `default: case Single: handle();`). There is nothing to assert here.\n */\nconst isFallthroughDefault = (\n node: TSESTree.SwitchStatement,\n defaultIndex: number,\n): boolean => {\n const defaultCase = node.cases[defaultIndex];\n return (\n defaultCase !== undefined &&\n defaultCase.consequent.length === 0 &&\n defaultIndex < node.cases.length - 1\n );\n};\n\n/**\n * A `default` clause is a deliberate, documented no-op when its body is empty\n * (or an empty block) but carries a comment — e.g. `default: // do nothing`.\n * We can't distinguish a config-string switch from an exhaustive union switch\n * without type info, so a written-out intent to do nothing is honoured;\n * injecting `assertNever` there would throw at runtime. A bare empty default\n * with no comment is still flagged.\n */\nconst isCommentOnlyNoopDefault = (\n defaultCase: TSESTree.SwitchCase,\n sourceCode: Readonly<TSESLint.SourceCode>,\n): boolean => {\n if (defaultCase.consequent.length === 0) {\n const defaultToken = sourceCode.getFirstToken(defaultCase);\n const colonToken = defaultToken\n ? sourceCode.getTokenAfter(defaultToken)\n : null;\n return (\n colonToken !== null && sourceCode.getCommentsAfter(colonToken).length > 0\n );\n }\n const only = defaultCase.consequent[0];\n if (\n only !== undefined &&\n defaultCase.consequent.length === 1 &&\n only.type === AST_NODE_TYPES.BlockStatement &&\n only.body.length === 0\n ) {\n return sourceCode.getCommentsInside(only).length > 0;\n }\n return false;\n};\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"require-assert-never\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Require an exhaustive-style switch whose `default` case does no runtime work to call `assertNever(_)` so that discriminated unions are exhaustively checked at compile time. Switches with a legitimate runtime default (a reducer's `return state`, an HTTP-status `return fallback()`, a `break`, a `throw`, etc.) are left alone.\",\n },\n schema: [],\n messages: {\n missingAssertNever:\n \"Empty switch `default` case — add runtime handling or call `assertNever()` so the discriminated union is exhaustively checked at compile time.\",\n },\n },\n defaultOptions: [],\n create(context) {\n return {\n SwitchStatement(node: TSESTree.SwitchStatement): void {\n const defaultIndex = node.cases.findIndex(\n (caseNode) => caseNode.test === null,\n );\n // No `default` at all is fine — we don't demand exhaustiveness of every\n // switch, only of those that opted into a (no-op) default.\n if (defaultIndex === -1) return;\n const defaultCase = node.cases[defaultIndex];\n if (defaultCase === undefined) return;\n\n // An explicit `assertNever(...)` is the canonical exhaustiveness check.\n if (defaultCase.consequent.some(statementContainsAssertNever)) return;\n\n // A default that does real runtime work (return a fallback, break,\n // throw, log, ...) is legitimate — don't demand assertNever there.\n if (defaultCase.consequent.some(isRuntimeHandlingStatement)) return;\n\n // A fallthrough default hands control to a following case, which does\n // the work — there is nothing to assert.\n if (isFallthroughDefault(node, defaultIndex)) return;\n\n // A deliberate, comment-documented no-op default is honoured; we can't\n // prove the discriminant is a union without type info, and injecting\n // assertNever would throw at runtime on a config-string switch.\n if (isCommentOnlyNoopDefault(defaultCase, context.sourceCode)) return;\n\n // Otherwise the default is a bare, undocumented no-op: flag it.\n context.report({\n node: defaultCase,\n messageId: \"missingAssertNever\",\n });\n },\n };\n },\n});\n","/**\n * @fileoverview Require Zod validation on values read out of a `FormData`.\n * `formData.get(k)` returns `FormDataEntryValue | null` — an unvalidated,\n * attacker-controlled value. Pipe it through a schema before use.\n *\n * Validation is recognised two ways:\n * - INLINE: the `.get(...)` call sits inside a Zod `.parse(...)` /\n * `.safeParse(...)` — found by walking up the parent chain.\n * - VIA A BINDING: the value is bound and validated one or more statements\n * later, which is how a real handler reads. Tracked through the scope\n * manager (the same approach `prefer-schema-for-api-payload` uses for\n * `response.json()`), so this is not reported:\n * const tokenRaw = formData.get(\"t\");\n * const parsed = ZForm.safeParse({ t: typeof tokenRaw === \"string\" ? tokenRaw : undefined });\n *\n * A binding narrowed by `instanceof File` / `instanceof Blob` is also exempt: a\n * Zod schema has nothing useful to say about a `File`, and `instanceof` IS the\n * validation for that branch.\n *\n * A Zod receiver is recognised by name — `Schema`-suffixed (`userSchema`), the\n * `Z<Capital>` house form (`ZUser`), or the bare `z` builder — matching\n * `zod-naming-convention`, which accepts both conventions.\n *\n * TEST FILES ARE EXEMPT. Corpus sweep (2220 files across zod / TanStack Query /\n * react-router / swr / zustand, 2026-07): 42 raw hits, 40 of them in\n * react-router suites and every one an assertion rather than a trust boundary.\n * `react-router/packages/react-router/__tests__/dom/data-browser-router-test.tsx:4183`\n * is the shape — `let formData = await actionSpy.mock.calls[0][0].request.formData();\n * expect(formData.get(\"a\")).toBe(\"1\")`. The FormData was built by the test two\n * lines earlier; there is no attacker, and validating it with a schema would\n * assert the schema instead of the router. The rule's premise — \"this value is\n * attacker-controlled\" — is false by construction in a fixture.\n */\n\nimport { ESLintUtils, type TSESTree, AST_NODE_TYPES } from \"@typescript-eslint/utils\";\nimport type { RuleContext, Scope } from \"@typescript-eslint/utils/ts-eslint\";\n\nimport { isTestFile } from \"./_paths.js\";\nimport { ZOD_SCHEMA_NAME_RE } from \"./_zod.js\";\n\ntype MessageIds = \"missingZodValidation\";\ntype Options = readonly [];\n\ntype Ctx = Readonly<RuleContext<MessageIds, Options>>;\n\n/**\n * Walk down a (possibly chained) receiver expression and decide whether it\n * originates from something that looks like a Zod schema — the bare `z` builder\n * (`z.object({...}).parse(...)`), a `Schema`-suffixed identifier\n * (`userSchema.parse(...)`), or a `Z`-prefixed identifier (`ZUser.parse(...)`).\n * Non-Zod receivers like `JSON` / `Date` are intentionally rejected.\n */\nconst looksLikeZodSchema = (node: TSESTree.Node): boolean => {\n let current: TSESTree.Node = node;\n while (true) {\n if (current.type === AST_NODE_TYPES.Identifier) {\n return current.name === \"z\" || ZOD_SCHEMA_NAME_RE.test(current.name);\n }\n if (current.type === AST_NODE_TYPES.CallExpression) {\n current = current.callee;\n continue;\n }\n if (current.type === AST_NODE_TYPES.MemberExpression) {\n current = current.object;\n continue;\n }\n return false;\n }\n};\n\n/**\n * Matches a Zod validation call: `<ZodSchema>.parse(...)` or\n * `<ZodSchema>.safeParse(...)`. Keys off the *receiver* looking like a Zod\n * schema rather than the method name alone, so `JSON.parse(...)` /\n * `Date.parse(...)` are NOT treated as validation.\n */\nconst isZodParseCall = (node: TSESTree.Node): boolean => {\n if (node.type !== AST_NODE_TYPES.CallExpression) return false;\n const callee = node.callee;\n if (callee.type !== AST_NODE_TYPES.MemberExpression) return false;\n if (callee.computed) return false;\n if (callee.property.type !== AST_NODE_TYPES.Identifier) return false;\n const method = callee.property.name;\n if (method !== \"parse\" && method !== \"safeParse\") return false;\n return looksLikeZodSchema(callee.object);\n};\n\n/**\n * Matches an (optionally awaited) `<x>.formData()` call — the canonical way a\n * `FormData` object is obtained from a `Request` / `Response`.\n */\nconst isFormDataMethodCall = (node: TSESTree.Node): boolean => {\n let current: TSESTree.Node = node;\n if (current.type === AST_NODE_TYPES.AwaitExpression) {\n current = current.argument;\n }\n if (current.type !== AST_NODE_TYPES.CallExpression) return false;\n const callee = current.callee;\n return (\n callee.type === AST_NODE_TYPES.MemberExpression &&\n !callee.computed &&\n callee.property.type === AST_NODE_TYPES.Identifier &&\n callee.property.name === \"formData\"\n );\n};\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"require-zod-form-validation\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Require Zod validation (`Schema.parse(...)` / `Schema.safeParse(...)`) when reading values out of a `FormData` object.\",\n },\n schema: [],\n messages: {\n missingZodValidation:\n \"FormData parsing must use Zod schema validation (e.g., Schema.parse() / Schema.safeParse())\",\n },\n },\n defaultOptions: [],\n create(context: Ctx) {\n if (isTestFile(context.filename)) {\n return {};\n }\n\n // A receiver is a FormData source if its name reads like form data, or if\n // it is a binding initialized from a `.formData()` call.\n const isFormSourceIdentifier = (node: TSESTree.Node): boolean => {\n if (node.type !== AST_NODE_TYPES.Identifier) return false;\n if (/formdata/i.test(node.name)) return true;\n\n let scope: Scope.Scope | null = context.sourceCode.getScope(node);\n while (scope !== null) {\n const variable = scope.set.get(node.name);\n if (variable !== undefined && variable.defs.length === 1) {\n const def = variable.defs[0];\n if (\n def !== undefined &&\n def.type === \"Variable\" &&\n def.node.type === AST_NODE_TYPES.VariableDeclarator &&\n def.node.init !== null\n ) {\n return isFormDataMethodCall(def.node.init);\n }\n return false;\n }\n scope = scope.upper;\n }\n return false;\n };\n\n const isFormDataGetCall = (node: TSESTree.CallExpression): boolean => {\n const callee = node.callee;\n if (callee.type !== AST_NODE_TYPES.MemberExpression) return false;\n if (\n callee.property.type !== AST_NODE_TYPES.Identifier ||\n callee.property.name !== \"get\"\n ) {\n return false;\n }\n return isFormSourceIdentifier(callee.object);\n };\n\n // Walk up from `node` looking for a surrounding Zod `.parse(...)` /\n // `.safeParse(...)` call. `.parent` is `null` at the Program root, so we\n // must guard for both null and undefined.\n const hasZodParseAncestor = (node: TSESTree.Node): boolean => {\n let parent: TSESTree.Node | null | undefined = node.parent;\n while (parent !== null && parent !== undefined) {\n if (isZodParseCall(parent)) return true;\n parent = parent.parent;\n }\n return false;\n };\n\n // `x instanceof File` / `x instanceof Blob` — a Zod schema adds nothing over\n // the `instanceof` narrowing for a binary upload.\n const isInstanceofNarrowing = (node: TSESTree.Node): boolean => {\n const parent: TSESTree.Node | null | undefined = node.parent;\n return (\n parent !== null &&\n parent !== undefined &&\n parent.type === AST_NODE_TYPES.BinaryExpression &&\n parent.operator === \"instanceof\" &&\n parent.left === node\n );\n };\n\n /** The identifier this `.get(...)` call is bound to, or null. */\n const boundDeclarator = (\n node: TSESTree.CallExpression,\n ): TSESTree.VariableDeclarator | null => {\n const parent = node.parent;\n if (\n parent.type === AST_NODE_TYPES.VariableDeclarator &&\n parent.init === node &&\n parent.id.type === AST_NODE_TYPES.Identifier\n ) {\n return parent;\n }\n return null;\n };\n\n /** Is any read of this binding validated (Zod parse, or `instanceof File`)? */\n const bindingIsValidated = (\n declarator: TSESTree.VariableDeclarator,\n ): boolean => {\n const variable = context.sourceCode.getDeclaredVariables(declarator)[0];\n if (variable === undefined) return false;\n return variable.references.some(\n (ref) =>\n hasZodParseAncestor(ref.identifier) ||\n isInstanceofNarrowing(ref.identifier),\n );\n };\n\n return {\n CallExpression(node: TSESTree.CallExpression): void {\n if (!isFormDataGetCall(node)) return;\n\n if (hasZodParseAncestor(node) || isInstanceofNarrowing(node)) return;\n\n const declarator = boundDeclarator(node);\n if (declarator !== null && bindingIsValidated(declarator)) return;\n\n context.report({\n node,\n messageId: \"missingZodValidation\",\n });\n },\n };\n },\n});\n","/**\n * @fileoverview The single source of truth for \"does this name read as a Zod\n * schema?\". Shared by `zod-naming-convention` (which ENFORCES a convention) and\n * `require-zod-form-validation` (which RECOGNISES a schema receiver), because\n * the two disagreeing is a bug: a plugin that accepts `SubmitFormSchema` as a\n * validator in one rule must not call the same symbol non-conforming in another.\n *\n * Two conventions are recognised, and both are correct:\n * - PREFIX (`ZUser`) — lets a schema and its inferred type share a base name\n * (`type User = z.infer<typeof ZUser>`) without collision.\n * - SUFFIX (`userSchema`, `SubmitFormDataSchema`) — the dominant convention in\n * the wider Zod ecosystem and in most existing codebases.\n */\n\n/** The `Z<Capital>` prefix convention: `ZUser`, `ZSubmitForm`. */\nexport const ZOD_PREFIX_RE = /^Z[A-Z]/;\n\n/** The `XxxSchema` suffix convention: `userSchema`, `SubmitFormDataSchema`. */\nexport const ZOD_SUFFIX_RE = /Schema$/;\n\n/** Either accepted convention. */\nexport const ZOD_SCHEMA_NAME_RE = /Schema$|^Z[A-Z]/;\n","/**\n * @fileoverview Enforce a consistent naming convention for Zod schemas, so a\n * schema is recognisable at its use site without chasing the declaration.\n *\n * BOTH conventions are accepted by default (`convention: \"either\"`):\n * - `Z`-prefix (`ZUser = z.object({...})`) — lets a schema and its inferred\n * type share a base name (`type User = z.infer<typeof ZUser>`).\n * - `Schema`-suffix (`userSchema`, `SubmitFormDataSchema`) — the dominant\n * convention in the wider Zod ecosystem and in most existing codebases.\n *\n * Defaulting to prefix-only was wrong on both counts. It contradicted the rest\n * of the plugin — `require-zod-form-validation` accepts `/Schema$|^Z[A-Z]/` as\n * \"this is a Zod schema\" — and on a real 42k-LOC codebase that uniformly uses\n * the suffix it declared 220 symbols non-conforming with no defect behind any of\n * them. Both regexes now come from `_zod.ts` so the two rules cannot drift\n * apart again.\n *\n * A team that wants exactly one form sets `convention: \"prefix\"` or\n * `convention: \"suffix\"`; the point of the rule is consistency, and either\n * choice delivers it.\n *\n * FALSE POSITIVES FOUND BY A 2220-FILE CORPUS SWEEP (zod / TanStack Query /\n * react-router / swr / zustand, 2026-07). The rule fired 1816 times, ALL of\n * them in the `zod` repo and none of them a defect. Three guards remove them:\n *\n * (a) TEST FILES — 1783 / 1816 hits. A schema declared inside a test is a\n * throwaway fixture named `a`, `b`, `t1`, read three lines below its\n * declaration; there is no cross-module use site for the convention to\n * serve. `zod/packages/zod/src/v4/classic/tests/index.test.ts:782`\n * (`const a = z.lazy(() => z.string())`) is representative — that file\n * alone contributed 70 reports.\n * (b) TERMINAL CALLS THAT DO NOT RETURN A SCHEMA — ~280 hits. The rule keyed\n * off \"the callee chain starts at `z`\", which is also true of\n * `z.string().safeParse(x)` (a RESULT), `z.toJSONSchema(s)` (a plain JSON\n * object), `z.registry()` (a registry), `codec.encode(v)` / `.decode(v)`\n * (a converted VALUE) and `z.function(...).implement(fn)` (a function).\n * Demanding a `Schema` suffix on any of those is simply wrong.\n * `zod/packages/zod/src/v3/tests/record.test.ts:166`\n * (`const result1 = z.record(z.any()).parse({ foo: undefined })`).\n * (c) NAMES THAT ALREADY SAY \"SCHEMA\" — 421 hits. `ZOD_SUFFIX_RE` is anchored\n * AND case-sensitive, so `schema`, `schema1` and `numberSchemaOptional`\n * were all declared non-conforming despite being unmistakable at a\n * glance. `schema` alone accounts for 371 reports, e.g.\n * `zod/packages/treeshake/zod-string.ts:3`. This widens only the ACCEPT\n * side, so it cannot make `require-zod-form-validation` — which uses the\n * shared `_zod.ts` recognisers to accept a receiver — reject anything it\n * previously accepted.\n */\n\nimport { ESLintUtils, type TSESTree, AST_NODE_TYPES } from \"@typescript-eslint/utils\";\n\nimport { isGeneratedFile, isTestFile } from \"./_paths.js\";\nimport { ZOD_PREFIX_RE, ZOD_SCHEMA_NAME_RE, ZOD_SUFFIX_RE } from \"./_zod.js\";\n\ntype MessageIds = \"zPrefix\" | \"schemaSuffix\" | \"zodSchemaName\";\ntype Convention = \"prefix\" | \"suffix\" | \"either\";\ntype Options = readonly [\n {\n convention?: Convention;\n }?,\n];\n\nconst CONVENTIONS: Record<\n Convention,\n { readonly test: RegExp; readonly messageId: MessageIds }\n> = {\n prefix: { test: ZOD_PREFIX_RE, messageId: \"zPrefix\" },\n suffix: { test: ZOD_SUFFIX_RE, messageId: \"schemaSuffix\" },\n either: { test: ZOD_SCHEMA_NAME_RE, messageId: \"zodSchemaName\" },\n};\n\n/**\n * Guard (c): the binding already reads as a schema. `ZOD_SUFFIX_RE` is anchored\n * and case-sensitive by design (it is also the *recogniser* other rules use), so\n * it rejects `schema`, `schema1` and `numberSchemaOptional`. Those need no\n * rename — the whole point of the convention is that a reader spots a schema at\n * the use site, and every one of them does. Accepting them here only widens what\n * this rule tolerates; it never makes the shared recogniser stricter.\n */\nconst CONTAINS_SCHEMA_RE = /schema/i;\n\n/**\n * Guard (b): terminal methods on a `z.…` chain whose result is NOT a schema.\n * `.parse`/`.safeParse` return a parsed value or a result envelope, the codec\n * methods return the converted value, `z.toJSONSchema` returns a plain JSON\n * object, `z.registry()` returns a registry, and `.implement()` returns a\n * function. Naming any of them `xSchema` would be a lie.\n */\nconst NON_SCHEMA_TERMINALS: ReadonlySet<string> = new Set([\n \"parse\",\n \"parseAsync\",\n \"safeParse\",\n \"safeParseAsync\",\n \"encode\",\n \"decode\",\n \"encodeAsync\",\n \"decodeAsync\",\n \"safeEncode\",\n \"safeDecode\",\n \"safeEncodeAsync\",\n \"safeDecodeAsync\",\n \"toJSONSchema\",\n \"registry\",\n \"implement\",\n]);\n\n/** The method name a call chain ends on, e.g. `safeParse` for `z.string().safeParse(x)`. */\nconst terminalMethodName = (callee: TSESTree.MemberExpression): string | null =>\n !callee.computed && callee.property.type === AST_NODE_TYPES.Identifier\n ? callee.property.name\n : null;\n\n/**\n * Walks down a (possibly chained) callee like `z.object().extend().refine()` and\n * returns `true` if the chain originates from a bare `z` identifier — i.e. the\n * outermost MemberExpression on the chain has `z` as its receiver.\n */\nconst calleeChainStartsWithZ = (node: TSESTree.Node): boolean => {\n let current: TSESTree.Node = node;\n\n while (current.type === AST_NODE_TYPES.MemberExpression) {\n const receiver: TSESTree.Node = current.object;\n if (receiver.type === AST_NODE_TYPES.Identifier && receiver.name === \"z\") {\n return true;\n }\n if (receiver.type === AST_NODE_TYPES.CallExpression) {\n current = receiver.callee;\n continue;\n }\n return false;\n }\n\n return false;\n};\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"zod-naming-convention\",\n meta: {\n type: \"suggestion\",\n docs: {\n description:\n \"Enforce a consistent Zod schema naming convention — a `Z` prefix (`ZUser`) or a `Schema` suffix (`userSchema`); both are accepted by default.\",\n },\n schema: [\n {\n type: \"object\",\n additionalProperties: false,\n properties: {\n convention: {\n type: \"string\",\n enum: [\"prefix\", \"suffix\", \"either\"],\n },\n },\n },\n ],\n messages: {\n zPrefix: \"Zod schema names should start with Z (e.g. `ZUser`)\",\n schemaSuffix: \"Zod schema names should end with Schema (e.g. `userSchema`)\",\n zodSchemaName:\n \"Zod schema names should start with Z (`ZUser`) or end with Schema (`userSchema`)\",\n },\n },\n defaultOptions: [{}],\n create(context, [optionsArg]) {\n const convention = optionsArg?.convention ?? \"either\";\n const { test, messageId } = CONVENTIONS[convention];\n // Guard (c) relaxes the SUFFIX spelling, so it must not apply to a team that\n // explicitly asked for prefix-only — there `userschema` really is off-convention.\n const acceptsSchemaWord = convention !== \"prefix\";\n\n // Guard (a): a schema declared in a test is a local fixture, not an API.\n if (isTestFile(context.filename) || isGeneratedFile(context.filename, context.sourceCode.text)) {\n return {};\n }\n\n return {\n VariableDeclarator(node: TSESTree.VariableDeclarator): void {\n const init = node.init;\n if (init === null || init === undefined) return;\n if (init.type !== AST_NODE_TYPES.CallExpression) return;\n\n const callee = init.callee;\n if (callee.type !== AST_NODE_TYPES.MemberExpression) return;\n\n if (!calleeChainStartsWithZ(callee)) return;\n\n // Guard (b): the chain ends on a call that yields a value/result, not a schema.\n const terminal = terminalMethodName(callee);\n if (terminal !== null && NON_SCHEMA_TERMINALS.has(terminal)) return;\n\n if (node.id.type !== AST_NODE_TYPES.Identifier) return;\n if (test.test(node.id.name)) return;\n // Guard (c): the name already reads as a schema in any casing.\n if (acceptsSchemaWord && CONTAINS_SCHEMA_RE.test(node.id.name)) return;\n\n context.report({\n node: node.id,\n messageId,\n });\n },\n };\n },\n});\n","/**\n * @fileoverview TS port of Python SARJ028\n * (`no-cors-wildcard-with-credentials`). Flags CORS configuration that reflects\n * ANY origin (`\"*\"`) while ALSO allowing credentials. The browser treats\n * `Access-Control-Allow-Origin: *` together with\n * `Access-Control-Allow-Credentials: true` as a directive to reflect the\n * request's Origin and expose authenticated (cookie/session) responses — which\n * lets any website read them cross-origin. That is a credential-theft surface.\n *\n * Two shapes are detected, and BOTH the wildcard origin and credentials=true\n * must co-occur before the rule fires (a `\"*\"` origin without credentials, or\n * credentials with a specific origin, is safe and is NOT reported):\n *\n * 1. A `cors(...)` / `new Cors(...)` call whose options `ObjectExpression`\n * has `credentials: true` AND an `origin` property whose value subtree\n * contains a `\"*\"` string literal anywhere — the bare `\"*\"`, the `[\"*\"]`\n * array, or a `flag ? origins : \"*\"` conditional branch. Reported at the\n * call.\n *\n * 2. Manual header setting where, within the SAME function (or module) scope,\n * `Access-Control-Allow-Origin` is set to `\"*\"` AND\n * `Access-Control-Allow-Credentials` is set to `\"true\"` — via\n * `res.setHeader(...)`, `headers.set(...)` / `.append(...)` (covers\n * `NextResponse` header objects), or a single object literal\n * `{ \"Access-Control-Allow-Origin\": \"*\", \"Access-Control-Allow-Credentials\": \"true\" }`.\n * Header-name matching is case-insensitive. The object-literal form is\n * reported at the object; the split `setHeader`/`set` form is reported at\n * the wildcard-origin call.\n *\n * References:\n * - https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#credentialed_requests_and_wildcards\n */\n\nimport { ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\ntype MessageIds = \"corsWildcardWithCredentials\";\ntype Options = readonly [];\n\nconst ACAO_HEADER = \"access-control-allow-origin\";\nconst ACAC_HEADER = \"access-control-allow-credentials\";\nconst HEADER_SET_METHODS = new Set([\"setheader\", \"set\", \"append\"]);\n\n/**\n * True only for the boolean literal `true` (not `1`, not a truthy expression).\n */\nfunction isTrueLiteral(node: TSESTree.Node): boolean {\n return node.type === \"Literal\" && node.value === true;\n}\n\n/**\n * True if `node` is a string literal whose value equals `\"true\"`\n * (case-insensitive), or the boolean literal `true`. Header values are strings,\n * but some frameworks coerce a boolean, so both are accepted.\n */\nfunction isCredentialsTrueValue(node: TSESTree.Node): boolean {\n if (node.type === \"Literal\") {\n if (node.value === true) {\n return true;\n }\n if (typeof node.value === \"string\") {\n return node.value.trim().toLowerCase() === \"true\";\n }\n }\n return false;\n}\n\n/**\n * True if `node` is the string literal `\"*\"`.\n */\nfunction isStarLiteral(node: TSESTree.Node): boolean {\n return node.type === \"Literal\" && node.value === \"*\";\n}\n\n/**\n * True if a `\"*\"` string literal appears anywhere in `node`'s subtree. Walking\n * the whole subtree catches `\"*\"`, `[\"*\"]`, and the `flag ? origins : \"*\"`\n * conditional branch. A dynamic `origin: someVar` has no `\"*\"` literal, so it\n * does not fire.\n */\nfunction subtreeContainsStarLiteral(node: TSESTree.Node): boolean {\n if (isStarLiteral(node)) {\n return true;\n }\n for (const key of Object.keys(node)) {\n if (key === \"parent\" || key === \"loc\" || key === \"range\") {\n continue;\n }\n const value = (node as unknown as Record<string, unknown>)[key];\n if (Array.isArray(value)) {\n for (const child of value) {\n if (isNode(child) && subtreeContainsStarLiteral(child)) {\n return true;\n }\n }\n } else if (isNode(value) && subtreeContainsStarLiteral(value)) {\n return true;\n }\n }\n return false;\n}\n\nfunction isNode(value: unknown): value is TSESTree.Node {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as { type?: unknown }).type === \"string\"\n );\n}\n\n/**\n * Returns the (non-computed) string name of a property key, or `undefined`.\n */\nfunction propertyKeyName(prop: TSESTree.Property): string | undefined {\n if (prop.computed) {\n return undefined;\n }\n const key = prop.key;\n if (key.type === \"Identifier\") {\n return key.name;\n }\n if (key.type === \"Literal\" && typeof key.value === \"string\") {\n return key.value;\n }\n return undefined;\n}\n\n/**\n * Extracts the callee's terminal identifier name for a call/new expression\n * (`cors` for `cors(...)` and `app.cors(...)`, `Cors` for `new Cors(...)`).\n */\nfunction calleeName(\n node: TSESTree.CallExpression | TSESTree.NewExpression,\n): string | undefined {\n const callee = node.callee;\n if (callee.type === \"Identifier\") {\n return callee.name;\n }\n if (\n callee.type === \"MemberExpression\" &&\n !callee.computed &&\n callee.property.type === \"Identifier\"\n ) {\n return callee.property.name;\n }\n return undefined;\n}\n\n/**\n * Detects the `cors({ origin: \"*\", credentials: true })` shape. Returns true\n * when the callee is `cors` / `Cors` and the first ObjectExpression argument\n * has `credentials: true` AND an `origin` whose subtree contains `\"*\"`.\n */\nfunction isCorsWildcardCredentialsCall(\n node: TSESTree.CallExpression | TSESTree.NewExpression,\n): boolean {\n const name = calleeName(node);\n if (name === undefined || name.toLowerCase() !== \"cors\") {\n return false;\n }\n const options = node.arguments.find(\n (arg): arg is TSESTree.ObjectExpression => arg.type === \"ObjectExpression\",\n );\n if (options === undefined) {\n return false;\n }\n let hasCredentials = false;\n let hasWildcardOrigin = false;\n for (const prop of options.properties) {\n if (prop.type !== \"Property\") {\n continue;\n }\n const key = propertyKeyName(prop);\n if (key === \"credentials\" && isTrueLiteral(prop.value)) {\n hasCredentials = true;\n } else if (key === \"origin\" && subtreeContainsStarLiteral(prop.value)) {\n hasWildcardOrigin = true;\n }\n }\n return hasCredentials && hasWildcardOrigin;\n}\n\n/**\n * True if the ObjectExpression is a header map setting BOTH\n * `Access-Control-Allow-Origin: \"*\"` and\n * `Access-Control-Allow-Credentials: \"true\"` (case-insensitive keys).\n */\nfunction isWildcardCredentialsHeaderObject(\n node: TSESTree.ObjectExpression,\n): boolean {\n let wildcardOrigin = false;\n let credentialsTrue = false;\n for (const prop of node.properties) {\n if (prop.type !== \"Property\") {\n continue;\n }\n const key = propertyKeyName(prop);\n if (key === undefined) {\n continue;\n }\n const header = key.toLowerCase();\n if (header === ACAO_HEADER && isStarLiteral(prop.value)) {\n wildcardOrigin = true;\n } else if (header === ACAC_HEADER && isCredentialsTrueValue(prop.value)) {\n credentialsTrue = true;\n }\n }\n return wildcardOrigin && credentialsTrue;\n}\n\ntype HeaderSetKind = \"origin\" | \"credentials\";\n\n/**\n * Classifies a `x.setHeader(name, value)` / `x.set(name, value)` /\n * `x.append(name, value)` call as an ACAO-wildcard set, an ACAC-true set, or\n * neither.\n */\nfunction classifyHeaderSetCall(\n node: TSESTree.CallExpression,\n): HeaderSetKind | undefined {\n const callee = node.callee;\n if (\n callee.type !== \"MemberExpression\" ||\n callee.computed ||\n callee.property.type !== \"Identifier\" ||\n !HEADER_SET_METHODS.has(callee.property.name.toLowerCase())\n ) {\n return undefined;\n }\n const [nameArg, valueArg] = node.arguments;\n if (\n nameArg === undefined ||\n valueArg === undefined ||\n nameArg.type !== \"Literal\" ||\n typeof nameArg.value !== \"string\"\n ) {\n return undefined;\n }\n const header = nameArg.value.toLowerCase();\n if (header === ACAO_HEADER && isStarLiteral(valueArg)) {\n return \"origin\";\n }\n if (header === ACAC_HEADER && isCredentialsTrueValue(valueArg)) {\n return \"credentials\";\n }\n return undefined;\n}\n\n/**\n * Nearest enclosing function node, or `undefined` for module scope. Used to\n * group split `setHeader`/`set` header assignments so a wildcard origin and a\n * credentials=true set only pair up when they live in the same scope.\n */\nfunction enclosingScope(node: TSESTree.Node): TSESTree.Node | undefined {\n let current: TSESTree.Node | undefined = node.parent;\n while (current) {\n if (\n current.type === \"FunctionDeclaration\" ||\n current.type === \"FunctionExpression\" ||\n current.type === \"ArrowFunctionExpression\"\n ) {\n return current;\n }\n current = current.parent;\n }\n return undefined;\n}\n\ninterface ScopeHeaderSets {\n originNodes: TSESTree.CallExpression[];\n credentialsNodes: TSESTree.CallExpression[];\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-cors-wildcard-with-credentials\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n 'Disallow CORS that reflects any Origin (`\"*\"`) while allowing credentials; any site could then read authenticated responses. Enumerate explicit trusted origins instead.',\n },\n schema: [],\n messages: {\n corsWildcardWithCredentials:\n 'CORS reflects any Origin (`\"*\"`) while allowing credentials — any site can read authenticated responses. Enumerate explicit trusted origins instead of using `\"*\"` with credentials.',\n },\n },\n defaultOptions: [],\n create(context) {\n const scopeHeaderSets = new Map<TSESTree.Node | \"module\", ScopeHeaderSets>();\n\n function recordHeaderSet(\n node: TSESTree.CallExpression,\n kind: HeaderSetKind,\n ): void {\n const key = enclosingScope(node) ?? \"module\";\n let entry = scopeHeaderSets.get(key);\n if (entry === undefined) {\n entry = { originNodes: [], credentialsNodes: [] };\n scopeHeaderSets.set(key, entry);\n }\n if (kind === \"origin\") {\n entry.originNodes.push(node);\n } else {\n entry.credentialsNodes.push(node);\n }\n }\n\n return {\n NewExpression(node: TSESTree.NewExpression): void {\n if (isCorsWildcardCredentialsCall(node)) {\n context.report({ node, messageId: \"corsWildcardWithCredentials\" });\n }\n },\n CallExpression(node: TSESTree.CallExpression): void {\n if (isCorsWildcardCredentialsCall(node)) {\n context.report({ node, messageId: \"corsWildcardWithCredentials\" });\n return;\n }\n const kind = classifyHeaderSetCall(node);\n if (kind !== undefined) {\n recordHeaderSet(node, kind);\n }\n },\n ObjectExpression(node: TSESTree.ObjectExpression): void {\n if (isWildcardCredentialsHeaderObject(node)) {\n context.report({ node, messageId: \"corsWildcardWithCredentials\" });\n }\n },\n \"Program:exit\"(): void {\n for (const { originNodes, credentialsNodes } of scopeHeaderSets.values()) {\n if (originNodes.length > 0 && credentialsNodes.length > 0) {\n for (const node of originNodes) {\n context.report({\n node,\n messageId: \"corsWildcardWithCredentials\",\n });\n }\n }\n }\n },\n };\n },\n});\n","/**\n * @fileoverview Flag `.catch()` handlers that silently swallow the rejection:\n * `promise.catch(() => null)` and friends. These are the promise-chain twin of\n * the empty-`catch`-block anti-pattern already covered by the try/catch rules —\n * the failure vanishes with no log, no metric, no rethrow, and the caller\n * receives a sentinel it usually cannot distinguish from a real value.\n *\n * Only handlers that provably do nothing are flagged: an arrow/function whose\n * entire body is a bare literal (`null`, `undefined`, a number, a string, a\n * boolean), an empty object/array literal, an empty block, or a block that\n * only `return`s one of those. A handler that references its error parameter\n * or calls anything (logging, metrics, rethrow) is never flagged.\n *\n * Two corpus-driven exemptions (5-repo sweep, 2026-07: 11/35 raw hits — 31% —\n * were these deliberate idioms):\n * - `res.json().catch(() => ({}))` / `res.text().catch(() => '')` — the\n * body-parse-fallback idiom while composing error diagnostics; the parse\n * failure itself is not the signal being handled.\n * - Test files (`.test.` / `.spec.` / `__tests__/`), where silencing a\n * promise is routine unhandled-rejection suppression.\n *\n * A SECOND SWEEP (2220 files across zod / TanStack Query / react-router / swr /\n * zustand, 2026-07) produced 16 hits, of which 11 were false positives in three\n * families. All three are now exempt:\n *\n * - **Documented on purpose.** The handler body (or the line above / beside\n * the call) carries a comment explaining the suppression. The rule's\n * complaint is that the decision is invisible; a comment is exactly the\n * thing that makes it visible, and the author has already answered.\n * `query/packages/query-core/src/thenable.ts:54`\n * (`thenable.catch(() => { /* prevent unhandled rejection errors *\\/ })`) and\n * `react-router/packages/react-router/lib/router/router.ts:6052-6053`\n * (`// Prevent unhandled rejection errors - handled inside of \\`callLoadOrAction\\``\n * directly above `lazyRoutePromise.catch(() => {})`) are the shape: the\n * promise is ALSO consumed by the real handler, and this `.catch` exists only\n * to stop the runtime's unhandled-rejection warning. 7 of the 16 hits.\n * - **The chain continues.** `p.catch(() => null).then(…)` — the sentinel is\n * consumed by the very next link, which IS the recovery, so no caller ever\n * receives an indistinguishable value.\n * `react-router/integration/helpers/playwright-fixture.ts:318`.\n * - **Teardown calls.** `.cancel()` / `.close()` / `.abort()` / `.destroy()` /\n * `.dispose()` / `.unlock()` reject when the resource is already gone, which\n * is the outcome the caller wanted; the rejection is not the signal, exactly\n * as for the `res.json()` fallback above.\n * `react-router/packages/react-router/lib/rsc/html-stream/server.ts:80`\n * (`await rscReader.cancel(reason).catch(() => {})`, inside the stream's own\n * abort path). 3 of the 16 hits.\n */\n\nimport { AST_NODE_TYPES, ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\nimport { isTestFile } from \"./_paths.js\";\n\ntype MessageIds = \"silentCatch\";\ntype Options = readonly [];\n\n/** True for `x.json()` / `x.text()` — the receiver of a body-parse-fallback catch. */\nfunction isBodyParseCall(node: TSESTree.Expression): boolean {\n return (\n node.type === AST_NODE_TYPES.CallExpression &&\n node.arguments.length === 0 &&\n node.callee.type === AST_NODE_TYPES.MemberExpression &&\n !node.callee.computed &&\n node.callee.property.type === AST_NODE_TYPES.Identifier &&\n (node.callee.property.name === \"json\" ||\n node.callee.property.name === \"text\")\n );\n}\n\n/**\n * Teardown methods that reject when the resource is already gone — which is the\n * state the caller was asking for. The rejection carries no signal, so silencing\n * it is correct, the same reasoning as the `res.json()` fallback above.\n */\nconst TEARDOWN_METHODS: ReadonlySet<string> = new Set([\n \"cancel\",\n \"close\",\n \"abort\",\n \"destroy\",\n \"dispose\",\n \"release\",\n \"unlock\",\n \"disconnect\",\n]);\n\n/**\n * Tooling directives are machinery, not an explanation of the swallow. An\n * `eslint-disable` already suppresses the report through the normal channel, so\n * counting it as documentation would turn every directive into an unused one.\n */\nconst DIRECTIVE_COMMENT_RE =\n /^\\s*(eslint-|@ts-|prettier-ignore|biome-ignore|c8 |v8 |istanbul )/;\n\nconst isExplanatory = (comment: { value: string }): boolean =>\n !DIRECTIVE_COMMENT_RE.test(comment.value);\n\n/** True for `reader.cancel(reason)` / `stream.close()` — a teardown receiver. */\nfunction isTeardownCall(node: TSESTree.Expression): boolean {\n return (\n node.type === AST_NODE_TYPES.CallExpression &&\n node.callee.type === AST_NODE_TYPES.MemberExpression &&\n !node.callee.computed &&\n node.callee.property.type === AST_NODE_TYPES.Identifier &&\n TEARDOWN_METHODS.has(node.callee.property.name)\n );\n}\n\n/** True for expressions that provably discard the error: bare literals,\n * `undefined`, empty object/array literals. */\nfunction isSilentExpression(node: TSESTree.Expression): boolean {\n switch (node.type) {\n case AST_NODE_TYPES.Literal:\n // null / number / string / boolean literals (regex literals excluded —\n // nobody writes `.catch(() => /x/)` and they are not sentinel values).\n return !(\"regex\" in node);\n case AST_NODE_TYPES.Identifier:\n return node.name === \"undefined\";\n case AST_NODE_TYPES.UnaryExpression:\n // `void 0` — the other spelling of undefined.\n return (\n node.operator === \"void\" &&\n node.argument.type === AST_NODE_TYPES.Literal\n );\n case AST_NODE_TYPES.ObjectExpression:\n return node.properties.length === 0;\n case AST_NODE_TYPES.ArrayExpression:\n return node.elements.length === 0;\n case AST_NODE_TYPES.TSAsExpression:\n // `.catch(() => null as Foo | null)` is still silent.\n return isSilentExpression(node.expression);\n default:\n return false;\n }\n}\n\n/** True when the handler's whole body provably does nothing with the error. */\nfunction isSilentHandler(\n handler: TSESTree.ArrowFunctionExpression | TSESTree.FunctionExpression,\n): boolean {\n const body = handler.body;\n\n if (body.type !== AST_NODE_TYPES.BlockStatement) {\n // Arrow expression body: `.catch(() => null)`\n return isSilentExpression(body);\n }\n\n if (body.body.length === 0) {\n // `.catch(() => {})` / `.catch(function () {})`\n return true;\n }\n\n if (body.body.length === 1) {\n const only = body.body[0];\n if (only !== undefined && only.type === AST_NODE_TYPES.ReturnStatement) {\n // `.catch(() => { return null; })`\n return only.argument === null || isSilentExpression(only.argument);\n }\n }\n\n return false;\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-silent-promise-catch\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Disallow `.catch()` handlers that silently swallow the rejection (e.g. `.catch(() => null)`); log, rethrow, or handle the error.\",\n },\n schema: [],\n messages: {\n silentCatch:\n \"This `.catch()` swallows the rejection without logging, rethrowing, or handling it — failures become invisible and callers get an indistinguishable sentinel. Log the error (and only then map to a fallback), or let it propagate.\",\n },\n },\n defaultOptions: [],\n create(context) {\n if (isTestFile(context.filename)) {\n return {};\n }\n\n /**\n * True when the suppression is DOCUMENTED: a comment inside the handler\n * body, trailing the call on the same line, or on the line(s) directly above\n * the statement the call belongs to. The rule's complaint is that the\n * decision to discard the error is invisible — a comment is precisely what\n * makes it visible, so there is nothing left to report.\n */\n const hasExplanatoryComment = (\n call: TSESTree.CallExpression,\n handler: TSESTree.ArrowFunctionExpression | TSESTree.FunctionExpression,\n ): boolean => {\n const sourceCode = context.sourceCode;\n if (sourceCode.getCommentsInside(handler).some(isExplanatory)) {\n return true;\n }\n // Walk out to the enclosing statement so a comment above or beside\n // `lazyRoutePromise.catch(() => {});` is found rather than one sitting\n // between the receiver and `.catch`.\n let statement: TSESTree.Node = call;\n while (\n statement.parent !== undefined &&\n statement.parent !== null &&\n !statement.type.endsWith(\"Statement\") &&\n statement.type !== AST_NODE_TYPES.VariableDeclaration\n ) {\n statement = statement.parent;\n }\n if (sourceCode.getCommentsBefore(statement).some(isExplanatory)) {\n return true;\n }\n return sourceCode\n .getCommentsAfter(statement)\n .some(\n (c) => isExplanatory(c) && c.loc.start.line === statement.loc.end.line,\n );\n };\n\n return {\n CallExpression(node: TSESTree.CallExpression): void {\n if (\n node.callee.type !== AST_NODE_TYPES.MemberExpression ||\n node.callee.computed ||\n node.callee.property.type !== AST_NODE_TYPES.Identifier ||\n node.callee.property.name !== \"catch\"\n ) {\n return;\n }\n\n if (isBodyParseCall(node.callee.object)) {\n return;\n }\n\n if (isTeardownCall(node.callee.object)) {\n return;\n }\n\n // `p.catch(() => null).then(...)` — the next link consumes the fallback,\n // so it is a recovery step, not a value handed back to an outside caller.\n if (\n node.parent.type === AST_NODE_TYPES.MemberExpression &&\n node.parent.object === node\n ) {\n return;\n }\n\n if (node.arguments.length !== 1) {\n return;\n }\n const handler = node.arguments[0];\n if (\n handler === undefined ||\n (handler.type !== AST_NODE_TYPES.ArrowFunctionExpression &&\n handler.type !== AST_NODE_TYPES.FunctionExpression)\n ) {\n return;\n }\n\n if (hasExplanatoryComment(node, handler)) {\n return;\n }\n\n if (isSilentHandler(handler)) {\n // Anchor on the CallExpression, not the handler: when the handler\n // sits on a later line than the call (multi-line `.catch(`), a\n // handler-anchored report escapes an `eslint-disable-next-line`\n // above the call AND marks that directive as unused.\n context.report({ node, messageId: \"silentCatch\" });\n }\n },\n };\n },\n});\n","/**\n * @fileoverview Require an abort `signal` on global `fetch()` calls. A fetch\n * without a timeout hangs forever when the upstream stalls, tying up the\n * request (or worker) with it. Pass `AbortSignal.timeout(ms)` — or a signal\n * from an `AbortController` — in the init object.\n *\n * Scope is deliberately narrow to keep false positives near zero:\n * - Only calls that resolve to the *global* `fetch` are checked: bare\n * `fetch(...)` (unless a local binding — import, parameter, variable —\n * shadows it) plus the explicit global spellings `globalThis.fetch(...)`,\n * `window.fetch(...)`, and `self.fetch(...)`. Other member calls like\n * Cloudflare Workers service bindings (`env.MY_SERVICE.fetch(...)`) or\n * custom clients (`client.fetch(...)`) are skipped.\n * - A call is flagged only when the init argument is absent, or is an\n * object literal that provably lacks `signal` (no spread, no `signal`\n * key). Anything dynamic (an identifier, a call result, a spread) is\n * assumed to carry a signal.\n * - Single-argument calls whose argument is not a string/template literal\n * are skipped: `fetch(request)` / `fetch(c.req.raw.clone())` are proxy\n * passthroughs (Workers idiom) where the inbound Request governs the\n * lifetime and attaching a fresh signal is impossible or wrong.\n * - Test files and one-off tooling (`scripts/**`, `*.mjs`) are skipped —\n * dev scripts die with the terminal, so hang-hardening is noise there.\n * - Codemod fixtures under `__testfixtures__/` are skipped. Corpus sweep\n * (2220 files across zod / TanStack Query / react-router / swr / zustand,\n * 2026-07): 8 of 79 hits came from a single jscodeshift input/output pair,\n * `query/packages/query-codemods/src/v5/remove-overloads/__testfixtures__/bug-reports.input.tsx`\n * and its `.output.tsx` twin. Those files are the BEFORE and AFTER text a\n * codemod test diffs; editing them to add a signal would break the test they\n * exist to drive, and the code never runs. The shared `isTestFile` predicate\n * knows `fixtures/` but not jscodeshift's `__testfixtures__/` spelling.\n *\n * Wrapper modules that centralize timeout handling can be exempted via the\n * `allowIn` glob-pattern option.\n */\n\nimport {\n AST_NODE_TYPES,\n ASTUtils,\n ESLintUtils,\n type TSESTree,\n} from \"@typescript-eslint/utils\";\n\nimport { isScriptFile, isTestFile } from \"./_paths.js\";\n\n/** jscodeshift's fixture directory — input/output text, not code that runs. */\nconst CODEMOD_FIXTURE_RE = /[\\\\/]__testfixtures__[\\\\/]/;\n\ntype MessageIds = \"missingSignal\";\ntype Options = readonly [\n {\n allowIn?: readonly string[];\n }?,\n];\n\n/** The explicit-global receivers of `<obj>.fetch(...)`. */\nconst GLOBAL_OBJECTS: ReadonlySet<string> = new Set([\n \"globalThis\",\n \"window\",\n \"self\",\n]);\n\nfunction matchesAnyPattern(\n filename: string,\n patterns: readonly string[],\n): boolean {\n for (const pattern of patterns) {\n // Convert minimatch-ish globs to regex: ** -> .*, * -> [^/\\\\]*\n const regexSource = pattern\n .replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\")\n .replace(/\\*\\*/g, \"::DOUBLESTAR::\")\n .replace(/\\*/g, \"[^/\\\\\\\\]*\")\n .replace(/::DOUBLESTAR::/g, \".*\");\n if (new RegExp(`^${regexSource}$`).test(filename)) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * True when the init argument provably lacks an abort signal: it is an object\n * literal with no `signal` property and no spread element. Any other shape\n * (identifier, call, spread, conditional, ...) may carry a signal, so it is\n * treated as safe.\n */\nfunction initProvablyLacksSignal(init: TSESTree.CallExpressionArgument): boolean {\n if (init.type !== AST_NODE_TYPES.ObjectExpression) {\n return false;\n }\n for (const prop of init.properties) {\n if (prop.type === AST_NODE_TYPES.SpreadElement) {\n return false;\n }\n if (\n (prop.key.type === AST_NODE_TYPES.Identifier &&\n prop.key.name === \"signal\") ||\n (prop.key.type === AST_NODE_TYPES.Literal && prop.key.value === \"signal\")\n ) {\n return false;\n }\n // A computed key could be \"signal\" at runtime; assume it is.\n if (prop.computed) {\n return false;\n }\n }\n return true;\n}\n\n/** True for a string or template literal — a URL spelled inline. */\nfunction isStringish(node: TSESTree.CallExpressionArgument): boolean {\n return (\n (node.type === AST_NODE_TYPES.Literal && typeof node.value === \"string\") ||\n node.type === AST_NODE_TYPES.TemplateLiteral\n );\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"require-fetch-timeout\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Require an abort `signal` (e.g. `AbortSignal.timeout(ms)`) on global `fetch()` calls so stalled upstreams cannot hang the caller forever.\",\n },\n schema: [\n {\n type: \"object\",\n additionalProperties: false,\n properties: {\n allowIn: {\n description:\n \"Glob patterns for wrapper modules exempt from the rule. Matched against the ABSOLUTE file path, so anchor with a `**/` prefix (e.g. `**/http-client.ts`).\",\n type: \"array\",\n items: { type: \"string\" },\n },\n },\n },\n ],\n messages: {\n missingSignal:\n \"This `fetch()` has no abort `signal` — a stalled upstream will hang it forever. Pass `{ signal: AbortSignal.timeout(ms) }` or a signal from an AbortController.\",\n },\n },\n defaultOptions: [{}],\n create(context, [optionsArg]) {\n if (\n isTestFile(context.filename) ||\n isScriptFile(context.filename) ||\n CODEMOD_FIXTURE_RE.test(context.filename)\n ) {\n return {};\n }\n const allowIn = optionsArg?.allowIn ?? [];\n if (allowIn.length > 0 && matchesAnyPattern(context.filename, allowIn)) {\n return {};\n }\n\n /** True when `identifier` resolves to the global (no local binding shadows it). */\n function resolvesToGlobal(identifier: TSESTree.Identifier): boolean {\n const scope = context.sourceCode.getScope(identifier);\n const variable = ASTUtils.findVariable(scope, identifier.name);\n // Unresolved → implicit global. Resolved with zero defs → declared\n // global (languageOptions.globals). Any def is a local shadow.\n return variable === null || variable.defs.length === 0;\n }\n\n /** True for `fetch(...)` / `globalThis.fetch(...)` / `window.fetch(...)` /\n * `self.fetch(...)` where the receiver resolves to the global scope. */\n function isGlobalFetchCall(callee: TSESTree.Expression): boolean {\n if (callee.type === AST_NODE_TYPES.Identifier) {\n return callee.name === \"fetch\" && resolvesToGlobal(callee);\n }\n return (\n callee.type === AST_NODE_TYPES.MemberExpression &&\n !callee.computed &&\n callee.property.type === AST_NODE_TYPES.Identifier &&\n callee.property.name === \"fetch\" &&\n callee.object.type === AST_NODE_TYPES.Identifier &&\n GLOBAL_OBJECTS.has(callee.object.name) &&\n resolvesToGlobal(callee.object)\n );\n }\n\n return {\n CallExpression(node: TSESTree.CallExpression): void {\n if (!isGlobalFetchCall(node.callee)) {\n return;\n }\n\n // Proxy passthrough: a lone non-string argument is a Request (or\n // equivalent) being forwarded — the inbound request owns the\n // lifetime, and a fresh signal cannot be attached without an init.\n const [first, init] = node.arguments;\n if (node.arguments.length === 1 && first !== undefined && !isStringish(first)) {\n return;\n }\n\n if (init === undefined || initProvablyLacksSignal(init)) {\n context.report({ node, messageId: \"missingSignal\" });\n }\n },\n };\n },\n});\n","/**\n * @fileoverview Disallow `try` blocks whose body contains more than three\n * top-level statements that can throw (TS port of Python's SARJ007).\n *\n * A fat `try` body obscures which statement is actually expected to throw and\n * widens the blast radius of the `catch` handler: unrelated failures get caught\n * (and often swallowed or mis-reported) by a handler written for a different\n * operation. Keep the `try` skinny — isolate the throwing statement(s) and move\n * the non-throwing setup and follow-up work outside.\n *\n * Only top-level statements that can *throw* are counted. What counts, and the\n * guards that keep the count aligned with intent (tuned against ~5.6k real TS\n * files to drive false positives to ~zero):\n *\n * - An `await` always counts — awaiting a promise is the canonical throwing\n * operation in async TS. A statement with an `await` in its same-scope\n * subtree counts.\n * - A synchronous call / `new` whose value is *used* (assigned, returned,\n * branched on, or passed as an argument) counts — e.g. `const x = parse(s)`,\n * `return build(x)`, `if (!validate(x))`.\n * - A bare fire-and-forget call statement with no `await` does NOT count. In\n * idiomatic TS these are side effects — React state setters (`setOpen(false)`),\n * toasts (`toast.error(...)`), `router.refresh()`, logging, optional\n * callbacks (`onSuccess?.()`). They are the post-success UI work that\n * naturally trails the one awaited action; counting them flagged nearly\n * every event handler. The exemption is applied STRUCTURALLY, recursing\n * through blocks and `if`/`else` branches, so wrapping the same call in a\n * guard (`if (!res.ok) { logEvent(\"http_error\", { path }); return null; }`)\n * does not resurrect it. A guard's TEST is still examined — a call whose\n * result is branched on (`if (!validate(x))`) still counts.\n * - Pure, non-throwing array / string / `Map` / `Object` / `Math` / `JSON`\n * helpers (`.map`, `.filter`, `.push`, `.get`, `.join`, `Object.keys`, ...)\n * do NOT count — they are data plumbing, not the operation being guarded.\n * - Calls inside a nested function / arrow body do not run when the `try`\n * executes, so they are not counted (same-scope walk).\n *\n * Two structural exemptions match the Python rule:\n *\n * - A `finally` clause is a deliberate cleanup contract that couples the body\n * to the handler — exempt.\n * - A `catch` handler guaranteed to re-throw (its body's last statement is a\n * `throw`) makes the wide body uniform error-context wrapping, not an\n * over-broad swallow — exempt.\n */\n\nimport {\n ESLintUtils,\n type TSESTree,\n AST_NODE_TYPES,\n} from \"@typescript-eslint/utils\";\n\nimport { isGeneratedFile } from \"./_paths.js\";\n\ntype MessageIds = \"fatTryBlock\";\ntype Options = readonly [];\n\nconst MAX_TRY_BODY_STATEMENTS = 3;\n\nconst NESTED_FUNCTION_TYPES = new Set<AST_NODE_TYPES>([\n AST_NODE_TYPES.FunctionDeclaration,\n AST_NODE_TYPES.FunctionExpression,\n AST_NODE_TYPES.ArrowFunctionExpression,\n]);\n\n/** Non-throwing member methods — array / string / Map / Set data plumbing. */\nconst PURE_METHODS = new Set<string>([\n \"map\", \"filter\", \"forEach\", \"reduce\", \"reduceRight\", \"find\", \"findIndex\",\n \"findLast\", \"findLastIndex\", \"some\", \"every\", \"push\", \"pop\", \"shift\",\n \"unshift\", \"slice\", \"splice\", \"concat\", \"flat\", \"flatMap\", \"join\", \"reverse\",\n \"sort\", \"fill\", \"includes\", \"indexOf\", \"lastIndexOf\", \"at\", \"keys\", \"values\",\n \"entries\", \"has\", \"get\", \"set\", \"add\", \"delete\", \"clear\", \"toString\",\n \"toLocaleString\", \"valueOf\", \"charAt\", \"charCodeAt\", \"codePointAt\", \"split\",\n \"padStart\", \"padEnd\", \"repeat\", \"trim\", \"trimStart\", \"trimEnd\", \"toUpperCase\",\n \"toLowerCase\", \"toFixed\", \"toPrecision\", \"startsWith\", \"endsWith\",\n]);\n\n/** Non-throwing global namespaces called as `X.method(...)`. */\nconst PURE_NAMESPACES = new Set<string>([\n \"Object\", \"Array\", \"Math\", \"JSON\", \"Number\", \"String\", \"Boolean\", \"console\",\n]);\n\n/** Constructors that do not throw on construction. */\nconst PURE_CONSTRUCTORS = new Set<string>([\n \"Map\", \"Set\", \"WeakMap\", \"WeakSet\", \"Date\", \"Error\", \"TypeError\",\n \"RangeError\", \"Array\", \"Object\", \"Headers\", \"URLSearchParams\", \"FormData\",\n \"TextEncoder\", \"TextDecoder\", \"Blob\", \"ReadableStream\", \"WritableStream\",\n \"TransformStream\", \"Response\", \"AbortController\",\n]);\n\nfunction isNode(value: unknown): value is TSESTree.Node {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as { type?: unknown }).type === \"string\"\n );\n}\n\n/** A call whose value is a known pure, non-throwing helper. */\nfunction isPureCall(node: TSESTree.CallExpression): boolean {\n const callee = node.callee;\n if (callee.type !== AST_NODE_TYPES.MemberExpression) {\n return false;\n }\n const property = callee.property;\n if (property.type !== AST_NODE_TYPES.Identifier) {\n return false;\n }\n if (\n callee.object.type === AST_NODE_TYPES.Identifier &&\n PURE_NAMESPACES.has(callee.object.name)\n ) {\n return true;\n }\n return PURE_METHODS.has(property.name);\n}\n\nfunction isPureNew(node: TSESTree.NewExpression): boolean {\n return (\n node.callee.type === AST_NODE_TYPES.Identifier &&\n PURE_CONSTRUCTORS.has(node.callee.name)\n );\n}\n\n/**\n * Walk `stmt`'s same-scope subtree (not descending into nested function/arrow\n * bodies) until `predicate` matches a node.\n */\nfunction subtreeMatches(\n stmt: TSESTree.Node,\n predicate: (node: TSESTree.Node) => boolean,\n): boolean {\n let found = false;\n\n const visit = (current: TSESTree.Node): void => {\n if (found) {\n return;\n }\n if (predicate(current)) {\n found = true;\n return;\n }\n for (const key of Object.keys(current)) {\n if (key === \"parent\") {\n continue;\n }\n if (NESTED_FUNCTION_TYPES.has(current.type) && key === \"body\") {\n continue;\n }\n const value = (current as unknown as Record<string, unknown>)[key];\n if (Array.isArray(value)) {\n for (const child of value) {\n if (isNode(child)) {\n visit(child);\n }\n }\n } else if (isNode(value)) {\n visit(value);\n }\n if (found) {\n return;\n }\n }\n };\n\n visit(stmt);\n return found;\n}\n\nconst hasAwait = (node: TSESTree.Node): boolean =>\n subtreeMatches(node, (n) => n.type === AST_NODE_TYPES.AwaitExpression);\n\nconst hasThrowingCallOrNew = (node: TSESTree.Node): boolean =>\n subtreeMatches(\n node,\n (n) =>\n (n.type === AST_NODE_TYPES.CallExpression && !isPureCall(n)) ||\n (n.type === AST_NODE_TYPES.NewExpression && !isPureNew(n)),\n );\n\n/** Unwrap `await` / optional-chain / non-null wrappers to the core expression. */\nfunction unwrap(expr: TSESTree.Expression): TSESTree.Expression {\n let current = expr;\n while (\n current.type === AST_NODE_TYPES.ChainExpression ||\n current.type === AST_NODE_TYPES.TSNonNullExpression\n ) {\n current = current.expression;\n }\n return current;\n}\n\n/** A bare fire-and-forget call statement — `toast(\"done\");`, `logEvent(...);`. */\nfunction isBareCallStatement(stmt: TSESTree.Statement): boolean {\n return (\n stmt.type === AST_NODE_TYPES.ExpressionStatement &&\n unwrap(stmt.expression).type === AST_NODE_TYPES.CallExpression\n );\n}\n\n/**\n * Whether a top-level try-body statement can plausibly throw when the `try`\n * runs. See the file overview for the guards; the key ones are: `await` always\n * counts, and a bare fire-and-forget call statement (no `await`) does not.\n *\n * Blocks and `if`/`else` branches recurse so the bare-call exemption survives a\n * guard — a fire-and-forget call is no more throwing for sitting inside an\n * `if`. The guard's own TEST is still checked for a throwing call, keeping\n * `if (!validate(x))` counted. A compound statement still contributes at most\n * ONE to the caller's count; this predicate only decides whether it counts.\n */\nfunction canThrow(stmt: TSESTree.Statement): boolean {\n if (hasAwait(stmt)) {\n return true;\n }\n if (isBareCallStatement(stmt)) {\n return false;\n }\n if (stmt.type === AST_NODE_TYPES.BlockStatement) {\n return stmt.body.some(canThrow);\n }\n if (stmt.type === AST_NODE_TYPES.IfStatement) {\n return (\n hasThrowingCallOrNew(stmt.test) ||\n canThrow(stmt.consequent) ||\n (stmt.alternate !== null && canThrow(stmt.alternate))\n );\n }\n return hasThrowingCallOrNew(stmt);\n}\n\n/**\n * Conservative: is the `catch` handler guaranteed to re-throw? True when a\n * handler is present and its body's last statement is a `throw`.\n */\nfunction handlerRethrows(handler: TSESTree.CatchClause | null): boolean {\n if (handler === null) {\n return false;\n }\n const body = handler.body.body;\n const last = body[body.length - 1];\n return last !== undefined && last.type === AST_NODE_TYPES.ThrowStatement;\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-fat-try-blocks\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Disallow `try` blocks with more than three top-level statements that can throw — isolate the throwing statement and move non-throwing work outside.\",\n },\n schema: [],\n messages: {\n fatTryBlock:\n \"This `try` block has {{count}} statements that can throw (max {{max}}). Isolate the throwing statement(s); move non-throwing work outside the `try`.\",\n },\n },\n defaultOptions: [],\n create(context) {\n if (isGeneratedFile(context.filename, context.sourceCode.text)) {\n return {};\n }\n\n const sourceCode = context.sourceCode;\n\n return {\n TryStatement(node: TSESTree.TryStatement): void {\n if (node.finalizer !== null) {\n return;\n }\n if (handlerRethrows(node.handler)) {\n return;\n }\n\n const count = node.block.body.filter(canThrow).length;\n if (count <= MAX_TRY_BODY_STATEMENTS) {\n return;\n }\n\n const tryKeyword = sourceCode.getFirstToken(node);\n context.report({\n node: tryKeyword ?? node,\n messageId: \"fatTryBlock\",\n data: { count, max: MAX_TRY_BODY_STATEMENTS },\n });\n },\n };\n },\n});\n","/**\n * @fileoverview TS port of SARJ012 (`no-secret-in-log`). Passing a secret value\n * (token, password, api key, jwt, credential, signature, ...) to a logging call\n * leaks it into log sinks — files, stdout, log aggregators — where it persists\n * far beyond its intended lifetime and is readable by anyone with log access.\n * Prefer redaction (`tokenPrefix: token.slice(0, 6)`) or omission.\n *\n * We fire on a logging call (`logger.info(...)`, `log.error(...)`, loguru/bind\n * builder chains, etc.) that passes a secret-named value either as a property of\n * an object argument (`logger.error(\"msg\", { token, apiKey })`) or as a bare\n * secret-named positional identifier (`logger.info(\"x\", password)`).\n *\n * Log recognition is shared with the catch rules via `_logging`, so the\n * `logFunctions` option applies here too. That matters: a structured logger is\n * usually a free function with no logger receiver, and `logEvent(\"slack.auth\",\n * { botToken })` was previously never even examined by this rule. Declaring the\n * project's logger closes that hole.\n *\n * The secret-name predicate matches a secret word only as a WHOLE token (after\n * snake_case / camelCase splitting) and disqualifies identifiers whose trailing\n * token is a counter / row-id / flag marker (`tokenCount`, `apiKeyId`,\n * `passwordEnabled`) or whose LEADING word is a boolean predicate (`hasSecret`,\n * `is_token`), so metadata *about* a secret is not mistaken for the secret\n * itself. Both guards live in the shared `_secret_names` predicate. Redaction\n * markers (prefix/mask/hash/redact/tag) are exempt on top of that.\n *\n * ## The raw-blob arm (`noRawBodyInLog`)\n *\n * A second, separately-messaged arm covers what the name-based arm structurally\n * cannot: a whole request/response **blob** — `logEvent(\"ashby.response\",\n * { status, body })`, `console.log(res.body)`. No property of that object is\n * secret-*named*; the object itself is the leak. Bodies are candidate PII and\n * routinely echo credentials back (an auth response containing the token it just\n * minted, a webhook payload carrying its own signing header), and a log sink has\n * no retention policy for either. The advice differs from the secret arm's —\n * \"redact or omit the blob\", not \"this named field is a credential\" — so it gets\n * its own messageId.\n *\n * This arm is deliberately name-driven and narrow. **Only these words, matched as\n * the identifier's TRAILING camel/snake word, count as a raw blob**: `body`,\n * `bodies`, `payload`, `payloads`, `params` (so `rawBody`, `requestBody`,\n * `responsePayload`, `webhookPayload`, `searchParams` all qualify), plus the\n * single whole identifier `formData` (whose camel split ends in the far too\n * generic `data`). Generic containers — `data`, `input`, `args`, `event`,\n * `result`, `record`, `req`, `res` — are NOT blob words: they name everything, so\n * firing on them would make the rule noise and get it switched off.\n *\n * A leak is reported only when the logged VALUE is the blob verbatim: a shorthand\n * property (`{ body }`), a bare identifier (`{ meta: body }`, `logEvent(\"x\",\n * payload)`), or a non-computed member access whose PROPERTY is blob-named\n * (`{ body: res.body }`, `console.log(res.body)`). Judging the value rather than\n * the key is what keeps `{ payload: body.id }` silent.\n *\n * Deliberately NOT reported:\n * - **A narrowed field** — `{ id: body.id }`, `{ bodyLength: body.length }`. The\n * member property, not the object, decides; picking a field is the fix.\n * - **Anything passed through a call** — `redact(body)`, `sanitize(payload)`,\n * `pick(body, [\"id\"])`, `JSON.stringify(body).slice(0, 200)`,\n * `summarizeIssues(body)`. Summarising is the behaviour we want, and no name\n * list can enumerate every project's summariser, so the *shape* is the exemption.\n * - **A string literal or template** — `{ body: \"ok\" }`, `` { body: `n=${n}` } ``.\n * Already a rendered, author-chosen string.\n * - **Redaction / derivation markers in the name** — `redactedBody`,\n * `sanitizedPayload`, `truncatedBody`, `bodyHash`, `bodyPreview`, `safeBody`.\n * - **Boolean flags** — `hasBody`, `isPayload`: a leading predicate word.\n * - **Object spread** — `{ ...body }`. Real, but no observed instances; left out\n * rather than shipped unmeasured.\n * - **Test files** (`_paths.isTestFile`) — a body there is a fixture the author\n * wrote, not production PII. The secret arm is deliberately NOT exempted this\n * way: its behaviour is unchanged by this port.\n *\n * Both arms sit behind the same `_logging` gate, so `logFunctions` /\n * `loggerNames` govern the blob arm identically — `logEvent(\"x\", { body })` is\n * invisible until the project declares `logEvent`, exactly like `{ botToken }`.\n *\n * References:\n * - https://owasp.org/www-community/vulnerabilities/Information_exposure_through_log_files\n */\n\nimport { ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\nimport {\n createLogMatcher,\n LOGGING_OPTION_PROPERTIES,\n type LoggingOptions,\n} from \"./_logging.js\";\nimport { isTestFile } from \"./_paths.js\";\nimport {\n FLAG_PREFIXES,\n INNOCUOUS_WORDS,\n isSecretName,\n leadingWord,\n tokenize,\n} from \"./_secret_names.js\";\n\ntype MessageIds = \"noSecretInLog\" | \"noRawBodyInLog\";\ntype Options = readonly [LoggingOptions?];\n\n/**\n * The shared metadata set plus the log-specific extras. Logging a secret's\n * label, expiry, ARN, URL, scope, or the DI component that holds it is metadata\n * ABOUT the credential, never the credential bytes, so it must not fire here —\n * `prefer-constant-time-secret-compare` uses the narrower shared set.\n */\nconst LOG_INNOCUOUS_WORDS: ReadonlySet<string> = new Set([\n ...INNOCUOUS_WORDS,\n \"name\",\n \"names\",\n \"label\",\n \"labels\",\n \"title\",\n \"expiry\",\n \"expiration\",\n \"expires\",\n \"ttl\",\n \"version\",\n \"versions\",\n \"policy\",\n \"rotation\",\n \"arn\",\n \"path\",\n \"paths\",\n \"issuer\",\n \"audience\",\n \"strength\",\n \"manager\",\n \"service\",\n \"services\",\n \"repository\",\n \"provider\",\n \"providers\",\n \"store\",\n \"factory\",\n \"handler\",\n \"controller\",\n \"bucket\",\n \"url\",\n \"uri\",\n \"endpoint\",\n \"endpoints\",\n \"scope\",\n \"scopes\",\n \"event\",\n \"events\",\n \"format\",\n \"at\",\n \"len\",\n \"length\",\n]);\n\nconst REDACTION_RE = /prefix|suffix|redact|mask|hash|hint|_len|length/i;\nconst WHOLE_TOKEN_REDACTION_MARKERS: ReadonlySet<string> = new Set([\"tag\"]);\n\n/** True if the name names a raw secret and is not a redacted derivative. */\nfunction isSecretKeyword(name: string): boolean {\n if (REDACTION_RE.test(name)) {\n return false;\n }\n if (tokenize(name).some((tok) => WHOLE_TOKEN_REDACTION_MARKERS.has(tok))) {\n return false;\n }\n return isSecretName(name, LOG_INNOCUOUS_WORDS);\n}\n\n/**\n * True if `prop`'s value is the raw secret rather than a redacted/derived form.\n * Shorthand (`{ token }`), a bare identifier (`{ apiKey: theKey }`), or a plain\n * member access (`{ apiKey: config.apiKey }`) all carry the secret verbatim. A\n * call (`token.slice(0, 6)`, `mask(token)`), template literal, ternary, concat,\n * or literal placeholder (`\"***\"`) is already redacted — logging it is safe.\n */\nfunction isRawSecretValue(prop: TSESTree.Property): boolean {\n if (prop.shorthand) {\n return true;\n }\n return prop.value.type === \"Identifier\" || prop.value.type === \"MemberExpression\";\n}\n\n/**\n * Trailing camel/snake words that name an un-redacted request/response blob. Kept\n * to an enumerated five: these are the containers whose *whole point* is \"the\n * bytes the client sent / the server returned\". Generic container words (`data`,\n * `input`, `args`, `event`, `result`, `payloadless` domain objects) are excluded\n * on purpose — see the `@fileoverview`.\n */\nconst RAW_BLOB_WORDS: ReadonlySet<string> = new Set([\n \"body\",\n \"bodies\",\n \"payload\",\n \"payloads\",\n \"params\",\n]);\n\n/**\n * Whole identifiers whose camelCase split ends in a word too generic to enumerate\n * (`formData` -> `data`) but which name a blob unambiguously on their own.\n */\nconst RAW_BLOB_IDENTIFIERS: ReadonlySet<string> = new Set([\"formdata\"]);\n\n/**\n * Substrings that mark a blob name as an already-derived, safe-to-log form:\n * `redactedBody`, `sanitizedPayload`, `truncatedBody`, `bodyPreview`.\n */\nconst BLOB_REDACTION_RE = /redact|sanit|scrub|mask|truncat|anonym|filtered|preview|summar/i;\n\n/**\n * Derivation markers that are only safe when matched as a WHOLE token — `safe` as\n * a substring would wrongly exempt `unsafeBody`.\n */\nconst BLOB_REDACTION_TOKENS: ReadonlySet<string> = new Set([\n \"safe\",\n \"clean\",\n \"shape\",\n \"keys\",\n \"public\",\n]);\n\n/** True if the name names a raw request/response blob and is not a derived form. */\nfunction isRawBlobName(name: string): boolean {\n if (REDACTION_RE.test(name) || BLOB_REDACTION_RE.test(name)) {\n return false;\n }\n const tokens = tokenize(name);\n if (tokens.some((tok) => BLOB_REDACTION_TOKENS.has(tok))) {\n return false;\n }\n // Same leading boolean-predicate words the secret arm uses: `hasBody` answers\n // \"is there one?\", it is not the blob.\n const first = leadingWord(name);\n if (first !== undefined && FLAG_PREFIXES.has(first)) {\n return false;\n }\n if (RAW_BLOB_IDENTIFIERS.has(name.toLowerCase())) {\n return true;\n }\n const last = tokens.at(-1);\n return last !== undefined && RAW_BLOB_WORDS.has(last);\n}\n\n/**\n * The blob name a logged value carries VERBATIM, or null when it does not carry\n * one. A bare identifier is the whole object; a non-computed member access is\n * judged by its PROPERTY, so `res.body` is the blob but `body.id` is a picked\n * field. Every other shape — call, template, literal, ternary, spread — has\n * already been through the author's hands and is left alone.\n */\nfunction rawBlobValueName(value: TSESTree.Node): string | null {\n if (value.type === \"Identifier\") {\n return isRawBlobName(value.name) ? value.name : null;\n }\n if (\n value.type === \"MemberExpression\" &&\n !value.computed &&\n value.property.type === \"Identifier\"\n ) {\n return isRawBlobName(value.property.name) ? value.property.name : null;\n }\n return null;\n}\n\n/** The static string name of an object-property key, or null when not statically named. */\nfunction propertyKeyName(prop: TSESTree.Property): string | null {\n if (prop.computed) {\n return null;\n }\n if (prop.key.type === \"Identifier\") {\n return prop.key.name;\n }\n if (prop.key.type === \"Literal\" && typeof prop.key.value === \"string\") {\n return prop.key.value;\n }\n return null;\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-secret-in-log\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Disallow passing a secret-named value or a raw request/response blob to a logging call; both leak to log sinks. Redact or omit.\",\n },\n schema: [\n {\n type: \"object\",\n additionalProperties: false,\n properties: { ...LOGGING_OPTION_PROPERTIES },\n },\n ],\n messages: {\n noSecretInLog:\n \"Secret `{{name}}` passed to a logging call leaks it to log sinks. Redact (e.g. `{{name}}Prefix: {{name}}.slice(0, 6)`) or omit it.\",\n noRawBodyInLog:\n \"Raw `{{name}}` passed to a logging call. Request/response blobs carry PII and often echo credentials back, and log sinks have no retention policy. Log a derived value instead (a status, `{{name}}.id`, a length, a truncated issue list) or pass it through a redactor (`redact({{name}})`).\",\n },\n },\n defaultOptions: [{}],\n create(context, [loggingOptions]) {\n const matcher = createLogMatcher(loggingOptions);\n // Bodies in a test file are fixtures the author wrote, not production PII.\n const blobArmApplies = !isTestFile(context.filename);\n\n /** Reports a bare positional argument that IS the secret. Did it fire? */\n function reportSecretArgument(arg: TSESTree.Node): boolean {\n const name =\n arg.type === \"Identifier\"\n ? arg.name\n : arg.type === \"MemberExpression\" &&\n !arg.computed &&\n arg.property.type === \"Identifier\"\n ? arg.property.name\n : null;\n if (name === null || !isSecretKeyword(name)) {\n return false;\n }\n context.report({ node: arg, messageId: \"noSecretInLog\", data: { name } });\n return true;\n }\n\n /** Reports a meta-object property whose key names a secret it carries raw. Did it fire? */\n function reportSecretProperty(prop: TSESTree.Property): boolean {\n const keyName = propertyKeyName(prop);\n if (keyName === null || !isSecretKeyword(keyName) || !isRawSecretValue(prop)) {\n return false;\n }\n context.report({ node: prop, messageId: \"noSecretInLog\", data: { name: keyName } });\n return true;\n }\n\n /** Reports `node` when `value` carries an un-redacted request/response blob. */\n function reportRawBlob(node: TSESTree.Node, value: TSESTree.Node): void {\n if (!blobArmApplies) {\n return;\n }\n const name = rawBlobValueName(value);\n if (name !== null) {\n context.report({ node, messageId: \"noRawBodyInLog\", data: { name } });\n }\n }\n\n return {\n CallExpression(node: TSESTree.CallExpression): void {\n if (!matcher.isLoggingCall(node)) {\n return;\n }\n\n for (const arg of node.arguments) {\n if (arg.type === \"ObjectExpression\") {\n for (const prop of arg.properties) {\n if (prop.type !== \"Property\") {\n continue;\n }\n if (!reportSecretProperty(prop)) {\n reportRawBlob(prop, prop.value);\n }\n }\n continue;\n }\n if (!reportSecretArgument(arg)) {\n reportRawBlob(arg, arg);\n }\n }\n },\n };\n },\n});\n","/**\n * @fileoverview Shared predicate for deciding whether an identifier names secret\n * material. TS port of Python's `_secret_names.py`, used by `no-secret-in-log`\n * (SARJ012) and `prefer-constant-time-secret-compare` (SARJ011) so the two rules\n * never diverge on what counts as a secret.\n *\n * A naive implementation matches a secret word as a bare *substring*, which\n * misfires on a large false-positive class observed in real audits:\n *\n * - LLM usage counters that merely embed `token`: `tokenCount`, `promptTokens`,\n * `completionTokens`, `totalTokens`, `maxTokens`, `tokenize`, `tokenizer`,\n * `tokenBudget`.\n * - Row-id / handle names: `apiKeyId`, `*KeyId` — the id of a key row, not the\n * key material.\n * - Boolean feature / presence / state flags: `passwordEnabled`, `tokenPresent`,\n * `passwordSet`, `passwordConfigured` — a boolean answering \"is it there / was\n * it set\", not the credential itself. A `type` discriminator is the same:\n * `tokenType` is `\"Bearer\"`, `credentialType` is a class name.\n * - Innocent words embedding a secret word: `secretary` (embeds `secret`),\n * `keyboardEvent` (embeds `key`).\n *\n * Two rules fix this:\n *\n * 1. Match a secret word only as a WHOLE token (after snake_case / camelCase\n * splitting), never a substring. This alone clears `tokenize`, `tokenizer`,\n * `secretary`, and every *pluralized* `tokens` counter (plural `tokens` is not\n * the singular secret word `token`).\n * 2. Disqualify an identifier whose TRAILING token is a counter / row-id / flag\n * marker (`count`, `budget`, `id`, `enabled`, ...) even when a secret word is\n * also present — this clears `tokenCount`, `apiKeyId`, `passwordEnabled`,\n * while still catching a credential that merely leads with such a word\n * (`validToken`, `presentToken` are secrets, not flags).\n *\n * `isAuthSecretName` narrows further for SARJ011: a *timing-attack* surface is\n * only an authenticator whose bytes gate access, so category discriminators,\n * boolean flags, and integrity-only content hashes are stripped there while\n * `no-secret-in-log` keeps its broader reach.\n */\n\nexport const SECRET_WORDS: ReadonlySet<string> = new Set([\n \"token\",\n \"secret\",\n \"password\",\n \"passwd\",\n \"jwt\",\n \"secrets\",\n \"passwords\",\n \"credential\",\n \"credentials\",\n \"authorization\",\n \"signature\",\n \"hmac\",\n \"digest\",\n \"hash\",\n \"apikey\",\n \"bearer\",\n]);\n\n/**\n * Tokens that mark a counter, row-id, feature flag, or boolean presence/state\n * marker. As the TRAILING token they mean the identifier is metadata *about* a\n * secret, not the secret itself, so it is not a leak / timing surface even when a\n * secret word is also present: `tokenPresent`, `passwordSet`, and\n * `passwordConfigured` are booleans, not credentials. Leading such a word does\n * not disqualify — `validToken` / `presentToken` are credentials.\n */\nexport const INNOCUOUS_WORDS: ReadonlySet<string> = new Set([\n \"count\",\n \"counts\",\n \"budget\",\n \"limit\",\n \"limits\",\n \"id\",\n \"ids\",\n \"enabled\",\n \"disabled\",\n \"flag\",\n \"flags\",\n \"present\",\n \"set\",\n \"unset\",\n \"configured\",\n \"missing\",\n \"required\",\n \"valid\",\n \"invalid\",\n \"exists\",\n \"type\",\n \"types\",\n]);\n\n/**\n * Trailing token that makes the identifier metadata *about* a secret (its\n * category / handle / label), not the credential: `tokenType`, `tokenName`,\n * `sessionId`, `credentialKind`. `type`/`id` are already dropped by the shared\n * innocuous set; `name`/`kind` are added here because logging them can still\n * matter (SARJ012) but they are never a timing surface.\n */\nconst DESCRIPTOR_WORDS: ReadonlySet<string> = new Set([\n \"type\",\n \"types\",\n \"name\",\n \"names\",\n \"id\",\n \"ids\",\n \"kind\",\n \"kinds\",\n]);\n\n/**\n * A `type`/`kind` token anywhere marks an enum/category discriminator, not a\n * credential: `TOKEN_TYPE_SYSTEM`, `credentialType`, `grantKind`.\n */\nconst CATEGORY_WORDS: ReadonlySet<string> = new Set([\"type\", \"types\", \"kind\", \"kinds\"]);\n\n/**\n * A leading boolean-predicate word marks a flag, not the credential itself:\n * `isToken`, `hasSecret`, `has_secret`, `isTokenStrategy`. Consulted by the\n * SHARED `isSecretName`, so every consumer gets it: this is the exact mirror of\n * the trailing innocuous-word check that already lives there — a boolean\n * answering \"does a secret exist?\" leaks nothing whether the marker leads or\n * trails, and word ORDER should not decide whether a name counts as a\n * credential. Matched as a whole leading WORD via `leadingWord`, never as a\n * character prefix, so `hash_secret` (`hash` != `has`), `issuer_token`\n * (`issuer` != `is`) and `canary_token` (`canary` != `can`) keep firing.\n */\nexport const FLAG_PREFIXES: ReadonlySet<string> = new Set([\n \"is\",\n \"has\",\n \"was\",\n \"are\",\n \"can\",\n \"should\",\n]);\n\n/**\n * Words that make an identifier a secret *only* via an integrity/content hash\n * (`contentHash`, `metadataHash`, `rowHash`) rather than an authenticator. A name\n * that ALSO carries one of these keeps firing (`passwordHash`, `tokenHash`,\n * `computedHmac`, `signature`): those gate access, a plain digest of content does\n * not.\n */\nconst AUTH_WORDS: ReadonlySet<string> = new Set([\n \"token\",\n \"secret\",\n \"secrets\",\n \"password\",\n \"passwd\",\n \"passwords\",\n \"jwt\",\n \"credential\",\n \"credentials\",\n \"authorization\",\n \"signature\",\n \"hmac\",\n \"apikey\",\n \"bearer\",\n]);\n\n/**\n * camelCase / PascalCase / ALLCAPS / digit-run splitter, applied to each\n * snake/kebab segment. `APIKey` -> [\"API\", \"Key\"], `authToken` -> [\"auth\", \"Token\"].\n */\nconst CAMEL_RE = /[A-Z]+(?=[A-Z][a-z])|[A-Z]?[a-z]+|[A-Z]+|\\d+/g;\nconst SEGMENT_RE = /[^A-Za-z0-9]+/;\n\n/**\n * Ordered lowercase tokens from snake_case + camelCase decomposition. Also\n * yields each whole snake/kebab segment lowercased, so a pathological mixed-case\n * single word like `ToKeN` (which camel-splitting shreds into `to`/`ke`/`n`)\n * still surfaces its intended `token` form.\n */\nexport function tokenize(identifier: string): string[] {\n const tokens: string[] = [];\n for (const segment of identifier.split(SEGMENT_RE)) {\n if (!segment) {\n continue;\n }\n tokens.push(segment.toLowerCase());\n for (const part of segment.match(CAMEL_RE) ?? []) {\n tokens.push(part.toLowerCase());\n }\n }\n return tokens;\n}\n\n/**\n * The first *word* of an identifier, from camelCase splitting.\n *\n * `tokenize` deliberately emits each whole snake/kebab segment before its camel\n * parts, so `tokens[0]` for the camelCase `hasSecret` is the useless `hassecret`\n * rather than `has`. Reading `tokens[0]` directly — as the Python original's\n * local check did — therefore never matched a camelCase flag at all, and Python's\n * shared predicate had no leading check whatsoever; both were false positives,\n * fixed on both sides. The leading-word check has to split the first segment\n * explicitly or every `isToken` / `hasSecret` flag is mistaken for a credential.\n */\nexport function leadingWord(identifier: string): string | undefined {\n for (const segment of identifier.split(SEGMENT_RE)) {\n if (segment) {\n return (segment.match(CAMEL_RE) ?? [segment])[0]?.toLowerCase();\n }\n }\n return undefined;\n}\n\n/** True if an `api` token is immediately followed by a `key` token (`api_key`). */\nexport function hasApiKey(tokens: readonly string[]): boolean {\n for (let i = 0; i + 1 < tokens.length; i++) {\n if (tokens[i] === \"api\" && tokens[i + 1] === \"key\") {\n return true;\n }\n }\n return false;\n}\n\n/**\n * True if `identifier` names raw secret material (a credential, not metadata).\n * `innocuous` defaults to the shared metadata set; callers that need a wider\n * exemption list (e.g. `no-secret-in-log`) pass their own superset.\n *\n * Two symmetric metadata guards run before the secret-word scan: a TRAILING\n * innocuous word (`tokenCount`, `apiKeyId`) and a LEADING boolean-predicate word\n * (`hasSecret`, `is_token`). Both describe a name that is *about* a credential\n * rather than being one.\n */\nexport function isSecretName(\n identifier: string,\n innocuous: ReadonlySet<string> = INNOCUOUS_WORDS,\n): boolean {\n const tokens = tokenize(identifier);\n const last = tokens.at(-1);\n if (last !== undefined && innocuous.has(last)) {\n return false;\n }\n const first = leadingWord(identifier);\n if (first !== undefined && FLAG_PREFIXES.has(first)) {\n return false;\n }\n if (tokens.some((tok) => SECRET_WORDS.has(tok))) {\n return true;\n }\n return hasApiKey(tokens);\n}\n\n/**\n * True if `identifier` names an *authenticator* — an access-gating secret whose\n * bytes an attacker could recover by timing a byte-wise comparison.\n *\n * Narrows `isSecretName` for SARJ011: strips category/handle descriptors and\n * `type`/`kind` discriminators, neither of which is a timing-attack surface even\n * though logging them may still matter. Leading boolean-predicate flags are no\n * longer stripped here — `isSecretName` now rejects them for every consumer, so\n * repeating the check would be dead code.\n */\nexport function isAuthSecretName(identifier: string): boolean {\n if (!isSecretName(identifier)) {\n return false;\n }\n const tokens = tokenize(identifier);\n const last = tokens.at(-1);\n if (last !== undefined && DESCRIPTOR_WORDS.has(last)) {\n return false;\n }\n if (tokens.some((tok) => CATEGORY_WORDS.has(tok))) {\n return false;\n }\n if (tokens.some((tok) => AUTH_WORDS.has(tok))) {\n return true;\n }\n return hasApiKey(tokens);\n}\n","import { ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\nimport { AST_NODE_TYPES } from \"@typescript-eslint/utils\";\n\nimport { isGeneratedFile } from \"./_paths.js\";\n\ntype MessageIds = \"unsafeMockCast\";\n\nfunction isMockTypeReference(node: TSESTree.TypeNode): boolean {\n if (node.type !== AST_NODE_TYPES.TSTypeReference) {\n return false;\n }\n \n const typeName = node.typeName;\n \n if (typeName.type === AST_NODE_TYPES.Identifier) {\n const name = typeName.name;\n return name === \"Mock\" || name === \"MockInstance\" || name === \"SpyInstance\";\n }\n \n if (typeName.type === AST_NODE_TYPES.TSQualifiedName) {\n const rightName = typeName.right.name;\n return rightName === \"Mock\" || rightName === \"MockInstance\" || rightName === \"SpyInstance\";\n }\n \n return false;\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<[], MessageIds>({\n name: \"no-unsafe-mock-casting\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Disallow casting to mock types like `jest.Mock` or `vi.Mock`. Use `vi.mocked()` or `jest.mocked()` instead.\",\n },\n schema: [],\n messages: {\n unsafeMockCast:\n \"Do not cast to a Mock type. Use `vi.mocked(fn)` or `jest.mocked(fn)` instead to preserve type safety.\",\n },\n },\n defaultOptions: [],\n create(context) {\n if (isGeneratedFile(context.filename, context.sourceCode.text)) {\n return {};\n }\n\n function checkAssertion(\n node: TSESTree.TSAsExpression | TSESTree.TSTypeAssertion,\n ): void {\n if (isMockTypeReference(node.typeAnnotation)) {\n context.report({ node, messageId: \"unsafeMockCast\" });\n }\n }\n\n return {\n TSAsExpression: checkAssertion,\n TSTypeAssertion: checkAssertion,\n };\n },\n});\n","/**\n * @fileoverview Flag raw `string` used where a closed enumeration is clearly\n * intended, and comparison clusters against a fixed set of string literals.\n * The prescribed replacement is a string-literal union type\n * (`type Status = \"active\" | \"inactive\"`) — NOT a `StrEnum`/`enum`, since the\n * companion `no-enum` rule bans TypeScript enums.\n *\n * This is the TypeScript analog of the Python rule SARJ006 (prefer-str-enum).\n * It fires on two shapes:\n *\n * 1. **Choice-like field** — a `TSPropertySignature` (interface / type literal)\n * or class `PropertyDefinition` whose key's last word is one of the\n * high-precision CHOICE tokens (`status`, `state`, `kind`, `role`,\n * `priority`, `severity`, `direction`, `tier`, `stage`, `type`, `mode`,\n * `level`) and whose type annotation is the bare `string` keyword. Because\n * open-set API DTO fields (`status: string` from an untyped backend) are the\n * dominant false positive, a bare field fires ONLY when CORROBORATED by a\n * sibling string-literal-union member in the SAME interface / class / object\n * type. A file-wide comparison cluster on the field's name is deliberately\n * NOT used to corroborate: it flags unrelated same-named fields (DB-row casts\n * like `as Array<{ status: string }>`, passthrough DTOs). The closed-set fact\n * is still surfaced — as a `comparisonCluster` diagnostic at the comparison\n * site, which is the actionable location.\n *\n * 2. **Comparison cluster** — within one function scope, the same identifier or\n * member expression compared (`===` / `!==` / `==` / `!=`, or a `switch`)\n * against 2+ distinct short lowercase string literals (each matching\n * `^[a-z][a-z0-9_-]{0,30}$`). One diagnostic per cluster. This shape is\n * type-aware: it fires ONLY when the compared operand's resolved type is the\n * general `string` type. An operand already typed as a string-literal union\n * (`CallStatus = \"a\" | \"b\"`, a discriminated-union tag) is the target state\n * and is suppressed — a syntactic rule can't see through a named/imported\n * union, so without type information this shape is inert (the choice-field\n * shape still runs).\n *\n * A cluster inside the very function that PRODUCES the union is suppressed\n * too: when the enclosing function's declared return type is a string-literal\n * union containing every compared literal, the `string` parameter is the\n * narrowing boundary and MUST stay `string` —\n * `function parse(s: string): Status { return s === \"a\" || s === \"b\" ? s : null }`\n * is the fix, not the violation.\n *\n * `Literal`-union types (`type X = \"a\" | \"b\"`) are the target state and never\n * fire. Generated files (`*.gen.ts`, `**\\/generated/**`, `*.d.ts`, or a\n * `@generated` marker) opt out. Vendor-owned OPEN sets that merely look closed\n * (a Resend `bounceType`, a Slack `event.subtype` — dozens of values, more added\n * over time) can be listed in the `ignoreFields` option; narrowing to a union we\n * don't control would be wrong, not better.\n */\n\nimport {\n ESLintUtils,\n type TSESTree,\n type ParserServicesWithTypeInformation,\n AST_NODE_TYPES,\n} from \"@typescript-eslint/utils\";\nimport * as ts from \"typescript\";\n\ntype MessageIds = \"bareChoiceField\" | \"comparisonCluster\";\ntype Options = readonly [\n {\n ignoreFields?: readonly string[];\n }?,\n];\n\ntype FunctionNode =\n | TSESTree.FunctionDeclaration\n | TSESTree.FunctionExpression\n | TSESTree.ArrowFunctionExpression;\n\nconst CHOICE_TOKENS: ReadonlySet<string> = new Set([\n \"status\",\n \"state\",\n \"kind\",\n \"role\",\n \"priority\",\n \"severity\",\n \"direction\",\n \"tier\",\n \"stage\",\n \"type\",\n \"mode\",\n \"level\",\n]);\n\nconst LOWER_TOKEN_RE = /^[a-z][a-z0-9_-]{0,30}$/;\nconst MIN_CLUSTER_SIZE = 2;\n\nconst BOOLEANISH: ReadonlySet<string> = new Set([\"true\", \"false\"]);\n\n/**\n * An enum-shaped token worth a string-literal union: a short lowercase word of\n * 2+ chars that isn't a boolean-string. Single characters (`'a'`), file\n * paths/URLs/i18n keys (contain `/`, `:`, `.`), and `'true'`/`'false'` are NOT\n * closed-enum members — comparing against them is a flag/path/boolean guard.\n */\nfunction isEnumToken(lit: string): boolean {\n return LOWER_TOKEN_RE.test(lit) && lit.length >= 2 && !BOOLEANISH.has(lit);\n}\n\nconst IGNORE_PATTERNS: readonly RegExp[] = [\n /[\\\\/]generated[\\\\/]/,\n /\\.gen\\.tsx?$/,\n /\\.generated\\.tsx?$/,\n /\\.d\\.ts$/,\n];\n\nfunction isIgnoredFile(filename: string, sourceText: string): boolean {\n if (IGNORE_PATTERNS.some((re) => re.test(filename))) {\n return true;\n }\n return /@generated\\b/.test(sourceText.slice(0, 1024));\n}\n\n/**\n * The trailing word of a camelCase / snake_case identifier, lowercased.\n * `callStatus` -> `status`, `user_role` -> `role`, `estate` -> `estate`.\n */\nfunction lastWord(name: string): string {\n const words = name\n .replace(/([a-z0-9])([A-Z])/g, \"$1 $2\")\n .split(/[_\\s]+/)\n .filter((w) => w.length > 0);\n const last = words[words.length - 1] ?? name;\n return last.toLowerCase();\n}\n\nfunction isChoiceLikeName(name: string): boolean {\n return CHOICE_TOKENS.has(lastWord(name));\n}\n\nfunction keyName(\n key: TSESTree.PropertyDefinition[\"key\"] | TSESTree.PropertyName,\n): string | null {\n if (key.type === AST_NODE_TYPES.Identifier) {\n return key.name;\n }\n if (key.type === AST_NODE_TYPES.Literal && typeof key.value === \"string\") {\n return key.value;\n }\n return null;\n}\n\nfunction isStringLiteralMember(t: TSESTree.TypeNode): boolean {\n return (\n t.type === AST_NODE_TYPES.TSLiteralType &&\n t.literal.type === AST_NODE_TYPES.Literal &&\n typeof t.literal.value === \"string\"\n );\n}\n\n/** Whether a type node is a union of 2+ string-literal types. */\nfunction isStringLiteralUnion(node: TSESTree.TypeNode | undefined): boolean {\n if (node?.type !== AST_NODE_TYPES.TSUnionType) {\n return false;\n }\n return node.types.filter(isStringLiteralMember).length >= MIN_CLUSTER_SIZE;\n}\n\n/**\n * Whether a resolved TS type includes the general `string` type — as opposed to\n * being (a union of) string-literal types. `CallStatus = \"a\" | \"b\"` has no\n * general-string member and is already the target state; a raw `string` (or\n * `string | undefined`) does, and a comparison cluster on it should become a\n * union. Requires type information; the comparison-cluster path is inert\n * without it.\n */\nfunction typeHasRawString(type: ts.Type): boolean {\n const parts = type.isUnion() ? type.types : [type];\n return parts.some((t) => (t.flags & ts.TypeFlags.String) !== 0);\n}\n\nfunction isExternalSourceFile(sf: ts.SourceFile | undefined): boolean {\n if (sf === undefined) {\n return false;\n }\n return sf.isDeclarationFile || sf.fileName.includes(\"/node_modules/\");\n}\n\nfunction symbolIsExternallyDeclared(sym: ts.Symbol | undefined): boolean {\n return (\n sym?.declarations?.some((d) => isExternalSourceFile(d.getSourceFile())) ??\n false\n );\n}\n\n/**\n * The iterable expression a destructuring binding element draws its value from,\n * e.g. `Object.entries(x)` for `key` in `for (const [key] of Object.entries(x))`\n * or the RHS of `const { a } = rhs`.\n */\nfunction bindingSourceExpression(\n decl: ts.BindingElement,\n): ts.Expression | undefined {\n let node: ts.Node = decl.parent;\n while (\n !ts.isForOfStatement(node) &&\n !(ts.isVariableDeclaration(node) && node.initializer !== undefined)\n ) {\n if (node.parent === undefined) {\n return undefined;\n }\n node = node.parent;\n }\n return ts.isForOfStatement(node) ? node.expression : node.initializer;\n}\n\n/** A stable key for a plain identifier or non-computed member chain, else null. */\nfunction refKey(node: TSESTree.Node): string | null {\n if (node.type === AST_NODE_TYPES.Identifier) {\n return node.name;\n }\n if (node.type === AST_NODE_TYPES.MemberExpression && !node.computed) {\n const inner = refKey(node.object);\n if (inner === null || node.property.type !== AST_NODE_TYPES.Identifier) {\n return null;\n }\n return `${inner}.${node.property.name}`;\n }\n return null;\n}\n\nfunction strLiteral(node: TSESTree.Node): string | null {\n if (node.type === AST_NODE_TYPES.Literal && typeof node.value === \"string\") {\n return node.value;\n }\n return null;\n}\n\ninterface ClusterEntry {\n node: TSESTree.Node;\n literals: Set<string>;\n allTokens: boolean;\n}\n\ninterface Scope {\n clusters: Map<string, ClusterEntry>;\n /** The function this scope belongs to; null at file scope. */\n fn: FunctionNode | null;\n}\n\ninterface CollectedProperty {\n name: string;\n container: TSESTree.Node;\n node: TSESTree.Node;\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"prefer-string-literal-union\",\n meta: {\n type: \"suggestion\",\n docs: {\n description:\n \"Flag raw `string` choice fields and string-literal comparison clusters; prefer a string-literal union type.\",\n },\n schema: [\n {\n type: \"object\",\n additionalProperties: false,\n properties: {\n ignoreFields: {\n type: \"array\",\n items: { type: \"string\" },\n },\n },\n },\n ],\n messages: {\n bareChoiceField:\n '`{{name}}: string` looks like a choice field — prefer a string-literal union type (e.g. `type X = \"a\" | \"b\"`). Enums are banned by `no-enum`; use a union.',\n comparisonCluster:\n '`{{key}}` is compared against a closed set of string literals — define a string-literal union type (e.g. `type X = \"a\" | \"b\"`).',\n },\n },\n defaultOptions: [{}],\n create(context, [optionsArg]) {\n const filename = context.filename;\n const sourceText = context.sourceCode.getText();\n if (isIgnoredFile(filename, sourceText)) {\n return {};\n }\n\n const ignoredFields = new Set(\n (optionsArg?.ignoreFields ?? []).map((name) => name.toLowerCase()),\n );\n\n let services: ParserServicesWithTypeInformation | null;\n try {\n services = ESLintUtils.getParserServices(context);\n } catch {\n services = null;\n }\n\n const scopeStack: Scope[] = [];\n const validClusters: TSESTree.Node[] = [];\n const bareChoiceProps: CollectedProperty[] = [];\n const containersWithUnion = new Set<TSESTree.Node>();\n\n function operandIsRawString(node: TSESTree.Node): boolean {\n if (services === null) {\n return false;\n }\n return typeHasRawString(services.getTypeAtLocation(node));\n }\n\n /**\n * Whether the compared value provably originates from a type we don't own —\n * a property of a `.d.ts` / node_modules type (DOM `getComputedStyle().overflowY`,\n * `Task.status` from another package), the return of such a method\n * (`URLSearchParams.get()`, `str.toLowerCase()`), or a destructuring of one\n * (`Object.entries()` keys). A string-literal union is not an available fix\n * for these, so the cluster shape must not flag them.\n */\n function originIsExternal(node: ts.Node | undefined, depth: number): boolean {\n if (node === undefined || services === null || depth > 6) {\n return false;\n }\n const checker = services.program.getTypeChecker();\n if (\n ts.isParenthesizedExpression(node) ||\n ts.isNonNullExpression(node) ||\n ts.isAsExpression(node)\n ) {\n return originIsExternal(node.expression, depth + 1);\n }\n if (ts.isPropertyAccessExpression(node)) {\n return symbolIsExternallyDeclared(checker.getSymbolAtLocation(node.name));\n }\n if (ts.isCallExpression(node)) {\n return originIsExternal(node.expression, depth + 1);\n }\n if (ts.isIdentifier(node)) {\n const decl = checker.getSymbolAtLocation(node)?.valueDeclaration;\n if (decl === undefined) {\n return false;\n }\n if (ts.isVariableDeclaration(decl) && decl.initializer !== undefined) {\n return originIsExternal(decl.initializer, depth + 1);\n }\n if (ts.isBindingElement(decl)) {\n return originIsExternal(bindingSourceExpression(decl), depth + 1);\n }\n }\n return false;\n }\n\n function operandIsFlaggable(node: TSESTree.Node): boolean {\n return (\n operandIsRawString(node) &&\n !originIsExternal(services?.esTreeNodeToTSNodeMap.get(node), 0)\n );\n }\n\n /**\n * The string-literal members of a function's DECLARED return type, resolved\n * through named aliases and `Promise<...>`. Null when the function has no\n * annotation, type information is unavailable, or the type is not a\n * string-literal union. Computed lazily — only for a function that actually\n * accumulated a cluster — since it costs a type resolution.\n */\n function declaredReturnLiterals(fn: FunctionNode): ReadonlySet<string> | null {\n const annotation = fn.returnType?.typeAnnotation;\n if (annotation === undefined || services === null) {\n return null;\n }\n const tsNode = services.esTreeNodeToTSNodeMap.get(annotation);\n if (!ts.isTypeNode(tsNode)) {\n return null;\n }\n const checker = services.program.getTypeChecker();\n const declared = checker.getTypeFromTypeNode(tsNode);\n const type = checker.getAwaitedType(declared) ?? declared;\n const literals = new Set<string>();\n for (const part of type.isUnion() ? type.types : [type]) {\n if (part.isStringLiteral()) {\n literals.add(part.value);\n }\n }\n return literals.size >= MIN_CLUSTER_SIZE ? literals : null;\n }\n\n function pushScope(node: FunctionNode): void {\n scopeStack.push({ clusters: new Map(), fn: node });\n }\n\n function popScope(): void {\n const scope = scopeStack.pop();\n if (scope === undefined) {\n return;\n }\n const candidates = [...scope.clusters.values()].filter(\n (entry) => entry.allTokens && entry.literals.size >= MIN_CLUSTER_SIZE,\n );\n if (candidates.length === 0) {\n return;\n }\n const returnLiterals =\n scope.fn === null ? null : declaredReturnLiterals(scope.fn);\n for (const entry of candidates) {\n // The enclosing function declares the very union being compared against:\n // this is the narrowing boundary that produces it, so the `string`\n // operand is required, not a defect.\n if (\n returnLiterals !== null &&\n [...entry.literals].every((lit) => returnLiterals.has(lit))\n ) {\n continue;\n }\n validClusters.push(entry.node);\n }\n }\n\n function accumulate(\n key: string,\n literals: string[],\n node: TSESTree.Node,\n ): void {\n const scope = scopeStack[scopeStack.length - 1];\n if (scope === undefined) {\n return;\n }\n const allTokens = literals.every((lit) => isEnumToken(lit));\n const existing = scope.clusters.get(key);\n if (existing === undefined) {\n scope.clusters.set(key, {\n node,\n literals: new Set(literals),\n allTokens,\n });\n return;\n }\n for (const lit of literals) {\n existing.literals.add(lit);\n }\n existing.allTokens = existing.allTokens && allTokens;\n }\n\n function collectProperty(\n key: TSESTree.PropertyDefinition[\"key\"] | TSESTree.PropertyName,\n typeNode: TSESTree.TypeNode | undefined,\n container: TSESTree.Node,\n node: TSESTree.Node,\n ): void {\n if (isStringLiteralUnion(typeNode)) {\n containersWithUnion.add(container);\n return;\n }\n if (typeNode?.type !== AST_NODE_TYPES.TSStringKeyword) {\n return;\n }\n const name = keyName(key);\n if (\n name === null ||\n !isChoiceLikeName(name) ||\n ignoredFields.has(name.toLowerCase())\n ) {\n return;\n }\n bareChoiceProps.push({ name, container, node });\n }\n\n return {\n FunctionDeclaration: pushScope,\n \"FunctionDeclaration:exit\": popScope,\n FunctionExpression: pushScope,\n \"FunctionExpression:exit\": popScope,\n ArrowFunctionExpression: pushScope,\n \"ArrowFunctionExpression:exit\": popScope,\n\n BinaryExpression(node: TSESTree.BinaryExpression): void {\n if (\n node.operator !== \"===\" &&\n node.operator !== \"!==\" &&\n node.operator !== \"==\" &&\n node.operator !== \"!=\"\n ) {\n return;\n }\n const leftKey = refKey(node.left);\n const rightLit = strLiteral(node.right);\n const rightKey = refKey(node.right);\n const leftLit = strLiteral(node.left);\n if (leftKey !== null && rightLit !== null) {\n if (operandIsFlaggable(node.left)) {\n accumulate(leftKey, [rightLit], node);\n }\n } else if (rightKey !== null && leftLit !== null) {\n if (operandIsFlaggable(node.right)) {\n accumulate(rightKey, [leftLit], node);\n }\n }\n },\n\n SwitchStatement(node: TSESTree.SwitchStatement): void {\n const key = refKey(node.discriminant);\n if (key === null || !operandIsFlaggable(node.discriminant)) {\n return;\n }\n const literals: string[] = [];\n for (const c of node.cases) {\n if (c.test !== null) {\n const lit = strLiteral(c.test);\n if (lit !== null) {\n literals.push(lit);\n }\n }\n }\n if (literals.length > 0) {\n accumulate(key, literals, node);\n }\n },\n\n TSPropertySignature(node: TSESTree.TSPropertySignature): void {\n collectProperty(\n node.key,\n node.typeAnnotation?.typeAnnotation,\n node.parent,\n node,\n );\n },\n\n PropertyDefinition(node: TSESTree.PropertyDefinition): void {\n collectProperty(\n node.key,\n node.typeAnnotation?.typeAnnotation,\n node.parent,\n node,\n );\n },\n\n \"Program:exit\"(): void {\n for (const clusterNode of validClusters) {\n context.report({\n node: clusterNode,\n messageId: \"comparisonCluster\",\n data: { key: refKeyText(clusterNode) },\n });\n }\n for (const prop of bareChoiceProps) {\n if (containersWithUnion.has(prop.container)) {\n context.report({\n node: prop.node,\n messageId: \"bareChoiceField\",\n data: { name: prop.name },\n });\n }\n }\n },\n };\n\n function refKeyText(node: TSESTree.Node): string {\n if (node.type === AST_NODE_TYPES.BinaryExpression) {\n return refKey(node.left) ?? refKey(node.right) ?? \"value\";\n }\n if (node.type === AST_NODE_TYPES.SwitchStatement) {\n return refKey(node.discriminant) ?? \"value\";\n }\n return \"value\";\n }\n },\n});\n","/**\n * @fileoverview Prefer `z.enum([\"a\", \"b\"])` over a union of Zod string\n * literals. The enum form is shorter, exposes the permitted values directly,\n * and infers the same string-literal union.\n *\n * The fix is deliberately limited to direct, non-empty arrays containing only\n * `z.literal(<string>)` calls. Spreads, identifiers, non-string literals, and\n * commented arrays are reported without an automatic rewrite because moving or\n * discarding their syntax would not be mechanical.\n */\n\nimport {\n AST_NODE_TYPES,\n ESLintUtils,\n type TSESLint,\n type TSESTree,\n} from \"@typescript-eslint/utils\";\n\ntype MessageIds = \"preferEnum\";\ntype Options = readonly [];\n\nfunction isZodModule(source: string): boolean {\n return /(^|[/@-])zod([/-]|$)/.test(source);\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/tree/main/packages/typescript#${name}`,\n)<Options, MessageIds>({\n name: \"prefer-zod-enum\",\n meta: {\n type: \"suggestion\",\n docs: {\n description:\n \"Prefer z.enum([...]) over z.union([z.literal(...), ...]) for string choices\",\n },\n fixable: \"code\",\n schema: [],\n messages: {\n preferEnum:\n \"Use `z.enum([...])` instead of a union of string-literal schemas.\",\n },\n },\n defaultOptions: [],\n create(context) {\n const sourceCode = context.sourceCode;\n const zodNamespaces = new Set<string>();\n\n function enumValues(\n node: TSESTree.CallExpression,\n ): readonly TSESTree.StringLiteral[] | null {\n const callee = node.callee;\n if (\n callee.type !== AST_NODE_TYPES.MemberExpression ||\n callee.computed ||\n callee.object.type !== AST_NODE_TYPES.Identifier ||\n !zodNamespaces.has(callee.object.name) ||\n callee.property.type !== AST_NODE_TYPES.Identifier ||\n callee.property.name !== \"union\" ||\n node.arguments.length !== 1\n ) {\n return null;\n }\n const argument = node.arguments[0];\n if (\n argument === undefined ||\n argument.type !== AST_NODE_TYPES.ArrayExpression ||\n argument.elements.length === 0\n ) {\n return null;\n }\n\n const values: TSESTree.StringLiteral[] = [];\n for (const element of argument.elements) {\n if (\n element === null ||\n element.type !== AST_NODE_TYPES.CallExpression ||\n element.arguments.length !== 1 ||\n element.callee.type !== AST_NODE_TYPES.MemberExpression ||\n element.callee.computed ||\n element.callee.object.type !== AST_NODE_TYPES.Identifier ||\n !zodNamespaces.has(element.callee.object.name) ||\n element.callee.property.type !== AST_NODE_TYPES.Identifier ||\n element.callee.property.name !== \"literal\"\n ) {\n return null;\n }\n const value = element.arguments[0];\n if (\n value === undefined ||\n value.type !== AST_NODE_TYPES.Literal ||\n typeof value.value !== \"string\"\n ) {\n return null;\n }\n values.push(value);\n }\n return values;\n }\n\n function buildFix(\n node: TSESTree.CallExpression,\n values: readonly TSESTree.StringLiteral[],\n ): TSESLint.ReportFixFunction | undefined {\n const argument = node.arguments[0];\n if (\n argument === undefined ||\n argument.type !== AST_NODE_TYPES.ArrayExpression ||\n sourceCode.getCommentsInside(argument).length > 0\n ) {\n return undefined;\n }\n const callee = node.callee;\n if (\n callee.type !== AST_NODE_TYPES.MemberExpression ||\n callee.property.type !== AST_NODE_TYPES.Identifier\n ) {\n return undefined;\n }\n return (fixer) => [\n fixer.replaceText(callee.property, \"enum\"),\n fixer.replaceText(\n argument,\n `[${values.map((value) => sourceCode.getText(value)).join(\", \")}]`,\n ),\n ];\n }\n\n return {\n ImportDeclaration(node): void {\n if (!isZodModule(node.source.value)) {\n return;\n }\n for (const specifier of node.specifiers) {\n if (\n specifier.type === AST_NODE_TYPES.ImportNamespaceSpecifier ||\n specifier.type === AST_NODE_TYPES.ImportDefaultSpecifier ||\n (specifier.type === AST_NODE_TYPES.ImportSpecifier &&\n specifier.imported.type === AST_NODE_TYPES.Identifier &&\n specifier.imported.name === \"z\")\n ) {\n zodNamespaces.add(specifier.local.name);\n }\n }\n },\n CallExpression(node): void {\n const values = enumValues(node);\n if (values === null) {\n return;\n }\n const fix = buildFix(node, values);\n context.report({\n node,\n messageId: \"preferEnum\",\n ...(fix === undefined ? {} : { fix }),\n });\n },\n };\n },\n});\n","/**\n * @fileoverview TS port of SARJ025 (`no-offset-pagination`). `LIMIT n OFFSET m`\n * makes the database scan and discard every one of the `m` skipped rows before\n * returning the page, so page N costs O(N): deep pages get linearly slower until\n * they blow the request budget. Worse, under concurrent inserts the offset window\n * shifts between requests, so a row can be returned twice or skipped entirely —\n * a paginated backfill silently misses records. Keyset / cursor pagination\n * (`WHERE id > ? ORDER BY id LIMIT n`) is O(page) and stable.\n *\n * The rule reads statically-resolvable SQL strings (literals, template literals,\n * `+` concatenations, `[...].join(\" \")` fragment arrays), neutralizes string\n * values and `--` / comment bodies first, and flags an `OFFSET` keyword\n * immediately followed by a value/param token — `?` or `?1` (SQLite/D1), `:name`,\n * `@name`, `$1`, or a bare digit. A `${...}` substitution has already become `?`\n * by then, so an interpolated offset is caught too.\n *\n * Requiring the value token is what keeps false positives at zero: it excludes\n * the English word (\"offset out of range\", \"no base offset\"), an `'offset'`\n * string value or object key, and array-index constructs like BigQuery's\n * `UNNEST(...) WITH OFFSET AS col`, none of which put a value after the keyword.\n *\n * // flagged\n * `SELECT id, status FROM runs ORDER BY created_at LIMIT ? OFFSET ?`\n *\n * // preferred\n * `SELECT id, status FROM runs WHERE id > ? ORDER BY id LIMIT ?`\n *\n * Test files are out of scope — a bounded fixture query is not a production\n * pagination path.\n *\n * CONVERGED WITH SARJ025 AND SARJ107 (2026-07). The concept is implemented three\n * times — here, in `packages/python/src/sarj_python_lint/rules/\n * no_offset_pagination.py` (SARJ025) and in\n * `packages/sql/src/sarj_sql_lint/rules/no_limit_offset.py` (SARJ107). This rule\n * and SARJ025 both required the value token and both documented why; SARJ107 was\n * a bare `\\bOFFSET\\b` and fired on `ALTER TABLE t ADD COLUMN offset INTEGER`,\n * and SARJ025's parameter set omitted `?`, so the `LIMIT ? OFFSET ?` spelling\n * this file uses as its own headline example was a silent false negative in\n * Python. Both are fixed, and all three now share ONE parameter alternation —\n * the union of the markers each dialect uses (`%s`, `%(name)s`, `?`, `?1`,\n * `:name`, `@name`, `$1`, digit) — so a marker added for one language cannot go\n * missing in another. `%s` is inert in TypeScript today; it is present so the\n * three patterns are literally identical and a diff between them is a bug.\n * Corpus delta here: 0 findings before and after over 748 first-party TS/TSX\n * files (bulbul `typescript/` + `sdks/typescript`, noura-fe).\n */\n\nimport { ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\nimport { isTestFile } from \"./_paths.js\";\nimport { createSqlListener } from \"./_sql.js\";\n\ntype MessageIds = \"noOffsetPagination\";\ntype Options = readonly [];\n\n/**\n * `OFFSET` followed by a value/param token — the real pagination construct.\n * The parameter alternatives are the UNION of every marker the three packages\n * see, and are kept identical in SARJ025 and SARJ107 — see the header.\n */\nconst OFFSET_PAGINATION = /\\bOFFSET\\s+(?:%s|%\\(\\w+\\)s|\\?\\d*|:\\w+|@\\w+|\\$\\d+|\\d+)/i;\n\n/** Cheap substring gate; noise-stripping can only ever remove keywords, never add them. */\nconst OFFSET_GATE = /offset/i;\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-offset-pagination\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Disallow OFFSET pagination in embedded SQL; it is O(N) per page and drops or repeats rows under concurrent writes. Use a keyset cursor.\",\n },\n schema: [],\n messages: {\n noOffsetPagination:\n \"OFFSET pagination scans and discards every skipped row (O(N) per page) and shifts under concurrent inserts, so rows get repeated or missed. Use a keyset cursor: `WHERE id > ? ORDER BY id LIMIT ?`.\",\n },\n },\n defaultOptions: [],\n create(context) {\n if (isTestFile(context.filename) || !OFFSET_GATE.test(context.sourceCode.text)) {\n return {};\n }\n return createSqlListener((sql: string, node: TSESTree.Node): void => {\n if (!OFFSET_PAGINATION.test(sql)) {\n return;\n }\n context.report({ node, messageId: \"noOffsetPagination\" });\n });\n },\n});\n","/**\n * @fileoverview Shared SQL-literal helpers for the SQL-aware store-lint rules.\n * TS port of Python's `rules/_sql.py`, adapted to where SQL actually lives in a\n * TypeScript / Cloudflare Workers codebase: D1 `db.prepare(`...`)` template\n * literals, `sql` tagged templates, `+`-concatenated fragments, and\n * `[...].join(\" \")` arrays of one-line fragments.\n *\n * These rules scan raw SQL for keywords (`INSERT`, `ON CONFLICT`, `OFFSET`,\n * `*`, ...). Scanning the raw text conflates SQL *code* with SQL string-literal\n * *values*: `WHERE p = 'on conflict'` holds no upsert clause, a `--` inside a\n * quoted value is not a comment, and `'*'` inside a value is not a projection\n * star. `stripSqlNoise` neutralizes both classes of noise before any keyword\n * scan — that single step is what keeps the false-positive rate of these rules\n * near zero.\n *\n * Template-literal substitutions (`${cols}`, `${values}`) are replaced with the\n * SQLite/D1 parameter marker `?` rather than dropped, so `LIMIT ? OFFSET ${n}`\n * still reads as a parameterised pagination clause and an interpolated\n * `VALUES ${rows}` still reads as an insert write.\n */\n\nimport { AST_NODE_TYPES, type TSESLint, type TSESTree } from \"@typescript-eslint/utils\";\n\n/**\n * Blank out SQL string-literal contents and comment bodies.\n *\n * A single left-to-right scan, so precedence between strings and comments is\n * correct: a `--` or quote inside a string literal is protected (masked as\n * string data, never read as a comment), and a quote inside a comment is\n * ignored. Every masked character becomes a space except newlines, which are\n * preserved so the text keeps its shape. Doubled quotes (`''` / `\"\"`) are SQL's\n * in-string escape and keep the scanner inside the literal.\n */\nexport function stripSqlNoise(text: string): string {\n const out = [...text];\n const n = text.length;\n let i = 0;\n while (i < n) {\n const ch = text[i];\n if (ch === \"'\" || ch === '\"') {\n out[i] = \" \";\n i += 1;\n while (i < n) {\n const c = text[i];\n if (c === ch) {\n if (i + 1 < n && text[i + 1] === ch) {\n out[i] = \" \";\n out[i + 1] = \" \";\n i += 2;\n continue;\n }\n out[i] = \" \";\n i += 1;\n break;\n }\n if (c !== \"\\n\") {\n out[i] = \" \";\n }\n i += 1;\n }\n continue;\n }\n if (ch === \"-\" && text[i + 1] === \"-\") {\n while (i < n && text[i] !== \"\\n\") {\n out[i] = \" \";\n i += 1;\n }\n continue;\n }\n if (ch === \"/\" && text[i + 1] === \"*\") {\n out[i] = \" \";\n out[i + 1] = \" \";\n i += 2;\n while (i < n && !(text[i] === \"*\" && text[i + 1] === \"/\")) {\n if (text[i] !== \"\\n\") {\n out[i] = \" \";\n }\n i += 1;\n }\n if (i < n) {\n out[i] = \" \";\n out[i + 1] = \" \";\n i += 2;\n }\n continue;\n }\n i += 1;\n }\n return out.join(\"\");\n}\n\n/** The parameter marker a `${...}` substitution is replaced with before scanning. */\nconst SUBSTITUTION_MARKER = \"?\";\n\n/**\n * Reconstruct the SQL text of a string-bearing node, or null when the node does\n * not statically resolve to one. Handles the four shapes real TS SQL takes:\n * a plain string literal, a template literal (substitutions become `?`), a\n * `+`-concatenation of those, and an array of fragments destined for `.join()`.\n */\nexport function sqlTextOf(node: TSESTree.Node): string | null {\n switch (node.type) {\n case AST_NODE_TYPES.Literal:\n return typeof node.value === \"string\" ? node.value : null;\n case AST_NODE_TYPES.TemplateLiteral:\n return node.quasis.map((q) => q.value.cooked ?? q.value.raw).join(SUBSTITUTION_MARKER);\n case AST_NODE_TYPES.TaggedTemplateExpression:\n return sqlTextOf(node.quasi);\n case AST_NODE_TYPES.BinaryExpression: {\n if (node.operator !== \"+\") {\n return null;\n }\n const left = sqlTextOf(node.left);\n const right = sqlTextOf(node.right);\n return left !== null && right !== null ? left + right : null;\n }\n case AST_NODE_TYPES.ArrayExpression: {\n const parts: string[] = [];\n for (const element of node.elements) {\n if (element === null) {\n return null;\n }\n const part = sqlTextOf(element);\n if (part === null) {\n return null;\n }\n parts.push(part);\n }\n return parts.length > 0 ? parts.join(\" \") : null;\n }\n default:\n return null;\n }\n}\n\n/**\n * True when `node` is an array whose fragments are glued together by `.join(...)`\n * — the `['INSERT INTO t (...)', 'VALUES (?)', 'ON CONFLICT ...'].join(' ')`\n * shape. Only then may the elements be read as one statement; an unrelated array\n * of strings is not SQL.\n */\nfunction isJoinedFragmentArray(node: TSESTree.ArrayExpression): boolean {\n const parent = node.parent;\n return (\n parent?.type === AST_NODE_TYPES.MemberExpression &&\n parent.object === node &&\n !parent.computed &&\n parent.property.type === AST_NODE_TYPES.Identifier &&\n parent.property.name === \"join\" &&\n parent.parent?.type === AST_NODE_TYPES.CallExpression\n );\n}\n\n/** Every string-bearing descendant that a composite node has already absorbed. */\nfunction markConsumed(node: TSESTree.Node, consumed: WeakSet<TSESTree.Node>): void {\n consumed.add(node);\n for (const key of Object.keys(node)) {\n if (key === \"parent\") {\n continue;\n }\n const value = (node as unknown as Record<string, unknown>)[key];\n for (const child of Array.isArray(value) ? value : [value]) {\n if (child !== null && typeof child === \"object\" && \"type\" in child) {\n markConsumed(child as TSESTree.Node, consumed);\n }\n }\n }\n}\n\n/**\n * Build the ESLint visitor that hands each *whole* SQL statement to `handler`\n * exactly once, along with the node to report on.\n *\n * ESLint traverses parents before children, so a composite node (a\n * `+`-concatenation or a joined fragment array) is seen first and marks its\n * string descendants consumed — the fragments are never re-reported on their own.\n */\nexport function createSqlListener(\n handler: (sql: string, node: TSESTree.Node) => void,\n): TSESLint.RuleListener {\n const consumed = new WeakSet<TSESTree.Node>();\n\n const visit = (node: TSESTree.Node): void => {\n if (consumed.has(node)) {\n return;\n }\n const text = sqlTextOf(node);\n if (text === null) {\n return;\n }\n markConsumed(node, consumed);\n handler(stripSqlNoise(text), node);\n };\n\n return {\n BinaryExpression: (node: TSESTree.BinaryExpression): void => {\n visit(node);\n },\n ArrayExpression: (node: TSESTree.ArrayExpression): void => {\n if (isJoinedFragmentArray(node)) {\n visit(node);\n }\n },\n TemplateLiteral: (node: TSESTree.TemplateLiteral): void => {\n visit(node);\n },\n Literal: (node: TSESTree.Literal): void => {\n visit(node);\n },\n };\n}\n","/**\n * @fileoverview TS port of SARJ026 (`prefer-namedtuple-over-tuple-return`). A\n * multi-field value returned across a module boundary should be an object with\n * named fields, not a positional tuple the caller has to unpack by position.\n *\n * `export async function fetchDoc(): Promise<[string, Headers, string | null]>`\n * forces every call site to remember which slot is which, and the names live\n * only in the destructuring at the call site — so two call sites can and do\n * disagree. Returning `{ body, headers, contentType }` names each field once, at\n * the definition, and a wrong-field access becomes a type error instead of a\n * silently wrong value.\n *\n * Fires on an *exported* function (or exported class method) whose declared\n * return type is a tuple of two or more elements — directly or wrapped in\n * `Promise<...>`. Exported means either the inline keyword (`export function\n * split`) or a detached specifier for the same module-scope binding (`export {\n * split }`, `export { split as s }`, `export default split`, `export = split`);\n * a re-export from another module and a type-only export do not count.\n *\n * The permitted tuple uses are deliberately NOT flagged, matching the Python\n * rule's tuning plus the TS-specific idioms:\n *\n * - **Homogeneous tuples** — `[number, number]`, `[Date, Date]`: a pair of the\n * same thing (a range, a coordinate), not distinct fields.\n * - **Variadic tuples** — `[string, ...number[]]`: an immutable sequence, not a\n * fixed record.\n * - **Labeled tuple members** — `[status: number, body: string]`: TypeScript\n * surfaces those names at the call site, which is the whole point of the rule.\n * - **Discriminated tags** — `[\"ok\", T]` with a literal first element: the tuple\n * IS the discriminated union.\n * - **React hooks** — any `use*` function. `[value, setValue]` is the\n * established contract of the entire hooks ecosystem; renaming at the call\n * site is the intended affordance, not a hazard.\n * - **Accessor/mutator pairs** — a 2-tuple with a function type in either slot\n * (`[T, (next: T) => void]`, `[() => void, Promise<void>]`). A function slot\n * is a capability handle, not a data field. That is the\n * same `[value, setValue]` contract without the `use` prefix, and every\n * reactive library outside React ships it: measured on 2,186 real TypeScript\n * files (zod / TanStack Query / react-router / swr / zustand), 2 of the 5 hits\n * were this shape —\n * query/packages/svelte-query/src/containers.svelte.ts:31 returning\n * `[T, (newValue: T) => void]` and\n * query/packages/query-persist-client-core/src/persist.ts:162 returning\n * `[() => void, Promise<void>]` (unsubscribe handle + completion). Neither has\n * \"fields\" to name; boxing them in an object would fight the ecosystem.\n * - Single-element tuples, unannotated returns, and non-exported helpers, whose\n * two or three call sites live in the same file as the definition.\n */\n\nimport { AST_NODE_TYPES, ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\ntype MessageIds = \"noPositionalTupleReturn\";\ntype Options = readonly [];\n\nconst MIN_ELEMENTS = 2;\n\n/** The pair length an accessor/mutator tuple has. */\nconst ACCESSOR_PAIR_LENGTH = 2;\n\n/** Type wrappers whose single type argument is the value actually returned. */\nconst AWAITABLE_TYPES: ReadonlySet<string> = new Set([\"Promise\", \"PromiseLike\", \"Awaited\"]);\n\n/** The tuple type a return annotation resolves to, unwrapping `Promise<...>`. */\nfunction tupleReturnType(node: TSESTree.TypeNode): TSESTree.TSTupleType | null {\n if (node.type === AST_NODE_TYPES.TSTupleType) {\n return node;\n }\n if (\n node.type === AST_NODE_TYPES.TSTypeReference &&\n node.typeName.type === AST_NODE_TYPES.Identifier &&\n AWAITABLE_TYPES.has(node.typeName.name)\n ) {\n const argument = node.typeArguments?.params[0];\n return argument === undefined ? null : tupleReturnType(argument);\n }\n return null;\n}\n\n/** Source text with all whitespace runs collapsed, for structural comparison. */\nfunction normalizedText(sourceCode: { getText: (node: TSESTree.Node) => string }, node: TSESTree.Node): string {\n return sourceCode.getText(node).replaceAll(/\\s+/g, \" \").trim();\n}\n\n/**\n * True when the tuple is one of the permitted shapes: variadic, labeled, tagged\n * by a leading literal type, or structurally homogeneous.\n */\nfunction isPermittedTuple(\n tuple: TSESTree.TSTupleType,\n sourceCode: { getText: (node: TSESTree.Node) => string },\n): boolean {\n const elements = tuple.elementTypes;\n if (elements.length < MIN_ELEMENTS) {\n return true;\n }\n if (elements.some((element) => element.type === AST_NODE_TYPES.TSRestType)) {\n return true;\n }\n if (elements.some((element) => element.type === AST_NODE_TYPES.TSNamedTupleMember)) {\n return true;\n }\n if (elements[0]?.type === AST_NODE_TYPES.TSLiteralType) {\n return true;\n }\n if (\n elements.length === ACCESSOR_PAIR_LENGTH &&\n elements.some((element) => element.type === AST_NODE_TYPES.TSFunctionType)\n ) {\n return true;\n }\n const texts = new Set(elements.map((element) => normalizedText(sourceCode, element)));\n return texts.size === 1;\n}\n\n/** The declared name of a function-ish node, or null for an anonymous one. */\nfunction functionName(node: TSESTree.Node): string | null {\n if (node.type === AST_NODE_TYPES.FunctionDeclaration) {\n return node.id?.name ?? null;\n }\n const parent = node.parent;\n if (parent?.type === AST_NODE_TYPES.VariableDeclarator && parent.id.type === AST_NODE_TYPES.Identifier) {\n return parent.id.name;\n }\n if (\n (parent?.type === AST_NODE_TYPES.MethodDefinition ||\n parent?.type === AST_NODE_TYPES.PropertyDefinition ||\n parent?.type === AST_NODE_TYPES.Property) &&\n parent.key.type === AST_NODE_TYPES.Identifier\n ) {\n return parent.key.name;\n }\n return null;\n}\n\n/**\n * True when an ancestor statement carries the `export` keyword inline —\n * `export function f`, `export const f =`, `export default function f`.\n */\nfunction isInlineExported(node: TSESTree.Node): boolean {\n for (let current: TSESTree.Node | undefined | null = node; current != null; current = current.parent) {\n const parent = current.parent;\n if (\n parent?.type === AST_NODE_TYPES.ExportNamedDeclaration ||\n parent?.type === AST_NODE_TYPES.ExportDefaultDeclaration\n ) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * The module-scope binding the function lives under — `split` for a top-level\n * `function split`, `Repo` for a method of a top-level `class Repo`. That is the\n * name an `export { … }` specifier elsewhere in the file would refer to. Null\n * when the function is not anchored to a named top-level declaration.\n */\nfunction moduleScopeBindingName(node: TSESTree.Node): string | null {\n let current: TSESTree.Node = node;\n let child: TSESTree.Node = node;\n while (current.parent != null && current.parent.type !== AST_NODE_TYPES.Program) {\n child = current;\n current = current.parent;\n }\n if (current.parent?.type !== AST_NODE_TYPES.Program) {\n return null;\n }\n if (\n current.type === AST_NODE_TYPES.FunctionDeclaration ||\n current.type === AST_NODE_TYPES.ClassDeclaration\n ) {\n return current.id?.name ?? null;\n }\n if (current.type === AST_NODE_TYPES.VariableDeclaration) {\n if (child.type !== AST_NODE_TYPES.VariableDeclarator || child.id.type !== AST_NODE_TYPES.Identifier) {\n return null;\n }\n return child.id.name;\n }\n return null;\n}\n\n/**\n * Local names this module puts on its public surface through a *detached* export\n * statement: `export { split }`, `export { split as s }`, `export default split`,\n * `export = split`. A re-export (`export { x } from \"./other\"`) names a binding\n * owned by another module, and a type-only export (`export type { x }`) does not\n * expose the value, so neither counts.\n */\nfunction specifierExportedNames(program: TSESTree.Program): ReadonlySet<string> {\n const names = new Set<string>();\n for (const statement of program.body) {\n if (\n statement.type === AST_NODE_TYPES.ExportNamedDeclaration &&\n statement.declaration == null &&\n statement.source == null &&\n statement.exportKind !== \"type\"\n ) {\n for (const specifier of statement.specifiers) {\n if (specifier.exportKind !== \"type\" && specifier.local.type === AST_NODE_TYPES.Identifier) {\n names.add(specifier.local.name);\n }\n }\n continue;\n }\n if (\n statement.type === AST_NODE_TYPES.ExportDefaultDeclaration &&\n statement.declaration.type === AST_NODE_TYPES.Identifier\n ) {\n names.add(statement.declaration.name);\n continue;\n }\n if (\n statement.type === AST_NODE_TYPES.TSExportAssignment &&\n statement.expression.type === AST_NODE_TYPES.Identifier\n ) {\n names.add(statement.expression.name);\n }\n }\n return names;\n}\n\n/**\n * True when the function is reachable from outside the module — the boundary the\n * rule is about. Either the declaration is exported inline, or its module-scope\n * binding is exported later through an export specifier.\n */\nfunction isExported(node: TSESTree.Node, specifierExports: ReadonlySet<string>): boolean {\n if (isInlineExported(node)) {\n return true;\n }\n if (specifierExports.size === 0) {\n return false;\n }\n const binding = moduleScopeBindingName(node);\n return binding !== null && specifierExports.has(binding);\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-positional-tuple-return\",\n meta: {\n type: \"suggestion\",\n docs: {\n description:\n \"Disallow returning a positional tuple of distinct fields from an exported function; return a named object so call sites cannot mismatch slots.\",\n },\n schema: [],\n messages: {\n noPositionalTupleReturn:\n \"Exported `{{name}}` returns a positional {{count}}-tuple, so every call site re-invents the field names and can disagree. Return a named object (or label the tuple members) instead.\",\n },\n },\n defaultOptions: [],\n create(context) {\n const specifierExports = specifierExportedNames(context.sourceCode.ast);\n const check = (\n node:\n | TSESTree.FunctionDeclaration\n | TSESTree.FunctionExpression\n | TSESTree.ArrowFunctionExpression,\n ): void => {\n const annotation = node.returnType?.typeAnnotation;\n if (annotation === undefined) {\n return;\n }\n const tuple = tupleReturnType(annotation);\n if (tuple === null || isPermittedTuple(tuple, context.sourceCode)) {\n return;\n }\n const name = functionName(node);\n if (name === null || name.startsWith(\"_\") || /^use[A-Z]/.test(name)) {\n return;\n }\n if (!isExported(node, specifierExports)) {\n return;\n }\n context.report({\n node: tuple,\n messageId: \"noPositionalTupleReturn\",\n data: { name, count: String(tuple.elementTypes.length) },\n });\n };\n return {\n FunctionDeclaration: check,\n FunctionExpression: check,\n ArrowFunctionExpression: check,\n };\n },\n});\n","/**\n * @fileoverview TS port of SARJ024 (`no-repeated-string-literal`). The same long,\n * *structured* string repeated across two or more functions of a module is a real\n * maintenance hazard: when one copy is edited the others silently drift. A column\n * list that gains a column in the read query but not in the upsert, or a prompt\n * template updated in one branch only, fails at runtime and nowhere else.\n *\n * COUNT THRESHOLD — CONVERGED WITH SARJ024 (2026-07). This rule and the Python\n * twin had drifted to two different definitions of \"repeated\": TS demanded three\n * total occurrences, Python only two distinct enclosing functions. A literal used\n * exactly twice therefore fired in `.py` and was clean in `.ts`, which is a\n * defect — the same code judged differently by file extension. Both were measured\n * and TS moved to Python's rule; **\"two distinct functions\" is the only count\n * threshold.** Evidence:\n * - Requiring three occurrences would drop 15 of the 18 findings over the\n * first-party Python corpora (bulbul `python/` + `sdks/python`, noura-be) and\n * the django/fastapi/celery FP controls, and the dropped ones are true\n * positives of exactly the shape this rule exists for:\n * `noura-be/.../modules/onboarding/store.py:631` repeats one\n * `SELECT stage FROM onboarding_profiles WHERE onboarding_token = %s FOR\n * UPDATE` verbatim between `submit_financial_info_atomic` and\n * `submit_legal_info_atomic`, and `.../modules/card/store.py:287` repeats a\n * 10-column `SELECT … JOIN card_types …` between two methods. Two copies is\n * where column-list drift *begins*; the third was arbitrary.\n * - Dropping the gate costs the TS side nothing. Over 748 first-party TS/TSX\n * files (bulbul `typescript/` + `sdks/typescript`, noura-fe) the rule yields\n * 0 findings at BOTH thresholds. Over the 2,186-file third-party corpus\n * (zod / TanStack Query / react-router / swr / zustand) it yields 0 at three\n * and 5 at two — and all 5 were react-router `*-test.ts` fixtures that the\n * test-file exemption should already have covered but did not, because\n * `_paths.isTestFile` only knew the `.test.ts` spelling. That gap is fixed at\n * its source, so the corpus delta of this change is 0 → 0 everywhere.\n * Precision here is carried by the *structured* filter below, not by counting.\n *\n * The rule is deliberately narrow — it fires only where cross-site drift is a\n * genuine bug, never on coincidentally-equal prose. Three filters combine:\n *\n * 1. **Structured only.** A literal qualifies only if it carries structural\n * signal that makes coincidental equality near-impossible: it contains a\n * newline (multi-line SQL / prompt templates), it matches an *uppercase* SQL\n * keyword (`SELECT`, `FROM`, `ON CONFLICT`, ...) — matched case-sensitively so\n * English prose does not trip it, only real SQL does — or it is a bare\n * snake_case / dotted identifier, i.e. a constraint / index / column-list name\n * reused across statements. Plain user-facing messages and log lines carry\n * none of these, so two same-text-different-intent messages are never coupled\n * into one shared constant.\n * 2. **Cross-function only.** Occurrences must span at least two distinct\n * enclosing functions. Two uses inside one function are edited together and\n * hoisting them buys no drift protection — that is pure locality loss. This\n * is the only count threshold; see the note above.\n * 3. **Exclusions.** Template literals *with* substitutions (the f-string\n * analogue — each fragment is half a sentence, not a reusable value), import\n * and `require` sources, JSX attribute values (a repeated Tailwind class list\n * is `no-duplicate-class-names` territory, not a drift bug), and test files,\n * where fixtures legitimately repeat literal payloads.\n *\n * A substitution-free template literal IS included: in TypeScript that is just a\n * multi-line string, and it is exactly where embedded SQL lives — but only when\n * it is UNTAGGED (see below).\n *\n * Every occurrence after the first is reported, so a deliberate duplicate can be\n * disabled on its own line.\n *\n * CORPUS SWEEP (2220 files across zod / TanStack Query / react-router / swr /\n * zustand, 2026-07): 96 raw hits, 100% of them the quasi of a TAGGED template\n * and every one a false positive. Two families:\n * - 94 in react-router's Playwright suites, where `js`…`` is the SOURCE TEXT of\n * a scratch app the test writes to disk\n * (`react-router/integration/single-fetch-test.ts:1482` repeats a 3-line\n * `app/routes/target.tsx` fixture). Hoisting a shared constant would move the\n * fixture away from the assertion that explains it — and the fixtures are\n * MEANT to be near-identical; that is what makes the assertions comparable.\n * - 2 in `query/packages/query-devtools/src/Devtools.tsx:398`, where ``css`…` ``\n * is a goober style block whose \"duplicate\" is a style rule, i.e. exactly the\n * styling-string category `isScaffolding` already excludes for JSX attributes.\n * A tagged template is an INVOCATION, not a string value: the tag decides what\n * the text means, so \"these two strings must not drift\" is not a claim the rule\n * can make about it. Tagged templates are therefore excluded. An untagged\n * multi-line SQL/prompt literal — the shape the rule exists for — still fires.\n */\n\nimport { AST_NODE_TYPES, ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\nimport { isTestFile } from \"./_paths.js\";\n\ntype MessageIds = \"noRepeatedStringLiteral\";\ntype Options = readonly [];\n\nconst MIN_LENGTH = 40;\nconst MIN_DISTINCT_SCOPES = 2;\nconst PREVIEW_LENGTH = 40;\n\n/** Case-sensitive on purpose: uppercase keywords mean SQL, lowercase means prose. */\nconst SQL_KEYWORD_RE =\n /\\b(SELECT|INSERT|UPDATE|DELETE|FROM|WHERE|JOIN|VALUES|ON CONFLICT|RETURNING|GROUP BY|ORDER BY)\\b/;\nconst IDENTIFIER_RE = /^[a-z_][a-z0-9_.]*$/;\n\nconst FUNCTION_TYPES: ReadonlySet<AST_NODE_TYPES> = new Set([\n AST_NODE_TYPES.FunctionDeclaration,\n AST_NODE_TYPES.FunctionExpression,\n AST_NODE_TYPES.ArrowFunctionExpression,\n]);\n\n/** True when the literal carries structure that rules out coincidental equality. */\nfunction isStructured(value: string): boolean {\n return value.includes(\"\\n\") || SQL_KEYWORD_RE.test(value) || IDENTIFIER_RE.test(value);\n}\n\nfunction preview(value: string): string {\n const oneLine = value.replaceAll(\"\\n\", \" \").trim();\n return oneLine.length <= PREVIEW_LENGTH ? oneLine : `${oneLine.slice(0, PREVIEW_LENGTH)}...`;\n}\n\n/** The enclosing function node, or null when the literal sits at module/class scope. */\nfunction enclosingFunction(node: TSESTree.Node): TSESTree.Node | null {\n for (let current = node.parent; current != null; current = current.parent) {\n if (FUNCTION_TYPES.has(current.type)) {\n return current;\n }\n }\n return null;\n}\n\n/**\n * True when the literal is scaffolding rather than a reusable value: an\n * import/`require` source (repeating a module path is the point) or a JSX\n * attribute value (styling strings, handled by the styling rules).\n */\nfunction isScaffolding(node: TSESTree.Node): boolean {\n const parent = node.parent;\n if (parent === undefined) {\n return true;\n }\n return (\n parent.type === AST_NODE_TYPES.ImportDeclaration ||\n parent.type === AST_NODE_TYPES.ExportNamedDeclaration ||\n parent.type === AST_NODE_TYPES.ExportAllDeclaration ||\n parent.type === AST_NODE_TYPES.TSImportType ||\n parent.type === AST_NODE_TYPES.JSXAttribute ||\n parent.type === AST_NODE_TYPES.TSLiteralType\n );\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-repeated-string-literal\",\n meta: {\n type: \"suggestion\",\n docs: {\n description:\n \"Disallow a long structured string literal repeated across functions; the copies drift when one is edited. Extract a module-level constant.\",\n },\n schema: [],\n messages: {\n noRepeatedStringLiteral:\n 'Structured string literal \"{{preview}}\" is repeated across functions (first use on line {{line}}) — extract a module-level constant so the copies cannot drift.',\n },\n },\n defaultOptions: [],\n create(context) {\n if (isTestFile(context.filename)) {\n return {};\n }\n const occurrences = new Map<string, TSESTree.Node[]>();\n const scopes = new WeakMap<TSESTree.Node, TSESTree.Node | null>();\n\n const record = (value: string, node: TSESTree.Node): void => {\n if (value.length < MIN_LENGTH || !isStructured(value) || isScaffolding(node)) {\n return;\n }\n const existing = occurrences.get(value);\n if (existing === undefined) {\n occurrences.set(value, [node]);\n } else {\n existing.push(node);\n }\n scopes.set(node, enclosingFunction(node));\n };\n\n return {\n Literal(node: TSESTree.Literal): void {\n if (typeof node.value === \"string\") {\n record(node.value, node);\n }\n },\n TemplateLiteral(node: TSESTree.TemplateLiteral): void {\n // A tagged template (`js`…``, `css`…``, `sql`…``) is a call, not a\n // string value — the tag, not this rule, decides what the text means.\n if (node.parent.type === AST_NODE_TYPES.TaggedTemplateExpression) {\n return;\n }\n const [only] = node.quasis;\n if (node.expressions.length === 0 && only !== undefined) {\n record(only.value.cooked ?? only.value.raw, node);\n }\n },\n \"Program:exit\": (): void => {\n for (const [value, nodes] of occurrences) {\n const distinctScopes = new Set(\n nodes.map((node) => scopes.get(node)).filter((scope) => scope != null),\n );\n if (distinctScopes.size < MIN_DISTINCT_SCOPES) {\n continue;\n }\n const [first, ...repeats] = nodes;\n if (first === undefined) {\n continue;\n }\n for (const node of repeats) {\n context.report({\n node,\n messageId: \"noRepeatedStringLiteral\",\n data: { preview: preview(value), line: String(first.loc.start.line) },\n });\n }\n }\n },\n };\n },\n});\n","/**\n * @fileoverview TS port of SARJ021 (`no-select-star`). `SELECT *` over-fetches\n * every column (including large JSON/text blobs the caller never reads) and,\n * more importantly, makes the row shape implicit: the moment a column is added,\n * renamed, or reordered, the zod schema that parses the row starts failing — or\n * worse, silently accepts extra keys — and nothing in the query says which\n * columns the code actually depends on. Naming the columns makes the dependency\n * explicit and pins the row contract at the query.\n *\n * The rule reads statically-resolvable SQL strings (literals, template literals,\n * `+` concatenations, `[...].join(\" \")` fragment arrays), neutralizes string\n * values and `--` / comment bodies first (so a `'*'` value is never mistaken for\n * a star), and flags a query — a string with a real `SELECT ... FROM` shape —\n * whose projection list holds a `*` in any position: bare (`SELECT *`,\n * `SELECT id, *`) or qualified (`c.*`, `main.runs.*`).\n *\n * Deliberately NOT flagged, matching the Python rule's tuning:\n * - `COUNT(*)` and other aggregate stars — the star is a function argument, not\n * a projection.\n * - `a * b` arithmetic — an operand follows the star.\n * - `EXISTS (SELECT * ...)` — the projection is unused by definition.\n * - Prose that merely contains the words \"select\" and \"from\".\n * - Test files, where a `SELECT *` assertion over a fixture table is fine.\n */\n\nimport { ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\nimport { isTestFile } from \"./_paths.js\";\nimport { createSqlListener } from \"./_sql.js\";\n\ntype MessageIds = \"noSelectStar\";\ntype Options = readonly [];\n\n/** A real SQL query shape, so prose containing the bare word \"from\" isn't matched. */\nconst QUERY_SHAPE = /\\bSELECT\\b[\\s\\S]*?\\bFROM\\b/i;\nconst SELECT_KEYWORD = /\\bSELECT\\b/gi;\nconst FROM_KEYWORD = /^FROM\\b/i;\nconst EXISTS_BEFORE = /\\bEXISTS\\s*\\(\\s*$/i;\n/** A `word.` immediately preceding a `*` marks a qualified star (`c.*`, `main.runs.*`). */\nconst QUALIFIED_PREFIX = /\\w\\.$/;\n\n/** Cheap substring gate; a file with no star and no SELECT can never produce a finding. */\nconst SELECT_GATE = /select/i;\n\n/**\n * True when the `*` at `pos` is a column-projection star.\n *\n * A projection star expands columns: bare (`SELECT *`, `id, *`) or qualified\n * (`c.*`). It is NOT a `COUNT(*)` argument (parenthesised on both sides) nor an\n * `a * b` multiply (an operand follows rather than a terminator).\n */\nfunction isProjectionStar(sql: string, pos: number): boolean {\n if (QUALIFIED_PREFIX.test(sql.slice(0, pos))) {\n return true;\n }\n let before = pos - 1;\n while (before >= 0 && /\\s/.test(sql[before] ?? \"\")) {\n before -= 1;\n }\n let after = pos + 1;\n while (after < sql.length && /\\s/.test(sql[after] ?? \"\")) {\n after += 1;\n }\n const beforeChar = before >= 0 ? (sql[before] ?? \"\") : \"\";\n const afterChar = after < sql.length ? (sql[after] ?? \"\") : \"\";\n const terminates =\n afterChar === \"\" || afterChar === \",\" || afterChar === \")\" || FROM_KEYWORD.test(sql.slice(after));\n if (!terminates) {\n return false;\n }\n return !(beforeChar === \"(\" && afterChar === \")\");\n}\n\n/** True when the query projects a star that is not inside an `EXISTS (...)` subquery. */\nfunction hasRealSelectStar(sql: string): boolean {\n const selects = [...sql.matchAll(SELECT_KEYWORD)].map((m) => m.index);\n for (let pos = 0; pos < sql.length; pos++) {\n if (sql[pos] !== \"*\" || !isProjectionStar(sql, pos)) {\n continue;\n }\n const owning = selects.filter((start) => start < pos).at(-1);\n if (owning !== undefined && !EXISTS_BEFORE.test(sql.slice(0, owning))) {\n return true;\n }\n }\n return false;\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-select-star\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Disallow SELECT * in embedded SQL; it over-fetches and leaves the row contract implicit, so a schema change breaks row parsing silently.\",\n },\n schema: [],\n messages: {\n noSelectStar:\n \"`SELECT *` over-fetches and leaves the row shape implicit — a new or reordered column silently changes what this query returns. List the columns explicitly.\",\n },\n },\n defaultOptions: [],\n create(context) {\n if (isTestFile(context.filename) || !SELECT_GATE.test(context.sourceCode.text)) {\n return {};\n }\n return createSqlListener((sql: string, node: TSESTree.Node): void => {\n if (!QUERY_SHAPE.test(sql) || !hasRealSelectStar(sql)) {\n return;\n }\n context.report({ node, messageId: \"noSelectStar\" });\n });\n },\n});\n","/**\n * @fileoverview TS port of SARJ031 (`no-sleep-in-test-body`). A real timed sleep\n * placed directly in a test body — `await new Promise((r) => setTimeout(r, 50))`\n * or `await sleep(200)` — is the canonical flaky-test pattern. The delay is a\n * guess about how long some other work takes: too short and the test fails on a\n * loaded CI runner, too long and every run pays for it. Either way the test is\n * asserting on wall-clock time rather than on the signal it actually cares about.\n *\n * The fix is to synchronize on the signal: await the promise the code returns,\n * await the flushed microtask queue, or drive time deterministically with\n * `vi.useFakeTimers()` + `await vi.advanceTimersByTimeAsync(ms)`, which makes the\n * elapsed time exact and the test instant.\n *\n * Fires only on the exact shape:\n *\n * - `new Promise(...)` whose executor body is a `setTimeout(resolve, <n>)`, or a\n * call to a bare `sleep`/`delay`/`wait`/`pause` helper, where\n * - the delay is a **nonzero numeric literal** — `setTimeout(r, 0)` is a\n * macrotask yield used to flush the event loop, not a timing guess, and a\n * non-literal `sleep(configuredDelay)` is a deliberate parameterised wait, and\n * - the **nearest enclosing function is the callback of an `it`/`test`** (any\n * `.only`/`.skip`/`.each` variant) or of a `beforeEach`/`afterEach` hook.\n *\n * The nearest-enclosing-function gate is the critical false-positive guard, and\n * it is ported deliberately: a sleep inside a nested helper or fake declared\n * within the test (`const slowFetch = async () => { await sleep(50); ... }`) is\n * *simulating* latency in order to exercise a timeout or cancellation path. That\n * is the intended use of a delay in a test, not a flaky synchronization, and it\n * must not fire. Because the check keys off the nearest enclosing function, such\n * a helper is excluded automatically. The `new Promise` executor arrow is not\n * treated as an enclosing function — it is part of the sleep idiom itself.\n *\n * Applies only in test files.\n */\n\nimport { AST_NODE_TYPES, ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\nimport { isTestFile } from \"./_paths.js\";\n\ntype MessageIds = \"noSleepInTestBody\";\ntype Options = readonly [];\n\n/** Free-function sleep helpers. A `sleep(50)` reads as a sleep whatever its module. */\nconst SLEEP_HELPERS: ReadonlySet<string> = new Set([\"sleep\", \"delay\", \"wait\", \"pause\"]);\n\n/** Test-case and per-test-hook callers whose callback body is \"the test body\". */\nconst TEST_CALLERS: ReadonlySet<string> = new Set([\n \"it\",\n \"test\",\n \"beforeEach\",\n \"afterEach\",\n]);\n\nconst FUNCTION_TYPES: ReadonlySet<AST_NODE_TYPES> = new Set([\n AST_NODE_TYPES.FunctionDeclaration,\n AST_NODE_TYPES.FunctionExpression,\n AST_NODE_TYPES.ArrowFunctionExpression,\n]);\n\n/** True for a nonzero numeric literal — the timing guess, as opposed to a `0` yield. */\nfunction isNonzeroNumericLiteral(node: TSESTree.Node | undefined): boolean {\n return node?.type === AST_NODE_TYPES.Literal && typeof node.value === \"number\" && node.value !== 0;\n}\n\n/** True when `node` is `setTimeout(<anything>, <nonzero literal>)`. */\nfunction isTimedSetTimeout(node: TSESTree.Node): boolean {\n return (\n node.type === AST_NODE_TYPES.CallExpression &&\n node.callee.type === AST_NODE_TYPES.Identifier &&\n node.callee.name === \"setTimeout\" &&\n node.arguments.length >= 2 &&\n isNonzeroNumericLiteral(node.arguments[1])\n );\n}\n\n/**\n * True when `node` is `new Promise((r) => setTimeout(r, n))` — including the\n * block-bodied `{ setTimeout(r, n); }` spelling.\n */\nfunction isPromiseSleep(node: TSESTree.NewExpression): boolean {\n if (node.callee.type !== AST_NODE_TYPES.Identifier || node.callee.name !== \"Promise\") {\n return false;\n }\n const executor = node.arguments[0];\n if (\n executor?.type !== AST_NODE_TYPES.ArrowFunctionExpression &&\n executor?.type !== AST_NODE_TYPES.FunctionExpression\n ) {\n return false;\n }\n const body = executor.body;\n if (body.type !== AST_NODE_TYPES.BlockStatement) {\n return isTimedSetTimeout(body);\n }\n return body.body.some(\n (stmt) => stmt.type === AST_NODE_TYPES.ExpressionStatement && isTimedSetTimeout(stmt.expression),\n );\n}\n\n/** True when `node` is `sleep(n)` / `delay(n)` with a nonzero numeric literal. */\nfunction isHelperSleep(node: TSESTree.CallExpression): boolean {\n return (\n node.callee.type === AST_NODE_TYPES.Identifier &&\n SLEEP_HELPERS.has(node.callee.name) &&\n node.arguments.length >= 1 &&\n isNonzeroNumericLiteral(node.arguments[0])\n );\n}\n\n/** The nearest enclosing function of `node`, skipping the `new Promise` executor. */\nfunction nearestEnclosingFunction(node: TSESTree.Node): TSESTree.Node | null {\n for (let current = node.parent; current != null; current = current.parent) {\n if (!FUNCTION_TYPES.has(current.type)) {\n continue;\n }\n const grandparent = current.parent;\n const isPromiseExecutor =\n grandparent?.type === AST_NODE_TYPES.NewExpression && isPromiseSleep(grandparent);\n if (!isPromiseExecutor) {\n return current;\n }\n }\n return null;\n}\n\n/** The base callee name of a call, unwrapping `.only` / `.skip` / `.each` chains. */\nfunction testCallerName(callee: TSESTree.Node): string | null {\n if (callee.type === AST_NODE_TYPES.Identifier) {\n return callee.name;\n }\n if (callee.type === AST_NODE_TYPES.MemberExpression) {\n return testCallerName(callee.object);\n }\n if (callee.type === AST_NODE_TYPES.CallExpression) {\n return testCallerName(callee.callee);\n }\n if (callee.type === AST_NODE_TYPES.TaggedTemplateExpression) {\n return testCallerName(callee.tag);\n }\n return null;\n}\n\n/** True when `fn` is the callback argument of an `it`/`test`/per-test-hook call. */\nfunction isTestBody(fn: TSESTree.Node): boolean {\n const call = fn.parent;\n if (\n call?.type !== AST_NODE_TYPES.CallExpression ||\n !call.arguments.some((argument) => argument === fn)\n ) {\n return false;\n }\n const name = testCallerName(call.callee);\n return name !== null && TEST_CALLERS.has(name);\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-sleep-in-test-body\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Disallow a fixed timed sleep directly in a test body; it flakes under CI load. Synchronize on the signal or use fake timers.\",\n },\n schema: [],\n messages: {\n noSleepInTestBody:\n \"A fixed sleep in a test body asserts on wall-clock time and flakes under CI load. Await the promise the code returns, or drive time with `vi.useFakeTimers()` + `await vi.advanceTimersByTimeAsync(ms)`.\",\n },\n },\n defaultOptions: [],\n create(context) {\n if (!isTestFile(context.filename)) {\n return {};\n }\n const report = (node: TSESTree.Node): void => {\n const enclosing = nearestEnclosingFunction(node);\n if (enclosing === null || !isTestBody(enclosing)) {\n return;\n }\n context.report({ node, messageId: \"noSleepInTestBody\" });\n };\n return {\n NewExpression(node: TSESTree.NewExpression): void {\n if (isPromiseSleep(node)) {\n report(node);\n }\n },\n CallExpression(node: TSESTree.CallExpression): void {\n if (isHelperSleep(node)) {\n report(node);\n }\n },\n };\n },\n});\n","import { AST_NODE_TYPES, ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\nimport { isTestFile } from \"./_paths.js\";\n\ntype MessageIds = \"noConditionalInTest\";\ntype Options = readonly [];\n\nconst TEST_CALLERS: ReadonlySet<string> = new Set([\"it\", \"test\"]);\n\nconst FUNCTION_TYPES: ReadonlySet<AST_NODE_TYPES> = new Set([\n AST_NODE_TYPES.FunctionDeclaration,\n AST_NODE_TYPES.FunctionExpression,\n AST_NODE_TYPES.ArrowFunctionExpression,\n]);\n\n/** The nearest enclosing function of `node`. */\nfunction nearestEnclosingFunction(node: TSESTree.Node): TSESTree.Node | null {\n for (let current = node.parent; current != null; current = current.parent) {\n if (FUNCTION_TYPES.has(current.type)) {\n return current;\n }\n }\n return null;\n}\n\n/** The base callee name of a call, unwrapping `.only` / `.skip` / `.each` chains. */\nfunction testCallerName(callee: TSESTree.Node): string | null {\n if (callee.type === AST_NODE_TYPES.Identifier) {\n return callee.name;\n }\n if (callee.type === AST_NODE_TYPES.MemberExpression) {\n return testCallerName(callee.object);\n }\n if (callee.type === AST_NODE_TYPES.CallExpression) {\n return testCallerName(callee.callee);\n }\n if (callee.type === AST_NODE_TYPES.TaggedTemplateExpression) {\n return testCallerName(callee.tag);\n }\n return null;\n}\n\n/** True when `fn` is the callback argument of an `it` or `test` call. */\nfunction isTestBody(fn: TSESTree.Node): boolean {\n const call = fn.parent;\n if (\n call?.type !== AST_NODE_TYPES.CallExpression ||\n !call.arguments.some((argument) => argument === fn)\n ) {\n return false;\n }\n const name = testCallerName(call.callee);\n return name !== null && TEST_CALLERS.has(name);\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-conditional-in-test\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Disallow conditional logic (if, switch, ternary) in test bodies, which can hide missing assertions or test multiple code paths.\",\n },\n schema: [],\n messages: {\n noConditionalInTest:\n \"Avoid using conditional logic in tests. It can obscure intent and hide unexecuted assertions. Split the test instead.\",\n },\n },\n defaultOptions: [],\n create(context) {\n if (!isTestFile(context.filename)) {\n return {};\n }\n const report = (node: TSESTree.Node): void => {\n const enclosing = nearestEnclosingFunction(node);\n if (enclosing === null || !isTestBody(enclosing)) {\n return;\n }\n context.report({ node, messageId: \"noConditionalInTest\" });\n };\n return {\n IfStatement(node: TSESTree.IfStatement): void {\n report(node);\n },\n SwitchStatement(node: TSESTree.SwitchStatement): void {\n report(node);\n },\n ConditionalExpression(node: TSESTree.ConditionalExpression): void {\n report(node);\n },\n LogicalExpression(node: TSESTree.LogicalExpression): void {\n report(node);\n },\n };\n },\n});\n","/**\n * @fileoverview TS port of SARJ011 (`prefer-constant-time-secret-compare`).\n * Comparing secrets (bearer tokens, signatures, HMACs, password hashes, API\n * keys) with `===`/`!==`/`==`/`!=` is timing-attack-prone: the engine's string\n * comparison short-circuits on the first differing byte, so the time it takes to\n * return `false` leaks how many leading bytes the attacker got right. Over enough\n * requests that is enough to recover the secret byte by byte.\n *\n * On Cloudflare Workers the correct primitive is `crypto.subtle.timingSafeEqual`\n * (over equal-length `ArrayBuffer`s — SHA-256 both sides first so the lengths\n * match and no length is leaked), or a hand-rolled XOR-accumulate loop over the\n * digests. Node exposes `crypto.timingSafeEqual`. `===` is never the answer.\n *\n * The secret-name predicate is shared with `no-secret-in-log` and then narrowed\n * (`isAuthSecretName`): a timing-attack surface is only an *authenticator* whose\n * bytes gate access, so the following are deliberately NOT flagged even though\n * they are secret-shaped by name:\n *\n * - Category / handle metadata: `tokenType === \"Bearer\"`, `tokenName`,\n * `credentialKind`, `apiKeyId` — a discriminator or row id, not the credential.\n * - Boolean flags: `isTokenValid`, `hasSecret` — a decision, not the bytes.\n * - Integrity-only content hashes: `contentHash === previousHash` — a change\n * detector, not an authenticator. `passwordHash` / `tokenHash` still fire\n * because those DO gate access.\n * - Presence checks and literal sentinels: `token === null`, `secret !== undefined`,\n * `token.length === 0`, `tokenType === \"bearer\"`, `x === TOKEN_TYPE_SYSTEM` —\n * comparing against a compile-time constant or a null check reveals nothing\n * about a runtime secret.\n * - Marker values whose camelCase name merely ENDS in a secret word:\n * `queryFn === skipToken`. A `skipToken` is a unique `Symbol` compared by\n * identity, so there are no bytes to walk and no timing to leak. This was the\n * rule's entire false-positive surface on the 2026-07 corpus sweep — 10 hits\n * out of 10, all `skipToken`; see `SENTINEL_PREFIX_RE`.\n * - Anything in a test file: no attacker measures a test's clock, and fixture\n * assertions (`expect(res.apiKey === \"known\")`) are not an auth path.\n *\n * References:\n * - https://developers.cloudflare.com/workers/runtime-apis/web-crypto/#timingsafeequal\n * - https://codahale.com/a-lesson-in-timing-attacks/\n */\n\nimport { AST_NODE_TYPES, ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\nimport { isAuthSecretName } from \"./_secret_names.js\";\nimport { isTestFile } from \"./_paths.js\";\n\ntype MessageIds = \"preferConstantTimeSecretCompare\";\ntype Options = readonly [];\n\nconst EQUALITY_OPERATORS: ReadonlySet<string> = new Set([\"===\", \"!==\", \"==\", \"!=\"]);\n\n/**\n * Identifiers that are compile-time sentinels rather than runtime values.\n * `undefined`/`NaN` make the comparison a presence check; an ALL-CAPS name is a\n * named constant or enum member (`TOKEN_TYPE_SYSTEM`), and timing a compare\n * against a value the attacker already knows leaks nothing.\n */\nconst SENTINEL_IDENTIFIERS: ReadonlySet<string> = new Set([\"undefined\", \"NaN\"]);\n\n/**\n * Words that mark a secret-SHAPED name as a placeholder rather than a live\n * credential: `TOKEN_SENTINEL`, `EMPTY_SECRET`, `PLACEHOLDER_API_KEY`. Comparing\n * against one of these is a presence check, not an authentication check, so it\n * stays excluded even though the secret-name heuristics match it.\n */\nconst SENTINEL_WORDS = /(^|_)(SENTINEL|EMPTY|NONE|NULL|UNSET|MISSING|PLACEHOLDER|DUMMY|FAKE|EXAMPLE)(_|$)/;\n\n/**\n * The camelCase spelling of the same idea: a marker VALUE whose name happens to\n * end in a secret-shaped word. A library exports `skipToken` as a unique\n * `Symbol` and callers test identity against it — there are no bytes to compare\n * and nothing an attacker could learn from the timing.\n *\n * Corpus evidence (2220 files across zod / TanStack Query / react-router / swr /\n * zustand, 2026-07): the rule fired 10 times and ALL TEN were\n * `options.queryFn === skipToken`, e.g.\n * `query/packages/query-core/src/queryClient.ts:604`. The list is deliberately\n * limited to modifiers that can only introduce a marker; `default`/`invalid`\n * are excluded because `defaultApiKey` really can hold a live credential.\n */\nconst SENTINEL_PREFIX_RE =\n /^(skip|sentinel|empty|none|missing|unset|placeholder|dummy|fake|example|noop)[A-Z]/;\nconst AST_NODE_TYPE_RE = /^(?:TS|JSX)?[A-Z][A-Za-z]*(?:Signature|Keyword|Expression|Declaration|Element|Literal|Identifier)$/;\n\n/**\n * True when every cased character is upper-case and at least one letter exists —\n * AND the name is not itself secret-shaped.\n *\n * The ALL-CAPS carve-out exists for public named constants (`TOKEN_TYPE_SYSTEM`).\n * But environment secrets are conventionally SCREAMING_SNAKE too\n * (`ADMIN_TOKEN`, `ASHBY_API_KEY`, `SLACK_SIGNING_SECRET`), so an unqualified\n * ALL-CAPS bail-out made this rule blind to essentially every real secret\n * compare: `env.ADMIN_TOKEN === supplied` was silent while the camelCase\n * `env.adminToken === supplied` fired. That is the exact comparison this rule\n * exists to catch. Defer to the shared secret-name heuristics first.\n */\nfunction isConstantReference(identifier: string): boolean {\n if (AST_NODE_TYPE_RE.test(identifier)) return true;\n if (isAuthSecretName(identifier) && !SENTINEL_WORDS.test(identifier)) return false;\n return identifier === identifier.toUpperCase() && /[A-Za-z]/.test(identifier);\n}\n\n/**\n * True when this operand makes the comparison a non-timing-attack surface:\n * any literal (string / number / boolean / null / regex), a template literal\n * with no substitutions, `undefined`/`NaN`, or an ALL-CAPS constant reference.\n */\nfunction isExcludedOperand(node: TSESTree.Node): boolean {\n switch (node.type) {\n case AST_NODE_TYPES.Literal:\n return true;\n case AST_NODE_TYPES.TemplateLiteral:\n return node.expressions.length === 0;\n case AST_NODE_TYPES.Identifier:\n return (\n SENTINEL_IDENTIFIERS.has(node.name) ||\n SENTINEL_PREFIX_RE.test(node.name) ||\n isConstantReference(node.name)\n );\n case AST_NODE_TYPES.MemberExpression:\n return (\n !node.computed &&\n node.property.type === AST_NODE_TYPES.Identifier &&\n (SENTINEL_PREFIX_RE.test(node.property.name) ||\n isConstantReference(node.property.name))\n );\n default:\n return false;\n }\n}\n\n/** The identifier a plain operand denotes, or null for anything else. */\nfunction operandName(node: TSESTree.Node): string | null {\n if (node.type === AST_NODE_TYPES.Identifier) {\n return node.name;\n }\n if (\n node.type === AST_NODE_TYPES.MemberExpression &&\n !node.computed &&\n node.property.type === AST_NODE_TYPES.Identifier\n ) {\n return node.property.name;\n }\n return null;\n}\n\n/**\n * True when the operand names an authenticator worth a constant-time compare.\n *\n * A template literal counts when it interpolates one: `header === \\`Bearer\n * ${adminToken}\\`` is the canonical Workers auth check and is exactly as\n * timing-leaky as comparing the bare token, since the constant prefix only makes\n * the first bytes free.\n */\nfunction isSecretOperand(node: TSESTree.Node): boolean {\n if (node.type === AST_NODE_TYPES.TemplateLiteral) {\n return node.expressions.some((expression) => isSecretOperand(expression));\n }\n const name = operandName(node);\n return name !== null && isAuthSecretName(name);\n}\n\n/** The secret identifier this comparison exposes, for the diagnostic message. */\nfunction secretNameOf(node: TSESTree.Node): string | null {\n if (node.type === AST_NODE_TYPES.TemplateLiteral) {\n for (const expression of node.expressions) {\n const nested = secretNameOf(expression);\n if (nested !== null) {\n return nested;\n }\n }\n return null;\n }\n return operandName(node);\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"prefer-constant-time-secret-compare\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Disallow `===`/`!==` on a secret-like value; short-circuiting comparison leaks the secret through timing. Use a constant-time compare.\",\n },\n schema: [],\n messages: {\n preferConstantTimeSecretCompare:\n \"`{{operator}}` on secret `{{name}}` short-circuits on the first differing byte and leaks it through timing. Compare constant-time instead (`crypto.subtle.timingSafeEqual` over equal-length SHA-256 digests).\",\n },\n },\n defaultOptions: [],\n create(context) {\n if (isTestFile(context.filename)) {\n return {};\n }\n return {\n BinaryExpression(node: TSESTree.BinaryExpression): void {\n if (!EQUALITY_OPERATORS.has(node.operator)) {\n return;\n }\n const { left, right } = node;\n if (isExcludedOperand(left) || isExcludedOperand(right)) {\n return;\n }\n const secret = [left, right].find((operand) => isSecretOperand(operand));\n if (secret === undefined) {\n return;\n }\n context.report({\n node,\n messageId: \"preferConstantTimeSecretCompare\",\n data: { operator: node.operator, name: secretNameOf(secret) ?? \"\" },\n });\n },\n };\n },\n});\n","/**\n * @fileoverview TS port of SARJ018 (`store-insert-requires-on-conflict`).\n * An embedded `INSERT INTO ... VALUES/SELECT` with no conflict clause must\n * become an idempotent upsert.\n *\n * Store write paths run under retries, races, and replays — and on Cloudflare\n * Workers that is the *normal* case, not the exception: a cron trigger can fire\n * twice, a Queue message is redelivered on any handler throw, and a `waitUntil`\n * task can be retried after the response is already sent. A bare `INSERT` under\n * redelivery either duplicates rows (a second Slack post, a second email, a\n * double-counted referral) or throws on a unique-constraint violation, which\n * fails the handler, which triggers another redelivery. Every store write should\n * be `INSERT ... ON CONFLICT ... DO UPDATE` / `DO NOTHING` so replay is a no-op.\n *\n * The rule reads every statically-resolvable SQL string in the file — plain\n * literals, template literals (`${...}` becomes a `?` parameter marker), `+`\n * concatenations, and `[...].join(\" \")` fragment arrays — and flags one that\n * contains a genuine `INSERT INTO ... VALUES` / `... SELECT` write with no\n * conflict handling. SQL string-literal values and `--` / comment bodies are\n * neutralized first, so an `ON CONFLICT` living inside a quoted value never\n * excuses a bare insert, a `--` inside a value never eats a real clause, and\n * commented-out keywords neither trigger nor excuse a finding.\n *\n * Deliberately NOT flagged:\n * - SQLite/D1's own idempotent insert forms: `INSERT OR IGNORE INTO ...` and\n * `INSERT OR REPLACE INTO ...` already survive replay.\n * - MySQL's `ON DUPLICATE KEY UPDATE`, the same contract under another name.\n * - Pure reads, DDL, and `RETURNING`-only tails.\n * - Test files. A fresh in-memory D1 has nothing to conflict with, so fixture\n * seeding legitimately uses a bare `INSERT`; flagging it would train people to\n * ignore the rule.\n *\n * For a deliberate append-only write (an event/audit log where duplicates are\n * the point) disable with an inline `eslint-disable-next-line` and a reason.\n */\n\nimport { ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\nimport { isTestFile } from \"./_paths.js\";\nimport { createSqlListener } from \"./_sql.js\";\n\ntype MessageIds = \"storeInsertRequiresOnConflict\";\ntype Options = readonly [];\n\n/**\n * A real insert *write*: the keyword, a table identifier, an optional column\n * list, then `VALUES` / `SELECT` / `DEFAULT VALUES` with nothing in between.\n * Requiring that exact adjacency is what keeps English prose out — \"failed to\n * insert into the queue: values were rejected\" has words between the table and\n * the verb, so it does not match. The optional `OR <action>` clause is SQLite's\n * conflict resolution; it is matched here so `INSERT OR IGNORE INTO` is\n * recognised as an insert at all, then excused below.\n */\nconst INSERT_WRITE =\n /\\bINSERT\\s+(?:OR\\s+\\w+\\s+)?INTO\\s+[\\w.\"'`?$:@-]+\\s*(?:\\([^)]*\\)\\s*)?(?:VALUES|SELECT|DEFAULT\\s+VALUES)\\b/i;\n\n/** Conflict handling that makes the write replay-safe. */\nconst CONFLICT_HANDLED = /\\bON\\s+CONFLICT\\b|\\bON\\s+DUPLICATE\\s+KEY\\b|\\bINSERT\\s+OR\\s+(?:IGNORE|REPLACE)\\b/i;\n\n/**\n * Cheap substring gate. Noise-stripping only ever blanks characters to spaces,\n * so it can never introduce a keyword the raw text lacks — a file with no\n * `insert` at all can never produce a finding, and most files in a repo sweep\n * are not SQL-bearing.\n */\nconst INSERT_GATE = /insert/i;\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"store-insert-requires-on-conflict\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Require an embedded SQL INSERT to carry ON CONFLICT; store writes replay under cron re-runs and queue redelivery and must be idempotent upserts.\",\n },\n schema: [],\n messages: {\n storeInsertRequiresOnConflict:\n \"This INSERT is not replay-safe: a cron re-run or queue redelivery duplicates the row (or fails the handler on a unique-constraint violation). Add `ON CONFLICT (...) DO UPDATE` / `DO NOTHING` (or `INSERT OR IGNORE`).\",\n },\n },\n defaultOptions: [],\n create(context) {\n if (isTestFile(context.filename) || !INSERT_GATE.test(context.sourceCode.text)) {\n return {};\n }\n return createSqlListener((sql: string, node: TSESTree.Node): void => {\n if (!INSERT_WRITE.test(sql) || CONFLICT_HANDLED.test(sql)) {\n return;\n }\n context.report({ node, messageId: \"storeInsertRequiresOnConflict\" });\n });\n },\n});\n","/**\n * @fileoverview Disallow building a SQL statement out of runtime values.\n *\n * `db.prepare(sql)` takes a STATIC statement plus `?` / `$1` placeholders bound\n * through `.bind(...)`. Interpolating a value into the statement text bypasses\n * the binding layer completely: that is SQL injection, and it also defeats the\n * driver's prepared-statement cache, because every distinct value produces a\n * distinct statement to compile. The shape is identical across Cloudflare D1,\n * better-sqlite3 and node-postgres, so the rule is driver-agnostic.\n *\n * WHAT IT CATCHES\n * db.prepare(`select * from users where id = '${userId}'`)\n * db.prepare(\"select * from users where id = '\" + userId + \"'\")\n * db.exec(`delete from sessions where token = '${token}'`)\n *\n * NOT FLAGGED\n * - `${CONSTANT_CASE}` fragments. A module-level constant — the column-list\n * constants several repos keep (`${CANDIDATE_COLS}`, `${TABLES.USERS}`) —\n * is a compile-time value, not user input. Anything starting lowercase is\n * treated as runtime data.\n * - A template literal with no interpolations at all.\n * - Tagged templates (`` sql`select ... ${id}` ``). A tag function receives the\n * static strings and the values separately and is the parameterising\n * mechanism, not a bypass of it.\n * - `.prepare()` on something that is not a database. Requiring an\n * interpolated runtime value keeps this rare in practice.\n * - A statement text that contains no SQL data-statement keyword. `exec` and\n * `query` are not SQL-specific names: a 5-repo sweep of real TypeScript\n * (zod / TanStack Query / react-router / swr / zustand, 2,186 files) found\n * 4/4 hits were `child_process.exec` building a SHELL command line —\n * an `open <url>` at\n * react-router/integration/helpers/playwright-fixture.ts:230 and an\n * `npm view <pkg>@<version> version` at\n * react-router/scripts/changes/publish.ts:111. Requiring the statement to\n * actually read as SQL (`SELECT` / `INSERT INTO` / `UPDATE` / `DELETE FROM`\n * / DDL / `PRAGMA` / a CTE head) takes that class to zero without touching\n * any real injection: an interpolated statement with no SQL verb in it was\n * never a SQL statement.\n *\n * CONFIGURATION\n * `methods` is the list of statement-taking method names to inspect. Extend it\n * for a driver that names things differently:\n *\n * \"@sarj/no-dynamic-sql\": [\"error\", { \"methods\": [\"prepare\", \"exec\", \"raw\"] }]\n *\n * Supplying `methods` REPLACES the defaults.\n */\n\nimport { AST_NODE_TYPES, ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\nimport { stripSqlNoise } from \"./_sql.js\";\n\ntype MessageIds = \"dynamicSql\";\n\nexport interface RuleOptions {\n /** Statement-taking method names to inspect. Replaces the defaults. */\n readonly methods?: readonly string[];\n}\n\ntype Options = readonly [RuleOptions?];\n\nconst DEFAULT_METHODS: readonly string[] = [\"prepare\", \"exec\", \"query\"];\n\n/** A module-level constant fragment: `CANDIDATE_COLS`, `TABLE_NAME`. */\nconst CONSTANT_CASE_RE = /^[A-Z][A-Z0-9_]*$/;\n\n/**\n * True when an interpolated expression is a compile-time SQL fragment rather\n * than runtime data: a CONSTANT_CASE identifier, a member access whose final\n * property is CONSTANT_CASE (`TABLES.USERS`), or a string literal.\n */\nfunction isStaticFragment(expression: TSESTree.Expression): boolean {\n if (expression.type === AST_NODE_TYPES.Identifier) {\n return CONSTANT_CASE_RE.test(expression.name);\n }\n if (\n expression.type === AST_NODE_TYPES.MemberExpression &&\n !expression.computed &&\n expression.property.type === AST_NODE_TYPES.Identifier\n ) {\n return CONSTANT_CASE_RE.test(expression.property.name);\n }\n if (expression.type === AST_NODE_TYPES.Literal) {\n return typeof expression.value === \"string\";\n }\n return false;\n}\n\n/** The interpolated runtime expressions in a template literal. */\nfunction runtimeInterpolations(\n template: TSESTree.TemplateLiteral,\n): TSESTree.Expression[] {\n return template.expressions.filter(\n (expression) => !isStaticFragment(expression),\n );\n}\n\n/**\n * Flatten a `+` chain into its operands. `\"a\" + b + \"c\"` yields three nodes so\n * each can be judged independently.\n */\nfunction concatOperands(node: TSESTree.Expression): TSESTree.Expression[] {\n if (node.type === AST_NODE_TYPES.BinaryExpression && node.operator === \"+\") {\n return [...concatOperands(node.left), ...concatOperands(node.right)];\n }\n return [node];\n}\n\n/**\n * The runtime operands of a string-concatenation chain, or an empty array when\n * the node is not a concatenation that mixes a literal with a runtime value.\n * Requiring at least one string literal is what distinguishes statement\n * building from ordinary arithmetic.\n */\nfunction runtimeConcatOperands(node: TSESTree.Node): TSESTree.Expression[] {\n if (node.type !== AST_NODE_TYPES.BinaryExpression || node.operator !== \"+\") {\n return [];\n }\n const operands = concatOperands(node);\n const hasStringLiteral = operands.some(\n (operand) =>\n operand.type === AST_NODE_TYPES.Literal &&\n typeof operand.value === \"string\",\n );\n if (!hasStringLiteral) {\n return [];\n }\n return operands.filter(\n (operand) =>\n operand.type !== AST_NODE_TYPES.Literal && !isStaticFragment(operand),\n );\n}\n\n/**\n * A SQL data/DDL statement keyword. `exec` and `query` are generic method names\n * — `child_process.exec` shares them — so the statement text itself has to prove\n * it is SQL before an interpolation into it can be an injection.\n */\nconst SQL_STATEMENT_RE =\n /\\b(?:select\\s|insert\\s+into\\b|insert\\s+or\\b|update\\s+\\w|delete\\s+from\\b|replace\\s+into\\b|merge\\s+into\\b|upsert\\s+into\\b|create\\s+(?:temp(?:orary)?\\s+)?(?:table|index|view|trigger|schema|database)\\b|alter\\s+table\\b|drop\\s+(?:table|index|view|trigger)\\b|truncate\\s+table\\b|pragma\\s+\\w|with\\s+\\w+\\s+as\\s*\\(|from\\s+\\w+\\s+where\\b)/i;\n\n/** The marker a non-static operand contributes to the reconstructed statement text. */\nconst RUNTIME_MARKER = \" ? \";\n\n/**\n * The statically known text of a statement argument, with every runtime operand\n * replaced by a placeholder. Enough to answer \"is this SQL?\" for both the\n * template-literal and the `+`-concatenation shape.\n */\nfunction staticStatementText(node: TSESTree.Node): string {\n if (node.type === AST_NODE_TYPES.TemplateLiteral) {\n return node.quasis.map((quasi) => quasi.value.cooked ?? quasi.value.raw).join(RUNTIME_MARKER);\n }\n if (node.type === AST_NODE_TYPES.Literal) {\n return typeof node.value === \"string\" ? node.value : RUNTIME_MARKER;\n }\n if (node.type === AST_NODE_TYPES.BinaryExpression && node.operator === \"+\") {\n return staticStatementText(node.left) + staticStatementText(node.right);\n }\n return RUNTIME_MARKER;\n}\n\n/** True when the statement argument reads as SQL rather than as a shell command line. */\nfunction looksLikeSql(node: TSESTree.Node): boolean {\n return SQL_STATEMENT_RE.test(stripSqlNoise(staticStatementText(node)));\n}\n\n/** The inspected method name of `receiver.method(...)`, or null. */\nfunction statementMethodName(\n node: TSESTree.CallExpression,\n methods: ReadonlySet<string>,\n): string | null {\n const callee = node.callee;\n if (\n callee.type !== AST_NODE_TYPES.MemberExpression ||\n callee.computed ||\n callee.property.type !== AST_NODE_TYPES.Identifier\n ) {\n return null;\n }\n const name = callee.property.name;\n return methods.has(name) ? name : null;\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-dynamic-sql\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Disallow interpolating or concatenating a runtime value into a SQL statement passed to `prepare`/`exec`/`query`; use a placeholder and bind the value.\",\n },\n schema: [\n {\n type: \"object\",\n properties: {\n methods: {\n type: \"array\",\n items: { type: \"string\" },\n description:\n \"Statement-taking method names to inspect. Replaces the defaults.\",\n },\n },\n additionalProperties: false,\n },\n ],\n messages: {\n dynamicSql:\n \"Runtime value built into a SQL statement passed to `{{method}}()`. Use a `?` placeholder and pass the value through `.bind(...)` so the driver parameterises it.\",\n },\n },\n defaultOptions: [{}],\n create(context, [options]) {\n const methods = new Set(options?.methods ?? DEFAULT_METHODS);\n\n return {\n CallExpression(node: TSESTree.CallExpression): void {\n const method = statementMethodName(node, methods);\n if (method === null) {\n return;\n }\n\n const statement = node.arguments[0];\n if (statement === undefined || !looksLikeSql(statement)) {\n return;\n }\n\n const offenders =\n statement.type === AST_NODE_TYPES.TemplateLiteral\n ? runtimeInterpolations(statement)\n : runtimeConcatOperands(statement);\n\n for (const offender of offenders) {\n context.report({\n node: offender,\n messageId: \"dynamicSql\",\n data: { method },\n });\n }\n },\n };\n },\n});\n","/**\n * @fileoverview Keep outbound HTTP behind a client module.\n *\n * A bare `fetch()` in a route handler, server action or component opts out of\n * whatever the codebase's client layer provides — retry/backoff, timeouts,\n * status handling, auth headers, structured log breadcrumbs. It is also the\n * shape that cannot be stubbed in a test without monkey-patching global\n * `fetch`, so the call site quietly becomes untestable.\n *\n * WHAT IT CATCHES\n * fetch(url) // bare global\n * globalThis.fetch(url) // explicit global receiver\n * window.fetch(url)\n *\n * NOT FLAGGED\n * - Files whose path matches one of the `allow` patterns. The defaults cover\n * the conventions we have seen in practice — a `clients/` directory, a\n * `*-client.ts` module, an `http-client.*` wrapper, an `api/` directory or\n * `api.ts` / `*-api.ts` module — plus test files and codemod fixtures.\n * - A method named `fetch` on some other receiver (`cache.fetch(k)`,\n * `queryClient.fetch()`): only the global is HTTP.\n * - A pre-signed upload/download URL transfer (`fetch(uploadUrl, ...)`,\n * `fetch(file.downloadUrl)`). Those URLs are one-off storage handoffs,\n * not calls to a first-party service API that belongs behind a client\n * wrapper.\n * - `new Request(...)` / `axios(...)` and friends. This rule is about the\n * global `fetch`, not about every HTTP library.\n *\n * CONFIGURATION\n * `allow` is a list of regular-expression sources matched against the absolute\n * filename, so a repo that keeps its client layer somewhere else can say so\n * rather than sprinkling disable comments:\n *\n * \"@sarj/no-raw-fetch-outside-clients\": [\"error\", {\n * \"allow\": [\"[\\\\\\\\/]lib[\\\\\\\\/]api[\\\\\\\\/]\", \"-gateway\\\\\\\\.ts$\"]\n * }]\n *\n * Supplying `allow` REPLACES the defaults, so include the test patterns if you\n * still want test files exempt.\n *\n * CORPUS SWEEP (2220 files, zod / TanStack Query / react-router / swr /\n * zustand, 2026-07): 96 raw hits. The defaults missed three path conventions\n * that ARE the client layer, so the reports landed on the very modules the rule\n * wants the `fetch` to live in:\n * - `api.ts` / `api/` / `*-api.ts` — 15 hits, e.g.\n * `query/examples/react/star-wars/src/api.ts:7`, a module that exists solely\n * to own `getFilm`/`getPerson` HTTP calls. `api` is the same convention\n * family as `clients/`, just the other common spelling.\n * - Hyphenated test basenames (`*-test.ts` / `*-spec.ts`) — the default list\n * only knew the dotted `*.test.ts` form, so react-router's entire suite\n * (which names files `single-fetch-test.ts`) was unprotected.\n * - `__testfixtures__/` — 8 hits from jscodeshift fixtures such as\n * `query/packages/query-codemods/src/v5/remove-overloads/__testfixtures__/bug-reports.input.tsx`,\n * which are input/output text for a codemod, not code that runs.\n */\n\nimport { ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\ntype MessageIds = \"rawFetch\";\n\nexport interface RuleOptions {\n /** Regular-expression sources matched against the filename. */\n readonly allow?: readonly string[];\n}\n\ntype Options = readonly [RuleOptions?];\n\n/**\n * Path shapes that legitimately own outbound HTTP, plus test files. Written as\n * regex sources (not globs) so they can express both path separators.\n */\nconst DEFAULT_ALLOW: readonly string[] = [\n \"[\\\\\\\\/]clients?[\\\\\\\\/]\",\n \"-client\\\\.[cm]?[jt]sx?$\",\n \"[\\\\\\\\/]http-client\\\\.[cm]?[jt]sx?$\",\n // The `api` spelling of the same client-layer convention: an `api/` directory,\n // a bare `api.ts`, or a `*-api.ts` / `*.api.ts` module.\n \"[\\\\\\\\/]api[\\\\\\\\/]\",\n \"[\\\\\\\\/]api\\\\.[cm]?[jt]sx?$\",\n \"[-.]api\\\\.[cm]?[jt]sx?$\",\n \"\\\\.test\\\\.\",\n \"\\\\.spec\\\\.\",\n // `*.test-d.ts` type tests, and react-router's `single-fetch-test.ts` spelling.\n \"\\\\.(test|spec)-d\\\\.[cm]?[jt]sx?$\",\n \"-(test|spec)\\\\.[cm]?[jt]sx?$\",\n \"[\\\\\\\\/]__tests__[\\\\\\\\/]\",\n \"[\\\\\\\\/]__mocks__[\\\\\\\\/]\",\n \"[\\\\\\\\/]tests?[\\\\\\\\/]\",\n // jscodeshift input/output fixtures — text a codemod transforms, not code.\n \"[\\\\\\\\/]__testfixtures__[\\\\\\\\/]\",\n];\n\n/** Receivers on which `.fetch()` is the global, not some cache/query API. */\nconst GLOBAL_RECEIVERS: ReadonlySet<string> = new Set([\n \"globalThis\",\n \"window\",\n \"self\",\n]);\n\nconst PRESIGNED_URL_NAME_RE = /(?:pre-?signed|signed|upload|download)Url$/i;\n\n/** True when `node` is a call to the global `fetch`. */\nfunction isGlobalFetchCall(node: TSESTree.CallExpression): boolean {\n const callee = node.callee;\n\n if (callee.type === \"Identifier\") {\n return callee.name === \"fetch\";\n }\n\n if (\n callee.type === \"MemberExpression\" &&\n !callee.computed &&\n callee.property.type === \"Identifier\" &&\n callee.property.name === \"fetch\" &&\n callee.object.type === \"Identifier\"\n ) {\n return GLOBAL_RECEIVERS.has(callee.object.name);\n }\n\n return false;\n}\n\nfunction identifierLikeName(node: TSESTree.Node): string | null {\n if (node.type === \"Identifier\") {\n return node.name;\n }\n if (\n node.type === \"MemberExpression\" &&\n !node.computed &&\n node.property.type === \"Identifier\"\n ) {\n return node.property.name;\n }\n return null;\n}\n\nfunction isPresignedUrlTransfer(node: TSESTree.CallExpression): boolean {\n const first = node.arguments[0];\n if (first === undefined) {\n return false;\n }\n const name = identifierLikeName(first);\n return name !== null && PRESIGNED_URL_NAME_RE.test(name);\n}\n\n/**\n * Compile the configured patterns once per file. An invalid pattern is skipped\n * rather than thrown: a bad `allow` entry should not take the whole lint run\n * down, and the rule failing closed (still reporting) is the safe direction.\n */\nfunction compile(patterns: readonly string[]): RegExp[] {\n const compiled: RegExp[] = [];\n for (const pattern of patterns) {\n try {\n compiled.push(new RegExp(pattern));\n } catch {\n // Skip the malformed entry; the remaining patterns still apply.\n }\n }\n return compiled;\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-raw-fetch-outside-clients\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Disallow calling the global `fetch` outside the client layer; route outbound HTTP through a client module that owns retry, timeout and status handling.\",\n },\n schema: [\n {\n type: \"object\",\n properties: {\n allow: {\n type: \"array\",\n items: { type: \"string\" },\n description:\n \"Regular-expression sources matched against the filename. Replaces the defaults.\",\n },\n },\n additionalProperties: false,\n },\n ],\n messages: {\n rawFetch:\n \"Raw `fetch()` outside a client module. Route the call through a client (e.g. `clients/*-client.ts`) so it inherits retry, timeout and status handling and stays stubbable in tests.\",\n },\n },\n defaultOptions: [{}],\n create(context, [options]) {\n const patterns = options?.allow ?? DEFAULT_ALLOW;\n const allowed = compile(patterns);\n const filename = context.filename;\n\n if (allowed.some((re) => re.test(filename))) {\n return {};\n }\n\n return {\n CallExpression(node: TSESTree.CallExpression): void {\n if (isPresignedUrlTransfer(node)) {\n return;\n }\n if (isGlobalFetchCall(node)) {\n context.report({ node, messageId: \"rawFetch\" });\n }\n },\n };\n },\n});\n","/**\n * @fileoverview Keep modules that are stateless by design free of private\n * storage.\n *\n * Some features are stateless deliberately: they derive everything they need\n * from reads against the systems of record (Slack, Linear, GitHub, a CRM) plus\n * markers in the artefacts they themselves produced. The reason is operational\n * — such a feature can be re-run and back-filled freely, whereas a private\n * table or key/value namespace immediately diverges from what a human can\n * actually see and audit in the system of record. Adding a store to one of\n * these modules silently deletes that property, and nothing else in the\n * toolchain notices.\n *\n * WHAT IT CATCHES, inside the configured modules only\n * db.prepare(sql) // a SQL statement\n * kv.put(key, value) // a key/value write\n * kv.getWithMetadata(key) // a key/value read\n *\n * OPT-IN BY DESIGN\n * `modules` defaults to an EMPTY list, which makes the rule a no-op. That is\n * deliberate: the method names alone (`put`, `prepare`) carry no type\n * information, so the rule is only meaningful — and only quiet enough to live\n * in a shared preset — when it is pointed at the specific directories a team\n * has declared stateless.\n *\n * \"@sarj/no-storage-in-stateless-modules\": [\"error\", {\n * \"modules\": [\"[\\\\\\\\/]engineer-digest[\\\\\\\\/]\", \"[\\\\\\\\/]digest[\\\\\\\\/]\"]\n * }]\n *\n * `modules` entries are regular-expression sources matched against the absolute\n * filename. `methods` overrides the storage method names if a driver names\n * things differently.\n *\n * NOT FLAGGED\n * - Anything outside the configured modules.\n * - `.put()` with fewer than two arguments — a one-argument `put` is more\n * often a builder or queue helper than a key/value write.\n *\n * If a feature genuinely cannot be expressed statelessly, that is a design\n * conversation, not a disable comment.\n */\n\nimport { AST_NODE_TYPES, ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\ntype MessageIds = \"storageInStatelessModule\";\n\nexport interface RuleOptions {\n /** Regex sources matched against the filename. Empty means the rule is off. */\n readonly modules?: readonly string[];\n /** Storage method names to flag. Replaces the defaults. */\n readonly methods?: readonly string[];\n}\n\ntype Options = readonly [RuleOptions?];\n\nconst DEFAULT_METHODS: readonly string[] = [\n \"prepare\",\n \"put\",\n \"getWithMetadata\",\n];\n\n/**\n * Minimum argument count before a method call counts as storage access. `put`\n * needs a key AND a value; a single-argument `put` is usually something else.\n * Methods absent from this map need one argument.\n */\nconst MIN_ARGUMENTS: ReadonlyMap<string, number> = new Map([[\"put\", 2]]);\n\n/** Compile regex sources, skipping malformed entries rather than throwing. */\nfunction compile(patterns: readonly string[]): RegExp[] {\n const compiled: RegExp[] = [];\n for (const pattern of patterns) {\n try {\n compiled.push(new RegExp(pattern));\n } catch {\n // Skip the malformed entry; the remaining patterns still apply.\n }\n }\n return compiled;\n}\n\n/** The flagged storage method name of `receiver.method(...)`, or null. */\nfunction storageMethodName(\n node: TSESTree.CallExpression,\n methods: ReadonlySet<string>,\n): string | null {\n const callee = node.callee;\n if (\n callee.type !== AST_NODE_TYPES.MemberExpression ||\n callee.computed ||\n callee.property.type !== AST_NODE_TYPES.Identifier\n ) {\n return null;\n }\n const name = callee.property.name;\n if (!methods.has(name)) {\n return null;\n }\n if (node.arguments.length < (MIN_ARGUMENTS.get(name) ?? 1)) {\n return null;\n }\n return name;\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-storage-in-stateless-modules\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Disallow SQL or key/value access inside modules a team has declared stateless; derive state from the systems of record instead. No-op until `modules` is configured.\",\n },\n schema: [\n {\n type: \"object\",\n properties: {\n modules: {\n type: \"array\",\n items: { type: \"string\" },\n description:\n \"Regex sources matched against the filename. Empty (the default) disables the rule.\",\n },\n methods: {\n type: \"array\",\n items: { type: \"string\" },\n description: \"Storage method names to flag. Replaces the defaults.\",\n },\n },\n additionalProperties: false,\n },\n ],\n messages: {\n storageInStatelessModule:\n \"`{{method}}()` reaches for private storage inside a module declared stateless. Derive the state from a read against the system of record, or from a marker in the artefact this feature already produces.\",\n },\n },\n defaultOptions: [{}],\n create(context, [options]) {\n const modules = options?.modules ?? [];\n if (modules.length === 0) {\n return {};\n }\n\n const scoped = compile(modules);\n if (!scoped.some((re) => re.test(context.filename))) {\n return {};\n }\n\n const methods = new Set(options?.methods ?? DEFAULT_METHODS);\n\n return {\n CallExpression(node: TSESTree.CallExpression): void {\n const method = storageMethodName(node, methods);\n if (method !== null) {\n context.report({\n node,\n messageId: \"storageInStatelessModule\",\n data: { method },\n });\n }\n },\n };\n },\n});\n","/**\n * @fileoverview Disallow `z.nativeEnum(...)` in zod schemas; use\n * `z.enum([\"a\", \"b\"])` and derive the type with `z.infer<typeof Schema>`.\n *\n * Rationale — this is the schema-layer sibling of the `no-enum` rule. Zod's\n * `nativeEnum` exists for exactly one purpose: to wrap a TypeScript `enum`,\n * which `no-enum` already bans (enums emit runtime code, have unintuitive\n * numeric defaults, and don't tree-shake). Reaching for `nativeEnum` is\n * therefore either (a) importing a banned construct through the back door, or\n * (b) wrapping an `as const` object, in which case `z.enum` over the value list\n * is the shorter, better-inferring form. Either way the target state is the\n * same string-literal union `no-enum` prescribes, so the two rules point at one\n * destination.\n *\n * Two shapes fire:\n *\n * 1. **`z.nativeEnum(x)`** — always. Autofixable ONLY when the argument is an\n * inline object literal (optionally `as const`) whose every value is a\n * string literal: that set is fully known at the call site, so the rewrite\n * to `z.enum([...])` is mechanical and value-preserving. A numeric member\n * (`{ A: 1 }`) is NOT fixed — `z.enum` accepts strings only, so rewriting\n * would silently change the accepted input. A spread, computed key, shorthand\n * property, method, or an empty object is not fixed either (the member set\n * isn't statically known, and `z.enum([])` is not a valid schema). An\n * identifier argument (`z.nativeEnum(Fruits)`) is reported without a fix:\n * inlining the values at the call site would duplicate the literal set that\n * the named object exists to own, so the correct edit is a human one.\n *\n * 2. **`z.enum(SomeTsEnum)`** — zod v4 lets `z.enum` take a TS enum directly,\n * which is `nativeEnum` under a friendlier name and re-opens the same hole.\n * Detected two ways: lexical resolution of the identifier to a\n * `TSEnumDeclaration` in the same file, and — when type information is\n * available — the resolved symbol carrying an enum flag, which also catches\n * an enum imported from another module. `z.enum(COLORS)` where `COLORS` is a\n * `readonly string[]` / `as const` array is the prescribed pattern and never\n * fires.\n *\n * False positives handled: a bare `nativeEnum(...)` call fires only when the\n * name is imported from a zod module, so a same-named local helper is not\n * flagged. Generated files (`*.gen.ts`, `**\\/generated/**`, `*.d.ts`, or a\n * `@generated` marker) opt out, matching `no-enum` — codegen from an OpenAPI\n * spec legitimately emits enums and the schemas that wrap them.\n *\n * TEST FILES ARE EXEMPT. Corpus sweep (2220 files across zod / TanStack Query /\n * react-router / swr / zustand, 2026-07): 32 raw hits, 32 of them in test files\n * and 100% false positives. A test that covers `z.nativeEnum` has to CALL\n * `z.nativeEnum` — `zod/packages/zod/src/v3/tests/nativeEnum.test.ts:12`\n * (`const fruitEnum = z.nativeEnum(Fruits)`) is not importing a banned construct\n * through the back door, it is the coverage for the construct. The same applies\n * to any consumer pinning the migration behaviour of a legacy enum schema, and\n * it mirrors the exemption `no-enum` already grants generated code: the rule\n * targets the DECISION to model a domain with an enum, and a fixture makes no\n * such decision.\n */\n\nimport {\n ESLintUtils,\n type TSESLint,\n type TSESTree,\n type ParserServicesWithTypeInformation,\n AST_NODE_TYPES,\n} from \"@typescript-eslint/utils\";\nimport * as ts from \"typescript\";\n\nimport { isTestFile } from \"./_paths.js\";\n\ntype MessageIds = \"nativeEnum\" | \"enumOfTsEnum\";\ntype Options = readonly [];\n\nconst IGNORE_PATTERNS: readonly RegExp[] = [\n /[\\\\/]generated[\\\\/]/,\n /\\.gen\\.tsx?$/,\n /\\.generated\\.tsx?$/,\n /\\.d\\.ts$/,\n];\n\nfunction isIgnoredFile(filename: string, sourceText: string): boolean {\n if (IGNORE_PATTERNS.some((re) => re.test(filename))) {\n return true;\n }\n return /@generated\\b/.test(sourceText.slice(0, 1024));\n}\n\n/** `zod`, `zod/v4`, `zod/mini`, `@hono/zod-openapi`, `@/lib/zod`, ... */\nfunction isZodModule(source: string): boolean {\n return /(^|[/@-])zod([/-]|$)/.test(source);\n}\n\n/** Unwraps `x as const` / `x satisfies T` / `(x)` down to the inner expression. */\nfunction unwrap(node: TSESTree.Expression): TSESTree.Expression {\n if (\n node.type === AST_NODE_TYPES.TSAsExpression ||\n node.type === AST_NODE_TYPES.TSSatisfiesExpression\n ) {\n return unwrap(node.expression);\n }\n return node;\n}\n\n/**\n * The string values of an object literal whose every property is a plain\n * `key: \"literal\"` pair, as raw source text (so quote style survives the fix).\n * Returns null when any member is a spread, a computed key, a method, a\n * shorthand, or a non-string value — those sets are not statically rewritable.\n */\nfunction stringValueTexts(\n node: TSESTree.ObjectExpression,\n sourceCode: Readonly<TSESLint.SourceCode>,\n): string[] | null {\n const texts: string[] = [];\n for (const prop of node.properties) {\n if (prop.type !== AST_NODE_TYPES.Property) {\n return null;\n }\n if (prop.computed || prop.shorthand || prop.method || prop.kind !== \"init\") {\n return null;\n }\n const value = prop.value;\n if (\n value.type !== AST_NODE_TYPES.Literal ||\n typeof value.value !== \"string\"\n ) {\n return null;\n }\n const text = sourceCode.getText(value);\n if (!texts.includes(text)) {\n texts.push(text);\n }\n }\n return texts.length > 0 ? texts : null;\n}\n\n/** Resolves an identifier to a `TSEnumDeclaration` declared in this file. */\nfunction resolvesToLocalEnum(\n node: TSESTree.Identifier,\n scope: TSESLint.Scope.Scope,\n): boolean {\n let current: TSESLint.Scope.Scope | null = scope;\n while (current !== null) {\n const variable = current.variables.find((v) => v.name === node.name);\n if (variable !== undefined) {\n return variable.defs.some(\n (def) => def.node.type === AST_NODE_TYPES.TSEnumDeclaration,\n );\n }\n current = current.upper;\n }\n return false;\n}\n\nconst ENUM_SYMBOL_FLAGS =\n ts.SymbolFlags.RegularEnum | ts.SymbolFlags.ConstEnum | ts.SymbolFlags.Enum;\n\n/**\n * Whether the identifier's resolved symbol is a TypeScript enum, following\n * import aliases. Catches `import { Status } from \"./types\"; z.enum(Status)`,\n * which lexical resolution alone cannot see.\n */\nfunction resolvesToImportedEnum(\n node: TSESTree.Identifier,\n services: ParserServicesWithTypeInformation,\n): boolean {\n const checker = services.program.getTypeChecker();\n const tsNode = services.esTreeNodeToTSNodeMap.get(node);\n let symbol = checker.getSymbolAtLocation(tsNode);\n if (symbol === undefined) {\n return false;\n }\n if ((symbol.flags & ts.SymbolFlags.Alias) !== 0) {\n symbol = checker.getAliasedSymbol(symbol);\n }\n return (symbol.flags & ENUM_SYMBOL_FLAGS) !== 0;\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-zod-native-enum\",\n meta: {\n type: \"suggestion\",\n fixable: \"code\",\n docs: {\n description:\n \"Disallow `z.nativeEnum()` (and `z.enum()` over a TypeScript enum); use `z.enum([\\\"a\\\", \\\"b\\\"])` with a string-literal union instead.\",\n },\n schema: [],\n messages: {\n nativeEnum:\n '`z.nativeEnum()` exists to wrap a TypeScript `enum`, which `no-enum` bans. Use `z.enum([\"a\", \"b\"])` and derive the union with `z.infer<typeof Schema>`.',\n enumOfTsEnum:\n '`z.enum()` is being passed the TypeScript enum `{{name}}`, which `no-enum` bans. Pass a string-literal array instead: `z.enum([\"a\", \"b\"])`.',\n },\n },\n defaultOptions: [],\n create(context) {\n const sourceCode = context.sourceCode;\n if (isIgnoredFile(context.filename, sourceCode.getText())) {\n return {};\n }\n // A test that covers `z.nativeEnum` must call it; see @fileoverview.\n if (isTestFile(context.filename)) {\n return {};\n }\n\n let services: ParserServicesWithTypeInformation | null;\n try {\n services = ESLintUtils.getParserServices(context);\n } catch {\n services = null;\n }\n\n /** Local names imported from a zod module, e.g. `nativeEnum`, `enum_`. */\n const zodImportedNames = new Map<string, string>();\n\n function isZodMemberCall(node: TSESTree.CallExpression, api: string): boolean {\n const callee = node.callee;\n if (\n callee.type === AST_NODE_TYPES.MemberExpression &&\n !callee.computed &&\n callee.property.type === AST_NODE_TYPES.Identifier\n ) {\n return callee.property.name === api;\n }\n if (callee.type === AST_NODE_TYPES.Identifier) {\n return zodImportedNames.get(callee.name) === api;\n }\n return false;\n }\n\n function buildFix(\n node: TSESTree.CallExpression,\n ): TSESLint.ReportFixFunction | null {\n // A bare `nativeEnum(...)` imported from zod cannot be renamed in place —\n // `enum` is a reserved word, so there is no bare callee to rewrite to.\n // Rewriting only the argument would leave a `nativeEnum` call taking an\n // array, so this shape is report-only.\n const callee = node.callee;\n if (\n callee.type !== AST_NODE_TYPES.MemberExpression ||\n callee.property.type !== AST_NODE_TYPES.Identifier\n ) {\n return null;\n }\n const arg = node.arguments[0];\n if (\n arg === undefined ||\n node.arguments.length !== 1 ||\n arg.type === AST_NODE_TYPES.SpreadElement\n ) {\n return null;\n }\n const inner = unwrap(arg);\n if (inner.type !== AST_NODE_TYPES.ObjectExpression) {\n return null;\n }\n const values = stringValueTexts(inner, sourceCode);\n if (values === null) {\n return null;\n }\n const property = callee.property;\n const replacementArg = `[${values.join(\", \")}]`;\n return (fixer) => [\n fixer.replaceText(property, \"enum\"),\n fixer.replaceText(arg, replacementArg),\n ];\n }\n\n return {\n ImportDeclaration(node: TSESTree.ImportDeclaration): void {\n if (!isZodModule(node.source.value)) {\n return;\n }\n for (const spec of node.specifiers) {\n if (\n spec.type === AST_NODE_TYPES.ImportSpecifier &&\n spec.imported.type === AST_NODE_TYPES.Identifier\n ) {\n zodImportedNames.set(spec.local.name, spec.imported.name);\n }\n }\n },\n\n CallExpression(node: TSESTree.CallExpression): void {\n if (isZodMemberCall(node, \"nativeEnum\")) {\n const fix = buildFix(node);\n context.report({\n node,\n messageId: \"nativeEnum\",\n ...(fix === null ? {} : { fix }),\n });\n return;\n }\n\n if (!isZodMemberCall(node, \"enum\")) {\n return;\n }\n const arg = node.arguments[0];\n if (arg === undefined || arg.type !== AST_NODE_TYPES.Identifier) {\n return;\n }\n const isEnum =\n resolvesToLocalEnum(arg, sourceCode.getScope(arg)) ||\n (services !== null && resolvesToImportedEnum(arg, services));\n if (isEnum) {\n context.report({\n node,\n messageId: \"enumOfTsEnum\",\n data: { name: arg.name },\n });\n }\n },\n };\n },\n});\n","/**\n * @fileoverview Flag a literal-only constant collection (or regex) declared\n * INSIDE a function body that should be hoisted to module scope.\n *\n * Rationale — this is the single most frequent recurring review comment in the\n * mined PR corpus (~37 distinct PRs, TypeScript and Python alike): a lookup\n * table, allow-list, `Set` membership test or validation regex written at the\n * top of a function, rebuilt on every single call. Three costs, in increasing\n * order of severity:\n *\n * 1. Allocation — every call re-walks the literal and re-allocates the array /\n * object / Set / Map. In a hot path or a React render this is pure waste.\n * 2. Identity churn — a fresh object every call means a fresh reference every\n * render, which silently defeats `useMemo`/`useEffect` dependency arrays and\n * `React.memo` on anything the value is threaded into.\n * 3. Discoverability — a domain constant buried in a function body is invisible\n * to the next reader and cannot be exported, tested, or reused, so it gets\n * duplicated in the next function that needs it. That is the defect class:\n * the duplicated copies drift apart.\n *\n * This rule is deliberately NOT a duplicate of `unicorn/consistent-function-scoping`,\n * which is enabled at error in the shipped strict config. That rule reports\n * nested FUNCTION declarations that reference nothing from their enclosing\n * scope; its entire visitor surface is the function node types and it never\n * looks at a `VariableDeclarator` whose init is an array, object, `new Set`,\n * `new Map` or a regex. The two rules are disjoint by construction.\n *\n * What fires — a `const` binding inside a function whose initializer is one of:\n *\n * - an array literal, an object literal, `new Set([...])`, `new Map([...])`,\n * or `Object.freeze(...)` around any of those, with at least `minElements`\n * entries and where EVERY leaf is a literal; or\n * - a regular-expression literal without the `g`/`y` flags.\n *\n * \"Every leaf is a literal\" means string/number/boolean/null, an unsigned or\n * negated numeric literal, a template literal with no interpolation, or a\n * nested array/object of the same — no identifiers, no calls, no member\n * expressions, no spreads, no computed non-literal keys, no shorthand\n * properties. That single condition is what makes the hoist provably safe and\n * is also what eliminates the largest false-positive class outright.\n *\n * False positives handled explicitly:\n *\n * - **Closes over a parameter or an outer binding.** `new Map([[userId, 1]])`\n * or `[prefix + \"-a\"]` contains an identifier / a computed value, so it is\n * not literal-only and never fires. Hoisting those would be a compile error\n * or a behaviour change, so the literal-only gate is load-bearing, not a\n * stylistic preference.\n * - **Deliberately fresh per call because someone mutates it.** Every\n * reference to the binding must be a provably non-mutating read: a member\n * read, a call to a known non-mutating method, `for…of` iteration, or a\n * spread into a NEW literal or argument list (which copies). A call to\n * `.push`/`.set`/`.add`/`.sort`/`.splice`/…, an index or property\n * assignment, a `delete`, or an `X++` bails immediately.\n * - **Escaping value the CALLER may mutate.** Returning the binding, passing\n * it bare as an argument, aliasing it into another variable, or embedding it\n * in a returned object/array all bail. Once the value leaves the function\n * this rule cannot see what happens to it, and a shared hoisted array that a\n * caller mutates is a genuine cross-call data-corruption bug — exactly the\n * kind of defect a lint rule must not introduce. Reads from an escaping\n * CLOSURE are fine and still fire: a nested arrow that only reads the\n * constant is unaffected by hoisting.\n * - **Tiny literals where hoisting hurts readability.** A one- or two-element\n * literal reads better next to its use, so `minElements` (default 3) sets\n * the floor and can be raised per repo.\n * - **Stateful regexes.** A `/…/g` or `/…/y` regex carries `lastIndex` across\n * calls to `.test()`/`.exec()`. Hoisting one changes behaviour — the second\n * call resumes mid-string — so global and sticky regexes are never reported\n * even though they are the most expensive to recompile.\n * - **Test files.** Fixture tables inside a test body are local by design and\n * hoisting them separates the data from the assertion that explains it.\n * Exempt by default via `ignoreTestFiles`.\n * - **Generated files** (`*.gen.ts`, `**\\/generated/**`, `*.d.ts`, or a\n * `@generated` marker) opt out, matching `no-enum` and `no-zod-native-enum`.\n *\n * There is no autofix. Hoisting has to pick an insertion point and may collide\n * with an existing module-scope name; both are judgement calls that belong to a\n * human, and a wrong automated hoist is worse than the warning.\n */\n\nimport {\n ESLintUtils,\n type TSESTree,\n AST_NODE_TYPES,\n} from \"@typescript-eslint/utils\";\n\ntype MessageIds = \"hoistCollection\" | \"hoistRegex\";\n\ntype Options = readonly [\n {\n minElements?: number;\n checkRegex?: boolean;\n ignoreTestFiles?: boolean;\n }?,\n];\n\nconst DEFAULT_MIN_ELEMENTS = 3;\n\n/** How deep a nested literal may go before we stop trying to prove it static. */\nconst MAX_LITERAL_DEPTH = 4;\n\nconst IGNORE_PATTERNS: readonly RegExp[] = [\n /[\\\\/]generated[\\\\/]/,\n /\\.gen\\.tsx?$/,\n /\\.generated\\.tsx?$/,\n /\\.d\\.ts$/,\n];\n\nconst TEST_FILE_PATTERNS: readonly RegExp[] = [\n /\\.(?:test|spec)\\.[cm]?[jt]sx?$/,\n /[\\\\/]__tests__[\\\\/]/,\n /[\\\\/]__mocks__[\\\\/]/,\n /[\\\\/]tests?[\\\\/]/,\n /\\.stories\\.[cm]?[jt]sx?$/,\n];\n\n/**\n * Methods that mutate the receiver. A call to any of these on the binding is\n * proof the value is meant to be rebuilt per call.\n */\nconst MUTATING_METHODS: ReadonlySet<string> = new Set([\n // Array\n \"push\",\n \"pop\",\n \"shift\",\n \"unshift\",\n \"splice\",\n \"sort\",\n \"reverse\",\n \"fill\",\n \"copyWithin\",\n // Set / Map\n \"add\",\n \"set\",\n \"delete\",\n \"clear\",\n // Object-ish escape hatches\n \"assign\",\n]);\n\nconst FUNCTION_TYPES: ReadonlySet<AST_NODE_TYPES> = new Set([\n AST_NODE_TYPES.FunctionDeclaration,\n AST_NODE_TYPES.FunctionExpression,\n AST_NODE_TYPES.ArrowFunctionExpression,\n]);\n\nconst COLLECTION_CONSTRUCTORS: ReadonlySet<string> = new Set([\"Set\", \"Map\"]);\n\nfunction isIgnoredFile(filename: string, sourceText: string): boolean {\n if (IGNORE_PATTERNS.some((re) => re.test(filename))) {\n return true;\n }\n return /@generated\\b/.test(sourceText.slice(0, 1024));\n}\n\nfunction isTestFile(filename: string): boolean {\n return TEST_FILE_PATTERNS.some((re) => re.test(filename));\n}\n\n/** Unwraps `x as const` / `x satisfies T` down to the inner expression. */\nfunction unwrap(node: TSESTree.Node): TSESTree.Node {\n if (\n node.type === AST_NODE_TYPES.TSAsExpression ||\n node.type === AST_NODE_TYPES.TSSatisfiesExpression ||\n node.type === AST_NODE_TYPES.TSNonNullExpression\n ) {\n return unwrap(node.expression);\n }\n return node;\n}\n\nfunction isRegexLiteral(node: TSESTree.Node): node is TSESTree.RegExpLiteral {\n return (\n node.type === AST_NODE_TYPES.Literal &&\n \"regex\" in node &&\n node.regex !== undefined\n );\n}\n\n/**\n * Whether the expression is built entirely out of literals — no identifier, no\n * call, no member access, no interpolation, no spread. This is the gate that\n * makes hoisting provably safe: a literal-only value cannot capture a\n * parameter, cannot observe call-time state, and cannot have side effects.\n */\nfunction isLiteralOnly(node: TSESTree.Node, depth: number): boolean {\n if (depth > MAX_LITERAL_DEPTH) {\n return false;\n }\n const inner = unwrap(node);\n switch (inner.type) {\n case AST_NODE_TYPES.Literal: {\n return true;\n }\n case AST_NODE_TYPES.TemplateLiteral: {\n return inner.expressions.length === 0;\n }\n case AST_NODE_TYPES.UnaryExpression: {\n return (\n (inner.operator === \"-\" || inner.operator === \"+\") &&\n inner.argument.type === AST_NODE_TYPES.Literal &&\n typeof inner.argument.value === \"number\"\n );\n }\n case AST_NODE_TYPES.ArrayExpression: {\n return inner.elements.every(\n (el) =>\n el !== null &&\n el.type !== AST_NODE_TYPES.SpreadElement &&\n isLiteralOnly(el, depth + 1),\n );\n }\n case AST_NODE_TYPES.ObjectExpression: {\n return inner.properties.every((prop) => {\n if (prop.type !== AST_NODE_TYPES.Property) {\n return false;\n }\n if (prop.shorthand || prop.method || prop.kind !== \"init\") {\n return false;\n }\n if (prop.computed && prop.key.type !== AST_NODE_TYPES.Literal) {\n return false;\n }\n return isLiteralOnly(prop.value, depth + 1);\n });\n }\n default: {\n return false;\n }\n }\n}\n\n/** `Object.freeze(x)` → `x`; anything else is returned untouched. */\nfunction unwrapObjectFreeze(node: TSESTree.Node): TSESTree.Node {\n const inner = unwrap(node);\n if (\n inner.type === AST_NODE_TYPES.CallExpression &&\n inner.callee.type === AST_NODE_TYPES.MemberExpression &&\n !inner.callee.computed &&\n inner.callee.object.type === AST_NODE_TYPES.Identifier &&\n inner.callee.object.name === \"Object\" &&\n inner.callee.property.type === AST_NODE_TYPES.Identifier &&\n inner.callee.property.name === \"freeze\" &&\n inner.arguments.length === 1 &&\n inner.arguments[0] !== undefined &&\n inner.arguments[0].type !== AST_NODE_TYPES.SpreadElement\n ) {\n return unwrap(inner.arguments[0]);\n }\n return inner;\n}\n\ntype Candidate =\n | { kind: \"array\" | \"object\" | \"Set\" | \"Map\"; size: number }\n | { kind: \"regex\"; size: number };\n\n/**\n * Classifies the initializer, or returns null when it is not a hoistable\n * literal-only collection / regex.\n */\nfunction classify(init: TSESTree.Node, checkRegex: boolean): Candidate | null {\n const node = unwrapObjectFreeze(init);\n\n if (isRegexLiteral(node)) {\n if (!checkRegex) {\n return null;\n }\n // `g` and `y` carry `lastIndex` between calls — hoisting changes behaviour.\n if (/[gy]/.test(node.regex.flags)) {\n return null;\n }\n return { kind: \"regex\", size: 1 };\n }\n\n if (node.type === AST_NODE_TYPES.ArrayExpression) {\n return isLiteralOnly(node, 0)\n ? { kind: \"array\", size: node.elements.length }\n : null;\n }\n\n if (node.type === AST_NODE_TYPES.ObjectExpression) {\n return isLiteralOnly(node, 0)\n ? { kind: \"object\", size: node.properties.length }\n : null;\n }\n\n if (\n node.type === AST_NODE_TYPES.NewExpression &&\n node.callee.type === AST_NODE_TYPES.Identifier &&\n COLLECTION_CONSTRUCTORS.has(node.callee.name)\n ) {\n const arg = node.arguments[0];\n if (\n node.arguments.length !== 1 ||\n arg === undefined ||\n arg.type === AST_NODE_TYPES.SpreadElement\n ) {\n return null;\n }\n const entries = unwrap(arg);\n if (entries.type !== AST_NODE_TYPES.ArrayExpression) {\n return null;\n }\n return isLiteralOnly(entries, 0)\n ? { kind: node.callee.name === \"Set\" ? \"Set\" : \"Map\", size: entries.elements.length }\n : null;\n }\n\n return null;\n}\n\n/** The nearest enclosing function, or null when the node is at module scope. */\nfunction enclosingFunction(node: TSESTree.Node): TSESTree.Node | null {\n let current: TSESTree.Node | undefined | null = node.parent;\n while (current !== undefined && current !== null) {\n if (FUNCTION_TYPES.has(current.type)) {\n return current;\n }\n current = current.parent;\n }\n return null;\n}\n\n/**\n * Built-ins that read their argument and return a fresh value without keeping\n * or mutating the original, so passing the binding to them is still a read.\n */\nconst NON_RETAINING_BUILTINS: ReadonlyMap<string, ReadonlySet<string>> = new Map(\n [\n [\n \"Object\",\n new Set([\"keys\", \"values\", \"entries\", \"freeze\", \"fromEntries\", \"assign\"]),\n ],\n [\"Array\", new Set([\"from\", \"isArray\"])],\n [\"JSON\", new Set([\"stringify\"])],\n ],\n);\n\nfunction isNonRetainingBuiltinCall(\n node: TSESTree.CallExpression,\n argument: TSESTree.Identifier,\n): boolean {\n const callee = node.callee;\n if (\n callee.type === AST_NODE_TYPES.Identifier &&\n callee.name === \"structuredClone\"\n ) {\n return true;\n }\n if (\n callee.type !== AST_NODE_TYPES.MemberExpression ||\n callee.computed ||\n callee.object.type !== AST_NODE_TYPES.Identifier ||\n callee.property.type !== AST_NODE_TYPES.Identifier\n ) {\n return false;\n }\n const members = NON_RETAINING_BUILTINS.get(callee.object.name);\n if (members === undefined || !members.has(callee.property.name)) {\n return false;\n }\n // `Object.assign(X, src)` mutates its FIRST argument; only later positions\n // (sources) are reads.\n if (callee.object.name === \"Object\" && callee.property.name === \"assign\") {\n return node.arguments[0] !== argument;\n }\n return true;\n}\n\n/**\n * Whether this single reference to the binding is a provably non-mutating,\n * non-escaping read. Anything not on the whitelist is treated as unsafe, so an\n * unrecognised usage suppresses the report rather than risking a bad hoist.\n */\nfunction isSafeRead(identifier: TSESTree.Identifier): boolean {\n const parent = identifier.parent;\n\n if (parent.type === AST_NODE_TYPES.MemberExpression) {\n if (parent.object !== identifier) {\n // `foo[X]` — the binding is used as a key, which is a plain read.\n return true;\n }\n const grandparent = parent.parent;\n // `X.a = 1`, `X[0] = 1`, `X.a += 1`\n if (\n grandparent.type === AST_NODE_TYPES.AssignmentExpression &&\n grandparent.left === parent\n ) {\n return false;\n }\n // `X.a++`\n if (grandparent.type === AST_NODE_TYPES.UpdateExpression) {\n return false;\n }\n // `delete X.a`\n if (\n grandparent.type === AST_NODE_TYPES.UnaryExpression &&\n grandparent.operator === \"delete\"\n ) {\n return false;\n }\n // `X.push(...)`, `X.sort()`, ...\n if (\n !parent.computed &&\n parent.property.type === AST_NODE_TYPES.Identifier &&\n MUTATING_METHODS.has(parent.property.name) &&\n grandparent.type === AST_NODE_TYPES.CallExpression &&\n grandparent.callee === parent\n ) {\n return false;\n }\n // Everything else through a member expression — `X.length`, `X.includes(v)`,\n // `X[i]`, `X.get(k)`, even `arr.map(X.has)` — reads a property of the\n // binding without handing the binding itself out, so it cannot mutate it.\n return true;\n }\n\n // `for (const x of X)` — iteration is a read.\n if (\n parent.type === AST_NODE_TYPES.ForOfStatement &&\n parent.right === identifier\n ) {\n return true;\n }\n\n // `[...X]`, `{...X}`, `f(...X)` — every spread copies, so the original is\n // never handed out and hoisting stays safe.\n if (parent.type === AST_NODE_TYPES.SpreadElement) {\n return true;\n }\n\n // `\"a\" in X`, `X instanceof Y`, `X === undefined`\n if (parent.type === AST_NODE_TYPES.BinaryExpression) {\n return true;\n }\n\n // A bare argument to a built-in that is known not to retain or mutate its\n // input: `Object.entries(X)`, `Array.from(X)`, `JSON.stringify(X)`. Any other\n // call receiving the binding bare is an escape.\n if (\n parent.type === AST_NODE_TYPES.CallExpression &&\n parent.arguments.includes(identifier) &&\n isNonRetainingBuiltinCall(parent, identifier)\n ) {\n return true;\n }\n\n // `typeof X`, `!X`\n if (\n parent.type === AST_NODE_TYPES.UnaryExpression &&\n parent.operator !== \"delete\"\n ) {\n return true;\n }\n\n return false;\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"prefer-module-level-constant\",\n meta: {\n type: \"suggestion\",\n docs: {\n description:\n \"Hoist literal-only constant collections and regexes out of function bodies to module scope so they are allocated once.\",\n },\n schema: [\n {\n type: \"object\",\n additionalProperties: false,\n properties: {\n minElements: { type: \"number\", minimum: 1 },\n checkRegex: { type: \"boolean\" },\n ignoreTestFiles: { type: \"boolean\" },\n },\n },\n ],\n messages: {\n hoistCollection:\n \"`{{name}}` is a literal-only {{kind}} rebuilt on every call. Hoist it to module scope so it is allocated once and can be reused, exported, and tested.\",\n hoistRegex:\n \"`{{name}}` is a constant regex recompiled on every call. Hoist it to module scope.\",\n },\n },\n defaultOptions: [{}],\n create(context, [optionsArg]) {\n const options = optionsArg ?? {};\n const minElements = options.minElements ?? DEFAULT_MIN_ELEMENTS;\n const checkRegex = options.checkRegex ?? true;\n const ignoreTestFiles = options.ignoreTestFiles ?? true;\n\n const sourceCode = context.sourceCode;\n const filename = context.filename;\n\n if (isIgnoredFile(filename, sourceCode.getText())) {\n return {};\n }\n if (ignoreTestFiles && isTestFile(filename)) {\n return {};\n }\n\n function allReferencesAreSafeReads(\n declarator: TSESTree.VariableDeclarator,\n ): boolean {\n const variables = sourceCode.getDeclaredVariables(declarator);\n const variable = variables[0];\n if (variable === undefined) {\n return false;\n }\n for (const reference of variable.references) {\n // The initializer write itself is not a usage.\n if (reference.init === true) {\n continue;\n }\n if (reference.isWrite()) {\n return false;\n }\n // A `JSXIdentifier` reference means the constant is rendered as a JSX\n // tag name — not something this rule reasons about, so bail.\n if (reference.identifier.type !== AST_NODE_TYPES.Identifier) {\n return false;\n }\n if (!isSafeRead(reference.identifier)) {\n return false;\n }\n }\n return true;\n }\n\n return {\n VariableDeclarator(node: TSESTree.VariableDeclarator): void {\n const declaration = node.parent;\n if (\n declaration.type !== AST_NODE_TYPES.VariableDeclaration ||\n declaration.kind !== \"const\" ||\n declaration.declare === true\n ) {\n return;\n }\n if (node.id.type !== AST_NODE_TYPES.Identifier || node.init === null) {\n return;\n }\n if (enclosingFunction(node) === null) {\n return;\n }\n\n const candidate = classify(node.init, checkRegex);\n if (candidate === null) {\n return;\n }\n if (candidate.kind !== \"regex\" && candidate.size < minElements) {\n return;\n }\n if (!allReferencesAreSafeReads(node)) {\n return;\n }\n\n context.report({\n node: node.id,\n messageId:\n candidate.kind === \"regex\" ? \"hoistRegex\" : \"hoistCollection\",\n data: { name: node.id.name, kind: candidate.kind },\n });\n },\n };\n },\n});\n","/**\n * @fileoverview Flag a JSDoc block whose every word is already in the signature\n * it documents.\n *\n * /** Get the user by id. *\\/\n * export function getUserById(id: string): User { … }\n *\n * The block costs three lines and a review, survives every rename that matters,\n * and tells the reader nothing. Delete it, or say what the caller cannot read\n * off the name: what it throws, what it assumes, why it exists.\n *\n * **The generated-file sniff is mandatory, not a nicety.** In the 817-block\n * hand-written JSDoc corpus, 87% of the raw hits for this shape came from\n * OpenAPI codegen output, where every `@param id The id.` is emitted by a\n * template and rewritten on the next `openapi-generator` run. Editing them is\n * work that gets reverted; `isGeneratedFile` (path AND header marker) is what\n * takes the count from \"hundreds, mostly noise\" to a readable handful.\n *\n * **Never flagged**: a block carrying a value tag (`@deprecated`, `@see`,\n * `@example`, `@throws`, `@template`, `@remarks`, `@since`, …) — those are the\n * content the signature cannot hold; a block carrying any tag this rule does not\n * model, because it cannot judge what it cannot read; a `@param` or `@returns`\n * description that adds a word of its own; and anything in the nine-signal\n * protected class from `_comments`.\n *\n * **Autofix is deliberately a SUGGESTION, never `--fix`.** Deleting a doc block\n * in bulk is silent information loss if the judgement is wrong once; a\n * suggestion makes a human accept each one.\n *\n * **Measured.** 40 hits across the maintained repos (noura-be 11, demo-gateway\n * 29, bulbul / portal / automations 0) and 5 across zod / swr / TanStack Query\n * (zustand 0). All 45 were read; the only debatable one is zod's\n * `/** The input data *\\/` on `readonly input: unknown`, which is a published\n * API doc that nonetheless says nothing the field does not. Everything else is\n * `/** Logout function *\\/` on `useLogout`, `/** Base64 encode a string *\\/` on\n * `base64Encode(str)`, `/** A list row. *\\/` on `ListRow`.\n */\n\nimport { AST_NODE_TYPES, ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\nimport { isProtected, splitIdentifier, stem } from \"./_comments.js\";\nimport { isGeneratedFile } from \"./_paths.js\";\n\ntype MessageIds = \"restatesSignature\" | \"deleteBlock\";\ntype Options = readonly [];\n\n// Tags that carry something the signature cannot. Any of them and the block stays.\nconst VALUE_TAGS: ReadonlySet<string> = new Set([\n \"alpha\", \"author\", \"beta\", \"category\", \"copyright\", \"default\", \"defaultvalue\",\n \"deprecated\", \"example\", \"experimental\", \"fileoverview\", \"fixme\", \"group\",\n \"inheritdoc\", \"internal\", \"license\", \"link\", \"module\", \"override\",\n \"packagedocumentation\", \"remarks\", \"see\", \"since\", \"template\", \"throws\",\n \"todo\", \"typeparam\",\n]);\n\n// Tags this rule models and can therefore judge. Anything else means the block\n// is doing something the rule does not understand, so it is left alone.\nconst MODELLED_TAGS: ReadonlySet<string> = new Set([\n \"arg\", \"argument\", \"async\", \"description\", \"param\", \"return\", \"returns\",\n]);\n\nconst PARAM_TAGS: ReadonlySet<string> = new Set([\"arg\", \"argument\", \"param\"]);\nconst RETURN_TAGS: ReadonlySet<string> = new Set([\"return\", \"returns\"]);\n\nconst DIRECTIVE_RE = /^\\s*(?:eslint\\b|eslint-|@ts-|prettier|biome-|c8\\b|v8\\b|istanbul\\b|@vite|webpack|@jsx|@jest-environment|@vitest-environment|#__)/i;\n\n// Prose filler that says nothing about *which* thing is being described. Wider\n// than the comment tokenizer's list because JSDoc conventionally repeats the\n// vocabulary of the type system itself (\"the callback prop\", \"an optional\n// string\") without that being a claim about this declaration.\nconst STOPWORDS: ReadonlySet<string> = new Set(\n `the a an of to for in on with and or as at by is are was be been being\n this that it its if whether when where which what will would can could should\n must may into from over about not no does do done has have had used use uses\n using given provided specified current new existing all any each per via based\n function method component hook class instance object value values data item\n items element callback handler prop props param parameter argument arg return\n returns returning result optional required default true false null undefined\n string number boolean array list promise`.split(/\\s+/),\n);\n\nconst WORD_RE = /[A-Za-z]+/g;\n\ninterface JsDocTag {\n readonly name: string;\n readonly text: string;\n}\n\n/** Split a JSDoc block into its free description and its tags. */\nfunction parseJsDoc(value: string): { description: string; tags: JsDocTag[] } {\n const lines = value.replace(/^\\*/, \"\").split(\"\\n\").map((line) => line.replace(/^\\s*\\*?\\s?/, \"\"));\n const description: string[] = [];\n const tags: { name: string; text: string }[] = [];\n let current: { name: string; text: string } | null = null;\n for (const line of lines) {\n const match = /^\\s*@(\\w[\\w-]*)\\s*(.*)$/.exec(line);\n if (match) {\n current = { name: (match[1] ?? \"\").toLowerCase(), text: match[2] ?? \"\" };\n tags.push(current);\n } else if (current !== null) {\n current.text += ` ${line.trim()}`;\n } else {\n description.push(line);\n }\n }\n return { description: description.join(\"\\n\").trim(), tags };\n}\n\n/** Every content word of `text`, lowercased, with filler dropped. */\nfunction proseTokens(text: string): string[] {\n return (text.match(WORD_RE) ?? [])\n .map((word) => word.toLowerCase())\n .filter((word) => word.length > 1 && !STOPWORDS.has(word));\n}\n\n/** True when every content word of `text` already appears in `known`. */\nfunction covered(text: string, known: ReadonlySet<string>): boolean {\n const stems = new Set<string>();\n for (const token of known) stems.add(stem(token));\n return proseTokens(text).every((word) => known.has(word) || stems.has(stem(word)));\n}\n\n/** The declared name and parameter names of the node a JSDoc block sits above. */\nfunction declarationNames(node: TSESTree.Node): { name: string; params: string[] } | null {\n switch (node.type) {\n // `export function f()` — the JSDoc sits above the `export`, so the token\n // after it resolves to the wrapper, not to the thing being documented.\n case AST_NODE_TYPES.ExportNamedDeclaration:\n case AST_NODE_TYPES.ExportDefaultDeclaration:\n return node.declaration == null ? null : declarationNames(node.declaration);\n case AST_NODE_TYPES.FunctionDeclaration:\n case AST_NODE_TYPES.TSDeclareFunction:\n return node.id === null ? null : { name: node.id.name, params: paramNames(node.params) };\n case AST_NODE_TYPES.ClassDeclaration:\n case AST_NODE_TYPES.TSInterfaceDeclaration:\n case AST_NODE_TYPES.TSTypeAliasDeclaration:\n case AST_NODE_TYPES.TSEnumDeclaration:\n return node.id === null ? null : { name: node.id.name, params: [] };\n case AST_NODE_TYPES.VariableDeclaration: {\n const declarator = node.declarations[0];\n if (declarator === undefined || declarator.id.type !== AST_NODE_TYPES.Identifier) return null;\n const init = declarator.init;\n const params =\n init != null &&\n (init.type === AST_NODE_TYPES.ArrowFunctionExpression ||\n init.type === AST_NODE_TYPES.FunctionExpression)\n ? paramNames(init.params)\n : [];\n return { name: declarator.id.name, params };\n }\n case AST_NODE_TYPES.MethodDefinition:\n case AST_NODE_TYPES.PropertyDefinition:\n case AST_NODE_TYPES.TSMethodSignature:\n case AST_NODE_TYPES.TSPropertySignature: {\n if (node.key.type !== AST_NODE_TYPES.Identifier) return null;\n const params =\n node.type === AST_NODE_TYPES.MethodDefinition\n ? paramNames(node.value.params)\n : node.type === AST_NODE_TYPES.TSMethodSignature\n ? paramNames(node.params)\n : [];\n return { name: node.key.name, params };\n }\n default:\n return null;\n }\n}\n\nfunction paramNames(params: readonly TSESTree.Parameter[]): string[] {\n const names: string[] = [];\n for (const param of params) {\n const target = param.type === AST_NODE_TYPES.AssignmentPattern ? param.left : param;\n if (target.type === AST_NODE_TYPES.Identifier) names.push(target.name);\n else if (target.type === AST_NODE_TYPES.TSParameterProperty) continue;\n }\n return names;\n}\n\nfunction tokensOf(names: readonly string[]): Set<string> {\n const tokens = new Set<string>();\n for (const name of names) for (const part of splitIdentifier(name)) tokens.add(part);\n return tokens;\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"jsdoc-restates-signature\",\n meta: {\n type: \"suggestion\",\n hasSuggestions: true,\n docs: {\n description:\n \"Flag a JSDoc block whose description and tags only re-spell the signature they document.\",\n },\n schema: [],\n messages: {\n restatesSignature:\n \"JSDoc only re-spells the signature — delete it, or say what the caller cannot read off the name (what it throws, what it assumes, why it exists).\",\n deleteBlock: \"Delete the JSDoc block.\",\n },\n },\n defaultOptions: [],\n create(context) {\n if (isGeneratedFile(context.filename, context.sourceCode.text)) {\n return {};\n }\n const sourceCode = context.sourceCode;\n\n return {\n Program(): void {\n for (const comment of sourceCode.getAllComments()) {\n if (comment.type !== \"Block\" || !comment.value.startsWith(\"*\")) continue;\n const { description, tags } = parseJsDoc(comment.value);\n if (DIRECTIVE_RE.test(description)) continue;\n const tagNames = new Set(tags.map((tag) => tag.name));\n if ([...tagNames].some((name) => VALUE_TAGS.has(name))) continue;\n if ([...tagNames].some((name) => !MODELLED_TAGS.has(name))) continue;\n if (isProtected(description)) continue;\n\n const token = sourceCode.getTokenAfter(comment, { includeComments: false });\n if (token === null || token.loc.start.line !== comment.loc.end.line + 1) continue;\n let node = sourceCode.getNodeByRangeIndex(token.range[0]);\n let declaration: { name: string; params: string[] } | null = null;\n while (node != null && node.type !== AST_NODE_TYPES.Program) {\n declaration = declarationNames(node);\n if (declaration !== null) break;\n node = node.parent ?? null;\n }\n if (declaration === null) continue;\n\n const paramTags = tags.filter((tag) => PARAM_TAGS.has(tag.name));\n const returnTags = tags.filter((tag) => RETURN_TAGS.has(tag.name));\n if (description.length === 0 && paramTags.length === 0 && returnTags.length === 0) {\n continue;\n }\n\n const nameTokens = tokensOf([declaration.name]);\n const paramTokens = tokensOf(declaration.params);\n const known = new Set([...nameTokens, ...paramTokens]);\n\n let addsNothing = covered(description, known);\n for (const tag of paramTags) {\n const text = tag.text.replace(/^\\{[^}]*\\}\\s*/, \"\");\n const match = /^\\[?([A-Za-z_$][\\w.$]*)\\]?\\s*-?\\s*([\\s\\S]*)$/.exec(text);\n if (match === null) {\n addsNothing = false;\n break;\n }\n const own = new Set([...splitIdentifier(match[1]?.split(\".\").pop() ?? \"\"), ...nameTokens]);\n if (!covered(match[2] ?? \"\", own)) {\n addsNothing = false;\n break;\n }\n for (const part of splitIdentifier(match[1]?.split(\".\").pop() ?? \"\")) known.add(part);\n }\n if (addsNothing) {\n for (const tag of returnTags) {\n if (!covered(tag.text.replace(/^\\{[^}]*\\}\\s*/, \"\"), known)) {\n addsNothing = false;\n break;\n }\n }\n }\n if (!addsNothing) continue;\n\n context.report({\n node: comment,\n messageId: \"restatesSignature\",\n suggest: [\n {\n messageId: \"deleteBlock\",\n fix: (fixer) => fixer.removeRange([comment.range[0], token.range[0]]),\n },\n ],\n });\n }\n },\n };\n },\n});\n","/**\n * @fileoverview Flag a one-line comment that only re-spells the statement\n * beneath it — the TypeScript twin of Python's SARJ049.\n *\n * // Login schemas\n * export const ZLoginResponseSchema = z.object({ … });\n *\n * Every content word of the comment is already an identifier on the line below.\n * It cannot go out of date usefully, only silently, and a reader who scans it\n * learns nothing the code did not already say. Delete it, or replace it with the\n * *why*.\n *\n * **Division of labour with `no-comment-cruft`.** That rule already reports the\n * VERB-LED shape (`// increment the counter` above `counter += 1`), corroborated\n * against the statement *head*. This rule defers to it — `restatesStatementHead`\n * is imported and used as an exemption — so a comment is never reported twice.\n * What is left for this rule is the noun-phrase label whose every word appears\n * anywhere on the line: `// VB Environment badge`, `// OTP schemas`,\n * `// Language enum`.\n *\n * **What makes it safe.** The first attempt at this shape (PR #98) corroborated\n * by substring — `service` matched `locationService` — and produced 933 hits at\n * a ~60% false-positive rate. Coincidental token overlap is the failure mode, so\n * every guard below is load-bearing: zero information (EVERY content token must\n * appear, exact or stemmed, never by prefix); at least two content tokens (one\n * word labels a thing, it does not restate a statement); a single-line comment\n * (a `//` run is a paragraph); a single-line, value-producing statement (a\n * comment above a block labels a region); the statement must invoke something\n * (a comment above a plain data declaration is a group label — that one shape\n * was every false positive left in the Python corpus sweep); and the whole\n * nine-signal protected class from `_comments` is exempt.\n *\n * **Measured.** 1 hit across the maintained repos (bulbul 0, noura-be 0,\n * portal 0, automations 0, demo-gateway 1) and 8 across zod / swr / TanStack\n * Query, of which one — zod's `// no issues with confirmPassword or password`\n * over `return payload.issues.every(…)` — was the last false positive and is\n * now guarded by `NEGATION_WORD_RE`. The single-line-statement requirement is\n * what keeps the sarj count at zero: noura-be's `// VB Environment badge` sits\n * over a multi-line `const renderVBEnvironmentBadge = () => (`, i.e. it labels\n * a REGION, so this rule leaves it to `no-comment-cruft`'s section-label check.\n * That makes it a preventive ratchet on TypeScript with essentially no\n * migration cost, unlike its Python twin (SARJ049, 29 hits in noura-be).\n */\n\nimport { AST_NODE_TYPES, ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\nimport {\n codeTokens,\n contentTokens,\n isProtected,\n restatableStatementBelow,\n restates,\n restatesStatementHead,\n} from \"./_comments.js\";\nimport { isGeneratedFile } from \"./_paths.js\";\n\ntype MessageIds = \"restatesLineBelow\";\ntype Options = readonly [];\n\nconst MAX_WORDS = 8;\nconst MIN_CONTENT_TOKENS = 2;\n\nconst DIRECTIVE_RE =\n /^(eslint\\b|eslint-|sarj-noqa\\b|@ts-|prettier-ignore|prettier\\b|biome-|c8\\b|v8\\b|istanbul\\b|@type\\b|@vite|webpack|<reference|<amd|global\\b|noinspection|todo\\b|fixme\\b|hack\\b|xxx\\b)/i;\n\n// Commented-out code and section banners belong to `no-comment-cruft`.\nconst CODEY_RE = /^[\\w.$[\\]'\"]+\\s*[:=]\\s*\\S|^[\\w.$]+\\s*\\(|^(?:return|throw|await|import|export|const|let|var)\\b.*[=()[\\]{}]/;\nconst BANNERISH_RE = /[=\\-─-╿*#~_.]{3,}|^[A-Z0-9 _:-]+$/;\n\n// Four shapes that pass the zero-information test and were wrong every time in\n// the famous-corpus sweep: modality (`can`, `should`, `must` state a\n// possibility or an obligation, which no arrangement of identifiers can say), a\n// colon-terminated lead-in (it announces what follows rather than describing the\n// line under it), inline emphasis (someone who wrote `*not*` or backticked an\n// identifier was making a point about it), and a bare negation — `no`/`not`/\n// `never` are stopwords for the tokenizer, so a comment stating a NEGATIVE\n// property passes against the positive spelling below it (`// no issues with\n// confirmPassword or password` over `return payload.issues.every(…)`,\n// zod/packages/zod/src/v4/classic/tests/refine.test.ts:546). Saying what is\n// absent is the one thing the code's own words cannot.\nconst MODALITY_RE = /\\b(?:can|could|should|shall|may|might|must|will|would|cannot)\\b/i;\nconst LEAD_IN_RE = /:$/;\nconst EMPHASIS_RE = /\\*\\w[^*]*\\*|`[^`]+`/;\nconst NEGATION_WORD_RE = /\\b(?:no|not|never|neither|nor|without|none|non)\\b/i;\n\n// The statement must *do* something. A comment above a plain data declaration\n// labels the datum, and that shape is a series of group labels rather than\n// narration — it was every false positive left in the Python corpus sweep.\nconst ACTION_STMT_RE = /[\\w.$\\])]\\s*\\(|^\\s*(?:return|throw|await|yield)\\b/;\n\nconst NON_ASCII_LETTER_RE = /[^\\p{ASCII}\\p{N}\\p{P}\\p{Z}]/u;\n\n/** True when `a` and `b` are `//` comments on consecutive lines. */\nfunction areAdjacentLineComments(\n a: TSESTree.Comment | undefined,\n b: TSESTree.Comment | undefined,\n): boolean {\n return (\n a !== undefined &&\n b !== undefined &&\n a.type === \"Line\" &&\n b.type === \"Line\" &&\n b.loc.start.line === a.loc.end.line + 1\n );\n}\n\n/**\n * True when the statement below the comment heads a run of siblings of the same\n * kind — `// register secrets` over eight `registerSecret(…)` calls. The label\n * provides the grouping, and deleting it loses that.\n */\nfunction headsSiblingRun(node: TSESTree.Node): boolean {\n const parent = node.parent;\n if (parent === undefined) return false;\n const body: readonly TSESTree.Node[] | undefined =\n \"body\" in parent && Array.isArray(parent.body)\n ? (parent.body as readonly TSESTree.Node[])\n : undefined;\n if (body === undefined) return false;\n const index = body.indexOf(node);\n const next = index >= 0 ? body[index + 1] : undefined;\n return next !== undefined && next.type === node.type;\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-restated-comment\",\n meta: {\n type: \"suggestion\",\n docs: {\n description:\n \"Flag a single-line comment whose every word already appears on the statement below it.\",\n },\n schema: [],\n messages: {\n restatesLineBelow:\n \"Comment restates the statement below it — delete it, or replace it with the *why*; the code already carries the *what*.\",\n },\n },\n defaultOptions: [],\n create(context) {\n if (isGeneratedFile(context.filename, context.sourceCode.text)) {\n return {};\n }\n const sourceCode = context.sourceCode;\n const lines = sourceCode.lines;\n\n function isStandalone(comment: TSESTree.Comment): boolean {\n const before = sourceCode.getTokenBefore(comment, { includeComments: false });\n return !before || before.loc.end.line < comment.loc.start.line;\n }\n\n function labelsASiblingRun(comment: TSESTree.Comment): boolean {\n const token = sourceCode.getTokenAfter(comment, { includeComments: false });\n if (token === null) return false;\n // Walk out to the STATEMENT, not merely to the first node starting on that\n // line. Stopping early looked at the innermost `Identifier`, whose parent\n // is an expression rather than a block, so the sibling test silently never\n // ran — zod's `assignability.test.ts` alone contributed 89 hits, a table\n // of one-line `z.string() satisfies z.core.$ZodString;` assertions each\n // labelled with the type it checks.\n for (\n let node: TSESTree.Node | undefined | null = sourceCode.getNodeByRangeIndex(token.range[0]);\n node != null && node.type !== AST_NODE_TYPES.Program;\n node = node.parent\n ) {\n if (headsSiblingRun(node)) return true;\n }\n return false;\n }\n\n return {\n Program(): void {\n const comments = sourceCode.getAllComments();\n for (let i = 0; i < comments.length; i++) {\n const comment = comments[i];\n if (comment === undefined || comment.type !== \"Line\") continue;\n if (!isStandalone(comment)) continue;\n if (\n areAdjacentLineComments(comments[i - 1], comment) ||\n areAdjacentLineComments(comment, comments[i + 1])\n ) {\n continue; // one line of a paragraph, not a label for the next statement\n }\n const body = comment.value.replace(/^\\/*/, \"\").trim();\n if (body.length === 0 || body.endsWith(\"?\")) continue;\n if (DIRECTIVE_RE.test(body) || CODEY_RE.test(body) || BANNERISH_RE.test(body)) continue;\n if (NON_ASCII_LETTER_RE.test(body) || isProtected(body)) continue;\n if (MODALITY_RE.test(body) || LEAD_IN_RE.test(body) || EMPHASIS_RE.test(body)) continue;\n if (NEGATION_WORD_RE.test(body)) continue;\n if (body.split(/\\s+/).length > MAX_WORDS) continue;\n\n const tokens = contentTokens(body);\n if (tokens.length < MIN_CONTENT_TOKENS) continue;\n\n const statement = restatableStatementBelow(comment, sourceCode);\n if (statement === null) continue;\n if (restatesStatementHead(body, statement)) continue; // `no-comment-cruft` owns it\n\n const line = lines[comment.loc.start.line] ?? \"\";\n if (!ACTION_STMT_RE.test(line)) continue;\n\n if (labelsASiblingRun(comment)) continue;\n\n if (restates(tokens, codeTokens(line))) {\n context.report({ node: comment, messageId: \"restatesLineBelow\" });\n }\n }\n },\n };\n },\n});\n","/**\n * @fileoverview Flag a trailing comment that spells out a literal already on the\n * line — the TypeScript twin of Python's SARJ051.\n *\n * staleTime: 5 * 60 * 1000, // 5 minutes\n *\n * The comment adds one thing — the unit — and the line already carries every\n * number in it. Put the unit in the *name* (`STALE_TIME_MS`, a `Duration`\n * helper) and the fact travels with the value: it survives a copy-paste, it is\n * visible at every call site, and it cannot drift when someone edits the\n * arithmetic and forgets the comment. That drift is the whole risk — a wrong\n * unit comment is worse than none.\n *\n * **The test is deliberately narrow.** Every one of these must hold: the code\n * before the comment contains a numeric literal; the comment contains at least\n * one number and EVERY number it contains appears verbatim in that code; and\n * every non-numeric word is either a unit word or already an identifier on the\n * line. So `// 5 minutes` over `5 * 60 * 1000` fires, while `// ~3.5 days` over\n * `300000` — a conversion the reader cannot do in their head — does not, and\n * neither does `// doubles per attempt, capped by the gateway`. A comment\n * carrying a ticket or URL is exempt (protected-class signal S1).\n *\n * **Measured.** 18 hits across the nine-repo corpus, 18 of 18 true positives.\n * They cluster hard: `noura-be/typescript/dashboard-fe/src/hooks/use-analytics.ts`\n * alone holds 12 `staleTime` lines, `lib/query-client.ts` two more, and\n * `automations/apps/sarj-ai-worker/src/config.ts:3` carries\n * `export const COOKIE_MAX_AGE_SECONDS = 60 * 60 * 24 * 90; // 90 days`.\n */\n\nimport { ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\nimport { codeTokens, hasExternalReference, stem } from \"./_comments.js\";\nimport { isGeneratedFile } from \"./_paths.js\";\n\ntype MessageIds = \"narratesValue\";\ntype Options = readonly [];\n\n// A number that is not part of an identifier or a dotted member path.\nconst NUMBER_RE = /(?<![\\w.])(\\d+(?:\\.\\d+)?)(?![\\w.])/g;\nconst WORD_RE = /[A-Za-z]+(?:'[a-z]+)?|\\d+(?:\\.\\d+)?/g;\n\n// Words that name the unit rather than the quantity — the one thing the code\n// does not say, and the reason the fix is a *name*, not a deletion.\nconst UNIT_WORDS: ReadonlySet<string> = new Set([\n \"bytes\", \"characters\", \"chars\", \"day\", \"days\", \"gb\", \"hour\", \"hours\", \"hr\",\n \"hrs\", \"hz\", \"items\", \"k\", \"kb\", \"khz\", \"m\", \"mb\", \"milliseconds\", \"min\",\n \"mins\", \"minute\", \"minutes\", \"ms\", \"pct\", \"percent\", \"px\", \"retries\", \"rows\",\n \"s\", \"sec\", \"second\", \"seconds\", \"secs\", \"times\", \"tokens\",\n]);\n\nconst STOPWORDS: ReadonlySet<string> = new Set([\n \"a\", \"an\", \"and\", \"are\", \"as\", \"at\", \"be\", \"by\", \"for\", \"in\", \"is\", \"it\",\n \"of\", \"on\", \"or\", \"that\", \"the\", \"this\", \"we\", \"with\",\n]);\n\nconst DIRECTIVE_RE =\n /^\\s*(?:eslint\\b|eslint-|sarj-noqa\\b|@ts-|prettier|biome-|c8\\b|v8\\b|istanbul\\b|todo\\b|fixme\\b|hack\\b|xxx\\b)/i;\n\nfunction numbersIn(text: string): Set<string> {\n return new Set(text.match(NUMBER_RE) ?? []);\n}\n\nfunction narratesValue(body: string, code: string): boolean {\n if (body.length === 0 || DIRECTIVE_RE.test(body) || hasExternalReference(body)) return false;\n const codeNumbers = numbersIn(code);\n if (codeNumbers.size === 0) return false;\n const words = (body.match(WORD_RE) ?? []).map((word) => word.toLowerCase());\n if (words.length === 0) return false;\n const commentNumbers = numbersIn(body);\n if (commentNumbers.size === 0) return false;\n for (const number of commentNumbers) {\n if (!codeNumbers.has(number)) return false;\n }\n const identifiers = codeTokens(code);\n const stems = new Set<string>();\n for (const token of identifiers) stems.add(stem(token));\n return words.every(\n (word) =>\n STOPWORDS.has(word) ||\n UNIT_WORDS.has(word) ||\n commentNumbers.has(word) ||\n identifiers.has(word) ||\n stems.has(stem(word)),\n );\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"trailing-value-narration\",\n meta: {\n type: \"suggestion\",\n docs: {\n description:\n \"Flag a trailing comment whose every word and number is already on the line it annotates.\",\n },\n schema: [],\n messages: {\n narratesValue:\n \"Trailing comment restates the literal on this line — put the unit in the name (STALE_TIME_MS) so it cannot drift.\",\n },\n },\n defaultOptions: [],\n create(context) {\n if (isGeneratedFile(context.filename, context.sourceCode.text)) {\n return {};\n }\n const sourceCode = context.sourceCode;\n\n function isTrailing(comment: TSESTree.Comment): boolean {\n const before = sourceCode.getTokenBefore(comment, { includeComments: false });\n return before !== null && before.loc.end.line === comment.loc.start.line;\n }\n\n return {\n Program(): void {\n for (const comment of sourceCode.getAllComments()) {\n if (!isTrailing(comment)) continue;\n const line = sourceCode.lines[comment.loc.start.line - 1] ?? \"\";\n const code = line.slice(0, comment.loc.start.column);\n const body = comment.value.replace(/^\\*+/, \"\").replace(/\\*+$/, \"\").trim();\n if (narratesValue(body, code)) {\n context.report({ node: comment, messageId: \"narratesValue\" });\n }\n }\n },\n };\n },\n});\n","/**\n * @fileoverview TS side of SARJ057 (`no-tautological-expect`). An `expect(...)`\n * whose operands are all literals has already decided its outcome before the\n * test runs: `expect(true).toBe(true)` passes if you delete the entire module\n * under test. It is not a weak assertion, it is a *non*-assertion.\n *\n * This is the placeholder that never got replaced. Every hit found in the\n * internal sweep is one:\n *\n * - `apps/internal-automations/test/customer-bridge-disambiguation-handler.test.ts:37`\n * — `expect(true).toBe(true); // placeholder`, in a suite whose name promises\n * it disambiguates customer bridges,\n * - `packages/precedent-node/src/tests/dummy.test.ts:5` — `expect(true).toBe(true)`,\n * - `packages/precedent-iso/src/tests/dummy.test.ts:4` — `expect(1).toEqual(1)`.\n *\n * The Python side (SARJ043 `zero-assertion-test`) has caught the assertion-free\n * version of this since 0.15.0 and has no TypeScript counterpart, which is\n * precisely why the `automations` placeholder went uncaught for as long as it\n * did: the file *has* an assertion, so nothing was looking at it.\n *\n * Fires on exactly two shapes:\n *\n * 1. `expect(<literal>).toBe|toEqual|toStrictEqual(<textually identical literal>)`;\n * 2. `expect(<literal>).<zero-argument matcher>()` — `toBeDefined`,\n * `toBeTruthy`, `toBeNull`, `toBeUndefined`, `toBeFalsy`, `toBeNaN`. With a\n * literal receiver the answer is fixed at parse time either way.\n *\n * **The narrowness is the rule.** The obvious generalisation — \"flag a\n * comparison of a thing with itself\" — was measured across five repositories\n * and is ~95% false positives. `expect(hash([o])).toEqual(hash([o]))` is a\n * *determinism* test; `expect(memo(x)).toBe(memo(x))` is a *memoization* test;\n * `expect(a).toEqual(a)` on a value with a custom equality is a *reflexivity*\n * test. All three can genuinely fail, and all three are correct code. So an\n * operand that is an identifier, a member expression or a call is never enough:\n * both sides must be literals, and textually identical ones.\n *\n * Deliberately NOT flagged:\n *\n * - any modified chain — `expect(x).not.toBe(...)`, `.resolves`, `.rejects`.\n * The matcher must hang directly off the `expect(...)` call, which keeps the\n * rule to the shape it can reason about;\n * - a literal compared with a *different* literal (`expect(1).toBe(2)`) — that\n * assertion always fails, which is loud on the first run rather than silent;\n * - a spread element anywhere in an array/object literal (`expect([...xs])`),\n * whose contents come from a runtime value;\n * - a template literal with interpolations, for the same reason;\n * - anything outside a test file.\n *\n * Measured before shipping: 3 hits across 5,819 `.ts`/`.tsx` files (1,003 of\n * them test files, where the rule is active) — bulbul, noura-be, kpi-hub, ai,\n * automations and demo-gateway plus got / hono / swr / trpc. 3 true positives,\n * 0 false positives; all three are the placeholders listed above.\n */\n\nimport { AST_NODE_TYPES, ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\nimport { isTestFile } from \"./_paths.js\";\n\ntype MessageIds = \"tautologicalComparison\" | \"tautologicalMatcher\";\ntype Options = readonly [];\n\n/** Matchers that take the expected value as their single argument. */\nconst EQUALITY_MATCHERS: ReadonlySet<string> = new Set([\"toBe\", \"toEqual\", \"toStrictEqual\"]);\n\n/** Matchers that take no argument, so the receiver alone fixes the outcome. */\nconst ZERO_ARG_MATCHERS: ReadonlySet<string> = new Set([\n \"toBeDefined\",\n \"toBeUndefined\",\n \"toBeNull\",\n \"toBeTruthy\",\n \"toBeFalsy\",\n \"toBeNaN\",\n]);\n\n/** Enough of the operand to identify it in the message without pasting a screenful. */\nconst OPERAND_PREVIEW_CHARS = 40;\n\n/** Sign prefixes: `-1` is a unary expression, not a literal, but it is constant. */\nconst NUMERIC_SIGNS: ReadonlySet<string> = new Set([\"-\", \"+\"]);\n\n/**\n * True when `node` is written entirely out of literals — no identifier, member\n * expression, call or spread anywhere inside it. That exclusion is the whole\n * false-positive guard: a value the code produced is what makes an assertion an\n * assertion.\n */\nfunction isLiteral(node: TSESTree.Node): boolean {\n switch (node.type) {\n case AST_NODE_TYPES.Literal:\n return true;\n case AST_NODE_TYPES.TemplateLiteral:\n return node.expressions.length === 0;\n case AST_NODE_TYPES.UnaryExpression:\n return NUMERIC_SIGNS.has(node.operator) && isLiteral(node.argument);\n case AST_NODE_TYPES.ArrayExpression:\n return node.elements.every((element) => element !== null && isLiteral(element));\n case AST_NODE_TYPES.ObjectExpression:\n return node.properties.every(\n (property) =>\n property.type === AST_NODE_TYPES.Property &&\n !property.computed &&\n isLiteral(property.value),\n );\n default:\n return false;\n }\n}\n\n/** The `expect(<single argument>)` call a matcher hangs directly off, if any. */\nfunction expectOperand(callee: TSESTree.MemberExpression): TSESTree.Node | null {\n const receiver = callee.object;\n if (\n receiver.type !== AST_NODE_TYPES.CallExpression ||\n receiver.callee.type !== AST_NODE_TYPES.Identifier ||\n receiver.callee.name !== \"expect\" ||\n receiver.arguments.length !== 1\n ) {\n return null;\n }\n return receiver.arguments[0] ?? null;\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-tautological-expect\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Disallow an assertion whose operands are all literals; its outcome is fixed before the code runs, so it can never fail.\",\n },\n schema: [],\n messages: {\n tautologicalComparison:\n \"`expect({{operand}}).{{matcher}}({{operand}})` compares a literal with an identical literal — it passes even if the code under test is deleted. Assert on a value the code produced, or delete the test.\",\n tautologicalMatcher:\n \"`expect({{operand}}).{{matcher}}()` asserts on a literal, so its outcome is fixed before the code runs. Assert on a value the code produced, or delete the test.\",\n },\n },\n defaultOptions: [],\n create(context) {\n if (!isTestFile(context.filename)) {\n return {};\n }\n /** The operand as written, collapsed to one line and elided for the message. */\n const preview = (node: TSESTree.Node): string => {\n const text = context.sourceCode.getText(node).replaceAll(/\\s+/gu, \" \");\n return text.length > OPERAND_PREVIEW_CHARS\n ? `${text.slice(0, OPERAND_PREVIEW_CHARS)}…`\n : text;\n };\n return {\n CallExpression(node: TSESTree.CallExpression): void {\n const callee = node.callee;\n if (callee.type !== AST_NODE_TYPES.MemberExpression || callee.computed) {\n return;\n }\n if (callee.property.type !== AST_NODE_TYPES.Identifier) {\n return;\n }\n const matcher = callee.property.name;\n const operand = expectOperand(callee);\n if (operand === null || !isLiteral(operand)) {\n return;\n }\n if (ZERO_ARG_MATCHERS.has(matcher) && node.arguments.length === 0) {\n context.report({\n node,\n messageId: \"tautologicalMatcher\",\n data: { operand: preview(operand), matcher },\n });\n return;\n }\n const expected = node.arguments[0];\n if (\n !EQUALITY_MATCHERS.has(matcher) ||\n node.arguments.length !== 1 ||\n expected === undefined ||\n !isLiteral(expected)\n ) {\n return;\n }\n // Textual identity, not structural: `expect(1).toBe(1.0)` is a\n // deliberate statement about representation and is left alone.\n if (context.sourceCode.getText(operand) !== context.sourceCode.getText(expected)) {\n return;\n }\n context.report({\n node,\n messageId: \"tautologicalComparison\",\n data: { operand: preview(operand), matcher },\n });\n },\n };\n },\n});\n","/**\n * @fileoverview An exported concrete class that takes injected collaborators and\n * has no interface above it is not substitutable. Every consumer must import and\n * depend on the concrete class, so the only way to test a consumer is to mock the\n * class — the exact failure the Python side of this standard attacks with\n * `prefer-real-store-in-tests` / `prefer-library-fake`. An interface (a port),\n * even a two-method one, lets a consumer's test pass a real alternative\n * implementation instead of a mock.\n *\n * The rule fires on an EXPORTED, non-abstract class declaration that\n * (a) has no `implements` clause and does not `extend` anything,\n * (b) stores >=1 constructor parameter whose type is a bare type reference\n * (not a primitive, literal, inline object type, union, or function type)\n * and is not a config/options/callback bag, and\n * (c) declares >=1 public instance method (a class with only a constructor,\n * fields, or getters is a value object / DTO, not a service).\n *\n * CORPUS SWEEP (2026-07, re-measured over 18 repos): bulbul/typescript,\n * automations, portal, demo-gateway, sarj-demos, noura-be, kpi-hub, najm,\n * kashta, summer, hala, ai-canvas-health, farwa, tamr, money2020, banking-demo,\n * pericles, digital-bank. 286 exported class declarations in non-test /\n * non-generated files; 216 already carry an `implements` clause, extend a base\n * class, or are themselves abstract. 76% of the population is ALREADY\n * compliant, which is what makes this a lint rule rather than a design\n * proposal. 30 fire after the two guards below (35 before them).\n *\n * The earlier sweep read 82% of 229 because it covered only the first twelve\n * repos; the six added here are where both false-positive families lived, and\n * both were invisible to it. Adoption varies far more than the aggregate\n * suggests: portal (62/62), bulbul (32/32), pericles (12/12), hala and\n * demo-gateway are at 100%; tamr 86%, summer 83%, farwa 80%, najm 71%, kashta\n * 56%, ai-canvas-health / money2020 / banking-demo 50%, automations 46% (32 of\n * 69), digital-bank 29%, noura-be 25%. The rule is a ratchet for the low half,\n * not a description of the high half.\n *\n * The convention being enforced is `interface`, not `abstract class`, and the\n * corpus is unambiguous about it: 175 `implements` clauses against exactly ONE\n * hand-written abstract class in 7,912 files\n * (hala/typescript/packages/app/src/app/sifi/lib/question-generator.ts:222).\n * (A raw grep finds ~220 more `abstract class` tokens in demo-gateway and\n * sarj-demos; every one is an ambient Cloudflare declaration inside\n * `worker-configuration.d.ts`, excluded here as generated.) Three impl-naming\n * conventions coexist — `HttpCallService implements CallService` (bulbul),\n * `AshbyClient implements IAshbyClient` (automations, portal, najm), and\n * `DocumentParserImpl` / `DocumentParser` (summer) — so the message names no\n * interface for you.\n *\n * FIRING DISTRIBUTION, and why the hits are outliers rather than a house style\n * (before -> after the two guards below):\n * automations 69 exported classes, 20 -> 20 hits\n * najm 17 exported classes, 3 -> 3 hits\n * summer 24 exported classes, 3 -> 2 hits\n * farwa 20 exported classes, 3 -> 2 hits\n * kashta 9 exported classes, 2 -> 2 hits\n * ai-canvas-health 2 exported classes, 1 -> 1 hit\n * tamr 14 exported classes, 1 -> 0 hits\n * money2020 2 exported classes, 1 -> 0 hits\n * banking-demo 2 exported classes, 1 -> 0 hits\n * bulbul / portal / pericles / hala / demo-gateway / sarj-demos / kpi-hub /\n * noura-be / digital-bank: 0 hits\n *\n * THE FALSE-POSITIVE RATE WAS 14%, NOT 0-3.4%. The earlier claim rested on a\n * sweep that never reached farwa, tamr, money2020 or banking-demo, and both\n * false-positive families live only there (plus one instance each already in\n * summer and, for the transport family, nowhere the old sweep looked). Of 35\n * raw hits, 5 were false: three copies of one express router template and two\n * copies of one `ky` wrapper. The two guards below remove exactly those 5 and\n * cost 0 true positives — verified hit-by-hit across all 18 repos, 35 -> 30.\n *\n * The origin case is\n * automations/apps/internal-automations/src/talent/name-healer/service.ts:28\n * (commit 5c5830aa, PR #241): `class NameHealerService` holds `svc: Services`,\n * exposes one `run` method, and implements nothing. Its own test\n * (test/talent-name-healer-service.test.ts:57) has to build the collaborator\n * with `{ ... } as unknown as Services` — the cast-mock this rule is trying to\n * make unnecessary. Its sibling in the same directory tree,\n * talent/referral-tracker/service.ts:129, is the same shape done right:\n * `class ReferralTrackerService implements IReferralTrackerService`.\n *\n * Two further confirmations that the rule finds real drift rather than taste:\n *\n * - najm/src/services/service-factory.ts types 8 of its 11 fields as `I*`\n * interfaces and 3 as concrete classes. The rule fires on exactly those\n * three (`VoiceCallService`, `UserService`, `AgentService`) — the repo's own\n * composition root already documents which classes are missing a port.\n * - summer/typescript/packages/credit/src/app/api/document-parser.ts:116\n * declares `interface DocumentParser` three lines above\n * `class DocumentParserImpl`, which never says `implements`. The port exists\n * and the impl is silently free to drift from it.\n *\n * NAMED FALSE POSITIVES that shaped the guards (each measured, not imagined):\n * - automations/packages/shared/src/http-client.ts:25 `JsonHttpClient`\n * takes `options: JsonHttpClientOptions`, whose three fields are all\n * primitives. A config bag is not a collaborator seam -> config-ish type\n * suffixes and parameter names are excluded. Cost: 1 of 32 raw hits.\n * - noura-be/.../AssistantSDK/src/voice/domain/LiveKitManager.ts:42 takes\n * `callbacks: LiveKitCallbacks`, an OUTBOUND observer bag, not an inbound\n * dependency; there is nothing to substitute. -> `Callbacks` suffix and\n * `callbacks` parameter name excluded. Cost: 1 of 32.\n * - najm/src/services/service-factory.ts:15 `ServiceFactory` receives a `db`\n * and `new`s eleven services onto its own fields. That is a composition\n * root: it is where concrete types are supposed to be named, and a port\n * above it protects nobody. -> a constructor that BUILDS more fields than it\n * RECEIVES collaborators is exempt. Cost: 1 of 32.\n *\n * The first cut of that guard exempted any constructor containing a `new`\n * at all, and the corpus immediately punished it: automations'\n * `talent/referral-message-handler.ts:5` and `talent/referral-reaction-handler.ts:5`\n * each take `svc: Services` AND build one internal `ReferralProcessor`, and\n * both silently stopped firing. Constructing a helper is not being a wiring\n * class; the ratio is what separates the two.\n *\n * - FRAMEWORK ROUTERS. summer/typescript/.../eight-job-runner/src/router.ts:11,\n * farwa/typescript/packages/farwa-job-runner/src/router.ts:11 and\n * tamr/typescript/packages/tamr-job-runner/src/router.ts:7 are three copies\n * of one template: `MainRouter(taskStore, taskExecutor)` whose `init()`\n * returns an `express.Router()`. The server's bootstrap mounts it; nothing\n * injects it, so there is no consumer to hand a port to. -> a class whose\n * body manufactures a router (`express.Router()` / `Router()`) or handles\n * the framework's own namespaced request/response types\n * (`express.Request`) is exempt. The DOM `Request`/`Response` globals a\n * Workers `fetch` handler names are unqualified and deliberately do not\n * match. Cost: 3 of 35 raw hits, all three false positives, 0 true\n * positives lost.\n *\n * - TRANSPORT WRAPPERS. money2020/src/lib/http-client.ts:34 and\n * banking-demo/src/lib/http-client.ts:39 are two copies of\n * `Money2020HttpClient(private readonly client: KyInstance)`. These are\n * false by this standard's own stated reasoning: the Python twin excludes\n * `*Client` precisely because an ABC over a class whose collaborator is\n * somebody else's HTTP transport substitutes nothing — a consumer's test\n * fakes the transport (`respx`, `nock`, `msw`), not the wrapper. -> a class\n * is exempt when its ONLY collaborator is a third-party transport\n * (`KyInstance`, `AxiosInstance`, `Session`), it is named `*Client`, AND no\n * interface in the same file shares its stem. All three arms are load\n * bearing. The `*Client` arm is what keeps kashta's\n * `.../precedent-node/src/services/call-service.ts:50` `HttpCallService`\n * and summer's `.../credit/src/app/api/document-parser.ts:116`\n * `DocumentParserImpl` firing — both are `AxiosInstance`-only, and both\n * wrap the transport in a domain operation their consumers can substitute.\n * The same-file-interface arm has zero measured cost and is kept as a\n * precaution: a `FooClient` beside an `IFooClient` it never `implements` is\n * exactly the drift this rule exists for, and the corpus cannot prove that\n * shape's absence elsewhere. Cost: 2 of 35 raw hits, both false positives,\n * 0 true positives lost.\n *\n * GUARDS WITH ZERO MEASURED COST, kept as precautions because the corpus is\n * first-party and cannot prove their absence elsewhere: decorated classes\n * (NestJS-style `@Injectable()` providers are instantiated by class token, so\n * the class IS the contract — 0 decorated classes in the corpus), and a\n * logger/clock-only dependency (weak evidence of a real seam — 0 such classes).\n *\n * GUARDS THAT NEED NO CODE, because `extends` already covers them: React class\n * components (`extends React.Component`), Lexical/editor nodes\n * (`extends TextNode`, `extends DecoratorNode`), error classes\n * (`extends Error`), Durable Objects, Workflows and custom elements. Every one\n * of the 4 exported classes in `.tsx` files across the corpus is such a case\n * (bulbul .../nodes/variable-node.tsx:36, .../lexical/nodes/section-node.tsx:219,\n * demo-gateway + sarj-demos .../PhoneMockupBoundary.tsx:13). Extending a\n * framework base class is itself a form of port.\n *\n * THE ONE-METHOD THRESHOLD is the load-bearing difference from the Python\n * sibling `require_port_for_service` (SARJ063), which demands >=2 public methods\n * and a `*Service`/`*Store`/`*Client` name. Measured here: 22 of the 29 hits\n * expose exactly ONE public method (the distribution is 22x1, 1x2, 1x5, 1x6,\n * 1x8, 2x9, 1x12), so a >=2 threshold would discard 76% of them — including the\n * origin case, whose sole method is `run`. A service-family NAME filter would\n * cost as much: `ReferralProcessor`, `InteractiveActionHandler`,\n * `DocumentParserImpl`, `MainRouter` and every `*Handler` in automations carry\n * no such suffix. TypeScript's `export` keyword plus a stored collaborator is\n * the proxy for \"someone depends on this\" that Python has to approximate with\n * naming, so this rule leans on those two instead.\n *\n * DELIBERATE FALSE NEGATIVES, each measured at 0 occurrences in the corpus and\n * therefore not worth the AST cost: a destructuring constructor\n * (`constructor({ a, b }: Deps)`) and a class whose public methods are all arrow\n * properties (`handle = async () => {}`) rather than method definitions.\n */\n\nimport { AST_NODE_TYPES, ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\nimport { isGeneratedFile, isScriptFile, isStoryFile, isTestFile } from \"./_paths.js\";\n\ntype MessageIds = \"requireInterface\";\ntype Options = readonly [];\n\n/**\n * Type-name suffixes that mark a constructor parameter as configuration rather\n * than a collaborator. Deliberately short: every entry here is a silent false\n * negative for anyone who names a real dependency that way, so it lists only\n * what the corpus proved (`*Options`, `*Callbacks`) plus the immediate synonyms.\n * `Handler` / `Listener` / `Store` / `Client` are pointedly ABSENT — those are\n * exactly the collaborators the rule exists to notice.\n */\nconst CONFIGISH_TYPE_RE =\n /(?:Options|Opts|Config|Configuration|Settings|Params|Props|Args|Env|Environment|Callbacks|Flags)$/;\n\n/**\n * Parameter names that carry the same signal from the other side, plus the two\n * ambient-capability names the brief called out as weak evidence of a seam: a\n * class whose ONLY dependency is a logger or a clock is not the substitutability\n * problem this rule is about.\n */\nconst CONFIGISH_NAME_RE =\n /^(?:options|opts|config|configuration|settings|params|props|args|env|environment|callbacks|flags|logger|log|clock)$/i;\n\n/**\n * Third-party HTTP transports. A class whose ONLY stored collaborator is one of\n * these is a thin wrapper over somebody else's wire client: the Python side of\n * this standard excludes `*Client` for exactly this reason — an ABC over a class\n * whose collaborator is somebody else's HTTP transport substitutes nothing, and\n * a consumer's test fakes the transport (`respx`, `nock`, `msw`), not the wrapper.\n */\nconst HTTP_TRANSPORT_TYPE_RE = /^(?:KyInstance|AxiosInstance|Session)$/;\n\n/** Only the `*Client` naming carries the guard; see the transport-wrapper note in the header. */\nconst TRANSPORT_WRAPPER_NAME_RE = /Client$/;\n\n/** `express.Router()` / `Router()` — the router-factory call that marks HTTP wiring. */\nconst ROUTER_FACTORY_NAME = \"Router\";\n\n/** Namespaced framework HTTP types (`express.Request`), never the DOM globals of the same name. */\nconst FRAMEWORK_HTTP_TYPES: ReadonlySet<string> = new Set([\"Request\", \"Response\", \"NextFunction\"]);\n\ninterface Collaborator {\n readonly name: string;\n /** Rightmost segment of the type name — what the config-ish guards test. */\n readonly typeName: string;\n /** Full source spelling, including any namespace qualifier, for the message. */\n readonly display: string;\n}\n\n/** The class's own declaration is what `export`s it; anonymous defaults have no name to report. */\nconst isExportedClass = (node: TSESTree.ClassDeclaration): boolean =>\n node.parent.type === AST_NODE_TYPES.ExportNamedDeclaration ||\n node.parent.type === AST_NODE_TYPES.ExportDefaultDeclaration;\n\n/** `ashby.Client` -> `\"ashby.Client\"`; a nested qualifier is flattened the same way. */\nconst qualifiedName = (name: TSESTree.EntityName): string =>\n name.type === AST_NODE_TYPES.Identifier\n ? name.name\n : name.type === AST_NODE_TYPES.TSQualifiedName\n ? `${qualifiedName(name.left)}.${name.right.name}`\n : \"\";\n\nconst typeReferenceName = (annotated: TSESTree.Parameter): Collaborator | null => {\n let target: TSESTree.Node = annotated;\n if (target.type === AST_NODE_TYPES.TSParameterProperty) target = target.parameter;\n if (target.type === AST_NODE_TYPES.AssignmentPattern) target = target.left;\n if (target.type !== AST_NODE_TYPES.Identifier) return null;\n\n const annotation = target.typeAnnotation?.typeAnnotation;\n // A bare type reference is the only shape that names a nominal collaborator.\n // An inline `{ a: string }`, a union, an array, or a function type is data or\n // a callback, never something a consumer would want to swap wholesale.\n if (annotation === undefined || annotation.type !== AST_NODE_TYPES.TSTypeReference) {\n return null;\n }\n const { typeName } = annotation;\n const rightmost =\n typeName.type === AST_NODE_TYPES.Identifier\n ? typeName.name\n : typeName.type === AST_NODE_TYPES.TSQualifiedName\n ? typeName.right.name\n : null;\n if (rightmost === null) return null;\n return { name: target.name, typeName: rightmost, display: qualifiedName(typeName) };\n};\n\ninterface ConstructorFacts {\n /** Parameters assigned to an instance field, either as a parameter property or in the body. */\n readonly collaborators: readonly Collaborator[];\n /** How many instance fields the constructor fills with a `new` expression. */\n readonly constructedFields: number;\n}\n\nconst readConstructor = (ctor: TSESTree.MethodDefinition): ConstructorFacts => {\n const body = ctor.value.body;\n const storedFrom = new Set<string>();\n let constructedFields = 0;\n\n if (body !== null && body !== undefined) {\n for (const statement of body.body) {\n if (statement.type !== AST_NODE_TYPES.ExpressionStatement) continue;\n const expression = statement.expression;\n if (\n expression.type !== AST_NODE_TYPES.AssignmentExpression ||\n expression.operator !== \"=\" ||\n expression.left.type !== AST_NODE_TYPES.MemberExpression ||\n expression.left.object.type !== AST_NODE_TYPES.ThisExpression\n ) {\n continue;\n }\n const source = expression.right;\n if (source.type === AST_NODE_TYPES.NewExpression) {\n constructedFields += 1;\n } else if (source.type === AST_NODE_TYPES.Identifier) {\n // `this.svc = svc`\n storedFrom.add(source.name);\n } else if (\n source.type === AST_NODE_TYPES.MemberExpression &&\n source.object.type === AST_NODE_TYPES.Identifier\n ) {\n // `this.slack = deps.slack` — a dependency bag spread onto fields.\n storedFrom.add(source.object.name);\n }\n }\n }\n\n const collaborators: Collaborator[] = [];\n for (const parameter of ctor.value.params) {\n const reference = typeReferenceName(parameter);\n if (reference === null) continue;\n const stored =\n parameter.type === AST_NODE_TYPES.TSParameterProperty || storedFrom.has(reference.name);\n if (!stored) continue;\n if (CONFIGISH_TYPE_RE.test(reference.typeName)) continue;\n if (CONFIGISH_NAME_RE.test(reference.name)) continue;\n collaborators.push(reference);\n }\n\n return { collaborators, constructedFields };\n};\n\n/** Walk every descendant node, `parent` links excluded, until `found` returns true. */\nconst subtreeHas = (root: TSESTree.Node, found: (node: TSESTree.Node) => boolean): boolean => {\n let hit = false;\n const visit = (current: TSESTree.Node): void => {\n if (hit) return;\n if (found(current)) {\n hit = true;\n return;\n }\n for (const key of Object.keys(current) as (keyof TSESTree.Node)[]) {\n if (key === \"parent\") continue;\n const value = current[key];\n for (const child of (Array.isArray(value) ? value : [value]) as unknown[]) {\n if (\n child !== null &&\n typeof child === \"object\" &&\n typeof (child as { type?: unknown }).type === \"string\"\n ) {\n visit(child as TSESTree.Node);\n }\n }\n }\n };\n visit(root);\n return hit;\n};\n\n/**\n * Report whether the class body is HTTP framework wiring: it manufactures a\n * router (`express.Router()`), or it handles the framework's own namespaced\n * request/response objects. Such a class is mounted by the server's bootstrap,\n * never injected into anything, so there is no consumer to give a port to.\n */\nconst isFrameworkWiring = (body: TSESTree.ClassBody): boolean =>\n subtreeHas(body, (node) => {\n if (node.type === AST_NODE_TYPES.CallExpression) {\n const { callee } = node;\n if (callee.type === AST_NODE_TYPES.Identifier) return callee.name === ROUTER_FACTORY_NAME;\n return (\n callee.type === AST_NODE_TYPES.MemberExpression &&\n !callee.computed &&\n callee.property.type === AST_NODE_TYPES.Identifier &&\n callee.property.name === ROUTER_FACTORY_NAME\n );\n }\n // `express.Request` — qualified on purpose, so the DOM `Request`/`Response`\n // globals a Workers `fetch` handler names do not read as express wiring.\n return (\n node.type === AST_NODE_TYPES.TSTypeReference &&\n node.typeName.type === AST_NODE_TYPES.TSQualifiedName &&\n FRAMEWORK_HTTP_TYPES.has(node.typeName.right.name)\n );\n });\n\n/** `IFooClient` / `FooClientImpl` both reduce to `FooClient`, the three impl conventions the corpus uses. */\nconst stem = (name: string): string => name.replace(/^I(?=[A-Z])/, \"\").replace(/Impl$/, \"\");\n\n/** Interface declarations at the top level of this module, exported or not. */\nconst fileInterfaceNames = (program: TSESTree.Program): string[] => {\n const names: string[] = [];\n for (const statement of program.body) {\n const declaration =\n statement.type === AST_NODE_TYPES.ExportNamedDeclaration ? statement.declaration : statement;\n if (declaration?.type === AST_NODE_TYPES.TSInterfaceDeclaration) names.push(declaration.id.name);\n }\n return names;\n};\n\n/**\n * Report whether the class is a thin wrapper over somebody else's HTTP transport.\n *\n * Three conditions, all necessary. The class receives exactly ONE collaborator\n * and that collaborator is a third-party transport, so there is no domain seam\n * hiding behind it. It is named `*Client`, which is the arm that keeps kashta's\n * `HttpCallService` and summer's `DocumentParserImpl` — both `AxiosInstance`-only\n * — firing, because those wrap a transport in a *domain* operation and their\n * consumers do have something to substitute. And no interface in the same file\n * shares its stem, because an in-file port the class silently fails to\n * `implements` is the drift this rule exists to catch.\n */\nconst isTransportWrapper = (\n className: string,\n collaborators: readonly Collaborator[],\n program: TSESTree.Program,\n): boolean => {\n const [only] = collaborators;\n if (collaborators.length !== 1 || only === undefined) return false;\n if (!HTTP_TRANSPORT_TYPE_RE.test(only.typeName)) return false;\n if (!TRANSPORT_WRAPPER_NAME_RE.test(className)) return false;\n const target = stem(className);\n return !fileInterfaceNames(program).some((name) => {\n const other = stem(name);\n return target.endsWith(other) || other.endsWith(target);\n });\n};\n\n/**\n * Public instance methods only. `kind === \"method\"` excludes getters and setters\n * on purpose: a class that exposes only accessors is a value object, and the\n * constructor is not a seam worth porting.\n */\nconst publicMethodNames = (body: TSESTree.ClassBody): string[] => {\n const names: string[] = [];\n for (const member of body.body) {\n if (member.type !== AST_NODE_TYPES.MethodDefinition) continue;\n if (member.kind !== \"method\" || member.static) continue;\n if (member.accessibility === \"private\" || member.accessibility === \"protected\") continue;\n if (member.key.type === AST_NODE_TYPES.PrivateIdentifier) continue;\n if (member.key.type === AST_NODE_TYPES.Identifier) names.push(member.key.name);\n else names.push(\"…\");\n }\n return names;\n};\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"require-interface-for-injected-service\",\n meta: {\n type: \"suggestion\",\n docs: {\n description:\n \"An exported service class with constructor-injected collaborators must implement an interface, so consumers depend on a port they can substitute instead of mocking the class.\",\n },\n schema: [],\n messages: {\n requireInterface:\n \"`{{name}}` stores injected collaborator(s) ({{deps}}) but implements no interface, so every consumer depends on this concrete class and can only be tested by mocking it. Declare an interface with its public method signature(s) ({{methods}}) and `class {{name}} implements <Interface>`.\",\n },\n },\n defaultOptions: [],\n create(context) {\n const { filename } = context;\n if (isTestFile(filename) || isStoryFile(filename) || isScriptFile(filename)) return {};\n if (isGeneratedFile(filename, context.sourceCode.getText())) return {};\n\n return {\n ClassDeclaration(node: TSESTree.ClassDeclaration): void {\n if (node.id === null) return;\n if (!isExportedClass(node)) return;\n // The port itself must never fire, and a class that extends anything —\n // an abstract base, a framework class, `Error` — already has one.\n if (node.abstract === true) return;\n if (node.superClass !== null) return;\n if (node.implements.length > 0) return;\n // A framework that instantiates by class token (NestJS `@Injectable()`)\n // makes the class its own contract.\n if (node.decorators.length > 0) return;\n\n const ctor = node.body.body.find(\n (member): member is TSESTree.MethodDefinition =>\n member.type === AST_NODE_TYPES.MethodDefinition && member.kind === \"constructor\",\n );\n if (ctor === undefined) return;\n\n const { collaborators, constructedFields } = readConstructor(ctor);\n if (collaborators.length === 0) return;\n // A composition root BUILDS more than it RECEIVES — that is where\n // concrete types are supposed to be named, and a port above it protects\n // nobody. Constructing one internal helper while receiving a real\n // collaborator is an ordinary service, not a wiring class, so the test\n // is a ratio rather than the mere presence of a `new`.\n if (constructedFields > collaborators.length) return;\n // HTTP wiring: a router factory is mounted by the bootstrap, not injected.\n if (isFrameworkWiring(node.body)) return;\n // A lone third-party transport is not a seam a port could protect.\n if (isTransportWrapper(node.id.name, collaborators, context.sourceCode.ast)) return;\n\n const methods = publicMethodNames(node.body);\n if (methods.length === 0) return;\n\n context.report({\n node: node.id,\n messageId: \"requireInterface\",\n data: {\n name: node.id.name,\n deps: collaborators.map((c) => `${c.name}: ${c.display}`).join(\", \"),\n methods: methods.join(\", \"),\n },\n });\n },\n };\n },\n});\n","/**\n * @fileoverview Prefer non-null arrays in declared TypeScript data shapes.\n *\n * An array type such as `OrganizationId[] | null` gives an empty collection two\n * representations: nullish and `[]`. Requiring an array removes null checks\n * from the call chain and makes the project-wide contract explicit.\n *\n * The rule checks declared data shapes: interface/class properties, properties\n * in object type aliases, and direct array type aliases. An optional property\n * (`items?: T[]`) does not fire unless its written type also explicitly includes\n * `null` or `undefined`: omission is API input syntax, not a nullable collection\n * value. Mixed scalar-or-array unions do not fire because they model more than\n * an empty collection. Function-local annotations, tests, and generated\n * declarations are exempt.\n *\n * This is an opinionated application convention, not a TypeScript type-system\n * fact. When null is a meaningful third state (for example React Router uses\n * `matches: Match[] | null` to distinguish \"no match\" from an empty match set),\n * retain the union with an inline ESLint disable and its reason.\n *\n * Corpus sweep (2026-07-27): FastAPI, Pydantic, SQLModel, Zod, and React Router;\n * 2,901 Python/TypeScript files total. The final TypeScript rule reported 13\n * explicit nullable-array declarations. Every match had the advertised AST\n * shape; optional-only properties, tests, generated files, and vendor code\n * produced no reports.\n *\n */\n\nimport { AST_NODE_TYPES, ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\nimport { isGeneratedFile, isTestFile } from \"./_paths.js\";\n\ntype MessageIds = \"preferNonNullableCollection\";\ntype Options = readonly [];\n\nconst ARRAY_TYPE_NAMES: ReadonlySet<string> = new Set([\"Array\", \"ReadonlyArray\"]);\n\nfunction propertyName(node: TSESTree.TSPropertySignature | TSESTree.PropertyDefinition): string {\n const key = node.key;\n if (key.type === AST_NODE_TYPES.Identifier) return key.name;\n if (key.type === AST_NODE_TYPES.Literal) return String(key.value);\n return \"collection\";\n}\n\nfunction isArrayType(node: TSESTree.TypeNode): boolean {\n if (node.type === AST_NODE_TYPES.TSArrayType) return true;\n return (\n node.type === AST_NODE_TYPES.TSTypeReference &&\n node.typeName.type === AST_NODE_TYPES.Identifier &&\n ARRAY_TYPE_NAMES.has(node.typeName.name)\n );\n}\n\nfunction isNullishType(node: TSESTree.TypeNode): boolean {\n return node.type === AST_NODE_TYPES.TSNullKeyword || node.type === AST_NODE_TYPES.TSUndefinedKeyword;\n}\n\nfunction isNullableArrayOnly(node: TSESTree.TSUnionType): boolean {\n const values = node.types.filter((member) => !isNullishType(member));\n return values.length > 0 && values.length < node.types.length && values.every(isArrayType);\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) => `https://github.com/sarj-ai/standards/tree/main/packages/typescript#${name}`,\n)<Options, MessageIds>({\n name: \"prefer-non-nullable-collection\",\n meta: {\n type: \"suggestion\",\n docs: {\n description:\n \"Require array types to use an empty array instead of explicit nullish unions with two equivalent empty states.\",\n },\n schema: [],\n messages: {\n preferNonNullableCollection:\n \"`{{name}}` is a nullable array, so nullish and `[]` represent the same empty collection. Use a non-null array and default omitted values to `[]`.\",\n },\n },\n defaultOptions: [],\n create(context) {\n const normalizedFilename = context.filename.replaceAll(\"\\\\\", \"/\");\n if (\n isTestFile(context.filename) ||\n isGeneratedFile(context.filename, context.sourceCode.text) ||\n /\\/(?:vendor|vendored)\\//u.test(normalizedFilename)\n ) {\n return {};\n }\n\n function checkOptionalProperty(\n node: TSESTree.TSPropertySignature | TSESTree.PropertyDefinition,\n ): void {\n const annotation = node.typeAnnotation?.typeAnnotation;\n if (annotation === undefined) return;\n if (annotation.type !== AST_NODE_TYPES.TSUnionType || !isNullableArrayOnly(annotation)) {\n return;\n }\n context.report({\n node,\n messageId: \"preferNonNullableCollection\",\n data: { name: propertyName(node) },\n });\n }\n\n return {\n TSPropertySignature: checkOptionalProperty,\n PropertyDefinition: checkOptionalProperty,\n TSTypeAliasDeclaration(node): void {\n if (node.typeAnnotation.type !== AST_NODE_TYPES.TSUnionType) return;\n if (!isNullableArrayOnly(node.typeAnnotation)) return;\n context.report({\n node,\n messageId: \"preferNonNullableCollection\",\n data: { name: node.id.name },\n });\n },\n };\n },\n});\n","/**\n * @fileoverview Flag a module whose filename stem does not match its primary export.\n *\n * Semantic File Naming Rule:\n * When a TypeScript/TSX module has a single primary export (a sole exported\n * function, class, or React component, or a default export), its filename stem\n * should semantically reflect that export's name in kebab-case.\n *\n * Examples:\n * - File `user-helpers.tsx` exporting sole `UserProfileCard` -> rename to `user-profile-card.tsx`.\n * - File `account-stuff.ts` exporting sole `AccountService` -> rename to `account-service.ts`.\n * - File `auth-hook.ts` exporting sole `useAuthSession` -> rename to `use-auth-session.ts`.\n *\n * Exemptions (Corpus-validated against noura-be, bulbul, and top TS repos):\n * - Barrel re-export files (`index.ts`, `index.tsx`).\n * - Declaration files (`*.d.ts`).\n * - Test files (`*.test.ts`, `*.spec.ts`, `isTestFile`).\n * - Framework route files (`page.tsx`, `layout.tsx`, `route.ts`, `$id.tsx`, `[id].tsx`, `__root.tsx`).\n * - Standard framework convention stems (`config`, `constants`, `errors`, `types`).\n * - Entrypoint functions (`main`, `run`, `cli`, `setup`, `teardown`, `execute`).\n * - Conventional exports (`cn`).\n */\n\nimport { AST_NODE_TYPES, ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\nimport { isGeneratedFile, isTestFile } from \"./_paths.js\";\n\ntype MessageIds = \"primaryExportMismatch\";\ntype Options = readonly [];\n\nconst CONVENTIONAL_BUCKET_EXPORTS = new Set([\"cn\"]);\nconst GENERIC_ENTRYPOINTS = new Set([\"main\", \"run\", \"cli\", \"setup\", \"teardown\", \"execute\"]);\n\nconst ACRONYM_OVERRIDES: ReadonlyArray<readonly [RegExp, string]> = [\n [/OAuth/g, \"Oauth\"],\n [/GraphQL/g, \"Graphql\"],\n [/gRPC/g, \"Grpc\"],\n];\n\nconst CAMEL_BOUNDARY_RE = /(?<=[a-z0-9])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])/g;\n\nconst basename = (filename: string): string =>\n filename.split(/[/\\\\]/).pop() ?? filename;\n\nconst stemOf = (base: string): string =>\n base.replace(/\\.(test|spec|config|d|stories)\\.[cm]?[jt]sx?$/i, \"\").replace(/\\.[cm]?[jt]sx?$/i, \"\");\n\nconst kebabCase = (name: string): string => {\n let normalized = name;\n for (const [pattern, replacement] of ACRONYM_OVERRIDES) {\n normalized = normalized.replace(pattern, replacement);\n }\n return normalized.replace(CAMEL_BOUNDARY_RE, \"-\").toLowerCase();\n};\n\nconst isFunctionOrClass = (node: TSESTree.Node): boolean =>\n node.type === AST_NODE_TYPES.ArrowFunctionExpression ||\n node.type === AST_NODE_TYPES.FunctionExpression;\n\ninterface PrimaryExportInfo {\n readonly name: string;\n readonly node: TSESTree.Node;\n readonly count: number;\n readonly isDefault: boolean;\n}\n\nconst findPrimaryExport = (body: readonly TSESTree.ProgramStatement[]): PrimaryExportInfo | null => {\n let exportCount = 0;\n let primaryCandidate: { name: string; node: TSESTree.Node; isDefault: boolean } | null = null;\n\n for (const statement of body) {\n if (statement.type === AST_NODE_TYPES.ExportAllDeclaration) {\n return null; // Barrel file\n }\n\n if (statement.type === AST_NODE_TYPES.ExportDefaultDeclaration) {\n exportCount += 1;\n const decl = statement.declaration;\n if (\n (decl.type === AST_NODE_TYPES.FunctionDeclaration || decl.type === AST_NODE_TYPES.ClassDeclaration) &&\n decl.id !== null\n ) {\n primaryCandidate = { name: decl.id.name, node: statement, isDefault: true };\n } else if (decl.type === AST_NODE_TYPES.Identifier) {\n primaryCandidate = { name: decl.name, node: statement, isDefault: true };\n }\n } else if (statement.type === AST_NODE_TYPES.ExportNamedDeclaration) {\n if (statement.source !== null) {\n return null; // Re-export\n }\n const decl = statement.declaration;\n if (decl === null) {\n exportCount += statement.specifiers.length;\n if (statement.specifiers.length === 1 && primaryCandidate === null) {\n const spec = statement.specifiers[0];\n if (spec && spec.exported.type === AST_NODE_TYPES.Identifier) {\n primaryCandidate = { name: spec.exported.name, node: statement, isDefault: false };\n }\n }\n } else if (decl.type === AST_NODE_TYPES.FunctionDeclaration || decl.type === AST_NODE_TYPES.ClassDeclaration) {\n if (decl.id !== null) {\n exportCount += 1;\n if (primaryCandidate === null) {\n primaryCandidate = { name: decl.id.name, node: statement, isDefault: false };\n }\n }\n } else if (decl.type === AST_NODE_TYPES.VariableDeclaration) {\n for (const declarator of decl.declarations) {\n if (declarator.id.type === AST_NODE_TYPES.Identifier) {\n exportCount += 1;\n if (primaryCandidate === null && declarator.init && isFunctionOrClass(declarator.init)) {\n primaryCandidate = { name: declarator.id.name, node: statement, isDefault: false };\n }\n }\n }\n } else if (decl.type === AST_NODE_TYPES.TSTypeAliasDeclaration || decl.type === AST_NODE_TYPES.TSInterfaceDeclaration) {\n exportCount += 1;\n if (primaryCandidate === null) {\n primaryCandidate = { name: decl.id.name, node: statement, isDefault: false };\n }\n }\n }\n }\n\n if (primaryCandidate === null || exportCount > 2) {\n return null;\n }\n\n return { ...primaryCandidate, count: exportCount };\n};\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"primary-export-file-name\",\n meta: {\n type: \"suggestion\",\n docs: {\n description:\n \"Enforce semantic naming alignment: a file with a single primary export must be named after that export.\",\n },\n schema: [],\n messages: {\n primaryExportMismatch:\n \"Module stem '{{stem}}' does not match its primary export '{{name}}' — rename the file to '{{expected}}{{ext}}' to describe its responsibility.\",\n },\n },\n defaultOptions: [],\n create(context) {\n const base = basename(context.filename);\n\n if (base.endsWith(\".d.ts\") || base.startsWith(\"index.\") || base.startsWith(\"_\")) return {};\n if (isTestFile(context.filename) || isGeneratedFile(context.filename, context.sourceCode.text)) return {};\n\n const stem = stemOf(base);\n // Skip framework routes like page.tsx, layout.tsx, route.ts, $id.tsx, [id].tsx and convention stems\n if (\n stem === \"page\" ||\n stem === \"layout\" ||\n stem === \"loading\" ||\n stem === \"error\" ||\n stem === \"not-found\" ||\n stem === \"route\" ||\n stem === \"__root\" ||\n stem === \"config\" ||\n stem === \"constants\" ||\n stem === \"types\" ||\n stem.startsWith(\"$\") ||\n stem.startsWith(\"[\")\n ) {\n return {};\n }\n\n return {\n Program(node: TSESTree.Program): void {\n const primary = findPrimaryExport(node.body);\n if (primary === null) return;\n if (CONVENTIONAL_BUCKET_EXPORTS.has(primary.name) || GENERIC_ENTRYPOINTS.has(primary.name)) return;\n\n const expectedStem = kebabCase(primary.name);\n if (stem === expectedStem) return;\n\n // Ensure extension is preserved (.tsx if component/JSX or .ts)\n const ext = base.endsWith(\".tsx\") ? \".tsx\" : \".ts\";\n\n context.report({\n node: primary.node,\n messageId: \"primaryExportMismatch\",\n data: { stem, name: primary.name, expected: expectedStem, ext },\n });\n },\n };\n },\n});\n","import { AST_NODE_TYPES, ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\ntype MessageIds = \"noImplicitAttributeAccess\";\n\nconst EXCLUDED_BASES = new Set([\n \"environ\", \"headers\", \"cookies\", \"session\", \"redis\", \"cache\", \n \"state\", \"config\", \"env\", \"process\", \"localStorage\", \"sessionStorage\"\n]);\n\nfunction getBaseName(node: TSESTree.Node): string | null {\n if (node.type === AST_NODE_TYPES.Identifier) {\n return node.name;\n }\n if (node.type === AST_NODE_TYPES.MemberExpression && node.property.type === AST_NODE_TYPES.Identifier) {\n return node.property.name;\n }\n return null;\n}\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<[], MessageIds>({\n name: \"no-implicit-attribute-access\",\n meta: {\n type: \"problem\",\n docs: {\n description: \"Disallow imperative dictionary access with string literals; use declarative Zod schemas.\",\n },\n schema: [],\n messages: {\n noImplicitAttributeAccess:\n \"Imperative lookup for '{{key}}' — if the key is known, parse the object declaratively with a Zod schema instead.\",\n },\n },\n defaultOptions: [],\n create(context) {\n return {\n MemberExpression(node: TSESTree.MemberExpression): void {\n if (!node.computed) {\n return;\n }\n \n // We are looking for something like: foo[\"price\"]\n if (node.property.type === AST_NODE_TYPES.Literal && typeof node.property.value === \"string\") {\n const baseName = getBaseName(node.object);\n if (!baseName || !EXCLUDED_BASES.has(baseName)) {\n context.report({\n node,\n messageId: \"noImplicitAttributeAccess\",\n data: { key: node.property.value },\n });\n }\n }\n },\n CallExpression(node: TSESTree.CallExpression): void {\n const callee = node.callee;\n if (callee.type === AST_NODE_TYPES.MemberExpression) {\n const method = callee.property.type === AST_NODE_TYPES.Identifier ? callee.property.name : null;\n if (method === \"get\") {\n const arg = node.arguments[0];\n if (arg && arg.type === AST_NODE_TYPES.Literal && typeof arg.value === \"string\") {\n const baseName = getBaseName(callee.object);\n if (!baseName || !EXCLUDED_BASES.has(baseName)) {\n context.report({\n node,\n messageId: \"noImplicitAttributeAccess\",\n data: { key: arg.value },\n });\n }\n }\n }\n }\n }\n };\n },\n});\n","import { AST_NODE_TYPES, ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\nexport default ESLintUtils.RuleCreator.withoutDocs({\n meta: {\n type: \"suggestion\",\n docs: {\n description: \"Replace multiple sequential assertions with a single toMatchObject or toStrictEqual\",\n },\n fixable: \"code\",\n messages: {\n combineAssertions: \"Combine multiple sequential assertions on the same object into a single toMatchObject or toStrictEqual\",\n },\n schema: [],\n },\n defaultOptions: [],\n create(context) {\n function checkBody(body: TSESTree.Statement[]) {\n let currentObject: TSESTree.Expression | null = null;\n let sequence: TSESTree.ExpressionStatement[] = [];\n\n function getExpectObject(expr: TSESTree.Expression): TSESTree.Expression | null {\n if (\n expr.type === AST_NODE_TYPES.CallExpression &&\n expr.callee.type === AST_NODE_TYPES.MemberExpression &&\n expr.callee.object.type === AST_NODE_TYPES.CallExpression &&\n expr.callee.object.callee.type === AST_NODE_TYPES.Identifier &&\n expr.callee.object.callee.name === \"expect\" &&\n expr.callee.object.arguments.length === 1\n ) {\n const arg = expr.callee.object.arguments.at(0);\n if (arg?.type === AST_NODE_TYPES.MemberExpression) {\n return arg.object;\n }\n }\n return null;\n }\n \n function reportSequence() {\n const first = sequence.at(0);\n if (sequence.length > 1 && first) {\n context.report({\n node: first,\n messageId: \"combineAssertions\",\n fix(fixer) {\n if (!currentObject) return null;\n const objectText = context.sourceCode.getText(currentObject);\n const props = sequence.map(stmt => {\n if (\n stmt.expression.type !== AST_NODE_TYPES.CallExpression ||\n stmt.expression.callee.type !== AST_NODE_TYPES.MemberExpression ||\n stmt.expression.callee.object.type !== AST_NODE_TYPES.CallExpression\n ) {\n return null;\n }\n\n const actual = stmt.expression.callee.object.arguments.at(0);\n const expected = stmt.expression.arguments.at(0);\n if (\n actual?.type === AST_NODE_TYPES.MemberExpression &&\n actual.property.type === AST_NODE_TYPES.Identifier &&\n expected\n ) {\n const propName = actual.property.name;\n const valText = context.sourceCode.getText(expected);\n return `${propName}: ${valText}`;\n }\n return null;\n });\n if (props.some(p => p === null)) return null;\n const replacement = `expect(${objectText}).toMatchObject({ ${props.join(\", \")} });`;\n \n return [\n fixer.replaceText(first, replacement),\n ...sequence.slice(1).map(stmt => fixer.remove(stmt))\n ];\n }\n });\n }\n }\n\n for (const statement of body) {\n if (statement.type === AST_NODE_TYPES.ExpressionStatement) {\n const obj = getExpectObject(statement.expression);\n if (obj && currentObject && context.sourceCode.getText(obj) === context.sourceCode.getText(currentObject)) {\n sequence.push(statement);\n } else {\n reportSequence();\n if (obj) {\n currentObject = obj;\n sequence = [statement];\n } else {\n currentObject = null;\n sequence = [];\n }\n }\n } else {\n reportSequence();\n currentObject = null;\n sequence = [];\n }\n }\n reportSequence();\n }\n \n return {\n BlockStatement(node) {\n checkBody(node.body);\n },\n Program(node) {\n checkBody(node.body);\n }\n };\n },\n});\n","import { AST_NODE_TYPES, ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\n\nimport { isTestFile } from \"./_paths.js\";\n\ntype MessageIds = \"noAsyncCallbackInWaitFor\";\ntype Options = readonly [];\n\nexport default ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"no-async-callback-in-waitfor\",\n meta: {\n type: \"problem\",\n docs: {\n description: \"Disallow async callbacks in `waitFor` to prevent swallowed promise rejections.\",\n },\n schema: [],\n messages: {\n noAsyncCallbackInWaitFor:\n \"The callback to `waitFor` should not be async. It expects synchronous assertions and runs the callback repeatedly.\",\n },\n },\n defaultOptions: [],\n create(context) {\n if (!isTestFile(context.filename)) {\n return {};\n }\n return {\n CallExpression(node: TSESTree.CallExpression) {\n if (\n node.callee.type === AST_NODE_TYPES.Identifier &&\n node.callee.name === \"waitFor\"\n ) {\n const callback = node.arguments[0];\n if (\n callback &&\n (callback.type === AST_NODE_TYPES.ArrowFunctionExpression ||\n callback.type === AST_NODE_TYPES.FunctionExpression) &&\n callback.async\n ) {\n context.report({\n node: callback,\n messageId: \"noAsyncCallbackInWaitFor\",\n });\n }\n }\n },\n };\n },\n});\n","import { AST_NODE_TYPES, ESLintUtils, type TSESTree } from \"@typescript-eslint/utils\";\nimport { isTestFile } from \"./_paths.js\";\n\ntype MessageIds = \"preferSetupFileMocks\";\ntype Options = readonly [];\n\nexport default ESLintUtils.RuleCreator(\n (name) => `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`,\n)<Options, MessageIds>({\n name: \"prefer-setup-file-mocks\",\n meta: {\n type: \"suggestion\",\n docs: {\n description: \"Prefer defining module mocks in setup files rather than using vi.mock or jest.mock inline in test files.\",\n },\n schema: [],\n messages: {\n preferSetupFileMocks:\n \"Define module mocks in a setup file (e.g. vitest.setup.ts) instead of using vi.mock or jest.mock directly in test files.\",\n },\n },\n defaultOptions: [],\n create(context) {\n if (!isTestFile(context.filename)) {\n return {};\n }\n\n return {\n CallExpression(node: TSESTree.CallExpression) {\n if (\n node.callee.type === AST_NODE_TYPES.MemberExpression &&\n node.callee.object.type === AST_NODE_TYPES.Identifier &&\n (node.callee.object.name === \"vi\" || node.callee.object.name === \"jest\") &&\n node.callee.property.type === AST_NODE_TYPES.Identifier &&\n node.callee.property.name === \"mock\"\n ) {\n context.report({\n node,\n messageId: \"preferSetupFileMocks\",\n });\n }\n },\n };\n },\n});\n"],"mappings":"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","names":["import_utils","import_utils","import_utils","import_utils","import_utils","import_utils","import_utils","propName","import_utils","import_utils","DEFAULT_IGNORE_PATTERNS","import_utils","import_utils","import_utils","import_utils","import_utils","import_utils","findVariable","import_utils","import_utils","propName","keyName","import_utils","import_utils","import_utils","import_utils","isNode","calleeName","import_utils","import_utils","matchesAnyPattern","isGlobalFetchCall","import_utils","isNode","unwrap","import_utils","propertyKeyName","keyName","import_utils","import_utils","import_utils","import_utils","import_utils","import_utils","import_utils","import_utils","import_utils","FUNCTION_TYPES","import_utils","TEST_CALLERS","FUNCTION_TYPES","nearestEnclosingFunction","testCallerName","isTestBody","import_utils","import_utils","import_utils","import_utils","import_utils","DEFAULT_METHODS","compile","import_utils","ts","IGNORE_PATTERNS","isIgnoredFile","isZodModule","unwrap","import_utils","IGNORE_PATTERNS","FUNCTION_TYPES","isIgnoredFile","isTestFile","unwrap","enclosingFunction","import_utils","DIRECTIVE_RE","STOPWORDS","WORD_RE","import_utils","DIRECTIVE_RE","areAdjacentLineComments","import_utils","WORD_RE","STOPWORDS","DIRECTIVE_RE","import_utils","preview","import_utils","stem","import_utils","import_utils","stem","import_utils","import_utils","propName","import_utils","import_utils"]}