@cavuno/board 3.2.0 → 4.1.0

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.
Files changed (78) hide show
  1. package/CHANGELOG.md +433 -0
  2. package/README.md +6 -2
  3. package/dist/{_spec-nkWAraS0.d.mts → _spec-BZDv2dc5.d.mts} +100 -68
  4. package/dist/{_spec-nkWAraS0.d.ts → _spec-BZDv2dc5.d.ts} +100 -68
  5. package/dist/bin.mjs +7 -2
  6. package/dist/{blog-iV9XEIGf.d.ts → blog-BHx6iH3Z.d.ts} +1 -1
  7. package/dist/{blog-DGBHmExV.d.mts → blog-DyDvyQrI.d.mts} +1 -1
  8. package/dist/{board-JhdUWIoq.d.ts → board-BF3Qao6J.d.mts} +7 -6
  9. package/dist/{board-BJbXd_9_.d.mts → board-BTEZ0uiI.d.ts} +7 -6
  10. package/dist/breadcrumbs-SGaWth6C.d.ts +372 -0
  11. package/dist/breadcrumbs-icxRVydh.d.mts +372 -0
  12. package/dist/chunk-BGKNXG4H.mjs +146 -0
  13. package/dist/{chunk-NXY3OWGF.mjs → chunk-T2KC6XGV.mjs} +1 -1
  14. package/dist/{chunk-STE77MAT.mjs → chunk-YEAQ3JEI.mjs} +143 -134
  15. package/dist/doctor.js +1 -1
  16. package/dist/doctor.mjs +1 -1
  17. package/dist/filters.d.mts +25 -20
  18. package/dist/filters.d.ts +25 -20
  19. package/dist/filters.js +3 -692
  20. package/dist/filters.mjs +3 -29
  21. package/dist/format.d.mts +57 -231
  22. package/dist/format.d.ts +57 -231
  23. package/dist/format.js +327 -968
  24. package/dist/format.mjs +207 -264
  25. package/dist/go.js +11 -1
  26. package/dist/go.mjs +1 -1
  27. package/dist/index.d.mts +67 -76
  28. package/dist/index.d.ts +67 -76
  29. package/dist/index.js +45 -36
  30. package/dist/index.mjs +45 -36
  31. package/dist/{jobs-BiEvL9d7.d.ts → jobs-BX6d_sdd.d.mts} +4 -4
  32. package/dist/{jobs-lVvKCBCU.d.mts → jobs-BbLJfz1z.d.ts} +4 -4
  33. package/dist/paths.d.mts +89 -1
  34. package/dist/paths.d.ts +89 -1
  35. package/dist/paths.js +62 -14
  36. package/dist/paths.mjs +11 -3
  37. package/dist/route-contract.js +23 -13
  38. package/dist/route-contract.mjs +2 -2
  39. package/dist/{search-CCyFWPi3.d.mts → search-7B-mVRNA.d.ts} +14 -3
  40. package/dist/{search-CyRvVMUp.d.ts → search-OKrdAHv8.d.mts} +14 -3
  41. package/dist/seo.d.mts +30 -29
  42. package/dist/seo.d.ts +30 -29
  43. package/dist/seo.js +197 -718
  44. package/dist/seo.mjs +26 -24
  45. package/dist/server.d.mts +5 -5
  46. package/dist/server.d.ts +5 -5
  47. package/dist/sitemap.d.mts +5 -5
  48. package/dist/sitemap.d.ts +5 -5
  49. package/dist/sitemap.js +25 -14
  50. package/dist/sitemap.mjs +3 -2
  51. package/dist/skills.js +1 -1
  52. package/dist/skills.mjs +1 -1
  53. package/dist/suggest.d.mts +2 -2
  54. package/dist/suggest.d.ts +2 -2
  55. package/dist/suggest.js +17 -3
  56. package/dist/suggest.mjs +17 -3
  57. package/dist/well-known.js +23 -13
  58. package/dist/well-known.mjs +2 -2
  59. package/package.json +2 -4
  60. package/skills/cavuno-board-api-client/SKILL.md +42 -0
  61. package/skills/cavuno-board-applications/SKILL.md +4 -1
  62. package/skills/cavuno-board-filters/SKILL.md +11 -12
  63. package/skills/cavuno-board-format/SKILL.md +68 -56
  64. package/skills/cavuno-board-i18n/SKILL.md +102 -21
  65. package/skills/cavuno-board-search-suggestions/SKILL.md +8 -3
  66. package/skills/cavuno-board-seo/BLOG.md +4 -1
  67. package/skills/cavuno-board-seo/JOB_AND_BREADCRUMBS.md +5 -2
  68. package/skills/cavuno-board-seo/LISTING.md +35 -12
  69. package/skills/cavuno-board-seo/SALARY.md +83 -18
  70. package/skills/cavuno-board-seo/SKILL.md +3 -2
  71. package/skills/manifest.json +3 -3
  72. package/dist/breadcrumbs-1hMjcsqA.d.mts +0 -171
  73. package/dist/breadcrumbs-DexU07TT.d.ts +0 -171
  74. package/dist/chunk-IJCJ26SQ.mjs +0 -596
  75. package/dist/chunk-L7ZOYWHW.mjs +0 -98
  76. package/dist/chunk-LUGJWOT7.mjs +0 -96
  77. package/dist/ui-copy-CCkz6CVF.d.mts +0 -230
  78. package/dist/ui-copy-CCkz6CVF.d.ts +0 -230
package/dist/paths.mjs CHANGED
@@ -7,17 +7,21 @@ import {
7
7
  companyMarketPath,
8
8
  companyPath,
9
9
  companySalaryPath,
10
+ encodePathSegment,
10
11
  goAlertsConfirmPath,
11
12
  goAlertsManagePath,
12
13
  goJobPath,
13
14
  jobDetailPath,
14
15
  jobsCategoryPath,
16
+ jobsLocationCategoryPath,
15
17
  jobsLocationPath,
18
+ jobsLocationSkillPath,
16
19
  jobsSkillPath,
17
20
  salaryLocationPath,
18
21
  salarySkillPath,
19
- salaryTitlePath
20
- } from "./chunk-L7ZOYWHW.mjs";
22
+ salaryTitlePath,
23
+ suggestionPath
24
+ } from "./chunk-BGKNXG4H.mjs";
21
25
  export {
22
26
  BOARD_PATHS,
23
27
  blogAuthorPath,
@@ -27,14 +31,18 @@ export {
27
31
  companyMarketPath,
28
32
  companyPath,
29
33
  companySalaryPath,
34
+ encodePathSegment,
30
35
  goAlertsConfirmPath,
31
36
  goAlertsManagePath,
32
37
  goJobPath,
33
38
  jobDetailPath,
34
39
  jobsCategoryPath,
40
+ jobsLocationCategoryPath,
35
41
  jobsLocationPath,
42
+ jobsLocationSkillPath,
36
43
  jobsSkillPath,
37
44
  salaryLocationPath,
38
45
  salarySkillPath,
39
- salaryTitlePath
46
+ salaryTitlePath,
47
+ suggestionPath
40
48
  };
@@ -51,44 +51,54 @@ __export(route_contract_exports, {
51
51
  module.exports = __toCommonJS(route_contract_exports);
52
52
 
53
53
  // src/paths/index.ts
54
+ function encodePathSegment(segment) {
55
+ try {
56
+ return encodeURIComponent(decodeURIComponent(segment)).replace(
57
+ /%3A/gi,
58
+ ":"
59
+ );
60
+ } catch {
61
+ return encodeURIComponent(segment).replace(/%3A/gi, ":");
62
+ }
63
+ }
54
64
  function jobDetailPath(companySlug, jobSlug) {
55
- return `/companies/${companySlug}/jobs/${jobSlug}`;
65
+ return `/companies/${encodePathSegment(companySlug)}/jobs/${encodePathSegment(jobSlug)}`;
56
66
  }
57
67
  function jobsCategoryPath(categorySlug) {
58
- return `/jobs/${categorySlug}`;
68
+ return `/jobs/${encodePathSegment(categorySlug)}`;
59
69
  }
60
70
  function jobsSkillPath(skillSlug) {
61
- return `/jobs/skills/${skillSlug}`;
71
+ return `/jobs/skills/${encodePathSegment(skillSlug)}`;
62
72
  }
63
73
  function jobsLocationPath(placeSlug) {
64
- return `/jobs/locations/${placeSlug}`;
74
+ return `/jobs/locations/${encodePathSegment(placeSlug)}`;
65
75
  }
66
76
  function companyPath(companySlug) {
67
- return `/companies/${companySlug}`;
77
+ return `/companies/${encodePathSegment(companySlug)}`;
68
78
  }
69
79
  function companyMarketPath(marketSlug) {
70
- return `/companies/markets/${marketSlug}`;
80
+ return `/companies/markets/${encodePathSegment(marketSlug)}`;
71
81
  }
72
82
  function companySalaryPath(companySlug) {
73
- return `/companies/${companySlug}/salaries`;
83
+ return `/companies/${encodePathSegment(companySlug)}/salaries`;
74
84
  }
75
85
  function salaryTitlePath(titleSlug) {
76
- return `/salaries/titles/${titleSlug}`;
86
+ return `/salaries/titles/${encodePathSegment(titleSlug)}`;
77
87
  }
78
88
  function salarySkillPath(skillSlug) {
79
- return `/salaries/skills/${skillSlug}`;
89
+ return `/salaries/skills/${encodePathSegment(skillSlug)}`;
80
90
  }
81
91
  function salaryLocationPath(placeSlug) {
82
- return `/salaries/locations/${placeSlug}`;
92
+ return `/salaries/locations/${encodePathSegment(placeSlug)}`;
83
93
  }
84
94
  function blogPostPath(postSlug) {
85
- return `/blog/${postSlug}`;
95
+ return `/blog/${encodePathSegment(postSlug)}`;
86
96
  }
87
97
  function blogTagPath(tagSlug) {
88
- return `/blog/tag/${tagSlug}`;
98
+ return `/blog/tag/${encodePathSegment(tagSlug)}`;
89
99
  }
90
100
  function blogAuthorPath(authorSlug) {
91
- return `/blog/author/${authorSlug}`;
101
+ return `/blog/author/${encodePathSegment(authorSlug)}`;
92
102
  }
93
103
  var BOARD_PATHS = {
94
104
  home: "/",
@@ -26,8 +26,8 @@ import {
26
26
  parseTanStackRoutes,
27
27
  sameParamSet,
28
28
  validateManifest
29
- } from "./chunk-NXY3OWGF.mjs";
30
- import "./chunk-L7ZOYWHW.mjs";
29
+ } from "./chunk-T2KC6XGV.mjs";
30
+ import "./chunk-BGKNXG4H.mjs";
31
31
  export {
32
32
  ALL_ROLES,
33
33
  CANONICAL_MANIFEST,
@@ -1,4 +1,4 @@
1
- import { S as Schemas } from './_spec-nkWAraS0.mjs';
1
+ import { S as Schemas } from './_spec-BZDv2dc5.js';
2
2
 
3
3
  type Awaitable<T> = T | Promise<T>;
4
4
  /**
@@ -69,13 +69,24 @@ declare class BoardClient {
69
69
  type SuggestResult = Schemas['SuggestResult'];
70
70
  type SuggestionItem = Schemas['SuggestionItem'];
71
71
  type CompanySuggestion = Schemas['CompanySuggestion'];
72
+ type MarketSuggestion = Schemas['MarketSuggestion'];
72
73
  type TermSuggestion = Schemas['TermSuggestion'];
74
+ /** Kinds accepted by `SearchSuggestQuery.types`. */
75
+ type SearchSuggestType = 'company' | 'category' | 'skill' | 'market' | 'post' | 'tag';
73
76
  /** Query for `board.search.suggest()`. */
74
77
  type SearchSuggestQuery = {
75
78
  /** Keyword; under 2 chars the server returns an empty `items` array. */
76
79
  q?: string;
77
- /** Max interleaved suggestions (1–25; default 25). */
80
+ /**
81
+ * Max interleaved suggestions after type filtering (1–25; default 25).
82
+ * With `types: ['skill']`, this is "up to N skills", not N of a mixed pool.
83
+ */
78
84
  limit?: number;
85
+ /**
86
+ * Include only these suggestion kinds. Omit for every kind (company,
87
+ * category, skill, market, post, tag). Repeated query keys on the wire.
88
+ */
89
+ types?: SearchSuggestType[];
79
90
  };
80
91
 
81
- export { type Awaitable as A, type BoardRequest as B, type CompanySuggestion as C, type FetchOptions as F, type Logger as L, REFRESH_TOKEN_KEY as R, type StorageMode as S, type TermSuggestion as T, BoardClient as a, type SearchSuggestQuery as b, ACCESS_TOKEN_KEY as c, BOARD_ACCESS_GRANT_KEY as d, type CustomStorage as e, type SuggestResult as f, type SuggestionItem as g };
92
+ export { type Awaitable as A, type BoardRequest as B, type CompanySuggestion as C, type FetchOptions as F, type Logger as L, type MarketSuggestion as M, REFRESH_TOKEN_KEY as R, type StorageMode as S, type TermSuggestion as T, BoardClient as a, type SearchSuggestQuery as b, ACCESS_TOKEN_KEY as c, BOARD_ACCESS_GRANT_KEY as d, type CustomStorage as e, type SearchSuggestType as f, type SuggestResult as g, type SuggestionItem as h };
@@ -1,4 +1,4 @@
1
- import { S as Schemas } from './_spec-nkWAraS0.js';
1
+ import { S as Schemas } from './_spec-BZDv2dc5.mjs';
2
2
 
3
3
  type Awaitable<T> = T | Promise<T>;
4
4
  /**
@@ -69,13 +69,24 @@ declare class BoardClient {
69
69
  type SuggestResult = Schemas['SuggestResult'];
70
70
  type SuggestionItem = Schemas['SuggestionItem'];
71
71
  type CompanySuggestion = Schemas['CompanySuggestion'];
72
+ type MarketSuggestion = Schemas['MarketSuggestion'];
72
73
  type TermSuggestion = Schemas['TermSuggestion'];
74
+ /** Kinds accepted by `SearchSuggestQuery.types`. */
75
+ type SearchSuggestType = 'company' | 'category' | 'skill' | 'market' | 'post' | 'tag';
73
76
  /** Query for `board.search.suggest()`. */
74
77
  type SearchSuggestQuery = {
75
78
  /** Keyword; under 2 chars the server returns an empty `items` array. */
76
79
  q?: string;
77
- /** Max interleaved suggestions (1–25; default 25). */
80
+ /**
81
+ * Max interleaved suggestions after type filtering (1–25; default 25).
82
+ * With `types: ['skill']`, this is "up to N skills", not N of a mixed pool.
83
+ */
78
84
  limit?: number;
85
+ /**
86
+ * Include only these suggestion kinds. Omit for every kind (company,
87
+ * category, skill, market, post, tag). Repeated query keys on the wire.
88
+ */
89
+ types?: SearchSuggestType[];
79
90
  };
80
91
 
81
- export { type Awaitable as A, type BoardRequest as B, type CompanySuggestion as C, type FetchOptions as F, type Logger as L, REFRESH_TOKEN_KEY as R, type StorageMode as S, type TermSuggestion as T, BoardClient as a, type SearchSuggestQuery as b, ACCESS_TOKEN_KEY as c, BOARD_ACCESS_GRANT_KEY as d, type CustomStorage as e, type SuggestResult as f, type SuggestionItem as g };
92
+ export { type Awaitable as A, type BoardRequest as B, type CompanySuggestion as C, type FetchOptions as F, type Logger as L, type MarketSuggestion as M, REFRESH_TOKEN_KEY as R, type StorageMode as S, type TermSuggestion as T, BoardClient as a, type SearchSuggestQuery as b, ACCESS_TOKEN_KEY as c, BOARD_ACCESS_GRANT_KEY as d, type CustomStorage as e, type SearchSuggestType as f, type SuggestResult as g, type SuggestionItem as h };
package/dist/seo.d.mts CHANGED
@@ -1,10 +1,9 @@
1
- import { P as PublicBlogPostSummary, B as BlogAuthorEmbed } from './blog-DGBHmExV.mjs';
2
- import { J as JsonLdObject } from './breadcrumbs-1hMjcsqA.mjs';
3
- export { A as ALL_COUNTRY_CODES, B as BreadcrumbItemInput, F as FaqItem, c as JsonLdBoard, S as SENIORITY_ORDER, b as buildJobBreadcrumbs, d as buildSalaryFaq, e as companyCategorySalaryJsonLd, g as companySalaryJsonLd, h as createBreadcrumbJsonLd, i as createJobPostingJsonLd, j as crossAxisSalaryJsonLd, k as faqJsonLd, f as formatRange, l as formatSeniority, a as formatUsd, m as itemListJsonLd, o as locationSalaryJsonLd, n as normalizeWebsiteUrl, s as skillSalaryJsonLd, p as sortBySeniority, t as titleSalaryJsonLd } from './breadcrumbs-1hMjcsqA.mjs';
4
- import './_spec-nkWAraS0.mjs';
5
- import './board-BJbXd_9_.mjs';
6
- import './jobs-lVvKCBCU.mjs';
7
- import './ui-copy-CCkz6CVF.mjs';
1
+ import { P as PublicBlogPostSummary, B as BlogAuthorEmbed } from './blog-DyDvyQrI.mjs';
2
+ import { e as JsonLdObject } from './breadcrumbs-icxRVydh.mjs';
3
+ export { A as ALL_COUNTRY_CODES, B as BreadcrumbItemInput, g as FaqItem, J as JobBreadcrumb, h as JsonLdBoard, N as NumberNotation, i as SENIORITY_ORDER, j as SalaryFaqEntry, k as SalaryJsonLdOptions, l as SalaryOccupationListOptions, b as buildJobBreadcrumbs, m as buildSalaryFaq, o as companyCategorySalaryJsonLd, p as companySalaryJsonLd, q as createBreadcrumbJsonLd, r as createJobPostingJsonLd, s as crossAxisSalaryJsonLd, t as faqJsonLd, c as formatSalaryStat, d as formatSalaryStatRange, u as itemListJsonLd, v as locationSalaryJsonLd, n as normalizeWebsiteUrl, w as skillSalaryJsonLd, x as sortBySeniority, y as titleSalaryJsonLd } from './breadcrumbs-icxRVydh.mjs';
4
+ import './_spec-BZDv2dc5.mjs';
5
+ import './board-BF3Qao6J.mjs';
6
+ import './jobs-BX6d_sdd.mjs';
8
7
 
9
8
  /**
10
9
  * Blog structured data on the `@cavuno/board` wire types, transcribed from
@@ -59,36 +58,32 @@ declare function createAuthorProfileJsonLd({ author, canonical, description, ori
59
58
  totalPosts: number;
60
59
  }): JsonLdObject | null;
61
60
 
62
- /**
63
- * Framework-neutral `<head>` + structural JSON-LD builders for jobs-listing
64
- * pages. Locale-neutral pass-through: the caller supplies the
65
- * heading/board copy (title/description COPY is board-SEO-config +
66
- * localized — a named exclusion; these builders replicate the hosted
67
- * STRUCTURE: title with result count, canonical, Open Graph, breadcrumb +
68
- * job `ItemList`).
69
- */
70
-
71
61
  interface ListingHeadOptions {
72
- boardName: string;
62
+ /**
63
+ * Fully composed document title — application owns counters, particles,
64
+ * word order, and separators (e.g. `1,225 Jobs | Acme`, `1.225 Jobs | Acme`,
65
+ * `1,225件の求人 | 求人ボード`). The SDK does not join count/heading/board.
66
+ */
67
+ title: string;
73
68
  origin: string;
74
69
  /** Absolute path of THIS page, for canonical / og:url, e.g. `/jobs/robotik`. */
75
70
  path: string;
76
- /** Human heading, e.g. `Robotik jobs in Berlin` or `Jobs`. */
77
- heading: string;
78
- /** Result count — prefixed into the title (`N heading`). */
79
- count?: number;
80
71
  /**
81
- * Board language (BCP-47). When supplied, `count` is grouped for that
82
- * locale in the title and description — `1,225 Jobs` / `1.225 Jobs` —
83
- * matching how listing bodies render the same number. Omit to keep the
84
- * ungrouped digits the hosted structure has always emitted.
72
+ * Meta description — fully owned by the application. Pass a sentence from
73
+ * the board's copy source (page config, message catalog, template). The SDK
74
+ * does not invent or case-fold this string.
85
75
  */
86
- language?: string;
76
+ description: string;
87
77
  }
88
78
  /**
89
- * Meta/link descriptors for a jobs-listing page — title (with result
90
- * count), meta description, Open Graph, and `<link rel=canonical>`.
91
- * Framework-neutral: map `meta`/`links` into your head manager.
79
+ * Meta/link descriptors for a jobs-listing page — caller-supplied title and
80
+ * meta description, Open Graph, and `<link rel=canonical>`. Framework-neutral:
81
+ * map `meta`/`links` into your head manager.
82
+ *
83
+ * **Why `title` and `description` are both accepted composed:** a function
84
+ * that takes a finished description but still composes its own title from
85
+ * count/heading/boardName is incoherent — both are display sentences whose
86
+ * arrangement is locale-dependent (Japanese counters, RTL order, …).
92
87
  */
93
88
  declare function listingHead(options: ListingHeadOptions): {
94
89
  meta: ({
@@ -123,6 +118,12 @@ interface JobLink {
123
118
  * page, mirroring the structural JSON-LD the hosted board emits. The rich
124
119
  * category set (`Occupation`/`FAQPage`/`MonetaryAmountDistribution`) lives
125
120
  * in `./salary` — feed it the salary detail reads.
121
+ *
122
+ * Empty / single-crumb trails omit `BreadcrumbList` (same min-2 rule as
123
+ * `createBreadcrumbJsonLd` — an empty `itemListElement` is invalid structured
124
+ * data). Job URLs go through `jobDetailPath` — `/jobs/{slug}` is always a
125
+ * listing route, never a job detail; company-less jobs are dropped from the
126
+ * ItemList rather than linked to an invalid path.
126
127
  */
127
128
  declare function listingJsonLd(options: {
128
129
  origin: string;
package/dist/seo.d.ts CHANGED
@@ -1,10 +1,9 @@
1
- import { P as PublicBlogPostSummary, B as BlogAuthorEmbed } from './blog-iV9XEIGf.js';
2
- import { J as JsonLdObject } from './breadcrumbs-DexU07TT.js';
3
- export { A as ALL_COUNTRY_CODES, B as BreadcrumbItemInput, F as FaqItem, c as JsonLdBoard, S as SENIORITY_ORDER, b as buildJobBreadcrumbs, d as buildSalaryFaq, e as companyCategorySalaryJsonLd, g as companySalaryJsonLd, h as createBreadcrumbJsonLd, i as createJobPostingJsonLd, j as crossAxisSalaryJsonLd, k as faqJsonLd, f as formatRange, l as formatSeniority, a as formatUsd, m as itemListJsonLd, o as locationSalaryJsonLd, n as normalizeWebsiteUrl, s as skillSalaryJsonLd, p as sortBySeniority, t as titleSalaryJsonLd } from './breadcrumbs-DexU07TT.js';
4
- import './_spec-nkWAraS0.js';
5
- import './board-JhdUWIoq.js';
6
- import './jobs-BiEvL9d7.js';
7
- import './ui-copy-CCkz6CVF.js';
1
+ import { P as PublicBlogPostSummary, B as BlogAuthorEmbed } from './blog-BHx6iH3Z.js';
2
+ import { e as JsonLdObject } from './breadcrumbs-SGaWth6C.js';
3
+ export { A as ALL_COUNTRY_CODES, B as BreadcrumbItemInput, g as FaqItem, J as JobBreadcrumb, h as JsonLdBoard, N as NumberNotation, i as SENIORITY_ORDER, j as SalaryFaqEntry, k as SalaryJsonLdOptions, l as SalaryOccupationListOptions, b as buildJobBreadcrumbs, m as buildSalaryFaq, o as companyCategorySalaryJsonLd, p as companySalaryJsonLd, q as createBreadcrumbJsonLd, r as createJobPostingJsonLd, s as crossAxisSalaryJsonLd, t as faqJsonLd, c as formatSalaryStat, d as formatSalaryStatRange, u as itemListJsonLd, v as locationSalaryJsonLd, n as normalizeWebsiteUrl, w as skillSalaryJsonLd, x as sortBySeniority, y as titleSalaryJsonLd } from './breadcrumbs-SGaWth6C.js';
4
+ import './_spec-BZDv2dc5.js';
5
+ import './board-BTEZ0uiI.js';
6
+ import './jobs-BbLJfz1z.js';
8
7
 
9
8
  /**
10
9
  * Blog structured data on the `@cavuno/board` wire types, transcribed from
@@ -59,36 +58,32 @@ declare function createAuthorProfileJsonLd({ author, canonical, description, ori
59
58
  totalPosts: number;
60
59
  }): JsonLdObject | null;
61
60
 
62
- /**
63
- * Framework-neutral `<head>` + structural JSON-LD builders for jobs-listing
64
- * pages. Locale-neutral pass-through: the caller supplies the
65
- * heading/board copy (title/description COPY is board-SEO-config +
66
- * localized — a named exclusion; these builders replicate the hosted
67
- * STRUCTURE: title with result count, canonical, Open Graph, breadcrumb +
68
- * job `ItemList`).
69
- */
70
-
71
61
  interface ListingHeadOptions {
72
- boardName: string;
62
+ /**
63
+ * Fully composed document title — application owns counters, particles,
64
+ * word order, and separators (e.g. `1,225 Jobs | Acme`, `1.225 Jobs | Acme`,
65
+ * `1,225件の求人 | 求人ボード`). The SDK does not join count/heading/board.
66
+ */
67
+ title: string;
73
68
  origin: string;
74
69
  /** Absolute path of THIS page, for canonical / og:url, e.g. `/jobs/robotik`. */
75
70
  path: string;
76
- /** Human heading, e.g. `Robotik jobs in Berlin` or `Jobs`. */
77
- heading: string;
78
- /** Result count — prefixed into the title (`N heading`). */
79
- count?: number;
80
71
  /**
81
- * Board language (BCP-47). When supplied, `count` is grouped for that
82
- * locale in the title and description — `1,225 Jobs` / `1.225 Jobs` —
83
- * matching how listing bodies render the same number. Omit to keep the
84
- * ungrouped digits the hosted structure has always emitted.
72
+ * Meta description — fully owned by the application. Pass a sentence from
73
+ * the board's copy source (page config, message catalog, template). The SDK
74
+ * does not invent or case-fold this string.
85
75
  */
86
- language?: string;
76
+ description: string;
87
77
  }
88
78
  /**
89
- * Meta/link descriptors for a jobs-listing page — title (with result
90
- * count), meta description, Open Graph, and `<link rel=canonical>`.
91
- * Framework-neutral: map `meta`/`links` into your head manager.
79
+ * Meta/link descriptors for a jobs-listing page — caller-supplied title and
80
+ * meta description, Open Graph, and `<link rel=canonical>`. Framework-neutral:
81
+ * map `meta`/`links` into your head manager.
82
+ *
83
+ * **Why `title` and `description` are both accepted composed:** a function
84
+ * that takes a finished description but still composes its own title from
85
+ * count/heading/boardName is incoherent — both are display sentences whose
86
+ * arrangement is locale-dependent (Japanese counters, RTL order, …).
92
87
  */
93
88
  declare function listingHead(options: ListingHeadOptions): {
94
89
  meta: ({
@@ -123,6 +118,12 @@ interface JobLink {
123
118
  * page, mirroring the structural JSON-LD the hosted board emits. The rich
124
119
  * category set (`Occupation`/`FAQPage`/`MonetaryAmountDistribution`) lives
125
120
  * in `./salary` — feed it the salary detail reads.
121
+ *
122
+ * Empty / single-crumb trails omit `BreadcrumbList` (same min-2 rule as
123
+ * `createBreadcrumbJsonLd` — an empty `itemListElement` is invalid structured
124
+ * data). Job URLs go through `jobDetailPath` — `/jobs/{slug}` is always a
125
+ * listing route, never a job detail; company-less jobs are dropped from the
126
+ * ItemList rather than linked to an invalid path.
126
127
  */
127
128
  declare function listingJsonLd(options: {
128
129
  origin: string;