driftdetect-detectors 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (509) hide show
  1. package/dist/accessibility/alt-text.d.ts +63 -0
  2. package/dist/accessibility/alt-text.d.ts.map +1 -0
  3. package/dist/accessibility/alt-text.js +100 -0
  4. package/dist/accessibility/alt-text.js.map +1 -0
  5. package/dist/accessibility/aria-roles.d.ts +65 -0
  6. package/dist/accessibility/aria-roles.d.ts.map +1 -0
  7. package/dist/accessibility/aria-roles.js +87 -0
  8. package/dist/accessibility/aria-roles.js.map +1 -0
  9. package/dist/accessibility/focus-management.d.ts +62 -0
  10. package/dist/accessibility/focus-management.d.ts.map +1 -0
  11. package/dist/accessibility/focus-management.js +88 -0
  12. package/dist/accessibility/focus-management.js.map +1 -0
  13. package/dist/accessibility/heading-hierarchy.d.ts +66 -0
  14. package/dist/accessibility/heading-hierarchy.d.ts.map +1 -0
  15. package/dist/accessibility/heading-hierarchy.js +94 -0
  16. package/dist/accessibility/heading-hierarchy.js.map +1 -0
  17. package/dist/accessibility/index.d.ts +25 -0
  18. package/dist/accessibility/index.d.ts.map +1 -0
  19. package/dist/accessibility/index.js +21 -0
  20. package/dist/accessibility/index.js.map +1 -0
  21. package/dist/accessibility/keyboard-nav.d.ts +63 -0
  22. package/dist/accessibility/keyboard-nav.d.ts.map +1 -0
  23. package/dist/accessibility/keyboard-nav.js +86 -0
  24. package/dist/accessibility/keyboard-nav.js.map +1 -0
  25. package/dist/accessibility/semantic-html.d.ts +76 -0
  26. package/dist/accessibility/semantic-html.d.ts.map +1 -0
  27. package/dist/accessibility/semantic-html.js +204 -0
  28. package/dist/accessibility/semantic-html.js.map +1 -0
  29. package/dist/api/client-patterns.d.ts +121 -0
  30. package/dist/api/client-patterns.d.ts.map +1 -0
  31. package/dist/api/client-patterns.js +478 -0
  32. package/dist/api/client-patterns.js.map +1 -0
  33. package/dist/api/error-format.d.ts +140 -0
  34. package/dist/api/error-format.d.ts.map +1 -0
  35. package/dist/api/error-format.js +614 -0
  36. package/dist/api/error-format.js.map +1 -0
  37. package/dist/api/http-methods.d.ts +255 -0
  38. package/dist/api/http-methods.d.ts.map +1 -0
  39. package/dist/api/http-methods.js +890 -0
  40. package/dist/api/http-methods.js.map +1 -0
  41. package/dist/api/index.d.ts +16 -0
  42. package/dist/api/index.d.ts.map +1 -0
  43. package/dist/api/index.js +37 -0
  44. package/dist/api/index.js.map +1 -0
  45. package/dist/api/pagination.d.ts +133 -0
  46. package/dist/api/pagination.d.ts.map +1 -0
  47. package/dist/api/pagination.js +521 -0
  48. package/dist/api/pagination.js.map +1 -0
  49. package/dist/api/response-envelope.d.ts +261 -0
  50. package/dist/api/response-envelope.d.ts.map +1 -0
  51. package/dist/api/response-envelope.js +1050 -0
  52. package/dist/api/response-envelope.js.map +1 -0
  53. package/dist/api/retry-patterns.d.ts +117 -0
  54. package/dist/api/retry-patterns.d.ts.map +1 -0
  55. package/dist/api/retry-patterns.js +480 -0
  56. package/dist/api/retry-patterns.js.map +1 -0
  57. package/dist/api/route-structure.d.ts +128 -0
  58. package/dist/api/route-structure.d.ts.map +1 -0
  59. package/dist/api/route-structure.js +738 -0
  60. package/dist/api/route-structure.js.map +1 -0
  61. package/dist/auth/audit-logging.d.ts +80 -0
  62. package/dist/auth/audit-logging.d.ts.map +1 -0
  63. package/dist/auth/audit-logging.js +370 -0
  64. package/dist/auth/audit-logging.js.map +1 -0
  65. package/dist/auth/index.d.ts +33 -0
  66. package/dist/auth/index.d.ts.map +1 -0
  67. package/dist/auth/index.js +49 -0
  68. package/dist/auth/index.js.map +1 -0
  69. package/dist/auth/middleware-usage.d.ts +65 -0
  70. package/dist/auth/middleware-usage.d.ts.map +1 -0
  71. package/dist/auth/middleware-usage.js +192 -0
  72. package/dist/auth/middleware-usage.js.map +1 -0
  73. package/dist/auth/permission-checks.d.ts +60 -0
  74. package/dist/auth/permission-checks.d.ts.map +1 -0
  75. package/dist/auth/permission-checks.js +159 -0
  76. package/dist/auth/permission-checks.js.map +1 -0
  77. package/dist/auth/rbac-patterns.d.ts +68 -0
  78. package/dist/auth/rbac-patterns.d.ts.map +1 -0
  79. package/dist/auth/rbac-patterns.js +143 -0
  80. package/dist/auth/rbac-patterns.js.map +1 -0
  81. package/dist/auth/resource-ownership.d.ts +77 -0
  82. package/dist/auth/resource-ownership.d.ts.map +1 -0
  83. package/dist/auth/resource-ownership.js +324 -0
  84. package/dist/auth/resource-ownership.js.map +1 -0
  85. package/dist/auth/token-handling.d.ts +64 -0
  86. package/dist/auth/token-handling.d.ts.map +1 -0
  87. package/dist/auth/token-handling.js +151 -0
  88. package/dist/auth/token-handling.js.map +1 -0
  89. package/dist/base/ast-detector.d.ts +421 -0
  90. package/dist/base/ast-detector.d.ts.map +1 -0
  91. package/dist/base/ast-detector.js +699 -0
  92. package/dist/base/ast-detector.js.map +1 -0
  93. package/dist/base/base-detector.d.ts +366 -0
  94. package/dist/base/base-detector.d.ts.map +1 -0
  95. package/dist/base/base-detector.js +170 -0
  96. package/dist/base/base-detector.js.map +1 -0
  97. package/dist/base/index.d.ts +12 -0
  98. package/dist/base/index.d.ts.map +1 -0
  99. package/dist/base/index.js +17 -0
  100. package/dist/base/index.js.map +1 -0
  101. package/dist/base/regex-detector.d.ts +421 -0
  102. package/dist/base/regex-detector.d.ts.map +1 -0
  103. package/dist/base/regex-detector.js +537 -0
  104. package/dist/base/regex-detector.js.map +1 -0
  105. package/dist/base/structural-detector.d.ts +424 -0
  106. package/dist/base/structural-detector.d.ts.map +1 -0
  107. package/dist/base/structural-detector.js +731 -0
  108. package/dist/base/structural-detector.js.map +1 -0
  109. package/dist/base/types.d.ts +53 -0
  110. package/dist/base/types.d.ts.map +1 -0
  111. package/dist/base/types.js +5 -0
  112. package/dist/base/types.js.map +1 -0
  113. package/dist/components/component-structure.d.ts +163 -0
  114. package/dist/components/component-structure.d.ts.map +1 -0
  115. package/dist/components/component-structure.js +500 -0
  116. package/dist/components/component-structure.js.map +1 -0
  117. package/dist/components/composition.d.ts +287 -0
  118. package/dist/components/composition.d.ts.map +1 -0
  119. package/dist/components/composition.js +1123 -0
  120. package/dist/components/composition.js.map +1 -0
  121. package/dist/components/duplicate-detection.d.ts +251 -0
  122. package/dist/components/duplicate-detection.d.ts.map +1 -0
  123. package/dist/components/duplicate-detection.js +804 -0
  124. package/dist/components/duplicate-detection.js.map +1 -0
  125. package/dist/components/index.d.ts +16 -0
  126. package/dist/components/index.d.ts.map +1 -0
  127. package/dist/components/index.js +51 -0
  128. package/dist/components/index.js.map +1 -0
  129. package/dist/components/near-duplicate.d.ts +402 -0
  130. package/dist/components/near-duplicate.d.ts.map +1 -0
  131. package/dist/components/near-duplicate.js +1090 -0
  132. package/dist/components/near-duplicate.js.map +1 -0
  133. package/dist/components/props-patterns.d.ts +194 -0
  134. package/dist/components/props-patterns.d.ts.map +1 -0
  135. package/dist/components/props-patterns.js +795 -0
  136. package/dist/components/props-patterns.js.map +1 -0
  137. package/dist/components/ref-forwarding.d.ts +250 -0
  138. package/dist/components/ref-forwarding.d.ts.map +1 -0
  139. package/dist/components/ref-forwarding.js +832 -0
  140. package/dist/components/ref-forwarding.js.map +1 -0
  141. package/dist/components/state-patterns.d.ts +291 -0
  142. package/dist/components/state-patterns.d.ts.map +1 -0
  143. package/dist/components/state-patterns.js +970 -0
  144. package/dist/components/state-patterns.js.map +1 -0
  145. package/dist/config/config-validation.d.ts +74 -0
  146. package/dist/config/config-validation.d.ts.map +1 -0
  147. package/dist/config/config-validation.js +446 -0
  148. package/dist/config/config-validation.js.map +1 -0
  149. package/dist/config/default-values.d.ts +72 -0
  150. package/dist/config/default-values.d.ts.map +1 -0
  151. package/dist/config/default-values.js +386 -0
  152. package/dist/config/default-values.js.map +1 -0
  153. package/dist/config/env-naming.d.ts +73 -0
  154. package/dist/config/env-naming.d.ts.map +1 -0
  155. package/dist/config/env-naming.js +429 -0
  156. package/dist/config/env-naming.js.map +1 -0
  157. package/dist/config/environment-detection.d.ts +72 -0
  158. package/dist/config/environment-detection.d.ts.map +1 -0
  159. package/dist/config/environment-detection.js +400 -0
  160. package/dist/config/environment-detection.js.map +1 -0
  161. package/dist/config/feature-flags.d.ts +72 -0
  162. package/dist/config/feature-flags.d.ts.map +1 -0
  163. package/dist/config/feature-flags.js +384 -0
  164. package/dist/config/feature-flags.js.map +1 -0
  165. package/dist/config/index.d.ts +27 -0
  166. package/dist/config/index.d.ts.map +1 -0
  167. package/dist/config/index.js +43 -0
  168. package/dist/config/index.js.map +1 -0
  169. package/dist/config/required-optional.d.ts +71 -0
  170. package/dist/config/required-optional.d.ts.map +1 -0
  171. package/dist/config/required-optional.js +344 -0
  172. package/dist/config/required-optional.js.map +1 -0
  173. package/dist/data-access/connection-pooling.d.ts +63 -0
  174. package/dist/data-access/connection-pooling.d.ts.map +1 -0
  175. package/dist/data-access/connection-pooling.js +297 -0
  176. package/dist/data-access/connection-pooling.js.map +1 -0
  177. package/dist/data-access/dto-patterns.d.ts +64 -0
  178. package/dist/data-access/dto-patterns.d.ts.map +1 -0
  179. package/dist/data-access/dto-patterns.js +291 -0
  180. package/dist/data-access/dto-patterns.js.map +1 -0
  181. package/dist/data-access/index.d.ts +31 -0
  182. package/dist/data-access/index.d.ts.map +1 -0
  183. package/dist/data-access/index.js +49 -0
  184. package/dist/data-access/index.js.map +1 -0
  185. package/dist/data-access/n-plus-one.d.ts +60 -0
  186. package/dist/data-access/n-plus-one.d.ts.map +1 -0
  187. package/dist/data-access/n-plus-one.js +264 -0
  188. package/dist/data-access/n-plus-one.js.map +1 -0
  189. package/dist/data-access/query-patterns.d.ts +64 -0
  190. package/dist/data-access/query-patterns.d.ts.map +1 -0
  191. package/dist/data-access/query-patterns.js +314 -0
  192. package/dist/data-access/query-patterns.js.map +1 -0
  193. package/dist/data-access/repository-pattern.d.ts +62 -0
  194. package/dist/data-access/repository-pattern.d.ts.map +1 -0
  195. package/dist/data-access/repository-pattern.js +257 -0
  196. package/dist/data-access/repository-pattern.js.map +1 -0
  197. package/dist/data-access/transaction-patterns.d.ts +61 -0
  198. package/dist/data-access/transaction-patterns.d.ts.map +1 -0
  199. package/dist/data-access/transaction-patterns.js +277 -0
  200. package/dist/data-access/transaction-patterns.js.map +1 -0
  201. package/dist/data-access/validation-patterns.d.ts +62 -0
  202. package/dist/data-access/validation-patterns.d.ts.map +1 -0
  203. package/dist/data-access/validation-patterns.js +301 -0
  204. package/dist/data-access/validation-patterns.js.map +1 -0
  205. package/dist/documentation/deprecation.d.ts +62 -0
  206. package/dist/documentation/deprecation.d.ts.map +1 -0
  207. package/dist/documentation/deprecation.js +83 -0
  208. package/dist/documentation/deprecation.js.map +1 -0
  209. package/dist/documentation/example-code.d.ts +64 -0
  210. package/dist/documentation/example-code.d.ts.map +1 -0
  211. package/dist/documentation/example-code.js +79 -0
  212. package/dist/documentation/example-code.js.map +1 -0
  213. package/dist/documentation/index.d.ts +22 -0
  214. package/dist/documentation/index.d.ts.map +1 -0
  215. package/dist/documentation/index.js +19 -0
  216. package/dist/documentation/index.js.map +1 -0
  217. package/dist/documentation/jsdoc-patterns.d.ts +72 -0
  218. package/dist/documentation/jsdoc-patterns.d.ts.map +1 -0
  219. package/dist/documentation/jsdoc-patterns.js +92 -0
  220. package/dist/documentation/jsdoc-patterns.js.map +1 -0
  221. package/dist/documentation/readme-structure.d.ts +67 -0
  222. package/dist/documentation/readme-structure.d.ts.map +1 -0
  223. package/dist/documentation/readme-structure.js +76 -0
  224. package/dist/documentation/readme-structure.js.map +1 -0
  225. package/dist/documentation/todo-patterns.d.ts +67 -0
  226. package/dist/documentation/todo-patterns.d.ts.map +1 -0
  227. package/dist/documentation/todo-patterns.js +73 -0
  228. package/dist/documentation/todo-patterns.js.map +1 -0
  229. package/dist/errors/async-errors.d.ts +72 -0
  230. package/dist/errors/async-errors.d.ts.map +1 -0
  231. package/dist/errors/async-errors.js +214 -0
  232. package/dist/errors/async-errors.js.map +1 -0
  233. package/dist/errors/circuit-breaker.d.ts +53 -0
  234. package/dist/errors/circuit-breaker.d.ts.map +1 -0
  235. package/dist/errors/circuit-breaker.js +241 -0
  236. package/dist/errors/circuit-breaker.js.map +1 -0
  237. package/dist/errors/error-codes.d.ts +73 -0
  238. package/dist/errors/error-codes.d.ts.map +1 -0
  239. package/dist/errors/error-codes.js +211 -0
  240. package/dist/errors/error-codes.js.map +1 -0
  241. package/dist/errors/error-logging.d.ts +73 -0
  242. package/dist/errors/error-logging.d.ts.map +1 -0
  243. package/dist/errors/error-logging.js +256 -0
  244. package/dist/errors/error-logging.js.map +1 -0
  245. package/dist/errors/error-propagation.d.ts +73 -0
  246. package/dist/errors/error-propagation.d.ts.map +1 -0
  247. package/dist/errors/error-propagation.js +244 -0
  248. package/dist/errors/error-propagation.js.map +1 -0
  249. package/dist/errors/exception-hierarchy.d.ts +75 -0
  250. package/dist/errors/exception-hierarchy.d.ts.map +1 -0
  251. package/dist/errors/exception-hierarchy.js +259 -0
  252. package/dist/errors/exception-hierarchy.js.map +1 -0
  253. package/dist/errors/index.d.ts +31 -0
  254. package/dist/errors/index.d.ts.map +1 -0
  255. package/dist/errors/index.js +49 -0
  256. package/dist/errors/index.js.map +1 -0
  257. package/dist/errors/try-catch-placement.d.ts +73 -0
  258. package/dist/errors/try-catch-placement.d.ts.map +1 -0
  259. package/dist/errors/try-catch-placement.js +214 -0
  260. package/dist/errors/try-catch-placement.js.map +1 -0
  261. package/dist/index.d.ts +221 -0
  262. package/dist/index.d.ts.map +1 -0
  263. package/dist/index.js +245 -0
  264. package/dist/index.js.map +1 -0
  265. package/dist/logging/context-fields.d.ts +48 -0
  266. package/dist/logging/context-fields.d.ts.map +1 -0
  267. package/dist/logging/context-fields.js +160 -0
  268. package/dist/logging/context-fields.js.map +1 -0
  269. package/dist/logging/correlation-ids.d.ts +44 -0
  270. package/dist/logging/correlation-ids.d.ts.map +1 -0
  271. package/dist/logging/correlation-ids.js +144 -0
  272. package/dist/logging/correlation-ids.js.map +1 -0
  273. package/dist/logging/health-checks.d.ts +45 -0
  274. package/dist/logging/health-checks.d.ts.map +1 -0
  275. package/dist/logging/health-checks.js +165 -0
  276. package/dist/logging/health-checks.js.map +1 -0
  277. package/dist/logging/index.d.ts +31 -0
  278. package/dist/logging/index.d.ts.map +1 -0
  279. package/dist/logging/index.js +49 -0
  280. package/dist/logging/index.js.map +1 -0
  281. package/dist/logging/log-levels.d.ts +46 -0
  282. package/dist/logging/log-levels.d.ts.map +1 -0
  283. package/dist/logging/log-levels.js +178 -0
  284. package/dist/logging/log-levels.js.map +1 -0
  285. package/dist/logging/metric-naming.d.ts +46 -0
  286. package/dist/logging/metric-naming.d.ts.map +1 -0
  287. package/dist/logging/metric-naming.js +157 -0
  288. package/dist/logging/metric-naming.js.map +1 -0
  289. package/dist/logging/pii-redaction.d.ts +44 -0
  290. package/dist/logging/pii-redaction.d.ts.map +1 -0
  291. package/dist/logging/pii-redaction.js +166 -0
  292. package/dist/logging/pii-redaction.js.map +1 -0
  293. package/dist/logging/structured-format.d.ts +53 -0
  294. package/dist/logging/structured-format.d.ts.map +1 -0
  295. package/dist/logging/structured-format.js +235 -0
  296. package/dist/logging/structured-format.js.map +1 -0
  297. package/dist/performance/bundle-size.d.ts +79 -0
  298. package/dist/performance/bundle-size.d.ts.map +1 -0
  299. package/dist/performance/bundle-size.js +276 -0
  300. package/dist/performance/bundle-size.js.map +1 -0
  301. package/dist/performance/caching-patterns.d.ts +78 -0
  302. package/dist/performance/caching-patterns.d.ts.map +1 -0
  303. package/dist/performance/caching-patterns.js +257 -0
  304. package/dist/performance/caching-patterns.js.map +1 -0
  305. package/dist/performance/code-splitting.d.ts +86 -0
  306. package/dist/performance/code-splitting.d.ts.map +1 -0
  307. package/dist/performance/code-splitting.js +447 -0
  308. package/dist/performance/code-splitting.js.map +1 -0
  309. package/dist/performance/debounce-throttle.d.ts +75 -0
  310. package/dist/performance/debounce-throttle.d.ts.map +1 -0
  311. package/dist/performance/debounce-throttle.js +232 -0
  312. package/dist/performance/debounce-throttle.js.map +1 -0
  313. package/dist/performance/index.d.ts +28 -0
  314. package/dist/performance/index.d.ts.map +1 -0
  315. package/dist/performance/index.js +39 -0
  316. package/dist/performance/index.js.map +1 -0
  317. package/dist/performance/lazy-loading.d.ts +75 -0
  318. package/dist/performance/lazy-loading.d.ts.map +1 -0
  319. package/dist/performance/lazy-loading.js +233 -0
  320. package/dist/performance/lazy-loading.js.map +1 -0
  321. package/dist/performance/memoization.d.ts +75 -0
  322. package/dist/performance/memoization.d.ts.map +1 -0
  323. package/dist/performance/memoization.js +251 -0
  324. package/dist/performance/memoization.js.map +1 -0
  325. package/dist/registry/detector-registry.d.ts +266 -0
  326. package/dist/registry/detector-registry.d.ts.map +1 -0
  327. package/dist/registry/detector-registry.js +526 -0
  328. package/dist/registry/detector-registry.js.map +1 -0
  329. package/dist/registry/index.d.ts +10 -0
  330. package/dist/registry/index.d.ts.map +1 -0
  331. package/dist/registry/index.js +10 -0
  332. package/dist/registry/index.js.map +1 -0
  333. package/dist/registry/loader.d.ts +232 -0
  334. package/dist/registry/loader.d.ts.map +1 -0
  335. package/dist/registry/loader.js +419 -0
  336. package/dist/registry/loader.js.map +1 -0
  337. package/dist/registry/types.d.ts +111 -0
  338. package/dist/registry/types.d.ts.map +1 -0
  339. package/dist/registry/types.js +19 -0
  340. package/dist/registry/types.js.map +1 -0
  341. package/dist/security/csp-headers.d.ts +78 -0
  342. package/dist/security/csp-headers.d.ts.map +1 -0
  343. package/dist/security/csp-headers.js +401 -0
  344. package/dist/security/csp-headers.js.map +1 -0
  345. package/dist/security/csrf-protection.d.ts +72 -0
  346. package/dist/security/csrf-protection.d.ts.map +1 -0
  347. package/dist/security/csrf-protection.js +344 -0
  348. package/dist/security/csrf-protection.js.map +1 -0
  349. package/dist/security/index.d.ts +30 -0
  350. package/dist/security/index.d.ts.map +1 -0
  351. package/dist/security/index.js +48 -0
  352. package/dist/security/index.js.map +1 -0
  353. package/dist/security/input-sanitization.d.ts +74 -0
  354. package/dist/security/input-sanitization.d.ts.map +1 -0
  355. package/dist/security/input-sanitization.js +373 -0
  356. package/dist/security/input-sanitization.js.map +1 -0
  357. package/dist/security/rate-limiting.d.ts +81 -0
  358. package/dist/security/rate-limiting.d.ts.map +1 -0
  359. package/dist/security/rate-limiting.js +535 -0
  360. package/dist/security/rate-limiting.js.map +1 -0
  361. package/dist/security/secret-management.d.ts +83 -0
  362. package/dist/security/secret-management.d.ts.map +1 -0
  363. package/dist/security/secret-management.js +547 -0
  364. package/dist/security/secret-management.js.map +1 -0
  365. package/dist/security/sql-injection.d.ts +76 -0
  366. package/dist/security/sql-injection.d.ts.map +1 -0
  367. package/dist/security/sql-injection.js +383 -0
  368. package/dist/security/sql-injection.js.map +1 -0
  369. package/dist/security/xss-prevention.d.ts +80 -0
  370. package/dist/security/xss-prevention.d.ts.map +1 -0
  371. package/dist/security/xss-prevention.js +416 -0
  372. package/dist/security/xss-prevention.js.map +1 -0
  373. package/dist/structural/barrel-exports.d.ts +178 -0
  374. package/dist/structural/barrel-exports.d.ts.map +1 -0
  375. package/dist/structural/barrel-exports.js +553 -0
  376. package/dist/structural/barrel-exports.js.map +1 -0
  377. package/dist/structural/circular-deps.d.ts +140 -0
  378. package/dist/structural/circular-deps.d.ts.map +1 -0
  379. package/dist/structural/circular-deps.js +422 -0
  380. package/dist/structural/circular-deps.js.map +1 -0
  381. package/dist/structural/co-location.d.ts +202 -0
  382. package/dist/structural/co-location.d.ts.map +1 -0
  383. package/dist/structural/co-location.js +640 -0
  384. package/dist/structural/co-location.js.map +1 -0
  385. package/dist/structural/directory-structure.d.ts +151 -0
  386. package/dist/structural/directory-structure.d.ts.map +1 -0
  387. package/dist/structural/directory-structure.js +457 -0
  388. package/dist/structural/directory-structure.js.map +1 -0
  389. package/dist/structural/file-naming.d.ts +61 -0
  390. package/dist/structural/file-naming.d.ts.map +1 -0
  391. package/dist/structural/file-naming.js +231 -0
  392. package/dist/structural/file-naming.js.map +1 -0
  393. package/dist/structural/import-ordering.d.ts +212 -0
  394. package/dist/structural/import-ordering.d.ts.map +1 -0
  395. package/dist/structural/import-ordering.js +821 -0
  396. package/dist/structural/import-ordering.js.map +1 -0
  397. package/dist/structural/index.d.ts +23 -0
  398. package/dist/structural/index.d.ts.map +1 -0
  399. package/dist/structural/index.js +26 -0
  400. package/dist/structural/index.js.map +1 -0
  401. package/dist/structural/module-boundaries.d.ts +164 -0
  402. package/dist/structural/module-boundaries.d.ts.map +1 -0
  403. package/dist/structural/module-boundaries.js +616 -0
  404. package/dist/structural/module-boundaries.js.map +1 -0
  405. package/dist/structural/package-boundaries.d.ts +182 -0
  406. package/dist/structural/package-boundaries.d.ts.map +1 -0
  407. package/dist/structural/package-boundaries.js +602 -0
  408. package/dist/structural/package-boundaries.js.map +1 -0
  409. package/dist/styling/class-naming.d.ts +263 -0
  410. package/dist/styling/class-naming.d.ts.map +1 -0
  411. package/dist/styling/class-naming.js +892 -0
  412. package/dist/styling/class-naming.js.map +1 -0
  413. package/dist/styling/color-usage.d.ts +213 -0
  414. package/dist/styling/color-usage.d.ts.map +1 -0
  415. package/dist/styling/color-usage.js +732 -0
  416. package/dist/styling/color-usage.js.map +1 -0
  417. package/dist/styling/design-tokens.d.ts +212 -0
  418. package/dist/styling/design-tokens.d.ts.map +1 -0
  419. package/dist/styling/design-tokens.js +748 -0
  420. package/dist/styling/design-tokens.js.map +1 -0
  421. package/dist/styling/index.d.ts +16 -0
  422. package/dist/styling/index.d.ts.map +1 -0
  423. package/dist/styling/index.js +56 -0
  424. package/dist/styling/index.js.map +1 -0
  425. package/dist/styling/responsive.d.ts +304 -0
  426. package/dist/styling/responsive.d.ts.map +1 -0
  427. package/dist/styling/responsive.js +888 -0
  428. package/dist/styling/responsive.js.map +1 -0
  429. package/dist/styling/spacing-scale.d.ts +248 -0
  430. package/dist/styling/spacing-scale.d.ts.map +1 -0
  431. package/dist/styling/spacing-scale.js +865 -0
  432. package/dist/styling/spacing-scale.js.map +1 -0
  433. package/dist/styling/tailwind-patterns.d.ts +305 -0
  434. package/dist/styling/tailwind-patterns.d.ts.map +1 -0
  435. package/dist/styling/tailwind-patterns.js +1181 -0
  436. package/dist/styling/tailwind-patterns.js.map +1 -0
  437. package/dist/styling/typography.d.ts +281 -0
  438. package/dist/styling/typography.d.ts.map +1 -0
  439. package/dist/styling/typography.js +1004 -0
  440. package/dist/styling/typography.js.map +1 -0
  441. package/dist/styling/z-index-scale.d.ts +270 -0
  442. package/dist/styling/z-index-scale.d.ts.map +1 -0
  443. package/dist/styling/z-index-scale.js +714 -0
  444. package/dist/styling/z-index-scale.js.map +1 -0
  445. package/dist/testing/co-location.d.ts +42 -0
  446. package/dist/testing/co-location.d.ts.map +1 -0
  447. package/dist/testing/co-location.js +134 -0
  448. package/dist/testing/co-location.js.map +1 -0
  449. package/dist/testing/describe-naming.d.ts +47 -0
  450. package/dist/testing/describe-naming.d.ts.map +1 -0
  451. package/dist/testing/describe-naming.js +150 -0
  452. package/dist/testing/describe-naming.js.map +1 -0
  453. package/dist/testing/file-naming.d.ts +44 -0
  454. package/dist/testing/file-naming.d.ts.map +1 -0
  455. package/dist/testing/file-naming.js +131 -0
  456. package/dist/testing/file-naming.js.map +1 -0
  457. package/dist/testing/fixture-patterns.d.ts +52 -0
  458. package/dist/testing/fixture-patterns.d.ts.map +1 -0
  459. package/dist/testing/fixture-patterns.js +228 -0
  460. package/dist/testing/fixture-patterns.js.map +1 -0
  461. package/dist/testing/index.d.ts +31 -0
  462. package/dist/testing/index.d.ts.map +1 -0
  463. package/dist/testing/index.js +49 -0
  464. package/dist/testing/index.js.map +1 -0
  465. package/dist/testing/mock-patterns.d.ts +53 -0
  466. package/dist/testing/mock-patterns.d.ts.map +1 -0
  467. package/dist/testing/mock-patterns.js +264 -0
  468. package/dist/testing/mock-patterns.js.map +1 -0
  469. package/dist/testing/setup-teardown.d.ts +55 -0
  470. package/dist/testing/setup-teardown.d.ts.map +1 -0
  471. package/dist/testing/setup-teardown.js +262 -0
  472. package/dist/testing/setup-teardown.js.map +1 -0
  473. package/dist/testing/test-structure.d.ts +51 -0
  474. package/dist/testing/test-structure.d.ts.map +1 -0
  475. package/dist/testing/test-structure.js +225 -0
  476. package/dist/testing/test-structure.js.map +1 -0
  477. package/dist/types/any-usage.d.ts +99 -0
  478. package/dist/types/any-usage.d.ts.map +1 -0
  479. package/dist/types/any-usage.js +641 -0
  480. package/dist/types/any-usage.js.map +1 -0
  481. package/dist/types/file-location.d.ts +76 -0
  482. package/dist/types/file-location.d.ts.map +1 -0
  483. package/dist/types/file-location.js +395 -0
  484. package/dist/types/file-location.js.map +1 -0
  485. package/dist/types/generic-patterns.d.ts +97 -0
  486. package/dist/types/generic-patterns.d.ts.map +1 -0
  487. package/dist/types/generic-patterns.js +615 -0
  488. package/dist/types/generic-patterns.js.map +1 -0
  489. package/dist/types/index.d.ts +31 -0
  490. package/dist/types/index.d.ts.map +1 -0
  491. package/dist/types/index.js +43 -0
  492. package/dist/types/index.js.map +1 -0
  493. package/dist/types/interface-vs-type.d.ts +81 -0
  494. package/dist/types/interface-vs-type.d.ts.map +1 -0
  495. package/dist/types/interface-vs-type.js +440 -0
  496. package/dist/types/interface-vs-type.js.map +1 -0
  497. package/dist/types/naming-conventions.d.ts +84 -0
  498. package/dist/types/naming-conventions.d.ts.map +1 -0
  499. package/dist/types/naming-conventions.js +455 -0
  500. package/dist/types/naming-conventions.js.map +1 -0
  501. package/dist/types/type-assertions.d.ts +98 -0
  502. package/dist/types/type-assertions.d.ts.map +1 -0
  503. package/dist/types/type-assertions.js +639 -0
  504. package/dist/types/type-assertions.js.map +1 -0
  505. package/dist/types/utility-types.d.ts +110 -0
  506. package/dist/types/utility-types.d.ts.map +1 -0
  507. package/dist/types/utility-types.js +547 -0
  508. package/dist/types/utility-types.js.map +1 -0
  509. package/package.json +44 -0
@@ -0,0 +1,1050 @@
1
+ /**
2
+ * Response Envelope Detector - Response format pattern detection
3
+ *
4
+ * Detects response envelope patterns including:
5
+ * - Standard response envelope patterns ({ data, error, meta, success, message })
6
+ * - JSON:API format ({ data, errors, meta, links, included })
7
+ * - HAL format ({ _links, _embedded })
8
+ * - Pagination metadata ({ page, limit, total, hasMore, nextCursor })
9
+ * - Success/error response structures
10
+ * - Response.json() usage in Next.js
11
+ * - Express res.json() / res.send() patterns
12
+ *
13
+ * Flags violations:
14
+ * - Inconsistent response envelope structure across endpoints
15
+ * - Missing standard fields (data, error, success)
16
+ * - Mixing different response formats (JSON:API vs custom)
17
+ * - Raw data responses without envelope
18
+ * - Inconsistent pagination format
19
+ *
20
+ * @requirements 10.3 - THE API_Detector SHALL detect response envelope patterns ({ data, error, meta })
21
+ */
22
+ import { RegexDetector } from '../base/index.js';
23
+ // ============================================================================
24
+ // Constants
25
+ // ============================================================================
26
+ /**
27
+ * Standard envelope fields
28
+ */
29
+ export const STANDARD_ENVELOPE_FIELDS = {
30
+ data: ['data', 'result', 'results', 'payload', 'body'],
31
+ error: ['error', 'errors', 'err', 'errorMessage', 'message'],
32
+ meta: ['meta', 'metadata', '_meta', 'info'],
33
+ success: ['success', 'ok', 'status', 'isSuccess', 'succeeded'],
34
+ message: ['message', 'msg', 'statusMessage', 'description'],
35
+ };
36
+ /**
37
+ * JSON:API envelope fields
38
+ */
39
+ export const JSON_API_FIELDS = ['data', 'errors', 'meta', 'links', 'included', 'jsonapi'];
40
+ /**
41
+ * HAL format fields
42
+ */
43
+ export const HAL_FIELDS = ['_links', '_embedded'];
44
+ /**
45
+ * GraphQL response fields
46
+ */
47
+ export const GRAPHQL_FIELDS = ['data', 'errors', 'extensions'];
48
+ /**
49
+ * Pagination fields by format
50
+ * Note: Fields are organized to avoid overlap where possible
51
+ * - offset: uses limit/offset terminology
52
+ * - pageBased: uses pageSize/totalPages terminology
53
+ * - cursor: uses cursor/hasMore terminology
54
+ * - linkBased: uses next/prev/first/last links
55
+ */
56
+ export const PAGINATION_FIELDS = {
57
+ offset: ['limit', 'offset', 'total', 'totalCount', 'count'],
58
+ cursor: ['cursor', 'nextCursor', 'prevCursor', 'hasMore', 'hasNext', 'hasPrev', 'endCursor', 'startCursor'],
59
+ pageBased: ['pageSize', 'totalPages', 'currentPage', 'perPage'],
60
+ linkBased: ['next', 'prev', 'first', 'last', 'self'],
61
+ // Common fields that can appear in multiple formats
62
+ common: ['page'],
63
+ };
64
+ /**
65
+ * Response patterns for Next.js
66
+ */
67
+ export const NEXTJS_RESPONSE_PATTERNS = [
68
+ // Response.json({ data, error })
69
+ /Response\.json\s*\(\s*\{([^}]+)\}/gi,
70
+ // NextResponse.json({ data, error })
71
+ /NextResponse\.json\s*\(\s*\{([^}]+)\}/gi,
72
+ // return new Response(JSON.stringify({ data }))
73
+ /new\s+Response\s*\(\s*JSON\.stringify\s*\(\s*\{([^}]+)\}/gi,
74
+ ];
75
+ /**
76
+ * Response patterns for Express
77
+ */
78
+ export const EXPRESS_RESPONSE_PATTERNS = [
79
+ // res.json({ data, error })
80
+ /res\.json\s*\(\s*\{([^}]+)\}/gi,
81
+ // res.send({ data, error })
82
+ /res\.send\s*\(\s*\{([^}]+)\}/gi,
83
+ // res.status(200).json({ data })
84
+ /res\.status\s*\([^)]+\)\s*\.json\s*\(\s*\{([^}]+)\}/gi,
85
+ // res.status(200).send({ data })
86
+ /res\.status\s*\([^)]+\)\s*\.send\s*\(\s*\{([^}]+)\}/gi,
87
+ ];
88
+ /**
89
+ * Generic response object patterns
90
+ */
91
+ export const RESPONSE_OBJECT_PATTERNS = [
92
+ // return { data, error, success }
93
+ /return\s+\{([^}]+)\}/gi,
94
+ // const response = { data, error }
95
+ /(?:const|let|var)\s+(?:response|result|res)\s*=\s*\{([^}]+)\}/gi,
96
+ // { data: ..., error: ..., meta: ... }
97
+ /\{\s*(?:data|error|success|meta|message)\s*:/gi,
98
+ ];
99
+ /**
100
+ * Error response patterns
101
+ */
102
+ export const ERROR_RESPONSE_PATTERNS = [
103
+ // { error: { message, code } }
104
+ /\{\s*error\s*:\s*\{([^}]+)\}\s*\}/gi,
105
+ // { errors: [...] }
106
+ /\{\s*errors\s*:\s*\[/gi,
107
+ // { success: false, error: ... }
108
+ /\{\s*success\s*:\s*false\s*,\s*error/gi,
109
+ // { ok: false, message: ... }
110
+ /\{\s*ok\s*:\s*false\s*,\s*message/gi,
111
+ ];
112
+ /**
113
+ * File patterns to exclude from detection
114
+ */
115
+ export const EXCLUDED_FILE_PATTERNS = [
116
+ /\.test\.[jt]sx?$/,
117
+ /\.spec\.[jt]sx?$/,
118
+ /\.stories\.[jt]sx?$/,
119
+ /\.d\.ts$/,
120
+ /node_modules\//,
121
+ /\.mock\.[jt]sx?$/,
122
+ ];
123
+ // ============================================================================
124
+ // Helper Functions
125
+ // ============================================================================
126
+ /**
127
+ * Check if a file should be excluded from detection
128
+ */
129
+ export function shouldExcludeFile(filePath) {
130
+ return EXCLUDED_FILE_PATTERNS.some(pattern => pattern.test(filePath));
131
+ }
132
+ /**
133
+ * Check if position is inside a comment
134
+ */
135
+ function isInsideComment(content, index) {
136
+ const beforeIndex = content.slice(0, index);
137
+ // Check for single-line comment
138
+ const lastNewline = beforeIndex.lastIndexOf('\n');
139
+ const currentLine = beforeIndex.slice(lastNewline + 1);
140
+ if (currentLine.includes('//')) {
141
+ const commentStart = currentLine.indexOf('//');
142
+ const positionInLine = index - lastNewline - 1;
143
+ if (positionInLine > commentStart) {
144
+ return true;
145
+ }
146
+ }
147
+ // Check for multi-line comment
148
+ const lastBlockCommentStart = beforeIndex.lastIndexOf('/*');
149
+ const lastBlockCommentEnd = beforeIndex.lastIndexOf('*/');
150
+ if (lastBlockCommentStart > lastBlockCommentEnd) {
151
+ return true;
152
+ }
153
+ return false;
154
+ }
155
+ /**
156
+ * Get line and column from index
157
+ */
158
+ function getPositionFromIndex(content, index) {
159
+ const beforeMatch = content.slice(0, index);
160
+ const lineNumber = beforeMatch.split('\n').length;
161
+ const lastNewline = beforeMatch.lastIndexOf('\n');
162
+ const column = index - lastNewline;
163
+ return { line: lineNumber, column };
164
+ }
165
+ /**
166
+ * Extract field names from an object literal string
167
+ */
168
+ export function extractFieldNames(objectContent) {
169
+ const fields = [];
170
+ // Match field names in object literal: { field1: ..., field2: ... }
171
+ const fieldPattern = /([a-zA-Z_$][a-zA-Z0-9_$]*)\s*:/g;
172
+ let match;
173
+ while ((match = fieldPattern.exec(objectContent)) !== null) {
174
+ if (match[1]) {
175
+ fields.push(match[1]);
176
+ }
177
+ }
178
+ return fields;
179
+ }
180
+ /**
181
+ * Detect the envelope format from fields
182
+ */
183
+ export function detectEnvelopeFormat(fields) {
184
+ const lowerFields = fields.map(f => f.toLowerCase());
185
+ // Check for HAL format (most specific)
186
+ if (lowerFields.includes('_links') || lowerFields.includes('_embedded')) {
187
+ return 'hal';
188
+ }
189
+ // Check for JSON:API format
190
+ const jsonApiMatches = JSON_API_FIELDS.filter(f => lowerFields.includes(f.toLowerCase()));
191
+ if (jsonApiMatches.length >= 2 && lowerFields.includes('data')) {
192
+ // JSON:API typically has 'data' plus 'links', 'included', or 'jsonapi'
193
+ if (lowerFields.includes('links') || lowerFields.includes('included') || lowerFields.includes('jsonapi')) {
194
+ return 'json-api';
195
+ }
196
+ }
197
+ // Check for GraphQL format
198
+ if (lowerFields.includes('data') && lowerFields.includes('extensions')) {
199
+ return 'graphql';
200
+ }
201
+ // Check for standard envelope format
202
+ const hasDataField = STANDARD_ENVELOPE_FIELDS.data.some(f => lowerFields.includes(f.toLowerCase()));
203
+ const hasErrorField = STANDARD_ENVELOPE_FIELDS.error.some(f => lowerFields.includes(f.toLowerCase()));
204
+ const hasSuccessField = STANDARD_ENVELOPE_FIELDS.success.some(f => lowerFields.includes(f.toLowerCase()));
205
+ const hasMetaField = STANDARD_ENVELOPE_FIELDS.meta.some(f => lowerFields.includes(f.toLowerCase()));
206
+ if (hasDataField || hasErrorField || hasSuccessField || hasMetaField) {
207
+ return 'standard';
208
+ }
209
+ // Check if it looks like a custom envelope (has some structure)
210
+ if (fields.length >= 2) {
211
+ return 'custom';
212
+ }
213
+ return 'direct';
214
+ }
215
+ /**
216
+ * Detect pagination format from fields
217
+ */
218
+ export function detectPaginationFormat(fields) {
219
+ const lowerFields = fields.map(f => f.toLowerCase());
220
+ let offsetMatches = 0;
221
+ let cursorMatches = 0;
222
+ let pageBasedMatches = 0;
223
+ let linkBasedMatches = 0;
224
+ let hasCommonPageField = false;
225
+ for (const field of lowerFields) {
226
+ // Check common fields
227
+ if (PAGINATION_FIELDS.common.some(f => f.toLowerCase() === field)) {
228
+ hasCommonPageField = true;
229
+ }
230
+ // Check specific format fields
231
+ if (PAGINATION_FIELDS.offset.some(f => f.toLowerCase() === field))
232
+ offsetMatches++;
233
+ if (PAGINATION_FIELDS.cursor.some(f => f.toLowerCase() === field))
234
+ cursorMatches++;
235
+ if (PAGINATION_FIELDS.pageBased.some(f => f.toLowerCase() === field))
236
+ pageBasedMatches++;
237
+ if (PAGINATION_FIELDS.linkBased.some(f => f.toLowerCase() === field))
238
+ linkBasedMatches++;
239
+ }
240
+ // Add common field to the most likely format
241
+ if (hasCommonPageField) {
242
+ // If we have limit/offset fields, it's offset pagination
243
+ if (offsetMatches > 0) {
244
+ offsetMatches++;
245
+ }
246
+ // If we have pageSize/totalPages, it's page-based
247
+ else if (pageBasedMatches > 0) {
248
+ pageBasedMatches++;
249
+ }
250
+ // Default to offset if only 'page' is present with total/count
251
+ else if (lowerFields.includes('total') || lowerFields.includes('count') || lowerFields.includes('limit')) {
252
+ offsetMatches++;
253
+ }
254
+ // Otherwise treat as page-based
255
+ else {
256
+ pageBasedMatches++;
257
+ }
258
+ }
259
+ // Determine dominant format
260
+ const maxMatches = Math.max(offsetMatches, cursorMatches, pageBasedMatches, linkBasedMatches);
261
+ if (maxMatches === 0) {
262
+ return null;
263
+ }
264
+ // Check for mixed formats (multiple distinct format types with significant matches)
265
+ const significantFormats = [
266
+ offsetMatches > 0 ? 1 : 0,
267
+ cursorMatches > 0 ? 1 : 0,
268
+ pageBasedMatches > 0 ? 1 : 0,
269
+ linkBasedMatches > 0 ? 1 : 0,
270
+ ].reduce((a, b) => a + b, 0);
271
+ if (significantFormats > 1 && cursorMatches > 0) {
272
+ // Mixing cursor with other formats is definitely mixed
273
+ return 'mixed';
274
+ }
275
+ if (cursorMatches === maxMatches)
276
+ return 'cursor';
277
+ if (linkBasedMatches === maxMatches)
278
+ return 'link-based';
279
+ if (pageBasedMatches === maxMatches && pageBasedMatches > offsetMatches)
280
+ return 'page-based';
281
+ if (offsetMatches > 0)
282
+ return 'offset';
283
+ if (pageBasedMatches > 0)
284
+ return 'page-based';
285
+ return null;
286
+ }
287
+ /**
288
+ * Check if fields indicate a list/collection response
289
+ */
290
+ export function isListResponse(fields, content) {
291
+ const lowerFields = fields.map(f => f.toLowerCase());
292
+ // Check for array indicators
293
+ if (lowerFields.includes('items') || lowerFields.includes('results') || lowerFields.includes('list')) {
294
+ return true;
295
+ }
296
+ // Check for pagination fields (any format)
297
+ const allPaginationFields = [
298
+ ...PAGINATION_FIELDS.offset,
299
+ ...PAGINATION_FIELDS.cursor,
300
+ ...PAGINATION_FIELDS.pageBased,
301
+ ...PAGINATION_FIELDS.common,
302
+ ];
303
+ const hasPagination = allPaginationFields.some(f => lowerFields.includes(f.toLowerCase()));
304
+ if (hasPagination) {
305
+ return true;
306
+ }
307
+ // Check for array in data field
308
+ if (content.includes('data: [') || content.includes('data:[]') || content.includes('results: [')) {
309
+ return true;
310
+ }
311
+ return false;
312
+ }
313
+ /**
314
+ * Detect Next.js response patterns
315
+ */
316
+ export function detectNextjsResponses(content, file) {
317
+ const results = [];
318
+ const lines = content.split('\n');
319
+ for (const pattern of NEXTJS_RESPONSE_PATTERNS) {
320
+ const regex = new RegExp(pattern.source, pattern.flags);
321
+ let match;
322
+ while ((match = regex.exec(content)) !== null) {
323
+ if (isInsideComment(content, match.index)) {
324
+ continue;
325
+ }
326
+ const { line, column } = getPositionFromIndex(content, match.index);
327
+ const objectContent = match[1] || '';
328
+ const fields = extractFieldNames(objectContent);
329
+ const format = detectEnvelopeFormat(fields);
330
+ const paginationFormat = detectPaginationFormat(fields);
331
+ results.push({
332
+ type: 'nextjs-response',
333
+ format,
334
+ file,
335
+ line,
336
+ column,
337
+ matchedText: match[0],
338
+ fields,
339
+ paginationFormat: paginationFormat || undefined,
340
+ context: lines[line - 1] || '',
341
+ });
342
+ }
343
+ }
344
+ return results;
345
+ }
346
+ /**
347
+ * Detect Express response patterns
348
+ */
349
+ export function detectExpressResponses(content, file) {
350
+ const results = [];
351
+ const lines = content.split('\n');
352
+ for (const pattern of EXPRESS_RESPONSE_PATTERNS) {
353
+ const regex = new RegExp(pattern.source, pattern.flags);
354
+ let match;
355
+ while ((match = regex.exec(content)) !== null) {
356
+ if (isInsideComment(content, match.index)) {
357
+ continue;
358
+ }
359
+ const { line, column } = getPositionFromIndex(content, match.index);
360
+ const objectContent = match[1] || '';
361
+ const fields = extractFieldNames(objectContent);
362
+ const format = detectEnvelopeFormat(fields);
363
+ const paginationFormat = detectPaginationFormat(fields);
364
+ results.push({
365
+ type: 'express-response',
366
+ format,
367
+ file,
368
+ line,
369
+ column,
370
+ matchedText: match[0],
371
+ fields,
372
+ paginationFormat: paginationFormat || undefined,
373
+ context: lines[line - 1] || '',
374
+ });
375
+ }
376
+ }
377
+ return results;
378
+ }
379
+ /**
380
+ * Detect generic response object patterns
381
+ */
382
+ export function detectResponseObjects(content, file) {
383
+ const results = [];
384
+ const lines = content.split('\n');
385
+ for (const pattern of RESPONSE_OBJECT_PATTERNS) {
386
+ const regex = new RegExp(pattern.source, pattern.flags);
387
+ let match;
388
+ while ((match = regex.exec(content)) !== null) {
389
+ if (isInsideComment(content, match.index)) {
390
+ continue;
391
+ }
392
+ const { line, column } = getPositionFromIndex(content, match.index);
393
+ const objectContent = match[1] || match[0];
394
+ const fields = extractFieldNames(objectContent);
395
+ // Skip if no relevant fields found
396
+ if (fields.length === 0) {
397
+ continue;
398
+ }
399
+ const format = detectEnvelopeFormat(fields);
400
+ // Skip direct format unless it has some envelope-like structure
401
+ if (format === 'direct' && fields.length < 2) {
402
+ continue;
403
+ }
404
+ const paginationFormat = detectPaginationFormat(fields);
405
+ results.push({
406
+ type: 'envelope-structure',
407
+ format,
408
+ file,
409
+ line,
410
+ column,
411
+ matchedText: match[0],
412
+ fields,
413
+ paginationFormat: paginationFormat || undefined,
414
+ context: lines[line - 1] || '',
415
+ });
416
+ }
417
+ }
418
+ return results;
419
+ }
420
+ /**
421
+ * Detect error response patterns
422
+ */
423
+ export function detectErrorResponses(content, file) {
424
+ const results = [];
425
+ const lines = content.split('\n');
426
+ for (const pattern of ERROR_RESPONSE_PATTERNS) {
427
+ const regex = new RegExp(pattern.source, pattern.flags);
428
+ let match;
429
+ while ((match = regex.exec(content)) !== null) {
430
+ if (isInsideComment(content, match.index)) {
431
+ continue;
432
+ }
433
+ const { line, column } = getPositionFromIndex(content, match.index);
434
+ const objectContent = match[1] || match[0];
435
+ const fields = extractFieldNames(objectContent);
436
+ const format = detectEnvelopeFormat(['error', ...fields]);
437
+ results.push({
438
+ type: 'error-response',
439
+ format,
440
+ file,
441
+ line,
442
+ column,
443
+ matchedText: match[0],
444
+ fields: ['error', ...fields],
445
+ context: lines[line - 1] || '',
446
+ });
447
+ }
448
+ }
449
+ return results;
450
+ }
451
+ /**
452
+ * Detect pagination metadata patterns
453
+ */
454
+ export function detectPaginationPatterns(content, file) {
455
+ const results = [];
456
+ const lines = content.split('\n');
457
+ // Pattern for pagination objects - include all pagination-related fields
458
+ const paginationPattern = /\{\s*(?:page|limit|offset|cursor|total|hasMore|nextCursor|pageSize|totalPages|currentPage|perPage|count|totalCount)\s*:/gi;
459
+ let match;
460
+ while ((match = paginationPattern.exec(content)) !== null) {
461
+ if (isInsideComment(content, match.index)) {
462
+ continue;
463
+ }
464
+ // Find the full object
465
+ const startIndex = match.index;
466
+ let braceCount = 0;
467
+ let endIndex = startIndex;
468
+ for (let i = startIndex; i < content.length; i++) {
469
+ if (content[i] === '{')
470
+ braceCount++;
471
+ if (content[i] === '}') {
472
+ braceCount--;
473
+ if (braceCount === 0) {
474
+ endIndex = i + 1;
475
+ break;
476
+ }
477
+ }
478
+ }
479
+ const objectContent = content.slice(startIndex, endIndex);
480
+ const fields = extractFieldNames(objectContent);
481
+ const paginationFormat = detectPaginationFormat(fields);
482
+ if (paginationFormat) {
483
+ const { line, column } = getPositionFromIndex(content, match.index);
484
+ results.push({
485
+ type: 'pagination-metadata',
486
+ format: 'standard',
487
+ file,
488
+ line,
489
+ column,
490
+ matchedText: objectContent.slice(0, 100), // Truncate for readability
491
+ fields,
492
+ paginationFormat,
493
+ context: lines[line - 1] || '',
494
+ });
495
+ }
496
+ }
497
+ return results;
498
+ }
499
+ // ============================================================================
500
+ // Violation Detection Functions
501
+ // ============================================================================
502
+ /**
503
+ * Detect inconsistent envelope structure violations
504
+ */
505
+ export function detectInconsistentEnvelopeViolations(patterns, file) {
506
+ const violations = [];
507
+ // Group patterns by format
508
+ const formatCounts = {
509
+ 'standard': 0,
510
+ 'json-api': 0,
511
+ 'hal': 0,
512
+ 'graphql': 0,
513
+ 'custom': 0,
514
+ 'direct': 0,
515
+ };
516
+ for (const pattern of patterns) {
517
+ formatCounts[pattern.format]++;
518
+ }
519
+ // Find dominant format (excluding direct)
520
+ let dominantFormat = null;
521
+ let maxCount = 0;
522
+ for (const [format, count] of Object.entries(formatCounts)) {
523
+ if (format !== 'direct' && count > maxCount) {
524
+ maxCount = count;
525
+ dominantFormat = format;
526
+ }
527
+ }
528
+ // Flag patterns that don't match dominant format
529
+ if (dominantFormat && maxCount >= 2) {
530
+ for (const pattern of patterns) {
531
+ if (pattern.format !== dominantFormat && pattern.format !== 'direct') {
532
+ violations.push({
533
+ type: 'mixed-formats',
534
+ file,
535
+ line: pattern.line,
536
+ column: pattern.column,
537
+ endLine: pattern.line,
538
+ endColumn: pattern.column + pattern.matchedText.length,
539
+ value: pattern.format,
540
+ issue: `Response uses ${pattern.format} format but project predominantly uses ${dominantFormat}`,
541
+ suggestedFix: `Convert to ${dominantFormat} format for consistency`,
542
+ lineContent: pattern.context || '',
543
+ });
544
+ }
545
+ }
546
+ }
547
+ return violations;
548
+ }
549
+ /**
550
+ * Detect missing standard fields violations
551
+ */
552
+ export function detectMissingFieldViolations(patterns, file) {
553
+ const violations = [];
554
+ // Check standard format responses for missing fields
555
+ const standardPatterns = patterns.filter(p => p.format === 'standard');
556
+ // Determine which fields are commonly used
557
+ const fieldUsage = {};
558
+ for (const pattern of standardPatterns) {
559
+ for (const field of pattern.fields || []) {
560
+ const lowerField = field.toLowerCase();
561
+ fieldUsage[lowerField] = (fieldUsage[lowerField] || 0) + 1;
562
+ }
563
+ }
564
+ // Check for missing common fields
565
+ const hasDataField = standardPatterns.some(p => p.fields?.some(f => STANDARD_ENVELOPE_FIELDS.data.includes(f.toLowerCase())));
566
+ const hasErrorField = standardPatterns.some(p => p.fields?.some(f => STANDARD_ENVELOPE_FIELDS.error.includes(f.toLowerCase())));
567
+ // If most responses have data field, flag those without
568
+ if (hasDataField) {
569
+ for (const pattern of standardPatterns) {
570
+ const patternHasData = pattern.fields?.some(f => STANDARD_ENVELOPE_FIELDS.data.includes(f.toLowerCase()));
571
+ const patternHasError = pattern.fields?.some(f => STANDARD_ENVELOPE_FIELDS.error.includes(f.toLowerCase()));
572
+ // Skip error responses - they don't need data field
573
+ if (patternHasError && !patternHasData) {
574
+ continue;
575
+ }
576
+ if (!patternHasData && !patternHasError && pattern.type !== 'error-response') {
577
+ violations.push({
578
+ type: 'missing-data-field',
579
+ file,
580
+ line: pattern.line,
581
+ column: pattern.column,
582
+ endLine: pattern.line,
583
+ endColumn: pattern.column + pattern.matchedText.length,
584
+ value: pattern.matchedText,
585
+ issue: 'Response envelope is missing a data field',
586
+ suggestedFix: 'Add a "data" field to wrap the response payload',
587
+ lineContent: pattern.context || '',
588
+ });
589
+ }
590
+ }
591
+ }
592
+ // If most responses have error handling, flag those without
593
+ if (hasErrorField && standardPatterns.length >= 3) {
594
+ const patternsWithError = standardPatterns.filter(p => p.fields?.some(f => STANDARD_ENVELOPE_FIELDS.error.includes(f.toLowerCase())));
595
+ // Only flag if majority have error field
596
+ if (patternsWithError.length > standardPatterns.length / 2) {
597
+ for (const pattern of standardPatterns) {
598
+ const patternHasError = pattern.fields?.some(f => STANDARD_ENVELOPE_FIELDS.error.includes(f.toLowerCase()));
599
+ if (!patternHasError && pattern.type !== 'error-response') {
600
+ violations.push({
601
+ type: 'missing-error-field',
602
+ file,
603
+ line: pattern.line,
604
+ column: pattern.column,
605
+ endLine: pattern.line,
606
+ endColumn: pattern.column + pattern.matchedText.length,
607
+ value: pattern.matchedText,
608
+ issue: 'Response envelope is missing an error field',
609
+ suggestedFix: 'Add an "error" field for consistent error handling',
610
+ lineContent: pattern.context || '',
611
+ });
612
+ }
613
+ }
614
+ }
615
+ }
616
+ return violations;
617
+ }
618
+ /**
619
+ * Detect raw data response violations
620
+ */
621
+ export function detectRawDataViolations(patterns, content, file) {
622
+ const violations = [];
623
+ const lines = content.split('\n');
624
+ // Check if file uses envelope pattern
625
+ const envelopePatterns = patterns.filter(p => p.format !== 'direct');
626
+ if (envelopePatterns.length === 0) {
627
+ return violations; // No envelope pattern established
628
+ }
629
+ // Look for direct array returns or simple object returns
630
+ const directReturnPatterns = [
631
+ // return [...] - direct array return
632
+ /return\s+\[/g,
633
+ // res.json([...]) - direct array in response
634
+ /res\.json\s*\(\s*\[/g,
635
+ // Response.json([...]) - direct array in Next.js
636
+ /Response\.json\s*\(\s*\[/g,
637
+ // NextResponse.json([...])
638
+ /NextResponse\.json\s*\(\s*\[/g,
639
+ ];
640
+ for (const pattern of directReturnPatterns) {
641
+ const regex = new RegExp(pattern.source, pattern.flags);
642
+ let match;
643
+ while ((match = regex.exec(content)) !== null) {
644
+ if (isInsideComment(content, match.index)) {
645
+ continue;
646
+ }
647
+ const { line, column } = getPositionFromIndex(content, match.index);
648
+ violations.push({
649
+ type: 'raw-data-response',
650
+ file,
651
+ line,
652
+ column,
653
+ endLine: line,
654
+ endColumn: column + match[0].length,
655
+ value: match[0],
656
+ issue: 'Direct array/data return without envelope wrapper',
657
+ suggestedFix: 'Wrap response in envelope: { data: [...], success: true }',
658
+ lineContent: lines[line - 1] || '',
659
+ });
660
+ }
661
+ }
662
+ return violations;
663
+ }
664
+ /**
665
+ * Detect inconsistent pagination violations
666
+ */
667
+ export function detectInconsistentPaginationViolations(patterns, file) {
668
+ const violations = [];
669
+ // Get patterns with pagination
670
+ const paginatedPatterns = patterns.filter(p => p.paginationFormat);
671
+ if (paginatedPatterns.length < 2) {
672
+ return violations; // Not enough patterns to detect inconsistency
673
+ }
674
+ // Count pagination formats
675
+ const formatCounts = {
676
+ 'offset': 0,
677
+ 'cursor': 0,
678
+ 'page-based': 0,
679
+ 'link-based': 0,
680
+ 'mixed': 0,
681
+ };
682
+ for (const pattern of paginatedPatterns) {
683
+ if (pattern.paginationFormat) {
684
+ formatCounts[pattern.paginationFormat]++;
685
+ }
686
+ }
687
+ // Find dominant format
688
+ let dominantFormat = null;
689
+ let maxCount = 0;
690
+ for (const [format, count] of Object.entries(formatCounts)) {
691
+ if (format !== 'mixed' && count > maxCount) {
692
+ maxCount = count;
693
+ dominantFormat = format;
694
+ }
695
+ }
696
+ // Flag inconsistent pagination
697
+ if (dominantFormat && maxCount >= 2) {
698
+ for (const pattern of paginatedPatterns) {
699
+ if (pattern.paginationFormat &&
700
+ pattern.paginationFormat !== dominantFormat &&
701
+ pattern.paginationFormat !== 'mixed') {
702
+ violations.push({
703
+ type: 'inconsistent-pagination',
704
+ file,
705
+ line: pattern.line,
706
+ column: pattern.column,
707
+ endLine: pattern.line,
708
+ endColumn: pattern.column + pattern.matchedText.length,
709
+ value: pattern.paginationFormat,
710
+ issue: `Pagination uses ${pattern.paginationFormat} format but project uses ${dominantFormat}`,
711
+ suggestedFix: `Convert to ${dominantFormat} pagination format`,
712
+ lineContent: pattern.context || '',
713
+ });
714
+ }
715
+ }
716
+ }
717
+ // Flag mixed pagination within single response
718
+ for (const pattern of paginatedPatterns) {
719
+ if (pattern.paginationFormat === 'mixed') {
720
+ violations.push({
721
+ type: 'inconsistent-pagination',
722
+ file,
723
+ line: pattern.line,
724
+ column: pattern.column,
725
+ endLine: pattern.line,
726
+ endColumn: pattern.column + pattern.matchedText.length,
727
+ value: 'mixed',
728
+ issue: 'Response mixes different pagination formats',
729
+ suggestedFix: 'Use a single consistent pagination format',
730
+ lineContent: pattern.context || '',
731
+ });
732
+ }
733
+ }
734
+ return violations;
735
+ }
736
+ // ============================================================================
737
+ // Main Analysis Function
738
+ // ============================================================================
739
+ /**
740
+ * Analyze response envelope patterns in a file
741
+ */
742
+ export function analyzeResponseEnvelope(content, file) {
743
+ // Skip excluded files
744
+ if (shouldExcludeFile(file)) {
745
+ return {
746
+ envelopePatterns: [],
747
+ violations: [],
748
+ dominantFormat: null,
749
+ paginationFormat: null,
750
+ usesConsistentEnvelope: true,
751
+ patternAdherenceConfidence: 1.0,
752
+ };
753
+ }
754
+ // Detect all response patterns
755
+ const nextjsResponses = detectNextjsResponses(content, file);
756
+ const expressResponses = detectExpressResponses(content, file);
757
+ const responseObjects = detectResponseObjects(content, file);
758
+ const errorResponses = detectErrorResponses(content, file);
759
+ const paginationPatterns = detectPaginationPatterns(content, file);
760
+ // Combine all patterns (deduplicate by line)
761
+ const seenLines = new Set();
762
+ const envelopePatterns = [];
763
+ for (const pattern of [...nextjsResponses, ...expressResponses, ...responseObjects, ...errorResponses, ...paginationPatterns]) {
764
+ if (!seenLines.has(pattern.line)) {
765
+ seenLines.add(pattern.line);
766
+ envelopePatterns.push(pattern);
767
+ }
768
+ }
769
+ // Determine dominant format
770
+ const formatCounts = {
771
+ 'standard': 0,
772
+ 'json-api': 0,
773
+ 'hal': 0,
774
+ 'graphql': 0,
775
+ 'custom': 0,
776
+ 'direct': 0,
777
+ };
778
+ for (const pattern of envelopePatterns) {
779
+ formatCounts[pattern.format]++;
780
+ }
781
+ let dominantFormat = null;
782
+ let maxCount = 0;
783
+ for (const [format, count] of Object.entries(formatCounts)) {
784
+ if (format !== 'direct' && count > maxCount) {
785
+ maxCount = count;
786
+ dominantFormat = format;
787
+ }
788
+ }
789
+ // Determine pagination format
790
+ const paginationFormats = envelopePatterns
791
+ .filter(p => p.paginationFormat)
792
+ .map(p => p.paginationFormat);
793
+ let paginationFormat = null;
794
+ if (paginationFormats.length > 0) {
795
+ const paginationCounts = {
796
+ 'offset': 0,
797
+ 'cursor': 0,
798
+ 'page-based': 0,
799
+ 'link-based': 0,
800
+ 'mixed': 0,
801
+ };
802
+ for (const format of paginationFormats) {
803
+ paginationCounts[format]++;
804
+ }
805
+ let maxPaginationCount = 0;
806
+ for (const [format, count] of Object.entries(paginationCounts)) {
807
+ if (count > maxPaginationCount) {
808
+ maxPaginationCount = count;
809
+ paginationFormat = format;
810
+ }
811
+ }
812
+ }
813
+ // Detect violations
814
+ const inconsistentEnvelopeViolations = detectInconsistentEnvelopeViolations(envelopePatterns, file);
815
+ const missingFieldViolations = detectMissingFieldViolations(envelopePatterns, file);
816
+ const rawDataViolations = detectRawDataViolations(envelopePatterns, content, file);
817
+ const paginationViolations = detectInconsistentPaginationViolations(envelopePatterns, file);
818
+ const violations = [
819
+ ...inconsistentEnvelopeViolations,
820
+ ...missingFieldViolations,
821
+ ...rawDataViolations,
822
+ ...paginationViolations,
823
+ ];
824
+ // Determine consistency
825
+ const usesConsistentEnvelope = violations.length === 0 && envelopePatterns.length > 0;
826
+ // Calculate confidence
827
+ let patternAdherenceConfidence = 1.0;
828
+ if (envelopePatterns.length > 0 && violations.length > 0) {
829
+ patternAdherenceConfidence = Math.max(0, 1 - (violations.length / envelopePatterns.length));
830
+ }
831
+ else if (envelopePatterns.length === 0) {
832
+ patternAdherenceConfidence = 0.5;
833
+ }
834
+ return {
835
+ envelopePatterns,
836
+ violations,
837
+ dominantFormat,
838
+ paginationFormat,
839
+ usesConsistentEnvelope,
840
+ patternAdherenceConfidence,
841
+ };
842
+ }
843
+ // ============================================================================
844
+ // Response Envelope Detector Class
845
+ // ============================================================================
846
+ /**
847
+ * Detector for response envelope patterns
848
+ *
849
+ * Identifies response envelope patterns and flags violations of consistency.
850
+ *
851
+ * @requirements 10.3 - THE API_Detector SHALL detect response envelope patterns ({ data, error, meta })
852
+ */
853
+ export class ResponseEnvelopeDetector extends RegexDetector {
854
+ id = 'api/response-envelope';
855
+ category = 'api';
856
+ subcategory = 'response-envelope';
857
+ name = 'Response Envelope Detector';
858
+ description = 'Detects response envelope patterns and flags inconsistencies';
859
+ supportedLanguages = ['typescript', 'javascript', 'python'];
860
+ /**
861
+ * Detect response envelope patterns and violations
862
+ */
863
+ async detect(context) {
864
+ const patterns = [];
865
+ const violations = [];
866
+ // Analyze the file
867
+ const analysis = analyzeResponseEnvelope(context.content, context.file);
868
+ // Create pattern matches for envelope formats
869
+ if (analysis.dominantFormat) {
870
+ patterns.push({
871
+ patternId: `${this.id}/format-${analysis.dominantFormat}`,
872
+ location: {
873
+ file: context.file,
874
+ line: analysis.envelopePatterns[0]?.line || 1,
875
+ column: analysis.envelopePatterns[0]?.column || 1,
876
+ },
877
+ confidence: 1.0,
878
+ isOutlier: false,
879
+ });
880
+ }
881
+ // Create pattern matches for pagination format
882
+ if (analysis.paginationFormat) {
883
+ const paginatedPattern = analysis.envelopePatterns.find(p => p.paginationFormat);
884
+ patterns.push({
885
+ patternId: `${this.id}/pagination-${analysis.paginationFormat}`,
886
+ location: {
887
+ file: context.file,
888
+ line: paginatedPattern?.line || 1,
889
+ column: paginatedPattern?.column || 1,
890
+ },
891
+ confidence: 1.0,
892
+ isOutlier: false,
893
+ });
894
+ }
895
+ // Create pattern matches by type
896
+ const patternTypes = new Set(analysis.envelopePatterns.map(p => p.type));
897
+ for (const patternType of patternTypes) {
898
+ const firstOfType = analysis.envelopePatterns.find(p => p.type === patternType);
899
+ if (firstOfType) {
900
+ patterns.push({
901
+ patternId: `${this.id}/${patternType}`,
902
+ location: {
903
+ file: context.file,
904
+ line: firstOfType.line,
905
+ column: firstOfType.column,
906
+ },
907
+ confidence: 1.0,
908
+ isOutlier: false,
909
+ });
910
+ }
911
+ }
912
+ // Create violations
913
+ for (const violation of analysis.violations) {
914
+ violations.push(this.createViolation(violation));
915
+ }
916
+ return this.createResult(patterns, violations, analysis.patternAdherenceConfidence);
917
+ }
918
+ /**
919
+ * Create a Violation from ResponseEnvelopeViolationInfo
920
+ */
921
+ createViolation(info) {
922
+ const severityMap = {
923
+ 'inconsistent-envelope': 'warning',
924
+ 'missing-data-field': 'info',
925
+ 'missing-error-field': 'info',
926
+ 'missing-success-indicator': 'info',
927
+ 'mixed-formats': 'warning',
928
+ 'raw-data-response': 'info',
929
+ 'inconsistent-pagination': 'warning',
930
+ 'missing-pagination': 'info',
931
+ };
932
+ const violation = {
933
+ id: `${this.id}-${info.file}-${info.line}-${info.column}`,
934
+ patternId: this.id,
935
+ severity: severityMap[info.type] || 'warning',
936
+ file: info.file,
937
+ range: {
938
+ start: { line: info.line - 1, character: info.column - 1 },
939
+ end: { line: info.endLine - 1, character: info.endColumn - 1 },
940
+ },
941
+ message: info.issue,
942
+ explanation: this.getExplanation(info.type),
943
+ expected: info.suggestedFix || 'Consistent response envelope',
944
+ actual: info.value,
945
+ aiExplainAvailable: true,
946
+ aiFixAvailable: !!info.suggestedFix,
947
+ firstSeen: new Date(),
948
+ occurrences: 1,
949
+ };
950
+ if (info.suggestedFix) {
951
+ const quickFix = this.createQuickFixForViolation(info);
952
+ if (quickFix) {
953
+ violation.quickFix = quickFix;
954
+ }
955
+ }
956
+ return violation;
957
+ }
958
+ /**
959
+ * Get explanation for a violation type
960
+ */
961
+ getExplanation(type) {
962
+ const explanations = {
963
+ 'inconsistent-envelope': 'Consistent response envelope structure makes APIs predictable and easier to consume. ' +
964
+ 'Clients can rely on a standard structure for parsing responses.',
965
+ 'missing-data-field': 'A "data" field in the response envelope clearly separates the payload from metadata. ' +
966
+ 'This makes it easier to add metadata without breaking existing clients.',
967
+ 'missing-error-field': 'An "error" field provides a consistent location for error information. ' +
968
+ 'This helps clients handle errors uniformly across all endpoints.',
969
+ 'missing-success-indicator': 'A success indicator (like "success" or "ok") makes it easy to check response status ' +
970
+ 'without parsing the entire response or relying solely on HTTP status codes.',
971
+ 'mixed-formats': 'Mixing different response formats (e.g., JSON:API with custom format) creates ' +
972
+ 'inconsistency and makes the API harder to consume. Choose one format and use it consistently.',
973
+ 'raw-data-response': 'Returning raw data without an envelope makes it harder to add metadata later. ' +
974
+ 'Wrapping responses in an envelope provides flexibility for future changes.',
975
+ 'inconsistent-pagination': 'Consistent pagination format across endpoints makes it easier for clients to ' +
976
+ 'implement pagination logic. Use the same pagination style throughout the API.',
977
+ 'missing-pagination': 'List endpoints should include pagination metadata to help clients navigate large datasets. ' +
978
+ 'Include fields like total, page, limit, or cursor information.',
979
+ };
980
+ return explanations[type] || 'Follow consistent response envelope patterns.';
981
+ }
982
+ /**
983
+ * Create a quick fix for a violation
984
+ */
985
+ createQuickFixForViolation(info) {
986
+ if (!info.suggestedFix) {
987
+ return undefined;
988
+ }
989
+ return {
990
+ title: info.suggestedFix,
991
+ kind: 'quickfix',
992
+ edit: {
993
+ changes: {
994
+ [info.file]: [
995
+ {
996
+ range: {
997
+ start: { line: info.line - 1, character: 0 },
998
+ end: { line: info.line - 1, character: info.lineContent.length },
999
+ },
1000
+ newText: info.lineContent, // Placeholder - actual fix would need more context
1001
+ },
1002
+ ],
1003
+ },
1004
+ },
1005
+ isPreferred: true,
1006
+ confidence: 0.5,
1007
+ preview: info.suggestedFix,
1008
+ };
1009
+ }
1010
+ /**
1011
+ * Generate a quick fix for a violation
1012
+ */
1013
+ generateQuickFix(violation) {
1014
+ // Check if this is a response envelope violation
1015
+ if (!violation.patternId.startsWith('api/response-envelope')) {
1016
+ return null;
1017
+ }
1018
+ // Generate appropriate fix based on violation type
1019
+ if (violation.message.includes('missing a data field')) {
1020
+ return {
1021
+ title: 'Wrap response in data field',
1022
+ kind: 'quickfix',
1023
+ edit: {
1024
+ changes: {
1025
+ [violation.file]: [
1026
+ {
1027
+ range: violation.range,
1028
+ newText: '{ data: ' + violation.actual + ' }',
1029
+ },
1030
+ ],
1031
+ },
1032
+ },
1033
+ isPreferred: true,
1034
+ confidence: 0.6,
1035
+ preview: 'Wrap response payload in { data: ... }',
1036
+ };
1037
+ }
1038
+ return null;
1039
+ }
1040
+ }
1041
+ // ============================================================================
1042
+ // Factory Function
1043
+ // ============================================================================
1044
+ /**
1045
+ * Create a new ResponseEnvelopeDetector instance
1046
+ */
1047
+ export function createResponseEnvelopeDetector() {
1048
+ return new ResponseEnvelopeDetector();
1049
+ }
1050
+ //# sourceMappingURL=response-envelope.js.map