import { l as ScraperProvider, V as ValidationResult, j as ScraperContext, P as PaginationCursor, L as ListingsResult, e as JobListing, d as JobDetailsResult, J as JobDetails } from '../provider-Ce-KxEDG.js'; export { U as UrlExtractionResult, a as applyExtractor, b as asNumber, c as asString, d as asStringArray, e as buildListingUrlFromExtractors, f as buildMeta, g as buildUrl, h as currentOffset, i as currentPage, j as cursorPagination, k as deduplicateListings, l as extractFieldFromUrl, m as extractNamedParam, n as extractParamFromUrl, o as extractPathSegment, p as extractUrlInfo, q as matchPathPattern, r as matchesAnyUrlPattern, s as matchesUrlPattern, t as offsetCursor, u as pageCursor, v as parseDate, w as parseLocation, x as parseLocations, y as toExtractorArray, z as urlCursor } from '../helpers-ndADLAXB.js'; /** * @jobo/scraper-sdk/runtime — Base Provider Classes * * Abstract base classes that provide common scaffolding for the two * scraper architectures. Extend one of these instead of implementing * {@link ScraperProvider} from scratch. * * - {@link BaseFullDetailsScraper} — For ATS platforms where the listing * page or API returns all job details in one pass. * * - {@link BaseSplitScraper} — For ATS platforms where listings are * minimal and a secondary detail fetch is required per job. */ /** * Base class for **full-details scrapers**. * * Use this when the ATS listing page or API returns complete job details * (title, description, locations, etc.) in a single pass. The platform * will NOT call `scrapeJobDetails()`. * * Subclasses must implement: * - `validate(url)` — URL validation and company ID extraction. * - `scrapeListings(ctx, cursor?)` — Paginated listing scrape that returns * full {@link JobDetails} objects (cast to {@link JobListing}). * * @example * ```ts * import { BaseFullDetailsScraper, ScraperContext, ListingsResult } from "@jobo/scraper-sdk/runtime"; * * export class MyAtsScraper extends BaseFullDetailsScraper { * async validate(url: string) { * const match = url.match(/myats\.com\/company\/(\w+)/); * return { isValid: !!match, companyId: match?.[1] }; * } * * async scrapeListings(ctx: ScraperContext, cursor?: PaginationCursor): Promise { * const page = cursor ? parseInt(cursor.value) : 0; * const res = await ctx.http.get(`${ctx.baseUrl}/api/jobs?offset=${page * 20}`); * return { * listings: res.data.jobs.map(j => this.mapJob(j, ctx)), * hasMore: res.data.jobs.length === 20, * nextCursor: { type: "offset", value: String((page + 1) * 20) }, * }; * } * } * ``` */ declare abstract class BaseFullDetailsScraper implements ScraperProvider { abstract validate(url: string): Promise | ValidationResult; abstract scrapeListings(ctx: ScraperContext, cursor?: PaginationCursor): Promise; /** * Optional initialization hook. Override to perform one-time setup. * Default implementation is a no-op. */ initialize(_ctx: ScraperContext): Promise; /** * Not applicable for full-details scrapers. * Throws an error if called — the platform should never call this. */ scrapeJobDetails(_ctx: ScraperContext, _listing: JobListing): Promise; } /** * Base class for **split scrapers**. * * Use this when the ATS listing page only provides basic identifiers * (URL, ID, maybe title) and a secondary request is needed to fetch * the full job description, locations, and metadata. * * Subclasses must implement: * - `validate(url)` — URL validation and company ID extraction. * - `scrapeListings(ctx, cursor?)` — Paginated listing scrape returning * minimal {@link JobListing} objects (at minimum: `externalId` + `applyUrl`). * - `scrapeJobDetails(ctx, listing)` — Fetch full details for one listing. * * @example * ```ts * import { BaseSplitScraper, ScraperContext, ListingsResult, JobDetailsResult } from "@jobo/scraper-sdk/runtime"; * * export class MyAtsScraper extends BaseSplitScraper { * async validate(url: string) { * return { isValid: url.includes("myats.com") }; * } * * async scrapeListings(ctx: ScraperContext): Promise { * const page = await ctx.browser.renderPage({ url: `${ctx.baseUrl}/jobs` }); * const links = extractLinks(page.html, /\/jobs\/\d+/); * return { * listings: links.map(link => ({ * externalId: link.split("/").pop()!, * applyUrl: resolveUrl(link + "/apply", ctx.baseUrl), * listingUrl: resolveUrl(link, ctx.baseUrl), * })), * hasMore: false, * }; * } * * async scrapeJobDetails(ctx: ScraperContext, listing: JobListing): Promise { * const page = await ctx.browser.renderPage({ url: listing.listingUrl! }); * return { * details: { * ...listing, * title: extractText(page.html, "h1")!, * description: extractText(page.html, 'div class="description"')!, * locations: [{ text: extractText(page.html, 'span class="location"')! }], * listingUrl: listing.listingUrl!, * }, * }; * } * } * ``` */ declare abstract class BaseSplitScraper implements ScraperProvider { abstract validate(url: string): Promise | ValidationResult; abstract scrapeListings(ctx: ScraperContext, cursor?: PaginationCursor): Promise; abstract scrapeJobDetails(ctx: ScraperContext, listing: JobListing): Promise; /** * Optional initialization hook. Override to perform one-time setup. * Default implementation is a no-op. */ initialize(_ctx: ScraperContext): Promise; } /** * Merge a minimal {@link JobListing} with additional detail fields * to produce a complete {@link JobDetails} object. * * Throws if any required detail field is missing after the merge. * * @param listing — The base listing (from `scrapeListings()`). * @param details — Additional fields discovered during detail scraping. * @returns A validated {@link JobDetails} object. * * @example * ```ts * const details = mergeListingWithDetails(listing, { * title: "Senior Engineer", * description: "

We are looking for...

", * locations: [{ text: "NYC" }], * listingUrl: "https://ats.example.com/jobs/123", * meta: { department: "Engineering" }, * }); * ``` */ declare function mergeListingWithDetails(listing: JobListing, details: Partial): JobDetails; /** * @jobo/scraper-sdk/runtime — HTML Parsing Helpers * * Lightweight, zero-dependency utilities for extracting data from HTML strings. * These run in Node.js (not the browser) and operate on raw HTML text. * * For complex DOM manipulation, use `ctx.browser.renderPage()` and parse * the returned HTML. These helpers are designed for simple, fast extraction * from static HTML or API-returned HTML fragments. */ /** * Strip all HTML tags from a string, returning plain text. * Decodes common HTML entities and normalizes whitespace. * * @param html — Raw HTML string. * @returns Plain text with tags removed and entities decoded. * * @example * ```ts * stripHtml("

Hello world & friends

"); * // → "Hello world & friends" * ``` */ declare function stripHtml(html: string): string; /** * Strip HTML and truncate to a maximum length, appending "..." if truncated. * * @param html — Raw HTML string. * @param maxLength — Maximum character length of the output. * @returns Truncated plain text. */ declare function stripHtmlAndTruncate(html: string, maxLength: number): string; /** * Extract the value of an attribute from the first matching HTML tag. * * @param html — Raw HTML string. * @param tagPattern — Regex pattern to match the opening tag (e.g. `` tag value by its `name` attribute. * * @param html — Raw HTML string. * @param name — The `name` attribute value (e.g. `"description"`). * @returns The `content` attribute value, or `null`. * * @example * ```ts * extractMetaByName(html, "description"); * // → "We are hiring a senior engineer..." * ``` */ declare function extractMetaByName(html: string, name: string): string | null; /** * Extract a `` tag value by its `property` attribute (Open Graph, etc.). * * @param html — Raw HTML string. * @param property — The `property` attribute value (e.g. `"og:title"`). * @returns The `content` attribute value, or `null`. * * @example * ```ts * extractMetaByProperty(html, "og:title"); * // → "Senior Engineer at Acme" * ``` */ declare function extractMetaByProperty(html: string, property: string): string | null; /** * Extract and parse all JSON-LD blocks (`