@cavuno/board 4.31.0 → 4.32.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 (44) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/dist/{_spec-BD5My4zL.d.mts → _spec-BdqHycDm.d.mts} +14 -0
  3. package/dist/{_spec-BD5My4zL.d.ts → _spec-BdqHycDm.d.ts} +14 -0
  4. package/dist/bin.mjs +31 -8
  5. package/dist/{blog-Dh-Ru5QZ.d.mts → blog-CE0g5csE.d.mts} +1 -1
  6. package/dist/{blog-CcLmN5Tw.d.ts → blog-D85cD8W0.d.ts} +1 -1
  7. package/dist/{board-CF66EdYi.d.ts → board-CEsr8xMh.d.ts} +2 -2
  8. package/dist/{board-BRWS1m6D.d.mts → board-CdG7z-Vq.d.mts} +2 -2
  9. package/dist/{breadcrumbs-AEupw3fi.d.mts → breadcrumbs-B3n9k95M.d.mts} +2 -2
  10. package/dist/{breadcrumbs-Bu1lN6qA.d.ts → breadcrumbs-DmVbf4_6.d.ts} +2 -2
  11. package/dist/browser/cavuno-board.global.min.js +1 -1
  12. package/dist/doctor.js +31 -8
  13. package/dist/doctor.mjs +31 -8
  14. package/dist/filters.d.mts +2 -2
  15. package/dist/filters.d.ts +2 -2
  16. package/dist/format.d.mts +4 -4
  17. package/dist/format.d.ts +4 -4
  18. package/dist/index.d.mts +12 -11
  19. package/dist/index.d.ts +12 -11
  20. package/dist/index.js +6 -2
  21. package/dist/index.mjs +6 -2
  22. package/dist/{jobs-BPTMfkal.d.mts → jobs-COOBWXWV.d.mts} +1 -1
  23. package/dist/{jobs-BbtjrUo2.d.ts → jobs-DbZuagy-.d.ts} +1 -1
  24. package/dist/{search-CqPIHkv8.d.ts → search-BV2B73ty.d.ts} +1 -1
  25. package/dist/{search-CIjAdPR-.d.mts → search-BapnDodc.d.mts} +1 -1
  26. package/dist/{seo-C-NQ0T9w.d.ts → seo-BcC8Tv0d.d.ts} +1 -1
  27. package/dist/{seo-BypmVm97.d.mts → seo-C_yEotTb.d.mts} +1 -1
  28. package/dist/seo-files.d.mts +2 -2
  29. package/dist/seo-files.d.ts +2 -2
  30. package/dist/seo.d.mts +6 -6
  31. package/dist/seo.d.ts +6 -6
  32. package/dist/server.d.mts +27 -22
  33. package/dist/server.d.ts +27 -22
  34. package/dist/server.js +8 -13
  35. package/dist/server.mjs +8 -13
  36. package/dist/sitemap.d.mts +6 -6
  37. package/dist/sitemap.d.ts +6 -6
  38. package/dist/suggest.d.mts +2 -2
  39. package/dist/suggest.d.ts +2 -2
  40. package/package.json +1 -1
  41. package/skills/cavuno-board-auth/SKILL.md +1 -1
  42. package/skills/cavuno-board-server-sessions/SKILL.md +5 -5
  43. package/skills/cavuno-board-setup/SKILL.md +1 -1
  44. package/skills/manifest.json +2 -2
package/CHANGELOG.md CHANGED
@@ -3,6 +3,37 @@
3
3
  This changelog records changes that affect Board API compatibility, exported
4
4
  types, runtime behavior, or supported integration patterns.
5
5
 
6
+ ## 4.32.0 — 2026-10-02
7
+
8
+ - Registration accepts an optional `returnTo`, a same-origin path carried
9
+ through the verification email link, so someone who signs up partway
10
+ through a task (for example, applying to a job) lands back where they
11
+ started after verifying. Resending the verification email accepts the same
12
+ field: `auth.resendVerification({ body: { returnTo } })`. Unsafe values are
13
+ ignored.
14
+
15
+ - Board context includes `features.contactPage`, which matches whether the
16
+ board's public contact form is live, so a frontend can decide whether to
17
+ link to the contact page without a separate request.
18
+
19
+ - Candidate offers include `entitlements` (`listings`, `matches`,
20
+ `job_alerts`), the candidate permissions each offer grants. Offers without
21
+ configured permissions grant full access.
22
+
23
+ ## 4.31.1 — 2026-10-01
24
+
25
+ - `createSessionRefresher` no longer shares an in-flight refresh between
26
+ requests. On Cloudflare Workers, a refresh started by a request that was
27
+ then cancelled could stay pending forever, and later requests for that
28
+ session waited on it. Each call now performs its own refresh; the refresh
29
+ endpoint returns the same successor refresh token for a token re-presented
30
+ within a short grace window, so concurrent refreshes converge on one
31
+ session. To avoid duplicate refresh calls, collapse them within a single
32
+ incoming request, never across requests.
33
+
34
+ - The attribution check counts a footer backlink that React streams in a late
35
+ Suspense segment as visible, matching how browsers and crawlers render it.
36
+
6
37
  ## 4.31.0 — 2026-10-01
7
38
 
8
39
  - Board SSO supports candidate and employer sign-in through OpenID Connect and
@@ -256,6 +256,8 @@ interface components {
256
256
  marketingConsent?: boolean;
257
257
  /** @description Send the email link to this development origin instead of the board's production origin, for example `http://localhost:5173` while testing a self-hosted frontend. Only accepted with a publishable-key (`pk_...`) identifier (else 400 `board_development_origin_requires_publishable_key`), and only for a localhost development origin: email links can be requested for anyone's address, so a registered https preview returns 400 `board_development_origin_not_allowed_for_email`. It must be one of the board's development origins; otherwise the request fails with 400 `board_development_origin_not_registered`. */
258
258
  developmentOrigin?: string;
259
+ /** @description Optional same-origin path to carry through the email link. */
260
+ returnTo?: string;
259
261
  };
260
262
  BoardAuthRequestMagicLinkBody: {
261
263
  audienceAttribution?: components["schemas"]["AudienceAttribution"];
@@ -271,6 +273,10 @@ interface components {
271
273
  /** @description Send the email link to this development origin instead of the board's production origin, for example `http://localhost:5173` while testing a self-hosted frontend. Only accepted with a publishable-key (`pk_...`) identifier (else 400 `board_development_origin_requires_publishable_key`), and only for a localhost development origin: email links can be requested for anyone's address, so a registered https preview returns 400 `board_development_origin_not_allowed_for_email`. It must be one of the board's development origins; otherwise the request fails with 400 `board_development_origin_not_registered`. */
272
274
  developmentOrigin?: string;
273
275
  };
276
+ BoardAuthResendVerificationBody: {
277
+ /** @description Optional same-origin path to carry through the email link. */
278
+ returnTo?: string;
279
+ };
274
280
  BoardAuthResetPasswordBody: {
275
281
  token: string;
276
282
  /** @description Minimum 8 characters. */
@@ -2007,6 +2013,12 @@ interface components {
2007
2013
  intervalUnit: string | null;
2008
2014
  intervalCount: number | null;
2009
2015
  isDefault: boolean;
2016
+ /** @description The effective candidate permissions granted by this offer. Offers without configured candidate permissions grant full access. */
2017
+ entitlements?: {
2018
+ listings: boolean;
2019
+ matches: boolean;
2020
+ job_alerts: boolean;
2021
+ };
2010
2022
  };
2011
2023
  Plan: {
2012
2024
  /** @enum {string} */
@@ -2285,6 +2297,8 @@ interface components {
2285
2297
  nativeApplications: boolean;
2286
2298
  /** @description Whether applicant↔employer messaging is enabled on the board. `false` means the `me/conversations` route family rejects with `messaging_disabled` (403). Hide inbox/dock/Message CTAs. */
2287
2299
  messaging: boolean;
2300
+ /** @description Whether the board's public contact form is live. Matches `enabled` on `GET /v1/boards/{identifier}/contact`, so chrome can decide whether to link to the contact page from this context alone. When `false`, `POST /contact` rejects. */
2301
+ contactPage: boolean;
2288
2302
  };
2289
2303
  /**
2290
2304
  * @description How the operator charges employers for the candidate directory. `paid_messaging` leaves profiles fully visible and spends a credit on a first cold message; `paid_unlocks_and_messaging` also redacts directory cards and gates the opaque `/p/{id}` profile route behind an unlock credit. `null` means the operator has not chosen explicitly, in which case infer it from the published `talent_access` plans: any plan granting profile unlocks means `paid_unlocks_and_messaging`, otherwise `paid_messaging`. The paywall is inert regardless when the board publishes no talent plan. An anonymous viewer has no entitlement read to derive this from, so it ships here rather than only on `me/talent-access`.
@@ -256,6 +256,8 @@ interface components {
256
256
  marketingConsent?: boolean;
257
257
  /** @description Send the email link to this development origin instead of the board's production origin, for example `http://localhost:5173` while testing a self-hosted frontend. Only accepted with a publishable-key (`pk_...`) identifier (else 400 `board_development_origin_requires_publishable_key`), and only for a localhost development origin: email links can be requested for anyone's address, so a registered https preview returns 400 `board_development_origin_not_allowed_for_email`. It must be one of the board's development origins; otherwise the request fails with 400 `board_development_origin_not_registered`. */
258
258
  developmentOrigin?: string;
259
+ /** @description Optional same-origin path to carry through the email link. */
260
+ returnTo?: string;
259
261
  };
260
262
  BoardAuthRequestMagicLinkBody: {
261
263
  audienceAttribution?: components["schemas"]["AudienceAttribution"];
@@ -271,6 +273,10 @@ interface components {
271
273
  /** @description Send the email link to this development origin instead of the board's production origin, for example `http://localhost:5173` while testing a self-hosted frontend. Only accepted with a publishable-key (`pk_...`) identifier (else 400 `board_development_origin_requires_publishable_key`), and only for a localhost development origin: email links can be requested for anyone's address, so a registered https preview returns 400 `board_development_origin_not_allowed_for_email`. It must be one of the board's development origins; otherwise the request fails with 400 `board_development_origin_not_registered`. */
272
274
  developmentOrigin?: string;
273
275
  };
276
+ BoardAuthResendVerificationBody: {
277
+ /** @description Optional same-origin path to carry through the email link. */
278
+ returnTo?: string;
279
+ };
274
280
  BoardAuthResetPasswordBody: {
275
281
  token: string;
276
282
  /** @description Minimum 8 characters. */
@@ -2007,6 +2013,12 @@ interface components {
2007
2013
  intervalUnit: string | null;
2008
2014
  intervalCount: number | null;
2009
2015
  isDefault: boolean;
2016
+ /** @description The effective candidate permissions granted by this offer. Offers without configured candidate permissions grant full access. */
2017
+ entitlements?: {
2018
+ listings: boolean;
2019
+ matches: boolean;
2020
+ job_alerts: boolean;
2021
+ };
2010
2022
  };
2011
2023
  Plan: {
2012
2024
  /** @enum {string} */
@@ -2285,6 +2297,8 @@ interface components {
2285
2297
  nativeApplications: boolean;
2286
2298
  /** @description Whether applicant↔employer messaging is enabled on the board. `false` means the `me/conversations` route family rejects with `messaging_disabled` (403). Hide inbox/dock/Message CTAs. */
2287
2299
  messaging: boolean;
2300
+ /** @description Whether the board's public contact form is live. Matches `enabled` on `GET /v1/boards/{identifier}/contact`, so chrome can decide whether to link to the contact page from this context alone. When `false`, `POST /contact` rejects. */
2301
+ contactPage: boolean;
2288
2302
  };
2289
2303
  /**
2290
2304
  * @description How the operator charges employers for the candidate directory. `paid_messaging` leaves profiles fully visible and spends a credit on a first cold message; `paid_unlocks_and_messaging` also redacts directory cards and gates the opaque `/p/{id}` profile route behind an unlock credit. `null` means the operator has not chosen explicitly, in which case infer it from the published `talent_access` plans: any plan granting profile unlocks means `paid_unlocks_and_messaging`, otherwise `paid_messaging`. The paywall is inert regardless when the board publishes no talent plan. An anonymous viewer has no entitlement read to derive this from, so it ships here rather than only on `me/talent-access`.
package/dist/bin.mjs CHANGED
@@ -8249,8 +8249,29 @@ var NON_RENDERED = /* @__PURE__ */ new Set([
8249
8249
  function attribute(node, name) {
8250
8250
  return node.attrs.find((attr) => attr.name === name)?.value;
8251
8251
  }
8252
- function hidden(node) {
8253
- if (NON_RENDERED.has(node.tagName) || attribute(node, "hidden") !== void 0 || attribute(node, "inert") !== void 0 || attribute(node, "aria-hidden")?.toLowerCase() === "true")
8252
+ var STREAMED_REVEAL = /\$R[CR]\(\s*"[^"]+"\s*,\s*"([^"]+)"|\$RS\(\s*"([^"]+)"/g;
8253
+ function revealedSegmentIds(root) {
8254
+ const ids = /* @__PURE__ */ new Set();
8255
+ function visit(node) {
8256
+ if ("tagName" in node && node.tagName === "script") {
8257
+ for (const child of node.childNodes) {
8258
+ if (child.nodeName !== "#text") continue;
8259
+ const text = child.value;
8260
+ for (const match of text.matchAll(STREAMED_REVEAL)) {
8261
+ ids.add(match[1] ?? match[2]);
8262
+ }
8263
+ }
8264
+ return;
8265
+ }
8266
+ if ("childNodes" in node) node.childNodes.forEach(visit);
8267
+ }
8268
+ visit(root);
8269
+ return ids;
8270
+ }
8271
+ function hidden(node, revealed) {
8272
+ const id = attribute(node, "id");
8273
+ const streamedSegment = node.tagName === "div" && id !== void 0 && revealed.has(id);
8274
+ if (NON_RENDERED.has(node.tagName) || attribute(node, "hidden") !== void 0 && !streamedSegment || attribute(node, "inert") !== void 0 || attribute(node, "aria-hidden")?.toLowerCase() === "true")
8254
8275
  return true;
8255
8276
  const style = (attribute(node, "style") ?? "").replace(
8256
8277
  /\/\*[\s\S]*?\*\//g,
@@ -8263,24 +8284,26 @@ function hidden(node) {
8263
8284
  return property === "display" && value === "none" || property === "visibility" && ["hidden", "collapse"].includes(value) || property === "opacity" && value !== "" && Number(value.replace(/%$/, "")) === 0;
8264
8285
  });
8265
8286
  }
8266
- function hasContent(node) {
8287
+ function hasContent(node, revealed) {
8267
8288
  if (node.nodeName === "#text")
8268
8289
  return /\S/.test(node.value);
8269
8290
  if ("tagName" in node) {
8270
- if (hidden(node)) return false;
8291
+ if (hidden(node, revealed)) return false;
8271
8292
  if (node.tagName === "img") return Boolean(attribute(node, "alt")?.trim());
8272
8293
  if (node.tagName === "svg" && attribute(node, "aria-label")?.trim())
8273
8294
  return true;
8274
8295
  }
8275
- return "childNodes" in node && node.childNodes.some(hasContent);
8296
+ return "childNodes" in node && node.childNodes.some((child) => hasContent(child, revealed));
8276
8297
  }
8277
8298
  function hasCrawlableCavunoBacklink(html) {
8299
+ const root = parse(html);
8300
+ const revealed = revealedSegmentIds(root);
8278
8301
  function visit(node) {
8279
8302
  if ("tagName" in node) {
8280
- if (hidden(node)) return false;
8303
+ if (hidden(node, revealed)) return false;
8281
8304
  if (node.tagName === "a") {
8282
8305
  const href = attribute(node, "href");
8283
- if (href !== void 0 && hasContent(node)) {
8306
+ if (href !== void 0 && hasContent(node, revealed)) {
8284
8307
  try {
8285
8308
  const url = new URL(href);
8286
8309
  if (["http:", "https:"].includes(url.protocol) && ["cavuno.com", "www.cavuno.com"].includes(url.hostname))
@@ -8292,7 +8315,7 @@ function hasCrawlableCavunoBacklink(html) {
8292
8315
  }
8293
8316
  return "childNodes" in node && node.childNodes.some(visit);
8294
8317
  }
8295
- return visit(parse(html));
8318
+ return visit(root);
8296
8319
  }
8297
8320
 
8298
8321
  // src/doctor/read.ts
@@ -1,4 +1,4 @@
1
- import { S as Schemas } from './_spec-BD5My4zL.mjs';
1
+ import { S as Schemas } from './_spec-BdqHycDm.mjs';
2
2
 
3
3
  /** Author shape embedded on posts (no `object` discriminator). */
4
4
  type BlogAuthorEmbed = Schemas['PublicBlogAuthorEmbed'];
@@ -1,4 +1,4 @@
1
- import { S as Schemas } from './_spec-BD5My4zL.js';
1
+ import { S as Schemas } from './_spec-BdqHycDm.js';
2
2
 
3
3
  /** Author shape embedded on posts (no `object` discriminator). */
4
4
  type BlogAuthorEmbed = Schemas['PublicBlogAuthorEmbed'];
@@ -1,5 +1,5 @@
1
- import { S as Schemas } from './_spec-BD5My4zL.js';
2
- import { L as ListEnvelope, s as RelatedSearch, a as CustomFieldFilter, O as ObjectReferenceFilter } from './jobs-BbtjrUo2.js';
1
+ import { S as Schemas } from './_spec-BdqHycDm.js';
2
+ import { L as ListEnvelope, s as RelatedSearch, a as CustomFieldFilter, O as ObjectReferenceFilter } from './jobs-DbZuagy-.js';
3
3
 
4
4
  type PublicCompany = Schemas['CompanyPublic'];
5
5
  /** A market (sector) a company operates in — name + its source slug. */
@@ -1,5 +1,5 @@
1
- import { S as Schemas } from './_spec-BD5My4zL.mjs';
2
- import { L as ListEnvelope, s as RelatedSearch, a as CustomFieldFilter, O as ObjectReferenceFilter } from './jobs-BPTMfkal.mjs';
1
+ import { S as Schemas } from './_spec-BdqHycDm.mjs';
2
+ import { L as ListEnvelope, s as RelatedSearch, a as CustomFieldFilter, O as ObjectReferenceFilter } from './jobs-COOBWXWV.mjs';
3
3
 
4
4
  type PublicCompany = Schemas['CompanyPublic'];
5
5
  /** A market (sector) a company operates in — name + its source slug. */
@@ -1,5 +1,5 @@
1
- import { P as PublicBoard, a as CompanyCategorySalary, b as CompanySalary, L as LocationSalaryDetail, S as SkillSalaryDetail, T as TitleSalaryDetail } from './board-BRWS1m6D.mjs';
2
- import { P as PublicJob } from './jobs-BPTMfkal.mjs';
1
+ import { P as PublicBoard, a as CompanyCategorySalary, b as CompanySalary, L as LocationSalaryDetail, S as SkillSalaryDetail, T as TitleSalaryDetail } from './board-CdG7z-Vq.mjs';
2
+ import { P as PublicJob } from './jobs-COOBWXWV.mjs';
3
3
 
4
4
  /**
5
5
  * Wire enum for `salaryTimeframe`. Defined here (not re-exported from any
@@ -1,5 +1,5 @@
1
- import { P as PublicBoard, a as CompanyCategorySalary, b as CompanySalary, L as LocationSalaryDetail, S as SkillSalaryDetail, T as TitleSalaryDetail } from './board-CF66EdYi.js';
2
- import { P as PublicJob } from './jobs-BbtjrUo2.js';
1
+ import { P as PublicBoard, a as CompanyCategorySalary, b as CompanySalary, L as LocationSalaryDetail, S as SkillSalaryDetail, T as TitleSalaryDetail } from './board-CEsr8xMh.js';
2
+ import { P as PublicJob } from './jobs-DbZuagy-.js';
3
3
 
4
4
  /**
5
5
  * Wire enum for `salaryTimeframe`. Defined here (not re-exported from any