@cavuno/board 4.9.0 → 4.11.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 (40) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/dist/{_spec-B9Ah8dNs.d.mts → _spec-Ck02NvUF.d.mts} +72 -1
  3. package/dist/{_spec-B9Ah8dNs.d.ts → _spec-Ck02NvUF.d.ts} +72 -1
  4. package/dist/{blog-BmqWfLZi.d.mts → blog-CZUXieNu.d.mts} +1 -1
  5. package/dist/{blog-bh07fZ8Q.d.ts → blog-ClwNmr_N.d.ts} +1 -1
  6. package/dist/{board-De8nLbk7.d.ts → board-6XZx1x5M.d.ts} +2 -2
  7. package/dist/{board-B4jLqmxn.d.mts → board-wHyyyAhT.d.mts} +2 -2
  8. package/dist/{breadcrumbs-B1HVufC7.d.ts → breadcrumbs-CPaEpS6h.d.ts} +2 -2
  9. package/dist/{breadcrumbs-BVQL2dNY.d.mts → breadcrumbs-DJjtyjlf.d.mts} +2 -2
  10. package/dist/browser/cavuno-board.global.min.js +1 -1
  11. package/dist/{chunk-T46JASKG.mjs → chunk-XEJWXRLK.mjs} +7 -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 +93 -14
  17. package/dist/index.d.ts +93 -14
  18. package/dist/index.js +118 -4
  19. package/dist/index.mjs +112 -5
  20. package/dist/{jobs-Iv6Wkvvf.d.ts → jobs-3BAwXBxR.d.ts} +1 -1
  21. package/dist/{jobs-B7q3BgIK.d.mts → jobs-iG5udM2R.d.mts} +1 -1
  22. package/dist/{search-DC0jatQN.d.mts → search-5DzB-OrD.d.mts} +1 -1
  23. package/dist/{search-CbDZwdyl.d.ts → search-kfabRi5l.d.ts} +1 -1
  24. package/dist/{seo-HCE7aCkh.d.mts → seo-C_Mt1i-Y.d.mts} +1 -1
  25. package/dist/{seo-BDFUuvXM.d.ts → seo-DndU15zg.d.ts} +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/suggest.d.mts +2 -2
  36. package/dist/suggest.d.ts +2 -2
  37. package/package.json +1 -1
  38. package/skills/cavuno-board-setup/SKILL.md +1 -0
  39. package/skills/cavuno-board-talent-access/SKILL.md +149 -0
  40. package/skills/manifest.json +8 -1
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.11.0 — 2026-08-28
7
+
8
+ - **`BoardAuthSession.isNewUser`**: optional boolean on OAuth exchange and
9
+ magic-link consume. True when the exchange created the board user account
10
+ (candidate or employer); false when it authenticated an existing account.
11
+ Login, register, and refresh omit the field.
12
+
13
+ ## 4.10.0 — 2026-08-28
14
+
15
+ - **Employer talent-access paywall** (a different buyer from the candidate
16
+ paywall): `board.me.talentAccess` gains `retrieveCandidate`, `checkout`
17
+ (connected-account embedded mount kit, `origin: talent_access`),
18
+ `retrieveCheckout`, `unlock` (idempotent credit spend), and `upgrade`
19
+ (in-place plan swap; never a second checkout). `retrieve()` now also
20
+ returns `accessModel`, `companyId`, `unlockCreditsRemaining`,
21
+ `messageCreditsRemaining`, and `hasUnlimitedMessages`.
22
+ - **Company billing portal**: `board.me.companies.billingPortal.create(slug)`
23
+ mints a Stripe Customer Portal session covering every subscription on the
24
+ company's Stripe customer (job posting + talent access).
25
+ - **Public talent reads**: `TalentDirectoryEntry` and `TalentProfile` gain
26
+ `id` (the board-user id, the opaque `/p/{id}` route key). When a board
27
+ sells profile unlocks, list and retrieve-by-id responses are redacted for
28
+ viewers without entitlement; retrieve-by-handle stays the named-route
29
+ share bypass.
30
+ - **Error codes**: `talent_access_unavailable`, `company_required`,
31
+ `already_on_plan`, `talent_access_required`,
32
+ `messaging_talent_access_required`, and `stripe_not_connected` join
33
+ `BOARD_API_ERROR_CODES`.
34
+ - **New skill** `cavuno-board-talent-access` covering the full employer
35
+ flow (CTA gating, checkout, unlock, upgrade, portal, message credits).
36
+
6
37
  ## 4.9.0 — 2026-08-27
7
38
 
8
39
  - **`@cavuno/board/seo-files`**: new dependency-free subpath with
@@ -261,6 +261,8 @@ interface components {
261
261
  /** @description Access-token expiry, epoch milliseconds. */
262
262
  expiresAt: number;
263
263
  boardUser: components["schemas"]["BoardUser"];
264
+ /** @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. */
265
+ isNewUser?: boolean;
264
266
  };
265
267
  BoardAuthVerifyEmailBody: {
266
268
  token: string;
@@ -381,6 +383,14 @@ interface components {
381
383
  slug: string;
382
384
  website: string | null;
383
385
  };
386
+ CompanyBillingPortalBody: {
387
+ returnPath?: string;
388
+ };
389
+ CompanyBillingPortalSession: {
390
+ /** @enum {string} */
391
+ object: "portal_session";
392
+ url: string;
393
+ };
384
394
  CompanyCategorySalary: {
385
395
  /** @enum {string} */
386
396
  object: "company_category_salary";
@@ -1814,6 +1824,11 @@ interface components {
1814
1824
  /** @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. */
1815
1825
  messaging: boolean;
1816
1826
  };
1827
+ /**
1828
+ * @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`.
1829
+ * @enum {string|null}
1830
+ */
1831
+ talentAccessModel: "paid_messaging" | "paid_unlocks_and_messaging" | null;
1817
1832
  analytics: {
1818
1833
  ga4MeasurementId: string | null;
1819
1834
  gtmId: string | null;
@@ -2563,14 +2578,52 @@ interface components {
2563
2578
  isEmployer: boolean;
2564
2579
  /** @description Whether this board sells talent access (a public `talent_access` plan exists). When `false` the talent surfaces are not credit-gated for employers. */
2565
2580
  paywallActive: boolean;
2566
- /** @description The CTA answer: the viewer is an approved employer AND either the board has no talent paywall or one of their companies holds an active talent_access subscription or a succeeded credit pack. Always `false` for candidates; anonymous viewers get 401. */
2581
+ /** @description The CTA answer: the viewer is an approved employer AND either the board has no talent paywall or one of their companies holds an active talent_access subscription or a succeeded credit pack. Always `false` for candidates; anonymous viewers get 401. Never cached. */
2567
2582
  hasTalentAccess: boolean;
2568
2583
  /** @description True when any active talent_access subscription carries the unlimited profile-unlock sentinel on its feature snapshot. */
2569
2584
  hasUnlimitedUnlocks: boolean;
2585
+ /**
2586
+ * @description `none` when the paywall is off. Otherwise the board charging model: paid messaging only, or paid unlocks and messaging. Inferred from public plan unlock credits when the stored config is unset.
2587
+ * @enum {string}
2588
+ */
2589
+ accessModel: "none" | "paid_messaging" | "paid_unlocks_and_messaging";
2590
+ /** @description The approved company to charge checkout/unlock against when the viewer has exactly one membership. `null` when they have none or more than one (pass `companyId` on write). */
2591
+ companyId: string | null;
2592
+ /** @description Summed finite remaining profile-unlock credits across the viewer’s companies. `0` when unlimited or when they hold no spendable credits. */
2593
+ unlockCreditsRemaining: number;
2594
+ /** @description Summed finite remaining first-message credits. `0` when unlimited or when they hold no spendable credits. */
2595
+ messageCreditsRemaining: number;
2596
+ /** @description True when any active talent_access subscription carries the unlimited message-credit sentinel. */
2597
+ hasUnlimitedMessages: boolean;
2598
+ };
2599
+ TalentAccessCheckoutBody: {
2600
+ planId: string;
2601
+ returnPath: string;
2602
+ /** @enum {string} */
2603
+ colorMode: "light" | "dark";
2604
+ companyId?: string;
2605
+ };
2606
+ TalentAccessUpgradeBody: {
2607
+ planId: string;
2608
+ companyId?: string;
2609
+ };
2610
+ TalentCandidateAccess: {
2611
+ /** @enum {string} */
2612
+ object: "talent_candidate_access";
2613
+ candidateId: string;
2614
+ companyId: string | null;
2615
+ alreadyUnlocked: boolean;
2616
+ unlockCreditsRemaining: number;
2617
+ messageCreditsRemaining: number;
2618
+ hasUnlimitedUnlocks: boolean;
2619
+ hasUnlimitedMessages: boolean;
2620
+ hasActiveTalentSubscription: boolean;
2570
2621
  };
2571
2622
  TalentDirectoryEntry: {
2572
2623
  /** @enum {string} */
2573
2624
  object: "talent_directory_entry";
2625
+ /** @description The candidate’s board-user id. When a public talent_access plan exists, directory cards link to the opaque `/p/{id}` route so profile unlocks can apply. The named `/p/{handle}` route is the share bypass. */
2626
+ id: string;
2574
2627
  handle: string | null;
2575
2628
  displayName: string | null;
2576
2629
  headline: string | null;
@@ -2600,6 +2653,8 @@ interface components {
2600
2653
  TalentProfile: {
2601
2654
  /** @enum {string} */
2602
2655
  object: "talent_profile";
2656
+ /** @description The candidate’s board-user id. The opaque talent-paywall route key (`/p/{id}`); `handle` is the named-share bypass. */
2657
+ id: string;
2603
2658
  handle: string | null;
2604
2659
  displayName: string | null;
2605
2660
  headline: string | null;
@@ -2649,6 +2704,22 @@ interface components {
2649
2704
  proficiency: string;
2650
2705
  }[];
2651
2706
  };
2707
+ TalentUnlock: {
2708
+ /** @enum {string} */
2709
+ object: "talent_unlock";
2710
+ alreadyUnlocked: boolean;
2711
+ unlockCreditsRemaining: number | null;
2712
+ };
2713
+ TalentUnlockBody: {
2714
+ candidateId: string;
2715
+ companyId?: string;
2716
+ };
2717
+ TalentUpgrade: {
2718
+ /** @enum {string} */
2719
+ object: "talent_upgrade";
2720
+ /** @enum {boolean} */
2721
+ ok: true;
2722
+ };
2652
2723
  /** @description Geo for place resolutions; `null` for category/skill. */
2653
2724
  TaxonomyGeo: {
2654
2725
  lat: number | null;
@@ -261,6 +261,8 @@ interface components {
261
261
  /** @description Access-token expiry, epoch milliseconds. */
262
262
  expiresAt: number;
263
263
  boardUser: components["schemas"]["BoardUser"];
264
+ /** @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. */
265
+ isNewUser?: boolean;
264
266
  };
265
267
  BoardAuthVerifyEmailBody: {
266
268
  token: string;
@@ -381,6 +383,14 @@ interface components {
381
383
  slug: string;
382
384
  website: string | null;
383
385
  };
386
+ CompanyBillingPortalBody: {
387
+ returnPath?: string;
388
+ };
389
+ CompanyBillingPortalSession: {
390
+ /** @enum {string} */
391
+ object: "portal_session";
392
+ url: string;
393
+ };
384
394
  CompanyCategorySalary: {
385
395
  /** @enum {string} */
386
396
  object: "company_category_salary";
@@ -1814,6 +1824,11 @@ interface components {
1814
1824
  /** @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. */
1815
1825
  messaging: boolean;
1816
1826
  };
1827
+ /**
1828
+ * @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`.
1829
+ * @enum {string|null}
1830
+ */
1831
+ talentAccessModel: "paid_messaging" | "paid_unlocks_and_messaging" | null;
1817
1832
  analytics: {
1818
1833
  ga4MeasurementId: string | null;
1819
1834
  gtmId: string | null;
@@ -2563,14 +2578,52 @@ interface components {
2563
2578
  isEmployer: boolean;
2564
2579
  /** @description Whether this board sells talent access (a public `talent_access` plan exists). When `false` the talent surfaces are not credit-gated for employers. */
2565
2580
  paywallActive: boolean;
2566
- /** @description The CTA answer: the viewer is an approved employer AND either the board has no talent paywall or one of their companies holds an active talent_access subscription or a succeeded credit pack. Always `false` for candidates; anonymous viewers get 401. */
2581
+ /** @description The CTA answer: the viewer is an approved employer AND either the board has no talent paywall or one of their companies holds an active talent_access subscription or a succeeded credit pack. Always `false` for candidates; anonymous viewers get 401. Never cached. */
2567
2582
  hasTalentAccess: boolean;
2568
2583
  /** @description True when any active talent_access subscription carries the unlimited profile-unlock sentinel on its feature snapshot. */
2569
2584
  hasUnlimitedUnlocks: boolean;
2585
+ /**
2586
+ * @description `none` when the paywall is off. Otherwise the board charging model: paid messaging only, or paid unlocks and messaging. Inferred from public plan unlock credits when the stored config is unset.
2587
+ * @enum {string}
2588
+ */
2589
+ accessModel: "none" | "paid_messaging" | "paid_unlocks_and_messaging";
2590
+ /** @description The approved company to charge checkout/unlock against when the viewer has exactly one membership. `null` when they have none or more than one (pass `companyId` on write). */
2591
+ companyId: string | null;
2592
+ /** @description Summed finite remaining profile-unlock credits across the viewer’s companies. `0` when unlimited or when they hold no spendable credits. */
2593
+ unlockCreditsRemaining: number;
2594
+ /** @description Summed finite remaining first-message credits. `0` when unlimited or when they hold no spendable credits. */
2595
+ messageCreditsRemaining: number;
2596
+ /** @description True when any active talent_access subscription carries the unlimited message-credit sentinel. */
2597
+ hasUnlimitedMessages: boolean;
2598
+ };
2599
+ TalentAccessCheckoutBody: {
2600
+ planId: string;
2601
+ returnPath: string;
2602
+ /** @enum {string} */
2603
+ colorMode: "light" | "dark";
2604
+ companyId?: string;
2605
+ };
2606
+ TalentAccessUpgradeBody: {
2607
+ planId: string;
2608
+ companyId?: string;
2609
+ };
2610
+ TalentCandidateAccess: {
2611
+ /** @enum {string} */
2612
+ object: "talent_candidate_access";
2613
+ candidateId: string;
2614
+ companyId: string | null;
2615
+ alreadyUnlocked: boolean;
2616
+ unlockCreditsRemaining: number;
2617
+ messageCreditsRemaining: number;
2618
+ hasUnlimitedUnlocks: boolean;
2619
+ hasUnlimitedMessages: boolean;
2620
+ hasActiveTalentSubscription: boolean;
2570
2621
  };
2571
2622
  TalentDirectoryEntry: {
2572
2623
  /** @enum {string} */
2573
2624
  object: "talent_directory_entry";
2625
+ /** @description The candidate’s board-user id. When a public talent_access plan exists, directory cards link to the opaque `/p/{id}` route so profile unlocks can apply. The named `/p/{handle}` route is the share bypass. */
2626
+ id: string;
2574
2627
  handle: string | null;
2575
2628
  displayName: string | null;
2576
2629
  headline: string | null;
@@ -2600,6 +2653,8 @@ interface components {
2600
2653
  TalentProfile: {
2601
2654
  /** @enum {string} */
2602
2655
  object: "talent_profile";
2656
+ /** @description The candidate’s board-user id. The opaque talent-paywall route key (`/p/{id}`); `handle` is the named-share bypass. */
2657
+ id: string;
2603
2658
  handle: string | null;
2604
2659
  displayName: string | null;
2605
2660
  headline: string | null;
@@ -2649,6 +2704,22 @@ interface components {
2649
2704
  proficiency: string;
2650
2705
  }[];
2651
2706
  };
2707
+ TalentUnlock: {
2708
+ /** @enum {string} */
2709
+ object: "talent_unlock";
2710
+ alreadyUnlocked: boolean;
2711
+ unlockCreditsRemaining: number | null;
2712
+ };
2713
+ TalentUnlockBody: {
2714
+ candidateId: string;
2715
+ companyId?: string;
2716
+ };
2717
+ TalentUpgrade: {
2718
+ /** @enum {string} */
2719
+ object: "talent_upgrade";
2720
+ /** @enum {boolean} */
2721
+ ok: true;
2722
+ };
2652
2723
  /** @description Geo for place resolutions; `null` for category/skill. */
2653
2724
  TaxonomyGeo: {
2654
2725
  lat: number | null;
@@ -1,4 +1,4 @@
1
- import { S as Schemas } from './_spec-B9Ah8dNs.mjs';
1
+ import { S as Schemas } from './_spec-Ck02NvUF.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-B9Ah8dNs.js';
1
+ import { S as Schemas } from './_spec-Ck02NvUF.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-B9Ah8dNs.js';
2
- import { L as ListEnvelope, m as RelatedSearch } from './jobs-Iv6Wkvvf.js';
1
+ import { S as Schemas } from './_spec-Ck02NvUF.js';
2
+ import { L as ListEnvelope, m as RelatedSearch } from './jobs-3BAwXBxR.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-B9Ah8dNs.mjs';
2
- import { L as ListEnvelope, m as RelatedSearch } from './jobs-B7q3BgIK.mjs';
1
+ import { S as Schemas } from './_spec-Ck02NvUF.mjs';
2
+ import { L as ListEnvelope, m as RelatedSearch } from './jobs-iG5udM2R.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-De8nLbk7.js';
2
- import { P as PublicJob } from './jobs-Iv6Wkvvf.js';
1
+ import { P as PublicBoard, a as CompanyCategorySalary, b as CompanySalary, L as LocationSalaryDetail, S as SkillSalaryDetail, T as TitleSalaryDetail } from './board-6XZx1x5M.js';
2
+ import { P as PublicJob } from './jobs-3BAwXBxR.js';
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-B4jLqmxn.mjs';
2
- import { P as PublicJob } from './jobs-B7q3BgIK.mjs';
1
+ import { P as PublicBoard, a as CompanyCategorySalary, b as CompanySalary, L as LocationSalaryDetail, S as SkillSalaryDetail, T as TitleSalaryDetail } from './board-wHyyyAhT.mjs';
2
+ import { P as PublicJob } from './jobs-iG5udM2R.mjs';
3
3
 
4
4
  /**
5
5
  * Wire enum for `salaryTimeframe`. Defined here (not re-exported from any