@cavuno/board 3.0.0 → 3.2.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 +21 -0
- package/README.md +11 -0
- package/dist/blog-DGBHmExV.d.mts +29 -0
- package/dist/blog-iV9XEIGf.d.ts +29 -0
- package/dist/{salaries-ChSqDBAM.d.mts → board-BJbXd_9_.d.mts} +14 -27
- package/dist/{salaries-BahBpxWT.d.ts → board-JhdUWIoq.d.ts} +14 -27
- package/dist/breadcrumbs-1hMjcsqA.d.mts +171 -0
- package/dist/breadcrumbs-DexU07TT.d.ts +171 -0
- package/dist/chunk-2ETP5UH4.mjs +271 -0
- package/dist/chunk-DLSO6XXY.mjs +33 -0
- package/dist/chunk-IJCJ26SQ.mjs +596 -0
- package/dist/chunk-L7ZOYWHW.mjs +98 -0
- package/dist/chunk-LUGJWOT7.mjs +96 -0
- package/dist/chunk-NXY3OWGF.mjs +1060 -0
- package/dist/chunk-STE77MAT.mjs +844 -0
- package/dist/filters.mjs +7 -668
- package/dist/format.d.mts +2 -1
- package/dist/format.d.ts +2 -1
- package/dist/format.js +592 -523
- package/dist/format.mjs +19 -680
- package/dist/go.mjs +4 -29
- package/dist/index.d.mts +5 -4
- package/dist/index.d.ts +5 -4
- package/dist/index.js +1 -1
- package/dist/index.mjs +22 -278
- package/dist/paths.mjs +20 -77
- package/dist/route-contract.mjs +30 -1079
- package/dist/seo.d.mts +14 -171
- package/dist/seo.d.ts +14 -171
- package/dist/seo.js +1 -1
- package/dist/seo.mjs +24 -1399
- package/dist/server.d.mts +2 -2
- package/dist/server.d.ts +2 -2
- package/dist/server.mjs +5 -31
- package/dist/sitemap.d.mts +2 -2
- package/dist/sitemap.d.ts +2 -2
- package/dist/sitemap.mjs +20 -96
- package/dist/well-known.mjs +4 -421
- package/package.json +6 -3
- package/skills/cavuno-board-account/SKILL.md +4 -0
- package/skills/cavuno-board-api-client/SKILL.md +4 -0
- package/skills/cavuno-board-applications/SKILL.md +4 -0
- package/skills/cavuno-board-auth/SKILL.md +4 -0
- package/skills/cavuno-board-blog/SKILL.md +4 -0
- package/skills/cavuno-board-companies/SKILL.md +4 -0
- package/skills/cavuno-board-errors/SKILL.md +4 -0
- package/skills/cavuno-board-filters/SKILL.md +4 -0
- package/skills/cavuno-board-format/SKILL.md +4 -0
- package/skills/cavuno-board-i18n/SKILL.md +4 -0
- package/skills/cavuno-board-job-alerts/SKILL.md +4 -0
- package/skills/cavuno-board-jobs/SKILL.md +4 -0
- package/skills/cavuno-board-messaging/SKILL.md +4 -0
- package/skills/cavuno-board-paywall/SKILL.md +4 -0
- package/skills/cavuno-board-post-a-job/SKILL.md +4 -0
- package/skills/cavuno-board-salaries/SKILL.md +4 -0
- package/skills/cavuno-board-search-suggestions/SKILL.md +4 -0
- package/skills/cavuno-board-seo/SKILL.md +4 -0
- package/skills/cavuno-board-server-sessions/SKILL.md +4 -0
- package/skills/cavuno-board-setup/SKILL.md +4 -0
- package/skills/cavuno-board-sitemap/SKILL.md +4 -0
- package/skills/cavuno-board-smoke-test/SKILL.md +4 -0
- package/skills/manifest.json +1 -1
- package/dist/board-NE98PlwN.d.ts +0 -16
- package/dist/board-muu8BI8P.d.mts +0 -16
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
// src/errors.ts
|
|
2
|
+
var BOARD_API_ERROR_CODES = [
|
|
3
|
+
// Cross-cutting envelope
|
|
4
|
+
"internal_error",
|
|
5
|
+
"validation_bad_request",
|
|
6
|
+
"validation_payload_too_large",
|
|
7
|
+
"rate_limited",
|
|
8
|
+
"rate_limit_unavailable",
|
|
9
|
+
"search_unavailable",
|
|
10
|
+
"too_many_filter_values",
|
|
11
|
+
"pagination_invalid_cursor",
|
|
12
|
+
"pagination_offset_too_large",
|
|
13
|
+
"auth_unauthenticated",
|
|
14
|
+
"auth_forbidden",
|
|
15
|
+
"plan_upgrade_required",
|
|
16
|
+
"unknown_error",
|
|
17
|
+
// Board resolution + custom pages
|
|
18
|
+
"boards_not_found",
|
|
19
|
+
"board_page_not_found",
|
|
20
|
+
// Board-user auth
|
|
21
|
+
"board_auth_email_taken",
|
|
22
|
+
"board_auth_invalid_credentials",
|
|
23
|
+
"board_auth_invalid_token",
|
|
24
|
+
"board_auth_registration_disabled",
|
|
25
|
+
"board_auth_token_expired",
|
|
26
|
+
// Board password gate
|
|
27
|
+
"board_password_required",
|
|
28
|
+
"board_password_invalid",
|
|
29
|
+
// Jobs browse
|
|
30
|
+
"jobs_not_found",
|
|
31
|
+
"jobs_invalid_filter",
|
|
32
|
+
// Taxonomy
|
|
33
|
+
"categories_not_found",
|
|
34
|
+
"skills_not_found",
|
|
35
|
+
"places_not_found",
|
|
36
|
+
// Companies (public reads)
|
|
37
|
+
"companies_not_found",
|
|
38
|
+
"company_markets_not_found",
|
|
39
|
+
"company_salary_not_found",
|
|
40
|
+
"company_category_salary_not_found",
|
|
41
|
+
// Salaries
|
|
42
|
+
"salaries_title_not_found",
|
|
43
|
+
"salaries_skill_not_found",
|
|
44
|
+
"salaries_location_not_found",
|
|
45
|
+
"salaries_title_location_not_found",
|
|
46
|
+
"salaries_skill_location_not_found",
|
|
47
|
+
// Blog (public reads)
|
|
48
|
+
"blog_post_not_found",
|
|
49
|
+
"blog_author_not_found",
|
|
50
|
+
"blog_tag_not_found",
|
|
51
|
+
// Talent directory
|
|
52
|
+
"talent_not_found",
|
|
53
|
+
"talent_directory_not_found",
|
|
54
|
+
"talent_directory_restricted",
|
|
55
|
+
// Job alerts (anonymous + authenticated)
|
|
56
|
+
"job_alert_not_found",
|
|
57
|
+
"job_alerts_disabled",
|
|
58
|
+
"candidate_alert_limit_reached",
|
|
59
|
+
"candidate_alert_not_found",
|
|
60
|
+
// Candidate account
|
|
61
|
+
"candidate_entry_not_found",
|
|
62
|
+
"candidate_handle_taken",
|
|
63
|
+
"candidate_job_not_found",
|
|
64
|
+
"resume_invalid_file",
|
|
65
|
+
"resume_upload_forbidden",
|
|
66
|
+
// Applications
|
|
67
|
+
"applications_external_apply_only",
|
|
68
|
+
"applications_guest_not_allowed",
|
|
69
|
+
"applications_job_not_found",
|
|
70
|
+
"applications_not_found",
|
|
71
|
+
"applications_resume_invalid_file",
|
|
72
|
+
"applications_unprocessable",
|
|
73
|
+
// Messaging
|
|
74
|
+
"messaging_application_not_found",
|
|
75
|
+
"messaging_application_not_linked",
|
|
76
|
+
"messaging_blocked",
|
|
77
|
+
"messaging_cannot_block_self",
|
|
78
|
+
"messaging_cold_rule",
|
|
79
|
+
"messaging_conversation_not_found",
|
|
80
|
+
"messaging_disabled",
|
|
81
|
+
"messaging_edit_window_expired",
|
|
82
|
+
"messaging_message_deleted",
|
|
83
|
+
"messaging_message_not_found",
|
|
84
|
+
"messaging_not_author",
|
|
85
|
+
"messaging_not_participant",
|
|
86
|
+
"messaging_not_permitted",
|
|
87
|
+
"messaging_not_recipient",
|
|
88
|
+
"messaging_rate_limited",
|
|
89
|
+
"messaging_recipient_not_found",
|
|
90
|
+
"messaging_recipient_not_open",
|
|
91
|
+
"messaging_unsend_window_expired",
|
|
92
|
+
// Employer self-service
|
|
93
|
+
"employer_applicant_not_found",
|
|
94
|
+
"employer_ats_unprocessable",
|
|
95
|
+
"employer_checkout_failed",
|
|
96
|
+
"employer_company_exists",
|
|
97
|
+
"employer_company_name_taken",
|
|
98
|
+
"employer_company_not_found",
|
|
99
|
+
"employer_job_not_found",
|
|
100
|
+
"employer_job_slug_taken",
|
|
101
|
+
"employer_jobs_quota_exceeded",
|
|
102
|
+
"employer_not_member",
|
|
103
|
+
"employer_payment_required",
|
|
104
|
+
"employer_pipeline_stage_not_found",
|
|
105
|
+
// Candidate job-access paywall
|
|
106
|
+
"paywall_already_active",
|
|
107
|
+
"paywall_disabled",
|
|
108
|
+
"paywall_invalid_checkout_session",
|
|
109
|
+
"paywall_no_candidate_profile",
|
|
110
|
+
"paywall_no_recurring_subscription",
|
|
111
|
+
"paywall_offer_not_found",
|
|
112
|
+
// Public job posting
|
|
113
|
+
"job_posting_logo_lookup_unavailable",
|
|
114
|
+
"job_posting_logo_not_found",
|
|
115
|
+
"job_posting_rejected"
|
|
116
|
+
];
|
|
117
|
+
var BoardApiError = class extends Error {
|
|
118
|
+
status;
|
|
119
|
+
/** `<domain>_<snake_reason>` code from the v1 error envelope. */
|
|
120
|
+
code;
|
|
121
|
+
/** Structured, per-code details — shape varies by `code`. */
|
|
122
|
+
details;
|
|
123
|
+
requestId;
|
|
124
|
+
/** The parsed response body, untouched. */
|
|
125
|
+
raw;
|
|
126
|
+
constructor(input) {
|
|
127
|
+
super(input.message);
|
|
128
|
+
this.name = "BoardApiError";
|
|
129
|
+
this.status = input.status;
|
|
130
|
+
this.code = input.code;
|
|
131
|
+
this.details = input.details;
|
|
132
|
+
this.requestId = input.requestId;
|
|
133
|
+
this.raw = input.raw;
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
function isBoardApiError(e) {
|
|
137
|
+
return e instanceof Error && e.name === "BoardApiError" && typeof e.status === "number" && typeof e.code === "string";
|
|
138
|
+
}
|
|
139
|
+
function isNotFound(e) {
|
|
140
|
+
return isBoardApiError(e) && e.status === 404;
|
|
141
|
+
}
|
|
142
|
+
function isUnauthorized(e) {
|
|
143
|
+
return isBoardApiError(e) && e.status === 401;
|
|
144
|
+
}
|
|
145
|
+
function isBoardPasswordRequired(e) {
|
|
146
|
+
return isBoardApiError(e) && e.status === 401 && e.code === "board_password_required";
|
|
147
|
+
}
|
|
148
|
+
function isForbidden(e) {
|
|
149
|
+
return isBoardApiError(e) && e.status === 403;
|
|
150
|
+
}
|
|
151
|
+
function isValidationError(e) {
|
|
152
|
+
return isBoardApiError(e) && e.status === 400 && e.code === "validation_bad_request";
|
|
153
|
+
}
|
|
154
|
+
function isRateLimited(e) {
|
|
155
|
+
return isBoardApiError(e) && e.status === 429;
|
|
156
|
+
}
|
|
157
|
+
function isConflict(e) {
|
|
158
|
+
return isBoardApiError(e) && e.status === 409;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// src/scope.ts
|
|
162
|
+
function scopeToken(board) {
|
|
163
|
+
let hash = 5381;
|
|
164
|
+
for (let i = 0; i < board.length; i++) {
|
|
165
|
+
hash = (hash << 5) + hash + board.charCodeAt(i) >>> 0;
|
|
166
|
+
}
|
|
167
|
+
const sanitized = board.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
168
|
+
return `${sanitized}-${hash.toString(36)}`;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// src/storage.ts
|
|
172
|
+
var ACCESS_TOKEN_KEY = "cavuno_board_access_token";
|
|
173
|
+
var REFRESH_TOKEN_KEY = "cavuno_board_refresh_token";
|
|
174
|
+
var BOARD_ACCESS_GRANT_KEY = "cavuno_board_access_grant";
|
|
175
|
+
function isBrowser() {
|
|
176
|
+
return typeof globalThis.document !== "undefined";
|
|
177
|
+
}
|
|
178
|
+
function memoryStorage() {
|
|
179
|
+
const backing = /* @__PURE__ */ new Map();
|
|
180
|
+
return {
|
|
181
|
+
getItem: (key) => backing.get(key) ?? null,
|
|
182
|
+
setItem: (key, value) => {
|
|
183
|
+
backing.set(key, value);
|
|
184
|
+
},
|
|
185
|
+
removeItem: (key) => {
|
|
186
|
+
backing.delete(key);
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
function browserStorage(mode, scope) {
|
|
191
|
+
if (!isBrowser()) {
|
|
192
|
+
throw new Error(
|
|
193
|
+
`storage mode '${mode}' is browser-only \u2014 use 'nostore' + per-call headers on the server`
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
let backing;
|
|
197
|
+
try {
|
|
198
|
+
backing = mode === "local" ? globalThis.localStorage : globalThis.sessionStorage;
|
|
199
|
+
if (!backing) throw new Error("unavailable");
|
|
200
|
+
} catch {
|
|
201
|
+
throw new Error(
|
|
202
|
+
`storage mode '${mode}' is unavailable in this browsing context (storage access is blocked \u2014 e.g. a sandboxed or third-party iframe). Use 'memory', or 'nostore' + per-call headers.`
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
const suffix = scope ? `:${scopeToken(scope)}` : "";
|
|
206
|
+
return {
|
|
207
|
+
getItem: (key) => backing.getItem(`${key}${suffix}`),
|
|
208
|
+
setItem: (key, value) => {
|
|
209
|
+
backing.setItem(`${key}${suffix}`, value);
|
|
210
|
+
},
|
|
211
|
+
removeItem: (key) => {
|
|
212
|
+
backing.removeItem(`${key}${suffix}`);
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
var NOSTORE_BRAND = /* @__PURE__ */ Symbol.for("@cavuno/board:nostore");
|
|
217
|
+
function isNoStore(storage) {
|
|
218
|
+
return storage[NOSTORE_BRAND] === true;
|
|
219
|
+
}
|
|
220
|
+
var NOSTORE = {
|
|
221
|
+
[NOSTORE_BRAND]: true,
|
|
222
|
+
getItem: () => null,
|
|
223
|
+
setItem: () => {
|
|
224
|
+
},
|
|
225
|
+
removeItem: () => {
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
function resolveStorage(mode, scope) {
|
|
229
|
+
const resolved = mode ?? (isBrowser() ? "memory" : "nostore");
|
|
230
|
+
if (typeof resolved === "object") return resolved;
|
|
231
|
+
switch (resolved) {
|
|
232
|
+
case "memory":
|
|
233
|
+
return memoryStorage();
|
|
234
|
+
case "nostore":
|
|
235
|
+
return NOSTORE;
|
|
236
|
+
case "local":
|
|
237
|
+
case "session":
|
|
238
|
+
return browserStorage(resolved, scope);
|
|
239
|
+
default:
|
|
240
|
+
throw new Error(`Unknown storage mode '${String(resolved)}'`);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
async function writeSession(storage, session) {
|
|
244
|
+
await storage.setItem(ACCESS_TOKEN_KEY, session.accessToken);
|
|
245
|
+
await storage.setItem(REFRESH_TOKEN_KEY, session.refreshToken);
|
|
246
|
+
}
|
|
247
|
+
async function clearSession(storage) {
|
|
248
|
+
await storage.removeItem(ACCESS_TOKEN_KEY);
|
|
249
|
+
await storage.removeItem(REFRESH_TOKEN_KEY);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export {
|
|
253
|
+
BOARD_API_ERROR_CODES,
|
|
254
|
+
BoardApiError,
|
|
255
|
+
isBoardApiError,
|
|
256
|
+
isNotFound,
|
|
257
|
+
isUnauthorized,
|
|
258
|
+
isBoardPasswordRequired,
|
|
259
|
+
isForbidden,
|
|
260
|
+
isValidationError,
|
|
261
|
+
isRateLimited,
|
|
262
|
+
isConflict,
|
|
263
|
+
scopeToken,
|
|
264
|
+
ACCESS_TOKEN_KEY,
|
|
265
|
+
REFRESH_TOKEN_KEY,
|
|
266
|
+
BOARD_ACCESS_GRANT_KEY,
|
|
267
|
+
isNoStore,
|
|
268
|
+
resolveStorage,
|
|
269
|
+
writeSession,
|
|
270
|
+
clearSession
|
|
271
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// src/pagination.ts
|
|
2
|
+
function paginate(listFn, query, options) {
|
|
3
|
+
async function* pages() {
|
|
4
|
+
let current = query;
|
|
5
|
+
for (; ; ) {
|
|
6
|
+
const page = await listFn(current, options);
|
|
7
|
+
yield page;
|
|
8
|
+
if (!page.hasMore || page.nextCursor === null) return;
|
|
9
|
+
const { offset: _offset, ...rest } = current ?? {};
|
|
10
|
+
current = { ...rest, cursor: page.nextCursor };
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return {
|
|
14
|
+
pages,
|
|
15
|
+
async *[Symbol.asyncIterator]() {
|
|
16
|
+
for await (const page of pages()) {
|
|
17
|
+
yield* page.data;
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
async toArray({ limit }) {
|
|
21
|
+
const items = [];
|
|
22
|
+
for await (const page of pages()) {
|
|
23
|
+
items.push(...page.data);
|
|
24
|
+
if (items.length >= limit) break;
|
|
25
|
+
}
|
|
26
|
+
return items.slice(0, limit);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export {
|
|
32
|
+
paginate
|
|
33
|
+
};
|