@cavuno/board 2.0.5 → 3.1.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 (73) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +12 -3
  3. package/dist/{_spec-BKtVFcV9.d.mts → _spec-nkWAraS0.d.mts} +4 -18
  4. package/dist/{_spec-BKtVFcV9.d.ts → _spec-nkWAraS0.d.ts} +4 -18
  5. package/dist/bin.mjs +40 -57
  6. package/dist/{board-3Xj_Y-Tx.d.ts → board-NE98PlwN.d.ts} +2 -3
  7. package/dist/{board-pNN710Zc.d.mts → board-muu8BI8P.d.mts} +2 -3
  8. package/dist/doctor.js +21 -47
  9. package/dist/doctor.mjs +24 -50
  10. package/dist/filters.d.mts +2 -2
  11. package/dist/filters.d.ts +2 -2
  12. package/dist/format.d.mts +3 -3
  13. package/dist/format.d.ts +3 -3
  14. package/dist/index.d.mts +11 -27
  15. package/dist/index.d.ts +11 -27
  16. package/dist/index.js +3 -3
  17. package/dist/index.mjs +3 -3
  18. package/dist/{jobs-C4qRfCdP.d.ts → jobs-BiEvL9d7.d.ts} +1 -1
  19. package/dist/{jobs-k84C0v_8.d.mts → jobs-lVvKCBCU.d.mts} +1 -1
  20. package/dist/{salaries--J5mTylN.d.ts → salaries-BahBpxWT.d.ts} +2 -2
  21. package/dist/{salaries-C9YLO5NG.d.mts → salaries-ChSqDBAM.d.mts} +2 -2
  22. package/dist/{search-CE_qQiM7.d.mts → search-CCyFWPi3.d.mts} +1 -1
  23. package/dist/{search-CLRQ_El9.d.ts → search-CyRvVMUp.d.ts} +1 -1
  24. package/dist/seo.d.mts +4 -4
  25. package/dist/seo.d.ts +4 -4
  26. package/dist/server.d.mts +5 -5
  27. package/dist/server.d.ts +5 -5
  28. package/dist/sitemap.d.mts +5 -5
  29. package/dist/sitemap.d.ts +5 -5
  30. package/dist/skills.d.mts +15 -4
  31. package/dist/skills.d.ts +15 -4
  32. package/dist/skills.js +30 -7
  33. package/dist/skills.mjs +30 -8
  34. package/dist/suggest.d.mts +2 -2
  35. package/dist/suggest.d.ts +2 -2
  36. package/package.json +1 -11
  37. package/skills/cavuno-board-account/SKILL.md +58 -66
  38. package/skills/cavuno-board-api-client/SKILL.md +78 -0
  39. package/skills/cavuno-board-applications/SKILL.md +42 -52
  40. package/skills/cavuno-board-auth/SKILL.md +39 -60
  41. package/skills/cavuno-board-blog/SKILL.md +64 -49
  42. package/skills/cavuno-board-companies/SKILL.md +75 -53
  43. package/skills/cavuno-board-errors/SKILL.md +32 -45
  44. package/skills/cavuno-board-filters/SKILL.md +68 -75
  45. package/skills/cavuno-board-format/SKILL.md +65 -66
  46. package/skills/cavuno-board-i18n/SKILL.md +43 -68
  47. package/skills/cavuno-board-job-alerts/SKILL.md +72 -57
  48. package/skills/cavuno-board-jobs/SKILL.md +67 -55
  49. package/skills/cavuno-board-messaging/SKILL.md +57 -59
  50. package/skills/cavuno-board-paywall/SKILL.md +52 -54
  51. package/skills/cavuno-board-post-a-job/SKILL.md +122 -0
  52. package/skills/cavuno-board-salaries/SKILL.md +77 -50
  53. package/skills/cavuno-board-search-suggestions/SKILL.md +86 -0
  54. package/skills/cavuno-board-seo/BLOG.md +40 -0
  55. package/skills/cavuno-board-seo/JOB_AND_BREADCRUMBS.md +49 -0
  56. package/skills/cavuno-board-seo/LISTING.md +36 -0
  57. package/skills/cavuno-board-seo/SALARY.md +61 -0
  58. package/skills/cavuno-board-seo/SKILL.md +34 -161
  59. package/skills/cavuno-board-server-sessions/SKILL.md +147 -0
  60. package/skills/cavuno-board-setup/SKILL.md +46 -79
  61. package/skills/cavuno-board-sitemap/SKILL.md +78 -91
  62. package/skills/cavuno-board-smoke-test/SKILL.md +38 -69
  63. package/skills/manifest.json +43 -57
  64. package/dist/theme.d.mts +0 -115
  65. package/dist/theme.d.ts +0 -115
  66. package/dist/theme.js +0 -195
  67. package/dist/theme.mjs +0 -174
  68. package/skills/cavuno-board-client/SKILL.md +0 -89
  69. package/skills/cavuno-board-job-posting/SKILL.md +0 -116
  70. package/skills/cavuno-board-server/SKILL.md +0 -229
  71. package/skills/cavuno-board-suggest/SKILL.md +0 -119
  72. package/skills/cavuno-board-theme/SKILL.md +0 -57
  73. package/skills/flavors/tanstack-start/SKILL.md +0 -102
package/dist/theme.d.mts DELETED
@@ -1,115 +0,0 @@
1
- /**
2
- * `@cavuno/board/theme` — board theme → shadcn CSS-variable overrides
3
- *.
4
- *
5
- * The board's stored theme (16 semantic colors × light/dark from
6
- * `board.context().theme`) is mapped onto the canonical shadcn token
7
- * vocabulary and emitted as overrides of the app's theme block. One
8
- * contract, two consumers: the dashboard edits the board theme, and agents
9
- * restyle through the standard shadcn theme file — both end up in the same
10
- * tokens. The hosted board renders the same 16 source colors through its
11
- * own token system; a coverage golden asserts neither side
12
- * silently drops a color key.
13
- *
14
- * A null theme emits nothing — the app's static default theme applies
15
- * untouched.
16
- */
17
- /** The 16 board color keys (the wire ships them as an open record). */
18
- declare const BOARD_COLOR_KEYS: readonly ["brandColor", "buttonPrimary", "buttonPrimaryText", "buttonDanger", "background", "surfaceBackground", "mutedBackground", "highlightBackground", "text", "textSubtle", "textMuted", "textDisabled", "textError", "border", "contrastBackground", "contrastText"];
19
- type BoardColorKey = (typeof BOARD_COLOR_KEYS)[number];
20
- /**
21
- * Structural input — satisfied by the SDK's `PublicBoardTheme` (whose color
22
- * records are open on the wire) and any narrowed server copy.
23
- */
24
- interface ThemeInput {
25
- mode: string;
26
- schemeId: string;
27
- typography: {
28
- fontSans: string;
29
- fontHeading?: string | null;
30
- };
31
- colors: {
32
- light?: Record<string, unknown>;
33
- dark?: Record<string, unknown>;
34
- };
35
- }
36
- /**
37
- * True when `value` is safe to emit inside a `<style>` CSS declaration — i.e.
38
- * carries none of the breakout characters above. Shared by the SDK render sink
39
- * (`boardThemeToCss`) and the hosted public-API serializer, so the denylist
40
- * lives in exactly one place.
41
- */
42
- declare function isSafeThemeColorValue(value: string): boolean;
43
- /**
44
- * Email-safe snapshot keys.
45
- * Light-only subset consumed by server-rendered 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 —
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 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 (without `--`) backed by optional color tokens.
81
- * A starter may omit these declarations; required declarations must still
82
- * be present.
83
- */
84
- declare const OPTIONAL_THEME_DECLARATIONS: readonly ["foreground-subtle", "foreground-disabled"];
85
- type OptionalThemeDeclaration = (typeof OPTIONAL_THEME_DECLARATIONS)[number];
86
- /** True when a CSS custom property name (with or without `--`) is optional. */
87
- declare function isOptionalThemeDeclaration(token: string): boolean;
88
- /**
89
- * Render the board theme as `:root` (+ `.dark`) CSS-variable overrides.
90
- * Inject once at the app shell, after the static theme stylesheet.
91
- *
92
- * @example
93
- * const css = boardThemeToCss((await board.context()).theme);
94
- */
95
- declare function boardThemeToCss(theme: ThemeInput | null): string;
96
- /** The board's color-scheme preference; unknown/absent → `system`. */
97
- declare function themeMode(theme: ThemeInput | null): 'light' | 'dark' | 'system';
98
- /**
99
- * Theme font key → Google Fonts family slug, transcribed from the hosted
100
- * board's `theme-fonts-metadata.ts` (tested against it). The wire's
101
- * `typography.fontSans` is an internal KEY (`'source-sans-pro'`), and the
102
- * Google family is not always a re-casing of it (`source-sans-pro` →
103
- * `Source+Sans+3`) — never build a font request from the raw key.
104
- */
105
- declare const THEME_FONT_GOOGLE_FAMILIES: Record<string, string>;
106
- /**
107
- * The CSS `font-family` name the Google stylesheet registers for a font key
108
- * — the name `--font-sans` must reference for the loaded font to apply
109
- * (e.g. `'source-sans-pro'` → `"Source Sans 3"`).
110
- */
111
- declare function themeFontFamily(fontKey: string): string;
112
- /** One Google Fonts request covering the sans + heading families. */
113
- declare function googleFontsUrl(theme: ThemeInput | null): string | null;
114
-
115
- 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 };
package/dist/theme.d.ts DELETED
@@ -1,115 +0,0 @@
1
- /**
2
- * `@cavuno/board/theme` — board theme → shadcn CSS-variable overrides
3
- *.
4
- *
5
- * The board's stored theme (16 semantic colors × light/dark from
6
- * `board.context().theme`) is mapped onto the canonical shadcn token
7
- * vocabulary and emitted as overrides of the app's theme block. One
8
- * contract, two consumers: the dashboard edits the board theme, and agents
9
- * restyle through the standard shadcn theme file — both end up in the same
10
- * tokens. The hosted board renders the same 16 source colors through its
11
- * own token system; a coverage golden asserts neither side
12
- * silently drops a color key.
13
- *
14
- * A null theme emits nothing — the app's static default theme applies
15
- * untouched.
16
- */
17
- /** The 16 board color keys (the wire ships them as an open record). */
18
- declare const BOARD_COLOR_KEYS: readonly ["brandColor", "buttonPrimary", "buttonPrimaryText", "buttonDanger", "background", "surfaceBackground", "mutedBackground", "highlightBackground", "text", "textSubtle", "textMuted", "textDisabled", "textError", "border", "contrastBackground", "contrastText"];
19
- type BoardColorKey = (typeof BOARD_COLOR_KEYS)[number];
20
- /**
21
- * Structural input — satisfied by the SDK's `PublicBoardTheme` (whose color
22
- * records are open on the wire) and any narrowed server copy.
23
- */
24
- interface ThemeInput {
25
- mode: string;
26
- schemeId: string;
27
- typography: {
28
- fontSans: string;
29
- fontHeading?: string | null;
30
- };
31
- colors: {
32
- light?: Record<string, unknown>;
33
- dark?: Record<string, unknown>;
34
- };
35
- }
36
- /**
37
- * True when `value` is safe to emit inside a `<style>` CSS declaration — i.e.
38
- * carries none of the breakout characters above. Shared by the SDK render sink
39
- * (`boardThemeToCss`) and the hosted public-API serializer, so the denylist
40
- * lives in exactly one place.
41
- */
42
- declare function isSafeThemeColorValue(value: string): boolean;
43
- /**
44
- * Email-safe snapshot keys.
45
- * Light-only subset consumed by server-rendered 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 —
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 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 (without `--`) backed by optional color tokens.
81
- * A starter may omit these declarations; required declarations must still
82
- * be present.
83
- */
84
- declare const OPTIONAL_THEME_DECLARATIONS: readonly ["foreground-subtle", "foreground-disabled"];
85
- type OptionalThemeDeclaration = (typeof OPTIONAL_THEME_DECLARATIONS)[number];
86
- /** True when a CSS custom property name (with or without `--`) is optional. */
87
- declare function isOptionalThemeDeclaration(token: string): boolean;
88
- /**
89
- * Render the board theme as `:root` (+ `.dark`) CSS-variable overrides.
90
- * Inject once at the app shell, after the static theme stylesheet.
91
- *
92
- * @example
93
- * const css = boardThemeToCss((await board.context()).theme);
94
- */
95
- declare function boardThemeToCss(theme: ThemeInput | null): string;
96
- /** The board's color-scheme preference; unknown/absent → `system`. */
97
- declare function themeMode(theme: ThemeInput | null): 'light' | 'dark' | 'system';
98
- /**
99
- * Theme font key → Google Fonts family slug, transcribed from the hosted
100
- * board's `theme-fonts-metadata.ts` (tested against it). The wire's
101
- * `typography.fontSans` is an internal KEY (`'source-sans-pro'`), and the
102
- * Google family is not always a re-casing of it (`source-sans-pro` →
103
- * `Source+Sans+3`) — never build a font request from the raw key.
104
- */
105
- declare const THEME_FONT_GOOGLE_FAMILIES: Record<string, string>;
106
- /**
107
- * The CSS `font-family` name the Google stylesheet registers for a font key
108
- * — the name `--font-sans` must reference for the loaded font to apply
109
- * (e.g. `'source-sans-pro'` → `"Source Sans 3"`).
110
- */
111
- declare function themeFontFamily(fontKey: string): string;
112
- /** One Google Fonts request covering the sans + heading families. */
113
- declare function googleFontsUrl(theme: ThemeInput | null): string | null;
114
-
115
- 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 };
package/dist/theme.js DELETED
@@ -1,195 +0,0 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
-
20
- // src/theme/index.ts
21
- var theme_exports = {};
22
- __export(theme_exports, {
23
- BOARD_COLOR_KEYS: () => BOARD_COLOR_KEYS,
24
- BOARD_COLOR_TO_CSS_TOKENS: () => BOARD_COLOR_TO_CSS_TOKENS,
25
- EMAIL_SAFE_SNAPSHOT_FROM_CSS: () => EMAIL_SAFE_SNAPSHOT_FROM_CSS,
26
- OPTIONAL_THEME_DECLARATIONS: () => OPTIONAL_THEME_DECLARATIONS,
27
- THEME_FONT_GOOGLE_FAMILIES: () => THEME_FONT_GOOGLE_FAMILIES,
28
- boardThemeToCss: () => boardThemeToCss,
29
- googleFontsUrl: () => googleFontsUrl,
30
- isOptionalThemeDeclaration: () => isOptionalThemeDeclaration,
31
- isSafeThemeColorValue: () => isSafeThemeColorValue,
32
- themeFontFamily: () => themeFontFamily,
33
- themeMode: () => themeMode
34
- });
35
- module.exports = __toCommonJS(theme_exports);
36
- var BOARD_COLOR_KEYS = [
37
- "brandColor",
38
- "buttonPrimary",
39
- "buttonPrimaryText",
40
- "buttonDanger",
41
- "background",
42
- "surfaceBackground",
43
- "mutedBackground",
44
- "highlightBackground",
45
- "text",
46
- "textSubtle",
47
- "textMuted",
48
- "textDisabled",
49
- "textError",
50
- "border",
51
- "contrastBackground",
52
- "contrastText"
53
- ];
54
- var CSS_INJECTION_CHARS = /[<>{};]|[\u0000-\u001f\u007f-\u009f\u2028\u2029]/;
55
- function isSafeThemeColorValue(value) {
56
- return !CSS_INJECTION_CHARS.test(value);
57
- }
58
- function color(colors, key) {
59
- const value = colors[key];
60
- if (typeof value !== "string" || !value) return void 0;
61
- return isSafeThemeColorValue(value) ? value : void 0;
62
- }
63
- var EMAIL_SAFE_SNAPSHOT_FROM_CSS = {
64
- buttonPrimary: ["primary"],
65
- buttonPrimaryText: ["primary-foreground"],
66
- background: ["background"],
67
- // Muted wins; secondary fills only when --muted is absent.
68
- mutedBackground: ["muted", "secondary"],
69
- border: ["border"],
70
- text: ["foreground"],
71
- textMuted: ["muted-foreground"],
72
- brandColor: ["ring"]
73
- };
74
- var BOARD_COLOR_TO_CSS_TOKENS = {
75
- background: ["background"],
76
- text: [
77
- "foreground",
78
- "card-foreground",
79
- "popover-foreground",
80
- "secondary-foreground",
81
- "accent-foreground"
82
- ],
83
- surfaceBackground: ["card", "popover"],
84
- mutedBackground: ["secondary", "muted"],
85
- highlightBackground: ["accent"],
86
- buttonPrimary: ["primary"],
87
- buttonPrimaryText: ["primary-foreground"],
88
- buttonDanger: ["destructive"],
89
- textMuted: ["muted-foreground"],
90
- border: ["border", "input"],
91
- brandColor: ["ring"],
92
- contrastBackground: ["contrast-background"],
93
- contrastText: ["contrast-foreground"],
94
- textSubtle: ["foreground-subtle"],
95
- textDisabled: ["foreground-disabled"],
96
- textError: ["foreground-error"]
97
- };
98
- var OPTIONAL_THEME_DECLARATIONS = [
99
- "foreground-subtle",
100
- "foreground-disabled"
101
- ];
102
- function isOptionalThemeDeclaration(token) {
103
- const name = token.startsWith("--") ? token.slice(2) : token;
104
- return OPTIONAL_THEME_DECLARATIONS.includes(name);
105
- }
106
- function tokenLines(colors) {
107
- const lines = [];
108
- const set = (token, value) => {
109
- if (value) lines.push(` --${token}: ${value};`);
110
- };
111
- for (const boardKey of BOARD_COLOR_KEYS) {
112
- const value = color(colors, boardKey);
113
- if (!value) continue;
114
- for (const cssToken of BOARD_COLOR_TO_CSS_TOKENS[boardKey]) {
115
- set(cssToken, value);
116
- }
117
- }
118
- if (!color(colors, "buttonDanger") && color(colors, "textError")) {
119
- set("destructive", color(colors, "textError"));
120
- }
121
- if (!color(colors, "brandColor") && color(colors, "buttonPrimary")) {
122
- set("ring", color(colors, "buttonPrimary"));
123
- }
124
- return lines;
125
- }
126
- function boardThemeToCss(theme) {
127
- if (!theme) return "";
128
- const light = tokenLines(theme.colors.light ?? {});
129
- const dark = tokenLines(theme.colors.dark ?? {});
130
- const fontSans = theme.typography?.fontSans;
131
- const genericStack = "ui-sans-serif, system-ui, sans-serif";
132
- const sansStack = fontSans ? `'${themeFontFamily(fontSans)}', ${genericStack}` : genericStack;
133
- if (fontSans) {
134
- light.push(` --font-sans: ${sansStack};`);
135
- }
136
- const fontHeading = theme.typography?.fontHeading;
137
- if (fontHeading) {
138
- light.push(
139
- ` --font-heading: '${themeFontFamily(fontHeading)}', ${sansStack};`
140
- );
141
- }
142
- const blocks = [];
143
- if (light.length > 0) blocks.push(`:root {
144
- ${light.join("\n")}
145
- }`);
146
- if (dark.length > 0) blocks.push(`.dark {
147
- ${dark.join("\n")}
148
- }`);
149
- return blocks.join("\n\n");
150
- }
151
- function themeMode(theme) {
152
- if (!theme) return "system";
153
- return theme.mode === "light" || theme.mode === "dark" ? theme.mode : "system";
154
- }
155
- var THEME_FONT_GOOGLE_FAMILIES = {
156
- "be-vietnam-pro": "Be+Vietnam+Pro",
157
- inter: "Inter",
158
- "plus-jakarta-sans": "Plus+Jakarta+Sans",
159
- "dm-sans": "DM+Sans",
160
- "public-sans": "Public+Sans",
161
- figtree: "Figtree",
162
- "work-sans": "Work+Sans",
163
- "open-sans": "Open+Sans",
164
- poppins: "Poppins",
165
- hind: "Hind",
166
- lexend: "Lexend",
167
- "fira-sans": "Fira+Sans",
168
- manrope: "Manrope",
169
- "source-sans-pro": "Source+Sans+3",
170
- outfit: "Outfit",
171
- "space-grotesk": "Space+Grotesk",
172
- geist: "Geist",
173
- "source-serif-4": "Source+Serif+4",
174
- lora: "Lora",
175
- "crimson-pro": "Crimson+Pro"
176
- };
177
- function googleFamily(fontKey) {
178
- return THEME_FONT_GOOGLE_FAMILIES[fontKey] ?? encodeURIComponent(fontKey).replaceAll("%20", "+");
179
- }
180
- function themeFontFamily(fontKey) {
181
- return googleFamily(fontKey).replaceAll("+", " ");
182
- }
183
- function googleFontsUrl(theme) {
184
- if (!theme) return null;
185
- const families = /* @__PURE__ */ new Set();
186
- if (theme.typography?.fontSans) {
187
- families.add(googleFamily(theme.typography.fontSans));
188
- }
189
- if (theme.typography?.fontHeading) {
190
- families.add(googleFamily(theme.typography.fontHeading));
191
- }
192
- if (families.size === 0) return null;
193
- const params = [...families].map((f) => `family=${f}:wght@400;500;600;700`).join("&");
194
- return `https://fonts.googleapis.com/css2?${params}&display=swap`;
195
- }
package/dist/theme.mjs DELETED
@@ -1,174 +0,0 @@
1
- // src/theme/index.ts
2
- var BOARD_COLOR_KEYS = [
3
- "brandColor",
4
- "buttonPrimary",
5
- "buttonPrimaryText",
6
- "buttonDanger",
7
- "background",
8
- "surfaceBackground",
9
- "mutedBackground",
10
- "highlightBackground",
11
- "text",
12
- "textSubtle",
13
- "textMuted",
14
- "textDisabled",
15
- "textError",
16
- "border",
17
- "contrastBackground",
18
- "contrastText"
19
- ];
20
- var CSS_INJECTION_CHARS = /[<>{};]|[\u0000-\u001f\u007f-\u009f\u2028\u2029]/;
21
- function isSafeThemeColorValue(value) {
22
- return !CSS_INJECTION_CHARS.test(value);
23
- }
24
- function color(colors, key) {
25
- const value = colors[key];
26
- if (typeof value !== "string" || !value) return void 0;
27
- return isSafeThemeColorValue(value) ? value : void 0;
28
- }
29
- var EMAIL_SAFE_SNAPSHOT_FROM_CSS = {
30
- buttonPrimary: ["primary"],
31
- buttonPrimaryText: ["primary-foreground"],
32
- background: ["background"],
33
- // Muted wins; secondary fills only when --muted is absent.
34
- mutedBackground: ["muted", "secondary"],
35
- border: ["border"],
36
- text: ["foreground"],
37
- textMuted: ["muted-foreground"],
38
- brandColor: ["ring"]
39
- };
40
- var BOARD_COLOR_TO_CSS_TOKENS = {
41
- background: ["background"],
42
- text: [
43
- "foreground",
44
- "card-foreground",
45
- "popover-foreground",
46
- "secondary-foreground",
47
- "accent-foreground"
48
- ],
49
- surfaceBackground: ["card", "popover"],
50
- mutedBackground: ["secondary", "muted"],
51
- highlightBackground: ["accent"],
52
- buttonPrimary: ["primary"],
53
- buttonPrimaryText: ["primary-foreground"],
54
- buttonDanger: ["destructive"],
55
- textMuted: ["muted-foreground"],
56
- border: ["border", "input"],
57
- brandColor: ["ring"],
58
- contrastBackground: ["contrast-background"],
59
- contrastText: ["contrast-foreground"],
60
- textSubtle: ["foreground-subtle"],
61
- textDisabled: ["foreground-disabled"],
62
- textError: ["foreground-error"]
63
- };
64
- var OPTIONAL_THEME_DECLARATIONS = [
65
- "foreground-subtle",
66
- "foreground-disabled"
67
- ];
68
- function isOptionalThemeDeclaration(token) {
69
- const name = token.startsWith("--") ? token.slice(2) : token;
70
- return OPTIONAL_THEME_DECLARATIONS.includes(name);
71
- }
72
- function tokenLines(colors) {
73
- const lines = [];
74
- const set = (token, value) => {
75
- if (value) lines.push(` --${token}: ${value};`);
76
- };
77
- for (const boardKey of BOARD_COLOR_KEYS) {
78
- const value = color(colors, boardKey);
79
- if (!value) continue;
80
- for (const cssToken of BOARD_COLOR_TO_CSS_TOKENS[boardKey]) {
81
- set(cssToken, value);
82
- }
83
- }
84
- if (!color(colors, "buttonDanger") && color(colors, "textError")) {
85
- set("destructive", color(colors, "textError"));
86
- }
87
- if (!color(colors, "brandColor") && color(colors, "buttonPrimary")) {
88
- set("ring", color(colors, "buttonPrimary"));
89
- }
90
- return lines;
91
- }
92
- function boardThemeToCss(theme) {
93
- if (!theme) return "";
94
- const light = tokenLines(theme.colors.light ?? {});
95
- const dark = tokenLines(theme.colors.dark ?? {});
96
- const fontSans = theme.typography?.fontSans;
97
- const genericStack = "ui-sans-serif, system-ui, sans-serif";
98
- const sansStack = fontSans ? `'${themeFontFamily(fontSans)}', ${genericStack}` : genericStack;
99
- if (fontSans) {
100
- light.push(` --font-sans: ${sansStack};`);
101
- }
102
- const fontHeading = theme.typography?.fontHeading;
103
- if (fontHeading) {
104
- light.push(
105
- ` --font-heading: '${themeFontFamily(fontHeading)}', ${sansStack};`
106
- );
107
- }
108
- const blocks = [];
109
- if (light.length > 0) blocks.push(`:root {
110
- ${light.join("\n")}
111
- }`);
112
- if (dark.length > 0) blocks.push(`.dark {
113
- ${dark.join("\n")}
114
- }`);
115
- return blocks.join("\n\n");
116
- }
117
- function themeMode(theme) {
118
- if (!theme) return "system";
119
- return theme.mode === "light" || theme.mode === "dark" ? theme.mode : "system";
120
- }
121
- var THEME_FONT_GOOGLE_FAMILIES = {
122
- "be-vietnam-pro": "Be+Vietnam+Pro",
123
- inter: "Inter",
124
- "plus-jakarta-sans": "Plus+Jakarta+Sans",
125
- "dm-sans": "DM+Sans",
126
- "public-sans": "Public+Sans",
127
- figtree: "Figtree",
128
- "work-sans": "Work+Sans",
129
- "open-sans": "Open+Sans",
130
- poppins: "Poppins",
131
- hind: "Hind",
132
- lexend: "Lexend",
133
- "fira-sans": "Fira+Sans",
134
- manrope: "Manrope",
135
- "source-sans-pro": "Source+Sans+3",
136
- outfit: "Outfit",
137
- "space-grotesk": "Space+Grotesk",
138
- geist: "Geist",
139
- "source-serif-4": "Source+Serif+4",
140
- lora: "Lora",
141
- "crimson-pro": "Crimson+Pro"
142
- };
143
- function googleFamily(fontKey) {
144
- return THEME_FONT_GOOGLE_FAMILIES[fontKey] ?? encodeURIComponent(fontKey).replaceAll("%20", "+");
145
- }
146
- function themeFontFamily(fontKey) {
147
- return googleFamily(fontKey).replaceAll("+", " ");
148
- }
149
- function googleFontsUrl(theme) {
150
- if (!theme) return null;
151
- const families = /* @__PURE__ */ new Set();
152
- if (theme.typography?.fontSans) {
153
- families.add(googleFamily(theme.typography.fontSans));
154
- }
155
- if (theme.typography?.fontHeading) {
156
- families.add(googleFamily(theme.typography.fontHeading));
157
- }
158
- if (families.size === 0) return null;
159
- const params = [...families].map((f) => `family=${f}:wght@400;500;600;700`).join("&");
160
- return `https://fonts.googleapis.com/css2?${params}&display=swap`;
161
- }
162
- export {
163
- BOARD_COLOR_KEYS,
164
- BOARD_COLOR_TO_CSS_TOKENS,
165
- EMAIL_SAFE_SNAPSHOT_FROM_CSS,
166
- OPTIONAL_THEME_DECLARATIONS,
167
- THEME_FONT_GOOGLE_FAMILIES,
168
- boardThemeToCss,
169
- googleFontsUrl,
170
- isOptionalThemeDeclaration,
171
- isSafeThemeColorValue,
172
- themeFontFamily,
173
- themeMode
174
- };
@@ -1,89 +0,0 @@
1
- ---
2
- name: cavuno-board-client
3
- description: Create and configure the @cavuno/board client — the pk_ board identifier, optional API-origin override, global headers, hooks, per-call FetchOptions, and the rule that keeps one shared instance safe under SSR.
4
- ---
5
-
6
- # The Board API client
7
-
8
- `createBoardClient` returns a typed client whose namespaces (`jobs`, `companies`, `blog`, `auth`, `me`, …) all route through one request pipeline: board-identifier base path, default headers, bearer token from storage, and your hooks.
9
-
10
- ## When to use
11
-
12
- - Creating the client instance your whole app shares.
13
- - Adding global headers, logging, request/response hooks, or framework caching.
14
- - Calling an endpoint the SDK doesn't expose yet (`client.fetch`).
15
-
16
- ## When not to use
17
-
18
- - Per-surface usage (listing jobs, auth) — see the surface skills; they assume the client exists.
19
-
20
- ## Create the client
21
-
22
- ```ts
23
- import { createBoardClient } from '@cavuno/board';
24
-
25
- const board = createBoardClient({
26
- board: 'pk_a8f3...', // pk_ key (preferred) | boards_ id | slug
27
- });
28
- ```
29
-
30
- Use the `pk_…` publishable key for `board`, not the slug: the slug is operator-mutable and renames break deployed frontends. The `pk_…` key is immutable and client-safe.
31
-
32
- ## Configuration
33
-
34
- ```ts no-check
35
- const board = createBoardClient({
36
- board: process.env.PUBLIC_CAVUNO_BOARD!,
37
- globalHeaders: { 'Accept-Language': 'en' },
38
- onRequest: async (req) => req, // mutate/replace the request (locale, URL rewrite)
39
- onResponse: async (res, req) => {}, // side effects only (logging, analytics)
40
- logger: console,
41
- auth: { storage: 'memory' }, // see cavuno-board-auth
42
- });
43
- ```
44
-
45
- The `onRequest`/`onResponse` hooks are first-class — do not monkey-patch the client. `onResponse` receives a **clone** of the response, so reading its body never disturbs the SDK's own parsing.
46
-
47
- ## Per-call FetchOptions ride straight to fetch
48
-
49
- Every method takes a trailing `options?` of type `FetchOptions` (`Omit<RequestInit,'body'>` plus `query`). Anything besides `body`/`query` passes through to `fetch` untouched, so framework caching works with zero SDK knowledge:
50
-
51
- ```ts snippet
52
- // Next.js ISR
53
- await board.jobs.list({ limit: 20 }, { next: { revalidate: 60, tags: ['jobs'] } });
54
- // Cloudflare Workers
55
- await board.jobs.list({ limit: 20 }, { cf: { cacheTtl: 60 } } as never);
56
- // Standard fetch + abort
57
- const ac = new AbortController();
58
- await board.jobs.list({ limit: 20 }, { cache: 'force-cache', signal: ac.signal });
59
- ```
60
-
61
- ## One shared instance is SSR-safe — if state stays per-call
62
-
63
- A single Workers isolate serves many users at once. A module-scoped client is safe **only** while per-user state is passed per call (`options.headers`), not stored on the instance. For browser/SPA usage, the instance may hold the session (`auth.storage`); for SSR, keep the session in an httpOnly cookie and pass `{ headers: { authorization } }` per call (see `cavuno-board-auth`).
64
-
65
- ```ts
66
- import { createBoardClient } from '@cavuno/board';
67
-
68
- // Safe: shared, stateless. Per-user token rides per call.
69
- export const board = createBoardClient({
70
- board: process.env.PUBLIC_CAVUNO_BOARD!,
71
- });
72
- ```
73
-
74
- ## Escape hatch: client.fetch
75
-
76
- `board.client.fetch<T>(path, init)` is public and first-class — it runs the full pipeline (base path, headers, token, hooks), not a raw `fetch`. Use it to call an endpoint before the SDK ships a typed method for it:
77
-
78
- ```ts snippet
79
- const data = await board.client.fetch<{ object: 'list'; data: unknown[] }>(
80
- '/some-new-endpoint',
81
- { query: { limit: 5 } });
82
- ```
83
-
84
- ## Checklist
85
-
86
- - [ ] One client created from env values, reused everywhere.
87
- - [ ] `board` is the `pk_…` key, not the slug.
88
- - [ ] No per-user state on a shared SSR instance.
89
- - [ ] Caching done via per-call `FetchOptions`, not a wrapper.