@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/format.mjs CHANGED
@@ -1,67 +1,77 @@
1
- import {
2
- fieldLabel
3
- } from "./chunk-LUGJWOT7.mjs";
4
1
  import {
5
2
  buildJobBreadcrumbs,
6
- formatRange,
7
- formatUsd,
3
+ formatSalaryStat,
4
+ formatSalaryStatRange,
5
+ normalizeLocale,
8
6
  normalizeWebsiteUrl
9
- } from "./chunk-STE77MAT.mjs";
10
- import {
11
- PUBLIC_LABEL_GROUPS,
12
- getSalaryLexicon,
13
- uiCopy
14
- } from "./chunk-IJCJ26SQ.mjs";
7
+ } from "./chunk-YEAQ3JEI.mjs";
8
+ import "./chunk-BGKNXG4H.mjs";
15
9
 
16
10
  // src/format/dates.ts
17
- var rtfNarrowCache = /* @__PURE__ */ new Map();
11
+ var rtfShortCache = /* @__PURE__ */ new Map();
12
+ var rtfNowCache = /* @__PURE__ */ new Map();
18
13
  var dtfCache = /* @__PURE__ */ new Map();
19
- function getNarrowRelativeTimeFormat(language) {
20
- const key = language ?? "en";
21
- let rtf = rtfNarrowCache.get(key);
14
+ var monthYearCache = /* @__PURE__ */ new Map();
15
+ function getShortRelativeTimeFormat(language) {
16
+ let rtf = rtfShortCache.get(language);
22
17
  if (!rtf) {
23
- rtf = new Intl.RelativeTimeFormat(key, {
18
+ rtf = new Intl.RelativeTimeFormat(language, {
24
19
  numeric: "always",
25
- style: "narrow"
20
+ style: "short"
21
+ });
22
+ rtfShortCache.set(language, rtf);
23
+ }
24
+ return rtf;
25
+ }
26
+ function getNowRelativeTimeFormat(language) {
27
+ let rtf = rtfNowCache.get(language);
28
+ if (!rtf) {
29
+ rtf = new Intl.RelativeTimeFormat(language, {
30
+ numeric: "auto",
31
+ style: "short"
26
32
  });
27
- rtfNarrowCache.set(key, rtf);
33
+ rtfNowCache.set(language, rtf);
28
34
  }
29
35
  return rtf;
30
36
  }
31
37
  function getDateTimeFormat(language) {
32
- const key = language ?? "en";
33
- let dtf = dtfCache.get(key);
38
+ let dtf = dtfCache.get(language);
34
39
  if (!dtf) {
35
- dtf = new Intl.DateTimeFormat(key, {
40
+ dtf = new Intl.DateTimeFormat(language, {
36
41
  dateStyle: "medium",
37
42
  timeZone: "UTC"
38
43
  });
39
- dtfCache.set(key, dtf);
44
+ dtfCache.set(language, dtf);
40
45
  }
41
46
  return dtf;
42
47
  }
43
- function formatNarrow(value, unit, language) {
48
+ function getMonthYearFormat(language) {
49
+ let dtf = monthYearCache.get(language);
50
+ if (!dtf) {
51
+ dtf = new Intl.DateTimeFormat(language, {
52
+ month: "short",
53
+ year: "numeric",
54
+ timeZone: "UTC"
55
+ });
56
+ monthYearCache.set(language, dtf);
57
+ }
58
+ return dtf;
59
+ }
60
+ function formatShort(value, unit, language) {
44
61
  try {
45
- return getNarrowRelativeTimeFormat(language).format(value, unit);
62
+ return getShortRelativeTimeFormat(language).format(value, unit);
46
63
  } catch {
47
- const shortMap = {
48
- year: "y",
49
- month: "mo",
50
- week: "w",
51
- day: "d",
52
- hour: "h",
53
- minute: "m",
54
- second: "s"
55
- };
56
- return `${Math.abs(value)}${shortMap[unit] ?? unit}`;
64
+ return null;
57
65
  }
58
66
  }
59
67
  function formatDate(locale, value) {
60
68
  if (!value) return null;
61
69
  const parsed = new Date(value);
62
70
  if (Number.isNaN(parsed.getTime())) return null;
71
+ const normalized = normalizeLocale(locale);
72
+ if (!normalized) return null;
63
73
  try {
64
- return getDateTimeFormat(locale).format(parsed);
74
+ return getDateTimeFormat(normalized).format(parsed);
65
75
  } catch {
66
76
  return parsed.toISOString().split("T")[0];
67
77
  }
@@ -70,6 +80,8 @@ function formatPublishedRelativeDate(locale, value, referenceNowMs = Date.now())
70
80
  if (!value) return null;
71
81
  const parsed = new Date(value);
72
82
  if (Number.isNaN(parsed.getTime())) return null;
83
+ const normalized = normalizeLocale(locale);
84
+ if (!normalized) return null;
73
85
  const diff = referenceNowMs - parsed.getTime();
74
86
  const absDiff = Math.abs(diff);
75
87
  const minute = 60 * 1e3;
@@ -79,7 +91,11 @@ function formatPublishedRelativeDate(locale, value, referenceNowMs = Date.now())
79
91
  const month = 30 * day;
80
92
  const year = 365 * day;
81
93
  if (absDiff < minute) {
82
- return "now";
94
+ try {
95
+ return getNowRelativeTimeFormat(normalized).format(0, "second");
96
+ } catch {
97
+ return formatShort(0, "second", normalized);
98
+ }
83
99
  }
84
100
  const units = [
85
101
  { unit: "year", ms: year },
@@ -93,249 +109,152 @@ function formatPublishedRelativeDate(locale, value, referenceNowMs = Date.now())
93
109
  if (absDiff >= candidate.ms) {
94
110
  const valueInUnit = Math.round(absDiff / candidate.ms);
95
111
  const direction2 = diff >= 0 ? -valueInUnit : valueInUnit;
96
- return formatNarrow(direction2, candidate.unit, locale);
112
+ return formatShort(direction2, candidate.unit, normalized);
97
113
  }
98
114
  }
99
115
  const seconds = Math.round(absDiff / 1e3);
100
116
  const direction = diff >= 0 ? -seconds : seconds;
101
- return formatNarrow(direction, "second", locale);
117
+ return formatShort(direction, "second", normalized);
102
118
  }
103
119
  function formatMonthYear(locale, dateStr) {
104
- const date = /* @__PURE__ */ new Date(dateStr + "T00:00:00");
105
- return date.toLocaleDateString(locale, {
106
- month: "short",
107
- year: "numeric"
108
- });
109
- }
110
-
111
- // src/format/location.ts
112
- function locationLabel(locale, job) {
113
- const office = job.officeLocations[0];
114
- const place = office ? office.displayName ?? [office.city ?? office.locality, office.country].filter(Boolean).join(", ") : null;
115
- if (job.remoteOption === "remote") {
116
- return job.remoteWorldwide ? "Remote (worldwide)" : "Remote";
117
- }
118
- if (!place) return fieldLabel(locale, job.remoteOption) ?? "";
119
- return job.remoteOption === "hybrid" ? `${place} (hybrid)` : place;
120
- }
121
- function cardLocationLabel(locale, job) {
122
- if (job.remoteOption === "remote") {
123
- return job.remoteLocationLabel ? `Remote \xB7 ${job.remoteLocationLabel}` : "Remote";
120
+ if (!dateStr) return null;
121
+ const isDateOnly = /^\d{4}-\d{2}(-\d{2})?$/.test(dateStr);
122
+ const parsed = isDateOnly ? /* @__PURE__ */ new Date(
123
+ dateStr.length === 7 ? `${dateStr}-01T00:00:00.000Z` : `${dateStr}T00:00:00.000Z`
124
+ ) : new Date(dateStr);
125
+ if (Number.isNaN(parsed.getTime())) return null;
126
+ const normalized = normalizeLocale(locale);
127
+ if (!normalized) return null;
128
+ try {
129
+ return getMonthYearFormat(normalized).format(parsed);
130
+ } catch {
131
+ return null;
124
132
  }
125
- return job.locationLabel ?? fieldLabel(locale, job.remoteOption) ?? "";
126
- }
127
-
128
- // src/format/job-card.ts
129
- function fullJobToCard(locale, job) {
130
- return {
131
- id: job.id,
132
- object: "job_card",
133
- slug: job.slug ?? "",
134
- title: job.title,
135
- publishedAt: job.publishedAt,
136
- employmentType: job.employmentType,
137
- remoteOption: job.remoteOption,
138
- remoteLocationLabel: job.remoteWorldwide ? "Worldwide" : null,
139
- salaryMin: job.salaryMin,
140
- salaryMax: job.salaryMax,
141
- salaryCurrency: job.salaryCurrency,
142
- salaryTimeframe: job.salaryTimeframe,
143
- isFeatured: job.isFeatured,
144
- locationLabel: locationLabel(locale, job),
145
- company: job.company?.slug ? {
146
- slug: job.company.slug,
147
- name: job.company.name ?? "",
148
- logoUrl: job.company.logoUrl
149
- } : null,
150
- categories: job.categories,
151
- skills: job.skills,
152
- links: job.links
153
- };
154
133
  }
155
134
 
156
135
  // src/format/salary-range.ts
157
- var CURRENCY_SYMBOLS = {
158
- USD: "$",
159
- EUR: "\u20AC",
160
- GBP: "\xA3",
161
- JPY: "\xA5",
162
- CNY: "\xA5",
163
- KRW: "\u20A9",
164
- INR: "\u20B9",
165
- RUB: "\u20BD",
166
- BRL: "R$",
167
- CAD: "C$",
168
- AUD: "A$",
169
- CHF: "CHF ",
170
- SEK: "kr ",
171
- NOK: "kr ",
172
- DKK: "kr ",
173
- PLN: "z\u0142 ",
174
- MXN: "MX$",
175
- SGD: "S$",
176
- HKD: "HK$",
177
- NZD: "NZ$",
178
- ZAR: "R ",
179
- THB: "\u0E3F",
180
- PHP: "\u20B1",
181
- IDR: "Rp ",
182
- MYR: "RM ",
183
- VND: "\u20AB",
184
- AED: "AED ",
185
- SAR: "SAR ",
186
- ILS: "\u20AA",
187
- TRY: "\u20BA",
188
- CZK: "K\u010D ",
189
- HUF: "Ft ",
190
- RON: "lei ",
191
- BGN: "\u043B\u0432 ",
192
- HRK: "kn ",
193
- ISK: "kr ",
194
- CLP: "CLP$",
195
- COP: "COP$",
196
- PEN: "S/",
197
- ARS: "ARS$",
198
- TWD: "NT$",
199
- NGN: "\u20A6",
200
- EGP: "E\xA3",
201
- PKR: "\u20A8",
202
- BDT: "\u09F3",
203
- UAH: "\u20B4",
204
- KZT: "\u20B8"
205
- };
206
- function getCurrencySymbol(currency) {
207
- if (!currency) {
208
- return "$";
209
- }
210
- const normalized = currency.trim().toUpperCase();
211
- return CURRENCY_SYMBOLS[normalized] ?? `${normalized} `;
212
- }
213
- var compactFormatCache = /* @__PURE__ */ new Map();
214
- function getCompactFormatter(language) {
215
- const cached = compactFormatCache.get(language);
216
- if (cached) return cached;
217
- const formatter = new Intl.NumberFormat(language, {
218
- notation: "compact",
219
- compactDisplay: "short",
220
- maximumFractionDigits: 1
221
- });
222
- compactFormatCache.set(language, formatter);
223
- return formatter;
224
- }
225
- function formatCompactNumber(value, language) {
226
- try {
227
- return getCompactFormatter(language ?? "en").format(value);
228
- } catch {
229
- if (value >= 1e6) {
230
- const millions = value / 1e6;
231
- return millions % 1 === 0 ? `${millions}M` : `${millions.toFixed(1)}M`;
232
- }
233
- if (value >= 1e3) {
234
- const thousands = value / 1e3;
235
- return thousands % 1 === 0 ? `${thousands}k` : `${thousands.toFixed(1)}k`;
136
+ var currencyCache = /* @__PURE__ */ new Map();
137
+ function formatAmount(value, language, currencyCode, currencyDisplay = "symbol", notation = "compact") {
138
+ const cacheKey = `${language}:${currencyCode}:${currencyDisplay}:${notation}`;
139
+ let formatter = currencyCache.get(cacheKey);
140
+ if (formatter === void 0) {
141
+ try {
142
+ const options = {
143
+ style: "currency",
144
+ currency: currencyCode,
145
+ currencyDisplay,
146
+ notation
147
+ };
148
+ if (notation === "compact") {
149
+ options.compactDisplay = "short";
150
+ options.maximumFractionDigits = 1;
151
+ options.minimumFractionDigits = 0;
152
+ }
153
+ formatter = new Intl.NumberFormat(language, options);
154
+ } catch {
155
+ formatter = null;
236
156
  }
237
- return value.toString();
157
+ currencyCache.set(cacheKey, formatter);
238
158
  }
159
+ return formatter ? formatter.format(value) : null;
239
160
  }
240
- var compactCurrencyCache = /* @__PURE__ */ new Map();
241
- function formatCompactCurrency(value, language, currency) {
242
- const currencyCode = (currency ?? "USD").trim().toUpperCase();
243
- const cacheKey = `${language}:${currencyCode}`;
244
- let formatter = compactCurrencyCache.get(cacheKey);
161
+ function formatRange(min, max, language, currencyCode, currencyDisplay = "symbol", notation = "compact") {
162
+ const cacheKey = `${language}:${currencyCode}:${currencyDisplay}:${notation}:range`;
163
+ let formatter = currencyCache.get(cacheKey);
245
164
  if (formatter === void 0) {
246
165
  try {
247
- formatter = new Intl.NumberFormat(language, {
166
+ const options = {
248
167
  style: "currency",
249
168
  currency: currencyCode,
250
- notation: "compact",
251
- compactDisplay: "short",
252
- maximumFractionDigits: 1
253
- });
169
+ currencyDisplay,
170
+ notation
171
+ };
172
+ if (notation === "compact") {
173
+ options.compactDisplay = "short";
174
+ options.maximumFractionDigits = 1;
175
+ options.minimumFractionDigits = 0;
176
+ }
177
+ formatter = new Intl.NumberFormat(language, options);
254
178
  } catch {
255
179
  formatter = null;
256
180
  }
257
- compactCurrencyCache.set(cacheKey, formatter);
181
+ currencyCache.set(cacheKey, formatter);
258
182
  }
259
- if (!formatter) {
260
- return `${getCurrencySymbol(currency)}${formatCompactNumber(value, language)}`;
183
+ if (!formatter) return null;
184
+ return formatter.formatRange(min, max);
185
+ }
186
+ var SALARY_TIMEFRAMES = /* @__PURE__ */ new Set([
187
+ "per_year",
188
+ "per_month",
189
+ "per_week",
190
+ "per_day",
191
+ "per_hour"
192
+ ]);
193
+ function notationForMagnitude(...values) {
194
+ let anyFinite = false;
195
+ for (const v of values) {
196
+ if (v == null || !Number.isFinite(v)) continue;
197
+ anyFinite = true;
198
+ if (Math.abs(v) >= 1e3) return "compact";
261
199
  }
262
- return formatter.format(value);
200
+ return anyFinite ? "standard" : "compact";
263
201
  }
264
- function appendTimeframe(value, timeframeLabel) {
265
- if (!value) {
202
+ function formatSalaryRange(language, min, max, timeframe, currency = null, currencyDisplay = "symbol", notation) {
203
+ const minOk = min == null || Number.isFinite(min);
204
+ const maxOk = max == null || Number.isFinite(max);
205
+ if (!minOk || !maxOk) {
266
206
  return null;
267
207
  }
268
- if (!timeframeLabel) {
269
- return value;
270
- }
271
- return `${value} ${timeframeLabel}`;
272
- }
273
- function formatSalaryRange(language, min, max, timeframe, currency = null, timeframeOverrides) {
274
208
  if (min == null && max == null) {
275
209
  return null;
276
210
  }
277
- const locale = language ?? "en";
278
- const lexicon = getSalaryLexicon(locale);
279
- const overrideMap = {
280
- per_year: timeframeOverrides?.yearlyLabel,
281
- per_month: timeframeOverrides?.monthlyLabel,
282
- per_week: timeframeOverrides?.weeklyLabel,
283
- per_day: timeframeOverrides?.dailyLabel,
284
- per_hour: timeframeOverrides?.hourlyLabel
285
- };
286
- const rawOverride = timeframe ? overrideMap[timeframe] : void 0;
287
- const enSourceWord = timeframe ? getSalaryLexicon("en").timeframe[timeframe] : void 0;
288
- const lexiconTimeframe = timeframe ? lexicon.timeframe[timeframe] ?? null : null;
289
- const timeframeLabel = rawOverride && rawOverride !== enSourceWord ? rawOverride : lexiconTimeframe;
290
- const formatAmount = locale === "en" ? (value) => `${getCurrencySymbol(currency)}${formatCompactNumber(value, locale)}` : (value) => formatCompactCurrency(value, locale, currency);
211
+ const currencyCode = currency?.trim().toUpperCase() || null;
212
+ if (!currencyCode) {
213
+ return null;
214
+ }
215
+ const locale = normalizeLocale(language);
216
+ if (!locale) {
217
+ return null;
218
+ }
219
+ const resolvedNotation = notation ?? notationForMagnitude(min, max);
220
+ const timeframeLabel = typeof timeframe === "string" && SALARY_TIMEFRAMES.has(timeframe) ? timeframe : null;
291
221
  if (min != null && max != null) {
292
- return appendTimeframe(
293
- `${formatAmount(min)} \u2013 ${formatAmount(max)}`,
294
- timeframeLabel
222
+ const low = min <= max ? min : max;
223
+ const high = min <= max ? max : min;
224
+ const text2 = low === high ? formatAmount(
225
+ low,
226
+ locale,
227
+ currencyCode,
228
+ currencyDisplay,
229
+ resolvedNotation
230
+ ) : formatRange(
231
+ low,
232
+ high,
233
+ locale,
234
+ currencyCode,
235
+ currencyDisplay,
236
+ resolvedNotation
295
237
  );
238
+ return text2 ? { text: text2, timeframe: timeframeLabel, bound: "range" } : null;
296
239
  }
297
240
  if (min != null) {
298
- return appendTimeframe(
299
- `${lexicon.rangePrefix.from} ${formatAmount(min)}`,
300
- timeframeLabel
241
+ const text2 = formatAmount(
242
+ min,
243
+ locale,
244
+ currencyCode,
245
+ currencyDisplay,
246
+ resolvedNotation
301
247
  );
248
+ return text2 ? { text: text2, timeframe: timeframeLabel, bound: "from" } : null;
302
249
  }
303
- return appendTimeframe(
304
- `${lexicon.rangePrefix.upTo} ${formatAmount(max)}`,
305
- timeframeLabel
250
+ const text = formatAmount(
251
+ max,
252
+ locale,
253
+ currencyCode,
254
+ currencyDisplay,
255
+ resolvedNotation
306
256
  );
307
- }
308
-
309
- // src/format/company-intro.ts
310
- var NAMED_ENTITIES = {
311
- amp: "&",
312
- lt: "<",
313
- gt: ">",
314
- quot: '"',
315
- apos: "'",
316
- nbsp: " "
317
- };
318
- function decodeEntities(input) {
319
- return input.replace(/&(#\d+|#x[0-9a-f]+|[a-z]+);/gi, (match, body) => {
320
- const token = String(body);
321
- if (token[0] === "#") {
322
- const code = token[1] === "x" || token[1] === "X" ? parseInt(token.slice(2), 16) : parseInt(token.slice(1), 10);
323
- if (!Number.isInteger(code) || code < 0 || code > 1114111) return match;
324
- return String.fromCodePoint(code);
325
- }
326
- return NAMED_ENTITIES[token.toLowerCase()] ?? match;
327
- });
328
- }
329
- function companyIntro(summary, description) {
330
- const trimmedSummary = summary?.trim();
331
- if (trimmedSummary) return trimmedSummary;
332
- if (!description) return null;
333
- const text = decodeEntities(
334
- description.replace(/<script[\s\S]*?<\/script>/gi, " ").replace(/<style[\s\S]*?<\/style>/gi, " ").replace(/<br\s*\/?>/gi, " ").replace(/<\/(p|div|li|h[1-6])>/gi, " ").replace(/<[^>]*>/g, "")
335
- ).replace(/\s+/g, " ").trim();
336
- if (!text) return null;
337
- const [first] = text.split(/(?<=[.!?])\s+/);
338
- return first ?? text;
257
+ return text ? { text, timeframe: timeframeLabel, bound: "upTo" } : null;
339
258
  }
340
259
 
341
260
  // src/format/countries.ts
@@ -605,8 +524,14 @@ function collator(language) {
605
524
  }
606
525
  }
607
526
  function countryOptions(language) {
608
- const names = regionNames(language);
609
- const compare = collator(language).compare;
527
+ const locale = normalizeLocale(language);
528
+ if (!locale) {
529
+ return COUNTRY_CODES.map((code) => ({ code, name: code })).sort(
530
+ (a, b) => a.code < b.code ? -1 : a.code > b.code ? 1 : 0
531
+ );
532
+ }
533
+ const names = regionNames(locale);
534
+ const compare = collator(locale).compare;
610
535
  return COUNTRY_CODES.map((code) => ({
611
536
  code,
612
537
  name: names?.of(code) ?? code
@@ -614,24 +539,27 @@ function countryOptions(language) {
614
539
  }
615
540
 
616
541
  // src/format/custom-fields.ts
542
+ function resolveMultiSelectLabels(def, raw) {
543
+ if (!Array.isArray(raw) || raw.length === 0) return null;
544
+ const labels = raw.map((key) => def.options?.find((o) => o.key === key)?.label).filter((label) => Boolean(label));
545
+ return labels.length > 0 ? labels : null;
546
+ }
617
547
  function renderTextValue(def, raw) {
618
548
  switch (def.type) {
619
549
  case "number":
620
- return typeof raw === "number" ? String(raw) : null;
550
+ return null;
551
+ case "multi_select":
552
+ return null;
621
553
  case "single_select": {
622
554
  if (typeof raw !== "string") return null;
623
555
  return def.options?.find((o) => o.key === raw)?.label ?? null;
624
556
  }
625
- case "multi_select": {
626
- if (!Array.isArray(raw) || raw.length === 0) return null;
627
- const labels = raw.map((key) => def.options?.find((o) => o.key === key)?.label).filter((label) => Boolean(label));
628
- return labels.length > 0 ? labels.join(", ") : null;
629
- }
630
557
  default:
631
558
  return typeof raw === "string" && raw.trim() !== "" ? raw : null;
632
559
  }
633
560
  }
634
- function resolveCustomFieldDisplay(definitions, values) {
561
+ function resolveCustomFieldDisplay(language, definitions, values) {
562
+ void language;
635
563
  const entries = [];
636
564
  for (const def of definitions ?? []) {
637
565
  const raw = values?.[def.key];
@@ -646,6 +574,29 @@ function resolveCustomFieldDisplay(definitions, values) {
646
574
  }
647
575
  continue;
648
576
  }
577
+ if (def.type === "number") {
578
+ if (typeof raw === "number" && Number.isFinite(raw)) {
579
+ entries.push({
580
+ key: def.key,
581
+ label: def.label,
582
+ kind: "number",
583
+ value: raw
584
+ });
585
+ }
586
+ continue;
587
+ }
588
+ if (def.type === "multi_select") {
589
+ const labels = resolveMultiSelectLabels(def, raw);
590
+ if (labels) {
591
+ entries.push({
592
+ key: def.key,
593
+ label: def.label,
594
+ kind: "multi_select",
595
+ values: labels
596
+ });
597
+ }
598
+ continue;
599
+ }
649
600
  const value = renderTextValue(def, raw);
650
601
  if (value !== null) {
651
602
  entries.push({ key: def.key, label: def.label, kind: "text", value });
@@ -655,22 +606,14 @@ function resolveCustomFieldDisplay(definitions, values) {
655
606
  }
656
607
  export {
657
608
  COUNTRY_CODES,
658
- PUBLIC_LABEL_GROUPS,
659
609
  buildJobBreadcrumbs,
660
- cardLocationLabel,
661
- companyIntro,
662
610
  countryOptions,
663
- fieldLabel,
664
611
  formatDate,
665
612
  formatMonthYear,
666
613
  formatPublishedRelativeDate,
667
614
  formatSalaryRange,
668
- formatRange as formatSalaryStatRange,
669
- formatUsd as formatSalaryStatUsd,
670
- fullJobToCard,
671
- getSalaryLexicon,
672
- locationLabel,
615
+ formatSalaryStat,
616
+ formatSalaryStatRange,
673
617
  normalizeWebsiteUrl,
674
- resolveCustomFieldDisplay,
675
- uiCopy
618
+ resolveCustomFieldDisplay
676
619
  };
package/dist/go.js CHANGED
@@ -27,8 +27,18 @@ __export(go_exports, {
27
27
  module.exports = __toCommonJS(go_exports);
28
28
 
29
29
  // src/paths/index.ts
30
+ function encodePathSegment(segment) {
31
+ try {
32
+ return encodeURIComponent(decodeURIComponent(segment)).replace(
33
+ /%3A/gi,
34
+ ":"
35
+ );
36
+ } catch {
37
+ return encodeURIComponent(segment).replace(/%3A/gi, ":");
38
+ }
39
+ }
30
40
  function jobDetailPath(companySlug, jobSlug) {
31
- return `/companies/${companySlug}/jobs/${jobSlug}`;
41
+ return `/companies/${encodePathSegment(companySlug)}/jobs/${encodePathSegment(jobSlug)}`;
32
42
  }
33
43
  var BOARD_PATHS = {
34
44
  home: "/",
package/dist/go.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  BOARD_PATHS,
3
3
  jobDetailPath
4
- } from "./chunk-L7ZOYWHW.mjs";
4
+ } from "./chunk-BGKNXG4H.mjs";
5
5
 
6
6
  // src/go/create-go-handler.ts
7
7
  var NOINDEX_ROBOTS = "noindex, nofollow";