@cavuno/board 4.68.0 → 4.70.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 (42) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/dist/{_spec-t2B6ioRS.d.mts → _spec-Dk7QyuoJ.d.mts} +69 -2
  3. package/dist/{_spec-t2B6ioRS.d.ts → _spec-Dk7QyuoJ.d.ts} +69 -2
  4. package/dist/{blog-W1cBNXyS.d.ts → blog-DfUNkngR.d.ts} +1 -1
  5. package/dist/{blog-B8UJJza1.d.mts → blog-Oo6YHXSE.d.mts} +1 -1
  6. package/dist/{board-On_r_ixp.d.ts → board-CfmGT0yV.d.ts} +2 -2
  7. package/dist/{board-8RMVpFBw.d.mts → board-DzBYIwKz.d.mts} +2 -2
  8. package/dist/{breadcrumbs-eV2OQqf6.d.ts → breadcrumbs-CS0nlknq.d.ts} +2 -2
  9. package/dist/{breadcrumbs-BgBj_AiZ.d.mts → breadcrumbs-DeIA0RQp.d.mts} +2 -2
  10. package/dist/browser/cavuno-board.global.min.js +1 -1
  11. package/dist/{chunk-ZA5TZ6UH.mjs → chunk-UIKN2ERI.mjs} +3 -0
  12. package/dist/filters.d.mts +2 -2
  13. package/dist/filters.d.ts +2 -2
  14. package/dist/format.d.mts +4 -4
  15. package/dist/format.d.ts +4 -4
  16. package/dist/index.d.mts +89 -38
  17. package/dist/index.d.ts +89 -38
  18. package/dist/index.js +60 -3
  19. package/dist/index.mjs +58 -4
  20. package/dist/{jobs-CrGBikhJ.d.mts → jobs-BMRvZlNx.d.mts} +1 -1
  21. package/dist/{jobs-D_LjWgIk.d.ts → jobs-BlAsTCQl.d.ts} +1 -1
  22. package/dist/{search-Yw5qsooq.d.mts → search-BB1TaaLa.d.mts} +1 -1
  23. package/dist/{search-ClbqUjkA.d.ts → search-CoBeDgYT.d.ts} +1 -1
  24. package/dist/{seo-CY5Vl13y.d.ts → seo-BmqJ_ChV.d.ts} +1 -1
  25. package/dist/{seo-Bjevxvzd.d.mts → seo-M_8j5vdP.d.mts} +1 -1
  26. package/dist/seo-files.d.mts +2 -2
  27. package/dist/seo-files.d.ts +2 -2
  28. package/dist/seo.d.mts +6 -6
  29. package/dist/seo.d.ts +6 -6
  30. package/dist/server.d.mts +6 -6
  31. package/dist/server.d.ts +6 -6
  32. package/dist/server.mjs +1 -1
  33. package/dist/sitemap.d.mts +6 -6
  34. package/dist/sitemap.d.ts +6 -6
  35. package/dist/sitemap.mjs +1 -1
  36. package/dist/suggest.d.mts +2 -2
  37. package/dist/suggest.d.ts +2 -2
  38. package/package.json +1 -1
  39. package/skills/cavuno-board-account/SKILL.md +16 -0
  40. package/skills/cavuno-board-job-alerts/SKILL.md +7 -0
  41. package/skills/cavuno-board-newsletter/SKILL.md +79 -0
  42. package/skills/manifest.json +8 -1
package/dist/index.js CHANGED
@@ -81,6 +81,7 @@ var BOARD_API_ERROR_CODES = [
81
81
  // Board-user auth
82
82
  "board_auth_account_not_found",
83
83
  "board_auth_email_taken",
84
+ "board_auth_email_undeliverable",
84
85
  "board_auth_invalid_credentials",
85
86
  "board_auth_invalid_token",
86
87
  "board_auth_registration_disabled",
@@ -199,6 +200,8 @@ var BOARD_API_ERROR_CODES = [
199
200
  "employer_not_member",
200
201
  "employer_payment_required",
201
202
  "employer_pipeline_stage_not_found",
203
+ "company_custom_field_not_found",
204
+ "company_custom_field_not_editable",
202
205
  "employer_member_not_found",
203
206
  "employer_invite_not_found",
204
207
  "not_company_admin",
@@ -446,7 +449,7 @@ async function clearSession(storage) {
446
449
  }
447
450
 
448
451
  // src/version.ts
449
- var SDK_VERSION = "4.68.0";
452
+ var SDK_VERSION = "4.70.0";
450
453
 
451
454
  // src/client.ts
452
455
  function isRawBody(body) {
@@ -2262,7 +2265,10 @@ function meNamespace(client) {
2262
2265
  { ...options, method: "PATCH", body }
2263
2266
  );
2264
2267
  },
2265
- /** Retrieve profile custom fields for a company I belong to. */
2268
+ /**
2269
+ * Retrieve profile custom fields for a company I belong to, with the
2270
+ * saved files of upload fields (`customFieldMedia`).
2271
+ */
2266
2272
  retrieveCustomFields(slug, options) {
2267
2273
  return client.fetch(
2268
2274
  `/me/companies/${encodeURIComponent(slug)}/custom-fields`,
@@ -2276,6 +2282,26 @@ function meNamespace(client) {
2276
2282
  { ...options, method: "PATCH", body }
2277
2283
  );
2278
2284
  },
2285
+ /**
2286
+ * Upload one file to an owner-editable `image_gallery` or `file` field
2287
+ * of a company I belong to (up to 10 MB; galleries take JPEG, PNG, WebP
2288
+ * or GIF). The file is a draft until its `id` is saved to the field with
2289
+ * `updateCustomFields`, as the ordered list of file IDs.
2290
+ *
2291
+ * @example
2292
+ * const image = await board.me.companies.uploadCustomFieldFile('acme', 'office_photos', file);
2293
+ * await board.me.companies.updateCustomFields('acme', {
2294
+ * values: { office_photos: [...savedIds, image.id] },
2295
+ * });
2296
+ */
2297
+ uploadCustomFieldFile(slug, fieldKey, file, options) {
2298
+ const form = new FormData();
2299
+ form.append("file", file);
2300
+ return client.fetch(
2301
+ `/me/companies/${encodeURIComponent(slug)}/custom-fields/${encodeURIComponent(fieldKey)}/files`,
2302
+ { ...options, method: "POST", body: form }
2303
+ );
2304
+ },
2279
2305
  /**
2280
2306
  * Delete a company I admin (soft-delete cascade + Forager
2281
2307
  * exclusion). Resolves void on success (204).
@@ -2358,11 +2384,17 @@ function meNamespace(client) {
2358
2384
  * membership on this board; 409 `membership_seat_taken` when the company
2359
2385
  * already holds one (or has an invoice pending).
2360
2386
  *
2387
+ * By default every completed checkout redirects to `returnPath`. Pass
2388
+ * `redirectOnCompletion: 'if_required'` to keep a card buyer on the page:
2389
+ * Stripe.js calls `onComplete` instead, and only a payment method that
2390
+ * needs a redirect returns to `returnPath`.
2391
+ *
2361
2392
  * @example
2362
2393
  * const kit = await board.me.companies.startMembershipCheckout('acme', {
2363
2394
  * planId,
2364
2395
  * returnPath: '/memberships',
2365
2396
  * colorMode: 'light',
2397
+ * redirectOnCompletion: 'if_required',
2366
2398
  * });
2367
2399
  */
2368
2400
  startMembershipCheckout(slug, body, options) {
@@ -2647,7 +2679,9 @@ function meNamespace(client) {
2647
2679
  * My reusable billing options for a company — active subscription slots +
2648
2680
  * remaining pre-purchased (bundle) order slots, the options a held draft
2649
2681
  * can be published with at no new charge (the checkout `billing.type:
2650
- * 'order' | 'subscription'`).
2682
+ * 'order' | 'subscription'`). `capabilities` says whether the company
2683
+ * can post at all (`canPost`), so a form can show a members-only gate
2684
+ * before the job is written.
2651
2685
  */
2652
2686
  billingOptions: {
2653
2687
  /**
@@ -4794,6 +4828,29 @@ function createBoardClient(options) {
4794
4828
  seo(options2) {
4795
4829
  return client.fetch("/seo", options2);
4796
4830
  },
4831
+ /**
4832
+ * Sign an address up for the board's email from a newsletter form. Sends
4833
+ * a confirmation email; nothing is granted until the person clicks it.
4834
+ * Always resolves `{ status: 'submitted', sendingPaused }`. Limited to 3
4835
+ * a minute per address and 10 requests a minute per IP; on a builder
4836
+ * site Cavuno hosts the IP limit counts each visitor, plus 6 sign-ups a
4837
+ * minute per visitor (429 `rate_limited`).
4838
+ *
4839
+ * @example
4840
+ * await board.subscribe({
4841
+ * email,
4842
+ * consent: true,
4843
+ * consentText: 'Send me news and offers from Acme Jobs.',
4844
+ * source: 'blog-footer',
4845
+ * });
4846
+ */
4847
+ subscribe(input, options2) {
4848
+ return client.fetch("/subscribe", {
4849
+ ...options2,
4850
+ method: "POST",
4851
+ body: input
4852
+ });
4853
+ },
4797
4854
  jobs: jobsNamespace(client),
4798
4855
  embed: embedNamespace(client),
4799
4856
  companies: companiesNamespace(client),
package/dist/index.mjs CHANGED
@@ -28,7 +28,7 @@ import {
28
28
  isSsoRequired,
29
29
  isUnauthorized,
30
30
  isValidationError
31
- } from "./chunk-ZA5TZ6UH.mjs";
31
+ } from "./chunk-UIKN2ERI.mjs";
32
32
 
33
33
  // src/query.ts
34
34
  function toSearchParams(query) {
@@ -49,7 +49,7 @@ function toSearchParams(query) {
49
49
  }
50
50
 
51
51
  // src/version.ts
52
- var SDK_VERSION = "4.68.0";
52
+ var SDK_VERSION = "4.70.0";
53
53
 
54
54
  // src/client.ts
55
55
  function isRawBody(body) {
@@ -1862,7 +1862,10 @@ function meNamespace(client) {
1862
1862
  { ...options, method: "PATCH", body }
1863
1863
  );
1864
1864
  },
1865
- /** Retrieve profile custom fields for a company I belong to. */
1865
+ /**
1866
+ * Retrieve profile custom fields for a company I belong to, with the
1867
+ * saved files of upload fields (`customFieldMedia`).
1868
+ */
1866
1869
  retrieveCustomFields(slug, options) {
1867
1870
  return client.fetch(
1868
1871
  `/me/companies/${encodeURIComponent(slug)}/custom-fields`,
@@ -1876,6 +1879,26 @@ function meNamespace(client) {
1876
1879
  { ...options, method: "PATCH", body }
1877
1880
  );
1878
1881
  },
1882
+ /**
1883
+ * Upload one file to an owner-editable `image_gallery` or `file` field
1884
+ * of a company I belong to (up to 10 MB; galleries take JPEG, PNG, WebP
1885
+ * or GIF). The file is a draft until its `id` is saved to the field with
1886
+ * `updateCustomFields`, as the ordered list of file IDs.
1887
+ *
1888
+ * @example
1889
+ * const image = await board.me.companies.uploadCustomFieldFile('acme', 'office_photos', file);
1890
+ * await board.me.companies.updateCustomFields('acme', {
1891
+ * values: { office_photos: [...savedIds, image.id] },
1892
+ * });
1893
+ */
1894
+ uploadCustomFieldFile(slug, fieldKey, file, options) {
1895
+ const form = new FormData();
1896
+ form.append("file", file);
1897
+ return client.fetch(
1898
+ `/me/companies/${encodeURIComponent(slug)}/custom-fields/${encodeURIComponent(fieldKey)}/files`,
1899
+ { ...options, method: "POST", body: form }
1900
+ );
1901
+ },
1879
1902
  /**
1880
1903
  * Delete a company I admin (soft-delete cascade + Forager
1881
1904
  * exclusion). Resolves void on success (204).
@@ -1958,11 +1981,17 @@ function meNamespace(client) {
1958
1981
  * membership on this board; 409 `membership_seat_taken` when the company
1959
1982
  * already holds one (or has an invoice pending).
1960
1983
  *
1984
+ * By default every completed checkout redirects to `returnPath`. Pass
1985
+ * `redirectOnCompletion: 'if_required'` to keep a card buyer on the page:
1986
+ * Stripe.js calls `onComplete` instead, and only a payment method that
1987
+ * needs a redirect returns to `returnPath`.
1988
+ *
1961
1989
  * @example
1962
1990
  * const kit = await board.me.companies.startMembershipCheckout('acme', {
1963
1991
  * planId,
1964
1992
  * returnPath: '/memberships',
1965
1993
  * colorMode: 'light',
1994
+ * redirectOnCompletion: 'if_required',
1966
1995
  * });
1967
1996
  */
1968
1997
  startMembershipCheckout(slug, body, options) {
@@ -2247,7 +2276,9 @@ function meNamespace(client) {
2247
2276
  * My reusable billing options for a company — active subscription slots +
2248
2277
  * remaining pre-purchased (bundle) order slots, the options a held draft
2249
2278
  * can be published with at no new charge (the checkout `billing.type:
2250
- * 'order' | 'subscription'`).
2279
+ * 'order' | 'subscription'`). `capabilities` says whether the company
2280
+ * can post at all (`canPost`), so a form can show a members-only gate
2281
+ * before the job is written.
2251
2282
  */
2252
2283
  billingOptions: {
2253
2284
  /**
@@ -4364,6 +4395,29 @@ function createBoardClient(options) {
4364
4395
  seo(options2) {
4365
4396
  return client.fetch("/seo", options2);
4366
4397
  },
4398
+ /**
4399
+ * Sign an address up for the board's email from a newsletter form. Sends
4400
+ * a confirmation email; nothing is granted until the person clicks it.
4401
+ * Always resolves `{ status: 'submitted', sendingPaused }`. Limited to 3
4402
+ * a minute per address and 10 requests a minute per IP; on a builder
4403
+ * site Cavuno hosts the IP limit counts each visitor, plus 6 sign-ups a
4404
+ * minute per visitor (429 `rate_limited`).
4405
+ *
4406
+ * @example
4407
+ * await board.subscribe({
4408
+ * email,
4409
+ * consent: true,
4410
+ * consentText: 'Send me news and offers from Acme Jobs.',
4411
+ * source: 'blog-footer',
4412
+ * });
4413
+ */
4414
+ subscribe(input, options2) {
4415
+ return client.fetch("/subscribe", {
4416
+ ...options2,
4417
+ method: "POST",
4418
+ body: input
4419
+ });
4420
+ },
4367
4421
  jobs: jobsNamespace(client),
4368
4422
  embed: embedNamespace(client),
4369
4423
  companies: companiesNamespace(client),
@@ -1,4 +1,4 @@
1
- import { S as Schemas } from './_spec-t2B6ioRS.mjs';
1
+ import { S as Schemas } from './_spec-Dk7QyuoJ.mjs';
2
2
 
3
3
  /**
4
4
  * Optional pagination metadata returned by Board API list operations.
@@ -1,4 +1,4 @@
1
- import { S as Schemas } from './_spec-t2B6ioRS.js';
1
+ import { S as Schemas } from './_spec-Dk7QyuoJ.js';
2
2
 
3
3
  /**
4
4
  * Optional pagination metadata returned by Board API list operations.
@@ -1,4 +1,4 @@
1
- import { S as Schemas } from './_spec-t2B6ioRS.mjs';
1
+ import { S as Schemas } from './_spec-Dk7QyuoJ.mjs';
2
2
 
3
3
  type Awaitable<T> = T | Promise<T>;
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { S as Schemas } from './_spec-t2B6ioRS.js';
1
+ import { S as Schemas } from './_spec-Dk7QyuoJ.js';
2
2
 
3
3
  type Awaitable<T> = T | Promise<T>;
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { S as Schemas } from './_spec-t2B6ioRS.js';
1
+ import { S as Schemas } from './_spec-Dk7QyuoJ.js';
2
2
 
3
3
  /**
4
4
  * The public SEO-infra payload (`board.seo()`) — platform infrastructure a
@@ -1,4 +1,4 @@
1
- import { S as Schemas } from './_spec-t2B6ioRS.mjs';
1
+ import { S as Schemas } from './_spec-Dk7QyuoJ.mjs';
2
2
 
3
3
  /**
4
4
  * The public SEO-infra payload (`board.seo()`) — platform infrastructure a
@@ -1,5 +1,5 @@
1
- import { B as BoardSeo } from './seo-Bjevxvzd.mjs';
2
- import './_spec-t2B6ioRS.mjs';
1
+ import { B as BoardSeo } from './seo-M_8j5vdP.mjs';
2
+ import './_spec-Dk7QyuoJ.mjs';
3
3
 
4
4
  /**
5
5
  * `@cavuno/board/seo-files` — platform-owned crawler/identity file bodies
@@ -1,5 +1,5 @@
1
- import { B as BoardSeo } from './seo-CY5Vl13y.js';
2
- import './_spec-t2B6ioRS.js';
1
+ import { B as BoardSeo } from './seo-BmqJ_ChV.js';
2
+ import './_spec-Dk7QyuoJ.js';
3
3
 
4
4
  /**
5
5
  * `@cavuno/board/seo-files` — platform-owned crawler/identity file bodies
package/dist/seo.d.mts CHANGED
@@ -1,9 +1,9 @@
1
- import { P as PublicBlogPostSummary, B as BlogAuthorEmbed } from './blog-B8UJJza1.mjs';
2
- import { e as JsonLdObject } from './breadcrumbs-BgBj_AiZ.mjs';
3
- export { A as ALL_COUNTRY_CODES, B as BreadcrumbItemInput, g as FaqItem, J as JobBreadcrumb, h as JsonLdBoard, N as NumberNotation, i as SENIORITY_ORDER, j as SalaryFaqEntry, k as SalaryJsonLdOptions, l as SalaryOccupationListOptions, b as buildJobBreadcrumbs, m as buildSalaryFaq, o as companyCategorySalaryJsonLd, p as companySalaryJsonLd, q as createBreadcrumbJsonLd, r as createJobPostingJsonLd, s as crossAxisSalaryJsonLd, t as faqJsonLd, c as formatSalaryStat, d as formatSalaryStatRange, u as itemListJsonLd, v as locationSalaryJsonLd, n as normalizeWebsiteUrl, w as skillSalaryJsonLd, x as sortBySeniority, y as titleSalaryJsonLd } from './breadcrumbs-BgBj_AiZ.mjs';
4
- import './_spec-t2B6ioRS.mjs';
5
- import './board-8RMVpFBw.mjs';
6
- import './jobs-CrGBikhJ.mjs';
1
+ import { P as PublicBlogPostSummary, B as BlogAuthorEmbed } from './blog-Oo6YHXSE.mjs';
2
+ import { e as JsonLdObject } from './breadcrumbs-DeIA0RQp.mjs';
3
+ export { A as ALL_COUNTRY_CODES, B as BreadcrumbItemInput, g as FaqItem, J as JobBreadcrumb, h as JsonLdBoard, N as NumberNotation, i as SENIORITY_ORDER, j as SalaryFaqEntry, k as SalaryJsonLdOptions, l as SalaryOccupationListOptions, b as buildJobBreadcrumbs, m as buildSalaryFaq, o as companyCategorySalaryJsonLd, p as companySalaryJsonLd, q as createBreadcrumbJsonLd, r as createJobPostingJsonLd, s as crossAxisSalaryJsonLd, t as faqJsonLd, c as formatSalaryStat, d as formatSalaryStatRange, u as itemListJsonLd, v as locationSalaryJsonLd, n as normalizeWebsiteUrl, w as skillSalaryJsonLd, x as sortBySeniority, y as titleSalaryJsonLd } from './breadcrumbs-DeIA0RQp.mjs';
4
+ import './_spec-Dk7QyuoJ.mjs';
5
+ import './board-DzBYIwKz.mjs';
6
+ import './jobs-BMRvZlNx.mjs';
7
7
 
8
8
  /**
9
9
  * Blog structured data on the `@cavuno/board` wire types, transcribed from
package/dist/seo.d.ts CHANGED
@@ -1,9 +1,9 @@
1
- import { P as PublicBlogPostSummary, B as BlogAuthorEmbed } from './blog-W1cBNXyS.js';
2
- import { e as JsonLdObject } from './breadcrumbs-eV2OQqf6.js';
3
- export { A as ALL_COUNTRY_CODES, B as BreadcrumbItemInput, g as FaqItem, J as JobBreadcrumb, h as JsonLdBoard, N as NumberNotation, i as SENIORITY_ORDER, j as SalaryFaqEntry, k as SalaryJsonLdOptions, l as SalaryOccupationListOptions, b as buildJobBreadcrumbs, m as buildSalaryFaq, o as companyCategorySalaryJsonLd, p as companySalaryJsonLd, q as createBreadcrumbJsonLd, r as createJobPostingJsonLd, s as crossAxisSalaryJsonLd, t as faqJsonLd, c as formatSalaryStat, d as formatSalaryStatRange, u as itemListJsonLd, v as locationSalaryJsonLd, n as normalizeWebsiteUrl, w as skillSalaryJsonLd, x as sortBySeniority, y as titleSalaryJsonLd } from './breadcrumbs-eV2OQqf6.js';
4
- import './_spec-t2B6ioRS.js';
5
- import './board-On_r_ixp.js';
6
- import './jobs-D_LjWgIk.js';
1
+ import { P as PublicBlogPostSummary, B as BlogAuthorEmbed } from './blog-DfUNkngR.js';
2
+ import { e as JsonLdObject } from './breadcrumbs-CS0nlknq.js';
3
+ export { A as ALL_COUNTRY_CODES, B as BreadcrumbItemInput, g as FaqItem, J as JobBreadcrumb, h as JsonLdBoard, N as NumberNotation, i as SENIORITY_ORDER, j as SalaryFaqEntry, k as SalaryJsonLdOptions, l as SalaryOccupationListOptions, b as buildJobBreadcrumbs, m as buildSalaryFaq, o as companyCategorySalaryJsonLd, p as companySalaryJsonLd, q as createBreadcrumbJsonLd, r as createJobPostingJsonLd, s as crossAxisSalaryJsonLd, t as faqJsonLd, c as formatSalaryStat, d as formatSalaryStatRange, u as itemListJsonLd, v as locationSalaryJsonLd, n as normalizeWebsiteUrl, w as skillSalaryJsonLd, x as sortBySeniority, y as titleSalaryJsonLd } from './breadcrumbs-CS0nlknq.js';
4
+ import './_spec-Dk7QyuoJ.js';
5
+ import './board-CfmGT0yV.js';
6
+ import './jobs-BlAsTCQl.js';
7
7
 
8
8
  /**
9
9
  * Blog structured data on the `@cavuno/board` wire types, transcribed from
package/dist/server.d.mts CHANGED
@@ -1,10 +1,10 @@
1
1
  import { BoardSdk } from './index.mjs';
2
- import './_spec-t2B6ioRS.mjs';
3
- import './search-Yw5qsooq.mjs';
4
- import './jobs-CrGBikhJ.mjs';
5
- import './board-8RMVpFBw.mjs';
6
- import './seo-Bjevxvzd.mjs';
7
- import './blog-B8UJJza1.mjs';
2
+ import './_spec-Dk7QyuoJ.mjs';
3
+ import './search-BB1TaaLa.mjs';
4
+ import './jobs-BMRvZlNx.mjs';
5
+ import './board-DzBYIwKz.mjs';
6
+ import './seo-M_8j5vdP.mjs';
7
+ import './blog-Oo6YHXSE.mjs';
8
8
 
9
9
  /**
10
10
  * Session cookie codec — pure (no framework imports, no node imports) so it
package/dist/server.d.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  import { BoardSdk } from './index.js';
2
- import './_spec-t2B6ioRS.js';
3
- import './search-ClbqUjkA.js';
4
- import './jobs-D_LjWgIk.js';
5
- import './board-On_r_ixp.js';
6
- import './seo-CY5Vl13y.js';
7
- import './blog-W1cBNXyS.js';
2
+ import './_spec-Dk7QyuoJ.js';
3
+ import './search-CoBeDgYT.js';
4
+ import './jobs-BlAsTCQl.js';
5
+ import './board-CfmGT0yV.js';
6
+ import './seo-BmqJ_ChV.js';
7
+ import './blog-DfUNkngR.js';
8
8
 
9
9
  /**
10
10
  * Session cookie codec — pure (no framework imports, no node imports) so it
package/dist/server.mjs CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-Z4ALWIGS.mjs";
5
5
  import {
6
6
  isUnauthorized
7
- } from "./chunk-ZA5TZ6UH.mjs";
7
+ } from "./chunk-UIKN2ERI.mjs";
8
8
 
9
9
  // src/server/cookie.ts
10
10
  var COOKIE_ATTRIBUTES = "Path=/; HttpOnly; Secure; SameSite=Lax";
@@ -1,10 +1,10 @@
1
1
  import { BoardSdk } from './index.mjs';
2
- import './_spec-t2B6ioRS.mjs';
3
- import './search-Yw5qsooq.mjs';
4
- import './jobs-CrGBikhJ.mjs';
5
- import './board-8RMVpFBw.mjs';
6
- import './seo-Bjevxvzd.mjs';
7
- import './blog-B8UJJza1.mjs';
2
+ import './_spec-Dk7QyuoJ.mjs';
3
+ import './search-BB1TaaLa.mjs';
4
+ import './jobs-BMRvZlNx.mjs';
5
+ import './board-DzBYIwKz.mjs';
6
+ import './seo-M_8j5vdP.mjs';
7
+ import './blog-Oo6YHXSE.mjs';
8
8
 
9
9
  /**
10
10
  * Sitemap primitives — the pure XML + bucket-filename logic behind a board
package/dist/sitemap.d.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  import { BoardSdk } from './index.js';
2
- import './_spec-t2B6ioRS.js';
3
- import './search-ClbqUjkA.js';
4
- import './jobs-D_LjWgIk.js';
5
- import './board-On_r_ixp.js';
6
- import './seo-CY5Vl13y.js';
7
- import './blog-W1cBNXyS.js';
2
+ import './_spec-Dk7QyuoJ.js';
3
+ import './search-CoBeDgYT.js';
4
+ import './jobs-BlAsTCQl.js';
5
+ import './board-CfmGT0yV.js';
6
+ import './seo-BmqJ_ChV.js';
7
+ import './blog-DfUNkngR.js';
8
8
 
9
9
  /**
10
10
  * Sitemap primitives — the pure XML + bucket-filename logic behind a board
package/dist/sitemap.mjs CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-DLSO6XXY.mjs";
4
4
  import {
5
5
  isNotFound
6
- } from "./chunk-ZA5TZ6UH.mjs";
6
+ } from "./chunk-UIKN2ERI.mjs";
7
7
  import {
8
8
  BOARD_PATHS,
9
9
  blogAuthorPath,
@@ -1,5 +1,5 @@
1
- import { h as SuggestionItem, b as SearchSuggestQuery, F as FetchOptions, g as SuggestResult } from './search-Yw5qsooq.mjs';
2
- import './_spec-t2B6ioRS.mjs';
1
+ import { h as SuggestionItem, b as SearchSuggestQuery, F as FetchOptions, g as SuggestResult } from './search-BB1TaaLa.mjs';
2
+ import './_spec-Dk7QyuoJ.mjs';
3
3
 
4
4
  /**
5
5
  * `@cavuno/board/suggest` — framework-agnostic headless search-suggest
package/dist/suggest.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { h as SuggestionItem, b as SearchSuggestQuery, F as FetchOptions, g as SuggestResult } from './search-ClbqUjkA.js';
2
- import './_spec-t2B6ioRS.js';
1
+ import { h as SuggestionItem, b as SearchSuggestQuery, F as FetchOptions, g as SuggestResult } from './search-CoBeDgYT.js';
2
+ import './_spec-Dk7QyuoJ.js';
3
3
 
4
4
  /**
5
5
  * `@cavuno/board/suggest` — framework-agnostic headless search-suggest
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cavuno/board",
3
- "version": "4.68.0",
3
+ "version": "4.70.0",
4
4
  "description": "Build custom job boards and careers pages with Cavuno’s TypeScript SDK.",
5
5
  "keywords": [
6
6
  "api-client",
@@ -217,6 +217,22 @@ await board.me.profile.updateCustomFields({
217
217
  });
218
218
  ```
219
219
 
220
+ Company members upload to the company's upload fields the same way, with the company slug first. `customFieldMedia` on `board.me.companies.retrieveCustomFields` lists the saved files.
221
+
222
+ ```ts snippet
223
+ const company = await board.me.companies.retrieveCustomFields('acme');
224
+ const photos = Array.isArray(company.values.office_photos)
225
+ ? company.values.office_photos
226
+ : [];
227
+ const photo = await board.me.companies.uploadCustomFieldFile(
228
+ 'acme',
229
+ 'office_photos',
230
+ file);
231
+ await board.me.companies.updateCustomFields('acme', {
232
+ values: { office_photos: [...photos, photo.id] },
233
+ });
234
+ ```
235
+
220
236
  Resume upload starts an asynchronous parse that may populate the profile. Poll within a fixed budget, surface failure, and keep manual editing available if parsing runs long.
221
237
 
222
238
  ```ts snippet
@@ -32,14 +32,21 @@ const result = await board.jobAlerts.confirm({ token });
32
32
  switch (result.status) {
33
33
  case 'confirmed':
34
34
  case 'already_confirmed':
35
+ case 'waitlisted':
35
36
  case 'not_found':
36
37
  break;
38
+ case 'unsubscribed':
39
+ // Show the manage page's resume option; the subscribe form sends this
40
+ // address no confirmation email.
41
+ break;
37
42
  case 'expired':
38
43
  await board.jobAlerts.resendConfirmation({ email });
39
44
  break;
40
45
  }
41
46
  ```
42
47
 
48
+ `waitlisted` means the alert is confirmed but the board can't send to more people yet; it starts once a spot opens. `unsubscribed` means the link was sent before the alerts were turned off, so it doesn't turn them back on. Subscribing again with the form sends no confirmation email to that address; the person resumes alerts from the manage link in an alert email (`board.jobAlerts.resubscribe`) or by signing in and creating an alert.
49
+
43
50
  Only `jobFunctions`, `placeSlugs`, and `remoteOptions` filter digest delivery. Seniority and salary fields are stored but do not scope delivery. The supported cadence is `weekly`.
44
51
 
45
52
  `jobFunctions` are category and skill slugs (the `slug` of a category or skill, such as `engineering` or `media-and-communication`). A digest matches a job when any entry equals one of the job's category or skill slugs. Prefer sending slugs. If you send display names or free-text keywords instead, the server slugifies each one when the alert is saved and matches it against the board's category and skill slugs, their aliases, and their board-language slugs. A display name that does not slugify to one of those (for example, a renamed category) is stored slugified and will not match, so send slugs whenever you have them. Alerts read back from the API therefore always carry slugs, so compare and display them as slugs.
@@ -0,0 +1,79 @@
1
+ ---
2
+ name: cavuno-board-newsletter
3
+ description: Newsletter sign-up forms with @cavuno/board. Use for an email sign-up for the board's news (footer, blog, landing page), its consent checkbox, the confirmation email, and sendingPaused.
4
+ ---
5
+
6
+ # Newsletter sign-up
7
+
8
+ `board.subscribe` signs an address up for the board's own email (news, offers, updates). It is separate from job alerts (`board.jobAlerts.subscribe`): use it for a form whose promise is "get our news", not "get matching jobs".
9
+
10
+ Every sign-up is double opt-in. The person gets a confirmation email, and nothing is sent to them until they click it. The response is the same whether the address is new, waiting to confirm, or already subscribed, so it never tells a visitor who is on the list.
11
+
12
+ ## The form
13
+
14
+ - An **unticked** checkbox the person must tick. Its wording must name the board, for example "Send me news and offers from Acme Jobs." Send that exact wording as `consentText`; it is kept as the record of what they agreed to, and a `consentText` without the board's name is refused with a 400.
15
+ - A `source` label for the form, such as `blog-footer` or `homepage-hero` (lowercase letters, digits and hyphens). It shows as the person's Source in the board's dashboard, so give each form its own.
16
+ - A hidden `website` field people leave empty. Bots fill it in; the API then answers as usual and does nothing.
17
+
18
+ Read the board's name from `board.context()` rather than typing it.
19
+
20
+ ```ts snippet
21
+ const { name } = await board.context();
22
+ const consentText = `Send me news and offers from ${name}.`;
23
+
24
+ const result = await board.subscribe({
25
+ email,
26
+ consent: true,
27
+ consentText,
28
+ source: 'blog-footer',
29
+ website: honeypotValue,
30
+ });
31
+
32
+ if (result.sendingPaused) {
33
+ // Recorded, but the board's email is paused for now.
34
+ }
35
+ ```
36
+
37
+ `pageUrl` is optional; it defaults to the request's `Referer`. Pass it when the call is made from your server, where the `Referer` is not the visitor's page.
38
+
39
+ ## After submitting
40
+
41
+ Always show the same message: "Check your inbox to confirm your subscription." Never show "you're already subscribed"; the API deliberately doesn't say.
42
+
43
+ When `sendingPaused` is `true`, add a short note that emails from the board may be delayed. The sign-up is still recorded.
44
+
45
+ ## Confirmation
46
+
47
+ The confirmation email links to the board's job alert confirmation page, the same page job alerts use. That page completes the sign-up with `board.jobAlerts.confirm({ token })`; no separate route is needed. `confirmed` and `already_confirmed` mean the person is subscribed, and `not_found` or `expired` mean the link no longer works.
48
+
49
+ The page serves both kinds of link, so word it by `subscription`. A newsletter link answers `subscription: 'marketing'` and never turns job alerts on, so don't tell that person their job alerts are confirmed:
50
+
51
+ ```ts
52
+ const result = await board.jobAlerts.confirm({ token });
53
+ const subscribed =
54
+ result.status === 'confirmed' || result.status === 'already_confirmed';
55
+ const message = !subscribed
56
+ ? 'This link no longer works.'
57
+ : result.subscription === 'marketing'
58
+ ? "You're subscribed to our emails."
59
+ : 'Your job alert is confirmed.';
60
+ ```
61
+
62
+ ## Errors
63
+
64
+ - `validation_bad_request` (400): a field is missing or malformed, `consent` isn't `true`, or `consentText` doesn't name the board.
65
+ - `rate_limited` (429): 3 sign-ups a minute per address across all boards, or 10 requests a minute per IP. A self-hosted site calls from its own server, so all its visitors share that IP limit with every other sign-in and sign-up call from that server; on a builder site Cavuno hosts it counts each visitor separately, plus 6 sign-ups a minute per visitor. `details.retryAfterSeconds` says when to try again.
66
+ - `rate_limit_unavailable` (503): retry shortly.
67
+
68
+ ```ts snippet
69
+ import { BoardApiError } from '@cavuno/board';
70
+
71
+ try {
72
+ await board.subscribe({ email, consent: true, consentText, source: 'footer' });
73
+ } catch (error) {
74
+ if (error instanceof BoardApiError && error.status === 429) {
75
+ // Ask them to try again in a minute.
76
+ }
77
+ throw error;
78
+ }
79
+ ```
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "4.68.0",
2
+ "version": "4.70.0",
3
3
  "skills": [
4
4
  {
5
5
  "name": "cavuno-board-account",
@@ -106,6 +106,13 @@
106
106
  "framework": null,
107
107
  "category": "core"
108
108
  },
109
+ {
110
+ "name": "cavuno-board-newsletter",
111
+ "description": "Newsletter sign-up forms with @cavuno/board. Use for an email sign-up for the board's news (footer, blog, landing page), its consent checkbox, the confirmation email, and sendingPaused.",
112
+ "path": "skills/cavuno-board-newsletter/SKILL.md",
113
+ "framework": null,
114
+ "category": "core"
115
+ },
109
116
  {
110
117
  "name": "cavuno-board-paywall",
111
118
  "description": "Grant-is-truth candidate paywall with @cavuno/board. Use for offers, gated job counts, embedded checkout handoff, access confirmation, or subscription portal links.",