job-pro 1.0.3 → 1.0.5

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/apply.js CHANGED
@@ -437,21 +437,16 @@ export async function submitApplication(staged, target, options = {}) {
437
437
  if (options.extraHeaders) {
438
438
  Object.assign(headers, options.extraHeaders);
439
439
  }
440
- let response;
441
- try {
442
- response = await fetch(url, {
443
- method: staged.submit_method ?? "POST",
444
- headers,
445
- body: fd,
446
- });
447
- }
448
- catch (err) {
440
+ const r = await fetchWithRetry(url, { method: staged.submit_method ?? "POST", headers, body: fd }, "submit");
441
+ if (!r.ok) {
449
442
  return {
450
443
  ok: false,
451
444
  posted_to: url,
452
- message: `network error: ${err instanceof Error ? err.message : String(err)}`,
445
+ status: r.status,
446
+ message: r.message,
453
447
  };
454
448
  }
449
+ const response = r.response;
455
450
  let preview = "";
456
451
  try {
457
452
  preview = (await response.text()).slice(0, 4000);
@@ -709,6 +704,50 @@ export async function executeFeishu3Step(staged, session, target) {
709
704
  steps,
710
705
  };
711
706
  }
707
+ async function fetchWithRetry(url, init, label, log) {
708
+ const maxRetries = Math.max(0, Math.min(5, Number.parseInt(process.env.JOB_PRO_RETRY ?? "2", 10) || 2));
709
+ let lastErr = "";
710
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
711
+ let response = null;
712
+ try {
713
+ response = await fetch(url, init);
714
+ }
715
+ catch (err) {
716
+ lastErr = `network error: ${err instanceof Error ? err.message : String(err)}`;
717
+ log?.push({ attempt: attempt + 1, ok: false, message: `${label}: ${lastErr}` });
718
+ // Network errors are retryable. Back off and try again.
719
+ if (attempt < maxRetries) {
720
+ await sleep(retryDelayMs(attempt));
721
+ continue;
722
+ }
723
+ return { ok: false, message: lastErr };
724
+ }
725
+ // 4xx → user error, don't retry.
726
+ if (response.status >= 400 && response.status < 500) {
727
+ log?.push({ attempt: attempt + 1, ok: false, status: response.status, message: `${label}: HTTP ${response.status} (no retry — 4xx)` });
728
+ return { ok: false, status: response.status, message: `HTTP ${response.status}: ${response.statusText}` };
729
+ }
730
+ // 5xx → server error, retry.
731
+ if (response.status >= 500 && attempt < maxRetries) {
732
+ lastErr = `HTTP ${response.status}: ${response.statusText}`;
733
+ log?.push({ attempt: attempt + 1, ok: false, status: response.status, message: `${label}: ${lastErr} (will retry)` });
734
+ await sleep(retryDelayMs(attempt));
735
+ continue;
736
+ }
737
+ log?.push({ attempt: attempt + 1, ok: response.ok, status: response.status, message: `${label}: HTTP ${response.status}` });
738
+ return { ok: true, response };
739
+ }
740
+ return { ok: false, message: lastErr || "exhausted retries" };
741
+ }
742
+ function retryDelayMs(attempt) {
743
+ // Exponential backoff with jitter: 250ms / 500ms / 1s / 2s / 4s, ±25%.
744
+ const base = 250 * Math.pow(2, attempt);
745
+ const jitter = base * (Math.random() * 0.5 - 0.25);
746
+ return Math.round(base + jitter);
747
+ }
748
+ function sleep(ms) {
749
+ return new Promise((resolve) => setTimeout(resolve, ms));
750
+ }
712
751
  /** Build the headers bag used by every Feishu/Beisen/Moka step. */
713
752
  function sessionHeaderBag(session, targetHost) {
714
753
  const out = {
package/dist/index.js CHANGED
@@ -60,7 +60,7 @@ import { createRequire as require_createRequire } from "node:module";
60
60
  function require_module() {
61
61
  return { createRequire: require_createRequire };
62
62
  }
63
- const VERSION = "1.0.3";
63
+ const VERSION = "1.0.5";
64
64
  const COMPANIES = [
65
65
  { key: "tencent", family: "Bespoke", source: "join.qq.com", label: "Tencent / 腾讯" },
66
66
  { key: "bytedance", family: "Bespoke", source: "jobs.bytedance.com", label: "ByteDance / 字节跳动" },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "job-pro",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "Query Chinese big-tech campus recruiting from your terminal. 50 companies, all 50 live. 46 via each company's own API; the 4 with no public canonical feed (Hikvision, CICC, Cainiao, WeBank) surfaced via Liepin as a clearly-labeled third-party fallback. No signup, no token, no server.",
5
5
  "homepage": "https://job.ha7ch.com",
6
6
  "repository": "https://github.com/HA7CH/job-pro",