@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
@@ -205,6 +205,8 @@ interface components {
205
205
  /** @description Minimum 8 characters. */
206
206
  password: string;
207
207
  displayName: string;
208
+ /** @description True only when the person ticked a marketing checkbox your UI displayed with its disclosure wording. Omit when no checkbox was shown; false and absent both record nothing. */
209
+ marketingConsent?: boolean;
208
210
  };
209
211
  BoardAuthRequestMagicLinkBody: {
210
212
  /** Format: email */
@@ -245,19 +247,9 @@ interface components {
245
247
  * @description The board's canonical base URL (honours a configured custom domain). Build `robots.txt`'s `Sitemap:` line (`${canonicalBase}/sitemap.xml`) and canonical links from it.
246
248
  */
247
249
  canonicalBase: string;
248
- /** @description Absolute URLs for the board's configured favicons / app icons (null when unset). The variant key implies the role + size; build `<link rel="icon">` tags + the web-manifest icon list from these. */
249
- icons: {
250
- ico: string | null;
251
- svg: string | null;
252
- appleTouch: string | null;
253
- icon192: string | null;
254
- icon512: string | null;
255
- iconMaskable512: string | null;
256
- };
257
- /** @description Web-manifest metadata; assemble `site.webmanifest` from this + `icons`. */
250
+ /** @description Web-manifest name only. Theme color and icons left the surface in 4.0.0: applications own brand assets and presentation tokens. */
258
251
  manifest: {
259
252
  name: string;
260
- themeColor: string;
261
253
  };
262
254
  };
263
255
  BoardUser: {
@@ -1150,17 +1142,17 @@ interface components {
1150
1142
  seniority?: "entry_level" | "associate" | "mid_level" | "senior" | "lead" | "principal" | "director" | "executive";
1151
1143
  /** @description Where candidates apply. Accepts an HTTPS URL, a `mailto:` URI, or a bare email address (normalized to `mailto:` form). Pass `null` to clear the stored value and switch the job to on-board (built-in) applications. Omitted means unchanged. */
1152
1144
  applicationUrl?: string | null;
1153
- /** @description Minimum salary, in `salaryCurrency` units. */
1154
- salaryMin?: number;
1155
- /** @description Maximum salary, in `salaryCurrency` units. */
1156
- salaryMax?: number;
1157
- /** @description Three-letter ISO 4217 currency code for `salaryMin` and `salaryMax`. */
1158
- salaryCurrency?: string;
1145
+ /** @description Minimum salary, in `salaryCurrency` units. Pass `null` to clear the stored value; omitted means unchanged. */
1146
+ salaryMin?: number | null;
1147
+ /** @description Maximum salary, in `salaryCurrency` units. Pass `null` to clear the stored value; omitted means unchanged. */
1148
+ salaryMax?: number | null;
1149
+ /** @description Three-letter ISO 4217 currency code for `salaryMin` and `salaryMax`. Pass `null` to clear the stored value; omitted means unchanged. */
1150
+ salaryCurrency?: string | null;
1159
1151
  /**
1160
- * @description Period the `salaryMin` and `salaryMax` figures are quoted against.
1161
- * @enum {string}
1152
+ * @description Period the `salaryMin` and `salaryMax` figures are quoted against. Pass `null` to clear the stored value; omitted means unchanged.
1153
+ * @enum {string|null}
1162
1154
  */
1163
- salaryTimeframe?: "per_year" | "per_month" | "per_week" | "per_day" | "per_hour";
1155
+ salaryTimeframe?: "per_year" | "per_month" | "per_week" | "per_day" | "per_hour" | null;
1164
1156
  /** @description Up to 100 canonical skill slugs from `GET /v1/taxonomies/skills`. Every slug must exist in this account. */
1165
1157
  skills?: string[];
1166
1158
  /** @description Up to 100 canonical category slugs from `GET /v1/taxonomies/categories`. Every slug must exist in this account. */
@@ -1339,23 +1331,6 @@ interface components {
1339
1331
  /** Format: email */
1340
1332
  email: string;
1341
1333
  };
1342
- LegalPage: {
1343
- /** @enum {string} */
1344
- object: "legal_page";
1345
- /** @description The resolved legal page type. */
1346
- type: string;
1347
- /** @description Page heading (H1), with `{{board_name}}` resolved. */
1348
- title: string;
1349
- /** @description Owner-authored prose as portable HTML. Empty string when the page has no text body. */
1350
- content: string;
1351
- /** @enum {string} */
1352
- contentFormat: "html";
1353
- /** @description Structured impressum legal-entity facts; `null` for non-impressum pages. */
1354
- legalEntity: {
1355
- legalName: string | null;
1356
- address: string | null;
1357
- } | null;
1358
- };
1359
1334
  LocationSalaryDetail: {
1360
1335
  /** @enum {string} */
1361
1336
  object: "location_salary_detail";
@@ -1465,6 +1440,38 @@ interface components {
1465
1440
  jobCount: number;
1466
1441
  }[];
1467
1442
  };
1443
+ MarketSuggestion: {
1444
+ /** @enum {string} */
1445
+ object: "suggestion";
1446
+ /**
1447
+ * @description discriminator enum property added by openapi-typescript
1448
+ * @enum {string}
1449
+ */
1450
+ type: "market";
1451
+ /** @description Stable market identity (source slug; markets are slug-keyed). */
1452
+ id: string;
1453
+ /** @description Public market URL slug. */
1454
+ slug: string;
1455
+ /** @description Market display name. */
1456
+ name: string;
1457
+ /** @description Number of companies on the board in this market. */
1458
+ companyCount: number;
1459
+ };
1460
+ MarketingConsent: {
1461
+ id: string;
1462
+ /** @enum {string} */
1463
+ object: "marketing_consent";
1464
+ /** @enum {string} */
1465
+ status: "granted" | "withdrawn";
1466
+ source: string;
1467
+ /** @enum {string|null} */
1468
+ reason: "person_request" | "operator_request" | "account_deleted" | null;
1469
+ /** Format: date-time */
1470
+ grantedAt: string | null;
1471
+ /** Format: date-time */
1472
+ withdrawnAt: string | null;
1473
+ revision: number;
1474
+ } | null;
1468
1475
  Message: {
1469
1476
  id: string;
1470
1477
  /** @enum {string} */
@@ -1528,7 +1535,9 @@ interface components {
1528
1535
  /** @enum {string} */
1529
1536
  object: "plan";
1530
1537
  id: string;
1538
+ /** @description The operator's authoring-default plan name, in the board's language. Board templates that localize chrome should treat this as the fallback and word plan names from `featureSummary` structure or their own catalogs. */
1531
1539
  name: string;
1540
+ /** @description Authoring default: the operator's prose description in the board's language. The localized public string lives in the board template — compose it from `featureSummary`, `billingInterval`, and `talent` rather than displaying this verbatim on localized surfaces. */
1532
1541
  description: string | null;
1533
1542
  /** @enum {string} */
1534
1543
  purpose: "job_posting" | "talent_access";
@@ -1561,6 +1570,21 @@ interface components {
1561
1570
  messagesPerPeriod: string;
1562
1571
  };
1563
1572
  };
1573
+ PostSuggestion: {
1574
+ /** @enum {string} */
1575
+ object: "suggestion";
1576
+ /**
1577
+ * @description discriminator enum property added by openapi-typescript
1578
+ * @enum {string}
1579
+ */
1580
+ type: "post";
1581
+ /** @description Stable published blog post identity. */
1582
+ id: string;
1583
+ /** @description Public blog post URL slug. */
1584
+ slug: string;
1585
+ /** @description Post title. */
1586
+ title: string;
1587
+ };
1564
1588
  PublicBlogAdjacentPosts: {
1565
1589
  /** @enum {string} */
1566
1590
  object: "blog_adjacent_posts";
@@ -1651,7 +1675,11 @@ interface components {
1651
1675
  candidates: boolean;
1652
1676
  employers: boolean;
1653
1677
  blog: boolean;
1654
- talentDirectory: boolean;
1678
+ /**
1679
+ * @description Talent directory access mode. Link /talent whenever this is not `'off'`. An employers-only directory renders a sign-in upsell for anonymous visitors, matching the hosted chrome. Replaces the former top-level `talentDirectoryVisibility` and the boolean `features.talentDirectory`.
1680
+ * @enum {string}
1681
+ */
1682
+ talentDirectory: "off" | "public" | "employers_only";
1655
1683
  registrationWall: boolean;
1656
1684
  passwordProtected: boolean;
1657
1685
  publicJobSubmission: boolean;
@@ -1662,11 +1690,6 @@ interface components {
1662
1690
  /** @description Whether applicant↔employer messaging is enabled on the board. `false` means the `me/conversations` route family rejects with `messaging_disabled` (403). Hide inbox/dock/Message CTAs. */
1663
1691
  messaging: boolean;
1664
1692
  };
1665
- /**
1666
- * @description The tri-state behind `features.talentDirectory` (which is `visibility === 'public'`). Link /talent whenever this is not `'off'`. An employers-only directory renders a sign-in upsell for anonymous visitors, matching the hosted chrome.
1667
- * @enum {string}
1668
- */
1669
- talentDirectoryVisibility: "off" | "public" | "employers_only";
1670
1693
  analytics: {
1671
1694
  ga4MeasurementId: string | null;
1672
1695
  gtmId: string | null;
@@ -1674,33 +1697,23 @@ interface components {
1674
1697
  linkedInPartnerId: string | null;
1675
1698
  cookieConsentRequired: boolean;
1676
1699
  };
1677
- /** @description Operator-defined custom job-field definitions, in display order. Board-wide; the frontend uses these to render and localize each job's opaque `customFieldValues`. Empty when the board defines none. Display-only: not filterable or searchable in v1. */
1678
- customFields: components["schemas"]["CustomFieldDefinition"][];
1679
- /** @description Stored operator label overrides by config group (`jobCardLabels`, `navLabels`, `breadcrumbsLabels`, …): plain-text chrome copy to merge over the `@cavuno/board` `uiCopy(language)` catalog via `uiCopy(language, labels)`. Empty object when the board stores none. */
1680
- labels: {
1681
- [key: string]: {
1682
- [key: string]: string;
1683
- };
1700
+ /** @description Operator-defined custom field definitions keyed by model (currently only `job`). Each key holds that model's definitions in display order. Company and talent keys are added when those models ship. The frontend uses these to render and localize each record's opaque `customFieldValues`. Display-only: not filterable or searchable in v1. */
1701
+ customFields: {
1702
+ /** @description This model's custom-field definitions in display order. Empty when the board defines none for the model. */
1703
+ job: components["schemas"]["CustomFieldDefinition"][];
1684
1704
  };
1685
- /** @description Footer/brand data the hosted board footer renders (description, contact, website + social links, navigation order). The brand/social URLs (`websiteUrl`, `facebookUrl`, `linkedinUrl`, `xUrl`) are sanitized to absolute http(s); `customLinks[].url` is served verbatim (see its own note). */
1686
- footer: {
1687
- /** @description Operator-written footer brand description. May carry a `{{board_name}}` placeholder. Resolve it before rendering. When null, fall back to the label catalog's `footer.defaultDescription` template. */
1688
- description: string | null;
1689
- /** @description Public contact email for the footer About column (render as `mailto:`). */
1690
- contactEmail: string | null;
1705
+ /** @description Operator-authored contact and social identity (settings › Contact & company details). Extracted from the former `footer` group in 4.0.0: contact is identity data, not layout. Brand/social URLs are sanitized to absolute http(s). */
1706
+ contact: {
1707
+ /** @description Public contact email (render as `mailto:`). Operator-set in settings › Contact & company details. */
1708
+ email: string | null;
1709
+ /** @description Company website URL, sanitized to absolute http(s). Null when unset or non-http(s). */
1691
1710
  websiteUrl: string | null;
1692
1711
  /** @description X (Twitter) profile URL, normalized from the stored handle. */
1693
1712
  xUrl: string | null;
1713
+ /** @description Facebook profile URL, sanitized to absolute http(s). Null when unset or non-http(s). */
1694
1714
  facebookUrl: string | null;
1715
+ /** @description LinkedIn profile URL, sanitized to absolute http(s). Null when unset or non-http(s). */
1695
1716
  linkedinUrl: string | null;
1696
- /** @description Operator-configured navigation order: system item ids (`home`, `companies`, `pricing`, `blog`) and `custom:<id>` refs into `customLinks`. Empty when the operator never reordered. Render the system defaults. System items still gate on their feature flags (e.g. `blog` only when `features.blog`). */
1697
- navigationOrder: string[];
1698
- /** @description Operator-defined navigation links referenced from `navigationOrder` as `custom:<id>`; links not referenced there append after the system items (hosted-footer behavior). Each `url` is served verbatim (trimmed only) and may be a relative path (e.g. `/hub`): unlike the brand/social URLs it is NOT run through http(s) sanitization (matching the hosted footer, which supports relative links), so a consumer MUST sanitize it before binding to an anchor href. */
1699
- customLinks: {
1700
- id: string;
1701
- label: string;
1702
- url: string;
1703
- }[];
1704
1717
  };
1705
1718
  };
1706
1719
  PublicCompaniesSearchBody: {
@@ -1841,7 +1854,7 @@ interface components {
1841
1854
  slug: string;
1842
1855
  name: string;
1843
1856
  }[];
1844
- /** @description Opaque, display-only custom-field values, keyed by each field's `key`. Values are the option `key`(s) for select fields, or the raw boolean/number/text otherwise. Resolve labels via the board's `customFields` definitions (see `GET /v1/boards/:identifier`). `{}` when the board defines no custom fields. Not filterable or searchable in v1. */
1857
+ /** @description Opaque, display-only custom-field values, keyed by each field's `key`. Values are the option `key`(s) for select fields, or the raw boolean/number/text otherwise. Resolve labels via the board's `customFields.job` definitions (see `GET /v1/boards/:identifier`). `{}` when the board defines no custom fields. Not filterable or searchable in v1. */
1845
1858
  customFieldValues: {
1846
1859
  [key: string]: string | string[] | boolean | number;
1847
1860
  };
@@ -1919,6 +1932,10 @@ interface components {
1919
1932
  remoteOption: "on_site" | "hybrid" | "remote" | null;
1920
1933
  /** @description Display region label for remote jobs (e.g. "United States", "Worldwide"); `null` for non-remote jobs. */
1921
1934
  remoteLocationLabel: string | null;
1935
+ /** @description Structured twin of `remoteLocationLabel`’s "Worldwide" case: `true` when the remote permit selection is unrestricted (explicit worldwide, or no constraint), `false` for a constrained remote job, `null` for non-remote jobs. Word it from your own catalog instead of matching the board-language label string. */
1936
+ remoteWorldwide: boolean | null;
1937
+ /** @description ISO 3166-1 alpha-2 codes the remote permit selection covers (derived expansion, mirroring the job detail field of the same name). Empty for worldwide/unconstrained and non-remote jobs. */
1938
+ remoteWorkPermitCountryCodes: string[];
1922
1939
  salaryMin: number | null;
1923
1940
  salaryMax: number | null;
1924
1941
  salaryCurrency: string | null;
@@ -2186,7 +2203,7 @@ interface components {
2186
2203
  jobId: string;
2187
2204
  /** Format: date-time */
2188
2205
  savedAt: string;
2189
- job: components["schemas"]["PublicJob"];
2206
+ job: components["schemas"]["PublicJobCard"];
2190
2207
  };
2191
2208
  SendWorkEmailBody: {
2192
2209
  /** Format: email */
@@ -2347,10 +2364,25 @@ interface components {
2347
2364
  object: "suggest_result";
2348
2365
  /** @description The trimmed query string that produced these suggestions. */
2349
2366
  query: string;
2350
- /** @description Interleaved company + taxonomy-term suggestions. Order is server-ranked; clients must not re-sort. */
2367
+ /** @description Interleaved company, market, taxonomy-term, blog-post, and tag suggestions. Order is server-ranked; clients must not re-sort. When `types` is set, only the requested kinds appear and `limit` applies after that filter. */
2351
2368
  items: components["schemas"]["SuggestionItem"][];
2352
2369
  };
2353
- SuggestionItem: components["schemas"]["CompanySuggestion"] | components["schemas"]["TermSuggestion"];
2370
+ SuggestionItem: components["schemas"]["CompanySuggestion"] | components["schemas"]["MarketSuggestion"] | components["schemas"]["TermSuggestion"] | components["schemas"]["PostSuggestion"] | components["schemas"]["TagSuggestion"];
2371
+ TagSuggestion: {
2372
+ /** @enum {string} */
2373
+ object: "suggestion";
2374
+ /**
2375
+ * @description discriminator enum property added by openapi-typescript
2376
+ * @enum {string}
2377
+ */
2378
+ type: "tag";
2379
+ /** @description Stable tag identity (source slug; tags are slug-keyed). */
2380
+ id: string;
2381
+ /** @description Public blog tag URL slug. */
2382
+ slug: string;
2383
+ /** @description Tag display name. */
2384
+ name: string;
2385
+ };
2354
2386
  TalentAccess: {
2355
2387
  /** @enum {string} */
2356
2388
  object: "talent_access";
@@ -205,6 +205,8 @@ interface components {
205
205
  /** @description Minimum 8 characters. */
206
206
  password: string;
207
207
  displayName: string;
208
+ /** @description True only when the person ticked a marketing checkbox your UI displayed with its disclosure wording. Omit when no checkbox was shown; false and absent both record nothing. */
209
+ marketingConsent?: boolean;
208
210
  };
209
211
  BoardAuthRequestMagicLinkBody: {
210
212
  /** Format: email */
@@ -245,19 +247,9 @@ interface components {
245
247
  * @description The board's canonical base URL (honours a configured custom domain). Build `robots.txt`'s `Sitemap:` line (`${canonicalBase}/sitemap.xml`) and canonical links from it.
246
248
  */
247
249
  canonicalBase: string;
248
- /** @description Absolute URLs for the board's configured favicons / app icons (null when unset). The variant key implies the role + size; build `<link rel="icon">` tags + the web-manifest icon list from these. */
249
- icons: {
250
- ico: string | null;
251
- svg: string | null;
252
- appleTouch: string | null;
253
- icon192: string | null;
254
- icon512: string | null;
255
- iconMaskable512: string | null;
256
- };
257
- /** @description Web-manifest metadata; assemble `site.webmanifest` from this + `icons`. */
250
+ /** @description Web-manifest name only. Theme color and icons left the surface in 4.0.0: applications own brand assets and presentation tokens. */
258
251
  manifest: {
259
252
  name: string;
260
- themeColor: string;
261
253
  };
262
254
  };
263
255
  BoardUser: {
@@ -1150,17 +1142,17 @@ interface components {
1150
1142
  seniority?: "entry_level" | "associate" | "mid_level" | "senior" | "lead" | "principal" | "director" | "executive";
1151
1143
  /** @description Where candidates apply. Accepts an HTTPS URL, a `mailto:` URI, or a bare email address (normalized to `mailto:` form). Pass `null` to clear the stored value and switch the job to on-board (built-in) applications. Omitted means unchanged. */
1152
1144
  applicationUrl?: string | null;
1153
- /** @description Minimum salary, in `salaryCurrency` units. */
1154
- salaryMin?: number;
1155
- /** @description Maximum salary, in `salaryCurrency` units. */
1156
- salaryMax?: number;
1157
- /** @description Three-letter ISO 4217 currency code for `salaryMin` and `salaryMax`. */
1158
- salaryCurrency?: string;
1145
+ /** @description Minimum salary, in `salaryCurrency` units. Pass `null` to clear the stored value; omitted means unchanged. */
1146
+ salaryMin?: number | null;
1147
+ /** @description Maximum salary, in `salaryCurrency` units. Pass `null` to clear the stored value; omitted means unchanged. */
1148
+ salaryMax?: number | null;
1149
+ /** @description Three-letter ISO 4217 currency code for `salaryMin` and `salaryMax`. Pass `null` to clear the stored value; omitted means unchanged. */
1150
+ salaryCurrency?: string | null;
1159
1151
  /**
1160
- * @description Period the `salaryMin` and `salaryMax` figures are quoted against.
1161
- * @enum {string}
1152
+ * @description Period the `salaryMin` and `salaryMax` figures are quoted against. Pass `null` to clear the stored value; omitted means unchanged.
1153
+ * @enum {string|null}
1162
1154
  */
1163
- salaryTimeframe?: "per_year" | "per_month" | "per_week" | "per_day" | "per_hour";
1155
+ salaryTimeframe?: "per_year" | "per_month" | "per_week" | "per_day" | "per_hour" | null;
1164
1156
  /** @description Up to 100 canonical skill slugs from `GET /v1/taxonomies/skills`. Every slug must exist in this account. */
1165
1157
  skills?: string[];
1166
1158
  /** @description Up to 100 canonical category slugs from `GET /v1/taxonomies/categories`. Every slug must exist in this account. */
@@ -1339,23 +1331,6 @@ interface components {
1339
1331
  /** Format: email */
1340
1332
  email: string;
1341
1333
  };
1342
- LegalPage: {
1343
- /** @enum {string} */
1344
- object: "legal_page";
1345
- /** @description The resolved legal page type. */
1346
- type: string;
1347
- /** @description Page heading (H1), with `{{board_name}}` resolved. */
1348
- title: string;
1349
- /** @description Owner-authored prose as portable HTML. Empty string when the page has no text body. */
1350
- content: string;
1351
- /** @enum {string} */
1352
- contentFormat: "html";
1353
- /** @description Structured impressum legal-entity facts; `null` for non-impressum pages. */
1354
- legalEntity: {
1355
- legalName: string | null;
1356
- address: string | null;
1357
- } | null;
1358
- };
1359
1334
  LocationSalaryDetail: {
1360
1335
  /** @enum {string} */
1361
1336
  object: "location_salary_detail";
@@ -1465,6 +1440,38 @@ interface components {
1465
1440
  jobCount: number;
1466
1441
  }[];
1467
1442
  };
1443
+ MarketSuggestion: {
1444
+ /** @enum {string} */
1445
+ object: "suggestion";
1446
+ /**
1447
+ * @description discriminator enum property added by openapi-typescript
1448
+ * @enum {string}
1449
+ */
1450
+ type: "market";
1451
+ /** @description Stable market identity (source slug; markets are slug-keyed). */
1452
+ id: string;
1453
+ /** @description Public market URL slug. */
1454
+ slug: string;
1455
+ /** @description Market display name. */
1456
+ name: string;
1457
+ /** @description Number of companies on the board in this market. */
1458
+ companyCount: number;
1459
+ };
1460
+ MarketingConsent: {
1461
+ id: string;
1462
+ /** @enum {string} */
1463
+ object: "marketing_consent";
1464
+ /** @enum {string} */
1465
+ status: "granted" | "withdrawn";
1466
+ source: string;
1467
+ /** @enum {string|null} */
1468
+ reason: "person_request" | "operator_request" | "account_deleted" | null;
1469
+ /** Format: date-time */
1470
+ grantedAt: string | null;
1471
+ /** Format: date-time */
1472
+ withdrawnAt: string | null;
1473
+ revision: number;
1474
+ } | null;
1468
1475
  Message: {
1469
1476
  id: string;
1470
1477
  /** @enum {string} */
@@ -1528,7 +1535,9 @@ interface components {
1528
1535
  /** @enum {string} */
1529
1536
  object: "plan";
1530
1537
  id: string;
1538
+ /** @description The operator's authoring-default plan name, in the board's language. Board templates that localize chrome should treat this as the fallback and word plan names from `featureSummary` structure or their own catalogs. */
1531
1539
  name: string;
1540
+ /** @description Authoring default: the operator's prose description in the board's language. The localized public string lives in the board template — compose it from `featureSummary`, `billingInterval`, and `talent` rather than displaying this verbatim on localized surfaces. */
1532
1541
  description: string | null;
1533
1542
  /** @enum {string} */
1534
1543
  purpose: "job_posting" | "talent_access";
@@ -1561,6 +1570,21 @@ interface components {
1561
1570
  messagesPerPeriod: string;
1562
1571
  };
1563
1572
  };
1573
+ PostSuggestion: {
1574
+ /** @enum {string} */
1575
+ object: "suggestion";
1576
+ /**
1577
+ * @description discriminator enum property added by openapi-typescript
1578
+ * @enum {string}
1579
+ */
1580
+ type: "post";
1581
+ /** @description Stable published blog post identity. */
1582
+ id: string;
1583
+ /** @description Public blog post URL slug. */
1584
+ slug: string;
1585
+ /** @description Post title. */
1586
+ title: string;
1587
+ };
1564
1588
  PublicBlogAdjacentPosts: {
1565
1589
  /** @enum {string} */
1566
1590
  object: "blog_adjacent_posts";
@@ -1651,7 +1675,11 @@ interface components {
1651
1675
  candidates: boolean;
1652
1676
  employers: boolean;
1653
1677
  blog: boolean;
1654
- talentDirectory: boolean;
1678
+ /**
1679
+ * @description Talent directory access mode. Link /talent whenever this is not `'off'`. An employers-only directory renders a sign-in upsell for anonymous visitors, matching the hosted chrome. Replaces the former top-level `talentDirectoryVisibility` and the boolean `features.talentDirectory`.
1680
+ * @enum {string}
1681
+ */
1682
+ talentDirectory: "off" | "public" | "employers_only";
1655
1683
  registrationWall: boolean;
1656
1684
  passwordProtected: boolean;
1657
1685
  publicJobSubmission: boolean;
@@ -1662,11 +1690,6 @@ interface components {
1662
1690
  /** @description Whether applicant↔employer messaging is enabled on the board. `false` means the `me/conversations` route family rejects with `messaging_disabled` (403). Hide inbox/dock/Message CTAs. */
1663
1691
  messaging: boolean;
1664
1692
  };
1665
- /**
1666
- * @description The tri-state behind `features.talentDirectory` (which is `visibility === 'public'`). Link /talent whenever this is not `'off'`. An employers-only directory renders a sign-in upsell for anonymous visitors, matching the hosted chrome.
1667
- * @enum {string}
1668
- */
1669
- talentDirectoryVisibility: "off" | "public" | "employers_only";
1670
1693
  analytics: {
1671
1694
  ga4MeasurementId: string | null;
1672
1695
  gtmId: string | null;
@@ -1674,33 +1697,23 @@ interface components {
1674
1697
  linkedInPartnerId: string | null;
1675
1698
  cookieConsentRequired: boolean;
1676
1699
  };
1677
- /** @description Operator-defined custom job-field definitions, in display order. Board-wide; the frontend uses these to render and localize each job's opaque `customFieldValues`. Empty when the board defines none. Display-only: not filterable or searchable in v1. */
1678
- customFields: components["schemas"]["CustomFieldDefinition"][];
1679
- /** @description Stored operator label overrides by config group (`jobCardLabels`, `navLabels`, `breadcrumbsLabels`, …): plain-text chrome copy to merge over the `@cavuno/board` `uiCopy(language)` catalog via `uiCopy(language, labels)`. Empty object when the board stores none. */
1680
- labels: {
1681
- [key: string]: {
1682
- [key: string]: string;
1683
- };
1700
+ /** @description Operator-defined custom field definitions keyed by model (currently only `job`). Each key holds that model's definitions in display order. Company and talent keys are added when those models ship. The frontend uses these to render and localize each record's opaque `customFieldValues`. Display-only: not filterable or searchable in v1. */
1701
+ customFields: {
1702
+ /** @description This model's custom-field definitions in display order. Empty when the board defines none for the model. */
1703
+ job: components["schemas"]["CustomFieldDefinition"][];
1684
1704
  };
1685
- /** @description Footer/brand data the hosted board footer renders (description, contact, website + social links, navigation order). The brand/social URLs (`websiteUrl`, `facebookUrl`, `linkedinUrl`, `xUrl`) are sanitized to absolute http(s); `customLinks[].url` is served verbatim (see its own note). */
1686
- footer: {
1687
- /** @description Operator-written footer brand description. May carry a `{{board_name}}` placeholder. Resolve it before rendering. When null, fall back to the label catalog's `footer.defaultDescription` template. */
1688
- description: string | null;
1689
- /** @description Public contact email for the footer About column (render as `mailto:`). */
1690
- contactEmail: string | null;
1705
+ /** @description Operator-authored contact and social identity (settings › Contact & company details). Extracted from the former `footer` group in 4.0.0: contact is identity data, not layout. Brand/social URLs are sanitized to absolute http(s). */
1706
+ contact: {
1707
+ /** @description Public contact email (render as `mailto:`). Operator-set in settings › Contact & company details. */
1708
+ email: string | null;
1709
+ /** @description Company website URL, sanitized to absolute http(s). Null when unset or non-http(s). */
1691
1710
  websiteUrl: string | null;
1692
1711
  /** @description X (Twitter) profile URL, normalized from the stored handle. */
1693
1712
  xUrl: string | null;
1713
+ /** @description Facebook profile URL, sanitized to absolute http(s). Null when unset or non-http(s). */
1694
1714
  facebookUrl: string | null;
1715
+ /** @description LinkedIn profile URL, sanitized to absolute http(s). Null when unset or non-http(s). */
1695
1716
  linkedinUrl: string | null;
1696
- /** @description Operator-configured navigation order: system item ids (`home`, `companies`, `pricing`, `blog`) and `custom:<id>` refs into `customLinks`. Empty when the operator never reordered. Render the system defaults. System items still gate on their feature flags (e.g. `blog` only when `features.blog`). */
1697
- navigationOrder: string[];
1698
- /** @description Operator-defined navigation links referenced from `navigationOrder` as `custom:<id>`; links not referenced there append after the system items (hosted-footer behavior). Each `url` is served verbatim (trimmed only) and may be a relative path (e.g. `/hub`): unlike the brand/social URLs it is NOT run through http(s) sanitization (matching the hosted footer, which supports relative links), so a consumer MUST sanitize it before binding to an anchor href. */
1699
- customLinks: {
1700
- id: string;
1701
- label: string;
1702
- url: string;
1703
- }[];
1704
1717
  };
1705
1718
  };
1706
1719
  PublicCompaniesSearchBody: {
@@ -1841,7 +1854,7 @@ interface components {
1841
1854
  slug: string;
1842
1855
  name: string;
1843
1856
  }[];
1844
- /** @description Opaque, display-only custom-field values, keyed by each field's `key`. Values are the option `key`(s) for select fields, or the raw boolean/number/text otherwise. Resolve labels via the board's `customFields` definitions (see `GET /v1/boards/:identifier`). `{}` when the board defines no custom fields. Not filterable or searchable in v1. */
1857
+ /** @description Opaque, display-only custom-field values, keyed by each field's `key`. Values are the option `key`(s) for select fields, or the raw boolean/number/text otherwise. Resolve labels via the board's `customFields.job` definitions (see `GET /v1/boards/:identifier`). `{}` when the board defines no custom fields. Not filterable or searchable in v1. */
1845
1858
  customFieldValues: {
1846
1859
  [key: string]: string | string[] | boolean | number;
1847
1860
  };
@@ -1919,6 +1932,10 @@ interface components {
1919
1932
  remoteOption: "on_site" | "hybrid" | "remote" | null;
1920
1933
  /** @description Display region label for remote jobs (e.g. "United States", "Worldwide"); `null` for non-remote jobs. */
1921
1934
  remoteLocationLabel: string | null;
1935
+ /** @description Structured twin of `remoteLocationLabel`’s "Worldwide" case: `true` when the remote permit selection is unrestricted (explicit worldwide, or no constraint), `false` for a constrained remote job, `null` for non-remote jobs. Word it from your own catalog instead of matching the board-language label string. */
1936
+ remoteWorldwide: boolean | null;
1937
+ /** @description ISO 3166-1 alpha-2 codes the remote permit selection covers (derived expansion, mirroring the job detail field of the same name). Empty for worldwide/unconstrained and non-remote jobs. */
1938
+ remoteWorkPermitCountryCodes: string[];
1922
1939
  salaryMin: number | null;
1923
1940
  salaryMax: number | null;
1924
1941
  salaryCurrency: string | null;
@@ -2186,7 +2203,7 @@ interface components {
2186
2203
  jobId: string;
2187
2204
  /** Format: date-time */
2188
2205
  savedAt: string;
2189
- job: components["schemas"]["PublicJob"];
2206
+ job: components["schemas"]["PublicJobCard"];
2190
2207
  };
2191
2208
  SendWorkEmailBody: {
2192
2209
  /** Format: email */
@@ -2347,10 +2364,25 @@ interface components {
2347
2364
  object: "suggest_result";
2348
2365
  /** @description The trimmed query string that produced these suggestions. */
2349
2366
  query: string;
2350
- /** @description Interleaved company + taxonomy-term suggestions. Order is server-ranked; clients must not re-sort. */
2367
+ /** @description Interleaved company, market, taxonomy-term, blog-post, and tag suggestions. Order is server-ranked; clients must not re-sort. When `types` is set, only the requested kinds appear and `limit` applies after that filter. */
2351
2368
  items: components["schemas"]["SuggestionItem"][];
2352
2369
  };
2353
- SuggestionItem: components["schemas"]["CompanySuggestion"] | components["schemas"]["TermSuggestion"];
2370
+ SuggestionItem: components["schemas"]["CompanySuggestion"] | components["schemas"]["MarketSuggestion"] | components["schemas"]["TermSuggestion"] | components["schemas"]["PostSuggestion"] | components["schemas"]["TagSuggestion"];
2371
+ TagSuggestion: {
2372
+ /** @enum {string} */
2373
+ object: "suggestion";
2374
+ /**
2375
+ * @description discriminator enum property added by openapi-typescript
2376
+ * @enum {string}
2377
+ */
2378
+ type: "tag";
2379
+ /** @description Stable tag identity (source slug; tags are slug-keyed). */
2380
+ id: string;
2381
+ /** @description Public blog tag URL slug. */
2382
+ slug: string;
2383
+ /** @description Tag display name. */
2384
+ name: string;
2385
+ };
2354
2386
  TalentAccess: {
2355
2387
  /** @enum {string} */
2356
2388
  object: "talent_access";
package/dist/bin.mjs CHANGED
@@ -29,7 +29,7 @@ function loadSkillCorpus(baseDir) {
29
29
  ).map((entry) => ({
30
30
  path: entry.name,
31
31
  content: readFileSync(resolve(skillDir, entry.name), "utf8")
32
- })).sort((a, b) => a.path.localeCompare(b.path));
32
+ })).sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
33
33
  return {
34
34
  ...skill,
35
35
  content: readFileSync(skillPath, "utf8"),
@@ -575,7 +575,12 @@ function runSetup(cwd = process.cwd()) {
575
575
  if (!copied.includes(skill.name)) copied.push(skill.name);
576
576
  }
577
577
  }
578
- return { version: manifest.version, framework, targetDirs, copied };
578
+ return {
579
+ version: manifest.version,
580
+ framework,
581
+ targetDirs,
582
+ copied
583
+ };
579
584
  }
580
585
 
581
586
  // src/bin.ts
@@ -1,4 +1,4 @@
1
- import { S as Schemas } from './_spec-nkWAraS0.js';
1
+ import { S as Schemas } from './_spec-BZDv2dc5.js';
2
2
 
3
3
  /** Author shape embedded on posts (no `object` discriminator). */
4
4
  type BlogAuthorEmbed = Schemas['PublicBlogAuthorEmbed'];
@@ -1,4 +1,4 @@
1
- import { S as Schemas } from './_spec-nkWAraS0.mjs';
1
+ import { S as Schemas } from './_spec-BZDv2dc5.mjs';
2
2
 
3
3
  /** Author shape embedded on posts (no `object` discriminator). */
4
4
  type BlogAuthorEmbed = Schemas['PublicBlogAuthorEmbed'];
@@ -1,5 +1,5 @@
1
- import { S as Schemas } from './_spec-nkWAraS0.js';
2
- import { L as ListEnvelope, m as RelatedSearch } from './jobs-BiEvL9d7.js';
1
+ import { S as Schemas } from './_spec-BZDv2dc5.mjs';
2
+ import { L as ListEnvelope, m as RelatedSearch } from './jobs-BX6d_sdd.mjs';
3
3
 
4
4
  type PublicCompany = Schemas['CompanyPublic'];
5
5
  /** A market (sector) a company operates in — name + its source slug. */
@@ -92,10 +92,11 @@ type PublicBoard = Schemas['PublicBoardContext'];
92
92
  type PublicBoardFeatures = PublicBoard['features'];
93
93
  type PublicBoardAnalytics = PublicBoard['analytics'];
94
94
  /**
95
- * An operator-defined custom job-field definition. Board-wide;
96
- * use it to render and localize a job's opaque `customFieldValues` (resolve
97
- * option `key`s → labels, honour field `type` and display order). Shared with
98
- * the Operator API's custom-field surface (one canonical schema).
95
+ * An operator-defined custom field definition. Board-wide and
96
+ * model-scoped under `board.context().customFields` (today only `job`);
97
+ * use it to render and localize a record's opaque `customFieldValues`
98
+ * (resolve option `key`s → labels, honour field `type` and display order).
99
+ * Shared with the Operator API's custom-field surface (one canonical schema).
99
100
  */
100
101
  type CustomFieldDefinition = Schemas['CustomFieldDefinition'];
101
102
  type CustomFieldType = CustomFieldDefinition['type'];