@cavuno/board 4.69.0 → 4.71.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.
- package/CHANGELOG.md +54 -0
- package/dist/{_spec-CFOj7Qdf.d.mts → _spec-OPrym0QJ.d.mts} +83 -2
- package/dist/{_spec-CFOj7Qdf.d.ts → _spec-OPrym0QJ.d.ts} +83 -2
- package/dist/{blog-BaVJi3q1.d.ts → blog-BlRb84MY.d.ts} +1 -1
- package/dist/{blog-DCRBPnpZ.d.mts → blog-CHQYIhHH.d.mts} +1 -1
- package/dist/{board-DMS3I3sJ.d.mts → board-Btc1dzQB.d.mts} +2 -2
- package/dist/{board-zLt7j6hj.d.ts → board-D74R4Iml.d.ts} +2 -2
- package/dist/{breadcrumbs-yGijUUtR.d.ts → breadcrumbs-Bv76gXvF.d.ts} +2 -2
- package/dist/{breadcrumbs-Dwf0yq7B.d.mts → breadcrumbs-CSm6P79s.d.mts} +2 -2
- package/dist/browser/cavuno-board.global.min.js +1 -1
- package/dist/{chunk-7SAHE7UW.mjs → chunk-AQD4OMX4.mjs} +4 -0
- package/dist/filters.d.mts +2 -2
- package/dist/filters.d.ts +2 -2
- package/dist/format.d.mts +4 -4
- package/dist/format.d.ts +4 -4
- package/dist/index.d.mts +92 -22
- package/dist/index.d.ts +92 -22
- package/dist/index.js +66 -8
- package/dist/index.mjs +63 -9
- package/dist/{jobs-Dz_w1jZq.d.ts → jobs-BMG0vrVI.d.ts} +1 -1
- package/dist/{jobs-Cew_q69W.d.mts → jobs-xjE0IrNu.d.mts} +1 -1
- package/dist/{search-DwvgvtEg.d.mts → search-BJkiU3Ic.d.mts} +1 -1
- package/dist/{search-BqAYR47P.d.ts → search-suUh-E7O.d.ts} +1 -1
- package/dist/{seo-CGy4fY5a.d.ts → seo-CW1JjEXH.d.ts} +1 -1
- package/dist/{seo-FKj1pSHZ.d.mts → seo-DGdqWPLo.d.mts} +1 -1
- package/dist/seo-files.d.mts +2 -2
- package/dist/seo-files.d.ts +2 -2
- package/dist/seo-files.js +6 -0
- package/dist/seo-files.mjs +6 -0
- package/dist/seo.d.mts +6 -6
- package/dist/seo.d.ts +6 -6
- package/dist/server.d.mts +6 -6
- package/dist/server.d.ts +6 -6
- package/dist/server.mjs +1 -1
- package/dist/sitemap.d.mts +6 -6
- package/dist/sitemap.d.ts +6 -6
- package/dist/sitemap.mjs +1 -1
- package/dist/suggest.d.mts +2 -2
- package/dist/suggest.d.ts +2 -2
- package/package.json +1 -1
- package/skills/cavuno-board-account/SKILL.md +16 -0
- package/skills/cavuno-board-auth/SKILL.md +3 -0
- package/skills/cavuno-board-job-alerts/SKILL.md +12 -0
- package/skills/cavuno-board-newsletter/SKILL.md +81 -0
- package/skills/manifest.json +8 -1
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cavuno-board-newsletter
|
|
3
|
+
description: Newsletter sign-up forms with @cavuno/board. Use for an email sign-up for the board's news (footer, blog, landing page), its consent checkbox, the confirmation email, and sendingPaused.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Newsletter sign-up
|
|
7
|
+
|
|
8
|
+
`board.subscribe` signs an address up for the board's own email (news, offers, updates). It is separate from job alerts (`board.jobAlerts.subscribe`): use it for a form whose promise is "get our news", not "get matching jobs".
|
|
9
|
+
|
|
10
|
+
Every sign-up is double opt-in. The person gets a confirmation email, and nothing is sent to them until they click it. The response is the same whether the address is new, waiting to confirm, or already subscribed, so it never tells a visitor who is on the list.
|
|
11
|
+
|
|
12
|
+
## The form
|
|
13
|
+
|
|
14
|
+
- An **unticked** checkbox the person must tick. Its wording must name the board, for example "Send me news and offers from Acme Jobs." Send that exact wording as `consentText`; it is kept as the record of what they agreed to, and a `consentText` without the board's name is refused with a 400.
|
|
15
|
+
- A `source` label for the form, such as `blog-footer` or `homepage-hero` (lowercase letters, digits and hyphens). It shows as the person's Source in the board's dashboard, so give each form its own.
|
|
16
|
+
- A hidden `website` field people leave empty. Bots fill it in; the API then answers as usual and does nothing.
|
|
17
|
+
- The person's time zone as `timeZone`: read `Intl.DateTimeFormat().resolvedOptions().timeZone` in their browser (such as `Europe/Berlin`) and send it with the form. On your server it returns the server's zone, so never fill it in there. The board keeps the first value it gets for a person and ignores an invalid one.
|
|
18
|
+
|
|
19
|
+
Read the board's name from `board.context()` rather than typing it.
|
|
20
|
+
|
|
21
|
+
```ts snippet
|
|
22
|
+
const { name } = await board.context();
|
|
23
|
+
const consentText = `Send me news and offers from ${name}.`;
|
|
24
|
+
|
|
25
|
+
const result = await board.subscribe({
|
|
26
|
+
email,
|
|
27
|
+
consent: true,
|
|
28
|
+
consentText,
|
|
29
|
+
source: 'blog-footer',
|
|
30
|
+
website: honeypotValue,
|
|
31
|
+
timeZone: browserTimeZone, // read in the browser, sent with the form
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
if (result.sendingPaused) {
|
|
35
|
+
// Recorded, but the board's email is paused for now.
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
`pageUrl` is optional; it defaults to the request's `Referer`. Pass it when the call is made from your server, where the `Referer` is not the visitor's page.
|
|
40
|
+
|
|
41
|
+
## After submitting
|
|
42
|
+
|
|
43
|
+
Always show the same message: "Check your inbox to confirm your subscription." Never show "you're already subscribed"; the API deliberately doesn't say.
|
|
44
|
+
|
|
45
|
+
When `sendingPaused` is `true`, add a short note that emails from the board may be delayed. The sign-up is still recorded.
|
|
46
|
+
|
|
47
|
+
## Confirmation
|
|
48
|
+
|
|
49
|
+
The confirmation email links to the board's job alert confirmation page, the same page job alerts use. That page completes the sign-up with `board.jobAlerts.confirm({ token })`; no separate route is needed. `confirmed` and `already_confirmed` mean the person is subscribed, and `not_found` or `expired` mean the link no longer works.
|
|
50
|
+
|
|
51
|
+
The page serves both kinds of link, so word it by `subscription`. A newsletter link answers `subscription: 'marketing'` and never turns job alerts on, so don't tell that person their job alerts are confirmed:
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
const result = await board.jobAlerts.confirm({ token });
|
|
55
|
+
const subscribed =
|
|
56
|
+
result.status === 'confirmed' || result.status === 'already_confirmed';
|
|
57
|
+
const message = !subscribed
|
|
58
|
+
? 'This link no longer works.'
|
|
59
|
+
: result.subscription === 'marketing'
|
|
60
|
+
? "You're subscribed to our emails."
|
|
61
|
+
: 'Your job alert is confirmed.';
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Errors
|
|
65
|
+
|
|
66
|
+
- `validation_bad_request` (400): a field is missing or malformed, `consent` isn't `true`, or `consentText` doesn't name the board.
|
|
67
|
+
- `rate_limited` (429): 3 sign-ups a minute per address across all boards, or 10 requests a minute per IP. A self-hosted site calls from its own server, so all its visitors share that IP limit with every other sign-in and sign-up call from that server; on a builder site Cavuno hosts it counts each visitor separately, plus 6 sign-ups a minute per visitor. `details.retryAfterSeconds` says when to try again.
|
|
68
|
+
- `rate_limit_unavailable` (503): retry shortly.
|
|
69
|
+
|
|
70
|
+
```ts snippet
|
|
71
|
+
import { BoardApiError } from '@cavuno/board';
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
await board.subscribe({ email, consent: true, consentText, source: 'footer' });
|
|
75
|
+
} catch (error) {
|
|
76
|
+
if (error instanceof BoardApiError && error.status === 429) {
|
|
77
|
+
// Ask them to try again in a minute.
|
|
78
|
+
}
|
|
79
|
+
throw error;
|
|
80
|
+
}
|
|
81
|
+
```
|
package/skills/manifest.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "4.
|
|
2
|
+
"version": "4.71.0",
|
|
3
3
|
"skills": [
|
|
4
4
|
{
|
|
5
5
|
"name": "cavuno-board-account",
|
|
@@ -106,6 +106,13 @@
|
|
|
106
106
|
"framework": null,
|
|
107
107
|
"category": "core"
|
|
108
108
|
},
|
|
109
|
+
{
|
|
110
|
+
"name": "cavuno-board-newsletter",
|
|
111
|
+
"description": "Newsletter sign-up forms with @cavuno/board. Use for an email sign-up for the board's news (footer, blog, landing page), its consent checkbox, the confirmation email, and sendingPaused.",
|
|
112
|
+
"path": "skills/cavuno-board-newsletter/SKILL.md",
|
|
113
|
+
"framework": null,
|
|
114
|
+
"category": "core"
|
|
115
|
+
},
|
|
109
116
|
{
|
|
110
117
|
"name": "cavuno-board-paywall",
|
|
111
118
|
"description": "Grant-is-truth candidate paywall with @cavuno/board. Use for offers, gated job counts, embedded checkout handoff, access confirmation, or subscription portal links.",
|