@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.
Files changed (77) hide show
  1. package/CHANGELOG.md +421 -0
  2. package/README.md +6 -2
  3. package/dist/{_spec-nkWAraS0.d.mts → _spec-GVe6WAfT.d.mts} +68 -59
  4. package/dist/{_spec-nkWAraS0.d.ts → _spec-GVe6WAfT.d.ts} +68 -59
  5. package/dist/bin.mjs +7 -2
  6. package/dist/blog-BsmKizpd.d.ts +29 -0
  7. package/dist/blog-Cf64mI8A.d.mts +29 -0
  8. package/dist/{salaries-BahBpxWT.d.ts → board-DUhgiacm.d.ts} +17 -29
  9. package/dist/{salaries-ChSqDBAM.d.mts → board-GGWJ4coe.d.mts} +17 -29
  10. package/dist/breadcrumbs-DFKr0HzU.d.mts +372 -0
  11. package/dist/breadcrumbs-DiccJhk4.d.ts +372 -0
  12. package/dist/chunk-2ETP5UH4.mjs +271 -0
  13. package/dist/chunk-BGKNXG4H.mjs +146 -0
  14. package/dist/chunk-DLSO6XXY.mjs +33 -0
  15. package/dist/chunk-T2KC6XGV.mjs +1060 -0
  16. package/dist/chunk-YEAQ3JEI.mjs +853 -0
  17. package/dist/doctor.js +1 -1
  18. package/dist/doctor.mjs +1 -1
  19. package/dist/filters.d.mts +25 -20
  20. package/dist/filters.d.ts +25 -20
  21. package/dist/filters.js +3 -692
  22. package/dist/filters.mjs +3 -690
  23. package/dist/format.d.mts +57 -230
  24. package/dist/format.d.ts +57 -230
  25. package/dist/format.js +355 -927
  26. package/dist/format.mjs +213 -931
  27. package/dist/go.js +11 -1
  28. package/dist/go.mjs +4 -29
  29. package/dist/index.d.mts +23 -75
  30. package/dist/index.d.ts +23 -75
  31. package/dist/index.js +11 -36
  32. package/dist/index.mjs +32 -313
  33. package/dist/{jobs-lVvKCBCU.d.mts → jobs-BuLpV8iy.d.mts} +4 -4
  34. package/dist/{jobs-BiEvL9d7.d.ts → jobs-QrbXOYB9.d.ts} +4 -4
  35. package/dist/paths.d.mts +89 -1
  36. package/dist/paths.d.ts +89 -1
  37. package/dist/paths.js +62 -14
  38. package/dist/paths.mjs +29 -78
  39. package/dist/route-contract.js +23 -13
  40. package/dist/route-contract.mjs +30 -1079
  41. package/dist/{search-CCyFWPi3.d.mts → search-Cr4noJJZ.d.mts} +14 -3
  42. package/dist/{search-CyRvVMUp.d.ts → search-DdsniXQv.d.ts} +14 -3
  43. package/dist/seo.d.mts +33 -189
  44. package/dist/seo.d.ts +33 -189
  45. package/dist/seo.js +197 -718
  46. package/dist/seo.mjs +44 -1417
  47. package/dist/server.d.mts +5 -5
  48. package/dist/server.d.ts +5 -5
  49. package/dist/server.mjs +5 -31
  50. package/dist/sitemap.d.mts +5 -5
  51. package/dist/sitemap.d.ts +5 -5
  52. package/dist/sitemap.js +25 -14
  53. package/dist/sitemap.mjs +22 -97
  54. package/dist/skills.js +1 -1
  55. package/dist/skills.mjs +1 -1
  56. package/dist/suggest.d.mts +2 -2
  57. package/dist/suggest.d.ts +2 -2
  58. package/dist/suggest.js +17 -3
  59. package/dist/suggest.mjs +17 -3
  60. package/dist/well-known.js +23 -13
  61. package/dist/well-known.mjs +4 -421
  62. package/package.json +5 -4
  63. package/skills/cavuno-board-applications/SKILL.md +4 -1
  64. package/skills/cavuno-board-filters/SKILL.md +11 -12
  65. package/skills/cavuno-board-format/SKILL.md +68 -56
  66. package/skills/cavuno-board-i18n/SKILL.md +102 -21
  67. package/skills/cavuno-board-search-suggestions/SKILL.md +8 -3
  68. package/skills/cavuno-board-seo/BLOG.md +4 -1
  69. package/skills/cavuno-board-seo/JOB_AND_BREADCRUMBS.md +5 -2
  70. package/skills/cavuno-board-seo/LISTING.md +35 -12
  71. package/skills/cavuno-board-seo/SALARY.md +83 -18
  72. package/skills/cavuno-board-seo/SKILL.md +3 -2
  73. package/skills/manifest.json +3 -3
  74. package/dist/board-NE98PlwN.d.ts +0 -16
  75. package/dist/board-muu8BI8P.d.mts +0 -16
  76. package/dist/ui-copy-CCkz6CVF.d.mts +0 -230
  77. package/dist/ui-copy-CCkz6CVF.d.ts +0 -230
package/dist/server.d.mts CHANGED
@@ -1,9 +1,9 @@
1
1
  import { BoardSdk } from './index.mjs';
2
- import './_spec-nkWAraS0.mjs';
3
- import './search-CCyFWPi3.mjs';
4
- import './board-muu8BI8P.mjs';
5
- import './jobs-lVvKCBCU.mjs';
6
- import './salaries-ChSqDBAM.mjs';
2
+ import './_spec-GVe6WAfT.mjs';
3
+ import './search-Cr4noJJZ.mjs';
4
+ import './board-GGWJ4coe.mjs';
5
+ import './jobs-BuLpV8iy.mjs';
6
+ import './blog-Cf64mI8A.mjs';
7
7
 
8
8
  /**
9
9
  * Session cookie codec — pure (no framework imports, no node imports) so it
package/dist/server.d.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import { BoardSdk } from './index.js';
2
- import './_spec-nkWAraS0.js';
3
- import './search-CyRvVMUp.js';
4
- import './board-NE98PlwN.js';
5
- import './jobs-BiEvL9d7.js';
6
- import './salaries-BahBpxWT.js';
2
+ import './_spec-GVe6WAfT.js';
3
+ import './search-DdsniXQv.js';
4
+ import './board-DUhgiacm.js';
5
+ import './jobs-QrbXOYB9.js';
6
+ import './blog-BsmKizpd.js';
7
7
 
8
8
  /**
9
9
  * Session cookie codec — pure (no framework imports, no node imports) so it
package/dist/server.mjs CHANGED
@@ -1,12 +1,8 @@
1
- // src/scope.ts
2
- function scopeToken(board) {
3
- let hash = 5381;
4
- for (let i = 0; i < board.length; i++) {
5
- hash = (hash << 5) + hash + board.charCodeAt(i) >>> 0;
6
- }
7
- const sanitized = board.replace(/[^a-zA-Z0-9_-]/g, "_");
8
- return `${sanitized}-${hash.toString(36)}`;
9
- }
1
+ import {
2
+ isNoStore,
3
+ isUnauthorized,
4
+ scopeToken
5
+ } from "./chunk-2ETP5UH4.mjs";
10
6
 
11
7
  // src/server/cookie.ts
12
8
  var COOKIE_ATTRIBUTES = "Path=/; HttpOnly; Secure; SameSite=Lax";
@@ -154,28 +150,6 @@ function getCavunoAppCanonicalRedirectUrl(params) {
154
150
  return `https://${canonical}${path.startsWith("/") ? path : `/${path}`}`;
155
151
  }
156
152
 
157
- // src/errors.ts
158
- function isBoardApiError(e) {
159
- return e instanceof Error && e.name === "BoardApiError" && typeof e.status === "number" && typeof e.code === "string";
160
- }
161
- function isUnauthorized(e) {
162
- return isBoardApiError(e) && e.status === 401;
163
- }
164
-
165
- // src/storage.ts
166
- var NOSTORE_BRAND = /* @__PURE__ */ Symbol.for("@cavuno/board:nostore");
167
- function isNoStore(storage) {
168
- return storage[NOSTORE_BRAND] === true;
169
- }
170
- var NOSTORE = {
171
- [NOSTORE_BRAND]: true,
172
- getItem: () => null,
173
- setItem: () => {
174
- },
175
- removeItem: () => {
176
- }
177
- };
178
-
179
153
  // src/server/refresher.ts
180
154
  function createSessionRefresher(board) {
181
155
  if (typeof globalThis.document === "undefined" && !isNoStore(board.client.storage)) {
@@ -1,9 +1,9 @@
1
1
  import { BoardSdk } from './index.mjs';
2
- import './_spec-nkWAraS0.mjs';
3
- import './search-CCyFWPi3.mjs';
4
- import './board-muu8BI8P.mjs';
5
- import './jobs-lVvKCBCU.mjs';
6
- import './salaries-ChSqDBAM.mjs';
2
+ import './_spec-GVe6WAfT.mjs';
3
+ import './search-Cr4noJJZ.mjs';
4
+ import './board-GGWJ4coe.mjs';
5
+ import './jobs-BuLpV8iy.mjs';
6
+ import './blog-Cf64mI8A.mjs';
7
7
 
8
8
  /**
9
9
  * Sitemap primitives — the pure XML + bucket-filename logic behind a board
package/dist/sitemap.d.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import { BoardSdk } from './index.js';
2
- import './_spec-nkWAraS0.js';
3
- import './search-CyRvVMUp.js';
4
- import './board-NE98PlwN.js';
5
- import './jobs-BiEvL9d7.js';
6
- import './salaries-BahBpxWT.js';
2
+ import './_spec-GVe6WAfT.js';
3
+ import './search-DdsniXQv.js';
4
+ import './board-DUhgiacm.js';
5
+ import './jobs-QrbXOYB9.js';
6
+ import './blog-BsmKizpd.js';
7
7
 
8
8
  /**
9
9
  * Sitemap primitives — the pure XML + bucket-filename logic behind a board
package/dist/sitemap.js CHANGED
@@ -165,44 +165,54 @@ function paginate(listFn, query, options) {
165
165
  }
166
166
 
167
167
  // src/paths/index.ts
168
+ function encodePathSegment(segment) {
169
+ try {
170
+ return encodeURIComponent(decodeURIComponent(segment)).replace(
171
+ /%3A/gi,
172
+ ":"
173
+ );
174
+ } catch {
175
+ return encodeURIComponent(segment).replace(/%3A/gi, ":");
176
+ }
177
+ }
168
178
  function jobDetailPath(companySlug, jobSlug) {
169
- return `/companies/${companySlug}/jobs/${jobSlug}`;
179
+ return `/companies/${encodePathSegment(companySlug)}/jobs/${encodePathSegment(jobSlug)}`;
170
180
  }
171
181
  function jobsCategoryPath(categorySlug) {
172
- return `/jobs/${categorySlug}`;
182
+ return `/jobs/${encodePathSegment(categorySlug)}`;
173
183
  }
174
184
  function jobsSkillPath(skillSlug) {
175
- return `/jobs/skills/${skillSlug}`;
185
+ return `/jobs/skills/${encodePathSegment(skillSlug)}`;
176
186
  }
177
187
  function jobsLocationPath(placeSlug) {
178
- return `/jobs/locations/${placeSlug}`;
188
+ return `/jobs/locations/${encodePathSegment(placeSlug)}`;
179
189
  }
180
190
  function companyPath(companySlug) {
181
- return `/companies/${companySlug}`;
191
+ return `/companies/${encodePathSegment(companySlug)}`;
182
192
  }
183
193
  function companyMarketPath(marketSlug) {
184
- return `/companies/markets/${marketSlug}`;
194
+ return `/companies/markets/${encodePathSegment(marketSlug)}`;
185
195
  }
186
196
  function companySalaryPath(companySlug) {
187
- return `/companies/${companySlug}/salaries`;
197
+ return `/companies/${encodePathSegment(companySlug)}/salaries`;
188
198
  }
189
199
  function salaryTitlePath(titleSlug) {
190
- return `/salaries/titles/${titleSlug}`;
200
+ return `/salaries/titles/${encodePathSegment(titleSlug)}`;
191
201
  }
192
202
  function salarySkillPath(skillSlug) {
193
- return `/salaries/skills/${skillSlug}`;
203
+ return `/salaries/skills/${encodePathSegment(skillSlug)}`;
194
204
  }
195
205
  function salaryLocationPath(placeSlug) {
196
- return `/salaries/locations/${placeSlug}`;
206
+ return `/salaries/locations/${encodePathSegment(placeSlug)}`;
197
207
  }
198
208
  function blogPostPath(postSlug) {
199
- return `/blog/${postSlug}`;
209
+ return `/blog/${encodePathSegment(postSlug)}`;
200
210
  }
201
211
  function blogTagPath(tagSlug) {
202
- return `/blog/tag/${tagSlug}`;
212
+ return `/blog/tag/${encodePathSegment(tagSlug)}`;
203
213
  }
204
214
  function blogAuthorPath(authorSlug) {
205
- return `/blog/author/${authorSlug}`;
215
+ return `/blog/author/${encodePathSegment(authorSlug)}`;
206
216
  }
207
217
  var BOARD_PATHS = {
208
218
  home: "/",
@@ -320,7 +330,8 @@ async function marketing(board, origin) {
320
330
  `${origin}${BOARD_PATHS.cookiePolicy}`
321
331
  ];
322
332
  if (features.impressum) urls.push(`${origin}${BOARD_PATHS.impressum}`);
323
- if (features.talentDirectory) urls.push(`${origin}${BOARD_PATHS.talent}`);
333
+ if (features.talentDirectory === "public")
334
+ urls.push(`${origin}${BOARD_PATHS.talent}`);
324
335
  if (features.employers) urls.push(`${origin}${BOARD_PATHS.employers}`);
325
336
  return urls;
326
337
  }
package/dist/sitemap.mjs CHANGED
@@ -1,3 +1,23 @@
1
+ import {
2
+ paginate
3
+ } from "./chunk-DLSO6XXY.mjs";
4
+ import {
5
+ BOARD_PATHS,
6
+ blogAuthorPath,
7
+ blogPostPath,
8
+ blogTagPath,
9
+ companyMarketPath,
10
+ companyPath,
11
+ companySalaryPath,
12
+ jobDetailPath,
13
+ jobsCategoryPath,
14
+ jobsLocationPath,
15
+ jobsSkillPath,
16
+ salaryLocationPath,
17
+ salarySkillPath,
18
+ salaryTitlePath
19
+ } from "./chunk-BGKNXG4H.mjs";
20
+
1
21
  // src/sitemap/xml.ts
2
22
  var SITEMAP_BUCKETS = [
3
23
  "marketing",
@@ -98,102 +118,6 @@ function renderSitemapIndex(entries) {
98
118
  return xml;
99
119
  }
100
120
 
101
- // src/pagination.ts
102
- function paginate(listFn, query, options) {
103
- async function* pages() {
104
- let current = query;
105
- for (; ; ) {
106
- const page = await listFn(current, options);
107
- yield page;
108
- if (!page.hasMore || page.nextCursor === null) return;
109
- const { offset: _offset, ...rest } = current ?? {};
110
- current = { ...rest, cursor: page.nextCursor };
111
- }
112
- }
113
- return {
114
- pages,
115
- async *[Symbol.asyncIterator]() {
116
- for await (const page of pages()) {
117
- yield* page.data;
118
- }
119
- },
120
- async toArray({ limit }) {
121
- const items = [];
122
- for await (const page of pages()) {
123
- items.push(...page.data);
124
- if (items.length >= limit) break;
125
- }
126
- return items.slice(0, limit);
127
- }
128
- };
129
- }
130
-
131
- // src/paths/index.ts
132
- function jobDetailPath(companySlug, jobSlug) {
133
- return `/companies/${companySlug}/jobs/${jobSlug}`;
134
- }
135
- function jobsCategoryPath(categorySlug) {
136
- return `/jobs/${categorySlug}`;
137
- }
138
- function jobsSkillPath(skillSlug) {
139
- return `/jobs/skills/${skillSlug}`;
140
- }
141
- function jobsLocationPath(placeSlug) {
142
- return `/jobs/locations/${placeSlug}`;
143
- }
144
- function companyPath(companySlug) {
145
- return `/companies/${companySlug}`;
146
- }
147
- function companyMarketPath(marketSlug) {
148
- return `/companies/markets/${marketSlug}`;
149
- }
150
- function companySalaryPath(companySlug) {
151
- return `/companies/${companySlug}/salaries`;
152
- }
153
- function salaryTitlePath(titleSlug) {
154
- return `/salaries/titles/${titleSlug}`;
155
- }
156
- function salarySkillPath(skillSlug) {
157
- return `/salaries/skills/${skillSlug}`;
158
- }
159
- function salaryLocationPath(placeSlug) {
160
- return `/salaries/locations/${placeSlug}`;
161
- }
162
- function blogPostPath(postSlug) {
163
- return `/blog/${postSlug}`;
164
- }
165
- function blogTagPath(tagSlug) {
166
- return `/blog/tag/${tagSlug}`;
167
- }
168
- function blogAuthorPath(authorSlug) {
169
- return `/blog/author/${authorSlug}`;
170
- }
171
- var BOARD_PATHS = {
172
- home: "/",
173
- jobs: "/jobs",
174
- companies: "/companies",
175
- salaries: "/salaries",
176
- salaryCompanies: "/salaries/companies",
177
- salaryTitles: "/salaries/titles",
178
- salarySkills: "/salaries/skills",
179
- salaryLocations: "/salaries/locations",
180
- blog: "/blog",
181
- about: "/about",
182
- privacyPolicy: "/privacy-policy",
183
- termsOfService: "/terms-of-service",
184
- cookiePolicy: "/cookie-policy",
185
- impressum: "/impressum",
186
- talent: "/talent",
187
- employers: "/employers",
188
- /**
189
- * Alert-email surfaces. Promoted out of raw literals in
190
- * the job-alerts composer so /go indirection and email composition share
191
- * one definition.
192
- */
193
- alertsManage: "/alerts/manage",
194
- alertsConfirm: "/alerts/confirm"
195
- };
196
-
197
121
  // src/sitemap/walker.ts
198
122
  var MIN_JOBS_PER_INDEXED_PAGE = 5;
199
123
  var MAX_PAGES = 200;
@@ -284,7 +208,8 @@ async function marketing(board, origin) {
284
208
  `${origin}${BOARD_PATHS.cookiePolicy}`
285
209
  ];
286
210
  if (features.impressum) urls.push(`${origin}${BOARD_PATHS.impressum}`);
287
- if (features.talentDirectory) urls.push(`${origin}${BOARD_PATHS.talent}`);
211
+ if (features.talentDirectory === "public")
212
+ urls.push(`${origin}${BOARD_PATHS.talent}`);
288
213
  if (features.employers) urls.push(`${origin}${BOARD_PATHS.employers}`);
289
214
  return urls;
290
215
  }
package/dist/skills.js CHANGED
@@ -57,7 +57,7 @@ function loadSkillCorpus(baseDir) {
57
57
  ).map((entry) => ({
58
58
  path: entry.name,
59
59
  content: (0, import_node_fs.readFileSync)((0, import_node_path.resolve)(skillDir, entry.name), "utf8")
60
- })).sort((a, b) => a.path.localeCompare(b.path));
60
+ })).sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
61
61
  return {
62
62
  ...skill,
63
63
  content: (0, import_node_fs.readFileSync)(skillPath, "utf8"),
package/dist/skills.mjs CHANGED
@@ -24,7 +24,7 @@ function loadSkillCorpus(baseDir) {
24
24
  ).map((entry) => ({
25
25
  path: entry.name,
26
26
  content: readFileSync(resolve(skillDir, entry.name), "utf8")
27
- })).sort((a, b) => a.path.localeCompare(b.path));
27
+ })).sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
28
28
  return {
29
29
  ...skill,
30
30
  content: readFileSync(skillPath, "utf8"),
@@ -1,5 +1,5 @@
1
- import { g as SuggestionItem, b as SearchSuggestQuery, F as FetchOptions, f as SuggestResult } from './search-CCyFWPi3.mjs';
2
- import './_spec-nkWAraS0.mjs';
1
+ import { h as SuggestionItem, b as SearchSuggestQuery, F as FetchOptions, g as SuggestResult } from './search-Cr4noJJZ.mjs';
2
+ import './_spec-GVe6WAfT.mjs';
3
3
 
4
4
  /**
5
5
  * `@cavuno/board/suggest` — framework-agnostic headless search-suggest
package/dist/suggest.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { g as SuggestionItem, b as SearchSuggestQuery, F as FetchOptions, f as SuggestResult } from './search-CyRvVMUp.js';
2
- import './_spec-nkWAraS0.js';
1
+ import { h as SuggestionItem, b as SearchSuggestQuery, F as FetchOptions, g as SuggestResult } from './search-DdsniXQv.js';
2
+ import './_spec-GVe6WAfT.js';
3
3
 
4
4
  /**
5
5
  * `@cavuno/board/suggest` — framework-agnostic headless search-suggest
package/dist/suggest.js CHANGED
@@ -28,10 +28,24 @@ var DEFAULT_DEBOUNCE_MS = 250;
28
28
  function isAbortError(error) {
29
29
  return typeof DOMException !== "undefined" && error instanceof DOMException && error.name === "AbortError" || error instanceof Error && error.name === "AbortError";
30
30
  }
31
+ function queryCharCount(query) {
32
+ try {
33
+ let n = 0;
34
+ for (const _ of new Intl.Segmenter(void 0, {
35
+ granularity: "grapheme"
36
+ }).segment(query)) {
37
+ n += 1;
38
+ }
39
+ return n;
40
+ } catch {
41
+ return [...query].length;
42
+ }
43
+ }
31
44
  function applyExclusions(items, excluded) {
32
45
  if (excluded.size === 0) return items.slice();
33
46
  return items.filter(
34
- (item) => !(item.type === "company" && excluded.has(item.slug.toLowerCase()))
47
+ (item) => !(item.type === "company" && // NFC + toLowerCase — same keying as setExcludedCompanySlugs.
48
+ excluded.has(item.slug.toLowerCase().normalize("NFC")))
35
49
  );
36
50
  }
37
51
  function createSuggestController(board, options) {
@@ -104,7 +118,7 @@ function createSuggestController(board, options) {
104
118
  if (disposed) return;
105
119
  const trimmed = query.trim();
106
120
  cancelTimer();
107
- if (trimmed.length < minChars) {
121
+ if (queryCharCount(trimmed) < minChars) {
108
122
  sequence += 1;
109
123
  abortInFlight();
110
124
  rawItems = [];
@@ -133,7 +147,7 @@ function createSuggestController(board, options) {
133
147
  setExcludedCompanySlugs(slugs) {
134
148
  if (disposed) return;
135
149
  excluded = new Set(
136
- slugs.map((s) => s.trim().toLowerCase()).filter(Boolean)
150
+ slugs.map((s) => s.trim().toLowerCase().normalize("NFC")).filter(Boolean)
137
151
  );
138
152
  setState({
139
153
  query: state.query,
package/dist/suggest.mjs CHANGED
@@ -4,10 +4,24 @@ var DEFAULT_DEBOUNCE_MS = 250;
4
4
  function isAbortError(error) {
5
5
  return typeof DOMException !== "undefined" && error instanceof DOMException && error.name === "AbortError" || error instanceof Error && error.name === "AbortError";
6
6
  }
7
+ function queryCharCount(query) {
8
+ try {
9
+ let n = 0;
10
+ for (const _ of new Intl.Segmenter(void 0, {
11
+ granularity: "grapheme"
12
+ }).segment(query)) {
13
+ n += 1;
14
+ }
15
+ return n;
16
+ } catch {
17
+ return [...query].length;
18
+ }
19
+ }
7
20
  function applyExclusions(items, excluded) {
8
21
  if (excluded.size === 0) return items.slice();
9
22
  return items.filter(
10
- (item) => !(item.type === "company" && excluded.has(item.slug.toLowerCase()))
23
+ (item) => !(item.type === "company" && // NFC + toLowerCase — same keying as setExcludedCompanySlugs.
24
+ excluded.has(item.slug.toLowerCase().normalize("NFC")))
11
25
  );
12
26
  }
13
27
  function createSuggestController(board, options) {
@@ -80,7 +94,7 @@ function createSuggestController(board, options) {
80
94
  if (disposed) return;
81
95
  const trimmed = query.trim();
82
96
  cancelTimer();
83
- if (trimmed.length < minChars) {
97
+ if (queryCharCount(trimmed) < minChars) {
84
98
  sequence += 1;
85
99
  abortInFlight();
86
100
  rawItems = [];
@@ -109,7 +123,7 @@ function createSuggestController(board, options) {
109
123
  setExcludedCompanySlugs(slugs) {
110
124
  if (disposed) return;
111
125
  excluded = new Set(
112
- slugs.map((s) => s.trim().toLowerCase()).filter(Boolean)
126
+ slugs.map((s) => s.trim().toLowerCase().normalize("NFC")).filter(Boolean)
113
127
  );
114
128
  setState({
115
129
  query: state.query,
@@ -32,44 +32,54 @@ __export(well_known_exports, {
32
32
  module.exports = __toCommonJS(well_known_exports);
33
33
 
34
34
  // src/paths/index.ts
35
+ function encodePathSegment(segment) {
36
+ try {
37
+ return encodeURIComponent(decodeURIComponent(segment)).replace(
38
+ /%3A/gi,
39
+ ":"
40
+ );
41
+ } catch {
42
+ return encodeURIComponent(segment).replace(/%3A/gi, ":");
43
+ }
44
+ }
35
45
  function jobDetailPath(companySlug, jobSlug) {
36
- return `/companies/${companySlug}/jobs/${jobSlug}`;
46
+ return `/companies/${encodePathSegment(companySlug)}/jobs/${encodePathSegment(jobSlug)}`;
37
47
  }
38
48
  function jobsCategoryPath(categorySlug) {
39
- return `/jobs/${categorySlug}`;
49
+ return `/jobs/${encodePathSegment(categorySlug)}`;
40
50
  }
41
51
  function jobsSkillPath(skillSlug) {
42
- return `/jobs/skills/${skillSlug}`;
52
+ return `/jobs/skills/${encodePathSegment(skillSlug)}`;
43
53
  }
44
54
  function jobsLocationPath(placeSlug) {
45
- return `/jobs/locations/${placeSlug}`;
55
+ return `/jobs/locations/${encodePathSegment(placeSlug)}`;
46
56
  }
47
57
  function companyPath(companySlug) {
48
- return `/companies/${companySlug}`;
58
+ return `/companies/${encodePathSegment(companySlug)}`;
49
59
  }
50
60
  function companyMarketPath(marketSlug) {
51
- return `/companies/markets/${marketSlug}`;
61
+ return `/companies/markets/${encodePathSegment(marketSlug)}`;
52
62
  }
53
63
  function companySalaryPath(companySlug) {
54
- return `/companies/${companySlug}/salaries`;
64
+ return `/companies/${encodePathSegment(companySlug)}/salaries`;
55
65
  }
56
66
  function salaryTitlePath(titleSlug) {
57
- return `/salaries/titles/${titleSlug}`;
67
+ return `/salaries/titles/${encodePathSegment(titleSlug)}`;
58
68
  }
59
69
  function salarySkillPath(skillSlug) {
60
- return `/salaries/skills/${skillSlug}`;
70
+ return `/salaries/skills/${encodePathSegment(skillSlug)}`;
61
71
  }
62
72
  function salaryLocationPath(placeSlug) {
63
- return `/salaries/locations/${placeSlug}`;
73
+ return `/salaries/locations/${encodePathSegment(placeSlug)}`;
64
74
  }
65
75
  function blogPostPath(postSlug) {
66
- return `/blog/${postSlug}`;
76
+ return `/blog/${encodePathSegment(postSlug)}`;
67
77
  }
68
78
  function blogTagPath(tagSlug) {
69
- return `/blog/tag/${tagSlug}`;
79
+ return `/blog/tag/${encodePathSegment(tagSlug)}`;
70
80
  }
71
81
  function blogAuthorPath(authorSlug) {
72
- return `/blog/author/${authorSlug}`;
82
+ return `/blog/author/${encodePathSegment(authorSlug)}`;
73
83
  }
74
84
  var BOARD_PATHS = {
75
85
  home: "/",