@cavuno/board 4.60.0 → 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 +22 -0
  2. package/dist/{_spec-DtsIUScQ.d.mts → _spec-B6YOcfhm.d.mts} +16 -0
  3. package/dist/{_spec-DtsIUScQ.d.ts → _spec-B6YOcfhm.d.ts} +16 -0
  4. package/dist/{blog-BKnmvibN.d.mts → blog-BazgjIMl.d.mts} +1 -1
  5. package/dist/{blog-VKligt-6.d.ts → blog-Iz3vKyol.d.ts} +1 -1
  6. package/dist/{board-QuwKsDOt.d.ts → board-Bd7H1jwl.d.ts} +2 -2
  7. package/dist/{board-CWx0nu39.d.mts → board-BwOeOQ6v.d.mts} +2 -2
  8. package/dist/{breadcrumbs-BEnF2UAW.d.mts → breadcrumbs-DxlE86G-.d.mts} +2 -2
  9. package/dist/{breadcrumbs-8k7a572J.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 +138 -13
  16. package/dist/index.d.ts +138 -13
  17. package/dist/index.js +111 -2
  18. package/dist/index.mjs +111 -2
  19. package/dist/{jobs-wc4h0pSV.d.ts → jobs-BwxCoTRJ.d.ts} +1 -1
  20. package/dist/{jobs-D-vpMQ6a.d.mts → jobs-DrDmqiBb.d.mts} +1 -1
  21. package/dist/{search-B4fsEVF8.d.mts → search-CbGEG89W.d.mts} +1 -1
  22. package/dist/{search-Cgel3YbJ.d.ts → search-D4Uo2bRh.d.ts} +1 -1
  23. package/dist/{seo-BGyEzgAX.d.ts → seo-CnqvU41p.d.ts} +1 -1
  24. package/dist/{seo-BgMmH3QS.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 +1 -1
  36. package/skills/cavuno-board-job-alerts/SKILL.md +31 -0
  37. package/skills/manifest.json +1 -1
package/dist/index.js CHANGED
@@ -48,6 +48,8 @@ __export(src_exports, {
48
48
  isValidationError: () => isValidationError,
49
49
  lastOwnMessageId: () => lastOwnMessageId,
50
50
  paginate: () => paginate,
51
+ parseJobAlertClickParams: () => parseJobAlertClickParams,
52
+ parseJobAlertUnsubscribeParams: () => parseJobAlertUnsubscribeParams,
51
53
  parseOAuthCompletion: () => parseOAuthCompletion,
52
54
  resolveApplyAction: () => resolveApplyAction,
53
55
  resolveApplyDecision: () => resolveApplyDecision,
@@ -430,7 +432,7 @@ async function clearSession(storage) {
430
432
  }
431
433
 
432
434
  // src/version.ts
433
- var SDK_VERSION = "4.60.0";
435
+ var SDK_VERSION = "4.61.0";
434
436
 
435
437
  // src/client.ts
436
438
  function isRawBody(body) {
@@ -798,6 +800,22 @@ function authNamespace(client, board = "") {
798
800
  body
799
801
  });
800
802
  },
803
+ /**
804
+ * Confirm a profile item's email-domain check with the token from the
805
+ * emailed link. No session required: the single-use token is the
806
+ * authorization. Returns the item, now verified. Any rejected token is a
807
+ * 401 `BoardApiError`; start the check again to recover.
808
+ *
809
+ * @example
810
+ * const item = await board.auth.verifyItemEmail({ token });
811
+ */
812
+ verifyItemEmail(body, options) {
813
+ return client.fetch("/auth/verify-item-email", {
814
+ ...options,
815
+ method: "POST",
816
+ body
817
+ });
818
+ },
801
819
  /**
802
820
  * Verify the signed-in board user's email with the 6-digit code from the
803
821
  * verification email (requires the bearer). Resolves void (204); an
@@ -1329,7 +1347,11 @@ function jobAlertsNamespace(client) {
1329
1347
  query
1330
1348
  });
1331
1349
  },
1332
- /** Deactivate a subscription via the HMAC manage token. */
1350
+ /**
1351
+ * Deactivate a subscription via the HMAC manage token. Spread
1352
+ * `parseJobAlertUnsubscribeParams(url)` into the input to record which
1353
+ * digest the subscriber unsubscribed from.
1354
+ */
1333
1355
  unsubscribe(input, options) {
1334
1356
  return client.fetch("/job-alerts/unsubscribe", {
1335
1357
  ...options,
@@ -1337,6 +1359,19 @@ function jobAlertsNamespace(client) {
1337
1359
  body: input
1338
1360
  });
1339
1361
  },
1362
+ /**
1363
+ * Record a visitor opening a job from a digest email. Call it once on a
1364
+ * job page load whose URL carries the digest params
1365
+ * (`parseJobAlertClickParams(url)` returns them). Resolves with a uniform
1366
+ * `accepted` receipt; the click is recorded after the response.
1367
+ */
1368
+ recordClick(input, options) {
1369
+ return client.fetch("/job-alerts/clicks", {
1370
+ ...options,
1371
+ method: "POST",
1372
+ body: input
1373
+ });
1374
+ },
1340
1375
  /** Re-activate a previously unsubscribed subscription via the manage token. */
1341
1376
  resubscribe(input, options) {
1342
1377
  return client.fetch("/job-alerts/resubscribe", {
@@ -1824,6 +1859,42 @@ function meNamespace(client) {
1824
1859
  body
1825
1860
  });
1826
1861
  },
1862
+ /**
1863
+ * Start an email-domain check of one item on my profile: a 6-digit code
1864
+ * and a link go to an address at the item's domain (for example a work
1865
+ * email at a current employer's website domain). Confirm with
1866
+ * `confirmEmailDomainVerification` or `board.auth.verifyItemEmail`.
1867
+ * `sent` is false when an email for the item went out under a minute
1868
+ * ago and nothing new was sent.
1869
+ *
1870
+ * @example
1871
+ * const check = await board.me.profile.submitEmailDomainVerification({
1872
+ * itemKey: 'experience:abc123',
1873
+ * email: 'me@acme.com',
1874
+ * });
1875
+ */
1876
+ submitEmailDomainVerification(body, options) {
1877
+ return client.fetch(
1878
+ "/me/profile/verifications/email-domain",
1879
+ { ...options, method: "POST", body }
1880
+ );
1881
+ },
1882
+ /**
1883
+ * Confirm an email-domain check with the 6-digit code from the email.
1884
+ * Returns the item, now verified; the emailed link stops working.
1885
+ *
1886
+ * @example
1887
+ * await board.me.profile.confirmEmailDomainVerification({
1888
+ * itemKey: 'experience:abc123',
1889
+ * code: '123456',
1890
+ * });
1891
+ */
1892
+ confirmEmailDomainVerification(body, options) {
1893
+ return client.fetch(
1894
+ "/me/profile/verifications/email-domain/confirm",
1895
+ { ...options, method: "POST", body }
1896
+ );
1897
+ },
1827
1898
  /**
1828
1899
  * Check whether a handle is available for me on this board (my own
1829
1900
  * current handle counts as available). Advisory — `update` re-checks.
@@ -4495,6 +4566,44 @@ function resolveApplyAction(state) {
4495
4566
  return action.kind === "sign-in" ? { kind: "sign-in" } : action;
4496
4567
  }
4497
4568
 
4569
+ // src/job-alert-links.ts
4570
+ var ID = /^[0-9A-Za-z]{16,64}$/;
4571
+ var EMAIL_ID = /^[0-9a-f]{16}$/;
4572
+ var MATCHER_VERSION = /^[a-z0-9-]{1,64}$/;
4573
+ var POSITION = /^[1-9][0-9]{0,2}$/;
4574
+ var MAX_POSITION = 100;
4575
+ function searchParamsOf(url) {
4576
+ if (url instanceof URLSearchParams) return url;
4577
+ if (url instanceof URL) return url.searchParams;
4578
+ if (url.startsWith("?") || !url.includes("/") && url.includes("=")) {
4579
+ return new URLSearchParams(url);
4580
+ }
4581
+ try {
4582
+ return new URL(url, "https://board.invalid").searchParams;
4583
+ } catch {
4584
+ return new URLSearchParams();
4585
+ }
4586
+ }
4587
+ function parseJobAlertClickParams(url) {
4588
+ const params = searchParamsOf(url);
4589
+ const alertId = params.get("ja_alert") ?? "";
4590
+ const emailId = params.get("ja_email") ?? "";
4591
+ const positionRaw = params.get("ja_pos") ?? "";
4592
+ const matcherVersion = params.get("ja_mv") ?? "";
4593
+ const position = POSITION.test(positionRaw) ? Number(positionRaw) : NaN;
4594
+ if (!ID.test(alertId) || !EMAIL_ID.test(emailId) || !MATCHER_VERSION.test(matcherVersion) || !(position >= 1 && position <= MAX_POSITION)) {
4595
+ return null;
4596
+ }
4597
+ return { alertId, emailId, position, matcherVersion };
4598
+ }
4599
+ function parseJobAlertUnsubscribeParams(url) {
4600
+ const params = searchParamsOf(url);
4601
+ const alertId = params.get("ja_alert") ?? "";
4602
+ const emailId = params.get("ja_email") ?? "";
4603
+ if (!ID.test(alertId) || !EMAIL_ID.test(emailId)) return null;
4604
+ return { alertId, emailId };
4605
+ }
4606
+
4498
4607
  // src/index.ts
4499
4608
  function createBoardClient(options) {
4500
4609
  const client = new BoardClient({
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.60.0";
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", {
@@ -1440,6 +1473,42 @@ function meNamespace(client) {
1440
1473
  body
1441
1474
  });
1442
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
+ },
1443
1512
  /**
1444
1513
  * Check whether a handle is available for me on this board (my own
1445
1514
  * current handle counts as available). Advisory — `update` re-checks.
@@ -4081,6 +4150,44 @@ function resolveApplyAction(state) {
4081
4150
  return action.kind === "sign-in" ? { kind: "sign-in" } : action;
4082
4151
  }
4083
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
+
4084
4191
  // src/index.ts
4085
4192
  function createBoardClient(options) {
4086
4193
  const client = new BoardClient({
@@ -4176,6 +4283,8 @@ export {
4176
4283
  isValidationError,
4177
4284
  lastOwnMessageId,
4178
4285
  paginate,
4286
+ parseJobAlertClickParams,
4287
+ parseJobAlertUnsubscribeParams,
4179
4288
  parseOAuthCompletion,
4180
4289
  resolveApplyAction,
4181
4290
  resolveApplyDecision,
@@ -1,4 +1,4 @@
1
- import { S as Schemas } from './_spec-DtsIUScQ.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-DtsIUScQ.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-DtsIUScQ.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-DtsIUScQ.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-DtsIUScQ.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-DtsIUScQ.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-BgMmH3QS.mjs';
2
- import './_spec-DtsIUScQ.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-BGyEzgAX.js';
2
- import './_spec-DtsIUScQ.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-BKnmvibN.mjs';
2
- import { e as JsonLdObject } from './breadcrumbs-BEnF2UAW.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-BEnF2UAW.mjs';
4
- import './_spec-DtsIUScQ.mjs';
5
- import './board-CWx0nu39.mjs';
6
- import './jobs-D-vpMQ6a.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-VKligt-6.js';
2
- import { e as JsonLdObject } from './breadcrumbs-8k7a572J.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-8k7a572J.js';
4
- import './_spec-DtsIUScQ.js';
5
- import './board-QuwKsDOt.js';
6
- import './jobs-wc4h0pSV.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-DtsIUScQ.mjs';
3
- import './search-B4fsEVF8.mjs';
4
- import './jobs-D-vpMQ6a.mjs';
5
- import './board-CWx0nu39.mjs';
6
- import './seo-BgMmH3QS.mjs';
7
- import './blog-BKnmvibN.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-DtsIUScQ.js';
3
- import './search-Cgel3YbJ.js';
4
- import './jobs-wc4h0pSV.js';
5
- import './board-QuwKsDOt.js';
6
- import './seo-BGyEzgAX.js';
7
- import './blog-VKligt-6.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-DtsIUScQ.mjs';
3
- import './search-B4fsEVF8.mjs';
4
- import './jobs-D-vpMQ6a.mjs';
5
- import './board-CWx0nu39.mjs';
6
- import './seo-BgMmH3QS.mjs';
7
- import './blog-BKnmvibN.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-DtsIUScQ.js';
3
- import './search-Cgel3YbJ.js';
4
- import './jobs-wc4h0pSV.js';
5
- import './board-QuwKsDOt.js';
6
- import './seo-BGyEzgAX.js';
7
- import './blog-VKligt-6.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-B4fsEVF8.mjs';
2
- import './_spec-DtsIUScQ.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-Cgel3YbJ.js';
2
- import './_spec-DtsIUScQ.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.60.0",
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",
@@ -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.60.0",
2
+ "version": "4.61.0",
3
3
  "skills": [
4
4
  {
5
5
  "name": "cavuno-board-account",