@cavuno/board 1.39.0 → 1.40.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/dist/{_spec-DxC1ze93.d.mts → _spec-DRbgPIlN.d.mts} +351 -0
- package/dist/{_spec-DxC1ze93.d.ts → _spec-DRbgPIlN.d.ts} +351 -0
- package/dist/bin.mjs +242 -18
- package/dist/{board-RfZEAJse.d.ts → board-BxPUtrOl.d.ts} +1 -1
- package/dist/{board-CqYibYUA.d.mts → board-DCiNpgFf.d.mts} +1 -1
- package/dist/doctor.js +238 -14
- package/dist/doctor.mjs +238 -14
- package/dist/filters.d.mts +2 -2
- package/dist/filters.d.ts +2 -2
- package/dist/format.d.mts +3 -3
- package/dist/format.d.ts +3 -3
- package/dist/index.d.mts +55 -10
- package/dist/index.d.ts +55 -10
- package/dist/index.js +50 -1
- package/dist/index.mjs +50 -1
- package/dist/{jobs-CLLIvtMc.d.ts → jobs-DAGAVHQL.d.ts} +1 -1
- package/dist/{jobs-DPPA1Nev.d.mts → jobs-DhePKSRe.d.mts} +1 -1
- package/dist/{salaries-Rb5h_eVZ.d.mts → salaries-D6SUVMmt.d.mts} +2 -2
- package/dist/{salaries-DK4RnJnw.d.ts → salaries-cqb78kg0.d.ts} +2 -2
- package/dist/{search-DBoMM-gE.d.ts → search-BalPAS0P.d.ts} +1 -1
- package/dist/{search-CqBa1Qc4.d.mts → search-DYUQzCq_.d.mts} +1 -1
- package/dist/seo.d.mts +4 -4
- package/dist/seo.d.ts +4 -4
- package/dist/server.d.mts +56 -6
- package/dist/server.d.ts +56 -6
- package/dist/server.js +41 -0
- package/dist/server.mjs +41 -0
- package/dist/sitemap.d.mts +5 -5
- package/dist/sitemap.d.ts +5 -5
- package/dist/suggest.d.mts +2 -2
- package/dist/suggest.d.ts +2 -2
- package/dist/theme.d.mts +49 -1
- package/dist/theme.d.ts +49 -1
- package/dist/theme.js +60 -26
- package/dist/theme.mjs +60 -26
- package/package.json +1 -1
- package/skills/manifest.json +1 -1
package/dist/index.js
CHANGED
|
@@ -312,7 +312,7 @@ async function clearSession(storage) {
|
|
|
312
312
|
}
|
|
313
313
|
|
|
314
314
|
// src/version.ts
|
|
315
|
-
var SDK_VERSION = "1.
|
|
315
|
+
var SDK_VERSION = "1.40.0";
|
|
316
316
|
|
|
317
317
|
// src/client.ts
|
|
318
318
|
function isRawBody(body) {
|
|
@@ -1398,6 +1398,20 @@ function meNamespace(client) {
|
|
|
1398
1398
|
{ ...options, method: "DELETE" }
|
|
1399
1399
|
);
|
|
1400
1400
|
},
|
|
1401
|
+
/**
|
|
1402
|
+
* Retrieve my full editable company profile (summary, socials, logo,
|
|
1403
|
+
* description) — the read half of `update`, for pre-populating an edit
|
|
1404
|
+
* form. Requires an approved membership.
|
|
1405
|
+
*
|
|
1406
|
+
* @example
|
|
1407
|
+
* const company = await board.me.companies.retrieve('acme');
|
|
1408
|
+
*/
|
|
1409
|
+
retrieve(slug, options) {
|
|
1410
|
+
return client.fetch(
|
|
1411
|
+
`/me/companies/${encodeURIComponent(slug)}`,
|
|
1412
|
+
options
|
|
1413
|
+
);
|
|
1414
|
+
},
|
|
1401
1415
|
/**
|
|
1402
1416
|
* Edit my company profile (merge-patch — omitted fields stay
|
|
1403
1417
|
* unchanged). Requires an approved membership. Returns the company.
|
|
@@ -1411,6 +1425,22 @@ function meNamespace(client) {
|
|
|
1411
1425
|
{ ...options, method: "PATCH", body }
|
|
1412
1426
|
);
|
|
1413
1427
|
},
|
|
1428
|
+
/**
|
|
1429
|
+
* Upload + attach my company logo (JPEG/PNG/WebP/GIF, ≤2 MB) via a single
|
|
1430
|
+
* multipart POST. Requires an approved membership. Returns the updated
|
|
1431
|
+
* company with its new `logoUrl`.
|
|
1432
|
+
*
|
|
1433
|
+
* @example
|
|
1434
|
+
* const company = await board.me.companies.uploadLogo('acme', file);
|
|
1435
|
+
*/
|
|
1436
|
+
uploadLogo(slug, file, options) {
|
|
1437
|
+
const form = new FormData();
|
|
1438
|
+
form.append("file", file);
|
|
1439
|
+
return client.fetch(
|
|
1440
|
+
`/me/companies/${encodeURIComponent(slug)}/logo`,
|
|
1441
|
+
{ ...options, method: "POST", body: form }
|
|
1442
|
+
);
|
|
1443
|
+
},
|
|
1414
1444
|
/** The work-email verification state machine for a pending claim. */
|
|
1415
1445
|
workEmail: {
|
|
1416
1446
|
/**
|
|
@@ -1736,6 +1766,25 @@ function meNamespace(client) {
|
|
|
1736
1766
|
}
|
|
1737
1767
|
}
|
|
1738
1768
|
},
|
|
1769
|
+
/**
|
|
1770
|
+
* The viewer's talent entitlement on this board (CAV-243) — "does this
|
|
1771
|
+
* employer have talent access?" for gating a Message-vs-upsell CTA.
|
|
1772
|
+
* Candidates and unaffiliated viewers get `hasTalentAccess: false`.
|
|
1773
|
+
*
|
|
1774
|
+
* @example
|
|
1775
|
+
* const { hasTalentAccess } = await board.me.talentAccess.retrieve();
|
|
1776
|
+
*/
|
|
1777
|
+
talentAccess: {
|
|
1778
|
+
/**
|
|
1779
|
+
* Retrieve the viewer's talent entitlement. Never cache — per-user.
|
|
1780
|
+
*
|
|
1781
|
+
* @example
|
|
1782
|
+
* const access = await board.me.talentAccess.retrieve();
|
|
1783
|
+
*/
|
|
1784
|
+
retrieve(options) {
|
|
1785
|
+
return client.fetch("/me/talent-access", options);
|
|
1786
|
+
}
|
|
1787
|
+
},
|
|
1739
1788
|
/**
|
|
1740
1789
|
* The authenticated board user's job-alert preferences (ADR-0053) —
|
|
1741
1790
|
* a collection (up to 10). Creating one is active immediately (no
|
package/dist/index.mjs
CHANGED
|
@@ -265,7 +265,7 @@ async function clearSession(storage) {
|
|
|
265
265
|
}
|
|
266
266
|
|
|
267
267
|
// src/version.ts
|
|
268
|
-
var SDK_VERSION = "1.
|
|
268
|
+
var SDK_VERSION = "1.40.0";
|
|
269
269
|
|
|
270
270
|
// src/client.ts
|
|
271
271
|
function isRawBody(body) {
|
|
@@ -1351,6 +1351,20 @@ function meNamespace(client) {
|
|
|
1351
1351
|
{ ...options, method: "DELETE" }
|
|
1352
1352
|
);
|
|
1353
1353
|
},
|
|
1354
|
+
/**
|
|
1355
|
+
* Retrieve my full editable company profile (summary, socials, logo,
|
|
1356
|
+
* description) — the read half of `update`, for pre-populating an edit
|
|
1357
|
+
* form. Requires an approved membership.
|
|
1358
|
+
*
|
|
1359
|
+
* @example
|
|
1360
|
+
* const company = await board.me.companies.retrieve('acme');
|
|
1361
|
+
*/
|
|
1362
|
+
retrieve(slug, options) {
|
|
1363
|
+
return client.fetch(
|
|
1364
|
+
`/me/companies/${encodeURIComponent(slug)}`,
|
|
1365
|
+
options
|
|
1366
|
+
);
|
|
1367
|
+
},
|
|
1354
1368
|
/**
|
|
1355
1369
|
* Edit my company profile (merge-patch — omitted fields stay
|
|
1356
1370
|
* unchanged). Requires an approved membership. Returns the company.
|
|
@@ -1364,6 +1378,22 @@ function meNamespace(client) {
|
|
|
1364
1378
|
{ ...options, method: "PATCH", body }
|
|
1365
1379
|
);
|
|
1366
1380
|
},
|
|
1381
|
+
/**
|
|
1382
|
+
* Upload + attach my company logo (JPEG/PNG/WebP/GIF, ≤2 MB) via a single
|
|
1383
|
+
* multipart POST. Requires an approved membership. Returns the updated
|
|
1384
|
+
* company with its new `logoUrl`.
|
|
1385
|
+
*
|
|
1386
|
+
* @example
|
|
1387
|
+
* const company = await board.me.companies.uploadLogo('acme', file);
|
|
1388
|
+
*/
|
|
1389
|
+
uploadLogo(slug, file, options) {
|
|
1390
|
+
const form = new FormData();
|
|
1391
|
+
form.append("file", file);
|
|
1392
|
+
return client.fetch(
|
|
1393
|
+
`/me/companies/${encodeURIComponent(slug)}/logo`,
|
|
1394
|
+
{ ...options, method: "POST", body: form }
|
|
1395
|
+
);
|
|
1396
|
+
},
|
|
1367
1397
|
/** The work-email verification state machine for a pending claim. */
|
|
1368
1398
|
workEmail: {
|
|
1369
1399
|
/**
|
|
@@ -1689,6 +1719,25 @@ function meNamespace(client) {
|
|
|
1689
1719
|
}
|
|
1690
1720
|
}
|
|
1691
1721
|
},
|
|
1722
|
+
/**
|
|
1723
|
+
* The viewer's talent entitlement on this board (CAV-243) — "does this
|
|
1724
|
+
* employer have talent access?" for gating a Message-vs-upsell CTA.
|
|
1725
|
+
* Candidates and unaffiliated viewers get `hasTalentAccess: false`.
|
|
1726
|
+
*
|
|
1727
|
+
* @example
|
|
1728
|
+
* const { hasTalentAccess } = await board.me.talentAccess.retrieve();
|
|
1729
|
+
*/
|
|
1730
|
+
talentAccess: {
|
|
1731
|
+
/**
|
|
1732
|
+
* Retrieve the viewer's talent entitlement. Never cache — per-user.
|
|
1733
|
+
*
|
|
1734
|
+
* @example
|
|
1735
|
+
* const access = await board.me.talentAccess.retrieve();
|
|
1736
|
+
*/
|
|
1737
|
+
retrieve(options) {
|
|
1738
|
+
return client.fetch("/me/talent-access", options);
|
|
1739
|
+
}
|
|
1740
|
+
},
|
|
1692
1741
|
/**
|
|
1693
1742
|
* The authenticated board user's job-alert preferences (ADR-0053) —
|
|
1694
1743
|
* a collection (up to 10). Creating one is active immediately (no
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { S as Schemas } from './_spec-
|
|
2
|
-
import { L as ListEnvelope, m as RelatedSearch } from './jobs-
|
|
1
|
+
import { S as Schemas } from './_spec-DRbgPIlN.mjs';
|
|
2
|
+
import { L as ListEnvelope, m as RelatedSearch } from './jobs-DhePKSRe.mjs';
|
|
3
3
|
|
|
4
4
|
/** Author shape embedded on posts (no `object` discriminator). */
|
|
5
5
|
type BlogAuthorEmbed = Schemas['PublicBlogAuthorEmbed'];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { S as Schemas } from './_spec-
|
|
2
|
-
import { L as ListEnvelope, m as RelatedSearch } from './jobs-
|
|
1
|
+
import { S as Schemas } from './_spec-DRbgPIlN.js';
|
|
2
|
+
import { L as ListEnvelope, m as RelatedSearch } from './jobs-DAGAVHQL.js';
|
|
3
3
|
|
|
4
4
|
/** Author shape embedded on posts (no `object` discriminator). */
|
|
5
5
|
type BlogAuthorEmbed = Schemas['PublicBlogAuthorEmbed'];
|
package/dist/seo.d.mts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { P as PublicBlogPostSummary, B as BlogAuthorEmbed, C as CompanyCategorySalary, a as CompanySalary, L as LocationSalaryDetail, S as SkillSalaryDetail, T as TitleSalaryDetail } from './salaries-
|
|
2
|
-
import { P as PublicBoard } from './board-
|
|
3
|
-
import { P as PublicJob } from './jobs-
|
|
1
|
+
import { P as PublicBlogPostSummary, B as BlogAuthorEmbed, C as CompanyCategorySalary, a as CompanySalary, L as LocationSalaryDetail, S as SkillSalaryDetail, T as TitleSalaryDetail } from './salaries-D6SUVMmt.mjs';
|
|
2
|
+
import { P as PublicBoard } from './board-DCiNpgFf.mjs';
|
|
3
|
+
import { P as PublicJob } from './jobs-DhePKSRe.mjs';
|
|
4
4
|
import { B as BoardLabelOverrides } from './ui-copy-CKfFTtLk.mjs';
|
|
5
|
-
import './_spec-
|
|
5
|
+
import './_spec-DRbgPIlN.mjs';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Google for Jobs `JobPosting` structured data on the `@cavuno/board` wire
|
package/dist/seo.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { P as PublicBlogPostSummary, B as BlogAuthorEmbed, C as CompanyCategorySalary, a as CompanySalary, L as LocationSalaryDetail, S as SkillSalaryDetail, T as TitleSalaryDetail } from './salaries-
|
|
2
|
-
import { P as PublicBoard } from './board-
|
|
3
|
-
import { P as PublicJob } from './jobs-
|
|
1
|
+
import { P as PublicBlogPostSummary, B as BlogAuthorEmbed, C as CompanyCategorySalary, a as CompanySalary, L as LocationSalaryDetail, S as SkillSalaryDetail, T as TitleSalaryDetail } from './salaries-cqb78kg0.js';
|
|
2
|
+
import { P as PublicBoard } from './board-BxPUtrOl.js';
|
|
3
|
+
import { P as PublicJob } from './jobs-DAGAVHQL.js';
|
|
4
4
|
import { B as BoardLabelOverrides } from './ui-copy-CKfFTtLk.js';
|
|
5
|
-
import './_spec-
|
|
5
|
+
import './_spec-DRbgPIlN.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Google for Jobs `JobPosting` structured data on the `@cavuno/board` wire
|
package/dist/server.d.mts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { BoardSdk } from './index.mjs';
|
|
2
|
-
import './_spec-
|
|
3
|
-
import './search-
|
|
4
|
-
import './board-
|
|
5
|
-
import './jobs-
|
|
6
|
-
import './salaries-
|
|
2
|
+
import './_spec-DRbgPIlN.mjs';
|
|
3
|
+
import './search-DYUQzCq_.mjs';
|
|
4
|
+
import './board-DCiNpgFf.mjs';
|
|
5
|
+
import './jobs-DhePKSRe.mjs';
|
|
6
|
+
import './salaries-D6SUVMmt.mjs';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Session cookie codec — pure (no framework imports, no node imports) so it
|
|
@@ -77,6 +77,56 @@ declare function safeRedirectPath(path: string | undefined | null, defaultPath?:
|
|
|
77
77
|
*/
|
|
78
78
|
declare function currentPathFromReferer(referer: string | null): string;
|
|
79
79
|
|
|
80
|
+
/**
|
|
81
|
+
* MIG-10 (added scope) — keep `<slug>.cavuno.app` live after a custom-domain
|
|
82
|
+
* cutover by 308-redirecting to the board's canonical custom domain.
|
|
83
|
+
*
|
|
84
|
+
* ## Where this lives (DEVIATION LOUDLY)
|
|
85
|
+
* **Serving-side, not the dispatch edge.** Production hostname→worker
|
|
86
|
+
* routing is the WFP dispatch Worker's KV map (`hostname → workerName`
|
|
87
|
+
* only — see `docs/recipes/ops/wfp-dispatch-custom-domain.md` and
|
|
88
|
+
* `createWfpReleaseDeploy` / `deterministicWorkerName`). That edge has
|
|
89
|
+
* **no board domain data** (no Convex, no primaryDomain). Inventing a
|
|
90
|
+
* second edge datastore for canonical hosts is forbidden by the ticket.
|
|
91
|
+
*
|
|
92
|
+
* Data source: the board's public context already carries
|
|
93
|
+
* `primaryDomain: string | null` (`GET /v1/boards/:id` via
|
|
94
|
+
* `serializeBoardContext`). The starter loads context for every request
|
|
95
|
+
* (theme, features, analytics) — reuse that field. When `primaryDomain`
|
|
96
|
+
* is set, a request whose Host is a `*.cavuno.app` serving host 308s to
|
|
97
|
+
* `https://<primaryDomain><path+query>` in **one hop**. Domainless boards
|
|
98
|
+
* (`primaryDomain === null`) keep **serving** from cavuno.app (no redirect).
|
|
99
|
+
*
|
|
100
|
+
* Wire this helper into the starter's root middleware / entry (the
|
|
101
|
+
* migration starter's host check) with `Response.redirect(url, 308)`.
|
|
102
|
+
* Pure so unit tests pin behaviour without a Worker runtime.
|
|
103
|
+
*
|
|
104
|
+
* Pattern: Vercel `*.vercel.app` / GitHub `github.io` permanent fallback
|
|
105
|
+
* origin — SEO-neutral 308 to the canonical production host.
|
|
106
|
+
*/
|
|
107
|
+
/**
|
|
108
|
+
* True when the request host is a cavuno.app board-serving host
|
|
109
|
+
* (slug or board-hash subdomain), not the apex and not a preview host
|
|
110
|
+
* we deliberately leave alone.
|
|
111
|
+
*/
|
|
112
|
+
declare function isCavunoAppServingHost(hostname: string): boolean;
|
|
113
|
+
/**
|
|
114
|
+
* When a custom-domain board is hit on its cavuno.app fallback origin,
|
|
115
|
+
* return the one-hop 308 Location to the canonical custom domain.
|
|
116
|
+
* Domainless boards (no primaryDomain) return null — serve in place.
|
|
117
|
+
*/
|
|
118
|
+
declare function getCavunoAppCanonicalRedirectUrl(params: {
|
|
119
|
+
currentHost: string | null;
|
|
120
|
+
/**
|
|
121
|
+
* From `board.context().primaryDomain` — the board's active primary
|
|
122
|
+
* custom domain hostname, or null when domainless.
|
|
123
|
+
*/
|
|
124
|
+
primaryDomain: string | null | undefined;
|
|
125
|
+
/** Full request URL or path+query; path+query is preserved on the hop. */
|
|
126
|
+
requestUrl?: string | null;
|
|
127
|
+
defaultPath?: string;
|
|
128
|
+
}): string | null;
|
|
129
|
+
|
|
80
130
|
/**
|
|
81
131
|
* Single-flight session refresh — dedupes concurrent refreshes for the same
|
|
82
132
|
* session WITHIN one process/isolate (the rotation race, ADR-0057 wart #4,
|
|
@@ -111,4 +161,4 @@ declare function currentPathFromReferer(referer: string | null): string;
|
|
|
111
161
|
*/
|
|
112
162
|
declare function createSessionRefresher(board: Pick<BoardSdk, 'auth' | 'client'>): (session: BoardSession) => Promise<BoardSession | null>;
|
|
113
163
|
|
|
114
|
-
export { BOARD_ACCESS_COOKIE_NAME, type BoardSession, SESSION_COOKIE_NAME, clearGrantCookie, clearSessionCookie, createSessionRefresher, currentPathFromReferer, grantCookieName, isExpiringSoon, parseGrantCookie, parseSessionCookie, safeRedirectPath, serializeGrantCookie, serializeSessionCookie, sessionCookieName };
|
|
164
|
+
export { BOARD_ACCESS_COOKIE_NAME, type BoardSession, SESSION_COOKIE_NAME, clearGrantCookie, clearSessionCookie, createSessionRefresher, currentPathFromReferer, getCavunoAppCanonicalRedirectUrl, grantCookieName, isCavunoAppServingHost, isExpiringSoon, parseGrantCookie, parseSessionCookie, safeRedirectPath, serializeGrantCookie, serializeSessionCookie, sessionCookieName };
|
package/dist/server.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { BoardSdk } from './index.js';
|
|
2
|
-
import './_spec-
|
|
3
|
-
import './search-
|
|
4
|
-
import './board-
|
|
5
|
-
import './jobs-
|
|
6
|
-
import './salaries-
|
|
2
|
+
import './_spec-DRbgPIlN.js';
|
|
3
|
+
import './search-BalPAS0P.js';
|
|
4
|
+
import './board-BxPUtrOl.js';
|
|
5
|
+
import './jobs-DAGAVHQL.js';
|
|
6
|
+
import './salaries-cqb78kg0.js';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Session cookie codec — pure (no framework imports, no node imports) so it
|
|
@@ -77,6 +77,56 @@ declare function safeRedirectPath(path: string | undefined | null, defaultPath?:
|
|
|
77
77
|
*/
|
|
78
78
|
declare function currentPathFromReferer(referer: string | null): string;
|
|
79
79
|
|
|
80
|
+
/**
|
|
81
|
+
* MIG-10 (added scope) — keep `<slug>.cavuno.app` live after a custom-domain
|
|
82
|
+
* cutover by 308-redirecting to the board's canonical custom domain.
|
|
83
|
+
*
|
|
84
|
+
* ## Where this lives (DEVIATION LOUDLY)
|
|
85
|
+
* **Serving-side, not the dispatch edge.** Production hostname→worker
|
|
86
|
+
* routing is the WFP dispatch Worker's KV map (`hostname → workerName`
|
|
87
|
+
* only — see `docs/recipes/ops/wfp-dispatch-custom-domain.md` and
|
|
88
|
+
* `createWfpReleaseDeploy` / `deterministicWorkerName`). That edge has
|
|
89
|
+
* **no board domain data** (no Convex, no primaryDomain). Inventing a
|
|
90
|
+
* second edge datastore for canonical hosts is forbidden by the ticket.
|
|
91
|
+
*
|
|
92
|
+
* Data source: the board's public context already carries
|
|
93
|
+
* `primaryDomain: string | null` (`GET /v1/boards/:id` via
|
|
94
|
+
* `serializeBoardContext`). The starter loads context for every request
|
|
95
|
+
* (theme, features, analytics) — reuse that field. When `primaryDomain`
|
|
96
|
+
* is set, a request whose Host is a `*.cavuno.app` serving host 308s to
|
|
97
|
+
* `https://<primaryDomain><path+query>` in **one hop**. Domainless boards
|
|
98
|
+
* (`primaryDomain === null`) keep **serving** from cavuno.app (no redirect).
|
|
99
|
+
*
|
|
100
|
+
* Wire this helper into the starter's root middleware / entry (the
|
|
101
|
+
* migration starter's host check) with `Response.redirect(url, 308)`.
|
|
102
|
+
* Pure so unit tests pin behaviour without a Worker runtime.
|
|
103
|
+
*
|
|
104
|
+
* Pattern: Vercel `*.vercel.app` / GitHub `github.io` permanent fallback
|
|
105
|
+
* origin — SEO-neutral 308 to the canonical production host.
|
|
106
|
+
*/
|
|
107
|
+
/**
|
|
108
|
+
* True when the request host is a cavuno.app board-serving host
|
|
109
|
+
* (slug or board-hash subdomain), not the apex and not a preview host
|
|
110
|
+
* we deliberately leave alone.
|
|
111
|
+
*/
|
|
112
|
+
declare function isCavunoAppServingHost(hostname: string): boolean;
|
|
113
|
+
/**
|
|
114
|
+
* When a custom-domain board is hit on its cavuno.app fallback origin,
|
|
115
|
+
* return the one-hop 308 Location to the canonical custom domain.
|
|
116
|
+
* Domainless boards (no primaryDomain) return null — serve in place.
|
|
117
|
+
*/
|
|
118
|
+
declare function getCavunoAppCanonicalRedirectUrl(params: {
|
|
119
|
+
currentHost: string | null;
|
|
120
|
+
/**
|
|
121
|
+
* From `board.context().primaryDomain` — the board's active primary
|
|
122
|
+
* custom domain hostname, or null when domainless.
|
|
123
|
+
*/
|
|
124
|
+
primaryDomain: string | null | undefined;
|
|
125
|
+
/** Full request URL or path+query; path+query is preserved on the hop. */
|
|
126
|
+
requestUrl?: string | null;
|
|
127
|
+
defaultPath?: string;
|
|
128
|
+
}): string | null;
|
|
129
|
+
|
|
80
130
|
/**
|
|
81
131
|
* Single-flight session refresh — dedupes concurrent refreshes for the same
|
|
82
132
|
* session WITHIN one process/isolate (the rotation race, ADR-0057 wart #4,
|
|
@@ -111,4 +161,4 @@ declare function currentPathFromReferer(referer: string | null): string;
|
|
|
111
161
|
*/
|
|
112
162
|
declare function createSessionRefresher(board: Pick<BoardSdk, 'auth' | 'client'>): (session: BoardSession) => Promise<BoardSession | null>;
|
|
113
163
|
|
|
114
|
-
export { BOARD_ACCESS_COOKIE_NAME, type BoardSession, SESSION_COOKIE_NAME, clearGrantCookie, clearSessionCookie, createSessionRefresher, currentPathFromReferer, grantCookieName, isExpiringSoon, parseGrantCookie, parseSessionCookie, safeRedirectPath, serializeGrantCookie, serializeSessionCookie, sessionCookieName };
|
|
164
|
+
export { BOARD_ACCESS_COOKIE_NAME, type BoardSession, SESSION_COOKIE_NAME, clearGrantCookie, clearSessionCookie, createSessionRefresher, currentPathFromReferer, getCavunoAppCanonicalRedirectUrl, grantCookieName, isCavunoAppServingHost, isExpiringSoon, parseGrantCookie, parseSessionCookie, safeRedirectPath, serializeGrantCookie, serializeSessionCookie, sessionCookieName };
|
package/dist/server.js
CHANGED
|
@@ -26,7 +26,9 @@ __export(server_exports, {
|
|
|
26
26
|
clearSessionCookie: () => clearSessionCookie,
|
|
27
27
|
createSessionRefresher: () => createSessionRefresher,
|
|
28
28
|
currentPathFromReferer: () => currentPathFromReferer,
|
|
29
|
+
getCavunoAppCanonicalRedirectUrl: () => getCavunoAppCanonicalRedirectUrl,
|
|
29
30
|
grantCookieName: () => grantCookieName,
|
|
31
|
+
isCavunoAppServingHost: () => isCavunoAppServingHost,
|
|
30
32
|
isExpiringSoon: () => isExpiringSoon,
|
|
31
33
|
parseGrantCookie: () => parseGrantCookie,
|
|
32
34
|
parseSessionCookie: () => parseSessionCookie,
|
|
@@ -154,6 +156,45 @@ function currentPathFromReferer(referer) {
|
|
|
154
156
|
}
|
|
155
157
|
}
|
|
156
158
|
|
|
159
|
+
// src/server/canonical-host-redirect.ts
|
|
160
|
+
var CAVUNO_APP_SERVING_SUFFIX = ".cavuno.app";
|
|
161
|
+
function normalizeHostname(host) {
|
|
162
|
+
if (!host) return "";
|
|
163
|
+
return host.toLowerCase().split(":")[0] ?? "";
|
|
164
|
+
}
|
|
165
|
+
function extractPathAndSearch(url) {
|
|
166
|
+
if (!url) return "/";
|
|
167
|
+
try {
|
|
168
|
+
const parsed = new URL(url, "https://placeholder.invalid");
|
|
169
|
+
return `${parsed.pathname}${parsed.search}` || "/";
|
|
170
|
+
} catch {
|
|
171
|
+
return "/";
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
function isCavunoAppServingHost(hostname) {
|
|
175
|
+
const h = hostname.toLowerCase();
|
|
176
|
+
if (!h.endsWith(CAVUNO_APP_SERVING_SUFFIX)) return false;
|
|
177
|
+
if (h === "cavuno.app") return false;
|
|
178
|
+
if (h.startsWith("preview-")) return false;
|
|
179
|
+
return true;
|
|
180
|
+
}
|
|
181
|
+
function getCavunoAppCanonicalRedirectUrl(params) {
|
|
182
|
+
const { currentHost, primaryDomain, requestUrl, defaultPath = "/" } = params;
|
|
183
|
+
const canonical = primaryDomain?.trim().toLowerCase().replace(/\.+$/, "");
|
|
184
|
+
if (!canonical) {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
const hostname = normalizeHostname(currentHost);
|
|
188
|
+
if (!hostname || !isCavunoAppServingHost(hostname)) {
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
if (hostname === canonical) {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
const path = requestUrl != null && requestUrl !== "" ? extractPathAndSearch(requestUrl) : defaultPath;
|
|
195
|
+
return `https://${canonical}${path.startsWith("/") ? path : `/${path}`}`;
|
|
196
|
+
}
|
|
197
|
+
|
|
157
198
|
// src/errors.ts
|
|
158
199
|
function isBoardApiError(e) {
|
|
159
200
|
return e instanceof Error && e.name === "BoardApiError" && typeof e.status === "number" && typeof e.code === "string";
|
package/dist/server.mjs
CHANGED
|
@@ -115,6 +115,45 @@ function currentPathFromReferer(referer) {
|
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
+
// src/server/canonical-host-redirect.ts
|
|
119
|
+
var CAVUNO_APP_SERVING_SUFFIX = ".cavuno.app";
|
|
120
|
+
function normalizeHostname(host) {
|
|
121
|
+
if (!host) return "";
|
|
122
|
+
return host.toLowerCase().split(":")[0] ?? "";
|
|
123
|
+
}
|
|
124
|
+
function extractPathAndSearch(url) {
|
|
125
|
+
if (!url) return "/";
|
|
126
|
+
try {
|
|
127
|
+
const parsed = new URL(url, "https://placeholder.invalid");
|
|
128
|
+
return `${parsed.pathname}${parsed.search}` || "/";
|
|
129
|
+
} catch {
|
|
130
|
+
return "/";
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
function isCavunoAppServingHost(hostname) {
|
|
134
|
+
const h = hostname.toLowerCase();
|
|
135
|
+
if (!h.endsWith(CAVUNO_APP_SERVING_SUFFIX)) return false;
|
|
136
|
+
if (h === "cavuno.app") return false;
|
|
137
|
+
if (h.startsWith("preview-")) return false;
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
function getCavunoAppCanonicalRedirectUrl(params) {
|
|
141
|
+
const { currentHost, primaryDomain, requestUrl, defaultPath = "/" } = params;
|
|
142
|
+
const canonical = primaryDomain?.trim().toLowerCase().replace(/\.+$/, "");
|
|
143
|
+
if (!canonical) {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
const hostname = normalizeHostname(currentHost);
|
|
147
|
+
if (!hostname || !isCavunoAppServingHost(hostname)) {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
if (hostname === canonical) {
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
const path = requestUrl != null && requestUrl !== "" ? extractPathAndSearch(requestUrl) : defaultPath;
|
|
154
|
+
return `https://${canonical}${path.startsWith("/") ? path : `/${path}`}`;
|
|
155
|
+
}
|
|
156
|
+
|
|
118
157
|
// src/errors.ts
|
|
119
158
|
function isBoardApiError(e) {
|
|
120
159
|
return e instanceof Error && e.name === "BoardApiError" && typeof e.status === "number" && typeof e.code === "string";
|
|
@@ -172,7 +211,9 @@ export {
|
|
|
172
211
|
clearSessionCookie,
|
|
173
212
|
createSessionRefresher,
|
|
174
213
|
currentPathFromReferer,
|
|
214
|
+
getCavunoAppCanonicalRedirectUrl,
|
|
175
215
|
grantCookieName,
|
|
216
|
+
isCavunoAppServingHost,
|
|
176
217
|
isExpiringSoon,
|
|
177
218
|
parseGrantCookie,
|
|
178
219
|
parseSessionCookie,
|
package/dist/sitemap.d.mts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { BoardSdk } from './index.mjs';
|
|
2
|
-
import './_spec-
|
|
3
|
-
import './search-
|
|
4
|
-
import './board-
|
|
5
|
-
import './jobs-
|
|
6
|
-
import './salaries-
|
|
2
|
+
import './_spec-DRbgPIlN.mjs';
|
|
3
|
+
import './search-DYUQzCq_.mjs';
|
|
4
|
+
import './board-DCiNpgFf.mjs';
|
|
5
|
+
import './jobs-DhePKSRe.mjs';
|
|
6
|
+
import './salaries-D6SUVMmt.mjs';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Sitemap primitives — the pure XML + bucket-filename logic behind a board
|
package/dist/sitemap.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { BoardSdk } from './index.js';
|
|
2
|
-
import './_spec-
|
|
3
|
-
import './search-
|
|
4
|
-
import './board-
|
|
5
|
-
import './jobs-
|
|
6
|
-
import './salaries-
|
|
2
|
+
import './_spec-DRbgPIlN.js';
|
|
3
|
+
import './search-BalPAS0P.js';
|
|
4
|
+
import './board-BxPUtrOl.js';
|
|
5
|
+
import './jobs-DAGAVHQL.js';
|
|
6
|
+
import './salaries-cqb78kg0.js';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Sitemap primitives — the pure XML + bucket-filename logic behind a board
|
package/dist/suggest.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { S as SuggestionItem, a as SearchSuggestQuery, F as FetchOptions, b as SuggestResult } from './search-
|
|
2
|
-
import './_spec-
|
|
1
|
+
import { S as SuggestionItem, a as SearchSuggestQuery, F as FetchOptions, b as SuggestResult } from './search-DYUQzCq_.mjs';
|
|
2
|
+
import './_spec-DRbgPIlN.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 { S as SuggestionItem, a as SearchSuggestQuery, F as FetchOptions, b as SuggestResult } from './search-
|
|
2
|
-
import './_spec-
|
|
1
|
+
import { S as SuggestionItem, a as SearchSuggestQuery, F as FetchOptions, b as SuggestResult } from './search-BalPAS0P.js';
|
|
2
|
+
import './_spec-DRbgPIlN.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* `@cavuno/board/suggest` — framework-agnostic headless search-suggest
|
package/dist/theme.d.mts
CHANGED
|
@@ -40,6 +40,54 @@ interface ThemeInput {
|
|
|
40
40
|
* lives in exactly one place.
|
|
41
41
|
*/
|
|
42
42
|
declare function isSafeThemeColorValue(value: string): boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Email-safe snapshot keys (platform brand snapshot / MIG-04).
|
|
45
|
+
* Light-only subset consumed by Convex email branding.
|
|
46
|
+
*/
|
|
47
|
+
type EmailSafeSnapshotColorKey = 'buttonPrimary' | 'buttonPrimaryText' | 'background' | 'mutedBackground' | 'border' | 'text' | 'textMuted' | 'brandColor';
|
|
48
|
+
/**
|
|
49
|
+
* Inverse of the email-safe subset of `tokenLines`: for each snapshot
|
|
50
|
+
* color key, the ordered list of CSS custom property names (no `--`)
|
|
51
|
+
* that may supply it. First PRESENT property wins when DIFFERENT CSS
|
|
52
|
+
* names map to one snapshot key (e.g. muted > secondary for
|
|
53
|
+
* mutedBackground). Within one CSS property name, multi-`:root` cascade
|
|
54
|
+
* is last-wins and is resolved before this table is consulted.
|
|
55
|
+
*
|
|
56
|
+
* Additive export for the platform brand-snapshot deriver (MIG-04) —
|
|
57
|
+
* keeps the CSS ↔ board contract next to its forward twin.
|
|
58
|
+
*/
|
|
59
|
+
declare const EMAIL_SAFE_SNAPSHOT_FROM_CSS: {
|
|
60
|
+
readonly [K in EmailSafeSnapshotColorKey]: readonly string[];
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Board key → CSS custom property name(s) (no `--` prefix).
|
|
64
|
+
*
|
|
65
|
+
* The forward write contract for the builder theme tab (MIG-05) and the
|
|
66
|
+
* emission table for `boardThemeToCss` / `tokenLines` — one table, so a
|
|
67
|
+
* tab edit and a render path cannot drift. When a board key fans out to
|
|
68
|
+
* several CSS names (e.g. `text` → foreground + card-foreground + …),
|
|
69
|
+
* a single tab edit writes every name with the same hex value.
|
|
70
|
+
*
|
|
71
|
+
* Fallbacks that only apply at *emit* time when a preferred key is
|
|
72
|
+
* absent (`destructive` ← buttonDanger ?? textError; `ring` ← brandColor
|
|
73
|
+
* ?? buttonPrimary) stay in `tokenLines` — they are not owned write
|
|
74
|
+
* targets of the fallback key.
|
|
75
|
+
*/
|
|
76
|
+
declare const BOARD_COLOR_TO_CSS_TOKENS: {
|
|
77
|
+
readonly [K in BoardColorKey]: readonly string[];
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* CSS custom property names (no `--`) that fan out from *optional*
|
|
81
|
+
* ColorTokensSchema keys (`textSubtle`, `textDisabled`). Production
|
|
82
|
+
* starter `tokens.css` may omit these declarations; the builder theme
|
|
83
|
+
* batch lane SKIPS missing optional declarations rather than escalating
|
|
84
|
+
* the whole preset to the model (MIG-05). Required declarations still
|
|
85
|
+
* escalate on absence — never invent (L5-B).
|
|
86
|
+
*/
|
|
87
|
+
declare const OPTIONAL_THEME_DECLARATIONS: readonly ["foreground-subtle", "foreground-disabled"];
|
|
88
|
+
type OptionalThemeDeclaration = (typeof OPTIONAL_THEME_DECLARATIONS)[number];
|
|
89
|
+
/** True when a CSS custom property name (with or without `--`) is optional. */
|
|
90
|
+
declare function isOptionalThemeDeclaration(token: string): boolean;
|
|
43
91
|
/**
|
|
44
92
|
* Render the board theme as `:root` (+ `.dark`) CSS-variable overrides.
|
|
45
93
|
* Inject once at the app shell, after the static theme stylesheet.
|
|
@@ -67,4 +115,4 @@ declare function themeFontFamily(fontKey: string): string;
|
|
|
67
115
|
/** One Google Fonts request covering the sans + heading families. */
|
|
68
116
|
declare function googleFontsUrl(theme: ThemeInput | null): string | null;
|
|
69
117
|
|
|
70
|
-
export { BOARD_COLOR_KEYS, type BoardColorKey, THEME_FONT_GOOGLE_FAMILIES, type ThemeInput, boardThemeToCss, googleFontsUrl, isSafeThemeColorValue, themeFontFamily, themeMode };
|
|
118
|
+
export { BOARD_COLOR_KEYS, BOARD_COLOR_TO_CSS_TOKENS, type BoardColorKey, EMAIL_SAFE_SNAPSHOT_FROM_CSS, type EmailSafeSnapshotColorKey, OPTIONAL_THEME_DECLARATIONS, type OptionalThemeDeclaration, THEME_FONT_GOOGLE_FAMILIES, type ThemeInput, boardThemeToCss, googleFontsUrl, isOptionalThemeDeclaration, isSafeThemeColorValue, themeFontFamily, themeMode };
|