@cavuno/board 1.0.0 → 1.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/dist/index.d.mts +352 -130
- package/dist/index.d.ts +352 -130
- package/dist/index.js +81 -2
- package/dist/index.mjs +81 -2
- package/package.json +10 -9
package/dist/index.d.mts
CHANGED
|
@@ -1,3 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stripe-shaped success envelopes (`01-conventions.md` §5.1). The
|
|
3
|
+
* server MAY add top-level fields; consumers MUST ignore unknown
|
|
4
|
+
* fields.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Medusa-style storefront pagination fields (ADR-0037 §7), populated only by
|
|
8
|
+
* the board jobs catalog reads (browse / search / company-jobs); absent on
|
|
9
|
+
* every other list/search. `nextCursor` is preserved alongside (offset-encoded).
|
|
10
|
+
*/
|
|
11
|
+
interface StorefrontPagination {
|
|
12
|
+
/** Total matching results ("X jobs"). */
|
|
13
|
+
count?: number;
|
|
14
|
+
/** The page size used for this response. */
|
|
15
|
+
limit?: number;
|
|
16
|
+
/** Number of items skipped before this page. */
|
|
17
|
+
offset?: number;
|
|
18
|
+
/** Items hidden behind the candidate paywall for the current viewer; absent/0 when entitled. */
|
|
19
|
+
gatedCount?: number;
|
|
20
|
+
}
|
|
21
|
+
interface ListEnvelope<T> extends StorefrontPagination {
|
|
22
|
+
object: 'list';
|
|
23
|
+
url: string;
|
|
24
|
+
hasMore: boolean;
|
|
25
|
+
/** `null` when `hasMore` is false — always present, never undefined. */
|
|
26
|
+
nextCursor: string | null;
|
|
27
|
+
data: T[];
|
|
28
|
+
}
|
|
29
|
+
interface SearchEnvelope<T> extends StorefrontPagination {
|
|
30
|
+
object: 'search_result';
|
|
31
|
+
url: string;
|
|
32
|
+
hasMore: boolean;
|
|
33
|
+
nextCursor: string | null;
|
|
34
|
+
data: T[];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
type RemoteOption = 'on_site' | 'hybrid' | 'remote';
|
|
38
|
+
type EmploymentType = 'full_time' | 'part_time' | 'contract' | 'internship' | 'temporary' | 'volunteer' | 'other';
|
|
39
|
+
type Seniority = 'entry_level' | 'associate' | 'mid_level' | 'senior' | 'lead' | 'principal' | 'director' | 'executive';
|
|
40
|
+
type EducationRequirement = 'high_school' | 'associate_degree' | 'bachelor_degree' | 'professional_certificate' | 'postgraduate_degree' | 'no_requirements';
|
|
41
|
+
interface OfficeLocation {
|
|
42
|
+
countryCode: string | null;
|
|
43
|
+
country: string | null;
|
|
44
|
+
locality: string | null;
|
|
45
|
+
city: string | null;
|
|
46
|
+
region: string | null;
|
|
47
|
+
regionCode: string | null;
|
|
48
|
+
postalCode: string | null;
|
|
49
|
+
displayName: string | null;
|
|
50
|
+
}
|
|
51
|
+
interface JobCompany {
|
|
52
|
+
id: string;
|
|
53
|
+
name: string | null;
|
|
54
|
+
slug: string | null;
|
|
55
|
+
logoUrl: string | null;
|
|
56
|
+
website: string | null;
|
|
57
|
+
}
|
|
58
|
+
interface RemotePermit {
|
|
59
|
+
type: string;
|
|
60
|
+
value: string;
|
|
61
|
+
}
|
|
62
|
+
interface RemoteTimezone {
|
|
63
|
+
type: string;
|
|
64
|
+
value: string;
|
|
65
|
+
plusMinus?: number;
|
|
66
|
+
}
|
|
67
|
+
interface PublicJob {
|
|
68
|
+
id: string;
|
|
69
|
+
object: 'public_job';
|
|
70
|
+
title: string;
|
|
71
|
+
slug: string | null;
|
|
72
|
+
status: string;
|
|
73
|
+
companyId: string | null;
|
|
74
|
+
employmentType: string | null;
|
|
75
|
+
remoteOption: string | null;
|
|
76
|
+
seniority: string | null;
|
|
77
|
+
salaryMin: number | null;
|
|
78
|
+
salaryMax: number | null;
|
|
79
|
+
salaryCurrency: string | null;
|
|
80
|
+
salaryTimeframe: string | null;
|
|
81
|
+
isFeatured: boolean;
|
|
82
|
+
publishedAt: string | null;
|
|
83
|
+
expiresAt: string | null;
|
|
84
|
+
createdAt: string;
|
|
85
|
+
updatedAt: string;
|
|
86
|
+
description: string | null;
|
|
87
|
+
applicationUrl: string | null;
|
|
88
|
+
/** Canonical hierarchical permit selection. */
|
|
89
|
+
remotePermits: RemotePermit[];
|
|
90
|
+
/** Read-only — derived from `remotePermits`. */
|
|
91
|
+
remoteWorldwide: boolean | null;
|
|
92
|
+
/** Canonical hierarchical timezone selection. */
|
|
93
|
+
remoteTimezones: RemoteTimezone[];
|
|
94
|
+
/** Read-only — derived from `remoteTimezones`. */
|
|
95
|
+
remoteAllowedTzOffsets: number[];
|
|
96
|
+
/** Read-only — derived from `remotePermits`. */
|
|
97
|
+
remoteWorkPermitCountryCodes: string[];
|
|
98
|
+
/** Read-only — derived from `remotePermits`. */
|
|
99
|
+
remoteWorkPermitSubdivisionCodes: string[];
|
|
100
|
+
remoteSponsorship: 'yes' | 'no' | 'unknown';
|
|
101
|
+
educationRequirements: EducationRequirement[];
|
|
102
|
+
experienceMonths: number | null;
|
|
103
|
+
experienceInPlaceOfEducation: boolean | null;
|
|
104
|
+
inOfficePeriod: 'per_week' | 'per_month' | 'per_year' | null;
|
|
105
|
+
inOfficeFrequency: number | null;
|
|
106
|
+
company: JobCompany | null;
|
|
107
|
+
officeLocations: OfficeLocation[];
|
|
108
|
+
/** Resolved taxonomy — same `{slug,name}` shape as `PublicJobCard`; names joined server-side. */
|
|
109
|
+
categories: Array<{
|
|
110
|
+
slug: string;
|
|
111
|
+
name: string;
|
|
112
|
+
}>;
|
|
113
|
+
skills: Array<{
|
|
114
|
+
slug: string;
|
|
115
|
+
name: string;
|
|
116
|
+
}>;
|
|
117
|
+
/**
|
|
118
|
+
* Place ancestor chain (country → region → city) for the breadcrumb. Each
|
|
119
|
+
* `slug` is the English SOURCE slug — the key `taxonomy.places.resolve()`
|
|
120
|
+
* accepts. On a localized board it may differ from the canonical
|
|
121
|
+
* board-language URL slug (the board router 308-redirects the source slug to
|
|
122
|
+
* the canonical one); link via `/jobs/locations/:slug`.
|
|
123
|
+
*/
|
|
124
|
+
placeHierarchy: Array<{
|
|
125
|
+
slug: string;
|
|
126
|
+
name: string;
|
|
127
|
+
}>;
|
|
128
|
+
links: {
|
|
129
|
+
public: string | null;
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* The slim listing card (ADR-0037 §1) returned by `jobs.list` / `jobs.search`
|
|
134
|
+
* / `companies.listJobs`. Full enrichment (description, officeLocations, the
|
|
135
|
+
* structured remote blocks, …) lives on `jobs.retrieve` (`PublicJob`).
|
|
136
|
+
* Transcribed from `serializeJobCard`.
|
|
137
|
+
*/
|
|
138
|
+
interface PublicJobCard {
|
|
139
|
+
id: string;
|
|
140
|
+
object: 'job_card';
|
|
141
|
+
slug: string;
|
|
142
|
+
title: string;
|
|
143
|
+
publishedAt: string | null;
|
|
144
|
+
employmentType: string | null;
|
|
145
|
+
remoteOption: string | null;
|
|
146
|
+
/** Remote-region label for remote jobs (e.g. "Worldwide"); `null` otherwise. */
|
|
147
|
+
remoteLocationLabel: string | null;
|
|
148
|
+
salaryMin: number | null;
|
|
149
|
+
salaryMax: number | null;
|
|
150
|
+
salaryCurrency: string | null;
|
|
151
|
+
salaryTimeframe: string | null;
|
|
152
|
+
isFeatured: boolean;
|
|
153
|
+
locationLabel: string | null;
|
|
154
|
+
company: {
|
|
155
|
+
slug: string;
|
|
156
|
+
name: string;
|
|
157
|
+
logoUrl: string | null;
|
|
158
|
+
} | null;
|
|
159
|
+
categories: Array<{
|
|
160
|
+
slug: string;
|
|
161
|
+
name: string;
|
|
162
|
+
}>;
|
|
163
|
+
skills: Array<{
|
|
164
|
+
slug: string;
|
|
165
|
+
name: string;
|
|
166
|
+
}>;
|
|
167
|
+
links: {
|
|
168
|
+
public: string | null;
|
|
169
|
+
};
|
|
170
|
+
/**
|
|
171
|
+
* Long-form description (HTML), or `null`. Present ONLY when the list/search
|
|
172
|
+
* was called with `fields: '+description'` (the RSS feed's sparse-fieldset
|
|
173
|
+
* opt-in); `undefined`/absent on the default slim card.
|
|
174
|
+
*/
|
|
175
|
+
description?: string | null;
|
|
176
|
+
}
|
|
177
|
+
/** Derived category/skill suggestion on the jobs browse list (ADR-0037 §8). */
|
|
178
|
+
interface RelatedSearch {
|
|
179
|
+
type: 'category' | 'skill';
|
|
180
|
+
slug: string;
|
|
181
|
+
term: string;
|
|
182
|
+
count: number;
|
|
183
|
+
}
|
|
184
|
+
/** The browse list envelope — `PublicJobCard`s + storefront fields + `relatedSearches`. */
|
|
185
|
+
interface JobCardListEnvelope extends ListEnvelope<PublicJobCard> {
|
|
186
|
+
relatedSearches?: RelatedSearch[];
|
|
187
|
+
}
|
|
188
|
+
/** The search envelope — `PublicJobCard`s + storefront fields. */
|
|
189
|
+
type JobCardSearchEnvelope = SearchEnvelope<PublicJobCard>;
|
|
190
|
+
type JobsListQuery = {
|
|
191
|
+
cursor?: string;
|
|
192
|
+
/** 1–100. */
|
|
193
|
+
limit?: number;
|
|
194
|
+
/** Storefront page offset; takes precedence over `cursor`. `offset + limit` ≤ 10,000. */
|
|
195
|
+
offset?: number;
|
|
196
|
+
/** Single or repeated (up to 10) — repeated params are OR-matched. */
|
|
197
|
+
companyId?: string | string[];
|
|
198
|
+
remoteOption?: RemoteOption | RemoteOption[];
|
|
199
|
+
employmentType?: EmploymentType | EmploymentType[];
|
|
200
|
+
seniority?: Seniority | Seniority[];
|
|
201
|
+
/** Place slug for a geo radius search; unresolvable slugs are ignored. */
|
|
202
|
+
location?: string;
|
|
203
|
+
/** Radius in km around `location` (10–250; default 50). */
|
|
204
|
+
radius?: number;
|
|
205
|
+
/** Category slug seed (the `/jobs/[keyword]` page) — server resolves it to the English source name; unresolvable → 404. */
|
|
206
|
+
category?: string;
|
|
207
|
+
/** Skill slug seed (the `/jobs/skills/[skill]` page) — server-resolved; unresolvable → 404. */
|
|
208
|
+
skill?: string;
|
|
209
|
+
/** Sparse fieldset (Medusa-style `+field`). Only `'+description'` is supported — adds `description` to each card. */
|
|
210
|
+
fields?: string;
|
|
211
|
+
};
|
|
212
|
+
type JobsSimilarQuery = {
|
|
213
|
+
/** How many similar jobs to return (1–20; default 5). */
|
|
214
|
+
limit?: number;
|
|
215
|
+
};
|
|
216
|
+
interface JobsSearchBody {
|
|
217
|
+
/** Free-text query, up to 200 characters. */
|
|
218
|
+
query?: string;
|
|
219
|
+
filters?: {
|
|
220
|
+
/** Up to 10 values each. */
|
|
221
|
+
companyId?: string[];
|
|
222
|
+
remoteOption?: RemoteOption[];
|
|
223
|
+
employmentType?: EmploymentType[];
|
|
224
|
+
seniority?: Seniority[];
|
|
225
|
+
/** ISO 8601 datetime bounds. */
|
|
226
|
+
publishedAt?: {
|
|
227
|
+
gte?: string;
|
|
228
|
+
lte?: string;
|
|
229
|
+
};
|
|
230
|
+
/** Place slug for a geo radius search. */
|
|
231
|
+
location?: string;
|
|
232
|
+
/** Radius in km around `location` (10–250; default 50). */
|
|
233
|
+
radius?: number;
|
|
234
|
+
};
|
|
235
|
+
cursor?: string;
|
|
236
|
+
/** 1–100. */
|
|
237
|
+
limit?: number;
|
|
238
|
+
/** Storefront page offset; takes precedence over `cursor`. */
|
|
239
|
+
offset?: number;
|
|
240
|
+
}
|
|
241
|
+
|
|
1
242
|
type Awaitable<T> = T | Promise<T>;
|
|
2
243
|
/**
|
|
3
244
|
* Async token storage. The SDK reads the access token from storage on
|
|
@@ -104,6 +345,27 @@ interface PublicBoard {
|
|
|
104
345
|
theme: PublicBoardTheme | null;
|
|
105
346
|
}
|
|
106
347
|
|
|
348
|
+
/**
|
|
349
|
+
* The public SEO-infra payload (`board.seo()`). The four values a headless
|
|
350
|
+
* frontend rebuilds `robots.txt` / `ads.txt` / `indexnow-key.txt` (+ the Google
|
|
351
|
+
* site-verification `<meta>`) from — byte-identically to the hosted board.
|
|
352
|
+
*/
|
|
353
|
+
interface BoardSeo {
|
|
354
|
+
object: 'board_seo';
|
|
355
|
+
/** Verbatim `ads.txt` content, or `null` when not configured (the handler 404s). */
|
|
356
|
+
adsTxt: string | null;
|
|
357
|
+
/** IndexNow key-file content, or `null` when not configured (the handler 404s). */
|
|
358
|
+
indexNowKey: string | null;
|
|
359
|
+
/** Google site-verification token for the `<meta>` tag, or `null`. */
|
|
360
|
+
googleSiteVerification: string | null;
|
|
361
|
+
/**
|
|
362
|
+
* The board's canonical base URL (honours a configured custom domain). Build
|
|
363
|
+
* the `robots.txt` `Sitemap:` line (`${canonicalBase}/sitemap.xml`) and
|
|
364
|
+
* canonical links from it — NOT the request origin.
|
|
365
|
+
*/
|
|
366
|
+
canonicalBase: string;
|
|
367
|
+
}
|
|
368
|
+
|
|
107
369
|
/**
|
|
108
370
|
* Error raised for every non-2xx Board API response.
|
|
109
371
|
*
|
|
@@ -151,7 +413,7 @@ declare function isConflict(e: unknown): e is BoardApiError;
|
|
|
151
413
|
* constant because the package is platform-neutral and cannot read
|
|
152
414
|
* package.json at runtime.
|
|
153
415
|
*/
|
|
154
|
-
declare const SDK_VERSION = "1.
|
|
416
|
+
declare const SDK_VERSION = "1.2.0";
|
|
155
417
|
|
|
156
418
|
interface BoardUser {
|
|
157
419
|
id: string;
|
|
@@ -202,6 +464,19 @@ interface ResetPasswordBody {
|
|
|
202
464
|
password: string;
|
|
203
465
|
}
|
|
204
466
|
|
|
467
|
+
/**
|
|
468
|
+
* The result of resolving a path against the board's configured redirects
|
|
469
|
+
* (`board.redirects.resolve()`). A headless frontend 308s to `target`, or 404s
|
|
470
|
+
* when it's `null`.
|
|
471
|
+
*/
|
|
472
|
+
interface RedirectResolution {
|
|
473
|
+
object: 'redirect_resolution';
|
|
474
|
+
/** The path that was resolved. */
|
|
475
|
+
path: string;
|
|
476
|
+
/** The redirect target (board-relative), or `null` when no redirect matches. */
|
|
477
|
+
target: string | null;
|
|
478
|
+
}
|
|
479
|
+
|
|
205
480
|
/** Author shape embedded on posts (no `object` discriminator). */
|
|
206
481
|
interface BlogAuthorEmbed {
|
|
207
482
|
id: string;
|
|
@@ -268,27 +543,6 @@ interface BlogSearchBody {
|
|
|
268
543
|
limit?: number;
|
|
269
544
|
}
|
|
270
545
|
|
|
271
|
-
/**
|
|
272
|
-
* Stripe-shaped success envelopes (`01-conventions.md` §5.1). The
|
|
273
|
-
* server MAY add top-level fields; consumers MUST ignore unknown
|
|
274
|
-
* fields.
|
|
275
|
-
*/
|
|
276
|
-
interface ListEnvelope<T> {
|
|
277
|
-
object: 'list';
|
|
278
|
-
url: string;
|
|
279
|
-
hasMore: boolean;
|
|
280
|
-
/** `null` when `hasMore` is false — always present, never undefined. */
|
|
281
|
-
nextCursor: string | null;
|
|
282
|
-
data: T[];
|
|
283
|
-
}
|
|
284
|
-
interface SearchEnvelope<T> {
|
|
285
|
-
object: 'search_result';
|
|
286
|
-
url: string;
|
|
287
|
-
hasMore: boolean;
|
|
288
|
-
nextCursor: string | null;
|
|
289
|
-
data: T[];
|
|
290
|
-
}
|
|
291
|
-
|
|
292
546
|
interface PublicCompany {
|
|
293
547
|
id: string;
|
|
294
548
|
object: 'public_company';
|
|
@@ -321,110 +575,6 @@ interface CompaniesSearchBody {
|
|
|
321
575
|
limit?: number;
|
|
322
576
|
}
|
|
323
577
|
|
|
324
|
-
type RemoteOption = 'on_site' | 'hybrid' | 'remote';
|
|
325
|
-
type EmploymentType = 'full_time' | 'part_time' | 'contract' | 'internship' | 'temporary' | 'volunteer' | 'other';
|
|
326
|
-
type Seniority = 'entry_level' | 'associate' | 'mid_level' | 'senior' | 'lead' | 'principal' | 'director' | 'executive';
|
|
327
|
-
type EducationRequirement = 'high_school' | 'associate_degree' | 'bachelor_degree' | 'professional_certificate' | 'postgraduate_degree' | 'no_requirements';
|
|
328
|
-
interface OfficeLocation {
|
|
329
|
-
countryCode: string | null;
|
|
330
|
-
country: string | null;
|
|
331
|
-
locality: string | null;
|
|
332
|
-
city: string | null;
|
|
333
|
-
region: string | null;
|
|
334
|
-
regionCode: string | null;
|
|
335
|
-
postalCode: string | null;
|
|
336
|
-
displayName: string | null;
|
|
337
|
-
}
|
|
338
|
-
interface JobCompany {
|
|
339
|
-
id: string;
|
|
340
|
-
name: string | null;
|
|
341
|
-
slug: string | null;
|
|
342
|
-
logoUrl: string | null;
|
|
343
|
-
website: string | null;
|
|
344
|
-
}
|
|
345
|
-
interface RemotePermit {
|
|
346
|
-
type: string;
|
|
347
|
-
value: string;
|
|
348
|
-
}
|
|
349
|
-
interface RemoteTimezone {
|
|
350
|
-
type: string;
|
|
351
|
-
value: string;
|
|
352
|
-
plusMinus?: number;
|
|
353
|
-
}
|
|
354
|
-
interface PublicJob {
|
|
355
|
-
id: string;
|
|
356
|
-
object: 'public_job';
|
|
357
|
-
title: string;
|
|
358
|
-
slug: string | null;
|
|
359
|
-
status: string;
|
|
360
|
-
companyId: string | null;
|
|
361
|
-
employmentType: string | null;
|
|
362
|
-
remoteOption: string | null;
|
|
363
|
-
seniority: string | null;
|
|
364
|
-
salaryMin: number | null;
|
|
365
|
-
salaryMax: number | null;
|
|
366
|
-
salaryCurrency: string | null;
|
|
367
|
-
salaryTimeframe: string | null;
|
|
368
|
-
isFeatured: boolean;
|
|
369
|
-
publishedAt: string | null;
|
|
370
|
-
expiresAt: string | null;
|
|
371
|
-
createdAt: string;
|
|
372
|
-
updatedAt: string;
|
|
373
|
-
description: string | null;
|
|
374
|
-
applicationUrl: string | null;
|
|
375
|
-
/** Canonical hierarchical permit selection. */
|
|
376
|
-
remotePermits: RemotePermit[];
|
|
377
|
-
/** Read-only — derived from `remotePermits`. */
|
|
378
|
-
remoteWorldwide: boolean | null;
|
|
379
|
-
/** Canonical hierarchical timezone selection. */
|
|
380
|
-
remoteTimezones: RemoteTimezone[];
|
|
381
|
-
/** Read-only — derived from `remoteTimezones`. */
|
|
382
|
-
remoteAllowedTzOffsets: number[];
|
|
383
|
-
/** Read-only — derived from `remotePermits`. */
|
|
384
|
-
remoteWorkPermitCountryCodes: string[];
|
|
385
|
-
/** Read-only — derived from `remotePermits`. */
|
|
386
|
-
remoteWorkPermitSubdivisionCodes: string[];
|
|
387
|
-
remoteSponsorship: 'yes' | 'no' | 'unknown';
|
|
388
|
-
educationRequirements: EducationRequirement[];
|
|
389
|
-
experienceMonths: number | null;
|
|
390
|
-
experienceInPlaceOfEducation: boolean | null;
|
|
391
|
-
inOfficePeriod: 'per_week' | 'per_month' | 'per_year' | null;
|
|
392
|
-
inOfficeFrequency: number | null;
|
|
393
|
-
company: JobCompany | null;
|
|
394
|
-
officeLocations: OfficeLocation[];
|
|
395
|
-
links: {
|
|
396
|
-
public: string | null;
|
|
397
|
-
};
|
|
398
|
-
}
|
|
399
|
-
type JobsListQuery = {
|
|
400
|
-
cursor?: string;
|
|
401
|
-
/** 1–100. */
|
|
402
|
-
limit?: number;
|
|
403
|
-
companyId?: string;
|
|
404
|
-
remoteOption?: RemoteOption;
|
|
405
|
-
employmentType?: EmploymentType;
|
|
406
|
-
seniority?: Seniority;
|
|
407
|
-
};
|
|
408
|
-
interface JobsSearchBody {
|
|
409
|
-
/** Free-text query, up to 200 characters. */
|
|
410
|
-
query?: string;
|
|
411
|
-
filters?: {
|
|
412
|
-
/** Up to 10 values each. */
|
|
413
|
-
companyId?: string[];
|
|
414
|
-
remoteOption?: RemoteOption[];
|
|
415
|
-
employmentType?: EmploymentType[];
|
|
416
|
-
seniority?: Seniority[];
|
|
417
|
-
/** ISO 8601 datetime bounds. */
|
|
418
|
-
publishedAt?: {
|
|
419
|
-
gte?: string;
|
|
420
|
-
lte?: string;
|
|
421
|
-
};
|
|
422
|
-
};
|
|
423
|
-
cursor?: string;
|
|
424
|
-
/** 1–100. */
|
|
425
|
-
limit?: number;
|
|
426
|
-
}
|
|
427
|
-
|
|
428
578
|
/**
|
|
429
579
|
* The embedded `job` is the SAME `public_job` shape the anonymous jobs
|
|
430
580
|
* list emits — saved rows and search rows render with one component.
|
|
@@ -446,6 +596,53 @@ interface SaveJobBody {
|
|
|
446
596
|
jobId: string;
|
|
447
597
|
}
|
|
448
598
|
|
|
599
|
+
interface TaxonomyGeo {
|
|
600
|
+
lat: number | null;
|
|
601
|
+
lng: number | null;
|
|
602
|
+
countryCode: string | null;
|
|
603
|
+
regionCode: string | null;
|
|
604
|
+
region: string | null;
|
|
605
|
+
city: string | null;
|
|
606
|
+
locality: string | null;
|
|
607
|
+
placeType: string | null;
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* Page-meta for a resolved taxonomy slug. `sourceSlug` is the immutable
|
|
611
|
+
* English search key; `canonicalSlug` is the board-language URL; `redirectTo`
|
|
612
|
+
* is the canonical slug to 308 to when the inbound slug isn't canonical (the
|
|
613
|
+
* host app emits the redirect — the SDK never navigates). `geo` is set for
|
|
614
|
+
* place resolutions only.
|
|
615
|
+
*/
|
|
616
|
+
interface TaxonomyResolution {
|
|
617
|
+
object: 'taxonomy_resolution';
|
|
618
|
+
type: 'category' | 'skill' | 'place';
|
|
619
|
+
sourceSlug: string;
|
|
620
|
+
canonicalSlug: string;
|
|
621
|
+
displayName: string;
|
|
622
|
+
redirectTo: string | null;
|
|
623
|
+
geo: TaxonomyGeo | null;
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* A place in the board's locations directory (`taxonomy.places.list()` →
|
|
627
|
+
* `GET /places`), the data the `/jobs/locations/` index renders. `jobCount` is
|
|
628
|
+
* subtree-summed (a parent counts its descendants); `id`/`parentId` carry the
|
|
629
|
+
* hierarchy so consumers rebuild the same nested tree the hosted index shows.
|
|
630
|
+
*/
|
|
631
|
+
interface PublicPlace {
|
|
632
|
+
object: 'place';
|
|
633
|
+
/** Stable place identity (locations-tree edge endpoint). */
|
|
634
|
+
id: string;
|
|
635
|
+
/** Parent place's `id`; `null` for a root place. */
|
|
636
|
+
parentId: string | null;
|
|
637
|
+
/** Public slug (links to `/jobs/locations/:slug`); `null` if unslugged. */
|
|
638
|
+
slug: string | null;
|
|
639
|
+
name: string;
|
|
640
|
+
placeType: string;
|
|
641
|
+
countryCode: string | null;
|
|
642
|
+
regionCode: string | null;
|
|
643
|
+
jobCount: number;
|
|
644
|
+
}
|
|
645
|
+
|
|
449
646
|
interface CreateBoardClientOptions {
|
|
450
647
|
baseUrl: string;
|
|
451
648
|
/** Board identifier: `pk_…` key (provisioned default) | `boards_…` ID | slug. */
|
|
@@ -489,16 +686,26 @@ declare function createBoardClient(options: CreateBoardClientOptions): {
|
|
|
489
686
|
* const { name, theme } = await board.context();
|
|
490
687
|
*/
|
|
491
688
|
context(options?: FetchOptions): Promise<PublicBoard>;
|
|
689
|
+
/**
|
|
690
|
+
* Board SEO infra — `ads.txt`, IndexNow key, Google site-verification, and
|
|
691
|
+
* the canonical base URL. Rebuild `robots.txt` / `ads.txt` /
|
|
692
|
+
* `indexnow-key.txt` from it.
|
|
693
|
+
*
|
|
694
|
+
* @example
|
|
695
|
+
* const { adsTxt, canonicalBase } = await board.seo();
|
|
696
|
+
*/
|
|
697
|
+
seo(options?: FetchOptions): Promise<BoardSeo>;
|
|
492
698
|
jobs: {
|
|
493
|
-
list(query?: JobsListQuery, options?: FetchOptions): Promise<
|
|
699
|
+
list(query?: JobsListQuery, options?: FetchOptions): Promise<JobCardListEnvelope>;
|
|
494
700
|
retrieve(jobSlug: string, query?: Record<string, never>, options?: FetchOptions): Promise<PublicJob>;
|
|
495
|
-
search(body: JobsSearchBody, query?: Record<string, never>, options?: FetchOptions): Promise<
|
|
701
|
+
search(body: JobsSearchBody, query?: Record<string, never>, options?: FetchOptions): Promise<JobCardSearchEnvelope>;
|
|
702
|
+
similar(jobSlug: string, query?: JobsSimilarQuery, options?: FetchOptions): Promise<ListEnvelope<PublicJobCard>>;
|
|
496
703
|
};
|
|
497
704
|
companies: {
|
|
498
705
|
list(query?: CompaniesListQuery, options?: FetchOptions): Promise<ListEnvelope<PublicCompany>>;
|
|
499
706
|
retrieve(companySlug: string, query?: Record<string, never>, options?: FetchOptions): Promise<PublicCompany>;
|
|
500
707
|
search(body: CompaniesSearchBody, query?: Record<string, never>, options?: FetchOptions): Promise<SearchEnvelope<PublicCompany>>;
|
|
501
|
-
listJobs(companySlug: string, query?: CompanyJobsListQuery, options?: FetchOptions): Promise<
|
|
708
|
+
listJobs(companySlug: string, query?: CompanyJobsListQuery, options?: FetchOptions): Promise<JobCardListEnvelope>;
|
|
502
709
|
};
|
|
503
710
|
blog: {
|
|
504
711
|
posts: {
|
|
@@ -532,7 +739,22 @@ declare function createBoardClient(options: CreateBoardClientOptions): {
|
|
|
532
739
|
unsave(jobId: string, query?: Record<string, never>, options?: FetchOptions): Promise<void>;
|
|
533
740
|
};
|
|
534
741
|
};
|
|
742
|
+
taxonomy: {
|
|
743
|
+
categories: {
|
|
744
|
+
resolve(slug: string, options?: FetchOptions): Promise<TaxonomyResolution>;
|
|
745
|
+
};
|
|
746
|
+
skills: {
|
|
747
|
+
resolve(slug: string, options?: FetchOptions): Promise<TaxonomyResolution>;
|
|
748
|
+
};
|
|
749
|
+
places: {
|
|
750
|
+
list(options?: FetchOptions): Promise<ListEnvelope<PublicPlace>>;
|
|
751
|
+
resolve(slug: string, options?: FetchOptions): Promise<TaxonomyResolution>;
|
|
752
|
+
};
|
|
753
|
+
};
|
|
754
|
+
redirects: {
|
|
755
|
+
resolve(path: string, options?: FetchOptions): Promise<RedirectResolution>;
|
|
756
|
+
};
|
|
535
757
|
};
|
|
536
758
|
type BoardSdk = ReturnType<typeof createBoardClient>;
|
|
537
759
|
|
|
538
|
-
export { ACCESS_TOKEN_KEY, type Awaitable, type BlogAuthorEmbed, type BlogPostsListQuery, type BlogSearchBody, type BlogTagEmbed, BoardApiError, type BoardAuthSession, BoardClient, type BoardRequest, type BoardSdk, type BoardUser, type CompaniesListQuery, type CompaniesSearchBody, type CompanyJobsListQuery, type CreateBoardClientOptions, type CustomStorage, type EducationRequirement, type EmploymentType, type FetchOptions, type ForgotPasswordBody, type JobCompany, type JobsListQuery, type JobsSearchBody, type ListEnvelope, type Logger, type LoginBody, type LogoutBody, type OfficeLocation, type PublicBlogAuthor, type PublicBlogPost, type PublicBlogPostSummary, type PublicBlogTag, type PublicBoard, type PublicBoardAnalytics, type PublicBoardFeatures, type PublicBoardTheme, type PublicCompany, type PublicJob, REFRESH_TOKEN_KEY, type RefreshBody, type RegisterBody, type RemoteOption, type RemotePermit, type RemoteTimezone, type ResetPasswordBody, SDK_VERSION, type SaveJobBody, type SavedJob, type SavedJobsListQuery, type SearchEnvelope, type Seniority, type StorageMode, type VerifyEmailBody, createBoardClient, isBoardApiError, isConflict, isForbidden, isNotFound, isRateLimited, isUnauthorized, isValidationError };
|
|
760
|
+
export { ACCESS_TOKEN_KEY, type Awaitable, type BlogAuthorEmbed, type BlogPostsListQuery, type BlogSearchBody, type BlogTagEmbed, BoardApiError, type BoardAuthSession, BoardClient, type BoardRequest, type BoardSdk, type BoardSeo, type BoardUser, type CompaniesListQuery, type CompaniesSearchBody, type CompanyJobsListQuery, type CreateBoardClientOptions, type CustomStorage, type EducationRequirement, type EmploymentType, type FetchOptions, type ForgotPasswordBody, type JobCardListEnvelope, type JobCardSearchEnvelope, type JobCompany, type JobsListQuery, type JobsSearchBody, type ListEnvelope, type Logger, type LoginBody, type LogoutBody, type OfficeLocation, type PublicBlogAuthor, type PublicBlogPost, type PublicBlogPostSummary, type PublicBlogTag, type PublicBoard, type PublicBoardAnalytics, type PublicBoardFeatures, type PublicBoardTheme, type PublicCompany, type PublicJob, type PublicJobCard, type PublicPlace, REFRESH_TOKEN_KEY, type RedirectResolution, type RefreshBody, type RegisterBody, type RelatedSearch, type RemoteOption, type RemotePermit, type RemoteTimezone, type ResetPasswordBody, SDK_VERSION, type SaveJobBody, type SavedJob, type SavedJobsListQuery, type SearchEnvelope, type Seniority, type StorageMode, type StorefrontPagination, type TaxonomyGeo, type TaxonomyResolution, type VerifyEmailBody, createBoardClient, isBoardApiError, isConflict, isForbidden, isNotFound, isRateLimited, isUnauthorized, isValidationError };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stripe-shaped success envelopes (`01-conventions.md` §5.1). The
|
|
3
|
+
* server MAY add top-level fields; consumers MUST ignore unknown
|
|
4
|
+
* fields.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Medusa-style storefront pagination fields (ADR-0037 §7), populated only by
|
|
8
|
+
* the board jobs catalog reads (browse / search / company-jobs); absent on
|
|
9
|
+
* every other list/search. `nextCursor` is preserved alongside (offset-encoded).
|
|
10
|
+
*/
|
|
11
|
+
interface StorefrontPagination {
|
|
12
|
+
/** Total matching results ("X jobs"). */
|
|
13
|
+
count?: number;
|
|
14
|
+
/** The page size used for this response. */
|
|
15
|
+
limit?: number;
|
|
16
|
+
/** Number of items skipped before this page. */
|
|
17
|
+
offset?: number;
|
|
18
|
+
/** Items hidden behind the candidate paywall for the current viewer; absent/0 when entitled. */
|
|
19
|
+
gatedCount?: number;
|
|
20
|
+
}
|
|
21
|
+
interface ListEnvelope<T> extends StorefrontPagination {
|
|
22
|
+
object: 'list';
|
|
23
|
+
url: string;
|
|
24
|
+
hasMore: boolean;
|
|
25
|
+
/** `null` when `hasMore` is false — always present, never undefined. */
|
|
26
|
+
nextCursor: string | null;
|
|
27
|
+
data: T[];
|
|
28
|
+
}
|
|
29
|
+
interface SearchEnvelope<T> extends StorefrontPagination {
|
|
30
|
+
object: 'search_result';
|
|
31
|
+
url: string;
|
|
32
|
+
hasMore: boolean;
|
|
33
|
+
nextCursor: string | null;
|
|
34
|
+
data: T[];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
type RemoteOption = 'on_site' | 'hybrid' | 'remote';
|
|
38
|
+
type EmploymentType = 'full_time' | 'part_time' | 'contract' | 'internship' | 'temporary' | 'volunteer' | 'other';
|
|
39
|
+
type Seniority = 'entry_level' | 'associate' | 'mid_level' | 'senior' | 'lead' | 'principal' | 'director' | 'executive';
|
|
40
|
+
type EducationRequirement = 'high_school' | 'associate_degree' | 'bachelor_degree' | 'professional_certificate' | 'postgraduate_degree' | 'no_requirements';
|
|
41
|
+
interface OfficeLocation {
|
|
42
|
+
countryCode: string | null;
|
|
43
|
+
country: string | null;
|
|
44
|
+
locality: string | null;
|
|
45
|
+
city: string | null;
|
|
46
|
+
region: string | null;
|
|
47
|
+
regionCode: string | null;
|
|
48
|
+
postalCode: string | null;
|
|
49
|
+
displayName: string | null;
|
|
50
|
+
}
|
|
51
|
+
interface JobCompany {
|
|
52
|
+
id: string;
|
|
53
|
+
name: string | null;
|
|
54
|
+
slug: string | null;
|
|
55
|
+
logoUrl: string | null;
|
|
56
|
+
website: string | null;
|
|
57
|
+
}
|
|
58
|
+
interface RemotePermit {
|
|
59
|
+
type: string;
|
|
60
|
+
value: string;
|
|
61
|
+
}
|
|
62
|
+
interface RemoteTimezone {
|
|
63
|
+
type: string;
|
|
64
|
+
value: string;
|
|
65
|
+
plusMinus?: number;
|
|
66
|
+
}
|
|
67
|
+
interface PublicJob {
|
|
68
|
+
id: string;
|
|
69
|
+
object: 'public_job';
|
|
70
|
+
title: string;
|
|
71
|
+
slug: string | null;
|
|
72
|
+
status: string;
|
|
73
|
+
companyId: string | null;
|
|
74
|
+
employmentType: string | null;
|
|
75
|
+
remoteOption: string | null;
|
|
76
|
+
seniority: string | null;
|
|
77
|
+
salaryMin: number | null;
|
|
78
|
+
salaryMax: number | null;
|
|
79
|
+
salaryCurrency: string | null;
|
|
80
|
+
salaryTimeframe: string | null;
|
|
81
|
+
isFeatured: boolean;
|
|
82
|
+
publishedAt: string | null;
|
|
83
|
+
expiresAt: string | null;
|
|
84
|
+
createdAt: string;
|
|
85
|
+
updatedAt: string;
|
|
86
|
+
description: string | null;
|
|
87
|
+
applicationUrl: string | null;
|
|
88
|
+
/** Canonical hierarchical permit selection. */
|
|
89
|
+
remotePermits: RemotePermit[];
|
|
90
|
+
/** Read-only — derived from `remotePermits`. */
|
|
91
|
+
remoteWorldwide: boolean | null;
|
|
92
|
+
/** Canonical hierarchical timezone selection. */
|
|
93
|
+
remoteTimezones: RemoteTimezone[];
|
|
94
|
+
/** Read-only — derived from `remoteTimezones`. */
|
|
95
|
+
remoteAllowedTzOffsets: number[];
|
|
96
|
+
/** Read-only — derived from `remotePermits`. */
|
|
97
|
+
remoteWorkPermitCountryCodes: string[];
|
|
98
|
+
/** Read-only — derived from `remotePermits`. */
|
|
99
|
+
remoteWorkPermitSubdivisionCodes: string[];
|
|
100
|
+
remoteSponsorship: 'yes' | 'no' | 'unknown';
|
|
101
|
+
educationRequirements: EducationRequirement[];
|
|
102
|
+
experienceMonths: number | null;
|
|
103
|
+
experienceInPlaceOfEducation: boolean | null;
|
|
104
|
+
inOfficePeriod: 'per_week' | 'per_month' | 'per_year' | null;
|
|
105
|
+
inOfficeFrequency: number | null;
|
|
106
|
+
company: JobCompany | null;
|
|
107
|
+
officeLocations: OfficeLocation[];
|
|
108
|
+
/** Resolved taxonomy — same `{slug,name}` shape as `PublicJobCard`; names joined server-side. */
|
|
109
|
+
categories: Array<{
|
|
110
|
+
slug: string;
|
|
111
|
+
name: string;
|
|
112
|
+
}>;
|
|
113
|
+
skills: Array<{
|
|
114
|
+
slug: string;
|
|
115
|
+
name: string;
|
|
116
|
+
}>;
|
|
117
|
+
/**
|
|
118
|
+
* Place ancestor chain (country → region → city) for the breadcrumb. Each
|
|
119
|
+
* `slug` is the English SOURCE slug — the key `taxonomy.places.resolve()`
|
|
120
|
+
* accepts. On a localized board it may differ from the canonical
|
|
121
|
+
* board-language URL slug (the board router 308-redirects the source slug to
|
|
122
|
+
* the canonical one); link via `/jobs/locations/:slug`.
|
|
123
|
+
*/
|
|
124
|
+
placeHierarchy: Array<{
|
|
125
|
+
slug: string;
|
|
126
|
+
name: string;
|
|
127
|
+
}>;
|
|
128
|
+
links: {
|
|
129
|
+
public: string | null;
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* The slim listing card (ADR-0037 §1) returned by `jobs.list` / `jobs.search`
|
|
134
|
+
* / `companies.listJobs`. Full enrichment (description, officeLocations, the
|
|
135
|
+
* structured remote blocks, …) lives on `jobs.retrieve` (`PublicJob`).
|
|
136
|
+
* Transcribed from `serializeJobCard`.
|
|
137
|
+
*/
|
|
138
|
+
interface PublicJobCard {
|
|
139
|
+
id: string;
|
|
140
|
+
object: 'job_card';
|
|
141
|
+
slug: string;
|
|
142
|
+
title: string;
|
|
143
|
+
publishedAt: string | null;
|
|
144
|
+
employmentType: string | null;
|
|
145
|
+
remoteOption: string | null;
|
|
146
|
+
/** Remote-region label for remote jobs (e.g. "Worldwide"); `null` otherwise. */
|
|
147
|
+
remoteLocationLabel: string | null;
|
|
148
|
+
salaryMin: number | null;
|
|
149
|
+
salaryMax: number | null;
|
|
150
|
+
salaryCurrency: string | null;
|
|
151
|
+
salaryTimeframe: string | null;
|
|
152
|
+
isFeatured: boolean;
|
|
153
|
+
locationLabel: string | null;
|
|
154
|
+
company: {
|
|
155
|
+
slug: string;
|
|
156
|
+
name: string;
|
|
157
|
+
logoUrl: string | null;
|
|
158
|
+
} | null;
|
|
159
|
+
categories: Array<{
|
|
160
|
+
slug: string;
|
|
161
|
+
name: string;
|
|
162
|
+
}>;
|
|
163
|
+
skills: Array<{
|
|
164
|
+
slug: string;
|
|
165
|
+
name: string;
|
|
166
|
+
}>;
|
|
167
|
+
links: {
|
|
168
|
+
public: string | null;
|
|
169
|
+
};
|
|
170
|
+
/**
|
|
171
|
+
* Long-form description (HTML), or `null`. Present ONLY when the list/search
|
|
172
|
+
* was called with `fields: '+description'` (the RSS feed's sparse-fieldset
|
|
173
|
+
* opt-in); `undefined`/absent on the default slim card.
|
|
174
|
+
*/
|
|
175
|
+
description?: string | null;
|
|
176
|
+
}
|
|
177
|
+
/** Derived category/skill suggestion on the jobs browse list (ADR-0037 §8). */
|
|
178
|
+
interface RelatedSearch {
|
|
179
|
+
type: 'category' | 'skill';
|
|
180
|
+
slug: string;
|
|
181
|
+
term: string;
|
|
182
|
+
count: number;
|
|
183
|
+
}
|
|
184
|
+
/** The browse list envelope — `PublicJobCard`s + storefront fields + `relatedSearches`. */
|
|
185
|
+
interface JobCardListEnvelope extends ListEnvelope<PublicJobCard> {
|
|
186
|
+
relatedSearches?: RelatedSearch[];
|
|
187
|
+
}
|
|
188
|
+
/** The search envelope — `PublicJobCard`s + storefront fields. */
|
|
189
|
+
type JobCardSearchEnvelope = SearchEnvelope<PublicJobCard>;
|
|
190
|
+
type JobsListQuery = {
|
|
191
|
+
cursor?: string;
|
|
192
|
+
/** 1–100. */
|
|
193
|
+
limit?: number;
|
|
194
|
+
/** Storefront page offset; takes precedence over `cursor`. `offset + limit` ≤ 10,000. */
|
|
195
|
+
offset?: number;
|
|
196
|
+
/** Single or repeated (up to 10) — repeated params are OR-matched. */
|
|
197
|
+
companyId?: string | string[];
|
|
198
|
+
remoteOption?: RemoteOption | RemoteOption[];
|
|
199
|
+
employmentType?: EmploymentType | EmploymentType[];
|
|
200
|
+
seniority?: Seniority | Seniority[];
|
|
201
|
+
/** Place slug for a geo radius search; unresolvable slugs are ignored. */
|
|
202
|
+
location?: string;
|
|
203
|
+
/** Radius in km around `location` (10–250; default 50). */
|
|
204
|
+
radius?: number;
|
|
205
|
+
/** Category slug seed (the `/jobs/[keyword]` page) — server resolves it to the English source name; unresolvable → 404. */
|
|
206
|
+
category?: string;
|
|
207
|
+
/** Skill slug seed (the `/jobs/skills/[skill]` page) — server-resolved; unresolvable → 404. */
|
|
208
|
+
skill?: string;
|
|
209
|
+
/** Sparse fieldset (Medusa-style `+field`). Only `'+description'` is supported — adds `description` to each card. */
|
|
210
|
+
fields?: string;
|
|
211
|
+
};
|
|
212
|
+
type JobsSimilarQuery = {
|
|
213
|
+
/** How many similar jobs to return (1–20; default 5). */
|
|
214
|
+
limit?: number;
|
|
215
|
+
};
|
|
216
|
+
interface JobsSearchBody {
|
|
217
|
+
/** Free-text query, up to 200 characters. */
|
|
218
|
+
query?: string;
|
|
219
|
+
filters?: {
|
|
220
|
+
/** Up to 10 values each. */
|
|
221
|
+
companyId?: string[];
|
|
222
|
+
remoteOption?: RemoteOption[];
|
|
223
|
+
employmentType?: EmploymentType[];
|
|
224
|
+
seniority?: Seniority[];
|
|
225
|
+
/** ISO 8601 datetime bounds. */
|
|
226
|
+
publishedAt?: {
|
|
227
|
+
gte?: string;
|
|
228
|
+
lte?: string;
|
|
229
|
+
};
|
|
230
|
+
/** Place slug for a geo radius search. */
|
|
231
|
+
location?: string;
|
|
232
|
+
/** Radius in km around `location` (10–250; default 50). */
|
|
233
|
+
radius?: number;
|
|
234
|
+
};
|
|
235
|
+
cursor?: string;
|
|
236
|
+
/** 1–100. */
|
|
237
|
+
limit?: number;
|
|
238
|
+
/** Storefront page offset; takes precedence over `cursor`. */
|
|
239
|
+
offset?: number;
|
|
240
|
+
}
|
|
241
|
+
|
|
1
242
|
type Awaitable<T> = T | Promise<T>;
|
|
2
243
|
/**
|
|
3
244
|
* Async token storage. The SDK reads the access token from storage on
|
|
@@ -104,6 +345,27 @@ interface PublicBoard {
|
|
|
104
345
|
theme: PublicBoardTheme | null;
|
|
105
346
|
}
|
|
106
347
|
|
|
348
|
+
/**
|
|
349
|
+
* The public SEO-infra payload (`board.seo()`). The four values a headless
|
|
350
|
+
* frontend rebuilds `robots.txt` / `ads.txt` / `indexnow-key.txt` (+ the Google
|
|
351
|
+
* site-verification `<meta>`) from — byte-identically to the hosted board.
|
|
352
|
+
*/
|
|
353
|
+
interface BoardSeo {
|
|
354
|
+
object: 'board_seo';
|
|
355
|
+
/** Verbatim `ads.txt` content, or `null` when not configured (the handler 404s). */
|
|
356
|
+
adsTxt: string | null;
|
|
357
|
+
/** IndexNow key-file content, or `null` when not configured (the handler 404s). */
|
|
358
|
+
indexNowKey: string | null;
|
|
359
|
+
/** Google site-verification token for the `<meta>` tag, or `null`. */
|
|
360
|
+
googleSiteVerification: string | null;
|
|
361
|
+
/**
|
|
362
|
+
* The board's canonical base URL (honours a configured custom domain). Build
|
|
363
|
+
* the `robots.txt` `Sitemap:` line (`${canonicalBase}/sitemap.xml`) and
|
|
364
|
+
* canonical links from it — NOT the request origin.
|
|
365
|
+
*/
|
|
366
|
+
canonicalBase: string;
|
|
367
|
+
}
|
|
368
|
+
|
|
107
369
|
/**
|
|
108
370
|
* Error raised for every non-2xx Board API response.
|
|
109
371
|
*
|
|
@@ -151,7 +413,7 @@ declare function isConflict(e: unknown): e is BoardApiError;
|
|
|
151
413
|
* constant because the package is platform-neutral and cannot read
|
|
152
414
|
* package.json at runtime.
|
|
153
415
|
*/
|
|
154
|
-
declare const SDK_VERSION = "1.
|
|
416
|
+
declare const SDK_VERSION = "1.2.0";
|
|
155
417
|
|
|
156
418
|
interface BoardUser {
|
|
157
419
|
id: string;
|
|
@@ -202,6 +464,19 @@ interface ResetPasswordBody {
|
|
|
202
464
|
password: string;
|
|
203
465
|
}
|
|
204
466
|
|
|
467
|
+
/**
|
|
468
|
+
* The result of resolving a path against the board's configured redirects
|
|
469
|
+
* (`board.redirects.resolve()`). A headless frontend 308s to `target`, or 404s
|
|
470
|
+
* when it's `null`.
|
|
471
|
+
*/
|
|
472
|
+
interface RedirectResolution {
|
|
473
|
+
object: 'redirect_resolution';
|
|
474
|
+
/** The path that was resolved. */
|
|
475
|
+
path: string;
|
|
476
|
+
/** The redirect target (board-relative), or `null` when no redirect matches. */
|
|
477
|
+
target: string | null;
|
|
478
|
+
}
|
|
479
|
+
|
|
205
480
|
/** Author shape embedded on posts (no `object` discriminator). */
|
|
206
481
|
interface BlogAuthorEmbed {
|
|
207
482
|
id: string;
|
|
@@ -268,27 +543,6 @@ interface BlogSearchBody {
|
|
|
268
543
|
limit?: number;
|
|
269
544
|
}
|
|
270
545
|
|
|
271
|
-
/**
|
|
272
|
-
* Stripe-shaped success envelopes (`01-conventions.md` §5.1). The
|
|
273
|
-
* server MAY add top-level fields; consumers MUST ignore unknown
|
|
274
|
-
* fields.
|
|
275
|
-
*/
|
|
276
|
-
interface ListEnvelope<T> {
|
|
277
|
-
object: 'list';
|
|
278
|
-
url: string;
|
|
279
|
-
hasMore: boolean;
|
|
280
|
-
/** `null` when `hasMore` is false — always present, never undefined. */
|
|
281
|
-
nextCursor: string | null;
|
|
282
|
-
data: T[];
|
|
283
|
-
}
|
|
284
|
-
interface SearchEnvelope<T> {
|
|
285
|
-
object: 'search_result';
|
|
286
|
-
url: string;
|
|
287
|
-
hasMore: boolean;
|
|
288
|
-
nextCursor: string | null;
|
|
289
|
-
data: T[];
|
|
290
|
-
}
|
|
291
|
-
|
|
292
546
|
interface PublicCompany {
|
|
293
547
|
id: string;
|
|
294
548
|
object: 'public_company';
|
|
@@ -321,110 +575,6 @@ interface CompaniesSearchBody {
|
|
|
321
575
|
limit?: number;
|
|
322
576
|
}
|
|
323
577
|
|
|
324
|
-
type RemoteOption = 'on_site' | 'hybrid' | 'remote';
|
|
325
|
-
type EmploymentType = 'full_time' | 'part_time' | 'contract' | 'internship' | 'temporary' | 'volunteer' | 'other';
|
|
326
|
-
type Seniority = 'entry_level' | 'associate' | 'mid_level' | 'senior' | 'lead' | 'principal' | 'director' | 'executive';
|
|
327
|
-
type EducationRequirement = 'high_school' | 'associate_degree' | 'bachelor_degree' | 'professional_certificate' | 'postgraduate_degree' | 'no_requirements';
|
|
328
|
-
interface OfficeLocation {
|
|
329
|
-
countryCode: string | null;
|
|
330
|
-
country: string | null;
|
|
331
|
-
locality: string | null;
|
|
332
|
-
city: string | null;
|
|
333
|
-
region: string | null;
|
|
334
|
-
regionCode: string | null;
|
|
335
|
-
postalCode: string | null;
|
|
336
|
-
displayName: string | null;
|
|
337
|
-
}
|
|
338
|
-
interface JobCompany {
|
|
339
|
-
id: string;
|
|
340
|
-
name: string | null;
|
|
341
|
-
slug: string | null;
|
|
342
|
-
logoUrl: string | null;
|
|
343
|
-
website: string | null;
|
|
344
|
-
}
|
|
345
|
-
interface RemotePermit {
|
|
346
|
-
type: string;
|
|
347
|
-
value: string;
|
|
348
|
-
}
|
|
349
|
-
interface RemoteTimezone {
|
|
350
|
-
type: string;
|
|
351
|
-
value: string;
|
|
352
|
-
plusMinus?: number;
|
|
353
|
-
}
|
|
354
|
-
interface PublicJob {
|
|
355
|
-
id: string;
|
|
356
|
-
object: 'public_job';
|
|
357
|
-
title: string;
|
|
358
|
-
slug: string | null;
|
|
359
|
-
status: string;
|
|
360
|
-
companyId: string | null;
|
|
361
|
-
employmentType: string | null;
|
|
362
|
-
remoteOption: string | null;
|
|
363
|
-
seniority: string | null;
|
|
364
|
-
salaryMin: number | null;
|
|
365
|
-
salaryMax: number | null;
|
|
366
|
-
salaryCurrency: string | null;
|
|
367
|
-
salaryTimeframe: string | null;
|
|
368
|
-
isFeatured: boolean;
|
|
369
|
-
publishedAt: string | null;
|
|
370
|
-
expiresAt: string | null;
|
|
371
|
-
createdAt: string;
|
|
372
|
-
updatedAt: string;
|
|
373
|
-
description: string | null;
|
|
374
|
-
applicationUrl: string | null;
|
|
375
|
-
/** Canonical hierarchical permit selection. */
|
|
376
|
-
remotePermits: RemotePermit[];
|
|
377
|
-
/** Read-only — derived from `remotePermits`. */
|
|
378
|
-
remoteWorldwide: boolean | null;
|
|
379
|
-
/** Canonical hierarchical timezone selection. */
|
|
380
|
-
remoteTimezones: RemoteTimezone[];
|
|
381
|
-
/** Read-only — derived from `remoteTimezones`. */
|
|
382
|
-
remoteAllowedTzOffsets: number[];
|
|
383
|
-
/** Read-only — derived from `remotePermits`. */
|
|
384
|
-
remoteWorkPermitCountryCodes: string[];
|
|
385
|
-
/** Read-only — derived from `remotePermits`. */
|
|
386
|
-
remoteWorkPermitSubdivisionCodes: string[];
|
|
387
|
-
remoteSponsorship: 'yes' | 'no' | 'unknown';
|
|
388
|
-
educationRequirements: EducationRequirement[];
|
|
389
|
-
experienceMonths: number | null;
|
|
390
|
-
experienceInPlaceOfEducation: boolean | null;
|
|
391
|
-
inOfficePeriod: 'per_week' | 'per_month' | 'per_year' | null;
|
|
392
|
-
inOfficeFrequency: number | null;
|
|
393
|
-
company: JobCompany | null;
|
|
394
|
-
officeLocations: OfficeLocation[];
|
|
395
|
-
links: {
|
|
396
|
-
public: string | null;
|
|
397
|
-
};
|
|
398
|
-
}
|
|
399
|
-
type JobsListQuery = {
|
|
400
|
-
cursor?: string;
|
|
401
|
-
/** 1–100. */
|
|
402
|
-
limit?: number;
|
|
403
|
-
companyId?: string;
|
|
404
|
-
remoteOption?: RemoteOption;
|
|
405
|
-
employmentType?: EmploymentType;
|
|
406
|
-
seniority?: Seniority;
|
|
407
|
-
};
|
|
408
|
-
interface JobsSearchBody {
|
|
409
|
-
/** Free-text query, up to 200 characters. */
|
|
410
|
-
query?: string;
|
|
411
|
-
filters?: {
|
|
412
|
-
/** Up to 10 values each. */
|
|
413
|
-
companyId?: string[];
|
|
414
|
-
remoteOption?: RemoteOption[];
|
|
415
|
-
employmentType?: EmploymentType[];
|
|
416
|
-
seniority?: Seniority[];
|
|
417
|
-
/** ISO 8601 datetime bounds. */
|
|
418
|
-
publishedAt?: {
|
|
419
|
-
gte?: string;
|
|
420
|
-
lte?: string;
|
|
421
|
-
};
|
|
422
|
-
};
|
|
423
|
-
cursor?: string;
|
|
424
|
-
/** 1–100. */
|
|
425
|
-
limit?: number;
|
|
426
|
-
}
|
|
427
|
-
|
|
428
578
|
/**
|
|
429
579
|
* The embedded `job` is the SAME `public_job` shape the anonymous jobs
|
|
430
580
|
* list emits — saved rows and search rows render with one component.
|
|
@@ -446,6 +596,53 @@ interface SaveJobBody {
|
|
|
446
596
|
jobId: string;
|
|
447
597
|
}
|
|
448
598
|
|
|
599
|
+
interface TaxonomyGeo {
|
|
600
|
+
lat: number | null;
|
|
601
|
+
lng: number | null;
|
|
602
|
+
countryCode: string | null;
|
|
603
|
+
regionCode: string | null;
|
|
604
|
+
region: string | null;
|
|
605
|
+
city: string | null;
|
|
606
|
+
locality: string | null;
|
|
607
|
+
placeType: string | null;
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* Page-meta for a resolved taxonomy slug. `sourceSlug` is the immutable
|
|
611
|
+
* English search key; `canonicalSlug` is the board-language URL; `redirectTo`
|
|
612
|
+
* is the canonical slug to 308 to when the inbound slug isn't canonical (the
|
|
613
|
+
* host app emits the redirect — the SDK never navigates). `geo` is set for
|
|
614
|
+
* place resolutions only.
|
|
615
|
+
*/
|
|
616
|
+
interface TaxonomyResolution {
|
|
617
|
+
object: 'taxonomy_resolution';
|
|
618
|
+
type: 'category' | 'skill' | 'place';
|
|
619
|
+
sourceSlug: string;
|
|
620
|
+
canonicalSlug: string;
|
|
621
|
+
displayName: string;
|
|
622
|
+
redirectTo: string | null;
|
|
623
|
+
geo: TaxonomyGeo | null;
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* A place in the board's locations directory (`taxonomy.places.list()` →
|
|
627
|
+
* `GET /places`), the data the `/jobs/locations/` index renders. `jobCount` is
|
|
628
|
+
* subtree-summed (a parent counts its descendants); `id`/`parentId` carry the
|
|
629
|
+
* hierarchy so consumers rebuild the same nested tree the hosted index shows.
|
|
630
|
+
*/
|
|
631
|
+
interface PublicPlace {
|
|
632
|
+
object: 'place';
|
|
633
|
+
/** Stable place identity (locations-tree edge endpoint). */
|
|
634
|
+
id: string;
|
|
635
|
+
/** Parent place's `id`; `null` for a root place. */
|
|
636
|
+
parentId: string | null;
|
|
637
|
+
/** Public slug (links to `/jobs/locations/:slug`); `null` if unslugged. */
|
|
638
|
+
slug: string | null;
|
|
639
|
+
name: string;
|
|
640
|
+
placeType: string;
|
|
641
|
+
countryCode: string | null;
|
|
642
|
+
regionCode: string | null;
|
|
643
|
+
jobCount: number;
|
|
644
|
+
}
|
|
645
|
+
|
|
449
646
|
interface CreateBoardClientOptions {
|
|
450
647
|
baseUrl: string;
|
|
451
648
|
/** Board identifier: `pk_…` key (provisioned default) | `boards_…` ID | slug. */
|
|
@@ -489,16 +686,26 @@ declare function createBoardClient(options: CreateBoardClientOptions): {
|
|
|
489
686
|
* const { name, theme } = await board.context();
|
|
490
687
|
*/
|
|
491
688
|
context(options?: FetchOptions): Promise<PublicBoard>;
|
|
689
|
+
/**
|
|
690
|
+
* Board SEO infra — `ads.txt`, IndexNow key, Google site-verification, and
|
|
691
|
+
* the canonical base URL. Rebuild `robots.txt` / `ads.txt` /
|
|
692
|
+
* `indexnow-key.txt` from it.
|
|
693
|
+
*
|
|
694
|
+
* @example
|
|
695
|
+
* const { adsTxt, canonicalBase } = await board.seo();
|
|
696
|
+
*/
|
|
697
|
+
seo(options?: FetchOptions): Promise<BoardSeo>;
|
|
492
698
|
jobs: {
|
|
493
|
-
list(query?: JobsListQuery, options?: FetchOptions): Promise<
|
|
699
|
+
list(query?: JobsListQuery, options?: FetchOptions): Promise<JobCardListEnvelope>;
|
|
494
700
|
retrieve(jobSlug: string, query?: Record<string, never>, options?: FetchOptions): Promise<PublicJob>;
|
|
495
|
-
search(body: JobsSearchBody, query?: Record<string, never>, options?: FetchOptions): Promise<
|
|
701
|
+
search(body: JobsSearchBody, query?: Record<string, never>, options?: FetchOptions): Promise<JobCardSearchEnvelope>;
|
|
702
|
+
similar(jobSlug: string, query?: JobsSimilarQuery, options?: FetchOptions): Promise<ListEnvelope<PublicJobCard>>;
|
|
496
703
|
};
|
|
497
704
|
companies: {
|
|
498
705
|
list(query?: CompaniesListQuery, options?: FetchOptions): Promise<ListEnvelope<PublicCompany>>;
|
|
499
706
|
retrieve(companySlug: string, query?: Record<string, never>, options?: FetchOptions): Promise<PublicCompany>;
|
|
500
707
|
search(body: CompaniesSearchBody, query?: Record<string, never>, options?: FetchOptions): Promise<SearchEnvelope<PublicCompany>>;
|
|
501
|
-
listJobs(companySlug: string, query?: CompanyJobsListQuery, options?: FetchOptions): Promise<
|
|
708
|
+
listJobs(companySlug: string, query?: CompanyJobsListQuery, options?: FetchOptions): Promise<JobCardListEnvelope>;
|
|
502
709
|
};
|
|
503
710
|
blog: {
|
|
504
711
|
posts: {
|
|
@@ -532,7 +739,22 @@ declare function createBoardClient(options: CreateBoardClientOptions): {
|
|
|
532
739
|
unsave(jobId: string, query?: Record<string, never>, options?: FetchOptions): Promise<void>;
|
|
533
740
|
};
|
|
534
741
|
};
|
|
742
|
+
taxonomy: {
|
|
743
|
+
categories: {
|
|
744
|
+
resolve(slug: string, options?: FetchOptions): Promise<TaxonomyResolution>;
|
|
745
|
+
};
|
|
746
|
+
skills: {
|
|
747
|
+
resolve(slug: string, options?: FetchOptions): Promise<TaxonomyResolution>;
|
|
748
|
+
};
|
|
749
|
+
places: {
|
|
750
|
+
list(options?: FetchOptions): Promise<ListEnvelope<PublicPlace>>;
|
|
751
|
+
resolve(slug: string, options?: FetchOptions): Promise<TaxonomyResolution>;
|
|
752
|
+
};
|
|
753
|
+
};
|
|
754
|
+
redirects: {
|
|
755
|
+
resolve(path: string, options?: FetchOptions): Promise<RedirectResolution>;
|
|
756
|
+
};
|
|
535
757
|
};
|
|
536
758
|
type BoardSdk = ReturnType<typeof createBoardClient>;
|
|
537
759
|
|
|
538
|
-
export { ACCESS_TOKEN_KEY, type Awaitable, type BlogAuthorEmbed, type BlogPostsListQuery, type BlogSearchBody, type BlogTagEmbed, BoardApiError, type BoardAuthSession, BoardClient, type BoardRequest, type BoardSdk, type BoardUser, type CompaniesListQuery, type CompaniesSearchBody, type CompanyJobsListQuery, type CreateBoardClientOptions, type CustomStorage, type EducationRequirement, type EmploymentType, type FetchOptions, type ForgotPasswordBody, type JobCompany, type JobsListQuery, type JobsSearchBody, type ListEnvelope, type Logger, type LoginBody, type LogoutBody, type OfficeLocation, type PublicBlogAuthor, type PublicBlogPost, type PublicBlogPostSummary, type PublicBlogTag, type PublicBoard, type PublicBoardAnalytics, type PublicBoardFeatures, type PublicBoardTheme, type PublicCompany, type PublicJob, REFRESH_TOKEN_KEY, type RefreshBody, type RegisterBody, type RemoteOption, type RemotePermit, type RemoteTimezone, type ResetPasswordBody, SDK_VERSION, type SaveJobBody, type SavedJob, type SavedJobsListQuery, type SearchEnvelope, type Seniority, type StorageMode, type VerifyEmailBody, createBoardClient, isBoardApiError, isConflict, isForbidden, isNotFound, isRateLimited, isUnauthorized, isValidationError };
|
|
760
|
+
export { ACCESS_TOKEN_KEY, type Awaitable, type BlogAuthorEmbed, type BlogPostsListQuery, type BlogSearchBody, type BlogTagEmbed, BoardApiError, type BoardAuthSession, BoardClient, type BoardRequest, type BoardSdk, type BoardSeo, type BoardUser, type CompaniesListQuery, type CompaniesSearchBody, type CompanyJobsListQuery, type CreateBoardClientOptions, type CustomStorage, type EducationRequirement, type EmploymentType, type FetchOptions, type ForgotPasswordBody, type JobCardListEnvelope, type JobCardSearchEnvelope, type JobCompany, type JobsListQuery, type JobsSearchBody, type ListEnvelope, type Logger, type LoginBody, type LogoutBody, type OfficeLocation, type PublicBlogAuthor, type PublicBlogPost, type PublicBlogPostSummary, type PublicBlogTag, type PublicBoard, type PublicBoardAnalytics, type PublicBoardFeatures, type PublicBoardTheme, type PublicCompany, type PublicJob, type PublicJobCard, type PublicPlace, REFRESH_TOKEN_KEY, type RedirectResolution, type RefreshBody, type RegisterBody, type RelatedSearch, type RemoteOption, type RemotePermit, type RemoteTimezone, type ResetPasswordBody, SDK_VERSION, type SaveJobBody, type SavedJob, type SavedJobsListQuery, type SearchEnvelope, type Seniority, type StorageMode, type StorefrontPagination, type TaxonomyGeo, type TaxonomyResolution, type VerifyEmailBody, createBoardClient, isBoardApiError, isConflict, isForbidden, isNotFound, isRateLimited, isUnauthorized, isValidationError };
|
package/dist/index.js
CHANGED
|
@@ -143,7 +143,7 @@ async function clearSession(storage) {
|
|
|
143
143
|
}
|
|
144
144
|
|
|
145
145
|
// src/version.ts
|
|
146
|
-
var SDK_VERSION = "1.
|
|
146
|
+
var SDK_VERSION = "1.2.0";
|
|
147
147
|
|
|
148
148
|
// src/client.ts
|
|
149
149
|
function isRawBody(body) {
|
|
@@ -544,6 +544,23 @@ function jobsNamespace(client) {
|
|
|
544
544
|
body,
|
|
545
545
|
query
|
|
546
546
|
});
|
|
547
|
+
},
|
|
548
|
+
/**
|
|
549
|
+
* List jobs similar to one job — the same ranking that powers the
|
|
550
|
+
* on-page similar-jobs rail. Returns up to `limit` slim cards (default
|
|
551
|
+
* 5), excluding the job itself and any role at the same company.
|
|
552
|
+
*
|
|
553
|
+
* @example
|
|
554
|
+
* const { data } = await board.jobs.similar('senior-chef', { limit: 5 });
|
|
555
|
+
*/
|
|
556
|
+
similar(jobSlug, query, options) {
|
|
557
|
+
return client.fetch(
|
|
558
|
+
`/jobs/${encodeURIComponent(jobSlug)}/similar`,
|
|
559
|
+
{
|
|
560
|
+
...options,
|
|
561
|
+
query
|
|
562
|
+
}
|
|
563
|
+
);
|
|
547
564
|
}
|
|
548
565
|
};
|
|
549
566
|
}
|
|
@@ -608,6 +625,55 @@ function meNamespace(client) {
|
|
|
608
625
|
};
|
|
609
626
|
}
|
|
610
627
|
|
|
628
|
+
// src/namespaces/redirects.ts
|
|
629
|
+
function redirectsNamespace(client) {
|
|
630
|
+
return {
|
|
631
|
+
/**
|
|
632
|
+
* Resolve a board-relative path against the board's configured redirects —
|
|
633
|
+
* the same resolution the hosted board's catch-all performs. 308 to
|
|
634
|
+
* `target`, or 404 when `target` is `null`.
|
|
635
|
+
*
|
|
636
|
+
* @example
|
|
637
|
+
* const { target } = await board.redirects.resolve('/old-jobs')
|
|
638
|
+
*/
|
|
639
|
+
resolve(path, options) {
|
|
640
|
+
return client.fetch("/redirects/resolve", {
|
|
641
|
+
...options,
|
|
642
|
+
query: { path }
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
// src/namespaces/taxonomy.ts
|
|
649
|
+
function taxonomyResolver(client, kind) {
|
|
650
|
+
return {
|
|
651
|
+
/**
|
|
652
|
+
* Resolve a (board-language or English) taxonomy slug to its page-meta.
|
|
653
|
+
* Rejects with a `not_found` `BoardApiError` when the slug doesn't resolve.
|
|
654
|
+
*/
|
|
655
|
+
resolve(slug, options) {
|
|
656
|
+
return client.fetch(
|
|
657
|
+
`/${kind}/${encodeURIComponent(slug)}`,
|
|
658
|
+
options
|
|
659
|
+
);
|
|
660
|
+
}
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
function taxonomyNamespace(client) {
|
|
664
|
+
return {
|
|
665
|
+
categories: taxonomyResolver(client, "categories"),
|
|
666
|
+
skills: taxonomyResolver(client, "skills"),
|
|
667
|
+
places: {
|
|
668
|
+
...taxonomyResolver(client, "places"),
|
|
669
|
+
/** List every place used by a published job, with its live job count. */
|
|
670
|
+
list(options) {
|
|
671
|
+
return client.fetch("/places", options);
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
|
|
611
677
|
// src/index.ts
|
|
612
678
|
function createBoardClient(options) {
|
|
613
679
|
const client = new BoardClient({
|
|
@@ -635,10 +701,23 @@ function createBoardClient(options) {
|
|
|
635
701
|
context(options2) {
|
|
636
702
|
return client.fetch("", options2);
|
|
637
703
|
},
|
|
704
|
+
/**
|
|
705
|
+
* Board SEO infra — `ads.txt`, IndexNow key, Google site-verification, and
|
|
706
|
+
* the canonical base URL. Rebuild `robots.txt` / `ads.txt` /
|
|
707
|
+
* `indexnow-key.txt` from it.
|
|
708
|
+
*
|
|
709
|
+
* @example
|
|
710
|
+
* const { adsTxt, canonicalBase } = await board.seo();
|
|
711
|
+
*/
|
|
712
|
+
seo(options2) {
|
|
713
|
+
return client.fetch("/seo", options2);
|
|
714
|
+
},
|
|
638
715
|
jobs: jobsNamespace(client),
|
|
639
716
|
companies: companiesNamespace(client),
|
|
640
717
|
blog: blogNamespace(client),
|
|
641
718
|
auth: authNamespace(client),
|
|
642
|
-
me: meNamespace(client)
|
|
719
|
+
me: meNamespace(client),
|
|
720
|
+
taxonomy: taxonomyNamespace(client),
|
|
721
|
+
redirects: redirectsNamespace(client)
|
|
643
722
|
};
|
|
644
723
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -105,7 +105,7 @@ async function clearSession(storage) {
|
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
// src/version.ts
|
|
108
|
-
var SDK_VERSION = "1.
|
|
108
|
+
var SDK_VERSION = "1.2.0";
|
|
109
109
|
|
|
110
110
|
// src/client.ts
|
|
111
111
|
function isRawBody(body) {
|
|
@@ -506,6 +506,23 @@ function jobsNamespace(client) {
|
|
|
506
506
|
body,
|
|
507
507
|
query
|
|
508
508
|
});
|
|
509
|
+
},
|
|
510
|
+
/**
|
|
511
|
+
* List jobs similar to one job — the same ranking that powers the
|
|
512
|
+
* on-page similar-jobs rail. Returns up to `limit` slim cards (default
|
|
513
|
+
* 5), excluding the job itself and any role at the same company.
|
|
514
|
+
*
|
|
515
|
+
* @example
|
|
516
|
+
* const { data } = await board.jobs.similar('senior-chef', { limit: 5 });
|
|
517
|
+
*/
|
|
518
|
+
similar(jobSlug, query, options) {
|
|
519
|
+
return client.fetch(
|
|
520
|
+
`/jobs/${encodeURIComponent(jobSlug)}/similar`,
|
|
521
|
+
{
|
|
522
|
+
...options,
|
|
523
|
+
query
|
|
524
|
+
}
|
|
525
|
+
);
|
|
509
526
|
}
|
|
510
527
|
};
|
|
511
528
|
}
|
|
@@ -570,6 +587,55 @@ function meNamespace(client) {
|
|
|
570
587
|
};
|
|
571
588
|
}
|
|
572
589
|
|
|
590
|
+
// src/namespaces/redirects.ts
|
|
591
|
+
function redirectsNamespace(client) {
|
|
592
|
+
return {
|
|
593
|
+
/**
|
|
594
|
+
* Resolve a board-relative path against the board's configured redirects —
|
|
595
|
+
* the same resolution the hosted board's catch-all performs. 308 to
|
|
596
|
+
* `target`, or 404 when `target` is `null`.
|
|
597
|
+
*
|
|
598
|
+
* @example
|
|
599
|
+
* const { target } = await board.redirects.resolve('/old-jobs')
|
|
600
|
+
*/
|
|
601
|
+
resolve(path, options) {
|
|
602
|
+
return client.fetch("/redirects/resolve", {
|
|
603
|
+
...options,
|
|
604
|
+
query: { path }
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
// src/namespaces/taxonomy.ts
|
|
611
|
+
function taxonomyResolver(client, kind) {
|
|
612
|
+
return {
|
|
613
|
+
/**
|
|
614
|
+
* Resolve a (board-language or English) taxonomy slug to its page-meta.
|
|
615
|
+
* Rejects with a `not_found` `BoardApiError` when the slug doesn't resolve.
|
|
616
|
+
*/
|
|
617
|
+
resolve(slug, options) {
|
|
618
|
+
return client.fetch(
|
|
619
|
+
`/${kind}/${encodeURIComponent(slug)}`,
|
|
620
|
+
options
|
|
621
|
+
);
|
|
622
|
+
}
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
function taxonomyNamespace(client) {
|
|
626
|
+
return {
|
|
627
|
+
categories: taxonomyResolver(client, "categories"),
|
|
628
|
+
skills: taxonomyResolver(client, "skills"),
|
|
629
|
+
places: {
|
|
630
|
+
...taxonomyResolver(client, "places"),
|
|
631
|
+
/** List every place used by a published job, with its live job count. */
|
|
632
|
+
list(options) {
|
|
633
|
+
return client.fetch("/places", options);
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
};
|
|
637
|
+
}
|
|
638
|
+
|
|
573
639
|
// src/index.ts
|
|
574
640
|
function createBoardClient(options) {
|
|
575
641
|
const client = new BoardClient({
|
|
@@ -597,11 +663,24 @@ function createBoardClient(options) {
|
|
|
597
663
|
context(options2) {
|
|
598
664
|
return client.fetch("", options2);
|
|
599
665
|
},
|
|
666
|
+
/**
|
|
667
|
+
* Board SEO infra — `ads.txt`, IndexNow key, Google site-verification, and
|
|
668
|
+
* the canonical base URL. Rebuild `robots.txt` / `ads.txt` /
|
|
669
|
+
* `indexnow-key.txt` from it.
|
|
670
|
+
*
|
|
671
|
+
* @example
|
|
672
|
+
* const { adsTxt, canonicalBase } = await board.seo();
|
|
673
|
+
*/
|
|
674
|
+
seo(options2) {
|
|
675
|
+
return client.fetch("/seo", options2);
|
|
676
|
+
},
|
|
600
677
|
jobs: jobsNamespace(client),
|
|
601
678
|
companies: companiesNamespace(client),
|
|
602
679
|
blog: blogNamespace(client),
|
|
603
680
|
auth: authNamespace(client),
|
|
604
|
-
me: meNamespace(client)
|
|
681
|
+
me: meNamespace(client),
|
|
682
|
+
taxonomy: taxonomyNamespace(client),
|
|
683
|
+
redirects: redirectsNamespace(client)
|
|
605
684
|
};
|
|
606
685
|
}
|
|
607
686
|
export {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cavuno/board",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Typed isomorphic client for the Cavuno Board API",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -33,17 +33,18 @@
|
|
|
33
33
|
"publishConfig": {
|
|
34
34
|
"access": "public"
|
|
35
35
|
},
|
|
36
|
-
"devDependencies": {
|
|
37
|
-
"@types/node": "24.9.1",
|
|
38
|
-
"tsup": "^8.4.0",
|
|
39
|
-
"vitest": "^2.1.9",
|
|
40
|
-
"@kit/tsconfig": "0.1.0"
|
|
41
|
-
},
|
|
42
36
|
"scripts": {
|
|
43
37
|
"build": "tsup",
|
|
44
38
|
"clean": "git clean -xdf .turbo dist node_modules",
|
|
45
39
|
"typecheck": "tsgo --noEmit",
|
|
46
40
|
"test": "vitest run",
|
|
47
|
-
"assert-publish-target": "node -e \"const p=require('./package.json'); if(p.name!=='@cavuno/board'){throw new Error('Refusing to publish: package.json name is '+p.name+', expected @cavuno/board')}; if(p.private){throw new Error('Refusing to publish: package.json has private:true')}\""
|
|
41
|
+
"assert-publish-target": "node -e \"const p=require('./package.json'); if(p.name!=='@cavuno/board'){throw new Error('Refusing to publish: package.json name is '+p.name+', expected @cavuno/board')}; if(p.private){throw new Error('Refusing to publish: package.json has private:true')}\"",
|
|
42
|
+
"prepublishOnly": "pnpm run assert-publish-target && pnpm run build"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@kit/tsconfig": "workspace:*",
|
|
46
|
+
"@types/node": "catalog:",
|
|
47
|
+
"tsup": "^8.4.0",
|
|
48
|
+
"vitest": "^2.1.9"
|
|
48
49
|
}
|
|
49
|
-
}
|
|
50
|
+
}
|