From 847ce2859091ae761da9f1fa59cb94bdfcca378b Mon Sep 17 00:00:00 2001 From: Dmytro Stanchiev Date: Tue, 21 Apr 2026 21:44:12 -0400 Subject: [PATCH] refactor: make cookie loading env-only --- packages/core/src/utils/cookies.ts | 116 ++--------------------- packages/core/test/facebook-core.test.ts | 62 +++++++++--- 2 files changed, 58 insertions(+), 120 deletions(-) diff --git a/packages/core/src/utils/cookies.ts b/packages/core/src/utils/cookies.ts index e07c8f1..2579365 100644 --- a/packages/core/src/utils/cookies.ts +++ b/packages/core/src/utils/cookies.ts @@ -23,8 +23,6 @@ export interface CookieConfig { domain: string; /** Environment variable name (e.g., "FACEBOOK_COOKIE") */ envVar: string; - /** Path to cookie file (e.g., "./cookies/facebook.json") */ - filePath: string; } /** @@ -66,61 +64,6 @@ export function parseCookieString( .filter((cookie): cookie is Cookie => cookie !== null); } -/** - * Parse JSON array format into Cookie array - * Supports format: [{"name": "foo", "value": "bar", ...}] - */ -export function parseJsonCookies(jsonString: string): Cookie[] { - const parsed = JSON.parse(jsonString); - if (!Array.isArray(parsed)) { - return []; - } - - return parsed.filter( - (cookie): cookie is Cookie => - cookie && - typeof cookie.name === "string" && - typeof cookie.value === "string", - ); -} - -/** - * Try to parse cookies from a string (tries JSON first, then cookie string format) - */ -export function parseCookiesAuto( - input: string, - defaultDomain: string, -): Cookie[] { - // Try JSON array format first - try { - const cookies = parseJsonCookies(input); - if (cookies.length > 0) { - return cookies; - } - } catch { - // JSON parse failed, try cookie string format - } - - // Try cookie string format - return parseCookieString(input, defaultDomain); -} - -/** - * Load cookies from file (supports both JSON array and cookie string formats) - */ -export async function loadCookiesFromFile( - filePath: string, - defaultDomain: string, -): Promise { - const file = Bun.file(filePath); - if (!(await file.exists())) { - return []; - } - - const content = await file.text(); - return parseCookiesAuto(content.trim(), defaultDomain); -} - /** * Format cookies array into Cookie header string for HTTP requests */ @@ -155,60 +98,21 @@ export function formatCookiesForHeader( } /** - * Load cookies with priority: URL param > ENV var > file - * Supports both JSON array and cookie string formats for all sources + * Load cookies from the configured environment variable */ -export async function ensureCookies( - config: CookieConfig, - cookiesSource?: string, -): Promise { - // Priority 1: URL/API parameter (if provided) - if (cookiesSource) { - const cookies = parseCookiesAuto(cookiesSource, config.domain); - if (cookies.length > 0) { - console.log( - `Loaded ${cookies.length} ${config.name} cookies from parameter`, - ); - return cookies; - } - console.warn( - `${config.name} cookies parameter provided but no valid cookies extracted`, - ); - } - - // Priority 2: Environment variable +export async function ensureCookies(config: CookieConfig): Promise { const envValue = process.env[config.envVar]; - if (envValue?.trim()) { - const cookies = parseCookiesAuto(envValue, config.domain); - if (cookies.length > 0) { - console.log( - `Loaded ${cookies.length} ${config.name} cookies from ${config.envVar} env var`, - ); - return cookies; - } - console.warn(`${config.envVar} env var contains no valid cookies`); + const cookies = parseCookieString(envValue ?? "", config.domain); + + if (cookies.length > 0) { + console.log( + `Loaded ${cookies.length} ${config.name} cookies from ${config.envVar} env var`, + ); + return cookies; } - // Priority 3: Cookie file (fallback) - try { - const cookies = await loadCookiesFromFile(config.filePath, config.domain); - if (cookies.length > 0) { - console.log( - `Loaded ${cookies.length} ${config.name} cookies from ${config.filePath}`, - ); - return cookies; - } - } catch (e) { - console.warn(`Could not load cookies from ${config.filePath}: ${e}`); - } - - // No cookies found from any source throw new Error( - `No valid ${config.name} cookies found. Provide cookies via (in priority order):\n` + - ` 1. 'cookies' parameter (highest priority), or\n` + - ` 2. ${config.envVar} environment variable, or\n` + - ` 3. ${config.filePath} file (lowest priority)\n` + - 'Format: JSON array or cookie string like "name1=value1; name2=value2"', + `No valid ${config.name} cookies found. Provide cookies via ${config.envVar} environment variable as a raw Cookie header string.`, ); } diff --git a/packages/core/test/facebook-core.test.ts b/packages/core/test/facebook-core.test.ts index 9734f01..2e682fa 100644 --- a/packages/core/test/facebook-core.test.ts +++ b/packages/core/test/facebook-core.test.ts @@ -1,5 +1,6 @@ import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test"; import { + ensureFacebookCookies, extractFacebookItemData, extractFacebookMarketplaceData, fetchFacebookItem, @@ -84,15 +85,58 @@ describe("Facebook Marketplace Scraper Core Tests", () => { expect(result[1].name).toBe("xs"); expect(result[1].value).toBe("abc"); }); + + test("should load Facebook cookies from FACEBOOK_COOKIE env var", async () => { + const previous = process.env.FACEBOOK_COOKIE; + process.env.FACEBOOK_COOKIE = "c_user=123; xs=abc"; + + try { + const cookies = await ensureFacebookCookies(); + + expect(cookies.map((cookie) => cookie.name)).toEqual(["c_user", "xs"]); + } finally { + if (previous === undefined) { + delete process.env.FACEBOOK_COOKIE; + } else { + process.env.FACEBOOK_COOKIE = previous; + } + } + }); + + test("should reject missing Facebook auth env var", async () => { + const previous = process.env.FACEBOOK_COOKIE; + delete process.env.FACEBOOK_COOKIE; + + try { + await expect(ensureFacebookCookies()).rejects.toThrow( + "Provide cookies via FACEBOOK_COOKIE environment variable as a raw Cookie header string", + ); + } finally { + if (previous !== undefined) { + process.env.FACEBOOK_COOKIE = previous; + } + } + }); }); }); describe("Facebook Item Fetching", () => { describe("fetchFacebookItem", () => { - const mockCookies = JSON.stringify([ - { name: "c_user", value: "12345", domain: ".facebook.com" }, - { name: "xs", value: "abc123", domain: ".facebook.com" }, - ]); + const mockCookies = "c_user=12345; xs=abc123"; + let previousCookie: string | undefined; + + beforeEach(() => { + previousCookie = process.env.FACEBOOK_COOKIE; + process.env.FACEBOOK_COOKIE = mockCookies; + }); + + afterEach(() => { + if (previousCookie === undefined) { + delete process.env.FACEBOOK_COOKIE; + } else { + process.env.FACEBOOK_COOKIE = previousCookie; + } + }); test("should handle authentication errors", async () => { global.fetch = mock(() => @@ -234,16 +278,6 @@ describe("Facebook Marketplace Scraper Core Tests", () => { expect(result?.listingStatus).toBe("SOLD"); }); - test("should handle missing authentication cookies", async () => { - // Use a test-specific cookie file that doesn't exist - const testCookiePath = "./cookies/facebook-test.json"; - - // Test with no cookies available (test file doesn't exist) - await expect( - fetchFacebookItem("123", undefined, testCookiePath), - ).rejects.toThrow("No valid Facebook cookies found"); - }); - test("should handle successful item extraction", async () => { const mockData = { require: [