@blumintinc/eslint-plugin-blumint 1.12.6 → 1.14.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.
Files changed (258) hide show
  1. package/README.md +171 -99
  2. package/lib/index.js +300 -17
  3. package/lib/rules/array-methods-this-context.js +20 -2
  4. package/lib/rules/avoid-utils-directory.js +11 -2
  5. package/lib/rules/class-methods-read-top-to-bottom.js +24 -5
  6. package/lib/rules/consistent-callback-naming.js +1 -1
  7. package/lib/rules/dynamic-https-errors.d.ts +3 -1
  8. package/lib/rules/dynamic-https-errors.js +48 -11
  9. package/lib/rules/enforce-assert-safe-object-key.d.ts +8 -0
  10. package/lib/rules/{enforce-assertSafe-object-key.js → enforce-assert-safe-object-key.js} +52 -96
  11. package/lib/rules/enforce-assert-throws.d.ts +3 -1
  12. package/lib/rules/enforce-assert-throws.js +493 -51
  13. package/lib/rules/enforce-boolean-naming-prefixes.d.ts +3 -1
  14. package/lib/rules/enforce-boolean-naming-prefixes.js +820 -39
  15. package/lib/rules/enforce-callable-types.js +4 -4
  16. package/lib/rules/enforce-callback-memo.js +143 -18
  17. package/lib/rules/enforce-centralized-mock-firestore.js +5 -2
  18. package/lib/rules/enforce-console-error.d.ts +3 -0
  19. package/lib/rules/enforce-console-error.js +278 -0
  20. package/lib/rules/enforce-css-media-queries.js +24 -46
  21. package/lib/rules/enforce-dynamic-file-naming.d.ts +2 -0
  22. package/lib/rules/enforce-dynamic-file-naming.js +53 -28
  23. package/lib/rules/enforce-dynamic-firebase-imports.js +100 -19
  24. package/lib/rules/enforce-dynamic-imports.js +1 -1
  25. package/lib/rules/enforce-early-destructuring.d.ts +2 -0
  26. package/lib/rules/enforce-early-destructuring.js +980 -0
  27. package/lib/rules/enforce-empty-object-check.d.ts +11 -0
  28. package/lib/rules/enforce-empty-object-check.js +502 -0
  29. package/lib/rules/enforce-exported-function-types.js +4 -4
  30. package/lib/rules/enforce-fieldpath-syntax-in-docsetter.d.ts +2 -0
  31. package/lib/rules/enforce-fieldpath-syntax-in-docsetter.js +262 -0
  32. package/lib/rules/enforce-firestore-doc-ref-generic.js +350 -3
  33. package/lib/rules/enforce-firestore-facade.js +349 -157
  34. package/lib/rules/enforce-firestore-rules-get-access.d.ts +3 -0
  35. package/lib/rules/enforce-firestore-rules-get-access.js +109 -0
  36. package/lib/rules/enforce-firestore-set-merge.js +3 -4
  37. package/lib/rules/enforce-global-constants.d.ts +4 -1
  38. package/lib/rules/enforce-global-constants.js +240 -12
  39. package/lib/rules/enforce-identifiable-firestore-type.js +108 -58
  40. package/lib/rules/enforce-memoize-async.d.ts +2 -1
  41. package/lib/rules/enforce-memoize-async.js +92 -27
  42. package/lib/rules/enforce-memoize-getters.d.ts +3 -0
  43. package/lib/rules/enforce-memoize-getters.js +147 -0
  44. package/lib/rules/enforce-microdiff.js +3 -2
  45. package/lib/rules/enforce-object-literal-as-const.js +4 -4
  46. package/lib/rules/enforce-positive-naming.js +9 -7
  47. package/lib/rules/enforce-props-argument-name.d.ts +2 -7
  48. package/lib/rules/enforce-props-argument-name.js +165 -151
  49. package/lib/rules/enforce-props-naming-consistency.d.ts +2 -0
  50. package/lib/rules/enforce-props-naming-consistency.js +145 -0
  51. package/lib/rules/enforce-querykey-ts.d.ts +9 -0
  52. package/lib/rules/enforce-querykey-ts.js +274 -0
  53. package/lib/rules/enforce-safe-stringify.js +1 -1
  54. package/lib/rules/enforce-serializable-params.js +3 -3
  55. package/lib/rules/enforce-singular-type-names.js +10 -6
  56. package/lib/rules/enforce-stable-hash-spread-props.d.ts +13 -0
  57. package/lib/rules/enforce-stable-hash-spread-props.js +342 -0
  58. package/lib/rules/enforce-storage-context.d.ts +9 -0
  59. package/lib/rules/enforce-storage-context.js +646 -0
  60. package/lib/rules/enforce-timestamp-now.js +23 -7
  61. package/lib/rules/enforce-transform-memoization.d.ts +4 -0
  62. package/lib/rules/enforce-transform-memoization.js +416 -0
  63. package/lib/rules/enforce-typescript-markdown-code-blocks.d.ts +1 -0
  64. package/lib/rules/enforce-typescript-markdown-code-blocks.js +100 -0
  65. package/lib/rules/enforce-unique-cursor-headers.d.ts +16 -0
  66. package/lib/rules/enforce-unique-cursor-headers.js +365 -0
  67. package/lib/rules/enforce-verb-noun-naming.js +90 -14
  68. package/lib/rules/ensure-pointer-events-none.js +28 -2
  69. package/lib/rules/export-if-in-doubt.js +27 -4
  70. package/lib/rules/extract-global-constants.js +92 -53
  71. package/lib/rules/fast-deep-equal-over-microdiff.d.ts +2 -1
  72. package/lib/rules/fast-deep-equal-over-microdiff.js +351 -83
  73. package/lib/rules/firestore-transaction-reads-before-writes.d.ts +1 -0
  74. package/lib/rules/firestore-transaction-reads-before-writes.js +277 -0
  75. package/lib/rules/flatten-push-calls.d.ts +2 -0
  76. package/lib/rules/flatten-push-calls.js +428 -0
  77. package/lib/rules/generic-starts-with-t.js +8 -2
  78. package/lib/rules/global-const-style.js +50 -19
  79. package/lib/rules/jsdoc-above-field.d.ts +11 -0
  80. package/lib/rules/jsdoc-above-field.js +208 -0
  81. package/lib/rules/key-only-outermost-element.js +21 -3
  82. package/lib/rules/logical-top-to-bottom-grouping.d.ts +5 -0
  83. package/lib/rules/logical-top-to-bottom-grouping.js +1258 -0
  84. package/lib/rules/memo-compare-deeply-complex-props.d.ts +3 -0
  85. package/lib/rules/memo-compare-deeply-complex-props.js +788 -0
  86. package/lib/rules/memo-nested-react-components.d.ts +8 -0
  87. package/lib/rules/memo-nested-react-components.js +410 -0
  88. package/lib/rules/memoize-root-level-hocs.d.ts +7 -0
  89. package/lib/rules/memoize-root-level-hocs.js +242 -0
  90. package/lib/rules/no-always-true-false-conditions.d.ts +2 -1
  91. package/lib/rules/no-always-true-false-conditions.js +68 -8
  92. package/lib/rules/no-array-length-in-deps.d.ts +12 -0
  93. package/lib/rules/no-array-length-in-deps.js +314 -0
  94. package/lib/rules/no-async-array-filter.js +5 -2
  95. package/lib/rules/no-async-foreach.js +210 -12
  96. package/lib/rules/no-circular-references.js +18 -17
  97. package/lib/rules/no-class-instance-destructuring.js +55 -12
  98. package/lib/rules/no-complex-cloud-params.js +22 -5
  99. package/lib/rules/no-compositing-layer-props.js +6 -4
  100. package/lib/rules/no-conditional-literals-in-jsx.js +37 -12
  101. package/lib/rules/no-console-error.d.ts +9 -0
  102. package/lib/rules/no-console-error.js +525 -0
  103. package/lib/rules/no-curly-brackets-around-commented-properties.d.ts +2 -0
  104. package/lib/rules/no-curly-brackets-around-commented-properties.js +258 -0
  105. package/lib/rules/no-empty-dependency-use-callbacks.d.ts +11 -0
  106. package/lib/rules/no-empty-dependency-use-callbacks.js +576 -0
  107. package/lib/rules/no-entire-object-hook-deps.d.ts +3 -1
  108. package/lib/rules/no-entire-object-hook-deps.js +320 -68
  109. package/lib/rules/no-excessive-parent-chain.d.ts +5 -0
  110. package/lib/rules/no-excessive-parent-chain.js +273 -0
  111. package/lib/rules/no-explicit-return-type.d.ts +2 -1
  112. package/lib/rules/no-explicit-return-type.js +215 -26
  113. package/lib/rules/no-filter-without-return.js +5 -1
  114. package/lib/rules/no-firestore-jest-mock.d.ts +2 -1
  115. package/lib/rules/no-firestore-jest-mock.js +126 -8
  116. package/lib/rules/no-firestore-object-arrays.js +398 -28
  117. package/lib/rules/no-handler-suffix.d.ts +12 -0
  118. package/lib/rules/no-handler-suffix.js +305 -0
  119. package/lib/rules/no-hungarian.js +14 -14
  120. package/lib/rules/no-inline-component-prop.d.ts +10 -0
  121. package/lib/rules/no-inline-component-prop.js +456 -0
  122. package/lib/rules/no-jsx-in-hooks.js +6 -1
  123. package/lib/rules/no-jsx-whitespace-literal.js +8 -2
  124. package/lib/rules/no-margin-properties.d.ts +7 -1
  125. package/lib/rules/no-margin-properties.js +22 -9
  126. package/lib/rules/no-memoize-on-static.js +9 -1
  127. package/lib/rules/no-misleading-boolean-prefixes.d.ts +7 -0
  128. package/lib/rules/no-misleading-boolean-prefixes.js +331 -0
  129. package/lib/rules/no-misused-switch-case.js +22 -1
  130. package/lib/rules/no-mixed-firestore-transactions.d.ts +3 -1
  131. package/lib/rules/no-mixed-firestore-transactions.js +296 -34
  132. package/lib/rules/no-mock-firebase-admin.js +5 -2
  133. package/lib/rules/no-object-values-on-strings.js +10 -2
  134. package/lib/rules/no-overridable-method-calls-in-constructor.d.ts +1 -0
  135. package/lib/rules/no-overridable-method-calls-in-constructor.js +272 -0
  136. package/lib/rules/no-passthrough-getters.d.ts +4 -0
  137. package/lib/rules/no-passthrough-getters.js +206 -0
  138. package/lib/rules/no-redundant-annotation-assertion.d.ts +2 -0
  139. package/lib/rules/no-redundant-annotation-assertion.js +402 -0
  140. package/lib/rules/no-redundant-param-types.js +16 -6
  141. package/lib/rules/no-redundant-this-params.d.ts +3 -0
  142. package/lib/rules/no-redundant-this-params.js +459 -0
  143. package/lib/rules/no-redundant-usecallback-wrapper.d.ts +8 -0
  144. package/lib/rules/no-redundant-usecallback-wrapper.js +328 -0
  145. package/lib/rules/no-res-error-status-in-onrequest.d.ts +4 -0
  146. package/lib/rules/no-res-error-status-in-onrequest.js +521 -0
  147. package/lib/rules/no-restricted-properties-fix.d.ts +12 -0
  148. package/lib/rules/no-restricted-properties-fix.js +173 -0
  149. package/lib/rules/no-separate-loading-state.d.ts +8 -0
  150. package/lib/rules/no-separate-loading-state.js +133 -0
  151. package/lib/rules/no-stale-state-across-await.d.ts +1 -0
  152. package/lib/rules/no-stale-state-across-await.js +185 -0
  153. package/lib/rules/no-static-constants-in-dynamic-files.d.ts +3 -0
  154. package/lib/rules/no-static-constants-in-dynamic-files.js +149 -0
  155. package/lib/rules/no-try-catch-already-exists-in-transaction.d.ts +1 -0
  156. package/lib/rules/no-try-catch-already-exists-in-transaction.js +342 -0
  157. package/lib/rules/no-type-assertion-returns.js +49 -49
  158. package/lib/rules/no-undefined-null-passthrough.js +217 -40
  159. package/lib/rules/no-unmemoized-memo-without-props.d.ts +8 -0
  160. package/lib/rules/no-unmemoized-memo-without-props.js +426 -0
  161. package/lib/rules/no-unnecessary-destructuring-rename.d.ts +2 -0
  162. package/lib/rules/no-unnecessary-destructuring-rename.js +347 -0
  163. package/lib/rules/no-unnecessary-destructuring.js +14 -4
  164. package/lib/rules/no-unnecessary-verb-suffix.js +2 -3
  165. package/lib/rules/no-unpinned-dependencies.js +36 -5
  166. package/lib/rules/no-unsafe-firestore-spread.js +8 -5
  167. package/lib/rules/no-unused-props.d.ts +5 -2
  168. package/lib/rules/no-unused-props.js +451 -112
  169. package/lib/rules/no-unused-usestate.js +6 -2
  170. package/lib/rules/no-useless-fragment.js +28 -2
  171. package/lib/rules/no-useless-usememo-primitives.d.ts +9 -0
  172. package/lib/rules/no-useless-usememo-primitives.js +393 -0
  173. package/lib/rules/no-usememo-for-pass-by-value.d.ts +13 -0
  174. package/lib/rules/no-usememo-for-pass-by-value.js +757 -0
  175. package/lib/rules/no-uuidv4-base62-as-key.js +18 -13
  176. package/lib/rules/omit-index-html.d.ts +2 -1
  177. package/lib/rules/omit-index-html.js +62 -7
  178. package/lib/rules/optimize-object-boolean-conditions.d.ts +1 -0
  179. package/lib/rules/optimize-object-boolean-conditions.js +236 -0
  180. package/lib/rules/parallelize-async-operations.d.ts +8 -0
  181. package/lib/rules/parallelize-async-operations.js +394 -0
  182. package/lib/rules/prefer-batch-operations.d.ts +1 -3
  183. package/lib/rules/prefer-batch-operations.js +228 -35
  184. package/lib/rules/prefer-block-comments-for-declarations.js +22 -7
  185. package/lib/rules/prefer-clone-deep.js +3 -3
  186. package/lib/rules/prefer-destructuring-no-class.d.ts +2 -1
  187. package/lib/rules/prefer-destructuring-no-class.js +244 -59
  188. package/lib/rules/prefer-docsetter-setall.d.ts +2 -0
  189. package/lib/rules/prefer-docsetter-setall.js +243 -0
  190. package/lib/rules/prefer-document-flattening.d.ts +4 -0
  191. package/lib/rules/prefer-document-flattening.js +239 -0
  192. package/lib/rules/prefer-field-paths-in-transforms.d.ts +7 -0
  193. package/lib/rules/prefer-field-paths-in-transforms.js +251 -0
  194. package/lib/rules/prefer-fragment-component.js +4 -4
  195. package/lib/rules/prefer-fragment-shorthand.js +2 -1
  196. package/lib/rules/prefer-getter-over-parameterless-method.d.ts +13 -0
  197. package/lib/rules/prefer-getter-over-parameterless-method.js +648 -0
  198. package/lib/rules/prefer-global-router-state-key.d.ts +7 -3
  199. package/lib/rules/prefer-global-router-state-key.js +305 -31
  200. package/lib/rules/prefer-memoized-props.d.ts +3 -0
  201. package/lib/rules/prefer-memoized-props.js +445 -0
  202. package/lib/rules/prefer-next-dynamic.d.ts +9 -0
  203. package/lib/rules/prefer-next-dynamic.js +357 -0
  204. package/lib/rules/prefer-nullish-coalescing-boolean-props.d.ts +1 -0
  205. package/lib/rules/prefer-nullish-coalescing-boolean-props.js +435 -0
  206. package/lib/rules/prefer-nullish-coalescing-override.d.ts +7 -0
  207. package/lib/rules/prefer-nullish-coalescing-override.js +188 -0
  208. package/lib/rules/prefer-params-over-parent-id.d.ts +5 -0
  209. package/lib/rules/prefer-params-over-parent-id.js +277 -0
  210. package/lib/rules/prefer-settings-object.js +23 -15
  211. package/lib/rules/prefer-type-alias-over-typeof-constant.d.ts +3 -0
  212. package/lib/rules/prefer-type-alias-over-typeof-constant.js +259 -0
  213. package/lib/rules/prefer-type-over-interface.js +7 -2
  214. package/lib/rules/prefer-url-tostring-over-tojson.d.ts +2 -0
  215. package/lib/rules/prefer-url-tostring-over-tojson.js +169 -0
  216. package/lib/rules/prefer-use-deep-compare-memo.d.ts +4 -0
  217. package/lib/rules/prefer-use-deep-compare-memo.js +431 -0
  218. package/lib/rules/prefer-usecallback-over-usememo-for-functions.js +25 -2
  219. package/lib/rules/prefer-usememo-over-useeffect-usestate.js +24 -5
  220. package/lib/rules/prefer-utility-function-over-private-static.js +34 -2
  221. package/lib/rules/prevent-children-clobber.d.ts +2 -0
  222. package/lib/rules/prevent-children-clobber.js +536 -0
  223. package/lib/rules/react-memoize-literals.d.ts +4 -0
  224. package/lib/rules/react-memoize-literals.js +495 -0
  225. package/lib/rules/react-usememo-should-be-component.d.ts +2 -1
  226. package/lib/rules/react-usememo-should-be-component.js +144 -78
  227. package/lib/rules/require-dynamic-firebase-imports.js +2 -1
  228. package/lib/rules/require-hooks-default-params.js +17 -16
  229. package/lib/rules/require-https-error-cause.d.ts +4 -0
  230. package/lib/rules/require-https-error-cause.js +136 -0
  231. package/lib/rules/require-https-error.js +43 -21
  232. package/lib/rules/require-image-optimized.js +2 -2
  233. package/lib/rules/require-memo.js +12 -2
  234. package/lib/rules/require-memoize-jsx-returners.d.ts +3 -0
  235. package/lib/rules/require-memoize-jsx-returners.js +485 -0
  236. package/lib/rules/require-usememo-object-literals.js +9 -4
  237. package/lib/rules/semantic-function-prefixes.js +38 -18
  238. package/lib/rules/sync-onwrite-name-func.js +5 -1
  239. package/lib/rules/test-file-location-enforcement.d.ts +1 -0
  240. package/lib/rules/test-file-location-enforcement.js +57 -0
  241. package/lib/rules/use-custom-link.js +6 -1
  242. package/lib/rules/use-custom-router.js +6 -1
  243. package/lib/rules/use-latest-callback.d.ts +3 -0
  244. package/lib/rules/use-latest-callback.js +271 -0
  245. package/lib/rules/vertically-group-related-functions.d.ts +16 -0
  246. package/lib/rules/vertically-group-related-functions.js +480 -0
  247. package/lib/utils/ASTHelpers.d.ts +13 -1
  248. package/lib/utils/ASTHelpers.js +34 -0
  249. package/lib/utils/createRule.js +1 -1
  250. package/lib/utils/getMethodName.d.ts +27 -0
  251. package/lib/utils/getMethodName.js +35 -0
  252. package/lib/utils/graph/ClassGraphSorterReadability.js +1 -1
  253. package/lib/utils/ruleTester.d.ts +1 -0
  254. package/lib/utils/ruleTester.js +4 -1
  255. package/lib/utils/tsTypeClassifier.d.ts +30 -0
  256. package/lib/utils/tsTypeClassifier.js +149 -0
  257. package/package.json +23 -14
  258. package/lib/rules/enforce-assertSafe-object-key.d.ts +0 -2
@@ -0,0 +1,521 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.requireHttpsErrorInOnRequestHandlers = void 0;
4
+ const utils_1 = require("@typescript-eslint/utils");
5
+ const createRule_1 = require("../utils/createRule");
6
+ const ASTHelpers_1 = require("../utils/ASTHelpers");
7
+ const ON_REQUEST_MODULE = 'functions/src/v2/https/onRequest';
8
+ const HTTPS_ERROR_IMPORT_PATH = 'functions/src/util/errors/HttpsError';
9
+ const isFunctionLike = (node) => !!node &&
10
+ (node.type === utils_1.AST_NODE_TYPES.FunctionDeclaration ||
11
+ node.type === utils_1.AST_NODE_TYPES.FunctionExpression ||
12
+ node.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression);
13
+ const isIdentifierWithName = (node, name) => !!node && node.type === utils_1.AST_NODE_TYPES.Identifier && node.name === name;
14
+ const unwrapTypedExpression = (expression) => {
15
+ let current = expression;
16
+ while (current.type === utils_1.AST_NODE_TYPES.TSAsExpression ||
17
+ current.type === utils_1.AST_NODE_TYPES.TSSatisfiesExpression ||
18
+ current.type === utils_1.AST_NODE_TYPES.TSTypeAssertion ||
19
+ current.type === utils_1.AST_NODE_TYPES.TSNonNullExpression ||
20
+ current.type === utils_1.AST_NODE_TYPES.TSInstantiationExpression) {
21
+ current = current.expression;
22
+ }
23
+ return current;
24
+ };
25
+ const getResponseParamNames = (node) => {
26
+ const RESPONSE_PARAM_KEYS = new Set(['res', 'response', 'resp']);
27
+ const extractName = (param) => {
28
+ if (param.type === utils_1.AST_NODE_TYPES.Identifier) {
29
+ return { name: param.name, isExplicitResponseBinding: false };
30
+ }
31
+ if (param.type === utils_1.AST_NODE_TYPES.AssignmentPattern &&
32
+ param.left.type === utils_1.AST_NODE_TYPES.Identifier) {
33
+ return { name: param.left.name, isExplicitResponseBinding: false };
34
+ }
35
+ if (param.type === utils_1.AST_NODE_TYPES.RestElement &&
36
+ param.argument.type === utils_1.AST_NODE_TYPES.Identifier) {
37
+ return { name: param.argument.name, isExplicitResponseBinding: false };
38
+ }
39
+ if (param.type === utils_1.AST_NODE_TYPES.ObjectPattern) {
40
+ for (const property of param.properties) {
41
+ if (property.type !== utils_1.AST_NODE_TYPES.Property || property.computed) {
42
+ continue;
43
+ }
44
+ const keyName = property.key.type === utils_1.AST_NODE_TYPES.Identifier
45
+ ? property.key.name
46
+ : property.key.type === utils_1.AST_NODE_TYPES.Literal &&
47
+ typeof property.key.value === 'string'
48
+ ? property.key.value
49
+ : null;
50
+ if (!keyName || !RESPONSE_PARAM_KEYS.has(keyName.toLowerCase())) {
51
+ continue;
52
+ }
53
+ if (property.value.type === utils_1.AST_NODE_TYPES.Identifier) {
54
+ return { name: property.value.name, isExplicitResponseBinding: true };
55
+ }
56
+ if (property.value.type === utils_1.AST_NODE_TYPES.AssignmentPattern &&
57
+ property.value.left.type === utils_1.AST_NODE_TYPES.Identifier) {
58
+ return {
59
+ name: property.value.left.name,
60
+ isExplicitResponseBinding: true,
61
+ };
62
+ }
63
+ }
64
+ return null;
65
+ }
66
+ if (param.type === utils_1.AST_NODE_TYPES.ArrayPattern) {
67
+ for (const element of param.elements) {
68
+ if (!element) {
69
+ continue;
70
+ }
71
+ const name = element.type === utils_1.AST_NODE_TYPES.Identifier
72
+ ? element.name
73
+ : element.type === utils_1.AST_NODE_TYPES.AssignmentPattern &&
74
+ element.left.type === utils_1.AST_NODE_TYPES.Identifier
75
+ ? element.left.name
76
+ : element.type === utils_1.AST_NODE_TYPES.RestElement &&
77
+ element.argument.type === utils_1.AST_NODE_TYPES.Identifier
78
+ ? element.argument.name
79
+ : null;
80
+ if (name && RESPONSE_PARAM_KEYS.has(name.toLowerCase())) {
81
+ return { name, isExplicitResponseBinding: false };
82
+ }
83
+ }
84
+ return null;
85
+ }
86
+ return null;
87
+ };
88
+ return node.params.reduce((acc, param, index) => {
89
+ const extracted = extractName(param);
90
+ if (!extracted)
91
+ return acc;
92
+ const lowered = extracted.name.toLowerCase();
93
+ const likelyResponse = extracted.isExplicitResponseBinding || RESPONSE_PARAM_KEYS.has(lowered);
94
+ /**
95
+ * In typical Express handlers, the response param is the second argument (req, res),
96
+ * so we treat the second parameter as a likely response name even if it doesn't match
97
+ * 'res', 'response', or 'resp'.
98
+ */
99
+ if (likelyResponse || index === 1) {
100
+ acc.push(extracted.name);
101
+ }
102
+ return acc;
103
+ }, []);
104
+ };
105
+ const getStatusCodeFromArg = (arg) => {
106
+ if (!arg || arg.type === utils_1.AST_NODE_TYPES.SpreadElement) {
107
+ return null;
108
+ }
109
+ if (arg.type === utils_1.AST_NODE_TYPES.Literal && typeof arg.value === 'number') {
110
+ return arg.value;
111
+ }
112
+ return null;
113
+ };
114
+ const mapStatusToHttpsCode = (status) => {
115
+ if (status === 400)
116
+ return 'invalid-argument';
117
+ if (status === 401)
118
+ return 'unauthenticated';
119
+ if (status === 403)
120
+ return 'permission-denied';
121
+ if (status === 404)
122
+ return 'not-found';
123
+ if (status === 409)
124
+ return 'already-exists';
125
+ if (status === 412 || status === 422)
126
+ return 'failed-precondition';
127
+ if (status === 429)
128
+ return 'resource-exhausted';
129
+ if (status === 499 || status === 504)
130
+ return 'deadline-exceeded';
131
+ if (status === 500)
132
+ return 'internal';
133
+ if (status === 502 || status === 503)
134
+ return 'unavailable';
135
+ return 'unknown';
136
+ };
137
+ const findNearestFunction = (ancestors) => {
138
+ for (let idx = ancestors.length - 1; idx >= 0; idx -= 1) {
139
+ const ancestor = ancestors[idx];
140
+ if (isFunctionLike(ancestor)) {
141
+ return ancestor;
142
+ }
143
+ }
144
+ return null;
145
+ };
146
+ const getMessageExample = (call) => {
147
+ const firstArg = call.arguments[0];
148
+ if (firstArg &&
149
+ firstArg.type === utils_1.AST_NODE_TYPES.Literal &&
150
+ typeof firstArg.value === 'string') {
151
+ return firstArg.value;
152
+ }
153
+ if (firstArg &&
154
+ firstArg.type === utils_1.AST_NODE_TYPES.TemplateLiteral &&
155
+ firstArg.expressions.length === 0) {
156
+ return firstArg.quasis.map((part) => part.value.raw).join('');
157
+ }
158
+ return 'provide a descriptive message';
159
+ };
160
+ const getObjectNameFromExpression = (node) => {
161
+ if (node.type === utils_1.AST_NODE_TYPES.Identifier) {
162
+ return node.name;
163
+ }
164
+ if (node.type === utils_1.AST_NODE_TYPES.MemberExpression &&
165
+ !node.computed &&
166
+ node.property.type === utils_1.AST_NODE_TYPES.Identifier &&
167
+ node.object.type === utils_1.AST_NODE_TYPES.Identifier) {
168
+ return `${node.object.name}.${node.property.name}`;
169
+ }
170
+ return null;
171
+ };
172
+ const getCalleeDisplayName = (callee) => {
173
+ if (callee.type === utils_1.AST_NODE_TYPES.Identifier) {
174
+ return callee.name;
175
+ }
176
+ if (callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
177
+ !callee.computed &&
178
+ callee.property.type === utils_1.AST_NODE_TYPES.Identifier) {
179
+ const objectName = getObjectNameFromExpression(callee.object);
180
+ if (objectName) {
181
+ return `${objectName}.${callee.property.name}`;
182
+ }
183
+ return callee.property.name;
184
+ }
185
+ return 'helper';
186
+ };
187
+ const getIdentifierNameFromExpression = (expression) => {
188
+ if (expression.type === utils_1.AST_NODE_TYPES.Identifier) {
189
+ return expression.name;
190
+ }
191
+ if (expression.type === utils_1.AST_NODE_TYPES.PrivateIdentifier) {
192
+ return null;
193
+ }
194
+ const unwrapped = unwrapTypedExpression(expression);
195
+ return unwrapped.type === utils_1.AST_NODE_TYPES.Identifier ? unwrapped.name : null;
196
+ };
197
+ const isStatusMember = (member, propertyName) => {
198
+ if (member.computed ||
199
+ member.property.type !== utils_1.AST_NODE_TYPES.Identifier ||
200
+ member.property.name !== propertyName) {
201
+ return null;
202
+ }
203
+ const responseName = getIdentifierNameFromExpression(member.object);
204
+ return responseName ? { responseName } : null;
205
+ };
206
+ const getStatusInfoFromStatusCall = (call) => {
207
+ if (call.callee.type === utils_1.AST_NODE_TYPES.MemberExpression) {
208
+ const statusMember = isStatusMember(call.callee, 'status');
209
+ if (!statusMember) {
210
+ return null;
211
+ }
212
+ return {
213
+ responseName: statusMember.responseName,
214
+ statusCode: getStatusCodeFromArg(call.arguments[0]),
215
+ };
216
+ }
217
+ return null;
218
+ };
219
+ const findStatusCall = (node) => {
220
+ if (node.callee.type === utils_1.AST_NODE_TYPES.MemberExpression) {
221
+ const sendStatusMember = isStatusMember(node.callee, 'sendStatus');
222
+ if (sendStatusMember) {
223
+ return {
224
+ responseName: sendStatusMember.responseName,
225
+ statusCode: getStatusCodeFromArg(node.arguments[0]),
226
+ method: 'sendStatus',
227
+ };
228
+ }
229
+ }
230
+ if (node.callee.type === utils_1.AST_NODE_TYPES.MemberExpression) {
231
+ const statusMember = isStatusMember(node.callee, 'status');
232
+ if (statusMember) {
233
+ const parent = node.parent;
234
+ if (parent &&
235
+ parent.type === utils_1.AST_NODE_TYPES.MemberExpression &&
236
+ parent.object === node &&
237
+ parent.parent &&
238
+ parent.parent.type === utils_1.AST_NODE_TYPES.CallExpression) {
239
+ return null;
240
+ }
241
+ return {
242
+ responseName: statusMember.responseName,
243
+ statusCode: getStatusCodeFromArg(node.arguments[0]),
244
+ method: 'status',
245
+ };
246
+ }
247
+ }
248
+ if (node.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
249
+ node.callee.object.type === utils_1.AST_NODE_TYPES.CallExpression) {
250
+ const statusInfo = getStatusInfoFromStatusCall(node.callee.object);
251
+ if (!statusInfo) {
252
+ return null;
253
+ }
254
+ const method = node.callee.property.type === utils_1.AST_NODE_TYPES.Identifier
255
+ ? node.callee.property.name
256
+ : 'status';
257
+ return {
258
+ ...statusInfo,
259
+ method,
260
+ };
261
+ }
262
+ return null;
263
+ };
264
+ const isErrorStatus = (statusCode) => statusCode === null || statusCode >= 400;
265
+ exports.requireHttpsErrorInOnRequestHandlers = (0, createRule_1.createRule)({
266
+ name: 'no-res-error-status-in-onrequest',
267
+ meta: {
268
+ type: 'problem',
269
+ docs: {
270
+ description: 'Forbid sending 4xx/5xx Express responses inside onRequest handlers; throw structured HttpsError instances instead so the wrapper can format, log, and map errors consistently.',
271
+ recommended: 'error',
272
+ },
273
+ schema: [],
274
+ messages: {
275
+ useHttpsErrorForStatus: "HTTP status {{statusCode}} sent via `{{responseName}}.{{method}}` inside an onRequest handler bypasses the centralized HttpsError pipeline → This creates observability gaps (errors won't appear in structured logs), inconsistent client error shapes (breaking API contracts), and missing error context (details object unavailable for debugging). → Throw new HttpsError('{{httpsCode}}', '{{messageExample}}', details) instead and import HttpsError from {{httpsErrorImport}} so the wrapper captures context, logs structured data, and returns consistent error responses.",
276
+ useHttpsErrorForComputedStatus: "Computed HTTP status sent via `{{responseName}}.{{method}}` inside an onRequest handler cannot be mapped to the expected HttpsError codes → This bypasses the HttpsError pipeline, leading to inconsistent error logging and unstandardized response shapes. → Choose an explicit HttpsError code (e.g., 'invalid-argument', 'permission-denied') and throw new HttpsError('{{httpsCode}}', '{{messageExample}}', details) after importing from {{httpsErrorImport}} so the wrapper can standardize logging, capture error context, and maintain a consistent API contract.",
277
+ useHttpsErrorForWrapper: "`{{calleeName}}` receives the Express response object `{{responseName}}` inside an onRequest handler, which writes HTTP errors directly and skips the HttpsError wrapper → This results in missing structured logs, inconsistent error responses for clients, and makes debugging difficult due to lost error context. → Refactor this helper to throw new HttpsError('{{httpsCode}}', '{{messageExample}}', details) (import from {{httpsErrorImport}}) so the onRequest wrapper can handle formatting, status mapping, and centralized logging.",
278
+ },
279
+ },
280
+ defaultOptions: [],
281
+ create(context) {
282
+ const onRequestIdentifiers = new Set();
283
+ const onRequestNamespaces = new Set();
284
+ const handlerFunctions = new Set();
285
+ const responseNamesByFunction = new Map();
286
+ const pendingViolations = [];
287
+ const recordFunction = (node) => {
288
+ responseNamesByFunction.set(node, getResponseParamNames(node));
289
+ };
290
+ const extractFunctionFromDefinition = (def) => {
291
+ const defNode = def.node;
292
+ if (isFunctionLike(defNode)) {
293
+ return defNode;
294
+ }
295
+ if (defNode.type === utils_1.AST_NODE_TYPES.VariableDeclarator && defNode.init) {
296
+ const initializer = unwrapTypedExpression(defNode.init);
297
+ if (isFunctionLike(initializer)) {
298
+ return initializer;
299
+ }
300
+ }
301
+ return null;
302
+ };
303
+ const resolveFunctionFromIdentifier = (identifier) => {
304
+ let scope = context.getScope();
305
+ while (scope) {
306
+ const variable = scope.set.get(identifier.name) ??
307
+ scope.variables.find((candidate) => candidate.name === identifier.name);
308
+ if (variable) {
309
+ for (const def of variable.defs) {
310
+ const fn = extractFunctionFromDefinition(def);
311
+ if (fn) {
312
+ return fn;
313
+ }
314
+ }
315
+ }
316
+ scope = scope.upper;
317
+ }
318
+ return null;
319
+ };
320
+ const isDirectOnRequestHandler = (fn) => {
321
+ return handlerFunctions.has(fn);
322
+ };
323
+ function findInFunctionChain(startNode, callback) {
324
+ let current = startNode;
325
+ while (current) {
326
+ if (isFunctionLike(current)) {
327
+ const result = callback(current);
328
+ if (result) {
329
+ return result;
330
+ }
331
+ }
332
+ current = current.parent;
333
+ }
334
+ return null;
335
+ }
336
+ const isWithinOnRequest = (fn) => !!findInFunctionChain(fn, (current) => isDirectOnRequestHandler(current));
337
+ const hasResponseBinding = (fn, responseName) => !!findInFunctionChain(fn, (current) => {
338
+ const params = responseNamesByFunction.get(current) ?? [];
339
+ return params.includes(responseName);
340
+ });
341
+ const findResponseIdentifierInArgs = (fn, call) => findInFunctionChain(fn, (current) => {
342
+ const params = responseNamesByFunction.get(current) ?? [];
343
+ return params.find((name) => call.arguments.some((arg) => {
344
+ const unwrapped = unwrapTypedExpression(arg);
345
+ return isIdentifierWithName(unwrapped, name);
346
+ }));
347
+ });
348
+ const handleOnRequestTracking = (node) => {
349
+ const isOnRequestCallee = (callee) => {
350
+ if (callee.type === utils_1.AST_NODE_TYPES.Identifier) {
351
+ return onRequestIdentifiers.has(callee.name);
352
+ }
353
+ if (callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
354
+ !callee.computed &&
355
+ callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
356
+ callee.property.name === 'onRequest' &&
357
+ callee.object.type === utils_1.AST_NODE_TYPES.Identifier) {
358
+ return onRequestNamespaces.has(callee.object.name);
359
+ }
360
+ return false;
361
+ };
362
+ if (isOnRequestCallee(node.callee)) {
363
+ const handlerArg = [...node.arguments]
364
+ .reverse()
365
+ .find((arg) => arg.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
366
+ arg.type === utils_1.AST_NODE_TYPES.FunctionExpression ||
367
+ arg.type === utils_1.AST_NODE_TYPES.Identifier ||
368
+ arg.type === utils_1.AST_NODE_TYPES.TSAsExpression ||
369
+ arg.type === utils_1.AST_NODE_TYPES.TSSatisfiesExpression ||
370
+ arg.type === utils_1.AST_NODE_TYPES.TSTypeAssertion ||
371
+ arg.type === utils_1.AST_NODE_TYPES.TSNonNullExpression ||
372
+ arg.type === utils_1.AST_NODE_TYPES.TSInstantiationExpression);
373
+ if (handlerArg) {
374
+ const expression = unwrapTypedExpression(handlerArg);
375
+ if (isFunctionLike(expression)) {
376
+ handlerFunctions.add(expression);
377
+ }
378
+ else if (expression.type === utils_1.AST_NODE_TYPES.Identifier) {
379
+ const resolvedHandler = resolveFunctionFromIdentifier(expression);
380
+ if (resolvedHandler) {
381
+ handlerFunctions.add(resolvedHandler);
382
+ }
383
+ }
384
+ }
385
+ }
386
+ };
387
+ const handleStatusViolation = (node) => {
388
+ const statusInfo = findStatusCall(node);
389
+ if (!statusInfo) {
390
+ return false;
391
+ }
392
+ const { responseName, statusCode, method } = statusInfo;
393
+ if (!isErrorStatus(statusCode)) {
394
+ return true;
395
+ }
396
+ const functionNode = findNearestFunction(ASTHelpers_1.ASTHelpers.getAncestors(context, node));
397
+ if (!functionNode) {
398
+ return true;
399
+ }
400
+ if (!hasResponseBinding(functionNode, responseName)) {
401
+ return true;
402
+ }
403
+ pendingViolations.push({
404
+ node,
405
+ functionNode,
406
+ responseName,
407
+ kind: statusCode === null ? 'computedStatus' : 'status',
408
+ statusCode,
409
+ method,
410
+ messageExample: getMessageExample(node),
411
+ });
412
+ return true;
413
+ };
414
+ const handleWrapperViolation = (node) => {
415
+ const functionNode = findNearestFunction(ASTHelpers_1.ASTHelpers.getAncestors(context, node));
416
+ if (!functionNode) {
417
+ return;
418
+ }
419
+ const responseName = findResponseIdentifierInArgs(functionNode, node);
420
+ /**
421
+ * Skip direct method calls on the response object (e.g., res.send(res)) so the
422
+ * wrapper detection only reports helpers that receive the response object as
423
+ * an argument.
424
+ */
425
+ const calleeUsesResponseDirectly = node.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
426
+ node.callee.object.type === utils_1.AST_NODE_TYPES.Identifier &&
427
+ responseName === node.callee.object.name;
428
+ if (!responseName || calleeUsesResponseDirectly) {
429
+ return;
430
+ }
431
+ pendingViolations.push({
432
+ node,
433
+ functionNode,
434
+ responseName,
435
+ kind: 'wrapper',
436
+ statusCode: null,
437
+ method: 'wrapper',
438
+ messageExample: getMessageExample(node),
439
+ calleeName: getCalleeDisplayName(node.callee),
440
+ });
441
+ };
442
+ return {
443
+ ImportDeclaration(node) {
444
+ if (node.source.value !== ON_REQUEST_MODULE) {
445
+ return;
446
+ }
447
+ node.specifiers.forEach((specifier) => {
448
+ if (specifier.type === utils_1.AST_NODE_TYPES.ImportDefaultSpecifier) {
449
+ onRequestIdentifiers.add(specifier.local.name);
450
+ }
451
+ else if (specifier.type === utils_1.AST_NODE_TYPES.ImportSpecifier &&
452
+ specifier.imported.type === utils_1.AST_NODE_TYPES.Identifier &&
453
+ specifier.imported.name === 'onRequest') {
454
+ onRequestIdentifiers.add(specifier.local.name);
455
+ }
456
+ else if (specifier.type === utils_1.AST_NODE_TYPES.ImportNamespaceSpecifier) {
457
+ onRequestNamespaces.add(specifier.local.name);
458
+ }
459
+ });
460
+ },
461
+ FunctionDeclaration: recordFunction,
462
+ FunctionExpression: recordFunction,
463
+ ArrowFunctionExpression: recordFunction,
464
+ CallExpression(node) {
465
+ handleOnRequestTracking(node);
466
+ if (handleStatusViolation(node)) {
467
+ return;
468
+ }
469
+ handleWrapperViolation(node);
470
+ },
471
+ 'Program:exit'() {
472
+ pendingViolations.forEach((violation) => {
473
+ if (!isWithinOnRequest(violation.functionNode)) {
474
+ return;
475
+ }
476
+ const httpsCode = mapStatusToHttpsCode(violation.statusCode);
477
+ const commonData = {
478
+ responseName: violation.responseName,
479
+ messageExample: violation.messageExample,
480
+ httpsCode,
481
+ httpsErrorImport: HTTPS_ERROR_IMPORT_PATH,
482
+ };
483
+ if (violation.kind === 'wrapper') {
484
+ context.report({
485
+ node: violation.node,
486
+ messageId: 'useHttpsErrorForWrapper',
487
+ data: {
488
+ ...commonData,
489
+ calleeName: violation.calleeName ?? 'helper',
490
+ },
491
+ });
492
+ return;
493
+ }
494
+ if (violation.kind === 'computedStatus') {
495
+ context.report({
496
+ node: violation.node,
497
+ messageId: 'useHttpsErrorForComputedStatus',
498
+ data: {
499
+ ...commonData,
500
+ method: violation.method,
501
+ },
502
+ });
503
+ return;
504
+ }
505
+ context.report({
506
+ node: violation.node,
507
+ messageId: 'useHttpsErrorForStatus',
508
+ data: {
509
+ ...commonData,
510
+ method: violation.method,
511
+ statusCode: violation.statusCode !== null
512
+ ? violation.statusCode.toString()
513
+ : 'computed status',
514
+ },
515
+ });
516
+ });
517
+ },
518
+ };
519
+ },
520
+ });
521
+ //# sourceMappingURL=no-res-error-status-in-onrequest.js.map
@@ -0,0 +1,12 @@
1
+ /**
2
+ * This rule is a wrapper around the core ESLint no-restricted-properties rule
3
+ * that adds special handling for Object.keys() and Object.values() results.
4
+ * It prevents false positives when accessing standard array properties/methods
5
+ * on the arrays returned by Object.keys() and Object.values().
6
+ */
7
+ export declare const noRestrictedPropertiesFix: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"restrictedProperty", [{
8
+ object?: string | undefined;
9
+ property?: string | undefined;
10
+ message?: string | undefined;
11
+ allowObjects?: string[] | undefined;
12
+ }[]], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;