@cavuno/board 3.1.0 → 4.0.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.
- package/CHANGELOG.md +421 -0
- package/README.md +6 -2
- package/dist/{_spec-nkWAraS0.d.mts → _spec-GVe6WAfT.d.mts} +68 -59
- package/dist/{_spec-nkWAraS0.d.ts → _spec-GVe6WAfT.d.ts} +68 -59
- package/dist/bin.mjs +7 -2
- package/dist/blog-BsmKizpd.d.ts +29 -0
- package/dist/blog-Cf64mI8A.d.mts +29 -0
- package/dist/{salaries-BahBpxWT.d.ts → board-DUhgiacm.d.ts} +17 -29
- package/dist/{salaries-ChSqDBAM.d.mts → board-GGWJ4coe.d.mts} +17 -29
- package/dist/breadcrumbs-DFKr0HzU.d.mts +372 -0
- package/dist/breadcrumbs-DiccJhk4.d.ts +372 -0
- package/dist/chunk-2ETP5UH4.mjs +271 -0
- package/dist/chunk-BGKNXG4H.mjs +146 -0
- package/dist/chunk-DLSO6XXY.mjs +33 -0
- package/dist/chunk-T2KC6XGV.mjs +1060 -0
- package/dist/chunk-YEAQ3JEI.mjs +853 -0
- package/dist/doctor.js +1 -1
- package/dist/doctor.mjs +1 -1
- package/dist/filters.d.mts +25 -20
- package/dist/filters.d.ts +25 -20
- package/dist/filters.js +3 -692
- package/dist/filters.mjs +3 -690
- package/dist/format.d.mts +57 -230
- package/dist/format.d.ts +57 -230
- package/dist/format.js +355 -927
- package/dist/format.mjs +213 -931
- package/dist/go.js +11 -1
- package/dist/go.mjs +4 -29
- package/dist/index.d.mts +23 -75
- package/dist/index.d.ts +23 -75
- package/dist/index.js +11 -36
- package/dist/index.mjs +32 -313
- package/dist/{jobs-lVvKCBCU.d.mts → jobs-BuLpV8iy.d.mts} +4 -4
- package/dist/{jobs-BiEvL9d7.d.ts → jobs-QrbXOYB9.d.ts} +4 -4
- package/dist/paths.d.mts +89 -1
- package/dist/paths.d.ts +89 -1
- package/dist/paths.js +62 -14
- package/dist/paths.mjs +29 -78
- package/dist/route-contract.js +23 -13
- package/dist/route-contract.mjs +30 -1079
- package/dist/{search-CCyFWPi3.d.mts → search-Cr4noJJZ.d.mts} +14 -3
- package/dist/{search-CyRvVMUp.d.ts → search-DdsniXQv.d.ts} +14 -3
- package/dist/seo.d.mts +33 -189
- package/dist/seo.d.ts +33 -189
- package/dist/seo.js +197 -718
- package/dist/seo.mjs +44 -1417
- package/dist/server.d.mts +5 -5
- package/dist/server.d.ts +5 -5
- package/dist/server.mjs +5 -31
- package/dist/sitemap.d.mts +5 -5
- package/dist/sitemap.d.ts +5 -5
- package/dist/sitemap.js +25 -14
- package/dist/sitemap.mjs +22 -97
- package/dist/skills.js +1 -1
- package/dist/skills.mjs +1 -1
- package/dist/suggest.d.mts +2 -2
- package/dist/suggest.d.ts +2 -2
- package/dist/suggest.js +17 -3
- package/dist/suggest.mjs +17 -3
- package/dist/well-known.js +23 -13
- package/dist/well-known.mjs +4 -421
- package/package.json +5 -4
- package/skills/cavuno-board-applications/SKILL.md +4 -1
- package/skills/cavuno-board-filters/SKILL.md +11 -12
- package/skills/cavuno-board-format/SKILL.md +68 -56
- package/skills/cavuno-board-i18n/SKILL.md +102 -21
- package/skills/cavuno-board-search-suggestions/SKILL.md +8 -3
- package/skills/cavuno-board-seo/BLOG.md +4 -1
- package/skills/cavuno-board-seo/JOB_AND_BREADCRUMBS.md +5 -2
- package/skills/cavuno-board-seo/LISTING.md +35 -12
- package/skills/cavuno-board-seo/SALARY.md +83 -18
- package/skills/cavuno-board-seo/SKILL.md +3 -2
- package/skills/manifest.json +3 -3
- package/dist/board-NE98PlwN.d.ts +0 -16
- package/dist/board-muu8BI8P.d.mts +0 -16
- package/dist/ui-copy-CCkz6CVF.d.mts +0 -230
- package/dist/ui-copy-CCkz6CVF.d.ts +0 -230
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,427 @@
|
|
|
3
3
|
This changelog records changes that affect Board API compatibility, exported
|
|
4
4
|
types, runtime behavior, or supported integration patterns.
|
|
5
5
|
|
|
6
|
+
## 4.0.0 — 2026-08-03
|
|
7
|
+
|
|
8
|
+
- **Lossy seams:** Resolution is fine;
|
|
9
|
+
discarding the input is not. After every call the application must
|
|
10
|
+
still be able to produce a different legitimate rendering.
|
|
11
|
+
- `resolveCustomFieldDisplay` multi-select returns
|
|
12
|
+
`{ kind: 'multi_select', values: string[] }` instead of a
|
|
13
|
+
pre-joined `kind: 'text'` sentence. `Intl.ListFormat` (or chips /
|
|
14
|
+
short style / disjunction) moves to the app — same reason numbers
|
|
15
|
+
stay raw. Leading `language` stays for signature stability; the SDK no longer reads it for multi-select.
|
|
16
|
+
- `buildSalaryFaq` average entries carry `avgMin`, `avgMax`, and
|
|
17
|
+
`currency` alongside the compact `range` convenience. FAQ prose can
|
|
18
|
+
reformat with `notation: 'standard'` without re-threading the detail
|
|
19
|
+
object. `label` is still echoed so FAQ mapping is a pure map over
|
|
20
|
+
entries (the SDK never reads it for formatting).
|
|
21
|
+
- `formatSalaryStat` / `formatSalaryStatRange` accept optional
|
|
22
|
+
`notation` with the same magnitude default as `formatSalaryRange`
|
|
23
|
+
(`|value| ≥ 1000` → compact; smaller → standard). `$90K` vs
|
|
24
|
+
`$90,000` is register, not locale.
|
|
25
|
+
- : remove `ListFormat` from "what the SDK keeps"; add the
|
|
26
|
+
mechanical recoverability test and worked table; document Google
|
|
27
|
+
controlled vocabularies (`EDUCATION_TO_CREDENTIAL`,
|
|
28
|
+
`EMPLOYMENT_TYPE_TO_GOOGLE`) as wire values, not copy; correct that
|
|
29
|
+
timeframe units ship as the wire enum, not unit words from `Intl`.
|
|
30
|
+
|
|
31
|
+
- **Pre-publish blockers (salary format + SEO paths):** Closes the final
|
|
32
|
+
review that blocked publish.
|
|
33
|
+
- `formatSalaryStat` / `formatSalaryStatRange` null-guard currency
|
|
34
|
+
(`currency?.trim()`) so a runtime `null` returns `null` instead of
|
|
35
|
+
`TypeError` — matches `formatSalaryRange` and the skill/changelog
|
|
36
|
+
contract (`string | null`).
|
|
37
|
+
- Non-finite amounts (`NaN`, `±Infinity`) return `null` across
|
|
38
|
+
`formatSalaryStat`, `formatSalaryStatRange`, and `formatSalaryRange`
|
|
39
|
+
(they are `number` at the type level and slip past `== null`).
|
|
40
|
+
- Identical range ends (`min === max`) format as a fixed salary
|
|
41
|
+
(single amount), not ICU's `approximatelySign` form (`~$140K`). A
|
|
42
|
+
posted pin is not a degenerate range.
|
|
43
|
+
- `formatSalaryRange` defaults `notation` by **magnitude**: compact
|
|
44
|
+
only where ICU shortens (`|value| ≥ 1000`); smaller values stay
|
|
45
|
+
`standard` so hourly/day rates keep CLDR minor units (`$22.50`,
|
|
46
|
+
`KWD 22.567`). Explicit `notation` still wins.
|
|
47
|
+
- `listingJsonLd` builds job URLs via `jobDetailPath` / `boardUrl`
|
|
48
|
+
(drops company-less jobs rather than linking `/jobs/{slug}`); omits
|
|
49
|
+
`BreadcrumbList` when the trail has fewer than 2 crumbs.
|
|
50
|
+
`buildJobBreadcrumbs` uses `paths/` helpers instead of hand-built
|
|
51
|
+
strings.
|
|
52
|
+
- `companySalaryJsonLd` / `locationSalaryJsonLd` accept omitted
|
|
53
|
+
`options` and return `null` (siblings already did; these threw).
|
|
54
|
+
|
|
55
|
+
- **i18n audit (format / paths / skills):** Closes the specialist audit
|
|
56
|
+
against ICU/CLDR/W3C practice. Architecture (discriminant seams, no
|
|
57
|
+
composition, `Intl`-over-catalogs) stands; defects were concentrated in
|
|
58
|
+
timezone pinning, CLDR width choice, and structure-vs-support locale
|
|
59
|
+
validation.
|
|
60
|
+
- `formatMonthYear` pins `timeZone: 'UTC'` (and date-only inputs as UTC
|
|
61
|
+
midnight) so full ISO timestamps agree with `formatDate` under every
|
|
62
|
+
host zone — no more `"Mai 2023"` / `"Dec 2022"` for June / Jan UTC.
|
|
63
|
+
- `formatPublishedRelativeDate` uses RTF `style: 'short'` (not
|
|
64
|
+
`narrow`). English reads `"5 days ago"`; French / Russian / Romanian
|
|
65
|
+
no longer degrade to signed numbers (`-5 j`).
|
|
66
|
+
- `normalizeLocale` requires `Intl.NumberFormat.supportedLocalesOf` —
|
|
67
|
+
well-formed but unsupported tags (`xx`, `qqq`, `und`, …) return
|
|
68
|
+
`null` instead of silently using the host default. Contract in
|
|
69
|
+
`locale.ts`, `salary-range.ts`, and `skills/cavuno-board-format` is
|
|
70
|
+
now enforced.
|
|
71
|
+
- Currency fraction digits: compact notation keeps a one-digit cap
|
|
72
|
+
(documented); `notation: 'standard'` (optional override on
|
|
73
|
+
`formatSalaryRange`) lets each currency's CLDR minor units win.
|
|
74
|
+
Default notation is magnitude-selected (see pre-publish blockers
|
|
75
|
+
above), not unconditional compact.
|
|
76
|
+
- `resolveCustomFieldDisplay` emits `kind: 'number'` with the raw
|
|
77
|
+
value (not pre-stringified `kind: 'text'`), so the app formats with
|
|
78
|
+
`Intl.NumberFormat`.
|
|
79
|
+
- Path helpers percent-encode dynamic segments (non-ASCII board-language
|
|
80
|
+
slugs become URIs for sitemap `<loc>`, canonical, email). `:` is kept
|
|
81
|
+
for route-contract templates; already-encoded input is not
|
|
82
|
+
double-encoded.
|
|
83
|
+
- Filter / suggest Set keys NFC-normalize; suggest `minChars` counts
|
|
84
|
+
graphemes (`Intl.Segmenter`), so a single CJK character qualifies.
|
|
85
|
+
- `skills/cavuno-board-i18n` carries the five obligations
|
|
86
|
+
(plural selection, gender, bidi isolation with FSI/PDI, grapheme
|
|
87
|
+
truncation, locale-aware case) and extends the completion gate.
|
|
88
|
+
Salary-range / format skills document bidi isolation at the join
|
|
89
|
+
site rather than returning `{ value, dir }`.
|
|
90
|
+
|
|
91
|
+
- **Rule (word order):** The SDK never decides what goes next to what.
|
|
92
|
+
"No hardcoded strings" is the easy half. Word order is the half that
|
|
93
|
+
survives a grep, because every operand is data and only the
|
|
94
|
+
*arrangement* is English. A template with two data holes and a space
|
|
95
|
+
(or `|`, or `/`) between them is an English sentence with the words
|
|
96
|
+
removed. The SDK formats with `Intl`, returns structure, and leaves
|
|
97
|
+
composition — counters, particles, separators, prefix vs postfix — to
|
|
98
|
+
the application. Invalid locales prefer `null` over English-arranged
|
|
99
|
+
fallbacks; underscore tags (`ja_JP`) normalize to BCP-47 (`ja-JP`) so
|
|
100
|
+
one typo does not drop every formatter into English at once.
|
|
101
|
+
|
|
102
|
+
- **Breaking:** `listingHead` requires a caller-supplied `title` (same
|
|
103
|
+
ownership as `description`). Removed `heading`, `boardName`, `count`,
|
|
104
|
+
and `language` from `ListingHeadOptions` — the SDK no longer composes
|
|
105
|
+
`` `${count} ${heading} | ${boardName}` `` (Japanese wants
|
|
106
|
+
`1,225件の求人`, not `1,225 求人 | …`). Pass the finished title from
|
|
107
|
+
the board's message catalog. Also removes the only unguarded
|
|
108
|
+
`Intl.NumberFormat` construction in the SDK (a malformed board
|
|
109
|
+
language no longer crashes the listing page).
|
|
110
|
+
|
|
111
|
+
- **Breaking:** `formatSalaryRange` returns
|
|
112
|
+
`{ text, timeframe, bound } | null`. `text` is the `Intl`-formatted
|
|
113
|
+
amount/range only; `timeframe` is the **wire enum** (`per_year`,
|
|
114
|
+
`per_hour`, …) — never a unit word from `Intl` and never an owner
|
|
115
|
+
override. Applications map the enum through their own catalog and
|
|
116
|
+
compose amount↔timeframe order (`$90K / year` vs `年収900万〜1200万円`).
|
|
117
|
+
Missing currency, invalid locale, or rejected currency format →
|
|
118
|
+
`null` (no bare number, no English `M`/`k`/`$`).
|
|
119
|
+
|
|
120
|
+
- **Breaking:** Removed `SalaryTimeframeOverrides` from
|
|
121
|
+
`@cavuno/board/format`. Owner-edited unit labels are application-side
|
|
122
|
+
copy keyed off the wire `timeframe` enum the helper returns; the SDK
|
|
123
|
+
no longer accepts an override map.
|
|
124
|
+
|
|
125
|
+
- **Breaking:** `GET/POST …/me/saved-jobs` embeds a slim `job_card` (the
|
|
126
|
+
same shape as jobs list/search), not a full `public_job` with HTML
|
|
127
|
+
`description`. Fetch the job detail by slug when the full body is
|
|
128
|
+
needed. `remoteLocationLabel` is resolved server-side from
|
|
129
|
+
`remoteRules`, so region-limited remote jobs keep their real region
|
|
130
|
+
label instead of a client-side lossy guess.
|
|
131
|
+
|
|
132
|
+
- **Breaking:** Removed `fullJobToCard` from `@cavuno/board/format`. The
|
|
133
|
+
saved-jobs endpoint now serves cards; convert with the same card VM as
|
|
134
|
+
listings (`toJobCardVM(saved.job, …)`). That helper was the last
|
|
135
|
+
user-facing hardcoded English in the SDK (`Remote`, `Worldwide`,
|
|
136
|
+
`(hybrid)`).
|
|
137
|
+
|
|
138
|
+
- **Breaking:** `formatSalaryRange`'s board language is required — no
|
|
139
|
+
silent `en` default. `formatPublishedRelativeDate` under one minute uses
|
|
140
|
+
`Intl.RelativeTimeFormat` (`"now"` / `"jetzt"` / `"maintenant"`), not a
|
|
141
|
+
hardcoded English string. `resolveCustomFieldDisplay` takes the board
|
|
142
|
+
language as its leading parameter (signature stability). Multi-select
|
|
143
|
+
originally joined via `Intl.ListFormat`; that was lossy and now returns
|
|
144
|
+
`kind: 'multi_select'` with `values: string[]` (see Lossy seams above).
|
|
145
|
+
|
|
146
|
+
- **Breaking:** Renamed and currency-aware salary-stat formatters:
|
|
147
|
+
`formatUsd` / `formatRange` → `formatSalaryStat(locale, value, currency)` /
|
|
148
|
+
`formatSalaryStatRange(locale, min, max, currency)`. The `/format`
|
|
149
|
+
aliases `formatSalaryStatUsd` / `formatSalaryStatRange` are replaced by
|
|
150
|
+
the same names (no USD-only alias). `buildSalaryFaq` takes `currency`
|
|
151
|
+
as a fourth argument so FAQ ranges match the board's money. Both formatters
|
|
152
|
+
return `string | null` (`null` when currency is empty or `Intl` rejects
|
|
153
|
+
the locale/currency) instead of inventing English `M`/`k` or a bare `$`.
|
|
154
|
+
|
|
155
|
+
- **Breaking:** `formatSalaryRange` first moved from `string | null` to
|
|
156
|
+
`{ text; bound } | null` so open floors/ceilings are named by the app;
|
|
157
|
+
the timeframe is now a third field (see top of this heading) rather than
|
|
158
|
+
a suffix glued onto `text`.
|
|
159
|
+
|
|
160
|
+
- **Breaking:** Unit, currency, and date *words* derive from `Intl`;
|
|
161
|
+
`skills/` snippets show shape (catalog placeholders), not pasteable
|
|
162
|
+
English copy or join templates. The SDK formats with `Intl` and returns
|
|
163
|
+
structure; it does not supply open-range chrome, nav/button copy, filter
|
|
164
|
+
labels, SEO sentence templates, or amount↔unit arrangement. Dropped the
|
|
165
|
+
private chrome-copy package dependency entirely. Closed ranges use
|
|
166
|
+
`Intl.NumberFormat.prototype.formatRange` (locale owns separator,
|
|
167
|
+
repeated currency/magnitude, and bidi marks). Open-range chrome and
|
|
168
|
+
amount↔timeframe order are application-owned via `bound` + `timeframe`.
|
|
169
|
+
`buildJobBreadcrumbs` no longer takes `language` or `labels` parameters —
|
|
170
|
+
it returns structural `kind`s for chrome crumbs (`home` / `jobs`) instead
|
|
171
|
+
of display names; applications supply the words when mapping to
|
|
172
|
+
`BreadcrumbList`. `seniorityLabels` / `sortLabels` / `formatSeniority`
|
|
173
|
+
are removed. Type exports removed with the chrome surface:
|
|
174
|
+
`AlertsCopy`, `ApplyCopy`, `BlogCopy`, `BoardLabelOverrides`,
|
|
175
|
+
`BreadcrumbsCopy`, `CopyLinkCopy`, `EntityCopy`, `FooterCopy`,
|
|
176
|
+
`JobCardCopy`, `JobDetailCopy`, `JobSearchCopy`, `NavCopy`,
|
|
177
|
+
`PaginationCopy`, `SalaryCopy`, `SalaryFrames`, `SalaryLexicon`,
|
|
178
|
+
`SeniorityKey`, `UiCopy`.
|
|
179
|
+
|
|
180
|
+
- **Breaking:** `listingHead` requires a caller-supplied `description` (and
|
|
181
|
+
now `title` — see top of this heading). It no longer composes
|
|
182
|
+
`Browse … on …` (English on every locale) or case-folds the heading with
|
|
183
|
+
locale-blind `.toLowerCase()`.
|
|
184
|
+
|
|
185
|
+
- **Breaking:** `buildSalaryFaq` returns structured
|
|
186
|
+
`SalaryFaqEntry` values (`kind: 'average' | 'methodology'` plus label,
|
|
187
|
+
`formatRange` range, and `jobCount`) instead of English FAQ prose.
|
|
188
|
+
Map entries to `{ q, a }` in the application (with proper plural rules),
|
|
189
|
+
then pass them to `faqJsonLd`.
|
|
190
|
+
|
|
191
|
+
- **Breaking:** Salary JSON-LD builders
|
|
192
|
+
(`titleSalaryJsonLd`, `skillSalaryJsonLd`, `locationSalaryJsonLd`,
|
|
193
|
+
`crossAxisSalaryJsonLd`, `companySalaryJsonLd`,
|
|
194
|
+
`companyCategorySalaryJsonLd`) use data-only `name` fields (entity
|
|
195
|
+
names). Removed English framing
|
|
196
|
+
(`… salary (all levels)`, `Average Salary in …`, `Jobs at …`,
|
|
197
|
+
`… salary in …`, trailing ` salary`).
|
|
198
|
+
|
|
199
|
+
- **Breaking:** Dropped the dead `locale` parameter from
|
|
200
|
+
`titleSalaryJsonLd`, `crossAxisSalaryJsonLd`, `companySalaryJsonLd`,
|
|
201
|
+
and `companyCategorySalaryJsonLd` — it was discarded (`void locale`)
|
|
202
|
+
and did not change output. Per-seniority distribution names are no
|
|
203
|
+
longer title-cased wire enums (`entry_level` → `Entry level`) and are
|
|
204
|
+
no longer joined as `` `${seniority} ${entity}` `` (English word order).
|
|
205
|
+
Pass optional `seniorityName({ seniority, entity })` that returns the
|
|
206
|
+
finished string in board-language order, or omit it so those
|
|
207
|
+
distributions ship without a `name` (valid structured data;
|
|
208
|
+
wrong-language / wrong-order names are not).
|
|
209
|
+
|
|
210
|
+
- **Breaking:** `formatSalaryRange` never invents USD when
|
|
211
|
+
`salaryCurrency` is null/empty — returns `null`, matching
|
|
212
|
+
`createJobPostingJsonLd` which omits `baseSalary` without a currency.
|
|
213
|
+
Invalid locales and rejected currency formats also return `null` rather
|
|
214
|
+
than English `M`/`k`/`$` or a bare number.
|
|
215
|
+
|
|
216
|
+
- **Breaking:** `formatMonthYear` returns `string | null` — `null` on
|
|
217
|
+
missing/unparseable input (never the English literal `"Invalid Date"`),
|
|
218
|
+
and accepts full ISO timestamps in addition to date-only forms.
|
|
219
|
+
|
|
220
|
+
- **Breaking:** `locationSalaryJsonLd` and `companySalaryJsonLd` no
|
|
221
|
+
longer emit an `Occupation` / `OccupationAggregationByEmployer`
|
|
222
|
+
envelope with the place or employer as `name` (neither is an
|
|
223
|
+
occupation). They emit an `ItemList` of the occupations present on
|
|
224
|
+
that page (`topCategories` / `byCategory`), each linking to its own
|
|
225
|
+
occupation page via required `occupationUrl`. Return `null` when
|
|
226
|
+
there are no per-occupation rows (or when the location is not
|
|
227
|
+
city/locality). The other four builders keep `Occupation`.
|
|
228
|
+
|
|
229
|
+
- **Breaking:** `board.taxonomy.suggestions.list` and
|
|
230
|
+
`GET /v1/boards/{identifier}/suggestions` are removed. Use
|
|
231
|
+
`board.search.suggest({ types: ['category', 'skill'], … })` instead. The
|
|
232
|
+
`taxonomy` namespace itself stays (`categories`, `skills`, `places`,
|
|
233
|
+
`remotePermits`, resolve methods).
|
|
234
|
+
|
|
235
|
+
- `board.search.suggest` accepts
|
|
236
|
+
`types?: ('company' | 'category' | 'skill' | 'market' | 'post' | 'tag')[]`.
|
|
237
|
+
Omit for every kind. `limit` applies **after** type filtering, so
|
|
238
|
+
`{ types: ['skill'], limit: 10 }` returns up to ten skills.
|
|
239
|
+
|
|
240
|
+
- `SuggestionItem` gains a `market` variant (`type: 'market'`, `slug`, `name`,
|
|
241
|
+
`companyCount`) for company-scope typeahead alongside companies.
|
|
242
|
+
|
|
243
|
+
- `SuggestionItem` gains `post` (`type: 'post'`, `id`, `slug`, `title`) and
|
|
244
|
+
`tag` (`type: 'tag'`, `id`, `slug`, `name`) for blog typeahead. Blog posts
|
|
245
|
+
and tags are public, so no extra access control.
|
|
246
|
+
|
|
247
|
+
- `@cavuno/board/paths` adds `suggestionPath(suggestion, { scope, location? })`
|
|
248
|
+
plus `jobsLocationCategoryPath` / `jobsLocationSkillPath`. A jobs-scope
|
|
249
|
+
company suggestion returns `null` — apply a `companySlug` filter; do not
|
|
250
|
+
invent `/jobs/companies/<slug>`. Blog scope routes posts via
|
|
251
|
+
`blogPostPath` and tags via `blogTagPath`.
|
|
252
|
+
|
|
253
|
+
- Added `sdk/reference/search.mdoc` and a docs-contract gate that every
|
|
254
|
+
`BoardSdk` namespace has a matching reference page.
|
|
255
|
+
|
|
256
|
+
- The sitemap no longer advertises `/talent` on boards whose talent directory
|
|
257
|
+
is `employers_only`. That page is gated behind an approved employer session,
|
|
258
|
+
so listing it pointed crawlers at the gate rather than the directory. Only a
|
|
259
|
+
`public` directory is listed.
|
|
260
|
+
|
|
261
|
+
- **Breaking:** `board.context().customFields` is now keyed by model instead of
|
|
262
|
+
a flat job-only array. Today only `job` is present; `company` / `talent` keys
|
|
263
|
+
land when those models ship (absent keys are not empty arrays).
|
|
264
|
+
|
|
265
|
+
Before:
|
|
266
|
+
|
|
267
|
+
```json
|
|
268
|
+
"customFields": [
|
|
269
|
+
{ "key": "security_clearance", "label": "Security clearance", "type": "single_select", "required": false }
|
|
270
|
+
]
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
After:
|
|
274
|
+
|
|
275
|
+
```json
|
|
276
|
+
"customFields": {
|
|
277
|
+
"job": [
|
|
278
|
+
{ "key": "security_clearance", "label": "Security clearance", "type": "single_select", "required": false }
|
|
279
|
+
]
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
Resolve job values with `resolveCustomFieldDisplay(context.customFields.job, job.customFieldValues)`
|
|
284
|
+
— the helper still takes a definitions array and did not change.
|
|
285
|
+
|
|
286
|
+
- **Breaking:** `features.talentDirectory` is now the tri-state access mode
|
|
287
|
+
(`off` | `public` | `employers_only`). The top-level
|
|
288
|
+
`talentDirectoryVisibility` field is removed — it duplicated the same gate.
|
|
289
|
+
|
|
290
|
+
Before:
|
|
291
|
+
|
|
292
|
+
```json
|
|
293
|
+
"features": { "talentDirectory": true },
|
|
294
|
+
"talentDirectoryVisibility": "employers_only"
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
After:
|
|
298
|
+
|
|
299
|
+
```json
|
|
300
|
+
"features": { "talentDirectory": "employers_only" }
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
Link `/talent` whenever `features.talentDirectory !== 'off'`. Do not treat
|
|
304
|
+
the field as a boolean — the string `'off'` is truthy in JavaScript.
|
|
305
|
+
|
|
306
|
+
- **Breaking:** `board.seo()` no longer returns presentation. Removed
|
|
307
|
+
`icons` (ico/svg/appleTouch/icon192/icon512/iconMaskable512) and
|
|
308
|
+
`manifest.themeColor`. Kept `canonicalBase`, `adsTxt`, `indexNowKey`,
|
|
309
|
+
`googleSiteVerification`, and `manifest.name`. Applications own brand
|
|
310
|
+
assets and theme tokens.
|
|
311
|
+
|
|
312
|
+
Before:
|
|
313
|
+
|
|
314
|
+
```json
|
|
315
|
+
{
|
|
316
|
+
"canonicalBase": "https://jobs.example.com",
|
|
317
|
+
"adsTxt": null,
|
|
318
|
+
"indexNowKey": null,
|
|
319
|
+
"googleSiteVerification": null,
|
|
320
|
+
"icons": { "ico": "…", "svg": null, "appleTouch": null, "icon192": null, "icon512": null, "iconMaskable512": null },
|
|
321
|
+
"manifest": { "name": "Acme Jobs", "themeColor": "#1d4ed8" }
|
|
322
|
+
}
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
After:
|
|
326
|
+
|
|
327
|
+
```json
|
|
328
|
+
{
|
|
329
|
+
"canonicalBase": "https://jobs.example.com",
|
|
330
|
+
"adsTxt": null,
|
|
331
|
+
"indexNowKey": null,
|
|
332
|
+
"googleSiteVerification": null,
|
|
333
|
+
"manifest": { "name": "Acme Jobs" }
|
|
334
|
+
}
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
- **Breaking:** `board.context().footer` is gone. Operator-authored contact
|
|
338
|
+
and social identity move to a top-level `contact` group; editor-authored
|
|
339
|
+
presentation (`description`, `navigationOrder`, `customLinks`) is removed
|
|
340
|
+
— applications own footer layout.
|
|
341
|
+
|
|
342
|
+
Before:
|
|
343
|
+
|
|
344
|
+
```json
|
|
345
|
+
"footer": {
|
|
346
|
+
"description": "Roles from {{board_name}}.",
|
|
347
|
+
"contactEmail": "hello@acme.com",
|
|
348
|
+
"websiteUrl": "https://acme.com/",
|
|
349
|
+
"xUrl": "https://x.com/acmejobs",
|
|
350
|
+
"facebookUrl": null,
|
|
351
|
+
"linkedinUrl": "https://linkedin.com/company/acme",
|
|
352
|
+
"navigationOrder": ["home", "blog"],
|
|
353
|
+
"customLinks": [{ "id": "abc", "label": "Hub", "url": "/hub" }]
|
|
354
|
+
}
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
After:
|
|
358
|
+
|
|
359
|
+
```json
|
|
360
|
+
"contact": {
|
|
361
|
+
"email": "hello@acme.com",
|
|
362
|
+
"websiteUrl": "https://acme.com/",
|
|
363
|
+
"xUrl": "https://x.com/acmejobs",
|
|
364
|
+
"facebookUrl": null,
|
|
365
|
+
"linkedinUrl": "https://linkedin.com/company/acme"
|
|
366
|
+
}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
Brand/social URL sanitization is unchanged (absolute http(s) only).
|
|
370
|
+
`companyLegalName` / `companyAddress` were never on this public context
|
|
371
|
+
payload (they ride the legal/impressum surface); no `company` group was
|
|
372
|
+
invented here.
|
|
373
|
+
|
|
374
|
+
- **Breaking:** Chrome copy leaves the public `@cavuno/board` surface. Removed from `@cavuno/board/format`: `uiCopy`,
|
|
375
|
+
`PUBLIC_LABEL_GROUPS`, `fieldLabel`, `getSalaryLexicon`, `locationLabel`,
|
|
376
|
+
`cardLocationLabel`, `companyIntro`, and the `UiCopy` type re-export.
|
|
377
|
+
Removed from `@cavuno/board/seo`: `formatSeniority`. Removed from
|
|
378
|
+
`@cavuno/board/filters`: `seniorityLabels`, `sortLabels`.
|
|
379
|
+
|
|
380
|
+
Hosted boards import chrome words from a private package. Third-party applications own their copy in code (or their
|
|
381
|
+
message catalogs). The published SDK no longer imports or bundles that
|
|
382
|
+
package.
|
|
383
|
+
|
|
384
|
+
What stays on `@cavuno/board/format`: `formatSalaryRange` (`Intl`-backed
|
|
385
|
+
amounts, wire `timeframe` enum, and a `bound` discriminant for open ranges),
|
|
386
|
+
date helpers, `resolveCustomFieldDisplay`, `normalizeWebsiteUrl`,
|
|
387
|
+
`buildJobBreadcrumbs` (kinds + data names; no `language`/`labels` args),
|
|
388
|
+
`COUNTRY_CODES` / `countryOptions`, and the salary-stat formatters. Filter
|
|
389
|
+
vocabularies (wire enums + parsers only) stay on `@cavuno/board/filters`.
|
|
390
|
+
|
|
391
|
+
- **Breaking:** `board.legal.retrieve`, the legal types (`PublicLegalPage`,
|
|
392
|
+
`LegalPageType`, `LegalEntity`), and `GET /v1/boards/{identifier}/legal/{type}`
|
|
393
|
+
(`getBoardLegal`) are removed. Legal prose is editor-authored
|
|
394
|
+
application content, not a Board API concern.
|
|
395
|
+
|
|
396
|
+
- **Breaking:** `board.context().labels` is removed. Operator label overrides
|
|
397
|
+
no longer ride the public Board API — applications own chrome words.
|
|
398
|
+
Hosted storage for operator overrides is unchanged (hosted chrome still
|
|
399
|
+
reads the bag).
|
|
400
|
+
|
|
401
|
+
Before:
|
|
402
|
+
|
|
403
|
+
```json
|
|
404
|
+
"labels": {
|
|
405
|
+
"jobCardLabels": { "featuredLabel": "Top Job" },
|
|
406
|
+
"navLabels": { "home": "Jobs" }
|
|
407
|
+
}
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
After: field absent.
|
|
411
|
+
|
|
412
|
+
## 3.2.0 — 2026-08-03
|
|
413
|
+
|
|
414
|
+
- Added `formatSalaryStatUsd`, `formatSalaryStatRange`, `normalizeWebsiteUrl`,
|
|
415
|
+
and `buildJobBreadcrumbs` to `@cavuno/board/format`. These presentation
|
|
416
|
+
helpers previously required importing `@cavuno/board/seo`, which pulled the
|
|
417
|
+
structured-data builders into client bundles; frontends can now keep the SEO
|
|
418
|
+
entry server-only. The `@cavuno/board/seo` exports are unchanged.
|
|
419
|
+
- Split the ESM builds of the isomorphic entries into shared chunks, so a
|
|
420
|
+
selective import no longer carries unrelated builders. CommonJS output is
|
|
421
|
+
unchanged.
|
|
422
|
+
- `listingHead` accepts an optional `language` and groups the result count for
|
|
423
|
+
that locale in the title and meta description (`1,225 Jobs` / `1.225 Jobs`),
|
|
424
|
+
matching how listing bodies render the same number. Callers that omit
|
|
425
|
+
`language` keep byte-identical output.
|
|
426
|
+
|
|
6
427
|
## 3.1.0 — 2026-08-03
|
|
7
428
|
|
|
8
429
|
- Added direct skills.sh installation guidance and repository grouping for all
|
package/README.md
CHANGED
|
@@ -53,8 +53,12 @@ const { name, language, features } = await board.context();
|
|
|
53
53
|
const page = await board.jobs.list({ limit: 20 });
|
|
54
54
|
const job = await board.jobs.retrieve('senior-chef');
|
|
55
55
|
|
|
56
|
-
// Federated search-dropdown suggestions (companies
|
|
57
|
-
const { items } = await board.search.suggest({
|
|
56
|
+
// Federated search-dropdown suggestions (companies, markets, taxonomy terms).
|
|
57
|
+
const { items } = await board.search.suggest({
|
|
58
|
+
q: 'acme',
|
|
59
|
+
limit: 10,
|
|
60
|
+
types: ['company', 'skill'],
|
|
61
|
+
});
|
|
58
62
|
// Or the headless controller (debounce / abort / stale-drop built in):
|
|
59
63
|
// import { createSuggestController } from '@cavuno/board/suggest';
|
|
60
64
|
// const suggest = createSuggestController(board);
|
|
@@ -245,19 +245,9 @@ interface components {
|
|
|
245
245
|
* @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
246
|
*/
|
|
247
247
|
canonicalBase: string;
|
|
248
|
-
/** @description
|
|
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`. */
|
|
248
|
+
/** @description Web-manifest name only. Theme color and icons left the surface in 4.0.0: applications own brand assets and presentation tokens. */
|
|
258
249
|
manifest: {
|
|
259
250
|
name: string;
|
|
260
|
-
themeColor: string;
|
|
261
251
|
};
|
|
262
252
|
};
|
|
263
253
|
BoardUser: {
|
|
@@ -1339,23 +1329,6 @@ interface components {
|
|
|
1339
1329
|
/** Format: email */
|
|
1340
1330
|
email: string;
|
|
1341
1331
|
};
|
|
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
1332
|
LocationSalaryDetail: {
|
|
1360
1333
|
/** @enum {string} */
|
|
1361
1334
|
object: "location_salary_detail";
|
|
@@ -1465,6 +1438,23 @@ interface components {
|
|
|
1465
1438
|
jobCount: number;
|
|
1466
1439
|
}[];
|
|
1467
1440
|
};
|
|
1441
|
+
MarketSuggestion: {
|
|
1442
|
+
/** @enum {string} */
|
|
1443
|
+
object: "suggestion";
|
|
1444
|
+
/**
|
|
1445
|
+
* @description discriminator enum property added by openapi-typescript
|
|
1446
|
+
* @enum {string}
|
|
1447
|
+
*/
|
|
1448
|
+
type: "market";
|
|
1449
|
+
/** @description Stable market identity (source slug; markets are slug-keyed). */
|
|
1450
|
+
id: string;
|
|
1451
|
+
/** @description Public market URL slug. */
|
|
1452
|
+
slug: string;
|
|
1453
|
+
/** @description Market display name. */
|
|
1454
|
+
name: string;
|
|
1455
|
+
/** @description Number of companies on the board in this market. */
|
|
1456
|
+
companyCount: number;
|
|
1457
|
+
};
|
|
1468
1458
|
Message: {
|
|
1469
1459
|
id: string;
|
|
1470
1460
|
/** @enum {string} */
|
|
@@ -1561,6 +1551,21 @@ interface components {
|
|
|
1561
1551
|
messagesPerPeriod: string;
|
|
1562
1552
|
};
|
|
1563
1553
|
};
|
|
1554
|
+
PostSuggestion: {
|
|
1555
|
+
/** @enum {string} */
|
|
1556
|
+
object: "suggestion";
|
|
1557
|
+
/**
|
|
1558
|
+
* @description discriminator enum property added by openapi-typescript
|
|
1559
|
+
* @enum {string}
|
|
1560
|
+
*/
|
|
1561
|
+
type: "post";
|
|
1562
|
+
/** @description Stable published blog post identity. */
|
|
1563
|
+
id: string;
|
|
1564
|
+
/** @description Public blog post URL slug. */
|
|
1565
|
+
slug: string;
|
|
1566
|
+
/** @description Post title. */
|
|
1567
|
+
title: string;
|
|
1568
|
+
};
|
|
1564
1569
|
PublicBlogAdjacentPosts: {
|
|
1565
1570
|
/** @enum {string} */
|
|
1566
1571
|
object: "blog_adjacent_posts";
|
|
@@ -1651,7 +1656,11 @@ interface components {
|
|
|
1651
1656
|
candidates: boolean;
|
|
1652
1657
|
employers: boolean;
|
|
1653
1658
|
blog: boolean;
|
|
1654
|
-
|
|
1659
|
+
/**
|
|
1660
|
+
* @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`.
|
|
1661
|
+
* @enum {string}
|
|
1662
|
+
*/
|
|
1663
|
+
talentDirectory: "off" | "public" | "employers_only";
|
|
1655
1664
|
registrationWall: boolean;
|
|
1656
1665
|
passwordProtected: boolean;
|
|
1657
1666
|
publicJobSubmission: boolean;
|
|
@@ -1662,11 +1671,6 @@ interface components {
|
|
|
1662
1671
|
/** @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
1672
|
messaging: boolean;
|
|
1664
1673
|
};
|
|
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
1674
|
analytics: {
|
|
1671
1675
|
ga4MeasurementId: string | null;
|
|
1672
1676
|
gtmId: string | null;
|
|
@@ -1674,33 +1678,23 @@ interface components {
|
|
|
1674
1678
|
linkedInPartnerId: string | null;
|
|
1675
1679
|
cookieConsentRequired: boolean;
|
|
1676
1680
|
};
|
|
1677
|
-
/** @description Operator-defined custom
|
|
1678
|
-
customFields:
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
[key: string]: {
|
|
1682
|
-
[key: string]: string;
|
|
1683
|
-
};
|
|
1681
|
+
/** @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. */
|
|
1682
|
+
customFields: {
|
|
1683
|
+
/** @description This model's custom-field definitions in display order. Empty when the board defines none for the model. */
|
|
1684
|
+
job: components["schemas"]["CustomFieldDefinition"][];
|
|
1684
1685
|
};
|
|
1685
|
-
/** @description
|
|
1686
|
-
|
|
1687
|
-
/** @description
|
|
1688
|
-
|
|
1689
|
-
/** @description
|
|
1690
|
-
contactEmail: string | null;
|
|
1686
|
+
/** @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). */
|
|
1687
|
+
contact: {
|
|
1688
|
+
/** @description Public contact email (render as `mailto:`). Operator-set in settings › Contact & company details. */
|
|
1689
|
+
email: string | null;
|
|
1690
|
+
/** @description Company website URL, sanitized to absolute http(s). Null when unset or non-http(s). */
|
|
1691
1691
|
websiteUrl: string | null;
|
|
1692
1692
|
/** @description X (Twitter) profile URL, normalized from the stored handle. */
|
|
1693
1693
|
xUrl: string | null;
|
|
1694
|
+
/** @description Facebook profile URL, sanitized to absolute http(s). Null when unset or non-http(s). */
|
|
1694
1695
|
facebookUrl: string | null;
|
|
1696
|
+
/** @description LinkedIn profile URL, sanitized to absolute http(s). Null when unset or non-http(s). */
|
|
1695
1697
|
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
1698
|
};
|
|
1705
1699
|
};
|
|
1706
1700
|
PublicCompaniesSearchBody: {
|
|
@@ -1841,7 +1835,7 @@ interface components {
|
|
|
1841
1835
|
slug: string;
|
|
1842
1836
|
name: string;
|
|
1843
1837
|
}[];
|
|
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. */
|
|
1838
|
+
/** @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
1839
|
customFieldValues: {
|
|
1846
1840
|
[key: string]: string | string[] | boolean | number;
|
|
1847
1841
|
};
|
|
@@ -2186,7 +2180,7 @@ interface components {
|
|
|
2186
2180
|
jobId: string;
|
|
2187
2181
|
/** Format: date-time */
|
|
2188
2182
|
savedAt: string;
|
|
2189
|
-
job: components["schemas"]["
|
|
2183
|
+
job: components["schemas"]["PublicJobCard"];
|
|
2190
2184
|
};
|
|
2191
2185
|
SendWorkEmailBody: {
|
|
2192
2186
|
/** Format: email */
|
|
@@ -2347,10 +2341,25 @@ interface components {
|
|
|
2347
2341
|
object: "suggest_result";
|
|
2348
2342
|
/** @description The trimmed query string that produced these suggestions. */
|
|
2349
2343
|
query: string;
|
|
2350
|
-
/** @description Interleaved company
|
|
2344
|
+
/** @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
2345
|
items: components["schemas"]["SuggestionItem"][];
|
|
2352
2346
|
};
|
|
2353
|
-
SuggestionItem: components["schemas"]["CompanySuggestion"] | components["schemas"]["TermSuggestion"];
|
|
2347
|
+
SuggestionItem: components["schemas"]["CompanySuggestion"] | components["schemas"]["MarketSuggestion"] | components["schemas"]["TermSuggestion"] | components["schemas"]["PostSuggestion"] | components["schemas"]["TagSuggestion"];
|
|
2348
|
+
TagSuggestion: {
|
|
2349
|
+
/** @enum {string} */
|
|
2350
|
+
object: "suggestion";
|
|
2351
|
+
/**
|
|
2352
|
+
* @description discriminator enum property added by openapi-typescript
|
|
2353
|
+
* @enum {string}
|
|
2354
|
+
*/
|
|
2355
|
+
type: "tag";
|
|
2356
|
+
/** @description Stable tag identity (source slug; tags are slug-keyed). */
|
|
2357
|
+
id: string;
|
|
2358
|
+
/** @description Public blog tag URL slug. */
|
|
2359
|
+
slug: string;
|
|
2360
|
+
/** @description Tag display name. */
|
|
2361
|
+
name: string;
|
|
2362
|
+
};
|
|
2354
2363
|
TalentAccess: {
|
|
2355
2364
|
/** @enum {string} */
|
|
2356
2365
|
object: "talent_access";
|