/** * Canonical board URL paths — the single source of truth for a board's * public URL structure. The sitemap generator (`src/sitemap/walker.ts`) * and every consumer (starter navigation, platform emails) build URLs * from these helpers, so the structure can never drift across surfaces. * * Each `*Path` returns an absolute path (leading slash, no origin); * `boardUrl(origin, path)` prefixes a board origin. Pure, isomorphic, * zero-dependency — safe to import in server runtimes, on the edge, * and in the browser. * * Dynamic path segments are percent-encoded (URI, not bare IRI) so * non-Latin board-language slugs (`SuggestionPathInput.canonicalSlug`) * are legal in sitemap ``, canonical/`og:url`, and email links. * Separators (`/`) are never encoded; already-encoded input is not * double-encoded. * * These paths mirror the hosted board's indexed URLs exactly (migration * parity). Do NOT change a pattern here without updating the hosted board * and the sitemap golden tests in lockstep — the structure is a locked * cross-surface contract. */ /** * Percent-encode one path segment for use in a URL path. * Round-trips already-encoded input so `%E6…` is not double-encoded. * Does not touch path separators — callers encode segments only. * * `:` is left unencoded so route-contract / well-known templates built via * `jobDetailPath(':companySlug', ':jobSlug')` stay RFC-6570-style. Real * board slugs are alphanumeric-plus-hyphen; non-ASCII is still encoded. */ declare function encodePathSegment(segment: string): string; /** Job detail — the canonical, indexed job URL. Requires BOTH slugs. */ declare function jobDetailPath(companySlug: string, jobSlug: string): string; /** Category (keyword) job listing. */ declare function jobsCategoryPath(categorySlug: string): string; /** Skill job listing. */ declare function jobsSkillPath(skillSlug: string): string; /** Location job listing. */ declare function jobsLocationPath(placeSlug: string): string; /** * Location + category combination job listing * (`/jobs/locations//`). */ declare function jobsLocationCategoryPath(placeSlug: string, categorySlug: string): string; /** * Location + skill combination job listing * (`/jobs/locations//skills/`). */ declare function jobsLocationSkillPath(placeSlug: string, skillSlug: string): string; /** Company profile. */ declare function companyPath(companySlug: string): string; /** Company market (sector) listing. */ declare function companyMarketPath(marketSlug: string): string; /** * Suggestion kinds `suggestionPath` understands. Mirrors the wire * `SuggestionItem` discriminators (and `termType` for terms) without * importing generated OpenAPI types into the paths entry. */ type SuggestionPathInput = { type: 'company'; slug: string; } | { type: 'market'; slug: string; } | { type: 'post'; slug: string; } | { type: 'tag'; slug: string; } | { type: 'term'; termType: 'category' | 'skill'; /** Board-language URL slug — use `canonicalSlug` from the wire item. */ canonicalSlug: string; }; /** Search scopes that own different destinations for the same suggestion. */ type SuggestionPathScope = 'jobs' | 'companies' | 'blog'; type SuggestionPathOptions = { scope: SuggestionPathScope; /** * Active place slug. When set on the `jobs` scope with a category or * skill term, routes into the location combination pages. */ location?: string; }; /** * Resolve a suggestion to a board-relative path for the given search scope * The API never returns an `href` because the same * `CompanySuggestion` means "navigate to the company page" under companies * scope and "apply a `companySlug` filter" under jobs scope — only the app * knows which. * * Returns `null` when the selection is not a navigation (jobs-scope company * → filter, or a kind that has no path under the active scope). Compose from * the existing `*Path` helpers; never string-build at the call site. * * | scope | suggestion | result | * | --- | --- | --- | * | jobs | skill | `/jobs/skills/` | * | jobs | category | `/jobs/` | * | jobs | skill + location | `/jobs/locations//skills/` | * | jobs | category + location | `/jobs/locations//` | * | jobs | company | `null` (apply `companySlug` filter) | * | jobs | market | `null` | * | jobs | post / tag | `null` | * | companies | company | `/companies/` | * | companies | market | `/companies/markets/` | * | companies | term / post / tag | `null` | * | blog | post | `/blog/` | * | blog | tag | `/blog/tag/` | * | blog | other | `null` | */ declare function suggestionPath(suggestion: SuggestionPathInput, options: SuggestionPathOptions): string | null; /** A company's salary overview. */ declare function companySalaryPath(companySlug: string): string; /** Salary page for a job title. */ declare function salaryTitlePath(titleSlug: string): string; /** Salary page for a skill. */ declare function salarySkillPath(skillSlug: string): string; /** Salary page for a location. */ declare function salaryLocationPath(placeSlug: string): string; /** Blog post. */ declare function blogPostPath(postSlug: string): string; /** Blog tag archive. */ declare function blogTagPath(tagSlug: string): string; /** Blog author archive. */ declare function blogAuthorPath(authorSlug: string): string; /** * Static top-level board paths (indexed marketing + index surfaces). Kept * here so the sitemap and consumers share one definition of the chrome * routes too. */ declare const BOARD_PATHS: { readonly home: "/"; readonly jobs: "/jobs"; readonly companies: "/companies"; readonly salaries: "/salaries"; readonly salaryCompanies: "/salaries/companies"; readonly salaryTitles: "/salaries/titles"; readonly salarySkills: "/salaries/skills"; readonly salaryLocations: "/salaries/locations"; readonly blog: "/blog"; readonly about: "/about"; readonly privacyPolicy: "/privacy-policy"; readonly termsOfService: "/terms-of-service"; readonly cookiePolicy: "/cookie-policy"; readonly impressum: "/impressum"; readonly talent: "/talent"; readonly employers: "/employers"; /** * Alert-email surfaces. Promoted out of raw literals in * the job-alerts composer so /go indirection and email composition share * one definition. */ readonly alertsManage: "/alerts/manage"; readonly alertsConfirm: "/alerts/confirm"; }; /** * Email `/go` indirection paths. Pure builders so * Server-side email composers can emit structure-independent links without * importing `@cavuno/board/go` (handler code is not safe in that runtime). * * Shapes match the hosted handler + SDK `createGoHandler` contracts: * /go/job/ * /go/alerts-manage * /go/alerts-confirm * * No encoding — callers pass opaque Cavuno ids / static role segments that * are already URL-safe path segments. Query strings are composed by the * caller (tokens must ride through verbatim). */ declare function goJobPath(jobId: string): string; declare function goAlertsManagePath(): string; declare function goAlertsConfirmPath(): string; /** * Prefix a board-relative path with the board origin. A trailing slash on * the origin is tolerated (stripped) so callers need not normalise first. * Paths from `*Path` helpers already percent-encode dynamic segments. */ declare function boardUrl(origin: string, path: string): string; export { BOARD_PATHS, type SuggestionPathInput, type SuggestionPathOptions, type SuggestionPathScope, blogAuthorPath, blogPostPath, blogTagPath, boardUrl, companyMarketPath, companyPath, companySalaryPath, encodePathSegment, goAlertsConfirmPath, goAlertsManagePath, goJobPath, jobDetailPath, jobsCategoryPath, jobsLocationCategoryPath, jobsLocationPath, jobsLocationSkillPath, jobsSkillPath, salaryLocationPath, salarySkillPath, salaryTitlePath, suggestionPath };