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.
- package/LICENSE +21 -0
- package/README.md +456 -0
- package/dist/auth/auth-manager.d.ts +139 -0
- package/dist/auth/auth-manager.d.ts.map +1 -0
- package/dist/auth/auth-manager.js +960 -0
- package/dist/auth/auth-manager.js.map +1 -0
- package/dist/config.d.ts +92 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +219 -0
- package/dist/config.js.map +1 -0
- package/dist/constants.d.ts +58 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +133 -0
- package/dist/constants.js.map +1 -0
- package/dist/errors.d.ts +26 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +41 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +325 -0
- package/dist/index.js.map +1 -0
- package/dist/library/notebook-library.d.ts +70 -0
- package/dist/library/notebook-library.d.ts.map +1 -0
- package/dist/library/notebook-library.js +279 -0
- package/dist/library/notebook-library.js.map +1 -0
- package/dist/library/types.d.ts +67 -0
- package/dist/library/types.d.ts.map +1 -0
- package/dist/library/types.js +8 -0
- package/dist/library/types.js.map +1 -0
- package/dist/playwright.config.d.ts +3 -0
- package/dist/playwright.config.d.ts.map +1 -0
- package/dist/playwright.config.js +38 -0
- package/dist/playwright.config.js.map +1 -0
- package/dist/resources/resource-handlers.d.ts +22 -0
- package/dist/resources/resource-handlers.d.ts.map +1 -0
- package/dist/resources/resource-handlers.js +216 -0
- package/dist/resources/resource-handlers.js.map +1 -0
- package/dist/scripts/save-auth-state.d.ts +2 -0
- package/dist/scripts/save-auth-state.d.ts.map +1 -0
- package/dist/scripts/save-auth-state.js +91 -0
- package/dist/scripts/save-auth-state.js.map +1 -0
- package/dist/session/browser-session.d.ts +108 -0
- package/dist/session/browser-session.d.ts.map +1 -0
- package/dist/session/browser-session.js +636 -0
- package/dist/session/browser-session.js.map +1 -0
- package/dist/session/session-manager.d.ts +76 -0
- package/dist/session/session-manager.d.ts.map +1 -0
- package/dist/session/session-manager.js +273 -0
- package/dist/session/session-manager.js.map +1 -0
- package/dist/session/shared-context-manager.d.ts +107 -0
- package/dist/session/shared-context-manager.d.ts.map +1 -0
- package/dist/session/shared-context-manager.js +447 -0
- package/dist/session/shared-context-manager.js.map +1 -0
- package/dist/src/auth/auth-manager.d.ts +139 -0
- package/dist/src/auth/auth-manager.d.ts.map +1 -0
- package/dist/src/auth/auth-manager.js +960 -0
- package/dist/src/auth/auth-manager.js.map +1 -0
- package/dist/src/config.d.ts +92 -0
- package/dist/src/config.d.ts.map +1 -0
- package/dist/src/config.js +219 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/constants.d.ts +58 -0
- package/dist/src/constants.d.ts.map +1 -0
- package/dist/src/constants.js +133 -0
- package/dist/src/constants.js.map +1 -0
- package/dist/src/errors.d.ts +26 -0
- package/dist/src/errors.d.ts.map +1 -0
- package/dist/src/errors.js +41 -0
- package/dist/src/errors.js.map +1 -0
- package/dist/src/index.d.ts +32 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +325 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/library/notebook-library.d.ts +70 -0
- package/dist/src/library/notebook-library.d.ts.map +1 -0
- package/dist/src/library/notebook-library.js +279 -0
- package/dist/src/library/notebook-library.js.map +1 -0
- package/dist/src/library/types.d.ts +67 -0
- package/dist/src/library/types.d.ts.map +1 -0
- package/dist/src/library/types.js +8 -0
- package/dist/src/library/types.js.map +1 -0
- package/dist/src/resources/resource-handlers.d.ts +22 -0
- package/dist/src/resources/resource-handlers.d.ts.map +1 -0
- package/dist/src/resources/resource-handlers.js +216 -0
- package/dist/src/resources/resource-handlers.js.map +1 -0
- package/dist/src/scripts/health-check.d.ts +13 -0
- package/dist/src/scripts/health-check.d.ts.map +1 -0
- package/dist/src/scripts/health-check.js +100 -0
- package/dist/src/scripts/health-check.js.map +1 -0
- package/dist/src/session/browser-session.d.ts +108 -0
- package/dist/src/session/browser-session.d.ts.map +1 -0
- package/dist/src/session/browser-session.js +642 -0
- package/dist/src/session/browser-session.js.map +1 -0
- package/dist/src/session/session-manager.d.ts +76 -0
- package/dist/src/session/session-manager.d.ts.map +1 -0
- package/dist/src/session/session-manager.js +273 -0
- package/dist/src/session/session-manager.js.map +1 -0
- package/dist/src/session/shared-context-manager.d.ts +107 -0
- package/dist/src/session/shared-context-manager.d.ts.map +1 -0
- package/dist/src/session/shared-context-manager.js +447 -0
- package/dist/src/session/shared-context-manager.js.map +1 -0
- package/dist/src/tools/definitions/ask-question.d.ts +8 -0
- package/dist/src/tools/definitions/ask-question.d.ts.map +1 -0
- package/dist/src/tools/definitions/ask-question.js +211 -0
- package/dist/src/tools/definitions/ask-question.js.map +1 -0
- package/dist/src/tools/definitions/notebook-management.d.ts +3 -0
- package/dist/src/tools/definitions/notebook-management.d.ts.map +1 -0
- package/dist/src/tools/definitions/notebook-management.js +243 -0
- package/dist/src/tools/definitions/notebook-management.js.map +1 -0
- package/dist/src/tools/definitions/session-management.d.ts +3 -0
- package/dist/src/tools/definitions/session-management.d.ts.map +1 -0
- package/dist/src/tools/definitions/session-management.js +41 -0
- package/dist/src/tools/definitions/session-management.js.map +1 -0
- package/dist/src/tools/definitions/system.d.ts +3 -0
- package/dist/src/tools/definitions/system.d.ts.map +1 -0
- package/dist/src/tools/definitions/system.js +143 -0
- package/dist/src/tools/definitions/system.js.map +1 -0
- package/dist/src/tools/definitions.d.ts +12 -0
- package/dist/src/tools/definitions.d.ts.map +1 -0
- package/dist/src/tools/definitions.js +26 -0
- package/dist/src/tools/definitions.js.map +1 -0
- package/dist/src/tools/handlers.d.ts +212 -0
- package/dist/src/tools/handlers.d.ts.map +1 -0
- package/dist/src/tools/handlers.js +712 -0
- package/dist/src/tools/handlers.js.map +1 -0
- package/dist/src/tools/index.d.ts +8 -0
- package/dist/src/tools/index.d.ts.map +1 -0
- package/dist/src/tools/index.js +8 -0
- package/dist/src/tools/index.js.map +1 -0
- package/dist/src/types.d.ts +88 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +5 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/utils/auth-manager.d.ts +2 -0
- package/dist/src/utils/auth-manager.d.ts.map +1 -0
- package/dist/src/utils/auth-manager.js +25 -0
- package/dist/src/utils/auth-manager.js.map +1 -0
- package/dist/src/utils/cleanup-manager.d.ts +133 -0
- package/dist/src/utils/cleanup-manager.d.ts.map +1 -0
- package/dist/src/utils/cleanup-manager.js +673 -0
- package/dist/src/utils/cleanup-manager.js.map +1 -0
- package/dist/src/utils/cli-handler.d.ts +16 -0
- package/dist/src/utils/cli-handler.d.ts.map +1 -0
- package/dist/src/utils/cli-handler.js +102 -0
- package/dist/src/utils/cli-handler.js.map +1 -0
- package/dist/src/utils/logger.d.ts +61 -0
- package/dist/src/utils/logger.d.ts.map +1 -0
- package/dist/src/utils/logger.js +92 -0
- package/dist/src/utils/logger.js.map +1 -0
- package/dist/src/utils/page-utils.d.ts +54 -0
- package/dist/src/utils/page-utils.d.ts.map +1 -0
- package/dist/src/utils/page-utils.js +381 -0
- package/dist/src/utils/page-utils.js.map +1 -0
- package/dist/src/utils/rate-limit-handler.d.ts +42 -0
- package/dist/src/utils/rate-limit-handler.d.ts.map +1 -0
- package/dist/src/utils/rate-limit-handler.js +88 -0
- package/dist/src/utils/rate-limit-handler.js.map +1 -0
- package/dist/src/utils/rate-limit-handler.test.d.ts +7 -0
- package/dist/src/utils/rate-limit-handler.test.d.ts.map +1 -0
- package/dist/src/utils/rate-limit-handler.test.js +86 -0
- package/dist/src/utils/rate-limit-handler.test.js.map +1 -0
- package/dist/src/utils/settings-manager.d.ts +37 -0
- package/dist/src/utils/settings-manager.d.ts.map +1 -0
- package/dist/src/utils/settings-manager.js +121 -0
- package/dist/src/utils/settings-manager.js.map +1 -0
- package/dist/src/utils/stealth-utils.d.ts +135 -0
- package/dist/src/utils/stealth-utils.d.ts.map +1 -0
- package/dist/src/utils/stealth-utils.js +396 -0
- package/dist/src/utils/stealth-utils.js.map +1 -0
- package/dist/src/utils/stealth-utils.test.d.ts +7 -0
- package/dist/src/utils/stealth-utils.test.d.ts.map +1 -0
- package/dist/src/utils/stealth-utils.test.js +72 -0
- package/dist/src/utils/stealth-utils.test.js.map +1 -0
- package/dist/tests/e2e/authenticated.spec.d.ts +2 -0
- package/dist/tests/e2e/authenticated.spec.d.ts.map +1 -0
- package/dist/tests/e2e/authenticated.spec.js +41 -0
- package/dist/tests/e2e/authenticated.spec.js.map +1 -0
- package/dist/tests/e2e/mocked.spec.d.ts +2 -0
- package/dist/tests/e2e/mocked.spec.d.ts.map +1 -0
- package/dist/tests/e2e/mocked.spec.js +32 -0
- package/dist/tests/e2e/mocked.spec.js.map +1 -0
- package/dist/tests/mocks/handlers.d.ts +4 -0
- package/dist/tests/mocks/handlers.d.ts.map +1 -0
- package/dist/tests/mocks/handlers.js +55 -0
- package/dist/tests/mocks/handlers.js.map +1 -0
- package/dist/tests/mocks/setup.d.ts +3 -0
- package/dist/tests/mocks/setup.d.ts.map +1 -0
- package/dist/tests/mocks/setup.js +77 -0
- package/dist/tests/mocks/setup.js.map +1 -0
- package/dist/tools/definitions/ask-question.d.ts +8 -0
- package/dist/tools/definitions/ask-question.d.ts.map +1 -0
- package/dist/tools/definitions/ask-question.js +211 -0
- package/dist/tools/definitions/ask-question.js.map +1 -0
- package/dist/tools/definitions/notebook-management.d.ts +3 -0
- package/dist/tools/definitions/notebook-management.d.ts.map +1 -0
- package/dist/tools/definitions/notebook-management.js +243 -0
- package/dist/tools/definitions/notebook-management.js.map +1 -0
- package/dist/tools/definitions/session-management.d.ts +3 -0
- package/dist/tools/definitions/session-management.d.ts.map +1 -0
- package/dist/tools/definitions/session-management.js +41 -0
- package/dist/tools/definitions/session-management.js.map +1 -0
- package/dist/tools/definitions/system.d.ts +3 -0
- package/dist/tools/definitions/system.d.ts.map +1 -0
- package/dist/tools/definitions/system.js +143 -0
- package/dist/tools/definitions/system.js.map +1 -0
- package/dist/tools/definitions.d.ts +12 -0
- package/dist/tools/definitions.d.ts.map +1 -0
- package/dist/tools/definitions.js +26 -0
- package/dist/tools/definitions.js.map +1 -0
- package/dist/tools/handlers.d.ts +212 -0
- package/dist/tools/handlers.d.ts.map +1 -0
- package/dist/tools/handlers.js +712 -0
- package/dist/tools/handlers.js.map +1 -0
- package/dist/tools/index.d.ts +8 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +8 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/types.d.ts +82 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/cleanup-manager.d.ts +133 -0
- package/dist/utils/cleanup-manager.d.ts.map +1 -0
- package/dist/utils/cleanup-manager.js +673 -0
- package/dist/utils/cleanup-manager.js.map +1 -0
- package/dist/utils/cli-handler.d.ts +16 -0
- package/dist/utils/cli-handler.d.ts.map +1 -0
- package/dist/utils/cli-handler.js +102 -0
- package/dist/utils/cli-handler.js.map +1 -0
- package/dist/utils/logger.d.ts +61 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +92 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/page-utils.d.ts +54 -0
- package/dist/utils/page-utils.d.ts.map +1 -0
- package/dist/utils/page-utils.js +381 -0
- package/dist/utils/page-utils.js.map +1 -0
- package/dist/utils/rate-limit-handler.d.ts +42 -0
- package/dist/utils/rate-limit-handler.d.ts.map +1 -0
- package/dist/utils/rate-limit-handler.js +88 -0
- package/dist/utils/rate-limit-handler.js.map +1 -0
- package/dist/utils/rate-limit-handler.test.d.ts +7 -0
- package/dist/utils/rate-limit-handler.test.d.ts.map +1 -0
- package/dist/utils/rate-limit-handler.test.js +91 -0
- package/dist/utils/rate-limit-handler.test.js.map +1 -0
- package/dist/utils/settings-manager.d.ts +37 -0
- package/dist/utils/settings-manager.d.ts.map +1 -0
- package/dist/utils/settings-manager.js +121 -0
- package/dist/utils/settings-manager.js.map +1 -0
- package/dist/utils/stealth-utils.d.ts +135 -0
- package/dist/utils/stealth-utils.d.ts.map +1 -0
- package/dist/utils/stealth-utils.js +396 -0
- package/dist/utils/stealth-utils.js.map +1 -0
- package/dist/utils/stealth-utils.test.d.ts +7 -0
- package/dist/utils/stealth-utils.test.d.ts.map +1 -0
- package/dist/utils/stealth-utils.test.js +72 -0
- package/dist/utils/stealth-utils.test.js.map +1 -0
- package/docs/01_configuration.md +94 -0
- package/docs/02_tools.md +34 -0
- package/docs/03_troubleshooting.md +59 -0
- package/docs/04_usage-guide.md +245 -0
- package/docs/05_project-analysis.qmd +309 -0
- package/docs/06_integration-analysis.html +914 -0
- package/docs/06_integration-analysis.qmd +255 -0
- package/docs/06_integration-analysis_files/libs/bootstrap/bootstrap-4f0954b6b0dd6bf39f4bb9151ba984db.min.css +12 -0
- package/docs/06_integration-analysis_files/libs/bootstrap/bootstrap-icons.css +2106 -0
- package/docs/06_integration-analysis_files/libs/bootstrap/bootstrap-icons.woff +0 -0
- package/docs/06_integration-analysis_files/libs/bootstrap/bootstrap.min.js +7 -0
- package/docs/06_integration-analysis_files/libs/clipboard/clipboard.min.js +7 -0
- package/docs/06_integration-analysis_files/libs/quarto-html/anchor.min.js +9 -0
- package/docs/06_integration-analysis_files/libs/quarto-html/axe/axe-check.js +145 -0
- package/docs/06_integration-analysis_files/libs/quarto-html/popper.min.js +6 -0
- package/docs/06_integration-analysis_files/libs/quarto-html/quarto-syntax-highlighting-587c61ba64f3a5504c4d52d930310e48.css +236 -0
- package/docs/06_integration-analysis_files/libs/quarto-html/quarto.js +847 -0
- package/docs/06_integration-analysis_files/libs/quarto-html/tabsets/tabsets.js +95 -0
- package/docs/06_integration-analysis_files/libs/quarto-html/tippy.css +1 -0
- package/docs/06_integration-analysis_files/libs/quarto-html/tippy.umd.min.js +2 -0
- package/docs/07_e2e-testing-safety.qmd +754 -0
- package/docs/08_project-re-evaluation.html +609 -0
- package/docs/08_project-re-evaluation.qmd +86 -0
- package/docs/08_project-re-evaluation_files/libs/bootstrap/bootstrap-4f0954b6b0dd6bf39f4bb9151ba984db.min.css +12 -0
- package/docs/08_project-re-evaluation_files/libs/bootstrap/bootstrap-icons.css +2106 -0
- package/docs/08_project-re-evaluation_files/libs/bootstrap/bootstrap-icons.woff +0 -0
- package/docs/08_project-re-evaluation_files/libs/bootstrap/bootstrap.min.js +7 -0
- package/docs/08_project-re-evaluation_files/libs/clipboard/clipboard.min.js +7 -0
- package/docs/08_project-re-evaluation_files/libs/quarto-html/anchor.min.js +9 -0
- package/docs/08_project-re-evaluation_files/libs/quarto-html/axe/axe-check.js +145 -0
- package/docs/08_project-re-evaluation_files/libs/quarto-html/popper.min.js +6 -0
- package/docs/08_project-re-evaluation_files/libs/quarto-html/quarto-syntax-highlighting-587c61ba64f3a5504c4d52d930310e48.css +236 -0
- package/docs/08_project-re-evaluation_files/libs/quarto-html/quarto.js +847 -0
- package/docs/08_project-re-evaluation_files/libs/quarto-html/tabsets/tabsets.js +95 -0
- package/docs/08_project-re-evaluation_files/libs/quarto-html/tippy.css +1 -0
- package/docs/08_project-re-evaluation_files/libs/quarto-html/tippy.umd.min.js +2 -0
- package/docs/notebooklm-mcp-usage.html +704 -0
- package/docs/notebooklm-mcp-usage.qmd +119 -0
- package/docs/notebooklm-mcp-usage_files/libs/bootstrap/bootstrap-6b71f2156b6a5230c6677325978bcf08.min.css +12 -0
- package/docs/notebooklm-mcp-usage_files/libs/bootstrap/bootstrap-icons.css +2106 -0
- package/docs/notebooklm-mcp-usage_files/libs/bootstrap/bootstrap-icons.woff +0 -0
- package/docs/notebooklm-mcp-usage_files/libs/bootstrap/bootstrap.min.js +7 -0
- package/docs/notebooklm-mcp-usage_files/libs/clipboard/clipboard.min.js +7 -0
- package/docs/notebooklm-mcp-usage_files/libs/quarto-html/anchor.min.js +9 -0
- package/docs/notebooklm-mcp-usage_files/libs/quarto-html/axe/axe-check.js +145 -0
- package/docs/notebooklm-mcp-usage_files/libs/quarto-html/popper.min.js +6 -0
- package/docs/notebooklm-mcp-usage_files/libs/quarto-html/quarto-syntax-highlighting-587c61ba64f3a5504c4d52d930310e48.css +236 -0
- package/docs/notebooklm-mcp-usage_files/libs/quarto-html/quarto.js +847 -0
- package/docs/notebooklm-mcp-usage_files/libs/quarto-html/tabsets/tabsets.js +95 -0
- package/docs/notebooklm-mcp-usage_files/libs/quarto-html/tippy.css +1 -0
- package/docs/notebooklm-mcp-usage_files/libs/quarto-html/tippy.umd.min.js +2 -0
- package/docs/repomix-usage_files/libs/bootstrap/bootstrap-6b71f2156b6a5230c6677325978bcf08.min.css +12 -0
- package/docs/repomix-usage_files/libs/bootstrap/bootstrap-icons.css +2106 -0
- package/docs/repomix-usage_files/libs/bootstrap/bootstrap-icons.woff +0 -0
- package/docs/repomix-usage_files/libs/bootstrap/bootstrap.min.js +7 -0
- package/docs/repomix-usage_files/libs/clipboard/clipboard.min.js +7 -0
- package/docs/repomix-usage_files/libs/quarto-html/anchor.min.js +9 -0
- package/docs/repomix-usage_files/libs/quarto-html/axe/axe-check.js +145 -0
- package/docs/repomix-usage_files/libs/quarto-html/popper.min.js +6 -0
- package/docs/repomix-usage_files/libs/quarto-html/quarto-syntax-highlighting-587c61ba64f3a5504c4d52d930310e48.css +236 -0
- package/docs/repomix-usage_files/libs/quarto-html/quarto.js +847 -0
- package/docs/repomix-usage_files/libs/quarto-html/tabsets/tabsets.js +95 -0
- package/docs/repomix-usage_files/libs/quarto-html/tippy.css +1 -0
- package/docs/repomix-usage_files/libs/quarto-html/tippy.umd.min.js +2 -0
- 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 @@
|
|
|
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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"}
|