@cavuno/board 4.59.1 → 4.61.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 (37) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/dist/{_spec-DZV8ME1P.d.mts → _spec-B6YOcfhm.d.mts} +152 -0
  3. package/dist/{_spec-DZV8ME1P.d.ts → _spec-B6YOcfhm.d.ts} +152 -0
  4. package/dist/{blog-BfVuJQvO.d.mts → blog-BazgjIMl.d.mts} +1 -1
  5. package/dist/{blog-CLVfmknV.d.ts → blog-Iz3vKyol.d.ts} +1 -1
  6. package/dist/{board-Cl6cfS-a.d.ts → board-Bd7H1jwl.d.ts} +2 -2
  7. package/dist/{board-gmV8MoLW.d.mts → board-BwOeOQ6v.d.mts} +2 -2
  8. package/dist/{breadcrumbs-BOM71gXT.d.mts → breadcrumbs-DxlE86G-.d.mts} +2 -2
  9. package/dist/{breadcrumbs-dTn6GsKo.d.ts → breadcrumbs-pccdTbvQ.d.ts} +2 -2
  10. package/dist/browser/cavuno-board.global.min.js +1 -1
  11. package/dist/filters.d.mts +2 -2
  12. package/dist/filters.d.ts +2 -2
  13. package/dist/format.d.mts +4 -4
  14. package/dist/format.d.ts +4 -4
  15. package/dist/index.d.mts +201 -13
  16. package/dist/index.d.ts +201 -13
  17. package/dist/index.js +154 -2
  18. package/dist/index.mjs +154 -2
  19. package/dist/{jobs-Di0L5sM1.d.ts → jobs-BwxCoTRJ.d.ts} +1 -1
  20. package/dist/{jobs-DfMyFAYs.d.mts → jobs-DrDmqiBb.d.mts} +1 -1
  21. package/dist/{search-1pxkvj_I.d.mts → search-CbGEG89W.d.mts} +1 -1
  22. package/dist/{search-BPnMYVa7.d.ts → search-D4Uo2bRh.d.ts} +1 -1
  23. package/dist/{seo-DFPj-7Rl.d.ts → seo-CnqvU41p.d.ts} +1 -1
  24. package/dist/{seo-CJtLrGD_.d.mts → seo-DiJBveAA.d.mts} +1 -1
  25. package/dist/seo-files.d.mts +2 -2
  26. package/dist/seo-files.d.ts +2 -2
  27. package/dist/seo.d.mts +6 -6
  28. package/dist/seo.d.ts +6 -6
  29. package/dist/server.d.mts +6 -6
  30. package/dist/server.d.ts +6 -6
  31. package/dist/sitemap.d.mts +6 -6
  32. package/dist/sitemap.d.ts +6 -6
  33. package/dist/suggest.d.mts +2 -2
  34. package/dist/suggest.d.ts +2 -2
  35. package/package.json +2 -2
  36. package/skills/cavuno-board-job-alerts/SKILL.md +31 -0
  37. package/skills/manifest.json +1 -1
package/dist/index.mjs CHANGED
@@ -46,7 +46,7 @@ function toSearchParams(query) {
46
46
  }
47
47
 
48
48
  // src/version.ts
49
- var SDK_VERSION = "4.59.1";
49
+ var SDK_VERSION = "4.61.0";
50
50
 
51
51
  // src/client.ts
52
52
  function isRawBody(body) {
@@ -414,6 +414,22 @@ function authNamespace(client, board = "") {
414
414
  body
415
415
  });
416
416
  },
417
+ /**
418
+ * Confirm a profile item's email-domain check with the token from the
419
+ * emailed link. No session required: the single-use token is the
420
+ * authorization. Returns the item, now verified. Any rejected token is a
421
+ * 401 `BoardApiError`; start the check again to recover.
422
+ *
423
+ * @example
424
+ * const item = await board.auth.verifyItemEmail({ token });
425
+ */
426
+ verifyItemEmail(body, options) {
427
+ return client.fetch("/auth/verify-item-email", {
428
+ ...options,
429
+ method: "POST",
430
+ body
431
+ });
432
+ },
417
433
  /**
418
434
  * Verify the signed-in board user's email with the 6-digit code from the
419
435
  * verification email (requires the bearer). Resolves void (204); an
@@ -945,7 +961,11 @@ function jobAlertsNamespace(client) {
945
961
  query
946
962
  });
947
963
  },
948
- /** Deactivate a subscription via the HMAC manage token. */
964
+ /**
965
+ * Deactivate a subscription via the HMAC manage token. Spread
966
+ * `parseJobAlertUnsubscribeParams(url)` into the input to record which
967
+ * digest the subscriber unsubscribed from.
968
+ */
949
969
  unsubscribe(input, options) {
950
970
  return client.fetch("/job-alerts/unsubscribe", {
951
971
  ...options,
@@ -953,6 +973,19 @@ function jobAlertsNamespace(client) {
953
973
  body: input
954
974
  });
955
975
  },
976
+ /**
977
+ * Record a visitor opening a job from a digest email. Call it once on a
978
+ * job page load whose URL carries the digest params
979
+ * (`parseJobAlertClickParams(url)` returns them). Resolves with a uniform
980
+ * `accepted` receipt; the click is recorded after the response.
981
+ */
982
+ recordClick(input, options) {
983
+ return client.fetch("/job-alerts/clicks", {
984
+ ...options,
985
+ method: "POST",
986
+ body: input
987
+ });
988
+ },
956
989
  /** Re-activate a previously unsubscribed subscription via the manage token. */
957
990
  resubscribe(input, options) {
958
991
  return client.fetch("/job-alerts/resubscribe", {
@@ -1411,6 +1444,71 @@ function meNamespace(client) {
1411
1444
  { ...options, method: "PUT", body }
1412
1445
  );
1413
1446
  },
1447
+ /**
1448
+ * List the verification status of the items on my profile (experience,
1449
+ * education and collection entries). Items not listed are unverified.
1450
+ *
1451
+ * @example
1452
+ * const { data } = await board.me.profile.listVerifications();
1453
+ */
1454
+ listVerifications(options) {
1455
+ return client.fetch(
1456
+ "/me/profile/verifications",
1457
+ options
1458
+ );
1459
+ },
1460
+ /**
1461
+ * Ask the board team to verify one item on my profile. It becomes
1462
+ * `pending` until they decide.
1463
+ *
1464
+ * @example
1465
+ * await board.me.profile.submitVerification({
1466
+ * itemKey: 'experience:abc123',
1467
+ * });
1468
+ */
1469
+ submitVerification(body, options) {
1470
+ return client.fetch("/me/profile/verifications", {
1471
+ ...options,
1472
+ method: "POST",
1473
+ body
1474
+ });
1475
+ },
1476
+ /**
1477
+ * Start an email-domain check of one item on my profile: a 6-digit code
1478
+ * and a link go to an address at the item's domain (for example a work
1479
+ * email at a current employer's website domain). Confirm with
1480
+ * `confirmEmailDomainVerification` or `board.auth.verifyItemEmail`.
1481
+ * `sent` is false when an email for the item went out under a minute
1482
+ * ago and nothing new was sent.
1483
+ *
1484
+ * @example
1485
+ * const check = await board.me.profile.submitEmailDomainVerification({
1486
+ * itemKey: 'experience:abc123',
1487
+ * email: 'me@acme.com',
1488
+ * });
1489
+ */
1490
+ submitEmailDomainVerification(body, options) {
1491
+ return client.fetch(
1492
+ "/me/profile/verifications/email-domain",
1493
+ { ...options, method: "POST", body }
1494
+ );
1495
+ },
1496
+ /**
1497
+ * Confirm an email-domain check with the 6-digit code from the email.
1498
+ * Returns the item, now verified; the emailed link stops working.
1499
+ *
1500
+ * @example
1501
+ * await board.me.profile.confirmEmailDomainVerification({
1502
+ * itemKey: 'experience:abc123',
1503
+ * code: '123456',
1504
+ * });
1505
+ */
1506
+ confirmEmailDomainVerification(body, options) {
1507
+ return client.fetch(
1508
+ "/me/profile/verifications/email-domain/confirm",
1509
+ { ...options, method: "POST", body }
1510
+ );
1511
+ },
1414
1512
  /**
1415
1513
  * Check whether a handle is available for me on this board (my own
1416
1514
  * current handle counts as available). Advisory — `update` re-checks.
@@ -1585,6 +1683,20 @@ function meNamespace(client) {
1585
1683
  { ...options, method: "PUT", body }
1586
1684
  );
1587
1685
  },
1686
+ /** List the verification status of the items on one of my companies. */
1687
+ listVerifications(slug, options) {
1688
+ return client.fetch(
1689
+ `/me/companies/${encodeURIComponent(slug)}/verifications`,
1690
+ options
1691
+ );
1692
+ },
1693
+ /** Ask the board team to verify one item on one of my companies. */
1694
+ submitVerification(slug, body, options) {
1695
+ return client.fetch(
1696
+ `/me/companies/${encodeURIComponent(slug)}/verifications`,
1697
+ { ...options, method: "POST", body }
1698
+ );
1699
+ },
1588
1700
  /**
1589
1701
  * Search companies on this board to claim.
1590
1702
  *
@@ -4038,6 +4150,44 @@ function resolveApplyAction(state) {
4038
4150
  return action.kind === "sign-in" ? { kind: "sign-in" } : action;
4039
4151
  }
4040
4152
 
4153
+ // src/job-alert-links.ts
4154
+ var ID = /^[0-9A-Za-z]{16,64}$/;
4155
+ var EMAIL_ID = /^[0-9a-f]{16}$/;
4156
+ var MATCHER_VERSION = /^[a-z0-9-]{1,64}$/;
4157
+ var POSITION = /^[1-9][0-9]{0,2}$/;
4158
+ var MAX_POSITION = 100;
4159
+ function searchParamsOf(url) {
4160
+ if (url instanceof URLSearchParams) return url;
4161
+ if (url instanceof URL) return url.searchParams;
4162
+ if (url.startsWith("?") || !url.includes("/") && url.includes("=")) {
4163
+ return new URLSearchParams(url);
4164
+ }
4165
+ try {
4166
+ return new URL(url, "https://board.invalid").searchParams;
4167
+ } catch {
4168
+ return new URLSearchParams();
4169
+ }
4170
+ }
4171
+ function parseJobAlertClickParams(url) {
4172
+ const params = searchParamsOf(url);
4173
+ const alertId = params.get("ja_alert") ?? "";
4174
+ const emailId = params.get("ja_email") ?? "";
4175
+ const positionRaw = params.get("ja_pos") ?? "";
4176
+ const matcherVersion = params.get("ja_mv") ?? "";
4177
+ const position = POSITION.test(positionRaw) ? Number(positionRaw) : NaN;
4178
+ if (!ID.test(alertId) || !EMAIL_ID.test(emailId) || !MATCHER_VERSION.test(matcherVersion) || !(position >= 1 && position <= MAX_POSITION)) {
4179
+ return null;
4180
+ }
4181
+ return { alertId, emailId, position, matcherVersion };
4182
+ }
4183
+ function parseJobAlertUnsubscribeParams(url) {
4184
+ const params = searchParamsOf(url);
4185
+ const alertId = params.get("ja_alert") ?? "";
4186
+ const emailId = params.get("ja_email") ?? "";
4187
+ if (!ID.test(alertId) || !EMAIL_ID.test(emailId)) return null;
4188
+ return { alertId, emailId };
4189
+ }
4190
+
4041
4191
  // src/index.ts
4042
4192
  function createBoardClient(options) {
4043
4193
  const client = new BoardClient({
@@ -4133,6 +4283,8 @@ export {
4133
4283
  isValidationError,
4134
4284
  lastOwnMessageId,
4135
4285
  paginate,
4286
+ parseJobAlertClickParams,
4287
+ parseJobAlertUnsubscribeParams,
4136
4288
  parseOAuthCompletion,
4137
4289
  resolveApplyAction,
4138
4290
  resolveApplyDecision,
@@ -1,4 +1,4 @@
1
- import { S as Schemas } from './_spec-DZV8ME1P.js';
1
+ import { S as Schemas } from './_spec-B6YOcfhm.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-DZV8ME1P.mjs';
1
+ import { S as Schemas } from './_spec-B6YOcfhm.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-DZV8ME1P.mjs';
1
+ import { S as Schemas } from './_spec-B6YOcfhm.mjs';
2
2
 
3
3
  type Awaitable<T> = T | Promise<T>;
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { S as Schemas } from './_spec-DZV8ME1P.js';
1
+ import { S as Schemas } from './_spec-B6YOcfhm.js';
2
2
 
3
3
  type Awaitable<T> = T | Promise<T>;
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { S as Schemas } from './_spec-DZV8ME1P.js';
1
+ import { S as Schemas } from './_spec-B6YOcfhm.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-DZV8ME1P.mjs';
1
+ import { S as Schemas } from './_spec-B6YOcfhm.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-CJtLrGD_.mjs';
2
- import './_spec-DZV8ME1P.mjs';
1
+ import { B as BoardSeo } from './seo-DiJBveAA.mjs';
2
+ import './_spec-B6YOcfhm.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-DFPj-7Rl.js';
2
- import './_spec-DZV8ME1P.js';
1
+ import { B as BoardSeo } from './seo-CnqvU41p.js';
2
+ import './_spec-B6YOcfhm.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-BfVuJQvO.mjs';
2
- import { e as JsonLdObject } from './breadcrumbs-BOM71gXT.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-BOM71gXT.mjs';
4
- import './_spec-DZV8ME1P.mjs';
5
- import './board-gmV8MoLW.mjs';
6
- import './jobs-DfMyFAYs.mjs';
1
+ import { P as PublicBlogPostSummary, B as BlogAuthorEmbed } from './blog-BazgjIMl.mjs';
2
+ import { e as JsonLdObject } from './breadcrumbs-DxlE86G-.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-DxlE86G-.mjs';
4
+ import './_spec-B6YOcfhm.mjs';
5
+ import './board-BwOeOQ6v.mjs';
6
+ import './jobs-DrDmqiBb.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-CLVfmknV.js';
2
- import { e as JsonLdObject } from './breadcrumbs-dTn6GsKo.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-dTn6GsKo.js';
4
- import './_spec-DZV8ME1P.js';
5
- import './board-Cl6cfS-a.js';
6
- import './jobs-Di0L5sM1.js';
1
+ import { P as PublicBlogPostSummary, B as BlogAuthorEmbed } from './blog-Iz3vKyol.js';
2
+ import { e as JsonLdObject } from './breadcrumbs-pccdTbvQ.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-pccdTbvQ.js';
4
+ import './_spec-B6YOcfhm.js';
5
+ import './board-Bd7H1jwl.js';
6
+ import './jobs-BwxCoTRJ.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-DZV8ME1P.mjs';
3
- import './search-1pxkvj_I.mjs';
4
- import './jobs-DfMyFAYs.mjs';
5
- import './board-gmV8MoLW.mjs';
6
- import './seo-CJtLrGD_.mjs';
7
- import './blog-BfVuJQvO.mjs';
2
+ import './_spec-B6YOcfhm.mjs';
3
+ import './search-CbGEG89W.mjs';
4
+ import './jobs-DrDmqiBb.mjs';
5
+ import './board-BwOeOQ6v.mjs';
6
+ import './seo-DiJBveAA.mjs';
7
+ import './blog-BazgjIMl.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-DZV8ME1P.js';
3
- import './search-BPnMYVa7.js';
4
- import './jobs-Di0L5sM1.js';
5
- import './board-Cl6cfS-a.js';
6
- import './seo-DFPj-7Rl.js';
7
- import './blog-CLVfmknV.js';
2
+ import './_spec-B6YOcfhm.js';
3
+ import './search-D4Uo2bRh.js';
4
+ import './jobs-BwxCoTRJ.js';
5
+ import './board-Bd7H1jwl.js';
6
+ import './seo-CnqvU41p.js';
7
+ import './blog-Iz3vKyol.js';
8
8
 
9
9
  /**
10
10
  * Session cookie codec — pure (no framework imports, no node imports) so it
@@ -1,10 +1,10 @@
1
1
  import { BoardSdk } from './index.mjs';
2
- import './_spec-DZV8ME1P.mjs';
3
- import './search-1pxkvj_I.mjs';
4
- import './jobs-DfMyFAYs.mjs';
5
- import './board-gmV8MoLW.mjs';
6
- import './seo-CJtLrGD_.mjs';
7
- import './blog-BfVuJQvO.mjs';
2
+ import './_spec-B6YOcfhm.mjs';
3
+ import './search-CbGEG89W.mjs';
4
+ import './jobs-DrDmqiBb.mjs';
5
+ import './board-BwOeOQ6v.mjs';
6
+ import './seo-DiJBveAA.mjs';
7
+ import './blog-BazgjIMl.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-DZV8ME1P.js';
3
- import './search-BPnMYVa7.js';
4
- import './jobs-Di0L5sM1.js';
5
- import './board-Cl6cfS-a.js';
6
- import './seo-DFPj-7Rl.js';
7
- import './blog-CLVfmknV.js';
2
+ import './_spec-B6YOcfhm.js';
3
+ import './search-D4Uo2bRh.js';
4
+ import './jobs-BwxCoTRJ.js';
5
+ import './board-Bd7H1jwl.js';
6
+ import './seo-CnqvU41p.js';
7
+ import './blog-Iz3vKyol.js';
8
8
 
9
9
  /**
10
10
  * Sitemap primitives — the pure XML + bucket-filename logic behind a board
@@ -1,5 +1,5 @@
1
- import { h as SuggestionItem, b as SearchSuggestQuery, F as FetchOptions, g as SuggestResult } from './search-1pxkvj_I.mjs';
2
- import './_spec-DZV8ME1P.mjs';
1
+ import { h as SuggestionItem, b as SearchSuggestQuery, F as FetchOptions, g as SuggestResult } from './search-CbGEG89W.mjs';
2
+ import './_spec-B6YOcfhm.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-BPnMYVa7.js';
2
- import './_spec-DZV8ME1P.js';
1
+ import { h as SuggestionItem, b as SearchSuggestQuery, F as FetchOptions, g as SuggestResult } from './search-D4Uo2bRh.js';
2
+ import './_spec-B6YOcfhm.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.59.1",
3
+ "version": "4.61.0",
4
4
  "description": "Build custom job boards and careers pages with Cavuno’s TypeScript SDK.",
5
5
  "keywords": [
6
6
  "api-client",
@@ -233,7 +233,7 @@
233
233
  },
234
234
  "devDependencies": {
235
235
  "@arethetypeswrong/cli": "^0.18.2",
236
- "@playwright/test": "^1.59.1",
236
+ "@playwright/test": "^1.64.1",
237
237
  "@types/node": "24.9.1",
238
238
  "esbuild": "^0.27.7",
239
239
  "parse5": "7.3.0",
@@ -108,6 +108,36 @@ await board.jobAlerts.updatePreference({
108
108
  });
109
109
  ```
110
110
 
111
+ ## Anonymous lane: digest engagement
112
+
113
+ Digest job links open your job page with `ja_alert`, `ja_email`, `ja_pos` and `ja_mv`. Report the click from the job page's server code, once per full `GET` (not on prefetches or `HEAD`), and forward the visitor's user agent so crawlers and link scanners are left out. `recordClick` always resolves with `status: 'accepted'`; never block the page on it.
114
+
115
+ Digest manage and unsubscribe links carry `ja_alert` and `ja_email`. Spread them into `unsubscribe` to attribute the unsubscribe to that email; a link without them returns `null`, which spreads to nothing.
116
+
117
+ ```ts snippet
118
+ import {
119
+ parseJobAlertClickParams,
120
+ parseJobAlertUnsubscribeParams,
121
+ } from '@cavuno/board';
122
+
123
+ const click = parseJobAlertClickParams(request.url);
124
+ if (click) {
125
+ await board.jobAlerts
126
+ .recordClick({
127
+ ...click,
128
+ jobId: job.id,
129
+ userAgent: request.headers.get('user-agent') ?? undefined,
130
+ })
131
+ .catch(() => undefined);
132
+ }
133
+
134
+ await board.jobAlerts.unsubscribe({
135
+ subscriptionId,
136
+ token,
137
+ ...parseJobAlertUnsubscribeParams(request.url),
138
+ });
139
+ ```
140
+
111
141
  ## Signed-in lane
112
142
 
113
143
  Authenticated alerts use `placeIds`, while anonymous alerts use `placeSlugs`. Create and update accept the same `AlertBody`; update is a full replacement.
@@ -150,6 +180,7 @@ Removal is the signed-in lane's stop action; there is no pause state.
150
180
  - Anonymous subscribe sends `consent: true` and reveals no subscription existence.
151
181
  - The confirm UI handles all four statuses, including resend after expiry.
152
182
  - Manage reads use `subscription`; manage writes use `subscriptionId`.
183
+ - Job pages opened from a digest call `recordClick` once, server-side, with the visitor's user agent; the unsubscribe passes the link's `ja_*` attribution.
153
184
  - Editing one field preserves every other field in both full-replace APIs.
154
185
  - UI promises only weekly delivery filtered by job function, place, and remote option.
155
186
 
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "4.59.1",
2
+ "version": "4.61.0",
3
3
  "skills": [
4
4
  {
5
5
  "name": "cavuno-board-account",