lazy-apply 0.1.0 → 0.1.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/dist/cli.js CHANGED
@@ -6,7 +6,7 @@ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node
6
6
  import { homedir, hostname, platform, tmpdir, userInfo } from "node:os";
7
7
  import { dirname, join } from "node:path";
8
8
  import { createInterface } from "node:readline/promises";
9
- var VERSION = true ? "0.1.0" : "dev";
9
+ var VERSION = true ? "0.1.1" : "dev";
10
10
  var DEFAULT_APP = "https://lazyapply.online";
11
11
  var HOME = join(homedir(), ".lazy-apply");
12
12
  var CONFIG = join(HOME, "config.json");
package/dist/runner.js CHANGED
@@ -380,6 +380,48 @@ async function findEmptyRequiredLive(page) {
380
380
  return page.evaluate(findEmptyRequired);
381
381
  }
382
382
 
383
+ // src/lib/apply/autocomplete.ts
384
+ async function pickSuggestion(page, typed, patience = 1300) {
385
+ const items = page.locator('[role="option"], [role="listbox"] li, [class*="dropdown-location"], [class*="dropdown-results"] > *, .pac-item, [class*="suggestion"] li, [class*="autocomplete"] li, [class*="autocomplete"] [class*="item"]').filter({ visible: true });
386
+ const until = Date.now() + patience;
387
+ await page.waitForTimeout(Math.min(700, patience));
388
+ while (!await items.count() && Date.now() < until) await page.waitForTimeout(250);
389
+ const n = await items.count();
390
+ if (!n) return void 0;
391
+ const texts = (await items.allInnerTexts()).map((t) => t.replace(/\s+/g, " ").trim());
392
+ const want = typed.toLowerCase();
393
+ let i = texts.findIndex((t) => t.toLowerCase().startsWith(want));
394
+ if (i < 0) i = texts.findIndex((t) => t.toLowerCase().includes(want));
395
+ if (i < 0) i = 0;
396
+ await items.nth(i).click();
397
+ await page.waitForTimeout(500);
398
+ return texts[i];
399
+ }
400
+ async function leverLocationPicked(page) {
401
+ const hidden = page.locator("#selected-location");
402
+ if (!await hidden.count()) return true;
403
+ return !!(await hidden.inputValue().catch(() => "")).trim();
404
+ }
405
+ async function ensureLeverLocation(page, city) {
406
+ const input = page.locator("#location-input");
407
+ if (!await input.count() || !city) return void 0;
408
+ for (const how of ["click", "keyboard"]) {
409
+ await input.scrollIntoViewIfNeeded();
410
+ await input.fill("");
411
+ await input.pressSequentially(city, { delay: 45 + Math.random() * 40 });
412
+ if (how === "click") await pickSuggestion(page, city, 8e3);
413
+ else {
414
+ await page.locator(".dropdown-location").first().waitFor({ state: "visible", timeout: 8e3 }).catch(() => {
415
+ });
416
+ await input.press("ArrowDown");
417
+ await input.press("Enter");
418
+ await page.waitForTimeout(500);
419
+ }
420
+ if (await leverLocationPicked(page)) return input.inputValue();
421
+ }
422
+ return void 0;
423
+ }
424
+
383
425
  // scripts/runner.ts
384
426
  import { existsSync, readFileSync as readSync, unlinkSync } from "node:fs";
385
427
  var SETTINGS;
@@ -886,20 +928,6 @@ async function attachResumeGeneric(page, resumePath, notes) {
886
928
  }
887
929
  notes.push(`Resume upload could not be confirmed on the page (looked for "${fileName}")`);
888
930
  }
889
- async function pickSuggestion(page, typed) {
890
- await page.waitForTimeout(1300);
891
- const items = page.locator('[role="option"], [role="listbox"] li, [class*="dropdown-location"], [class*="dropdown-results"] > *, .pac-item, [class*="suggestion"] li, [class*="autocomplete"] li, [class*="autocomplete"] [class*="item"]').filter({ visible: true });
892
- const n = await items.count();
893
- if (!n) return void 0;
894
- const texts = (await items.allInnerTexts()).map((t) => t.replace(/\s+/g, " ").trim());
895
- const want = typed.toLowerCase();
896
- let i = texts.findIndex((t) => t.toLowerCase().startsWith(want));
897
- if (i < 0) i = texts.findIndex((t) => t.toLowerCase().includes(want));
898
- if (i < 0) i = 0;
899
- await items.nth(i).click();
900
- await page.waitForTimeout(500);
901
- return texts[i];
902
- }
903
931
  async function discoverAndFillGeneric(page, app, notes) {
904
932
  await page.waitForLoadState("domcontentloaded");
905
933
  await page.waitForLoadState("networkidle", { timeout: 8e3 }).catch(() => {
@@ -961,9 +989,13 @@ async function discoverAndFillGeneric(page, app, notes) {
961
989
  const isPlace = /location|city|where (are|do) you/i.test(f.label);
962
990
  const typed = isPlace ? value.split(",")[0].trim() : value;
963
991
  await el.pressSequentially(typed, { delay: jitter(35, 80) });
964
- const picked = await pickSuggestion(page, typed);
992
+ const picked = await pickSuggestion(page, typed, isPlace ? 8e3 : 1300);
965
993
  if (picked) notes.push(`${f.label}: chose "${picked}" from the suggestions`);
966
994
  else if (isPlace && await el.inputValue() !== typed) notes.push(`${f.label}: the field did not keep "${typed}"; check it in the window`);
995
+ if (isPlace && !await leverLocationPicked(page)) {
996
+ const fixed = await ensureLeverLocation(page, typed);
997
+ notes.push(fixed ? `${f.label}: picked "${fixed}" on a second try` : `${f.label}: Lever did not accept a location; pick one in the window`);
998
+ }
967
999
  }
968
1000
  } catch (e) {
969
1001
  notes.push(`Could not fill "${f.label}": ${e.message.slice(0, 80)}`);
@@ -1028,6 +1060,10 @@ async function submit(app) {
1028
1060
  if (!entry) return;
1029
1061
  }
1030
1062
  try {
1063
+ if (!await leverLocationPicked(entry.page) && SETTINGS.location) {
1064
+ const fixed = await ensureLeverLocation(entry.page, SETTINGS.location.split(",")[0].trim());
1065
+ log(fixed ? `repaired location for ${app.id}: "${fixed}"` : `could not repair location for ${app.id}`);
1066
+ }
1031
1067
  const missing = await findEmptyRequiredLive(entry.page).catch(() => []);
1032
1068
  if (missing.length) {
1033
1069
  const known = new Set(app.questions.map((q) => q.label.toLowerCase()));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lazy-apply",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "The Lazy Apply runner: fills your approved job applications in Chrome on your own computer. Run `npx lazy-apply`.",
5
5
  "type": "module",
6
6
  "bin": { "lazy-apply": "dist/cli.js" },