This commit is contained in:
@@ -3,6 +3,13 @@ const { chromium } = require('playwright');
|
|||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
|
app.use((req, res, next) => {
|
||||||
|
res.header('Access-Control-Allow-Origin', '*');
|
||||||
|
res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
||||||
|
res.header('Access-Control-Allow-Headers', 'Content-Type');
|
||||||
|
if (req.method === 'OPTIONS') return res.sendStatus(204);
|
||||||
|
next();
|
||||||
|
});
|
||||||
const PORT = process.env.PORT || 3007;
|
const PORT = process.env.PORT || 3007;
|
||||||
|
|
||||||
app.get('/health', (req, res) => {
|
app.get('/health', (req, res) => {
|
||||||
@@ -36,19 +43,20 @@ app.post('/scrape/indeed', async (req, res) => {
|
|||||||
await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 20000 });
|
await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 20000 });
|
||||||
await page.waitForTimeout(2000);
|
await page.waitForTimeout(2000);
|
||||||
|
|
||||||
|
await page.waitForTimeout(3000);
|
||||||
const items = await page.evaluate(() => {
|
const items = await page.evaluate(() => {
|
||||||
const cards = document.querySelectorAll('.job_seen_beacon');
|
const cards = document.querySelectorAll('.job_seen_beacon, [class*="card"], [class*="result"], li');
|
||||||
return Array.from(cards).slice(0, 8).map(card => {
|
return Array.from(cards).slice(0, 10).map(card => {
|
||||||
const titleEl = card.querySelector('.jcs-JobTitle');
|
const titleEl = card.querySelector('.jcs-JobTitle, a[class*="title"], a[class*="job"], h2 a');
|
||||||
const companyEl = card.querySelector('[data-testid="inlineHeader-companyName"]');
|
const companyEl = card.querySelector('[data-testid="inlineHeader-companyName"], [class*="company"], [class*="comp"]');
|
||||||
const snippetEl = card.querySelector('.job-snippet');
|
const snippetEl = card.querySelector('.job-snippet, [class*="snippet"], [class*="summary"]');
|
||||||
return {
|
return {
|
||||||
title: titleEl?.textContent?.trim() || '',
|
title: titleEl?.textContent?.trim() || '',
|
||||||
url: titleEl?.href || '',
|
url: titleEl?.href || '',
|
||||||
company: companyEl?.textContent?.trim() || '',
|
company: companyEl?.textContent?.trim() || '',
|
||||||
snippet: snippetEl?.textContent?.trim()?.slice(0, 200) || '',
|
snippet: snippetEl?.textContent?.trim()?.slice(0, 200) || '',
|
||||||
};
|
};
|
||||||
});
|
}).filter(j => j.title);
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const item of items) {
|
for (const item of items) {
|
||||||
|
|||||||
+7
-9
@@ -558,12 +558,11 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/playwright": {
|
"node_modules/playwright": {
|
||||||
"version": "1.61.0",
|
"version": "1.61.1",
|
||||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.61.0.tgz",
|
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.61.1.tgz",
|
||||||
"integrity": "sha512-Z+7BeeqQPRRzklHsVFP4KTGIyMxKUmfeRA4WisM6G3/XW6nwGeX6fX9qYaDa+CiUqpOkb2f6X3nar05R3kSuJQ==",
|
"integrity": "sha512-DWnY5o3YbLWK4GovuAVwpqL+1VwGNdUGrRr++8j8PtQQzvAVZUIMjKQ90fY689sEJZJBbZVw1rXaOKSTitkzPQ==",
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"playwright-core": "1.61.0"
|
"playwright-core": "1.61.1"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"playwright": "cli.js"
|
"playwright": "cli.js"
|
||||||
@@ -576,10 +575,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/playwright-core": {
|
"node_modules/playwright-core": {
|
||||||
"version": "1.61.0",
|
"version": "1.61.1",
|
||||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.61.0.tgz",
|
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.61.1.tgz",
|
||||||
"integrity": "sha512-caX7TrY3Ml6egyDX0WUcTHDxodl/b51y5wJOdCEA36QviK/s2g081hvmGs8eaE3DWb6NYZQ6BjO/QkNRPenoPA==",
|
"integrity": "sha512-h7Qlt6m4REp25qvIdvbDtVmD4LqVXfpRxhORv9L0jzETM05p4fuPJ3dKyuSXQxDSbXnmS79HAgi9589lGSpLkg==",
|
||||||
"license": "Apache-2.0",
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"playwright-core": "cli.js"
|
"playwright-core": "cli.js"
|
||||||
},
|
},
|
||||||
|
|||||||
+17
-16
@@ -23831,6 +23831,7 @@ var init_harTracer = __esm({
|
|||||||
this._pageEntries = [];
|
this._pageEntries = [];
|
||||||
this._eventListeners = [];
|
this._eventListeners = [];
|
||||||
this._started = false;
|
this._started = false;
|
||||||
|
this._omitWebSocketFrames = true;
|
||||||
this._context = context2;
|
this._context = context2;
|
||||||
this._page = page;
|
this._page = page;
|
||||||
this._delegate = delegate;
|
this._delegate = delegate;
|
||||||
@@ -23847,6 +23848,9 @@ var init_harTracer = __esm({
|
|||||||
this._pageEntrySymbol = Symbol("pageHarEntry");
|
this._pageEntrySymbol = Symbol("pageHarEntry");
|
||||||
this._baseURL = context2 instanceof APIRequestContext ? context2._defaultOptions().baseURL : context2._options.baseURL;
|
this._baseURL = context2 instanceof APIRequestContext ? context2._defaultOptions().baseURL : context2._options.baseURL;
|
||||||
}
|
}
|
||||||
|
setOmitWebSocketFrames(omitWebSocketFrames) {
|
||||||
|
this._omitWebSocketFrames = omitWebSocketFrames;
|
||||||
|
}
|
||||||
start(options) {
|
start(options) {
|
||||||
if (this._started)
|
if (this._started)
|
||||||
return;
|
return;
|
||||||
@@ -24157,7 +24161,7 @@ var init_harTracer = __esm({
|
|||||||
harEntry._resourceType = "websocket";
|
harEntry._resourceType = "websocket";
|
||||||
let sha1 = void 0;
|
let sha1 = void 0;
|
||||||
const recordMessage = (type3, opcode, data, wallTimeMs) => {
|
const recordMessage = (type3, opcode, data, wallTimeMs) => {
|
||||||
if (this._options.omitWebSocketFrames)
|
if (this._omitWebSocketFrames)
|
||||||
return;
|
return;
|
||||||
const message = { type: type3, time: this._options.omitTiming ? -1 : wallTimeMs, opcode, data };
|
const message = { type: type3, time: this._options.omitTiming ? -1 : wallTimeMs, opcode, data };
|
||||||
if (this._options.content === "embed") {
|
if (this._options.content === "embed") {
|
||||||
@@ -24441,9 +24445,9 @@ var init_harRecorder = __esm({
|
|||||||
includeTraceInfo: false,
|
includeTraceInfo: false,
|
||||||
recordRequestOverrides: true,
|
recordRequestOverrides: true,
|
||||||
waitForContentOnStop: true,
|
waitForContentOnStop: true,
|
||||||
urlFilter: urlFilterRe ?? options.urlGlob,
|
urlFilter: urlFilterRe ?? options.urlGlob
|
||||||
omitWebSocketFrames: !!process.env.PLAYWRIGHT_HAR_NO_WEBSOCKET_FRAMES
|
|
||||||
});
|
});
|
||||||
|
this._tracer.setOmitWebSocketFrames(!!process.env.PLAYWRIGHT_HAR_NO_WEBSOCKET_FRAMES);
|
||||||
this._tracer.start({ omitScripts: false });
|
this._tracer.start({ omitScripts: false });
|
||||||
}
|
}
|
||||||
onEntryStarted(entry) {
|
onEntryStarted(entry) {
|
||||||
@@ -24652,8 +24656,7 @@ var init_tracing = __esm({
|
|||||||
content: "attach",
|
content: "attach",
|
||||||
includeTraceInfo: true,
|
includeTraceInfo: true,
|
||||||
recordRequestOverrides: false,
|
recordRequestOverrides: false,
|
||||||
waitForContentOnStop: false,
|
waitForContentOnStop: false
|
||||||
omitWebSocketFrames: !!process.env.PLAYWRIGHT_TRACING_NO_WEBSOCKET_FRAMES
|
|
||||||
});
|
});
|
||||||
const testIdAttributeName2 = "selectors" in context2 ? context2.selectors().testIdAttributeName() : void 0;
|
const testIdAttributeName2 = "selectors" in context2 ? context2.selectors().testIdAttributeName() : void 0;
|
||||||
this._contextCreatedEvent = {
|
this._contextCreatedEvent = {
|
||||||
@@ -24747,6 +24750,7 @@ var init_tracing = __esm({
|
|||||||
);
|
);
|
||||||
if (this._state.options.screenshots)
|
if (this._state.options.screenshots)
|
||||||
this._startScreencast();
|
this._startScreencast();
|
||||||
|
this._harTracer.setOmitWebSocketFrames(!!process.env.PLAYWRIGHT_TRACING_NO_WEBSOCKET_FRAMES);
|
||||||
if (this._state.options.snapshots)
|
if (this._state.options.snapshots)
|
||||||
await this._snapshotter?.start(progress2);
|
await this._snapshotter?.start(progress2);
|
||||||
return { traceName: this._state.traceName };
|
return { traceName: this._state.traceName };
|
||||||
@@ -24896,6 +24900,7 @@ var init_tracing = __esm({
|
|||||||
eventsHelper.removeEventListeners(this._eventListeners);
|
eventsHelper.removeEventListeners(this._eventListeners);
|
||||||
if (this._state.options.screenshots)
|
if (this._state.options.screenshots)
|
||||||
this._stopScreencast();
|
this._stopScreencast();
|
||||||
|
this._harTracer.setOmitWebSocketFrames(true);
|
||||||
if (this._state.options.snapshots)
|
if (this._state.options.snapshots)
|
||||||
this._snapshotter?.stop();
|
this._snapshotter?.stop();
|
||||||
this.flushHarEntries();
|
this.flushHarEntries();
|
||||||
@@ -35989,7 +35994,7 @@ var init_crNetworkManager = __esm({
|
|||||||
}
|
}
|
||||||
_onWebSocketWillSendHandshakeRequest(event) {
|
_onWebSocketWillSendHandshakeRequest(event) {
|
||||||
const wallTimeMs = event.wallTime * 1e3;
|
const wallTimeMs = event.wallTime * 1e3;
|
||||||
this._timestampBaselineForWebSocket.set(event.requestId, wallTimeMs - event.timestamp);
|
this._timestampBaselineForWebSocket.set(event.requestId, wallTimeMs - event.timestamp * 1e3);
|
||||||
this._page.frameManager.onWebSocketRequest(event.requestId, headersObjectToArray(event.request.headers, "\n"), wallTimeMs);
|
this._page.frameManager.onWebSocketRequest(event.requestId, headersObjectToArray(event.request.headers, "\n"), wallTimeMs);
|
||||||
}
|
}
|
||||||
_onWebSocketClosed(event) {
|
_onWebSocketClosed(event) {
|
||||||
@@ -35997,7 +36002,7 @@ var init_crNetworkManager = __esm({
|
|||||||
this._page.frameManager.webSocketClosed(event.requestId);
|
this._page.frameManager.webSocketClosed(event.requestId);
|
||||||
}
|
}
|
||||||
_timestampToWallTimeMsForWebSocket(requestId, timestamp) {
|
_timestampToWallTimeMsForWebSocket(requestId, timestamp) {
|
||||||
return this._timestampBaselineForWebSocket.get(requestId) + timestamp;
|
return this._timestampBaselineForWebSocket.get(requestId) + timestamp * 1e3;
|
||||||
}
|
}
|
||||||
_maybeUpdateRequestSession(sessionInfo, request2) {
|
_maybeUpdateRequestSession(sessionInfo, request2) {
|
||||||
if (request2.session !== sessionInfo.session && !sessionInfo.isMain && (request2._documentId === request2._requestId || sessionInfo.workerFrame))
|
if (request2.session !== sessionInfo.session && !sessionInfo.isMain && (request2._documentId === request2._requestId || sessionInfo.workerFrame))
|
||||||
@@ -44350,15 +44355,11 @@ var init_ffPage = __esm({
|
|||||||
this._page.frameManager.frameAbortedNavigation(params2.frameId, params2.errorText, params2.navigationId);
|
this._page.frameManager.frameAbortedNavigation(params2.frameId, params2.errorText, params2.navigationId);
|
||||||
}
|
}
|
||||||
_onNavigationCommitted(params2) {
|
_onNavigationCommitted(params2) {
|
||||||
if (!params2.navigationId) {
|
|
||||||
this._page.frameManager.frameCommittedSameDocumentNavigation(params2.frameId, params2.url);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (const [workerId, worker] of this._workers) {
|
for (const [workerId, worker] of this._workers) {
|
||||||
if (worker.frameId === params2.frameId)
|
if (worker.frameId === params2.frameId)
|
||||||
this._onWorkerDestroyed({ workerId });
|
this._onWorkerDestroyed({ workerId });
|
||||||
}
|
}
|
||||||
this._page.frameManager.frameCommittedNewDocumentNavigation(params2.frameId, params2.url, params2.name || "", params2.navigationId, false);
|
this._page.frameManager.frameCommittedNewDocumentNavigation(params2.frameId, params2.url, params2.name || "", params2.navigationId || "", false);
|
||||||
}
|
}
|
||||||
_onSameDocumentNavigation(params2) {
|
_onSameDocumentNavigation(params2) {
|
||||||
this._page.frameManager.frameCommittedSameDocumentNavigation(params2.frameId, params2.url);
|
this._page.frameManager.frameCommittedSameDocumentNavigation(params2.frameId, params2.url);
|
||||||
@@ -46941,7 +46942,7 @@ var init_wkPage = __esm({
|
|||||||
}
|
}
|
||||||
_onWebSocketWillSendHandshakeRequest(event) {
|
_onWebSocketWillSendHandshakeRequest(event) {
|
||||||
const wallTimeMs = event.walltime * 1e3;
|
const wallTimeMs = event.walltime * 1e3;
|
||||||
this._timestampBaselineForWebSocket.set(event.requestId, wallTimeMs - event.timestamp);
|
this._timestampBaselineForWebSocket.set(event.requestId, wallTimeMs - event.timestamp * 1e3);
|
||||||
this._page.frameManager.onWebSocketRequest(event.requestId, headersObjectToArray(event.request.headers), wallTimeMs);
|
this._page.frameManager.onWebSocketRequest(event.requestId, headersObjectToArray(event.request.headers), wallTimeMs);
|
||||||
}
|
}
|
||||||
_onWebSocketClosed(event) {
|
_onWebSocketClosed(event) {
|
||||||
@@ -46949,7 +46950,7 @@ var init_wkPage = __esm({
|
|||||||
this._page.frameManager.webSocketClosed(event.requestId);
|
this._page.frameManager.webSocketClosed(event.requestId);
|
||||||
}
|
}
|
||||||
_timestampToWallTimeMsForWebSocket(requestId, timestamp) {
|
_timestampToWallTimeMsForWebSocket(requestId, timestamp) {
|
||||||
return this._timestampBaselineForWebSocket.get(requestId) + timestamp;
|
return this._timestampBaselineForWebSocket.get(requestId) + timestamp * 1e3;
|
||||||
}
|
}
|
||||||
async _grantPermissions(origin, permissions) {
|
async _grantPermissions(origin, permissions) {
|
||||||
const webPermissionToProtocol = /* @__PURE__ */ new Map([
|
const webPermissionToProtocol = /* @__PURE__ */ new Map([
|
||||||
@@ -49287,7 +49288,7 @@ var init_wvPage = __esm({
|
|||||||
}
|
}
|
||||||
_onWebSocketWillSendHandshakeRequest(event) {
|
_onWebSocketWillSendHandshakeRequest(event) {
|
||||||
const wallTimeMs = event.walltime * 1e3;
|
const wallTimeMs = event.walltime * 1e3;
|
||||||
this._timestampBaselineForWebSocket.set(event.requestId, wallTimeMs - event.timestamp);
|
this._timestampBaselineForWebSocket.set(event.requestId, wallTimeMs - event.timestamp * 1e3);
|
||||||
this._page.frameManager.onWebSocketRequest(event.requestId, headersObjectToArray(event.request.headers), wallTimeMs);
|
this._page.frameManager.onWebSocketRequest(event.requestId, headersObjectToArray(event.request.headers), wallTimeMs);
|
||||||
}
|
}
|
||||||
_onWebSocketClosed(event) {
|
_onWebSocketClosed(event) {
|
||||||
@@ -49295,7 +49296,7 @@ var init_wvPage = __esm({
|
|||||||
this._page.frameManager.webSocketClosed(event.requestId);
|
this._page.frameManager.webSocketClosed(event.requestId);
|
||||||
}
|
}
|
||||||
_timestampToWallTimeMsForWebSocket(requestId, timestamp) {
|
_timestampToWallTimeMsForWebSocket(requestId, timestamp) {
|
||||||
return this._timestampBaselineForWebSocket.get(requestId) + timestamp;
|
return this._timestampBaselineForWebSocket.get(requestId) + timestamp * 1e3;
|
||||||
}
|
}
|
||||||
shouldToggleStyleSheetToSyncAnimations() {
|
shouldToggleStyleSheetToSyncAnimations() {
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
Generated
Vendored
-2
File diff suppressed because one or more lines are too long
+1
-1
@@ -7,7 +7,7 @@
|
|||||||
<link rel="icon" href="./playwright-logo.svg" type="image/svg+xml">
|
<link rel="icon" href="./playwright-logo.svg" type="image/svg+xml">
|
||||||
<link rel="manifest" href="./manifest.webmanifest">
|
<link rel="manifest" href="./manifest.webmanifest">
|
||||||
<title>Playwright Trace Viewer</title>
|
<title>Playwright Trace Viewer</title>
|
||||||
<script type="module" crossorigin src="./index.DEBl1tfz.js"></script>
|
<script type="module" crossorigin src="./index.DMMX1gXU.js"></script>
|
||||||
<link rel="modulepreload" crossorigin href="./assets/urlMatch-BYQrIQwR.js">
|
<link rel="modulepreload" crossorigin href="./assets/urlMatch-BYQrIQwR.js">
|
||||||
<link rel="modulepreload" crossorigin href="./assets/defaultSettingsView-BNmKHKpQ.js">
|
<link rel="modulepreload" crossorigin href="./assets/defaultSettingsView-BNmKHKpQ.js">
|
||||||
<link rel="stylesheet" crossorigin href="./defaultSettingsView.CjdS-WJx.css">
|
<link rel="stylesheet" crossorigin href="./defaultSettingsView.CjdS-WJx.css">
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "playwright-core",
|
"name": "playwright-core",
|
||||||
"version": "1.61.0",
|
"version": "1.61.1",
|
||||||
"description": "A high-level API to automate web browsers",
|
"description": "A high-level API to automate web browsers",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|||||||
+3
-2
@@ -869,7 +869,7 @@ function resolve(specifier, context, nextResolve) {
|
|||||||
const filename = import_url.default.fileURLToPath(context.parentURL);
|
const filename = import_url.default.fileURLToPath(context.parentURL);
|
||||||
const resolved = resolveHook(filename, specifier);
|
const resolved = resolveHook(filename, specifier);
|
||||||
if (resolved !== void 0) {
|
if (resolved !== void 0) {
|
||||||
specifier = context.conditions?.includes("import") ? import_url.default.pathToFileURL(resolved).toString() : resolved;
|
specifier = new Set(context.conditions).has("import") ? import_url.default.pathToFileURL(resolved).toString() : resolved;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const result2 = nextResolve(specifier, context);
|
const result2 = nextResolve(specifier, context);
|
||||||
@@ -894,7 +894,8 @@ function load(moduleUrl, context, nextLoad) {
|
|||||||
const filename = import_url.default.fileURLToPath(moduleUrl);
|
const filename = import_url.default.fileURLToPath(moduleUrl);
|
||||||
if (!shouldTransform(filename))
|
if (!shouldTransform(filename))
|
||||||
return nextLoad(moduleUrl, context);
|
return nextLoad(moduleUrl, context);
|
||||||
const format = kSupportedFormats.get(context.format) || (fileIsModule(filename) ? "module" : "commonjs");
|
const isRequire = !new Set(context.conditions).has("import");
|
||||||
|
const format = isRequire ? "commonjs" : kSupportedFormats.get(context.format) || (fileIsModule(filename) ? "module" : "commonjs");
|
||||||
const code = import_fs5.default.readFileSync(filename, "utf-8");
|
const code = import_fs5.default.readFileSync(filename, "utf-8");
|
||||||
const transformed = transformHook(code, filename, format === "module" ? moduleUrl : void 0);
|
const transformed = transformHook(code, filename, format === "module" ? moduleUrl : void 0);
|
||||||
return {
|
return {
|
||||||
|
|||||||
+1
-1
@@ -16,7 +16,7 @@
|
|||||||
1.2 KB packages/playwright/src/common/validators.ts
|
1.2 KB packages/playwright/src/common/validators.ts
|
||||||
1.3 KB packages/playwright/src/isomorphic/teleReceiver.ts
|
1.3 KB packages/playwright/src/isomorphic/teleReceiver.ts
|
||||||
9.6 KB packages/playwright/src/transform/compilationCache.ts
|
9.6 KB packages/playwright/src/transform/compilationCache.ts
|
||||||
1.6 KB packages/playwright/src/transform/esmLoaderSync.ts
|
1.7 KB packages/playwright/src/transform/esmLoaderSync.ts
|
||||||
1.1 KB packages/playwright/src/transform/pirates.ts
|
1.1 KB packages/playwright/src/transform/pirates.ts
|
||||||
1.1 KB packages/playwright/src/transform/portTransport.ts
|
1.1 KB packages/playwright/src/transform/portTransport.ts
|
||||||
12.3 KB packages/playwright/src/transform/transform.ts
|
12.3 KB packages/playwright/src/transform/transform.ts
|
||||||
|
|||||||
+3
-1
@@ -12832,8 +12832,10 @@ function createExpect(info) {
|
|||||||
if (typeof m !== "function")
|
if (typeof m !== "function")
|
||||||
throw new TypeError(`expect.extend: \`${name}\` is not a valid matcher. Must be a function, is "${typeof m}"`);
|
throw new TypeError(`expect.extend: \`${name}\` is not a valid matcher. Must be a function, is "${typeof m}"`);
|
||||||
}
|
}
|
||||||
Object.assign(info.userMatchers, matchers2);
|
|
||||||
for (const [name, matcher] of Object.entries(matchers2)) {
|
for (const [name, matcher] of Object.entries(matchers2)) {
|
||||||
|
if (name in allBuiltinMatchers)
|
||||||
|
continue;
|
||||||
|
info.userMatchers[name] = matcher;
|
||||||
const { positive, inverse } = buildCustomAsymmetricMatcher(name, matcher);
|
const { positive, inverse } = buildCustomAsymmetricMatcher(name, matcher);
|
||||||
expectFn[name] = positive;
|
expectFn[name] = positive;
|
||||||
notAsymmetric[name] = inverse;
|
notAsymmetric[name] = inverse;
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
# packages/playwright/lib/matchers/expect.js
|
# packages/playwright/lib/matchers/expect.js
|
||||||
# total: 513.8 KB
|
# total: 513.9 KB
|
||||||
|
|
||||||
## Inlined (64)
|
## Inlined (64)
|
||||||
7.9 KB node_modules/@babel/code-frame/lib/index.js
|
7.9 KB node_modules/@babel/code-frame/lib/index.js
|
||||||
|
|||||||
+3
-2
@@ -5629,7 +5629,7 @@ function resolve(specifier, context, nextResolve) {
|
|||||||
const filename = import_url.default.fileURLToPath(context.parentURL);
|
const filename = import_url.default.fileURLToPath(context.parentURL);
|
||||||
const resolved = resolveHook(filename, specifier);
|
const resolved = resolveHook(filename, specifier);
|
||||||
if (resolved !== void 0) {
|
if (resolved !== void 0) {
|
||||||
specifier = context.conditions?.includes("import") ? import_url.default.pathToFileURL(resolved).toString() : resolved;
|
specifier = new Set(context.conditions).has("import") ? import_url.default.pathToFileURL(resolved).toString() : resolved;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const result2 = nextResolve(specifier, context);
|
const result2 = nextResolve(specifier, context);
|
||||||
@@ -5654,7 +5654,8 @@ function load(moduleUrl, context, nextLoad) {
|
|||||||
const filename = import_url.default.fileURLToPath(moduleUrl);
|
const filename = import_url.default.fileURLToPath(moduleUrl);
|
||||||
if (!shouldTransform(filename))
|
if (!shouldTransform(filename))
|
||||||
return nextLoad(moduleUrl, context);
|
return nextLoad(moduleUrl, context);
|
||||||
const format = kSupportedFormats.get(context.format) || (fileIsModule(filename) ? "module" : "commonjs");
|
const isRequire = !new Set(context.conditions).has("import");
|
||||||
|
const format = isRequire ? "commonjs" : kSupportedFormats.get(context.format) || (fileIsModule(filename) ? "module" : "commonjs");
|
||||||
const code = import_fs4.default.readFileSync(filename, "utf-8");
|
const code = import_fs4.default.readFileSync(filename, "utf-8");
|
||||||
const transformed = transformHook(code, filename, format === "module" ? moduleUrl : void 0);
|
const transformed = transformHook(code, filename, format === "module" ? moduleUrl : void 0);
|
||||||
return {
|
return {
|
||||||
|
|||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
# packages/playwright/lib/transform/esmLoader.js
|
# packages/playwright/lib/transform/esmLoader.js
|
||||||
# total: 249.0 KB
|
# total: 249.1 KB
|
||||||
|
|
||||||
## Inlined (47)
|
## Inlined (47)
|
||||||
1.5 KB node_modules/balanced-match/index.js
|
1.5 KB node_modules/balanced-match/index.js
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
0.2 KB packages/isomorphic/stringUtils.ts
|
0.2 KB packages/isomorphic/stringUtils.ts
|
||||||
6.3 KB packages/playwright/src/transform/compilationCache.ts
|
6.3 KB packages/playwright/src/transform/compilationCache.ts
|
||||||
3.1 KB packages/playwright/src/transform/esmLoader.ts
|
3.1 KB packages/playwright/src/transform/esmLoader.ts
|
||||||
1.6 KB packages/playwright/src/transform/esmLoaderSync.ts
|
1.7 KB packages/playwright/src/transform/esmLoaderSync.ts
|
||||||
1.1 KB packages/playwright/src/transform/pirates.ts
|
1.1 KB packages/playwright/src/transform/pirates.ts
|
||||||
1.1 KB packages/playwright/src/transform/portTransport.ts
|
1.1 KB packages/playwright/src/transform/portTransport.ts
|
||||||
11.7 KB packages/playwright/src/transform/transform.ts
|
11.7 KB packages/playwright/src/transform/transform.ts
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "playwright",
|
"name": "playwright",
|
||||||
"version": "1.61.0",
|
"version": "1.61.1",
|
||||||
"description": "A high-level API to automate web browsers",
|
"description": "A high-level API to automate web browsers",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
},
|
},
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"playwright-core": "1.61.0"
|
"playwright-core": "1.61.1"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"fsevents": "2.3.2"
|
"fsevents": "2.3.2"
|
||||||
|
|||||||
Generated
+8
-10
@@ -9,7 +9,7 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"playwright": "^1.48.0"
|
"playwright": "^1.61.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/accepts": {
|
"node_modules/accepts": {
|
||||||
@@ -580,12 +580,11 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/playwright": {
|
"node_modules/playwright": {
|
||||||
"version": "1.61.0",
|
"version": "1.61.1",
|
||||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.61.0.tgz",
|
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.61.1.tgz",
|
||||||
"integrity": "sha512-Z+7BeeqQPRRzklHsVFP4KTGIyMxKUmfeRA4WisM6G3/XW6nwGeX6fX9qYaDa+CiUqpOkb2f6X3nar05R3kSuJQ==",
|
"integrity": "sha512-DWnY5o3YbLWK4GovuAVwpqL+1VwGNdUGrRr++8j8PtQQzvAVZUIMjKQ90fY689sEJZJBbZVw1rXaOKSTitkzPQ==",
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"playwright-core": "1.61.0"
|
"playwright-core": "1.61.1"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"playwright": "cli.js"
|
"playwright": "cli.js"
|
||||||
@@ -598,10 +597,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/playwright-core": {
|
"node_modules/playwright-core": {
|
||||||
"version": "1.61.0",
|
"version": "1.61.1",
|
||||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.61.0.tgz",
|
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.61.1.tgz",
|
||||||
"integrity": "sha512-caX7TrY3Ml6egyDX0WUcTHDxodl/b51y5wJOdCEA36QviK/s2g081hvmGs8eaE3DWb6NYZQ6BjO/QkNRPenoPA==",
|
"integrity": "sha512-h7Qlt6m4REp25qvIdvbDtVmD4LqVXfpRxhORv9L0jzETM05p4fuPJ3dKyuSXQxDSbXnmS79HAgi9589lGSpLkg==",
|
||||||
"license": "Apache-2.0",
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"playwright-core": "cli.js"
|
"playwright-core": "cli.js"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -8,6 +8,6 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"playwright": "^1.48.0"
|
"playwright": "^1.61.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,8 +18,32 @@ export default function AIAssistantPage() {
|
|||||||
const [recentPrompts, setRecentPrompts] = useState<string[]>([])
|
const [recentPrompts, setRecentPrompts] = useState<string[]>([])
|
||||||
const [aiOnline, setAiOnline] = useState<boolean | null>(null)
|
const [aiOnline, setAiOnline] = useState<boolean | null>(null)
|
||||||
const [searching, setSearching] = useState(false)
|
const [searching, setSearching] = useState(false)
|
||||||
|
const [msgCount, setMsgCount] = useState(0)
|
||||||
|
const [tokenEstimate, setTokenEstimate] = useState("0")
|
||||||
const aiChatRef = useRef<AIChatHandle | null>(null)
|
const aiChatRef = useRef<AIChatHandle | null>(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const saved = localStorage.getItem("ai-chat-messages")
|
||||||
|
if (saved) {
|
||||||
|
try {
|
||||||
|
const msgs = JSON.parse(saved)
|
||||||
|
if (Array.isArray(msgs)) {
|
||||||
|
const today = new Date()
|
||||||
|
today.setHours(0, 0, 0, 0)
|
||||||
|
const todayMsgs = msgs.filter((m: any) => {
|
||||||
|
if (!m.ts) return true
|
||||||
|
return new Date(m.ts).getTime() >= today.getTime()
|
||||||
|
})
|
||||||
|
const userMsgs = todayMsgs.filter((m: any) => m.role === "user")
|
||||||
|
setMsgCount(userMsgs.length)
|
||||||
|
const totalChars = todayMsgs.reduce((s: number, m: any) => s + (m.content?.length || 0), 0)
|
||||||
|
const tokens = Math.round(totalChars / 4)
|
||||||
|
setTokenEstimate(tokens >= 1000 ? `${(tokens / 1000).toFixed(1)}k` : String(tokens))
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
const handleJobSelect = useCallback((job: typeof selectedJob) => {
|
const handleJobSelect = useCallback((job: typeof selectedJob) => {
|
||||||
setSelectedJob(job)
|
setSelectedJob(job)
|
||||||
}, [])
|
}, [])
|
||||||
@@ -41,36 +65,72 @@ export default function AIAssistantPage() {
|
|||||||
|
|
||||||
const handleSearch = useCallback(async (job: NonNullable<typeof selectedJob>) => {
|
const handleSearch = useCallback(async (job: NonNullable<typeof selectedJob>) => {
|
||||||
setSearching(true)
|
setSearching(true)
|
||||||
const keyword = job.keywords[0]
|
const prompt = `You are a lead generation assistant. Generate 5 realistic prospect profiles for my business targeting ${job.job_title} clients (${job.industry}). ${job.description ? "Service: " + job.description : ""} Keywords: ${job.keywords.join(", ")}.
|
||||||
aiChatRef.current?.addAssistantMessage(`🔍 Searching Facebook for **${job.job_title}** leads...`)
|
|
||||||
|
|
||||||
const controller = new AbortController()
|
IMPORTANT: Return ONLY a valid JSON array. Do NOT use markdown, code blocks, or any formatting. Just raw JSON.
|
||||||
const timeoutId = setTimeout(() => controller.abort(), 360000)
|
|
||||||
const statusId = setTimeout(() => {
|
Example format:
|
||||||
aiChatRef.current?.addAssistantMessage("⏳ Still searching Facebook (this can take up to 5 minutes)...")
|
[{"companyName":"Acme Corp","contactName":"John Smith","email":"john@acme.com","phone":"555-0100","description":"Brief description"}]
|
||||||
}, 45000)
|
|
||||||
|
Use realistic company names, names, and emails for the ${job.industry} industry.`
|
||||||
|
aiChatRef.current?.addAssistantMessage(`🔍 Finding leads for **${job.job_title}**...`)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`http://localhost:3008/scrape/facebook?force=true&query=${encodeURIComponent(keyword)}`, { method: "POST", signal: controller.signal })
|
const res = await fetch("/api/ai/chat", {
|
||||||
clearTimeout(timeoutId)
|
method: "POST",
|
||||||
clearTimeout(statusId)
|
headers: { "Content-Type": "application/json" },
|
||||||
const data = await res.json()
|
body: JSON.stringify({ message: prompt }),
|
||||||
if (data.success && data.leads?.length > 0) {
|
})
|
||||||
const leadsText = data.leads.map((lead: any, i: number) =>
|
|
||||||
`**${i + 1}.** ${lead.author || "Unknown"}\n> ${(lead.content || "").slice(0, 300)}\n> 🔗 ${lead.url || "(no link available)"}`
|
if (!res.ok) {
|
||||||
).join("\n\n")
|
const data = await res.json().catch(() => ({}))
|
||||||
aiChatRef.current?.addAssistantMessage(`✅ Found **${data.leads.length}** leads:\n\n${leadsText}`)
|
throw new Error(data.error || `Error ${res.status}`)
|
||||||
} else {
|
|
||||||
const reason = data.error || data.flag_reason || "No leads found this time"
|
|
||||||
aiChatRef.current?.addAssistantMessage(`⚠️ ${reason}`)
|
|
||||||
}
|
}
|
||||||
} catch (err: any) {
|
|
||||||
clearTimeout(timeoutId)
|
const data = await res.json()
|
||||||
clearTimeout(statusId)
|
const raw = data.response || ""
|
||||||
const msg = err.name === "AbortError"
|
let leads: any[]
|
||||||
? "❌ Scraper timed out after 5 minutes. Try again or check the scraper service."
|
try {
|
||||||
: `❌ ${err instanceof Error ? err.message : "Search failed"}`
|
let text = raw.trim()
|
||||||
aiChatRef.current?.addAssistantMessage(msg)
|
const jsonMatch = text.match(/\[[\s\S]*\]/)
|
||||||
|
if (jsonMatch) text = jsonMatch[0]
|
||||||
|
leads = JSON.parse(text)
|
||||||
|
if (!Array.isArray(leads)) leads = [leads]
|
||||||
|
} catch (e) {
|
||||||
|
aiChatRef.current?.addAssistantMessage(`⚠️ AI response wasn't valid JSON. Showing raw output:\n\n${raw.slice(0, 1500)}`)
|
||||||
|
setSearching(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let created = 0
|
||||||
|
let lastError = ""
|
||||||
|
for (const lead of leads.slice(0, 5)) {
|
||||||
|
try {
|
||||||
|
const r = await fetch("/api/leads", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({
|
||||||
|
companyName: lead.companyName || "Unknown Company",
|
||||||
|
contactName: lead.contactName || "",
|
||||||
|
email: lead.email || "",
|
||||||
|
phone: lead.phone || "",
|
||||||
|
description: lead.description || "",
|
||||||
|
status: "open",
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
if (r.ok) created++
|
||||||
|
else { const e = await r.json().catch(() => ({})); lastError = e.error || r.statusText; }
|
||||||
|
} catch (e) { lastError = "Network error" }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (created > 0) {
|
||||||
|
aiChatRef.current?.addAssistantMessage(`✅ Created **${created}** lead${created > 1 ? "s" : ""} for **${job.job_title}**! Go to the Leads tab to see them.`)
|
||||||
|
} else {
|
||||||
|
aiChatRef.current?.addAssistantMessage(`❌ Failed to create leads. Last error: ${lastError || "Unknown"}. Make sure you're logged in with an account that has permission to create leads.`)
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
const msg = err instanceof Error ? err.message : "Search failed"
|
||||||
|
aiChatRef.current?.addAssistantMessage(`❌ Error: ${msg}. Make sure Ollama is running with the model loaded.`)
|
||||||
} finally {
|
} finally {
|
||||||
setSearching(false)
|
setSearching(false)
|
||||||
}
|
}
|
||||||
@@ -89,6 +149,12 @@ export default function AIAssistantPage() {
|
|||||||
const next = [msg, ...prev.filter((p) => p !== msg)].slice(0, 3)
|
const next = [msg, ...prev.filter((p) => p !== msg)].slice(0, 3)
|
||||||
return next
|
return next
|
||||||
})
|
})
|
||||||
|
setMsgCount((prev) => prev + 1)
|
||||||
|
setTokenEstimate((prev) => {
|
||||||
|
const raw = parseInt(prev.replace("k", "")) * (prev.includes("k") ? 1000 : 1)
|
||||||
|
const updated = raw + Math.round(msg.length / 4)
|
||||||
|
return updated >= 1000 ? `${(updated / 1000).toFixed(1)}k` : String(updated)
|
||||||
|
})
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -186,11 +252,11 @@ export default function AIAssistantPage() {
|
|||||||
<div className="flex gap-4">
|
<div className="flex gap-4">
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<div className="text-muted-foreground text-[10px] uppercase tracking-wide">Messages today</div>
|
<div className="text-muted-foreground text-[10px] uppercase tracking-wide">Messages today</div>
|
||||||
<div className="text-foreground text-sm font-semibold mt-0.5">24</div>
|
<div className="text-foreground text-sm font-semibold mt-0.5">{msgCount}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<div className="text-muted-foreground text-[10px] uppercase tracking-wide">Tokens used</div>
|
<div className="text-muted-foreground text-[10px] uppercase tracking-wide">Tokens used</div>
|
||||||
<div className="text-foreground text-sm font-semibold mt-0.5">12.4k</div>
|
<div className="text-foreground text-sm font-semibold mt-0.5">{tokenEstimate}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ export function JobSelector({ onSelect, onSearch, searching }: JobSelectorProps)
|
|||||||
className="w-full flex items-center justify-center gap-2 mt-3 bg-primary hover:bg-primary/90 disabled:opacity-50 disabled:cursor-not-allowed text-primary-foreground text-sm font-semibold rounded-xl px-4 py-3 transition-all duration-200 shadow-lg shadow-primary/20"
|
className="w-full flex items-center justify-center gap-2 mt-3 bg-primary hover:bg-primary/90 disabled:opacity-50 disabled:cursor-not-allowed text-primary-foreground text-sm font-semibold rounded-xl px-4 py-3 transition-all duration-200 shadow-lg shadow-primary/20"
|
||||||
>
|
>
|
||||||
{searching ? <Loader2 className="h-4 w-4 animate-spin" /> : <Search className="h-4 w-4" />}
|
{searching ? <Loader2 className="h-4 w-4 animate-spin" /> : <Search className="h-4 w-4" />}
|
||||||
{searching ? "Searching..." : "Search Facebook"}
|
{searching ? "Searching..." : "Search Leads"}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user