cauthy-auth 0.5.2 → 0.9.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 (72) hide show
  1. package/README.md +68 -3
  2. package/dist/{cauthy-BCTCqubt.d.ts → cauthy-Bn3iv50y.d.ts} +1 -1
  3. package/dist/{cauthy-hswMoS7B.d.mts → cauthy-C29HCvag.d.mts} +1 -1
  4. package/dist/cli.js +490 -25
  5. package/dist/client.d.mts +273 -0
  6. package/dist/client.d.ts +273 -0
  7. package/dist/client.js +342 -0
  8. package/dist/client.js.map +1 -0
  9. package/dist/client.mjs +308 -0
  10. package/dist/client.mjs.map +1 -0
  11. package/dist/express.d.mts +24 -0
  12. package/dist/express.d.ts +24 -0
  13. package/dist/express.js +102 -0
  14. package/dist/express.js.map +1 -0
  15. package/dist/express.mjs +76 -0
  16. package/dist/express.mjs.map +1 -0
  17. package/dist/fastify.d.mts +2 -2
  18. package/dist/fastify.d.ts +2 -2
  19. package/dist/fastify.js +3 -1
  20. package/dist/fastify.js.map +1 -1
  21. package/dist/fastify.mjs +3 -1
  22. package/dist/fastify.mjs.map +1 -1
  23. package/dist/hono.d.mts +23 -0
  24. package/dist/hono.d.ts +23 -0
  25. package/dist/hono.js +50 -0
  26. package/dist/hono.js.map +1 -0
  27. package/dist/hono.mjs +24 -0
  28. package/dist/hono.mjs.map +1 -0
  29. package/dist/index.d.mts +44 -6
  30. package/dist/index.d.ts +44 -6
  31. package/dist/index.js +713 -37
  32. package/dist/index.js.map +1 -1
  33. package/dist/index.mjs +701 -37
  34. package/dist/index.mjs.map +1 -1
  35. package/dist/next.d.mts +24 -0
  36. package/dist/next.d.ts +24 -0
  37. package/dist/next.js +77 -0
  38. package/dist/next.js.map +1 -0
  39. package/dist/next.mjs +52 -0
  40. package/dist/next.mjs.map +1 -0
  41. package/dist/react.d.mts +592 -7
  42. package/dist/react.d.ts +592 -7
  43. package/dist/react.js +1025 -14
  44. package/dist/react.js.map +1 -1
  45. package/dist/react.mjs +1013 -10
  46. package/dist/react.mjs.map +1 -1
  47. package/dist/styles.css +271 -0
  48. package/dist/svelte.d.mts +26 -0
  49. package/dist/svelte.d.ts +26 -0
  50. package/dist/svelte.js +413 -0
  51. package/dist/svelte.js.map +1 -0
  52. package/dist/svelte.mjs +384 -0
  53. package/dist/svelte.mjs.map +1 -0
  54. package/dist/{system-DvcQjORr.d.ts → system-DhRr_WxR.d.ts} +139 -1
  55. package/dist/{system-CR8p4BjB.d.mts → system-ZD_Wa8Bm.d.mts} +139 -1
  56. package/dist/theme.d.mts +56 -1
  57. package/dist/theme.d.ts +56 -1
  58. package/dist/theme.js +181 -5
  59. package/dist/theme.js.map +1 -1
  60. package/dist/theme.mjs +176 -4
  61. package/dist/theme.mjs.map +1 -1
  62. package/dist/validate-B_iC-4y8.d.mts +19 -0
  63. package/dist/validate-B_iC-4y8.d.ts +19 -0
  64. package/dist/vue.d.mts +28 -0
  65. package/dist/vue.d.ts +28 -0
  66. package/dist/vue.js +431 -0
  67. package/dist/vue.js.map +1 -0
  68. package/dist/vue.mjs +402 -0
  69. package/dist/vue.mjs.map +1 -0
  70. package/package.json +68 -7
  71. package/dist/permissions-B-a3eN0h.d.mts +0 -52
  72. package/dist/permissions-B-a3eN0h.d.ts +0 -52
package/dist/theme.js CHANGED
@@ -20,11 +20,15 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/theme.ts
21
21
  var theme_exports = {};
22
22
  __export(theme_exports, {
23
+ cssVariables: () => cssVariables,
23
24
  fromWire: () => fromWire,
24
25
  pushTheme: () => pushTheme,
26
+ themeFromCss: () => themeFromCss,
25
27
  themeFromStackmyth: () => themeFromStackmyth,
28
+ themeVariables: () => themeVariables,
26
29
  toColor: () => toColor,
27
- toWire: () => toWire
30
+ toWire: () => toWire,
31
+ tokensFromStackmyth: () => tokensFromStackmyth
28
32
  });
29
33
  module.exports = __toCommonJS(theme_exports);
30
34
  var MAX_RADIUS = 40;
@@ -78,10 +82,92 @@ function applies(d, o) {
78
82
  if (o.mode === "dark" && LIGHT.test(scope)) return false;
79
83
  const named = NAMED.exec(scope);
80
84
  if (named && !/^(dark|light)$/i.test(named[1]) && named[1] !== o.theme) return false;
81
- if (!scope.startsWith("@") && !/:root|^html\b|^body\b|\[data-|^\*$|:host/.test(scope)) return false;
85
+ if (!scope.startsWith("@") && !/:root|^html\b|^body\b|\[data-|^\*$|:host|^\.(dark|light)\b/.test(scope)) return false;
82
86
  }
83
87
  return true;
84
88
  }
89
+ var TOKEN_CHARS = /^[A-Za-z0-9 #%.,()/'"_+*-]+$/;
90
+ var TOKEN_FUNCS = /* @__PURE__ */ new Set([
91
+ "var",
92
+ "calc",
93
+ "min",
94
+ "max",
95
+ "clamp",
96
+ "rgb",
97
+ "rgba",
98
+ "hsl",
99
+ "hsla",
100
+ "hwb",
101
+ "lab",
102
+ "lch",
103
+ "oklab",
104
+ "oklch",
105
+ "color",
106
+ "color-mix",
107
+ "light-dark",
108
+ "cubic-bezier",
109
+ "steps",
110
+ "linear",
111
+ "linear-gradient",
112
+ "radial-gradient",
113
+ "conic-gradient",
114
+ "repeating-linear-gradient",
115
+ "repeating-radial-gradient",
116
+ "drop-shadow",
117
+ "blur",
118
+ "translate",
119
+ "translatex",
120
+ "translatey",
121
+ "scale",
122
+ "rotate"
123
+ ]);
124
+ function tokenValue(value) {
125
+ const v = value.replace(/\s+/g, " ").trim();
126
+ if (v === "" || v.length > 400 || !TOKEN_CHARS.test(v)) return null;
127
+ for (const m of v.matchAll(/([A-Za-z-]+)\(/g)) if (!TOKEN_FUNCS.has(m[1].toLowerCase())) return null;
128
+ if (/^\(|[ ,(]\(/.test(v)) return null;
129
+ return v;
130
+ }
131
+ var THEME_SCOPE = /^(?:\s*(?::root|html|body|\[data-(?:theme|mode|color-scheme)(?:=["']?[\w-]+["']?)?\])\s*,?)+$/i;
132
+ function tokensFromStackmyth(css, options = {}) {
133
+ const mode = options.mode ?? "light";
134
+ const all = (Array.isArray(css) ? css : [css]).flatMap(declarations);
135
+ const named = options.theme ?? all.map((d) => d.within.map((w) => NAMED.exec(w)?.[1]).find((n) => n && !/^(dark|light)$/i.test(n))).find(Boolean);
136
+ const vars = /* @__PURE__ */ new Map();
137
+ for (const d of all) {
138
+ if (!applies(d, { mode, theme: named })) continue;
139
+ if (!d.within.every((w) => w.startsWith("@") || THEME_SCOPE.test(w))) continue;
140
+ vars.set(d.name, d.value);
141
+ }
142
+ const inline = (value, depth = 0) => {
143
+ if (depth > 16) return null;
144
+ let failed = false;
145
+ let out2 = value;
146
+ for (let guard = 0; guard < 64; guard++) {
147
+ const next = out2.replace(/var\(\s*(--(?!sm-)[\w-]+)\s*(?:,\s*([^()]*(?:\([^()]*\)[^()]*)*))?\)/, (_m, name, fallback) => {
148
+ const known = vars.get(name);
149
+ const got = known !== void 0 ? inline(known, depth + 1) : fallback !== void 0 ? inline(fallback.trim(), depth + 1) : null;
150
+ if (got === null) failed = true;
151
+ return got ?? "";
152
+ });
153
+ if (failed) return null;
154
+ if (next === out2) break;
155
+ out2 = next;
156
+ }
157
+ return out2;
158
+ };
159
+ const tokens = {};
160
+ const warnings = [];
161
+ for (const [name, raw] of vars) {
162
+ if (!/^--sm-[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name)) continue;
163
+ const written = inline(raw);
164
+ const value = written === null ? null : tokenValue(written);
165
+ if (value === null) warnings.push(`${name}: "${raw.length > 60 ? raw.slice(0, 57) + "\u2026" : raw}" is not a value Cauthy takes`);
166
+ else tokens[name] = value;
167
+ }
168
+ if (Object.keys(tokens).length === 0) warnings.push("no --sm-* token was found in these stylesheets");
169
+ return { tokens, warnings };
170
+ }
85
171
  function resolve(value, vars, seen = 0) {
86
172
  if (seen > 16) return null;
87
173
  let unresolved = false;
@@ -203,6 +289,90 @@ function themeFromStackmyth(css, options = {}) {
203
289
  else if (![...vars.keys()].some((k) => k.startsWith("--sm-"))) warnings.push("no --sm-* token was found in these stylesheets");
204
290
  return { theme, warnings };
205
291
  }
292
+ var RADII = ["radius", "buttonRadius"];
293
+ var SHADCN = [
294
+ ["background", ["--background", "--color-background"]],
295
+ ["surface", ["--card", "--color-card", "--popover", "--color-popover"]],
296
+ ["text", ["--foreground", "--color-foreground"]],
297
+ ["textMuted", ["--muted-foreground", "--color-muted-foreground"]],
298
+ ["border", ["--border", "--color-border"]],
299
+ ["inputBorder", ["--input", "--color-input"]],
300
+ ["primary", ["--primary", "--color-primary"]],
301
+ ["primaryText", ["--primary-foreground", "--color-primary-foreground"]],
302
+ ["accent", ["--ring", "--color-ring", "--primary", "--color-primary"]],
303
+ ["error", ["--destructive", "--color-destructive"]],
304
+ ["radius", ["--radius"]],
305
+ ["buttonRadius", ["--radius"]],
306
+ ["fontFamily", ["--font-sans", "--font-family"]]
307
+ ];
308
+ function shadcnColor(value) {
309
+ return /^[\d.]+(?:deg)?\s+[\d.]+%\s+[\d.]+%(?:\s*\/\s*[\d.]+%?)?$/.test(value.trim()) ? toColor(`hsl(${value.trim()})`) : toColor(value);
310
+ }
311
+ function pageVariables(css, mode, theme) {
312
+ const vars = /* @__PURE__ */ new Map();
313
+ for (const sheet of Array.isArray(css) ? css : [css]) {
314
+ for (const d of declarations(sheet)) if (applies(d, { mode, theme })) vars.set(d.name, d.value);
315
+ }
316
+ return vars;
317
+ }
318
+ function cssVariables(css, options = {}) {
319
+ return [...pageVariables(css, options.mode ?? "light", options.theme).keys()].sort();
320
+ }
321
+ function themeFromCss(css, options = {}) {
322
+ const mode = options.mode ?? "light";
323
+ const vars = pageVariables(css, mode, options.theme);
324
+ const names = [...vars.keys()];
325
+ let format;
326
+ if (options.format && options.format !== "auto") format = options.format;
327
+ else if (options.map && Object.keys(options.map).length > 0) format = "custom";
328
+ else if (names.some((n) => n.startsWith("--sm-"))) format = "stackmyth";
329
+ else if (names.some((n) => /^--(color-)?(primary|background|foreground)$/.test(n))) format = "shadcn";
330
+ else format = "custom";
331
+ if (format === "stackmyth") return { ...themeFromStackmyth(css, { mode, theme: options.theme }), format };
332
+ const spec = format === "shadcn" ? SHADCN : Object.entries(options.map ?? {}).filter(([, v]) => v).map(([k, v]) => [k, [String(v).startsWith("--") ? String(v) : `--${v}`]]);
333
+ const theme = {};
334
+ const warnings = [];
335
+ for (const [field, tokens] of spec) {
336
+ for (const token of tokens) {
337
+ const raw = vars.get(token);
338
+ if (raw === void 0) continue;
339
+ const value = resolve(raw, vars);
340
+ const got = value === null ? null : RADII.includes(field) ? toRadius(value) : field === "fontFamily" ? toFont(value) : format === "shadcn" ? shadcnColor(value) : toColor(value);
341
+ if (got !== null) {
342
+ theme[field] = got;
343
+ break;
344
+ }
345
+ warnings.push(`${token}: "${raw.length > 60 ? raw.slice(0, 57) + "\u2026" : raw}" is not ${RADII.includes(field) ? "a length" : field === "fontFamily" ? "a font list" : "a colour"} Cauthy can take`);
346
+ }
347
+ }
348
+ if (vars.size === 0) warnings.push("no custom properties were found in these stylesheets");
349
+ else if (format === "custom" && spec.length === 0) warnings.push("these stylesheets use names of their own: say which variable each value is read from");
350
+ return { theme, warnings, format };
351
+ }
352
+ function themeVariables(theme) {
353
+ const vars = {};
354
+ const put = (name, v) => {
355
+ if (v === void 0 || v === "") return;
356
+ vars[`--cauthy-${name}`] = typeof v === "number" ? `${v}px` : v;
357
+ };
358
+ put("surface", theme.surface);
359
+ put("text", theme.text);
360
+ put("text-muted", theme.textMuted);
361
+ put("border", theme.border);
362
+ put("input-background", theme.inputBackground);
363
+ put("input-border", theme.inputBorder);
364
+ put("primary", theme.primary);
365
+ put("primary-text", theme.primaryText);
366
+ put("accent", theme.accent);
367
+ put("error", theme.error);
368
+ put("error-background", theme.errorBackground);
369
+ put("success", theme.success);
370
+ put("success-background", theme.successBackground);
371
+ put("radius", theme.radius);
372
+ put("button-radius", theme.buttonRadius);
373
+ put("font", theme.fontFamily);
374
+ return vars;
375
+ }
206
376
  function toWire(theme) {
207
377
  const names = {
208
378
  background: "background",
@@ -228,7 +398,8 @@ function toWire(theme) {
228
398
  panelIconUrl: "panel_icon_url",
229
399
  texts: "texts",
230
400
  inputBackground: "input_background",
231
- inputBorder: "input_border"
401
+ inputBorder: "input_border",
402
+ tokens: "tokens"
232
403
  };
233
404
  const wire = {};
234
405
  for (const [k, v] of Object.entries(theme)) {
@@ -262,7 +433,8 @@ function fromWire(wire) {
262
433
  panel_icon_url: "panelIconUrl",
263
434
  texts: "texts",
264
435
  input_background: "inputBackground",
265
- input_border: "inputBorder"
436
+ input_border: "inputBorder",
437
+ tokens: "tokens"
266
438
  };
267
439
  const theme = {};
268
440
  for (const [k, v] of Object.entries(wire ?? {})) if (names[k] && v !== void 0 && v !== null && v !== "") theme[names[k]] = v;
@@ -289,10 +461,14 @@ async function pushTheme(o) {
289
461
  }
290
462
  // Annotate the CommonJS export names for ESM import in node:
291
463
  0 && (module.exports = {
464
+ cssVariables,
292
465
  fromWire,
293
466
  pushTheme,
467
+ themeFromCss,
294
468
  themeFromStackmyth,
469
+ themeVariables,
295
470
  toColor,
296
- toWire
471
+ toWire,
472
+ tokensFromStackmyth
297
473
  });
298
474
  //# sourceMappingURL=theme.js.map
package/dist/theme.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/theme.ts"],"sourcesContent":["// An application's look, for Cauthy's hosted pages.\n//\n// Cauthy's pages — sign-in, sign-up, the account page — can take the colours,\n// corners, typeface and logo of the application that sends its people there,\n// so signing in reads as a page of the application. A theme is a short list of\n// values, which Cauthy checks one by one; it is never CSS.\n//\n// An application built with Stackmyth already has those values, as its\n// `--sm-*` tokens. `themeFromStackmyth` reads them out of its stylesheets.\n\n/** How an application looks on Cauthy's hosted pages. Every value is optional. */\n/** One font file the application serves: a family in one weight (or a range) and one style. */\nexport interface ThemeFont {\n family: string\n /** An https address of a `.woff2` file. It must answer with `Access-Control-Allow-Origin` for Cauthy's origin. */\n url: string\n /** `400`, or `\"100 900\"` for a variable font. Default: 400. */\n weight?: number | string\n style?: 'normal' | 'italic'\n}\n\n/** What an application says on its sign-in page, in one language. */\nexport interface ThemeTexts {\n /** Replaces \"Sign in\". */\n heading?: string\n /** Replaces \"to continue to …\". */\n subheading?: string\n /** The messages of the split layout's panel, shown one at a time (up to 5). */\n panel?: string[]\n /** Links at the foot of the split layout: privacy, terms (up to 4). */\n links?: { label: string; url: string }[]\n}\n\nexport interface CauthyTheme {\n /** The page. Colours are `#rrggbb` or `rgba(r, g, b, a)`. */\n background?: string\n /** The card, and inputs. */\n surface?: string\n /** Headings and what is typed. */\n text?: string\n /** Paragraphs, labels, small print. */\n textMuted?: string\n border?: string\n /** The fields and the secondary buttons, when they are not the card's colours. */\n inputBackground?: string\n inputBorder?: string\n /** The main button, and what is written on it. */\n primary?: string\n primaryText?: string\n /** Links, and the focus ring. */\n accent?: string\n error?: string\n errorBackground?: string\n success?: string\n successBackground?: string\n /** Corners of cards and inputs, in pixels (0–40). */\n radius?: number\n /** Corners of buttons, in pixels (0–40). */\n buttonRadius?: number\n /** A CSS font-family list: the application's typeface first, a system font after it. */\n fontFamily?: string\n /** The files of the typefaces `fontFamily` names, for people who do not have them installed. */\n fonts?: ThemeFont[]\n /** An image shown where \"Cauthy\" is, over https. */\n logoUrl?: string\n /**\n * How the sign-in pages are laid out: `card` (the default, one card in the\n * middle) or `split` (the form on one side, a panel that presents the\n * application on the other).\n */\n layout?: 'card' | 'split'\n /** The colours the split layout's backdrop blends, one to four. */\n panelColors?: string[]\n /** The colour of what is written on the panel. */\n panelText?: string\n /** An https image shown over the panel's messages. */\n panelIconUrl?: string\n /** The application's own words on the sign-in page, by language (`en`, `es`). */\n texts?: Record<string, ThemeTexts>\n}\n\nexport interface StackmythThemeOptions {\n /**\n * The named theme to read, when the stylesheet keeps several under\n * `[data-theme=…]` (`\"vennet\"`). Without it only what applies to every\n * page (`:root`) is read.\n */\n theme?: string\n /** Which of the application's two modes to take. Default: `light`. */\n mode?: 'light' | 'dark'\n logoUrl?: string\n}\n\nexport interface ImportedTheme {\n theme: CauthyTheme\n /** Tokens that were there and could not be carried over, and why. */\n warnings: string[]\n}\n\nconst MAX_RADIUS = 40\n\n// ── Reading custom properties out of CSS ───────────────────────────────────\n\ninterface Declaration {\n name: string\n value: string\n /** The selectors and at-rules it sits inside, outermost first. */\n within: string[]\n}\n\nfunction declarations(css: string): Declaration[] {\n const text = css.replace(/\\/\\*[\\s\\S]*?\\*\\//g, '')\n const out: Declaration[] = []\n const within: string[] = []\n let chunk = ''\n let quote = ''\n let depth = 0 // parentheses, so a `;` inside url(…) does not end anything\n const flush = () => {\n const m = /^\\s*(--[\\w-]+)\\s*:\\s*([\\s\\S]+?)\\s*$/.exec(chunk)\n if (m) out.push({ name: m[1], value: m[2].replace(/\\s+/g, ' ').replace(/\\s*!important$/, ''), within: [...within] })\n chunk = ''\n }\n for (const ch of text) {\n if (quote) {\n chunk += ch\n if (ch === quote) quote = ''\n continue\n }\n if (ch === '\"' || ch === \"'\") {\n quote = ch\n chunk += ch\n } else if (ch === '(') {\n depth++\n chunk += ch\n } else if (ch === ')') {\n depth = Math.max(0, depth - 1)\n chunk += ch\n } else if (ch === '{' && depth === 0) {\n within.push(chunk.trim().replace(/\\s+/g, ' '))\n chunk = ''\n } else if (ch === '}' && depth === 0) {\n flush()\n within.pop()\n } else if (ch === ';' && depth === 0) {\n flush()\n } else {\n chunk += ch\n }\n }\n return out\n}\n\nconst DARK = /prefers-color-scheme:\\s*dark|data-(mode|theme|color-scheme)=[\"']?dark|\\.dark\\b/i\nconst LIGHT = /prefers-color-scheme:\\s*light|data-(mode|theme|color-scheme)=[\"']?light|\\.light\\b/i\nconst NAMED = /data-theme=[\"']?([\\w-]+)/i\n\n/** Whether a declaration applies to the page being themed. */\nfunction applies(d: Declaration, o: Required<Pick<StackmythThemeOptions, 'mode'>> & { theme?: string }): boolean {\n for (const scope of d.within) {\n if (o.mode === 'light' && DARK.test(scope)) return false\n if (o.mode === 'dark' && LIGHT.test(scope)) return false\n const named = NAMED.exec(scope)\n if (named && !/^(dark|light)$/i.test(named[1]) && named[1] !== o.theme) return false\n // Component-level rules (`.btn { --sm-… }`) are not the page's theme.\n if (!scope.startsWith('@') && !/:root|^html\\b|^body\\b|\\[data-|^\\*$|:host/.test(scope)) return false\n }\n return true\n}\n\n// ── Values ─────────────────────────────────────────────────────────────────\n\nfunction resolve(value: string, vars: Map<string, string>, seen = 0): string | null {\n if (seen > 16) return null\n let unresolved = false\n // Innermost var() first: fallbacks may hold another var().\n let out = value\n for (let guard = 0; guard < 32 && /var\\(/.test(out); guard++) {\n out = out.replace(/var\\(\\s*(--[\\w-]+)\\s*(?:,\\s*([^()]*(?:\\([^()]*\\)[^()]*)*))?\\)/, (_m, name: string, fallback?: string) => {\n const known = vars.get(name)\n const next = known !== undefined ? resolve(known, vars, seen + 1) : fallback !== undefined ? resolve(fallback.trim(), vars, seen + 1) : null\n if (next === null) unresolved = true\n return next ?? ''\n })\n if (unresolved) return null\n }\n return /var\\(/.test(out) ? null : out.trim()\n}\n\nconst clamp01 = (n: number) => Math.min(1, Math.max(0, n))\nconst hex2 = (n: number) => Math.round(clamp01(n) * 255).toString(16).padStart(2, '0')\nconst gamma = (c: number) => (c <= 0.0031308 ? 12.92 * c : 1.055 * Math.pow(c, 1 / 2.4) - 0.055)\n\nfunction out(r: number, g: number, b: number, a = 1): string {\n if (a >= 1) return `#${hex2(r)}${hex2(g)}${hex2(b)}`\n return `rgba(${Math.round(clamp01(r) * 255)}, ${Math.round(clamp01(g) * 255)}, ${Math.round(clamp01(b) * 255)}, ${Math.round(clamp01(a) * 1000) / 1000})`\n}\n\nconst alpha = (s: string | undefined) => (s === undefined ? 1 : s.endsWith('%') ? parseFloat(s) / 100 : parseFloat(s))\n\nconst KEYWORDS: Record<string, string> = { white: '#ffffff', black: '#000000' }\n\n/** A colour as Cauthy takes it — `#rrggbb` or `rgba(…)` — or null when it is none it can read. */\nexport function toColor(value: string): string | null {\n const v = value.trim().toLowerCase()\n if (KEYWORDS[v]) return KEYWORDS[v]\n if (/^#([0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/.test(v)) return v\n let m = /^rgba?\\(\\s*([\\d.]+%?)[\\s,]+([\\d.]+%?)[\\s,]+([\\d.]+%?)(?:\\s*[,/]\\s*([\\d.]+%?))?\\s*\\)$/.exec(v)\n if (m) {\n const ch = (s: string) => (s.endsWith('%') ? parseFloat(s) / 100 : parseFloat(s) / 255)\n return out(ch(m[1]), ch(m[2]), ch(m[3]), alpha(m[4]))\n }\n m = /^hsla?\\(\\s*([\\d.]+)(?:deg)?[\\s,]+([\\d.]+)%[\\s,]+([\\d.]+)%(?:\\s*[,/]\\s*([\\d.]+%?))?\\s*\\)$/.exec(v)\n if (m) {\n const h = ((parseFloat(m[1]) % 360) + 360) % 360\n const s = parseFloat(m[2]) / 100\n const l = parseFloat(m[3]) / 100\n const f = (n: number) => {\n const k = (n + h / 30) % 12\n return l - s * Math.min(l, 1 - l) * Math.max(-1, Math.min(k - 3, 9 - k, 1))\n }\n return out(f(0), f(8), f(4), alpha(m[4]))\n }\n m = /^oklch\\(\\s*([\\d.]+%?)\\s+([\\d.]+%?)\\s+([\\d.]+)(?:deg)?(?:\\s*\\/\\s*([\\d.]+%?))?\\s*\\)$/.exec(v)\n if (m) {\n const L = m[1].endsWith('%') ? parseFloat(m[1]) / 100 : parseFloat(m[1])\n const C = m[2].endsWith('%') ? (parseFloat(m[2]) / 100) * 0.4 : parseFloat(m[2])\n const H = (parseFloat(m[3]) * Math.PI) / 180\n const a = C * Math.cos(H)\n const b = C * Math.sin(H)\n const l = Math.pow(L + 0.3963377774 * a + 0.2158037573 * b, 3)\n const mm = Math.pow(L - 0.1055613458 * a - 0.0638541728 * b, 3)\n const s = Math.pow(L - 0.0894841775 * a - 1.291485548 * b, 3)\n return out(\n gamma(4.0767416621 * l - 3.3077115913 * mm + 0.2309699292 * s),\n gamma(-1.2684380046 * l + 2.6097574011 * mm - 0.3413193965 * s),\n gamma(-0.0041960863 * l - 0.7034186147 * mm + 1.707614701 * s),\n alpha(m[4]),\n )\n }\n return null\n}\n\n/** A length in pixels, within what Cauthy takes, or null. */\nfunction toRadius(value: string): number | null {\n const m = /^([\\d.]+)(px|rem|em)?$/.exec(value.trim())\n if (!m) return null\n const px = parseFloat(m[1]) * (m[2] === 'rem' || m[2] === 'em' ? 16 : 1)\n return Number.isFinite(px) ? Math.min(MAX_RADIUS, Math.max(0, Math.round(px))) : null\n}\n\n/** A font list as Cauthy takes it, or null. */\nfunction toFont(value: string): string | null {\n const v = value.trim()\n return /^[A-Za-z0-9 ,'\"_-]{1,200}$/.test(v) && (v.match(/\"/g)?.length ?? 0) % 2 === 0 && (v.match(/'/g)?.length ?? 0) % 2 === 0 ? v : null\n}\n\n// ── Stackmyth ──────────────────────────────────────────────────────────────\n\n/** Which Stackmyth token each value of a theme is read from, first found wins. */\nconst COLORS: [keyof CauthyTheme, string[]][] = [\n ['background', ['--sm-bg-surface', '--sm-surface']],\n ['surface', ['--sm-bg-surface-raised', '--sm-surface-raised']],\n ['text', ['--sm-text-primary', '--sm-surface-text']],\n ['textMuted', ['--sm-text-secondary']],\n ['border', ['--sm-border-default', '--sm-surface-border']],\n ['primary', ['--sm-primary', '--sm-bg-interactive']],\n ['primaryText', ['--sm-primary-text', '--sm-text-inverse']],\n ['accent', ['--sm-text-brand', '--sm-border-focus', '--sm-accent']],\n ['error', ['--sm-error-soft-text', '--sm-error']],\n ['errorBackground', ['--sm-error-soft']],\n ['success', ['--sm-success-soft-text', '--sm-success']],\n ['successBackground', ['--sm-success-soft']],\n]\n\n/**\n * Reads an application's Stackmyth theme out of its stylesheets.\n *\n * Give it the CSS that declares the tokens, in the order the application\n * loads it — Stackmyth's own (`@stackmyth/core/core.vars.css`, or a theme\n * file) and then the application's brand file that re-points them. Tokens\n * that point at the application's own variables are followed. The\n * application's dark mode is left out unless asked for.\n */\nexport function themeFromStackmyth(css: string | string[], options: StackmythThemeOptions = {}): ImportedTheme {\n const o = { mode: options.mode ?? 'light', theme: options.theme }\n const vars = new Map<string, string>()\n for (const sheet of Array.isArray(css) ? css : [css]) {\n for (const d of declarations(sheet)) if (applies(d, o)) vars.set(d.name, d.value)\n }\n const theme: CauthyTheme = {}\n const warnings: string[] = []\n\n const read = <T>(tokens: string[], to: (v: string) => T | null, what: string): T | undefined => {\n for (const token of tokens) {\n const raw = vars.get(token)\n if (raw === undefined) continue\n const value = resolve(raw, vars)\n const got = value === null ? null : to(value)\n if (got !== null) return got\n warnings.push(`${token}: \"${raw}\" is not ${what} Cauthy can take`)\n }\n return undefined\n }\n\n for (const [field, tokens] of COLORS) {\n const c = read(tokens, toColor, 'a colour')\n if (c !== undefined) (theme as Record<string, unknown>)[field] = c\n }\n const radius = read(['--sm-radius-container', '--sm-radius-md'], toRadius, 'a length')\n if (radius !== undefined) theme.radius = radius\n const button = read(['--sm-radius-interactive', '--sm-radius-sm'], toRadius, 'a length')\n if (button !== undefined) theme.buttonRadius = button\n const font = read(['--sm-font-family'], toFont, 'a font list')\n if (font !== undefined) theme.fontFamily = font\n if (options.logoUrl) theme.logoUrl = options.logoUrl\n\n if (vars.size === 0) warnings.push('no custom properties were found: is this the stylesheet that declares the --sm-* tokens?')\n else if (![...vars.keys()].some((k) => k.startsWith('--sm-'))) warnings.push('no --sm-* token was found in these stylesheets')\n return { theme, warnings }\n}\n\n// ── Sending it to Cauthy ───────────────────────────────────────────────────\n\n/** A theme as Cauthy's API names its values. */\nexport function toWire(theme: CauthyTheme): Record<string, unknown> {\n const names: Record<keyof CauthyTheme, string> = {\n background: 'background', surface: 'surface', text: 'text', textMuted: 'text_muted', border: 'border',\n primary: 'primary', primaryText: 'primary_text', accent: 'accent', error: 'error', errorBackground: 'error_background',\n success: 'success', successBackground: 'success_background', radius: 'radius', buttonRadius: 'button_radius',\n fontFamily: 'font_family', logoUrl: 'logo_url', fonts: 'fonts', layout: 'layout', panelColors: 'panel_colors',\n panelText: 'panel_text', panelIconUrl: 'panel_icon_url', texts: 'texts', inputBackground: 'input_background', inputBorder: 'input_border',\n }\n const wire: Record<string, unknown> = {}\n for (const [k, v] of Object.entries(theme)) {\n if (v === undefined || v === '' || (Array.isArray(v) && v.length === 0)) continue\n wire[names[k as keyof CauthyTheme]] = v\n }\n return wire\n}\n\n/** A theme as Cauthy answers it, in this package's names. */\nexport function fromWire(wire: Record<string, unknown>): CauthyTheme {\n const names: Record<string, keyof CauthyTheme> = {\n background: 'background', surface: 'surface', text: 'text', text_muted: 'textMuted', border: 'border',\n primary: 'primary', primary_text: 'primaryText', accent: 'accent', error: 'error', error_background: 'errorBackground',\n success: 'success', success_background: 'successBackground', radius: 'radius', button_radius: 'buttonRadius',\n font_family: 'fontFamily', logo_url: 'logoUrl', fonts: 'fonts', layout: 'layout', panel_colors: 'panelColors',\n panel_text: 'panelText', panel_icon_url: 'panelIconUrl', texts: 'texts', input_background: 'inputBackground', input_border: 'inputBorder',\n }\n const theme: Record<string, unknown> = {}\n for (const [k, v] of Object.entries(wire ?? {})) if (names[k] && v !== undefined && v !== null && v !== '') theme[names[k]] = v\n return theme as CauthyTheme\n}\n\nexport interface PushThemeOptions {\n /** Cauthy's address. Default: `CAUTHY_ISSUER`. */\n issuer?: string\n /** The application's client id. Default: `CAUTHY_CLIENT_ID`. */\n clientId?: string\n /** The workspace's slug. Default: `CAUTHY_TENANT`. */\n tenant?: string\n /**\n * A personal access token with `oauth_clients:manage`. Default:\n * `CAUTHY_TOKEN`. It belongs in the deploy pipeline, never in the application.\n */\n token?: string\n theme: CauthyTheme\n fetch?: typeof fetch\n}\n\n/**\n * Gives the application its look on Cauthy's pages. It replaces the theme: a\n * value left out goes back to Cauthy's own. Run it when the brand changes —\n * from a script or the deploy — not on every request.\n */\nexport async function pushTheme(o: PushThemeOptions): Promise<void> {\n const env = (n: string) => (typeof process !== 'undefined' ? process.env?.[n] : undefined)\n const issuer = (o.issuer ?? env('CAUTHY_ISSUER') ?? '').replace(/\\/+$/, '')\n const clientId = o.clientId ?? env('CAUTHY_CLIENT_ID') ?? ''\n const tenant = o.tenant ?? env('CAUTHY_TENANT') ?? ''\n const token = o.token ?? env('CAUTHY_TOKEN') ?? ''\n for (const [name, v] of Object.entries({ CAUTHY_ISSUER: issuer, CAUTHY_CLIENT_ID: clientId, CAUTHY_TENANT: tenant, CAUTHY_TOKEN: token })) {\n if (v === '') throw new Error(`cauthy-auth: ${name} is required to push a theme`)\n }\n const res = await (o.fetch ?? fetch)(`${issuer}/v1/oauth/clients/${encodeURIComponent(clientId)}/theme`, {\n method: 'PUT',\n headers: { authorization: `Bearer ${token}`, 'x-tenant-key': tenant, 'content-type': 'application/json' },\n body: JSON.stringify(toWire(o.theme)),\n })\n if (!res.ok) {\n const body = (await res.json().catch(() => null)) as { errors?: { message?: string }[] } | null\n throw new Error(`cauthy-auth: Cauthy refused the theme (${res.status}): ${body?.errors?.[0]?.message ?? res.statusText}`)\n }\n}\n"],"mappings":"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","names":["out"]}
1
+ {"version":3,"sources":["../src/theme.ts"],"sourcesContent":["// An application's look, for Cauthy's hosted pages.\n//\n// Cauthy's pages — sign-in, sign-up, the account page — can take the colours,\n// corners, typeface and logo of the application that sends its people there,\n// so signing in reads as a page of the application. A theme is a short list of\n// values, which Cauthy checks one by one; it is never CSS.\n//\n// An application built with Stackmyth already has those values, as its\n// `--sm-*` tokens. `themeFromStackmyth` reads them out of its stylesheets.\n\n/** How an application looks on Cauthy's hosted pages. Every value is optional. */\n/** One font file the application serves: a family in one weight (or a range) and one style. */\nexport interface ThemeFont {\n family: string\n /** An https address of a `.woff2` file. It must answer with `Access-Control-Allow-Origin` for Cauthy's origin. */\n url: string\n /** `400`, or `\"100 900\"` for a variable font. Default: 400. */\n weight?: number | string\n style?: 'normal' | 'italic'\n}\n\n/** What an application says on its sign-in page, in one language. */\nexport interface ThemeTexts {\n /** Replaces \"Sign in\". */\n heading?: string\n /** Replaces \"to continue to …\". */\n subheading?: string\n /** The messages of the split layout's panel, shown one at a time (up to 5). */\n panel?: string[]\n /** Links at the foot of the split layout: privacy, terms (up to 4). */\n links?: { label: string; url: string }[]\n}\n\nexport interface CauthyTheme {\n /** The page. Colours are `#rrggbb` or `rgba(r, g, b, a)`. */\n background?: string\n /** The card, and inputs. */\n surface?: string\n /** Headings and what is typed. */\n text?: string\n /** Paragraphs, labels, small print. */\n textMuted?: string\n border?: string\n /** The fields and the secondary buttons, when they are not the card's colours. */\n inputBackground?: string\n inputBorder?: string\n /** The main button, and what is written on it. */\n primary?: string\n primaryText?: string\n /** Links, and the focus ring. */\n accent?: string\n error?: string\n errorBackground?: string\n success?: string\n successBackground?: string\n /** Corners of cards and inputs, in pixels (0–40). */\n radius?: number\n /** Corners of buttons, in pixels (0–40). */\n buttonRadius?: number\n /** A CSS font-family list: the application's typeface first, a system font after it. */\n fontFamily?: string\n /** The files of the typefaces `fontFamily` names, for people who do not have them installed. */\n fonts?: ThemeFont[]\n /** An image shown where \"Cauthy\" is, over https. */\n logoUrl?: string\n /**\n * How the sign-in pages are laid out: `card` (the default, one card in the\n * middle) or `split` (the form on one side, a panel that presents the\n * application on the other).\n */\n layout?: 'card' | 'split'\n /** The colours the split layout's backdrop blends, one to four. */\n panelColors?: string[]\n /** The colour of what is written on the panel. */\n panelText?: string\n /** An https image shown over the panel's messages. */\n panelIconUrl?: string\n /** The application's own words on the sign-in page, by language (`en`, `es`). */\n texts?: Record<string, ThemeTexts>\n /**\n * The application's Stackmyth tokens (`--sm-*`), from `tokensFromStackmyth`.\n * Cauthy's pages are drawn with Stackmyth's components, so these make its\n * buttons and fields the application's own. They are laid over the named values.\n */\n tokens?: Record<string, string>\n}\n\nexport interface StackmythThemeOptions {\n /**\n * The named theme to read, when the stylesheet keeps several under\n * `[data-theme=…]` (`\"vennet\"`). Without it only what applies to every\n * page (`:root`) is read.\n */\n theme?: string\n /** Which of the application's two modes to take. Default: `light`. */\n mode?: 'light' | 'dark'\n logoUrl?: string\n}\n\nexport interface ImportedTheme {\n theme: CauthyTheme\n /** Tokens that were there and could not be carried over, and why. */\n warnings: string[]\n}\n\nconst MAX_RADIUS = 40\n\n// ── Reading custom properties out of CSS ───────────────────────────────────\n\ninterface Declaration {\n name: string\n value: string\n /** The selectors and at-rules it sits inside, outermost first. */\n within: string[]\n}\n\nfunction declarations(css: string): Declaration[] {\n const text = css.replace(/\\/\\*[\\s\\S]*?\\*\\//g, '')\n const out: Declaration[] = []\n const within: string[] = []\n let chunk = ''\n let quote = ''\n let depth = 0 // parentheses, so a `;` inside url(…) does not end anything\n const flush = () => {\n const m = /^\\s*(--[\\w-]+)\\s*:\\s*([\\s\\S]+?)\\s*$/.exec(chunk)\n if (m) out.push({ name: m[1], value: m[2].replace(/\\s+/g, ' ').replace(/\\s*!important$/, ''), within: [...within] })\n chunk = ''\n }\n for (const ch of text) {\n if (quote) {\n chunk += ch\n if (ch === quote) quote = ''\n continue\n }\n if (ch === '\"' || ch === \"'\") {\n quote = ch\n chunk += ch\n } else if (ch === '(') {\n depth++\n chunk += ch\n } else if (ch === ')') {\n depth = Math.max(0, depth - 1)\n chunk += ch\n } else if (ch === '{' && depth === 0) {\n within.push(chunk.trim().replace(/\\s+/g, ' '))\n chunk = ''\n } else if (ch === '}' && depth === 0) {\n flush()\n within.pop()\n } else if (ch === ';' && depth === 0) {\n flush()\n } else {\n chunk += ch\n }\n }\n return out\n}\n\nconst DARK = /prefers-color-scheme:\\s*dark|data-(mode|theme|color-scheme)=[\"']?dark|\\.dark\\b/i\nconst LIGHT = /prefers-color-scheme:\\s*light|data-(mode|theme|color-scheme)=[\"']?light|\\.light\\b/i\nconst NAMED = /data-theme=[\"']?([\\w-]+)/i\n\n/** Whether a declaration applies to the page being themed. */\nfunction applies(d: Declaration, o: Required<Pick<StackmythThemeOptions, 'mode'>> & { theme?: string }): boolean {\n for (const scope of d.within) {\n if (o.mode === 'light' && DARK.test(scope)) return false\n if (o.mode === 'dark' && LIGHT.test(scope)) return false\n const named = NAMED.exec(scope)\n if (named && !/^(dark|light)$/i.test(named[1]) && named[1] !== o.theme) return false\n // Component-level rules (`.btn { --sm-… }`) are not the page's theme;\n // the class a mode is switched with (`.dark`, as shadcn/ui does) is.\n if (!scope.startsWith('@') && !/:root|^html\\b|^body\\b|\\[data-|^\\*$|:host|^\\.(dark|light)\\b/.test(scope)) return false\n }\n return true\n}\n\n// ── Tokens ─────────────────────────────────────────────────────────────────\n\n/** What a token's value may be written with, as Cauthy checks it. */\nconst TOKEN_CHARS = /^[A-Za-z0-9 #%.,()/'\"_+*-]+$/\nconst TOKEN_FUNCS = new Set(['var', 'calc', 'min', 'max', 'clamp', 'rgb', 'rgba', 'hsl', 'hsla', 'hwb', 'lab', 'lch', 'oklab', 'oklch', 'color',\n 'color-mix', 'light-dark', 'cubic-bezier', 'steps', 'linear', 'linear-gradient', 'radial-gradient', 'conic-gradient',\n 'repeating-linear-gradient', 'repeating-radial-gradient', 'drop-shadow', 'blur', 'translate', 'translatex', 'translatey', 'scale', 'rotate'])\n\nfunction tokenValue(value: string): string | null {\n const v = value.replace(/\\s+/g, ' ').trim()\n if (v === '' || v.length > 400 || !TOKEN_CHARS.test(v)) return null\n for (const m of v.matchAll(/([A-Za-z-]+)\\(/g)) if (!TOKEN_FUNCS.has(m[1].toLowerCase())) return null\n if (/^\\(|[ ,(]\\(/.test(v)) return null\n return v\n}\n\n/** A scope that is the page's theme itself: the root, or the theme and mode attributes — never a component inside it. */\nconst THEME_SCOPE = /^(?:\\s*(?::root|html|body|\\[data-(?:theme|mode|color-scheme)(?:=[\"']?[\\w-]+[\"']?)?\\])\\s*,?)+$/i\n\nexport interface StackmythTokens {\n /** Every `--sm-*` token the stylesheets set for the page, ready for `theme.tokens`. */\n tokens: Record<string, string>\n /** Tokens that were there and Cauthy would not take, and why. */\n warnings: string[]\n}\n\n/**\n * Reads an application's Stackmyth tokens out of its stylesheets: every\n * `--sm-*` custom property its theme sets, in the order the sheets are given\n * (a later one wins). Its own names — a brand palette the tokens point at —\n * are written in, so what comes out stands on Stackmyth's names alone.\n *\n * Cauthy's sign-in pages are drawn with Stackmyth's components: these tokens\n * are what makes them look like the application.\n */\nexport function tokensFromStackmyth(css: string | string[], options: StackmythThemeOptions = {}): StackmythTokens {\n const mode = options.mode ?? 'light'\n const all = (Array.isArray(css) ? css : [css]).flatMap(declarations)\n const named = options.theme ?? all.map((d) => d.within.map((w) => NAMED.exec(w)?.[1]).find((n) => n && !/^(dark|light)$/i.test(n))).find(Boolean)\n const vars = new Map<string, string>()\n for (const d of all) {\n if (!applies(d, { mode, theme: named })) continue\n if (!d.within.every((w) => w.startsWith('@') || THEME_SCOPE.test(w))) continue\n vars.set(d.name, d.value)\n }\n // The application's own names are written in; Stackmyth's are left as references.\n const inline = (value: string, depth = 0): string | null => {\n if (depth > 16) return null\n let failed = false\n let out = value\n for (let guard = 0; guard < 64; guard++) {\n const next = out.replace(/var\\(\\s*(--(?!sm-)[\\w-]+)\\s*(?:,\\s*([^()]*(?:\\([^()]*\\)[^()]*)*))?\\)/, (_m, name: string, fallback?: string) => {\n const known = vars.get(name)\n const got = known !== undefined ? inline(known, depth + 1) : fallback !== undefined ? inline(fallback.trim(), depth + 1) : null\n if (got === null) failed = true\n return got ?? ''\n })\n if (failed) return null\n if (next === out) break\n out = next\n }\n return out\n }\n const tokens: Record<string, string> = {}\n const warnings: string[] = []\n for (const [name, raw] of vars) {\n if (!/^--sm-[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name)) continue\n const written = inline(raw)\n const value = written === null ? null : tokenValue(written)\n if (value === null) warnings.push(`${name}: \"${raw.length > 60 ? raw.slice(0, 57) + '…' : raw}\" is not a value Cauthy takes`)\n else tokens[name] = value\n }\n if (Object.keys(tokens).length === 0) warnings.push('no --sm-* token was found in these stylesheets')\n return { tokens, warnings }\n}\n\n// ── Values ─────────────────────────────────────────────────────────────────\n\nfunction resolve(value: string, vars: Map<string, string>, seen = 0): string | null {\n if (seen > 16) return null\n let unresolved = false\n // Innermost var() first: fallbacks may hold another var().\n let out = value\n for (let guard = 0; guard < 32 && /var\\(/.test(out); guard++) {\n out = out.replace(/var\\(\\s*(--[\\w-]+)\\s*(?:,\\s*([^()]*(?:\\([^()]*\\)[^()]*)*))?\\)/, (_m, name: string, fallback?: string) => {\n const known = vars.get(name)\n const next = known !== undefined ? resolve(known, vars, seen + 1) : fallback !== undefined ? resolve(fallback.trim(), vars, seen + 1) : null\n if (next === null) unresolved = true\n return next ?? ''\n })\n if (unresolved) return null\n }\n return /var\\(/.test(out) ? null : out.trim()\n}\n\nconst clamp01 = (n: number) => Math.min(1, Math.max(0, n))\nconst hex2 = (n: number) => Math.round(clamp01(n) * 255).toString(16).padStart(2, '0')\nconst gamma = (c: number) => (c <= 0.0031308 ? 12.92 * c : 1.055 * Math.pow(c, 1 / 2.4) - 0.055)\n\nfunction out(r: number, g: number, b: number, a = 1): string {\n if (a >= 1) return `#${hex2(r)}${hex2(g)}${hex2(b)}`\n return `rgba(${Math.round(clamp01(r) * 255)}, ${Math.round(clamp01(g) * 255)}, ${Math.round(clamp01(b) * 255)}, ${Math.round(clamp01(a) * 1000) / 1000})`\n}\n\nconst alpha = (s: string | undefined) => (s === undefined ? 1 : s.endsWith('%') ? parseFloat(s) / 100 : parseFloat(s))\n\nconst KEYWORDS: Record<string, string> = { white: '#ffffff', black: '#000000' }\n\n/** A colour as Cauthy takes it — `#rrggbb` or `rgba(…)` — or null when it is none it can read. */\nexport function toColor(value: string): string | null {\n const v = value.trim().toLowerCase()\n if (KEYWORDS[v]) return KEYWORDS[v]\n if (/^#([0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/.test(v)) return v\n let m = /^rgba?\\(\\s*([\\d.]+%?)[\\s,]+([\\d.]+%?)[\\s,]+([\\d.]+%?)(?:\\s*[,/]\\s*([\\d.]+%?))?\\s*\\)$/.exec(v)\n if (m) {\n const ch = (s: string) => (s.endsWith('%') ? parseFloat(s) / 100 : parseFloat(s) / 255)\n return out(ch(m[1]), ch(m[2]), ch(m[3]), alpha(m[4]))\n }\n m = /^hsla?\\(\\s*([\\d.]+)(?:deg)?[\\s,]+([\\d.]+)%[\\s,]+([\\d.]+)%(?:\\s*[,/]\\s*([\\d.]+%?))?\\s*\\)$/.exec(v)\n if (m) {\n const h = ((parseFloat(m[1]) % 360) + 360) % 360\n const s = parseFloat(m[2]) / 100\n const l = parseFloat(m[3]) / 100\n const f = (n: number) => {\n const k = (n + h / 30) % 12\n return l - s * Math.min(l, 1 - l) * Math.max(-1, Math.min(k - 3, 9 - k, 1))\n }\n return out(f(0), f(8), f(4), alpha(m[4]))\n }\n m = /^oklch\\(\\s*([\\d.]+%?)\\s+([\\d.]+%?)\\s+([\\d.]+)(?:deg)?(?:\\s*\\/\\s*([\\d.]+%?))?\\s*\\)$/.exec(v)\n if (m) {\n const L = m[1].endsWith('%') ? parseFloat(m[1]) / 100 : parseFloat(m[1])\n const C = m[2].endsWith('%') ? (parseFloat(m[2]) / 100) * 0.4 : parseFloat(m[2])\n const H = (parseFloat(m[3]) * Math.PI) / 180\n const a = C * Math.cos(H)\n const b = C * Math.sin(H)\n const l = Math.pow(L + 0.3963377774 * a + 0.2158037573 * b, 3)\n const mm = Math.pow(L - 0.1055613458 * a - 0.0638541728 * b, 3)\n const s = Math.pow(L - 0.0894841775 * a - 1.291485548 * b, 3)\n return out(\n gamma(4.0767416621 * l - 3.3077115913 * mm + 0.2309699292 * s),\n gamma(-1.2684380046 * l + 2.6097574011 * mm - 0.3413193965 * s),\n gamma(-0.0041960863 * l - 0.7034186147 * mm + 1.707614701 * s),\n alpha(m[4]),\n )\n }\n return null\n}\n\n/** A length in pixels, within what Cauthy takes, or null. */\nfunction toRadius(value: string): number | null {\n const m = /^([\\d.]+)(px|rem|em)?$/.exec(value.trim())\n if (!m) return null\n const px = parseFloat(m[1]) * (m[2] === 'rem' || m[2] === 'em' ? 16 : 1)\n return Number.isFinite(px) ? Math.min(MAX_RADIUS, Math.max(0, Math.round(px))) : null\n}\n\n/** A font list as Cauthy takes it, or null. */\nfunction toFont(value: string): string | null {\n const v = value.trim()\n return /^[A-Za-z0-9 ,'\"_-]{1,200}$/.test(v) && (v.match(/\"/g)?.length ?? 0) % 2 === 0 && (v.match(/'/g)?.length ?? 0) % 2 === 0 ? v : null\n}\n\n// ── Stackmyth ──────────────────────────────────────────────────────────────\n\n/** Which Stackmyth token each value of a theme is read from, first found wins. */\nconst COLORS: [keyof CauthyTheme, string[]][] = [\n ['background', ['--sm-bg-surface', '--sm-surface']],\n ['surface', ['--sm-bg-surface-raised', '--sm-surface-raised']],\n ['text', ['--sm-text-primary', '--sm-surface-text']],\n ['textMuted', ['--sm-text-secondary']],\n ['border', ['--sm-border-default', '--sm-surface-border']],\n ['primary', ['--sm-primary', '--sm-bg-interactive']],\n ['primaryText', ['--sm-primary-text', '--sm-text-inverse']],\n ['accent', ['--sm-text-brand', '--sm-border-focus', '--sm-accent']],\n ['error', ['--sm-error-soft-text', '--sm-error']],\n ['errorBackground', ['--sm-error-soft']],\n ['success', ['--sm-success-soft-text', '--sm-success']],\n ['successBackground', ['--sm-success-soft']],\n]\n\n/**\n * Reads an application's Stackmyth theme out of its stylesheets.\n *\n * Give it the CSS that declares the tokens, in the order the application\n * loads it — Stackmyth's own (`@stackmyth/core/core.vars.css`, or a theme\n * file) and then the application's brand file that re-points them. Tokens\n * that point at the application's own variables are followed. The\n * application's dark mode is left out unless asked for.\n */\nexport function themeFromStackmyth(css: string | string[], options: StackmythThemeOptions = {}): ImportedTheme {\n const o = { mode: options.mode ?? 'light', theme: options.theme }\n const vars = new Map<string, string>()\n for (const sheet of Array.isArray(css) ? css : [css]) {\n for (const d of declarations(sheet)) if (applies(d, o)) vars.set(d.name, d.value)\n }\n const theme: CauthyTheme = {}\n const warnings: string[] = []\n\n const read = <T>(tokens: string[], to: (v: string) => T | null, what: string): T | undefined => {\n for (const token of tokens) {\n const raw = vars.get(token)\n if (raw === undefined) continue\n const value = resolve(raw, vars)\n const got = value === null ? null : to(value)\n if (got !== null) return got\n warnings.push(`${token}: \"${raw}\" is not ${what} Cauthy can take`)\n }\n return undefined\n }\n\n for (const [field, tokens] of COLORS) {\n const c = read(tokens, toColor, 'a colour')\n if (c !== undefined) (theme as Record<string, unknown>)[field] = c\n }\n const radius = read(['--sm-radius-container', '--sm-radius-md'], toRadius, 'a length')\n if (radius !== undefined) theme.radius = radius\n const button = read(['--sm-radius-interactive', '--sm-radius-sm'], toRadius, 'a length')\n if (button !== undefined) theme.buttonRadius = button\n const font = read(['--sm-font-family'], toFont, 'a font list')\n if (font !== undefined) theme.fontFamily = font\n if (options.logoUrl) theme.logoUrl = options.logoUrl\n\n if (vars.size === 0) warnings.push('no custom properties were found: is this the stylesheet that declares the --sm-* tokens?')\n else if (![...vars.keys()].some((k) => k.startsWith('--sm-'))) warnings.push('no --sm-* token was found in these stylesheets')\n return { theme, warnings }\n}\n\n// ── Any stylesheet ─────────────────────────────────────────────────────────\n\n/** The values of a theme a stylesheet can say. */\nexport type ThemeField =\n | 'background' | 'surface' | 'text' | 'textMuted' | 'border' | 'inputBackground' | 'inputBorder'\n | 'primary' | 'primaryText' | 'accent' | 'error' | 'errorBackground' | 'success' | 'successBackground'\n | 'radius' | 'buttonRadius' | 'fontFamily'\n\n/** How a stylesheet names its look: Stackmyth's tokens, shadcn/ui's (and Tailwind's), or the application's own. */\nexport type CssFormat = 'stackmyth' | 'shadcn' | 'custom'\n\nexport interface CssThemeOptions {\n /** Which names to read. Default: `auto`, told from the stylesheet itself. */\n format?: CssFormat | 'auto'\n /** Which of the application's two modes to take. Default: `light`. */\n mode?: 'light' | 'dark'\n /** The named theme to read, when the stylesheet keeps several under `[data-theme=…]`. */\n theme?: string\n /** For `custom`: the variable each value is read from, `{ primary: '--brand' }`. */\n map?: Partial<Record<ThemeField, string>>\n}\n\nexport interface CssImportedTheme extends ImportedTheme {\n /** The names that were read. */\n format: CssFormat\n}\n\nconst RADII: ThemeField[] = ['radius', 'buttonRadius']\n\n/** shadcn/ui's names, as Tailwind 3 (`--primary`) and Tailwind 4 (`--color-primary`) write them. */\nconst SHADCN: [ThemeField, string[]][] = [\n ['background', ['--background', '--color-background']],\n ['surface', ['--card', '--color-card', '--popover', '--color-popover']],\n ['text', ['--foreground', '--color-foreground']],\n ['textMuted', ['--muted-foreground', '--color-muted-foreground']],\n ['border', ['--border', '--color-border']],\n ['inputBorder', ['--input', '--color-input']],\n ['primary', ['--primary', '--color-primary']],\n ['primaryText', ['--primary-foreground', '--color-primary-foreground']],\n ['accent', ['--ring', '--color-ring', '--primary', '--color-primary']],\n ['error', ['--destructive', '--color-destructive']],\n ['radius', ['--radius']],\n ['buttonRadius', ['--radius']],\n ['fontFamily', ['--font-sans', '--font-family']],\n]\n\n/** A colour as shadcn/ui writes it on Tailwind 3 — `222.2 84% 4.9%`, the inside of hsl() — or any other. */\nfunction shadcnColor(value: string): string | null {\n return /^[\\d.]+(?:deg)?\\s+[\\d.]+%\\s+[\\d.]+%(?:\\s*\\/\\s*[\\d.]+%?)?$/.test(value.trim()) ? toColor(`hsl(${value.trim()})`) : toColor(value)\n}\n\n/** Every custom property a stylesheet sets for the page, the last one winning. */\nfunction pageVariables(css: string | string[], mode: 'light' | 'dark', theme?: string): Map<string, string> {\n const vars = new Map<string, string>()\n for (const sheet of Array.isArray(css) ? css : [css]) {\n for (const d of declarations(sheet)) if (applies(d, { mode, theme })) vars.set(d.name, d.value)\n }\n return vars\n}\n\n/** The custom properties a stylesheet sets for the page, by name: what a person picks from to map their own. */\nexport function cssVariables(css: string | string[], options: Pick<CssThemeOptions, 'mode' | 'theme'> = {}): string[] {\n return [...pageVariables(css, options.mode ?? 'light', options.theme).keys()].sort()\n}\n\n/**\n * Reads an application's look out of its stylesheets, whatever names it uses:\n * Stackmyth's tokens, shadcn/ui's (`--primary`, `--radius`, on Tailwind 3 or\n * 4), or the application's own variables given in `map`. What comes out is a\n * theme of named values, never CSS.\n */\nexport function themeFromCss(css: string | string[], options: CssThemeOptions = {}): CssImportedTheme {\n const mode = options.mode ?? 'light'\n const vars = pageVariables(css, mode, options.theme)\n const names = [...vars.keys()]\n let format: CssFormat\n if (options.format && options.format !== 'auto') format = options.format\n else if (options.map && Object.keys(options.map).length > 0) format = 'custom'\n else if (names.some((n) => n.startsWith('--sm-'))) format = 'stackmyth'\n else if (names.some((n) => /^--(color-)?(primary|background|foreground)$/.test(n))) format = 'shadcn'\n else format = 'custom'\n\n if (format === 'stackmyth') return { ...themeFromStackmyth(css, { mode, theme: options.theme }), format }\n\n const spec: [ThemeField, string[]][] = format === 'shadcn'\n ? SHADCN\n : Object.entries(options.map ?? {}).filter(([, v]) => v).map(([k, v]) => [k as ThemeField, [String(v).startsWith('--') ? String(v) : `--${v}`]])\n const theme: CauthyTheme = {}\n const warnings: string[] = []\n for (const [field, tokens] of spec) {\n for (const token of tokens) {\n const raw = vars.get(token)\n if (raw === undefined) continue\n const value = resolve(raw, vars)\n const got = value === null ? null\n : RADII.includes(field) ? toRadius(value)\n : field === 'fontFamily' ? toFont(value)\n : format === 'shadcn' ? shadcnColor(value) : toColor(value)\n if (got !== null) {\n (theme as Record<string, unknown>)[field] = got\n break\n }\n warnings.push(`${token}: \"${raw.length > 60 ? raw.slice(0, 57) + '…' : raw}\" is not ${RADII.includes(field) ? 'a length' : field === 'fontFamily' ? 'a font list' : 'a colour'} Cauthy can take`)\n }\n }\n if (vars.size === 0) warnings.push('no custom properties were found in these stylesheets')\n else if (format === 'custom' && spec.length === 0) warnings.push('these stylesheets use names of their own: say which variable each value is read from')\n return { theme, warnings, format }\n}\n\n// ── On the application's own screens ───────────────────────────────────────\n\n/**\n * A theme as the CSS variables of `cauthy-auth/styles.css` (`--cauthy-*`), for\n * the forms an application shows on its own pages. The page's own background,\n * the layout and the words are Cauthy's pages' matter, and left out.\n */\nexport function themeVariables(theme: CauthyTheme): Record<string, string> {\n const vars: Record<string, string> = {}\n const put = (name: string, v: string | number | undefined) => {\n if (v === undefined || v === '') return\n vars[`--cauthy-${name}`] = typeof v === 'number' ? `${v}px` : v\n }\n put('surface', theme.surface)\n put('text', theme.text)\n put('text-muted', theme.textMuted)\n put('border', theme.border)\n put('input-background', theme.inputBackground)\n put('input-border', theme.inputBorder)\n put('primary', theme.primary)\n put('primary-text', theme.primaryText)\n put('accent', theme.accent)\n put('error', theme.error)\n put('error-background', theme.errorBackground)\n put('success', theme.success)\n put('success-background', theme.successBackground)\n put('radius', theme.radius)\n put('button-radius', theme.buttonRadius)\n put('font', theme.fontFamily)\n return vars\n}\n\n// ── Sending it to Cauthy ───────────────────────────────────────────────────\n\n/** A theme as Cauthy's API names its values. */\nexport function toWire(theme: CauthyTheme): Record<string, unknown> {\n const names: Record<keyof CauthyTheme, string> = {\n background: 'background', surface: 'surface', text: 'text', textMuted: 'text_muted', border: 'border',\n primary: 'primary', primaryText: 'primary_text', accent: 'accent', error: 'error', errorBackground: 'error_background',\n success: 'success', successBackground: 'success_background', radius: 'radius', buttonRadius: 'button_radius',\n fontFamily: 'font_family', logoUrl: 'logo_url', fonts: 'fonts', layout: 'layout', panelColors: 'panel_colors',\n panelText: 'panel_text', panelIconUrl: 'panel_icon_url', texts: 'texts', inputBackground: 'input_background', inputBorder: 'input_border', tokens: 'tokens',\n }\n const wire: Record<string, unknown> = {}\n for (const [k, v] of Object.entries(theme)) {\n if (v === undefined || v === '' || (Array.isArray(v) && v.length === 0)) continue\n wire[names[k as keyof CauthyTheme]] = v\n }\n return wire\n}\n\n/** A theme as Cauthy answers it, in this package's names. */\nexport function fromWire(wire: Record<string, unknown>): CauthyTheme {\n const names: Record<string, keyof CauthyTheme> = {\n background: 'background', surface: 'surface', text: 'text', text_muted: 'textMuted', border: 'border',\n primary: 'primary', primary_text: 'primaryText', accent: 'accent', error: 'error', error_background: 'errorBackground',\n success: 'success', success_background: 'successBackground', radius: 'radius', button_radius: 'buttonRadius',\n font_family: 'fontFamily', logo_url: 'logoUrl', fonts: 'fonts', layout: 'layout', panel_colors: 'panelColors',\n panel_text: 'panelText', panel_icon_url: 'panelIconUrl', texts: 'texts', input_background: 'inputBackground', input_border: 'inputBorder', tokens: 'tokens',\n }\n const theme: Record<string, unknown> = {}\n for (const [k, v] of Object.entries(wire ?? {})) if (names[k] && v !== undefined && v !== null && v !== '') theme[names[k]] = v\n return theme as CauthyTheme\n}\n\nexport interface PushThemeOptions {\n /** Cauthy's address. Default: `CAUTHY_ISSUER`. */\n issuer?: string\n /** The application's client id. Default: `CAUTHY_CLIENT_ID`. */\n clientId?: string\n /** The workspace's slug. Default: `CAUTHY_TENANT`. */\n tenant?: string\n /**\n * A personal access token with `oauth_clients:manage`. Default:\n * `CAUTHY_TOKEN`. It belongs in the deploy pipeline, never in the application.\n */\n token?: string\n theme: CauthyTheme\n fetch?: typeof fetch\n}\n\n/**\n * Gives the application its look on Cauthy's pages. It replaces the theme: a\n * value left out goes back to Cauthy's own. Run it when the brand changes —\n * from a script or the deploy — not on every request.\n */\nexport async function pushTheme(o: PushThemeOptions): Promise<void> {\n const env = (n: string) => (typeof process !== 'undefined' ? process.env?.[n] : undefined)\n const issuer = (o.issuer ?? env('CAUTHY_ISSUER') ?? '').replace(/\\/+$/, '')\n const clientId = o.clientId ?? env('CAUTHY_CLIENT_ID') ?? ''\n const tenant = o.tenant ?? env('CAUTHY_TENANT') ?? ''\n const token = o.token ?? env('CAUTHY_TOKEN') ?? ''\n for (const [name, v] of Object.entries({ CAUTHY_ISSUER: issuer, CAUTHY_CLIENT_ID: clientId, CAUTHY_TENANT: tenant, CAUTHY_TOKEN: token })) {\n if (v === '') throw new Error(`cauthy-auth: ${name} is required to push a theme`)\n }\n const res = await (o.fetch ?? fetch)(`${issuer}/v1/oauth/clients/${encodeURIComponent(clientId)}/theme`, {\n method: 'PUT',\n headers: { authorization: `Bearer ${token}`, 'x-tenant-key': tenant, 'content-type': 'application/json' },\n body: JSON.stringify(toWire(o.theme)),\n })\n if (!res.ok) {\n const body = (await res.json().catch(() => null)) as { errors?: { message?: string }[] } | null\n throw new Error(`cauthy-auth: Cauthy refused the theme (${res.status}): ${body?.errors?.[0]?.message ?? res.statusText}`)\n }\n}\n"],"mappings":"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","names":["out"]}
package/dist/theme.mjs CHANGED
@@ -50,10 +50,92 @@ function applies(d, o) {
50
50
  if (o.mode === "dark" && LIGHT.test(scope)) return false;
51
51
  const named = NAMED.exec(scope);
52
52
  if (named && !/^(dark|light)$/i.test(named[1]) && named[1] !== o.theme) return false;
53
- if (!scope.startsWith("@") && !/:root|^html\b|^body\b|\[data-|^\*$|:host/.test(scope)) return false;
53
+ if (!scope.startsWith("@") && !/:root|^html\b|^body\b|\[data-|^\*$|:host|^\.(dark|light)\b/.test(scope)) return false;
54
54
  }
55
55
  return true;
56
56
  }
57
+ var TOKEN_CHARS = /^[A-Za-z0-9 #%.,()/'"_+*-]+$/;
58
+ var TOKEN_FUNCS = /* @__PURE__ */ new Set([
59
+ "var",
60
+ "calc",
61
+ "min",
62
+ "max",
63
+ "clamp",
64
+ "rgb",
65
+ "rgba",
66
+ "hsl",
67
+ "hsla",
68
+ "hwb",
69
+ "lab",
70
+ "lch",
71
+ "oklab",
72
+ "oklch",
73
+ "color",
74
+ "color-mix",
75
+ "light-dark",
76
+ "cubic-bezier",
77
+ "steps",
78
+ "linear",
79
+ "linear-gradient",
80
+ "radial-gradient",
81
+ "conic-gradient",
82
+ "repeating-linear-gradient",
83
+ "repeating-radial-gradient",
84
+ "drop-shadow",
85
+ "blur",
86
+ "translate",
87
+ "translatex",
88
+ "translatey",
89
+ "scale",
90
+ "rotate"
91
+ ]);
92
+ function tokenValue(value) {
93
+ const v = value.replace(/\s+/g, " ").trim();
94
+ if (v === "" || v.length > 400 || !TOKEN_CHARS.test(v)) return null;
95
+ for (const m of v.matchAll(/([A-Za-z-]+)\(/g)) if (!TOKEN_FUNCS.has(m[1].toLowerCase())) return null;
96
+ if (/^\(|[ ,(]\(/.test(v)) return null;
97
+ return v;
98
+ }
99
+ var THEME_SCOPE = /^(?:\s*(?::root|html|body|\[data-(?:theme|mode|color-scheme)(?:=["']?[\w-]+["']?)?\])\s*,?)+$/i;
100
+ function tokensFromStackmyth(css, options = {}) {
101
+ const mode = options.mode ?? "light";
102
+ const all = (Array.isArray(css) ? css : [css]).flatMap(declarations);
103
+ const named = options.theme ?? all.map((d) => d.within.map((w) => NAMED.exec(w)?.[1]).find((n) => n && !/^(dark|light)$/i.test(n))).find(Boolean);
104
+ const vars = /* @__PURE__ */ new Map();
105
+ for (const d of all) {
106
+ if (!applies(d, { mode, theme: named })) continue;
107
+ if (!d.within.every((w) => w.startsWith("@") || THEME_SCOPE.test(w))) continue;
108
+ vars.set(d.name, d.value);
109
+ }
110
+ const inline = (value, depth = 0) => {
111
+ if (depth > 16) return null;
112
+ let failed = false;
113
+ let out2 = value;
114
+ for (let guard = 0; guard < 64; guard++) {
115
+ const next = out2.replace(/var\(\s*(--(?!sm-)[\w-]+)\s*(?:,\s*([^()]*(?:\([^()]*\)[^()]*)*))?\)/, (_m, name, fallback) => {
116
+ const known = vars.get(name);
117
+ const got = known !== void 0 ? inline(known, depth + 1) : fallback !== void 0 ? inline(fallback.trim(), depth + 1) : null;
118
+ if (got === null) failed = true;
119
+ return got ?? "";
120
+ });
121
+ if (failed) return null;
122
+ if (next === out2) break;
123
+ out2 = next;
124
+ }
125
+ return out2;
126
+ };
127
+ const tokens = {};
128
+ const warnings = [];
129
+ for (const [name, raw] of vars) {
130
+ if (!/^--sm-[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name)) continue;
131
+ const written = inline(raw);
132
+ const value = written === null ? null : tokenValue(written);
133
+ if (value === null) warnings.push(`${name}: "${raw.length > 60 ? raw.slice(0, 57) + "\u2026" : raw}" is not a value Cauthy takes`);
134
+ else tokens[name] = value;
135
+ }
136
+ if (Object.keys(tokens).length === 0) warnings.push("no --sm-* token was found in these stylesheets");
137
+ return { tokens, warnings };
138
+ }
57
139
  function resolve(value, vars, seen = 0) {
58
140
  if (seen > 16) return null;
59
141
  let unresolved = false;
@@ -175,6 +257,90 @@ function themeFromStackmyth(css, options = {}) {
175
257
  else if (![...vars.keys()].some((k) => k.startsWith("--sm-"))) warnings.push("no --sm-* token was found in these stylesheets");
176
258
  return { theme, warnings };
177
259
  }
260
+ var RADII = ["radius", "buttonRadius"];
261
+ var SHADCN = [
262
+ ["background", ["--background", "--color-background"]],
263
+ ["surface", ["--card", "--color-card", "--popover", "--color-popover"]],
264
+ ["text", ["--foreground", "--color-foreground"]],
265
+ ["textMuted", ["--muted-foreground", "--color-muted-foreground"]],
266
+ ["border", ["--border", "--color-border"]],
267
+ ["inputBorder", ["--input", "--color-input"]],
268
+ ["primary", ["--primary", "--color-primary"]],
269
+ ["primaryText", ["--primary-foreground", "--color-primary-foreground"]],
270
+ ["accent", ["--ring", "--color-ring", "--primary", "--color-primary"]],
271
+ ["error", ["--destructive", "--color-destructive"]],
272
+ ["radius", ["--radius"]],
273
+ ["buttonRadius", ["--radius"]],
274
+ ["fontFamily", ["--font-sans", "--font-family"]]
275
+ ];
276
+ function shadcnColor(value) {
277
+ return /^[\d.]+(?:deg)?\s+[\d.]+%\s+[\d.]+%(?:\s*\/\s*[\d.]+%?)?$/.test(value.trim()) ? toColor(`hsl(${value.trim()})`) : toColor(value);
278
+ }
279
+ function pageVariables(css, mode, theme) {
280
+ const vars = /* @__PURE__ */ new Map();
281
+ for (const sheet of Array.isArray(css) ? css : [css]) {
282
+ for (const d of declarations(sheet)) if (applies(d, { mode, theme })) vars.set(d.name, d.value);
283
+ }
284
+ return vars;
285
+ }
286
+ function cssVariables(css, options = {}) {
287
+ return [...pageVariables(css, options.mode ?? "light", options.theme).keys()].sort();
288
+ }
289
+ function themeFromCss(css, options = {}) {
290
+ const mode = options.mode ?? "light";
291
+ const vars = pageVariables(css, mode, options.theme);
292
+ const names = [...vars.keys()];
293
+ let format;
294
+ if (options.format && options.format !== "auto") format = options.format;
295
+ else if (options.map && Object.keys(options.map).length > 0) format = "custom";
296
+ else if (names.some((n) => n.startsWith("--sm-"))) format = "stackmyth";
297
+ else if (names.some((n) => /^--(color-)?(primary|background|foreground)$/.test(n))) format = "shadcn";
298
+ else format = "custom";
299
+ if (format === "stackmyth") return { ...themeFromStackmyth(css, { mode, theme: options.theme }), format };
300
+ const spec = format === "shadcn" ? SHADCN : Object.entries(options.map ?? {}).filter(([, v]) => v).map(([k, v]) => [k, [String(v).startsWith("--") ? String(v) : `--${v}`]]);
301
+ const theme = {};
302
+ const warnings = [];
303
+ for (const [field, tokens] of spec) {
304
+ for (const token of tokens) {
305
+ const raw = vars.get(token);
306
+ if (raw === void 0) continue;
307
+ const value = resolve(raw, vars);
308
+ const got = value === null ? null : RADII.includes(field) ? toRadius(value) : field === "fontFamily" ? toFont(value) : format === "shadcn" ? shadcnColor(value) : toColor(value);
309
+ if (got !== null) {
310
+ theme[field] = got;
311
+ break;
312
+ }
313
+ warnings.push(`${token}: "${raw.length > 60 ? raw.slice(0, 57) + "\u2026" : raw}" is not ${RADII.includes(field) ? "a length" : field === "fontFamily" ? "a font list" : "a colour"} Cauthy can take`);
314
+ }
315
+ }
316
+ if (vars.size === 0) warnings.push("no custom properties were found in these stylesheets");
317
+ else if (format === "custom" && spec.length === 0) warnings.push("these stylesheets use names of their own: say which variable each value is read from");
318
+ return { theme, warnings, format };
319
+ }
320
+ function themeVariables(theme) {
321
+ const vars = {};
322
+ const put = (name, v) => {
323
+ if (v === void 0 || v === "") return;
324
+ vars[`--cauthy-${name}`] = typeof v === "number" ? `${v}px` : v;
325
+ };
326
+ put("surface", theme.surface);
327
+ put("text", theme.text);
328
+ put("text-muted", theme.textMuted);
329
+ put("border", theme.border);
330
+ put("input-background", theme.inputBackground);
331
+ put("input-border", theme.inputBorder);
332
+ put("primary", theme.primary);
333
+ put("primary-text", theme.primaryText);
334
+ put("accent", theme.accent);
335
+ put("error", theme.error);
336
+ put("error-background", theme.errorBackground);
337
+ put("success", theme.success);
338
+ put("success-background", theme.successBackground);
339
+ put("radius", theme.radius);
340
+ put("button-radius", theme.buttonRadius);
341
+ put("font", theme.fontFamily);
342
+ return vars;
343
+ }
178
344
  function toWire(theme) {
179
345
  const names = {
180
346
  background: "background",
@@ -200,7 +366,8 @@ function toWire(theme) {
200
366
  panelIconUrl: "panel_icon_url",
201
367
  texts: "texts",
202
368
  inputBackground: "input_background",
203
- inputBorder: "input_border"
369
+ inputBorder: "input_border",
370
+ tokens: "tokens"
204
371
  };
205
372
  const wire = {};
206
373
  for (const [k, v] of Object.entries(theme)) {
@@ -234,7 +401,8 @@ function fromWire(wire) {
234
401
  panel_icon_url: "panelIconUrl",
235
402
  texts: "texts",
236
403
  input_background: "inputBackground",
237
- input_border: "inputBorder"
404
+ input_border: "inputBorder",
405
+ tokens: "tokens"
238
406
  };
239
407
  const theme = {};
240
408
  for (const [k, v] of Object.entries(wire ?? {})) if (names[k] && v !== void 0 && v !== null && v !== "") theme[names[k]] = v;
@@ -260,10 +428,14 @@ async function pushTheme(o) {
260
428
  }
261
429
  }
262
430
  export {
431
+ cssVariables,
263
432
  fromWire,
264
433
  pushTheme,
434
+ themeFromCss,
265
435
  themeFromStackmyth,
436
+ themeVariables,
266
437
  toColor,
267
- toWire
438
+ toWire,
439
+ tokensFromStackmyth
268
440
  };
269
441
  //# sourceMappingURL=theme.mjs.map