@cavuno/board 3.2.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 +406 -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-iV9XEIGf.d.ts → blog-BsmKizpd.d.ts} +1 -1
- package/dist/{blog-DGBHmExV.d.mts → blog-Cf64mI8A.d.mts} +1 -1
- package/dist/{board-BJbXd_9_.d.mts → board-DUhgiacm.d.ts} +7 -6
- package/dist/{board-JhdUWIoq.d.ts → board-GGWJ4coe.d.mts} +7 -6
- package/dist/breadcrumbs-DFKr0HzU.d.mts +372 -0
- package/dist/breadcrumbs-DiccJhk4.d.ts +372 -0
- package/dist/chunk-BGKNXG4H.mjs +146 -0
- package/dist/{chunk-NXY3OWGF.mjs → chunk-T2KC6XGV.mjs} +1 -1
- package/dist/{chunk-STE77MAT.mjs → chunk-YEAQ3JEI.mjs} +143 -134
- 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 -29
- package/dist/format.d.mts +57 -231
- package/dist/format.d.ts +57 -231
- package/dist/format.js +327 -968
- package/dist/format.mjs +207 -264
- package/dist/go.js +11 -1
- package/dist/go.mjs +1 -1
- package/dist/index.d.mts +23 -76
- package/dist/index.d.ts +23 -76
- package/dist/index.js +11 -36
- package/dist/index.mjs +11 -36
- package/dist/{jobs-BiEvL9d7.d.ts → jobs-BuLpV8iy.d.mts} +4 -4
- package/dist/{jobs-lVvKCBCU.d.mts → 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 +11 -3
- package/dist/route-contract.js +23 -13
- package/dist/route-contract.mjs +2 -2
- package/dist/{search-CyRvVMUp.d.ts → search-Cr4noJJZ.d.mts} +14 -3
- package/dist/{search-CCyFWPi3.d.mts → search-DdsniXQv.d.ts} +14 -3
- package/dist/seo.d.mts +30 -29
- package/dist/seo.d.ts +30 -29
- package/dist/seo.js +197 -718
- package/dist/seo.mjs +26 -24
- package/dist/server.d.mts +5 -5
- package/dist/server.d.ts +5 -5
- package/dist/sitemap.d.mts +5 -5
- package/dist/sitemap.d.ts +5 -5
- package/dist/sitemap.js +25 -14
- package/dist/sitemap.mjs +3 -2
- 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 +2 -2
- package/package.json +2 -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/breadcrumbs-1hMjcsqA.d.mts +0 -171
- package/dist/breadcrumbs-DexU07TT.d.ts +0 -171
- package/dist/chunk-IJCJ26SQ.mjs +0 -596
- package/dist/chunk-L7ZOYWHW.mjs +0 -98
- package/dist/chunk-LUGJWOT7.mjs +0 -96
- package/dist/ui-copy-CCkz6CVF.d.mts +0 -230
- package/dist/ui-copy-CCkz6CVF.d.ts +0 -230
package/dist/paths.d.mts
CHANGED
|
@@ -9,11 +9,27 @@
|
|
|
9
9
|
* zero-dependency — safe to import in server runtimes, on the edge,
|
|
10
10
|
* and in the browser.
|
|
11
11
|
*
|
|
12
|
+
* Dynamic path segments are percent-encoded (URI, not bare IRI) so
|
|
13
|
+
* non-Latin board-language slugs (`SuggestionPathInput.canonicalSlug`)
|
|
14
|
+
* are legal in sitemap `<loc>`, canonical/`og:url`, and email links.
|
|
15
|
+
* Separators (`/`) are never encoded; already-encoded input is not
|
|
16
|
+
* double-encoded.
|
|
17
|
+
*
|
|
12
18
|
* These paths mirror the hosted board's indexed URLs exactly (migration
|
|
13
19
|
* parity). Do NOT change a pattern here without updating the hosted board
|
|
14
20
|
* and the sitemap golden tests in lockstep — the structure is a locked
|
|
15
21
|
* cross-surface contract.
|
|
16
22
|
*/
|
|
23
|
+
/**
|
|
24
|
+
* Percent-encode one path segment for use in a URL path.
|
|
25
|
+
* Round-trips already-encoded input so `%E6…` is not double-encoded.
|
|
26
|
+
* Does not touch path separators — callers encode segments only.
|
|
27
|
+
*
|
|
28
|
+
* `:` is left unencoded so route-contract / well-known templates built via
|
|
29
|
+
* `jobDetailPath(':companySlug', ':jobSlug')` stay RFC-6570-style. Real
|
|
30
|
+
* board slugs are alphanumeric-plus-hyphen; non-ASCII is still encoded.
|
|
31
|
+
*/
|
|
32
|
+
declare function encodePathSegment(segment: string): string;
|
|
17
33
|
/** Job detail — the canonical, indexed job URL. Requires BOTH slugs. */
|
|
18
34
|
declare function jobDetailPath(companySlug: string, jobSlug: string): string;
|
|
19
35
|
/** Category (keyword) job listing. */
|
|
@@ -22,10 +38,81 @@ declare function jobsCategoryPath(categorySlug: string): string;
|
|
|
22
38
|
declare function jobsSkillPath(skillSlug: string): string;
|
|
23
39
|
/** Location job listing. */
|
|
24
40
|
declare function jobsLocationPath(placeSlug: string): string;
|
|
41
|
+
/**
|
|
42
|
+
* Location + category combination job listing
|
|
43
|
+
* (`/jobs/locations/<place>/<category>`).
|
|
44
|
+
*/
|
|
45
|
+
declare function jobsLocationCategoryPath(placeSlug: string, categorySlug: string): string;
|
|
46
|
+
/**
|
|
47
|
+
* Location + skill combination job listing
|
|
48
|
+
* (`/jobs/locations/<place>/skills/<skill>`).
|
|
49
|
+
*/
|
|
50
|
+
declare function jobsLocationSkillPath(placeSlug: string, skillSlug: string): string;
|
|
25
51
|
/** Company profile. */
|
|
26
52
|
declare function companyPath(companySlug: string): string;
|
|
27
53
|
/** Company market (sector) listing. */
|
|
28
54
|
declare function companyMarketPath(marketSlug: string): string;
|
|
55
|
+
/**
|
|
56
|
+
* Suggestion kinds `suggestionPath` understands. Mirrors the wire
|
|
57
|
+
* `SuggestionItem` discriminators (and `termType` for terms) without
|
|
58
|
+
* importing generated OpenAPI types into the paths entry.
|
|
59
|
+
*/
|
|
60
|
+
type SuggestionPathInput = {
|
|
61
|
+
type: 'company';
|
|
62
|
+
slug: string;
|
|
63
|
+
} | {
|
|
64
|
+
type: 'market';
|
|
65
|
+
slug: string;
|
|
66
|
+
} | {
|
|
67
|
+
type: 'post';
|
|
68
|
+
slug: string;
|
|
69
|
+
} | {
|
|
70
|
+
type: 'tag';
|
|
71
|
+
slug: string;
|
|
72
|
+
} | {
|
|
73
|
+
type: 'term';
|
|
74
|
+
termType: 'category' | 'skill';
|
|
75
|
+
/** Board-language URL slug — use `canonicalSlug` from the wire item. */
|
|
76
|
+
canonicalSlug: string;
|
|
77
|
+
};
|
|
78
|
+
/** Search scopes that own different destinations for the same suggestion. */
|
|
79
|
+
type SuggestionPathScope = 'jobs' | 'companies' | 'blog';
|
|
80
|
+
type SuggestionPathOptions = {
|
|
81
|
+
scope: SuggestionPathScope;
|
|
82
|
+
/**
|
|
83
|
+
* Active place slug. When set on the `jobs` scope with a category or
|
|
84
|
+
* skill term, routes into the location combination pages.
|
|
85
|
+
*/
|
|
86
|
+
location?: string;
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* Resolve a suggestion to a board-relative path for the given search scope
|
|
90
|
+
* The API never returns an `href` because the same
|
|
91
|
+
* `CompanySuggestion` means "navigate to the company page" under companies
|
|
92
|
+
* scope and "apply a `companySlug` filter" under jobs scope — only the app
|
|
93
|
+
* knows which.
|
|
94
|
+
*
|
|
95
|
+
* Returns `null` when the selection is not a navigation (jobs-scope company
|
|
96
|
+
* → filter, or a kind that has no path under the active scope). Compose from
|
|
97
|
+
* the existing `*Path` helpers; never string-build at the call site.
|
|
98
|
+
*
|
|
99
|
+
* | scope | suggestion | result |
|
|
100
|
+
* | --- | --- | --- |
|
|
101
|
+
* | jobs | skill | `/jobs/skills/<slug>` |
|
|
102
|
+
* | jobs | category | `/jobs/<slug>` |
|
|
103
|
+
* | jobs | skill + location | `/jobs/locations/<loc>/skills/<slug>` |
|
|
104
|
+
* | jobs | category + location | `/jobs/locations/<loc>/<slug>` |
|
|
105
|
+
* | jobs | company | `null` (apply `companySlug` filter) |
|
|
106
|
+
* | jobs | market | `null` |
|
|
107
|
+
* | jobs | post / tag | `null` |
|
|
108
|
+
* | companies | company | `/companies/<slug>` |
|
|
109
|
+
* | companies | market | `/companies/markets/<slug>` |
|
|
110
|
+
* | companies | term / post / tag | `null` |
|
|
111
|
+
* | blog | post | `/blog/<slug>` |
|
|
112
|
+
* | blog | tag | `/blog/tag/<slug>` |
|
|
113
|
+
* | blog | other | `null` |
|
|
114
|
+
*/
|
|
115
|
+
declare function suggestionPath(suggestion: SuggestionPathInput, options: SuggestionPathOptions): string | null;
|
|
29
116
|
/** A company's salary overview. */
|
|
30
117
|
declare function companySalaryPath(companySlug: string): string;
|
|
31
118
|
/** Salary page for a job title. */
|
|
@@ -90,7 +177,8 @@ declare function goAlertsConfirmPath(): string;
|
|
|
90
177
|
/**
|
|
91
178
|
* Prefix a board-relative path with the board origin. A trailing slash on
|
|
92
179
|
* the origin is tolerated (stripped) so callers need not normalise first.
|
|
180
|
+
* Paths from `*Path` helpers already percent-encode dynamic segments.
|
|
93
181
|
*/
|
|
94
182
|
declare function boardUrl(origin: string, path: string): string;
|
|
95
183
|
|
|
96
|
-
export { BOARD_PATHS, blogAuthorPath, blogPostPath, blogTagPath, boardUrl, companyMarketPath, companyPath, companySalaryPath, goAlertsConfirmPath, goAlertsManagePath, goJobPath, jobDetailPath, jobsCategoryPath, jobsLocationPath, jobsSkillPath, salaryLocationPath, salarySkillPath, salaryTitlePath };
|
|
184
|
+
export { BOARD_PATHS, type SuggestionPathInput, type SuggestionPathOptions, type SuggestionPathScope, blogAuthorPath, blogPostPath, blogTagPath, boardUrl, companyMarketPath, companyPath, companySalaryPath, encodePathSegment, goAlertsConfirmPath, goAlertsManagePath, goJobPath, jobDetailPath, jobsCategoryPath, jobsLocationCategoryPath, jobsLocationPath, jobsLocationSkillPath, jobsSkillPath, salaryLocationPath, salarySkillPath, salaryTitlePath, suggestionPath };
|
package/dist/paths.d.ts
CHANGED
|
@@ -9,11 +9,27 @@
|
|
|
9
9
|
* zero-dependency — safe to import in server runtimes, on the edge,
|
|
10
10
|
* and in the browser.
|
|
11
11
|
*
|
|
12
|
+
* Dynamic path segments are percent-encoded (URI, not bare IRI) so
|
|
13
|
+
* non-Latin board-language slugs (`SuggestionPathInput.canonicalSlug`)
|
|
14
|
+
* are legal in sitemap `<loc>`, canonical/`og:url`, and email links.
|
|
15
|
+
* Separators (`/`) are never encoded; already-encoded input is not
|
|
16
|
+
* double-encoded.
|
|
17
|
+
*
|
|
12
18
|
* These paths mirror the hosted board's indexed URLs exactly (migration
|
|
13
19
|
* parity). Do NOT change a pattern here without updating the hosted board
|
|
14
20
|
* and the sitemap golden tests in lockstep — the structure is a locked
|
|
15
21
|
* cross-surface contract.
|
|
16
22
|
*/
|
|
23
|
+
/**
|
|
24
|
+
* Percent-encode one path segment for use in a URL path.
|
|
25
|
+
* Round-trips already-encoded input so `%E6…` is not double-encoded.
|
|
26
|
+
* Does not touch path separators — callers encode segments only.
|
|
27
|
+
*
|
|
28
|
+
* `:` is left unencoded so route-contract / well-known templates built via
|
|
29
|
+
* `jobDetailPath(':companySlug', ':jobSlug')` stay RFC-6570-style. Real
|
|
30
|
+
* board slugs are alphanumeric-plus-hyphen; non-ASCII is still encoded.
|
|
31
|
+
*/
|
|
32
|
+
declare function encodePathSegment(segment: string): string;
|
|
17
33
|
/** Job detail — the canonical, indexed job URL. Requires BOTH slugs. */
|
|
18
34
|
declare function jobDetailPath(companySlug: string, jobSlug: string): string;
|
|
19
35
|
/** Category (keyword) job listing. */
|
|
@@ -22,10 +38,81 @@ declare function jobsCategoryPath(categorySlug: string): string;
|
|
|
22
38
|
declare function jobsSkillPath(skillSlug: string): string;
|
|
23
39
|
/** Location job listing. */
|
|
24
40
|
declare function jobsLocationPath(placeSlug: string): string;
|
|
41
|
+
/**
|
|
42
|
+
* Location + category combination job listing
|
|
43
|
+
* (`/jobs/locations/<place>/<category>`).
|
|
44
|
+
*/
|
|
45
|
+
declare function jobsLocationCategoryPath(placeSlug: string, categorySlug: string): string;
|
|
46
|
+
/**
|
|
47
|
+
* Location + skill combination job listing
|
|
48
|
+
* (`/jobs/locations/<place>/skills/<skill>`).
|
|
49
|
+
*/
|
|
50
|
+
declare function jobsLocationSkillPath(placeSlug: string, skillSlug: string): string;
|
|
25
51
|
/** Company profile. */
|
|
26
52
|
declare function companyPath(companySlug: string): string;
|
|
27
53
|
/** Company market (sector) listing. */
|
|
28
54
|
declare function companyMarketPath(marketSlug: string): string;
|
|
55
|
+
/**
|
|
56
|
+
* Suggestion kinds `suggestionPath` understands. Mirrors the wire
|
|
57
|
+
* `SuggestionItem` discriminators (and `termType` for terms) without
|
|
58
|
+
* importing generated OpenAPI types into the paths entry.
|
|
59
|
+
*/
|
|
60
|
+
type SuggestionPathInput = {
|
|
61
|
+
type: 'company';
|
|
62
|
+
slug: string;
|
|
63
|
+
} | {
|
|
64
|
+
type: 'market';
|
|
65
|
+
slug: string;
|
|
66
|
+
} | {
|
|
67
|
+
type: 'post';
|
|
68
|
+
slug: string;
|
|
69
|
+
} | {
|
|
70
|
+
type: 'tag';
|
|
71
|
+
slug: string;
|
|
72
|
+
} | {
|
|
73
|
+
type: 'term';
|
|
74
|
+
termType: 'category' | 'skill';
|
|
75
|
+
/** Board-language URL slug — use `canonicalSlug` from the wire item. */
|
|
76
|
+
canonicalSlug: string;
|
|
77
|
+
};
|
|
78
|
+
/** Search scopes that own different destinations for the same suggestion. */
|
|
79
|
+
type SuggestionPathScope = 'jobs' | 'companies' | 'blog';
|
|
80
|
+
type SuggestionPathOptions = {
|
|
81
|
+
scope: SuggestionPathScope;
|
|
82
|
+
/**
|
|
83
|
+
* Active place slug. When set on the `jobs` scope with a category or
|
|
84
|
+
* skill term, routes into the location combination pages.
|
|
85
|
+
*/
|
|
86
|
+
location?: string;
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* Resolve a suggestion to a board-relative path for the given search scope
|
|
90
|
+
* The API never returns an `href` because the same
|
|
91
|
+
* `CompanySuggestion` means "navigate to the company page" under companies
|
|
92
|
+
* scope and "apply a `companySlug` filter" under jobs scope — only the app
|
|
93
|
+
* knows which.
|
|
94
|
+
*
|
|
95
|
+
* Returns `null` when the selection is not a navigation (jobs-scope company
|
|
96
|
+
* → filter, or a kind that has no path under the active scope). Compose from
|
|
97
|
+
* the existing `*Path` helpers; never string-build at the call site.
|
|
98
|
+
*
|
|
99
|
+
* | scope | suggestion | result |
|
|
100
|
+
* | --- | --- | --- |
|
|
101
|
+
* | jobs | skill | `/jobs/skills/<slug>` |
|
|
102
|
+
* | jobs | category | `/jobs/<slug>` |
|
|
103
|
+
* | jobs | skill + location | `/jobs/locations/<loc>/skills/<slug>` |
|
|
104
|
+
* | jobs | category + location | `/jobs/locations/<loc>/<slug>` |
|
|
105
|
+
* | jobs | company | `null` (apply `companySlug` filter) |
|
|
106
|
+
* | jobs | market | `null` |
|
|
107
|
+
* | jobs | post / tag | `null` |
|
|
108
|
+
* | companies | company | `/companies/<slug>` |
|
|
109
|
+
* | companies | market | `/companies/markets/<slug>` |
|
|
110
|
+
* | companies | term / post / tag | `null` |
|
|
111
|
+
* | blog | post | `/blog/<slug>` |
|
|
112
|
+
* | blog | tag | `/blog/tag/<slug>` |
|
|
113
|
+
* | blog | other | `null` |
|
|
114
|
+
*/
|
|
115
|
+
declare function suggestionPath(suggestion: SuggestionPathInput, options: SuggestionPathOptions): string | null;
|
|
29
116
|
/** A company's salary overview. */
|
|
30
117
|
declare function companySalaryPath(companySlug: string): string;
|
|
31
118
|
/** Salary page for a job title. */
|
|
@@ -90,7 +177,8 @@ declare function goAlertsConfirmPath(): string;
|
|
|
90
177
|
/**
|
|
91
178
|
* Prefix a board-relative path with the board origin. A trailing slash on
|
|
92
179
|
* the origin is tolerated (stripped) so callers need not normalise first.
|
|
180
|
+
* Paths from `*Path` helpers already percent-encode dynamic segments.
|
|
93
181
|
*/
|
|
94
182
|
declare function boardUrl(origin: string, path: string): string;
|
|
95
183
|
|
|
96
|
-
export { BOARD_PATHS, blogAuthorPath, blogPostPath, blogTagPath, boardUrl, companyMarketPath, companyPath, companySalaryPath, goAlertsConfirmPath, goAlertsManagePath, goJobPath, jobDetailPath, jobsCategoryPath, jobsLocationPath, jobsSkillPath, salaryLocationPath, salarySkillPath, salaryTitlePath };
|
|
184
|
+
export { BOARD_PATHS, type SuggestionPathInput, type SuggestionPathOptions, type SuggestionPathScope, blogAuthorPath, blogPostPath, blogTagPath, boardUrl, companyMarketPath, companyPath, companySalaryPath, encodePathSegment, goAlertsConfirmPath, goAlertsManagePath, goJobPath, jobDetailPath, jobsCategoryPath, jobsLocationCategoryPath, jobsLocationPath, jobsLocationSkillPath, jobsSkillPath, salaryLocationPath, salarySkillPath, salaryTitlePath, suggestionPath };
|
package/dist/paths.js
CHANGED
|
@@ -28,56 +28,104 @@ __export(paths_exports, {
|
|
|
28
28
|
companyMarketPath: () => companyMarketPath,
|
|
29
29
|
companyPath: () => companyPath,
|
|
30
30
|
companySalaryPath: () => companySalaryPath,
|
|
31
|
+
encodePathSegment: () => encodePathSegment,
|
|
31
32
|
goAlertsConfirmPath: () => goAlertsConfirmPath,
|
|
32
33
|
goAlertsManagePath: () => goAlertsManagePath,
|
|
33
34
|
goJobPath: () => goJobPath,
|
|
34
35
|
jobDetailPath: () => jobDetailPath,
|
|
35
36
|
jobsCategoryPath: () => jobsCategoryPath,
|
|
37
|
+
jobsLocationCategoryPath: () => jobsLocationCategoryPath,
|
|
36
38
|
jobsLocationPath: () => jobsLocationPath,
|
|
39
|
+
jobsLocationSkillPath: () => jobsLocationSkillPath,
|
|
37
40
|
jobsSkillPath: () => jobsSkillPath,
|
|
38
41
|
salaryLocationPath: () => salaryLocationPath,
|
|
39
42
|
salarySkillPath: () => salarySkillPath,
|
|
40
|
-
salaryTitlePath: () => salaryTitlePath
|
|
43
|
+
salaryTitlePath: () => salaryTitlePath,
|
|
44
|
+
suggestionPath: () => suggestionPath
|
|
41
45
|
});
|
|
42
46
|
module.exports = __toCommonJS(paths_exports);
|
|
47
|
+
function encodePathSegment(segment) {
|
|
48
|
+
try {
|
|
49
|
+
return encodeURIComponent(decodeURIComponent(segment)).replace(
|
|
50
|
+
/%3A/gi,
|
|
51
|
+
":"
|
|
52
|
+
);
|
|
53
|
+
} catch {
|
|
54
|
+
return encodeURIComponent(segment).replace(/%3A/gi, ":");
|
|
55
|
+
}
|
|
56
|
+
}
|
|
43
57
|
function jobDetailPath(companySlug, jobSlug) {
|
|
44
|
-
return `/companies/${companySlug}/jobs/${jobSlug}`;
|
|
58
|
+
return `/companies/${encodePathSegment(companySlug)}/jobs/${encodePathSegment(jobSlug)}`;
|
|
45
59
|
}
|
|
46
60
|
function jobsCategoryPath(categorySlug) {
|
|
47
|
-
return `/jobs/${categorySlug}`;
|
|
61
|
+
return `/jobs/${encodePathSegment(categorySlug)}`;
|
|
48
62
|
}
|
|
49
63
|
function jobsSkillPath(skillSlug) {
|
|
50
|
-
return `/jobs/skills/${skillSlug}`;
|
|
64
|
+
return `/jobs/skills/${encodePathSegment(skillSlug)}`;
|
|
51
65
|
}
|
|
52
66
|
function jobsLocationPath(placeSlug) {
|
|
53
|
-
return `/jobs/locations/${placeSlug}`;
|
|
67
|
+
return `/jobs/locations/${encodePathSegment(placeSlug)}`;
|
|
68
|
+
}
|
|
69
|
+
function jobsLocationCategoryPath(placeSlug, categorySlug) {
|
|
70
|
+
return `${jobsLocationPath(placeSlug)}/${encodePathSegment(categorySlug)}`;
|
|
71
|
+
}
|
|
72
|
+
function jobsLocationSkillPath(placeSlug, skillSlug) {
|
|
73
|
+
return `${jobsLocationPath(placeSlug)}/skills/${encodePathSegment(skillSlug)}`;
|
|
54
74
|
}
|
|
55
75
|
function companyPath(companySlug) {
|
|
56
|
-
return `/companies/${companySlug}`;
|
|
76
|
+
return `/companies/${encodePathSegment(companySlug)}`;
|
|
57
77
|
}
|
|
58
78
|
function companyMarketPath(marketSlug) {
|
|
59
|
-
return `/companies/markets/${marketSlug}`;
|
|
79
|
+
return `/companies/markets/${encodePathSegment(marketSlug)}`;
|
|
80
|
+
}
|
|
81
|
+
function suggestionPath(suggestion, options) {
|
|
82
|
+
const location = options.location?.trim() || void 0;
|
|
83
|
+
if (options.scope === "blog") {
|
|
84
|
+
if (suggestion.type === "post") {
|
|
85
|
+
return blogPostPath(suggestion.slug);
|
|
86
|
+
}
|
|
87
|
+
if (suggestion.type === "tag") {
|
|
88
|
+
return blogTagPath(suggestion.slug);
|
|
89
|
+
}
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
if (options.scope === "jobs") {
|
|
93
|
+
if (suggestion.type === "company" || suggestion.type === "market" || suggestion.type === "post" || suggestion.type === "tag") {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
if (suggestion.termType === "skill") {
|
|
97
|
+
return location ? jobsLocationSkillPath(location, suggestion.canonicalSlug) : jobsSkillPath(suggestion.canonicalSlug);
|
|
98
|
+
}
|
|
99
|
+
return location ? jobsLocationCategoryPath(location, suggestion.canonicalSlug) : jobsCategoryPath(suggestion.canonicalSlug);
|
|
100
|
+
}
|
|
101
|
+
if (suggestion.type === "company") {
|
|
102
|
+
return companyPath(suggestion.slug);
|
|
103
|
+
}
|
|
104
|
+
if (suggestion.type === "market") {
|
|
105
|
+
return companyMarketPath(suggestion.slug);
|
|
106
|
+
}
|
|
107
|
+
return null;
|
|
60
108
|
}
|
|
61
109
|
function companySalaryPath(companySlug) {
|
|
62
|
-
return `/companies/${companySlug}/salaries`;
|
|
110
|
+
return `/companies/${encodePathSegment(companySlug)}/salaries`;
|
|
63
111
|
}
|
|
64
112
|
function salaryTitlePath(titleSlug) {
|
|
65
|
-
return `/salaries/titles/${titleSlug}`;
|
|
113
|
+
return `/salaries/titles/${encodePathSegment(titleSlug)}`;
|
|
66
114
|
}
|
|
67
115
|
function salarySkillPath(skillSlug) {
|
|
68
|
-
return `/salaries/skills/${skillSlug}`;
|
|
116
|
+
return `/salaries/skills/${encodePathSegment(skillSlug)}`;
|
|
69
117
|
}
|
|
70
118
|
function salaryLocationPath(placeSlug) {
|
|
71
|
-
return `/salaries/locations/${placeSlug}`;
|
|
119
|
+
return `/salaries/locations/${encodePathSegment(placeSlug)}`;
|
|
72
120
|
}
|
|
73
121
|
function blogPostPath(postSlug) {
|
|
74
|
-
return `/blog/${postSlug}`;
|
|
122
|
+
return `/blog/${encodePathSegment(postSlug)}`;
|
|
75
123
|
}
|
|
76
124
|
function blogTagPath(tagSlug) {
|
|
77
|
-
return `/blog/tag/${tagSlug}`;
|
|
125
|
+
return `/blog/tag/${encodePathSegment(tagSlug)}`;
|
|
78
126
|
}
|
|
79
127
|
function blogAuthorPath(authorSlug) {
|
|
80
|
-
return `/blog/author/${authorSlug}`;
|
|
128
|
+
return `/blog/author/${encodePathSegment(authorSlug)}`;
|
|
81
129
|
}
|
|
82
130
|
var BOARD_PATHS = {
|
|
83
131
|
home: "/",
|
package/dist/paths.mjs
CHANGED
|
@@ -7,17 +7,21 @@ import {
|
|
|
7
7
|
companyMarketPath,
|
|
8
8
|
companyPath,
|
|
9
9
|
companySalaryPath,
|
|
10
|
+
encodePathSegment,
|
|
10
11
|
goAlertsConfirmPath,
|
|
11
12
|
goAlertsManagePath,
|
|
12
13
|
goJobPath,
|
|
13
14
|
jobDetailPath,
|
|
14
15
|
jobsCategoryPath,
|
|
16
|
+
jobsLocationCategoryPath,
|
|
15
17
|
jobsLocationPath,
|
|
18
|
+
jobsLocationSkillPath,
|
|
16
19
|
jobsSkillPath,
|
|
17
20
|
salaryLocationPath,
|
|
18
21
|
salarySkillPath,
|
|
19
|
-
salaryTitlePath
|
|
20
|
-
|
|
22
|
+
salaryTitlePath,
|
|
23
|
+
suggestionPath
|
|
24
|
+
} from "./chunk-BGKNXG4H.mjs";
|
|
21
25
|
export {
|
|
22
26
|
BOARD_PATHS,
|
|
23
27
|
blogAuthorPath,
|
|
@@ -27,14 +31,18 @@ export {
|
|
|
27
31
|
companyMarketPath,
|
|
28
32
|
companyPath,
|
|
29
33
|
companySalaryPath,
|
|
34
|
+
encodePathSegment,
|
|
30
35
|
goAlertsConfirmPath,
|
|
31
36
|
goAlertsManagePath,
|
|
32
37
|
goJobPath,
|
|
33
38
|
jobDetailPath,
|
|
34
39
|
jobsCategoryPath,
|
|
40
|
+
jobsLocationCategoryPath,
|
|
35
41
|
jobsLocationPath,
|
|
42
|
+
jobsLocationSkillPath,
|
|
36
43
|
jobsSkillPath,
|
|
37
44
|
salaryLocationPath,
|
|
38
45
|
salarySkillPath,
|
|
39
|
-
salaryTitlePath
|
|
46
|
+
salaryTitlePath,
|
|
47
|
+
suggestionPath
|
|
40
48
|
};
|
package/dist/route-contract.js
CHANGED
|
@@ -51,44 +51,54 @@ __export(route_contract_exports, {
|
|
|
51
51
|
module.exports = __toCommonJS(route_contract_exports);
|
|
52
52
|
|
|
53
53
|
// src/paths/index.ts
|
|
54
|
+
function encodePathSegment(segment) {
|
|
55
|
+
try {
|
|
56
|
+
return encodeURIComponent(decodeURIComponent(segment)).replace(
|
|
57
|
+
/%3A/gi,
|
|
58
|
+
":"
|
|
59
|
+
);
|
|
60
|
+
} catch {
|
|
61
|
+
return encodeURIComponent(segment).replace(/%3A/gi, ":");
|
|
62
|
+
}
|
|
63
|
+
}
|
|
54
64
|
function jobDetailPath(companySlug, jobSlug) {
|
|
55
|
-
return `/companies/${companySlug}/jobs/${jobSlug}`;
|
|
65
|
+
return `/companies/${encodePathSegment(companySlug)}/jobs/${encodePathSegment(jobSlug)}`;
|
|
56
66
|
}
|
|
57
67
|
function jobsCategoryPath(categorySlug) {
|
|
58
|
-
return `/jobs/${categorySlug}`;
|
|
68
|
+
return `/jobs/${encodePathSegment(categorySlug)}`;
|
|
59
69
|
}
|
|
60
70
|
function jobsSkillPath(skillSlug) {
|
|
61
|
-
return `/jobs/skills/${skillSlug}`;
|
|
71
|
+
return `/jobs/skills/${encodePathSegment(skillSlug)}`;
|
|
62
72
|
}
|
|
63
73
|
function jobsLocationPath(placeSlug) {
|
|
64
|
-
return `/jobs/locations/${placeSlug}`;
|
|
74
|
+
return `/jobs/locations/${encodePathSegment(placeSlug)}`;
|
|
65
75
|
}
|
|
66
76
|
function companyPath(companySlug) {
|
|
67
|
-
return `/companies/${companySlug}`;
|
|
77
|
+
return `/companies/${encodePathSegment(companySlug)}`;
|
|
68
78
|
}
|
|
69
79
|
function companyMarketPath(marketSlug) {
|
|
70
|
-
return `/companies/markets/${marketSlug}`;
|
|
80
|
+
return `/companies/markets/${encodePathSegment(marketSlug)}`;
|
|
71
81
|
}
|
|
72
82
|
function companySalaryPath(companySlug) {
|
|
73
|
-
return `/companies/${companySlug}/salaries`;
|
|
83
|
+
return `/companies/${encodePathSegment(companySlug)}/salaries`;
|
|
74
84
|
}
|
|
75
85
|
function salaryTitlePath(titleSlug) {
|
|
76
|
-
return `/salaries/titles/${titleSlug}`;
|
|
86
|
+
return `/salaries/titles/${encodePathSegment(titleSlug)}`;
|
|
77
87
|
}
|
|
78
88
|
function salarySkillPath(skillSlug) {
|
|
79
|
-
return `/salaries/skills/${skillSlug}`;
|
|
89
|
+
return `/salaries/skills/${encodePathSegment(skillSlug)}`;
|
|
80
90
|
}
|
|
81
91
|
function salaryLocationPath(placeSlug) {
|
|
82
|
-
return `/salaries/locations/${placeSlug}`;
|
|
92
|
+
return `/salaries/locations/${encodePathSegment(placeSlug)}`;
|
|
83
93
|
}
|
|
84
94
|
function blogPostPath(postSlug) {
|
|
85
|
-
return `/blog/${postSlug}`;
|
|
95
|
+
return `/blog/${encodePathSegment(postSlug)}`;
|
|
86
96
|
}
|
|
87
97
|
function blogTagPath(tagSlug) {
|
|
88
|
-
return `/blog/tag/${tagSlug}`;
|
|
98
|
+
return `/blog/tag/${encodePathSegment(tagSlug)}`;
|
|
89
99
|
}
|
|
90
100
|
function blogAuthorPath(authorSlug) {
|
|
91
|
-
return `/blog/author/${authorSlug}`;
|
|
101
|
+
return `/blog/author/${encodePathSegment(authorSlug)}`;
|
|
92
102
|
}
|
|
93
103
|
var BOARD_PATHS = {
|
|
94
104
|
home: "/",
|
package/dist/route-contract.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { S as Schemas } from './_spec-
|
|
1
|
+
import { S as Schemas } from './_spec-GVe6WAfT.mjs';
|
|
2
2
|
|
|
3
3
|
type Awaitable<T> = T | Promise<T>;
|
|
4
4
|
/**
|
|
@@ -69,13 +69,24 @@ declare class BoardClient {
|
|
|
69
69
|
type SuggestResult = Schemas['SuggestResult'];
|
|
70
70
|
type SuggestionItem = Schemas['SuggestionItem'];
|
|
71
71
|
type CompanySuggestion = Schemas['CompanySuggestion'];
|
|
72
|
+
type MarketSuggestion = Schemas['MarketSuggestion'];
|
|
72
73
|
type TermSuggestion = Schemas['TermSuggestion'];
|
|
74
|
+
/** Kinds accepted by `SearchSuggestQuery.types`. */
|
|
75
|
+
type SearchSuggestType = 'company' | 'category' | 'skill' | 'market' | 'post' | 'tag';
|
|
73
76
|
/** Query for `board.search.suggest()`. */
|
|
74
77
|
type SearchSuggestQuery = {
|
|
75
78
|
/** Keyword; under 2 chars the server returns an empty `items` array. */
|
|
76
79
|
q?: string;
|
|
77
|
-
/**
|
|
80
|
+
/**
|
|
81
|
+
* Max interleaved suggestions after type filtering (1–25; default 25).
|
|
82
|
+
* With `types: ['skill']`, this is "up to N skills", not N of a mixed pool.
|
|
83
|
+
*/
|
|
78
84
|
limit?: number;
|
|
85
|
+
/**
|
|
86
|
+
* Include only these suggestion kinds. Omit for every kind (company,
|
|
87
|
+
* category, skill, market, post, tag). Repeated query keys on the wire.
|
|
88
|
+
*/
|
|
89
|
+
types?: SearchSuggestType[];
|
|
79
90
|
};
|
|
80
91
|
|
|
81
|
-
export { type Awaitable as A, type BoardRequest as B, type CompanySuggestion as C, type FetchOptions as F, type Logger as L, REFRESH_TOKEN_KEY as R, type StorageMode as S, type TermSuggestion as T, BoardClient as a, type SearchSuggestQuery as b, ACCESS_TOKEN_KEY as c, BOARD_ACCESS_GRANT_KEY as d, type CustomStorage as e, type
|
|
92
|
+
export { type Awaitable as A, type BoardRequest as B, type CompanySuggestion as C, type FetchOptions as F, type Logger as L, type MarketSuggestion as M, REFRESH_TOKEN_KEY as R, type StorageMode as S, type TermSuggestion as T, BoardClient as a, type SearchSuggestQuery as b, ACCESS_TOKEN_KEY as c, BOARD_ACCESS_GRANT_KEY as d, type CustomStorage as e, type SearchSuggestType as f, type SuggestResult as g, type SuggestionItem as h };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { S as Schemas } from './_spec-
|
|
1
|
+
import { S as Schemas } from './_spec-GVe6WAfT.js';
|
|
2
2
|
|
|
3
3
|
type Awaitable<T> = T | Promise<T>;
|
|
4
4
|
/**
|
|
@@ -69,13 +69,24 @@ declare class BoardClient {
|
|
|
69
69
|
type SuggestResult = Schemas['SuggestResult'];
|
|
70
70
|
type SuggestionItem = Schemas['SuggestionItem'];
|
|
71
71
|
type CompanySuggestion = Schemas['CompanySuggestion'];
|
|
72
|
+
type MarketSuggestion = Schemas['MarketSuggestion'];
|
|
72
73
|
type TermSuggestion = Schemas['TermSuggestion'];
|
|
74
|
+
/** Kinds accepted by `SearchSuggestQuery.types`. */
|
|
75
|
+
type SearchSuggestType = 'company' | 'category' | 'skill' | 'market' | 'post' | 'tag';
|
|
73
76
|
/** Query for `board.search.suggest()`. */
|
|
74
77
|
type SearchSuggestQuery = {
|
|
75
78
|
/** Keyword; under 2 chars the server returns an empty `items` array. */
|
|
76
79
|
q?: string;
|
|
77
|
-
/**
|
|
80
|
+
/**
|
|
81
|
+
* Max interleaved suggestions after type filtering (1–25; default 25).
|
|
82
|
+
* With `types: ['skill']`, this is "up to N skills", not N of a mixed pool.
|
|
83
|
+
*/
|
|
78
84
|
limit?: number;
|
|
85
|
+
/**
|
|
86
|
+
* Include only these suggestion kinds. Omit for every kind (company,
|
|
87
|
+
* category, skill, market, post, tag). Repeated query keys on the wire.
|
|
88
|
+
*/
|
|
89
|
+
types?: SearchSuggestType[];
|
|
79
90
|
};
|
|
80
91
|
|
|
81
|
-
export { type Awaitable as A, type BoardRequest as B, type CompanySuggestion as C, type FetchOptions as F, type Logger as L, REFRESH_TOKEN_KEY as R, type StorageMode as S, type TermSuggestion as T, BoardClient as a, type SearchSuggestQuery as b, ACCESS_TOKEN_KEY as c, BOARD_ACCESS_GRANT_KEY as d, type CustomStorage as e, type
|
|
92
|
+
export { type Awaitable as A, type BoardRequest as B, type CompanySuggestion as C, type FetchOptions as F, type Logger as L, type MarketSuggestion as M, REFRESH_TOKEN_KEY as R, type StorageMode as S, type TermSuggestion as T, BoardClient as a, type SearchSuggestQuery as b, ACCESS_TOKEN_KEY as c, BOARD_ACCESS_GRANT_KEY as d, type CustomStorage as e, type SearchSuggestType as f, type SuggestResult as g, type SuggestionItem as h };
|
package/dist/seo.d.mts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { P as PublicBlogPostSummary, B as BlogAuthorEmbed } from './blog-
|
|
2
|
-
import {
|
|
3
|
-
export { A as ALL_COUNTRY_CODES, B as BreadcrumbItemInput,
|
|
4
|
-
import './_spec-
|
|
5
|
-
import './board-
|
|
6
|
-
import './jobs-
|
|
7
|
-
import './ui-copy-CCkz6CVF.mjs';
|
|
1
|
+
import { P as PublicBlogPostSummary, B as BlogAuthorEmbed } from './blog-Cf64mI8A.mjs';
|
|
2
|
+
import { e as JsonLdObject } from './breadcrumbs-DFKr0HzU.mjs';
|
|
3
|
+
export { A as ALL_COUNTRY_CODES, B as BreadcrumbItemInput, g as FaqItem, J as JobBreadcrumb, h as JsonLdBoard, N as NumberNotation, i as SENIORITY_ORDER, j as SalaryFaqEntry, k as SalaryJsonLdOptions, l as SalaryOccupationListOptions, b as buildJobBreadcrumbs, m as buildSalaryFaq, o as companyCategorySalaryJsonLd, p as companySalaryJsonLd, q as createBreadcrumbJsonLd, r as createJobPostingJsonLd, s as crossAxisSalaryJsonLd, t as faqJsonLd, c as formatSalaryStat, d as formatSalaryStatRange, u as itemListJsonLd, v as locationSalaryJsonLd, n as normalizeWebsiteUrl, w as skillSalaryJsonLd, x as sortBySeniority, y as titleSalaryJsonLd } from './breadcrumbs-DFKr0HzU.mjs';
|
|
4
|
+
import './_spec-GVe6WAfT.mjs';
|
|
5
|
+
import './board-GGWJ4coe.mjs';
|
|
6
|
+
import './jobs-BuLpV8iy.mjs';
|
|
8
7
|
|
|
9
8
|
/**
|
|
10
9
|
* Blog structured data on the `@cavuno/board` wire types, transcribed from
|
|
@@ -59,36 +58,32 @@ declare function createAuthorProfileJsonLd({ author, canonical, description, ori
|
|
|
59
58
|
totalPosts: number;
|
|
60
59
|
}): JsonLdObject | null;
|
|
61
60
|
|
|
62
|
-
/**
|
|
63
|
-
* Framework-neutral `<head>` + structural JSON-LD builders for jobs-listing
|
|
64
|
-
* pages. Locale-neutral pass-through: the caller supplies the
|
|
65
|
-
* heading/board copy (title/description COPY is board-SEO-config +
|
|
66
|
-
* localized — a named exclusion; these builders replicate the hosted
|
|
67
|
-
* STRUCTURE: title with result count, canonical, Open Graph, breadcrumb +
|
|
68
|
-
* job `ItemList`).
|
|
69
|
-
*/
|
|
70
|
-
|
|
71
61
|
interface ListingHeadOptions {
|
|
72
|
-
|
|
62
|
+
/**
|
|
63
|
+
* Fully composed document title — application owns counters, particles,
|
|
64
|
+
* word order, and separators (e.g. `1,225 Jobs | Acme`, `1.225 Jobs | Acme`,
|
|
65
|
+
* `1,225件の求人 | 求人ボード`). The SDK does not join count/heading/board.
|
|
66
|
+
*/
|
|
67
|
+
title: string;
|
|
73
68
|
origin: string;
|
|
74
69
|
/** Absolute path of THIS page, for canonical / og:url, e.g. `/jobs/robotik`. */
|
|
75
70
|
path: string;
|
|
76
|
-
/** Human heading, e.g. `Robotik jobs in Berlin` or `Jobs`. */
|
|
77
|
-
heading: string;
|
|
78
|
-
/** Result count — prefixed into the title (`N heading`). */
|
|
79
|
-
count?: number;
|
|
80
71
|
/**
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
* ungrouped digits the hosted structure has always emitted.
|
|
72
|
+
* Meta description — fully owned by the application. Pass a sentence from
|
|
73
|
+
* the board's copy source (page config, message catalog, template). The SDK
|
|
74
|
+
* does not invent or case-fold this string.
|
|
85
75
|
*/
|
|
86
|
-
|
|
76
|
+
description: string;
|
|
87
77
|
}
|
|
88
78
|
/**
|
|
89
|
-
* Meta/link descriptors for a jobs-listing page — title
|
|
90
|
-
*
|
|
91
|
-
*
|
|
79
|
+
* Meta/link descriptors for a jobs-listing page — caller-supplied title and
|
|
80
|
+
* meta description, Open Graph, and `<link rel=canonical>`. Framework-neutral:
|
|
81
|
+
* map `meta`/`links` into your head manager.
|
|
82
|
+
*
|
|
83
|
+
* **Why `title` and `description` are both accepted composed:** a function
|
|
84
|
+
* that takes a finished description but still composes its own title from
|
|
85
|
+
* count/heading/boardName is incoherent — both are display sentences whose
|
|
86
|
+
* arrangement is locale-dependent (Japanese counters, RTL order, …).
|
|
92
87
|
*/
|
|
93
88
|
declare function listingHead(options: ListingHeadOptions): {
|
|
94
89
|
meta: ({
|
|
@@ -123,6 +118,12 @@ interface JobLink {
|
|
|
123
118
|
* page, mirroring the structural JSON-LD the hosted board emits. The rich
|
|
124
119
|
* category set (`Occupation`/`FAQPage`/`MonetaryAmountDistribution`) lives
|
|
125
120
|
* in `./salary` — feed it the salary detail reads.
|
|
121
|
+
*
|
|
122
|
+
* Empty / single-crumb trails omit `BreadcrumbList` (same min-2 rule as
|
|
123
|
+
* `createBreadcrumbJsonLd` — an empty `itemListElement` is invalid structured
|
|
124
|
+
* data). Job URLs go through `jobDetailPath` — `/jobs/{slug}` is always a
|
|
125
|
+
* listing route, never a job detail; company-less jobs are dropped from the
|
|
126
|
+
* ItemList rather than linked to an invalid path.
|
|
126
127
|
*/
|
|
127
128
|
declare function listingJsonLd(options: {
|
|
128
129
|
origin: string;
|