@empline/preflight 1.1.11 → 1.1.13

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 (709) hide show
  1. package/dist/checks/consolidated/auth-storage-state.d.ts +3 -0
  2. package/dist/checks/consolidated/auth-storage-state.d.ts.map +1 -0
  3. package/dist/checks/consolidated/auth-storage-state.js +146 -0
  4. package/dist/checks/consolidated/auth-storage-state.js.map +1 -0
  5. package/dist/checks/consolidated/business.d.ts +50 -0
  6. package/dist/checks/consolidated/business.d.ts.map +1 -0
  7. package/dist/checks/consolidated/business.js +252 -0
  8. package/dist/checks/consolidated/business.js.map +1 -0
  9. package/dist/checks/consolidated/caching-strategy.d.ts +104 -0
  10. package/dist/checks/consolidated/caching-strategy.d.ts.map +1 -0
  11. package/dist/checks/consolidated/caching-strategy.js +725 -0
  12. package/dist/checks/consolidated/caching-strategy.js.map +1 -0
  13. package/dist/checks/consolidated/code-quality.d.ts +83 -0
  14. package/dist/checks/consolidated/code-quality.d.ts.map +1 -0
  15. package/dist/checks/consolidated/code-quality.js +445 -0
  16. package/dist/checks/consolidated/code-quality.js.map +1 -0
  17. package/dist/checks/consolidated/console-statements.d.ts +32 -0
  18. package/dist/checks/consolidated/console-statements.d.ts.map +1 -0
  19. package/dist/checks/consolidated/console-statements.js +304 -0
  20. package/dist/checks/consolidated/console-statements.js.map +1 -0
  21. package/dist/checks/consolidated/css-advanced-validation.d.ts +24 -0
  22. package/dist/checks/consolidated/css-advanced-validation.d.ts.map +1 -0
  23. package/dist/checks/consolidated/css-advanced-validation.js +415 -0
  24. package/dist/checks/consolidated/css-advanced-validation.js.map +1 -0
  25. package/dist/checks/consolidated/css-organization.d.ts +14 -0
  26. package/dist/checks/consolidated/css-organization.d.ts.map +1 -0
  27. package/dist/checks/consolidated/css-organization.js +432 -0
  28. package/dist/checks/consolidated/css-organization.js.map +1 -0
  29. package/dist/checks/consolidated/css-runtime-validation.d.ts +22 -0
  30. package/dist/checks/consolidated/css-runtime-validation.d.ts.map +1 -0
  31. package/dist/checks/consolidated/css-runtime-validation.js +330 -0
  32. package/dist/checks/consolidated/css-runtime-validation.js.map +1 -0
  33. package/dist/checks/consolidated/css-variable-validation.d.ts +17 -0
  34. package/dist/checks/consolidated/css-variable-validation.d.ts.map +1 -0
  35. package/dist/checks/consolidated/css-variable-validation.js +412 -0
  36. package/dist/checks/consolidated/css-variable-validation.js.map +1 -0
  37. package/dist/checks/consolidated/dark-mode-consistency.d.ts +23 -0
  38. package/dist/checks/consolidated/dark-mode-consistency.d.ts.map +1 -0
  39. package/dist/checks/consolidated/dark-mode-consistency.js +291 -0
  40. package/dist/checks/consolidated/dark-mode-consistency.js.map +1 -0
  41. package/dist/checks/consolidated/database.d.ts +95 -0
  42. package/dist/checks/consolidated/database.d.ts.map +1 -0
  43. package/dist/checks/consolidated/database.js +427 -0
  44. package/dist/checks/consolidated/database.js.map +1 -0
  45. package/dist/checks/consolidated/e2e-checks.d.ts +52 -0
  46. package/dist/checks/consolidated/e2e-checks.d.ts.map +1 -0
  47. package/dist/checks/consolidated/e2e-checks.js +157 -0
  48. package/dist/checks/consolidated/e2e-checks.js.map +1 -0
  49. package/dist/checks/consolidated/e2e-regression-coverage.d.ts +14 -0
  50. package/dist/checks/consolidated/e2e-regression-coverage.d.ts.map +1 -0
  51. package/dist/checks/consolidated/e2e-regression-coverage.js +151 -0
  52. package/dist/checks/consolidated/e2e-regression-coverage.js.map +1 -0
  53. package/dist/checks/consolidated/e2e-validation.d.ts +137 -0
  54. package/dist/checks/consolidated/e2e-validation.d.ts.map +1 -0
  55. package/dist/checks/consolidated/e2e-validation.js +1001 -0
  56. package/dist/checks/consolidated/e2e-validation.js.map +1 -0
  57. package/dist/checks/consolidated/enterprise-baseline.d.ts +9 -0
  58. package/dist/checks/consolidated/enterprise-baseline.d.ts.map +1 -0
  59. package/dist/checks/consolidated/enterprise-baseline.js +277 -0
  60. package/dist/checks/consolidated/enterprise-baseline.js.map +1 -0
  61. package/dist/checks/consolidated/generate-pageload-config.d.ts +6 -0
  62. package/dist/checks/consolidated/generate-pageload-config.d.ts.map +1 -0
  63. package/dist/checks/consolidated/generate-pageload-config.js +161 -0
  64. package/dist/checks/consolidated/generate-pageload-config.js.map +1 -0
  65. package/dist/checks/consolidated/hardened-checks.d.ts +276 -0
  66. package/dist/checks/consolidated/hardened-checks.d.ts.map +1 -0
  67. package/dist/checks/consolidated/hardened-checks.js +3056 -0
  68. package/dist/checks/consolidated/hardened-checks.js.map +1 -0
  69. package/dist/checks/consolidated/homepage-ux.d.ts +12 -0
  70. package/dist/checks/consolidated/homepage-ux.d.ts.map +1 -0
  71. package/dist/checks/consolidated/homepage-ux.js +242 -0
  72. package/dist/checks/consolidated/homepage-ux.js.map +1 -0
  73. package/dist/checks/consolidated/images.d.ts +76 -0
  74. package/dist/checks/consolidated/images.d.ts.map +1 -0
  75. package/dist/checks/consolidated/images.js +311 -0
  76. package/dist/checks/consolidated/images.js.map +1 -0
  77. package/dist/checks/consolidated/import-cycles.d.ts +63 -0
  78. package/dist/checks/consolidated/import-cycles.d.ts.map +1 -0
  79. package/dist/checks/consolidated/import-cycles.js +291 -0
  80. package/dist/checks/consolidated/import-cycles.js.map +1 -0
  81. package/dist/checks/consolidated/imports.d.ts +112 -0
  82. package/dist/checks/consolidated/imports.d.ts.map +1 -0
  83. package/dist/checks/consolidated/imports.js +977 -0
  84. package/dist/checks/consolidated/imports.js.map +1 -0
  85. package/dist/checks/consolidated/inline-style-conflicts.d.ts +21 -0
  86. package/dist/checks/consolidated/inline-style-conflicts.d.ts.map +1 -0
  87. package/dist/checks/consolidated/inline-style-conflicts.js +300 -0
  88. package/dist/checks/consolidated/inline-style-conflicts.js.map +1 -0
  89. package/dist/checks/consolidated/lib-organization.d.ts +12 -0
  90. package/dist/checks/consolidated/lib-organization.d.ts.map +1 -0
  91. package/dist/checks/consolidated/lib-organization.js +419 -0
  92. package/dist/checks/consolidated/lib-organization.js.map +1 -0
  93. package/dist/checks/consolidated/n-plus-one.d.ts +63 -0
  94. package/dist/checks/consolidated/n-plus-one.d.ts.map +1 -0
  95. package/dist/checks/consolidated/n-plus-one.js +331 -0
  96. package/dist/checks/consolidated/n-plus-one.js.map +1 -0
  97. package/dist/checks/consolidated/nextjs.d.ts +51 -0
  98. package/dist/checks/consolidated/nextjs.d.ts.map +1 -0
  99. package/dist/checks/consolidated/nextjs.js +205 -0
  100. package/dist/checks/consolidated/nextjs.js.map +1 -0
  101. package/dist/checks/consolidated/organization.d.ts +54 -0
  102. package/dist/checks/consolidated/organization.d.ts.map +1 -0
  103. package/dist/checks/consolidated/organization.js +158 -0
  104. package/dist/checks/consolidated/organization.js.map +1 -0
  105. package/dist/checks/consolidated/pageload.d.ts +12 -0
  106. package/dist/checks/consolidated/pageload.d.ts.map +1 -0
  107. package/dist/checks/consolidated/pageload.js +138 -0
  108. package/dist/checks/consolidated/pageload.js.map +1 -0
  109. package/dist/checks/consolidated/performance.d.ts +112 -0
  110. package/dist/checks/consolidated/performance.d.ts.map +1 -0
  111. package/dist/checks/consolidated/performance.js +1546 -0
  112. package/dist/checks/consolidated/performance.js.map +1 -0
  113. package/dist/checks/consolidated/quality.d.ts +52 -0
  114. package/dist/checks/consolidated/quality.d.ts.map +1 -0
  115. package/dist/checks/consolidated/quality.js +253 -0
  116. package/dist/checks/consolidated/quality.js.map +1 -0
  117. package/dist/checks/consolidated/react.d.ts +48 -0
  118. package/dist/checks/consolidated/react.d.ts.map +1 -0
  119. package/dist/checks/consolidated/react.js +203 -0
  120. package/dist/checks/consolidated/react.js.map +1 -0
  121. package/dist/checks/consolidated/regression-hygiene.d.ts +17 -0
  122. package/dist/checks/consolidated/regression-hygiene.d.ts.map +1 -0
  123. package/dist/checks/consolidated/regression-hygiene.js +242 -0
  124. package/dist/checks/consolidated/regression-hygiene.js.map +1 -0
  125. package/dist/checks/consolidated/regression.d.ts +20 -0
  126. package/dist/checks/consolidated/regression.d.ts.map +1 -0
  127. package/dist/checks/consolidated/regression.js +121 -0
  128. package/dist/checks/consolidated/regression.js.map +1 -0
  129. package/dist/checks/consolidated/runtime.d.ts +53 -0
  130. package/dist/checks/consolidated/runtime.d.ts.map +1 -0
  131. package/dist/checks/consolidated/runtime.js +160 -0
  132. package/dist/checks/consolidated/runtime.js.map +1 -0
  133. package/dist/checks/consolidated/script-performance.d.ts +17 -0
  134. package/dist/checks/consolidated/script-performance.d.ts.map +1 -0
  135. package/dist/checks/consolidated/script-performance.js +137 -0
  136. package/dist/checks/consolidated/script-performance.js.map +1 -0
  137. package/dist/checks/consolidated/security.d.ts +78 -0
  138. package/dist/checks/consolidated/security.d.ts.map +1 -0
  139. package/dist/checks/consolidated/security.js +404 -0
  140. package/dist/checks/consolidated/security.js.map +1 -0
  141. package/dist/checks/consolidated/seo.d.ts +31 -0
  142. package/dist/checks/consolidated/seo.d.ts.map +1 -0
  143. package/dist/checks/consolidated/seo.js +1438 -0
  144. package/dist/checks/consolidated/seo.js.map +1 -0
  145. package/dist/checks/consolidated/sx-prop-deprecation.d.ts +22 -0
  146. package/dist/checks/consolidated/sx-prop-deprecation.d.ts.map +1 -0
  147. package/dist/checks/consolidated/sx-prop-deprecation.js +280 -0
  148. package/dist/checks/consolidated/sx-prop-deprecation.js.map +1 -0
  149. package/dist/checks/consolidated/tailwind-class-validation.d.ts +25 -0
  150. package/dist/checks/consolidated/tailwind-class-validation.d.ts.map +1 -0
  151. package/dist/checks/consolidated/tailwind-class-validation.js +533 -0
  152. package/dist/checks/consolidated/tailwind-class-validation.js.map +1 -0
  153. package/dist/checks/consolidated/testing.d.ts +54 -0
  154. package/dist/checks/consolidated/testing.d.ts.map +1 -0
  155. package/dist/checks/consolidated/testing.js +163 -0
  156. package/dist/checks/consolidated/testing.js.map +1 -0
  157. package/dist/checks/consolidated/typescript.d.ts +3 -0
  158. package/dist/checks/consolidated/typescript.d.ts.map +1 -0
  159. package/dist/checks/consolidated/typescript.js +31 -0
  160. package/dist/checks/consolidated/typescript.js.map +1 -0
  161. package/dist/checks/consolidated/ui-accessibility-advanced.d.ts +104 -0
  162. package/dist/checks/consolidated/ui-accessibility-advanced.d.ts.map +1 -0
  163. package/dist/checks/consolidated/ui-accessibility-advanced.js +689 -0
  164. package/dist/checks/consolidated/ui-accessibility-advanced.js.map +1 -0
  165. package/dist/checks/consolidated/ui-accessibility.d.ts +121 -0
  166. package/dist/checks/consolidated/ui-accessibility.d.ts.map +1 -0
  167. package/dist/checks/consolidated/ui-accessibility.js +776 -0
  168. package/dist/checks/consolidated/ui-accessibility.js.map +1 -0
  169. package/dist/checks/consolidated/ui-advanced-spacing.d.ts +142 -0
  170. package/dist/checks/consolidated/ui-advanced-spacing.d.ts.map +1 -0
  171. package/dist/checks/consolidated/ui-advanced-spacing.js +1220 -0
  172. package/dist/checks/consolidated/ui-advanced-spacing.js.map +1 -0
  173. package/dist/checks/consolidated/ui-animation-duration.d.ts +108 -0
  174. package/dist/checks/consolidated/ui-animation-duration.d.ts.map +1 -0
  175. package/dist/checks/consolidated/ui-animation-duration.js +531 -0
  176. package/dist/checks/consolidated/ui-animation-duration.js.map +1 -0
  177. package/dist/checks/consolidated/ui-border-radius.d.ts +90 -0
  178. package/dist/checks/consolidated/ui-border-radius.d.ts.map +1 -0
  179. package/dist/checks/consolidated/ui-border-radius.js +519 -0
  180. package/dist/checks/consolidated/ui-border-radius.js.map +1 -0
  181. package/dist/checks/consolidated/ui-buttons.d.ts +32 -0
  182. package/dist/checks/consolidated/ui-buttons.d.ts.map +1 -0
  183. package/dist/checks/consolidated/ui-buttons.js +481 -0
  184. package/dist/checks/consolidated/ui-buttons.js.map +1 -0
  185. package/dist/checks/consolidated/ui-cards.d.ts +29 -0
  186. package/dist/checks/consolidated/ui-cards.d.ts.map +1 -0
  187. package/dist/checks/consolidated/ui-cards.js +504 -0
  188. package/dist/checks/consolidated/ui-cards.js.map +1 -0
  189. package/dist/checks/consolidated/ui-checks.d.ts +48 -0
  190. package/dist/checks/consolidated/ui-checks.d.ts.map +1 -0
  191. package/dist/checks/consolidated/ui-checks.js +264 -0
  192. package/dist/checks/consolidated/ui-checks.js.map +1 -0
  193. package/dist/checks/consolidated/ui-cleanup.d.ts +81 -0
  194. package/dist/checks/consolidated/ui-cleanup.d.ts.map +1 -0
  195. package/dist/checks/consolidated/ui-cleanup.js +650 -0
  196. package/dist/checks/consolidated/ui-cleanup.js.map +1 -0
  197. package/dist/checks/consolidated/ui-components.d.ts +255 -0
  198. package/dist/checks/consolidated/ui-components.d.ts.map +1 -0
  199. package/dist/checks/consolidated/ui-components.js +2008 -0
  200. package/dist/checks/consolidated/ui-components.js.map +1 -0
  201. package/dist/checks/consolidated/ui-consistency-advanced.d.ts +130 -0
  202. package/dist/checks/consolidated/ui-consistency-advanced.d.ts.map +1 -0
  203. package/dist/checks/consolidated/ui-consistency-advanced.js +982 -0
  204. package/dist/checks/consolidated/ui-consistency-advanced.js.map +1 -0
  205. package/dist/checks/consolidated/ui-consistency-comprehensive.d.ts +30 -0
  206. package/dist/checks/consolidated/ui-consistency-comprehensive.d.ts.map +1 -0
  207. package/dist/checks/consolidated/ui-consistency-comprehensive.js +1018 -0
  208. package/dist/checks/consolidated/ui-consistency-comprehensive.js.map +1 -0
  209. package/dist/checks/consolidated/ui-consistency-extended.d.ts +26 -0
  210. package/dist/checks/consolidated/ui-consistency-extended.d.ts.map +1 -0
  211. package/dist/checks/consolidated/ui-consistency-extended.js +606 -0
  212. package/dist/checks/consolidated/ui-consistency-extended.js.map +1 -0
  213. package/dist/checks/consolidated/ui-data-display.d.ts +103 -0
  214. package/dist/checks/consolidated/ui-data-display.d.ts.map +1 -0
  215. package/dist/checks/consolidated/ui-data-display.js +740 -0
  216. package/dist/checks/consolidated/ui-data-display.js.map +1 -0
  217. package/dist/checks/consolidated/ui-deprecated.d.ts +22 -0
  218. package/dist/checks/consolidated/ui-deprecated.d.ts.map +1 -0
  219. package/dist/checks/consolidated/ui-deprecated.js +336 -0
  220. package/dist/checks/consolidated/ui-deprecated.js.map +1 -0
  221. package/dist/checks/consolidated/ui-empty-null-states.d.ts +90 -0
  222. package/dist/checks/consolidated/ui-empty-null-states.d.ts.map +1 -0
  223. package/dist/checks/consolidated/ui-empty-null-states.js +511 -0
  224. package/dist/checks/consolidated/ui-empty-null-states.js.map +1 -0
  225. package/dist/checks/consolidated/ui-error-states.d.ts +99 -0
  226. package/dist/checks/consolidated/ui-error-states.d.ts.map +1 -0
  227. package/dist/checks/consolidated/ui-error-states.js +694 -0
  228. package/dist/checks/consolidated/ui-error-states.js.map +1 -0
  229. package/dist/checks/consolidated/ui-feedback-confirmations.d.ts +90 -0
  230. package/dist/checks/consolidated/ui-feedback-confirmations.d.ts.map +1 -0
  231. package/dist/checks/consolidated/ui-feedback-confirmations.js +596 -0
  232. package/dist/checks/consolidated/ui-feedback-confirmations.js.map +1 -0
  233. package/dist/checks/consolidated/ui-forms.d.ts +32 -0
  234. package/dist/checks/consolidated/ui-forms.d.ts.map +1 -0
  235. package/dist/checks/consolidated/ui-forms.js +568 -0
  236. package/dist/checks/consolidated/ui-forms.js.map +1 -0
  237. package/dist/checks/consolidated/ui-gradient-shadow.d.ts +90 -0
  238. package/dist/checks/consolidated/ui-gradient-shadow.d.ts.map +1 -0
  239. package/dist/checks/consolidated/ui-gradient-shadow.js +568 -0
  240. package/dist/checks/consolidated/ui-gradient-shadow.js.map +1 -0
  241. package/dist/checks/consolidated/ui-grid-responsive.d.ts +27 -0
  242. package/dist/checks/consolidated/ui-grid-responsive.d.ts.map +1 -0
  243. package/dist/checks/consolidated/ui-grid-responsive.js +441 -0
  244. package/dist/checks/consolidated/ui-grid-responsive.js.map +1 -0
  245. package/dist/checks/consolidated/ui-icon-size-tokens.d.ts +104 -0
  246. package/dist/checks/consolidated/ui-icon-size-tokens.d.ts.map +1 -0
  247. package/dist/checks/consolidated/ui-icon-size-tokens.js +514 -0
  248. package/dist/checks/consolidated/ui-icon-size-tokens.js.map +1 -0
  249. package/dist/checks/consolidated/ui-iconography.d.ts +90 -0
  250. package/dist/checks/consolidated/ui-iconography.d.ts.map +1 -0
  251. package/dist/checks/consolidated/ui-iconography.js +565 -0
  252. package/dist/checks/consolidated/ui-iconography.js.map +1 -0
  253. package/dist/checks/consolidated/ui-interactive-states.d.ts +240 -0
  254. package/dist/checks/consolidated/ui-interactive-states.d.ts.map +1 -0
  255. package/dist/checks/consolidated/ui-interactive-states.js +2474 -0
  256. package/dist/checks/consolidated/ui-interactive-states.js.map +1 -0
  257. package/dist/checks/consolidated/ui-layout.d.ts +256 -0
  258. package/dist/checks/consolidated/ui-layout.d.ts.map +1 -0
  259. package/dist/checks/consolidated/ui-layout.js +1371 -0
  260. package/dist/checks/consolidated/ui-layout.js.map +1 -0
  261. package/dist/checks/consolidated/ui-loading-skeletons.d.ts +11 -0
  262. package/dist/checks/consolidated/ui-loading-skeletons.d.ts.map +1 -0
  263. package/dist/checks/consolidated/ui-loading-skeletons.js +145 -0
  264. package/dist/checks/consolidated/ui-loading-skeletons.js.map +1 -0
  265. package/dist/checks/consolidated/ui-loading-state-skeletons.d.ts +9 -0
  266. package/dist/checks/consolidated/ui-loading-state-skeletons.d.ts.map +1 -0
  267. package/dist/checks/consolidated/ui-loading-state-skeletons.js +125 -0
  268. package/dist/checks/consolidated/ui-loading-state-skeletons.js.map +1 -0
  269. package/dist/checks/consolidated/ui-media.d.ts +74 -0
  270. package/dist/checks/consolidated/ui-media.d.ts.map +1 -0
  271. package/dist/checks/consolidated/ui-media.js +408 -0
  272. package/dist/checks/consolidated/ui-media.js.map +1 -0
  273. package/dist/checks/consolidated/ui-micro-interactions.d.ts +107 -0
  274. package/dist/checks/consolidated/ui-micro-interactions.d.ts.map +1 -0
  275. package/dist/checks/consolidated/ui-micro-interactions.js +825 -0
  276. package/dist/checks/consolidated/ui-micro-interactions.js.map +1 -0
  277. package/dist/checks/consolidated/ui-microcopy-consistency.d.ts +114 -0
  278. package/dist/checks/consolidated/ui-microcopy-consistency.d.ts.map +1 -0
  279. package/dist/checks/consolidated/ui-microcopy-consistency.js +566 -0
  280. package/dist/checks/consolidated/ui-microcopy-consistency.js.map +1 -0
  281. package/dist/checks/consolidated/ui-mobile-ux.d.ts +251 -0
  282. package/dist/checks/consolidated/ui-mobile-ux.d.ts.map +1 -0
  283. package/dist/checks/consolidated/ui-mobile-ux.js +2212 -0
  284. package/dist/checks/consolidated/ui-mobile-ux.js.map +1 -0
  285. package/dist/checks/consolidated/ui-motion-accessibility.d.ts +93 -0
  286. package/dist/checks/consolidated/ui-motion-accessibility.d.ts.map +1 -0
  287. package/dist/checks/consolidated/ui-motion-accessibility.js +450 -0
  288. package/dist/checks/consolidated/ui-motion-accessibility.js.map +1 -0
  289. package/dist/checks/consolidated/ui-navigation.d.ts +85 -0
  290. package/dist/checks/consolidated/ui-navigation.d.ts.map +1 -0
  291. package/dist/checks/consolidated/ui-navigation.js +673 -0
  292. package/dist/checks/consolidated/ui-navigation.js.map +1 -0
  293. package/dist/checks/consolidated/ui-patterns.d.ts +174 -0
  294. package/dist/checks/consolidated/ui-patterns.d.ts.map +1 -0
  295. package/dist/checks/consolidated/ui-patterns.js +1532 -0
  296. package/dist/checks/consolidated/ui-patterns.js.map +1 -0
  297. package/dist/checks/consolidated/ui-responsive.d.ts +89 -0
  298. package/dist/checks/consolidated/ui-responsive.d.ts.map +1 -0
  299. package/dist/checks/consolidated/ui-responsive.js +588 -0
  300. package/dist/checks/consolidated/ui-responsive.js.map +1 -0
  301. package/dist/checks/consolidated/ui-spacing-standards.d.ts +43 -0
  302. package/dist/checks/consolidated/ui-spacing-standards.d.ts.map +1 -0
  303. package/dist/checks/consolidated/ui-spacing-standards.js +874 -0
  304. package/dist/checks/consolidated/ui-spacing-standards.js.map +1 -0
  305. package/dist/checks/consolidated/ui-spacing.d.ts +751 -0
  306. package/dist/checks/consolidated/ui-spacing.d.ts.map +1 -0
  307. package/dist/checks/consolidated/ui-spacing.js +4996 -0
  308. package/dist/checks/consolidated/ui-spacing.js.map +1 -0
  309. package/dist/checks/consolidated/ui-standards-auto-fixer.d.ts +70 -0
  310. package/dist/checks/consolidated/ui-standards-auto-fixer.d.ts.map +1 -0
  311. package/dist/checks/consolidated/ui-standards-auto-fixer.js +429 -0
  312. package/dist/checks/consolidated/ui-standards-auto-fixer.js.map +1 -0
  313. package/dist/checks/consolidated/ui-standards-enforcement.d.ts +100 -0
  314. package/dist/checks/consolidated/ui-standards-enforcement.d.ts.map +1 -0
  315. package/dist/checks/consolidated/ui-standards-enforcement.js +935 -0
  316. package/dist/checks/consolidated/ui-standards-enforcement.js.map +1 -0
  317. package/dist/checks/consolidated/ui-state-consistency.d.ts +90 -0
  318. package/dist/checks/consolidated/ui-state-consistency.d.ts.map +1 -0
  319. package/dist/checks/consolidated/ui-state-consistency.js +659 -0
  320. package/dist/checks/consolidated/ui-state-consistency.js.map +1 -0
  321. package/dist/checks/consolidated/ui-style-validation.d.ts +74 -0
  322. package/dist/checks/consolidated/ui-style-validation.d.ts.map +1 -0
  323. package/dist/checks/consolidated/ui-style-validation.js +403 -0
  324. package/dist/checks/consolidated/ui-style-validation.js.map +1 -0
  325. package/dist/checks/consolidated/ui-tokens.d.ts +110 -0
  326. package/dist/checks/consolidated/ui-tokens.d.ts.map +1 -0
  327. package/dist/checks/consolidated/ui-tokens.js +990 -0
  328. package/dist/checks/consolidated/ui-tokens.js.map +1 -0
  329. package/dist/checks/consolidated/ui-typography.d.ts +77 -0
  330. package/dist/checks/consolidated/ui-typography.d.ts.map +1 -0
  331. package/dist/checks/consolidated/ui-typography.js +416 -0
  332. package/dist/checks/consolidated/ui-typography.js.map +1 -0
  333. package/dist/checks/consolidated/ui-visual-hierarchy.d.ts +90 -0
  334. package/dist/checks/consolidated/ui-visual-hierarchy.d.ts.map +1 -0
  335. package/dist/checks/consolidated/ui-visual-hierarchy.js +562 -0
  336. package/dist/checks/consolidated/ui-visual-hierarchy.js.map +1 -0
  337. package/dist/checks/consolidated/woocommerce.d.ts +50 -0
  338. package/dist/checks/consolidated/woocommerce.d.ts.map +1 -0
  339. package/dist/checks/consolidated/woocommerce.js +198 -0
  340. package/dist/checks/consolidated/woocommerce.js.map +1 -0
  341. package/dist/checks/core/api-route-protection.d.ts +2 -0
  342. package/dist/checks/core/api-route-protection.d.ts.map +1 -0
  343. package/dist/checks/core/api-route-protection.js +101 -0
  344. package/dist/checks/core/api-route-protection.js.map +1 -0
  345. package/dist/checks/core/critical.d.ts +8 -0
  346. package/dist/checks/core/critical.d.ts.map +1 -0
  347. package/dist/checks/core/critical.js +200 -0
  348. package/dist/checks/core/critical.js.map +1 -0
  349. package/dist/checks/core/database.d.ts +8 -0
  350. package/dist/checks/core/database.d.ts.map +1 -0
  351. package/dist/checks/core/database.js +699 -0
  352. package/dist/checks/core/database.js.map +1 -0
  353. package/dist/checks/core/development.d.ts +8 -0
  354. package/dist/checks/core/development.d.ts.map +1 -0
  355. package/dist/checks/core/development.js +417 -0
  356. package/dist/checks/core/development.js.map +1 -0
  357. package/dist/checks/core/hydration-mismatch-check.d.ts +38 -0
  358. package/dist/checks/core/hydration-mismatch-check.d.ts.map +1 -0
  359. package/dist/checks/core/hydration-mismatch-check.js +411 -0
  360. package/dist/checks/core/hydration-mismatch-check.js.map +1 -0
  361. package/dist/checks/core/performance.d.ts +8 -0
  362. package/dist/checks/core/performance.d.ts.map +1 -0
  363. package/dist/checks/core/performance.js +474 -0
  364. package/dist/checks/core/performance.js.map +1 -0
  365. package/dist/checks/core/security.d.ts +8 -0
  366. package/dist/checks/core/security.d.ts.map +1 -0
  367. package/dist/checks/core/security.js +275 -0
  368. package/dist/checks/core/security.js.map +1 -0
  369. package/dist/checks/core/standardized-error-handling.d.ts +43 -0
  370. package/dist/checks/core/standardized-error-handling.d.ts.map +1 -0
  371. package/dist/checks/core/standardized-error-handling.js +384 -0
  372. package/dist/checks/core/standardized-error-handling.js.map +1 -0
  373. package/dist/checks/core/supercatch.d.ts +8 -0
  374. package/dist/checks/core/supercatch.d.ts.map +1 -0
  375. package/dist/checks/core/supercatch.js +750 -0
  376. package/dist/checks/core/supercatch.js.map +1 -0
  377. package/dist/checks/core/suppression-check.d.ts +2 -0
  378. package/dist/checks/core/suppression-check.d.ts.map +1 -0
  379. package/dist/checks/core/suppression-check.js +129 -0
  380. package/dist/checks/core/suppression-check.js.map +1 -0
  381. package/dist/checks/core/ui-quality.d.ts +8 -0
  382. package/dist/checks/core/ui-quality.d.ts.map +1 -0
  383. package/dist/checks/core/ui-quality.js +1736 -0
  384. package/dist/checks/core/ui-quality.js.map +1 -0
  385. package/dist/checks/core/unused-assets-check.d.ts +2 -0
  386. package/dist/checks/core/unused-assets-check.d.ts.map +1 -0
  387. package/dist/checks/core/unused-assets-check.js +112 -0
  388. package/dist/checks/core/unused-assets-check.js.map +1 -0
  389. package/dist/checks/core/use-status-ssr-safety.d.ts +34 -0
  390. package/dist/checks/core/use-status-ssr-safety.d.ts.map +1 -0
  391. package/dist/checks/core/use-status-ssr-safety.js +283 -0
  392. package/dist/checks/core/use-status-ssr-safety.js.map +1 -0
  393. package/dist/checks/email/email-flow-validation.d.ts +23 -0
  394. package/dist/checks/email/email-flow-validation.d.ts.map +1 -0
  395. package/dist/checks/email/email-flow-validation.js +468 -0
  396. package/dist/checks/email/email-flow-validation.js.map +1 -0
  397. package/dist/checks/email/email-template-db-verification.d.ts +20 -0
  398. package/dist/checks/email/email-template-db-verification.d.ts.map +1 -0
  399. package/dist/checks/email/email-template-db-verification.js +46 -0
  400. package/dist/checks/email/email-template-db-verification.js.map +1 -0
  401. package/dist/checks/email/email-template-validation.d.ts +24 -0
  402. package/dist/checks/email/email-template-validation.d.ts.map +1 -0
  403. package/dist/checks/email/email-template-validation.js +688 -0
  404. package/dist/checks/email/email-template-validation.js.map +1 -0
  405. package/dist/checks/jsx/comment-placement.d.ts +45 -0
  406. package/dist/checks/jsx/comment-placement.d.ts.map +1 -0
  407. package/dist/checks/jsx/comment-placement.js +316 -0
  408. package/dist/checks/jsx/comment-placement.js.map +1 -0
  409. package/dist/checks/specialized/admin-layout-check.d.ts +19 -0
  410. package/dist/checks/specialized/admin-layout-check.d.ts.map +1 -0
  411. package/dist/checks/specialized/admin-layout-check.js +166 -0
  412. package/dist/checks/specialized/admin-layout-check.js.map +1 -0
  413. package/dist/checks/specialized/client-server-separation.d.ts +14 -0
  414. package/dist/checks/specialized/client-server-separation.d.ts.map +1 -0
  415. package/dist/checks/specialized/client-server-separation.js +197 -0
  416. package/dist/checks/specialized/client-server-separation.js.map +1 -0
  417. package/dist/checks/specialized/cost-optimization.d.ts +18 -0
  418. package/dist/checks/specialized/cost-optimization.d.ts.map +1 -0
  419. package/dist/checks/specialized/cost-optimization.js +78 -0
  420. package/dist/checks/specialized/cost-optimization.js.map +1 -0
  421. package/dist/checks/specialized/database-migration-sync.d.ts +21 -0
  422. package/dist/checks/specialized/database-migration-sync.d.ts.map +1 -0
  423. package/dist/checks/specialized/database-migration-sync.js +150 -0
  424. package/dist/checks/specialized/database-migration-sync.js.map +1 -0
  425. package/dist/checks/specialized/database-model-validation.d.ts +15 -0
  426. package/dist/checks/specialized/database-model-validation.d.ts.map +1 -0
  427. package/dist/checks/specialized/database-model-validation.js +35 -0
  428. package/dist/checks/specialized/database-model-validation.js.map +1 -0
  429. package/dist/checks/specialized/database-schema-migrations-diff.d.ts +27 -0
  430. package/dist/checks/specialized/database-schema-migrations-diff.d.ts.map +1 -0
  431. package/dist/checks/specialized/database-schema-migrations-diff.js +177 -0
  432. package/dist/checks/specialized/database-schema-migrations-diff.js.map +1 -0
  433. package/dist/checks/specialized/database-schema-sync.d.ts +23 -0
  434. package/dist/checks/specialized/database-schema-sync.d.ts.map +1 -0
  435. package/dist/checks/specialized/database-schema-sync.js +77 -0
  436. package/dist/checks/specialized/database-schema-sync.js.map +1 -0
  437. package/dist/checks/specialized/decimal-serialization.d.ts +24 -0
  438. package/dist/checks/specialized/decimal-serialization.d.ts.map +1 -0
  439. package/dist/checks/specialized/decimal-serialization.js +400 -0
  440. package/dist/checks/specialized/decimal-serialization.js.map +1 -0
  441. package/dist/checks/specialized/detect-router-issues.d.ts +14 -0
  442. package/dist/checks/specialized/detect-router-issues.d.ts.map +1 -0
  443. package/dist/checks/specialized/detect-router-issues.js +96 -0
  444. package/dist/checks/specialized/detect-router-issues.js.map +1 -0
  445. package/dist/checks/specialized/enum-validation.d.ts +15 -0
  446. package/dist/checks/specialized/enum-validation.d.ts.map +1 -0
  447. package/dist/checks/specialized/enum-validation.js +35 -0
  448. package/dist/checks/specialized/enum-validation.js.map +1 -0
  449. package/dist/checks/specialized/hash-collision.d.ts +18 -0
  450. package/dist/checks/specialized/hash-collision.d.ts.map +1 -0
  451. package/dist/checks/specialized/hash-collision.js +78 -0
  452. package/dist/checks/specialized/hash-collision.js.map +1 -0
  453. package/dist/checks/specialized/id-generation-enforcement.d.ts +16 -0
  454. package/dist/checks/specialized/id-generation-enforcement.d.ts.map +1 -0
  455. package/dist/checks/specialized/id-generation-enforcement.js +307 -0
  456. package/dist/checks/specialized/id-generation-enforcement.js.map +1 -0
  457. package/dist/checks/specialized/image-data-integrity.d.ts +15 -0
  458. package/dist/checks/specialized/image-data-integrity.d.ts.map +1 -0
  459. package/dist/checks/specialized/image-data-integrity.js +79 -0
  460. package/dist/checks/specialized/image-data-integrity.js.map +1 -0
  461. package/dist/checks/specialized/image-health.d.ts +14 -0
  462. package/dist/checks/specialized/image-health.d.ts.map +1 -0
  463. package/dist/checks/specialized/image-health.js +122 -0
  464. package/dist/checks/specialized/image-health.js.map +1 -0
  465. package/dist/checks/specialized/image-metadata-validation.d.ts +14 -0
  466. package/dist/checks/specialized/image-metadata-validation.d.ts.map +1 -0
  467. package/dist/checks/specialized/image-metadata-validation.js +95 -0
  468. package/dist/checks/specialized/image-metadata-validation.js.map +1 -0
  469. package/dist/checks/specialized/image-optimization.d.ts +16 -0
  470. package/dist/checks/specialized/image-optimization.d.ts.map +1 -0
  471. package/dist/checks/specialized/image-optimization.js +86 -0
  472. package/dist/checks/specialized/image-optimization.js.map +1 -0
  473. package/dist/checks/specialized/invalid-module-imports.d.ts +24 -0
  474. package/dist/checks/specialized/invalid-module-imports.d.ts.map +1 -0
  475. package/dist/checks/specialized/invalid-module-imports.js +209 -0
  476. package/dist/checks/specialized/invalid-module-imports.js.map +1 -0
  477. package/dist/checks/specialized/lint-validation.d.ts +26 -0
  478. package/dist/checks/specialized/lint-validation.d.ts.map +1 -0
  479. package/dist/checks/specialized/lint-validation.js +193 -0
  480. package/dist/checks/specialized/lint-validation.js.map +1 -0
  481. package/dist/checks/specialized/listing-workflow.d.ts +19 -0
  482. package/dist/checks/specialized/listing-workflow.d.ts.map +1 -0
  483. package/dist/checks/specialized/listing-workflow.js +89 -0
  484. package/dist/checks/specialized/listing-workflow.js.map +1 -0
  485. package/dist/checks/specialized/mui-imports-validation.d.ts +18 -0
  486. package/dist/checks/specialized/mui-imports-validation.d.ts.map +1 -0
  487. package/dist/checks/specialized/mui-imports-validation.js +134 -0
  488. package/dist/checks/specialized/mui-imports-validation.js.map +1 -0
  489. package/dist/checks/specialized/nextauth-v5-compliance.d.ts +16 -0
  490. package/dist/checks/specialized/nextauth-v5-compliance.d.ts.map +1 -0
  491. package/dist/checks/specialized/nextauth-v5-compliance.js +164 -0
  492. package/dist/checks/specialized/nextauth-v5-compliance.js.map +1 -0
  493. package/dist/checks/specialized/nextjs-params-check.d.ts +14 -0
  494. package/dist/checks/specialized/nextjs-params-check.d.ts.map +1 -0
  495. package/dist/checks/specialized/nextjs-params-check.js +140 -0
  496. package/dist/checks/specialized/nextjs-params-check.js.map +1 -0
  497. package/dist/checks/specialized/no-legacy-catalog-aliases-validation.d.ts +16 -0
  498. package/dist/checks/specialized/no-legacy-catalog-aliases-validation.d.ts.map +1 -0
  499. package/dist/checks/specialized/no-legacy-catalog-aliases-validation.js +36 -0
  500. package/dist/checks/specialized/no-legacy-catalog-aliases-validation.js.map +1 -0
  501. package/dist/checks/specialized/no-wata-cardgraded-validation.d.ts +22 -0
  502. package/dist/checks/specialized/no-wata-cardgraded-validation.d.ts.map +1 -0
  503. package/dist/checks/specialized/no-wata-cardgraded-validation.js +97 -0
  504. package/dist/checks/specialized/no-wata-cardgraded-validation.js.map +1 -0
  505. package/dist/checks/specialized/parameter-consistency-check.d.ts +20 -0
  506. package/dist/checks/specialized/parameter-consistency-check.d.ts.map +1 -0
  507. package/dist/checks/specialized/parameter-consistency-check.js +115 -0
  508. package/dist/checks/specialized/parameter-consistency-check.js.map +1 -0
  509. package/dist/checks/specialized/prisma-field-names-validation.d.ts +15 -0
  510. package/dist/checks/specialized/prisma-field-names-validation.d.ts.map +1 -0
  511. package/dist/checks/specialized/prisma-field-names-validation.js +35 -0
  512. package/dist/checks/specialized/prisma-field-names-validation.js.map +1 -0
  513. package/dist/checks/specialized/prisma-null-syntax.d.ts +34 -0
  514. package/dist/checks/specialized/prisma-null-syntax.d.ts.map +1 -0
  515. package/dist/checks/specialized/prisma-null-syntax.js +330 -0
  516. package/dist/checks/specialized/prisma-null-syntax.js.map +1 -0
  517. package/dist/checks/specialized/prisma-query-validation.d.ts +15 -0
  518. package/dist/checks/specialized/prisma-query-validation.d.ts.map +1 -0
  519. package/dist/checks/specialized/prisma-query-validation.js +35 -0
  520. package/dist/checks/specialized/prisma-query-validation.js.map +1 -0
  521. package/dist/checks/specialized/product-type-validation.d.ts +17 -0
  522. package/dist/checks/specialized/product-type-validation.d.ts.map +1 -0
  523. package/dist/checks/specialized/product-type-validation.js +129 -0
  524. package/dist/checks/specialized/product-type-validation.js.map +1 -0
  525. package/dist/checks/specialized/responsive-image-validation.d.ts +14 -0
  526. package/dist/checks/specialized/responsive-image-validation.d.ts.map +1 -0
  527. package/dist/checks/specialized/responsive-image-validation.js +101 -0
  528. package/dist/checks/specialized/responsive-image-validation.js.map +1 -0
  529. package/dist/checks/specialized/root-cleanliness.d.ts +21 -0
  530. package/dist/checks/specialized/root-cleanliness.d.ts.map +1 -0
  531. package/dist/checks/specialized/root-cleanliness.js +251 -0
  532. package/dist/checks/specialized/root-cleanliness.js.map +1 -0
  533. package/dist/checks/specialized/rotation-detection-validation.d.ts +16 -0
  534. package/dist/checks/specialized/rotation-detection-validation.d.ts.map +1 -0
  535. package/dist/checks/specialized/rotation-detection-validation.js +113 -0
  536. package/dist/checks/specialized/rotation-detection-validation.js.map +1 -0
  537. package/dist/checks/specialized/script-organization.d.ts +17 -0
  538. package/dist/checks/specialized/script-organization.d.ts.map +1 -0
  539. package/dist/checks/specialized/script-organization.js +487 -0
  540. package/dist/checks/specialized/script-organization.js.map +1 -0
  541. package/dist/checks/specialized/shared-components-migration.d.ts +137 -0
  542. package/dist/checks/specialized/shared-components-migration.d.ts.map +1 -0
  543. package/dist/checks/specialized/shared-components-migration.js +1288 -0
  544. package/dist/checks/specialized/shared-components-migration.js.map +1 -0
  545. package/dist/checks/specialized/store-specialties-normalization.d.ts +10 -0
  546. package/dist/checks/specialized/store-specialties-normalization.d.ts.map +1 -0
  547. package/dist/checks/specialized/store-specialties-normalization.js +126 -0
  548. package/dist/checks/specialized/store-specialties-normalization.js.map +1 -0
  549. package/dist/checks/specialized/two-stage-trim-validation.d.ts +16 -0
  550. package/dist/checks/specialized/two-stage-trim-validation.d.ts.map +1 -0
  551. package/dist/checks/specialized/two-stage-trim-validation.js +115 -0
  552. package/dist/checks/specialized/two-stage-trim-validation.js.map +1 -0
  553. package/dist/checks/specialized/underscore-variable-audit.d.ts +26 -0
  554. package/dist/checks/specialized/underscore-variable-audit.d.ts.map +1 -0
  555. package/dist/checks/specialized/underscore-variable-audit.js +219 -0
  556. package/dist/checks/specialized/underscore-variable-audit.js.map +1 -0
  557. package/dist/checks/specialized/unified-badge-consistency.d.ts +16 -0
  558. package/dist/checks/specialized/unified-badge-consistency.d.ts.map +1 -0
  559. package/dist/checks/specialized/unified-badge-consistency.js +284 -0
  560. package/dist/checks/specialized/unified-badge-consistency.js.map +1 -0
  561. package/dist/checks/specialized/validate-integration-enums.d.ts +15 -0
  562. package/dist/checks/specialized/validate-integration-enums.d.ts.map +1 -0
  563. package/dist/checks/specialized/validate-integration-enums.js +131 -0
  564. package/dist/checks/specialized/validate-integration-enums.js.map +1 -0
  565. package/dist/checks/testing/action-regression.d.ts +23 -0
  566. package/dist/checks/testing/action-regression.d.ts.map +1 -0
  567. package/dist/checks/testing/action-regression.js +192 -0
  568. package/dist/checks/testing/action-regression.js.map +1 -0
  569. package/dist/checks/testing/critical-api-coverage.d.ts +21 -0
  570. package/dist/checks/testing/critical-api-coverage.d.ts.map +1 -0
  571. package/dist/checks/testing/critical-api-coverage.js +158 -0
  572. package/dist/checks/testing/critical-api-coverage.js.map +1 -0
  573. package/dist/checks/testing/data-entry-regression-required.d.ts +24 -0
  574. package/dist/checks/testing/data-entry-regression-required.d.ts.map +1 -0
  575. package/dist/checks/testing/data-entry-regression-required.js +378 -0
  576. package/dist/checks/testing/data-entry-regression-required.js.map +1 -0
  577. package/dist/checks/testing/e2e-best-practices.d.ts +24 -0
  578. package/dist/checks/testing/e2e-best-practices.d.ts.map +1 -0
  579. package/dist/checks/testing/e2e-best-practices.js +791 -0
  580. package/dist/checks/testing/e2e-best-practices.js.map +1 -0
  581. package/dist/checks/testing/e2e-flake-patterns.d.ts +26 -0
  582. package/dist/checks/testing/e2e-flake-patterns.d.ts.map +1 -0
  583. package/dist/checks/testing/e2e-flake-patterns.js +305 -0
  584. package/dist/checks/testing/e2e-flake-patterns.js.map +1 -0
  585. package/dist/checks/testing/e2e-redundant-visibility-checks.d.ts +25 -0
  586. package/dist/checks/testing/e2e-redundant-visibility-checks.d.ts.map +1 -0
  587. package/dist/checks/testing/e2e-redundant-visibility-checks.js +613 -0
  588. package/dist/checks/testing/e2e-redundant-visibility-checks.js.map +1 -0
  589. package/dist/checks/testing/e2e-slow-tests.d.ts +9 -0
  590. package/dist/checks/testing/e2e-slow-tests.d.ts.map +1 -0
  591. package/dist/checks/testing/e2e-slow-tests.js +142 -0
  592. package/dist/checks/testing/e2e-slow-tests.js.map +1 -0
  593. package/dist/checks/testing/e2e-timeouts.d.ts +9 -0
  594. package/dist/checks/testing/e2e-timeouts.d.ts.map +1 -0
  595. package/dist/checks/testing/e2e-timeouts.js +82 -0
  596. package/dist/checks/testing/e2e-timeouts.js.map +1 -0
  597. package/dist/checks/testing/integration-e2e-depth.d.ts +20 -0
  598. package/dist/checks/testing/integration-e2e-depth.d.ts.map +1 -0
  599. package/dist/checks/testing/integration-e2e-depth.js +575 -0
  600. package/dist/checks/testing/integration-e2e-depth.js.map +1 -0
  601. package/dist/checks/testing/playwright-feature-coverage-gaps.d.ts +31 -0
  602. package/dist/checks/testing/playwright-feature-coverage-gaps.d.ts.map +1 -0
  603. package/dist/checks/testing/playwright-feature-coverage-gaps.js +1582 -0
  604. package/dist/checks/testing/playwright-feature-coverage-gaps.js.map +1 -0
  605. package/dist/checks/testing/playwright-mock-inventory.d.ts +24 -0
  606. package/dist/checks/testing/playwright-mock-inventory.d.ts.map +1 -0
  607. package/dist/checks/testing/playwright-mock-inventory.js +380 -0
  608. package/dist/checks/testing/playwright-mock-inventory.js.map +1 -0
  609. package/dist/checks/testing/test-coverage-threshold.d.ts +25 -0
  610. package/dist/checks/testing/test-coverage-threshold.d.ts.map +1 -0
  611. package/dist/checks/testing/test-coverage-threshold.js +166 -0
  612. package/dist/checks/testing/test-coverage-threshold.js.map +1 -0
  613. package/dist/checks/testing/test-flakiness-score.d.ts +27 -0
  614. package/dist/checks/testing/test-flakiness-score.d.ts.map +1 -0
  615. package/dist/checks/testing/test-flakiness-score.js +358 -0
  616. package/dist/checks/testing/test-flakiness-score.js.map +1 -0
  617. package/dist/checks/testing/test-patterns.d.ts +16 -0
  618. package/dist/checks/testing/test-patterns.d.ts.map +1 -0
  619. package/dist/checks/testing/test-patterns.js +156 -0
  620. package/dist/checks/testing/test-patterns.js.map +1 -0
  621. package/dist/checks/workflows/a-plus-rating-validation.d.ts +42 -0
  622. package/dist/checks/workflows/a-plus-rating-validation.d.ts.map +1 -0
  623. package/dist/checks/workflows/a-plus-rating-validation.js +527 -0
  624. package/dist/checks/workflows/a-plus-rating-validation.js.map +1 -0
  625. package/dist/checks/workflows/affected.d.ts +14 -0
  626. package/dist/checks/workflows/affected.d.ts.map +1 -0
  627. package/dist/checks/workflows/affected.js +126 -0
  628. package/dist/checks/workflows/affected.js.map +1 -0
  629. package/dist/checks/workflows/ai.d.ts +6 -0
  630. package/dist/checks/workflows/ai.d.ts.map +1 -0
  631. package/dist/checks/workflows/ai.js +42 -0
  632. package/dist/checks/workflows/ai.js.map +1 -0
  633. package/dist/checks/workflows/all.d.ts +31 -0
  634. package/dist/checks/workflows/all.d.ts.map +1 -0
  635. package/dist/checks/workflows/all.js +2688 -0
  636. package/dist/checks/workflows/all.js.map +1 -0
  637. package/dist/checks/workflows/commit.d.ts +19 -0
  638. package/dist/checks/workflows/commit.d.ts.map +1 -0
  639. package/dist/checks/workflows/commit.js +207 -0
  640. package/dist/checks/workflows/commit.js.map +1 -0
  641. package/dist/checks/workflows/critical.d.ts +9 -0
  642. package/dist/checks/workflows/critical.d.ts.map +1 -0
  643. package/dist/checks/workflows/critical.js +213 -0
  644. package/dist/checks/workflows/critical.js.map +1 -0
  645. package/dist/checks/workflows/database-id-validation.d.ts +9 -0
  646. package/dist/checks/workflows/database-id-validation.d.ts.map +1 -0
  647. package/dist/checks/workflows/database-id-validation.js +13 -0
  648. package/dist/checks/workflows/database-id-validation.js.map +1 -0
  649. package/dist/checks/workflows/deploy.d.ts +20 -0
  650. package/dist/checks/workflows/deploy.d.ts.map +1 -0
  651. package/dist/checks/workflows/deploy.js +107 -0
  652. package/dist/checks/workflows/deploy.js.map +1 -0
  653. package/dist/checks/workflows/deployment-readiness.d.ts +12 -0
  654. package/dist/checks/workflows/deployment-readiness.d.ts.map +1 -0
  655. package/dist/checks/workflows/deployment-readiness.js +403 -0
  656. package/dist/checks/workflows/deployment-readiness.js.map +1 -0
  657. package/dist/checks/workflows/dev.d.ts +19 -0
  658. package/dist/checks/workflows/dev.d.ts.map +1 -0
  659. package/dist/checks/workflows/dev.js +88 -0
  660. package/dist/checks/workflows/dev.js.map +1 -0
  661. package/dist/checks/workflows/development.d.ts +9 -0
  662. package/dist/checks/workflows/development.d.ts.map +1 -0
  663. package/dist/checks/workflows/development.js +65 -0
  664. package/dist/checks/workflows/development.js.map +1 -0
  665. package/dist/checks/workflows/enterprise.d.ts +10 -0
  666. package/dist/checks/workflows/enterprise.d.ts.map +1 -0
  667. package/dist/checks/workflows/enterprise.js +359 -0
  668. package/dist/checks/workflows/enterprise.js.map +1 -0
  669. package/dist/checks/workflows/images.d.ts +6 -0
  670. package/dist/checks/workflows/images.d.ts.map +1 -0
  671. package/dist/checks/workflows/images.js +58 -0
  672. package/dist/checks/workflows/images.js.map +1 -0
  673. package/dist/checks/workflows/naming.d.ts +19 -0
  674. package/dist/checks/workflows/naming.d.ts.map +1 -0
  675. package/dist/checks/workflows/naming.js +42 -0
  676. package/dist/checks/workflows/naming.js.map +1 -0
  677. package/dist/checks/workflows/performance.d.ts +8 -0
  678. package/dist/checks/workflows/performance.d.ts.map +1 -0
  679. package/dist/checks/workflows/performance.js +77 -0
  680. package/dist/checks/workflows/performance.js.map +1 -0
  681. package/dist/checks/workflows/pre-deploy.d.ts +6 -0
  682. package/dist/checks/workflows/pre-deploy.d.ts.map +1 -0
  683. package/dist/checks/workflows/pre-deploy.js +41 -0
  684. package/dist/checks/workflows/pre-deploy.js.map +1 -0
  685. package/dist/checks/workflows/security.d.ts +8 -0
  686. package/dist/checks/workflows/security.d.ts.map +1 -0
  687. package/dist/checks/workflows/security.js +71 -0
  688. package/dist/checks/workflows/security.js.map +1 -0
  689. package/dist/checks/workflows/supercatch.d.ts +8 -0
  690. package/dist/checks/workflows/supercatch.d.ts.map +1 -0
  691. package/dist/checks/workflows/supercatch.js +127 -0
  692. package/dist/checks/workflows/supercatch.js.map +1 -0
  693. package/dist/checks/workflows/ui-quality.d.ts +9 -0
  694. package/dist/checks/workflows/ui-quality.d.ts.map +1 -0
  695. package/dist/checks/workflows/ui-quality.js +264 -0
  696. package/dist/checks/workflows/ui-quality.js.map +1 -0
  697. package/dist/checks/workflows/ui-uniformity.d.ts +18 -0
  698. package/dist/checks/workflows/ui-uniformity.d.ts.map +1 -0
  699. package/dist/checks/workflows/ui-uniformity.js +265 -0
  700. package/dist/checks/workflows/ui-uniformity.js.map +1 -0
  701. package/dist/checks/workflows/vercel.d.ts +16 -0
  702. package/dist/checks/workflows/vercel.d.ts.map +1 -0
  703. package/dist/checks/workflows/vercel.js +173 -0
  704. package/dist/checks/workflows/vercel.js.map +1 -0
  705. package/dist/utils/validation-helpers.d.ts +43 -0
  706. package/dist/utils/validation-helpers.d.ts.map +1 -0
  707. package/dist/utils/validation-helpers.js +370 -0
  708. package/dist/utils/validation-helpers.js.map +1 -0
  709. package/package.json +1 -1
@@ -0,0 +1,3056 @@
1
+ "use strict";
2
+ /**
3
+ * Hardened Preflight Checks
4
+ *
5
+ * CRITICAL checks that block deployment to prevent production issues.
6
+ * These are the "iron-clad" rules that cannot be violated.
7
+ *
8
+ * Checks included:
9
+ * 1. DRAFT Status Enforcement - No direct ACTIVE listing creation
10
+ * 2. Async/Await Error Handling - All async code must have error handling
11
+ * 3. Console Cleanup - No console.log in production code
12
+ * 4. Secret Detection - No hardcoded API keys or secrets
13
+ * 5. React Hook Rules - Hooks must follow rules of hooks
14
+ * 6. Promise Handling - No floating promises
15
+ * 7. CVA Enforcement - Components with variants MUST use CVA
16
+ * 8. Type Safety - No unsafe type assertions in critical paths
17
+ * 9. ESLint Concurrency - ESLint commands must use multithreading
18
+ * 10. CardGraded Semantics - Prevent WATA in catalogNonSportsCards
19
+ * 11. Card Grade Field Consistency - prevent mixed grade fields
20
+ * 12. Serial Number Consistency - serialNumbered requires serialNumber
21
+ * 13. Autograph Consistency - auth flags require autographed
22
+ * 14. Video Game Grading Consistency - graded/grader/wata fields
23
+ * 15. Syndication Consistency - syndicated flags require platform IDs
24
+ * 16. Featured Consistency - featuredUntil requires featured
25
+ * 17. Flagging Consistency - flagged fields require isFlagged
26
+ * 18. Soft Delete Consistency - deletedBy requires deletedAt
27
+ * 19. Seller Approval Consistency - sellerApprovedAt requires sellerApproved
28
+ * 20. Recognition Metadata Consistency - recognitionConfidence/jobId require method
29
+ * 21. Autograph Grade Field Consistency - prevent mixed autograph grade systems
30
+ * 22. Autograph Grading Metadata Consistency - autographGraded required for autograph grades
31
+ *
32
+ * Usage:
33
+ * pnpm preflight:hardened # All hardened checks
34
+ * pnpm preflight:hardened draft # DRAFT status only
35
+ * pnpm preflight:hardened async # Async error handling only
36
+ * pnpm preflight:hardened console # Console cleanup only
37
+ * pnpm preflight:hardened secrets # Secret detection only
38
+ * pnpm preflight:hardened hooks # React hook rules only
39
+ * pnpm preflight:hardened promises # Promise handling only
40
+ * pnpm preflight:hardened cva # CVA enforcement only
41
+ * pnpm preflight:hardened types # Type safety only
42
+ * pnpm preflight:hardened eslint-concurrency # ESLint concurrency enforcement only
43
+ *
44
+ * All checks are BLOCKING by default.
45
+ */
46
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
47
+ if (k2 === undefined) k2 = k;
48
+ var desc = Object.getOwnPropertyDescriptor(m, k);
49
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
50
+ desc = { enumerable: true, get: function() { return m[k]; } };
51
+ }
52
+ Object.defineProperty(o, k2, desc);
53
+ }) : (function(o, m, k, k2) {
54
+ if (k2 === undefined) k2 = k;
55
+ o[k2] = m[k];
56
+ }));
57
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
58
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
59
+ }) : function(o, v) {
60
+ o["default"] = v;
61
+ });
62
+ var __importStar = (this && this.__importStar) || (function () {
63
+ var ownKeys = function(o) {
64
+ ownKeys = Object.getOwnPropertyNames || function (o) {
65
+ var ar = [];
66
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
67
+ return ar;
68
+ };
69
+ return ownKeys(o);
70
+ };
71
+ return function (mod) {
72
+ if (mod && mod.__esModule) return mod;
73
+ var result = {};
74
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
75
+ __setModuleDefault(result, mod);
76
+ return result;
77
+ };
78
+ })();
79
+ Object.defineProperty(exports, "__esModule", { value: true });
80
+ exports.HardenedPreflightModule = void 0;
81
+ const fs = __importStar(require("fs"));
82
+ const path = __importStar(require("path"));
83
+ const console_chars_1 = require("../../utils/console-chars");
84
+ const file_cache_1 = require("../../shared/file-cache");
85
+ const glob_patterns_1 = require("../../shared/glob-patterns");
86
+ const universal_progress_reporter_1 = require("../system/universal-progress-reporter");
87
+ const concurrency_config_1 = require("../../shared/concurrency-config");
88
+ // Use shared excludes + hardened-specific exclusions
89
+ const EXCLUDED = (0, glob_patterns_1.extendExcludes)(glob_patterns_1.STANDARD_EXCLUDES, [
90
+ "**/test-results/**",
91
+ "**/*.test.*",
92
+ "**/*.spec.*",
93
+ "**/*.stories.*",
94
+ "**/lib/examples/**",
95
+ "**/scripts/active/preflights/**",
96
+ "**/scripts/archived/**",
97
+ "**/__mocks__/**",
98
+ ]);
99
+ // CACHED FILE LISTS - Scan once, use everywhere
100
+ let cachedAppLibFiles = null;
101
+ let cachedAppLibScriptsFiles = null;
102
+ let cachedApiFiles = null;
103
+ let cachedComponentFiles = null;
104
+ async function getAppLibFiles() {
105
+ if (!cachedAppLibFiles) {
106
+ cachedAppLibFiles = await file_cache_1.fileCache.getFiles("{app,lib}/**/*.{ts,tsx}", { ignore: EXCLUDED });
107
+ }
108
+ return cachedAppLibFiles;
109
+ }
110
+ async function getAppLibScriptsFiles() {
111
+ if (!cachedAppLibScriptsFiles) {
112
+ cachedAppLibScriptsFiles = await file_cache_1.fileCache.getFiles("{app,lib,scripts}/**/*.{ts,tsx,js,jsx}", {
113
+ ignore: EXCLUDED,
114
+ });
115
+ }
116
+ return cachedAppLibScriptsFiles;
117
+ }
118
+ async function getApiFiles() {
119
+ if (!cachedApiFiles) {
120
+ cachedApiFiles = await file_cache_1.fileCache.getFiles("app/api/**/*.{ts,tsx}", { ignore: EXCLUDED });
121
+ }
122
+ return cachedApiFiles;
123
+ }
124
+ async function getComponentHookFiles() {
125
+ if (!cachedComponentFiles) {
126
+ cachedComponentFiles = await file_cache_1.fileCache.getFiles("{app,components,hooks}/**/*.{tsx,jsx}", {
127
+ ignore: EXCLUDED,
128
+ });
129
+ }
130
+ return cachedComponentFiles;
131
+ }
132
+ // Get concurrency from shared config (respects PREFLIGHT_CONCURRENCY env var)
133
+ const concurrencyConfig = (0, concurrency_config_1.getConcurrencyConfig)();
134
+ class HardenedPreflightModule {
135
+ verbose;
136
+ parallel = false;
137
+ constructor(options = {}) {
138
+ this.verbose = options.verbose || false;
139
+ this.parallel = options.parallel || concurrencyConfig.parallel;
140
+ }
141
+ /**
142
+ * Check 1: DRAFT Status Enforcement (BLOCKING)
143
+ *
144
+ * CRITICAL: Listings MUST be created in DRAFT or PENDING status.
145
+ * No direct ACTIVE creation allowed - saves $19.2K/year from fraud prevention.
146
+ */
147
+ async checkDraftStatusEnforcement() {
148
+ const startTime = Date.now();
149
+ const issues = [];
150
+ const files = await getAppLibFiles();
151
+ // Files that are ALLOWED to set ACTIVE status (admin approval flows)
152
+ const allowedActivePatterns = [
153
+ "admin/listings/approve",
154
+ "admin/listings/[id]",
155
+ "admin/listing-approval",
156
+ "admin/listings/republish",
157
+ "admin/listings/bulk-approve",
158
+ "lib/actions/admin",
159
+ ];
160
+ for (const file of files) {
161
+ const content = fs.readFileSync(file, "utf8");
162
+ const lines = content.split("\n");
163
+ // Normalize path for cross-platform matching
164
+ const normalizedFile = file.replace(/\\/g, "/");
165
+ const isAllowedFile = allowedActivePatterns.some((allowed) => normalizedFile.includes(allowed));
166
+ lines.forEach((line, index) => {
167
+ // Skip comments
168
+ if (line.trim().startsWith("//") || line.trim().startsWith("*"))
169
+ return;
170
+ // Pattern 1: Direct ACTIVE creation in data objects
171
+ // Match: listingStatus: "ACTIVE" or listingStatus: 'ACTIVE'
172
+ // But exclude: where: { listingStatus: "ACTIVE" } (queries are OK)
173
+ if (/listingStatus:\s*["']ACTIVE["']/.test(line)) {
174
+ // Check if this is within a 'data:' block (creation) vs 'where:' (query)
175
+ const contextStart = Math.max(0, index - 10);
176
+ const context = lines.slice(contextStart, index + 1).join("\n");
177
+ // If there's a 'data:' or 'create(' or 'createMany(' before this, it's a creation
178
+ const isCreation = /(?:data\s*:|create\s*\(|createMany\s*\(|upsert\s*\()[^}]*listingStatus:\s*["']ACTIVE["']/s.test(context);
179
+ // Query patterns are OK
180
+ const isQuery = /(?:where\s*:|findFirst|findMany|findUnique)[^}]*listingStatus:\s*["']ACTIVE["']/s.test(context);
181
+ if (isCreation && !isQuery && !isAllowedFile) {
182
+ issues.push({
183
+ file,
184
+ line: index + 1,
185
+ type: "direct-active-creation",
186
+ severity: "error",
187
+ message: "CRITICAL: Direct ACTIVE listing creation detected",
188
+ suggestion: 'Create with listingStatus: "DRAFT" or "PENDING". Admin approval required for ACTIVE.',
189
+ snippet: line.trim().substring(0, 100),
190
+ });
191
+ }
192
+ }
193
+ // Pattern 2: Status update to ACTIVE outside admin context
194
+ if (/update\s*\([^)]*listingStatus:\s*["']ACTIVE["']/.test(line) && !isAllowedFile) {
195
+ issues.push({
196
+ file,
197
+ line: index + 1,
198
+ type: "unauthorized-active-update",
199
+ severity: "error",
200
+ message: "Unauthorized ACTIVE status update detected",
201
+ suggestion: "Only admin approval routes can set ACTIVE status",
202
+ snippet: line.trim().substring(0, 100),
203
+ });
204
+ }
205
+ });
206
+ }
207
+ const hasErrors = issues.some((i) => i.severity === "error");
208
+ return {
209
+ name: "DRAFT Status Enforcement",
210
+ passed: !hasErrors,
211
+ blocking: true,
212
+ issues,
213
+ duration: Date.now() - startTime,
214
+ };
215
+ }
216
+ /**
217
+ * Check 2: Async/Await Error Handling (BLOCKING)
218
+ *
219
+ * Unhandled promise rejections crash the server.
220
+ * All async functions in API routes must have try/catch.
221
+ */
222
+ async checkAsyncErrorHandling() {
223
+ const startTime = Date.now();
224
+ const issues = [];
225
+ const files = await getApiFiles();
226
+ // Exclude simple test/health routes that don't need error handling
227
+ const simpleRoutePatterns = [
228
+ "test-vercel",
229
+ "test-route",
230
+ "auth\\test", // Windows path
231
+ "auth/test", // Unix path
232
+ "clear-cookies",
233
+ "health",
234
+ "ping",
235
+ "status",
236
+ ];
237
+ for (const file of files) {
238
+ // Skip simple test routes
239
+ const isSimpleRoute = simpleRoutePatterns.some((pattern) => file.includes(pattern));
240
+ if (isSimpleRoute)
241
+ continue;
242
+ const content = fs.readFileSync(file, "utf8");
243
+ const lines = content.split("\n");
244
+ // Skip if this is a simple wrapper/delegator route (just calls another function)
245
+ const isDelegator = /return\s+\w+\s*\(request/.test(content) && lines.length < 30;
246
+ if (isDelegator)
247
+ continue;
248
+ // Check if file has async functions
249
+ const hasAsyncExport = /export\s+(?:async\s+)?function\s+(?:GET|POST|PUT|DELETE|PATCH)/i.test(content);
250
+ if (hasAsyncExport) {
251
+ // Check if there's a try/catch block
252
+ const hasTryCatch = /try\s*\{/.test(content);
253
+ // Check if using a wrapper that handles errors
254
+ const hasErrorWrapper = /withErrorHandler|handleApiError|apiHandler|catchAsync/.test(content);
255
+ if (!hasTryCatch && !hasErrorWrapper) {
256
+ // Find the line with the async export
257
+ lines.forEach((line, index) => {
258
+ if (/export\s+(?:async\s+)?function\s+(?:GET|POST|PUT|DELETE|PATCH)/i.test(line)) {
259
+ issues.push({
260
+ file,
261
+ line: index + 1,
262
+ type: "missing-try-catch",
263
+ severity: "error",
264
+ message: "API route handler missing try/catch error handling",
265
+ suggestion: "Wrap handler body in try/catch or use withErrorHandler wrapper",
266
+ snippet: line.trim().substring(0, 100),
267
+ });
268
+ }
269
+ });
270
+ }
271
+ }
272
+ }
273
+ const hasErrors = issues.some((i) => i.severity === "error");
274
+ return {
275
+ name: "Async Error Handling",
276
+ passed: !hasErrors,
277
+ blocking: true,
278
+ issues,
279
+ duration: Date.now() - startTime,
280
+ };
281
+ }
282
+ /**
283
+ * Check 3: Console Cleanup (WARNING)
284
+ *
285
+ * console.log statements should not be in production code.
286
+ * Use proper logging with log levels instead.
287
+ */
288
+ async checkConsoleCleanup() {
289
+ const startTime = Date.now();
290
+ const issues = [];
291
+ const files = await file_cache_1.fileCache.getFiles("{app,components,lib,hooks}/**/*.{ts,tsx}", {
292
+ ignore: EXCLUDED,
293
+ });
294
+ // Allowed console usage patterns
295
+ const allowedPatterns = [
296
+ /console\.error/, // Errors are OK
297
+ /console\.warn/, // Warnings are OK (for deprecation notices)
298
+ /\/\/.*console/, // Commented out
299
+ /['"]console/, // String containing 'console'
300
+ ];
301
+ for (const file of files) {
302
+ const content = fs.readFileSync(file, "utf8");
303
+ const lines = content.split("\n");
304
+ lines.forEach((line, index) => {
305
+ // Skip if line is a comment
306
+ if (line.trim().startsWith("//") || line.trim().startsWith("*"))
307
+ return;
308
+ // Check for console.log, console.debug, console.info, console.table
309
+ if (/console\.(?:log|debug|info|table|dir|trace)\s*\(/.test(line)) {
310
+ // Check if it's an allowed pattern
311
+ const isAllowed = allowedPatterns.some((pattern) => pattern.test(line));
312
+ if (!isAllowed) {
313
+ issues.push({
314
+ file,
315
+ line: index + 1,
316
+ type: "console-log",
317
+ severity: "warning",
318
+ message: "console.log detected in production code",
319
+ suggestion: "Remove or use a proper logging utility with log levels",
320
+ snippet: line.trim().substring(0, 80),
321
+ });
322
+ }
323
+ }
324
+ });
325
+ }
326
+ // Warning only - doesn't block deployment
327
+ return {
328
+ name: "Console Cleanup",
329
+ passed: true,
330
+ blocking: false,
331
+ issues,
332
+ duration: Date.now() - startTime,
333
+ };
334
+ }
335
+ /**
336
+ * Check 4: Secret Detection (BLOCKING)
337
+ *
338
+ * Hardcoded secrets in code are a security vulnerability.
339
+ */
340
+ async checkSecretDetection() {
341
+ const startTime = Date.now();
342
+ const issues = [];
343
+ const files = await file_cache_1.fileCache.getFiles("{app,components,lib,hooks}/**/*.{ts,tsx,js,jsx}", {
344
+ ignore: EXCLUDED,
345
+ });
346
+ // Patterns that indicate hardcoded secrets
347
+ const secretPatterns = [
348
+ {
349
+ pattern: /["']sk-[a-zA-Z0-9]{20,}["']/,
350
+ type: "openai-key",
351
+ message: "Hardcoded OpenAI API key",
352
+ },
353
+ {
354
+ pattern: /["']pk_(?:live|test)_[a-zA-Z0-9]{20,}["']/,
355
+ type: "stripe-key",
356
+ message: "Hardcoded Stripe API key",
357
+ },
358
+ {
359
+ pattern: /["']ghp_[a-zA-Z0-9]{36}["']/,
360
+ type: "github-token",
361
+ message: "Hardcoded GitHub token",
362
+ },
363
+ { pattern: /["']AKIA[A-Z0-9]{16}["']/, type: "aws-key", message: "Hardcoded AWS access key" },
364
+ {
365
+ pattern: /["'](?:password|passwd|pwd)\s*[:=]\s*["'][^"']{8,}["']/,
366
+ type: "password",
367
+ message: "Hardcoded password",
368
+ },
369
+ {
370
+ pattern: /["'](?:secret|api_?key)\s*[:=]\s*["'][^"']{10,}["']/i,
371
+ type: "generic-secret",
372
+ message: "Potential hardcoded secret",
373
+ },
374
+ {
375
+ pattern: /Authorization.*Bearer\s+[a-zA-Z0-9._-]{20,}/,
376
+ type: "bearer-token",
377
+ message: "Hardcoded bearer token",
378
+ },
379
+ ];
380
+ // False positive patterns (environment variable references, etc.)
381
+ const falsePositivePatterns = [
382
+ /process\.env\./,
383
+ /env\(\)/,
384
+ /getEnv/,
385
+ /["']sk-\[/, // Placeholder
386
+ /["']sk-xxx/i, // Placeholder
387
+ /["']your[-_]?api[-_]?key/i,
388
+ ];
389
+ for (const file of files) {
390
+ const content = fs.readFileSync(file, "utf8");
391
+ const lines = content.split("\n");
392
+ lines.forEach((line, index) => {
393
+ // Skip comments
394
+ if (line.trim().startsWith("//") || line.trim().startsWith("*"))
395
+ return;
396
+ // Skip if it's a false positive pattern
397
+ if (falsePositivePatterns.some((fp) => fp.test(line)))
398
+ return;
399
+ for (const { pattern, type, message } of secretPatterns) {
400
+ if (pattern.test(line)) {
401
+ issues.push({
402
+ file,
403
+ line: index + 1,
404
+ type,
405
+ severity: "error",
406
+ message,
407
+ suggestion: "Move to environment variables and use process.env",
408
+ snippet: line.trim().substring(0, 60) + "...[REDACTED]",
409
+ });
410
+ }
411
+ }
412
+ });
413
+ }
414
+ const hasErrors = issues.some((i) => i.severity === "error");
415
+ return {
416
+ name: "Secret Detection",
417
+ passed: !hasErrors,
418
+ blocking: true,
419
+ issues,
420
+ duration: Date.now() - startTime,
421
+ };
422
+ }
423
+ /**
424
+ * Check 5: React Hook Rules (BLOCKING)
425
+ *
426
+ * Hooks must follow the rules of hooks:
427
+ * - Only call hooks at the top level
428
+ * - Only call hooks from React functions
429
+ *
430
+ * This is a simplified heuristic check - use ESLint react-hooks plugin for comprehensive coverage.
431
+ * Only flags obvious violations: hooks directly inside if/else/switch/try/catch blocks.
432
+ */
433
+ async checkReactHookRules() {
434
+ const startTime = Date.now();
435
+ const issues = [];
436
+ const files = await getComponentHookFiles();
437
+ // Known hook patterns
438
+ const hookPattern = /\b(use[A-Z]\w+|useState|useEffect|useCallback|useMemo|useRef|useContext|useReducer|useLayoutEffect|useImperativeHandle|useDebugValue)\s*\(/;
439
+ for (const file of files) {
440
+ const content = fs.readFileSync(file, "utf8");
441
+ const lines = content.split("\n");
442
+ lines.forEach((line, index) => {
443
+ // Skip comments
444
+ const trimmed = line.trim();
445
+ if (trimmed.startsWith("//") || trimmed.startsWith("*") || trimmed.startsWith("/*"))
446
+ return;
447
+ // Look for hooks that appear immediately after conditional keywords
448
+ // Pattern: if (...) { useState(...) } or if (...) useState(...)
449
+ // Check for hook on a line that starts with conditional pattern
450
+ const conditionalHookPattern = /^\s*(if|else|switch|case|default|try|catch|finally)\s*[\({].*use[A-Z]\w+\s*\(/;
451
+ if (conditionalHookPattern.test(line)) {
452
+ issues.push({
453
+ file,
454
+ line: index + 1,
455
+ type: "conditional-hook-inline",
456
+ severity: "error",
457
+ message: "Hook called in conditional statement - violates Rules of Hooks",
458
+ suggestion: "Move hook call to top level of component, before any conditionals",
459
+ snippet: trimmed.substring(0, 80),
460
+ });
461
+ return;
462
+ }
463
+ // Check for hooks inside callback functions that might be called conditionally
464
+ // Example: .filter(x => useHook())
465
+ if (/\.\s*(filter|find|some|every|reduce|map|forEach)\s*\([^)]*use[A-Z]\w+/.test(line)) {
466
+ issues.push({
467
+ file,
468
+ line: index + 1,
469
+ type: "callback-hook",
470
+ severity: "error",
471
+ message: "Hook called inside array callback - violates Rules of Hooks",
472
+ suggestion: "Extract the hook to component level and pass values to callback",
473
+ snippet: trimmed.substring(0, 80),
474
+ });
475
+ return;
476
+ }
477
+ // Check for ternary operators with hooks
478
+ // Example: condition ? useHookA() : useHookB()
479
+ if (/\?\s*use[A-Z]\w+\s*\(|\:\s*use[A-Z]\w+\s*\(/.test(line)) {
480
+ issues.push({
481
+ file,
482
+ line: index + 1,
483
+ type: "ternary-hook",
484
+ severity: "error",
485
+ message: "Hook called in ternary expression - violates Rules of Hooks",
486
+ suggestion: "Call both hooks unconditionally and choose result conditionally",
487
+ snippet: trimmed.substring(0, 80),
488
+ });
489
+ return;
490
+ }
491
+ // Check for hooks in logical expressions
492
+ // Example: condition && useHook()
493
+ if (/\&\&\s*use[A-Z]\w+\s*\(|\|\|\s*use[A-Z]\w+\s*\(/.test(line)) {
494
+ issues.push({
495
+ file,
496
+ line: index + 1,
497
+ type: "logical-hook",
498
+ severity: "error",
499
+ message: "Hook called in logical expression - violates Rules of Hooks",
500
+ suggestion: "Call hooks unconditionally at component top level",
501
+ snippet: trimmed.substring(0, 80),
502
+ });
503
+ }
504
+ });
505
+ }
506
+ const hasErrors = issues.some((i) => i.severity === "error");
507
+ return {
508
+ name: "React Hook Rules",
509
+ passed: !hasErrors,
510
+ blocking: true,
511
+ issues,
512
+ duration: Date.now() - startTime,
513
+ };
514
+ }
515
+ /**
516
+ * Check 9: Prisma Enum Literal Validation (BLOCKING)
517
+ *
518
+ * Prevents runtime PrismaClientValidationError caused by passing incorrect
519
+ * enum values (commonly lowercase strings) into Prisma queries/mutations.
520
+ */
521
+ async checkPrismaEnumLiterals() {
522
+ const startTime = Date.now();
523
+ const issues = [];
524
+ const schemaPath = path.join(process.cwd(), "prisma", "schema.prisma");
525
+ if (!fs.existsSync(schemaPath)) {
526
+ return {
527
+ name: "Prisma Enum Literals",
528
+ passed: false,
529
+ blocking: true,
530
+ issues: [
531
+ {
532
+ file: schemaPath,
533
+ line: 0,
534
+ type: "missing-schema",
535
+ severity: "error",
536
+ message: "Missing prisma/schema.prisma - cannot validate enum literals",
537
+ },
538
+ ],
539
+ duration: Date.now() - startTime,
540
+ };
541
+ }
542
+ const schema = fs.readFileSync(schemaPath, "utf8");
543
+ // Parse enums: enum Name { VALUE ... }
544
+ const enumValues = new Map();
545
+ const enumValueByLower = new Map();
546
+ {
547
+ const enumRegex = /\benum\s+(\w+)\s*\{([\s\S]*?)\n\}/g;
548
+ let match;
549
+ while ((match = enumRegex.exec(schema))) {
550
+ const enumName = match[1];
551
+ const body = match[2] ?? "";
552
+ const values = body
553
+ .split("\n")
554
+ .map((line) => line.replace(/\/\/.*$/, "").trim())
555
+ .filter((line) => line.length > 0)
556
+ .map((line) => line.split(/\s+/)[0] ?? "")
557
+ .filter((token) => token.length > 0 && token !== "}" && token !== "{");
558
+ if (values.length > 0) {
559
+ enumValues.set(enumName, values);
560
+ const lowerMap = new Map();
561
+ for (const value of values) {
562
+ lowerMap.set(value.toLowerCase(), value);
563
+ }
564
+ enumValueByLower.set(enumName, lowerMap);
565
+ }
566
+ }
567
+ }
568
+ if (enumValues.size === 0) {
569
+ return {
570
+ name: "Prisma Enum Literals",
571
+ passed: true,
572
+ blocking: true,
573
+ issues: [],
574
+ duration: Date.now() - startTime,
575
+ };
576
+ }
577
+ // Parse model fields whose type is an enum.
578
+ // Map: fieldName -> set(enumName)
579
+ const enumFieldNames = new Map();
580
+ {
581
+ const modelRegex = /\bmodel\s+(\w+)\s*\{([\s\S]*?)\n\}/g;
582
+ let match;
583
+ while ((match = modelRegex.exec(schema))) {
584
+ const body = match[2] ?? "";
585
+ for (const rawLine of body.split("\n")) {
586
+ const line = rawLine.replace(/\/\/.*$/, "").trim();
587
+ if (!line)
588
+ continue;
589
+ if (line.startsWith("@@"))
590
+ continue;
591
+ if (line.startsWith("@"))
592
+ continue;
593
+ const parts = line.split(/\s+/);
594
+ const fieldName = parts[0];
595
+ const typeTokenRaw = parts[1];
596
+ if (!fieldName || !typeTokenRaw)
597
+ continue;
598
+ const typeToken = typeTokenRaw.replace(/\?$/, "").replace(/\[\]$/, "");
599
+ if (!enumValues.has(typeToken))
600
+ continue;
601
+ const set = enumFieldNames.get(fieldName) ?? new Set();
602
+ set.add(typeToken);
603
+ enumFieldNames.set(fieldName, set);
604
+ }
605
+ }
606
+ }
607
+ if (enumFieldNames.size === 0) {
608
+ return {
609
+ name: "Prisma Enum Literals",
610
+ passed: true,
611
+ blocking: true,
612
+ issues: [],
613
+ duration: Date.now() - startTime,
614
+ };
615
+ }
616
+ const files = await file_cache_1.fileCache.getFiles("{app/api,lib}/**/*.{ts,tsx}", { ignore: EXCLUDED });
617
+ const keyValueRegex = /\b([A-Za-z_][A-Za-z0-9_]*)\s*:\s*(['"])([A-Za-z_][A-Za-z0-9_]*)\2/g;
618
+ for (const file of files) {
619
+ const content = fs.readFileSync(file, "utf8");
620
+ if (!content.includes("prisma."))
621
+ continue;
622
+ const lines = content.split("\n");
623
+ const prismaLineIndexes = [];
624
+ for (let i = 0; i < lines.length; i++) {
625
+ if ((lines[i] ?? "").includes("prisma."))
626
+ prismaLineIndexes.push(i);
627
+ }
628
+ // Heuristic ignore: progress callback payloads often have `status: "processing"`
629
+ // and other literals that are NOT Prisma enum fields, but may be located near Prisma
630
+ // usage in the same file. This avoids false positives without weakening Prisma writes.
631
+ const ignoreEnumLiteralLine = new Array(lines.length).fill(false);
632
+ {
633
+ let inOnProgressObject = false;
634
+ let inInterfaceOrType = false;
635
+ let inResultsPush = false;
636
+ let braceDepth = 0;
637
+ for (let i = 0; i < lines.length; i++) {
638
+ const raw = lines[i] ?? "";
639
+ // Track interface/type definitions (not Prisma writes)
640
+ if (!inInterfaceOrType && /^\s*(?:interface|type)\s+\w+/.test(raw)) {
641
+ inInterfaceOrType = true;
642
+ braceDepth = 0;
643
+ }
644
+ if (inInterfaceOrType) {
645
+ braceDepth += (raw.match(/\{/g) || []).length;
646
+ braceDepth -= (raw.match(/\}/g) || []).length;
647
+ ignoreEnumLiteralLine[i] = true;
648
+ if (braceDepth <= 0 && raw.includes("}")) {
649
+ inInterfaceOrType = false;
650
+ }
651
+ }
652
+ // Track results.push({ ... }) patterns (local result objects, not Prisma)
653
+ if (!inResultsPush && /results\.push\s*\(\s*\{/.test(raw)) {
654
+ inResultsPush = true;
655
+ }
656
+ if (inResultsPush) {
657
+ ignoreEnumLiteralLine[i] = true;
658
+ if (/\}\s*\)\s*;/.test(raw) || raw.includes("});")) {
659
+ inResultsPush = false;
660
+ }
661
+ }
662
+ // Track onProgress callback payloads
663
+ if (!inOnProgressObject && /\bonProgress\?\.\s*\(\s*\{/.test(raw)) {
664
+ inOnProgressObject = true;
665
+ }
666
+ if (inOnProgressObject) {
667
+ ignoreEnumLiteralLine[i] = true;
668
+ if (/\}\s*\)\s*;/.test(raw) || raw.includes("});")) {
669
+ inOnProgressObject = false;
670
+ }
671
+ }
672
+ }
673
+ }
674
+ // Cheap guard: if no Prisma usage, skip
675
+ if (prismaLineIndexes.length === 0)
676
+ continue;
677
+ for (let index = 0; index < lines.length; index++) {
678
+ const rawLine = lines[index] ?? "";
679
+ const line = rawLine.trim();
680
+ if (ignoreEnumLiteralLine[index])
681
+ continue;
682
+ // Skip comment-only lines
683
+ if (line.startsWith("//") || line.startsWith("*"))
684
+ continue;
685
+ // Only consider lines near Prisma usage to reduce false positives.
686
+ const minDistance = prismaLineIndexes.reduce((min, prismaIndex) => {
687
+ const dist = Math.abs(prismaIndex - index);
688
+ return dist < min ? dist : min;
689
+ }, Number.POSITIVE_INFINITY);
690
+ if (minDistance > 50)
691
+ continue;
692
+ keyValueRegex.lastIndex = 0;
693
+ let m;
694
+ while ((m = keyValueRegex.exec(rawLine))) {
695
+ const fieldName = m[1];
696
+ const literal = m[3];
697
+ const enumCandidates = enumFieldNames.get(fieldName);
698
+ if (!enumCandidates)
699
+ continue;
700
+ const lower = literal.toLowerCase();
701
+ for (const enumName of enumCandidates) {
702
+ const lowerMap = enumValueByLower.get(enumName);
703
+ if (!lowerMap)
704
+ continue;
705
+ const expected = lowerMap.get(lower);
706
+ if (!expected)
707
+ continue;
708
+ // If the literal matches a known enum value case-insensitively, require exact match.
709
+ if (literal !== expected) {
710
+ issues.push({
711
+ file,
712
+ line: index + 1,
713
+ type: "prisma-enum-literal",
714
+ severity: "error",
715
+ message: `Prisma enum field "${fieldName}" uses "${literal}" but schema expects ${enumName}.${expected}`,
716
+ suggestion: `Use ${enumName}.${expected} (import from @prisma/client) or the exact string "${expected}"`,
717
+ snippet: rawLine.trim().substring(0, 140),
718
+ });
719
+ break;
720
+ }
721
+ }
722
+ }
723
+ }
724
+ }
725
+ const hasErrors = issues.some((i) => i.severity === "error");
726
+ return {
727
+ name: "Prisma Enum Literals",
728
+ passed: !hasErrors,
729
+ blocking: true,
730
+ issues,
731
+ duration: Date.now() - startTime,
732
+ };
733
+ }
734
+ /**
735
+ * Check 6: Floating Promise Detection (BLOCKING)
736
+ *
737
+ * Promises that are not awaited or handled can cause silent failures.
738
+ */
739
+ async checkFloatingPromises() {
740
+ const startTime = Date.now();
741
+ const issues = [];
742
+ const files = await getAppLibFiles();
743
+ for (const file of files) {
744
+ const content = fs.readFileSync(file, "utf8");
745
+ const lines = content.split("\n");
746
+ lines.forEach((line, index) => {
747
+ // Skip comments
748
+ if (line.trim().startsWith("//") || line.trim().startsWith("*"))
749
+ return;
750
+ // Pattern: Calling async function without await, .then(), or .catch()
751
+ // This is a simplified check - comprehensive check needs TypeScript analysis
752
+ // Check for Promise.all/Promise.race/Promise.allSettled without await
753
+ if (/(?<!await\s+)Promise\.(all|race|allSettled)\s*\(/.test(line) &&
754
+ !/\.then\(|\.catch\(|await/.test(line)) {
755
+ issues.push({
756
+ file,
757
+ line: index + 1,
758
+ type: "floating-promise",
759
+ severity: "warning",
760
+ message: "Promise.* call may not be awaited",
761
+ suggestion: "Add await or handle with .then()/.catch()",
762
+ snippet: line.trim().substring(0, 80),
763
+ });
764
+ }
765
+ // Check for fetch() without await
766
+ if (/(?<!await\s+)fetch\s*\(/.test(line) &&
767
+ !/\.then\(|\.catch\(|await|=\s*fetch/.test(line)) {
768
+ issues.push({
769
+ file,
770
+ line: index + 1,
771
+ type: "floating-fetch",
772
+ severity: "warning",
773
+ message: "fetch() call may not be awaited",
774
+ suggestion: "Add await or handle the promise",
775
+ snippet: line.trim().substring(0, 80),
776
+ });
777
+ }
778
+ });
779
+ }
780
+ // These are warnings, not blocking
781
+ return {
782
+ name: "Floating Promise Detection",
783
+ passed: true,
784
+ blocking: false,
785
+ issues,
786
+ duration: Date.now() - startTime,
787
+ };
788
+ }
789
+ /**
790
+ * Check 7: CVA Enforcement for Variant Components (WARNING ${chars.arrow} BLOCKING after migration)
791
+ *
792
+ * Components with variant/size props MUST use CVA for type-safe, consistent styling.
793
+ */
794
+ async checkCvaEnforcement() {
795
+ const startTime = Date.now();
796
+ const issues = [];
797
+ const files = await file_cache_1.fileCache.getFiles("components/**/*.{tsx,jsx}", {
798
+ ignore: [...EXCLUDED, "**/ui/**"], // Skip UI package which should already use CVA
799
+ });
800
+ for (const file of files) {
801
+ const content = fs.readFileSync(file, "utf8");
802
+ const lines = content.split("\n");
803
+ // Check if component declares variant/size props
804
+ const hasVariantProp = /\bvariant\s*[?:]?\s*:\s*(?:string|['"][^'"]+['"]|VariantProps)/.test(content);
805
+ const hasSizeProp = /\bsize\s*[?:]?\s*:\s*(?:string|['"][^'"]+['"])/.test(content);
806
+ // Check if using CVA
807
+ const hasCva = /import.*\bcva\b|from\s+['"]class-variance-authority['"]/.test(content);
808
+ // Heuristic: only require CVA when variant/size appear to be used for runtime styling/branching.
809
+ // This avoids false positives for thin wrapper components that simply type and forward variant/size
810
+ // into a downstream CVA-backed UI component.
811
+ const usesVariantAtRuntime = lines.some((line) => {
812
+ const trimmed = line.trim();
813
+ if (trimmed.startsWith("//") || trimmed.startsWith("*"))
814
+ return false;
815
+ // Only flag when variant/size drive style branching (not simple forwarding)
816
+ if (/\bvariant\b\s*===?/.test(trimmed))
817
+ return true;
818
+ if (/\bsize\b\s*===?/.test(trimmed))
819
+ return true;
820
+ if (/\bswitch\s*\(\s*variant\s*\)/.test(trimmed))
821
+ return true;
822
+ if (/\bswitch\s*\(\s*size\s*\)/.test(trimmed))
823
+ return true;
824
+ if (/\$\{\s*variant\s*\}/.test(trimmed))
825
+ return true;
826
+ if (/\$\{\s*size\s*\}/.test(trimmed))
827
+ return true;
828
+ return false;
829
+ });
830
+ if ((hasVariantProp || hasSizeProp) && usesVariantAtRuntime && !hasCva) {
831
+ // Find the first line that defines variant/size to point the developer at the API surface.
832
+ for (let index = 0; index < lines.length; index++) {
833
+ const line = lines[index] ?? "";
834
+ if (/\bvariant\s*[?:]?\s*:/.test(line) || /\bsize\s*[?:]?\s*:/.test(line)) {
835
+ issues.push({
836
+ file,
837
+ line: index + 1,
838
+ type: "missing-cva",
839
+ severity: "warning", // Will become 'error' after migration
840
+ message: "Component uses variant/size for styling but is missing CVA",
841
+ suggestion: 'Import cva from "class-variance-authority" and define variants',
842
+ snippet: line.trim().substring(0, 120),
843
+ });
844
+ break;
845
+ }
846
+ }
847
+ }
848
+ // Check for manual variant handling
849
+ lines.forEach((line, index) => {
850
+ // Pattern: variant === "primary" ? "..." : "..."
851
+ if (/variant\s*===?\s*["'][^"']+["']\s*\?/.test(line)) {
852
+ if (!hasCva) {
853
+ issues.push({
854
+ file,
855
+ line: index + 1,
856
+ type: "manual-variant-handling",
857
+ severity: "warning",
858
+ message: "Manual variant ternary should use CVA variants",
859
+ suggestion: "Define variants with cva() for type-safe styling",
860
+ snippet: line.trim().substring(0, 80),
861
+ });
862
+ }
863
+ }
864
+ });
865
+ }
866
+ // Currently warning-only during migration
867
+ return {
868
+ name: "CVA Enforcement",
869
+ passed: true,
870
+ blocking: false, // Will become blocking after migration complete
871
+ issues,
872
+ duration: Date.now() - startTime,
873
+ };
874
+ }
875
+ /**
876
+ * Check 8: Unsafe Type Assertions in Critical Paths (WARNING)
877
+ *
878
+ * Excessive 'as any' or 'as unknown' can hide type errors.
879
+ */
880
+ async checkTypeSafety() {
881
+ const startTime = Date.now();
882
+ const issues = [];
883
+ const files = await file_cache_1.fileCache.getFiles("{app/api,lib/api}/**/*.{ts,tsx}", { ignore: EXCLUDED });
884
+ for (const file of files) {
885
+ const content = fs.readFileSync(file, "utf8");
886
+ const lines = content.split("\n");
887
+ lines.forEach((line, index) => {
888
+ // Skip comments
889
+ if (line.trim().startsWith("//") || line.trim().startsWith("*"))
890
+ return;
891
+ // Check for dangerous patterns
892
+ if (/as\s+any\s*\)\./.test(line)) {
893
+ issues.push({
894
+ file,
895
+ line: index + 1,
896
+ type: "dangerous-any-chain",
897
+ severity: "warning",
898
+ message: '"as any" followed by property access - potential runtime error',
899
+ suggestion: "Use proper type narrowing or type guards",
900
+ snippet: line.trim().substring(0, 80),
901
+ });
902
+ }
903
+ // Check for 'as unknown as X' pattern (double casting)
904
+ if (/as\s+unknown\s+as\s+/.test(line)) {
905
+ issues.push({
906
+ file,
907
+ line: index + 1,
908
+ type: "double-cast",
909
+ severity: "warning",
910
+ message: "Double type assertion (as unknown as X) - indicates type mismatch",
911
+ suggestion: "Review types and fix the underlying type issue",
912
+ snippet: line.trim().substring(0, 80),
913
+ });
914
+ }
915
+ });
916
+ }
917
+ return {
918
+ name: "Type Safety",
919
+ passed: true,
920
+ blocking: false,
921
+ issues,
922
+ duration: Date.now() - startTime,
923
+ };
924
+ }
925
+ /**
926
+ * Check 9: ESLint Concurrency Enforcement (BLOCKING)
927
+ *
928
+ * ESLint must run with multithreading enabled in our scripts/preflights.
929
+ * ESLint v9 defaults to `--concurrency off`, so we hard-fail if any
930
+ * maintained runner regresses and drops the flag.
931
+ */
932
+ async checkEslintConcurrencyEnforcement() {
933
+ const startTime = Date.now();
934
+ const issues = [];
935
+ const requiredScriptKeys = [
936
+ { key: "lint", suggestion: "Ensure lint script includes `--concurrency auto`." },
937
+ { key: "lint:fix", suggestion: "Ensure lint:fix script includes `--concurrency auto`." },
938
+ ];
939
+ const requiredFiles = [
940
+ {
941
+ file: "scripts/active/preflights/specialized/lint-validation.ts",
942
+ mustInclude: /--concurrency\s+auto/,
943
+ suggestion: "Add `--concurrency auto` to the ESLint command used by lint-validation preflight.",
944
+ },
945
+ {
946
+ file: "scripts/active/preflights/utils/eslint-integration.ts",
947
+ mustInclude: /--concurrency\s+auto/,
948
+ suggestion: "Ensure ESLintIntegration always adds `--concurrency auto` when invoking ESLint.",
949
+ },
950
+ ];
951
+ const lineOfIndex = (content, index) => {
952
+ if (index <= 0)
953
+ return 1;
954
+ return content.slice(0, index).split("\n").length;
955
+ };
956
+ const readText = (relPath) => {
957
+ const abs = path.join(process.cwd(), relPath);
958
+ return { abs, content: fs.readFileSync(abs, "utf8") };
959
+ };
960
+ // 1) package.json scripts must include --concurrency (and not off)
961
+ try {
962
+ const { abs, content } = readText("package.json");
963
+ const parsed = JSON.parse(content);
964
+ const scripts = parsed?.scripts;
965
+ if (!scripts) {
966
+ issues.push({
967
+ file: abs,
968
+ line: 1,
969
+ type: "missing-scripts",
970
+ severity: "error",
971
+ message: "package.json missing scripts section",
972
+ });
973
+ }
974
+ else {
975
+ for (const { key, suggestion } of requiredScriptKeys) {
976
+ const value = scripts[key];
977
+ if (!value) {
978
+ issues.push({
979
+ file: abs,
980
+ line: 1,
981
+ type: "missing-script",
982
+ severity: "error",
983
+ message: `package.json scripts.${key} is missing`,
984
+ suggestion,
985
+ });
986
+ continue;
987
+ }
988
+ const hasConcurrency = /--concurrency\b/.test(value);
989
+ const explicitlyOff = /--concurrency\s+off\b/.test(value);
990
+ if (!hasConcurrency || explicitlyOff) {
991
+ const idx = content.indexOf(`\"${key}\"`);
992
+ issues.push({
993
+ file: abs,
994
+ line: lineOfIndex(content, idx),
995
+ type: "eslint-concurrency-missing",
996
+ severity: "error",
997
+ message: `package.json scripts.${key} must enable ESLint multithreading (missing/disabled --concurrency)`,
998
+ suggestion,
999
+ snippet: value.substring(0, 140),
1000
+ });
1001
+ }
1002
+ }
1003
+ }
1004
+ }
1005
+ catch (error) {
1006
+ issues.push({
1007
+ file: "package.json",
1008
+ line: 1,
1009
+ type: "package-json-read-failed",
1010
+ severity: "error",
1011
+ message: `Failed reading/parsing package.json: ${error?.message ?? String(error)}`,
1012
+ });
1013
+ }
1014
+ // 2) Known maintained runners must include concurrency
1015
+ for (const entry of requiredFiles) {
1016
+ try {
1017
+ const { abs, content } = readText(entry.file);
1018
+ if (!entry.mustInclude.test(content)) {
1019
+ issues.push({
1020
+ file: abs,
1021
+ line: 1,
1022
+ type: "eslint-concurrency-missing",
1023
+ severity: "error",
1024
+ message: `Missing required ESLint concurrency flag in ${entry.file}`,
1025
+ suggestion: entry.suggestion,
1026
+ });
1027
+ }
1028
+ }
1029
+ catch (error) {
1030
+ issues.push({
1031
+ file: entry.file,
1032
+ line: 1,
1033
+ type: "file-read-failed",
1034
+ severity: "error",
1035
+ message: `Failed to read ${entry.file}: ${error?.message ?? String(error)}`,
1036
+ });
1037
+ }
1038
+ }
1039
+ // 3) Scan preflight scripts for direct execSync('...eslint...') usage without concurrency
1040
+ try {
1041
+ const preflightFiles = await file_cache_1.fileCache.getFiles("scripts/active/preflights/**/*.{ts,js,mjs,cjs}", { ignore: EXCLUDED });
1042
+ const literalRe = /execSync\(\s*([`"'])([\s\S]*?)\1\s*(?:,|\))/g;
1043
+ for (const file of preflightFiles) {
1044
+ const content = fs.readFileSync(file, "utf8");
1045
+ let match;
1046
+ // eslint-disable-next-line no-cond-assign
1047
+ while ((match = literalRe.exec(content))) {
1048
+ const cmd = match[2] ?? "";
1049
+ if (!/\beslint\b/.test(cmd))
1050
+ continue;
1051
+ // Allow non-lint informational calls
1052
+ if (/\beslint\b\s+--(?:help|version)\b/.test(cmd))
1053
+ continue;
1054
+ const hasConcurrency = /--concurrency\b/.test(cmd);
1055
+ const explicitlyOff = /--concurrency\s+off\b/.test(cmd);
1056
+ if (!hasConcurrency || explicitlyOff) {
1057
+ issues.push({
1058
+ file,
1059
+ line: lineOfIndex(content, match.index),
1060
+ type: "eslint-concurrency-missing",
1061
+ severity: "error",
1062
+ message: "Preflight execSync ESLint command missing `--concurrency` (defaults to off)",
1063
+ suggestion: "Add `--concurrency auto` to the ESLint command string.",
1064
+ snippet: cmd.replace(/\s+/g, " ").trim().substring(0, 140),
1065
+ });
1066
+ }
1067
+ }
1068
+ }
1069
+ }
1070
+ catch (error) {
1071
+ issues.push({
1072
+ file: "scripts/active/preflights/**",
1073
+ line: 1,
1074
+ type: "preflight-scan-failed",
1075
+ severity: "warning",
1076
+ message: `Failed scanning preflight scripts for ESLint commands: ${error?.message ?? String(error)}`,
1077
+ });
1078
+ }
1079
+ const hasErrors = issues.some((i) => i.severity === "error");
1080
+ return {
1081
+ name: "ESLint Concurrency Enforcement",
1082
+ passed: !hasErrors,
1083
+ blocking: true,
1084
+ issues,
1085
+ duration: Date.now() - startTime,
1086
+ };
1087
+ }
1088
+ /**
1089
+ * Check 10: CardGraded Semantics (BLOCKING)
1090
+ *
1091
+ * Guardrail: WATA is a video-game grader and must not appear as a `CardGraded`
1092
+ * value on catalogNonSportsCards writes (create/upsert/update).
1093
+ */
1094
+ async checkCardGradedSemantics() {
1095
+ const startTime = Date.now();
1096
+ const issues = [];
1097
+ const files = await file_cache_1.fileCache.getFiles("{app,lib,scripts,components}/**/*.{ts,tsx,js,jsx}", {
1098
+ ignore: EXCLUDED,
1099
+ });
1100
+ const callPattern = /catalogNonSportsCards\.(?:create|createMany|upsert|update|updateMany)\s*\(/;
1101
+ const violationPattern = /catalogNonSportsCards\.(?:create|createMany|upsert|update|updateMany)\s*\([\s\S]{0,2500}?\bgraded\s*:\s*(?:CardGraded\.)?["']WATA["']/;
1102
+ for (const file of files) {
1103
+ const content = fs.readFileSync(file, "utf8");
1104
+ if (!callPattern.test(content))
1105
+ continue;
1106
+ // Fast path: no WATA anywhere in the file
1107
+ if (!/\bWATA\b/.test(content))
1108
+ continue;
1109
+ const match = content.match(violationPattern);
1110
+ if (!match)
1111
+ continue;
1112
+ issues.push({
1113
+ file,
1114
+ line: 1,
1115
+ type: "cardgraded-wata-in-nonsports-write",
1116
+ severity: "error",
1117
+ message: "Invalid CardGraded usage: catalogNonSportsCards write sets graded to WATA",
1118
+ suggestion: 'Use graded: "UNGRADED" (or another card-appropriate value). Video-game grading belongs in catalogVideoGames fields (grader/wataGrade/wataSealGrade).',
1119
+ snippet: match[0].replace(/\s+/g, " ").trim().substring(0, 160),
1120
+ });
1121
+ }
1122
+ const hasErrors = issues.some((i) => i.severity === "error");
1123
+ return {
1124
+ name: "CardGraded Semantics (No WATA in catalogNonSportsCards)",
1125
+ passed: !hasErrors,
1126
+ blocking: true,
1127
+ issues,
1128
+ duration: Date.now() - startTime,
1129
+ };
1130
+ }
1131
+ /**
1132
+ * Check 11: Card grade-field consistency (BLOCKING)
1133
+ *
1134
+ * Prevents semantically invalid grader/grade-field combinations:
1135
+ * - graded: "PSA" + bgsGrade set
1136
+ * - graded: "BGS" + psaGrade set
1137
+ * - graded: "CGC" + psaGrade/bgsGrade/sgcGrade set
1138
+ * - graded: "SGC" + psaGrade/bgsGrade/cgcGrade set
1139
+ * - graded: "UNGRADED" + any grade field set
1140
+ */
1141
+ async checkCardGradeFieldConsistency() {
1142
+ const startTime = Date.now();
1143
+ const issues = [];
1144
+ const files = await getAppLibScriptsFiles();
1145
+ const writeCallPattern = /(?:catalog(?:SportsCards|NonSportsCards|TcgCards)|coreListings)\.(?:create|createMany|upsert|update|updateMany)\s*\(/g;
1146
+ const hasNonNullPsaGrade = /\bpsaGrade\s*:\s*(?!null\b|undefined\b)/;
1147
+ const hasNonNullBgsGrade = /\bbgsGrade\s*:\s*(?!null\b|undefined\b)/;
1148
+ const hasNonNullCgcGrade = /\bcgcGrade\s*:\s*(?!null\b|undefined\b)/;
1149
+ const hasNonNullSgcGrade = /\bsgcGrade\s*:\s*(?!null\b|undefined\b)/;
1150
+ const extractCallObjectLiteral = (content, callStartIndex) => {
1151
+ const firstBrace = content.indexOf("{", callStartIndex);
1152
+ if (firstBrace === -1)
1153
+ return null;
1154
+ let depth = 0;
1155
+ let inSingle = false;
1156
+ let inDouble = false;
1157
+ let inTemplate = false;
1158
+ let inLineComment = false;
1159
+ let inBlockComment = false;
1160
+ let escaped = false;
1161
+ for (let i = firstBrace; i < content.length; i++) {
1162
+ const ch = content[i];
1163
+ const next = content[i + 1];
1164
+ if (inLineComment) {
1165
+ if (ch === "\n")
1166
+ inLineComment = false;
1167
+ continue;
1168
+ }
1169
+ if (inBlockComment) {
1170
+ if (ch === "*" && next === "/") {
1171
+ inBlockComment = false;
1172
+ i++;
1173
+ }
1174
+ continue;
1175
+ }
1176
+ if (!inSingle && !inDouble && !inTemplate) {
1177
+ if (ch === "/" && next === "/") {
1178
+ inLineComment = true;
1179
+ i++;
1180
+ continue;
1181
+ }
1182
+ if (ch === "/" && next === "*") {
1183
+ inBlockComment = true;
1184
+ i++;
1185
+ continue;
1186
+ }
1187
+ }
1188
+ if (escaped) {
1189
+ escaped = false;
1190
+ continue;
1191
+ }
1192
+ if (ch === "\\" && (inSingle || inDouble || inTemplate)) {
1193
+ escaped = true;
1194
+ continue;
1195
+ }
1196
+ if (!inDouble && !inTemplate && ch === "'") {
1197
+ inSingle = !inSingle;
1198
+ continue;
1199
+ }
1200
+ if (!inSingle && !inTemplate && ch === '"') {
1201
+ inDouble = !inDouble;
1202
+ continue;
1203
+ }
1204
+ if (!inSingle && !inDouble && ch === "`") {
1205
+ inTemplate = !inTemplate;
1206
+ continue;
1207
+ }
1208
+ if (inSingle || inDouble || inTemplate)
1209
+ continue;
1210
+ if (ch === "{") {
1211
+ depth++;
1212
+ continue;
1213
+ }
1214
+ if (ch === "}") {
1215
+ depth--;
1216
+ if (depth === 0) {
1217
+ return content.slice(firstBrace, i + 1);
1218
+ }
1219
+ }
1220
+ }
1221
+ return null;
1222
+ };
1223
+ for (const file of files) {
1224
+ const content = fs.readFileSync(file, "utf8");
1225
+ if (!/(?:catalog(?:SportsCards|NonSportsCards|TcgCards)|coreListings)\./.test(content))
1226
+ continue;
1227
+ if (!/\bgraded\b|\bpsaGrade\b|\bbgsGrade\b|\bcgcGrade\b|\bsgcGrade\b/.test(content))
1228
+ continue;
1229
+ writeCallPattern.lastIndex = 0;
1230
+ let match;
1231
+ while ((match = writeCallPattern.exec(content)) !== null) {
1232
+ const window = extractCallObjectLiteral(content, match.index);
1233
+ if (!window)
1234
+ continue;
1235
+ const isPSA = /\bgraded\s*:\s*(?:CardGraded\.)?["']PSA["']/.test(window);
1236
+ const isBGS = /\bgraded\s*:\s*(?:CardGraded\.)?["']BGS["']/.test(window);
1237
+ const isCGC = /\bgraded\s*:\s*(?:CardGraded\.)?['"]CGC['"]/.test(window);
1238
+ const isSGC = /\bgraded\s*:\s*(?:CardGraded\.)?['"]SGC['"]/.test(window);
1239
+ const isUngraded = /\bgraded\s*:\s*(?:CardGraded\.)?["']UNGRADED["']/.test(window);
1240
+ if (isPSA &&
1241
+ (hasNonNullBgsGrade.test(window) ||
1242
+ hasNonNullCgcGrade.test(window) ||
1243
+ hasNonNullSgcGrade.test(window))) {
1244
+ issues.push({
1245
+ file,
1246
+ line: 1,
1247
+ type: "grading-field-mismatch",
1248
+ severity: "error",
1249
+ message: "Invalid grading payload: graded is PSA but a non-PSA grade field is set",
1250
+ suggestion: "Clear bgsGrade/cgcGrade/sgcGrade (or change graded to match the grade field).",
1251
+ snippet: window.replace(/\s+/g, " ").trim().substring(0, 180),
1252
+ });
1253
+ }
1254
+ if (isBGS &&
1255
+ (hasNonNullPsaGrade.test(window) ||
1256
+ hasNonNullCgcGrade.test(window) ||
1257
+ hasNonNullSgcGrade.test(window))) {
1258
+ issues.push({
1259
+ file,
1260
+ line: 1,
1261
+ type: "grading-field-mismatch",
1262
+ severity: "error",
1263
+ message: "Invalid grading payload: graded is BGS but a non-BGS grade field is set",
1264
+ suggestion: "Clear psaGrade/cgcGrade/sgcGrade (or change graded to match the grade field).",
1265
+ snippet: window.replace(/\s+/g, " ").trim().substring(0, 180),
1266
+ });
1267
+ }
1268
+ if (isCGC &&
1269
+ (hasNonNullPsaGrade.test(window) ||
1270
+ hasNonNullBgsGrade.test(window) ||
1271
+ hasNonNullSgcGrade.test(window))) {
1272
+ issues.push({
1273
+ file,
1274
+ line: 1,
1275
+ type: "grading-field-mismatch",
1276
+ severity: "error",
1277
+ message: "Invalid grading payload: graded is CGC but a non-CGC grade field is set",
1278
+ suggestion: "Clear psaGrade/bgsGrade/sgcGrade (or change graded to match the grade field).",
1279
+ snippet: window.replace(/\s+/g, " ").trim().substring(0, 180),
1280
+ });
1281
+ }
1282
+ if (isSGC &&
1283
+ (hasNonNullPsaGrade.test(window) ||
1284
+ hasNonNullBgsGrade.test(window) ||
1285
+ hasNonNullCgcGrade.test(window))) {
1286
+ issues.push({
1287
+ file,
1288
+ line: 1,
1289
+ type: "grading-field-mismatch",
1290
+ severity: "error",
1291
+ message: "Invalid grading payload: graded is SGC but a non-SGC grade field is set",
1292
+ suggestion: "Clear psaGrade/bgsGrade/cgcGrade (or change graded to match the grade field).",
1293
+ snippet: window.replace(/\s+/g, " ").trim().substring(0, 180),
1294
+ });
1295
+ }
1296
+ if (isUngraded &&
1297
+ (hasNonNullPsaGrade.test(window) ||
1298
+ hasNonNullBgsGrade.test(window) ||
1299
+ hasNonNullCgcGrade.test(window) ||
1300
+ hasNonNullSgcGrade.test(window))) {
1301
+ issues.push({
1302
+ file,
1303
+ line: 1,
1304
+ type: "grading-field-mismatch",
1305
+ severity: "error",
1306
+ message: "Invalid grading payload: graded is UNGRADED but a grade field is set",
1307
+ suggestion: 'Either set graded to the correct grader (e.g., "PSA"/"BGS"/"CGC"/"SGC") or clear psaGrade/bgsGrade/cgcGrade/sgcGrade.',
1308
+ snippet: window.replace(/\s+/g, " ").trim().substring(0, 180),
1309
+ });
1310
+ }
1311
+ }
1312
+ }
1313
+ const hasErrors = issues.some((i) => i.severity === "error");
1314
+ return {
1315
+ name: "Card Grade Field Consistency",
1316
+ passed: !hasErrors,
1317
+ blocking: true,
1318
+ issues,
1319
+ duration: Date.now() - startTime,
1320
+ };
1321
+ }
1322
+ /**
1323
+ * Check 12: Serial number consistency (BLOCKING)
1324
+ *
1325
+ * Invariant:
1326
+ * - If serialNumbered is true, serialNumber must be present and non-empty.
1327
+ *
1328
+ * This catches common catalog/listing data-quality bugs in write payloads.
1329
+ */
1330
+ async checkSerialNumberConsistency() {
1331
+ const startTime = Date.now();
1332
+ const issues = [];
1333
+ const files = await getAppLibScriptsFiles();
1334
+ const writeCallPattern = /(catalog(?:SportsCards|NonSportsCards|TcgCards)|coreListings)\.(?:create|createMany|upsert|update|updateMany)\s*\(/g;
1335
+ const extractCallObjectLiteral = (content, callStartIndex) => {
1336
+ const firstBrace = content.indexOf("{", callStartIndex);
1337
+ if (firstBrace === -1)
1338
+ return null;
1339
+ let depth = 0;
1340
+ let inSingle = false;
1341
+ let inDouble = false;
1342
+ let inTemplate = false;
1343
+ let inLineComment = false;
1344
+ let inBlockComment = false;
1345
+ let escaped = false;
1346
+ for (let i = firstBrace; i < content.length; i++) {
1347
+ const ch = content[i];
1348
+ const next = content[i + 1];
1349
+ if (inLineComment) {
1350
+ if (ch === "\n")
1351
+ inLineComment = false;
1352
+ continue;
1353
+ }
1354
+ if (inBlockComment) {
1355
+ if (ch === "*" && next === "/") {
1356
+ inBlockComment = false;
1357
+ i++;
1358
+ }
1359
+ continue;
1360
+ }
1361
+ if (!inSingle && !inDouble && !inTemplate) {
1362
+ if (ch === "/" && next === "/") {
1363
+ inLineComment = true;
1364
+ i++;
1365
+ continue;
1366
+ }
1367
+ if (ch === "/" && next === "*") {
1368
+ inBlockComment = true;
1369
+ i++;
1370
+ continue;
1371
+ }
1372
+ }
1373
+ if (escaped) {
1374
+ escaped = false;
1375
+ continue;
1376
+ }
1377
+ if (ch === "\\" && (inSingle || inDouble || inTemplate)) {
1378
+ escaped = true;
1379
+ continue;
1380
+ }
1381
+ if (!inDouble && !inTemplate && ch === "'") {
1382
+ inSingle = !inSingle;
1383
+ continue;
1384
+ }
1385
+ if (!inSingle && !inTemplate && ch === '"') {
1386
+ inDouble = !inDouble;
1387
+ continue;
1388
+ }
1389
+ if (!inSingle && !inDouble && ch === "`") {
1390
+ inTemplate = !inTemplate;
1391
+ continue;
1392
+ }
1393
+ if (inSingle || inDouble || inTemplate)
1394
+ continue;
1395
+ if (ch === "{") {
1396
+ depth++;
1397
+ continue;
1398
+ }
1399
+ if (ch === "}") {
1400
+ depth--;
1401
+ if (depth === 0) {
1402
+ return content.slice(firstBrace, i + 1);
1403
+ }
1404
+ }
1405
+ }
1406
+ return null;
1407
+ };
1408
+ const serialFlagTrue = /\bserialNumbered\s*:\s*true\b/;
1409
+ const hasSerialField = /\bserialNumber\s*:/;
1410
+ const serialEmpty = /\bserialNumber\s*:\s*(?:''|""|null|undefined)\b/;
1411
+ for (const file of files) {
1412
+ const content = fs.readFileSync(file, "utf8");
1413
+ if (!/serialNumbered|serialNumber/.test(content))
1414
+ continue;
1415
+ if (!/(catalog(?:SportsCards|NonSportsCards|TcgCards)|coreListings)\./.test(content))
1416
+ continue;
1417
+ writeCallPattern.lastIndex = 0;
1418
+ let match;
1419
+ while ((match = writeCallPattern.exec(content)) !== null) {
1420
+ const window = extractCallObjectLiteral(content, match.index);
1421
+ if (!window)
1422
+ continue;
1423
+ if (!serialFlagTrue.test(window))
1424
+ continue;
1425
+ if (!hasSerialField.test(window) || serialEmpty.test(window)) {
1426
+ issues.push({
1427
+ file,
1428
+ line: 1,
1429
+ type: "serial-number-missing",
1430
+ severity: "error",
1431
+ message: "Invalid payload: serialNumbered is true but serialNumber is missing/empty",
1432
+ suggestion: "Provide a non-empty serialNumber when serialNumbered is true (or set serialNumbered to false).",
1433
+ snippet: window.replace(/\s+/g, " ").trim().substring(0, 180),
1434
+ });
1435
+ }
1436
+ }
1437
+ }
1438
+ const hasErrors = issues.some((i) => i.severity === "error");
1439
+ return {
1440
+ name: "Serial Number Consistency",
1441
+ passed: !hasErrors,
1442
+ blocking: true,
1443
+ issues,
1444
+ duration: Date.now() - startTime,
1445
+ };
1446
+ }
1447
+ /**
1448
+ * Check 13: Autograph consistency (BLOCKING)
1449
+ *
1450
+ * Invariants:
1451
+ * - If autographedAuthenticated/autographAuthenticated is true, autographed must be true.
1452
+ * - If autographed is explicitly false, autograph auth fields should not be populated.
1453
+ */
1454
+ async checkAutographConsistency() {
1455
+ const startTime = Date.now();
1456
+ const issues = [];
1457
+ const files = await getAppLibScriptsFiles();
1458
+ const writeCallPattern = /(catalog(?:SportsCards|NonSportsCards|TcgCards)|coreListings)\.(?:create|createMany|upsert|update|updateMany)\s*\(/g;
1459
+ const extractCallObjectLiteral = (content, callStartIndex) => {
1460
+ const firstBrace = content.indexOf("{", callStartIndex);
1461
+ if (firstBrace === -1)
1462
+ return null;
1463
+ let depth = 0;
1464
+ let inSingle = false;
1465
+ let inDouble = false;
1466
+ let inTemplate = false;
1467
+ let inLineComment = false;
1468
+ let inBlockComment = false;
1469
+ let escaped = false;
1470
+ for (let i = firstBrace; i < content.length; i++) {
1471
+ const ch = content[i];
1472
+ const next = content[i + 1];
1473
+ if (inLineComment) {
1474
+ if (ch === "\n")
1475
+ inLineComment = false;
1476
+ continue;
1477
+ }
1478
+ if (inBlockComment) {
1479
+ if (ch === "*" && next === "/") {
1480
+ inBlockComment = false;
1481
+ i++;
1482
+ }
1483
+ continue;
1484
+ }
1485
+ if (!inSingle && !inDouble && !inTemplate) {
1486
+ if (ch === "/" && next === "/") {
1487
+ inLineComment = true;
1488
+ i++;
1489
+ continue;
1490
+ }
1491
+ if (ch === "/" && next === "*") {
1492
+ inBlockComment = true;
1493
+ i++;
1494
+ continue;
1495
+ }
1496
+ }
1497
+ if (escaped) {
1498
+ escaped = false;
1499
+ continue;
1500
+ }
1501
+ if (ch === "\\" && (inSingle || inDouble || inTemplate)) {
1502
+ escaped = true;
1503
+ continue;
1504
+ }
1505
+ if (!inDouble && !inTemplate && ch === "'") {
1506
+ inSingle = !inSingle;
1507
+ continue;
1508
+ }
1509
+ if (!inSingle && !inTemplate && ch === '"') {
1510
+ inDouble = !inDouble;
1511
+ continue;
1512
+ }
1513
+ if (!inSingle && !inDouble && ch === "`") {
1514
+ inTemplate = !inTemplate;
1515
+ continue;
1516
+ }
1517
+ if (inSingle || inDouble || inTemplate)
1518
+ continue;
1519
+ if (ch === "{") {
1520
+ depth++;
1521
+ continue;
1522
+ }
1523
+ if (ch === "}") {
1524
+ depth--;
1525
+ if (depth === 0) {
1526
+ return content.slice(firstBrace, i + 1);
1527
+ }
1528
+ }
1529
+ }
1530
+ return null;
1531
+ };
1532
+ const autographedTrue = /\bautographed\s*:\s*true\b/;
1533
+ const autographedFalse = /\bautographed\s*:\s*false\b/;
1534
+ const authenticatedTrue = /\b(?:autographedAuthenticated|autographAuthenticated)\s*:\s*true\b/;
1535
+ const hasAuthenticationValue = /\bautographAuthentication\s*:\s*(?!null\b|undefined\b|''|""\b)/;
1536
+ const hasSigners = /\bautographSigners\s*:\s*\[/;
1537
+ const hasAutographGradedValue = /\bautographGraded\s*:\s*(?!null\b|undefined\b|''|""\b)/;
1538
+ const hasAutographGradeField = /\b(?:autographPsaGrade|autographBgsGrade|autographJsaGrade|autographSgcGrade)\s*:\s*(?!null\b|undefined\b)/;
1539
+ const hasAutographGradeMetadata = (w) => hasAutographGradedValue.test(w) || hasAutographGradeField.test(w);
1540
+ for (const file of files) {
1541
+ const content = fs.readFileSync(file, "utf8");
1542
+ if (!/autograph|autographed/.test(content))
1543
+ continue;
1544
+ if (!/(catalog(?:SportsCards|NonSportsCards|TcgCards)|coreListings)\./.test(content))
1545
+ continue;
1546
+ writeCallPattern.lastIndex = 0;
1547
+ let match;
1548
+ while ((match = writeCallPattern.exec(content)) !== null) {
1549
+ const window = extractCallObjectLiteral(content, match.index);
1550
+ if (!window)
1551
+ continue;
1552
+ const isAutographed = autographedTrue.test(window);
1553
+ if (authenticatedTrue.test(window) && !isAutographed) {
1554
+ issues.push({
1555
+ file,
1556
+ line: 1,
1557
+ type: "autograph-auth-without-autograph",
1558
+ severity: "error",
1559
+ message: "Invalid payload: autograph authenticated flag is true but autographed is not true",
1560
+ suggestion: "Set autographed: true when setting autographAuthenticated/autographedAuthenticated: true.",
1561
+ snippet: window.replace(/\s+/g, " ").trim().substring(0, 180),
1562
+ });
1563
+ }
1564
+ if (authenticatedTrue.test(window) && !hasAuthenticationValue.test(window)) {
1565
+ issues.push({
1566
+ file,
1567
+ line: 1,
1568
+ type: "autograph-auth-missing-authentication-method",
1569
+ severity: "error",
1570
+ message: "Invalid payload: autograph authenticated flag is true but autographAuthentication is missing/empty",
1571
+ suggestion: 'Provide autographAuthentication (e.g., "PSA/DNA", "BGS", "JSA") when setting autographAuthenticated/autographedAuthenticated: true.',
1572
+ snippet: window.replace(/\s+/g, " ").trim().substring(0, 180),
1573
+ });
1574
+ }
1575
+ if (hasAutographGradeMetadata(window) && !isAutographed) {
1576
+ issues.push({
1577
+ file,
1578
+ line: 1,
1579
+ type: "autograph-grade-without-autograph",
1580
+ severity: "error",
1581
+ message: "Invalid payload: autograph grading fields are populated but autographed is not true",
1582
+ suggestion: "Set autographed: true when setting autographGraded/autograph*Grade fields (or clear those fields).",
1583
+ snippet: window.replace(/\s+/g, " ").trim().substring(0, 180),
1584
+ });
1585
+ }
1586
+ if (autographedFalse.test(window) &&
1587
+ (hasAuthenticationValue.test(window) ||
1588
+ hasSigners.test(window) ||
1589
+ authenticatedTrue.test(window) ||
1590
+ hasAutographGradeMetadata(window))) {
1591
+ issues.push({
1592
+ file,
1593
+ line: 1,
1594
+ type: "autograph-fields-with-autographed-false",
1595
+ severity: "error",
1596
+ message: "Invalid payload: autographed is false but autograph fields/auth flags are populated",
1597
+ suggestion: "Either set autographed: true or clear autographAuthentication/autographSigners/auth flags and any autograph grading fields.",
1598
+ snippet: window.replace(/\s+/g, " ").trim().substring(0, 180),
1599
+ });
1600
+ }
1601
+ }
1602
+ }
1603
+ const hasErrors = issues.some((i) => i.severity === "error");
1604
+ return {
1605
+ name: "Autograph Consistency",
1606
+ passed: !hasErrors,
1607
+ blocking: true,
1608
+ issues,
1609
+ duration: Date.now() - startTime,
1610
+ };
1611
+ }
1612
+ /**
1613
+ * Check 14: Video game grading consistency (BLOCKING)
1614
+ *
1615
+ * Invariants (conservative / low false positives):
1616
+ * - If graded is explicitly true, grader must be present and not null/undefined.
1617
+ * - If graded is explicitly false, grader/wataGrade/wataSealGrade must not be populated.
1618
+ * - If wataSealGrade is set and grader is an explicit literal, it must be WATA.
1619
+ */
1620
+ async checkVideoGameGradingConsistency() {
1621
+ const startTime = Date.now();
1622
+ const issues = [];
1623
+ const files = await getAppLibScriptsFiles();
1624
+ const writeCallPattern = /catalogVideoGames\.(?:create|createMany|upsert|update|updateMany)\s*\(/g;
1625
+ const extractCallObjectLiteral = (content, callStartIndex) => {
1626
+ const firstBrace = content.indexOf("{", callStartIndex);
1627
+ if (firstBrace === -1)
1628
+ return null;
1629
+ let depth = 0;
1630
+ let inSingle = false;
1631
+ let inDouble = false;
1632
+ let inTemplate = false;
1633
+ let inLineComment = false;
1634
+ let inBlockComment = false;
1635
+ let escaped = false;
1636
+ for (let i = firstBrace; i < content.length; i++) {
1637
+ const ch = content[i];
1638
+ const next = content[i + 1];
1639
+ if (inLineComment) {
1640
+ if (ch === "\n")
1641
+ inLineComment = false;
1642
+ continue;
1643
+ }
1644
+ if (inBlockComment) {
1645
+ if (ch === "*" && next === "/") {
1646
+ inBlockComment = false;
1647
+ i++;
1648
+ }
1649
+ continue;
1650
+ }
1651
+ if (!inSingle && !inDouble && !inTemplate) {
1652
+ if (ch === "/" && next === "/") {
1653
+ inLineComment = true;
1654
+ i++;
1655
+ continue;
1656
+ }
1657
+ if (ch === "/" && next === "*") {
1658
+ inBlockComment = true;
1659
+ i++;
1660
+ continue;
1661
+ }
1662
+ }
1663
+ if (escaped) {
1664
+ escaped = false;
1665
+ continue;
1666
+ }
1667
+ if (ch === "\\" && (inSingle || inDouble || inTemplate)) {
1668
+ escaped = true;
1669
+ continue;
1670
+ }
1671
+ if (!inDouble && !inTemplate && ch === "'") {
1672
+ inSingle = !inSingle;
1673
+ continue;
1674
+ }
1675
+ if (!inSingle && !inTemplate && ch === '"') {
1676
+ inDouble = !inDouble;
1677
+ continue;
1678
+ }
1679
+ if (!inSingle && !inDouble && ch === "`") {
1680
+ inTemplate = !inTemplate;
1681
+ continue;
1682
+ }
1683
+ if (inSingle || inDouble || inTemplate)
1684
+ continue;
1685
+ if (ch === "{") {
1686
+ depth++;
1687
+ continue;
1688
+ }
1689
+ if (ch === "}") {
1690
+ depth--;
1691
+ if (depth === 0) {
1692
+ return content.slice(firstBrace, i + 1);
1693
+ }
1694
+ }
1695
+ }
1696
+ return null;
1697
+ };
1698
+ const gradedTrue = /\bgraded\s*:\s*true\b/;
1699
+ const gradedFalse = /\bgraded\s*:\s*false\b/;
1700
+ const hasGraderField = /\bgrader\s*:/;
1701
+ const graderEmpty = /\bgrader\s*:\s*(?:null|undefined)\b/;
1702
+ const graderNonNull = /\bgrader\s*:\s*(?!null\b|undefined\b)/;
1703
+ const hasNonNullWataGrade = /\bwataGrade\s*:\s*(?!null\b|undefined\b)/;
1704
+ const hasNonNullWataSealGrade = /\bwataSealGrade\s*:\s*(?!null\b|undefined\b)/;
1705
+ const graderLiteralNotWata = /\bgrader\s*:\s*(?:GameGrader\.)?['"](?!WATA\b)[A-Z_]+['"]/;
1706
+ for (const file of files) {
1707
+ const content = fs.readFileSync(file, "utf8");
1708
+ if (!/catalogVideoGames\./.test(content))
1709
+ continue;
1710
+ if (!/\bgraded\b|\bgrader\b|\bwataGrade\b|\bwataSealGrade\b/.test(content))
1711
+ continue;
1712
+ writeCallPattern.lastIndex = 0;
1713
+ let match;
1714
+ while ((match = writeCallPattern.exec(content)) !== null) {
1715
+ const window = extractCallObjectLiteral(content, match.index);
1716
+ if (!window)
1717
+ continue;
1718
+ if (gradedTrue.test(window)) {
1719
+ if (!hasGraderField.test(window) || graderEmpty.test(window)) {
1720
+ issues.push({
1721
+ file,
1722
+ line: 1,
1723
+ type: "videogame-grading-missing-grader",
1724
+ severity: "error",
1725
+ message: "Invalid payload: catalogVideoGames graded is true but grader is missing/empty",
1726
+ suggestion: 'Set grader when graded: true (e.g., "WATA"/"VGA"/"CGC_GAMES") or set graded to false.',
1727
+ snippet: window.replace(/\s+/g, " ").trim().substring(0, 180),
1728
+ });
1729
+ }
1730
+ }
1731
+ if (gradedFalse.test(window)) {
1732
+ if (graderNonNull.test(window) ||
1733
+ hasNonNullWataGrade.test(window) ||
1734
+ hasNonNullWataSealGrade.test(window)) {
1735
+ issues.push({
1736
+ file,
1737
+ line: 1,
1738
+ type: "videogame-grading-fields-while-ungraded",
1739
+ severity: "error",
1740
+ message: "Invalid payload: catalogVideoGames graded is false but grading fields are populated",
1741
+ suggestion: "Clear grader/wataGrade/wataSealGrade when graded is false (or set graded to true).",
1742
+ snippet: window.replace(/\s+/g, " ").trim().substring(0, 180),
1743
+ });
1744
+ }
1745
+ }
1746
+ if (hasNonNullWataSealGrade.test(window) && graderLiteralNotWata.test(window)) {
1747
+ issues.push({
1748
+ file,
1749
+ line: 1,
1750
+ type: "videogame-watasealgrade-nonwata",
1751
+ severity: "error",
1752
+ message: "Invalid payload: wataSealGrade is set but grader is explicitly not WATA",
1753
+ suggestion: 'Only set wataSealGrade with grader: "WATA" (or clear wataSealGrade).',
1754
+ snippet: window.replace(/\s+/g, " ").trim().substring(0, 180),
1755
+ });
1756
+ }
1757
+ }
1758
+ }
1759
+ const hasErrors = issues.some((i) => i.severity === "error");
1760
+ return {
1761
+ name: "Video Game Grading Consistency",
1762
+ passed: !hasErrors,
1763
+ blocking: true,
1764
+ issues,
1765
+ duration: Date.now() - startTime,
1766
+ };
1767
+ }
1768
+ /**
1769
+ * Check 15: Syndication consistency (BLOCKING)
1770
+ *
1771
+ * Invariant:
1772
+ * - If syndicatedToEbay/Amazon/Tcgplayer/Preplo is true, the corresponding
1773
+ * platform listing id must be present and non-empty.
1774
+ */
1775
+ async checkSyndicationConsistency() {
1776
+ const startTime = Date.now();
1777
+ const issues = [];
1778
+ const files = await getAppLibScriptsFiles();
1779
+ const writeCallPattern = /coreListings\.(?:create|createMany|upsert|update|updateMany)\s*\(/g;
1780
+ const extractCallObjectLiteral = (content, callStartIndex) => {
1781
+ const firstBrace = content.indexOf("{", callStartIndex);
1782
+ if (firstBrace === -1)
1783
+ return null;
1784
+ let depth = 0;
1785
+ let inSingle = false;
1786
+ let inDouble = false;
1787
+ let inTemplate = false;
1788
+ let inLineComment = false;
1789
+ let inBlockComment = false;
1790
+ let escaped = false;
1791
+ for (let i = firstBrace; i < content.length; i++) {
1792
+ const ch = content[i];
1793
+ const next = content[i + 1];
1794
+ if (inLineComment) {
1795
+ if (ch === "\n")
1796
+ inLineComment = false;
1797
+ continue;
1798
+ }
1799
+ if (inBlockComment) {
1800
+ if (ch === "*" && next === "/") {
1801
+ inBlockComment = false;
1802
+ i++;
1803
+ }
1804
+ continue;
1805
+ }
1806
+ if (!inSingle && !inDouble && !inTemplate) {
1807
+ if (ch === "/" && next === "/") {
1808
+ inLineComment = true;
1809
+ i++;
1810
+ continue;
1811
+ }
1812
+ if (ch === "/" && next === "*") {
1813
+ inBlockComment = true;
1814
+ i++;
1815
+ continue;
1816
+ }
1817
+ }
1818
+ if (escaped) {
1819
+ escaped = false;
1820
+ continue;
1821
+ }
1822
+ if (ch === "\\" && (inSingle || inDouble || inTemplate)) {
1823
+ escaped = true;
1824
+ continue;
1825
+ }
1826
+ if (!inDouble && !inTemplate && ch === "'") {
1827
+ inSingle = !inSingle;
1828
+ continue;
1829
+ }
1830
+ if (!inSingle && !inTemplate && ch === '"') {
1831
+ inDouble = !inDouble;
1832
+ continue;
1833
+ }
1834
+ if (!inSingle && !inDouble && ch === "`") {
1835
+ inTemplate = !inTemplate;
1836
+ continue;
1837
+ }
1838
+ if (inSingle || inDouble || inTemplate)
1839
+ continue;
1840
+ if (ch === "{") {
1841
+ depth++;
1842
+ continue;
1843
+ }
1844
+ if (ch === "}") {
1845
+ depth--;
1846
+ if (depth === 0) {
1847
+ return content.slice(firstBrace, i + 1);
1848
+ }
1849
+ }
1850
+ }
1851
+ return null;
1852
+ };
1853
+ const makeFlagTrue = (flag) => new RegExp(`\\b${flag}\\s*:\\s*true\\b`);
1854
+ const makeIdField = (idField) => new RegExp(`\\b${idField}\\s*:`);
1855
+ const makeIdEmpty = (idField) => new RegExp(`\\b${idField}\\s*:\\s*(?:''|\"\"|null|undefined)\\b`);
1856
+ const rules = [
1857
+ { flag: "syndicatedToEbay", id: "ebayListingId" },
1858
+ { flag: "syndicatedToAmazon", id: "amazonListingId" },
1859
+ { flag: "syndicatedToTcgplayer", id: "tcgPlayerListingId" },
1860
+ { flag: "syndicatedToPreplo", id: "preploListingId" },
1861
+ ];
1862
+ for (const file of files) {
1863
+ const content = fs.readFileSync(file, "utf8");
1864
+ if (!/coreListings\./.test(content))
1865
+ continue;
1866
+ if (!/syndicatedTo(Ebay|Amazon|Tcgplayer|Preplo)|ListingId/.test(content))
1867
+ continue;
1868
+ writeCallPattern.lastIndex = 0;
1869
+ let match;
1870
+ while ((match = writeCallPattern.exec(content)) !== null) {
1871
+ const window = extractCallObjectLiteral(content, match.index);
1872
+ if (!window)
1873
+ continue;
1874
+ for (const rule of rules) {
1875
+ const flagTrue = makeFlagTrue(rule.flag);
1876
+ if (!flagTrue.test(window))
1877
+ continue;
1878
+ const hasId = makeIdField(rule.id).test(window);
1879
+ const idEmpty = makeIdEmpty(rule.id).test(window);
1880
+ if (!hasId || idEmpty) {
1881
+ issues.push({
1882
+ file,
1883
+ line: 1,
1884
+ type: "syndication-missing-platform-id",
1885
+ severity: "error",
1886
+ message: `Invalid payload: ${rule.flag} is true but ${rule.id} is missing/empty`,
1887
+ suggestion: `Provide ${rule.id} when setting ${rule.flag}: true (or set ${rule.flag}: false).`,
1888
+ snippet: window.replace(/\s+/g, " ").trim().substring(0, 180),
1889
+ });
1890
+ }
1891
+ }
1892
+ }
1893
+ }
1894
+ const hasErrors = issues.some((i) => i.severity === "error");
1895
+ return {
1896
+ name: "Syndication Consistency",
1897
+ passed: !hasErrors,
1898
+ blocking: true,
1899
+ issues,
1900
+ duration: Date.now() - startTime,
1901
+ };
1902
+ }
1903
+ /**
1904
+ * Check 16: Featured consistency (BLOCKING)
1905
+ *
1906
+ * Invariants (low false positives):
1907
+ * - If featuredUntil is set (non-null), featured must be true.
1908
+ * - If featured is explicitly false, featuredUntil must not be populated.
1909
+ */
1910
+ async checkFeaturedConsistency() {
1911
+ const startTime = Date.now();
1912
+ const issues = [];
1913
+ const files = await getAppLibScriptsFiles();
1914
+ const writeCallPattern = /coreListings\.(?:create|createMany|upsert|update|updateMany)\s*\(/g;
1915
+ const extractCallObjectLiteral = (content, callStartIndex) => {
1916
+ const firstBrace = content.indexOf("{", callStartIndex);
1917
+ if (firstBrace === -1)
1918
+ return null;
1919
+ let depth = 0;
1920
+ let inSingle = false;
1921
+ let inDouble = false;
1922
+ let inTemplate = false;
1923
+ let inLineComment = false;
1924
+ let inBlockComment = false;
1925
+ let escaped = false;
1926
+ for (let i = firstBrace; i < content.length; i++) {
1927
+ const ch = content[i];
1928
+ const next = content[i + 1];
1929
+ if (inLineComment) {
1930
+ if (ch === "\n")
1931
+ inLineComment = false;
1932
+ continue;
1933
+ }
1934
+ if (inBlockComment) {
1935
+ if (ch === "*" && next === "/") {
1936
+ inBlockComment = false;
1937
+ i++;
1938
+ }
1939
+ continue;
1940
+ }
1941
+ if (!inSingle && !inDouble && !inTemplate) {
1942
+ if (ch === "/" && next === "/") {
1943
+ inLineComment = true;
1944
+ i++;
1945
+ continue;
1946
+ }
1947
+ if (ch === "/" && next === "*") {
1948
+ inBlockComment = true;
1949
+ i++;
1950
+ continue;
1951
+ }
1952
+ }
1953
+ if (escaped) {
1954
+ escaped = false;
1955
+ continue;
1956
+ }
1957
+ if (ch === "\\" && (inSingle || inDouble || inTemplate)) {
1958
+ escaped = true;
1959
+ continue;
1960
+ }
1961
+ if (!inDouble && !inTemplate && ch === "'") {
1962
+ inSingle = !inSingle;
1963
+ continue;
1964
+ }
1965
+ if (!inSingle && !inTemplate && ch === '"') {
1966
+ inDouble = !inDouble;
1967
+ continue;
1968
+ }
1969
+ if (!inSingle && !inDouble && ch === "`") {
1970
+ inTemplate = !inTemplate;
1971
+ continue;
1972
+ }
1973
+ if (inSingle || inDouble || inTemplate)
1974
+ continue;
1975
+ if (ch === "{") {
1976
+ depth++;
1977
+ continue;
1978
+ }
1979
+ if (ch === "}") {
1980
+ depth--;
1981
+ if (depth === 0) {
1982
+ return content.slice(firstBrace, i + 1);
1983
+ }
1984
+ }
1985
+ }
1986
+ return null;
1987
+ };
1988
+ const featuredTrue = /\bfeatured\s*:\s*true\b/;
1989
+ const featuredFalse = /\bfeatured\s*:\s*false\b/;
1990
+ const featuredUntilNonNull = /\bfeaturedUntil\s*:\s*(?!null\b|undefined\b)/;
1991
+ for (const file of files) {
1992
+ const content = fs.readFileSync(file, "utf8");
1993
+ if (!/coreListings\./.test(content))
1994
+ continue;
1995
+ if (!/\bfeatured\b|\bfeaturedUntil\b/.test(content))
1996
+ continue;
1997
+ writeCallPattern.lastIndex = 0;
1998
+ let match;
1999
+ while ((match = writeCallPattern.exec(content)) !== null) {
2000
+ const window = extractCallObjectLiteral(content, match.index);
2001
+ if (!window)
2002
+ continue;
2003
+ if (featuredUntilNonNull.test(window) && !featuredTrue.test(window)) {
2004
+ issues.push({
2005
+ file,
2006
+ line: 1,
2007
+ type: "featured-until-without-featured",
2008
+ severity: "error",
2009
+ message: "Invalid payload: featuredUntil is set but featured is not true",
2010
+ suggestion: "Set featured: true when setting featuredUntil (or clear featuredUntil).",
2011
+ snippet: window.replace(/\s+/g, " ").trim().substring(0, 180),
2012
+ });
2013
+ }
2014
+ if (featuredFalse.test(window) && featuredUntilNonNull.test(window)) {
2015
+ issues.push({
2016
+ file,
2017
+ line: 1,
2018
+ type: "featured-false-with-featureduntil",
2019
+ severity: "error",
2020
+ message: "Invalid payload: featured is false but featuredUntil is populated",
2021
+ suggestion: "Clear featuredUntil when featured is false (or set featured: true).",
2022
+ snippet: window.replace(/\s+/g, " ").trim().substring(0, 180),
2023
+ });
2024
+ }
2025
+ }
2026
+ }
2027
+ const hasErrors = issues.some((i) => i.severity === "error");
2028
+ return {
2029
+ name: "Featured Consistency",
2030
+ passed: !hasErrors,
2031
+ blocking: true,
2032
+ issues,
2033
+ duration: Date.now() - startTime,
2034
+ };
2035
+ }
2036
+ /**
2037
+ * Check 17: Flagging consistency (BLOCKING)
2038
+ *
2039
+ * Invariants (low false positives):
2040
+ * - If isFlagged is explicitly false, flaggedAt/flaggedReason/flaggedBy must not be populated.
2041
+ * - If any flagged metadata is populated, isFlagged must be true.
2042
+ */
2043
+ async checkFlaggingConsistency() {
2044
+ const startTime = Date.now();
2045
+ const issues = [];
2046
+ const files = await getAppLibScriptsFiles();
2047
+ const writeCallPattern = /coreListings\.(?:create|createMany|upsert|update|updateMany)\s*\(/g;
2048
+ const extractCallObjectLiteral = (content, callStartIndex) => {
2049
+ const firstBrace = content.indexOf("{", callStartIndex);
2050
+ if (firstBrace === -1)
2051
+ return null;
2052
+ let depth = 0;
2053
+ let inSingle = false;
2054
+ let inDouble = false;
2055
+ let inTemplate = false;
2056
+ let inLineComment = false;
2057
+ let inBlockComment = false;
2058
+ let escaped = false;
2059
+ for (let i = firstBrace; i < content.length; i++) {
2060
+ const ch = content[i];
2061
+ const next = content[i + 1];
2062
+ if (inLineComment) {
2063
+ if (ch === "\n")
2064
+ inLineComment = false;
2065
+ continue;
2066
+ }
2067
+ if (inBlockComment) {
2068
+ if (ch === "*" && next === "/") {
2069
+ inBlockComment = false;
2070
+ i++;
2071
+ }
2072
+ continue;
2073
+ }
2074
+ if (!inSingle && !inDouble && !inTemplate) {
2075
+ if (ch === "/" && next === "/") {
2076
+ inLineComment = true;
2077
+ i++;
2078
+ continue;
2079
+ }
2080
+ if (ch === "/" && next === "*") {
2081
+ inBlockComment = true;
2082
+ i++;
2083
+ continue;
2084
+ }
2085
+ }
2086
+ if (escaped) {
2087
+ escaped = false;
2088
+ continue;
2089
+ }
2090
+ if (ch === "\\" && (inSingle || inDouble || inTemplate)) {
2091
+ escaped = true;
2092
+ continue;
2093
+ }
2094
+ if (!inDouble && !inTemplate && ch === "'") {
2095
+ inSingle = !inSingle;
2096
+ continue;
2097
+ }
2098
+ if (!inSingle && !inTemplate && ch === '"') {
2099
+ inDouble = !inDouble;
2100
+ continue;
2101
+ }
2102
+ if (!inSingle && !inDouble && ch === "`") {
2103
+ inTemplate = !inTemplate;
2104
+ continue;
2105
+ }
2106
+ if (inSingle || inDouble || inTemplate)
2107
+ continue;
2108
+ if (ch === "{") {
2109
+ depth++;
2110
+ continue;
2111
+ }
2112
+ if (ch === "}") {
2113
+ depth--;
2114
+ if (depth === 0) {
2115
+ return content.slice(firstBrace, i + 1);
2116
+ }
2117
+ }
2118
+ }
2119
+ return null;
2120
+ };
2121
+ const flaggedTrue = /\bisFlagged\s*:\s*true\b/;
2122
+ const flaggedFalse = /\bisFlagged\s*:\s*false\b/;
2123
+ const flaggedReasonNonNull = /\bflaggedReason\s*:\s*(?!null\b|undefined\b|''|""\b)/;
2124
+ const flaggedAtNonNull = /\bflaggedAt\s*:\s*(?!null\b|undefined\b)/;
2125
+ const flaggedByNonNull = /\bflaggedBy\s*:\s*(?!null\b|undefined\b|''|""\b)/;
2126
+ const anyFlagMetadata = (w) => flaggedReasonNonNull.test(w) || flaggedAtNonNull.test(w) || flaggedByNonNull.test(w);
2127
+ for (const file of files) {
2128
+ const content = fs.readFileSync(file, "utf8");
2129
+ if (!/coreListings\./.test(content))
2130
+ continue;
2131
+ if (!/isFlagged|flaggedReason|flaggedAt|flaggedBy/.test(content))
2132
+ continue;
2133
+ writeCallPattern.lastIndex = 0;
2134
+ let match;
2135
+ while ((match = writeCallPattern.exec(content)) !== null) {
2136
+ const window = extractCallObjectLiteral(content, match.index);
2137
+ if (!window)
2138
+ continue;
2139
+ if (flaggedFalse.test(window) && anyFlagMetadata(window)) {
2140
+ issues.push({
2141
+ file,
2142
+ line: 1,
2143
+ type: "flag-metadata-while-unflagged",
2144
+ severity: "error",
2145
+ message: "Invalid payload: isFlagged is false but flagged metadata is populated",
2146
+ suggestion: "Clear flaggedReason/flaggedAt/flaggedBy when isFlagged is false (or set isFlagged: true).",
2147
+ snippet: window.replace(/\s+/g, " ").trim().substring(0, 180),
2148
+ });
2149
+ }
2150
+ if (anyFlagMetadata(window) && !flaggedTrue.test(window)) {
2151
+ issues.push({
2152
+ file,
2153
+ line: 1,
2154
+ type: "flag-metadata-without-flag",
2155
+ severity: "error",
2156
+ message: "Invalid payload: flagged metadata is populated but isFlagged is not true",
2157
+ suggestion: "Set isFlagged: true when setting flaggedReason/flaggedAt/flaggedBy (or clear the metadata).",
2158
+ snippet: window.replace(/\s+/g, " ").trim().substring(0, 180),
2159
+ });
2160
+ }
2161
+ }
2162
+ }
2163
+ const hasErrors = issues.some((i) => i.severity === "error");
2164
+ return {
2165
+ name: "Flagging Consistency",
2166
+ passed: !hasErrors,
2167
+ blocking: true,
2168
+ issues,
2169
+ duration: Date.now() - startTime,
2170
+ };
2171
+ }
2172
+ /**
2173
+ * Check 18: Soft delete consistency (BLOCKING)
2174
+ *
2175
+ * Invariants (low false positives):
2176
+ * - If deletedBy is set (non-empty), deletedAt must be set (non-null).
2177
+ * - If deletedAt is explicitly null/undefined, deletedBy must not be populated.
2178
+ */
2179
+ async checkSoftDeleteConsistency() {
2180
+ const startTime = Date.now();
2181
+ const issues = [];
2182
+ const files = await getAppLibScriptsFiles();
2183
+ const writeCallPattern = /coreListings\.(?:create|createMany|upsert|update|updateMany)\s*\(/g;
2184
+ const extractCallObjectLiteral = (content, callStartIndex) => {
2185
+ const firstBrace = content.indexOf("{", callStartIndex);
2186
+ if (firstBrace === -1)
2187
+ return null;
2188
+ let depth = 0;
2189
+ let inSingle = false;
2190
+ let inDouble = false;
2191
+ let inTemplate = false;
2192
+ let inLineComment = false;
2193
+ let inBlockComment = false;
2194
+ let escaped = false;
2195
+ for (let i = firstBrace; i < content.length; i++) {
2196
+ const ch = content[i];
2197
+ const next = content[i + 1];
2198
+ if (inLineComment) {
2199
+ if (ch === "\n")
2200
+ inLineComment = false;
2201
+ continue;
2202
+ }
2203
+ if (inBlockComment) {
2204
+ if (ch === "*" && next === "/") {
2205
+ inBlockComment = false;
2206
+ i++;
2207
+ }
2208
+ continue;
2209
+ }
2210
+ if (!inSingle && !inDouble && !inTemplate) {
2211
+ if (ch === "/" && next === "/") {
2212
+ inLineComment = true;
2213
+ i++;
2214
+ continue;
2215
+ }
2216
+ if (ch === "/" && next === "*") {
2217
+ inBlockComment = true;
2218
+ i++;
2219
+ continue;
2220
+ }
2221
+ }
2222
+ if (escaped) {
2223
+ escaped = false;
2224
+ continue;
2225
+ }
2226
+ if (ch === "\\" && (inSingle || inDouble || inTemplate)) {
2227
+ escaped = true;
2228
+ continue;
2229
+ }
2230
+ if (!inDouble && !inTemplate && ch === "'") {
2231
+ inSingle = !inSingle;
2232
+ continue;
2233
+ }
2234
+ if (!inSingle && !inTemplate && ch === '"') {
2235
+ inDouble = !inDouble;
2236
+ continue;
2237
+ }
2238
+ if (!inSingle && !inDouble && ch === "`") {
2239
+ inTemplate = !inTemplate;
2240
+ continue;
2241
+ }
2242
+ if (inSingle || inDouble || inTemplate)
2243
+ continue;
2244
+ if (ch === "{") {
2245
+ depth++;
2246
+ continue;
2247
+ }
2248
+ if (ch === "}") {
2249
+ depth--;
2250
+ if (depth === 0) {
2251
+ return content.slice(firstBrace, i + 1);
2252
+ }
2253
+ }
2254
+ }
2255
+ return null;
2256
+ };
2257
+ const deletedByNonEmpty = /\bdeletedBy\s*:\s*(?!null\b|undefined\b|''|""\b)/;
2258
+ const deletedAtNonNull = /\bdeletedAt\s*:\s*(?!null\b|undefined\b)/;
2259
+ const deletedAtExplicitlyEmpty = /\bdeletedAt\s*:\s*(?:null|undefined)\b/;
2260
+ for (const file of files) {
2261
+ const content = fs.readFileSync(file, "utf8");
2262
+ if (!/coreListings\./.test(content))
2263
+ continue;
2264
+ if (!/deletedAt|deletedBy/.test(content))
2265
+ continue;
2266
+ writeCallPattern.lastIndex = 0;
2267
+ let match;
2268
+ while ((match = writeCallPattern.exec(content)) !== null) {
2269
+ const window = extractCallObjectLiteral(content, match.index);
2270
+ if (!window)
2271
+ continue;
2272
+ if (deletedByNonEmpty.test(window) && !deletedAtNonNull.test(window)) {
2273
+ issues.push({
2274
+ file,
2275
+ line: 1,
2276
+ type: "deletedby-without-deletedat",
2277
+ severity: "error",
2278
+ message: "Invalid payload: deletedBy is set but deletedAt is not set",
2279
+ suggestion: "Set deletedAt when setting deletedBy (or clear deletedBy).",
2280
+ snippet: window.replace(/\s+/g, " ").trim().substring(0, 180),
2281
+ });
2282
+ }
2283
+ if (deletedAtExplicitlyEmpty.test(window) && deletedByNonEmpty.test(window)) {
2284
+ issues.push({
2285
+ file,
2286
+ line: 1,
2287
+ type: "deletedat-empty-with-deletedby",
2288
+ severity: "error",
2289
+ message: "Invalid payload: deletedAt is null/undefined but deletedBy is populated",
2290
+ suggestion: "Clear deletedBy when deletedAt is null/undefined (or set deletedAt to a timestamp).",
2291
+ snippet: window.replace(/\s+/g, " ").trim().substring(0, 180),
2292
+ });
2293
+ }
2294
+ }
2295
+ }
2296
+ const hasErrors = issues.some((i) => i.severity === "error");
2297
+ return {
2298
+ name: "Soft Delete Consistency",
2299
+ passed: !hasErrors,
2300
+ blocking: true,
2301
+ issues,
2302
+ duration: Date.now() - startTime,
2303
+ };
2304
+ }
2305
+ /**
2306
+ * Check 19: Seller approval consistency (BLOCKING)
2307
+ *
2308
+ * Invariants (low false positives):
2309
+ * - If sellerApproved is true, sellerApprovedAt must be set (non-null).
2310
+ * - If sellerApprovedAt is set, sellerApproved must be true.
2311
+ * - If sellerApproved is explicitly false, sellerApprovedAt must not be populated.
2312
+ */
2313
+ async checkSellerApprovalConsistency() {
2314
+ const startTime = Date.now();
2315
+ const issues = [];
2316
+ const files = await getAppLibScriptsFiles();
2317
+ const writeCallPattern = /coreListings\.(?:create|createMany|upsert|update|updateMany)\s*\(/g;
2318
+ const extractCallObjectLiteral = (content, callStartIndex) => {
2319
+ const firstBrace = content.indexOf("{", callStartIndex);
2320
+ if (firstBrace === -1)
2321
+ return null;
2322
+ let depth = 0;
2323
+ let inSingle = false;
2324
+ let inDouble = false;
2325
+ let inTemplate = false;
2326
+ let inLineComment = false;
2327
+ let inBlockComment = false;
2328
+ let escaped = false;
2329
+ for (let i = firstBrace; i < content.length; i++) {
2330
+ const ch = content[i];
2331
+ const next = content[i + 1];
2332
+ if (inLineComment) {
2333
+ if (ch === "\n")
2334
+ inLineComment = false;
2335
+ continue;
2336
+ }
2337
+ if (inBlockComment) {
2338
+ if (ch === "*" && next === "/") {
2339
+ inBlockComment = false;
2340
+ i++;
2341
+ }
2342
+ continue;
2343
+ }
2344
+ if (!inSingle && !inDouble && !inTemplate) {
2345
+ if (ch === "/" && next === "/") {
2346
+ inLineComment = true;
2347
+ i++;
2348
+ continue;
2349
+ }
2350
+ if (ch === "/" && next === "*") {
2351
+ inBlockComment = true;
2352
+ i++;
2353
+ continue;
2354
+ }
2355
+ }
2356
+ if (escaped) {
2357
+ escaped = false;
2358
+ continue;
2359
+ }
2360
+ if (ch === "\\" && (inSingle || inDouble || inTemplate)) {
2361
+ escaped = true;
2362
+ continue;
2363
+ }
2364
+ if (!inDouble && !inTemplate && ch === "'") {
2365
+ inSingle = !inSingle;
2366
+ continue;
2367
+ }
2368
+ if (!inSingle && !inTemplate && ch === '"') {
2369
+ inDouble = !inDouble;
2370
+ continue;
2371
+ }
2372
+ if (!inSingle && !inDouble && ch === "`") {
2373
+ inTemplate = !inTemplate;
2374
+ continue;
2375
+ }
2376
+ if (inSingle || inDouble || inTemplate)
2377
+ continue;
2378
+ if (ch === "{") {
2379
+ depth++;
2380
+ continue;
2381
+ }
2382
+ if (ch === "}") {
2383
+ depth--;
2384
+ if (depth === 0) {
2385
+ return content.slice(firstBrace, i + 1);
2386
+ }
2387
+ }
2388
+ }
2389
+ return null;
2390
+ };
2391
+ const approvedTrue = /\bsellerApproved\s*:\s*true\b/;
2392
+ const approvedFalse = /\bsellerApproved\s*:\s*false\b/;
2393
+ const approvedAtNonNull = /\bsellerApprovedAt\s*:\s*(?!null\b|undefined\b)/;
2394
+ for (const file of files) {
2395
+ const content = fs.readFileSync(file, "utf8");
2396
+ if (!/coreListings\./.test(content))
2397
+ continue;
2398
+ if (!/sellerApproved|sellerApprovedAt/.test(content))
2399
+ continue;
2400
+ writeCallPattern.lastIndex = 0;
2401
+ let match;
2402
+ while ((match = writeCallPattern.exec(content)) !== null) {
2403
+ const window = extractCallObjectLiteral(content, match.index);
2404
+ if (!window)
2405
+ continue;
2406
+ if (approvedTrue.test(window) && !approvedAtNonNull.test(window)) {
2407
+ issues.push({
2408
+ file,
2409
+ line: 1,
2410
+ type: "seller-approved-missing-approvedat",
2411
+ severity: "error",
2412
+ message: "Invalid payload: sellerApproved is true but sellerApprovedAt is not set",
2413
+ suggestion: "Set sellerApprovedAt when setting sellerApproved: true (or set sellerApproved: false).",
2414
+ snippet: window.replace(/\s+/g, " ").trim().substring(0, 180),
2415
+ });
2416
+ }
2417
+ if (approvedAtNonNull.test(window) && !approvedTrue.test(window)) {
2418
+ issues.push({
2419
+ file,
2420
+ line: 1,
2421
+ type: "seller-approvedat-without-approved",
2422
+ severity: "error",
2423
+ message: "Invalid payload: sellerApprovedAt is set but sellerApproved is not true",
2424
+ suggestion: "Set sellerApproved: true when setting sellerApprovedAt (or clear sellerApprovedAt).",
2425
+ snippet: window.replace(/\s+/g, " ").trim().substring(0, 180),
2426
+ });
2427
+ }
2428
+ if (approvedFalse.test(window) && approvedAtNonNull.test(window)) {
2429
+ issues.push({
2430
+ file,
2431
+ line: 1,
2432
+ type: "seller-approved-false-with-approvedat",
2433
+ severity: "error",
2434
+ message: "Invalid payload: sellerApproved is false but sellerApprovedAt is populated",
2435
+ suggestion: "Clear sellerApprovedAt when sellerApproved is false (or set sellerApproved: true).",
2436
+ snippet: window.replace(/\s+/g, " ").trim().substring(0, 180),
2437
+ });
2438
+ }
2439
+ }
2440
+ }
2441
+ const hasErrors = issues.some((i) => i.severity === "error");
2442
+ return {
2443
+ name: "Seller Approval Consistency",
2444
+ passed: !hasErrors,
2445
+ blocking: true,
2446
+ issues,
2447
+ duration: Date.now() - startTime,
2448
+ };
2449
+ }
2450
+ /**
2451
+ * Check 20: AI recognition metadata consistency (BLOCKING)
2452
+ *
2453
+ * Invariants (low false positives):
2454
+ * - If recognitionConfidence is set, recognitionMethod must be set (non-empty).
2455
+ * - If recognitionJobId is set, recognitionMethod must be set (non-empty).
2456
+ */
2457
+ async checkRecognitionMetadataConsistency() {
2458
+ const startTime = Date.now();
2459
+ const issues = [];
2460
+ const files = await getAppLibScriptsFiles();
2461
+ const writeCallPattern = /coreListings\.(?:create|createMany|upsert|update|updateMany)\s*\(/g;
2462
+ const extractCallObjectLiteral = (content, callStartIndex) => {
2463
+ const firstBrace = content.indexOf("{", callStartIndex);
2464
+ if (firstBrace === -1)
2465
+ return null;
2466
+ let depth = 0;
2467
+ let inSingle = false;
2468
+ let inDouble = false;
2469
+ let inTemplate = false;
2470
+ let inLineComment = false;
2471
+ let inBlockComment = false;
2472
+ let escaped = false;
2473
+ for (let i = firstBrace; i < content.length; i++) {
2474
+ const ch = content[i];
2475
+ const next = content[i + 1];
2476
+ if (inLineComment) {
2477
+ if (ch === "\n")
2478
+ inLineComment = false;
2479
+ continue;
2480
+ }
2481
+ if (inBlockComment) {
2482
+ if (ch === "*" && next === "/") {
2483
+ inBlockComment = false;
2484
+ i++;
2485
+ }
2486
+ continue;
2487
+ }
2488
+ if (!inSingle && !inDouble && !inTemplate) {
2489
+ if (ch === "/" && next === "/") {
2490
+ inLineComment = true;
2491
+ i++;
2492
+ continue;
2493
+ }
2494
+ if (ch === "/" && next === "*") {
2495
+ inBlockComment = true;
2496
+ i++;
2497
+ continue;
2498
+ }
2499
+ }
2500
+ if (escaped) {
2501
+ escaped = false;
2502
+ continue;
2503
+ }
2504
+ if (ch === "\\" && (inSingle || inDouble || inTemplate)) {
2505
+ escaped = true;
2506
+ continue;
2507
+ }
2508
+ if (!inDouble && !inTemplate && ch === "'") {
2509
+ inSingle = !inSingle;
2510
+ continue;
2511
+ }
2512
+ if (!inSingle && !inTemplate && ch === '"') {
2513
+ inDouble = !inDouble;
2514
+ continue;
2515
+ }
2516
+ if (!inSingle && !inDouble && ch === "`") {
2517
+ inTemplate = !inTemplate;
2518
+ continue;
2519
+ }
2520
+ if (inSingle || inDouble || inTemplate)
2521
+ continue;
2522
+ if (ch === "{") {
2523
+ depth++;
2524
+ continue;
2525
+ }
2526
+ if (ch === "}") {
2527
+ depth--;
2528
+ if (depth === 0) {
2529
+ return content.slice(firstBrace, i + 1);
2530
+ }
2531
+ }
2532
+ }
2533
+ return null;
2534
+ };
2535
+ const confidenceNonNull = /\brecognitionConfidence\s*:\s*(?!null\b|undefined\b)/;
2536
+ const jobIdNonEmpty = /\brecognitionJobId\s*:\s*(?!null\b|undefined\b|''|""\b)/;
2537
+ const methodNonEmpty = /\brecognitionMethod\s*:\s*(?!null\b|undefined\b|''|""\b)/;
2538
+ for (const file of files) {
2539
+ const content = fs.readFileSync(file, "utf8");
2540
+ if (!/coreListings\./.test(content))
2541
+ continue;
2542
+ if (!/recognitionConfidence|recognitionJobId|recognitionMethod/.test(content))
2543
+ continue;
2544
+ writeCallPattern.lastIndex = 0;
2545
+ let match;
2546
+ while ((match = writeCallPattern.exec(content)) !== null) {
2547
+ const window = extractCallObjectLiteral(content, match.index);
2548
+ if (!window)
2549
+ continue;
2550
+ const needsMethod = confidenceNonNull.test(window) || jobIdNonEmpty.test(window);
2551
+ if (needsMethod && !methodNonEmpty.test(window)) {
2552
+ issues.push({
2553
+ file,
2554
+ line: 1,
2555
+ type: "recognition-metadata-missing-method",
2556
+ severity: "error",
2557
+ message: "Invalid payload: recognitionConfidence/recognitionJobId is set but recognitionMethod is missing/empty",
2558
+ suggestion: "Set recognitionMethod when setting recognitionConfidence/recognitionJobId (or clear those fields).",
2559
+ snippet: window.replace(/\s+/g, " ").trim().substring(0, 180),
2560
+ });
2561
+ }
2562
+ }
2563
+ }
2564
+ const hasErrors = issues.some((i) => i.severity === "error");
2565
+ return {
2566
+ name: "Recognition Metadata Consistency",
2567
+ passed: !hasErrors,
2568
+ blocking: true,
2569
+ issues,
2570
+ duration: Date.now() - startTime,
2571
+ };
2572
+ }
2573
+ /**
2574
+ * Check 21: Autograph grade-field consistency (BLOCKING)
2575
+ *
2576
+ * IMPORTANT: This is ONLY about autograph grading systems.
2577
+ * It is VALID to have a card grade + autograph grade at the same time:
2578
+ * - Example: psaGrade (card) + autographPsaGrade (PSA/DNA autograph) ${emoji.success}
2579
+ * - Example: bgsGrade (card) + autographBgsGrade (BGS autograph) ${emoji.success}
2580
+ *
2581
+ * Invariant:
2582
+ * - At most one of autographPsaGrade/autographBgsGrade/autographJsaGrade/autographSgcGrade
2583
+ * may be populated in a single coreListings write payload.
2584
+ */
2585
+ async checkAutographGradeFieldConsistency() {
2586
+ const startTime = Date.now();
2587
+ const issues = [];
2588
+ const files = await getAppLibScriptsFiles();
2589
+ const writeCallPattern = /coreListings\.(?:create|createMany|upsert|update|updateMany)\s*\(/g;
2590
+ const extractCallObjectLiteral = (content, callStartIndex) => {
2591
+ const firstBrace = content.indexOf("{", callStartIndex);
2592
+ if (firstBrace === -1)
2593
+ return null;
2594
+ let depth = 0;
2595
+ let inSingle = false;
2596
+ let inDouble = false;
2597
+ let inTemplate = false;
2598
+ let inLineComment = false;
2599
+ let inBlockComment = false;
2600
+ let escaped = false;
2601
+ for (let i = firstBrace; i < content.length; i++) {
2602
+ const ch = content[i];
2603
+ const next = content[i + 1];
2604
+ if (inLineComment) {
2605
+ if (ch === "\n")
2606
+ inLineComment = false;
2607
+ continue;
2608
+ }
2609
+ if (inBlockComment) {
2610
+ if (ch === "*" && next === "/") {
2611
+ inBlockComment = false;
2612
+ i++;
2613
+ }
2614
+ continue;
2615
+ }
2616
+ if (!inSingle && !inDouble && !inTemplate) {
2617
+ if (ch === "/" && next === "/") {
2618
+ inLineComment = true;
2619
+ i++;
2620
+ continue;
2621
+ }
2622
+ if (ch === "/" && next === "*") {
2623
+ inBlockComment = true;
2624
+ i++;
2625
+ continue;
2626
+ }
2627
+ }
2628
+ if (escaped) {
2629
+ escaped = false;
2630
+ continue;
2631
+ }
2632
+ if (ch === "\\" && (inSingle || inDouble || inTemplate)) {
2633
+ escaped = true;
2634
+ continue;
2635
+ }
2636
+ if (!inDouble && !inTemplate && ch === "'") {
2637
+ inSingle = !inSingle;
2638
+ continue;
2639
+ }
2640
+ if (!inSingle && !inTemplate && ch === '"') {
2641
+ inDouble = !inDouble;
2642
+ continue;
2643
+ }
2644
+ if (!inSingle && !inDouble && ch === "`") {
2645
+ inTemplate = !inTemplate;
2646
+ continue;
2647
+ }
2648
+ if (inSingle || inDouble || inTemplate)
2649
+ continue;
2650
+ if (ch === "{") {
2651
+ depth++;
2652
+ continue;
2653
+ }
2654
+ if (ch === "}") {
2655
+ depth--;
2656
+ if (depth === 0) {
2657
+ return content.slice(firstBrace, i + 1);
2658
+ }
2659
+ }
2660
+ }
2661
+ return null;
2662
+ };
2663
+ const hasNonNullAutographPsaGrade = /\bautographPsaGrade\s*:\s*(?!null\b|undefined\b)/;
2664
+ const hasNonNullAutographBgsGrade = /\bautographBgsGrade\s*:\s*(?!null\b|undefined\b)/;
2665
+ const hasNonNullAutographJsaGrade = /\bautographJsaGrade\s*:\s*(?!null\b|undefined\b)/;
2666
+ const hasNonNullAutographSgcGrade = /\bautographSgcGrade\s*:\s*(?!null\b|undefined\b)/;
2667
+ const countAutographGradeSystems = (w) => {
2668
+ let count = 0;
2669
+ if (hasNonNullAutographPsaGrade.test(w))
2670
+ count++;
2671
+ if (hasNonNullAutographBgsGrade.test(w))
2672
+ count++;
2673
+ if (hasNonNullAutographJsaGrade.test(w))
2674
+ count++;
2675
+ if (hasNonNullAutographSgcGrade.test(w))
2676
+ count++;
2677
+ return count;
2678
+ };
2679
+ for (const file of files) {
2680
+ const content = fs.readFileSync(file, "utf8");
2681
+ if (!/coreListings\./.test(content))
2682
+ continue;
2683
+ if (!/autographPsaGrade|autographBgsGrade|autographJsaGrade|autographSgcGrade/.test(content))
2684
+ continue;
2685
+ writeCallPattern.lastIndex = 0;
2686
+ let match;
2687
+ while ((match = writeCallPattern.exec(content)) !== null) {
2688
+ const window = extractCallObjectLiteral(content, match.index);
2689
+ if (!window)
2690
+ continue;
2691
+ const gradeSystemsSet = countAutographGradeSystems(window);
2692
+ if (gradeSystemsSet <= 1)
2693
+ continue;
2694
+ issues.push({
2695
+ file,
2696
+ line: 1,
2697
+ type: "autograph-grade-field-mismatch",
2698
+ severity: "error",
2699
+ message: "Invalid payload: multiple autograph grade systems are populated in one write (PSA/BGS/JSA/SGC)",
2700
+ suggestion: "Only set one of autographPsaGrade/autographBgsGrade/autographJsaGrade/autographSgcGrade per listing. Card grade fields (psaGrade/bgsGrade/cgcGrade/sgcGrade) are separate and can coexist with a single autograph grade.",
2701
+ snippet: window.replace(/\s+/g, " ").trim().substring(0, 180),
2702
+ });
2703
+ }
2704
+ }
2705
+ const hasErrors = issues.some((i) => i.severity === "error");
2706
+ return {
2707
+ name: "Autograph Grade Field Consistency",
2708
+ passed: !hasErrors,
2709
+ blocking: true,
2710
+ issues,
2711
+ duration: Date.now() - startTime,
2712
+ };
2713
+ }
2714
+ /**
2715
+ * Check 22: Autograph grading metadata consistency (BLOCKING)
2716
+ *
2717
+ * Best-practice invariants (low false positives):
2718
+ * - If an autograph numeric grade is provided (autograph*Grade), `autographGraded` must also be set.
2719
+ * - If `autographGraded` is a string literal, it must match the grading system used.
2720
+ *
2721
+ * NOTE: This does NOT require the card itself to be graded. It's valid for:
2722
+ * - graded: UNGRADED + autographPsaGrade set ${emoji.success}
2723
+ */
2724
+ async checkAutographGradingMetadataConsistency() {
2725
+ const startTime = Date.now();
2726
+ const issues = [];
2727
+ const files = await getAppLibScriptsFiles();
2728
+ const writeCallPattern = /coreListings\.(?:create|createMany|upsert|update|updateMany)\s*\(/g;
2729
+ const extractCallObjectLiteral = (content, callStartIndex) => {
2730
+ const firstBrace = content.indexOf("{", callStartIndex);
2731
+ if (firstBrace === -1)
2732
+ return null;
2733
+ let depth = 0;
2734
+ let inSingle = false;
2735
+ let inDouble = false;
2736
+ let inTemplate = false;
2737
+ let inLineComment = false;
2738
+ let inBlockComment = false;
2739
+ let escaped = false;
2740
+ for (let i = firstBrace; i < content.length; i++) {
2741
+ const ch = content[i];
2742
+ const next = content[i + 1];
2743
+ if (inLineComment) {
2744
+ if (ch === "\n")
2745
+ inLineComment = false;
2746
+ continue;
2747
+ }
2748
+ if (inBlockComment) {
2749
+ if (ch === "*" && next === "/") {
2750
+ inBlockComment = false;
2751
+ i++;
2752
+ }
2753
+ continue;
2754
+ }
2755
+ if (!inSingle && !inDouble && !inTemplate) {
2756
+ if (ch === "/" && next === "/") {
2757
+ inLineComment = true;
2758
+ i++;
2759
+ continue;
2760
+ }
2761
+ if (ch === "/" && next === "*") {
2762
+ inBlockComment = true;
2763
+ i++;
2764
+ continue;
2765
+ }
2766
+ }
2767
+ if (escaped) {
2768
+ escaped = false;
2769
+ continue;
2770
+ }
2771
+ if (ch === "\\" && (inSingle || inDouble || inTemplate)) {
2772
+ escaped = true;
2773
+ continue;
2774
+ }
2775
+ if (!inDouble && !inTemplate && ch === "'") {
2776
+ inSingle = !inSingle;
2777
+ continue;
2778
+ }
2779
+ if (!inSingle && !inTemplate && ch === '"') {
2780
+ inDouble = !inDouble;
2781
+ continue;
2782
+ }
2783
+ if (!inSingle && !inDouble && ch === "`") {
2784
+ inTemplate = !inTemplate;
2785
+ continue;
2786
+ }
2787
+ if (inSingle || inDouble || inTemplate)
2788
+ continue;
2789
+ if (ch === "{") {
2790
+ depth++;
2791
+ continue;
2792
+ }
2793
+ if (ch === "}") {
2794
+ depth--;
2795
+ if (depth === 0) {
2796
+ return content.slice(firstBrace, i + 1);
2797
+ }
2798
+ }
2799
+ }
2800
+ return null;
2801
+ };
2802
+ const hasNonNullAutographPsaGrade = /\bautographPsaGrade\s*:\s*(?!null\b|undefined\b)/;
2803
+ const hasNonNullAutographBgsGrade = /\bautographBgsGrade\s*:\s*(?!null\b|undefined\b)/;
2804
+ const hasNonNullAutographJsaGrade = /\bautographJsaGrade\s*:\s*(?!null\b|undefined\b)/;
2805
+ const hasNonNullAutographSgcGrade = /\bautographSgcGrade\s*:\s*(?!null\b|undefined\b)/;
2806
+ const anyAutographGrade = (w) => hasNonNullAutographPsaGrade.test(w) ||
2807
+ hasNonNullAutographBgsGrade.test(w) ||
2808
+ hasNonNullAutographJsaGrade.test(w) ||
2809
+ hasNonNullAutographSgcGrade.test(w);
2810
+ const autographGradedNonEmpty = /\bautographGraded\s*:\s*(?!null\b|undefined\b|''|""\b)/;
2811
+ const autographGradedLiteral = /\bautographGraded\s*:\s*['"]([^'\"]+)['"]/;
2812
+ for (const file of files) {
2813
+ const content = fs.readFileSync(file, "utf8");
2814
+ if (!/coreListings\./.test(content))
2815
+ continue;
2816
+ if (!/autographGraded|autographPsaGrade|autographBgsGrade|autographJsaGrade|autographSgcGrade/.test(content))
2817
+ continue;
2818
+ writeCallPattern.lastIndex = 0;
2819
+ let match;
2820
+ while ((match = writeCallPattern.exec(content)) !== null) {
2821
+ const window = extractCallObjectLiteral(content, match.index);
2822
+ if (!window)
2823
+ continue;
2824
+ if (!anyAutographGrade(window))
2825
+ continue;
2826
+ if (!autographGradedNonEmpty.test(window)) {
2827
+ issues.push({
2828
+ file,
2829
+ line: 1,
2830
+ type: "autograph-grade-missing-autographgraded",
2831
+ severity: "error",
2832
+ message: "Invalid payload: autograph grade is set but autographGraded is missing/empty",
2833
+ suggestion: 'Set autographGraded (e.g., "PSA/DNA", "BGS", "JSA", "SGC") when setting autographPsaGrade/autographBgsGrade/autographJsaGrade/autographSgcGrade.',
2834
+ snippet: window.replace(/\s+/g, " ").trim().substring(0, 180),
2835
+ });
2836
+ continue;
2837
+ }
2838
+ const lit = window.match(autographGradedLiteral);
2839
+ if (!lit)
2840
+ continue;
2841
+ const value = (lit[1] ?? "").toLowerCase();
2842
+ const expectsPsa = hasNonNullAutographPsaGrade.test(window);
2843
+ const expectsBgs = hasNonNullAutographBgsGrade.test(window);
2844
+ const expectsJsa = hasNonNullAutographJsaGrade.test(window);
2845
+ const expectsSgc = hasNonNullAutographSgcGrade.test(window);
2846
+ const matchesPsa = /psa|dna/.test(value);
2847
+ const matchesBgs = /bgs|beckett/.test(value);
2848
+ const matchesJsa = /jsa/.test(value);
2849
+ const matchesSgc = /sgc/.test(value);
2850
+ const mismatch = (expectsPsa && !matchesPsa) ||
2851
+ (expectsBgs && !matchesBgs) ||
2852
+ (expectsJsa && !matchesJsa) ||
2853
+ (expectsSgc && !matchesSgc);
2854
+ if (mismatch) {
2855
+ issues.push({
2856
+ file,
2857
+ line: 1,
2858
+ type: "autograph-grade-autographgraded-mismatch",
2859
+ severity: "error",
2860
+ message: "Invalid payload: autograph grade system does not match autographGraded",
2861
+ suggestion: "Align autographGraded with the autograph grade field being set (PSA/DNA ↔ autographPsaGrade, BGS ↔ autographBgsGrade, JSA ↔ autographJsaGrade, SGC ↔ autographSgcGrade).",
2862
+ snippet: window.replace(/\s+/g, " ").trim().substring(0, 180),
2863
+ });
2864
+ }
2865
+ }
2866
+ }
2867
+ const hasErrors = issues.some((i) => i.severity === "error");
2868
+ return {
2869
+ name: "Autograph Grading Metadata Consistency",
2870
+ passed: !hasErrors,
2871
+ blocking: true,
2872
+ issues,
2873
+ duration: Date.now() - startTime,
2874
+ };
2875
+ }
2876
+ /**
2877
+ * Run all hardened checks
2878
+ */
2879
+ async runAll() {
2880
+ const startTime = Date.now();
2881
+ console.log(`\n${console_chars_1.emoji.shield} HARDENED PREFLIGHT CHECKS`);
2882
+ console.log((0, console_chars_1.createDivider)(80, "heavy"));
2883
+ const checks = [];
2884
+ // BLOCKING CHECKS
2885
+ checks.push(await this.checkEslintConcurrencyEnforcement());
2886
+ checks.push(await this.checkDraftStatusEnforcement());
2887
+ checks.push(await this.checkAsyncErrorHandling());
2888
+ checks.push(await this.checkPrismaEnumLiterals());
2889
+ checks.push(await this.checkCardGradedSemantics());
2890
+ checks.push(await this.checkCardGradeFieldConsistency());
2891
+ checks.push(await this.checkSerialNumberConsistency());
2892
+ checks.push(await this.checkAutographConsistency());
2893
+ checks.push(await this.checkVideoGameGradingConsistency());
2894
+ checks.push(await this.checkSyndicationConsistency());
2895
+ checks.push(await this.checkFeaturedConsistency());
2896
+ checks.push(await this.checkFlaggingConsistency());
2897
+ checks.push(await this.checkSoftDeleteConsistency());
2898
+ checks.push(await this.checkSellerApprovalConsistency());
2899
+ checks.push(await this.checkRecognitionMetadataConsistency());
2900
+ checks.push(await this.checkAutographGradeFieldConsistency());
2901
+ checks.push(await this.checkAutographGradingMetadataConsistency());
2902
+ checks.push(await this.checkSecretDetection());
2903
+ checks.push(await this.checkReactHookRules());
2904
+ // WARNING CHECKS
2905
+ checks.push(await this.checkConsoleCleanup());
2906
+ checks.push(await this.checkFloatingPromises());
2907
+ checks.push(await this.checkCvaEnforcement());
2908
+ checks.push(await this.checkTypeSafety());
2909
+ const totalDuration = Date.now() - startTime;
2910
+ const allIssues = checks.flatMap((c) => c.issues);
2911
+ const hasBlockingFailure = checks.some((c) => !c.passed && c.blocking);
2912
+ const summary = {
2913
+ total: checks.length,
2914
+ passed: checks.filter((c) => c.passed).length,
2915
+ failed: checks.filter((c) => !c.passed).length,
2916
+ errors: allIssues.filter((i) => i.severity === "error").length,
2917
+ warnings: allIssues.filter((i) => i.severity === "warning").length,
2918
+ };
2919
+ this.printResults(checks, totalDuration, summary, hasBlockingFailure);
2920
+ return {
2921
+ module: "hardened",
2922
+ passed: !hasBlockingFailure,
2923
+ totalDuration,
2924
+ checks,
2925
+ summary,
2926
+ };
2927
+ }
2928
+ printResults(checks, totalDuration, summary, hasBlockingFailure) {
2929
+ console.log("\n" + (0, console_chars_1.createDivider)(80, "heavy"));
2930
+ console.log(`${console_chars_1.emoji.chart} HARDENED CHECK RESULTS`);
2931
+ console.log((0, console_chars_1.createDivider)(80, "heavy"));
2932
+ checks.forEach((check) => {
2933
+ const icon = check.passed
2934
+ ? `${console_chars_1.emoji.success}`
2935
+ : check.blocking
2936
+ ? `${console_chars_1.emoji.error}`
2937
+ : `${console_chars_1.emoji.warning}`;
2938
+ const count = check.issues.length > 0 ? ` (${check.issues.length} issues)` : "";
2939
+ const blocking = check.blocking ? " [BLOCKING]" : "";
2940
+ console.log(`${icon} ${check.name.padEnd(35)} ${(check.duration / 1000).toFixed(1)}s${count}${blocking}`);
2941
+ // Show first few issues
2942
+ if (check.issues.length > 0 && (this.verbose || !check.passed)) {
2943
+ const showIssues = check.issues.slice(0, this.verbose ? 10 : 3);
2944
+ showIssues.forEach((issue) => {
2945
+ const loc = issue.line ? `:${issue.line}` : "";
2946
+ console.log(` ${issue.severity === "error" ? "${emoji.error}" : "${emoji.warning}"} ${issue.file}${loc}`);
2947
+ console.log(` ${issue.message}`);
2948
+ if (issue.suggestion) {
2949
+ console.log(` ${console_chars_1.emoji.hint} ${issue.suggestion}`);
2950
+ }
2951
+ });
2952
+ if (check.issues.length > showIssues.length) {
2953
+ console.log(` ... and ${check.issues.length - showIssues.length} more`);
2954
+ }
2955
+ }
2956
+ });
2957
+ console.log((0, console_chars_1.createDivider)(80, "heavy"));
2958
+ console.log(`Total Checks: ${summary.total} | Passed: ${summary.passed} | Failed: ${summary.failed}`);
2959
+ console.log(`Errors: ${summary.errors} | Warnings: ${summary.warnings}`);
2960
+ console.log(`Time: ${(totalDuration / 1000).toFixed(1)}s`);
2961
+ console.log((0, console_chars_1.createDivider)(80, "heavy"));
2962
+ if (hasBlockingFailure) {
2963
+ console.log(`\n${console_chars_1.emoji.error} HARDENED CHECKS FAILED`);
2964
+ console.log(" Fix blocking errors before deployment\n");
2965
+ }
2966
+ else if (summary.warnings > 0) {
2967
+ console.log(`\n${console_chars_1.emoji.warning} HARDENED CHECKS PASSED (with warnings)\n`);
2968
+ }
2969
+ else {
2970
+ console.log(`\n${console_chars_1.emoji.success} HARDENED CHECKS PASSED\n`);
2971
+ }
2972
+ }
2973
+ }
2974
+ exports.HardenedPreflightModule = HardenedPreflightModule;
2975
+ // CLI
2976
+ async function main() {
2977
+ const reporter = (0, universal_progress_reporter_1.createUniversalProgressReporter)(path.basename(__filename, ".ts"));
2978
+ const args = process.argv.slice(2);
2979
+ const mode = args[0] || "all";
2980
+ const verbose = args.includes("--verbose") || args.includes("-v");
2981
+ const parallel = args.includes("--parallel") || args.includes("-p");
2982
+ const module = new HardenedPreflightModule({ verbose, parallel });
2983
+ const runSingle = async (name, fn) => {
2984
+ const result = await fn();
2985
+ const icon = result.passed
2986
+ ? `${console_chars_1.emoji.success}`
2987
+ : result.blocking
2988
+ ? `${console_chars_1.emoji.error}`
2989
+ : `${console_chars_1.emoji.warning}`;
2990
+ console.log(`\n${icon} ${result.name}: ${result.issues.length} issues`);
2991
+ if (result.issues.length > 0) {
2992
+ const showCount = verbose ? result.issues.length : 5;
2993
+ result.issues.slice(0, showCount).forEach((i) => {
2994
+ console.log(` ${i.file}${i.line ? `:${i.line}` : ""} - ${i.message}`);
2995
+ });
2996
+ if (!verbose && result.issues.length > 5) {
2997
+ console.log(` ... and ${result.issues.length - 5} more`);
2998
+ }
2999
+ }
3000
+ return {
3001
+ module: `hardened:${name}`,
3002
+ passed: result.passed || !result.blocking,
3003
+ totalDuration: result.duration,
3004
+ checks: [result],
3005
+ summary: {
3006
+ total: 1,
3007
+ passed: result.passed ? 1 : 0,
3008
+ failed: result.passed ? 0 : 1,
3009
+ errors: result.issues.filter((i) => i.severity === "error").length,
3010
+ warnings: result.issues.filter((i) => i.severity === "warning").length,
3011
+ },
3012
+ };
3013
+ };
3014
+ let result;
3015
+ switch (mode) {
3016
+ case "eslint-concurrency":
3017
+ result = await runSingle("eslint-concurrency", () => module.checkEslintConcurrencyEnforcement());
3018
+ break;
3019
+ case "draft":
3020
+ result = await runSingle("draft", () => module.checkDraftStatusEnforcement());
3021
+ break;
3022
+ case "async":
3023
+ result = await runSingle("async", () => module.checkAsyncErrorHandling());
3024
+ break;
3025
+ case "console":
3026
+ result = await runSingle("console", () => module.checkConsoleCleanup());
3027
+ break;
3028
+ case "secrets":
3029
+ result = await runSingle("secrets", () => module.checkSecretDetection());
3030
+ break;
3031
+ case "hooks":
3032
+ result = await runSingle("hooks", () => module.checkReactHookRules());
3033
+ break;
3034
+ case "enums":
3035
+ case "prisma-enums":
3036
+ result = await runSingle("prisma-enums", () => module.checkPrismaEnumLiterals());
3037
+ break;
3038
+ case "promises":
3039
+ result = await runSingle("promises", () => module.checkFloatingPromises());
3040
+ break;
3041
+ case "cva":
3042
+ result = await runSingle("cva", () => module.checkCvaEnforcement());
3043
+ break;
3044
+ case "types":
3045
+ result = await runSingle("types", () => module.checkTypeSafety());
3046
+ break;
3047
+ default:
3048
+ result = await module.runAll();
3049
+ break;
3050
+ }
3051
+ process.exit(result.passed ? 0 : 1);
3052
+ }
3053
+ if (require.main === module) {
3054
+ main();
3055
+ }
3056
+ //# sourceMappingURL=hardened-checks.js.map