@cavuno/board 1.38.0 → 1.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/README.md +7 -0
  2. package/dist/{jobs-DMH3Ytoq.d.mts → _spec-DRbgPIlN.d.mts} +404 -96
  3. package/dist/{jobs-DMH3Ytoq.d.ts → _spec-DRbgPIlN.d.ts} +404 -96
  4. package/dist/bin.mjs +242 -18
  5. package/dist/{board-CBry4f7H.d.mts → board-BxPUtrOl.d.ts} +1 -1
  6. package/dist/{board-tV-zcaHs.d.ts → board-DCiNpgFf.d.mts} +1 -1
  7. package/dist/doctor.js +238 -14
  8. package/dist/doctor.mjs +238 -14
  9. package/dist/filters.d.mts +18 -4
  10. package/dist/filters.d.ts +18 -4
  11. package/dist/filters.js +17 -0
  12. package/dist/filters.mjs +17 -0
  13. package/dist/format.d.mts +3 -2
  14. package/dist/format.d.ts +3 -2
  15. package/dist/index.d.mts +62 -73
  16. package/dist/index.d.ts +62 -73
  17. package/dist/index.js +71 -1
  18. package/dist/index.mjs +71 -1
  19. package/dist/jobs-DAGAVHQL.d.ts +105 -0
  20. package/dist/jobs-DhePKSRe.d.mts +105 -0
  21. package/dist/{salaries-CaGlcCC6.d.ts → salaries-D6SUVMmt.d.mts} +2 -1
  22. package/dist/{salaries-01g4wK8y.d.mts → salaries-cqb78kg0.d.ts} +2 -1
  23. package/dist/search-BalPAS0P.d.ts +81 -0
  24. package/dist/search-DYUQzCq_.d.mts +81 -0
  25. package/dist/seo.d.mts +4 -3
  26. package/dist/seo.d.ts +4 -3
  27. package/dist/server.d.mts +56 -4
  28. package/dist/server.d.ts +56 -4
  29. package/dist/server.js +41 -0
  30. package/dist/server.mjs +41 -0
  31. package/dist/sitemap.d.mts +5 -3
  32. package/dist/sitemap.d.ts +5 -3
  33. package/dist/suggest.d.mts +70 -0
  34. package/dist/suggest.d.ts +70 -0
  35. package/dist/suggest.js +165 -0
  36. package/dist/suggest.mjs +144 -0
  37. package/dist/theme.d.mts +49 -1
  38. package/dist/theme.d.ts +49 -1
  39. package/dist/theme.js +60 -26
  40. package/dist/theme.mjs +60 -26
  41. package/package.json +11 -1
  42. package/skills/cavuno-board-filters/SKILL.md +11 -4
  43. package/skills/cavuno-board-jobs/SKILL.md +18 -2
  44. package/skills/cavuno-board-suggest/SKILL.md +119 -0
  45. package/skills/manifest.json +9 -2
package/dist/index.mjs CHANGED
@@ -265,7 +265,7 @@ async function clearSession(storage) {
265
265
  }
266
266
 
267
267
  // src/version.ts
268
- var SDK_VERSION = "1.38.0";
268
+ var SDK_VERSION = "1.40.0";
269
269
 
270
270
  // src/client.ts
271
271
  function isRawBody(body) {
@@ -1351,6 +1351,20 @@ function meNamespace(client) {
1351
1351
  { ...options, method: "DELETE" }
1352
1352
  );
1353
1353
  },
1354
+ /**
1355
+ * Retrieve my full editable company profile (summary, socials, logo,
1356
+ * description) — the read half of `update`, for pre-populating an edit
1357
+ * form. Requires an approved membership.
1358
+ *
1359
+ * @example
1360
+ * const company = await board.me.companies.retrieve('acme');
1361
+ */
1362
+ retrieve(slug, options) {
1363
+ return client.fetch(
1364
+ `/me/companies/${encodeURIComponent(slug)}`,
1365
+ options
1366
+ );
1367
+ },
1354
1368
  /**
1355
1369
  * Edit my company profile (merge-patch — omitted fields stay
1356
1370
  * unchanged). Requires an approved membership. Returns the company.
@@ -1364,6 +1378,22 @@ function meNamespace(client) {
1364
1378
  { ...options, method: "PATCH", body }
1365
1379
  );
1366
1380
  },
1381
+ /**
1382
+ * Upload + attach my company logo (JPEG/PNG/WebP/GIF, ≤2 MB) via a single
1383
+ * multipart POST. Requires an approved membership. Returns the updated
1384
+ * company with its new `logoUrl`.
1385
+ *
1386
+ * @example
1387
+ * const company = await board.me.companies.uploadLogo('acme', file);
1388
+ */
1389
+ uploadLogo(slug, file, options) {
1390
+ const form = new FormData();
1391
+ form.append("file", file);
1392
+ return client.fetch(
1393
+ `/me/companies/${encodeURIComponent(slug)}/logo`,
1394
+ { ...options, method: "POST", body: form }
1395
+ );
1396
+ },
1367
1397
  /** The work-email verification state machine for a pending claim. */
1368
1398
  workEmail: {
1369
1399
  /**
@@ -1689,6 +1719,25 @@ function meNamespace(client) {
1689
1719
  }
1690
1720
  }
1691
1721
  },
1722
+ /**
1723
+ * The viewer's talent entitlement on this board (CAV-243) — "does this
1724
+ * employer have talent access?" for gating a Message-vs-upsell CTA.
1725
+ * Candidates and unaffiliated viewers get `hasTalentAccess: false`.
1726
+ *
1727
+ * @example
1728
+ * const { hasTalentAccess } = await board.me.talentAccess.retrieve();
1729
+ */
1730
+ talentAccess: {
1731
+ /**
1732
+ * Retrieve the viewer's talent entitlement. Never cache — per-user.
1733
+ *
1734
+ * @example
1735
+ * const access = await board.me.talentAccess.retrieve();
1736
+ */
1737
+ retrieve(options) {
1738
+ return client.fetch("/me/talent-access", options);
1739
+ }
1740
+ },
1692
1741
  /**
1693
1742
  * The authenticated board user's job-alert preferences (ADR-0053) —
1694
1743
  * a collection (up to 10). Creating one is active immediately (no
@@ -2431,6 +2480,26 @@ function salariesNamespace(client) {
2431
2480
  };
2432
2481
  }
2433
2482
 
2483
+ // src/namespaces/search.ts
2484
+ function searchNamespace(client) {
2485
+ return {
2486
+ /**
2487
+ * GET /search/suggest — federated keyword suggestions (companies +
2488
+ * taxonomy terms), one interleaved server-ranked list. Order is the
2489
+ * contract: do not re-sort.
2490
+ *
2491
+ * @example
2492
+ * const { items } = await board.search.suggest({ q: 'acme', limit: 10 });
2493
+ */
2494
+ suggest(query, options) {
2495
+ return client.fetch("/search/suggest", {
2496
+ ...options,
2497
+ query
2498
+ });
2499
+ }
2500
+ };
2501
+ }
2502
+
2434
2503
  // src/namespaces/talent.ts
2435
2504
  function talentNamespace(client) {
2436
2505
  return {
@@ -2652,6 +2721,7 @@ function createBoardClient(options) {
2652
2721
  me: meNamespace(client),
2653
2722
  password: passwordNamespace(client),
2654
2723
  taxonomy: taxonomyNamespace(client),
2724
+ search: searchNamespace(client),
2655
2725
  redirects: redirectsNamespace(client),
2656
2726
  jobAlerts: jobAlertsNamespace(client),
2657
2727
  jobPosting: jobPostingNamespace(client),
@@ -0,0 +1,105 @@
1
+ import { S as Schemas } from './_spec-DRbgPIlN.js';
2
+
3
+ /**
4
+ * Stripe-shaped success envelopes (`01-conventions.md` §5.1). The
5
+ * server MAY add top-level fields; consumers MUST ignore unknown
6
+ * fields.
7
+ */
8
+ /**
9
+ * Optional offset-pagination metadata generated from the Board API contract.
10
+ * `nextCursor` is preserved alongside for forward cursor iteration.
11
+ */
12
+ type OffsetPagination = Schemas['OffsetPagination'];
13
+ /** @deprecated Use `OffsetPagination`. */
14
+ type StorefrontPagination = OffsetPagination;
15
+ /** @deprecated Use `OffsetPagination`. */
16
+ type JobCatalogPagination = OffsetPagination;
17
+ type ListEnvelope<T> = OffsetPagination & {
18
+ object: 'list';
19
+ url: string;
20
+ hasMore: boolean;
21
+ /** `null` when `hasMore` is false — always present, never undefined. */
22
+ nextCursor: string | null;
23
+ data: T[];
24
+ };
25
+ type SearchEnvelope<T> = OffsetPagination & {
26
+ object: 'search_result';
27
+ url: string;
28
+ hasMore: boolean;
29
+ nextCursor: string | null;
30
+ data: T[];
31
+ };
32
+
33
+ type PublicJob = Schemas['PublicJob'];
34
+ type PublicJobCard = Schemas['PublicJobCard'];
35
+ /**
36
+ * A job's opaque, display-only custom-field values (CAV-294), keyed by each
37
+ * field's `key`. Resolve labels via the board's `CustomFieldDefinition`s
38
+ * (`board.context().customFields`).
39
+ */
40
+ type CustomFieldValues = PublicJob['customFieldValues'];
41
+ type CustomFieldValue = CustomFieldValues[string];
42
+ type JobCompany = Schemas['JobCompany'];
43
+ type OfficeLocation = Schemas['JobOfficeLocation'];
44
+ type RemoteOption = NonNullable<PublicJob['remoteOption']>;
45
+ type EmploymentType = NonNullable<PublicJob['employmentType']>;
46
+ type Seniority = NonNullable<PublicJob['seniority']>;
47
+ /** Candidate-facing result ordering (ADR-0048); `relevance` is the default. */
48
+ type JobSort = NonNullable<Schemas['PublicSearchJobsBody']['sort']>;
49
+ type EducationRequirement = PublicJob['educationRequirements'][number];
50
+ type RemotePermit = PublicJob['remotePermits'][number];
51
+ type RemoteTimezone = PublicJob['remoteTimezones'][number];
52
+ /**
53
+ * Derived suggestion on a browse list (ADR-0037 §8): jobs surface `category`
54
+ * and `skill` terms; the companies list surfaces `market` terms.
55
+ */
56
+ interface RelatedSearch {
57
+ type: 'category' | 'skill' | 'market';
58
+ slug: string;
59
+ term: string;
60
+ count: number;
61
+ }
62
+ /** The browse list envelope — `PublicJobCard`s + job catalog pagination + `relatedSearches`. */
63
+ type JobCardListEnvelope = ListEnvelope<PublicJobCard> & {
64
+ relatedSearches?: RelatedSearch[];
65
+ };
66
+ /** The search envelope — `PublicJobCard`s + job catalog pagination. */
67
+ type JobCardSearchEnvelope = SearchEnvelope<PublicJobCard>;
68
+ type JobsListQuery = {
69
+ cursor?: string;
70
+ /** 1–100. */
71
+ limit?: number;
72
+ /** Job catalog page offset; takes precedence over `cursor`. `offset + limit` ≤ 10,000. */
73
+ offset?: number;
74
+ /** Repeated param (up to 10) — OR-matched. Repeat `companyId` per value. */
75
+ companyId?: string[];
76
+ /**
77
+ * Company slugs (the public URL identity). Repeat for multiple values
78
+ * (up to 10). Resolved server-side; unknown slugs are dropped. Combined
79
+ * with `companyId` as a union. If the provided company filter resolves
80
+ * to no known companies, the result is empty.
81
+ */
82
+ companySlug?: string[];
83
+ remoteOption?: RemoteOption[];
84
+ employmentType?: EmploymentType[];
85
+ seniority?: Seniority[];
86
+ /** Result ordering (ADR-0048). Absent ⇒ `relevance` (the featured-ranked browse). */
87
+ sort?: JobSort;
88
+ /** Place slug for a geo radius search; unresolvable slugs are ignored. */
89
+ location?: string;
90
+ /** Radius in km around `location` (10–250; default 50). */
91
+ radius?: number;
92
+ /** Category slug seed (the `/jobs/[keyword]` page) — server resolves it to the English source name; unresolvable → 404. */
93
+ category?: string;
94
+ /** Skill slug seed (the `/jobs/skills/[skill]` page) — server-resolved; unresolvable → 404. */
95
+ skill?: string;
96
+ /** Sparse fieldset (Medusa-style `+field`). Only `'+description'` is supported — adds `description` to each card. */
97
+ fields?: string;
98
+ };
99
+ type JobsSimilarQuery = {
100
+ /** How many similar jobs to return (1–20; default 5). */
101
+ limit?: number;
102
+ };
103
+ type JobsSearchBody = Schemas['PublicSearchJobsBody'];
104
+
105
+ export type { CustomFieldValues as C, EmploymentType as E, JobSort as J, ListEnvelope as L, OfficeLocation as O, PublicJob as P, RemoteOption as R, Seniority as S, PublicJobCard as a, JobsListQuery as b, JobCardListEnvelope as c, JobsSearchBody as d, JobCardSearchEnvelope as e, JobsSimilarQuery as f, SearchEnvelope as g, CustomFieldValue as h, EducationRequirement as i, JobCatalogPagination as j, JobCompany as k, OffsetPagination as l, RelatedSearch as m, RemotePermit as n, RemoteTimezone as o, StorefrontPagination as p };
@@ -0,0 +1,105 @@
1
+ import { S as Schemas } from './_spec-DRbgPIlN.mjs';
2
+
3
+ /**
4
+ * Stripe-shaped success envelopes (`01-conventions.md` §5.1). The
5
+ * server MAY add top-level fields; consumers MUST ignore unknown
6
+ * fields.
7
+ */
8
+ /**
9
+ * Optional offset-pagination metadata generated from the Board API contract.
10
+ * `nextCursor` is preserved alongside for forward cursor iteration.
11
+ */
12
+ type OffsetPagination = Schemas['OffsetPagination'];
13
+ /** @deprecated Use `OffsetPagination`. */
14
+ type StorefrontPagination = OffsetPagination;
15
+ /** @deprecated Use `OffsetPagination`. */
16
+ type JobCatalogPagination = OffsetPagination;
17
+ type ListEnvelope<T> = OffsetPagination & {
18
+ object: 'list';
19
+ url: string;
20
+ hasMore: boolean;
21
+ /** `null` when `hasMore` is false — always present, never undefined. */
22
+ nextCursor: string | null;
23
+ data: T[];
24
+ };
25
+ type SearchEnvelope<T> = OffsetPagination & {
26
+ object: 'search_result';
27
+ url: string;
28
+ hasMore: boolean;
29
+ nextCursor: string | null;
30
+ data: T[];
31
+ };
32
+
33
+ type PublicJob = Schemas['PublicJob'];
34
+ type PublicJobCard = Schemas['PublicJobCard'];
35
+ /**
36
+ * A job's opaque, display-only custom-field values (CAV-294), keyed by each
37
+ * field's `key`. Resolve labels via the board's `CustomFieldDefinition`s
38
+ * (`board.context().customFields`).
39
+ */
40
+ type CustomFieldValues = PublicJob['customFieldValues'];
41
+ type CustomFieldValue = CustomFieldValues[string];
42
+ type JobCompany = Schemas['JobCompany'];
43
+ type OfficeLocation = Schemas['JobOfficeLocation'];
44
+ type RemoteOption = NonNullable<PublicJob['remoteOption']>;
45
+ type EmploymentType = NonNullable<PublicJob['employmentType']>;
46
+ type Seniority = NonNullable<PublicJob['seniority']>;
47
+ /** Candidate-facing result ordering (ADR-0048); `relevance` is the default. */
48
+ type JobSort = NonNullable<Schemas['PublicSearchJobsBody']['sort']>;
49
+ type EducationRequirement = PublicJob['educationRequirements'][number];
50
+ type RemotePermit = PublicJob['remotePermits'][number];
51
+ type RemoteTimezone = PublicJob['remoteTimezones'][number];
52
+ /**
53
+ * Derived suggestion on a browse list (ADR-0037 §8): jobs surface `category`
54
+ * and `skill` terms; the companies list surfaces `market` terms.
55
+ */
56
+ interface RelatedSearch {
57
+ type: 'category' | 'skill' | 'market';
58
+ slug: string;
59
+ term: string;
60
+ count: number;
61
+ }
62
+ /** The browse list envelope — `PublicJobCard`s + job catalog pagination + `relatedSearches`. */
63
+ type JobCardListEnvelope = ListEnvelope<PublicJobCard> & {
64
+ relatedSearches?: RelatedSearch[];
65
+ };
66
+ /** The search envelope — `PublicJobCard`s + job catalog pagination. */
67
+ type JobCardSearchEnvelope = SearchEnvelope<PublicJobCard>;
68
+ type JobsListQuery = {
69
+ cursor?: string;
70
+ /** 1–100. */
71
+ limit?: number;
72
+ /** Job catalog page offset; takes precedence over `cursor`. `offset + limit` ≤ 10,000. */
73
+ offset?: number;
74
+ /** Repeated param (up to 10) — OR-matched. Repeat `companyId` per value. */
75
+ companyId?: string[];
76
+ /**
77
+ * Company slugs (the public URL identity). Repeat for multiple values
78
+ * (up to 10). Resolved server-side; unknown slugs are dropped. Combined
79
+ * with `companyId` as a union. If the provided company filter resolves
80
+ * to no known companies, the result is empty.
81
+ */
82
+ companySlug?: string[];
83
+ remoteOption?: RemoteOption[];
84
+ employmentType?: EmploymentType[];
85
+ seniority?: Seniority[];
86
+ /** Result ordering (ADR-0048). Absent ⇒ `relevance` (the featured-ranked browse). */
87
+ sort?: JobSort;
88
+ /** Place slug for a geo radius search; unresolvable slugs are ignored. */
89
+ location?: string;
90
+ /** Radius in km around `location` (10–250; default 50). */
91
+ radius?: number;
92
+ /** Category slug seed (the `/jobs/[keyword]` page) — server resolves it to the English source name; unresolvable → 404. */
93
+ category?: string;
94
+ /** Skill slug seed (the `/jobs/skills/[skill]` page) — server-resolved; unresolvable → 404. */
95
+ skill?: string;
96
+ /** Sparse fieldset (Medusa-style `+field`). Only `'+description'` is supported — adds `description` to each card. */
97
+ fields?: string;
98
+ };
99
+ type JobsSimilarQuery = {
100
+ /** How many similar jobs to return (1–20; default 5). */
101
+ limit?: number;
102
+ };
103
+ type JobsSearchBody = Schemas['PublicSearchJobsBody'];
104
+
105
+ export type { CustomFieldValues as C, EmploymentType as E, JobSort as J, ListEnvelope as L, OfficeLocation as O, PublicJob as P, RemoteOption as R, Seniority as S, PublicJobCard as a, JobsListQuery as b, JobCardListEnvelope as c, JobsSearchBody as d, JobCardSearchEnvelope as e, JobsSimilarQuery as f, SearchEnvelope as g, CustomFieldValue as h, EducationRequirement as i, JobCatalogPagination as j, JobCompany as k, OffsetPagination as l, RelatedSearch as m, RemotePermit as n, RemoteTimezone as o, StorefrontPagination as p };
@@ -1,4 +1,5 @@
1
- import { b as Schemas, L as ListEnvelope, o as RelatedSearch } from './jobs-DMH3Ytoq.js';
1
+ import { S as Schemas } from './_spec-DRbgPIlN.mjs';
2
+ import { L as ListEnvelope, m as RelatedSearch } from './jobs-DhePKSRe.mjs';
2
3
 
3
4
  /** Author shape embedded on posts (no `object` discriminator). */
4
5
  type BlogAuthorEmbed = Schemas['PublicBlogAuthorEmbed'];
@@ -1,4 +1,5 @@
1
- import { b as Schemas, L as ListEnvelope, o as RelatedSearch } from './jobs-DMH3Ytoq.mjs';
1
+ import { S as Schemas } from './_spec-DRbgPIlN.js';
2
+ import { L as ListEnvelope, m as RelatedSearch } from './jobs-DAGAVHQL.js';
2
3
 
3
4
  /** Author shape embedded on posts (no `object` discriminator). */
4
5
  type BlogAuthorEmbed = Schemas['PublicBlogAuthorEmbed'];
@@ -0,0 +1,81 @@
1
+ import { S as Schemas } from './_spec-DRbgPIlN.js';
2
+
3
+ type Awaitable<T> = T | Promise<T>;
4
+ /**
5
+ * Async token storage. The SDK reads the access token from storage on
6
+ * every request (fresh-token rule); auth methods write/clear the pair.
7
+ */
8
+ interface CustomStorage {
9
+ getItem(key: string): Awaitable<string | null>;
10
+ setItem(key: string, value: string): Awaitable<void>;
11
+ removeItem(key: string): Awaitable<void>;
12
+ }
13
+ type StorageMode = 'memory' | 'nostore' | 'local' | 'session' | CustomStorage;
14
+ declare const ACCESS_TOKEN_KEY = "cavuno_board_access_token";
15
+ declare const REFRESH_TOKEN_KEY = "cavuno_board_refresh_token";
16
+ /** Board-password access grant — sent as `X-Board-Access` to pass the wall. */
17
+ declare const BOARD_ACCESS_GRANT_KEY = "cavuno_board_access_grant";
18
+
19
+ /**
20
+ * The request handed to `onRequest` and `fetch`. `onRequest` may
21
+ * mutate or replace it wholesale (locale headers, URL rewrites);
22
+ * whatever it returns is what gets fetched.
23
+ */
24
+ interface BoardRequest {
25
+ /** Fully built URL, query string included. */
26
+ url: string;
27
+ /** Final RequestInit: method, Headers instance, serialized body, plus passthrough keys (`signal`, `cache`, `next`, `cf`, …). */
28
+ init: RequestInit;
29
+ }
30
+ interface Logger {
31
+ debug(message: string): void;
32
+ error(message: string): void;
33
+ }
34
+ /**
35
+ * Per-call options on every SDK method. Everything besides `body` and
36
+ * `query` is passed through to `fetch` untouched, so framework caching
37
+ * (`next: { tags }`, `cf: {...}`, `cache:`) rides for free.
38
+ */
39
+ type FetchOptions = Omit<RequestInit, 'body'> & {
40
+ body?: unknown;
41
+ query?: Record<string, unknown>;
42
+ };
43
+ interface BoardClientOptions {
44
+ baseUrl: string;
45
+ /** Board identifier: `pk_…` key | `boards_…` ID | slug (ADR-0032). */
46
+ board: string;
47
+ storage: CustomStorage;
48
+ globalHeaders?: Record<string, string>;
49
+ onRequest?: (req: BoardRequest) => Awaitable<BoardRequest>;
50
+ onResponse?: (res: Response, req: BoardRequest) => Awaitable<void>;
51
+ logger?: Logger;
52
+ }
53
+ declare class BoardClient {
54
+ readonly storage: CustomStorage;
55
+ private readonly basePath;
56
+ private readonly options;
57
+ constructor(options: BoardClientOptions);
58
+ /**
59
+ * The full request pipeline. Public and first-class: custom endpoints
60
+ * work without an SDK release, and still get the board-identifier
61
+ * base path, default headers, bearer token, and both hooks.
62
+ *
63
+ * @example
64
+ * const stats = await board.client.fetch<MyShape>('/custom/stats');
65
+ */
66
+ fetch<T>(path: string, init?: FetchOptions): Promise<T>;
67
+ }
68
+
69
+ type SuggestResult = Schemas['SuggestResult'];
70
+ type SuggestionItem = Schemas['SuggestionItem'];
71
+ type CompanySuggestion = Schemas['CompanySuggestion'];
72
+ type TermSuggestion = Schemas['TermSuggestion'];
73
+ /** Query for `board.search.suggest()`. */
74
+ type SearchSuggestQuery = {
75
+ /** Keyword; under 2 chars the server returns an empty `items` array. */
76
+ q?: string;
77
+ /** Max interleaved suggestions (1–25; default 25). */
78
+ limit?: number;
79
+ };
80
+
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 SuggestionItem as S, type TermSuggestion as T, type SearchSuggestQuery as a, type SuggestResult as b, type StorageMode as c, BoardClient as d, ACCESS_TOKEN_KEY as e, BOARD_ACCESS_GRANT_KEY as f, type CustomStorage as g };
@@ -0,0 +1,81 @@
1
+ import { S as Schemas } from './_spec-DRbgPIlN.mjs';
2
+
3
+ type Awaitable<T> = T | Promise<T>;
4
+ /**
5
+ * Async token storage. The SDK reads the access token from storage on
6
+ * every request (fresh-token rule); auth methods write/clear the pair.
7
+ */
8
+ interface CustomStorage {
9
+ getItem(key: string): Awaitable<string | null>;
10
+ setItem(key: string, value: string): Awaitable<void>;
11
+ removeItem(key: string): Awaitable<void>;
12
+ }
13
+ type StorageMode = 'memory' | 'nostore' | 'local' | 'session' | CustomStorage;
14
+ declare const ACCESS_TOKEN_KEY = "cavuno_board_access_token";
15
+ declare const REFRESH_TOKEN_KEY = "cavuno_board_refresh_token";
16
+ /** Board-password access grant — sent as `X-Board-Access` to pass the wall. */
17
+ declare const BOARD_ACCESS_GRANT_KEY = "cavuno_board_access_grant";
18
+
19
+ /**
20
+ * The request handed to `onRequest` and `fetch`. `onRequest` may
21
+ * mutate or replace it wholesale (locale headers, URL rewrites);
22
+ * whatever it returns is what gets fetched.
23
+ */
24
+ interface BoardRequest {
25
+ /** Fully built URL, query string included. */
26
+ url: string;
27
+ /** Final RequestInit: method, Headers instance, serialized body, plus passthrough keys (`signal`, `cache`, `next`, `cf`, …). */
28
+ init: RequestInit;
29
+ }
30
+ interface Logger {
31
+ debug(message: string): void;
32
+ error(message: string): void;
33
+ }
34
+ /**
35
+ * Per-call options on every SDK method. Everything besides `body` and
36
+ * `query` is passed through to `fetch` untouched, so framework caching
37
+ * (`next: { tags }`, `cf: {...}`, `cache:`) rides for free.
38
+ */
39
+ type FetchOptions = Omit<RequestInit, 'body'> & {
40
+ body?: unknown;
41
+ query?: Record<string, unknown>;
42
+ };
43
+ interface BoardClientOptions {
44
+ baseUrl: string;
45
+ /** Board identifier: `pk_…` key | `boards_…` ID | slug (ADR-0032). */
46
+ board: string;
47
+ storage: CustomStorage;
48
+ globalHeaders?: Record<string, string>;
49
+ onRequest?: (req: BoardRequest) => Awaitable<BoardRequest>;
50
+ onResponse?: (res: Response, req: BoardRequest) => Awaitable<void>;
51
+ logger?: Logger;
52
+ }
53
+ declare class BoardClient {
54
+ readonly storage: CustomStorage;
55
+ private readonly basePath;
56
+ private readonly options;
57
+ constructor(options: BoardClientOptions);
58
+ /**
59
+ * The full request pipeline. Public and first-class: custom endpoints
60
+ * work without an SDK release, and still get the board-identifier
61
+ * base path, default headers, bearer token, and both hooks.
62
+ *
63
+ * @example
64
+ * const stats = await board.client.fetch<MyShape>('/custom/stats');
65
+ */
66
+ fetch<T>(path: string, init?: FetchOptions): Promise<T>;
67
+ }
68
+
69
+ type SuggestResult = Schemas['SuggestResult'];
70
+ type SuggestionItem = Schemas['SuggestionItem'];
71
+ type CompanySuggestion = Schemas['CompanySuggestion'];
72
+ type TermSuggestion = Schemas['TermSuggestion'];
73
+ /** Query for `board.search.suggest()`. */
74
+ type SearchSuggestQuery = {
75
+ /** Keyword; under 2 chars the server returns an empty `items` array. */
76
+ q?: string;
77
+ /** Max interleaved suggestions (1–25; default 25). */
78
+ limit?: number;
79
+ };
80
+
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 SuggestionItem as S, type TermSuggestion as T, type SearchSuggestQuery as a, type SuggestResult as b, type StorageMode as c, BoardClient as d, ACCESS_TOKEN_KEY as e, BOARD_ACCESS_GRANT_KEY as f, type CustomStorage as g };
package/dist/seo.d.mts CHANGED
@@ -1,7 +1,8 @@
1
- import { P as PublicBlogPostSummary, B as BlogAuthorEmbed, C as CompanyCategorySalary, a as CompanySalary, L as LocationSalaryDetail, S as SkillSalaryDetail, T as TitleSalaryDetail } from './salaries-01g4wK8y.mjs';
2
- import { P as PublicBoard } from './board-CBry4f7H.mjs';
3
- import { P as PublicJob } from './jobs-DMH3Ytoq.mjs';
1
+ import { P as PublicBlogPostSummary, B as BlogAuthorEmbed, C as CompanyCategorySalary, a as CompanySalary, L as LocationSalaryDetail, S as SkillSalaryDetail, T as TitleSalaryDetail } from './salaries-D6SUVMmt.mjs';
2
+ import { P as PublicBoard } from './board-DCiNpgFf.mjs';
3
+ import { P as PublicJob } from './jobs-DhePKSRe.mjs';
4
4
  import { B as BoardLabelOverrides } from './ui-copy-CKfFTtLk.mjs';
5
+ import './_spec-DRbgPIlN.mjs';
5
6
 
6
7
  /**
7
8
  * Google for Jobs `JobPosting` structured data on the `@cavuno/board` wire
package/dist/seo.d.ts CHANGED
@@ -1,7 +1,8 @@
1
- import { P as PublicBlogPostSummary, B as BlogAuthorEmbed, C as CompanyCategorySalary, a as CompanySalary, L as LocationSalaryDetail, S as SkillSalaryDetail, T as TitleSalaryDetail } from './salaries-CaGlcCC6.js';
2
- import { P as PublicBoard } from './board-tV-zcaHs.js';
3
- import { P as PublicJob } from './jobs-DMH3Ytoq.js';
1
+ import { P as PublicBlogPostSummary, B as BlogAuthorEmbed, C as CompanyCategorySalary, a as CompanySalary, L as LocationSalaryDetail, S as SkillSalaryDetail, T as TitleSalaryDetail } from './salaries-cqb78kg0.js';
2
+ import { P as PublicBoard } from './board-BxPUtrOl.js';
3
+ import { P as PublicJob } from './jobs-DAGAVHQL.js';
4
4
  import { B as BoardLabelOverrides } from './ui-copy-CKfFTtLk.js';
5
+ import './_spec-DRbgPIlN.js';
5
6
 
6
7
  /**
7
8
  * Google for Jobs `JobPosting` structured data on the `@cavuno/board` wire
package/dist/server.d.mts CHANGED
@@ -1,7 +1,9 @@
1
1
  import { BoardSdk } from './index.mjs';
2
- import './jobs-DMH3Ytoq.mjs';
3
- import './board-CBry4f7H.mjs';
4
- import './salaries-01g4wK8y.mjs';
2
+ import './_spec-DRbgPIlN.mjs';
3
+ import './search-DYUQzCq_.mjs';
4
+ import './board-DCiNpgFf.mjs';
5
+ import './jobs-DhePKSRe.mjs';
6
+ import './salaries-D6SUVMmt.mjs';
5
7
 
6
8
  /**
7
9
  * Session cookie codec — pure (no framework imports, no node imports) so it
@@ -75,6 +77,56 @@ declare function safeRedirectPath(path: string | undefined | null, defaultPath?:
75
77
  */
76
78
  declare function currentPathFromReferer(referer: string | null): string;
77
79
 
80
+ /**
81
+ * MIG-10 (added scope) — keep `<slug>.cavuno.app` live after a custom-domain
82
+ * cutover by 308-redirecting to the board's canonical custom domain.
83
+ *
84
+ * ## Where this lives (DEVIATION LOUDLY)
85
+ * **Serving-side, not the dispatch edge.** Production hostname→worker
86
+ * routing is the WFP dispatch Worker's KV map (`hostname → workerName`
87
+ * only — see `docs/recipes/ops/wfp-dispatch-custom-domain.md` and
88
+ * `createWfpReleaseDeploy` / `deterministicWorkerName`). That edge has
89
+ * **no board domain data** (no Convex, no primaryDomain). Inventing a
90
+ * second edge datastore for canonical hosts is forbidden by the ticket.
91
+ *
92
+ * Data source: the board's public context already carries
93
+ * `primaryDomain: string | null` (`GET /v1/boards/:id` via
94
+ * `serializeBoardContext`). The starter loads context for every request
95
+ * (theme, features, analytics) — reuse that field. When `primaryDomain`
96
+ * is set, a request whose Host is a `*.cavuno.app` serving host 308s to
97
+ * `https://<primaryDomain><path+query>` in **one hop**. Domainless boards
98
+ * (`primaryDomain === null`) keep **serving** from cavuno.app (no redirect).
99
+ *
100
+ * Wire this helper into the starter's root middleware / entry (the
101
+ * migration starter's host check) with `Response.redirect(url, 308)`.
102
+ * Pure so unit tests pin behaviour without a Worker runtime.
103
+ *
104
+ * Pattern: Vercel `*.vercel.app` / GitHub `github.io` permanent fallback
105
+ * origin — SEO-neutral 308 to the canonical production host.
106
+ */
107
+ /**
108
+ * True when the request host is a cavuno.app board-serving host
109
+ * (slug or board-hash subdomain), not the apex and not a preview host
110
+ * we deliberately leave alone.
111
+ */
112
+ declare function isCavunoAppServingHost(hostname: string): boolean;
113
+ /**
114
+ * When a custom-domain board is hit on its cavuno.app fallback origin,
115
+ * return the one-hop 308 Location to the canonical custom domain.
116
+ * Domainless boards (no primaryDomain) return null — serve in place.
117
+ */
118
+ declare function getCavunoAppCanonicalRedirectUrl(params: {
119
+ currentHost: string | null;
120
+ /**
121
+ * From `board.context().primaryDomain` — the board's active primary
122
+ * custom domain hostname, or null when domainless.
123
+ */
124
+ primaryDomain: string | null | undefined;
125
+ /** Full request URL or path+query; path+query is preserved on the hop. */
126
+ requestUrl?: string | null;
127
+ defaultPath?: string;
128
+ }): string | null;
129
+
78
130
  /**
79
131
  * Single-flight session refresh — dedupes concurrent refreshes for the same
80
132
  * session WITHIN one process/isolate (the rotation race, ADR-0057 wart #4,
@@ -109,4 +161,4 @@ declare function currentPathFromReferer(referer: string | null): string;
109
161
  */
110
162
  declare function createSessionRefresher(board: Pick<BoardSdk, 'auth' | 'client'>): (session: BoardSession) => Promise<BoardSession | null>;
111
163
 
112
- export { BOARD_ACCESS_COOKIE_NAME, type BoardSession, SESSION_COOKIE_NAME, clearGrantCookie, clearSessionCookie, createSessionRefresher, currentPathFromReferer, grantCookieName, isExpiringSoon, parseGrantCookie, parseSessionCookie, safeRedirectPath, serializeGrantCookie, serializeSessionCookie, sessionCookieName };
164
+ export { BOARD_ACCESS_COOKIE_NAME, type BoardSession, SESSION_COOKIE_NAME, clearGrantCookie, clearSessionCookie, createSessionRefresher, currentPathFromReferer, getCavunoAppCanonicalRedirectUrl, grantCookieName, isCavunoAppServingHost, isExpiringSoon, parseGrantCookie, parseSessionCookie, safeRedirectPath, serializeGrantCookie, serializeSessionCookie, sessionCookieName };