@pan-sec/notebooklm-mcp 1.4.0 ā 1.7.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.
- package/README.md +61 -7
- package/SECURITY.md +40 -6
- package/dist/auth/mcp-auth.d.ts.map +1 -1
- package/dist/auth/mcp-auth.js +3 -6
- package/dist/auth/mcp-auth.js.map +1 -1
- package/dist/compliance/alert-manager.d.ts +120 -0
- package/dist/compliance/alert-manager.d.ts.map +1 -0
- package/dist/compliance/alert-manager.js +420 -0
- package/dist/compliance/alert-manager.js.map +1 -0
- package/dist/compliance/breach-detection.d.ts +134 -0
- package/dist/compliance/breach-detection.d.ts.map +1 -0
- package/dist/compliance/breach-detection.js +456 -0
- package/dist/compliance/breach-detection.js.map +1 -0
- package/dist/compliance/change-log.d.ts +113 -0
- package/dist/compliance/change-log.d.ts.map +1 -0
- package/dist/compliance/change-log.js +275 -0
- package/dist/compliance/change-log.js.map +1 -0
- package/dist/compliance/compliance-logger.d.ts +136 -0
- package/dist/compliance/compliance-logger.d.ts.map +1 -0
- package/dist/compliance/compliance-logger.js +425 -0
- package/dist/compliance/compliance-logger.js.map +1 -0
- package/dist/compliance/compliance-tools.d.ts +18 -0
- package/dist/compliance/compliance-tools.d.ts.map +1 -0
- package/dist/compliance/compliance-tools.js +673 -0
- package/dist/compliance/compliance-tools.js.map +1 -0
- package/dist/compliance/consent-manager.d.ts +130 -0
- package/dist/compliance/consent-manager.d.ts.map +1 -0
- package/dist/compliance/consent-manager.js +386 -0
- package/dist/compliance/consent-manager.js.map +1 -0
- package/dist/compliance/dashboard.d.ts +243 -0
- package/dist/compliance/dashboard.d.ts.map +1 -0
- package/dist/compliance/dashboard.js +519 -0
- package/dist/compliance/dashboard.js.map +1 -0
- package/dist/compliance/data-classification.d.ts +117 -0
- package/dist/compliance/data-classification.d.ts.map +1 -0
- package/dist/compliance/data-classification.js +469 -0
- package/dist/compliance/data-classification.js.map +1 -0
- package/dist/compliance/data-erasure.d.ts +110 -0
- package/dist/compliance/data-erasure.d.ts.map +1 -0
- package/dist/compliance/data-erasure.js +501 -0
- package/dist/compliance/data-erasure.js.map +1 -0
- package/dist/compliance/data-export.d.ts +85 -0
- package/dist/compliance/data-export.d.ts.map +1 -0
- package/dist/compliance/data-export.js +394 -0
- package/dist/compliance/data-export.js.map +1 -0
- package/dist/compliance/data-inventory.d.ts +136 -0
- package/dist/compliance/data-inventory.d.ts.map +1 -0
- package/dist/compliance/data-inventory.js +335 -0
- package/dist/compliance/data-inventory.js.map +1 -0
- package/dist/compliance/dsar-handler.d.ts +123 -0
- package/dist/compliance/dsar-handler.d.ts.map +1 -0
- package/dist/compliance/dsar-handler.js +371 -0
- package/dist/compliance/dsar-handler.js.map +1 -0
- package/dist/compliance/evidence-collector.d.ts +187 -0
- package/dist/compliance/evidence-collector.d.ts.map +1 -0
- package/dist/compliance/evidence-collector.js +656 -0
- package/dist/compliance/evidence-collector.js.map +1 -0
- package/dist/compliance/health-monitor.d.ts +111 -0
- package/dist/compliance/health-monitor.d.ts.map +1 -0
- package/dist/compliance/health-monitor.js +509 -0
- package/dist/compliance/health-monitor.js.map +1 -0
- package/dist/compliance/incident-manager.d.ts +131 -0
- package/dist/compliance/incident-manager.d.ts.map +1 -0
- package/dist/compliance/incident-manager.js +418 -0
- package/dist/compliance/incident-manager.js.map +1 -0
- package/dist/compliance/index.d.ts +32 -0
- package/dist/compliance/index.d.ts.map +1 -0
- package/dist/compliance/index.js +35 -0
- package/dist/compliance/index.js.map +1 -0
- package/dist/compliance/policy-docs.d.ts +108 -0
- package/dist/compliance/policy-docs.d.ts.map +1 -0
- package/dist/compliance/policy-docs.js +464 -0
- package/dist/compliance/policy-docs.js.map +1 -0
- package/dist/compliance/privacy-notice-text.d.ts +58 -0
- package/dist/compliance/privacy-notice-text.d.ts.map +1 -0
- package/dist/compliance/privacy-notice-text.js +161 -0
- package/dist/compliance/privacy-notice-text.js.map +1 -0
- package/dist/compliance/privacy-notice.d.ts +128 -0
- package/dist/compliance/privacy-notice.d.ts.map +1 -0
- package/dist/compliance/privacy-notice.js +250 -0
- package/dist/compliance/privacy-notice.js.map +1 -0
- package/dist/compliance/report-generator.d.ts +168 -0
- package/dist/compliance/report-generator.d.ts.map +1 -0
- package/dist/compliance/report-generator.js +830 -0
- package/dist/compliance/report-generator.js.map +1 -0
- package/dist/compliance/retention-engine.d.ts +130 -0
- package/dist/compliance/retention-engine.d.ts.map +1 -0
- package/dist/compliance/retention-engine.js +510 -0
- package/dist/compliance/retention-engine.js.map +1 -0
- package/dist/compliance/siem-exporter.d.ts +150 -0
- package/dist/compliance/siem-exporter.d.ts.map +1 -0
- package/dist/compliance/siem-exporter.js +509 -0
- package/dist/compliance/siem-exporter.js.map +1 -0
- package/dist/compliance/types.d.ts +601 -0
- package/dist/compliance/types.d.ts.map +1 -0
- package/dist/compliance/types.js +22 -0
- package/dist/compliance/types.js.map +1 -0
- package/dist/config.d.ts +4 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +8 -1
- package/dist/config.js.map +1 -1
- package/dist/events/event-emitter.d.ts +45 -0
- package/dist/events/event-emitter.d.ts.map +1 -0
- package/dist/events/event-emitter.js +100 -0
- package/dist/events/event-emitter.js.map +1 -0
- package/dist/events/event-types.d.ts +124 -0
- package/dist/events/event-types.d.ts.map +1 -0
- package/dist/events/event-types.js +18 -0
- package/dist/events/event-types.js.map +1 -0
- package/dist/index.js +59 -2
- package/dist/index.js.map +1 -1
- package/dist/library/notebook-library.d.ts +25 -2
- package/dist/library/notebook-library.d.ts.map +1 -1
- package/dist/library/notebook-library.js +144 -3
- package/dist/library/notebook-library.js.map +1 -1
- package/dist/library/types.d.ts +15 -0
- package/dist/library/types.d.ts.map +1 -1
- package/dist/notebook-creation/audio-manager.d.ts +56 -0
- package/dist/notebook-creation/audio-manager.d.ts.map +1 -0
- package/dist/notebook-creation/audio-manager.js +335 -0
- package/dist/notebook-creation/audio-manager.js.map +1 -0
- package/dist/notebook-creation/discover-creation-flow.d.ts +8 -0
- package/dist/notebook-creation/discover-creation-flow.d.ts.map +1 -0
- package/dist/notebook-creation/discover-creation-flow.js +177 -0
- package/dist/notebook-creation/discover-creation-flow.js.map +1 -0
- package/dist/notebook-creation/discover-quota.d.ts +8 -0
- package/dist/notebook-creation/discover-quota.d.ts.map +1 -0
- package/dist/notebook-creation/discover-quota.js +195 -0
- package/dist/notebook-creation/discover-quota.js.map +1 -0
- package/dist/notebook-creation/discover-source-dialog.d.ts +8 -0
- package/dist/notebook-creation/discover-source-dialog.d.ts.map +1 -0
- package/dist/notebook-creation/discover-source-dialog.js +134 -0
- package/dist/notebook-creation/discover-source-dialog.js.map +1 -0
- package/dist/notebook-creation/discover-sources.d.ts +8 -0
- package/dist/notebook-creation/discover-sources.d.ts.map +1 -0
- package/dist/notebook-creation/discover-sources.js +273 -0
- package/dist/notebook-creation/discover-sources.js.map +1 -0
- package/dist/notebook-creation/discover-text-input.d.ts +7 -0
- package/dist/notebook-creation/discover-text-input.d.ts.map +1 -0
- package/dist/notebook-creation/discover-text-input.js +135 -0
- package/dist/notebook-creation/discover-text-input.js.map +1 -0
- package/dist/notebook-creation/index.d.ts +12 -0
- package/dist/notebook-creation/index.d.ts.map +1 -0
- package/dist/notebook-creation/index.js +12 -0
- package/dist/notebook-creation/index.js.map +1 -0
- package/dist/notebook-creation/notebook-creator.d.ts +95 -0
- package/dist/notebook-creation/notebook-creator.d.ts.map +1 -0
- package/dist/notebook-creation/notebook-creator.js +689 -0
- package/dist/notebook-creation/notebook-creator.js.map +1 -0
- package/dist/notebook-creation/notebook-sync.d.ts +93 -0
- package/dist/notebook-creation/notebook-sync.d.ts.map +1 -0
- package/dist/notebook-creation/notebook-sync.js +370 -0
- package/dist/notebook-creation/notebook-sync.js.map +1 -0
- package/dist/notebook-creation/run-discovery.d.ts +11 -0
- package/dist/notebook-creation/run-discovery.d.ts.map +1 -0
- package/dist/notebook-creation/run-discovery.js +151 -0
- package/dist/notebook-creation/run-discovery.js.map +1 -0
- package/dist/notebook-creation/selector-discovery.d.ts +65 -0
- package/dist/notebook-creation/selector-discovery.d.ts.map +1 -0
- package/dist/notebook-creation/selector-discovery.js +421 -0
- package/dist/notebook-creation/selector-discovery.js.map +1 -0
- package/dist/notebook-creation/selectors.d.ts +150 -0
- package/dist/notebook-creation/selectors.d.ts.map +1 -0
- package/dist/notebook-creation/selectors.js +225 -0
- package/dist/notebook-creation/selectors.js.map +1 -0
- package/dist/notebook-creation/source-manager.d.ts +73 -0
- package/dist/notebook-creation/source-manager.d.ts.map +1 -0
- package/dist/notebook-creation/source-manager.js +486 -0
- package/dist/notebook-creation/source-manager.js.map +1 -0
- package/dist/notebook-creation/test-create.d.ts +8 -0
- package/dist/notebook-creation/test-create.d.ts.map +1 -0
- package/dist/notebook-creation/test-create.js +72 -0
- package/dist/notebook-creation/test-create.js.map +1 -0
- package/dist/notebook-creation/types.d.ts +173 -0
- package/dist/notebook-creation/types.d.ts.map +1 -0
- package/dist/notebook-creation/types.js +5 -0
- package/dist/notebook-creation/types.js.map +1 -0
- package/dist/quota/index.d.ts +8 -0
- package/dist/quota/index.d.ts.map +1 -0
- package/dist/quota/index.js +8 -0
- package/dist/quota/index.js.map +1 -0
- package/dist/quota/quota-manager.d.ts +125 -0
- package/dist/quota/quota-manager.d.ts.map +1 -0
- package/dist/quota/quota-manager.js +330 -0
- package/dist/quota/quota-manager.js.map +1 -0
- package/dist/session/session-manager.d.ts +5 -0
- package/dist/session/session-manager.d.ts.map +1 -1
- package/dist/session/session-manager.js +6 -0
- package/dist/session/session-manager.js.map +1 -1
- package/dist/session/shared-context-manager.d.ts.map +1 -1
- package/dist/session/shared-context-manager.js +2 -1
- package/dist/session/shared-context-manager.js.map +1 -1
- package/dist/tools/definitions/notebook-management.d.ts.map +1 -1
- package/dist/tools/definitions/notebook-management.js +525 -0
- package/dist/tools/definitions/notebook-management.js.map +1 -1
- package/dist/tools/definitions/system.d.ts.map +1 -1
- package/dist/tools/definitions/system.js +158 -0
- package/dist/tools/definitions/system.js.map +1 -1
- package/dist/tools/handlers.d.ts +225 -0
- package/dist/tools/handlers.d.ts.map +1 -1
- package/dist/tools/handlers.js +911 -0
- package/dist/tools/handlers.js.map +1 -1
- package/dist/utils/audit-logger.d.ts +21 -1
- package/dist/utils/audit-logger.d.ts.map +1 -1
- package/dist/utils/audit-logger.js +53 -4
- package/dist/utils/audit-logger.js.map +1 -1
- package/dist/utils/crypto.d.ts.map +1 -1
- package/dist/utils/crypto.js +8 -15
- package/dist/utils/crypto.js.map +1 -1
- package/dist/utils/file-permissions.d.ts +85 -0
- package/dist/utils/file-permissions.d.ts.map +1 -0
- package/dist/utils/file-permissions.js +180 -0
- package/dist/utils/file-permissions.js.map +1 -0
- package/dist/utils/settings-manager.d.ts.map +1 -1
- package/dist/utils/settings-manager.js +6 -11
- package/dist/utils/settings-manager.js.map +1 -1
- package/dist/webhooks/index.d.ts +8 -0
- package/dist/webhooks/index.d.ts.map +1 -0
- package/dist/webhooks/index.js +8 -0
- package/dist/webhooks/index.js.map +1 -0
- package/dist/webhooks/types.d.ts +57 -0
- package/dist/webhooks/types.d.ts.map +1 -0
- package/dist/webhooks/types.js +5 -0
- package/dist/webhooks/types.js.map +1 -0
- package/dist/webhooks/webhook-dispatcher.d.ts +120 -0
- package/dist/webhooks/webhook-dispatcher.d.ts.map +1 -0
- package/dist/webhooks/webhook-dispatcher.js +519 -0
- package/dist/webhooks/webhook-dispatcher.js.map +1 -0
- package/docs/COMPLIANCE-SPEC.md +1452 -0
- package/package.json +30 -4
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Run NotebookLM UI Selector Discovery
|
|
4
|
+
*
|
|
5
|
+
* This script launches a browser, navigates NotebookLM's interface,
|
|
6
|
+
* and discovers CSS selectors for notebook creation elements.
|
|
7
|
+
*
|
|
8
|
+
* Usage: npx ts-node src/notebook-creation/run-discovery.ts
|
|
9
|
+
*/
|
|
10
|
+
import { AuthManager } from "../auth/auth-manager.js";
|
|
11
|
+
import { SharedContextManager } from "../session/shared-context-manager.js";
|
|
12
|
+
import { discoverSelectors } from "./selector-discovery.js";
|
|
13
|
+
import { log } from "../utils/logger.js";
|
|
14
|
+
import { CONFIG } from "../config.js";
|
|
15
|
+
import fs from "fs";
|
|
16
|
+
import path from "path";
|
|
17
|
+
async function main() {
|
|
18
|
+
log.info("š Starting NotebookLM UI Selector Discovery");
|
|
19
|
+
log.info(" This will open a browser and analyze the NotebookLM interface.");
|
|
20
|
+
log.info("");
|
|
21
|
+
// Initialize managers
|
|
22
|
+
const authManager = new AuthManager();
|
|
23
|
+
const contextManager = new SharedContextManager(authManager);
|
|
24
|
+
try {
|
|
25
|
+
// Get browser context (visible for debugging)
|
|
26
|
+
log.info("š Getting browser context (visible mode)...");
|
|
27
|
+
const context = await contextManager.getOrCreateContext(false); // false = show browser
|
|
28
|
+
// Check authentication
|
|
29
|
+
log.info("š Checking authentication...");
|
|
30
|
+
const isAuthenticated = await authManager.validateCookiesExpiry(context);
|
|
31
|
+
if (!isAuthenticated) {
|
|
32
|
+
log.warning("ā ļø Not authenticated or session expired!");
|
|
33
|
+
log.warning(" Please login first using setup_auth tool.");
|
|
34
|
+
log.info(" Run the MCP server and use the setup_auth tool.");
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
log.success("ā
Authenticated!");
|
|
38
|
+
// Run discovery
|
|
39
|
+
log.info("\nš Running selector discovery...\n");
|
|
40
|
+
const result = await discoverSelectors(context);
|
|
41
|
+
// Save results to file
|
|
42
|
+
const outputPath = path.join(CONFIG.dataDir, "discovered-selectors.json");
|
|
43
|
+
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
44
|
+
fs.writeFileSync(outputPath, JSON.stringify(result, null, 2));
|
|
45
|
+
log.success(`\nā
Discovery complete! Results saved to: ${outputPath}`);
|
|
46
|
+
// Generate selectors.ts content
|
|
47
|
+
if (Object.keys(result.selectors).length > 0) {
|
|
48
|
+
const selectorsCode = generateSelectorsCode(result);
|
|
49
|
+
const selectorsPath = path.join(path.dirname(new URL(import.meta.url).pathname), "selectors.ts");
|
|
50
|
+
fs.writeFileSync(selectorsPath, selectorsCode);
|
|
51
|
+
log.success(`š Generated selectors.ts at: ${selectorsPath}`);
|
|
52
|
+
}
|
|
53
|
+
// Print summary
|
|
54
|
+
printSummary(result);
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
log.error(`ā Discovery failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
finally {
|
|
61
|
+
// Keep browser open briefly for inspection
|
|
62
|
+
log.info("\nā³ Keeping browser open for 5 seconds for inspection...");
|
|
63
|
+
await new Promise((resolve) => setTimeout(resolve, 5000));
|
|
64
|
+
await contextManager.closeContext();
|
|
65
|
+
log.info("š Done!");
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Generate selectors.ts code from discovery results
|
|
70
|
+
*/
|
|
71
|
+
function generateSelectorsCode(result) {
|
|
72
|
+
const lines = [
|
|
73
|
+
'/**',
|
|
74
|
+
' * NotebookLM UI Selectors',
|
|
75
|
+
' *',
|
|
76
|
+
` * Auto-generated on ${result.discoveredAt}`,
|
|
77
|
+
' * by selector-discovery.ts',
|
|
78
|
+
' *',
|
|
79
|
+
' * These selectors are used for notebook creation automation.',
|
|
80
|
+
' */',
|
|
81
|
+
'',
|
|
82
|
+
'export const NOTEBOOKLM_SELECTORS = {',
|
|
83
|
+
];
|
|
84
|
+
for (const [key, info] of Object.entries(result.selectors)) {
|
|
85
|
+
if (info && typeof info === 'object' && 'primary' in info) {
|
|
86
|
+
const selectorInfo = info;
|
|
87
|
+
lines.push(` /** ${selectorInfo.description} */`);
|
|
88
|
+
lines.push(` ${key}: {`);
|
|
89
|
+
lines.push(` primary: ${JSON.stringify(selectorInfo.primary)},`);
|
|
90
|
+
lines.push(` fallbacks: ${JSON.stringify(selectorInfo.fallbacks)},`);
|
|
91
|
+
lines.push(` confirmed: ${selectorInfo.confirmed},`);
|
|
92
|
+
lines.push(` },`);
|
|
93
|
+
lines.push('');
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
lines.push('} as const;');
|
|
97
|
+
lines.push('');
|
|
98
|
+
lines.push('export type SelectorKey = keyof typeof NOTEBOOKLM_SELECTORS;');
|
|
99
|
+
lines.push('');
|
|
100
|
+
lines.push('/**');
|
|
101
|
+
lines.push(' * Get selector with fallbacks');
|
|
102
|
+
lines.push(' */');
|
|
103
|
+
lines.push('export function getSelector(key: SelectorKey): string[] {');
|
|
104
|
+
lines.push(' const info = NOTEBOOKLM_SELECTORS[key];');
|
|
105
|
+
lines.push(' return [info.primary, ...info.fallbacks].filter(Boolean);');
|
|
106
|
+
lines.push('}');
|
|
107
|
+
lines.push('');
|
|
108
|
+
return lines.join('\n');
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Print discovery summary
|
|
112
|
+
*/
|
|
113
|
+
function printSummary(result) {
|
|
114
|
+
log.info("\n" + "=".repeat(60));
|
|
115
|
+
log.info("š DISCOVERY SUMMARY");
|
|
116
|
+
log.info("=".repeat(60));
|
|
117
|
+
const found = [];
|
|
118
|
+
const notFound = [];
|
|
119
|
+
for (const [key, info] of Object.entries(result.selectors)) {
|
|
120
|
+
if (info && typeof info === 'object' && 'primary' in info) {
|
|
121
|
+
const selectorInfo = info;
|
|
122
|
+
if (selectorInfo.primary) {
|
|
123
|
+
found.push(key);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
notFound.push(key);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
log.info(`\nā
Found (${found.length}):`);
|
|
131
|
+
for (const key of found) {
|
|
132
|
+
log.info(` - ${key}`);
|
|
133
|
+
}
|
|
134
|
+
if (notFound.length > 0) {
|
|
135
|
+
log.warning(`\nā ļø Not Found (${notFound.length}):`);
|
|
136
|
+
for (const key of notFound) {
|
|
137
|
+
log.warning(` - ${key}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
log.info("\n" + "=".repeat(60));
|
|
141
|
+
if (notFound.length > 0) {
|
|
142
|
+
log.info("\nš” Manual inspection may be needed for missing selectors.");
|
|
143
|
+
log.info(" The browser was kept open briefly for visual inspection.");
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// Run main
|
|
147
|
+
main().catch((error) => {
|
|
148
|
+
log.error(`Fatal error: ${error}`);
|
|
149
|
+
process.exit(1);
|
|
150
|
+
});
|
|
151
|
+
//# sourceMappingURL=run-discovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-discovery.js","sourceRoot":"","sources":["../../src/notebook-creation/run-discovery.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Automated UI selector discovery for NotebookLM
|
|
3
|
+
*
|
|
4
|
+
* This tool navigates NotebookLM's interface and discovers CSS selectors
|
|
5
|
+
* for elements needed to create notebooks programmatically.
|
|
6
|
+
*/
|
|
7
|
+
import type { BrowserContext } from "patchright";
|
|
8
|
+
import type { ElementInfo, DiscoveryResult } from "./types.js";
|
|
9
|
+
/**
|
|
10
|
+
* Discovers UI selectors for NotebookLM's notebook creation workflow
|
|
11
|
+
*/
|
|
12
|
+
export declare class SelectorDiscovery {
|
|
13
|
+
private context;
|
|
14
|
+
private page;
|
|
15
|
+
constructor(context: BrowserContext);
|
|
16
|
+
/**
|
|
17
|
+
* Run the full discovery process
|
|
18
|
+
*/
|
|
19
|
+
discover(): Promise<DiscoveryResult>;
|
|
20
|
+
/**
|
|
21
|
+
* Navigate to NotebookLM homepage
|
|
22
|
+
*/
|
|
23
|
+
private navigateToHome;
|
|
24
|
+
/**
|
|
25
|
+
* Dump all interactive elements on the current page
|
|
26
|
+
*/
|
|
27
|
+
dumpInteractiveElements(): Promise<ElementInfo[]>;
|
|
28
|
+
/**
|
|
29
|
+
* Find a selector matching keywords
|
|
30
|
+
*/
|
|
31
|
+
private findSelector;
|
|
32
|
+
/**
|
|
33
|
+
* Find input element selector
|
|
34
|
+
*/
|
|
35
|
+
private findInputSelector;
|
|
36
|
+
/**
|
|
37
|
+
* Find textarea selector
|
|
38
|
+
*/
|
|
39
|
+
private findTextareaSelector;
|
|
40
|
+
/**
|
|
41
|
+
* Find file input element
|
|
42
|
+
*/
|
|
43
|
+
private findFileInput;
|
|
44
|
+
/**
|
|
45
|
+
* Build a CSS selector for an element
|
|
46
|
+
*/
|
|
47
|
+
private buildSelector;
|
|
48
|
+
/**
|
|
49
|
+
* Try to click the new notebook button
|
|
50
|
+
*/
|
|
51
|
+
private tryClickNewNotebook;
|
|
52
|
+
/**
|
|
53
|
+
* Try to click an element by selector
|
|
54
|
+
*/
|
|
55
|
+
private tryClick;
|
|
56
|
+
/**
|
|
57
|
+
* Log discovery results summary
|
|
58
|
+
*/
|
|
59
|
+
private logDiscoveryResults;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Run selector discovery and return results
|
|
63
|
+
*/
|
|
64
|
+
export declare function discoverSelectors(context: BrowserContext): Promise<DiscoveryResult>;
|
|
65
|
+
//# sourceMappingURL=selector-discovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selector-discovery.d.ts","sourceRoot":"","sources":["../../src/notebook-creation/selector-discovery.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAQ,cAAc,EAAE,MAAM,YAAY,CAAC;AACvD,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAgB,MAAM,YAAY,CAAC;AAoB7E;;GAEG;AACH,qBAAa,iBAAiB;IAGhB,OAAO,CAAC,OAAO;IAF3B,OAAO,CAAC,IAAI,CAAqB;gBAEb,OAAO,EAAE,cAAc;IAE3C;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,eAAe,CAAC;IAiJ1C;;OAEG;YACW,cAAc;IAc5B;;OAEG;IACG,uBAAuB,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAsDvD;;OAEG;IACH,OAAO,CAAC,YAAY;IAyDpB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAiBzB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAc5B;;OAEG;YACW,aAAa;IAuC3B;;OAEG;IACH,OAAO,CAAC,aAAa;IAoCrB;;OAEG;YACW,mBAAmB;IA2CjC;;OAEG;YACW,QAAQ;IAgBtB;;OAEG;IACH,OAAO,CAAC,mBAAmB;CAyB5B;AAWD;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC,CAGzF"}
|
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Automated UI selector discovery for NotebookLM
|
|
3
|
+
*
|
|
4
|
+
* This tool navigates NotebookLM's interface and discovers CSS selectors
|
|
5
|
+
* for elements needed to create notebooks programmatically.
|
|
6
|
+
*/
|
|
7
|
+
import { log } from "../utils/logger.js";
|
|
8
|
+
import { randomDelay, realisticClick } from "../utils/stealth-utils.js";
|
|
9
|
+
import { CONFIG } from "../config.js";
|
|
10
|
+
const NOTEBOOKLM_URL = "https://notebooklm.google.com/";
|
|
11
|
+
/**
|
|
12
|
+
* Keywords to identify UI elements by their text/aria-label
|
|
13
|
+
*/
|
|
14
|
+
const ELEMENT_KEYWORDS = {
|
|
15
|
+
newNotebook: ["new", "create", "notebook", "+", "add notebook"],
|
|
16
|
+
addSource: ["add source", "add", "source", "+", "upload"],
|
|
17
|
+
urlSource: ["url", "website", "web", "link", "webpage"],
|
|
18
|
+
textSource: ["text", "paste", "copy", "clipboard"],
|
|
19
|
+
fileSource: ["file", "upload", "pdf", "document", "drive"],
|
|
20
|
+
submit: ["add", "submit", "done", "create", "save", "ok", "confirm"],
|
|
21
|
+
cancel: ["cancel", "close", "back", "x"],
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Discovers UI selectors for NotebookLM's notebook creation workflow
|
|
25
|
+
*/
|
|
26
|
+
export class SelectorDiscovery {
|
|
27
|
+
context;
|
|
28
|
+
page = null;
|
|
29
|
+
constructor(context) {
|
|
30
|
+
this.context = context;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Run the full discovery process
|
|
34
|
+
*/
|
|
35
|
+
async discover() {
|
|
36
|
+
const result = {
|
|
37
|
+
selectors: {},
|
|
38
|
+
homepageElements: [],
|
|
39
|
+
creationElements: [],
|
|
40
|
+
sourceElements: [],
|
|
41
|
+
discoveredAt: new Date().toISOString(),
|
|
42
|
+
errors: [],
|
|
43
|
+
};
|
|
44
|
+
try {
|
|
45
|
+
// Create a new page for discovery
|
|
46
|
+
this.page = await this.context.newPage();
|
|
47
|
+
log.info("š Starting NotebookLM UI selector discovery...");
|
|
48
|
+
// Phase 1: Discover homepage elements
|
|
49
|
+
log.info("š Phase 1: Analyzing homepage...");
|
|
50
|
+
await this.navigateToHome();
|
|
51
|
+
result.homepageElements = await this.dumpInteractiveElements();
|
|
52
|
+
result.selectors.newNotebookButton = this.findSelector(result.homepageElements, ELEMENT_KEYWORDS.newNotebook, "New notebook button");
|
|
53
|
+
// Phase 2: Navigate to notebook creation and discover elements
|
|
54
|
+
log.info("š Phase 2: Analyzing notebook creation UI...");
|
|
55
|
+
const clickedNew = await this.tryClickNewNotebook(result.selectors.newNotebookButton);
|
|
56
|
+
if (clickedNew) {
|
|
57
|
+
await randomDelay(1500, 2500);
|
|
58
|
+
result.creationElements = await this.dumpInteractiveElements();
|
|
59
|
+
// Find notebook name input
|
|
60
|
+
result.selectors.notebookNameInput = this.findInputSelector(result.creationElements, ["name", "title", "notebook"], "Notebook name input");
|
|
61
|
+
// Find add source button
|
|
62
|
+
result.selectors.addSourceButton = this.findSelector(result.creationElements, ELEMENT_KEYWORDS.addSource, "Add source button");
|
|
63
|
+
}
|
|
64
|
+
// Phase 3: Try to access source addition UI
|
|
65
|
+
log.info("š Phase 3: Analyzing source addition UI...");
|
|
66
|
+
if (result.selectors.addSourceButton?.primary) {
|
|
67
|
+
const clickedAddSource = await this.tryClick(result.selectors.addSourceButton.primary);
|
|
68
|
+
if (clickedAddSource) {
|
|
69
|
+
await randomDelay(1000, 2000);
|
|
70
|
+
result.sourceElements = await this.dumpInteractiveElements();
|
|
71
|
+
// Find source type options
|
|
72
|
+
result.selectors.urlSourceOption = this.findSelector(result.sourceElements, ELEMENT_KEYWORDS.urlSource, "URL source option");
|
|
73
|
+
result.selectors.textSourceOption = this.findSelector(result.sourceElements, ELEMENT_KEYWORDS.textSource, "Text source option");
|
|
74
|
+
result.selectors.fileSourceOption = this.findSelector(result.sourceElements, ELEMENT_KEYWORDS.fileSource, "File source option");
|
|
75
|
+
// Find file input element
|
|
76
|
+
result.selectors.fileInput = await this.findFileInput();
|
|
77
|
+
// Find URL input
|
|
78
|
+
result.selectors.urlInput = this.findInputSelector(result.sourceElements, ["url", "link", "website", "http"], "URL input field");
|
|
79
|
+
// Find text input
|
|
80
|
+
result.selectors.textInput = this.findTextareaSelector(result.sourceElements, ["text", "paste", "content"], "Text input area");
|
|
81
|
+
// Find submit button
|
|
82
|
+
result.selectors.submitButton = this.findSelector(result.sourceElements, ELEMENT_KEYWORDS.submit, "Submit/Add button");
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// Phase 4: Look for status indicators
|
|
86
|
+
log.info("š Phase 4: Identifying status indicators...");
|
|
87
|
+
const allElements = [
|
|
88
|
+
...result.homepageElements,
|
|
89
|
+
...result.creationElements,
|
|
90
|
+
...result.sourceElements,
|
|
91
|
+
];
|
|
92
|
+
result.selectors.processingIndicator = this.findSelector(allElements, ["loading", "processing", "uploading", "progress", "spinner"], "Processing indicator");
|
|
93
|
+
result.selectors.successIndicator = this.findSelector(allElements, ["success", "done", "complete", "added", "ā", "check"], "Success indicator");
|
|
94
|
+
result.selectors.errorMessage = this.findSelector(allElements, ["error", "failed", "invalid", "warning"], "Error message");
|
|
95
|
+
log.success("ā
Discovery complete!");
|
|
96
|
+
this.logDiscoveryResults(result);
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
100
|
+
log.error(`ā Discovery error: ${errorMsg}`);
|
|
101
|
+
result.errors.push(errorMsg);
|
|
102
|
+
}
|
|
103
|
+
finally {
|
|
104
|
+
if (this.page) {
|
|
105
|
+
await this.page.close().catch(() => { });
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Navigate to NotebookLM homepage
|
|
112
|
+
*/
|
|
113
|
+
async navigateToHome() {
|
|
114
|
+
if (!this.page)
|
|
115
|
+
throw new Error("Page not initialized");
|
|
116
|
+
await this.page.goto(NOTEBOOKLM_URL, {
|
|
117
|
+
waitUntil: "domcontentloaded",
|
|
118
|
+
timeout: CONFIG.browserTimeout,
|
|
119
|
+
});
|
|
120
|
+
await randomDelay(2000, 3000);
|
|
121
|
+
// Wait for page to be interactive
|
|
122
|
+
await this.page.waitForLoadState("networkidle").catch(() => { });
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Dump all interactive elements on the current page
|
|
126
|
+
*/
|
|
127
|
+
async dumpInteractiveElements() {
|
|
128
|
+
if (!this.page)
|
|
129
|
+
return [];
|
|
130
|
+
// Note: page.evaluate runs in browser context - uses any types
|
|
131
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
132
|
+
const results = await this.page.evaluate(() => {
|
|
133
|
+
// @ts-expect-error - DOM types available in browser context
|
|
134
|
+
const elements = document.querySelectorAll('button, input, textarea, [role="button"], [role="menuitem"], ' +
|
|
135
|
+
'[role="option"], a[href], [tabindex="0"], [onclick], ' +
|
|
136
|
+
'[data-action], .clickable, [aria-haspopup]');
|
|
137
|
+
return Array.from(elements).map((el) => {
|
|
138
|
+
const rect = el.getBoundingClientRect();
|
|
139
|
+
// @ts-expect-error - DOM types available in browser context
|
|
140
|
+
const computedStyle = window.getComputedStyle(el);
|
|
141
|
+
const isVisible = rect.width > 0 &&
|
|
142
|
+
rect.height > 0 &&
|
|
143
|
+
computedStyle.visibility !== "hidden" &&
|
|
144
|
+
computedStyle.display !== "none" &&
|
|
145
|
+
computedStyle.opacity !== "0";
|
|
146
|
+
const attrs = Array.from(el.attributes || []);
|
|
147
|
+
const dataAttrs = {};
|
|
148
|
+
for (const attr of attrs) {
|
|
149
|
+
if (attr.name && attr.name.startsWith("data-")) {
|
|
150
|
+
dataAttrs[attr.name] = attr.value;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
tag: el.tagName?.toLowerCase() || "",
|
|
155
|
+
id: el.id || "",
|
|
156
|
+
classes: typeof el.className === 'string' ? el.className : "",
|
|
157
|
+
ariaLabel: el.getAttribute?.("aria-label") || null,
|
|
158
|
+
text: el.textContent?.trim().slice(0, 100) || null,
|
|
159
|
+
dataAttrs,
|
|
160
|
+
role: el.getAttribute?.("role") || null,
|
|
161
|
+
isVisible,
|
|
162
|
+
boundingBox: isVisible ? {
|
|
163
|
+
x: rect.x,
|
|
164
|
+
y: rect.y,
|
|
165
|
+
width: rect.width,
|
|
166
|
+
height: rect.height,
|
|
167
|
+
} : undefined,
|
|
168
|
+
};
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
return results;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Find a selector matching keywords
|
|
175
|
+
*/
|
|
176
|
+
findSelector(elements, keywords, description) {
|
|
177
|
+
const matches = [];
|
|
178
|
+
for (const el of elements) {
|
|
179
|
+
if (!el.isVisible)
|
|
180
|
+
continue;
|
|
181
|
+
let score = 0;
|
|
182
|
+
const textLower = (el.text || "").toLowerCase();
|
|
183
|
+
const ariaLower = (el.ariaLabel || "").toLowerCase();
|
|
184
|
+
const classLower = (el.classes || "").toLowerCase();
|
|
185
|
+
for (const keyword of keywords) {
|
|
186
|
+
const kw = keyword.toLowerCase();
|
|
187
|
+
if (textLower.includes(kw))
|
|
188
|
+
score += 3;
|
|
189
|
+
if (ariaLower.includes(kw))
|
|
190
|
+
score += 4;
|
|
191
|
+
if (classLower.includes(kw))
|
|
192
|
+
score += 2;
|
|
193
|
+
if (el.id.toLowerCase().includes(kw))
|
|
194
|
+
score += 3;
|
|
195
|
+
// Check data attributes
|
|
196
|
+
for (const [, value] of Object.entries(el.dataAttrs)) {
|
|
197
|
+
if (value.toLowerCase().includes(kw))
|
|
198
|
+
score += 2;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (score > 0) {
|
|
202
|
+
matches.push({
|
|
203
|
+
element: el,
|
|
204
|
+
score,
|
|
205
|
+
selector: this.buildSelector(el),
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
// Sort by score descending
|
|
210
|
+
matches.sort((a, b) => b.score - a.score);
|
|
211
|
+
if (matches.length === 0) {
|
|
212
|
+
return {
|
|
213
|
+
primary: "",
|
|
214
|
+
fallbacks: [],
|
|
215
|
+
description,
|
|
216
|
+
confirmed: false,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
return {
|
|
220
|
+
primary: matches[0].selector,
|
|
221
|
+
fallbacks: matches.slice(1, 4).map((m) => m.selector),
|
|
222
|
+
description,
|
|
223
|
+
confirmed: false,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Find input element selector
|
|
228
|
+
*/
|
|
229
|
+
findInputSelector(elements, keywords, description) {
|
|
230
|
+
const inputs = elements.filter((el) => el.tag === "input" || el.tag === "textarea");
|
|
231
|
+
if (inputs.length === 0) {
|
|
232
|
+
// Fall back to general search
|
|
233
|
+
return this.findSelector(elements, keywords, description);
|
|
234
|
+
}
|
|
235
|
+
return this.findSelector(inputs, keywords, description);
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Find textarea selector
|
|
239
|
+
*/
|
|
240
|
+
findTextareaSelector(elements, keywords, description) {
|
|
241
|
+
const textareas = elements.filter((el) => el.tag === "textarea");
|
|
242
|
+
if (textareas.length === 0) {
|
|
243
|
+
return this.findSelector(elements, keywords, description);
|
|
244
|
+
}
|
|
245
|
+
return this.findSelector(textareas, keywords, description);
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Find file input element
|
|
249
|
+
*/
|
|
250
|
+
async findFileInput() {
|
|
251
|
+
if (!this.page) {
|
|
252
|
+
return {
|
|
253
|
+
primary: "",
|
|
254
|
+
fallbacks: [],
|
|
255
|
+
description: "File input element",
|
|
256
|
+
confirmed: false,
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
// Look for file input directly
|
|
260
|
+
const fileInputs = await this.page.$$('input[type="file"]');
|
|
261
|
+
if (fileInputs.length > 0) {
|
|
262
|
+
// Try to get a more specific selector
|
|
263
|
+
const selector = await this.page.evaluate((el) => {
|
|
264
|
+
// @ts-expect-error - DOM types available in browser context
|
|
265
|
+
const input = el;
|
|
266
|
+
if (input.id)
|
|
267
|
+
return `#${input.id}`;
|
|
268
|
+
if (input.name)
|
|
269
|
+
return `input[type="file"][name="${input.name}"]`;
|
|
270
|
+
return 'input[type="file"]';
|
|
271
|
+
}, fileInputs[0]);
|
|
272
|
+
return {
|
|
273
|
+
primary: selector,
|
|
274
|
+
fallbacks: ['input[type="file"]'],
|
|
275
|
+
description: "File input element",
|
|
276
|
+
confirmed: true,
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
return {
|
|
280
|
+
primary: 'input[type="file"]',
|
|
281
|
+
fallbacks: [],
|
|
282
|
+
description: "File input element",
|
|
283
|
+
confirmed: false,
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Build a CSS selector for an element
|
|
288
|
+
*/
|
|
289
|
+
buildSelector(el) {
|
|
290
|
+
// Priority: ID > unique class > aria-label > data attribute > tag + position
|
|
291
|
+
if (el.id) {
|
|
292
|
+
return `#${CSS.escape(el.id)}`;
|
|
293
|
+
}
|
|
294
|
+
if (el.ariaLabel) {
|
|
295
|
+
return `[aria-label="${CSS.escape(el.ariaLabel)}"]`;
|
|
296
|
+
}
|
|
297
|
+
// Try to use a unique class
|
|
298
|
+
const classes = (el.classes || "").split(/\s+/).filter(Boolean);
|
|
299
|
+
for (const cls of classes) {
|
|
300
|
+
// Skip generic classes
|
|
301
|
+
if (cls.length > 3 && !["button", "btn", "input", "text"].includes(cls.toLowerCase())) {
|
|
302
|
+
return `.${CSS.escape(cls)}`;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
// Use data attribute if available
|
|
306
|
+
for (const [attr, value] of Object.entries(el.dataAttrs)) {
|
|
307
|
+
if (value) {
|
|
308
|
+
return `[${attr}="${CSS.escape(value)}"]`;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
// Fall back to role
|
|
312
|
+
if (el.role) {
|
|
313
|
+
return `[role="${el.role}"]`;
|
|
314
|
+
}
|
|
315
|
+
// Last resort: tag
|
|
316
|
+
return el.tag;
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Try to click the new notebook button
|
|
320
|
+
*/
|
|
321
|
+
async tryClickNewNotebook(selectorInfo) {
|
|
322
|
+
if (!this.page || !selectorInfo?.primary)
|
|
323
|
+
return false;
|
|
324
|
+
const selectors = [selectorInfo.primary, ...selectorInfo.fallbacks];
|
|
325
|
+
for (const selector of selectors) {
|
|
326
|
+
try {
|
|
327
|
+
const element = await this.page.$(selector);
|
|
328
|
+
if (element && await element.isVisible()) {
|
|
329
|
+
await realisticClick(this.page, selector, true);
|
|
330
|
+
return true;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
catch {
|
|
334
|
+
continue;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
// Try common patterns for "new" buttons
|
|
338
|
+
const commonPatterns = [
|
|
339
|
+
'button:has-text("New")',
|
|
340
|
+
'button:has-text("Create")',
|
|
341
|
+
'[aria-label*="new" i]',
|
|
342
|
+
'[aria-label*="create" i]',
|
|
343
|
+
'button[data-action="create"]',
|
|
344
|
+
'.create-button',
|
|
345
|
+
'.new-button',
|
|
346
|
+
];
|
|
347
|
+
for (const pattern of commonPatterns) {
|
|
348
|
+
try {
|
|
349
|
+
const element = await this.page.$(pattern);
|
|
350
|
+
if (element && await element.isVisible()) {
|
|
351
|
+
await element.click();
|
|
352
|
+
return true;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
catch {
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
return false;
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Try to click an element by selector
|
|
363
|
+
*/
|
|
364
|
+
async tryClick(selector) {
|
|
365
|
+
if (!this.page)
|
|
366
|
+
return false;
|
|
367
|
+
try {
|
|
368
|
+
const element = await this.page.$(selector);
|
|
369
|
+
if (element && await element.isVisible()) {
|
|
370
|
+
await element.click();
|
|
371
|
+
return true;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
catch {
|
|
375
|
+
return false;
|
|
376
|
+
}
|
|
377
|
+
return false;
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Log discovery results summary
|
|
381
|
+
*/
|
|
382
|
+
logDiscoveryResults(result) {
|
|
383
|
+
log.info("\nš Discovery Results Summary:");
|
|
384
|
+
log.info(` Homepage elements: ${result.homepageElements.length}`);
|
|
385
|
+
log.info(` Creation UI elements: ${result.creationElements.length}`);
|
|
386
|
+
log.info(` Source UI elements: ${result.sourceElements.length}`);
|
|
387
|
+
log.info("\nšÆ Discovered Selectors:");
|
|
388
|
+
for (const [key, info] of Object.entries(result.selectors)) {
|
|
389
|
+
if (info && typeof info === 'object' && 'primary' in info) {
|
|
390
|
+
const selectorInfo = info;
|
|
391
|
+
const status = selectorInfo.primary ? "ā" : "ā";
|
|
392
|
+
log.info(` ${status} ${key}: ${selectorInfo.primary || "(not found)"}`);
|
|
393
|
+
if (selectorInfo.fallbacks.length > 0) {
|
|
394
|
+
log.info(` Fallbacks: ${selectorInfo.fallbacks.join(", ")}`);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
if (result.errors.length > 0) {
|
|
399
|
+
log.warning("\nā ļø Errors encountered:");
|
|
400
|
+
for (const err of result.errors) {
|
|
401
|
+
log.warning(` - ${err}`);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* CSS.escape polyfill for Node.js
|
|
408
|
+
*/
|
|
409
|
+
const CSS = {
|
|
410
|
+
escape: (value) => {
|
|
411
|
+
return value.replace(/([!"#$%&'()*+,./:;<=>?@[\\\]^`{|}~])/g, "\\$1");
|
|
412
|
+
},
|
|
413
|
+
};
|
|
414
|
+
/**
|
|
415
|
+
* Run selector discovery and return results
|
|
416
|
+
*/
|
|
417
|
+
export async function discoverSelectors(context) {
|
|
418
|
+
const discovery = new SelectorDiscovery(context);
|
|
419
|
+
return await discovery.discover();
|
|
420
|
+
}
|
|
421
|
+
//# sourceMappingURL=selector-discovery.js.map
|