cauthy-auth 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -57,6 +57,254 @@ async function unseal(sealed, key) {
57
57
  // src/system.ts
58
58
  import { exportJWK, importJWK, importPKCS8, SignJWT } from "jose";
59
59
 
60
+ // src/theme.ts
61
+ var MAX_RADIUS = 40;
62
+ function declarations(css) {
63
+ const text = css.replace(/\/\*[\s\S]*?\*\//g, "");
64
+ const out2 = [];
65
+ const within = [];
66
+ let chunk = "";
67
+ let quote = "";
68
+ let depth = 0;
69
+ const flush = () => {
70
+ const m = /^\s*(--[\w-]+)\s*:\s*([\s\S]+?)\s*$/.exec(chunk);
71
+ if (m) out2.push({ name: m[1], value: m[2].replace(/\s+/g, " ").replace(/\s*!important$/, ""), within: [...within] });
72
+ chunk = "";
73
+ };
74
+ for (const ch of text) {
75
+ if (quote) {
76
+ chunk += ch;
77
+ if (ch === quote) quote = "";
78
+ continue;
79
+ }
80
+ if (ch === '"' || ch === "'") {
81
+ quote = ch;
82
+ chunk += ch;
83
+ } else if (ch === "(") {
84
+ depth++;
85
+ chunk += ch;
86
+ } else if (ch === ")") {
87
+ depth = Math.max(0, depth - 1);
88
+ chunk += ch;
89
+ } else if (ch === "{" && depth === 0) {
90
+ within.push(chunk.trim().replace(/\s+/g, " "));
91
+ chunk = "";
92
+ } else if (ch === "}" && depth === 0) {
93
+ flush();
94
+ within.pop();
95
+ } else if (ch === ";" && depth === 0) {
96
+ flush();
97
+ } else {
98
+ chunk += ch;
99
+ }
100
+ }
101
+ return out2;
102
+ }
103
+ var DARK = /prefers-color-scheme:\s*dark|data-(mode|theme|color-scheme)=["']?dark|\.dark\b/i;
104
+ var LIGHT = /prefers-color-scheme:\s*light|data-(mode|theme|color-scheme)=["']?light|\.light\b/i;
105
+ var NAMED = /data-theme=["']?([\w-]+)/i;
106
+ function applies(d, o) {
107
+ for (const scope of d.within) {
108
+ if (o.mode === "light" && DARK.test(scope)) return false;
109
+ if (o.mode === "dark" && LIGHT.test(scope)) return false;
110
+ const named = NAMED.exec(scope);
111
+ if (named && !/^(dark|light)$/i.test(named[1]) && named[1] !== o.theme) return false;
112
+ if (!scope.startsWith("@") && !/:root|^html\b|^body\b|\[data-|^\*$|:host/.test(scope)) return false;
113
+ }
114
+ return true;
115
+ }
116
+ function resolve(value, vars, seen = 0) {
117
+ if (seen > 16) return null;
118
+ let unresolved = false;
119
+ let out2 = value;
120
+ for (let guard = 0; guard < 32 && /var\(/.test(out2); guard++) {
121
+ out2 = out2.replace(/var\(\s*(--[\w-]+)\s*(?:,\s*([^()]*(?:\([^()]*\)[^()]*)*))?\)/, (_m, name, fallback) => {
122
+ const known = vars.get(name);
123
+ const next = known !== void 0 ? resolve(known, vars, seen + 1) : fallback !== void 0 ? resolve(fallback.trim(), vars, seen + 1) : null;
124
+ if (next === null) unresolved = true;
125
+ return next ?? "";
126
+ });
127
+ if (unresolved) return null;
128
+ }
129
+ return /var\(/.test(out2) ? null : out2.trim();
130
+ }
131
+ var clamp01 = (n) => Math.min(1, Math.max(0, n));
132
+ var hex2 = (n) => Math.round(clamp01(n) * 255).toString(16).padStart(2, "0");
133
+ var gamma = (c) => c <= 31308e-7 ? 12.92 * c : 1.055 * Math.pow(c, 1 / 2.4) - 0.055;
134
+ function out(r, g, b, a = 1) {
135
+ if (a >= 1) return `#${hex2(r)}${hex2(g)}${hex2(b)}`;
136
+ return `rgba(${Math.round(clamp01(r) * 255)}, ${Math.round(clamp01(g) * 255)}, ${Math.round(clamp01(b) * 255)}, ${Math.round(clamp01(a) * 1e3) / 1e3})`;
137
+ }
138
+ var alpha = (s) => s === void 0 ? 1 : s.endsWith("%") ? parseFloat(s) / 100 : parseFloat(s);
139
+ var KEYWORDS = { white: "#ffffff", black: "#000000" };
140
+ function toColor(value) {
141
+ const v = value.trim().toLowerCase();
142
+ if (KEYWORDS[v]) return KEYWORDS[v];
143
+ if (/^#([0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/.test(v)) return v;
144
+ let m = /^rgba?\(\s*([\d.]+%?)[\s,]+([\d.]+%?)[\s,]+([\d.]+%?)(?:\s*[,/]\s*([\d.]+%?))?\s*\)$/.exec(v);
145
+ if (m) {
146
+ const ch = (s) => s.endsWith("%") ? parseFloat(s) / 100 : parseFloat(s) / 255;
147
+ return out(ch(m[1]), ch(m[2]), ch(m[3]), alpha(m[4]));
148
+ }
149
+ m = /^hsla?\(\s*([\d.]+)(?:deg)?[\s,]+([\d.]+)%[\s,]+([\d.]+)%(?:\s*[,/]\s*([\d.]+%?))?\s*\)$/.exec(v);
150
+ if (m) {
151
+ const h = (parseFloat(m[1]) % 360 + 360) % 360;
152
+ const s = parseFloat(m[2]) / 100;
153
+ const l = parseFloat(m[3]) / 100;
154
+ const f = (n) => {
155
+ const k = (n + h / 30) % 12;
156
+ return l - s * Math.min(l, 1 - l) * Math.max(-1, Math.min(k - 3, 9 - k, 1));
157
+ };
158
+ return out(f(0), f(8), f(4), alpha(m[4]));
159
+ }
160
+ m = /^oklch\(\s*([\d.]+%?)\s+([\d.]+%?)\s+([\d.]+)(?:deg)?(?:\s*\/\s*([\d.]+%?))?\s*\)$/.exec(v);
161
+ if (m) {
162
+ const L = m[1].endsWith("%") ? parseFloat(m[1]) / 100 : parseFloat(m[1]);
163
+ const C = m[2].endsWith("%") ? parseFloat(m[2]) / 100 * 0.4 : parseFloat(m[2]);
164
+ const H = parseFloat(m[3]) * Math.PI / 180;
165
+ const a = C * Math.cos(H);
166
+ const b = C * Math.sin(H);
167
+ const l = Math.pow(L + 0.3963377774 * a + 0.2158037573 * b, 3);
168
+ const mm = Math.pow(L - 0.1055613458 * a - 0.0638541728 * b, 3);
169
+ const s = Math.pow(L - 0.0894841775 * a - 1.291485548 * b, 3);
170
+ return out(
171
+ gamma(4.0767416621 * l - 3.3077115913 * mm + 0.2309699292 * s),
172
+ gamma(-1.2684380046 * l + 2.6097574011 * mm - 0.3413193965 * s),
173
+ gamma(-0.0041960863 * l - 0.7034186147 * mm + 1.707614701 * s),
174
+ alpha(m[4])
175
+ );
176
+ }
177
+ return null;
178
+ }
179
+ function toRadius(value) {
180
+ const m = /^([\d.]+)(px|rem|em)?$/.exec(value.trim());
181
+ if (!m) return null;
182
+ const px = parseFloat(m[1]) * (m[2] === "rem" || m[2] === "em" ? 16 : 1);
183
+ return Number.isFinite(px) ? Math.min(MAX_RADIUS, Math.max(0, Math.round(px))) : null;
184
+ }
185
+ function toFont(value) {
186
+ const v = value.trim();
187
+ 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;
188
+ }
189
+ var COLORS = [
190
+ ["background", ["--sm-bg-surface", "--sm-surface"]],
191
+ ["surface", ["--sm-bg-surface-raised", "--sm-surface-raised"]],
192
+ ["text", ["--sm-text-primary", "--sm-surface-text"]],
193
+ ["textMuted", ["--sm-text-secondary"]],
194
+ ["border", ["--sm-border-default", "--sm-surface-border"]],
195
+ ["primary", ["--sm-primary", "--sm-bg-interactive"]],
196
+ ["primaryText", ["--sm-primary-text", "--sm-text-inverse"]],
197
+ ["accent", ["--sm-text-brand", "--sm-border-focus", "--sm-accent"]],
198
+ ["error", ["--sm-error-soft-text", "--sm-error"]],
199
+ ["errorBackground", ["--sm-error-soft"]],
200
+ ["success", ["--sm-success-soft-text", "--sm-success"]],
201
+ ["successBackground", ["--sm-success-soft"]]
202
+ ];
203
+ function themeFromStackmyth(css, options = {}) {
204
+ const o = { mode: options.mode ?? "light", theme: options.theme };
205
+ const vars = /* @__PURE__ */ new Map();
206
+ for (const sheet of Array.isArray(css) ? css : [css]) {
207
+ for (const d of declarations(sheet)) if (applies(d, o)) vars.set(d.name, d.value);
208
+ }
209
+ const theme = {};
210
+ const warnings = [];
211
+ const read = (tokens, to, what) => {
212
+ for (const token of tokens) {
213
+ const raw = vars.get(token);
214
+ if (raw === void 0) continue;
215
+ const value = resolve(raw, vars);
216
+ const got = value === null ? null : to(value);
217
+ if (got !== null) return got;
218
+ warnings.push(`${token}: "${raw}" is not ${what} Cauthy can take`);
219
+ }
220
+ return void 0;
221
+ };
222
+ for (const [field, tokens] of COLORS) {
223
+ const c = read(tokens, toColor, "a colour");
224
+ if (c !== void 0) theme[field] = c;
225
+ }
226
+ const radius = read(["--sm-radius-container", "--sm-radius-md"], toRadius, "a length");
227
+ if (radius !== void 0) theme.radius = radius;
228
+ const button = read(["--sm-radius-interactive", "--sm-radius-sm"], toRadius, "a length");
229
+ if (button !== void 0) theme.buttonRadius = button;
230
+ const font = read(["--sm-font-family"], toFont, "a font list");
231
+ if (font !== void 0) theme.fontFamily = font;
232
+ if (options.logoUrl) theme.logoUrl = options.logoUrl;
233
+ if (vars.size === 0) warnings.push("no custom properties were found: is this the stylesheet that declares the --sm-* tokens?");
234
+ else if (![...vars.keys()].some((k) => k.startsWith("--sm-"))) warnings.push("no --sm-* token was found in these stylesheets");
235
+ return { theme, warnings };
236
+ }
237
+ function toWire(theme) {
238
+ const names = {
239
+ background: "background",
240
+ surface: "surface",
241
+ text: "text",
242
+ textMuted: "text_muted",
243
+ border: "border",
244
+ primary: "primary",
245
+ primaryText: "primary_text",
246
+ accent: "accent",
247
+ error: "error",
248
+ errorBackground: "error_background",
249
+ success: "success",
250
+ successBackground: "success_background",
251
+ radius: "radius",
252
+ buttonRadius: "button_radius",
253
+ fontFamily: "font_family",
254
+ logoUrl: "logo_url",
255
+ fonts: "fonts"
256
+ };
257
+ const wire = {};
258
+ for (const [k, v] of Object.entries(theme)) {
259
+ if (v === void 0 || v === "" || Array.isArray(v) && v.length === 0) continue;
260
+ wire[names[k]] = v;
261
+ }
262
+ return wire;
263
+ }
264
+ function fromWire(wire) {
265
+ const names = {
266
+ background: "background",
267
+ surface: "surface",
268
+ text: "text",
269
+ text_muted: "textMuted",
270
+ border: "border",
271
+ primary: "primary",
272
+ primary_text: "primaryText",
273
+ accent: "accent",
274
+ error: "error",
275
+ error_background: "errorBackground",
276
+ success: "success",
277
+ success_background: "successBackground",
278
+ radius: "radius",
279
+ button_radius: "buttonRadius",
280
+ font_family: "fontFamily",
281
+ logo_url: "logoUrl",
282
+ fonts: "fonts"
283
+ };
284
+ const theme = {};
285
+ for (const [k, v] of Object.entries(wire ?? {})) if (names[k] && v !== void 0 && v !== null && v !== "") theme[names[k]] = v;
286
+ return theme;
287
+ }
288
+ async function pushTheme(o) {
289
+ const env3 = (n) => typeof process !== "undefined" ? process.env?.[n] : void 0;
290
+ const issuer = (o.issuer ?? env3("CAUTHY_ISSUER") ?? "").replace(/\/+$/, "");
291
+ const clientId = o.clientId ?? env3("CAUTHY_CLIENT_ID") ?? "";
292
+ const tenant = o.tenant ?? env3("CAUTHY_TENANT") ?? "";
293
+ const token = o.token ?? env3("CAUTHY_TOKEN") ?? "";
294
+ for (const [name, v] of Object.entries({ CAUTHY_ISSUER: issuer, CAUTHY_CLIENT_ID: clientId, CAUTHY_TENANT: tenant, CAUTHY_TOKEN: token })) {
295
+ if (v === "") throw new Error(`cauthy-auth: ${name} is required to push a theme`);
296
+ }
297
+ const res = await (o.fetch ?? fetch)(`${issuer}/v1/oauth/clients/${encodeURIComponent(clientId)}/theme`, {
298
+ method: "PUT",
299
+ headers: { authorization: `Bearer ${token}`, "x-tenant-key": tenant, "content-type": "application/json" },
300
+ body: JSON.stringify(toWire(o.theme))
301
+ });
302
+ if (!res.ok) {
303
+ const body = await res.json().catch(() => null);
304
+ throw new Error(`cauthy-auth: Cauthy refused the theme (${res.status}): ${body?.errors?.[0]?.message ?? res.statusText}`);
305
+ }
306
+ }
307
+
60
308
  // src/types.ts
61
309
  var CauthyConfigError = class extends Error {
62
310
  constructor(message) {
@@ -217,6 +465,15 @@ function createSystem(given = {}) {
217
465
  const u = await data("POST", "/v1/users", { ...person, client_id: clientId });
218
466
  return { id: u.id, email: u.email };
219
467
  },
468
+ theme: {
469
+ async get(of = clientId) {
470
+ return fromWire(await data("GET", `/v1/oauth/clients/${id(of)}/theme`));
471
+ },
472
+ async set(theme, of = clientId) {
473
+ if (theme === null) await call("DELETE", `/v1/oauth/clients/${id(of)}/theme`);
474
+ else await call("PUT", `/v1/oauth/clients/${id(of)}/theme`, toWire(theme));
475
+ }
476
+ },
220
477
  async publicJwks() {
221
478
  if (!key) return { keys: [] };
222
479
  const { kty, n, e } = asJwk(key) ?? await exportJWK(await signingKey());
@@ -613,226 +870,6 @@ function createCauthy(given = {}) {
613
870
  };
614
871
  }
615
872
 
616
- // src/theme.ts
617
- var MAX_RADIUS = 40;
618
- function declarations(css) {
619
- const text = css.replace(/\/\*[\s\S]*?\*\//g, "");
620
- const out2 = [];
621
- const within = [];
622
- let chunk = "";
623
- let quote = "";
624
- let depth = 0;
625
- const flush = () => {
626
- const m = /^\s*(--[\w-]+)\s*:\s*([\s\S]+?)\s*$/.exec(chunk);
627
- if (m) out2.push({ name: m[1], value: m[2].replace(/\s+/g, " ").replace(/\s*!important$/, ""), within: [...within] });
628
- chunk = "";
629
- };
630
- for (const ch of text) {
631
- if (quote) {
632
- chunk += ch;
633
- if (ch === quote) quote = "";
634
- continue;
635
- }
636
- if (ch === '"' || ch === "'") {
637
- quote = ch;
638
- chunk += ch;
639
- } else if (ch === "(") {
640
- depth++;
641
- chunk += ch;
642
- } else if (ch === ")") {
643
- depth = Math.max(0, depth - 1);
644
- chunk += ch;
645
- } else if (ch === "{" && depth === 0) {
646
- within.push(chunk.trim().replace(/\s+/g, " "));
647
- chunk = "";
648
- } else if (ch === "}" && depth === 0) {
649
- flush();
650
- within.pop();
651
- } else if (ch === ";" && depth === 0) {
652
- flush();
653
- } else {
654
- chunk += ch;
655
- }
656
- }
657
- return out2;
658
- }
659
- var DARK = /prefers-color-scheme:\s*dark|data-(mode|theme|color-scheme)=["']?dark|\.dark\b/i;
660
- var LIGHT = /prefers-color-scheme:\s*light|data-(mode|theme|color-scheme)=["']?light|\.light\b/i;
661
- var NAMED = /data-theme=["']?([\w-]+)/i;
662
- function applies(d, o) {
663
- for (const scope of d.within) {
664
- if (o.mode === "light" && DARK.test(scope)) return false;
665
- if (o.mode === "dark" && LIGHT.test(scope)) return false;
666
- const named = NAMED.exec(scope);
667
- if (named && !/^(dark|light)$/i.test(named[1]) && named[1] !== o.theme) return false;
668
- if (!scope.startsWith("@") && !/:root|^html\b|^body\b|\[data-|^\*$|:host/.test(scope)) return false;
669
- }
670
- return true;
671
- }
672
- function resolve(value, vars, seen = 0) {
673
- if (seen > 16) return null;
674
- let unresolved = false;
675
- let out2 = value;
676
- for (let guard = 0; guard < 32 && /var\(/.test(out2); guard++) {
677
- out2 = out2.replace(/var\(\s*(--[\w-]+)\s*(?:,\s*([^()]*(?:\([^()]*\)[^()]*)*))?\)/, (_m, name, fallback) => {
678
- const known = vars.get(name);
679
- const next = known !== void 0 ? resolve(known, vars, seen + 1) : fallback !== void 0 ? resolve(fallback.trim(), vars, seen + 1) : null;
680
- if (next === null) unresolved = true;
681
- return next ?? "";
682
- });
683
- if (unresolved) return null;
684
- }
685
- return /var\(/.test(out2) ? null : out2.trim();
686
- }
687
- var clamp01 = (n) => Math.min(1, Math.max(0, n));
688
- var hex2 = (n) => Math.round(clamp01(n) * 255).toString(16).padStart(2, "0");
689
- var gamma = (c) => c <= 31308e-7 ? 12.92 * c : 1.055 * Math.pow(c, 1 / 2.4) - 0.055;
690
- function out(r, g, b, a = 1) {
691
- if (a >= 1) return `#${hex2(r)}${hex2(g)}${hex2(b)}`;
692
- return `rgba(${Math.round(clamp01(r) * 255)}, ${Math.round(clamp01(g) * 255)}, ${Math.round(clamp01(b) * 255)}, ${Math.round(clamp01(a) * 1e3) / 1e3})`;
693
- }
694
- var alpha = (s) => s === void 0 ? 1 : s.endsWith("%") ? parseFloat(s) / 100 : parseFloat(s);
695
- var KEYWORDS = { white: "#ffffff", black: "#000000" };
696
- function toColor(value) {
697
- const v = value.trim().toLowerCase();
698
- if (KEYWORDS[v]) return KEYWORDS[v];
699
- if (/^#([0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/.test(v)) return v;
700
- let m = /^rgba?\(\s*([\d.]+%?)[\s,]+([\d.]+%?)[\s,]+([\d.]+%?)(?:\s*[,/]\s*([\d.]+%?))?\s*\)$/.exec(v);
701
- if (m) {
702
- const ch = (s) => s.endsWith("%") ? parseFloat(s) / 100 : parseFloat(s) / 255;
703
- return out(ch(m[1]), ch(m[2]), ch(m[3]), alpha(m[4]));
704
- }
705
- m = /^hsla?\(\s*([\d.]+)(?:deg)?[\s,]+([\d.]+)%[\s,]+([\d.]+)%(?:\s*[,/]\s*([\d.]+%?))?\s*\)$/.exec(v);
706
- if (m) {
707
- const h = (parseFloat(m[1]) % 360 + 360) % 360;
708
- const s = parseFloat(m[2]) / 100;
709
- const l = parseFloat(m[3]) / 100;
710
- const f = (n) => {
711
- const k = (n + h / 30) % 12;
712
- return l - s * Math.min(l, 1 - l) * Math.max(-1, Math.min(k - 3, 9 - k, 1));
713
- };
714
- return out(f(0), f(8), f(4), alpha(m[4]));
715
- }
716
- m = /^oklch\(\s*([\d.]+%?)\s+([\d.]+%?)\s+([\d.]+)(?:deg)?(?:\s*\/\s*([\d.]+%?))?\s*\)$/.exec(v);
717
- if (m) {
718
- const L = m[1].endsWith("%") ? parseFloat(m[1]) / 100 : parseFloat(m[1]);
719
- const C = m[2].endsWith("%") ? parseFloat(m[2]) / 100 * 0.4 : parseFloat(m[2]);
720
- const H = parseFloat(m[3]) * Math.PI / 180;
721
- const a = C * Math.cos(H);
722
- const b = C * Math.sin(H);
723
- const l = Math.pow(L + 0.3963377774 * a + 0.2158037573 * b, 3);
724
- const mm = Math.pow(L - 0.1055613458 * a - 0.0638541728 * b, 3);
725
- const s = Math.pow(L - 0.0894841775 * a - 1.291485548 * b, 3);
726
- return out(
727
- gamma(4.0767416621 * l - 3.3077115913 * mm + 0.2309699292 * s),
728
- gamma(-1.2684380046 * l + 2.6097574011 * mm - 0.3413193965 * s),
729
- gamma(-0.0041960863 * l - 0.7034186147 * mm + 1.707614701 * s),
730
- alpha(m[4])
731
- );
732
- }
733
- return null;
734
- }
735
- function toRadius(value) {
736
- const m = /^([\d.]+)(px|rem|em)?$/.exec(value.trim());
737
- if (!m) return null;
738
- const px = parseFloat(m[1]) * (m[2] === "rem" || m[2] === "em" ? 16 : 1);
739
- return Number.isFinite(px) ? Math.min(MAX_RADIUS, Math.max(0, Math.round(px))) : null;
740
- }
741
- function toFont(value) {
742
- const v = value.trim();
743
- 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;
744
- }
745
- var COLORS = [
746
- ["background", ["--sm-bg-surface", "--sm-surface"]],
747
- ["surface", ["--sm-bg-surface-raised", "--sm-surface-raised"]],
748
- ["text", ["--sm-text-primary", "--sm-surface-text"]],
749
- ["textMuted", ["--sm-text-secondary"]],
750
- ["border", ["--sm-border-default", "--sm-surface-border"]],
751
- ["primary", ["--sm-primary", "--sm-bg-interactive"]],
752
- ["primaryText", ["--sm-primary-text", "--sm-text-inverse"]],
753
- ["accent", ["--sm-text-brand", "--sm-border-focus", "--sm-accent"]],
754
- ["error", ["--sm-error-soft-text", "--sm-error"]],
755
- ["errorBackground", ["--sm-error-soft"]],
756
- ["success", ["--sm-success-soft-text", "--sm-success"]],
757
- ["successBackground", ["--sm-success-soft"]]
758
- ];
759
- function themeFromStackmyth(css, options = {}) {
760
- const o = { mode: options.mode ?? "light", theme: options.theme };
761
- const vars = /* @__PURE__ */ new Map();
762
- for (const sheet of Array.isArray(css) ? css : [css]) {
763
- for (const d of declarations(sheet)) if (applies(d, o)) vars.set(d.name, d.value);
764
- }
765
- const theme = {};
766
- const warnings = [];
767
- const read = (tokens, to, what) => {
768
- for (const token of tokens) {
769
- const raw = vars.get(token);
770
- if (raw === void 0) continue;
771
- const value = resolve(raw, vars);
772
- const got = value === null ? null : to(value);
773
- if (got !== null) return got;
774
- warnings.push(`${token}: "${raw}" is not ${what} Cauthy can take`);
775
- }
776
- return void 0;
777
- };
778
- for (const [field, tokens] of COLORS) {
779
- const c = read(tokens, toColor, "a colour");
780
- if (c !== void 0) theme[field] = c;
781
- }
782
- const radius = read(["--sm-radius-container", "--sm-radius-md"], toRadius, "a length");
783
- if (radius !== void 0) theme.radius = radius;
784
- const button = read(["--sm-radius-interactive", "--sm-radius-sm"], toRadius, "a length");
785
- if (button !== void 0) theme.buttonRadius = button;
786
- const font = read(["--sm-font-family"], toFont, "a font list");
787
- if (font !== void 0) theme.fontFamily = font;
788
- if (options.logoUrl) theme.logoUrl = options.logoUrl;
789
- if (vars.size === 0) warnings.push("no custom properties were found: is this the stylesheet that declares the --sm-* tokens?");
790
- else if (![...vars.keys()].some((k) => k.startsWith("--sm-"))) warnings.push("no --sm-* token was found in these stylesheets");
791
- return { theme, warnings };
792
- }
793
- function toWire(theme) {
794
- const names = {
795
- background: "background",
796
- surface: "surface",
797
- text: "text",
798
- textMuted: "text_muted",
799
- border: "border",
800
- primary: "primary",
801
- primaryText: "primary_text",
802
- accent: "accent",
803
- error: "error",
804
- errorBackground: "error_background",
805
- success: "success",
806
- successBackground: "success_background",
807
- radius: "radius",
808
- buttonRadius: "button_radius",
809
- fontFamily: "font_family",
810
- logoUrl: "logo_url"
811
- };
812
- const wire = {};
813
- for (const [k, v] of Object.entries(theme)) if (v !== void 0 && v !== "") wire[names[k]] = v;
814
- return wire;
815
- }
816
- async function pushTheme(o) {
817
- const env3 = (n) => typeof process !== "undefined" ? process.env?.[n] : void 0;
818
- const issuer = (o.issuer ?? env3("CAUTHY_ISSUER") ?? "").replace(/\/+$/, "");
819
- const clientId = o.clientId ?? env3("CAUTHY_CLIENT_ID") ?? "";
820
- const tenant = o.tenant ?? env3("CAUTHY_TENANT") ?? "";
821
- const token = o.token ?? env3("CAUTHY_TOKEN") ?? "";
822
- for (const [name, v] of Object.entries({ CAUTHY_ISSUER: issuer, CAUTHY_CLIENT_ID: clientId, CAUTHY_TENANT: tenant, CAUTHY_TOKEN: token })) {
823
- if (v === "") throw new Error(`cauthy-auth: ${name} is required to push a theme`);
824
- }
825
- const res = await (o.fetch ?? fetch)(`${issuer}/v1/oauth/clients/${encodeURIComponent(clientId)}/theme`, {
826
- method: "PUT",
827
- headers: { authorization: `Bearer ${token}`, "x-tenant-key": tenant, "content-type": "application/json" },
828
- body: JSON.stringify(toWire(o.theme))
829
- });
830
- if (!res.ok) {
831
- const body = await res.json().catch(() => null);
832
- throw new Error(`cauthy-auth: Cauthy refused the theme (${res.status}): ${body?.errors?.[0]?.message ?? res.statusText}`);
833
- }
834
- }
835
-
836
873
  // src/permissions.ts
837
874
  function matches(entry, resource, action) {
838
875
  const i = entry.lastIndexOf(":");