@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/dist/seo.d.ts CHANGED
@@ -1,9 +1,9 @@
1
- import { P as PublicBlogPostSummary, B as BlogAuthorEmbed } from './blog-CvQeoBc8.js';
2
- import { e as JsonLdObject } from './breadcrumbs-CJ5eu_ye.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-CJ5eu_ye.js';
4
- import './_spec-CuSpfit_.js';
5
- import './board-t_j16FrX.js';
6
- import './jobs-CErce8Tw.js';
1
+ import { P as PublicBlogPostSummary, B as BlogAuthorEmbed } from './blog-CcLmN5Tw.js';
2
+ import { e as JsonLdObject } from './breadcrumbs-Bu1lN6qA.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-Bu1lN6qA.js';
4
+ import './_spec-BD5My4zL.js';
5
+ import './board-CF66EdYi.js';
6
+ import './jobs-BbtjrUo2.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-CuSpfit_.mjs';
3
- import './search-BefsJD_f.mjs';
4
- import './board-VrjkKuZ-.mjs';
5
- import './jobs-h-ewgbXn.mjs';
6
- import './seo-BFQIA81t.mjs';
7
- import './blog-21XnewAu.mjs';
2
+ import './_spec-BD5My4zL.mjs';
3
+ import './search-CIjAdPR-.mjs';
4
+ import './board-BRWS1m6D.mjs';
5
+ import './jobs-BPTMfkal.mjs';
6
+ import './seo-BypmVm97.mjs';
7
+ import './blog-Dh-Ru5QZ.mjs';
8
8
 
9
9
  /**
10
10
  * Session cookie codec — pure (no framework imports, no node imports) so it
@@ -111,26 +111,31 @@ declare function getCavunoAppCanonicalRedirectUrl(params: {
111
111
  }): string | null;
112
112
 
113
113
  /**
114
- * Single-flight session refresh — dedupes concurrent refreshes for the same
115
- * session WITHIN one process/isolate (the rotation race, ,
116
- * mitigated at the one layer a client library can reach).
114
+ * Session refresh for server-rendered apps. Each call rotates the given
115
+ * session's refresh token with `board.auth.refresh` and maps the result to a
116
+ * `BoardSession`.
117
117
  *
118
- * Refresh tokens are single-use: two concurrent refreshes for the same
119
- * session burn the pair — the loser 401s and the user is signed out
120
- * mid-session. This helper keys an in-flight slot per refreshToken so every
121
- * concurrent caller awaits the SAME rotation; sequential calls after settle
122
- * start a fresh one.
118
+ * No state is held across calls. The refresher is usually module-scoped and
119
+ * shared by every request, so a promise kept between calls would be shared
120
+ * across requests too. On edge runtimes such as Cloudflare Workers, I/O
121
+ * belongs to the request that started it: when that request is cancelled
122
+ * (for example the client disconnects), its fetch is cancelled and a promise
123
+ * waiting on it never settles. Any later request awaiting that shared promise
124
+ * would hang. Each call therefore performs its own refresh.
123
125
  *
124
- * Scope honestly stated: the dedupe is PER PROCESS/ISOLATE (an in-memory
125
- * map). Two simultaneous requests served by different instances can still
126
- * race the token; the proactive `isExpiringSoon` window keeps that rare, it
127
- * does not eliminate it. Pair with `storage: 'nostore'` on the shared
128
- * server client — `auth.refresh` persists the rotated pair into
129
- * `client.storage`, and any persistent shared storage would bleed one
130
- * user's tokens into another's requests.
126
+ * Concurrent refreshes of the same session are safe without client-side
127
+ * coordination: the refresh endpoint returns the same successor refresh token
128
+ * for a token re-presented within a short grace window, so parallel calls
129
+ * (in one process or across instances) converge on one refresh token. Their
130
+ * access tokens may differ; each is valid.
131
+ *
132
+ * Pair with `storage: 'nostore'` on the shared server client.
133
+ * `auth.refresh` persists the rotated pair into `client.storage`, and any
134
+ * persistent shared storage would bleed one user's tokens into another's
135
+ * requests.
131
136
  *
132
137
  * Returns the rotated `BoardSession` (persist it back to the cookie), or
133
- * `null` on a 401 — the token is burned or revoked: clear the cookie and
138
+ * `null` on a 401: the token is expired or revoked, so clear the cookie and
134
139
  * continue signed out, never retry. Other errors (network, 5xx, 429)
135
140
  * rethrow untouched.
136
141
  *
package/dist/server.d.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  import { BoardSdk } from './index.js';
2
- import './_spec-CuSpfit_.js';
3
- import './search-B2LdM6bj.js';
4
- import './board-t_j16FrX.js';
5
- import './jobs-CErce8Tw.js';
6
- import './seo-BPDNxOiZ.js';
7
- import './blog-CvQeoBc8.js';
2
+ import './_spec-BD5My4zL.js';
3
+ import './search-CqPIHkv8.js';
4
+ import './board-CF66EdYi.js';
5
+ import './jobs-BbtjrUo2.js';
6
+ import './seo-C-NQ0T9w.js';
7
+ import './blog-CcLmN5Tw.js';
8
8
 
9
9
  /**
10
10
  * Session cookie codec — pure (no framework imports, no node imports) so it
@@ -111,26 +111,31 @@ declare function getCavunoAppCanonicalRedirectUrl(params: {
111
111
  }): string | null;
112
112
 
113
113
  /**
114
- * Single-flight session refresh — dedupes concurrent refreshes for the same
115
- * session WITHIN one process/isolate (the rotation race, ,
116
- * mitigated at the one layer a client library can reach).
114
+ * Session refresh for server-rendered apps. Each call rotates the given
115
+ * session's refresh token with `board.auth.refresh` and maps the result to a
116
+ * `BoardSession`.
117
117
  *
118
- * Refresh tokens are single-use: two concurrent refreshes for the same
119
- * session burn the pair — the loser 401s and the user is signed out
120
- * mid-session. This helper keys an in-flight slot per refreshToken so every
121
- * concurrent caller awaits the SAME rotation; sequential calls after settle
122
- * start a fresh one.
118
+ * No state is held across calls. The refresher is usually module-scoped and
119
+ * shared by every request, so a promise kept between calls would be shared
120
+ * across requests too. On edge runtimes such as Cloudflare Workers, I/O
121
+ * belongs to the request that started it: when that request is cancelled
122
+ * (for example the client disconnects), its fetch is cancelled and a promise
123
+ * waiting on it never settles. Any later request awaiting that shared promise
124
+ * would hang. Each call therefore performs its own refresh.
123
125
  *
124
- * Scope honestly stated: the dedupe is PER PROCESS/ISOLATE (an in-memory
125
- * map). Two simultaneous requests served by different instances can still
126
- * race the token; the proactive `isExpiringSoon` window keeps that rare, it
127
- * does not eliminate it. Pair with `storage: 'nostore'` on the shared
128
- * server client — `auth.refresh` persists the rotated pair into
129
- * `client.storage`, and any persistent shared storage would bleed one
130
- * user's tokens into another's requests.
126
+ * Concurrent refreshes of the same session are safe without client-side
127
+ * coordination: the refresh endpoint returns the same successor refresh token
128
+ * for a token re-presented within a short grace window, so parallel calls
129
+ * (in one process or across instances) converge on one refresh token. Their
130
+ * access tokens may differ; each is valid.
131
+ *
132
+ * Pair with `storage: 'nostore'` on the shared server client.
133
+ * `auth.refresh` persists the rotated pair into `client.storage`, and any
134
+ * persistent shared storage would bleed one user's tokens into another's
135
+ * requests.
131
136
  *
132
137
  * Returns the rotated `BoardSession` (persist it back to the cookie), or
133
- * `null` on a 401 — the token is burned or revoked: clear the cookie and
138
+ * `null` on a 401: the token is expired or revoked, so clear the cookie and
134
139
  * continue signed out, never retry. Other errors (network, 5xx, 429)
135
140
  * rethrow untouched.
136
141
  *
package/dist/server.js CHANGED
@@ -224,24 +224,19 @@ function createSessionRefresher(board) {
224
224
  "createSessionRefresher requires the server client to use storage: 'nostore' \u2014 a shared persistent store would leak rotated tokens across requests. Keep the session in the httpOnly cookie and pass tokens per call."
225
225
  );
226
226
  }
227
- const inflight = /* @__PURE__ */ new Map();
228
227
  return async function refresh(session) {
229
- const key = session.refreshToken;
230
- const existing = inflight.get(key);
231
- if (existing) return existing;
232
- const attempt = board.auth.refresh({ refreshToken: key }).then(
233
- (rotated) => ({
228
+ try {
229
+ const rotated = await board.auth.refresh({
230
+ refreshToken: session.refreshToken
231
+ });
232
+ return {
234
233
  accessToken: rotated.accessToken,
235
234
  refreshToken: rotated.refreshToken,
236
235
  expiresAt: rotated.expiresAt
237
- })
238
- ).catch((error) => {
236
+ };
237
+ } catch (error) {
239
238
  if (isUnauthorized(error)) return null;
240
239
  throw error;
241
- }).finally(() => {
242
- inflight.delete(key);
243
- });
244
- inflight.set(key, attempt);
245
- return attempt;
240
+ }
246
241
  };
247
242
  }
package/dist/server.mjs CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-Z4ALWIGS.mjs";
5
5
  import {
6
6
  isUnauthorized
7
- } from "./chunk-EHSB2VAJ.mjs";
7
+ } from "./chunk-WSO6JN5C.mjs";
8
8
 
9
9
  // src/server/cookie.ts
10
10
  var COOKIE_ATTRIBUTES = "Path=/; HttpOnly; Secure; SameSite=Lax";
@@ -159,25 +159,20 @@ function createSessionRefresher(board) {
159
159
  "createSessionRefresher requires the server client to use storage: 'nostore' \u2014 a shared persistent store would leak rotated tokens across requests. Keep the session in the httpOnly cookie and pass tokens per call."
160
160
  );
161
161
  }
162
- const inflight = /* @__PURE__ */ new Map();
163
162
  return async function refresh(session) {
164
- const key = session.refreshToken;
165
- const existing = inflight.get(key);
166
- if (existing) return existing;
167
- const attempt = board.auth.refresh({ refreshToken: key }).then(
168
- (rotated) => ({
163
+ try {
164
+ const rotated = await board.auth.refresh({
165
+ refreshToken: session.refreshToken
166
+ });
167
+ return {
169
168
  accessToken: rotated.accessToken,
170
169
  refreshToken: rotated.refreshToken,
171
170
  expiresAt: rotated.expiresAt
172
- })
173
- ).catch((error) => {
171
+ };
172
+ } catch (error) {
174
173
  if (isUnauthorized(error)) return null;
175
174
  throw error;
176
- }).finally(() => {
177
- inflight.delete(key);
178
- });
179
- inflight.set(key, attempt);
180
- return attempt;
175
+ }
181
176
  };
182
177
  }
183
178
  export {
@@ -1,10 +1,10 @@
1
1
  import { BoardSdk } from './index.mjs';
2
- import './_spec-CuSpfit_.mjs';
3
- import './search-BefsJD_f.mjs';
4
- import './board-VrjkKuZ-.mjs';
5
- import './jobs-h-ewgbXn.mjs';
6
- import './seo-BFQIA81t.mjs';
7
- import './blog-21XnewAu.mjs';
2
+ import './_spec-BD5My4zL.mjs';
3
+ import './search-CIjAdPR-.mjs';
4
+ import './board-BRWS1m6D.mjs';
5
+ import './jobs-BPTMfkal.mjs';
6
+ import './seo-BypmVm97.mjs';
7
+ import './blog-Dh-Ru5QZ.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-CuSpfit_.js';
3
- import './search-B2LdM6bj.js';
4
- import './board-t_j16FrX.js';
5
- import './jobs-CErce8Tw.js';
6
- import './seo-BPDNxOiZ.js';
7
- import './blog-CvQeoBc8.js';
2
+ import './_spec-BD5My4zL.js';
3
+ import './search-CqPIHkv8.js';
4
+ import './board-CF66EdYi.js';
5
+ import './jobs-BbtjrUo2.js';
6
+ import './seo-C-NQ0T9w.js';
7
+ import './blog-CcLmN5Tw.js';
8
8
 
9
9
  /**
10
10
  * Sitemap primitives — the pure XML + bucket-filename logic behind a board
package/dist/sitemap.mjs CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-DLSO6XXY.mjs";
4
4
  import {
5
5
  isNotFound
6
- } from "./chunk-EHSB2VAJ.mjs";
6
+ } from "./chunk-WSO6JN5C.mjs";
7
7
  import {
8
8
  BOARD_PATHS,
9
9
  blogAuthorPath,
@@ -1,5 +1,5 @@
1
- import { h as SuggestionItem, b as SearchSuggestQuery, F as FetchOptions, g as SuggestResult } from './search-BefsJD_f.mjs';
2
- import './_spec-CuSpfit_.mjs';
1
+ import { h as SuggestionItem, b as SearchSuggestQuery, F as FetchOptions, g as SuggestResult } from './search-CIjAdPR-.mjs';
2
+ import './_spec-BD5My4zL.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-B2LdM6bj.js';
2
- import './_spec-CuSpfit_.js';
1
+ import { h as SuggestionItem, b as SearchSuggestQuery, F as FetchOptions, g as SuggestResult } from './search-CqPIHkv8.js';
2
+ import './_spec-BD5My4zL.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.30.0",
3
+ "version": "4.31.1",
4
4
  "description": "Build custom job boards and careers pages with Cavuno’s TypeScript SDK.",
5
5
  "keywords": [
6
6
  "api-client",
@@ -58,7 +58,7 @@ try {
58
58
  }
59
59
  ```
60
60
 
61
- On SSR, use the module-scoped `createSessionRefresher` pattern in `cavuno-board-server-sessions`; it is the single source of truth for concurrency, cookies, retry limits, and per-call authorization headers.
61
+ On SSR, use the module-scoped `createSessionRefresher` pattern in `cavuno-board-server-sessions`; it is the single source of truth for cookies, retry limits, and per-call authorization headers.
62
62
 
63
63
  **Complete when:** concurrent expiry paths share one rotation and the original operation is attempted at most once after a successful refresh.
64
64
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: cavuno-board-server-sessions
3
- description: Wire Cavuno server sessions and board-password access. Use when a server-rendered app needs httpOnly cookies, per-request bearer headers, single-flight refresh, grant cookies, or safe redirect-back handling.
3
+ description: Wire Cavuno server sessions and board-password access. Use when a server-rendered app needs httpOnly cookies, per-request bearer headers, session refresh, grant cookies, or safe redirect-back handling.
4
4
  ---
5
5
 
6
6
  # Wire server sessions
@@ -11,7 +11,7 @@ The server contract has three invariants:
11
11
 
12
12
  1. One module-scoped board client uses server-default `nostore`.
13
13
  2. Each request reads an app-owned httpOnly cookie and passes credentials in that call's headers.
14
- 3. One module-scoped `createSessionRefresher(board)` coordinates refreshes within the process or isolate.
14
+ 3. One module-scoped `createSessionRefresher(board)` refreshes expiring sessions. It holds no state between requests.
15
15
 
16
16
  ## 1. Persist the board-user session
17
17
 
@@ -41,7 +41,7 @@ const clearedCookie = clearSessionCookie();
41
41
 
42
42
  ## 2. Resolve one fresh session per request
43
43
 
44
- Refresh tokens rotate once. Create the refresher beside the shared client so concurrent requests using the same refresh token await one rotation. `isExpiringSoon` selects sessions within the proactive five-minute window.
44
+ Refresh tokens rotate on use. Create the refresher beside the shared client. Each call performs its own refresh; concurrent requests presenting the same refresh token within a short grace window receive the same successor refresh token, so they converge without client-side coordination. `isExpiringSoon` selects sessions within the proactive five-minute window.
45
45
 
46
46
  ```ts snippet
47
47
  import {
@@ -68,9 +68,9 @@ async function resolveSession(cookieHeader: string | null) {
68
68
  }
69
69
  ```
70
70
 
71
- A rotated session is written back before the response completes. A `null` result represents a 401 from a burned or revoked token: clear the cookie and continue signed out. Network, 5xx, and 429 failures rethrow to the app's error boundary. The helper deduplicates within one process or isolate; separate deployment instances can still race.
71
+ A rotated session is written back before the response completes. A `null` result represents a 401 from a burned or revoked token: clear the cookie and continue signed out. Network, 5xx, and 429 failures rethrow to the app's error boundary. The helper keeps no in-flight promise between requests: on edge runtimes such as Cloudflare Workers, a promise started by a request that is later cancelled never settles, and sharing it would hang every later request for that session.
72
72
 
73
- **Complete when:** two simultaneous resolutions for one expiring session invoke one rotation in the process, both observe the same result, success writes the rotated pair, and `null` produces one clear-cookie response with no refresh retry.
73
+ **Complete when:** two simultaneous resolutions for one expiring session both succeed with the same refresh token, success writes the rotated pair, and `null` produces one clear-cookie response with no refresh retry.
74
74
 
75
75
  ## 3. Authenticate each SDK call
76
76
 
@@ -48,7 +48,7 @@ Use `cavuno-board-format`, `cavuno-board-filters`, and `cavuno-board-search-sugg
48
48
 
49
49
  Use `cavuno-board-auth` for board-user registration, login, verification, recovery, and browser storage. In a server-rendered app, use `cavuno-board-server-sessions` as the authority for the httpOnly session cookie, per-call bearer header, and `createSessionRefresher`. Use `cavuno-board-errors` for typed failure branches and board-password grants.
50
50
 
51
- **Complete when:** an authenticated `board.me.retrieve()` succeeds, logout returns the app to a signed-out state, and concurrent server requests share the server skill's single-flight refresher.
51
+ **Complete when:** an authenticated `board.me.retrieve()` succeeds, logout returns the app to a signed-out state, and server requests refresh expiring sessions through the server skill's module-scoped refresher.
52
52
 
53
53
  ## 5. Add only the enabled surfaces
54
54
 
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "4.30.0",
2
+ "version": "4.31.1",
3
3
  "skills": [
4
4
  {
5
5
  "name": "cavuno-board-account",
@@ -136,7 +136,7 @@
136
136
  },
137
137
  {
138
138
  "name": "cavuno-board-server-sessions",
139
- "description": "Wire Cavuno server sessions and board-password access. Use when a server-rendered app needs httpOnly cookies, per-request bearer headers, single-flight refresh, grant cookies, or safe redirect-back handling.",
139
+ "description": "Wire Cavuno server sessions and board-password access. Use when a server-rendered app needs httpOnly cookies, per-request bearer headers, session refresh, grant cookies, or safe redirect-back handling.",
140
140
  "path": "skills/cavuno-board-server-sessions/SKILL.md",
141
141
  "framework": null,
142
142
  "category": "core"