@cavuno/board 4.30.0 → 4.31.1

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 (46) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/dist/{_spec-CuSpfit_.d.mts → _spec-BD5My4zL.d.mts} +76 -0
  3. package/dist/{_spec-CuSpfit_.d.ts → _spec-BD5My4zL.d.ts} +76 -0
  4. package/dist/bin.mjs +31 -8
  5. package/dist/{blog-CvQeoBc8.d.ts → blog-CcLmN5Tw.d.ts} +1 -1
  6. package/dist/{blog-21XnewAu.d.mts → blog-Dh-Ru5QZ.d.mts} +1 -1
  7. package/dist/{board-t_j16FrX.d.ts → board-BRWS1m6D.d.mts} +16 -3
  8. package/dist/{board-VrjkKuZ-.d.mts → board-CF66EdYi.d.ts} +16 -3
  9. package/dist/{breadcrumbs-BpONykkn.d.mts → breadcrumbs-AEupw3fi.d.mts} +2 -2
  10. package/dist/{breadcrumbs-CJ5eu_ye.d.ts → breadcrumbs-Bu1lN6qA.d.ts} +2 -2
  11. package/dist/browser/cavuno-board.global.min.js +1 -1
  12. package/dist/{chunk-EHSB2VAJ.mjs → chunk-WSO6JN5C.mjs} +37 -1
  13. package/dist/doctor.js +31 -8
  14. package/dist/doctor.mjs +31 -8
  15. package/dist/filters.d.mts +2 -2
  16. package/dist/filters.d.ts +2 -2
  17. package/dist/format.d.mts +4 -4
  18. package/dist/format.d.ts +4 -4
  19. package/dist/index.d.mts +238 -13
  20. package/dist/index.d.ts +238 -13
  21. package/dist/index.js +250 -5
  22. package/dist/index.mjs +222 -6
  23. package/dist/{jobs-h-ewgbXn.d.mts → jobs-BPTMfkal.d.mts} +1 -1
  24. package/dist/{jobs-CErce8Tw.d.ts → jobs-BbtjrUo2.d.ts} +1 -1
  25. package/dist/{search-BefsJD_f.d.mts → search-CIjAdPR-.d.mts} +1 -1
  26. package/dist/{search-B2LdM6bj.d.ts → search-CqPIHkv8.d.ts} +1 -1
  27. package/dist/{seo-BFQIA81t.d.mts → seo-BypmVm97.d.mts} +1 -1
  28. package/dist/{seo-BPDNxOiZ.d.ts → seo-C-NQ0T9w.d.ts} +1 -1
  29. package/dist/seo-files.d.mts +2 -2
  30. package/dist/seo-files.d.ts +2 -2
  31. package/dist/seo.d.mts +6 -6
  32. package/dist/seo.d.ts +6 -6
  33. package/dist/server.d.mts +27 -22
  34. package/dist/server.d.ts +27 -22
  35. package/dist/server.js +8 -13
  36. package/dist/server.mjs +9 -14
  37. package/dist/sitemap.d.mts +6 -6
  38. package/dist/sitemap.d.ts +6 -6
  39. package/dist/sitemap.mjs +1 -1
  40. package/dist/suggest.d.mts +2 -2
  41. package/dist/suggest.d.ts +2 -2
  42. package/package.json +1 -1
  43. package/skills/cavuno-board-auth/SKILL.md +1 -1
  44. package/skills/cavuno-board-server-sessions/SKILL.md +5 -5
  45. package/skills/cavuno-board-setup/SKILL.md +1 -1
  46. 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.31.1 — 2026-10-01
7
+
8
+ - `createSessionRefresher` no longer shares an in-flight refresh between
9
+ requests. On Cloudflare Workers, a refresh started by a request that was
10
+ then cancelled could stay pending forever, and later requests for that
11
+ session waited on it. Each call now performs its own refresh; the refresh
12
+ endpoint returns the same successor refresh token for a token re-presented
13
+ within a short grace window, so concurrent refreshes converge on one
14
+ session. To avoid duplicate refresh calls, collapse them within a single
15
+ incoming request, never across requests.
16
+
17
+ - The attribution check counts a footer backlink that React streams in a late
18
+ Suspense segment as visible, matching how browsers and crawlers render it.
19
+
20
+ ## 4.31.0 — 2026-10-01
21
+
22
+ - Board SSO supports candidate and employer sign-in through OpenID Connect and
23
+ OAuth providers. Frontends can start sign-in, complete a one-time handoff, and
24
+ verify an emailed proof, with completion bound to the browser that started it.
25
+ Board context exposes the available sign-in methods for each role.
26
+
27
+ - Development origins support local and preview frontends without changing the
28
+ board's public address. Employer accounts can be deactivated and reactivated
29
+ through the Board API.
30
+
31
+ - Publish XML job feeds on your board's own domain for job aggregators, with
32
+ filters, directly posted or published job selection, and tagged job links.
33
+
34
+ - PATCH requests can clear author links, canonical URLs, and seniority values.
35
+ External apply intents respect the board's registration wall.
36
+
6
37
  ## 4.30.0 — 2026-09-29
7
38
 
8
39
  - Company profiles and board settings can include an Instagram profile link.
@@ -208,6 +208,8 @@ interface components {
208
208
  BoardAuthForgotPasswordBody: {
209
209
  /** Format: email */
210
210
  email: string;
211
+ /** @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`. */
212
+ developmentOrigin?: string;
211
213
  };
212
214
  BoardAuthLoginBody: {
213
215
  /** Format: email */
@@ -227,6 +229,8 @@ interface components {
227
229
  };
228
230
  BoardAuthOAuthExchangeBody: {
229
231
  token: string;
232
+ /** @description For SSO, the random secret established in the initiating browser before requesting authorization. Required for SSO tokens; never read it from a completion URL. */
233
+ browserBinding?: string;
230
234
  };
231
235
  BoardAuthRefreshBody: {
232
236
  refreshToken: string;
@@ -250,6 +254,8 @@ interface components {
250
254
  audienceAttribution?: components["schemas"]["AudienceAttribution"];
251
255
  /** @description True only when the person ticked a marketing checkbox your UI displayed with its disclosure wording. Omit when no checkbox was shown; false and absent both record nothing. */
252
256
  marketingConsent?: boolean;
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
+ developmentOrigin?: string;
253
259
  };
254
260
  BoardAuthRequestMagicLinkBody: {
255
261
  audienceAttribution?: components["schemas"]["AudienceAttribution"];
@@ -262,6 +268,8 @@ interface components {
262
268
  * @enum {string}
263
269
  */
264
270
  intent?: "sign_in";
271
+ /** @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
+ developmentOrigin?: string;
265
273
  };
266
274
  BoardAuthResetPasswordBody: {
267
275
  token: string;
@@ -281,9 +289,30 @@ interface components {
281
289
  /** @description Present on OAuth exchange and magic-link consume. True when this exchange created the board user account (candidate or employer); false when it authenticated an existing account. */
282
290
  isNewUser?: boolean;
283
291
  };
292
+ BoardAuthSsoAuthorizationUrl: {
293
+ /** @enum {string} */
294
+ object: "sso_authorization_url";
295
+ /** @description The SSO connection ID. */
296
+ connectionId: string;
297
+ /** Format: uri */
298
+ authorizeUrl: string;
299
+ };
300
+ BoardAuthSsoLinkProofBody: {
301
+ /** @description The `linkProof` token from the emailed link. */
302
+ token: string;
303
+ /** @description The random secret established in the initiating browser BEFORE authorization. Completion URLs contain only its hash; never save their `linkProofBinding` as the secret. Without the matching value the proof is refused with `board_auth_sso_browser_mismatch`. */
304
+ browserBinding?: string;
305
+ };
284
306
  BoardAuthVerifyEmailBody: {
285
307
  token: string;
286
308
  };
309
+ BoardContact: {
310
+ /** @enum {string} */
311
+ object: "board_contact";
312
+ /** @description Whether visitors can send the board team a message. */
313
+ enabled: boolean;
314
+ boardName: string;
315
+ };
287
316
  BoardFormBuiltinField: {
288
317
  /**
289
318
  * @description discriminator enum property added by openapi-typescript
@@ -367,6 +396,19 @@ interface components {
367
396
  definition: components["schemas"]["ProfileCustomFieldDefinition"];
368
397
  };
369
398
  BoardProfileFormField: components["schemas"]["BoardFormBuiltinField"] | components["schemas"]["BoardProfileFormCustomField"] | components["schemas"]["BoardProfileFormCollectionField"];
399
+ BoardRoleSignIn: {
400
+ /** @description Deprecated: true when only SSO connections are available. Inspect methods and ssoConnections instead. */
401
+ ssoRequired: boolean;
402
+ /** @description Which built-in sign-in methods are switched on for this role. All `false` when sign-in for the role is switched off. All `false` with SSO connections listed means the role signs in only with SSO. */
403
+ methods: {
404
+ password: boolean;
405
+ magicLink: boolean;
406
+ google: boolean;
407
+ linkedin: boolean;
408
+ };
409
+ /** @description SSO connections switched on for this role, in the order the operator created them. */
410
+ ssoConnections: components["schemas"]["BoardSignInSsoConnection"][];
411
+ };
370
412
  BoardSeo: {
371
413
  /** @enum {string} */
372
414
  object: "board_seo";
@@ -386,6 +428,19 @@ interface components {
386
428
  name: string;
387
429
  };
388
430
  };
431
+ BoardSignInSsoConnection: {
432
+ /** @description SSO connection ID. Pass it to `GET /boards/{identifier}/auth/sso/{connectionId}` to start sign-in. */
433
+ id: string;
434
+ /** @description Operator-chosen display name of the identity provider, e.g. "Acme Members". Render the button as "Continue with {label}". */
435
+ label: string;
436
+ /** @description Absolute URL of the provider logo, or `null` when unset. */
437
+ logoUrl: string | null;
438
+ /**
439
+ * @description Deprecated: infer SSO-only sign-in from methods and ssoConnections.
440
+ * @enum {string}
441
+ */
442
+ mode: "available" | "required";
443
+ };
389
444
  BoardSitemap: {
390
445
  /** @enum {string} */
391
446
  object: "board_sitemap";
@@ -758,6 +813,22 @@ interface components {
758
813
  ConfirmWorkEmailBody: {
759
814
  token: string;
760
815
  };
816
+ ContactSubmission: {
817
+ /** @enum {string} */
818
+ object: "contact_submission";
819
+ /** @enum {boolean} */
820
+ success: true;
821
+ };
822
+ ContactSubmissionBody: {
823
+ name: string;
824
+ /** Format: email */
825
+ email: string;
826
+ subject: string;
827
+ body: string;
828
+ /** Format: uuid */
829
+ requestId: string;
830
+ website?: string;
831
+ };
761
832
  Conversation: {
762
833
  id: string;
763
834
  /** @enum {string} */
@@ -2225,6 +2296,11 @@ interface components {
2225
2296
  /** @description Whether posting a job on this board requires an active membership. When `true`, render the join gate in place of the anonymous post form: the platform rejects a create from a company with no contributing assignment (`membership_required`). Absent board config defaults to `false`. This flag is board policy and is safe to read anonymously; a signed-in employer's own standing rides their company billing options. */
2226
2297
  requiresMembership: boolean;
2227
2298
  };
2299
+ /** @description Sign-in options per role: the built-in methods and SSO connections switched on for that role. Render the sign-in page from this instead of hard-coding providers; any other method is refused with `board_auth_method_unavailable`. */
2300
+ signIn: {
2301
+ candidate: components["schemas"]["BoardRoleSignIn"];
2302
+ employer: components["schemas"]["BoardRoleSignIn"];
2303
+ };
2228
2304
  analytics: {
2229
2305
  ga4MeasurementId: string | null;
2230
2306
  gtmId: string | null;
@@ -208,6 +208,8 @@ interface components {
208
208
  BoardAuthForgotPasswordBody: {
209
209
  /** Format: email */
210
210
  email: string;
211
+ /** @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`. */
212
+ developmentOrigin?: string;
211
213
  };
212
214
  BoardAuthLoginBody: {
213
215
  /** Format: email */
@@ -227,6 +229,8 @@ interface components {
227
229
  };
228
230
  BoardAuthOAuthExchangeBody: {
229
231
  token: string;
232
+ /** @description For SSO, the random secret established in the initiating browser before requesting authorization. Required for SSO tokens; never read it from a completion URL. */
233
+ browserBinding?: string;
230
234
  };
231
235
  BoardAuthRefreshBody: {
232
236
  refreshToken: string;
@@ -250,6 +254,8 @@ interface components {
250
254
  audienceAttribution?: components["schemas"]["AudienceAttribution"];
251
255
  /** @description True only when the person ticked a marketing checkbox your UI displayed with its disclosure wording. Omit when no checkbox was shown; false and absent both record nothing. */
252
256
  marketingConsent?: boolean;
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
+ developmentOrigin?: string;
253
259
  };
254
260
  BoardAuthRequestMagicLinkBody: {
255
261
  audienceAttribution?: components["schemas"]["AudienceAttribution"];
@@ -262,6 +268,8 @@ interface components {
262
268
  * @enum {string}
263
269
  */
264
270
  intent?: "sign_in";
271
+ /** @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
+ developmentOrigin?: string;
265
273
  };
266
274
  BoardAuthResetPasswordBody: {
267
275
  token: string;
@@ -281,9 +289,30 @@ interface components {
281
289
  /** @description Present on OAuth exchange and magic-link consume. True when this exchange created the board user account (candidate or employer); false when it authenticated an existing account. */
282
290
  isNewUser?: boolean;
283
291
  };
292
+ BoardAuthSsoAuthorizationUrl: {
293
+ /** @enum {string} */
294
+ object: "sso_authorization_url";
295
+ /** @description The SSO connection ID. */
296
+ connectionId: string;
297
+ /** Format: uri */
298
+ authorizeUrl: string;
299
+ };
300
+ BoardAuthSsoLinkProofBody: {
301
+ /** @description The `linkProof` token from the emailed link. */
302
+ token: string;
303
+ /** @description The random secret established in the initiating browser BEFORE authorization. Completion URLs contain only its hash; never save their `linkProofBinding` as the secret. Without the matching value the proof is refused with `board_auth_sso_browser_mismatch`. */
304
+ browserBinding?: string;
305
+ };
284
306
  BoardAuthVerifyEmailBody: {
285
307
  token: string;
286
308
  };
309
+ BoardContact: {
310
+ /** @enum {string} */
311
+ object: "board_contact";
312
+ /** @description Whether visitors can send the board team a message. */
313
+ enabled: boolean;
314
+ boardName: string;
315
+ };
287
316
  BoardFormBuiltinField: {
288
317
  /**
289
318
  * @description discriminator enum property added by openapi-typescript
@@ -367,6 +396,19 @@ interface components {
367
396
  definition: components["schemas"]["ProfileCustomFieldDefinition"];
368
397
  };
369
398
  BoardProfileFormField: components["schemas"]["BoardFormBuiltinField"] | components["schemas"]["BoardProfileFormCustomField"] | components["schemas"]["BoardProfileFormCollectionField"];
399
+ BoardRoleSignIn: {
400
+ /** @description Deprecated: true when only SSO connections are available. Inspect methods and ssoConnections instead. */
401
+ ssoRequired: boolean;
402
+ /** @description Which built-in sign-in methods are switched on for this role. All `false` when sign-in for the role is switched off. All `false` with SSO connections listed means the role signs in only with SSO. */
403
+ methods: {
404
+ password: boolean;
405
+ magicLink: boolean;
406
+ google: boolean;
407
+ linkedin: boolean;
408
+ };
409
+ /** @description SSO connections switched on for this role, in the order the operator created them. */
410
+ ssoConnections: components["schemas"]["BoardSignInSsoConnection"][];
411
+ };
370
412
  BoardSeo: {
371
413
  /** @enum {string} */
372
414
  object: "board_seo";
@@ -386,6 +428,19 @@ interface components {
386
428
  name: string;
387
429
  };
388
430
  };
431
+ BoardSignInSsoConnection: {
432
+ /** @description SSO connection ID. Pass it to `GET /boards/{identifier}/auth/sso/{connectionId}` to start sign-in. */
433
+ id: string;
434
+ /** @description Operator-chosen display name of the identity provider, e.g. "Acme Members". Render the button as "Continue with {label}". */
435
+ label: string;
436
+ /** @description Absolute URL of the provider logo, or `null` when unset. */
437
+ logoUrl: string | null;
438
+ /**
439
+ * @description Deprecated: infer SSO-only sign-in from methods and ssoConnections.
440
+ * @enum {string}
441
+ */
442
+ mode: "available" | "required";
443
+ };
389
444
  BoardSitemap: {
390
445
  /** @enum {string} */
391
446
  object: "board_sitemap";
@@ -758,6 +813,22 @@ interface components {
758
813
  ConfirmWorkEmailBody: {
759
814
  token: string;
760
815
  };
816
+ ContactSubmission: {
817
+ /** @enum {string} */
818
+ object: "contact_submission";
819
+ /** @enum {boolean} */
820
+ success: true;
821
+ };
822
+ ContactSubmissionBody: {
823
+ name: string;
824
+ /** Format: email */
825
+ email: string;
826
+ subject: string;
827
+ body: string;
828
+ /** Format: uuid */
829
+ requestId: string;
830
+ website?: string;
831
+ };
761
832
  Conversation: {
762
833
  id: string;
763
834
  /** @enum {string} */
@@ -2225,6 +2296,11 @@ interface components {
2225
2296
  /** @description Whether posting a job on this board requires an active membership. When `true`, render the join gate in place of the anonymous post form: the platform rejects a create from a company with no contributing assignment (`membership_required`). Absent board config defaults to `false`. This flag is board policy and is safe to read anonymously; a signed-in employer's own standing rides their company billing options. */
2226
2297
  requiresMembership: boolean;
2227
2298
  };
2299
+ /** @description Sign-in options per role: the built-in methods and SSO connections switched on for that role. Render the sign-in page from this instead of hard-coding providers; any other method is refused with `board_auth_method_unavailable`. */
2300
+ signIn: {
2301
+ candidate: components["schemas"]["BoardRoleSignIn"];
2302
+ employer: components["schemas"]["BoardRoleSignIn"];
2303
+ };
2228
2304
  analytics: {
2229
2305
  ga4MeasurementId: string | null;
2230
2306
  gtmId: string | null;
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-CuSpfit_.js';
1
+ import { S as Schemas } from './_spec-BD5My4zL.js';
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-CuSpfit_.mjs';
1
+ import { S as Schemas } from './_spec-BD5My4zL.mjs';
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-CuSpfit_.js';
2
- import { L as ListEnvelope, s as RelatedSearch, a as CustomFieldFilter, O as ObjectReferenceFilter } from './jobs-CErce8Tw.js';
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';
3
3
 
4
4
  type PublicCompany = Schemas['CompanyPublic'];
5
5
  /** A market (sector) a company operates in — name + its source slug. */
@@ -123,6 +123,19 @@ type PublicBoardFeatures = PublicBoard['features'];
123
123
  type PublicBoardAnalytics = PublicBoard['analytics'];
124
124
  type PublicBoardAds = PublicBoard['ads'];
125
125
  type PublicBoardJobForm = PublicBoard['jobForm'];
126
+ /**
127
+ * Sign-in options per role from `board.context().signIn`: which built-in
128
+ * methods to offer and the board's SSO connections. Render the sign-in page
129
+ * from this instead of hard-coding providers.
130
+ */
131
+ type PublicBoardSignIn = PublicBoard['signIn'];
132
+ /** One role's sign-in options (`signIn.candidate` / `signIn.employer`). */
133
+ type BoardRoleSignIn = Schemas['BoardRoleSignIn'];
134
+ /**
135
+ * An SSO connection offered to a role. Start sign-in with
136
+ * `board.auth.getSsoAuthorizationUrl(connection.id, { role })`.
137
+ */
138
+ type BoardSignInSsoConnection = Schemas['BoardSignInSsoConnection'];
126
139
  /**
127
140
  * The operator's job, company and talent forms from
128
141
  * `board.context().forms`: one ordered field list per form. Render each list
@@ -152,4 +165,4 @@ type CustomFieldDefinition = Schemas['CustomFieldDefinition'];
152
165
  type CustomFieldType = CustomFieldDefinition['type'];
153
166
  type CustomFieldOption = Schemas['CustomFieldOption'];
154
167
 
155
- export type { PublicCompanyDetail as A, BoardFormBuiltinField as B, CustomFieldDefinition as C, PublicCompanyMembership as D, SalaryCompany as E, SalaryLocation as F, SalarySkill as G, SalaryTitle as H, SkillLocationSalary as I, SkillLocationsIndex as J, TitleLocationSalary as K, LocationSalaryDetail as L, TitleLocationsIndex as M, PublicBoard as P, SkillSalaryDetail as S, TitleSalaryDetail as T, CompanyCategorySalary as a, CompanySalary as b, CompaniesListQuery as c, CompanyListEnvelope as d, CompaniesSearchBody as e, CompanyJobsListQuery as f, CompanySimilarQuery as g, CompanyMarketsListQuery as h, SalaryDetailQuery as i, BoardFormField as j, BoardFormLayout as k, BoardFormLockReason as l, BoardJobFormField as m, BoardProfileFormField as n, CompanyMarket as o, CompanyMarketRef as p, CompanySalarySummary as q, CustomFieldOption as r, CustomFieldType as s, LocationSkillsIndex as t, LocationTitlesIndex as u, PublicBoardAds as v, PublicBoardAnalytics as w, PublicBoardFeatures as x, PublicBoardJobForm as y, PublicCompany as z };
168
+ export type { PublicBoardJobForm as A, BoardFormBuiltinField as B, CustomFieldDefinition as C, PublicBoardSignIn as D, PublicCompany as E, PublicCompanyDetail as F, PublicCompanyMembership as G, SalaryCompany as H, SalaryLocation as I, SalarySkill as J, SalaryTitle as K, LocationSalaryDetail as L, SkillLocationSalary as M, SkillLocationsIndex as N, TitleLocationSalary as O, PublicBoard as P, TitleLocationsIndex as Q, SkillSalaryDetail as S, TitleSalaryDetail as T, CompanyCategorySalary as a, CompanySalary as b, CompaniesListQuery as c, CompanyListEnvelope as d, CompaniesSearchBody as e, CompanyJobsListQuery as f, CompanySimilarQuery as g, CompanyMarketsListQuery as h, SalaryDetailQuery as i, BoardFormField as j, BoardFormLayout as k, BoardFormLockReason as l, BoardJobFormField as m, BoardProfileFormField as n, BoardRoleSignIn as o, BoardSignInSsoConnection as p, CompanyMarket as q, CompanyMarketRef as r, CompanySalarySummary as s, CustomFieldOption as t, CustomFieldType as u, LocationSkillsIndex as v, LocationTitlesIndex as w, PublicBoardAds as x, PublicBoardAnalytics as y, PublicBoardFeatures as z };
@@ -1,5 +1,5 @@
1
- import { S as Schemas } from './_spec-CuSpfit_.mjs';
2
- import { L as ListEnvelope, s as RelatedSearch, a as CustomFieldFilter, O as ObjectReferenceFilter } from './jobs-h-ewgbXn.mjs';
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';
3
3
 
4
4
  type PublicCompany = Schemas['CompanyPublic'];
5
5
  /** A market (sector) a company operates in — name + its source slug. */
@@ -123,6 +123,19 @@ type PublicBoardFeatures = PublicBoard['features'];
123
123
  type PublicBoardAnalytics = PublicBoard['analytics'];
124
124
  type PublicBoardAds = PublicBoard['ads'];
125
125
  type PublicBoardJobForm = PublicBoard['jobForm'];
126
+ /**
127
+ * Sign-in options per role from `board.context().signIn`: which built-in
128
+ * methods to offer and the board's SSO connections. Render the sign-in page
129
+ * from this instead of hard-coding providers.
130
+ */
131
+ type PublicBoardSignIn = PublicBoard['signIn'];
132
+ /** One role's sign-in options (`signIn.candidate` / `signIn.employer`). */
133
+ type BoardRoleSignIn = Schemas['BoardRoleSignIn'];
134
+ /**
135
+ * An SSO connection offered to a role. Start sign-in with
136
+ * `board.auth.getSsoAuthorizationUrl(connection.id, { role })`.
137
+ */
138
+ type BoardSignInSsoConnection = Schemas['BoardSignInSsoConnection'];
126
139
  /**
127
140
  * The operator's job, company and talent forms from
128
141
  * `board.context().forms`: one ordered field list per form. Render each list
@@ -152,4 +165,4 @@ type CustomFieldDefinition = Schemas['CustomFieldDefinition'];
152
165
  type CustomFieldType = CustomFieldDefinition['type'];
153
166
  type CustomFieldOption = Schemas['CustomFieldOption'];
154
167
 
155
- export type { PublicCompanyDetail as A, BoardFormBuiltinField as B, CustomFieldDefinition as C, PublicCompanyMembership as D, SalaryCompany as E, SalaryLocation as F, SalarySkill as G, SalaryTitle as H, SkillLocationSalary as I, SkillLocationsIndex as J, TitleLocationSalary as K, LocationSalaryDetail as L, TitleLocationsIndex as M, PublicBoard as P, SkillSalaryDetail as S, TitleSalaryDetail as T, CompanyCategorySalary as a, CompanySalary as b, CompaniesListQuery as c, CompanyListEnvelope as d, CompaniesSearchBody as e, CompanyJobsListQuery as f, CompanySimilarQuery as g, CompanyMarketsListQuery as h, SalaryDetailQuery as i, BoardFormField as j, BoardFormLayout as k, BoardFormLockReason as l, BoardJobFormField as m, BoardProfileFormField as n, CompanyMarket as o, CompanyMarketRef as p, CompanySalarySummary as q, CustomFieldOption as r, CustomFieldType as s, LocationSkillsIndex as t, LocationTitlesIndex as u, PublicBoardAds as v, PublicBoardAnalytics as w, PublicBoardFeatures as x, PublicBoardJobForm as y, PublicCompany as z };
168
+ export type { PublicBoardJobForm as A, BoardFormBuiltinField as B, CustomFieldDefinition as C, PublicBoardSignIn as D, PublicCompany as E, PublicCompanyDetail as F, PublicCompanyMembership as G, SalaryCompany as H, SalaryLocation as I, SalarySkill as J, SalaryTitle as K, LocationSalaryDetail as L, SkillLocationSalary as M, SkillLocationsIndex as N, TitleLocationSalary as O, PublicBoard as P, TitleLocationsIndex as Q, SkillSalaryDetail as S, TitleSalaryDetail as T, CompanyCategorySalary as a, CompanySalary as b, CompaniesListQuery as c, CompanyListEnvelope as d, CompaniesSearchBody as e, CompanyJobsListQuery as f, CompanySimilarQuery as g, CompanyMarketsListQuery as h, SalaryDetailQuery as i, BoardFormField as j, BoardFormLayout as k, BoardFormLockReason as l, BoardJobFormField as m, BoardProfileFormField as n, BoardRoleSignIn as o, BoardSignInSsoConnection as p, CompanyMarket as q, CompanyMarketRef as r, CompanySalarySummary as s, CustomFieldOption as t, CustomFieldType as u, LocationSkillsIndex as v, LocationTitlesIndex as w, PublicBoardAds as x, PublicBoardAnalytics as y, PublicBoardFeatures as z };
@@ -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-VrjkKuZ-.mjs';
2
- import { P as PublicJob } from './jobs-h-ewgbXn.mjs';
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';
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-t_j16FrX.js';
2
- import { P as PublicJob } from './jobs-CErce8Tw.js';
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';
3
3
 
4
4
  /**
5
5
  * Wire enum for `salaryTimeframe`. Defined here (not re-exported from any