notelm-mcp 1.2.1

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 (323) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +456 -0
  3. package/dist/auth/auth-manager.d.ts +139 -0
  4. package/dist/auth/auth-manager.d.ts.map +1 -0
  5. package/dist/auth/auth-manager.js +960 -0
  6. package/dist/auth/auth-manager.js.map +1 -0
  7. package/dist/config.d.ts +92 -0
  8. package/dist/config.d.ts.map +1 -0
  9. package/dist/config.js +219 -0
  10. package/dist/config.js.map +1 -0
  11. package/dist/constants.d.ts +58 -0
  12. package/dist/constants.d.ts.map +1 -0
  13. package/dist/constants.js +133 -0
  14. package/dist/constants.js.map +1 -0
  15. package/dist/errors.d.ts +26 -0
  16. package/dist/errors.d.ts.map +1 -0
  17. package/dist/errors.js +41 -0
  18. package/dist/errors.js.map +1 -0
  19. package/dist/index.d.ts +32 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +325 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/library/notebook-library.d.ts +70 -0
  24. package/dist/library/notebook-library.d.ts.map +1 -0
  25. package/dist/library/notebook-library.js +279 -0
  26. package/dist/library/notebook-library.js.map +1 -0
  27. package/dist/library/types.d.ts +67 -0
  28. package/dist/library/types.d.ts.map +1 -0
  29. package/dist/library/types.js +8 -0
  30. package/dist/library/types.js.map +1 -0
  31. package/dist/playwright.config.d.ts +3 -0
  32. package/dist/playwright.config.d.ts.map +1 -0
  33. package/dist/playwright.config.js +38 -0
  34. package/dist/playwright.config.js.map +1 -0
  35. package/dist/resources/resource-handlers.d.ts +22 -0
  36. package/dist/resources/resource-handlers.d.ts.map +1 -0
  37. package/dist/resources/resource-handlers.js +216 -0
  38. package/dist/resources/resource-handlers.js.map +1 -0
  39. package/dist/scripts/save-auth-state.d.ts +2 -0
  40. package/dist/scripts/save-auth-state.d.ts.map +1 -0
  41. package/dist/scripts/save-auth-state.js +91 -0
  42. package/dist/scripts/save-auth-state.js.map +1 -0
  43. package/dist/session/browser-session.d.ts +108 -0
  44. package/dist/session/browser-session.d.ts.map +1 -0
  45. package/dist/session/browser-session.js +636 -0
  46. package/dist/session/browser-session.js.map +1 -0
  47. package/dist/session/session-manager.d.ts +76 -0
  48. package/dist/session/session-manager.d.ts.map +1 -0
  49. package/dist/session/session-manager.js +273 -0
  50. package/dist/session/session-manager.js.map +1 -0
  51. package/dist/session/shared-context-manager.d.ts +107 -0
  52. package/dist/session/shared-context-manager.d.ts.map +1 -0
  53. package/dist/session/shared-context-manager.js +447 -0
  54. package/dist/session/shared-context-manager.js.map +1 -0
  55. package/dist/src/auth/auth-manager.d.ts +139 -0
  56. package/dist/src/auth/auth-manager.d.ts.map +1 -0
  57. package/dist/src/auth/auth-manager.js +960 -0
  58. package/dist/src/auth/auth-manager.js.map +1 -0
  59. package/dist/src/config.d.ts +92 -0
  60. package/dist/src/config.d.ts.map +1 -0
  61. package/dist/src/config.js +219 -0
  62. package/dist/src/config.js.map +1 -0
  63. package/dist/src/constants.d.ts +58 -0
  64. package/dist/src/constants.d.ts.map +1 -0
  65. package/dist/src/constants.js +133 -0
  66. package/dist/src/constants.js.map +1 -0
  67. package/dist/src/errors.d.ts +26 -0
  68. package/dist/src/errors.d.ts.map +1 -0
  69. package/dist/src/errors.js +41 -0
  70. package/dist/src/errors.js.map +1 -0
  71. package/dist/src/index.d.ts +32 -0
  72. package/dist/src/index.d.ts.map +1 -0
  73. package/dist/src/index.js +325 -0
  74. package/dist/src/index.js.map +1 -0
  75. package/dist/src/library/notebook-library.d.ts +70 -0
  76. package/dist/src/library/notebook-library.d.ts.map +1 -0
  77. package/dist/src/library/notebook-library.js +279 -0
  78. package/dist/src/library/notebook-library.js.map +1 -0
  79. package/dist/src/library/types.d.ts +67 -0
  80. package/dist/src/library/types.d.ts.map +1 -0
  81. package/dist/src/library/types.js +8 -0
  82. package/dist/src/library/types.js.map +1 -0
  83. package/dist/src/resources/resource-handlers.d.ts +22 -0
  84. package/dist/src/resources/resource-handlers.d.ts.map +1 -0
  85. package/dist/src/resources/resource-handlers.js +216 -0
  86. package/dist/src/resources/resource-handlers.js.map +1 -0
  87. package/dist/src/scripts/health-check.d.ts +13 -0
  88. package/dist/src/scripts/health-check.d.ts.map +1 -0
  89. package/dist/src/scripts/health-check.js +100 -0
  90. package/dist/src/scripts/health-check.js.map +1 -0
  91. package/dist/src/session/browser-session.d.ts +108 -0
  92. package/dist/src/session/browser-session.d.ts.map +1 -0
  93. package/dist/src/session/browser-session.js +642 -0
  94. package/dist/src/session/browser-session.js.map +1 -0
  95. package/dist/src/session/session-manager.d.ts +76 -0
  96. package/dist/src/session/session-manager.d.ts.map +1 -0
  97. package/dist/src/session/session-manager.js +273 -0
  98. package/dist/src/session/session-manager.js.map +1 -0
  99. package/dist/src/session/shared-context-manager.d.ts +107 -0
  100. package/dist/src/session/shared-context-manager.d.ts.map +1 -0
  101. package/dist/src/session/shared-context-manager.js +447 -0
  102. package/dist/src/session/shared-context-manager.js.map +1 -0
  103. package/dist/src/tools/definitions/ask-question.d.ts +8 -0
  104. package/dist/src/tools/definitions/ask-question.d.ts.map +1 -0
  105. package/dist/src/tools/definitions/ask-question.js +211 -0
  106. package/dist/src/tools/definitions/ask-question.js.map +1 -0
  107. package/dist/src/tools/definitions/notebook-management.d.ts +3 -0
  108. package/dist/src/tools/definitions/notebook-management.d.ts.map +1 -0
  109. package/dist/src/tools/definitions/notebook-management.js +243 -0
  110. package/dist/src/tools/definitions/notebook-management.js.map +1 -0
  111. package/dist/src/tools/definitions/session-management.d.ts +3 -0
  112. package/dist/src/tools/definitions/session-management.d.ts.map +1 -0
  113. package/dist/src/tools/definitions/session-management.js +41 -0
  114. package/dist/src/tools/definitions/session-management.js.map +1 -0
  115. package/dist/src/tools/definitions/system.d.ts +3 -0
  116. package/dist/src/tools/definitions/system.d.ts.map +1 -0
  117. package/dist/src/tools/definitions/system.js +143 -0
  118. package/dist/src/tools/definitions/system.js.map +1 -0
  119. package/dist/src/tools/definitions.d.ts +12 -0
  120. package/dist/src/tools/definitions.d.ts.map +1 -0
  121. package/dist/src/tools/definitions.js +26 -0
  122. package/dist/src/tools/definitions.js.map +1 -0
  123. package/dist/src/tools/handlers.d.ts +212 -0
  124. package/dist/src/tools/handlers.d.ts.map +1 -0
  125. package/dist/src/tools/handlers.js +712 -0
  126. package/dist/src/tools/handlers.js.map +1 -0
  127. package/dist/src/tools/index.d.ts +8 -0
  128. package/dist/src/tools/index.d.ts.map +1 -0
  129. package/dist/src/tools/index.js +8 -0
  130. package/dist/src/tools/index.js.map +1 -0
  131. package/dist/src/types.d.ts +88 -0
  132. package/dist/src/types.d.ts.map +1 -0
  133. package/dist/src/types.js +5 -0
  134. package/dist/src/types.js.map +1 -0
  135. package/dist/src/utils/auth-manager.d.ts +2 -0
  136. package/dist/src/utils/auth-manager.d.ts.map +1 -0
  137. package/dist/src/utils/auth-manager.js +25 -0
  138. package/dist/src/utils/auth-manager.js.map +1 -0
  139. package/dist/src/utils/cleanup-manager.d.ts +133 -0
  140. package/dist/src/utils/cleanup-manager.d.ts.map +1 -0
  141. package/dist/src/utils/cleanup-manager.js +673 -0
  142. package/dist/src/utils/cleanup-manager.js.map +1 -0
  143. package/dist/src/utils/cli-handler.d.ts +16 -0
  144. package/dist/src/utils/cli-handler.d.ts.map +1 -0
  145. package/dist/src/utils/cli-handler.js +102 -0
  146. package/dist/src/utils/cli-handler.js.map +1 -0
  147. package/dist/src/utils/logger.d.ts +61 -0
  148. package/dist/src/utils/logger.d.ts.map +1 -0
  149. package/dist/src/utils/logger.js +92 -0
  150. package/dist/src/utils/logger.js.map +1 -0
  151. package/dist/src/utils/page-utils.d.ts +54 -0
  152. package/dist/src/utils/page-utils.d.ts.map +1 -0
  153. package/dist/src/utils/page-utils.js +381 -0
  154. package/dist/src/utils/page-utils.js.map +1 -0
  155. package/dist/src/utils/rate-limit-handler.d.ts +42 -0
  156. package/dist/src/utils/rate-limit-handler.d.ts.map +1 -0
  157. package/dist/src/utils/rate-limit-handler.js +88 -0
  158. package/dist/src/utils/rate-limit-handler.js.map +1 -0
  159. package/dist/src/utils/rate-limit-handler.test.d.ts +7 -0
  160. package/dist/src/utils/rate-limit-handler.test.d.ts.map +1 -0
  161. package/dist/src/utils/rate-limit-handler.test.js +86 -0
  162. package/dist/src/utils/rate-limit-handler.test.js.map +1 -0
  163. package/dist/src/utils/settings-manager.d.ts +37 -0
  164. package/dist/src/utils/settings-manager.d.ts.map +1 -0
  165. package/dist/src/utils/settings-manager.js +121 -0
  166. package/dist/src/utils/settings-manager.js.map +1 -0
  167. package/dist/src/utils/stealth-utils.d.ts +135 -0
  168. package/dist/src/utils/stealth-utils.d.ts.map +1 -0
  169. package/dist/src/utils/stealth-utils.js +396 -0
  170. package/dist/src/utils/stealth-utils.js.map +1 -0
  171. package/dist/src/utils/stealth-utils.test.d.ts +7 -0
  172. package/dist/src/utils/stealth-utils.test.d.ts.map +1 -0
  173. package/dist/src/utils/stealth-utils.test.js +72 -0
  174. package/dist/src/utils/stealth-utils.test.js.map +1 -0
  175. package/dist/tests/e2e/authenticated.spec.d.ts +2 -0
  176. package/dist/tests/e2e/authenticated.spec.d.ts.map +1 -0
  177. package/dist/tests/e2e/authenticated.spec.js +41 -0
  178. package/dist/tests/e2e/authenticated.spec.js.map +1 -0
  179. package/dist/tests/e2e/mocked.spec.d.ts +2 -0
  180. package/dist/tests/e2e/mocked.spec.d.ts.map +1 -0
  181. package/dist/tests/e2e/mocked.spec.js +32 -0
  182. package/dist/tests/e2e/mocked.spec.js.map +1 -0
  183. package/dist/tests/mocks/handlers.d.ts +4 -0
  184. package/dist/tests/mocks/handlers.d.ts.map +1 -0
  185. package/dist/tests/mocks/handlers.js +55 -0
  186. package/dist/tests/mocks/handlers.js.map +1 -0
  187. package/dist/tests/mocks/setup.d.ts +3 -0
  188. package/dist/tests/mocks/setup.d.ts.map +1 -0
  189. package/dist/tests/mocks/setup.js +77 -0
  190. package/dist/tests/mocks/setup.js.map +1 -0
  191. package/dist/tools/definitions/ask-question.d.ts +8 -0
  192. package/dist/tools/definitions/ask-question.d.ts.map +1 -0
  193. package/dist/tools/definitions/ask-question.js +211 -0
  194. package/dist/tools/definitions/ask-question.js.map +1 -0
  195. package/dist/tools/definitions/notebook-management.d.ts +3 -0
  196. package/dist/tools/definitions/notebook-management.d.ts.map +1 -0
  197. package/dist/tools/definitions/notebook-management.js +243 -0
  198. package/dist/tools/definitions/notebook-management.js.map +1 -0
  199. package/dist/tools/definitions/session-management.d.ts +3 -0
  200. package/dist/tools/definitions/session-management.d.ts.map +1 -0
  201. package/dist/tools/definitions/session-management.js +41 -0
  202. package/dist/tools/definitions/session-management.js.map +1 -0
  203. package/dist/tools/definitions/system.d.ts +3 -0
  204. package/dist/tools/definitions/system.d.ts.map +1 -0
  205. package/dist/tools/definitions/system.js +143 -0
  206. package/dist/tools/definitions/system.js.map +1 -0
  207. package/dist/tools/definitions.d.ts +12 -0
  208. package/dist/tools/definitions.d.ts.map +1 -0
  209. package/dist/tools/definitions.js +26 -0
  210. package/dist/tools/definitions.js.map +1 -0
  211. package/dist/tools/handlers.d.ts +212 -0
  212. package/dist/tools/handlers.d.ts.map +1 -0
  213. package/dist/tools/handlers.js +712 -0
  214. package/dist/tools/handlers.js.map +1 -0
  215. package/dist/tools/index.d.ts +8 -0
  216. package/dist/tools/index.d.ts.map +1 -0
  217. package/dist/tools/index.js +8 -0
  218. package/dist/tools/index.js.map +1 -0
  219. package/dist/types.d.ts +82 -0
  220. package/dist/types.d.ts.map +1 -0
  221. package/dist/types.js +5 -0
  222. package/dist/types.js.map +1 -0
  223. package/dist/utils/cleanup-manager.d.ts +133 -0
  224. package/dist/utils/cleanup-manager.d.ts.map +1 -0
  225. package/dist/utils/cleanup-manager.js +673 -0
  226. package/dist/utils/cleanup-manager.js.map +1 -0
  227. package/dist/utils/cli-handler.d.ts +16 -0
  228. package/dist/utils/cli-handler.d.ts.map +1 -0
  229. package/dist/utils/cli-handler.js +102 -0
  230. package/dist/utils/cli-handler.js.map +1 -0
  231. package/dist/utils/logger.d.ts +61 -0
  232. package/dist/utils/logger.d.ts.map +1 -0
  233. package/dist/utils/logger.js +92 -0
  234. package/dist/utils/logger.js.map +1 -0
  235. package/dist/utils/page-utils.d.ts +54 -0
  236. package/dist/utils/page-utils.d.ts.map +1 -0
  237. package/dist/utils/page-utils.js +381 -0
  238. package/dist/utils/page-utils.js.map +1 -0
  239. package/dist/utils/rate-limit-handler.d.ts +42 -0
  240. package/dist/utils/rate-limit-handler.d.ts.map +1 -0
  241. package/dist/utils/rate-limit-handler.js +88 -0
  242. package/dist/utils/rate-limit-handler.js.map +1 -0
  243. package/dist/utils/rate-limit-handler.test.d.ts +7 -0
  244. package/dist/utils/rate-limit-handler.test.d.ts.map +1 -0
  245. package/dist/utils/rate-limit-handler.test.js +91 -0
  246. package/dist/utils/rate-limit-handler.test.js.map +1 -0
  247. package/dist/utils/settings-manager.d.ts +37 -0
  248. package/dist/utils/settings-manager.d.ts.map +1 -0
  249. package/dist/utils/settings-manager.js +121 -0
  250. package/dist/utils/settings-manager.js.map +1 -0
  251. package/dist/utils/stealth-utils.d.ts +135 -0
  252. package/dist/utils/stealth-utils.d.ts.map +1 -0
  253. package/dist/utils/stealth-utils.js +396 -0
  254. package/dist/utils/stealth-utils.js.map +1 -0
  255. package/dist/utils/stealth-utils.test.d.ts +7 -0
  256. package/dist/utils/stealth-utils.test.d.ts.map +1 -0
  257. package/dist/utils/stealth-utils.test.js +72 -0
  258. package/dist/utils/stealth-utils.test.js.map +1 -0
  259. package/docs/01_configuration.md +94 -0
  260. package/docs/02_tools.md +34 -0
  261. package/docs/03_troubleshooting.md +59 -0
  262. package/docs/04_usage-guide.md +245 -0
  263. package/docs/05_project-analysis.qmd +309 -0
  264. package/docs/06_integration-analysis.html +914 -0
  265. package/docs/06_integration-analysis.qmd +255 -0
  266. package/docs/06_integration-analysis_files/libs/bootstrap/bootstrap-4f0954b6b0dd6bf39f4bb9151ba984db.min.css +12 -0
  267. package/docs/06_integration-analysis_files/libs/bootstrap/bootstrap-icons.css +2106 -0
  268. package/docs/06_integration-analysis_files/libs/bootstrap/bootstrap-icons.woff +0 -0
  269. package/docs/06_integration-analysis_files/libs/bootstrap/bootstrap.min.js +7 -0
  270. package/docs/06_integration-analysis_files/libs/clipboard/clipboard.min.js +7 -0
  271. package/docs/06_integration-analysis_files/libs/quarto-html/anchor.min.js +9 -0
  272. package/docs/06_integration-analysis_files/libs/quarto-html/axe/axe-check.js +145 -0
  273. package/docs/06_integration-analysis_files/libs/quarto-html/popper.min.js +6 -0
  274. package/docs/06_integration-analysis_files/libs/quarto-html/quarto-syntax-highlighting-587c61ba64f3a5504c4d52d930310e48.css +236 -0
  275. package/docs/06_integration-analysis_files/libs/quarto-html/quarto.js +847 -0
  276. package/docs/06_integration-analysis_files/libs/quarto-html/tabsets/tabsets.js +95 -0
  277. package/docs/06_integration-analysis_files/libs/quarto-html/tippy.css +1 -0
  278. package/docs/06_integration-analysis_files/libs/quarto-html/tippy.umd.min.js +2 -0
  279. package/docs/07_e2e-testing-safety.qmd +754 -0
  280. package/docs/08_project-re-evaluation.html +609 -0
  281. package/docs/08_project-re-evaluation.qmd +86 -0
  282. package/docs/08_project-re-evaluation_files/libs/bootstrap/bootstrap-4f0954b6b0dd6bf39f4bb9151ba984db.min.css +12 -0
  283. package/docs/08_project-re-evaluation_files/libs/bootstrap/bootstrap-icons.css +2106 -0
  284. package/docs/08_project-re-evaluation_files/libs/bootstrap/bootstrap-icons.woff +0 -0
  285. package/docs/08_project-re-evaluation_files/libs/bootstrap/bootstrap.min.js +7 -0
  286. package/docs/08_project-re-evaluation_files/libs/clipboard/clipboard.min.js +7 -0
  287. package/docs/08_project-re-evaluation_files/libs/quarto-html/anchor.min.js +9 -0
  288. package/docs/08_project-re-evaluation_files/libs/quarto-html/axe/axe-check.js +145 -0
  289. package/docs/08_project-re-evaluation_files/libs/quarto-html/popper.min.js +6 -0
  290. package/docs/08_project-re-evaluation_files/libs/quarto-html/quarto-syntax-highlighting-587c61ba64f3a5504c4d52d930310e48.css +236 -0
  291. package/docs/08_project-re-evaluation_files/libs/quarto-html/quarto.js +847 -0
  292. package/docs/08_project-re-evaluation_files/libs/quarto-html/tabsets/tabsets.js +95 -0
  293. package/docs/08_project-re-evaluation_files/libs/quarto-html/tippy.css +1 -0
  294. package/docs/08_project-re-evaluation_files/libs/quarto-html/tippy.umd.min.js +2 -0
  295. package/docs/notebooklm-mcp-usage.html +704 -0
  296. package/docs/notebooklm-mcp-usage.qmd +119 -0
  297. package/docs/notebooklm-mcp-usage_files/libs/bootstrap/bootstrap-6b71f2156b6a5230c6677325978bcf08.min.css +12 -0
  298. package/docs/notebooklm-mcp-usage_files/libs/bootstrap/bootstrap-icons.css +2106 -0
  299. package/docs/notebooklm-mcp-usage_files/libs/bootstrap/bootstrap-icons.woff +0 -0
  300. package/docs/notebooklm-mcp-usage_files/libs/bootstrap/bootstrap.min.js +7 -0
  301. package/docs/notebooklm-mcp-usage_files/libs/clipboard/clipboard.min.js +7 -0
  302. package/docs/notebooklm-mcp-usage_files/libs/quarto-html/anchor.min.js +9 -0
  303. package/docs/notebooklm-mcp-usage_files/libs/quarto-html/axe/axe-check.js +145 -0
  304. package/docs/notebooklm-mcp-usage_files/libs/quarto-html/popper.min.js +6 -0
  305. package/docs/notebooklm-mcp-usage_files/libs/quarto-html/quarto-syntax-highlighting-587c61ba64f3a5504c4d52d930310e48.css +236 -0
  306. package/docs/notebooklm-mcp-usage_files/libs/quarto-html/quarto.js +847 -0
  307. package/docs/notebooklm-mcp-usage_files/libs/quarto-html/tabsets/tabsets.js +95 -0
  308. package/docs/notebooklm-mcp-usage_files/libs/quarto-html/tippy.css +1 -0
  309. package/docs/notebooklm-mcp-usage_files/libs/quarto-html/tippy.umd.min.js +2 -0
  310. package/docs/repomix-usage_files/libs/bootstrap/bootstrap-6b71f2156b6a5230c6677325978bcf08.min.css +12 -0
  311. package/docs/repomix-usage_files/libs/bootstrap/bootstrap-icons.css +2106 -0
  312. package/docs/repomix-usage_files/libs/bootstrap/bootstrap-icons.woff +0 -0
  313. package/docs/repomix-usage_files/libs/bootstrap/bootstrap.min.js +7 -0
  314. package/docs/repomix-usage_files/libs/clipboard/clipboard.min.js +7 -0
  315. package/docs/repomix-usage_files/libs/quarto-html/anchor.min.js +9 -0
  316. package/docs/repomix-usage_files/libs/quarto-html/axe/axe-check.js +145 -0
  317. package/docs/repomix-usage_files/libs/quarto-html/popper.min.js +6 -0
  318. package/docs/repomix-usage_files/libs/quarto-html/quarto-syntax-highlighting-587c61ba64f3a5504c4d52d930310e48.css +236 -0
  319. package/docs/repomix-usage_files/libs/quarto-html/quarto.js +847 -0
  320. package/docs/repomix-usage_files/libs/quarto-html/tabsets/tabsets.js +95 -0
  321. package/docs/repomix-usage_files/libs/quarto-html/tippy.css +1 -0
  322. package/docs/repomix-usage_files/libs/quarto-html/tippy.umd.min.js +2 -0
  323. package/package.json +62 -0
@@ -0,0 +1,381 @@
1
+ /**
2
+ * Page utilities for extracting responses from NotebookLM web UI
3
+ *
4
+ * This module provides functions to:
5
+ * - Extract latest assistant responses from the page
6
+ * - Wait for new responses with streaming detection
7
+ * - Detect placeholders and loading states
8
+ * - Snapshot existing responses for comparison
9
+ *
10
+ * Based on the Python implementation from page_utils.py
11
+ */
12
+ import { log } from "./logger.js";
13
+ import { RESPONSE_SELECTORS, THINKING_SELECTOR } from "../constants.js";
14
+ // ============================================================================
15
+ // Helper Functions
16
+ // ============================================================================
17
+ /**
18
+ * Simple string hash function (for efficient comparison)
19
+ */
20
+ function hashString(str) {
21
+ let hash = 0;
22
+ for (let i = 0; i < str.length; i++) {
23
+ const char = str.charCodeAt(i);
24
+ hash = (hash << 5) - hash + char;
25
+ hash = hash & hash; // Convert to 32bit integer
26
+ }
27
+ return hash;
28
+ }
29
+ // ============================================================================
30
+ // Main Functions
31
+ // ============================================================================
32
+ /**
33
+ * Snapshot the latest response text currently visible
34
+ * Returns null if no response found
35
+ */
36
+ export async function snapshotLatestResponse(page) {
37
+ return await extractLatestText(page, new Set(), false, 0);
38
+ }
39
+ /**
40
+ * Snapshot ALL existing assistant response texts
41
+ * Used to capture visible responses BEFORE submitting a new question
42
+ */
43
+ export async function snapshotAllResponses(page) {
44
+ const allTexts = [];
45
+ const primarySelector = ".to-user-container";
46
+ try {
47
+ const containers = await page.$$(primarySelector);
48
+ if (containers.length > 0) {
49
+ for (const container of containers) {
50
+ try {
51
+ const textElement = await container.$(".message-text-content");
52
+ if (textElement) {
53
+ const text = await textElement.innerText();
54
+ if (text && text.trim()) {
55
+ allTexts.push(text.trim());
56
+ }
57
+ }
58
+ }
59
+ catch {
60
+ continue;
61
+ }
62
+ }
63
+ log.info(`📸 [SNAPSHOT] Captured ${allTexts.length} existing responses`);
64
+ }
65
+ }
66
+ catch (error) {
67
+ log.warning(`⚠️ [SNAPSHOT] Failed to snapshot responses: ${error}`);
68
+ }
69
+ return allTexts;
70
+ }
71
+ /**
72
+ * Count the number of visible assistant response elements
73
+ */
74
+ export async function countResponseElements(page) {
75
+ let count = 0;
76
+ for (const selector of RESPONSE_SELECTORS) {
77
+ try {
78
+ const elements = await page.$$(selector);
79
+ if (elements.length > 0) {
80
+ // Count only visible elements
81
+ for (const el of elements) {
82
+ try {
83
+ const isVisible = await el.isVisible();
84
+ if (isVisible) {
85
+ count++;
86
+ }
87
+ }
88
+ catch {
89
+ continue;
90
+ }
91
+ }
92
+ // If we found elements with this selector, stop trying others
93
+ if (count > 0) {
94
+ break;
95
+ }
96
+ }
97
+ }
98
+ catch {
99
+ continue;
100
+ }
101
+ }
102
+ return count;
103
+ }
104
+ /**
105
+ * Wait for a new assistant response with streaming detection
106
+ *
107
+ * This function:
108
+ * 1. Polls the page for new response text
109
+ * 2. Detects streaming (text changes) vs. complete (text stable)
110
+ * 3. Requires text to be stable for 3 consecutive polls before returning
111
+ * 4. Ignores placeholders, question echoes, and known responses
112
+ *
113
+ * @param page Playwright page instance
114
+ * @param options Options for waiting
115
+ * @returns The new response text, or null if timeout
116
+ */
117
+ export async function waitForLatestAnswer(page, options = {}) {
118
+ const { question = "", timeoutMs = 120000, pollIntervalMs = 1000, ignoreTexts = [], debug = false, } = options;
119
+ const deadline = Date.now() + timeoutMs;
120
+ const sanitizedQuestion = question.trim().toLowerCase();
121
+ // Track ALL known texts as HASHES (memory efficient!)
122
+ const knownHashes = new Set();
123
+ for (const text of ignoreTexts) {
124
+ if (typeof text === "string" && text.trim()) {
125
+ knownHashes.add(hashString(text.trim()));
126
+ }
127
+ }
128
+ if (debug) {
129
+ log.debug(`🔍 [DEBUG] Waiting for NEW answer. Ignoring ${knownHashes.size} known responses`);
130
+ }
131
+ let pollCount = 0;
132
+ let lastCandidate = null;
133
+ let stableCount = 0; // Track how many times we see the same text
134
+ const requiredStablePolls = 3; // Text must be stable for 3 consecutive polls
135
+ while (Date.now() < deadline) {
136
+ pollCount++;
137
+ // Check if NotebookLM is still "thinking" (most reliable indicator)
138
+ try {
139
+ const thinkingElement = await page.$(THINKING_SELECTOR);
140
+ if (thinkingElement) {
141
+ const isVisible = await thinkingElement.isVisible();
142
+ if (isVisible) {
143
+ if (debug && pollCount % 5 === 0) {
144
+ log.debug(`🔍 [DEBUG] NotebookLM still thinking (${THINKING_SELECTOR} visible)...`);
145
+ }
146
+ await page.waitForTimeout(pollIntervalMs);
147
+ continue;
148
+ }
149
+ }
150
+ }
151
+ catch {
152
+ // Ignore errors checking thinking state
153
+ }
154
+ // Extract latest NEW text
155
+ const candidate = await extractLatestText(page, knownHashes, debug, pollCount);
156
+ if (candidate) {
157
+ const normalized = candidate.trim();
158
+ if (normalized) {
159
+ const lower = normalized.toLowerCase();
160
+ // Check if it's the question echo
161
+ if (lower === sanitizedQuestion) {
162
+ if (debug) {
163
+ log.debug("🔍 [DEBUG] Found question echo, ignoring");
164
+ }
165
+ knownHashes.add(hashString(normalized)); // Mark as seen
166
+ await page.waitForTimeout(pollIntervalMs);
167
+ continue;
168
+ }
169
+ // ========================================
170
+ // STREAMING DETECTION: Check if text is stable
171
+ // ========================================
172
+ if (normalized === lastCandidate) {
173
+ // Text hasn't changed - it's stable
174
+ stableCount++;
175
+ if (debug && stableCount === requiredStablePolls) {
176
+ log.debug(`✅ [DEBUG] Text stable for ${stableCount} polls (${normalized.length} chars)`);
177
+ }
178
+ }
179
+ else {
180
+ // Text changed - streaming in progress
181
+ if (debug && lastCandidate) {
182
+ log.debug(`🔄 [DEBUG] Text changed (${normalized.length} chars, was ${lastCandidate.length})`);
183
+ }
184
+ stableCount = 1;
185
+ lastCandidate = normalized;
186
+ }
187
+ // Only return once text is stable
188
+ if (stableCount >= requiredStablePolls) {
189
+ if (debug) {
190
+ log.debug(`✅ [DEBUG] Returning stable answer (${normalized.length} chars)`);
191
+ }
192
+ return normalized;
193
+ }
194
+ }
195
+ }
196
+ await page.waitForTimeout(pollIntervalMs);
197
+ }
198
+ if (debug) {
199
+ log.debug(`⏱️ [DEBUG] Timeout after ${pollCount} polls`);
200
+ }
201
+ return null;
202
+ }
203
+ /**
204
+ * Extract the latest NEW response text from the page
205
+ * Uses hash-based comparison for efficiency
206
+ *
207
+ * @param page Playwright page instance
208
+ * @param knownHashes Set of hashes of already-seen response texts
209
+ * @param debug Enable debug logging
210
+ * @param pollCount Current poll number (for conditional logging)
211
+ * @returns First NEW response text found, or null
212
+ */
213
+ async function extractLatestText(page, knownHashes, debug, pollCount) {
214
+ // Try the primary selector first (most specific for NotebookLM)
215
+ const primarySelector = ".to-user-container";
216
+ try {
217
+ const containers = await page.$$(primarySelector);
218
+ const totalContainers = containers.length;
219
+ // Early exit if no new containers possible
220
+ if (totalContainers <= knownHashes.size) {
221
+ if (debug && pollCount % 5 === 0) {
222
+ log.dim(`⏭️ [EXTRACT] No new containers (${totalContainers} total, ${knownHashes.size} known)`);
223
+ }
224
+ return null;
225
+ }
226
+ if (containers.length > 0) {
227
+ // Only log every 5th poll to reduce noise
228
+ if (debug && pollCount % 5 === 0) {
229
+ log.dim(`🔍 [EXTRACT] Scanning ${totalContainers} containers (${knownHashes.size} known)`);
230
+ }
231
+ let skipped = 0;
232
+ let empty = 0;
233
+ // Scan ALL containers to find the FIRST with NEW text
234
+ for (let idx = 0; idx < containers.length; idx++) {
235
+ const container = containers[idx];
236
+ try {
237
+ const textElement = await container.$(".message-text-content");
238
+ if (textElement) {
239
+ const text = await textElement.innerText();
240
+ if (text && text.trim()) {
241
+ // Hash-based comparison (faster & less memory)
242
+ const textHash = hashString(text.trim());
243
+ if (!knownHashes.has(textHash)) {
244
+ log.success(`✅ [EXTRACT] Found NEW text in container[${idx}]: ${text.trim().length} chars`);
245
+ return text.trim();
246
+ }
247
+ else {
248
+ skipped++;
249
+ }
250
+ }
251
+ else {
252
+ empty++;
253
+ }
254
+ }
255
+ }
256
+ catch {
257
+ continue;
258
+ }
259
+ }
260
+ // Only log summary if debug enabled
261
+ if (debug && pollCount % 5 === 0) {
262
+ log.dim(`⏭️ [EXTRACT] No NEW text (skipped ${skipped} known, ${empty} empty)`);
263
+ }
264
+ return null; // Don't fall through to fallback!
265
+ }
266
+ else {
267
+ if (debug) {
268
+ log.warning("⚠️ [EXTRACT] No containers found");
269
+ }
270
+ }
271
+ }
272
+ catch (error) {
273
+ log.error(`❌ [EXTRACT] Primary selector failed: ${error}`);
274
+ }
275
+ // Fallback: Try other selectors (only if primary selector failed/found nothing)
276
+ if (debug) {
277
+ log.dim("🔄 [EXTRACT] Trying fallback selectors...");
278
+ }
279
+ for (const selector of RESPONSE_SELECTORS) {
280
+ try {
281
+ const elements = await page.$$(selector);
282
+ if (elements.length === 0)
283
+ continue;
284
+ // Scan ALL elements to find the first with NEW text
285
+ for (const element of elements) {
286
+ try {
287
+ // Prefer full container text when available
288
+ let container = element;
289
+ try {
290
+ const closest = await element.evaluateHandle((el) => {
291
+ return el.closest("[data-message-author], [data-message-role], [data-author], " +
292
+ "[data-testid*='assistant'], [data-automation-id*='response'], article, section");
293
+ });
294
+ if (closest) {
295
+ const closestElement = closest.asElement();
296
+ if (closestElement) {
297
+ container = closestElement;
298
+ }
299
+ }
300
+ }
301
+ catch {
302
+ container = element;
303
+ }
304
+ const text = await container.innerText();
305
+ if (text && text.trim() && !knownHashes.has(hashString(text.trim()))) {
306
+ return text.trim();
307
+ }
308
+ }
309
+ catch {
310
+ continue;
311
+ }
312
+ }
313
+ }
314
+ catch {
315
+ continue;
316
+ }
317
+ }
318
+ // Final fallback: JavaScript evaluation
319
+ try {
320
+ const fallbackText = await page.evaluate(() => {
321
+ const unique = new Set();
322
+ const isVisible = (el) => {
323
+ if (!el || !el.isConnected)
324
+ return false;
325
+ const rect = el.getBoundingClientRect();
326
+ if (rect.width === 0 || rect.height === 0)
327
+ return false;
328
+ const style = window.getComputedStyle(el);
329
+ if (style.visibility === "hidden" ||
330
+ style.display === "none" ||
331
+ parseFloat(style.opacity || "1") === 0) {
332
+ return false;
333
+ }
334
+ return true;
335
+ };
336
+ const selectors = [
337
+ "[data-message-author]",
338
+ "[data-message-role]",
339
+ "[data-author]",
340
+ "[data-renderer*='assistant']",
341
+ "[data-testid*='assistant']",
342
+ "[data-automation-id*='response']",
343
+ ];
344
+ const candidates = [];
345
+ for (const selector of selectors) {
346
+ for (const el of Array.from(document.querySelectorAll(selector))) {
347
+ if (!isVisible(el))
348
+ continue;
349
+ if (unique.has(el))
350
+ continue;
351
+ unique.add(el);
352
+ const text = el.innerText || el.textContent || "";
353
+ if (!text.trim())
354
+ continue;
355
+ candidates.push(text.trim());
356
+ }
357
+ }
358
+ if (candidates.length > 0) {
359
+ return candidates[candidates.length - 1];
360
+ }
361
+ return null;
362
+ });
363
+ if (typeof fallbackText === "string" && fallbackText.trim()) {
364
+ return fallbackText.trim();
365
+ }
366
+ }
367
+ catch {
368
+ // Ignore evaluation errors
369
+ }
370
+ return null;
371
+ }
372
+ // ============================================================================
373
+ // Exports
374
+ // ============================================================================
375
+ export default {
376
+ snapshotLatestResponse,
377
+ snapshotAllResponses,
378
+ countResponseElements,
379
+ waitForLatestAnswer,
380
+ };
381
+ //# sourceMappingURL=page-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-utils.js","sourceRoot":"","sources":["../../src/utils/page-utils.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Rate Limit Handler
3
+ *
4
+ * Provides automatic retry logic with exponential backoff for rate-limited operations.
5
+ * NotebookLM free accounts have a 50 queries/day limit.
6
+ *
7
+ * Features:
8
+ * - Configurable max retries (default: 3)
9
+ * - Exponential backoff with jitter
10
+ * - RateLimitError-specific handling
11
+ */
12
+ export interface RetryOptions {
13
+ maxRetries?: number;
14
+ baseDelayMs?: number;
15
+ maxDelayMs?: number;
16
+ onRetry?: (attempt: number, delay: number, error: Error) => void;
17
+ }
18
+ /**
19
+ * Rate Limit Handler with exponential backoff
20
+ */
21
+ export declare class RateLimitHandler {
22
+ private options;
23
+ constructor(options?: RetryOptions);
24
+ /**
25
+ * Execute a function with automatic retry on RateLimitError
26
+ *
27
+ * @param fn - Async function to execute
28
+ * @returns Result of the function
29
+ * @throws RateLimitError if all retries exhausted
30
+ */
31
+ executeWithRetry<T>(fn: () => Promise<T>): Promise<T>;
32
+ /**
33
+ * Calculate delay using exponential backoff with jitter
34
+ */
35
+ private calculateDelay;
36
+ }
37
+ /**
38
+ * Convenience function to execute with retry using default options
39
+ */
40
+ export declare function withRateLimitRetry<T>(fn: () => Promise<T>, options?: RetryOptions): Promise<T>;
41
+ export default RateLimitHandler;
42
+ //# sourceMappingURL=rate-limit-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limit-handler.d.ts","sourceRoot":"","sources":["../../src/utils/rate-limit-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAMH,MAAM,WAAW,YAAY;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CACpE;AAQD;;GAEG;AACH,qBAAa,gBAAgB;IACzB,OAAO,CAAC,OAAO,CAA0E;gBAE7E,OAAO,GAAE,YAAiB;IAOtC;;;;;;OAMG;IACG,gBAAgB,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAwC3D;;OAEG;IACH,OAAO,CAAC,cAAc;CAUzB;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,CAAC,EACtC,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,OAAO,CAAC,EAAE,YAAY,GACvB,OAAO,CAAC,CAAC,CAAC,CAGZ;AAED,eAAe,gBAAgB,CAAC"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Rate Limit Handler
3
+ *
4
+ * Provides automatic retry logic with exponential backoff for rate-limited operations.
5
+ * NotebookLM free accounts have a 50 queries/day limit.
6
+ *
7
+ * Features:
8
+ * - Configurable max retries (default: 3)
9
+ * - Exponential backoff with jitter
10
+ * - RateLimitError-specific handling
11
+ */
12
+ import { RateLimitError } from "../errors.js";
13
+ import { log } from "./logger.js";
14
+ import { sleep } from "./stealth-utils.js";
15
+ const DEFAULT_OPTIONS = {
16
+ maxRetries: 3,
17
+ baseDelayMs: 1000,
18
+ maxDelayMs: 30000,
19
+ };
20
+ /**
21
+ * Rate Limit Handler with exponential backoff
22
+ */
23
+ export class RateLimitHandler {
24
+ options;
25
+ constructor(options = {}) {
26
+ this.options = {
27
+ ...DEFAULT_OPTIONS,
28
+ ...options,
29
+ };
30
+ }
31
+ /**
32
+ * Execute a function with automatic retry on RateLimitError
33
+ *
34
+ * @param fn - Async function to execute
35
+ * @returns Result of the function
36
+ * @throws RateLimitError if all retries exhausted
37
+ */
38
+ async executeWithRetry(fn) {
39
+ let lastError = null;
40
+ for (let attempt = 0; attempt <= this.options.maxRetries; attempt++) {
41
+ try {
42
+ return await fn();
43
+ }
44
+ catch (error) {
45
+ if (!(error instanceof RateLimitError)) {
46
+ // Non-rate-limit errors are thrown immediately
47
+ throw error;
48
+ }
49
+ lastError = error;
50
+ if (attempt === this.options.maxRetries) {
51
+ // All retries exhausted
52
+ log.error(`❌ Rate limit: All ${this.options.maxRetries} retries exhausted`);
53
+ break;
54
+ }
55
+ // Calculate delay with exponential backoff + jitter
56
+ const delay = this.calculateDelay(attempt);
57
+ log.warning(`⚠️ Rate limit hit (attempt ${attempt + 1}/${this.options.maxRetries + 1}). ` +
58
+ `Retrying in ${Math.round(delay / 1000)}s...`);
59
+ // Call optional retry callback
60
+ if (this.options.onRetry) {
61
+ this.options.onRetry(attempt + 1, delay, error);
62
+ }
63
+ await sleep(delay);
64
+ }
65
+ }
66
+ throw lastError ?? new RateLimitError("Rate limit exceeded after all retries");
67
+ }
68
+ /**
69
+ * Calculate delay using exponential backoff with jitter
70
+ */
71
+ calculateDelay(attempt) {
72
+ // Exponential: baseDelay * 2^attempt
73
+ const exponentialDelay = this.options.baseDelayMs * Math.pow(2, attempt);
74
+ // Add random jitter (±25% of delay)
75
+ const jitter = exponentialDelay * 0.25 * (Math.random() * 2 - 1);
76
+ // Clamp to max delay
77
+ return Math.min(exponentialDelay + jitter, this.options.maxDelayMs);
78
+ }
79
+ }
80
+ /**
81
+ * Convenience function to execute with retry using default options
82
+ */
83
+ export async function withRateLimitRetry(fn, options) {
84
+ const handler = new RateLimitHandler(options);
85
+ return handler.executeWithRetry(fn);
86
+ }
87
+ export default RateLimitHandler;
88
+ //# sourceMappingURL=rate-limit-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limit-handler.js","sourceRoot":"","sources":["../../src/utils/rate-limit-handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Tests for rate-limit-handler.ts
3
+ *
4
+ * Unit tests for rate limit retry logic with exponential backoff.
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=rate-limit-handler.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limit-handler.test.d.ts","sourceRoot":"","sources":["../../src/utils/rate-limit-handler.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Tests for rate-limit-handler.ts
3
+ *
4
+ * Unit tests for rate limit retry logic with exponential backoff.
5
+ */
6
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
7
+ import { RateLimitHandler, withRateLimitRetry } from './rate-limit-handler.js';
8
+ import { RateLimitError } from '../errors.js';
9
+ describe('RateLimitHandler', () => {
10
+ beforeEach(() => {
11
+ vi.useFakeTimers();
12
+ });
13
+ afterEach(() => {
14
+ vi.useRealTimers();
15
+ });
16
+ describe('executeWithRetry', () => {
17
+ it('should execute function successfully on first attempt', async () => {
18
+ const handler = new RateLimitHandler();
19
+ const fn = vi.fn().mockResolvedValue('success');
20
+ const resultPromise = handler.executeWithRetry(fn);
21
+ await vi.runAllTimersAsync();
22
+ const result = await resultPromise;
23
+ expect(result).toBe('success');
24
+ expect(fn).toHaveBeenCalledTimes(1);
25
+ });
26
+ it('should retry on RateLimitError and succeed', async () => {
27
+ const handler = new RateLimitHandler({ maxRetries: 3, baseDelayMs: 100 });
28
+ const fn = vi.fn()
29
+ .mockRejectedValueOnce(new RateLimitError())
30
+ .mockResolvedValue('success after retry');
31
+ const resultPromise = handler.executeWithRetry(fn);
32
+ await vi.runAllTimersAsync();
33
+ const result = await resultPromise;
34
+ expect(result).toBe('success after retry');
35
+ expect(fn).toHaveBeenCalledTimes(2);
36
+ });
37
+ it('should throw non-RateLimitError immediately', async () => {
38
+ const handler = new RateLimitHandler();
39
+ const error = new Error('Other error');
40
+ const fn = vi.fn().mockRejectedValue(error);
41
+ await expect(handler.executeWithRetry(fn)).rejects.toThrow('Other error');
42
+ expect(fn).toHaveBeenCalledTimes(1);
43
+ });
44
+ it('should throw RateLimitError after all retries exhausted', async () => {
45
+ const handler = new RateLimitHandler({ maxRetries: 2, baseDelayMs: 100 });
46
+ const fn = vi.fn().mockRejectedValue(new RateLimitError());
47
+ const resultPromise = handler.executeWithRetry(fn);
48
+ // Run all timers and expect rejection
49
+ await vi.runAllTimersAsync();
50
+ // Awaiting the promise rejection properly
51
+ let caughtError = null;
52
+ try {
53
+ await resultPromise;
54
+ }
55
+ catch (e) {
56
+ caughtError = e;
57
+ }
58
+ expect(caughtError).toBeInstanceOf(RateLimitError);
59
+ // Initial attempt + 2 retries = 3 calls
60
+ expect(fn).toHaveBeenCalledTimes(3);
61
+ });
62
+ it('should call onRetry callback on each retry', async () => {
63
+ const onRetry = vi.fn();
64
+ const handler = new RateLimitHandler({
65
+ maxRetries: 2,
66
+ baseDelayMs: 100,
67
+ onRetry,
68
+ });
69
+ const fn = vi.fn()
70
+ .mockRejectedValueOnce(new RateLimitError())
71
+ .mockRejectedValueOnce(new RateLimitError())
72
+ .mockResolvedValue('success');
73
+ const resultPromise = handler.executeWithRetry(fn);
74
+ await vi.runAllTimersAsync();
75
+ await resultPromise;
76
+ expect(onRetry).toHaveBeenCalledTimes(2);
77
+ expect(onRetry).toHaveBeenCalledWith(1, expect.any(Number), expect.any(RateLimitError));
78
+ expect(onRetry).toHaveBeenCalledWith(2, expect.any(Number), expect.any(RateLimitError));
79
+ });
80
+ });
81
+ describe('withRateLimitRetry convenience function', () => {
82
+ it('should work with default options', async () => {
83
+ const fn = vi.fn().mockResolvedValue('success');
84
+ const resultPromise = withRateLimitRetry(fn);
85
+ await vi.runAllTimersAsync();
86
+ const result = await resultPromise;
87
+ expect(result).toBe('success');
88
+ });
89
+ });
90
+ });
91
+ //# sourceMappingURL=rate-limit-handler.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limit-handler.test.js","sourceRoot":"","sources":["../../src/utils/rate-limit-handler.test.ts"],"names":[],"mappings":"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"}